Logistic growth is widely used in biology and ecology to describe populations growth under environmental constraints. Essentially this is a modification of exponential growth with a growth rate modified (reduced) as the population approaches limits set by nutrients or other environmental conditions.
Exponential growth is written: \[ \frac{dP}{dt} = g P \]
where P is population and g is the groth rate (fractional increase in opulation per unit time).
Next we modify the growth rate to decrease as the population approaches a limit imposed by the environment, for example by a limiting nutrient:
\[ \frac{dP}{dt} = g(1-\frac{P}{K}) P \].
It's helpful to think of \( g(1-\frac{P}{K}) \) as the modified or constrained growth rate, which is the probability of reproduction for each individual in the population per tinmestep.
Finally, we add death. Here mortality is implemented as an average lifetime \( L \), so that in each time step the probability of an individual dying is \( \frac{g}{L} \).
The final model as implemented here is then
\[ \frac{dP}{dt} = g(1-\frac{P}{K}-\frac{1}{L}) P \].
The program that does the calculation is very simple, and the code that controls this website is surprisingly simple too! It is all written in the programming language R, using a web programming package called shiny. You can read all about it on the “Website Code” tab to the right.
This website is controlled using the R package “shiny.” There are five important components:
Scroll down or click links in the list above to read all about it!
advect.R:
advect <- function(nGrid=100, nCycles=5, courant=0.5) {
# How many time steps to complete nCycles?
nSteps <- nCycles * (nGrid / courant)
# Create an array to hold tracer concentrations at every time step
tracer <- matrix(0, nrow=nGrid, ncol=nSteps,) # fill with zeros
# Initialize tracer distribution to be a "top-hat" pattern
tracer[round(nGrid/3) : round(2*nGrid/3), 1] <- 100.
# Advect the tracer completely around the domain nCycles times
for (iStep in 1:(nSteps-1))
tracer[,iStep+1] <- upstream(nGrid, tracer[,iStep], courant)
return(list(nGrid=nGrid, nCycles=nCycles, courant=courant, nSteps=nSteps,
tracer=tracer))
}
upstream.R: upstream <- function(nGrid, tracer, courant){
# Tracer: advected quantity that will be updated
# courant: speed of advection in grid cells per time step (must be < 1)
# Make temporary local copy of tracer array
newTracer <- tracer
# Advect tarcer in grid interior using upstream scheme
newTracer[2:nGrid] <- (1.-courant)*tracer[2:nGrid] + courant*tracer[1:nGrid-1]
# Wrap at lateral boundaries (left edge of grid receives from right edge)
newTracer[1] <- (1.-courant)*tracer[1] + courant*tracer[nGrid]
return(newTracer)
}
plotadvected.R:
plotadvected <- function(model.output) {
# plot initial and final tracer distributions
attach(model.output)
initial <- tracer[,1]
final <- tracer[,nSteps]
# Initial and final tracer distribution
# legendName<-['Initial', strcompress('After '+string(nCycles)+' cycles')]
plot(1:nGrid, initial, col='black', type='l', lwd=4, xlab='grid cell',
ylab='Concentration',
main='Initial and Final Tracer Distribution', cex=1.5)
lines(1:nGrid, final, col='red', lwd=4)
legend('topleft', c('Initial','Final'), col=c('black','red'), lwd=c(4,4))
# Label plot with courant number
#t1 <- TEXT(0.65, .8, string('Courant <- '+ string(courant)), /normal)
detach(model.output)
}
ui.R:
library(shiny)
library(markdown)
# Define user interface for 1-D advection model
shinyUI(pageWithSidebar(
# Application title
headerPanel("Tracer Advection in One Dimension with Constant Speed"),
# Sidebar on the left with controls for the user to specify parameters
sidebarPanel(
h4('Set Models Parameter Below'),
sliderInput("courant", "Courant Number (speed in grid cells per time step)",
min = 0.1, max = 1, value = 0.5, step= 0.01),
sliderInput("nCycles", "Number of times to cycle around domain",
min = 1, max = 50, value = 10, step= 1)
),
# Show a tabset in the main panel of the browser that displays model output
mainPanel(
tabsetPanel(
# First tab shows plots of model output for both control and modified atmosphere
tabPanel("Output",
h4('Model Output'),
plotOutput('advected.tracer')),
# Second tab displays a brief model description
tabPanel("Model Description",
includeHTML('doc/model.description.html')),
# Third tab displays website code
tabPanel("Website code",
includeMarkdown('doc/website.code.md'))
)
)
))
server.R:
library(shiny)
# Source required R scripts
source('model/advect.R')
source('model/plotadvected.R')
shinyServer(function(input, output) {
# Run the advection model and save output in a list
model.output <- reactive(advect(nCycles=input$nCycles, courant=input$courant))
# plot of model output
output$advected.tracer <- renderPlot(plotadvected(model.output()))
})