feat: Add post-optimization tools and mandatory best design archiving

New Tools (tools/):
- analyze_study.py: Generate comprehensive optimization reports
- find_best_iteration.py: Find best iteration folder, optionally copy it
- archive_best_design.py: Archive best design to 3_results/best_design_archive/<timestamp>/

Protocol Updates:
- OP_02_RUN_OPTIMIZATION.md v1.1: Add mandatory archive_best_design step
  in Post-Run Actions. This MUST be done after every optimization run.

V14 Updates:
- run_optimization.py: Auto-archive best design at end of optimization
- optimization_config.json: Expand bounds for V14 continuation
  - lateral_outer_angle: min 13->11 deg (was at 4.7%)
  - lateral_inner_pivot: min 7->5 mm (was at 8.1%)
  - lateral_middle_pivot: max 23->27 mm (was at 99.4%)
  - whiffle_min: max 60->72 mm (was at 96.3%)

Usage:
  python tools/analyze_study.py m1_mirror_adaptive_V14
  python tools/find_best_iteration.py m1_mirror_adaptive_V14
  python tools/archive_best_design.py m1_mirror_adaptive_V14

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Antoine
2025-12-12 10:28:35 -05:00
parent 70ac34e3d3
commit 1bb201e0b7
6 changed files with 913 additions and 15 deletions

View File

@@ -600,6 +600,32 @@ class TPEOptimizer:
logger.info(f"\nResults saved to {RESULTS_DIR / 'final_results.json'}")
# Archive best design
self._archive_best_design()
def _archive_best_design(self):
"""Archive the best design iteration folder."""
try:
# Import archive tool
tools_dir = Path(__file__).parent.parent.parent / "tools"
sys.path.insert(0, str(tools_dir))
from archive_best_design import archive_best_design
logger.info("\n" + "-" * 70)
logger.info("ARCHIVING BEST DESIGN")
logger.info("-" * 70)
result = archive_best_design(str(Path(__file__).parent))
if result.get('success'):
logger.info(f"[OK] Best design archived to: {result['archive_path']}")
logger.info(f" Trial #{result['trial_number']}, WS={result['weighted_sum']:.2f}")
else:
logger.warning(f"[WARN] Archive skipped: {result.get('reason', 'Unknown')}")
except Exception as e:
logger.error(f"[ERROR] Failed to archive best design: {e}")
# ============================================================================
# Main