A pure Julia package to simulate biophysical processes for plants such as photosynthesis, conductances for heat, water vapor and CO₂, latent, sensible energy fluxes, net radiation and temperature.
The benefits of using this package are:
- Blazing fast (few μs for the whole energy balance + photosynthesis + conductances)
- Easy to use and interactive
- Parameter calibration and simulation in one place, works with DataFrames and MTGs
- Great composability:
- easy to extend, add your model for any process, and it just works, with automatic coupling with other models
- easy to integrate into other models or platforms thanks to Julia's great compatibility with other languages
- Easy to read, the code implements the equations close to how they are written in the scientific articles (thanks Julia Unicode!)
- Compatible with other simulation platforms thanks to the MTG format (OpenAlea, ARCHIMED, AMAPStudio...)
- Error propagation using Measurements.jl or MonteCarloMeasurements.jl
Try it !
Start by creating a new environment for your project using the pkg manager. To enter the package manager, just press
] in the REPL, and it will become blue (press backspace to return to the Julia REPL). Then create the environment using this command:
Don't forget the "."! It is used to tell the pkg manager that you activate the project were you currently are.
You can then install PlantBiophysics using this command (still from the pkg manager):
Here is an example usage with a simulation of the energy balance and assimilation of a leaf.
Note that PlantBiophysics is an extension of PlantSimEngine, so we must import both to make a simulation
# Import the packages (you need to install PlantSimEngine first): using PlantBiophysics, PlantSimEngine # Declare the meteorology for the simulated time-step (also possible to import meteo files): meteo = Atmosphere(T = 22.0, Wind = 0.8333, P = 101.325, Rh = 0.4490995) # Using the model from Medlyn et al. (2011) for Gs and the model of Monteith and Unsworth (2013) for the energy balance: leaf = ModelList( energy_balance = Monteith(), photosynthesis = Fvcb(), stomatal_conductance = Medlyn(0.03, 12.0), status = (Ra_SW_f = [13.747, 14.5], sky_fraction = 1.0, aPPFD = 1500.0, d = 0.03) ) run!(leaf, meteo) leaf
And the output would be:
For more examples, please read the documentation.
- Add FvCB model
- Add FvCB iterative model
- Add stomatal + boundary layer conductance models
- Add energy balance model, coupled with photosynthesis amd stomatal conductance models
- Make the functions work on the output from
- Rename skyFraction into sky_fraction
- Add a new conductance model using the version from Duursma, Remko A, Christopher J Blackman, Rosana Lop, et K Martin-StPaul. 2018. « On the Minimum Leaf Conductance: Its Role in Models of Plant Water Use, and Ecological and Environmental Controls ». New Phytologist, 13.
- Make the functions compatible with an MTG, e.g. apply photosynthesis to an MTG, and use the right method for each node. NB: I think the models should be a field of the node.
- Make the functions compatible with several meteorological time-steps
- Add a new struct for that: Weather
- Do it for energy_balance
- stomatal conductance
- Add tests for each
- Update the doc!
- Check if it works with MTGs
- Evaluate using Schymanski et al. (2017) data + leaf measurements models (in progress)
- Check Schymanski input: is Rs = Rnleaf ok? Because Rs is Rn - Rll.
- Add more documentation + tutorial:
- add doc about the design (components, models, model values, multiple dispatch)
- add doc about input files
- add doc for each process
- add a list of models for each process
- add documentation for each model
- add a tutorial for a single leaf at one time-step
- add a tutorial for a single leaf at several time-step
- add a tutorial for a plant
- How to implement a new model -> e.g. conductance (add a
- How to implement a new component:
- Use PrettyTables.jl for printing the Weather and simulation outputs
- Use leaf[:var] in the models implementations instead of leaf.status.var. It will make the code way cleaner.
- Do we have a
leaf[:var]? Implement it if missing.
- Make boundary layer conductances true models as for stomatal conductances, but maybe define the current ones as default when calling the function (I mean if no model is provided, use the ones currently in use).
- Make a diagram of a leaf for gaz and energy exchanges
- Add checks on the models provided for a simulation: for example Fvcb requires a stomatal conductance model. At the moment Julia returns an error on missing method for the particular implementation of run!(Fvcb,Gs) (in short). We could check before that both are needed and present, and return a more informational error if missing.
- Implement a Gm model. Cf. the GECROS model.
- Replace component models by MutableNamedTuples ? It could alleviate the need to implement a different component model when needing a new model as it allows as many fields as we want. A call to a function would need to include the type of the model though ? e.g.
run!(mtnt.energy_balance, mtnt, meteo, constant), or we do that in the low-level functions so the use only pass the mtnt. -> solution was to make the call to the low-level functions generic for the models (no constraint on the type), but dispatch on the type of the first argument instead that is the model type for this function. This changes nothing to the high-level functions but make the possibility to provide other things than component models.
- Move the definitions of the abstract models near their processes: e.g. the definition of
AbstractPhotosynthesisModelshould be in the
- Change the way we store parameters, models and status:
- Add a new struct for the list of models, with two fields: models and status.
- The status field must be part of the struct in input of the high-level functions for easy construction, but be passed as an argument to the low-level functions so we avoid the copy(object, object[i]) in the high-level calls.
- Make a custom type for status so they are indexable? Maybe use StaticArrays.jl instead ? Not sure,
status[i].A = 12does not modify the array, only
status.A[i] = 12does, and we will provide status[i] as input to the low-level function so... Or else we get the output of the low level function, and re-assign it to the status:
status[i] = output_status, but this is not the best solution. Use a dataframe with a view instead for several time steps ? Make a microbenchmark, dataframes should be fast.
- Make sure all functions currently applied to the status are still needed, and if so check if they work.
- Add a check on the combination of models + status to see if the initialization is complete, but make it optional (arg
- Make a new
depfunction for each model that list all models that are used by a model. It can be nothing, an abstract model (e.g.
AbstractPhotosynthesisModel), a concrete model (e.g.
Fvcb) or any combinations of models (e.g. photosynthesis + stomatal conductance).
- Make a function to build a tree of models based on the
- Remove checks on the models when calling the processes functions, and move it to the construction of the group of models and/or to the user with a check function.
- For the computation of MTG + Weather, give an option on which way the computation is done: compute one time-step for each node, and then the second..., or compute all time-steps for each node at once. The latter avoids visiting the tree n times, so it should be the default. But sometimes models need the result of other nodes before continuing, so the former is necessary. Add the option with a type so we use dispatch, e.g.:
- Add a nicer print method to the ModelList and to the Status / TimeSteps
- Merge meteo variables and status variables ? In this case we could update their values, e.g. re-compute the microclimate inside the canopy and use it for the simulation. -> No we can't, if we have a lot of objects in the scene, it would mean copying a lot of data for nothing. Meteo variables are forced, if a model has to modify the value, it means it is another variable (e.g. air temperature, but around the leaf)
- Interface TimeSteps with
Tables.jl, and make the code compatible with Table-alike objects ? That would mean we can interface with e.g. DataFrames and profit of the ecosystem capabilities (e.g. future compatibility with GPU). Adding the interface is trivial, but making all functions with it may not. At least we can keep the
status.varnotation, as we will pass TableRows to the functions.
- Implement TimeStepTable that makes the interface with Tables.jl
- Implement TimeStepRow for managing unique time steps
- Use Status directly as a mnt, and as the value for TimeStepRow
- Test if I can improve implementation of Status (see comments in code)
- Remove dependency to MutableNamedTuples
- Remove complicated code for DataFrame (thanks to Tables interface). Remove completely the dependency ? Can't do that, the meteorology needs it. Or does it? We could implement a parser that parse the CSV file values into a Weather directly as a sink, but in this case we have to implement the Tables interface for Weather. But it would mean loosing column transformations on the fly.
- Implement usage of TimeStepTable everywhere
- Use it for the meteo too? No, it uses Atmosphere that is an immutable struct.
- Implement methods for push!(x, row), append!(x, rows), and x[i] = row for TimeStepTable to fully meet guidelines of Tables.jl for mutable Tables.
- Test with a DataFrame instead of a TimeStepTable
- Replace default
missingvalues in the initializations ? But check the impact on performance because we can't do it easily because everything is typed using MutableNamedTuples.
- Review file import for LiCOR and Walz
- Add variable boundaries in the status, and add a method for setting the values with a control on the boundary. This can be implemented in two ways: we add a new type that will be used as a value in the status, and that would have the parameter value + boundaries in two fields; or we add a new field to the status with the upper and lower boundaries of each variable. I think the first solution is better as it allows to pass this new type easily to the ModelList, and is more straightforward to get the boundaries of a particular variable (it is cleaner conceptually).
Contributions are welcome! If you develop a model for a process, please make a pull request so the community can enjoy it!
- MAESPA model, in FORTRAN.
- photosynthesis R package
- plantecophys R package
- LeafGasExchange Julia package (that uses Python's SymPy library under the hood)
Baldocchi, Dennis. 1994. « An analytical solution for coupled leaf photosynthesis and stomatal conductance models ». Tree Physiology 14 (7-8‑9): 1069‑79. https://doi.org/10.1093/treephys/14.7-8-9.1069.
Duursma, R. A., et B. E. Medlyn. 2012. « MAESPA: a model to study interactions between water limitation, environmental drivers and vegetation function at tree and stand levels, with an example application to [CO2] × drought interactions ». Geoscientific Model Development 5 (4): 919‑40. https://doi.org/10.5194/gmd-5-919-2012
Farquhar, G. D., S. von von Caemmerer, et J. A. Berry. 1980. « A biochemical model of photosynthetic CO2 assimilation in leaves of C3 species ». Planta 149 (1): 78‑90.
Leuning, R., F. M. Kelliher, DGG de Pury, et E.-D. SCHULZE. 1995. « Leaf nitrogen, photosynthesis, conductance and transpiration: scaling from leaves to canopies ». Plant, Cell & Environment 18 (10): 1183‑1200.
Medlyn, B. E., E. Dreyer, D. Ellsworth, M. Forstreuter, P. C. Harley, M. U. F. Kirschbaum, X. Le Roux, et al. 2002. « Temperature response of parameters of a biochemically based model of photosynthesis. II. A review of experimental data ». Plant, Cell & Environment 25 (9): 1167‑79. https://doi.org/10.1046/j.1365-3040.2002.00891.x.
Monteith, John L., et Mike H. Unsworth. 2013. « Chapter 13 - Steady-State Heat Balance: (i) Water Surfaces, Soil, and Vegetation ». In Principles of Environmental Physics (Fourth Edition), edited by John L. Monteith et Mike H. Unsworth, 217‑47. Boston: Academic Press.
Schymanski, Stanislaus J., et Dani Or. 2017. « Leaf-Scale Experiments Reveal an Important Omission in the Penman–Monteith Equation ». Hydrology and Earth System Sciences 21 (2): 685‑706. https://doi.org/10.5194/hess-21-685-2017.
Vezy, Rémi, Mathias Christina, Olivier Roupsard, Yann Nouvellon, Remko Duursma, Belinda Medlyn, Maxime Soma, et al. 2018. « Measuring and modelling energy partitioning in canopies of varying complexity using MAESPA model ». Agricultural and Forest Meteorology 253‑254 (printemps): 203‑17. https://doi.org/10.1016/j.agrformet.2018.02.005.