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

@@ -342,8 +342,8 @@ def extract_zernike_with_relative(
bdf_path: Optional[Path] = None,
n_modes: int = 50,
subcases: List[str] = ["1", "2", "3", "4"],
subcase_labels: Dict[str, str] = {"1": "20deg", "2": "40deg", "3": "60deg", "4": "90deg"},
reference_subcase: str = "1"
subcase_labels: Dict[str, str] = {"1": "90deg", "2": "20deg", "3": "40deg", "4": "60deg"},
reference_subcase: str = "2"
) -> Dict[str, Dict[str, Any]]:
"""
Extract Zernike coefficients using displacement-based subtraction for relative metrics.
@@ -717,8 +717,9 @@ def fea_objective(
try:
zernike_settings = config.get('zernike_settings', {})
subcases = zernike_settings.get('subcases', ['1', '2', '3', '4'])
subcase_labels = zernike_settings.get('subcase_labels', {"1": "20deg", "2": "40deg", "3": "60deg", "4": "90deg"})
reference_subcase = zernike_settings.get('reference_subcase', '1')
# CORRECT mapping: 1=90deg (polishing), 2=20deg (reference), 3=40deg, 4=60deg
subcase_labels = zernike_settings.get('subcase_labels', {"1": "90deg", "2": "20deg", "3": "40deg", "4": "60deg"})
reference_subcase = zernike_settings.get('reference_subcase', '2')
n_modes = zernike_settings.get('n_modes', 50)
zernike_data = extract_zernike_with_relative(