BREAKING CHANGE: Module paths have been reorganized for better maintainability. Backwards compatibility aliases with deprecation warnings are provided. New Structure: - core/ - Optimization runners (runner, intelligent_optimizer, etc.) - processors/ - Data processing - surrogates/ - Neural network surrogates - nx/ - NX/Nastran integration (solver, updater, session_manager) - study/ - Study management (creator, wizard, state, reset) - reporting/ - Reports and analysis (visualizer, report_generator) - config/ - Configuration management (manager, builder) - utils/ - Utilities (logger, auto_doc, etc.) - future/ - Research/experimental code Migration: - ~200 import changes across 125 files - All __init__.py files use lazy loading to avoid circular imports - Backwards compatibility layer supports old import paths with warnings - All existing functionality preserved To migrate existing code: OLD: from optimization_engine.nx_solver import NXSolver NEW: from optimization_engine.nx.solver import NXSolver OLD: from optimization_engine.runner import OptimizationRunner NEW: from optimization_engine.core.runner import OptimizationRunner 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
445 lines
14 KiB
Python
445 lines
14 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
Atomizer CLI - Neural-Accelerated Structural Optimization
|
|
|
|
One-command interface for the complete Atomizer workflow:
|
|
- Create studies from templates
|
|
- Run FEA optimizations with auto training data export
|
|
- Auto-train neural networks when data threshold is reached
|
|
- Run neural-accelerated optimization (2200x faster!)
|
|
|
|
Usage:
|
|
python atomizer.py neural-optimize --study my_study --trials 500
|
|
python atomizer.py create-study --template beam_stiffness --name my_beam
|
|
python atomizer.py status --study my_study
|
|
python atomizer.py train --study my_study --epochs 100
|
|
|
|
The neural-optimize command is the main entry point - it handles the complete
|
|
workflow automatically:
|
|
1. Runs FEA optimization with training data export
|
|
2. Triggers neural network training when enough data is collected
|
|
3. Switches to neural-accelerated mode for remaining trials
|
|
4. Detects model drift and retrains as needed
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
# Add project root to path
|
|
PROJECT_ROOT = Path(__file__).parent
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from optimization_engine.processors.surrogates.auto_trainer import AutoTrainer, check_training_status
|
|
from optimization_engine.config.template_loader import (
|
|
create_study_from_template,
|
|
list_templates,
|
|
get_template
|
|
)
|
|
from optimization_engine.validators.study_validator import (
|
|
validate_study,
|
|
list_studies,
|
|
quick_check
|
|
)
|
|
|
|
|
|
def setup_logging(verbose: bool = False) -> None:
|
|
"""Configure logging."""
|
|
level = logging.DEBUG if verbose else logging.INFO
|
|
logging.basicConfig(
|
|
level=level,
|
|
format='%(asctime)s [%(levelname)s] %(message)s',
|
|
datefmt='%H:%M:%S'
|
|
)
|
|
|
|
|
|
def cmd_neural_optimize(args) -> int:
|
|
"""
|
|
Run neural-accelerated optimization.
|
|
|
|
This is the main workflow that:
|
|
1. Validates study setup
|
|
2. Runs FEA exploration with training data export
|
|
3. Auto-trains neural model when threshold reached
|
|
4. Runs remaining trials with neural acceleration
|
|
"""
|
|
print("=" * 60)
|
|
print("ATOMIZER NEURAL-ACCELERATED OPTIMIZATION")
|
|
print("=" * 60)
|
|
print(f"Study: {args.study}")
|
|
print(f"Total trials: {args.trials}")
|
|
print(f"Auto-train threshold: {args.min_points} points")
|
|
print(f"Retrain every: {args.retrain_every} new points")
|
|
print("=" * 60)
|
|
|
|
# Validate study
|
|
print("\n[1/5] Validating study setup...")
|
|
validation = validate_study(args.study)
|
|
|
|
if not validation.is_ready_to_run:
|
|
print(f"\nStudy validation failed:")
|
|
print(validation)
|
|
return 1
|
|
|
|
print(f" Study is ready to run")
|
|
print(f" Design variables: {validation.summary.get('design_variables', 0)}")
|
|
print(f" Objectives: {validation.summary.get('objectives', 0)}")
|
|
|
|
# Initialize auto-trainer
|
|
print("\n[2/5] Initializing auto-trainer...")
|
|
trainer = AutoTrainer(
|
|
study_name=args.study,
|
|
min_points=args.min_points,
|
|
epochs=args.epochs,
|
|
retrain_threshold=args.retrain_every
|
|
)
|
|
|
|
status = trainer.get_status()
|
|
print(f" Current data points: {status['total_points']}")
|
|
print(f" Model version: v{status['model_version']}")
|
|
|
|
# Determine workflow phase
|
|
has_trained_model = status['model_version'] > 0
|
|
current_points = status['total_points']
|
|
|
|
if has_trained_model and current_points >= args.min_points:
|
|
print("\n[3/5] Neural model available - starting neural-accelerated optimization...")
|
|
return _run_neural_phase(args, trainer)
|
|
else:
|
|
print("\n[3/5] Building training dataset with FEA exploration...")
|
|
return _run_exploration_phase(args, trainer)
|
|
|
|
|
|
def _run_exploration_phase(args, trainer: AutoTrainer) -> int:
|
|
"""Run FEA exploration to build training dataset."""
|
|
study_dir = PROJECT_ROOT / "studies" / args.study
|
|
run_script = study_dir / "run_optimization.py"
|
|
|
|
if not run_script.exists():
|
|
print(f"Error: run_optimization.py not found in {study_dir}")
|
|
return 1
|
|
|
|
# Calculate how many FEA trials we need
|
|
current_points = trainer.count_training_points()
|
|
needed_for_training = args.min_points - current_points
|
|
|
|
if needed_for_training > 0:
|
|
fea_trials = min(needed_for_training + 10, args.trials) # Extra buffer
|
|
print(f"\n Running {fea_trials} FEA trials to build training data...")
|
|
print(f" (Need {needed_for_training} more points for neural training)")
|
|
else:
|
|
fea_trials = args.trials
|
|
print(f"\n Running {fea_trials} FEA trials...")
|
|
|
|
# Run FEA optimization
|
|
import subprocess
|
|
|
|
cmd = [
|
|
sys.executable,
|
|
str(run_script),
|
|
"--trials", str(fea_trials)
|
|
]
|
|
|
|
if args.resume:
|
|
cmd.append("--resume")
|
|
|
|
print(f"\n[4/5] Executing: {' '.join(cmd)}")
|
|
print("-" * 60)
|
|
|
|
start_time = time.time()
|
|
result = subprocess.run(cmd, cwd=str(study_dir))
|
|
elapsed = time.time() - start_time
|
|
|
|
print("-" * 60)
|
|
print(f"FEA optimization completed in {elapsed/60:.1f} minutes")
|
|
|
|
# Check if we can now train
|
|
print("\n[5/5] Checking training data...")
|
|
if trainer.should_train():
|
|
print(" Threshold reached! Training neural model...")
|
|
model_path = trainer.train()
|
|
if model_path:
|
|
print(f" Neural model trained: {model_path}")
|
|
print(f"\n Re-run with --resume to continue with neural acceleration!")
|
|
else:
|
|
print(" Training failed - check logs")
|
|
else:
|
|
status = trainer.get_status()
|
|
remaining = args.min_points - status['total_points']
|
|
print(f" {status['total_points']} points collected")
|
|
print(f" Need {remaining} more for neural training")
|
|
|
|
return result.returncode
|
|
|
|
|
|
def _run_neural_phase(args, trainer: AutoTrainer) -> int:
|
|
"""Run neural-accelerated optimization."""
|
|
study_dir = PROJECT_ROOT / "studies" / args.study
|
|
run_script = study_dir / "run_optimization.py"
|
|
|
|
if not run_script.exists():
|
|
print(f"Error: run_optimization.py not found in {study_dir}")
|
|
return 1
|
|
|
|
# Run with neural acceleration
|
|
import subprocess
|
|
|
|
cmd = [
|
|
sys.executable,
|
|
str(run_script),
|
|
"--trials", str(args.trials),
|
|
"--enable-nn"
|
|
]
|
|
|
|
if args.resume:
|
|
cmd.append("--resume")
|
|
|
|
print(f"\n[4/5] Executing: {' '.join(cmd)}")
|
|
print("-" * 60)
|
|
|
|
start_time = time.time()
|
|
result = subprocess.run(cmd, cwd=str(study_dir))
|
|
elapsed = time.time() - start_time
|
|
|
|
print("-" * 60)
|
|
print(f"Neural optimization completed in {elapsed/60:.1f} minutes")
|
|
|
|
# Check for retraining
|
|
print("\n[5/5] Checking if retraining needed...")
|
|
if trainer.should_train():
|
|
print(" New data accumulated - triggering retraining...")
|
|
model_path = trainer.train()
|
|
if model_path:
|
|
print(f" New model version: {model_path}")
|
|
else:
|
|
status = trainer.get_status()
|
|
print(f" {status['new_points_since_training']} new points since last training")
|
|
print(f" (Retrain threshold: {args.retrain_every})")
|
|
|
|
return result.returncode
|
|
|
|
|
|
def cmd_create_study(args) -> int:
|
|
"""Create a new study from template."""
|
|
print(f"Creating study '{args.name}' from template '{args.template}'...")
|
|
|
|
try:
|
|
study_path = create_study_from_template(
|
|
template_name=args.template,
|
|
study_name=args.name
|
|
)
|
|
print(f"\nSuccess! Study created at: {study_path}")
|
|
return 0
|
|
except FileNotFoundError as e:
|
|
print(f"Error: {e}")
|
|
return 1
|
|
except FileExistsError as e:
|
|
print(f"Error: {e}")
|
|
return 1
|
|
|
|
|
|
def cmd_list_templates(args) -> int:
|
|
"""List available templates."""
|
|
templates = list_templates()
|
|
|
|
if not templates:
|
|
print("No templates found in templates/")
|
|
return 1
|
|
|
|
print("\nAvailable Templates:")
|
|
print("=" * 60)
|
|
|
|
for t in templates:
|
|
print(f"\n{t['name']}")
|
|
print(f" {t['description']}")
|
|
print(f" Category: {t['category']} | Analysis: {t['analysis_type']}")
|
|
print(f" Design vars: {t['design_variables']} | Objectives: {t['objectives']}")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("Use: atomizer create-study --template <name> --name <study_name>")
|
|
|
|
return 0
|
|
|
|
|
|
def cmd_status(args) -> int:
|
|
"""Show study and training status."""
|
|
if args.study:
|
|
# Show specific study status
|
|
print(f"\n=== Study: {args.study} ===\n")
|
|
|
|
# Validation status
|
|
validation = validate_study(args.study)
|
|
print("VALIDATION STATUS")
|
|
print("-" * 40)
|
|
print(f" Status: {validation.status.value}")
|
|
print(f" Ready to run: {validation.is_ready_to_run}")
|
|
|
|
for key, value in validation.summary.items():
|
|
print(f" {key}: {value}")
|
|
|
|
# Training status
|
|
print("\nTRAINING DATA STATUS")
|
|
print("-" * 40)
|
|
status = check_training_status(args.study)
|
|
print(f" Data points: {status['total_points']}")
|
|
print(f" New since training: {status['new_points_since_training']}")
|
|
print(f" Model version: v{status['model_version']}")
|
|
print(f" Should train: {status['should_train']}")
|
|
|
|
if status['latest_model']:
|
|
print(f" Latest model: {status['latest_model']}")
|
|
|
|
else:
|
|
# List all studies
|
|
print("\nAll Studies:")
|
|
print("=" * 60)
|
|
|
|
studies = list_studies()
|
|
if not studies:
|
|
print(" No studies found in studies/")
|
|
return 0
|
|
|
|
for study in studies:
|
|
icon = "[OK]" if study["is_ready"] else "[!]"
|
|
trials_info = f"{study['trials']} trials" if study['trials'] > 0 else "no trials"
|
|
pareto_info = f", {study['pareto']} Pareto" if study['pareto'] > 0 else ""
|
|
print(f" {icon} {study['name']}")
|
|
print(f" Status: {study['status']} ({trials_info}{pareto_info})")
|
|
|
|
return 0
|
|
|
|
|
|
def cmd_train(args) -> int:
|
|
"""Trigger neural network training."""
|
|
print(f"Training neural model for study: {args.study}")
|
|
|
|
trainer = AutoTrainer(
|
|
study_name=args.study,
|
|
min_points=args.min_points,
|
|
epochs=args.epochs
|
|
)
|
|
|
|
status = trainer.get_status()
|
|
print(f"\nCurrent status:")
|
|
print(f" Data points: {status['total_points']}")
|
|
print(f" Min threshold: {args.min_points}")
|
|
|
|
if args.force or trainer.should_train():
|
|
if args.force and status['total_points'] < args.min_points:
|
|
print(f"\nWarning: Force training with {status['total_points']} points (< {args.min_points})")
|
|
|
|
print("\nStarting training...")
|
|
model_path = trainer.train()
|
|
|
|
if model_path:
|
|
print(f"\nSuccess! Model saved to: {model_path}")
|
|
return 0
|
|
else:
|
|
print("\nTraining failed - check logs")
|
|
return 1
|
|
else:
|
|
needed = args.min_points - status['total_points']
|
|
print(f"\nNot enough data for training. Need {needed} more points.")
|
|
print("Use --force to train anyway.")
|
|
return 1
|
|
|
|
|
|
def cmd_validate(args) -> int:
|
|
"""Validate study setup."""
|
|
validation = validate_study(args.study)
|
|
print(validation)
|
|
return 0 if validation.is_ready_to_run else 1
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Atomizer - Neural-Accelerated Structural Optimization",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
# Complete neural workflow
|
|
python atomizer.py neural-optimize --study my_study --trials 500
|
|
|
|
# Create study from template
|
|
python atomizer.py create-study --template beam_stiffness --name my_beam
|
|
|
|
# Check status
|
|
python atomizer.py status --study my_study
|
|
|
|
# Manual training
|
|
python atomizer.py train --study my_study --epochs 100
|
|
"""
|
|
)
|
|
|
|
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
|
|
|
|
subparsers = parser.add_subparsers(dest="command", help="Commands")
|
|
|
|
# neural-optimize command
|
|
neural_parser = subparsers.add_parser(
|
|
"neural-optimize",
|
|
help="Run neural-accelerated optimization (main workflow)"
|
|
)
|
|
neural_parser.add_argument("--study", "-s", required=True, help="Study name")
|
|
neural_parser.add_argument("--trials", "-n", type=int, default=500, help="Total trials")
|
|
neural_parser.add_argument("--min-points", type=int, default=50, help="Min points for training")
|
|
neural_parser.add_argument("--retrain-every", type=int, default=50, help="Retrain after N new points")
|
|
neural_parser.add_argument("--epochs", type=int, default=100, help="Training epochs")
|
|
neural_parser.add_argument("--resume", action="store_true", help="Resume existing study")
|
|
|
|
# create-study command
|
|
create_parser = subparsers.add_parser("create-study", help="Create study from template")
|
|
create_parser.add_argument("--template", "-t", required=True, help="Template name")
|
|
create_parser.add_argument("--name", "-n", required=True, help="Study name")
|
|
|
|
# list-templates command
|
|
list_parser = subparsers.add_parser("list-templates", help="List available templates")
|
|
|
|
# status command
|
|
status_parser = subparsers.add_parser("status", help="Show status")
|
|
status_parser.add_argument("--study", "-s", help="Study name (omit for all)")
|
|
|
|
# train command
|
|
train_parser = subparsers.add_parser("train", help="Train neural model")
|
|
train_parser.add_argument("--study", "-s", required=True, help="Study name")
|
|
train_parser.add_argument("--epochs", type=int, default=100, help="Training epochs")
|
|
train_parser.add_argument("--min-points", type=int, default=50, help="Min points threshold")
|
|
train_parser.add_argument("--force", action="store_true", help="Force training")
|
|
|
|
# validate command
|
|
validate_parser = subparsers.add_parser("validate", help="Validate study setup")
|
|
validate_parser.add_argument("--study", "-s", required=True, help="Study name")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if not args.command:
|
|
parser.print_help()
|
|
return 0
|
|
|
|
setup_logging(args.verbose)
|
|
|
|
# Dispatch to command handler
|
|
commands = {
|
|
"neural-optimize": cmd_neural_optimize,
|
|
"create-study": cmd_create_study,
|
|
"list-templates": cmd_list_templates,
|
|
"status": cmd_status,
|
|
"train": cmd_train,
|
|
"validate": cmd_validate
|
|
}
|
|
|
|
handler = commands.get(args.command)
|
|
if handler:
|
|
return handler(args)
|
|
else:
|
|
parser.print_help()
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|