Major additions: - Training data export system for AtomizerField neural network training - Bracket stiffness optimization study with 50+ training samples - Intelligent NX model discovery (auto-detect solutions, expressions, mesh) - Result extractors module for displacement, stress, frequency, mass - User-generated NX journals for advanced workflows - Archive structure for legacy scripts and test outputs - Protocol documentation and dashboard launcher 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
598 lines
18 KiB
Python
598 lines
18 KiB
Python
"""
|
|
Create a new circular plate frequency tuning study using Protocol 10.
|
|
|
|
This script creates a complete study configured for intelligent multi-strategy
|
|
optimization (IMSO) to test the self-tuning framework.
|
|
"""
|
|
|
|
import json
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
# Study configuration
|
|
STUDY_NAME = "circular_plate_frequency_tuning_intelligent_optimizer"
|
|
BASE_DIR = Path(__file__).parent
|
|
STUDIES_DIR = BASE_DIR / "studies"
|
|
STUDY_DIR = STUDIES_DIR / STUDY_NAME
|
|
|
|
# Source model files (copy from examples)
|
|
SOURCE_MODEL_DIR = BASE_DIR / "examples" / "Models" / "Circular Plate"
|
|
|
|
def create_study_structure():
|
|
"""Create complete study directory structure."""
|
|
print(f"Creating study: {STUDY_NAME}")
|
|
|
|
# Create directories
|
|
setup_dir = STUDY_DIR / "1_setup"
|
|
model_dir = setup_dir / "model"
|
|
results_dir = STUDY_DIR / "2_results"
|
|
reports_dir = STUDY_DIR / "3_reports"
|
|
|
|
for directory in [setup_dir, model_dir, results_dir, reports_dir]:
|
|
directory.mkdir(parents=True, exist_ok=True)
|
|
print(f" Created: {directory.relative_to(BASE_DIR)}")
|
|
|
|
return setup_dir, model_dir, results_dir, reports_dir
|
|
|
|
|
|
def copy_model_files(model_dir):
|
|
"""Copy model files from examples."""
|
|
print("\nCopying model files...")
|
|
|
|
model_files = [
|
|
"Circular_Plate.prt",
|
|
"Circular_Plate_sim1.sim",
|
|
"Circular_Plate_fem1.fem",
|
|
"Circular_Plate_fem1_i.prt"
|
|
]
|
|
|
|
for filename in model_files:
|
|
source = SOURCE_MODEL_DIR / filename
|
|
dest = model_dir / filename
|
|
|
|
if source.exists():
|
|
shutil.copy2(source, dest)
|
|
print(f" Copied: {filename}")
|
|
else:
|
|
print(f" WARNING: Source not found: {filename}")
|
|
|
|
return list(model_dir.glob("*"))
|
|
|
|
|
|
def create_workflow_config(setup_dir):
|
|
"""Create workflow configuration."""
|
|
print("\nCreating workflow configuration...")
|
|
|
|
workflow = {
|
|
"study_name": STUDY_NAME,
|
|
"optimization_request": "Tune the first natural frequency mode to exactly 115 Hz using intelligent multi-strategy optimization",
|
|
"design_variables": [
|
|
{
|
|
"parameter": "inner_diameter",
|
|
"bounds": [50, 150],
|
|
"units": "mm",
|
|
"description": "Inner diameter of circular plate"
|
|
},
|
|
{
|
|
"parameter": "plate_thickness",
|
|
"bounds": [2, 10],
|
|
"units": "mm",
|
|
"description": "Thickness of circular plate"
|
|
}
|
|
],
|
|
"objectives": [
|
|
{
|
|
"name": "frequency_error",
|
|
"goal": "minimize",
|
|
"extraction": {
|
|
"action": "extract_first_natural_frequency",
|
|
"params": {
|
|
"mode_number": 1,
|
|
"target_frequency": 115.0
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"constraints": [
|
|
{
|
|
"name": "frequency_tolerance",
|
|
"type": "less_than",
|
|
"threshold": 0.1,
|
|
"description": "Error from target must be less than 0.1 Hz"
|
|
}
|
|
]
|
|
}
|
|
|
|
workflow_file = setup_dir / "workflow_config.json"
|
|
with open(workflow_file, 'w', encoding='utf-8') as f:
|
|
json.dump(workflow, f, indent=2)
|
|
|
|
print(f" Saved: {workflow_file.relative_to(BASE_DIR)}")
|
|
return workflow
|
|
|
|
|
|
def create_optimization_config(setup_dir):
|
|
"""Create Protocol 10 optimization configuration."""
|
|
print("\nCreating Protocol 10 optimization configuration...")
|
|
|
|
config = {
|
|
"_description": "Protocol 10: Intelligent Multi-Strategy Optimization - Circular Plate Test",
|
|
"_version": "1.0",
|
|
|
|
"study_name": STUDY_NAME,
|
|
"direction": "minimize",
|
|
|
|
"intelligent_optimization": {
|
|
"_description": "Protocol 10 - Automatic landscape analysis and strategy selection",
|
|
"enabled": True,
|
|
|
|
"characterization_trials": 15,
|
|
"stagnation_window": 10,
|
|
"min_improvement_threshold": 0.001,
|
|
"min_analysis_trials": 10,
|
|
"reanalysis_interval": 15,
|
|
|
|
"strategy_preferences": {
|
|
"prefer_cmaes_for_smooth": True,
|
|
"prefer_tpe_for_multimodal": True,
|
|
"enable_hybrid_strategies": False
|
|
}
|
|
},
|
|
|
|
"sampler": {
|
|
"_description": "Fallback sampler if Protocol 10 disabled",
|
|
"type": "TPESampler",
|
|
"params": {
|
|
"n_startup_trials": 10,
|
|
"n_ei_candidates": 24,
|
|
"multivariate": True,
|
|
"warn_independent_sampling": True
|
|
}
|
|
},
|
|
|
|
"pruner": {
|
|
"type": "MedianPruner",
|
|
"params": {
|
|
"n_startup_trials": 5,
|
|
"n_warmup_steps": 0
|
|
}
|
|
},
|
|
|
|
"adaptive_strategy": {
|
|
"_description": "Protocol 8 - Adaptive exploitation based on surrogate confidence",
|
|
"enabled": True,
|
|
"min_confidence_for_exploitation": 0.65,
|
|
"min_trials_for_confidence": 15,
|
|
"target_confidence_metrics": {
|
|
"convergence_weight": 0.4,
|
|
"coverage_weight": 0.3,
|
|
"stability_weight": 0.3
|
|
}
|
|
},
|
|
|
|
"trials": {
|
|
"n_trials": 100,
|
|
"timeout": None,
|
|
"catch": []
|
|
},
|
|
|
|
"reporting": {
|
|
"auto_generate_plots": True,
|
|
"include_optuna_visualizations": True,
|
|
"include_confidence_report": True,
|
|
"include_strategy_performance": True,
|
|
"save_intelligence_report": True
|
|
},
|
|
|
|
"verbosity": {
|
|
"print_landscape_report": True,
|
|
"print_strategy_recommendation": True,
|
|
"print_phase_transitions": True,
|
|
"print_confidence_updates": True,
|
|
"log_to_file": True
|
|
},
|
|
|
|
"optimization_notes": "Protocol 10 Test: Atomizer will automatically characterize the circular plate problem, select the best optimization algorithm (TPE, CMA-ES, or GP-BO), and adapt strategy if stagnation is detected. Expected: smooth_unimodal landscape → CMA-ES recommendation."
|
|
}
|
|
|
|
config_file = setup_dir / "optimization_config.json"
|
|
with open(config_file, 'w', encoding='utf-8') as f:
|
|
json.dump(config, f, indent=2)
|
|
|
|
print(f" Saved: {config_file.relative_to(BASE_DIR)}")
|
|
return config
|
|
|
|
|
|
def create_runner_script(study_dir, workflow, config):
|
|
"""Create optimization runner using Protocol 10."""
|
|
print("\nCreating Protocol 10 optimization runner...")
|
|
|
|
runner_code = '''"""
|
|
Intelligent Multi-Strategy Optimization Runner
|
|
Study: circular_plate_frequency_tuning_intelligent_optimizer
|
|
|
|
This runner uses Protocol 10 (IMSO) to automatically:
|
|
1. Characterize the optimization landscape
|
|
2. Select the best optimization algorithm
|
|
3. Adapt strategy dynamically if needed
|
|
|
|
Generated: 2025-11-19
|
|
Protocol: 10 (Intelligent Multi-Strategy Optimization)
|
|
"""
|
|
|
|
import sys
|
|
import json
|
|
import optuna
|
|
from pathlib import Path
|
|
|
|
# Add optimization engine to path
|
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
|
|
from optimization_engine.intelligent_optimizer import IntelligentOptimizer
|
|
from optimization_engine.nx_updater import NXParameterUpdater
|
|
from optimization_engine.nx_solver import NXSolver
|
|
from optimization_engine.extractors.frequency_extractor import extract_first_frequency
|
|
from optimization_engine.generate_report_markdown import generate_markdown_report
|
|
|
|
|
|
def main():
|
|
"""Run Protocol 10 intelligent optimization."""
|
|
|
|
# Setup paths
|
|
study_dir = Path(__file__).parent
|
|
setup_dir = study_dir / "1_setup"
|
|
model_dir = setup_dir / "model"
|
|
results_dir = study_dir / "2_results"
|
|
reports_dir = study_dir / "3_reports"
|
|
|
|
# Create directories
|
|
results_dir.mkdir(exist_ok=True)
|
|
reports_dir.mkdir(exist_ok=True)
|
|
|
|
# Load configuration
|
|
print("\\nLoading configuration...")
|
|
with open(setup_dir / "workflow_config.json") as f:
|
|
workflow = json.load(f)
|
|
|
|
with open(setup_dir / "optimization_config.json") as f:
|
|
opt_config = json.load(f)
|
|
|
|
print(f"Study: {workflow['study_name']}")
|
|
print(f"Protocol 10: {opt_config['intelligent_optimization']['enabled']}")
|
|
|
|
# Model files
|
|
prt_file = model_dir / "Circular_Plate.prt"
|
|
sim_file = model_dir / "Circular_Plate_sim1.sim"
|
|
|
|
# Initialize NX components
|
|
updater = NXParameterUpdater(str(prt_file))
|
|
solver = NXSolver()
|
|
|
|
# Incremental history tracking
|
|
history_file = results_dir / "optimization_history_incremental.json"
|
|
history = []
|
|
|
|
def objective(trial):
|
|
"""Objective function for optimization."""
|
|
|
|
# Sample design variables
|
|
inner_diameter = trial.suggest_float('inner_diameter', 50, 150)
|
|
plate_thickness = trial.suggest_float('plate_thickness', 2, 10)
|
|
|
|
params = {
|
|
'inner_diameter': inner_diameter,
|
|
'plate_thickness': plate_thickness
|
|
}
|
|
|
|
print(f"\\n Trial #{trial.number}")
|
|
print(f" Inner Diameter: {inner_diameter:.4f} mm")
|
|
print(f" Plate Thickness: {plate_thickness:.4f} mm")
|
|
|
|
# Update CAD model
|
|
updater.update_expressions(params)
|
|
|
|
# Run simulation (use discovered solution name from benchmarking)
|
|
result = solver.run_simulation(str(sim_file), solution_name="Solution_Normal_Modes")
|
|
|
|
if not result['success']:
|
|
print(f" Simulation FAILED: {result.get('error', 'Unknown error')}")
|
|
raise optuna.TrialPruned()
|
|
|
|
# Extract frequency
|
|
op2_file = result['op2_file']
|
|
frequency = extract_first_frequency(op2_file, mode_number=1)
|
|
|
|
# Calculate objective (error from target)
|
|
target_frequency = 115.0
|
|
objective_value = abs(frequency - target_frequency)
|
|
|
|
print(f" Frequency: {frequency:.4f} Hz")
|
|
print(f" Target: {target_frequency:.4f} Hz")
|
|
print(f" Error: {objective_value:.4f} Hz")
|
|
|
|
# Save to incremental history
|
|
trial_data = {
|
|
"trial_number": trial.number,
|
|
"design_variables": params,
|
|
"results": {"first_frequency": frequency},
|
|
"objective": objective_value
|
|
}
|
|
|
|
history.append(trial_data)
|
|
|
|
with open(history_file, 'w', encoding='utf-8') as f:
|
|
json.dump(history, f, indent=2)
|
|
|
|
return objective_value
|
|
|
|
# Create intelligent optimizer
|
|
print("\\n" + "="*70)
|
|
print(" PROTOCOL 10: INTELLIGENT MULTI-STRATEGY OPTIMIZATION")
|
|
print("="*70)
|
|
|
|
optimizer = IntelligentOptimizer(
|
|
study_name=workflow['study_name'],
|
|
study_dir=results_dir,
|
|
config=opt_config,
|
|
verbose=True
|
|
)
|
|
|
|
# Extract design variable bounds
|
|
design_vars = {
|
|
var['parameter']: tuple(var['bounds'])
|
|
for var in workflow['design_variables']
|
|
}
|
|
|
|
# Run optimization
|
|
results = optimizer.optimize(
|
|
objective_function=objective,
|
|
design_variables=design_vars,
|
|
n_trials=opt_config['trials']['n_trials'],
|
|
target_value=115.0,
|
|
tolerance=0.1
|
|
)
|
|
|
|
# Save intelligence report
|
|
optimizer.save_intelligence_report()
|
|
|
|
# Generate markdown report
|
|
print("\\nGenerating optimization report...")
|
|
|
|
# Load study for Optuna visualizations
|
|
storage = f"sqlite:///{results_dir / 'study.db'}"
|
|
study = optuna.load_study(
|
|
study_name=workflow['study_name'],
|
|
storage=storage
|
|
)
|
|
|
|
report = generate_markdown_report(
|
|
history_file=history_file,
|
|
target_value=115.0,
|
|
tolerance=0.1,
|
|
reports_dir=reports_dir,
|
|
study=study
|
|
)
|
|
|
|
report_file = reports_dir / "OPTIMIZATION_REPORT.md"
|
|
with open(report_file, 'w', encoding='utf-8') as f:
|
|
f.write(report)
|
|
|
|
print(f"\\nReport saved: {report_file}")
|
|
|
|
# Print final summary
|
|
print("\\n" + "="*70)
|
|
print(" PROTOCOL 10 TEST COMPLETE")
|
|
print("="*70)
|
|
print(f"Best Frequency: {results['best_value'] + 115.0:.4f} Hz")
|
|
print(f"Best Error: {results['best_value']:.4f} Hz")
|
|
print(f"Best Parameters:")
|
|
for param, value in results['best_params'].items():
|
|
print(f" {param}: {value:.4f}")
|
|
|
|
if 'landscape_analysis' in results and results['landscape_analysis'].get('ready'):
|
|
landscape = results['landscape_analysis']
|
|
print(f"\\nLandscape Type: {landscape['landscape_type'].upper()}")
|
|
print(f"Recommended Strategy: {results.get('final_strategy', 'N/A').upper()}")
|
|
|
|
print("="*70)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
'''
|
|
|
|
runner_file = study_dir / "run_optimization.py"
|
|
with open(runner_file, 'w', encoding='utf-8') as f:
|
|
f.write(runner_code)
|
|
|
|
print(f" Saved: {runner_file.relative_to(BASE_DIR)}")
|
|
return runner_file
|
|
|
|
|
|
def create_readme(study_dir):
|
|
"""Create README for the study."""
|
|
print("\nCreating README...")
|
|
|
|
readme = f"""# {STUDY_NAME}
|
|
|
|
**Protocol 10 Test Study** - Intelligent Multi-Strategy Optimization
|
|
|
|
## Overview
|
|
|
|
This study tests Atomizer's Protocol 10 (IMSO) framework on a circular plate frequency tuning problem.
|
|
|
|
**Goal**: Tune first natural frequency to exactly 115 Hz
|
|
|
|
**Protocol 10 Features Tested**:
|
|
- Automatic landscape characterization (smoothness, multimodality, correlation)
|
|
- Intelligent strategy selection (TPE, CMA-ES, or GP-BO)
|
|
- Dynamic strategy switching based on stagnation detection
|
|
- Comprehensive decision logging for transparency
|
|
|
|
## Expected Behavior
|
|
|
|
### Stage 1: Landscape Characterization (Trials 1-15)
|
|
- Random exploration to gather data
|
|
- Analyze problem characteristics
|
|
- Expected classification: `smooth_unimodal` with strong parameter correlation
|
|
|
|
### Stage 2: Strategy Selection (Trial 15)
|
|
- Expected recommendation: **CMA-ES** (92% confidence)
|
|
- Reasoning: "Smooth unimodal with strong correlation - CMA-ES converges quickly"
|
|
|
|
### Stage 3: Adaptive Optimization (Trials 16-100)
|
|
- Run with CMA-ES sampler
|
|
- Monitor for stagnation
|
|
- Switch strategies if needed (unlikely for this problem)
|
|
|
|
## Study Structure
|
|
|
|
```
|
|
{STUDY_NAME}/
|
|
├── 1_setup/
|
|
│ ├── model/ # CAD and simulation files
|
|
│ ├── workflow_config.json # Optimization goals
|
|
│ └── optimization_config.json # Protocol 10 configuration
|
|
├── 2_results/
|
|
│ ├── study.db # Optuna database
|
|
│ ├── optimization_history_incremental.json
|
|
│ └── intelligent_optimizer/ # Protocol 10 tracking
|
|
│ ├── strategy_transitions.json
|
|
│ ├── strategy_performance.json
|
|
│ └── intelligence_report.json
|
|
└── 3_reports/
|
|
└── OPTIMIZATION_REPORT.md # Final report with visualizations
|
|
```
|
|
|
|
## Running the Optimization
|
|
|
|
```bash
|
|
# Activate environment
|
|
conda activate test_env
|
|
|
|
# Run optimization
|
|
python run_optimization.py
|
|
```
|
|
|
|
## What to Look For
|
|
|
|
### Console Output
|
|
|
|
**Landscape Analysis Report**:
|
|
```
|
|
======================================================================
|
|
LANDSCAPE ANALYSIS REPORT
|
|
======================================================================
|
|
Type: SMOOTH_UNIMODAL
|
|
Smoothness: 0.7X (smooth)
|
|
Multimodal: NO (1 modes)
|
|
Parameter Correlation: 0.6X (strong)
|
|
```
|
|
|
|
**Strategy Recommendation**:
|
|
```
|
|
======================================================================
|
|
STRATEGY RECOMMENDATION
|
|
======================================================================
|
|
Recommended: CMAES
|
|
Confidence: 92.0%
|
|
Reasoning: Smooth unimodal with strong correlation - CMA-ES converges quickly
|
|
```
|
|
|
|
**Phase Transitions** (if any):
|
|
```
|
|
======================================================================
|
|
STRATEGY TRANSITION
|
|
======================================================================
|
|
Trial #45
|
|
TPE → CMAES
|
|
Reason: Stagnation detected
|
|
```
|
|
|
|
### Intelligence Report
|
|
|
|
Check `2_results/intelligent_optimizer/intelligence_report.json` for:
|
|
- Complete landscape analysis
|
|
- Strategy recommendation reasoning
|
|
- All transition events
|
|
- Performance breakdown by strategy
|
|
|
|
### Optimization Report
|
|
|
|
Check `3_reports/OPTIMIZATION_REPORT.md` for:
|
|
- Best result (should be < 0.1 Hz error)
|
|
- Convergence plots
|
|
- Optuna visualizations
|
|
- (Future: Protocol 10 analysis section)
|
|
|
|
## Expected Results
|
|
|
|
**Baseline (TPE only)**: ~160 trials to achieve 0.18 Hz error
|
|
|
|
**Protocol 10 (Intelligent)**: ~60-80 trials to achieve < 0.1 Hz error
|
|
|
|
**Improvement**: 40-50% faster convergence by selecting optimal algorithm
|
|
|
|
## Configuration
|
|
|
|
See [`optimization_config.json`](1_setup/optimization_config.json) for full Protocol 10 settings.
|
|
|
|
Key parameters:
|
|
- `characterization_trials`: 15 (initial exploration)
|
|
- `stagnation_window`: 10 (trials to check for stagnation)
|
|
- `min_improvement_threshold`: 0.001 (0.1% minimum improvement)
|
|
|
|
## References
|
|
|
|
- [PROTOCOL.md](../../PROTOCOL.md) - Complete Protocol 10 documentation
|
|
- [Protocol 10 Implementation Summary](../../docs/PROTOCOL_10_IMPLEMENTATION_SUMMARY.md)
|
|
- [Example Configuration](../../examples/optimization_config_protocol10.json)
|
|
|
|
---
|
|
|
|
*Study created: 2025-11-19*
|
|
*Protocol: 10 (Intelligent Multi-Strategy Optimization)*
|
|
"""
|
|
|
|
readme_file = study_dir / "README.md"
|
|
with open(readme_file, 'w', encoding='utf-8') as f:
|
|
f.write(readme)
|
|
|
|
print(f" Saved: {readme_file.relative_to(BASE_DIR)}")
|
|
|
|
|
|
def main():
|
|
"""Create complete Protocol 10 test study."""
|
|
print("\n" + "="*70)
|
|
print(" CREATING PROTOCOL 10 TEST STUDY")
|
|
print("="*70)
|
|
|
|
# Create structure
|
|
setup_dir, model_dir, results_dir, reports_dir = create_study_structure()
|
|
|
|
# Copy model files
|
|
model_files = copy_model_files(model_dir)
|
|
|
|
# Create configurations
|
|
workflow = create_workflow_config(setup_dir)
|
|
config = create_optimization_config(setup_dir)
|
|
|
|
# Create runner
|
|
runner_file = create_runner_script(STUDY_DIR, workflow, config)
|
|
|
|
# Create README
|
|
create_readme(STUDY_DIR)
|
|
|
|
print("\n" + "="*70)
|
|
print(" STUDY CREATION COMPLETE")
|
|
print("="*70)
|
|
print(f"\nStudy directory: {STUDY_DIR.relative_to(BASE_DIR)}")
|
|
print(f"\nTo run optimization:")
|
|
print(f" cd {STUDY_DIR.relative_to(BASE_DIR)}")
|
|
print(f" python run_optimization.py")
|
|
print("\n" + "="*70)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|