Problem: When running optimization studies with multiple solutions (e.g., static + modal), NX opens solution monitor windows for each trial. These windows superpose and cause usability issues during long optimization runs. Solution: - Automatically disable solution monitor when solving all solutions (solution_name=None) - Loop through all solutions and set "solution monitor" property to False - Implemented in solve_simulation.py before solve execution (lines 271-295) - Includes error handling with graceful fallback Benefits: - No monitor window pile-up during optimization studies - Better performance (no GUI overhead) - No user configuration required - works automatically - Based on user-recorded journal (journal_monitor_window_off.py) Documentation: - Updated docs/NX_MULTI_SOLUTION_PROTOCOL.md with solution monitor control section - Added implementation details and when the feature activates - Cross-referenced user's recorded journal Implementation: optimization_engine/solve_simulation.py Documentation: docs/NX_MULTI_SOLUTION_PROTOCOL.md Reference: nx_journals/user_generated_journals/journal_monitor_window_off.py
252 lines
8.6 KiB
Markdown
252 lines
8.6 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
|
|
```
|
|
|
|
---
|
|
|
|
## Solution Monitor Window Control (November 24, 2025)
|
|
|
|
### Problem: Monitor Window Pile-Up
|
|
|
|
When running optimization studies with multiple trials, NX opens solution monitor windows for each trial. These windows:
|
|
- Superpose on top of each other
|
|
- Cannot be easily closed programmatically
|
|
- Cause usability issues during long optimization runs
|
|
- Slow down the optimization process
|
|
|
|
### Solution: Automatic Monitor Disabling
|
|
|
|
The solution monitor is now automatically disabled when solving multiple solutions (when `solution_name=None`).
|
|
|
|
**Implementation**: `optimization_engine/solve_simulation.py` lines 271-295
|
|
|
|
```python
|
|
# CRITICAL: Disable solution monitor when solving multiple solutions
|
|
# This prevents NX from opening multiple monitor windows which superpose and cause usability issues
|
|
if not solution_name:
|
|
print("[JOURNAL] Disabling solution monitor for all solutions to prevent window pile-up...")
|
|
try:
|
|
# Get all solutions in the simulation
|
|
solutions_disabled = 0
|
|
solution_num = 1
|
|
while True:
|
|
try:
|
|
solution_obj_name = f"Solution[Solution {solution_num}]"
|
|
simSolution = simSimulation1.FindObject(solution_obj_name)
|
|
if simSolution:
|
|
propertyTable = simSolution.SolverOptionsPropertyTable
|
|
propertyTable.SetBooleanPropertyValue("solution monitor", False)
|
|
solutions_disabled += 1
|
|
solution_num += 1
|
|
else:
|
|
break
|
|
except:
|
|
break # No more solutions
|
|
print(f"[JOURNAL] Solution monitor disabled for {solutions_disabled} solution(s)")
|
|
except Exception as e:
|
|
print(f"[JOURNAL] WARNING: Could not disable solution monitor: {e}")
|
|
print(f"[JOURNAL] Continuing with solve anyway...")
|
|
```
|
|
|
|
**When this activates**:
|
|
- Automatically when `solution_name=None` (solve all solutions mode)
|
|
- For any study with multiple trials (typical optimization scenario)
|
|
- No user configuration required
|
|
|
|
**User-recorded journal**: `nx_journals/user_generated_journals/journal_monitor_window_off.py`
|
|
|
|
---
|
|
|
|
## 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
|
|
4. **Solution monitor windows piling up**: Fixed by automatically disabling monitor for multi-solution runs
|
|
|
|
---
|
|
|
|
## 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
|