API - internal functions
Un-exported
Private functions, types or constants from PlantSimEngine
. These are not exported, so you need to use PlantSimEngine.
to access them (e.g. PlantSimEngine.DataFormat
). Most of them are developer code, but some may be useful for tinkerers, or to have greater control over some simulation parameters (future versions of this documentation might break those categories into separate pages for clarity).
Index
DataFrames.DataFrame
DataFrames.DataFrame
PlantSimEngine.AbstractBasic_Current_TimestepModel
PlantSimEngine.AbstractBasic_Next_TimestepModel
PlantSimEngine.AbstractNodeMapping
PlantSimEngine.DataFormat
PlantSimEngine.DependencyGraph
PlantSimEngine.DependencyTrait
PlantSimEngine.GraphSimulation
PlantSimEngine.MappedVar
PlantSimEngine.MultiNodeMapping
PlantSimEngine.ObjectDependencyTrait
PlantSimEngine.RefVariable
PlantSimEngine.RefVector
PlantSimEngine.SelfNodeMapping
PlantSimEngine.SingleNodeMapping
PlantSimEngine.TimeStepDependencyTrait
PlantSimEngine.UninitializedVar
Base.copy
Base.copy
Base.copy
Base.getindex
PlantSimEngine.add_mapped_variables_with_outputs_as_inputs!
PlantSimEngine.add_model_vars
PlantSimEngine.check_dimensions
PlantSimEngine.convert_reference_values!
PlantSimEngine.convert_vars
PlantSimEngine.convert_vars!
PlantSimEngine.convert_vars!
PlantSimEngine.default_variables_from_mapping
PlantSimEngine.diff_vars
PlantSimEngine.draw_guide
PlantSimEngine.draw_panel
PlantSimEngine.drop_process
PlantSimEngine.flatten_vars
PlantSimEngine.get_mapped_variables
PlantSimEngine.get_model_nodes
PlantSimEngine.get_models
PlantSimEngine.get_multiscale_default_value
PlantSimEngine.get_nsteps
PlantSimEngine.get_status
PlantSimEngine.hard_dependencies
PlantSimEngine.homogeneous_ts_kwargs
PlantSimEngine.homogeneous_ts_kwargs
PlantSimEngine.init_node_status!
PlantSimEngine.init_simulation
PlantSimEngine.init_statuses
PlantSimEngine.init_variables_manual
PlantSimEngine.initialise_all_as_hard_dependency_node
PlantSimEngine.is_graph_cyclic
PlantSimEngine.mapped_variables
PlantSimEngine.mapped_variables_no_outputs_from_other_scale
PlantSimEngine.model_
PlantSimEngine.object_parallelizable
PlantSimEngine.parallelizable
PlantSimEngine.pre_allocate_outputs
PlantSimEngine.ref_var
PlantSimEngine.reverse_mapping
PlantSimEngine.save_results!
PlantSimEngine.search_inputs_in_multiscale_output
PlantSimEngine.search_inputs_in_output
PlantSimEngine.soft_dependencies
PlantSimEngine.status_from_template
PlantSimEngine.timestep_parallelizable
PlantSimEngine.transform_single_node_mapped_variables_as_self_node_output!
PlantSimEngine.traverse_dependency_graph
PlantSimEngine.traverse_dependency_graph!
PlantSimEngine.traverse_dependency_graph!
PlantSimEngine.variables_multiscale
PlantSimEngine.variables_outputs_from_other_scale
PlantSimEngine.variables_typed
PlantSimEngine.vars_not_init_
API documentation
DataFrames.DataFrame
— MethodDataFrame(components <: AbstractArray{<:ModelList})
DataFrame(components <: AbstractDict{N,<:ModelList})
Fetch the data from a ModelList
(or an Array/Dict of) status into a DataFrame.
Examples
using PlantSimEngine
using DataFrames
# Creating a ModelList
models = ModelList(
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model(),
status=(var1=15.0, var2=0.3)
)
# Converting to a DataFrame
df = DataFrame(models)
# Converting to a Dict of ModelLists
models = Dict(
"Leaf" => ModelList(
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model()
),
"InterNode" => ModelList(
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model()
)
)
# Converting to a DataFrame
df = DataFrame(models)
DataFrames.DataFrame
— MethodDataFrame(components::ModelList{T,S}) where {T,S<:Status}
Implementation of DataFrame
for a ModelList
model with one time step.
PlantSimEngine.AbstractBasic_Current_TimestepModel
— Typebasic_current_timestep
process abstract model.
All models implemented to simulate the basic_current_timestep
process must be a subtype of this type, e.g. struct MyBasic_Current_TimestepModel <: AbstractBasic_Current_TimestepModel end
.
You can list all models implementing this process using subtypes
:
Examples
subtypes(AbstractBasic_Current_TimestepModel)
PlantSimEngine.AbstractBasic_Next_TimestepModel
— Typebasic_next_timestep
process abstract model.
All models implemented to simulate the basic_next_timestep
process must be a subtype of this type, e.g. struct MyBasic_Next_TimestepModel <: AbstractBasic_Next_TimestepModel end
.
You can list all models implementing this process using subtypes
:
Examples
subtypes(AbstractBasic_Next_TimestepModel)
PlantSimEngine.AbstractNodeMapping
— TypeAbstractNodeMapping
Abstract type for the type of node mapping, e.g. single node mapping or multiple node mapping.
PlantSimEngine.DataFormat
— MethodDataFormat(T::Type)
Returns the data format of the type T
. The data format is used to determine how to iterate over the data. The following data formats are supported:
TableAlike
: The data is a table-like object, e.g. aDataFrame
or aTimeStepTable
. The data is iterated over by rows using theTables.jl
interface.SingletonAlike
: The data is a singleton-like object, e.g. aNamedTuple
or aTimeStepRow
. The data is iterated over by columns.TreeAlike
: The data is a tree-like object, e.g. aNode
.
The default implementation returns TableAlike
for AbstractDataFrame
, TimeStepTable
, AbstractVector
and Dict
, TreeAlike
for GraphSimulation
, SingletonAlike
for Status
, ModelList
, NamedTuple
and TimeStepRow
.
The default implementation for Any
throws an error. Users that want to use another input should define this trait for the new data format, e.g.:
PlantSimEngine.DataFormat(::Type{<:MyType}) = TableAlike()
Examples
julia> using PlantSimEngine, PlantMeteo, DataFrames
julia> PlantSimEngine.DataFormat(DataFrame)
PlantSimEngine.TableAlike()
julia> PlantSimEngine.DataFormat(TimeStepTable([Status(a = 1, b = 2, c = 3)]))
PlantSimEngine.TableAlike()
julia> PlantSimEngine.DataFormat([1, 2, 3])
PlantSimEngine.TableAlike()
julia> PlantSimEngine.DataFormat(Dict(:a => 1, :b => 2))
PlantSimEngine.TableAlike()
julia> PlantSimEngine.DataFormat(Status(a = 1, b = 2, c = 3))
PlantSimEngine.SingletonAlike()
PlantSimEngine.DependencyGraph
— TypeDependencyGraph{T}(roots::T, not_found::Dict{Symbol,DataType})
A graph of dependencies between models.
Arguments
roots::T
: the root nodes of the graph.not_found::Dict{Symbol,DataType}
: the models that were not found in the graph.
PlantSimEngine.DependencyTrait
— TypeDependencyTrait(T::Type)
Returns information about the eventual dependence of a model T
to other time-steps or objects for its computation. The dependence trait is used to determine if a model is parallelizable or not.
The following dependence traits are supported:
TimeStepDependencyTrait
: Trait that defines whether a model can be parallelizable over time-steps for its computation.ObjectDependencyTrait
: Trait that defines whether a model can be parallelizable over objects for its computation.
PlantSimEngine.GraphSimulation
— TypeGraphSimulation(graph, mapping)
GraphSimulation(graph, statuses, dependency_graph, models, outputs)
A type that holds all information for a simulation over a graph.
Arguments
graph
: an graph, such as an MTGmapping
: a dictionary of model mappingstatuses
: a structure that defines the status of each node in the graphstatus_templates
: a dictionary of status templatesreverse_multiscale_mapping
: a dictionary of mapping for other scalesvar_need_init
: a dictionary indicating if a variable needs to be initializeddependency_graph
: the dependency graph of the models applied to the graphmodels
: a dictionary of modelsoutputs
: a dictionary of outputs
PlantSimEngine.MappedVar
— TypeMappedVar(source_organ, variable, source_variable, source_default)
A variable mapped to another scale.
Arguments
source_organ
: the organ(s) that are targeted by the mappingvariable
: the name of the variable that is mappedsource_variable
: the name of the variable from the source organ (the one that computes the variable)source_default
: the default value of the variable
Examples
julia> using PlantSimEngine
julia> PlantSimEngine.MappedVar(PlantSimEngine.SingleNodeMapping("Leaf"), :carbon_assimilation, :carbon_assimilation, 1.0)
PlantSimEngine.MappedVar{PlantSimEngine.SingleNodeMapping, Symbol, Symbol, Float64}(PlantSimEngine.SingleNodeMapping("Leaf"), :carbon_assimilation, :carbon_assimilation, 1.0)
PlantSimEngine.MultiNodeMapping
— TypeMultiNodeMapping(scale)
Type for the multiple node mapping, e.g. [:carbon_assimilation => ["Leaf"],]
. Note that "Leaf" is given as a vector, which means :carbon_assimilation
will be a vector of values taken from each "Leaf" in the plant graph.
PlantSimEngine.ObjectDependencyTrait
— TypeObjectDependencyTrait(::Type{T})
Defines the trait about the eventual dependence of a model T
to other objects for its computation. This dependency trait is used to determine if a model is parallelizable over objects or not.
The following dependency traits are supported:
IsObjectDependent
: The model depends on other objects for its computation, it cannot be run in parallel.IsObjectIndependent
: The model does not depend on other objects for its computation, it can be run in parallel.
All models are object dependent by default (i.e. IsObjectDependent
). This is probably not right for the majority of models, but:
- It is the safest default, as it will not lead to incorrect results if the user forgets to override this trait
which is not the case for the opposite (i.e. IsObjectIndependent
)
- It is easy to override this trait for models that are object independent
See also
timestep_parallelizable
: Returnstrue
if the model is parallelizable over time-steps, andfalse
otherwise.object_parallelizable
: Returnstrue
if the model is parallelizable over objects, andfalse
otherwise.parallelizable
: Returnstrue
if the model is parallelizable, andfalse
otherwise.TimeStepDependencyTrait
: Defines the trait about the eventual dependence of a model to other time-steps for its computation.
Examples
Define a dummy process:
using PlantSimEngine
# Define a test process:
@process "TestProcess"
Define a model that is object independent:
struct MyModel <: AbstractTestprocessModel end
# Override the object dependency trait:
PlantSimEngine.ObjectDependencyTrait(::Type{MyModel}) = IsObjectIndependent()
Check if the model is parallelizable over objects:
object_parallelizable(MyModel()) # false
Define a model that is object dependent:
struct MyModel2 <: AbstractTestprocessModel end
# Override the object dependency trait:
PlantSimEngine.ObjectDependencyTrait(::Type{MyModel2}) = IsObjectDependent()
Check if the model is parallelizable over objects:
object_parallelizable(MyModel()) # true
PlantSimEngine.RefVariable
— TypeRefVariable(reference_variable)
A structure to manually flag a variable in a model to use the value of another variable at the same scale. This is used for variable renaming, when a variable is computed by a model but is used by another model with a different name.
Note: we don't really rename the variable in the status (we need it for the other models), but we create a new one that is a reference to the first one.
PlantSimEngine.RefVector
— TypeRefVector(field::Symbol, sts...)
RefVector(field::Symbol, sts::Vector{<:Status})
RefVector(v::Vector{Base.RefValue{T}})
A vector of references to a field of a vector of structs. This is used to efficiently pass the values between scales.
Arguments
field
: the field of the struct to referencests...
: the structs to referencests::Vector{<:Status}
: a vector of structs to reference
Examples
julia> using PlantSimEngine
Let's take two Status structs:
julia> status1 = Status(a = 1.0, b = 2.0, c = 3.0);
julia> status2 = Status(a = 2.0, b = 3.0, c = 4.0);
We can make a RefVector of the field a
of the structs st1
and st2
:
julia> rv = PlantSimEngine.RefVector(:a, status1, status2)
2-element PlantSimEngine.RefVector{Float64}:
1.0
2.0
Which is equivalent to:
julia> rv = PlantSimEngine.RefVector(:a, [status1, status2])
2-element PlantSimEngine.RefVector{Float64}:
1.0
2.0
We can access the values of the RefVector:
julia> rv[1]
1.0
Updating the value in the RefVector will update the value in the original struct:
julia> rv[1] = 10.0
10.0
julia> status1.a
10.0
We can also make a RefVector from a vector of references:
julia> vec = [Ref(1.0), Ref(2.0), Ref(3.0)]
3-element Vector{Base.RefValue{Float64}}:
Base.RefValue{Float64}(1.0)
Base.RefValue{Float64}(2.0)
Base.RefValue{Float64}(3.0)
julia> rv = PlantSimEngine.RefVector(vec)
3-element PlantSimEngine.RefVector{Float64}:
1.0
2.0
3.0
julia> rv[1]
1.0
PlantSimEngine.SelfNodeMapping
— TypeSelfNodeMapping()
Type for the self node mapping, i.e. a node that maps onto itself. This is used to flag variables that will be referenced as a scalar value by other models. It can happen in two conditions: - the variable is computed by another scale, so we need this variable to exist as an input to this scale (it is not computed at this scale otherwise) - the variable is used as input to another scale but as a single value (scalar), so we need to reference it as a scalar.
PlantSimEngine.SingleNodeMapping
— TypeSingleNodeMapping(scale)
Type for the single node mapping, e.g. [:soil_water_content => "Soil",]
. Note that "Soil" is given as a scalar, which means that :soil_water_content
will be a scalar value taken from the unique "Soil" node in the plant graph.
PlantSimEngine.TimeStepDependencyTrait
— MethodTimeStepDependencyTrait(::Type{T})
Defines the trait about the eventual dependence of a model T
to other time-steps for its computation. This dependency trait is used to determine if a model is parallelizable over time-steps or not.
The following dependency traits are supported:
IsTimeStepDependent
: The model depends on other time-steps for its computation, it cannot be run in parallel.IsTimeStepIndependent
: The model does not depend on other time-steps for its computation, it can be run in parallel.
All models are time-step dependent by default (i.e. IsTimeStepDependent
). This is probably not right for the majority of models, but:
- It is the safest default, as it will not lead to incorrect results if the user forgets to override this trait
which is not the case for the opposite (i.e. IsTimeStepIndependent
)
- It is easy to override this trait for models that are time-step independent
See also
timestep_parallelizable
: Returnstrue
if the model is parallelizable over time-steps, andfalse
otherwise.object_parallelizable
: Returnstrue
if the model is parallelizable over objects, andfalse
otherwise.parallelizable
: Returnstrue
if the model is parallelizable, andfalse
otherwise.ObjectDependencyTrait
: Defines the trait about the eventual dependence of a model to other objects for its computation.
Examples
Define a dummy process:
using PlantSimEngine
# Define a test process:
@process "TestProcess"
Define a model that is time-step independent:
struct MyModel <: AbstractTestprocessModel end
# Override the time-step dependency trait:
PlantSimEngine.TimeStepDependencyTrait(::Type{MyModel}) = IsTimeStepIndependent()
Check if the model is parallelizable over time-steps:
timestep_parallelizable(MyModel()) # false
Define a model that is time-step dependent:
struct MyModel2 <: AbstractTestprocessModel end
# Override the time-step dependency trait:
PlantSimEngine.TimeStepDependencyTrait(::Type{MyModel2}) = IsTimeStepDependent()
Check if the model is parallelizable over time-steps:
timestep_parallelizable(MyModel()) # true
PlantSimEngine.UninitializedVar
— TypeUninitializedVar(variable, value)
A variable that is not initialized yet, it is given a name and a default value.
Base.copy
— MethodBase.copy(l::AbstractArray{<:ModelList})
Copy an array-alike of ModelList
Base.copy
— MethodBase.copy(l::AbstractDict{N,<:ModelList} where N)
Copy a Dict-alike ModelList
Base.copy
— MethodBase.copy(l::ModelList)
Base.copy(l::ModelList, status)
Copy a ModelList
, eventually with new values for the status.
Examples
using PlantSimEngine
# Including example processes and models:
using PlantSimEngine.Examples;
# Create a model list:
models = ModelList(
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model(),
status=(var1=15.0, var2=0.3)
)
# Copy the model list:
ml2 = copy(models)
# Copy the model list with new status:
ml3 = copy(models, TimeStepTable([Status(var1=20.0, var2=0.5))])
Base.getindex
— Methodgetindex(component<:ModelList, key::Symbol)
getindex(component<:ModelList, key)
Indexing a component models structure: - with an integer, will return the status at the ith time-step - with anything else (Symbol, String) will return the required variable from the status
Examples
using PlantSimEngine
lm = ModelList(
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model(),
status = (var1=[15.0, 16.0], var2=0.3)
);
lm[:var1] # Returns the value of the Tₗ variable
lm[2] # Returns the status at the second time-step
lm[2][:var1] # Returns the value of Tₗ at the second time-step
lm[:var1][2] # Equivalent of the above
# output
16.0
PlantSimEngine.add_mapped_variables_with_outputs_as_inputs!
— Methodadd_mapped_variables_with_outputs_as_inputs!(mapped_vars)
Add the variables that are computed at a scale and written to another scale into the mapping.
PlantSimEngine.add_model_vars
— Methodadd_model_vars(x, models, type_promotion)
Check which variables in x
are not initialized considering a set of models
and the variables needed for their simulation. If some variables are uninitialized, initialize them to their default values.
This function needs to be implemented for each type of x
. The default method works for any Tables.jl-compatible x
and for NamedTuples.
Careful, the function makes a copy of the input x
if it does not list all needed variables.
PlantSimEngine.check_dimensions
— Methodcheck_dimensions(component,weather)
check_dimensions(status,weather)
Checks if a component status (or a status directly) and the weather have the same length, or if they can be recycled (length 1 for one of them).
Examples
using PlantSimEngine, PlantMeteo
# Including an example script that implements dummy processes and models:
using PlantSimEngine.Examples
# Creating a dummy weather:
w = Atmosphere(T = 20.0, Rh = 0.5, Wind = 1.0)
# Creating a dummy component:
models = ModelList(
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model(),
status=(var1=[15.0, 16.0], var2=0.3)
)
# Checking that the number of time-steps are compatible (here, they are, it returns nothing):
PlantSimEngine.check_dimensions(models, w)
# Creating a dummy weather with 3 time-steps:
w = Weather([
Atmosphere(T = 20.0, Rh = 0.5, Wind = 1.0),
Atmosphere(T = 25.0, Rh = 0.5, Wind = 1.0),
Atmosphere(T = 30.0, Rh = 0.5, Wind = 1.0)
])
# Checking that the number of time-steps are compatible (here, they are not, it throws an error):
PlantSimEngine.check_dimensions(models, w)
# output
ERROR: DimensionMismatch: Component status has a vector variable : var1 implying multiple timesteps but weather data only provides a single timestep.
PlantSimEngine.convert_reference_values!
— Methodconvert_reference_values!(mapped_vars::Dict{String,Dict{Symbol,Any}})
Convert the variables that are MappedVar{SelfNodeMapping}
or MappedVar{SingleNodeMapping}
to RefValues that reference a common value for the variable; and convert MappedVar{MultiNodeMapping}
to RefVectors that reference the values for the variable in the source organs.
PlantSimEngine.convert_vars
— Functionconvert_vars(ref_vars, type_promotion::Dict{DataType,DataType})
convert_vars(ref_vars, type_promotion::Nothing)
convert_vars!(ref_vars::Dict{Symbol}, type_promotion::Dict{DataType,DataType})
convert_vars!(ref_vars::Dict{Symbol}, type_promotion::Nothing)
Convert the status variables to the type specified in the type promotion dictionary. Note: the mutating version only works with a dictionary of variables.
Examples
If we want all the variables that are Reals to be Float32, we can use:
using PlantSimEngine
# Including example processes and models:
using PlantSimEngine.Examples;
ref_vars = init_variables(
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model(),
)
type_promotion = Dict(Real => Float32)
PlantSimEngine.convert_vars(type_promotion, ref_vars.process3)
PlantSimEngine.convert_vars!
— Functionconvert_vars(ref_vars, type_promotion::Dict{DataType,DataType})
convert_vars(ref_vars, type_promotion::Nothing)
convert_vars!(ref_vars::Dict{Symbol}, type_promotion::Dict{DataType,DataType})
convert_vars!(ref_vars::Dict{Symbol}, type_promotion::Nothing)
Convert the status variables to the type specified in the type promotion dictionary. Note: the mutating version only works with a dictionary of variables.
Examples
If we want all the variables that are Reals to be Float32, we can use:
using PlantSimEngine
# Including example processes and models:
using PlantSimEngine.Examples;
ref_vars = init_variables(
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model(),
)
type_promotion = Dict(Real => Float32)
PlantSimEngine.convert_vars(type_promotion, ref_vars.process3)
PlantSimEngine.convert_vars!
— Methodconvert_vars!(mapped_vars::Dict{String,Dict{String,Any}}, type_promotion)
Converts the types of the variables in a mapping (mapped_vars
) using the type_promotion
dictionary.
The mapping should be a dictionary with organ name as keys and a dictionary of variables as values, with variable names as symbols and variable value as value.
PlantSimEngine.default_variables_from_mapping
— Functiondefault_variables_from_mapping(mapped_vars, verbose=true)
Get the default values for the mapped variables by recursively searching from the mapping to find the original mapped value.
Arguments
mapped_vars::Dict{String,Dict{Symbol,Any}}
: the variables mapped to each organ.verbose::Bool
: whether to print the stacktrace of the search for the default value in the mapping.
PlantSimEngine.diff_vars
— Methoddiff_vars(x, y)
Returns the names of variables that have different values in x and y.
PlantSimEngine.draw_guide
— Methoddraw_guide(h, w, prefix, isleaf, guides)
Draw the line guide for one node of the dependency graph.
PlantSimEngine.draw_panel
— Methoddraw_panel(node, graph, prefix, dep_graph_guides, parent; title="Soft-coupled model")
Draw the panels for all dependencies
PlantSimEngine.drop_process
— Methoddrop_process(proc_vars, process)
Return a new NamedTuple
with the process process
removed from the NamedTuple
proc_vars
.
Arguments
proc_vars::NamedTuple
: theNamedTuple
from which we want to remove the processprocess
.process::Symbol
: the process we want to remove from theNamedTuple
proc_vars
.
Returns
A new NamedTuple
with the process process
removed from the NamedTuple
proc_vars
.
Example
julia> drop_process((a = 1, b = 2, c = 3), :b)
(a = 1, c = 3)
julia> drop_process((a = 1, b = 2, c = 3), (:a, :c))
(b = 2,)
PlantSimEngine.flatten_vars
— Methodflatten_vars(vars)
Return a set of the variables in the vars
dictionary.
Arguments
vars::Dict{Symbol, Tuple{Symbol, Vararg{Symbol}}}
: a dict of process => namedtuple of variables => value.
Returns
A set of the variables in the vars
dictionary.
Example
julia> flatten_vars(Dict(:process1 => (:var1, :var2), :process2 => (:var3, :var4)))
Set{Symbol} with 4 elements:
:var4
:var3
:var2
:var1
julia> flatten_vars([:process1 => (var1 = -Inf, var2 = -Inf), :process2 => (var3 = -Inf, var4 = -Inf)])
(var2 = -Inf, var4 = -Inf, var3 = -Inf, var1 = -Inf)
PlantSimEngine.get_mapped_variables
— Methodget_mapped_variables(m)
Get the mapping of a dictionary of model mapping.
Arguments
m::Dict{String,Any}
: a dictionary of model mapping
Returns a vector of pairs of symbols and strings or vectors of strings
Examples
See get_models
for examples.
PlantSimEngine.get_model_nodes
— Methodget_model_nodes(dep_graph::DependencyGraph, model)
Get the nodes in the dependency graph implementing a type of model.
Arguments
dep_graph::DependencyGraph
: the dependency graph.model
: the model type to look for.
Returns
- An array of nodes implementing the model type.
Examples
PlantSimEngine.get_model_nodes(dependency_graph, Beer)
PlantSimEngine.get_models
— Methodget_models(m)
Get the models of a dictionary of model mapping.
Arguments
m::Dict{String,Any}
: a dictionary of model mapping
Returns a vector of models
Examples
julia> using PlantSimEngine;
Import example models (can be found in the examples
folder of the package, or in the Examples
sub-modules):
julia> using PlantSimEngine.Examples;
If we just give a MultiScaleModel, we get its model as a one-element vector:
julia> models = MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_assimilation => ["Leaf"], :carbon_demand => ["Leaf", "Internode"], :carbon_allocation => ["Leaf", "Internode"] ], );
julia> PlantSimEngine.get_models(models)
1-element Vector{ToyCAllocationModel}:
ToyCAllocationModel()
If we give a tuple of models, we get each model in a vector:
julia> models2 = ( MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => "Soil",], ), ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), Status(aPPFD=1300.0, TT=10.0), );
Notice that we provide "Soil", not ["Soil"] in the mapping because a single value is expected for the mapping here.
julia> PlantSimEngine.get_models(models2)
2-element Vector{AbstractModel}:
ToyAssimModel{Float64}(0.2)
ToyCDemandModel{Float64}(10.0, 200.0)
PlantSimEngine.get_multiscale_default_value
— Functionget_multiscale_default_value(mapped_vars, val, mapping_stacktrace=[])
Get the default value of a variable from a mapping.
Arguments
mapped_vars::Dict{String,Dict{Symbol,Any}}
: the variables mapped to each organ.val::Any
: the variable to get the default value of.mapping_stacktrace::Vector{Any}
: the stacktrace of the search for the value in ascendind the mapping.
PlantSimEngine.get_nsteps
— Methodget_nsteps(t)
Get the number of steps in the object.
PlantSimEngine.get_status
— Methodget_status(m)
Get the status of a dictionary of model mapping.
Arguments
m::Dict{String,Any}
: a dictionary of model mapping
Returns a Status
or nothing
.
Examples
See get_models
for examples.
PlantSimEngine.hard_dependencies
— Methodhard_dependencies(models; verbose::Bool=true)
hard_dependencies(mapping::Dict{String,T}; verbose::Bool=true)
Compute the hard dependencies between models.
PlantSimEngine.homogeneous_ts_kwargs
— Methodhomogeneous_ts_kwargs(kwargs)
By default, the function returns its argument.
PlantSimEngine.homogeneous_ts_kwargs
— Methodkwargs_to_timestep(kwargs::NamedTuple{N,T}) where {N,T}
Takes a NamedTuple with optionnaly vector of values for each variable, and makes a vector of NamedTuple, with each being a time step. It is used to be able to e.g. give constant values for all time-steps for one variable.
Examples
PlantSimEngine.homogeneous_ts_kwargs((Tₗ=[25.0, 26.0], aPPFD=1000.0))
PlantSimEngine.init_node_status!
— Functioninit_node_status!(
node,
statuses,
mapped_vars,
reverse_multiscale_mapping,
vars_need_init=Dict{String,Any}(),
type_promotion=nothing;
check=true,
attribute_name=:plantsimengine_status)
)
Initialise the status of a plant graph node, taking into account the multiscale mapping, and add it to the statuses dictionary.
Arguments
node
: the node to initialisestatuses
: the dictionary of statuses by node typemapped_vars
: the template of status for each node typereverse_multiscale_mapping
: the variables that are mapped to other scalesvar_need_init
: the variables that are not initialised or computed by other modelsnodes_with_models
: the nodes that have a model defined for their symboltype_promotion
: the type promotion to use for the variablescheck
: whether to check the mapping for errors (see details)attribute_name
: the name of the attribute to store the status in the node, by default::plantsimengine_status
Details
Most arguments can be computed from the graph and the mapping:
statuses
is given by the first initialisation:statuses = Dict(i => Status[] for i in nodes_with_models)
mapped_vars
is computed usingmapped_variables()
, see code ininit_statuses
vars_need_init
is computed using `varsneedinit = Dict(org => filter(x -> isa(last(x), UninitializedVar), vars) |> keys for (org, vars) in mapped_vars) |>
filter(x -> length(last(x)) > 0)`
The check
argument is a boolean indicating if variables initialisation should be checked. In the case that some variables need initialisation (partially initialized mapping), we check if the value can be found in the node attributes (using the variable name). If true
, the function returns an error if the attribute is missing, otherwise it uses the default value from the model.
PlantSimEngine.init_simulation
— Methodinit_simulation(mtg, mapping; nsteps=1, outputs=nothing, type_promotion=nothing, check=true, verbose=true)
Initialise the simulation. Returns:
- the mtg
- a status for each node by organ type, considering multi-scale variables
- the dependency graph of the models
- the models parsed as a Dict of organ type => NamedTuple of process => model mapping
- the pre-allocated outputs
Arguments
mtg
: the MTGmapping::Dict{String,Any}
: a dictionary of model mappingnsteps
: the number of steps of the simulationoutputs
: the dynamic outputs needed for the simulationtype_promotion
: the type promotion to use for the variablescheck
: whether to check the mapping for errors. Passed toinit_node_status!
.verbose
: print information about errors in the mapping
Details
The function first computes a template of status for each organ type that has a model in the mapping. This template is used to initialise the status of each node of the MTG, taking into account the user-defined initialisation, and the (multiscale) mapping. The mapping is used to make references to the variables that are defined at another scale, so that the values are automatically updated when the variable is changed at the other scale. Two types of multiscale variables are available: RefVector
and MappedVar
. The first one is used when the variable is mapped to a vector of nodes, and the second one when it is mapped to a single node. This is given by the user through the mapping, using a string for a single node (e.g. => "Leaf"
), and a vector of strings for a vector of nodes (e.g. => ["Leaf"]
for one type of node or => ["Leaf", "Internode"]
for several).
The function also computes the dependency graph of the models, i.e. the order in which the models should be called, considering the dependencies between them. The dependency graph is used to call the models in the right order when the simulation is run.
Note that if a variable is not computed by models or initialised from the mapping, it is searched in the MTG attributes. The value is not a reference to the one in the attribute of the MTG, but a copy of it. This is because we can't reference a value in a Dict. If you need a reference, you can use a Ref
for your variable in the MTG directly, and it will be automatically passed as is.
PlantSimEngine.init_statuses
— Functioninit_statuses(mtg, mapping, dependency_graph=dep(mapping); type_promotion=nothing, verbose=true, check=true)
Get the status of each node in the MTG by node type, pre-initialised considering multi-scale variables.
Arguments
mtg
: the plant graphmapping
: a dictionary of model mappingdependency_graph::DependencyGraph
: the first-order dependency graph where each model in the mapping is assigned a node.
However, models that are identified as hard-dependencies are not given individual nodes. Instead, they are nested as child nodes under other models.
type_promotion
: the type promotion to use for the variablesverbose
: print information when compiling the mappingcheck
: whether to check the mapping for errors. Passed toinit_node_status!
.
Return
A NamedTuple of status by node type, a dictionary of status templates by node type, a dictionary of variables mapped to other scales, a dictionary of variables that need to be initialised or computed by other models, and a vector of nodes that have a model defined for their symbol:
(;statuses, status_templates, reverse_multiscale_mapping, vars_need_init, nodes_with_models)
PlantSimEngine.init_variables_manual
— Methodinit_variables_manual(models...;vars...)
Return an initialisation of the model variables with given values.
Examples
using PlantSimEngine
# Load the dummy models given as example in the package:
using PlantSimEngine.Examples
models = ModelList(
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model()
)
PlantSimEngine.init_variables_manual(status(models), (var1=20.0,))
PlantSimEngine.initialise_all_as_hard_dependency_node
— Methodinitialise_all_as_hard_dependency_node(models)
Take a set of models and initialise them all as a hard dependency node, and return a dictionary of :process => HardDependencyNode
.
PlantSimEngine.is_graph_cyclic
— Methodis_graph_cyclic(dependency_graph::DependencyGraph; full_stack=false, verbose=true)
Check if the dependency graph is cyclic.
Arguments
dependency_graph::DependencyGraph
: the dependency graph to check.full_stack::Bool=false
: iftrue
, return the full stack of nodes that makes the cycle, otherwise return only the cycle.warn::Bool=true
: iftrue
, print a stylised warning message when a cycle is detected.
Return a boolean indicating if the graph is cyclic, and the stack of nodes as a vector.
PlantSimEngine.mapped_variables
— Functionmapped_variables(mapping, dependency_graph=first(hard_dependencies(mapping; verbose=false)); verbose=false)
Get the variables for each organ type from a dependency graph, with MappedVar
s for the multiscale mapping.
Arguments
mapping::Dict{String,T}
: the mapping between models and scales.dependency_graph::DependencyGraph
: the first-order dependency graph where each model in the mapping is assigned a node.
However, models that are identified as hard-dependencies are not given individual nodes. Instead, they are nested as child nodes under other models.
verbose::Bool
: whether to print the stacktrace of the search for the default value in the mapping.
PlantSimEngine.mapped_variables_no_outputs_from_other_scale
— Functionmapped_variables_no_outputs_from_other_scale(mapping, dependency_graph=first(hard_dependencies(mapping; verbose=false)))
Get the variables for each organ type from a dependency graph, without the variables that are outputs from another scale.
Arguments
mapping::Dict{String,T}
: the mapping between models and scales.dependency_graph::DependencyGraph
: the first-order dependency graph where each model in the mapping is assigned a node.
However, models that are identified as hard-dependencies are not given individual nodes. Instead, they are nested as child nodes under other models.
Details
This function returns a dictionary with the (multiscale-) inputs and outputs variables for each organ type.
Note that this function does not include the variables that are outputs from another scale and not computed by this scale, see mapped_variables_with_outputs_as_inputs
for that.
PlantSimEngine.model_
— Methodmodel_(m::AbstractModel)
Get the model of an AbstractModel (it is the model itself if it is not a MultiScaleModel).
PlantSimEngine.object_parallelizable
— Methodobject_parallelizable(x::T)
object_parallelizable(x::DependencyGraph)
Returns true
if the model x
is parallelizable, i.e. if the model can be computed in parallel for different objects, or false
otherwise.
The default implementation returns false
for all models. If you develop a model that is parallelizable over objects, you should add a method to ObjectDependencyTrait
for your model.
Note that this method can also be applied on a DependencyGraph
directly, in which case it returns true
if all models in the graph are parallelizable, and false
otherwise.
See also
timestep_parallelizable
: Returnstrue
if the model is parallelizable over time-steps, andfalse
otherwise.parallelizable
: Returnstrue
if the model is parallelizable, andfalse
otherwise.ObjectDependencyTrait
: Defines the trait about the eventual dependence of a model to other objects for its computation.
Examples
Define a dummy process:
using PlantSimEngine
# Define a test process:
@process "TestProcess"
Define a model that is object independent:
struct MyModel <: AbstractTestprocessModel end
# Override the object dependency trait:
PlantSimEngine.ObjectDependencyTrait(::Type{MyModel}) = IsObjectIndependent()
Check if the model is parallelizable over objects:
object_parallelizable(MyModel()) # true
PlantSimEngine.parallelizable
— Methodparallelizable(::T)
object_parallelizable(x::DependencyGraph)
Returns true
if the model T
or the whole dependency graph is parallelizable, i.e. if the model can be computed in parallel for different time-steps or objects. The default implementation returns false
for all models.
See also
timestep_parallelizable
: Returnstrue
if the model is parallelizable over time-steps, andfalse
otherwise.object_parallelizable
: Returnstrue
if the model is parallelizable over objects, andfalse
otherwise.TimeStepDependencyTrait
: Defines the trait about the eventual dependence of a model to other time-steps for its computation.
Examples
Define a dummy process:
using PlantSimEngine
# Define a test process:
@process "TestProcess"
Define a model that is parallelizable:
struct MyModel <: AbstractTestprocessModel end
# Override the time-step dependency trait:
PlantSimEngine.TimeStepDependencyTrait(::Type{MyModel}) = IsTimeStepIndependent()
# Override the object dependency trait:
PlantSimEngine.ObjectDependencyTrait(::Type{MyModel}) = IsObjectIndependent()
Check if the model is parallelizable:
parallelizable(MyModel()) # true
Or if we want to be more explicit:
timestep_parallelizable(MyModel())
object_parallelizable(MyModel())
PlantSimEngine.pre_allocate_outputs
— Methodpre_allocate_outputs(statuses, outs, nsteps; check=true)
Pre-allocate the outputs of needed variable for each node type in vectors of vectors. The first level vectors have length nsteps, and the second level vectors have length n_nodes of this type.
Note that we pre-allocate the vectors for the time-steps, but not for each organ, because we don't know how many nodes will be in each organ in the future (organs can appear or disapear).
Arguments
statuses
: a dictionary of status by node typeouts
: a dictionary of outputs by node typensteps
: the number of time-stepscheck
: whether to check the mapping for errors. Default (true
) returns an error if some variables do not exist.
If false and some variables are missing, return an info, remove the unknown variables and continue.
Returns
- A dictionary of pre-allocated output of vector of time-step and vector of node of that type.
Examples
julia> using PlantSimEngine, MultiScaleTreeGraph, PlantSimEngine.Examples
Import example models (can be found in the examples
folder of the package, or in the Examples
sub-modules):
julia> using PlantSimEngine.Examples;
Define the models mapping:
julia> mapping = Dict( "Plant" => ( MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_assimilation => ["Leaf"], :carbon_demand => ["Leaf", "Internode"], :carbon_allocation => ["Leaf", "Internode"] ], ),
MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => ["Leaf" => :Rm, "Internode" => :Rm],] ), ),"Internode" => ( ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), Status(TT=10.0, carbon_biomass=1.0) ), "Leaf" => ( MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => "Soil",], ), ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), Status(aPPFD=1300.0, TT=10.0, carbon_biomass=1.0), ), "Soil" => ( ToySoilWaterModel(), ), );
Importing an example MTG provided by the package:
julia> mtg = import_mtg_example();
julia> statuses, status_templates, reverse_multiscale_mapping, vars_need_init = PlantSimEngine.init_statuses(mtg, mapping);
julia> outs = Dict("Leaf" => (:carbon_assimilation, :carbon_demand), "Soil" => (:soil_water_content,));
Pre-allocate the outputs as a dictionary:
julia> preallocated_vars = PlantSimEngine.pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outs, 2);
The dictionary has a key for each organ from which we want outputs:
julia> collect(keys(preallocated_vars))
2-element Vector{String}:
"Soil"
"Leaf"
Each organ has a dictionary of variables for which we want outputs from, with the pre-allocated empty vectors (one per time-step that will be filled with one value per node):
julia> collect(keys(preallocated_vars["Leaf"]))
3-element Vector{Symbol}:
:carbon_assimilation
:node
:carbon_demand
PlantSimEngine.ref_var
— Methodref_var(v)
Create a reference to a variable. If the variable is already a Base.RefValue
, it is returned as is, else it is returned as a Ref to the copy of the value, or a Ref to the RefVector
(in case v
is a RefVector
).
Examples
julia> using PlantSimEngine;
julia> PlantSimEngine.ref_var(1.0)
Base.RefValue{Float64}(1.0)
julia> PlantSimEngine.ref_var([1.0])
Base.RefValue{Vector{Float64}}([1.0])
julia> PlantSimEngine.ref_var(Base.RefValue(1.0))
Base.RefValue{Float64}(1.0)
julia> PlantSimEngine.ref_var(Base.RefValue([1.0]))
Base.RefValue{Vector{Float64}}([1.0])
julia> PlantSimEngine.ref_var(PlantSimEngine.RefVector([Ref(1.0), Ref(2.0), Ref(3.0)]))
Base.RefValue{PlantSimEngine.RefVector{Float64}}(RefVector{Float64}[1.0, 2.0, 3.0])
PlantSimEngine.reverse_mapping
— Methodreverse_mapping(mapping::Dict{String,Tuple{Any,Vararg{Any}}}; all=true)
reverse_mapping(mapped_vars::Dict{String,Dict{Symbol,Any}})
Get the reverse mapping of a dictionary of model mapping, i.e. the variables that are mapped to other scales, or in other words, what variables are given to other scales from a given scale. This is used for e.g. knowing which scales are needed to add values to others.
Arguments
mapping::Dict{String,Any}
: A dictionary of model mapping.all::Bool
: Whether to get all the variables that are mapped to other scales, including the ones that are mapped as single values.
Returns
A dictionary of organs (keys) with a dictionary of organs => vector of pair of variables. You can read the output as: "for each organ (source organ), to which other organ (target organ) it is giving values for its own variables. Then for each of these source organs, which variable it is giving to the target organ (first symbol in the pair), and to which variable it is mapping the value into the target organ (second symbol in the pair)".
Examples
julia> using PlantSimEngine
Import example models (can be found in the examples
folder of the package, or in the Examples
sub-modules):
julia> using PlantSimEngine.Examples;
julia> mapping = Dict( "Plant" => MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_assimilation => ["Leaf"], :carbon_demand => ["Leaf", "Internode"], :carbon_allocation => ["Leaf", "Internode"] ], ), "Internode" => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), "Leaf" => ( MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => "Soil",], ), ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), Status(aPPFD=1300.0, TT=10.0), ), "Soil" => ( ToySoilWaterModel(), ), );
Notice we provide "Soil", not ["Soil"] in the mapping of the ToyAssimModel
for the Leaf
. This is because we expect a single value for the soil_water_content
to be mapped here (there is only one soil). This allows to get the value as a singleton instead of a vector of values.
julia> PlantSimEngine.reverse_mapping(mapping)
Dict{String, Dict{String, Dict{Symbol, Any}}} with 3 entries:
"Soil" => Dict("Leaf"=>Dict(:soil_water_content=>:soil_water_content))
"Internode" => Dict("Plant"=>Dict(:carbon_allocation=>:carbon_allocation, :ca…
"Leaf" => Dict("Plant"=>Dict(:carbon_allocation=>:carbon_allocation, :ca…
PlantSimEngine.save_results!
— Methodsave_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)
.
PlantSimEngine.search_inputs_in_multiscale_output
— Methodsearch_inputs_in_multiscale_output(process, organ, inputs, soft_dep_graphs)
Arguments
process::Symbol
: the process for which we want to find the soft dependencies at other scales.organ::String
: the organ for which we want to find the soft dependencies.inputs::Dict{Symbol, Vector{Pair{Symbol}, Tuple{Symbol, Vararg{Symbol}}}}
: a dict of process => [:subprocess => (:var1, :var2)].soft_dep_graphs::Dict{String, ...}
: a dict of organ => (softdepgraph, inputs, outputs).rev_mapping::Dict{Symbol, Symbol}
: a dict of mapped variable => source variable (this is the reverse mapping).- 'harddependenciesfromotherscale' : a vector of HardDependencyNode to provide access to the hard dependencies without traversing the whole graph
Details
The inputs (and similarly, outputs) give the inputs of each process, classified by the process it comes from. It can come from itself (its own inputs), or from another process that is a hard-dependency.
Returns
A dictionary with the soft dependencies variables found in outputs of other scales for each process, e.g.:
Dict{String, Dict{Symbol, Vector{Symbol}}} with 2 entries:
"Internode" => Dict(:carbon_demand=>[:carbon_demand])
"Leaf" => Dict(:carbon_assimilation=>[:carbon_assimilation], :carbon_demand=>[:carbon_demand])
This means that the variable :carbon_demand
is computed by the process :carbon_demand
at the scale "Internode", and the variable :carbon_assimilation
is computed by the process :carbon_assimilation
at the scale "Leaf". Those variables are used as inputs for the process that we just passed.
PlantSimEngine.search_inputs_in_output
— Methodsearch_inputs_in_output(process, inputs, outputs)
Return a dictionary with the soft dependencies of the processes in the dependency graph d
. A soft dependency is a dependency that is not explicitely defined in the model, but that can be inferred from the inputs and outputs of the processes.
Arguments
process::Symbol
: the process for which we want to find the soft dependencies.inputs::Dict{Symbol, Vector{Pair{Symbol}, Tuple{Symbol, Vararg{Symbol}}}}
: a dict of process => symbols of inputs per process.outputs::Dict{Symbol, Tuple{Symbol, Vararg{Symbol}}}
: a dict of process => symbols of outputs per process.
Details
The inputs (and similarly, outputs) give the inputs of each process, classified by the process it comes from. It can come from itself (its own inputs), or from another process that is a hard-dependency.
Returns
A dictionary with the soft dependencies for the processes.
Example
in_ = Dict(
:process3 => [:process3=>(:var4, :var5), :process2=>(:var1, :var3), :process1=>(:var1, :var2)],
:process4 => [:process4=>(:var0,)],
:process6 => [:process6=>(:var7, :var9)],
:process5 => [:process5=>(:var5, :var6)],
)
out_ = Dict(
:process3 => Pair{Symbol}[:process3=>(:var4, :var6), :process2=>(:var4, :var5), :process1=>(:var3,)],
:process4 => [:process4=>(:var1, :var2)],
:process6 => [:process6=>(:var8,)],
:process5 => [:process5=>(:var7,)],
)
search_inputs_in_output(:process3, in_, out_)
(process4 = (:var1, :var2),)
PlantSimEngine.soft_dependencies
— Functionsoft_dependencies(d::DependencyGraph)
Return a DependencyGraph
with the soft dependencies of the processes in the dependency graph d
. A soft dependency is a dependency that is not explicitely defined in the model, but that can be inferred from the inputs and outputs of the processes.
Arguments
d::DependencyGraph
: the hard-dependency graph.
Example
using PlantSimEngine
# Load the dummy models given as example in the package:
using PlantSimEngine.Examples
# Create a model list:
models = ModelList(
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model(),
process4=Process4Model(),
process5=Process5Model(),
process6=Process6Model(),
)
# Create the hard-dependency graph:
hard_dep = hard_dependencies(models.models, verbose=true)
# Get the soft dependencies graph:
soft_dep = soft_dependencies(hard_dep)
PlantSimEngine.status_from_template
— Methodstatus_from_template(d::Dict{Symbol,Any})
Create a status from a template dictionary of variables and values. If the values are already RefValues or RefVectors, they are used as is, else they are converted to Refs.
Arguments
d::Dict{Symbol,Any}
: A dictionary of variables and values.
Returns
- A
Status
.
Examples
julia> using PlantSimEngine
julia> a, b = PlantSimEngine.status_from_template(Dict(:a => 1.0, :b => 2.0));
julia> a
1.0
julia> b
2.0
PlantSimEngine.timestep_parallelizable
— Methodtimestep_parallelizable(x::T)
timestep_parallelizable(x::DependencyGraph)
Returns true
if the model x
is parallelizable, i.e. if the model can be computed in parallel over time-steps, or false
otherwise.
The default implementation returns false
for all models. If you develop a model that is parallelizable over time-steps, you should add a method to ObjectDependencyTrait
for your model.
Note that this method can also be applied on a DependencyGraph
directly, in which case it returns true
if all models in the graph are parallelizable, and false
otherwise.
See also
object_parallelizable
: Returnstrue
if the model is parallelizable over time-steps, andfalse
otherwise.parallelizable
: Returnstrue
if the model is parallelizable, andfalse
otherwise.TimeStepDependencyTrait
: Defines the trait about the eventual dependence of a model to other time-steps for its computation.
Examples
Define a dummy process:
using PlantSimEngine
# Define a test process:
@process "TestProcess"
Define a model that is time-step independent:
struct MyModel <: AbstractTestprocessModel end
# Override the time-step dependency trait:
PlantSimEngine.TimeStepDependencyTrait(::Type{MyModel}) = IsTimeStepIndependent()
Check if the model is parallelizable over objects:
timestep_parallelizable(MyModel()) # true
PlantSimEngine.transform_single_node_mapped_variables_as_self_node_output!
— Methodtransform_single_node_mapped_variables_as_self_node_output!(mapped_vars)
Find variables that are inputs to other scales as a SingleNodeMapping
and declare them as MappedVar from themselves in the source scale. This helps us declare it as a reference when we create the template status objects.
These node are found in the mapping as [:variable_name => "Plant"]
(notice that "Plant" is a scalar value).
PlantSimEngine.traverse_dependency_graph!
— Methodtraverse_dependency_graph(node::HardDependencyNode, f::Function, var::Vector)
Apply function f
to node
, and then its children (hard-dependency nodes).
Mutate the vector var
by pushing a pair of the node process name and the result of the function f
.
PlantSimEngine.traverse_dependency_graph!
— Methodtraverse_dependency_graph(node::SoftDependencyNode, f::Function, var::Vector; visit_hard_dep=true)
Apply function f
to node
, visit its hard dependency nodes (if visit_hard_dep=true
), and then its soft dependency children.
Mutate the vector var
by pushing a pair of the node process name and the result of the function f
.
PlantSimEngine.traverse_dependency_graph
— Methodtraverse_dependency_graph(graph::DependencyGraph, f::Function; visit_hard_dep=true)
Traverse the dependency graph
and apply the function f
to each node. The first-level soft-dependencies are traversed first, then their hard-dependencies (if visit_hard_dep=true
), and then the children of the soft-dependencies.
Return a vector of pairs of the node and the result of the function f
.
Example
using PlantSimEngine
# Including example processes and models:
using PlantSimEngine.Examples;
function f(node)
node.value
end
vars = (
process1=Process1Model(1.0),
process2=Process2Model(),
process3=Process3Model(),
process4=Process4Model(),
process5=Process5Model(),
process6=Process6Model(),
process7=Process7Model(),
)
graph = dep(vars)
traverse_dependency_graph(graph, f)
PlantSimEngine.variables_multiscale
— Functionvariables_multiscale(node, organ, mapping, st=NamedTuple())
Get the variables of a HardDependencyNode, taking into account the multiscale mapping, i.e. defining variables as MappedVar
if they are mapped to another scale. The default values are taken from the model if not given by the user (st
), and are marked as UninitializedVar
if they are inputs of the node.
Return a NamedTuple with the variables and their default values.
Arguments
node::HardDependencyNode
: the node to get the variables from.organ::String
: the organ type, e.g. "Leaf".vars_mapping::Dict{String,T}
: the mapping of the models (see details below).st::NamedTuple
: an optional named tuple with default values for the variables.
Details
The vars_mapping
is a dictionary with the organ type as key and a dictionary as value. It is computed from the user mapping like so:
PlantSimEngine.variables_outputs_from_other_scale
— Methodvariables_outputs_from_other_scale(mapped_vars)
For each organ in the mapped_vars
, find the variables that are outputs from another scale and not computed at this scale otherwise. This function is used with mapped_variables
PlantSimEngine.variables_typed
— Methodvariables_typed(model)
variables_typed(model, models...)
Returns a named tuple with the name and the types of the variables needed by a model, or a union of those for several models.
Examples
using PlantSimEngine;
# Load the dummy models given as example in the package:
using PlantSimEngine.Examples;
PlantSimEngine.variables_typed(Process1Model(1.0))
(var1 = Float64, var2 = Float64, var3 = Float64)
PlantSimEngine.variables_typed(Process1Model(1.0), Process2Model())
# output
(var4 = Float64, var5 = Float64, var1 = Float64, var2 = Float64, var3 = Float64)
See also
PlantSimEngine.vars_not_init_
— Methodvars_not_init_(st<:Status, var_names)
Get which variable is not properly initialized in the status struct.