96 lines
3.2 KiB
Python
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()
|