# Analyze Optimization Workflow Skill

**Last Updated**: November 23, 2025
**Version**: 2.0 - Phase 3.3 Integration

You are analyzing a structural optimization request for the Atomizer system.

When the user provides a request, break it down into atomic workflow steps and classify each step intelligently.

## Step Types

**1. ENGINEERING FEATURES** - Complex FEA/CAE operations needing specialized knowledge:
- Extract results from OP2 files using centralized extractors
- Modify FEA properties (CBUSH/CBAR stiffness, PCOMP layup, material properties)
- Run simulations (SOL101 static, SOL103 modal, etc.)
- Create/modify geometry in NX
- Multi-solution workflows (static + modal, thermal + structural)

**2. INLINE CALCULATIONS** - Simple math operations (auto-generate Python):
- Calculate average, min, max, sum
- Compare values, compute ratios
- Statistical operations

**3. POST-PROCESSING HOOKS** - Custom calculations between FEA steps:
- Custom objective functions combining multiple results
- Data transformations
- Filtering/aggregation logic

**4. OPTIMIZATION** - Algorithm and configuration:
- Single-objective: Optuna TPE, CMA-ES, Random Search
- Multi-objective: NSGA-II (Protocol 11), NSGA-III, MOEA/D
- Design variables and their ranges
- Constraints and objectives

## Centralized Extractor Library

**Location**: `optimization_engine/extractors/`

Use these standardized extractors instead of custom OP2 code:

### Available Extractors:

1. **extract_displacement.py**
   - `extract_displacement(op2_file, subcase)` → max displacement in mm
   - Returns: `{'max_displacement': float, 'node_id': int}`

2. **extract_von_mises_stress.py**
   - `extract_solid_stress(op2_file, subcase, element_type)` → max von Mises stress in MPa
   - Element types: `'ctetra'`, `'chexa'`, `'cquad4'`, `'ctria3'`
   - Returns: `{'max_von_mises': float, 'element_id': int}`

3. **extract_frequency.py**
   - `extract_frequency(op2_file, subcase, mode_number)` → frequency in Hz
   - Returns: `{'frequency': float, 'mode': int, 'eigenvalue': float}`

4. **extract_mass_from_bdf.py**
   - `extract_mass_from_bdf(bdf_file)` → FEM mass in kg
   - Returns: `{'mass_kg': float, 'cg': [x, y, z], 'inertia': [[...]]}`

5. **extract_mass_from_expression.py**
   - `extract_mass_from_expression(prt_file, expression_name)` → CAD mass in kg
   - Returns: `float` (mass value from NX expression)

6. **op2_extractor.py** (Base Class)
   - `OP2Extractor` - Base class for custom extractors
   - Provides common OP2 file handling and error management

### Extractor Selection Guide:

- **Displacement** → Use `extract_displacement`
- **Von Mises Stress** (solids/shells) → Use `extract_solid_stress`
- **Natural Frequency** (modal analysis) → Use `extract_frequency`
- **Mass** (from FEM) → Use `extract_mass_from_bdf`
- **Mass** (from CAD geometry) → Use `extract_mass_from_expression`

## Multi-Objective Optimization (Protocol 11)

### NSGA-II Configuration:

```python
from optuna.samplers import NSGAIISampler

study = optuna.create_study(
    study_name="study_name",
    storage="sqlite:///study.db",
    directions=['minimize', 'maximize'],  # Semantic directions
    sampler=NSGAIISampler()
)
```

### Key Concepts:

- **Pareto Front**: Set of non-dominated solutions
- **Semantic Directions**: `['minimize', 'maximize']` - NEVER use negative values
- **Trade-offs**: Conflicting objectives (e.g., minimize mass vs maximize stiffness)
- **Feasibility**: Solutions must satisfy all constraints
- **Visualization**: Parallel Coordinates Plot + Pareto Front scatter plot

### Multi-Objective Return Format:

```python
def objective(trial: optuna.Trial) -> tuple:
    """
    Returns:
        (obj1, obj2): Tuple for NSGA-II
    """
    # Extract objectives
    mass = extract_mass(...)  # to minimize
    freq = extract_frequency(...)  # to maximize

    # Return POSITIVE values - directions handled by study config
    return (mass, freq)
```

### Common Pitfall - AVOID:

```python
# ❌ WRONG: Using negative values to simulate maximization
return (mass, -frequency)  # Creates degenerate Pareto front

# ✅ CORRECT: Use proper semantic directions
return (mass, frequency)  # Study configured with ['minimize', 'maximize']
```

## NX Multi-Solution Protocol

**Critical Protocol**: When simulations have multiple solutions (e.g., Solution 1 = Static, Solution 2 = Modal):

### Required API:

```python
# ✅ CORRECT: Use SolveAllSolutions() with Foreground mode
result = nx_solver.run_simulation(
    sim_file=sim_file,
    working_dir=model_dir,
    expression_updates=design_vars,
    solution_name=None  # Solve ALL solutions
)
```

### Why This Matters:

- `SolveChainOfSolutions()` with Background mode only solves the first solution
- Causes stale OP2 files and identical results across trials
- `SolveAllSolutions()` ensures all solutions complete before returning
- See: `docs/NX_MULTI_SOLUTION_PROTOCOL.md`

## Dashboard Integration

### Visualization Features:

1. **Parallel Coordinates Plot**
   - Structure: Design Variables → Objectives → Constraints
   - Color-coded axes (blue/green/yellow)
   - Interactive trial selection
   - Feasibility coloring (green = feasible, red = infeasible)

2. **Pareto Front Plot**
   - 2D scatter for bi-objective problems
   - Shows trade-offs between objectives
   - Highlights non-dominated solutions

3. **Real-Time Updates**
   - WebSocket connection for live monitoring
   - Backend: FastAPI (port 8000)
   - Frontend: React + Vite (port 3003)

### Dashboard Access:

```bash
# Backend
cd atomizer-dashboard/backend && python -m uvicorn api.main:app --reload

# Frontend
cd atomizer-dashboard/frontend && npm run dev

# Access: http://localhost:3003
```

## Important Distinctions

### Element Types:
- CBUSH vs CBAR vs CBEAM are different 1D elements
- CQUAD4 vs CTRIA3 are shell elements
- CTETRA vs CHEXA are solid elements

### Result Types:
- Element forces vs Reaction forces are DIFFERENT
- Von Mises stress vs Principal stress
- Displacement magnitude vs Component displacement

### Data Sources:
- OP2 file (FEA results) → Use extractors
- .prt expression (CAD parameters) → Use `extract_mass_from_expression`
- .fem/.bdf file (FEM model) → Use `extract_mass_from_bdf`
- Multi-solution OP2 files: `solution_1.op2`, `solution_2.op2`

### Optimization Algorithms:
- **TPE** (Tree-structured Parzen Estimator): Single-objective, adaptive
- **NSGA-II**: Multi-objective, Pareto-based
- **CMA-ES**: Single-objective, gradient-free
- **Random Search**: Baseline comparison

## Output Format

Return a detailed JSON analysis with this structure:

```json
{
  "engineering_features": [
    {
      "action": "extract_frequency",
      "domain": "result_extraction",
      "description": "Extract fundamental frequency from modal analysis using centralized extractor",
      "params": {
        "extractor": "extract_frequency",
        "op2_file": "solution_2.op2",
        "subcase": 1,
        "mode_number": 1
      },
      "why_engineering": "Uses centralized extractor library for standardized OP2 extraction"
    },
    {
      "action": "extract_mass",
      "domain": "result_extraction",
      "description": "Extract mass from CAD expression p173",
      "params": {
        "extractor": "extract_mass_from_expression",
        "prt_file": "Model.prt",
        "expression_name": "p173"
      },
      "why_engineering": "Reads NX expression value directly from part file"
    }
  ],
  "inline_calculations": [
    {
      "action": "convert_units",
      "description": "Convert mass from kg to grams",
      "params": {
        "input": "mass_kg",
        "operation": "multiply",
        "factor": 1000.0
      },
      "code_hint": "mass_g = mass_kg * 1000.0"
    }
  ],
  "post_processing_hooks": [],
  "optimization": {
    "protocol": "protocol_11_multi_objective",
    "algorithm": "NSGAIISampler",
    "type": "multi_objective",
    "design_variables": [
      {
        "parameter": "beam_thickness",
        "type": "NX_expression",
        "bounds": [5.0, 10.0],
        "unit": "mm"
      }
    ],
    "objectives": [
      {
        "name": "mass",
        "type": "minimize",
        "target": "mass_g",
        "unit": "g"
      },
      {
        "name": "frequency",
        "type": "maximize",
        "target": "fundamental_freq",
        "unit": "Hz"
      }
    ],
    "constraints": [
      {
        "name": "max_displacement",
        "type": "less_than",
        "threshold": 1.5,
        "unit": "mm"
      }
    ],
    "multi_solution_workflow": {
      "required": true,
      "solutions": ["static", "modal"],
      "protocol": "SolveAllSolutions with Foreground mode"
    }
  },
  "dashboard": {
    "visualization": "parallel_coordinates + pareto_front",
    "backend_port": 8000,
    "frontend_port": 3003
  },
  "summary": {
    "total_steps": 3,
    "engineering_needed": 2,
    "auto_generate": 1,
    "uses_centralized_extractors": true,
    "multi_objective": true,
    "multi_solution_workflow": true,
    "research_needed": []
  }
}
```

## Analysis Guidelines

Be intelligent about:

1. **Extractor Selection**
   - Always prefer centralized extractors over custom OP2 code
   - Match extractor to result type (displacement, stress, frequency, mass)

2. **Multi-Objective Optimization**
   - Identify conflicting objectives requiring Pareto analysis
   - Use NSGA-II for 2-3 objectives
   - Use proper semantic directions (no negative values)

3. **Multi-Solution Workflows**
   - Detect when static + modal analysis is needed
   - Flag requirement for `SolveAllSolutions()` protocol
   - Identify separate OP2 files per solution

4. **Dashboard Integration**
   - Suggest parallel coordinates for high-dimensional problems
   - Recommend Pareto front visualization for multi-objective
   - Note real-time monitoring capability

5. **Protocol Selection**
   - Protocol 11: Multi-objective NSGA-II
   - Protocol 10: Single-objective with intelligent strategies
   - Legacy: Basic single-objective TPE

Return ONLY the JSON analysis, no other text.
