feat: Add substudy system with live history tracking and workflow fixes
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>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""
|
||||
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]
|
||||
Reference in New Issue
Block a user