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ᵢ)
    )
outs_sim = run!(leaf)
df_sim = PlantSimEngine.convert_outputs(outs_sim, DataFrame);
30×4 DataFrame
RowaPPFDTₗCᵢA
Float64Float64Float64Float64
11274.9126.44192.9117.06117
21274.9126.44193.6687.09454
31275.026.38155.415.3488
41275.626.37156.2485.39005
51274.7526.41113.1523.23804
61274.9126.45112.5543.2002
71274.9126.4272.82971.03639
81274.9126.4273.06431.04978
91274.7526.4351.7086-0.20059
101274.9126.4251.7036-0.199125
111275.3426.38211.7037.8809
121275.026.38210.0697.8114
131274.9126.39210.1157.81245
141275.626.37209.9257.80616
151274.9126.33316.56811.9153
161274.9126.34315.84211.8904
171275.026.35451.14814.4488
181274.9126.33448.55514.4191
191274.9126.42575.08615.4166
201274.1526.42570.87415.3896
211275.026.42700.04416.0729
221274.9126.42695.75716.0537
231274.9126.42867.60916.245
241274.1526.44867.31816.2443
251274.7526.411067.0116.2453
261274.9126.391068.9416.246
271274.7526.471273.1216.2433
281275.626.471273.8216.2433
291274.7526.481392.716.243
301275.626.451385.6716.244

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))
outs_sim2 = run!(leaf, w)
df_sim2 = PlantSimEngine.convert_outputs(outs_sim2, DataFrame);

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.