---
title: "Why sinks hold high density"
description: "Why a sink can hold more animals than the source that feeds it, worked in R, and why population density is a treacherous guide to habitat quality."
date: "2026-07-31 13:00"
categories: [ecology tutorial, R, population ecology, conservation, habitat selection]
image: thumbnail.png
image-alt: "A bar chart with a low-quality sink holding far more individuals than the high-quality source that supplies it."
---
```{r setup}
#| include: false
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE,
dev = "png", dpi = 120, fig.path = "figures/",
fig.width = 7, fig.height = 4.4)
library(ggplot2)
theme_te <- theme_minimal(base_size = 12) +
theme(panel.background = element_rect(fill = "white", colour = NA),
plot.background = element_rect(fill = "white", colour = NA),
panel.grid.minor = element_blank(),
plot.title = element_text(face = "bold", size = 12))
theme_set(theme_te)
col_source <- "#3a6b52"; col_sink <- "#b5651d"; col_ref <- "#555555"
l1 <- 1.5; l2 <- 0.84; n1 <- 100
n2 <- n1 * (l1 - 1) / (1 - l2)
ratio <- n2 / n1
set.seed(408)
imm <- 40
habitat <- data.frame(lambda = seq(0.5, 1.5, length = 25))
habitat$density <- ifelse(habitat$lambda >= 1, 80 + rnorm(25, 0, 4),
pmin(imm/(1 - habitat$lambda), 400))
cor_dq <- cor(habitat$lambda, habitat$density)
dens_top <- habitat$lambda[order(-habitat$density)[1:3]]
```
The [previous post](../source-sink-dynamics/) ended on a warning: a habitat's local growth rate tells you whether it can sustain itself, not how many animals it holds. This post makes that concrete with the result that most surprises people the first time they meet it. At equilibrium a sink can hold *more* individuals than the source that keeps it alive. The worst habitat on the landscape can be the most crowded.
## The arithmetic of the paradox
A source at equilibrium is capped. It has a fixed number of breeding sites, and once they are full the extra production has nowhere to go but out, so the source sits at its own capacity and exports the rest. A sink has no such cap from below: it keeps accepting immigrants, and its population climbs until the annual shortfall between births and deaths, which grows with population size, finally balances the steady inflow. That balance point is the immigration divided by the per-capita shortfall.
```{r paradox}
l1 <- 1.5; l2 <- 0.84; n1 <- 100 # source growth rate, sink growth rate, source size
surplus <- n1 * (l1 - 1) # emigrants the source sends each year
n2 <- surplus / (1 - l2) # equilibrium sink size
c(source_size = n1, sink_size = n2, ratio = n2 / n1)
```
The source holds `r sprintf("%.0f", n1)` breeding adults and exports `r sprintf("%.0f", surplus)` surplus animals a year. The sink, losing `r sprintf("%.0f", 100*(1-l2))` per cent of its population to the shortfall each year, swells until that loss equals the inflow, which happens at `r sprintf("%.0f", n2)`, more than `r sprintf("%.1f", ratio)` times the source. Stand in each habitat and count: the sink looks like the stronghold and the source like the marginal patch. The count has it exactly backwards.
```{r fig-paradox}
#| fig-cap: "Standing population in the source and the sink. The sink (ochre, local growth rate below one) holds several times the source (green, local growth rate above one) that produces every surplus animal it receives."
#| fig-alt: "Two bars: a short green source bar and a much taller ochre sink bar, with labels noting the source grows and the sink shrinks locally."
dp <- data.frame(habitat = c("source\n(grows locally)", "sink\n(shrinks locally)"),
N = c(n1, n2), kind = c("source", "sink"))
ggplot(dp, aes(habitat, N, fill = kind)) +
geom_col(width = 0.6) +
scale_fill_manual(values = c("source" = col_source, "sink" = col_sink), guide = "none") +
labs(x = NULL, y = "Standing population", title = "The worst habitat holds the most animals")
```
## Density is a trap for the manager
Now put the whole landscape together: many habitats of different quality, some sources, some sinks, all connected by movement. Plot the standing density in each against its local growth rate, the real measure of quality.
```{r density-quality}
set.seed(408)
imm <- 40
habitat <- data.frame(lambda = seq(0.5, 1.5, length = 25))
habitat$density <- ifelse(habitat$lambda >= 1, 80 + rnorm(25, 0, 4), # sources capped
pmin(imm/(1 - habitat$lambda), 400)) # sinks pile up near lambda = 1
c(correlation_density_quality = cor(habitat$lambda, habitat$density))
```
The correlation between density and quality is `r sprintf("%+.2f", cor_dq)`: if anything, slightly negative. The three most crowded habitats have local growth rates of `r sprintf("%.2f", dens_top[1])`, `r sprintf("%.2f", dens_top[2])` and `r sprintf("%.2f", dens_top[3])`, every one of them a sink. A conservation plan that ranks habitats by how many animals they hold, a natural and common thing to do, will pour effort into the crowded sinks and neglect the sparse sources that are quietly producing the entire population (Van Horne's warning, and the reason Pulliam 1988 The American Naturalist 132(5):652-661 mattered so much). Protect the sink and you protect a display; protect the source and you protect the supply.
```{r fig-density}
#| fig-cap: "Standing density against local growth rate across a landscape of habitats. Density does not track quality: the densest habitats (ochre, sinks) grow less than one, while the true sources (green) can be sparse."
#| fig-alt: "Scatter of density against local growth rate; the highest densities occur at growth rates below one, and habitats above one are only moderately dense."
habitat$kind <- ifelse(habitat$lambda >= 1, "source", "sink")
ggplot(habitat, aes(lambda, density, colour = kind)) +
geom_vline(xintercept = 1, colour = col_ref, linetype = "dashed") +
geom_point(size = 2.6) +
scale_colour_manual(values = c("source" = col_source, "sink" = col_sink)) +
labs(x = "Local growth rate (habitat quality)", y = "Standing density", colour = NULL,
title = "Density does not measure quality")
```
## When the pattern appears, and when it does not
This upside-down world is not guaranteed; it depends on how animals settle. If they distribute themselves to equalise their prospects, filling the good habitat first and spilling into the poor one only when the good one is crowded, the sink stays empty until the source is full, and it never gets absurdly crowded. The extreme version here, where the sink dwarfs the source, needs animals that keep settling in the sink regardless, either because they cannot tell it is poor or because they are forced there. That is exactly the setting where a habitat can become a trap, luring settlers into a patch that quietly kills them, which is the [next post](../ecological-traps-and-pseudo-sinks/). And because density has now failed twice as a guide to quality, the [checking post](../checking-a-source-sink-analysis/) turns to what you actually have to measure.
## Related tutorials
- [Source-sink population dynamics](../source-sink-dynamics/)
- [Ecological traps and pseudo-sinks](../ecological-traps-and-pseudo-sinks/)
- [Checking a source-sink analysis](../checking-a-source-sink-analysis/)
- [Resource selection functions](../resource-selection-functions/)
## References
- Pulliam HR 1988. The American Naturalist 132(5):652-661 (10.1086/284880).