Source code for sdom.models.formulations_storage

from pyomo.core import Var, Constraint, Expression
from pyomo.environ import Set, Param, Binary, NonNegativeReals, sqrt
from ..constants import STORAGE_PROPERTIES_NAMES, MW_TO_KW
from .models_utils import crf_rule

[docs] def initialize_storage_sets(block, data: dict): block.j = Set( initialize = data['STORAGE_SET_J_TECHS'] ) block.b = Set( within=block.j, initialize = data['STORAGE_SET_B_TECHS'] ) # Initialize storage properties block.properties_set = Set( initialize = STORAGE_PROPERTIES_NAMES )
####################################################################################| # ----------------------------------- Parameters -----------------------------------| ####################################################################################|
[docs] def add_storage_parameters(model, data: dict): # Battery life and cycling max_cycles_dict = data['storage_data'].loc['MaxCycles'].to_dict() model.storage.MaxCycles = Param( model.storage.j, initialize = max_cycles_dict ) # Storage data initialization storage_dict = data["storage_data"].stack().to_dict() storage_tuple_dict = {(prop, tech): storage_dict[(prop, tech)] for prop in STORAGE_PROPERTIES_NAMES for tech in model.storage.j} model.storage.data = Param( model.storage.properties_set, model.storage.j, initialize = storage_tuple_dict ) model.storage.r = Param( initialize = float(data["scalars"].loc["r"].Value) ) # Interest rate model.storage.CRF = Param( model.storage.j, initialize = crf_rule ) #Capital Recovery Factor -STORAGE
####################################################################################| # ------------------------------------ Variables -----------------------------------| ####################################################################################|
[docs] def add_storage_variables(model): # Charging power for storage technology j in hour h model.storage.PC = Var(model.h, model.storage.j, domain=NonNegativeReals, initialize=0) # Discharging power for storage technology j in hour h model.storage.PD = Var(model.h, model.storage.j, domain=NonNegativeReals, initialize=0) # State-of-charge for storage technology j in hour h model.storage.SOC = Var(model.h, model.storage.j, domain=NonNegativeReals, initialize=0) # Charging capacity for storage technology j model.storage.Pcha = Var(model.storage.j, domain=NonNegativeReals, initialize=0) # Discharging capacity for storage technology j model.storage.Pdis = Var(model.storage.j, domain=NonNegativeReals, initialize=0) # Energy capacity for storage technology j model.storage.Ecap = Var(model.storage.j, domain=NonNegativeReals, initialize=0) model.storage.capacity_fraction = Var(model.storage.j, model.h, domain=Binary, initialize=0)
####################################################################################| # ----------------------------------- Expressions ----------------------------------| ####################################################################################|
[docs] def storage_power_capex_cost_expr_rule(block, j): return ( block.CRF[j] * ( MW_TO_KW * block.data['CostRatio', j] * block.data['P_Capex', j]*block.Pcha[j] + MW_TO_KW * (1 - block.data['CostRatio', j]) * block.data['P_Capex', j]*block.Pdis[j] ) )
[docs] def storage_energy_capex_cost_expr_rule(block, j): return ( block.CRF[j] * ( MW_TO_KW *block.data['E_Capex', j]*block.Ecap[j] ) )
[docs] def storage_fixed_om_cost_expr_rule(block, j): return ( MW_TO_KW * block.data['CostRatio', j] * block.data['FOM', j]*block.Pcha[j] + MW_TO_KW * (1 - block.data['CostRatio', j]) * block.data['FOM', j]*block.Pdis[j] )
def _add_storage_expressions(block): block.power_capex_cost_expr = Expression(block.j, rule = storage_power_capex_cost_expr_rule ) block.energy_capex_cost_expr = Expression(block.j, rule = storage_energy_capex_cost_expr_rule ) block.capex_cost_expr = Expression(block.j, rule = lambda m,j: m.power_capex_cost_expr[j] + m.energy_capex_cost_expr[j] ) block.fixed_om_cost_expr = Expression(block.j, rule = storage_fixed_om_cost_expr_rule ) block.total_capex_cost = Expression( rule = sum( block.capex_cost_expr[j] for j in block.j ) ) block.total_fixed_om_cost = Expression( rule = sum( block.fixed_om_cost_expr[j] for j in block.j ) )
[docs] def add_storage_expressions(model): _add_storage_expressions(model.storage)
####################################################################################| # ----------------------------------- Add_costs -----------------------------------| ####################################################################################|
[docs] def add_storage_fixed_costs(model): """ Add cost-related variables for storage technologies to the model. Parameters: model: The optimization model to which storage cost variables will be added. Returns: Costs sum for storage technologies, including capital and fixed O&M costs. """ return ( # Storage Capex and Fixed O&M model.storage.total_capex_cost + model.storage.total_fixed_om_cost )
[docs] def add_storage_variable_costs(model): """ Add variable costs for storage technologies to the model. Parameters: model: The optimization model to which storage variable costs will be added. Returns: Variable costs sum for storage technologies, including variable O&M costs. """ return ( sum( model.storage.data['VOM', j] * sum(model.storage.PD[h, j] for h in model.h) for j in model.storage.j ) )
####################################################################################| # ----------------------------------- Constraints ----------------------------------| ####################################################################################| # State-Of-Charge Balance -
[docs] def soc_balance_rule(model, h, j): if h > 1: return model.storage.SOC[h, j] == model.storage.SOC[h-1, j] \ + sqrt(model.storage.data['Eff', j]) * model.storage.PC[h, j] \ - model.storage.PD[h, j] / sqrt(model.storage.data['Eff', j]) else: # cyclical or initial condition return model.storage.SOC[h, j] == model.storage.SOC[max(model.h), j] \ + sqrt(model.storage.data['Eff', j]) * model.storage.PC[h, j] \ - model.storage.PD[h, j] / sqrt(model.storage.data['Eff', j])
# Max cycle year
[docs] def max_cycle_year_rule(model, j): #TODO check here this hardcoded Li-Ion n_steps = model.n_steps_modeled iterate = range(1, n_steps + 1) return sum(model.PD[h, j] for h in iterate) <= (model.MaxCycles[j] / model.data['Lifetime', j]) * model.Ecap[j]
[docs] def add_storage_constraints( model ): """ Add storage-related constraints to the model. Parameters: model: The optimization model to which storage constraints will be added. Returns: None """ # Ensure that the charging and discharging power do not exceed storage limits model.storage.ChargSt= Constraint(model.h, model.storage.j, rule=lambda m, h, j: m.PC[h, j] <= m.data['Max_P', j] * m.capacity_fraction[j, h]) model.storage.DischargeSt = Constraint(model.h, model.storage.j, rule=lambda m, h, j: m.PD[h, j] <= m.data['Max_P', j] * (1 - m.capacity_fraction[j, h])) # Hourly capacity bounds model.storage.MaxHourlyCharging = Constraint(model.h, model.storage.j, rule= lambda m,h,j: m.PC[h, j] <= m.Pcha[j]) model.storage.MaxHourlyDischarging = Constraint(model.h, model.storage.j, rule= lambda m,h,j: m.PD[h, j] <= m.Pdis[j]) # Limit state of charge of storage by its capacity model.storage.MaxSOC = Constraint(model.h, model.storage.j, rule=lambda m, h, j: m.SOC[h,j]<= m.Ecap[j]) # SOC Balance Constraint model.SOCBalance = Constraint(model.h, model.storage.j, rule=soc_balance_rule) # - Constraints on the maximum charging (Pcha) and discharging (Pdis) power for each technology model.storage.MaxPcha = Constraint( model.storage.j, rule=lambda m, j: m.Pcha[j] <= m.data['Max_P', j] ) model.storage.MaxPdis = Constraint( model.storage.j, rule=lambda m, j: m.Pdis[j] <= m.data['Max_P', j] ) # Charge and discharge rates are equal - model.storage.PchaPdis = Constraint( model.storage.b, rule=lambda m, j: m.Pcha[j] == m.Pdis[j] ) # Max and min energy capacity constraints (handle uninitialized variables) model.storage.MinEcap = Constraint(model.storage.j, rule= lambda m,j: m.Ecap[j] >= m.data['Min_Duration', j] * m.Pdis[j] / sqrt(m.data['Eff', j])) model.storage.MaxEcap = Constraint(model.storage.j, rule= lambda m,j: m.Ecap[j] <= m.data['Max_Duration', j] * m.Pdis[j] / sqrt(m.data['Eff', j])) model.storage.MaxCycleYear_constraint = Constraint( model.storage.j, rule=max_cycle_year_rule)