Skip to content

Commit a03a253

Browse files
committed
[v0.0.4] 2025-08-25
🚨++ New Plug-in Integrated ++ 🚨 - Energy Balance plug-in integrated with PyHelios ## Visualizer - Added `Visualizer.colorContextPrimitivesByData()` - Fixed a number of issues where visualizer methods were using lists instead of Helios data types (e.g., vec2, vec3, etc.)
1 parent 4633f2f commit a03a253

40 files changed

Lines changed: 4072 additions & 2174 deletions

.github/workflows/pytest-linux-gpu.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ jobs:
2121
role-to-assume: ${{ secrets.OIDC_ROLE_ARN }}
2222
aws-region: us-west-2
2323
- run: |
24-
aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID }}
25-
aws ec2 wait instance-running --instance-ids ${{ secrets.EC2_INSTANCE_ID }}
24+
aws ec2 start-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_LINUX }}
25+
aws ec2 wait instance-running --instance-ids ${{ secrets.EC2_INSTANCE_ID_LINUX }}
2626
2727
run_pyhelios_gpu_tests:
2828
runs-on: [self-hosted]
@@ -90,5 +90,5 @@ jobs:
9090
role-to-assume: ${{ secrets.OIDC_ROLE_ARN }}
9191
aws-region: us-west-2
9292
- run: |
93-
aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID }}
94-
aws ec2 wait instance-stopped --instance-ids ${{ secrets.EC2_INSTANCE_ID }}
93+
aws ec2 stop-instances --instance-ids ${{ secrets.EC2_INSTANCE_ID_LINUX }}
94+
aws ec2 wait instance-stopped --instance-ids ${{ secrets.EC2_INSTANCE_ID_LINUX }}

CLAUDE.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,6 @@ python -m pyhelios.plugins info radiation
9696
# Validate plugin configuration
9797
python -m pyhelios.plugins validate --plugins radiation,visualizer
9898

99-
# List all available profiles
100-
python -m pyhelios.plugins profiles
10199
```
102100

103101
#### Configuration File Support
@@ -106,7 +104,9 @@ Create `pyhelios_config.yaml` to specify default plugin selection:
106104

107105
```yaml
108106
plugins:
109-
profile: "standard"
107+
explicit_plugins:
108+
- weberpenntree
109+
- visualizer
110110
excluded_plugins:
111111
- radiation # Exclude if no GPU available
112112

@@ -182,7 +182,7 @@ PyHelios uses a sophisticated plugin-based architecture supporting **21 availabl
182182

183183
#### Plugin Selection Profiles
184184

185-
The system includes predefined profiles for common use cases:
185+
The system supports flexible plugin selection:
186186

187187
- **minimal**: Core functionality only (weberpenntree, canopygenerator, solarposition)
188188
- **standard**: Standard features with visualization (adds visualizer, energybalance, photosynthesis)
@@ -512,7 +512,7 @@ PyHelios has successfully integrated 3 major Helios C++ plugins (radiation, visu
512512

513513
**ALWAYS follow the 8-phase integration process:**
514514

515-
1. **Plugin Metadata Registration** - Add to `pyhelios/config/plugin_metadata.py` and relevant profiles
515+
1. **Plugin Metadata Registration** - Add to `pyhelios/config/plugin_metadata.py`
516516
2. **Build System Integration** - CMake and flexible plugin selection system
517517
3. **C++ Interface Implementation** - Add wrapper functions to `pyhelios_build/pyhelios_interface.cpp`
518518
4. **ctypes Wrapper Creation** - Python-to-C++ interface in `pyhelios/wrappers/`
@@ -554,7 +554,7 @@ For plugin integration tasks, ALWAYS:
554554
All plugin integrations MUST include:
555555
- **Cross-platform tests** (`@pytest.mark.cross_platform`) that work with mock mode
556556
- **Native-only tests** (`@pytest.mark.native_only`) that require actual plugin functionality
557-
- **Plugin metadata tests** to verify registration and profiles
557+
- **Plugin metadata tests** to verify registration
558558
- **Error handling tests** to verify proper exception translation
559559
- **Asset management tests** to verify runtime dependencies are available
560560

README.md

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ PyHelios provides a Python interface to the powerful Helios C++ library for 3D p
1414

1515
📖 **[Complete Documentation](https://plantsimulationlab.github.io/PyHelios/)**
1616

17+
⚠️Note that this is a work in progress. Not all Helios functionality has been implemented in PyHelios ⚠️
18+
19+
See the Helios C++ documentation for a more in-depth description of Helios: https://baileylab.ucdavis.edu/software/helios
20+
1721
## Quick Start
1822

1923
### Installation
@@ -66,10 +70,6 @@ pip install -e .
6670
- Python 3.7+
6771

6872
```bash
69-
# Install prerequisites
70-
sudo apt-get update
71-
sudo apt-get install build-essential cmake python3-dev
72-
7373
# Clone repository
7474
git clone --recursive https://github.com/PlantSimulationLab/PyHelios.git
7575
cd PyHelios/
@@ -100,18 +100,6 @@ patch_uuid = context.addPatch(center=center, size=size, color=color)
100100
print(f"Created patch: {patch_uuid}")
101101
```
102102

103-
### Tree Modeling
104-
105-
```python
106-
from pyhelios import Context, WeberPennTree
107-
108-
context = Context()
109-
wpt = WeberPennTree(context)
110-
111-
# Generate procedural tree
112-
tree_id = wpt.buildTree(WeberPennTree.WPTType.LEMON)
113-
```
114-
115103
## Documentation
116104

117105
| Section | Description |
@@ -127,7 +115,7 @@ tree_id = wpt.buildTree(WeberPennTree.WPTType.LEMON)
127115
- **Plant modeling**: WeberPennTree procedural generation
128116
- **GPU acceleration**: OptiX-powered radiation simulation
129117
- **3D visualization**: OpenGL-based real-time rendering
130-
- **Flexible plugins**: 21 available plugins for specialized tasks
118+
- **Flexible plugins**: Currently 4 plug-ins implemented
131119
- **Development mode**: Mock mode for development without native libraries
132120

133121
## Quick Commands

build_scripts/build_helios.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ def clean_build_artifacts(self) -> None:
219219
With the current architecture, all build artifacts are contained within
220220
the build directory, so cleaning is simply removing that directory.
221221
"""
222-
print("🧹 Cleaning build artifacts...")
222+
print("[CLEAN] Cleaning build artifacts...")
223223

224224
# Remove the build directory - this contains all build artifacts
225225
if self.build_dir.exists():
@@ -265,13 +265,13 @@ def resolve_plugin_selection(self) -> List[str]:
265265

266266
if result.warnings:
267267
for warning in result.warnings:
268-
print(f"⚠️ Warning: {warning}")
268+
print(f"[WARN] {warning}")
269269

270270
if result.added_plugins:
271271
print(f"[OK] Added dependencies: {result.added_plugins}")
272272

273273
if result.removed_plugins:
274-
print(f"🚫 Removed incompatible plugins: {result.removed_plugins}")
274+
print(f"[EXCLUDED] Removed incompatible plugins: {result.removed_plugins}")
275275

276276
print(f"[OK] Final plugin selection: {result.final_plugins}")
277277
return result.final_plugins
@@ -1027,7 +1027,7 @@ def _copy_visualizer_assets(self) -> None:
10271027
target_base_dir = self.output_dir.parent / 'plugins' / 'visualizer'
10281028

10291029
if not build_visualizer_dir.exists():
1030-
print(f"ℹ️ Visualizer assets directory not found: {build_visualizer_dir} (visualizer plugin may not be enabled)")
1030+
print(f"[INFO] Visualizer assets directory not found: {build_visualizer_dir} (visualizer plugin may not be enabled)")
10311031
return
10321032

10331033
total_files_copied = 0
@@ -1093,9 +1093,9 @@ def _copy_visualizer_assets(self) -> None:
10931093
print(f"[OK] Copied {font_count} font files")
10941094

10951095
if total_files_copied > 0:
1096-
print(f"🎨 Successfully copied {total_files_copied} visualizer assets to {target_base_dir}")
1096+
print(f"[OK] Successfully copied {total_files_copied} visualizer assets to {target_base_dir}")
10971097
else:
1098-
print(f"⚠️ No visualizer assets found to copy")
1098+
print(f"[WARN] No visualizer assets found to copy")
10991099

11001100
def _clean_duplicate_symbols(self, main_lib_path: Path, build_lib_dir: Path) -> Dict[str, Any]:
11011101
"""
@@ -1399,18 +1399,19 @@ def _patch_zlib_cmake_for_windows(self) -> None:
13991399

14001400
def get_default_plugins() -> List[str]:
14011401
"""
1402-
Get the default set of plugins (only the 3 currently integrated plugins).
1402+
Get the default set of plugins (only the currently integrated plugins).
14031403
1404-
Currently only 3 plugins are integrated into PyHelios:
1404+
Currently integrated plugins in PyHelios:
14051405
- visualizer: OpenGL-based 3D visualization
14061406
- weberpenntree: Procedural tree generation
14071407
- radiation: OptiX-accelerated ray tracing (GPU optional)
1408+
- energybalance: GPU-accelerated thermal modeling and energy balance
14081409
14091410
Returns:
14101411
List of default plugins
14111412
"""
1412-
# Only return the 3 plugins that are actually integrated into PyHelios
1413-
integrated_plugins = ["visualizer", "weberpenntree", "radiation"]
1413+
# Return the plugins that are actually integrated into PyHelios
1414+
integrated_plugins = ["visualizer", "weberpenntree", "radiation", "energybalance"]
14141415

14151416
# Filter by platform compatibility
14161417
default_plugins = []
@@ -1449,24 +1450,40 @@ def parse_plugin_selection(args) -> List[str]:
14491450
# 2. Apply exclusion flags
14501451
if args.nogpu:
14511452
# Remove GPU-dependent plugins
1453+
gpu_plugins = [p for p in selected_plugins
1454+
if PLUGIN_METADATA.get(p, PluginMetadata("", "", [], [], [], False, True, [], [])).gpu_required]
14521455
selected_plugins = [p for p in selected_plugins
14531456
if not PLUGIN_METADATA.get(p, PluginMetadata("", "", [], [], [], False, True, [], [])).gpu_required]
1457+
if gpu_plugins:
1458+
print(f"[EXCLUDED] GPU-dependent plugins excluded (--nogpu): {gpu_plugins}")
14541459

14551460
if args.novis:
14561461
# Remove visualization plugins
1462+
vis_plugins = [p for p in selected_plugins
1463+
if any(dep in ["opengl", "glfw", "imgui"]
1464+
for dep in PLUGIN_METADATA.get(p, PluginMetadata("", "", [], [], [], False, True, [], [])).system_dependencies)]
14571465
selected_plugins = [p for p in selected_plugins
14581466
if not any(dep in ["opengl", "glfw", "imgui"]
14591467
for dep in PLUGIN_METADATA.get(p, PluginMetadata("", "", [], [], [], False, True, [], [])).system_dependencies)]
1468+
if vis_plugins:
1469+
print(f"[EXCLUDED] Visualization plugins excluded (--novis): {vis_plugins}")
14601470

14611471
# 3. Apply additional exclusions
14621472
if args.exclude:
1473+
excluded_plugins = [p for p in selected_plugins if p in args.exclude]
14631474
selected_plugins = [p for p in selected_plugins if p not in args.exclude]
1475+
if excluded_plugins:
1476+
print(f"[EXCLUDED] Explicitly excluded plugins (--exclude): {excluded_plugins}")
14641477

14651478

14661479
# 4. Check environment variables for additional exclusions
14671480
if os.environ.get('PYHELIOS_EXCLUDE_GPU', '').lower() in ['1', 'true', 'yes']:
1481+
env_gpu_plugins = [p for p in selected_plugins
1482+
if PLUGIN_METADATA.get(p, PluginMetadata("", "", [], [], [], False, True, [], [])).gpu_required]
14681483
selected_plugins = [p for p in selected_plugins
14691484
if not PLUGIN_METADATA.get(p, PluginMetadata("", "", [], [], [], False, True, [], [])).gpu_required]
1485+
if env_gpu_plugins:
1486+
print(f"[EXCLUDED] GPU-dependent plugins excluded (PYHELIOS_EXCLUDE_GPU): {env_gpu_plugins}")
14701487

14711488
# 5. Return final plugin list
14721489
return selected_plugins
@@ -1657,7 +1674,7 @@ def main():
16571674
print(f"[ERROR] Invalid plugins: {validation['invalid_plugins']}")
16581675
print(f"Platform compatible: {validation['platform_compatible']}")
16591676
if validation['platform_incompatible']:
1660-
print(f"⚠️ Platform incompatible: {validation['platform_incompatible']}")
1677+
print(f"[WARN] Platform incompatible: {validation['platform_incompatible']}")
16611678

16621679
if validation['system_dependencies']:
16631680
print("\nSystem Dependencies:")
@@ -1733,7 +1750,7 @@ def main():
17331750
# PyHelios automatically discovers libraries in the build directory
17341751

17351752
except Exception as e:
1736-
print(f"⚠️ Warning: Could not load built library: {e}")
1753+
print(f"[WARN] Could not load built library: {e}")
17371754
if platform.system() != 'Windows':
17381755
print(" Try setting LD_LIBRARY_PATH and running again:")
17391756
print(f" export LD_LIBRARY_PATH=\"{args.output_dir}:$LD_LIBRARY_PATH\"")

docs/CHANGELOG.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
# Changelog
22

3+
# [v0.0.4] 2025-08-25
4+
5+
🚨++ New Plug-in Integrated ++ 🚨
6+
- Energy Balance plug-in integrated with PyHelios
7+
8+
## Visualizer
9+
- Added `Visualizer.colorContextPrimitivesByData()`
10+
- Fixed a number of issues where visualizer methods were using lists instead of Helios data types (e.g., vec2, vec3, etc.)
11+
312
# [v0.0.3] 2025-08-23
413

514
## Context
6-
- Added comprehensive file loading support with `loadPLY()`, `loadOBJ()`, and `loadXML()` methods
7-
- Enhanced `loadPLY()` with 5 overloads supporting origin, height, rotation, color, and upaxis transformations
8-
- Enhanced `loadOBJ()` with 4 overloads including scale transformations and upaxis specification
9-
- Added complete `loadXML()` implementation for Helios XML geometry files
15+
- Added comprehensive file loading support with `Context.loadPLY()`, `Context.loadOBJ()`, and `Context.loadXML()` methods
16+
- Enhanced `Context.loadPLY()` with 5 overloads supporting origin, height, rotation, color, and upaxis transformations
17+
- Enhanced `Context.loadOBJ()` with 4 overloads including scale transformations and upaxis specification
18+
- Added complete `Context.loadXML()` implementation for Helios XML geometry files
1019
- Extended native C++ wrapper with 9 new file loading functions and proper error handling
1120
- Added comprehensive parameter validation and security path checking
12-
- Implemented `addTriangleTextured()`
13-
- Implemented `addTrianglesFromArraysTextured()`
21+
- Implemented `Context.addTriangleTextured()`
22+
- Implemented `Context.addTrianglesFromArraysTextured()`
1423

1524
## Examples
1625
- Added example geometry files: `suzanne.ply`, `suzanne.obj`, `suzanne.mtl`, and `leaf_cube.xml`

docs/Doxyfile.python

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#---------------------------------------------------------------------------
77

88
PROJECT_NAME = "PyHelios"
9-
PROJECT_NUMBER = 0.0.3
9+
PROJECT_NUMBER = 0.0.4
1010
PROJECT_BRIEF =
1111
PROJECT_LOGO = docs/images/PyHelios_logo_whiteborder.png
1212
OUTPUT_DIRECTORY = docs/generated
@@ -130,6 +130,7 @@ INPUT = pyhelios \
130130
docs/user_guide.md \
131131
docs/plugin_integration_guide.md \
132132
docs/cpp_plugin_integration_guide.md \
133+
docs/plugin_energybalance.md \
133134
docs/plugin_radiation.md \
134135
docs/plugin_visualizer.md \
135136
docs/plugin_weberpenntree.md \
@@ -143,6 +144,20 @@ FILE_PATTERNS = *.py \
143144
RECURSIVE = YES
144145
EXCLUDE = pyhelios/plugins/__pycache__ \
145146
pyhelios/__pycache__ \
147+
pyhelios/wrappers/UContextWrapper.py \
148+
pyhelios/wrappers/UGlobalWrapper.py \
149+
pyhelios/wrappers/ULoggerWrapper.py \
150+
pyhelios/wrappers/URadiationModelWrapper.py \
151+
pyhelios/wrappers/UEnergyBalanceWrapper.py \
152+
pyhelios/wrappers/UVisualizerWrapper.py \
153+
pyhelios/wrappers/UWeberPennTreeWrapper.py \
154+
pyhelios/config \
155+
pyhelios/assets \
156+
pyhelios/plugins/loader.py \
157+
pyhelios/plugins/registry.py \
158+
pyhelios/dev_utils.py \
159+
pyhelios/_types.py \
160+
pyhelios/_version.py \
146161
tests \
147162
build_scripts \
148163
helios-core

docs/DoxygenLayout.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<!-- Plugin Documentation Section -->
2424
<tab type="usergroup" visible="yes" url="@ref Plugins" title="Plugins" intro="">
2525
<!-- Currently Implemented Plugins (Alphabetized) -->
26+
<tab type="user" visible="yes" url="@ref EnergyBalanceDoc" title="Energy Balance Model"/>
2627
<tab type="user" visible="yes" url="@ref RadiationDoc" title="Radiation Model"/>
2728
<tab type="user" visible="yes" url="@ref VisualizerDoc" title="Visualizer"/>
2829
<tab type="user" visible="yes" url="@ref WeberPennTreeDoc" title="Weber-Penn Tree"/>

0 commit comments

Comments
 (0)