feat: Add MCP build_optimization_config tool
Integrate OP2 data extraction with optimization config builder: - Add build_optimization_config() MCP tool - Add list_optimization_options() helper - Add format_optimization_options_for_llm() formatter - Update MCP tools documentation with full API details - Test with bracket example, generates valid config Features: - Discovers design variables from FEA model - Lists 4 available objectives (mass, stress, displacement, volume) - Lists 4 available constraints (stress/displacement/mass limits) - Validates user selections against model - Generates complete optimization_config.json Tested with examples/bracket/Bracket_sim1.sim: - Found 4 design variables (support_angle, tip_thickness, p3, support_blend_radius) - Created config with 2 objectives, 2 constraints, 150 trials 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2
LICENSE
2
LICENSE
@@ -9,4 +9,4 @@ property of Atomaste. Unauthorized copying, modification, distribution, or use o
|
|||||||
Software, via any medium, is strictly prohibited without prior written permission from
|
Software, via any medium, is strictly prohibited without prior written permission from
|
||||||
Atomaste.
|
Atomaste.
|
||||||
|
|
||||||
For licensing inquiries, please contact: contact@atomaste.com
|
For licensing inquiries, please contact: antoine@atomaste.ca
|
||||||
|
|||||||
180
examples/bracket/optimization_config.json
Normal file
180
examples/bracket/optimization_config.json
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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": "C:/Users/antoi/Documents/Atomaste/Atomizer/examples/bracket/Bracket_sim1.sim",
|
||||||
|
"solutions": [
|
||||||
|
{
|
||||||
|
"name": "Direct Frequency Response",
|
||||||
|
"type": "Direct Frequency Response",
|
||||||
|
"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": "Nonlinear Statics",
|
||||||
|
"type": "Nonlinear Statics",
|
||||||
|
"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": "*Thermal-Flow Coupled Solution Parameters",
|
||||||
|
"type": "*Thermal-Flow Coupled Solution Parameters",
|
||||||
|
"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": "Disable in Thermal Solution 3D",
|
||||||
|
"type": "Disable in Thermal Solution 3D",
|
||||||
|
"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": "Direct Transient Response",
|
||||||
|
"type": "Direct Transient Response",
|
||||||
|
"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": "Normal Modes",
|
||||||
|
"type": "Normal Modes",
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "\"ObjectDisableInThermalSolution3D",
|
||||||
|
"type": "\"ObjectDisableInThermalSolution3D",
|
||||||
|
"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": "0Thermal-Structural Coupled Solution Parameters",
|
||||||
|
"type": "0Thermal-Structural Coupled Solution Parameters",
|
||||||
|
"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": "DisableInThermalSolution",
|
||||||
|
"type": "DisableInThermalSolution",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "\"ObjectDisableInThermalSolution2D",
|
||||||
|
"type": "\"ObjectDisableInThermalSolution2D",
|
||||||
|
"solver": "NX Nastran",
|
||||||
|
"description": "Extracted from binary .sim file"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,17 +53,77 @@ python mcp_server/tools/model_discovery.py examples/test_bracket.sim
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 2. Build Optimization Config (PLANNED)
|
### 2. Build Optimization Config (`optimization_config.py`) ✅ IMPLEMENTED
|
||||||
|
|
||||||
**Purpose**: Generate `optimization_config.json` from natural language requirements.
|
**Purpose**: Generate `optimization_config.json` from user selections of objectives, constraints, and design variables.
|
||||||
|
|
||||||
**Function**: `build_optimization_config(requirements: str, model_info: Dict) -> Dict[str, Any]`
|
**Functions**:
|
||||||
|
- `build_optimization_config(...)` - Create complete optimization configuration
|
||||||
|
- `list_optimization_options(sim_file_path)` - List all available options for a model
|
||||||
|
- `format_optimization_options_for_llm(options)` - Format options as Markdown
|
||||||
|
|
||||||
**Planned Features**:
|
**What it does**:
|
||||||
- Parse LLM instructions ("minimize stress while reducing mass")
|
- Discovers available design variables from the FEA model
|
||||||
- Select appropriate result extractors
|
- Lists available objectives (minimize mass, stress, displacement, volume)
|
||||||
- Suggest reasonable parameter bounds
|
- Lists available constraints (max stress, max displacement, mass limits)
|
||||||
- Generate complete config for optimization engine
|
- Builds a complete `optimization_config.json` based on user selections
|
||||||
|
- Validates that all selections are valid for the model
|
||||||
|
|
||||||
|
**Usage Example**:
|
||||||
|
```python
|
||||||
|
from mcp_server.tools import build_optimization_config, list_optimization_options
|
||||||
|
|
||||||
|
# Step 1: List available options
|
||||||
|
options = list_optimization_options("examples/bracket/Bracket_sim1.sim")
|
||||||
|
print(f"Available design variables: {len(options['available_design_variables'])}")
|
||||||
|
|
||||||
|
# Step 2: Build configuration
|
||||||
|
result = build_optimization_config(
|
||||||
|
sim_file_path="examples/bracket/Bracket_sim1.sim",
|
||||||
|
design_variables=[
|
||||||
|
{'name': 'tip_thickness', 'lower_bound': 15.0, 'upper_bound': 25.0},
|
||||||
|
{'name': 'support_angle', 'lower_bound': 20.0, 'upper_bound': 40.0}
|
||||||
|
],
|
||||||
|
objectives=[
|
||||||
|
{'objective_key': 'minimize_mass', 'weight': 5.0},
|
||||||
|
{'objective_key': 'minimize_max_stress', 'weight': 10.0}
|
||||||
|
],
|
||||||
|
constraints=[
|
||||||
|
{'constraint_key': 'max_displacement_limit', 'limit_value': 1.0},
|
||||||
|
{'constraint_key': 'max_stress_limit', 'limit_value': 200.0}
|
||||||
|
],
|
||||||
|
optimization_settings={
|
||||||
|
'n_trials': 150,
|
||||||
|
'sampler': 'TPE'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if result['status'] == 'success':
|
||||||
|
print(f"Config saved to: {result['config_file']}")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Command Line Usage**:
|
||||||
|
```bash
|
||||||
|
python mcp_server/tools/optimization_config.py examples/bracket/Bracket_sim1.sim
|
||||||
|
```
|
||||||
|
|
||||||
|
**Available Objectives**:
|
||||||
|
- `minimize_mass`: Minimize total mass (weight reduction)
|
||||||
|
- `minimize_max_stress`: Minimize maximum von Mises stress
|
||||||
|
- `minimize_max_displacement`: Minimize maximum displacement (increase stiffness)
|
||||||
|
- `minimize_volume`: Minimize total volume (material usage)
|
||||||
|
|
||||||
|
**Available Constraints**:
|
||||||
|
- `max_stress_limit`: Maximum allowable von Mises stress
|
||||||
|
- `max_displacement_limit`: Maximum allowable displacement
|
||||||
|
- `min_mass_limit`: Minimum required mass (structural integrity)
|
||||||
|
- `max_mass_limit`: Maximum allowable mass (weight budget)
|
||||||
|
|
||||||
|
**Output**: Creates `optimization_config.json` with:
|
||||||
|
- Design variable definitions with bounds
|
||||||
|
- Multi-objective configuration with weights
|
||||||
|
- Constraint definitions with limits
|
||||||
|
- Optimization algorithm settings (trials, sampler)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -218,4 +278,4 @@ These tools are designed to be called by the MCP server and consumed by LLMs. Th
|
|||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: 2025-11-15
|
**Last Updated**: 2025-11-15
|
||||||
**Status**: Phase 1 (Model Discovery) ✅ COMPLETE
|
**Status**: Phase 1 (Model Discovery) ✅ COMPLETE | Phase 2 (Optimization Config Builder) ✅ COMPLETE
|
||||||
|
|||||||
@@ -13,11 +13,18 @@ Available tools:
|
|||||||
|
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
from .model_discovery import discover_fea_model, format_discovery_result_for_llm
|
from .model_discovery import discover_fea_model, format_discovery_result_for_llm
|
||||||
|
from .optimization_config import (
|
||||||
|
build_optimization_config,
|
||||||
|
list_optimization_options,
|
||||||
|
format_optimization_options_for_llm
|
||||||
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"discover_fea_model",
|
"discover_fea_model",
|
||||||
"format_discovery_result_for_llm",
|
"format_discovery_result_for_llm",
|
||||||
"build_optimization_config",
|
"build_optimization_config",
|
||||||
|
"list_optimization_options",
|
||||||
|
"format_optimization_options_for_llm",
|
||||||
"start_optimization",
|
"start_optimization",
|
||||||
"query_optimization_status",
|
"query_optimization_status",
|
||||||
"extract_results",
|
"extract_results",
|
||||||
|
|||||||
368
mcp_server/tools/optimization_config.py
Normal file
368
mcp_server/tools/optimization_config.py
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
"""
|
||||||
|
MCP Tool: Build Optimization Configuration
|
||||||
|
|
||||||
|
Wraps the OptimizationConfigBuilder to create an MCP-compatible tool
|
||||||
|
that helps LLMs guide users through building optimization configurations.
|
||||||
|
|
||||||
|
This tool:
|
||||||
|
1. Discovers the FEA model (design variables)
|
||||||
|
2. Lists available objectives and constraints
|
||||||
|
3. Builds a complete optimization_config.json based on user selections
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, Any, List, Optional
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Add project root to path for imports
|
||||||
|
project_root = Path(__file__).parent.parent.parent
|
||||||
|
sys.path.insert(0, str(project_root))
|
||||||
|
|
||||||
|
from optimization_engine.optimization_config_builder import OptimizationConfigBuilder
|
||||||
|
from mcp_server.tools.model_discovery import discover_fea_model
|
||||||
|
|
||||||
|
|
||||||
|
def build_optimization_config(
|
||||||
|
sim_file_path: str,
|
||||||
|
design_variables: List[Dict[str, Any]],
|
||||||
|
objectives: List[Dict[str, Any]],
|
||||||
|
constraints: Optional[List[Dict[str, Any]]] = None,
|
||||||
|
optimization_settings: Optional[Dict[str, Any]] = None,
|
||||||
|
output_path: Optional[str] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
MCP Tool: Build Optimization Configuration
|
||||||
|
|
||||||
|
Creates a complete optimization configuration file from user selections.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sim_file_path: Absolute path to .sim file
|
||||||
|
design_variables: List of design variable definitions
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'name': 'tip_thickness',
|
||||||
|
'lower_bound': 15.0,
|
||||||
|
'upper_bound': 25.0
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
objectives: List of objective definitions
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'objective_key': 'minimize_mass',
|
||||||
|
'weight': 5.0, # optional
|
||||||
|
'target': None # optional, for goal programming
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
constraints: Optional list of constraint definitions
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'constraint_key': 'max_stress_limit',
|
||||||
|
'limit_value': 200.0
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
optimization_settings: Optional dict with algorithm settings
|
||||||
|
{
|
||||||
|
'n_trials': 100,
|
||||||
|
'sampler': 'TPE'
|
||||||
|
}
|
||||||
|
output_path: Optional path to save config JSON.
|
||||||
|
Defaults to 'optimization_config.json' in sim file directory
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with status and configuration details
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> result = build_optimization_config(
|
||||||
|
... sim_file_path="C:/Projects/Bracket/analysis.sim",
|
||||||
|
... design_variables=[
|
||||||
|
... {'name': 'tip_thickness', 'lower_bound': 15.0, 'upper_bound': 25.0}
|
||||||
|
... ],
|
||||||
|
... objectives=[
|
||||||
|
... {'objective_key': 'minimize_mass', 'weight': 5.0}
|
||||||
|
... ],
|
||||||
|
... constraints=[
|
||||||
|
... {'constraint_key': 'max_stress_limit', 'limit_value': 200.0}
|
||||||
|
... ]
|
||||||
|
... )
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Step 1: Discover model
|
||||||
|
model_result = discover_fea_model(sim_file_path)
|
||||||
|
|
||||||
|
if model_result['status'] != 'success':
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'error_type': 'model_discovery_failed',
|
||||||
|
'message': model_result.get('message', 'Failed to discover FEA model'),
|
||||||
|
'suggestion': model_result.get('suggestion', 'Check that the .sim file is valid')
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step 2: Create builder
|
||||||
|
builder = OptimizationConfigBuilder(model_result)
|
||||||
|
|
||||||
|
# Step 3: Validate and add design variables
|
||||||
|
available_vars = {dv['name']: dv for dv in builder.list_available_design_variables()}
|
||||||
|
|
||||||
|
for dv in design_variables:
|
||||||
|
name = dv['name']
|
||||||
|
if name not in available_vars:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'error_type': 'invalid_design_variable',
|
||||||
|
'message': f"Design variable '{name}' not found in model",
|
||||||
|
'available_variables': list(available_vars.keys()),
|
||||||
|
'suggestion': f"Choose from: {', '.join(available_vars.keys())}"
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.add_design_variable(
|
||||||
|
name=name,
|
||||||
|
lower_bound=dv['lower_bound'],
|
||||||
|
upper_bound=dv['upper_bound']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 4: Add objectives
|
||||||
|
available_objectives = builder.list_available_objectives()
|
||||||
|
|
||||||
|
for obj in objectives:
|
||||||
|
obj_key = obj['objective_key']
|
||||||
|
if obj_key not in available_objectives:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'error_type': 'invalid_objective',
|
||||||
|
'message': f"Objective '{obj_key}' not recognized",
|
||||||
|
'available_objectives': list(available_objectives.keys()),
|
||||||
|
'suggestion': f"Choose from: {', '.join(available_objectives.keys())}"
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.add_objective(
|
||||||
|
objective_key=obj_key,
|
||||||
|
weight=obj.get('weight'),
|
||||||
|
target=obj.get('target')
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 5: Add constraints (optional)
|
||||||
|
if constraints:
|
||||||
|
available_constraints = builder.list_available_constraints()
|
||||||
|
|
||||||
|
for const in constraints:
|
||||||
|
const_key = const['constraint_key']
|
||||||
|
if const_key not in available_constraints:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'error_type': 'invalid_constraint',
|
||||||
|
'message': f"Constraint '{const_key}' not recognized",
|
||||||
|
'available_constraints': list(available_constraints.keys()),
|
||||||
|
'suggestion': f"Choose from: {', '.join(available_constraints.keys())}"
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.add_constraint(
|
||||||
|
constraint_key=const_key,
|
||||||
|
limit_value=const['limit_value']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 6: Set optimization settings (optional)
|
||||||
|
if optimization_settings:
|
||||||
|
builder.set_optimization_settings(
|
||||||
|
n_trials=optimization_settings.get('n_trials'),
|
||||||
|
sampler=optimization_settings.get('sampler')
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 7: Build and validate configuration
|
||||||
|
config = builder.build()
|
||||||
|
|
||||||
|
# Step 8: Save to file
|
||||||
|
if output_path is None:
|
||||||
|
sim_path = Path(sim_file_path)
|
||||||
|
output_path = sim_path.parent / 'optimization_config.json'
|
||||||
|
else:
|
||||||
|
output_path = Path(output_path)
|
||||||
|
|
||||||
|
with open(output_path, 'w') as f:
|
||||||
|
json.dump(config, f, indent=2)
|
||||||
|
|
||||||
|
# Step 9: Return success with summary
|
||||||
|
return {
|
||||||
|
'status': 'success',
|
||||||
|
'message': 'Optimization configuration created successfully',
|
||||||
|
'config_file': str(output_path),
|
||||||
|
'summary': {
|
||||||
|
'design_variables': len(config['design_variables']),
|
||||||
|
'objectives': len(config['objectives']),
|
||||||
|
'constraints': len(config['constraints']),
|
||||||
|
'n_trials': config['optimization_settings']['n_trials'],
|
||||||
|
'sampler': config['optimization_settings']['sampler']
|
||||||
|
},
|
||||||
|
'config': config
|
||||||
|
}
|
||||||
|
|
||||||
|
except ValueError as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'error_type': 'validation_error',
|
||||||
|
'message': str(e),
|
||||||
|
'suggestion': 'Check that all required fields are provided correctly'
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'error_type': 'unexpected_error',
|
||||||
|
'message': str(e),
|
||||||
|
'suggestion': 'This may be a bug. Please report this issue.'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def list_optimization_options(sim_file_path: str) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Helper tool: List all available optimization options for a model.
|
||||||
|
|
||||||
|
This is useful for LLMs to show users what they can choose from.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sim_file_path: Absolute path to .sim file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with all available options
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Discover model
|
||||||
|
model_result = discover_fea_model(sim_file_path)
|
||||||
|
|
||||||
|
if model_result['status'] != 'success':
|
||||||
|
return model_result
|
||||||
|
|
||||||
|
# Create builder to get options
|
||||||
|
builder = OptimizationConfigBuilder(model_result)
|
||||||
|
|
||||||
|
# Get all available options
|
||||||
|
design_vars = builder.list_available_design_variables()
|
||||||
|
objectives = builder.list_available_objectives()
|
||||||
|
constraints = builder.list_available_constraints()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'status': 'success',
|
||||||
|
'sim_file': sim_file_path,
|
||||||
|
'available_design_variables': design_vars,
|
||||||
|
'available_objectives': objectives,
|
||||||
|
'available_constraints': constraints,
|
||||||
|
'model_info': {
|
||||||
|
'solutions': model_result.get('solutions', []),
|
||||||
|
'expression_count': len(model_result.get('expressions', []))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return {
|
||||||
|
'status': 'error',
|
||||||
|
'error_type': 'unexpected_error',
|
||||||
|
'message': str(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def format_optimization_options_for_llm(options: Dict[str, Any]) -> str:
|
||||||
|
"""
|
||||||
|
Format optimization options for LLM consumption (Markdown).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
options: Output from list_optimization_options()
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Markdown-formatted string
|
||||||
|
"""
|
||||||
|
if options['status'] != 'success':
|
||||||
|
return f"❌ **Error**: {options['message']}\n\n💡 {options.get('suggestion', '')}"
|
||||||
|
|
||||||
|
md = []
|
||||||
|
md.append(f"# Optimization Configuration Options\n")
|
||||||
|
md.append(f"**Model**: `{options['sim_file']}`\n")
|
||||||
|
|
||||||
|
# Design Variables
|
||||||
|
md.append(f"## Available Design Variables ({len(options['available_design_variables'])})\n")
|
||||||
|
if options['available_design_variables']:
|
||||||
|
md.append("| Name | Current Value | Units | Suggested Bounds |")
|
||||||
|
md.append("|------|---------------|-------|------------------|")
|
||||||
|
for dv in options['available_design_variables']:
|
||||||
|
bounds = dv['suggested_bounds']
|
||||||
|
md.append(f"| `{dv['name']}` | {dv['current_value']} | {dv['units']} | [{bounds[0]:.2f}, {bounds[1]:.2f}] |")
|
||||||
|
else:
|
||||||
|
md.append("⚠️ No design variables found. Model may not be parametric.")
|
||||||
|
md.append("")
|
||||||
|
|
||||||
|
# Objectives
|
||||||
|
md.append(f"## Available Objectives\n")
|
||||||
|
for key, obj in options['available_objectives'].items():
|
||||||
|
md.append(f"### `{key}`")
|
||||||
|
md.append(f"- **Description**: {obj['description']}")
|
||||||
|
md.append(f"- **Metric**: {obj['metric']} ({obj['units']})")
|
||||||
|
md.append(f"- **Default Weight**: {obj['typical_weight']}")
|
||||||
|
md.append(f"- **Extractor**: `{obj['extractor']}`")
|
||||||
|
md.append("")
|
||||||
|
|
||||||
|
# Constraints
|
||||||
|
md.append(f"## Available Constraints\n")
|
||||||
|
for key, const in options['available_constraints'].items():
|
||||||
|
md.append(f"### `{key}`")
|
||||||
|
md.append(f"- **Description**: {const['description']}")
|
||||||
|
md.append(f"- **Metric**: {const['metric']} ({const['units']})")
|
||||||
|
md.append(f"- **Typical Value**: {const['typical_value']}")
|
||||||
|
md.append(f"- **Type**: {const['constraint_type']}")
|
||||||
|
md.append(f"- **Extractor**: `{const['extractor']}`")
|
||||||
|
md.append("")
|
||||||
|
|
||||||
|
return "\n".join(md)
|
||||||
|
|
||||||
|
|
||||||
|
# For testing
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: python optimization_config.py <path_to_sim_file>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
sim_path = sys.argv[1]
|
||||||
|
|
||||||
|
# Test 1: List options
|
||||||
|
print("=" * 60)
|
||||||
|
print("TEST 1: List Available Options")
|
||||||
|
print("=" * 60)
|
||||||
|
options = list_optimization_options(sim_path)
|
||||||
|
print(format_optimization_options_for_llm(options))
|
||||||
|
|
||||||
|
# Test 2: Build configuration
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("TEST 2: Build Optimization Configuration")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
result = build_optimization_config(
|
||||||
|
sim_file_path=sim_path,
|
||||||
|
design_variables=[
|
||||||
|
{'name': 'tip_thickness', 'lower_bound': 15.0, 'upper_bound': 25.0},
|
||||||
|
{'name': 'support_angle', 'lower_bound': 20.0, 'upper_bound': 40.0},
|
||||||
|
],
|
||||||
|
objectives=[
|
||||||
|
{'objective_key': 'minimize_mass', 'weight': 5.0},
|
||||||
|
{'objective_key': 'minimize_max_stress', 'weight': 10.0}
|
||||||
|
],
|
||||||
|
constraints=[
|
||||||
|
{'constraint_key': 'max_displacement_limit', 'limit_value': 1.0},
|
||||||
|
{'constraint_key': 'max_stress_limit', 'limit_value': 200.0}
|
||||||
|
],
|
||||||
|
optimization_settings={
|
||||||
|
'n_trials': 150,
|
||||||
|
'sampler': 'TPE'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if result['status'] == 'success':
|
||||||
|
print(f"SUCCESS: Configuration saved to: {result['config_file']}")
|
||||||
|
print(f"\nSummary:")
|
||||||
|
for key, value in result['summary'].items():
|
||||||
|
print(f" - {key}: {value}")
|
||||||
|
else:
|
||||||
|
print(f"ERROR: {result['message']}")
|
||||||
|
print(f"Suggestion: {result.get('suggestion', '')}")
|
||||||
Reference in New Issue
Block a user