# Generate Report Skill

**Last Updated**: December 3, 2025
**Version**: 1.1 - Optimization Results Analysis and Reporting (Dashboard Integration)

You are helping the user understand and communicate optimization results.

## Purpose

Generate comprehensive reports from completed optimization studies:
1. Pareto front analysis and visualization
2. Design space exploration insights
3. Constraint satisfaction analysis
4. Engineering recommendations
5. Export-ready summaries

## Triggers

- "generate report"
- "show results"
- "analyze the optimization"
- "what did we find?"
- "summarize the study"
- "export results"

## Prerequisites

- Completed optimization study with `study.db`
- At least 10 completed trials (for meaningful analysis)
- Optional: `optimization_config.json` for context

## Report Types

### 1. Quick Summary
Fast overview of key findings (default).

### 2. Full Technical Report
Comprehensive analysis with all visualizations.

### 3. Executive Summary
High-level findings for stakeholders.

### 4. Export Package
Data files for external analysis.

## Execution Steps

### Step 1: Load Study Data

```python
import optuna
import json
from pathlib import Path

def load_study_data(study_name: str):
    """Load all optimization data for analysis."""
    study_dir = Path(f"studies/{study_name}")

    # Load Optuna study
    storage = f"sqlite:///{study_dir / '2_results' / 'study.db'}"
    study = optuna.load_study(study_name=study_name, storage=storage)

    # Load config for context
    config_path = study_dir / "1_setup" / "optimization_config.json"
    config = json.loads(config_path.read_text()) if config_path.exists() else {}

    return {
        'study': study,
        'config': config,
        'n_trials': len(study.trials),
        'n_completed': len([t for t in study.trials if t.state == optuna.trial.TrialState.COMPLETE]),
        'is_multi_objective': len(study.directions) > 1
    }
```

### Step 2: Analyze Results

#### For Multi-Objective (Pareto Analysis)

```python
def analyze_pareto_front(study):
    """Extract and analyze Pareto-optimal solutions."""
    pareto_trials = study.best_trials

    results = {
        'n_pareto': len(pareto_trials),
        'designs': [],
        'ranges': {},
        'trade_offs': []
    }

    for trial in pareto_trials:
        design = {
            'trial_number': trial.number,
            'objectives': trial.values,
            'parameters': trial.params,
            'user_attrs': trial.user_attrs
        }
        results['designs'].append(design)

    # Calculate objective ranges on Pareto front
    if pareto_trials:
        obj_names = ['mass', 'frequency']  # From config
        for i, name in enumerate(obj_names):
            values = [t.values[i] for t in pareto_trials]
            results['ranges'][name] = {
                'min': min(values),
                'max': max(values),
                'spread': max(values) - min(values)
            }

    return results
```

#### For Single-Objective

```python
def analyze_single_objective(study):
    """Analyze single-objective optimization results."""
    best = study.best_trial

    return {
        'best_value': best.value,
        'best_params': best.params,
        'convergence': get_convergence_history(study),
        'improvement_rate': calculate_improvement_rate(study)
    }
```

### Step 3: Constraint Analysis

```python
def analyze_constraints(study, config):
    """Analyze constraint satisfaction across trials."""
    constraints = config.get('constraints', [])

    results = {
        'total_trials': len(study.trials),
        'feasible': 0,
        'infeasible': 0,
        'violations': {}
    }

    for constraint in constraints:
        results['violations'][constraint['name']] = 0

    for trial in study.trials:
        if trial.state != optuna.trial.TrialState.COMPLETE:
            continue

        is_feasible = trial.user_attrs.get('feasible', True)
        if is_feasible:
            results['feasible'] += 1
        else:
            results['infeasible'] += 1
            # Track which constraints were violated
            for constraint in constraints:
                if constraint['name'] in trial.user_attrs.get('violated_constraints', []):
                    results['violations'][constraint['name']] += 1

    results['feasibility_rate'] = results['feasible'] / results['total_trials'] * 100

    return results
```

### Step 4: Generate Visualizations

Describe what plots to generate (actual generation done via Python scripts):

1. **Pareto Front Plot**: Mass vs Frequency with feasibility coloring
2. **Parallel Coordinates**: All design variables with objective coloring
3. **Parameter Importance**: Which variables most affect objectives
4. **Convergence History**: Best value over trials
5. **Design Space Coverage**: Parameter distributions

### Step 5: Generate Report Text

## Output Format: Quick Summary

```
OPTIMIZATION RESULTS: {study_name}
===================================

Study Info:
  Protocol: Protocol 11 (Multi-Objective NSGA-II)
  Trials: 30 completed
  Duration: 14m 23s
  Feasibility Rate: 80%

PARETO FRONT (8 optimal designs)
--------------------------------

| # | Mass (g) | Freq (Hz) | Feasible | Notes |
|---|----------|-----------|----------|-------|
| 1 | 231      | 118       | Yes      | Lightest |
| 2 | 248      | 128       | Yes      | |
| 3 | 265      | 138       | Yes      | Balanced |
| 4 | 282      | 148       | Yes      | |
| 5 | 298      | 156       | Yes      | Stiffest |

Trade-off Summary:
  - Mass range: 231g - 298g (67g spread)
  - Frequency range: 118Hz - 156Hz (38Hz spread)
  - Trade-off rate: ~1.7g per Hz

BEST DESIGNS BY OBJECTIVE
-------------------------

Lightest Design (#1):
  Mass: 231g, Frequency: 118Hz
  Parameters:
    beam_half_core_thickness: 8.2mm
    beam_face_thickness: 1.1mm
    holes_diameter: 42mm
    hole_count: 10

Stiffest Design (#5):
  Mass: 298g, Frequency: 156Hz
  Parameters:
    beam_half_core_thickness: 18.5mm
    beam_face_thickness: 2.8mm
    holes_diameter: 22mm
    hole_count: 6

CONSTRAINT ANALYSIS
-------------------

Constraints checked:
  - max_displacement < 5mm: 24/30 satisfied (80%)
  - max_stress < 200 MPa: 28/30 satisfied (93%)

Most common violation: Displacement (6 trials)
  Occurred when: holes_diameter > 40mm AND face_thickness < 1.5mm

PARAMETER INSIGHTS
------------------

Most influential on mass:
  1. holes_diameter (negative correlation)
  2. beam_face_thickness (positive)
  3. beam_half_core_thickness (positive)

Most influential on frequency:
  1. beam_half_core_thickness (positive)
  2. holes_diameter (negative)
  3. beam_face_thickness (moderate positive)

RECOMMENDATIONS
---------------

1. For lightweight priority:
   - Use larger holes (40-45mm)
   - Minimize face thickness (1.0-1.5mm)
   - Accept frequency around 120Hz

2. For stiffness priority:
   - Use smaller holes (20-25mm)
   - Increase core thickness (18-20mm)
   - Accept mass around 300g

3. Balanced recommendation:
   - Design #3 offers good compromise
   - 265g mass with 138Hz frequency
   - 13% lighter than stiffest, 17% stiffer than lightest

NEXT STEPS
----------

1. Export selected designs: python export_pareto.py --designs 1,3,5
2. Validate in NX: Open model with exported parameters
3. Continue optimization: python run_optimization.py --trials 50 --resume
4. Enable NN acceleration: Add --enable-nn flag for faster exploration
```

## Output Format: Full Technical Report

Generate markdown file with:

```markdown
# {study_name} Optimization Report

Generated: {timestamp}
Protocol: {protocol}

## Executive Summary
[2-3 paragraph overview]

## Study Configuration
### Design Variables
[Table of variables, bounds, types]

### Objectives
[Table of objectives, goals, targets]

### Constraints
[Table of constraints, thresholds]

## Results Analysis

### Pareto Front
[Detailed table of all Pareto-optimal designs]

![Pareto Front](pareto_front.png)

### Design Space Exploration
[Parallel coordinates plot]

![Parallel Coordinates](parallel_coords.png)

### Convergence History
[Plot of best values over trials]

### Parameter Importance
[SHAP or importance analysis]

## Constraint Satisfaction
[Detailed constraint analysis]

## Engineering Recommendations
[Specific design recommendations]

## Appendix
### All Trial Data
[Exportable CSV reference]

### Methodology Notes
[Protocol details, sampler settings]
```

## Export Package

Generate files:
```
{study_name}_export/
├── pareto_designs.csv       # All Pareto-optimal designs
├── all_trials.csv           # Complete trial history
├── optimization_summary.json # Machine-readable summary
├── pareto_front.png         # Visualization
├── parallel_coords.png      # Visualization
└── README.md                # Export documentation
```

## Error Handling

| Error | Recovery |
|-------|----------|
| No completed trials | Suggest running more trials |
| No feasible designs | Analyze constraint violations |
| Study not found | List available studies |
| Incomplete data | Note missing fields |

## Integration with Other Skills

- After report → `/export-results` for specific designs
- If poor results → `/troubleshoot` for analysis
- To continue → `/run-optimization` with --resume

## Example Conversation

```
USER: Show me the results for uav_arm_optimization

CLAUDE: I'll generate a report for the UAV arm optimization study...

[Loads study data]

OPTIMIZATION RESULTS: uav_arm_optimization
==========================================

Found 30 completed trials with 8 Pareto-optimal designs.

PARETO FRONT SUMMARY
--------------------
| Design | Mass (g) | Freq (Hz) | Trade-off |
|--------|----------|-----------|-----------|
| #1     | 231      | 118       | Lightest  |
| #3     | 265      | 138       | Balanced  |
| #5     | 298      | 156       | Stiffest  |

Key Finding: You can save 67g (23%) by accepting 38Hz (24%)
lower frequency.

RECOMMENDATION: Design #3 (265g, 138Hz) offers the best balance
for a camera gimbal application where both vibration and weight
matter.

Would you like me to:
1. Generate a full technical report with visualizations?
2. Export the top designs for validation in NX?
3. Continue optimization to explore more designs?
```

## Dashboard Integration (December 2025)

When a STUDY_REPORT.md file is generated, it can be viewed directly in the Atomizer Dashboard:

1. **Save report to**: `studies/{study_name}/STUDY_REPORT.md` (study root folder)
2. **View in dashboard**: Click "Study Report" button on the dashboard
3. **Features**:
   - Full markdown rendering with proper typography
   - Math equation support via KaTeX (`$...$` inline, `$$...$$` block)
   - Tables, code blocks, task lists
   - Live refresh button for updates during analysis

### Report Format for Dashboard

Use standard markdown with optional LaTeX math:

```markdown
# Study Report: {study_name}

## Summary

The optimization achieved a **{improvement}%** improvement in objective.

## Results

| Trial | Objective | Improvement |
|-------|-----------|-------------|
| 0     | 100.0     | baseline    |
| 10    | 85.5      | 14.5%       |

## Mathematical Formulation

The RMS wavefront error is calculated as:

$$\text{RMS} = \sqrt{\frac{1}{N}\sum_{i=1}^{N}c_i^2}$$

where $c_i$ are the Zernike coefficients.
```

## Notes

- Reports should be actionable, not just data dumps
- Always provide engineering context and recommendations
- Consider the user's stated goals when highlighting results
- Visualizations should be generated via Python scripts
- Export formats should be compatible with common tools (Excel, etc.)
- **Use STUDY_REPORT.md** for dashboard-viewable reports with math support
