Data model

monee represents multi-energy grids as a directed graph. There are four fundamental model types:

Type

Description

Node

A connection point where energy flows meet — a bus in an electricity grid, or a junction in a pipe network.

Branch

An energy transfer element connecting two nodes — an electric line, a gas pipe, or a water pipe.

Child

A unit that feeds power into or draws power from a node — generators, loads, external grids.

Grid

Grid-level parameters shared by all components in the same carrier (e.g. reference pressure, temperature, base MVA).

Each type is represented by a Component container class (Node, Branch, Child). The physical behaviour lives in a model object attached to the component. This separation makes it easy to swap models without touching the topology.

Tip

The monee.express API creates all of these objects for you. You only need to work with the low-level classes when implementing custom components.


Variables and parameters

Inside a model object, decision variables are declared with Var and fixed parameters with Const. The solver replaces Var instances with its own variable type at solve time.

from monee.model.core import Var, Const

class MyPipeModel:
    def __init__(self):
        self.mass_flow = Var(0.0, min=-5.0, max=5.0, name="mass_flow")
        self.diameter_m = Const(0.1)

Variables in neighbouring models are accessible via the vars dictionary:

# Inside equations(self, branch, grid, from_node_model, to_node_model, **kwargs):
p_from = from_node_model.vars["pressure_pu"]

Nodes

Subclass NodeModel and implement equations(). The method receives:

  • grid — the grid the node belongs to.

  • from_branch_models — models of branches arriving at this node.

  • to_branch_models — models of branches leaving this node.

  • connected_child_models — models of child components at this node.

import monee.model as mm

@mm.model
class MyBus(mm.NodeModel):
    def __init__(self):
        self.pressure_pu = mm.Var(1, min=0, max=2, name="pressure_pu")

    def equations(self, grid, from_branch_models, to_branch_models,
                  connected_child_models, **kwargs):
        flow_in  = sum(b.vars.get("mass_flow", 0) for b in from_branch_models)
        flow_out = sum(b.vars.get("mass_flow", 0) for b in to_branch_models)
        injected = sum(c.vars.get("mass_flow", 0) for c in connected_child_models)
        return [flow_in - flow_out + injected == 0]

Note

Nodes that participate in more than one grid — for example a power-to-heat coupling junction — should use MultiGridNodeModel as the base class instead of NodeModel.

Branches

Subclass BranchModel and implement equations(). Parameters:

  • grid — the grid the branch belongs to.

  • from_node_model — model of the upstream node.

  • to_node_model — model of the downstream node.

For multi-carrier branches (e.g. a gas-to-power unit), use MultiGridBranchModel.

Children

Subclass ChildModel and implement equations(). Parameters:

  • grid — the grid the child is attached to.

  • node — the node the child is connected to.


Assembling a network

Use Network to assemble the graph:

import monee.model as mm

net = mm.Network(mm.PowerGrid(name="power", sn_mva=1))

child_id  = net.child(mm.PowerLoad(p_mw=mm.Const(0.1), q_mvar=mm.Const(0.0)))
node_id   = net.node(mm.Bus(base_kv=1), child_ids=[child_id], grid=mm.EL)
node_id_2 = net.node(mm.Bus(base_kv=1), grid=mm.EL)
net.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_id_2, grid=mm.EL)

Tip

For most use cases, prefer the monee.express API over this low-level interface. The express functions set sensible defaults and handle multi-energy bookkeeping automatically.