33 - Photosynthesis and fluorescence sources¶
This chapter computes leaf photosynthesis, stomatal conductance and chlorophyll fluorescence that changes with the photosynthetic state. Photosynthesis uses the Farquhar–von Caemmerer–Berry model and stomatal conductance the Ball–Berry model. Leaf temperature can be prescribed or solved together with the energy balance, all three in one coupled state.
Required properties¶
Leaves taking part in photosynthesis and fluorescence need four kinds of property:
| Domain | Property | Role |
|---|---|---|
| Optical | Fluspect |
Leaf reflectance, transmittance, absorption and fluorescence spectra |
| Thermal | ThermalProperty |
Starting temperature and emissivity |
| Biophysical | BiophysicalProperty |
Leaf width, stomata side, heat and water vapour exchange |
| Physiological | Farquhar |
Photosynthesis and stomatal conductance parameters |
import less
scene = less.Scene()
scene.size = 6.0
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.15))
scene.terrain.set_property(less.ThermalProperty(temperature=300.0, emissivity=0.95))
scene.illumination = less.Illumination(
source=less.Sun(zenith=30, azimuth=150), atmosphere=less.NoAtmosphere())
plant = less.Object("plant")
plant.add_component("leaves", less.Mesh(less.asset_path("tutorial_tree.obj"), group="leaves"))
plant.set_property("leaves", less.Fluspect(N=1.5, cab=45, car=10, cw=0.012, cm=0.006,
fqe="dynamic"))
plant.set_property("leaves", less.ThermalProperty(temperature="air", emissivity=0.98))
plant.set_property("leaves", less.BiophysicalProperty(leaf_width=0.05, stomata_side="bottom"))
plant.set_property("leaves", less.Farquhar(Vcmax25=60.0))
scene.add(plant, positions=[(3, 3, 0)])
fqe="dynamic" means the fluorescence yield follows from the photosynthetic state.
Photosynthesis models¶
Farquhar() uses the C3 photosynthesis equations of SCOPE by default (formulation="scope"), with Vcmax25 as the main parameter:
To give the maximum electron transport rate separately, use formulation="jmax"; without Jmax25 it is 2 × Vcmax25:
| Parameter | Meaning |
|---|---|
Vcmax25 |
Maximum carboxylation rate at 25 °C (µmol m⁻² s⁻¹) |
Jmax25 |
Maximum electron transport rate at 25 °C, for "jmax" only |
Rd25_ratio |
Ratio of dark respiration to Vcmax25 |
BallBerrySlope, BallBerryIntercept |
Slope and intercept of the Ball–Berry stomatal conductance model |
absorption_component |
Absorption that drives photosynthesis, "chlorophyll" by default |
Chlorophyll absorption and total absorption¶
By default photosynthesis is driven by PAR absorbed by chlorophyll. Prospect and Fluspect give the fraction of absorption due to chlorophyll at each wavelength. To use the total absorption of the leaf instead:
Measured spectra (MeasuredSpectrum) need a pigment partition model before chlorophyll absorption can be computed (see chapter 10). When comparing pigment-related absorption, use a spectral spacing of about 1 nm in the photosynthetically active range; bands that are too wide bias the chlorophyll absorption fraction.
Solving¶
climate = less.Microclimate(air_temperature=25.0, humidity=60.0, wind_speed=2.0, ca=400.0)
state = scene.solve(microclimate=climate,
modules=["energy_balance", "photosynthesis", "sif"],
photosynthesis_kwargs={"quality": "preview"})
photo = state.photosynthesis
print("net photosynthesis:", photo.An)
print("stomatal conductance:", photo.gs)
print("fluorescence yield:", photo.Phi_f)
print("leaf temperature (K):", photo.T_leaf)
scene.solve() computes the radiation field, energy balance, photosynthesis and fluorescence in turn, all sharing one coupled state: leaf temperature affects photosynthesis, photosynthesis sets stomatal conductance and transpiration, and transpiration in turn changes leaf temperature. After solving, thermal and fluorescence imaging use this state automatically:
thermal = scene.simulate(less.ThermalImager(less.Orthographic(image_size=64),
bands=[10600.0], quality=32), microclimate=climate)
sif = scene.simulate(less.SIFImager(less.Orthographic(image_size=64),
bands=[687.0, 740.0, 760.0], quality=32))
thermal.save("dynamic_temperature.tif")
sif.save("dynamic_sif.tif")
In Microclimate, pressure is in Pa, oxygen in mmol mol⁻¹ and ca (CO₂) in µmol mol⁻¹. The actual vapour pressure is computed from air temperature and relative humidity.
Sunlit and shaded leaves¶
Direct light in a canopy is very uneven. LESS computes sunlit and shaded leaves separately and combines them by sunlit fraction:
| Result | Meaning |
|---|---|
sunlit_fraction |
Fraction of leaf area that sees the sun directly |
APAR, APAR_total |
APAR of the selected absorption component, APAR of total leaf absorption |
APAR_sunlit, APAR_shaded |
APAR of sunlit and shaded leaves |
T_sunlit, T_shaded |
Temperatures of sunlit and shaded leaves |
An_sunlit, An_shaded |
Net photosynthesis of sunlit and shaded leaves |
Skylight and multiple scattering reach both sunlit and shaded leaves; only sunlit leaves also receive direct sunlight. The final result is
where p is the sunlit fraction. Photosynthesis responds non-linearly to light, so computing separately and then averaging differs from averaging the light first.
APAR is in µmol photons m⁻² leaf s⁻¹ and An in µmol CO₂ m⁻² leaf s⁻¹.
photo.spatial_mode describes the spatial unit of the results: "primitive" (one value per triangle) when there are only triangle meshes; with statistical vegetation, results are per component, and a crown can be split into several components for finer results. photo.scene_elements identifies the scene element of each row.
Fluorescence sources¶
The fluorescence source is the fluorescence spectrum emitted by each leaf, computed by SIFProcess. The scene.solve() above already includes it; it can also be computed on its own:
import numpy as np
source = state.sif
wavelengths = np.arange(650.0, 851.0, 1.0)
leaf = source.emission(wavelengths) # per unit leaf area, W m⁻² nm⁻¹
photosystem = source.emission(wavelengths, level="photosystem") # before reabsorption in the leaf
total = source.total_emission(wavelengths) # whole scene, W nm⁻¹
by_component = source.total_emission(wavelengths, by="component")
by_layer = source.total_emission(wavelengths, by="layer", layers=[0.0, 2.0, 4.0, 6.0])
level="leaf"(default): fluorescence leaving both leaf surfaces, i.e. the fluorescence that travels through the canopy;level="photosystem": fluorescence emitted by the photosystems before reabsorption by the leaf itself. Most red fluorescence is reabsorbed inside the leaf, while most far-red fluorescence escapes;bycan be"scene","component","instance"or"layer"(height layers, withlayersas layer boundaries).
Fluorescence with a fixed yield (mode="static", using Fluspect.fqe) needs no temperature or photosynthesis; see chapter 27. Secondary fluorescence, excited again after reabsorption in the leaf, is not included; at typical yields it is below 1–2% of total fluorescence.
Sampling quality¶
state = scene.solve(microclimate=climate,
modules=["energy_balance", "photosynthesis", "sif"],
photosynthesis_kwargs={"quality": "standard"},
sif_kwargs={"quality": "standard"},
seed=42)
Quality can be "preview", "standard" or "high". For quantitative work, compare APAR, leaf temperature, An, gs and fluorescence yield at different qualities to make sure the results are stable. A fixed seed reproduces the same result.
Checking convergence¶
eb = state.energy_balance.result
print("energy balance converged:", eb.converged)
print("photosynthesis converged:", state.photosynthesis.converged,
"iterations:", state.photosynthesis.iterations)
print("largest residual:", abs(eb.residual[eb.active_mask]).max())
After the illumination, microclimate, properties or geometry change, run scene.solve() again.
Statistical vegetation¶
Statistical vegetation canopies are used in exactly the same way; just set the leaf properties on the statistical vegetation component (with Fluspect for the representative leaf):
crown = less.Object("crown")
crown.add_component("crown", less.TurbidBoundary(less.asset_path("tutorial_tree.obj"), group="crown"))
crown.set_property("crown", less.OpticalVegetation(
leaf=less.Fluspect(cab=45, fqe="dynamic"), leaf_area_density=1.5,
leaf_angle_distribution="spherical"))
crown.set_property("crown", less.ThermalProperty(temperature="air", emissivity=0.98))
crown.set_property("crown", less.BiophysicalProperty(leaf_width=0.05, stomata_side="bottom"))
crown.set_property("crown", less.Farquhar(Vcmax25=60.0))
scene.add(crown, positions=[(3, 3, 0)])
mixed = scene.solve(microclimate=climate, modules=["energy_balance", "photosynthesis", "sif"],
photosynthesis_kwargs={"quality": "preview"})
print(mixed.photosynthesis.spatial_mode)
Triangle-mesh leaves, statistical vegetation and terrain can be computed in the same solution.