Standard model coupling
Setting up your environment
Again, make sure you have a working Julia environment with PlantSimengine added to it, and the other recommended companion packages. Details for getting to that point are provided on the Installing and running PlantSimEngine page.
ModelList
The ModelList
is a container that holds a list of models, their parameter values, and the status of the variables associated to them.
If one looks at prior examples, the Modellists so far have only contained a single model, whose input variables are initialised in the Modellist status
keyword argument.
Example models are all taken from the example scripts in the examples
folder.
Here's a first ModelList
declaration with a light interception model, requiring input Leaf Area Index (LAI):
modellist_coupling_part_1 = ModelList(Beer(0.5), status = (LAI = 2.0,))
Here's a second one with a Leaf Area Index model, with some example Cumulated Thermal Time as input. (This TT_cu is usually computed from weather data):
modellist_coupling_part_2 = ModelList(
ToyLAIModel(),
status=(TT_cu=1.0:2000.0,), # Pass the cumulated degree-days as input to the model
)
Combining models
Suppose we want our ToyLAIModel to compute the LAI
for the light interception model.
We can couple the two models by having them be part of a single ModelList
. The LAI
variable will then be a coupled output computed by the ToyLAIModel, then used as input by Beer
. It will no longer need to be declared as part of the [status
.
This is an instance of what we call a "soft dependency" coupling: a model depends on another model's outputs for its inputs.
Here's a first attempt :
using PlantSimEngine
# Import the examples defined in the `Examples` sub-module:
using PlantSimEngine.Examples
# A ModelList with two coupled models
models = ModelList(
ToyLAIModel(),
Beer(0.5),
status=(TT_cu=1.0:2000.0,),
)
run!(models)
type NamedTuple has no field Ri_PAR_f
Oops, we get an error related to the weather data, with the detailed output being:
ERROR: type NamedTuple has no field Ri_PAR_f
Stacktrace:
[1] getindex(mnt::Atmosphere{(), Tuple{}}, i::Symbol)
@ PlantMeteo ~/Path/to/PlantMeteo/src/structs/atmosphere.jl:147
[2] getcolumn(row::PlantMeteo.TimeStepRow{Atmosphere{(), Tuple{}}}, nm::Symbol)
@ PlantMeteo ~/Path/to/PlantMeteo/src/structs/TimeStepTable.jl:205
...
The Beer
model requires a specific meteorological parameter. Let's fix that by importing the example weather data :
using PlantSimEngine
# PlantMeteo and CSV packages are now used
using PlantMeteo, CSV
# Import the examples defined in the `Examples` sub-module:
using PlantSimEngine.Examples
# Import example weather data
meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18)
# A ModelList with two coupled models
models = ModelList(
ToyLAIModel(),
Beer(0.5),
status=(TT_cu=cumsum(meteo_day.TT),), # We can now compute a genuine cumulative thermal time from the weather data
)
# Add the weather data to the run! call
outputs_coupled = run!(models, meteo_day)
TimeStepTable{Status{(:TT_cu, :LAI, :aPPFD)...}(365 x 3):
╭─────┬──────────┬────────────┬───────────╮
│ Row │ TT_cu │ LAI │ aPPFD │
│ │ Float64 │ Float64 │ Float64 │
├─────┼──────────┼────────────┼───────────┤
│ 1 │ 0.0 │ 0.00554988 │ 0.0396961 │
│ 2 │ 0.0 │ 0.00554988 │ 0.02173 │
│ 3 │ 0.0 │ 0.00554988 │ 0.0314899 │
│ 4 │ 0.0 │ 0.00554988 │ 0.0390834 │
│ 5 │ 0.0 │ 0.00554988 │ 0.0454514 │
│ 6 │ 0.0 │ 0.00554988 │ 0.0472677 │
│ 7 │ 0.0 │ 0.00554988 │ 0.04346 │
│ 8 │ 0.0 │ 0.00554988 │ 0.0469832 │
│ 9 │ 0.0 │ 0.00554988 │ 0.0291703 │
│ 10 │ 0.0 │ 0.00554988 │ 0.0140052 │
│ 11 │ 0.0 │ 0.00554988 │ 0.0505283 │
│ 12 │ 0.0 │ 0.00554988 │ 0.0405277 │
│ 13 │ 0.5625 │ 0.00557831 │ 0.0297814 │
│ 14 │ 0.945833 │ 0.00559777 │ 0.0433269 │
│ 15 │ 0.979167 │ 0.00559946 │ 0.0470271 │
│ ⋮ │ ⋮ │ ⋮ │ ⋮ │
╰─────┴──────────┴────────────┴───────────╯
350 rows omitted
And there you have it. The light interception model made its computations using the Leaf Area Index computed by ToyLAIModel.
Further coupling
Of course, one can keep adding models. Here's an example ModelList with another model, ToyRUEGrowthModel, which computes the carbon biomass increment caused by photosynthesis.
models = ModelList(
ToyLAIModel(),
Beer(0.5),
ToyRUEGrowthModel(0.2),
status=(TT_cu=cumsum(meteo_day.TT),),
)
nothing # hide