diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index bc5bc8891..020bb6920 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -2,7 +2,7 @@ name: CI on: push: branches: - - main + - dev tags: "*" pull_request: workflow_dispatch: diff --git a/.github/workflows/Integration.yml b/.github/workflows/Integration.yml index 4d0671497..9e965ce92 100644 --- a/.github/workflows/Integration.yml +++ b/.github/workflows/Integration.yml @@ -2,7 +2,7 @@ name: Integration on: push: branches: - - main + - dev tags: "*" pull_request: workflow_dispatch: @@ -32,8 +32,8 @@ jobs: arch: - x64 package: - - {user: PalmStudio, repo: XPalm.jl, branch: PSE-API-changes} - - {user: VEZY, repo: PlantBioPhysics.jl, branch: ModelList-outputs-filtering-changes} + - {user: PalmStudio, repo: XPalm.jl, branch: dev} + - {user: VEZY, repo: PlantBioPhysics.jl, branch: dev} steps: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v2 diff --git a/.github/workflows/benchmarks_and_downstream.yml b/.github/workflows/benchmarks_and_downstream.yml index 0b0db73b3..be23e19cf 100644 --- a/.github/workflows/benchmarks_and_downstream.yml +++ b/.github/workflows/benchmarks_and_downstream.yml @@ -1,11 +1,12 @@ -name: BenchmarksAndDownstream +name: Benchmarks on: push: branches: - - main + - dev - benchmarks-github-action tags: "*" - workflow-dispatch: + pull_request: + workflow_dispatch: permissions: # deployments permission to deploy GitHub pages website deployments: write @@ -16,8 +17,6 @@ jobs: name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} runs-on: ${{ matrix.os }} timeout-minutes: 60 - env: - GROUP: ${{ matrix.package.group }} strategy: fail-fast: false matrix: @@ -30,6 +29,7 @@ jobs: arch: - x64 package: + # the group setting is unused atm - {user: VEZY, repo: PlantSimEngine.jl, group: Downstream} steps: - uses: actions/checkout@v4 @@ -43,8 +43,7 @@ jobs: run: | cd test/downstream julia --project --threads 4 --color=yes -e ' - using Pkg; - Pkg.instantiate(); + using Pkg; include("test-all-benchmarks.jl")' rm Manifest.toml - name: Store benchmark result diff --git a/.gitignore b/.gitignore index 0bd0f101a..4fead6a18 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ .DS_Store docs/Manifest.toml test/Manifest.toml -docs/build/ \ No newline at end of file +docs/build/ +test/downstream/Manifest.toml diff --git a/docs/src/index.md b/docs/src/index.md index 697124245..d946933c6 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -291,12 +291,12 @@ out = run!(mtg, mapping, meteo, tracked_outputs=out_vars, executor=SequentialEx( nothing # hide ``` -We can then extract the outputs in a `DataFrame` and sort them: +We can then extract the outputs and convert them to a `DataFrame` for each scale and sort them: ```@example readme using DataFrames -df_out = convert_outputs(out, DataFrame) -sort!(df_out, [:timestep, :node]) +df_dict = convert_outputs(out, DataFrame) +sort!(df_dict["Leaf"], [:timestep, :node]) ``` An example output of a multiscale simulation is shown in the documentation of PlantBiophysics.jl: diff --git a/docs/src/multiscale/multiscale.md b/docs/src/multiscale/multiscale.md index 3c7948954..42973bb6d 100644 --- a/docs/src/multiscale/multiscale.md +++ b/docs/src/multiscale/multiscale.md @@ -245,11 +245,11 @@ outputs_sim = run!(mtg, mapping, meteo, tracked_outputs = outs); nothing # hide ``` -And that's it! We can now access the outputs for each scale as a dictionary of vectors of values per variable and scale. +And that's it! We can now access the outputs for each scale as a dictionary of vectors of NamedTuple objects. -Or as a `DataFrame` using the [`DataFrames`](https://dataframes.juliadata.org) package: +Or as a `DataFrame` dictionary using the [`DataFrames`](https://dataframes.juliadata.org) package: ```@example usepkg using DataFrames -convert_outputs(outputs_sim, DataFrame) +df_dict = convert_outputs(outputs_sim, DataFrame) ``` \ No newline at end of file diff --git a/docs/src/multiscale/multiscale_considerations.md b/docs/src/multiscale/multiscale_considerations.md index b2ddf2b14..7f2b1fe08 100644 --- a/docs/src/multiscale/multiscale_considerations.md +++ b/docs/src/multiscale/multiscale_considerations.md @@ -83,39 +83,65 @@ Instead of a [`ModelList`](@ref), it takes an MTG and a mapping. The optional `m ## Multi-scale output data structure -The output structure, like the mapping, is a Julia `Dict` structure indexed by scale. In each scale, another `Dict` maps variables to their values per timestep, per node. This makes the structure a little bulkier and a little more verbose to inspect than in single-scale, but the general usage is similar. Multiscale Tree Graph nodes are also added to the output data, as a `:node` entry. + +The output structure, like the mapping, is a Julia `Dict` structure indexed by the scale name. Values are a per-scale `Vector{NamedTuple}` which lists the requested variables for every node at that scale, for every timestep in the simulation. Timestep and Multiscale Tree Graph nodes are also added to the output data, as a `:timestep`and a `:node` entry. + +This dictionary structure makes the outputs as-is a little more verbose to inspect than in single-scale, but the general usage is similar, and it is both compact, and fast to convert to a `Dict{String, DataFrame}` which can make queries easier. + +!!! note + Some of the mapped variables -those that map from scalar to vector- will not be added to the outputs to save some memory and space since they are redundant. + To illustrate, here's an example output from part 3 of the Toy plant tutorial, zeroing in on a variable at the "Root" scale: [Fixing bugs in the plant simulation](@ref): ```julia julia> outs -Dict{String, Dict{Symbol, Vector}} with 5 entries: - "Internode" => Dict(:carbon_root_creation_consumed=>[[50.0, 50.0], [50.0, 50.0], [50.0, 50.0], [50.0, 50.0], [50.0, … - "Root" => Dict(:carbon_root_creation_consumed=>[[50.0, 50.0], [50.0, 50.0, 50.0], [50.0, 50.0, 50.0, 50.0], [50… - "Scene" => Dict(:TT_cu=>[[0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], [0.0] … [2099.61], [20… - "Plant" => Dict(:carbon_root_creation_consumed=>[[50.0], [50.0], [50.0], [50.0], [50.0], [50.0], [50.0], [50.0],… - "Leaf" => Dict(:node=>Vector{Node{NodeMTG, Dict{Symbol, Any}}}[[+ 4: Leaf… +Dict{String, Vector} with 5 entries: + "Internode" => @NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, carbon_root_creation_consumed::Float64, TT_cu::Float64, carbon_… + "Root" => @NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, carbon_root_creation_consumed::Float64, water_absorbed::Float64… + "Scene" => @NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, TT_cu::Float64, TT::Float64}[(timestep = 1, node = / 1: Scene… + "Plant" => @NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, carbon_root_creation_consumed::Float64, carbon_stock::Float64, … + "Leaf" => @NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, carbon_captured::Float64}[(timestep = 1, node = + 4: Leaf… julia> outs["Root"] -Dict{Symbol, Vector} with 4 entries: - :carbon_root_creation_consumed => [[50.0, 50.0], [50.0, 50.0, 50.0], [50.0, 50.0, 50.0, 50.0], [50.0, 50.0, 50.0, 50… - :node => Vector{Node{NodeMTG, Dict{Symbol, Any}}}[[+ 9: Root… - :water_absorbed => [[0.5, 0.0], [1.0, 1.0, 0.0], [0.0, 0.0, 0.0, 0.0], [1.1, 1.1, 1.1, 1.1, 0.0], [0.… - :root_water_assimilation => [[1.0, 1.0], [1.0, 1.0, 1.0], [1.0, 1.0, 1.0, 1.0], [1.0, 1.0, 1.0, 1.0, 1.0], [1.… - -julia> outs["Root"][:carbon_root_creation_consumed] -365-element Vector{Vector{Float64}}: - [50.0, 50.0] # timestep 1: two root nodes - [50.0, 50.0, 50.0] - [50.0, 50.0, 50.0, 50.0] - [50.0, 50.0, 50.0, 50.0, 50.0] - [50.0, 50.0, 50.0, 50.0, 50.0, 50.0] - [50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0] # timestep 6: 7 root nodes +3257-element Vector{@NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, carbon_root_creation_consumed::Float64, water_absorbed::Float64, root_water_assimilation::Float64}}: + (timestep = 1, node = + 9: Root +└─ < 10: Root + └─ < 11: Root + └─ < 12: Root + └─ < 13: Root + └─ < 14: Root + └─ < 15: Root + └─ < 16: Root + └─ < 17: Root +, carbon_root_creation_consumed = 50.0, water_absorbed = 0.5, root_water_assimilation = 1.0) ⋮ ``` -As more roots get added in this simulation, the vectors expand to list the values of all the nodes for every variable for every timestep. +Values are more complex to query than in a single-scale simulation since the indexing isn't straightforward to map to a timestep: + +```julia +julia> [Pair(outs["Root"][i][:timestep], outs["Root"][i][:carbon_root_creation_consumed]) for i in 1:length(outs["Root"])] +3257-element Vector{Pair{Int64, Float64}}: + 1 => 50.0 + 1 => 50.0 + 2 => 50.0 + 2 => 50.0 + 2 => 50.0 + ⋮ + 365 => 50.0 + 365 => 50.0 + 365 => 50.0 + 365 => 50.0 + 365 => 50.0 + 365 => 50.0 + 365 => 50.0 + 365 => 50.0 + 365 => 50.0 +``` + +Converting to a dictionary of DataFrame objects can make such queries easier to write. !!! warning Currently, the `:node` entry only shallow copies nodes. The `:node` values at each scale for every timestep actually reflect the final state of the node, meaning attribute values may not correspond to the value at that timestep. You may need to output these values via a dedicated model to keep track of them properly. diff --git a/docs/src/multiscale/multiscale_example_3.md b/docs/src/multiscale/multiscale_example_3.md index 749914071..5ab116d50 100644 --- a/docs/src/multiscale/multiscale_example_3.md +++ b/docs/src/multiscale/multiscale_example_3.md @@ -244,13 +244,19 @@ Depth = 3 There is one quirk you may have noticed when inspecting the data : when a root expands, the new root is immediately active, and some models may act on it immediately... including the root growth model. Meaning this new root may also sprout another root in the same timestep, and so on. -You can notice this by looking at the simulation's state after the first timestep: +You can notice this by looking at the simulation's state during the first two timesteps: ```@example usepkg outs = run!(mtg, mapping, first(meteo_day, 2)) -nodes_per_timestep = outs["Root"][:node] -root_lengths_per_timestep = [length(nodes_per_timestep[i]) for i in 1:length(nodes_per_timestep)] +root_nodes_per_timestep = [0, 0] +for i in 1:length(outs["Root"]) + if outs["Root"][i].timestep < 3 + root_nodes_per_timestep[outs["Root"][i].timestep] += 1 + end +end + +root_nodes_per_timestep ``` Our root grew to full length within one timestep. Oops. diff --git a/docs/src/multiscale/single_to_multiscale.md b/docs/src/multiscale/single_to_multiscale.md index 169e8260c..5f5ccb240 100644 --- a/docs/src/multiscale/single_to_multiscale.md +++ b/docs/src/multiscale/single_to_multiscale.md @@ -203,20 +203,18 @@ We can access the output variables at the "Scene" scale by indexing our outputs: ```@example usepkg outputs_multiscale["Scene"] ``` -and then the computed `:TT_cu`: +We have a `Vector{NamedTuple}`structure. Our single-scale output is a `Vector{T}`: ```@example usepkg -outputs_multiscale["Scene"][:TT_cu] +outputs_singlescale.TT_cu ``` -As you can see, it is a `Vector{Vector{T}}`, whereas our single-scale output is a `Vector{T}`: + Let's extract the multi-scale `:TT_cu`: ```@example usepkg -outputs_singlescale.TT_cu +computed_TT_cu_multiscale = [outputs_multiscale["Scene"][i].TT_cu for i in 1:length(outputs_multiscale["Scene"])] ``` -To compare them value-by-value, we can flatten the multiscale Vector and then do a piecewise approximate equality test : +We can now compare them value-by-value and do a piecewise approximate equality test : ```@example usepkg -computed_TT_cu_multiscale = collect(Base.Iterators.flatten(outputs_multiscale["Scene"][:TT_cu])) - for i in 1:length(computed_TT_cu_multiscale) if !(computed_TT_cu_multiscale[i] ≈ outputs_singlescale.TT_cu[i]) println(i) diff --git a/docs/src/working_with_data/floating_point_accumulation_error.md b/docs/src/working_with_data/floating_point_accumulation_error.md index 12e09b20a..f17a482f9 100644 --- a/docs/src/working_with_data/floating_point_accumulation_error.md +++ b/docs/src/working_with_data/floating_point_accumulation_error.md @@ -95,13 +95,11 @@ mtg_multiscale = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", "Scen plant = MultiScaleTreeGraph.Node(mtg_multiscale, MultiScaleTreeGraph.NodeMTG("+", "Plant", 1, 1)) outputs_multiscale = run!(mtg_multiscale, mapping_multiscale, meteo_day) -computed_TT_cu_multiscale = collect(Base.Iterators.flatten(outputs_multiscale["Scene"][:TT_cu])) ``` ```@example usepkg -computed_TT_cu_multiscale = collect(Base.Iterators.flatten(outputs_multiscale["Scene"][:TT_cu])) - +computed_TT_cu_multiscale = [outputs_multiscale["Scene"][i].TT_cu for i in 1:length(outputs_multiscale["Scene"])] is_approx_equal = length(unique(computed_TT_cu_multiscale .≈ outputs_singlescale.TT_cu)) == 1 ``` @@ -110,8 +108,7 @@ Why was the comparison only approximate ? Why `≈` instead of `==`? Let's try it out. What if write instead: ```@example usepkg -computed_TT_cu_multiscale = collect(Base.Iterators.flatten(outputs_multiscale["Scene"][:TT_cu])) - +computed_TT_cu_multiscale = [outputs_multiscale["Scene"][i].TT_cu for i in 1:length(outputs_multiscale["Scene"])] is_perfectly_equal = length(unique(computed_TT_cu_multiscale .== outputs_singlescale.TT_cu)) == 1 ``` diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl index 9f243aeb3..37874e12e 100644 --- a/src/mtg/GraphSimulation.jl +++ b/src/mtg/GraphSimulation.jl @@ -25,6 +25,7 @@ struct GraphSimulation{T,S,U,O,V} dependency_graph::DependencyGraph models::Dict{String,U} outputs::Dict{String,O} + outputs_index::Dict{String, Int} end function GraphSimulation(graph, mapping; nsteps=1, outputs=nothing, type_promotion=nothing, check=true, verbose=false) @@ -89,73 +90,45 @@ out = run!(mtg, mapping, meteo, tracked_outputs = Dict( convert_outputs(out, DataFrames) ``` """ +# Another, possibly better way would be to just create the DataFrame directly from the outputs +# and then remove the RefVector columns and replace the node one, hmm function convert_outputs(outs::Dict{String,O} where O, sink; refvectors=false, no_value=nothing) - @assert Tables.istable(sink) "The sink argument must be compatible with the Tables.jl interface (`Tables.istable(sink)` must return `true`, *e.g.* `DataFrame`)" - - - variables_names_types = Iterators.flatten(collect(i.first => eltype(i.second[1]) for i in filter(x -> x.first != :node, vars)) for (organs, vars) in outs) |> collect - variables_names_types_dict = Dict{Symbol,Any}() - - for (k, v) in variables_names_types - if !haskey(variables_names_types_dict, k) - variables_names_types_dict[k] = Union{typeof(no_value),v} - else - if !refvectors && v <: RefVector && !(variables_names_types_dict[k] <: Union{typeof(no_value),RefVector}) - continue + ret = Dict{String, sink}() + for (organ, status_vector) in outs + # remove RefVector variables + refv = () + if length(status_vector) > 0 + for (var, val) in pairs(status_vector[1]) + if !refvectors && isa(val, RefVector) + refv = (refv..., var) + end + if var == :node + refv = (refv..., var) + end end - variables_names_types_dict[k] = Union{variables_names_types_dict[k],v} end - end + + # Get the new NamedTuple type + refv_nt = NamedTuple{refv} - # If we have a variable that is only RefVector, we remove it from the variables_names_types: - !refvectors && filter!(x -> !(last(x) <: Union{typeof(no_value),RefVector}), variables_names_types_dict) - - variables_names_types = (timestep=Int, organ=String, node=Int, NamedTuple(variables_names_types_dict)...) - var_names_all = keys(variables_names_types) - t = NamedTuple{var_names_all,Tuple{values(variables_names_types)...}}[] - #=size_hint = 0 - for (organ, vars) in outs # organ = "Leaf"; vars = outs[organ] - var_names = setdiff(collect(keys(vars)), [:node]) - if length(var_names) == 0 - continue - end - steps_iterable = axes(vars[var_names[1]], 1) - for timestep in steps_iterable # timestep = 1 - node_iterable = axes(vars[var_names[1]][timestep], 1) - size_hint+=length(node_iterable) - end - end + # Piddle around with the first element to get the final type to be able to allocate the exact vector size with a definite element type + vector_named_tuple_1 = NamedTuple(status_vector[1]) - sizehint!(t, size_hint)=# + # replace the MTG node var with the id (MTG nodes aren't CSV-friendly) + filtered_named_tuple = (;node=MultiScaleTreeGraph.node_id(vector_named_tuple_1.node),Base.structdiff(vector_named_tuple_1, refv_nt)...) + filtered_vector_named_tuple = Vector{typeof(filtered_named_tuple)}(undef, length(status_vector)) - for (organ, vars) in outs # organ = "Leaf"; vars = outs[organ] - var_names = setdiff(collect(keys(vars)), [:node]) - if length(var_names) == 0 - continue + for i in 1:length(status_vector) + vector_named_tuple_i = NamedTuple(status_vector[i]) + filtered_vector_named_tuple[i] = (;node=MultiScaleTreeGraph.node_id(vector_named_tuple_i.node), Base.structdiff(vector_named_tuple_i, refv_nt)...) end - steps_iterable = axes(vars[var_names[1]], 1) - for timestep in steps_iterable # timestep = 1 - node_iterable = axes(vars[var_names[1]][timestep], 1) - for node in node_iterable # node = 1 - vals = Dict(zip(var_names, [something(vars[v][timestep][node], no_value) for v in var_names])) - # Remove RefVector values: - !refvectors && filter!(x -> !isa(x.second, RefVector), vals) - vars_values = (; timestep=timestep, organ=organ, node=MultiScaleTreeGraph.node_id(vars[:node][timestep][node]), vals...) - vars_no_values = setdiff(var_names_all, keys(vars_values)) - if length(vars_no_values) > 0 - vars_values = (; vars_values..., zip(vars_no_values, [no_value for v in vars_no_values])...) - end - push!( - t, - NamedTuple{var_names_all}(vars_values) - ) - end - end - end - return sink(t) + ret[organ] = sink(filtered_vector_named_tuple) + end + return ret end +# TODO adapt these to new output structure or remove them function outputs(outs::Dict{String, O} where O, key::Symbol) Tables.columns(convert_outputs(outs, Vector{NamedTuple}))[key] end diff --git a/src/mtg/initialisation.jl b/src/mtg/initialisation.jl index 16be962fb..57aef81cf 100644 --- a/src/mtg/initialisation.jl +++ b/src/mtg/initialisation.jl @@ -328,5 +328,6 @@ function init_simulation(mtg, mapping; nsteps=1, outputs=nothing, type_promotion outputs = pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) - return (; mtg, statuses, status_templates, reverse_multiscale_mapping, vars_need_init, dependency_graph=dep(mapping, verbose=verbose), models, outputs) + outputs_index = Dict{String, Int}(s => 1 for s in keys(outputs)) + return (; mtg, statuses, status_templates, reverse_multiscale_mapping, vars_need_init, dependency_graph=dep(mapping, verbose=verbose), models, outputs, outputs_index) end \ No newline at end of file diff --git a/src/mtg/save_results.jl b/src/mtg/save_results.jl index a024e1468..67b569148 100644 --- a/src/mtg/save_results.jl +++ b/src/mtg/save_results.jl @@ -109,6 +109,7 @@ julia> collect(keys(preallocated_vars["Leaf"])) :carbon_demand ``` """ + function pre_allocate_outputs(statuses, statuses_template, reverse_multiscale_mapping, vars_need_init, outs, nsteps; type_promotion=nothing, check=true) outs_ = Dict{String,Vector{Symbol}}() @@ -128,6 +129,20 @@ function pre_allocate_outputs(statuses, statuses_template, reverse_multiscale_ma outs_[i] = [outs[i]...] end end + + len = Dict{String, Int}() + for (organ, vals) in outs_ + len[organ] = length(outs_[organ]) + unique!(outs_[organ]) + end + + + for (organ, vals) in outs_ + if length(outs_[organ]) != len[organ] + @info "One or more requested output variable duplicated at scale $organ, removed it" + end + end + statuses_ = copy(statuses_template) # Checking that organs in outputs exist in the mtg (in the statuses): if !all(i in keys(statuses) for i in keys(outs_)) @@ -183,8 +198,6 @@ function pre_allocate_outputs(statuses, statuses_template, reverse_multiscale_ma end end - outs_tuple = Dict(i => Tuple(x for x in outs_[i]) for i in keys(outs_)) - node_types = [] for o in keys(statuses) if length(statuses[o]) > 0 @@ -196,10 +209,39 @@ function pre_allocate_outputs(statuses, statuses_template, reverse_multiscale_ma @assert length(node_type) == 1 "All plant graph nodes should have the same type, found $(unique(node_type))." node_type = only(node_type) - # Making the pre-allocated outputs: - return Dict(organ => Dict(var => [var == :node ? node_type[] : typeof(status_from_template(statuses_template[organ])[var])[] for n in 1:nsteps] for var in vars) for (organ, vars) in outs_tuple) - # Note: we use the type of the variable from the status template for each organ to pre-allocate the outputs. We transform the status template into a status to get the types of the variables - # without the reference types, e.g. RefVector{Float64} becomes Vector{Float64}. + # I don't know if this function barrier is necessary + preallocated_outputs = Dict{String, Vector}() + complete_preallocation_from_types!(preallocated_outputs, nsteps, outs_, node_type, statuses_template) + return preallocated_outputs +end + +function complete_preallocation_from_types!(preallocated_outputs, nsteps, outs_, node_type, statuses_template) + types = Vector{DataType}() + for organ in keys(outs_) + + outs_no_node = filter(x -> x != :node, outs_[organ]) + + #types = [typeof(status_from_template(statuses_template[organ])[var]) for var in outs[organ]] + values = [status_from_template(statuses_template[organ])[var] for var in outs_no_node] + + #push!(types, node_type) + + # contains :node + symbols_tuple = (:timestep, :node, outs_no_node...,) + # using node_type.parameters[1] is clunky, but covers both NodeMTG and AbstractNodeMTG types + values_tuple = (1, MultiScaleTreeGraph.Node((node_type.parameters[1])("/", "Uninitialized", 0, 0),), values...,) + + # Dummy value to make accessing the type easier + # (empty arrays don't have references to an instance, so their types can't be inspected and manipulated as easily) + dummy_status = (;zip(symbols_tuple, values_tuple)...) + data = typeof(Status(dummy_status))[] + resize!(data, nsteps) + + for ii in 1:nsteps + data[ii] = Status(dummy_status) + end + preallocated_outputs[organ] = data + end end @@ -218,14 +260,63 @@ function save_results!(object::GraphSimulation, i) end statuses = status(object) + indexes = object.outputs_index + for organ in keys(outs) + + if length(outs[organ]) == 0 + continue + end + + index = indexes[organ] + + # Samuel : Simple resizing heuristic + # This can be made much more conservative with the right heuristic, or with user hints + # The array filling bit of code is clunky, but building NamedTuples on the fly was tanking performance + # And it wasn't straightforward to avoid Status Ref reallocations causing performance issues + # I then tried various approaches with Status, resizing, using fill, deepcopying, ... + # I may have gotten a little confused while fighting the type system + # So there may be possible simplifications (maybe no need for a function barrier, perhaps the resizing could be made a one-liner...) + # But this should work without causing visible performance regressions on XPalm + len = length(outs[organ]) + if length(statuses[organ]) + index - 1 > len + min_required = max(length(statuses[organ]) + index - len, index) + + extra_length = 2*min_required - len + data = eltype(outs[organ])[] + resize!(data, extra_length) + dummy_value = NamedTuple(outs[organ][1]) + # TODO set timestep to 0 for clarity ? + + # Using fill! caused Ref issues, so call a Status constructor here instead of passing a prebuilt value + # This will avoid having all array entries point to the same ref but keep construction cost at a minimum + for new_entry in 1:extra_length + data[new_entry] = Status(dummy_value) + end - for (organ, vars) in outs - for (var, values) in vars - values[i] = [status[var] for status in statuses[organ]] + outs[organ] = cat(outs[organ], data, dims=1) + #println("len : ", len, " statuses #", length(statuses[organ]), " index ", index) + #println("min_required : ", min_required, " extra_length ", extra_length, " new len ", length(outs[organ])) end + + tracked_outputs = filter(i -> i != :timestep, keys(outs[organ][1])) + + indexes[organ] = copy_tracked_outputs_into_vector!(outs[organ], i, statuses[organ], tracked_outputs, indexes[organ]) end end +function copy_tracked_outputs_into_vector!(outs_organ, i, statuses_organ, tracked_outputs, index) + j = index + for status in statuses_organ + outs_organ[j].timestep = i + for var in tracked_outputs + outs_organ[j][var] = status[var] + end + j += 1 + end + return j +end + + function pre_allocate_outputs(m::ModelList, outs, nsteps; type_promotion=nothing, check=true) # NOTE : init_variables recreates a DependencyGraph, it's not great diff --git a/src/run.jl b/src/run.jl index 98362eeeb..8958127a3 100644 --- a/src/run.jl +++ b/src/run.jl @@ -422,6 +422,12 @@ function run!( end end + # save_results! resizes the outputs melodramatically because the total # of nodes at a given scale can't always be known + # if models create organs, so shrink it down to the final size here + for (organ, index) in object.outputs_index + resize!(outputs(object)[organ], index - 1) + end + return outputs(object) end diff --git a/test/downstream/Manifest.toml b/test/downstream/Manifest.toml deleted file mode 100644 index a90d7a7c3..000000000 --- a/test/downstream/Manifest.toml +++ /dev/null @@ -1,1153 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.11.1" -manifest_format = "2.0" -project_hash = "48c0cf248125af237657d8187328ec0ddef91af0" - -[[deps.AbstractTrees]] -git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" -uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" -version = "0.4.5" - -[[deps.Accessors]] -deps = ["CompositionsBase", "ConstructionBase", "InverseFunctions", "LinearAlgebra", "MacroTools", "Markdown"] -git-tree-sha1 = "b392ede862e506d451fc1616e79aa6f4c673dab8" -uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" -version = "0.1.38" - - [deps.Accessors.extensions] - AccessorsAxisKeysExt = "AxisKeys" - AccessorsDatesExt = "Dates" - AccessorsIntervalSetsExt = "IntervalSets" - AccessorsStaticArraysExt = "StaticArrays" - AccessorsStructArraysExt = "StructArrays" - AccessorsTestExt = "Test" - AccessorsUnitfulExt = "Unitful" - - [deps.Accessors.weakdeps] - AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" - Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" - IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" - Requires = "ae029012-a4dd-5104-9daa-d747884805df" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" - Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" - -[[deps.Adapt]] -deps = ["LinearAlgebra", "Requires"] -git-tree-sha1 = "50c3c56a52972d78e8be9fd135bfb91c9574c140" -uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "4.1.1" -weakdeps = ["StaticArrays"] - - [deps.Adapt.extensions] - AdaptStaticArraysExt = "StaticArrays" - -[[deps.AliasTables]] -deps = ["PtrArrays", "Random"] -git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" -uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" -version = "1.1.3" - -[[deps.ArgCheck]] -git-tree-sha1 = "680b3b8759bd4c54052ada14e52355ab69e07876" -uuid = "dce04be8-c92d-5529-be00-80e4d2c0e197" -version = "2.4.0" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.2" - -[[deps.ArnoldiMethod]] -deps = ["LinearAlgebra", "Random", "StaticArrays"] -git-tree-sha1 = "d57bd3762d308bded22c3b82d033bff85f6195c6" -uuid = "ec485272-7323-5ecc-a04f-4719b315124d" -version = "0.4.0" - -[[deps.ArrayInterface]] -deps = ["Adapt", "LinearAlgebra"] -git-tree-sha1 = "d5140b60b87473df18cf4fe66382b7c3596df047" -uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.17.1" - - [deps.ArrayInterface.extensions] - ArrayInterfaceBandedMatricesExt = "BandedMatrices" - ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices" - ArrayInterfaceCUDAExt = "CUDA" - ArrayInterfaceCUDSSExt = "CUDSS" - ArrayInterfaceChainRulesCoreExt = "ChainRulesCore" - ArrayInterfaceChainRulesExt = "ChainRules" - ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" - ArrayInterfaceReverseDiffExt = "ReverseDiff" - ArrayInterfaceSparseArraysExt = "SparseArrays" - ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" - ArrayInterfaceTrackerExt = "Tracker" - - [deps.ArrayInterface.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" - BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" - CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" - CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" - ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" - ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" - Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -version = "1.11.0" - -[[deps.BangBang]] -deps = ["Accessors", "ConstructionBase", "InitialValues", "LinearAlgebra", "Requires"] -git-tree-sha1 = "e2144b631226d9eeab2d746ca8880b7ccff504ae" -uuid = "198e06fe-97b7-11e9-32a5-e1d131e6ad66" -version = "0.4.3" - - [deps.BangBang.extensions] - BangBangChainRulesCoreExt = "ChainRulesCore" - BangBangDataFramesExt = "DataFrames" - BangBangStaticArraysExt = "StaticArrays" - BangBangStructArraysExt = "StructArrays" - BangBangTablesExt = "Tables" - BangBangTypedTablesExt = "TypedTables" - - [deps.BangBang.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" - Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" - TypedTables = "9d95f2ec-7b3d-5a63-8d20-e2491e220bb9" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -version = "1.11.0" - -[[deps.Baselet]] -git-tree-sha1 = "aebf55e6d7795e02ca500a689d326ac979aaf89e" -uuid = "9718e550-a3fa-408a-8086-8db961cd8217" -version = "0.1.1" - -[[deps.BenchmarkTools]] -deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] -git-tree-sha1 = "f1dff6729bc61f4d49e140da1af55dcd1ac97b2f" -uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -version = "1.5.0" - -[[deps.BitFlags]] -git-tree-sha1 = "0691e34b3bb8be9307330f88d1a3c3f25466c24d" -uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" -version = "0.1.9" - -[[deps.CSV]] -deps = ["CodecZlib", "Dates", "FilePathsBase", "InlineStrings", "Mmap", "Parsers", "PooledArrays", "PrecompileTools", "SentinelArrays", "Tables", "Unicode", "WeakRefStrings", "WorkerUtilities"] -git-tree-sha1 = "deddd8725e5e1cc49ee205a1964256043720a6c3" -uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" -version = "0.10.15" - -[[deps.CodeTracking]] -deps = ["InteractiveUtils", "UUIDs"] -git-tree-sha1 = "7eee164f122511d3e4e1ebadb7956939ea7e1c77" -uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2" -version = "1.3.6" - -[[deps.CodecInflate64]] -deps = ["TranscodingStreams"] -git-tree-sha1 = "d981a6e8656b1e363a2731716f46851a2257deb7" -uuid = "6309b1aa-fc58-479c-8956-599a07234577" -version = "0.1.3" - -[[deps.CodecZlib]] -deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" -uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.6" - -[[deps.CommonSubexpressions]] -deps = ["MacroTools"] -git-tree-sha1 = "cda2cfaebb4be89c9084adaca7dd7333369715c5" -uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" -version = "0.3.1" - -[[deps.Compat]] -deps = ["TOML", "UUIDs"] -git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.16.0" -weakdeps = ["Dates", "LinearAlgebra"] - - [deps.Compat.extensions] - CompatLinearAlgebraExt = "LinearAlgebra" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.1+0" - -[[deps.CompositionsBase]] -git-tree-sha1 = "802bb88cd69dfd1509f6670416bd4434015693ad" -uuid = "a33af91c-f02d-484b-be07-31d278c5ca2b" -version = "0.1.2" -weakdeps = ["InverseFunctions"] - - [deps.CompositionsBase.extensions] - CompositionsBaseInverseFunctionsExt = "InverseFunctions" - -[[deps.ConcurrentUtilities]] -deps = ["Serialization", "Sockets"] -git-tree-sha1 = "ea32b83ca4fefa1768dc84e504cc0a94fb1ab8d1" -uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" -version = "2.4.2" - -[[deps.ConstructionBase]] -git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157" -uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.5.8" - - [deps.ConstructionBase.extensions] - ConstructionBaseIntervalSetsExt = "IntervalSets" - ConstructionBaseLinearAlgebraExt = "LinearAlgebra" - ConstructionBaseStaticArraysExt = "StaticArrays" - - [deps.ConstructionBase.weakdeps] - IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" - LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - -[[deps.ContextVariablesX]] -deps = ["Compat", "Logging", "UUIDs"] -git-tree-sha1 = "25cc3803f1030ab855e383129dcd3dc294e322cc" -uuid = "6add18c4-b38d-439d-96f6-d6bc489c04c5" -version = "0.1.3" - -[[deps.Crayons]] -git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" -uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" -version = "4.1.1" - -[[deps.DataAPI]] -git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" -uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.16.0" - -[[deps.DataFrames]] -deps = ["Compat", "DataAPI", "DataStructures", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] -git-tree-sha1 = "fb61b4812c49343d7ef0b533ba982c46021938a6" -uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" -version = "1.7.0" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.20" - -[[deps.DataValueInterfaces]] -git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" -uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" -version = "1.0.0" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -version = "1.11.0" - -[[deps.DefineSingletons]] -git-tree-sha1 = "0fba8b706d0178b4dc7fd44a96a92382c9065c2c" -uuid = "244e2a9f-e319-4986-a169-4d1fe445cd52" -version = "0.1.2" - -[[deps.DelimitedFiles]] -deps = ["Mmap"] -git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" -version = "1.9.1" - -[[deps.DiffResults]] -deps = ["StaticArraysCore"] -git-tree-sha1 = "782dd5f4561f5d267313f23853baaaa4c52ea621" -uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" -version = "1.1.0" - -[[deps.DiffRules]] -deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] -git-tree-sha1 = "23163d55f885173722d1e4cf0f6110cdbaf7e272" -uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" -version = "1.15.1" - -[[deps.Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -version = "1.11.0" - -[[deps.Distributions]] -deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "3101c32aab536e7a27b1763c0797dba151b899ad" -uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.113" - - [deps.Distributions.extensions] - DistributionsChainRulesCoreExt = "ChainRulesCore" - DistributionsDensityInterfaceExt = "DensityInterface" - DistributionsTestExt = "Test" - - [deps.Distributions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" - Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.3" - -[[deps.Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.6.0" - -[[deps.ExceptionUnwrapping]] -deps = ["Test"] -git-tree-sha1 = "d36f682e590a83d63d1c7dbd287573764682d12a" -uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4" -version = "0.1.11" - -[[deps.EzXML]] -deps = ["Printf", "XML2_jll"] -git-tree-sha1 = "380053d61bb9064d6aa4a9777413b40429c79901" -uuid = "8f5d6c58-4d21-5cfd-889c-e3ad7ee6a615" -version = "1.2.0" - -[[deps.FLoops]] -deps = ["BangBang", "Compat", "FLoopsBase", "InitialValues", "JuliaVariables", "MLStyle", "Serialization", "Setfield", "Transducers"] -git-tree-sha1 = "0a2e5873e9a5f54abb06418d57a8df689336a660" -uuid = "cc61a311-1640-44b5-9fba-1b764f453329" -version = "0.2.2" - -[[deps.FLoopsBase]] -deps = ["ContextVariablesX"] -git-tree-sha1 = "656f7a6859be8673bf1f35da5670246b923964f7" -uuid = "b9860ae5-e623-471e-878b-f6a53c775ea6" -version = "0.1.1" - -[[deps.FileIO]] -deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "2dd20384bf8c6d411b5c7370865b1e9b26cb2ea3" -uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.16.6" -weakdeps = ["HTTP"] - - [deps.FileIO.extensions] - HTTPExt = "HTTP" - -[[deps.FilePathsBase]] -deps = ["Compat", "Dates"] -git-tree-sha1 = "7878ff7172a8e6beedd1dea14bd27c3c6340d361" -uuid = "48062228-2e41-5def-b9a4-89aafe57970f" -version = "0.9.22" -weakdeps = ["Mmap", "Test"] - - [deps.FilePathsBase.extensions] - FilePathsBaseMmapExt = "Mmap" - FilePathsBaseTestExt = "Test" - -[[deps.FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" -version = "1.11.0" - -[[deps.FillArrays]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" -uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.13.0" -weakdeps = ["PDMats", "SparseArrays", "Statistics"] - - [deps.FillArrays.extensions] - FillArraysPDMatsExt = "PDMats" - FillArraysSparseArraysExt = "SparseArrays" - FillArraysStatisticsExt = "Statistics" - -[[deps.FiniteDiff]] -deps = ["ArrayInterface", "LinearAlgebra", "Setfield"] -git-tree-sha1 = "84e3a47db33be7248daa6274b287507dd6ff84e8" -uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" -version = "2.26.2" - - [deps.FiniteDiff.extensions] - FiniteDiffBandedMatricesExt = "BandedMatrices" - FiniteDiffBlockBandedMatricesExt = "BlockBandedMatrices" - FiniteDiffSparseArraysExt = "SparseArrays" - FiniteDiffStaticArraysExt = "StaticArrays" - - [deps.FiniteDiff.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" - BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - -[[deps.ForwardDiff]] -deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] -git-tree-sha1 = "a2df1b776752e3f344e5116c06d75a10436ab853" -uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "0.10.38" -weakdeps = ["StaticArrays"] - - [deps.ForwardDiff.extensions] - ForwardDiffStaticArraysExt = "StaticArrays" - -[[deps.Future]] -deps = ["Random"] -uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" -version = "1.11.0" - -[[deps.Graphs]] -deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] -git-tree-sha1 = "1dc470db8b1131cfc7fb4c115de89fe391b9e780" -uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" -version = "1.12.0" - -[[deps.HTTP]] -deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "ae350b8225575cc3ea385d4131c81594f86dfe4f" -uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.10.12" - -[[deps.Highlights]] -deps = ["DocStringExtensions", "InteractiveUtils", "REPL"] -git-tree-sha1 = "9e13b8d8b1367d9692a90ea4711b4278e4755c32" -uuid = "eafb193a-b7ab-5a9e-9068-77385905fa72" -version = "0.5.3" - -[[deps.HypergeometricFunctions]] -deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] -git-tree-sha1 = "b1c2585431c382e3fe5805874bda6aea90a95de9" -uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.25" - -[[deps.Inflate]] -git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d" -uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" -version = "0.1.5" - -[[deps.InitialValues]] -git-tree-sha1 = "4da0f88e9a39111c2fa3add390ab15f3a44f3ca3" -uuid = "22cec73e-a1b8-11e9-2c92-598750a2cf9c" -version = "0.3.1" - -[[deps.InlineStrings]] -git-tree-sha1 = "45521d31238e87ee9f9732561bfee12d4eebd52d" -uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" -version = "1.4.2" - - [deps.InlineStrings.extensions] - ArrowTypesExt = "ArrowTypes" - ParsersExt = "Parsers" - - [deps.InlineStrings.weakdeps] - ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" - Parsers = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" - -[[deps.InputBuffers]] -git-tree-sha1 = "d5c278bee2efd4fda62725a4a794d7e5f55e14f1" -uuid = "0c81fc1b-5583-44fc-8770-48be1e1cca08" -version = "1.0.0" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -version = "1.11.0" - -[[deps.InverseFunctions]] -git-tree-sha1 = "a779299d77cd080bf77b97535acecd73e1c5e5cb" -uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.17" -weakdeps = ["Dates", "Test"] - - [deps.InverseFunctions.extensions] - InverseFunctionsDatesExt = "Dates" - InverseFunctionsTestExt = "Test" - -[[deps.InvertedIndices]] -git-tree-sha1 = "0dc7b50b8d436461be01300fd8cd45aa0274b038" -uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" -version = "1.3.0" - -[[deps.IrrationalConstants]] -git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.2" - -[[deps.IteratorInterfaceExtensions]] -git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" -uuid = "82899510-4779-5014-852e-03e436cf321d" -version = "1.0.0" - -[[deps.JLD2]] -deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "PrecompileTools", "Requires", "TranscodingStreams"] -git-tree-sha1 = "f1a1c1037af2a4541ea186b26b0c0e7eeaad232b" -uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" -version = "0.5.10" - -[[deps.JLLWrappers]] -deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.6.1" - -[[deps.JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.4" - -[[deps.JuliaVariables]] -deps = ["MLStyle", "NameResolution"] -git-tree-sha1 = "49fb3cb53362ddadb4415e9b73926d6b40709e70" -uuid = "b14d175d-62b4-44ba-8fb7-3064adc8c3ec" -version = "0.2.4" - -[[deps.LaTeXStrings]] -git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c" -uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.4.0" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.6.0+0" - -[[deps.LibGit2]] -deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -version = "1.11.0" - -[[deps.LibGit2_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] -uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.7.2+0" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.0+1" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -version = "1.11.0" - -[[deps.Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "61dfdba58e585066d8bce214c5a51eaa0539f269" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+1" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -version = "1.11.0" - -[[deps.LogExpFunctions]] -deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.28" - - [deps.LogExpFunctions.extensions] - LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" - LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" - LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - - [deps.LogExpFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -version = "1.11.0" - -[[deps.LoggingExtras]] -deps = ["Dates", "Logging"] -git-tree-sha1 = "f02b56007b064fbfddb4c9cd60161b6dd0f40df3" -uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" -version = "1.1.0" - -[[deps.LsqFit]] -deps = ["Distributions", "ForwardDiff", "LinearAlgebra", "NLSolversBase", "Printf", "StatsAPI"] -git-tree-sha1 = "40acc20cfb253cf061c1a2a2ea28de85235eeee1" -uuid = "2fda8390-95c7-5789-9bda-21331edee243" -version = "0.15.0" - -[[deps.MLStyle]] -git-tree-sha1 = "bc38dff0548128765760c79eb7388a4b37fae2c8" -uuid = "d8e11817-5142-5d16-987a-aa16d5891078" -version = "0.4.17" - -[[deps.MacroTools]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.13" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -version = "1.11.0" - -[[deps.MbedTLS]] -deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] -git-tree-sha1 = "c067a280ddc25f196b5e7df3877c6b226d390aaf" -uuid = "739be429-bea8-5141-9913-cc70e7f3736d" -version = "1.1.9" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.6+0" - -[[deps.MetaGraphsNext]] -deps = ["Graphs", "JLD2", "SimpleTraits"] -git-tree-sha1 = "d2ecf4a20f4ac694987dd08ac489b7f7ff805f35" -uuid = "fa8bd995-216d-47f1-8a91-f3b68fbeb377" -version = "0.7.1" - -[[deps.MicroCollections]] -deps = ["Accessors", "BangBang", "InitialValues"] -git-tree-sha1 = "44d32db644e84c75dab479f1bc15ee76a1a3618f" -uuid = "128add7d-3638-4c79-886c-908ea0c25c34" -version = "0.2.0" - -[[deps.Missings]] -deps = ["DataAPI"] -git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d" -uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.2.0" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" -version = "1.11.0" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.12.12" - -[[deps.MultiScaleTreeGraph]] -deps = ["AbstractTrees", "DataFrames", "Dates", "DelimitedFiles", "Graphs", "MetaGraphsNext", "MutableNamedTuples", "OrderedCollections", "Printf", "SHA", "XLSX"] -git-tree-sha1 = "7a1e10b3bbfe5f327518e99a0d2d26728eb26718" -uuid = "dd4a991b-8a45-4075-bede-262ee62d5583" -version = "0.14.1" - -[[deps.MutableNamedTuples]] -git-tree-sha1 = "0faaabea6ebbfde9a5a01455f851009fb2603aac" -uuid = "af6c499f-54b4-48cc-bbd2-094bba7533c7" -version = "0.1.3" - -[[deps.MyterialColors]] -git-tree-sha1 = "01d8466fb449436348999d7c6ad740f8f853a579" -uuid = "1c23619d-4212-4747-83aa-717207fae70f" -version = "0.3.0" - -[[deps.NLSolversBase]] -deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] -git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" -uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" -version = "7.8.3" - -[[deps.NaNMath]] -deps = ["OpenLibm_jll"] -git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" -uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "1.0.2" - -[[deps.NameResolution]] -deps = ["PrettyPrint"] -git-tree-sha1 = "1a0fa0e9613f46c9b8c11eee38ebb4f590013c5e" -uuid = "71a1bf82-56d0-4bbc-8a3c-48b961074391" -version = "0.1.5" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.27+1" - -[[deps.OpenLibm_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" - -[[deps.OpenSSL]] -deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "OpenSSL_jll", "Sockets"] -git-tree-sha1 = "38cb508d080d21dc1128f7fb04f20387ed4c0af4" -uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" -version = "1.4.3" - -[[deps.OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.15+1" - -[[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" -uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+0" - -[[deps.OrderedCollections]] -git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.6.3" - -[[deps.PDMats]] -deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "949347156c25054de2db3b166c52ac4728cbad65" -uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" -version = "0.11.31" - -[[deps.Parameters]] -deps = ["OrderedCollections", "UnPack"] -git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" -uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" -version = "0.12.3" - -[[deps.Parsers]] -deps = ["Dates", "PrecompileTools", "UUIDs"] -git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.8.1" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.11.0" -weakdeps = ["REPL"] - - [deps.Pkg.extensions] - REPLExt = "REPL" - -[[deps.PlantBiophysics]] -deps = ["CSV", "DataFrames", "Dates", "LsqFit", "OrderedCollections", "PlantMeteo", "PlantSimEngine", "RecipesBase", "Statistics", "YAML"] -git-tree-sha1 = "b1d51f6d8ab93de04fcbfe65c7d0daa1c9909778" -uuid = "7ae8fcfa-76ad-4ec6-9ea7-5f8f5e2d6ec9" -version = "0.13.0" - -[[deps.PlantMeteo]] -deps = ["CSV", "Crayons", "DataAPI", "DataFrames", "Dates", "HTTP", "JSON", "PrettyTables", "Statistics", "Tables", "Term", "YAML"] -git-tree-sha1 = "2abe6662473f25d50e43a0c70d02dfaf48eee661" -uuid = "4630fe09-e0fb-4da5-a846-781cb73437b6" -version = "0.6.0" - -[[deps.PlantSimEngine]] -deps = ["AbstractTrees", "CSV", "DataAPI", "DataFrames", "FLoops", "Markdown", "MultiScaleTreeGraph", "PlantMeteo", "Statistics", "Tables", "Term"] -git-tree-sha1 = "82802fb5d66bc1084438de82cf1f3a2a613510a1" -uuid = "9a576370-710b-4269-adf9-4f603a9c6423" -version = "0.10.3" - -[[deps.PooledArrays]] -deps = ["DataAPI", "Future"] -git-tree-sha1 = "36d8b4b899628fb92c2749eb488d884a926614d3" -uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" -version = "1.4.3" - -[[deps.PrecompileTools]] -deps = ["Preferences"] -git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" -uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.2.1" - -[[deps.Preferences]] -deps = ["TOML"] -git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.3" - -[[deps.PrettyPrint]] -git-tree-sha1 = "632eb4abab3449ab30c5e1afaa874f0b98b586e4" -uuid = "8162dcfd-2161-5ef2-ae6c-7681170c5f98" -version = "0.2.0" - -[[deps.PrettyTables]] -deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "Reexport", "StringManipulation", "Tables"] -git-tree-sha1 = "1101cd475833706e4d0e7b122218257178f48f34" -uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "2.4.0" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -version = "1.11.0" - -[[deps.Profile]] -uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" -version = "1.11.0" - -[[deps.ProgressLogging]] -deps = ["Logging", "SHA", "UUIDs"] -git-tree-sha1 = "80d919dee55b9c50e8d9e2da5eeafff3fe58b539" -uuid = "33c8b6b6-d38a-422a-b730-caa89a2f386c" -version = "0.1.4" - -[[deps.PtrArrays]] -git-tree-sha1 = "77a42d78b6a92df47ab37e177b2deac405e1c88f" -uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" -version = "1.2.1" - -[[deps.QuadGK]] -deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "cda3b045cf9ef07a08ad46731f5a3165e56cf3da" -uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.11.1" - - [deps.QuadGK.extensions] - QuadGKEnzymeExt = "Enzyme" - - [deps.QuadGK.weakdeps] - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - -[[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -version = "1.11.0" - -[[deps.Random]] -deps = ["SHA"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -version = "1.11.0" - -[[deps.RecipesBase]] -deps = ["PrecompileTools"] -git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" -uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.3.4" - -[[deps.Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" - -[[deps.Rmath]] -deps = ["Random", "Rmath_jll"] -git-tree-sha1 = "852bd0f55565a9e973fcfee83a84413270224dc4" -uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" -version = "0.8.0" - -[[deps.Rmath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "58cdd8fb2201a6267e1db87ff148dd6c1dbd8ad8" -uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" -version = "0.5.1+0" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[deps.SentinelArrays]] -deps = ["Dates", "Random"] -git-tree-sha1 = "d0553ce4031a081cc42387a9b9c8441b7d99f32d" -uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.4.7" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -version = "1.11.0" - -[[deps.Setfield]] -deps = ["ConstructionBase", "Future", "MacroTools", "StaticArraysCore"] -git-tree-sha1 = "e2cc6d8c88613c05e1defb55170bf5ff211fbeac" -uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" -version = "1.1.1" - -[[deps.SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" -version = "1.11.0" - -[[deps.SimpleBufferStream]] -git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1" -uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" -version = "1.2.0" - -[[deps.SimpleTraits]] -deps = ["InteractiveUtils", "MacroTools"] -git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" -uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" -version = "0.9.4" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -version = "1.11.0" - -[[deps.SortingAlgorithms]] -deps = ["DataStructures"] -git-tree-sha1 = "66e0a8e672a0bdfca2c3f5937efb8538b9ddc085" -uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.2.1" - -[[deps.SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.11.0" - -[[deps.SpecialFunctions]] -deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" -uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.4.0" - - [deps.SpecialFunctions.extensions] - SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - - [deps.SpecialFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - -[[deps.SplittablesBase]] -deps = ["Setfield", "Test"] -git-tree-sha1 = "e08a62abc517eb79667d0a29dc08a3b589516bb5" -uuid = "171d559e-b47b-412a-8079-5efa626c420e" -version = "0.1.15" - -[[deps.StaticArrays]] -deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "777657803913ffc7e8cc20f0fd04b634f871af8f" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.8" - - [deps.StaticArrays.extensions] - StaticArraysChainRulesCoreExt = "ChainRulesCore" - StaticArraysStatisticsExt = "Statistics" - - [deps.StaticArrays.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.StaticArraysCore]] -git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" -uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.3" - -[[deps.Statistics]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0" -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.11.1" -weakdeps = ["SparseArrays"] - - [deps.Statistics.extensions] - SparseArraysExt = ["SparseArrays"] - -[[deps.StatsAPI]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" -uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.7.0" - -[[deps.StatsBase]] -deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21" -uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.34.3" - -[[deps.StatsFuns]] -deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] -git-tree-sha1 = "b423576adc27097764a90e163157bcfc9acf0f46" -uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" -version = "1.3.2" - - [deps.StatsFuns.extensions] - StatsFunsChainRulesCoreExt = "ChainRulesCore" - StatsFunsInverseFunctionsExt = "InverseFunctions" - - [deps.StatsFuns.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.StringEncodings]] -deps = ["Libiconv_jll"] -git-tree-sha1 = "b765e46ba27ecf6b44faf70df40c57aa3a547dcb" -uuid = "69024149-9ee7-55f6-a4c4-859efe599b68" -version = "0.3.7" - -[[deps.StringManipulation]] -deps = ["PrecompileTools"] -git-tree-sha1 = "a6b1675a536c5ad1a60e5a5153e1fee12eb146e3" -uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" -version = "0.4.0" - -[[deps.StyledStrings]] -uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" -version = "1.11.0" - -[[deps.SuiteSparse]] -deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] -uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" - -[[deps.SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.7.0+0" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[deps.TableTraits]] -deps = ["IteratorInterfaceExtensions"] -git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" -uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" -version = "1.0.1" - -[[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297" -uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.12.0" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[deps.Term]] -deps = ["AbstractTrees", "CodeTracking", "Dates", "Highlights", "InteractiveUtils", "Logging", "Markdown", "MyterialColors", "OrderedCollections", "Parameters", "PrecompileTools", "ProgressLogging", "REPL", "Tables", "UUIDs", "Unicode", "UnicodeFun"] -git-tree-sha1 = "387e63d0b12d43982a3aacaef81cbbfb465715ae" -uuid = "22787eb5-b846-44ae-b979-8e399b8463ab" -version = "2.0.6" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -version = "1.11.0" - -[[deps.TranscodingStreams]] -git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" -uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.11.3" - -[[deps.Transducers]] -deps = ["Accessors", "ArgCheck", "BangBang", "Baselet", "CompositionsBase", "ConstructionBase", "DefineSingletons", "Distributed", "InitialValues", "Logging", "Markdown", "MicroCollections", "Requires", "SplittablesBase", "Tables"] -git-tree-sha1 = "7deeab4ff96b85c5f72c824cae53a1398da3d1cb" -uuid = "28d57a85-8fef-5791-bfe6-a80928e7c999" -version = "0.4.84" - - [deps.Transducers.extensions] - TransducersAdaptExt = "Adapt" - TransducersBlockArraysExt = "BlockArrays" - TransducersDataFramesExt = "DataFrames" - TransducersLazyArraysExt = "LazyArrays" - TransducersOnlineStatsBaseExt = "OnlineStatsBase" - TransducersReferenceablesExt = "Referenceables" - - [deps.Transducers.weakdeps] - Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" - BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - LazyArrays = "5078a376-72f3-5289-bfd5-ec5146d43c02" - OnlineStatsBase = "925886fa-5bf2-5e8e-b522-a9147a512338" - Referenceables = "42d2dcc6-99eb-4e98-b66c-637b7d73030e" - -[[deps.URIs]] -git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b" -uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.5.1" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -version = "1.11.0" - -[[deps.UnPack]] -git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" -uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" -version = "1.0.2" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -version = "1.11.0" - -[[deps.UnicodeFun]] -deps = ["REPL"] -git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" -uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" -version = "0.4.1" - -[[deps.WeakRefStrings]] -deps = ["DataAPI", "InlineStrings", "Parsers"] -git-tree-sha1 = "b1be2855ed9ed8eac54e5caff2afcdb442d52c23" -uuid = "ea10d353-3f73-51f8-a26c-33c1cb351aa5" -version = "1.4.2" - -[[deps.WorkerUtilities]] -git-tree-sha1 = "cd1659ba0d57b71a464a29e64dbc67cfe83d54e7" -uuid = "76eceee3-57b5-4d4a-8e66-0e911cebbf60" -version = "1.6.1" - -[[deps.XLSX]] -deps = ["Artifacts", "Dates", "EzXML", "Printf", "Tables", "ZipArchives", "ZipFile"] -git-tree-sha1 = "7fca49e6dbb35b7b7471956c2a9d3d921360a00f" -uuid = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0" -version = "0.10.4" - -[[deps.XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "a2fccc6559132927d4c5dc183e3e01048c6dcbd6" -uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.13.5+0" - -[[deps.YAML]] -deps = ["Base64", "Dates", "Printf", "StringEncodings"] -git-tree-sha1 = "dea63ff72079443240fbd013ba006bcbc8a9ac00" -uuid = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" -version = "0.4.12" - -[[deps.ZipArchives]] -deps = ["ArgCheck", "CodecInflate64", "CodecZlib", "InputBuffers", "PrecompileTools", "TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "f7fd93aa03f519c25b8b328693f2d36ce01220a9" -uuid = "49080126-0e18-4c2a-b176-c102e4b3760c" -version = "2.4.0" - -[[deps.ZipFile]] -deps = ["Libdl", "Printf", "Zlib_jll"] -git-tree-sha1 = "f492b7fe1698e623024e873244f10d89c95c340a" -uuid = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea" -version = "0.10.1" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.13+1" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.11.0+0" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.59.0+0" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+2" diff --git a/test/downstream/Project.toml b/test/downstream/Project.toml index 397e54380..783efd236 100644 --- a/test/downstream/Project.toml +++ b/test/downstream/Project.toml @@ -9,3 +9,4 @@ PlantSimEngine = "9a576370-710b-4269-adf9-4f603a9c6423" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +XPalm = "6b523e1e-d512-416c-8e51-a8fbef0064e7" diff --git a/test/downstream/test-all-benchmarks.jl b/test/downstream/test-all-benchmarks.jl index 36c059f0f..31b753ac8 100644 --- a/test/downstream/test-all-benchmarks.jl +++ b/test/downstream/test-all-benchmarks.jl @@ -1,6 +1,9 @@ using Pkg Pkg.activate(dirname(@__FILE__)) Pkg.develop(PackageSpec(path=dirname(dirname(@__DIR__)))) +Pkg.add(url="https://github.com/VEZY/PlantBiophysics.jl", rev="dev") +Pkg.add(url="https://github.com/PalmStudio/XPalm.jl", rev="dev") +Pkg.resolve() Pkg.instantiate() using PlantSimEngine @@ -37,7 +40,7 @@ suite[suite_name]["PBP_multiple_timesteps_MT"] = @benchmarkable benchmark_plantb suite[suite_name]["PBP_multiple_timesteps_ST"] = @benchmarkable benchmark_plantbiophysics_multitimestep_ST($leaf, $meteo) # "XPalm benchmark" -#=include("test-xpalm.jl") +include("test-xpalm.jl") suite[suite_name]["XPalm_setup"] = @benchmarkable xpalm_default_param_create() seconds = 120 palm, models, out_vars, meteo = xpalm_default_param_create() @@ -45,7 +48,7 @@ sim_outputs = xpalm_default_param_run(palm, models, out_vars, meteo) suite[suite_name]["XPalm_run"] = @benchmarkable xpalm_default_param_run($palm, $models, $out_vars, $meteo) seconds = 120 suite[suite_name]["XPalm_convert_outputs"] = @benchmarkable xpalm_default_param_convert_outputs($sim_outputs) seconds = 120 -=# + tune!(suite) results = run(suite, verbose=true) BenchmarkTools.save(dirname(@__FILE__) * "/output.json", median(results)) \ No newline at end of file diff --git a/test/downstream/test-plantbiophysics.jl b/test/downstream/test-plantbiophysics.jl index 8f026c30b..175a15651 100644 --- a/test/downstream/test-plantbiophysics.jl +++ b/test/downstream/test-plantbiophysics.jl @@ -2,6 +2,10 @@ #using Pkg #Pkg.develop("PlantSimEngine") #using PlantSimEngine + +using Pkg +#Pkg.add(url="https://github.com/VEZY/PlantBiophysics.jl#dev") +#Pkg.instantiate() using Statistics #using DataFrames #using CSV diff --git a/test/downstream/test-xpalm.jl b/test/downstream/test-xpalm.jl index c2708374d..52e7d2eba 100644 --- a/test/downstream/test-xpalm.jl +++ b/test/downstream/test-xpalm.jl @@ -2,10 +2,9 @@ #Pkg.develop("PlantSimEngine") #using PlantSimEngine -# no release of XPalm yet, so can't just add it to the .toml using Pkg -Pkg.add(url="https://github.com/PalmStudio/XPalm.jl") - +#Pkg.add(url="https://github.com/PalmStudio/XPalm.jl#dev") +#Pkg.instantiate() using Test using PlantMeteo#, MultiScaleTreeGraph #using CairoMakie, AlgebraOfGraphics @@ -15,15 +14,15 @@ using XPalm using BenchmarkTools function xpalm_default_param_create() - meteo = CSV.read("../XPalm.jl/0-data/Meteo_Nigeria_PR.txt", DataFrame) - meteo.duration = [Dates.Day(i[1:1]) for i in meteo.duration] + meteo = CSV.read(joinpath(dirname(dirname(pathof(XPalm))), "0-data", "meteo.csv"), DataFrame) + #meteo.duration = [Dates.Day(i[1:1]) for i in meteo.duration] m = Weather(meteo) out_vars = Dict{String,Any}( "Scene" => (:lai,), - # "Scene" => (:lai, :scene_leaf_area, :aPPFD, :TEff), + # "Scene" => (:LAI, :scene_leaf_area, :aPPFD, :TEff), # "Plant" => (:plant_age, :ftsw, :newPhytomerEmergence, :aPPFD, :plant_leaf_area, :carbon_assimilation, :carbon_offer_after_rm, :Rm, :TT_since_init, :TEff, :phytomer_count, :newPhytomerEmergence), - "Leaf" => (:Rm, :potential_area, :TT_since_init, :TEff, :A, :carbon_demand, :carbon_allocation,), + "Leaf" => (:Rm, :potential_area, :TT_since_init, :TEff, :biomass, :carbon_demand, :carbon_allocation,), # "Leaf" => (:Rm, :potential_area), # "Internode" => (:Rm, :carbon_allocation, :carbon_demand), "Male" => (:Rm,), @@ -32,18 +31,18 @@ function xpalm_default_param_create() ) # Example 1: Run the model with the default parameters (but output as a DataFrame): - palm = Palm(initiation_age=0, parameters=default_parameters()) - models = model_mapping(palm) + palm = XPalm.Palm(initiation_age=0, parameters=XPalm.default_parameters()) + models = XPalm.model_mapping(palm) return palm, models, out_vars, meteo end -function xpalm_default_param_run(palm, models, meteo, out_vars) +function xpalm_default_param_run(palm, models, out_vars, meteo) sim_outputs = PlantSimEngine.run!(palm.mtg, models, meteo, tracked_outputs=out_vars, executor=PlantSimEngine.SequentialEx(), check=false) return sim_outputs end function xpalm_default_param_convert_outputs(sim_outputs) - df = PlantSimEngine.convert_outputs(out, DataFrame, no_value=missing) + df = PlantSimEngine.convert_outputs(sim_outputs, DataFrame, no_value=missing) return df end @@ -51,7 +50,7 @@ end #=@testset "XPalm simple test" begin # default number of seconds is 5 b_XP = @benchmark xpalm_default_param_run() seconds = 120 - + #N = length(b_XP.times) @test mean(b_XP.times*1e-9) > 10 diff --git a/test/helper-functions.jl b/test/helper-functions.jl index 9cf3f8477..e4db3532d 100644 --- a/test/helper-functions.jl +++ b/test/helper-functions.jl @@ -1,9 +1,11 @@ # Simple helper functions that can be used in various tests here and there -function compare_outputs_modellist_mapping(filtered_outputs, graphsim) - outputs_df = convert_outputs(graphsim.outputs, DataFrame) +function compare_outputs_modellist_mapping(filtered_outputs, graphsim) + outputs_df_dict = convert_outputs(graphsim.outputs, DataFrame) + @assert length(outputs_df_dict) == 1 - outputs_df_outputs_only = select(outputs_df, Not([:timestep, :organ, :node])) + outputs_df = last(first(outputs_df_dict)) + outputs_df_outputs_only = select(outputs_df, Not([:timestep#=, :organ=#, :node])) models_df = DataFrame(filtered_outputs) models_df_sorted = models_df[:, sortperm(names(models_df))] @@ -12,7 +14,7 @@ function compare_outputs_modellist_mapping(filtered_outputs, graphsim) end # doesn't check for mtg equality -function compare_outputs_graphsim(graphsim, graphsim2) +function compare_outputs_graphsim_old(graphsim, graphsim2) outputs_df = convert_outputs(graphsim.outputs, DataFrame) outputs_df_sorted = outputs_df[:, sortperm(names(outputs_df))] @@ -21,6 +23,27 @@ function compare_outputs_graphsim(graphsim, graphsim2) return outputs_df_sorted == outputs2_df_sorted end +# doesn't check for mtg equality +function compare_outputs_graphsim(graphsim, graphsim2) + outputs_df_dict = convert_outputs(graphsim.outputs, DataFrame) + outputs2_df_dict = convert_outputs(graphsim2.outputs, DataFrame) + + if length(outputs_df_dict) != length(outputs2_df_dict) + return false + end + + for (organ, vals) in outputs2_df_dict + outputs_df_sorted = outputs_df_dict[organ][:, sortperm(names(outputs_df_dict[organ]))] + outputs2_df_sorted = outputs2_df_dict[organ][:, sortperm(names(outputs2_df_dict[organ]))] + + if outputs_df_sorted != outputs2_df_sorted + return false + end + end + + return true +end + function compare_outputs_modellists(filtered_outputs_1, filtered_outputs_2) models_df_1 = DataFrame(filtered_outputs_1) models_df_sorted_1 = models_df_1[:, sortperm(names(models_df_1))] diff --git a/test/test-corner-cases.jl b/test/test-corner-cases.jl index cc881a23d..e1c9ac48e 100644 --- a/test/test-corner-cases.jl +++ b/test/test-corner-cases.jl @@ -529,8 +529,9 @@ end vars = Dict{String,Any}("Leaf" => (:var1,)) out = run!(mtg, m, Atmosphere(T=20.0, Wind=1.0, Rh=0.65), tracked_outputs=vars, executor=SequentialEx()) - df = convert_outputs(out, DataFrame) - @test DataFrames.nrow(df) == 2 + df_dict = convert_outputs(out, DataFrame) + @test DataFrames.nrow(df_dict["Leaf"]) == 2 + @test DataFrames.ncol(df_dict["Leaf"]) == 3 end ########################## @@ -563,9 +564,8 @@ end sim = run!(mtg, mapping, meteo; tracked_outputs=outs) using DataFrames - df = convert_outputs(sim, DataFrame) - @test DataFrames.nrow(df) == PlantSimEngine.get_nsteps(meteo) - + df_dict = convert_outputs(sim, DataFrame) + @test DataFrames.nrow(df_dict["Default"]) == PlantSimEngine.get_nsteps(meteo) end diff --git a/test/test-mtg-dynamic.jl b/test/test-mtg-dynamic.jl index b767c391e..498f1d146 100644 --- a/test/test-mtg-dynamic.jl +++ b/test/test-mtg-dynamic.jl @@ -82,8 +82,8 @@ out = run!(sim,meteo) @test st["Internode"][1].TT_cu_emergence == 0.0 @test st["Internode"][end].TT_cu_emergence == 25.0 - out_df = convert_outputs(out, DataFrame) - @test unique(out_df[:, :organ]) |> sort == ["Internode", "Leaf", "Plant", "Soil"] - @test filter(row -> row.organ == "Internode", out_df)[:, :TT_cu_emergence] == [0.0, 0.0, 0.0, 0.0, 25.0] - @test filter(row -> row.organ == "Leaf", out_df)[:, :carbon_demand] == [0.5, 0.5, 0.75, 0.75, 0.75] + out_df_dict = convert_outputs(out, DataFrame) + @test collect(keys(out_df_dict)) |> sort == ["Internode", "Leaf", "Plant", "Soil"] + @test out_df_dict["Internode"][:, :TT_cu_emergence] == [0.0, 0.0, 0.0, 0.0, 25.0] + @test out_df_dict["Leaf"][:, :carbon_demand] == [0.5, 0.5, 0.75, 0.75, 0.75] end \ No newline at end of file diff --git a/test/test-mtg-multiscale-cyclic-dep.jl b/test/test-mtg-multiscale-cyclic-dep.jl index 134941e86..ca3258ea2 100644 --- a/test/test-mtg-multiscale-cyclic-dep.jl +++ b/test/test-mtg-multiscale-cyclic-dep.jl @@ -226,5 +226,16 @@ end ref_path = joinpath(pkgdir(PlantSimEngine), "test/references/ref_output_simulation.csv") # CSV.write(ref_path, sort(convert_outputs(out, DataFrame, no_value=missing), [:timestep, :node]), transform=(col, val) -> something(val, missing)) ref_df = CSV.read(ref_path, DataFrame) - @test isequal(sort(convert_outputs(out, DataFrame, no_value=missing), [:timestep, :node]), ref_df) + + @test sort(unique(ref_df.organ)) == sort(collect(keys(out))) + + out_df_dict = convert_outputs(out, DataFrame, no_value=missing) + + for organ in keys(out) + reduced_ref_df = ref_df[(ref_df.organ .== organ), Not(:organ)] + reduced_ref_df_no_missing = reduced_ref_df[:, any.(!ismissing, eachcol(reduced_ref_df))] + sorted_reduced_ref_df_no_missing = select(reduced_ref_df_no_missing, sort(propertynames(reduced_ref_df_no_missing))) + sorted_out_df_dict_organ = select(out_df_dict[organ], sort(propertynames(out_df_dict[organ]))) + @test sorted_reduced_ref_df_no_missing == sorted_out_df_dict_organ + end end \ No newline at end of file diff --git a/test/test-mtg-multiscale.jl b/test/test-mtg-multiscale.jl index d1113f46b..ecb34fd38 100644 --- a/test/test-mtg-multiscale.jl +++ b/test/test-mtg-multiscale.jl @@ -213,8 +213,10 @@ end outs_ = @test_logs (:info, "You requested outputs for organs Soil, Flowers, Leaf, but organs Flowers have no models.") (:info, "You requested outputs for variable non_existing_variable in organ Leaf, but it has no model.") PlantSimEngine.pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outs, nsteps, check=false) @test outs_ == Dict( - "Soil" => Dict(:node => [[], []], :soil_water_content => [[], []]), - "Leaf" => Dict(:carbon_assimilation => [[], []], :node => [[], []], :carbon_demand => [[], []]) + "Soil" => [Status(timestep = 1, node = MultiScaleTreeGraph.Node(NodeMTG("/", "Uninitialized", 0, 0)), soil_water_content = -Inf), + Status(timestep = 1, node = MultiScaleTreeGraph.Node(NodeMTG("/", "Uninitialized", 0, 0),), soil_water_content = -Inf)], + "Leaf" => [Status(timestep = 1, node = MultiScaleTreeGraph.Node(NodeMTG("/", "Uninitialized", 0, 0),), carbon_assimilation = -Inf, carbon_demand = -Inf), + Status(timestep = 1, node = MultiScaleTreeGraph.Node(NodeMTG("/", "Uninitialized", 0, 0),), carbon_assimilation = -Inf, carbon_demand = -Inf)] ) end @@ -689,26 +691,33 @@ end @test sim.statuses["Leaf"][1].var5 == 32.4806 @test sim.statuses["Leaf"][1].var8 ≈ 1321.0700490800002 atol = 1e-6 - @test sim.outputs["Leaf"][:carbon_demand] == [[0.5, 0.5], [0.5, 0.5]] - @test sim.outputs["Leaf"][:soil_water_content][1] == fill(sim.outputs["Soil"][:soil_water_content][1][1], 2) - @test sim.outputs["Leaf"][:soil_water_content][2] == fill(sim.outputs["Soil"][:soil_water_content][2][1], 2) + @test unique([sim.outputs["Leaf"][i][:carbon_demand] == 0.5 for i in 1:4]) == [1] + @test sim.outputs["Leaf"][1][:soil_water_content] == sim.outputs["Soil"][1][:soil_water_content] + @test sim.outputs["Leaf"][2][:soil_water_content] == sim.outputs["Soil"][2][:soil_water_content] - @test sim.outputs["Leaf"][:carbon_allocation] == sim.outputs["Internode"][:carbon_allocation] - @test sim.outputs["Plant"][:carbon_allocation][1][1][1] === sim.outputs["Internode"][:carbon_allocation][1][1] + @test all([sim.outputs["Leaf"][i][:carbon_allocation] == sim.outputs["Internode"][i][:carbon_allocation] for i in 1:4])==1 + @test sim.outputs["Plant"][1][:carbon_allocation][1] === sim.outputs["Internode"][1][:carbon_allocation] # Testing the outputs if transformed into a DataFrame: - outs = convert_outputs(out, DataFrame) + outs_df_dict = convert_outputs(out, DataFrame) - @test isa(outs, DataFrame) - @test size(outs) == (12, 7) + @test isa(outs_df_dict, Dict{String, DataFrame}) + @test size(outs_df_dict["Leaf"]) == (4, 6) + @test size(outs_df_dict["Internode"]) == (4, 3) + @test size(outs_df_dict["Soil"]) == (2, 3) + @test size(outs_df_dict["Plant"]) == (2, 2) - @test unique(outs.timestep) == [1, 2] - @test sort(unique(outs.organ)) == sort(collect(keys(out_vars))) - @test length(filter(x -> x !== nothing, outs.carbon_assimilation)) == length(filter(x -> x, traverse(mtg, node -> MultiScaleTreeGraph.scale(node) == 2))) - # a = status(out, TimeStepTable{Status}) - A = outputs(out, :carbon_assimilation) - @test A == outs.carbon_assimilation + @test sort(collect(keys(outs_df_dict))) == sort(collect(keys(out_vars))) + for organ in keys(outs_df_dict) + @test unique(outs_df_dict[organ][!, :timestep]) == [1, 2] + end + # output structure change makes this test obsolete without any trivial equivalent + #@test length(filter(x -> x !== nothing, outs.carbon_assimilation)) == length(filter(x -> x, traverse(mtg, node -> MultiScaleTreeGraph.scale(node) == 2))) + + # TODO, find some equivalent with new output structure + #A = outputs(out, :carbon_assimilation) + #@test A == outs.carbon_assimilation - A2 = outputs(out, 5) - @test A == A2 + #A2 = outputs(out, 5) + #@test A == A2 end \ No newline at end of file diff --git a/test/test-performance.jl b/test/test-performance.jl index e09541a2f..f11aee7c0 100644 --- a/test/test-performance.jl +++ b/test/test-performance.jl @@ -1,13 +1,13 @@ - + using BenchmarkTools using Dates PlantSimEngine.@process "sleep" verbose = false -struct ToySleepModel <: AbstractSleepModel +struct ToySleepModel <: AbstractSleepModel end -PlantSimEngine.inputs_(::ToySleepModel) = (a = -Inf,) +PlantSimEngine.inputs_(::ToySleepModel) = (a=-Inf,) PlantSimEngine.outputs_(::ToySleepModel) = NamedTuple() function PlantSimEngine.run!(m::ToySleepModel, models, status, meteo, constants=nothing, extra=nothing) @@ -18,27 +18,28 @@ end PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToySleepModel}) = PlantSimEngine.IsTimeStepIndependent() meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day) - nrows = nrow(meteo_day) - - vc = [0 for i in 1:nrows] +nrows = nrow(meteo_day) + +vc = [0 for i in 1:nrows] models1 = ModelList(process1=ToySleepModel(), status=(a=vc,)) models2 = ModelList(process1=ToySleepModel(), status=(a=vc,)) -@testset begin "Check number of threads" +@testset begin + "Check number of threads" nthr = Threads.nthreads() @test nthr > 1 - - t_seq = @benchmark run!(models1, meteo_day; executor = SequentialEx()) + + t_seq = @benchmark run!(models1, meteo_day; executor=SequentialEx()) #t_seq = run!(models1, meteo_day; executor = SequentialEx()) - med_time_seq = median(t_seq).time + med_time_seq = median(t_seq).time #time is in nanoseconds @test med_time_seq > nrows * 1000000 - t_mt = @benchmark run!(models2, meteo_day; executor = ThreadedEx()) + t_mt = @benchmark run!(models2, meteo_day; executor=ThreadedEx()) #t_mt = run!(models2, meteo_day; executor = ThreadedEx()) - med_time_mt = median(t_mt).time + med_time_mt = median(t_mt).time @test med_time_mt > nrows * 1000000 / nthr @@ -52,13 +53,13 @@ models2 = ModelList(process1=ToySleepModel(), status=(a=vc,)) #end # unsure how to recover outputs in benchmarked expressions to compare them, rerun the functions as a workaround for now - @test run!(models1, meteo_day; executor = SequentialEx()) == run!(models2, meteo_day; executor = ThreadedEx()) + @test run!(models1, meteo_day; executor=SequentialEx()) == run!(models2, meteo_day; executor=ThreadedEx()) end # TODO make sure a mt test with nthreads == 1 also is tested and is correct @testset "Single and multi-threaded output consistency" begin nthr = Threads.nthreads() - @test nthr == 4 + @test nthr > 1 using Dates meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day) @@ -70,18 +71,18 @@ end ) tracked_outputs = (:LAI,) - + out_seq, out_mt = run_single_and_multi_thread_modellist(models, tracked_outputs, meteo_day) @test compare_outputs_modellists(out_seq, out_mt) modellists, status_tuples, outs_vectors = get_modellist_bank() meteos_all = get_simple_meteo_bank() - + # First meteo only has one timestep meteos = meteos_all[2:length(meteos_all)] for i in 1:length(modellists) - #i = 1 + #i = 1 modellist = modellists[i] status_tuple = status_tuples[i] outs_vector = outs_vectors[i] @@ -89,10 +90,11 @@ end for j in 1:length(meteos) meteo = meteos[j] for k in 1:length(outs_vector) - #k = 1 - out_tuple = outs_vector[k] - - try out_st, out_mt = run_single_and_multi_thread_modellist(modellist, out_tuple, meteo) + #k = 1 + out_tuple = outs_vector[k] + + try + out_st, out_mt = run_single_and_multi_thread_modellist(modellist, out_tuple, meteo) @test compare_outputs_modellists(out_st, out_mt) catch e #print(i," ", j, " ", k)