Files
Atomizer/tests/journal_with_regenerate.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

133 lines
5.8 KiB
Python

# NX 2412
# Journal created by antoi on Sat Nov 15 12:34:27 2025 Eastern Standard Time
#
import math
import NXOpen
import NXOpen.CAE
def main(args) :
theSession = NXOpen.Session.GetSession() #type: NXOpen.Session
workSimPart = theSession.Parts.BaseWork
displaySimPart = theSession.Parts.BaseDisplay
markId1 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Change Displayed Part")
part1 = theSession.Parts.FindObject("Bracket")
baseTemplateManager1 = theSession.XYPlotManager.TemplateManager
status1, partLoadStatus1 = theSession.Parts.SetActiveDisplay(part1, NXOpen.DisplayPartOption.AllowAdditional, NXOpen.PartDisplayPartWorkPartOption.UseLast)
workSimPart = NXOpen.BasePart.Null
workPart = theSession.Parts.Work
displaySimPart = NXOpen.BasePart.Null
displayPart = theSession.Parts.Display
partLoadStatus1.Dispose()
# ----------------------------------------------
# Menu: Tools->Utilities->Expressions...
# ----------------------------------------------
markId2 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start")
theSession.SetUndoMarkName(markId2, "Expressions Dialog")
markId3 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Edit Expression")
expression1 = workPart.Expressions.FindObject("support_angle")
unit1 = workPart.UnitCollection.FindObject("Degrees")
workPart.Expressions.EditExpressionWithUnits(expression1, unit1, "36")
markId4 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Expressions")
theSession.DeleteUndoMark(markId4, None)
markId5 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Expressions")
markId6 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Make Up to Date")
objects1 = [NXOpen.NXObject.Null] * 1
objects1[0] = expression1
theSession.UpdateManager.MakeUpToDate(objects1, markId6)
markId7 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "NX update")
nErrs1 = theSession.UpdateManager.DoUpdate(markId7)
theSession.DeleteUndoMark(markId7, "NX update")
theSession.DeleteUndoMark(markId6, None)
theSession.DeleteUndoMark(markId5, None)
theSession.SetUndoMarkName(markId2, "Expressions")
markId8 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Change Displayed Part")
simPart1 = theSession.Parts.FindObject("Bracket_sim1")
status2, partLoadStatus2 = theSession.Parts.SetActiveDisplay(simPart1, NXOpen.DisplayPartOption.AllowAdditional, NXOpen.PartDisplayPartWorkPartOption.UseLast)
workPart = NXOpen.Part.Null
workSimPart = theSession.Parts.BaseWork # Bracket_sim1
displayPart = NXOpen.Part.Null
displaySimPart = theSession.Parts.BaseDisplay # Bracket_sim1
partLoadStatus2.Dispose()
markId9 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Open in Window")
femPart1 = theSession.Parts.FindObject("Bracket_fem1")
baseTemplateManager2 = theSession.XYPlotManager.TemplateManager
status3, partLoadStatus3 = theSession.Parts.SetActiveDisplay(femPart1, NXOpen.DisplayPartOption.AllowAdditional, NXOpen.PartDisplayPartWorkPartOption.SameAsDisplay)
workFemPart = theSession.Parts.BaseWork
displayFemPart = theSession.Parts.BaseDisplay
partLoadStatus3.Dispose()
# ----------------------------------------------
# Menu: Edit->Update
# ----------------------------------------------
markId10 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Update FE Model")
fEModel1 = workFemPart.FindObject("FEModel")
fEModel1.UpdateFemodel()
markId11 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Change Displayed Part")
status4, partLoadStatus4 = theSession.Parts.SetActiveDisplay(simPart1, NXOpen.DisplayPartOption.AllowAdditional, NXOpen.PartDisplayPartWorkPartOption.UseLast)
workSimPart = theSession.Parts.BaseWork # Bracket_sim1
displaySimPart = theSession.Parts.BaseDisplay # Bracket_sim1
partLoadStatus4.Dispose()
# ----------------------------------------------
# Menu: Analysis->Solve...
# ----------------------------------------------
markId12 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start")
theSession.SetUndoMarkName(markId12, "Solve Dialog")
markId13 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Solve")
theSession.DeleteUndoMark(markId13, None)
markId14 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Solve")
theCAESimSolveManager = NXOpen.CAE.SimSolveManager.GetSimSolveManager(theSession)
psolutions1 = [NXOpen.CAE.SimSolution.Null] * 1
simSimulation1 = workSimPart.FindObject("Simulation")
simSolution1 = simSimulation1.FindObject("Solution[Solution 1]")
psolutions1[0] = simSolution1
numsolutionssolved1, numsolutionsfailed1, numsolutionsskipped1 = theCAESimSolveManager.SolveChainOfSolutions(psolutions1, NXOpen.CAE.SimSolution.SolveOption.Solve, NXOpen.CAE.SimSolution.SetupCheckOption.CompleteDeepCheckAndOutputErrors, NXOpen.CAE.SimSolution.SolveMode.Background)
theSession.DeleteUndoMark(markId14, None)
theSession.SetUndoMarkName(markId12, "Solve")
# ----------------------------------------------
# Menu: File->Save
# ----------------------------------------------
simPart2 = workSimPart
partSaveStatus1 = simPart2.Save(NXOpen.BasePart.SaveComponents.TrueValue, NXOpen.BasePart.CloseAfterSave.FalseValue)
partSaveStatus1.Dispose()
# ----------------------------------------------
# Menu: Tools->Automation->Journal->Stop Recording
# ----------------------------------------------
if __name__ == '__main__':
main(sys.argv[1:])