# Assembly FEM Optimization Workflow This document describes the multi-part assembly FEM workflow used when optimizing complex assemblies with `.afm` (Assembly FEM) files. ## 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 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 ## 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