# 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