## 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>
330 lines
11 KiB
Markdown
330 lines
11 KiB
Markdown
# 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:
|
|
|
|
```python
|
|
# 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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```json
|
|
"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:
|
|
|
|
```json
|
|
"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
|