Major additions: - Training data export system for AtomizerField neural network training - Bracket stiffness optimization study with 50+ training samples - Intelligent NX model discovery (auto-detect solutions, expressions, mesh) - Result extractors module for displacement, stress, frequency, mass - User-generated NX journals for advanced workflows - Archive structure for legacy scripts and test outputs - Protocol documentation and dashboard launcher 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
106 lines
3.4 KiB
Python
106 lines
3.4 KiB
Python
"""
|
|
Extract maximum von Mises stress from structural analysis
|
|
Auto-generated by Atomizer Phase 3 - pyNastran Research Agent
|
|
|
|
Pattern: solid_stress
|
|
Element Type: CTETRA
|
|
Result Type: stress
|
|
API: model.ctetra_stress[subcase] or model.chexa_stress[subcase]
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from typing import Dict, Any
|
|
import numpy as np
|
|
from pyNastran.op2.op2 import OP2
|
|
|
|
|
|
def extract_solid_stress(op2_file: Path, subcase: int = 1, element_type: str = 'ctetra'):
|
|
"""Extract stress from solid elements."""
|
|
from pyNastran.op2.op2 import OP2
|
|
import numpy as np
|
|
|
|
model = OP2()
|
|
model.read_op2(str(op2_file))
|
|
|
|
# Get stress object for element type
|
|
# Different element types have different stress attributes
|
|
stress_attr_map = {
|
|
'ctetra': 'ctetra_stress',
|
|
'chexa': 'chexa_stress',
|
|
'cquad4': 'cquad4_stress',
|
|
'ctria3': 'ctria3_stress'
|
|
}
|
|
|
|
stress_attr = stress_attr_map.get(element_type.lower())
|
|
if not stress_attr:
|
|
raise ValueError(f"Unknown element type: {element_type}")
|
|
|
|
# Access stress through op2_results container
|
|
# pyNastran structure: model.op2_results.stress.cquad4_stress[subcase]
|
|
stress_dict = None
|
|
|
|
if hasattr(model, 'op2_results') and hasattr(model.op2_results, 'stress'):
|
|
stress_container = model.op2_results.stress
|
|
if hasattr(stress_container, stress_attr):
|
|
stress_dict = getattr(stress_container, stress_attr)
|
|
|
|
if stress_dict is None:
|
|
raise ValueError(f"No {element_type} stress results in OP2. Available attributes: {[a for a in dir(model) if 'stress' in a.lower()]}")
|
|
|
|
# stress_dict is a dictionary with subcase IDs as keys
|
|
available_subcases = list(stress_dict.keys())
|
|
if not available_subcases:
|
|
raise ValueError(f"No stress data found in OP2 file")
|
|
|
|
# Use the specified subcase or first available
|
|
if subcase in available_subcases:
|
|
actual_subcase = subcase
|
|
else:
|
|
actual_subcase = available_subcases[0]
|
|
|
|
stress = stress_dict[actual_subcase]
|
|
|
|
itime = 0
|
|
|
|
# Extract von Mises if available
|
|
if stress.is_von_mises: # Property, not method
|
|
# Different element types have von Mises at different column indices
|
|
# Shell elements (CQUAD4, CTRIA3): 8 columns, von Mises at column 7
|
|
# Solid elements (CTETRA, CHEXA): 10 columns, von Mises at column 9
|
|
ncols = stress.data.shape[2]
|
|
|
|
if ncols == 8:
|
|
# Shell elements - von Mises is last column
|
|
von_mises_col = 7
|
|
elif ncols >= 10:
|
|
# Solid elements - von Mises is column 9
|
|
von_mises_col = 9
|
|
else:
|
|
# Unknown format, try last column
|
|
von_mises_col = ncols - 1
|
|
|
|
von_mises = stress.data[itime, :, von_mises_col]
|
|
max_stress = float(np.max(von_mises))
|
|
|
|
# Get element info
|
|
element_ids = [eid for (eid, node) in stress.element_node]
|
|
max_stress_elem = element_ids[np.argmax(von_mises)]
|
|
|
|
return {
|
|
'max_von_mises': max_stress,
|
|
'max_stress_element': int(max_stress_elem)
|
|
}
|
|
else:
|
|
raise ValueError("von Mises stress not available")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Example usage
|
|
import sys
|
|
if len(sys.argv) > 1:
|
|
op2_file = Path(sys.argv[1])
|
|
result = extract_solid_stress(op2_file)
|
|
print(f"Extraction result: {result}")
|
|
else:
|
|
print("Usage: python {sys.argv[0]} <op2_file>")
|