""" 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 [--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)