feat: Implement Agentic Architecture for robust session workflows
Phase 1 - Session Bootstrap: - Add .claude/ATOMIZER_CONTEXT.md as single entry point for new sessions - Add study state detection and task routing Phase 2 - Code Deduplication: - Add optimization_engine/base_runner.py (ConfigDrivenRunner) - Add optimization_engine/generic_surrogate.py (ConfigDrivenSurrogate) - Add optimization_engine/study_state.py for study detection - Add optimization_engine/templates/ with registry and templates - Studies now require ~50 lines instead of ~300 Phase 3 - Skill Consolidation: - Add YAML frontmatter metadata to all skills (versioning, dependencies) - Consolidate create-study.md into core/study-creation-core.md - Update 00_BOOTSTRAP.md, 01_CHEATSHEET.md, 02_CONTEXT_LOADER.md Phase 4 - Self-Expanding Knowledge: - Add optimization_engine/auto_doc.py for auto-generating documentation - Generate docs/generated/EXTRACTORS.md (27 extractors documented) - Generate docs/generated/TEMPLATES.md (6 templates) - Generate docs/generated/EXTRACTOR_CHEATSHEET.md Phase 5 - Subagent Implementation: - Add .claude/commands/study-builder.md (create studies) - Add .claude/commands/nx-expert.md (NX Open API) - Add .claude/commands/protocol-auditor.md (config validation) - Add .claude/commands/results-analyzer.md (results analysis) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
183
optimization_engine/templates/__init__.py
Normal file
183
optimization_engine/templates/__init__.py
Normal file
@@ -0,0 +1,183 @@
|
||||
"""
|
||||
Template Registry for Atomizer
|
||||
|
||||
Provides study templates for common optimization scenarios.
|
||||
Used by Claude to quickly create new studies via wizard-driven workflow.
|
||||
"""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
|
||||
REGISTRY_PATH = Path(__file__).parent / "registry.json"
|
||||
|
||||
|
||||
def load_registry() -> Dict[str, Any]:
|
||||
"""Load the template registry."""
|
||||
with open(REGISTRY_PATH, 'r') as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def list_templates() -> List[Dict[str, Any]]:
|
||||
"""List all available templates with summary info."""
|
||||
registry = load_registry()
|
||||
templates = []
|
||||
|
||||
for t in registry["templates"]:
|
||||
templates.append({
|
||||
"id": t["id"],
|
||||
"name": t["name"],
|
||||
"description": t["description"],
|
||||
"category": t["category"],
|
||||
"n_objectives": len(t["objectives"]),
|
||||
"turbo_suitable": t.get("turbo_suitable", False),
|
||||
"example_study": t.get("example_study")
|
||||
})
|
||||
|
||||
return templates
|
||||
|
||||
|
||||
def get_template(template_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get a specific template by ID."""
|
||||
registry = load_registry()
|
||||
|
||||
for t in registry["templates"]:
|
||||
if t["id"] == template_id:
|
||||
return t
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_templates_by_category(category: str) -> List[Dict[str, Any]]:
|
||||
"""Get all templates in a category."""
|
||||
registry = load_registry()
|
||||
|
||||
return [t for t in registry["templates"] if t["category"] == category]
|
||||
|
||||
|
||||
def list_categories() -> Dict[str, Dict[str, str]]:
|
||||
"""List all template categories."""
|
||||
registry = load_registry()
|
||||
return registry.get("categories", {})
|
||||
|
||||
|
||||
def get_extractor_info(extractor_id: str) -> Optional[Dict[str, Any]]:
|
||||
"""Get information about a specific extractor."""
|
||||
registry = load_registry()
|
||||
return registry.get("extractors", {}).get(extractor_id)
|
||||
|
||||
|
||||
def suggest_template(
|
||||
n_objectives: int = 1,
|
||||
physics_type: str = "structural",
|
||||
element_types: Optional[List[str]] = None
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
Suggest a template based on problem characteristics.
|
||||
|
||||
Args:
|
||||
n_objectives: Number of objectives (1 = single, 2+ = multi)
|
||||
physics_type: Type of physics (structural, dynamics, optics, multiphysics)
|
||||
element_types: List of element types in the mesh
|
||||
|
||||
Returns:
|
||||
Best matching template or None
|
||||
"""
|
||||
registry = load_registry()
|
||||
candidates = []
|
||||
|
||||
for t in registry["templates"]:
|
||||
score = 0
|
||||
|
||||
# Match number of objectives
|
||||
t_obj = len(t["objectives"])
|
||||
if n_objectives == 1 and t_obj == 1:
|
||||
score += 10
|
||||
elif n_objectives > 1 and t_obj > 1:
|
||||
score += 10
|
||||
|
||||
# Match category
|
||||
if t["category"] == physics_type:
|
||||
score += 20
|
||||
|
||||
# Match element types
|
||||
if element_types:
|
||||
t_elements = set(t.get("element_types", []))
|
||||
user_elements = set(element_types)
|
||||
if t_elements & user_elements:
|
||||
score += 15
|
||||
if "CQUAD4" in user_elements and "shell" in t["id"].lower():
|
||||
score += 10
|
||||
|
||||
if score > 0:
|
||||
candidates.append((score, t))
|
||||
|
||||
if not candidates:
|
||||
return None
|
||||
|
||||
# Sort by score descending
|
||||
candidates.sort(key=lambda x: x[0], reverse=True)
|
||||
return candidates[0][1]
|
||||
|
||||
|
||||
def format_template_summary(template: Dict[str, Any]) -> str:
|
||||
"""Format a template as a human-readable summary."""
|
||||
lines = [
|
||||
f"**{template['name']}**",
|
||||
f"_{template['description']}_",
|
||||
"",
|
||||
f"**Category**: {template['category']}",
|
||||
f"**Solver**: {template.get('solver', 'SOL 101')}",
|
||||
"",
|
||||
"**Objectives**:"
|
||||
]
|
||||
|
||||
for obj in template["objectives"]:
|
||||
lines.append(f" - {obj['name']} ({obj['direction']}) → Extractor {obj['extractor']}")
|
||||
|
||||
lines.append("")
|
||||
lines.append("**Recommended Trials**:")
|
||||
trials = template.get("recommended_trials", {})
|
||||
for phase, count in trials.items():
|
||||
lines.append(f" - {phase}: {count}")
|
||||
|
||||
if template.get("turbo_suitable"):
|
||||
lines.append("")
|
||||
lines.append("✅ **Turbo Mode**: Suitable for neural acceleration")
|
||||
|
||||
if template.get("notes"):
|
||||
lines.append("")
|
||||
lines.append(f"⚠️ **Note**: {template['notes']}")
|
||||
|
||||
if template.get("example_study"):
|
||||
lines.append("")
|
||||
lines.append(f"📁 **Example**: studies/{template['example_study']}/")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def get_wizard_questions(template_id: str) -> List[Dict[str, Any]]:
|
||||
"""Get wizard questions for a template."""
|
||||
template = get_template(template_id)
|
||||
if not template:
|
||||
return []
|
||||
return template.get("wizard_questions", [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Demo: list all templates
|
||||
print("=== Atomizer Template Registry ===\n")
|
||||
|
||||
for category_id, category in list_categories().items():
|
||||
print(f"{category['icon']} {category['name']}")
|
||||
print(f" {category['description']}\n")
|
||||
|
||||
print("\n=== Available Templates ===\n")
|
||||
|
||||
for t in list_templates():
|
||||
status = "🚀" if t["turbo_suitable"] else "📊"
|
||||
print(f"{status} {t['name']} ({t['id']})")
|
||||
print(f" {t['description']}")
|
||||
print(f" Objectives: {t['n_objectives']} | Example: {t['example_study'] or 'N/A'}")
|
||||
print()
|
||||
28
optimization_engine/templates/__main__.py
Normal file
28
optimization_engine/templates/__main__.py
Normal file
@@ -0,0 +1,28 @@
|
||||
"""
|
||||
CLI for the Atomizer Template Registry.
|
||||
"""
|
||||
|
||||
from . import list_templates, list_categories, format_template_summary, get_template
|
||||
|
||||
|
||||
def main():
|
||||
print("=== Atomizer Template Registry ===\n")
|
||||
|
||||
for category_id, category in list_categories().items():
|
||||
# Use ASCII-safe icons for Windows compatibility
|
||||
icon = "[" + category_id[:3].upper() + "]"
|
||||
print(f"{icon} {category['name']}")
|
||||
print(f" {category['description']}\n")
|
||||
|
||||
print("\n=== Available Templates ===\n")
|
||||
|
||||
for t in list_templates():
|
||||
status = "[TURBO]" if t["turbo_suitable"] else "[FEA]"
|
||||
print(f"{status} {t['name']} ({t['id']})")
|
||||
print(f" {t['description']}")
|
||||
print(f" Objectives: {t['n_objectives']} | Example: {t['example_study'] or 'N/A'}")
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
205
optimization_engine/templates/registry.json
Normal file
205
optimization_engine/templates/registry.json
Normal file
@@ -0,0 +1,205 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"last_updated": "2025-12-07",
|
||||
"templates": [
|
||||
{
|
||||
"id": "multi_objective_structural",
|
||||
"name": "Multi-Objective Structural",
|
||||
"description": "NSGA-II optimization for structural analysis with mass, stress, and stiffness objectives",
|
||||
"category": "structural",
|
||||
"objectives": [
|
||||
{"name": "mass", "direction": "minimize", "extractor": "E4"},
|
||||
{"name": "stress", "direction": "minimize", "extractor": "E3"},
|
||||
{"name": "stiffness", "direction": "maximize", "extractor": "E1"}
|
||||
],
|
||||
"extractors": ["E1", "E3", "E4"],
|
||||
"solver": "SOL 101",
|
||||
"element_types": ["CTETRA", "CHEXA", "CQUAD4"],
|
||||
"sampler": "NSGAIISampler",
|
||||
"recommended_trials": {
|
||||
"discovery": 1,
|
||||
"validation": 3,
|
||||
"quick": 20,
|
||||
"full": 50,
|
||||
"comprehensive": 100
|
||||
},
|
||||
"turbo_suitable": true,
|
||||
"example_study": "bracket_pareto_3obj",
|
||||
"wizard_questions": [
|
||||
{"key": "element_type", "question": "What element type does your mesh use?", "options": ["CTETRA (solid)", "CHEXA (solid)", "CQUAD4 (shell)"]},
|
||||
{"key": "stress_limit", "question": "What is the allowable stress limit (MPa)?", "default": 200},
|
||||
{"key": "displacement_limit", "question": "What is the max allowable displacement (mm)?", "default": 10}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "frequency_optimization",
|
||||
"name": "Frequency Optimization",
|
||||
"description": "Maximize natural frequency while minimizing mass for vibration-sensitive structures",
|
||||
"category": "dynamics",
|
||||
"objectives": [
|
||||
{"name": "frequency", "direction": "maximize", "extractor": "E2"},
|
||||
{"name": "mass", "direction": "minimize", "extractor": "E4"}
|
||||
],
|
||||
"extractors": ["E2", "E4"],
|
||||
"solver": "SOL 103",
|
||||
"element_types": ["CTETRA", "CHEXA", "CQUAD4", "CBAR"],
|
||||
"sampler": "NSGAIISampler",
|
||||
"recommended_trials": {
|
||||
"discovery": 1,
|
||||
"validation": 3,
|
||||
"quick": 20,
|
||||
"full": 50
|
||||
},
|
||||
"turbo_suitable": true,
|
||||
"example_study": "uav_arm_optimization",
|
||||
"wizard_questions": [
|
||||
{"key": "target_mode", "question": "Which vibration mode to optimize?", "default": 1},
|
||||
{"key": "min_frequency", "question": "Minimum acceptable frequency (Hz)?", "default": 50}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "single_objective_mass",
|
||||
"name": "Mass Minimization",
|
||||
"description": "Minimize mass subject to stress and displacement constraints",
|
||||
"category": "structural",
|
||||
"objectives": [
|
||||
{"name": "mass", "direction": "minimize", "extractor": "E4"}
|
||||
],
|
||||
"extractors": ["E1", "E3", "E4"],
|
||||
"solver": "SOL 101",
|
||||
"element_types": ["CTETRA", "CHEXA", "CQUAD4"],
|
||||
"sampler": "TPESampler",
|
||||
"recommended_trials": {
|
||||
"discovery": 1,
|
||||
"validation": 3,
|
||||
"quick": 30,
|
||||
"full": 100
|
||||
},
|
||||
"turbo_suitable": true,
|
||||
"example_study": "bracket_stiffness_optimization_V3",
|
||||
"wizard_questions": [
|
||||
{"key": "stress_constraint", "question": "Max stress constraint (MPa)?", "default": 200},
|
||||
{"key": "displacement_constraint", "question": "Max displacement constraint (mm)?", "default": 5}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mirror_wavefront",
|
||||
"name": "Mirror Wavefront Optimization",
|
||||
"description": "Minimize Zernike wavefront error for optical mirror deformation",
|
||||
"category": "optics",
|
||||
"objectives": [
|
||||
{"name": "zernike_rms", "direction": "minimize", "extractor": "E8"}
|
||||
],
|
||||
"extractors": ["E8", "E9", "E10"],
|
||||
"solver": "SOL 101",
|
||||
"element_types": ["CQUAD4", "CTRIA3"],
|
||||
"sampler": "TPESampler",
|
||||
"recommended_trials": {
|
||||
"discovery": 1,
|
||||
"validation": 3,
|
||||
"quick": 30,
|
||||
"full": 100
|
||||
},
|
||||
"turbo_suitable": false,
|
||||
"example_study": "m1_mirror_zernike_optimization",
|
||||
"wizard_questions": [
|
||||
{"key": "mirror_radius", "question": "Mirror radius (mm)?", "required": true},
|
||||
{"key": "zernike_modes", "question": "Number of Zernike modes?", "default": 36},
|
||||
{"key": "target_wfe", "question": "Target WFE RMS (nm)?", "default": 50}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "thermal_structural",
|
||||
"name": "Thermal-Structural Coupled",
|
||||
"description": "Optimize for thermal and structural performance",
|
||||
"category": "multiphysics",
|
||||
"objectives": [
|
||||
{"name": "max_temperature", "direction": "minimize", "extractor": "E15"},
|
||||
{"name": "thermal_stress", "direction": "minimize", "extractor": "E3"}
|
||||
],
|
||||
"extractors": ["E3", "E15", "E16"],
|
||||
"solver": "SOL 153/400",
|
||||
"element_types": ["CTETRA", "CHEXA"],
|
||||
"sampler": "NSGAIISampler",
|
||||
"recommended_trials": {
|
||||
"discovery": 1,
|
||||
"validation": 3,
|
||||
"quick": 20,
|
||||
"full": 50
|
||||
},
|
||||
"turbo_suitable": false,
|
||||
"example_study": null,
|
||||
"wizard_questions": [
|
||||
{"key": "max_temp_limit", "question": "Maximum allowable temperature (°C)?", "default": 100},
|
||||
{"key": "stress_limit", "question": "Maximum allowable thermal stress (MPa)?", "default": 150}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "shell_structural",
|
||||
"name": "Shell Structure Optimization",
|
||||
"description": "Optimize shell structures (CQUAD4/CTRIA3) for mass and stress",
|
||||
"category": "structural",
|
||||
"objectives": [
|
||||
{"name": "mass", "direction": "minimize", "extractor": "E4"},
|
||||
{"name": "stress", "direction": "minimize", "extractor": "E3"}
|
||||
],
|
||||
"extractors": ["E1", "E3", "E4"],
|
||||
"solver": "SOL 101",
|
||||
"element_types": ["CQUAD4", "CTRIA3"],
|
||||
"sampler": "NSGAIISampler",
|
||||
"recommended_trials": {
|
||||
"discovery": 1,
|
||||
"validation": 3,
|
||||
"quick": 20,
|
||||
"full": 50
|
||||
},
|
||||
"turbo_suitable": true,
|
||||
"example_study": "beam_pareto_4var",
|
||||
"notes": "Remember to specify element_type='cquad4' in stress extractor",
|
||||
"wizard_questions": [
|
||||
{"key": "stress_limit", "question": "Max stress constraint (MPa)?", "default": 200}
|
||||
]
|
||||
}
|
||||
],
|
||||
"extractors": {
|
||||
"E1": {"name": "Displacement", "function": "extract_displacement", "units": "mm", "phase": 1},
|
||||
"E2": {"name": "Frequency", "function": "extract_frequency", "units": "Hz", "phase": 1},
|
||||
"E3": {"name": "Von Mises Stress", "function": "extract_solid_stress", "units": "MPa", "phase": 1, "notes": "Specify element_type for shell elements"},
|
||||
"E4": {"name": "BDF Mass", "function": "extract_mass_from_bdf", "units": "kg", "phase": 1},
|
||||
"E5": {"name": "CAD Mass", "function": "extract_mass_from_expression", "units": "kg", "phase": 1},
|
||||
"E6": {"name": "Stiffness (from disp)", "function": "calculate_stiffness", "units": "N/mm", "phase": 1},
|
||||
"E7": {"name": "Compliance", "function": "calculate_compliance", "units": "mm/N", "phase": 1},
|
||||
"E8": {"name": "Zernike WFE RMS", "function": "extract_zernike_wfe_rms", "units": "nm", "phase": 1},
|
||||
"E9": {"name": "Zernike Coefficients", "function": "extract_zernike_coefficients", "units": "nm", "phase": 1},
|
||||
"E10": {"name": "Zernike RMS per Mode", "function": "extract_zernike_rms_per_mode", "units": "nm", "phase": 1},
|
||||
"E12": {"name": "Principal Stress", "function": "extract_principal_stress", "units": "MPa", "phase": 2},
|
||||
"E13": {"name": "Strain Energy", "function": "extract_strain_energy", "units": "J", "phase": 2},
|
||||
"E14": {"name": "SPC Forces", "function": "extract_spc_forces", "units": "N", "phase": 2},
|
||||
"E15": {"name": "Temperature", "function": "extract_temperature", "units": "°C", "phase": 3},
|
||||
"E16": {"name": "Temperature Gradient", "function": "extract_temperature_gradient", "units": "°C/mm", "phase": 3},
|
||||
"E17": {"name": "Heat Flux", "function": "extract_heat_flux", "units": "W/mm²", "phase": 3},
|
||||
"E18": {"name": "Modal Mass", "function": "extract_modal_mass", "units": "kg", "phase": 3}
|
||||
},
|
||||
"categories": {
|
||||
"structural": {
|
||||
"name": "Structural Analysis",
|
||||
"description": "Static structural optimization (SOL 101)",
|
||||
"icon": "🏗️"
|
||||
},
|
||||
"dynamics": {
|
||||
"name": "Dynamics / Modal",
|
||||
"description": "Frequency and modal optimization (SOL 103)",
|
||||
"icon": "📳"
|
||||
},
|
||||
"optics": {
|
||||
"name": "Optical Systems",
|
||||
"description": "Wavefront error optimization for mirrors/lenses",
|
||||
"icon": "🔭"
|
||||
},
|
||||
"multiphysics": {
|
||||
"name": "Multi-Physics",
|
||||
"description": "Coupled thermal-structural analysis",
|
||||
"icon": "🔥"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
{STUDY_NAME} - Neural Network Acceleration Script (Simplified)
|
||||
================================================================
|
||||
|
||||
This script uses ConfigDrivenSurrogate for config-driven NN optimization.
|
||||
The ~600 lines of boilerplate code is now handled automatically.
|
||||
|
||||
Workflow:
|
||||
---------
|
||||
1. First run FEA: python run_optimization.py --run --trials 50
|
||||
2. Then run NN: python run_nn_optimization.py --turbo --nn-trials 5000
|
||||
|
||||
Or combine:
|
||||
python run_nn_optimization.py --all
|
||||
|
||||
Generated by Atomizer StudyWizard
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
# Add project root to path
|
||||
project_root = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from optimization_engine.generic_surrogate import ConfigDrivenSurrogate
|
||||
|
||||
|
||||
def main():
|
||||
"""Run neural acceleration using config-driven surrogate."""
|
||||
# Create surrogate - all config read from optimization_config.json
|
||||
surrogate = ConfigDrivenSurrogate(__file__)
|
||||
|
||||
# Element type: 'auto' detects from DAT file
|
||||
# Override if needed: surrogate.element_type = 'cquad4' (shell) or 'ctetra' (solid)
|
||||
|
||||
return surrogate.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
41
optimization_engine/templates/run_optimization_template.py
Normal file
41
optimization_engine/templates/run_optimization_template.py
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
{STUDY_NAME} - Optimization Script (Simplified)
|
||||
================================================================
|
||||
|
||||
This script uses the ConfigDrivenRunner for config-driven optimization.
|
||||
The ~300 lines of boilerplate code is now handled automatically.
|
||||
|
||||
Workflow:
|
||||
---------
|
||||
1. python run_optimization.py --discover # Model introspection
|
||||
2. python run_optimization.py --validate # Single trial validation
|
||||
3. python run_optimization.py --test # Quick 3-trial test
|
||||
4. python run_optimization.py --run # Full optimization
|
||||
|
||||
Generated by Atomizer StudyWizard
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
# Add project root to path
|
||||
project_root = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from optimization_engine.base_runner import ConfigDrivenRunner
|
||||
|
||||
|
||||
def main():
|
||||
"""Run optimization using config-driven runner."""
|
||||
# Create runner - all config read from optimization_config.json
|
||||
runner = ConfigDrivenRunner(__file__)
|
||||
|
||||
# Element type: 'auto' detects from DAT file
|
||||
# Override if needed: runner.element_type = 'cquad4' (shell) or 'ctetra' (solid)
|
||||
|
||||
return runner.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
Reference in New Issue
Block a user