Skip to content

Commit e2676ea

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 e2676ea

17 files changed

Lines changed: 2223 additions & 76 deletions

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,9 @@ print(f"Created patch: {patch_uuid}")
162162
## Key Features
163163

164164
- **Cross-platform**: Windows, macOS, and Linux support
165-
- **Plant modeling**: WeberPennTree procedural generation
165+
- **Plant modeling**: 20+ plant species models in the plant architecture plug-in
166166
- **GPU acceleration**: OptiX-powered radiation simulation
167167
- **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
170168

171169
## Updating PyHelios
172170

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/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)