600 lines
18 KiB
Markdown
600 lines
18 KiB
Markdown
|
|
# Bracket Stiffness Optimization - Issues Log
|
||
|
|
**Date**: November 21, 2025
|
||
|
|
**Study**: bracket_stiffness_optimization
|
||
|
|
**Protocol**: Protocol 10 (IMSO)
|
||
|
|
|
||
|
|
## Executive Summary
|
||
|
|
Attempted to create a new bracket stiffness optimization study using Protocol 10. Encountered **8 critical issues** that prevented the study from running successfully. All issues are protocol violations that should be prevented by better templates, validation, and documentation.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Issue #1: Unicode/Emoji Characters Breaking Windows Console
|
||
|
|
**Severity**: CRITICAL
|
||
|
|
**Category**: Output Formatting
|
||
|
|
**Protocol Violation**: Using non-ASCII characters in code output
|
||
|
|
|
||
|
|
### What Happened
|
||
|
|
Code contained unicode symbols (≤, ✓, ✗, 🎯, 📊, ⚠) in print statements, causing:
|
||
|
|
```
|
||
|
|
UnicodeEncodeError: 'charmap' codec can't encode character '\u2264' in position 17
|
||
|
|
```
|
||
|
|
|
||
|
|
### Root Cause
|
||
|
|
- Windows cmd uses cp1252 encoding by default
|
||
|
|
- Unicode symbols not in cp1252 cause crashes
|
||
|
|
- User explicitly requested NO emojis/unicode in previous sessions
|
||
|
|
|
||
|
|
### Files Affected
|
||
|
|
- `run_optimization.py` (multiple print statements)
|
||
|
|
- `bracket_stiffness_extractor.py` (print statements)
|
||
|
|
- `export_displacement_field.py` (success messages)
|
||
|
|
|
||
|
|
### Fix Applied
|
||
|
|
Replace ALL unicode with ASCII equivalents:
|
||
|
|
- `≤` → `<=`
|
||
|
|
- `✓` → `[OK]`
|
||
|
|
- `✗` → `[X]`
|
||
|
|
- `⚠` → `[!]`
|
||
|
|
- `🎯` → `[BEST]`
|
||
|
|
- etc.
|
||
|
|
|
||
|
|
### Protocol Fix Required
|
||
|
|
**MANDATORY RULE**: Never use unicode symbols or emojis in any Python code that prints to console.
|
||
|
|
|
||
|
|
Create `atomizer/utils/safe_print.py`:
|
||
|
|
```python
|
||
|
|
"""Windows-safe printing utilities - ASCII only"""
|
||
|
|
|
||
|
|
def print_success(msg):
|
||
|
|
print(f"[OK] {msg}")
|
||
|
|
|
||
|
|
def print_error(msg):
|
||
|
|
print(f"[X] {msg}")
|
||
|
|
|
||
|
|
def print_warning(msg):
|
||
|
|
print(f"[!] {msg}")
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Issue #2: Hardcoded NX Version Instead of Using config.py
|
||
|
|
**Severity**: CRITICAL
|
||
|
|
**Category**: Configuration Management
|
||
|
|
**Protocol Violation**: Not using central configuration
|
||
|
|
|
||
|
|
### What Happened
|
||
|
|
Code hardcoded `nastran_version="2306"` but user has NX 2412 installed:
|
||
|
|
```
|
||
|
|
FileNotFoundError: Could not auto-detect NX 2306 installation
|
||
|
|
```
|
||
|
|
|
||
|
|
User explicitly asked: "isn't it in the protocole to use the actual config in config.py????"
|
||
|
|
|
||
|
|
### Root Cause
|
||
|
|
- Ignored `config.py` which has `NX_VERSION = "2412"`
|
||
|
|
- Hardcoded old version number
|
||
|
|
- Same issue in bracket_stiffness_extractor.py line 152
|
||
|
|
|
||
|
|
### Files Affected
|
||
|
|
- `run_optimization.py` line 85
|
||
|
|
- `bracket_stiffness_extractor.py` line 152
|
||
|
|
|
||
|
|
### Fix Applied
|
||
|
|
```python
|
||
|
|
import config as atomizer_config
|
||
|
|
|
||
|
|
nx_solver = NXSolver(
|
||
|
|
nastran_version=atomizer_config.NX_VERSION, # Use central config
|
||
|
|
timeout=atomizer_config.NASTRAN_TIMEOUT,
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Protocol Fix Required
|
||
|
|
**MANDATORY RULE**: ALWAYS import and use `config.py` for ALL system paths and versions.
|
||
|
|
|
||
|
|
Add validation check in all study templates:
|
||
|
|
```python
|
||
|
|
# Validate using central config
|
||
|
|
assert 'atomizer_config' in dir(), "Must import config as atomizer_config"
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Issue #3: Module Name Collision (config vs config parameter)
|
||
|
|
**Severity**: HIGH
|
||
|
|
**Category**: Code Quality
|
||
|
|
**Protocol Violation**: Poor naming conventions
|
||
|
|
|
||
|
|
### What Happened
|
||
|
|
```python
|
||
|
|
import config # Module named 'config'
|
||
|
|
|
||
|
|
def create_objective_function(config: dict, ...): # Parameter named 'config'
|
||
|
|
# Inside function:
|
||
|
|
nastran_version=config.NX_VERSION # ERROR: config is the dict, not the module!
|
||
|
|
```
|
||
|
|
|
||
|
|
Error: `AttributeError: 'dict' object has no attribute 'NX_VERSION'`
|
||
|
|
|
||
|
|
### Root Cause
|
||
|
|
Variable shadowing - parameter `config` shadows imported module `config`
|
||
|
|
|
||
|
|
### Fix Applied
|
||
|
|
```python
|
||
|
|
import config as atomizer_config # Unique name
|
||
|
|
|
||
|
|
def create_objective_function(config: dict, ...):
|
||
|
|
nastran_version=atomizer_config.NX_VERSION # Now unambiguous
|
||
|
|
```
|
||
|
|
|
||
|
|
### Protocol Fix Required
|
||
|
|
**MANDATORY RULE**: Always import config as `atomizer_config` to prevent collisions.
|
||
|
|
|
||
|
|
Update all templates and examples to use:
|
||
|
|
```python
|
||
|
|
import config as atomizer_config
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Issue #4: Protocol 10 Didn't Support Multi-Objective Optimization
|
||
|
|
**Severity**: CRITICAL
|
||
|
|
**Category**: Feature Gap
|
||
|
|
**Protocol Violation**: Protocol 10 documentation claims multi-objective support but doesn't implement it
|
||
|
|
|
||
|
|
### What Happened
|
||
|
|
Protocol 10 (`IntelligentOptimizer`) hardcoded `direction='minimize'` for single-objective only.
|
||
|
|
Multi-objective problems (like bracket: maximize stiffness, minimize mass) couldn't use Protocol 10.
|
||
|
|
|
||
|
|
### Root Cause
|
||
|
|
`IntelligentOptimizer.optimize()` didn't accept `directions` parameter
|
||
|
|
`_create_study()` always created single-objective studies
|
||
|
|
|
||
|
|
### Fix Applied
|
||
|
|
Enhanced `intelligent_optimizer.py`:
|
||
|
|
```python
|
||
|
|
def optimize(self, ..., directions: Optional[list] = None):
|
||
|
|
self.directions = directions
|
||
|
|
|
||
|
|
def _create_study(self):
|
||
|
|
if self.directions is not None:
|
||
|
|
# Multi-objective
|
||
|
|
study = optuna.create_study(directions=self.directions, ...)
|
||
|
|
else:
|
||
|
|
# Single-objective (backward compatible)
|
||
|
|
study = optuna.create_study(direction='minimize', ...)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Protocol Fix Required
|
||
|
|
**PROTOCOL 10 UPDATE**: Document and test multi-objective support.
|
||
|
|
|
||
|
|
Add to Protocol 10 documentation:
|
||
|
|
- Single-objective: `directions=None` or `directions=["minimize"]`
|
||
|
|
- Multi-objective: `directions=["minimize", "maximize", ...]`
|
||
|
|
- Update all examples to show both cases
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Issue #5: Wrong Solution Name Parameter to NX Solver
|
||
|
|
**Severity**: HIGH
|
||
|
|
**Category**: NX API Usage
|
||
|
|
**Protocol Violation**: Incorrect understanding of NX solution naming
|
||
|
|
|
||
|
|
### What Happened
|
||
|
|
Passed `solution_name="Bracket_sim1"` to NX solver, causing:
|
||
|
|
```
|
||
|
|
NXOpen.NXException: No object found with this name: Solution[Bracket_sim1]
|
||
|
|
```
|
||
|
|
|
||
|
|
All trials pruned because solver couldn't find solution.
|
||
|
|
|
||
|
|
### Root Cause
|
||
|
|
- NX solver looks for "Solution[<name>]" object
|
||
|
|
- Solution name should be "Solution 1", not the sim file name
|
||
|
|
- Passing `None` solves all solutions in .sim file (correct for most cases)
|
||
|
|
|
||
|
|
### Fix Applied
|
||
|
|
```python
|
||
|
|
result = nx_solver.run_simulation(
|
||
|
|
sim_file=sim_file,
|
||
|
|
solution_name=None # Solve all solutions
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Protocol Fix Required
|
||
|
|
**DOCUMENTATION**: Clarify `solution_name` parameter in NX solver docs.
|
||
|
|
|
||
|
|
Default should be `None` (solve all solutions). Only specify when you need to solve a specific solution from a multi-solution .sim file.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Issue #6: NX Journal Needs to Open Simulation File
|
||
|
|
**Severity**: HIGH
|
||
|
|
**Category**: NX Journal Design
|
||
|
|
**Protocol Violation**: Journal assumes file is already open
|
||
|
|
|
||
|
|
### What Happened
|
||
|
|
`export_displacement_field.py` expected a simulation to already be open:
|
||
|
|
```python
|
||
|
|
workSimPart = theSession.Parts.BaseWork
|
||
|
|
if workSimPart is None:
|
||
|
|
print("ERROR: No work part loaded")
|
||
|
|
return 1
|
||
|
|
```
|
||
|
|
|
||
|
|
When called via `run_journal.exe`, NX starts with no files open.
|
||
|
|
|
||
|
|
### Root Cause
|
||
|
|
Journal template didn't handle opening the sim file
|
||
|
|
|
||
|
|
### Fix Applied
|
||
|
|
Enhanced journal to open sim file:
|
||
|
|
```python
|
||
|
|
def main(args):
|
||
|
|
# Accept sim file path as argument
|
||
|
|
if len(args) > 0:
|
||
|
|
sim_file = Path(args[0])
|
||
|
|
else:
|
||
|
|
sim_file = Path(__file__).parent / "Bracket_sim1.sim"
|
||
|
|
|
||
|
|
# Open the simulation
|
||
|
|
basePart1, partLoadStatus1 = theSession.Parts.OpenBaseDisplay(str(sim_file))
|
||
|
|
partLoadStatus1.Dispose()
|
||
|
|
```
|
||
|
|
|
||
|
|
### Protocol Fix Required
|
||
|
|
**JOURNAL TEMPLATE**: All NX journals should handle opening required files.
|
||
|
|
|
||
|
|
Create standard journal template that:
|
||
|
|
1. Accepts file paths as arguments
|
||
|
|
2. Opens required files (part, sim, fem)
|
||
|
|
3. Performs operation
|
||
|
|
4. Closes gracefully
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Issue #7: Subprocess Check Fails on NX sys.exit(0)
|
||
|
|
**Severity**: MEDIUM
|
||
|
|
**Category**: NX Integration
|
||
|
|
**Protocol Violation**: Incorrect error handling for NX journals
|
||
|
|
|
||
|
|
### What Happened
|
||
|
|
```python
|
||
|
|
subprocess.run([nx_exe, journal], check=True) # Raises exception even on success!
|
||
|
|
```
|
||
|
|
|
||
|
|
NX's `run_journal.exe` returns non-zero exit code even when journal exits with `sys.exit(0)`.
|
||
|
|
The stderr shows:
|
||
|
|
```
|
||
|
|
SystemExit: 0 <-- Success!
|
||
|
|
```
|
||
|
|
|
||
|
|
But subprocess.run with `check=True` raises `CalledProcessError`.
|
||
|
|
|
||
|
|
### Root Cause
|
||
|
|
NX wraps Python journals and reports `sys.exit()` as a "Syntax error" in stderr, even for exit code 0.
|
||
|
|
|
||
|
|
### Fix Applied
|
||
|
|
Don't use `check=True`. Instead, verify output file was created:
|
||
|
|
```python
|
||
|
|
result = subprocess.run([nx_exe, journal], capture_output=True, text=True)
|
||
|
|
if not output_file.exists():
|
||
|
|
raise RuntimeError(f"Journal completed but output file not created")
|
||
|
|
```
|
||
|
|
|
||
|
|
### Protocol Fix Required
|
||
|
|
**NX SOLVER WRAPPER**: Never use `check=True` for NX journal execution.
|
||
|
|
|
||
|
|
Create `nx_utils.run_journal_safe()`:
|
||
|
|
```python
|
||
|
|
def run_journal_safe(journal_path, expected_outputs=[]):
|
||
|
|
"""Run NX journal and verify outputs, ignoring exit code"""
|
||
|
|
result = subprocess.run([NX_RUN_JOURNAL, journal_path],
|
||
|
|
capture_output=True, text=True)
|
||
|
|
|
||
|
|
for output_file in expected_outputs:
|
||
|
|
if not Path(output_file).exists():
|
||
|
|
raise RuntimeError(f"Journal failed: {output_file} not created")
|
||
|
|
|
||
|
|
return result
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Issue #8: OP2 File Naming Mismatch
|
||
|
|
**Severity**: HIGH
|
||
|
|
**Category**: File Path Management
|
||
|
|
**Protocol Violation**: Assumed file naming instead of detecting actual names
|
||
|
|
|
||
|
|
### What Happened
|
||
|
|
Extractor looked for `Bracket_sim1.op2` but NX created `bracket_sim1-solution_1.op2`:
|
||
|
|
```
|
||
|
|
ERROR: OP2 file not found: Bracket_sim1.op2
|
||
|
|
```
|
||
|
|
|
||
|
|
### Root Cause
|
||
|
|
- NX creates OP2 with lowercase sim base name
|
||
|
|
- NX adds `-solution_1` suffix
|
||
|
|
- Extractor hardcoded expected name without checking
|
||
|
|
|
||
|
|
### Fix Applied
|
||
|
|
```python
|
||
|
|
self.sim_base = Path(sim_file).stem
|
||
|
|
self.op2_file = self.model_dir / f"{self.sim_base.lower()}-solution_1.op2"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Protocol Fix Required
|
||
|
|
**FILE DETECTION**: Never hardcode output file names. Always detect or construct from input names.
|
||
|
|
|
||
|
|
Create `nx_utils.find_op2_file()`:
|
||
|
|
```python
|
||
|
|
def find_op2_file(sim_file: Path, working_dir: Path) -> Path:
|
||
|
|
"""Find OP2 file generated by NX simulation"""
|
||
|
|
sim_base = sim_file.stem.lower()
|
||
|
|
|
||
|
|
# Try common patterns
|
||
|
|
patterns = [
|
||
|
|
f"{sim_base}-solution_1.op2",
|
||
|
|
f"{sim_base}.op2",
|
||
|
|
f"{sim_base}-*.op2",
|
||
|
|
]
|
||
|
|
|
||
|
|
for pattern in patterns:
|
||
|
|
matches = list(working_dir.glob(pattern))
|
||
|
|
if matches:
|
||
|
|
return matches[0] # Return first match
|
||
|
|
|
||
|
|
raise FileNotFoundError(f"No OP2 file found for {sim_file}")
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Issue #9: Field Data Extractor Expects CSV, NX Exports Custom Format
|
||
|
|
**Severity**: CRITICAL
|
||
|
|
**Category**: Data Format Mismatch
|
||
|
|
**Protocol Violation**: Generic extractor not actually generic
|
||
|
|
|
||
|
|
### What Happened
|
||
|
|
```
|
||
|
|
ERROR: No valid data found in column 'z(mm)'
|
||
|
|
```
|
||
|
|
|
||
|
|
### Root Cause
|
||
|
|
NX field export format:
|
||
|
|
```
|
||
|
|
FIELD: [ResultProbe] : [TABLE]
|
||
|
|
INDEP VAR: [step] : [] : [] : [0]
|
||
|
|
INDEP VAR: [node_id] : [] : [] : [5]
|
||
|
|
DEP VAR: [x] : [Length] : [mm] : [0]
|
||
|
|
START DATA
|
||
|
|
0, 396, -0.086716040968895
|
||
|
|
0, 397, -0.087386816740036
|
||
|
|
...
|
||
|
|
END DATA
|
||
|
|
```
|
||
|
|
|
||
|
|
This is NOT a CSV with headers! But `FieldDataExtractor` uses:
|
||
|
|
```python
|
||
|
|
reader = csv.DictReader(f) # Expects CSV headers!
|
||
|
|
value = float(row[self.result_column]) # Looks for column 'z(mm)'
|
||
|
|
```
|
||
|
|
|
||
|
|
### Fix Required
|
||
|
|
`FieldDataExtractor` needs complete rewrite to handle NX field format:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def _parse_nx_field_file(self, file_path: Path) -> np.ndarray:
|
||
|
|
"""Parse NX field export format (.fld)"""
|
||
|
|
values = []
|
||
|
|
in_data_section = False
|
||
|
|
|
||
|
|
with open(file_path, 'r') as f:
|
||
|
|
for line in f:
|
||
|
|
if line.startswith('START DATA'):
|
||
|
|
in_data_section = True
|
||
|
|
continue
|
||
|
|
if line.startswith('END DATA'):
|
||
|
|
break
|
||
|
|
|
||
|
|
if in_data_section:
|
||
|
|
parts = line.strip().split(',')
|
||
|
|
if len(parts) >= 3:
|
||
|
|
try:
|
||
|
|
value = float(parts[2].strip()) # Third column is value
|
||
|
|
values.append(value)
|
||
|
|
except ValueError:
|
||
|
|
continue
|
||
|
|
|
||
|
|
return np.array(values)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Protocol Fix Required
|
||
|
|
**CRITICAL**: Fix `FieldDataExtractor` to actually parse NX field format.
|
||
|
|
|
||
|
|
The extractor claims to be "generic" and "reusable" but only works with CSV files, not NX field exports!
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Issue #10: Grid Point Forces Not Requested in OP2 Output
|
||
|
|
**Severity**: CRITICAL - BLOCKING ALL TRIALS
|
||
|
|
**Category**: NX Simulation Configuration
|
||
|
|
**Protocol Violation**: Missing output request validation
|
||
|
|
|
||
|
|
### What Happened
|
||
|
|
ALL trials (44-74+) are being pruned with the same error:
|
||
|
|
```
|
||
|
|
ERROR: Extraction failed: No grid point forces found in OP2 file
|
||
|
|
```
|
||
|
|
|
||
|
|
Simulation completes successfully:
|
||
|
|
- NX solver runs without errors
|
||
|
|
- OP2 file is generated and regenerated with fresh timestamps
|
||
|
|
- Displacement field is exported successfully
|
||
|
|
- Field data is parsed correctly
|
||
|
|
|
||
|
|
But stiffness calculation fails because applied force cannot be extracted from OP2.
|
||
|
|
|
||
|
|
### Root Cause
|
||
|
|
The NX simulation is not configured to output grid point forces to the OP2 file.
|
||
|
|
|
||
|
|
Nastran requires explicit output requests in the Case Control section. The bracket simulation likely only requests:
|
||
|
|
- Displacement results
|
||
|
|
- Stress results (maybe)
|
||
|
|
|
||
|
|
But does NOT request:
|
||
|
|
- Grid point forces (GPFORCE)
|
||
|
|
|
||
|
|
Without this output request, the OP2 file contains nodal displacements but not reaction forces at grid points.
|
||
|
|
|
||
|
|
### Evidence
|
||
|
|
From stiffness_calculator.py (optimization_engine/extractors/stiffness_calculator.py):
|
||
|
|
```python
|
||
|
|
# Extract applied force from OP2
|
||
|
|
force_results = self.op2_extractor.extract_force(component=self.force_component)
|
||
|
|
# Raises: ValueError("No grid point forces found in OP2 file")
|
||
|
|
```
|
||
|
|
|
||
|
|
The OP2Extractor tries to read `op2.grid_point_forces` which is empty because NX didn't request this output.
|
||
|
|
|
||
|
|
### Fix Required
|
||
|
|
**Option A: Modify NX Simulation Configuration (Recommended)**
|
||
|
|
|
||
|
|
Open `Bracket_sim1.sim` in NX and add grid point forces output request:
|
||
|
|
1. Edit Solution 1
|
||
|
|
2. Go to "Solution Control" or "Output Requests"
|
||
|
|
3. Add "Grid Point Forces" to output requests
|
||
|
|
4. Save simulation
|
||
|
|
|
||
|
|
This will add to the Nastran deck:
|
||
|
|
```
|
||
|
|
GPFORCE = ALL
|
||
|
|
```
|
||
|
|
|
||
|
|
**Option B: Extract Forces from Load Definition (Alternative)**
|
||
|
|
|
||
|
|
If the applied load is constant and defined in the model, extract it from the .sim file or model expressions instead of relying on OP2:
|
||
|
|
```python
|
||
|
|
# In bracket_stiffness_extractor.py
|
||
|
|
def _get_applied_force_from_model(self):
|
||
|
|
"""Extract applied force magnitude from model definition"""
|
||
|
|
# Load is 1000N in Z-direction based on model setup
|
||
|
|
return 1000.0 # N
|
||
|
|
```
|
||
|
|
|
||
|
|
This is less robust but works if the load is constant.
|
||
|
|
|
||
|
|
**Option C: Enhance OP2Extractor to Read from F06 File**
|
||
|
|
|
||
|
|
Nastran always writes grid point forces to the F06 text file. Add F06 parsing as fallback:
|
||
|
|
```python
|
||
|
|
def extract_force(self, component='fz'):
|
||
|
|
# Try OP2 first
|
||
|
|
if self.op2.grid_point_forces:
|
||
|
|
return self._extract_from_op2(component)
|
||
|
|
|
||
|
|
# Fallback to F06 file
|
||
|
|
f06_file = self.op2_file.with_suffix('.f06')
|
||
|
|
if f06_file.exists():
|
||
|
|
return self._extract_from_f06(f06_file, component)
|
||
|
|
|
||
|
|
raise ValueError("No grid point forces found in OP2 or F06 file")
|
||
|
|
```
|
||
|
|
|
||
|
|
### Protocol Fix Required
|
||
|
|
**MANDATORY VALIDATION**: Add pre-flight check for required output requests.
|
||
|
|
|
||
|
|
Create `nx_utils.validate_simulation_outputs()`:
|
||
|
|
```python
|
||
|
|
def validate_simulation_outputs(sim_file: Path, required_outputs: list):
|
||
|
|
"""
|
||
|
|
Validate that NX simulation has required output requests configured.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
sim_file: Path to .sim file
|
||
|
|
required_outputs: List of required outputs, e.g.,
|
||
|
|
['displacement', 'stress', 'grid_point_forces']
|
||
|
|
|
||
|
|
Raises:
|
||
|
|
ValueError: If required outputs are not configured
|
||
|
|
"""
|
||
|
|
# Parse .sim file or generated .dat file to check output requests
|
||
|
|
# Provide helpful error message with instructions to add missing outputs
|
||
|
|
pass
|
||
|
|
```
|
||
|
|
|
||
|
|
Call this validation BEFORE starting optimization:
|
||
|
|
```python
|
||
|
|
# In run_optimization.py, before optimizer.optimize()
|
||
|
|
validate_simulation_outputs(
|
||
|
|
sim_file=sim_file,
|
||
|
|
required_outputs=['displacement', 'grid_point_forces']
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Immediate Action
|
||
|
|
**For bracket study**: Open Bracket_sim1.sim in NX and add Grid Point Forces output request.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Summary of Protocol Fixes Needed
|
||
|
|
|
||
|
|
### HIGH PRIORITY (Blocking)
|
||
|
|
1. ✅ Fix `FieldDataExtractor` to parse NX field format
|
||
|
|
2. ✅ Create "no unicode" rule and safe_print utilities
|
||
|
|
3. ✅ Enforce config.py usage in all templates
|
||
|
|
4. ✅ Update Protocol 10 for multi-objective support
|
||
|
|
5. ❌ **CURRENT BLOCKER**: Fix grid point forces extraction (Issue #10)
|
||
|
|
|
||
|
|
### MEDIUM PRIORITY (Quality)
|
||
|
|
5. ✅ Create NX journal template with file opening
|
||
|
|
6. ✅ Create nx_utils.run_journal_safe() wrapper
|
||
|
|
7. ✅ Create nx_utils.find_op2_file() detection
|
||
|
|
8. ✅ Add naming convention (import config as atomizer_config)
|
||
|
|
|
||
|
|
### DOCUMENTATION
|
||
|
|
9. ✅ Document solution_name parameter behavior
|
||
|
|
10. ✅ Update Protocol 10 docs with multi-objective examples
|
||
|
|
11. ✅ Create "Windows Compatibility Guide"
|
||
|
|
12. ✅ Add field file format documentation
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Lessons Learned
|
||
|
|
|
||
|
|
### What Went Wrong
|
||
|
|
1. **Generic tools weren't actually generic** - FieldDataExtractor only worked for CSV
|
||
|
|
2. **No validation of central config usage** - Easy to forget to import
|
||
|
|
3. **Unicode symbols slip in during development** - Need linter check
|
||
|
|
4. **Subprocess error handling assumed standard behavior** - NX is non-standard
|
||
|
|
5. **File naming assumptions instead of detection** - Brittle
|
||
|
|
6. **Protocol 10 feature gap** - Claims multi-objective but didn't implement it
|
||
|
|
7. **Journal templates incomplete** - Didn't handle file opening
|
||
|
|
|
||
|
|
### What Should Have Been Caught
|
||
|
|
- Pre-flight validation script should check:
|
||
|
|
- ✅ No unicode in any .py files
|
||
|
|
- ✅ All studies import config.py
|
||
|
|
- ✅ All output files use detected names, not hardcoded
|
||
|
|
- ✅ All journals can run standalone (no assumptions about open files)
|
||
|
|
|
||
|
|
### Time Lost
|
||
|
|
- Approximately 60+ minutes debugging issues that should have been prevented
|
||
|
|
- Would have been 5 minutes to run successfully with proper templates
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Action Items
|
||
|
|
|
||
|
|
1. [ ] Rewrite FieldDataExtractor to handle NX format
|
||
|
|
2. [ ] Create pre-flight validation script
|
||
|
|
3. [ ] Update all study templates
|
||
|
|
4. [ ] Add linter rules for unicode detection
|
||
|
|
5. [ ] Create nx_utils module with safe wrappers
|
||
|
|
6. [ ] Update Protocol 10 documentation
|
||
|
|
7. [ ] Create Windows compatibility guide
|
||
|
|
8. [ ] Add integration tests for NX file formats
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Next Step**: Fix FieldDataExtractor and test complete workflow end-to-end.
|