Use the Pyomo solver

The Pyomo solver interface lets you back monee with any solver that Pyomo supports, including Gurobi, HiGHS, SCIP, GLPK, CBC, and CPLEX. Use it when you need a MILP or MIQCP back-end, for example to solve an AC optimal power flow with the EL_MISOCP_FORMULATION.


Prerequisites

Install Pyomo and at least one solver back-end:

Open-source LP / MILP / MIQCP solver. Works out of the box on most platforms.

pip install highspy

or via conda:

conda install -c conda-forge highs

Open-source LP / MILP solver.

conda install -c conda-forge glpk

Commercial solver with a free academic licence available at gurobi.com. Install the Python bindings after obtaining a licence:

pip install gurobipy

Pick a solver by name

Pass a solver name to the top-level entry points. Any name other than a GEKKO solver ("apopt", "bpopt", "ipopt") routes to Pyomo automatically:

import monee

# Plain energy flow via Pyomo + HiGHS
result = monee.run_energy_flow(net, solver="highs")

# Optimisation via Pyomo + Gurobi
result = monee.solve(net, optimization_problem=problem, solver="gurobi")

"ipopt" is available in several back-ends and defaults to CasADi (the in-process IPOPT backend), falling back to GEKKO’s bundled IPOPT when CasADi is not installed. Force the Pyomo back-end for such ambiguous names with backend="pyomo":

result = monee.solve(net, solver="ipopt", backend="pyomo")

To reuse a solver object across solves, or to override the solver per solve, instantiate PyomoSolver directly:

solver = monee.PyomoSolver(solver_name="scip")
result = solver.solve(net)

# Per-solve override: same instance, different back-end solver
result = solver.solve(net, solver_name="gurobi")

Minimal example

Build a small electricity grid and solve an AC optimal power flow with the MISOCP relaxation and HiGHS:

import monee
import monee.express as mx

# Build the network
net = mx.create_multi_energy_network()

bus_0 = mx.create_bus(net)
bus_1 = mx.create_bus(net)

mx.create_line(net, bus_0, bus_1, 100, r_ohm_per_m=7e-5, x_ohm_per_m=7e-5)
mx.create_ext_power_grid(net, bus_0)
mx.create_power_load(net, bus_1, p_mw=0.1, q_mvar=0.0)

# Switch to the MISOCP formulation
net.apply_formulation(monee.EL_MISOCP_FORMULATION)

# Solve with Pyomo + HiGHS
result = monee.solve(net, solver="highs")
print(result.objective)

Passing an optimisation problem

Pass an OptimizationProblem the same way as with the GEKKO solver:

import monee
import monee.problem as mp

problem = monee.OptimizationProblem()
problem.controllable_demands([
    (
        "regulation",
        mp.AttributeParameter(
            min=lambda a, v: 0,
            max=lambda a, v: 1,
            val=lambda a, v: 1,
        ),
    )
])

result = monee.PyomoSolver().solve(
    net,
    optimization_problem=problem,
    solver_name="gurobi",
)

Warm starts, options, and robustness

The Pyomo back-end applies a few conveniences automatically:

Note

Warm starts: after every solve, the solution is persisted back into the input network, and warmstart=True is passed to the solver whenever it reports warm-start capability. Repeated solves on the same network, for example in a timeseries loop, therefore start from the previous solution without any extra code.

Note

Per-solver option presets: monee ships tuned default options for some solvers. For Gurobi these are ScaleFlag=2, MIPFocus=2, MIPGap=1e-3, and TimeLimit=300, chosen for the MISOCP and McCormick formulations. See PER_SOLVER_OPTIONS in monee.solver.pyo if you need to inspect or adjust them.

Note

Trivial equations are skipped: equations that reduce to a plain True or False during model construction, typically produced by deactivated or islanded components, are dropped instead of crashing the build. Always-True (tautology) equations are dropped silently, while always-False (structurally infeasible) equations are dropped but logged at warning level so a genuine modelling error, such as an over-deactivated node or branch, is surfaced. This lets problems such as load shedding drive the solution even when parts of the network are switched off.

Lexicographic objectives (Pyomo only)

Some formulations (e.g. MISOCP, McCormick) add auxiliary relaxation-tightening terms to the objective, which normally have to be weighted against your own objectives. Constructing the problem with OptimizationProblem(lex_objectives=True) removes this weight tuning. The Pyomo back-end then runs a two-phase solve: it first minimises your objectives alone, then minimises the formulation’s auxiliary terms under a cap that holds the user objective at its phase-one optimum (up to solver tolerance). The GEKKO back-end ignores the flag and falls back to a single summed objective. See Solvers & Backends for how this fits into the solver architecture.


Solver name reference

The solver_name argument is forwarded to pyomo.environ.SolverFactory:

Value

Solver

Notes

"highs"

HiGHS

Open-source LP / MILP / MIQCP. pip install highspy.

"gurobi"

Gurobi

Commercial, requires a valid licence.

"scip"

SCIP

Open-source MINLP / MILP. conda install -c conda-forge pyscipopt.

"glpk"

GLPK

Open-source, LP / MILP only.

"cbc"

CBC

Open-source, LP / MILP.

"ipopt"

IPOPT

Open-source NLP. Requires a separate IPOPT binary; the string "ipopt" routes to CasADi by default, so pass backend="pyomo".

Unknown or uninstalled names raise a ValueError that lists the Pyomo solvers actually installed on your system.


Formulation compatibility

Not every formulation is compatible with every solver back-end. The GEKKO-specific helpers if2, max2, and sign2 are not available in the Pyomo translation layer and will raise NotImplementedError if a formulation tries to use them.

Note

Piecewise-linear constraints are translated differently per back-end: Pyomo uses an SOS2 Piecewise construct, which requires a MIP-capable solver (HiGHS, Gurobi, SCIP, CBC, …), whereas GEKKO approximates the same data with cubic splines. A PWL formulation that solves fine under GEKKO + IPOPT therefore needs a MIP solver under Pyomo.

Tip

For the Pyomo back-end, prefer:

  • EL_MISOCP_FORMULATION for electricity optimal power flow.

  • Custom formulations written with standard Pyomo / monee expressions.

See Formulations for the full list of built-in formulations and a guide to writing custom ones.


Troubleshooting

If the solver reports infeasibility, the result carries a structured diagnosis: result.infeasibility_report is attached automatically on infeasible termination, with constraint residuals, bound violations, and an optional minimal infeasible subsystem. See Diagnose infeasible and failed solves for how to read and act on it.