Major Features: - Hierarchical substudy system (like NX Solutions/Subcases) * Shared model files across all substudies * Independent configuration per substudy * Continuation support from previous substudies * Real-time incremental history updates - Live history tracking with optimization_history_incremental.json - Complete bracket_displacement_maximizing study with substudy examples Core Fixes: - Fixed expression update workflow to pass design_vars through simulation_runner * Restored working NX journal expression update mechanism * OP2 timestamp verification instead of file deletion * Resolved issue where all trials returned identical objective values - Fixed LLMOptimizationRunner to pass design variables to simulation runner - Enhanced NXSolver with timestamp-based file regeneration verification New Components: - optimization_engine/llm_optimization_runner.py - LLM-driven optimization runner - optimization_engine/optimization_setup_wizard.py - Phase 3.3 setup wizard - studies/bracket_displacement_maximizing/ - Complete substudy example * run_substudy.py - Substudy runner with continuation * run_optimization.py - Standalone optimization runner * config/substudy_template.json - Template for new substudies * substudies/coarse_exploration/ - 20-trial coarse search * substudies/fine_tuning/ - 50-trial refinement (continuation example) * SUBSTUDIES_README.md - Complete substudy documentation Technical Improvements: - Incremental history saving after each trial (optimization_history_incremental.json) - Expression update workflow: .prt update → NX journal receives values → geometry update → FEM update → solve - Trial indexing fix in substudy result saving - Updated README with substudy system documentation Testing: - Successfully ran 20-trial coarse_exploration substudy - Verified different objective values across trials (workflow fix validated) - Confirmed live history updates in real-time - Tested shared model file usage across substudies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
91 lines
2.9 KiB
Python
91 lines
2.9 KiB
Python
"""
|
|
Safety Factor Constraint Hook - Manual Implementation
|
|
|
|
This hook enforces a minimum safety factor constraint on stress.
|
|
If safety_factor < minimum required, the objective is heavily penalized.
|
|
|
|
Safety Factor = Yield Strength / Max Stress
|
|
|
|
For Aluminum 6061-T6:
|
|
- Yield Strength: 276 MPa
|
|
- Required Safety Factor: 4.0
|
|
- Allowable Stress: 69 MPa
|
|
|
|
Author: Atomizer Development Team
|
|
Version: 1.0
|
|
"""
|
|
|
|
from optimization_engine.plugins.hooks import Hook, HookPoint
|
|
|
|
|
|
def safety_factor_constraint_hook(context: dict) -> dict:
|
|
"""
|
|
Enforce safety factor constraint on optimization.
|
|
|
|
This hook checks if the calculated safety factor meets the minimum requirement.
|
|
If violated, it adds a large penalty to the objective to guide optimization
|
|
away from unsafe designs.
|
|
|
|
Args:
|
|
context: Dict containing:
|
|
- calculations: Dict with 'safety_factor' value
|
|
- results: Dict with stress results
|
|
|
|
Returns:
|
|
Dict with:
|
|
- safety_factor_satisfied: bool
|
|
- safety_factor_violation: float (0 if satisfied, penalty otherwise)
|
|
- constrained_objective: float (original or penalized objective)
|
|
"""
|
|
calculations = context.get('calculations', {})
|
|
|
|
# Get safety factor from calculations
|
|
safety_factor = calculations.get('safety_factor', 0.0)
|
|
|
|
# Get objective (negative displacement to maximize)
|
|
neg_displacement = calculations.get('neg_displacement', 0.0)
|
|
|
|
# Required minimum safety factor
|
|
min_safety_factor = 4.0
|
|
|
|
# Check constraint
|
|
satisfied = safety_factor >= min_safety_factor
|
|
|
|
# Calculate violation (how much we're under the limit)
|
|
violation = max(0.0, min_safety_factor - safety_factor)
|
|
|
|
# Apply penalty if constraint violated
|
|
if not satisfied:
|
|
# Heavy penalty: add large value to objective (we're minimizing)
|
|
# Penalty scales with violation severity
|
|
penalty = 1000.0 * violation
|
|
constrained_objective = neg_displacement + penalty
|
|
|
|
print(f" [CONSTRAINT VIOLATED] Safety factor {safety_factor:.2f} < {min_safety_factor}")
|
|
print(f" [PENALTY APPLIED] Adding {penalty:.2f} to objective")
|
|
else:
|
|
constrained_objective = neg_displacement
|
|
print(f" [CONSTRAINT SATISFIED] Safety factor {safety_factor:.2f} >= {min_safety_factor}")
|
|
|
|
return {
|
|
'safety_factor_satisfied': satisfied,
|
|
'safety_factor_violation': violation,
|
|
'constrained_objective': constrained_objective,
|
|
'objective': constrained_objective # This becomes the final objective
|
|
}
|
|
|
|
|
|
# Register hook with plugin system
|
|
hook = Hook(
|
|
name="safety_factor_constraint",
|
|
hook_point=HookPoint.POST_CALCULATION,
|
|
function=safety_factor_constraint_hook,
|
|
enabled=True,
|
|
description="Enforce minimum safety factor constraint with penalty"
|
|
)
|
|
|
|
|
|
def register_hooks(hook_manager):
|
|
"""Register hooks with the plugin system."""
|
|
return [hook]
|