diff --git a/optimization_engine/llm_optimization_runner.py b/optimization_engine/llm_optimization_runner.py index c0e6b6b2..e5e25598 100644 --- a/optimization_engine/llm_optimization_runner.py +++ b/optimization_engine/llm_optimization_runner.py @@ -225,8 +225,11 @@ class LLMOptimizationRunner: # STEP 3: Run Simulation # ==================================================================== logger.info("Running simulation...") - # Pass design_vars to simulation_runner so NX journal can update expressions - op2_file = self.simulation_runner(design_vars) + # NOTE: We do NOT pass design_vars to simulation_runner because: + # 1. The PRT file was already updated by model_updater (via NX import journal) + # 2. The solver just needs to load the SIM which references the updated PRT + # 3. Passing design_vars would use hardcoded expression names that don't match our model + op2_file = self.simulation_runner() logger.info(f"Simulation complete: {op2_file}") # Execute post-solve hooks diff --git a/optimization_engine/run_optimization.py b/optimization_engine/run_optimization.py index 65daf743..589dca24 100644 --- a/optimization_engine/run_optimization.py +++ b/optimization_engine/run_optimization.py @@ -207,8 +207,12 @@ def run_llm_mode(args) -> Dict[str, Any]: updater.update_expressions(design_vars) solver = NXSolver(nastran_version=args.nastran_version, use_journal=True) - def simulation_runner(design_vars: dict) -> Path: - result = solver.run_simulation(args.sim, expression_updates=design_vars) + def simulation_runner() -> Path: + # NOTE: We do NOT pass expression_updates because: + # 1. The PRT file was already updated by model_updater (via NX import journal) + # 2. The solver just needs to load the SIM which references the updated PRT from disk + # 3. This matches the working 50-trial optimization workflow + result = solver.run_simulation(args.sim) return result['op2_file'] logger.info(" Model updater ready") diff --git a/studies/simple_beam_optimization/1_setup/model/Beam.prt b/studies/simple_beam_optimization/1_setup/model/Beam.prt index c3926787..bee74343 100644 Binary files a/studies/simple_beam_optimization/1_setup/model/Beam.prt and b/studies/simple_beam_optimization/1_setup/model/Beam.prt differ diff --git a/studies/simple_beam_optimization/1_setup/model/Beam_fem1.fem b/studies/simple_beam_optimization/1_setup/model/Beam_fem1.fem index ffef842a..e63322b0 100644 Binary files a/studies/simple_beam_optimization/1_setup/model/Beam_fem1.fem and b/studies/simple_beam_optimization/1_setup/model/Beam_fem1.fem differ diff --git a/studies/simple_beam_optimization/1_setup/model/Beam_fem1_i.prt b/studies/simple_beam_optimization/1_setup/model/Beam_fem1_i.prt index 42df33a1..4a9dde87 100644 Binary files a/studies/simple_beam_optimization/1_setup/model/Beam_fem1_i.prt and b/studies/simple_beam_optimization/1_setup/model/Beam_fem1_i.prt differ diff --git a/studies/simple_beam_optimization/1_setup/model/Beam_sim1.sim b/studies/simple_beam_optimization/1_setup/model/Beam_sim1.sim index 0114efd1..c9f79251 100644 Binary files a/studies/simple_beam_optimization/1_setup/model/Beam_sim1.sim and b/studies/simple_beam_optimization/1_setup/model/Beam_sim1.sim differ diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/generated_extractors/extract_displacement.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/generated_extractors/extract_displacement.py deleted file mode 100644 index b590b1a6..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/generated_extractors/extract_displacement.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Extract maximum displacement from simulation results -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: displacement -Element Type: General -Result Type: displacement -API: model.displacements[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_displacement(op2_file: Path, subcase: int = 1): - """Extract displacement results from OP2 file.""" - from pyNastran.op2.op2 import OP2 - import numpy as np - - model = OP2() - model.read_op2(str(op2_file)) - - disp = model.displacements[subcase] - itime = 0 # static case - - # Extract translation components - txyz = disp.data[itime, :, :3] # [tx, ty, tz] - - # Calculate total displacement - total_disp = np.linalg.norm(txyz, axis=1) - max_disp = np.max(total_disp) - - # Get node info - node_ids = [nid for (nid, grid_type) in disp.node_gridtype] - max_disp_node = node_ids[np.argmax(total_disp)] - - return { - 'max_displacement': float(max_disp), - 'max_disp_node': int(max_disp_node), - 'max_disp_x': float(np.max(np.abs(txyz[:, 0]))), - 'max_disp_y': float(np.max(np.abs(txyz[:, 1]))), - 'max_disp_z': float(np.max(np.abs(txyz[:, 2]))) - } - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_displacement(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/generated_extractors/extract_mass.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/generated_extractors/extract_mass.py deleted file mode 100644 index 90e10b60..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/generated_extractors/extract_mass.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Extract total mass from model -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: generic_extraction -Element Type: General -Result Type: unknown -API: model.[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_generic(op2_file: Path): - """Generic OP2 extraction - needs customization.""" - from pyNastran.op2.op2 import OP2 - - model = OP2() - model.read_op2(str(op2_file)) - - # TODO: Customize extraction based on requirements - # Available: model.displacements, model.ctetra_stress, etc. - # Use model.get_op2_stats() to see available results - - return {'result': None} - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_generic(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/generated_extractors/extract_von_mises_stress.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/generated_extractors/extract_von_mises_stress.py deleted file mode 100644 index 7ce54b75..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/generated_extractors/extract_von_mises_stress.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Extract maximum von Mises stress from simulation results -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: solid_stress -Element Type: CTETRA -Result Type: stress -API: model.ctetra_stress[subcase] or model.chexa_stress[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_solid_stress(op2_file: Path, subcase: int = 1, element_type: str = 'ctetra'): - """Extract stress from solid elements.""" - from pyNastran.op2.op2 import OP2 - import numpy as np - - model = OP2() - model.read_op2(str(op2_file)) - - # Get stress object for element type - # In pyNastran, stress is stored in model.op2_results.stress - stress_attr = f"{element_type}_stress" - - if not hasattr(model, 'op2_results') or not hasattr(model.op2_results, 'stress'): - raise ValueError(f"No stress results in OP2") - - stress_obj = model.op2_results.stress - if not hasattr(stress_obj, stress_attr): - raise ValueError(f"No {element_type} stress results in OP2") - - stress = getattr(stress_obj, stress_attr)[subcase] - itime = 0 - - # Extract von Mises if available - if stress.is_von_mises: # Property, not method - von_mises = stress.data[itime, :, 9] # Column 9 is von Mises - max_stress = float(np.max(von_mises)) - - # Get element info - element_ids = [eid for (eid, node) in stress.element_node] - max_stress_elem = element_ids[np.argmax(von_mises)] - - return { - 'max_von_mises': max_stress, - 'max_stress_element': int(max_stress_elem) - } - else: - raise ValueError("von Mises stress not available") - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_solid_stress(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/optimization_history_incremental.json b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/optimization_history_incremental.json deleted file mode 100644 index a2eab1d9..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/optimization_history_incremental.json +++ /dev/null @@ -1,50 +0,0 @@ -[ - { - "trial_number": 0, - "design_variables": { - "beam_half_core_thickness": 0.8723317421947234, - "beam_face_thickness": 0.17588076114040685 - }, - "results": { - "max_displacement": 22.118558883666992, - "max_disp_node": 5186.0, - "max_disp_x": 1.4659312963485718, - "max_disp_y": 0.021927518770098686, - "max_disp_z": 22.07024574279785 - }, - "calculations": {}, - "objective": 22.118558883666992 - }, - { - "trial_number": 1, - "design_variables": { - "beam_half_core_thickness": 0.15876778707832584, - "beam_face_thickness": 0.607712401606436 - }, - "results": { - "max_displacement": 22.118558883666992, - "max_disp_node": 5186.0, - "max_disp_x": 1.4659312963485718, - "max_disp_y": 0.021927518770098686, - "max_disp_z": 22.07024574279785 - }, - "calculations": {}, - "objective": 22.118558883666992 - }, - { - "trial_number": 2, - "design_variables": { - "beam_half_core_thickness": 0.05067172698672462, - "beam_face_thickness": 0.9034405496402335 - }, - "results": { - "max_displacement": 22.118558883666992, - "max_disp_node": 5186.0, - "max_disp_x": 1.4659312963485718, - "max_disp_y": 0.021927518770098686, - "max_disp_z": 22.07024574279785 - }, - "calculations": {}, - "objective": 22.118558883666992 - } -] \ No newline at end of file diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/optimization_results.json b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/optimization_results.json deleted file mode 100644 index 8a2ab9d9..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/optimization_results.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "best_params": { - "beam_half_core_thickness": 0.8723317421947234, - "beam_face_thickness": 0.17588076114040685 - }, - "best_value": 22.118558883666992, - "best_trial_number": 0, - "timestamp": "2025-11-17T21:10:42.032661", - "study_name": "test_e2e_3trials_20251117_210933", - "n_trials": 3 -} \ No newline at end of file diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_extractors/calculate_mass.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_extractors/calculate_mass.py deleted file mode 100644 index 04fe3829..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_extractors/calculate_mass.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Extract total structural mass from analysis results -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: generic_extraction -Element Type: General -Result Type: unknown -API: model.[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_generic(op2_file: Path): - """Generic OP2 extraction - needs customization.""" - from pyNastran.op2.op2 import OP2 - - model = OP2() - model.read_op2(str(op2_file)) - - # TODO: Customize extraction based on requirements - # Available: model.displacements, model.ctetra_stress, etc. - # Use model.get_op2_stats() to see available results - - return {'result': None} - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_generic(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_extractors/extract_displacement_results.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_extractors/extract_displacement_results.py deleted file mode 100644 index d87470c6..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_extractors/extract_displacement_results.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Extract maximum displacement from OP2 results -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: displacement -Element Type: General -Result Type: displacement -API: model.displacements[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_displacement(op2_file: Path, subcase: int = 1): - """Extract displacement results from OP2 file.""" - from pyNastran.op2.op2 import OP2 - import numpy as np - - model = OP2() - model.read_op2(str(op2_file)) - - disp = model.displacements[subcase] - itime = 0 # static case - - # Extract translation components - txyz = disp.data[itime, :, :3] # [tx, ty, tz] - - # Calculate total displacement - total_disp = np.linalg.norm(txyz, axis=1) - max_disp = np.max(total_disp) - - # Get node info - node_ids = [nid for (nid, grid_type) in disp.node_gridtype] - max_disp_node = node_ids[np.argmax(total_disp)] - - return { - 'max_displacement': float(max_disp), - 'max_disp_node': int(max_disp_node), - 'max_disp_x': float(np.max(np.abs(txyz[:, 0]))), - 'max_disp_y': float(np.max(np.abs(txyz[:, 1]))), - 'max_disp_z': float(np.max(np.abs(txyz[:, 2]))) - } - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_displacement(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_extractors/extract_stress_results.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_extractors/extract_stress_results.py deleted file mode 100644 index a86ead62..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_extractors/extract_stress_results.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Extract von Mises stress from OP2 results -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: solid_stress -Element Type: CTETRA -Result Type: stress -API: model.ctetra_stress[subcase] or model.chexa_stress[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_solid_stress(op2_file: Path, subcase: int = 1, element_type: str = 'ctetra'): - """Extract stress from solid elements.""" - from pyNastran.op2.op2 import OP2 - import numpy as np - - model = OP2() - model.read_op2(str(op2_file)) - - # Get stress object for element type - # In pyNastran, stress is stored in model.op2_results.stress - stress_attr = f"{element_type}_stress" - - if not hasattr(model, 'op2_results') or not hasattr(model.op2_results, 'stress'): - raise ValueError(f"No stress results in OP2") - - stress_obj = model.op2_results.stress - if not hasattr(stress_obj, stress_attr): - raise ValueError(f"No {element_type} stress results in OP2") - - stress = getattr(stress_obj, stress_attr)[subcase] - itime = 0 - - # Extract von Mises if available - if stress.is_von_mises: # Property, not method - von_mises = stress.data[itime, :, 9] # Column 9 is von Mises - max_stress = float(np.max(von_mises)) - - # Get element info - element_ids = [eid for (eid, node) in stress.element_node] - max_stress_elem = element_ids[np.argmax(von_mises)] - - return { - 'max_von_mises': max_stress, - 'max_stress_element': int(max_stress_elem) - } - else: - raise ValueError("von Mises stress not available") - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_solid_stress(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_hooks/constraint_evaluation.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_hooks/constraint_evaluation.py deleted file mode 100644 index 34c47b5e..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/generated_hooks/constraint_evaluation.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Evaluate stress constraint -Auto-generated lifecycle hook by Atomizer Phase 2.9 - -Hook Point: post_calculation -Inputs: max_von_mises_stress -Outputs: constraint, constraint_satisfied, constraint_violation -""" - -import logging -from typing import Dict, Any, Optional - -logger = logging.getLogger(__name__) - - -def constraint_hook(context: Dict[str, Any]) -> Optional[Dict[str, Any]]: - """ - Evaluate stress constraint - - Args: - context: Hook context containing: - - trial_number: Current optimization trial - - results: Dictionary with extracted FEA results - - calculations: Dictionary with inline calculation results - - Returns: - Dictionary with calculated values to add to context - """ - logger.info(f"Executing constraint_hook for trial {context.get('trial_number', 'unknown')}") - - # Extract inputs from context - results = context.get('results', {}) - calculations = context.get('calculations', {}) - - max_von_mises_stress = calculations.get('max_von_mises_stress') or results.get('max_von_mises_stress') - if max_von_mises_stress is None: - logger.error(f"Required input 'max_von_mises_stress' not found in context") - raise ValueError(f"Missing required input: max_von_mises_stress") - - # Check constraint - value = max_von_mises_stress / 1.0 - satisfied = value <= 1.0 - violation = max(0.0, value - 1.0) - - status = "SATISFIED" if satisfied else "VIOLATED" - logger.info(f"Constraint {status}: {value:.6f} (threshold: 1.0)") - - return { - 'constraint': value, - 'constraint_satisfied': satisfied, - 'constraint_violation': violation - } - - -def register_hooks(hook_manager): - """ - Register this hook with the HookManager. - - This function is called automatically when the plugin is loaded. - - Args: - hook_manager: The HookManager instance - """ - hook_manager.register_hook( - hook_point='post_calculation', - function=constraint_hook, - description="Evaluate stress constraint", - name="constraint_hook", - priority=100, - enabled=True - ) - logger.info(f"Registered constraint_hook at post_calculation") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/optimization_history_incremental.json b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/optimization_history_incremental.json deleted file mode 100644 index 38284b9a..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/optimization_history_incremental.json +++ /dev/null @@ -1,50 +0,0 @@ -[ - { - "trial_number": 0, - "design_variables": { - "beam_half_core_thickness": 0.9173900726469443, - "beam_face_thickness": 0.16579089837702876 - }, - "results": { - "max_displacement": 22.118558883666992, - "max_disp_node": 5186.0, - "max_disp_x": 1.4659312963485718, - "max_disp_y": 0.021927518770098686, - "max_disp_z": 22.07024574279785 - }, - "calculations": {}, - "objective": 22.118558883666992 - }, - { - "trial_number": 1, - "design_variables": { - "beam_half_core_thickness": 0.4734084174808131, - "beam_face_thickness": 0.21389447965746633 - }, - "results": { - "max_displacement": 22.118558883666992, - "max_disp_node": 5186.0, - "max_disp_x": 1.4659312963485718, - "max_disp_y": 0.021927518770098686, - "max_disp_z": 22.07024574279785 - }, - "calculations": {}, - "objective": 22.118558883666992 - }, - { - "trial_number": 2, - "design_variables": { - "beam_half_core_thickness": 0.28158671645718203, - "beam_face_thickness": 0.6940495616914318 - }, - "results": { - "max_displacement": 22.118558883666992, - "max_disp_node": 5186.0, - "max_disp_x": 1.4659312963485718, - "max_disp_y": 0.021927518770098686, - "max_disp_z": 22.07024574279785 - }, - "calculations": {}, - "objective": 22.118558883666992 - } -] \ No newline at end of file diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/optimization_results.json b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/optimization_results.json deleted file mode 100644 index 6f639a62..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/optimization_results.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "best_params": { - "beam_half_core_thickness": 0.9173900726469443, - "beam_face_thickness": 0.16579089837702876 - }, - "best_value": 22.118558883666992, - "best_trial_number": 0, - "timestamp": "2025-11-17T21:13:26.644967", - "study_name": "test_e2e_3trials_20251117_211216", - "n_trials": 3 -} \ No newline at end of file diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/test_e2e_3trials_20251117_211216.db b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/test_e2e_3trials_20251117_211216.db deleted file mode 100644 index a6998b5d..00000000 Binary files a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211216/test_e2e_3trials_20251117_211216.db and /dev/null differ diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_hooks/check_displacement_constraint.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_hooks/check_displacement_constraint.py deleted file mode 100644 index 82bcb191..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_hooks/check_displacement_constraint.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Verify maximum displacement constraint is satisfied -Auto-generated lifecycle hook by Atomizer Phase 2.9 - -Hook Point: post_calculation -Inputs: max_displacement -Outputs: constraint, constraint_satisfied, constraint_violation -""" - -import logging -from typing import Dict, Any, Optional - -logger = logging.getLogger(__name__) - - -def constraint_hook(context: Dict[str, Any]) -> Optional[Dict[str, Any]]: - """ - Verify maximum displacement constraint is satisfied - - Args: - context: Hook context containing: - - trial_number: Current optimization trial - - results: Dictionary with extracted FEA results - - calculations: Dictionary with inline calculation results - - Returns: - Dictionary with calculated values to add to context - """ - logger.info(f"Executing constraint_hook for trial {context.get('trial_number', 'unknown')}") - - # Extract inputs from context - results = context.get('results', {}) - calculations = context.get('calculations', {}) - - max_displacement = calculations.get('max_displacement') or results.get('max_displacement') - if max_displacement is None: - logger.error(f"Required input 'max_displacement' not found in context") - raise ValueError(f"Missing required input: max_displacement") - - # Check constraint - value = max_displacement / 1.0 - satisfied = value <= 1.0 - violation = max(0.0, value - 1.0) - - status = "SATISFIED" if satisfied else "VIOLATED" - logger.info(f"Constraint {status}: {value:.6f} (threshold: 1.0)") - - return { - 'constraint': value, - 'constraint_satisfied': satisfied, - 'constraint_violation': violation - } - - -def register_hooks(hook_manager): - """ - Register this hook with the HookManager. - - This function is called automatically when the plugin is loaded. - - Args: - hook_manager: The HookManager instance - """ - hook_manager.register_hook( - hook_point='post_calculation', - function=constraint_hook, - description="Verify maximum displacement constraint is satisfied", - name="constraint_hook", - priority=100, - enabled=True - ) - logger.info(f"Registered constraint_hook at post_calculation") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_hooks/check_stress_constraint.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_hooks/check_stress_constraint.py deleted file mode 100644 index a1afc2dc..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_hooks/check_stress_constraint.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -Verify von Mises stress constraint is satisfied -Auto-generated lifecycle hook by Atomizer Phase 2.9 - -Hook Point: post_calculation -Inputs: max_von_mises_stress -Outputs: constraint, constraint_satisfied, constraint_violation -""" - -import logging -from typing import Dict, Any, Optional - -logger = logging.getLogger(__name__) - - -def constraint_hook(context: Dict[str, Any]) -> Optional[Dict[str, Any]]: - """ - Verify von Mises stress constraint is satisfied - - Args: - context: Hook context containing: - - trial_number: Current optimization trial - - results: Dictionary with extracted FEA results - - calculations: Dictionary with inline calculation results - - Returns: - Dictionary with calculated values to add to context - """ - logger.info(f"Executing constraint_hook for trial {context.get('trial_number', 'unknown')}") - - # Extract inputs from context - results = context.get('results', {}) - calculations = context.get('calculations', {}) - - max_von_mises_stress = calculations.get('max_von_mises_stress') or results.get('max_von_mises_stress') - if max_von_mises_stress is None: - logger.error(f"Required input 'max_von_mises_stress' not found in context") - raise ValueError(f"Missing required input: max_von_mises_stress") - - # Check constraint - value = max_von_mises_stress / 1.0 - satisfied = value <= 1.0 - violation = max(0.0, value - 1.0) - - status = "SATISFIED" if satisfied else "VIOLATED" - logger.info(f"Constraint {status}: {value:.6f} (threshold: 1.0)") - - return { - 'constraint': value, - 'constraint_satisfied': satisfied, - 'constraint_violation': violation - } - - -def register_hooks(hook_manager): - """ - Register this hook with the HookManager. - - This function is called automatically when the plugin is loaded. - - Args: - hook_manager: The HookManager instance - """ - hook_manager.register_hook( - hook_point='post_calculation', - function=constraint_hook, - description="Verify von Mises stress constraint is satisfied", - name="constraint_hook", - priority=100, - enabled=True - ) - logger.info(f"Registered constraint_hook at post_calculation") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/optimization_history_incremental.json b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/optimization_history_incremental.json deleted file mode 100644 index 7468b63d..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/optimization_history_incremental.json +++ /dev/null @@ -1,18 +0,0 @@ -[ - { - "trial_number": 0, - "design_variables": { - "beam_half_core_thickness": 0.21839456732865625, - "beam_face_thickness": 0.32902432018790606 - }, - "results": { - "max_displacement": 76444.203125, - "max_disp_node": 5244.0, - "max_disp_x": 94.31665802001953, - "max_disp_y": 1.4079378843307495, - "max_disp_z": 76444.1484375 - }, - "calculations": {}, - "objective": 76444.203125 - } -] \ No newline at end of file diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/test_e2e_3trials_20251117_211500.db b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/test_e2e_3trials_20251117_211500.db deleted file mode 100644 index cda5d7e2..00000000 Binary files a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/test_e2e_3trials_20251117_211500.db and /dev/null differ diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/generated_extractors/calculate_mass.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/generated_extractors/calculate_mass.py deleted file mode 100644 index ce9639b3..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/generated_extractors/calculate_mass.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Calculate total structural mass -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: generic_extraction -Element Type: General -Result Type: unknown -API: model.[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_generic(op2_file: Path): - """Generic OP2 extraction - needs customization.""" - from pyNastran.op2.op2 import OP2 - - model = OP2() - model.read_op2(str(op2_file)) - - # TODO: Customize extraction based on requirements - # Available: model.displacements, model.ctetra_stress, etc. - # Use model.get_op2_stats() to see available results - - return {'result': None} - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_generic(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/generated_extractors/extract_displacement.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/generated_extractors/extract_displacement.py deleted file mode 100644 index bc7dd284..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/generated_extractors/extract_displacement.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Extract maximum displacement from structural analysis -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: displacement -Element Type: General -Result Type: displacement -API: model.displacements[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_displacement(op2_file: Path, subcase: int = 1): - """Extract displacement results from OP2 file.""" - from pyNastran.op2.op2 import OP2 - import numpy as np - - model = OP2() - model.read_op2(str(op2_file)) - - disp = model.displacements[subcase] - itime = 0 # static case - - # Extract translation components - txyz = disp.data[itime, :, :3] # [tx, ty, tz] - - # Calculate total displacement - total_disp = np.linalg.norm(txyz, axis=1) - max_disp = np.max(total_disp) - - # Get node info - node_ids = [nid for (nid, grid_type) in disp.node_gridtype] - max_disp_node = node_ids[np.argmax(total_disp)] - - return { - 'max_displacement': float(max_disp), - 'max_disp_node': int(max_disp_node), - 'max_disp_x': float(np.max(np.abs(txyz[:, 0]))), - 'max_disp_y': float(np.max(np.abs(txyz[:, 1]))), - 'max_disp_z': float(np.max(np.abs(txyz[:, 2]))) - } - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_displacement(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/generated_extractors/extract_von_mises_stress.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/generated_extractors/extract_von_mises_stress.py deleted file mode 100644 index 90c4469d..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/generated_extractors/extract_von_mises_stress.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Extract von Mises stress from structural analysis -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: solid_stress -Element Type: CTETRA -Result Type: stress -API: model.ctetra_stress[subcase] or model.chexa_stress[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_solid_stress(op2_file: Path, subcase: int = 1, element_type: str = 'ctetra'): - """Extract stress from solid elements.""" - from pyNastran.op2.op2 import OP2 - import numpy as np - - model = OP2() - model.read_op2(str(op2_file)) - - # Get stress object for element type - # In pyNastran, stress is stored in model.op2_results.stress - stress_attr = f"{element_type}_stress" - - if not hasattr(model, 'op2_results') or not hasattr(model.op2_results, 'stress'): - raise ValueError(f"No stress results in OP2") - - stress_obj = model.op2_results.stress - if not hasattr(stress_obj, stress_attr): - raise ValueError(f"No {element_type} stress results in OP2") - - stress = getattr(stress_obj, stress_attr)[subcase] - itime = 0 - - # Extract von Mises if available - if stress.is_von_mises: # Property, not method - von_mises = stress.data[itime, :, 9] # Column 9 is von Mises - max_stress = float(np.max(von_mises)) - - # Get element info - element_ids = [eid for (eid, node) in stress.element_node] - max_stress_elem = element_ids[np.argmax(von_mises)] - - return { - 'max_von_mises': max_stress, - 'max_stress_element': int(max_stress_elem) - } - else: - raise ValueError("von Mises stress not available") - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_solid_stress(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/test_e2e_3trials_20251117_211638.db b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/test_e2e_3trials_20251117_211638.db deleted file mode 100644 index bb518c6e..00000000 Binary files a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211638/test_e2e_3trials_20251117_211638.db and /dev/null differ diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_extractors/extract_displacement.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_extractors/extract_displacement.py deleted file mode 100644 index bc7dd284..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_extractors/extract_displacement.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Extract maximum displacement from structural analysis -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: displacement -Element Type: General -Result Type: displacement -API: model.displacements[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_displacement(op2_file: Path, subcase: int = 1): - """Extract displacement results from OP2 file.""" - from pyNastran.op2.op2 import OP2 - import numpy as np - - model = OP2() - model.read_op2(str(op2_file)) - - disp = model.displacements[subcase] - itime = 0 # static case - - # Extract translation components - txyz = disp.data[itime, :, :3] # [tx, ty, tz] - - # Calculate total displacement - total_disp = np.linalg.norm(txyz, axis=1) - max_disp = np.max(total_disp) - - # Get node info - node_ids = [nid for (nid, grid_type) in disp.node_gridtype] - max_disp_node = node_ids[np.argmax(total_disp)] - - return { - 'max_displacement': float(max_disp), - 'max_disp_node': int(max_disp_node), - 'max_disp_x': float(np.max(np.abs(txyz[:, 0]))), - 'max_disp_y': float(np.max(np.abs(txyz[:, 1]))), - 'max_disp_z': float(np.max(np.abs(txyz[:, 2]))) - } - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_displacement(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_extractors/extract_mass.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_extractors/extract_mass.py deleted file mode 100644 index 2ee2f0b2..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_extractors/extract_mass.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Extract total structural mass -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: generic_extraction -Element Type: General -Result Type: unknown -API: model.[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_generic(op2_file: Path): - """Generic OP2 extraction - needs customization.""" - from pyNastran.op2.op2 import OP2 - - model = OP2() - model.read_op2(str(op2_file)) - - # TODO: Customize extraction based on requirements - # Available: model.displacements, model.ctetra_stress, etc. - # Use model.get_op2_stats() to see available results - - return {'result': None} - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_generic(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_extractors/extract_von_mises_stress.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_extractors/extract_von_mises_stress.py deleted file mode 100644 index 13db6227..00000000 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_extractors/extract_von_mises_stress.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Extract maximum von Mises stress from structural analysis -Auto-generated by Atomizer Phase 3 - pyNastran Research Agent - -Pattern: solid_stress -Element Type: CTETRA -Result Type: stress -API: model.ctetra_stress[subcase] or model.chexa_stress[subcase] -""" - -from pathlib import Path -from typing import Dict, Any -import numpy as np -from pyNastran.op2.op2 import OP2 - - -def extract_solid_stress(op2_file: Path, subcase: int = 1, element_type: str = 'ctetra'): - """Extract stress from solid elements.""" - from pyNastran.op2.op2 import OP2 - import numpy as np - - model = OP2() - model.read_op2(str(op2_file)) - - # Get stress object for element type - # In pyNastran, stress is stored in model.op2_results.stress - stress_attr = f"{element_type}_stress" - - if not hasattr(model, 'op2_results') or not hasattr(model.op2_results, 'stress'): - raise ValueError(f"No stress results in OP2") - - stress_obj = model.op2_results.stress - if not hasattr(stress_obj, stress_attr): - raise ValueError(f"No {element_type} stress results in OP2") - - stress = getattr(stress_obj, stress_attr)[subcase] - itime = 0 - - # Extract von Mises if available - if stress.is_von_mises: # Property, not method - von_mises = stress.data[itime, :, 9] # Column 9 is von Mises - max_stress = float(np.max(von_mises)) - - # Get element info - element_ids = [eid for (eid, node) in stress.element_node] - max_stress_elem = element_ids[np.argmax(von_mises)] - - return { - 'max_von_mises': max_stress, - 'max_stress_element': int(max_stress_elem) - } - else: - raise ValueError("von Mises stress not available") - - -if __name__ == '__main__': - # Example usage - import sys - if len(sys.argv) > 1: - op2_file = Path(sys.argv[1]) - result = extract_solid_stress(op2_file) - print(f"Extraction result: {result}") - else: - print("Usage: python {sys.argv[0]} ") diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/test_e2e_3trials_20251117_211724.db b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/test_e2e_3trials_20251117_211724.db deleted file mode 100644 index 48b7eb93..00000000 Binary files a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/test_e2e_3trials_20251117_211724.db and /dev/null differ diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_extractors/extract_displacement.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/generated_extractors/extract_displacement.py similarity index 100% rename from studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_extractors/extract_displacement.py rename to studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/generated_extractors/extract_displacement.py diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_extractors/extract_mass.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/generated_extractors/extract_mass.py similarity index 96% rename from studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_extractors/extract_mass.py rename to studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/generated_extractors/extract_mass.py index 2ee2f0b2..d8fe0ad9 100644 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_extractors/extract_mass.py +++ b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/generated_extractors/extract_mass.py @@ -1,5 +1,5 @@ """ -Extract total structural mass +Extract total mass of the structure Auto-generated by Atomizer Phase 3 - pyNastran Research Agent Pattern: generic_extraction diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_extractors/extract_von_mises_stress.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/generated_extractors/extract_von_mises_stress.py similarity index 100% rename from studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211500/generated_extractors/extract_von_mises_stress.py rename to studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/generated_extractors/extract_von_mises_stress.py diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_hooks/constraint_evaluation.py b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/generated_hooks/constraint_penalty.py similarity index 81% rename from studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_hooks/constraint_evaluation.py rename to studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/generated_hooks/constraint_penalty.py index 1756d158..e77cc654 100644 --- a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_211724/generated_hooks/constraint_evaluation.py +++ b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/generated_hooks/constraint_penalty.py @@ -1,9 +1,9 @@ """ -Evaluate displacement and stress constraints +Apply penalty to objective function if constraints are violated Auto-generated lifecycle hook by Atomizer Phase 2.9 Hook Point: post_calculation -Inputs: max_displacement, max_von_mises_stress +Inputs: max_displacement, max_von_mises_stress, total_mass Outputs: constraint, constraint_satisfied, constraint_violation """ @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) def constraint_hook(context: Dict[str, Any]) -> Optional[Dict[str, Any]]: """ - Evaluate displacement and stress constraints + Apply penalty to objective function if constraints are violated Args: context: Hook context containing: @@ -42,6 +42,11 @@ def constraint_hook(context: Dict[str, Any]) -> Optional[Dict[str, Any]]: logger.error(f"Required input 'max_von_mises_stress' not found in context") raise ValueError(f"Missing required input: max_von_mises_stress") + total_mass = calculations.get('total_mass') or results.get('total_mass') + if total_mass is None: + logger.error(f"Required input 'total_mass' not found in context") + raise ValueError(f"Missing required input: total_mass") + # Check constraint value = max_displacement / 1.0 satisfied = value <= 1.0 @@ -69,7 +74,7 @@ def register_hooks(hook_manager): hook_manager.register_hook( hook_point='post_calculation', function=constraint_hook, - description="Evaluate displacement and stress constraints", + description="Apply penalty to objective function if constraints are violated", name="constraint_hook", priority=100, enabled=True diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/optimization_history_incremental.json b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/optimization_history_incremental.json new file mode 100644 index 00000000..256ba4b8 --- /dev/null +++ b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/optimization_history_incremental.json @@ -0,0 +1,56 @@ +[ + { + "trial_number": 0, + "design_variables": { + "beam_half_core_thickness": 0.3934195274712988, + "beam_face_thickness": 0.07146930128208218 + }, + "results": { + "max_displacement": 7315679.0, + "max_disp_node": 5225.0, + "max_disp_x": 156.94375610351562, + "max_disp_y": 2.3414955139160156, + "max_disp_z": 7315679.0 + }, + "calculations": { + "check_displacement_constraint_result": 7315679.0 + }, + "objective": 7315679.0 + }, + { + "trial_number": 1, + "design_variables": { + "beam_half_core_thickness": 0.9471234007929267, + "beam_face_thickness": 0.6764087446304129 + }, + "results": { + "max_displacement": 9158.6748046875, + "max_disp_node": 5204.0, + "max_disp_x": 36.21176528930664, + "max_disp_y": 0.5410690903663635, + "max_disp_z": 9158.603515625 + }, + "calculations": { + "check_displacement_constraint_result": 9158.6748046875 + }, + "objective": 9158.6748046875 + }, + { + "trial_number": 2, + "design_variables": { + "beam_half_core_thickness": 0.2679911361372036, + "beam_face_thickness": 0.7283091311059705 + }, + "results": { + "max_displacement": 7655.2783203125, + "max_disp_node": 5224.0, + "max_disp_x": 47.8192024230957, + "max_disp_y": 0.7131573557853699, + "max_disp_z": 7655.1298828125 + }, + "calculations": { + "check_displacement_constraint_result": 7655.2783203125 + }, + "objective": 7655.2783203125 + } +] \ No newline at end of file diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/optimization_results.json b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/optimization_results.json new file mode 100644 index 00000000..6e173135 --- /dev/null +++ b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/optimization_results.json @@ -0,0 +1,11 @@ +{ + "best_params": { + "beam_half_core_thickness": 0.2679911361372036, + "beam_face_thickness": 0.7283091311059705 + }, + "best_value": 7655.2783203125, + "best_trial_number": 2, + "timestamp": "2025-11-17T21:28:43.477179", + "study_name": "test_e2e_3trials_20251117_212730", + "n_trials": 3 +} \ No newline at end of file diff --git a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/test_e2e_3trials_20251117_210933.db b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/test_e2e_3trials_20251117_212730.db similarity index 99% rename from studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/test_e2e_3trials_20251117_210933.db rename to studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/test_e2e_3trials_20251117_212730.db index e75df211..fee8b847 100644 Binary files a/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_210933/test_e2e_3trials_20251117_210933.db and b/studies/simple_beam_optimization/2_substudies/test_e2e_3trials_20251117_212730/test_e2e_3trials_20251117_212730.db differ diff --git a/tests/test_phase_3_2_e2e.py b/tests/test_phase_3_2_e2e.py index ea6dcd36..35e9d18c 100644 --- a/tests/test_phase_3_2_e2e.py +++ b/tests/test_phase_3_2_e2e.py @@ -280,24 +280,24 @@ def test_e2e_llm_mode_with_api_key(): traceback.print_exc() checks.append(False) - # Verify best trial file - if best_trial_file.exists(): - print("Verifying best trial file...") + # Verify results file + if results_file.exists(): + print("Verifying results file...") try: - with open(best_trial_file) as f: - best = json.load(f) + with open(results_file) as f: + results = json.load(f) - if "design_variables" in best and "objective" in best: - print(f" [OK] Best trial file has correct structure") - print(f" Best objective: {best['objective']:.6f}") + if "best_params" in results and "best_value" in results: + print(f" [OK] Results file has correct structure") + print(f" Best value: {results['best_value']:.6f}") checks.append(True) else: - print(f" [FAIL] Best trial file missing fields") + print(f" [FAIL] Results file missing fields") checks.append(False) except Exception as e: - print(f" [FAIL] Error reading best trial file: {e}") + print(f" [FAIL] Error reading results file: {e}") checks.append(False) print()