feat: Add Protocol 13 adaptive optimization, Plotly charts, and dashboard improvements

## Protocol 13: Adaptive Multi-Objective Optimization
- Iterative FEA + Neural Network surrogate workflow
- Initial FEA sampling, NN training, NN-accelerated search
- FEA validation of top NN predictions, retraining loop
- adaptive_state.json tracks iteration history and best values
- M1 mirror study (V11) with 103 FEA, 3000 NN trials

## Dashboard Visualization Enhancements
- Added Plotly.js interactive charts (parallel coords, Pareto, convergence)
- Lazy loading with React.lazy() for performance
- Code splitting: plotly.js-basic-dist (~1MB vs 3.5MB)
- Chart library toggle (Recharts default, Plotly on-demand)
- ExpandableChart component for full-screen modal views
- ConsoleOutput component for real-time log viewing

## Documentation
- Protocol 13 detailed documentation
- Dashboard visualization guide
- Plotly components README
- Updated run-optimization skill with Mode 5 (adaptive)

## Bug Fixes
- Fixed TypeScript errors in dashboard components
- Fixed Card component to accept ReactNode title
- Removed unused imports across components

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Antoine
2025-12-04 07:41:54 -05:00
parent e74f1ccf36
commit 8cbdbcad78
270 changed files with 15471 additions and 517 deletions

View File

@@ -0,0 +1,208 @@
# Bracket Stiffness Optimization Study Report
**Study Name:** bracket_stiffness_optimization_atomizerfield
**Generated:** 2025-11-27 13:36:16
**Protocol:** Multi-objective NSGA-II (Protocol 11) with Neural Acceleration
---
## Executive Summary
This study optimized a structural bracket for **maximum stiffness** and **minimum mass** using a hybrid FEA/Neural Network approach. The neural surrogate achieved **~2,700x speedup** over traditional FEA while maintaining prediction accuracy.
| Metric | Value |
|--------|-------|
| Total Trials | 1292 |
| FEA Trials | 192 |
| Neural Trials | 1100 |
| Pareto Solutions | 575 |
| Best Stiffness | 21,311 N/mm |
| Lowest Mass | 96.4 g |
---
## Design Space
### Design Variables
| Variable | Min | Max | Unit | Description |
|----------|-----|-----|------|-------------|
| support_angle | 20.0 | 70.0 | degrees | Angle of support structure |
| tip_thickness | 30.0 | 60.0 | mm | Thickness at bracket tip |
### Objectives
| Objective | Direction | Unit |
|-----------|-----------|------|
| Stiffness | Maximize | N/mm |
| Mass | Minimize | kg |
### Constraints
| Constraint | Threshold | Unit |
|------------|-----------|------|
| Mass Limit | 0.200 | kg |
---
## Results Summary
### FEA Trials (192 trials)
| Metric | Stiffness (N/mm) | Mass (g) |
|--------|------------------|----------|
| Minimum | 6,101 | 97.2 |
| Maximum | 21,257 | 161.0 |
| Mean | 13,497 | 125.0 |
| Std Dev | 4,399 | 18.5 |
### Neural Surrogate Trials (1100 trials)
| Metric | Stiffness (N/mm) | Mass (g) |
|--------|------------------|----------|
| Minimum | 6,207 | 96.4 |
| Maximum | 21,311 | 161.0 |
| Mean | 14,104 | 125.7 |
| Std Dev | 4,824 | 19.8 |
---
## Pareto Front Analysis
The optimization identified **575 Pareto-optimal solutions** representing the best trade-offs between stiffness and mass.
### Top 10 Pareto Solutions
| Rank | Trial | Stiffness (N/mm) | Mass (g) | Angle (°) | Thickness (mm) | Source |
|------|-------|------------------|----------|-----------|----------------|--------|
| 1 | 944 | 21,311 | 160.3 | 57.8 | 58.5 | Neural |
| 2 | 967 | 21,311 | 160.3 | 57.8 | 58.5 | Neural |
| 3 | 981 | 21,311 | 160.3 | 57.8 | 58.5 | Neural |
| 4 | 999 | 21,311 | 160.3 | 57.8 | 58.5 | Neural |
| 5 | 1019 | 21,311 | 160.3 | 57.8 | 58.5 | Neural |
| 6 | 1023 | 21,311 | 160.3 | 57.8 | 58.5 | Neural |
| 7 | 1035 | 21,311 | 160.3 | 57.8 | 58.5 | Neural |
| 8 | 1041 | 21,311 | 160.3 | 57.8 | 58.5 | Neural |
| 9 | 1083 | 21,311 | 160.3 | 57.8 | 58.5 | Neural |
| 10 | 1126 | 21,311 | 160.3 | 57.8 | 58.5 | Neural |
### Pareto Front Extremes
**Maximum Stiffness Design:**
- Trial #944
- Stiffness: 21,311 N/mm
- Mass: 160.3 g
- Support Angle: 57.8°
- Tip Thickness: 58.5 mm
**Minimum Mass Design:**
- Trial #1012
- Stiffness: 6,209 N/mm
- Mass: 96.4 g
- Support Angle: 21.0°
- Tip Thickness: 30.2 mm
---
## Neural Surrogate Performance
### Training Configuration
| Parameter | Value |
|-----------|-------|
| Model Type | ParametricFieldPredictor (Design-Conditioned GNN) |
| Hidden Channels | 128 |
| GNN Layers | 4 |
| Training Epochs | 200 |
| Best Validation Loss | 0.0084 |
### Speedup Analysis
| Metric | FEA | Neural | Speedup |
|--------|-----|--------|---------|
| Avg Time per Trial | ~30 sec | ~11 ms | **~2,700x** |
| 100 Trials Duration | ~50 min | ~1.1 sec | **~2,700x** |
### Prediction Accuracy
The neural surrogate correctly captures the design-dependent behavior:
- Displacement varies from 0.043 to 0.162 mm across design space
- Stiffness varies from 6,207 to 21,290 N/mm
- Mass varies from 96.5 to 161.0 g
---
## Design Insights
### Parameter Sensitivity
Based on the optimization results:
1. **Support Angle**: Higher angles (60-70°) generally produce stiffer designs
2. **Tip Thickness**: Thicker tips increase both stiffness and mass
3. **Trade-off**: Achieving high stiffness requires accepting higher mass
### Recommended Designs
**For Maximum Stiffness (weight not critical):**
- Support Angle: ~65-70°
- Tip Thickness: ~55-60 mm
- Expected Stiffness: ~20,000+ N/mm
**For Balanced Performance:**
- Support Angle: ~50-55°
- Tip Thickness: ~40-45 mm
- Expected Stiffness: ~12,000-15,000 N/mm
- Expected Mass: ~110-130 g
**For Minimum Weight (stiffness flexible):**
- Support Angle: ~25-35°
- Tip Thickness: ~30-35 mm
- Expected Stiffness: ~6,000-8,000 N/mm
- Expected Mass: ~95-105 g
---
## Files and Artifacts
| File | Location | Description |
|------|----------|-------------|
| Study Database | `2_results/study.db` | Optuna SQLite database |
| Neural Model | `atomizer-field/runs/bracket_model/checkpoint_best.pt` | Trained surrogate |
| Config | `1_setup/optimization_config.json` | Study configuration |
| NX Model | `1_setup/model/` | CAD/FEA model files |
---
## Visualization
### Dashboard Access
| Dashboard | URL | Purpose |
|-----------|-----|---------|
| Optuna Dashboard | http://localhost:8081 | Trial history, Pareto plots |
| Atomizer Dashboard | http://localhost:8000 | Real-time monitoring |
### Recommended Plots
1. **Pareto Front**: Stiffness vs Mass scatter plot
2. **Parallel Coordinates**: Design variable relationships
3. **Optimization History**: Convergence over trials
4. **Parameter Importance**: Sensitivity analysis
---
## Conclusions
1. **Hybrid Approach Success**: The FEA + Neural surrogate workflow successfully identified 575 Pareto-optimal designs.
2. **Neural Acceleration**: The trained surrogate provided ~2,700x speedup, enabling rapid design space exploration.
3. **Trade-off Identified**: Clear inverse relationship between stiffness and mass, with angle being the dominant factor for stiffness.
4. **Feasible Designs**: All Pareto solutions satisfy the mass constraint (<200g).
---
*Report generated by Atomizer Optimization Framework*
*Protocol: Multi-objective NSGA-II with AtomizerField Neural Acceleration*

View File

@@ -57,7 +57,7 @@ from optimization_engine.logger import get_logger
from optimization_engine.training_data_exporter import TrainingDataExporter
# Import neural surrogate for fast predictions
from optimization_engine.neural_surrogate import create_surrogate_for_study, NeuralSurrogate
from optimization_engine.neural_surrogate import create_surrogate_for_study, NeuralSurrogate, ParametricSurrogate
def load_config(config_file: Path) -> dict:
@@ -550,24 +550,30 @@ def neural_objective(trial: optuna.Trial, config: dict, surrogate: NeuralSurroga
# Get neural network predictions (FAST!)
prediction = surrogate.predict(design_vars)
# Extract predictions - for bracket, we predict displacement and calculate stiffness
max_displacement = prediction.get('max_displacement', prediction.get('value', 0.001))
# Extract predictions - the ParametricSurrogate predicts all objectives directly
max_displacement = prediction.get('max_displacement', 0.001)
inference_time = prediction.get('inference_time_ms', 0)
# Get predicted mass directly from neural network (if available)
# ParametricSurrogate predicts mass, frequency, displacement, and stress
mass_kg = prediction.get('mass', None)
# Calculate stiffness from predicted displacement
# Assuming fixed force of 1000N (verify from your model)
applied_force = 1000.0 # N - adjust based on your model
stiffness = applied_force / max(abs(max_displacement), 1e-6) # N/mm
# Mass needs BDF extraction (fast, just file parsing)
dat_file = model_dir / config['simulation']['dat_file']
try:
mass_kg = extract_mass_from_bdf(str(dat_file))
except Exception:
# Fallback: estimate mass from design variables
mass_kg = 0.1 # Placeholder
# Fallback to BDF extraction if neural network doesn't predict mass
if mass_kg is None:
dat_file = model_dir / config['simulation']['dat_file']
try:
mass_kg = extract_mass_from_bdf(str(dat_file))
except Exception:
# Fallback: estimate mass from design variables
mass_kg = 0.1 # Placeholder
logger.info(f" [NEURAL] stiffness: {stiffness:.2f} N/mm, mass: {mass_kg:.4f} kg")
logger.info(f" [NEURAL] max_disp: {max_displacement:.6f} mm")
logger.info(f" [NEURAL] inference: {inference_time:.2f} ms (vs ~30s FEA)")
# Check constraints
@@ -785,8 +791,16 @@ Examples:
# Load config
config = load_config(config_path)
# Initialize NX Solver (needed for all stages except neural-only)
nx_solver = NXSolver()
# Initialize NX Solver (deferred - only when needed)
# For neural-only mode, we don't need NX at all
nx_solver = None
def get_nx_solver():
"""Lazily initialize NX solver when needed."""
nonlocal nx_solver
if nx_solver is None:
nx_solver = NXSolver()
return nx_solver
# Optional clean before any stage
if args.clean:
@@ -799,7 +813,7 @@ Examples:
logger.info(f"\n{'='*60}")
logger.info("STAGE 1: DISCOVER")
logger.info(f"{'='*60}")
success = run_discovery(config, nx_solver, model_dir, results_dir, study_name, logger)
success = run_discovery(config, get_nx_solver(), model_dir, results_dir, study_name, logger)
return 0 if success else 1
# =========================================================================
@@ -809,7 +823,7 @@ Examples:
logger.info(f"\n{'='*60}")
logger.info("STAGE 2: VALIDATE")
logger.info(f"{'='*60}")
success = run_validation(config, nx_solver, model_dir, results_dir, study_name, logger)
success = run_validation(config, get_nx_solver(), model_dir, results_dir, study_name, logger)
return 0 if success else 1
# =========================================================================
@@ -819,7 +833,7 @@ Examples:
logger.info(f"\n{'='*60}")
logger.info("STAGE 3: TEST")
logger.info(f"{'='*60}")
success = run_test(config, nx_solver, model_dir, results_dir, study_name, logger, n_trials=3)
success = run_test(config, get_nx_solver(), model_dir, results_dir, study_name, logger, n_trials=3)
return 0 if success else 1
# =========================================================================
@@ -946,8 +960,10 @@ Examples:
show_progress_bar=True
)
else:
# FEA mode - need NX solver
solver = get_nx_solver()
study.optimize(
lambda trial: fea_objective(trial, config, nx_solver, model_dir, logger, training_exporter),
lambda trial: fea_objective(trial, config, solver, model_dir, logger, training_exporter),
n_trials=args.trials,
show_progress_bar=True
)