# ATOMIZER PROTOCOL
## The Single Source of Truth for Atomizer Operations

**Version**: 1.0
**Date**: 2025-11-19
**Status**: ACTIVE - MUST BE FOLLOWED AT ALL TIMES

---

## CORE PRINCIPLE

**Atomizer is INTELLIGENT and AUTONOMOUS. It discovers, analyzes, and configures everything automatically.**

Human users should provide:
1. A CAD model (.prt)
2. A simulation setup (.sim)
3. An optimization goal (in natural language)

Atomizer handles the rest.

---

## PROTOCOL: Study Creation Workflow

### Phase 1: Intelligent Benchmarking (MANDATORY)

**Purpose**: Discover EVERYTHING about the simulation before running optimization.

**Steps**:

1. **Extract ALL Expressions**
   - Use `NXParameterUpdater.get_all_expressions()`
   - Catalog all design variables
   - Identify bounds and units

2. **Solve ALL Solutions in .sim File**
   - Use `IntelligentSetup._solve_all_solutions()`
   - This runs NXOpen journal that:
     - Updates FEM from master .prt (CRITICAL for modal analysis)
     - Calls `SolveAllSolutions()`
     - Saves simulation to write output files
   - Returns: Number solved, failed, skipped, solution names

3. **Analyze ALL Result Files**
   - Scan for all .op2 files in model directory
   - Use pyNastran to read each .op2
   - Catalog available results:
     - Eigenvalues (modal frequencies)
     - Displacements
     - Stresses (CQUAD4, CTRIA3, etc.)
     - Forces
   - Map each result type to its source solution

4. **Match Objectives to Available Results**
   - Parse user's optimization goal
   - Match requested objectives to discovered results
   - Example mappings:
     - "frequency" → eigenvalues → Solution_Normal_Modes
     - "displacement" → displacements → Solution_1
     - "stress" → cquad4_stress → Solution_1
   - Return recommended solution for optimization

**Output**: Benchmark data structure
```json
{
  "success": true,
  "expressions": {"param1": {"value": 10.0, "units": "mm"}, ...},
  "solutions": {"num_solved": 2, "solution_names": ["Solution[Solution 1]", "Solution[Solution_Normal_Modes]"]},
  "available_results": {
    "eigenvalues": {"file": "model_sim1-solution_normal_modes.op2", "count": 10, "solution": "Solution_Normal_Modes"},
    "displacements": {"file": "model_sim1-solution_1.op2", "count": 613, "solution": "Solution_1"}
  },
  "objective_mapping": {
    "objectives": {
      "frequency_error": {
        "solution": "Solution_Normal_Modes",
        "result_type": "eigenvalues",
        "extractor": "extract_first_frequency",
        "op2_file": Path("..."),
        "match_confidence": "HIGH"
      }
    },
    "primary_solution": "Solution_Normal_Modes"
  }
}
```

### Phase 2: Study Structure Creation

1. Create directory structure:
   ```
   studies/{study_name}/
   ├── 1_setup/
   │   ├── model/           # Model files (.prt, .sim, .fem, .fem_i)
   │   └── workflow_config.json
   ├── 2_results/           # Optimization database and history
   └── 3_reports/           # Human-readable markdown reports with graphs
   ```

2. Copy model files to `1_setup/model/`

3. Save workflow configuration to `1_setup/workflow_config.json`

### Phase 3: Optimization Runner Generation

**Purpose**: Generate a runner that is PERFECTLY configured based on benchmarking.

**Critical Rules**:

1. **Solution Selection**
   - Use `benchmark_results['objective_mapping']['primary_solution']`
   - This is the solution that contains the required results
   - Generate code: `solver.run_simulation(sim_file, solution_name="{primary_solution}")`

2. **Extractor Generation**
   - Use extractors matched to available results
   - For eigenvalues: `extract_first_frequency`
   - For displacements: `extract_max_displacement`
   - For stresses: `extract_max_stress`

3. **Objective Function**
   - For target-matching objectives (e.g., "tune to 115 Hz"):
     ```python
     if 'target_frequency' in obj_config.get('extraction', {}).get('params', {}):
         target = obj_config['extraction']['params']['target_frequency']
         objective_value = abs(results[result_name] - target)
     ```
   - For minimization: `objective_value = results[result_name]`
   - For maximization: `objective_value = -results[result_name]`

4. **Incremental History Tracking**
   - Write JSON after each trial
   - File: `2_results/optimization_history_incremental.json`
   - Format:
     ```json
     [
       {
         "trial_number": 0,
         "design_variables": {"param1": 10.5, ...},
         "results": {"first_frequency": 115.2},
         "objective": 0.2
       },
       ...
     ]
     ```

5. **Report Generation**
   - Use `generate_markdown_report()` NOT plain text
   - Extract target values from workflow
   - Save to `3_reports/OPTIMIZATION_REPORT.md`
   - Include convergence plots, design space plots, sensitivity plots

### Phase 4: Configuration Report

Generate `1_setup/CONFIGURATION_REPORT.md` documenting:
- All discovered expressions
- All solutions found and solved
- Objective matching results
- Recommended configuration
- Any warnings or issues

---

## CRITICAL PROTOCOLS

### Protocol 1: NEVER Guess Solution Names

**WRONG**:
```python
result = solver.run_simulation(sim_file, solution_name="normal_modes")  # WRONG - guessing!
```

**RIGHT**:
```python
# Use benchmark data to get actual solution name
recommended_solution = benchmark_results['objective_mapping']['primary_solution']
result = solver.run_simulation(sim_file, solution_name=recommended_solution)
```

**WHY**: Solution names in NX are user-defined and unpredictable. They might be "Solution 1", "SOLUTION_NORMAL_MODES", "Modal Analysis", etc. ALWAYS discover them via benchmarking.

### Protocol 2: ALWAYS Update FEM Before Solving

**Why**: Modal analysis (eigenvalues) requires the FEM to match the current geometry. If geometry changed but FEM wasn't updated, eigenvalues will be wrong.

**How**: The `_solve_all_solutions()` journal includes:
```python
femPart.UpdateFemodel()  # Updates FEM from master CAD
```

This is done automatically during benchmarking.

### Protocol 3: Target-Matching Objectives

**User says**: "Tune frequency to exactly 115 Hz"

**Workflow**:
```json
{
  "objectives": [{
    "name": "frequency_error",
    "goal": "minimize",
    "extraction": {
      "action": "extract_first_natural_frequency",
      "params": {"mode_number": 1, "target_frequency": 115.0}
    }
  }]
}
```

**Generated objective function**:
```python
if 'target_frequency' in obj_config.get('extraction', {}).get('params', {}):
    target = obj_config['extraction']['params']['target_frequency']
    objective_value = abs(results[result_name] - target)
    print(f"  Frequency: {results[result_name]:.4f} Hz, Target: {target} Hz, Error: {objective_value:.4f} Hz")
```

**WHY**: Optuna must minimize ERROR FROM TARGET, not minimize the raw frequency value.

### Protocol 4: Optimization Configuration (CRITICAL!)

**MANDATORY**: Every study MUST have `1_setup/optimization_config.json`.

**Purpose**: Full control over Optuna sampler, pruner, and optimization strategy.

**Configuration File**: `{study_dir}/1_setup/optimization_config.json`

```json
{
  "study_name": "my_optimization",
  "direction": "minimize",
  "sampler": {
    "type": "TPESampler",
    "params": {
      "n_startup_trials": 5,
      "n_ei_candidates": 24,
      "multivariate": true,
      "warn_independent_sampling": true
    }
  },
  "pruner": {
    "type": "MedianPruner",
    "params": {
      "n_startup_trials": 5,
      "n_warmup_steps": 0
    }
  },
  "trials": {
    "n_trials": 50,
    "timeout": null,
    "catch": []
  },
  "optimization_notes": "TPE sampler with multivariate enabled for correlated parameters. n_startup_trials=5 means first 5 trials are random exploration, then TPE exploitation begins."
}
```

**Sampler Configuration**:

1. **TPESampler** (RECOMMENDED - Tree-structured Parzen Estimator)
   - `n_startup_trials`: Number of random trials before TPE kicks in (default: 10)
   - `n_ei_candidates`: Number of candidate samples (default: 24)
   - `multivariate`: Use multivariate TPE for correlated parameters (default: false, **SHOULD BE TRUE**)
   - `warn_independent_sampling`: Warn when parameters sampled independently

2. **RandomSampler** (for baseline comparison only)
   ```json
   {
     "type": "RandomSampler",
     "params": {"seed": 42}
   }
   ```

3. **CmaEsSampler** (for continuous optimization)
   ```json
   {
     "type": "CmaEsSampler",
     "params": {
       "n_startup_trials": 5,
       "restart_strategy": "ipop"
     }
   }
   ```

**Runner Implementation**:

```python
# Load optimization configuration
opt_config_file = Path(__file__).parent / "1_setup/optimization_config.json"
with open(opt_config_file) as f:
    opt_config = json.load(f)

# Create sampler from config
sampler_type = opt_config['sampler']['type']
sampler_params = opt_config['sampler']['params']

if sampler_type == "TPESampler":
    sampler = optuna.samplers.TPESampler(**sampler_params)
elif sampler_type == "CmaEsSampler":
    sampler = optuna.samplers.CmaEsSampler(**sampler_params)
else:
    sampler = None  # Use default

# Create study with configured sampler
study = optuna.create_study(
    study_name=opt_config['study_name'],
    storage=storage,
    load_if_exists=True,
    direction=opt_config['direction'],
    sampler=sampler  # CRITICAL - do not omit!
)
```

**WHY THIS IS CRITICAL**:

Without explicit sampler configuration, Optuna uses **random sampling**, which does NOT learn from previous trials! This is why your optimization wasn't converging. TPE with multivariate=True is essential for problems with correlated parameters (like diameter and thickness affecting frequency together).

**Common Mistakes**:
- ❌ Omitting `sampler` parameter in `create_study()` → Uses random search
- ❌ Setting `multivariate=False` for correlated parameters → Poor convergence
- ❌ Too high `n_startup_trials` → Wastes trials on random search
- ❌ No configuration file → No reproducibility, no tuning

### Protocol 5: Folder Structure

**ALWAYS** use this structure:
- `1_setup/` - Model files, workflow_config.json, **optimization_config.json**
- `2_results/` - Optimization database and incremental history
- `3_reports/` - Markdown reports with graphs

**NEVER** use:
- `2_substudies/` ❌
- `results/` without parent folder ❌
- Reports in `2_results/` ❌
- Missing `optimization_config.json` ❌

### Protocol 6: Intelligent Early Stopping

**Purpose**: Stop optimization automatically when target is achieved with confidence, instead of wasting trials.

**Implementation**: Use Optuna callbacks to monitor convergence.

```python
class TargetAchievedCallback:
    """Stop when target is achieved with confidence."""
    def __init__(self, target_value, tolerance, min_trials=10):
        self.target_value = target_value
        self.tolerance = tolerance
        self.min_trials = min_trials
        self.consecutive_successes = 0
        self.required_consecutive = 3  # Need 3 consecutive successes to confirm

    def __call__(self, study, trial):
        # Need minimum trials for surrogate to learn
        if len(study.trials) < self.min_trials:
            return

        # Check if current trial achieved target
        if trial.value <= self.tolerance:
            self.consecutive_successes += 1
            print(f"  [STOPPING] Target achieved! ({self.consecutive_successes}/{self.required_consecutive} confirmations)")

            if self.consecutive_successes >= self.required_consecutive:
                print(f"  [STOPPING] Confidence achieved - stopping optimization")
                study.stop()
        else:
            self.consecutive_successes = 0

# Usage in runner
callback = TargetAchievedCallback(target_value=115.0, tolerance=0.1, min_trials=10)
study.optimize(objective, n_trials=max_trials, callbacks=[callback])
```

**WHY THIS IS CRITICAL**:
- Bayesian optimization with TPE learns from trials - need minimum 10 trials before stopping
- Require 3 consecutive successes to ensure it's not a lucky guess
- This is **intelligent** stopping based on surrogate model confidence
- Avoids wasting computational resources once target is reliably achieved

**Parameters to Tune**:
- `min_trials`: Minimum trials before considering stopping (default: 10 for TPE)
- `required_consecutive`: Number of consecutive successes needed (default: 3)
- `tolerance`: How close to target counts as "achieved"

### Protocol 7: Client-Friendly Reports

**Purpose**: Reports must show ACTUAL METRICS first (what clients care about), not just technical objective values.

**Report Structure** (in order of importance):

1. **Achieved Performance** (FIRST - what the client cares about)
   ```markdown
   ### Achieved Performance
   - **First Frequency**: 115.4780 Hz
     - Target: 115.0000 Hz
     - Error: 0.4780 Hz (0.42%)
   ```

2. **Design Parameters** (SECOND - how to build it)
   ```markdown
   ### Design Parameters
   - **Inner Diameter**: 94.07 mm
   - **Plate Thickness**: 6.14 mm
   ```

3. **Technical Details** (LAST - for engineers, in collapsible section)
   ```markdown
   <details>
   <summary>Technical Details (Objective Function)</summary>

   - **Objective Value (Error)**: 0.478014 Hz
   </details>
   ```

**Graph Placement**: Graphs MUST be saved to `3_reports/` folder (same as markdown file)

```python
def generate_markdown_report(history_file: Path, target_value: Optional[float] = None,
                             tolerance: float = 0.1, reports_dir: Optional[Path] = None) -> str:
    # Graphs should be saved to 3_reports/ folder (same as markdown file)
    study_dir = history_file.parent.parent
    if reports_dir is None:
        reports_dir = study_dir / "3_reports"
    reports_dir.mkdir(parents=True, exist_ok=True)

    # Generate plots in reports folder
    convergence_plot = create_convergence_plot(history, target_value, reports_dir)
    design_space_plot = create_design_space_plot(history, reports_dir)
    sensitivity_plot = create_parameter_sensitivity_plot(history, reports_dir)
```

**Trial Tables**: Show ACTUAL RESULTS (frequency), not just objective

```markdown
| Rank | Trial | First Frequency | Inner Diameter | Plate Thickness |
|------|-------|-----------------|----------------|-----------------|
| 1    | #45   | 115.48          | 94.07          | 6.14            |
| 2    | #54   | 114.24          | 102.22         | 5.85            |
| 3    | #31   | 113.99          | 85.78          | 6.38            |
```

**WHY THIS IS CRITICAL**:
- Clients don't understand "objective = 0.478" - they need "Frequency = 115.48 Hz"
- Showing target comparison immediately tells them if goal was achieved
- Percentage error gives intuitive sense of accuracy
- Design parameters tell them how to manufacture the part

**NEVER**:
- Show only objective values without explaining what they mean
- Hide actual performance metrics in technical details
- Put graphs in wrong folder (must be in 3_reports/ with markdown)

### Protocol 8: Adaptive Surrogate-Based Optimization

**Purpose**: Intelligently transition from exploration to exploitation based on surrogate model confidence, not arbitrary trial counts.

**State-of-the-Art Confidence Metrics**:

1. **Convergence Score** (40% weight)
   - Measures consistency of recent improvements
   - High score = surrogate reliably finding better regions

2. **Exploration Coverage** (30% weight)
   - How well parameter space has been explored
   - Calculated as spread relative to parameter bounds

3. **Prediction Stability** (30% weight)
   - Are recent trials clustered in promising regions?
   - High stability = surrogate has learned the landscape

**Overall Confidence Formula**:
```
confidence = 0.4 * convergence + 0.3 * coverage + 0.3 * stability
```

**Phase Transition**: When confidence reaches 65%, automatically transition from exploration to exploitation.

**Implementation**:

```python
from optimization_engine.adaptive_surrogate import AdaptiveExploitationCallback

# In optimization_config.json
{
  "adaptive_strategy": {
    "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
    }
  }
}

# In run_optimization.py
callback = AdaptiveExploitationCallback(
    target_value=target_value,
    tolerance=tolerance,
    min_confidence_for_exploitation=0.65,
    min_trials=15,
    verbose=True
)
study.optimize(objective, n_trials=100, callbacks=[callback])
```

**What You'll See During Optimization**:

```
  [CONFIDENCE REPORT - Trial #15]
  Phase: EXPLORATION
  Overall Confidence: 42.3%
    - Convergence: 38.5%
    - Coverage: 52.1%
    - Stability: 35.8%
  MEDIUM CONFIDENCE (42.3%) - Continue exploration with some exploitation

  [CONFIDENCE REPORT - Trial #25]
  Phase: EXPLORATION
  Overall Confidence: 68.7%
    - Convergence: 71.2%
    - Coverage: 67.5%
    - Stability: 66.4%
  HIGH CONFIDENCE (68.7%) - Transitioning to exploitation phase

============================================================
  PHASE TRANSITION: EXPLORATION → EXPLOITATION
  Surrogate confidence: 68.7%
  Now focusing on refining best regions
============================================================
```

**WHY THIS IS CRITICAL**:
- **Not arbitrary**: Transitions based on actual surrogate learning, not fixed trial counts
- **Adaptive**: Different problems may need different exploration durations
- **Efficient**: Stops wasting trials on exploration once landscape is understood
- **Rigorous**: Uses statistical metrics (coefficient of variation, fANOVA) not guesswork
- **Configurable**: Confidence threshold and weights can be tuned per study

**Parameters**:
- `min_confidence_for_exploitation`: Default 0.65 (65%)
- `min_trials_for_confidence`: Minimum 15 trials before assessing confidence
- Confidence weights: Convergence (0.4), Coverage (0.3), Stability (0.3)

### Protocol 9: Professional Optuna Visualizations

**Purpose**: Reports MUST use Optuna's built-in visualization library for professional-quality analysis plots.

**Required Optuna Plots**:

1. **Parallel Coordinate Plot** (`plot_parallel_coordinate`)
   - Shows parameter interactions and relationships
   - Each line = one trial, colored by objective value
   - Identifies promising parameter combinations

2. **Optimization History** (`plot_optimization_history`)
   - Professional convergence visualization
   - Better than basic matplotlib plots

3. **Parameter Importance** (`plot_param_importances`)
   - Quantifies which parameters matter most
   - Uses fANOVA or other importance metrics

4. **Slice Plot** (`plot_slice`)
   - Individual parameter effects
   - Shows objective vs each parameter

5. **Contour Plot** (`plot_contour`) - 2D only
   - Parameter interaction heatmap
   - Reveals coupled effects

**Implementation**:

```python
from optuna.visualization import (
    plot_optimization_history,
    plot_parallel_coordinate,
    plot_param_importances,
    plot_slice,
    plot_contour
)

def create_optuna_plots(study: optuna.Study, output_dir: Path) -> Dict[str, str]:
    """Create professional Optuna visualization plots."""
    plots = {}

    # Parallel Coordinate
    fig = plot_parallel_coordinate(study)
    fig.write_image(str(output_dir / 'optuna_parallel_coordinate.png'), width=1200, height=600)

    # Optimization History
    fig = plot_optimization_history(study)
    fig.write_image(str(output_dir / 'optuna_optimization_history.png'), width=1000, height=600)

    # Parameter Importance
    fig = plot_param_importances(study)
    fig.write_image(str(output_dir / 'optuna_param_importances.png'), width=800, height=500)

    # Slice Plot
    fig = plot_slice(study)
    fig.write_image(str(output_dir / 'optuna_slice.png'), width=1000, height=600)

    # Contour (2D only)
    if len(study.best_params) == 2:
        fig = plot_contour(study)
        fig.write_image(str(output_dir / 'optuna_contour.png'), width=800, height=800)

    return plots

# Pass study object to report generator
report = generate_markdown_report(
    history_file,
    target_value=target_value,
    tolerance=tolerance,
    reports_dir=reports_dir,
    study=study  # CRITICAL - pass study for Optuna plots
)
```

**Report Section**:

```markdown
## Advanced Optimization Analysis (Optuna)

The following plots leverage Optuna's professional visualization library to provide deeper insights into the optimization process.

### Parallel Coordinate Plot

![Parallel Coordinate](optuna_parallel_coordinate.png)

This interactive plot shows how different parameter combinations lead to different objective values...

### Parameter Importance Analysis

![Parameter Importance](optuna_param_importances.png)

This analysis quantifies which design variables have the most impact...
```

**WHY THIS IS CRITICAL**:
- **Professional Quality**: Optuna plots are research-grade, publication-ready
- **Deeper Insights**: Parallel coordinates show interactions basic plots miss
- **Parameter Importance**: Tells you which variables actually matter
- **Industry Standard**: Optuna is state-of-the-art, used by top research labs
- **Client Expectations**: "WAY better report than that" requires professional visualizations

**NEVER**:
- Use only matplotlib when Optuna visualizations are available
- Forget to pass `study` object to report generator
- Skip parameter importance analysis

### Protocol 6: UTF-8 Encoding

**ALWAYS** use `encoding='utf-8'` when writing files:
```python
with open(file_path, 'w', encoding='utf-8') as f:
    f.write(content)
```

**WHY**: Checkmarks (✓), mathematical symbols, and international characters require UTF-8.

---

## FILE STRUCTURE REFERENCE

### Workflow Configuration (`1_setup/workflow_config.json`)

```json
{
  "study_name": "my_optimization",
  "optimization_request": "Tune the first natural frequency mode to exactly 115 Hz",
  "design_variables": [
    {"parameter": "thickness", "bounds": [2, 10]},
    {"parameter": "diameter", "bounds": [50, 150]}
  ],
  "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
  }]
}
```

### Generated Runner Template Structure

```python
"""
Auto-generated optimization runner
Created: {timestamp}
Intelligently configured from benchmarking
"""

import sys, json, optuna
from pathlib import Path
from optimization_engine.nx_updater import NXParameterUpdater
from optimization_engine.nx_solver import NXSolver

def extract_results(op2_file, workflow):
    """Extract results matched to workflow objectives"""
    # Auto-generated based on benchmark discoveries
    ...

def main():
    # Load workflow
    config_file = Path(__file__).parent / "1_setup/workflow_config.json"
    workflow = json.load(open(config_file))

    # Setup paths
    output_dir = Path(__file__).parent / "2_results"
    reports_dir = Path(__file__).parent / "3_reports"

    # Initialize
    updater = NXParameterUpdater(prt_file)
    solver = NXSolver()

    # Create Optuna study
    study = optuna.create_study(...)

    def objective(trial):
        # Sample design variables
        params = {var['parameter']: trial.suggest_float(var['parameter'], *var['bounds'])
                  for var in workflow['design_variables']}

        # Update model
        updater.update_expressions(params)

        # Run simulation (with discovered solution name!)
        result = solver.run_simulation(sim_file, solution_name="{DISCOVERED_SOLUTION}")

        # Extract results
        results = extract_results(result['op2_file'], workflow)

        # Calculate objective (with target matching if applicable)
        obj_config = workflow['objectives'][0]
        if 'target_frequency' in obj_config.get('extraction', {}).get('params', {}):
            target = obj_config['extraction']['params']['target_frequency']
            objective_value = abs(results['first_frequency'] - target)
        else:
            objective_value = results[list(results.keys())[0]]

        # Save incremental history
        with open(history_file, 'w', encoding='utf-8') as f:
            json.dump(history, f, indent=2)

        return objective_value

    # Run optimization
    study.optimize(objective, n_trials=n_trials)

    # Generate markdown report with graphs
    from optimization_engine.generate_report_markdown import generate_markdown_report
    report = generate_markdown_report(history_file, target_value={TARGET}, tolerance=0.1)
    with open(reports_dir / 'OPTIMIZATION_REPORT.md', 'w', encoding='utf-8') as f:
        f.write(report)

if __name__ == "__main__":
    main()
```

---

## TESTING PROTOCOL

Before releasing any study:

1. ✅ Benchmarking completed successfully
2. ✅ All solutions discovered and solved
3. ✅ Objectives matched to results with HIGH confidence
4. ✅ Runner uses discovered solution name (not hardcoded)
5. ✅ Objective function handles target-matching correctly
6. ✅ Incremental history updates after each trial
7. ✅ Reports generate in `3_reports/` as markdown with graphs
8. ✅ Reports mention target values explicitly

---

## TROUBLESHOOTING

### Issue: "No object found with this name" when solving

**Cause**: Using guessed solution name instead of discovered name.

**Fix**: Use benchmark data to get actual solution name.

### Issue: Optimization not learning / converging

**Cause**: Objective function returning wrong value (e.g., raw frequency instead of error from target).

**Fix**: Check if objective has target value. If yes, minimize `abs(result - target)`.

### Issue: No eigenvalues found in OP2

**Cause**: Either (1) solving wrong solution, or (2) FEM not updated before solving.

**Fix**:
1. Check benchmark discovered correct modal solution
2. Verify `_solve_all_solutions()` calls `UpdateFemodel()`

### Issue: Reports are plain text without graphs

**Cause**: Using old `generate_report` instead of `generate_report_markdown`.

**Fix**: Import and use `generate_markdown_report()` from `optimization_engine.generate_report_markdown`.

---

## EXTENSIBILITY ARCHITECTURE

### Core Principle: Modular Hooks System

Atomizer is designed to be extensible without modifying core code. All customization happens through **hooks**, **extractors**, and **plugins**.

### Hook System

**Purpose**: Allow users and future features to inject custom behavior at key points in the workflow.

**Hook Points**:

1. **pre_trial_hook** - Before each optimization trial starts
   - Use case: Custom parameter validation, parameter transformations
   - Receives: trial_number, design_variables
   - Returns: modified design_variables or None to skip trial

2. **post_trial_hook** - After each trial completes
   - Use case: Custom logging, notifications, early stopping
   - Receives: trial_number, design_variables, results, objective
   - Returns: None or dict with custom metrics

3. **pre_solve_hook** - Before NX solver is called
   - Use case: Custom model modifications, additional setup
   - Receives: prt_file, sim_file, design_variables
   - Returns: None

4. **post_solve_hook** - After NX solver completes
   - Use case: Custom result extraction, post-processing
   - Receives: op2_file, sim_file, solve_success
   - Returns: dict with additional results

5. **post_study_hook** - After entire optimization study completes
   - Use case: Custom reports, notifications, deployment
   - Receives: study_dir, best_params, best_objective, history
   - Returns: None

**Hook Implementation**:

Hooks are Python functions defined in `{study_dir}/1_setup/hooks.py`:

```python
# studies/my_study/1_setup/hooks.py

def pre_trial_hook(trial_number, design_variables):
    """Validate parameters before trial."""
    print(f"[HOOK] Starting trial {trial_number}")

    # Example: Apply custom constraints
    if design_variables['thickness'] < design_variables['diameter'] / 10:
        print("[HOOK] Skipping trial - thickness too small")
        return None  # Skip trial

    return design_variables  # Proceed with trial

def post_trial_hook(trial_number, design_variables, results, objective):
    """Log custom metrics after trial."""
    frequency = results.get('first_frequency', 0)

    # Example: Save to custom database
    custom_metrics = {
        'frequency_ratio': frequency / 115.0,
        'parameter_sum': sum(design_variables.values())
    }

    print(f"[HOOK] Custom metrics: {custom_metrics}")
    return custom_metrics

def post_study_hook(study_dir, best_params, best_objective, history):
    """Generate custom reports after optimization."""
    print(f"[HOOK] Optimization complete!")
    print(f"[HOOK] Best objective: {best_objective}")

    # Example: Send email notification
    # send_email(f"Optimization complete: {best_objective}")

    # Example: Deploy best design
    # deploy_to_production(best_params)
```

**Hook Loading** (in generated runner):

```python
# Load hooks if they exist
hooks_file = Path(__file__).parent / "1_setup/hooks.py"
hooks = {}
if hooks_file.exists():
    import importlib.util
    spec = importlib.util.spec_from_file_location("hooks", hooks_file)
    hooks_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(hooks_module)

    hooks['pre_trial'] = getattr(hooks_module, 'pre_trial_hook', None)
    hooks['post_trial'] = getattr(hooks_module, 'post_trial_hook', None)
    hooks['post_study'] = getattr(hooks_module, 'post_study_hook', None)

# In objective function:
if hooks.get('pre_trial'):
    modified_params = hooks['pre_trial'](trial.number, params)
    if modified_params is None:
        raise optuna.TrialPruned()  # Skip this trial
    params = modified_params
```

### Extractor System

**Purpose**: Modular result extraction from OP2 files.

**Extractor Library**: `optimization_engine/extractors/`

```
optimization_engine/
└── extractors/
    ├── __init__.py
    ├── frequency_extractor.py
    ├── displacement_extractor.py
    ├── stress_extractor.py
    └── custom_extractor.py  # User-defined
```

**Extractor Interface**:

```python
# optimization_engine/extractors/base_extractor.py

from abc import ABC, abstractmethod
from pathlib import Path
from typing import Dict, Any

class BaseExtractor(ABC):
    """Base class for all extractors."""

    @abstractmethod
    def extract(self, op2_file: Path, params: Dict[str, Any]) -> Dict[str, float]:
        """
        Extract results from OP2 file.

        Args:
            op2_file: Path to OP2 result file
            params: Extraction parameters (e.g., mode_number, node_id)

        Returns:
            Dict of result name → value
        """
        pass

    @property
    @abstractmethod
    def result_types(self) -> list:
        """List of result types this extractor can handle."""
        pass
```

**Example Custom Extractor**:

```python
# optimization_engine/extractors/custom_extractor.py

from .base_extractor import BaseExtractor
from pyNastran.op2.op2 import OP2
import numpy as np

class CustomResonanceExtractor(BaseExtractor):
    """Extract resonance quality factor from frequency response."""

    @property
    def result_types(self):
        return ['quality_factor', 'resonance_bandwidth']

    def extract(self, op2_file, params):
        model = OP2()
        model.read_op2(str(op2_file))

        # Custom extraction logic
        frequencies = model.eigenvalues[1].eigenvalues

        # Calculate Q-factor
        f0 = frequencies[0]
        f1 = frequencies[1]
        bandwidth = abs(f1 - f0)
        q_factor = f0 / bandwidth

        return {
            'quality_factor': q_factor,
            'resonance_bandwidth': bandwidth
        }
```

**Extractor Registration** (in workflow config):

```json
{
  "objectives": [{
    "name": "q_factor",
    "goal": "maximize",
    "extraction": {
      "action": "custom_resonance_extractor",
      "params": {}
    }
  }]
}
```

### Plugin System

**Purpose**: Add entirely new functionality without modifying core.

**Plugin Structure**:

```
plugins/
├── ai_design_suggester/
│   ├── __init__.py
│   ├── plugin.py
│   └── README.md
├── sensitivity_analyzer/
│   ├── __init__.py
│   ├── plugin.py
│   └── README.md
└── cad_generator/
    ├── __init__.py
    ├── plugin.py
    └── README.md
```

**Plugin Interface**:

```python
# plugins/base_plugin.py

class BasePlugin(ABC):
    """Base class for Atomizer plugins."""

    @property
    @abstractmethod
    def name(self) -> str:
        """Plugin name."""
        pass

    @abstractmethod
    def initialize(self, config: Dict):
        """Initialize plugin with configuration."""
        pass

    @abstractmethod
    def execute(self, context: Dict) -> Dict:
        """
        Execute plugin logic.

        Args:
            context: Current optimization context (study_dir, history, etc.)

        Returns:
            Dict with plugin results
        """
        pass
```

**Example Plugin**:

```python
# plugins/ai_design_suggester/plugin.py

from plugins.base_plugin import BasePlugin

class AIDesignSuggester(BasePlugin):
    """Use AI to suggest promising design regions."""

    @property
    def name(self):
        return "ai_design_suggester"

    def initialize(self, config):
        self.model_type = config.get('model', 'random_forest')

    def execute(self, context):
        """Analyze history and suggest next designs."""
        history = context['history']

        # Train surrogate model
        X = [list(h['design_variables'].values()) for h in history]
        y = [h['objective'] for h in history]

        # Suggest next promising region
        suggested_params = self._suggest_next_design(X, y)

        return {
            'suggested_design': suggested_params,
            'expected_improvement': 0.15
        }
```

### API-Ready Architecture

**Purpose**: Enable future web API / REST interface without refactoring.

**API Endpoints** (future):

```python
# api/server.py (future implementation)

from fastapi import FastAPI
from optimization_engine.hybrid_study_creator import HybridStudyCreator

app = FastAPI()

@app.post("/api/v1/studies/create")
async def create_study(request: StudyRequest):
    """Create optimization study from API request."""
    creator = HybridStudyCreator()

    study_dir = creator.create_from_workflow(
        workflow_json=request.workflow,
        model_files=request.model_files,
        study_name=request.study_name
    )

    return {"study_id": study_dir.name, "status": "created"}

@app.post("/api/v1/studies/{study_id}/run")
async def run_optimization(study_id: str, n_trials: int):
    """Run optimization via API."""
    # Import and run the generated runner
    # Stream progress back to client
    pass

@app.get("/api/v1/studies/{study_id}/status")
async def get_status(study_id: str):
    """Get optimization status and current best."""
    # Read incremental history JSON
    # Return current progress
    pass

@app.get("/api/v1/studies/{study_id}/report")
async def get_report(study_id: str):
    """Get markdown report."""
    # Read OPTIMIZATION_REPORT.md
    # Return as markdown or HTML
    pass
```

**Design Principles for API Compatibility**:

1. **Stateless Operations**: All operations work with paths and JSON, no global state
2. **Incremental Results**: History JSON updated after each trial (enables streaming)
3. **Standard Formats**: All inputs/outputs are JSON, Markdown, or standard file formats
4. **Self-Contained Studies**: Each study folder is independent and portable

### Configuration System

**Purpose**: Centralized configuration with environment overrides.

**Configuration Hierarchy**:

1. Default config (in code)
2. Global config: `~/.atomizer/config.json`
3. Project config: `./atomizer.config.json`
4. Study config: `{study_dir}/1_setup/config.json`
5. Environment variables: `ATOMIZER_*`

**Example Configuration**:

```json
{
  "nx": {
    "executable": "C:/Program Files/Siemens/NX2412/ugraf.exe",
    "timeout_seconds": 600,
    "journal_runner": "C:/Program Files/Siemens/NX2412/run_journal.exe"
  },
  "optimization": {
    "default_n_trials": 50,
    "sampler": "TPE",
    "pruner": "MedianPruner"
  },
  "reports": {
    "auto_generate": true,
    "include_sensitivity": true,
    "plot_dpi": 150
  },
  "hooks": {
    "enabled": true,
    "timeout_seconds": 30
  },
  "plugins": {
    "enabled": ["ai_design_suggester", "sensitivity_analyzer"],
    "disabled": []
  }
}
```

### Future Feature Foundations

**Ready for Implementation**:

1. **Multi-Objective Optimization**
   - Hook: `extract_multiple_objectives()`
   - Extractor: Return dict with multiple values
   - Report: Pareto front visualization

2. **Constraint Handling**
   - Hook: `evaluate_constraints()`
   - Optuna: Add constraint callbacks
   - Report: Constraint violation history

3. **Surrogate Models**
   - Plugin: `surrogate_model_trainer`
   - Hook: `post_trial_hook` trains model
   - Use for cheap pre-screening

4. **Sensitivity Analysis**
   - Plugin: `sensitivity_analyzer`
   - Hook: `post_study_hook` runs analysis
   - Report: Sensitivity charts

5. **Design Space Exploration**
   - Plugin: `design_space_explorer`
   - Different sampling strategies
   - Adaptive design of experiments

6. **Parallel Evaluation**
   - Multiple NX instances
   - Distributed solver via Ray/Dask
   - Asynchronous trial management

7. **CAD Generation**
   - Plugin: `cad_generator`
   - Hook: `pre_solve_hook` generates geometry
   - Parametric model creation

8. **Live Dashboards**
   - Web UI showing real-time progress
   - Reads incremental history JSON
   - Interactive design space plots

---

## PROTOCOL 8: Adaptive Surrogate-Based Optimization (STUDY-AWARE)

### Purpose

Intelligent Bayesian optimization with confidence-based exploration→exploitation transitions.

**CRITICAL**: All adaptive components MUST be study-aware, tracking state across multiple optimization sessions using the Optuna study database, not just the current session.

### Implementation

**Module**: `optimization_engine/adaptive_surrogate.py`

**Key Classes**:
1. `SurrogateConfidenceMetrics`: Study-aware confidence calculation
2. `AdaptiveExploitationCallback`: Optuna callback with phase transition tracking

### Confidence Metrics (Study-Aware)

Uses `study.trials` directly, NOT session-based history:

```python
all_trials = [t for t in study.trials if t.state == COMPLETE]
```

**Metrics**:
1. **Convergence Score** (40% weight)
   - Recent improvement rate and consistency
   - Based on last 10 completed trials

2. **Exploration Coverage** (30% weight)
   - Parameter space coverage relative to bounds
   - Calculated as spread/range for each parameter

3. **Prediction Stability** (30% weight)
   - How stable recent best values are
   - Indicates surrogate model reliability

**Overall Confidence** = weighted combination
**Ready for Exploitation** = confidence ≥ 65% AND coverage ≥ 50%

### Phase Transitions

**Exploration Phase**:
- Initial trials (usually < 15)
- Focus: broad parameter space coverage
- TPE sampler with n_startup_trials random sampling

**Exploitation Phase**:
- After confidence threshold reached
- Focus: refining best regions
- TPE sampler with n_ei_candidates=24 for intensive Expected Improvement

**Transition Triggering**:
- Automatic when confidence ≥ 65%
- Logged to terminal with banner
- Saved to `phase_transitions.json`

### Tracking Files (Study-Aware)

**Location**: `studies/{study_name}/2_results/`

**Files**:
1. `phase_transitions.json`: Phase transition events
   ```json
   [{
     "trial_number": 45,
     "from_phase": "exploration",
     "to_phase": "exploitation",
     "confidence_metrics": {
       "overall_confidence": 0.72,
       "convergence_score": 0.68,
       "exploration_coverage": 0.75,
       "prediction_stability": 0.81
     }
   }]
   ```

2. `confidence_history.json`: Confidence snapshots every 5 trials
   ```json
   [{
     "trial_number": 15,
     "phase": "exploration",
     "confidence_metrics": {...},
     "total_trials": 15
   }]
   ```

### Configuration

**File**: `1_setup/optimization_config.json`

```json
{
  "adaptive_strategy": {
    "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
    }
  },
  "sampler": {
    "type": "TPESampler",
    "params": {
      "n_startup_trials": 10,
      "n_ei_candidates": 24,
      "multivariate": true
    }
  }
}
```

### Report Integration

**Markdown Report Sections**:
1. **Adaptive Optimization Strategy** section
   - Phase transition details
   - Confidence at transition
   - Metrics breakdown

2. **Confidence Progression Plot**
   - Overall confidence over trials
   - Component metrics (convergence, coverage, stability)
   - Red vertical line marking exploitation transition
   - Horizontal threshold line at 65%

**Generator**: `optimization_engine/generate_report_markdown.py`
- Reads `phase_transitions.json`
- Reads `confidence_history.json`
- Creates `confidence_progression.png`

### Critical Bug Fix (Nov 19, 2025)

**Problem**: Original implementation tracked trials in session-based `self.history`, resetting on each run.

**Solution**: Use `study.trials` directly for ALL calculations:
- Confidence metrics
- Phase transition decisions
- Trial counting

This ensures optimization behavior is consistent across interrupted/resumed sessions.

---

## PROTOCOL 9: Professional Optimization Visualizations (Optuna Integration)

### Purpose

Leverage Optuna's professional visualization library for publication-quality optimization analysis.

### Implementation

**Module**: `optimization_engine/generate_report_markdown.py`

**Function**: `create_optuna_plots(study, output_dir)`

### Generated Plots

1. **Parallel Coordinate Plot** (`optuna_parallel_coordinate.png`)
   - Multi-dimensional parameter visualization
   - Color-coded by objective value
   - Shows parameter interactions and promising regions

2. **Optimization History** (`optuna_optimization_history.png`)
   - Trial-by-trial objective progression
   - Best value trajectory
   - Professional alternative to custom convergence plot

3. **Parameter Importance** (`optuna_param_importances.png`)
   - fANOVA-based importance analysis
   - Quantifies which parameters most affect objective
   - Critical for understanding sensitivity

4. **Slice Plot** (`optuna_slice.png`)
   - Individual parameter effects
   - Other parameters held constant
   - Shows univariate relationships

5. **Contour Plot** (`optuna_contour.png`)
   - 2D parameter interaction heatmaps
   - All pairwise combinations
   - Reveals interaction effects

### Report Integration

All plots automatically added to markdown report under:
**"Advanced Optimization Analysis (Optuna)"** section

Each plot includes explanatory text about:
- What the plot shows
- How to interpret it
- What insights it provides

### Requirements

- `study` object must be passed to `generate_markdown_report()`
- Minimum 10 trials for meaningful visualizations
- Handles errors gracefully if plots fail

---

## PROTOCOL 10: Intelligent Multi-Strategy Optimization (IMSO)

### Purpose

**Make Atomizer self-tuning** - automatically discover problem characteristics and select the best optimization algorithm, adapting strategy mid-run if needed.

**User Experience**: User provides optimization goal. Atomizer intelligently tests strategies, analyzes landscape, and converges efficiently WITHOUT manual algorithm tuning.

### Design Philosophy

Different FEA problems need different optimization strategies:
- **Smooth unimodal** (e.g., beam bending) → CMA-ES for fast local convergence
- **Smooth multimodal** (e.g., resonance tuning) → GP-BO → CMA-ES hybrid
- **Rugged multimodal** (e.g., complex assemblies) → TPE for robust exploration
- **High-dimensional** (e.g., topology optimization) → TPE scales best

**Traditional approach**: User manually configures sampler (error-prone, suboptimal)

**Protocol 10 approach**: Atomizer discovers problem type, recommends strategy, switches mid-run if stagnating

### Three-Phase Architecture

**Stage 1: Landscape Characterization (Trials 1-15)**
- Run initial exploration with random sampling
- Analyze problem characteristics:
  - Smoothness (nearby points → similar objectives?)
  - Multimodality (multiple local optima?)
  - Parameter correlation (coupled effects?)
  - Noise level (simulation stability?)
- Generate landscape report for transparency

**Stage 2: Intelligent Strategy Selection (Trial 15+)**
- Use decision tree to match landscape → strategy
- Recommend sampler with confidence score
- Switch to recommended strategy
- Log decision reasoning

**Stage 3: Adaptive Optimization with Monitoring (Ongoing)**
- Monitor strategy performance
- Detect stagnation (no improvement over N trials)
- Re-analyze landscape if needed
- Switch strategies dynamically
- Track transitions for learning

### Core Components

**1. Landscape Analyzer** (`optimization_engine/landscape_analyzer.py`)

Computes problem characteristics from trial history:

```python
from optimization_engine.landscape_analyzer import LandscapeAnalyzer

analyzer = LandscapeAnalyzer(min_trials_for_analysis=10)
landscape = analyzer.analyze(study)

# Returns:
{
  'smoothness': 0.75,              # 0-1, higher = smoother
  'multimodal': False,             # Multiple local optima?
  'n_modes': 1,                    # Estimated local optima count
  'parameter_correlation': {...},  # Correlation with objective
  'noise_level': 0.12,             # Evaluation noise estimate
  'landscape_type': 'smooth_unimodal'  # Classification
}
```

**Landscape Types**:
- `smooth_unimodal`: Single smooth bowl → **CMA-ES**
- `smooth_multimodal`: Multiple smooth regions → **GP-BO or TPE**
- `rugged_unimodal`: Single rough region → **TPE**
- `rugged_multimodal`: Multiple rough regions → **TPE**
- `noisy`: High noise → **Robust methods**

**2. Strategy Selector** (`optimization_engine/strategy_selector.py`)

Expert decision tree for strategy recommendation:

```python
from optimization_engine.strategy_selector import IntelligentStrategySelector

selector = IntelligentStrategySelector(verbose=True)
strategy, details = selector.recommend_strategy(
    landscape=landscape,
    trials_completed=15,
    trials_budget=100
)

# Returns:
('cmaes', {
  'confidence': 0.92,
  'reasoning': 'Smooth unimodal with strong correlation - CMA-ES converges quickly',
  'sampler_config': {
    'type': 'CmaEsSampler',
    'params': {'restart_strategy': 'ipop'}
  }
})
```

**Decision Tree Logic**:
1. **High noise** → TPE (robust)
2. **Smooth + correlated** → CMA-ES (fast local convergence)
3. **Smooth + low-D** → GP-BO (sample efficient)
4. **Multimodal** → TPE (handles multiple modes)
5. **High-D** → TPE (scales best)
6. **Default** → TPE (safe general-purpose)

**3. Strategy Portfolio Manager** (`optimization_engine/strategy_portfolio.py`)

Manages dynamic strategy switching:

```python
from optimization_engine.strategy_portfolio import StrategyTransitionManager

manager = StrategyTransitionManager(
    stagnation_window=10,
    min_improvement_threshold=0.001,
    verbose=True,
    tracking_dir=study_dir / "intelligent_optimizer"
)

# Checks for switching conditions
should_switch, reason = manager.should_switch_strategy(study, landscape)

# Executes transition with logging
manager.execute_strategy_switch(
    study, from_strategy='tpe', to_strategy='cmaes',
    reason='Stagnation detected', trial_number=45
)
```

**Switching Triggers**:
- **Stagnation**: <0.1% improvement over 10 trials
- **Thrashing**: High variance without improvement
- **Strategy exhaustion**: Algorithm reached theoretical limit

**4. Intelligent Optimizer Orchestrator** (`optimization_engine/intelligent_optimizer.py`)

Main entry point coordinating all components:

```python
from optimization_engine.intelligent_optimizer import IntelligentOptimizer

optimizer = IntelligentOptimizer(
    study_name="my_study",
    study_dir=Path("studies/my_study/2_results"),
    config=opt_config,
    verbose=True
)

results = optimizer.optimize(
    objective_function=objective,
    design_variables={'thickness': (2, 10), 'diameter': (50, 150)},
    n_trials=100,
    target_value=115.0,
    tolerance=0.1
)

# Returns comprehensive results with strategy performance breakdown
```

### Configuration

**File**: `1_setup/optimization_config.json`

```json
{
  "intelligent_optimization": {
    "enabled": true,
    "characterization_trials": 15,
    "stagnation_window": 10,
    "min_improvement_threshold": 0.001,
    "min_analysis_trials": 10,
    "reanalysis_interval": 15
  },
  "sampler": {
    "type": "intelligent",
    "fallback": "TPESampler"
  }
}
```

**Parameters**:
- `enabled`: Enable Protocol 10 (default: true)
- `characterization_trials`: Initial exploration trials (default: 15)
- `stagnation_window`: Trials to check for stagnation (default: 10)
- `min_improvement_threshold`: Minimum relative improvement (default: 0.001 = 0.1%)
- `min_analysis_trials`: Minimum trials for landscape analysis (default: 10)
- `reanalysis_interval`: Re-analyze landscape every N trials (default: 15)

### Tracking and Transparency

**Location**: `studies/{study_name}/2_results/intelligent_optimizer/`

**Files Generated**:

1. **`strategy_transitions.json`** - All strategy switches
   ```json
   [{
     "trial_number": 45,
     "from_strategy": "tpe",
     "to_strategy": "cmaes",
     "reason": "Stagnation detected - <0.1% improvement in 10 trials",
     "best_value_at_switch": 0.485,
     "timestamp": "2025-11-19T14:23:15"
   }]
   ```

2. **`strategy_performance.json`** - Performance breakdown by strategy
   ```json
   {
     "strategies": {
       "tpe": {
         "trials_used": 45,
         "best_value_achieved": 0.485,
         "improvement_rate": 0.032
       },
       "cmaes": {
         "trials_used": 55,
         "best_value_achieved": 0.185,
         "improvement_rate": 0.055
       }
     }
   }
   ```

3. **`intelligence_report.json`** - Complete decision log
   - Landscape analysis at each interval
   - Strategy recommendations with reasoning
   - Confidence metrics over time
   - All transition events

### Console Output

**During Optimization**:

```
======================================================================
  STAGE 1: LANDSCAPE CHARACTERIZATION
======================================================================

  Trial #10: Objective = 5.234
  Trial #15: Objective = 3.456

======================================================================
  LANDSCAPE ANALYSIS REPORT
======================================================================
  Total Trials Analyzed: 15
  Dimensionality: 2 parameters

  LANDSCAPE CHARACTERISTICS:
    Type: SMOOTH_UNIMODAL
    Smoothness: 0.78 (smooth)
    Multimodal: NO (1 modes)
    Noise Level: 0.08 (low)

  PARAMETER CORRELATIONS:
    inner_diameter: +0.652 (strong positive)
    plate_thickness: -0.543 (strong negative)

======================================================================

======================================================================
  STAGE 2: STRATEGY SELECTION
======================================================================

======================================================================
  STRATEGY RECOMMENDATION
======================================================================
  Recommended: CMAES
  Confidence: 92.0%
  Reasoning: Smooth unimodal with strong correlation - CMA-ES converges quickly
======================================================================

======================================================================
  STAGE 3: ADAPTIVE OPTIMIZATION
======================================================================

  Trial #25: Objective = 1.234
  Trial #45: Objective = 0.485

======================================================================
  STRATEGY TRANSITION
======================================================================
  Trial #45
  TPE → CMAES
  Reason: Stagnation detected - <0.1% improvement in 10 trials
  Best value at transition: 0.485
======================================================================

  Trial #55: Objective = 0.350
  Trial #75: Objective = 0.185

======================================================================
  OPTIMIZATION COMPLETE
======================================================================
  Protocol: Protocol 10: Intelligent Multi-Strategy Optimization
  Total Trials: 100
  Best Value: 0.185 (Trial #98)
  Final Strategy: CMAES

  Strategy Transitions: 1
    Trial #45: tpe → cmaes

  Best Parameters:
    inner_diameter: 124.486
    plate_thickness: 5.072
======================================================================
```

### Report Integration

**Markdown Report Section** (auto-generated):

```markdown
## Intelligent Multi-Strategy Optimization (Protocol 10)

This optimization used **Protocol 10**, Atomizer's intelligent self-tuning framework.

### Problem Characteristics

Based on initial exploration (15 trials), the problem was classified as:

- **Landscape Type**: Smooth Unimodal
- **Smoothness**: 0.78 (smooth, well-behaved)
- **Multimodality**: Single optimum (no competing solutions)
- **Parameter Correlation**: Strong (0.65 overall)

### Strategy Selection

**Recommended Strategy**: CMA-ES (Covariance Matrix Adaptation)
- **Confidence**: 92%
- **Reasoning**: Smooth unimodal landscape with strong parameter correlation - CMA-ES will converge quickly

### Strategy Performance

| Strategy | Trials | Best Value | Improvement/Trial |
|----------|--------|------------|-------------------|
| TPE      | 45     | 0.485      | 0.032             |
| CMA-ES   | 55     | 0.185      | 0.055             |

**Transition Event (Trial #45)**: Switched from TPE to CMA-ES due to stagnation detection.

This automatic adaptation achieved **2.6x faster** convergence rate with CMA-ES compared to TPE.
```

### Integration with Existing Protocols

**Protocol 10 + Protocol 8 (Adaptive Surrogate)**:
- Landscape analyzer provides data for confidence calculation
- Confidence metrics inform strategy switching decisions
- Phase transitions tracked alongside strategy transitions

**Protocol 10 + Protocol 9 (Optuna Visualizations)**:
- Optuna plots show different strategy regions
- Parameter importance analysis validates landscape classification
- Slice plots confirm smoothness/multimodality assessment

### Algorithm Portfolio

**Available Strategies**:

1. **TPE (Tree-structured Parzen Estimator)** - Default safe choice
   - Strengths: Robust, handles multimodality, scales to ~50D
   - Weaknesses: Slower convergence on smooth problems
   - Best for: General purpose, multimodal, rugged landscapes

2. **CMA-ES (Covariance Matrix Adaptation)** - Fast local optimizer
   - Strengths: Very fast convergence, handles parameter correlation
   - Weaknesses: Needs good initialization, poor for multimodal
   - Best for: Smooth unimodal, correlated parameters, final refinement

3. **GP-BO (Gaussian Process Bayesian Optimization)** - Sample efficient
   - Strengths: Excellent for expensive evaluations, good uncertainty
   - Weaknesses: Scales poorly >10D, expensive surrogate training
   - Best for: Smooth landscapes, low-dimensional, expensive simulations

4. **Random** - Baseline exploration
   - Strengths: No assumptions, good for initial characterization
   - Weaknesses: No learning, very slow convergence
   - Best for: Initial exploration only

5. **Hybrid GP→CMA-ES** (Future) - Best of both worlds
   - GP finds promising basin, CMA-ES refines locally
   - Requires transition logic (planned)

### Critical Implementation Details

**Study-Aware Design**:
- All components use `study.trials` not session history
- Supports interrupted/resumed optimization
- State persisted to JSON files

**Callback Integration**:
```python
# In generated runner
from optimization_engine.intelligent_optimizer import create_intelligent_optimizer

optimizer = create_intelligent_optimizer(
    study_name=study_name,
    study_dir=results_dir,
    verbose=True
)

results = optimizer.optimize(
    objective_function=objective,
    design_variables=design_vars,
    n_trials=100
)
```

**Fallback Behavior**:
If `intelligent_optimization.enabled = false`, falls back to standard TPE optimization.

### When to Use Protocol 10

**ALWAYS use for**:
- New problem types (unknown landscape)
- Production optimization runs (want best performance)
- Studies with >50 trial budget (enough for characterization)

**Consider disabling for**:
- Quick tests (<20 trials)
- Known problem types with proven strategy
- Debugging/development work

### Future Enhancements

**Planned Features**:
1. **Transfer Learning**: Build database of landscape → strategy mappings
2. **Multi-Armed Bandit**: Thompson sampling for strategy portfolio
3. **Hybrid Strategies**: Automatic GP→CMA-ES transitions
4. **Parallel Strategy Testing**: Run multiple strategies concurrently
5. **Meta-Learning**: Learn switching thresholds from historical data

---

## VERSION HISTORY

- **v1.3** (2025-11-19): Added Protocol 10 - Intelligent Multi-Strategy Optimization
  - Automatic landscape characterization
  - Intelligent strategy selection with decision tree
  - Dynamic strategy switching with stagnation detection
  - Comprehensive tracking and transparency
  - Four new modules: landscape_analyzer, strategy_selector, strategy_portfolio, intelligent_optimizer
  - Full integration with Protocols 8 and 9

- **v1.2** (2025-11-19): Overhauled adaptive optimization
  - Fixed critical study-aware bug in confidence tracking
  - Added phase transition persistence and reporting
  - Added confidence progression visualization
  - Integrated Optuna professional visualization library
  - Updated all protocols with study-aware architecture

- **v1.1** (2025-11-19): Added extensibility architecture
  - Hook system specification
  - Extractor library architecture
  - Plugin system design
  - API-ready architecture
  - Configuration hierarchy
  - Future feature foundations

- **v1.0** (2025-11-19): Initial protocol established
  - Intelligent benchmarking workflow
  - Solution discovery and matching
  - Target-matching objective functions
  - Markdown reports with graphs
  - UTF-8 encoding standards
  - Folder structure standards

---

**END OF PROTOCOL**

*This protocol must be followed for ALL Atomizer operations. Deviations require explicit documentation and user approval.*
