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:
2025-12-20 13:46:28 -05:00
parent 01a7d7d121
commit 1612991d0d
15 changed files with 4450 additions and 173 deletions

View File

@@ -656,7 +656,8 @@ class ZernikeExtractor:
def extract_relative(
self,
target_subcase: str,
reference_subcase: str
reference_subcase: str,
include_coefficients: bool = False
) -> Dict[str, Any]:
"""
Extract Zernike metrics relative to a reference subcase.
@@ -666,6 +667,7 @@ class ZernikeExtractor:
Args:
target_subcase: Subcase to analyze
reference_subcase: Reference subcase to subtract
include_coefficients: Whether to include all Zernike coefficients
Returns:
Dict with relative RMS metrics and aberrations
@@ -711,7 +713,7 @@ class ZernikeExtractor:
)
aberrations = compute_aberration_magnitudes(rms_result['coefficients'])
return {
result = {
'target_subcase': target_subcase,
'reference_subcase': reference_subcase,
'relative_global_rms_nm': rms_result['global_rms_nm'],
@@ -721,6 +723,12 @@ class ZernikeExtractor:
**{f'relative_{k}': v for k, v in aberrations.items()}
}
if include_coefficients:
result['coefficients'] = rms_result['coefficients'].tolist()
result['coefficient_labels'] = [zernike_label(j) for j in range(1, self.n_modes + 1)]
return result
def extract_all_subcases(
self,
reference_subcase: Optional[str] = '20'