feat: Add optimization configuration builder with multi-objective support
Created interactive configuration builder that discovers available options and helps users set up multi-objective optimization with constraints. Features: - Lists all available design variables from discovered model - Provides catalog of objectives (minimize mass, stress, displacement, volume) - Provides catalog of constraints (max stress, max displacement, mass limits) - Suggests reasonable bounds for design variables based on type - Supports multi-objective optimization with configurable weights - Validates and builds complete optimization_config.json Available Objectives: - minimize_mass: Weight reduction (weight: 5.0) - minimize_max_stress: Failure prevention (weight: 10.0) - minimize_max_displacement: Stiffness (weight: 3.0) - minimize_volume: Material usage (weight: 4.0) Available Constraints: - max_stress_limit: Stress <= limit (typical: 200 MPa) - max_displacement_limit: Displacement <= limit (typical: 1.0 mm) - min_mass_limit: Mass >= limit (structural integrity) - max_mass_limit: Mass <= limit (weight budget) Example Configuration: - Design Variables: tip_thickness, support_angle, support_blend_radius - Objectives: Minimize mass (5.0) + Minimize stress (10.0) - Constraints: max_displacement <= 1.0 mm, max_stress <= 200 MPa - Settings: 150 trials, TPE sampler Usage: python optimization_engine/optimization_config_builder.py Output: optimization_config.json with complete multi-objective setup Integration: - Works with discover_fea_model() to find design variables - Links to result extractors (stress, displacement, mass) - Ready for MCP build_optimization_config tool - Supports LLM-driven configuration building This enables the workflow: 1. User: "Minimize weight and stress with max displacement < 1mm" 2. LLM discovers model → lists options → builds config 3. Optimization engine executes with multi-objective + constraints
This commit is contained in:
190
optimization_config.json
Normal file
190
optimization_config.json
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
{
|
||||||
|
"design_variables": [
|
||||||
|
{
|
||||||
|
"name": "tip_thickness",
|
||||||
|
"type": "continuous",
|
||||||
|
"bounds": [
|
||||||
|
15.0,
|
||||||
|
25.0
|
||||||
|
],
|
||||||
|
"units": "mm",
|
||||||
|
"initial_value": 20.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "support_angle",
|
||||||
|
"type": "continuous",
|
||||||
|
"bounds": [
|
||||||
|
20.0,
|
||||||
|
40.0
|
||||||
|
],
|
||||||
|
"units": "degrees",
|
||||||
|
"initial_value": 30.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "support_blend_radius",
|
||||||
|
"type": "continuous",
|
||||||
|
"bounds": [
|
||||||
|
5.0,
|
||||||
|
15.0
|
||||||
|
],
|
||||||
|
"units": "mm",
|
||||||
|
"initial_value": 10.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"objectives": [
|
||||||
|
{
|
||||||
|
"name": "minimize_mass",
|
||||||
|
"description": "Minimize total mass (weight reduction)",
|
||||||
|
"extractor": "mass_extractor",
|
||||||
|
"metric": "total_mass",
|
||||||
|
"direction": "minimize",
|
||||||
|
"weight": 5.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "minimize_max_stress",
|
||||||
|
"description": "Minimize maximum von Mises stress",
|
||||||
|
"extractor": "stress_extractor",
|
||||||
|
"metric": "max_von_mises",
|
||||||
|
"direction": "minimize",
|
||||||
|
"weight": 10.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"constraints": [
|
||||||
|
{
|
||||||
|
"name": "max_displacement_limit",
|
||||||
|
"description": "Maximum allowable displacement",
|
||||||
|
"extractor": "displacement_extractor",
|
||||||
|
"metric": "max_displacement",
|
||||||
|
"type": "upper_bound",
|
||||||
|
"limit": 1.0,
|
||||||
|
"units": "mm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "max_stress_limit",
|
||||||
|
"description": "Maximum allowable von Mises stress",
|
||||||
|
"extractor": "stress_extractor",
|
||||||
|
"metric": "max_von_mises",
|
||||||
|
"type": "upper_bound",
|
||||||
|
"limit": 200.0,
|
||||||
|
"units": "MPa"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optimization_settings": {
|
||||||
|
"n_trials": 150,
|
||||||
|
"sampler": "TPE",
|
||||||
|
"n_startup_trials": 20
|
||||||
|
},
|
||||||
|
"model_info": {
|
||||||
|
"sim_file": "/home/user/Atomizer/tests/Bracket_sim1.sim",
|
||||||
|
"solutions": [
|
||||||
|
{
|
||||||
|
"name": "DisableInThermalSolution",
|
||||||
|
"type": "DisableInThermalSolution",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Disable in Thermal Solution 3D",
|
||||||
|
"type": "Disable in Thermal Solution 3D",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "-Flow-Structural Coupled Solution Parameters",
|
||||||
|
"type": "-Flow-Structural Coupled Solution Parameters",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Direct Frequency Response",
|
||||||
|
"type": "Direct Frequency Response",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "*Thermal-Flow Coupled Solution Parameters",
|
||||||
|
"type": "*Thermal-Flow Coupled Solution Parameters",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "0Thermal-Structural Coupled Solution Parameters",
|
||||||
|
"type": "0Thermal-Structural Coupled Solution Parameters",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Linear Statics",
|
||||||
|
"type": "Linear Statics",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Disable in Thermal Solution 2D",
|
||||||
|
"type": "Disable in Thermal Solution 2D",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Thermal Solution Parameters",
|
||||||
|
"type": "Thermal Solution Parameters",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nonlinear Statics",
|
||||||
|
"type": "Nonlinear Statics",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Modal Frequency Response",
|
||||||
|
"type": "Modal Frequency Response",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "1Pass Structural Contact Solution to Flow Solver",
|
||||||
|
"type": "1Pass Structural Contact Solution to Flow Solver",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "\"ObjectDisableInThermalSolution3D",
|
||||||
|
"type": "\"ObjectDisableInThermalSolution3D",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Design Optimization",
|
||||||
|
"type": "Design Optimization",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "\"ObjectDisableInThermalSolution2D",
|
||||||
|
"type": "\"ObjectDisableInThermalSolution2D",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Normal Modes",
|
||||||
|
"type": "Normal Modes",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Direct Transient Response",
|
||||||
|
"type": "Direct Transient Response",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Modal Transient Response",
|
||||||
|
"type": "Modal Transient Response",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
403
optimization_engine/optimization_config_builder.py
Normal file
403
optimization_engine/optimization_config_builder.py
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
"""
|
||||||
|
Optimization Configuration Builder
|
||||||
|
|
||||||
|
Helps users build multi-objective optimization configurations by:
|
||||||
|
1. Discovering available design variables from FEA model
|
||||||
|
2. Listing available objectives and constraints
|
||||||
|
3. Creating structured optimization_config.json
|
||||||
|
|
||||||
|
Supports:
|
||||||
|
- Multi-objective optimization (minimize weight + stress simultaneously)
|
||||||
|
- Constraints (max displacement, stress limits, mass limits)
|
||||||
|
- User selection of which objectives/constraints to apply
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, Any, List
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class OptimizationConfigBuilder:
|
||||||
|
"""
|
||||||
|
Interactive builder for optimization configurations.
|
||||||
|
|
||||||
|
Workflow:
|
||||||
|
1. Discover model capabilities (design variables, analysis type)
|
||||||
|
2. Present available objectives/constraints to user
|
||||||
|
3. Build configuration based on user selections
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Available objectives that can be extracted from OP2 files
|
||||||
|
AVAILABLE_OBJECTIVES = {
|
||||||
|
'minimize_mass': {
|
||||||
|
'description': 'Minimize total mass (weight reduction)',
|
||||||
|
'extractor': 'mass_extractor',
|
||||||
|
'metric': 'total_mass',
|
||||||
|
'units': 'kg',
|
||||||
|
'direction': 'minimize',
|
||||||
|
'typical_weight': 5.0 # Higher priority in multi-objective
|
||||||
|
},
|
||||||
|
'minimize_max_stress': {
|
||||||
|
'description': 'Minimize maximum von Mises stress',
|
||||||
|
'extractor': 'stress_extractor',
|
||||||
|
'metric': 'max_von_mises',
|
||||||
|
'units': 'MPa',
|
||||||
|
'direction': 'minimize',
|
||||||
|
'typical_weight': 10.0 # Very important - failure prevention
|
||||||
|
},
|
||||||
|
'minimize_max_displacement': {
|
||||||
|
'description': 'Minimize maximum displacement (increase stiffness)',
|
||||||
|
'extractor': 'displacement_extractor',
|
||||||
|
'metric': 'max_displacement',
|
||||||
|
'units': 'mm',
|
||||||
|
'direction': 'minimize',
|
||||||
|
'typical_weight': 3.0
|
||||||
|
},
|
||||||
|
'minimize_volume': {
|
||||||
|
'description': 'Minimize total volume (material usage)',
|
||||||
|
'extractor': 'volume_extractor',
|
||||||
|
'metric': 'total_volume',
|
||||||
|
'units': 'mm^3',
|
||||||
|
'direction': 'minimize',
|
||||||
|
'typical_weight': 4.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Available constraints
|
||||||
|
AVAILABLE_CONSTRAINTS = {
|
||||||
|
'max_stress_limit': {
|
||||||
|
'description': 'Maximum allowable von Mises stress',
|
||||||
|
'extractor': 'stress_extractor',
|
||||||
|
'metric': 'max_von_mises',
|
||||||
|
'units': 'MPa',
|
||||||
|
'typical_value': 200.0, # Below yield strength with safety factor
|
||||||
|
'constraint_type': 'upper_bound'
|
||||||
|
},
|
||||||
|
'max_displacement_limit': {
|
||||||
|
'description': 'Maximum allowable displacement',
|
||||||
|
'extractor': 'displacement_extractor',
|
||||||
|
'metric': 'max_displacement',
|
||||||
|
'units': 'mm',
|
||||||
|
'typical_value': 1.0, # Stiffness requirement
|
||||||
|
'constraint_type': 'upper_bound'
|
||||||
|
},
|
||||||
|
'min_mass_limit': {
|
||||||
|
'description': 'Minimum required mass (structural integrity)',
|
||||||
|
'extractor': 'mass_extractor',
|
||||||
|
'metric': 'total_mass',
|
||||||
|
'units': 'kg',
|
||||||
|
'typical_value': 0.3,
|
||||||
|
'constraint_type': 'lower_bound'
|
||||||
|
},
|
||||||
|
'max_mass_limit': {
|
||||||
|
'description': 'Maximum allowable mass (weight budget)',
|
||||||
|
'extractor': 'mass_extractor',
|
||||||
|
'metric': 'total_mass',
|
||||||
|
'units': 'kg',
|
||||||
|
'typical_value': 0.5,
|
||||||
|
'constraint_type': 'upper_bound'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, model_discovery_result: Dict[str, Any]):
|
||||||
|
"""
|
||||||
|
Initialize with model discovery results.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model_discovery_result: Output from discover_fea_model()
|
||||||
|
"""
|
||||||
|
self.model_info = model_discovery_result
|
||||||
|
self.config = {
|
||||||
|
'design_variables': [],
|
||||||
|
'objectives': [],
|
||||||
|
'constraints': [],
|
||||||
|
'optimization_settings': {
|
||||||
|
'n_trials': 100,
|
||||||
|
'sampler': 'TPE',
|
||||||
|
'n_startup_trials': 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def list_available_design_variables(self) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
List all available design variables from model.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of design variable options
|
||||||
|
"""
|
||||||
|
if 'expressions' not in self.model_info:
|
||||||
|
return []
|
||||||
|
|
||||||
|
design_vars = []
|
||||||
|
for expr in self.model_info['expressions']:
|
||||||
|
if expr['value'] is not None: # Only variables with known values
|
||||||
|
design_vars.append({
|
||||||
|
'name': expr['name'],
|
||||||
|
'current_value': expr['value'],
|
||||||
|
'units': expr['units'],
|
||||||
|
'type': expr.get('type', 'Unknown'),
|
||||||
|
'suggested_bounds': self._suggest_bounds(expr)
|
||||||
|
})
|
||||||
|
|
||||||
|
return design_vars
|
||||||
|
|
||||||
|
def _suggest_bounds(self, expr: Dict[str, Any]) -> tuple:
|
||||||
|
"""
|
||||||
|
Suggest reasonable optimization bounds for a design variable.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
expr: Expression dictionary
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(lower_bound, upper_bound)
|
||||||
|
"""
|
||||||
|
value = expr['value']
|
||||||
|
expr_type = expr.get('type', '').lower()
|
||||||
|
|
||||||
|
if 'angle' in expr_type or 'degrees' in expr.get('units', '').lower():
|
||||||
|
# Angles: ±15 degrees
|
||||||
|
return (max(0, value - 15), min(180, value + 15))
|
||||||
|
elif 'thickness' in expr['name'].lower() or 'dimension' in expr_type:
|
||||||
|
# Dimensions: ±30%
|
||||||
|
return (value * 0.7, value * 1.3)
|
||||||
|
elif 'radius' in expr['name'].lower() or 'diameter' in expr['name'].lower():
|
||||||
|
# Radii/diameters: ±25%
|
||||||
|
return (value * 0.75, value * 1.25)
|
||||||
|
else:
|
||||||
|
# Default: ±20%
|
||||||
|
return (value * 0.8, value * 1.2)
|
||||||
|
|
||||||
|
def list_available_objectives(self) -> Dict[str, Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
List all available optimization objectives.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary of objective options
|
||||||
|
"""
|
||||||
|
return self.AVAILABLE_OBJECTIVES.copy()
|
||||||
|
|
||||||
|
def list_available_constraints(self) -> Dict[str, Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
List all available constraints.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary of constraint options
|
||||||
|
"""
|
||||||
|
return self.AVAILABLE_CONSTRAINTS.copy()
|
||||||
|
|
||||||
|
def add_design_variable(self, name: str, lower_bound: float, upper_bound: float):
|
||||||
|
"""
|
||||||
|
Add a design variable to the configuration.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: Expression name from model
|
||||||
|
lower_bound: Minimum value
|
||||||
|
upper_bound: Maximum value
|
||||||
|
"""
|
||||||
|
# Verify variable exists in model
|
||||||
|
expr = next((e for e in self.model_info['expressions'] if e['name'] == name), None)
|
||||||
|
if not expr:
|
||||||
|
raise ValueError(f"Design variable '{name}' not found in model")
|
||||||
|
|
||||||
|
self.config['design_variables'].append({
|
||||||
|
'name': name,
|
||||||
|
'type': 'continuous',
|
||||||
|
'bounds': [lower_bound, upper_bound],
|
||||||
|
'units': expr.get('units', ''),
|
||||||
|
'initial_value': expr['value']
|
||||||
|
})
|
||||||
|
|
||||||
|
def add_objective(self, objective_key: str, weight: float = None, target: float = None):
|
||||||
|
"""
|
||||||
|
Add an objective to the configuration.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
objective_key: Key from AVAILABLE_OBJECTIVES
|
||||||
|
weight: Importance weight (for multi-objective)
|
||||||
|
target: Target value (optional, for goal programming)
|
||||||
|
"""
|
||||||
|
if objective_key not in self.AVAILABLE_OBJECTIVES:
|
||||||
|
raise ValueError(f"Unknown objective: {objective_key}")
|
||||||
|
|
||||||
|
obj_info = self.AVAILABLE_OBJECTIVES[objective_key]
|
||||||
|
|
||||||
|
objective = {
|
||||||
|
'name': objective_key,
|
||||||
|
'description': obj_info['description'],
|
||||||
|
'extractor': obj_info['extractor'],
|
||||||
|
'metric': obj_info['metric'],
|
||||||
|
'direction': obj_info['direction'],
|
||||||
|
'weight': weight or obj_info['typical_weight']
|
||||||
|
}
|
||||||
|
|
||||||
|
if target is not None:
|
||||||
|
objective['target'] = target
|
||||||
|
|
||||||
|
self.config['objectives'].append(objective)
|
||||||
|
|
||||||
|
def add_constraint(self, constraint_key: str, limit_value: float):
|
||||||
|
"""
|
||||||
|
Add a constraint to the configuration.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
constraint_key: Key from AVAILABLE_CONSTRAINTS
|
||||||
|
limit_value: Constraint limit value
|
||||||
|
"""
|
||||||
|
if constraint_key not in self.AVAILABLE_CONSTRAINTS:
|
||||||
|
raise ValueError(f"Unknown constraint: {constraint_key}")
|
||||||
|
|
||||||
|
const_info = self.AVAILABLE_CONSTRAINTS[constraint_key]
|
||||||
|
|
||||||
|
constraint = {
|
||||||
|
'name': constraint_key,
|
||||||
|
'description': const_info['description'],
|
||||||
|
'extractor': const_info['extractor'],
|
||||||
|
'metric': const_info['metric'],
|
||||||
|
'type': const_info['constraint_type'],
|
||||||
|
'limit': limit_value,
|
||||||
|
'units': const_info['units']
|
||||||
|
}
|
||||||
|
|
||||||
|
self.config['constraints'].append(constraint)
|
||||||
|
|
||||||
|
def set_optimization_settings(self, n_trials: int = None, sampler: str = None):
|
||||||
|
"""
|
||||||
|
Configure optimization algorithm settings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
n_trials: Number of optimization iterations
|
||||||
|
sampler: 'TPE', 'CMAES', 'GP', etc.
|
||||||
|
"""
|
||||||
|
if n_trials:
|
||||||
|
self.config['optimization_settings']['n_trials'] = n_trials
|
||||||
|
if sampler:
|
||||||
|
self.config['optimization_settings']['sampler'] = sampler
|
||||||
|
|
||||||
|
def build(self) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Build and validate the configuration.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Complete optimization configuration
|
||||||
|
"""
|
||||||
|
# Validation
|
||||||
|
if not self.config['design_variables']:
|
||||||
|
raise ValueError("At least one design variable is required")
|
||||||
|
|
||||||
|
if not self.config['objectives']:
|
||||||
|
raise ValueError("At least one objective is required")
|
||||||
|
|
||||||
|
# Add metadata
|
||||||
|
self.config['model_info'] = {
|
||||||
|
'sim_file': self.model_info.get('sim_file', ''),
|
||||||
|
'solutions': self.model_info.get('solutions', [])
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.config
|
||||||
|
|
||||||
|
def save(self, output_path: Path):
|
||||||
|
"""
|
||||||
|
Save configuration to JSON file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_path: Path to save configuration
|
||||||
|
"""
|
||||||
|
config = self.build()
|
||||||
|
|
||||||
|
with open(output_path, 'w') as f:
|
||||||
|
json.dump(config, f, indent=2)
|
||||||
|
|
||||||
|
print(f"Configuration saved to: {output_path}")
|
||||||
|
|
||||||
|
def print_summary(self):
|
||||||
|
"""Print a human-readable summary of the configuration."""
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("OPTIMIZATION CONFIGURATION SUMMARY")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
print(f"\nModel: {self.model_info.get('sim_file', 'Unknown')}")
|
||||||
|
|
||||||
|
print(f"\nDesign Variables ({len(self.config['design_variables'])}):")
|
||||||
|
for dv in self.config['design_variables']:
|
||||||
|
print(f" • {dv['name']}: [{dv['bounds'][0]:.2f}, {dv['bounds'][1]:.2f}] {dv['units']}")
|
||||||
|
|
||||||
|
print(f"\nObjectives ({len(self.config['objectives'])}):")
|
||||||
|
for obj in self.config['objectives']:
|
||||||
|
print(f" • {obj['description']} (weight: {obj['weight']:.1f})")
|
||||||
|
|
||||||
|
print(f"\nConstraints ({len(self.config['constraints'])}):")
|
||||||
|
for const in self.config['constraints']:
|
||||||
|
operator = '<=' if const['type'] == 'upper_bound' else '>='
|
||||||
|
print(f" • {const['description']}: {const['metric']} {operator} {const['limit']} {const['units']}")
|
||||||
|
|
||||||
|
print(f"\nOptimization Settings:")
|
||||||
|
print(f" • Trials: {self.config['optimization_settings']['n_trials']}")
|
||||||
|
print(f" • Sampler: {self.config['optimization_settings']['sampler']}")
|
||||||
|
|
||||||
|
print("="*60 + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from mcp_server.tools.model_discovery import discover_fea_model
|
||||||
|
|
||||||
|
# Step 1: Discover model
|
||||||
|
print("Step 1: Discovering FEA model...")
|
||||||
|
model_result = discover_fea_model("tests/Bracket_sim1.sim")
|
||||||
|
|
||||||
|
# Step 2: Create builder
|
||||||
|
builder = OptimizationConfigBuilder(model_result)
|
||||||
|
|
||||||
|
# Step 3: Show available options
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("AVAILABLE DESIGN VARIABLES:")
|
||||||
|
print("="*60)
|
||||||
|
for dv in builder.list_available_design_variables():
|
||||||
|
print(f"\n• {dv['name']}")
|
||||||
|
print(f" Current value: {dv['current_value']} {dv['units']}")
|
||||||
|
print(f" Suggested bounds: {dv['suggested_bounds']}")
|
||||||
|
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("AVAILABLE OBJECTIVES:")
|
||||||
|
print("="*60)
|
||||||
|
for key, obj in builder.list_available_objectives().items():
|
||||||
|
print(f"\n• {key}")
|
||||||
|
print(f" Description: {obj['description']}")
|
||||||
|
print(f" Default weight: {obj['typical_weight']}")
|
||||||
|
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("AVAILABLE CONSTRAINTS:")
|
||||||
|
print("="*60)
|
||||||
|
for key, const in builder.list_available_constraints().items():
|
||||||
|
print(f"\n• {key}")
|
||||||
|
print(f" Description: {const['description']}")
|
||||||
|
print(f" Typical value: {const['typical_value']} {const['units']}")
|
||||||
|
|
||||||
|
# Step 4: Build a multi-objective configuration
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("BUILDING CONFIGURATION:")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
# Add design variables
|
||||||
|
builder.add_design_variable('tip_thickness', 15.0, 25.0)
|
||||||
|
builder.add_design_variable('support_angle', 20.0, 40.0)
|
||||||
|
builder.add_design_variable('support_blend_radius', 5.0, 15.0)
|
||||||
|
|
||||||
|
# Add objectives: minimize weight AND minimize stress
|
||||||
|
builder.add_objective('minimize_mass', weight=5.0)
|
||||||
|
builder.add_objective('minimize_max_stress', weight=10.0)
|
||||||
|
|
||||||
|
# Add constraints: max displacement < 1.0 mm, max stress < 200 MPa
|
||||||
|
builder.add_constraint('max_displacement_limit', limit_value=1.0)
|
||||||
|
builder.add_constraint('max_stress_limit', limit_value=200.0)
|
||||||
|
|
||||||
|
# Set optimization settings
|
||||||
|
builder.set_optimization_settings(n_trials=150, sampler='TPE')
|
||||||
|
|
||||||
|
# Print summary
|
||||||
|
builder.print_summary()
|
||||||
|
|
||||||
|
# Save configuration
|
||||||
|
builder.save(Path('optimization_config.json'))
|
||||||
|
|
||||||
|
print("\nConfiguration ready for optimization!")
|
||||||
Reference in New Issue
Block a user