"""Audit V10 WFE values - independent verification.""" import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent)) from optimization_engine.extractors import ZernikeOPDExtractor, ZernikeExtractor print('='*70) print('AUDIT: V10 WFE Values - Independent Verification') print('='*70) # V10 iter1 (baseline trial) op2_v10 = Path('studies/M1_Mirror/m1_mirror_cost_reduction_V10/2_iterations/iter1/assy_m1_assyfem1_sim1-solution_1.op2') if not op2_v10.exists(): print('V10 OP2 file not found!') sys.exit(1) print(f'OP2 file: {op2_v10}') print(f'Size: {op2_v10.stat().st_size / 1024 / 1024:.1f} MB') # Test with ZernikeOPDExtractor (what V10 uses) print() print('='*70) print('Method 1: ZernikeOPDExtractor (what V10 uses)') print('='*70) extractor_opd = ZernikeOPDExtractor(op2_v10, n_modes=50, filter_orders=4) result_20_opd = extractor_opd.extract_subcase('2') # Reference result_40_opd = extractor_opd.extract_subcase('3') # 40 deg result_60_opd = extractor_opd.extract_subcase('4') # 60 deg result_90_opd = extractor_opd.extract_subcase('1') # 90 deg MFG print() print('ABSOLUTE values (ZernikeOPD):') print(f' 20 deg: filtered_rms = {result_20_opd["filtered_rms_nm"]:.2f} nm') print(f' 40 deg: filtered_rms = {result_40_opd["filtered_rms_nm"]:.2f} nm') print(f' 60 deg: filtered_rms = {result_60_opd["filtered_rms_nm"]:.2f} nm') print(f' 90 deg: filtered_rms = {result_90_opd["filtered_rms_nm"]:.2f} nm') print() print('RELATIVE values (target - ref) as V10 computes:') rel_40_opd = result_40_opd['filtered_rms_nm'] - result_20_opd['filtered_rms_nm'] rel_60_opd = result_60_opd['filtered_rms_nm'] - result_20_opd['filtered_rms_nm'] rel_mfg_opd = result_90_opd['rms_filter_j1to3_nm'] - result_20_opd['rms_filter_j1to3_nm'] print(f' 40-20: {rel_40_opd:.2f} nm (abs: {abs(rel_40_opd):.2f})') print(f' 60-20: {rel_60_opd:.2f} nm (abs: {abs(rel_60_opd):.2f})') print(f' 90-20 (j1to3): {rel_mfg_opd:.2f} nm (abs: {abs(rel_mfg_opd):.2f})') print() print('V10 uses abs() -> stores:') print(f' rel_filtered_rms_40_vs_20: {abs(rel_40_opd):.2f}') print(f' rel_filtered_rms_60_vs_20: {abs(rel_60_opd):.2f}') print(f' mfg_90_optician_workload: {abs(rel_mfg_opd):.2f}') # Test with Standard ZernikeExtractor (what V9 uses) print() print('='*70) print('Method 2: Standard ZernikeExtractor (what V9 likely uses)') print('='*70) # Find the BDF file bdf_files = list(op2_v10.parent.glob('*.dat')) bdf_path = bdf_files[0] if bdf_files else None print(f'BDF file: {bdf_path}') extractor_std = ZernikeExtractor(op2_v10, bdf_path=bdf_path, n_modes=50, filter_orders=4) result_20_std = extractor_std.extract_subcase('2') result_40_std = extractor_std.extract_subcase('3') result_60_std = extractor_std.extract_subcase('4') result_90_std = extractor_std.extract_subcase('1') print() print('ABSOLUTE values (Standard Z-only):') print(f' 20 deg: filtered_rms = {result_20_std["filtered_rms_nm"]:.2f} nm') print(f' 40 deg: filtered_rms = {result_40_std["filtered_rms_nm"]:.2f} nm') print(f' 60 deg: filtered_rms = {result_60_std["filtered_rms_nm"]:.2f} nm') print(f' 90 deg: filtered_rms = {result_90_std["filtered_rms_nm"]:.2f} nm') print() print('RELATIVE values (Standard):') rel_40_std = result_40_std['filtered_rms_nm'] - result_20_std['filtered_rms_nm'] rel_60_std = result_60_std['filtered_rms_nm'] - result_20_std['filtered_rms_nm'] print(f' 40-20: {rel_40_std:.2f} nm (abs: {abs(rel_40_std):.2f})') print(f' 60-20: {rel_60_std:.2f} nm (abs: {abs(rel_60_std):.2f})') # Compare print() print('='*70) print('COMPARISON: OPD vs Standard') print('='*70) print() print(f'40-20: OPD={abs(rel_40_opd):.2f} nm vs Standard={abs(rel_40_std):.2f} nm') print(f'60-20: OPD={abs(rel_60_opd):.2f} nm vs Standard={abs(rel_60_std):.2f} nm') print() print('Lateral displacement (OPD method):') print(f' Max: {result_40_opd.get("max_lateral_displacement_um", 0):.3f} um') print(f' RMS: {result_40_opd.get("rms_lateral_displacement_um", 0):.3f} um') # Now check what V9 reports print() print('='*70) print('V9 COMPARISON (iter12 from best archive)') print('='*70) op2_v9 = Path('studies/M1_Mirror/m1_mirror_cost_reduction_V9/2_iterations/iter12/assy_m1_assyfem1_sim1-solution_1.op2') if op2_v9.exists(): extractor_v9_opd = ZernikeOPDExtractor(op2_v9, n_modes=50, filter_orders=4) extractor_v9_std = ZernikeExtractor(op2_v9, n_modes=50, filter_orders=4) r20_v9_opd = extractor_v9_opd.extract_subcase('2') r40_v9_opd = extractor_v9_opd.extract_subcase('3') r60_v9_opd = extractor_v9_opd.extract_subcase('4') r20_v9_std = extractor_v9_std.extract_subcase('2') r40_v9_std = extractor_v9_std.extract_subcase('3') r60_v9_std = extractor_v9_std.extract_subcase('4') rel_40_v9_opd = abs(r40_v9_opd['filtered_rms_nm'] - r20_v9_opd['filtered_rms_nm']) rel_60_v9_opd = abs(r60_v9_opd['filtered_rms_nm'] - r20_v9_opd['filtered_rms_nm']) rel_40_v9_std = abs(r40_v9_std['filtered_rms_nm'] - r20_v9_std['filtered_rms_nm']) rel_60_v9_std = abs(r60_v9_std['filtered_rms_nm'] - r20_v9_std['filtered_rms_nm']) print() print('V9 iter12 relative values:') print(f' 40-20: OPD={rel_40_v9_opd:.2f} nm vs Standard={rel_40_v9_std:.2f} nm') print(f' 60-20: OPD={rel_60_v9_opd:.2f} nm vs Standard={rel_60_v9_std:.2f} nm') else: print('V9 OP2 not found') print() print('='*70) print('SUMMARY') print('='*70) print() print('V10 reports: 40-20=1.99nm, 60-20=6.82nm (using OPD method)') print('V9 reports: 40-20=6.10nm, 60-20=12.76nm (likely Standard method)') print() print('If both studies have SIMILAR geometry, the OPD method should NOT') print('give such dramatically different values. This needs investigation.')