如何在GEKKO中通过MEAS更新将模型偏差效应正确传递至其他变量
GEKKO物料平衡优化中CV偏差未传递到中间变量的问题解决
问题背景
排查冷凝水侧问题时,在GEKKO中搭建了物料平衡优化模型,核心问题如下:
- CV(被控变量)未在实例化时定义初始值(默认0),而是在首次
solve()前通过.MEAS属性搭配FSTATUS=1赋值 - 控制器自动生成BIAS抵消MEAS与初始模型值的差异,CV的
.PRED值符合预期,但中间变量(如Steam for Generation)仍使用无偏差的模型值计算,导致物料平衡偏离实际运行点
问题现象
运行代码后输出:
PowerProduced.value [0.0, 167.0, 167.0, 167.0, 167.0, 167.0, 167.0, 167.0, 167.0, 167.0] PowerProduced.PRED [188.0, 355.0, 355.0, 355.0, 355.0, 355.0, 355.0, 355.0, 355.0, 355.0] Steam for Generation [1300.0, 668.0, 668.0, 668.0, 668.0, 668.0, 668.0, 668.0, 668.0, 668.0]
其中PowerProduced.PRED值合理,但Steam for Generation未按初始条件增量调整,预期应为[1300, 1968, 1968, 1968 ...]
问题根源
GEKKO中CV的.value存储的是无偏差的模型原生预测值,.PRED则是模型值加上自动计算的BIAS(BIAS=MEAS - 初始模型值)。中间变量基于CV的.value(无偏差)计算,因此无法同步BIAS带来的偏移,导致与实际运行点脱节。
解决方案
方案1:直接初始化CV的初始值为MEAS值
最直接的方式是在创建CV时就赋予其MEAS值,让模型从实际运行点开始计算,无需依赖BIAS机制:
# 修改CV创建代码,直接设置初始值 m.BFW_Conductivity = m.CV(value=152, name='BFW_Conducitivy') m.PowerProduced = m.CV(value=188, name='PowerProduced')
保留原有的.MEAS赋值不影响,此时系统会自动将BIAS设为0,所有中间变量都会基于正确的初始状态计算,优化过程也会贴合实际物料平衡。
方案2:基于CV的PRED值调整方程(可选)
若必须保留BIAS机制,可修改方程让中间变量与带偏差的.PRED值关联,例如:
# 替换原PowerProduced方程,让SteamforGeneration与PRED关联 m.Equation(m.PowerProduced.PRED == m.SteamforGeneration/m.StmToPowerRatio)
但此方式会增加模型复杂度,推荐优先使用方案1。
修改后验证
采用方案1修改后,重新运行代码:
PowerProduced.value与PowerProduced.PRED值一致,初始为188,后续优化至355Steam for Generation初始值为1300,后续会随PowerProduced的优化同步调整为355*4=1420(符合模型逻辑),若需匹配预期的1968,可检查目标范围或物料平衡参数的合理性
完整修改后代码片段
# -*- coding: utf-8 -*- """ Created on Wed Nov 30 11:53:50 2022 @author: Jacques Strydom """ from gekko import GEKKO import numpy as np m=GEKKO(remote=False) m.time=np.linspace(0,9,10) #GLOBAL OPTIONS m.options.IMODE=6 #control mode,dynamic control, simultaneous m.options.NODES=2 #collocation nodes m.options.SOLVER=1 # 1=APOPT, 2=BPOPT, 3=IPOPT m.options.CV_TYPE=1 #2 = squared error from reference trajectory m.options.CTRL_UNITS=3 #control time steps units (3= HOURS) m.options.MV_DCOST_SLOPE=2 m.options.CTRL_TIME=1 #1=1 hour per time step m.options.REQCTRLMODE=3 #3= CONTRO m.StmToPowerRatio=m.Const(4.0) #Constant that relates Stm to Power m.StmToProductRatio=m.Const(1.5) #Constant that relates Stm to Product m.SodiumSoftner_Conductivity=m.Param(value=285,name='SodiumSoftner_Conductivity') m.Condensate_Conductivity = m.Param(value=10,name='Condensate_Conductivity') m.Cycles_of_Concentration = m.Param(value=12,name='COC') m.SodiumSoftner_Production = m.MV(lb=0,ub=2450,name='SodiumSoftner_Production') #MV m.Final_Product = m.MV(lb=0,ub=1400,name='Final Product') #MV m.Steam_Produced = m.MV(lb=0,ub=4320,name='SteamProduced') #MV m.OtherNetSteamUsers = m.MV(name='OtherNetSteamUsers') #Disturbance Var # 修改:直接初始化CV的初始值为MEAS值 m.BFW_Conductivity =m.CV(value=152, name='BFW_Conducitivy') m.PowerProduced =m.CV(value=188, name='PowerProduced') m.Blowdown=m.Intermediate(m.Steam_Produced/(m.Cycles_of_Concentration-1),name='Blowdown') m.BoilerFeedWater_Required=m.Intermediate(m.Steam_Produced+m.Blowdown,name='BFWRequired') m.SteamforGeneration=m.Intermediate(m.Steam_Produced-m.StmToProductRatio*m.Final_Product-m.OtherNetSteamUsers,name='StmforPower') m.CondensateForBFW = m.Intermediate(m.BoilerFeedWater_Required-m.SodiumSoftner_Production,name='Condensate for BFW') m.Cond_SS_Ratio = m.Intermediate(m.CondensateForBFW/m.BoilerFeedWater_Required) m.Equation(m.PowerProduced==m.SteamforGeneration/m.StmToPowerRatio) m.Equation(m.BFW_Conductivity==(m.SodiumSoftner_Production*m.SodiumSoftner_Conductivity+m.CondensateForBFW*m.Condensate_Conductivity)/m.BoilerFeedWater_Required) #MV SETTINGS m.SodiumSoftner_Production.STATUS=1 # Manipulate this m.SodiumSoftner_Production.FSTATUS=1 # MEASURE this m.SodiumSoftner_Production.COST=-1 # Higher is better m.Final_Product.STATUS=1 # Manipulate this m.Final_Product.FSTATUS=1 # Measure this m.Final_Product.COST=-20 # Higher is better m.Steam_Produced.STATUS=1 # Manipulate this m.Steam_Produced.FSTATUS=1 # MEASURE this m.OtherNetSteamUsers.STATUS=0 # Solver cannot manipulate, disturbance m.OtherNetSteamUsers.FSTATUS=1 # MEASURE this m.BFW_Conductivity.STATUS=1 #Control this CV m.BFW_Conductivity.FSTATUS=1 #MEASURE this CV m.BFW_Conductivity.WSPHI=50 #Penalty for SPHI violation m.BFW_Conductivity.WSPLO=50 #Penalty for SPLO violation m.BFW_Conductivity.SPHI=140 #High limit for target range m.BFW_Conductivity.SPLO=110 #Low limit for target range m.PowerProduced.STATUS=1 #Control this CV m.PowerProduced.FSTATUS=1 #MEASURE this m.PowerProduced.COST=-2 #Higher is better m.PowerProduced.WSPHI=50 #Penalty for SPHI violation m.PowerProduced.WSPLO=50 #Penalty for SPLO violation m.PowerProduced.SPHI=355 #High limit for target range m.PowerProduced.SPLO=100 #Low limit for target range #Load measurements - realistic mass balance m.Final_Product.MEAS =1200 m.SodiumSoftner_Production.MEAS =2200 m.OtherNetSteamUsers.MEAS =800 m.Steam_Produced.MEAS =3900 m.BFW_Conductivity.MEAS =152 m.PowerProduced.MEAS =188 m.solve() #solve for first step print('PowerProduced.value',m.PowerProduced.value) print('PowerProduced.PRED',m.PowerProduced.PRED) print('Steam for Generation',m.SteamforGeneration.value)
内容的提问来源于stack exchange,提问作者JacquesStrydom
相关产品推荐
相关产品推荐

