Files
Atomizer/optimization_engine/extractors/stiffness_calculator.py
Anto01 2b3573ec42 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>
2025-11-26 12:01:50 -05:00

173 lines
5.5 KiB
Python

"""
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")