Leaf temperature

Start with initial guess: Tleaf = Tair
        │
        ▼
┌──────────────────────────────┐
│  Calculate at current Tleaf: │
│  • esat(Tleaf), desat        │  ← How much moisture the leaf "wants" to release
│  • lwrad = 2·ε·σ·T⁴          │  ← Infrared glow (both sides)
│  • shflx = 2·cp·(T-Ta)·gbh   │  ← Heat to air (both sides)
│  • lhflx = λ·(esat-ea)·glw   │  ← Evaporative cooling
└──────────────────────────────┘
        │
        ▼
   f0 = Qa - lwrad - shflx - lhflx    ← Energy imbalance
        │
        ▼
   Is |f0| < 0.000001 W/m² ?
      /          \
    YES           NO
     │             │
     ▼             ▼
   DONE      dtleaf = -f0/df0
     │        Tleaf += dtleaf
     │             │
     │             └──→ Go back to top
     ▼
  Calculate final:
  • rnet = Qa - lwrad
  • etflx = lhflx / λ
  • Verify: rnet - shflx - lhflx ≈ 0

source

leaf_temperature


def leaf_temperature(
    physcon:PhysCon, # Physical constants:
- tfrz : float
    Freezing point of water (K).
- mmh2o : float
    Molecular mass of water (kg/mol).
- sigma : float
    Stefan-Boltzmann constant (W/m2/K4).
    atmos:Atmos, # Atmospheric forcing variables:
- patm : float
    Atmospheric pressure (Pa).
- cpair : float
    Specific heat of air at constant pressure (J/mol/K).
- tair : float
    Air temperature (K).
- eair : float
    Vapor pressure of air (Pa).
    leaf:Leaf, # Leaf parameters:
- emiss : float
    Leaf emissivity (-).
- tleaf : float
    Leaf temperature (K). Used as initial guess on input
    flux:Flux, # Flux variables with the following inputs:
- gbh : float
    Leaf boundary layer conductance for heat (mol/m2 leaf/s).
- gbv : float
    Leaf boundary layer conductance for H2O (mol H2O/m2 leaf/s).
- gs : float
    Leaf stomatal conductance (mol H2O/m2 leaf/s).
- qa : float
    Leaf radiative forcing (W/m2 leaf).
)->Flux: # Updated flux object with the following attributes:
- tleaf : float
    Leaf temperature (K). Updated to energy-balance solution on output.
- rnet : float
    Leaf net radiation (W/m2 leaf).
- lwrad : float
    Longwave radiation emitted from leaf (W/m2 leaf).
- shflx : float
    Leaf sensible heat flux (W/m2 leaf).
- lhflx : float
    Leaf latent heat flux (W/m2 leaf).
- etflx : float
    Leaf transpiration flux (mol H2O/m2 leaf/s).

Calculate leaf temperature and energy fluxes.

Solve the leaf energy balance using Newton-Raphson iteration to find the temperature that balances radiative forcing against longwave emission, sensible heat flux, and latent heat flux.

Parameters:

  • PhysCon: Physical constants

    • tfrz: Freezing point of water (K).
    • mmh2o: Molecular mass of water (kg/mol).
    • sigma: Stefan-Boltzmann constant (W/m2/K4).
  • Atmos: Atmospheric forcing variables:

    • patm: Atmospheric pressure (Pa).
    • cpair: Specific heat of air at constant pressure (J/mol/K).
    • tair: Air temperature (K)
    • eair: Vapor pressure of air (Pa).
  • Leaf: Leaf parameters

    • emiss: Leaf emissivity (-).
    • tleaf: Leaf temperature (K). Used as initial guess on input
  • Flux: variables with the following inputs:

    • gbh: Leaf boundary layer conductance for heat (mol/m2 leaf/s).
    • gbv: Leaf boundary layer conductance for H2O (mol H2O/m2 leaf/s).
    • gs: Leaf stomatal conductance (mol H2O/m2 leaf/s).
    • qa: Leaf radiative forcing (W/m2 leaf).

Returns:

  • Flux: Updated flux object with the following attributes:

    • tleaf: Leaf temperature (K). Updated to energy-balance solution on output.
    • rnet: Leaf net radiation (W/m2 leaf).
    • lwrad: Longwave radiation emitted from leaf (W/m2 leaf).
    • shflx: Leaf sensible heat flux (W/m2 leaf).
    • lhflx: Leaf latent heat flux (W/m2 leaf).
    • etflx: Leaf transpiration flux (mol H2O/m2 leaf/s).

Example leaf_temperature()

Reproducing the results from table 10.2 of the book

Load packages

from bonan_hydraulics.utils import satvap
from bonan_hydraulics.leaf_boundary_layer import *
from bonan_hydraulics.utils import calc_radiative_forcing_qa
from bonan_hydraulics.parameter_classes import Params, PhysCon, Atmos, Leaf

Call objects

params = Params()
phys_constants = PhysCon
atmos = Atmos()
leaf = Leaf()

Edit object parameters

# Atmospheric pressure
atmos.patm = 101325.0

# Ambient temp in Kelvin
atmos.tair = 35.0 + phys_constants.tfrz

relative_humidity = 50

# Saturation vapor pressure at 35°C. Ignoring dsat that is why the underscore (_)
esat_air, _ = satvap(35.0)

# Actual vapor pressure (Pa)
atmos.eair = (relative_humidity / 100.0) * esat_air

# Molar density of air: ρm = P / (R * T)
atmos.rhomol = atmos.patm / (phys_constants.rgas * atmos.tair)

# Specific heat of moist air (J/mol/K)
# cp = cpd * Md + cpw * Mw * (eair/patm), simplified to ~29.2 J/mol/K
atmos.cpair = phys_constants.cpd * phys_constants.mmdry

# Longwave radiation
atmos.irsky = 300.0
# Leaf dimension (m)
leaf.dleaf = 0.05

# Emissivity
leaf.emiss = 0.98

# Reflectance [vis, nir]
leaf.rho = [0.1, 0.4]

# Transmittance [vis, nir]
leaf.tau = [0.1, 0.4]
# Ground albedo [vis, nir]
ground_albedo = [0.1, 0.2]

# Ground longwave (W/m2)
ground_lw = 545.0
# Calculate Qa for clear and cloudy sky
qa_clear = calc_radiative_forcing_qa(
    1100.0, leaf, ground_albedo, ground_lw, atmos.irsky
)
qa_cloudy = calc_radiative_forcing_qa(
    550.0, leaf, ground_albedo, ground_lw, atmos.irsky
)
# Loop over conditions and compute leaf temperature
#
# For each combination of:
#   - Sky condition (clear, cloudy)
#   - Stomatal conductance (0, 0.4 mol/m2/s)
#   - Wind speed (0.01, 0.1, 1, 5 m/s)
#
#   (a) Compute boundary layer conductances (gbh, gbv, gbc)
#   (b) Set stomatal conductance
#   (c) Solve for leaf temperature via Newton-Raphson

wind_speeds = [0.01, 0.1, 1.0, 5.0]
gsw_values = [0.0, 0.4]
sky_conditions = [
    ("Clear sky", qa_clear, 1444),
    ("Cloudy sky", qa_cloudy, 1136),
]
for sky_name, qa, qa_book in sky_conditions:
    for each_gsw in gsw_values:
        for each_wind in wind_speeds:
            print(f"\ngs = {each_gsw}, wind_speed = {each_wind}")

            # Set wind speed for boundary layer calculation
            atmos.wind = each_wind

            # Create fresh flux object for each case
            flux = Flux()

            # Initial guess = air temperature
            flux.tleaf = atmos.tair

            # Radiative forcing
            flux.qa = qa

            # (a) Set stomatal conductance
            # Note: gs = 0 causes division by zero in gleaf = gs*gbv/(gs+gbv)
            # Use a tiny value instead to represent closed stomata
            flux.gs = max(each_gsw, 1e-06)

            # (b) Iterate: boundary layer depends on Tleaf (via Grashof number)
            #     and Tleaf depends on boundary layer conductance.
            #     At low wind speeds, free convection matters and this
            #     coupling must be resolved iteratively.
            for iteration in range(20):
                tleaf_old = flux.tleaf

                # Compute boundary layer conductances at current Tleaf
                flux = leaf_boundary_layer(phys_constants, atmos, leaf, flux)

                # Solve energy balance for leaf temperature
                flux = leaf_temperature(phys_constants, atmos, leaf, flux)

                # Check convergence
                if abs(flux.tleaf - tleaf_old) < 0.01:
                    break

            # Convert to Celsius and print
            tleaf_C = flux.tleaf - phys_constants.tfrz
            print(f"Leaf temperature = {tleaf_C:6.1f}\n", end="")

gs = 0.0, wind_speed = 0.01
Leaf temperature =   49.4

gs = 0.0, wind_speed = 0.1
Leaf temperature =   45.8

gs = 0.0, wind_speed = 1.0
Leaf temperature =   40.9

gs = 0.0, wind_speed = 5.0
Leaf temperature =   38.2

gs = 0.4, wind_speed = 0.01
Leaf temperature =   39.8

gs = 0.4, wind_speed = 0.1
Leaf temperature =   37.7

gs = 0.4, wind_speed = 1.0
Leaf temperature =   35.8

gs = 0.4, wind_speed = 5.0
Leaf temperature =   35.2

gs = 0.0, wind_speed = 0.01
Leaf temperature =   39.9

gs = 0.0, wind_speed = 0.1
Leaf temperature =   38.5

gs = 0.0, wind_speed = 1.0
Leaf temperature =   36.9

gs = 0.0, wind_speed = 5.0
Leaf temperature =   36.0

gs = 0.4, wind_speed = 0.01
Leaf temperature =   34.1

gs = 0.4, wind_speed = 0.1
Leaf temperature =   33.5

gs = 0.4, wind_speed = 1.0
Leaf temperature =   32.9

gs = 0.4, wind_speed = 5.0
Leaf temperature =   33.4