# Analyze Model Skill

**Last Updated**: December 6, 2025
**Version**: 2.0 - Added Comprehensive Model Introspection

You are helping the user understand their NX model's structure and identify optimization opportunities.

## Purpose

Extract and present information about an NX model to help the user:
1. Identify available parametric expressions (potential design variables)
2. Understand the simulation setup (analysis types, boundary conditions)
3. Discover material properties
4. Identify extractable results from OP2 files
5. Recommend optimization strategies based on model characteristics

## Triggers

- "analyze this model"
- "what can I optimize"
- "show me the expressions"
- "look at my NX model"
- "what parameters are available"
- "introspect my model"
- "what results are available"

## Prerequisites

- User must provide path to NX model files (.prt, .sim, .fem) or study directory
- NX must be available on the system for part/sim introspection
- OP2 introspection works without NX (pure Python)

## Information Gathering

Ask these questions if not already provided:

1. **Model Location**:
   - "Where is your NX model? (path to .prt file or study directory)"
   - Default: Look in `studies/*/1_setup/model/`

2. **Analysis Interest**:
   - "What type of optimization are you considering?" (optional)
   - This helps focus the analysis on relevant aspects

---

## MANDATORY: Model Introspection

**ALWAYS use the introspection module for comprehensive model analysis:**

```python
from optimization_engine.hooks.nx_cad.model_introspection import (
    introspect_part,
    introspect_simulation,
    introspect_op2,
    introspect_study
)

# Option 1: Introspect entire study directory (recommended)
study_info = introspect_study("studies/my_study/")

# Option 2: Introspect individual files
part_info = introspect_part("path/to/model.prt")
sim_info = introspect_simulation("path/to/model.sim")
op2_info = introspect_op2("path/to/results.op2")
```

### What Introspection Extracts

| Source | Information Extracted |
|--------|----------------------|
| `.prt` | Expressions (count, values, types), bodies, mass, material, features |
| `.sim` | Solutions, boundary conditions, loads, materials, mesh info, output requests |
| `.op2` | Available results (displacement, stress, strain, SPC forces, etc.), subcases |

### Introspection Report Generation

**MANDATORY**: Generate `MODEL_INTROSPECTION.md` for every study:

```python
# Generate and save introspection report
study_info = introspect_study(study_dir)

# Create markdown report
report = generate_introspection_report(study_info)
with open(study_dir / "MODEL_INTROSPECTION.md", "w") as f:
    f.write(report)
```

---

## Execution Steps

### Step 1: Run Comprehensive Introspection

**Use the introspection module (MANDATORY)**:

```python
from optimization_engine.hooks.nx_cad.model_introspection import introspect_study

# Introspect the entire study
result = introspect_study("studies/my_study/")

if result["success"]:
    # Part information
    for part in result["data"]["parts"]:
        print(f"Part: {part['file']}")
        print(f"  Expressions: {part['data'].get('expression_count', 0)}")
        print(f"  Bodies: {part['data'].get('body_count', 0)}")

    # Simulation information
    for sim in result["data"]["simulations"]:
        print(f"Simulation: {sim['file']}")
        print(f"  Solutions: {sim['data'].get('solution_count', 0)}")

    # OP2 results
    for op2 in result["data"]["results"]:
        print(f"OP2: {op2['file']}")
        available = op2['data'].get('available_results', {})
        print(f"  Displacement: {available.get('displacement', False)}")
        print(f"  Stress: {available.get('stress', False)}")
```

### Step 2: Validate Model Files

Check that required files exist:

```python
from pathlib import Path

def validate_model_files(model_path: Path) -> dict:
    """Validate NX model files exist."""
    prt_file = model_path
    base_name = prt_file.stem
    parent_dir = prt_file.parent

    result = {
        'prt_exists': prt_file.exists(),
        'sim_file': None,
        'fem_file': None,
        'errors': []
    }

    # Look for simulation file
    sim_patterns = [
        f"{base_name}_sim1.sim",
        f"{base_name}_sim.sim",
        f"{base_name}.sim"
    ]
    for pattern in sim_patterns:
        sim_path = parent_dir / pattern
        if sim_path.exists():
            result['sim_file'] = sim_path
            break

    # Look for FEM file
    fem_patterns = [
        f"{base_name}_fem1.fem",
        f"{base_name}_fem.fem",
        f"{base_name}.fem"
    ]
    for pattern in fem_patterns:
        fem_path = parent_dir / pattern
        if fem_path.exists():
            result['fem_file'] = fem_path
            break

    if not result['prt_exists']:
        result['errors'].append(f"Part file not found: {prt_file}")
    if not result['sim_file']:
        result['errors'].append("No simulation file (.sim) found")
    if not result['fem_file']:
        result['errors'].append("No FEM file (.fem) found - will be created on first solve")

    return result
```

### Step 3: Extract Expressions (via Introspection)

The introspection module extracts expressions automatically:

```python
from optimization_engine.hooks.nx_cad.model_introspection import introspect_part

result = introspect_part("path/to/model.prt")
if result["success"]:
    expressions = result["data"].get("expressions", [])
    for expr in expressions:
        print(f"  {expr['name']}: {expr['value']} {expr.get('unit', '')}")
```

### Step 4: Classify Expressions

Categorize expressions by likely purpose:

| Pattern | Category | Optimization Relevance |
|---------|----------|----------------------|
| `*_thickness`, `*_t`, `wall_*` | Structural | High - affects mass & stiffness |
| `*_diameter`, `*_d`, `hole_*` | Geometric | High - affects mass & stress |
| `*_radius`, `*_r`, `fillet_*` | Geometric | Medium - affects stress concentration |
| `*_count`, `n_*`, `num_*` | Discrete | Medium - affects mass & complexity |
| `*_length`, `*_l`, `span_*` | Dimensional | Context-dependent |
| `*_angle`, `*_deg` | Orientation | Low-Medium |
| `material_*`, `mat_*` | Material | Special handling required |

### Step 4: Analyze Simulation Setup

If .sim file exists, analyze:

1. **Solution Types**:
   - SOL 101 (Static) → Displacement, Stress
   - SOL 103 (Modal) → Natural Frequencies
   - SOL 105 (Buckling) → Buckling Load Factor
   - SOL 106 (Nonlinear Static) → Large deformation
   - SOL 112 (Transient) → Dynamic response

2. **Load Cases**: Identify applied loads and boundary conditions

3. **Material Properties**: Extract material definitions

### Step 5: Generate Recommendations

Based on analysis, recommend:

1. **Design Variables**: Which expressions are good candidates
2. **Objectives**: What can be optimized
3. **Constraints**: What should be bounded
4. **Protocol**: Which optimization protocol fits best

## Output Format

Present analysis using the **MODEL_INTROSPECTION.md** format:

```markdown
# Model Introspection Report

**Study**: {study_name}
**Generated**: {date}
**Introspection Version**: 1.0

---

## 1. Files Discovered

| Type | File | Status |
|------|------|--------|
| Part (.prt) | {prt_file} | ✓ Found |
| Simulation (.sim) | {sim_file} | ✓ Found |
| FEM (.fem) | {fem_file} | ✓ Found |
| Results (.op2) | {op2_file} | ✓ Found |

---

## 2. Part Information

### Expressions (Potential Design Variables)

| Name | Value | Unit | Type | Optimization Candidate |
|------|-------|------|------|------------------------|
| thickness | 2.0 | mm | User | ✓ High |
| hole_diameter | 10.0 | mm | User | ✓ High |
| p173_mass | 0.125 | kg | Reference | Read-only |

### Mass Properties

| Property | Value | Unit |
|----------|-------|------|
| Mass | 0.125 | kg |
| Material | Aluminum 6061-T6 | - |

---

## 3. Simulation Information

### Solutions

| Solution | Type | Nastran SOL | Status |
|----------|------|-------------|--------|
| Solution 1 | Static | SOL 101 | ✓ Active |
| Solution 2 | Modal | SOL 103 | ✓ Active |

### Boundary Conditions

| Name | Type | Applied To |
|------|------|------------|
| Fixed_Root | SPC | Face_1 |

### Loads

| Name | Type | Magnitude | Direction |
|------|------|-----------|-----------|
| Tip_Force | FORCE | 500 N | -Z |

---

## 4. Available Results (from OP2)

| Result Type | Available | Subcases |
|-------------|-----------|----------|
| Displacement | ✓ | 1 |
| SPC Forces | ✓ | 1 |
| Stress (CHEXA) | ✓ | 1 |
| Stress (CPENTA) | ✓ | 1 |
| Strain Energy | ✗ | - |
| Frequencies | ✓ | 2 |

---

## 5. Optimization Recommendations

### Suggested Objectives

| Objective | Extractor | Source |
|-----------|-----------|--------|
| Minimize mass | E4: `extract_mass_from_bdf` | .dat |
| Maximize stiffness | E1: `extract_displacement` → k=F/δ | .op2 |

### Suggested Constraints

| Constraint | Type | Threshold | Extractor |
|------------|------|-----------|-----------|
| Max stress | less_than | 250 MPa | E3: `extract_solid_stress` |

### Recommended Protocol

- **Protocol 11 (Multi-Objective NSGA-II)** - Multiple competing objectives
- Multi-Solution: **Yes** (static + modal)

---

*Ready to create optimization study? Say "create study" to proceed.*
```

### Saving the Report

**MANDATORY**: Save the introspection report to the study directory:

```python
from pathlib import Path

def save_introspection_report(study_dir: Path, report_content: str):
    """Save MODEL_INTROSPECTION.md to study directory."""
    report_path = study_dir / "MODEL_INTROSPECTION.md"
    with open(report_path, 'w') as f:
        f.write(report_content)
    print(f"Saved introspection report: {report_path}")

## Error Handling

| Error | Recovery |
|-------|----------|
| File not found | Ask user for correct path |
| Invalid NX format | Suggest checking NX version compatibility |
| No expressions found | Guide user to create expressions in NX |
| Simulation incomplete | List missing components |

## Integration with Other Skills

After analysis, can chain to:
- `/create-study` - Create optimization based on findings
- `/configure-surrogate` - Setup NN if large-scale optimization needed

## Example Conversation

```
USER: Look at my NX model at studies/uav_arm_optimization/1_setup/model/Beam.prt

CLAUDE: I'll analyze your UAV arm model. Let me examine its structure...

[Runs analysis]

MODEL ANALYSIS REPORT
=====================
Model: Beam
Location: studies/uav_arm_optimization/1_setup/model/

PARAMETRIC EXPRESSIONS
----------------------
Found 4 controllable parameters:
| Name | Value | Bounds Suggestion |
|------|-------|------------------|
| beam_half_core_thickness | 15.0 mm | [5, 25] |
| beam_face_thickness | 2.0 mm | [1, 5] |
| holes_diameter | 30.0 mm | [10, 50] |
| hole_count | 8 | [4, 12] |

SIMULATION SETUP
----------------
✓ Static Analysis (SOL 101) - for stress/displacement
✓ Modal Analysis (SOL 103) - for natural frequencies
Material: Aluminum sandwich panel

RECOMMENDATION
--------------
This model is well-suited for multi-objective optimization:
- Objective 1: Minimize mass
- Objective 2: Maximize first natural frequency
- Constraint: Keep stress below yield strength

Would you like me to create an optimization study with these settings?

USER: Yes, but add a displacement constraint of 5mm max

CLAUDE: [Invokes create-study skill with analyzed parameters + user constraint]
```

## Notes

- Expression extraction requires NX to be running or using file parsing
- Some expressions may be formula-driven (not directly editable)
- Material properties may need separate handling
- Consider mesh quality when recommending optimization bounds
