GEKKO Недостижимая система уравнений ОДУ для биореактора с подпиткой

Я новичок в GEKKO, а также в моделировании биореакторов, поэтому я могу упустить что-то очевидное.

У меня есть система из 10 ODE, описывающих биореактор периодического действия с подпиткой. Приведены все константы. На рисунке ниже показано ожидаемое поведение этой модели (взято из статьи). Однако единственное возможное решение, которое я нашел, - это когда плотность жизнеспособных клеток (XV) = 0 и остается 0 все время t, или если время T действительно мало (‹20). Если нижняя граница> = 0 или начальное значение установлено на XV и t> 20, система становится неработоспособной.

Уравнения и константы проверялись несколько раз. Я попытался присвоить своим переменным начальные значения, но это тоже не сработало. Я могу думать только о двух проблемах: я неправильно инициализирую переменные или я неправильно использую GEKKO. Любые идеи? Спасибо!!

Ожидаемое поведение модели

Уравнения

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

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

#constants 3L continuous fed-batch
KdQ = 0.001        #degree of degradation of glutamine (1/h)
mG = 1.1*10**-10   #glucose maintenance coefficient (mmol/cell/hour)
YAQ = 0.90         #yield of ammonia from glutamine
YLG = 2            #yield of lactate from glucose
YXG = 2.2*10**8    #yield of cells from glucose (cells/mmol)
YXQ = 1.5*10**9    #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 = 2*10**-2  #rate of cell lysis (1/h)
Ci_star = 100      #inhibitor saturation concentration (mM)
qi = 2.5*10**-10   #specific inhibitor production rate (1/h)

#Flow, volume and concentration
Fo = 0.001         #feed-rate (L/h)
Fi = 0.001         #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)

# create GEKKO parameter
t = np.linspace(0,120,121)
m.time = t

XT = m.Var(name='XT')            #total cell density (cells/L)
XV = m.Var(lb=0, name='XV')      #viable cell density (cells/L)
XD = m.Var(name='XD')            #dead cell density (cells/L)
G = m.Var(value = 30, name='G')  #glucose concentration (mM)
Q = m.Var(value = 4.5, name='Q') #glutamine concentration (mM)
L = m.Var(name='L')              #lactate concentration (mM)
A = m.Var(name='A')              #ammonia concentration (mM)
Ci = m.Var(name='Ci')            #inhibitor concentration (mM)
mu = m.Var(name='mu')            #growth rate (1/h)
Kd = m.Var(name='Kd')            #death rate(1/h)

# create GEEKO equations
m.Equation(XT.dt() == mu*XV - Klysis*XD - XT*Fo/V)
m.Equation(XV.dt() == (mu - Kd)*XV - XV*Fo/V)
m.Equation(XD.dt() == Kd*XV - Klysis*XD - XV*Fo/V)
m.Equation(G.dt() == (Fi/V)*SG - (Fo/V)*G + (-mu/YXG - mG)*XV)
m.Equation(Q.dt() == (Fi/V)*SQ - (Fo/V)*Q + (-mu/YXQ - mQ)*XV - KdQ*Q)
m.Equation(L.dt() == -YLG*(-mu/YXG -mG)*XV-(Fo/V)*L)
m.Equation(A.dt() == -YAQ*(-mu/YXQ - mQ)*XV +KdQ*Q-(Fo/V)*A)
m.Equation(Ci.dt() == qi*XV - (Fo/V)*Ci)
m.Equation(mu.dt() == (mumax*G*Q*(Ci_star-Ci)) / (Ci_star*(KG+G)*(KQ+Q)*(L/KL + 1)*(A/KA + 1)))
m.Equation(Kd.dt() == Kdmax*(kmu/(mu+kmu)))

# solve ODE
m.options.IMODE = 4
m.open_folder()
m.solve(display = False)

plt.plot(m.time, XV.value)

Статьи, использующие одну и ту же модель:

1) Магистерская работа с использованием GEKKO "МОДЕЛИРОВАНИЕ КЛЕТОЧНОЙ КУЛЬТУРЫ млекопитающих" по ссылке:

https://search.proquest.com/openview/e4df2d115cbc48ec63235a64b352249c/1.pdf?pq-origsite=gscholar&cbl=18750&diss=y

2) Оригинальный документ с описанием уравнений: «Сравнение моделей процесса и переносимость в масштабах биореактора и режимы работы биопроцесса клеток млекопитающих»

ссылка: https://sci-hub.tw/10.1002/btpr.1664

3) Бумага с контрольной системой, использующей эту модель: «Контроль концентрации глюкозы в биопроцессе клеток млекопитающих с подпиткой с использованием предсказывающего контроллера нелинейной модели»

ссылка: https://sci-hub.tw/https://doi.org/10.1016/j.jprocont.2014.02.007


person Felipe Mello    schedule 10.12.2019    source источник
comment
У вас есть 10 уравнений, но установлено только 2 значения. Остальные начальные значения автоматически устанавливаются на 0? Вы пробовали сократить временной интервал? Вы увидите, что при начальном XV = 5 решение расходится в районе t = 20.   -  person Lutz Lehmann    schedule 10.12.2019
comment
mu растет как 0.04*t, а XV как 5*exp(0.02*t^2), что составляет примерно 3e5 для t = 24. Это ожидаемая динамика? Сообщаемая проблема, приводящая к несовпадению, вероятно, состоит в том, что шкала возникающих ошибок для этих больших значений слишком сильно отклоняется от допусков по умолчанию.   -  person Lutz Lehmann    schedule 10.12.2019
comment
Спасибо за ответ @ Dr.LutzLehmann! Я добавил картинку с ожидаемым поведением модели. Как видите, симуляция длится более 100 часов, а XV находится в масштабе 10 ^ 9, поэтому наличие 3e5 для t = 24 вполне приемлемо. Все переменные имеют начальное значение, близкое к 0 (кроме XV). Я пробовал дать ему начальные значения, но не уверен, что это нужно. У меня есть 10 уравнений и 10 переменных. Разве этого не должно быть достаточно? Как вы думаете, поможет ли ослабление допусков?   -  person Felipe Mello    schedule 10.12.2019
comment
Вот несколько дополнительных советов по масштабированию и инициализации модели: apmonitor.com/do/index.php / Main / ModelInitialization Модели системной биологии часто имеют очень нелинейную динамику, требующую особого внимания. Мне также нравится делать меньшие временные шаги и решать с IMODE = 7 для этих типов проблем. Я скоро рассмотрю более подробно.   -  person John Hedengren    schedule 10.12.2019
comment
Неясно, подходит ли GEKKO для этого, я предполагаю, что это только первый шаг, ведущий к оценке параметров или тому подобному. Обратите внимание, что решатель коллокаций преобразует каждый временной шаг в систему из 10 уравнений, которые затем решаются одновременно с помощью внешнего инструмента оптимизации. Хотя это добавляет гибкости для добавления задач оптимизации поверх ODE, он выполняет гораздо больше работы, чем любой традиционный интегратор ODE. Использование odeint по-прежнему дает ошибки на протяжении всего интервала, но намного быстрее при малых интервалах.   -  person Lutz Lehmann    schedule 10.12.2019
comment
Универсальная рекомендация - изменить масштаб переменных так, чтобы решатель в идеале видел значения в диапазоне от 0,01 до 1000. Это означает удаление 1e9 из XV, XD, XT и, при необходимости, изменение констант соответствующим образом. Тогда эвристика управления, основанная на оценках ошибок решателя, больше попадает в диапазон тестовых примеров, использованных при его разработке.   -  person Lutz Lehmann    schedule 10.12.2019
comment
Не могли бы вы как-нибудь процитировать источник системы ODE? Либо как изображение, либо как ссылку? Я нахожу похожие статьи, но, похоже, они имеют разные темы и разные соглашения об именах и масштабировании количеств.   -  person Lutz Lehmann    schedule 10.12.2019
comment
@ Доктор ЛутцЛеманн, спасибо за ваши ответы. Я отредактировал свой вопрос и добавил 3 статьи, в которых использовалась та же самая модель. Одна из них - магистерская диссертация, в которой GEKKO использовалась для ее моделирования, но код проприетарный, поэтому я не знаю, как он это сделал.   -  person Felipe Mello    schedule 10.12.2019
comment
Большое спасибо @JohnHedengren. Я проверю эту ссылку. Сообщите мне, если я могу чем-нибудь помочь.   -  person Felipe Mello    schedule 10.12.2019
comment
Роберт Джексон (автор диссертации) linkedin.com/in/robert-jackson-0ab075187 также может пожелать поделиться моделью и другими особенностями своей реализации.   -  person John Hedengren    schedule 10.12.2019


Ответы (2)


Есть пара проблем:

  • последние два уравнения являются алгебраическими, а не дифференциальными. Это должно быть mu==..., а не mu.dt()==...
  • некоторые из уравнений могут делиться на ноль. Такие уравнения, как x.dt() = z/y, можно заменить на y * x.dt()==z, чтобы уравнение стало 0 * x.dt() == z, если y приближается к нулю.
  • некоторые из начальных условий не заданы, поэтому они используют значение по умолчанию 0. Это, вероятно, создает нулевое решение.

Я ввел несколько других значений и использовал m.options.COLDSTART=2, чтобы помочь ему найти начальное решение. Я также использовал промежуточные звенья, чтобы визуализировать любые термины, которые становятся популярными. Я поместил концентрации клеток в миллионы клеток на литр, чтобы облегчить масштабирование.

результаты

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

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

#constants 3L continuous 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)

#Flow, volume and concentration
Fo = 0.001         #feed-rate (L/h)
Fi = 0.001         #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)

# create GEKKO parameter
t = np.linspace(0,50,121)
m.time = t

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

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

e1 = m.Intermediate((mu*XV - Klysis*XD - XT*Fo/V)/1e7)
e2 = m.Intermediate(((mu - Kd)*XV - XV*Fo/V)/1e7)
e3 = m.Intermediate((Kd*XV - Klysis*XD - XV*Fo/V)/1e7)
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.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()
person John Hedengren    schedule 10.12.2019

В конце концов, это не проблема программирования, а проблема чтения уравнений и их правильного перевода.

mu и Kd не являются динамическими переменными, это обычные функции вектора состояния (который тогда имеет только размерность 8). Для таких промежуточных переменных у Gekko есть функция построения m.Intermediate

mu = m.Intermediate((mumax*G*Q*(Ci_star-Ci)) / (Ci_star*(KG+G)*(KQ+Q)*(L/KL + 1)*(A/KA + 1)), name='mu') #growth rate (1/h)
Kd = m.Intermediate(Kdmax*(kmu/(mu+kmu)))    #death rate(1/h)

С этим изменением и начальным значением 5e8 для XT и XV сценарий дает графики ниже, которые показывают, что можно найти в ваших цитируемых статьях.

введите здесь описание изображения

person Lutz Lehmann    schedule 10.12.2019
comment
Я не могу выразить достаточно, насколько я благодарен. Спасибо, доктор Лутц Леманн! - person Felipe Mello; 11.12.2019
comment
Ух ты! Я отправил ответ, но этого не увидел. Хорошая работа! - person John Hedengren; 11.12.2019