Files
Atomizer/examples/test_journal_optimization.py
Anto01 718c72bea2 feat: Implement complete FEM regeneration workflow
This commit completes the optimization loop infrastructure by implementing
the full FEM regeneration workflow based on the user's working journal.

## Changes

### FEM Regeneration Workflow (solve_simulation.py)
- Added STEP 1: Switch to Bracket.prt and update geometry
  - Uses SetActiveDisplay() to make Bracket.prt active
  - Calls UpdateManager.DoUpdate() to rebuild CAD geometry with new expressions
- Added STEP 2: Switch to Bracket_fem1 and update FE model
  - Uses SetActiveDisplay() to make FEM active
  - Calls fEModel1.UpdateFemodel() to regenerate FEM with updated geometry
- Added STEP 3: Switch back to sim part before solving
- Close and reopen .sim file to force reload from disk

### Enhanced Journal Output (nx_solver.py)
- Display journal stdout output for debugging
- Shows all journal steps: geometry update, FEM regeneration, solve, save
- Helps verify workflow execution

### Verification Tools
- Added verify_parametric_link.py journal to check expression dependencies
- Added FEM_REGENERATION_STATUS.md documenting the complete status

## Status

###  Fully Functional Components
1. Parameter updates - nx_updater.py modifies .prt expressions
2. NX solver - ~4s per solve via journal
3. Result extraction - pyNastran reads .op2 files
4. History tracking - saves to JSON/CSV
5. Optimization loop - Optuna explores parameter space
6. **FEM regeneration workflow** - Journal executes all steps successfully

###  Remaining Issue: Expressions Not Linked to Geometry
The optimization returns identical stress values (197.89 MPa) for all trials
because the Bracket.prt expressions are not referenced by any geometry features.

Evidence:
- Journal verification shows FEM update steps execute successfully
- Feature dependency check shows no features reference the expressions
- All optimization infrastructure is working correctly

The code is ready - waiting for Bracket.prt to have its expressions properly
linked to the geometry features in NX.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-15 12:43:31 -05:00

134 lines
4.2 KiB
Python

"""
Test: Complete Optimization with Journal-Based NX Solver
This tests the complete workflow:
1. Update model parameters in .prt
2. Solve via journal (using running NX GUI)
3. Extract results from OP2
4. Run optimization loop
REQUIREMENTS:
- Simcenter3D must be open (but no files need to be loaded)
- test_env conda environment activated
"""
from pathlib import Path
import sys
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
from optimization_engine.runner import OptimizationRunner
from optimization_engine.nx_updater import update_nx_model
from optimization_engine.nx_solver import run_nx_simulation
from optimization_engine.result_extractors.extractors import (
stress_extractor,
displacement_extractor
)
def bracket_model_updater(design_vars: dict):
"""Update bracket model parameters."""
prt_file = project_root / "examples/bracket/Bracket.prt"
print(f"\n[MODEL UPDATE] {prt_file.name}")
for name, value in design_vars.items():
print(f" {name} = {value:.4f}")
update_nx_model(prt_file, design_vars, backup=False)
def bracket_simulation_runner() -> Path:
"""
Run NX solver via journal on running NX GUI session.
This connects to the running Simcenter3D GUI and:
1. Opens the .sim file
2. Updates the FEM component
3. Solves the simulation
4. Returns path to .op2 file
"""
sim_file = project_root / "examples/bracket/Bracket_sim1.sim"
print("\n[SIMULATION] Running via journal on NX GUI...")
print(f" SIM file: {sim_file.name}")
try:
# Run solver via journal (connects to running NX GUI)
op2_file = run_nx_simulation(
sim_file=sim_file,
nastran_version="2412",
timeout=300, # 5 minute timeout
cleanup=True, # Clean up temp files
use_journal=True # Use journal mode (requires NX GUI open)
)
print(f"[SIMULATION] Complete! Results: {op2_file.name}")
return op2_file
except Exception as e:
print(f"[SIMULATION] FAILED: {e}")
raise
if __name__ == "__main__":
print("="*60)
print("JOURNAL-BASED OPTIMIZATION TEST")
print("="*60)
print("\nREQUIREMENTS:")
print("- Simcenter3D must be OPEN (no files need to be loaded)")
print("- Will run 3 optimization trials")
print("- Each trial: update params -> solve via journal -> extract results")
print("="*60)
response = input("\nIs Simcenter3D open? (yes/no): ")
if response.lower() not in ['yes', 'y']:
print("Please open Simcenter3D and try again.")
sys.exit(0)
config_path = project_root / "examples/bracket/optimization_config_stress_displacement.json"
runner = OptimizationRunner(
config_path=config_path,
model_updater=bracket_model_updater,
simulation_runner=bracket_simulation_runner, # Journal-based solver!
result_extractors={
'stress_extractor': stress_extractor,
'displacement_extractor': displacement_extractor
}
)
# Run just 3 trials for testing
runner.config['optimization_settings']['n_trials'] = 3
print("\n" + "="*60)
print("Starting optimization with 3 trials")
print("Objective: Minimize max von Mises stress")
print("Constraint: Max displacement <= 1.0 mm")
print("Solver: Journal-based (using running NX GUI)")
print("="*60)
try:
study = runner.run(study_name="journal_solver_test")
print("\n" + "="*60)
print("OPTIMIZATION COMPLETE!")
print("="*60)
print(f"\nBest stress: {study.best_value:.2f} MPa")
print(f"\nBest parameters:")
for param, value in study.best_params.items():
print(f" {param}: {value:.4f}")
print(f"\nResults saved to: {runner.output_dir}")
print("\nCheck history.csv to see optimization progress!")
except Exception as e:
print(f"\n{'='*60}")
print("ERROR DURING OPTIMIZATION")
print("="*60)
print(f"{e}")
import traceback
traceback.print_exc()
print("\nMake sure:")
print(" - Simcenter3D is open and running")
print(" - .sim file is valid and solvable")
print(" - No other processes are locking the files")