Phase 3 implements automated OP2 extraction code generation using pyNastran documentation research. This completes the zero-manual-coding pipeline for FEA optimization workflows. Key Features: - PyNastranResearchAgent for automated OP2 code generation - Documentation research via WebFetch integration - 3 core extraction patterns (displacement, stress, force) - Knowledge base architecture for learned patterns - Successfully tested on real OP2 files Phase 2.9 Integration: - Updated HookGenerator with lifecycle hook generation - Added POST_CALCULATION hook point to hooks.py - Created post_calculation/ plugin directory - Generated hooks integrate seamlessly with HookManager New Files: - optimization_engine/pynastran_research_agent.py (600+ lines) - optimization_engine/hook_generator.py (800+ lines) - optimization_engine/inline_code_generator.py - optimization_engine/plugins/post_calculation/ - tests/test_lifecycle_hook_integration.py - docs/SESSION_SUMMARY_PHASE_3.md - docs/SESSION_SUMMARY_PHASE_2_9.md - docs/SESSION_SUMMARY_PHASE_2_8.md - docs/HOOK_ARCHITECTURE.md Modified Files: - README.md - Added Phase 3 completion status - optimization_engine/plugins/hooks.py - Added POST_CALCULATION hook Test Results: - Phase 3 research agent: PASSED - Real OP2 extraction: PASSED (max_disp=0.362mm) - Lifecycle hook integration: PASSED Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
57 lines
1.5 KiB
Python
57 lines
1.5 KiB
Python
"""
|
|
Extract displacement from bracket OP2
|
|
Auto-generated by Atomizer Phase 3 - pyNastran Research Agent
|
|
|
|
Pattern: displacement
|
|
Element Type: General
|
|
Result Type: displacement
|
|
API: model.displacements[subcase]
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from typing import Dict, Any
|
|
import numpy as np
|
|
from pyNastran.op2.op2 import OP2
|
|
|
|
|
|
def extract_displacement(op2_file: Path, subcase: int = 1):
|
|
"""Extract displacement results from OP2 file."""
|
|
from pyNastran.op2.op2 import OP2
|
|
import numpy as np
|
|
|
|
model = OP2()
|
|
model.read_op2(str(op2_file))
|
|
|
|
disp = model.displacements[subcase]
|
|
itime = 0 # static case
|
|
|
|
# Extract translation components
|
|
txyz = disp.data[itime, :, :3] # [tx, ty, tz]
|
|
|
|
# Calculate total displacement
|
|
total_disp = np.linalg.norm(txyz, axis=1)
|
|
max_disp = np.max(total_disp)
|
|
|
|
# Get node info
|
|
node_ids = [nid for (nid, grid_type) in disp.node_gridtype]
|
|
max_disp_node = node_ids[np.argmax(total_disp)]
|
|
|
|
return {
|
|
'max_displacement': float(max_disp),
|
|
'max_disp_node': int(max_disp_node),
|
|
'max_disp_x': float(np.max(np.abs(txyz[:, 0]))),
|
|
'max_disp_y': float(np.max(np.abs(txyz[:, 1]))),
|
|
'max_disp_z': float(np.max(np.abs(txyz[:, 2])))
|
|
}
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Example usage
|
|
import sys
|
|
if len(sys.argv) > 1:
|
|
op2_file = Path(sys.argv[1])
|
|
result = extract_displacement(op2_file)
|
|
print(f"Extraction result: {result}")
|
|
else:
|
|
print("Usage: python {sys.argv[0]} <op2_file>")
|