Files
Atomizer/docs/protocols/extensions/EXT_02_CREATE_HOOK.md

367 lines
8.8 KiB
Markdown
Raw Normal View History

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