```
library(plant)
library(parallel)
run <- function(seed_rain_in, p) {
p$seed_rain <- seed_rain_in
run_scm(p)$seed_rains
}
run_new_schedule <- function(seed_rain_in, p) {
p$seed_rain <- seed_rain_in
res <- build_schedule(p)
attr(res, "seed_rain_out")
}
cobweb <- function(m, ...) {
lines(rep(m[,1], each=2), c(t(m)), ...)
}
```

Try to establish what the equilibrium seed rain is.

Set model parameters

```
p0 <- scm_base_parameters("FF16")
p <- expand_parameters(trait_matrix(0.0825, "lma"), p0, FALSE)
```

Some output seed rains, given an input seed rain:

`run(1.0, p)`

`## [1] 19.82443`

`run(10.0, p)`

`## [1] 17.63104`

`run(50.0, p)`

`## [1] 16.58796`

When below the equilibrium, `run`

returns a seed rain that is greater than the input, when above it, `run`

returns a seed rain that is less than the input.

`p_eq <- equilibrium_seed_rain(p)`

Equilibrium seed rain is right around where the last two values from run were producing

`p_eq$seed_rain`

`## [1] 17.31512`

From a distance, these both hone in nicely on the equilibrium, and rapidly, too.

```
approach <- attr(p_eq, "progress")
r <- range(approach)
plot(approach, type="n", las=1, xlim=r, ylim=r)
abline(0, 1, lty=2, col="grey")
cobweb(approach, pch=19, cex=.5, type="o")
```

Then, in the vicinity of the root we should look at what the curve actually looks like, without adaptive refinement.

```
dr <- 2 # range of input to vary (plus and minus this many seeds)
seed_rain_in <- seq(p_eq$seed_rain - dr,
p_eq$seed_rain + dr, length.out=31)
seed_rain_out <- unlist(mclapply(seed_rain_in, run, p_eq))
```

Here is input seeds vs. output seeds:

```
plot(seed_rain_in, seed_rain_out, xlab="Incoming seed rain",
ylab="Outgoing seed rain", las=1, type="l", asp=5, col="red")
abline(0, 1, lty=2, col="grey")
cobweb(approach)
```

`seed_rain_in_global <- seq(1, max(approach), length.out=51)`

This takes quite a while to compute.

```
seed_rain_out_global <-
unlist(mclapply(seed_rain_in_global, run_new_schedule, p))
```

This is pretty patchy, which is due to incompletely refining the cohort schedule, I believe. Tighten `schedule_eps`

to make the curve smoother, at the cost of potentially a lot more effort.

```
plot(seed_rain_in_global, seed_rain_out_global,
las=1, type="l",
xlab="Incoming seed rain", ylab="Outgoing seed rain")
abline(0, 1, lty=2, col="grey")
cobweb(approach, lty=3)
```

```
lma <- c(0.0825, 0.15)
p2 <- expand_parameters(trait_matrix(lma, "lma"), p0, FALSE)
p2_eq <- equilibrium_seed_rain(p2)
approach <- attr(p2_eq, "progress")
```

From a distance, these both hone in nicely on the equilibrium, and rapidly, too.

```
r <- range(unlist(approach))
plot(approach[[1]], type="n", las=1, xlim=r, ylim=r, xlab="in", ylab="out")
abline(0, 1, lty=2, col="grey")
cols <- c("black", "red")
for (i in 1:2) {
cobweb(approach[, i + c(0, 2)], pch=19, cex=.5, type="o", col=cols[[i]])
}
abline(v=p2_eq$seed_rain, col=1:2, lty=3)
```

Note that the first guess position of the red species is higher than the black species, but in the end the output seed rain is lower. This is the difficulty in computing multi species equilibria - the different solutions affect each other. In general multi-dimensional root finding is difficult; even knowing that there are roots is not straightforward, much less proving that we’ve converged on the “correct” root (for example [0,0] is a root in this case but that root is not stable). plant uses some heuristics to try to ensure that the root returned is an attracting point but sequentially applying rounds of iteration and non-linear root finding algorithms, as well as rescaling seed rains to repel from known unstable roots.

To illustrate this a little further, though still in the fairly trivial 2d case, first identify the other two equilibria.

```
pp <- mclapply(lma, function(x)
equilibrium_seed_rain(expand_parameters(trait_matrix(x, "lma"), p0, FALSE)))
```

Here’s the seed rains of each species when alone:

`seed_rain1 <- sapply(pp, function(x) x$seed_rain)`

So that means that we have *four* equilibria: 1: The trivial equilibrium:

```
eq00 <- c(0, 0)
run(eq00, p2_eq) - eq00
```

`## [1] 0 0`

2: Species 1 alone

```
eq10 <- c(seed_rain1[[1]], 0)
run_new_schedule(eq10, p2) - eq10
```

`## [1] -0.009399603 0.000000000`

3: Species 2 alone

```
eq01 <- c(0, seed_rain1[[2]])
run_new_schedule(eq01, p2) - eq01
```

`## [1] 0.000000000 0.007976816`

4: Species 1 and 2 together:

```
eq11 <- p2_eq$seed_rain
run(eq11, p2_eq) - eq11
```

`## [1] 0.0011239196 0.0003123585`

(note that the approximations here mean that these equilibria are not terribly well polished - there are a set of nested approximations that make this difficult. Possibly the biggest culprit is the cohort refinement step).

```
len <- 21
dx <- max(seed_rain1) / (len - 1)
n1 <- seq(0.001, by=dx, to=seed_rain1[[1]] + dx)
n2 <- seq(0.001, by=dx, to=seed_rain1[[2]] + dx)
nn_in <- as.matrix(expand.grid(n1, n2))
tmp <- mclapply(unname(split(nn_in, seq_len(nrow(nn_in)))),
run_new_schedule, p2_eq)
nn_out <- do.call("rbind", tmp)
len <- log(rowSums(sqrt((nn_out - nn_in)^2)))
rlen <- len / max(len) * dx * 0.8
theta <- atan2(nn_out[, 2] - nn_in[, 2], nn_out[, 1] - nn_in[, 1])
x1 <- nn_in[, 1] + rlen * cos(theta)
y1 <- nn_in[, 2] + rlen * sin(theta)
```

NOTE: I’m not sure why the point really close to the equilibrium here looks like it’s moving so quickly, and possibly in the wrong direction. Cohort instability perhaps?

```
plot(nn_in, xlab="Species 1", ylab="Species 2", pch=19, cex=.25,
col="grey", asp=1)
arrows(nn_in[, 1], nn_in[, 2], x1, y1, length=0.02)
lines(rbind(approach[1, 1:2], approach[, 3:4]), type="o", col="red",
pch=19, cex=.5)
points(p2_eq$seed_rain[[1]], p2_eq$seed_rain[[2]], pch=19)
points(rbind(eq00, eq10, eq01))
```