# EXT_02: Create Lifecycle Hook ## 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 |