# Neural Workflow Tutorial

**End-to-End Guide: From FEA Data to Neural-Accelerated Optimization**

This tutorial walks you through the complete workflow of setting up neural network acceleration for your optimization studies.

---

## Prerequisites

Before starting, ensure you have:

- [ ] Atomizer installed and working
- [ ] An NX Nastran model with parametric geometry
- [ ] Python environment with PyTorch and PyTorch Geometric
- [ ] GPU recommended (CUDA) but not required

### Install Neural Dependencies

```bash
# Install PyTorch (with CUDA support)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

# Install PyTorch Geometric
pip install torch-geometric

# Install other dependencies
pip install h5py pyNastran
```

---

## Overview

The workflow consists of 5 phases:

```
Phase 1: Initial FEA Study → Collect Training Data
                ↓
Phase 2: Parse Data → Convert BDF/OP2 to Neural Format
                ↓
Phase 3: Train Model → Train GNN on Collected Data
                ↓
Phase 4: Validate → Verify Model Accuracy
                ↓
Phase 5: Deploy → Run Neural-Accelerated Optimization
```

**Time Investment**:
- Phase 1: 4-8 hours (initial FEA runs)
- Phase 2: 30 minutes (parsing)
- Phase 3: 30-60 minutes (training)
- Phase 4: 10 minutes (validation)
- Phase 5: Minutes instead of hours!

---

## Phase 1: Collect Training Data

### Step 1.1: Configure Training Data Export

Edit your `workflow_config.json` to enable training data export:

```json
{
  "study_name": "uav_arm_optimization",

  "design_variables": [
    {
      "name": "beam_half_core_thickness",
      "expression_name": "beam_half_core_thickness",
      "min": 5.0,
      "max": 15.0,
      "units": "mm"
    },
    {
      "name": "beam_face_thickness",
      "expression_name": "beam_face_thickness",
      "min": 1.0,
      "max": 5.0,
      "units": "mm"
    },
    {
      "name": "holes_diameter",
      "expression_name": "holes_diameter",
      "min": 20.0,
      "max": 50.0,
      "units": "mm"
    },
    {
      "name": "hole_count",
      "expression_name": "hole_count",
      "min": 5,
      "max": 15,
      "units": ""
    }
  ],

  "objectives": [
    {"name": "mass", "direction": "minimize"},
    {"name": "frequency", "direction": "maximize"},
    {"name": "max_displacement", "direction": "minimize"},
    {"name": "max_stress", "direction": "minimize"}
  ],

  "training_data_export": {
    "enabled": true,
    "export_dir": "atomizer_field_training_data/uav_arm"
  },

  "optimization_settings": {
    "n_trials": 50,
    "sampler": "TPE"
  }
}
```

### Step 1.2: Run Initial Optimization

```bash
cd studies/uav_arm_optimization
python run_optimization.py --trials 50
```

This will:
1. Run 50 FEA simulations
2. Export each trial's BDF and OP2 files
3. Save design parameters and objectives

**Expected output**:
```
Trial 1/50: beam_half_core_thickness=10.2, beam_face_thickness=2.8...
  → Exporting training data to atomizer_field_training_data/uav_arm/trial_0001/
Trial 2/50: beam_half_core_thickness=7.5, beam_face_thickness=3.1...
  → Exporting training data to atomizer_field_training_data/uav_arm/trial_0002/
...
```

### Step 1.3: Verify Exported Data

Check the exported data structure:

```bash
ls atomizer_field_training_data/uav_arm/
```

Expected:
```
trial_0001/
trial_0002/
...
trial_0050/
study_summary.json
README.md
```

Each trial folder contains:
```
trial_0001/
├── input/
│   └── model.bdf      # Nastran input deck
├── output/
│   └── model.op2      # Binary results
└── metadata.json      # Design variables and objectives
```

---

## Phase 2: Parse Data

### Step 2.1: Navigate to AtomizerField

```bash
cd atomizer-field
```

### Step 2.2: Parse All Cases

```bash
python batch_parser.py ../atomizer_field_training_data/uav_arm
```

**What this does**:
1. Reads each BDF file (mesh, materials, BCs, loads)
2. Reads each OP2 file (displacement, stress, strain fields)
3. Converts to HDF5 + JSON format
4. Validates physics consistency

**Expected output**:
```
Processing 50 cases...
[1/50] trial_0001: ✓ Parsed successfully (2.3s)
[2/50] trial_0002: ✓ Parsed successfully (2.1s)
...
[50/50] trial_0050: ✓ Parsed successfully (2.4s)

Summary:
├── Successful: 50/50
├── Failed: 0
└── Total time: 115.2s
```

### Step 2.3: Validate Parsed Data

Run validation on a few cases:

```bash
python validate_parsed_data.py ../atomizer_field_training_data/uav_arm/trial_0001
```

**Expected output**:
```
Validation Results for trial_0001:
├── File Structure: ✓ Valid
├── Mesh Quality: ✓ Valid (15,432 nodes, 8,765 elements)
├── Material Properties: ✓ Valid (E=70 GPa, nu=0.33)
├── Boundary Conditions: ✓ Valid (12 fixed nodes)
├── Load Data: ✓ Valid (1 gravity load)
├── Displacement Field: ✓ Valid (max: 0.042 mm)
├── Stress Field: ✓ Valid (max: 125.3 MPa)
└── Overall: ✓ VALID
```

---

## Phase 3: Train Model

### Step 3.1: Split Data

Create train/validation split:

```bash
# Create directories
mkdir -p ../atomizer_field_training_data/uav_arm_train
mkdir -p ../atomizer_field_training_data/uav_arm_val

# Move 80% to train, 20% to validation
# (You can write a script or do this manually)
```

### Step 3.2: Train Parametric Model

```bash
python train_parametric.py \
  --train_dir ../atomizer_field_training_data/uav_arm_train \
  --val_dir ../atomizer_field_training_data/uav_arm_val \
  --epochs 200 \
  --hidden_channels 128 \
  --num_layers 4 \
  --learning_rate 0.001 \
  --output_dir runs/my_uav_model
```

**What this does**:
1. Loads parsed training data
2. Builds design-conditioned GNN
3. Trains with physics-informed loss
4. Saves best checkpoint based on validation loss

**Expected output**:
```
Training Parametric GNN
├── Training samples: 40
├── Validation samples: 10
├── Model parameters: 523,412

Epoch [1/200]:
├── Train Loss: 0.3421
├── Val Loss: 0.2987
└── Best model saved!

Epoch [50/200]:
├── Train Loss: 0.0234
├── Val Loss: 0.0312

Epoch [200/200]:
├── Train Loss: 0.0089
├── Val Loss: 0.0156
└── Training complete!

Best validation loss: 0.0142 (epoch 187)
Model saved to: runs/my_uav_model/checkpoint_best.pt
```

### Step 3.3: Monitor Training (Optional)

If TensorBoard is installed:

```bash
tensorboard --logdir runs/my_uav_model/logs
```

Open http://localhost:6006 to view:
- Loss curves
- Learning rate schedule
- Validation metrics

---

## Phase 4: Validate Model

### Step 4.1: Run Validation Script

```bash
python validate.py --checkpoint runs/my_uav_model/checkpoint_best.pt
```

**Expected output**:
```
Model Validation Results
========================

Per-Objective Metrics:
├── mass:
│   ├── MAE: 0.52 g
│   ├── MAPE: 0.8%
│   └── R²: 0.998
├── frequency:
│   ├── MAE: 2.1 Hz
│   ├── MAPE: 1.2%
│   └── R²: 0.995
├── max_displacement:
│   ├── MAE: 0.001 mm
│   ├── MAPE: 2.8%
│   └── R²: 0.987
└── max_stress:
    ├── MAE: 3.2 MPa
    ├── MAPE: 3.5%
    └── R²: 0.981

Performance:
├── Inference time: 4.5 ms ± 0.8 ms
├── GPU memory: 512 MB
└── Throughput: 220 predictions/sec

✓ Model validation passed!
```

### Step 4.2: Test on New Designs

```python
# test_model.py
import torch
from atomizer_field.neural_models.parametric_predictor import ParametricFieldPredictor

# Load model
checkpoint = torch.load('runs/my_uav_model/checkpoint_best.pt')
model = ParametricFieldPredictor(**checkpoint['config'])
model.load_state_dict(checkpoint['model_state_dict'])
model.eval()

# Test prediction
design = {
    'beam_half_core_thickness': 7.0,
    'beam_face_thickness': 2.5,
    'holes_diameter': 35.0,
    'hole_count': 10.0
}

# Convert to tensor
design_tensor = torch.tensor([[
    design['beam_half_core_thickness'],
    design['beam_face_thickness'],
    design['holes_diameter'],
    design['hole_count']
]])

# Predict
with torch.no_grad():
    predictions = model(design_tensor)

print(f"Mass: {predictions[0, 0]:.2f} g")
print(f"Frequency: {predictions[0, 1]:.2f} Hz")
print(f"Displacement: {predictions[0, 2]:.6f} mm")
print(f"Stress: {predictions[0, 3]:.2f} MPa")
```

---

## Phase 5: Deploy Neural-Accelerated Optimization

### Step 5.1: Update Configuration

Edit `workflow_config.json` to enable neural acceleration:

```json
{
  "study_name": "uav_arm_optimization_neural",

  "neural_surrogate": {
    "enabled": true,
    "model_checkpoint": "atomizer-field/runs/my_uav_model/checkpoint_best.pt",
    "confidence_threshold": 0.85,
    "device": "cuda"
  },

  "hybrid_optimization": {
    "enabled": true,
    "exploration_trials": 20,
    "validation_frequency": 50
  },

  "optimization_settings": {
    "n_trials": 5000
  }
}
```

### Step 5.2: Run Neural-Accelerated Optimization

```bash
python run_optimization.py --trials 5000 --use-neural
```

**Expected output**:
```
Neural-Accelerated Optimization
===============================

Loading neural model from: atomizer-field/runs/my_uav_model/checkpoint_best.pt
Model loaded successfully (4.5 ms inference time)

Phase 1: Exploration (FEA)
Trial [1/5000]: Using FEA (exploration phase)
Trial [2/5000]: Using FEA (exploration phase)
...
Trial [20/5000]: Using FEA (exploration phase)

Phase 2: Exploitation (Neural)
Trial [21/5000]: Using Neural (conf: 94.2%, time: 4.8 ms)
Trial [22/5000]: Using Neural (conf: 91.8%, time: 4.3 ms)
...
Trial [5000/5000]: Using Neural (conf: 93.1%, time: 4.6 ms)

============================================================
OPTIMIZATION COMPLETE
============================================================
Total trials: 5,000
├── FEA trials: 120 (2.4%)
├── Neural trials: 4,880 (97.6%)
├── Total time: 8.3 minutes
├── Equivalent FEA time: 14.2 hours
└── Speedup: 103x

Best Design Found:
├── beam_half_core_thickness: 6.8 mm
├── beam_face_thickness: 2.3 mm
├── holes_diameter: 32.5 mm
├── hole_count: 12

Objectives:
├── mass: 45.2 g (minimized)
├── frequency: 312.5 Hz (maximized)
├── max_displacement: 0.028 mm
└── max_stress: 89.3 MPa
============================================================
```

### Step 5.3: Validate Best Designs

Run FEA validation on top designs:

```python
# validate_best_designs.py
from optimization_engine.runner import OptimizationRunner

runner = OptimizationRunner(config_path="workflow_config.json")

# Get top 10 designs from neural optimization
top_designs = runner.get_best_trials(10)

print("Validating top 10 designs with FEA...")
for i, design in enumerate(top_designs):
    # Run actual FEA
    fea_result = runner.run_fea_simulation(design.params)
    nn_result = design.values

    # Compare
    mass_error = abs(fea_result['mass'] - nn_result['mass']) / fea_result['mass'] * 100
    freq_error = abs(fea_result['frequency'] - nn_result['frequency']) / fea_result['frequency'] * 100

    print(f"Design {i+1}: Mass error={mass_error:.1f}%, Freq error={freq_error:.1f}%")
```

---

## Troubleshooting

### Common Issues

**Issue: Low confidence predictions**
```
WARNING: Neural confidence below threshold (65.3% < 85%)
```

**Solution**:
- Collect more diverse training data
- Train for more epochs
- Reduce confidence threshold
- Check if design is outside training distribution

**Issue: Training loss not decreasing**
```
Epoch [100/200]: Train Loss: 0.3421 (same as epoch 1)
```

**Solution**:
- Reduce learning rate
- Check data preprocessing
- Increase hidden channels
- Add more training data

**Issue: Large validation error**
```
Val MAE: 15.2% (expected < 5%)
```

**Solution**:
- Check for data leakage
- Add regularization (dropout)
- Use physics-informed loss
- Collect more training data

---

## Best Practices

### Data Collection

1. **Diverse sampling**: Use Latin Hypercube or Sobol sequences
2. **Sufficient quantity**: Aim for 10-20x the number of design variables
3. **Full range coverage**: Ensure designs span the entire design space
4. **Quality control**: Validate all FEA results before training

### Training

1. **Start simple**: Begin with smaller models, increase if needed
2. **Use validation**: Always monitor validation loss
3. **Early stopping**: Stop training when validation loss plateaus
4. **Save checkpoints**: Keep intermediate models

### Deployment

1. **Conservative thresholds**: Start with high confidence (0.9)
2. **Periodic validation**: Always validate with FEA periodically
3. **Monitor drift**: Track prediction accuracy over time
4. **Retrain**: Update model when drift is detected

---

## Next Steps

After completing this tutorial, explore:

1. **[Neural Features Complete](NEURAL_FEATURES_COMPLETE.md)** - Advanced features
2. **[GNN Architecture](GNN_ARCHITECTURE.md)** - Technical deep-dive
3. **[Physics Loss Guide](PHYSICS_LOSS_GUIDE.md)** - Loss function selection

---

## Summary

You've learned how to:

- [x] Configure training data export
- [x] Collect training data from FEA
- [x] Parse BDF/OP2 to neural format
- [x] Train a parametric GNN
- [x] Validate model accuracy
- [x] Deploy neural-accelerated optimization

**Result**: 1000x faster optimization with <5% prediction error!

---

*Questions? See the [troubleshooting section](#troubleshooting) or check the [main documentation](../README.md).*
