PlantSimEngine.jlPlantSimEngine.jl

Couple Models On One Object​#

In this example, we combine three models to calculate how a canopy develops and absorbs light over 30 days. We represent the whole canopy as one object: one part of the simulated system, with its own values. We do not describe individual leaves here.

The three models pass values to one another:

  1. ToyDegreeDaysCumulModel uses temperature to calculate cumulative thermal time, a measure of accumulated warmth.

  2. ToyLAIModel uses that thermal time to calculate leaf area index (LAI).

  3. Beer uses LAI and incoming radiation to calculate the light absorbed by the canopy.

These are teaching models with illustrative parameters. They show how to connect calculations; their results are not predictions for a particular crop.

Start with the tutorial installation if these packages are not yet available in your Julia project.

Prepare the weather data​#

We use a weather file included with PlantSimEngine. Each row describes one day. The file records daily radiation totals in MJ m⁻² d⁻¹. Beer needs the average radiation during that day in W m⁻², so the code below converts those columns. SW means shortwave radiation, PAR is the light used for photosynthesis, and NIR means near-infrared radiation. The weather data are called the environment in this simulation.

julia
using PlantSimEngine, PlantMeteo, Dates, DataFrames
using PlantSimEngine.Examples

weather = read_weather(
    joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"),
    :Ri_SW_f => (x -> x .* 1e6 ./ 86_400) => :Ri_SW_f,
    :Ri_PAR_f => (x -> x .* 1e6 ./ 86_400) => :Ri_PAR_f,
    :Ri_NIR_f => (x -> x .* 1e6 ./ 86_400) => :Ri_NIR_f;
    duration=Day,
)

Connect the models​#

Put the three models together in a CompositeModel and give it the weather data:

julia
model = CompositeModel(
    ToyDegreeDaysCumulModel(),
    ToyLAIModel(),
    Beer(0.6);
    environment=weather,
)
CompositeModel Objects: 1 (Scene: 1) Shared environment: TimeStepTable (365 rows) Model applications: 3 ToyDegreeDaysCumulModel ToyLAIModel Beer

PlantSimEngine connects these models automatically. The thermal-time model provides TT_cu to the LAI model, which provides LAI to Beer. Each input has the same name as an output from exactly one other model on this canopy. PlantSimEngine also runs the models in that order, so each calculation can use the result it needs.

Run 30 days and look at the results​#

run! starts the simulation. Here, steps=30 runs the first 30 weather rows, and outputs=:all saves the results from every model at each step. collect_outputs(simulation; sink=DataFrame) gathers those saved results into a DataFrame, the table type provided by DataFrames.jl. DataFrame is already the default, so you can also write collect_outputs(simulation).

These first 30 days fall in winter, so thermal time increases slowly and LAI stays small. The plotting guide shows how to plot results and compare two canopies.

julia
simulation = run!(model; steps=30, outputs=:all)
results = collect_outputs(simulation; sink=DataFrame)
first(select(results, :timestep, :variable, :value), 8)
8×3 DataFrame
Rowtimestepvariablevalue
Int64SymbolFloat64
11LAI0.00554988
21TT0.0
31TT_cu0.0
41aPPFD0.551182
52LAI0.00554988
62TT0.0
72TT_cu0.0
82aPPFD0.301722

Each row records one variable on one day. The last line selects three columns and shows the first eight rows: four variables for each of the first two days. TT is daily thermal time and TT_cu is cumulative thermal time, both in °C d. LAI is leaf area per ground area, in m² m⁻².

final_state gives the latest values, here at the end of day 30. It is also available when you run a simulation without saving its history:

julia
state_at_day_30 = final_state(simulation)
(
    current_step=current_step(simulation),
    TT_cu=state_at_day_30.TT_cu,
    LAI=state_at_day_30.LAI,
    aPPFD=state_at_day_30.aPPFD,
)
(current_step = 30, TT_cu = 6.791666666666664, LAI = 0.005903077441182999, aPPFD = 0.24721139582261295)

aPPFD measures absorbed photosynthetically active radiation (PAR), the light available for photosynthesis. Its unit here is μmol of photons per m² of ground per second, averaged over the day. The area refers to the ground covered by the canopy, rather than the area of its leaves.

Continue for another day​#

step! runs the next day and adds its results to the same simulation:

julia
step!(simulation)
state_at_day_31 = final_state(simulation)
(current_step=current_step(simulation), TT_cu=state_at_day_31.TT_cu)
(current_step = 31, TT_cu = 7.845833333333331)

You have now extended the same history to day 31. Continue with several independent objects, or plot the results.