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