0.1.26
Loading...
Searching...
No Matches
BoundaryLayerConductance.py
Go to the documentation of this file.
1"""
2High-level BoundaryLayerConductanceModel interface for PyHelios.
3
4This module provides a user-friendly interface to the boundary layer conductance modeling
5capabilities with graceful plugin handling and informative error messages.
6"""
7
8import logging
9from typing import List, Optional
10
11from .plugins.registry import get_plugin_registry
12from .wrappers import UBoundaryLayerConductanceWrapper as bl_wrapper
13from .Context import Context, check_context_alive
14from .exceptions import HeliosError
15
16logger = logging.getLogger(__name__)
17
18
20 """Exception raised for BoundaryLayerConductanceModel-specific errors."""
21 pass
22
23
25 """
26 High-level interface for boundary layer conductance modeling and heat/mass transfer calculations.
27
28 This class provides a user-friendly wrapper around the native Helios
29 boundary layer conductance plugin with automatic plugin availability checking and
30 graceful error handling.
31
32 The boundary layer conductance model implements four different boundary-layer models:
33 - Pohlhausen: Laminar flat plate, forced convection (default)
34 - InclinedPlate: Mixed free-forced convection for inclined plates
35 - Sphere: Laminar flow around a sphere
36 - Ground: Flow over bare ground surface
37
38 System requirements:
39 - Cross-platform support (Windows, Linux, macOS)
40 - No GPU required
41 - No special dependencies
42 - Boundary layer conductance plugin compiled into PyHelios
43
44 Example:
45 >>> from pyhelios import Context, BoundaryLayerConductanceModel
46 >>>
47 >>> with Context() as context:
48 ... # Add leaf geometry
49 ... leaf_uuid = context.addPatch(center=[0, 0, 1], size=[0.1, 0.1])
50 ...
51 ... with BoundaryLayerConductanceModel(context) as bl_model:
52 ... # Set model for all primitives (default is Pohlhausen)
53 ... bl_model.setBoundaryLayerModel("InclinedPlate")
54 ...
55 ... # Run calculation
56 ... bl_model.run()
57 ...
58 ... # Or set different models for different primitives
59 ... bl_model.setBoundaryLayerModel("Sphere", uuids=[leaf_uuid])
60 ... bl_model.run(uuids=[leaf_uuid])
61 """
62
63 def __init__(self, context: Context):
64 """
65 Initialize BoundaryLayerConductanceModel with graceful plugin handling.
66
67 Args:
68 context: Helios Context instance
69
70 Raises:
71 TypeError: If context is not a Context instance
72 BoundaryLayerConductanceModelError: If boundary layer conductance plugin is not available
73 """
74 # Validate context type - use duck typing to handle import state issues during testing
75 if not (hasattr(context, '__class__') and
76 (isinstance(context, Context) or
77 context.__class__.__name__ == 'Context')):
78 raise TypeError(f"BoundaryLayerConductanceModel requires a Context instance, got {type(context).__name__}")
79
80 self.context = context
81 self.bl_model = None
82
83 # Check plugin availability using registry
84 registry = get_plugin_registry()
85
86 if not registry.is_plugin_available('boundarylayerconductance'):
87 # Get helpful information about the missing plugin
88 available_plugins = registry.get_available_plugins()
89
90 error_msg = (
91 "BoundaryLayerConductanceModel requires the 'boundarylayerconductance' plugin which is not available.\n\n"
92 "The boundary layer conductance plugin provides heat and mass transfer calculations using four validated models:\n"
93 "- Pohlhausen: Laminar flat plate, forced convection\n"
94 "- InclinedPlate: Mixed free-forced convection for inclined surfaces\n"
95 "- Sphere: Laminar flow around spherical objects\n"
96 "- Ground: Convective transfer over bare ground\n\n"
97 "Features:\n"
98 "- Cross-platform support (Windows, Linux, macOS)\n"
99 "- No GPU or special dependencies required\n"
100 "- Applicable to plant leaves, fruits, and soil surfaces\n\n"
101 "To enable boundary layer conductance modeling:\n"
102 "1. Build PyHelios with boundary layer conductance plugin:\n"
103 " build_scripts/build_helios --plugins boundarylayerconductance\n"
104 "2. Or build with multiple physics plugins:\n"
105 " build_scripts/build_helios --plugins boundarylayerconductance,energybalance,stomatalconductance\n"
106 f"\nCurrently available plugins: {available_plugins}"
107 )
108
109 # Suggest alternatives if available
110 alternatives = registry.suggest_alternatives('boundarylayerconductance')
111 if alternatives:
112 error_msg += f"\n\nAlternative plugins available: {alternatives}"
113 error_msg += "\nConsider using energybalance or stomatalconductance for related plant physiology modeling."
114
116
117 # Plugin is available - create boundary layer conductance model
118 try:
119 self.bl_model = bl_wrapper.createBoundaryLayerConductanceModel(context.getNativePtr())
120 if self.bl_model is None:
122 "Failed to create BoundaryLayerConductanceModel instance. "
123 "This may indicate a problem with the native library."
124 )
125 logger.info("BoundaryLayerConductanceModel created successfully")
126
127 except Exception as e:
128 raise BoundaryLayerConductanceModelError(f"Failed to initialize BoundaryLayerConductanceModel: {e}")
129
130 def _check_context_alive(self):
131 """Raise if the owning Context has been destroyed (see Context.check_context_alive)."""
132 check_context_alive(getattr(self, "context", None), "BoundaryLayerConductanceModel")
134 def __enter__(self):
135 """Context manager entry."""
136 return self
138 def __exit__(self, exc_type, exc_value, traceback):
139 """Context manager exit with proper cleanup."""
140 if self.bl_model is not None:
141 try:
142 bl_wrapper.destroyBoundaryLayerConductanceModel(self.bl_model)
143 logger.debug("BoundaryLayerConductanceModel destroyed successfully")
144 except Exception as e:
145 logger.warning(f"Error destroying BoundaryLayerConductanceModel: {e}")
146 finally:
147 self.bl_model = None
148
149 def __del__(self):
150 """Destructor to ensure C++ resources freed even without 'with' statement."""
151 if hasattr(self, 'bl_model') and self.bl_model is not None:
152 try:
153 bl_wrapper.destroyBoundaryLayerConductanceModel(self.bl_model)
154 self.bl_model = None
155 except Exception as e:
156 import warnings
157 warnings.warn(f"Error in BoundaryLayerConductanceModel.__del__: {e}")
158
159 def getNativePtr(self):
160 """Get the native pointer for advanced operations."""
161 return self.bl_model
163 def enableMessages(self) -> None:
164 """
165 Enable console output messages from the boundary layer conductance model.
166
167 Raises:
168 BoundaryLayerConductanceModelError: If operation fails
169 """
171 try:
172 bl_wrapper.enableMessages(self.bl_model)
173 except Exception as e:
174 raise BoundaryLayerConductanceModelError(f"Failed to enable messages: {e}")
175
176 def disableMessages(self) -> None:
177 """
178 Disable console output messages from the boundary layer conductance model.
179
180 Raises:
181 BoundaryLayerConductanceModelError: If operation fails
182 """
184 try:
185 bl_wrapper.disableMessages(self.bl_model)
186 except Exception as e:
187 raise BoundaryLayerConductanceModelError(f"Failed to disable messages: {e}")
188
189 def setBoundaryLayerModel(self, model_name: str, uuids: Optional[List[int]] = None) -> None:
190 """
191 Set the boundary layer conductance model to be used.
192
193 Four models are available:
194 - "Pohlhausen": Laminar flat plate, forced convection (default)
195 - "InclinedPlate": Mixed free-forced convection for inclined plates
196 - "Sphere": Laminar flow around a sphere
197 - "Ground": Flow over bare ground surface
198
199 Args:
200 model_name: Name of the boundary layer model to use.
201 Must be one of: "Pohlhausen", "InclinedPlate", "Sphere", "Ground"
202 uuids: Optional list of primitive UUIDs to apply the model to.
203 If None, applies to all primitives in the Context.
204
205 Raises:
206 ValueError: If model_name is not valid
207 BoundaryLayerConductanceModelError: If operation fails
208
209 Example:
210 >>> # Set Pohlhausen model for all primitives
211 >>> bl_model.setBoundaryLayerModel("Pohlhausen")
212
213 >>> # Set InclinedPlate model for specific leaves
214 >>> bl_model.setBoundaryLayerModel("InclinedPlate", uuids=[uuid1, uuid2])
215
216 >>> # Set Sphere model for fruit geometry
217 >>> bl_model.setBoundaryLayerModel("Sphere", uuids=[fruit_uuid])
218
219 >>> # Set Ground model for soil patches
220 >>> bl_model.setBoundaryLayerModel("Ground", uuids=[ground_uuids])
221 """
222 # Validate model name
223 valid_models = ["Pohlhausen", "InclinedPlate", "Sphere", "Ground"]
224 if model_name not in valid_models:
225 raise ValueError(
226 f"Invalid boundary layer model '{model_name}'. "
227 f"Must be one of: {', '.join(valid_models)}"
228 )
229
231 try:
232 if uuids is None:
233 # Apply to all primitives
234 bl_wrapper.setBoundaryLayerModel(self.bl_model, model_name)
235 elif len(uuids) == 1:
236 # Single UUID - use UUID-specific function
237 bl_wrapper.setBoundaryLayerModelForUUID(self.bl_model, uuids[0], model_name)
238 else:
239 # Multiple UUIDs
240 bl_wrapper.setBoundaryLayerModelForUUIDs(self.bl_model, uuids, model_name)
241
242 except Exception as e:
243 raise BoundaryLayerConductanceModelError(f"Failed to set boundary layer model: {e}")
244
245 def run(self, uuids: Optional[List[int]] = None) -> None:
246 """
247 Run the boundary layer conductance calculations.
248
249 Calculates boundary-layer conductance values and stores results as
250 primitive data "boundarylayer_conductance" (mol air/m²/s).
251
252 Args:
253 uuids: Optional list of primitive UUIDs to process.
254 If None, processes all primitives in the Context.
255
256 Raises:
257 BoundaryLayerConductanceModelError: If calculation fails
258
259 Example:
260 >>> # Calculate for all primitives
261 >>> bl_model.run()
262
263 >>> # Calculate for specific primitives
264 >>> bl_model.run(uuids=[leaf1_uuid, leaf2_uuid])
265 """
267 try:
268 if uuids is None:
269 # Run for all primitives
270 bl_wrapper.runBoundaryLayerModel(self.bl_model)
271 else:
272 # Run for specific UUIDs
273 bl_wrapper.runBoundaryLayerModelForUUIDs(self.bl_model, uuids)
274
275 except Exception as e:
276 raise BoundaryLayerConductanceModelError(f"Failed to run boundary layer conductance calculation: {e}")
277
278 @staticmethod
279 def is_available() -> bool:
280 """
281 Check if BoundaryLayerConductanceModel plugin is available in current build.
282
283 Returns:
284 True if plugin is available, False otherwise
285
286 Example:
287 >>> if BoundaryLayerConductanceModel.is_available():
288 ... print("Boundary layer conductance modeling is available!")
289 """
290 registry = get_plugin_registry()
291 return registry.is_plugin_available('boundarylayerconductance')
Exception raised for BoundaryLayerConductanceModel-specific errors.
High-level interface for boundary layer conductance modeling and heat/mass transfer calculations.
__del__(self)
Destructor to ensure C++ resources freed even without 'with' statement.
__exit__(self, exc_type, exc_value, traceback)
Context manager exit with proper cleanup.
getNativePtr(self)
Get the native pointer for advanced operations.
None disableMessages(self)
Disable console output messages from the boundary layer conductance model.
None run(self, Optional[List[int]] uuids=None)
Run the boundary layer conductance calculations.
bool is_available()
Check if BoundaryLayerConductanceModel plugin is available in current build.
None setBoundaryLayerModel(self, str model_name, Optional[List[int]] uuids=None)
Set the boundary layer conductance model to be used.
_check_context_alive(self)
Raise if the owning Context has been destroyed (see Context.check_context_alive).
__init__(self, Context context)
Initialize BoundaryLayerConductanceModel with graceful plugin handling.
None enableMessages(self)
Enable console output messages from the boundary layer conductance model.
Exception classes for PyHelios library.
Definition exceptions.py:10