API for the energy_balance module

The models.abiotic.energy_balance module calculates the energy balance for the Virtual Ecosystem. Given that the time increments of the model are an hour or longer, we can assume that below-canopy heat and vapour exchange attain steady state and heat storage in the canopy does not need to be simulated explicitly. (For application where very fine-temporal resolution data might be needed, heat and vapour exchange must be modelled as transient processes, and heat storage by the canopy, and the exchange of heat between different layers of the canopy, must be considered explicitly, see Maclean and Klinges (2021). This is currently not implemented.)

Under steady-state, the balance equation for the leaves in each canopy layer is as follows (after Maclean and Klinges (2021)):

\[R_{abs} - R_{em} - H - \lambda E = R_{abs} - \epsilon_{s} \sigma T_{L}^{4} - c_{P}g_{Ha}(T_{L} - T_{A}) - \lambda g_{v} \frac {e_{L} - e_{A}}{p_{A}} = 0\]

where \(R_{abs}\) is absorbed radiation, \(R_{em}\) emitted radiation, \(H\) the sensible heat flux, \(\lambda E\) the latent heat flux, \(\epsilon_{s}\) the emissivity of the leaf, \(\sigma\) the Stefan-Boltzmann constant, \(T_{L}\) the absolute temperature of the leaf, \(T_{A}\) the absolute temperature of the air surrounding the leaf, \(\lambda\) the latent heat of vapourisation of water, \(e_{L}\) the effective vapour pressure of the leaf, \(e_{A}\) the vapour pressure of air and \(p_{A}\) atmospheric pressure. \(g_{Ha}\) is the heat conductance between leaf and atmosphere, \(g_{v}\) represents the conductance for vapour loss from the leaves as a function of the stomatal conductance \(g_{c}\).

A challenge in solving this equation is the dependency of latent heat and emitted radiation on leaf temperature. We use a linearisation approach to solve the equation for leaf temperature and air temperature simultaneously after Maclean and Klinges (2021).

The soil energy balance functions are described in soil_energy_balance.

The conductivities are calculated as described in conductivities.

Functions:

calculate_delta_canopy_temperature(...)

Calculate change in canopy temperature (delta).

calculate_leaf_and_air_temperature(data, ...)

Calculate leaf and air temperature under steady state.

calculate_longwave_emission(temperature, ...)

Calculate longwave emission using the Stefan Boltzmann law, [W m-2].

calculate_slope_of_saturated_pressure_curve(...)

Calculate slope of the saturated pressure curve.

initialise_absorbed_radiation(...)

Calculate initial light absorption profile.

initialise_canopy_and_soil_fluxes(...)

Initialise canopy temperature and energy fluxes.

initialise_canopy_temperature(...)

Initialise canopy temperature.

latent_heat_flux_linearisation(...)

Calculate factors for latent heat flux linearisation.

leaf_and_air_temperature_linearisation(...)

Calculate factors for leaf and air temperature linearisation.

longwave_radiation_flux_linearisation(a_A, ...)

Calculate factors for longwave radiative flux linearisation.

vapour_pressure_linearisation(...)

Calculate factors for vapour pressure linearisation.

virtual_ecosystem.models.abiotic.energy_balance.calculate_delta_canopy_temperature(absorbed_radiation: ndarray[Any, dtype[float32]], a_R: ndarray[Any, dtype[float32]], a_L: ndarray[Any, dtype[float32]], b_R: ndarray[Any, dtype[float32]], b_L: ndarray[Any, dtype[float32]], b_H: ndarray[Any, dtype[float32]]) ndarray[Any, dtype[float32]]

Calculate change in canopy temperature (delta).

Parameters:
  • absorbed_radiation – Radiation (shortwave) absorved by canopy, [W m-2]

  • a_R – Factor for longwave radiation emission linearisation

  • a_L – Factor for latent heat flux linearisation

  • b_R – Factor for longwave radiation emission linearisation

  • b_L – Factor for latent heat flux linearisation

  • b_H – Factor for sensible heat flux linearisation

Returns:

Change in canopy temperature, [C]

virtual_ecosystem.models.abiotic.energy_balance.calculate_leaf_and_air_temperature(data: Data, time_index: int, topsoil_layer_index: int, true_canopy_indexes: ndarray[Any, dtype[integer]], true_canopy_layers_n: int, layer_structure: LayerStructure, abiotic_constants: AbioticConsts, abiotic_simple_constants: AbioticSimpleConsts, core_constants: CoreConsts) dict[str, DataArray]

Calculate leaf and air temperature under steady state.

The air temperature surrounding the leaf \(T_{A}\) is assumed to be influenced by leaf temperature \(T_{L}\), soil temperature \(T_{0}\), and reference air temperature \(T_{R}\) as follows:

\[g_{tR} c_{p} (T_{R} - T_{A}) + g_{t0} c_{p} (T_{0} - T_{A}) + g_{L} c_{p} (T_{L} - T_{A}) = 0\]

where \(c_{p}\) is the specific heat of air at constant pressure and \(g_{tR}\), \(g_{t0}\) and \(g_{L}\) are conductance from reference height, the ground and from the leaf, respectively. \(g_{L} = 1/(1/g_{HA} + 1/g_{z})\) where \(g_{HA}\) is leaf boundary layer conductance and \(g_{z}\) is the sub-canopy turbulent conductance at the height of the leaf over the mean distance between the leaf and the air.

Defining \(T_{L} - T_{A}\) as \(\Delta T\) and rearranging gives:

\[T_{A} = a_{A} + b_{A} \Delta T_{L}\]

where \(a_{A} = \frac{(g_{tR} T_{R} + g_{t0} T_{0})}{(g_{tR} + g_{t0})}\) and \(b_{A} = \frac{g_{L}}{(g_{tR} + g_{t0})}\) .

The sensible heat flux between the leaf and the air is given by

\[g_{Ha} c_{p} (T_{L} - T_{A}) = b_{H} \Delta T_{L}\]

where \(b_{H} = g_{Ha} c_{p}\). The equivalent vapour flux equation is

\[g_{tR}(e_{R} - e_{a}) + g_{t0} (e_{0} - e_{a}) + g_{v} (e_{L} - e_{a}) = 0\]

where \(e_{L}\), \(e_{A}\), \(e_{0}\) and \(e_{R}\) are the vapour pressure of the leaf, air, soil and air at reference height, respectively, and \(g_{v}\) is leaf conductance for vapour given by \(g_{v} = \frac{1}{(\frac{1}{g_{c} + g_{L})}}\) where \(g_{c}\) is stomatal conductance. Assuming the leaf to be saturated, and approximated by \(e_{s} [T_{R}]+\Delta_{v} [T_{R}]\Delta T_{L}\) where \(\Delta_{v}\) is the slope of the saturated pressure curve at temperature \(T_{R}\), and rearranging gives

\[e_{a} = a_{E} + b_{E} \Delta T_{L}\]

where \(a_{E} = \frac{(g_{tR} e_{R} + g_{t0} e_{0} + g_{v} e_{s}[T_{R}])} {(g_{tR} + g_{t0} + g_{v})}\) and \(b_{E} = \frac{\Delta_{V} [T_{R}])}{(g_{tR} + g_{t0} + g_{v})}\).

The latent heat term is given by

\[\lambda E = \frac{\lambda g_{v}}{p_{a}} (e_{L} - e_{A})\]

Substituting \(e_{A}\) for its linearized form, again assuming \(e_{L}\) is approximated by \(e_{s} [T_{R}]+\Delta_{v} [T_{R}]\Delta T_{L}\), and rearranging gives:

\[\lambda E = a_{L} + b_{L} \Delta T_{L},\]

where \(a_{L} = \frac{\lambda g_{v}}{p_{a}} (e_{s} [T_{R}] - a_{E})\) and \(b_{L} = \frac{\lambda g_{v}}{p_{a}} (\Delta_{V} [T_{R}] - b_{E})\).

The radiation emitted by the leaf \(R_{em}\) is given by the Stefan Boltzmann law and can be linearised as follows:

\[R_{em} = a_{R} + b_{R} \Delta T_{L}\]

where \(a_{R} = \epsilon_{s} \sigma a_{A}^{4}\) and \(b_{R} = 4 \epsilon_{s} \sigma (a_{A}^{3} b_{A} + T_{R}^{3})\).

The full heat balance equation for the difference between leaf and canopy air temperature becomes

\[\Delta T_{L} = \frac{R_{abs} - a_{R} - a_{L}}{(1 + b_{R} + b_{L} + b_{H})}\]

The equation is then used to calculate air and leaf temperature as follows:

\[T_{A} = a_{A} + b_{A} \Delta T_{L}\]

and

\[T_{L} = T_{A} + \Delta T_{L}.\]

the data object has to contain the previous and current values for the following:

  • air_temperature_ref: Air temperature at reference height 2m above canopy, [C]

  • vapour_pressure_ref: vapour pressure at reference height 2m above canopy, [kPa]

  • soil_temperature: Soil temperature, [C]

  • soil_moisture: Soil moisture, [mm]

  • layer_heights: Layer heights, [mm]

  • atmospheric_pressure_ref: Atmospheric pressure at reference height, [kPa]

  • air_temperature: Air temperature, [C]

  • canopy_temperature: Leaf temperature, [C]

  • latent_heat_vapourisation: Latent heat of vapourisation, [J kg-1]

  • absorbed_radiation: Absorbed radiation, [W m-2]

  • specific_heat_air: Specific heat of air, [J mol-1 K-1]

TODO * add latent heat flux from soil to atmosphere (-> VPD) * check time integration * set limits to temperature and VPD

Parameters:
  • data – Instance of data object

  • time_index – Time index

  • topsoil_layer_index – Index of top soil layer

  • true_canopy_indexes – indexes of canopy layers that are not NaN

  • true_canopy_layers_n – Maximum number of canopy layers that are not NaN

  • layer_structure – Instance of LayerStructure that countains details about layers

  • abiotic_constants – Set of abiotic constants

  • abiotic_simple_constants – Set of abiotic constants

  • core_constants – Set of core constants

Returns:

air temperature, [C], canopy temperature, [C], vapour pressure [kPa], vapour pressure deficit, [kPa]

virtual_ecosystem.models.abiotic.energy_balance.calculate_longwave_emission(temperature: ndarray[Any, dtype[float32]], emissivity: float | ndarray[Any, dtype[float32]], stefan_boltzmann: float) ndarray[Any, dtype[float32]]

Calculate longwave emission using the Stefan Boltzmann law, [W m-2].

According to the Stefan Boltzmann law, the amount of radiation emitted per unit time from the area of a black body at absolute temperature is directly proportional to the fourth power of the temperature. Emissivity (which is equal to absorptive power) lies between 0 to 1.

Parameters:
  • temperature – Temperature, [K]

  • emissivity – Emissivity, dimensionless

  • stefan_boltzmann – Stefan Boltzmann constant, [W m-2 K-4]

Returns:

Longwave emission, [W m-2]

virtual_ecosystem.models.abiotic.energy_balance.calculate_slope_of_saturated_pressure_curve(temperature: ndarray[Any, dtype[float32]], saturated_pressure_slope_parameters: list[float]) ndarray[Any, dtype[float32]]

Calculate slope of the saturated pressure curve.

Parameters:
  • temperature – Temperature, [C]

  • saturated_pressure_slope_parameters – List of parameters to calcualte the slope of the saturated vapour pressure curve

Returns:

Slope of the saturated pressure curve, \(\Delta_{v}\)

virtual_ecosystem.models.abiotic.energy_balance.initialise_absorbed_radiation(topofcanopy_radiation: ndarray[Any, dtype[float32]], leaf_area_index: ndarray[Any, dtype[float32]], layer_heights: ndarray[Any, dtype[float32]], light_extinction_coefficient: float) ndarray[Any, dtype[float32]]

Calculate initial light absorption profile.

This function calculates the fraction of radiation absorbed by a multi-layered canopy based on its leaf area index (\(LAI\)) and extinction coefficient (\(k\)) at each layer, the depth of each measurement (\(z\)), and the incoming light intensity at the top of the canopy (\(I_{0}\)). The implementation based on Beer’s law:

\[I(z) = I_{0} * e^{(-k * LAI * z)}\]
Parameters:
  • topofcanopy_radiation – Top of canopy radiation shortwave radiation, [W m-2]

  • leaf_area_index – Leaf area index of each canopy layer, [m m-1]

  • layer_heights – Layer heights, [m]

  • light_extinction_coefficient – Light extinction coefficient, [m-1]

Returns:

Shortwave radiation absorbed by canopy layers, [W m-2]

virtual_ecosystem.models.abiotic.energy_balance.initialise_canopy_and_soil_fluxes(air_temperature: DataArray, topofcanopy_radiation: DataArray, leaf_area_index: DataArray, layer_heights: DataArray, true_canopy_indexes: ndarray[Any, dtype[integer]], topsoil_layer_index: int, light_extinction_coefficient: float, canopy_temperature_ini_factor: float) dict[str, DataArray]

Initialise canopy temperature and energy fluxes.

This function initializes the following variables to run the first step of the energy balance routine: absorbed radiation (canopy), canopy temperature, sensible and latent heat flux (canopy and soil), and ground heat flux.

Parameters:
  • air_temperature – Air temperature, [C]

  • topofcanopy_radiation – Top of canopy radiation, [W m-2]

  • leaf_area_index – Leaf area index, [m m-2]

  • layer_heights – Layer heights, [m]

  • true_canopy_indexes – Indexes of canopy layers that are not NaN (maximum extent to capture all depths even if grid cells have different number of layers)

  • topsoil_layer_index – Index of topsoil layer

  • light_extinction_coefficient – Light extinction coefficient for canopy

  • canopy_temperature_ini_factor – Factor used to initialise canopy temperature as a function of air temperature and absorbed shortwave radiation

Returns:

Dictionary with absorbed radiation (canopy), canopy temperature, sensible

and latent heat flux (canopy and soil), and ground heat flux.

virtual_ecosystem.models.abiotic.energy_balance.initialise_canopy_temperature(air_temperature: ndarray[Any, dtype[float32]], absorbed_radiation: ndarray[Any, dtype[float32]], canopy_temperature_ini_factor: float) ndarray[Any, dtype[float32]]

Initialise canopy temperature.

Parameters:
  • air_temperature – Air temperature, [C]

  • canopy_temperature_ini_factor – Factor used to initialise canopy temperature as a function of air temperature and absorbed shortwave radiation

  • absorbed_radiation – Shortwave radiation absorbed by canopy

Returns:

Initial canopy temperature, [C]

virtual_ecosystem.models.abiotic.energy_balance.latent_heat_flux_linearisation(latent_heat_vapourisation: ndarray[Any, dtype[float32]], leaf_vapour_conductivity: ndarray[Any, dtype[float32]], atmospheric_pressure_ref: ndarray[Any, dtype[float32]], saturated_vapour_pressure_ref: ndarray[Any, dtype[float32]], a_E: ndarray[Any, dtype[float32]], b_E: ndarray[Any, dtype[float32]], delta_v_ref: ndarray[Any, dtype[float32]]) tuple[ndarray[Any, dtype[float32]], ndarray[Any, dtype[float32]]]

Calculate factors for latent heat flux linearisation.

Parameters:
  • latent_heat_vapourisation – latent heat of vapourisation

  • leaf_vapour_conductivity – leaf vapour conductivity, [mol m-2 s-1]

  • atmospheric_pressure_ref – Atmospheric pressure at reference height 2 m above canopy, [kPa]

  • saturated_vapour_pressure_ref – Satuated vapour pressure at reference height 2 m above canopy, [kPa]

  • a_E – Factor for vapour pressure linearisation

  • b_E – Factor for vapour pressure linearisation

  • delta_v_ref – Slope of saturated vapour pressure curve

Returns:

Factors a_L and b_L for latent heat flux linearisation

virtual_ecosystem.models.abiotic.energy_balance.leaf_and_air_temperature_linearisation(conductivity_from_ref_height: ndarray[Any, dtype[float32]], conductivity_from_soil: ndarray[Any, dtype[float32]], leaf_air_heat_conductivity: ndarray[Any, dtype[float32]], air_temperature_ref: ndarray[Any, dtype[float32]], top_soil_temperature: ndarray[Any, dtype[float32]]) tuple[ndarray[Any, dtype[float32]], ndarray[Any, dtype[float32]]]

Calculate factors for leaf and air temperature linearisation.

Parameters:
  • conductivity_from_ref_height – Conductivity from reference height, [mol m-2 s-2]

  • conductivity_from_soil – Conductivity from soil, [mol m-2 s-2]

  • leaf_air_heat_conductivity – Leaf air heat conductivity, [mol m-2 s-2]

  • air_temperature_ref – Air temperature at reference height 2m above the canopy,[C]

  • top_soil_temperature – Top soil temperature, [C]

Returns:

Factors a_A and b_A for leaf and air temperature linearisation

virtual_ecosystem.models.abiotic.energy_balance.longwave_radiation_flux_linearisation(a_A: ndarray[Any, dtype[float32]], b_A: ndarray[Any, dtype[float32]], air_temperature_ref: ndarray[Any, dtype[float32]], leaf_emissivity: float, stefan_boltzmann_constant: float) tuple[ndarray[Any, dtype[float32]], ndarray[Any, dtype[float32]]]

Calculate factors for longwave radiative flux linearisation.

Parameters:
  • a_A – Factor for leaf and air temperature linearisation

  • b_A – Factor for leaf and air temperature linearisation

  • air_temperature_ref – Air temperature at reference height 2m above the canopy,[C]

  • leaf_emissivity – Leaf emissivity, dimensionless

  • stefan_boltzmann_constant – Stefan Boltzmann constant, [W m-2 K-4]

Returns:

Factors a_R and b_R for longwave radiative flux linearisation

virtual_ecosystem.models.abiotic.energy_balance.vapour_pressure_linearisation(vapour_pressure_ref: ndarray[Any, dtype[float32]], saturated_vapour_pressure_ref: ndarray[Any, dtype[float32]], soil_vapour_pressure: ndarray[Any, dtype[float32]], conductivity_from_soil: ndarray[Any, dtype[float32]], leaf_vapour_conductivity: ndarray[Any, dtype[float32]], conductivity_from_ref_height: ndarray[Any, dtype[float32]], delta_v_ref: ndarray[Any, dtype[float32]]) tuple[ndarray[Any, dtype[float32]], ndarray[Any, dtype[float32]]]

Calculate factors for vapour pressure linearisation.

Parameters:
  • vapour_pressure_ref – Vapour pressure at reference height 2 m above canopy, [kPa]

  • saturated_vapour_pressure_ref – Saturated vapour pressure at reference height 2 m above canopy, [kPa]

  • soil_vapour_pressure – Soil vapour pressure, [kPa]

  • conductivity_from_soil – Conductivity from soil TODO unit

  • leaf_vapour_conductivity – Leaf vapour conductivity, [mol m-2 s-1]

  • conductivity_from_ref_height – Conductivity frm reference height, [mol m-2 s-1]

  • delta_v_ref – Slope of saturated vapour pressure curve

Returns:

Factors a_E and b_E for vapour pressure linearisation