Appendix S3 ## CODE FOR MODEL SIMULATION ## -------- FUNCTIONS TO SIMULATE RANGE SHIFTS ------------# tempconv() convert temp to elevation # int.mat() determines range overlaps as co_occurrences # range.shift() deterministic model of range shift # cm.taxon() does all range.shift runs for each taxon # FUNCTION TO CREATE co_occurrence MATRIX based on ranges int.mat <- function(species,ELmin,ELmax) { temp.mat <- matrix(0,nrow=length(species),ncol=length(species)) for (i in 1:length(species)) { for (j in 1:length(species)) { if (i < j & ELmin[j] <= ELmax[i] & is.na(ELmin[j])==FALSE & is.na(ELmax[i])==FALSE ) temp.mat[i,j] = 1 } # end of for j spp loop } # end for i spp loop temp.mat } # end of function # RANGE SHIFT FUNCTION (calls on tempconv, elevconv, int.mat, tempshift = number of degrees shifting; habitat = 'none', 'upper', 'lower', or 'both' limit(s) to strictly set by physiological tolerance; dispersal = a coefficient(0,1), 'infinite', or 'random'; subsample = number of spp to control for biodiversity or 'FALSE') range.shift <- function(cmrange,Site,Taxon,tempshift='predicted',habitat='both',dispersal) { # subset the data and extract necessary variables: subtaxon <- subset(cmrange,cmrange$site==Site & cmrange$taxa==Taxon) # range limits set as midpoints between last observed and first absent data in each direction ELmin <- rowMeans(cbind(ELmin,subtaxon$low_absent)) ELmax <- rowMeans(cbind(ELmax,subtaxon$high_absent)) # convert elevation (m) into temperature(degC): lapse <- subtaxon$lapse/1000 # degraes/m tempconv <- function(elev,meanT,newelev) { meanT - lapse*(newelev - elev) } elev <- mean(subtaxon$elev) meanT <- mean(subtaxon$mean) CTmin <- tempconv(elev,meanT,ELmin) CTmax <- tempconv(elev,meanT,ELmax) j <- order(ELmin,ELmax) # order subset by range height subtaxon <- subtaxon[j,] ELmin <- ELmin[j] ELmax <- ELmax[j] CTmin <- CTmin[j] CTmax <- CTmax[j] species <- subtaxon$species[j] if (tempshift == 'predicted') {tempshift <- mean(subtaxon$projected_mean)} if (habitat == 'both') { nhabmin <- ELmin + (1/lapse)*tempshift # new habitable ELmin after climate change (CC) nhabmax <- ELmax + (1/lapse)*tempshift # new habitable ELmax post-CC nhabmax[nhabmax >= mean(subtaxon$mountmax)] <mean(subtaxon$mountmax) nhabmin[nhabmin < 0] <- 0 nhabmax[nhabmax >= mean(subtaxon$mountmax)] <mean(subtaxon$mountmax) } if (habitat == 'none') # no limits determined by physiological tolerance { nhabmin <- ELmin nhabmax <- ELmax } if (habitat == 'lower') # only lower limit is defined by physiology { nhabmin <- ELmin + (1/lapse)*tempshift nhabmax <- ELmax } if (habitat == 'upper') # only upper limit is defined by physiology { nhabmin <- ELmin nhabmax <- ELmax + (1/lapse)*tempshift nhabmax[nhabmax >= mean(subtaxon$mountmax)] <mean(subtaxon$mountmax) } # add in dispersal ability if (dispersal == 'infinite') {post.max <- nhabmax post.min <- nhabmin} if (class(dispersal) == 'numeric' & length(dispersal)==1) { rangesize <- ELmax-ELmin d.ability <- dispersal*rangesize post.max <- ELmax + d.ability # with no dispersal, upper range poss: ELmax post.min <- ELmin - d.ability nix <- which(nhabmin>post.max) # find spp where dispersed max is less than new habitable minimum post.min[nhabmin > post.min] <- nhabmin[nhabmin > post.min] post.max[nhabmax < post.max] <- nhabmax[nhabmax < post.max] #remove extinct spp post.min[nix]<- NA post.max[nix]<- NA } if (dispersal == 'random') # selects from a log-normal distribution { # solve for mu and sigma of lognormal distribution from the mean E(x) and variance Var(x) according to equations found in the rlnorm help file mu_sd <- function(taxon) { data <- subset(cmrange,cmrange$taxa==taxon) range <- data$maxelev-data$minelev EX <- mean(range) VarX <- var(range) sigma <- sqrt(log((log(VarX)/(2*log(EX)))+1)) mu <- log(EX)-0.5*sigma^2 # hist(range,xlab='range size (m)',freq=FALSE,main=taxon) # curve((1/(sqrt(2*pi)*sigma*x))*exp(-((log(x) - mu)^2 / (2*sigma^2))),col=2,add=T) cbind(mu,sigma) } lgnrm <- mu_sd(taxon=Taxon) rangesize <- ELmax-ELmin d.ability <- rlnorm(length(ELmin), meanlog=lgnrm[1], sdlog=lgnrm[2]) post.min <- ELmin - d.ability post.max <- ELmax + d.ability nix <- which(nhabmin>post.max) post.min[nhabmin > post.min] <- nhabmin[nhabmin > post.min] post.max[nhabmax < post.max] <- nhabmax[nhabmax < post.max] #remove extinct spp post.min[nix]<- NA post.max[nix]<- NA dispersal <- 'random' } if (length(dispersal) == 2) # selects from a log-normal distribution with dispersal = c(meanlog,sdlog) { rangesize <- ELmax-ELmin d.ability <- rlnorm(length(ELmin), meanlog=dispersal[1], sdlog=dispersal[2]) post.min <- ELmin - d.ability post.max <- ELmax + d.ability nix <- which(nhabmin>post.max) post.min[nhabmin > post.min] <- nhabmin[nhabmin > post.min] post.max[nhabmax < post.max] <- nhabmax[nhabmax < post.max] #remove extinct spp post.min[nix]<- NA post.max[nix]<- NA dispersal <- dispersal[2] } post.min[post.min>post.max] <- NA post.max[post.min>post.max] <- NA post.min[post.min<0] <- NA post.max[post.max<0] <- NA extinct <- sum(is.na(post.min)) # calculate changes in co_occurrences intI <- int.mat(species,ELmin,ELmax) # create initial co_occurrence matrix intF <- int.mat(species,post.min,post.max) # create final co_occurrence matrix intF[post.max==0,] <- 0 intF[,post.max==0] <- 0 Dint <- intF-intI int.poss <- sum(intI) int.lost <- sum(Dint==-1) int.gain <- sum(Dint==1) #outputs pre.rangesize <- mean(ELmax-ELmin,na.rm=T) post.rangesize <- mean(post.max-post.min,na.rm=T) latit <- mean(subtaxon$lat) output1 <c(Taxon,Site,latit,habitat,dispersal,mean(lapse),tempshift,length(species),extinct,int.poss,int.lost, int.gain,pre.rangesize,post.rangesize) names(output1) <c('taxon','site','latitude','habitat','dispersal','lapse','Tshift','spp','extinct','t_int','lost','gain','pre.range', 'post.range') output1 } # end range.shift function # FUNCTION TO RUN range.shift() with predicted temperature shifts for a particular taxon, habitat=both, and 4 dispersal types (0,1,'infinite', and 10 replicates of 'random') cm.taxon <- function(cmrange,Taxon,tempshift='predicted') { LAT <- tapply(cmrange$lat,list(cmrange$taxa,cmrange$site),mean,na.rm=T) omitLAT <- na.omit(LAT[Taxon,]) data3 <matrix(NA,nrow=1,ncol=14,dimnames=list('',c('taxon','site','latitude','habitat','dispersal','lapse','Ts hift','spp','extinct','t_int','lost','gain','pre.range','post.range'))) for (i in 1:length(omitLAT)) # for every site {temp1<range.shift(cmrange,Site=labels(omitLAT)[i],Taxon=Taxon,tempshift=tempshift, habitat='both',dispersal=0) data3 <- rbind(data3,temp1) temp2<-range.shift(cmrange,Site=labels(omitLAT)[i],Taxon=Taxon,tempshift=tempshift, habitat='both',dispersal=1) data3 <- rbind(data3,temp2) temp3<-range.shift(cmrange,Site=labels(omitLAT)[i],Taxon=Taxon,tempshift=tempshift, habitat='both',dispersal='infinite') data3 <- rbind(data3,temp3) for (j in 1:10) # for 10 replicates of random scenario { temp4<-range.shift(cmrange,Site=labels(omitLAT)[i],Taxon=Taxon,tempshift=tempshift, habitat='both',dispersal='random') print(Taxon) print(j) data3 <- rbind(data3,temp4) } } # end for every site data3 } # end cm.taxon() function # -- FUNCTIONS setwd('/Users/sylvia/Documents/Rwork') cmrange <- read.csv('climaterange_new.csv') #levels(cmrange$taxa) # dispersal <- 'infinite','random' or scaling coefficient for range size that describes the proportion of new range that the animal actually disperses to (0 = does not track, 1 = proportionally tracks new range), (calls on tempconv, elevconv, int.mat, tempshift = number of degrees shifting; habitat = 'none', 'upper', 'lower', or 'both' limit(s) to strictly set by physiological tolerance; dispersal = a coefficient(0,1), 'infinite', or 'random'; subsample = number of spp to control for biodiversity or 'FALSE') # simulation runs dung <- cm.taxon(cmrange,Taxon='dung',tempshift='predicted') bird <- cm.taxon(cmrange,Taxon='bird',tempshift='predicted') lizd <- cm.taxon(cmrange,Taxon='lizard',tempshift='predicted') frog <- cm.taxon(cmrange,Taxon='frog',tempshift='predicted') rodt <- cm.taxon(cmrange,Taxon='rodentia',tempshift='predicted') snke <- cm.taxon(cmrange,Taxon='snake',tempshift='predicted') simdata <- data.frame(rbind(bird,dung,frog,lizd,rodt,snke),row.names=NULL) simdata write.csv(simdata,file='climatesim041811.csv', row.names=FALSE) # --- KEEP PREDICTED temp CHANGE CONSTANT --- # st <- tapply(cmrange$lat,list(cmrange$site,cmrange$taxa),mean,na.rm=T) st <- as.data.frame(st) tax <- labels(st)[[2]] allsites <- labels(st)[[1]] taxtempfull <- matrix(NA,ncol=14) for (k in 1:length(tax)) # for each taxon { sites <- allsites[-which(is.na(st[,k]))] # which sites exist with data for (j in 1:length(sites)) # for each site in taxon { sub.lat <- mean(cmrange$lat[cmrange$taxa==tax[k] & cmrange$site==sites[j]],na.rm=T) print(c(sites[j],tax[k],sub.lat)) taxtemp <- rbind( range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift=0,habitat='both',dispersal=1), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift=1,habitat='both',dispersal=1), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift=2,habitat='both',dispersal=1), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift=3,habitat='both',dispersal=1), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift=4,habitat='both',dispersal=1), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift=5,habitat='both',dispersal=1)) taxtempfull <- rbind(taxtempfull,taxtemp) } } taxtempfull taxtempfull <- as.data.frame(taxtempfull) write.csv(taxtempfull,file='climatetemp041911.csv',row.names=FALSE) # --- VARY RANDOM DISPERSAL variance parameter --- # # solve for mu and sigma of lognormal distribution from the mean E(x) and variance Var(x) according to equations found in the rlnorm help file mu_sd <- function(taxon) { data <- subset(cmrange,cmrange$taxa==taxon) range <- data$maxelev-data$minelev EX <- mean(range) VarX <- var(range) sigma <- sqrt(log((log(VarX)/(2*log(EX)))+1)) mu <- log(EX)-0.5*sigma^2 cbind(mu,sigma) } st <- tapply(cmrange$lat,list(cmrange$site,cmrange$taxa),mean,na.rm=T) st <- as.data.frame(st) tax <- labels(st)[[2]] allsites <- labels(st)[[1]] taxSDfull <- matrix(NA,ncol=14) for (k in 1:length(tax)) # for each taxon { sites <- allsites[-which(is.na(st[,k]))] # which sites exist with data for (j in 1:length(sites)) # for each site in taxon { # for (m in 1:3) # number of replicates # { sub.lat <- mean(cmrange$lat[cmrange$taxa==tax[k] & cmrange$site==sites[j]],na.rm=T) print(c(sites[j],tax[k],sub.lat)) taxSD <- rbind( range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift='predicted',habitat='both',dispersal=c( mu_sd(taxon=tax[k])[1],0.825)), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift='predicted',habitat='both',dispersal=c( mu_sd(taxon=tax[k])[1],0.83)), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift='predicted',habitat='both',dispersal=c( mu_sd(taxon=tax[k])[1],0.85)), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift='predicted',habitat='both',dispersal=c( mu_sd(taxon=tax[k])[1],0.9)), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift='predicted',habitat='both',dispersal=c( mu_sd(taxon=tax[k])[1],0.95)), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift='predicted',habitat='both',dispersal=c( mu_sd(taxon=tax[k])[1],0.99)), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift='predicted',habitat='both',dispersal=c( mu_sd(taxon=tax[k])[1],2)), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift='predicted',habitat='both',dispersal=c( mu_sd(taxon=tax[k])[1],4)), range.shift(cmrange,Site=sites[j],Taxon=tax[k],tempshift='predicted',habitat='both',dispersal=c( mu_sd(taxon=tax[k])[1],8)) ) taxSDfull <- rbind(taxSDfull,taxSD) # } # for each replicate } # for each site } # for each taxon taxSDfull <- as.data.frame(taxSDfull) taxSDfull write.csv(taxSDfull,file='climatedisp041911.csv',row.names=FALSE)