diff --git a/Project.toml b/Project.toml index f8ebd97c..13319280 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,7 @@ authors = ["Stuart Daines "] version = "0.21.36" [deps] +Atomix = "a9b6321e-bd34-4604-b9c9-b65b8de01458" BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" @@ -30,6 +31,7 @@ TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" [compat] +Atomix = "0.1, 1.0" BenchmarkTools = "1.0" DataFrames = "1.1" DocStringExtensions = "0.8, 0.9" diff --git a/docs/src/CreateInitializeLoop.md b/docs/src/CreateInitializeLoop.md index 2056dfa4..e7e0f4a8 100644 --- a/docs/src/CreateInitializeLoop.md +++ b/docs/src/CreateInitializeLoop.md @@ -51,7 +51,7 @@ The custom Vector of [`CellRange`](@ref)s should then be supplied to ### Multithreaded models To use with a multithreaded solver eg for a spatially tiled model: -- Set `threadsafe=true` when calling [`create_modeldata`](@ref). The subsequent call to [`allocate_variables!`](@ref) will then allocate Julia `Threads.Atomic` variables for PALEO Variables with attribute `atomic: = true` (usually scalar accumulator variables for totals etc). +- Set the global parameter `threadsafe=true` in the YAML file before calling [`create_model_from_config`](@ref). This is used by Reactions that require special handling eg atomic operations to maintain thread safety (usually those with scalar accumulator variables for totals etc). - Supply a `method_barrier` implementing a thread barrier function to [`initialize_reactiondata!`](@ref). Reactions are sorted into groups, where each group has no dependencies and later groups depend on earlier ones. The `method_barrier` will be inserted between these groups of independent Reactions. ### Automatic differentiation diff --git a/docs/src/Reaction API.md b/docs/src/Reaction API.md index 7afe1cc0..1befd909 100644 --- a/docs/src/Reaction API.md +++ b/docs/src/Reaction API.md @@ -176,6 +176,13 @@ zero_ad value_ad ``` +#### Writing thread-safe code + +Reactions that accumulate per-cell quantities into a scalar variable (eg to calculate Domain totals) and are intended for use in +large models that use a multithreaded solver with tiled Domains should use `atomic_add!`: +```@docs +atomic_add! +``` #### Optimising loops over cells using explicit SIMD instructions Reactions with simple loops over `cellindices` that implement time-consuming per-cell calculations diff --git a/src/Fields.jl b/src/Fields.jl index 859a65c6..27c69b6e 100644 --- a/src/Fields.jl +++ b/src/Fields.jl @@ -1,5 +1,5 @@ -import Infiltrator + ############################################################ # (Function) Spaces ########################################################### @@ -109,6 +109,9 @@ If the subtype needs to provide values for a numerical solver (eg as a state var If the subtype has a representation as components, it should implement: [`num_components`](@ref), [`get_components`](@ref) + +If the subtype needs to provide a thread-safe atomic addition operation eg to provide scalar accumulator variables for Domain totals with a tiled model, +it should implement [`atomic_add!`](@ref) for the field `data_type`. """ AbstractData @@ -116,7 +119,7 @@ AbstractData """ allocate_values( field_data::Type{<:AbstractData}, data_dims::Tuple{Vararg{NamedDimension}}, data_type, space::Type{<:AbstractSpace}, spatial_size::Tuple; - thread_safe::Bool, allocatenans::Bool, + allocatenans::Bool, ) -> values allocate `Field.values` (eg an Array) for `field_data` with dimensions defined by `spatial_size` and `data_dims` @@ -127,7 +130,7 @@ function allocate_values( data_type, space::Type{<:AbstractSpace}, spatial_size::Tuple{Integer, Vararg{Integer}}, # an NTuple with at least one element - thread_safe::Bool, allocatenans::Bool, + allocatenans::Bool, ) end @@ -315,7 +318,7 @@ Convert Field `values` to a Vector of components function get_components(values, field_data::Type{<:AbstractData}) end "Optional: sanitize `values` for storing as model output. -Default implementation is usually OK - only implement eg for Atomic types that should be converted to standard types for storage" +Default implementation is usually OK - only implement for custom types that should be converted to standard types for storage" get_values_output(values, data_type::Type{<:AbstractData}, data_dims::Tuple{Vararg{NamedDimension}}, space, mesh) = values @@ -371,11 +374,11 @@ function add_field! end "create a new Field, allocating `values` data arrays" function allocate_field( field_data::Type, data_dims::NTuple{N, NamedDimension}, data_type::Type, space::Type{<:AbstractSpace}, mesh; - thread_safe::Bool, allocatenans + allocatenans ) where {N} v = allocate_values( - field_data, data_dims, data_type, space, spatial_size(space, mesh), - thread_safe=thread_safe, allocatenans=allocatenans, + field_data, data_dims, data_type, space, spatial_size(space, mesh); + allocatenans, ) return Field{field_data, space, typeof(v), N, typeof(mesh)}(v, data_dims, mesh) diff --git a/src/Model.jl b/src/Model.jl index 2f0499d5..34e5ddea 100644 --- a/src/Model.jl +++ b/src/Model.jl @@ -393,16 +393,18 @@ end """ - create_modeldata(model::Model [, eltype] [; threadsafe]) -> modeldata::ModelData + create_modeldata(model::Model [; arrays_eltype=Float64]) -> modeldata::ModelData -Create a new [`ModelData`](@ref) struct for model variables of element type `eltype`. +Create a new [`ModelData`](@ref) struct for model variables with data arrays element type `arrays_eltype`. """ function create_modeldata( model::Model, arrays_eltype::DataType=Float64; - threadsafe=false, allocatenans=true, # fill Arrays with NaN when first allocated + threadsafe=false, # deprecated ) - modeldata = ModelData(model; arrays_eltype, threadsafe, allocatenans) + threadsafe == false || error("create_modeldata: 'threadsafe=true' no longer supported. Set parameter 'threadsafe: true' in YAML configuration file instead.") + + modeldata = ModelData(model; arrays_eltype, allocatenans) modeldata.cellranges_all = create_default_cellrange(model) diff --git a/src/ModelData.jl b/src/ModelData.jl index 5d67f329..ba02a764 100644 --- a/src/ModelData.jl +++ b/src/ModelData.jl @@ -20,7 +20,7 @@ end """ - ModelData(model::Model; arrays_eltype::DataType=Float64, threadsafe::Bool=false, allocatenans::Bool=true) + ModelData(model::Model; arrays_eltype::DataType=Float64, allocatenans::Bool=true) Create a ModelData struct containing `model` data arrays. @@ -37,7 +37,6 @@ which requires Dual numbers as the array element type. Base.@kwdef mutable struct ModelData <: AbstractModelData model::Model domain_data::Vector{Tuple{DataType, String, Vector{DomainData}}} = Tuple{DataType, String, Vector{DomainData}}[] - threadsafe::Bool allocatenans::Bool # fill with NaN when allocating sorted_methodsdata_setup = nothing # only for arrays_idx = 1 @@ -49,11 +48,13 @@ Base.@kwdef mutable struct ModelData <: AbstractModelData solver_view_all = nothing # only for arrays_idx = 1 end -function ModelData(model::Model; arrays_eltype::DataType=Float64, threadsafe::Bool=false, allocatenans::Bool=true) - +function ModelData( + model::Model; + arrays_eltype::DataType=Float64, + allocatenans::Bool=true, +) modeldata = ModelData(; model, - threadsafe, allocatenans, ) diff --git a/src/PALEOboxes.jl b/src/PALEOboxes.jl index a971ac12..8bad61c9 100644 --- a/src/PALEOboxes.jl +++ b/src/PALEOboxes.jl @@ -27,6 +27,7 @@ using DocStringExtensions import OrderedCollections import Logging import Printf +import Atomix import PrecompileTools import TimerOutputs: @timeit, @timeit_debug @@ -37,7 +38,6 @@ include("Types.jl") include("CoordsDims.jl") include("Fields.jl") -include("data/AtomicScalar.jl") include("data/ScalarData.jl") include("data/ArrayScalarData.jl") include("data/IsotopeData.jl") diff --git a/src/VariableDomain.jl b/src/VariableDomain.jl index bb5ba8b7..b20f1b6c 100644 --- a/src/VariableDomain.jl +++ b/src/VariableDomain.jl @@ -169,8 +169,8 @@ get_data(var::VariableDomain, modeldata::AbstractModelData, arrays_idx::Int=1) = get_data(var::VariableDomain, domaindata::AbstractDomainData) = get_field(var, domaindata).values """ - get_data(var::VariableDomain, modeldata::AbstractModelData, arrays_idx::Int) -> get_values_output(field.values) - get_data(var::VariableDomain, domaindata::AbstractDomainData) -> get_values_output(field.values) + get_data_output(var::VariableDomain, modeldata::AbstractModelData, arrays_idx::Int) -> get_values_output(field.values) + get_data_output(var::VariableDomain, domaindata::AbstractDomainData) -> get_values_output(field.values) Get a sanitized version of Variable `var` data array for storing as output from [`get_values_output`]@ref)`(`[`Field`](@ref).values`)` @@ -265,12 +265,6 @@ function allocate_variables!( mdeltype = get(eltypemap, mdeltype_str, Float64) @debug "Variable $(fullname(v)) mdeltype $mdeltype_str -> $mdeltype" end - - thread_safe = false - if get_attribute(v, :atomic, false) && modeldata.threadsafe - @info " $(fullname(v)) allocating Atomic data" - thread_safe = true - end field_data = get_attribute(v, :field_data) space = get_attribute(v, :space) @@ -297,8 +291,8 @@ function allocate_variables!( else # allocate new field array v_field = allocate_field( - field_data, data_dims, mdeltype, space, v.domain.grid, - thread_safe=thread_safe, allocatenans=modeldata.allocatenans + field_data, data_dims, mdeltype, space, v.domain.grid; + allocatenans=modeldata.allocatenans ) end diff --git a/src/data/ArrayScalarData.jl b/src/data/ArrayScalarData.jl index 098c698c..8eba0476 100644 --- a/src/data/ArrayScalarData.jl +++ b/src/data/ArrayScalarData.jl @@ -15,10 +15,8 @@ end function allocate_values( field_data::Type{ArrayScalarData}, data_dims::Tuple{NamedDimension, Vararg{NamedDimension}}, data_type, space::Type{<:AbstractSpace}, spatial_size::Tuple{Integer, Vararg{Integer}}; - thread_safe, allocatenans, + allocatenans, ) - !thread_safe || throw(ArgumentError("ArrayScalarData with thread_safe==true")) - s = _size_arrayscalardata(data_dims, space, spatial_size) d = Array{data_type, length(s)}(undef, s...) diff --git a/src/data/AtomicScalar.jl b/src/data/AtomicScalar.jl deleted file mode 100644 index dbfabc17..00000000 --- a/src/data/AtomicScalar.jl +++ /dev/null @@ -1,42 +0,0 @@ - -""" - AtomicScalar{T} - -Provides an implementation of the Julia Array interface -for a length 1 Vector that holds an atomic scalar variable. -""" -mutable struct AtomicScalar{T} <: AbstractArray{T, 1} - value::Threads.Atomic{T} - function AtomicScalar{T}() where T - return new(Threads.Atomic{T}()) - end -end - -Base.size(as::AtomicScalar) = (1, ) -Base.IndexStyle(::Type{<:AtomicScalar}) = IndexLinear() - -Base.getindex(as::AtomicScalar) = as.value[] -function Base.getindex(as::AtomicScalar, i::Integer) - @boundscheck(checkbounds(as, i)) - return as.value[] -end - -Base.setindex!(as::AtomicScalar, v) = (as.value[] = v) -function Base.setindex!(as::AtomicScalar, v, i::Integer) - @boundscheck(checkbounds(as, i)) - as.value[] = v - return v -end - -""" - atomic_add!(as::AtomicScalar, v) - atomic_add!(a::Array, v) - -Add v to as[] using Thread-safe atomic operation, with fallback for -normal Julia Arrays. - -Fallback adds v to a[] using standard addition. -""" -atomic_add!(as::AtomicScalar, v) = Threads.atomic_add!(as.value, v) - -@Base.propagate_inbounds atomic_add!(a::AbstractArray, v) = (a[] += v) \ No newline at end of file diff --git a/src/data/IsotopeData.jl b/src/data/IsotopeData.jl index 8cec129c..5ae4abb3 100644 --- a/src/data/IsotopeData.jl +++ b/src/data/IsotopeData.jl @@ -255,10 +255,10 @@ end function allocate_values( data::Type{IsotopeLinear}, data_dims::Tuple{}, data_type, space::Type{<:AbstractSpace}, spatial_size::Tuple{Integer, Vararg{Integer}}; - thread_safe, allocatenans, + allocatenans, ) - v = allocate_values(ScalarData, data_dims, data_type, space, spatial_size, thread_safe=thread_safe, allocatenans=allocatenans) - v_moldelta = allocate_values(ScalarData, data_dims, data_type, space, spatial_size, thread_safe=thread_safe, allocatenans=allocatenans) + v = allocate_values(ScalarData, data_dims, data_type, space, spatial_size; allocatenans) + v_moldelta = allocate_values(ScalarData, data_dims, data_type, space, spatial_size; allocatenans) return StructArrays.StructArray{IsotopeLinear{data_type, data_type}}((v, v_moldelta)) end @@ -403,18 +403,8 @@ function create_accessor( end ############################################################# -# Special cases for atomic scalar Vectors of IsotopeLinear +# Special cases for atomic addition ########################################################## -"type of an atomic scalar Vector of IsotopeLinear" -const AtomicIsotopeLinear = StructArrays.StructVector{ - IsotopeLinear{Float64, Float64}, - NamedTuple{(:v, :v_moldelta), Tuple{AtomicScalar{Float64}, AtomicScalar{Float64}}}, - Int64 -} - -atomic_add!(ias::AtomicIsotopeLinear, iv::IsotopeLinear) = (atomic_add!(ias.v, iv.v); atomic_add!(ias.v_moldelta, iv.v_moldelta)) - -function get_values_output(values::AtomicIsotopeLinear, data_type::Type{IsotopeLinear}, data_dims::Tuple{}, space::Type{<:AbstractSpace}, mesh) - # convert to an IsotopeLinear for output - return StructArrays.StructArray( [values[]]) -end \ No newline at end of file + +atomic_add!(ias::StructArrays.StructVector{<:IsotopeLinear}, iv::IsotopeLinear) = (atomic_add!(ias.v, iv.v); atomic_add!(ias.v_moldelta, iv.v_moldelta);) + diff --git a/src/data/ScalarData.jl b/src/data/ScalarData.jl index 6ad72069..e1dbd7ab 100644 --- a/src/data/ScalarData.jl +++ b/src/data/ScalarData.jl @@ -18,16 +18,10 @@ get_components(values, field_data::Type{ScalarData}) = [values] function allocate_values( field_data::Type{ScalarData}, data_dims::Tuple{}, data_type, space::Type{<:AbstractSpace}, spatial_size::Tuple{Integer, Vararg{Integer}}; - thread_safe, allocatenans, + allocatenans, ) - if thread_safe - spatial_size == (1, ) || - throw(ArgumentError("thread_safe with spatial_size != (1, )")) - d = AtomicScalar{data_type}() - else - d = Array{data_type, length(spatial_size)}(undef, spatial_size...) - end + d = Array{data_type, length(spatial_size)}(undef, spatial_size...) if allocatenans fill!(d, NaN) @@ -36,10 +30,6 @@ function allocate_values( return d end -function get_values_output(values::AtomicScalar, data_type::Type{ScalarData}, data_dims::Tuple{}, space, mesh) - return [values[]] -end - function check_values( existing_values, field_data::Type{ScalarData}, data_dims::Tuple{}, data_type, space::Type{<:AbstractSpace}, spatial_size::Tuple{Integer, Vararg{Integer}} ) @@ -83,6 +73,16 @@ function init_values!( return nothing end +""" + atomic_add!(values::AbstractArray, v) + +Add `v` to `values[]` using Thread-safe atomic operation. + +This is a generic fallback for ScalarData containing `values` of any `data_type`. +""" +atomic_add!(values::AbstractArray, v) = Atomix.@atomic values[] += v + + function zero_values!(values, field_data::Type{ScalarData}, data_dims::Tuple{}, space::Type{ScalarSpace}, cellrange) values[] = 0.0 return nothing diff --git a/src/reactioncatalog/Reservoirs.jl b/src/reactioncatalog/Reservoirs.jl index 45fcdacf..4f5f3493 100644 --- a/src/reactioncatalog/Reservoirs.jl +++ b/src/reactioncatalog/Reservoirs.jl @@ -628,16 +628,18 @@ function PB.register_methods!(rj::ReactionReservoirWellMixed) end PB.setfrozen!(rj.pars.initialization_type) + threadsafe = get(rj.external_parameters, "threadsafe", false) + PB.add_method_do!(rj, do_reservoir_well_mixed, (PB.VarList_namedtuple(vars),)) # add method to sum per-cell vec_sms to scalar sms vars_sms = [ PB.VarDerivScalar( "R_sms", "mol yr-1", "scalar reservoir source-sinks", - attributes=(:field_data=>rj.pars.field_data[], :atomic=>true,)), + attributes=(:field_data=>rj.pars.field_data[],)), PB.VarTarget( "R_vec_sms","mol yr-1", "vector reservoir source-sinks", attributes=(:field_data=>rj.pars.field_data[],)) ] - PB.add_method_do!(rj, do_reservoir_well_mixed_sms, (PB.VarList_namedtuple(vars_sms),)) + PB.add_method_do!(rj, do_reservoir_well_mixed_sms, (PB.VarList_namedtuple(vars_sms),); p=threadsafe) PB.add_method_initialize_zero_vars_default!(rj) @@ -678,6 +680,7 @@ function do_reservoir_well_mixed_sms( cellrange::PB.AbstractCellRange, deltat, ) + threadsafe = m.p # accumulate first into a subtotal, # in order to minimise time spent with lock held if this is one tile of a threaded model @@ -685,7 +688,11 @@ function do_reservoir_well_mixed_sms( @inbounds for i in cellrange.indices subtotal += vars.R_vec_sms[i] end - PB.atomic_add!(vars.R_sms, subtotal) + if threadsafe + PB.atomic_add!(vars.R_sms, subtotal) + else + vars.R_sms[] += subtotal + end return nothing end diff --git a/src/reactioncatalog/VariableStats.jl b/src/reactioncatalog/VariableStats.jl index 12898595..0494ffbe 100644 --- a/src/reactioncatalog/VariableStats.jl +++ b/src/reactioncatalog/VariableStats.jl @@ -236,10 +236,12 @@ function PB.register_methods!(rj::ReactionWeightedMean) PB.VarDep( "measure", "unknown", "cell area or volume"), PB.VarDepScalar( "measure_total", "unknown", "total Domain area or volume"), PB.VarPropScalar("var_mean", "unknown", "weighted mean over Domain area or volume", - attributes=(:field_data=>rj.pars.field_data[], :initialize_to_zero=>true, :atomic=>true)), + attributes=(:field_data=>rj.pars.field_data[], :initialize_to_zero=>true,)), ] - PB.add_method_do!(rj, do_weighted_mean, (PB.VarList_namedtuple(vars),)) + threadsafe = get(rj.external_parameters, "threadsafe", false) + + PB.add_method_do!(rj, do_weighted_mean, (PB.VarList_namedtuple(vars),); p=threadsafe) PB.add_method_initialize_zero_vars_default!(rj) @@ -247,7 +249,7 @@ function PB.register_methods!(rj::ReactionWeightedMean) end function do_weighted_mean(m::PB.ReactionMethod, (vars,), cellrange::PB.AbstractCellRange, deltat) - rj = m.reaction + threadsafe = m.p # use a subtotal to minimise time lock held if this is one tile of a threaded model subtotal = zero(vars.var_mean[]) @@ -255,8 +257,14 @@ function do_weighted_mean(m::PB.ReactionMethod, (vars,), cellrange::PB.AbstractC subtotal += vars.var[i]*vars.measure[i] # normalisation by measure_total below end - PB.atomic_add!(vars.var_mean, subtotal/vars.measure_total[]) - + var_mean_to_add = subtotal/vars.measure_total[] + + if threadsafe + PB.atomic_add!(vars.var_mean, var_mean_to_add) + else + vars.var_mean[] += var_mean_to_add + end + return nothing end @@ -294,10 +302,11 @@ function PB.register_methods!(rj::ReactionAreaVolumeValInRange) PB.VarDep( "measure", "unknown", "cell area or volume"), PB.VarDepScalar( "measure_total", "unknown", "total Domain area or volume"), PB.VarPropScalar("frac", "", "fraction of Domain area or volume in specified range", - attributes=(:initialize_to_zero=>true, :atomic=>true)), + attributes=(:initialize_to_zero=>true,)), ] - PB.add_method_do!(rj, do_area_volume_in_range, (PB.VarList_namedtuple(vars),)) + threadsafe = get(rj.external_parameters, "threadsafe", false) + PB.add_method_do!(rj, do_area_volume_in_range, (PB.VarList_namedtuple(vars),); p=threadsafe) PB.add_method_initialize_zero_vars_default!(rj) @@ -305,6 +314,7 @@ function PB.register_methods!(rj::ReactionAreaVolumeValInRange) end function do_area_volume_in_range(m::PB.ReactionMethod, pars, (vars,), cellrange::PB.AbstractCellRange, deltat) + threadsafe = m.p # use a subtotal to minimise time lock held if this is one tile of a threaded model frac_subtotal = zero(vars.frac[]) @@ -314,7 +324,13 @@ function do_area_volume_in_range(m::PB.ReactionMethod, pars, (vars,), cellrange: end end - PB.atomic_add!(vars.frac, frac_subtotal/vars.measure_total[]) + frac_to_add = frac_subtotal/vars.measure_total[] + + if threadsafe + PB.atomic_add!(vars.frac, frac_to_add) + else + vars.frac[] += frac_to_add + end return nothing end diff --git a/src/reactionmethods/VariableStatsMethods.jl b/src/reactionmethods/VariableStatsMethods.jl index f7e0d0e1..2d4f8fe3 100644 --- a/src/reactionmethods/VariableStatsMethods.jl +++ b/src/reactionmethods/VariableStatsMethods.jl @@ -95,26 +95,34 @@ function create_totalvar(var, total_localname=nothing) total_localname = var.localname*"_total" end return VarPropScalar(total_localname, get_attribute(var, :units), "total "*get_attribute(var, :description), - attributes=(:field_data=>get_attribute(var, :field_data), :initialize_to_zero=>true, :atomic=>true), + attributes=(:field_data=>get_attribute(var, :field_data), :initialize_to_zero=>true,), ) end """ do_vartotals(varstats, varstats_data, cellrange::AbstractCellRange) -Calculate totals. `varstats_data` is `accessors` returned by `create_accessors`. Call from the -`ReactionPhase` supplied to [`add_var`](@ref). +Calculate totals. """ function do_vartotals(m::AbstractReactionMethod, (vars_data, var_totals_data), cellrange::AbstractCellRange, deltat) - function calc_total(data, total_data, cellrange) + threadsafe = m.p + + function calc_total(data, total_data, cellrange, ts) # accumulate first into a subtotal, and then into total_data[], in order to minimise time spent with lock held # if this is one tile of a threaded model subtotal = zero(total_data[]) + @inbounds for i in cellrange.indices subtotal += data[i] end - atomic_add!(total_data, subtotal) + + if ts + atomic_add!(total_data, subtotal) + else + total_data[] += subtotal + end + return nothing end @@ -122,7 +130,7 @@ function do_vartotals(m::AbstractReactionMethod, (vars_data, var_totals_data), c error("do_vartotals: components length mismatch $(fullname(m)) $(get_variables_tuple(m)) (check :field_data (ScalarData, IsotopeLinear etc) match)") # if cellrange.operatorID == 0 || cellrange.operatorID in totals_method.operatorID for iv in eachindex(vars_data) - calc_total(vars_data[iv], var_totals_data[iv], cellrange) + calc_total(vars_data[iv], var_totals_data[iv], cellrange, threadsafe) end # end @@ -141,7 +149,7 @@ function _create_totals_method( reaction, methodname, (VarList_components(VarDep.(vars)), VarList_components(var_totals)), - nothing, + get(reaction.external_parameters, "threadsafe", false), operatorID, reaction.domain ) diff --git a/test/runfieldtests.jl b/test/runfieldtests.jl index 2e08324f..e4f44cdc 100644 --- a/test/runfieldtests.jl +++ b/test/runfieldtests.jl @@ -9,7 +9,7 @@ import PALEOboxes as PB @testset "ScalarData, ScalarSpace" begin - f = PB.allocate_field(PB.ScalarData, (), Float64, PB.ScalarSpace, nothing; thread_safe=false, allocatenans=true) + f = PB.allocate_field(PB.ScalarData, (), Float64, PB.ScalarSpace, nothing; allocatenans=true) @test isnan(f.values[]) @@ -18,7 +18,7 @@ import PALEOboxes as PB @testset "ScalarData, CellSpace, no grid" begin # check that a CellSpace Field with no grid behaves as a ScalarSpace Field - f = PB.allocate_field(PB.ScalarData, (), Float64, PB.CellSpace, nothing; thread_safe=false, allocatenans=true) + f = PB.allocate_field(PB.ScalarData, (), Float64, PB.CellSpace, nothing; allocatenans=true) @test isnan(f.values[]) end @@ -26,14 +26,14 @@ import PALEOboxes as PB @testset "ScalarData, CellSpace, UnstructuredColumnGrid" begin g = PB.Grids.UnstructuredColumnGrid(ncells=5, Icolumns=[collect(1:5)]) - f = PB.allocate_field(PB.ScalarData, (), Float64, PB.CellSpace, g; thread_safe=false, allocatenans=true) + f = PB.allocate_field(PB.ScalarData, (), Float64, PB.CellSpace, g; allocatenans=true) @test isnan.(f.values) == [true for i in 1:5] end @testset "ArrayScalarData, ScalarSpace" begin - f = PB.allocate_field(PB.ArrayScalarData, (PB.NamedDimension("test", 2, []), ), Float64, PB.ScalarSpace, nothing; thread_safe=false, allocatenans=true) + f = PB.allocate_field(PB.ArrayScalarData, (PB.NamedDimension("test", 2, []), ), Float64, PB.ScalarSpace, nothing; allocatenans=true) @test isnan.(f.values) == [true, true] @@ -42,7 +42,7 @@ import PALEOboxes as PB @testset "ArrayScalarData, CellSpace, no grid" begin # TODO this is possibly inconsistent with (ScalarData, CellSpace, no grid), # as Field.values here is a (1, 2) Array, not a (2,) Vector - f = PB.allocate_field(PB.ArrayScalarData, (PB.NamedDimension("test", 2, []), ), Float64, PB.CellSpace, nothing; thread_safe=false, allocatenans=true) + f = PB.allocate_field(PB.ArrayScalarData, (PB.NamedDimension("test", 2, []), ), Float64, PB.CellSpace, nothing; allocatenans=true) @test_broken size(f.values) == (2, ) # TODO should be a Vector ? @test size(f.values) == (1, 2) @@ -53,7 +53,7 @@ import PALEOboxes as PB @testset "ArrayScalarData, CellSpace, UnstructuredColumnGrid" begin g = PB.Grids.UnstructuredColumnGrid(ncells=5, Icolumns=[collect(1:5)]) - f = PB.allocate_field(PB.ArrayScalarData, (PB.NamedDimension("test", 2, []), ), Float64, PB.CellSpace, g; thread_safe=false, allocatenans=true) + f = PB.allocate_field(PB.ArrayScalarData, (PB.NamedDimension("test", 2, []), ), Float64, PB.CellSpace, g; allocatenans=true) @test size(f.values) == (5, 2)