From b326491581b802608dccc9497d7df9f73aee85fe Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Wed, 26 Mar 2025 16:18:07 +0100 Subject: [PATCH 01/24] Attempt at changing outputs structure for performance reasons. Some type issues remaining --- src/mtg/GraphSimulation.jl | 1 + src/mtg/initialisation.jl | 3 +- src/mtg/save_results.jl | 149 +++++++++++++++++++++++++++++++++++++ src/run.jl | 4 +- 4 files changed, 154 insertions(+), 3 deletions(-) diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl index 9f243aeb3..95ff06b78 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) diff --git a/src/mtg/initialisation.jl b/src/mtg/initialisation.jl index 16be962fb..a3314fd8c 100644 --- a/src/mtg/initialisation.jl +++ b/src/mtg/initialisation.jl @@ -326,7 +326,8 @@ function init_simulation(mtg, mapping; nsteps=1, outputs=nothing, type_promotion models = Dict(first(m) => parse_models(get_models(last(m))) for m in mapping) - outputs = pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) + outputs = pre_allocate_outputs_2(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) + outputs_index = Dict{String, Int}(s => 0 for s in keys(outputs)) return (; mtg, statuses, status_templates, reverse_multiscale_mapping, vars_need_init, dependency_graph=dep(mapping, verbose=verbose), models, outputs) end \ No newline at end of file diff --git a/src/mtg/save_results.jl b/src/mtg/save_results.jl index a024e1468..6fb3ffd44 100644 --- a/src/mtg/save_results.jl +++ b/src/mtg/save_results.jl @@ -203,6 +203,155 @@ function pre_allocate_outputs(statuses, statuses_template, reverse_multiscale_ma end +function pre_allocate_outputs_2(statuses, statuses_template, reverse_multiscale_mapping, vars_need_init, outs, nsteps; type_promotion=nothing, check=true) + outs_ = Dict{String,Vector{Symbol}}() + + # default behaviour : track everything + if isnothing(outs) + for organ in keys(statuses) + outs_[organ] = [keys(statuses_template[organ])...] + end + # No outputs requested by user : just return the timestep and node + elseif length(outs) == 0 + for i in keys(statuses) + outs_[i] = [] + end + else + for i in keys(outs) # i = "Plant" + @assert isa(outs[i], Tuple{Vararg{Symbol}}) """Outputs for scale $i should be a tuple of symbols, *e.g.* `"$i" => (:a, :b)`, found `"$i" => $(outs[i])` instead.""" + outs_[i] = [outs[i]...] + 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_)) + not_in_statuses = setdiff(keys(outs_), keys(statuses)) + e = string( + "You requested outputs for organs ", + join(keys(outs_), ", "), + ", but organs ", + join(not_in_statuses, ", "), + " have no models." + ) + + if check + error(e) + else + @info e + [delete!(outs_, i) for i in not_in_statuses] + end + end + + # Checking that variables in outputs exist in the statuses, and adding the :node variable: + for (organ, vars) in outs_ # organ = "Leaf"; vars = outs_[organ] + if length(statuses[organ]) == 0 + # The organ is not found in the mtg, we return an info and get along (it might be created during the simulation): + check && @info "You required outputs for organ $organ, but this organ is not found in the provided MTG at this point." + end + if !all(i in collect(keys(statuses_[organ])) for i in vars) + not_in_statuses = (setdiff(vars, keys(statuses_[organ]))...,) + plural = length(not_in_statuses) == 1 ? "" : "s" + e = string( + "You requested outputs for variable", plural, " ", + join(not_in_statuses, ", "), + " in organ $organ, but ", + length(not_in_statuses) == 1 ? "it has no model." : "they have no models." + ) + if check + error(e) + else + @info e + existing_vars_requested = setdiff(outs_[organ], not_in_statuses) + if length(existing_vars_requested) == 0 + # None of the variables requested by the user exist at this scale for this set of models + delete!(outs_, organ) + else + # Some still exist, we only use the ones that do: + outs_[organ] = [existing_vars_requested...] + end + end + end + + if :node ∉ outs_[organ] + push!(outs_[organ], :node) + end + end + + node_types = [] + for o in keys(statuses) + if length(statuses[o]) > 0 + push!(node_types, typeof(statuses[o][1].node)) + end + end + + node_type = unique(node_types) + @assert length(node_type) == 1 "All plant graph nodes should have the same type, found $(unique(node_type))." + node_type = only(node_type) + + preallocated_outputs = Dict{String, TimeStepTable}() + + types = Vector{DataType}() + for organ in keys(outs_) + + 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[organ]] + + push!(types, node_type) + + # contains :node + symbols_tuple = (outs_[organ]...,) + values_tuple = (values..., MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", "Uninitialized", 0, 0),)) + named_tuple = NamedTuple{symbols_tuple,Tuple{types...,}} + + #data = fill(Status(;zip(symbols_tuple, values_tuple)...), nsteps) #Vector{named_tuple}() + data = Status{named_tuple}[] + sizehint!(data, nsteps) + + # create one dummy element otherwise can't recover the NamedTuple keys + push!(data, Status(;zip(symbols_tuple, values_tuple))) + preallocated_outputs[organ] = TimeStepTable(data) + # remove that dummy element without + empty!(preallocated_outputs[organ].ts) + end + + return preallocated_outputs +end + +# TODO better sizehint estimation to reduce reallocations +function save_results_2!(object::GraphSimulation, i) + outs = outputs(object) + + if length(outs) == 0 + return + end + + statuses = status(object) + + for organ in keys(outs) + + if length(outs[organ]) == 0 + continue + end + + #named_tuple_type = eltype(outs[organ]) + #data = fill(get_index_raw(outs[organ],1), length(statuses[organ])) #Vector{named_tuple_type}(undef, length(statuses[organ])) + data = fill(get_index_raw(outs[organ],1), length(statuses[organ])) + timestep_filtered_outputs = TimeStepTable(data) + + for (i,status) in enumerate(statuses[organ]) + for var in keys(outs[organ]) + timestep_filtered_outputs[i][var] = status[var] + end + end + + append!(outs[organ], timestep_filtered_outputs) + end +end + +function Base.append!(ts::TimeStepTable, x::TimeStepTable) + append!(getfield(ts, :ts), getfield(x, :ts)) +end + """ save_results!(object::GraphSimulation, i) diff --git a/src/run.jl b/src/run.jl index 98362eeeb..49c2004c8 100644 --- a/src/run.jl +++ b/src/run.jl @@ -410,7 +410,7 @@ function run!( for (process_key, dependency_node) in roots run_node_multiscale!(object, dependency_node, 1, models, meteo, constants, object, check, executor) end - save_results!(object, 1) + save_results_2!(object, 1) else for (i, meteo_i) in enumerate(Tables.rows(meteo)) roots = collect(dep_graph.roots) @@ -418,7 +418,7 @@ function run!( run_node_multiscale!(object, dependency_node, i, models, meteo_i, constants, object, check, executor) end # At the end of the time-step, we save the results of the simulation in the object: - save_results!(object, i) + save_results_2!(object, i) end end From b83830a296689dffe3d006675741df34f786a9e2 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Thu, 3 Apr 2025 14:51:31 +0200 Subject: [PATCH 02/24] Functional outputs structure rework (notable performance and memory usage gain on the XPalm side). Currently deactivated. Some details will need fixing, many tests will be broken (for both PSE and XPalm), documentation not up-to-date --- src/mtg/GraphSimulation.jl | 24 ++++++++------------ src/mtg/initialisation.jl | 7 +++--- src/mtg/save_results.jl | 46 +++++++++++++++++++++++--------------- src/run.jl | 10 +++++++-- 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl index 95ff06b78..c87977715 100644 --- a/src/mtg/GraphSimulation.jl +++ b/src/mtg/GraphSimulation.jl @@ -114,20 +114,6 @@ function convert_outputs(outs::Dict{String,O} where O, sink; refvectors=false, n 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 - - sizehint!(t, size_hint)=# for (organ, vars) in outs # organ = "Leaf"; vars = outs[organ] var_names = setdiff(collect(keys(vars)), [:node]) @@ -169,4 +155,12 @@ end function convert_outputs(out::TimeStepTable{T} where T, sink) @assert Tables.istable(sink) "The sink argument must be compatible with the Tables.jl interface (`Tables.istable(sink)` must return `true`, *e.g.* `DataFrame`)" return sink(out) -end \ No newline at end of file +end + +function convert_outputs_2(outs::Dict{String,O} where O, sink; refvectors=false, no_value=nothing) + ret = Dict{String, sink}() + for (organ, vector_named_tuple) in outs + ret[organ] = sink(vector_named_tuple) + end + return ret +end diff --git a/src/mtg/initialisation.jl b/src/mtg/initialisation.jl index a3314fd8c..c2401801a 100644 --- a/src/mtg/initialisation.jl +++ b/src/mtg/initialisation.jl @@ -326,8 +326,9 @@ function init_simulation(mtg, mapping; nsteps=1, outputs=nothing, type_promotion models = Dict(first(m) => parse_models(get_models(last(m))) for m in mapping) - outputs = pre_allocate_outputs_2(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) + outputs = pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) + #outputs = pre_allocate_outputs_2(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) - outputs_index = Dict{String, Int}(s => 0 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 = 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 6fb3ffd44..48bded610 100644 --- a/src/mtg/save_results.jl +++ b/src/mtg/save_results.jl @@ -288,7 +288,7 @@ function pre_allocate_outputs_2(statuses, statuses_template, reverse_multiscale_ @assert length(node_type) == 1 "All plant graph nodes should have the same type, found $(unique(node_type))." node_type = only(node_type) - preallocated_outputs = Dict{String, TimeStepTable}() + preallocated_outputs = Dict{String, Vector}() types = Vector{DataType}() for organ in keys(outs_) @@ -304,14 +304,16 @@ function pre_allocate_outputs_2(statuses, statuses_template, reverse_multiscale_ named_tuple = NamedTuple{symbols_tuple,Tuple{types...,}} #data = fill(Status(;zip(symbols_tuple, values_tuple)...), nsteps) #Vector{named_tuple}() - data = Status{named_tuple}[] - sizehint!(data, nsteps) + dummy_status = #=Status=#(;zip(symbols_tuple, values_tuple)...) + data = typeof(dummy_status)[] + resize!(data, nsteps) + #sizehint!(data, nsteps) # create one dummy element otherwise can't recover the NamedTuple keys - push!(data, Status(;zip(symbols_tuple, values_tuple))) - preallocated_outputs[organ] = TimeStepTable(data) + data[1] = dummy_status + preallocated_outputs[organ] = data #TimeStepTable(data) # remove that dummy element without - empty!(preallocated_outputs[organ].ts) + #empty!(getfield(preallocated_outputs[organ], :ts)) end return preallocated_outputs @@ -326,32 +328,40 @@ function save_results_2!(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] + + # this can be made much more conservative with the right heuristic, or with user hints + len = length(outs[organ]) + if length(statuses[organ]) + index > len + resize!(outs[organ], 2*index) + end + + tracked_outputs = keys(outs[organ][1]) + #named_tuple_type = eltype(outs[organ]) #data = fill(get_index_raw(outs[organ],1), length(statuses[organ])) #Vector{named_tuple_type}(undef, length(statuses[organ])) - data = fill(get_index_raw(outs[organ],1), length(statuses[organ])) - timestep_filtered_outputs = TimeStepTable(data) + #data = fill(get_index_raw(outs[organ],1), length(statuses[organ])) + #timestep_filtered_outputs = TimeStepTable(data) for (i,status) in enumerate(statuses[organ]) - for var in keys(outs[organ]) - timestep_filtered_outputs[i][var] = status[var] - end + #for var in keys(outs[organ]) + #timestep_filtered_outputs[i][var] = status[var] + outs[organ][index] = (;zip(tracked_outputs, [status[var] for var in tracked_outputs])...) + #end + index += 1 end - - append!(outs[organ], timestep_filtered_outputs) + indexes[organ] = index + #append!(outs[organ], timestep_filtered_outputs) end end -function Base.append!(ts::TimeStepTable, x::TimeStepTable) - append!(getfield(ts, :ts), getfield(x, :ts)) -end - """ save_results!(object::GraphSimulation, i) diff --git a/src/run.jl b/src/run.jl index 49c2004c8..62fc40051 100644 --- a/src/run.jl +++ b/src/run.jl @@ -410,7 +410,8 @@ function run!( for (process_key, dependency_node) in roots run_node_multiscale!(object, dependency_node, 1, models, meteo, constants, object, check, executor) end - save_results_2!(object, 1) + #save_results_2!(object, 1) + save_results!(object, 1) else for (i, meteo_i) in enumerate(Tables.rows(meteo)) roots = collect(dep_graph.roots) @@ -418,10 +419,15 @@ function run!( run_node_multiscale!(object, dependency_node, i, models, meteo_i, constants, object, check, executor) end # At the end of the time-step, we save the results of the simulation in the object: - save_results_2!(object, i) + #save_results_2!(object, i) + save_results!(object, i) end end + #for (organ, index) in object.outputs_index + # resize!(outputs(object)[organ], index - 1) + #end + return outputs(object) end From dc171cc14542777d399e6ae91126027599ce3f1c Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Thu, 3 Apr 2025 15:08:52 +0200 Subject: [PATCH 03/24] Unsaved file, oops --- src/mtg/GraphSimulation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl index c87977715..d3caa5431 100644 --- a/src/mtg/GraphSimulation.jl +++ b/src/mtg/GraphSimulation.jl @@ -25,7 +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} + #outputs_index::Dict{String, Int} end function GraphSimulation(graph, mapping; nsteps=1, outputs=nothing, type_promotion=nothing, check=true, verbose=false) From 9773528d673d10fb289ac12902ae55e63263b165 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Fri, 4 Apr 2025 16:42:04 +0200 Subject: [PATCH 04/24] Fix refvector removal and node conversion to int when transforming outputs. Should probably document some of the differences with single-scale conversion (CSV friendly, no refvectors) --- src/mtg/GraphSimulation.jl | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl index d3caa5431..2d1abedb2 100644 --- a/src/mtg/GraphSimulation.jl +++ b/src/mtg/GraphSimulation.jl @@ -143,12 +143,13 @@ function convert_outputs(outs::Dict{String,O} where O, sink; refvectors=false, n return sink(t) 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] + Tables.columns(convert_outputs_2(outs, Vector{NamedTuple}))[key] end function outputs(outs::Dict{String, O} where O, i::T) where {T<:Integer} - Tables.columns(convert_outputs(outs, Vector{NamedTuple}))[i] + Tables.columns(convert_outputs_2(outs, Vector{NamedTuple}))[i] end # ModelLists now return outputs as a TimeStepTable{Status}, conversion is straightforward @@ -160,7 +161,31 @@ end function convert_outputs_2(outs::Dict{String,O} where O, sink; refvectors=false, no_value=nothing) ret = Dict{String, sink}() for (organ, vector_named_tuple) in outs - ret[organ] = sink(vector_named_tuple) + # remove RefVector variables + refv = () + if length(vector_named_tuple) > 0 + for (var, val) in pairs(vector_named_tuple[1]) + if !refvectors && isa(val, RefVector) + refv = (refv..., var) + end + if var == :node + refv = (refv..., var) + end + end + end + + # Another, probably better way would be to just create the DataFrame and then remove the RefVector columns, hmm + # Get the new NamedTuple type + refv_nt = NamedTuple{refv} + # 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(vector_named_tuple)) + + for i in 1:length(vector_named_tuple) + filtered_vector_named_tuple[i] = (;node=MultiScaleTreeGraph.node_id(vector_named_tuple[i].node), Base.structdiff(vector_named_tuple[i], refv_nt)...) + end + + ret[organ] = sink(filtered_vector_named_tuple) end return ret end From 592633d4ed18ef909bc79ea5469046ca2ec50694 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Fri, 4 Apr 2025 16:43:11 +0200 Subject: [PATCH 05/24] Reactivate output structure changes --- src/mtg/GraphSimulation.jl | 2 +- src/mtg/initialisation.jl | 8 +++--- src/mtg/save_results.jl | 59 +++++++++++++++++++++----------------- src/run.jl | 14 ++++----- 4 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl index 2d1abedb2..bcf3acbe8 100644 --- a/src/mtg/GraphSimulation.jl +++ b/src/mtg/GraphSimulation.jl @@ -25,7 +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} + outputs_index::Dict{String, Int} end function GraphSimulation(graph, mapping; nsteps=1, outputs=nothing, type_promotion=nothing, check=true, verbose=false) diff --git a/src/mtg/initialisation.jl b/src/mtg/initialisation.jl index c2401801a..436a7eef8 100644 --- a/src/mtg/initialisation.jl +++ b/src/mtg/initialisation.jl @@ -326,9 +326,9 @@ function init_simulation(mtg, mapping; nsteps=1, outputs=nothing, type_promotion models = Dict(first(m) => parse_models(get_models(last(m))) for m in mapping) - outputs = pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) - #outputs = pre_allocate_outputs_2(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) + #outputs = pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) + outputs = pre_allocate_outputs_2(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) - #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) + 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 48bded610..3367f358b 100644 --- a/src/mtg/save_results.jl +++ b/src/mtg/save_results.jl @@ -222,6 +222,20 @@ function pre_allocate_outputs_2(statuses, statuses_template, reverse_multiscale_ 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_)) @@ -293,27 +307,26 @@ function pre_allocate_outputs_2(statuses, statuses_template, reverse_multiscale_ types = Vector{DataType}() for organ in keys(outs_) - 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[organ]] + outs_no_node = filter(x -> x != :node, outs_[organ]) - push!(types, node_type) + #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 = (outs_[organ]...,) - values_tuple = (values..., MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", "Uninitialized", 0, 0),)) - named_tuple = NamedTuple{symbols_tuple,Tuple{types...,}} + symbols_tuple = (:timestep, :node, outs_no_node...,) + values_tuple = (1, MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", "Uninitialized", 0, 0),), values...,) + #named_tuple = NamedTuple{symbols_tuple,Tuple{Int, types...,}} - #data = fill(Status(;zip(symbols_tuple, values_tuple)...), nsteps) #Vector{named_tuple}() - dummy_status = #=Status=#(;zip(symbols_tuple, values_tuple)...) + dummy_status = (;zip(symbols_tuple, values_tuple)...) data = typeof(dummy_status)[] resize!(data, nsteps) - #sizehint!(data, nsteps) - # create one dummy element otherwise can't recover the NamedTuple keys + # Create one dummy element otherwise when saving the results it's a pain to recover the NamedTuple type from an empty array + # There's probably a better way to do this, but it'll do for now data[1] = dummy_status - preallocated_outputs[organ] = data #TimeStepTable(data) - # remove that dummy element without - #empty!(getfield(preallocated_outputs[organ], :ts)) + preallocated_outputs[organ] = data end return preallocated_outputs @@ -339,26 +352,18 @@ function save_results_2!(object::GraphSimulation, i) # this can be made much more conservative with the right heuristic, or with user hints len = length(outs[organ]) - if length(statuses[organ]) + index > len - resize!(outs[organ], 2*index) + if length(statuses[organ]) + index > len + min_required = max(length(statuses[organ]) + index - len, index) + resize!(outs[organ], 2*min_required) end - tracked_outputs = keys(outs[organ][1]) + tracked_outputs = filter(i -> i != :timestep, keys(outs[organ][1])) - #named_tuple_type = eltype(outs[organ]) - #data = fill(get_index_raw(outs[organ],1), length(statuses[organ])) #Vector{named_tuple_type}(undef, length(statuses[organ])) - #data = fill(get_index_raw(outs[organ],1), length(statuses[organ])) - #timestep_filtered_outputs = TimeStepTable(data) - - for (i,status) in enumerate(statuses[organ]) - #for var in keys(outs[organ]) - #timestep_filtered_outputs[i][var] = status[var] - outs[organ][index] = (;zip(tracked_outputs, [status[var] for var in tracked_outputs])...) - #end + for status in statuses[organ] + outs[organ][index] = (;timestep=i,zip(tracked_outputs, [status[var] for var in tracked_outputs])...) index += 1 end indexes[organ] = index - #append!(outs[organ], timestep_filtered_outputs) end end diff --git a/src/run.jl b/src/run.jl index 62fc40051..35347cb4c 100644 --- a/src/run.jl +++ b/src/run.jl @@ -410,8 +410,8 @@ function run!( for (process_key, dependency_node) in roots run_node_multiscale!(object, dependency_node, 1, models, meteo, constants, object, check, executor) end - #save_results_2!(object, 1) - save_results!(object, 1) + save_results_2!(object, 1) + #save_results!(object, 1) else for (i, meteo_i) in enumerate(Tables.rows(meteo)) roots = collect(dep_graph.roots) @@ -419,14 +419,14 @@ function run!( run_node_multiscale!(object, dependency_node, i, models, meteo_i, constants, object, check, executor) end # At the end of the time-step, we save the results of the simulation in the object: - #save_results_2!(object, i) - save_results!(object, i) + save_results_2!(object, i) + #save_results!(object, i) end end - #for (organ, index) in object.outputs_index - # resize!(outputs(object)[organ], index - 1) - #end + for (organ, index) in object.outputs_index + resize!(outputs(object)[organ], index - 1) + end return outputs(object) end From 838521ccfd054911d7ecf8ecb95aa362a52a80b3 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Fri, 4 Apr 2025 16:43:43 +0200 Subject: [PATCH 06/24] Adjust tests to new API changes --- test/helper-functions.jl | 31 +++++++++++++++++--- test/test-corner-cases.jl | 10 +++---- test/test-mtg-dynamic.jl | 8 +++--- test/test-mtg-multiscale-cyclic-dep.jl | 13 ++++++++- test/test-mtg-multiscale.jl | 39 +++++++++++++++----------- 5 files changed, 71 insertions(+), 30 deletions(-) diff --git a/test/helper-functions.jl b/test/helper-functions.jl index 9cf3f8477..b148c003b 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 = PlantSimEngine.convert_outputs_2(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 = PlantSimEngine.convert_outputs_2(graphsim.outputs, DataFrame) + outputs2_df_dict = PlantSimEngine.convert_outputs_2(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..902385753 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 = PlantSimEngine.convert_outputs_2(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 = PlantSimEngine.convert_outputs_2(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..7fe624d5b 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 = PlantSimEngine.convert_outputs_2(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..bc15cd7b5 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 = PlantSimEngine.convert_outputs_2(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..0bccb4acc 100644 --- a/test/test-mtg-multiscale.jl +++ b/test/test-mtg-multiscale.jl @@ -689,26 +689,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 = PlantSimEngine.convert_outputs_2(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"]) == (6, 6) + @test size(outs_df_dict["Internode"]) == (6, 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 From 83de9cfe46d86a65da38e03386f7cbe1fb6b030c Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Fri, 4 Apr 2025 16:44:28 +0200 Subject: [PATCH 07/24] Adjust docs to new API changes. Need to fix benchmarks and double-check docs --- docs/src/index.md | 6 +++--- docs/src/multiscale/multiscale.md | 6 +++--- docs/src/multiscale/multiscale_example_3.md | 12 +++++++++--- docs/src/multiscale/single_to_multiscale.md | 12 +++++------- .../floating_point_accumulation_error.md | 7 ++----- 5 files changed, 22 insertions(+), 21 deletions(-) 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_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 ``` From 80e854a73d746eaaa03d6413a04ee9316f379eff Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Fri, 4 Apr 2025 17:08:06 +0200 Subject: [PATCH 08/24] Documentation : output structure discussion also needed updating. Cleanup still required. --- .../multiscale/multiscale_considerations.md | 70 +++++++++++++------ 1 file changed, 48 insertions(+), 22 deletions(-) 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. From bc5d7d1b22a252fd6b708a6d9cc1fbf5eddefc16 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Fri, 4 Apr 2025 17:13:01 +0200 Subject: [PATCH 09/24] Fix test that still had a broken component --- test/test-mtg-multiscale.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-mtg-multiscale.jl b/test/test-mtg-multiscale.jl index 0bccb4acc..aba196dc0 100644 --- a/test/test-mtg-multiscale.jl +++ b/test/test-mtg-multiscale.jl @@ -700,8 +700,8 @@ end outs_df_dict = PlantSimEngine.convert_outputs_2(out, DataFrame) @test isa(outs_df_dict, Dict{String, DataFrame}) - @test size(outs_df_dict["Leaf"]) == (6, 6) - @test size(outs_df_dict["Internode"]) == (6, 3) + @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) From 7fa2d447b2df8ffefd384e6c9040e29b998f9368 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Mon, 7 Apr 2025 11:36:46 +0200 Subject: [PATCH 10/24] Awkward change to adjust for different node types (can't use the same approach as the previous output preallocation function), to fix test-mtg in PBP, the only test making use of MutableNodeMTG --- src/mtg/save_results.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mtg/save_results.jl b/src/mtg/save_results.jl index 3367f358b..4b7278d26 100644 --- a/src/mtg/save_results.jl +++ b/src/mtg/save_results.jl @@ -316,7 +316,7 @@ function pre_allocate_outputs_2(statuses, statuses_template, reverse_multiscale_ # contains :node symbols_tuple = (:timestep, :node, outs_no_node...,) - values_tuple = (1, MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", "Uninitialized", 0, 0),), values...,) + values_tuple = (1, MultiScaleTreeGraph.Node((node_type.parameters[1])("/", "Uninitialized", 0, 0),), values...,) #named_tuple = NamedTuple{symbols_tuple,Tuple{Int, types...,}} dummy_status = (;zip(symbols_tuple, values_tuple)...) From 02c4023c76727f0be9eb1d55787213c27f5011e9 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Wed, 9 Apr 2025 16:04:52 +0200 Subject: [PATCH 11/24] Point the downstream yml to the related PBP/XPalm branches, for now --- .github/workflows/Integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Integration.yml b/.github/workflows/Integration.yml index 4d0671497..8f9ebd356 100644 --- a/.github/workflows/Integration.yml +++ b/.github/workflows/Integration.yml @@ -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: PSE-multiscale-outputs-structure-changes} + - {user: VEZY, repo: PlantBioPhysics.jl, branch: PSE-multiscale-outputs-structure-changes} steps: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v2 From 45f0ec51bb58cfa1d00378024c8cdb6d48268fd0 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Wed, 9 Apr 2025 16:37:22 +0200 Subject: [PATCH 12/24] Cleanup code somewhat, fix resizing issues exposed by tests. All tests now pass for PSE, XPalm and PBP. Performance should be adequate, to be confirmed, possible light loss for XPalm. Benchmarks and documentation still need work. --- src/mtg/GraphSimulation.jl | 107 ++++---------- src/mtg/initialisation.jl | 3 +- src/mtg/save_results.jl | 189 ++++++++----------------- src/run.jl | 8 +- test/helper-functions.jl | 6 +- test/test-corner-cases.jl | 4 +- test/test-mtg-dynamic.jl | 2 +- test/test-mtg-multiscale-cyclic-dep.jl | 2 +- test/test-mtg-multiscale.jl | 8 +- 9 files changed, 105 insertions(+), 224 deletions(-) diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl index bcf3acbe8..37874e12e 100644 --- a/src/mtg/GraphSimulation.jl +++ b/src/mtg/GraphSimulation.jl @@ -90,81 +90,15 @@ 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 - end - variables_names_types_dict[k] = Union{variables_names_types_dict[k],v} - end - end - - # 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)...}}[] - - 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) - 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) -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_2(outs, Vector{NamedTuple}))[key] -end - -function outputs(outs::Dict{String, O} where O, i::T) where {T<:Integer} - Tables.columns(convert_outputs_2(outs, Vector{NamedTuple}))[i] -end - -# ModelLists now return outputs as a TimeStepTable{Status}, conversion is straightforward -function convert_outputs(out::TimeStepTable{T} where T, sink) - @assert Tables.istable(sink) "The sink argument must be compatible with the Tables.jl interface (`Tables.istable(sink)` must return `true`, *e.g.* `DataFrame`)" - return sink(out) -end - -function convert_outputs_2(outs::Dict{String,O} where O, sink; refvectors=false, no_value=nothing) ret = Dict{String, sink}() - for (organ, vector_named_tuple) in outs + for (organ, status_vector) in outs # remove RefVector variables refv = () - if length(vector_named_tuple) > 0 - for (var, val) in pairs(vector_named_tuple[1]) + if length(status_vector) > 0 + for (var, val) in pairs(status_vector[1]) if !refvectors && isa(val, RefVector) refv = (refv..., var) end @@ -173,19 +107,38 @@ function convert_outputs_2(outs::Dict{String,O} where O, sink; refvectors=false, end end end - - # Another, probably better way would be to just create the DataFrame and then remove the RefVector columns, hmm + # Get the new NamedTuple type refv_nt = NamedTuple{refv} + + # 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]) + # 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(vector_named_tuple)) + 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 i in 1:length(vector_named_tuple) - filtered_vector_named_tuple[i] = (;node=MultiScaleTreeGraph.node_id(vector_named_tuple[i].node), Base.structdiff(vector_named_tuple[i], refv_nt)...) + 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 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 + +function outputs(outs::Dict{String, O} where O, i::T) where {T<:Integer} + Tables.columns(convert_outputs(outs, Vector{NamedTuple}))[i] +end + +# ModelLists now return outputs as a TimeStepTable{Status}, conversion is straightforward +function convert_outputs(out::TimeStepTable{T} where T, sink) + @assert Tables.istable(sink) "The sink argument must be compatible with the Tables.jl interface (`Tables.istable(sink)` must return `true`, *e.g.* `DataFrame`)" + return sink(out) +end \ No newline at end of file diff --git a/src/mtg/initialisation.jl b/src/mtg/initialisation.jl index 436a7eef8..57aef81cf 100644 --- a/src/mtg/initialisation.jl +++ b/src/mtg/initialisation.jl @@ -326,8 +326,7 @@ function init_simulation(mtg, mapping; nsteps=1, outputs=nothing, type_promotion models = Dict(first(m) => parse_models(get_models(last(m))) for m in mapping) - #outputs = pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) - outputs = pre_allocate_outputs_2(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) + outputs = pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) 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) diff --git a/src/mtg/save_results.jl b/src/mtg/save_results.jl index 4b7278d26..67b569148 100644 --- a/src/mtg/save_results.jl +++ b/src/mtg/save_results.jl @@ -109,101 +109,8 @@ 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}}() - - # default behaviour : track everything - if isnothing(outs) - for organ in keys(statuses) - outs_[organ] = [keys(statuses_template[organ])...] - end - # No outputs requested by user : just return the timestep and node - elseif length(outs) == 0 - for i in keys(statuses) - outs_[i] = [] - end - else - for i in keys(outs) # i = "Plant" - @assert isa(outs[i], Tuple{Vararg{Symbol}}) """Outputs for scale $i should be a tuple of symbols, *e.g.* `"$i" => (:a, :b)`, found `"$i" => $(outs[i])` instead.""" - outs_[i] = [outs[i]...] - 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_)) - not_in_statuses = setdiff(keys(outs_), keys(statuses)) - e = string( - "You requested outputs for organs ", - join(keys(outs_), ", "), - ", but organs ", - join(not_in_statuses, ", "), - " have no models." - ) - if check - error(e) - else - @info e - [delete!(outs_, i) for i in not_in_statuses] - end - end - - # Checking that variables in outputs exist in the statuses, and adding the :node variable: - for (organ, vars) in outs_ # organ = "Leaf"; vars = outs_[organ] - if length(statuses[organ]) == 0 - # The organ is not found in the mtg, we return an info and get along (it might be created during the simulation): - check && @info "You required outputs for organ $organ, but this organ is not found in the provided MTG at this point." - end - if !all(i in collect(keys(statuses_[organ])) for i in vars) - not_in_statuses = (setdiff(vars, keys(statuses_[organ]))...,) - plural = length(not_in_statuses) == 1 ? "" : "s" - e = string( - "You requested outputs for variable", plural, " ", - join(not_in_statuses, ", "), - " in organ $organ, but ", - length(not_in_statuses) == 1 ? "it has no model." : "they have no models." - ) - if check - error(e) - else - @info e - existing_vars_requested = setdiff(outs_[organ], not_in_statuses) - if length(existing_vars_requested) == 0 - # None of the variables requested by the user exist at this scale for this set of models - delete!(outs_, organ) - else - # Some still exist, we only use the ones that do: - outs_[organ] = [existing_vars_requested...] - end - end - end - - if :node ∉ outs_[organ] - push!(outs_[organ], :node) - 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 - push!(node_types, typeof(statuses[o][1].node)) - end - end - - node_type = unique(node_types) - @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}. -end - - -function pre_allocate_outputs_2(statuses, statuses_template, reverse_multiscale_mapping, vars_need_init, outs, nsteps; type_promotion=nothing, check=true) +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}}() # default behaviour : track everything @@ -302,8 +209,13 @@ function pre_allocate_outputs_2(statuses, statuses_template, reverse_multiscale_ @assert length(node_type) == 1 "All plant graph nodes should have the same type, found $(unique(node_type))." node_type = only(node_type) + # 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_) @@ -316,24 +228,31 @@ function pre_allocate_outputs_2(statuses, statuses_template, reverse_multiscale_ # 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...,) - #named_tuple = NamedTuple{symbols_tuple,Tuple{Int, types...,}} + # 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(dummy_status)[] + data = typeof(Status(dummy_status))[] resize!(data, nsteps) - - # Create one dummy element otherwise when saving the results it's a pain to recover the NamedTuple type from an empty array - # There's probably a better way to do this, but it'll do for now - data[1] = dummy_status + + for ii in 1:nsteps + data[ii] = Status(dummy_status) + end preallocated_outputs[organ] = data end - - return preallocated_outputs end -# TODO better sizehint estimation to reduce reallocations -function save_results_2!(object::GraphSimulation, i) + +""" + save_results!(object::GraphSimulation, i) + +Save the results of the simulation for time-step `i` into the +object. For a `GraphSimulation` object, this will save the results +from the `status(object)` in the `outputs(object)`. +""" +function save_results!(object::GraphSimulation, i) outs = outputs(object) if length(outs) == 0 @@ -350,46 +269,54 @@ function save_results_2!(object::GraphSimulation, i) index = indexes[organ] - # this can be made much more conservative with the right heuristic, or with user hints + # 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 > len + if length(statuses[organ]) + index - 1 > len min_required = max(length(statuses[organ]) + index - len, index) - resize!(outs[organ], 2*min_required) + + 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 + + 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])) - for status in statuses[organ] - outs[organ][index] = (;timestep=i,zip(tracked_outputs, [status[var] for var in tracked_outputs])...) - index += 1 - end - indexes[organ] = index + indexes[organ] = copy_tracked_outputs_into_vector!(outs[organ], i, statuses[organ], tracked_outputs, indexes[organ]) end end -""" - save_results!(object::GraphSimulation, i) - -Save the results of the simulation for time-step `i` into the -object. For a `GraphSimulation` object, this will save the results -from the `status(object)` in the `outputs(object)`. -""" -function save_results!(object::GraphSimulation, i) - outs = outputs(object) - - if length(outs) == 0 - return - end - - statuses = status(object) - - for (organ, vars) in outs - for (var, values) in vars - values[i] = [status[var] for status in statuses[organ]] +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 35347cb4c..8958127a3 100644 --- a/src/run.jl +++ b/src/run.jl @@ -410,8 +410,7 @@ function run!( for (process_key, dependency_node) in roots run_node_multiscale!(object, dependency_node, 1, models, meteo, constants, object, check, executor) end - save_results_2!(object, 1) - #save_results!(object, 1) + save_results!(object, 1) else for (i, meteo_i) in enumerate(Tables.rows(meteo)) roots = collect(dep_graph.roots) @@ -419,11 +418,12 @@ function run!( run_node_multiscale!(object, dependency_node, i, models, meteo_i, constants, object, check, executor) end # At the end of the time-step, we save the results of the simulation in the object: - save_results_2!(object, i) - #save_results!(object, i) + save_results!(object, i) 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 diff --git a/test/helper-functions.jl b/test/helper-functions.jl index b148c003b..e4db3532d 100644 --- a/test/helper-functions.jl +++ b/test/helper-functions.jl @@ -1,7 +1,7 @@ # Simple helper functions that can be used in various tests here and there function compare_outputs_modellist_mapping(filtered_outputs, graphsim) - outputs_df_dict = PlantSimEngine.convert_outputs_2(graphsim.outputs, DataFrame) + outputs_df_dict = convert_outputs(graphsim.outputs, DataFrame) @assert length(outputs_df_dict) == 1 outputs_df = last(first(outputs_df_dict)) @@ -25,8 +25,8 @@ end # doesn't check for mtg equality function compare_outputs_graphsim(graphsim, graphsim2) - outputs_df_dict = PlantSimEngine.convert_outputs_2(graphsim.outputs, DataFrame) - outputs2_df_dict = PlantSimEngine.convert_outputs_2(graphsim2.outputs, DataFrame) + 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 diff --git a/test/test-corner-cases.jl b/test/test-corner-cases.jl index 902385753..e1c9ac48e 100644 --- a/test/test-corner-cases.jl +++ b/test/test-corner-cases.jl @@ -529,7 +529,7 @@ 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_dict = PlantSimEngine.convert_outputs_2(out, DataFrame) + df_dict = convert_outputs(out, DataFrame) @test DataFrames.nrow(df_dict["Leaf"]) == 2 @test DataFrames.ncol(df_dict["Leaf"]) == 3 end @@ -564,7 +564,7 @@ end sim = run!(mtg, mapping, meteo; tracked_outputs=outs) using DataFrames - df_dict = PlantSimEngine.convert_outputs_2(sim, DataFrame) + 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 7fe624d5b..498f1d146 100644 --- a/test/test-mtg-dynamic.jl +++ b/test/test-mtg-dynamic.jl @@ -82,7 +82,7 @@ out = run!(sim,meteo) @test st["Internode"][1].TT_cu_emergence == 0.0 @test st["Internode"][end].TT_cu_emergence == 25.0 - out_df_dict = PlantSimEngine.convert_outputs_2(out, DataFrame) + 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] diff --git a/test/test-mtg-multiscale-cyclic-dep.jl b/test/test-mtg-multiscale-cyclic-dep.jl index bc15cd7b5..ca3258ea2 100644 --- a/test/test-mtg-multiscale-cyclic-dep.jl +++ b/test/test-mtg-multiscale-cyclic-dep.jl @@ -229,7 +229,7 @@ end @test sort(unique(ref_df.organ)) == sort(collect(keys(out))) - out_df_dict = PlantSimEngine.convert_outputs_2(out, DataFrame, no_value=missing) + 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)] diff --git a/test/test-mtg-multiscale.jl b/test/test-mtg-multiscale.jl index aba196dc0..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 @@ -697,7 +699,7 @@ end @test sim.outputs["Plant"][1][:carbon_allocation][1] === sim.outputs["Internode"][1][:carbon_allocation] # Testing the outputs if transformed into a DataFrame: - outs_df_dict = PlantSimEngine.convert_outputs_2(out, DataFrame) + outs_df_dict = convert_outputs(out, DataFrame) @test isa(outs_df_dict, Dict{String, DataFrame}) @test size(outs_df_dict["Leaf"]) == (4, 6) From fd3680d8c189db12fc8e018295ef53d07f3e7071 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Wed, 9 Apr 2025 17:13:01 +0200 Subject: [PATCH 13/24] Integration yml, typo in the branch name... --- .github/workflows/Integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Integration.yml b/.github/workflows/Integration.yml index 8f9ebd356..6e42fe56b 100644 --- a/.github/workflows/Integration.yml +++ b/.github/workflows/Integration.yml @@ -32,8 +32,8 @@ jobs: arch: - x64 package: - - {user: PalmStudio, repo: XPalm.jl, branch: PSE-multiscale-outputs-structure-changes} - - {user: VEZY, repo: PlantBioPhysics.jl, branch: PSE-multiscale-outputs-structure-changes} + - {user: PalmStudio, repo: XPalm.jl, branch: PSE-multiscale-outputs-structure-change} + - {user: VEZY, repo: PlantBioPhysics.jl, branch: PSE-multiscale-outputs-structure-change} steps: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v2 From 484fb91b1d51eb5d1f78f90c9be6eb1b5324f096 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Thu, 10 Apr 2025 10:04:05 +0200 Subject: [PATCH 14/24] Update and activate benchmarks on this branch, and enable workflow dispatch --- .github/workflows/benchmarks_and_downstream.yml | 3 ++- test/downstream/test-all-benchmarks.jl | 4 ++-- test/downstream/test-xpalm.jl | 15 ++++++++------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/benchmarks_and_downstream.yml b/.github/workflows/benchmarks_and_downstream.yml index 9ae3e49e1..fb0f96e5d 100644 --- a/.github/workflows/benchmarks_and_downstream.yml +++ b/.github/workflows/benchmarks_and_downstream.yml @@ -4,8 +4,9 @@ on: branches: - main - Outputs-filtering2 + - PSE-multiscale-outputs-structure-change tags: "*" - workflow-dispatch: + workflow-dispatch: permissions: # deployments permission to deploy GitHub pages website deployments: write diff --git a/test/downstream/test-all-benchmarks.jl b/test/downstream/test-all-benchmarks.jl index db9436fde..1b2f5a771 100644 --- a/test/downstream/test-all-benchmarks.jl +++ b/test/downstream/test-all-benchmarks.jl @@ -41,9 +41,9 @@ 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() -sim_outputs = xpalm_default_param_run(palm, models, out_vars, meteo) +sim_outputs = xpalm_default_param_run(palm, models, meteo, out_vars) -suite[suite_name]["XPalm_run"] = @benchmarkable xpalm_default_param_run($palm, $models, $out_vars, $meteo) seconds = 120 +suite[suite_name]["XPalm_run"] = @benchmarkable xpalm_default_param_run($palm, $models, $meteo, $out_vars) seconds = 120 suite[suite_name]["XPalm_convert_outputs"] = @benchmarkable xpalm_default_param_convert_outputs($sim_outputs) seconds = 120 tune!(suite) diff --git a/test/downstream/test-xpalm.jl b/test/downstream/test-xpalm.jl index c2708374d..a9321a8ab 100644 --- a/test/downstream/test-xpalm.jl +++ b/test/downstream/test-xpalm.jl @@ -4,7 +4,8 @@ # 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#PSE-multiscale-outputs-structure-changes") +Pkg.develop("XPalm") using Test using PlantMeteo#, MultiScaleTreeGraph @@ -15,13 +16,13 @@ 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("../XPalm.jl/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), @@ -32,8 +33,8 @@ 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 @@ -43,7 +44,7 @@ function xpalm_default_param_run(palm, models, meteo, out_vars) 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 From c9bf51d417b6e08f733b8cdf5740b538a2dbda21 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Thu, 10 Apr 2025 10:09:24 +0200 Subject: [PATCH 15/24] Typo in the yml --- .github/workflows/benchmarks_and_downstream.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks_and_downstream.yml b/.github/workflows/benchmarks_and_downstream.yml index fb0f96e5d..519d2028f 100644 --- a/.github/workflows/benchmarks_and_downstream.yml +++ b/.github/workflows/benchmarks_and_downstream.yml @@ -6,7 +6,7 @@ on: - Outputs-filtering2 - PSE-multiscale-outputs-structure-change tags: "*" - workflow-dispatch: + workflow_dispatch: permissions: # deployments permission to deploy GitHub pages website deployments: write From a08a3328c4000f6c75d1dbca47a5fe82c097a114 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Thu, 10 Apr 2025 10:27:28 +0200 Subject: [PATCH 16/24] Attempt at triggering benchmark again --- .github/workflows/benchmarks_and_downstream.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks_and_downstream.yml b/.github/workflows/benchmarks_and_downstream.yml index 519d2028f..7f421731a 100644 --- a/.github/workflows/benchmarks_and_downstream.yml +++ b/.github/workflows/benchmarks_and_downstream.yml @@ -1,4 +1,4 @@ -name: BenchmarksAndDownstream +name: Benchmarks on: push: branches: @@ -6,6 +6,7 @@ on: - Outputs-filtering2 - PSE-multiscale-outputs-structure-change tags: "*" + pull_request: workflow_dispatch: permissions: # deployments permission to deploy GitHub pages website From 331056554ddc9429c78a9120625b17967cc3af84 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Thu, 10 Apr 2025 11:05:57 +0200 Subject: [PATCH 17/24] Looks like I unintentionally added the manifest when creating the downstream folder, let's uh, remove it from the repo --- test/downstream/Manifest.toml | 1153 --------------------------------- test/downstream/test-xpalm.jl | 3 +- 2 files changed, 1 insertion(+), 1155 deletions(-) delete mode 100644 test/downstream/Manifest.toml 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/test-xpalm.jl b/test/downstream/test-xpalm.jl index a9321a8ab..fb1d206ed 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#PSE-multiscale-outputs-structure-changes") -Pkg.develop("XPalm") +#Pkg.develop("XPalm") using Test using PlantMeteo#, MultiScaleTreeGraph From 26496cdd8bf0192e280266ac577568c087a0108d Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Thu, 10 Apr 2025 11:19:32 +0200 Subject: [PATCH 18/24] Fix broken link to meteo csv file. (With the XPalm release the package got renamed from XPalm.jl to XPalm ? I had to tinker with my local repo at some point, I think that's why) --- test/downstream/test-xpalm.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/downstream/test-xpalm.jl b/test/downstream/test-xpalm.jl index fb1d206ed..76e465025 100644 --- a/test/downstream/test-xpalm.jl +++ b/test/downstream/test-xpalm.jl @@ -15,7 +15,7 @@ using XPalm using BenchmarkTools function xpalm_default_param_create() - meteo = CSV.read("../XPalm.jl/0-data/meteo.csv", DataFrame) + meteo = CSV.read("../XPalm/0-data/meteo.csv", DataFrame) #meteo.duration = [Dates.Day(i[1:1]) for i in meteo.duration] m = Weather(meteo) From c41082e41bc2208d1695eb4d4ceb89b93a57b3b2 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Thu, 10 Apr 2025 11:45:54 +0200 Subject: [PATCH 19/24] Nope, nope, that wasn't quite it, try again --- test/downstream/test-xpalm.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/downstream/test-xpalm.jl b/test/downstream/test-xpalm.jl index 76e465025..053b24bfa 100644 --- a/test/downstream/test-xpalm.jl +++ b/test/downstream/test-xpalm.jl @@ -15,7 +15,7 @@ using XPalm using BenchmarkTools function xpalm_default_param_create() - meteo = CSV.read("../XPalm/0-data/meteo.csv", DataFrame) + meteo = CSV.read("../../XPalm.jl/0-data/meteo.csv", DataFrame) #meteo.duration = [Dates.Day(i[1:1]) for i in meteo.duration] m = Weather(meteo) From b5f8846ed75b3ff1ce81c27dc0cc7d8000d63fd8 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Thu, 10 Apr 2025 16:40:54 +0200 Subject: [PATCH 20/24] Still not the right path --- test/downstream/test-xpalm.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/downstream/test-xpalm.jl b/test/downstream/test-xpalm.jl index 053b24bfa..15c8ea8b9 100644 --- a/test/downstream/test-xpalm.jl +++ b/test/downstream/test-xpalm.jl @@ -15,7 +15,7 @@ using XPalm using BenchmarkTools function xpalm_default_param_create() - meteo = CSV.read("../../XPalm.jl/0-data/meteo.csv", DataFrame) + meteo = CSV.read("../../XPalm/0-data/meteo.csv", DataFrame) #meteo.duration = [Dates.Day(i[1:1]) for i in meteo.duration] m = Weather(meteo) From 162284e34a47d253d9922e8c421248d4a548ece0 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Fri, 11 Apr 2025 10:13:56 +0200 Subject: [PATCH 21/24] Just get the package path automagically, I don't know why I kept trying the other stuff --- test/downstream/test-xpalm.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/downstream/test-xpalm.jl b/test/downstream/test-xpalm.jl index 15c8ea8b9..65b14dad8 100644 --- a/test/downstream/test-xpalm.jl +++ b/test/downstream/test-xpalm.jl @@ -15,7 +15,7 @@ using XPalm using BenchmarkTools function xpalm_default_param_create() - meteo = CSV.read("../../XPalm/0-data/meteo.csv", DataFrame) + 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) From 0d1f8e2311605e87510645b5309893a4122c34d5 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Fri, 11 Apr 2025 10:16:04 +0200 Subject: [PATCH 22/24] Might as well update the .gitignore while I'm at it --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From 7e8324406262453aa322aa3be7cc33d83a202a97 Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Fri, 11 Apr 2025 11:45:09 +0200 Subject: [PATCH 23/24] Tinker with the benchmarks yml to enable usage of specific PRs (to be improved upon) --- .github/workflows/benchmarks_and_downstream.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/benchmarks_and_downstream.yml b/.github/workflows/benchmarks_and_downstream.yml index 7f421731a..949ee96de 100644 --- a/.github/workflows/benchmarks_and_downstream.yml +++ b/.github/workflows/benchmarks_and_downstream.yml @@ -18,8 +18,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: @@ -32,13 +30,21 @@ jobs: arch: - x64 package: - - {user: VEZY, repo: PlantSimEngine.jl, group: Downstream} + - {user: PalmStudio, repo: XPalm.jl, branch: PSE-multiscale-outputs-structure-change} + - {user: VEZY, repo: PlantBioPhysics.jl, branch: PSE-multiscale-outputs-structure-change} steps: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} + - uses: julia-actions/julia-buildpkg@v1 + - name: Clone Downstream + uses: actions/checkout@v4 + with: + repository: ${{ matrix.package.user }}/${{ matrix.package.repo }} + ref: ${{matrix.package.branch}} + path: downstream # TODO handle breaking changes the way downstream tests do ? # NOTE : manifest toml file is removed otherwise git whines about untracked changes when switching branches for the gh-pages commit - name: Run benchmarks From 15331aa87f1d493b3f6f272f6bdd35c6825ffe9a Mon Sep 17 00:00:00 2001 From: Samuel-AMAP Date: Mon, 28 Apr 2025 15:41:58 +0200 Subject: [PATCH 24/24] Set the dev branch to be the one for CI and integration checks (including PBP and XPalm dev branches), we'll see what happens when merging this into dev --- .github/workflows/CI.yml | 2 +- .github/workflows/Integration.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) 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 6e42fe56b..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-multiscale-outputs-structure-change} - - {user: VEZY, repo: PlantBioPhysics.jl, branch: PSE-multiscale-outputs-structure-change} + - {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