Skip to content

Dispatchable

Controllable energy resources whose power is allocated by a Dispatch Policy. Subclass Dispatchable to model any device with a controllable power setpoint. See the Dispatchables and Dispatch Policies concept page for usage examples and a guide to writing custom dispatchables.

Dispatchable

Bases: ABC

Abstract base class for dispatchable energy resources.

Dispatchables are energy resources whose power output can be controlled by a dispatch policy (e.g., batteries, diesel generators, gas turbines).

Parameters:

Name Type Description Default
name str

The name of the dispatchable.

required
Source code in vessim/dispatchable.py
10
11
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class Dispatchable(ABC):
    """Abstract base class for dispatchable energy resources.

    Dispatchables are energy resources whose power output can be controlled by a
    dispatch policy (e.g., batteries, diesel generators, gas turbines).

    Args:
        name: The name of the dispatchable.
    """

    def __init__(self, name: str) -> None:
        self.name = name
        self.current_power: float = 0.0

    def set_power(self, power: float, duration: int) -> None:
        """Set the power setpoint for the given duration.

        Raises:
            ValueError: If power is outside the feasible range.
        """
        lo, hi = self.feasible_range(duration)
        if power < lo or power > hi:
            raise ValueError(
                f"Power {power} W is outside feasible range [{lo}, {hi}] W "
                f"for '{self.name}' with duration {duration}s."
            )
        self.current_power = power

    @abstractmethod
    def feasible_range(self, duration: int) -> tuple[float, float]:
        """Returns the (min, max) power achievable for the given timestep duration.

        Args:
            duration: Duration of the timestep in seconds.

        Returns:
            (min_power, max_power) tuple. Negative values indicate discharging/generation,
            positive values indicate charging/consumption.
        """

    @abstractmethod
    def step(self, duration: int) -> None:
        """Update internal state after a timestep has elapsed.

        Args:
            duration: Duration of the timestep in seconds.
        """

    @abstractmethod
    def config(self) -> dict:
        """Returns the static configuration parameters of the dispatchable.

        These are parameters that are fixed at construction time and do not change
        during the simulation (e.g. capacity, C-rate). Used for experiment metadata.
        """

    @abstractmethod
    def state(self) -> dict:
        """Returns the dynamic state of the dispatchable at the current timestep.

        These are values that change during the simulation (e.g. SoC, charge level).
        Used for time-series logging.
        """

set_power

Set the power setpoint for the given duration.

Raises:

Type Description
ValueError

If power is outside the feasible range.

Source code in vessim/dispatchable.py
24
25
26
27
28
29
30
31
32
33
34
35
36
def set_power(self, power: float, duration: int) -> None:
    """Set the power setpoint for the given duration.

    Raises:
        ValueError: If power is outside the feasible range.
    """
    lo, hi = self.feasible_range(duration)
    if power < lo or power > hi:
        raise ValueError(
            f"Power {power} W is outside feasible range [{lo}, {hi}] W "
            f"for '{self.name}' with duration {duration}s."
        )
    self.current_power = power

feasible_range abstractmethod

Returns the (min, max) power achievable for the given timestep duration.

Parameters:

Name Type Description Default
duration int

Duration of the timestep in seconds.

required

Returns:

Type Description
float

(min_power, max_power) tuple. Negative values indicate discharging/generation,

float

positive values indicate charging/consumption.

Source code in vessim/dispatchable.py
38
39
40
41
42
43
44
45
46
47
48
@abstractmethod
def feasible_range(self, duration: int) -> tuple[float, float]:
    """Returns the (min, max) power achievable for the given timestep duration.

    Args:
        duration: Duration of the timestep in seconds.

    Returns:
        (min_power, max_power) tuple. Negative values indicate discharging/generation,
        positive values indicate charging/consumption.
    """

step abstractmethod

Update internal state after a timestep has elapsed.

Parameters:

Name Type Description Default
duration int

Duration of the timestep in seconds.

required
Source code in vessim/dispatchable.py
50
51
52
53
54
55
56
@abstractmethod
def step(self, duration: int) -> None:
    """Update internal state after a timestep has elapsed.

    Args:
        duration: Duration of the timestep in seconds.
    """

config abstractmethod

Returns the static configuration parameters of the dispatchable.

These are parameters that are fixed at construction time and do not change during the simulation (e.g. capacity, C-rate). Used for experiment metadata.

Source code in vessim/dispatchable.py
58
59
60
61
62
63
64
@abstractmethod
def config(self) -> dict:
    """Returns the static configuration parameters of the dispatchable.

    These are parameters that are fixed at construction time and do not change
    during the simulation (e.g. capacity, C-rate). Used for experiment metadata.
    """

state abstractmethod

Returns the dynamic state of the dispatchable at the current timestep.

These are values that change during the simulation (e.g. SoC, charge level). Used for time-series logging.

Source code in vessim/dispatchable.py
66
67
68
69
70
71
72
@abstractmethod
def state(self) -> dict:
    """Returns the dynamic state of the dispatchable at the current timestep.

    These are values that change during the simulation (e.g. SoC, charge level).
    Used for time-series logging.
    """

Storage

Bases: Dispatchable

Abstract base class for energy storage devices.

Storage is a Dispatchable that can both absorb and release energy, and tracks its state-of-charge (SoC).

Parameters:

Name Type Description Default
name str

The name of the storage device.

required
Source code in vessim/dispatchable.py
78
79
80
81
82
83
84
85
86
87
88
89
90
class Storage(Dispatchable):
    """Abstract base class for energy storage devices.

    Storage is a `Dispatchable` that can both absorb and release energy, and
    tracks its state-of-charge (SoC).

    Args:
        name: The name of the storage device.
    """

    @abstractmethod
    def soc(self) -> float:
        """Returns the state-of-charge (SoC) of the storage (0 to 1)."""

soc abstractmethod

Returns the state-of-charge (SoC) of the storage (0 to 1).

Source code in vessim/dispatchable.py
88
89
90
@abstractmethod
def soc(self) -> float:
    """Returns the state-of-charge (SoC) of the storage (0 to 1)."""

SimpleBattery

Bases: Storage

Simple battery model.

Parameters:

Name Type Description Default
name str

The name of the battery.

required
capacity float

Battery's energy capacity in Wh.

required
initial_soc float

Initial battery state-of-charge (SoC). Has to be between 0 and 1. Defaults to 0.

0
min_soc float

Minimum allowed SoC for the battery. Has to be between 0 and 1. If current SoC is below or equal to the minimum SoC, battery is not discharged further. Defaults to 0. Can be altered during simulation.

0
c_rate Optional[float]

Optional C-rate, which defines the charge and discharge rate of the battery. For more information on C-rate, see C-rate explanation <https://www.batterydesign.net/electrical/c-rate/>_. Defaults to None.

None
Source code in vessim/dispatchable.py
 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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
class SimpleBattery(Storage):
    """Simple battery model.

    Args:
        name: The name of the battery.
        capacity: Battery's energy capacity in Wh.
        initial_soc: Initial battery state-of-charge (SoC). Has to be between 0 and 1.
            Defaults to 0.
        min_soc: Minimum allowed SoC for the battery. Has to be between 0 and 1.
            If current SoC is below or equal to the minimum SoC, battery is not discharged
            further. Defaults to 0. Can be altered during simulation.
        c_rate: Optional C-rate, which defines the charge and discharge rate of the battery.
            For more information on C-rate, see
            `C-rate explanation <https://www.batterydesign.net/electrical/c-rate/>`_.
            Defaults to None.
    """

    def __init__(
        self,
        name: str,
        capacity: float,
        initial_soc: float = 0,
        min_soc: float = 0,
        c_rate: Optional[float] = None,
    ):
        super().__init__(name)
        self.capacity = capacity
        assert 0 <= initial_soc <= 1, "Invalid initial state-of-charge. Has to be between 0 and 1."
        self.charge_level = capacity * initial_soc
        self._soc = initial_soc
        assert 0 <= min_soc <= 1, "Invalid minimum state-of-charge. Has to be between 0 and 1."
        self.min_soc = min_soc
        self.c_rate = c_rate

    def feasible_range(self, duration: int) -> tuple[float, float]:
        """Returns (min_power, max_power) achievable for the given duration.

        Accounts for both c_rate limits and energy capacity limits.

        Args:
            duration: Duration of the timestep in seconds.
        """
        # Discharge limit (negative power)
        if self._soc <= self.min_soc:
            min_power = 0.0
        else:
            # Energy-limited: can only discharge down to min_soc
            energy_available = (self.charge_level - self.min_soc * self.capacity) * 3600  # Ws
            energy_limited = -energy_available / duration if duration > 0 else 0.0
            if self.c_rate is not None:
                min_power = max(energy_limited, -(self.c_rate * self.capacity))
            else:
                min_power = energy_limited

        # Charge limit (positive power)
        if self._soc >= 1.0:
            max_power = 0.0
        else:
            # Energy-limited: can only charge up to capacity
            energy_headroom = (self.capacity - self.charge_level) * 3600  # Ws
            energy_limited = energy_headroom / duration if duration > 0 else 0.0
            if self.c_rate is not None:
                max_power = min(energy_limited, self.c_rate * self.capacity)
            else:
                max_power = energy_limited

        return (min_power, max_power)

    def step(self, duration: int) -> None:
        """Update battery state based on current_power setpoint and duration."""
        if duration <= 0:
            raise ValueError("Duration needs to be a positive value")

        power = self.current_power
        if power == 0.0:
            return

        if self._soc <= self.min_soc and power <= 0.0:
            return

        if self.c_rate is not None:
            max_power = self.c_rate * self.capacity
            if power >= max_power:
                logger.info(
                    f"Trying to charge '{self.name}' with "
                    f"{power} W but only {max_power} W are supported."
                )
                power = max_power
            if power <= -max_power:
                logger.info(
                    f"Trying to discharge '{self.name}' "
                    f"with {power} W but only {max_power} W are supported."
                )
                power = -max_power

        charged_energy = power * duration
        new_charge_level = self.charge_level + charged_energy / 3600

        abs_min_soc = self.min_soc * self.capacity
        if new_charge_level < abs_min_soc:
            self.charge_level = abs_min_soc
            self._soc = self.min_soc
        elif new_charge_level > self.capacity:
            self.charge_level = self.capacity
            self._soc = 1.0
        else:
            self.charge_level = new_charge_level
            self._soc = self.charge_level / self.capacity

    def soc(self) -> float:
        """Returns the state-of-charge (SoC) of the battery (0 to 1)."""
        return self._soc

    def config(self) -> dict:
        return {
            "capacity": self.capacity,
            "min_soc": self.min_soc,
            "c_rate": self.c_rate,
        }

    def state(self) -> dict:
        return {
            "soc": self._soc,
            "charge_level": self.charge_level,
        }

feasible_range

Returns (min_power, max_power) achievable for the given duration.

Accounts for both c_rate limits and energy capacity limits.

Parameters:

Name Type Description Default
duration int

Duration of the timestep in seconds.

required
Source code in vessim/dispatchable.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def feasible_range(self, duration: int) -> tuple[float, float]:
    """Returns (min_power, max_power) achievable for the given duration.

    Accounts for both c_rate limits and energy capacity limits.

    Args:
        duration: Duration of the timestep in seconds.
    """
    # Discharge limit (negative power)
    if self._soc <= self.min_soc:
        min_power = 0.0
    else:
        # Energy-limited: can only discharge down to min_soc
        energy_available = (self.charge_level - self.min_soc * self.capacity) * 3600  # Ws
        energy_limited = -energy_available / duration if duration > 0 else 0.0
        if self.c_rate is not None:
            min_power = max(energy_limited, -(self.c_rate * self.capacity))
        else:
            min_power = energy_limited

    # Charge limit (positive power)
    if self._soc >= 1.0:
        max_power = 0.0
    else:
        # Energy-limited: can only charge up to capacity
        energy_headroom = (self.capacity - self.charge_level) * 3600  # Ws
        energy_limited = energy_headroom / duration if duration > 0 else 0.0
        if self.c_rate is not None:
            max_power = min(energy_limited, self.c_rate * self.capacity)
        else:
            max_power = energy_limited

    return (min_power, max_power)

step

Update battery state based on current_power setpoint and duration.

Source code in vessim/dispatchable.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
def step(self, duration: int) -> None:
    """Update battery state based on current_power setpoint and duration."""
    if duration <= 0:
        raise ValueError("Duration needs to be a positive value")

    power = self.current_power
    if power == 0.0:
        return

    if self._soc <= self.min_soc and power <= 0.0:
        return

    if self.c_rate is not None:
        max_power = self.c_rate * self.capacity
        if power >= max_power:
            logger.info(
                f"Trying to charge '{self.name}' with "
                f"{power} W but only {max_power} W are supported."
            )
            power = max_power
        if power <= -max_power:
            logger.info(
                f"Trying to discharge '{self.name}' "
                f"with {power} W but only {max_power} W are supported."
            )
            power = -max_power

    charged_energy = power * duration
    new_charge_level = self.charge_level + charged_energy / 3600

    abs_min_soc = self.min_soc * self.capacity
    if new_charge_level < abs_min_soc:
        self.charge_level = abs_min_soc
        self._soc = self.min_soc
    elif new_charge_level > self.capacity:
        self.charge_level = self.capacity
        self._soc = 1.0
    else:
        self.charge_level = new_charge_level
        self._soc = self.charge_level / self.capacity

soc

Returns the state-of-charge (SoC) of the battery (0 to 1).

Source code in vessim/dispatchable.py
202
203
204
def soc(self) -> float:
    """Returns the state-of-charge (SoC) of the battery (0 to 1)."""
    return self._soc

ClcBattery

Bases: Storage

Implementation of the C-L-C Battery model for lithium-ion batteries.

This class implements the C-L-C model as described in

Kazhamiaka, F., Rosenberg, C. & Keshav, S. Tractable lithium-ion storage models for optimizing energy systems. Energy Inform 2, 4 (2019). doi:10.1186/s42162-019-0070-6 <https://doi.org/10.1186/s42162-019-0070-6>_

The default parameterization models a pack of LGM50 21700 rechargable lithium-ion cells. This model should not be used in combination with large step sizes.

Parameters:

Name Type Description Default
name str

The name of the battery.

required
number_of_cells int

Number of cells in the battery pack. Defaults to 1.

1
initial_soc float

Initial battery state-of-charge (SoC). Has to be between 0 and 1. Defaults to 0.

0
min_soc float

Minimum allowed SoC for the battery. Has to be between 0 and 1. Defaults to 0. Can be altered during simulation.

0
nom_voltage float

Single cell nominal voltage in V. Defaults to 3.63V.

3.63
u_1 float

Linear factor for lower energy limit of cell depending on applied discharge current. Defaults to -0.087Wh/A.

-0.087
v_1 float

Offset for lower energy limit of cell depending on the applied discharge current. Defaults to 0.0Wh.

0.0
u_2 float

Linear factor for upper energy limit of cell depending on the applied charge current. Defaults to -1.326Wh/A.

-1.326
v_2 float

Offset for upper energy limit of cell depending on the applied charge current. This parameter can be viewed as the capacity of a single cell. Defaults to 19.14Wh.

19.14
alpha_d float

Maximum discharging C-rate. Defaults to -1.5C.

-1.5
alpha_c float

Maximum charging C-rate. Defaults to 0.7C.

0.7
eta_d float

Average fraction of power that has to be discharged from cell to obtain said power. Is equivalent to the discharging inefficiency. Should be >= 1. Defaults to 1.014.

1.014
eta_c float

Average fraction of power that is stored in cell when charged at said power. Is equivalent to the charging inefficiency. Should be between 0 and 1. Defaults to 0.978.

0.978
discharging_current_cutoff float

If the maximum allowed discharging current is higher than this value, discharging is stopped. Defaults to -0.05A.

-0.05
charging_current_cutoff float

If the maximum allowed charging current is lower than this value, charging is stopped. Defaults to 0.05A.

0.05
Source code in vessim/dispatchable.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
class ClcBattery(Storage):
    """Implementation of the C-L-C Battery model for lithium-ion batteries.

    This class implements the C-L-C model as described in:
        Kazhamiaka, F., Rosenberg, C. & Keshav, S.
        Tractable lithium-ion storage models for optimizing energy systems.
        Energy Inform 2, 4 (2019).
        `doi:10.1186/s42162-019-0070-6 <https://doi.org/10.1186/s42162-019-0070-6>`_

    The default parameterization models a pack of LGM50 21700 rechargable lithium-ion cells.
    This model should not be used in combination with large step sizes.

    Args:
        name: The name of the battery.
        number_of_cells: Number of cells in the battery pack. Defaults to 1.
        initial_soc: Initial battery state-of-charge (SoC). Has to be between 0 and 1.
            Defaults to 0.
        min_soc: Minimum allowed SoC for the battery. Has to be between 0 and 1.
            Defaults to 0. Can be altered during simulation.
        nom_voltage: Single cell nominal voltage in V. Defaults to 3.63V.
        u_1: Linear factor for lower energy limit of cell depending on applied discharge current.
            Defaults to -0.087Wh/A.
        v_1: Offset for lower energy limit of cell depending on the applied discharge current.
            Defaults to 0.0Wh.
        u_2: Linear factor for upper energy limit of cell depending on the applied charge current.
            Defaults to -1.326Wh/A.
        v_2: Offset for upper energy limit of cell depending on the applied charge current. This
            parameter can be viewed as the capacity of a single cell. Defaults to 19.14Wh.
        alpha_d: Maximum discharging C-rate. Defaults to -1.5C.
        alpha_c: Maximum charging C-rate. Defaults to 0.7C.
        eta_d: Average fraction of power that has to be discharged from cell to obtain said power.
            Is equivalent to the discharging inefficiency. Should be >= 1. Defaults to 1.014.
        eta_c: Average fraction of power that is stored in cell when charged at said power. Is
            equivalent to the charging inefficiency. Should be between 0 and 1. Defaults to 0.978.
        discharging_current_cutoff: If the maximum allowed discharging current is higher than this
            value, discharging is stopped. Defaults to -0.05A.
        charging_current_cutoff: If the maximum allowed charging current is lower than this value,
            charging is stopped. Defaults to 0.05A.
    """

    def __init__(
        self,
        name: str,
        number_of_cells: int = 1,
        initial_soc: float = 0,
        min_soc: float = 0,
        nom_voltage: float = 3.63,
        u_1: float = -0.087,
        v_1: float = 0.0,
        u_2: float = -1.326,
        v_2: float = 19.14,
        alpha_d: float = -1.5,
        alpha_c: float = 0.7,
        eta_d: float = 1.014,
        eta_c: float = 0.978,
        discharging_current_cutoff: float = -0.05,
        charging_current_cutoff: float = 0.05,
    ) -> None:
        super().__init__(name)
        assert number_of_cells > 0, "There has to be a positive number of cells."
        self.number_of_cells = number_of_cells
        self.u_1 = u_1
        self.v_1 = v_1
        self.u_2 = u_2
        self.v_2 = v_2
        assert 0 <= initial_soc <= 1, "Invalid initial state-of-charge. Has to be between 0 and 1."
        self._soc = initial_soc
        self.charge_level = self.v_2 * initial_soc  # Charge level of one cell
        assert 0 <= min_soc <= 1, "Invalid minimum state-of-charge. Has to be between 0 and 1."
        self.min_soc = min_soc
        self.nom_voltage = nom_voltage
        self.alpha_d = alpha_d * self.v_2
        self.alpha_c = alpha_c * self.v_2
        assert eta_d >= 1, "Invalid discharging inefficiency. Has to be greater or equal to 1."
        self.eta_d = eta_d
        assert 0 <= eta_c <= 1, "Invalid charging inefficiency. Has to be between 0 and 1."
        self.eta_c = eta_c
        self.discharging_power_cutoff = discharging_current_cutoff * self.nom_voltage
        self.charging_power_cutoff = charging_current_cutoff * self.nom_voltage

    def feasible_range(self, duration: int) -> tuple[float, float]:
        """Returns (min_power, max_power) based on CLC model constraints.

        Args:
            duration: Duration of the timestep in seconds.
        """
        # Discharge limit
        if self._soc <= self.min_soc:
            min_power = 0.0
        else:
            min_power = (
                np.maximum(
                    (self.charge_level - self.v_1)
                    / (self.u_1 / self.nom_voltage - duration * self.eta_d / 3600),
                    self.alpha_d,
                )
                * self.number_of_cells
            )
            if min_power > self.discharging_power_cutoff:
                min_power = 0.0

        # Charge limit
        if self._soc >= 1.0:
            max_power = 0.0
        else:
            max_power = (
                np.minimum(
                    (self.charge_level - self.v_2)
                    / (self.u_2 / self.nom_voltage - duration * self.eta_c / 3600),
                    self.alpha_c,
                )
                * self.number_of_cells
            )
            if max_power < self.charging_power_cutoff:
                max_power = 0.0

        return (min_power, max_power)

    def soc(self) -> float:
        """Returns the state-of-charge (SoC) of the battery (0 to 1)."""
        return self._soc

    def step(self, duration: int) -> None:
        """Update battery state based on current_power setpoint and duration."""
        if duration <= 0:
            raise ValueError("Duration needs to be a positive value")

        power = self.current_power
        if power > 0:
            self._charge(power, duration)
        elif power < 0 and self.soc() > self.min_soc:
            self._discharge(power, duration)

    def _charge(self, power: float, duration: int) -> None:
        max_power = (
            np.minimum(
                (self.charge_level - self.v_2)
                / (self.u_2 / self.nom_voltage - duration * self.eta_c / 3600),
                self.alpha_c,
            )
            * self.number_of_cells
        )
        if power > max_power:
            power = max_power if max_power >= self.charging_power_cutoff else 0.0

        self.charge_level += self.eta_c * (power / self.number_of_cells) * (duration / 3600)
        self._soc = self.charge_level / self.v_2

    def _discharge(self, power: float, duration: int) -> None:
        min_power = (
            np.maximum(
                (self.charge_level - self.v_1)
                / (self.u_1 / self.nom_voltage - duration * self.eta_d / 3600),
                self.alpha_d,
            )
            * self.number_of_cells
        )
        if power < min_power:
            power = min_power if min_power <= self.charging_power_cutoff else 0.0

        discharge_energy = self.eta_d * (power / self.number_of_cells) * (duration / 3600)

        if (self.charge_level + discharge_energy) < self.min_soc * self.v_2:
            self.charge_level = self.v_2 * self.min_soc
            self._soc = self.min_soc
            return

        self.charge_level += discharge_energy
        self._soc = self.charge_level / self.v_2

    def config(self) -> dict:
        return {
            "capacity": self.v_2 * self.number_of_cells,
            "min_soc": self.min_soc,
        }

    def state(self) -> dict:
        return {
            "soc": self._soc,
            "charge_level": self.charge_level * self.number_of_cells,
        }

feasible_range

Returns (min_power, max_power) based on CLC model constraints.

Parameters:

Name Type Description Default
duration int

Duration of the timestep in seconds.

required
Source code in vessim/dispatchable.py
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
def feasible_range(self, duration: int) -> tuple[float, float]:
    """Returns (min_power, max_power) based on CLC model constraints.

    Args:
        duration: Duration of the timestep in seconds.
    """
    # Discharge limit
    if self._soc <= self.min_soc:
        min_power = 0.0
    else:
        min_power = (
            np.maximum(
                (self.charge_level - self.v_1)
                / (self.u_1 / self.nom_voltage - duration * self.eta_d / 3600),
                self.alpha_d,
            )
            * self.number_of_cells
        )
        if min_power > self.discharging_power_cutoff:
            min_power = 0.0

    # Charge limit
    if self._soc >= 1.0:
        max_power = 0.0
    else:
        max_power = (
            np.minimum(
                (self.charge_level - self.v_2)
                / (self.u_2 / self.nom_voltage - duration * self.eta_c / 3600),
                self.alpha_c,
            )
            * self.number_of_cells
        )
        if max_power < self.charging_power_cutoff:
            max_power = 0.0

    return (min_power, max_power)

soc

Returns the state-of-charge (SoC) of the battery (0 to 1).

Source code in vessim/dispatchable.py
342
343
344
def soc(self) -> float:
    """Returns the state-of-charge (SoC) of the battery (0 to 1)."""
    return self._soc

step

Update battery state based on current_power setpoint and duration.

Source code in vessim/dispatchable.py
346
347
348
349
350
351
352
353
354
355
def step(self, duration: int) -> None:
    """Update battery state based on current_power setpoint and duration."""
    if duration <= 0:
        raise ValueError("Duration needs to be a positive value")

    power = self.current_power
    if power > 0:
        self._charge(power, duration)
    elif power < 0 and self.soc() > self.min_soc:
        self._discharge(power, duration)