Skip to content

Commit 200ac08

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 200ac08

69 files changed

Lines changed: 7455 additions & 2727 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docs.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ jobs:
5454
doxygen --version
5555
doxypypy --help
5656
57+
- name: Sync version to Doxygen config
58+
run: |
59+
# Get version from setuptools-scm and update Doxyfile
60+
VERSION=$(python -c "from pyhelios._version import __version__; print(__version__)")
61+
echo "Updating Doxygen PROJECT_NUMBER to: $VERSION"
62+
sed -i "s/^PROJECT_NUMBER.*$/PROJECT_NUMBER = $VERSION/" docs/Doxyfile.python
63+
5764
- name: Build documentation
5865
run: |
5966
# Create output directory
@@ -118,6 +125,13 @@ jobs:
118125
pip install -r requirements.txt
119126
pip install -e .
120127
128+
- name: Sync version to Doxygen config (preview)
129+
run: |
130+
# Get version from setuptools-scm and update Doxyfile
131+
VERSION=$(python -c "from pyhelios._version import __version__; print(__version__)")
132+
echo "Updating Doxygen PROJECT_NUMBER to: $VERSION"
133+
sed -i "s/^PROJECT_NUMBER.*$/PROJECT_NUMBER = $VERSION/" docs/Doxyfile.python
134+
121135
- name: Build documentation (preview)
122136
run: |
123137
mkdir -p docs/generated

.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: 28 additions & 19 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

@@ -180,20 +180,6 @@ PyHelios uses a sophisticated plugin-based architecture supporting **21 availabl
180180
- **visualizer**: OpenGL-based 3D visualization and rendering
181181
- **projectbuilder**: GUI project builder with ImGui interface
182182

183-
#### Plugin Selection Profiles
184-
185-
The system includes predefined profiles for common use cases:
186-
187-
- **minimal**: Core functionality only (weberpenntree, canopygenerator, solarposition)
188-
- **standard**: Standard features with visualization (adds visualizer, energybalance, photosynthesis)
189-
- **gpu-accelerated**: High-performance GPU features (adds radiation for ray tracing)
190-
- **research**: Comprehensive research suite (includes most plugins for academic use)
191-
- **production**: Production-ready features (reliable, well-tested plugins)
192-
- **visualization**: Focus on rendering and visualization capabilities
193-
- **sensing**: Remote sensing and LiDAR simulation capabilities
194-
- **physics**: Comprehensive physics modeling and simulation
195-
- **development**: Minimal set for PyHelios development and testing
196-
197183
### Cross-Platform Library Loading
198184

199185
PyHelios includes a sophisticated cross-platform library loader (`pyhelios/plugins/loader.py`):
@@ -448,6 +434,16 @@ pytest tests/test_context.py -v
448434
- Tests provide clear output showing passed/skipped/failed counts
449435
- Zero warnings and errors in final test execution
450436

437+
**CRITICAL: Mandatory Full Verification Protocol**
438+
For ANY changes to C++ interface files (native/src/*, native/include/*, pyhelios_build/*), ctypes wrappers, or core functionality, you MUST complete this verification sequence:
439+
440+
1. **Full Native Rebuild**: Run `build_scripts/build_helios --clean` to rebuild from scratch
441+
2. **Complete Test Suite**: Run `pytest` (not individual tests) to verify ALL tests pass
442+
3. **Zero Tolerance**: Any failing tests must be fixed before declaring success
443+
4. **No Shortcuts**: Never skip the full test suite even if "individual tests pass"
444+
445+
This protocol is NON-NEGOTIABLE and must be completed regardless of time constraints or apparent simplicity of changes.
446+
451447
**GitHub CI/CD Integration:**
452448
- Comprehensive CI/CD workflows test PyHelios across all platforms
453449
- Quick tests (`test-quick.yml`) for fast development feedback
@@ -469,6 +465,16 @@ pytest tests/test_context.py -v
469465
- **Mock mode development**: Develop and test PyHelios functionality without requiring native library compilation
470466
- **Test categories**: Use `pytest -m cross_platform` for platform-independent tests, `pytest -m native_only` for native library tests
471467

468+
**MANDATORY: Final Verification Checklist**
469+
Before declaring ANY task complete involving C++/Python interface changes:
470+
□ Built native libraries from scratch using `build_scripts/build_helios --clean`
471+
□ Ran complete `pytest` suite (not selective tests)
472+
□ All tests pass with zero failures
473+
□ No regressions introduced in any module
474+
□ Changes committed to git if task involves file modifications
475+
476+
Failure to complete this checklist constitutes incomplete task execution.
477+
472478
**Architecture and Integration:**
473479
- **UUID-based tracking**: All geometric operations return UUIDs for object tracking
474480
- **Context state management**: The Context manages geometry state with dirty/clean marking
@@ -512,7 +518,7 @@ PyHelios has successfully integrated 3 major Helios C++ plugins (radiation, visu
512518

513519
**ALWAYS follow the 8-phase integration process:**
514520

515-
1. **Plugin Metadata Registration** - Add to `pyhelios/config/plugin_metadata.py` and relevant profiles
521+
1. **Plugin Metadata Registration** - Add to `pyhelios/config/plugin_metadata.py`
516522
2. **Build System Integration** - CMake and flexible plugin selection system
517523
3. **C++ Interface Implementation** - Add wrapper functions to `pyhelios_build/pyhelios_interface.cpp`
518524
4. **ctypes Wrapper Creation** - Python-to-C++ interface in `pyhelios/wrappers/`
@@ -541,6 +547,9 @@ Many plugins require runtime assets that must be copied to specific locations wh
541547
- Map parameters semantically, not just positionally
542548
- Test different parameter combinations
543549

550+
**Git Control**:
551+
- Be sure to add any files to git control that will be needed in the repository.
552+
544553
#### Documentation Requirements
545554

546555
For plugin integration tasks, ALWAYS:
@@ -554,7 +563,7 @@ For plugin integration tasks, ALWAYS:
554563
All plugin integrations MUST include:
555564
- **Cross-platform tests** (`@pytest.mark.cross_platform`) that work with mock mode
556565
- **Native-only tests** (`@pytest.mark.native_only`) that require actual plugin functionality
557-
- **Plugin metadata tests** to verify registration and profiles
566+
- **Plugin metadata tests** to verify registration
558567
- **Error handling tests** to verify proper exception translation
559568
- **Asset management tests** to verify runtime dependencies are available
560569

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: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
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+
- Updated Helios native C++ library to v1.3.46
9+
10+
## Visualizer
11+
- Added `Visualizer.colorContextPrimitivesByData()`
12+
- Fixed a number of issues where visualizer methods were using lists instead of Helios data types (e.g., vec2, vec3, etc.)
13+
314
# [v0.0.3] 2025-08-23
415

516
## 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
17+
- Added comprehensive file loading support with `Context.loadPLY()`, `Context.loadOBJ()`, and `Context.loadXML()` methods
18+
- Enhanced `Context.loadPLY()` with 5 overloads supporting origin, height, rotation, color, and upaxis transformations
19+
- Enhanced `Context.loadOBJ()` with 4 overloads including scale transformations and upaxis specification
20+
- Added complete `Context.loadXML()` implementation for Helios XML geometry files
1021
- Extended native C++ wrapper with 9 new file loading functions and proper error handling
1122
- Added comprehensive parameter validation and security path checking
12-
- Implemented `addTriangleTextured()`
13-
- Implemented `addTrianglesFromArraysTextured()`
23+
- Implemented `Context.addTriangleTextured()`
24+
- Implemented `Context.addTrianglesFromArraysTextured()`
1425

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

0 commit comments

Comments
 (0)