Files
Atomizer/docs/06_PROTOCOLS_DETAILED/ASSEMBLY_FEM_WORKFLOW.md
Antoine 8cbdbcad78 feat: Add Protocol 13 adaptive optimization, Plotly charts, and dashboard improvements
## Protocol 13: Adaptive Multi-Objective Optimization
- Iterative FEA + Neural Network surrogate workflow
- Initial FEA sampling, NN training, NN-accelerated search
- FEA validation of top NN predictions, retraining loop
- adaptive_state.json tracks iteration history and best values
- M1 mirror study (V11) with 103 FEA, 3000 NN trials

## Dashboard Visualization Enhancements
- Added Plotly.js interactive charts (parallel coords, Pareto, convergence)
- Lazy loading with React.lazy() for performance
- Code splitting: plotly.js-basic-dist (~1MB vs 3.5MB)
- Chart library toggle (Recharts default, Plotly on-demand)
- ExpandableChart component for full-screen modal views
- ConsoleOutput component for real-time log viewing

## Documentation
- Protocol 13 detailed documentation
- Dashboard visualization guide
- Plotly components README
- Updated run-optimization skill with Mode 5 (adaptive)

## Bug Fixes
- Fixed TypeScript errors in dashboard components
- Fixed Card component to accept ReactNode title
- Removed unused imports across components

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 07:41:54 -05:00

11 KiB

Assembly FEM Optimization Workflow

This document describes the multi-part assembly FEM workflow used when optimizing complex assemblies with .afm (Assembly FEM) files.

CRITICAL: Working Copy Requirement

NEVER run optimization directly on user's master model files.

Before any optimization run, ALL model files must be copied to the study's working directory:

Source (NEVER MODIFY)                    Working Copy (optimization runs here)
────────────────────────────────────────────────────────────────────────────
C:/Users/.../M1-Gigabit/Latest/          studies/{study}/1_setup/model/
├── M1_Blank.prt                    →    ├── M1_Blank.prt
├── M1_Blank_fem1.fem               →    ├── M1_Blank_fem1.fem
├── M1_Blank_fem1_i.prt             →    ├── M1_Blank_fem1_i.prt
├── M1_Vertical_Support_Skeleton.prt →   ├── M1_Vertical_Support_Skeleton.prt
├── ASSY_M1_assyfem1.afm            →    ├── ASSY_M1_assyfem1.afm
└── ASSY_M1_assyfem1_sim1.sim       →    └── ASSY_M1_assyfem1_sim1.sim

Why: Optimization iteratively modifies expressions, meshes, and saves files. If corruption occurs during iteration (solver crash, bad parameter combo), the working copy can be deleted and re-copied. Master files remain safe.

Files to Copy:

  • *.prt - All part files (geometry + idealized)
  • *.fem - All FEM files
  • *.afm - Assembly FEM files
  • *.sim - Simulation files
  • *.exp - Expression files (if any)

Overview

Assembly FEMs have a more complex dependency chain than single-part simulations:

.prt (geometry) → _fem1.fem (component mesh) → .afm (assembly mesh) → .sim (solution)

Each level must be updated in sequence when design parameters change.

When This Workflow Applies

This workflow is automatically triggered when:

  • The working directory contains .afm files
  • Multiple .fem files exist (component meshes)
  • Multiple .prt files exist (component geometry)

Examples:

  • M1 Mirror assembly (M1_Blank + M1_Vertical_Support_Skeleton)
  • Multi-component mechanical assemblies
  • Any NX assembly where components have separate FEM files

The 4-Step Workflow

Step 1: Update Expressions in Geometry Part (.prt)

Open M1_Blank.prt
├── Find and update design expressions
│   ├── whiffle_min = 42.5
│   ├── whiffle_outer_to_vertical = 75.0
│   └── inner_circular_rib_dia = 550.0
├── Rebuild geometry (DoUpdate)
└── Save part

The .prt file contains the parametric CAD model with expressions that drive dimensions. These expressions are updated with new design parameter values, then the geometry is rebuilt.

Step 1b: Update ALL Linked Geometry Parts (CRITICAL!)

⚠️ THIS STEP IS CRITICAL - SKIPPING IT CAUSES CORRUPT RESULTS ⚠️

For each geometry part with linked expressions:
├── Open M1_Vertical_Support_Skeleton.prt
├── DoUpdate() - propagate linked expression changes
├── Geometry rebuilds to match M1_Blank
└── Save part

Why this is critical:

  • M1_Vertical_Support_Skeleton has expressions linked to M1_Blank
  • When M1_Blank geometry changes, the support skeleton MUST also update
  • If not updated, FEM nodes will be at OLD positions → nodes not coincident → merge fails
  • Result: "billion nm" RMS values (corrupt displacement data)

Rule: YOU MUST UPDATE ALL GEOMETRY PARTS UNDER THE .sim FILE!

  • If there are 5 geometry parts, update all 5
  • If there are 10 geometry parts, update all 10
  • Unless explicitly told otherwise in the study config

Step 2: Update Component FEM Files (.fem)

For each component FEM:
├── Open M1_Blank_fem1.fem
│   ├── UpdateFemodel() - regenerates mesh from updated geometry
│   └── Save FEM
├── Open M1_Vertical_Support_Skeleton_fem1.fem
│   ├── UpdateFemodel()
│   └── Save FEM
└── ... (repeat for all component FEMs)

Each component FEM is linked to its source geometry. UpdateFemodel() regenerates the mesh based on the updated geometry.

Step 3: Update Assembly FEM (.afm)

Open ASSY_M1_assyfem1.afm
├── UpdateFemodel() - updates assembly mesh
├── Merge coincident nodes (at component interfaces)
├── Resolve labeling conflicts (duplicate node/element IDs)
└── Save AFM

The assembly FEM combines component meshes. This step:

  • Reconnects meshes at shared interfaces
  • Resolves numbering conflicts between component meshes
  • Ensures mesh continuity for accurate analysis

Step 4: Solve Simulation (.sim)

Open ASSY_M1_assyfem1_sim1.sim
├── Execute solve
│   ├── Foreground mode for all solutions
│   └── or Background mode for specific solution
└── Save simulation

The simulation file references the assembly FEM and contains solution setup (loads, constraints, subcases).

File Dependencies

M1 Mirror Example:

M1_Blank.prt ─────────────────────> M1_Blank_fem1.fem ─────────┐
     │                                    │                    │
     │ (expressions)                      │ (component mesh)   │
     ↓                                    ↓                    │
M1_Vertical_Support_Skeleton.prt ──> M1_..._Skeleton_fem1.fem ─┤
                                                               │
                                                               ↓
                                          ASSY_M1_assyfem1.afm ──> ASSY_M1_assyfem1_sim1.sim
                                               (assembly mesh)         (solution)

API Functions Used

Step NX API Call Purpose
1 OpenBase() Open .prt file
1 ImportFromFile() Import expressions from .exp file (preferred)
1 DoUpdate() Rebuild geometry
2-3 UpdateFemodel() Regenerate mesh from geometry
3 DuplicateNodesCheckBuilder Merge coincident nodes at interfaces
3 MergeOccurrenceNodes = True Critical: enables cross-component merge
4 SolveAllSolutions() Execute FEA (Foreground mode recommended)

Expression Update Method

The recommended approach uses expression file import:

# Write expressions to .exp file
with open(exp_path, 'w') as f:
    for name, value in expressions.items():
        unit = get_unit_for_expression(name)
        f.write(f"[{unit}]{name}={value}\n")

# Import into part
modified, errors = workPart.Expressions.ImportFromFile(
    exp_path,
    NXOpen.ExpressionCollection.ImportMode.Replace
)

This is more reliable than EditExpressionWithUnits() for batch updates.

Error Handling

Common issues and solutions:

"Update undo happened"

  • Geometry update failed due to constraint violations
  • Check expression values are within valid ranges
  • May need to adjust parameter bounds

"This operation can only be done on the work part"

  • Work part not properly set before operation
  • Use SetWork() to make target part the work part

Node merge warnings

  • Manual intervention may be needed for complex interfaces
  • Check mesh connectivity in NX after solve

"Billion nm" RMS values

  • Indicates node merging failed - coincident nodes not properly merged
  • Check MergeOccurrenceNodes = True is set
  • Verify tolerance (0.01 mm recommended)
  • Run node merge after every FEM update, not just once

Configuration

The workflow auto-detects assembly FEMs, but you can configure behavior:

{
  "nx_settings": {
    "expression_part": "M1_Blank",  // Override auto-detection
    "component_fems": [              // Explicit list of FEMs to update
      "M1_Blank_fem1.fem",
      "M1_Vertical_Support_Skeleton_fem1.fem"
    ],
    "afm_file": "ASSY_M1_assyfem1.afm"
  }
}

Implementation Reference

See optimization_engine/solve_simulation.py for the full implementation:

  • detect_assembly_fem() - Detects if assembly workflow needed
  • update_expressions_in_part() - Step 1 implementation
  • update_fem_part() - Step 2 implementation
  • update_assembly_fem() - Step 3 implementation
  • solve_simulation_file() - Step 4 implementation

HEEDS-Style Iteration Folder Management (V9+)

For complex assemblies, each optimization trial uses a fresh copy of the master model:

study_name/
├── 1_setup/
│   └── model/           # Master model files (NEVER MODIFY)
│       ├── ASSY_M1.prt
│       ├── ASSY_M1_assyfem1.afm
│       ├── ASSY_M1_assyfem1_sim1.sim
│       ├── M1_Blank.prt
│       ├── M1_Blank_fem1.fem
│       └── ...
├── 2_iterations/
│   ├── iter0/           # Trial 0 working copy
│   │   ├── [all model files]
│   │   ├── params.exp   # Expression values for this trial
│   │   └── results/     # OP2, Zernike CSV, etc.
│   ├── iter1/           # Trial 1 working copy
│   └── ...
└── 3_results/
    └── study.db         # Optuna database

Why Fresh Copies Per Iteration?

  1. Corruption isolation: If mesh regeneration fails mid-trial, only that iteration is affected
  2. Reproducibility: Can re-run any trial by using its params.exp
  3. Debugging: All intermediate files preserved for post-mortem analysis
  4. Parallelization: Multiple NX sessions could run different iterations (future)

Iteration Folder Contents

File Purpose
*.prt, *.fem, *.afm, *.sim Fresh copy of all NX model files
params.exp Expression file with trial parameter values
*-solution_1.op2 Nastran results (after solve)
results/zernike_trial_N.csv Extracted Zernike metrics

0-Based Iteration Numbering

Iterations are numbered starting from 0 to match Optuna trial numbers:

  • iter0 = Optuna trial 0 = Dashboard shows trial 0
  • iter1 = Optuna trial 1 = Dashboard shows trial 1

This ensures cross-referencing between dashboard, database, and file system is straightforward.

Multi-Subcase Solutions

For gravity analysis at multiple orientations, use subcases:

Simulation Setup in NX:
├── Subcase 1: 90 deg elevation (zenith/polishing)
├── Subcase 2: 20 deg elevation (low angle reference)
├── Subcase 3: 40 deg elevation
└── Subcase 4: 60 deg elevation

Solving All Subcases

Use solution_name=None or solve_all_subcases=True to ensure all subcases are solved:

"nx_settings": {
  "solution_name": "Solution 1",
  "solve_all_subcases": true
}

Subcase ID Mapping

NX subcase IDs (1, 2, 3, 4) may not match the angle labels. Always define explicit mapping:

"zernike_settings": {
  "subcases": ["1", "2", "3", "4"],
  "subcase_labels": {
    "1": "90deg",
    "2": "20deg",
    "3": "40deg",
    "4": "60deg"
  },
  "reference_subcase": "2"
}

Tips

  1. Start with baseline solve: Before optimization, manually verify the full workflow completes in NX
  2. Check mesh quality: Poor mesh quality after updates can cause solve failures
  3. Monitor memory: Assembly FEMs with many components use significant memory
  4. Use Foreground mode: For multi-subcase solutions, Foreground mode ensures all subcases complete
  5. Validate OP2 data: Check for corrupt results (all zeros, unrealistic magnitudes) before processing
  6. Preserve user NX sessions: NXSessionManager tracks PIDs to avoid closing user's NX instances