2High-level StomatalConductance interface for PyHelios.
4This module provides a user-friendly interface to the stomatal conductance modeling
5capabilities with graceful plugin handling and informative error messages.
9from typing
import List, Optional, Union, NamedTuple
10from contextlib
import contextmanager
12from .plugins.registry
import get_plugin_registry
13from .wrappers
import UStomatalConductanceWrapper
as stomatal_wrapper
14from .Context
import Context
15from .exceptions
import HeliosError
17logger = logging.getLogger(__name__)
21 """Exception raised for StomatalConductance-specific errors."""
27 """Ball-Woodrow-Berry model coefficients."""
33 """Ball-Berry-Leuning model coefficients."""
40 """Medlyn et al. optimality model coefficients."""
46 """Buckley-Mott-Farquhar model coefficients."""
54 """Bailey model coefficients."""
64 High-level interface for stomatal conductance modeling and gas exchange calculations.
66 This class provides a user-friendly wrapper around the native Helios
67 stomatal conductance plugin with automatic plugin availability checking and
68 graceful error handling.
70 The stomatal conductance model implements five different stomatal response models:
71 - BWB: Ball, Woodrow, Berry (1987) - original model
72 - BBL: Ball, Berry, Leuning (1990, 1995) - includes VPD response
73 - MOPT: Medlyn et al. (2011) - optimality-based model
74 - BMF: Buckley, Mott, Farquhar - simplified mechanistic model
75 - BB: Bailey - hydraulic-based model
77 The plugin includes a species library with pre-calibrated coefficients for
78 common plant species (Almond, Apple, Avocado, Grape, Lemon, Olive, Walnut, etc.).
80 Both steady-state and dynamic (time-stepping) calculations are supported,
81 with configurable time constants for stomatal opening and closing dynamics.
84 - Cross-platform support (Windows, Linux, macOS)
86 - No special dependencies
87 - Stomatal conductance plugin compiled into PyHelios
90 >>> with Context() as context:
91 ... # Add leaf geometry
92 ... leaf_uuid = context.addPatch(center=[0, 0, 1], size=[0.1, 0.1])
94 ... with StomatalConductanceModel(context) as stomatal:
95 ... # Set model coefficients using species library
96 ... stomatal.setBMFCoefficientsFromLibrary("Almond")
98 ... # Run steady-state calculation
101 ... # Or run dynamic simulation with timestep
102 ... stomatal.run(dt=60.0) # 60 second timestep
104 ... # Set custom BMF coefficients for specific leaves
105 ... bmf_coeffs = BMFCoefficients(Em=258.25, i0=38.65, k=232916.82, b=609.67)
106 ... stomatal.setBMFCoefficients(bmf_coeffs, uuids=[leaf_uuid])
111 Initialize StomatalConductanceModel with graceful plugin handling.
114 context: Helios Context instance
117 TypeError: If context is not a Context instance
118 StomatalConductanceModelError: If stomatal conductance plugin is not available
121 if not (hasattr(context,
'__class__')
and
122 (isinstance(context, Context)
or
123 context.__class__.__name__ ==
'Context')):
124 raise TypeError(f
"StomatalConductanceModel requires a Context instance, got {type(context).__name__}")
130 registry = get_plugin_registry()
132 if not registry.is_plugin_available(
'stomatalconductance'):
134 plugin_info = registry.get_plugin_capabilities()
135 available_plugins = registry.get_available_plugins()
138 "StomatalConductanceModel requires the 'stomatalconductance' plugin which is not available.\n\n"
139 "The stomatal conductance plugin provides gas exchange calculations using five validated models:\n"
140 "- Ball-Woodrow-Berry (BWB) - classic stomatal response\n"
141 "- Ball-Berry-Leuning (BBL) - includes vapor pressure deficit\n"
142 "- Medlyn et al. optimality (MOPT) - optimal stomatal behavior\n"
143 "- Buckley-Mott-Farquhar (BMF) - mechanistic approach\n"
144 "- Bailey (BB) - hydraulic-based model\n\n"
146 "- Species library with pre-calibrated coefficients\n"
147 "- Dynamic time-stepping with configurable time constants\n"
148 "- No GPU or special dependencies required\n\n"
149 "To enable stomatal conductance modeling:\n"
150 "1. Build PyHelios with stomatal conductance plugin:\n"
151 " build_scripts/build_helios --plugins stomatalconductance\n"
152 "2. Or build with multiple plugins:\n"
153 " build_scripts/build_helios --plugins stomatalconductance,energybalance,photosynthesis\n"
154 f
"\nCurrently available plugins: {available_plugins}"
158 alternatives = registry.suggest_alternatives(
'stomatalconductance')
160 error_msg += f
"\n\nAlternative plugins available: {alternatives}"
161 error_msg +=
"\nConsider using photosynthesis or energybalance for related plant physiology modeling."
167 self.
stomatal_model = stomatal_wrapper.createStomatalConductanceModel(context.getNativePtr())
170 "Failed to create StomatalConductanceModel instance. "
171 "This may indicate a problem with the native library."
173 logger.info(
"StomatalConductanceModel created successfully")
175 except Exception
as e:
179 """Context manager entry."""
182 def __exit__(self, exc_type, exc_value, traceback):
183 """Context manager exit with proper cleanup."""
186 stomatal_wrapper.destroyStomatalConductanceModel(self.
stomatal_model)
187 logger.debug(
"StomatalConductanceModel destroyed successfully")
188 except Exception
as e:
189 logger.warning(f
"Error destroying StomatalConductanceModel: {e}")
194 """Get the native pointer for advanced operations."""
199 Enable console output messages from the stomatal conductance model.
202 StomatalConductanceModelError: If operation fails
206 except Exception
as e:
211 Disable console output messages from the stomatal conductance model.
214 StomatalConductanceModelError: If operation fails
218 except Exception
as e:
221 def run(self, uuids: Optional[List[int]] =
None, dt: Optional[float] =
None) ->
None:
223 Run the stomatal conductance model.
225 This method supports multiple execution modes:
226 - Steady state for all primitives: run()
227 - Dynamic with timestep for all primitives: run(dt=60.0)
228 - Steady state for specific primitives: run(uuids=[1, 2, 3])
229 - Dynamic with timestep for specific primitives: run(uuids=[1, 2, 3], dt=60.0)
232 uuids: Optional list of primitive UUIDs to process. If None, processes all primitives.
233 dt: Optional timestep in seconds for dynamic simulation. If None, runs steady-state.
236 ValueError: If parameters are invalid
237 StomatalConductanceModelError: If calculation fails
240 >>> # Steady state for all primitives
243 >>> # Dynamic simulation with 60-second timestep
244 >>> stomatal.run(dt=60.0)
246 >>> # Steady state for specific leaves
247 >>> stomatal.run(uuids=[leaf1_uuid, leaf2_uuid])
249 >>> # Dynamic simulation for specific leaves
250 >>> stomatal.run(uuids=[leaf1_uuid, leaf2_uuid], dt=30.0)
253 if dt
is not None and uuids
is not None:
259 elif uuids
is not None:
266 except Exception
as e:
270 def setBWBCoefficients(self, coeffs: BWBCoefficients, uuids: Optional[List[int]] =
None) ->
None:
272 Set Ball-Woodrow-Berry model coefficients.
275 coeffs: BWB model coefficients (gs0, a1)
276 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
279 ValueError: If coefficients are invalid
280 StomatalConductanceModelError: If operation fails
283 >>> bwb_coeffs = BWBCoefficients(gs0=0.0733, a1=9.422)
284 >>> stomatal.setBWBCoefficients(bwb_coeffs)
286 if not isinstance(coeffs, BWBCoefficients):
287 raise ValueError(
"coeffs must be a BWBCoefficients instance")
289 raise ValueError(
"gs0 must be non-negative")
291 raise ValueError(
"a1 must be non-negative")
294 if uuids
is not None:
295 stomatal_wrapper.setBWBCoefficientsForUUIDs(self.
stomatal_model, coeffs.gs0, coeffs.a1, uuids)
297 stomatal_wrapper.setBWBCoefficients(self.
stomatal_model, coeffs.gs0, coeffs.a1)
298 except Exception
as e:
302 def setBBLCoefficients(self, coeffs: BBLCoefficients, uuids: Optional[List[int]] =
None) ->
None:
304 Set Ball-Berry-Leuning model coefficients.
307 coeffs: BBL model coefficients (gs0, a1, D0)
308 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
311 ValueError: If coefficients are invalid
312 StomatalConductanceModelError: If operation fails
315 >>> bbl_coeffs = BBLCoefficients(gs0=0.0743, a1=4.265, D0=14570.0)
316 >>> stomatal.setBBLCoefficients(bbl_coeffs)
318 if not isinstance(coeffs, BBLCoefficients):
319 raise ValueError(
"coeffs must be a BBLCoefficients instance")
321 raise ValueError(
"gs0 must be non-negative")
323 raise ValueError(
"a1 must be non-negative")
325 raise ValueError(
"D0 must be positive")
328 if uuids
is not None:
329 stomatal_wrapper.setBBLCoefficientsForUUIDs(self.
stomatal_model, coeffs.gs0, coeffs.a1, coeffs.D0, uuids)
331 stomatal_wrapper.setBBLCoefficients(self.
stomatal_model, coeffs.gs0, coeffs.a1, coeffs.D0)
332 except Exception
as e:
336 def setMOPTCoefficients(self, coeffs: MOPTCoefficients, uuids: Optional[List[int]] =
None) ->
None:
338 Set Medlyn et al. optimality model coefficients.
341 coeffs: MOPT model coefficients (gs0, g1)
342 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
345 ValueError: If coefficients are invalid
346 StomatalConductanceModelError: If operation fails
349 >>> mopt_coeffs = MOPTCoefficients(gs0=0.0825, g1=2.637)
350 >>> stomatal.setMOPTCoefficients(mopt_coeffs)
352 if not isinstance(coeffs, MOPTCoefficients):
353 raise ValueError(
"coeffs must be a MOPTCoefficients instance")
355 raise ValueError(
"gs0 must be non-negative")
357 raise ValueError(
"g1 must be positive")
360 if uuids
is not None:
361 stomatal_wrapper.setMOPTCoefficientsForUUIDs(self.
stomatal_model, coeffs.gs0, coeffs.g1, uuids)
363 stomatal_wrapper.setMOPTCoefficients(self.
stomatal_model, coeffs.gs0, coeffs.g1)
364 except Exception
as e:
368 def setBMFCoefficients(self, coeffs: BMFCoefficients, uuids: Optional[List[int]] =
None) ->
None:
370 Set Buckley-Mott-Farquhar model coefficients.
373 coeffs: BMF model coefficients (Em, i0, k, b)
374 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
377 ValueError: If coefficients are invalid
378 StomatalConductanceModelError: If operation fails
381 >>> bmf_coeffs = BMFCoefficients(Em=258.25, i0=38.65, k=232916.82, b=609.67)
382 >>> stomatal.setBMFCoefficients(bmf_coeffs)
384 if not isinstance(coeffs, BMFCoefficients):
385 raise ValueError(
"coeffs must be a BMFCoefficients instance")
387 raise ValueError(
"Em must be positive")
389 raise ValueError(
"i0 must be non-negative")
391 raise ValueError(
"k must be positive")
393 raise ValueError(
"b must be positive")
396 if uuids
is not None:
397 stomatal_wrapper.setBMFCoefficientsForUUIDs(self.
stomatal_model, coeffs.Em, coeffs.i0, coeffs.k, coeffs.b, uuids)
399 stomatal_wrapper.setBMFCoefficients(self.
stomatal_model, coeffs.Em, coeffs.i0, coeffs.k, coeffs.b)
400 except Exception
as e:
404 def setBBCoefficients(self, coeffs: BBCoefficients, uuids: Optional[List[int]] =
None) ->
None:
406 Set Bailey model coefficients.
409 coeffs: BB model coefficients (pi_0, pi_m, theta, sigma, chi)
410 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
413 ValueError: If coefficients are invalid
414 StomatalConductanceModelError: If operation fails
417 >>> bb_coeffs = BBCoefficients(pi_0=1.0, pi_m=1.67, theta=211.22, sigma=0.4408, chi=2.076)
418 >>> stomatal.setBBCoefficients(bb_coeffs)
420 if not isinstance(coeffs, BBCoefficients):
421 raise ValueError(
"coeffs must be a BBCoefficients instance")
422 if coeffs.pi_0 <= 0.0:
423 raise ValueError(
"pi_0 must be positive")
424 if coeffs.pi_m <= 0.0:
425 raise ValueError(
"pi_m must be positive")
426 if coeffs.theta <= 0.0:
427 raise ValueError(
"theta must be positive")
428 if coeffs.sigma <= 0.0:
429 raise ValueError(
"sigma must be positive")
430 if coeffs.chi <= 0.0:
431 raise ValueError(
"chi must be positive")
434 if uuids
is not None:
435 stomatal_wrapper.setBBCoefficientsForUUIDs(self.
stomatal_model, coeffs.pi_0, coeffs.pi_m, coeffs.theta, coeffs.sigma, coeffs.chi, uuids)
437 stomatal_wrapper.setBBCoefficients(self.
stomatal_model, coeffs.pi_0, coeffs.pi_m, coeffs.theta, coeffs.sigma, coeffs.chi)
438 except Exception
as e:
444 Set BMF model coefficients using the built-in species library.
447 species: Species name from the library (e.g., "Almond", "Apple", "Grape", "Walnut")
448 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
451 ValueError: If species name is invalid
452 StomatalConductanceModelError: If operation fails
455 >>> # Set coefficients for almond tree
456 >>> stomatal.setBMFCoefficientsFromLibrary("Almond")
458 >>> # Set coefficients for specific leaves only
459 >>> stomatal.setBMFCoefficientsFromLibrary("Grape", uuids=[leaf1_uuid, leaf2_uuid])
462 raise ValueError(
"Species name cannot be empty")
465 available_species = [
466 "Almond",
"Apple",
"Avocado",
"Cherry",
"Grape",
"Lemon",
467 "Olive",
"Orange",
"Peach",
"Pear",
"Plum",
"Walnut"
471 if uuids
is not None:
472 stomatal_wrapper.setBMFCoefficientsFromLibraryForUUIDs(self.
stomatal_model, species, uuids)
474 stomatal_wrapper.setBMFCoefficientsFromLibrary(self.
stomatal_model, species)
475 except Exception
as e:
476 error_msg = f
"Failed to set BMF coefficients from library for species '{species}': {e}"
477 if "species not found" in str(e).lower()
or "invalid species" in str(e).lower():
478 error_msg += f
"\nAvailable species: {', '.join(available_species)}"
482 def setDynamicTimeConstants(self, tau_open: float, tau_close: float, uuids: Optional[List[int]] =
None) ->
None:
484 Set time constants for dynamic stomatal opening and closing.
487 tau_open: Time constant (seconds) for stomatal opening
488 tau_close: Time constant (seconds) for stomatal closing
489 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
492 ValueError: If time constants are invalid
493 StomatalConductanceModelError: If operation fails
496 >>> # Set time constants for all leaves
497 >>> stomatal.setDynamicTimeConstants(tau_open=120.0, tau_close=240.0)
499 >>> # Set different time constants for specific leaves
500 >>> stomatal.setDynamicTimeConstants(tau_open=60.0, tau_close=180.0, uuids=[leaf1_uuid])
503 raise ValueError(
"Opening time constant must be positive")
505 raise ValueError(
"Closing time constant must be positive")
508 if uuids
is not None:
509 stomatal_wrapper.setDynamicTimeConstantsForUUIDs(self.
stomatal_model, tau_open, tau_close, uuids)
511 stomatal_wrapper.setDynamicTimeConstants(self.
stomatal_model, tau_open, tau_close)
512 except Exception
as e:
518 Add optional output primitive data to the Context.
521 label: Name of primitive data to output (e.g., "Ci", "gs", "E")
524 ValueError: If label is invalid
525 StomatalConductanceModelError: If operation fails
528 >>> # Output stomatal conductance values
529 >>> stomatal.optionalOutputPrimitiveData("gs")
531 >>> # Output intercellular CO2 concentration
532 >>> stomatal.optionalOutputPrimitiveData("Ci")
535 raise ValueError(
"Label cannot be empty")
538 stomatal_wrapper.optionalOutputPrimitiveData(self.
stomatal_model, label)
539 except Exception
as e:
544 Print a report detailing usage of default input values.
547 uuids: Optional list of primitive UUIDs. If None, reports on all primitives.
550 StomatalConductanceModelError: If operation fails
553 >>> # Print report for all primitives
554 >>> stomatal.printDefaultValueReport()
556 >>> # Print report for specific leaves
557 >>> stomatal.printDefaultValueReport(uuids=[leaf1_uuid, leaf2_uuid])
560 if uuids
is not None:
561 stomatal_wrapper.printDefaultValueReportForUUIDs(self.
stomatal_model, uuids)
564 except Exception
as e:
569 Check if StomatalConductanceModel is available in current build.
572 True if plugin is available, False otherwise
574 registry = get_plugin_registry()
575 return registry.is_plugin_available(
'stomatalconductance')
581 Create StomatalConductanceModel instance with context.
584 context: Helios Context
587 StomatalConductanceModel instance
Bailey model coefficients.
Ball-Berry-Leuning model coefficients.
Buckley-Mott-Farquhar model coefficients.
Ball-Woodrow-Berry model coefficients.
Exception raised for StomatalConductance-specific errors.
High-level interface for stomatal conductance modeling and gas exchange calculations.
bool is_available(self)
Check if StomatalConductanceModel is available in current build.
__enter__(self)
Context manager entry.
None setBMFCoefficients(self, BMFCoefficients coeffs, Optional[List[int]] uuids=None)
Set Buckley-Mott-Farquhar model coefficients.
None setMOPTCoefficients(self, MOPTCoefficients coeffs, Optional[List[int]] uuids=None)
Set Medlyn et al.
None printDefaultValueReport(self, Optional[List[int]] uuids=None)
Print a report detailing usage of default input values.
None setDynamicTimeConstants(self, float tau_open, float tau_close, Optional[List[int]] uuids=None)
Set time constants for dynamic stomatal opening and closing.
None enableMessages(self)
Enable console output messages from the stomatal conductance model.
None setBBLCoefficients(self, BBLCoefficients coeffs, Optional[List[int]] uuids=None)
Set Ball-Berry-Leuning model coefficients.
__init__(self, Context context)
Initialize StomatalConductanceModel with graceful plugin handling.
None setBWBCoefficients(self, BWBCoefficients coeffs, Optional[List[int]] uuids=None)
Set Ball-Woodrow-Berry model coefficients.
__exit__(self, exc_type, exc_value, traceback)
Context manager exit with proper cleanup.
getNativePtr(self)
Get the native pointer for advanced operations.
None run(self, Optional[List[int]] uuids=None, Optional[float] dt=None)
Run the stomatal conductance model.
None optionalOutputPrimitiveData(self, str label)
Add optional output primitive data to the Context.
None setBMFCoefficientsFromLibrary(self, str species, Optional[List[int]] uuids=None)
Set BMF model coefficients using the built-in species library.
None disableMessages(self)
Disable console output messages from the stomatal conductance model.
None setBBCoefficients(self, BBCoefficients coeffs, Optional[List[int]] uuids=None)
Set Bailey model coefficients.
Exception classes for PyHelios library.
StomatalConductanceModel create_stomatal_conductance_model(Context context)
Create StomatalConductanceModel instance with context.