PyHelios 0.1.11
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
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 __enter__(self):
131 """Context manager entry."""
132 return self
133
134 def __exit__(self, exc_type, exc_value, traceback):
135 """Context manager exit with proper cleanup."""
136 if self.bl_model is not None:
137 try:
138 bl_wrapper.destroyBoundaryLayerConductanceModel(self.bl_model)
139 logger.debug("BoundaryLayerConductanceModel destroyed successfully")
140 except Exception as e:
141 logger.warning(f"Error destroying BoundaryLayerConductanceModel: {e}")
142 finally:
143 self.bl_model = None
144
145 def __del__(self):
146 """Destructor to ensure C++ resources freed even without 'with' statement."""
147 if hasattr(self, 'bl_model') and self.bl_model is not None:
148 try:
149 bl_wrapper.destroyBoundaryLayerConductanceModel(self.bl_model)
150 self.bl_model = None
151 except Exception as e:
152 import warnings
153 warnings.warn(f"Error in BoundaryLayerConductanceModel.__del__: {e}")
154
155 def getNativePtr(self):
156 """Get the native pointer for advanced operations."""
157 return self.bl_model
158
159 def enableMessages(self) -> None:
160 """
161 Enable console output messages from the boundary layer conductance model.
162
163 Raises:
164 BoundaryLayerConductanceModelError: If operation fails
165 """
166 try:
167 bl_wrapper.enableMessages(self.bl_model)
168 except Exception as e:
169 raise BoundaryLayerConductanceModelError(f"Failed to enable messages: {e}")
170
171 def disableMessages(self) -> None:
172 """
173 Disable console output messages from the boundary layer conductance model.
174
175 Raises:
176 BoundaryLayerConductanceModelError: If operation fails
177 """
178 try:
179 bl_wrapper.disableMessages(self.bl_model)
180 except Exception as e:
181 raise BoundaryLayerConductanceModelError(f"Failed to disable messages: {e}")
182
183 def setBoundaryLayerModel(self, model_name: str, uuids: Optional[List[int]] = None) -> None:
184 """
185 Set the boundary layer conductance model to be used.
186
187 Four models are available:
188 - "Pohlhausen": Laminar flat plate, forced convection (default)
189 - "InclinedPlate": Mixed free-forced convection for inclined plates
190 - "Sphere": Laminar flow around a sphere
191 - "Ground": Flow over bare ground surface
192
193 Args:
194 model_name: Name of the boundary layer model to use.
195 Must be one of: "Pohlhausen", "InclinedPlate", "Sphere", "Ground"
196 uuids: Optional list of primitive UUIDs to apply the model to.
197 If None, applies to all primitives in the Context.
198
199 Raises:
200 ValueError: If model_name is not valid
201 BoundaryLayerConductanceModelError: If operation fails
202
203 Example:
204 >>> # Set Pohlhausen model for all primitives
205 >>> bl_model.setBoundaryLayerModel("Pohlhausen")
206
207 >>> # Set InclinedPlate model for specific leaves
208 >>> bl_model.setBoundaryLayerModel("InclinedPlate", uuids=[uuid1, uuid2])
209
210 >>> # Set Sphere model for fruit geometry
211 >>> bl_model.setBoundaryLayerModel("Sphere", uuids=[fruit_uuid])
212
213 >>> # Set Ground model for soil patches
214 >>> bl_model.setBoundaryLayerModel("Ground", uuids=[ground_uuids])
215 """
216 # Validate model name
217 valid_models = ["Pohlhausen", "InclinedPlate", "Sphere", "Ground"]
218 if model_name not in valid_models:
219 raise ValueError(
220 f"Invalid boundary layer model '{model_name}'. "
221 f"Must be one of: {', '.join(valid_models)}"
222 )
223
224 try:
225 if uuids is None:
226 # Apply to all primitives
227 bl_wrapper.setBoundaryLayerModel(self.bl_model, model_name)
228 elif len(uuids) == 1:
229 # Single UUID - use UUID-specific function
230 bl_wrapper.setBoundaryLayerModelForUUID(self.bl_model, uuids[0], model_name)
231 else:
232 # Multiple UUIDs
233 bl_wrapper.setBoundaryLayerModelForUUIDs(self.bl_model, uuids, model_name)
234
235 except Exception as e:
236 raise BoundaryLayerConductanceModelError(f"Failed to set boundary layer model: {e}")
237
238 def run(self, uuids: Optional[List[int]] = None) -> None:
239 """
240 Run the boundary layer conductance calculations.
241
242 Calculates boundary-layer conductance values and stores results as
243 primitive data "boundarylayer_conductance" (mol air/m²/s).
244
245 Args:
246 uuids: Optional list of primitive UUIDs to process.
247 If None, processes all primitives in the Context.
248
249 Raises:
250 BoundaryLayerConductanceModelError: If calculation fails
251
252 Example:
253 >>> # Calculate for all primitives
254 >>> bl_model.run()
255
256 >>> # Calculate for specific primitives
257 >>> bl_model.run(uuids=[leaf1_uuid, leaf2_uuid])
258 """
259 try:
260 if uuids is None:
261 # Run for all primitives
262 bl_wrapper.runBoundaryLayerModel(self.bl_model)
263 else:
264 # Run for specific UUIDs
265 bl_wrapper.runBoundaryLayerModelForUUIDs(self.bl_model, uuids)
266
267 except Exception as e:
268 raise BoundaryLayerConductanceModelError(f"Failed to run boundary layer conductance calculation: {e}")
269
270 @staticmethod
271 def is_available() -> bool:
272 """
273 Check if BoundaryLayerConductanceModel plugin is available in current build.
274
275 Returns:
276 True if plugin is available, False otherwise
277
278 Example:
279 >>> if BoundaryLayerConductanceModel.is_available():
280 ... print("Boundary layer conductance modeling is available!")
281 """
282 registry = get_plugin_registry()
283 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.
__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