Parameter fitting

The fit method

The package provides methods to the generic fit function from PlantSimEngine to calibrate a model with data.

The arguments depends on the methods, but usually takes several parameters:

  • the model type, e.g. FvCB
  • a DataFrame with the data (depends on the given method)
  • keyword arguments (also depend on the fit method)

Example with FvCB

A fit method is provided by the package to calibrate the parameters of the FvCB model (Farquhar et al., 1980).

Here is an example usage from the documentation of the method:

using PlantBiophysics, PlantSimEngine
using DataFrames, Plots

df = read_walz(joinpath(dirname(dirname(pathof(PlantBiophysics))),"test","inputs","data","P1F20129.csv"))
# Removing the Rh and light curves for the fitting because temperature varies
filter!(x -> x.curve != "Rh Curve" && x.curve != "ligth Curve", df)

# Fit the parameter values:
VcMaxRef, JMaxRef, RdRef, TPURef = fit(Fvcb, df; Tᵣ = 25.0)
(VcMaxRef = 46.2467784134993, JMaxRef = 80.36773471227262, RdRef = 0.49949504402060546, TPURef = 5.596944472747526, Tᵣ = 25.0)

Now that our parameters are optimized, we can check how close to the data a simulation would get.

First, let's select only the data used for the CO₂ curve:

# Checking the results:
filter!(x -> x.curve == "CO2 Curve", df)

Now let's re-simulate the assimilation with our optimized parameter values:

leaf =
    ModelList(
        FvcbRaw(VcMaxRef = VcMaxRef, JMaxRef = JMaxRef, RdRef = RdRef, TPURef = TPURef),
        status = (Tₗ = df.Tₗ, aPPFD = df.aPPFD, Cᵢ = df.Cᵢ)
    )
run!(leaf)
df_sim = DataFrame(leaf);
30×5 DataFrame
RowaPPFDTₗCᵢAtimestep
Float64Float64Float64Float64Int64
11274.9126.44192.9117.061171
21274.9126.44193.6687.094542
31275.026.38155.415.34883
41275.626.37156.2485.390054
51274.7526.41113.1523.238045
61274.9126.45112.5543.20026
71274.9126.4272.82971.036397
81274.9126.4273.06431.049788
91274.7526.4351.7086-0.200599
101274.9126.4251.7036-0.19912510
111275.3426.38211.7037.880911
121275.026.38210.0697.811412
131274.9126.39210.1157.8124513
141275.626.37209.9257.8061614
151274.9126.33316.56811.915315
161274.9126.34315.84211.890416
171275.026.35451.14814.448817
181274.9126.33448.55514.419118
191274.9126.42575.08615.416619
201274.1526.42570.87415.389620
211275.026.42700.04416.072921
221274.9126.42695.75716.053722
231274.9126.42867.60916.24523
241274.1526.44867.31816.244324
251274.7526.411067.0116.245325
261274.9126.391068.9416.24626
271274.7526.471273.1216.243327
281275.626.471273.8216.243328
291274.7526.481392.716.24329
301275.626.451385.6716.24430

Finally, we can make an A-Cᵢ plot using our custom ACi structure as follows:

aci = PlantBiophysics.ACi(VcMaxRef, JMaxRef, RdRef, df[:,:A], df_sim[:,:A], df[:,:Cᵢ], df_sim[:,:Cᵢ])
plot(aci, leg=:bottomright)
Example block output

Our simulation fits very closely the observations, nice!

There are another implementation of the FvCB model in our package. One that couples the photosynthesis with the stomatal conductance. And this one computes Cᵢ too. Let's check if it works with this one too by using dummy parameter values for the conductance model:

leaf = ModelList(
        Fvcb(VcMaxRef = VcMaxRef, JMaxRef = JMaxRef, RdRef = RdRef, Tᵣ = 25.0, TPURef = TPURef),
        Medlyn(0.03, 12.),
        status = (Tₗ = df.Tₗ, aPPFD = df.aPPFD, Cₛ = df.Cₐ, Dₗ = 0.1)
    )

w = Weather(select(df, :T, :P, :Rh, :Cₐ, :T => (x -> 10) => :Wind))
run!(leaf, w)
df_sim2 = DataFrame(leaf)

aci2 = PlantBiophysics.ACi(VcMaxRef, JMaxRef, RdRef, df[:,:A], df_sim2[:,:A], df[:,:Cᵢ], df_sim2[:,:Cᵢ])
plot(aci2, leg = :bottomright)
Example block output

We can see the results differ a bit, but it is because we add a lot more computation here, hence adding some degrees of liberty.