library("rodeo")
# runs a dynamic simulation
dyn <- function(
file, # workbook defining the ODE model
update=NULL, # named vector to alter parameters, initial v.
times=seq(0, 240, 1), # requested prediction times
graphics=TRUE, # shall results be plotted?
maxcols=3, # max. columns in plot layout
trans=function(x){x^0.25} # function transforming y-axis in plots
) {
m <- buildFromWorkbook(file) # load model, including default values
if (!is.null(update)) { # possibly overwrite defaults
if (is.null(names(update)))
stop("cannot update items without names")
if (!all(names(update) %in% c(m$namesVars(), m$namesPars())))
stop("cannot update value(s) of unknown items")
change <- function(m, x, pars=TRUE) {
what <- if (pars) "Pars" else "Vars"
x <- x[names(x) %in% do.call(m[[paste0("names",what)]], list())]
if (length(x) > 0) {
z <- do.call(m[[paste0("get",what)]], list())
z[names(x)] <- x
do.call(m[[paste0("set",what)]], list(z))
}
m
}
m <- change(m, update[m$namesVars()], pars=FALSE)
m <- change(m, update[m$namesPars()], pars=TRUE)
}
x <- m$dynamics(times) # run dynamic simulation
if (graphics) {
nc <- min(m$lenVars(), maxcols)
nr <- ceiling(m$lenVars() / nc)
par(mfrow=c(nr,nc)) # plot with custom layout
par(cex=1)
for (v in m$namesVars()) {
plot(x[,"time"], trans(x[,v]), type="l", bty="L",
yaxt="n", xlab="time", ylab=v)
yt <- c(0,10^(-6:9))
axis(2, at=trans(yt), labels=yt)
}
par(mfrow=c(1,1))
invisible(NULL)
} else {
x[nrow(x), m$namesVars()] # return final state
}
}
# computes a steady-state solution
std <- function(
file, # workbook defining the ODE model
update=NULL, # named vector to alter parameters, initial values
tini=5000, # assumed time to steady state tried initially
tmul=4, # factor applied to tmax in every new iteration
abstol=1e6, # stopping criterion, max. abs. change in any state v.
maxiter=10 # max. number of iterations (emergency exit)
) {
t <- tini
i <- 0
old <- dyn(file, update, times=c(0, t/2), graphics=FALSE)
while (i < maxiter) {
new <- dyn(file, update, times=c(0, t), graphics=FALSE)
if (max(abs(old - new)) < abstol) {
break
} else {
t <- t * tmul
old <- new
i <- i + 1
}
}
if (i == maxiter) stop("no steady state found in 'maxiter' iterations")
new
}System analysis: Competition and coexistence
1 Introduction
According to classical theory, the key to co-existence is the presence of contrasting ecological niches. But even in seemingly simply-structured environments like the pelagic zone, we find substantial species diversity.
Does that diversity actually reflect stable co-existence between species? Or is it rather a case of mere co-presence of unequal competitors? In fact, this question is very hard to answer with field studies. First of all, investigation time is usually limited (no time to study long-term competition, unless its about microbes). Second, field observations are always contaminated with noise, reflecting various random events.
Therefore, models are a quite useful means to study the possible mechanisms and necessary conditions for co-existence. In particular, simulations can easily be run over very long periods of time such that even very minor selective advantages lead to ultimate outcompetition.
In spite of the apparent simplicity of models, the interpretation of results may be challenging! In this lecture, we focus on a small selection of simple cases and scenarios. This selection is not exhaustive but a reasonable starting point for understanding more complex situations.
To make the topic digestible at all, we build on technology for ODE-based modeling introduced earlier, namely the rodeo package.
2 General study system
We consider a continuous flow stirred tank reactor (CFSTR) again, so as to avoid all unnecessary complexity. In contrast to earlier lectures, we now consider two species sharing space and the single unique resource pumped into the reactor.
In a series of scenarios, we play with the particular capabilities of the species and the external forcings to see if and when stable coexistence can be achieved.
3 Technical basis of subsequent analyses
3.1 Concept
It is assumed that the model specification is in a workbook in .xlsx format with the two worksheets declarations and equations such that it can be imported by the buildFromWorkbook function of the rodeo package.
Two make the study of scenarios more convenient, I wrote the two wrapper functions below. They can be used to instantly run a dynamic simulation (dyn()) or steady-state computation (std()), including visualization, with minimum inputs. Nevertheless, the functions are flexible enough to allow parameters or initial values to be adjusted using their update argument.
To make use of these functions in your own R script, just copy the code and paste it into an empty text file. Let this file be called utility_functions.r, for example. Then, you would make the functions available by including the following line at the top of your script like so:
source("utility_functions.r")3.2 Demonstration of the dyn and std function
For the purpose of demonstration, we consider a single-species case again: a hypothetical species A is growing on a resource R. Growth of the species and flushing of the reactor are the only processes considered (Figure 1).
| type | name | unit | description | default |
|---|---|---|---|---|
| variable | R | mg / mL | resource concentration | 0.0e+00 |
| variable | A | cells / mL | abundance of organisms | 1.0e+05 |
| parameter | mu | 1 / h | max. growth rate | 2.0e+00 |
| parameter | h | mg / mL | half saturation const. | 1.8e-01 |
| parameter | y | cells / mg | yield of A growing on R | 1.0e+06 |
| parameter | q | mL / h | flow rate | 1.0e+01 |
| parameter | v | mL | reactor volume | 1.0e+02 |
| parameter | Rin | mg / mL | input resource conc. | 1.5e+01 |
| parameter | Ain | cells / mL | abundance of A in input | 0.0e+00 |
Let the model be available as a workbook singleSpecies.xlsx with the contents of Table 1 in sheet declarations and the contents of Table 2 on sheet equations.
| name | unit | description | rate | A | R |
|---|---|---|---|---|---|
| flow | 1 / h | im-/export | q / v | Ain-A | Rin - R |
| growth | cells / mL / h | growth | mu * R / (R + h) * A | 1 | -1 / y |
Then, we can trigger a dynamic simulation, including visualization (Figure 2) with this single line of code:
dyn("models/singleSpecies.xlsx")To rerun the simulation with certain parameters or initial values being changed, we use the same call but specify the update argument. Here, we modify the flow rate and the growth rate at the same time.
dyn("models/singleSpecies.xlsx", update=c(q=100, mu=1.5))Finally, we can directly ask for the steady-state conditions (optionally making use of the update argument again).
std("models/singleSpecies.xlsx") R A
9.473684e-03 1.499053e+07
4 Scenarios with two competitors
4.1 Base scenario: Straight outcompetition
This scenario differs from the single species bioreactor (species A only) by the mere presence of a competitor species B. With regard to the considered processes, not much has changed: Suspended cells grow and they get flushed by the continuous flow (Figure 4).
Even the default parameters and initial values for A and B were chosen to be identical (Table 3, Table 4).
| type | name | unit | description | default |
|---|---|---|---|---|
| variable | R | mg / mL | resource concentration | 0.0e+00 |
| variable | A | cells / mL | abundance, organism A | 1.0e+05 |
| variable | B | cells / mL | abundance, organism B | 1.0e+05 |
| parameter | mu_A | 1 / h | max. growth rate of A | 2.0e+00 |
| parameter | mu_B | 1 / h | max. growth rate of B | 2.0e+00 |
| parameter | h_A | mg / mL | half saturation const. of A | 1.8e-01 |
| parameter | h_B | mg / mL | half saturation const. of B | 1.8e-01 |
| parameter | y_A | cells / mg | yield of A on R | 1.0e+06 |
| parameter | y_B | cells / mg | yield of B on R | 1.0e+06 |
| parameter | q | mL / h | flow rate | 1.0e+01 |
| parameter | v | mL | reactor volume | 1.0e+02 |
| parameter | Ain | cells / mL | abundance of A in input | 0.0e+00 |
| parameter | Bin | cells / mL | abundance of B in input | 0.0e+00 |
| parameter | Rin | mg / mL | input resource conc. | 1.5e+01 |
| name | unit | description | rate | A | B | R |
|---|---|---|---|---|---|---|
| flow | 1 / h | im-/export | q / v | Ain-A | Bin-B | Rin - R |
| growth_A | cells / mL / h | growth | mu_A * R / (R + h_A) * A | 1 | -1 / y_A | |
| growth_B | cells / mL / h | growth | mu_B * R / (R + h_B) * B | 1 | -1 / y_B |
Running a dynamic simulation suggest that both competitors survive and the abundances of A and B in steady state are equal (Figure 5). This is not very surprising because, with identical default parameters, A and B are essentially indistinguishable as if they were the same species.
dyn("models/twoSpecies_basic.xlsx")Now, let species A have a fitness advantage over B. We could create this situation, for example, by setting mu_A greater than mu_B. Alternatively, we can adjust the half saturation constant of the Monod model (h_B) such that B requires a higher resource concentration than A to reach an identical effective growth rate. Apparently, the superiority of A with regard to resource dependency drives B straight into extinction (Figure 6).
dyn("models/twoSpecies_basic.xlsx", update=c(h_B=0.36))This is easily verified by computing the steady state (note the very low value for species B).
std("models/twoSpecies_basic.xlsx", update=c(h_B=0.36)) R A B
9.473684e-03 1.499053e+07 1.156247e-12
Can we possibly compensate for the disadvantage of B by a higher inoculum? We can test this by modifying the initial proportions of A and B accordingly and rerun the model (Figure 6).
dyn("models/twoSpecies_basic.xlsx", update=c(h_B=0.36, A=1e3, B=1e8))Apparently, the higher initial value does not save the survival of species B in the long run, as confirmed by the calculation of the steady state. The results do not change, no matter how extreme the initial proportion is chosen.
std("models/twoSpecies_basic.xlsx", update=c(h_B=0.36, A=1e3, B=1e8)) R A B
9.473684e-03 1.499053e+07 2.992268e-11
4.2 Forced co-presence by invasion
The simplest option to keep the inferior competitor in the system is to allow for a certain rate of invasion. In the model, we can simply mimic invasion by setting the inflow concentration of species B (Bin) to a non-zero value (Figure 8).
dyn("models/twoSpecies_basic.xlsx", update=c(h_B=0.36, Bin=1e5))Invasion apparently allows for co-existence, however, we must be careful with wording. What we see in Figure 8 is not an example of stable co-existence. It is rather an example of co-presence!
In real world settings, it is actually quite difficult to distinguish cases of stable co-existence and co-presence because most ecosystems are interconnected and invasion is often a rule rather than an exception. Moreover, the possible time span of scientific investigations is usually too short to really test for stable co-existence. This is especially so when generation times are rather long and the differences in competitive ability are small.
4.3 Temporal variability can guard against extinction
Let’s study a scenario where each of the two competitors actually has its ecological niche. We assume that species A has a somewhat higher intrinsic growth rate at the reference temperature of 20°C compared to B. At the same time, we assume that the growth rate of B is more sensitive to temperature (Figure 9).
Tref <- 20 # reference temperature
mu_Tref <- c(A=1, B=0.95) # growth rates at reference temperature
theta <- c(A=1.05, B=1.1) # temperature dependency parameters
# 1.07 is equivalent to factor 2 per 10 K
Tm <- 5:30 # a gradient of temperatures
# temperature-adjusted growth rates of the competitors
mu_A <- mu_Tref["A"] * theta["A"]^(Tm - Tref)
mu_B <- mu_Tref["B"] * theta["B"]^(Tm - Tref)
plot(range(Tm), range(c(mu_A, mu_B)), type="n",
xlab="Temperature", ylab="Temperature-adjusted growth rate")
lines(Tm, mu_A)
lines(Tm, mu_B, lty=2)
legend("topleft", bty="n", lty=1:2, legend=c("A","B"), title="Species")The extended model is specified by Table 5 and Table 6. Note the additional temperature-adjustment terms multiplied with the growth rates.
| type | name | unit | description | default |
|---|---|---|---|---|
| variable | R | mg / mL | resource concentration | 0 |
| variable | A | cells / mL | abundance, organism A | 100000 |
| variable | B | cells / mL | abundance, organism B | 100000 |
| parameter | mu_A | 1 / h | max. growth rate of A at ref. Temp. | 1 |
| parameter | mu_B | 1 / h | max. growth rate of B at ref. Temp. | 0.95 |
| parameter | th_A | - | Temp. sensitivity parameter | 1.05 |
| parameter | th_B | - | Temp. sensitivity parameter | 1.1 |
| parameter | h_A | mg / mL | half saturation const. of A | 0.18 |
| parameter | h_B | mg / mL | half saturation const. of B | 0.18 |
| parameter | y_A | cells / mg | yield of A on R | 1000000 |
| parameter | y_B | cells / mg | yield of B on R | 1000000 |
| parameter | q | mL / h | flow rate | 10 |
| parameter | v | mL | reactor volume | 100 |
| parameter | Ain | cells / mL | abundance of A in input | 0 |
| parameter | Bin | cells / mL | abundance of B in input | 0 |
| parameter | Rin | mg / mL | input resource conc. | 15 |
| parameter | Tref | °C | Reference temp. for growth rate const. | 20 |
| function | Temp | °C | Time-varying system temperature | NA |
| name | unit | description | rate | A | B | R |
|---|---|---|---|---|---|---|
| flow | 1 / h | im-/export | q / v | Ain-A | Bin-B | Rin - R |
| growth_A | cells / mL / h | growth | mu_A * th_A^(Temp(time) - Tref) * R / (R + h_A) * A | 1 | -1 / y_A | |
| growth_B | cells / mL / h | growth | mu_B * th_B^(Temp(time) - Tref) * R / (R + h_B) * B | 1 | -1 / y_B |
Considering Figure 9, we expect A to win the competition game in a cold environment. This is confirmed by the following scenario (Figure 10).
Temp <- function(time) {17} # constant in time
dyn("models/twoSpecies_variability.xlsx",
times=seq(0, 1200, 24))Likewise, we expect from Figure 9 that switching to a warm environment reverses the outcome of the competition experiment such that A goes extinct in the long run (Figure 11).
Temp <- function(time) {23} # constant in time
dyn("models/twoSpecies_variability.xlsx",
times=seq(0, 1200, 24))Finally, we consider a scenario where the ambient temperature varies around the critical point where the two graphs of Figure 9 intersect. The simplest mean to achieve such fluctuations is by using periodic functions like \(sine\) or \(cosine\) as illustrated below (Figure 12).
Temp <- function(time, mini=17, maxi=25, lambda=72) { # cyclic fluctuations
pi <- 3.1415
mini + (maxi - mini) * 0.5 * (1 + sin(time/lambda * 2*pi))
}
plot(0:120, Temp(0:120), type="l", xlab="Time", ylab="Temperature")
abline(v=seq(0, 240, 24), lty=3)lambda parameter in the corresponding code.
Now let’s rerun the competition model with the time-variable temperature forcing. To check for convergence against a steady state, we need to extend the time axis a bit (Figure 13). Note that calling the std() function is not a good idea now, because the state variables will never converge to constant values.
dyn("models/twoSpecies_variability.xlsx",
times=seq(0, 24000, 480))Under the particular settings chosen, stable coexistence seems possible. However, the range of suitable settings is quite narrow. As soon as one of the species profits from its preferred temperature over longer times, it is likely to outcompete the other one.
Nevertheless, temporal variability is certainly an important mechanism explaining species coexistence. As demonstrated by the example, it even works in very simply structured environments with no spatial structure and just a single resource where ecological niches can only be exploited in the time domain.
Even when temporal variability alone does not explain coexistence sufficiently, it can make an important contribution by delaying extinction at least.
4.4 Compensation of a fitness disadvange 1: Stabilizing effect of predation
Prediction the effects of predation is a surprisingly complex topic and you likely know the classical work of Lotka and Volterra. Here, we will focus on the simplest possible case where only one of the two species in the model reactor undergoes predation und thus experiences additional losses (Figure 14). We impose these additional loss on the species with otherwise greater fitness. The other (inferior) species is simply assumed to be inedible for any reason (may it be due to size, taste, swimming speed, …). The idea behind is that the two contrasting advantages / disadvantages may compensate each other.
The model is specified by Table 7 and Table 8. Note that the dependency of predation on prey density was considered by an extended Monod-term with an offset. The idea is that the predator becomes inactive when there is too few prey because energy gain does not compensate for energy spent on hunting or filtration activity.
| type | name | unit | description | default |
|---|---|---|---|---|
| variable | R | mg / mL | resource concentration | 0 |
| variable | A | cells / mL | abundance, organism A | 100000 |
| variable | B | cells / mL | abundance, organism B | 100000 |
| variable | P | cells / mL | abundance of predator | 100 |
| parameter | mu_A | 1 / h | max. growth rate of A | 2 |
| parameter | mu_B | 1 / h | max. growth rate of B | 1 |
| parameter | mu_P | 1 / h | max. growth rate of predator | 0.1 |
| parameter | h_A | mg / mL | half saturation const. of A | 0.18 |
| parameter | h_B | mg / mL | half saturation const. of B | 0.18 |
| parameter | h_P | cells / mL | half saturation const. of predator | 1000000 |
| parameter | y_A | cells / mg | yield of A on R | 1000000 |
| parameter | y_B | cells / mg | yield of B on R | 1000000 |
| parameter | y_P | cells / cells | yield of P on A or B | 0.01 |
| parameter | i_P | - | minimum prey level (relative to h_P) | 0.01 |
| parameter | q | mL / h | flow rate | 2 |
| parameter | v | mL | reactor volume | 100 |
| parameter | Ain | cells / mL | abundance of A in input | 0 |
| parameter | Bin | cells / mL | abundance of B in input | 0 |
| parameter | Pin | cells / mL | abundance of predator in input | 0 |
| parameter | Rin | mg / mL | input resource conc. | 15 |
| function | max | - | built-in function | NA |
| name | unit | description | rate | A | B | P | R |
|---|---|---|---|---|---|---|---|
| flow | 1 / h | im-/export | q / v | Ain-A | Bin-B | Pin-P | Rin - R |
| growth_A | cells / mL / h | growth | mu_A * R / (R + h_A) * A | 1 | -1 / y_A | ||
| growth_B | cells / mL / h | growth | mu_B * R / (R + h_B) * B | 1 | -1 / y_B | ||
| growth_P | cells / mL / h | growth | mu_P * max(0, (A - h_P * i_P) / (A - h_P * i_P + h_P)) * P | -1 / y_P | 1 |
I already adjusted the default values of all parameters to demonstrate a stabilizing effect of predation on co-existence (Figure 15).
dyn("models/twoSpecies_predation.xlsx",
times=seq(0, 4800, 40), maxcols=2)For different parameter settings, you have to expect different outcomes, including extinction of one of the competitors. Finally, the example below demonstrates that predation does not necessarily trigger an infinite series of oscillations (Figure 16). It all depends on the relations between the various model parameters.
dyn("models/twoSpecies_predation.xlsx", update=c(mu_P=0.3),
times=seq(0, 4800, 40), maxcols=2)Finally, you should note that the oscillations visible in Figure 15 and Figure 16 are of a totally different kind compared to those seen in Figure 13. In the later figure, oscillations were due to the prescribed periodic temperature forcing imposed on the system. By contrast, oscillations in the predation example represent an internal feature of the system! The appear even if forcings are perfectly constants.
Now, you can probably imagine that the interpretation of oscillation in real-world observation data is challenging. In general, one has to expect that external variations at multiple time scales (daily, seasonal, other, yet other) possibly get superimposed by intrinsic oscillations generated by the system itself.
4.5 Compensation of a fitness disadvange 2: Persistence
In the previous example, the lower fitness of species B was compensated by increased losses of the competitor species A owing to its susceptibility to predation. The present example is somewhat similar but assumes an additional trait in species B rather than the presence of a predator. Specifically, it is assumed that species B tends to form biofilm covering the surface of the reactor (Figure 17, attached cells are denoted Ba). Thereby, species B partly escapes the permanent loss of suspended individuals from the reactor.
The model is specified by Table 9 and Table 10. Note the two simultaneous process of attachment and detachment. In fact, the approach to model the distribution of species B over the suspended and biofilm phase is identical to what is used to describe chemical equilibria.
| type | name | unit | description | default |
|---|---|---|---|---|
| variable | R | mg / mL | resource concentration | 0 |
| variable | A | cells / mL | abundance, organism A | 100000 |
| variable | B | cells / mL | abundance, organism B | 100000 |
| variable | Ba | cells / cm2 | abundance of attached B | 0 |
| parameter | mu_A | 1 / h | max. growth rate of A | 2 |
| parameter | mu_B | 1 / h | max. growth rate of B | 2 |
| parameter | h_A | mg / mL | half saturation const. of A | 0.18 |
| parameter | h_B | mg / mL | half saturation const. of B | 0.18 |
| parameter | y_A | cells / mg | yield of A on R | 1000000 |
| parameter | y_B | cells / mg | yield of B on R | 1000000 |
| parameter | ka | 1 / h | attachment rate constant | 1 |
| parameter | kd | 1 / h | detachment rate constant | 1 |
| parameter | ccap_Ba | cells / cm2 | upper limit of biofilm density | 100000000 |
| parameter | q | mL / h | flow rate | 10 |
| parameter | v | mL | reactor volume | 100 |
| parameter | a | cm2 | surface area for attachment | 100 |
| parameter | Rin | mg / mL | input resource conc. | 15 |
| parameter | Ain | cells / mL | abundance of A in input | 0 |
| parameter | Bin | cells / mL | abundance of B in input | 0 |
| function | max | - | built-in function | NA |
| name | unit | description | rate | A | B | Ba | R |
|---|---|---|---|---|---|---|---|
| flow | 1 / h | im-/export | q / v | Ain-A | Bin-B | Rin - R | |
| growth_A | cells / mL / h | growth | mu_A * R / (R + h_A) * A | 1 | -1 / y_A | ||
| growth_B | cells / mL / h | growth | mu_B * R / (R + h_B) * B | 1 | -1 / y_B | ||
| growth_Ba | cells / cm2 / h | growth | mu_B * R / (R + h_B) * max(0, 1 - Ba/ccap_Ba) * Ba | 1 | -1 / y_B * a / v | ||
| attach | cells / mL / h | attachment | ka * B | -1 | v / a | ||
| detach | cells / cm2 / h | detachment | kd * Ba | a / v | -1 |
When the model is run with defaults settings, species B outcompetes A because the growth rates were set equal (Figure 18).
dyn("models/twoSpecies_biofilm.xlsx", maxcols=2)However, by lowering the growth rate of B, we can get to a state where the two species co-exist in a stable manner (Figure 19). Because of the additional biofilm trait, the system in general reacts a bit slower and the steady state is only reached after longer time.
dyn("models/twoSpecies_biofilm.xlsx", c(mu_B=1),
times=seq(0, 4800, 24), maxcols=2)Again, the ability of the inferior competitor to attach to surfaces may facilitate coexistence, but it depends on the particular relation between parameter values. For this example, one could identify a feasible range of conditions, for example, by computing steady state solutions over a range of assumed growth rates of species B (Figure 20).
x <- NULL
for (mu_B in seq(0.7, 1.3, 0.1)) {
x <- rbind(x, c(mu_B=mu_B,
std("models/twoSpecies_biofilm.xlsx", c(mu_B=mu_B))
))
}
plot(range(x[,"mu_B"]), range(x[,c("A","B")]), type="n",
xlab="mu_B", ylab="Steady state abundance (cells / ml)")
points(A ~ mu_B, data=x, pch="A")
points(B ~ mu_B, data=x, pch="B")5 Summary
The mere presence of multiple species in a system at a certain point in time does not necessarily indicate their stable co-existence. Species presence may well be a result of invasion.
Modeling provides a unique means to study the conditions that allow for stable species co-existence.
In general, in accordance with theory, co-existence requires niches in the sense that each species is superior to competitors in a certain aspect.
Superiority arises from either faster growth or reduced losses (or both).
In real ecosystems, the co-presence of many seemingly similar species is often a result of multiple factors, including niches, invasion, spatial separation and mixing, temporary escape from competition, and temporal variability of boundary conditions.