Source code for suboptimumg.sweep.pack_optimizer_sweep
import time
import warnings
from token import OP
from typing import List, Optional
import numpy as np
from click import Option
from scipy.optimize import differential_evolution
from ..compsim import energy_data
from ..compsim.competition_factory import from_data
from ..compsim.models import CompetitionData
from .pack_optimizer_models import *
from .pack_optimizer_results import *
[docs]
class PackOptimizer:
"""Differential-evolution optimizer for vehicle pack and driving parameters."""
def __init__(self, comp_data: CompetitionData, config: PackOptimizerConfig):
self.comp_data = comp_data
self.config = config
self.best_score: float = -np.inf
self.best_valaution: Optional[EvaluationRecord] = None
self.history: List[EvaluationRecord] = []
[docs]
def sweep(self) -> PackOptimizerResults:
"""Run the optimization and return a PackOptimizerResults object.
Returns
-------
PackOptimizerResults
"""
print(f"Starting optimization with {len(self.config.parameters)} parameters")
print(f"Max iterations: {self.config.max_iterations}")
print(f"Population size: {self.config.population_size}")
# Convert our SweepParamConfig list to scipy DE bounds format
bounds = [(p.min, p.max) for p in self.config.parameters]
de_result = differential_evolution(
self._objective,
bounds,
maxiter=self.config.max_iterations,
popsize=self.config.population_size,
seed=self.config.seed,
)
if de_result.success:
print(f"Optimization completed successfully.")
else:
print(f"Optimization did not converge: {de_result.message}")
data = PackOptimizationData(
optimal_evaluation=self.best_record,
evaluation_history=self.history,
)
return PackOptimizerResults(data, self.config)
def _objective(self, params: np.ndarray) -> float:
"""
Simulates a competition with the given car parameter modifications. Then, calculates
an objective score based on the competition results and capacity constraints
Parameters
-----------
params : np.ndarray
Array of parameter values corresponding to self.config.parameters order (DE passes a numpy array)
e.g. if parameters=[mass, ratio], then params[0] is mass,
params[1] is ratio, etc.
Returns
-------
float
The penalized score to be minimized by the optimizer (negative total points minus any penalties)
"""
try:
comp = from_data(self.comp_data)
# Apply each optimized parameter
for param, value in zip(self.config.parameters, params):
comp.mycar.modify_params(param.name, float(value))
# Apply fixed mass and capacity when in fixed pack mode
if isinstance(self.config, FixedPackConfig):
capacity_adjusted_mass = comp.mycar.accum.capacity_to_mass(
self.config.capacity
)
comp.mycar.modify_params("mass", capacity_adjusted_mass)
comp.mycar.modify_params("accum.capacity", self.config.capacity)
result = comp.run()
# Penalized score
energy_consumption_overage = result.endu_consumed_energy - (
comp.mycar.accum.params.capacity - self.config.capacity_margin
)
penalty = 1000 * max(0, energy_consumption_overage)
score = result.total_points - penalty
record = EvaluationRecord(
params=params,
param_names=[p.name for p in self.config.parameters],
mass=comp.mycar.params.mass,
capacity=comp.mycar.accum.params.capacity,
score=score,
competition_results=result,
feasible=energy_consumption_overage <= 0,
)
self.history.append(record)
# Update best score and record if this is the best so far
if score > self.best_score:
self.best_score = score
self.best_record = record
# We return the negative score because DE minimizes the objective, but we want to maximize points
return -score
except Exception as e:
print(f"Evaluation failed with params {params}: {e}")
record = EvaluationRecord(
params=params,
param_names=[p.name for p in self.config.parameters],
mass=np.nan,
capacity=np.nan,
score=-np.inf,
competition_results=None,
feasible=False,
)
self.history.append(record)
return np.inf