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>
This commit is contained in:
@@ -278,6 +278,13 @@ sys.argv = ['', r'{sim_file.absolute()}'] # Set argv for the main function
|
||||
|
||||
elapsed_time = time.time() - start_time
|
||||
|
||||
# Display journal output for debugging
|
||||
if self.use_journal:
|
||||
if result.stdout and result.stdout.strip():
|
||||
print("[JOURNAL OUTPUT]")
|
||||
for line in result.stdout.strip().split('\n'):
|
||||
print(f" {line}")
|
||||
|
||||
# Check for journal errors
|
||||
if self.use_journal and result.stderr and "error" in result.stderr.lower():
|
||||
print("[JOURNAL ERRORS]")
|
||||
|
||||
@@ -62,63 +62,74 @@ def main(args):
|
||||
simPart1 = workSimPart
|
||||
theSession.Post.UpdateUserGroupsFromSimPart(simPart1)
|
||||
|
||||
# Reload all components to pick up parameter changes from .prt files
|
||||
print("[JOURNAL] Reloading components to pick up .prt parameter changes...")
|
||||
# STEP 1: Switch to Bracket.prt and update geometry with new expression values
|
||||
print("[JOURNAL] STEP 1: Updating Bracket.prt geometry...")
|
||||
try:
|
||||
workSimPart.ComponentAssembly.ReloadComponents(
|
||||
NXOpen.Assemblies.ComponentAssembly.ReloadOption.AllLoaded
|
||||
)
|
||||
print("[JOURNAL] Components reloaded")
|
||||
# Find the Bracket part
|
||||
bracketPart = theSession.Parts.FindObject("Bracket")
|
||||
if bracketPart:
|
||||
# Make Bracket the active display part
|
||||
status, partLoadStatus = theSession.Parts.SetActiveDisplay(
|
||||
bracketPart,
|
||||
NXOpen.DisplayPartOption.AllowAdditional,
|
||||
NXOpen.PartDisplayPartWorkPartOption.UseLast
|
||||
)
|
||||
partLoadStatus.Dispose()
|
||||
|
||||
# CRITICAL: Update the geometry model - rebuilds features with new expressions
|
||||
markId_update = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "NX update")
|
||||
nErrs = theSession.UpdateManager.DoUpdate(markId_update)
|
||||
theSession.DeleteUndoMark(markId_update, "NX update")
|
||||
print(f"[JOURNAL] Bracket geometry updated ({nErrs} errors)")
|
||||
else:
|
||||
print("[JOURNAL] WARNING: Could not find Bracket part")
|
||||
except Exception as e:
|
||||
print(f"[JOURNAL] Warning: Could not reload components: {e}")
|
||||
print(f"[JOURNAL] ERROR updating Bracket.prt: {e}")
|
||||
|
||||
# Make FEM work component (to ensure it's updated)
|
||||
# Find the FEM component - pattern: "COMPONENT <name>_fem1 1"
|
||||
markId1 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Make Work Part")
|
||||
# STEP 2: Switch to Bracket_fem1 and update FE model
|
||||
print("[JOURNAL] STEP 2: Opening Bracket_fem1.fem...")
|
||||
try:
|
||||
# Find the FEM part
|
||||
femPart1 = theSession.Parts.FindObject("Bracket_fem1")
|
||||
if femPart1:
|
||||
# Make FEM the active display part
|
||||
status, partLoadStatus = theSession.Parts.SetActiveDisplay(
|
||||
femPart1,
|
||||
NXOpen.DisplayPartOption.AllowAdditional,
|
||||
NXOpen.PartDisplayPartWorkPartOption.SameAsDisplay
|
||||
)
|
||||
partLoadStatus.Dispose()
|
||||
|
||||
# Get all components and find the FEM one
|
||||
rootComponent = workSimPart.ComponentAssembly.RootComponent
|
||||
femComponent = None
|
||||
workFemPart = theSession.Parts.BaseWork
|
||||
|
||||
for component in rootComponent.GetChildren():
|
||||
if "_fem" in component.DisplayName.lower():
|
||||
femComponent = component
|
||||
break
|
||||
# CRITICAL: Update FE Model - regenerates FEM with new geometry from Bracket.prt
|
||||
print("[JOURNAL] Updating FE Model...")
|
||||
fEModel1 = workFemPart.FindObject("FEModel")
|
||||
if fEModel1:
|
||||
fEModel1.UpdateFemodel()
|
||||
print("[JOURNAL] FE Model updated with new geometry!")
|
||||
else:
|
||||
print("[JOURNAL] WARNING: Could not find FEModel object")
|
||||
else:
|
||||
print("[JOURNAL] WARNING: Could not find Bracket_fem1 part")
|
||||
except Exception as e:
|
||||
print(f"[JOURNAL] ERROR updating FEM: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if femComponent:
|
||||
print(f"[JOURNAL] Switching to FEM component: {femComponent.DisplayName}")
|
||||
|
||||
# Make FEM the work component (this is what your recorded journal does)
|
||||
partLoadStatus2 = theSession.Parts.SetWorkComponent(
|
||||
femComponent,
|
||||
NXOpen.PartCollection.RefsetOption.Entire,
|
||||
NXOpen.PartCollection.WorkComponentOption.Visible
|
||||
)
|
||||
partLoadStatus2.Dispose()
|
||||
|
||||
# Get the FEM part and try to update it
|
||||
workFemPart = theSession.Parts.BaseWork
|
||||
try:
|
||||
# Try to update the FEM to pick up geometry/parameter changes
|
||||
print("[JOURNAL] Updating FEM to recognize parameter changes...")
|
||||
if hasattr(workFemPart, 'FemPart'):
|
||||
workFemPart.FemPart.UpdateFeModel()
|
||||
print("[JOURNAL] FEM updated")
|
||||
except Exception as e:
|
||||
print(f"[JOURNAL] Note: Could not update FEM (may not be necessary): {e}")
|
||||
|
||||
# Switch back to sim part (this forces NX to recognize updates)
|
||||
markId2 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Make Work Part")
|
||||
partLoadStatus3 = theSession.Parts.SetWorkComponent(
|
||||
NXOpen.Assemblies.Component.Null,
|
||||
NXOpen.PartCollection.RefsetOption.Entire,
|
||||
NXOpen.PartCollection.WorkComponentOption.Visible
|
||||
# STEP 3: Switch back to sim part
|
||||
print("[JOURNAL] STEP 3: Switching back to sim part...")
|
||||
try:
|
||||
status, partLoadStatus = theSession.Parts.SetActiveDisplay(
|
||||
simPart1,
|
||||
NXOpen.DisplayPartOption.AllowAdditional,
|
||||
NXOpen.PartDisplayPartWorkPartOption.UseLast
|
||||
)
|
||||
partLoadStatus.Dispose()
|
||||
workSimPart = theSession.Parts.BaseWork
|
||||
partLoadStatus3.Dispose()
|
||||
print("[JOURNAL] Switched back to sim part")
|
||||
else:
|
||||
print("[JOURNAL] WARNING: No FEM component found, proceeding with solve anyway")
|
||||
print("[JOURNAL] Switched back to sim part")
|
||||
except Exception as e:
|
||||
print(f"[JOURNAL] WARNING: Error switching to sim part: {e}")
|
||||
|
||||
# Note: Old output files are deleted by nx_solver.py before calling this journal
|
||||
# This ensures NX performs a fresh solve
|
||||
|
||||
Reference in New Issue
Block a user