Files
Atomizer/docs/04_USER_GUIDES/hybrid_mode.md
Anto01 e3bdb08a22 feat: Major update with validators, skills, dashboard, and docs reorganization
- Add validation framework (config, model, results, study validators)
- Add Claude Code skills (create-study, run-optimization, generate-report,
  troubleshoot, analyze-model)
- Add Atomizer Dashboard (React frontend + FastAPI backend)
- Reorganize docs into structured directories (00-09)
- Add neural surrogate modules and training infrastructure
- Add multi-objective optimization support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 19:23:58 -05:00

13 KiB

Hybrid LLM Mode Guide

Recommended Mode for Development | Phase 3.2 Architecture | November 18, 2025

What is Hybrid Mode?

Hybrid Mode (Mode 2) gives you 90% of the automation with 10% of the complexity. It's the sweet spot between manual configuration and full LLM autonomy.

Why Hybrid Mode?

  • No API Key Required - Use Claude Code/Desktop instead of Claude API
  • 90% Automation - Auto-generates extractors, calculations, and hooks
  • Full Transparency - You see and approve the workflow JSON
  • Production Ready - Uses centralized library system
  • Easy to Upgrade - Can enable full API mode later

How It Works

The Workflow

┌─────────────────────────────────────────────────────────────┐
│ HYBRID MODE - 90% Automation, No API Key                    │
└─────────────────────────────────────────────────────────────┘

1. YOU + CLAUDE CODE:
   ├─ Describe optimization in natural language
   └─ Claude helps create workflow JSON
        ↓
2. SAVE workflow JSON:
   ├─ llm_workflow_config.json
   └─ Contains: design vars, objectives, constraints
        ↓
3. LLMOptimizationRunner:
   ├─ Auto-generates extractors (pyNastran)
   ├─ Auto-generates calculations
   ├─ Auto-generates hooks
   ├─ Adds to core library (deduplication!)
   └─ Runs optimization loop (Optuna)
        ↓
4. RESULTS:
   ├─ optimization_results.json (best design)
   ├─ optimization_history.json (all trials)
   ├─ extractors_manifest.json (what was used)
   └─ Study folder stays clean!

Step-by-Step: Your First Hybrid Optimization

Step 1: Describe Your Optimization to Claude

Example conversation with Claude Code:

YOU: I want to optimize a bracket design.
     - Design variables: wall_thickness (1-5mm), fillet_radius (2-8mm)
     - Objective: minimize mass
     - Constraints: max_stress < 200 MPa, max_displacement < 0.5mm
     - I have a Beam.prt file and Beam_sim1.sim file ready

CLAUDE: I'll help you create the workflow JSON...

Step 2: Claude Creates Workflow JSON

Claude will generate a file like this:

{
  "study_name": "bracket_optimization",
  "optimization_request": "Minimize mass while keeping stress below 200 MPa and displacement below 0.5mm",

  "design_variables": [
    {
      "parameter": "wall_thickness",
      "bounds": [1, 5],
      "description": "Bracket wall thickness in mm"
    },
    {
      "parameter": "fillet_radius",
      "bounds": [2, 8],
      "description": "Fillet radius in mm"
    }
  ],

  "objectives": [
    {
      "name": "mass",
      "goal": "minimize",
      "weight": 1.0,
      "extraction": {
        "action": "extract_mass",
        "domain": "result_extraction",
        "params": {
          "result_type": "mass",
          "metric": "total"
        }
      }
    }
  ],

  "constraints": [
    {
      "name": "max_stress_limit",
      "type": "less_than",
      "threshold": 200,
      "extraction": {
        "action": "extract_von_mises_stress",
        "domain": "result_extraction",
        "params": {
          "result_type": "stress",
          "metric": "max"
        }
      }
    },
    {
      "name": "max_displacement_limit",
      "type": "less_than",
      "threshold": 0.5,
      "extraction": {
        "action": "extract_displacement",
        "domain": "result_extraction",
        "params": {
          "result_type": "displacement",
          "metric": "max"
        }
      }
    }
  ]
}

Step 3: Save and Review

Save the JSON to your study directory:

studies/
  bracket_optimization/
    1_setup/
      model/
        Bracket.prt           # Your NX model
        Bracket_sim1.sim      # Your FEM setup
      workflow_config.json    # ← SAVE HERE

IMPORTANT: Review the JSON before running! Check:

  • Design variable names match your NX expressions
  • Bounds are in correct units (mm not m!)
  • Extraction actions match available OP2 results

Step 4: Run LLMOptimizationRunner

Now the magic happens - 90% automation kicks in:

from pathlib import Path
from optimization_engine.llm_optimization_runner import LLMOptimizationRunner

# Point to your files
study_dir = Path("studies/bracket_optimization")
workflow_json = study_dir / "1_setup/workflow_config.json"
prt_file = study_dir / "1_setup/model/Bracket.prt"
sim_file = study_dir / "1_setup/model/Bracket_sim1.sim"
output_dir = study_dir / "2_substudies/optimization_run_001"

# Create runner
runner = LLMOptimizationRunner(
    llm_workflow_file=workflow_json,
    prt_file=prt_file,
    sim_file=sim_file,
    output_dir=output_dir,
    n_trials=20
)

# Run optimization (this is where automation happens!)
study = runner.run()

print(f"Best design found:")
print(f"  wall_thickness: {study.best_params['wall_thickness']:.2f} mm")
print(f"  fillet_radius: {study.best_params['fillet_radius']:.2f} mm")
print(f"  mass: {study.best_value:.4f} kg")

Step 5: What Gets Auto-Generated

During the run, the system automatically:

  1. Analyzes OP2 structure (pyNastran research agent)
  2. Generates extractors and adds to core library:
    optimization_engine/extractors/
      ├── extract_mass.py              ← Generated!
      ├── extract_von_mises_stress.py  ← Generated!
      └── extract_displacement.py      ← Generated!
    
  3. Creates study manifest (no code pollution!):
    2_substudies/optimization_run_001/
      └── extractors_manifest.json     ← References only
    
  4. Runs optimization loop with Optuna
  5. Saves full audit trail:
    2_substudies/optimization_run_001/
      ├── llm_workflow_config.json     ← What you specified
      ├── extractors_manifest.json     ← What was used
      ├── optimization_results.json    ← Best design
      └── optimization_history.json    ← All trials
    

Real Example: Beam Optimization

Let's walk through the existing beam optimization:

Natural Language Request

"I want to optimize a sandwich beam. Design variables are core thickness (20-30mm), face thickness (1-3mm), hole diameter (180-280mm), and number of holes (8-14). Minimize weight while keeping displacement under 2mm."

Claude Creates JSON

{
  "study_name": "simple_beam_optimization",
  "optimization_request": "Minimize weight subject to max displacement < 2mm",

  "design_variables": [
    {"parameter": "beam_half_core_thickness", "bounds": [20, 30]},
    {"parameter": "beam_face_thickness", "bounds": [1, 3]},
    {"parameter": "holes_diameter", "bounds": [180, 280]},
    {"parameter": "hole_count", "bounds": [8, 14]}
  ],

  "objectives": [
    {
      "name": "mass",
      "goal": "minimize",
      "weight": 1.0,
      "extraction": {
        "action": "extract_mass",
        "domain": "result_extraction",
        "params": {"result_type": "mass", "metric": "total"}
      }
    }
  ],

  "constraints": [
    {
      "name": "max_displacement_limit",
      "type": "less_than",
      "threshold": 2.0,
      "extraction": {
        "action": "extract_displacement",
        "domain": "result_extraction",
        "params": {"result_type": "displacement", "metric": "max"}
      }
    }
  ]
}

Run Script

from pathlib import Path
from optimization_engine.llm_optimization_runner import LLMOptimizationRunner

study_dir = Path("studies/simple_beam_optimization")
runner = LLMOptimizationRunner(
    llm_workflow_file=study_dir / "1_setup/workflow_config.json",
    prt_file=study_dir / "1_setup/model/Beam.prt",
    sim_file=study_dir / "1_setup/model/Beam_sim1.sim",
    output_dir=study_dir / "2_substudies/test_run",
    n_trials=20
)

study = runner.run()

Results After 20 Trials

Best design found:
  beam_half_core_thickness: 27.3 mm
  beam_face_thickness: 2.1 mm
  holes_diameter: 245.2 mm
  hole_count: 11
  mass: 1.234 kg (45% reduction!)
  max_displacement: 1.87 mm (within limit)

Study Folder (Clean!)

2_substudies/test_run/
├── extractors_manifest.json     # Just references
├── llm_workflow_config.json     # What you wanted
├── optimization_results.json    # Best design
└── optimization_history.json    # All 20 trials

Advanced: Extractor Library Reuse

The beauty of centralized library system:

First Optimization Run

# First beam optimization
runner1 = LLMOptimizationRunner(...)
runner1.run()

# Creates:
# optimization_engine/extractors/extract_mass.py
# optimization_engine/extractors/extract_displacement.py

Second Optimization Run (Different Study!)

# Different bracket optimization (but same extractions!)
runner2 = LLMOptimizationRunner(...)
runner2.run()

# REUSES existing extractors!
# No duplicate code generated
# Study folder stays clean

The system automatically detects identical extraction functionality and reuses code from the core library.

Comparison: Three Modes

Feature Manual Mode Hybrid Mode Full LLM Mode
API Key Required No No Yes
Automation Level 0% (you code) 90% (auto-gen) 100% (NL only)
Extractor Generation Manual Auto Auto
Hook Generation Manual Auto Auto
Core Library Manual Auto Auto
Transparency Full Full High
Development Speed Slow Fast Fastest
Production Ready Yes Yes ⚠️ Alpha
Recommended For Complex custom Most users Future

Troubleshooting

Issue: "Expression not found in NX model"

Problem: Design variable name doesn't match NX expression name

Solution:

  1. Open your .prt file in NX
  2. Tools → Expression → check exact names
  3. Update workflow JSON with exact names

Example:

// WRONG
"parameter": "thickness"

// RIGHT (must match NX exactly)
"parameter": "beam_half_core_thickness"

Issue: "No mass results in OP2"

Problem: OP2 file doesn't contain mass data

Solution:

  1. Check what's actually in the OP2:
from pyNastran.op2.op2 import OP2
model = OP2()
model.read_op2('path/to/results.op2')
print(dir(model))  # See available results
  1. Use available result type instead (e.g., von Mises stress, displacement)

Issue: "Extractor generation failed"

Problem: pyNastran research agent couldn't figure out extraction pattern

Solution:

  1. Check optimization_engine/knowledge_base/ for available patterns
  2. Manually create extractor in optimization_engine/extractors/
  3. Reference it in workflow JSON using existing action name

Issue: "Parameter values too extreme"

Problem: Design variables using wrong range (0.2-1.0 instead of 20-30)

Fixed: This was the bug we fixed on Nov 17! Make sure you're using latest code.

Verify:

# Check bounds parsing in llm_optimization_runner.py
if 'bounds' in var_config:
    var_min, var_max = var_config['bounds']  # Should use this!

Tips for Success

1. Start Small

  • First run: 5-10 trials to verify everything works
  • Check results, review auto-generated extractors
  • Then scale up to 50-100 trials

2. Verify Units

  • NX expressions: Check Tools → Expression
  • Workflow JSON: Match units exactly
  • Common mistake: mm vs m, kg vs g

3. Use Existing Examples

  • studies/simple_beam_optimization/ - Working example
  • Copy the structure, modify workflow JSON
  • Reuse proven patterns

4. Review Auto-Generated Code

# After first run, check what was generated:
from optimization_engine.extractor_library import ExtractorLibrary

library = ExtractorLibrary()
print(library.get_library_summary())

5. Leverage Deduplication

  • Same extraction across studies? Library reuses code!
  • No need to regenerate extractors
  • Study folders stay clean automatically

Next Steps

Ready to Test?

  1. Read this guide
  2. Review beam optimization example
  3. Create your workflow JSON with Claude's help
  4. Run your first optimization!

Want Full Automation?

When you're ready for Full LLM Mode (Mode 3):

  1. Set up Claude API key
  2. Use natural language requests (no JSON needed!)
  3. System creates workflow JSON automatically
  4. Everything else identical to Hybrid Mode

Questions?

  • Check docs/ARCHITECTURE_REFACTOR_NOV17.md for library system details
  • Review optimization_engine/llm_optimization_runner.py for implementation
  • Run E2E test: python tests/test_phase_3_2_e2e.py

Status: Production Ready Mode: Hybrid (90% Automation) API Required: No Testing: E2E tests passing (18/18 checks) Architecture: Centralized library with deduplication

Ready to revolutionize your optimization workflow! 🚀