From f16ffefeb794b7b395f162ee905bf49a842d8564 Mon Sep 17 00:00:00 2001 From: ahalev Date: Sun, 14 Apr 2024 16:02:24 -0700 Subject: [PATCH] iter through solvers --- src/pymgrid/algos/mpc/mpc.py | 61 +++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/src/pymgrid/algos/mpc/mpc.py b/src/pymgrid/algos/mpc/mpc.py index 39f3698c..92337b30 100644 --- a/src/pymgrid/algos/mpc/mpc.py +++ b/src/pymgrid/algos/mpc/mpc.py @@ -19,6 +19,13 @@ logger = logging.getLogger(__name__) + +if mosek is not None: + SOLVER_ERRS = mosek.Error, cp.error.SolverError +else: + SOLVER_ERRS = cp.error.SolverError + + """ Attributes: -------------- @@ -101,6 +108,7 @@ def __init__(self, microgrid, solver=None): self.problem = self._create_problem(*parameters) self._passed_solver = solver self._solver = self._get_solver() + self._all_solvers = self._solvers(solver) @property def has_genset(self): @@ -373,6 +381,31 @@ def _create_problem(self, eta, battery_capacity, fuel_cost, cost_battery_cycle, return cp.Problem(objective, constraints) + def _solvers(self, solver=None): + solvers = [] + + if solver is not None: + solvers.append(solver) + + if 'MOSEK' in cp.installed_solvers(): + solvers.append(cp.MOSEK) + + if 'GLPK_MI' in cp.installed_solvers(): + solvers.append(cp.GLPK_MI) + + if self.problem.is_mixed_integer(): + if not solvers: + raise RuntimeError( + "If microgrid has a genset, the cvxpy problem becomes mixed integer. Either MOSEK or " + "CVXOPT must be installed.\n" + "You can install both by calling pip install -e .'[genset_mpc]' in the root folder of " + "pymgrid. Note that MOSEK requires a license; see https://www.mosek.com/ for details.\n" + "Academic and trial licenses are available.") + else: + solvers.append(cp.CLARABEL) + + return solvers + def _get_solver(self, failure=False): if not failure: logger.info("Using default solver." if self._passed_solver is None else f"Using {self._passed_solver} solver.") @@ -793,20 +826,34 @@ def _set_and_solve(self, else: errs = cp.error.SolverError - try: - self.problem.solve(warm_start=True, solver=self._solver) - except errs: - self._solver = self._get_solver(failure=True) - self.problem.solve(warm_start=True, solver=self._solver) + # try: + # self.problem.solve(warm_start=True, solver=self._solver) + # except errs: + # self._solver = self._get_solver(failure=True) + # self.problem.solve(warm_start=True, solver=self._solver) + # + # if self.problem.status == 'infeasible': + # warn("Infeasible problem") - if self.problem.status == 'infeasible': - warn("Infeasible problem") + for solver in self.solvers(): + self.problem.solve(warm_start=True, solver=solver) + break if self.is_modular: return self._extract_modular_control(load_vector, verbose) else: return self._extract_control_dict(return_steps, pv_vector, load_vector) + def solvers(self): + for solver in self._all_solvers: + try: + yield solver + except SOLVER_ERRS as e: + if solver == self._all_solvers[-1]: + raise cp.error.SolverError(f'Unable to solve problem with solvers: {self._all_solvers}') from e + else: + raise StopIteration # this should never hit, should break out first or hit above exception + def _extract_control_dict(self, return_steps, pv_vector, load_vector): if return_steps == 0: if self.has_genset: