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

6.4 KiB

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

# 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:

# 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
    

  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

# 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)

# 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