我正在使用 Pyomo 5.6.6 和 Couenne 0.5.6 作为 MacOS 下载的可执行文件将热交换器优化问题编程为 MINLP 问题。该模型有两个决策变量,即蒸汽形式的外部供热和冷却水形式的冷却效用。
正在发生的问题是,只有当我在最佳值附近的一个相当小的窗口中设置使用热/冷却的边界时,该模型才能得到解决。否则 Couenne 说这个问题是不可行的。
有人知道为什么求解器难以解决更大的界限吗?
换热器优化模型基于扩展转运模型。
目前,热交换器网络由两股热流和两股冷流组成,它们由入口和出口温度以及它们的热容量流量定义。此外,还应通过优化计算出一股自由热流(热蒸汽)和一股冷流(冷却水),以实现热平衡,同时最大限度地减少额外热蒸汽的使用。
我在表格中设置了使用加热和冷却设施的界限作为约束 Constraint1: Q <= value1 and Constraint2: Q >= value2
。
问题是我已经将这些值设置为非常接近最优值,如果我对该值没有一个好主意,这是不切实际的。
模型的代码以及工作/不工作情况的示例数据和求解器输出可以在Github上找到。
我知道在我的模型中,额外供热的最小值必须为 950。但是,仅当我将边界设置在 1 和 1100 之间时才会计算此值。如果我将边界更改为例如 0 和 5000,则求解器返回不可行。
在最好的情况下,我想将额外加热和冷却的使用设置为非负值,然后计算其余部分。
该模型的代码是:
import sys
sys.path.append('Here comes the path of the data.dat file')
from pyomo.environ import *
model = AbstractModel(name="(HEN_MODEL_V1)")
# hot streams
model.I = Set()
# cold streams
model.J = Set()
# k = grid point and kk =temperature interval boundaries
model.K = Set()
# hot/ cold utilities
model.I_HU = Set(within=model.I)
model.J_CU = Set(within=model.J)
# hot process streams
model.I_P = Set(within=model.I)
model.J_P = Set(within=model.J)
# temperature intervals
model.K_I = Set(within=model.K)
# ----- Parameters ----
# Minimal temperature approach
model.MAT = Param()
# Upper bound for inlet temperature
model.ai = Param(model.I)
model.aj = Param(model.J)
# Upperbound for outlet temperature
model.bi = Param(model.I)
model.bj = Param(model.J)
#Upperbound for heat steam
model.yi = Param(model.I, model.K_I)
model.yj = Param(model.J, model.K_I)
#Upperbounds for R and D
model.yyi = Param(model.I, model.K_I)
model.yyj = Param(model.J, model.K_I)
# Parameter for in and outlet temperature constraints
model.T_H_IN_CON = Param(model.I)
model.T_H_OUT_CON = Param(model.I)
model.T_C_IN_CON = Param(model.J)
model.T_C_OUT_CON = Param(model.J)
model.F_CON = Param(model.I)
model.F2_CON = Param(model.J)
# ----- Variables-----
# Gridtemperature
model.T = Var(model.K, within=NonNegativeReals)
# Residual heat of heat steam i in intervall k
model.R = Var(model.I, model.K_I, within=NonNegativeReals)
# Deficite heat of cold stream j in intervall k
model.D = Var(model.J, model.K_I, within=NonNegativeReals)
# Exchanged Heat from i to j in intervall k
model.Q = Var(model.I, model.J, model.K_I, within=NonNegativeReals)
# Heat in Cold stream j
model.QC = Var(model.J, model.K_I, within=NonNegativeReals)
#Disaggregates Cold streams j
model.QC1 = Var(model.J, model.K_I, within=NonNegativeReals)
model.QC2 = Var(model.J, model.K_I, within=NonNegativeReals)
# Heat in hot stream i
model.QH = Var(model.I, model.K_I, within=NonNegativeReals)
# Disaggregates hot streams i
model.QH1 = Var(model.I, model.K_I, within=NonNegativeReals)
model.QH2 = Var(model.I, model.K_I, within=NonNegativeReals)
# Heat Capacity flows of i and j
model.FH = Var(model.I_P, within=NonNegativeReals)
model.FC = Var(model.J, within=NonNegativeReals)
# Used additional Steam
model.steam = Var()
# Binary variables X for Inlet-Temp on Gridpoint
# Binary variables Y for Outlet-Temp in Grid k
# Binary variales Z for Stream through intervall k
model.XC = Var(model.J, model.K, within=Binary)
model.XH = Var(model.I, model.K, within=Binary)
model.YC = Var(model.J, model.K_I, within=Binary)
model.YH = Var(model.I, model.K_I, within=Binary)
model.ZC = Var(model.J, model.K_I, within=Binary)
model.ZH = Var(model.I, model.K_I, within=Binary)
# Disaggregated Inlettemperatures from Cold and Hot stream
model.TIN_HD = Var(model.I, model.K, within=NonNegativeReals)
model.TIN_CD = Var(model.J, model.K, within=NonNegativeReals)
# Inlettemperatures from Cold and hot stream
model.TIN_H = Var(model.I, within=NonNegativeReals)
model.TIN_C = Var(model.J, within=NonNegativeReals)
# Outlettemperature from Hot and cold streams
model.TOUT_HD = Var(model.I, model.K_I, within=NonNegativeReals)
model.TOUT_CD = Var(model.J, model.K_I, within=NonNegativeReals)
# Outlettemperatures from Cold and hot stream
model.TOUT_H = Var(model.I, within=NonNegativeReals)
model.TOUT_C = Var(model.J, within=NonNegativeReals)
# ------ Constraints ------
# DEFINITION OF THE INLET AND OUTLET TEMPERATURES OF THE HOT AND COLD STREAMS
# THAT HAVE TO BE MET
def T1_rule(model, i):
return model.TIN_H[i] == model.T_H_IN_CON[i]
def T2_rule(model,i):
return model.TOUT_H[i] == model.T_H_OUT_CON[i]
def T3_rule(model,j):
return model.TIN_C[j] == model.T_C_IN_CON[j]
def T4_rule(model, j):
return model.TOUT_C[j] == model.T_C_OUT_CON[j]
model.T1_con = Constraint(model.I, rule=T1_rule)
model.T2_con = Constraint(model.I, rule=T2_rule)
model.T3_con = Constraint(model.J, rule=T3_rule)
model.T4_con = Constraint(model.J, rule=T4_rule)
# DEFINITION OF THE FLOW CAPACITIES OF THE HEAT AND COLD PROCESS STREAMS
# DEFINTION OF THE UPPER AND LOWER BOUNDS OF THE FLOW CAPACITY OF THE
# COOLING ULTILITY
def F1_rule(model, i):
return model.FH[i] == model.F_CON[i]
def F2_rule_a(model,j):
return model.FC[j] == model.F2_CON[j]
def F2_rule_b(model,j):
return model.FC[j] >= 0
def F2_rule_c(model,j):
return model.FC[j] <= 5000
model.F1_con = Constraint(model.I_P, rule=F1_rule)
model.F2_con_a = Constraint(model.J_P, rule=F2_rule_a)
model.F2_con_b = Constraint(model.J_CU, rule=F2_rule_b)
model.F2_con_c = Constraint(model.J_CU, rule=F2_rule_c)
############ GLG 1 + 2 ################
# DEFINITION OF HEAT CASCADE
def Rcas_rule(model,i,k):
if k > 1:
return model.R[i,k] + sum(model.Q[i,j,k] for j in model.J) == model.R[i,k-1] + model.QH[i,k]
else:
return model.R[i,k] + sum(model.Q[i,j,k] for j in model.J) == model.QH[i,k]
def Dcas_rule(model,j,k):
a=len(model.K_I)
if k < a:
return model.D[j,k] + sum(model.Q[i,j,k] for i in model.I) == model.D[j,k+1] + model.QC[j,k]
else:
return model.D[j,k] + sum(model.Q[i,j,k] for i in model.I) == model.QC[j,k]
model.Rcas_con = Constraint(model.I, model.K_I, rule=Rcas_rule)
model.Dcas_con = Constraint(model.J, model.K_I, rule=Dcas_rule)
######### GLG 3 ############
# DEFINITION OF GRIDPOINT TEMPERATURES
def T_k_rule(model,k):
a = len(model.K)
if k < a-1:
return model.T[k] >= model.T[k+1]
else:
return model.T[k] <= model.T[k-1]
model.T_k_con = Constraint(model.K, rule=T_k_rule)
######### GLG 4 - 7 ###########
# DISAGGREGATED HOT INLET TEMPERATURES
# ASSIGNMENT TO GRIDPOINT TEMPERATURES
def TIN_Hot(model,i):
return sum(model.TIN_HD[i,k] for k in model.K) == model.TIN_H[i]
def THD_1(model,i,k):
return model.TIN_HD[i,k] >= model.T[k] - model.ai[i] * (1-model.XH[i,k])
def THD_2(model,i,k):
return model.TIN_HD[i,k] <= model.T[k]
def THD_3(model,i,k):
return model.TIN_HD[i,k] <= model.ai[i] * model.XH[i,k]
model.THIN1 = Constraint(model.I, rule=TIN_Hot)
model.THIN2 = Constraint(model.I, model.K, rule=THD_1)
model.THIN3 = Constraint(model.I, model.K, rule=THD_2)
model.THIN4 = Constraint(model.I, model.K, rule=THD_3)
#
########## GLG 8 - 11 #############
# DISSAGREGATED COLD INLET TEMPERATURES
# ASSIGMENT TO GRIDPOINT TEMPERATUES
def TIN_Cold(model,j):
return sum(model.TIN_CD[j,k] for k in model.K) == model.TIN_C[j]
# return model.TIN_C[j] == sum(model.TIN_CD[j,k] for k in model.K)
def TCD_1(model,j,k):
return model.TIN_CD[j,k] >= model.T[k] - model.MAT - model.aj[j] * (1-model.XC[j,k])
def TCD_2(model,j,k):
return model.TIN_CD[j,k] <= model.T[k] - model.MAT
def TCD_3(model,j,k):
return model.TIN_CD[j,k] <= model.aj[j] * model.XC[j,k]
model.TCIN1 = Constraint(model.J, rule=TIN_Cold)
model.TCIN2 = Constraint(model.J, model.K, rule=TCD_1)
model.TCIN3 = Constraint(model.J, model.K, rule=TCD_2)
model.TCIN4 = Constraint(model.J, model.K, rule=TCD_3)
#
######### GLG 12 - 15 ##############
# DISSAGREGATED HOT OUTLET TEMPERATURES
# ASSIGMENT TO TEMPERATURE INTERVALS
def TOUT_Hot(model,i):
return sum(model.TOUT_HD[i,k] for k in model.K_I) == model.TOUT_H[i]
def THD_11(model,i,k):
return model.TOUT_HD[i,k] >= model.T[k] - model.bi[i] * (1-model.YH[i,k])
def THD_22(model,i,k):
return model.TOUT_HD[i,k] <= model.T[k-1] # -1 Funktuniert sicher nicht
def THD_33(model,i,k):
return model.TOUT_HD[i,k] <= model.bi[i] * model.YH[i,k]
model.THOUT1 = Constraint(model.I, rule=TOUT_Hot)
model.THOUT2 = Constraint(model.I, model.K_I, rule=THD_11)
model.THOUT3 = Constraint(model.I, model.K_I, rule=THD_22)
model.THOUT4 = Constraint(model.I, model.K_I, rule=THD_33)
#
#
######### GLG 16 - 19 ###########
# DISAGGREGATED COLD OUTLET TEMPERATURES
# ASSIGNMENT TO TEMPERATURE INVERTALS
def TOUT_Cold(model,j):
return sum(model.TOUT_CD[j,k] for k in model.K_I) == model.TOUT_C[j]
def TCD_11(model,j,k):
return model.TOUT_CD[j,k] >= model.T[k] - model.MAT - model.bj[j] * (1-model.YC[j,k])
def TCD_22(model,j,k):
return model.TOUT_CD[j,k] <= model.T[k-1] # Auch hier -1 in der Klammer
def TCD_33(model,j,k):
return model.TOUT_CD[j,k] <= model.bj[j] * model.YC[j,k]
model.TCOUT1 = Constraint(model.J, rule=TOUT_Cold)
model.TCOUT2 = Constraint(model.J, model.K_I, rule=TCD_11)
model.TCOUT3 = Constraint(model.J, model.K_I, rule=TCD_22)
model.TCOUT4 = Constraint(model.J, model.K_I, rule=TCD_33)
############ GLG 20 - 25 ###############
# BINARY CONSTRAINTS FÜR TEMPERATURE GRID
# Constraints for Binary Variables of Temperature Grid
def XH_rule(model,i):
return sum(model.XH[i,k] for k in model.K) == 1
def XC_rule(model,j):
return sum(model.XC[j,k] for k in model.K) == 1
def YH_rule(model,i):
return sum(model.YH[i,k] for k in model.K_I) == 1
def YC_rule(model,j):
return sum(model.YC[j,k] for k in model.K_I) == 1
def ZH_rule(model,i,k):
if k==1:
return model.ZH[i,k] == model.XH[i,k-1] - model.YH[i,k] # Auch hier die -1
else:
return model.ZH[i,k] == model.ZH[i,k-1] + model.XH[i,k-1] - model.YH[i,k] # Auch hier die -1
def ZC_rule(model,j,k):
temp = len(model.K_I)
if k < temp:
return model.ZC[j,k] == model.ZC[j,k+1] + model.XC[j,k] - model.YC[j,k]
else:
return model.ZC[j,k] == model.XC[j,k] - model.YC[j,k]
model.XH_con = Constraint(model.I, rule=XH_rule)
model.XC_con = Constraint(model.J, rule=XC_rule)
model.YH_con = Constraint(model.I, rule=YH_rule)
model.YC_con = Constraint(model.J, rule=YC_rule)
model.ZH_con = Constraint(model.I, model.K_I, rule=ZH_rule)
model.ZC_con = Constraint(model.J, model.K_I, rule=ZC_rule)
############### GLG 26 - 32 ################
# DISAGGRATED HEAT LOADS IN Q1 IF HEAT GOES THROUGH k AND Q2 if PARTIAL SPANS k
# Constraints for disaggregated Heat streams
def QH_rule(model,i,k):
return model.QH[i,k] == model.QH1[i,k] + model.QH2[i,k]
###### QH1 ######
def QH2_rule(model,i,k):
return model.QH1[i,k] >= model.FH[i] * (model.T[k-1] - model.T[k]) - model.yi[i,k] * (1-model.ZH[i,k]) # Hier wieder -1
def QH3_rule(model,i,k):
return model.QH1[i,k] <= model.FH[i] * (model.T[k-1] - model.T[k]) # hier wieder die -1
def QH4_rule(model,i,k):
return model.QH1[i,k] <= model.yi[i,k] * model.ZH[i,k]
###### QH2 ######
def QH5_rule(model,i,k):
return model.QH2[i,k] >= model.FH[i] * (model.T[k-1] - model.TOUT_HD[i,k]) - model.yi[i,k] * (1-model.YH[i,k])
def QH6_rule(model,i,k):
return model.QH2[i,k] <= model.FH[i] * (model.T[k-1] - model.TOUT_HD[i,k]) + model.yi[i,k] * (1-model.YH[i,k])
def QH7_rule(model,i,k):
return model.QH2[i,k] <= model.yi[i,k] * model.YH[i,k]
model.QH_con = Constraint(model.I_P, model.K_I, rule=QH_rule)
model.QH2_con = Constraint(model.I_P, model.K_I, rule=QH2_rule)
model.QH3_con = Constraint(model.I_P, model.K_I, rule=QH3_rule)
model.QH4_con = Constraint(model.I_P, model.K_I, rule=QH4_rule)
model.QH5_con = Constraint(model.I_P, model.K_I, rule=QH5_rule)
model.QH6_con = Constraint(model.I_P, model.K_I, rule=QH6_rule)
model.QH7_con = Constraint(model.I_P, model.K_I, rule=QH7_rule)
############### GLG 33 ###################
# HEAT LOAD FOR HEAT UTILITY
def QH8_rule(model,i,k):
return model.QH[i,k] <= model.yi[i,k] * model.XH[i,k-1]
model.QH8_con = Constraint(model.I_HU, model.K_I, rule=QH8_rule)
############## GLG 34 - 40 #####################
# DISAGGREGATED COLD HEAT LOADS; Q1 IF COLD GOES THROUGH k , Q2 IF IT ENDS THERE
# Constraints for disaggregated Cold streams
def QC_rule(model,j,k):
return model.QC[j,k] == model.QC1[j,k] + model.QC2[j,k]
####### QC1 #########
def QC2_rule(model,j,k):
return model.QC1[j,k] >= model.FC[j] * (model.T[k-1] - model.T[k]) - model.yj[j,k] * (1 - model.ZC[j,k]) # Hier wieder -1
def QC3_rule(model,j,k):
return model.QC1[j,k] <= model.FC[j] * (model.T[k-1] - model.T[k]) # hier wieder die -1
def QC4_rule(model,j,k):
return model.QC1[j,k] <= model.yj[j,k] * model.ZC[j,k]
####### QC2 ###########
def QC5_rule(model,j,k):
return model.QC2[j,k] >= model.FC[j] * (model.TOUT_CD[j,k] - model.T[k] + model.MAT) - model.yj[j,k] * (1 - model.YC[j,k])
def QC6_rule(model,j,k):
return model.QC2[j,k] <= model.FC[j] * (model.TOUT_CD[j,k] - model.T[k] + model.MAT) + model.yj[j,k] * (1 - model.YC[j,k])
def QC7_rule(model,j,k):
return model.QC2[j,k] <= model.yj[j,k] * model.YC[j,k]
model.QC_con = Constraint(model.J, model.K_I, rule=QC_rule)
model.QC2_con = Constraint(model.J, model.K_I, rule=QC2_rule)
model.QC3_con = Constraint(model.J, model.K_I, rule=QC3_rule)
model.QC4_con = Constraint(model.J, model.K_I, rule=QC4_rule)
###
model.QC5_con = Constraint(model.J, model.K_I, rule=QC5_rule)
model.QC6_con = Constraint(model.J, model.K_I, rule=QC6_rule)
model.QC7_con = Constraint(model.J, model.K_I, rule=QC7_rule)
###### GLG 41 + 42 #########
# RESIDUAL HEAT AND DEFIZIT SHOULD BE BELOW A GIVEN UPPER BOUND
#####
def R_rule(model, i, k):
temp=len(model.K_I)
if k < temp:
return model.R[i,k] <= model.yyi[i,k] * sum(model.XH[i,kk] for kk in model.K if kk<= k-1)
else:
return model.R[i,k] == 0
def D_rule(model, j, k):
if k > 1:
return model.D[j,k] <= model.yyj[j,k] * (1 - sum(model.XC[j,kk] for kk in model.K if kk <= k-1))
else:
return model.D[j,k] == 0
model.R1_con = Constraint(model.I, model.K_I, rule=R_rule)
model.D1_con = Constraint(model.J, model.K_I, rule=D_rule)
#####
# DEFINITION OF LOWER AND UPPER BOUND OF STEAM- HEAT THAT CAN BE USED
#####
def Steam_lb(model,):
return model.QH[3,1] >= 1
def Steam_ub(model,):
return model.QH[3,1] <= 1500
model.St_lb=Constraint(rule=Steam_lb)
model.St_ub=Constraint(rule=Steam_ub)
#####
# DEFINITION OF ADDITIONAL VARIABLE TO DISPLAY THE OBJECTIVE VALUE
####
def Steam_use(model):
return model.steam == sum(model.QH[3,k] for k in model.K_I)
model.Steam_use_con = Constraint(rule=Steam_use)
######
# DEFINITION OF OBJECTIVE RULE: MINIMIZE THE USAGE OF ADDITIONAL STEAM
####
def obj_rule(model):
return sum(model.QH[3,k] for k in model.K_I)
model.objective= Objective(rule=obj_rule)
#
#-------------------------Solving---------------------------------------------
#--------------------------------------------------------------------
instance= model.create_instance('data.dat')
solver = SolverFactory('couenne')
results = solver.solve(instance, tee=True)
instance.steam.pprint()
以及在上面代码中指定的路径中保存为“data.dat”的数据文件中的文本:
set K := 0 1 2 3 4 5;
set K_I := 1 2 3 4 5;
set I := 1 2 3;
set J := 1 2 3;
set I_P := 1 2;
set J_P := 1 2;
set I_HU := 3;
set J_CU := 3;
param MAT := 10;
param T_H_IN_CON :=
1 600
2 590
3 680
;
param T_H_OUT_CON :=
1 370
2 400
3 680
;
param T_C_OUT_CON :=
1 650
2 490
3 320
;
param T_C_IN_CON :=
1 400
2 350
3 300
;
param ai :=
1 1000
2 1000
3 1000
;
param aj :=
1 1000
2 1000
3 1000
;
param bi :=
1 1000
2 1000
3 1000
;
param bj :=
1 1000
2 1000
3 1000
;
param yi :=
1 1 100000
1 2 100000
1 3 100000
1 4 100000
1 5 100000
2 1 100000
2 2 100000
2 3 100000
2 4 100000
2 5 100000
3 1 100000
3 2 100000
3 3 100000
3 4 100000
3 5 100000
;
param yj :=
1 1 100000
1 2 100000
1 3 100000
1 4 100000
1 5 100000
2 1 100000
2 2 100000
2 3 100000
2 4 100000
2 5 100000
3 1 100000
3 2 100000
3 3 100000
3 4 100000
3 5 100000
;
param yyi :=
1 1 100000
1 2 100000
1 3 100000
1 4 100000
1 5 100000
2 1 100000
2 2 100000
2 3 100000
2 4 100000
2 5 100000
3 1 100000
3 2 100000
3 3 100000
3 4 100000
3 5 100000
;
param yyj :=
1 1 100000
1 2 100000
1 3 100000
1 4 100000
1 5 100000
2 1 100000
2 2 100000
2 3 100000
2 4 100000
2 5 100000
3 1 100000
3 2 100000
3 3 100000
3 4 100000
3 5 100000
;
param F_CON :=
1 10
2 20
;
param F2_CON :=
1 15
2 14
;