Skip to content

Commit 0c1934a

Browse files
committed
[v0.1.13] 2025-12-25
- Updated helios-core to v1.3.61 ## Energy Balance - **CUDA is now optional**: Plugin uses three-tier execution - GPU (CUDA), OpenMP (parallel CPU), or serial CPU fallback - Added GPU acceleration control methods: `enableGPUAcceleration()`, `disableGPUAcceleration()`, `isGPUAccelerationEnabled()`, and `isGPUAccelerationAvailable()` - OpenMP CPU mode is recommended for most workloads without GPU ## Plant Architecture - Added capability to modify parameters for library plants or build custom plants
1 parent 7d17cfd commit 0c1934a

18 files changed

+2274
-89
lines changed

README.md

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,14 @@ pip install pyhelios3d
3131
```
3232

3333
This installs pre-built PyHelios with platform-appropriate plugins:
34-
- **macOS (Apple Silicon)**: All plugins except GPU-accelerated ones (automatically detected)
34+
- **macOS (Apple Silicon)**: All plugins including energybalance in CPU mode (radiation requires CUDA)
3535
- **macOS (Intel)**: Pre-built wheels not available - please [build from source](#build-from-source)
36-
- **Windows/Linux**: All plugins including GPU acceleration (when hardware supports it)
36+
- **Windows/Linux**: All plugins with optional GPU acceleration (automatic CPU fallback)
3737

38-
PyHelios will gracefully handle GPU features - if you don't have CUDA-capable hardware, GPU plugins will display helpful error messages with setup instructions.
38+
PyHelios automatically selects the best execution mode:
39+
- **Plugins with GPU-only modes** (radiation): Require CUDA-capable GPU
40+
- **Plugins with CPU/GPU modes** (energybalance): Work on all platforms, GPU acceleration optional
41+
- **CPU-only plugins**: Work on all platforms without special hardware
3942

4043
> **Note for Intel Mac Users**: Due to GitHub Actions infrastructure limitations, pre-built wheels are only available for Apple Silicon Macs. Intel Mac users must build PyHelios from source following the [macOS build instructions](#macos) below.
4144
@@ -103,11 +106,22 @@ source helios-core/utilities/dependencies.sh
103106
pip install -e .
104107
```
105108

106-
### GPU Features Setup
109+
### GPU Features Setup (Optional)
107110

108-
If you want to use GPU-accelerated features (radiation modeling, aerial LiDAR), ensure you have:
111+
PyHelios plugins have three types of GPU support:
109112

110-
**Requirements:**
113+
**GPU-Required Plugins** (need CUDA):
114+
- **Radiation Model**: OptiX-powered ray tracing for light simulation
115+
- **Aerial LiDAR**: GPU-accelerated LiDAR simulation
116+
117+
**GPU-Optional Plugins** (work with or without CUDA):
118+
- **Energy Balance** *(v1.3.61+)*: Automatic mode selection - GPU (CUDA) → OpenMP (parallel CPU) → Serial CPU
119+
- CPU mode recommended for most workloads without GPU
120+
121+
**CPU-Only Plugins** (no GPU needed):
122+
- All other plugins (PlantArchitecture, Photosynthesis, SolarPosition, etc.)
123+
124+
**For GPU Acceleration** (optional), ensure you have:
111125
- NVIDIA GPU with CUDA support
112126
- NVIDIA drivers installed
113127
- CUDA Toolkit (version 11.8 or 12.x)
@@ -120,18 +134,25 @@ nvcc --version # Should show CUDA compiler version
120134

121135
**Testing GPU Features:**
122136
```python
123-
from pyhelios import Context, RadiationModel
137+
from pyhelios import Context, RadiationModel, EnergyBalanceModel
124138

125139
context = Context()
140+
141+
# Test GPU-required plugin (radiation)
126142
try:
127143
radiation = RadiationModel(context)
128144
print("GPU radiation modeling available!")
129145
except RuntimeError as e:
130-
print(f"GPU features unavailable: {e}")
146+
print(f"Radiation requires GPU: {e}")
147+
148+
# Test GPU-optional plugin (energybalance)
149+
with EnergyBalanceModel(context) as energy:
150+
if energy.isGPUAccelerationEnabled():
151+
print("EnergyBalance using GPU acceleration")
152+
else:
153+
print("EnergyBalance using CPU mode (OpenMP or serial)")
131154
```
132155

133-
If GPU features fail, PyHelios will provide specific guidance on installation and setup requirements.
134-
135156
### First Example
136157

137158
```python
@@ -162,11 +183,9 @@ print(f"Created patch: {patch_uuid}")
162183
## Key Features
163184

164185
- **Cross-platform**: Windows, macOS, and Linux support
165-
- **Plant modeling**: WeberPennTree procedural generation
186+
- **Plant modeling**: 20+ plant species models in the plant architecture plug-in
166187
- **GPU acceleration**: OptiX-powered radiation simulation
167188
- **3D visualization**: OpenGL-based real-time rendering
168-
- **Flexible plugins**: Currently 5 plug-ins implemented
169-
- **Development mode**: Mock mode for development without native libraries
170189

171190
## Updating PyHelios
172191

docs/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
# [v0.1.13] 2025-12-25
4+
5+
- Updated helios-core to v1.3.61
6+
7+
## Energy Balance
8+
- **CUDA is now optional**: Plugin uses three-tier execution - GPU (CUDA), OpenMP (parallel CPU), or serial CPU fallback
9+
- Added GPU acceleration control methods: `enableGPUAcceleration()`, `disableGPUAcceleration()`, `isGPUAccelerationEnabled()`, and `isGPUAccelerationAvailable()`
10+
- OpenMP CPU mode is recommended for most workloads without GPU
11+
12+
## Plant Architecture
13+
- Added capability to modify parameters for library plants or build custom plants
14+
315
# [v0.1.12] 2025-12-15
416

517
- Many updates to documentation, transitioning toward consistency with c++ docs

docs/examples/stanford_bunny_energy_balance.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
temperatures, and visualizes the temperature distribution using the native PyHelios visualizer.
99
1010
HARDWARE REQUIREMENTS:
11-
- NVIDIA GPU with CUDA compute capability (for radiation simulation and energy balance)
12-
- CUDA Toolkit installed and configured
11+
- NVIDIA GPU with CUDA compute capability (for radiation simulation GPU acceleration)
12+
- CUDA Toolkit installed and configured (for radiation)
1313
- OptiX ray tracing SDK installed (for radiation)
1414
- OpenGL-compatible graphics system (for visualization)
1515
@@ -18,7 +18,9 @@
1818
- Stanford Bunny PLY file (included in helios-core)
1919
2020
NOTE: This example demonstrates the full PyHelios workflow including native 3D visualization.
21-
Both radiation and energy balance plugins require GPU acceleration for full functionality.
21+
- Radiation plugin REQUIRES GPU (CUDA + OptiX)
22+
- EnergyBalance plugin works on CPU or GPU (automatic fallback, GPU optional as of v1.3.61+)
23+
- Visualizer plugin works on all platforms with OpenGL
2224
2325
This is the PyHelios equivalent of helios-core/samples/energybalance_StanfordBunny/main.cpp
2426
"""

docs/plugin_energybalance.md

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[TOC]
66

77
<table>
8-
<tr><th>Dependencies</th><td>NVIDIA CUDA 9.0+</td></tr>
8+
<tr><th>Dependencies</th><td>None (CUDA optional for GPU acceleration)</td></tr>
99
<tr><th>Python Import</th><td>`from pyhelios import EnergyBalanceModel`</td></tr>
1010
<tr><th>Main Class</th><td>\ref pyhelios.EnergyBalance.EnergyBalanceModel "EnergyBalanceModel"</td></tr>
1111
</table>
@@ -15,18 +15,24 @@
1515
<table>
1616
<tr>
1717
<th>Dependencies</th>
18-
<td>NVIDIA CUDA 9.0+</td>
18+
<td>None required (OpenMP recommended for parallel CPU execution)</td>
1919
</tr>
2020
<tr>
2121
<th>Platforms</th>
22-
<td>Windows, Linux</td>
22+
<td>Windows, Linux, macOS</td>
2323
</tr>
2424
<tr>
2525
<th>GPU</th>
26-
<td>Required - NVIDIA CUDA-capable GPU</td>
26+
<td>Optional - NVIDIA CUDA-capable GPU for GPU acceleration</td>
27+
</tr>
28+
<tr>
29+
<th>Execution Modes</th>
30+
<td>Three-tier: GPU (CUDA) → OpenMP (parallel CPU) → Serial CPU fallback</td>
2731
</tr>
2832
</table>
2933

34+
**Note:** As of helios-core v1.3.61, CUDA is **optional**. The plugin automatically selects the best available execution mode: GPU acceleration with CUDA if available, parallel CPU with OpenMP if available, or serial CPU as fallback. OpenMP is recommended for most workloads on systems without CUDA.
35+
3036
## Quick Start
3137

3238
```python
@@ -68,37 +74,52 @@ with Context() as context:
6874
<tr>
6975
<td rowspan="2">**Runtime**<br>(pip install)</td>
7076
<td>NVIDIA GPU</td>
71-
<td>Not supported</td>
72-
<td colspan="2">Required - CUDA-capable (compute capability 3.5+)</td>
77+
<td colspan="3">Optional - Enables GPU acceleration if available</td>
7378
</tr>
7479
<tr>
7580
<td>CUDA Runtime</td>
76-
<td>Not supported</td>
77-
<td colspan="2">Version 9.0+<br>Typically installed with NVIDIA drivers<br>Or install <a href="https://developer.nvidia.com/cuda-downloads">CUDA Toolkit</a></td>
81+
<td colspan="3">Optional - Version 9.0+ for GPU acceleration<br>If not available, uses OpenMP CPU mode or serial fallback</td>
82+
</tr>
83+
<tr>
84+
<td>**Runtime**<br>(recommended)</td>
85+
<td>OpenMP</td>
86+
<td colspan="3">Recommended - Enables parallel CPU execution<br>Typically included with GCC/Clang compilers</td>
7887
</tr>
7988
<tr style="border-top: 3px double #888">
80-
<td>**Build from Source**<br>(additional deps)</td>
89+
<td>**Build from Source**<br>(optional)</td>
8190
<td>CUDA Toolkit</td>
82-
<td>Not supported</td>
83-
<td colspan="2">Version 9.0+ with nvcc compiler<br><a href="https://developer.nvidia.com/cuda-downloads">Download CUDA Toolkit</a></td>
91+
<td colspan="3">Optional - Version 9.0+ with nvcc compiler for GPU support<br><a href="https://developer.nvidia.com/cuda-downloads">Download CUDA Toolkit</a></td>
8492
</tr>
8593
</table>
8694

87-
**For detailed CUDA installation instructions**, see the comprehensive \ref CUDASetup "CUDA Setup Guide", which covers:
95+
**Execution Mode Selection (v1.3.61+):**
96+
The plugin automatically selects the best available execution mode at runtime:
97+
1. **GPU mode (CUDA)** - Fastest, requires NVIDIA GPU and CUDA
98+
2. **Parallel CPU mode (OpenMP)** - Recommended for systems without GPU, uses multi-core parallelization
99+
3. **Serial CPU mode** - Fallback if neither GPU nor OpenMP available
100+
101+
**For GPU acceleration setup**, see the comprehensive \ref CUDASetup "CUDA Setup Guide", which covers:
88102
- Choosing the correct CUDA toolkit version: \ref ChoosingCUDA
89103
- Platform-specific installation steps (Windows, Linux)
90104
- GPU timeout settings for Windows: \ref PCGPUTimeout
91-
- OptiX requirements: \ref OptiXSetup
92105
- Troubleshooting common issues
93106

107+
**Note:** CUDA is **no longer required** as of helios-core v1.3.61. The plugin works on all platforms with automatic CPU fallback.
108+
94109

95110
## Known Issues {#EBissues}
96111

97112
None.
98113

99114
## Introduction {#EBIntro}
100115

101-
This model plugin calculates a local energy balance for every primitive, and ultimately predicts sensible, latent, and longwave fluxes as well as surface temperature. The energy balance equation is solved in parallel on the GPU to accelerate calculations.
116+
This model plugin calculates a local energy balance for every primitive, and ultimately predicts sensible, latent, and longwave fluxes as well as surface temperature. The energy balance equation is solved using one of three execution modes:
117+
118+
1. **GPU acceleration (CUDA)** - Parallel execution on NVIDIA GPU for maximum performance
119+
2. **OpenMP parallel CPU** - Multi-core CPU parallelization (recommended for systems without GPU)
120+
3. **Serial CPU** - Single-threaded fallback if neither GPU nor OpenMP available
121+
122+
The execution mode is automatically selected based on available hardware and compiled libraries. GPU acceleration can be explicitly controlled using the GPU acceleration methods (see \ref EBGPUControl).
102123

103124
The model is solving the steady-state budget between absorbed radiation, emitted radiation, sensible heat exchange, and latent heat exchange, which is written as
104125

native/include/pyhelios_wrapper_energybalance.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,32 @@ PYHELIOS_API void printDefaultValueReport(EnergyBalanceModel* energy_model);
148148
*/
149149
PYHELIOS_API void printDefaultValueReportForUUIDs(EnergyBalanceModel* energy_model, const unsigned int* uuids, unsigned int uuid_count);
150150

151+
//=============================================================================
152+
// GPU Acceleration Control (Only available when compiled with CUDA)
153+
//=============================================================================
154+
155+
/**
156+
* @brief Enable GPU acceleration for energy balance calculations
157+
* @param energy_model Pointer to the EnergyBalanceModel
158+
* @note Only available when compiled with CUDA support
159+
*/
160+
PYHELIOS_API void enableGPUAcceleration(EnergyBalanceModel* energy_model);
161+
162+
/**
163+
* @brief Disable GPU acceleration and force CPU mode
164+
* @param energy_model Pointer to the EnergyBalanceModel
165+
* @note Only available when compiled with CUDA support
166+
*/
167+
PYHELIOS_API void disableGPUAcceleration(EnergyBalanceModel* energy_model);
168+
169+
/**
170+
* @brief Check if GPU acceleration is currently enabled
171+
* @param energy_model Pointer to the EnergyBalanceModel
172+
* @return 1 if GPU acceleration is enabled, 0 if not, -1 on error
173+
* @note Only available when compiled with CUDA support
174+
*/
175+
PYHELIOS_API int isGPUAccelerationEnabled(EnergyBalanceModel* energy_model);
176+
151177
#ifdef __cplusplus
152178
}
153179
#endif

native/include/pyhelios_wrapper_plantarchitecture.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ PYHELIOS_API void destroyPlantArchitecture(PlantArchitecture* plantarch);
2121

2222
// Plant library functions
2323
PYHELIOS_API int loadPlantModelFromLibrary(PlantArchitecture* plantarch, const char* plant_label);
24-
PYHELIOS_API unsigned int buildPlantInstanceFromLibrary(PlantArchitecture* plantarch, float* base_position, float age);
25-
PYHELIOS_API int buildPlantCanopyFromLibrary(PlantArchitecture* plantarch, float* canopy_center, float* plant_spacing, int* plant_count, float age, unsigned int** plant_ids, int* num_plants);
24+
PYHELIOS_API unsigned int buildPlantInstanceFromLibrary(PlantArchitecture* plantarch, float* base_position, float age, char** param_keys, float* param_values, int param_count);
25+
PYHELIOS_API int buildPlantCanopyFromLibrary(PlantArchitecture* plantarch, float* canopy_center, float* plant_spacing, int* plant_count, float age, unsigned int** plant_ids, int* num_plants, char** param_keys, float* param_values, int param_count_params);
2626
PYHELIOS_API int advanceTime(PlantArchitecture* plantarch, float dt);
2727

2828
// Custom plant building functions
@@ -56,6 +56,18 @@ PYHELIOS_API int writePlantStructureXML(PlantArchitecture* plantarch, unsigned i
5656
PYHELIOS_API int writeQSMCylinderFile(PlantArchitecture* plantarch, unsigned int plantID, const char* filename);
5757
PYHELIOS_API int readPlantStructureXML(PlantArchitecture* plantarch, const char* filename, bool quiet, unsigned int** plant_ids, int* num_plants);
5858

59+
// Parameter management functions
60+
PYHELIOS_API const char* getCurrentShootParametersJSON(PlantArchitecture* plantarch, const char* shoot_type_label);
61+
PYHELIOS_API int defineShootTypeFromJSON(PlantArchitecture* plantarch, helios::Context* context, const char* shoot_type_label, const char* json_params);
62+
63+
// Phenological control functions
64+
PYHELIOS_API int setPlantPhenologicalThresholds(PlantArchitecture* plantarch, unsigned int plantID, float time_to_dormancy_break, float time_to_flower_initiation, float time_to_flower_opening, float time_to_fruit_set, float time_to_fruit_maturity, float time_to_dormancy, float max_leaf_lifespan);
65+
66+
// Plant state query functions
67+
PYHELIOS_API float getPlantAge(PlantArchitecture* plantarch, unsigned int plantID);
68+
PYHELIOS_API float getPlantHeight(PlantArchitecture* plantarch, unsigned int plantID);
69+
PYHELIOS_API float sumPlantLeafArea(PlantArchitecture* plantarch, unsigned int plantID);
70+
5971
#ifdef __cplusplus
6072
}
6173
#endif

native/src/pyhelios_wrapper_energybalance.cpp

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,76 @@ extern "C" {
385385
setError(PYHELIOS_ERROR_UNKNOWN, "ERROR (EnergyBalanceModel::printDefaultValueReport): Unknown error printing default value report for UUIDs.");
386386
}
387387
}
388-
388+
389+
//=============================================================================
390+
// GPU Acceleration Control (Only available when compiled with CUDA)
391+
//=============================================================================
392+
393+
PYHELIOS_API void enableGPUAcceleration(EnergyBalanceModel* energy_model) {
394+
try {
395+
clearError();
396+
if (!energy_model) {
397+
setError(PYHELIOS_ERROR_INVALID_PARAMETER, "EnergyBalanceModel pointer is null");
398+
return;
399+
}
400+
401+
#ifdef HELIOS_CUDA_AVAILABLE
402+
energy_model->enableGPUAcceleration();
403+
#else
404+
setError(PYHELIOS_ERROR_RUNTIME, "GPU acceleration not available - library not compiled with CUDA support");
405+
#endif
406+
407+
} catch (const std::exception& e) {
408+
setError(PYHELIOS_ERROR_RUNTIME, std::string("ERROR (EnergyBalanceModel::enableGPUAcceleration): ") + e.what());
409+
} catch (...) {
410+
setError(PYHELIOS_ERROR_UNKNOWN, "ERROR (EnergyBalanceModel::enableGPUAcceleration): Unknown error enabling GPU acceleration.");
411+
}
412+
}
413+
414+
PYHELIOS_API void disableGPUAcceleration(EnergyBalanceModel* energy_model) {
415+
try {
416+
clearError();
417+
if (!energy_model) {
418+
setError(PYHELIOS_ERROR_INVALID_PARAMETER, "EnergyBalanceModel pointer is null");
419+
return;
420+
}
421+
422+
#ifdef HELIOS_CUDA_AVAILABLE
423+
energy_model->disableGPUAcceleration();
424+
#else
425+
// No-op if CUDA not available - already running in CPU mode
426+
#endif
427+
428+
} catch (const std::exception& e) {
429+
setError(PYHELIOS_ERROR_RUNTIME, std::string("ERROR (EnergyBalanceModel::disableGPUAcceleration): ") + e.what());
430+
} catch (...) {
431+
setError(PYHELIOS_ERROR_UNKNOWN, "ERROR (EnergyBalanceModel::disableGPUAcceleration): Unknown error disabling GPU acceleration.");
432+
}
433+
}
434+
435+
PYHELIOS_API int isGPUAccelerationEnabled(EnergyBalanceModel* energy_model) {
436+
try {
437+
clearError();
438+
if (!energy_model) {
439+
setError(PYHELIOS_ERROR_INVALID_PARAMETER, "EnergyBalanceModel pointer is null");
440+
return -1;
441+
}
442+
443+
#ifdef HELIOS_CUDA_AVAILABLE
444+
return energy_model->isGPUAccelerationEnabled() ? 1 : 0;
445+
#else
446+
return 0; // GPU acceleration never enabled without CUDA
447+
#endif
448+
449+
} catch (const std::exception& e) {
450+
setError(PYHELIOS_ERROR_RUNTIME, std::string("ERROR (EnergyBalanceModel::isGPUAccelerationEnabled): ") + e.what());
451+
return -1;
452+
} catch (...) {
453+
setError(PYHELIOS_ERROR_UNKNOWN, "ERROR (EnergyBalanceModel::isGPUAccelerationEnabled): Unknown error checking GPU acceleration status.");
454+
return -1;
455+
}
456+
}
457+
389458
} //extern "C"
390459

391460
#endif //ENERGYBALANCE_PLUGIN_AVAILABLE

0 commit comments

Comments
 (0)