feat: Major update with validators, skills, dashboard, and docs reorganization

- Add validation framework (config, model, results, study validators)
- Add Claude Code skills (create-study, run-optimization, generate-report,
  troubleshoot, analyze-model)
- Add Atomizer Dashboard (React frontend + FastAPI backend)
- Reorganize docs into structured directories (00-09)
- Add neural surrogate modules and training infrastructure
- Add multi-objective optimization support

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-25 19:23:58 -05:00
parent 74a92803b7
commit e3bdb08a22
155 changed files with 52729 additions and 37 deletions

View File

@@ -0,0 +1,449 @@
# GNN Architecture Deep Dive
**Technical documentation for AtomizerField Graph Neural Networks**
---
## Overview
AtomizerField uses Graph Neural Networks (GNNs) to learn physics from FEA simulations. This document explains the architecture in detail.
---
## Why Graph Neural Networks?
FEA meshes are naturally graphs:
- **Nodes** = Grid points (GRID cards in Nastran)
- **Edges** = Element connectivity (CTETRA, CQUAD, etc.)
- **Node features** = Position, BCs, material properties
- **Edge features** = Element type, length, direction
Traditional neural networks (MLPs, CNNs) can't handle this irregular structure. GNNs can.
```
FEA Mesh Graph
═══════════════════════════════════════════════
o───o (N1)──(N2)
/│ │\ │╲ │╱
o─┼───┼─o → (N3)──(N4)
\│ │/ │╱ │╲
o───o (N5)──(N6)
```
---
## Model Architectures
### 1. Field Predictor GNN
Predicts complete displacement and stress fields.
```
┌─────────────────────────────────────────────────────────┐
│ Field Predictor GNN │
├─────────────────────────────────────────────────────────┤
│ │
│ Input Encoding │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Node Features (12D per node): │ │
│ │ • Position (x, y, z) [3D] │ │
│ │ • Material (E, nu, rho) [3D] │ │
│ │ • Boundary conditions (fixed per DOF) [6D] │ │
│ │ │ │
│ │ Edge Features (5D per edge): │ │
│ │ • Edge length [1D] │ │
│ │ • Direction vector [3D] │ │
│ │ • Element type [1D] │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ Message Passing Layers (6 layers) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ for layer in range(6): │ │
│ │ h = MeshGraphConv(h, edge_index, edge_attr) │ │
│ │ h = LayerNorm(h) │ │
│ │ h = ReLU(h) │ │
│ │ h = Dropout(h, p=0.1) │ │
│ │ h = h + residual # Skip connection │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ Output Heads │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Displacement Head: │ │
│ │ Linear(hidden → 64 → 6) # 6 DOF per node │ │
│ │ │ │
│ │ Stress Head: │ │
│ │ Linear(hidden → 64 → 1) # Von Mises stress │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ Output: [N_nodes, 7] (6 displacement + 1 stress) │
└─────────────────────────────────────────────────────────┘
```
**Parameters**: 718,221 trainable
### 2. Parametric Field Predictor GNN
Predicts scalar objectives directly from design parameters.
```
┌─────────────────────────────────────────────────────────┐
│ Parametric Field Predictor GNN │
├─────────────────────────────────────────────────────────┤
│ │
│ Design Parameter Encoding │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Design Params (4D): │ │
│ │ • beam_half_core_thickness │ │
│ │ • beam_face_thickness │ │
│ │ • holes_diameter │ │
│ │ • hole_count │ │
│ │ │ │
│ │ Design Encoder MLP: │ │
│ │ Linear(4 → 64) → ReLU → Linear(64 → 128) │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ Design-Conditioned GNN │
│ ┌──────────────────────────────────────────────────┐ │
│ │ # Broadcast design encoding to all nodes │ │
│ │ node_features = node_features + design_encoding │ │
│ │ │ │
│ │ for layer in range(4): │ │
│ │ h = GraphConv(h, edge_index) │ │
│ │ h = BatchNorm(h) │ │
│ │ h = ReLU(h) │ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ Global Pooling │
│ ┌──────────────────────────────────────────────────┐ │
│ │ mean_pool = global_mean_pool(h) # [batch, 128] │ │
│ │ max_pool = global_max_pool(h) # [batch, 128] │ │
│ │ design = design_encoding # [batch, 128] │ │
│ │ │ │
│ │ global_features = concat([mean_pool, max_pool, │ │
│ │ design]) # [batch, 384]│ │
│ └──────────────────────────────────────────────────┘ │
│ ↓ │
│ Scalar Prediction Heads │
│ ┌──────────────────────────────────────────────────┐ │
│ │ MLP: Linear(384 → 128 → 64 → 4) │ │
│ │ │ │
│ │ Output: │ │
│ │ [0] = mass (grams) │ │
│ │ [1] = frequency (Hz) │ │
│ │ [2] = max_displacement (mm) │ │
│ │ [3] = max_stress (MPa) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ Output: [batch, 4] (4 objectives) │
└─────────────────────────────────────────────────────────┘
```
**Parameters**: ~500,000 trainable
---
## Message Passing
The core of GNNs is message passing. Here's how it works:
### Standard Message Passing
```python
def message_passing(node_features, edge_index, edge_attr):
"""
node_features: [N_nodes, D_node]
edge_index: [2, N_edges] # Source → Target
edge_attr: [N_edges, D_edge]
"""
# Step 1: Compute messages
source_nodes = node_features[edge_index[0]] # [N_edges, D_node]
target_nodes = node_features[edge_index[1]] # [N_edges, D_node]
messages = MLP([source_nodes, target_nodes, edge_attr]) # [N_edges, D_msg]
# Step 2: Aggregate messages at each node
aggregated = scatter_add(messages, edge_index[1]) # [N_nodes, D_msg]
# Step 3: Update node features
updated = MLP([node_features, aggregated]) # [N_nodes, D_node]
return updated
```
### Custom MeshGraphConv
We use a custom convolution that respects FEA mesh structure:
```python
class MeshGraphConv(MessagePassing):
"""
Custom message passing for FEA meshes.
Accounts for:
- Edge lengths (stiffness depends on distance)
- Element types (different physics for solid/shell/beam)
- Direction vectors (anisotropic behavior)
"""
def message(self, x_i, x_j, edge_attr):
# x_i: Target node features
# x_j: Source node features
# edge_attr: Edge features (length, direction, type)
# Compute message
edge_length = edge_attr[:, 0:1]
edge_direction = edge_attr[:, 1:4]
element_type = edge_attr[:, 4:5]
# Scale by inverse distance (like stiffness)
distance_weight = 1.0 / (edge_length + 1e-6)
# Combine source and target features
combined = torch.cat([x_i, x_j, edge_attr], dim=-1)
message = self.mlp(combined) * distance_weight
return message
def aggregate(self, messages, index):
# Sum messages at each node (like force equilibrium)
return scatter_add(messages, index, dim=0)
```
---
## Feature Engineering
### Node Features (12D)
| Feature | Dimensions | Range | Description |
|---------|------------|-------|-------------|
| Position (x, y, z) | 3 | Normalized | Node coordinates |
| Material E | 1 | Log-scaled | Young's modulus |
| Material nu | 1 | [0, 0.5] | Poisson's ratio |
| Material rho | 1 | Log-scaled | Density |
| BC_x, BC_y, BC_z | 3 | {0, 1} | Fixed translation |
| BC_rx, BC_ry, BC_rz | 3 | {0, 1} | Fixed rotation |
### Edge Features (5D)
| Feature | Dimensions | Range | Description |
|---------|------------|-------|-------------|
| Length | 1 | Normalized | Edge length |
| Direction | 3 | [-1, 1] | Unit direction vector |
| Element type | 1 | Encoded | CTETRA=0, CHEXA=1, etc. |
### Normalization
```python
def normalize_features(node_features, edge_features, stats):
"""Normalize to zero mean, unit variance"""
# Node features
node_features = (node_features - stats['node_mean']) / stats['node_std']
# Edge features (length uses log normalization)
edge_features[:, 0] = torch.log(edge_features[:, 0] + 1e-6)
edge_features = (edge_features - stats['edge_mean']) / stats['edge_std']
return node_features, edge_features
```
---
## Training Details
### Optimizer
```python
optimizer = AdamW(
model.parameters(),
lr=1e-3,
weight_decay=1e-4,
betas=(0.9, 0.999)
)
```
### Learning Rate Schedule
```python
scheduler = CosineAnnealingWarmRestarts(
optimizer,
T_0=50, # Restart every 50 epochs
T_mult=2, # Double period after each restart
eta_min=1e-6
)
```
### Data Augmentation
```python
def augment_graph(data):
"""Random augmentation for better generalization"""
# Random rotation (physics is rotation-invariant)
if random.random() < 0.5:
angle = random.uniform(0, 2 * math.pi)
data = rotate_graph(data, angle, axis='z')
# Random noise (robustness)
if random.random() < 0.3:
data.x += torch.randn_like(data.x) * 0.01
return data
```
### Batch Processing
```python
from torch_geometric.data import DataLoader
loader = DataLoader(
dataset,
batch_size=32,
shuffle=True,
num_workers=4
)
for batch in loader:
# batch.x: [total_nodes, D_node]
# batch.edge_index: [2, total_edges]
# batch.batch: [total_nodes] - maps nodes to graphs
predictions = model(batch)
```
---
## Model Comparison
| Model | Parameters | Inference | Output | Use Case |
|-------|------------|-----------|--------|----------|
| Field Predictor | 718K | 50ms | Full field | When you need field visualization |
| Parametric | 500K | 4.5ms | 4 scalars | Direct optimization (fastest) |
| Ensemble (5x) | 2.5M | 25ms | 4 scalars + uncertainty | When confidence matters |
---
## Implementation Notes
### PyTorch Geometric
We use [PyTorch Geometric](https://pytorch-geometric.readthedocs.io/) for GNN operations:
```python
import torch_geometric
from torch_geometric.nn import MessagePassing, global_mean_pool
# Version requirements
# torch >= 2.0
# torch_geometric >= 2.3
```
### GPU Memory
| Model | Batch Size | GPU Memory |
|-------|------------|------------|
| Field Predictor | 16 | 4 GB |
| Parametric | 32 | 2 GB |
| Training | 16 | 8 GB |
### Checkpoints
```python
# Save checkpoint
torch.save({
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'config': model_config,
'normalization_stats': stats,
'epoch': epoch,
'best_val_loss': best_loss
}, 'checkpoint.pt')
# Load checkpoint
checkpoint = torch.load('checkpoint.pt')
model = ParametricFieldPredictor(**checkpoint['config'])
model.load_state_dict(checkpoint['model_state_dict'])
```
---
## Physics Interpretation
### Why GNNs Work for FEA
1. **Locality**: FEA solutions are local (nodes only affect neighbors)
2. **Superposition**: Linear FEA is additive (sum of effects)
3. **Equilibrium**: Force balance at each node (sum of messages = 0)
The GNN learns these principles:
- Message passing ≈ Force distribution through elements
- Aggregation ≈ Force equilibrium at nodes
- Multiple layers ≈ Load path propagation
### Physical Constraints
The architecture enforces physics:
```python
# Displacement at fixed nodes = 0
displacement = model(data)
fixed_mask = data.boundary_conditions > 0
displacement[fixed_mask] = 0.0 # Hard constraint
# Stress-strain relationship (implicit)
# Learned by the network through training
```
---
## Extension Points
### Adding New Element Types
```python
# In data_loader.py
ELEMENT_TYPES = {
'CTETRA': 0,
'CHEXA': 1,
'CPENTA': 2,
'CQUAD4': 3,
'CTRIA3': 4,
'CBAR': 5,
'CBEAM': 6,
# Add new types here
'CTETRA10': 7, # New 10-node tetrahedron
}
```
### Custom Output Heads
```python
class CustomFieldPredictor(FieldPredictorGNN):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Add custom head for thermal analysis
self.temperature_head = nn.Linear(hidden_channels, 1)
def forward(self, data):
h = super().forward(data)
# Add temperature prediction
temperature = self.temperature_head(h)
return torch.cat([h, temperature], dim=-1)
```
---
## References
1. Battaglia et al. (2018) "Relational inductive biases, deep learning, and graph networks"
2. Pfaff et al. (2021) "Learning Mesh-Based Simulation with Graph Networks"
3. Sanchez-Gonzalez et al. (2020) "Learning to Simulate Complex Physics with Graph Networks"
---
## See Also
- [Neural Features Complete](NEURAL_FEATURES_COMPLETE.md) - Overview of all features
- [Physics Loss Guide](PHYSICS_LOSS_GUIDE.md) - Loss function selection
- [Neural Workflow Tutorial](NEURAL_WORKFLOW_TUTORIAL.md) - Step-by-step guide

View File

@@ -0,0 +1,306 @@
# NXOpen Python Intellisense Setup
> **Status**: ✅ Implemented (2025-11-17)
>
> Enable intelligent code completion for NXOpen Python API using Siemens-provided stub files
---
## Overview
Siemens NX 2412 includes Python stub files (`.pyi`) that provide full type hints for the NXOpen API. These enable:
- **Autocomplete**: Suggestions for classes, methods, and properties
- **Type Hints**: Parameter types and return values
- **Documentation**: Inline docstrings and API descriptions
- **Error Detection**: Type checking catches errors before runtime
This dramatically improves development speed and reduces NXOpen API lookup time.
---
## Prerequisites
- **Siemens NX 2412** (or later) installed with Programming Tools
- **VSCode** with **Pylance extension** (usually installed with Python extension)
- **Python 3.11** environment (required for NXOpen module compatibility)
---
## Setup Instructions
### Step 0: Ensure Python 3.11 Environment
NXOpen modules are compiled for Python 3.11. **You must use Python 3.11**:
```bash
# Check your Python version
python --version # Should show: Python 3.11.x
# If using conda, upgrade atomizer environment:
conda install -n atomizer python=3.11 -y
```
### Step 1: Add NXOpen to Python Path
Create a `.pth` file in your Python environment's site-packages to enable NXOpen imports:
```bash
# For atomizer environment:
# Create file: C:\Users\<username>\anaconda3\envs\atomizer\Lib\site-packages\nxopen.pth
# Contents:
C:\Program Files\Siemens\NX2412\NXBIN\python
```
This allows `import NXOpen` to work in your Python scripts!
### Step 2: Verify Stub Files Exist
Check that stub files are installed:
```bash
# Windows path:
dir "C:\Program Files\Siemens\NX2412\UGOPEN\pythonStubs\NXOpen"
# Should show: __init__.pyi and many module folders (CAE, Assemblies, etc.)
```
**If missing**: Reinstall NX 2412 and ensure "Programming Tools" is checked during installation.
### Step 3: Configure VSCode
Update `.vscode/settings.json` in your Atomizer project:
```json
{
"python.analysis.typeCheckingMode": "basic",
"python.analysis.stubPath": "C:\\Program Files\\Siemens\\NX2412\\UGOPEN\\pythonStubs"
}
```
**Note**: Use double backslashes (`\\`) in Windows paths for JSON.
### Step 4: Restart VSCode
Close and reopen VSCode to load the new stub files.
### Step 5: Verify NXOpen Import and Intellisense Works
First, test that NXOpen can be imported:
```python
python
>>> import NXOpen
>>> print("Success! NXOpen is available")
>>> exit()
```
Then test intellisense:
Open `tests/test_nxopen_intellisense.py` and verify:
1. **Import Autocomplete**:
- Type `import NXOpen.` → Should suggest: Part, Session, CAE, Assemblies, etc.
2. **Method Autocomplete**:
- Type `session.` → Should suggest: GetSession(), Parts, etc.
- Type `expressions.` → Should suggest: FindObject, CreateExpression, etc.
3. **Parameter Hints**:
- Hover over `CreateExpression()` → Shows parameter types and documentation
4. **Documentation Tooltips**:
- Hover over any NXOpen class/method → Shows docstring
**If working**: ✅ Intellisense is configured correctly!
---
## What You Get
### Before Intellisense:
```python
import NXOpen
session = NXOpen.Session.GetSession()
# ❌ No suggestions when typing "session."
# ❌ No parameter hints for methods
# ❌ Must look up API in documentation
```
### After Intellisense:
```python
import NXOpen
session = NXOpen.Session.GetSession()
# ✅ Type "session." → See: Parts, ListingWindow, LogFile, etc.
# ✅ Type "session.Parts." → See: Work, Display, FindObject, etc.
# ✅ Hover over methods → See parameter types and documentation
# ✅ Catch type errors before running code
```
---
## Available Modules
The stub files cover **all** NXOpen modules:
**Core Modules**:
- `NXOpen.Session` - NX session management
- `NXOpen.Part` - Part objects and operations
- `NXOpen.Assemblies` - Assembly operations
**CAE Modules**:
- `NXOpen.CAE` - Finite element analysis
- `NXOpen.CAE.FemPart` - FEM models
- `NXOpen.CAE.SimSolution` - Solutions and solver control
**Design Modules**:
- `NXOpen.Features` - Parametric features
- `NXOpen.Sketches` - Sketch operations
- `NXOpen.Modeling` - Modeling operations
**And many more**: Drafting, Display, Motion, Optimization, etc.
---
## Example: Using Intellisense During Development
### Scenario: Update Part Expression
**Without Intellisense** (manual lookup required):
```python
# 1. Google: "NXOpen get expression"
# 2. Find documentation
# 3. Copy method signature
# 4. Hope you got it right
work_part.Expressions.FindObject("tip_thickness")
```
**With Intellisense** (guided development):
```python
# 1. Type "work_part.Exp" → Autocomplete suggests "Expressions"
# 2. Type "work_part.Expressions." → See all methods
# 3. Select "FindObject" → See parameter types
# 4. Hover for documentation
work_part.Expressions.FindObject("tip_thickness") # ✅ Correct!
```
---
## Benefits for Atomizer Development
### 1. **Faster Development**
- No context switching to documentation
- Discover APIs as you type
- Reduce typos and API misuse
### 2. **Better Code Quality**
- Type checking catches errors early
- Method signatures documented inline
- Parameter validation before runtime
### 3. **LLM-Assisted Coding**
When using Claude Code to develop Atomizer:
- Claude can "see" NXOpen API structure via stub files
- Better code generation suggestions
- Reduced hallucination of API methods
### 4. **Onboarding**
- New contributors learn NXOpen API faster
- Inline documentation reduces learning curve
- Explore API without leaving IDE
---
## Integration with Atomizer Workflow
### Journal Script Development
When writing NX journal scripts (`optimization_engine/solve_simulation.py`):
```python
import NXOpen
theSession = NXOpen.Session.GetSession()
workPart = theSession.Parts.Work
# Intellisense shows:
# - workPart.Expressions.FindObject(...)
# - workPart.Expressions.EditWithUnits(...)
# - workPart.Update()
# - workPart.Save(...)
```
### LLM Code Generation
When LLM generates NXOpen code, stub files help:
- Validate generated code against actual API
- Suggest corrections for API misuse
- Provide parameter type hints
### Future: NXOpen Documentation Integration
This is **Step 1** of NXOpen integration. Future work:
1.**Stub files for intellisense** (current)
2. 🔜 **Documentation scraping** for LLM knowledge base
3. 🔜 **Authenticated docs access** for latest API references
4. 🔜 **LLM-generated journal scripts** with validation
---
## Troubleshooting
### Intellisense Not Working
**Problem**: No autocomplete suggestions appear
**Solutions**:
1. **Check Pylance Extension**: VSCode → Extensions → Search "Pylance" → Ensure installed
2. **Verify Settings**: `.vscode/settings.json` has correct stub path
3. **Check Python Interpreter**: VSCode bottom-left → Select correct Python environment
4. **Restart VSCode**: Close all windows and reopen
5. **Check Stub Path**: Ensure path exists and contains `NXOpen` folder
### Wrong Suggestions
**Problem**: Autocomplete shows incorrect or outdated methods
**Solution**: Ensure stub files match your NX version:
- NX 2412 → Use `NX2412\ugopen\pythonStubs`
- Different NX version → Update stub path in settings
### Type Errors Shown
**Problem**: Pylance shows type errors for valid code
**Solutions**:
1. Set `"python.analysis.typeCheckingMode": "basic"` (not "strict")
2. Add `# type: ignore` for false positives
3. Update stub files if using newer NX version
---
## References
- **Siemens NX Documentation**: [PLM Portal](https://plm.sw.siemens.com)
- **TheScriptingEngineer**: [Blog with NXOpen examples](https://thescriptingengineer.com)
- **Pylance Documentation**: [VSCode Python](https://code.visualstudio.com/docs/python/editing)
---
## Next Steps
Now that intellisense is configured:
1. **Try It**: Open `tests/test_nxopen_intellisense.py` and explore
2. **Develop Faster**: Use autocomplete when writing journal scripts
3. **Contribute**: Help improve Atomizer's NXOpen integration
**See**: [DEVELOPMENT_GUIDANCE.md](../DEVELOPMENT_GUIDANCE.md) for strategic roadmap including NXOpen documentation access.
---
**Implemented By**: Antoine Letarte
**Date**: 2025-11-17
**NX Version**: 2412
**Status**: Production Ready

View File

@@ -0,0 +1,335 @@
# NXOpen Resources and References
## Overview
This document lists valuable resources for NXOpen development that can inform our implementation without direct code copying.
---
## Primary Resources
### 1. **Official Siemens NXOpen API Documentation**
**URL**: https://docs.sw.siemens.com/en-US/doc/209349590/PL20231101866122454.custom_api.nxopen_net
**Usage**:
- Primary reference for API syntax and methods
- Official namespace documentation
- Method signatures and return types
- Parameter descriptions
**Integration Strategy**:
- MCP tool `search_nxopen_docs` will fetch pages on-demand
- Cache frequently-used API snippets locally
- LLM can reference documentation when generating NXOpen code
---
### 2. **NXOpenTSE by The Scripting Engineer**
**GitHub**: https://github.com/theScriptingEngineer/nxopentse/tree/main
**Documentation**: https://nxopentsedocumentation.thescriptingengineer.com/
#### About NXOpenTSE
NXOpenTSE is an open-source Python library that provides:
- **High-level wrappers** around NXOpen API
- **Utility functions** for common NX operations
- **Well-documented examples** of NX automation patterns
- **Best practices** for NX scripting
**License**: MIT (as of last check - verify before use)
#### Why NXOpenTSE is Valuable for Atomizer
1. **Reference for Design Patterns**:
- How to structure NXOpen scripts
- Error handling approaches
- Session management patterns
- Part loading/unloading workflows
2. **Understanding API Usage**:
- See real-world examples of API calls
- Learn parameter combinations that work
- Understand method call sequences
3. **Avoiding Common Pitfalls**:
- See solutions to typical problems
- Learn about NX-specific gotchas
- Understand threading/transaction requirements
4. **Inspiration for Features**:
- Discover what's possible with NXOpen
- See advanced techniques
- Learn about lesser-known APIs
#### Integration Strategy for Atomizer
**Approach**: Reference, don't copy
```
✅ DO:
- Study NXOpenTSE documentation for understanding NX concepts
- Reference example patterns when writing our own code
- Learn from error handling approaches
- Use as inspiration for our API wrapper design
- Link to NXOpenTSE docs in our MCP system prompts
- Ask LLM to "check NXOpenTSE documentation for similar examples"
❌ DON'T:
- Copy code verbatim without attribution
- Replicate their library structure
- Import NXOpenTSE as a dependency (we build our own)
- Reuse their code without understanding it
```
#### Specific Areas to Reference
| Our Component | NXOpenTSE Reference Area | What to Learn |
|--------------|-------------------------|---------------|
| `nx_journals/update_and_solve.py` | Expression handling, part updates | How to safely modify expressions |
| `nx_journals/api_dispatcher.py` | Session management | Best practices for NX session handling |
| `mcp_server/tools/model_discovery.py` | Part file reading | How to extract model information |
| `optimization_engine/result_extractors/` | CAE result reading | Methods for accessing simulation results |
---
## Using NXOpenTSE Documentation with MCP
### MCP System Prompt Extension
When generating NXOpen code, the LLM can be instructed to:
```markdown
When writing NXOpen code:
1. First check official Siemens NXOpen API docs for method signatures
2. Reference NXOpenTSE documentation (https://nxopentsedocumentation.thescriptingengineer.com/)
for real-world usage patterns and best practices
3. Adapt patterns to our specific use case rather than copying directly
4. Always include error handling and validation
5. Add comments explaining NX-specific requirements
```
### Example LLM Workflow
**User**: "I need to read all expressions from a .sim file"
**LLM Reasoning**:
1. Check official API: `NXOpen.Part.Expressions`
2. Look at NXOpenTSE example: https://nxopentsedocumentation.thescriptingengineer.com/expressions.html
3. See pattern: Loop through `work_part.Expressions`, handle edge cases
4. Adapt to our needs: Add filtering, type checking, JSON serialization
**Generated Code** (our implementation, inspired by reference):
```python
def get_expressions(sim_file_path: Path) -> dict:
"""Extract all expressions from a .sim file."""
import NXOpen
session = NXOpen.Session.GetSession()
# Load sim file
part, status = session.Parts.OpenActiveDisplay(str(sim_file_path))
expressions = {}
try:
for expr in part.Expressions:
# Filter out system expressions (start with underscore)
if not expr.Name.startswith("_"):
expressions[expr.Name] = {
"value": expr.Value,
"units": expr.Units if expr.Units else "",
"type": str(expr.Type)
}
finally:
# Clean up
session.Parts.SetWork(None)
return expressions
```
---
## Additional Resources
### 3. **Eng-Tips NXOpen Forum**
**URL**: https://www.eng-tips.com/threadminder.cfm?pid=561
- Community Q&A
- Troubleshooting help
- User-contributed examples
### 4. **Stack Overflow - NXOpen Tag**
**URL**: https://stackoverflow.com/questions/tagged/nxopen
- Specific problem solutions
- Code snippets for common tasks
### 5. **Siemens PLM Community Forums**
**URL**: https://community.sw.siemens.com/
- Official support
- Product announcements
- Beta access information
---
## Best Practices Learned from NXOpenTSE
### 1. **Session Management**
```python
# Always get session at the start
session = NXOpen.Session.GetSession()
# Always check if part is loaded
if session.Parts.Work is None:
raise ValueError("No work part loaded")
```
### 2. **Error Handling**
```python
# Wrap NX operations in try-finally for cleanup
try:
# NX operations here
result = do_something()
finally:
# Always clean up, even on error
if temp_part:
session.Parts.CloseAll(NXOpen.BasePart.CloseWholeTree.True)
```
### 3. **Expression Updates**
```python
# Use Edit method for updating expressions
expr = part.Expressions.FindObject("parameter_name")
if expr:
expr.Edit(new_value)
else:
# Create if doesn't exist
unit = part.UnitCollection.FindObject("MilliMeter")
part.Expressions.CreateExpression(unit, "parameter_name", str(new_value))
```
### 4. **Simulation Solution Access**
```python
# Access simulation objects safely
sim_simulation = sim_part.Simulation
if sim_simulation:
solutions = sim_simulation.Solutions
for solution in solutions:
if solution.Name == target_name:
# Found our solution
pass
```
---
## Attribution and Licensing
### When Using Ideas from NXOpenTSE
1. **Add attribution in comments**:
```python
# Approach inspired by NXOpenTSE expression handling
# See: https://nxopentsedocumentation.thescriptingengineer.com/expressions.html
```
2. **Link in documentation**:
- Acknowledge inspiration in our docs
- Link to relevant NXOpenTSE pages
- Credit The Scripting Engineer for educational resources
3. **Respect MIT License** (verify current license):
- Give credit to original authors
- Don't claim their work as ours
- Contribute back to community if we find improvements
---
## Contributing to NXOpenTSE
If we discover useful patterns or fixes while building Atomizer:
- Consider contributing examples back to NXOpenTSE
- Report issues if we find documentation errors
- Share knowledge with the NX scripting community
---
## Integration with Atomizer MCP
### MCP Tool: `search_nxopen_resources`
```python
{
"name": "search_nxopen_resources",
"description": "Search NXOpen documentation and reference materials",
"inputSchema": {
"query": "How to update expressions in NX",
"sources": ["official", "nxopentse", "community"],
"return_examples": true
}
}
```
**Output**:
```json
{
"official_docs": "https://docs.sw.siemens.com/.../Expressions",
"nxopentse_example": "https://nxopentsedocumentation.thescriptingengineer.com/expressions.html",
"code_pattern": "Use part.Expressions.CreateExpression() or FindObject().Edit()",
"community_threads": [...]
}
```
### System Prompt Reference Section
```markdown
## NXOpen Development Resources
When implementing NXOpen functionality:
1. **Official API**: Consult Siemens NXOpen .NET documentation for authoritative API reference
2. **NXOpenTSE**: Reference https://nxopentsedocumentation.thescriptingengineer.com/ for:
- Practical usage patterns
- Common parameter combinations
- Error handling approaches
- Real-world examples
3. **Adaptation**: Always adapt patterns to Atomizer's specific architecture rather than copying
Remember: NXOpenTSE is a reference for learning, not a dependency to import.
```
---
## Summary
**NXOpenTSE is invaluable** for accelerating Atomizer development by:
- ✅ Showing proven patterns
- ✅ Teaching NX best practices
- ✅ Providing working examples to learn from
- ✅ Documenting edge cases and gotchas
**We will use it as**:
- 📚 Educational reference
- 🎯 Design pattern inspiration
- 🔍 Problem-solving resource
- 🧭 Navigation aid through complex NXOpen API
**Not as**:
- ❌ Code to copy-paste
- ❌ Dependency to import
- ❌ Replacement for understanding
This approach allows us to learn from the community while building something unique and tailored to Atomizer's specific optimization use case.
---
**Last Updated**: 2025-11-15
**Maintainer**: Atomaster Development Team

View File

@@ -0,0 +1,406 @@
# NX File Structure Protocol for Atomizer Studies
## Overview
This document defines the **mandatory file structure** for all NX Simcenter optimization studies. Following this protocol ensures optimization runs succeed without manual intervention.
## File Types and Purposes
### Part Files (.prt)
**Master Geometry File**: `<ModelName>.prt`
- Contains parametric CAD geometry
- Design variables are NX expressions
- Primary file opened by NX during optimization
**Idealized Part File**: `<ModelName>_fem<N>_i.prt`
- Auto-generated by NX during FEM idealization
- Contains simplified geometry for meshing
- Required for mesh regeneration
- MUST be copied with the study
**Assembly Part File** (for assemblies): `<AssemblyName>.prt`
- Top-level assembly structure
- References component parts
- Contains assembly constraints
### FEM Files (.fem)
**Part-Level FEM**: `<ModelName>_fem<N>.fem`
- Mesh definition for single part
- Material properties
- Boundary conditions
- Mesh parameters
**Assembly FEM** (.afem): `<AssemblyName>_fem<N>.afem`
- Assembly-level FEM structure
- Component FEM references
- Contact definitions
- Assembly-level boundary conditions
- **Requires all component .fem files**
### Simulation Files (.sim)
**Simulation Setup**: `<ModelName>_sim<N>.sim`
- Solution definitions (SOL 101, 103, etc.)
- Load cases
- Analysis types (static, modal, buckling, etc.)
- Results configuration
- References FEM file(s)
---
## Mandatory File Sets
### SINGLE PART ANALYSIS
**REQUIRED FILES** (All 4 must be present):
```
1_setup/model/
├── <ModelName>.prt # Master geometry (parametric)
├── <ModelName>_fem1.fem # FEM mesh definition
├── <ModelName>_fem1_i.prt # Idealized geometry (CRITICAL!)
└── <ModelName>_sim1.sim # Simulation setup
```
**Example** (Beam Study):
```
1_setup/model/
├── Beam.prt
├── Beam_fem1.fem
├── Beam_fem1_i.prt ← Don't forget this!
└── Beam_sim1.sim
```
**Why _i.prt is Required**:
- NX needs it to regenerate mesh when parameters change
- Without it: "Unable to find idealized body" error
- Generated once, reused for all trials
- Must be version-controlled with study
---
### ASSEMBLY ANALYSIS
**REQUIRED FILES** (More complex):
```
1_setup/model/
├── <Assembly>.prt # Top assembly
├── <Assembly>_fem1.afem # Assembly FEM (references below)
├── <Assembly>_sim1.sim # Simulation setup
├── components/ # Component parts subdirectory
│ ├── <Component1>.prt # Component part
│ ├── <Component1>_fem1.fem # Component FEM
│ ├── <Component1>_fem1_i.prt # Component idealized part
│ │
│ ├── <Component2>.prt
│ ├── <Component2>_fem1.fem
│ ├── <Component2>_fem1_i.prt
│ │
│ └── ... # Additional components
```
**Example** (Bracket Assembly with 3 components):
```
1_setup/model/
├── BracketAssembly.prt
├── BracketAssembly_fem1.afem
├── BracketAssembly_sim1.sim
└── components/
├── Bracket.prt
├── Bracket_fem1.fem
├── Bracket_fem1_i.prt
├── Plate.prt
├── Plate_fem1.fem
├── Plate_fem1_i.prt
├── Bolt.prt
├── Bolt_fem1.fem
└── Bolt_fem1_i.prt
```
---
## File Naming Conventions
### Standard Naming Pattern
```
<BaseName>_<FileType><Index>.<extension>
BaseName: Model/assembly name (e.g., "Beam", "BracketAssembly")
FileType: fem, sim
Index: Sequential number (1, 2, 3, ...)
Extension: .fem, .afem, .sim, .prt
```
### Examples
**Good**:
- `Beam_fem1.fem` → First FEM file for Beam
- `Beam_fem1_i.prt` → Idealized part for Beam FEM #1
- `Beam_sim1.sim` → First simulation for Beam
- `BracketAssembly_fem1.afem` → Assembly FEM
**Bad**:
- `BeamFEM.fem` → Missing index
- `Beam fem 1.fem` → Spaces not allowed
- `beam_fem1.fem` → Inconsistent capitalization
- `Beam_idealized.prt` → Non-standard naming
---
## Assembly FEM (.afem) Structure
### What Goes in .afem vs .fem
**Assembly FEM (.afem)**:
- References to component .fem files
- Contact pairs between components
- Assembly-level boundary conditions
- Component positioning/orientation
- Assembly-level loads
- Glue/weld connections
**Component FEM (.fem)**:
- Individual component mesh
- Component material properties
- Component-specific boundary conditions
- Local mesh refinement
- Component-specific loads
### Assembly FEM Dependencies
**Critical**: `.afem` file MUST have:
1. All referenced component `.prt` files
2. All referenced component `.fem` files
3. All referenced component `_i.prt` files
4. Proper relative paths to components directory
**Path Configuration**:
```
# In .afem file, component references should use:
./components/<Component>.prt
./components/<Component>_fem1.fem
```
---
## Study Directory Structure
### Complete Single-Part Study
```
study_name/
├── 1_setup/
│ ├── model/
│ │ ├── <Model>.prt ← Master geometry
│ │ ├── <Model>_fem1.fem ← FEM definition
│ │ ├── <Model>_fem1_i.prt ← Idealized part (REQUIRED!)
│ │ └── <Model>_sim1.sim ← Simulation setup
│ ├── optimization_config.json
│ └── workflow_config.json
├── 2_results/
│ └── (generated during optimization)
└── run_optimization.py
```
### Complete Assembly Study
```
study_name/
├── 1_setup/
│ ├── model/
│ │ ├── <Assembly>.prt
│ │ ├── <Assembly>_fem1.afem
│ │ ├── <Assembly>_sim1.sim
│ │ │
│ │ └── components/
│ │ ├── <Comp1>.prt
│ │ ├── <Comp1>_fem1.fem
│ │ ├── <Comp1>_fem1_i.prt
│ │ ├── <Comp2>.prt
│ │ ├── <Comp2>_fem1.fem
│ │ ├── <Comp2>_fem1_i.prt
│ │ └── ...
│ │
│ ├── optimization_config.json
│ └── workflow_config.json
├── 2_results/
└── run_optimization.py
```
---
## Common Errors and Solutions
### Error: "Unable to find idealized body"
**Cause**: Missing `_fem1_i.prt` file
**Solution**: Always copy the `_i.prt` file with your study
### Error: "Cannot open FEM file"
**Cause**: FEM file references missing component
**Solution**: Verify all component `.fem` and `.prt` files are present
### Error: "Mesh regeneration failed"
**Cause**: Idealized part doesn't match current geometry
**Solution**: Regenerate idealization in NX, copy new `_i.prt`
### Error: "Component not found in assembly"
**Cause**: Wrong path to components directory
**Solution**: Ensure `components/` subdirectory structure is correct
---
## Validation Checklist
### Before Creating Study
**Single Part**:
- [ ] `<Model>.prt` exists
- [ ] `<Model>_fem1.fem` exists
- [ ] `<Model>_fem1_i.prt` exists (CRITICAL!)
- [ ] `<Model>_sim1.sim` exists
- [ ] All 4 files in same directory
**Assembly**:
- [ ] `<Assembly>.prt` exists
- [ ] `<Assembly>_fem1.afem` exists
- [ ] `<Assembly>_sim1.sim` exists
- [ ] `components/` directory exists
- [ ] Each component has `.prt`, `.fem`, `_i.prt`
- [ ] All component paths in `.afem` are correct
### After Copying Study
**Run Quick Test**:
1. Open `<Model>.prt` in NX
2. Open Simulation Navigator
3. Solve simulation manually
4. If it solves → files are correct
5. If it fails → check error for missing files
---
## Best Practices
### 1. Always Copy Complete File Sets
Don't copy just `.prt` and `.sim`:
```bash
# Bad
cp Model.prt new_study/
cp Model_sim1.sim new_study/
# Good
cp Model.prt Model_fem1.fem Model_fem1_i.prt Model_sim1.sim new_study/
```
### 2. Preserve Directory Structure
For assemblies, maintain the `components/` structure:
```bash
# Create structure first
mkdir -p new_study/1_setup/model/components
# Then copy files
cp Assembly* new_study/1_setup/model/
cp components/* new_study/1_setup/model/components/
```
### 3. Version Control All Files
In git, track:
- All `.prt` files (including `_i.prt`)
- All `.fem` and `.afem` files
- All `.sim` files
- Do NOT use `.gitignore` to exclude `_i.prt`!
### 4. Document Component Dependencies
For assemblies, create a `COMPONENTS.md`:
```markdown
# Assembly Components
Main Assembly: BracketAssembly.prt
Components:
1. Bracket.prt - Main structural member (parametric)
2. Plate.prt - Mounting plate (parametric)
3. Bolt.prt - M6 bolt (fixed geometry)
Contact Pairs:
- Bracket <-> Plate: Bonded contact
- Bolt <-> Bracket: Frictional (μ=0.3)
```
---
## Advanced: Multi-Solution Studies
For studies with multiple analysis types (static + modal, etc.):
```
1_setup/model/
├── Model.prt
├── Model_fem1.fem # Shared mesh
├── Model_fem1_i.prt # Shared idealization
├── Model_sim1.sim # Static analysis
└── Model_sim2.sim # Modal analysis
```
Both `.sim` files reference the same `.fem` and `_i.prt`.
---
## Quick Reference Card
**Minimum Files for Single Part**:
```
✓ <Model>.prt
✓ <Model>_fem1.fem
✓ <Model>_fem1_i.prt ← Don't forget!
✓ <Model>_sim1.sim
```
**Minimum Files for Assembly**:
```
✓ <Assembly>.prt
✓ <Assembly>_fem1.afem
✓ <Assembly>_sim1.sim
✓ components/<Comp1>.prt
✓ components/<Comp1>_fem1.fem
✓ components/<Comp1>_fem1_i.prt
(repeat for each component)
```
**Golden Rule**: If NX created it during meshing/simulation setup, you need to copy it for optimization!
---
## Future Enhancements
Planned protocol additions:
- [ ] Multi-mesh assembly support
- [ ] Submodeling workflows
- [ ] Contact table definitions
- [ ] Result mesh specification
- [ ] Optimization-specific mesh controls
---
**Last Updated**: 2025-01-22
**Version**: 1.0
**Status**: MANDATORY for all studies

View File

@@ -0,0 +1,419 @@
# NX Session Management
**Status**: Implemented
**Version**: 1.0
**Date**: 2025-11-20
## Problem
When running multiple optimizations concurrently or when a user has NX open for manual work, conflicts can occur:
1. **Multiple Optimizations**: Two optimization studies trying to modify the same model simultaneously
2. **User's Interactive NX**: Batch optimization interfering with user's manual work
3. **File Corruption**: Concurrent writes to .prt/.sim files causing corruption
4. **License Conflicts**: Multiple NX instances competing for licenses
5. **Journal Failures**: Journals trying to run on wrong NX session
## Solution: NX Session Manager
The `NXSessionManager` class provides intelligent session conflict prevention.
### Key Features
1. **Session Detection**
- Detects all running NX processes (interactive + batch)
- Identifies interactive vs batch sessions
- Warns if user has NX open
2. **File Locking**
- Exclusive locks on model files (.prt)
- Prevents two optimizations from modifying same model
- Queues trials if model is locked
3. **Process Queuing**
- Limits concurrent NX batch sessions (default: 1)
- Waits if max sessions reached
- Automatic timeout and error handling
4. **Stale Lock Cleanup**
- Detects crashed processes
- Removes orphaned lock files
- Prevents permanent deadlocks
## Architecture
### Session Manager Components
```python
from optimization_engine.nx_session_manager import NXSessionManager
# Initialize
session_mgr = NXSessionManager(
lock_dir=Path.home() / ".atomizer" / "locks",
max_concurrent_sessions=1, # Max parallel NX instances
wait_timeout=300, # Max wait time (5 min)
verbose=True
)
```
### Two-Level Locking
**Level 1: Model File Lock** (most important)
```python
# Ensures exclusive access to a specific model
with session_mgr.acquire_model_lock(prt_file, study_name):
# Update CAD model
updater.update_expressions(params)
# Run simulation
result = solver.run_simulation(sim_file)
```
**Level 2: NX Session Lock** (optional)
```python
# Limits total concurrent NX batch instances
with session_mgr.acquire_nx_session(study_name):
# Run NX batch operation
pass
```
## Usage Examples
### Example 1: Single Optimization (Recommended)
```python
from optimization_engine.nx_solver import NXSolver
from optimization_engine.nx_updater import NXParameterUpdater
from optimization_engine.nx_session_manager import NXSessionManager
# Initialize components
session_mgr = NXSessionManager(verbose=True)
updater = NXParameterUpdater("model.prt")
solver = NXSolver()
# Check for interactive NX sessions
if session_mgr.is_nx_interactive_session_running():
print("WARNING: NX is open! Close it before running optimization.")
# You can choose to abort or continue
# Run trials with session management
for trial in trials:
with session_mgr.acquire_model_lock(prt_file, "my_study"):
# Exclusive access to model - safe to modify
updater.update_expressions(params)
result = solver.run_simulation(sim_file)
```
### Example 2: Multiple Concurrent Optimizations
```python
# Study A (in one terminal)
session_mgr_A = NXSessionManager()
with session_mgr_A.acquire_model_lock(model_A_prt, "study_A"):
# Works on model A
updater_A.update_expressions(params_A)
solver_A.run_simulation(sim_A)
# Study B (in another terminal, simultaneously)
session_mgr_B = NXSessionManager()
with session_mgr_B.acquire_model_lock(model_B_prt, "study_B"):
# Works on model B (different model - no conflict)
updater_B.update_expressions(params_B)
solver_B.run_simulation(sim_B)
# If they try to use SAME model:
with session_mgr_A.acquire_model_lock(model_SAME, "study_A"):
pass # Acquires lock
with session_mgr_B.acquire_model_lock(model_SAME, "study_B"):
# Waits here until study_A releases lock
# Then proceeds safely
pass
```
### Example 3: Protection Against User's Interactive NX
```python
session_mgr = NXSessionManager(verbose=True)
# Detect if user has NX open
nx_sessions = session_mgr.get_running_nx_sessions()
for session in nx_sessions:
print(f"Detected: {session.name} (PID {session.pid})")
if session_mgr.is_nx_interactive_session_running():
print("Interactive NX session detected!")
print("Recommend closing NX before running optimization.")
# Option 1: Abort
raise RuntimeError("Close NX and try again")
# Option 2: Continue with warning
print("Continuing anyway... (may cause conflicts)")
```
## Configuration
### Lock Directory
Default: `~/.atomizer/locks/`
Custom:
```python
session_mgr = NXSessionManager(
lock_dir=Path("/custom/lock/dir")
)
```
### Concurrent Session Limit
Default: 1 (safest)
Allow multiple:
```python
session_mgr = NXSessionManager(
max_concurrent_sessions=2 # Allow 2 parallel NX batches
)
```
**Warning**: Multiple concurrent NX sessions require multiple licenses!
### Wait Timeout
Default: 300 seconds (5 minutes)
Custom:
```python
session_mgr = NXSessionManager(
wait_timeout=600 # Wait up to 10 minutes
)
```
## Integration with NXSolver
The `NXSolver` class has built-in session management:
```python
from optimization_engine.nx_solver import NXSolver
solver = NXSolver(
enable_session_management=True, # Default
study_name="my_study"
)
# Session management happens automatically
result = solver.run_simulation(sim_file)
```
**Note**: Full automatic integration is planned but not yet implemented. Currently, manual wrapping is recommended.
## Status Monitoring
### Get Current Status
```python
report = session_mgr.get_status_report()
print(report)
```
Output:
```
======================================================================
NX SESSION MANAGER STATUS
======================================================================
Running NX Processes: 2
PID 12345: ugraf.exe
Working dir: C:/Users/username/project
PID 12346: run_journal.exe
WARNING: Interactive NX session detected!
Batch operations may conflict with user's work.
Active Optimization Sessions: 1/1
my_study (PID 12347)
Active Lock Files: 1
======================================================================
```
### Cleanup Stale Locks
```python
# Run at startup
session_mgr.cleanup_stale_locks()
```
Removes lock files from crashed processes.
## Error Handling
### Lock Timeout
```python
try:
with session_mgr.acquire_model_lock(prt_file, study_name):
# ... modify model ...
pass
except TimeoutError as e:
print(f"Could not acquire model lock: {e}")
print("Another optimization may be using this model.")
# Handle error (skip trial, abort, etc.)
```
### NX Session Timeout
```python
try:
with session_mgr.acquire_nx_session(study_name):
# ... run NX batch ...
pass
except TimeoutError as e:
print(f"Could not acquire NX session: {e}")
print(f"Max concurrent sessions ({session_mgr.max_concurrent}) reached.")
# Handle error
```
## Platform Support
-**Windows**: Full support (uses `msvcrt` for file locking)
-**Linux/Mac**: Full support (uses `fcntl` for file locking)
-**Cross-Platform**: Lock files work across different OS instances
## Limitations
1. **Same Machine Only**: Session manager only prevents conflicts on the same machine
- For networked optimizations, need distributed lock manager
2. **File System Required**: Requires writable lock directory
- May not work on read-only filesystems
3. **Process Detection**: Relies on `psutil` for process detection
- May miss processes in some edge cases
4. **Not Real-Time**: Lock checking has small latency
- Not suitable for microsecond-level synchronization
## Best Practices
### 1. Always Use Model Locks
```python
# GOOD: Protected
with session_mgr.acquire_model_lock(prt_file, study_name):
updater.update_expressions(params)
# BAD: Unprotected (race condition!)
updater.update_expressions(params)
```
### 2. Check for Interactive NX
```python
# Before starting optimization
if session_mgr.is_nx_interactive_session_running():
print("WARNING: Close NX before running optimization!")
# Decide: abort or continue with warning
```
### 3. Cleanup on Startup
```python
# At optimization start
session_mgr = NXSessionManager()
session_mgr.cleanup_stale_locks() # Remove crashed process locks
```
### 4. Use Unique Study Names
```python
# GOOD: Unique names
solver_A = NXSolver(study_name="beam_optimization_trial_42")
solver_B = NXSolver(study_name="plate_optimization_trial_15")
# BAD: Same name (confusing logs)
solver_A = NXSolver(study_name="default_study")
solver_B = NXSolver(study_name="default_study")
```
### 5. Handle Timeouts Gracefully
```python
try:
with session_mgr.acquire_model_lock(prt_file, study_name):
result = solver.run_simulation(sim_file)
except TimeoutError:
# Don't crash entire optimization!
print("Lock timeout - skipping this trial")
raise optuna.TrialPruned() # Optuna will continue
```
## Troubleshooting
### "Lock timeout" errors
**Cause**: Another process holds the lock longer than timeout
**Solutions**:
1. Check if another optimization is running
2. Increase timeout: `wait_timeout=600`
3. Check for stale locks: `cleanup_stale_locks()`
### "Interactive NX session detected" warnings
**Cause**: User has NX open in GUI mode
**Solutions**:
1. Close interactive NX before optimization
2. Use different model files
3. Continue with warning (risky!)
### Stale lock files
**Cause**: Optimization crashed without releasing locks
**Solution**:
```python
session_mgr.cleanup_stale_locks()
```
### Multiple optimizations on different models still conflict
**Cause**: NX session limit reached
**Solution**:
```python
session_mgr = NXSessionManager(
max_concurrent_sessions=2 # Allow 2 parallel NX instances
)
```
**Warning**: Requires 2 NX licenses!
## Future Enhancements
- [ ] Distributed lock manager (for cluster computing)
- [ ] Automatic NX session affinity (assign trials to specific NX instances)
- [ ] License pool management
- [ ] Network file lock support (for shared drives)
- [ ] Real-time session monitoring dashboard
- [ ] Automatic crash recovery
## Version History
### Version 1.0 (2025-11-20)
- Initial implementation
- Model file locking
- NX session detection
- Concurrent session limiting
- Stale lock cleanup
- Status reporting
---
**Implementation Status**: ✅ Core functionality complete
**Testing Status**: ⚠️ Needs production testing
**Documentation Status**: ✅ Complete

View File

@@ -0,0 +1,144 @@
# System Configuration
> **Critical**: These are the ONLY paths and environments to be used unless explicitly reconfigured by the user.
---
## Python Environment
**Environment Name**: `atomizer`
**Path**: `c:/Users/antoi/anaconda3/envs/atomizer/python.exe`
**Usage**: ALL Python scripts and commands MUST use this environment.
### Examples:
```bash
# Correct
"c:/Users/antoi/anaconda3/envs/atomizer/python.exe" script.py
# WRONG - Never use test_env
"c:/Users/antoi/anaconda3/envs/test_env/python.exe" script.py
```
---
## NX/Simcenter Installation
**Active Installation**: NX 2412
**Base Path**: `C:\Program Files\Siemens\NX2412`
**Key Directories**:
- NX Binaries: `C:\Program Files\Siemens\NX2412\NXBIN`
- Material Library: `C:\Program Files\Siemens\NX2412\UGII\materials`
- Python Stubs: `C:\Program Files\Siemens\NX2412\ugopen\pythonStubs`
### Critical Files:
- **run_journal.exe**: `C:\Program Files\Siemens\NX2412\NXBIN\run_journal.exe`
- **Material Library**: `C:\Program Files\Siemens\NX2412\UGII\materials\physicalmateriallibrary.xml`
### PROHIBITED Paths:
-`C:\Program Files\Siemens\Simcenter3D_2412` - DO NOT USE
- ❌ Any path containing "Simcenter3D" - DO NOT USE
**Reason**: NX2412 is the primary CAD/CAE environment. Simcenter3D_2412 is a separate installation and should not be accessed unless explicitly configured by the user.
---
## NX Journal Execution
**Command Template**:
```bash
"C:/Program Files/Siemens/NX2412/NXBIN/run_journal.exe" <journal_script.py> -args <arg1> <arg2>
```
**Example**:
```bash
"C:/Program Files/Siemens/NX2412/NXBIN/run_journal.exe" "optimization_engine/import_expressions.py" -args "studies/beam/model/Beam.prt" "studies/beam/model/Beam_study_variables.exp"
```
---
## NXOpen Python Stubs (for Intellisense)
**Path**: `C:\Program Files\Siemens\NX2412\ugopen\pythonStubs`
**VSCode Configuration** (`.vscode/settings.json`):
```json
{
"python.analysis.extraPaths": [
"C:\\Program Files\\Siemens\\NX2412\\ugopen\\pythonStubs"
],
"python.analysis.typeCheckingMode": "basic"
}
```
---
## Material Library Access
**Library File**: `C:\Program Files\Siemens\NX2412\UGII\materials\physicalmateriallibrary.xml`
**Format**: MatML XML format
**Properties Available**:
- `Mass_Density__RHO__6` (kg/mm³)
- `Youngs_Modulus_E__31` (Pa)
- `PoissonsRatio` (dimensionless)
- `Yield_Strength_32` (Pa)
- `Thermal_Expansion_A__34` (1/°C)
- `Thermal_Conductivity__K__35` (mW/mm/°C)
- `Specific_Heat_CP__23` (mJ/kg/°C)
**Common Materials**:
- AISI_Steel_1005 (E=200 GPa, ρ=7872 kg/m³, ν=0.25)
- AISI_Steel_4340 (E=193 GPa, ρ=7850 kg/m³, ν=0.284)
- Aluminum_6061-T6 (E=69 GPa, ρ=2700 kg/m³, ν=0.33)
- Titanium_Ti-6Al-4V (E=114 GPa, ρ=4430 kg/m³, ν=0.34)
---
## Nastran Solver
**Solver Path**: Embedded in NX2412 installation
**Input Files**: `.dat` (Nastran bulk data)
**Output Files**:
- `.op2` (binary results - use pyNastran)
- `.f06` (text results - human readable)
**Material Units in .dat files**:
- Young's Modulus: Pa (Pascals)
- Density: kg/mm³
- Poisson's Ratio: dimensionless
---
## Future Expansion
If using a different NX or Simcenter version, the user will explicitly configure:
1. Update this file with new paths
2. Update `nx_updater.py` configuration
3. Update `.vscode/settings.json` for new stub paths
**Until then**: ALWAYS use NX2412 paths as documented above.
---
## Validation Checklist
Before running any NX-related operation, verify:
- ✅ Python command uses `atomizer` environment
- ✅ NX paths point to `NX2412` (NOT Simcenter3D_2412)
- ✅ Material library accessed from `NX2412\UGII\materials`
- ✅ Journal script uses `NX2412\NXBIN\run_journal.exe`
---
**Last Updated**: 2025-11-17
**Maintained By**: Antoine Letarte
**Critical Importance**: HIGH - Incorrect paths will cause system failures