Skip to content

Commit 4ced294

Browse files
committed
[v0.1.3] 2025-09-22
🚨++ New Plug-in Integrated ++ 🚨 - Initial phase of plant architecture plug-in integrated with PyHelios. This includes basic functionality for building plants from the library. - Updated Helios native C++ library to v1.3.50 *Improved Error Handling, Build System Optimization, and Testing Infrastructure* - **Context API**: Enhanced lifecycle state tracking with detailed error messages for better debugging - **Build System**: Streamlined asset management by removing redundant asset copying code and optimizing build process - **Testing**: Enhanced cross-platform test coverage with improved mock mode handling and context lifecycle testing - **Documentation**: Major updates to plugin integration guide with critical implementation patterns and best practices - **Visualizer**: Enhanced compatibility and error handling for cross-platform visualization workflows - Removed redundant asset copying for visualizer and weberpenntree plugins (using environment variable approach) - Optimized build process with cleaner CMake integration - Enhanced cross-platform library validation with fail-fast behavior
1 parent 2f26caa commit 4ced294

27 files changed

Lines changed: 2798 additions & 353 deletions

β€Ž.github/workflows/build-wheels.ymlβ€Ž

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Build Wheels
22

33
on:
44
push:
5-
tags: ['v*']
5+
tags: ['v*','testing-v*']
66
workflow_dispatch:
77

88
jobs:
@@ -335,8 +335,8 @@ jobs:
335335
# Test Linux testable plugins including headless visualizer
336336
# Only GPU-accelerated plugins require special hardware not available in CI
337337
available_plugins = info.get('available_plugins', [])
338-
testable_plugins = ['weberpenntree', 'visualizer', 'photosynthesis', 'solarposition', 'stomatalconductance']
339-
built_plugins = ['weberpenntree', 'visualizer', 'photosynthesis', 'solarposition', 'stomatalconductance', 'energybalance', 'radiation']
338+
testable_plugins = ['weberpenntree', 'visualizer', 'photosynthesis', 'solarposition', 'stomatalconductance', 'plantarchitecture']
339+
built_plugins = ['weberpenntree', 'visualizer', 'photosynthesis', 'solarposition', 'stomatalconductance', 'energybalance', 'radiation', 'plantarchitecture']
340340
341341
print(f'[INFO] Available plugins: {sorted(available_plugins)}')
342342
print(f'[INFO] Built plugins (expected): {sorted(built_plugins)}')
@@ -431,9 +431,9 @@ jobs:
431431
lib_path = info.get('library_path', 'Unknown')
432432
print(f'[SUCCESS] Native library loaded: {lib_path}')
433433
434-
# Test macOS expected plugins (5 plugins including visualization but no GPU)
434+
# Test macOS expected plugins (6 plugins including visualization but no GPU)
435435
available_plugins = info.get('available_plugins', [])
436-
expected_plugins = ['weberpenntree', 'visualizer', 'photosynthesis', 'solarposition', 'stomatalconductance']
436+
expected_plugins = ['weberpenntree', 'visualizer', 'photosynthesis', 'solarposition', 'stomatalconductance', 'plantarchitecture']
437437
print(f'[INFO] Available plugins: {sorted(available_plugins)}')
438438
print(f'[INFO] Expected plugins: {sorted(expected_plugins)}')
439439
@@ -524,8 +524,8 @@ jobs:
524524
# Test Windows testable plugins (CI containers don't have GPU for radiation/energybalance testing)
525525
# We build GPU plugins but can't test them in CI environment
526526
available_plugins = info.get('available_plugins', [])
527-
testable_plugins = ['weberpenntree', 'visualizer', 'photosynthesis', 'solarposition', 'stomatalconductance']
528-
built_plugins = ['weberpenntree', 'visualizer', 'photosynthesis', 'solarposition', 'stomatalconductance', 'energybalance', 'radiation']
527+
testable_plugins = ['weberpenntree', 'visualizer', 'photosynthesis', 'solarposition', 'stomatalconductance', 'plantarchitecture']
528+
built_plugins = ['weberpenntree', 'visualizer', 'photosynthesis', 'solarposition', 'stomatalconductance', 'energybalance', 'radiation', 'plantarchitecture']
529529
530530
print(f'[INFO] Available plugins: {sorted(available_plugins)}')
531531
print(f'[INFO] Built plugins (expected): {sorted(built_plugins)}')

β€Ž.github/workflows/pytest-linux-gpu.yamlβ€Ž

Lines changed: 0 additions & 94 deletions
This file was deleted.

β€ŽREADME.mdβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<br><br>
22

3-
[![Build Wheels](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/build-wheels.yml/badge.svg?branch=master)](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/build-wheels.yml) [![Test Linux](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-linux.yml/badge.svg?branch=master)](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-linux.yml) [![Test Windows](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-windows.yml/badge.svg?branch=master)](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-windows.yml) [![Test MacOS](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-macos.yml/badge.svg?branch=master)](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-macos.yml)
3+
[![Build Wheels](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/build-wheels.yml/badge.svg?event=push)](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/build-wheels.yml) [![Test Linux](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-linux.yml/badge.svg?branch=master)](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-linux.yml) [![Test Windows](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-windows.yml/badge.svg?branch=master)](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-windows.yml) [![Test MacOS](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-macos.yml/badge.svg?branch=master)](https://github.com/PlantSimulationLab/PyHelios/actions/workflows/pytest-macos.yml)
44

55
<div align="center">
66
<img src="https://raw.githubusercontent.com/PlantSimulationLab/PyHelios/master/docs/images/PyHelios_logo_whiteborder.png" alt="" width="300" />

β€Žbuild_scripts/build_helios.pyβ€Ž

Lines changed: 22 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@
3636
get_all_plugin_names = plugin_metadata_globals['get_all_plugin_names']
3737
get_platform_compatible_plugins = plugin_metadata_globals['get_platform_compatible_plugins']
3838

39+
# Canonical list of plugins integrated into PyHelios
40+
# This is the single source of truth - add new integrated plugins here
41+
INTEGRATED_PLUGINS = [
42+
"visualizer",
43+
"weberpenntree",
44+
"radiation",
45+
"energybalance",
46+
"solarposition",
47+
"stomatalconductance",
48+
"photosynthesis",
49+
"plantarchitecture"
50+
]
51+
3952
# Execute dependency_resolver.py to get PluginDependencyResolver
4053
dependency_resolver_globals = plugin_metadata_globals.copy() # Start with plugin_metadata context
4154
dependency_resolver_globals['__name__'] = 'dependency_resolver'
@@ -1269,15 +1282,7 @@ def copy_to_output(self, library_path: Path) -> Path:
12691282

12701283
# Validate that the copied library can be loaded by ctypes (FAIL-FAST validation)
12711284
self._validate_library_loadable(output_path)
1272-
1273-
# NOTE: Asset copying for visualizer plugin disabled - Stage 1 asset management
1274-
# uses environment variables to reference assets directly from helios-core source
1275-
# self._copy_visualizer_assets()
1276-
1277-
# NOTE: Asset copying for weberpenntree plugin disabled - Stage 1 asset management
1278-
# uses environment variables to reference assets directly from helios-core source
1279-
# self._copy_weberpenntree_assets()
1280-
1285+
12811286
# Also copy any additional required files
12821287
if self.platform_name == 'Windows':
12831288
# Look for PDB files for debugging
@@ -1287,95 +1292,7 @@ def copy_to_output(self, library_path: Path) -> Path:
12871292
print(f"Copied debug symbols: {pdb_path}")
12881293

12891294
return output_path
1290-
1291-
def _copy_visualizer_assets(self) -> None:
1292-
"""
1293-
Copy all visualizer assets (shaders, textures, fonts) to the expected location.
1294-
1295-
The C++ Visualizer code expects assets at "plugins/visualizer/" relative to the
1296-
working directory. This method copies all assets from the build directory to
1297-
the PyHelios directory structure where they can be found at runtime.
1298-
1299-
Assets copied:
1300-
- Shader files (.vert, .frag)
1301-
- Texture files
1302-
- Font files
1303-
"""
1304-
# Base paths
1305-
build_visualizer_dir = self.build_dir / 'plugins' / 'visualizer'
1306-
target_base_dir = self.output_dir.parent / 'plugins' / 'visualizer'
1307-
1308-
if not build_visualizer_dir.exists():
1309-
print(f"[INFO] Visualizer assets directory not found: {build_visualizer_dir} (visualizer plugin may not be enabled)")
1310-
return
1311-
1312-
total_files_copied = 0
1313-
1314-
# Copy shader files
1315-
build_shader_dir = build_visualizer_dir / 'shaders'
1316-
if build_shader_dir.exists():
1317-
target_shader_dir = target_base_dir / 'shaders'
1318-
target_shader_dir.mkdir(parents=True, exist_ok=True)
1319-
1320-
shader_count = 0
1321-
for shader_file in build_shader_dir.glob('*'):
1322-
if shader_file.is_file():
1323-
dest_file = target_shader_dir / shader_file.name
1324-
shutil.copy2(shader_file, dest_file)
1325-
shader_count += 1
1326-
print(f"Copied shader: {shader_file.name}")
1327-
1328-
total_files_copied += shader_count
1329-
if shader_count > 0:
1330-
print(f"[OK] Copied {shader_count} shader files")
1331-
1332-
# Copy texture files
1333-
build_texture_dir = build_visualizer_dir / 'textures'
1334-
if build_texture_dir.exists():
1335-
target_texture_dir = target_base_dir / 'textures'
1336-
target_texture_dir.mkdir(parents=True, exist_ok=True)
1337-
1338-
texture_count = 0
1339-
for texture_file in build_texture_dir.rglob('*'):
1340-
if texture_file.is_file():
1341-
# Preserve directory structure for textures
1342-
rel_path = texture_file.relative_to(build_texture_dir)
1343-
dest_file = target_texture_dir / rel_path
1344-
dest_file.parent.mkdir(parents=True, exist_ok=True)
1345-
shutil.copy2(texture_file, dest_file)
1346-
texture_count += 1
1347-
print(f"Copied texture: {rel_path}")
1348-
1349-
total_files_copied += texture_count
1350-
if texture_count > 0:
1351-
print(f"[OK] Copied {texture_count} texture files")
1352-
1353-
# Copy font files
1354-
build_font_dir = build_visualizer_dir / 'fonts'
1355-
if build_font_dir.exists():
1356-
target_font_dir = target_base_dir / 'fonts'
1357-
target_font_dir.mkdir(parents=True, exist_ok=True)
1358-
1359-
font_count = 0
1360-
for font_file in build_font_dir.rglob('*'):
1361-
if font_file.is_file():
1362-
# Preserve directory structure for fonts
1363-
rel_path = font_file.relative_to(build_font_dir)
1364-
dest_file = target_font_dir / rel_path
1365-
dest_file.parent.mkdir(parents=True, exist_ok=True)
1366-
shutil.copy2(font_file, dest_file)
1367-
font_count += 1
1368-
print(f"Copied font: {rel_path}")
1369-
1370-
total_files_copied += font_count
1371-
if font_count > 0:
1372-
print(f"[OK] Copied {font_count} font files")
1373-
1374-
if total_files_copied > 0:
1375-
print(f"[OK] Successfully copied {total_files_copied} visualizer assets to {target_base_dir}")
1376-
else:
1377-
print(f"[WARN] No visualizer assets found to copy")
1378-
1295+
13791296
def _clean_duplicate_symbols(self, main_lib_path: Path, build_lib_dir: Path) -> Dict[str, Any]:
13801297
"""
13811298
Clean duplicate symbols from static libraries by removing test objects.
@@ -1710,20 +1627,21 @@ def _patch_zlib_cmake_for_windows(self) -> None:
17101627
def get_default_plugins() -> List[str]:
17111628
"""
17121629
Get the default set of plugins (only the currently integrated plugins).
1713-
1630+
17141631
Currently integrated plugins in PyHelios:
17151632
- visualizer: OpenGL-based 3D visualization
17161633
- weberpenntree: Procedural tree generation
17171634
- radiation: OptiX-accelerated ray tracing (GPU optional)
17181635
- energybalance: GPU-accelerated thermal modeling and energy balance
17191636
- solarposition: Solar position calculations and sun angle modeling
17201637
- photosynthesis: Photosynthesis modeling and carbon assimilation
1721-
1638+
- plantarchitecture: Advanced plant structure and architecture modeling with procedural plant library
1639+
17221640
Returns:
17231641
List of default plugins
17241642
"""
17251643
# Return the plugins that are actually integrated into PyHelios
1726-
integrated_plugins = ["visualizer", "weberpenntree", "radiation", "energybalance", "solarposition", "stomatalconductance", "photosynthesis"]
1644+
integrated_plugins = INTEGRATED_PLUGINS
17271645

17281646
# Filter by platform compatibility
17291647
default_plugins = []
@@ -1843,7 +1761,7 @@ def interactive_plugin_selection() -> List[str]:
18431761
elif choice == "6":
18441762
print("\nAvailable plugins:")
18451763
compatible_plugins = get_platform_compatible_plugins()
1846-
integrated_plugins = ["visualizer", "weberpenntree", "radiation", "energybalance", "solarposition", "stomatalconductance", "photosynthesis"]
1764+
integrated_plugins = INTEGRATED_PLUGINS
18471765

18481766
for plugin in sorted(get_all_plugin_names()):
18491767
metadata = PLUGIN_METADATA[plugin]
@@ -1939,7 +1857,7 @@ def main():
19391857
print("PyHelios Integrated Plugins:")
19401858
print("=" * 30)
19411859
compatible_plugins = get_platform_compatible_plugins()
1942-
integrated_plugins = ["visualizer", "weberpenntree", "radiation", "energybalance", "solarposition", "stomatalconductance", "photosynthesis"]
1860+
integrated_plugins = INTEGRATED_PLUGINS
19431861

19441862
for plugin in sorted(integrated_plugins):
19451863
if plugin in PLUGIN_METADATA:
@@ -1964,7 +1882,7 @@ def main():
19641882
print("All Helios-Core Plugins:")
19651883
print("=" * 25)
19661884
compatible_plugins = get_platform_compatible_plugins()
1967-
integrated_plugins = ["visualizer", "weberpenntree", "radiation", "energybalance", "solarposition", "stomatalconductance", "photosynthesis"]
1885+
integrated_plugins = INTEGRATED_PLUGINS
19681886

19691887
for plugin in sorted(get_all_plugin_names()):
19701888
metadata = PLUGIN_METADATA[plugin]

β€Žbuild_scripts/prepare_wheel.pyβ€Ž

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ def copy_assets_for_packaging(project_root):
212212
plugin_asset_dirs = {
213213
'weberpenntree': ['leaves', 'wood', 'xml'],
214214
'visualizer': ['textures', 'shaders'],
215-
# NOTE: plantarchitecture and canopygenerator are not integrated with PyHelios - assets not needed
215+
'plantarchitecture': ['assets/textures', 'assets/obj'],
216+
# NOTE: canopygenerator is not integrated with PyHelios - assets not needed
216217
}
217218

218219
# Add radiation assets only on platforms that build GPU plugins (Windows/Linux)
@@ -273,11 +274,19 @@ def copy_assets_for_packaging(project_root):
273274
asset_src = plugin_dir / asset_dir_path
274275
if not asset_src.exists():
275276
continue
276-
277-
# Use just the final directory name for destination
278-
asset_dir_name = asset_dir_path.split('/')[-1]
279-
asset_dest = plugin_dest / asset_dir_name
280-
277+
278+
# For nested paths, preserve the directory structure
279+
if '/' in asset_dir_path:
280+
# Create nested destination path to preserve structure
281+
asset_dest = plugin_dest / asset_dir_path
282+
else:
283+
# Use just the final directory name for flat directories
284+
asset_dir_name = asset_dir_path.split('/')[-1]
285+
asset_dest = plugin_dest / asset_dir_name
286+
287+
# Ensure parent directories exist
288+
asset_dest.parent.mkdir(parents=True, exist_ok=True)
289+
281290
if asset_dest.exists():
282291
shutil.rmtree(asset_dest)
283292
shutil.copytree(asset_src, asset_dest)

β€Ždocs/CHANGELOG.mdβ€Ž

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

3+
# [v0.1.3] 2025-09-22
4+
5+
🚨++ New Plug-in Integrated ++ 🚨
6+
- Initial phase of plant architecture plug-in integrated with PyHelios. This includes basic functionality for building plants from the library.
7+
8+
- Updated Helios native C++ library to v1.3.50
9+
10+
*Improved Error Handling, Build System Optimization, and Testing Infrastructure*
11+
- **Context API**: Enhanced lifecycle state tracking with detailed error messages for better debugging
12+
- **Build System**: Streamlined asset management by removing redundant asset copying code and optimizing build process
13+
- **Testing**: Enhanced cross-platform test coverage with improved mock mode handling and context lifecycle testing
14+
- **Documentation**: Major updates to plugin integration guide with critical implementation patterns and best practices
15+
- **Visualizer**: Enhanced compatibility and error handling for cross-platform visualization workflows
16+
17+
## Build System
18+
- Removed redundant asset copying for visualizer and weberpenntree plugins (using environment variable approach)
19+
- Optimized build process with cleaner CMake integration
20+
- Enhanced cross-platform library validation with fail-fast behavior
21+
322
# [v0.1.2] 2025-09-18
423

524
πŸŽ‰PyPI package distribution should now be working for all integrated plug-ins πŸŽ‰

0 commit comments

Comments
Β (0)