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:
122
atomizer-field/check_op2_units.py
Normal file
122
atomizer-field/check_op2_units.py
Normal file
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
Check actual units from pyNastran OP2 reader
|
||||
"""
|
||||
from pyNastran.op2.op2 import OP2
|
||||
import numpy as np
|
||||
|
||||
print("="*60)
|
||||
print("CHECKING OP2 FILE UNITS")
|
||||
print("="*60)
|
||||
|
||||
# Load OP2 file
|
||||
op2_file = 'test_case_beam/output/model.op2'
|
||||
print(f"\nLoading: {op2_file}")
|
||||
|
||||
op2 = OP2()
|
||||
op2.read_op2(op2_file)
|
||||
|
||||
print("\n1. DISPLACEMENT DATA:")
|
||||
print("-"*60)
|
||||
if hasattr(op2, 'displacements') and op2.displacements:
|
||||
for subcase_id, disp in op2.displacements.items():
|
||||
print(f" Subcase {subcase_id}:")
|
||||
print(f" Data shape: {disp.data.shape}")
|
||||
print(f" Max displacement (all DOF): {np.max(np.abs(disp.data)):.6f}")
|
||||
print(f" Translation max (DOF 1-3): {np.max(np.abs(disp.data[0, :, :3])):.6f}")
|
||||
|
||||
# Check if pyNastran has unit info
|
||||
if hasattr(disp, 'units'):
|
||||
print(f" Units: {disp.units}")
|
||||
else:
|
||||
print(f" Units: Not specified by pyNastran")
|
||||
|
||||
print("\n2. STRESS DATA:")
|
||||
print("-"*60)
|
||||
# Try new API first
|
||||
stress_dict = None
|
||||
if hasattr(op2, 'op2_results') and hasattr(op2.op2_results, 'stress'):
|
||||
if hasattr(op2.op2_results.stress, 'cquad4_stress'):
|
||||
stress_dict = op2.op2_results.stress.cquad4_stress
|
||||
elif hasattr(op2, 'cquad4_stress'):
|
||||
try:
|
||||
stress_dict = op2.cquad4_stress
|
||||
except:
|
||||
stress_dict = None
|
||||
|
||||
if stress_dict:
|
||||
for subcase_id, stress in stress_dict.items():
|
||||
print(f" Subcase {subcase_id}:")
|
||||
print(f" Data shape: {stress.data.shape}")
|
||||
|
||||
# Get von Mises stress (last column)
|
||||
von_mises = stress.data[0, :, -1]
|
||||
print(f" Von Mises min: {np.min(von_mises):.2e}")
|
||||
print(f" Von Mises max: {np.max(von_mises):.2e}")
|
||||
print(f" Von Mises mean: {np.mean(von_mises):.2e}")
|
||||
|
||||
# Check if pyNastran has unit info
|
||||
if hasattr(stress, 'units'):
|
||||
print(f" Units: {stress.units}")
|
||||
else:
|
||||
print(f" Units: Not specified by pyNastran")
|
||||
|
||||
# Check data type names
|
||||
if hasattr(stress, 'data_names'):
|
||||
print(f" Data names: {stress.data_names}")
|
||||
else:
|
||||
print(" No CQUAD4 stress data found")
|
||||
|
||||
print("\n3. REACTION FORCES:")
|
||||
print("-"*60)
|
||||
if hasattr(op2, 'grid_point_forces') and op2.grid_point_forces:
|
||||
for subcase_id, forces in op2.grid_point_forces.items():
|
||||
print(f" Subcase {subcase_id}:")
|
||||
print(f" Data shape: {forces.data.shape}")
|
||||
print(f" Max force: {np.max(np.abs(forces.data)):.2e}")
|
||||
|
||||
if hasattr(forces, 'units'):
|
||||
print(f" Units: {forces.units}")
|
||||
else:
|
||||
print(f" Units: Not specified by pyNastran")
|
||||
|
||||
print("\n4. BDF FILE UNITS:")
|
||||
print("-"*60)
|
||||
# Try to read BDF to check units
|
||||
from pyNastran.bdf.bdf import BDF
|
||||
bdf_file = 'test_case_beam/input/model.bdf'
|
||||
print(f"Loading: {bdf_file}")
|
||||
|
||||
bdf = BDF()
|
||||
bdf.read_bdf(bdf_file)
|
||||
|
||||
# Check for PARAM cards that define units
|
||||
if hasattr(bdf, 'params') and bdf.params:
|
||||
print(" PARAM cards found:")
|
||||
for param_name, param in bdf.params.items():
|
||||
if 'UNIT' in param_name.upper() or 'WTMASS' in param_name.upper():
|
||||
print(f" {param_name}: {param}")
|
||||
|
||||
# Check material properties (can infer units from magnitude)
|
||||
if hasattr(bdf, 'materials') and bdf.materials:
|
||||
print("\n Material properties (first material):")
|
||||
mat = list(bdf.materials.values())[0]
|
||||
print(f" Type: {mat.type}")
|
||||
if hasattr(mat, 'e') and mat.e:
|
||||
print(f" Young's modulus E: {mat.e:.2e}")
|
||||
print(f" If E~200,000 then units are MPa (steel)")
|
||||
print(f" If E~200,000,000 then units are Pa (steel)")
|
||||
print(f" If E~29,000,000 then units are psi (steel)")
|
||||
|
||||
print("\n5. NASTRAN UNIT SYSTEM ANALYSIS:")
|
||||
print("-"*60)
|
||||
print(" Common Nastran unit systems:")
|
||||
print(" - SI: m, kg, N, Pa, s")
|
||||
print(" - mm-tonne-N: mm, tonne, N, MPa, s")
|
||||
print(" - mm-kg-N: mm, kg, N, MPa, s")
|
||||
print(" - in-lb-s: in, lb, lbf, psi, s")
|
||||
print("")
|
||||
print(" Our metadata claims: mm, kg, N, MPa")
|
||||
print(" But pyNastran might return stress in Pa (base SI)")
|
||||
print(" This would explain the 1000× error!")
|
||||
|
||||
print("\n" + "="*60)
|
||||
Reference in New Issue
Block a user