feat: Complete Phase 3 - pyNastran Documentation Integration
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>
This commit is contained in:
75
generated_hooks/hook_compare_min_to_avg_ratio.py
Normal file
75
generated_hooks/hook_compare_min_to_avg_ratio.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""
|
||||
Comparison Hook
|
||||
Auto-generated by Atomizer Phase 2.9
|
||||
|
||||
Compare min force to average
|
||||
|
||||
Operation: ratio
|
||||
Formula: min_to_avg_ratio = min_force / avg_force
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def compare_ratio(min_force, avg_force):
|
||||
"""
|
||||
Compare values using ratio.
|
||||
|
||||
Args:
|
||||
min_force: float
|
||||
avg_force: float
|
||||
|
||||
Returns:
|
||||
float: Comparison result
|
||||
"""
|
||||
result = min_force / avg_force
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for hook execution."""
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python {} <input_file.json>".format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
input_file = Path(sys.argv[1])
|
||||
|
||||
# Read inputs
|
||||
with open(input_file, 'r') as f:
|
||||
inputs = json.load(f)
|
||||
|
||||
# Extract required inputs
|
||||
min_force = inputs.get("min_force")
|
||||
if min_force is None:
|
||||
print(f"Error: Required input 'min_force' not found")
|
||||
sys.exit(1)
|
||||
avg_force = inputs.get("avg_force")
|
||||
if avg_force is None:
|
||||
print(f"Error: Required input 'avg_force' not found")
|
||||
sys.exit(1)
|
||||
|
||||
# Calculate comparison
|
||||
result = compare_ratio(min_force, avg_force)
|
||||
|
||||
# Write output
|
||||
output_file = input_file.parent / "min_to_avg_ratio.json"
|
||||
output = {
|
||||
"min_to_avg_ratio": result,
|
||||
"operation": "ratio",
|
||||
"formula": "min_force / avg_force",
|
||||
"inputs_used": {"min_force": min_force, "avg_force": avg_force}
|
||||
}
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
json.dump(output, f, indent=2)
|
||||
|
||||
print(f"min_to_avg_ratio = {result:.6f}")
|
||||
print(f"Result saved to: {output_file}")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
83
generated_hooks/hook_constraint_yield_constraint.py
Normal file
83
generated_hooks/hook_constraint_yield_constraint.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
Constraint Check Hook
|
||||
Auto-generated by Atomizer Phase 2.9
|
||||
|
||||
Check if stress is below yield
|
||||
|
||||
Constraint: max_stress / yield_strength
|
||||
Threshold: 1.0
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def check_yield_constraint(max_stress, yield_strength):
|
||||
"""
|
||||
Check constraint condition.
|
||||
|
||||
Args:
|
||||
max_stress: float
|
||||
yield_strength: float
|
||||
|
||||
Returns:
|
||||
tuple: (satisfied: bool, value: float, violation: float)
|
||||
"""
|
||||
value = max_stress / yield_strength
|
||||
satisfied = value <= 1.0
|
||||
violation = max(0.0, value - 1.0)
|
||||
|
||||
return satisfied, value, violation
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for hook execution."""
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python {} <input_file.json>".format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
input_file = Path(sys.argv[1])
|
||||
|
||||
# Read inputs
|
||||
with open(input_file, 'r') as f:
|
||||
inputs = json.load(f)
|
||||
|
||||
# Extract required inputs
|
||||
max_stress = inputs.get("max_stress")
|
||||
if max_stress is None:
|
||||
print(f"Error: Required input 'max_stress' not found")
|
||||
sys.exit(1)
|
||||
yield_strength = inputs.get("yield_strength")
|
||||
if yield_strength is None:
|
||||
print(f"Error: Required input 'yield_strength' not found")
|
||||
sys.exit(1)
|
||||
|
||||
# Check constraint
|
||||
satisfied, value, violation = check_yield_constraint(max_stress, yield_strength)
|
||||
|
||||
# Write output
|
||||
output_file = input_file.parent / "yield_constraint_check.json"
|
||||
output = {
|
||||
"constraint_name": "yield_constraint",
|
||||
"satisfied": satisfied,
|
||||
"value": value,
|
||||
"threshold": 1.0,
|
||||
"violation": violation,
|
||||
"inputs_used": {"max_stress": max_stress, "yield_strength": yield_strength}
|
||||
}
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
json.dump(output, f, indent=2)
|
||||
|
||||
status = "SATISFIED" if satisfied else "VIOLATED"
|
||||
print(f"Constraint {status}: {value:.6f} (threshold: 1.0)")
|
||||
if not satisfied:
|
||||
print(f"Violation: {violation:.6f}")
|
||||
print(f"Result saved to: {output_file}")
|
||||
|
||||
return value
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
74
generated_hooks/hook_custom_safety_factor.py
Normal file
74
generated_hooks/hook_custom_safety_factor.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""
|
||||
Custom Formula Hook
|
||||
Auto-generated by Atomizer Phase 2.9
|
||||
|
||||
Calculate safety factor
|
||||
|
||||
Formula: safety_factor = yield_strength / max_stress
|
||||
Inputs: max_stress, yield_strength
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def calculate_safety_factor(max_stress, yield_strength):
|
||||
"""
|
||||
Calculate custom metric using formula.
|
||||
|
||||
Args:
|
||||
max_stress: float
|
||||
yield_strength: float
|
||||
|
||||
Returns:
|
||||
float: safety_factor
|
||||
"""
|
||||
safety_factor = yield_strength / max_stress
|
||||
return safety_factor
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for hook execution."""
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python {} <input_file.json>".format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
input_file = Path(sys.argv[1])
|
||||
|
||||
# Read inputs
|
||||
with open(input_file, 'r') as f:
|
||||
inputs = json.load(f)
|
||||
|
||||
# Extract required inputs
|
||||
max_stress = inputs.get("max_stress")
|
||||
if max_stress is None:
|
||||
print(f"Error: Required input 'max_stress' not found")
|
||||
sys.exit(1)
|
||||
yield_strength = inputs.get("yield_strength")
|
||||
if yield_strength is None:
|
||||
print(f"Error: Required input 'yield_strength' not found")
|
||||
sys.exit(1)
|
||||
|
||||
# Calculate result
|
||||
result = calculate_safety_factor(max_stress, yield_strength)
|
||||
|
||||
# Write output
|
||||
output_file = input_file.parent / "safety_factor_result.json"
|
||||
output = {
|
||||
"safety_factor": result,
|
||||
"formula": "yield_strength / max_stress",
|
||||
"inputs_used": {"max_stress": max_stress, "yield_strength": yield_strength}
|
||||
}
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
json.dump(output, f, indent=2)
|
||||
|
||||
print(f"safety_factor = {result:.6f}")
|
||||
print(f"Result saved to: {output_file}")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
54
generated_hooks/hook_registry.json
Normal file
54
generated_hooks/hook_registry.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"name": "hook_weighted_objective_norm_stress_norm_disp.py",
|
||||
"type": "weighted_objective",
|
||||
"description": "Combine normalized stress (70%) and displacement (30%)",
|
||||
"inputs": [
|
||||
"norm_stress",
|
||||
"norm_disp"
|
||||
],
|
||||
"outputs": [
|
||||
"weighted_objective"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hook_custom_safety_factor.py",
|
||||
"type": "custom_formula",
|
||||
"description": "Calculate safety factor",
|
||||
"inputs": [
|
||||
"max_stress",
|
||||
"yield_strength"
|
||||
],
|
||||
"outputs": [
|
||||
"safety_factor"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hook_compare_min_to_avg_ratio.py",
|
||||
"type": "comparison",
|
||||
"description": "Compare min force to average",
|
||||
"inputs": [
|
||||
"min_force",
|
||||
"avg_force"
|
||||
],
|
||||
"outputs": [
|
||||
"min_to_avg_ratio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hook_constraint_yield_constraint.py",
|
||||
"type": "constraint_check",
|
||||
"description": "Check if stress is below yield",
|
||||
"inputs": [
|
||||
"max_stress",
|
||||
"yield_strength"
|
||||
],
|
||||
"outputs": [
|
||||
"yield_constraint",
|
||||
"yield_constraint_satisfied",
|
||||
"yield_constraint_violation"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Weighted Objective Function Hook
|
||||
Auto-generated by Atomizer Phase 2.9
|
||||
|
||||
Combine normalized stress (70%) and displacement (30%)
|
||||
|
||||
Inputs: norm_stress, norm_disp
|
||||
Weights: 0.7, 0.3
|
||||
Formula: 0.7 * norm_stress + 0.3 * norm_disp
|
||||
Objective: minimize
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def weighted_objective(norm_stress, norm_disp):
|
||||
"""
|
||||
Calculate weighted objective from multiple inputs.
|
||||
|
||||
Args:
|
||||
norm_stress: float
|
||||
norm_disp: float
|
||||
|
||||
Returns:
|
||||
float: Weighted objective value
|
||||
"""
|
||||
result = 0.7 * norm_stress + 0.3 * norm_disp
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for hook execution.
|
||||
Reads inputs from JSON file, calculates objective, writes output.
|
||||
"""
|
||||
# Parse command line arguments
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python {} <input_file.json>".format(sys.argv[0]))
|
||||
sys.exit(1)
|
||||
|
||||
input_file = Path(sys.argv[1])
|
||||
|
||||
# Read inputs
|
||||
if not input_file.exists():
|
||||
print(f"Error: Input file {input_file} not found")
|
||||
sys.exit(1)
|
||||
|
||||
with open(input_file, 'r') as f:
|
||||
inputs = json.load(f)
|
||||
|
||||
# Extract required inputs
|
||||
norm_stress = inputs.get("norm_stress")
|
||||
if norm_stress is None:
|
||||
print(f"Error: Required input 'norm_stress' not found")
|
||||
sys.exit(1)
|
||||
norm_disp = inputs.get("norm_disp")
|
||||
if norm_disp is None:
|
||||
print(f"Error: Required input 'norm_disp' not found")
|
||||
sys.exit(1)
|
||||
|
||||
# Calculate weighted objective
|
||||
result = weighted_objective(norm_stress, norm_disp)
|
||||
|
||||
# Write output
|
||||
output_file = input_file.parent / "weighted_objective_result.json"
|
||||
output = {
|
||||
"weighted_objective": result,
|
||||
"objective_type": "minimize",
|
||||
"inputs_used": {"norm_stress": norm_stress, "norm_disp": norm_disp},
|
||||
"formula": "0.7 * norm_stress + 0.3 * norm_disp"
|
||||
}
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
json.dump(output, f, indent=2)
|
||||
|
||||
print(f"Weighted objective calculated: {result:.6f}")
|
||||
print(f"Result saved to: {output_file}")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
4
generated_hooks/test_input.json
Normal file
4
generated_hooks/test_input.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"norm_stress": 0.75,
|
||||
"norm_disp": 0.64
|
||||
}
|
||||
9
generated_hooks/weighted_objective_result.json
Normal file
9
generated_hooks/weighted_objective_result.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"weighted_objective": 0.7169999999999999,
|
||||
"objective_type": "minimize",
|
||||
"inputs_used": {
|
||||
"norm_stress": 0.75,
|
||||
"norm_disp": 0.64
|
||||
},
|
||||
"formula": "0.7 * norm_stress + 0.3 * norm_disp"
|
||||
}
|
||||
Reference in New Issue
Block a user