Files
Atomizer/tools/analysis/audit_v10_method_diff.py
Anto01 a3f18dc377 chore: Project cleanup and Canvas UX improvements (Phase 7-9)
## Cleanup (v0.5.0)
- Delete 102+ orphaned MCP session temp files
- Remove build artifacts (htmlcov, dist, __pycache__)
- Archive superseded plan docs (RALPH_LOOP V2/V3, CANVAS V3, etc.)
- Move debug/analysis scripts from tests/ to tools/analysis/
- Archive redundant NX journals to archive/nx_journals/
- Archive monolithic PROTOCOL.md to docs/archive/
- Update .gitignore with missing patterns
- Clean old study files (optimization_log_old.txt, run_optimization_old.py)

## Canvas UX (Phases 7-9)
- Phase 7: Resizable panels with localStorage persistence
  - Left sidebar: 200-400px, Right panel: 280-600px
  - New useResizablePanel hook and ResizeHandle component
- Phase 8: Enable all palette items
  - All 8 node types now draggable
  - Singleton logic for model/solver/algorithm/surrogate
- Phase 9: Solver configuration
  - Add SolverEngine type (nxnastran, mscnastran, python, etc.)
  - Add NastranSolutionType (SOL101-SOL200)
  - Engine/solution dropdowns in config panel
  - Python script path support

## Documentation
- Update CHANGELOG.md with recent versions
- Update docs/00_INDEX.md
- Create examples/README.md
- Add docs/plans/CANVAS_UX_IMPROVEMENTS.md
2026-01-24 15:17:34 -05:00

73 lines
2.7 KiB
Python

"""Compare V9 vs V10 calculation methods."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from optimization_engine.extractors import ZernikeExtractor
op2 = Path('studies/M1_Mirror/m1_mirror_cost_reduction_V10/2_iterations/iter1/assy_m1_assyfem1_sim1-solution_1.op2')
extractor = ZernikeExtractor(op2, n_modes=50, filter_orders=4)
print("="*70)
print("CRITICAL: V9 vs V10 Calculation Method Comparison")
print("="*70)
print()
# This is what V9 does - computes relative WFE THEN fits Zernike
rel_40 = extractor.extract_relative('3', '2')
rel_60 = extractor.extract_relative('4', '2')
rel_90 = extractor.extract_relative('1', '2')
print('V9 method (ZernikeExtractor.extract_relative):')
print(' Computes WFE_diff = WFE_target - WFE_ref node-by-node')
print(' Then fits Zernike to WFE_diff')
print()
print(f' 40-20: {rel_40["relative_filtered_rms_nm"]:.2f} nm')
print(f' 60-20: {rel_60["relative_filtered_rms_nm"]:.2f} nm')
print(f' 90-20 (j1to3): {rel_90["relative_rms_filter_j1to3"]:.2f} nm')
# Individual absolute values
r20 = extractor.extract_subcase('2')
r40 = extractor.extract_subcase('3')
r60 = extractor.extract_subcase('4')
r90 = extractor.extract_subcase('1')
print()
print('='*70)
print('Individual absolute RMS values:')
print('='*70)
print(f' 20 deg: {r20["filtered_rms_nm"]:.2f} nm')
print(f' 40 deg: {r40["filtered_rms_nm"]:.2f} nm')
print(f' 60 deg: {r60["filtered_rms_nm"]:.2f} nm')
print(f' 90 deg: {r90["filtered_rms_nm"]:.2f} nm')
print()
print('='*70)
print('V10 method (WRONG - difference of RMS values):')
print(' Computes RMS_target - RMS_ref')
print(' This is NOT the same as RMS of the difference!')
print('='*70)
print()
print(f' 40-20: {r40["filtered_rms_nm"] - r20["filtered_rms_nm"]:.2f} nm')
print(f' 60-20: {r60["filtered_rms_nm"] - r20["filtered_rms_nm"]:.2f} nm')
print(f' After abs(): {abs(r40["filtered_rms_nm"] - r20["filtered_rms_nm"]):.2f} nm')
print(f' After abs(): {abs(r60["filtered_rms_nm"] - r20["filtered_rms_nm"]):.2f} nm')
print()
print('='*70)
print('CONCLUSION')
print('='*70)
print()
print('V10 BUG: Computes abs(RMS_target - RMS_ref) instead of RMS(WFE_target - WFE_ref)')
print()
print('The CORRECT relative WFE (from V9 method):')
print(f' 40-20: {rel_40["relative_filtered_rms_nm"]:.2f} nm')
print(f' 60-20: {rel_60["relative_filtered_rms_nm"]:.2f} nm')
print(f' 90-20: {rel_90["relative_rms_filter_j1to3"]:.2f} nm')
print()
print('The WRONG values V10 reports:')
print(f' 40-20: {abs(r40["filtered_rms_nm"] - r20["filtered_rms_nm"]):.2f} nm')
print(f' 60-20: {abs(r60["filtered_rms_nm"] - r20["filtered_rms_nm"]):.2f} nm')
print()
print('V10 values are ~3-4x LOWER than correct values!')