#------------------------------------------------------
# Coupled chemical reactions including an equilibrium
# modeled as (1) an ODE and (2) as a DAE
#
# The model describes three chemical species A,B,D:
# subjected to equilibrium reaction D <-> A + B
# D is produced at a constant rate, prod
# B is consumed at 1s-t order rate, r
#------------------------------------------------------
# Dissociation constant
K = 1
# parameters
pars <- c(
ka = 1e6, # forward rate
r = 1,
prod = 0.1)
#-------------------------------------
# Chemical problem formulation 1: ODE
#-------------------------------------
Fun_ODE <- function (t,y,pars)
{
with (as.list(c(y,pars)), {
ra = ka*D # forward rate
rb = ka/K *A*B # backward rate
# rates of changes
dD = -ra + rb + prod
dA = ra - rb
dB = ra - rb - r*B
return(list(dy=c(dA,dB,dD),
CONC=A+B+D))
})
}
#-------------------------------------------------------
# Chemical problem formulation 2: DAE
# 1. get rid of the fast reactions ra and rb by taking
# linear combinations : dD+dA = prod (res1) and
# dB-dA = -r*B (res2)
# 2. In addition, the equilibrium condition (eq) reads:
# as ra=rb : ka*D=ka/K*A*B => K*D=A*B
#-------------------------------------------------------
Res_DAE <- function (t,y,yprime, pars)
{
with (as.list(c(y,yprime,pars)), {
# residuals of lumped rates of changes
res1 = -dD - dA + prod
res2 = -dB + dA - r*B
# and the equilibrium equation
eq = K*D - A*B
return(list(c(res1,res2,eq),
CONC=A+B+D))
})
}
times <- seq(0,100,by=2)
# Initial conc; D is in equilibrium with A,B
y <- c(A=2,B=3,D=2*3/K)
# ODE model solved with daspk
ODE <- as.data.frame(daspk(y=y,times=times,func=Fun_ODE,
parms=pars,atol=1e-10,rtol=1e-10))
# Initial rate of change
dy <- c(dA=0, dB=0, dD=0)
# DAE model solved with daspk
DAE <- as.data.frame(daspk(y=y,dy=dy,times=times,
res=Res_DAE,parms=pars,atol=1e-10,rtol=1e-10))
#------------------------------------------------------
# plotting output
#------------------------------------------------------
opa <- par(mfrow=c(2,2))
for (i in 2:5)
{
plot(ODE$time,ODE[,i],xlab="time",
ylab="conc",main=names(ODE)[i],type="l")
points(DAE$time,DAE[,i],col="red")
}
legend("bottomright",lty=c(1,NA),pch=c(NA,1),
col=c("black","red"),legend=c("ODE","DAE"))
# difference between both implementations:
max(abs(ODE-DAE))
#------------------------------------------------------
# same DAE model, now with the jacobian
#------------------------------------------------------
jacres_DAE <- function (t,y,yprime, pars,cj)
{
with (as.list(c(y,yprime,pars)), {
# res1 = -dD - dA + prod
PD[1,1] <- -1*cj #d(res1)/d(A)-cj*d(res1)/d(dA)
PD[1,2] <- 0 #d(res1)/d(B)-cj*d(res1)/d(dB)
PD[1,3] <- -1*cj #d(res1)/d(D)-cj*d(res1)/d(dD)
# res2 = -dB + dA - r*B
PD[2,1] <- 1*cj
PD[2,2] <- -r -1*cj
PD[2,3] <- 0
# eq = K*D - A*B
PD[3,1] <- -B
PD[3,2] <- -A
PD[3,3] <- K
return(PD)
})
}
PD <- matrix(nc= 3, nr=3,0)
DAE2 <- as.data.frame(daspk(y=y,dy=dy,times=times,
res=Res_DAE, jacres=jacres_DAE, jactype="fullusr",
parms=pars,atol=1e-10,rtol=1e-10))
max(abs(DAE-DAE2))
# See \dynload subdirectory for a FORTRAN implementation of this model
Run the code above in your browser using DataLab