Hacking limnology workshop 2024
Why?
How?
Select / create a working directory
Therein, create a text file ending with “.r” or “.R”)
Copy & paste R code in given order
RStudio
Have file with R code open
Session \(\rightarrow\) Set work. dir. \(\rightarrow\) To source file loc.
Hit source button or press Ctrl + Shift + S
Using a terminal
Rscript --vanilla myscript.r
Graphics will appear in Rplots.pdf
Do NOT run scripts line by line but execute all statements from top of the file!
Inspect intermediate results by print()
& stop()
To disable the stop()
, just put a #
in front.
Conventional writing
\[ \begin{align} \tfrac{d}{dt} B & = & & \mu \cdot B \cdot \left( \tfrac{R}{R+h} \right) & - \tfrac{1}{\tau} \cdot B \\ \tfrac{d}{dt} R & = & -\tfrac{1}{y} \cdot & \mu \cdot B \cdot \left( \tfrac{R}{R+h} \right) & + \tfrac{1}{\tau} \cdot (R_{in} - R) \end{align} \]
… and in vector format
\[ \begin{align} \begin{bmatrix} \tfrac{d}{dt}B \\ \tfrac{d}{dt}R \end{bmatrix} ^T & = & \begin{bmatrix} \mu \cdot B \cdot \tfrac{R}{R+h} \\ \tfrac{1}{\tau} \end{bmatrix} ^T & \cdot & \begin{bmatrix} 1, & -\tfrac{1}{y} \\ -B, & R_{in}-R \end{bmatrix} \end{align} \]
\[ \begin{align} \begin{bmatrix} \tfrac{d}{dt}B \\ \tfrac{d}{dt}R \end{bmatrix} ^T & = & \color{teal}{ \begin{bmatrix} \mu \cdot B \cdot \tfrac{R}{R+h} \\ \tfrac{1}{\tau} \end{bmatrix} ^T } & \cdot & \color{brown}{ \begin{bmatrix} 1, & -\tfrac{1}{y} \\ -B, & R_{in}-R \end{bmatrix} } \end{align} \]
Less coding, more time for science behind
Language-independence & documentation
Interoperability and reusability
The version available on CRAN is often outdated
Please install the latest development version to profit from recent updates and fixes (see below)
Simplest approach to a rodeo-based model
Learn how to extend an existing model
Learn how to update model inputs
Understand the use of dynamic forcings
\[ \begin{align} \tfrac{d}{dt} B & = & & \color{teal}{\mu \cdot B \cdot \left( \tfrac{R}{R+h} \right)} & \color{blue}{- \tfrac{1}{\tau} \cdot B} \\ \tfrac{d}{dt} R & = & \color{red}{-\tfrac{1}{y}} \cdot & \color{teal}{\mu \cdot B \cdot \left( \tfrac{R}{R+h} \right)} & \color{blue}{+ \tfrac{1}{\tau} \cdot (R_{in} - R)} \end{align} \]
B: Bacteria
R: Resource
Resource limited growth
Yield term
Renewal of medium (mass balance)
Choose / create a working directory
Download the workbook reactor_eqns_v1.xlsx into that directory
Inspect contents using spreadsheet software
name | unit | description | default |
---|---|---|---|
B | cells / ml | Bacterial density | 100000 |
R | mg / ml | Concentration of resource | 21 |
name | unit | description | default |
---|---|---|---|
tau | h | residence time of reactor | 2.4e+01 |
mu | 1 / h | growth rate constant | 1.0e+00 |
y | cells / mg | yield coefficient | 1.0e+07 |
h | mg / ml | half saturation const. f. resource | 2.1e+00 |
Rin | mg / ml | resource conc. in inflow | 2.1e+01 |
\[ \begin{align} \tfrac{d}{dt} B & = & & \color{teal}{\mu \cdot B \cdot \left( \tfrac{R}{R+h} \right)} & \color{blue}{- \tfrac{1}{\tau} \cdot B} \\ \tfrac{d}{dt} R & = & \color{red}{-\tfrac{1}{y}} \cdot & \color{teal}{\mu \cdot B \cdot \left( \tfrac{R}{R+h} \right)} & \color{blue}{+ \tfrac{1}{\tau} \cdot (R_{in} - R)} \end{align} \]
name | rate | B | R |
---|---|---|---|
growth | mu * B * R / (R + h) | 1 | -1 / y |
renewal | 1 / tau | -B | Rin - R |
Build model object
Set / check input data
Perform integration
Inspect results
Go to the folder with the downloaded workbook
Create & open a new script file there (e.g. “reactor.r”)
Adjust R’s working directory (if needed)
Usual R code
x
is a variable passed to function fun
Using R6 classes
x
is an object having a member function fun
time B R growth renewal
[1,] 0 100000.0 21.00000 90909.09 0.04166667
[2,] 1 238071.7 20.98578 216415.47 0.04166667
[3,] 2 566731.0 20.95253 515103.91 0.04166667
time B R growth renewal
[23,] 22 209127187 0.09128602 8711957 0.04166667
[24,] 23 209125546 0.09128677 8711957 0.04166667
[25,] 24 209123971 0.09128748 8711957 0.04166667
Add drug resistant bacteria as new state variable \(BR\)
Add antibiotic as new state variable \(A\)
Account for input of antibiotic (\(Ain\)) & removal of \(A\)
Account for growth of \(BR\) and the “cost” of resistance implying \(\mu(BR) < \mu(B)\)
Account for drug susceptibility of strain \(B\)
Consider removal of \(BR\) during transfer
Usual procedure
Make a copy of “reactor_eqns_v1.xlsx”
Save as “reactor_eqns_v2.xlsx”
Implement the modifications
Fallback
name | unit | description | default |
---|---|---|---|
B | cells / ml | Susceptible bacterial strain | 100000 |
BR | cells / ml | Drug-resistant bacterial strain | 100000 |
R | mg / ml | Concentration of resource | 21 |
A | mg / ml | Concentration of antibiotic | 0 |
name | unit | description | default |
---|---|---|---|
tau | h | residence time of reactor | 2.4e+01 |
mu | 1 / h | growth rate const. of B | 1.0e+00 |
cost | - | cost of drug resistance in BR | 1.0e-01 |
y | cells / mg | yield coefficient | 1.0e+07 |
h | mg / ml | half saturation const. f. resource | 2.1e+00 |
Rin | mg / ml | resource conc. in inflow | 2.1e+01 |
Ain | mg / ml | drug conc. in inflow | 0.0e+00 |
mic | mg / ml | minimum inhibitory drug conc. | 5.0e+00 |
name | rate | B | BR | R | A |
---|---|---|---|---|---|
growth_B | mu * B * R / (R + h) * max(0, 1 - A / mic) | 1 | 0 | -1 / y | 0 |
growth_BR | mu * (1 - cost) * BR * R / (R + h) | 0 | 1 | -1 / y | 0 |
renewal | 1 / tau | -B | -BR | Rin - R | Ain - A |
We reuse the previously defined function
Option 1: Modify defaults in worksheet
Option 2: Modify through rodeo methods
Declare Ain
as a function, not as a parameter
Replace Ain
by Ain(time)
in expressions (time
is a reserved word)
Implement the function in R (or Fortran)
Usual procedure
Make a copy of “reactor_eqns_v2.xlsx”
Save as “reactor_eqns_v3.xlsx”
Modify as necessary
Fallback
name | unit | description |
---|---|---|
max | - | intrinsic |
Ain | mg / ml | drug conc. in inflow |
name | unit | description | default |
---|---|---|---|
tau | h | residence time of reactor | 2.4e+01 |
mu | 1 / h | growth rate const. of B | 1.0e+00 |
cost | - | cost of drug resistance in BR | 1.0e-01 |
y | cells / mg | yield coefficient | 1.0e+07 |
h | mg / ml | half saturation const. f. resource | 2.1e+00 |
Rin | mg / ml | resource conc. in inflow | 2.1e+01 |
mic | mg / ml | minimum inhibitory drug conc. | 5.0e+00 |
name | rate | B | BR | R | A |
---|---|---|---|---|---|
growth_B | mu * B * R / (R + h) * max(0, 1 - A / mic) | 1 | 0 | -1 / y | 0 |
growth_BR | mu * (1 - cost) * BR * R / (R + h) | 0 | 1 | -1 / y | 0 |
renewal | 1 / tau | -B | -BR | Rin - R | Ain(time) - A |
\(\rightarrow\) Antibiotic added from midnight till 6 am
Solution of PDE problems with ODE strategies
Use of Fortran for improved performance
Allows for multiple sources & time-varying inputs
Treats bacteria as a dynamic state variable
Purely advective transport
Neglects hyporheic or benthic processes
Other shortcomings …
For lateral & vertical mixing, a 1D model may fit
\[ \frac{\partial c}{\partial t} = \color{teal}{-u \cdot \frac{\partial c}{\partial x}} + \color{blue}{\frac{\partial}{\partial x} \left( D \cdot \frac{\partial c}{\partial x} \right)} + \color{red}{r(c, p)} + \color{grey}{s} \]
\(\partial c / \partial t\) Change in concentration
\(\partial c / \partial x\) Longitudinal gradient
Advection
Dispersion
Turnover
Sources
Original advection term
\[\frac{\partial c}{\partial t} = -u \cdot \frac{\partial c}{\partial x}\]
Semi-discretized
\[\frac{\partial c_i}{\partial t} = -u \cdot \frac{c_i - c_{i-1}}{x_i - x_{i-1}}\]
\[\frac{\partial c_{\color{red}i}}{\partial t} = -u \cdot \frac{c_{\color{red}{i}} - c_{\color{blue}{i-1}}}{x_{\color{red}{i}} - x_{\color{blue}{i-1}}}\]
Semi-discretization transforms PDE into a set of ODE
The ODE for any cell \(\color{red}{i}\) references the state of neighboring cells (here: \(\color{blue}{i-1}\))
ODE are simultaneous across cells (at least)
Backward, forward, central schemes
Schemes differ w.r.t. stability, accuracy, and numerical dispersion
See the documentation of function ‘fiadeiro’ in the ReacTran package
Choose / create a working directory
Download the workbook river_eqns_v1.xlsx into that directory
Inspect contents using spreadsheet software
Note that the ODE are specified for a single cell only, no matter how many cells the model consists of.
name | unit | description | default |
---|---|---|---|
B | mol / m3 | bacteria (as carbon) | 0.0003 |
S | mol / m3 | substrate (as carbon) | 0.0000 |
X | mol / m3 | dissolved oxygen | 0.3125 |
name | unit | description |
---|---|---|
mu | 1 / hour | growth rate constant |
hs | mol S / m3 | controls limitation of growth by S |
hx | mol X / m3 | controls limitation of growth by X |
f | mol B / mol S | carbon conversion efficiency |
name | unit | description |
---|---|---|
q | m3 / h | flow rate |
a | m2 | cross-section area |
h | m | sub-section length |
name | unit | description |
---|---|---|
Bin | mol / m3 | bacteria in inflow |
Sin | mol / m3 | substrate in inflow |
Xin | mol / m3 | oxygen in inflow |
Sload | mol / h | lateral input of substrate |
Xsat | mol / m3 | oxygen saturation level |
k | 1 / hour | rate constant of aeration |
name | unit | description |
---|---|---|
is_upstr | 0 or 1 | mask to select upstr. boundary |
is_centr | 0 or 1 | mask to select central cells |
has_src | 0 or 1 | mask to assign external source |
name | rate | B | S | X |
---|---|---|---|---|
growth | mu * B * S/(S+hs) * X/(X+hx) | 1 | -1/f | 1 - 1/f |
aeration | k * (Xsat - X) | 0 | 0 | 1 |
adv_in | is_centr * q / (a * h) | left(B) | left(S) | left(X) |
adv_out | q / (a* h) | -B | -S | -X |
bc_upstr | is_upstr * q / (a * h) | Bin | Sin | Xin |
bc_lateral | has_src / (a * h) | 0 | Sload | 0 |
columns ‘unit’ and ‘description’ omitted for clarity
name | rate | B | S | X |
---|---|---|---|---|
growth | mu * B * S/(S+hs) * X/(X+hx) | 1 | -1/f | 1 - 1/f |
aeration | k * (Xsat - X) | 0 | 0 | 1 |
adv_in | is_centr * q / (a * h) | left(B) | left(S) | left(X) |
adv_out | q / (a* h) | -B | -S | -X |
bc_upstr | is_upstr * q / (a * h) | Bin | Sin | Xin |
bc_lateral | has_src / (a * h) | 0 | Sload | 0 |
red: pseudo functions pointing to adjacent cells
blue: 0/1 masks to (de)activate processes in particular cells
Build model object
Set input data
Perform integration
Inspect results
Preparation of parameters
Preparation of parameters (cont.)
Similar procedure for initial values
time B.1 B.2 B.3 B.4 B.5 B.6
[1,] 0 3e-04 3e-04 3e-04 3e-04 3e-04 3e-04
[2,] 12 3e-04 3e-04 3e-04 3e-04 3e-04 3e-04
[3,] 24 3e-04 3e-04 3e-04 3e-04 3e-04 3e-04
[4,] 48 3e-04 3e-04 3e-04 3e-04 3e-04 3e-04
[5,] 96 3e-04 3e-04 3e-04 3e-04 3e-04 3e-04
[6,] 120 3e-04 3e-04 3e-04 3e-04 3e-04 3e-04
# dimension 1: time
# dimension 2: cell
# dimension 3: variables and process rates
a <- array(x[,colnames(x) != "time"],
dim=c(nrow(x), ncells, sum(m$lenVars(),
m$lenPros())),
dimnames=list(time=x[,"time"], cell=1:ncells,
variable=c(m$namesVars(), m$namesPros()))
)
print(a["48", "10", "X"])
[1] 0.3122861
plot_longit <- function(v, a, leg) {
nc <- dim(a)[names(dimnames(a)) == "cell"]
plot(c(1, nc), range(a[,,v]),
type="n", xlab="Cell", ylab="")
times <- dimnames(a)[["time"]]
for (t in times)
lines(1:nc, a[t, , v], col=match(t, times))
if (leg) legend("right", bty="n", title="Time",
lty=1, col=1:length(times), legend=times)
abline(v=which(p[,"has_src"] == 1), lty=2)
mtext(side=3, paste("Var.:",v), cex=par("cex"))
}
ODEs may involve complicated expressions
These can be wrapped into functions for clarity
Let’s try with the Monod term …
Original expression
\[X / (X + hx)\]
Wrapped as a function
\[monod(X, hx)\]
Declare the new function
Use the new function in equations
Implement the function (now in Fortran)
Rebuild / recompile the model
Make a copy of the workbook “river_eqns_v1.xlsx” and save as “river_eqns_v2.xlsx”
On sheet “declarations”, declare the new function:
type | name | unit | description |
---|---|---|---|
function | monod | - | Monod model |
On worksheet “equations” …
replace
replace
\(S / (S + hs)\)
\(X / (X + hx)\)
by
by
\(monod(S, hs)\)
\(monod(X, hx)\)
Create a file “river_func.f95” and enter the following piece of Fortran
Rebuild / compile the model object using …
the name of the modified workbook
the “sources” argument to pass Fortran code
Can be solved by MOL
Pseudo-functions ‘left’ and ‘right’ give access to quantities of neighboring cells
Only 1D models are currently supported
Use Fortran for performance
Use shown examples as guidance
Look at the package vignette for more examples
Provide me with a fully self-contained minimum working example, including …
Brief description of problem
Worksheets as tab-delimited text files (not .xlsx)
R / Fortran code