feat: Add Study Insights module (SYS_16) for physics visualizations
Introduces a new plugin architecture for study-specific physics visualizations, separating "optimizer perspective" (Analysis) from "engineer perspective" (Insights). New module: optimization_engine/insights/ - base.py: StudyInsight base class, InsightConfig, InsightResult, registry - zernike_wfe.py: Mirror WFE with 3D surface and Zernike decomposition - stress_field.py: Von Mises stress contours with safety factors - modal_analysis.py: Natural frequencies and mode shapes - thermal_field.py: Temperature distribution visualization - design_space.py: Parameter-objective landscape exploration Features: - 5 insight types: zernike_wfe, stress_field, modal, thermal, design_space - CLI: python -m optimization_engine.insights generate <study> - Standalone HTML generation with Plotly - Enhanced Zernike viz: Turbo colorscale, smooth shading, 0.5x AMP - Dashboard API fix: Added include_coefficients param to extract_relative() Documentation: - docs/protocols/system/SYS_16_STUDY_INSIGHTS.md - Updated ATOMIZER_CONTEXT.md (v1.7) - Updated 01_CHEATSHEET.md with insights section Tools: - tools/zernike_html_generator.py: Standalone WFE HTML generator - tools/analyze_wfe.bat: Double-click to analyze OP2 files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
233
optimization_engine/insights/__init__.py
Normal file
233
optimization_engine/insights/__init__.py
Normal file
@@ -0,0 +1,233 @@
|
||||
"""
|
||||
Atomizer Study Insights Module
|
||||
|
||||
Provides physics-focused visualizations for FEA optimization results.
|
||||
Unlike the Analysis page (optimizer-centric), Insights show the engineering
|
||||
reality of specific designs through interactive 3D visualizations.
|
||||
|
||||
Architecture:
|
||||
- StudyInsight: Abstract base class for all insight types
|
||||
- InsightRegistry: Central registry for available insight types
|
||||
- Each insight generates standalone HTML or Plotly data for dashboard
|
||||
|
||||
Available Insight Types:
|
||||
-----------------------
|
||||
| Type ID | Name | Description |
|
||||
|----------------|------------------------|------------------------------------------|
|
||||
| zernike_wfe | Zernike WFE Analysis | 3D wavefront error with Zernike decomp |
|
||||
| stress_field | Stress Distribution | Von Mises stress contours |
|
||||
| modal | Modal Analysis | Natural frequencies and mode shapes |
|
||||
| thermal | Thermal Analysis | Temperature distribution |
|
||||
| design_space | Design Space Explorer | Parameter-objective relationships |
|
||||
|
||||
Quick Start:
|
||||
-----------
|
||||
```python
|
||||
from optimization_engine.insights import get_insight, list_available_insights
|
||||
from pathlib import Path
|
||||
|
||||
study_path = Path("studies/my_study")
|
||||
|
||||
# List available insights for a study
|
||||
available = list_available_insights(study_path)
|
||||
print(available)
|
||||
|
||||
# Generate a specific insight
|
||||
insight = get_insight('zernike_wfe', study_path)
|
||||
if insight and insight.can_generate():
|
||||
result = insight.generate()
|
||||
print(f"Generated: {result.html_path}")
|
||||
print(f"Summary: {result.summary}")
|
||||
```
|
||||
|
||||
CLI Usage:
|
||||
---------
|
||||
```bash
|
||||
# Generate all available insights for a study
|
||||
python -m optimization_engine.insights generate studies/my_study
|
||||
|
||||
# Generate specific insight type
|
||||
python -m optimization_engine.insights generate studies/my_study --type zernike_wfe
|
||||
|
||||
# List available insight types
|
||||
python -m optimization_engine.insights list
|
||||
```
|
||||
"""
|
||||
|
||||
# Import base classes first
|
||||
from .base import (
|
||||
StudyInsight,
|
||||
InsightConfig,
|
||||
InsightResult,
|
||||
InsightRegistry,
|
||||
register_insight,
|
||||
get_insight,
|
||||
list_insights,
|
||||
list_available_insights,
|
||||
)
|
||||
|
||||
# Import insight implementations (triggers @register_insight decorators)
|
||||
from .zernike_wfe import ZernikeWFEInsight
|
||||
from .stress_field import StressFieldInsight
|
||||
from .modal_analysis import ModalInsight
|
||||
from .thermal_field import ThermalInsight
|
||||
from .design_space import DesignSpaceInsight
|
||||
|
||||
# Public API
|
||||
__all__ = [
|
||||
# Base classes
|
||||
'StudyInsight',
|
||||
'InsightConfig',
|
||||
'InsightResult',
|
||||
'InsightRegistry',
|
||||
'register_insight',
|
||||
|
||||
# API functions
|
||||
'get_insight',
|
||||
'list_insights',
|
||||
'list_available_insights',
|
||||
|
||||
# Insight implementations
|
||||
'ZernikeWFEInsight',
|
||||
'StressFieldInsight',
|
||||
'ModalInsight',
|
||||
'ThermalInsight',
|
||||
'DesignSpaceInsight',
|
||||
]
|
||||
|
||||
|
||||
def generate_all_insights(study_path, output_dir=None):
|
||||
"""
|
||||
Generate all available insights for a study.
|
||||
|
||||
Args:
|
||||
study_path: Path to study directory
|
||||
output_dir: Optional output directory (defaults to study/3_insights/)
|
||||
|
||||
Returns:
|
||||
List of InsightResult objects
|
||||
"""
|
||||
from pathlib import Path
|
||||
study_path = Path(study_path)
|
||||
|
||||
results = []
|
||||
available = list_available_insights(study_path)
|
||||
|
||||
for info in available:
|
||||
insight = get_insight(info['type'], study_path)
|
||||
if insight:
|
||||
config = InsightConfig()
|
||||
if output_dir:
|
||||
config.output_dir = Path(output_dir)
|
||||
|
||||
result = insight.generate(config)
|
||||
results.append({
|
||||
'type': info['type'],
|
||||
'name': info['name'],
|
||||
'result': result
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
# CLI entry point
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
def print_usage():
|
||||
print("Atomizer Study Insights")
|
||||
print("=" * 50)
|
||||
print()
|
||||
print("Usage:")
|
||||
print(" python -m optimization_engine.insights list")
|
||||
print(" python -m optimization_engine.insights generate <study_path> [--type TYPE]")
|
||||
print()
|
||||
print("Commands:")
|
||||
print(" list - List all registered insight types")
|
||||
print(" generate - Generate insights for a study")
|
||||
print()
|
||||
print("Options:")
|
||||
print(" --type TYPE Generate only the specified insight type")
|
||||
print()
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print_usage()
|
||||
sys.exit(0)
|
||||
|
||||
command = sys.argv[1]
|
||||
|
||||
if command == 'list':
|
||||
print("\nRegistered Insight Types:")
|
||||
print("-" * 60)
|
||||
for info in list_insights():
|
||||
print(f" {info['type']:15} - {info['name']}")
|
||||
print(f" {info['description']}")
|
||||
print(f" Applies to: {', '.join(info['applicable_to'])}")
|
||||
print()
|
||||
|
||||
elif command == 'generate':
|
||||
if len(sys.argv) < 3:
|
||||
print("Error: Missing study path")
|
||||
print_usage()
|
||||
sys.exit(1)
|
||||
|
||||
study_path = Path(sys.argv[2])
|
||||
if not study_path.exists():
|
||||
print(f"Error: Study path does not exist: {study_path}")
|
||||
sys.exit(1)
|
||||
|
||||
# Parse options
|
||||
insight_type = None
|
||||
for i, arg in enumerate(sys.argv[3:], 3):
|
||||
if arg == '--type' and i + 1 < len(sys.argv):
|
||||
insight_type = sys.argv[i + 1]
|
||||
|
||||
print(f"\nGenerating insights for: {study_path}")
|
||||
print("-" * 60)
|
||||
|
||||
if insight_type:
|
||||
# Generate specific type
|
||||
insight = get_insight(insight_type, study_path)
|
||||
if insight is None:
|
||||
print(f"Error: Unknown insight type: {insight_type}")
|
||||
sys.exit(1)
|
||||
|
||||
if not insight.can_generate():
|
||||
print(f"Cannot generate {insight_type}: required data not found")
|
||||
sys.exit(1)
|
||||
|
||||
result = insight.generate()
|
||||
if result.success:
|
||||
print(f"Generated: {result.html_path}")
|
||||
if result.summary:
|
||||
print(f"Summary: {result.summary}")
|
||||
else:
|
||||
print(f"Error: {result.error}")
|
||||
else:
|
||||
# Generate all available
|
||||
available = list_available_insights(study_path)
|
||||
if not available:
|
||||
print("No insights available for this study")
|
||||
sys.exit(0)
|
||||
|
||||
print(f"Found {len(available)} available insight(s)")
|
||||
print()
|
||||
|
||||
for info in available:
|
||||
print(f"Generating {info['name']}...")
|
||||
insight = get_insight(info['type'], study_path)
|
||||
result = insight.generate()
|
||||
|
||||
if result.success:
|
||||
print(f" Created: {result.html_path}")
|
||||
else:
|
||||
print(f" Error: {result.error}")
|
||||
|
||||
print()
|
||||
print("Done!")
|
||||
|
||||
else:
|
||||
print(f"Unknown command: {command}")
|
||||
print_usage()
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user