feat: Add precision rounding for optimization values

Round design variables, objectives, and constraints to appropriate
decimal precision based on physical units (4 decimals for mm, degrees, MPa).

- Added _get_precision() method with unit-based precision mapping
- Round design variables when sampled from Optuna
- Round extracted results (objectives and constraints)
- Added units field to objectives in config files
- Tested: values now show 4 decimals instead of 17+

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-15 12:56:50 -05:00
parent d694344b9f
commit a267e2d6f0
12 changed files with 5753 additions and 6303 deletions

View File

@@ -98,6 +98,32 @@ class OptimizationRunner:
else:
raise ValueError(f"Unknown sampler: {sampler_name}. Choose from ['TPE', 'CMAES', 'GP']")
def _get_precision(self, var_name: str, units: str) -> int:
"""
Get appropriate decimal precision based on units.
Args:
var_name: Variable name
units: Physical units (mm, degrees, MPa, etc.)
Returns:
Number of decimal places
"""
precision_map = {
'mm': 4,
'millimeter': 4,
'degrees': 4,
'deg': 4,
'mpa': 4,
'gpa': 6,
'kg': 3,
'n': 2,
'dimensionless': 6
}
units_lower = units.lower() if units else 'dimensionless'
return precision_map.get(units_lower, 4) # Default to 4 decimals
def _objective_function(self, trial: optuna.Trial) -> float:
"""
Optuna objective function.
@@ -110,15 +136,18 @@ class OptimizationRunner:
Returns:
Objective value (float) or tuple of values for multi-objective
"""
# 1. Sample design variables
# 1. Sample design variables with appropriate precision
design_vars = {}
for dv in self.config['design_variables']:
if dv['type'] == 'continuous':
design_vars[dv['name']] = trial.suggest_float(
value = trial.suggest_float(
dv['name'],
dv['bounds'][0],
dv['bounds'][1]
)
# Round to appropriate precision
precision = self._get_precision(dv['name'], dv.get('units', ''))
design_vars[dv['name']] = round(value, precision)
elif dv['type'] == 'discrete':
design_vars[dv['name']] = trial.suggest_int(
dv['name'],
@@ -140,7 +169,7 @@ class OptimizationRunner:
print(f"Error running simulation: {e}")
raise optuna.TrialPruned()
# 4. Extract results
# 4. Extract results with appropriate precision
extracted_results = {}
for obj in self.config['objectives']:
extractor_name = obj['extractor']
@@ -151,12 +180,15 @@ class OptimizationRunner:
try:
result = extractor_func(result_path)
metric_name = obj['metric']
extracted_results[obj['name']] = result[metric_name]
value = result[metric_name]
# Round to appropriate precision based on units
precision = self._get_precision(obj['name'], obj.get('units', ''))
extracted_results[obj['name']] = round(value, precision)
except Exception as e:
print(f"Error extracting {obj['name']}: {e}")
raise optuna.TrialPruned()
# Extract constraints
# Extract constraints with appropriate precision
for const in self.config.get('constraints', []):
extractor_name = const['extractor']
if extractor_name not in self.result_extractors:
@@ -166,7 +198,10 @@ class OptimizationRunner:
try:
result = extractor_func(result_path)
metric_name = const['metric']
extracted_results[const['name']] = result[metric_name]
value = result[metric_name]
# Round to appropriate precision based on units
precision = self._get_precision(const['name'], const.get('units', ''))
extracted_results[const['name']] = round(value, precision)
except Exception as e:
print(f"Error extracting {const['name']}: {e}")
raise optuna.TrialPruned()