2Photosynthesis parameter structures and data classes for PyHelios.
4This module provides Python data structures that mirror the C++ parameter
5classes used by the PhotosynthesisModel plugin, with proper defaults and
9from dataclasses
import dataclass, field
10from typing
import List, Optional, Union
14PHOTOSYNTHESIS_SPECIES = [
15 "Almond",
"Apple",
"Cherry",
"Prune",
"Pear",
16 "PistachioFemale",
"PistachioMale",
"Walnut",
18 "Elderberry",
"Toyon",
"Big_Leaf_Maple",
"Western_Redbud",
"Baylaurel",
"Olive",
19 "EasternRedbudSunlit",
"EasternRedbudShaded"
30 "pistachiofemale":
"PistachioFemale",
31 "pistachiomale":
"PistachioMale",
34 "elderberry":
"Elderberry",
36 "big_leaf_maple":
"Big_Leaf_Maple",
37 "western_redbud":
"Western_Redbud",
38 "baylaurel":
"Baylaurel",
40 "easternredbudsunlit":
"EasternRedbudSunlit",
41 "easternredbudshaded":
"EasternRedbudShaded",
44 "bigleafmaple":
"Big_Leaf_Maple",
45 "bigmaple":
"Big_Leaf_Maple",
46 "westernredbud":
"Western_Redbud",
47 "redbud":
"Western_Redbud",
48 "easternredbud":
"EasternRedbudSunlit",
49 "pistachio":
"PistachioFemale",
51 "cabernetSauvignon":
"Grape",
61_NO_OPTIMUM_TOPT_C = 200.0
67 Temperature response parameters for photosynthetic processes.
69 These parameters define how photosynthetic rates vary with temperature
70 using the modified Arrhenius equation.
73 value_at_25C: Value of the parameter at 25°C
74 dHa: Activation energy (rate of increase parameter)
75 dHd: Deactivation energy (rate of decrease parameter)
76 Topt: Optimum temperature in Kelvin (10000K means no optimum)
78 value_at_25C: float = 100.0
84 """Validate parameter values after initialization."""
86 raise ValueError(
"value_at_25C must be finite")
87 if not math.isfinite(self.
dHa)
or self.
dHa < 0:
88 raise ValueError(
"dHa must be finite and non-negative")
89 if not math.isfinite(self.
dHd)
or self.
dHd < 0:
90 raise ValueError(
"dHd must be finite and non-negative")
91 if not math.isfinite(self.
Topt)
or self.
Topt < 0:
92 raise ValueError(
"Topt must be finite and non-negative")
98 f
"Deactivation energy dHd must be strictly greater than activation energy dHa "
99 f
"for a peaked temperature response. Received dHa = {self.dHa} kJ/mol and "
100 f
"dHd = {self.dHd} kJ/mol. The peaked Arrhenius form evaluates "
101 f
"ln(dHd/dHa - 1), which is undefined when dHd <= dHa. Increase dHd "
102 f
"(a typical value is 10*dHa, or 200-600 kJ/mol) or omit dHd to use the default."
109 Empirical photosynthesis model coefficients.
111 This model uses empirical relationships to estimate photosynthetic
112 rates based on environmental conditions.
115 Tref: Reference temperature (K)
116 Ci_ref: Reference CO2 concentration (μmol CO2/mol air)
117 Asat: Light-saturated photosynthetic rate (μmol/m²/s)
118 theta: Half-saturation light level (W/m²)
119 Tmin: Minimum temperature for photosynthesis (K)
120 Topt: Optimum temperature for photosynthesis (K)
121 q: Temperature response parameter (unitless)
122 R: Respiration temperature coefficient (μmol·K^0.5/m²/s)
123 ER: Respiration activation energy (1/K)
124 kC: CO2 response coefficient (unitless)
127 Ci_ref: float = 290.0
138 """Validate parameter values after initialization."""
140 raise ValueError(
"Reference temperature must be positive")
142 raise ValueError(
"Reference CO2 concentration must be positive")
144 raise ValueError(
"Light-saturated photosynthetic rate cannot be negative")
146 raise ValueError(
"Half-saturation light level must be positive")
148 raise ValueError(
"Minimum temperature must be positive")
150 raise ValueError(
"Optimum temperature must be positive")
152 raise ValueError(
"Minimum temperature must be less than optimum temperature")
154 raise ValueError(
"Temperature response parameter must be positive")
156 raise ValueError(
"Respiration coefficient cannot be negative")
158 raise ValueError(
"Respiration activation energy cannot be negative")
160 raise ValueError(
"CO2 response coefficient cannot be negative")
167 f
"Reference temperature Tref ({self.Tref} K) must be greater than the minimum "
168 f
"temperature Tmin ({self.Tmin} K); otherwise the empirical temperature "
169 f
"response f_T has a zero or negative reference denominator."
171 denom_ref = (1.0 + self.
q) * self.
Topt - self.
Tmin - self.
q * self.
Tref
172 if abs(denom_ref) < 1e-6:
174 f
"Empirical temperature response coefficients are degenerate: "
175 f
"(1+q)*Topt - Tmin - q*Tref = {denom_ref}, which makes the reference "
176 f
"denominator of f_T zero. Adjust Topt, Tmin, Tref or q."
180 """Convert to float array for C++ interface."""
187 def from_array(cls, coefficients: List[float]) ->
'EmpiricalModelCoefficients':
188 """Create from float array (from C++ interface)."""
189 if len(coefficients) < 10:
190 raise ValueError(
"Need at least 10 coefficients for empirical model")
192 Tref=coefficients[0], Ci_ref=coefficients[1], Asat=coefficients[2],
193 theta=coefficients[3], Tmin=coefficients[4], Topt=coefficients[5],
194 q=coefficients[6], R=coefficients[7], ER=coefficients[8], kC=coefficients[9]
201 Farquhar-von Caemmerer-Berry photosynthesis model coefficients.
203 This model provides a mechanistic description of leaf photosynthesis
204 based on biochemical limitations and temperature responses.
206 Core Parameters (at 25°C):
207 Vcmax: Maximum carboxylation rate (μmol/m²/s, -1 = uninitialized)
208 Jmax: Maximum electron transport rate (μmol/m²/s, -1 = uninitialized)
209 alpha: Quantum efficiency of photosystem II (μmol electrons/μmol photons)
210 Rd: Dark respiration rate (μmol/m²/s, -1 = uninitialized)
211 O: Ambient oxygen concentration (mmol/mol)
212 TPU_flag: Enable triose phosphate utilization limitation (0/1)
214 Temperature Response Parameters:
215 c_*: Scaling factor for Arrhenius equation
216 dH_*: Activation energy for temperature response
228 c_Vcmax: float = 26.35
229 c_Jmax: float = 18.86
230 c_Gamma: float = 19.02
236 dH_Vcmax: float = 65.33
237 dH_Jmax: float = 46.36
238 dH_Gamma: float = 37.83
247 gm_at_25C: float = float(
'inf')
249 Topt_gm_C: float = -1.0
253 _vcmax_temp_response: Optional[PhotosyntheticTemperatureResponseParameters] = field(default=
None, init=
False)
254 _jmax_temp_response: Optional[PhotosyntheticTemperatureResponseParameters] = field(default=
None, init=
False)
255 _rd_temp_response: Optional[PhotosyntheticTemperatureResponseParameters] = field(default=
None, init=
False)
256 _alpha_temp_response: Optional[PhotosyntheticTemperatureResponseParameters] = field(default=
None, init=
False)
257 _theta_temp_response: Optional[PhotosyntheticTemperatureResponseParameters] = field(default=
None, init=
False)
260 """Validate parameter values after initialization."""
262 raise ValueError(
"Oxygen concentration must be positive")
264 raise ValueError(
"TPU_flag must be 0 or 1")
267 for param_name, value
in [
273 if not math.isfinite(value):
274 raise ValueError(f
"Temperature parameter {param_name} must be finite")
276 def setVcmax(self, vcmax_at_25c: float, dha: Optional[float] =
None,
277 topt: Optional[float] =
None, dhd: Optional[float] =
None) ->
None:
278 """Set Vcmax with temperature response (mimics C++ overloads)."""
289 vcmax_at_25c, dHa=dha, dHd=10.0 * dha, Topt=273.15 + topt)
293 vcmax_at_25c, dHa=dha, dHd=dhd, Topt=273.15 + topt)
295 self.
Vcmax = vcmax_at_25c
297 def setJmax(self, jmax_at_25c: float, dha: Optional[float] =
None,
298 topt: Optional[float] =
None, dhd: Optional[float] =
None) ->
None:
299 """Set Jmax with temperature response (mimics C++ overloads)."""
307 jmax_at_25c, dHa=dha, dHd=10.0 * dha, Topt=273.15 + topt)
310 jmax_at_25c, dHa=dha, dHd=dhd, Topt=273.15 + topt)
312 self.
Jmax = jmax_at_25c
314 def setRd(self, rd_at_25c: float, dha: Optional[float] =
None,
315 topt: Optional[float] =
None, dhd: Optional[float] =
None) ->
None:
316 """Set dark respiration with temperature response (mimics C++ overloads)."""
324 rd_at_25c, dHa=dha, dHd=10.0 * dha, Topt=273.15 + topt)
327 rd_at_25c, dHa=dha, dHd=dhd, Topt=273.15 + topt)
332 topt: Optional[float] =
None, dhd: Optional[float] =
None) ->
None:
333 """Set quantum efficiency with temperature response (mimics C++ overloads)."""
341 alpha_at_25c, dHa=dha, dHd=10.0 * dha, Topt=273.15 + topt)
344 alpha_at_25c, dHa=dha, dHd=dhd, Topt=273.15 + topt)
349 topt: Optional[float] =
None, dhd: Optional[float] =
None) ->
None:
350 """Set light response curvature with temperature response (mimics C++ overloads)."""
358 theta_at_25c, dHa=dha, dHd=10.0 * dha, Topt=273.15 + topt)
361 theta_at_25c, dHa=dha, dHd=dhd, Topt=273.15 + topt)
364 """Get Vcmax temperature response parameters."""
370 """Get Jmax temperature response parameters."""
376 """Get dark respiration temperature response parameters."""
382 """Get quantum efficiency temperature response parameters."""
388 """Get light response curvature temperature response parameters."""
394 fallback_value: float) -> List[float]:
395 """Pack a temperature response into its 4-float (value, dHa, Topt_C, dHd) block.
397 ``Topt`` is stored in Kelvin and defaults to 10000 K to mean "no optimum". The flat
398 array carries Topt in Celsius with -1 as the no-optimum sentinel, matching
399 ``packTempResponse`` in native/src/pyhelios_wrapper_photosynthesis.cpp.
402 return [fallback_value, -1.0, -1.0, -1.0]
403 topt_c = response.Topt - 273.15
404 if topt_c >= _NO_OPTIMUM_TOPT_C:
406 return [response.value_at_25C, response.dHa, topt_c, response.dHd]
409 """Convert to float array for C++ interface (38 floats; helios-core 1.3.80+).
411 Slots 0..17 are the legacy Farquhar fields (Vcmax/Jmax/alpha/Rd/O/TPU_flag plus
412 the 12 c_*/dH_* temperature constants). Slots 18..21 carry the mesophyll
413 conductance gm temperature response: (gm_at_25C, dHa, Topt_C, dHd) using the
414 -1 sentinel convention (dHa < 0 → constant, Topt_C < 0 → monotonic Arrhenius,
415 dHd < 0 → default deactivation energy). Slots 22..37 carry the same 4-float block
416 for Vcmax, Jmax, Rd and alpha in that order, and slots 38..41 the same block for the
417 light response curvature theta.
419 The rate blocks in slots 22..37 exist because slots 0..3 can only express a rate at
420 25 C. As of helios-core 1.3.80 the C++ setters stamp the deprecated scalar fields to
421 -1 so the temperature-response object is authoritative, and every species in the
422 library is populated through those setters — so slots 0..3 alone cannot round-trip a
444 def from_array(cls, coefficients: List[float]) ->
'FarquharModelCoefficients':
445 """Create from float array (from C++ interface).
447 Accepts the legacy 18-float layout (pre-1.3.72), the 22-float layout with mesophyll
448 conductance gm in slots 18..21, and the 38-float layout (1.3.80+) that additionally
449 carries the Vcmax/Jmax/Rd/alpha temperature responses in slots 22..37, and the
450 42-float layout that adds the light response curvature theta in slots 38..41. Shorter
451 arrays leave the corresponding responses unset, reproducing the earlier behaviour.
453 if len(coefficients) < 18:
454 raise ValueError(
"Need at least 18 coefficients for Farquhar model")
456 gm_at_25C = coefficients[18]
if len(coefficients) > 18
else float(
'inf')
457 dHa_gm = coefficients[19]
if len(coefficients) > 19
else -1.0
458 Topt_gm_C = coefficients[20]
if len(coefficients) > 20
else -1.0
459 dHd_gm = coefficients[21]
if len(coefficients) > 21
else -1.0
462 Vcmax=coefficients[0], Jmax=coefficients[1], alpha=coefficients[2],
463 Rd=coefficients[3], O=coefficients[4], TPU_flag=int(coefficients[5]),
464 c_Vcmax=coefficients[6], dH_Vcmax=coefficients[7],
465 c_Jmax=coefficients[8], dH_Jmax=coefficients[9],
466 c_Rd=coefficients[10], dH_Rd=coefficients[11],
467 c_Kc=coefficients[12], dH_Kc=coefficients[13],
468 c_Ko=coefficients[14], dH_Ko=coefficients[15],
469 c_Gamma=coefficients[16], dH_Gamma=coefficients[17],
470 gm_at_25C=gm_at_25C, dHa_gm=dHa_gm, Topt_gm_C=Topt_gm_C, dHd_gm=dHd_gm,
473 if len(coefficients) >= 38:
474 for offset, setter
in ((22, instance.setVcmax), (26, instance.setJmax),
475 (30, instance.setRd), (34, instance.setQuantumEfficiency_alpha)):
476 value, dha, topt, dhd = coefficients[offset:offset + 4]
482 setter(value, dha, topt)
484 setter(value, dha, topt, dhd)
486 if len(coefficients) >= 42:
487 value, dha, topt, dhd = coefficients[38:42]
489 instance.setLightResponseCurvature_theta(value)
491 instance.setLightResponseCurvature_theta(value, dha)
493 instance.setLightResponseCurvature_theta(value, dha, topt)
495 instance.setLightResponseCurvature_theta(value, dha, topt, dhd)
502 Validate and normalize species name for photosynthesis library.
505 species: Species name (case insensitive, supports aliases)
508 Normalized species name
511 ValueError: If species is not recognized
514 raise ValueError(
"Species name cannot be empty")
517 if species
in PHOTOSYNTHESIS_SPECIES:
521 species_lower = species.lower()
522 if species_lower
in SPECIES_ALIASES:
523 return SPECIES_ALIASES[species_lower]
526 for known_species
in PHOTOSYNTHESIS_SPECIES:
527 if known_species.lower() == species_lower:
531 available_species = sorted(set(list(PHOTOSYNTHESIS_SPECIES) + list(SPECIES_ALIASES.keys())))
533 f
"Unknown species '{species}'. Available species and aliases:\n"
534 f
" {', '.join(available_species[:8])}\n"
535 f
" {', '.join(available_species[8:16])}\n"
536 f
" {', '.join(available_species[16:])}"
541 """Get list of available species in the photosynthesis library."""
542 return sorted(PHOTOSYNTHESIS_SPECIES.copy())
546 """Get dictionary of species aliases."""
547 return SPECIES_ALIASES.copy()
Empirical photosynthesis model coefficients.
float Topt
Optimum temperature for photosynthesis (K)
float Tmin
Minimum temperature for photosynthesis (K)
float Asat
Light-saturated photosynthetic rate (μmol/m²/s)
__post_init__(self)
Validate parameter values after initialization.
float q
Temperature response parameter (unitless)
'EmpiricalModelCoefficients' from_array(cls, List[float] coefficients)
Create from float array (from C++ interface).
float R
Respiration temperature coefficient (μmol·K^0.5/m²/s)
List[float] to_array(self)
Convert to float array for C++ interface.
float Tref
Reference temperature (K)
float ER
Respiration activation energy (1/K)
float Ci_ref
Reference CO2 concentration (μmol CO2/mol air)
float theta
Half-saturation light level (W/m²)
float kC
CO2 response coefficient (unitless)
Farquhar-von Caemmerer-Berry photosynthesis model coefficients.
List[float] to_array(self)
Convert to float array for C++ interface (38 floats; helios-core 1.3.80+).
None setVcmax(self, float vcmax_at_25c, Optional[float] dha=None, Optional[float] topt=None, Optional[float] dhd=None)
Set Vcmax with temperature response (mimics C++ overloads).
Optional _vcmax_temp_response
__post_init__(self)
Validate parameter values after initialization.
PhotosyntheticTemperatureResponseParameters getLightResponseCurvatureTempResponse(self)
Get light response curvature temperature response parameters.
None setQuantumEfficiency_alpha(self, float alpha_at_25c, Optional[float] dha=None, Optional[float] topt=None, Optional[float] dhd=None)
Set quantum efficiency with temperature response (mimics C++ overloads).
PhotosyntheticTemperatureResponseParameters getQuantumEfficiencyTempResponse(self)
Get quantum efficiency temperature response parameters.
Optional _theta_temp_response
List[float] _temp_response_block(self, PhotosyntheticTemperatureResponseParameters response, float fallback_value)
Pack a temperature response into its 4-float (value, dHa, Topt_C, dHd) block.
PhotosyntheticTemperatureResponseParameters getVcmaxTempResponse(self)
Get Vcmax temperature response parameters.
Optional _jmax_temp_response
'FarquharModelCoefficients' from_array(cls, List[float] coefficients)
Create from float array (from C++ interface).
PhotosyntheticTemperatureResponseParameters getRdTempResponse(self)
Get dark respiration temperature response parameters.
PhotosyntheticTemperatureResponseParameters getJmaxTempResponse(self)
Get Jmax temperature response parameters.
None setLightResponseCurvature_theta(self, float theta_at_25c, Optional[float] dha=None, Optional[float] topt=None, Optional[float] dhd=None)
Set light response curvature with temperature response (mimics C++ overloads).
None setRd(self, float rd_at_25c, Optional[float] dha=None, Optional[float] topt=None, Optional[float] dhd=None)
Set dark respiration with temperature response (mimics C++ overloads).
Optional _alpha_temp_response
None setJmax(self, float jmax_at_25c, Optional[float] dha=None, Optional[float] topt=None, Optional[float] dhd=None)
Set Jmax with temperature response (mimics C++ overloads).
Optional _rd_temp_response
Temperature response parameters for photosynthetic processes.
float dHa
Activation energy (rate of increase parameter)
float dHd
Deactivation energy (rate of decrease parameter)
__post_init__(self)
Validate parameter values after initialization.
float value_at_25C
Value of the parameter at 25°C.
float Topt
Optimum temperature in Kelvin (10000K means no optimum)
List[str] get_available_species()
Get list of available species in the photosynthesis library.
str validate_species_name(str species)
Validate and normalize species name for photosynthesis library.
dict get_species_aliases()
Get dictionary of species aliases.