Efficient addition von custom constraints

34 views
Skip to first unread message

Julius Ostertag

unread,
Jul 9, 2025, 10:39:20 AMJul 9
to pypsa
Dear PyPSA community,

I am working on an option, to add custom constraints to model proper battery degradation (both time and cycle-dependent). 
To add this, I am adding custom constraints to every timestep, in order to limit the maximum charge e for every timestep, depending on the past cycles and time. Funcitonally this works fine in my implementation, however the writing of the constraints takes a really long time. For longer timeframes (1 year in 15 minute steps) I get a memory error. My current function to add the constraints looks like this: 

def con_battery_degradation(model, net, DoD, size_battery_pack, name, num, update_deg):
'''

:param model: Linopy Model from pypsa network
:param net: pypsa network
:param DoD: maximum depth of discharge of battery packs
:param size_battery_pack: size [kWh] of battery packs
:param name: name of the current battery pack
:param num: number of the current battery pack
:param update_deg: how often the degradation should be updated

:return: model with added constraints
'''
# Adding constraint for maximum depth of discharge
con_DoD = get_con_DoD(model, DoD, size_battery_pack, name)
model.add_constraints(con_DoD, name=f'battery_depth_of_discharge_{name}')

# Selecting current battery from all stores
battery_capacity_t = model.variables['Store-e'].sel(Store=name)

max_cycles = {0.8: 10000, 0.9: 6500}.get(DoD)
if max_cycles is None:
    raise ValueError(f"Invalid DoD {DoD}. Must be 0.8 or 0.9")


# 2) Precompute constant value for degradation per cycle
Q_bat_block_EoL = calculate_battery_linear_charge_throughput(max_cycles, DoD, C_bat_block=120, Deg_bat_EoL=0.3)

deg_abs = 0

for idx, timestep in enumerate(net.snapshots):
# For higher efficiency the calculation can be set to not be every timestep => faster writing of constraints
if idx % update_deg == 0:
# Current power through separate link to battery
power_total = (model.variables['Link-p'].loc[:timestep, f"battery_in_{num}"]).sum() * 0.25

# Calculate current degradation. deg_abs is of type LinearExpression
deg_abs = battery_degradation(timestep, power_total, size_battery_pack, model, Q_bat_block_EoL)

# Create constraint for every timestep
con_batt_deg = battery_capacity_t.loc[timestep] + deg_abs <= size_battery_pack

# Add Constraints
model.add_constraints(con_batt_deg, name=f'battery_{num}_degradation_{timestep}')

return model

Can you recommend options to speed up the writing of constraints? 
I have tried vectorization and chunking (to avoid MemoryErrors) but could not get it to work. 

Best regards,
Julius
Reply all
Reply to author
Forward
0 new messages