Dear Oemof Developers,
I’m currently working on a district heating model using oemof and I’m stuck on defining charging constraints for a thermal storage. I’d greatly appreciate any advice or suggestions, eventhough any referenceable code!
The scope of the storage is 50 MWh, and I’d like to implement the following logic:
- Charging can only start when SOC < 25 MWh.
- Once charging has started, there is no restriction on continuing until SOC reaches 50 MWh.
- There’s no limit on the charging rate or on the final SOC (balanced=True).
What I’ve tried:
I defined the thermal storage and used a Big-M binary variable instead of if to control whether charging is allowed at each timestep. But it doesn’t work. The relevant code snippet is below:
from oemof.solph import EnergySystem, Bus, Flow, GenericStorage, Sink
import pyomo.environ as po
— Create buses —
heat_bus = Bus(label=‘heat_bus’)
— Thermal storage —
thermal_storage = GenericStorage(
label=‘thermal_storage’,
inputs={heat_bus: Flow(nominal_value=50)},
outputs={heat_bus:Flow(nominal_value=50)},
nominal_storage_capacity=50,
initial_storage_level=0,
inflow_conversion_factor=1,
outflow_conversion_factor=1,
loss_rate=0.01,
balanced=True,
)
— Heat demand —
heat_sink = Sink(
label=‘heat_demand’,
inputs={heat_bus: Flow(nominal_value=1, fix=heat_load)}
)
— Model —
model = Model(energysystem)
— SOC-based discrete charging —
M_flow = 1e3 # Big-M for max flow
threshold = 25 # SOC threshold for starting charge
# Binary variable: 1 if charging allowed
model.charge_mode = po.Var(model.TIMEINDEX, within=po.Binary)
# Constraint: inflow allowed only if charge_mode=1
def charging_activation_rule(m, t):
inflow_var = m.flow[(heat_bus, thermal_storage)][t]
return inflow_var <= M_flow * m.charge_mode[t]
model.charging_activation = po.Constraint(model.TIMEINDEX,rule=charging_activation_rule)
# Constraint: activate charge_mode only if SOC < threshold
def soc_limit_rule(m, t):soc = m.GenericStorageBlock.storage_content[thermal_storage, t]return soc <= threshold + M_flow*(1 - m.charge_mode[t])
model.soc_limit = po.Constraint(model.TIMEINDEX, rule=soc_limit_rule)
Looking forward to your any words!
Yours Sincerely
Xinyuan