The BioCro C++ Library
leaf_energy_balance.cpp
Go to the documentation of this file.
1#include <cmath> // for pow, std::abs
2#include "../framework/constants.h" // for stefan_boltzmann,
3 // celsius_to_kelvin, molar_mass_of_water
4#include "boundary_layer_conductance.h" // for leaf_boundary_layer_conductance_nikolov
5#include "conductance_helpers.h" // for g_to_mass, g_to_molecular, sequential_conductance
6#include "../math/roots/onedim/dekker.h" // for dekker
7#include "water_and_air_properties.h" // for TempToCp, dry_air_density, etc
9
26 double const epsilon_s, // dimensionless
27 double const J_a, // J / m^2 / s
28 double const leaf_temperature // degrees C
29)
30{
31 // Get longwave energy losses
32 double const R_l = epsilon_s * physical_constants::stefan_boltzmann *
33 pow(conversion_constants::celsius_to_kelvin + leaf_temperature, 4); // J / m^2 / s
34
35 // Get the energy available for transpiration and heat loss
36 return J_a - R_l; // J / m^2 / s
37}
38
49 double const air_pressure, // Pa
50 double const air_temperature, // degrees C
51 double const leaf_temperature, // degrees C
52 double const leaf_width, // m
53 double const wind_speed // m / s
54)
55{
57 air_temperature,
58 leaf_temperature - air_temperature,
59 leaf_width,
60 wind_speed,
61 air_pressure); // m / s
62}
63
69 double const air_pressure, // Pa
70 double const air_temperature, // degrees C
71 double const Delta_rho, // kg / m^3
72 double const epsilon_s, // dimensionless
73 double const gamma, // kg / m^3 / K
74 double const gbw_canopy, // m / s
75 double const J_a, // J / m^2 / s
76 double const lambda, // J / kg
77 double const leaf_temperature, // degrees C
78 double const leaf_width, // m
79 double const s, // kg / m^3 / K
80 double const stomatal_conductance, // mol / m^2 / s
81 double const wind_speed // m / s
82)
83{
84 // Get stomatal conductance to water vapor as a mass conductance
85 double const gsw = g_to_mass(air_pressure, stomatal_conductance, leaf_temperature); // m / s
86
87 // Get leaf boundary layer conductance to water vapor
88 double const gbw_leaf = calculate_gbw_leaf(
89 air_pressure,
90 air_temperature,
91 leaf_temperature,
92 leaf_width,
93 wind_speed); // m / s
94
95 // Get the boundary layer conductance and total conductance to water
96 // vapor
97 double const gbw = sequential_conductance(gbw_leaf, gbw_canopy); // m / s
98 double const gw = sequential_conductance(gsw, gbw); // m / s
99
100 // Get the new leaf temperature using the Penman-Monteith equation
101 double const Phi_N = calculate_Phi_N(epsilon_s, J_a, leaf_temperature);
102 double const pm_top = Phi_N / gw - lambda * Delta_rho; // J / m^3
103 double const pm_bottom = lambda * (s + gamma * (1.0 + gbw / gsw)); // J / m^3 / K
104
105 double const leaf_temperature_new = air_temperature + pm_top / pm_bottom; // degrees C
106
107 return leaf_temperature - leaf_temperature_new; // degrees C
108}
109
148 double absorbed_longwave_energy, // J / m^2 / s
149 double absorbed_shortwave_energy, // J / m^2 / s
150 double air_pressure, // Pa
151 double air_temperature, // degrees C
152 double gbw_canopy, // m / s
153 double leaf_width, // m
154 double relative_humidity, // dimensionless from Pa / Pa
155 double stomatal_conductance, // mol / m^2 / s
156 double wind_speed // m / s
157)
158{
159 // Set some constants
160 double constexpr epsilon_s = 1.0; // dimensionless
161
162 // Get water vapor and air properties based on the air temperature
163 double const c_p = TempToCp(air_temperature); // J / kg / K
164 double const lambda = water_latent_heat_of_vaporization_henderson(air_temperature); // J / kg
165 double const p_w_sat_air = saturation_vapor_pressure(air_temperature); // Pa
166 double const rho_ta = dry_air_density(air_temperature, air_pressure); // kg / m^3
167 double const s = TempToSFS(air_temperature); // kg / m^3 / K
168
169 // Get the pyschrometric parameter
170 double const gamma = rho_ta * c_p / lambda; // kg / m^3 / K
171
172 // Get vapor density in the ambient air.
173 double const p_w_air = p_w_sat_air * relative_humidity; // Pa
174
175 double const rho_w_air =
176 vapor_density_from_pressure(rho_ta, air_pressure, p_w_air); // kg / m^3
177
178 // Get vapor density deficit
179 double const rho_w_sat =
180 vapor_density_from_pressure(rho_ta, air_pressure, p_w_sat_air); // kg / m^3
181
182 double const Delta_rho = rho_w_sat - rho_w_air; // kg / m^3
183
184 // Get total absorbed light energy (longwave and shortwave)
185 double const J_a = absorbed_shortwave_energy + absorbed_longwave_energy; // J / m^2 / s
186
187 // Use partial application to fix all inputs to `check_leaf_temp` except
188 // leaf temperature. To solve the energy balance equations, a root of this
189 // function must be found.
190 auto check_leaf_temp_partial = [=](double const leaf_temperature) {
191 return check_leaf_temp(
192 air_pressure, // Pa
193 air_temperature, // degrees C
194 Delta_rho, // kg / m^3
195 epsilon_s, // dimensionless
196 gamma, // kg / m^3 / K
197 gbw_canopy, // m / s
198 J_a, // J / m^2 / s
199 lambda, // J / kg
200 leaf_temperature, // degrees C
201 leaf_width, // m
202 s, // kg / m^3 / K
203 stomatal_conductance, // mol / m^2 / s
204 wind_speed // m / s
205 );
206 };
207
208 // Run Dekker's method
209 double constexpr delta_temp = 50; // degrees C
210
211 root_finding::dekker solver{500, 1e-12, 1e-12};
212
213 root_finding::result_t result = solver.solve(
214 check_leaf_temp_partial,
215 air_temperature + 0.9 * delta_temp, // guess
216 air_temperature - delta_temp, // lower
217 air_temperature + delta_temp // upper
218 );
219
220 // Throw exception if not converged
221 if (!root_finding::is_successful(result.flag)) {
222 throw std::runtime_error(
223 "leaf_temperature solver reports failed convergence with termination flag:\n " +
224 root_finding::flag_message(result.flag));
225 }
226
227 // Get final value
228 double const leaf_temperature = result.root; // degrees C
229
230 // Calculate additional outputs
231 double const gsw = g_to_mass(air_pressure, stomatal_conductance, leaf_temperature); // m / s
232
233 double const gbw_leaf = calculate_gbw_leaf(
234 air_pressure,
235 air_temperature,
236 leaf_temperature,
237 leaf_width,
238 wind_speed); // m / s
239
240 double const gbw = sequential_conductance(gbw_leaf, gbw_canopy); // m / s
241 double const gbw_molecular = g_to_molecular(air_pressure, gbw, leaf_temperature); // mol / m^2 / s
242 double const gw = sequential_conductance(gsw, gbw); // m / s
243 double const Phi_N = calculate_Phi_N(epsilon_s, J_a, leaf_temperature); // degrees C
244 double const Delta_T = leaf_temperature - air_temperature; // degrees C
245 double const E = (Delta_rho + s * Delta_T) * gw; // kg / m^2 / s
246 double const H = rho_ta * c_p * Delta_T * gbw; // J / m^2 / s
247 double const storage = Phi_N - H - lambda * E; // J / m^2 / s
248
249 // Relative humidity just outside the leaf boundary layer
250 double const RH_canopy = (rho_w_air + E / gbw_canopy) / rho_w_sat; // dimensionless
251
252 // Potential evapotranspiration can be calculated assuming infinite stomatal
253 // conductance; here we call this "Penman transpiration."
254 double const EPen = (s * Phi_N + lambda * gamma * gbw * Delta_rho) /
255 (lambda * (s + gamma)); // kg / m^2 / s
256
257 // Evapotranspiration according to Priestly (?)
258 double constexpr dryness_coefficient = 1.26; // dimensionless
259 double const EPries = dryness_coefficient * s * Phi_N /
260 (lambda * (s + gamma)); // kg / m^2 / s
261
262 // The transpiration rates here have units of kg / m^2 / s. They can be
263 // converted to mmol / m^2 / s using the molar mass of water (in kg / mol)
264 // and noting that 1e3 mmol = 1 mol.
265 double constexpr cf = 1e3 / physical_constants::molar_mass_of_water; // mmol / kg for water
266
268 /* Deltat = */ Delta_T, // degrees C
269 /* E_loss = */ lambda * E, // J / m^2 / s
270 /* EPenman = */ EPen * cf, // mmol / m^2 / s
271 /* EPriestly = */ EPries * cf, // mmol / m^2 / s
272 /* gbw = */ gbw, // m / s
273 /* gbw_canopy = */ gbw_canopy, // m / s
274 /* gbw_leaf = */ gbw_leaf, // m / s
275 /* gbw_molecular = */ gbw_molecular, // mol / m^2 / s
276 /* gsw = */ gsw, // m / s
277 /* H = */ H, // J / m^2 / s
278 /* leaf_temp_check = */ result.residual, // degrees C
279 /* PhiN = */ Phi_N, // J / m^2 / s
280 /* RH_canopy = */ RH_canopy, // dimensionless
281 /* storage = */ storage, // J / m^2 / s
282 /* TransR = */ E * cf, // mmol / m^2 / s
283 /* iterations = */ result.iteration // not a physical quantity
284 };
285}
double leaf_boundary_layer_conductance_campbell(double air_temperature, double delta_t, double lw, double windspeed, double p)
Calculates the conductance for water vapor flow from the leaf across its boundary layer using a model...
double sequential_conductance(double const conductance_1, double const conductance_2)
Calculates the total conductance across two sequential gas paths.
double g_to_molecular(double const pressure, double const conductance, double const temperature)
Convert a conductance value from a "mass" basis (in units of m / s) to a "molecular" basis (in units ...
double g_to_mass(double const pressure, double const conductance, double const temperature)
Convert a conductance value from a "molecular" basis (in units of mol / m^2 / s) to a "mass" basis (i...
double check_leaf_temp(double const air_pressure, double const air_temperature, double const Delta_rho, double const epsilon_s, double const gamma, double const gbw_canopy, double const J_a, double const lambda, double const leaf_temperature, double const leaf_width, double const s, double const stomatal_conductance, double const wind_speed)
Calculates a difference in leaf temperature; this function will return zero only if leaf temperature ...
energy_balance_outputs leaf_energy_balance(double absorbed_longwave_energy, double absorbed_shortwave_energy, double air_pressure, double air_temperature, double gbw_canopy, double leaf_width, double relative_humidity, double stomatal_conductance, double wind_speed)
Calculates leaf-level temperature and transpiration rate for a leaf within a canopy using a Penman-Mo...
double calculate_Phi_N(double const epsilon_s, double const J_a, double const leaf_temperature)
Calculates the total energy available to the leaf for transpiration and sensible heat loss,...
double calculate_gbw_leaf(double const air_pressure, double const air_temperature, double const leaf_temperature, double const leaf_width, double const wind_speed)
Calculates the leaf boundary layer conductance using the Nikolov model.
double TempToSFS(double air_temperature)
Determine the derivative of saturation water vapor density with respect to temperature at a particula...
double saturation_vapor_pressure(double air_temperature)
Determine saturation water vapor pressure (Pa) from air temperature (degrees C) using the Arden Buck ...
double TempToCp(double air_temperature)
Determine the specific heat capacity of dry air at constant pressure (c_p) at a particular value of a...
double dry_air_density(const double air_temperature, const double air_pressure)
Calculate the density of dry air from temperature and pressure using the ideal gas law.
double vapor_density_from_pressure(double density_of_dry_air, double total_pressure, double vapor_pressure)
Use Equation 14.5a from Thornley & Johnson (1990) to calculate water vapor density from water vapor p...
double water_latent_heat_of_vaporization_henderson(double temperature)
Determine the latent heat of vaporization of water from its temperature.