Added JSON Schema: - optimization_engine/schemas/atomizer_spec_v2.json Migrated 28 studies to AtomizerSpec v2.0 format: - Drone_Gimbal studies (1) - M1_Mirror studies (21) - M2_Mirror studies (2) - SheetMetal_Bracket studies (4) Each atomizer_spec.json is the unified configuration containing: - Design variables with bounds and expressions - Extractors (standard and custom) - Objectives and constraints - Optimization algorithm settings - Canvas layout information
M1 Mirror Adaptive Surrogate Optimization V12
Adaptive neural-accelerated optimization of telescope primary mirror (M1) support structure using Zernike wavefront error decomposition with auto-tuned hyperparameters, ensemble surrogates, and mass constraints.
Created: 2024-12-04 Protocol: Protocol 12 (Adaptive Hybrid FEA/Neural with Hyperparameter Tuning) Status: Running Source Data: V11 (107 FEA samples)
1. Engineering Problem
1.1 Objective
Optimize the telescope primary mirror (M1) whiffle tree and lateral support structure to minimize wavefront error (WFE) across different gravity orientations while maintaining mass under 99 kg.
1.2 Physical System
| Property | Value |
|---|---|
| Component | M1 primary mirror assembly with whiffle tree support |
| Material | Borosilicate glass (mirror blank), steel (support structure) |
| Loading | Gravity at multiple zenith angles (90°, 20°, 40°, 60°) |
| Boundary Conditions | Whiffle tree kinematic mount with lateral supports |
| Analysis Type | Linear static multi-subcase (Nastran SOL 101) |
| Subcases | 4 orientations with different gravity vectors |
| Output | Surface deformation → Zernike polynomial decomposition |
1.3 Key Improvements in V12
| Feature | V11 | V12 |
|---|---|---|
| Hyperparameter Tuning | Fixed architecture | Optuna auto-tuning |
| Model Architecture | Single network | Ensemble of 3 models |
| Validation | Train/test split | K-fold cross-validation |
| Mass Constraint | Post-hoc check | Integrated penalty |
| Convergence | Fixed iterations | Early stopping with patience |
2. Mathematical Formulation
2.1 Objectives
| Objective | Goal | Weight | Formula | Units | Target |
|---|---|---|---|---|---|
rel_filtered_rms_40_vs_20 |
minimize | 5.0 | \sigma_{40/20} = \sqrt{\sum_{j=5}^{50} (Z_j^{40} - Z_j^{20})^2} |
nm | 4 nm |
rel_filtered_rms_60_vs_20 |
minimize | 5.0 | \sigma_{60/20} = \sqrt{\sum_{j=5}^{50} (Z_j^{60} - Z_j^{20})^2} |
nm | 10 nm |
mfg_90_optician_workload |
minimize | 1.0 | \sigma_{90}^{J4+} = \sqrt{\sum_{j=4}^{50} (Z_j^{90} - Z_j^{20})^2} |
nm | 20 nm |
Weighted Sum Objective:
J(\mathbf{x}) = \sum_{i=1}^{3} w_i \cdot \frac{f_i(\mathbf{x})}{t_i} + P_{mass}(\mathbf{x})
Where:
w_i= weight for objectiveif_i(\mathbf{x})= objective valuet_i= target value (normalization)P_{mass}= mass constraint penalty
2.2 Zernike Decomposition
The wavefront error W(r,\theta) is decomposed into Noll-indexed Zernike polynomials:
W(r,\theta) = \sum_{j=1}^{50} Z_j \cdot P_j(r,\theta)
WFE from Displacement (reflection factor of 2):
W_{nm} = 2 \cdot \delta_z \cdot 10^6
Where \delta_z is the Z-displacement in mm.
Filtered RMS (excluding alignable terms J1-J4):
\sigma_{filtered} = \sqrt{\sum_{j=5}^{50} Z_j^2}
Manufacturing RMS (excluding J1-J3, keeping defocus J4):
\sigma_{mfg} = \sqrt{\sum_{j=4}^{50} Z_j^2}
2.3 Design Variables (11 Parameters)
| Parameter | Symbol | Bounds | Baseline | Units | Description |
|---|---|---|---|---|---|
lateral_inner_angle |
\alpha_{in} |
[25, 28.5] | 26.79 | deg | Inner lateral support angle |
lateral_outer_angle |
\alpha_{out} |
[13, 17] | 14.64 | deg | Outer lateral support angle |
lateral_outer_pivot |
p_{out} |
[9, 12] | 10.40 | mm | Outer pivot offset |
lateral_inner_pivot |
p_{in} |
[9, 12] | 10.07 | mm | Inner pivot offset |
lateral_middle_pivot |
p_{mid} |
[18, 23] | 20.73 | mm | Middle pivot offset |
lateral_closeness |
c_{lat} |
[9.5, 12.5] | 11.02 | mm | Lateral support spacing |
whiffle_min |
w_{min} |
[35, 55] | 40.55 | mm | Whiffle tree minimum |
whiffle_outer_to_vertical |
\theta_w |
[68, 80] | 75.67 | deg | Outer whiffle angle |
whiffle_triangle_closeness |
c_w |
[50, 65] | 60.00 | mm | Whiffle triangle spacing |
blank_backface_angle |
\beta |
[4.0, 5.0] | 4.23 | deg | Mirror backface angle (mass driver) |
inner_circular_rib_dia |
D_{rib} |
[480, 620] | 534.00 | mm | Inner rib diameter |
Design Space:
\mathbf{x} = [\alpha_{in}, \alpha_{out}, p_{out}, p_{in}, p_{mid}, c_{lat}, w_{min}, \theta_w, c_w, \beta, D_{rib}]^T \in \mathbb{R}^{11}
2.4 Mass Constraint
| Constraint | Type | Formula | Threshold | Handling |
|---|---|---|---|---|
mass_limit |
upper_bound | m(\mathbf{x}) \leq m_{max} |
99 kg | Penalty in objective |
Penalty Function: $$P_{mass}(\mathbf{x}) = \begin{cases} 0 & \text{if } m \leq 99 \ 100 \cdot (m - 99) & \text{if } m > 99 \end{cases}$$
Mass Estimation (from blank_backface_angle):
\hat{m}(\beta) = 105 - 15 \cdot (\beta - 4.0) \text{ kg}
3. Optimization Algorithm
3.1 Adaptive Hybrid Strategy
| Parameter | Value | Description |
|---|---|---|
| Algorithm | Adaptive Hybrid | FEA + Neural Surrogate |
| Surrogate | Tuned Ensemble (3 models) | Auto-tuned architecture |
| Sampler | TPE | Tree-structured Parzen Estimator |
| Max Iterations | 100 | Adaptive loop iterations |
| FEA Batch Size | 5 | Real FEA evaluations per iteration |
| NN Trials | 1000 | Surrogate evaluations per iteration |
| Patience | 5 | Early stopping threshold |
| Convergence | 0.3 nm | Objective improvement threshold |
3.2 Hyperparameter Tuning
| Setting | Value |
|---|---|
| Tuning Trials | 30 |
| Cross-Validation | 5-fold |
| Search Space | Hidden dims, dropout, learning rate |
| Ensemble Size | 3 models |
| MC Dropout Samples | 30 |
Tuned Architecture:
Input(11) → Linear(128) → ReLU → Dropout →
Linear(256) → ReLU → Dropout →
Linear(256) → ReLU → Dropout →
Linear(128) → ReLU → Dropout →
Linear(64) → ReLU → Linear(4)
3.3 Adaptive Loop Flow
┌─────────────────────────────────────────────────────────────────────────┐
│ ADAPTIVE ITERATION k │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. SURROGATE EXPLORATION (1000 trials) │
│ ├── Sample 11 design variables via TPE │
│ ├── Predict objectives with ensemble (mean + uncertainty) │
│ └── Select top candidates (exploitation) + diverse (exploration) │
│ │
│ 2. FEA VALIDATION (5 trials) │
│ ├── Run NX Nastran SOL 101 (4 subcases) │
│ ├── Extract Zernike coefficients from OP2 │
│ ├── Compute relative filtered RMS │
│ └── Store in Optuna database │
│ │
│ 3. SURROGATE RETRAINING │
│ ├── Load all FEA data from database │
│ ├── Retrain ensemble with new data │
│ └── Update uncertainty estimates │
│ │
│ 4. CONVERGENCE CHECK │
│ ├── Δbest < 0.3 nm for patience=5 iterations? │
│ └── If converged → STOP, else → next iteration │
│ │
└─────────────────────────────────────────────────────────────────────────┘
4. Simulation Pipeline
4.1 FEA Trial Execution Flow
┌─────────────────────────────────────────────────────────────────────────┐
│ FEA TRIAL n EXECUTION │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. CANDIDATE SELECTION │
│ Hybrid strategy: 70% exploitation (best NN predictions) │
│ 30% exploration (uncertain regions) │
│ │
│ 2. NX PARAMETER UPDATE │
│ Module: optimization_engine/nx_solver.py │
│ Target Part: M1_Blank.prt (and related components) │
│ Action: Update 11 expressions with new design values │
│ │
│ 3. NX SIMULATION (Nastran SOL 101 - 4 Subcases) │
│ Module: optimization_engine/solve_simulation.py │
│ Input: ASSY_M1_assyfem1_sim1.sim │
│ Subcases: │
│ 1 = 90° zenith (polishing/manufacturing) │
│ 2 = 20° zenith (reference) │
│ 3 = 40° zenith (operational target 1) │
│ 4 = 60° zenith (operational target 2) │
│ Output: .dat, .op2, .f06 │
│ │
│ 4. ZERNIKE EXTRACTION │
│ Module: optimization_engine/extractors/extract_zernike.py │
│ a. Read node coordinates from BDF/DAT │
│ b. Read Z-displacements from OP2 for each subcase │
│ c. Compute RELATIVE displacement (target - reference) │
│ d. Convert to WFE: W = 2 × Δδz × 10⁶ nm │
│ e. Fit 50 Zernike coefficients via least-squares │
│ f. Compute filtered RMS (exclude J1-J4) │
│ │
│ 5. MASS EXTRACTION │
│ Module: optimization_engine/extractors/extract_mass_from_expression │
│ Expression: p173 (CAD mass property) │
│ │
│ 6. OBJECTIVE COMPUTATION │
│ rel_filtered_rms_40_vs_20 ← Zernike RMS (subcase 3 - 2) │
│ rel_filtered_rms_60_vs_20 ← Zernike RMS (subcase 4 - 2) │
│ mfg_90_optician_workload ← Zernike RMS J4+ (subcase 1 - 2) │
│ │
│ 7. WEIGHTED OBJECTIVE + MASS PENALTY │
│ J = Σ (weight × objective / target) + mass_penalty │
│ │
│ 8. STORE IN DATABASE │
│ Optuna SQLite: 3_results/study.db │
│ User attrs: trial_source='fea', mass_kg, all Zernike coefficients │
│ │
└─────────────────────────────────────────────────────────────────────────┘
4.2 Subcase Configuration
| Subcase | Zenith Angle | Gravity Direction | Role |
|---|---|---|---|
| 1 | 90° | Horizontal | Manufacturing/polishing reference |
| 2 | 20° | Near-vertical | Operational reference (baseline) |
| 3 | 40° | Mid-elevation | Operational target 1 |
| 4 | 60° | Low-elevation | Operational target 2 |
5. Result Extraction Methods
5.1 Zernike WFE Extraction
| Attribute | Value |
|---|---|
| Extractor | ZernikeExtractor |
| Module | optimization_engine.extractors.extract_zernike |
| Method | extract_relative() |
| Geometry Source | .dat (BDF format, auto-detected) |
| Displacement Source | .op2 (OP2 binary) |
| Output | 50 Zernike coefficients + RMS metrics per subcase pair |
Algorithm:
- Load node coordinates
(X_i, Y_i)from BDF - Load Z-displacements
\delta_{z,i}from OP2 for each subcase - Compute relative displacement (node-by-node):
\Delta\delta_{z,i} = \delta_{z,i}^{target} - \delta_{z,i}^{reference} - Convert to WFE:
W_i = 2 \cdot \Delta\delta_{z,i} \cdot 10^6 \text{ nm} - Fit Zernike coefficients via least-squares:
\min_{\mathbf{Z}} \| \mathbf{W} - \mathbf{P} \mathbf{Z} \|^2 - Compute filtered RMS:
\sigma_{filtered} = \sqrt{\sum_{j=5}^{50} Z_j^2}
Code:
from optimization_engine.extractors import ZernikeExtractor
extractor = ZernikeExtractor(op2_file, bdf_file)
result = extractor.extract_relative(
target_subcase="3", # 40 deg
reference_subcase="2", # 20 deg
displacement_unit="mm"
)
filtered_rms = result['relative_filtered_rms_nm'] # nm
5.2 Mass Extraction
| Attribute | Value |
|---|---|
| Extractor | extract_mass_from_expression |
| Module | optimization_engine.extractors |
| Expression | p173 (CAD mass property) |
| Output | kg |
Code:
from optimization_engine.extractors import extract_mass_from_expression
mass_kg = extract_mass_from_expression(model_file, expression_name="p173")
6. Neural Acceleration (Tuned Ensemble Surrogate)
6.1 Configuration
| Setting | Value | Description |
|---|---|---|
enabled |
true |
Neural surrogate active |
model_type |
TunedEnsembleSurrogate |
Ensemble of tuned networks |
ensemble_size |
3 | Number of models in ensemble |
hidden_dims |
[128, 256, 256, 128, 64] |
Auto-tuned architecture |
dropout |
0.1 | Regularization |
learning_rate |
0.001 | Adam optimizer |
batch_size |
16 | Mini-batch size |
mc_dropout_samples |
30 | Monte Carlo uncertainty |
6.2 Hyperparameter Tuning
| Parameter | Search Space |
|---|---|
n_layers |
[3, 4, 5, 6] |
hidden_dim |
[64, 128, 256, 512] |
dropout |
[0.0, 0.1, 0.2, 0.3] |
learning_rate |
[1e-4, 1e-3, 1e-2] |
batch_size |
[8, 16, 32, 64] |
Tuning Objective:
\mathcal{L}_{tune} = \frac{1}{K} \sum_{k=1}^{K} MSE_{val}^{(k)}
Using 5-fold cross-validation.
6.3 Surrogate Model
Input: \mathbf{x} = [11 \text{ design variables}]^T \in \mathbb{R}^{11}
Output: \hat{\mathbf{y}} = [4 \text{ objectives/constraints}]^T \in \mathbb{R}^{4}
rel_filtered_rms_40_vs_20(nm)rel_filtered_rms_60_vs_20(nm)mfg_90_optician_workload(nm)mass_kg(kg)
Ensemble Prediction:
\hat{y} = \frac{1}{M} \sum_{m=1}^{M} f_m(\mathbf{x})
Uncertainty Quantification (MC Dropout):
\sigma_y^2 = \frac{1}{T} \sum_{t=1}^{T} f_{dropout}^{(t)}(\mathbf{x})^2 - \hat{y}^2
6.4 Training Data Location
m1_mirror_adaptive_V12/
├── 2_iterations/
│ ├── iter_001/ # Iteration 1 working files
│ ├── iter_002/
│ └── ...
├── 3_results/
│ ├── study.db # Optuna database (all trials)
│ ├── optimization.log # Detailed log
│ ├── surrogate_best.pt # Best tuned model weights
│ └── tuning_results.json # Hyperparameter tuning history
6.5 Expected Performance
| Metric | Value |
|---|---|
| Source Data | V11: 107 FEA samples |
| FEA time per trial | 10-15 min |
| Neural time per trial | ~5 ms |
| Speedup | ~120,000x |
| Expected R² | > 0.90 (after tuning) |
| Uncertainty Coverage | ~95% (ensemble + MC dropout) |
7. Study File Structure
m1_mirror_adaptive_V12/
│
├── 1_setup/ # INPUT CONFIGURATION
│ ├── model/ → symlink to V11 # NX Model Files
│ │ ├── ASSY_M1.prt # Top-level assembly
│ │ ├── M1_Blank.prt # Mirror blank (expressions)
│ │ ├── ASSY_M1_assyfem1.afm # Assembly FEM
│ │ ├── ASSY_M1_assyfem1_sim1.sim # Simulation file
│ │ └── *-solution_1.op2 # Results (generated)
│ │
│ └── optimization_config.json # Study configuration
│
├── 2_iterations/ # WORKING DIRECTORY
│ ├── iter_001/ # Iteration 1 model copy
│ ├── iter_002/
│ └── ...
│
├── 3_results/ # OUTPUT (auto-generated)
│ ├── study.db # Optuna SQLite database
│ ├── optimization.log # Structured log
│ ├── surrogate_best.pt # Trained ensemble weights
│ ├── tuning_results.json # Hyperparameter tuning
│ └── convergence.json # Iteration history
│
├── run_optimization.py # Main entry point
├── final_validation.py # FEA validation of best NN trials
├── README.md # This blueprint
└── STUDY_REPORT.md # Results report (updated during run)
8. Results Location
After optimization, results are stored in 3_results/:
| File | Description | Format |
|---|---|---|
study.db |
Optuna database with all trials (FEA + NN) | SQLite |
optimization.log |
Detailed execution log | Text |
surrogate_best.pt |
Tuned ensemble model weights | PyTorch |
tuning_results.json |
Hyperparameter search history | JSON |
convergence.json |
Best value per iteration | JSON |
8.1 Trial Identification
Trials are tagged with source:
- FEA trials:
trial.user_attrs['trial_source'] = 'fea' - NN trials:
trial.user_attrs['trial_source'] = 'nn'
Dashboard Visualization:
- FEA trials: Blue circles
- NN trials: Orange crosses
8.2 Results Report
See STUDY_REPORT.md for:
- Optimization progress and convergence
- Best designs found (FEA-validated)
- Surrogate model accuracy (R², MAE)
- Pareto trade-off analysis
- Engineering recommendations
9. Quick Start
Launch Optimization
cd studies/m1_mirror_adaptive_V12
# Start with default settings (uses V11 FEA data)
python run_optimization.py --start
# Custom tuning parameters
python run_optimization.py --start --tune-trials 30 --ensemble-size 3 --fea-batch 5 --patience 5
# Tune hyperparameters only (no FEA)
python run_optimization.py --tune-only
Command Line Options
| Option | Default | Description |
|---|---|---|
--start |
- | Start adaptive optimization |
--tune-only |
- | Only tune hyperparameters, no optimization |
--tune-trials |
30 | Number of hyperparameter tuning trials |
--ensemble-size |
3 | Number of models in ensemble |
--fea-batch |
5 | FEA evaluations per iteration |
--patience |
5 | Early stopping patience |
Monitor Progress
# View log
tail -f 3_results/optimization.log
# Check database
sqlite3 3_results/study.db "SELECT COUNT(*) FROM trials WHERE state='COMPLETE'"
# Launch Optuna dashboard
optuna-dashboard sqlite:///3_results/study.db --port 8081
Dashboard Access
| Dashboard | URL | Purpose |
|---|---|---|
| Atomizer Dashboard | http://localhost:3000 | Real-time monitoring |
| Optuna Dashboard | http://localhost:8081 | Trial history |
10. Configuration Reference
File: 1_setup/optimization_config.json
| Section | Key | Description |
|---|---|---|
design_variables[] |
11 parameters | All lateral/whiffle/blank params |
objectives[] |
3 WFE metrics | Relative filtered RMS |
constraints[] |
mass_limit | Upper bound 99 kg |
zernike_settings.n_modes |
50 | Zernike polynomial count |
zernike_settings.filter_low_orders |
4 | Exclude J1-J4 |
zernike_settings.subcases |
[1,2,3,4] | Zenith angles |
adaptive_settings.max_iterations |
100 | Loop limit |
adaptive_settings.surrogate_trials_per_iter |
1000 | NN trials |
adaptive_settings.fea_batch_size |
5 | FEA per iteration |
adaptive_settings.patience |
5 | Early stopping |
surrogate_settings.ensemble_size |
3 | Model ensemble |
surrogate_settings.mc_dropout_samples |
30 | Uncertainty samples |
11. References
- Deb, K. et al. (2002). A fast and elitist multiobjective genetic algorithm: NSGA-II. IEEE TEC.
- Noll, R.J. (1976). Zernike polynomials and atmospheric turbulence. JOSA.
- Wilson, R.N. (2004). Reflecting Telescope Optics I. Springer.
- Snoek, J. et al. (2012). Practical Bayesian optimization of machine learning algorithms. NeurIPS.
- Gal, Y. & Ghahramani, Z. (2016). Dropout as a Bayesian approximation. ICML.
- pyNastran Documentation: BDF/OP2 parsing for FEA post-processing.
- Optuna Documentation: Hyperparameter optimization framework.
Atomizer V12: Where adaptive AI meets precision optics.