""" Full End-to-End Bracket Optimization Test - Phase 3.2 This test demonstrates the complete LLM-enhanced optimization workflow: 1. LLM workflow configuration 2. Automatic extractor generation (displacement + stress) 3. Inline calculation generation (safety factor) 4. Manual safety factor constraint hook 5. Real NX simulation execution 6. OP2 result extraction 7. Constraint checking 8. Optimization with Optuna 9. Report generation Objective: Maximize displacement Constraint: Safety factor >= 4.0 (stress < yield/4) Material: Aluminum 6061-T6 (Yield = 276 MPa) Design Variable: wall_thickness (3-8mm) """ import sys from pathlib import Path from datetime import datetime import json sys.path.insert(0, str(Path(__file__).parent.parent)) from optimization_engine.future.llm_optimization_runner import LLMOptimizationRunner from optimization_engine.nx.solver import NXSolver from optimization_engine.nx.updater import NXParameterUpdater # LLM workflow for bracket optimization llm_workflow = { 'engineering_features': [ { 'action': 'extract_displacement', 'domain': 'result_extraction', 'description': 'Extract displacement results from OP2 file', 'params': {'result_type': 'displacement'} }, { 'action': 'extract_solid_stress', 'domain': 'result_extraction', 'description': 'Extract von Mises stress from solid elements', 'params': { 'result_type': 'stress', 'element_type': 'chexa' # Bracket uses CHEXA elements, not CTETRA } } ], 'inline_calculations': [ { 'action': 'calculate_safety_factor', 'params': { 'input': 'max_von_mises', 'yield_strength': 276.0, # MPa for Aluminum 6061-T6 'operation': 'divide' }, 'code_hint': 'safety_factor = 276.0 / max_von_mises' }, { 'action': 'negate_displacement', 'params': { 'input': 'max_displacement', 'operation': 'negate' }, 'code_hint': 'neg_displacement = -max_displacement' } ], 'post_processing_hooks': [], # Using manual safety_factor_constraint hook 'optimization': { 'algorithm': 'TPE', 'direction': 'minimize', # Minimize neg_displacement = maximize displacement 'design_variables': [ { 'parameter': 'tip_thickness', 'min': 15.0, 'max': 25.0, 'units': 'mm' }, { 'parameter': 'support_angle', 'min': 20.0, 'max': 40.0, 'units': 'degrees' } ] } } def create_model_updater(prt_file: Path): """Create model updater for NX.""" updater = NXParameterUpdater(prt_file_path=prt_file) def update_model(design_vars: dict): """Update NX model with design variables.""" updater.update_expressions(design_vars) updater.save() # Save changes to disk return update_model def create_simulation_runner(sim_file: Path): """Create simulation runner for NX.""" solver = NXSolver(nastran_version='2412', use_journal=True) def run_simulation() -> Path: """Run NX simulation and return OP2 file path.""" result = solver.run_simulation(sim_file) return result['op2_file'] # Extract Path from result dict return run_simulation def generate_report(results: dict, output_dir: Path): """Generate optimization report with history.""" report_file = output_dir / "optimization_report.md" best_params = results['best_params'] best_value = results['best_value'] history = results['history'] with open(report_file, 'w') as f: f.write("# Bracket Optimization Report - Phase 3.2\n\n") f.write(f"**Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n") f.write("## Problem Definition\n\n") f.write("- **Objective**: Maximize displacement\n") f.write("- **Constraint**: Safety factor >= 4.0\n") f.write("- **Material**: Aluminum 6061-T6 (Yield = 276 MPa)\n") f.write("- **Allowable Stress**: 69 MPa (276/4)\n") f.write("- **Design Variables**:\n") f.write(" - tip_thickness (15-25 mm)\n") f.write(" - support_angle (20-40 degrees)\n\n") f.write("## Optimization Results\n\n") f.write(f"- **Best tip_thickness**: {best_params['tip_thickness']:.3f} mm\n") f.write(f"- **Best support_angle**: {best_params['support_angle']:.3f} degrees\n") f.write(f"- **Best objective value**: {best_value:.6f}\n") f.write(f"- **Total trials**: {len(history)}\n\n") # Find best trial details best_trial = history[results['best_trial_number']] best_results = best_trial['results'] best_calcs = best_trial['calculations'] f.write("## Best Design Details\n\n") f.write(f"- **Max displacement**: {best_results.get('max_displacement', 0):.6f} mm\n") f.write(f"- **Max stress**: {best_results.get('max_von_mises', 0):.3f} MPa\n") f.write(f"- **Safety factor**: {best_calcs.get('safety_factor', 0):.3f}\n") f.write(f"- **Constraint status**: {'SATISFIED' if best_calcs.get('safety_factor', 0) >= 4.0 else 'VIOLATED'}\n\n") f.write("## Optimization History\n\n") f.write("| Trial | Tip Thick (mm) | Support Angle (°) | Displacement (mm) | Stress (MPa) | Safety Factor | Constraint | Objective |\n") f.write("|-------|----------------|-------------------|-------------------|--------------|---------------|------------|-----------|\n") for trial in history: trial_num = trial['trial_number'] tip_thick = trial['design_variables']['tip_thickness'] support_ang = trial['design_variables']['support_angle'] disp = trial['results'].get('max_displacement', 0) stress = trial['results'].get('max_von_mises', 0) sf = trial['calculations'].get('safety_factor', 0) constraint = 'OK' if sf >= 4.0 else 'FAIL' obj = trial['objective'] f.write(f"| {trial_num} | {tip_thick:.3f} | {support_ang:.3f} | {disp:.6f} | {stress:.3f} | {sf:.3f} | {constraint} | {obj:.6f} |\n") f.write("\n## LLM-Enhanced Workflow\n\n") f.write("This optimization was run using the Phase 3.2 LLM-enhanced runner with:\n\n") f.write("- **Automatic extractor generation**: Displacement + Stress (Phase 3.1)\n") f.write("- **Inline calculations**: Safety factor + Objective negation (Phase 2.8)\n") f.write("- **Manual constraint hook**: Safety factor constraint (demonstrates flexibility)\n") f.write("- **Real NX simulation**: Journal-based solver execution\n") f.write("- **Optuna optimization**: TPE sampler\n\n") f.write("---\n\n") f.write("*Generated by Atomizer Phase 3.2 - LLM-Enhanced Optimization Framework*\n") print(f"\nReport saved to: {report_file}") return report_file if __name__ == '__main__': print("=" * 80) print("Phase 3.2: Full End-to-End Bracket Optimization Test") print("=" * 80) print() print("Problem:") print(" Maximize displacement while maintaining safety factor >= 4.0") print(" Material: Aluminum 6061-T6 (Yield = 276 MPa)") print(" Design Variables:") print(" - tip_thickness (15-25 mm)") print(" - support_angle (20-40 degrees)") print() # Configuration sim_file = Path("tests/Bracket_sim1.sim") prt_file = Path("tests/Bracket.prt") n_trials = 5 # Start with 5 trials for testing if not sim_file.exists(): print(f"ERROR: Simulation file not found: {sim_file}") print("Please ensure the bracket .sim file is available") sys.exit(1) if not prt_file.exists(): print(f"ERROR: Part file not found: {prt_file}") print("Please ensure the bracket .prt file is available") sys.exit(1) print(f"Simulation file: {sim_file}") print(f"Part file: {prt_file}") print(f"Number of trials: {n_trials}") print() try: # Create model updater and simulation runner print("Setting up NX integration...") model_updater = create_model_updater(prt_file) simulation_runner = create_simulation_runner(sim_file) print(" NX updater: OK") print(" NX solver: OK") print() # Initialize LLM optimization runner print("Initializing LLM-enhanced optimization runner...") runner = LLMOptimizationRunner( llm_workflow=llm_workflow, model_updater=model_updater, simulation_runner=simulation_runner, study_name='bracket_maximize_disp_sf4' ) print(f" Extractors generated: {len(runner.extractors)}") for ext in runner.extractors: print(f" - {ext}") print(f" Inline calculations: {len(runner.inline_code)}") hook_summary = runner.hook_manager.get_summary() print(f" Hooks loaded: {hook_summary['enabled_hooks']}") print(" (Including manual safety_factor_constraint hook)") print() # Run optimization print("=" * 80) print("Starting Optimization...") print("=" * 80) print() results = runner.run_optimization(n_trials=n_trials) print() print("=" * 80) print("Optimization Complete!") print("=" * 80) print() print(f"Best tip_thickness: {results['best_params']['tip_thickness']:.3f} mm") print(f"Best support_angle: {results['best_params']['support_angle']:.3f} degrees") print(f"Best objective value: {results['best_value']:.6f}") print() # Generate report print("Generating optimization report...") report_file = generate_report(results, runner.output_dir) print() print("=" * 80) print("Test Complete!") print("=" * 80) print(f"Results directory: {runner.output_dir}") print(f"Report: {report_file}") except Exception as e: print(f"\nERROR during optimization: {e}") import traceback traceback.print_exc() sys.exit(1)