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])
109 def __init__(self, context: Context):
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 """Destructor to ensure C++ resources freed even without 'with' statement."""
195 if hasattr(self,
'stomatal_model')
and self.
stomatal_model is not None:
197 stomatal_wrapper.destroyStomatalConductanceModel(self.
stomatal_model)
199 except Exception
as e:
201 warnings.warn(f
"Error in StomatalConductanceModel.__del__: {e}")
204 """Get the native pointer for advanced operations."""
209 Enable console output messages from the stomatal conductance model.
212 StomatalConductanceModelError: If operation fails
216 except Exception
as e:
221 Disable console output messages from the stomatal conductance model.
224 StomatalConductanceModelError: If operation fails
228 except Exception
as e:
231 def run(self, uuids: Optional[List[int]] =
None, dt: Optional[float] =
None) ->
None:
233 Run the stomatal conductance model.
235 This method supports multiple execution modes:
236 - Steady state for all primitives: run()
237 - Dynamic with timestep for all primitives: run(dt=60.0)
238 - Steady state for specific primitives: run(uuids=[1, 2, 3])
239 - Dynamic with timestep for specific primitives: run(uuids=[1, 2, 3], dt=60.0)
242 uuids: Optional list of primitive UUIDs to process. If None, processes all primitives.
243 dt: Optional timestep in seconds for dynamic simulation. If None, runs steady-state.
246 ValueError: If parameters are invalid
247 StomatalConductanceModelError: If calculation fails
250 >>> # Steady state for all primitives
253 >>> # Dynamic simulation with 60-second timestep
254 >>> stomatal.run(dt=60.0)
256 >>> # Steady state for specific leaves
257 >>> stomatal.run(uuids=[leaf1_uuid, leaf2_uuid])
259 >>> # Dynamic simulation for specific leaves
260 >>> stomatal.run(uuids=[leaf1_uuid, leaf2_uuid], dt=30.0)
263 if dt
is not None and uuids
is not None:
265 stomatal_wrapper.runForUUIDsDynamic(self.
stomatal_model, uuids, dt)
269 elif uuids
is not None:
276 except Exception
as e:
280 def setBWBCoefficients(self, coeffs: BWBCoefficients, uuids: Optional[List[int]] =
None) ->
None:
282 Set Ball-Woodrow-Berry model coefficients.
285 coeffs: BWB model coefficients (gs0, a1)
286 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
289 ValueError: If coefficients are invalid
290 StomatalConductanceModelError: If operation fails
293 >>> bwb_coeffs = BWBCoefficients(gs0=0.0733, a1=9.422)
294 >>> stomatal.setBWBCoefficients(bwb_coeffs)
296 if not isinstance(coeffs, BWBCoefficients):
297 raise ValueError(
"coeffs must be a BWBCoefficients instance")
299 raise ValueError(
"gs0 must be non-negative")
301 raise ValueError(
"a1 must be non-negative")
304 if uuids
is not None:
305 stomatal_wrapper.setBWBCoefficientsForUUIDs(self.
stomatal_model, coeffs.gs0, coeffs.a1, uuids)
307 stomatal_wrapper.setBWBCoefficients(self.
stomatal_model, coeffs.gs0, coeffs.a1)
308 except Exception
as e:
312 def setBBLCoefficients(self, coeffs: BBLCoefficients, uuids: Optional[List[int]] =
None) ->
None:
314 Set Ball-Berry-Leuning model coefficients.
317 coeffs: BBL model coefficients (gs0, a1, D0)
318 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
321 ValueError: If coefficients are invalid
322 StomatalConductanceModelError: If operation fails
325 >>> bbl_coeffs = BBLCoefficients(gs0=0.0743, a1=4.265, D0=14570.0)
326 >>> stomatal.setBBLCoefficients(bbl_coeffs)
328 if not isinstance(coeffs, BBLCoefficients):
329 raise ValueError(
"coeffs must be a BBLCoefficients instance")
331 raise ValueError(
"gs0 must be non-negative")
333 raise ValueError(
"a1 must be non-negative")
335 raise ValueError(
"D0 must be positive")
338 if uuids
is not None:
339 stomatal_wrapper.setBBLCoefficientsForUUIDs(self.
stomatal_model, coeffs.gs0, coeffs.a1, coeffs.D0, uuids)
341 stomatal_wrapper.setBBLCoefficients(self.
stomatal_model, coeffs.gs0, coeffs.a1, coeffs.D0)
342 except Exception
as e:
346 def setMOPTCoefficients(self, coeffs: MOPTCoefficients, uuids: Optional[List[int]] =
None) ->
None:
348 Set Medlyn et al. optimality model coefficients.
351 coeffs: MOPT model coefficients (gs0, g1)
352 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
355 ValueError: If coefficients are invalid
356 StomatalConductanceModelError: If operation fails
359 >>> mopt_coeffs = MOPTCoefficients(gs0=0.0825, g1=2.637)
360 >>> stomatal.setMOPTCoefficients(mopt_coeffs)
362 if not isinstance(coeffs, MOPTCoefficients):
363 raise ValueError(
"coeffs must be a MOPTCoefficients instance")
365 raise ValueError(
"gs0 must be non-negative")
367 raise ValueError(
"g1 must be positive")
370 if uuids
is not None:
371 stomatal_wrapper.setMOPTCoefficientsForUUIDs(self.
stomatal_model, coeffs.gs0, coeffs.g1, uuids)
373 stomatal_wrapper.setMOPTCoefficients(self.
stomatal_model, coeffs.gs0, coeffs.g1)
374 except Exception
as e:
378 def setBMFCoefficients(self, coeffs: BMFCoefficients, uuids: Optional[List[int]] =
None) ->
None:
380 Set Buckley-Mott-Farquhar model coefficients.
383 coeffs: BMF model coefficients (Em, i0, k, b)
384 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
387 ValueError: If coefficients are invalid
388 StomatalConductanceModelError: If operation fails
391 >>> bmf_coeffs = BMFCoefficients(Em=258.25, i0=38.65, k=232916.82, b=609.67)
392 >>> stomatal.setBMFCoefficients(bmf_coeffs)
394 if not isinstance(coeffs, BMFCoefficients):
395 raise ValueError(
"coeffs must be a BMFCoefficients instance")
397 raise ValueError(
"Em must be positive")
399 raise ValueError(
"i0 must be non-negative")
401 raise ValueError(
"k must be positive")
403 raise ValueError(
"b must be positive")
406 if uuids
is not None:
407 stomatal_wrapper.setBMFCoefficientsForUUIDs(self.
stomatal_model, coeffs.Em, coeffs.i0, coeffs.k, coeffs.b, uuids)
409 stomatal_wrapper.setBMFCoefficients(self.
stomatal_model, coeffs.Em, coeffs.i0, coeffs.k, coeffs.b)
410 except Exception
as e:
414 def setBBCoefficients(self, coeffs: BBCoefficients, uuids: Optional[List[int]] =
None) ->
None:
416 Set Bailey model coefficients.
419 coeffs: BB model coefficients (pi_0, pi_m, theta, sigma, chi)
420 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
423 ValueError: If coefficients are invalid
424 StomatalConductanceModelError: If operation fails
427 >>> bb_coeffs = BBCoefficients(pi_0=1.0, pi_m=1.67, theta=211.22, sigma=0.4408, chi=2.076)
428 >>> stomatal.setBBCoefficients(bb_coeffs)
430 if not isinstance(coeffs, BBCoefficients):
431 raise ValueError(
"coeffs must be a BBCoefficients instance")
432 if coeffs.pi_0 <= 0.0:
433 raise ValueError(
"pi_0 must be positive")
434 if coeffs.pi_m <= 0.0:
435 raise ValueError(
"pi_m must be positive")
436 if coeffs.theta <= 0.0:
437 raise ValueError(
"theta must be positive")
438 if coeffs.sigma <= 0.0:
439 raise ValueError(
"sigma must be positive")
440 if coeffs.chi <= 0.0:
441 raise ValueError(
"chi must be positive")
444 if uuids
is not None:
445 stomatal_wrapper.setBBCoefficientsForUUIDs(self.
stomatal_model, coeffs.pi_0, coeffs.pi_m, coeffs.theta, coeffs.sigma, coeffs.chi, uuids)
447 stomatal_wrapper.setBBCoefficients(self.
stomatal_model, coeffs.pi_0, coeffs.pi_m, coeffs.theta, coeffs.sigma, coeffs.chi)
448 except Exception
as e:
454 Set BMF model coefficients using the built-in species library.
457 species: Species name from the library (e.g., "Almond", "Apple", "Grape", "Walnut")
458 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
461 ValueError: If species name is invalid
462 StomatalConductanceModelError: If operation fails
465 >>> # Set coefficients for almond tree
466 >>> stomatal.setBMFCoefficientsFromLibrary("Almond")
468 >>> # Set coefficients for specific leaves only
469 >>> stomatal.setBMFCoefficientsFromLibrary("Grape", uuids=[leaf1_uuid, leaf2_uuid])
472 raise ValueError(
"Species name cannot be empty")
475 available_species = [
476 "Almond",
"Apple",
"Avocado",
"Cherry",
"Grape",
"Lemon",
477 "Olive",
"Orange",
"Peach",
"Pear",
"Plum",
"Walnut"
481 if uuids
is not None:
482 stomatal_wrapper.setBMFCoefficientsFromLibraryForUUIDs(self.
stomatal_model, species, uuids)
484 stomatal_wrapper.setBMFCoefficientsFromLibrary(self.
stomatal_model, species)
485 except Exception
as e:
486 error_msg = f
"Failed to set BMF coefficients from library for species '{species}': {e}"
487 if "species not found" in str(e).lower()
or "invalid species" in str(e).lower():
488 error_msg += f
"\nAvailable species: {', '.join(available_species)}"
492 def setDynamicTimeConstants(self, tau_open: float, tau_close: float, uuids: Optional[List[int]] =
None) ->
None:
494 Set time constants for dynamic stomatal opening and closing.
497 tau_open: Time constant (seconds) for stomatal opening
498 tau_close: Time constant (seconds) for stomatal closing
499 uuids: Optional list of primitive UUIDs. If None, applies to all primitives.
502 ValueError: If time constants are invalid
503 StomatalConductanceModelError: If operation fails
506 >>> # Set time constants for all leaves
507 >>> stomatal.setDynamicTimeConstants(tau_open=120.0, tau_close=240.0)
509 >>> # Set different time constants for specific leaves
510 >>> stomatal.setDynamicTimeConstants(tau_open=60.0, tau_close=180.0, uuids=[leaf1_uuid])
513 raise ValueError(
"Opening time constant must be positive")
515 raise ValueError(
"Closing time constant must be positive")
518 if uuids
is not None:
519 stomatal_wrapper.setDynamicTimeConstantsForUUIDs(self.
stomatal_model, tau_open, tau_close, uuids)
521 stomatal_wrapper.setDynamicTimeConstants(self.
stomatal_model, tau_open, tau_close)
522 except Exception
as e:
528 Add optional output primitive data to the Context.
531 label: Name of primitive data to output (e.g., "Ci", "gs", "E")
534 ValueError: If label is invalid
535 StomatalConductanceModelError: If operation fails
538 >>> # Output stomatal conductance values
539 >>> stomatal.optionalOutputPrimitiveData("gs")
541 >>> # Output intercellular CO2 concentration
542 >>> stomatal.optionalOutputPrimitiveData("Ci")
545 raise ValueError(
"Label cannot be empty")
548 stomatal_wrapper.optionalOutputPrimitiveData(self.
stomatal_model, label)
549 except Exception
as e:
554 Print a report detailing usage of default input values.
557 uuids: Optional list of primitive UUIDs. If None, reports on all primitives.
560 StomatalConductanceModelError: If operation fails
563 >>> # Print report for all primitives
564 >>> stomatal.printDefaultValueReport()
566 >>> # Print report for specific leaves
567 >>> stomatal.printDefaultValueReport(uuids=[leaf1_uuid, leaf2_uuid])
570 if uuids
is not None:
571 stomatal_wrapper.printDefaultValueReportForUUIDs(self.
stomatal_model, uuids)
574 except Exception
as e:
579 Check if StomatalConductanceModel is available in current build.
582 True if plugin is available, False otherwise
584 registry = get_plugin_registry()
585 return registry.is_plugin_available(
'stomatalconductance')
591 Create StomatalConductanceModel instance with context.
594 context: Helios Context
597 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.
__del__(self)
Destructor to ensure C++ resources freed even without 'with' statement.
__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.