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 ModelMapping, 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 ModelMapping 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 ModelMapping:

models = ModelMapping(
    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 = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day)

We can now run the simulation:

output_initial = run!(models, meteo_day)
output_initial[1:3,:] # show the first 3 rows of the output
TimeStepTable{Status{(:TT_cu, :LAI, :aPPFD,...}(3 x 5):
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

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 ModelMapping, and PlantSimEngine will automatically update the dependency graph, and adapt the simulation to the new model.

Let's switch ToyRUEGrowthModel with ToyAssimGrowthModel:

models2 = ModelMapping(
    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)
output_updated[1:3,:] # show the first 3 rows of the output
TimeStepTable{Status{(:TT_cu, :LAI, :aPPFD,...}(3 x 8):
TT_cu LAI aPPFD carbon_assimilation Rm Rg biomass_increment biomass
Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64
1 0.0 0.00554988 0.0396961 0.00793922 0.00396961 0.996692 -0.992722 -0.992722
2 0.0 0.00554988 0.02173 0.004346 0.002173 0.998189 -0.996016 -1.98874
3 0.0 0.00554988 0.0314899 0.00629798 0.00314899 0.997376 -0.994227 -2.98297

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!💪

Note

This was a very standard but straightforward example. Sometimes other models will require to add other models to the ModelMapping. 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.

Note

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.