Phase 3.3: Multi-objective optimization fix, updated docs & Claude skill
- Fixed drone gimbal optimization to use proper semantic directions - Changed from ['minimize', 'minimize'] to ['minimize', 'maximize'] - Updated Claude skill (v2.0) with Phase 3.3 integration - Added centralized extractor library documentation - Added multi-objective optimization (Protocol 11) section - Added NX multi-solution protocol documentation - Added dashboard integration documentation - Fixed Pareto front degenerate issue with proper NSGA-II configuration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
390
docs/DASHBOARD.md
Normal file
390
docs/DASHBOARD.md
Normal file
@@ -0,0 +1,390 @@
|
||||
# Atomizer Dashboard
|
||||
|
||||
**Last Updated**: November 23, 2025
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Atomizer Dashboard is a real-time web-based interface for monitoring and analyzing multi-objective optimization studies. Built with React, TypeScript, and Tailwind CSS, it provides comprehensive visualization and interaction capabilities for NSGA-II based structural optimization.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
### Frontend Stack
|
||||
- **Framework**: React 18 with TypeScript
|
||||
- **Build Tool**: Vite
|
||||
- **Styling**: Tailwind CSS with custom dark/light theme support
|
||||
- **Charts**: Recharts for data visualization
|
||||
- **State Management**: React hooks (useState, useEffect)
|
||||
- **WebSocket**: Real-time optimization updates
|
||||
|
||||
### Backend Stack
|
||||
- **Framework**: FastAPI (Python)
|
||||
- **Database**: Optuna SQLite studies
|
||||
- **API**: RESTful endpoints with WebSocket support
|
||||
- **CORS**: Configured for local development
|
||||
|
||||
### Ports
|
||||
- **Frontend**: `http://localhost:3003` (Vite dev server)
|
||||
- **Backend**: `http://localhost:8000` (FastAPI/Uvicorn)
|
||||
|
||||
---
|
||||
|
||||
## Key Features
|
||||
|
||||
### 1. Multi-Objective Visualization
|
||||
|
||||
#### Pareto Front Plot
|
||||
- 2D scatter plot showing trade-offs between objectives
|
||||
- Color-coded by constraint satisfaction (green = feasible, red = infeasible)
|
||||
- Interactive hover tooltips with trial details
|
||||
- Automatically extracts Pareto-optimal solutions using NSGA-II
|
||||
|
||||
#### Parallel Coordinates Plot
|
||||
**Research-Based Multi-Dimensional Visualization**
|
||||
|
||||
Structure: **Design Variables → Objectives → Constraints**
|
||||
|
||||
Features:
|
||||
- **Light Theme**: White background with high-visibility dark text and colors
|
||||
- **Color-Coded Axes**:
|
||||
- Blue background: Design variables
|
||||
- Green background: Objectives
|
||||
- Yellow background: Constraints
|
||||
- **Interactive Selection**:
|
||||
- Hover over lines to highlight individual trials
|
||||
- Click to select/deselect trials
|
||||
- Multi-select with visual feedback (orange highlight)
|
||||
- **Type Badges**: Labels showing DESIGN VAR, OBJECTIVE, or CONSTRAINT
|
||||
- **Units Display**: Automatic unit labeling (mm, MPa, Hz, g, etc.)
|
||||
- **Min/Max Labels**: Range values displayed on each axis
|
||||
- **Feasibility Coloring**:
|
||||
- Green: Feasible solutions
|
||||
- Red: Infeasible solutions (constraint violations)
|
||||
- Blue: Hover highlight
|
||||
- Orange: Selected trials
|
||||
|
||||
**Implementation**: [ParallelCoordinatesPlot.tsx](atomizer-dashboard/frontend/src/components/ParallelCoordinatesPlot.tsx:1)
|
||||
|
||||
**Line colors**:
|
||||
```typescript
|
||||
if (isSelected) return '#FF6B00'; // Orange for selected
|
||||
if (!trial.feasible) return '#DC2626'; // Red for infeasible
|
||||
if (isHovered) return '#2563EB'; // Blue for hover
|
||||
return '#10B981'; // Green for feasible
|
||||
```
|
||||
|
||||
### 2. Optimizer Strategy Panel
|
||||
Displays algorithm information:
|
||||
- **Algorithm**: NSGA-II, TPE, or custom
|
||||
- **Type**: Single-objective or Multi-objective
|
||||
- **Objectives Count**: Number of optimization objectives
|
||||
- **Design Variables Count**: Number of design parameters
|
||||
|
||||
### 3. Convergence Monitoring
|
||||
- **Convergence Plot**: Best value vs. trial number
|
||||
- **Real-time Updates**: WebSocket-driven live updates
|
||||
- **Pruned Trials**: Visual indication of pruned trials
|
||||
|
||||
### 4. Parameter Space Exploration
|
||||
- **2D Scatter Plot**: Design variable relationships
|
||||
- **Color Mapping**: Objective values mapped to color intensity
|
||||
- **Interactive Tooltips**: Trial details on hover
|
||||
|
||||
### 5. Trial History Table
|
||||
- Comprehensive list of all trials
|
||||
- Sortable columns
|
||||
- Status indicators (COMPLETE, PRUNED, FAIL)
|
||||
- Parameter values and objective values
|
||||
- User attributes (constraints)
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Studies
|
||||
|
||||
#### GET `/api/optimization/studies`
|
||||
List all available optimization studies.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "drone_gimbal_arm_optimization",
|
||||
"name": "drone_gimbal_arm_optimization",
|
||||
"direction": ["minimize", "maximize"],
|
||||
"n_trials": 100,
|
||||
"best_value": [3245.67, 165.3],
|
||||
"sampler": "NSGAIISampler"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### GET `/api/optimization/studies/{study_id}/trials`
|
||||
Get all trials for a study.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"trials": [
|
||||
{
|
||||
"number": 0,
|
||||
"values": [3456.2, 145.6],
|
||||
"params": {
|
||||
"beam_half_core_thickness": 7.5,
|
||||
"beam_face_thickness": 2.1,
|
||||
"holes_diameter": 30.0,
|
||||
"hole_count": 11
|
||||
},
|
||||
"state": "COMPLETE",
|
||||
"user_attrs": {
|
||||
"max_stress": 95.3,
|
||||
"max_displacement": 1.2,
|
||||
"frequency": 145.6,
|
||||
"mass": 3456.2,
|
||||
"constraint_satisfied": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### GET `/api/optimization/studies/{study_id}/metadata`
|
||||
Get study metadata including objectives and design variables.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"objectives": [
|
||||
{
|
||||
"name": "mass",
|
||||
"type": "minimize",
|
||||
"unit": "g"
|
||||
},
|
||||
{
|
||||
"name": "frequency",
|
||||
"type": "maximize",
|
||||
"unit": "Hz"
|
||||
}
|
||||
],
|
||||
"design_variables": [
|
||||
{
|
||||
"name": "beam_half_core_thickness",
|
||||
"unit": "mm",
|
||||
"min": 5.0,
|
||||
"max": 10.0
|
||||
}
|
||||
],
|
||||
"sampler": "NSGAIISampler"
|
||||
}
|
||||
```
|
||||
|
||||
#### GET `/api/optimization/studies/{study_id}/pareto-front`
|
||||
Get Pareto-optimal solutions for multi-objective studies.
|
||||
|
||||
**Response**:
|
||||
```json
|
||||
{
|
||||
"is_multi_objective": true,
|
||||
"pareto_front": [
|
||||
{
|
||||
"trial_number": 0,
|
||||
"values": [3245.67, 165.3],
|
||||
"params": {...},
|
||||
"user_attrs": {...},
|
||||
"constraint_satisfied": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### WebSocket
|
||||
|
||||
#### WS `/ws/optimization/{study_id}`
|
||||
Real-time trial updates during optimization.
|
||||
|
||||
**Message Format**:
|
||||
```json
|
||||
{
|
||||
"type": "trial_complete",
|
||||
"trial": {
|
||||
"number": 5,
|
||||
"values": [3456.2, 145.6],
|
||||
"params": {...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Running the Dashboard
|
||||
|
||||
### Backend
|
||||
|
||||
```bash
|
||||
cd atomizer-dashboard/backend
|
||||
python -m uvicorn api.main:app --reload --port 8000
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
```bash
|
||||
cd atomizer-dashboard/frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Access at: `http://localhost:3003`
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Vite Proxy ([vite.config.ts](atomizer-dashboard/frontend/vite.config.ts:1))
|
||||
|
||||
```typescript
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 3003,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://127.0.0.1:8000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true, // WebSocket support
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### CORS ([backend/api/main.py](atomizer-dashboard/backend/api/main.py:1))
|
||||
|
||||
```python
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["http://localhost:3003"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Component Structure
|
||||
|
||||
```
|
||||
atomizer-dashboard/
|
||||
├── frontend/
|
||||
│ ├── src/
|
||||
│ │ ├── components/
|
||||
│ │ │ ├── ParallelCoordinatesPlot.tsx # Multi-objective visualization
|
||||
│ │ │ ├── ParetoPlot.tsx # Pareto front scatter plot
|
||||
│ │ │ ├── OptimizerPanel.tsx # Strategy information
|
||||
│ │ │ ├── common/
|
||||
│ │ │ │ └── Card.tsx # Reusable card component
|
||||
│ │ │ └── dashboard/
|
||||
│ │ │ ├── MetricCard.tsx # KPI display
|
||||
│ │ │ └── StudyCard.tsx # Study selector
|
||||
│ │ ├── pages/
|
||||
│ │ │ └── Dashboard.tsx # Main dashboard page
|
||||
│ │ ├── hooks/
|
||||
│ │ │ └── useWebSocket.ts # WebSocket connection
|
||||
│ │ ├── api/
|
||||
│ │ │ └── client.ts # API client
|
||||
│ │ └── types/
|
||||
│ │ └── index.ts # TypeScript types
|
||||
│ └── vite.config.ts
|
||||
└── backend/
|
||||
└── api/
|
||||
├── main.py # FastAPI app
|
||||
└── routes/
|
||||
└── optimization.py # Optimization endpoints
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Flow
|
||||
|
||||
1. **Optimization Engine** runs trials and stores results in Optuna SQLite database
|
||||
2. **Backend API** reads from database and exposes REST endpoints
|
||||
3. **Frontend** fetches data via `/api/optimization/*` endpoints
|
||||
4. **WebSocket** pushes real-time updates to connected clients
|
||||
5. **React Components** render visualizations based on fetched data
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Dashboard Page Crashes
|
||||
|
||||
**Issue**: `TypeError: Cannot read properties of undefined (reading 'split')`
|
||||
|
||||
**Fix**: Ensure all data is validated before rendering. ParallelCoordinatesPlot now includes:
|
||||
```typescript
|
||||
if (!paretoData || paretoData.length === 0) return <EmptyState />;
|
||||
if (!objectives || !designVariables) return <EmptyState />;
|
||||
```
|
||||
|
||||
### No Data Showing
|
||||
|
||||
1. Check backend is running: `curl http://localhost:8000/api/optimization/studies`
|
||||
2. Verify study exists in Optuna database
|
||||
3. Check browser console for API errors
|
||||
4. Ensure WebSocket connection is established
|
||||
|
||||
### CORS Errors
|
||||
|
||||
- Backend must allow origin `http://localhost:3003`
|
||||
- Frontend proxy must target `http://127.0.0.1:8000` (not `localhost`)
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### For Multi-Objective Studies
|
||||
|
||||
1. **Always use metadata endpoint** to get objective/variable definitions
|
||||
2. **Extract constraints from user_attrs** for parallel coordinates
|
||||
3. **Filter Pareto front** using `paretoData.pareto_front` array
|
||||
4. **Validate constraint_satisfied** field before coloring
|
||||
|
||||
### For Real-Time Updates
|
||||
|
||||
1. **Use WebSocket** for live trial updates
|
||||
2. **Debounce state updates** to avoid excessive re-renders
|
||||
3. **Close WebSocket** connection on component unmount
|
||||
|
||||
### For Performance
|
||||
|
||||
1. **Limit displayed trials** for large studies (e.g., show last 1000)
|
||||
2. **Use React.memo** for expensive components
|
||||
3. **Virtualize large lists** if showing >100 trials in tables
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- [ ] 3D Pareto front visualization for 3+ objectives
|
||||
- [ ] Advanced filtering and search in trial history
|
||||
- [ ] Export results to CSV/JSON
|
||||
- [ ] Custom parallel coordinates brushing/filtering
|
||||
- [ ] Multi-study comparison view
|
||||
- [ ] Hypervolume indicator tracking
|
||||
- [ ] Interactive design variable sliders
|
||||
- [ ] Constraint importance analysis
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **Optuna Documentation**: https://optuna.readthedocs.io/
|
||||
- **NSGA-II Algorithm**: Deb et al. (2002)
|
||||
- **Parallel Coordinates**: Inselberg & Dimsdale (1990)
|
||||
- **React Documentation**: https://react.dev/
|
||||
- **FastAPI Documentation**: https://fastapi.tiangolo.com/
|
||||
197
docs/NX_MULTI_SOLUTION_PROTOCOL.md
Normal file
197
docs/NX_MULTI_SOLUTION_PROTOCOL.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# NX Multi-Solution Solve Protocol
|
||||
|
||||
## Critical Finding: SolveAllSolutions API Required for Multi-Solution Models
|
||||
|
||||
**Date**: November 23, 2025
|
||||
**Last Updated**: November 23, 2025
|
||||
**Protocol**: Multi-Solution Nastran Solve
|
||||
**Affected Models**: Any NX simulation with multiple solutions (e.g., static + modal, thermal + structural)
|
||||
|
||||
---
|
||||
|
||||
## Problem Statement
|
||||
|
||||
When an NX simulation contains multiple solutions (e.g., Solution 1 = Static Analysis, Solution 2 = Modal Analysis), using `SolveChainOfSolutions()` with Background mode **does not wait for all solutions to complete** before returning control to Python. This causes:
|
||||
|
||||
1. **Missing OP2 Files**: Only the first solution's OP2 file is generated
|
||||
2. **Stale Data**: Subsequent trials read old OP2 files from previous runs
|
||||
3. **Identical Results**: All trials show the same values for results from missing solutions
|
||||
4. **Silent Failures**: No error is raised - the solve completes but files are not written
|
||||
|
||||
### Example Scenario
|
||||
|
||||
**Drone Gimbal Arm Optimization**:
|
||||
- Solution 1: Static analysis (stress, displacement)
|
||||
- Solution 2: Modal analysis (frequency)
|
||||
|
||||
**Symptoms**:
|
||||
- All 100 trials showed **identical frequency** (27.476 Hz)
|
||||
- Only `beam_sim1-solution_1.op2` was created
|
||||
- `beam_sim1-solution_2.op2` was never regenerated after Trial 0
|
||||
- Both `.dat` files were written correctly, but solve didn't wait for completion
|
||||
|
||||
---
|
||||
|
||||
## Root Cause
|
||||
|
||||
```python
|
||||
# WRONG APPROACH (doesn't wait for completion)
|
||||
psolutions1 = []
|
||||
solution_idx = 1
|
||||
while True:
|
||||
solution_obj_name = f"Solution[Solution {solution_idx}]"
|
||||
simSolution = simSimulation1.FindObject(solution_obj_name)
|
||||
if simSolution:
|
||||
psolutions1.append(simSolution)
|
||||
solution_idx += 1
|
||||
else:
|
||||
break
|
||||
|
||||
theCAESimSolveManager.SolveChainOfSolutions(
|
||||
psolutions1,
|
||||
NXOpen.CAE.SimSolution.SolveOption.Solve,
|
||||
NXOpen.CAE.SimSolution.SetupCheckOption.CompleteDeepCheckAndOutputErrors,
|
||||
NXOpen.CAE.SimSolution.SolveMode.Background # ❌ Returns immediately!
|
||||
)
|
||||
```
|
||||
|
||||
**Issue**: Background mode runs asynchronously and returns control to Python before all solutions finish solving.
|
||||
|
||||
---
|
||||
|
||||
## Correct Solution
|
||||
|
||||
### For Solving All Solutions
|
||||
|
||||
Use `SolveAllSolutions()` API with **Foreground mode**:
|
||||
|
||||
```python
|
||||
# CORRECT APPROACH (waits for completion)
|
||||
if solution_name:
|
||||
# Solve specific solution in background mode
|
||||
solution_obj_name = f"Solution[{solution_name}]"
|
||||
simSolution1 = simSimulation1.FindObject(solution_obj_name)
|
||||
psolutions1 = [simSolution1]
|
||||
|
||||
numsolutionssolved1, numsolutionsfailed1, numsolutionsskipped1 = theCAESimSolveManager.SolveChainOfSolutions(
|
||||
psolutions1,
|
||||
NXOpen.CAE.SimSolution.SolveOption.Solve,
|
||||
NXOpen.CAE.SimSolution.SetupCheckOption.CompleteDeepCheckAndOutputErrors,
|
||||
NXOpen.CAE.SimSolution.SolveMode.Background
|
||||
)
|
||||
else:
|
||||
# Solve ALL solutions using SolveAllSolutions API (Foreground mode)
|
||||
# This ensures all solutions (static + modal, etc.) complete before returning
|
||||
print(f"[JOURNAL] Solving all solutions using SolveAllSolutions API (Foreground mode)...")
|
||||
|
||||
numsolutionssolved1, numsolutionsfailed1, numsolutionsskipped1 = theCAESimSolveManager.SolveAllSolutions(
|
||||
NXOpen.CAE.SimSolution.SolveOption.Solve,
|
||||
NXOpen.CAE.SimSolution.SetupCheckOption.CompleteCheckAndOutputErrors,
|
||||
NXOpen.CAE.SimSolution.SolveMode.Foreground, # ✅ Blocks until complete
|
||||
False
|
||||
)
|
||||
```
|
||||
|
||||
### Key Differences
|
||||
|
||||
| Aspect | SolveChainOfSolutions | SolveAllSolutions |
|
||||
|--------|----------------------|-------------------|
|
||||
| **Manual enumeration** | Required (loop through solutions) | Automatic (handles all solutions) |
|
||||
| **Background mode behavior** | Returns immediately, async | N/A (Foreground recommended) |
|
||||
| **Foreground mode behavior** | Blocks until complete | Blocks until complete ✅ |
|
||||
| **Use case** | Specific solution selection | Solve all solutions |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Location
|
||||
|
||||
**File**: `optimization_engine/solve_simulation.py`
|
||||
**Lines**: 271-295
|
||||
|
||||
**When to use this protocol**:
|
||||
- When `solution_name=None` is passed to `NXSolver.run_simulation()`
|
||||
- Any simulation with multiple solutions that must all complete
|
||||
- Multi-objective optimization requiring results from different analysis types
|
||||
|
||||
---
|
||||
|
||||
## Verification Steps
|
||||
|
||||
After implementing the fix, verify:
|
||||
|
||||
1. **Both .dat files are written** (one per solution)
|
||||
```
|
||||
beam_sim1-solution_1.dat # Static analysis
|
||||
beam_sim1-solution_2.dat # Modal analysis
|
||||
```
|
||||
|
||||
2. **Both .op2 files are created** with updated timestamps
|
||||
```
|
||||
beam_sim1-solution_1.op2 # Contains stress, displacement
|
||||
beam_sim1-solution_2.op2 # Contains eigenvalues, mode shapes
|
||||
```
|
||||
|
||||
3. **Results are unique per trial** - check that frequency values vary across trials
|
||||
|
||||
4. **Journal log shows**:
|
||||
```
|
||||
[JOURNAL] Solving all solutions using SolveAllSolutions API (Foreground mode)...
|
||||
[JOURNAL] Solve completed!
|
||||
[JOURNAL] Solutions solved: 2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Issues Fixed
|
||||
|
||||
1. **All trials showing identical frequency**: Fixed by ensuring modal solution runs
|
||||
2. **Only one data point in dashboard**: Fixed by all trials succeeding
|
||||
3. **Parallel coordinates with NaN**: Fixed by having complete data from all solutions
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **User's Example**: `nx_journals/user_generated_journals/journal_solve_all_solution.py` (line 27)
|
||||
- **NX Open Documentation**: SimSolveManager.SolveAllSolutions() method
|
||||
- **Implementation**: `optimization_engine/solve_simulation.py`
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always use Foreground mode** when solving all solutions
|
||||
2. **Verify OP2 timestamp changes** to ensure fresh solves
|
||||
3. **Check solve counts** in journal output to confirm both solutions ran
|
||||
4. **Test with 5 trials** before running large optimizations
|
||||
5. **Monitor unique frequency values** as a smoke test for multi-solution models
|
||||
|
||||
---
|
||||
|
||||
## Example Use Cases
|
||||
|
||||
### ✅ Correct Usage
|
||||
|
||||
```python
|
||||
# Multi-objective optimization with static + modal
|
||||
result = nx_solver.run_simulation(
|
||||
sim_file=sim_file,
|
||||
working_dir=model_dir,
|
||||
expression_updates=design_vars,
|
||||
solution_name=None # Solve ALL solutions
|
||||
)
|
||||
```
|
||||
|
||||
### ❌ Incorrect Usage (Don't Do This)
|
||||
|
||||
```python
|
||||
# Running modal separately - inefficient and error-prone
|
||||
result1 = nx_solver.run_simulation(..., solution_name="Solution 1") # Static
|
||||
result2 = nx_solver.run_simulation(..., solution_name="Solution 2") # Modal
|
||||
# This doubles the solve time and requires managing two result objects
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Status**: ✅ Implemented and Verified
|
||||
**Impact**: Critical for all multi-solution optimization workflows
|
||||
Reference in New Issue
Block a user