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>
367 lines
8.8 KiB
Markdown
367 lines
8.8 KiB
Markdown
# EXT_02: Create Lifecycle Hook
|
|
|
|
<!--
|
|
PROTOCOL: Create Lifecycle Hook Plugin
|
|
LAYER: Extensions
|
|
VERSION: 1.0
|
|
STATUS: Active
|
|
LAST_UPDATED: 2025-12-05
|
|
PRIVILEGE: power_user
|
|
LOAD_WITH: []
|
|
-->
|
|
|
|
## Overview
|
|
|
|
This protocol guides you through creating lifecycle hooks that execute at specific points during optimization. Hooks enable custom logic injection without modifying core code.
|
|
|
|
**Privilege Required**: power_user or admin
|
|
|
|
---
|
|
|
|
## When to Use
|
|
|
|
| Trigger | Action |
|
|
|---------|--------|
|
|
| Need custom logic at specific point | Follow this protocol |
|
|
| "create hook", "callback" | Follow this protocol |
|
|
| Want to log/validate/modify at runtime | Follow this protocol |
|
|
|
|
---
|
|
|
|
## Quick Reference
|
|
|
|
**Hook Points Available**:
|
|
|
|
| Hook Point | When It Runs | Use Case |
|
|
|------------|--------------|----------|
|
|
| `pre_mesh` | Before meshing | Validate geometry |
|
|
| `post_mesh` | After meshing | Check mesh quality |
|
|
| `pre_solve` | Before solver | Log trial start |
|
|
| `post_solve` | After solver | Validate results |
|
|
| `post_extraction` | After extraction | Custom metrics |
|
|
| `post_calculation` | After objectives | Derived quantities |
|
|
| `custom_objective` | Custom objective | Complex objectives |
|
|
|
|
**Create in**: `optimization_engine/plugins/{hook_point}/`
|
|
|
|
---
|
|
|
|
## Step-by-Step Guide
|
|
|
|
### Step 1: Identify Hook Point
|
|
|
|
Choose the appropriate hook point:
|
|
|
|
```
|
|
Trial Flow:
|
|
│
|
|
├─► PRE_MESH → Validate model before meshing
|
|
│
|
|
├─► POST_MESH → Check mesh quality
|
|
│
|
|
├─► PRE_SOLVE → Log trial start, validate inputs
|
|
│
|
|
├─► POST_SOLVE → Check solve success, capture timing
|
|
│
|
|
├─► POST_EXTRACTION → Compute derived quantities
|
|
│
|
|
├─► POST_CALCULATION → Final validation, logging
|
|
│
|
|
└─► CUSTOM_OBJECTIVE → Custom objective functions
|
|
```
|
|
|
|
### Step 2: Create Hook File
|
|
|
|
Create `optimization_engine/plugins/{hook_point}/{hook_name}.py`:
|
|
|
|
```python
|
|
"""
|
|
{Hook Description}
|
|
|
|
Author: {Your Name}
|
|
Created: {Date}
|
|
Version: 1.0
|
|
Hook Point: {hook_point}
|
|
"""
|
|
|
|
from typing import Dict, Any
|
|
|
|
|
|
def {hook_name}_hook(context: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
{Description of what this hook does}.
|
|
|
|
Args:
|
|
context: Dictionary containing:
|
|
- trial_number: Current trial number
|
|
- design_params: Current design parameters
|
|
- results: Results so far (if post-extraction)
|
|
- config: Optimization config
|
|
- working_dir: Path to working directory
|
|
|
|
Returns:
|
|
Dictionary with computed values or modifications.
|
|
Return empty dict if no modifications needed.
|
|
|
|
Example:
|
|
>>> result = {hook_name}_hook({'trial_number': 1, ...})
|
|
>>> print(result)
|
|
{'{computed_key}': 123.45}
|
|
"""
|
|
# Access context
|
|
trial_num = context.get('trial_number')
|
|
design_params = context.get('design_params', {})
|
|
results = context.get('results', {})
|
|
|
|
# Your logic here
|
|
# ...
|
|
|
|
# Return computed values
|
|
return {
|
|
'{computed_key}': computed_value,
|
|
}
|
|
|
|
|
|
def register_hooks(hook_manager):
|
|
"""
|
|
Register this hook with the hook manager.
|
|
|
|
This function is called automatically when plugins are loaded.
|
|
|
|
Args:
|
|
hook_manager: The HookManager instance
|
|
"""
|
|
hook_manager.register_hook(
|
|
hook_point='{hook_point}',
|
|
function={hook_name}_hook,
|
|
name='{hook_name}_hook',
|
|
description='{Brief description}',
|
|
priority=100, # Lower = runs earlier
|
|
enabled=True
|
|
)
|
|
```
|
|
|
|
### Step 3: Test Hook
|
|
|
|
```python
|
|
# Test in isolation
|
|
from optimization_engine.plugins.{hook_point}.{hook_name} import {hook_name}_hook
|
|
|
|
test_context = {
|
|
'trial_number': 1,
|
|
'design_params': {'thickness': 5.0},
|
|
'results': {'max_stress': 200.0},
|
|
}
|
|
|
|
result = {hook_name}_hook(test_context)
|
|
print(result)
|
|
```
|
|
|
|
### Step 4: Enable Hook
|
|
|
|
Hooks are auto-discovered from the plugins directory. To verify:
|
|
|
|
```python
|
|
from optimization_engine.plugins.hook_manager import HookManager
|
|
|
|
manager = HookManager()
|
|
manager.discover_plugins()
|
|
print(manager.list_hooks())
|
|
```
|
|
|
|
---
|
|
|
|
## Hook Examples
|
|
|
|
### Example 1: Safety Factor Calculator (post_calculation)
|
|
|
|
```python
|
|
"""Calculate safety factor after stress extraction."""
|
|
|
|
def safety_factor_hook(context):
|
|
"""Calculate safety factor from stress results."""
|
|
results = context.get('results', {})
|
|
config = context.get('config', {})
|
|
|
|
max_stress = results.get('max_von_mises', 0)
|
|
yield_strength = config.get('material', {}).get('yield_strength', 250)
|
|
|
|
if max_stress > 0:
|
|
safety_factor = yield_strength / max_stress
|
|
else:
|
|
safety_factor = float('inf')
|
|
|
|
return {
|
|
'safety_factor': safety_factor,
|
|
'yield_strength': yield_strength,
|
|
}
|
|
|
|
|
|
def register_hooks(hook_manager):
|
|
hook_manager.register_hook(
|
|
hook_point='post_calculation',
|
|
function=safety_factor_hook,
|
|
name='safety_factor_hook',
|
|
description='Calculate safety factor from stress',
|
|
priority=100,
|
|
enabled=True
|
|
)
|
|
```
|
|
|
|
### Example 2: Trial Logger (pre_solve)
|
|
|
|
```python
|
|
"""Log trial information before solve."""
|
|
|
|
import json
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
|
|
def trial_logger_hook(context):
|
|
"""Log trial start information."""
|
|
trial_num = context.get('trial_number')
|
|
design_params = context.get('design_params', {})
|
|
working_dir = context.get('working_dir', Path('.'))
|
|
|
|
log_entry = {
|
|
'trial': trial_num,
|
|
'timestamp': datetime.now().isoformat(),
|
|
'params': design_params,
|
|
}
|
|
|
|
log_file = working_dir / 'trial_log.jsonl'
|
|
with open(log_file, 'a') as f:
|
|
f.write(json.dumps(log_entry) + '\n')
|
|
|
|
return {} # No modifications
|
|
|
|
|
|
def register_hooks(hook_manager):
|
|
hook_manager.register_hook(
|
|
hook_point='pre_solve',
|
|
function=trial_logger_hook,
|
|
name='trial_logger_hook',
|
|
description='Log trial parameters before solve',
|
|
priority=10, # Run early
|
|
enabled=True
|
|
)
|
|
```
|
|
|
|
### Example 3: Mesh Quality Check (post_mesh)
|
|
|
|
```python
|
|
"""Validate mesh quality after meshing."""
|
|
|
|
|
|
def mesh_quality_hook(context):
|
|
"""Check mesh quality metrics."""
|
|
mesh_file = context.get('mesh_file')
|
|
|
|
# Check quality metrics
|
|
quality_issues = []
|
|
|
|
# ... quality checks ...
|
|
|
|
if quality_issues:
|
|
context['warnings'] = context.get('warnings', []) + quality_issues
|
|
|
|
return {
|
|
'mesh_quality_passed': len(quality_issues) == 0,
|
|
'mesh_issues': quality_issues,
|
|
}
|
|
|
|
|
|
def register_hooks(hook_manager):
|
|
hook_manager.register_hook(
|
|
hook_point='post_mesh',
|
|
function=mesh_quality_hook,
|
|
name='mesh_quality_hook',
|
|
description='Validate mesh quality',
|
|
priority=50,
|
|
enabled=True
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Hook Context Reference
|
|
|
|
### Standard Context Keys
|
|
|
|
| Key | Type | Available At | Description |
|
|
|-----|------|--------------|-------------|
|
|
| `trial_number` | int | All | Current trial number |
|
|
| `design_params` | dict | All | Design parameter values |
|
|
| `config` | dict | All | Optimization config |
|
|
| `working_dir` | Path | All | Study working directory |
|
|
| `model_file` | Path | pre_mesh+ | NX model file path |
|
|
| `mesh_file` | Path | post_mesh+ | Mesh file path |
|
|
| `op2_file` | Path | post_solve+ | Results file path |
|
|
| `results` | dict | post_extraction+ | Extracted results |
|
|
| `objectives` | dict | post_calculation | Computed objectives |
|
|
|
|
### Priority Guidelines
|
|
|
|
| Priority Range | Use For |
|
|
|----------------|---------|
|
|
| 1-50 | Critical hooks that must run first |
|
|
| 50-100 | Standard hooks |
|
|
| 100-150 | Logging and monitoring |
|
|
| 150+ | Cleanup and finalization |
|
|
|
|
---
|
|
|
|
## Managing Hooks
|
|
|
|
### Enable/Disable at Runtime
|
|
|
|
```python
|
|
hook_manager.disable_hook('my_hook')
|
|
hook_manager.enable_hook('my_hook')
|
|
```
|
|
|
|
### Check Hook Status
|
|
|
|
```python
|
|
hooks = hook_manager.list_hooks()
|
|
for hook in hooks:
|
|
print(f"{hook['name']}: {'enabled' if hook['enabled'] else 'disabled'}")
|
|
```
|
|
|
|
### Hook Execution Order
|
|
|
|
Hooks at the same point run in priority order (lower first):
|
|
```
|
|
Priority 10: trial_logger_hook
|
|
Priority 50: mesh_quality_hook
|
|
Priority 100: safety_factor_hook
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
| Issue | Cause | Solution |
|
|
|-------|-------|----------|
|
|
| Hook not running | Not registered | Check `register_hooks` function |
|
|
| Wrong hook point | Misnamed directory | Check directory name matches hook point |
|
|
| Context missing key | Wrong hook point | Use appropriate hook point for data needed |
|
|
| Hook error crashes trial | Unhandled exception | Add try/except in hook |
|
|
|
|
---
|
|
|
|
## Cross-References
|
|
|
|
- **Related**: [EXT_01_CREATE_EXTRACTOR](./EXT_01_CREATE_EXTRACTOR.md)
|
|
- **System**: `optimization_engine/plugins/hook_manager.py`
|
|
- **Template**: `templates/hook_template.py`
|
|
|
|
---
|
|
|
|
## Version History
|
|
|
|
| Version | Date | Changes |
|
|
|---------|------|---------|
|
|
| 1.0 | 2025-12-05 | Initial release |
|