feat: Add AtomizerField training data export and intelligent model discovery
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>
This commit is contained in:
172
optimization_engine/extractors/stiffness_calculator.py
Normal file
172
optimization_engine/extractors/stiffness_calculator.py
Normal file
@@ -0,0 +1,172 @@
|
||||
"""
|
||||
Generic Stiffness Calculator
|
||||
============================
|
||||
|
||||
Reusable calculator for structural stiffness from FEA results.
|
||||
Works with any structure: bracket, beam, plate, etc.
|
||||
|
||||
Stiffness (k) = Applied Force (F) / Displacement (δ)
|
||||
|
||||
Usage:
|
||||
calculator = StiffnessCalculator(
|
||||
field_file="export_field_dz.fld",
|
||||
op2_file="model.op2",
|
||||
force_component="fz",
|
||||
displacement_component="z"
|
||||
)
|
||||
results = calculator.calculate()
|
||||
stiffness = results['stiffness']
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
from .field_data_extractor import FieldDataExtractor
|
||||
from .op2_extractor import OP2Extractor
|
||||
|
||||
|
||||
class StiffnessCalculator:
|
||||
"""
|
||||
Generic stiffness calculator for structural analysis.
|
||||
|
||||
Can be used for:
|
||||
- Bracket stiffness (force/deflection)
|
||||
- Beam bending stiffness
|
||||
- Plate flexural stiffness
|
||||
- Any structure where k = F/δ
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
field_file: str,
|
||||
op2_file: str,
|
||||
force_component: str = "fz", # fx, fy, fz, total
|
||||
displacement_component: str = "z", # x, y, z
|
||||
displacement_aggregation: str = "max_abs",
|
||||
applied_force: Optional[float] = None, # If known, skip OP2 extraction
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
field_file: Path to displacement field file (.fld or .csv)
|
||||
op2_file: Path to OP2 file
|
||||
force_component: Force component to extract (fx, fy, fz, total)
|
||||
displacement_component: Displacement component (x, y, z)
|
||||
displacement_aggregation: How to aggregate displacement (max_abs, max, min)
|
||||
applied_force: Optional pre-known force value (skips OP2 extraction)
|
||||
"""
|
||||
self.field_file = Path(field_file)
|
||||
self.op2_file = Path(op2_file)
|
||||
self.force_component = force_component
|
||||
self.displacement_component = displacement_component
|
||||
self.displacement_aggregation = displacement_aggregation
|
||||
self.applied_force = applied_force
|
||||
|
||||
def calculate(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Calculate stiffness from FEA results.
|
||||
|
||||
Returns:
|
||||
dict: {
|
||||
'stiffness': k value (N/mm or appropriate units),
|
||||
'displacement': max displacement (mm),
|
||||
'force': applied force (N),
|
||||
'compliance': 1/k (mm/N),
|
||||
'units': unit description
|
||||
}
|
||||
"""
|
||||
# Extract displacement
|
||||
disp_extractor = FieldDataExtractor(
|
||||
field_file=str(self.field_file),
|
||||
result_column=f"{self.displacement_component}(mm)",
|
||||
aggregation=self.displacement_aggregation
|
||||
)
|
||||
disp_result = disp_extractor.extract()
|
||||
displacement = disp_result['value'] # mm
|
||||
|
||||
if displacement == 0:
|
||||
raise ValueError(f"Zero displacement found. Check field file: {self.field_file}")
|
||||
|
||||
# Extract force
|
||||
if self.applied_force is not None:
|
||||
force = self.applied_force
|
||||
else:
|
||||
op2_extractor = OP2Extractor(op2_file=str(self.op2_file))
|
||||
force_result = op2_extractor.extract_grid_point_forces(component=self.force_component)
|
||||
force = force_result['force'] # N (or mN, check units)
|
||||
|
||||
# Note: Forces in OP2 might be in milli-Newtons (mN)
|
||||
# Check f06 header for units and convert if needed
|
||||
# For now, assuming Newtons
|
||||
|
||||
if force == 0:
|
||||
raise ValueError(f"Zero force found. Check OP2 file: {self.op2_file}")
|
||||
|
||||
# Calculate stiffness: k = F / δ
|
||||
stiffness = force / displacement # N/mm
|
||||
|
||||
# Calculate compliance (inverse of stiffness)
|
||||
compliance = displacement / force # mm/N
|
||||
|
||||
return {
|
||||
'stiffness': stiffness,
|
||||
'displacement': displacement,
|
||||
'force': force,
|
||||
'compliance': compliance,
|
||||
'units': {
|
||||
'stiffness': 'N/mm',
|
||||
'displacement': 'mm',
|
||||
'force': 'N (verify from f06)',
|
||||
'compliance': 'mm/N'
|
||||
},
|
||||
'field_file': str(self.field_file),
|
||||
'op2_file': str(self.op2_file),
|
||||
'displacement_stats': disp_result['stats'],
|
||||
}
|
||||
|
||||
|
||||
def calculate_stiffness(
|
||||
field_file: str,
|
||||
op2_file: str,
|
||||
force_component: str = "fz",
|
||||
displacement_component: str = "z"
|
||||
) -> float:
|
||||
"""
|
||||
Convenience function to calculate stiffness.
|
||||
|
||||
Args:
|
||||
field_file: Path to displacement field file
|
||||
op2_file: Path to OP2 file
|
||||
force_component: Force component (fx, fy, fz, total)
|
||||
displacement_component: Displacement component (x, y, z)
|
||||
|
||||
Returns:
|
||||
Stiffness value (N/mm)
|
||||
"""
|
||||
calculator = StiffnessCalculator(
|
||||
field_file=field_file,
|
||||
op2_file=op2_file,
|
||||
force_component=force_component,
|
||||
displacement_component=displacement_component
|
||||
)
|
||||
result = calculator.calculate()
|
||||
return result['stiffness']
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Example usage
|
||||
import sys
|
||||
if len(sys.argv) > 2:
|
||||
field_file = sys.argv[1]
|
||||
op2_file = sys.argv[2]
|
||||
|
||||
calculator = StiffnessCalculator(
|
||||
field_file=field_file,
|
||||
op2_file=op2_file,
|
||||
force_component="fz",
|
||||
displacement_component="z"
|
||||
)
|
||||
|
||||
results = calculator.calculate()
|
||||
print(f"Stiffness: {results['stiffness']:.2f} N/mm")
|
||||
print(f"Displacement: {results['displacement']:.6f} mm")
|
||||
print(f"Force: {results['force']:.2f} N")
|
||||
print(f"Compliance: {results['compliance']:.6e} mm/N")
|
||||
Reference in New Issue
Block a user