考虑绿电与储能的供热流量日前优化分配案例:基于pyomo实现

问题描述

本案例在微电网日前优化调度基础上加以修改,其结构如下图所示。系统电力侧包含有风电、光伏、以及电网。

热力侧包含有热泵、储熱罐、电加热、以及相应的热负荷需求。日前经济调度调度问题是指在对风电出力、光伏出力、热负荷进行日前(未来24h)预测的基础上,充分消纳绿电为前提,考虑电网的分时电价,通过热泵与储熱罐等可调控手段,使得系统经济性最优。

image

案例要求在如下已知条件下,求不同调度方案的平均用电成本

  • 未来24h、每隔15min共96个时间点的负荷、光伏、风电出力预测,及分时电价数据;
  • 两台热泵额定制热功率为 1000kw 和 1500kw,COP 分别为 3.5 和 5.5,负荷率不得低于 30%,15 分钟内负荷变化小于额定的 10%,设定热泵出口温度为 45 度。
  • 储热罐容量为 1.5 吨,储热损失 1 度,储热罐容量百分比需要在 0.1 到 0.9 之间,初始在 0.2,要求调控时间周期结束后容量百分比不变。
  • 电加热效率为 0.9,主要用于储热罐供热流量的再热。
  • 储热罐流入与流出需要两台水泵,水泵扬程记为 2m,轴效率为 0.8,电效率因数 k=1.25。
  • 需求侧回水温度为 30 度。

模型构建

案例涉及热泵、储热罐、电力侧和热力侧,我们依次建立数学模型,然后交予求解器求解。

一个基于 pyomo 的数学模型包含六大要素(决策变量,约束,目标函数,索引集合,参数,表达式)

  • 索引集合:决策变量的索引,包含系统的时间索引和设备索引。
  • 决策变量:水泵的流量和储热罐的进出水流量。
  • 约束:能量平衡和流量平衡,以及各设备需要满足的参数要求。例如热泵的功率限制,储热罐的容量限制等。
  • 目标函数:运行成本,即系统电费
  • 参数、表达式:用于表示系统各项属性。

开始先抽象一个模型基类,表示各类调度设备,包含名称、资源上线、等基本属性,同时提供一个create_model()方法,用于实现决策变量、约束条件、目标函数等模型三要素。

class Model:
    """Model基类模型"""
    def __init__(self, name: str,
                 Upper, # 资源上限
                 efficiency,# 使用能源时的效率
                 ):
        """资源模型的基类"""
        # technical parameters
        self.name = name
        self.Upper = Upper
        self.efficiency = efficiency
    def create_model(self, model, dt: float):
        raise NotImplementedError

然后定义一个最后执行的优化函数。

其中利用pyo.ConcreteModel()定义模型。

设置表示时间点的索引集合。data中包含负荷需求序列与绿电出力序列。

pyo.Set(initialize=[i for i in range(len(data))])由于有 96 个时间点,所以此处设置长度为 96 的时间索引。

def optimize(data,resources):
    model = pyo.ConcreteModel()
    model.TimePoint = pyo.Set(initialize=[i for i in range(len(data))])  # 采用列表初始化集合索引,表示时间点

1、热泵模型

热泵模型继承自Model基类,同时新增了 回水温度出水温度的属性。

在 create_model()方法中设定索引、决策变量、表达式、与约束。

1)定义索引

有两台热泵,所以定义长度为 2 的热泵设备 ID 索引,用于区分热本 1 与热泵 2。

model.pumpID = pyo.Set(initialize = [i for i in range(len(self.efficiency))])

2)决策变量

设置热泵的流量为决策变量,由时间索引与热泵设备 ID 索引可设置 96*2 的二维决策变量,同时设置下限为 0,上限暂时未定由约束给出。

model.Pump_M = pyo.Var(model.TimePoint,model.pumpID,
                       within=pyo.NonNegativeReals, bounds=(0,None))

3)表达式

热泵制热功率表达式:流量温差比热。考虑到单位转换后为M(OutT-InT)4.2*1000/3.6

def Pump_Q_rule(model,timestamp,id):
    return (model.Pump_M[timestamp,id]*(self.OutT-self.InT)*4.2*1000/3.6)
model.Pump_Q = pyo.Expression(model.TimePoint,model.pumpID, rule=Pump_Q_rule)

热泵耗电表达式:制热功率/SOC

def Pump_P_rule(model,timestamp,id):
    return model.Pump_Q[timestamp,id]/self.efficiency[id]
model.Pump_P = pyo.Expression(model.TimePoint,model.pumpID, rule=Pump_P_rule)

4)约束

# 约束
# 热泵制热功率限制,小于额定功率,大于额定功率的0.3,功率变化值小于额定的0.1
def Pump_Q_constr_rule1(model,timestamp,id):
    return model.Pump_Q[timestamp,id] <= self.Upper[id]
model.Pump_Q_constr1 = pyo.Constraint(model.TimePoint,model.pumpID, rule = Pump_Q_constr_rule1)
def Pump_Q_constr_rule2(model,timestamp,id):
    return model.Pump_Q[timestamp,id] >= 0.3*(self.Upper[id])
model.Pump_Q_constr2 = pyo.Constraint(model.TimePoint,model.pumpID, rule = Pump_Q_constr_rule2)
def Pump_Q_constr_rule3(model,timestamp,id):
    if timestamp==0:
       return pyo.Constraint.Skip
    else:
       return (model.Pump_Q[timestamp,id] - model.Pump_Q[timestamp-1,id]) <= 0.1*(self.Upper[id])
model.Pump_Q_constr3 = pyo.Constraint(model.TimePoint,model.pumpID, rule = Pump_Q_constr_rule3)
def Pump_Q_constr_rule4(model,timestamp,id):
    if timestamp==0:
       return pyo.Constraint.Skip
    else:
       return (model.Pump_Q[timestamp,id] - model.Pump_Q[timestamp-1,id]) >= -0.1*(self.Upper[id])
model.Pump_Q_constr4 = pyo.Constraint(model.TimePoint,model.pumpID, rule = Pump_Q_constr_rule4)

2、储热罐模型

    def __init__(self, name: str,
                 Upper: float, # 储熱罐容量(吨)
                 efficiency: float, # 出口电加热效率
                 InT: float, # 进水温度
                 OutT: float,# 出口温度
                 init_soc: float, # 初始储水比例
                 soc_limit: list, # 储水比例可允许的范围(0.1-0.99)

新增了 进水温度出水温度的属性和 初始储水比例以及储水比例可允许的范围(0.1-0.99)。

1)决策变量

储热罐有两个流量,一个流出一个流入,在此定义成两组变量,每组变量长度为 96。下限为 0,初始值为 0.

model.Stor_In_M = pyo.Var(model.TimePoint,within=pyo.NonNegativeReals, bounds=(0,None),initialize = 0)
model.Stor_Out_M = pyo.Var(model.TimePoint,within=pyo.NonNegativeReals, bounds=(0,None),initialize = 0)      

2) 表达式

        # 储熱罐水泵用电功率
        def Stor_pump_P_rule(model,timestamp):
            return ((model.Stor_In_M[timestamp] + model.Stor_Out_M[timestamp])*5*9.8*1.25/0.8)
        model.Stor_pump_P = pyo.Expression(model.TimePoint, rule=Stor_pump_P_rule)
        # 出水流量电加热 热能出力表达式
        def Stor_Out_Q_rule(model,timestamp):
            return (model.Stor_Out_M[timestamp]*(self.InT-self.OutT)*4.2*1000/3.6)
        model.Stor_Out_Q = pyo.Expression(model.TimePoint, rule=Stor_Out_Q_rule)
        # 出水流量电加热功率
        def Stor_P_rule(model,timestamp):
            return model.Stor_Out_Q[timestamp]/self.efficiency
        model.Stor_P = pyo.Expression(model.TimePoint, rule=Stor_P_rule)
        # SOC表达式
        def StorageSOC_rule(model,timepoint):
            soc = self.init_soc
            delta = ( (model.Stor_In_M[timepoint]-model.Stor_Out_M[timepoint]) * dt) / self.Upper
            if timepoint==0:
                soc = soc + delta
            else:
                soc = model.Stor_soc[timepoint-1] + delta
            return soc
        model.Stor_soc = pyo.Expression(model.TimePoint, initialize=StorageSOC_rule)

3)约束

  • 储热罐流入与流出只能有其一。

当采用 流入流量 * 流出流量 = 0 约束时,出现无法求解的情况。

def Stor_M_constr_rule(model,timestamp):
    return model.Stor_In_M[timestamp]*model.Stor_Out_M[timestamp]==0
model.Stor_M_constr = pyo.Constraint(model.TimePoint, rule=Stor_M_constr_rule)

ValueError: Cannot load a SolverResults object with bad status: error

为此对约束进行松弛, 改为 (流入流量 - 流出流量)2 >=流入流量2 和 >= 流出流量**2

def Stor_M_constr_rule2_1(model,timestamp):
    return (model.Stor_In_M[timestamp]-model.Stor_Out_M[timestamp])**2 >= model.Stor_In_M[timestamp]**2
model.Stor_M_constr2_1 = pyo.Constraint(model.TimePoint, rule=Stor_M_constr_rule2_1)
def Stor_M_constr_rule2_2(model,timestamp):
    return (model.Stor_In_M[timestamp]-model.Stor_Out_M[timestamp])**2 >= model.Stor_Out_M[timestamp]**2
model.Stor_M_constr2_2 = pyo.Constraint(model.TimePoint, rule=Stor_M_constr_rule2_2)
  • 流量变化限制约束:15 分钟内流量变化小于容量的 0.2
th = 0.2
def Stor_In_M_constr_rule1(model,timestamp):
    if timestamp==0:
        return pyo.Constraint.Skip
    else:
        return (model.Stor_In_M[timestamp] - model.Stor_In_M[timestamp-1]) <= th*self.Upper
model.Stor_In_M_constr1 = pyo.Constraint(model.TimePoint, rule = Stor_In_M_constr_rule1)
def Stor_In_M_constr_rule2(model,timestamp):
    if timestamp==0:
        return pyo.Constraint.Skip
    else:
        return (model.Stor_In_M[timestamp] - model.Stor_In_M[timestamp-1]) >= -th*self.Upper
model.Stor_In_M_constr2 = pyo.Constraint(model.TimePoint, rule = Stor_In_M_constr_rule2)
def Stor_Out_M_constr_rule1(model,timestamp):
    if timestamp==0:
        return pyo.Constraint.Skip
    else:
        return (model.Stor_Out_M[timestamp] - model.Stor_Out_M[timestamp-1]) <= th*self.Upper
model.Stor_Out_M_constr1 = pyo.Constraint(model.TimePoint, rule = Stor_Out_M_constr_rule1)
def Stor_Out_M_constr_rule2(model,timestamp):
    if timestamp==0:
        return pyo.Constraint.Skip
    else:
        return (model.Stor_Out_M[timestamp] - model.Stor_Out_M[timestamp-1]) >= -th*self.Upper
model.Stor_Out_M_constr2 = pyo.Constraint(model.TimePoint, rule = Stor_Out_M_constr_rule2)
  • SOC 约束
s1, s2 = self.soc_limit  # s1 soc的下限,s2 soc的上限
def Stor_soc_constr_rule1(model,timepoint):
    return model.Stor_soc[timepoint] <= s2
model.Stor_soc_constr1 = pyo.Constraint(model.TimePoint, rule=Stor_soc_constr_rule1 )
def Stor_soc_constr_rule2(model,timepoint):
    return model.Stor_soc[timepoint] >= s1
model.Stor_soc_constr2 = pyo.Constraint(model.TimePoint, rule=Stor_soc_constr_rule2 )
# SOC经过一个调控周期后=初始
model.Stor_soc_constr3 = pyo.Constraint(
    expr=self.init_soc +sum(
        [(model.Stor_In_M[timepoint]-model.Stor_Out_M[timepoint])*dt for timepoint in model.TimePoint]
    )/ self.Upper== self.init_soc)

3、电源侧模型

def __init__(self, name: str,
             Upper: float, # 最大买电功率
             efficiency,
             unit_cost: list,  # 向公用电网购买电力时的单位成本
             wt: list, # 风电功率
             pt: list,# 光伏功率
             ):

新增了 电价绿电出力的属性。

1)表达式

# 买电功率,热泵和电加热用电量减新能源发电量
def Grid_P(model,timepoint):
    P = sum([model.Pump_P[timepoint,id] for id in model.pumpID]) + \
        model.Stor_P[timepoint] + model.Stor_pump_P[timepoint] - \
        (self.wt[timepoint] + self.pt[timepoint])
    return P
model.Grid_P = pyo.Expression(model.TimePoint,rule = Grid_P)
# 买电成本
model.Grid_cost = pyo.Expression(
    expr = sum([(model.Grid_P[timepoint] * self.unit_cost[timepoint] * dt) for timepoint in model.TimePoint]))

2)约束

# 买电功率约束,大于0,小于上限
def Grid_P_constr_rule1(model,timepoint):
    return model.Grid_P[timepoint] >= 0
model.Grid_P_constr1 = pyo.Constraint(model.TimePoint, rule=Grid_P_constr_rule1)
def Grid_P_constr_rule2(model,timepoint):
    return model.Grid_P[timepoint] <= self.Upper
model.Grid_P_constr2 = pyo.Constraint(model.TimePoint, rule=Grid_P_constr_rule2)

4、热力侧模型

不基于基类模型,设置如下属性

def __init__(self, name: str,
             load: list, # 热负荷需求
             FromT: float,  # 供水温度
             ToT: float,  # 回水温度
             ):

1)表达式

热泵流量之和 - 存入储热罐的流量 + 储热罐流出的流量 = 供热流量

# 供热流量
def Heat_M_constr_rule(model,timepoint):
    M_all = sum([model.Pump_M[timepoint,id] for id in model.pumpID]) - \
            model.Stor_In_M[timepoint] + model.Stor_Out_M[timepoint]
    return M_all
model.Heat_All_M = pyo.Expression(model.TimePoint, rule=Heat_M_constr_rule)

2)约束

  • 储熱罐进水流量小于两个热泵的流量
def Stor_In_M_constr_rule(model,timepoint):
    return model.Stor_In_M[timepoint]<=sum([model.Pump_M[timepoint,id] for id in model.pumpID])
model.Stor_In_M_constr = pyo.Constraint(model.TimePoint, rule=Stor_In_M_constr_rule)
  • 热力平衡

供热流量 * 温差 * 比热 = 热力需求

def Heat_Q_constr_rule(model,timepoint):
    return model.Heat_All_M[timepoint]*(self.FromT-self.ToT)*4.2*1000/3.6 == self.load[timepoint]
model.Heat_Q_constr = pyo.Constraint(model.TimePoint, rule=Heat_Q_constr_rule)

求解

扩充之前的 optimize 函数。指定 ipopt 作为求解器

def optimize(data,resources):
    model = pyo.ConcreteModel()
    model.TimePoint = pyo.Set(initialize=[i for i in range(len(data))])  # 采用列表初始化集合索引,表示时间点
    for re in resources:
        re.create_model(model, dt=15 / 60)
    # 设定最小化优化目标
    model.allcost = pyo.Objective(rule=model.Grid_cost, sense=pyo.minimize)
    # 求解
    opt = pyo.SolverFactory('ipopt')  # 指定 ipopt 作为求解器
    solution = opt.solve(model, symbolic_solver_labels=True)  # 调用求解器求解
    solution.write()  # 写入求解结果
    log_infeasible_constraints(model)
    print(solution.solver.termination_condition)  # 终止条件 一般包括三种 optimal, feasible, infeasible
    print(solution.solver.termination_message)  # 求解得到的信息 一般为一行字符串信息
    print(solution.solver.status)  # 表示求解的状态 包括 ok, warning, error, aborted, or unknown
    return model

优化结果分析

1、情景 1

没有绿电,所有电力均来自电网。电价为阶梯式电价,其中时间点 40 到 80 电价较高。分别计算最优方案的热泵流量、储热罐流量、购买电力、全天电费。

Solver: 
- Status: ok
  Message: Ipopt 3.12.4\x3a Optimal Solution Found
  Termination condition: optimal
  Id: 0
  Error rc: 0
  Time: 1.3437001705169678
# ----------------------------------------------------------
optimal objective: 3727.5781256704713

image

分析:

全天总供电费用:3727.5 元

因为可以根据电价与负荷的情况选择热力来源与是否蓄能所以:

  • 1 号热泵 cop 较低,所以全程流量都较小,仅在时刻 80 附近由于储热罐容量不足时增加了流量。
  • 2 号热泵曲线与负荷基本一致,在时刻 65 到 85 附近负荷达到上限。
  • 储热罐在电价较低且负荷较低的的时段存入部分热水,在 65 至 85 负荷较大且电价较高的时段放出热水。

image

电力数据与流量数据体现的内容一致。

2、情景 2

增加绿电,不足的电力向电网购买。电价为阶梯式电价,其中时间点 40 到 80 电价较高。分别计算最优方案的热泵流量、储热罐流量、购买电力、全天电费。

Solver: 
- Status: ok
  Message: Ipopt 3.12.4\x3a Optimal Solution Found
  Termination condition: optimal
  Id: 0
  Error rc: 0
  Time: 1.3954508304595947
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution: 
- number of solutions: 0
  number of solutions displayed: 0
optimal objective: 2313.878033993671

image

image

分析:

全天总供电费用:2313.8元

因为有了绿电做补充所以很明显的费用下降:

  • 热泵的曲线与情景 1 基本一致,仅在 0 到 40 的时段内由于风电的波动导致了 2 号热泵负荷的波动。
  • 购买的电力由于绿电的提供,前半天曲线明显降低。

3、情景 3

在情景 2 的基础上,调整储热损失为 0.5 度。分别计算最优方案的热泵流量、储热罐流量、购买电力、全天电费。

Solver: 
- Status: ok
  Message: Ipopt 3.12.4\x3a Optimal Solution Found
  Termination condition: optimal
  Id: 0
  Error rc: 0
  Time: 0.9821372032165527
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution: 
- number of solutions: 0
  number of solutions displayed: 0
optimal objective: 2003.602815292267

image

分析:

全天总供电费用:2003.6元

  • 由于降低了储能损失,在电价较低且有绿电的情况下,储能量相比情景 2 大幅增加,从而也降低了成本。
  • 2 号热泵在电价较低且有绿电的情况下满负荷运行,在高电价时段为最低负荷运行。

image

4、情景 4

在情景 3 的基础上,限制购电功率为 280。分别计算最优方案的热泵流量、储热罐流量、购买电力、全天电费。

Solver: 
- Status: ok
  Message: Ipopt 3.12.4\x3a Optimal Solution Found
  Termination condition: optimal
  Id: 0
  Error rc: 0
  Time: 0.9911162853240967
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution: 
- number of solutions: 0
  number of solutions displayed: 0
optimal objective: 2057.101508010534

image

分析:

全天总供电费用:2057.1 元

限制购电功率的情况下,为满足负荷需求从而会增加储能量,但储能存在损失也会耗能所以成本反而增加。

image

参考资料

https://www.cnblogs.com/aimoboshu/p/16869948.html

https://www.cnblogs.com/aimoboshu/p/16902062.html

posted @ 2022-11-24 14:32  薄书  阅读(8)  评论(0)    收藏  举报