Files
Atomizer/docs/NX_MULTI_SOLUTION_PROTOCOL.md
Anto01 dd7f0c0f82 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>
2025-11-24 07:49:48 -05:00

198 lines
6.4 KiB
Markdown

# 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