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>
345 lines
10 KiB
Markdown
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
|