0.1.39
Loading...
Searching...
No Matches
photosynthesis.py
Go to the documentation of this file.
1"""
2Photosynthesis parameter structures and data classes for PyHelios.
3
4This module provides Python data structures that mirror the C++ parameter
5classes used by the PhotosynthesisModel plugin, with proper defaults and
6validation support.
7"""
8
9from dataclasses import dataclass, field
10from typing import List, Optional, Union
11import math
12
13# Known species in the photosynthesis library (21 species with aliases)
14PHOTOSYNTHESIS_SPECIES = [
15 "Almond", "Apple", "Cherry", "Prune", "Pear",
16 "PistachioFemale", "PistachioMale", "Walnut",
17 "Grape", # cv. Cabernet Sauvignon
18 "Elderberry", "Toyon", "Big_Leaf_Maple", "Western_Redbud", "Baylaurel", "Olive",
19 "EasternRedbudSunlit", "EasternRedbudShaded"
20]
21
22# Species aliases for case-insensitive and format-flexible lookup
23SPECIES_ALIASES = {
24 # Standard names (already in PHOTOSYNTHESIS_SPECIES)
25 "almond": "Almond",
26 "apple": "Apple",
27 "cherry": "Cherry",
28 "prune": "Prune",
29 "pear": "Pear",
30 "pistachiofemale": "PistachioFemale",
31 "pistachiomale": "PistachioMale",
32 "walnut": "Walnut",
33 "grape": "Grape",
34 "elderberry": "Elderberry",
35 "toyon": "Toyon",
36 "big_leaf_maple": "Big_Leaf_Maple",
37 "western_redbud": "Western_Redbud",
38 "baylaurel": "Baylaurel",
39 "olive": "Olive",
40 "easternredbudsunlit": "EasternRedbudSunlit",
41 "easternredbudshaded": "EasternRedbudShaded",
42
43 # Common aliases and variations
44 "bigleafmaple": "Big_Leaf_Maple",
45 "bigmaple": "Big_Leaf_Maple",
46 "westernredbud": "Western_Redbud",
47 "redbud": "Western_Redbud",
48 "easternredbud": "EasternRedbudSunlit", # Default to sunlit
49 "pistachio": "PistachioFemale", # Default to female
50 "cabernet": "Grape",
51 "cabernetSauvignon": "Grape",
52 "grapevine": "Grape"
53}
54
55
56# Topt sentinel: PhotosyntheticTemperatureResponseParameters stores Topt in Kelvin and uses
57# 10000 K to mean "no optimum". After conversion to Celsius that is ~9726.85, far above the
58# 100 C that helios-core's own validateOptimalTemperature accepts, so any value at or above
59# 200 C in the flat array must be the sentinel rather than user data. Mirrors
60# C4_NO_OPTIMUM_TOPT_C in native/src/pyhelios_wrapper_photosynthesis.cpp.
61_NO_OPTIMUM_TOPT_C = 200.0
62
63
64@dataclass
66 """
67 Temperature response parameters for photosynthetic processes.
68
69 These parameters define how photosynthetic rates vary with temperature
70 using the modified Arrhenius equation.
71
72 Attributes:
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)
77 """
78 value_at_25C: float = 100.0
79 dHa: float = 60.0
80 dHd: float = 600.0
81 Topt: float = 10000.0 # Very high = no temperature optimum
82
83 def __post_init__(self):
84 """Validate parameter values after initialization."""
85 if not math.isfinite(self.value_at_25C):
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")
93 # Mirrors validateDeactivationEnergy in helios-core 1.3.80: the peaked Arrhenius form
94 # evaluates ln(dHd/dHa - 1), which is undefined unless dHd > dHa. dHa <= 0 means no
95 # Arrhenius term is applied, so dHd is unused.
96 if self.dHa > 0 and self.dHd <= self.dHa:
97 raise ValueError(
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."
103 )
104
105
106@dataclass
108 """
109 Empirical photosynthesis model coefficients.
110
111 This model uses empirical relationships to estimate photosynthetic
112 rates based on environmental conditions.
113
114 Attributes:
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)
125 """
126 Tref: float = 298.0 # K
127 Ci_ref: float = 290.0 # μmol CO2/mol air
128 Asat: float = 18.18 # μmol/m²/s
129 theta: float = 62.03 # W/m²
130 Tmin: float = 290.0 # K
131 Topt: float = 303.0 # K
132 q: float = 0.344 # unitless
133 R: float = 1.663e5 # μmol·K^0.5/m²/s
134 ER: float = 3740.0 # 1/K
135 kC: float = 0.791 # unitless
136
137 def __post_init__(self):
138 """Validate parameter values after initialization."""
139 if self.Tref <= 0:
140 raise ValueError("Reference temperature must be positive")
141 if self.Ci_ref <= 0:
142 raise ValueError("Reference CO2 concentration must be positive")
143 if self.Asat < 0:
144 raise ValueError("Light-saturated photosynthetic rate cannot be negative")
145 if self.theta <= 0:
146 raise ValueError("Half-saturation light level must be positive")
147 if self.Tmin <= 0:
148 raise ValueError("Minimum temperature must be positive")
149 if self.Topt <= 0:
150 raise ValueError("Optimum temperature must be positive")
151 if self.Tmin >= self.Topt:
152 raise ValueError("Minimum temperature must be less than optimum temperature")
153 if self.q <= 0:
154 raise ValueError("Temperature response parameter must be positive")
155 if self.R < 0:
156 raise ValueError("Respiration coefficient cannot be negative")
157 if self.ER < 0:
158 raise ValueError("Respiration activation energy cannot be negative")
159 if self.kC < 0:
160 raise ValueError("CO2 response coefficient cannot be negative")
161 # Mirrors the coefficient checks helios-core 1.3.80 added to the empirical
162 # temperature response f_T. Before 1.3.80 the Tmin/Topt/Tref/q coefficients were
163 # completely inert; now they drive f_T, and these two combinations make its
164 # reference denominator zero, giving an infinite or NaN assimilation rate.
165 if self.Tref <= self.Tmin:
166 raise ValueError(
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."
170 )
171 denom_ref = (1.0 + self.q) * self.Topt - self.Tmin - self.q * self.Tref
172 if abs(denom_ref) < 1e-6:
173 raise ValueError(
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."
177 )
178
179 def to_array(self) -> List[float]:
180 """Convert to float array for C++ interface."""
181 return [
182 self.Tref, self.Ci_ref, self.Asat, self.theta, self.Tmin,
183 self.Topt, self.q, self.R, self.ER, self.kC
184 ]
185
186 @classmethod
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")
191 return cls(
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]
195 )
196
197
198@dataclass
200 """
201 Farquhar-von Caemmerer-Berry photosynthesis model coefficients.
202
203 This model provides a mechanistic description of leaf photosynthesis
204 based on biochemical limitations and temperature responses.
205
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)
213
214 Temperature Response Parameters:
215 c_*: Scaling factor for Arrhenius equation
216 dH_*: Activation energy for temperature response
217 """
218 # Core parameters at 25°C
219 Vcmax: float = -1.0 # μmol/m²/s (uninitialized)
220 Jmax: float = -1.0 # μmol/m²/s (uninitialized)
221 alpha: float = -1.0 # unitless (uninitialized)
222 Rd: float = -1.0 # μmol/m²/s (uninitialized)
223 O: float = 213.5 # ambient oxygen concentration (mmol/mol)
224 TPU_flag: int = 0 # enable TPU limitation
225
226 # Temperature scaling factors (c_*)
227 c_Rd: float = 18.72
228 c_Vcmax: float = 26.35
229 c_Jmax: float = 18.86
230 c_Gamma: float = 19.02
231 c_Kc: float = 38.05
232 c_Ko: float = 20.30
233
234 # Activation energies (dH_*)
235 dH_Rd: float = 46.39
236 dH_Vcmax: float = 65.33
237 dH_Jmax: float = 46.36
238 dH_Gamma: float = 37.83
239 dH_Kc: float = 79.43
240 dH_Ko: float = 36.38
241
242 # Mesophyll conductance gm (helios-core 1.3.72+). The default math.inf reproduces
243 # the legacy Cc = Ci behaviour (no mesophyll diffusion limitation). When packed into
244 # the flat coefficient array, slots [18..21] are (gm_at_25C, dHa, Topt_C, dHd) using
245 # the same -1 sentinel convention as the C4 model: dHa < 0 → constant gm with no
246 # temperature response.
247 gm_at_25C: float = float('inf') # mol CO2 / m^2 / s / bar
248 dHa_gm: float = -1.0 # kJ/mol; -1 disables temperature response
249 Topt_gm_C: float = -1.0 # Celsius; -1 → monotonic Arrhenius
250 dHd_gm: float = -1.0 # kJ/mol; -1 → default
252 # Temperature response parameter containers
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)
259 def __post_init__(self):
260 """Validate parameter values after initialization."""
261 if self.O <= 0:
262 raise ValueError("Oxygen concentration must be positive")
263 if self.TPU_flag not in (0, 1):
264 raise ValueError("TPU_flag must be 0 or 1")
265
266 # Validate temperature parameters
267 for param_name, value in [
268 ('c_Rd', self.c_Rd), ('c_Vcmax', self.c_Vcmax), ('c_Jmax', self.c_Jmax),
269 ('c_Gamma', self.c_Gamma), ('c_Kc', self.c_Kc), ('c_Ko', self.c_Ko),
270 ('dH_Rd', self.dH_Rd), ('dH_Vcmax', self.dH_Vcmax), ('dH_Jmax', self.dH_Jmax),
271 ('dH_Gamma', self.dH_Gamma), ('dH_Kc', self.dH_Kc), ('dH_Ko', self.dH_Ko)
272 ]:
273 if not math.isfinite(value):
274 raise ValueError(f"Temperature parameter {param_name} must be finite")
275
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)."""
279 if dha is None:
280 # 1-parameter version
282 elif topt is None:
283 # 2-parameter version
285 elif dhd is None:
286 # 3-parameter version
287 # C++ defaults dHd to 10*dHa for the 3-argument peaked form.
289 vcmax_at_25c, dHa=dha, dHd=10.0 * dha, Topt=273.15 + topt)
290 else:
291 # 4-parameter version
293 vcmax_at_25c, dHa=dha, dHd=dhd, Topt=273.15 + topt)
294
295 self.Vcmax = vcmax_at_25c
296
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)."""
300 if dha is None:
302 elif topt is None:
304 elif dhd is None:
305 # C++ defaults dHd to 10*dHa for the 3-argument peaked form.
307 jmax_at_25c, dHa=dha, dHd=10.0 * dha, Topt=273.15 + topt)
308 else:
310 jmax_at_25c, dHa=dha, dHd=dhd, Topt=273.15 + topt)
311
312 self.Jmax = jmax_at_25c
313
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)."""
317 if dha is None:
319 elif topt is None:
321 elif dhd is None:
322 # C++ defaults dHd to 10*dHa for the 3-argument peaked form.
324 rd_at_25c, dHa=dha, dHd=10.0 * dha, Topt=273.15 + topt)
325 else:
327 rd_at_25c, dHa=dha, dHd=dhd, Topt=273.15 + topt)
328
329 self.Rd = rd_at_25c
330
331 def setQuantumEfficiency_alpha(self, alpha_at_25c: float, dha: Optional[float] = None,
332 topt: Optional[float] = None, dhd: Optional[float] = None) -> None:
333 """Set quantum efficiency with temperature response (mimics C++ overloads)."""
334 if dha is None:
336 elif topt is None:
338 elif dhd is None:
339 # C++ defaults dHd to 10*dHa for the 3-argument peaked form.
341 alpha_at_25c, dHa=dha, dHd=10.0 * dha, Topt=273.15 + topt)
342 else:
344 alpha_at_25c, dHa=dha, dHd=dhd, Topt=273.15 + topt)
345
346 self.alpha = alpha_at_25c
347
348 def setLightResponseCurvature_theta(self, theta_at_25c: float, dha: Optional[float] = None,
349 topt: Optional[float] = None, dhd: Optional[float] = None) -> None:
350 """Set light response curvature with temperature response (mimics C++ overloads)."""
351 if dha is None:
353 elif topt is None:
355 elif dhd is None:
356 # C++ defaults dHd to 10*dHa for the 3-argument peaked form.
358 theta_at_25c, dHa=dha, dHd=10.0 * dha, Topt=273.15 + topt)
359 else:
361 theta_at_25c, dHa=dha, dHd=dhd, Topt=273.15 + topt)
362
363 def getVcmaxTempResponse(self) -> PhotosyntheticTemperatureResponseParameters:
364 """Get Vcmax temperature response parameters."""
365 if self._vcmax_temp_response is None:
366 return PhotosyntheticTemperatureResponseParameters(self.Vcmax if self.Vcmax > 0 else 100.0)
367 return self._vcmax_temp_response
368
369 def getJmaxTempResponse(self) -> PhotosyntheticTemperatureResponseParameters:
370 """Get Jmax temperature response parameters."""
371 if self._jmax_temp_response is None:
372 return PhotosyntheticTemperatureResponseParameters(self.Jmax if self.Jmax > 0 else 100.0)
373 return self._jmax_temp_response
374
375 def getRdTempResponse(self) -> PhotosyntheticTemperatureResponseParameters:
376 """Get dark respiration temperature response parameters."""
377 if self._rd_temp_response is None:
378 return PhotosyntheticTemperatureResponseParameters(self.Rd if self.Rd > 0 else 1.0)
379 return self._rd_temp_response
381 def getQuantumEfficiencyTempResponse(self) -> PhotosyntheticTemperatureResponseParameters:
382 """Get quantum efficiency temperature response parameters."""
383 if self._alpha_temp_response is None:
384 return PhotosyntheticTemperatureResponseParameters(self.alpha if self.alpha > 0 else 0.3)
385 return self._alpha_temp_response
386
387 def getLightResponseCurvatureTempResponse(self) -> PhotosyntheticTemperatureResponseParameters:
388 """Get light response curvature temperature response parameters."""
389 if self._theta_temp_response is None:
391 return self._theta_temp_response
392
393 def _temp_response_block(self, response: PhotosyntheticTemperatureResponseParameters,
394 fallback_value: float) -> List[float]:
395 """Pack a temperature response into its 4-float (value, dHa, Topt_C, dHd) block.
396
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.
400 """
401 if response is None:
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:
405 topt_c = -1.0
406 return [response.value_at_25C, response.dHa, topt_c, response.dHd]
408 def to_array(self) -> List[float]:
409 """Convert to the flat float array used by the native interface.
410
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.
418 """
419 return [
420 self.Vcmax, self.Jmax, self.alpha, self.Rd, self.O, float(self.TPU_flag),
421 # Temperature scaling factors
422 self.c_Vcmax, self.dH_Vcmax, self.c_Jmax, self.dH_Jmax,
423 self.c_Rd, self.dH_Rd, self.c_Kc, self.dH_Kc,
424 self.c_Ko, self.dH_Ko, self.c_Gamma, self.dH_Gamma,
425 # Mesophyll conductance gm temperature response (1.3.72+)
426 self.gm_at_25C, self.dHa_gm, self.Topt_gm_C, self.dHd_gm,
427 # Full rate temperature responses (1.3.80+)
430 *self._temp_response_block(self._rd_temp_response, self.Rd),
432 # Light response curvature theta temperature response (slots 38..41). Theta has no
433 # legacy scalar slot, so this block is the only representation that crosses.
435 ]
436
437 @classmethod
438 def from_array(cls, coefficients: List[float]) -> 'FarquharModelCoefficients':
439 """Create from float array (from C++ interface).
440
441 Accepts the legacy 18-float layout (pre-1.3.72), the 22-float layout with mesophyll
442 conductance gm in slots 18..21, and the 38-float layout (1.3.80+) that additionally
443 carries the Vcmax/Jmax/Rd/alpha temperature responses in slots 22..37, and the
444 42-float layout that adds the light response curvature theta in slots 38..41. Shorter
445 arrays leave the corresponding responses unset, reproducing the earlier behaviour.
446 """
447 if len(coefficients) < 18:
448 raise ValueError("Need at least 18 coefficients for Farquhar model")
449
450 gm_at_25C = coefficients[18] if len(coefficients) > 18 else float('inf')
451 dHa_gm = coefficients[19] if len(coefficients) > 19 else -1.0
452 Topt_gm_C = coefficients[20] if len(coefficients) > 20 else -1.0
453 dHd_gm = coefficients[21] if len(coefficients) > 21 else -1.0
454
455 instance = cls(
456 Vcmax=coefficients[0], Jmax=coefficients[1], alpha=coefficients[2],
457 Rd=coefficients[3], O=coefficients[4], TPU_flag=int(coefficients[5]),
458 c_Vcmax=coefficients[6], dH_Vcmax=coefficients[7],
459 c_Jmax=coefficients[8], dH_Jmax=coefficients[9],
460 c_Rd=coefficients[10], dH_Rd=coefficients[11],
461 c_Kc=coefficients[12], dH_Kc=coefficients[13],
462 c_Ko=coefficients[14], dH_Ko=coefficients[15],
463 c_Gamma=coefficients[16], dH_Gamma=coefficients[17],
464 gm_at_25C=gm_at_25C, dHa_gm=dHa_gm, Topt_gm_C=Topt_gm_C, dHd_gm=dHd_gm,
465 )
466
467 if len(coefficients) >= 38:
468 for offset, setter in ((22, instance.setVcmax), (26, instance.setJmax),
469 (30, instance.setRd), (34, instance.setQuantumEfficiency_alpha)):
470 value, dha, topt, dhd = coefficients[offset:offset + 4]
471 if dha < 0:
472 setter(value)
473 elif topt < 0:
474 setter(value, dha)
475 elif dhd < 0:
476 setter(value, dha, topt)
477 else:
478 setter(value, dha, topt, dhd)
480 if len(coefficients) >= 42:
481 value, dha, topt, dhd = coefficients[38:42]
482 if dha < 0:
483 instance.setLightResponseCurvature_theta(value)
484 elif topt < 0:
485 instance.setLightResponseCurvature_theta(value, dha)
486 elif dhd < 0:
487 instance.setLightResponseCurvature_theta(value, dha, topt)
488 else:
489 instance.setLightResponseCurvature_theta(value, dha, topt, dhd)
490
491 return instance
492
493
494def validate_species_name(species: str) -> str:
495 """
496 Validate and normalize species name for photosynthesis library.
497
498 Args:
499 species: Species name (case insensitive, supports aliases)
500
501 Returns:
502 Normalized species name
503
504 Raises:
505 ValueError: If species is not recognized
506 """
507 if not species:
508 raise ValueError("Species name cannot be empty")
509
510 # Try exact match first (case sensitive)
511 if species in PHOTOSYNTHESIS_SPECIES:
512 return species
513
514 # Try case-insensitive match
515 species_lower = species.lower()
516 if species_lower in SPECIES_ALIASES:
517 return SPECIES_ALIASES[species_lower]
518
519 # Try case-insensitive match against known species
520 for known_species in PHOTOSYNTHESIS_SPECIES:
521 if known_species.lower() == species_lower:
522 return known_species
523
524 # Species not found - provide helpful error message
525 available_species = sorted(set(list(PHOTOSYNTHESIS_SPECIES) + list(SPECIES_ALIASES.keys())))
526 raise ValueError(
527 f"Unknown species '{species}'. Available species and aliases:\n"
528 f" {', '.join(available_species[:8])}\n"
529 f" {', '.join(available_species[8:16])}\n"
530 f" {', '.join(available_species[16:])}"
531 )
532
533
534def get_available_species() -> List[str]:
535 """Get list of available species in the photosynthesis library."""
536 return sorted(PHOTOSYNTHESIS_SPECIES.copy())
537
538
540 """Get dictionary of species aliases."""
541 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 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 the flat float array used by the native interface.
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).
__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.
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.
'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).
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).
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 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.