sysetholApp (Systems Ethology Platform)
The central Systems Ethology platform orchestrating host-parasite individual-based simulations, age class distributions over time, spatial substrate networks, and parameter evaluations.
The live application below is running completely client-side in your browser using serverless Shinylive (WebAssembly). It provides a streamlined Systems Ethology platform (sysetholApp()) with clean sidebar controls (sysetholInput), dynamic conditional panels (Steps per click log-scale geometric slider when nsim = 1, Confidence Band when nsim > 1), sidebar Run Engine & Reset buttons, and modular tabs (sysetholOutput: Dist Plots fine time-granularity step plots, Substrate Plots, Envelope Plots, and Input Data via inputApp).
#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 880
#| components: [viewer]
library(shiny)
library(bslib)
library(ggplot2)
library(cowplot)
library(dplyr)
library(tidyr)
library(tibble)
library(rlang)
library(splines)
library(stats)
library(graphics)
# --- Auto-Included Data Table: future.host ---
future.host <- structure(list(current = c("crawler", "first.instar", "first.molt",
"second.1", "second.2", "second.3", "second.3", "female", "male",
"second.molt", "third.1", "third.2", "third.3", "virgin", "gravid",
"death", "starved"), future = c("first.instar", "first.molt",
"second.1", "second.2", "second.3", "female", "male", "second.molt",
"death", "third.1", "third.2", "third.3", "virgin", "gravid",
"gravid", "death", "death"), fid = c(2L, 3L, 4L, 5L, 6L, 8L,
9L, 10L, 16L, 11L, 12L, 13L, 14L, 15L, 15L, 16L, 16L), time = c(55L,
92L, 48L, 30L, 30L, 52L, 52L, 0L, 0L, 48L, 55L, 55L, 95L, 90L,
10L, 0L, 0L), pch = c("0", "1", "1", "2", "2", "2", "2", "2",
"2", "2", "3", "3", "3", "V", "G", "D", "D"), color = c("brown",
"green", "green", "turquoise", "turquoise", "turquoise", "turquoise",
"turquoise", "turquoise", "turquoise", "blue", "blue", "blue",
"violet", "black", "red", "red"), ageclass = c("crawler", "host",
"host", "host", "host", "host", "host", "host", "host", "host",
"host", "host", "host", "host", "gravid", NA, "gravid"), event = c("future",
"future", "future", "future", "future", "future", "future", "future",
"future", "future", "future", "future", "future", "future", "birth",
"death", "future"), init = c(10L, 4L, 4L, 1L, 1L, 1L, 1L, 0L,
0L, 1L, 1L, 1L, 1L, 1L, 2L, 0L, 0L)), class = "data.frame", row.names = c("1",
"2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13",
"14", "15", "16", "17"))
# --- Auto-Included Data Table: future.parasite ---
future.parasite <- structure(list(current = c("egg", "larvae", "prepupae", "pupae",
"adult", "adult", "feed", "ovip", "death", "male", "starved"),
future = c("larvae", "prepupae", "pupae", "adult", "feed",
"ovip", "adult", "adult", "death", "death", "death"), fid = c(2L,
3L, 4L, 5L, 7L, 8L, 5L, 5L, 9L, 9L, 9L), time = c(48L, 60L,
24L, 144L, 12L, 12L, 12L, 12L, 0L, 0L, 0L), pch = c("E",
"L", "p", "P", "F", "H", "F", "H", "D", "D", "D"), color = c("brown",
"green", "turquoise", "blue", "orange", "purple", "orange",
"purple", "red", "red", "red"), ageclass = c("young", "young",
"young", "young", "adult", "adult", "adult", "adult", NA,
"adult", "adult"), event = c("future", "future", "future",
"future", "future", "future", "attack", "attack", "death",
"future", "future"), init = c(10L, 5L, 3L, 2L, 1L, 0L, 0L,
0L, 0L, 0L, 0L)), class = "data.frame", row.names = c("1",
"2", "3", "4", "5", "6", "7", "8", "9", "10", "11"))
# --- Auto-Included Data Table: host.parasite ---
host.parasite <- structure(list(ovip = c(0L, 0L, 0L, 0L, 3L, 4L, 2L, 3L, 5L, 5L,
2L, 0L, 0L, 0L, 0L, 0L), feed = c(0L, 5L, 2L, 5L, 2L, 1L, 0L,
0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), offspring = c(0, 0, 0, 0,
0, 0, 0, 1.3, 1.3, 1.3, 2.7, 0, 0, 0, 0, 0), male = c(NA, 5L,
5L, 5L, 5L, 5L, 4L, 2L, 1L, 1L, 0L, NA, NA, NA, NA, NA)), class = "data.frame", row.names = c("crawler",
"first.instar", "first.molt", "second.1", "second.2", "second.3",
"second.molt", "third.1", "third.2", "third.3", "virgin", "gravid",
"death", "parasite", "male", "female"))
# --- Auto-Included Data Table: organism.features ---
organism.features <- structure(list(units = c("DD", "hr", NA), offspring = c("20",
"host", NA), attack = c(NA, "host", NA), birth = c("gravid",
NA, NA), substrate = c("substrate", "substrate", NA), deplete = c(100L,
48L, NA), subclass = c("host", "adult", NA), parasite = c(NA,
"ecto", NA), move = c("crawler", "adult", NA)), class = "data.frame", row.names = c("host",
"parasite", "substrate"))
# --- Auto-Included Data Table: redscale ---
redscale <- structure(list(DD = c(-5L, 0L, 0L, 55L, 147L, 195L, 225L, 255L,
307L, 355L, 410L, 465L, 560L, 650L, 651L), aphytis.feed = c(0L,
0L, 0L, 5L, 2L, 5L, 2L, 1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L), aphytis = c(0L,
0L, 0L, 0L, 0L, 0L, 3L, 4L, 2L, 3L, 5L, 5L, 2L, 0L, 0L), comperiella = c(0,
0, 0, 3, 3, 2, 2, 3, 4, 5, 5, 5, 3.5, 0, 0), encarsia = c(0,
0, 0, 4, 3.5, 5, 5, 5, 4.5, 4.5, 4, 3, 3, 0, 0), gender = c("either",
"either", "neuter", "neuter", "neuter", "neuter", "neuter", "neuter",
"female", "female", "female", "female", "female", "female", "female"
), pch = c("P", "P", "0", "1", "1", "2", "2", "2", "2", "3",
"3", "3", "V", "G", "D"), color = c("white", "white", "brown",
"green", "green", "turquoise", "turquoise", "turquoise", "turquoise",
"blue", "blue", "blue", "violet", "black", "red"), stage = c("parasite",
"parasite", "crawler", "first.instar", "first.molt", "second.1",
"second.2", "second.3", "second.molt", "third.1", "third.2",
"third.3", "virgin", "gravid", "death")), class = "data.frame", row.names = c(NA,
-15L))
# --- Auto-Included Data Table: substrate.host ---
substrate.host <- structure(list(substrate = c("fruit", "fruit", "fruit", "fruit",
"twig", "leaf", "leaf"), side = c("1", "2", "3", "4", NA, "top",
"bottom"), init = c(1L, 1L, 1L, 1L, 1L, 5L, 0L), find = c(5L,
5L, 5L, 5L, 1L, 3L, 2L), move = c(1L, 1L, 1L, 1L, 5L, 3L, 4L),
fruit = c(3L, 3L, 3L, 3L, 3L, 3L, 3L), twig = c(2L, 2L, 2L,
2L, 1L, 2L, 1L), leaf = c(1L, 1L, 1L, 1L, 2L, 1L, 2L)), class = "data.frame", row.names = c("fr1",
"fr2", "fr3", "fr4", "twig", "lftop", "lfbot"))
# --- Auto-Included Data Table: substrate.parasite ---
substrate.parasite <- structure(list(substrate = c("fruit", "fruit", "fruit", "fruit",
"twig", "leaf", "leaf"), side = c("1", "2", "3", "4", NA, "top",
"bottom"), init = c(1L, 1L, 1L, 1L, 1L, 5L, 0L), find = c(5L,
5L, 5L, 5L, 1L, 3L, 2L), move = c(1L, 1L, 1L, 1L, 5L, 3L, 4L),
fruit = c(3L, 3L, 3L, 3L, 3L, 3L, 3L), twig = c(2L, 2L, 2L,
2L, 1L, 2L, 1L), leaf = c(1L, 1L, 1L, 1L, 2L, 1L, 2L)), class = "data.frame", row.names = c("fr1",
"fr2", "fr3", "fr4", "twig", "lftop", "lfbot"))
# --- Auto-Included Data Table: substrate.substrate ---
substrate.substrate <- structure(list(fr1 = c(0L, 1L, 1L, 1L, 1L, 1L, 1L), fr2 = c(1L,
0L, 1L, 1L, 1L, 1L, 1L), fr3 = c(1L, 1L, 0L, 1L, 1L, 1L, 1L),
fr4 = c(1L, 1L, 1L, 0L, 1L, 1L, 1L), twig = c(1L, 1L, 1L,
1L, 1L, 1L, 1L), lftop = c(1L, 1L, 1L, 1L, 1L, 1L, 1L), lfbot = c(1L,
1L, 1L, 1L, 1L, 1L, 1L)), class = "data.frame", row.names = c("fr1",
"fr2", "fr3", "fr4", "twig", "lftop", "lfbot"))
# --- Auto-Included Data Table: temperature.base ---
temperature.base <- structure(list(Day = c(0L, 0L, 0L, 0L, 0L, 30L, 30L, 30L, 30L,
30L, 30L), Time = c(0L, 8L, 12L, 18L, 20L, 0L, 8L, 12L, 15L,
18L, 20L), Base = c(0L, 0L, 100L, 75L, 0L, 0L, 0L, 80L, 100L,
80L, 60L)), class = "data.frame", row.names = c(NA, -11L))
# --- Auto-Included Data Table: temperature.par ---
temperature.par <- structure(list(value = c(24L, 200L, 52L, 70L, 75L, 85L, 90L,
8L), description = c("hours in day", "number of days", "minimum temperature for degree-day calculation (Fahrenheit)",
"begining daily low temperature", "ending daily low temperature",
"begining daily high temperature", "ending daily high temperature",
"number of nodes for interpolating spline")), class = "data.frame", row.names = c("Unit",
"Days", "Min", "LowBeg", "LowEnd", "HighBeg", "HighEnd", "Length"
))
# --- Source: spline.R ---
## $Id: spline.R,v 1.0 2002/12/09 yandell@stat.wisc.edu Exp $
##
## Functions for Bland Ewing's modeling.
##
## Copyright (C) 2000,2001,2002 Brian S. Yandell.
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2, or (at your option) any
## later version.
##
## These functions are distributed in the hope that they will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## The text of the GNU General Public License, version 2, is available
## as http://www.gnu.org/copyleft or by writing to the Free Software
## Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
###########################################################################################
## Support routines for future.meanvalue
###########################################################################################
spline.rate <- function( meanvalue, x )
{
coeff <- stats::coef( meanvalue )
for( i in 2:ncol( coeff ))
coeff[,i-1] <- i * coeff[,i]
coeff[,ncol( coeff ) ] <- 0
meanvalue$coefficients <- coeff
if( missing( x ))
stats::predict( meanvalue )
else
stats::predict( meanvalue, x )
}
###########################################################################################
spline.deriv <- function( s )
{
s$coefficients <- s$coefficients[,-1]
for( i in seq( 2, ncol( s$coefficients )))
s$coefficients[,i] <- s$coefficients[,i] * i
s
}
###########################################################################################
splinesum <- function( xy, fit = splines::interpSpline( xy$x, xy$y ),
log2 = log( 2 ), tol = 1e-5 )
{
n <- nrow( xy )
## mean time to future event (assume linear off end )
maxx <- xy[n,"x"]
mean.y <- maxx * mean( exp( - stats::predict( fit )$y )) + xy[1,"x"]
rate <- spline.rate( fit, maxx )$y
if( rate > tol )
mean.y <- mean.y + exp( - xy[n,"y"] ) / rate
## median time to future event
adiff <- diff( stats::coef( fit )[,1] )
## undefined if mean value function not monotone
if( !( all( adiff < 0) || all( adiff > 0 )))
median.y <- NA
else {
invmvalue <- splines::backSpline( fit )
median.y <- stats::predict( invmvalue, log2 )$y
if( is.na( median.y ))
median.y <- spline.extrapolate( fit, invmvalue, log2 )
}
c( mean = mean.y, median = median.y )
}
###########################################################################################
summaryshow <- function( xy, fit, col = "black", sums = splinesum( xy, fit ))
{
tmpar <- graphics::par( col = col )
graphics::mtext( paste( "mean =", round( sums[1], 2 )), 3, at = graphics::par("usr")[1], adj = 0 )
medianshow <- if( is.na( sums[2] ))
"curve not monotone"
else
paste( "median =", round( sums[2], 2 ))
graphics::mtext( medianshow, 3, at = graphics::par("usr")[2] / 1.25, adj = 1 )
graphics::par( tmpar )
invisible( sums )
}
###########################################################################################
curve.plot <- function( xy = seq(0,1,by=.25), y = c(0,.1,.5,.9,1),
z = graphics::locator(1,"n"), n=5, action="add",
fit = splines::interpSpline( xy$x, xy$y ),
backfit = TRUE, save.ends = 3, col = c("blue","red"), lwd = 4,
f = function( x ) x, finv = function( x ) x )
{
if( !is.list( xy )) {
xy <- data.frame( x = xy )
xy$y <- y
}
else
xy <- as.data.frame( xy )
if( !match( action, c("refresh","finish"), nomatch = 0 )) {
z <- as.data.frame(z)
tmp <- z$x > max( xy$x )
if( any( tmp )) {
if( all( tmp ))
return( xy )
z$x <- z$x[!tmp]
z$y <- z$y[!tmp]
}
}
remove.points <- function( xy, finv, save.ends = TRUE ) {
## find closest point after standardizing distances
usr <- graphics::par( "usr" )
tmp <- (( xy$x - z$x ) / diff( usr[1:2] )) ^ 2 +
(( xy$y - z$y ) / diff( usr[3:4] )) ^ 2
if( save.ends )
tmp <- tmp[ - c( 1, nrow( xy )) ]
tmp <- save.ends + min( seq( tmp )[ tmp == min( tmp ) ] )
tmpd <- xy[tmp,"y"]
tmpd <- finv( tmpd )
graphics::points( xy[tmp,"x"], tmpd, lwd = lwd, col = "white" )
tmp
}
for( i in 1:n) {
switch( action,
noaction =
return( xy )
,
add = {
graphics::points(z$x,z$y, lwd = lwd )
z$y <- f( z$y )
xy <- rbind(xy,z)
xy <- xy[order(xy$x),]
fit <- splines::interpSpline( xy$x, xy$y )
},
replace = {
graphics::points(z$x,z$y, lwd = lwd )
z$y <- f( z$y )
tmp <- remove.points( xy, finv, save.ends == 3 )
tmpp <- ( tmp > 1 & tmp < length( xy$x ))
if( save.ends != 2 | tmpp )
xy$x[tmp] <- z$x
if( save.ends != 1 | tmpp )
xy$y[tmp] <- z$y
fit <- splines::interpSpline( xy$x, xy$y )
},
delete = {
z$y <- f( z$y )
tmp <- remove.points( xy, finv, save.ends > 0 )
xy <- xy[-tmp,]
fit <- splines::interpSpline( xy$x, xy$y )
},
refresh =, finish = {
xy
}
)
tmpp <- stats::predict( fit )
graphics::lines( tmpp$x, finv( tmpp$y ), col = col[1], lwd = lwd )
adiff <- diff( stats::coef( fit )[,1] )
if( backfit & ( all( adiff < 0) || all( adiff > 0 ))) {
## backspline (not quite a spline) fit to inverse
tmpback <- stats::predict( splines::backSpline( fit ))
graphics::lines( tmpback$y, finv( tmpback$x ), col = col[2], lwd = lwd )
}
}
list( xy = xy[order(xy$x),], fit = fit )
}
###########################################################################################
cdf.lines <- function( data, fig = "mean value", nspline = 8,
conf = c(50,80,90,95), rescale = 1,
col = c("green","blue","red","orange") )
{
rate <- fig == "mean value"
n <- length( data )
data <- sort( data )
prob <- seq( n ) / ( n + 1 )
f <- - rescale * log( 1 - prob )
ylab <- "prob"
if( rate )
ylab <- "cum rate"
else
f <- 1 - exp( -f )
graphics::lines( data, f, lwd = 2 )
conf <- conf / 100
for( i in seq( length( conf ))) {
## lower confidence
tmp <- stats::qbinom( conf[i], n, prob, lower.tail = TRUE ) / ( n + 1 )
tmp <- 1 - exp( rescale * log( 1 - tmp ))
tmpna <- is.na( tmp )
if( any( tmpna ))
tmp[tmpna] <- f[1]
if( rate )
tmp <- - log( 1 - tmp )
graphics::lines( data, tmp, lty = 2, col = col[i] )
## upper confidence
tmp <- stats::qbinom( conf[i], n, prob, lower.tail = FALSE ) / ( n + 1 )
tmp <- 1 - exp( rescale * log( 1 - tmp ))
tmpna <- is.na( tmp )
if( any( tmpna ))
tmp[tmpna] <- f[n]
if( rate )
tmp <- - log( 1 - tmp )
graphics::lines( data, tmp, lty = 2, col = col[i] )
}
}
###########################################################################################
rspline <- function( meantime = 1,
fivepar = c(dispersion = 1, location = 0, intensity = 1, truncation = 0,
rejection = Inf ),
fit = NULL,
meanvalue = fit$meanvalue, invmvalue = fit$invmvalue,
span = Inf )
{
## dispersion = a, location = b, intensity = c
## truncation = -log(1-d), rejection = -log(1-e)
## y = a M^-1( G(d)+cV ) + b if 1 - exp( -cV ) < G(e)
## y = span if 1 - exp( -cV ) >= G(e)
## this is not quite right for truncation, as we know event happened before b
## if 1-exp(-cV) < d, but I am not sure how to pass that information along yet
default <- is.null( meanvalue )
## V ~ exp(1)
## intensity: V/c
## truncation: (G(d)+V)/c
rate <- ( fivepar["truncation"] + rexp( 1 )) / fivepar["intensity"]
## mean value inverse: M^-1( G(d)+V/c )
if( default )
y <- rate
else {
y <- stats::predict( invmvalue, rate )$y
## kludge to linearly extrapolate beyond cubic spline fit
if( is.na( y ))
y <- spline.extrapolate( meanvalue, invmvalue, rate )
}
## dispersion and location: y = a M^-1( cV ) + b
y <- meantime * fivepar["dispersion"] * y + fivepar["location"]
## rejection: y >= e? then set y to span
if( y > fivepar["rejection"] )
y <- span
y
}
###########################################################################################
spline.extrapolate <- function( meanvalue, invmvalue, x )
{
## linear extrapolation of inverse spline beyond upper end
coeff <- stats::coef( invmvalue )
nr <- nrow( coeff ) - 1
xknot <- splines::splineKnots( invmvalue )[nr+(0:1)]
yknot <- splines::splineKnots( meanvalue )[nr+1]
tmpr <- ( xknot[2] - xknot[1] )
slope <- coeff[nr,2] + tmpr * ( 2 * coeff[nr,3] + tmpr * 3 * coeff[nr,4] )
yknot + slope * ( x - xknot[2] )
}
###########################################################################################
### spline.design() is a prototype for designing spline curves
### Ultimately, pieces of spline.design, spline.temp() and spline.meanvalue()
### will be pulled out as subroutines to reduce code overlap
###########################################################################################
spline.design <- function (y = yinit, x = xinit, nspline = 8, xy = data.frame(x = x,
y = y), n = 1, horizontal = FALSE)
{
is.data <- !missing(y)
if (is.data) {
data <- y
if (missing(x))
x <- as.numeric(names(y))
datax <- x
ndata <- length(data)
choose <- round(seq(1, ndata, length = nspline))
xinit <- x <- x[choose]
yinit <- y <- y[choose]
}
else {
tmp <- seq(0, nspline - 1)
if (missing(x))
xinit <- tmp
else xinit <- x
if (missing(y))
yinit <- rep(50, nspline)
else yinit <- y
}
## plot curve and surrounding axes
graphics::par( mfrow = c(1,1), mar = rep(4.1,4))
plotit <- function( xy, fig = "temp", fit = splines::interpSpline( xy$x, xy$y ),
horizontal = FALSE, strip = .25, margin = .1 )
{
switch( fig, {
y <- xy$y
ylim <- range(y)
}
)
xlim <- range(xy$x)
xlim <- xlim + c(-1,1) * margin * diff( xlim )
if( horizontal ) {
if( diff( ylim ) == 0 )
ylim <- ylim * c(.75,1.25)
separator <- ylim[2]
ylim[2] <- ylim[2] + strip * diff( ylim )
}
else {
separator <- xlim[2]
xlim[2] <- xlim[2] + strip * diff( xlim )
}
axt <- c("n","s")
tmpar <- graphics::par( xaxt = axt[1+horizontal], yaxt = axt[2-horizontal] )
plot( xy$x, y, xlim = xlim, ylim = ylim, type="n", xlab = "", ylab = "" )
graphics::par( xaxt = "s", yaxt = "s" )
graphics::points( xy$x, y, lwd = 4 )
graphics::title( fig )
graphics::mtext( "time", 1, 2 )
graphics::mtext( fig, 2, 2 )
if( horizontal ) {
p <- pretty( c(ylim[1],separator) )
graphics::axis( 2, p[ p <= separator ] )
graphics::abline( h = separator, lty = 2 )
}
else {
p <- pretty( c(xlim[1],separator) )
graphics::axis( 1, p[ p <= separator ] )
graphics::abline( v = separator, lty = 2 )
}
curve.plot( xy, n = n, action = "refresh", fit = fit, backfit = FALSE,
save.ends = 0 )
separator
}
## place commands along right strip of plot, highlighting current command
plotcmd <- function( ans, fig, cmds, cmdlocs, usr, col = "green", rest = "black",
horizontal = TRUE )
{
ans <- c( ans, fig )
tmp <- is.na( match( cmds, ans ))
if( any( tmp )) for( i in unique( cmdlocs$adj )) {
tmpi <- tmp & i == cmdlocs$adj
if( any( tmpi ))
graphics::text( cmdlocs$x[tmpi], cmdlocs$y[tmpi], cmds[tmpi], col = rest, adj = i )
}
if( any( !tmp )) for( i in unique( cmdlocs$adj )) {
tmpi <- !tmp & i == cmdlocs$adj
if( any( tmpi ))
graphics::text( cmdlocs$x[tmpi], cmdlocs$y[tmpi], cmds[tmpi], col = col, adj = i )
}
}
cmds <- c("add","delete","replace","","finish","restart","refresh","rescale",
"","data","temp")
newlocs <- if( horizontal )
function( cmds, data = FALSE, usr )
{
if( !data )
cmds <- cmds[ cmds != "data" ]
n <- length( cmds )
blank <- seq( n )[ cmds == "" | cmds == " " ]
tmp <- diff(usr[3:4]) / 20
m <- mean( usr[1:2] )
y <- usr[4] + 0.5 * tmp - c( tmp * seq( blank[1] - 1 ), 0,
tmp * seq( blank[2] - blank[1] - 1 ), 0,
tmp * seq( n - blank[2] ))
x <- c( rep( usr[1], blank[1] - 1 ), mean( m, usr[1] ),
rep( m, blank[2] - blank[1] - 1 ), mean( m, usr[2] ),
rep( usr[2], n - blank[2] ))
adj <- c( rep( 0, blank[1] ),
rep( 0.5, blank[2] - blank[1] ),
rep( 1, n - blank[2] ))
tmp <- data.frame( x = x, y = y, adj = adj )
cmds[blank[2]] <- " "
row.names( tmp ) <- cmds
tmp
}
else
function( cmds, data = FALSE, usr )
{
if( !data )
cmds <- cmds[ cmds != "data" ]
n <- length( cmds )
blank <- seq( n )[ cmds == "" ]
tmp <- diff(usr[3:4]) / 20
m <- mean( usr[3:4] )
tmp <- c( usr[4] - tmp * seq( blank[1] - 1 ),
mean( m, usr[4] ),
m + tmp * ( seq( blank[1] + 1, blank[2] - 1 ) - mean( blank )),
mean( m, usr[3] ),
usr[3] + tmp * seq( n - blank[2] ))
tmp <- data.frame( x = rep( usr[2], n ), y = tmp, adj = rep( 1, n ))
cmds[blank[2]] <- " "
row.names( tmp ) <- cmds
tmp
}
fig <- "temp"
newans <- ans <- "replace"
graphics::par( mar = c(4.1,4.1,3.1,4.1),omi=rep(.25,4))
fit <- splines::interpSpline( xy$x, xy$y )
separator <- plotit( xy, fig, fit, horizontal )
usr <- graphics::par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
cmds <- row.names( cmdlocs )
plotcmd( ans, fig, cmds, cmdlocs, usr )
use.data <- FALSE
rescale.data <- 1
repeat {
## get command from plot using cursor
z <- graphics::locator(1,"n")
if(( !horizontal & z$x > separator ) | ( horizontal * z$y > separator )) {
if( horizontal ) { # need to look at both z&y
x <- abs( z$x - cmdlocs$x )
x <- x == min( x )
newans <- cmds[x]
z <- abs( z$y - cmdlocs$y )[x]
newans <- newans[ z == min( z ) ][1]
}
else {
z <- abs(z$y - cmdlocs$y )
newans <- cmds[ z == min( z ) ][1]
}
switch( newans,
finish =, refresh = {
separator <- plotit( xy, fig, fit, horizontal )
usr <- graphics::par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
data = {
use.data <- is.data & !use.data
if( is.data & !use.data )
plotit( xy, fig, fit, horizontal )
},
temp = {
fig <- newans
separator <- plotit( xy, fig, fit, horizontal )
usr <- graphics::par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
rescale = {
cat( "enter new values followed by RETURN key\n" )
tmpy <- readline( paste( "maximum ", fig, "(",
round( max( xy$y ), 2 ), "):", sep = "" ))
if( tmpy != "" ) {
tmpy <- suppressWarnings(as.numeric( tmpy )) / max( xy$y )
xy$y <- tmpy * xy$y
if( is.data )
rescale.data <- rescale.data * tmpy
}
tmpx <- readline( paste( "maximum time(",
round( max( xy$x ), 2 ), "):", sep = "" ))
if( tmpx != "" ) {
tmpx <- suppressWarnings(as.numeric( tmpx )) / max( xy$x )
xy$x <- tmpx * xy$x
}
fit <- splines::interpSpline( xy$x, xy$y )
separator <- plotit( xy, fig, fit, horizontal )
usr <- graphics::par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
restart = {
if( is.data )
rescale.data <- 1
xy <- data.frame( x = xinit, y = yinit )
fit <- splines::interpSpline( xy$x, xy$y )
separator <- plotit( xy, fig, fit, horizontal )
usr <- graphics::par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
add =, delete =, replace = {
ans <- newans
}
)
if( use.data ) {
rx <- range( xy$x )
dx <- range( datax )
graphics::lines( rx[1] + ( datax - dx[1] ) * diff( rx ) / diff( dx ), data * rescale.data )
}
plotcmd( ans, fig, cmds, cmdlocs, usr )
}
else {
fit <- curve.plot( xy, n = n, action = ans, z = z, fit = fit, backfit = FALSE,
save.ends = 0 )
xy <- fit$xy
fit <- fit$fit
ans <- "replace"
plotcmd( ans, fig, cmds, cmdlocs, usr )
}
if( newans == "finish" )
break
}
plotcmd( newans, fig, cmds, cmdlocs, "red" )
tmp <- curve.plot( xy, n = n, action = "refresh", backfit = FALSE, save.ends = 0 )
tmp
}
# --- Source: triangle.R ---
## $Id: triangle.R,v 0.9 2002/12/09 yandell@stat.wisc.edu Exp $
##
## Functions for Bland Ewing's modeling.
##
## Copyright (C) 2000,2001,2002 Brian S. Yandell.
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2, or (at your option) any
## later version.
##
## These functions are distributed in the hope that they will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## The text of the GNU General Public License, version 2, is available
## as http://www.gnu.org/copyleft or by writing to the Free Software
## Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
###########################################################################################
## rtri( n, width )
##
## plot.current( x, species )
## text.current( x, species )
##
###########################################################################################
###########################################################################################
### Tridiagonal Coordinate System S3 Classes & Algebra
###########################################################################################
tricoord <- function(a, b = NULL, c = NULL) {
if (is.data.frame(a) && all(c("a", "b", "c") %in% names(a))) {
res <- a
} else if (is.matrix(a) && ncol(a) == 3) {
res <- as.data.frame(a)
names(res) <- c("a", "b", "c")
} else if (!is.null(b) && !is.null(c)) {
res <- data.frame(a = a, b = b, c = c)
} else if (is.numeric(a) && length(a) == 3) {
res <- data.frame(a = a[1], b = a[2], c = a[3])
} else {
stop("Invalid tricoord input format")
}
class(res) <- c("tricoord", "data.frame")
res
}
`+.tricoord` <- function(e1, e2) {
# Handle vector offsets cleanly
if (is.numeric(e2) && length(e2) == 3) {
tricoord(e1$a + e2[1], e1$b + e2[2], e1$c + e2[3])
} else if (inherits(e2, "tricoord")) {
tricoord(e1$a + e2$a, e1$b + e2$b, e1$c + e2$c)
} else {
stop("Invalid right hand operand for tricoord addition")
}
}
`-.tricoord` <- function(e1, e2) {
if (is.numeric(e2) && length(e2) == 3) {
tricoord(e1$a - e2[1], e1$b - e2[2], e1$c - e2[3])
} else if (inherits(e2, "tricoord")) {
tricoord(e1$a - e2$a, e1$b - e2$b, e1$c - e2$c)
} else {
stop("Invalid right hand operand for tricoord subtraction")
}
}
###########################################################################################
rtri <- function( n, width, tri = matrix(0,3,n), roundoff = TRUE )
{
tri <- as.matrix( tri )
if( n == 1 ) {
xy <- stats::runif( 2, 0, width )
if( roundoff )
xy <- round( xy )
i <- sample( 3, 1 )
i1 <- 1 + i%%3
i2 <- 1 + (i+1)%%3
tri[i1,] <- tri[i1,] + xy[1]
tri[i2,] <- tri[i2,] - xy[2]
tri[i,] <- - ( tri[i1,] + tri[i2,] )
return( tri )
}
else {
xy <- data.frame( x = stats::runif( n, 0, width ),
y = - stats::runif( n, 0, width ))
if( roundoff )
xy <- round( xy )
out <- sample( 3, n, replace = TRUE )
for( i in 1:3 ) {
outi <- out == i
if( any( outi )) {
i1 <- 1 + i%%3
i2 <- 1 + (i+1)%%3
tri[i1,outi] <- tri[i1,outi] + xy$x[outi]
tri[i2,outi] <- tri[i2,outi] + xy$y[outi]
tri[i,outi] <- - ( tri[i1,outi] + tri[i2,outi] )
}
}
}
tri
}
###########################################################################################
car2tri.default <- function(x,y)
car2tri( cbind( x, y ))
car2tri <- function( xy, xmult = ( 2 + sq3 ) / 4, ymult = ( 3 + 2 * sq3 ) / 12,
sq3 = sqrt( 3 ))
{
# if( !is.matrix( xy ))
# xy <- t( as.matrix( xy ))
aa <- xmult * xy[,1] - ymult * xy[,2]
bb <- - xmult * xy[,1] - ymult * xy[,2]
cc <- -( aa + bb )
rbind( a = aa, b = bb, c = cc )
}
###########################################################################################
tri2car.default <- function(aa,bb,cc=-(aa+bb))
tri2car( rbind( aa, bb, cc ))
tri2car <- function(tri, xmult = 2 / ( 2 + sq3 ), ymult = 6 / ( 3 + 2 * sq3 ),
sq3 = sqrt( 3 ))
{
if( inherits(tri, "tricoord") ) {
# If the user passes our S3 tricoord dataframe, map it correctly natively.
x <- ( tri$a - tri$b ) * xmult
y <- - ( tri$a + tri$b ) * ymult
} else {
if( !is.matrix( tri ))
tri <- as.matrix( tri )
x <- ( tri[1,] - tri[2,] ) * xmult
y <- - ( tri[1,] + tri[2,] ) * ymult
}
data.frame( x = x, y = y )
}
###########################################################################################
cardist <- function( xy )
sqrt( xy[,1]^2 + xy[,2]^2 )
###########################################################################################
tridist <- function( tri )
apply( tri, 1, max )
###########################################################################################
gasket <- function( aa, bb )
{
n <- length( aa )
pp <- c(-1,1,0,1)
dda <- diff( aa )
ddb <- diff( bb )
ss <- sign( sign( dda ) - sign( ddb ))
dda <- 2 - abs( dda )
ddb <- 2 - abs( ddb )
aa <- 2 * aa
aa <- c( aa[1], rbind( aa[-n] + pp[dda+1+ss], aa[-1] + pp[dda+1-ss], aa[-1] ))
bb <- 2 * bb
bb <- c( bb[1], rbind( bb[-n] + pp[ddb+1-ss], bb[-1] + pp[ddb+1+ss], bb[-1] ))
data.frame( aa = aa, bb = bb )
}
# --- Source: substrate_triangle.R ---
get_substrate_grid <- function(width, step = 1, orientation = "up") {
pts <- expand.grid(a = seq(0, width - step, by = step),
b = seq(0, width - step, by = step))
if (orientation == "up") {
pts <- subset(pts, a + b <= width - step)
pts$c <- -(pts$a + pts$b)
} else {
pts <- subset(pts, a + b <= width - step)
pts$a <- -pts$a
pts$b <- -pts$b
pts$c <- -(pts$a + pts$b)
}
return(pts)
}
substrate_topology <- function(width = 10, step = 1) {
W <- width - step
# Topology adjacency offsets
list(
fr2 = list(offset = c(0, 0, 0), dir = "down"),
fr1 = list(offset = c(-W, -W, 2*W), dir = "up"),
fr3 = list(offset = c(-W, 0, W), dir = "up"),
fr4 = list(offset = c(0, -W, W), dir = "up"),
tw1 = list(offset = c(-W, 0, W), dir = "down"),
twig = list(offset = c(-W, 0, W), dir = "down"),
tw2 = list(offset = c(-2*W, 0, 2*W), dir = "up"),
lftop = list(offset = c(-2*W, W, W), dir = "down"),
lfbot = list(offset = c(-3*W, W, 2*W), dir = "up")
)
}
create_substrate <- function(topology, width = 10, step = 1) {
W <- width - step
all_points <- data.frame()
labels_df <- data.frame()
poly_df <- data.frame()
for (sub in names(topology)) {
cfg <- topology[[sub]]
grid <- get_substrate_grid(width, step, cfg$dir)
o_a <- cfg$offset[1]
o_b <- cfg$offset[2]
o_c <- cfg$offset[3]
# Needs tricoord and tri2car which are presumably exported/available from R/triangle.R
grid_tri <- tricoord(grid$a, grid$b, grid$c)
grid_tri <- grid_tri + cfg$offset
car_pts <- tri2car(grid_tri)
car_pts$substrate <- sub
all_points <- rbind(all_points, car_pts)
# Determine bounds and midpoints for side labels 1,2,3
if (cfg$dir == "up") {
v_top <- c(o_a, o_b, o_c)
v_br <- c(o_a + W, o_b, o_c - W)
v_bl <- c(o_a, o_b + W, o_c - W)
m1 <- (v_top + v_bl) / 2
m2 <- (v_top + v_br) / 2
m3 <- (v_bl + v_br) / 2
centroid <- (v_top + v_br + v_bl) / 3
p_mat <- cbind(v_top, v_br, v_bl)
} else {
v_bot <- c(o_a, o_b, o_c)
v_tr <- c(o_a, o_b - W, o_c + W)
v_tl <- c(o_a - W, o_b, o_c + W)
m1 <- (v_bot + v_tr) / 2
m2 <- (v_bot + v_tl) / 2
m3 <- (v_tl + v_tr) / 2
centroid <- (v_bot + v_tr + v_tl) / 3
p_mat <- cbind(v_bot, v_tr, v_tl)
}
# Interpolate slightly towards the centroid to put text "just inside" the edges
w_in <- 0.25 # weight towards centroid
l1 <- m1 * (1 - w_in) + centroid * w_in
l2 <- m2 * (1 - w_in) + centroid * w_in
l3 <- m3 * (1 - w_in) + centroid * w_in
mat_l <- cbind(l1, l2, l3)
car_l <- tri2car(mat_l)
car_l$label <- c("1", "2", "3")
car_l$substrate <- sub
labels_df <- rbind(labels_df, car_l)
car_p <- tri2car(p_mat)
car_p$substrate <- sub
poly_df <- rbind(poly_df, car_p)
}
centers <- stats::aggregate(cbind(x,y) ~ substrate, data=all_points, mean)
obj <- list(
points = all_points,
labels = labels_df,
poly = poly_df,
centers = centers,
topology = topology
)
class(obj) <- "substrate"
return(obj)
}
autoplot.substrate <- function(object, ...) {
ggplot2::ggplot() +
# Draw the black boundary lines outlining the substrates exactly over outer dots
ggplot2::geom_polygon(data=object$poly, ggplot2::aes(x=x, y=y, group=substrate), fill=NA, color="black", linewidth=0.7) +
# Plot grid dots
ggplot2::geom_point(data=object$points, ggplot2::aes(x=x, y=y, color=substrate), size=1.5) +
# Plot Substrate Labels (Centers)
ggplot2::geom_text(data=object$centers, ggplot2::aes(x=x, y=y, label=substrate), color="black", fontface="bold", size=5) +
# Axis side numbers
ggplot2::geom_text(data=object$labels, ggplot2::aes(x=x, y=y, label=label), color="darkred", fontface="bold", size=4) +
ggplot2::theme_void() +
ggplot2::coord_fixed() +
ggplot2::ggtitle("Ewing Tridiagonal Substrate Network Mapping")
}
create_hex_overlay <- function(object, step = 1) {
pts <- if (inherits(object, "substrate")) object$points else object
if (is.null(pts) || nrow(pts) == 0) return(data.frame())
xmult <- 2 / (2 + sqrt(3))
ymult <- 6 / (3 + 2 * sqrt(3))
d <- step * sqrt(xmult^2 + ymult^2)
r <- d / sqrt(3)
angles <- (seq(0, 5) * 60 + 30) * pi / 180
dx <- r * cos(angles)
dy <- r * sin(angles)
n_pts <- nrow(pts)
hex_list <- vector("list", n_pts)
for (i in seq_len(n_pts)) {
px <- pts$x[i] + dx
py <- pts$y[i] + dy
sub <- pts$substrate[i]
hex_list[[i]] <- data.frame(
x = px,
y = py,
cell_id = i,
substrate = sub,
stringsAsFactors = FALSE
)
}
do.call(rbind, hex_list)
}
# --- Source: ewing_substrate.R ---
ewing_substrate <- function( community,
species,
headstuff = c( 0, "start", sum( to.plot )),
units = getOrgFeature( community, species[1], "units" ),
right = species[1], adj = c(0,.5,1),
show_sub = NULL,
step = 0,
layout = c("facet", "hex"),
width = 10,
step_density = 1,
rescale = TRUE,
x_var = c("step", "time"),
...)
{
x_var <- match.arg(x_var)
if (inherits(community, "isle_royale_sim")) {
p_map <- autoplot(community$habitat_overlay, show_landmarks = TRUE)
moose_sf <- sf::st_as_sf(community$moose_pop, coords = c("lon", "lat"), crs = sf::st_crs(community$habitat_overlay$layer))
wolf_sf <- sf::st_as_sf(community$wolf_pop, coords = c("lon", "lat"), crs = sf::st_crs(community$habitat_overlay$layer))
hdr_str <- if (x_var == "step") paste0("Step ", community$nstep) else paste0("Time Units ", community$nstep)
p_map <- p_map +
ggplot2::geom_sf(data = moose_sf, color = "#27ae60", shape = 21, fill = NA, stroke = 1.0, size = 0.8, alpha = 0.85) +
ggplot2::geom_sf(data = wolf_sf, color = "#e74c3c", shape = 21, fill = NA, stroke = 1.4, size = 1.5, alpha = 0.95) +
ggplot2::ggtitle(paste0("Isle Royale Substrate Plot (", hdr_str, ")"))
return(p_map)
}
layout <- match.arg(layout)
if (length(species) > 1) {
res_list <- lapply(species, function(sp) {
df <- ewing_substrate(community = community, species = sp, headstuff = headstuff,
units = units, right = right, adj = adj, show_sub = show_sub,
step = step, layout = layout, width = width, step_density = step_density,
rescale = rescale, ...)
if (!is.null(df) && nrow(df) > 0) {
df$species <- sp
}
df
})
res_list <- res_list[!sapply(res_list, is.null)]
if (length(res_list) == 0) return(NULL)
combined <- do.call(rbind, res_list)
attr(combined, "species") <- paste(species, collapse = " & ")
step_val <- if (!is.null(community$step)) community$step else if (!is.null(community$count$step)) community$count$step else if (!is.null(attr(community, "nstep"))) attr(community, "nstep") else step
attr(combined, "step") <- step_val
attr(combined, "layout") <- layout
attr(combined, "width") <- width
attr(combined, "step_density") <- step_density
class(combined) <- c("ewing_substrate", class(combined))
return(combined)
}
## plot current stages for species (except random parasites)
organism <- get.species( community, species )[,-1]
if(is.null(organism)) # species is not in community
return(NULL)
future <- getOrgFuture( community, species, c("color","pch") )
# Substrate names mapping (e.g. fr1, fr2, fr3, fr4, twig, lftop, lfbot)
substrate_feat <- getOrgFeature( community, species, "substrate")
sub_interact <- getOrgInteract(community, substrate_feat, species)
substrates <- rownames(sub_interact)
if (is.null(substrates) || length(substrates) == 0) {
substrates <- names(getOrgInteract(community, substrate_feat, substrate_feat))
}
if (is.null(show_sub)) show_sub <- substrates
position <- paste( "pos", letters[1:3], sep = "." )
if (layout == "hex") {
topo <- substrate_topology(width = width, step = step_density)
n_org <- ncol(organism)
gx <- numeric(n_org)
gy <- numeric(n_org)
sub_indices <- organism["sub.stage", ]
org_sub_names <- substrates[sub_indices]
# Process organisms per substrate patch to rescale local coordinates into substrate surface triangle
unique_subs <- unique(org_sub_names)
for (sub in unique_subs) {
idx <- which(org_sub_names == sub)
target_name <- sub
if (!target_name %in% names(topo)) {
if (target_name == "twig") target_name <- "tw1"
if (target_name == "tw1") target_name <- "twig"
}
cfg <- topo[[target_name]]
pa <- organism["pos.a", idx]
pb <- organism["pos.b", idx]
pc <- organism["pos.c", idx]
if (!is.null(cfg)) {
# Determine substrate surface width (allows substrates of different sizes in future)
W_sub <- if (!is.null(cfg$width)) (cfg$width - step_density) else (width - step_density)
if (rescale) {
amin <- min(pa); amax <- max(pa)
bmin <- min(pb); bmax <- max(pb)
u <- if (amax > amin) (pa - amin) / (amax - amin) else rep(0.5, length(idx))
v <- if (bmax > bmin) (pb - bmin) / (bmax - bmin) else rep(0.5, length(idx))
# 15% inner padding to ensure symbols sit comfortably inside substrate polygon borders
u_m <- 0.15 + 0.70 * u
v_m <- 0.15 + 0.70 * v
a_p <- u_m * W_sub
b_p <- v_m * (W_sub - a_p)
c_p <- -(a_p + b_p)
} else {
a_p <- pa
b_p <- pb
c_p <- pc
}
off <- cfg$offset
if (cfg$dir == "up") {
ga <- a_p + off[1]
gb <- b_p + off[2]
gc <- c_p + off[3]
} else {
ga <- -a_p + off[1]
gb <- -b_p + off[2]
gc <- -c_p + off[3]
}
car <- tri2car(tricoord(ga, gb, gc))
gx[idx] <- car$x
gy[idx] <- car$y
} else {
car <- tri2car(organism[position, idx, drop = FALSE])
gx[idx] <- car$x
gy[idx] <- car$y
}
}
xy <- data.frame(x = gx, y = gy)
} else {
xy <- tri2car( organism[position,] )
}
dat <- dplyr::filter(
dplyr::mutate(
tibble::tibble(xy),
stage = organism["stage",],
substrate = substrates[organism["sub.stage",]],
pchar = factor(as.character( future$pch[.data$stage + 1] ), levels = unique(as.character(future$pch))),
color = as.character( future$color[.data$stage + 1] ),
species = species),
.data$substrate %in% show_sub)
attr(dat, "species") <- species
step_val <- if (!is.null(community$step)) community$step else if (!is.null(community$count$step)) community$count$step else if (!is.null(attr(community, "nstep"))) attr(community, "nstep") else step
attr(dat, "step") <- step_val
attr(dat, "layout") <- layout
attr(dat, "width") <- width
attr(dat, "step_density") <- step_density
class(dat) <- c("ewing_substrate", class(dat))
dat
}
ggplot_ewing_substrate <- function(object,
xlab = "horizontal", ylab = "vertical",
layout = attr(object, "layout"),
width = attr(object, "width"),
step_density = attr(object, "step_density"),
layers = c("poly", "hex", "organisms", "centers", "labels"),
...)
{
if (inherits(object, "ggplot")) return(object)
if (is.null(layout)) layout <- "facet"
if (is.null(width)) width <- 10
if (is.null(step_density)) step_density <- 1
species <- attr(object, "species")
step <- attr(object, "step")
# Allows same color for different pchar, but only one color per pchar.
tmp <- dplyr::arrange(
dplyr::distinct(
dplyr::distinct(object, .data$pchar, .data$color),
.data$pchar, .keep_all = TRUE),
.data$pchar)
col.palate <- tmp$color
names(col.palate) <- as.character(tmp$pchar)
if (layout == "hex") {
topo <- substrate_topology(width = width, step = step_density)
sub_obj <- create_substrate(topo, width = width, step = step_density)
hex_overlay <- create_hex_overlay(sub_obj, step = step_density)
p <- ggplot2::ggplot()
if ("poly" %in% layers && nrow(sub_obj$poly) > 0) {
p <- p + ggplot2::geom_polygon(data = sub_obj$poly, ggplot2::aes(x = x, y = y, group = substrate),
fill = NA, color = "black", linewidth = 0.7)
}
if ("hex" %in% layers && nrow(hex_overlay) > 0) {
p <- p + ggplot2::geom_polygon(data = hex_overlay, ggplot2::aes(x = x, y = y, group = cell_id),
fill = NA, color = "gray75", linewidth = 0.3)
}
if ("organisms" %in% layers && nrow(object) > 0) {
p <- p + ggplot2::geom_text(data = object, ggplot2::aes(x = x, y = y, label = pchar, color = pchar),
fontface = "bold", size = 4) +
ggplot2::scale_color_manual(name = "Stage", values = col.palate) +
ggplot2::guides(color = ggplot2::guide_legend(override.aes = list(label = names(col.palate))))
}
if ("centers" %in% layers && nrow(sub_obj$centers) > 0) {
p <- p + ggplot2::geom_text(data = sub_obj$centers, ggplot2::aes(x = x, y = y, label = substrate),
color = "black", fontface = "bold", size = 4.5)
}
if ("labels" %in% layers && nrow(sub_obj$labels) > 0) {
p <- p + ggplot2::geom_text(data = sub_obj$labels, ggplot2::aes(x = x, y = y, label = label),
color = "darkred", fontface = "bold", size = 3.5)
}
return(p + ggplot2::theme_void() +
ggplot2::theme(plot.margin = ggplot2::margin(2, 2, 2, 2, "pt")) +
ggplot2::coord_fixed() +
ggplot2::ggtitle(paste(species, "on Hex Substrate Grid at", step, "steps")))
}
# Default Facet View by Substrate Component
ggplot2::ggplot(object) +
ggplot2::aes(.data$x, .data$y, label = .data$pchar, col = .data$pchar) +
ggplot2::geom_text() +
ggplot2::facet_wrap(~ substrate) +
ggplot2::xlab(xlab) +
ggplot2::ylab(ylab) +
ggplot2::scale_color_manual(name = "Stage", values = col.palate) +
ggplot2::guides(color = ggplot2::guide_legend(override.aes = list(label = names(col.palate)))) +
ggplot2::ggtitle(paste(species, "on substrate at", step, "steps"))
}
autoplot.ewing_substrate <- function(object, ...)
ggplot_ewing_substrate(object, ...)
# --- Source: ewing_ageclass.R ---
ewing_ageclass <- function(community, substrate = TRUE, total = TRUE,
normalize = TRUE, ...) {
nsim_val <- NULL
nstep_val <- NULL
if (inherits(community, "isle_royale_sim")) {
if (is.null(community$history) || nrow(community$history) == 0) return(NULL)
out <- community$history
if (!substrate) {
out <- dplyr::filter(out, .data$Type != "substrate")
}
if (total) {
tot <- dplyr::mutate(
dplyr::ungroup(
dplyr::summarize(
dplyr::group_by(out, .data$Species, .data$step, .data$time, .data$Type),
Count = sum(.data$Count),
.groups = "drop"
)
),
State = "total"
)
out <- dplyr::bind_rows(out, tot)
}
if (normalize) {
out <- dplyr::ungroup(
dplyr::mutate(
dplyr::group_by(out, .data$Species, .data$State, .data$Type),
Count = {
m <- max(.data$Count, na.rm = TRUE)
if (!is.na(m) && m > 0) .data$Count / m else 0
}
)
)
}
ordered_levels <- unique(c("calf", "yearling", "adult", "senior", "pup", "subadult", "total"))
out$State <- factor(out$State, levels = ordered_levels[ordered_levels %in% unique(out$State)])
attr(out, "nstep") <- community$nstep
attr(out, "units") <- "days"
class(out) <- c("ewing_ageclass", class(out))
return(out)
}
if (inherits(community, "ewing_discrete")) {
nsim_val <- attr(community, "nsim")
nstep_val <- attr(community, "nstep")
community <- community[[1]]
}
count <- readCount(community)
if(!length(count)) return(NULL)
species <- names(count)
if(is.null(species)) return(NULL)
ageclass <- list()
for( i in species ) {
ageclass[[i]] <- levels( getOrgFuture( community, i, "ageclass" ))
}
substrates <- list()
for( i in species ) {
substrates[[i]] <- levels( getOrgInteract( community,, i, "substrate" ))
}
out <- list()
for(i in species) {
out[[i]] <- dplyr::mutate(
tidyr::pivot_longer(
tibble::tibble(
as.data.frame(count[[i]])),
dplyr::any_of(c(ageclass[[i]], substrates[[i]])),
names_to = "State",
values_to = "Count"),
Type = ifelse(.data$State %in% substrates[[i]], "substrate", "ageclass"))
}
out <- dplyr::bind_rows(out, .id = "Species")
if(!substrate) {
out <- dplyr::filter(out, .data$Type != "substrate")
}
if(total) {
tot <- dplyr::mutate(
dplyr::ungroup(
dplyr::summarize(
dplyr::group_by(
out,
.data$Species, .data$step, .data$time, .data$future, .data$Type),
Count = sum(.data$Count),
.groups = "drop")
),
State = "total")
out <- dplyr::bind_rows(out, tot)
}
if(normalize) {
out <- dplyr::ungroup(
dplyr::mutate(
dplyr::group_by(
out,
.data$Species, .data$State, .data$Type),
Count = {
m <- max(.data$Count, na.rm = TRUE)
if (!is.na(m) && m > 0) .data$Count / m else 0
}))
}
subs <- if (substrate) unlist(substrates) else NULL
if (length(species) > 1) {
ordered_levels <- unique(c(ageclass[[species[1]]], "total", unlist(ageclass[species[-1]]), subs))
} else {
ordered_levels <- unique(c(unlist(ageclass), "total", subs))
}
out$State <- factor(out$State, levels = ordered_levels)
attr(out, "nstep") <- if (!is.null(nstep_val)) nstep_val else attr(community, "nstep")
if (is.null(attr(out, "nstep")) && !is.null(out$step)) {
attr(out, "nstep") <- max(out$step, na.rm = TRUE)
}
attr(out, "nsim") <- if (!is.null(nsim_val)) nsim_val else attr(community, "nsim")
attr(out, "units") <- if (inherits(community, "isle_royale_sim")) "days" else tryCatch(getOrgFeature(community, species[1], "units"), error = function(e) "time")
class(out) <- c("ewing_ageclass", class(out))
out
}
ggplot_ewing_ageclass <- function(object, main = NULL, title = NULL, x_var = c("step", "time"), time_unit = NULL, ... )
{
x_var <- match.arg(x_var)
if (is.null(time_unit)) {
time_unit <- attr(object, "units")
}
if (is.null(time_unit) || is.na(time_unit) || time_unit == "NA") {
time_unit <- "time"
}
if (is.null(title)) title <- main
if (is.null(title)) {
nstep <- attr(object, "nstep")
if (is.null(nstep) && !is.null(object$step)) {
nstep <- max(object$step, na.rm = TRUE)
}
nsim <- attr(object, "nsim")
unit_str <- if (x_var == "step") "steps" else time_unit
time_hdr <- if (x_var == "step") "Steps" else paste0(toupper(substring(time_unit, 1, 1)), substring(time_unit, 2))
if (!is.null(nstep)) {
if (!is.null(nsim) && nsim > 1) {
title <- paste0("Age Classes over ", time_hdr, " (", nstep, " ", unit_str, ", nsim = ", nsim, ")")
} else {
title <- paste0("Age Classes over ", time_hdr, " (", nstep, " ", unit_str, ")")
}
} else {
title <- paste0("Age Classes over ", time_hdr)
}
}
species_vec <- unique(as.character(object$Species))
if (length(species_vec) == 0) species_vec <- "Organism"
p_list <- list()
for (sp in species_vec) {
df_sp <- object[object$Species == sp, , drop = FALSE]
if (is.factor(df_sp$State)) {
df_sp$State <- droplevels(df_sp$State)
}
x_col <- if (x_var == "step" && "step" %in% names(df_sp)) "step" else "time"
x_lbl <- if (x_var == "step") "steps" else time_unit
sp_title <- paste(toupper(substring(sp, 1, 1)), substring(sp, 2), " Age Classes", sep = "")
p_sub <- ggplot2::ggplot(df_sp, ggplot2::aes(x = .data[[x_col]], y = .data$Count, col = .data$State, group = .data$State)) +
ggplot2::geom_step(na.rm = TRUE, linewidth = 0.8) +
ggplot2::geom_point(size = 2, na.rm = TRUE) +
ggplot2::theme_minimal() +
ggplot2::labs(
title = sp_title,
x = x_lbl,
y = "Count",
color = "Age Class"
) +
ggplot2::theme(
plot.title = ggplot2::element_text(size = 11, face = "bold", hjust = 0.5),
legend.position = "right"
)
p_list[[sp]] <- p_sub
}
if (length(p_list) == 1) {
return(p_list[[1]] + ggplot2::ggtitle(title))
}
grid_plots <- cowplot::plot_grid(plotlist = p_list, ncol = length(p_list), align = "h")
title_widget <- cowplot::ggdraw() +
cowplot::draw_label(title, fontface = 'bold', x = 0.5, hjust = 0.5, size = 13)
cowplot::plot_grid(title_widget, grid_plots, ncol = 1, rel_heights = c(0.12, 1))
}
autoplot.ewing_ageclass <- function(object, x_var = c("step", "time"), ...) {
x_var <- match.arg(x_var)
ggplot_ewing_ageclass(object, x_var = x_var, ...)
}
# --- Source: ewing_envelope.R ---
ewing_envelope <- function(object, species, item, ordinate = "time", increment = 0.5) {
# Pull out `ordinate` and `item` for each run
pulled <-
tidyr::fill(
dplyr::arrange(
tidyr::pivot_wider(
dplyr::bind_rows(
purrr::map(
object,
function(x) {
dplyr::distinct(
as.data.frame(x[[species]][,c(ordinate, item)]),
.data[[ordinate]],
.keep_all = TRUE)
}),
.id = "run"),
names_from = "run",
values_from = item),
.data[[ordinate]]),
-dplyr::matches(ordinate))
out <- GET::create_curve_set(list(r = as.matrix(pulled)[,1],
obs = as.matrix(pulled[,-1])))
class(out) <- c("ewing_envelope", class(out))
attr(out, "count") <- attr(object, "count")
attr(out, "nstep") <- attr(object, "nstep")
attr(out, "nsim") <- attr(object, "nsim")
attr(out, "species") <- species
attr(out, "item") <- item
attr(out, "ordinate") <- ordinate
out
}
ewing_envelopes <- function(object) {
species <- attr(object, "species")
items <- attr(object, "items")
ordinate <- attr(object, "ordinate")
nstep <- attr(object, "nstep")
count <- attr(object, "count")
nsim <- attr(object, "nsim")
confidence <- (nsim > 2)
envs <- as.list(species)
names(envs) <- species
if(confidence) {
confs <- envs
} else {
confs <- NULL
}
for(specy in species) {
env1 <- as.list(items[[specy]])
if(confidence) {
conf1 <- env1
}
for(item in items[[specy]]) {
env1[[item]] <- ewing_envelope(object, specy, item, ordinate)
if(confidence) {
conf1[[item]] <- GET::fBoxplot(env1[[item]], type = 'area')
}
}
envs[[specy]] <- env1
if(confidence) {
confs[[specy]] <- conf1
}
}
object <- list(env = envs, conf = confs)
class(object) <- c("ewing_envelopes", class(object))
attr(object, "species") <- species
attr(object, "items") <- items
attr(object, "ordinate") <- ordinate
attr(object, "nstep") <- nstep
attr(object, "count") <- count
attr(object, "nsim") <- nsim
attr(object, "confidence") <- confidence
object
}
summary.ewing_envelopes <- function(object, species = NULL, verbose = TRUE, ...) {
# object$conf[[specy]][[item]] is time by 6-num boxplot summary
if(verbose) {
nstep <- attr(object, "nstep")
count <- attr(object, "count")
nsim <- attr(object, "nsim")
cat(nsim, "Runs of ",
nstep, "Steps for",
paste(names(count), count, sep = "=", collapse = ", "), "\n")
}
out <- print(object, species, ...)
if(!is.null(out)) {
out <- dplyr::ungroup(
dplyr::filter(
dplyr::group_by(
out,
.data$species, .data$item),
(.data$r == 0) | (.data$r == max(.data$r))))
}
out
}
print.ewing_envelopes <- function(x, species = NULL, ...) {
# x$conf[[specy]][[item]] is time by 6-num boxplot summary
if(is.null(x$conf)) {
return(NULL)
}
out <- dplyr::bind_rows(
purrr::map(
x$conf,
function(x) {
# somehow get summary across species and items using as.data.frame
x <- x[names(x) != ""]
dplyr::bind_rows(
purrr::map(
x,
as.data.frame),
.id = "item")
}),
.id = "species")
if(!is.null(species)) {
if(species %in% unique(out$species)) {
sp <- species
out <- dplyr::filter(out, species == sp)
}
}
dplyr::mutate(out, dplyr::across(where(is.numeric), function(x) pmax(x,0)))
out
}
ggplot_ewing_envelopes <- function(object, confidence = FALSE, main = "", ...) {
if(inherits(object, "ewing_discrete")) {
object <- ewing_envelopes(object)
}
species <- attr(object, "species")
items <- attr(object, "items")
ordinate <- attr(object, "ordinate")
nstep <- attr(object, "nstep")
count <- attr(object, "count")
nsim <- attr(object, "nsim")
confidence <- confidence & attr(object, "confidence")
patch <- list()
for(specy in species) {
p <- list()
for(item in items[[specy]]) {
if(confidence) {
p[[item]] <- plot(object$conf[[specy]][[item]], main = main) +
ggplot2::labs(x = "time", y = item) +
ggplot2::ggtitle(main) +
ggplot2::ylim(0, NA)
} else {
p[[item]] <- ggplot_ewing_envelope(object$env[[specy]][[item]])
}
}
patch[[specy]] <- cowplot::plot_grid(plotlist = p, nrow = length(p))
}
# NEED TO get attribute count and nstep in here
patch <- cowplot::plot_grid(plotlist = patch, ncol = length(patch))
# Add a title. <https://wilkelab.org/cowplot/articles/plot_grid.html>
title <- cowplot::ggdraw() +
cowplot::draw_label(
paste(nsim, "Runs of ", nstep, "Steps for",
paste(species, count, sep = "=", collapse = ", ")),
x = 0, hjust = 0
) +
ggplot2::theme(
# add margin on the left of the drawing canvas,
# so title is aligned with left edge of first plot
plot.margin = ggplot2::margin(0, 0, 0, 7)
)
cowplot::plot_grid(title, patch, ncol = 1, rel_heights = c(0.1, 1))
}
ggplot_ewing_envelope <- function(object, cols = c("#21908CFF", "#440154FF", "#5DC863FF"),
main = "", ...) {
# Kludge. GET::forder needs at least 3 points; cols can be at most length(object).
lcols <- length(cols)
nsim <- ncol(object$funcs)
if(nsim >= min(3, lcols)) {
A <- GET::forder(object, measure = 'area')
lcols <- min(lcols, length(object))
idx <- order(A)[seq_len(lcols)]
cols <- cols[seq_len(lcols)]
} else {
lcols <- nsim
idx <- seq_len(lcols)
cols <- cols[seq_len(lcols)]
}
item <- attr(object, "item")
species <- attr(object, "species")
ordinate <- attr(object, "ordinate")
if(length(object) >= 50) {
p <- plot(object, idx = idx, col_idx = cols, main = main)
} else {
p <- plot(object)
}
p +
ggplot2::labs(x = ordinate, y = item) +
ggplot2::ggtitle(paste(species, item))
}
autoplot.ewing_envelope <- function(object, ...) {
ggplot_ewing_envelope(object, ...)
}
# --- Source: ewing_snapshot.R ---
ewing_snapshot <- function(object, step = 0, ...)
{
out <- list(step = step,
ageclass = ewing_ageclass(object, ...))
species <- get.species(object)
subs <- list()
for(j in species) {
subs[[j]] <- ewing_substrate(object, j, step = step, ...)
}
out$substrate <- subs
class(out) <- c("ewing_snapshot", "ewing", "list")
out
}
# --- Source: temp.R ---
## $Id: temp.R,v 1.0 2002/12/09 yandell@stat.wisc.edu Exp $
##
## Functions for Bland Ewing's modeling.
##
## Copyright (C) 2000,2001,2002 Brian S. Yandell.
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2, or (at your option) any
## later version.
##
## These functions are distributed in the hope that they will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## The text of the GNU General Public License, version 2, is available
## as http://www.gnu.org/copyleft or by writing to the Free Software
## Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
###########################################################################################
## initTemp( community, lo.hour, hi.hour ) ### creates Temperature object
## activeTemp( community, lo.hour, hi.hour ) ### updates Temperature object
## checkTime( ) # check if activeTemp needs updating to cover time interval
##
## temp.design( )
## temp.plot( )
###########################################################################################
rescale.temp <- function( low, high, newry = ry, newrx = rx,
ry = range( c( stats::predict( low, low$knots )$y, stats::predict( high, high$knots )$y )))
{
if( max( abs( ry - newry )) > 0 ) {
tmpy <- diff( newry ) / diff( ry )
low$coefficients <- low$coefficients * tmpy
high$coefficients <- high$coefficients * tmpy
tmpy <- newry[1] - ry[1] * tmpy
low$coefficients[,1] <- tmpy + low$coefficients[,1]
high$coefficients[,1] <- tmpy + high$coefficients[,1]
}
rx <- range( low$knots, high$knots )
if( max( abs( rx - newrx )) > 0 ) {
newx <- diff( newrx ) / diff( rx )
low$knots <- newrx[1] + newx * low$knots
high$knots <- newrx[1] + newx * high$knots
tmp <- 1
for( i in seq( 2, ncol( low$coefficients ))) {
tmp <- tmp * newx
low$coefficients[,i] <- low$coefficients[,i] / tmp
high$coefficients[,i] <- high$coefficients[,i] / tmp
}
}
list( low = low, high = high )
}
###########################################################################################
### Temperature data structure
### Min minimum temperature for degree-day computations
### Time list of hours when temperature changes through a day
### Base list of temperature shifts through a day
### list names indicate first day Hour and Base applies
### Low spline fit for daily low temperatures
### High spline fit for daily high temperatures
### DegreeDay spline fit (hour->DD) for currently active days
### Hour ramped backspline fit (DD->hour) for currently active days
### Idea is that DegreeDay and Hour are updated whenever a future event is scheduled
### past the latest future event already scheduled. At that time, the first
### and last time of fit are both adjusted. This can be done without rebuilding
### the whole spline fit by appropriate adjustment of knots and coefficents.
### Steps: (1) combine Hour, Low and High to get knots
### (2) build spline coefficients
###########################################################################################
temp.spline <- function( community, hour, temp, start = 0,
mintemp = getTemp( community, "Min" ), cumulative = TRUE,
mult = getTemp( community, "Unit" ) )
{
if( is.data.frame( hour ) & missing( temp )) {
temp <- hour$temp
hour <- hour$hour
}
## drop low value if ones on either side are low
low <- temp <= mintemp
toolow <- low & c(FALSE,low[-length(low)]) & c(low[-1],FALSE)
hour <- hour[!toolow]
temp <- temp[!toolow]
lh <- length( hour )
## expand single low in middle to two
low <- temp <= mintemp
toolow <- low & c(FALSE,!low[-length(low)]) & c(!low[-1],FALSE)
if( any( toolow )) {
hour <- c(hour,hour[toolow])
temp <- c(temp,temp[toolow])
temp <- temp[ order( hour ) ]
hour <- sort( hour )
}
## truncate on left
low <- temp <= mintemp & c(temp[-1] > mintemp,FALSE)
low1 <- c(FALSE,low[-lh])
if( any( low ))
hour[low] <- hour[low] + ( hour[low1] - hour[low] ) * ( mintemp - temp[low] ) /
( temp[low1] - temp[low] )
## truncate on right
low <- temp <= mintemp & c(FALSE,temp[-lh] > mintemp)
low1 <- c(low[-1],FALSE)
if( any( low ))
hour[low] <- hour[low] + ( hour[low1] - hour[low] ) * ( mintemp - temp[low] ) /
( temp[low1] - temp[low] )
temp[ temp < mintemp ] <- mintemp
temp <- temp[ !duplicated( hour ) ]
hour <- unique( hour )
## linear interpolating spline for temp
lh <- length( hour ) - 1
h0 <- 1:lh
aa <- ( temp[1+h0] - temp[h0] ) / ( hour[1+h0]-hour[h0] )
aa <- c( aa, aa[lh] )
bb <- temp - mintemp
s <- list( knots = hour, coefficients = cbind( bb,aa,0,0) )
dimnames( s$coefficients ) <- list( paste( floor( hour / mult ), round( hour %% mult ),
sep = "." ), c("const","linear","quad","cubic") )
attr(s,"formula") <- temp ~ hour
class( s ) <- c("npolySpline","polySpline","spline")
if( !cumulative )
return( s )
## quadratic spline for cumulative temp
aa <- s$coefficients[,3] <- s$coefficients[,2] / 2 / mult / mult
lh <- length( aa ) - 1
h0 <- 1:lh
h1 <- 1 + h0
hour <- s$knots
bb <- s$coefficients[,2] <- s$coefficients[,1] / mult
cc <- ( hour[h1] - hour[h0] ) * ( bb[h0] + aa[h0] *
( hour[h1] - hour[h0] ))
s$coefficients[,1] <- start + c( 0, cumsum( cc ))
s
}
###########################################################################################
temp.repeat <- function( community, period = range( days ))
{
lodays <- range( getTemp( community, "Low" )$knots )
hidays <- range( getTemp( community, "High" )$knots )
days <- c( max( lodays[1], hidays[1] ), min( lodays[2], hidays[2] ))
lodays <- period[1] >= days[1] & period[1] <= days[2]
if( lodays )
days[1] <- period[1]
hidays <- period[2] <= days[2] & period[2] >= days[1]
if( hidays )
days[2] <- period[2]
if( !( lodays & hidays )) {
stop( paste( "\nSimulation period is outside of Temperature days:\n period =",
paste( round( period ), collapse = "," ), "; days =",
paste( round( days ), collapse = "," ), "\nNeed to run temp.design() and start over!" ))
}
period <- days
days <- seq( days[1], days[2] )
low <- stats::predict( getTemp( community, "Low" ), days )$y
high <- stats::predict( getTemp( community, "High" ), days )$y
periods <- c( as.numeric( names( getTemp( community, "Time" ))), Inf )
temps <- hours <- numeric( )
period[2] <- period[2] + 1
for( i in seq( length( periods ) - 1 )) {
day <- max( period[1], periods[i] )
this.period <- days >= day & days < min( period[2], periods[i+1] )
n <- sum( this.period )
if( n ) {
if( !is.null( getTemp( community, "Time", i ))) {
time <- getTemp( community, "Time", i )
base <- getTemp( community, "Base", i )
}
h <- length( time )
lotemp <- rep( low[this.period], rep(h,n) )
hitemp <- rep( high[this.period], rep(h,n) )
temp <- ( base - min( base )) / diff( range( base ))
temps <- c( temps, rep( temp, n ) * ( hitemp - lotemp ) + lotemp )
hours <- c( hours, rep( time, n ) + rep( getTemp( community, "Unit" ) *
seq( day, day + n - 1 ), rep(h,n) ))
}
}
day <- days == period[2]
if( any( day )) {
temps <- c( temps, low[day] + temp[1] * ( high[day] -
getTemp( community, "Low" )[day] ))
hours <- c( hours, getTemp( community, "Unit" ) * period[2] )
}
data.frame( hour = hours, temp = temps )
}
##########################################################################################
showTemp <- function( community )
{
cat( "Temperature set for days",
paste( range( getTemp( community, "Low" )$knots ), collapse = " to " ), "\n" )
cat( "Daily low temperature range:",
paste( round( range( stats::predict( getTemp( community, "Low" ))$y )),
collapse = " to " ), "\n" )
cat( "Daily high temperature range:",
paste( round( range( stats::predict( getTemp( community, "High" ))$y )),
collapse = " to " ), "\n" )
if( !is.null( getTemp( community, "DegreeDay" ) ))
cat( "Active temperature range:",
paste( round( range( getTemp( community, "DegreeDay", "knots" ) )),
collapse = " to " ), "\n" )
cat( "Run temp.design() to adjust temperature range\n" )
}
##########################################################################################
activeTemp <- function( community,
lo.hour = min( getTemp( community, "DegreeDay", "knots" ) ),
hi.hour = max( getTemp( community, "DegreeDay", "knots" ) ),
degreeday, messages = TRUE )
{
unit <- getTemp( community, "Unit" )
if( missing( hi.hour )) {
hi.hour <- if( length( lo.hour ) > 1 )
lo.hour[2]
else
lo.hour + unit
}
## period is in units of hours
period <- c( floor( lo.hour[1] / unit ), ceiling( hi.hour / unit ))
if( missing( degreeday )) {
degreeday <- if( is.null( getTemp( community, "DegreeDay" ) ))
0
else
getDegreeDay( community, lo.hour[1] )
}
if( is.na( degreeday ))
degreeday <- 0
community <- setTemp( community, "DegreeDay",
temp.spline( community, temp.repeat( community, period ),
start = degreeday ))
community <- setTemp( community, "Hour",
break.backSpline( getTemp( community, "DegreeDay" )))
if(messages) {
tmp <- period * unit
names( tmp ) <- c("lo.hour","hi.hour")
print( tmp )
}
community
}
###########################################################################################
updateTemp <- function( community,
period = range( getTemp( community, "DegreeDay", "knots" ) ))
{
## period is in units of hours
first <- period[1]
last <- period[2]
s <- getTemp( community, "DegreeDay" )
knots <- s$knots
## drop earlier times that are now in the past
drop <- sum( knots < first ) - 1
nk <- length( knots )
change <- drop > 0
if( change ) {
s$knots <- knots[ - seq( drop ) ]
s$coefficients <- s$coefficients[ - seq( drop ), ]
}
## add new days to include last
period <- ceiling( c( knots[nk], last ) / getTemp( community, "Unit" ) )
if( period[1] <= period[2] ) {
change <- TRUE
news <- temp.spline( community, temp.repeat( community, period ),
start = getHour( getTemp( community, "Unit" ) * period[1] ))
nk <- length( s$knots )
if( s$knots[nk] == news$knots[1] ) {
s$knots <- s$knots[-nk]
s$coefficients <- s$coefficients[-nk,]
}
else
s$coefficients[nk,] <- news$coefficients[1,]
s$knots <- c( s$knots, news$knots )
s$coefficients <- rbind( s$coefficients, news$coefficients )
}
if( change ) {
community <- setTemp( community, "DegreeDay", s )
community <- setTemp( community, "Hour", break.backSpline( s ))
}
community
}
###########################################################################################
ramp.backSpline <- function( s )
{
## ramped backspline is a trick to get backSpline when curve is flat in spots
## if tmp <- stats::predict( ramp.backSpline( s ))
## then plot tmp$y versus tmp$x-tmp$y to "recover" original curve.
## problem is that one cannot recover particular x this way!
s$coefficients[,1] <- s$coefficients[,1] + s$knots
s$coefficients[,2] <- s$coefficients[,2] + 1
splines::backSpline( s )
}
###########################################################################################
break.backSpline <- function( tmp )
{
## alternative to ramped backSpline that first removes flat regions (slope 0)
## problem remains that leftover may still have slope 0 at a point
## this can cause anomolous results!
## find flat regions and cut out
tmpc <- diff(tmp$coefficients[,1]) == 0
tmpk <- cumsum( diff(tmp$knots) * ( tmpc ))
tmpk <- c(tmpk,tmpk[length(tmpk)])
tmp$knots <- tmp$knots - tmpk
tmpc <- c(!tmpc,TRUE)
tmp$knots <- tmp$knots[tmpc]
tmp$coefficients <- tmp$coefficients[tmpc,]
tmp$breaks
nb <- dim( tmp$coefficients )
## kludge for backspline: cannot handle slope of zero
tmpn <- tmp$coefficients[,2] == 0
if( any( tmpn )) {
tmpn <- seq( tmpn )[ tmpn ]
tmp$coefficients[ tmpn, 2:nb[2] ] <-
( tmp$coefficients[ pmin( nb[1], tmpn + 1 ), 2:nb[2] ] +
tmp$coefficients[ pmax( 1, tmpn - 1 ), 2:nb[2] ] ) / 2
}
## back spline
tmpb <- splines::backSpline(tmp )
if( any( tmpc )) {
## shift back spline based on breaks
tmpb$coefficients[,1] <- tmpb$coefficients[,1] + tmpk[tmpc]
}
tmpb
}
###########################################################################################
getDegreeDay <- function( community, hour )
{
stats::predict( getTemp( community, "DegreeDay" ), hour )$y
}
###########################################################################################
getHour <- function( community, dd )
{
stats::predict( getTemp( community, "Hour" ), dd )$y
}
###########################################################################################
getTime <- function( community, species, x )
{
if( is.na(x)){
cat("getTime missing value\n")
browser()
}
switch( getOrgFeature( community, species, "units" ),
## organisms on hour basis assumed to be active only 6am-6pm
hr = getDegreeDay( community,
( getTemp( community, "Unit" ) / 2 + x + floor( x / getTemp( community, "Unit" ) )) / 2 ),
DD = x )
}
###########################################################################################
checkTime <- function( community, x, base, units )
{
## NOTE: Sometimes base can be negative!! (reset to 0)
if( units == "hr" ) {
x <- max( x )
# print( c( base = base, x = x, knots = range( getTemp( community, "DegreeDay", "knots" ))))
if( x > max( getTemp( community, "DegreeDay", "knots" )))
community <- activeTemp( community, max( base, 0 ),
x + getTemp( community, "Unit" ), messages = FALSE )
}
community
}
###########################################################################################
transTime <- function( community, org1name, org2name, x,
unit1 = getOrgFeature( community, org1name, "units" ),
unit2 = getOrgFeature( community, org2name, "units" ))
{
if( is.na(x)){
cat("transTime missing value\n")
browser()
}
if( unit1 == unit2 )
return( x )
switch( unit1,
## organisms on hour basis assumed to be active only 6am-6pm
hr = getDegreeDay( community,
( getTemp( community, "Unit" ) / 2 + x + floor( x / getTemp( community, "Unit" ))) / 2 ),
DD = getHour( x ))
}
###########################################################################################
### To do:
### 1. interactive designer for hourly temp fluctation over one day
### 2. interactive designer for daily lows and highs over season: temp.design() DONE
### 3. check future event trees hour vs. DD
### 4. schedule interaction events with hour-DD translation
### 5. make aphytis dormant at night
###########################################################################################
# --- Source: initTemp.R ---
##########################################################################################
### simulation temperature administration
##########################################################################################
initTemp <- function( community, lo.hour = 0, hi.hour = getTemp( community, "Unit" ),
days = TemperaturePar["Days"],
messages = TRUE, datafile = "", ... )
{
if(messages) {
cat( "Initializing Temperature Profile ...\n" )
}
Temperature <- list()
TemperaturePar <- getOrgData(community, "temperature", "par", messages, datafile)
# mydata( "TemperaturePar", getOrgInfo( community, "package" ), messages = messages)
TemperaturePar <- array( TemperaturePar[,"value"],
dimnames = list( row.names( TemperaturePar )))
Temperature$Unit <- TemperaturePar["Unit"]
Temperature$Min <- TemperaturePar["Min"]
## set up daily temperature base
TemperatureBase <- getOrgData(community, "temperature", "base", messages, datafile)
# mydata( "TemperatureBase", getOrgInfo( community, "package" ), messages = messages)
Temperature$Time <- split( TemperatureBase$Time, TemperatureBase$Day )
Temperature$Base <- split( TemperatureBase$Base, TemperatureBase$Day )
community$temp <- Temperature
tmp <- seq( lo.hour / Temperature$Unit,
days + 1 + ( hi.hour / Temperature$Unit ),
length = TemperaturePar["Length"] )
tmp0 <- seq( 0, 1, length = TemperaturePar["Length"] )
tmp1 <- 0.25 * ( TemperaturePar["HighBeg"] - TemperaturePar["LowBeg"] )
Temperature$Low <- splines::interpSpline( tmp, TemperaturePar["LowBeg"] * ( 1 - tmp0 ) +
TemperaturePar["LowEnd"] * tmp0 +
sin( pi * 4 * tmp0 ) * tmp1 )
Temperature$High <- splines::interpSpline( tmp, TemperaturePar["HighBeg"] * ( 1 - tmp0 ) +
TemperaturePar["HighEnd"] * tmp0 +
sin( pi * ( 0.125 + 4 * tmp0 )) * tmp1 )
Temperature$DegreeDay <- NULL
if(messages) {
cat( "Base daily temperature fluctuation:\n" )
}
for( i in names( Temperature$time )) {
cat( "From day", i, ":\n" )
tmp <- Temperature$Base[[i]]
names( tmp ) <- Temperature$Time[[i]]
print( tmp )
}
community$temp <- Temperature
if(messages) {
showTemp( community )
cat( "Initial active temperature:\n" )
}
activeTemp( community, lo.hour, hi.hour, getTemp( community, "Time", 1 )[1],
messages = messages)
}
###########################################################################################
getTemp <- function( community, element, sub )
{
tempelem <- community$temp[[element]]
if( !missing( sub ))
tempelem <- tempelem[[sub]]
tempelem
}
###########################################################################################
setTemp <- function( community, element, value )
{
community$temp[[element]] <- value
community
}
# --- Source: temp.design.R ---
temp.design <- function( community, nspline = 8, n = 1, horizontal = TRUE,
col = c(low="blue",high="red") )
{
low <- getTemp( community, "Low" )
high <- getTemp( community, "High" )
is.data <- !is.null( low )
if( !is.data ) {
tmp <- seq( 0, 60, length = nspline )
low <- interpSpline( tmp, 60 + 0.125 * tmp + sin( 0.25 * tmp ))
high <- interpSpline( tmp, 70 + 0.15 * tmp + sin(( pi / 8 ) + 0.225 * tmp ))
}
## plot curve and surrounding axes
par( mfrow = c(1,1), mar = rep(4.1,4))
cat( "Switch to Graphic Screen to Adjust High and Low Temperatures\n" )
plotit <- function( low, high, fig = fig, horizontal = FALSE, strip = .25, margin = 0 )
{
lowpred <- stats::predict( low, low$knots )
highpred <- stats::predict( high, high$knots )
ylim <- range( c( lowpred$y, highpred$y ))
xlim <- range( c( lowpred$x, highpred$x))
xlim <- xlim + c(-1,1) * margin * diff( xlim )
if( horizontal ) {
if( diff( ylim ) == 0 )
ylim <- ylim * c(.75,1.25)
separator <- ylim[2]
ylim[2] <- ylim[2] + strip * diff( ylim )
}
else {
separator <- xlim[2]
xlim[2] <- xlim[2] + strip * diff( xlim )
}
axt <- c("n","s")
tmpar <- par( xaxt = axt[1+horizontal], yaxt = axt[2-horizontal] )
plot( xlim, ylim, xlim = xlim, ylim = ylim, type="n", xlab = "", ylab = "" )
par( xaxt = "s", yaxt = "s" )
title( fig )
mtext( "day", 1, 2 )
mtext( "temp", 2, 2 )
if( horizontal ) {
p <- pretty( c(ylim[1],separator) )
axis( 2, p[ p <= separator ] )
abline( h = separator, lty = 2 )
}
else {
p <- pretty( c(xlim[1],separator) )
axis( 1, p[ p <= separator ] )
abline( v = separator, lty = 2 )
}
points( highpred$x, highpred$y, lwd = 4 )
curve.plot( highpred, n = n, action = "refresh", fit = high, backfit = FALSE,
save.ends = 3, col = "red", lwd = 2 * ( 1 + ( fig == "high" )))
points( lowpred$x, lowpred$y, lwd = 4 )
curve.plot( lowpred, n = n, action = "refresh", fit = low, backfit = FALSE,
save.ends = 3, col = "blue", lwd = 2 * ( 1 + ( fig == "low" )))
separator
}
## place commands along right strip of plot, highlighting current command
plotcmd <- function( ans, fig, cmds, cmdlocs, usr, col = "green", rest = "black",
horizontal = TRUE, data = FALSE )
{
ans <- c( ans, fig )
if( data )
ans <- c( ans, "data" )
tmp <- is.na( match( cmds, ans ))
if( any( tmp )) for( i in unique( cmdlocs$adj )) {
tmpi <- tmp & i == cmdlocs$adj
if( any( tmpi ))
text( cmdlocs$x[tmpi], cmdlocs$y[tmpi], cmds[tmpi], col = rest, adj = i )
}
if( any( !tmp )) for( i in unique( cmdlocs$adj )) {
tmpi <- !tmp & i == cmdlocs$adj
if( any( tmpi ))
text( cmdlocs$x[tmpi], cmdlocs$y[tmpi], cmds[tmpi], col = col, adj = i )
}
}
cmds <- c("add","delete","replace","rescale","","finish","refresh","restart",
"","data","high","low")
newlocs <- if( horizontal )
function( cmds, data = FALSE, usr )
{
if( !data )
cmds <- cmds[ cmds != "data" ]
n <- length( cmds )
blank <- seq( n )[ cmds == "" | cmds == " " ]
tmp <- diff(usr[3:4]) / 20
m <- mean( usr[1:2] )
y <- usr[4] + 0.5 * tmp - c( tmp * seq( blank[1] - 1 ), 0,
tmp * seq( blank[2] - blank[1] - 1 ), 0,
tmp * seq( n - blank[2] ))
x <- c( rep( usr[1], blank[1] - 1 ), mean( m, usr[1] ),
rep( m, blank[2] - blank[1] - 1 ), mean( m, usr[2] ),
rep( usr[2], n - blank[2] ))
adj <- c( rep( 0, blank[1] ),
rep( 0.5, blank[2] - blank[1] ),
rep( 1, n - blank[2] ))
tmp <- data.frame( x = x, y = y, adj = adj )
cmds[blank[2]] <- " "
row.names( tmp ) <- cmds
tmp
}
else
function( cmds, data = FALSE, usr )
{
if( !data )
cmds <- cmds[ cmds != "data" ]
n <- length( cmds )
blank <- seq( n )[ cmds == "" | cmds == " " ]
tmp <- diff(usr[3:4]) / 20
m <- mean( usr[3:4] )
tmp <- c( usr[4] - tmp * seq( blank[1] - 1 ),
mean( m, usr[4] ),
m + tmp * ( seq( blank[1] + 1, blank[2] - 1 ) - mean( blank )),
mean( m, usr[3] ),
usr[3] + tmp * seq( n - blank[2] ))
tmp <- data.frame( x = rep( usr[2], n ), y = tmp, adj = rep( 1, n ))
cmds[blank[2]] <- " "
row.names( tmp ) <- cmds
tmp
}
newans <- ans <- "replace"
par( mar = c(4.1,4.1,3.1,4.1),omi=rep(.25,4))
fig <- "low"
fit <- low
separator <- plotit( low, high, fig, horizontal )
usr <- par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
cmds <- row.names( cmdlocs )
use.data <- FALSE
plotcmd( ans, fig, cmds, cmdlocs, usr, data = use.data )
rescale.data <- c( range( stats::predict( low, low$knots )$y,
stats::predict( high, high$knots )$y ), range( low$knots, high$knots ))
repeat {
## get command from plot using cursor
z <- locator(1,"n")
if(( !horizontal & z$x > separator ) | ( horizontal & z$y > separator )) {
if( horizontal ) { # need to look at both z&y
x <- abs( z$x - cmdlocs$x )
x <- x == min( x )
newans <- cmds[x]
z <- abs( z$y - cmdlocs$y )[x]
newans <- newans[ z == min( z ) ][1]
}
else {
z <- abs(z$y - cmdlocs$y )
newans <- cmds[ z == min( z ) ][1]
}
switch( newans,
finish =, refresh = {
separator <- plotit( low, high, fig, horizontal )
usr <- par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
data = {
use.data <- is.data & !use.data
if( is.data & !use.data )
plotit( low, high, fig, horizontal )
},
high =, low = {
if( newans != fig )
fig <- newans
separator <- plotit( low, high, fig, horizontal )
usr <- par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
rescale = {
tmpcmds <- cmds
tmpans <- tmpcmds[ cmds == "rescale" ] <-
"rescale: Switch to Character Screen"
plotcmd( tmpans, fig, tmpcmds, cmdlocs, usr, data = use.data )
ry <- range( c( stats::predict( low, low$knots )$y, stats::predict( high, high$knots )$y ))
cat( "\nEnter new minimum/maximum followed by RETURN key\n" )
newry <- ry
show <- c("minimum","maximum")
change <- FALSE
for( i in 1:2 ) {
tmpy <- readline( paste( show[i], " temp (",
round( ry[i], 2 ), "):", sep = "" ))
tmpy <- if( tmpy == "" ) NA
else as.numeric( tmpy )
if( is.na( tmpy ))
tmpy <- ry[i]
else
change <- TRUE
rescale.data[i] <- tmpy
}
tmp <- range( c( low$knots, high$knots ))
for( i in 1:2 ) {
tmpx <- readline( paste( show[i], " time (",
round( tmp[i], 2 ), "):", sep = "" ))
tmpx <- if( tmpx == "" ) NA
else as.numeric( tmpx )
if( is.na( tmpx ))
tmpx <- tmp[i]
else
change <- TRUE
rescale.data[2+i] <- tmpx
}
cat( "Switch to Graphic Screen to Adjust High and Low Temperatures\n" )
if( change ) {
tmp <- rescale.temp( low, high, rescale.data[1:2],
rescale.data[3:4], ry )
low <- tmp$low
high <- tmp$high
}
separator <- plotit( low, high, fig, horizontal )
usr <- par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
restart = {
low <- getTemp( community, "Low" )
high <- getTemp( community, "High" )
if( is.data )
rescale.data <- c( range( stats::predict( low, low$knots )$y,
stats::predict( high, high$knots )$y ), range( low$knots, high$knots ))
separator <- plotit( low, high, fig, horizontal )
usr <- par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
add =, delete =, replace = {
ans <- newans
}
)
if( use.data ) {
tmp <- rescale.temp( getTemp( community, "Low" ), getTemp( community, "High" ),
rescale.data[1:2], rescale.data[3:4] )
for( i in c("low","high") ) {
datax <- tmp[[i]]$knots
lines( datax, stats::predict( tmp[[i]], datax )$y )
}
}
plotcmd( ans, fig, cmds, cmdlocs, usr, data = use.data )
}
else { # modify the curve knots
fit <- get( fig )
if( z$x >= min( fit$knots ) & z$x <= max( fit$knots )) {
fit <- curve.plot( as.data.frame( stats::predict( fit, fit$knots )), n = n, action = ans,
z = z, fit = fit, backfit = FALSE, save.ends = 2, col = col[fig] )
assign( fig, fit$fit )
}
ans <- "replace"
plotcmd( ans, fig, cmds, cmdlocs, usr, data = use.data )
}
if( newans == "finish" )
break
}
plotcmd( newans, fig, cmds, cmdlocs, "red", data = use.data )
community <- setTemp( community, "Low", low )
community <- setTemp( community, "High", high )
activeTemp( community )
}
# --- Source: temp.plot.R ---
temp.plot <- function( community, lo.hour = s$knots[1], hi.hour = max( s$knots ),
length = 201,
col = NULL, derivative = FALSE, ..., printit = FALSE )
{
s <- getTemp( community, "DegreeDay" )
x <- seq( lo.hour, hi.hour, length = length )
## make sure to pick up knots to plot in this region
x <- unique( sort( c( x, s$knots[ s$knots >= lo.hour & s$knots <= hi.hour ] )))
ylab <- "degree-days"
if( derivative ) {
ylab <- "degrees above min"
s$coefficients <- s$coefficients[,-1]
for( i in seq( 2, ncol( s$coefficients )))
s$coefficients[,i] <- s$coefficients[,i] * i
}
y <- stats::predict( s, x )$y
plot( x / getTemp( community, "Unit" ), y, type = "l", xlab = "day",
ylab = ylab, ... )
if( printit )
print( cbind( hi.hour, stats::predict( s, hi.hour )$y ))
if( !is.null( col ))
points( s$knots, coef(s)[,1], col = col )
if( !derivative ) {
s <- getTemp( community, "Hour" )
x <- seq( min( y ), max( y ), length = length )
tmp <- stats::predict( s, x )
lines( tmp$y / getTemp( community, "Unit" ), tmp$x, col = "blue" )
}
}
###########################################################################################
temp.lines <- function( s, mult = 24, col = "red" )
{
x <- seq( s$knots[1], max( s$knots ), length = 51 )
x <- unique( sort( c( x, s$knots )))
p <- stats::predict( s, x )
lines( p$x, p$y, col = col )
if( !is.null( col ))
points( s$knots / mult, coef(s)[,1], col = col )
}
# --- Source: my.R ---
###########################################################################################
## System files
###########################################################################################
my.read <- function(dataname, stringsAsFactors = TRUE)
{
switch(tools::file_ext(dataname),
"txt" =, "tsv" = read.table(dataname, stringsAsFactors = stringsAsFactors),
"csv" = read.csv(dataname, stringsAsFactors = stringsAsFactors),
"xls" = readxl::read_xls(dataname),
"xlsx" = readxl::read_xlsx(dataname))
}
###########################################################################################
my.eval <- function(species, extension, element, checkdata = FALSE )
{
if( !missing( extension ))
species <- paste( species, extension, sep = ".")
if( exists( species ))
organism <- get( species )
else {
if( checkdata ) {
organism <- utils::data( list = species )
if( organism == species )
organism <- NULL
}
else
organism <- NULL
}
if(!( missing(element) | is.null( organism )))
organism <- organism[[element]]
organism
}
###########################################################################################
mydata <- function( dataname, package, restart = FALSE, messages = TRUE )
{
edata <- exists( dataname )
if( restart & edata ) {
remove( list = dataname, pos = 1 )
edata <- !edata
}
if( !edata ) {
utils::data( list = dataname, package = eval( package ))
if(messages) {
cat( "Data", dataname, "loaded\n" )
}
}
else
if(messages) {
cat( "Data", dataname, "already loaded\n" )
}
}
# --- Source: Org.R ---
###########################################################################################
### Organism Features
###########################################################################################
getOrgFeature <- function( community, species, feature = names( OrgFeature ))
{
OrgFeature <- getOrgInfo( community, "Feature" )
if (!is.null(OrgFeature)) {
if( missing( species ))
return( row.names( OrgFeature ))
f <- OrgFeature[ species, feature ]
if( length( feature ) == 1 ) {
if( any( is.na( f )))
return( NA )
f <- as.character( f )
}
else {
f <- apply( f, 2, as.character )
}
f <- c( unlist( f ))
numf <- suppressWarnings(as.numeric( f ))
if( all( !is.na( numf )))
f <- numf
return(f)
}
# Fallback for webR standalone adapter objects
if (missing(species)) return(c("host", "parasite"))
if (missing(feature) || is.null(feature)) return(c(substrate = "substrate", units = "units"))
if (length(feature) == 1) {
if (feature == "substrate") return("substrate")
if (feature == "units") return("units")
return(NA)
}
res <- rep("substrate", length(feature))
names(res) <- feature
res
}
##########################################################################################
getOrgHosts <- function( community, species,
feature = c("offspring","attack","substrate") ###HOST SPECIFIC###
)
{
# This seems overly complicated and adds substrate; maybe function name is wrong
f <- unique( getOrgFeature( community, species, feature ))
# f[match(species, f, nomatch = 0)] # this would only get species
o <- getOrgFeature( community )
o[ match( f, o, nomatch = 0 ) ]
}
###########################################################################################
getOrgFuture <- function( community, species, feature, current,
future = OrgFuture[[species]] )
{
OrgFuture <- getOrgInfo( community, "Future" )
if (!is.null(OrgFuture) && !is.null(OrgFuture[[species]])) {
future <- OrgFuture[[species]]
if( missing( current )) {
if( missing( feature ))
return( future )
future <- future[,feature]
}
else {
if( !is.numeric( current ))
current <- match( current, future$current, nomatch = 0 )
if( missing( feature ))
future <- future[ current, ]
else
future <- future[ current, feature ]
}
if( is.null( future ))
return( NA )
if(is.character( future ))
future <- as.factor(future)
return(future)
}
# Fallback for webR standalone adapter objects
if (!is.null(community$pop[[species]])) {
pch_vec <- community$pop[[species]]$pch
col_vec <- community$pop[[species]]$col
fut <- data.frame(pch = pch_vec, color = col_vec, stringsAsFactors = FALSE)
if (missing(feature)) return(fut)
if (length(feature) == 1) return(fut[[feature]])
return(fut[, feature, drop = FALSE])
}
NULL
}
###########################################################################################
get.interact <- function( community, species, host, avail, event )
{
id <- get.species.element( community, host, "stage", avail )
interact <- getOrgInteract( community, host, species, event )[id]
interact[ is.na( interact ) ] <- 0
interact
}
###########################################################################################
getOrgInteract <- function( community,
org1name = getOrgFeature( community, org2name, "substrate" ),
org2name, event = NULL )
{
OrgInteract <- getOrgInfo( community, "Interact" )
if (!is.null(OrgInteract) && !is.null(OrgInteract[[org1name]][[org2name]])) {
tmp <- OrgInteract[[org1name]][[org2name]]
if( is.null( event ))
return( tmp )
event <- as.character( event )
inter <- tmp[,event]
if( length( event ) == 1 )
names( inter ) <- row.names( tmp )
if(is.character(inter))
inter <- factor(inter)
return(inter)
}
# Fallback for webR standalone adapter objects
sub_names <- if (!is.null(community$sub_names)) community$sub_names else c("fr1", "fr2", "fr3", "fr4", "twig", "lftop", "lfbot")
mat <- matrix(1, nrow = length(sub_names), ncol = 1, dimnames = list(sub_names, "substrate"))
as.data.frame(mat)
}
###########################################################################################
getOrgMeanValue <- function( community, species )
{
OrgMeanValue <- getOrgInfo( community, "MeanValue" )
## The global org$MeanValue[[species]] contains mean value information.
OrgMeanValue[[species]]
}
###########################################################################################
copyOrgInfo <- function( fromname, toname )
{
out <- list()
for( i in c("sim")) {
from <- paste( fromname, i, sep = "." )
if( exists( from )) {
toto <- paste( toname, i, sep = "." )
out[[toto]] <- get( from )
cat( "copied", from, "to", toto, "into list\n" )
}
}
return(out)
}
###########################################################################################
get.alive <- function( community, species, substrate )
{
alive <- getOrgAlive( community, species )
alive <- seq_along( alive )[alive]
alive[ substrate == get.species.element( community, species, "sub.stage", alive ) ]
}
###########################################################################################
getOrgAlive <- function( community, species, element )
{
organism <- get.species( community, species )
## identify dead organisms (free nodes for leftist tree)
tmp <- c( FALSE, apply( organism[c("dist","up","left","right"),-1], 2,
function( x ) any( x > 1 )))
if( !any( tmp )) {
tmpp <- organism["up",1]
if( tmpp > 1 )
tmp[tmpp] <- TRUE
}
if( !missing( element ))
tmp <- organism[ element, tmp ]
tmp
}
###########################################################################################
getOrgAgeClass <- function( community, species, stage = seq_len( nrow( future )),
future = getOrgFuture( community, species ))
{
ageclass <- future$ageclass[stage]
tmp <- !is.na( ageclass )
if( any( tmp ))
ageclass[ !is.na( ageclass ) ]
else
NA
}
###########################################################################################
getOrgSubstrate <- function( community, species, elements = seq_len( nrow( inter )),
substrate = getOrgFeature( community, species, "substrate" ),
inter = getOrgInteract( community, substrate, species ))
{
sites <- inter$substrate[elements]
tmp <- !is.na( sites )
if( any( tmp ))
sites[ !is.na( sites ) ]
else
NA
}
###########################################################################################
sampleOrgSubstrate <- function( community, species, elements = seq_len( nrow( inter )),
substrate.name = getOrgFeature( community, species, "substrate" ),
inter = getOrgInteract( community, substrate.name, species ))
{
if( is.na( substrate.name ))
return( elements )
newsub <- as.matrix( cbind( elements, inter[ elements, levels( factor(inter$substrate) ) ] ))
apply( newsub, 1, function( x, is ) {
ns <- sample( levels( factor(is) ), 1, prob = x[-1] / sum( x[-1] ))
sub <- seq_len( nrow( inter ))[ ns == is ]
if( length( sub ) > 1 ) {
newsub <- getOrgInteract( community, substrate.name, substrate.name )[x[1],sub]
sample( sub, 1, prob = newsub / sum( newsub ))
}
else
sub
}, inter$substrate )
}
##########################################################################################
### simulation organism administration
##########################################################################################
initOrgInfo <- function( package, messages = TRUE, datafile = "", ... )
{
community <- list( pop = list( ))
community$org <- list( )
community$org$package <- package
## Get data
community$org$Feature <- getOrgData(community, "organism", "features",
messages, datafile)
community$pop <- list()
community
}
##########################################################################################
setOrgInfo <- function( community, species, hosts, package, messages = TRUE,
datafile = "", ... )
{
Organism <- community$org
if( is.null( Organism$Future ))
Organism$Future <- list( )
if( is.null( Organism$Interact ))
Organism$Interact <- list( )
for( j in hosts )
if( is.null( Organism$Interact[[j]] ))
Organism$Interact[[j]] <- list( )
## Do not reset MeanValue as it may have important spline fits!
if( is.null( Organism$MeanValue ))
Organism$MeanValue <- list( )
for( i in species ) {
future <- getOrgData(community, "future", i,
messages, datafile)
# Check that future agrees with organism.feature information
subclass <- Organism$Feature[i,"subclass"]
if(!(subclass %in% unique(future$ageclass))) {
stop(paste("Future table", paste("future", i, sep = "."),
"does not include", subclass))
}
level.ageclass <- unique( future$ageclass )
level.ageclass <- as.character( level.ageclass[ !is.na( level.ageclass ) ] )
future$ageclass <- ordered( future$ageclass, level.ageclass )
Organism$Future[[i]] <- future
for( j in hosts )
if( i != j ) {
Organism$Interact[[j]][[i]] <- getOrgData(community, j, i,
messages, datafile)
# Check that interaction agrees with host current stage information
# This is messy!
if(j %in% species) {
if(!all(row.names(Organism$Interact[[j]][[i]]) %in%
c(as.character(Organism$Future[[j]]$current), i))) {
stop(paste("Interaction table", paste(j, i, sep = "."),
"does not match", j, "current stages"))
}
}
}
if( is.null( Organism$MeanValue[[i]] ))
Organism$MeanValue[[i]] <- list( )
else
cat( "Keeping Mean Value information for", i, "if any\n" )
}
for( i in unique( getOrgFeature( community, species, "substrate" ))) {
Organism$Interact[[i]][[i]] <- getOrgData(community, i, i,
messages, datafile)
}
community$org <- Organism
community
}
###########################################################################################
getOrgData <- function(community, left, right,
messages = TRUE, datafile = "")
{
# Get Organism Data from
# package data
# global data supplied by user
# external data file supplied by user
sheet <- paste( left, right, sep = "." )
data_exists <- FALSE
if (is.character(datafile) && length(datafile) == 1 && nzchar(datafile)) {
if (dir.exists(datafile)) {
extensions <- c(".txt", ".tsv", ".csv", ".xls", ".xlsx")
datafile_paths <- file.path(datafile, paste0(sheet, extensions))
exist_idx <- file.exists(datafile_paths)
if (any(exist_idx)) {
datafile <- datafile_paths[exist_idx][1]
data_exists <- TRUE
} else {
data_exists <- FALSE
}
sheet <- ""
} else if (file.exists(datafile)) {
data_exists <- TRUE
}
}
if(!data_exists) {
# Load package data or get user-provided global data.
mydata( sheet, getOrgInfo( community, "package" ), messages = messages)
my.eval( sheet )
} else {
# Read data file from user if provided.
if(sheet == "")
my.read(datafile)
else {
out <- as.data.frame(readxl::read_excel(datafile, sheet = sheet, .name_repair = "none"))
if(names(out)[1] == "") { # first column is actual row names
rownames(out) <- out[[1]]
out[[1]] <- NULL
}
out
}
}
}
###########################################################################################
getOrgNames <- function(datafile = "") {
if(datafile == "") {
c("organism.features", "future.host", "future.parasite",
"substrate.host", "substrate.parasite", "substrate.substrate",
"temperature.base", "temperature.par")
} else {
readxl::excel_sheets(datafile)
}
}
###########################################################################################
getOrgDataSimple <- function(community, dataname, datafile = ""){
out <- getOrgData(
community,
left = stringr::str_remove(dataname, "\\..*"),
right = stringr::str_remove(dataname, ".*\\."),
messages = FALSE, datafile = datafile)
# Kludge to reinstate rownames as a column
if(!identical(rownames(out), as.character(seq_len(nrow(out))))) {
out <- data.frame(rownames = rownames(out), out)
}
out
}
###########################################################################################
getOrgInfo <- function( community, element )
{
community$org[[element]]
}
###########################################################################################
setOrgMeanValue <- function( community, species, stage, mvalue )
{
## The global Organism$MeanValue[[species]] contains mean value information.
community$org$MeanValue[[species]][[stage]] <- mvalue
community
}
# --- Source: organism.features.R ---
NULL
# --- Source: future.R ---
## $Id: future.R,v 1.0 2002/12/11 yandell@stat.wisc.edu Exp $
##
## Functions for Bland Ewing's modeling.
##
## Copyright (C) 2000,2001,2002 Brian S. Yandell.
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2, or (at your option) any
## later version.
##
## These functions are distributed in the hope that they will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## The text of the GNU General Public License, version 2, is available
## as http://www.gnu.org/copyleft or by writing to the Free Software
## Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
###############################################################################
##
## future.events( community )
##
###############################################################################
###############################################################################
### Get birth and future event
###############################################################################
get.future <- function (community, species,
individuals = get.individual(community, species, id),
id = get.base(community, species))
{
## NOTE: This is the slow routine. For every event, it has to check if there
## is a mean value function and then call rspline.
## the structure future.species is set up to handle competing risks!
future <- getOrgFuture(community, species, c("current", "future", "time"))
future$fid <- match(future$future, future$current)
individuals <- as.matrix(individuals)
rownames <- dimnames(individuals)[[1]]
for (i in seq(ncol(individuals))) {
individual <- individuals[,i]
current <- individual["stage"]
individual["location"] <- individual["time"]
## competing risks based on potential future event times
futures <- future$current == future$current[current]
times <- rep( individual["time"], sum( futures ))
cur <- seq(nrow(future))[futures]
for( j in seq( sum( futures ))) {
meantime <- future[cur[j], "time"]
if( meantime > 0 ) {
for.stage <- as.character(future$current[future$fid[ cur[j] ]])
times[j] <- rspline( meantime, individual,
getOrgMeanValue(community, species)[[for.stage]])
}
}
individual["time"] <- min( times )
current <- cur[ times == individual["time"] ][1]
individual["future"] <- future$fid[current]
if (individual["time"] == individual["rejection"])
individual["future"] <- future$fid[future$current == "death"]
individuals[,i] <- individual
}
individuals
}
###############################################################################
event.death <- function( community, species,
id = get.base( community, species ))
{
## remove dead individual from leftist tree
community <- put.species( community, species,
leftist.remove( get.species( community, species ), id ))
## free up individual for reuse
community <- put.base( community, species, id )
community
}
###############################################################################
update_mintime <- function( object, species, ... )
{
base <- get.base( object, species )
mintime <- max( getCount( object, species, "mintime" ),
getTime( object, species,
get.species.element( object, species, "time", base )))
setCount( object, species, list( base = base, mintime = mintime ))
}
###############################################################################
set.birth <- function( community, species, neworg )
{
## merge immediate new births (if any)
newbirths <- ncol( neworg )
if( newbirths > 0 ) {
neworg[c("dist","left","right","up"),] <- 1
community <- checkTime( community, neworg["time",],
getCount( community, species, "mintime" ),
getOrgFeature( community, species, "units" ))
oldbase <- getCount( community, species, "base" )
tmp <- leftist.birth( get.species( community, species ), neworg,
getCount( community, species, "free" ))
community <- put.species( community, species, tmp$tree )
community <- put.base( community, species, free = tmp$free )
community <- updateCounts( community, species, newbirths )
}
community
}
# --- Source: future.host.R ---
NULL
# --- Source: future.meanvalue.R ---
###########################################################################################
## init.meanvalue( organism, stage )
## spline.meanvalue( x, y )
## spline.meanvalue( data = data )
##
## five.show( )
## five.plot( )
##
###########################################################################################
# Curve Designing routines -- under development
###########################################################################################
future.meanvalue <- function( community, species, event = future$current[1],
data )
{
future <- getOrgFuture( community, species )
mvalue <- getOrgMeanValue( community, species )[[event]]
if( missing( data )) {
if( !is.null( mvalue )) {
mvalue <- stats::predict( mvalue$meanvalue, mvalue$meanvalue$knots )
mvalue <- spline.meanvalue( mvalue$x, mvalue$y )$fit
}
else
mvalue <- spline.meanvalue( )$fit
}
else
mvalue <- spline.meanvalue( data = data )$fit
setOrgMeanValue( community, species, event, mvalue )
}
###########################################################################################
spline.meanvalue <- function( x = xinit, y = yinit, data, nspline = 8,
xy = data.frame( x = x, y = y ),
tol = 1e-5, n = 1 )
{
is.data <- !missing( data )
if( !is.data ) {
tmp <- - log( 1 - seq( 0, 1 - exp( -5 ), length = nspline ))
if( missing( x ))
xinit <- tmp
else
xinit <- x
if( missing( y ))
yinit <- tmp
else
yinit <- y
data <- NULL
}
else {
xinit <- sort( data )
ndata <- length( data )
yinit <- seq( ndata ) / ( 1 + ndata )
choose <- round( seq( 1, ndata, length = nspline ))
xinit <- xinit[choose]
yinit <- - log( 1 - yinit[choose] )
}
fs <- list( probability = function( x ) { - log( 1 - x ) } )
finvs <- list( probability = function( x ) { 1 - exp( - x ) } )
for( i in c("mean value","rate","density") )
fs[[i]] <- finvs[[i]] <- function( x ) x
## plot curve and surrounding axes
graphics::par( mfrow = c(1,1), mar = rep(4.1,4))
plotit <- function( xy, fig = "mean value", fit = splines::interpSpline( xy$x, xy$y ))
{
switch( fig, {
y <- xy$y
ylim <- range(c(0,y))
},
probability = {
y <- 1 - exp( - xy$y )
ylim <- range(c(0,y))
},
rate = {
y <- spline.rate( fit, xy$x )$y
tmp <- spline.rate( fit )
ylim <- range(c(0,tmp$y))
},
density = {
y <- spline.rate( fit, xy$x )$y * exp( - stats::predict( fit, xy$x )$y )
tmp <- spline.rate( fit )
tmp$y <- tmp$y * exp( - stats::predict( fit )$y )
ylim <- range(c(0,tmp$y))
}
)
plot(xy$x,y,xlim=1.25*range(c(0,xy$x)), ylim = ylim,
type="n", xlab = "", ylab = "" )
graphics::points( xy$x, y, lwd = 4 )
graphics::title( fig )
graphics::mtext( "time", 1, 2 )
graphics::mtext( fig, 2, 2 )
graphics::abline( v = max( xy$x ), lty = 2 )
switch( fig, {
if( fig == "probability" ) {
tmp <- c(.1,.2,.5,1:10)
ltmp <- 1-exp(-tmp)
graphics::mtext( "mean value", 4, 2 )
}
else {
tmp <- c(seq(0,.9,,by=.1),.95,.98,.99,.999)
ltmp <- -log(1-tmp)
graphics::mtext( "probability", 4, 2 )
}
usr <- graphics::par("usr")
s <- ltmp <= usr[4] & ltmp >= usr[3]
tmpar <- graphics::par( cex = .75 )
graphics::axis(4,ltmp[s],tmp[s])
graphics::par( tmpar )
summaryshow( xy, fit, "white" )
tmp <- curve.plot( xy, n = n, action = "refresh", fit = fit,
f = fs[[fig]], finv = finvs[[fig]] )
summaryshow( xy, tmp$fit )
},
rate =, density = {
graphics::lines( tmp$x, tmp$y )
summaryshow( xy, fit )
}
)
}
## place commands along right strip of plot, highlighting current command
plotcmd <- function( ans, fig, cmds, cmdlocs, usr, col = "green", rest = "black",
data = FALSE )
{
ans <- c( ans, fig )
if( data )
ans <- c( ans, "data" )
tmp <- is.na( match( cmds, ans ))
if( any( tmp ))
graphics::text( rep(usr[2],sum(tmp)), cmdlocs[tmp], cmds[tmp], col = rest, adj = 1 )
if( any( !tmp ))
graphics::text( rep(usr[2],sum(!tmp)), cmdlocs[!tmp], cmds[!tmp], col = col, adj = 1 )
}
fig <- "mean value"
newans <- ans <- "replace"
graphics::par( mar = c(4.1,4.1,3.1,4.1),omi=rep(.25,4))
fit <- splines::interpSpline( xy$x, xy$y )
sums <- plotit( xy, fig, fit )
cmds <- c("refresh","add","delete","replace","rescale","shrink to 1","finish","restart",
"","data","mean value","probability","rate","density")
newlocs <- function( cmds, data = FALSE, usr )
{
if( !data )
cmds <- cmds[ cmds != "data" ]
n <- length( cmds )
blank <- seq( n )[ cmds == "" ]
tmp <- diff(usr[3:4]) / 20
tmp <- c( usr[4] - tmp * seq( blank - 1 ), mean( usr[3:4] ),
usr[3] + tmp * seq( n - blank ))
names( tmp ) <- cmds
tmp
}
usr <- graphics::par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
cmds <- names( cmdlocs )
use.data <- FALSE
plotcmd( ans, fig, cmds, cmdlocs, usr )
rescale.data <- 1
repeat {
## get command from plot using cursor
z <- graphics::locator(1,"n")
if( z$x > max( xy$x )) {
z <- abs(z$y - cmdlocs )
newans <- cmds[z==min(z)][1]
switch( newans,
finish =, refresh = {
sums <- plotit( xy, fig, fit )
},
data = {
use.data <- is.data & !use.data
if( is.data & !use.data & match( fig, c("mean value","probability"),
nomatch = 0 ))
sums <- plotit( xy, fig, fit )
},
"mean value" =, probability =, rate =, density = {
fig <- newans
sums <- plotit( xy, fig, fit )
usr <- graphics::par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
"shrink to 1" = {
while( abs( sums[1] - 1 ) > tol ) {
xy$y <- xy$y * sums[1]
if( is.data )
rescale.data <- rescale.data * sums[1]
fit <- splines::interpSpline( xy$x, xy$y )
sums <- splinesum( xy, fit, tol )
}
sums <- plotit( xy, fig, fit )
usr <- graphics::par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
rescale = {
cat( "enter new values followed by RETURN key\n" )
tmpy <- readline( paste( "maximum mean value(",
round( max( xy$y ), 2 ), "):", sep = "" ))
if( tmpy != "" ) {
tmpy <- as.numeric( tmpy ) / max( xy$y )
xy$y <- tmpy * xy$y
if( is.data )
rescale.data <- rescale.data * tmpy
}
tmpx <- readline( paste( "maximum time(",
round( max( xy$x ), 2 ), "):", sep = "" ))
if( tmpx != "" ) {
tmpx <- as.numeric( tmpx ) / max( xy$x )
xy$x <- tmpx * xy$x
if( is.data )
data <- data * tmpx
}
fit <- splines::interpSpline( xy$x, xy$y )
sums <- plotit( xy, fig, fit )
usr <- graphics::par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
restart = {
if( is.data )
rescale.data <- 1
xy <- data.frame( x = xinit, y = yinit )
fit <- splines::interpSpline( xy$x, xy$y )
sums <- plotit( xy, fig, fit )
usr <- graphics::par("usr")
cmdlocs <- newlocs( cmds, data = is.data, usr = usr)
},
add =, delete =, replace = {
ans <- newans
}
)
if( use.data & match( fig, c("mean value","probability"), nomatch = 0 ))
cdf.lines( data, fig, rescale = rescale.data )
plotcmd( ans, fig, cmds, cmdlocs, usr, data = use.data )
}
else {
if( is.na( match( fig, c("rate","density") ))) {
summaryshow( xy, fit, "white", sums )
fit <- curve.plot( xy, n = n, action = ans, z = z, fit = fit,
f = fs[[fig]], finv = finvs[[fig]] )
xy <- fit$xy
fit <- fit$fit
sums <- summaryshow( xy, fit )
ans <- "replace"
plotcmd( ans, fig, cmds, cmdlocs, usr, data = use.data )
}
}
if( newans == "finish" )
break
}
plotcmd( newans, fig, cmds, cmdlocs, "red", data = use.data )
summaryshow( xy, fit, "white", sums )
tmp <- curve.plot( xy, n = n, action = "refresh",
f = fs[[fig]], finv = finvs[[fig]] )
tmp$meanvalue <- tmp$fit
tmp$fit <- NULL
tmp$invmvalue <- splines::backSpline( tmp$meanvalue )
sums <- summaryshow( xy, tmp$meanvalue )
tmp$mean <- sums[1]
tmp$median <- sums[2]
tmp
}
# --- Source: substrate.host.R ---
NULL
# --- Source: host.parasite.R ---
NULL
# --- Source: simdata.R ---
NULL
# --- Source: redscale.R ---
NULL
# --- Source: init.simulation.R ---
init.simulation <- function( package = "ewing",
count = 200,
interact = FALSE,
messages = TRUE,
...)
{
community <- initOrgInfo( package, messages = messages, ... )
community <- initTemp( community, messages = messages, ... )
species <- getOrgFeature( community )[1:2]
hosts <- getOrgHosts( community, species )
if(messages) {
cat( "Creating simulation organism set using species:\n",
paste( species, collapse = ", " ), "\n\n" )
}
community <- setOrgInfo( community, species, hosts, package,
messages = messages, ... )
if(messages) {
cat( "\n" )
}
species <- unique( species )
num <- numeric( length( species ))
names( num ) <- species
count <- rep_len(count, length(species))
names(count) <- species
for( i in species ) {
num[i] <- reuse <- count[i]
if(messages | (interact & interactive())) {
cat( paste( "Initialize ", i, " at size ", reuse, sep = "" ))
}
if(interact & interactive()) {
cat(" :")
r <- readline( )
if( r != "" & is.na( pmatch( substring( r, 1, 1 ), c("y","Y") )))
reuse <- suppressWarnings(as.numeric( r ))
if( is.na( reuse ))
reuse <- count[i]
}
if( reuse ) {
if(messages) {
cat( "...\n" )
}
community <- init.population( community, i, n = reuse, messages = messages, ... )
num[i] <- reuse
}
}
class( community ) <- c("ewing", "list")
attr(community, "count") <- count
community
}
# --- Source: init.population.R ---
init.population <- function( community, species, n = 200, width = 100,
units = getOrgFeature( community, species, "units" ),
timeit = FALSE,
reject = Inf,
position = rtri( n, width ),
colnames = c(leftistnames,paramnames,posnames,eventnames),
init.stage = istage,
init.weight = getOrgFuture( community, species, "init" ),
messages = TRUE,
...)
{
leftistnames <- c("dist","left","right","up")
paramnames <- c("dispersion","location","intensity","truncation","rejection")
posnames <- paste("pos",letters[1:3], sep = ".")
eventnames <- c("time","stage","future","offspring","sex","sub.stage","sub.future")
organism <- matrix( 0, length( colnames ), n+1,
dimnames = list( colnames, NULL ))
organism["time",1] <- Inf
## 5-parameter initialization
organism[c("dispersion","intensity"),-1] <- 1
organism["rejection",-1] <- if( reject == Inf )
rep( Inf, n )
else
reject * stats::rexp( n )
## triangular coordinates
organism[posnames,-1] <- position
## substrate
substrate.name <- getOrgFeature( community, species, "substrate" )
if( !is.na( substrate.name )) {
substrate <- getOrgInteract( community, substrate.name, species, "init" )
organism["sub.stage",-1] <- organism["sub.future",-1] <- sample( length( substrate ),
n, replace = TRUE, prob = substrate / sum( substrate ))
}
## randomly generate events proportional to future time units
nstage <- length( init.weight )
istage <- sample( nstage, n, replace = TRUE, prob = init.weight / sum( init.weight ))
init.stage <- array( init.stage, n )
organism["stage",-1] <- init.stage
## schedule future events
if( timeit )
proctime <- proc.time()
organism[,-1] <- get.future( community, species, organism[,-1] )
if( timeit ) {
tmp <- proc.time() - proctime
cat( "future time: user=", tmp[1], "system=", tmp[2], "total=", tmp[3], "\n" )
}
## create leftist tree
if( timeit )
proctime <- proc.time()
community <- put.species( community, species, leftist.create( organism ))
if( timeit ) {
tmp <- proc.time() - proctime
cat( "leftist time: user=", tmp[1], "system=", tmp[2], "total=", tmp[3], "\n" )
}
## mean number of offspring
if( timeit )
proctime <- proc.time()
#***This is where parasite is crashing--no offspring?**
community <- initOffspring( community, species )
if( timeit ) {
tmp <- proc.time() - proctime
cat( "offspring time: user=", tmp[1], "system=", tmp[2], "total=", tmp[3], "\n" )
}
if(messages) {
cat( "Initializing events for", species, "with", ncol( organism ) - 1, "individuals\n" )
}
community
}
# --- Source: init.timing.R ---
###########################################################################################
### Timing of simulation run
###########################################################################################
init.timing <- function( community )
{
## initialize timing
events <- NULL
for( species in get.species( community ))
events <- c( events, levels( getOrgFuture( community, species, "event" )))
events <- sort( unique( events ))
tmp <- c("total",events,"refresh","other")
cpu <- matrix( 0, 3, length( tmp ),
dimnames = list( c("user","system","total"), tmp ))
community$cpu <- cpu
community <- set.timing( community, "total" )
community
}
###########################################################################################
set.timing <- function( community, string, flag = -1 ) {
if( !is.null( community$cpu ))
community$cpu[,string] <- community$cpu[,string] + flag * proc.time()[1:3]
community
}
###########################################################################################
fini.timing <- function( community )
{
if( !is.null( community$cpu )) {
community <- set.timing( community, "total", 1 )
community$cpu[,"other"] <- community$cpu[,"total"] - apply( community$cpu[,-1], 1, sum )
}
community
}
# --- Source: initCount.R ---
###########################################################################################
### Simulation count object administration
###########################################################################################
initCount <- function( community, species, debugit = FALSE, file = NULL, append = FALSE,
messages = TRUE, ... )
{
if(messages) {
cat( "initial" )
for( i in species)
cat( ":", i, sum( apply( get.species( community, i ), 2,
function(x) !all(x[c("dist","left","right","up")]==1))) - 1 )
cat( "\n" )
}
old_counts <- if (append) getCount( community, , "counts" ) else NULL
old_step <- if (append) getCount( community, , "step" ) else 0
count <- list()
## leftist tree counters
count$mintime <- numeric( length( species ))
count$base <- numeric( length( species ))
names( count$base ) <- names( count$mintime ) <- species
count$free <- list()
## initialize lists to keep track of events
count$events <- count$countage <- count$countsub <- count$nameage <- count$namesub <- list()
## Set up hour to degreeday spline based on range of hours if any
simmin <- c(hr=Inf,DD=Inf)
for( i in species ) {
if( is.null( get.species( community, i )))
stop( paste( "Missing species", i ))
count$free[[i]] <- 1
count$base[i] <- get.base( community, i )
units <- getOrgFeature( community, i, "units" )
tmp <- get.individual( community, i )["time"]
if( tmp < simmin[units] )
simmin[units]<- tmp
}
if( max( simmin ) < Inf ) {
community <- activeTemp( community, simmin["hr"], , simmin["DD"], messages = messages )
}
esums <- c("initial","during","final")
tmpfn <- function( counter )
{
rownames <- levels( counter )
array( 0, length( rownames ), dimnames = list( rownames ))
}
subclass <- getOrgFeature( community, species, "subclass" )
names( subclass ) <- species
for( i in species ) {
species.time <- get.individual( community, i )["time"]
count$mintime[i] <- getTime( community, i, species.time )
future <- getOrgFuture( community, i )
## possible future events
count$events[[i]] <- matrix( 0, nrow( future ), length( esums ),
dimnames = list( as.character( future$current ), esums ))
## current record of future events
count$countage[[i]] <- tmpfn( getOrgFuture( community, i, "ageclass" ))
count$countsub[[i]] <- tmpfn( getOrgInteract( community,, i, "substrate" ))
if( species.time < Inf ) {
## count by age groups
stage <- getOrgAlive( community, i, "stage" )
if( length( stage )) {
classes <- getOrgAgeClass( community, i, stage )
tmp <- table( classes )
count$countage[[i]][ names( tmp ) ] <- tmp
}
## count by substrate
substage <- getOrgAlive( community, i, "sub.stage" )
## only for individuals of class = subclass[i]
substage <- substage[ subclass[i] == getOrgAgeClass( community, i, stage ) ]
if( length( substage )) {
classes <- getOrgInteract( community,, i, "substrate" )
tmp <- table( classes[substage] )
count$countsub[[i]][ names( tmp ) ] <- tmp
}
}
}
count$debug <- debugit
# If file is NULL, then don't write to file; keep counts internal
count$file <- file
if (append && !is.null(old_counts)) {
count$counts <- old_counts
count$step <- old_step
}
community$count <- count
## Put counts in file
community <- putCount( community, append )
## tally events at start of simulation
setEvents( community, "initial" )
}
###########################################################################################
getCount <- function( community, species, element )
{
count <- community$count[[element]]
if( !missing( species ))
count <- count[[species]]
count
}
###########################################################################################
set.step <- function( community, step )
setCount( community,, list( step = step ))
###########################################################################################
setCount <- function( community, species, elements )
{
count <- community$count
for( i in names( elements )) {
if( missing( species ))
count[[i]] <- elements[[i]]
else
count[[i]][[species]] <- elements[[i]]
}
community$count <- count
community
}
# --- Source: future.events.R ---
future.events <- function( community,
nstep = 4000,
species = get.species( community ),
refresh = nstep / 20, cex = 0.5,
substrate.plot = TRUE, extinct = TRUE,
timeit = TRUE, debugit = FALSE,
messages = TRUE, append = NULL, ... )
{
## Integrity check of dataset, and initialization of tallies.
if( missing( community ))
stop( "Must specify a community." )
if (is.null(append)) {
append <- !is.null(community$count$counts) && nrow(community$count$counts) > 0
}
if( debugit ) cat( "initialization\n" )
community <- initCount( community, species, debugit = debugit, file = NULL,
append = append, messages = messages )
if( timeit )
community <- init.timing( community )
mintime <- getCount( community, , "mintime" )
species.now <- species[ mintime == min( mintime ) ][1]
future <- getOrgFuture( community, species.now )
# Set up list for plot information.
p <- list()
pstep <- 0
if (!is.null(community$plot)) {
p <- community$plot
pstep <- length(p)
}
start_step <- if (append && !is.null(community$count$step)) community$count$step else 0
## for nstep steps schedule future events and process immediate events
for( istep in seq( nstep )) {
## stop if any extinct and extinct flag on, or all extinct
omintime <- mintime
mintime <- getCount( community, , "mintime" )
if( debugit ) print( mintime )
if( min( mintime ) < min( omintime )) {
cat( "time reversal!\n" ) # should not happen
browser()
}
tmp <- mintime == Inf
if( any( tmp )) {
if( extinct | all( tmp )) {
for( i in names( mintime )[tmp] )
cat( "***", i, "is extinct ***\n" )
if( plotit )
plot.ewing( community, substrate = substrate.plot, cex = cex, ...)
break
}
}
## each species is always sorted so 1st element is next future event
species.prev <- species.now
species.now <- species[ mintime == min( mintime ) ][1]
individual <- get.individual( community, species.now )
if( is.na( individual["time"] ) | individual["time"] == Inf ) {
cat( "No more finite future events. End of simulation.\n" )
break
}
if( all( individual[c("dist","left","right","up")] == 1 )) {
cat( individual["time"], ": last", species.now, "alive",
getCount( community, species.now, "base" ), "\n" )
}
if( species.now != species.prev )
future <- getOrgFuture( community, species.now )
## make future event the current stage
current <- individual["stage"]
stage <- as.character( future$current[current] )
if(!length(stage))
stop(paste("no stage", current))
community <- updateCount( community, species.now, individual, stage == "death",
start_step + istep )
individual["stage"] <- current
individual["sub.stage"] <- individual["sub.future"]
community <- put.individual( community, species.now, individual )
if( debugit ) {
cat( species.now, istep, "base",
getCount( community, species.now, "base" ), "\n" )
print( c( step = istep,
countage = sum( getCount( community, species.now, "countage" )),
countsub = sum( getCount( community, species.now, "countsub" )),
round( individual["time"], 2 ), current, stage ))
}
## processing of immediate, pending and future events
## this is the main show--all the rest is setup
event.type <- as.character( future[ current, "event" ] )
if( debugit ) cat( "do", event.type, istep, stage,
as.character( future$current[ individual["future"] ] ),
individual["time"], "\n" )
community <- set.timing( community, event.type )
if( event.type == "death" )
community <- event.death( community, species.now )
else {
if( event.type != "future" ) {
## this routine could be user supplied
## generic routines are event.birth, event.attack
event.parsed <- get( paste( "event", event.type, sep = "." ))
community <- event.parsed( community, species.now )
}
community <- event.future( community, species.now )
}
community <- set.timing( community, event.type, 1 )
## refresh plot
if( refresh & ! ( istep %% refresh )) {
community <- set.timing( community, "refresh" )
# Save the ewing_ageclass and ewing_substrate objects.
pstep <- pstep + 1
p[[pstep]] <- ewing_snapshot(community, start_step + istep, ...)
if(messages) {
cat( "refresh", istep )
for( j in get.species( community ))
cat( ":", j, sum( getCount( community, j, "countage" ), na.rm = TRUE ))
cat( "\n" )
}
community <- set.timing( community, "refresh", 1 )
}
## periodic browser if in debug mode
if( debugit ) {
cat( "done", istep, "\n" )
if( refresh & !( istep %% refresh )) {
cat( "Type \"c\" to continue or \"Q\" to quit.\n" )
browser()
}
}
}
community <- set.timing( community, "refresh" )
## end of main loop on future events
if( debugit ) cat("done\n")
if( sum( getOrgAlive( community, species.now )) > 1 ) {
## tally events at end of simulation
community <- setEvents( community, "final" )
}
community <- set.timing( community, "refresh", 1 )
community <- fini.timing( community )
community$step <- start_step + nstep
attr(community, "nstep") <- start_step + nstep
if( !refresh | (nstep%%refresh)) {
pstep <- pstep + 1
p[[pstep]] <- ewing_snapshot(community, start_step + istep, ...)
}
community$plot <- p
community
}
# --- Source: event.R ---
## $Id: event.R,v 1.0 2002/12/11 yandell@stat.wisc.edu Exp $
##
## Functions for Bland Ewing's modeling.
##
## Copyright (C) 2000,2001,2002 Brian S. Yandell.
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2, or (at your option) any
## later version.
##
## These functions are distributed in the hope that they will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## The text of the GNU General Public License, version 2, is available
## as http://www.gnu.org/copyleft or by writing to the Free Software
## Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
###############################################################################
##
## event.birth( community, species )
## event.attack( community, species )
##
## Issues to resolve:
## 2. search strategy for predator/parasite/parasitoid
## 3. generic calls to do.x
###############################################################################
event.birth <- function( community, species )
{
## now only one offspring at a time,
## but could depend on individual
offspring <- get.offspring( community, species)
## update parent based on anticipated offspring
community <- parent.birth( community, species, offspring )
if( offspring ) {
## get new births
newbirths <- get.birth( community, species, offspring )
## merge births into community
community <- set.birth( community, species, newbirths )
}
community
}
###############################################################################
parent.birth <- function( community, species, offspring )
{
## get individual record
individual <- get.individual( community, species )
individual["offspring"] <- individual["offspring"] - offspring
## female starves when egg load depleted (less than or equal to 0)
if( individual["offspring"] <= 0 )
individual["stage"] <- set.future( community, species, "starved" )
community <- put.individual( community, species, individual )
}
###############################################################################
get.birth <- function( community, species, offspring )
{
## get individual record
individual <- get.individual( community, species )
## matrix of new offspring for community
newbirths <- matrix( individual, length( individual ), offspring )
dimnames( newbirths ) <- list( names( individual ), NULL )
if( offspring ) {
## assumes newborn is stage 1, and next stage is 2
newbirths["stage",] <- 1
newbirths["future",] <- 2
## set up as unlinked node for leftist tree
newbirths[c("dist","left","right","up"),] <- 1
## disperse offspring across substrate types
newbirths <- event.move( community, species, newbirths )
}
if( getCount( community,, "debug" ))
cat( round( individual["time"] ),
getOrgFeature( community, species, "units" ),
":", species, "offspring", individual["offspring"], "\n" )
if( offspring ) {
## get future events for new organisms
get.future( community, species, newbirths)
}
else
community
}
###############################################################################
get.deplete <- function( community, species )
{
## get individual record of attacker
individual <- get.individual( community, species )
## Deplete reserves based on time spent searching for host.
individual["offspring"] <- individual["offspring"] -
( individual["time"] - individual["location"] ) /
getOrgFeature( community, species, "deplete" )
community <- put.individual( community, species, individual )
}
###############################################################################
set.future <- function( community, species, stage )
{
current <- getOrgFuture( community, species, "current" )
seq( length( current ))[ current == stage ]
}
###############################################################################
### Interaction Events (only attack for now)
###############################################################################
event.attack <- function( community, species )
{
## dyadic event: attack of host by adult parasitoid
## deplete individual based on time spent searching for host
community <- get.deplete( community, species )
## get individual record of attacker
individual <- get.individual( community, species )
## find a host if parasite has offspring reserve left
if( individual["offspring"] > 0 ) {
## get name of host for attacker
host <- getOrgFeature( community, species, "attack" )
## get attack parasite and event types
attack <- get.attack( community, species, individual )
## find host located on the same substrate
found <- event.find( community, species, host, attack["event"] )
if( length( found )) {
## host-parasite interaction
event.parsed <- get( paste( "event", attack["event"], sep="." ))
community <- event.parsed( community, species, host, found )
event.parsed <- get( paste( "host", attack["parasite"], sep="." ))
community <- event.parsed( community, species, host, found )
individual <- get.individual( community, species )
}
}
if( individual["offspring"] > 0 ) {
## parasite moves along substrate
individual <- event.move( community, species, individual )
}
else {
## parasite dies if it does not feed enough
individual["stage"] <- set.future( community, species, "starved" )
}
## put updated individual back in community
community <- put.individual( community, species, individual )
}
###############################################################################
get.attack <- function( community, species, individual )
{
## get parasite type ("ecto" or "endo") and current event ("feed" or "ovip")
parasite <- getOrgFeature( community, species, "parasite" )
event <- getOrgFuture( community, species, "current", individual["future"] )
event <- as.character( event )
if( parasite=="endo" )
event <- "ovip"
else if( individual["offspring"] < 1 ) {
## must feed if depleted
event <- "feed"
}
c( event = event, parasite = parasite )
}
###############################################################################
host.ecto <- function( community, species, host, dead )
{
## ectoparasites effectively kill their host
## get individual doing the attack
individual <- get.individual( community, species )
## get host individual that is attacked
hostindiv <- get.individual( community, host, dead )
## set host time to now, which may involve hr-DD translation
hostindiv["time"] <- transTime( community, species, host, individual["time"] )
## schedule immediate death of host
hostindiv["future"] <- set.future( community, host, "death" )
## update host record in community
community <- put.individual( community, host, hostindiv, dead )
## update leftist tree and mintime
community <- put.species( community, host,
leftist.update( get.species( community, host ), dead ))
update_mintime( community, host )
}
###############################################################################
host.endo <- function( community, species, host, dead, harm )
{
## endoparasites reduces capacity of host (assumed by half here)
## get host individual that is attacked
hostindiv <- get.individual( community, host, harm )
## schedule harm for hosts (reduce egg capacity by half)
hostindiv["offspring"] <- floor( hostindiv["offspring"] / 2 )
if( hostindiv["offspring"] == 0 ) {
## get individual doing the attack
individual <- get.individual( community, species )
## set host time to now, which may involve hr-DD translation
hostindiv["time"] <- transTime( community, species, host, individual["time"] )
## schedule immediate death of host
hostindiv["future"] <- set.future( community, host, "death" )
}
## update host record in community
community <- put.individual( community, host, hostindiv, harm )
if( hostindiv["offspring"] == 0 ) {
## update leftist tree and mintime
community <- put.species( community, host,
leftist.update( get.species( community, host ), dead ))
community <- update_mintime( community, host )
}
community
}
###############################################################################
event.feed <- function( community, species, host, dead )
{
## feed: adult parasite feeds on host
interact <- get.interact( community, species, host, dead, "feed" )
## host-parasite interaction: feeding
if( interact ) {
individual <- get.individual( community, species )
individual["offspring"] <- individual["offspring"] + interact
community <- put.individual( community, species, individual )
}
community
}
###############################################################################
event.ovip <- function( community, species, host, dead, gender=TRUE )
{
## ovip: adult lays egg in the host to emerge later as adult
offspring <- get.offspring( community, species )
interact <- get.interact( community, species, host, dead, "ovip" )
## host-parasite interaction: feeding
if( interact ) {
## update parent individual, depleting energy after egg laying
community <- parent.birth( community, species, offspring )
## get new births
newbirths <- get.birth( community, species, offspring )
## gender preference for offspring
if( get.interact( community, species, host, dead, "male" ) < stats::runif( 1 ) |
!gender ){
## set offspring for female eggs based on dead host
newbirths["offspring", ] <- set.offspring( community, species, host, dead )
}
else {
## produce male and put on queue for immediate death
newbirths <- set.male( community, species, host, newbirths )
}
community <- set.birth( community, species, newbirths )
}
community
}
###############################################################################
set.male <- function( community, species, host, newbirths )
{
## produce male offspring, which is queued for immediate death
newbirths["future",1] <- set.future( community, species, "male" )
newbirths["time",1] <- get.individual( community, species )["time"]
newbirths
}
# --- Source: event.future.R ---
event.future <- function( community, species )
{
## schedule future event based on current stage
individual <- get.future( community, species )
## move if appropriate
individual <- event.move( community, species, individual )
## update time translation if needed
community <- checkTime( community, individual["time"],
getCount( community, species, "mintime"),
getOrgFeature( community, species, "units" ))
## put updated individual back in community
community <- put.individual( community, species, individual )
## reprioritize the leftist tree if time has changed
if( individual["time"] > individual["location"] ) {
## and time is longer than next scheduled time
if( individual["time"] >
min( get.species.element( community, species, "time",
individual[c("left","right")] ))) {
## remove individual from leftist tree
community <- put.species( community, species,
leftist.update( get.species( community, species )))
}
community <- update_mintime( community, species )
}
community
}
# --- Source: Events.R ---
###########################################################################################
updateEvents <- function( community, species, event, increment = 1 )
{
community$count$events[[species]][event,"during"] <-
community$count$events[[species]][event,"during"] + increment
community
}
###########################################################################################
setEvents <- function( community, period )
{
count <- community$count
for( species in get.species( community )) {
current <- getOrgFuture( community, species, "current" )
events <- rep( 0, length( current ))
names( events ) <- as.character( current )
stage <- getOrgAlive( community, species, "stage" )
if( length( stage )) {
tmp <- tapply( stage, current[stage], length )
tmp[ is.na( tmp ) ] <- 0
events[ names( tmp ) ] <- tmp
}
count$events[[species]][,period] <- events
}
community$count <- count
community
}
# --- Source: leftist.R ---
## $Id: leftist.R,v 1.0 2002/12/11 yandell@stat.wisc.edu Exp $
##
## Functions for Bland Ewing's modeling.
##
## Copyright (C) 2000,2001,2002 Brian S. Yandell.
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2, or (at your option) any
## later version.
##
## These functions are distributed in the hope that they will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## The text of the GNU General Public License, version 2, is available
## as http://www.gnu.org/copyleft or by writing to the Free Software
## Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
###########################################################################################
## This version has list structure for individuals.
## In addition it will keep species separate.
###########################################################################################
## Triply linked leftist trees
###########################################################################################
leftist.tree <- function( x,
tree = data.frame( root = empty ),
key = "time",
empty = c( time = NA, dist = 1, left = 1, right = 1, up = 1 ))
{
node <- empty
node[key] <- Inf
node[c("right","left")] <- 2
node["dist"] <- 0
if( missing( tree ))
names( tree ) <- NULL
node <- empty
node[key] <- x[1]
base <- 2
tree[[base]] <- node
for( i in 1 + seq( 2, length( x ))) {
node <- empty
node[key] <- x[i-1]
if( node[key] <= tree[[base]][key] ) {
## insert at root
node["left"] <- base
tree[[base]]["up"] <- i
base <- i
tree[[base]] <- node
}
else {
tree[[i]] <- node
## insert in tree (e.g. merge two trees)
tree <- as.data.frame( leftist.merge( as.matrix( tree ), i, base ))
base <- tree["up",1]
}
}
tree[[1]][c("right","left","up")] <- base
tree
}
###########################################################################################
leftist.create <- function( tree, key = "time" )
{
## assume 1st element of tree is for base
## and keys are in place already
base <- 2
tree[c("right","left","up"),] <- 1
tree["dist",-1] <- 1
ntree <- ncol( tree )
for( i in seq( 3, ntree )) {
if( tree[key,i] <= tree[key,base] ) {
## insert at root
tree["left",i] <- base
tree["up",base] <- i
base <- i
}
else {
## insert in tree (e.g. merge two trees)
tree <- leftist.merge( tree, i, base )
base <- tree["up",1]
}
}
## set 1st element to point to base
tree[c("right","left","up"),1] <- base
tree
}
###########################################################################################
leftist.merge <- function( tree, P = 1, Q = 1, R = 1, key = "time" )
{
while( TRUE ) {
if( P == 1 ) {
P <- Q
Q <- 1
}
if( Q == 1 ) {
D <- tree["dist",P]
while( R > 1 ) {
Q <- tree["right",R]
temp <- tree["left",R]
if( tree["dist",temp] < D ) {
D <- tree["dist",temp] + 1
tree["right",R] <- tree["left",R]
tree["left",R] <- P
}
else {
D <- D + 1
tree["right",R] <- P
}
tree["up",P] <- R
tree["dist",R] <- D
P <- R
R <- Q
}
tree["up",P] <- 1
tree[c("left","right","up"),1] <- P
return( tree )
}
## merge two right lists
if( tree[key,P] <= tree[key,Q] ) {
temp <- tree["right",P]
tree["right",P] <- R
tree["up",R] <- P
R <- P
P <- temp
}
else {
temp <- tree["right",Q]
tree["right",Q] <- R
tree["up",R] <- Q
R <- Q
Q <- temp
}
}
tree["up",1] <- P
tree[c("left","right","up"),P] <- 1
tree
}
###########################################################################################
leftist.remove <- function( tree, P )
{
oldbase <- tree["up",1]
up <- tree["up",P]
## merge the subtrees below P
tree <- leftist.merge( tree, tree["left",P], tree["right",P] )
## reset node to empty
tree[c("up","left","right","dist"),P] <- 1
## return if base node removed
if( oldbase == P )
return( tree )
## make up node leftist
if( tree["left",up] == P )
tree["left",up] <- tree["right",up]
tree["right",up] <- 1
tree["dist",up] <- 1
## traverse back up the tree to make sure it is leftist to base
upup <- tree["up",up]
left <- tree["left",upup]
right <- tree["right",upup]
while( tree["dist",left] < tree["dist",right] ) {
tree["dist",upup] <- tree["dist",left] + 1
tree["left",upup] <- right
tree["right",upup] <- left
upup <- tree["up",upup]
left <- tree["left",upup]
right <- tree["right",upup]
}
## merge down and up trees
leftist.merge( tree, oldbase, tree["up",1] )
}
###########################################################################################
leftist.birth <- function( organism, neworg, free )
{
newbase <- organism["up",1]
norganism <- ncol( neworg )
j <- norganism
nfree <- free[1]
while( j > 0 & nfree > 1 ) {
newbabe <- free[nfree]
organism[,newbabe] <- neworg[,j]
organism <- leftist.merge( organism, newbase, newbabe )
newbase <- organism["up",1]
nfree <- nfree - 1
free[1] <- nfree
j <- j - 1
}
if( j > 0 ) {
norganism <- ncol( organism )
js <- 1:j
organism <- cbind( organism, neworg[,js] )
for( i in norganism + js ) {
organism <- leftist.merge( organism, newbase, i )
newbase <- organism["up",1]
}
}
list( tree = organism, base = newbase, free = free )
}
###########################################################################################
leftist.free <- function( free, id )
{
free[1] <- tmp <- free[1] + 1
free[tmp] <- id
free
}
###########################################################################################
leftist.update <- function( tree, P = tree["up",1] )
{
tree <- leftist.remove( tree, P )
leftist.merge( tree, tree["up",1], P )
}
# --- Source: community.R ---
get.organisms <- function(datafile = "") {
org <- list(species = c("host", "parasite"), substrates = "substrate")
if(datafile != "") {
if(tools::file_ext(datafile) %in% c("xls","xlsx")){
sheets <- readxl::excel_sheets(datafile)
species <- stringr::str_remove(
sheets[stringr::str_detect(sheets, "future\\.")],
"future\\.")
substrates <- unique(stringr::str_remove(
sheets[stringr::str_detect(sheets, paste("\\.", species, sep = "", collapse = "|")) &
!stringr::str_detect(sheets, paste(c("future", species), "\\.", sep = "", collapse = "|"))],
"\\..*"))
org <- list(species = species, substrates = substrates)
}
}
org
}
###########################################################################################
get.species <- function( community, species ) {
if( missing( species ))
return( names( community$pop ))
if( is.numeric( species ))
species <- names( community$pop )[species]
if( is.null( species ) || !species %in% names( community$pop ))
return( NULL )
ans <- community$pop[[species]]
if (is.list(ans) && !is.matrix(ans) && !is.data.frame(ans) && !is.null(ans$org)) {
ans <- ans$org
}
if (is.matrix(ans) || is.data.frame(ans)) {
if (!"up" %in% rownames(ans) && (is.null(colnames(ans)) || colnames(ans)[1] != "dummy")) {
dummy <- ans[, 1, drop = FALSE]
colnames(dummy) <- "dummy"
return(cbind(dummy, ans))
}
}
ans
}
###########################################################################################
get.species.element <- function( community, species, rows, cols )
community$pop[[species]][rows,cols]
###########################################################################################
put.species <- function( community, species, value )
{
community$pop[[species]] <- value
community
}
###############################################################################
put.individual <- function( community, species, individual,
id = get.base( community, species ))
{
community$pop[[species]][,id] <- individual
community
}
###############################################################################
get.individual <- function( community, species,
id = get.base( community, species ))
community$pop[[species]][,id]
###############################################################################
get.base <- function( community, species )
community$pop[[species]]["up",1]
# --- Source: c.ewing.R ---
c.ewing <- function(...) {
communities <- list(...)
if(length(communities) < 2) {
if(length(communities) == 1)
return(communities[[1]])
return(NULL)
}
community <- communities[[1]]
# *** need to verify that all communities have save structure
# Elements org, temp should be identical
for(comi in seq(2, length(communities))) {
# Element pop
for(species in names(community$pop)) {
community$pop[[species]] <-
cbind(community$pop[[species]], communities[[comi]]$pop[[species]])
}
# Element cpu
community$cpu <- community$cpu + communities[[comi]]$cpu
}
# Element plot is more complicated as it contains items for plots
# Probably want some form of appendX functions
}
# --- Source: move.R ---
## $Id: move.R,v 1.0 2002/12/09 yandell@stat.wisc.edu Exp $
##
## Functions for Bland Ewing's modeling.
##
## Copyright (C) 2000,2001,2002 Brian S. Yandell.
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2, or (at your option) any
## later version.
##
## These functions are distributed in the hope that they will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## The text of the GNU General Public License, version 2, is available
## as http://www.gnu.org/copyleft or by writing to the Free Software
## Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
###############################################################################
event.move <- function( community, species, individual )
{
individual <- as.matrix( individual )
## move only if individual is in stage that moves
if( all( is.move( community, species, individual ))) {
## move among substrates?
individual["sub.future",] <-
sampleOrgSubstrate( community, species, individual["sub.stage",] )
## move to new position in substrate
position <- paste( "pos", letters[1:3], sep = "." )
individual[position,] <- rtri( ncol( individual ), 10, individual[position,] )
}
if( ncol( individual ) == 1 )
individual <- individual[,1]
individual
}
###############################################################################
is.move <- function( community, species, individual )
{
!is.na( match( getOrgFeature( community, species, "move" ),
getOrgFuture( community, species, "current" )[ individual["stage",] ] ))
}
###############################################################################
event.find <- function( community, species, host, event )
{
individual <- get.individual( community, species )
substrate <- individual["sub.stage"]
## pending event: need to find available hosts on substrate
avail <- get.alive( community, host, substrate )
navail <- length( avail )
if( !navail )
return( avail )
## preferences based on schedule
interact <- get.interact( community, species, host, avail, event )
if( length( event ) > 1 )
interact <- apply( interact, 1, sum )
sinteract <- sum( interact )
if( sinteract ) {
if( navail > 1 ) {
found <- sample( avail, 1, prob = interact / sinteract )
}
else
found <- avail
found
}
else
numeric(0)
}
# --- Source: offspring.R ---
## $Id: init.R,v 1.0 2002/12/09 yandell@stat.wisc.edu Exp $
##
## Functions for Bland Ewing's modeling.
##
## Copyright (C) 2000,2001,2002 Brian S. Yandell.
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2, or (at your option) any
## later version.
##
## These functions are distributed in the hope that they will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## Offspring Information
##########################################################################################
getOffspring <- function( community, species,
offspring = getOrgFeature( community, species, "offspring" ))
{
if( is.na( offspring ))
return( 0 )
if( is.numeric( offspring ))
return( offspring )
getOrgInteract( community, offspring, species, "offspring" )
}
##########################################################################################
initOffspring <- function( community, species )
{
hostname <- getOrgFeature( community, species, "offspring" )
## find if there is offspring load based on host
orgoffspring <- getOffspring( community, species, hostname )
norganism <- sum( getOrgAlive( community, species ))
if( length( orgoffspring ) == 1 ) {
## mean offspring does not depend on any host
offspring <- stats::rpois( norganism, orgoffspring )
}
else {
orgoffspring <- orgoffspring[ orgoffspring > 0 ]
if(!length(orgoffspring))
return(community)
## figure out initial offspring load based on host distribution
## mean offspring depends on host stages and events
host <- get.species( community, hostname )
if( is.null( host ))
stop( paste( "Host", hostname, "not initiated yet" ))
## get weights of host stages in terms of future event times
host <- host[ , getOrgAlive( community, hostname ) ]
## find host stages that are preferred by parasite
## need to take subset of current that are actually in host
hoststages <- match( names( orgoffspring ), getOrgFuture( community, hostname )$current,
nomatch = 0 )
host <- as.matrix( host[ , !is.na( match( host["stage",], hoststages )) ] )
if( ncol( host ) == 0 )
return( community )
tmp <- !is.na( match( hoststages, host["stage",] ))
hoststages <- hoststages[tmp]
orgoffspring <- orgoffspring[tmp]
if(!length(orgoffspring))
return(community)
dd <- tapply( host["time",], host["stage",], sum )
dd[ as.character( hoststages[
is.na( match( hoststages, names( dd ))) ] ) ] <- 0
dd[ is.na( dd ) ] <- 0
sdd <- sum( dd )
if( length( dd ) > 1 & sdd > 0)
offspring <- as.vector( sample( orgoffspring, norganism, replace = TRUE,
prob = dd / sdd ))
else
offspring <- rep( ( sdd > 0 ) * orgoffspring[1], norganism )
offspring[ is.na( offspring ) ] <- 0
}
organism <- get.species( community, species )
organism["offspring",-1] <- offspring
put.species( community, species, organism )
}
###############################################################################
get.offspring <- function( community, species )
{
individual <- get.individual( community, species )
if( individual["offspring"] > 0 )
1
else
0
}
###########################################################################################
set.offspring <- function( community, species, host, dead )
{
stage <- get.species.element( community, host, "stage", dead )
current <- getOrgFuture( community, host, "current", stage )
offspring <- getOrgInteract( community, host, species, "offspring")
offspring <- as.vector( offspring[ as.character( current ) ] )
offspring[ is.na( offspring ) ] <- 0
offspring
}
# --- Source: sim.R ---
## $Id: sim.R,v 0.9 2002/12/09 yandell@stat.wisc.edu Exp $
##
## Functions for Bland Ewing's modeling.
##
## Copyright (C) 2000,2001,2002 Brian S. Yandell.
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2, or (at your option) any
## later version.
##
## These functions are distributed in the hope that they will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## The text of the GNU General Public License, version 2, is available
## as http://www.gnu.org/copyleft or by writing to the Free Software
## Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
### migrate count$count* to writeCount and then retrieve with readCount
###########################################################################################
updateCount <- function(community, species, individual, is.death = FALSE, step) {
community <- updateEvents(community, species, individual["future"])
if (!missing(step)) {
community <- setCount(community, elements = list(step = step))
}
## move individual through age classes or drop if it dies
countage <- getCount( community, species, "countage" )
ageclass <- getOrgAgeClass( community, species, individual["stage"] )
if( !is.na( ageclass )) {
ageclass <- as.character( ageclass )
countage[ageclass] <- max(countage[ageclass] - 1, 0)
}
ageclass <- getOrgAgeClass( community, species, individual["future"] )
if( !is.na( ageclass )) {
ageclass <- as.character( ageclass )
countage[ageclass] <- countage[ageclass] + 1
}
## move individual across substrate elements or drop if it dies
countsub <- getCount( community, species, "countsub" )
substrate <- getOrgFeature( community, species, "substrate" )
elements <- getOrgInteract( community, substrate, species, "substrate" )
element <- elements[ individual["sub.stage"] ]
subclass <- getOrgFeature( community, species, "subclass" )
include <- subclass == getOrgAgeClass( community, species, individual[c("stage","future")] )
# need 2 values; if only one, replicate it.
include <- rep_len(include, 2)
## leave old substrate
if (!is.na(element) && include[1]) {
element <- as.character( element )
countsub[element] <- max(countsub[element] - 1, 0)
}
if (!is.death && include[2]) {
## move to new substrate
newsub <- individual["sub.future"]
if( !is.na( newsub )) {
element <- elements[newsub]
if( !is.na( element )) {
element <- as.character( element )
countsub[element] <- countsub[element] + 1
}
}
}
## record counts
community <- setCount( community, species,
list( countage = countage, countsub = countsub ))
community <- writeCount( community, species, individual["time"], individual["future"],
countage, countsub )
}
###########################################################################################
updateCounts <- function( community, species, newbirths )
{
community <- updateEvents( community, species, 1, newbirths )
countage <- getCount( community, species, "countage" )
countsub <- getCount( community, species, "countsub" )
subclass <- getOrgFeature( community, species, "subclass" )
ageclass <- getOrgAgeClass( community, species, 1 )
individual <- get.individual( community, species )
if( !is.na( ageclass[1] )) {
ageclass <- as.character( ageclass )
countage[ageclass] <- newbirths + countage[ageclass]
if( subclass == ageclass ) {
substrate <- getOrgSubstrate( community, species, individual["sub.stage"] )
if( !is.na( substrate[1] )) {
substrate <- as.character( substrate )
countsub[substrate] <- newbirths + countsub[substrate]
}
}
}
community <- setCount( community, species,
list( countage = countage, countsub = countsub ))
community <- writeCount( community, species, individual["time"], 1, countage, countsub )
}
###########################################################################################
put.base <- function( community, species, id,
free = getCount( community, species, "free" ))
{
base <- get.base( community, species )
if( !missing( id ))
free <- leftist.free( free, id )
mintime <- max( getCount( community, species, "mintime" ),
getTime( community, species,
get.species.element( community, species, "time", base )))
setCount( community, species, list( base = base, free = free, mintime = mintime ))
}
# --- Source: simple.R ---
simpleServer <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
})
}
simpleInput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::h4("Simulation Settings"),
shiny::sliderInput(ns("steps"),
label = "Simulation steps:",
min = 1000,
max = 10000,
value = 1000,
step = 500),
shiny::radioButtons(ns("nsim"),
"Number of Simulations",
c(1,10,20,50,100,200),
1, inline = TRUE),
shiny::actionButton(ns("go"), "Start Simulation"),
shiny::HTML("<hr style='height:1px;border:none;color:#333;background-color:#333;' />"),
shiny::h4("Save Files"),
shiny::uiOutput(ns("version"))
)
}
simpleOutput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::radioButtons(ns("button"), "", c("Plots", "Input Data"),
"Plots", inline = TRUE),
)
}
simpleApp <- function(title = "Population Ethology") {
ui <- shiny::fluidPage(
shiny::titlePanel(title),
shiny::sidebarLayout(
shiny::sidebarPanel(
simpleInput("simple")
),
shiny::mainPanel(
simpleOutput("simple")
)))
server <- function(input, output, server) {
simpleServer("simple")
}
shiny::shinyApp(ui = ui, server = server)
}
# --- Source: ring.R ---
## $Id: ring.R,v 1.0 2002/12/09 yandell@stat.wisc.edu Exp $
##
## Functions for Bland Ewing's modeling.
##
## Copyright (C) 2000,2001,2002 Brian S. Yandell.
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2, or (at your option) any
## later version.
##
## These functions are distributed in the hope that they will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## The text of the GNU General Public License, version 2, is available
## as http://www.gnu.org/copyleft or by writing to the Free Software
## Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
###########################################################################################
## Doubly linked rings
###########################################################################################
ring.add <- function( ring = data.frame( root = c( key = NA, left = 1 + nx, right = 2 )),
x )
{
nx <- length( x )
n1 <- ring["left","root"]
ring[[ as.character( n1 + 1 ) ]] <- c(
key = x[1],
left = suppressWarnings(as.numeric( ring["left","root"] )),
right = 1 )
if( nx > 1 ) for( i in seq( 2, nx ))
{
ring[[ as.character( n1 + i ) ]] <- c(
key = x[i],
left = n1+i-1,
right = 1 )
ring[ "right", as.character( n1 + i - 1 ) ] <- n1+i
}
ring["left","root"] <- n1 + nx
ring
}
###########################################################################################
ring.remove <- function( ring, P )
{
aP <- as.character( P )
if( is.na( match( aP, names( ring ))))
{
cat( paste( "Warning:", aP, "not found in ring\n" ))
return( ring )
}
left <- ring["left",aP]
right <- ring["right",aP]
ring[ "right", as.character( left ) ] <- right
ring[ "left", as.character( right ) ] <- left
ring[[aP]] <- NULL
ring
}
# --- Source: sierpinski.R ---
sierpinski <- function( stage = 5, reset = TRUE )
{
if( reset )
tmpar <- graphics::par( pty = "s", bty = "n", xaxt = "n", yaxt = "n", omi = rep(0,4),
mar = rep(0,4) )
aa <- 0:1
bb <- - aa
for( i in seq( stage )) {
tmp <- gasket( aa, bb )
aa <- tmp$aa
bb <- tmp$bb
tri <- tri2car.default( aa, bb )
r <- range( unlist( tri ))
plot( r, r, type = "n", xlab = "", ylab = "" )
graphics::lines(tri )
graphics::mtext( paste( "(", letters[1+i], ") Gasket of Order ", i, sep = "" ), 3, -2 )
# graphics::mtext( paste( "Gasket of order", i ), 3, -2 )
}
if( reset )
graphics::par( tmpar )
invisible( tri )
}
# --- Source: fileCount.R ---
putCount <- function( community, append = FALSE )
{
species <- get.species( community )
countage <- getCount( community,, "countage")
countsub <- getCount( community,, "countsub")
countbase <- getCount( community,, "base")
step_now <- if (append) (getCount( community,, "step" ) %||% 0) else 0
cnames <- c("step", "time", "future",
paste( "count",
seq( max( unlist( lapply( countage, length )) +
unlist( lapply( countsub, length )))),
sep = "" ))
cnums <- list()
for( i in species ) {
cnums[[i]] <- c(
step_now,
get.species.element( community, i, c("time","stage"), countbase[i] ),
countage[[i]],
countsub[[i]])
}
file <- getCount( community,, "file" )
if(!is.null(file) && is.character(file) && length(file) > 0) {
if( !( file.exists( file ) & append ))
cat( "species", cnames, "\n", file = file )
for( i in species ) {
cat( i, cnums[[i]], "\n", file = file, append = TRUE )
}
community
} else {
if(append) {
counts <- getCount( community,, "counts")
} else {
counts <- NULL
}
newcounts <- matrix(NA, length(species), length(cnames),
dimnames = list(species, cnames))
for(i in species) {
newcounts[i, seq_along(cnums[[i]])] <- cnums[[i]]
}
newcounts <- tibble::tibble(
data.frame(
species = species,
newcounts))
counts <- dplyr::bind_rows(
counts,
newcounts)
setCount( community,, list(counts = counts))
}
}
###########################################################################################
writeCount <- function( community, species, time, future, countage, countsub)
{
nstep <- getCount( community,, "step" )
cnums <- c(nstep, time, future, countage, countsub)
file <- getCount( community,, "file" )
if(!is.null(file) && is.character(file) && length(file) > 0) {
cat( species, cnums, "\n", file = file, append = TRUE )
community
} else {
counts <- getCount( community,, "counts")
cnames <- names(counts)[-1]
newcounts <- matrix(NA, 1, length(cnames),
dimnames = list(species, cnames))
newcounts[1, seq_along(cnums)] <- cnums
newcounts <- tibble::tibble(
data.frame(
species = species,
newcounts))
counts <- dplyr::bind_rows(
counts,
newcounts)
setCount( community,, list(counts = counts))
}
}
###########################################################################################
readCount <- function( community, species = unique(counts$species) )
{
file <- getCount( community,, "file" )
if(!is.null(file)) {
counts <- utils::read.table( file, header = TRUE, fill = TRUE )
} else {
counts <- getCount( community,, "counts")
}
count <- list()
for( i in species ) {
colnames <- c( levels( getOrgFuture( community, i, "ageclass" )),
levels( getOrgInteract( community,, i, "substrate" )))
count[[i]] <- as.matrix( counts[ counts$species == i, seq( 2, 4 + length( colnames )) ] )
dimnames( count[[i]] ) <- list( count[[i]][,"step"],
c( "step", "time", "future", colnames ))
}
count
}
# --- Source: count.join.R ---
count.join <- function( ... )
{
x <- list( ... )
numnum <- list()
for( i in seq( length( x ))) {
for( j in names( x[[i]] )) {
if( is.null( numnum[[j]] ))
numnum[[j]] <- x[[i]][[j]]
else
numnum[[j]] <- cbind( numnum[[j]], x[[i]][[j]] )
}
}
numnum
}
# --- Source: summary.ewing.R ---
summary.ewing <- function(object, ...) {
out <- list()
out$package <- object$org$package
out$species <- names(object$org$Future)
out$interact <- names(object$org$Interact)
out$meanvalue <- list()
for(i in out$species) {
out$meanvalue <- names(object$org$MeanValue[[i]])
}
if(length(object$pop)) {
out$stage <- list()
for(i in out$species)
out$stage[[i]] <-
table(object$org$Future[[i]]$current[getOrgAlive(object, i, "stage")])
}
if(!is.null(object$count)) {
out$events <- object$count$events
for( i in seq( length(out$events)))
out$events[[i]] <- apply(out$events[[i]], 2, function(x) {
tmp <- sum(x, na.rm = TRUE)
if(tmp > 0)
c(round(100 * x / tmp, 1), total = tmp)
else
c(x, total = 0)
})
}
out$cpu <- signif(object$cpu, 4)
class(out) <- c("summary.ewing", class(out))
out
}
print.summary.ewing <- function(x, ...) {
cat("Data initialization package:", x$package, "\n")
cat("Community species:", paste(x$species, collapse = ", " ), "\n")
cat("Community hosts:", paste(x$interact, collapse = ", "), "\n")
cat("Mean Value curves by species:")
mv <- FALSE
for(i in x$species) {
meanvalue <- x$meanvalue[[i]]
mv <- mv | !is.null(meanvalue)
if( !is.null(meanvalue)) {
cat("\n ", i, ":", paste( meanvalue, collapse = ", " ), "\n")
}
}
if(!mv)
cat(" none\n")
if(!is.null(x$stage)) {
cat("\nSimulation community has following counts:\n",
paste(x$species, lapply(x$stage, sum), sep = "=", collapse = ", "),
"\n")
print(x$stage)
}
if( !is.null( x$temp )) {
# ** later
}
if(!is.null(x$events)) {
print(x$events)
}
if(!is.null(x$cpu)) {
cat( "CPU timing by event in simulation\n" )
print(x$cpu)
}
}
# --- Source: summary_simobj.R ---
summary_simobj <- function(object) {
out <- sapply(object, function(x) {
if(is.list(x)) {
x <- names(x)
}
paste(x, collapse=",")
})
paste(paste(names(out), out, sep = ": "), collapse = "<br>")
}
# --- Source: ewing_discrete.R ---
ewing_discrete <- function(nsim, verbose = FALSE, ...) {
sims <- seq_len(nsim)
object <- as.list(sims)
names(object) <- sims
for(i in sims) {
if(verbose) cat(".")
object[[i]] <- ewing_discrete1(...)
}
make_ewing_discrete(object)
}
ewing_discrete1 <- function(siminit = init.simulation(interact = FALSE,
messages = FALSE, ...),
increment = 0.5, ...)
{
# Make sure increment is 1,2,5 x power of 10
incr <- pretty(increment)
increment <- incr[which.min(abs(incr - increment))[1]]
out <- future.events(siminit, refresh = 1000,
plotit = FALSE, messages = FALSE, ...)
attrs <- attributes(out)
# Get age classes used later for summaries and plots
items <- purrr::map(out$org$Future, function(x) levels(factor(x$ageclass)))
out <- readCount(out)
out <- purrr::map(
out,
function(x) {
purrr::map_df(
dplyr::distinct(
purrr::map_df(
dplyr::mutate(
as.data.frame(x),
time = ifelse(.data$step == 0, 0,
increment * ceiling(.data$time / increment))),
rev),
.data$time, .keep_all = TRUE),
rev)
})
attr(out, "count") <- attrs$count
attr(out, "nstep") <- attrs$nstep
attr(out, "items") <- items
out
}
summary.ewing_discrete <- function(object, ...) {
summary(ewing_envelopes(object), ...)
}
# --- Source: make_ewing_discrete.R ---
make_ewing_discrete <- function(object) {
nsim <- length(object)
class(object) <- c("ewing_discrete", class(object))
attr(object, "species") <- species <- names(object[[1]])
attr(object, "ordinate") <- "time"
attr(object, "count") <- attr(object[[1]], "count")
attr(object, "nstep") <- attr(object[[1]], "nstep")
attr(object, "items") <- attr(object[[1]], "items")
attr(object, "nsim") <- nsim
object
}
# --- Source: ggplot_ewing.R ---
ggplot_ewing <- function(object, step = 0, ageclass = TRUE,
substrate = !ageclass, ...)
{
if(!inherits(object, "ewing_snapshot")) {
object <- ewing_snapshot(object, step, ...)
}
step <- object$step
p <- list()
i <- 0
if(ageclass) {
i <- i + 1
p[[i]] <- ggplot2::autoplot(object$ageclass, ...)
}
if(substrate) {
species <- names(object$substrate)
for(j in species) {
i <- i + 1
p[[i]] <- ggplot2::autoplot(object$substrate[[j]], ...)
}
}
if(length(p) == 1) p <- p[[1]]
p
}
autoplot.ewing <- function(object, ...) {
ggplot_ewing(object, ...)
}
plot.ewing <- function(x, ...) {
ggplot_ewing(x, ...)
}
# --- Source: ggplot_current.R ---
ggplot_current <- function( x,
species,
col = as.character( future$color[stage] ),
headstuff = c( 0, "start"),
units = getOrgFeature( x, species, "units" ),
right = species, adj = c(0,.5,1),
position = paste( "pos", letters[1:3], sep = "." ),
pch = as.character( future$pch[stage] ), cex = 0.5,
stage = organism["stage",],
xlab = "horizontal", ylab = "vertical",
future = getOrgFuture( x, species, c("color","pch") ),
facet = TRUE, ...)
{
## plot current stages for species (except random parasites)
organism <- get.species( x, species )[,-1]
if(is.null(organism))
return(NULL)
tri_coord <- tri2car( organism[position,] )
tri_coord$col <- NA
tri_coord$label <- pch
values <- future$color
names(values) <- future$pch
values <- unique(values)
# Facet by Substrate.
# This needs to use generic function to get substrate names.
tmp <- names(x$org$Interact$substrate$substrate)
tri_coord$substrate <- tmp[organism["sub.stage",]]
p <- ggplot2::ggplot(tri_coord) +
ggplot2::aes(x, y, col = label, label = label) +
ggplot2::geom_text(size=3) +
ggplot2::xlab(xlab) +
ggplot2::ylab(ylab) +
ggplot2::ggtitle(paste(units, "future event", right),
subtitle = paste(headstuff, collapse = " ")) +
ggplot2::scale_color_manual(values = values)
if(facet) {
p <- p + ggplot2::facet_wrap(~substrate)
}
p
}
# --- Source: plot_null.R ---
plot_null <- function(msg = "no data") {
ggplot2::ggplot(data.frame(x = 1, y = 1),
ggplot2::aes(.data$x, .data$y, label = msg)) +
ggplot2::geom_text(size = 10) +
ggplot2::theme_void()
}
# --- Source: step_controls.R ---
step_size_choices <- c(1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000)
step_size_slider <- function(inputId, label = "Steps per click:", selected = 50) {
idx <- match(selected, step_size_choices)
if (is.na(idx)) idx <- 6
sl <- shiny::sliderInput(inputId, label, min = 1, max = length(step_size_choices), value = idx, step = 1, ticks = TRUE)
sl$children[[2]]$attribs[['data-values']] <- paste(step_size_choices, collapse = ",")
sl
}
parse_step_size <- function(val) {
if (is.null(val)) return(50)
num <- round(as.numeric(val))
if (is.na(num)) return(50)
# Direct large values (e.g., 20, 50, 100, 200, 500, 1000, 2000)
if (num %in% step_size_choices && num > 10) {
return(num)
}
# 0-based JavaScript index from ion.rangeSlider (0 to 10)
if (num >= 0 && num < length(step_size_choices)) {
return(step_size_choices[num + 1])
}
# Fallback for direct value
if (num %in% step_size_choices) {
return(num)
}
50
}
axisUnitInput <- function(id, time_label = "Time", selected = "step") {
ns <- shiny::NS(id)
choices <- c("Steps" = "step")
choices[time_label] <- "time"
shiny::radioButtons(ns("x_var"), "Display Units:",
choices = choices,
selected = selected, inline = TRUE)
}
axisUnitServer <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
shiny::reactive({ if (!is.null(input$x_var)) input$x_var else "step" })
})
}
ageClassControlInput <- function(id, time_label = "Time") {
ns <- shiny::NS(id)
shiny::tagList(
axisUnitInput(ns("axis_unit"), time_label = time_label),
shiny::checkboxInput(ns("norm"), "Normalize Plot", TRUE),
shiny::checkboxInput(ns("total"), "Include Total in Plot", TRUE)
)
}
ageClassControlServer <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
x_var <- axisUnitServer("axis_unit")
list(
x_var = x_var,
norm = shiny::reactive({ if (!is.null(input$norm)) input$norm else TRUE }),
total = shiny::reactive({ if (!is.null(input$total)) input$total else TRUE })
)
})
}
# --- Source: initParApp.R ---
initParApp <- function(title = "Population Ethology") {
ui <- bslib::page_sidebar(
title = title,
sidebar = bslib::sidebar(
initParInput("init_par")),
initParUI("init_par"),
initParOutput("init_par")
)
server <- function(input, output, server) {
init_par <- initParServer("init_par")
}
shiny::shinyApp(ui = ui, server = server)
}
initParServer <- function(id, simres = shiny::reactiveVal(NULL), datafile = shiny::reactiveVal("")) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
species <- shiny::reactive({
get.organisms()$species
})
output$sppsize <- shiny::renderUI({
shiny::req(species()) # "host", "parasite"
lapply(species(), function(x) {
shiny::sliderInput(ns(x),
label = paste0("Number of ", x, "s:"),
min = 0,
max = 500,
value = 100,
step = 20)
})
})
datanames <- shiny::reactive({
getOrgNames()
})
output$inputfiles <- shiny::renderUI({
shiny::tagList(
shiny::selectInput(ns("dataname"), "", datanames(), "organism.features"),
DT::dataTableOutput(ns("org_table")))
})
output$org_table <- DT::renderDataTable({
getOrgDataSimple(simres(), shiny::req(input$dataname), datafile())
}, escape = FALSE, options = list(scrollX = TRUE, pageLength = 10))
# Show parameters
output$show_par <- shiny::renderUI({
nlist <- names(input)
# Remove any internal inputs, which have numbers.
glist <- grep("[0-9]", names(input))
if(length(glist))
nlist <- nlist[-glist]
# Construct output string.
out <- paste0("inputs: ", paste(nlist, collapse = ", "))
for(i in nlist) {
out <- paste(out, "<br>",
paste(i, input[[i]], sep = " = "))
}
shiny::HTML(out)
})
# Return.
input
})
}
initParInput <- function(id) {
ns <- shiny::NS(id)
shiny::uiOutput(ns("sppsize"))
}
initParUI <- function(id) {
ns <- shiny::NS(id)
shiny::uiOutput(ns("show_par"))
}
initParOutput <- function(id) {
ns <- shiny::NS(id)
shiny::uiOutput(ns("inputfiles"))
}
# --- Source: initApp.R ---
initApp <- function(title = "Population Ethology") {
ui <- bslib::page_sidebar(
title = title,
sidebar = bslib::sidebar(
initParInput("init_par")),
initOutput("init"),
substrateOutput("substrate")
)
server <- function(input, output, server) {
init_par <- initParServer("init_par")
siminit <- initServer("init", init_par)
substrateServer("substrate", siminit)
}
shiny::shinyApp(ui = ui, server = server)
}
initServer <- function(id, init_par) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
siminit <- shiny::reactive({
init.simulation(count = as.numeric(c(shiny::req(init_par$host),
shiny::req(init_par$parasite))))
})
output$init <- shiny::renderUI({
out <- summary_simobj(shiny::req(siminit()))
shiny::HTML(out)
})
# Return.
siminit
})
}
initOutput <- function(id) {
ns <- shiny::NS(id)
shiny::uiOutput(ns("init"))
}
# --- Source: substrateApp.R ---
substrateApp <- function(title = "Substrate Organism Movement Explorer") {
ui <- bslib::page_sidebar(
title = title,
sidebar = bslib::sidebar(
initParInput("init_par"),
shiny::hr(),
substrateInput("substrate")),
substrateOutput("substrate")
)
server <- function(input, output, server) {
init_par <- initParServer("init_par")
siminit <- initServer("init", init_par)
substrateServer("substrate", siminit)
}
shiny::shinyApp(ui = ui, server = server)
}
substrateInput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::div(
style = "font-size: 0.85rem;",
shiny::h4("Substrate Display & Stepping", style = "font-size: 1rem; font-weight: 600; margin-bottom: 8px;"),
shiny::checkboxGroupInput(ns("show_species"), "Species to Display:",
choices = c("Host" = "host", "Parasite" = "parasite"),
selected = c("host", "parasite"),
inline = TRUE),
shiny::radioButtons(ns("species_mode"), "Species Mode:",
choices = c("Overlay (1 Map)" = "overlay", "Separate (Adjacent Maps)" = "separate"),
selected = "overlay", inline = TRUE),
shiny::div(style = "border-top: 1px solid rgba(0,0,0,0.1); margin: 6px 0;"),
shiny::radioButtons(ns("layout"), "Layout View:",
choices = c("Hex Substrate Overlay" = "hex", "Faceted Substrates" = "facet"),
selected = "hex", inline = TRUE),
shiny::div(style = "border-top: 1px solid rgba(0,0,0,0.1); margin: 6px 0;"),
shiny::span("Simulation Stepping:", style = "font-weight: 600; color: #1a73e8; display: block; margin-bottom: 4px;"),
shiny::div(
style = "display: flex; gap: 4px; margin-bottom: 8px;",
shiny::actionButton(ns("step1"), "+1 Step", class = "btn-sm btn-outline-primary flex-fill"),
shiny::actionButton(ns("step10"), "+10 Steps", class = "btn-sm btn-outline-primary flex-fill"),
shiny::actionButton(ns("step100"), "+100 Steps", class = "btn-sm btn-outline-primary flex-fill"),
shiny::actionButton(ns("reset_sim"), "Reset", class = "btn-sm btn-outline-secondary")
),
shiny::div(style = "border-top: 1px solid rgba(0,0,0,0.1); margin: 6px 0;"),
shiny::checkboxGroupInput(ns("layers"), "Display Layers:",
choices = c("Substrate Boundaries" = "poly",
"Hex Grid Overlay" = "hex",
"Organisms" = "organisms",
"Substrate Names" = "centers",
"Side Numbers" = "labels"),
selected = c("poly", "hex", "organisms", "centers", "labels")),
shiny::div(style = "border-top: 1px solid rgba(0,0,0,0.1); margin: 6px 0;"),
shiny::div(
style = "display: flex; gap: 8px;",
shiny::numericInput(ns("width"), "Radius:", value = 10, min = 2, max = 30, step = 1),
shiny::numericInput(ns("step_density"), "Step Density:", value = 1, min = 0.5, max = 5, step = 0.5)
)
)
)
}
substrateServer <- function(id, simres, width = 10, step_density = 1) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
current_sim <- shiny::reactiveVal(NULL)
shiny::observeEvent(simres(), {
current_sim(simres())
})
shiny::observeEvent(input$step1, {
sim <- current_sim()
if (!is.null(sim)) {
res <- future.events(sim, nstep = 1, plotit = FALSE)
current_sim(res)
}
})
shiny::observeEvent(input$step10, {
sim <- current_sim()
if (!is.null(sim)) {
res <- future.events(sim, nstep = 10, plotit = FALSE)
current_sim(res)
}
})
shiny::observeEvent(input$step100, {
sim <- current_sim()
if (!is.null(sim)) {
res <- future.events(sim, nstep = 100, plotit = FALSE)
current_sim(res)
}
})
shiny::observeEvent(input$reset_sim, {
current_sim(simres())
})
available_species <- shiny::reactive({
sim <- current_sim()
if (!is.null(sim) && !is.null(sim$pop)) names(sim$pop) else NULL
})
selected_species <- shiny::reactive({
avail <- available_species()
if (is.null(avail)) return(NULL)
sel <- input$show_species
if (is.null(sel) || length(sel) == 0) avail else intersect(sel, avail)
})
sppplot <- shiny::reactive({
spp <- selected_species()
shiny::req(spp)
sim <- current_sim()
shiny::req(sim)
layout_val <- if (!is.null(input$layout)) input$layout else "hex"
mode_val <- if (!is.null(input$species_mode)) input$species_mode else "overlay"
w_val <- if (!is.null(input$width)) input$width else width
sd_val <- if (!is.null(input$step_density)) input$step_density else step_density
layers_val <- if (!is.null(input$layers)) input$layers else c("poly", "hex", "organisms", "centers", "labels")
sim_single <- if (inherits(sim, "ewing_discrete") && is.list(sim) && length(sim) > 0) sim[[1]] else sim
if (inherits(sim_single, "ewing")) {
if (mode_val == "overlay" && layout_val == "hex") {
sub_data <- ewing_substrate(sim_single, spp, layout = layout_val, width = w_val, step_density = sd_val)
if (!is.null(sub_data)) {
p_obj <- ggplot_ewing_substrate(sub_data, layout = layout_val, width = w_val, step_density = sd_val, layers = layers_val)
list(p_obj)
} else {
list()
}
} else {
p <- lapply(spp, function(x) {
sub_data <- ewing_substrate(sim_single, x, layout = layout_val, width = w_val, step_density = sd_val)
if (!is.null(sub_data)) {
p_obj <- ggplot_ewing_substrate(sub_data, layout = layout_val, width = w_val, step_density = sd_val, layers = layers_val)
p_obj
} else {
NULL
}
})
p[!sapply(p, is.null)]
}
} else {
list()
}
})
output$sppPlot <- shiny::renderPlot({
plots <- sppplot()
if (!is.null(plots) && length(plots) > 0) {
cowplot::plot_grid(plotlist = plots, ncol = length(plots), align = "h")
} else {
ggplot2::ggplot() + ggplot2::theme_void() + ggplot2::ggtitle("No active species selected to plot")
}
})
output$substrate_plot <- shiny::renderUI({
plots <- sppplot()
h_px <- 500
shiny::plotOutput(ns("sppPlot"), height = paste0(h_px, "px"))
})
# Return current simulation state for downstream composition
current_sim
})
}
substrateOutput <- function(id) {
ns <- shiny::NS(id)
shiny::uiOutput(ns("substrate_plot"))
}
# --- Source: distPlotApp.R ---
distPlotApp <- function(title = "Population Ethology") {
ui <- bslib::page_sidebar(
title = title,
sidebar = bslib::sidebar(
initParInput("init_par"),
distPlotInput("dist_plot"),
futureInput("future")),
distPlotOutput("dist_plot")
)
server <- function(input, output, server) {
init_par <- initParServer("init_par")
siminit <- initServer("init", init_par)
simres <- futureServer("future", siminit)
distPlotServer("dist_plot", simres)
}
shiny::shinyApp(ui = ui, server = server)
}
distPlotServer <- function(id, simres, x_var = NULL, total = NULL, norm = NULL) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
dist_plot <- shiny::reactive({
sim <- if (is.reactive(simres)) simres() else simres
shiny::req(sim)
tot_val <- if (is.reactive(total)) total() else if (!is.null(total)) total else if (!is.null(input$total)) input$total else TRUE
norm_val <- if (is.reactive(norm)) norm() else if (!is.null(norm)) norm else if (!is.null(input$norm)) input$norm else TRUE
xv_val <- if (is.reactive(x_var)) x_var() else if (!is.null(x_var)) x_var else if (!is.null(input$x_var)) input$x_var else "step"
if (is.null(tot_val)) tot_val <- TRUE
if (is.null(norm_val)) norm_val <- TRUE
if (is.null(xv_val) || !xv_val %in% c("step", "time")) xv_val <- "step"
object <- tryCatch({
ewing_ageclass(sim, total = tot_val, normalize = norm_val)
}, error = function(e) NULL)
if (is.null(object)) return(plot_null("no simulation"))
ggplot2::autoplot(object, x_var = xv_val)
})
output$dist_plot <- shiny::renderPlot({
dist_plot()
})
# Return.
dist_plot
})
}
distPlotInput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::checkboxInput(ns("norm"), "Normalize Plot", TRUE),
shiny::checkboxInput(ns("total"), "Include Total", TRUE))
}
distPlotOutput <- function(id) {
ns <- shiny::NS(id)
shiny::plotOutput(ns("dist_plot"), height = "400px")
}
# --- Source: multApp.R ---
multApp <- function(title = "Population Ethology") {
ui <- shiny::fluidPage(
shiny::titlePanel(title),
shiny::sidebarLayout(
shiny::sidebarPanel(
multInput("ewing")
),
shiny::mainPanel(
multOutput("ewing")
)))
server <- function(input, output, server) {
multServer("ewing")
}
shiny::shinyApp(ui = ui, server = server)
}
multServer <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
simres <- shiny::reactive({
nsim <- as.integer(shiny::req(input$nsim))
shiny::withProgress(
message = paste('Ewing Discrete', nsim, 'Simulations ...'),
value = 0,
{
out <- as.list(seq_len(nsim))
inc <- 1 / nsim
for(i in seq_len(nsim)) {
shiny::incProgress(inc)
out[[i]] <- ewing_discrete1(
count = as.numeric(c(input$host, input$parasite)),
nstep = input$steps)
}
}
)
})
distplot <- shiny::reactive({
if(inherits(simres(), "ewing")) {
ggplot2::autoplot(ewing_ageclass(simres(), total = input$total,
normalize = input$norm))
} else {
NULL
}
})
output$distPlot <- shiny::renderPlot({
distplot()
})
# *** This is not right. Need to get each species name here and in `ewing_substrate`
# species <- ewing:::getOrgFeature(simres)
# gives list but includes substrates.
# can figure out what substrate goes to species with
# ewing:::getOrgFeature(simres, species[i], "substrate")
# if it is NA (or "NA"), then that is a substrate.
# So cycle through species generating plots.
# put as much in `ewing_substrate` as possible.
species <- shiny::reactive({
get.organisms(datafile())$species
})
substrates <- shiny::reactive({
get.organisms(datafile())$substrates
})
output$sppsize <- shiny::renderUI({
shiny::req(species())
lapply(species(), function(x) {
shiny::sliderInput(ns(x),
label = paste0("Number of ", x, "s:"),
min = 0,
max = 500,
value = 100,
step = 20)
})
})
sppplot <- shiny::reactive({
shiny::req(species(), simres())
if(inherits(simres(), "ewing")) {
if(!is.null(simres())) {
p <- lapply(species(), function(x) {
p <- ggplot2::autoplot(ewing_substrate(simres(), x))
if(inherits(p, "ggplot"))
p <- p + ggplot2::ggtitle(paste(x, "on", substrates()[1]))
p
})
if(any(unlist(purrr::map(p, is.null))))
p <- NULL
p
}
} else {
ggplot2::ggplot()
}
})
output$sppPlot <- shiny::renderPlot({
if(!is.null(sppplot())) {
spp <- length(species())
cowplot::plot_grid(plotlist = sppplot(), nrow = spp)
} else {
ggplot2::ggplot()
}
})
envdata <- shiny::reactive({
shiny::req(simres())
if(inherits(simres(), "ewing_discrete")) {
ewing_envelopes(simres())
} else {
NULL
}
})
envelopePlot <- shiny::reactive({
shiny::req(envdata())
nsim <- as.integer(shiny::req(input$nsim))
conf <- (nsim >= 10) & input$conf
if(inherits(simres(), "ewing_discrete")) {
ggplot_ewing_envelopes(envdata(), conf)
} else {
NULL
}
})
output$envPlot <- shiny::renderPlot({
envelopePlot()
})
output$plots <- shiny::renderUI({
nsim <- as.integer(shiny::req(input$nsim), simres())
if(nsim == 1) {
shiny::req(species())
shiny::tagList(
shiny::plotOutput(ns("distPlot"), height = "400px"),
shiny::plotOutput(ns("sppPlot"), height = paste0(200 * length(species()), "px")))
} else {
shiny::plotOutput(ns("envPlot"))
}
})
data <- reactive({
nsim <- as.integer(shiny::req(input$nsim))
species <- shiny::req(input$species)
if(nsim == 1) {
readCount(simres())[[species]]
} else {
shiny::req(envdata())
print(envdata(), species = species)
}
})
params <- shiny::reactive({
nsim <- shiny::req(input$nsim)
paste(shiny::req(input$host), shiny::req(input$parasite),
shiny::req(input$steps), nsim, sep = "_")
})
output$downloadRun <- shiny::downloadHandler(
filename = function() {
paste0(paste(shiny::req(input$outfile), shiny::req(input$species), params(), sep = "_"), ".csv") },
content = function(file) {
utils::write.csv(data(), file, row.names = FALSE)
}
)
output$downloadPlot <- shiny::downloadHandler(
filename = function() {
paste0(paste(shiny::req(input$plotfile), params(), sep = "_"), ".pdf") },
content = function(file) {
grDevices::pdf(file, width = 9)
nsim <- as.integer(shiny::req(input$nsim))
if(nsim == 1) {
print(distplot())
for(i in species()) {
print(sppplot()[[i]])
}
} else {
print(envelopePlot())
}
grDevices::dev.off()
}
)
datanames <- shiny::reactive({
getOrgNames(datafile())
})
output$inputfiles <- shiny::renderUI({
shiny::tagList(
shiny::selectInput(ns("dataname"), "", datanames(), "organism.features"),
DT::renderDataTable({
getOrgDataSimple(simres(),shiny::req(input$dataname), datafile())
}, escape = FALSE,
options = list(scrollX = TRUE, pageLength = 10)))
})
datafile <- shiny::reactive({
if(shiny::isTruthy(input$datafile)) {
input$datafile$datapath
} else {
""
}
})
output$outs <- shiny::renderUI({
switch(input$button,
Plots = shiny::uiOutput(ns("plots")),
"Input Data" = shiny::uiOutput(ns("inputfiles")))
})
output$plottype <- shiny::renderUI({
if(input$nsim == 1) {
shiny::tagList(
shiny::checkboxInput(ns("norm"), "Normalize Plot", TRUE),
shiny::checkboxInput(ns("total"), "Include Total", TRUE))
} else {
shiny::checkboxInput(ns("conf"), "Confidence band", FALSE)
}
})
output$version <- shiny::renderText({
paste("Ewing package version ", utils::packageVersion("ewing"))
})
})
}
multInput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::h4("Simulation Settings"),
shiny::uiOutput(ns("sppsize")),
shiny::sliderInput(ns("steps"),
label = "Simulation steps:",
min = 1000,
max = 10000,
value = 1000,
step = 500),
shiny::radioButtons(ns("nsim"),
"Number of Simulations",
c(1,10,20,50,100,200),
1, inline = TRUE),
shiny::fileInput(ns("datafile"), "Optional XLSX Input Data File",
multiple = FALSE,
accept = c(".xls", ".xlsx")),
shiny::actionButton(ns("go"), "Start Simulation"),
shiny::HTML("<hr style='height:1px;border:none;color:#333;background-color:#333;' />"),
shiny::uiOutput(ns("plottype")),
shiny::h4("Save Files"),
shiny::fluidRow(
shiny::column(6, shiny::textInput(ns("outfile"), "Species Table", "mysim")),
shiny::column(3, shiny::selectInput(ns("species"), "", c("host", "parasite"), "host")),
shiny::column(3, shiny::downloadButton(ns("downloadRun"), "Table"))),
shiny::fluidRow(
shiny::column(9, shiny::textInput(ns("plotfile"), "Plot File", "myplot")),
shiny::column(3, shiny::downloadButton(ns("downloadPlot"), "Plots"))),
shiny::HTML("<hr style='height:1px;border:none;color:#333;background-color:#333;' />"),
shiny::HTML("See <a href='https://github.com/byandell/ewing'>ewing package on github</a>"),
shiny::uiOutput(ns("version"))
)
}
multOutput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::radioButtons(ns("button"), "", c("Plots", "Input Data"),
"Plots", inline = TRUE),
shiny::uiOutput(ns("outs"))
)
}
# --- Source: inputApp.R ---
inputApp <- function(title = "Input Data Explorer") {
ui <- bslib::page_sidebar(
title = title,
sidebar = bslib::sidebar(
inputAppInput("input_app")
),
bslib::card(
inputAppOutput("input_app")
)
)
server <- function(input, output, session) {
inputAppServer("input_app")
}
shiny::shinyApp(ui = ui, server = server)
}
discover_dataset_tables <- function(datafile = "", sim = NULL) {
found <- character(0)
# 1. Inspect datafile directory
d_path <- if (is.character(datafile) && datafile != "") datafile else if (!is.null(sim) && !is.null(sim$datafile)) sim$datafile else ""
if (d_path != "" && file.exists(d_path)) {
if (dir.exists(d_path)) {
files <- list.files(d_path, pattern = "\\.(txt|csv|rds)$", full.names = FALSE)
if (length(files) > 0) {
found <- tools::file_path_sans_ext(files)
}
} else if (grepl("\\.xlsx$", d_path, ignore.case = TRUE)) {
sheets <- tryCatch(readxl::excel_sheets(d_path), error = function(e) character(0))
if (length(sheets) > 0) found <- sheets
}
}
# 2. Inspect sim$datasets if present
if (!is.null(sim) && !is.null(sim$datasets)) {
found <- unique(c(found, names(sim$datasets)))
}
# 3. Default fallback choices if nothing found
if (length(found) == 0) {
found <- c(
"organism.features", "future.host", "future.parasite",
"substrate.host", "substrate.parasite", "substrate.substrate",
"host.parasite", "temperature.base", "temperature.par"
)
}
unique(found)
}
inputAppInput <- function(id, choices = NULL) {
ns <- shiny::NS(id)
default_choices <- if (!is.null(choices)) choices else c(
"organism.features", "future.host", "future.parasite",
"substrate.host", "substrate.parasite", "substrate.substrate",
"host.parasite", "temperature.base", "temperature.par"
)
shiny::tagList(
shiny::selectInput(ns("dataname"), "Select Dataset Table:",
choices = default_choices,
selected = default_choices[1]
)
)
}
inputAppOutput <- function(id) {
ns <- shiny::NS(id)
shiny::tableOutput(ns("org_table"))
}
inputAppServer <- function(id, simres = shiny::reactiveVal(NULL), datafile = shiny::reactiveVal("")) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
# Dynamically update select choices based on folder / simulation contents
shiny::observe({
sim <- if (is.reactive(simres)) simres() else simres
dfile <- if (is.reactive(datafile)) datafile() else datafile
discovered <- discover_dataset_tables(dfile, sim)
if (length(discovered) > 0) {
current_sel <- input$dataname
sel <- if (!is.null(current_sel) && current_sel %in% discovered) current_sel else discovered[1]
shiny::updateSelectInput(session, "dataname", choices = discovered, selected = sel)
}
})
output$org_table <- shiny::renderTable(
{
name <- input$dataname %||% "organism.features"
sim <- if (is.reactive(simres)) simres() else simres
dfile <- if (is.reactive(datafile)) datafile() else datafile
res <- NULL
# 0. Check direct file in datafile directory if points to folder
if (is.character(dfile) && dfile != "" && dir.exists(dfile)) {
txt_path <- file.path(dfile, paste0(name, ".txt"))
if (file.exists(txt_path)) res <- tryCatch(utils::read.table(txt_path, header = TRUE, sep = "\t", stringsAsFactors = FALSE), error = function(e) NULL)
if (is.null(res)) {
csv_path <- file.path(dfile, paste0(name, ".csv"))
if (file.exists(csv_path)) res <- tryCatch(utils::read.csv(csv_path, stringsAsFactors = FALSE), error = function(e) NULL)
}
}
# 1. Check if dataset is stored in sim$datasets (e.g. injected in webR demo)
if (is.null(res) && !is.null(sim) && !is.null(sim$datasets) && !is.null(sim$datasets[[name]])) {
res <- sim$datasets[[name]]
}
# 2. Extract dynamically via getOrgDataSimple or getOrg* package routines
if (is.null(res) && !is.null(sim) && inherits(sim, "ewing")) {
sim_single <- if (inherits(sim, "ewing_discrete")) sim[[1]] else sim
res <- tryCatch({
getOrgDataSimple(sim_single, name, datafile = dfile)
}, error = function(e) NULL)
}
# 3. Dynamic fallback to extracting from sim_single$org state structures
if ((is.null(res) || !is.data.frame(res) || nrow(res) == 0) && !is.null(sim) && inherits(sim, "ewing")) {
sim_single <- if (inherits(sim, "ewing_discrete")) sim[[1]] else sim
res <- tryCatch({
left <- stringr::str_remove(name, "\\..*")
right <- stringr::str_remove(name, ".*\\.")
if (left == "organism" && right == "features") {
if (!is.null(sim_single$org$Feature)) as.data.frame(sim_single$org$Feature) else NULL
} else if (left == "future") {
if (!is.null(sim_single$org$Future[[right]])) sim_single$org$Future[[right]] else getOrgFuture(sim_single, right)
} else if (!is.null(sim_single$org$Interact[[left]][[right]])) {
sim_single$org$Interact[[left]][[right]]
} else if (!is.null(sim_single$org[[left]][[right]])) {
sim_single$org[[left]][[right]]
} else {
NULL
}
}, error = function(e) NULL)
}
if (is.null(res) || !is.data.frame(res) || nrow(res) == 0) {
res <- data.frame(Info = paste("Dataset", name, "is not available in current simulation instance."))
}
res
},
striped = TRUE,
hover = TRUE,
bordered = TRUE
)
})
}
# --- Source: origEwingApp.R ---
origEwingApp <- function(title = "Population Ethology") {
ui <- shiny::fluidPage(
shiny::titlePanel(title),
shiny::sidebarLayout(
shiny::sidebarPanel(
origEwingInput("ewing")
),
shiny::mainPanel(
origEwingOutput("ewing")
)))
server <- function(input, output, server) {
origEwingServer("ewing")
}
shiny::shinyApp(ui = ui, server = server)
}
origEwingServer <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
simres <- shiny::bindEvent(
shiny::bindCache(
shiny::reactive({
nsim <- as.integer(input$nsim)
if(nsim == 1) {
# Ideally, would like to continue simulation. That would require
# - feed simres() back into future.events, which requires some logic
# - use option "append = TRUE" to append to outfile
siminit <- init.simulation(count = as.numeric(c(input$host, input$parasite)),
datafile = datafile()) # initialize simulation
future.events(siminit, nstep = input$steps, plotit = FALSE) # simulate future events
} else {
shiny::withProgress(message = paste('Ewing Discrete', nsim,
'Simulations ...'),
value = 0,
{
out <- as.list(seq_len(nsim))
inc <- 1 / nsim
for(i in seq_len(nsim)) {
shiny::incProgress(inc)
out[[i]] <- ewing_discrete1(
count = as.numeric(c(input$host, input$parasite)),
nstep = input$steps)
}
make_ewing_discrete(out)
})
}
}),
input$host, input$parasite, input$steps, input$nsim, input$go,
input$datafile),
input$go)
distplot <- shiny::reactive({
if(inherits(simres(), "ewing")) {
ggplot2::autoplot(ewing_ageclass(simres(), total = input$total,
normalize = input$norm))
} else {
NULL
}
})
output$distPlot <- shiny::renderPlot({
distplot()
})
# *** This is not right. Need to get each species name here and in `ewing_substrate`
# species <- ewing:::getOrgFeature(simres)
# gives list but includes substrates.
# can figure out what substrate goes to species with
# ewing:::getOrgFeature(simres, species[i], "substrate")
# if it is NA (or "NA"), then that is a substrate.
# So cycle through species generating plots.
# put as much in `ewing_substrate` as possible.
species <- shiny::reactive({
get.organisms(datafile())$species
})
substrates <- shiny::reactive({
get.organisms(datafile())$substrates
})
output$sppsize <- shiny::renderUI({
shiny::req(species())
lapply(species(), function(x) {
shiny::sliderInput(ns(x),
label = paste0("Number of ", x, "s:"),
min = 0,
max = 500,
value = 100,
step = 20)
})
})
sppplot <- shiny::reactive({
shiny::req(species(), simres())
if(inherits(simres(), "ewing")) {
if(!is.null(simres())) {
p <- lapply(species(), function(x) {
p <- ggplot2::autoplot(ewing_substrate(simres(), x))
if(inherits(p, "ggplot"))
p <- p + ggplot2::ggtitle(paste(x, "on", substrates()[1]))
p
})
if(any(unlist(purrr::map(p, is.null))))
p <- NULL
p
}
} else {
ggplot2::ggplot()
}
})
output$sppPlot <- shiny::renderPlot({
if(!is.null(sppplot())) {
spp <- length(species())
cowplot::plot_grid(plotlist = sppplot(), nrow = spp)
} else {
ggplot2::ggplot()
}
})
envdata <- shiny::reactive({
shiny::req(simres())
if(inherits(simres(), "ewing_discrete")) {
ewing_envelopes(simres())
} else {
NULL
}
})
envelopePlot <- shiny::reactive({
shiny::req(envdata())
nsim <- as.integer(shiny::req(input$nsim))
conf <- (nsim >= 10) & input$conf
if(inherits(simres(), "ewing_discrete")) {
ggplot_ewing_envelopes(envdata(), conf)
} else {
NULL
}
})
output$envPlot <- shiny::renderPlot({
envelopePlot()
})
output$plots <- shiny::renderUI({
nsim <- as.integer(shiny::req(input$nsim), simres())
if(nsim == 1) {
shiny::req(species())
shiny::tagList(
shiny::plotOutput(ns("distPlot"), height = "400px"),
shiny::plotOutput(ns("sppPlot"), height = paste0(200 * length(species()), "px")))
} else {
shiny::plotOutput(ns("envPlot"))
}
})
data <- reactive({
nsim <- as.integer(shiny::req(input$nsim))
species <- shiny::req(input$species)
if(nsim == 1) {
readCount(simres())[[species]]
} else {
shiny::req(envdata())
print(envdata(), species = species)
}
})
params <- shiny::reactive({
nsim <- shiny::req(input$nsim)
paste(shiny::req(input$host), shiny::req(input$parasite),
shiny::req(input$steps), nsim, sep = "_")
})
output$downloadRun <- shiny::downloadHandler(
filename = function() {
paste0(paste(shiny::req(input$outfile), shiny::req(input$species), params(), sep = "_"), ".csv") },
content = function(file) {
utils::write.csv(data(), file, row.names = FALSE)
}
)
output$downloadPlot <- shiny::downloadHandler(
filename = function() {
paste0(paste(shiny::req(input$plotfile), params(), sep = "_"), ".pdf") },
content = function(file) {
grDevices::pdf(file, width = 9)
nsim <- as.integer(shiny::req(input$nsim))
if(nsim == 1) {
print(distplot())
for(i in species()) {
print(sppplot()[[i]])
}
} else {
print(envelopePlot())
}
grDevices::dev.off()
}
)
datanames <- shiny::reactive({
getOrgNames(datafile())
})
output$inputfiles <- shiny::renderUI({
shiny::tagList(
shiny::selectInput(ns("dataname"), "", datanames(), "organism.features"),
DT::renderDataTable({
getOrgDataSimple(simres(),shiny::req(input$dataname), datafile())
}, escape = FALSE,
options = list(scrollX = TRUE, pageLength = 10)))
})
datafile <- shiny::reactive({
if(shiny::isTruthy(input$datafile)) {
input$datafile$datapath
} else {
""
}
})
output$outs <- shiny::renderUI({
switch(input$button,
Plots = shiny::uiOutput(ns("plots")),
"Input Data" = shiny::uiOutput(ns("inputfiles")))
})
output$plottype <- shiny::renderUI({
if(input$nsim == 1) {
shiny::tagList(
shiny::checkboxInput(ns("norm"), "Normalize Plot", TRUE),
shiny::checkboxInput(ns("total"), "Include Total", TRUE))
} else {
shiny::checkboxInput(ns("conf"), "Confidence band", FALSE)
}
})
output$version <- shiny::renderText({
paste("Ewing package version ", utils::packageVersion("ewing"))
})
})
}
origEwingInput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::h4("Simulation Settings"),
shiny::uiOutput(ns("sppsize")),
shiny::sliderInput(ns("steps"),
label = "Simulation steps:",
min = 1000,
max = 10000,
value = 1000,
step = 500),
shiny::radioButtons(ns("nsim"),
"Number of Simulations",
c(1,10,20,50,100,200),
1, inline = TRUE),
shiny::fileInput(ns("datafile"), "Optional XLSX Input Data File",
multiple = FALSE,
accept = c(".xls", ".xlsx")),
shiny::actionButton(ns("go"), "Start Simulation"),
shiny::HTML("<hr style='height:1px;border:none;color:#333;background-color:#333;' />"),
shiny::uiOutput(ns("plottype")),
shiny::h4("Save Files"),
shiny::fluidRow(
shiny::column(6, shiny::textInput(ns("outfile"), "Species Table", "mysim")),
shiny::column(3, shiny::selectInput(ns("species"), "", c("host", "parasite"), "host")),
shiny::column(3, shiny::downloadButton(ns("downloadRun"), "Table"))),
shiny::fluidRow(
shiny::column(9, shiny::textInput(ns("plotfile"), "Plot File", "myplot")),
shiny::column(3, shiny::downloadButton(ns("downloadPlot"), "Plots"))),
shiny::HTML("<hr style='height:1px;border:none;color:#333;background-color:#333;' />"),
shiny::HTML("See <a href='https://github.com/byandell/ewing'>ewing package on github</a>"),
shiny::uiOutput(ns("version"))
)
}
origEwingOutput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::radioButtons(ns("button"), "", c("Plots", "Input Data"),
"Plots", inline = TRUE),
shiny::uiOutput(ns("outs"))
)
}
# --- Source: envPlotApp.R ---
envPlotApp <- function(title = "Envelope Plots") {
ui <- bslib::page_sidebar(
title = title,
sidebar = bslib::sidebar(
envPlotInput("env_plot")
),
envPlotOutput("env_plot")
)
server <- function(input, output, server) {
# Dummy mock context
}
shiny::shinyApp(ui = ui, server = server)
}
envPlotServer <- function(id, simres, nsim) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
envdata <- shiny::reactive({
res <- shiny::req(simres())
if(inherits(res, "ewing_discrete")) {
ewing_envelopes(res)
} else {
NULL
}
})
envelopePlot <- shiny::reactive({
shiny::req(envdata())
sims <- shiny::req(nsim())
conf <- (sims >= 10) & input$conf
if(inherits(simres(), "ewing_discrete")) {
ggplot_ewing_envelopes(envdata(), conf)
} else {
NULL
}
})
output$envPlot <- shiny::renderPlot({
envelopePlot()
})
# Return outputs for download hooks
list(
envdata = envdata,
envelopePlot = envelopePlot
)
})
}
envPlotInput <- function(id) {
ns <- shiny::NS(id)
shiny::checkboxInput(ns("conf"), "Confidence band", FALSE)
}
envPlotOutput <- function(id) {
ns <- shiny::NS(id)
shiny::plotOutput(ns("envPlot"))
}
# --- Source: downloadApp.R ---
downloadApp <- function(title = "Download Options") {
# Stub for testing downloadApp independently
ui <- bslib::page_sidebar(
title = title,
sidebar = bslib::sidebar(
downloadInput("download")
),
"Test Server Hook"
)
server <- function(input, output, server) { }
shiny::shinyApp(ui = ui, server = server)
}
downloadServer <- function(id, sim_data, distplot = shiny::reactive(NULL), sppplot = shiny::reactive(NULL), envplot = shiny::reactive(NULL)) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
# Internal parameters string extraction
params <- shiny::reactive({
nsim <- shiny::req(sim_data$nsim)
inps <- shiny::req(sim_data$input)
paste(inps$steps, nsim(), sep = "_") # Modified bounds logic since we dynamically extract
})
# CSV Data Generator extraction mapping exactly to original ewingApp
data <- shiny::reactive({
nsim <- as.integer(shiny::req(sim_data$nsim()))
sp <- shiny::req(input$species)
res <- shiny::req(sim_data$simres())
if(nsim == 1) {
readCount(res)[[sp]]
} else {
# For discrete states, evaluate envdata
env_d <- shiny::req(envplot$envdata())
print(env_d, species = sp)
}
})
output$downloadRun <- shiny::downloadHandler(
filename = function() {
paste0(paste(shiny::req(input$outfile), shiny::req(input$species), params(), sep = "_"), ".csv")
},
content = function(file) {
utils::write.csv(data(), file, row.names = FALSE)
}
)
output$downloadPlot <- shiny::downloadHandler(
filename = function() {
paste0(paste(shiny::req(input$plotfile), params(), sep = "_"), ".pdf")
},
content = function(file) {
grDevices::pdf(file, width = 9)
on.exit(grDevices::dev.off(), add = TRUE)
nsim <- as.integer(shiny::req(sim_data$nsim()))
if(nsim == 1) {
# Handle discrete plot captures natively
print(shiny::req(distplot()))
sp_lists <- shiny::req(sppplot())
if (!is.null(sp_lists)) {
for(p in sp_lists) {
if(!is.null(p)) print(p)
}
}
} else {
print(shiny::req(envplot$envelopePlot()))
}
}
)
})
}
downloadInput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::h4("Save Files"),
shiny::fluidRow(
shiny::column(6, shiny::textInput(ns("outfile"), "Species Table", "mysim")),
shiny::column(3, shiny::selectInput(ns("species"), "", c("host", "parasite"), "host")),
shiny::column(3, shiny::downloadButton(ns("downloadRun"), "Table"))),
shiny::fluidRow(
shiny::column(9, shiny::textInput(ns("plotfile"), "Plot File", "myplot")),
shiny::column(3, shiny::downloadButton(ns("downloadPlot"), "Plots")))
)
}
# --- Source: simApp.R ---
simApp <- function(title = "Population Ethology Simulation") {
# Stub for testing simApp individually if desired
ui <- bslib::page_sidebar(
title = title,
sidebar = bslib::sidebar(
initParInput("init_par"),
simInput("sim")
),
simUI("sim"),
initParOutput("init_par")
)
server <- function(input, output, server) {
init_par <- initParServer("init_par")
simServer("sim", init_par)
}
shiny::shinyApp(ui = ui, server = server)
}
simServer <- function(id, init_par, datafile = shiny::reactiveVal("")) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
active_sim <- shiny::reactiveVal(NULL)
shiny::observeEvent(input$go_init, {
nsim <- as.integer(input$nsim)
if(nsim == 1) {
siminit <- init.simulation(count = as.numeric(c(shiny::req(init_par$host), shiny::req(init_par$parasite))),
datafile = datafile())
# automatically advance by step_size to avoid step 0 blank plots
new_state <- future.events(siminit, nstep = input$step_size, plotit = FALSE)
active_sim(new_state)
} else {
shiny::withProgress(message = paste('Ewing Discrete', nsim, 'Simulations ...'),
value = 0,
{
out <- as.list(seq_len(nsim))
inc <- 1 / nsim
for(i in seq_len(nsim)) {
shiny::incProgress(inc)
out[[i]] <- ewing_discrete1(
count = as.numeric(c(shiny::req(init_par$host), shiny::req(init_par$parasite))),
nstep = input$steps) # Does ewing_discrete1 accept datafile? Original didn't pass it.
}
active_sim(make_ewing_discrete(out))
})
}
})
shiny::observeEvent(input$go_step, {
nsim <- as.integer(input$nsim)
if(nsim == 1) {
shiny::req(active_sim())
new_state <- future.events(active_sim(), nstep = input$step_size, plotit = FALSE)
active_sim(new_state)
}
})
simres <- shiny::reactive({ active_sim() })
# Return both the active reactive and the input bindings natively
list(
simres = simres,
nsim = shiny::reactive({ as.integer(input$nsim) }),
input = input
)
})
}
simInput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::sliderInput(ns("steps"),
label = "Simulation steps:",
min = 1000,
max = 10000,
value = 1000,
step = 500),
shiny::radioButtons(ns("nsim"),
"Number of Simulations",
c(1,10,20,50,100,200),
1, inline = TRUE),
shiny::sliderInput(ns("step_size"),
label = "Steps per click (for nsim=1):",
min = 10,
max = 500,
value = 50,
step = 10)
)
}
simUI <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::fluidRow(
shiny::column(6, shiny::actionButton(ns("go_init"), "Init / Run")),
shiny::conditionalPanel(
condition = sprintf("input['%s'] > 0 && input['%s'] == '1'", ns("go_init"), ns("nsim")),
shiny::column(6, shiny::actionButton(ns("go_step"), "Step Forward"))
)
)
)
}
# --- Source: futureApp.R ---
futureApp <- function(title = "Population Ethology") {
ui <- bslib::page_sidebar(
title = title,
sidebar = bslib::sidebar(
initParInput("init_par"),
distPlotInput("dist_plot"),
futureInput("future")),
bslib::navset_tab(
bslib::nav_panel("Dist", distPlotOutput("dist_plot")),
bslib::nav_panel("Substrate",
bslib::card(substrateOutput("substrate_init")),
bslib::card(substrateOutput("substrate"))),
bslib::nav_panel("Params", futureOutput("future")))
)
server <- function(input, output, server) {
init_par <- initParServer("init_par")
siminit <- initServer("init", init_par)
simres <- futureServer("future", siminit)
distPlotServer("dist_plot", simres)
substrateServer("substrate_init", siminit)
substrateServer("substrate", simres)
}
shiny::shinyApp(ui = ui, server = server)
}
futureServer <- function(id, siminit) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
simres <- shiny::reactive({
future.events(shiny::req(siminit()), nstep = shiny::req(input$steps),
plotit = FALSE) # simulate future events
})
output$summary <- shiny::renderUI({
out <- summary_simobj(summary(shiny::req(simres())))
shiny::HTML(out)
})
# Result.
simres
})
}
futureInput <- function(id) {
ns <- shiny::NS(id)
shiny::sliderInput(ns("steps"),
label = "Simulation steps:",
min = 1000,
max = 10000,
value = 1000,
step = 500)
}
futureOutput <- function(id) {
ns <- shiny::NS(id)
shiny::uiOutput(ns("summary"))
}
# --- Source: sysetholApp.R ---
sysetholApp <- function(title = "Systems Ethology Platform") {
ui <- bslib::page_sidebar(
title = title,
sidebar = bslib::sidebar(
width = 320,
sysetholInput("sysethol")
),
sysetholOutput("sysethol")
)
server <- function(input, output, session) {
sysetholServer("sysethol")
}
shiny::shinyApp(ui = ui, server = server)
}
sysetholInput <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::div(
style = "font-size: 0.85rem;",
shiny::h4("Simulation Setup", style = "font-size: 0.95rem; font-weight: 600; margin-bottom: 6px;"),
shiny::sliderInput(ns("n_host"), "Number of hosts:", min = 0, max = 500, value = 200, step = 20),
shiny::sliderInput(ns("n_parasite"), "Number of parasites:", min = 0, max = 500, value = 100, step = 20),
shiny::radioButtons(ns("nsim"), "Number of Simulations:", choices = c(1, 10, 20, 50, 100, 200), selected = 1, inline = TRUE),
# Conditional Control: Steps per click ONLY shown when nsim == 1 (Geometric log scale choices 1..2000)
shiny::conditionalPanel(
condition = sprintf("input['%s'] == '1'", ns("nsim")),
step_size_slider(ns("step_size"), "Steps per click:", selected = 50)
),
# Conditional Control: Total Simulation steps ONLY shown when nsim > 1
shiny::conditionalPanel(
condition = sprintf("input['%s'] != '1'", ns("nsim")),
shiny::sliderInput(ns("steps"), "Total Simulation steps:", min = 500, max = 5000, value = 1000, step = 500)
),
shiny::div(
style = "display: flex; gap: 6px; margin: 10px 0 8px 0;",
shiny::actionButton(ns("run_engine"), "Run Engine", class = "btn-sm btn-primary flex-fill", style = "font-weight: 600;"),
shiny::actionButton(ns("reset_engine"), "Reset", class = "btn-sm btn-outline-secondary")
),
# Conditional Controls for Substrate Plots (shown ONLY on Substrate Plots tab)
shiny::conditionalPanel(
condition = sprintf("input['%s'] == 'Substrate Plots'", ns("tabset")),
shiny::div(style = "border-top: 1px solid rgba(0,0,0,0.1); margin: 6px 0;"),
shiny::h4("Substrate Display Options", style = "font-size: 0.9rem; font-weight: 600; margin-bottom: 4px;"),
axisUnitInput(ns("substrate_axis")),
substrateInput(ns("substrate"))
),
# Conditional Controls for Age Classes (shown ONLY on Age Classes tab)
shiny::conditionalPanel(
condition = sprintf("input['%s'] == 'Age Classes'", ns("tabset")),
shiny::div(style = "border-top: 1px solid rgba(0,0,0,0.1); margin: 6px 0;"),
shiny::h4("Age Classes Display Options", style = "font-size: 0.9rem; font-weight: 600; margin-bottom: 4px;"),
ageClassControlInput(ns("age_ctrls"))
),
# Conditional Controls for Envelope Plots (shown ONLY on Envelope Plots tab when nsim > 1)
shiny::conditionalPanel(
condition = sprintf("input['%s'] == 'Envelope Plots' && input['%s'] != '1'", ns("tabset"), ns("nsim")),
shiny::div(style = "border-top: 1px solid rgba(0,0,0,0.1); margin: 6px 0;"),
shiny::h4("Envelope Display Options", style = "font-size: 0.9rem; font-weight: 600; margin-bottom: 4px;"),
shiny::checkboxInput(ns("confidence"), "Confidence Band Envelope", TRUE)
)
)
)
}
sysetholOutput <- function(id) {
ns <- shiny::NS(id)
shiny::uiOutput(ns("sysethol_tabs"))
}
sysetholServer <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
current_sim <- shiny::reactiveVal(NULL)
sub_x_var <- axisUnitServer("substrate_axis")
age_ctrls <- ageClassControlServer("age_ctrls")
# Initialize simulation on start
shiny::observe({
if (is.null(current_sim())) {
nh <- input$n_host %||% 200
np <- input$n_parasite %||% 100
sim <- init.simulation(count = c(nh, np))
sz <- parse_step_size(input$step_size %||% 5)
sim <- future.events(sim, nstep = sz, plotit = FALSE)
current_sim(sim)
}
})
# Run Engine button
shiny::observeEvent(input$run_engine, {
nsim_val <- as.numeric(input$nsim %||% 1)
nh <- input$n_host %||% 200
np <- input$n_parasite %||% 100
withProgress(message = "Executing Simulation Engine...", value = 0.2, {
if (nsim_val == 1) {
# Single run mode
sz <- parse_step_size(input$step_size %||% 50)
sim <- current_sim()
if (is.null(sim)) sim <- init.simulation(count = c(nh, np))
sim <- future.events(sim, nstep = sz, plotit = FALSE)
current_sim(sim)
} else {
# Multi-run discrete envelope mode
tot_steps <- as.numeric(input$steps %||% 1000)
sims <- ewing_discrete(nsim = nsim_val, nstep = tot_steps, count = c(nh, np), verbose = FALSE)
current_sim(sims)
}
incProgress(0.8, detail = "Done")
})
})
# Reset Engine button
shiny::observeEvent(input$reset_engine, {
nh <- input$n_host %||% 200
np <- input$n_parasite %||% 100
sim <- init.simulation(count = c(nh, np))
current_sim(sim)
})
# Input Data App Server
inputAppServer("input_app", simres = current_sim)
# Compose Dist Plot Module for Age Classes
distPlotServer("dist_plot", simres = current_sim, x_var = age_ctrls$x_var, total = age_ctrls$total, norm = age_ctrls$norm)
# Dynamic Tabs (Envelope Plots shown ONLY when nsim > 1)
output$sysethol_tabs <- shiny::renderUI({
nsim_val <- as.numeric(input$nsim %||% 1)
if (nsim_val == 1) {
bslib::navset_tab(
id = ns("tabset"),
bslib::nav_panel("Substrate Plots", bslib::card(substrateOutput(ns("substrate")))),
bslib::nav_panel("Age Classes", bslib::card(distPlotOutput(ns("dist_plot")))),
bslib::nav_panel("Input Data", bslib::card(
inputAppInput(ns("input_app")),
inputAppOutput(ns("input_app"))
))
)
} else {
bslib::navset_tab(
id = ns("tabset"),
bslib::nav_panel("Substrate Plots", bslib::card(substrateOutput(ns("substrate")))),
bslib::nav_panel("Age Classes", bslib::card(distPlotOutput(ns("dist_plot")))),
bslib::nav_panel("Envelope Plots", bslib::card(shiny::plotOutput(ns("env_plot"), height = "500px"))),
bslib::nav_panel("Input Data", bslib::card(
inputAppInput(ns("input_app")),
inputAppOutput(ns("input_app"))
))
)
}
})
# Substrate Plots Tab Module
substrateServer("substrate", simres = current_sim)
# Envelope Plots Tab
output$env_plot <- shiny::renderPlot({
sim <- current_sim()
conf_val <- input$confidence %||% TRUE
if (inherits(sim, "ewing_discrete")) {
ggplot_ewing_envelopes(sim, confidence = conf_val)
} else if (!is.null(sim)) {
# Single run fallback: build envelope from current sim
env <- ewing_envelopes(sim)
ggplot_ewing_envelopes(env, confidence = conf_val)
} else {
ggplot2::ggplot() + ggplot2::theme_void() + ggplot2::ggtitle("No active simulation for envelope plot")
}
})
# Return active simulation state
current_sim
})
}
# --- Launch Application ---
sysetholApp()
Programmatic Application Usage
Launch the Systems Ethology Platform natively in R using exported package functions:
library(ewing)
sysetholApp()