Files
Atomizer/optimization_engine/hooks/README.md
Antoine 602560c46a feat: Add MLP surrogate with Turbo Mode for 100x faster optimization
Neural Acceleration (MLP Surrogate):
- Add run_nn_optimization.py with hybrid FEA/NN workflow
- MLP architecture: 4-layer (64->128->128->64) with BatchNorm/Dropout
- Three workflow modes:
  - --all: Sequential export->train->optimize->validate
  - --hybrid-loop: Iterative Train->NN->Validate->Retrain cycle
  - --turbo: Aggressive single-best validation (RECOMMENDED)
- Turbo mode: 5000 NN trials + 50 FEA validations in ~12 minutes
- Separate nn_study.db to avoid overloading dashboard

Performance Results (bracket_pareto_3obj study):
- NN prediction errors: mass 1-5%, stress 1-4%, stiffness 5-15%
- Found minimum mass designs at boundary (angle~30deg, thick~30mm)
- 100x speedup vs pure FEA exploration

Protocol Operating System:
- Add .claude/skills/ with Bootstrap, Cheatsheet, Context Loader
- Add docs/protocols/ with operations (OP_01-06) and system (SYS_10-14)
- Update SYS_14_NEURAL_ACCELERATION.md with MLP Turbo Mode docs

NX Automation:
- Add optimization_engine/hooks/ for NX CAD/CAE automation
- Add study_wizard.py for guided study creation
- Fix FEM mesh update: load idealized part before UpdateFemodel()

New Study:
- bracket_pareto_3obj: 3-objective Pareto (mass, stress, stiffness)
- 167 FEA trials + 5000 NN trials completed
- Demonstrates full hybrid workflow

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 20:01:59 -05:00

345 lines
10 KiB
Markdown

# Atomizer NX Open Hooks
Direct Python hooks for NX CAD/CAE operations via NX Open API.
## Overview
This module provides a clean Python API for manipulating NX parts programmatically. Each hook executes NX journals via `run_journal.exe` and returns structured JSON results.
## Architecture
```
hooks/
├── __init__.py # Main entry point
├── README.md # This file
├── test_hooks.py # Test script
└── nx_cad/ # CAD manipulation hooks
├── __init__.py
├── part_manager.py # Open/Close/Save parts
├── expression_manager.py # Get/Set expressions
├── geometry_query.py # Mass properties, bodies
└── feature_manager.py # Suppress/Unsuppress features
```
## Requirements
- **NX Installation**: Siemens NX 2506 or compatible version
- **Environment Variable**: `NX_BIN_PATH` (defaults to `C:\Program Files\Siemens\NX2506\NXBIN`)
- **Python**: 3.8+ with `atomizer` conda environment
## Quick Start
```python
from optimization_engine.hooks.nx_cad import (
part_manager,
expression_manager,
geometry_query,
feature_manager,
)
# Path to your NX part
part_path = "C:/path/to/model.prt"
# Get all expressions
result = expression_manager.get_expressions(part_path)
if result["success"]:
for name, expr in result["data"]["expressions"].items():
print(f"{name} = {expr['value']} {expr['units']}")
# Get mass properties
result = geometry_query.get_mass_properties(part_path)
if result["success"]:
print(f"Mass: {result['data']['mass']:.4f} kg")
print(f"Material: {result['data']['material']}")
```
## Module Reference
### part_manager
Manage NX part files (open, close, save).
| Function | Description | Returns |
|----------|-------------|---------|
| `open_part(path)` | Open an NX part file | Part info dict |
| `close_part(path)` | Close an open part | Success status |
| `save_part(path)` | Save a part | Success status |
| `save_part_as(path, new_path)` | Save with new name | Success status |
| `get_part_info(path)` | Get part metadata | Part info dict |
**Example:**
```python
from optimization_engine.hooks.nx_cad import part_manager
# Open a part
result = part_manager.open_part("C:/models/bracket.prt")
if result["success"]:
print(f"Opened: {result['data']['part_name']}")
print(f"Modified: {result['data']['is_modified']}")
# Save the part
result = part_manager.save_part("C:/models/bracket.prt")
# Save as new file
result = part_manager.save_part_as(
"C:/models/bracket.prt",
"C:/models/bracket_v2.prt"
)
```
### expression_manager
Get and set NX expressions (design parameters).
| Function | Description | Returns |
|----------|-------------|---------|
| `get_expressions(path)` | Get all expressions | Dict of expressions |
| `get_expression(path, name)` | Get single expression | Expression dict |
| `set_expression(path, name, value)` | Set single expression | Success status |
| `set_expressions(path, dict)` | Set multiple expressions | Success status |
**Example:**
```python
from optimization_engine.hooks.nx_cad import expression_manager
part = "C:/models/bracket.prt"
# Get all expressions
result = expression_manager.get_expressions(part)
if result["success"]:
for name, expr in result["data"]["expressions"].items():
print(f"{name} = {expr['value']} {expr['units']}")
# Example output:
# thickness = 5.0 MilliMeter
# width = 50.0 MilliMeter
# Get specific expression
result = expression_manager.get_expression(part, "thickness")
if result["success"]:
print(f"Thickness: {result['data']['value']} {result['data']['units']}")
# Set single expression
result = expression_manager.set_expression(part, "thickness", 7.5)
# Set multiple expressions (batch update)
result = expression_manager.set_expressions(part, {
"thickness": 7.5,
"width": 60.0,
"height": 100.0
})
if result["success"]:
print(f"Updated {result['data']['update_count']} expressions")
```
### geometry_query
Query geometric properties (mass, volume, bodies).
| Function | Description | Returns |
|----------|-------------|---------|
| `get_mass_properties(path)` | Get mass, volume, area, centroid | Properties dict |
| `get_bodies(path)` | Get body count and types | Bodies dict |
| `get_volume(path)` | Get total volume | Volume float |
| `get_surface_area(path)` | Get total surface area | Area float |
| `get_material(path)` | Get material name | Material string |
**Example:**
```python
from optimization_engine.hooks.nx_cad import geometry_query
part = "C:/models/bracket.prt"
# Get mass properties
result = geometry_query.get_mass_properties(part)
if result["success"]:
data = result["data"]
print(f"Mass: {data['mass']:.6f} {data['mass_unit']}")
print(f"Volume: {data['volume']:.2f} {data['volume_unit']}")
print(f"Surface Area: {data['surface_area']:.2f} {data['area_unit']}")
print(f"Centroid: ({data['centroid']['x']:.2f}, "
f"{data['centroid']['y']:.2f}, {data['centroid']['z']:.2f}) mm")
print(f"Material: {data['material']}")
# Example output:
# Mass: 0.109838 kg
# Volume: 39311.99 mm^3
# Surface Area: 10876.71 mm^2
# Centroid: (0.00, 42.30, 39.58) mm
# Material: Aluminum_2014
# Get body information
result = geometry_query.get_bodies(part)
if result["success"]:
print(f"Total bodies: {result['data']['count']}")
print(f"Solid bodies: {result['data']['solid_count']}")
```
### feature_manager
Suppress and unsuppress features for design exploration.
| Function | Description | Returns |
|----------|-------------|---------|
| `get_features(path)` | List all features | Features list |
| `get_feature_status(path, name)` | Check if suppressed | Boolean |
| `suppress_feature(path, name)` | Suppress a feature | Success status |
| `unsuppress_feature(path, name)` | Unsuppress a feature | Success status |
| `suppress_features(path, names)` | Suppress multiple | Success status |
| `unsuppress_features(path, names)` | Unsuppress multiple | Success status |
**Example:**
```python
from optimization_engine.hooks.nx_cad import feature_manager
part = "C:/models/bracket.prt"
# List all features
result = feature_manager.get_features(part)
if result["success"]:
print(f"Found {result['data']['count']} features")
for feat in result["data"]["features"]:
status = "suppressed" if feat["is_suppressed"] else "active"
print(f" {feat['name']} ({feat['type']}): {status}")
# Suppress a feature
result = feature_manager.suppress_feature(part, "FILLET(3)")
if result["success"]:
print("Feature suppressed!")
# Unsuppress multiple features
result = feature_manager.unsuppress_features(part, ["FILLET(3)", "CHAMFER(1)"])
```
## Return Format
All hook functions return a consistent dictionary structure:
```python
{
"success": bool, # True if operation succeeded
"error": str | None, # Error message if failed
"data": dict # Operation-specific results
}
```
**Error Handling:**
```python
result = expression_manager.get_expressions(part_path)
if not result["success"]:
print(f"Error: {result['error']}")
# Handle error...
else:
# Process result["data"]...
```
## NX Open API Reference
These hooks use the following NX Open APIs (verified via Siemens MCP documentation):
| Hook | NX Open API |
|------|-------------|
| Open part | `Session.Parts.OpenActiveDisplay()` |
| Close part | `Part.Close()` |
| Save part | `Part.Save()`, `Part.SaveAs()` |
| Get expressions | `Part.Expressions` collection |
| Set expression | `ExpressionCollection.Edit()` |
| Update model | `Session.UpdateManager.DoUpdate()` |
| Mass properties | `MeasureManager.NewMassProperties()` |
| Get bodies | `Part.Bodies` collection |
| Suppress feature | `Feature.Suppress()` |
| Unsuppress feature | `Feature.Unsuppress()` |
## Configuration
### NX Path
Set the NX installation path via environment variable:
```bash
# Windows
set NX_BIN_PATH=C:\Program Files\Siemens\NX2506\NXBIN
# Or in Python before importing
import os
os.environ["NX_BIN_PATH"] = r"C:\Program Files\Siemens\NX2506\NXBIN"
```
### Timeout
Journal execution has a default 2-minute timeout. For large parts, you may need to increase this in the hook source code.
## Integration with Atomizer
These hooks are designed to integrate with Atomizer's optimization workflow:
```python
# In run_optimization.py or custom extractor
from optimization_engine.hooks.nx_cad import expression_manager, geometry_query
def evaluate_design(part_path: str, params: dict) -> dict:
"""Evaluate a design point by updating NX model and extracting metrics."""
# 1. Update design parameters
result = expression_manager.set_expressions(part_path, params)
if not result["success"]:
raise RuntimeError(f"Failed to set expressions: {result['error']}")
# 2. Extract mass (objective)
result = geometry_query.get_mass_properties(part_path)
if not result["success"]:
raise RuntimeError(f"Failed to get mass: {result['error']}")
return {
"mass_kg": result["data"]["mass"],
"volume_mm3": result["data"]["volume"],
"material": result["data"]["material"]
}
```
## Testing
Run the test script to verify hooks work with your NX installation:
```bash
# Activate atomizer environment
conda activate atomizer
# Run tests with default bracket part
python -m optimization_engine.hooks.test_hooks
# Or specify a custom part
python -m optimization_engine.hooks.test_hooks "C:/path/to/your/part.prt"
```
## Troubleshooting
### "Part file not found"
- Verify the path exists and is accessible
- Use forward slashes or raw strings: `r"C:\path\to\file.prt"`
### "Failed to open part"
- Ensure NX license is available
- Check `NX_BIN_PATH` environment variable
- Verify NX version compatibility
### "Expression not found"
- Expression names are case-sensitive
- Use `get_expressions()` to list available names
### Journal execution timeout
- Large parts may need longer timeout
- Check NX is not displaying modal dialogs
## Version History
| Version | Date | Changes |
|---------|------|---------|
| 1.0.0 | 2025-12-06 | Initial release with CAD hooks |
## See Also
- [NX_OPEN_AUTOMATION_ROADMAP.md](../../docs/plans/NX_OPEN_AUTOMATION_ROADMAP.md) - Development roadmap
- [SYS_12_EXTRACTOR_LIBRARY.md](../../docs/protocols/system/SYS_12_EXTRACTOR_LIBRARY.md) - Extractor catalog
- [NXJournaling.com](https://nxjournaling.com/) - NX Open examples