Files
Atomizer/tests/test_zernike_opd_comparison.py
Anto01 f13563d7ab feat: Major update - Physics docs, Zernike OPD, insights, NX journals, tools
Documentation:
- Add docs/06_PHYSICS/ with Zernike fundamentals and OPD method docs
- Add docs/guides/CMA-ES_EXPLAINED.md optimization guide
- Update CLAUDE.md and ATOMIZER_CONTEXT.md with current architecture
- Update OP_01_CREATE_STUDY protocol

Planning:
- Add DYNAMIC_RESPONSE plans for random vibration/PSD support
- Add OPTIMIZATION_ENGINE_MIGRATION_PLAN for code reorganization

Insights System:
- Update design_space, modal_analysis, stress_field, thermal_field insights
- Improve error handling and data validation

NX Journals:
- Add analyze_wfe_zernike.py for Zernike WFE analysis
- Add capture_study_images.py for automated screenshots
- Add extract_expressions.py and introspect_part.py utilities
- Add user_generated_journals/journal_top_view_image_taking.py

Tests & Tools:
- Add comprehensive Zernike OPD test suite
- Add audit_v10 tests for WFE validation
- Add tools for Pareto graphs and mirror data extraction
- Add migrate_studies_to_topics.py utility

Knowledge Base:
- Initialize LAC (Learning Atomizer Core) with failure/success patterns

Dashboard:
- Update Setup.tsx and launch_dashboard.py
- Add restart-dev.bat helper script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 19:47:37 -05:00

96 lines
3.2 KiB
Python

#!/usr/bin/env python
"""
Quick test script to compare Standard vs OPD Zernike methods.
Usage:
conda activate atomizer
python test_zernike_opd_comparison.py
This will analyze a recent OP2 file and show you:
1. How much lateral displacement exists
2. How different the WFE metrics are between methods
3. Whether you need to switch to OPD method for your optimizations
"""
from pathlib import Path
import sys
# Add project root to path
sys.path.insert(0, str(Path(__file__).parent))
def main():
import numpy as np
from optimization_engine.extractors.extract_zernike_opd import (
ZernikeOPDExtractor,
)
# Find a recent OP2 file from your studies
studies_path = Path("studies/M1_Mirror")
op2_files = list(studies_path.glob("**/2_iterations/**/*.op2"))
if not op2_files:
op2_files = list(studies_path.glob("**/*.op2"))
if not op2_files:
print("No OP2 files found in studies/M1_Mirror")
return
# Use the most recent one
op2_file = max(op2_files, key=lambda p: p.stat().st_mtime)
print(f"Analyzing: {op2_file}")
print("=" * 80)
# Run comparison
try:
extractor = ZernikeOPDExtractor(op2_file)
print(f"\nAvailable subcases: {list(extractor.displacements.keys())}")
# Show geometry info
geo = extractor.node_geometry
all_pos = np.array(list(geo.values()))
print(f"\n--- Geometry Info ---")
print(f" Nodes: {len(geo)}")
print(f" X range: {all_pos[:,0].min():.1f} to {all_pos[:,0].max():.1f} mm")
print(f" Y range: {all_pos[:,1].min():.1f} to {all_pos[:,1].max():.1f} mm")
print(f" Z range: {all_pos[:,2].min():.1f} to {all_pos[:,2].max():.1f} mm")
for label in extractor.displacements.keys():
print(f"\n{'=' * 80}")
print(f"SUBCASE {label}")
print('=' * 80)
comparison = extractor.extract_comparison(label)
print(f"\n--- Standard Method (Z-only) ---")
print(f" Global RMS: {comparison['standard_method']['global_rms_nm']:.2f} nm")
print(f" Filtered RMS: {comparison['standard_method']['filtered_rms_nm']:.2f} nm")
print(f"\n--- Rigorous OPD Method ---")
print(f" Global RMS: {comparison['opd_method']['global_rms_nm']:.2f} nm")
print(f" Filtered RMS: {comparison['opd_method']['filtered_rms_nm']:.2f} nm")
print(f"\n--- Difference (OPD - Standard) ---")
delta = comparison['delta']['filtered_rms_nm']
pct = comparison['delta']['percent_difference_filtered']
sign = "+" if delta > 0 else ""
print(f" Filtered RMS: {sign}{delta:.2f} nm ({sign}{pct:.1f}%)")
print(f"\n--- Lateral Displacement ---")
print(f" Max: {comparison['lateral_displacement']['max_um']:.3f} µm")
print(f" RMS: {comparison['lateral_displacement']['rms_um']:.3f} µm")
print(f" P99: {comparison['lateral_displacement']['p99_um']:.3f} µm")
print(f"\n>>> {comparison['recommendation']}")
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
return
if __name__ == '__main__':
main()