PlantBiophysics.jlPlantBiophysics.jl

Micro-climate​#

Leaf processes respond to the air temperature, humidity, wind, and CO₂ concentration around the leaf. PlantMeteo describes these conditions with an Atmosphere for one timestep or a Weather series for several timesteps. Pass either one as environment=meteo when creating a CompositeModel.

Describe one timestep​#

Start with the conditions measured near your leaf:

InputMeaningUnit
TAir temperature°C
RhRelative humidityFraction from 0 to 1
WindWind speedm s⁻¹
PAir pressurekPa
CₐAir CO₂ concentrationµmol mol⁻¹
durationTimestep durationA period such as Hour(1)

Atmosphere supplies defaults for optional inputs, including Cₐ, but provide measured values when available. Specify the duration explicitly when using a time series or calculating totals.

julia
using PlantMeteo
meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, duration=Hour(1))
Atmosphere(date = Dates.DateTime("2026-10-01T09:30:19.070"), duration = Dates.Hour(1), T = 20.0, Wind = 1.0, P = 101.3, Rh = 0.65, Precipitations = 0.0, Cₐ = 400.0, e = 1.5255470730405223, eₛ = 2.3469954969854188, VPD = 0.8214484239448965, ρ = 1.2037851579511918, λ = 2.4537e6, γ = 0.06723680111943287, ε = 0.5848056484857892, Δ = 0.14573378083416522)

Atmosphere calculates related quantities such as vapour pressure deficit (VPD, kPa) and air density (ρ, kg m⁻³). You can override a derived value when you have an independent measurement or calculation:

julia
using PlantMeteo
Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, VPD=0.82, duration=Hour(1))
Atmosphere(date = Dates.DateTime("2026-10-01T09:30:19.164"), duration = Dates.Hour(1), T = 20.0, Wind = 1.0, P = 101.3, Rh = 0.65, Precipitations = 0.0, Cₐ = 400.0, e = 1.5255470730405223, eₛ = 2.3469954969854188, VPD = 0.82, ρ = 1.2037851579511918, λ = 2.4537e6, γ = 0.06723680111943287, ε = 0.5848056484857892, Δ = 0.14573378083416522)

Read a value with the dot syntax. For example, saturation vapour pressure (eₛ) is in kPa:

julia
meteo.eₛ
2.3469954969854188

Incident radiation can also be supplied through Atmosphere, using Ri_PAR_f and Ri_NIR_f in W m⁻². Leaf models need absorbed radiation, which is supplied on the leaf or calculated by a light model. See Light interception for the distinction.

Describe changing weather​#

Weather collects consecutive Atmosphere values, with optional metadata such as a site name. Here are three hourly timesteps:

julia
using PlantMeteo
w = Weather(
    [
        Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, duration=Hour(1)),
        Atmosphere(T=23.0, Wind=1.5, P=101.3, Rh=0.60, duration=Hour(1)),
        Atmosphere(T=25.0, Wind=3.0, P=101.3, Rh=0.55, duration=Hour(1))
    ],
    (
        site = "Montpellier",
    )
)
TimeStepTable{Atmosphere{(:date, :duration,...}(3 x 16):
date duration T Wind P Rh Precipitations Cₐ e eₛ VPD ρ λ γ ε Δ
Dates.DateTime Dates.Hour Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64
1 2026-10-01T09:30:19.183 1 hour 20.0 1.0 101.3 0.65 0.0 400.0 1.52555 2.347 0.821448 1.20379 2.4537e6 0.0672368 0.584806 0.145734
2 2026-10-01T09:30:19.183 1 hour 23.0 1.5 101.3 0.6 0.0 400.0 1.69211 2.82018 1.12807 1.19159 2.4466e6 0.0674318 0.592664 0.171147
3 2026-10-01T09:30:19.183 1 hour 25.0 3.0 101.3 0.55 0.0 400.0 1.74911 3.1802 1.43109 1.1836 2.44188e6 0.0675624 0.594904 0.190095
Metadata: `Dict("site" => "Montpellier")`

Use this series as the scene's environment and run three steps with run!(scene; steps=3, outputs=:all). PlantSimEngine reads the corresponding weather row at each step; the several-timestep tutorial shows the complete workflow.

Read a weather table​#

A Weather can also be declared from a DataFrame, provided each row is an observation from a time-step, and each column is a variable needed for Atmosphere (see the help of Atmosphere for more details on the possible variables and their units).

This example uses a CSV fixture shipped with PlantMeteo. Replace its path with your own file and match the column names and units to your data.

julia
using CSV, DataFrames, PlantMeteo
file = joinpath(pkgdir(PlantMeteo), "test", "data", "meteo.csv")
df = CSV.read(file, DataFrame; header=5, skipto = 6, dateformat = "yyyy/mm/dd")
# Preserve the start time of each observation before selecting columns:
df.date = Date.(df.date) .+ Time.(df.hour_start)
# Select and rename the variables:
select!(df, :date, :temperature => :T, :relativeHumidity => (x -> x ./ 100 ) => :Rh, :wind => :Wind, :atmosphereCO2_ppm => :Cₐ)
df[!, :duration] = fill(Minute(30), nrow(df))

# Make the weather, and add some metadata:
Weather(df, (site = "Aquiares", file = file))
TimeStepTable{Atmosphere{(:date, :duration,...}(3 x 16):
date duration T Wind P Rh Precipitations Cₐ e eₛ VPD ρ λ γ ε Δ
Dates.DateTime Dates.Minute Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64
1 2016-06-12T12:00:00 30 minutes 25.0 1.0 101.325 0.6 0.0 380.0 1.90812 3.1802 1.27208 1.18389 2.44188e6 0.0675791 0.602345 0.190095
2 2016-06-12T12:30:00 30 minutes 26.0 1.5 101.325 0.62 0.0 380.0 2.09238 3.37481 1.28243 1.17993 2.43951e6 0.0676446 0.610038 0.200215
3 2016-06-12T13:00:00 30 minutes 25.3 1.5 101.325 0.58 0.0 380.0 1.87776 3.23752 1.35976 1.1827 2.44117e6 0.0675987 0.60088 0.193085
Metadata: `Dict("file" => "/home/runner/.julia/packages/PlantMeteo/58jvN/test/data/meteo.csv", "site" => "Aquiares")`

The three records retain their start times: 12:00, 12:30, and 13:00 on 12 June 2016. Keeping these timestamps makes it possible to match simulated outputs to the original measurements.

For an Archimed-ϕ-formatted CSV with metadata, read_weather handles the import directly. The column transformations below convert relative humidity from percent to a fraction and rename the weather variables:

julia
using Dates, PlantMeteo

meteo = read_weather(
    file,
    :temperature => :T,
    :relativeHumidity => (x -> x ./100) => :Rh,
    :wind => :Wind,
    :atmosphereCO2_ppm => :Cₐ,
    date_format = DateFormat("yyyy/mm/dd")
)
TimeStepTable{Atmosphere{(:date, :duration,...}(3 x 24):
date duration T Wind P Rh Precipitations Cₐ e eₛ VPD ρ λ γ ε Δ clearness hour_start hour_end temperature relativeHumidity Re_SW_f wind atmosphereCO2_ppm
Dates.DateTime Dates.CompoundPeriod Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Dates.Time Dates.Time Float64 Float64 Float64 Float64 Float64
1 2016-06-12T12:00:00 30 minutes 25.0 1.0 101.325 0.6 0.0 380.0 1.90812 3.1802 1.27208 1.18389 2.44188e6 0.0675791 0.602345 0.190095 0.75 12:00:00 12:30:00 25.0 60.0 500.0 1.0 380.0
2 2016-06-12T12:30:00 30 minutes 26.0 1.5 101.325 0.62 0.0 380.0 2.09238 3.37481 1.28243 1.17993 2.43951e6 0.0676446 0.610038 0.200215 0.75 12:30:00 13:00:00 26.0 62.0 500.0 1.5 380.0
3 2016-06-12T13:00:00 30 minutes 25.3 1.5 101.325 0.58 0.0 380.0 1.87776 3.23752 1.35976 1.1827 2.44117e6 0.0675987 0.60088 0.193085 0.75 13:00:00 13:30:00 25.3 58.0 500.0 1.5 380.0
Metadata: `Dict{String, Any}("name" => "Aquiares", "latitude" => 15.0, "altitude" => 100.0, "use" => "Re_SW_f, clearness", "file" => "/home/runner/.julia/packages/PlantMeteo/58jvN/test/data/meteo.csv", "import_normalization" => Dict{String, Any}[Dict("output_units" => Dict{String, Any}("P" => "kPa", "Rh" => "fraction"), "schema" => "PlantMeteo.import-normalization.v1", "conversions" => Dict{String, Any}[], "input_units" => Dict{String, Any}("P" => "kPa", "Rh" => "fraction"))])`

Helper functions​#

PlantMeteo also provides functions for individual weather calculations:

  • vapor_pressure computes e (kPa), the vapor pressure from the air temperature and the relative humidity

  • e_sat computes eₛ (kPa), the saturated vapor pressure from the air temperature

  • air_density computes ρ (kg m-3), the air density from the air temperature, the pressure, and some constants

  • latent_heat_vaporization computes λ (J kg-1), the latent heat of vaporization from the air temperature and a constant

  • psychrometer_constant computes γ (kPa K−1), the psychrometer "constant" from the air pressure, the latent heat of vaporization and some constants

  • atmosphere_emissivity(T,e,constants.K₀) computes ε (0-1), the atmosphere emissivity from the air temperature, the vapor pressure and a constant

  • PlantMeteo.e_sat_slope computes Δ (kPa K⁻¹), the slope of saturation vapour pressure with temperature

Note

All constants are found in Constants