Model switching
One of the main objective of PlantSimEngine is allowing users to switch between model implementations for a given process without making any change to the PlantSimEngine codebase.
The package was designed around this idea to make easy changes easy and efficient. Switch models in the ModelList
, and call the run!
function again. No other changes are required if no new variables are introduced.
A first simulation as a starting point
With a working environment, let's create a ModelList
with several models from the example scripts in the examples
folder:
Importing the models from the scripts:
using PlantSimEngine
# Import the examples defined in the `Examples` sub-module:
using PlantSimEngine.Examples
Coupling the models in a ModelList
:
models = ModelList(
ToyLAIModel(),
Beer(0.5),
ToyRUEGrowthModel(0.2),
status=(TT_cu=cumsum(meteo_day.TT),),
)
We can the simulation by calling the run!
function with meteorology data. Here we use an example data set:
meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18)
We can now run the simulation:
output_initial = run!(models, meteo_day)
TimeStepTable{Status{(:TT_cu, :LAI, :aPPFD,...}(365 x 5):
╭─────┬──────────┬────────────┬───────────┬────────────┬───────────────────╮
│ Row │ TT_cu │ LAI │ aPPFD │ biomass │ biomass_increment │
│ │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼──────────┼────────────┼───────────┼────────────┼───────────────────┤
│ 1 │ 0.0 │ 0.00554988 │ 0.0396961 │ 0.00793922 │ 0.00793922 │
│ 2 │ 0.0 │ 0.00554988 │ 0.02173 │ 0.0122852 │ 0.004346 │
│ 3 │ 0.0 │ 0.00554988 │ 0.0314899 │ 0.0185832 │ 0.00629798 │
│ 4 │ 0.0 │ 0.00554988 │ 0.0390834 │ 0.0263999 │ 0.00781668 │
│ 5 │ 0.0 │ 0.00554988 │ 0.0454514 │ 0.0354902 │ 0.00909028 │
│ 6 │ 0.0 │ 0.00554988 │ 0.0472677 │ 0.0449437 │ 0.00945354 │
│ 7 │ 0.0 │ 0.00554988 │ 0.04346 │ 0.0536357 │ 0.00869201 │
│ 8 │ 0.0 │ 0.00554988 │ 0.0469832 │ 0.0630324 │ 0.00939665 │
│ 9 │ 0.0 │ 0.00554988 │ 0.0291703 │ 0.0688664 │ 0.00583406 │
│ 10 │ 0.0 │ 0.00554988 │ 0.0140052 │ 0.0716675 │ 0.00280105 │
│ 11 │ 0.0 │ 0.00554988 │ 0.0505283 │ 0.0817731 │ 0.0101057 │
│ 12 │ 0.0 │ 0.00554988 │ 0.0405277 │ 0.0898787 │ 0.00810554 │
│ 13 │ 0.5625 │ 0.00557831 │ 0.0297814 │ 0.095835 │ 0.00595629 │
│ 14 │ 0.945833 │ 0.00559777 │ 0.0433269 │ 0.1045 │ 0.00866538 │
│ 15 │ 0.979167 │ 0.00559946 │ 0.0470271 │ 0.113906 │ 0.00940542 │
│ ⋮ │ ⋮ │ ⋮ │ ⋮ │ ⋮ │ ⋮ │
╰─────┴──────────┴────────────┴───────────┴────────────┴───────────────────╯
350 rows omitted
Switching one model in the simulation
Now what if we want to switch the model that computes growth ? We can do this by simply replacing the model in the ModelList
, and PlantSimEngine will automatically update the dependency graph, and adapt the simulation to the new model.
Let's switch ToyRUEGrowthModel with ToyAssimGrowthModel:
models2 = ModelList(
ToyLAIModel(),
Beer(0.5),
ToyAssimGrowthModel(), # This was `ToyRUEGrowthModel(0.2)` before
status=(TT_cu=cumsum(meteo_day.TT),),
)
ToyAssimGrowthModel is a little bit more complex than ToyRUEGrowthModel
](@ref), as it also computes the maintenance and growth respiration of the plant, so it has more parameters (we use the default values here).
We can run a new simulation and see that the simulation's results are different from the previous simulation:
output_updated = run!(models2, meteo_day)
TimeStepTable{Status{(:TT_cu, :LAI, :aPPFD,...}(365 x 8):
╭─────┬──────────┬────────────┬───────────┬─────────────────────┬────────────┬──
│ Row │ TT_cu │ LAI │ aPPFD │ carbon_assimilation │ Rm │ ⋯
│ │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ ⋯
├─────┼──────────┼────────────┼───────────┼─────────────────────┼────────────┼──
│ 1 │ 0.0 │ 0.00554988 │ 0.0396961 │ 0.00793922 │ 0.00396961 │ ⋯
│ 2 │ 0.0 │ 0.00554988 │ 0.02173 │ 0.004346 │ 0.002173 │ ⋯
│ 3 │ 0.0 │ 0.00554988 │ 0.0314899 │ 0.00629798 │ 0.00314899 │ ⋯
│ 4 │ 0.0 │ 0.00554988 │ 0.0390834 │ 0.00781668 │ 0.00390834 │ ⋯
│ 5 │ 0.0 │ 0.00554988 │ 0.0454514 │ 0.00909028 │ 0.00454514 │ ⋯
│ 6 │ 0.0 │ 0.00554988 │ 0.0472677 │ 0.00945354 │ 0.00472677 │ ⋯
│ 7 │ 0.0 │ 0.00554988 │ 0.04346 │ 0.00869201 │ 0.004346 │ ⋯
│ 8 │ 0.0 │ 0.00554988 │ 0.0469832 │ 0.00939665 │ 0.00469832 │ ⋯
│ 9 │ 0.0 │ 0.00554988 │ 0.0291703 │ 0.00583406 │ 0.00291703 │ ⋯
│ 10 │ 0.0 │ 0.00554988 │ 0.0140052 │ 0.00280105 │ 0.00140052 │ ⋯
│ 11 │ 0.0 │ 0.00554988 │ 0.0505283 │ 0.0101057 │ 0.00505283 │ ⋯
│ 12 │ 0.0 │ 0.00554988 │ 0.0405277 │ 0.00810554 │ 0.00405277 │ ⋯
│ 13 │ 0.5625 │ 0.00557831 │ 0.0297814 │ 0.00595629 │ 0.00297814 │ ⋯
│ 14 │ 0.945833 │ 0.00559777 │ 0.0433269 │ 0.00866538 │ 0.00433269 │ ⋯
│ 15 │ 0.979167 │ 0.00559946 │ 0.0470271 │ 0.00940542 │ 0.00470271 │ ⋯
│ ⋮ │ ⋮ │ ⋮ │ ⋮ │ ⋮ │ ⋮ │ ⋱
╰─────┴──────────┴────────────┴───────────┴─────────────────────┴────────────┴──
3 columns and 350 rows omitted
And that's it! We can switch between models without changing the code, and without having to recompute the dependency graph manually. This is a very powerful feature of PlantSimEngine!💪
This was a very standard but straightforward example. Sometimes other models will require to add other models to the ModelList
. For example ToyAssimGrowthModel could have required a maintenance respiration model. In this case PlantSimEngine
will indicate what kind of model is required for the simulation.
In our example we replaced what we call a soft-dependency coupling, but the same principle applies to hard-dependencies. Hard and Soft dependencies are concepts related to model coupling, and are discussed in more detail in Standard model coupling and Coupling more complex models.