docs: Complete M1 mirror optimization campaign V11-V15

## M1 Mirror Campaign Summary
- V11-V15 optimization campaign completed (~1,400 FEA evaluations)
- Best design: V14 Trial #725 with Weighted Sum = 121.72
- V15 NSGA-II confirmed V14 TPE found optimal solution
- Campaign improved from WS=129.33 (V11) to WS=121.72 (V14): -5.9%

## Key Results
- 40° tracking: 5.99 nm (target 4.0 nm)
- 60° tracking: 13.10 nm (target 10.0 nm)
- Manufacturing: 26.28 nm (target 20.0 nm)
- Targets not achievable within current design space

## Documentation Added
- V15 STUDY_REPORT.md: Detailed NSGA-II results analysis
- M1_MIRROR_CAMPAIGN_SUMMARY.md: Full V11-V15 campaign overview
- Updated CLAUDE.md, ATOMIZER_CONTEXT.md with NXSolver patterns
- Updated 01_CHEATSHEET.md with --resume guidance
- Updated OP_01_CREATE_STUDY.md with FEARunner template

## Studies Added
- m1_mirror_adaptive_V13: TPE validation (291 trials)
- m1_mirror_adaptive_V14: TPE intensive (785 trials, BEST)
- m1_mirror_adaptive_V15: NSGA-II exploration (126 new FEA)

🤖 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-16 14:55:23 -05:00
parent d1261d62fd
commit 01a7d7d121
88 changed files with 2574 additions and 62 deletions

View File

@@ -197,62 +197,132 @@ Create `optimization_config.json`:
### Step 6: Generate run_optimization.py
**CRITICAL**: Always use the `FEARunner` class pattern with proper `NXSolver` initialization.
```python
#!/usr/bin/env python
#!/usr/bin/env python3
"""
{study_name} - Optimization Runner
Generated by Atomizer LLM
"""
import sys
import re
import json
from pathlib import Path
from typing import Dict, Optional, Any
# Add optimization engine to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
import optuna
from optimization_engine.nx_solver import NXSolver
from optimization_engine.extractors import extract_displacement, extract_solid_stress
from optimization_engine.utils import ensure_nx_running
from optimization_engine.extractors import extract_solid_stress
# Paths
STUDY_DIR = Path(__file__).parent
MODEL_DIR = STUDY_DIR / "1_setup" / "model"
RESULTS_DIR = STUDY_DIR / "2_results"
SETUP_DIR = STUDY_DIR / "1_setup"
ITERATIONS_DIR = STUDY_DIR / "2_iterations"
RESULTS_DIR = STUDY_DIR / "3_results"
CONFIG_PATH = SETUP_DIR / "optimization_config.json"
def objective(trial):
"""Optimization objective function."""
# Sample design variables
thickness = trial.suggest_float("thickness", 2.0, 10.0)
# Ensure directories exist
ITERATIONS_DIR.mkdir(exist_ok=True)
RESULTS_DIR.mkdir(exist_ok=True)
# Update NX model and solve
nx_solver = NXSolver(...)
result = nx_solver.run_simulation(
sim_file=MODEL_DIR / "bracket.sim",
working_dir=MODEL_DIR,
expression_updates={"thickness": thickness}
)
if not result['success']:
raise optuna.TrialPruned("Simulation failed")
class FEARunner:
"""Runs actual FEA simulations. Always use this pattern!"""
# Extract results using library extractors
op2_file = result['op2_file']
stress_result = extract_solid_stress(op2_file)
max_stress = stress_result['max_von_mises']
def __init__(self, config: Dict[str, Any]):
self.config = config
self.nx_solver = None
self.nx_manager = None
self.master_model_dir = SETUP_DIR / "model"
# Check constraint
if max_stress > 250.0:
raise optuna.TrialPruned(f"Stress constraint violated: {max_stress} MPa")
def setup(self):
"""Setup NX and solver. Called lazily on first use."""
study_name = self.config.get('study_name', 'my_study')
# Return objective
mass = extract_mass(...)
return mass
# Ensure NX is running
self.nx_manager, nx_was_started = ensure_nx_running(
session_id=study_name,
auto_start=True,
start_timeout=120
)
if __name__ == "__main__":
# Run optimization
import optuna
study = optuna.create_study(direction="minimize")
study.optimize(objective, n_trials=50)
# CRITICAL: Initialize NXSolver with named parameters, NOT config dict
nx_settings = self.config.get('nx_settings', {})
nx_install_dir = nx_settings.get('nx_install_path', 'C:\\Program Files\\Siemens\\NX2506')
# Extract version from path
version_match = re.search(r'NX(\d+)', nx_install_dir)
nastran_version = version_match.group(1) if version_match else "2506"
self.nx_solver = NXSolver(
master_model_dir=str(self.master_model_dir),
nx_install_dir=nx_install_dir,
nastran_version=nastran_version,
timeout=nx_settings.get('simulation_timeout_s', 600),
use_iteration_folders=True,
study_name=study_name
)
def run_fea(self, params: Dict[str, float], iter_num: int) -> Optional[Dict]:
"""Run FEA simulation and extract results."""
if self.nx_solver is None:
self.setup()
# Create expression updates
expressions = {var['expression_name']: params[var['name']]
for var in self.config['design_variables']}
# Create iteration folder with model copies
iter_folder = self.nx_solver.create_iteration_folder(
iterations_base_dir=ITERATIONS_DIR,
iteration_number=iter_num,
expression_updates=expressions
)
# Run simulation
nx_settings = self.config.get('nx_settings', {})
sim_file = iter_folder / nx_settings.get('sim_file', 'model.sim')
result = self.nx_solver.run_simulation(
sim_file=sim_file,
working_dir=iter_folder,
expression_updates=expressions,
solution_name=nx_settings.get('solution_name', 'Solution 1'),
cleanup=False
)
if not result['success']:
return None
# Extract results
op2_file = result['op2_file']
stress_result = extract_solid_stress(op2_file)
return {
'params': params,
'max_stress': stress_result['max_von_mises'],
'op2_file': op2_file
}
# Optimizer class would use FEARunner...
# See m1_mirror_adaptive_V14/run_optimization.py for full example
```
**WRONG** - causes `TypeError: expected str, bytes or os.PathLike object, not dict`:
```python
self.nx_solver = NXSolver(self.config) # ❌ NEVER DO THIS
```
**Reference implementations**:
- `studies/m1_mirror_adaptive_V14/run_optimization.py` (TPE single-objective)
- `studies/m1_mirror_adaptive_V15/run_optimization.py` (NSGA-II multi-objective)
### Step 7: Generate Documentation
**README.md** (11 sections required):
@@ -400,4 +470,5 @@ Generated config:
| Version | Date | Changes |
|---------|------|---------|
| 1.1 | 2025-12-12 | Added FEARunner class pattern, NXSolver initialization warning |
| 1.0 | 2025-12-05 | Initial release |