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