Load shedding¶
Load shedding finds the minimum amount of demand that must be curtailed to keep a network feasible under given operational limits. monee ships a ready-made load-shedding formulation for multi-energy networks with a one-call interface and a lower-level builder function for customisation.
How it works¶
The optimiser makes each demand, generator, and coupling unit controllable
via a regulation multiplier in [0, 1]. It then minimises the total
deviation from full service, subject to operational bounds on voltage
(electrical), pressure (gas), and temperature (heat):
Bound argument |
Controls |
|---|---|
|
Voltage magnitude at buses (per unit, e.g. |
|
Normalised temperature at heating junctions (per unit) |
|
Normalised pressure at gas junctions (per unit) |
|
Active power / mass-flow range at external grid connections |
One-call interface¶
Tip
Use monee.solve_load_shedding_problem() for the simplest path — it
picks sensible defaults and requires minimal configuration.
from monee import solve_load_shedding_problem
result = solve_load_shedding_problem(
network,
bounds_vm=(0.9, 1.1),
bounds_t=(0.8, 1.2),
bounds_pressure=(0.8, 1.2),
bounds_ext_el=(-0.5, 0.5),
bounds_ext_gas=(-2.0, 2.0),
)
print(f"Objective (shed load cost): {result.objective_value:.4f}")
The result is a SolverResult with the solved
network and DataFrames for each model type.
Custom problem¶
For finer control — different weights per carrier, extra constraints, or
different bounds — use
create_load_shedding_optimization_problem() directly:
import monee.express as mx
from monee import run_energy_flow_optimization
from monee.problem import create_load_shedding_optimization_problem
# Build a small test 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.4, q_mvar=0.0)
# Create and customise the load-shedding problem
problem = create_load_shedding_optimization_problem(
load_weight=20, # penalty per MW of shed load
bounds_el=(0.92, 1.08), # tighter voltage bounds
check_pressure=False, # no gas grid present
check_t=False, # no heat grid present
debug=False,
)
result = run_energy_flow_optimization(net, problem)
Key parameters of create_load_shedding_optimization_problem:
Parameter |
Effect |
|---|---|
|
Penalty factor applied to each unit of shed load. Higher values push the solver to avoid curtailment. Default: 10. |
|
|
|
|
|
|
|
Disable individual bound checks for carriers not present in the network. |
|
Enforce bounds on external grid exchanges. Default: |
|
Enable verbose solver output. Default: |
Interpreting the result¶
After solving, the regulation attribute of each controllable component
shows how much of its nominal setpoint was served. A value of 1.0 means
fully served; 0.0 means completely shed.
for child in result.network.childs:
reg = getattr(child.model, "regulation", None)
if reg is not None:
print(f"{child.name or child.id}: regulation = {reg:.2f}")
Note
A regulation value between 0 and 1 indicates partial load curtailment.
Inspect the dataframes dict for per-component voltage, pressure, and
temperature to understand why curtailment was needed.