Files
Atomizer/.claude/skills/create-study-wizard.md
Antoine 602560c46a feat: Add MLP surrogate with Turbo Mode for 100x faster optimization
Neural Acceleration (MLP Surrogate):
- Add run_nn_optimization.py with hybrid FEA/NN workflow
- MLP architecture: 4-layer (64->128->128->64) with BatchNorm/Dropout
- Three workflow modes:
  - --all: Sequential export->train->optimize->validate
  - --hybrid-loop: Iterative Train->NN->Validate->Retrain cycle
  - --turbo: Aggressive single-best validation (RECOMMENDED)
- Turbo mode: 5000 NN trials + 50 FEA validations in ~12 minutes
- Separate nn_study.db to avoid overloading dashboard

Performance Results (bracket_pareto_3obj study):
- NN prediction errors: mass 1-5%, stress 1-4%, stiffness 5-15%
- Found minimum mass designs at boundary (angle~30deg, thick~30mm)
- 100x speedup vs pure FEA exploration

Protocol Operating System:
- Add .claude/skills/ with Bootstrap, Cheatsheet, Context Loader
- Add docs/protocols/ with operations (OP_01-06) and system (SYS_10-14)
- Update SYS_14_NEURAL_ACCELERATION.md with MLP Turbo Mode docs

NX Automation:
- Add optimization_engine/hooks/ for NX CAD/CAE automation
- Add study_wizard.py for guided study creation
- Fix FEM mesh update: load idealized part before UpdateFemodel()

New Study:
- bracket_pareto_3obj: 3-objective Pareto (mass, stress, stiffness)
- 167 FEA trials + 5000 NN trials completed
- Demonstrates full hybrid workflow

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 20:01:59 -05:00

12 KiB

Create Study Wizard Skill

Version: 3.0 - StudyWizard Integration Last Updated: 2025-12-06

You are helping the user create a complete Atomizer optimization study using the powerful StudyWizard class.


Quick Reference

from optimization_engine.study_wizard import StudyWizard, create_study, list_extractors

# Option 1: One-liner for simple studies
create_study(
    study_name="my_study",
    description="Optimize bracket for stiffness",
    prt_file="path/to/model.prt",
    design_variables=[
        {"parameter": "thickness", "bounds": [5, 20], "units": "mm"}
    ],
    objectives=[
        {"name": "stiffness", "goal": "maximize", "extractor": "extract_displacement"}
    ],
    constraints=[
        {"name": "mass", "type": "less_than", "threshold": 0.5, "extractor": "extract_mass_from_bdf", "units": "kg"}
    ]
)

# Option 2: Step-by-step with full control
wizard = StudyWizard("my_study", "Optimize bracket")
wizard.set_model_files("path/to/model.prt")
wizard.introspect()  # Discover expressions, solutions
wizard.add_design_variable("thickness", bounds=(5, 20), units="mm")
wizard.add_objective("mass", goal="minimize", extractor="extract_mass_from_bdf")
wizard.add_constraint("stress", type="less_than", threshold=250, extractor="extract_solid_stress", units="MPa")
wizard.generate()

Trigger Phrases

Use this skill when user says:

  • "create study", "new study", "set up study", "create optimization"
  • "optimize my [part/model/bracket/component]"
  • "help me minimize [mass/weight/cost]"
  • "help me maximize [stiffness/strength/frequency]"
  • "I want to find the best [design/parameters]"

Workflow Steps

Step 1: Gather Requirements

Ask the user (if not already provided):

  1. Model files: "Where is your NX model? (path to .prt file)"
  2. Optimization goal: "What do you want to optimize?"
    • Minimize mass/weight
    • Maximize stiffness
    • Target a specific frequency
    • Multi-objective trade-off
  3. Constraints: "What limits must be respected?"
    • Max stress < yield/safety factor
    • Max displacement < tolerance
    • Mass budget

Step 2: Introspect Model

from optimization_engine.study_wizard import StudyWizard

wizard = StudyWizard("study_name", "Description")
wizard.set_model_files("path/to/model.prt")
result = wizard.introspect()

# Show user what was found
print(f"Found {len(result.expressions)} expressions:")
for expr in result.expressions[:10]:
    print(f"  {expr['name']}: {expr.get('value', 'N/A')}")

print(f"\nFound {len(result.solutions)} solutions:")
for sol in result.solutions:
    print(f"  {sol['name']}")

# Suggest design variables
suggestions = result.suggest_design_variables()
for s in suggestions:
    print(f"  {s['name']}: {s['current_value']} -> bounds {s['suggested_bounds']}")

Step 3: Configure Study

# Add design variables from introspection suggestions
for dv in selected_design_variables:
    wizard.add_design_variable(
        parameter=dv['name'],
        bounds=dv['bounds'],
        units=dv.get('units', ''),
        description=dv.get('description', '')
    )

# Add objectives
wizard.add_objective(
    name="mass",
    goal="minimize",
    extractor="extract_mass_from_bdf",
    description="Minimize total bracket mass"
)

wizard.add_objective(
    name="stiffness",
    goal="maximize",
    extractor="extract_displacement",
    params={"invert_for_stiffness": True},
    description="Maximize structural stiffness"
)

# Add constraints
wizard.add_constraint(
    name="max_stress",
    constraint_type="less_than",
    threshold=250,
    extractor="extract_solid_stress",
    units="MPa",
    description="Keep stress below yield/4"
)

# Set protocol based on objectives
if len(wizard.objectives) > 1:
    wizard.set_protocol("protocol_11_multi")  # NSGA-II
else:
    wizard.set_protocol("protocol_10_single")  # TPE

wizard.set_trials(100)

Step 4: Generate Study

files = wizard.generate()

print("Study generated successfully!")
print(f"Location: {wizard.study_dir}")
print("\nNext steps:")
print("  1. cd", wizard.study_dir)
print("  2. python run_optimization.py --discover")
print("  3. python run_optimization.py --validate")
print("  4. python run_optimization.py --run --trials 100")

Available Extractors

Extractor What it extracts Input Output
extract_mass_from_bdf Total mass .dat/.bdf kg
extract_part_mass CAD mass .prt kg
extract_displacement Max displacement .op2 mm
extract_solid_stress Von Mises stress .op2 MPa
extract_principal_stress Principal stresses .op2 MPa
extract_strain_energy Strain energy .op2 J
extract_spc_forces Reaction forces .op2 N
extract_frequency Natural frequencies .op2 Hz
get_first_frequency First mode frequency .f06 Hz
extract_temperature Nodal temperatures .op2 K/°C
extract_modal_mass Modal effective mass .f06 kg
extract_zernike_from_op2 Zernike WFE .op2+.bdf nm

List all extractors programmatically:

from optimization_engine.study_wizard import list_extractors
for name, info in list_extractors().items():
    print(f"{name}: {info['description']}")

Common Optimization Patterns

Pattern 1: Minimize Mass with Stress Constraint

create_study(
    study_name="lightweight_bracket",
    description="Minimize mass while keeping stress below yield",
    prt_file="Bracket.prt",
    design_variables=[
        {"parameter": "wall_thickness", "bounds": [2, 10], "units": "mm"},
        {"parameter": "rib_count", "bounds": [2, 8], "units": "count"}
    ],
    objectives=[
        {"name": "mass", "goal": "minimize", "extractor": "extract_mass_from_bdf"}
    ],
    constraints=[
        {"name": "stress", "type": "less_than", "threshold": 250,
         "extractor": "extract_solid_stress", "units": "MPa"}
    ],
    protocol="protocol_10_single"
)

Pattern 2: Multi-Objective Stiffness vs Mass

create_study(
    study_name="pareto_bracket",
    description="Trade-off between stiffness and mass",
    prt_file="Bracket.prt",
    design_variables=[
        {"parameter": "thickness", "bounds": [5, 25], "units": "mm"},
        {"parameter": "support_angle", "bounds": [20, 70], "units": "degrees"}
    ],
    objectives=[
        {"name": "stiffness", "goal": "maximize", "extractor": "extract_displacement"},
        {"name": "mass", "goal": "minimize", "extractor": "extract_mass_from_bdf"}
    ],
    constraints=[
        {"name": "mass_limit", "type": "less_than", "threshold": 0.5,
         "extractor": "extract_mass_from_bdf", "units": "kg"}
    ],
    protocol="protocol_11_multi",
    n_trials=150
)

Pattern 3: Frequency-Targeted Modal Optimization

create_study(
    study_name="modal_bracket",
    description="Tune first natural frequency to target",
    prt_file="Bracket.prt",
    design_variables=[
        {"parameter": "thickness", "bounds": [3, 15], "units": "mm"},
        {"parameter": "length", "bounds": [50, 150], "units": "mm"}
    ],
    objectives=[
        {"name": "frequency_error", "goal": "minimize",
         "extractor": "get_first_frequency",
         "params": {"target": 100}}  # Target 100 Hz
    ],
    constraints=[
        {"name": "mass", "type": "less_than", "threshold": 0.3,
         "extractor": "extract_mass_from_bdf", "units": "kg"}
    ]
)

Pattern 4: Thermal Optimization

create_study(
    study_name="heat_sink",
    description="Minimize max temperature",
    prt_file="HeatSink.prt",
    design_variables=[
        {"parameter": "fin_height", "bounds": [10, 50], "units": "mm"},
        {"parameter": "fin_count", "bounds": [5, 20], "units": "count"}
    ],
    objectives=[
        {"name": "max_temp", "goal": "minimize", "extractor": "get_max_temperature"}
    ],
    constraints=[
        {"name": "mass", "type": "less_than", "threshold": 0.2,
         "extractor": "extract_mass_from_bdf", "units": "kg"}
    ]
)

Protocol Selection Guide

Scenario Protocol Sampler
Single objective protocol_10_single TPESampler
Multiple objectives (Pareto) protocol_11_multi NSGAIISampler
Smooth design space protocol_10_single CmaEsSampler
Discrete variables protocol_10_single TPESampler

Files Generated

The wizard generates a complete study structure:

studies/{study_name}/
├── 1_setup/
│   ├── model/              # NX model files (copied)
│   ├── optimization_config.json
│   └── workflow_config.json
├── 2_results/              # Created on first run
├── run_optimization.py     # Main script with staged workflow
├── reset_study.py          # Reset results
├── README.md               # Engineering documentation
├── STUDY_REPORT.md         # Results tracking template
└── MODEL_INTROSPECTION.md  # Model analysis report

Staged Workflow

After generation, guide user through staged validation:

# Stage 1: Discover model outputs
python run_optimization.py --discover

# Stage 2: Validate with single trial
python run_optimization.py --validate

# Stage 3: Integration test (3 trials)
python run_optimization.py --test

# Stage 4: Full optimization
python run_optimization.py --run --trials 100

# Resume if interrupted
python run_optimization.py --run --trials 50 --resume

Error Handling

Common Issues and Solutions

Error Cause Solution
"Part file not found" Wrong path Use absolute path or relative to studies/
"No expressions found" Part not parametric Add expressions in NX Modeling
"Simulation failed" NX not running Start NX before running
"Extractor not found" Typo in name Use list_extractors() to see valid names

Example Conversation

User: "I want to optimize my bracket for minimum weight while keeping stress low"

Claude: "I'll help you create a weight optimization study. Let me gather some information:

  1. Where is your NX model file?
  2. What's the maximum allowable stress (e.g., yield/4)?
  3. Are there specific parameters you want to vary (thickness, angles, etc.)?"

User: "The model is at studies/my_bracket/model/Bracket.prt. Max stress should be 250 MPa. I want to vary the wall_thickness and support_angle."

Claude: "I'll create the study and introspect your model to confirm the parameters exist."

from optimization_engine.study_wizard import StudyWizard

wizard = StudyWizard(
    study_name="my_bracket_optimization",
    description="Minimize bracket mass with stress constraint"
)

wizard.set_model_files("studies/my_bracket/model/Bracket.prt")

# Introspect to verify parameters
result = wizard.introspect()
print(f"Found expressions: {result.get_expression_names()}")

# Configure
wizard.add_design_variable("wall_thickness", bounds=(2, 15), units="mm")
wizard.add_design_variable("support_angle", bounds=(20, 60), units="degrees")
wizard.add_objective("mass", goal="minimize", extractor="extract_mass_from_bdf")
wizard.add_constraint("stress", constraint_type="less_than", threshold=250,
                      extractor="extract_solid_stress", units="MPa")
wizard.set_trials(100)

files = wizard.generate()

Claude: "Study created! Here's what I generated:

  • studies/my_bracket_optimization/run_optimization.py
  • studies/my_bracket_optimization/README.md
  • Configuration files in 1_setup/

Next steps:

  1. cd studies/my_bracket_optimization
  2. python run_optimization.py --discover
  3. python run_optimization.py --validate
  4. python run_optimization.py --run --trials 100"

Cross-References

  • Extractor Library: docs/protocols/system/SYS_12_EXTRACTOR_LIBRARY.md
  • Protocol 10 (IMSO): docs/protocols/system/SYS_10_IMSO.md
  • Protocol 11 (Multi-Objective): docs/protocols/system/SYS_11_MULTI_OBJECTIVE.md
  • StudyWizard Source: optimization_engine/study_wizard.py