feat: Merge Atomizer-Field neural network module into main repository
Permanently integrates the Atomizer-Field GNN surrogate system: - neural_models/: Graph Neural Network for FEA field prediction - batch_parser.py: Parse training data from FEA exports - train.py: Neural network training pipeline - predict.py: Inference engine for fast predictions This enables 600x-2200x speedup over traditional FEA by replacing expensive simulations with millisecond neural network predictions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
674
atomizer-field/Instructions.md
Normal file
674
atomizer-field/Instructions.md
Normal file
@@ -0,0 +1,674 @@
|
||||
Neural Field Data Parser: From NX Nastran Files to Training Data
|
||||
Complete Implementation Guide
|
||||
|
||||
What You Have vs What You Need
|
||||
✅ What NX Nastran Gives You:
|
||||
Files Available:
|
||||
|
||||
.sim - Simulation file with load/BC definitions
|
||||
.fem - Finite element model
|
||||
.prt - Part geometry
|
||||
.bdf/.dat - Nastran input deck (mesh, materials, loads, BCs)
|
||||
.op2 - Binary results (stress, displacement, strain)
|
||||
.f06 - ASCII results (human readable)
|
||||
.log - Solver log
|
||||
|
||||
This is SUFFICIENT! The BDF contains everything about setup, OP2 contains all results.
|
||||
|
||||
Step-by-Step Instructions for Manual Data Generation
|
||||
Step 1: Set Up Your Analysis in NX
|
||||
1. Create your geometry in NX
|
||||
2. Generate mesh (record statistics)
|
||||
3. Apply materials
|
||||
4. Define boundary conditions:
|
||||
- Fixed supports
|
||||
- Pinned constraints
|
||||
- Contact (if needed)
|
||||
5. Apply loads:
|
||||
- Forces
|
||||
- Pressures
|
||||
- Gravity
|
||||
6. Set up solution parameters
|
||||
7. Run analysis
|
||||
8. Ensure these files are generated:
|
||||
- model.bdf (or .dat)
|
||||
- model.op2
|
||||
- model.f06
|
||||
Step 2: Organize Your Files
|
||||
training_case_001/
|
||||
├── input/
|
||||
│ ├── model.bdf # Main input deck
|
||||
│ ├── model.sim # NX simulation file
|
||||
│ └── geometry.prt # Original geometry
|
||||
├── output/
|
||||
│ ├── model.op2 # Binary results
|
||||
│ ├── model.f06 # ASCII results
|
||||
│ └── model.log # Solver log
|
||||
└── metadata.json # Your manual annotations
|
||||
|
||||
Python Parser Implementation
|
||||
Main Parser Script
|
||||
python"""
|
||||
neural_field_parser.py
|
||||
Parses NX Nastran files into Neural Field training data
|
||||
"""
|
||||
|
||||
import json
|
||||
import numpy as np
|
||||
import h5py
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
import hashlib
|
||||
|
||||
# pyNastran imports
|
||||
from pyNastran.bdf.bdf import BDF
|
||||
from pyNastran.op2.op2 import OP2
|
||||
|
||||
class NastranToNeuralFieldParser:
|
||||
"""
|
||||
Parses Nastran BDF/OP2 files into Neural Field data structure
|
||||
"""
|
||||
|
||||
def __init__(self, case_directory):
|
||||
self.case_dir = Path(case_directory)
|
||||
self.bdf_file = self.case_dir / "input" / "model.bdf"
|
||||
self.op2_file = self.case_dir / "output" / "model.op2"
|
||||
|
||||
# Initialize readers
|
||||
self.bdf = BDF(debug=False)
|
||||
self.op2 = OP2(debug=False)
|
||||
|
||||
# Data structure
|
||||
self.neural_field_data = {
|
||||
"metadata": {},
|
||||
"geometry": {},
|
||||
"mesh": {},
|
||||
"materials": {},
|
||||
"boundary_conditions": {},
|
||||
"loads": {},
|
||||
"results": {}
|
||||
}
|
||||
|
||||
def parse_all(self):
|
||||
"""
|
||||
Main parsing function
|
||||
"""
|
||||
print("Starting parse of Nastran files...")
|
||||
|
||||
# Parse input deck
|
||||
print("Reading BDF file...")
|
||||
self.bdf.read_bdf(str(self.bdf_file))
|
||||
|
||||
# Parse results
|
||||
print("Reading OP2 file...")
|
||||
self.op2.read_op2(str(self.op2_file))
|
||||
|
||||
# Extract all data
|
||||
self.extract_metadata()
|
||||
self.extract_mesh()
|
||||
self.extract_materials()
|
||||
self.extract_boundary_conditions()
|
||||
self.extract_loads()
|
||||
self.extract_results()
|
||||
|
||||
# Save to file
|
||||
self.save_data()
|
||||
|
||||
print("Parse complete!")
|
||||
return self.neural_field_data
|
||||
|
||||
def extract_metadata(self):
|
||||
"""
|
||||
Extract metadata and analysis info
|
||||
"""
|
||||
self.neural_field_data["metadata"] = {
|
||||
"version": "1.0.0",
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"source": "NX_Nastran",
|
||||
"case_directory": str(self.case_dir),
|
||||
"analysis_type": self.op2.sol, # SOL 101, 103, etc.
|
||||
"title": self.bdf.case_control_deck.title.title if hasattr(self.bdf.case_control_deck, 'title') else "",
|
||||
"units": {
|
||||
"length": "mm", # You may need to specify this
|
||||
"force": "N",
|
||||
"stress": "Pa",
|
||||
"temperature": "K"
|
||||
}
|
||||
}
|
||||
|
||||
def extract_mesh(self):
|
||||
"""
|
||||
Extract mesh data from BDF
|
||||
"""
|
||||
print("Extracting mesh...")
|
||||
|
||||
# Nodes
|
||||
nodes = []
|
||||
node_ids = []
|
||||
for nid, node in sorted(self.bdf.nodes.items()):
|
||||
node_ids.append(nid)
|
||||
nodes.append(node.get_position())
|
||||
|
||||
nodes_array = np.array(nodes)
|
||||
|
||||
# Elements
|
||||
element_data = {
|
||||
"solid": [],
|
||||
"shell": [],
|
||||
"beam": [],
|
||||
"rigid": []
|
||||
}
|
||||
|
||||
# Solid elements (TETRA, HEXA, PENTA)
|
||||
for eid, elem in self.bdf.elements.items():
|
||||
elem_type = elem.type
|
||||
|
||||
if elem_type in ['CTETRA', 'CHEXA', 'CPENTA', 'CTETRA10', 'CHEXA20']:
|
||||
element_data["solid"].append({
|
||||
"id": eid,
|
||||
"type": elem_type,
|
||||
"nodes": elem.node_ids,
|
||||
"material_id": elem.mid,
|
||||
"property_id": elem.pid if hasattr(elem, 'pid') else None
|
||||
})
|
||||
|
||||
elif elem_type in ['CQUAD4', 'CTRIA3', 'CQUAD8', 'CTRIA6']:
|
||||
element_data["shell"].append({
|
||||
"id": eid,
|
||||
"type": elem_type,
|
||||
"nodes": elem.node_ids,
|
||||
"material_id": elem.mid,
|
||||
"property_id": elem.pid,
|
||||
"thickness": elem.T() if hasattr(elem, 'T') else None
|
||||
})
|
||||
|
||||
elif elem_type in ['CBAR', 'CBEAM', 'CROD']:
|
||||
element_data["beam"].append({
|
||||
"id": eid,
|
||||
"type": elem_type,
|
||||
"nodes": elem.node_ids,
|
||||
"material_id": elem.mid,
|
||||
"property_id": elem.pid
|
||||
})
|
||||
|
||||
elif elem_type in ['RBE2', 'RBE3', 'RBAR']:
|
||||
element_data["rigid"].append({
|
||||
"id": eid,
|
||||
"type": elem_type,
|
||||
"nodes": elem.node_ids
|
||||
})
|
||||
|
||||
# Store mesh data
|
||||
self.neural_field_data["mesh"] = {
|
||||
"statistics": {
|
||||
"n_nodes": len(nodes),
|
||||
"n_elements": len(self.bdf.elements),
|
||||
"element_types": {
|
||||
"solid": len(element_data["solid"]),
|
||||
"shell": len(element_data["shell"]),
|
||||
"beam": len(element_data["beam"]),
|
||||
"rigid": len(element_data["rigid"])
|
||||
}
|
||||
},
|
||||
"nodes": {
|
||||
"ids": node_ids,
|
||||
"coordinates": nodes_array.tolist(),
|
||||
"shape": list(nodes_array.shape)
|
||||
},
|
||||
"elements": element_data
|
||||
}
|
||||
|
||||
def extract_materials(self):
|
||||
"""
|
||||
Extract material properties
|
||||
"""
|
||||
print("Extracting materials...")
|
||||
|
||||
materials = []
|
||||
for mid, mat in self.bdf.materials.items():
|
||||
mat_data = {
|
||||
"id": mid,
|
||||
"type": mat.type
|
||||
}
|
||||
|
||||
if mat.type == 'MAT1': # Isotropic material
|
||||
mat_data.update({
|
||||
"E": mat.e, # Young's modulus
|
||||
"nu": mat.nu, # Poisson's ratio
|
||||
"rho": mat.rho, # Density
|
||||
"G": mat.g, # Shear modulus
|
||||
"alpha": mat.a if hasattr(mat, 'a') else None, # Thermal expansion
|
||||
"tref": mat.tref if hasattr(mat, 'tref') else None,
|
||||
"ST": mat.St() if hasattr(mat, 'St') else None, # Tensile stress limit
|
||||
"SC": mat.Sc() if hasattr(mat, 'Sc') else None, # Compressive stress limit
|
||||
"SS": mat.Ss() if hasattr(mat, 'Ss') else None # Shear stress limit
|
||||
})
|
||||
|
||||
materials.append(mat_data)
|
||||
|
||||
self.neural_field_data["materials"] = materials
|
||||
|
||||
def extract_boundary_conditions(self):
|
||||
"""
|
||||
Extract boundary conditions from BDF
|
||||
"""
|
||||
print("Extracting boundary conditions...")
|
||||
|
||||
bcs = {
|
||||
"spc": [], # Single point constraints
|
||||
"mpc": [], # Multi-point constraints
|
||||
"suport": [] # Free body supports
|
||||
}
|
||||
|
||||
# SPC (fixed DOFs)
|
||||
for spc_id, spc_list in self.bdf.spcs.items():
|
||||
for spc in spc_list:
|
||||
bcs["spc"].append({
|
||||
"id": spc_id,
|
||||
"node": spc.node_ids[0] if hasattr(spc, 'node_ids') else spc.node,
|
||||
"dofs": spc.components, # Which DOFs are constrained (123456)
|
||||
"enforced_motion": spc.enforced
|
||||
})
|
||||
|
||||
# MPC equations
|
||||
for mpc_id, mpc_list in self.bdf.mpcs.items():
|
||||
for mpc in mpc_list:
|
||||
bcs["mpc"].append({
|
||||
"id": mpc_id,
|
||||
"nodes": mpc.node_ids,
|
||||
"coefficients": mpc.coefficients,
|
||||
"components": mpc.components
|
||||
})
|
||||
|
||||
self.neural_field_data["boundary_conditions"] = bcs
|
||||
|
||||
def extract_loads(self):
|
||||
"""
|
||||
Extract loads from BDF
|
||||
"""
|
||||
print("Extracting loads...")
|
||||
|
||||
loads = {
|
||||
"point_forces": [],
|
||||
"pressure": [],
|
||||
"gravity": [],
|
||||
"thermal": []
|
||||
}
|
||||
|
||||
# Point forces (FORCE, MOMENT)
|
||||
for load_id, load_list in self.bdf.loads.items():
|
||||
for load in load_list:
|
||||
if load.type == 'FORCE':
|
||||
loads["point_forces"].append({
|
||||
"id": load_id,
|
||||
"node": load.node,
|
||||
"magnitude": load.mag,
|
||||
"direction": [load.xyz[0], load.xyz[1], load.xyz[2]],
|
||||
"coord_system": load.cid
|
||||
})
|
||||
|
||||
elif load.type == 'MOMENT':
|
||||
loads["point_forces"].append({
|
||||
"id": load_id,
|
||||
"node": load.node,
|
||||
"moment": load.mag,
|
||||
"direction": [load.xyz[0], load.xyz[1], load.xyz[2]],
|
||||
"coord_system": load.cid
|
||||
})
|
||||
|
||||
elif load.type in ['PLOAD', 'PLOAD2', 'PLOAD4']:
|
||||
loads["pressure"].append({
|
||||
"id": load_id,
|
||||
"elements": load.element_ids,
|
||||
"pressure": load.pressure,
|
||||
"type": load.type
|
||||
})
|
||||
|
||||
elif load.type == 'GRAV':
|
||||
loads["gravity"].append({
|
||||
"id": load_id,
|
||||
"acceleration": load.scale,
|
||||
"direction": [load.N[0], load.N[1], load.N[2]],
|
||||
"coord_system": load.cid
|
||||
})
|
||||
|
||||
# Temperature loads
|
||||
for temp_id, temp_list in self.bdf.temps.items():
|
||||
for temp in temp_list:
|
||||
loads["thermal"].append({
|
||||
"id": temp_id,
|
||||
"node": temp.node,
|
||||
"temperature": temp.temperature
|
||||
})
|
||||
|
||||
self.neural_field_data["loads"] = loads
|
||||
|
||||
def extract_results(self):
|
||||
"""
|
||||
Extract results from OP2
|
||||
"""
|
||||
print("Extracting results...")
|
||||
|
||||
results = {}
|
||||
|
||||
# Get subcase ID (usually 1 for linear static)
|
||||
subcase_id = 1
|
||||
|
||||
# Displacement
|
||||
if hasattr(self.op2, 'displacements'):
|
||||
disp = self.op2.displacements[subcase_id]
|
||||
disp_data = disp.data[0, :, :] # [itime=0, all_nodes, 6_dofs]
|
||||
|
||||
results["displacement"] = {
|
||||
"node_ids": disp.node_gridtype[:, 0].tolist(),
|
||||
"data": disp_data.tolist(),
|
||||
"shape": list(disp_data.shape),
|
||||
"max_magnitude": float(np.max(np.linalg.norm(disp_data[:, :3], axis=1)))
|
||||
}
|
||||
|
||||
# Stress - handle different element types
|
||||
stress_results = {}
|
||||
|
||||
# Solid stress
|
||||
if hasattr(self.op2, 'ctetra_stress'):
|
||||
stress = self.op2.ctetra_stress[subcase_id]
|
||||
stress_data = stress.data[0, :, :]
|
||||
stress_results["solid_stress"] = {
|
||||
"element_ids": stress.element_node[:, 0].tolist(),
|
||||
"data": stress_data.tolist(),
|
||||
"von_mises": stress_data[:, -1].tolist() if stress_data.shape[1] > 6 else None
|
||||
}
|
||||
|
||||
# Shell stress
|
||||
if hasattr(self.op2, 'cquad4_stress'):
|
||||
stress = self.op2.cquad4_stress[subcase_id]
|
||||
stress_data = stress.data[0, :, :]
|
||||
stress_results["shell_stress"] = {
|
||||
"element_ids": stress.element_node[:, 0].tolist(),
|
||||
"data": stress_data.tolist()
|
||||
}
|
||||
|
||||
results["stress"] = stress_results
|
||||
|
||||
# Strain
|
||||
strain_results = {}
|
||||
if hasattr(self.op2, 'ctetra_strain'):
|
||||
strain = self.op2.ctetra_strain[subcase_id]
|
||||
strain_data = strain.data[0, :, :]
|
||||
strain_results["solid_strain"] = {
|
||||
"element_ids": strain.element_node[:, 0].tolist(),
|
||||
"data": strain_data.tolist()
|
||||
}
|
||||
|
||||
results["strain"] = strain_results
|
||||
|
||||
# SPC Forces (reactions)
|
||||
if hasattr(self.op2, 'spc_forces'):
|
||||
spc = self.op2.spc_forces[subcase_id]
|
||||
spc_data = spc.data[0, :, :]
|
||||
results["reactions"] = {
|
||||
"node_ids": spc.node_gridtype[:, 0].tolist(),
|
||||
"forces": spc_data.tolist()
|
||||
}
|
||||
|
||||
self.neural_field_data["results"] = results
|
||||
|
||||
def save_data(self):
|
||||
"""
|
||||
Save parsed data to JSON and HDF5
|
||||
"""
|
||||
print("Saving data...")
|
||||
|
||||
# Save JSON metadata
|
||||
json_file = self.case_dir / "neural_field_data.json"
|
||||
with open(json_file, 'w') as f:
|
||||
# Convert numpy arrays to lists for JSON serialization
|
||||
json.dump(self.neural_field_data, f, indent=2, default=str)
|
||||
|
||||
# Save HDF5 for large arrays
|
||||
h5_file = self.case_dir / "neural_field_data.h5"
|
||||
with h5py.File(h5_file, 'w') as f:
|
||||
# Save mesh data
|
||||
mesh_grp = f.create_group('mesh')
|
||||
mesh_grp.create_dataset('node_coordinates',
|
||||
data=np.array(self.neural_field_data["mesh"]["nodes"]["coordinates"]))
|
||||
|
||||
# Save results
|
||||
if "results" in self.neural_field_data:
|
||||
results_grp = f.create_group('results')
|
||||
if "displacement" in self.neural_field_data["results"]:
|
||||
results_grp.create_dataset('displacement',
|
||||
data=np.array(self.neural_field_data["results"]["displacement"]["data"]))
|
||||
|
||||
print(f"Data saved to {json_file} and {h5_file}")
|
||||
|
||||
# ============================================================================
|
||||
# USAGE SCRIPT
|
||||
# ============================================================================
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main function to run the parser
|
||||
"""
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python neural_field_parser.py <case_directory>")
|
||||
sys.exit(1)
|
||||
|
||||
case_dir = sys.argv[1]
|
||||
|
||||
# Create parser
|
||||
parser = NastranToNeuralFieldParser(case_dir)
|
||||
|
||||
# Parse all data
|
||||
try:
|
||||
data = parser.parse_all()
|
||||
print("\nParsing successful!")
|
||||
print(f"Nodes: {data['mesh']['statistics']['n_nodes']}")
|
||||
print(f"Elements: {data['mesh']['statistics']['n_elements']}")
|
||||
print(f"Materials: {len(data['materials'])}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\nError during parsing: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Validation Script
|
||||
python"""
|
||||
validate_parsed_data.py
|
||||
Validates the parsed neural field data
|
||||
"""
|
||||
|
||||
import json
|
||||
import h5py
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
|
||||
class NeuralFieldDataValidator:
|
||||
"""
|
||||
Validates parsed data for completeness and consistency
|
||||
"""
|
||||
|
||||
def __init__(self, case_directory):
|
||||
self.case_dir = Path(case_directory)
|
||||
self.json_file = self.case_dir / "neural_field_data.json"
|
||||
self.h5_file = self.case_dir / "neural_field_data.h5"
|
||||
|
||||
def validate(self):
|
||||
"""
|
||||
Run all validation checks
|
||||
"""
|
||||
print("Starting validation...")
|
||||
|
||||
# Load data
|
||||
with open(self.json_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Check required fields
|
||||
required_fields = [
|
||||
"metadata", "mesh", "materials",
|
||||
"boundary_conditions", "loads", "results"
|
||||
]
|
||||
|
||||
for field in required_fields:
|
||||
if field not in data:
|
||||
print(f"❌ Missing required field: {field}")
|
||||
return False
|
||||
else:
|
||||
print(f"✅ Found {field}")
|
||||
|
||||
# Validate mesh
|
||||
n_nodes = data["mesh"]["statistics"]["n_nodes"]
|
||||
n_elements = data["mesh"]["statistics"]["n_elements"]
|
||||
|
||||
print(f"\nMesh Statistics:")
|
||||
print(f" Nodes: {n_nodes}")
|
||||
print(f" Elements: {n_elements}")
|
||||
|
||||
# Check results consistency
|
||||
if "displacement" in data["results"]:
|
||||
disp_nodes = len(data["results"]["displacement"]["node_ids"])
|
||||
if disp_nodes != n_nodes:
|
||||
print(f"⚠️ Displacement nodes ({disp_nodes}) != mesh nodes ({n_nodes})")
|
||||
|
||||
# Check HDF5 file
|
||||
with h5py.File(self.h5_file, 'r') as f:
|
||||
print(f"\nHDF5 Contents:")
|
||||
for key in f.keys():
|
||||
print(f" {key}: {list(f[key].keys())}")
|
||||
|
||||
print("\n✅ Validation complete!")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
validator = NeuralFieldDataValidator(sys.argv[1])
|
||||
validator.validate()
|
||||
|
||||
Step-by-Step Usage Instructions
|
||||
1. Prepare Your Analysis
|
||||
bash# In NX:
|
||||
1. Create geometry
|
||||
2. Generate mesh
|
||||
3. Apply materials (MAT1 cards)
|
||||
4. Apply constraints (SPC)
|
||||
5. Apply loads (FORCE, PLOAD4)
|
||||
6. Run SOL 101 (Linear Static)
|
||||
7. Request output: DISPLACEMENT=ALL, STRESS=ALL, STRAIN=ALL
|
||||
2. Organize Files
|
||||
bashmkdir training_case_001
|
||||
mkdir training_case_001/input
|
||||
mkdir training_case_001/output
|
||||
|
||||
# Copy files
|
||||
cp your_model.bdf training_case_001/input/model.bdf
|
||||
cp your_model.op2 training_case_001/output/model.op2
|
||||
cp your_model.f06 training_case_001/output/model.f06
|
||||
3. Run Parser
|
||||
bash# Install requirements
|
||||
pip install pyNastran numpy h5py
|
||||
|
||||
# Run parser
|
||||
python neural_field_parser.py training_case_001
|
||||
|
||||
# Validate
|
||||
python validate_parsed_data.py training_case_001
|
||||
4. Check Output
|
||||
You'll get:
|
||||
|
||||
neural_field_data.json - Complete metadata and structure
|
||||
neural_field_data.h5 - Large arrays (mesh, results)
|
||||
|
||||
|
||||
Automation Script for Multiple Cases
|
||||
python"""
|
||||
batch_parser.py
|
||||
Parse multiple cases automatically
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from neural_field_parser import NastranToNeuralFieldParser
|
||||
|
||||
def batch_parse(root_directory):
|
||||
"""
|
||||
Parse all cases in directory
|
||||
"""
|
||||
root = Path(root_directory)
|
||||
cases = [d for d in root.iterdir() if d.is_dir()]
|
||||
|
||||
results = []
|
||||
for case in cases:
|
||||
print(f"\nProcessing {case.name}...")
|
||||
try:
|
||||
parser = NastranToNeuralFieldParser(case)
|
||||
data = parser.parse_all()
|
||||
results.append({
|
||||
"case": case.name,
|
||||
"status": "success",
|
||||
"nodes": data["mesh"]["statistics"]["n_nodes"],
|
||||
"elements": data["mesh"]["statistics"]["n_elements"]
|
||||
})
|
||||
except Exception as e:
|
||||
results.append({
|
||||
"case": case.name,
|
||||
"status": "failed",
|
||||
"error": str(e)
|
||||
})
|
||||
|
||||
# Summary
|
||||
print("\n" + "="*50)
|
||||
print("BATCH PROCESSING COMPLETE")
|
||||
print("="*50)
|
||||
for r in results:
|
||||
status = "✅" if r["status"] == "success" else "❌"
|
||||
print(f"{status} {r['case']}: {r['status']}")
|
||||
|
||||
return results
|
||||
|
||||
if __name__ == "__main__":
|
||||
batch_parse("./training_data")
|
||||
|
||||
What to Add Manually
|
||||
Create a metadata.json in each case directory with design intent:
|
||||
json{
|
||||
"design_parameters": {
|
||||
"thickness": 2.5,
|
||||
"fillet_radius": 5.0,
|
||||
"rib_height": 15.0
|
||||
},
|
||||
"optimization_context": {
|
||||
"objectives": ["minimize_weight", "minimize_stress"],
|
||||
"constraints": ["max_displacement < 2mm"],
|
||||
"iteration": 42
|
||||
},
|
||||
"notes": "Baseline design with standard loading"
|
||||
}
|
||||
|
||||
Troubleshooting
|
||||
Common Issues:
|
||||
|
||||
"Can't find BDF nodes"
|
||||
|
||||
Make sure you're using .bdf or .dat, not .sim
|
||||
Check that mesh was exported to solver deck
|
||||
|
||||
|
||||
"OP2 has no results"
|
||||
|
||||
Ensure analysis completed successfully
|
||||
Check that you requested output (DISP=ALL, STRESS=ALL)
|
||||
|
||||
|
||||
"Memory error with large models"
|
||||
|
||||
Use HDF5 chunking for very large models
|
||||
Process in batches
|
||||
|
||||
|
||||
|
||||
This parser gives you everything you need to start training neural networks on your FEA data. The format is future-proof and will work with your automated generation pipeline!
|
||||
Reference in New Issue
Block a user