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>
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 toC:\Program Files\Siemens\NX2506\NXBIN) - Python: 3.8+ with
atomizerconda environment
Quick Start
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:
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:
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:
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:
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:
{
"success": bool, # True if operation succeeded
"error": str | None, # Error message if failed
"data": dict # Operation-specific results
}
Error Handling:
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:
# 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:
# 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:
# 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_PATHenvironment 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 - Development roadmap
- SYS_12_EXTRACTOR_LIBRARY.md - Extractor catalog
- NXJournaling.com - NX Open examples