Pyomo储能系统调度优化

Pyomo energy storage system dispatch optimization

我正在尝试使用 pyomo 为储能系统创建模型优化。使用家庭的千瓦时需求和电价,我想将电池在正确时间充电和放电的成本降至最低。我已经有一个适用于 1 年数据的工作模型,并且该模型能够找到最佳解决方案(请参见下面的代码)。然而,当我试图找到一个只有三个月的模型时(比如从 10 月到 12 月),pyomo returns 终止条件为“未绑定”,但我不明白为什么。

1 年数据的模型

#Battery parameters
battery_capacity = 252
total_energy = 13.1
usable_energy = 12.4 
nominal_voltage = 51.8
ratio = total_energy/usable_energy
power = usable_energy / ratio
nominal_current = usable_energy / nominal_voltage * 1000
recharging_hours = battery_capacity/nominal_current
battery_level_threshold = 0

model = pyo.ConcreteModel()
#Set time period
model.T = pyo.Set(initialize=pyo.RangeSet(len(df_2021)),ordered=True)

#PARAMETERS:
model.b_efficiency  = pyo.Param(initialize=0.9)
model.b_min_cap = pyo.Param(initialize=0)
model.b_max_cap = pyo.Param(initialize=12.4)
model.b_charge_power = pyo.Param(initialize=power)
model.b_discharge_power = pyo.Param(initialize=power)

model.spot_prices = pyo.Param(model.T,initialize=dict(enumerate(df_2021["Price/kWh"],1)),within=pyo.Any)
model.demand = pyo.Param(model.T, initialize=dict(enumerate(df_2021["Demand"],1)),within=pyo.Any)
#Variables : also the variable has to  be indexed with the time T
model.b_soc = pyo.Var(model.T, domain = pyo.NonNegativeReals, bounds = (model.b_min_cap, model.b_max_cap))
model.b_discharge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.b_charge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.elect_purchased = pyo.Var(model.T, domain = pyo.NonNegativeReals)

#CONSTRAINTS
#Purchase constraint
def purchase_constraint(model,t):
    return model.elect_purchased[t] >= model.demand[t] - model.b_discharge[t] + model.b_charge[t]

#State of charge constraint
def soc_constraint(model,t):
    if t == model.T.first():
        return model.b_soc[t] == model.b_max_cap / 2 #- model.b_discharge[t] + model.b_charge[t]
    else: 
        return model.b_soc[t] == model.b_soc[t-1] - model.b_discharge[t-1] + model.b_charge[t-1]
#Discharge and charge constraints
def discharge_constraint_1(model,t):
    """ Maximum discharge rate within a single hour """
    return model.b_discharge[t] <= model.b_discharge_power    
def discharge_constraint_2(model,t):<br/>
    """ Sets the maximum energy available to be discharged as the SOC - minimum SOC """
    return model.b_discharge[t] <= model.b_soc[t] - model.b_min_cap

def charge_constraint_1(model,t):
    """ Maximum charge rate within a single hour """
    return model.b_charge[t] <= model.b_charge_power 

def charge_constraint_2(model,t):<br/>
    """ Sets the maximum energy available to be cahrge as the SOC max """
    return model.b_charge[t]  <= model.b_max_cap - model.b_soc[t]

model.purchase_c = pyo.Constraint(model.T, rule = purchase_constraint)
model.soc_c = pyo.Constraint(model.T, rule = soc_constraint)<br/>
model.discharge_c1 = pyo.Constraint(model.T,rule = discharge_constraint_1)
model.discharge_c2 = pyo.Constraint(model.T,rule = discharge_constraint_2)
model.charge_c1 = pyo.Constraint(model.T,rule = charge_constraint_1)
model.charge_c2 = pyo.Constraint(model.T,rule = charge_constraint_2)

#OBJECTIVE 
expr = sum(model.elect_purchased[t] * model.spot_prices[t] for t in model.T)
model.objective = pyo.Objective(rule = expr, sense = pyo.minimize)    
opt = pyo.SolverFactory('cbc',executable='/usr/bin/cbc')
results = opt.solve(model)
results.write()

结果1年数据
找到最优解

但是,当我使用相同的模型结构和约束更改数据集时,pyomo 找不到解决方案。

模型3个月:

#Battery parameters
battery_capacity = 252
total_energy = 13.1
usable_energy = 12.4 
nominal_voltage = 51.8
ratio = total_energy/usable_energy
power = usable_energy / ratio
nominal_current = usable_energy / nominal_voltage * 1000
recharging_hours = battery_capacity/nominal_current
battery_level_threshold = 0

model = pyo.ConcreteModel()
#Set time period
model.T = pyo.Set(initialize=pyo.RangeSet(len(df_2021)),ordered=True)

#PARAMETERS:
model.b_efficiency  = pyo.Param(initialize=0.9)
model.b_min_cap = pyo.Param(initialize=0)
model.b_max_cap = pyo.Param(initialize=12.4)
model.b_charge_power = pyo.Param(initialize=power)
model.b_discharge_power = pyo.Param(initialize=power)

model.spot_prices = pyo.Param(model.T,initialize=dict(enumerate(df_2021["Price/kWh"],1)),within=pyo.Any)
model.demand = pyo.Param(model.T, initialize=dict(enumerate(df_2021["Demand"],1)),within=pyo.Any)
#Variables : also the variable has to  be indexed with the time T
model.b_soc = pyo.Var(model.T, domain = pyo.NonNegativeReals, bounds = (model.b_min_cap, model.b_max_cap))
model.b_discharge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.b_charge = pyo.Var(model.T, domain = pyo.NonNegativeReals)
model.elect_purchased = pyo.Var(model.T, domain = pyo.NonNegativeReals)

#CONSTRAINTS
#Purchase constraint
def purchase_constraint(model,t):
    return model.elect_purchased[t] >= model.demand[t] - model.b_discharge[t] + model.b_charge[t]

#State of charge constraint
def soc_constraint(model,t):
    if t == model.T.first():
        return model.b_soc[t] == model.b_max_cap / 2 #- model.b_discharge[t] + model.b_charge[t]
    else: 
        return model.b_soc[t] == model.b_soc[t-1] - model.b_discharge[t-1] + model.b_charge[t-1]
#Discharge and charge constraints
def discharge_constraint_1(model,t):
    """ Maximum discharge rate within a single hour """
    return model.b_discharge[t] <= model.b_discharge_power    
def discharge_constraint_2(model,t):<br/>
    """ Sets the maximum energy available to be discharged as the SOC - minimum SOC """
    return model.b_discharge[t] <= model.b_soc[t] - model.b_min_cap

def charge_constraint_1(model,t):
    """ Maximum charge rate within a single hour """
    return model.b_charge[t] <= model.b_charge_power 

def charge_constraint_2(model,t):<br/>
    """ Sets the maximum energy available to be cahrge as the SOC max """
    return model.b_charge[t]  <= model.b_max_cap - model.b_soc[t]

model.purchase_c = pyo.Constraint(model.T, rule = purchase_constraint)
model.soc_c = pyo.Constraint(model.T, rule = soc_constraint)<br/>
model.discharge_c1 = pyo.Constraint(model.T,rule = discharge_constraint_1)
model.discharge_c2 = pyo.Constraint(model.T,rule = discharge_constraint_2)
model.charge_c1 = pyo.Constraint(model.T,rule = charge_constraint_1)
model.charge_c2 = pyo.Constraint(model.T,rule = charge_constraint_2)

#OBJECTIVE 
expr = sum(model.elect_purchased[t] * model.spot_prices[t] for t in model.T)
model.objective = pyo.Objective(rule = expr, sense = pyo.minimize)    
opt = pyo.SolverFactory('cbc',executable='/usr/bin/cbc')
results = opt.solv

Pyomo returns:

WARNING: Loading a SolverResults object with a warning status into
    model.name="unknown";
      - termination condition: unbounded
      - message from solver: <undefined>
# ==========================================================
# = Solver Results                                         =
# ==========================================================
# ----------------------------------------------------------
#   Problem Information
# ----------------------------------------------------------
Problem: 
- Name: unknown
  Lower bound: None
  Upper bound: inf
  Number of objectives: 1
  Number of constraints: 16993
  Number of variables: 11329
  Number of nonzeros: 2832
  Sense: minimize
# ----------------------------------------------------------
#   Solver Information
# ----------------------------------------------------------
Solver: 
- Status: warning
  User time: -1.0
  System time: 0.45
  Wallclock time: 0.58
  Termination condition: unbounded
  Termination message: Model was proven to be unbounded.
  Statistics: 
    Branch and bound: 
      Number of bounded subproblems: 0
      Number of created subproblems: 0
    Black box: 
      Number of iterations: 0
  Error rc: 0
  Time: 0.6151924133300781

由于两次运行之间唯一的变化是数据帧的长度,我真的不知道在哪里寻找错误。

鉴于该模型适用于某些数据,但不适用于备用数据源,我们显然可以稍微关注数据集(未显示)。

我们在错误报告中有一个很大的线索,即问题是 unbounded。这意味着在最小化问题的情况下,没有什么可以阻止 objective 函数 运行 趋于无穷大或负无穷大。那么,让我们看看您的 objective 函数。你是:

sum(demand[t] * price[t] for t in T)

您的 demand 变量的域设置为 non-negative 实数(好)并且 price[t] 是从数据中读入的参数,并且您正在最小化。因此,这可能 运行 远离负无穷大的唯一方法是,如果您的数据中存在一个(或多个)负价格,那么求解器将对其采取行动并且 demand 将是无限的。

所以,梳理你的数据集。如果您使用 pandas,只需对价格 < 0 的行使用逻辑搜索,您可能会找到至少一个。然后你必须决定这是一个错字还是负价格是否现实(这可能发生在某些系统中),如果它是合法的,你将不得不施加一些其他约束来限制模型那种情况。