""" Extract {Physics Name} from FEA results. This is a template for creating new physics extractors. Copy this file to optimization_engine/extractors/extract_{physics}.py and customize for your specific physics extraction. Author: {Your Name} Created: {Date} Version: 1.0 """ from pathlib import Path from typing import Dict, Any, Optional, Union from pyNastran.op2.op2 import OP2 def extract_{physics}( op2_file: Union[str, Path], subcase: int = 1, # Add other parameters specific to your physics ) -> Dict[str, Any]: """ Extract {physics description} from OP2 file. Args: op2_file: Path to the OP2 results file subcase: Subcase number to extract (default: 1) # Document other parameters Returns: Dictionary containing: - '{main_result}': The primary result value ({unit}) - '{secondary_result}': Secondary result info - 'subcase': The subcase extracted - 'unit': Unit of the result Raises: FileNotFoundError: If OP2 file doesn't exist KeyError: If subcase not found in results ValueError: If result data is invalid Example: >>> result = extract_{physics}('model.op2', subcase=1) >>> print(result['{main_result}']) 123.45 >>> print(result['unit']) '{unit}' """ # Convert to Path for consistency op2_file = Path(op2_file) # Validate file exists if not op2_file.exists(): raise FileNotFoundError(f"OP2 file not found: {op2_file}") # Read OP2 file op2 = OP2() op2.read_op2(str(op2_file)) # ========================================= # CUSTOMIZE: Your extraction logic here # ========================================= # Example: Access displacement data # if subcase not in op2.displacements: # raise KeyError(f"Subcase {subcase} not found in displacement results") # data = op2.displacements[subcase] # Example: Access stress data # if subcase not in op2.cquad4_stress: # raise KeyError(f"Subcase {subcase} not found in stress results") # stress_data = op2.cquad4_stress[subcase] # Example: Process data # values = data.data # numpy array # max_value = values.max() # max_index = values.argmax() # ========================================= # Replace with your actual computation # ========================================= main_result = 0.0 # TODO: Compute actual value secondary_result = 0 # TODO: Compute actual value return { '{main_result}': main_result, '{secondary_result}': secondary_result, 'subcase': subcase, 'unit': '{unit}', } # Optional: Class-based extractor for complex cases class {Physics}Extractor: """ Class-based extractor for {physics} with state management. Use this pattern when: - Extraction requires multiple steps - You need to cache the OP2 data - Configuration is complex Example: >>> extractor = {Physics}Extractor('model.op2', config={'option': value}) >>> result = extractor.extract(subcase=1) >>> print(result) """ def __init__( self, op2_file: Union[str, Path], bdf_file: Optional[Union[str, Path]] = None, **config ): """ Initialize the extractor. Args: op2_file: Path to OP2 results file bdf_file: Optional path to BDF mesh file (for node coordinates) **config: Additional configuration options """ self.op2_file = Path(op2_file) self.bdf_file = Path(bdf_file) if bdf_file else None self.config = config self._op2 = None # Lazy-loaded def _load_op2(self) -> OP2: """Lazy load OP2 file (caches result).""" if self._op2 is None: self._op2 = OP2() self._op2.read_op2(str(self.op2_file)) return self._op2 def extract(self, subcase: int = 1) -> Dict[str, Any]: """ Extract results for given subcase. Args: subcase: Subcase number Returns: Dictionary with extraction results """ op2 = self._load_op2() # TODO: Implement your extraction logic # Use self.config for configuration options return { '{main_result}': 0.0, 'subcase': subcase, } def extract_all_subcases(self) -> Dict[int, Dict[str, Any]]: """ Extract results for all available subcases. Returns: Dictionary mapping subcase number to results """ op2 = self._load_op2() # TODO: Find available subcases # available_subcases = list(op2.displacements.keys()) results = {} # for sc in available_subcases: # results[sc] = self.extract(subcase=sc) return results # ========================================= # After creating your extractor: # 1. Add to optimization_engine/extractors/__init__.py: # from .extract_{physics} import extract_{physics} # __all__ = [..., 'extract_{physics}'] # # 2. Update docs/protocols/system/SYS_12_EXTRACTOR_LIBRARY.md # - Add to Quick Reference table # - Add detailed section with example # # 3. Create test file: tests/test_extract_{physics}.py # =========================================