【问题标题】:How to describe a discrete parameter using GEKKO?如何使用 GEKKO 描述离散参数?
【发布时间】:2020-04-11 11:11:45
【问题描述】:

我有一个模型,其中包含 10 个描述补料分批生物反应器的方程。基本上,每 24 小时将“食物”(葡萄糖和其他成分)添加到系统中。我的问题是这个喂食过程目前被建模为两个时间步长 (delta_T) 上的流速 (L/H),而不是单个离散的食物添加 (delta_T = 0)。

这是葡萄糖方程的样子:

e4 = m.Intermediate(**(Fi/V)*SG** - (Fo/V)*G + (-mu/YXG - mG)*XV)

m.Equation(G.dt() == e4)

其中G 是生物反应器中的葡萄糖浓度 (mM),Fi 是输入进料速率 (L/h),V 是生物反应器体积 (L),SG 是葡萄糖饲料中的浓度(mM)。

我通过调用delta_T = 0.2 hours 设法使系统可行,换句话说,葡萄糖浓度连续(而不是立即)从G1 上升时间t1G2 在时间t1 + 0.2h。如果我尝试降低这个delta_T,系统会显示一个非常奇怪的行为。

时间数组如下所示:[..., 19.5, 20.0, 20.5, 21.0, 21.5, 22.0, 22.5, 23.0, 23.5, 24.0, 24.2, 24.5, 25.0, ... ]。

它以 0.5 小时的步长变化,每 24 小时,当我向生物反应器中添加葡萄糖时,我强制下一个时间步长只有 0.2,而不是 0.5。我希望这个 delta 为 0。

我的进给速度如下所示:

[..., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0,...]

无论如何我可以使这个喂食过程离散吗?完整代码如下所示。谢谢!!

import numpy as np
from gekko import GEKKO
import matplotlib.pyplot as plt
import pandas as pd

m = GEKKO(remote=False)    # create GEKKO model

#constants 3L fed-batch
KdQ = 0.001        #degree of degradation of glutamine (1/h)
mG = 1.1e-10   #glucose maintenance coefficient (mmol/cell/hour)
YAQ = 0.90         #yield of ammonia from glutamine
YLG = 2            #yield of lactate from glucose
YXG = 2.2e8    #yield of cells from glucose (cells/mmol)
YXQ = 1.5e9    #yield of cells from glutamine (cells/mmol)
KL = 150           #lactate saturation constant (mM)
KA = 40            #ammonia saturation constant (mM)
Kdmax = 0.01       #maximum death rate (1/h)
mumax = 0.044      #maximum growth rate (1/h)
KG = 1             #glucose saturation constant (mM)
KQ = 0.22          #glutamine saturation constant (mM)
mQ = 0             #glutamine maintenance coefficient (mmol/cell/hour)
kmu = 0.01         #intrinsic death rate (1/h)
Klysis = 2e-2  #rate of cell lysis (1/h)
Ci_star = 100      #inhibitor saturation concentration (mM)
qi = 2.5e-10   #specific inhibitor production rate (1/h)

N_HOURS = 150 #Number of hours of the experiment
TIME_STEP = 0.5
feed_small_delta_t = 0.2 

#create time array. It will be from 0 to N_HOURS, with time step TIME_STEP, 
#and every 24h, there will be a feed_small_delta_t
t = []
for i in range(int(N_HOURS/TIME_STEP +1)):
    t_value = i*TIME_STEP
    t.append(t_value)

    if t_value%24 == 0:
        t.append(t_value + feed_small_delta_t)

m.time = t

#Create input feed-rate array
Fi = np.zeros(len(t))
for i in range(1,len(t)):
    if t[i]%(24) == 0:
        Fi[i] = 0.1/feed_small_delta_t #it is divided by feed_small_delta_t so volume added is constant.

#Flow, volume and concentration
Fi = m.Param(Fi)   #input feed-rate (L/h)     
Fo = 0              #output feed-rate (L/h)
V = 3              #volume (L)
SG = 653           #glucose concentration in the feed (mM)
SQ = 58.8          #glutamine concentration in the feed (mM)


XTMM = m.Var(value=2,lb=-0.0000,name='XT')            #total cell density (MMcells/L)
XVMM = m.Var(value=2,lb=-0.0000, name='XV')      #viable cell density (MMcells/L)
XDMM = m.Var(value=0,lb=-0.0000,name='XD')          #dead cell density (MMcells/L)
G = m.Var(value = 20,lb=-0.0000,name='G')            #glucose concentration (mM)
Q = m.Var(value = 3.8,lb=-0.0000, name='Q')           #glutamine concentration (mM)
L = m.Var(value=0.1,lb=-0.0000,name='L')                #lactate concentration (mM)
A = m.Var(value=1.8,lb=-0.0000,name='A')              #ammonia concentration (mM)
Ci = m.Var(lb=-0.0000, name='Ci')            #inhibitor concentration (mM)
mu = m.Var(lb=-0.0000, name='mu')            #growth rate (1/h)
Kd = m.Var(lb=-0.0000, name='Kd')            #death rate(1/h)

# scale back to cells/L from million cells/L
XT = m.Intermediate(XTMM*1e8)
XV = m.Intermediate(XVMM*1e8)
XD = m.Intermediate(XDMM*1e8)

e1 = m.Intermediate((mu*XV - Klysis*XD - XT*Fo/V)/1e8)
e2 = m.Intermediate(((mu - Kd)*XV - XV*Fo/V)/1e8)
e3 = m.Intermediate((Kd*XV - Klysis*XD - XV*Fo/V)/1e8)
e4 = m.Intermediate((Fi/V)*SG - (Fo/V)*G + (-mu/YXG - mG)*XV)
e5 = m.Intermediate((Fi/V)*SQ - (Fo/V)*Q + (-mu/YXQ - mQ)*XV - KdQ*Q)
e6 = m.Intermediate(-YLG*(-mu/YXG -mG)*XV-(Fo/V)*L)
e7 = m.Intermediate(-YAQ*(-mu/YXQ - mQ)*XV +KdQ*Q-(Fo/V)*A)
e8 = m.Intermediate(qi*XV - (Fo/V)*Ci)
e9a = m.Intermediate((Ci_star*(KG+G)*(KQ+Q)*(L/KL + 1)*(A/KA + 1)))
e9b = m.Intermediate((mumax*G*Q*(Ci_star-Ci)))
e10a = m.Intermediate((mu+kmu))
e10b = m.Intermediate(Kdmax*kmu)

# create GEEKO equations
m.Equation(XTMM.dt() == e1)
m.Equation(XVMM.dt() == e2)
m.Equation(XDMM.dt() == e3)
m.Equation(G.dt() == e4)
m.Equation(Q.dt() == e5)
m.Equation(L.dt() == e6)
m.Equation(A.dt() == e7)
m.Equation(Ci.dt() == e8)
m.Equation(e9a * mu == e9b)
m.Equation(e10a * Kd == e10b)

# solve ODE
m.options.IMODE = 4
m.options.SOLVER = 1
m.options.NODES = 3
m.options.COLDSTART = 2
#m.open_folder()
m.solve(display=False)

plt.figure()
plt.subplot(3,1,1)
plt.plot(m.time, XV.value,label='XV')
plt.plot(m.time, XT.value,label='XT')
plt.plot(m.time, XD.value,label='XD')
plt.legend()
plt.subplot(3,1,2)
plt.plot(m.time, G.value,label='G')
plt.plot(m.time, Q.value,label='Q')
plt.plot(m.time, L.value,label='L')
plt.plot(m.time, A.value,label='A')
plt.plot(m.time, Ci.value,label='Ci')
plt.legend()
plt.subplot(3,1,3)
plt.plot(m.time, mu.value,label='mu')
plt.plot(m.time, Kd.value,label='Kd')
plt.legend()
plt.xlabel('Time (hr)')

plt.figure()
plt.plot(m.time, e1.value,'r-.',label='eqn1')
plt.plot(m.time, e2.value,'g:',label='eqn2')
plt.plot(m.time, e3.value,'b:',label='eqn3')
plt.plot(m.time, e4.value,'b--',label='eqn4')
plt.plot(m.time, e5.value,'y:',label='eqn5')
plt.plot(m.time, e6.value,'m--',label='eqn6')
plt.plot(m.time, e7.value,'b-.',label='eqn7')
plt.plot(m.time, e8.value,'g--',label='eqn8')
plt.plot(m.time, e9a.value,'r:',label='eqn9a')
plt.plot(m.time, e9b.value,'r--',label='eqn9b')
plt.plot(m.time, e10a.value,'k:',label='eqn10a')
plt.plot(m.time, e10b.value,'k--',label='eqn10b')
plt.legend()

plt.show()

【问题讨论】:

    标签: python ode gekko


    【解决方案1】:

    您将葡萄糖作为脉冲输入的策略是一种不连续输入的好方法。葡萄糖浓度离散跳跃的问题在于方程 4 中存在葡萄糖导数项:m.Equation(G.dt() == e4)。如果dG/dt 项在很短的时间内发生变化,则导数项会变得非常大。

    处理离散点的大导数的一种策略是使用m.options.NODES=2 来避免在有限元上具有正交配置的附加内部节点的问题。如果没有内部节点,您可能需要增加时间点的数量以保持集成的准确性。这允许将葡萄糖以非常短的持续时间脉冲输入到间歇反应器中,例如3.6 seconds 以进行添加。

    feed_small_delta_t = 0.001 # 3.6 seconds

    feed 输入的索引减一,所以Fi[i+1] 应该是施加脉冲的位置,而不是Fi[i]

    Fi = np.zeros(len(t))
    for i in range(1,len(t)):
        if t[i]%(24) == 0:
            Fi[i+1] = 0.1/feed_small_delta_t
    

    这给出了与您之前的结果相似的结果,但对于向批次中添加额外糖的离散事件的输入脉冲更短。

    带有修改的完整脚本

    import numpy as np
    from gekko import GEKKO
    import matplotlib.pyplot as plt
    import pandas as pd
    
    m = GEKKO(remote=False)    # create GEKKO model
    
    #constants 3L fed-batch
    KdQ = 0.001        #degree of degradation of glutamine (1/h)
    mG = 1.1e-10   #glucose maintenance coefficient (mmol/cell/hour)
    YAQ = 0.90         #yield of ammonia from glutamine
    YLG = 2            #yield of lactate from glucose
    YXG = 2.2e8    #yield of cells from glucose (cells/mmol)
    YXQ = 1.5e9    #yield of cells from glutamine (cells/mmol)
    KL = 150           #lactate saturation constant (mM)
    KA = 40            #ammonia saturation constant (mM)
    Kdmax = 0.01       #maximum death rate (1/h)
    mumax = 0.044      #maximum growth rate (1/h)
    KG = 1             #glucose saturation constant (mM)
    KQ = 0.22          #glutamine saturation constant (mM)
    mQ = 0             #glutamine maintenance coefficient (mmol/cell/hour)
    kmu = 0.01         #intrinsic death rate (1/h)
    Klysis = 2e-2  #rate of cell lysis (1/h)
    Ci_star = 100      #inhibitor saturation concentration (mM)
    qi = 2.5e-10   #specific inhibitor production rate (1/h)
    
    N_HOURS = 150 #Number of hours of the experiment
    TIME_STEP = 0.5
    feed_small_delta_t = 0.001 
    
    #create time array. It will be from 0 to N_HOURS, with time step TIME_STEP, 
    #and every 24h, there will be a feed_small_delta_t
    t = []
    for i in range(int(N_HOURS/TIME_STEP +1)):
        t_value = i*TIME_STEP
        t.append(t_value)
    
        if t_value%24 == 0:
            t.append(t_value + feed_small_delta_t)
    
    m.time = t
    
    #Create input feed-rate array
    Fi = np.zeros(len(t))
    for i in range(1,len(t)):
        if t[i]%(24) == 0:
            Fi[i+1] = 0.1/feed_small_delta_t #it is divided by feed_small_delta_t so volume added is constant.
    
    #Flow, volume and concentration
    Fi = m.Param(Fi)   #input feed-rate (L/h)     
    Fo = 0              #output feed-rate (L/h)
    V = 3              #volume (L)
    SG = 653           #glucose concentration in the feed (mM)
    SQ = 58.8          #glutamine concentration in the feed (mM)
    
    
    XTMM = m.Var(value=2,lb=-0.0000,name='XT')            #total cell density (MMcells/L)
    XVMM = m.Var(value=2,lb=-0.0000, name='XV')      #viable cell density (MMcells/L)
    XDMM = m.Var(value=0,lb=-0.0000,name='XD')          #dead cell density (MMcells/L)
    G = m.Var(value = 20,lb=-0.0000,name='G')            #glucose concentration (mM)
    Q = m.Var(value = 3.8,lb=-0.0000, name='Q')           #glutamine concentration (mM)
    L = m.Var(value=0.1,lb=-0.0000,name='L')                #lactate concentration (mM)
    A = m.Var(value=1.8,lb=-0.0000,name='A')              #ammonia concentration (mM)
    Ci = m.Var(lb=-0.0000, name='Ci')            #inhibitor concentration (mM)
    mu = m.Var(lb=-0.0000, name='mu')            #growth rate (1/h)
    Kd = m.Var(lb=-0.0000, name='Kd')            #death rate(1/h)
    
    # scale back to cells/L from million cells/L
    XT = m.Intermediate(XTMM*1e8)
    XV = m.Intermediate(XVMM*1e8)
    XD = m.Intermediate(XDMM*1e8)
    
    e1 = m.Intermediate((mu*XV - Klysis*XD - XT*Fo/V)/1e8)
    e2 = m.Intermediate(((mu - Kd)*XV - XV*Fo/V)/1e8)
    e3 = m.Intermediate((Kd*XV - Klysis*XD - XV*Fo/V)/1e8)
    e4 = m.Intermediate((Fi/V)*SG - (Fo/V)*G + (-mu/YXG - mG)*XV)
    e5 = m.Intermediate((Fi/V)*SQ - (Fo/V)*Q + (-mu/YXQ - mQ)*XV - KdQ*Q)
    e6 = m.Intermediate(-YLG*(-mu/YXG -mG)*XV-(Fo/V)*L)
    e7 = m.Intermediate(-YAQ*(-mu/YXQ - mQ)*XV +KdQ*Q-(Fo/V)*A)
    e8 = m.Intermediate(qi*XV - (Fo/V)*Ci)
    e9a = m.Intermediate((Ci_star*(KG+G)*(KQ+Q)*(L/KL + 1)*(A/KA + 1)))
    e9b = m.Intermediate((mumax*G*Q*(Ci_star-Ci)))
    e10a = m.Intermediate((mu+kmu))
    e10b = m.Intermediate(Kdmax*kmu)
    
    # create GEEKO equations
    m.Equation(XTMM.dt() == e1)
    m.Equation(XVMM.dt() == e2)
    m.Equation(XDMM.dt() == e3)
    m.Equation(G.dt() == e4)
    m.Equation(Q.dt() == e5)
    m.Equation(L.dt() == e6)
    m.Equation(A.dt() == e7)
    m.Equation(Ci.dt() == e8)
    m.Equation(e9a * mu == e9b)
    m.Equation(e10a * Kd == e10b)
    
    # solve ODE
    m.options.IMODE = 4
    m.options.SOLVER = 1
    m.options.NODES = 2
    m.options.COLDSTART = 2
    #m.open_folder()
    m.solve(display=False)
    
    plt.figure()
    plt.subplot(3,1,1)
    plt.plot(m.time, XV.value,label='XV')
    plt.plot(m.time, XT.value,label='XT')
    plt.plot(m.time, XD.value,label='XD')
    plt.legend()
    plt.subplot(3,1,2)
    plt.plot(m.time, G.value,label='G')
    plt.plot(m.time, Q.value,label='Q')
    plt.plot(m.time, L.value,label='L')
    plt.plot(m.time, A.value,label='A')
    plt.plot(m.time, Ci.value,label='Ci')
    plt.legend()
    plt.subplot(3,1,3)
    plt.plot(m.time, mu.value,label='mu')
    plt.plot(m.time, Kd.value,label='Kd')
    plt.legend()
    plt.xlabel('Time (hr)')
    
    plt.figure()
    plt.plot(m.time, e1.value,'r-.',label='eqn1')
    plt.plot(m.time, e2.value,'g:',label='eqn2')
    plt.plot(m.time, e3.value,'b:',label='eqn3')
    plt.plot(m.time, e4.value,'b--',label='eqn4')
    plt.plot(m.time, e5.value,'y:',label='eqn5')
    plt.plot(m.time, e6.value,'m--',label='eqn6')
    plt.plot(m.time, e7.value,'b-.',label='eqn7')
    plt.plot(m.time, e8.value,'g--',label='eqn8')
    plt.plot(m.time, e9a.value,'r:',label='eqn9a')
    plt.plot(m.time, e9b.value,'r--',label='eqn9b')
    plt.plot(m.time, e10a.value,'k:',label='eqn10a')
    plt.plot(m.time, e10b.value,'k--',label='eqn10b')
    plt.legend()
    
    plt.show()
    

    【讨论】:

    • 太棒了!谢谢赫登仁先生。令人惊讶的是,有像您这样的人在 stackoverflow 上编写开源代码并回答问题。
    猜你喜欢
    • 2021-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-10
    • 1970-01-01
    • 2019-06-08
    相关资源
    最近更新 更多