Battery¶
A Battery models an energy storage system in your portfolio. The optimizer decides when to charge and discharge it to minimize costs (or maximize revenue).
Basic usage¶
from odys.energy_system_models.assets.storage import Battery
battery = Battery(
name="bess",
capacity=100.0, # MWh of storage
max_power=50.0, # MW charge/discharge limit
efficiency_charging=0.95,
efficiency_discharging=0.95,
soc_start=0.5, # starts at 50%
)
Fields¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
str |
Yes | - | Unique identifier for the battery |
capacity |
float |
Yes | - | Total energy capacity (MWh) |
max_power |
float |
Yes | - | Maximum charge/discharge power (MW) |
efficiency_charging |
float |
Yes | - | Charging efficiency, between 0 and 1 |
efficiency_discharging |
float |
Yes | - | Discharging efficiency, between 0 and 1 |
soc_start |
float |
Yes | - | Initial state of charge, as a fraction of capacity (0-1) |
soc_end |
float |
No | None |
Required final state of charge (0-1). If None, the optimizer is free to choose |
soc_min |
float |
No | 0.0 |
Minimum allowed state of charge (0-1) |
soc_max |
float |
No | 1.0 |
Maximum allowed state of charge (0-1) |
degradation_cost |
float |
No | None |
Cost per MWh cycled through the battery |
self_discharge_rate |
float |
No | None |
Fraction of stored energy lost per hour (0-1) |
State of charge (SOC)¶
The SOC fields control how the battery's energy level behaves:
soc_startis where the battery begins. A value of0.5means it starts at 50% of its capacity.soc_endconstrains where the battery must end up. This is useful when you want to ensure the battery isn't fully drained at the end of the optimization horizon.soc_minandsoc_maxset the operating range. For example, if you don't want to go below 20% or above 90%:
battery = Battery(
name="bess",
capacity=100.0,
max_power=50.0,
efficiency_charging=0.90,
efficiency_discharging=0.85,
soc_start=0.5,
soc_end=0.5,
soc_min=0.2,
soc_max=0.9,
)
Note
soc_start and soc_end must fall within the [soc_min, soc_max] range. Pydantic validation will catch this if you get it wrong.
Efficiency¶
Charging and discharging efficiencies are applied separately. If you charge 10 MWh with 90% efficiency, 9 MWh actually goes into the battery. If you then discharge those 9 MWh at 85% efficiency, you get 7.65 MWh out.
This means the round-trip efficiency is efficiency_charging * efficiency_discharging.
Degradation cost¶
If you want the optimizer to account for battery wear, set a degradation_cost:
battery = Battery(
name="bess",
capacity=100.0,
max_power=50.0,
efficiency_charging=0.95,
efficiency_discharging=0.95,
soc_start=0.5,
degradation_cost=5.0, # 5 currency units per MWh cycled
)
This adds a cost penalty for each MWh that flows through the battery, discouraging unnecessary cycling.
Results¶
After optimization, access battery results through result.batteries:
result = energy_system.optimize()
result.batteries.net_power # charge/discharge per timestep
result.batteries.state_of_charge # SOC at each timestep
Positive net_power means discharging, negative means charging.