Quickstart¶
monee represents multi-energy grids as directed graphs and solves steady-state energy flows or optimisation problems over them. This page walks through the core workflow in five minutes.
Tip
You will need monee installed before running any of the examples below. See the Install page if you have not set it up yet.
Solving an electricity network¶
Build a radial electricity network, attach an external grid as the slack bus, add a load, and run the energy-flow calculation:
from monee import mx, run_energy_flow
net = mx.create_multi_energy_network()
# Two buses connected by a 100 m line
bus_0 = mx.create_bus(net)
bus_1 = mx.create_bus(net)
mx.create_line(net, bus_0, bus_1, length_m=100,
r_ohm_per_m=7e-5, x_ohm_per_m=7e-5)
# Slack bus and demand
mx.create_ext_power_grid(net, bus_0)
mx.create_power_load(net, bus_1, p_mw=0.1, q_mvar=0.0)
result = run_energy_flow(net)
Inspecting the result¶
run_energy_flow() returns a
SolverResult with two main attributes:
result.networkThe solved network object with all variables updated in-place.
result.dataframesA
dict[str, pandas.DataFrame]keyed by model class name. Each row is one component; each column is a model variable or parameter.
bus_df = result.dataframes["Bus"]
print(len(bus_df)) # one row per bus
2
Access individual columns by name:
print(bus_df[["id", "vm_pu", "va_degree"]])
Multi-energy networks¶
The key feature of monee is coupling several energy carriers in a single simulation. The example below connects an electricity grid to a district heating grid via a power-to-heat (P2H) unit:
from monee import mx, run_energy_flow
net = mx.create_multi_energy_network()
# ── Electricity grid ──────────────────────────────────────────────────
bus_0 = mx.create_bus(net)
bus_1 = mx.create_bus(net)
mx.create_line(net, bus_0, bus_1, length_m=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)
# ── District heating grid (hot-water pipes) ───────────────────────────
j_supply = mx.create_water_junction(net) # supply header
j_mid = mx.create_water_junction(net) # junction after pipe
j_return = mx.create_water_junction(net) # return header
mx.create_ext_hydr_grid(net, j_supply)
mx.create_water_pipe(net, j_supply, j_mid, diameter_m=0.3, length_m=100)
mx.create_sink(net, j_return, mass_flow=1)
# ── Couple the two grids ──────────────────────────────────────────────
mx.create_p2h(net, bus_1, j_mid, j_return,
heat_energy_w=10_000, diameter_m=0.1, efficiency=0.9)
result = run_energy_flow(net)
# Both carriers appear in the result
print("Bus" in result.dataframes)
print("Junction" in result.dataframes)
True
True
Tip
Other coupling units — create_g2p() (gas-to-power),
create_p2g() (power-to-gas),
create_g2h() (gas-to-heat), and
create_chp() (combined heat and power) — follow the
same pattern. See Multi-energy coupling for details on all
supported coupling types.
Optimisation¶
Replace run_energy_flow() with
run_energy_flow_optimization() to solve an optimisation problem
over the network. monee ships a ready-made load-shedding formulation and
supports fully custom objectives and constraints:
from monee import mx, run_energy_flow_optimization
from monee.problem import create_load_shedding_optimization_problem
opt_net = mx.create_multi_energy_network()
opt_bus_0 = mx.create_bus(opt_net)
opt_bus_1 = mx.create_bus(opt_net)
mx.create_line(opt_net, opt_bus_0, opt_bus_1, length_m=100,
r_ohm_per_m=7e-5, x_ohm_per_m=7e-5)
mx.create_ext_power_grid(opt_net, opt_bus_0)
mx.create_power_load(opt_net, opt_bus_1, p_mw=0.5, q_mvar=0.0)
problem = create_load_shedding_optimization_problem(
bounds_el=(0.9, 1.1),
check_pressure=False, # no gas grid in this network
check_t=False, # no heat grid in this network
)
result = run_energy_flow_optimization(opt_net, problem)
print(f"Objective: {result.objective:.4f}")
Objective: ...
See 01 · Minimum-cost load curtailment and Load shedding for end-to-end worked examples.
Extending with custom models¶
The express API covers the built-in component library. For custom physics,
subclass the appropriate model base class and implement equations:
import monee.model as mm
from monee import run_energy_flow
@mm.model
class FlexibleLoad(mm.PowerLoad):
"""A load whose active power is a free variable constrained by *c*."""
def __init__(self, c, **kwargs):
# Declare p_mw and q_mvar as solver decision variables
super().__init__(mm.Var(c, min=0, max=c), mm.Var(0), **kwargs)
self._c = c
def equations(self, grid, node, **kwargs):
return [
self.p_mw <= self._c,
self.q_mvar == 0,
]
# Build a single-bus network using the custom load
pn = mm.Network(mm.PowerGrid(name="power", sn_mva=1))
child_id = pn.child(FlexibleLoad(c=0.5))
node_id = pn.node(mm.Bus(base_kv=1), child_ids=[pn.child(mm.ExtPowerGrid(p_mw=1, q_mvar=1))], grid=mm.EL) # slack
node_load = pn.node(mm.Bus(base_kv=1), child_ids=[child_id], grid=mm.EL) # load bus
pn.branch(mm.PowerLine(length_m=100, r_ohm_per_m=1e-4, x_ohm_per_m=1e-4, parallel=1),
from_node_id=node_id, to_node_id=node_load, grid=mm.EL)
print(run_energy_flow(pn))
SolverResult
Bus (2 instances)
────────────────────────────────────────────────────────────────────
id base_kv vm_pu vm_pu_squared va_radians va_degree p_mw q_mvar
0 1 1 1 1.887e-05 0 -0.003774 -1.424e-07
1 1 1 1 -1.887e-05 -0.001081 0.003773 0
FlexibleLoad (1 instance)
────────────────────────────────────────────────────────────────────
id regulation p_mw q_mvar node_id
0 1 0.003773 0 1
ExtPowerGrid (1 instance)
────────────────────────────────────────────────────────────────────
id regulation p_mw q_mvar vm_pu va_degree node_id
1 1 -0.003774 -1.424e-07 1 0 0
PowerLine (1 instance)
────────────────────────────────────────────────────────────────────
id tap shift br_r br_x g_fr b_fr g_to b_to max_i_ka backup on_off p_from_mw q_from_mvar i_from_ka loading_from_percent p_to_mw q_to_mvar i_to_ka loading_to_percent length_m r_ohm_per_m x_ohm_per_m parallel
(0, 1, 0) 1 0 0.01 0.01 0 0 0 0 3.19 False 1 0.003774 1.424e-07 8.222e-06 2.577e-06 -0.003773 0 8.221e-06 2.577e-06 100 0.0001 0.0001 1
Read the Data model concept page for the full model contract and how to implement custom branches and nodes.
Next steps¶
End-to-end worked examples: optimisation and time-series simulation.
The equations for electricity, gas, and heat networks.
All coupling components — P2H, G2P, CHP, G2H — in detail.
Choose between AC, MISOCP, and nonlinear formulations.
GEKKO versus Pyomo: when to use each.
Complete reference for every public function and class.