Neural Acceleration (MLP Surrogate): - Add run_nn_optimization.py with hybrid FEA/NN workflow - MLP architecture: 4-layer (64->128->128->64) with BatchNorm/Dropout - Three workflow modes: - --all: Sequential export->train->optimize->validate - --hybrid-loop: Iterative Train->NN->Validate->Retrain cycle - --turbo: Aggressive single-best validation (RECOMMENDED) - Turbo mode: 5000 NN trials + 50 FEA validations in ~12 minutes - Separate nn_study.db to avoid overloading dashboard Performance Results (bracket_pareto_3obj study): - NN prediction errors: mass 1-5%, stress 1-4%, stiffness 5-15% - Found minimum mass designs at boundary (angle~30deg, thick~30mm) - 100x speedup vs pure FEA exploration Protocol Operating System: - Add .claude/skills/ with Bootstrap, Cheatsheet, Context Loader - Add docs/protocols/ with operations (OP_01-06) and system (SYS_10-14) - Update SYS_14_NEURAL_ACCELERATION.md with MLP Turbo Mode docs NX Automation: - Add optimization_engine/hooks/ for NX CAD/CAE automation - Add study_wizard.py for guided study creation - Fix FEM mesh update: load idealized part before UpdateFemodel() New Study: - bracket_pareto_3obj: 3-objective Pareto (mass, stress, stiffness) - 167 FEA trials + 5000 NN trials completed - Demonstrates full hybrid workflow 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
187 lines
5.2 KiB
Python
187 lines
5.2 KiB
Python
"""
|
|
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
|
|
# =========================================
|