Skip to content

Commit 8e5416c

Browse files
committed
added BA example update
1 parent 02a5078 commit 8e5416c

File tree

1 file changed

+345
-0
lines changed

1 file changed

+345
-0
lines changed

inst/examples/BA_transport.R

Lines changed: 345 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
## Load Libraries -----
2+
rm(list=ls())
3+
library(dMod)
4+
library(dplyr)
5+
library(ggplot2)
6+
theme_set(theme_dMod())
7+
setwd(tempdir()) # change if you want to keep the files that dMod creates internally
8+
set.seed(5555)
9+
10+
## Set up the ODE model -----------------------------------------------------------------------
11+
12+
# Define via reactions
13+
14+
reactions <- eqnlist() %>%
15+
addReaction("TCA_buffer", "TCA_cell", rate = "import*TCA_buffer", description = "Uptake") %>%
16+
addReaction("TCA_cell", "TCA_buffer", rate = "export_sinus*TCA_cell", description = "Sinusoidal export") %>%
17+
addReaction("TCA_cell", "TCA_cana", rate = "export_cana*TCA_cell", description = "Canalicular export") %>%
18+
addReaction("TCA_cana", "TCA_buffer", rate = "reflux*TCA_cana", description = "Reflux into the buffer")
19+
20+
# Define via ODE system (equivalent)
21+
22+
# f <- eqnvec(TCA_buffer = "-import * TCA_buffer + export_sinus * TCA_cell + reflux * TCA_cana",
23+
# TCA_cana = "export_cana * TCA_cell - reflux * TCA_cana",
24+
# TCA_cell = "import * TCA_buffer - export_sinus * TCA_cell - export_cana * TCA_cell")
25+
26+
# Translate reactions into ODE model object
27+
mymodel <- odemodel(reactions, modelname = "bamodel", compile = T)
28+
29+
30+
# Generate trajectories for the default condition
31+
x <- Xs(mymodel, condition = NULL)
32+
33+
34+
# For demonstration define parameters (initials and dynamic parameters)
35+
pars <- c(reflux = 0.1, TCA_buffer = 1, TCA_cell = 0, TCA_cana = 0, import = 0.2, export_sinus = 0.2, export_cana = 0.04)
36+
37+
38+
# Plot trjectories
39+
times <- seq(0, 50, 0.01)
40+
out <- x(times, pars)
41+
plot(out) # Note this is a ggplot object
42+
43+
# Define observables buffer and cellular
44+
observables <- eqnvec(buffer = "s*TCA_buffer", cellular = "s*(TCA_cana + TCA_cell)")
45+
g <- Y(observables, f = x, condition = NULL, compile = TRUE, modelname = "obsfn")
46+
47+
48+
49+
## Simulate data -------------------------------------------------------------------------------------------------------------
50+
51+
# Fix parameters to steady state values (calculated on paper / by hand), replace buffer -> TCA_buffer = 0
52+
pars["TCA_cell"] <- 0.3846154
53+
pars["TCA_cana"] <- 0.1538462
54+
pars["TCA_buffer"] <- 0
55+
pars["s"] <- 1e3
56+
57+
# Simulate trajectories
58+
# Evaluate the expression separately
59+
out <- (g * x)(times, pars, conditions = "closed")
60+
plot(out)
61+
62+
63+
64+
65+
66+
# simulate data for time points
67+
timesD <- c(0.1, 1, 3, 7, 11, 15, 20, 41)
68+
datasheet <- subset(as.data.frame(out), time %in% timesD & name %in% names(observables))
69+
datasheet <- within(datasheet, {
70+
sigma <- 0.1*value
71+
value <- rnorm(length(value), value, sigma)
72+
})
73+
data <- as.datalist(datasheet)
74+
plot(out, data)
75+
76+
77+
# Define parameter transformations using define(), insert() and branch(). Old function repar also avaiable!
78+
innerpars <- getParameters(x,g)
79+
trafo <- NULL %>%
80+
define("x~x", x = innerpars) %>% # identity
81+
define("TCA_buffer~0") %>%
82+
insert("x~exp(log(10)*x)", x = .currentSymbols)
83+
84+
85+
# # Explicit trafo
86+
# trafo <- eqnvec(TCA_buffer = "0",
87+
# TCA_cell = "exp(log(10)*TCA_cell)",
88+
# TCA_cana = "exp(log(10)*TCA_cana)",
89+
# import = "exp(log(10)*import)",
90+
# export_sinus = "exp(log(10)*export_sinus)",
91+
# export_cana = "exp(log(10)*export_cana)",
92+
# reflux = "exp(log(10)*reflux)",
93+
# s = "exp(log(10)*s)")
94+
95+
96+
p <- P(trafo, condition = "closed")
97+
# Set every parameter to -1 in the log-space
98+
outerpars <- getParameters(p)
99+
pouter <- structure(rep(-1, length(outerpars)), names = outerpars)
100+
plot((g*x*p)(times, pouter),data)
101+
102+
103+
## Use simulate data to calibrate outer model parameters -------------------------------------------------------------
104+
# # One Fit
105+
obj <- normL2(data, g * x * p) + constraintL2(pouter, sigma = 20)
106+
107+
# Fit on time (starting from pouter)
108+
myfit_1 <- trust(obj, pouter, rinit = 0.1, rmax = 5)
109+
mypred_1 <- (g * x * p)(times, myfit_1$argument)
110+
plot(mypred_1, data)
111+
112+
## Handling different experimental conditions
113+
114+
# Define reflux and open condition according to "Dynamic Modelling in R p. 19-20"
115+
pars["reflux"] <- 1e3
116+
out <- (g * x)(times, pars, conditions = "open")
117+
datasheet <- subset(as.data.frame(out), time %in% timesD & name %in% names(observables))
118+
datasheet <- within(datasheet, {
119+
sigma <- 0.1*value
120+
value <- rnorm(length(value), value, sigma)
121+
})
122+
data <- data + as.datalist(datasheet)
123+
plotData(data)
124+
125+
# Parameter Trafo, usage of "+" operator for trafo functions (output of P())
126+
trafo <- getEquations(p, conditions = "closed")
127+
trafo["reflux"] <- "exp(log(10)*reflux_open)"
128+
p <- p + P(trafo, condition = "open")
129+
130+
outerpars <- getParameters(p)
131+
pouter <- structure(rep(-1, length(outerpars)), names = outerpars)
132+
plot((g*x*p)(times, pouter),data)
133+
134+
135+
# Objective function
136+
obj <- normL2(data, g * x * p) + constraintL2(pouter, sigma = 20)
137+
138+
# Evaluation of obj at pouter
139+
obj(pouter)
140+
141+
142+
# Fit 50 times, sample with sd=4 around pouter
143+
out_frame <- mstrust(obj, pouter, sd = 4, studyname = "bamodel", cores=8, fits=50, iterlim = 200)
144+
out_frame <- as.parframe(out_frame)
145+
plotValues(out_frame) # Show "Waterfall" plot
146+
plotPars(out_frame) # Show parameter plot
147+
bestfit <- as.parvec(out_frame)
148+
149+
150+
# Plot predictions along data
151+
plot((g * x * p)(times, bestfit), data)
152+
153+
# Plot sensis
154+
plot(getDerivs((g * x * p)(times, bestfit)))
155+
156+
# Calculate Parameter Profiles and plot different contributions (for identifiablility only "data" is of interest)
157+
profiles <- profile(obj, bestfit, names(bestfit), method = "optimize", cores = 12)
158+
plotProfile(profiles)
159+
plotProfile(profiles,mode %in% c("data", "prior"))
160+
plotPaths(profiles, whichPar = "TCA_cana")
161+
plotPaths(profiles, whichPar = "export_cana")
162+
plotPaths(profiles, whichPar = "s")
163+
164+
# Tighten model assumptions with steady state constraint
165+
pSS <- NULL
166+
equations <- getEquations(p) # getEquations retrieves the "trafo" from p
167+
conditions <- names(equations)
168+
169+
# Set up steady state constraint
170+
for (n in conditions) {
171+
equations[[n]]["TCA_cana"] <- "exp(log(10)*export_cana)*exp(log(10)*TCA_cell)/exp(log(10)*reflux)"
172+
pSS <- pSS + P(equations[[n]], condition = n)
173+
}
174+
outerpars <- getParameters(pSS)
175+
pouter <- structure(rep(-1, length(outerpars)), names = outerpars)
176+
plot((g*x*pSS)(times, pouter),data)
177+
178+
# Objective function
179+
obj <- normL2(data, g * x * pSS, times = times, attr.name = "data") + constraintL2(pouter, sigma = 5, attr.name = "prior")
180+
181+
# Fit 50 times again
182+
out_frame <- mstrust(obj,pouter,studyname = "bamodel", cores=10,fits=50)
183+
out_frame <- as.parframe(out_frame)
184+
plotValues(out_frame) # Show "Waterfall" plot
185+
plotPars(out_frame) # Show parameter plot
186+
bestfit <- as.parvec(out_frame)
187+
plot((g * x * pSS)(times, bestfit), data)
188+
189+
190+
# Calculate Parameter Profiles
191+
profiles <- profile(obj, bestfit, names(bestfit), cores = 10, limits = c(lower = -10, upper = 10),
192+
stepControl = list(stepsize = 1e-4, min = 1e-4, max = Inf, atol = 1e-2, rtol = 1e-2, limit = 100, stop = "data"))
193+
plotProfile(profiles,mode %in% c("data", "prior"))
194+
plotPaths(profiles, whichPar = "s")
195+
196+
197+
# s and initial value TCA_cell render model structurally non identifiable
198+
# --> fix s to 1 (on log scale to 0)
199+
200+
trafo <- getEquations(pSS)
201+
trafo <- repar("x~0", x = "s", trafo)
202+
p <- P(trafo)
203+
204+
205+
# Objective function
206+
outerpars <- getParameters(p)
207+
pouter <- structure(rep(-1, length(outerpars)), names = outerpars)
208+
obj <- normL2(data, g * x * p, times = times, attr.name = "data") + constraintL2(pouter, sigma = 20, attr.name = "prior")
209+
210+
# Fit 50 times again
211+
out_frame <- mstrust(obj,pouter,studyname = "bamodel", cores=10,fits=50)
212+
out_frame <- as.parframe(out_frame)
213+
plotValues(out_frame) # Show "Waterfall" plot
214+
plotPars(out_frame) # Show parameter plot
215+
bestfit <- as.parvec(out_frame)
216+
plot((g * x * p)(times, bestfit), data)
217+
218+
# remove the prior
219+
obj <- normL2(data, g * x * p, times = times, attr.name = "data")
220+
221+
# refit
222+
refit <- trust(obj, bestfit, rinit = 1, rmax = 10)
223+
plot((g * x * p)(times, refit$argument), data)
224+
225+
profiles <- profile(obj, refit$argument, names(refit$argument), cores = 10, limits = c(lower = -5, upper = 5), method = "optimize",
226+
stepControl = list(stop = "data"))
227+
plotProfile(profiles, mode == "data")
228+
plotPaths(profiles, whichPar = "reflux_open")
229+
230+
# Note: The parameter "reflux_open" is still practical non-identifiable
231+
# The profile is open to the left -> A possible reduction of the model would be the assumption of an immediate reflux, i.e. the limit case reflux_open -> infinity
232+
# An pragmatic but ugly way to circumvent the reformulation of the ODE system is to fix the reflux_open parameter to a high value. E.g. 1e3
233+
234+
# One could also check the models ability to produce reliable predictions, by the calculation of prediction uncertainty with profile likelihood
235+
# The calculation of prediction confidence intervals is done at next
236+
237+
## Prediction uncertainty taken from validation profile --------------------------------------------------------------------------
238+
239+
# choose sigma below 1 percent of the prediction in order to pull the prediction strongly towards d1
240+
obj.validation <- normL2(data, g * x * p, times = times, attr.name = "data") +
241+
datapointL2(name = "TCA_cell", time = 10, value = "v", sigma = 1, attr.name = "validation", condition = "closed")
242+
243+
# If sigma is not known, and you therefore decide to calculate prediction confidence intervals, just choose a very small sigma, in order to "pull strongly" on the trajectory
244+
245+
# refit
246+
myfit <- trust(obj.validation, parinit = c(v = 180, bestfit), rinit = 1, rmax = 10, iterlim = 1000)
247+
248+
# Calculate profile
249+
validation_profile <- profile(obj.validation, myfit$argument, "v", cores = 4, method = "optimize",
250+
optControl = list(rinit = .1, rmax = 10, iterlim = 100))
251+
252+
253+
# plotProfile(validation_profile) # This also plos the prediction colums, which is a bug in the code.
254+
plotProfile(validation_profile, mode %in% c("validation", "data")) # Plots only the two contributions validation and data, along with the sum (total)
255+
# Is the contribution of validation small?? # If yes: The total aligns with a prediction profile
256+
257+
# Confidence Interval of the prediction
258+
confint(validation_profile, val.column = "value")
259+
260+
261+
262+
## Prediction band (prediction uncertainty for several time points) --------------------------------------------------------------
263+
# Here we calculate a prediction CI for different timepoints. In the end we interpolate to a "prediction band"
264+
prediction_band <- do.call(rbind, lapply(seq(10, 50, 10), function(t) {
265+
266+
cat("Computing prediction profile for t =", t, "\n")
267+
268+
obj.validation <- normL2(data, g * x * p, times = times, attr.name = "data") +
269+
datapointL2(name = "TCA_cell", time = t, value = "v", sigma = 1, attr.name = "validation", condition = "closed")
270+
271+
refit <- trust(obj.validation, parinit = c(v = 180, bestfit), rinit = 1, rmax = 10, iterlim = 1000)
272+
273+
profile_prediction <- profile(obj.validation, myfit$argument, "v", cores = 4, method = "optimize")
274+
275+
d1 <- confint(profile_prediction, val.column = "value")
276+
277+
# Output
278+
data.frame(time = t, condition = "closed", name = "TCA_cell", d1[-1])
279+
280+
}))
281+
282+
prediction <- (g * x * p)(times, refit$argument) %>%
283+
as.data.frame()
284+
285+
286+
prediction_band_spline <- data.frame(
287+
time = prediction$time[prediction$time>=10],
288+
value = prediction$value[prediction$time>=10],
289+
condition = "closed",
290+
name = "TCA_cell",
291+
lower = spline(prediction_band$time, prediction_band$lower, xout = prediction$time[prediction$time>=10])$y,
292+
upper = spline(prediction_band$time, prediction_band$upper, xout = prediction$time[prediction$time>=10])$y
293+
)
294+
295+
# Create the ggplot
296+
ggplot(prediction, aes(x = time, y = value, color = condition)) +
297+
geom_line() + # Line connecting the points for each condition
298+
geom_ribbon(data = prediction_band_spline, aes(x = time, ymin = lower, ymax = upper, fill = condition),
299+
lty = 0, alpha = .3, show.legend = F) + # Show ribbon in the legend
300+
geom_point(data = prediction_band, aes(x = time, y = lower, color = condition), shape = 4, show.legend = F) +
301+
geom_point(data = prediction_band, aes(x = time, y = upper, color = condition), shape = 4, show.legend = F) +
302+
facet_wrap(~ name, scales = "free_y") + # Facet by 'name' column
303+
labs(
304+
x = "Time",
305+
y = "Value",
306+
color = "Condition"
307+
) +
308+
dMod::theme_dMod() + # Apply dMod theme
309+
dMod::scale_color_dMod() + # Apply dMod color scale to lines
310+
dMod::scale_fill_dMod() # Apply the same color scale to the fill
311+
312+
313+
# ## Alternative implementation of the Steady state via implicit parameter transformation of the steady state -----------------------------------------------
314+
#
315+
# # Redefine reactions in order to control the standard and open condition by events
316+
# reactions <- eqnlist() %>%
317+
# addReaction("TCA_buffer", "TCA_cell", rate = "import*TCA_buffer", description = "Uptake")%>%
318+
# addReaction("TCA_cell", "TCA_buffer", rate = "export_sinus*TCA_cell", description = "Sinusoidal export")%>%
319+
# addReaction("TCA_cell", "TCA_cana", rate = "export_cana*TCA_cell", description = "Canalicular export")%>%
320+
# addReaction("TCA_cana", "TCA_buffer", rate = "(reflux*(1-switch) + reflux_open*switch)*TCA_cana", description = "Reflux into the buffer")%>%
321+
# addReaction("0", "switch", rate = "0", description = "Create a switch")
322+
#
323+
# events <- NULL
324+
# events <- addEvent(events, var = "TCA_buffer", time = 0, value = 0)
325+
# events <- addEvent(events, var = "switch" , time = 0, value = "OnOff")
326+
# mymodel <- odemodel(reactions, modelname = "bamodel2", events = events)
327+
# x <- Xs(mymodel)
328+
#
329+
#
330+
#
331+
# # Replace one reaction with a analytical expression for the conserved quantity: TCR_tot
332+
# f <- as.eqnvec(reactions)[c("TCA_buffer", "TCA_cana", "TCA_cell")]
333+
# f["TCA_cell"] <- "TCA_buffer + TCA_cana + TCA_cell - TCA_tot"
334+
# pSS <- P(f, method = "implicit", compile = TRUE, modelname = "pfn")
335+
#
336+
# observables <- eqnvec(buffer = "s*TCA_buffer", cellular = "s*(TCA_cana + TCA_cell)")
337+
#
338+
# innerpars <- unique(c(getParameters(mymodel), getSymbols(observables), getSymbols(f)))
339+
# trafo <- repar("x~x" , x = innerpars)
340+
# trafo <- repar("x~0" , x = reactions$states, trafo)
341+
#
342+
# trafo <- repar("x~exp(log(10)*x)", x = setdiff(innerpars, "OnOff"), trafo)
343+
# p <- P(repar("OnOff~0", trafo), condition = "closed") + P(repar("OnOff~1", trafo), condition = "open")
344+
# g <- Y(observables, f = x, compile = TRUE, modelname = "obsfn2")
345+
#

0 commit comments

Comments
 (0)