Files
Atomizer/docs/protocols/extensions/templates/extractor_template.py
Antoine 602560c46a feat: Add MLP surrogate with Turbo Mode for 100x faster optimization
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>
2025-12-06 20:01:59 -05:00

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
# =========================================