Skip to content

Dispatch Policy

A dispatch policy receives the microgrid's current power delta and allocates it across Dispatchables, returning the remainder to be exchanged with the public grid. Subclass DispatchPolicy to implement custom strategies. See the Dispatchables and Dispatch Policies concept page for usage examples.

DispatchPolicy

Bases: ABC

Policy that describes how a microgrid dispatches power across dispatchables.

The dispatch policy manages energy excess and shortage of a microgrid by allocating power across dispatchable resources (batteries, generators, etc.) and exchanging remaining energy with the public grid.

Source code in vessim/dispatch_policy.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class DispatchPolicy(ABC):
    """Policy that describes how a microgrid dispatches power across dispatchables.

    The dispatch policy manages energy excess and shortage of a microgrid by allocating
    power across dispatchable resources (batteries, generators, etc.) and exchanging
    remaining energy with the public grid.
    """

    @abstractmethod
    def apply(
        self,
        p_delta: float,
        duration: int,
        dispatchables: list[Dispatchable],
        grid_signals: Optional[dict[str, float]] = None,
    ) -> float:
        """Allocate power delta across dispatchables.

        Args:
            p_delta: Power imbalance in W. Positive means excess power (can charge),
                negative means power deficit (need to discharge/generate).
            duration: Duration of the timestep in seconds.
            dispatchables: List of flexible components available for dispatch,
                like batteries or on-site diesel or gas generators.
            grid_signals: Current grid signal values (e.g., carbon intensity, energy
                price, curtailment), if any are configured on the microgrid.

        Returns:
            Power in W exchanged with the public grid. Negative means power drawn
            from the grid, positive means power fed to the grid.
        """

    def state(self) -> dict:
        """Returns information about the current state of the policy."""
        return {}

apply abstractmethod

Allocate power delta across dispatchables.

Parameters:

Name Type Description Default
p_delta float

Power imbalance in W. Positive means excess power (can charge), negative means power deficit (need to discharge/generate).

required
duration int

Duration of the timestep in seconds.

required
dispatchables list[Dispatchable]

List of flexible components available for dispatch, like batteries or on-site diesel or gas generators.

required
grid_signals Optional[dict[str, float]]

Current grid signal values (e.g., carbon intensity, energy price, curtailment), if any are configured on the microgrid.

None

Returns:

Type Description
float

Power in W exchanged with the public grid. Negative means power drawn

float

from the grid, positive means power fed to the grid.

Source code in vessim/dispatch_policy.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@abstractmethod
def apply(
    self,
    p_delta: float,
    duration: int,
    dispatchables: list[Dispatchable],
    grid_signals: Optional[dict[str, float]] = None,
) -> float:
    """Allocate power delta across dispatchables.

    Args:
        p_delta: Power imbalance in W. Positive means excess power (can charge),
            negative means power deficit (need to discharge/generate).
        duration: Duration of the timestep in seconds.
        dispatchables: List of flexible components available for dispatch,
            like batteries or on-site diesel or gas generators.
        grid_signals: Current grid signal values (e.g., carbon intensity, energy
            price, curtailment), if any are configured on the microgrid.

    Returns:
        Power in W exchanged with the public grid. Negative means power drawn
        from the grid, positive means power fed to the grid.
    """

state

Returns information about the current state of the policy.

Source code in vessim/dispatch_policy.py
44
45
46
def state(self) -> dict:
    """Returns information about the current state of the policy."""
    return {}

DefaultDispatchPolicy

Bases: DispatchPolicy

Default dispatch policy that allocates power in list order.

Iterates through dispatchables in the order they are given and allocates as much of the power delta as each can handle. Remaining power is exchanged with the public grid in grid-connected mode.

Parameters:

Name Type Description Default
mode Literal['grid-connected', 'islanded']

Operating mode. In "grid-connected" mode, remaining power is exchanged with the grid. In "islanded" mode, an error is raised if the delta cannot be fully served by dispatchables.

'grid-connected'
charge_power Optional[float]

Optional fixed charge/discharge rate applied to storage dispatchables. If set, storage is charged/discharged at this rate regardless of the power delta, and remaining dispatchables are dispatched sequentially as usual. Only works in grid-connected mode. Defaults to None.

None
Source code in vessim/dispatch_policy.py
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
class DefaultDispatchPolicy(DispatchPolicy):
    """Default dispatch policy that allocates power in list order.

    Iterates through dispatchables in the order they are given and allocates as much
    of the power delta as each can handle. Remaining power is exchanged with the
    public grid in grid-connected mode.

    Args:
        mode: Operating mode. In ``"grid-connected"`` mode, remaining power is
            exchanged with the grid. In ``"islanded"`` mode, an error is raised
            if the delta cannot be fully served by dispatchables.
        charge_power: Optional fixed charge/discharge rate applied to storage dispatchables.
            If set, storage is charged/discharged at this rate regardless of the power delta,
            and remaining dispatchables are dispatched sequentially as usual.
            Only works in grid-connected mode. Defaults to None.
    """

    def __init__(
        self,
        mode: Literal["grid-connected", "islanded"] = "grid-connected",
        charge_power: Optional[float] = None,
    ):
        self.mode = mode
        self.charge_power = charge_power if charge_power else 0.0

    def apply(
        self,
        p_delta: float,
        duration: int,
        dispatchables: list[Dispatchable],
        grid_signals: Optional[dict[str, float]] = None,
    ) -> float:
        remaining = p_delta
        force_charged = set()

        if self.charge_power and self.mode == "grid-connected":
            # Pre-pass: force-charge/discharge storage at specified rate
            for d in dispatchables:
                if isinstance(d, Storage):
                    lo, hi = d.feasible_range(duration)
                    power = max(lo, min(hi, self.charge_power))
                    d.set_power(power, duration)
                    remaining -= power
                    force_charged.add(d)

        # Sequential dispatch for remaining dispatchables
        for d in dispatchables:
            if d in force_charged:
                continue
            if remaining == 0:
                d.set_power(0.0, duration)
                continue

            lo, hi = d.feasible_range(duration)

            if remaining > 0:  # excess: try to charge (positive power)
                allocated = max(0.0, min(hi, remaining))
            else:  # deficit: try to discharge (negative power)
                allocated = min(0.0, max(lo, remaining))

            d.set_power(allocated, duration)
            remaining -= allocated

        if self.mode == "islanded":
            if remaining < 0:
                raise RuntimeError(
                    "Not enough energy available to operate in islanded mode."
                )
            remaining = 0.0

        return remaining  # p_grid

    def state(self) -> dict:
        return {
            "mode": self.mode,
            "charge_power": self.charge_power,
        }