#Background
#Bormann e al 1977 investigated nitrogen (N) dynamics as one of the most important limiting nutrients for ecosystem production of the Northern hardwood forest ecosystem. Modeling N cycle further elucidates the biogeochemistry of N in a temperate forest ecosystem and the ecological response in aggrading vegetation considering the fluxes between different pools.
#Modeling structures
#Attempts were made to represent the model structure of N in the study area and the interactive effects among different pools. In particular, model equations of fluxes and parameterization of the Bormann model were done to study the effect of in- and -out fluxes on the patterns of vegetation, available and bound N pools over time.
#Modeling stages (Shown in R codes)Bormann original model#???   Constraint (vegetation carrying capacity correction) ???    Calibration (optimization and parameter sensitivity) ???    What happens if the system is disturbed?
#Base_model digram

getwd()
## [1] "C:/Users/Paul/Documents/Rwork"
##Load DiagrammeR
library(DiagrammeR)
# a digraph is a directed graph
# the model is written in the "dot" language
Bormann_model <-
  "digraph{
graph[overlap=true,fontsize=10]

Vegetation_532 -> AvailableN_26 [label='7.4'];

Vegetation_532-> Boundpool_4700[label='63.3'];

AvailableN_26-> Vegetation_532 [label='79.6'];
Boundpool_4700-> AvailableN_26 [label='69.6'];
Boundpool_4700-> External_output_0 [label='0.1'];
External_output_0->Boundpool_4700 [label='14.2'];
External_output_0 -> AvailableN_26 [label='6.5'];
AvailableN_26 -> External_output_0 [label='3.9'];
}"
grViz(Bormann_model)
#Base_model diagram in the second assignment link
##The response shows that vegetation growth is indefinite which doesn't represent the actual system. So, next is to add a constraint to check the time of aggrading of the vegetation pool.
#code with carrying capacity
library (deSolve)
borman_base <- function(t,y,p){with(as.list(c(y,p)),
                                    {dV.dt<-a12*A*V*(1-(V/K))-a21*V-a31*V
                                    dA.dt<-F20 + a21*V + a23*B - a12*A*V - a02*A
                                    dB.dt<-F30 + a31*V - a23*B - a03*B
                                    return(list(c(dV.dt,dA.dt,dB.dt)))})}
#Define p,y,t
#p
p <- c(F20=6.5,
       F30=14.2,
       a12=79.6/(26*532),
       a31=63.6/532,
       a21=7.4/532,
       a23=69.6/4700,
       a02=3.9/26,
       a03=0.1/4700,
       A=26,
        B=4700,
       K=1000)
y <- c(V=500,A=20,B=4000) #inital state of the system
#t <- c(0,1,10,100)#timepoints we want output from
t=seq(0,500,by=0.5)
out2 <- ode(y=y ,time=t, func=borman_base,parms=p)
plot(out2)

##From here we used bormann.logistic as an input function from file BormannLogistic.R

bormann.logistic <- function(t, y, p) {
  
  with(as.list( c(y, p) ), {
    ### ODEs
    ## We hypothesize that the growth function is all that Bormann et al. included. 
    ## Therefore, our logistic inhibition term acts on the entire forest.
    dV.dt <- (a12 * A * V  - a21 * V - a31 * V) * (1 - V/K )
    ## 
    
    ## A still loses N to vegetation, so we leave the loss function the same.
    dA.dt <- ( a20 + a23 * B ) - ( a12 * V * A + a02 * A)
    
    ## 
    dB.dt <- ( a30 + a31 * V ) - ( a23 * B + a03 * B )
    loss <- a02*A + a03*B
    # returning a list whose first (and only) element 
    # is a vector of the ODE values
    return( 
      list(
        c( dV.dt, dA.dt, dB.dt ), loss=loss
      )
    )
  }
  )
}
One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

##Next tested sensitivity of model
source("BormannLogistic.R") #Sourcing the file mentioned above
bormann.logistic #Pull the function from the above file into the workspace
## function(t, y, p) {
##   
##   with(as.list( c(y, p) ), {
##     ### ODEs
##     ## We hypothesize that the growth function is all that Bormann et al. included. 
##     ## Therefore, our logistic inhibition term acts on the entire forest.
##     dV.dt <- (a12 * A * V  - a21 * V - a31 * V) * (1 - V/K )
##     ## 
##     
##     ## A still loses N to vegetation, so we leave the loss function the same.
##     dA.dt <- ( a20 + a23 * B ) - ( a12 * V * A + a02 * A)
##     
##     ## 
##     dB.dt <- ( a30 + a31 * V ) - ( a23 * B + a03 * B )
##     loss <- a02*A + a03*B
##     # returning a list whose first (and only) element 
##     # is a vector of the ODE values
##     return( 
##       list(
##         c( dV.dt, dA.dt, dB.dt ), loss=loss
##       )
##     )
##   }
##   )
## }
#Running the model using following pyt
p <- c( a20 = 6.5, a30 = 14.2, a12 = 79.6/(532*26), a21 = (6.6 + 0.8)/532, 
        a31 = (54.2 + 2.7 + 6.2 + 0.1)/532,
        a23 = 69.6/4700, a02 = 3.9/26, a03 = 0.1/4700,
        K = 1000,
          A=26,
        B=4700)

y <- c(V = 532, A = 26, B = 4700)

t <- seq(from = 0, to = 500, by = 1)

out3 <- ode(y = y, times = t, func = bormann.logistic, parms = p)

plot( out3 ) #plot initial model
One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

##The following is to find a steady state to test the changes against
#Our model -- looks pretty steady-state-ish to me. We will drop the first 500 years to create our reference."}

y <- c(V = 532, A = 26, B = 4700)
t <- seq(from = 0, to = 1000, by = 1)
out4 <- ode(y = y, times = t, func = bormann.logistic, parms = p)
plot(out4)
One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

Reference <- as.data.frame(out4-(1:500))
## Warning in out4 - (1:500): longer object length is not a multiple of
## shorter object length
## Make small deviations to see the difference in parameters
#One percent deviations in some parameters cause substantial variation."}
## we make a clean COPY of the parameters and make sure p is not a list, 
## in case we created it as a list in the first place....
pp <- unlist( p ) 

## proportional amount by which we change parameters, i.e. 1%
tiny <- 0.01 

## Make a graphics device with 3 rows and 3 columns of figures
par( mfrow=c(3,3) )

## number of parameters
npar <- length(p)

## do the following for each parameter, i
for (i in 1:npar ) {
  
  ## make a slightly bigger parameter value
  p[i]  <- pp[i]*(1+tiny)
  
  ## run the model with a bigger p[i], dropping the first 500 obs.
  outp <- as.data.frame(
    ode(y = y, times=t, fun=bormann.logistic, parms = p) 
  )[ -(1:500), ]
  
  # run the model with a smaller p[i], dropping the first 500 obs.
  p[i]  <- pp[i]*(1-tiny)
  outm <- as.data.frame(
    ode(y = y, times=t, fun=bormann.logistic, parms = p) 
  )[ -(1:500), ]
  my.max <- 5900 
  my.min  <- 5700
  
  par(mar = c(2,2,2,2)) #changes margins of figures
  
  ## plot the result for parameter i
  plot( x = Reference$time, y = Reference$B, xlab="year", ylab="kg/m2", 
        type="l", main = names(p)[i], ylim = c(my.min, my.max))
  
  ## add a blue dashed line for plus tiny 
  lines(outp$time, outp$B, lty = 2, col = "blue")
  
  ## add a red dotted line for minus tiny
  lines(outm$time, outm$B, lty=3, col="red") 
  
  ## We RESET p[i] to its original value using our clean copy
  p[i] <- pp[i]
  
} # This is the loop to go through all parameters
One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

#Create reference to consider a numerical approach to sensitivity

out5 <- as.data.frame(
  ode(y = y, times=t, fun=bormann.logistic, parms = p) 
)
reference <- out5[-(1:500),]


pp <- unlist(p) 
state   <- y
yRef    <- reference$B
pp      <- unlist(p)
nout    <- length(yRef)
npar    <- length(p)

## now we use a truly small value: 0.000001%
tiny    <- 1e-8
dp      <- pp*tiny # the relative changes

## a matrix to hold our sensitivity values 
Sens    <- matrix(NA, nrow=nout, ncol=npar)
## name the columns by the parameters
colnames(Sens) <- names(p)

for (i in 1:npar)
{
  ## change the parrameter value
  dval    <- pp[i] + dp[i]
  p[i] <- dval
  
  ## run the model, and drop the first 500
  yPert   <- as.data.frame(ode(state, t, fun = bormann.logistic, p))$B[-(1:500)] 
  
  ##Calculates the sensitivities at all time points
  Sens[,i]<- (yPert-yRef)/dp[i]*pp[i] 
  
  ##...AND return p to its original values
  p[i] <- pp[i]
}

## Name the rows as the time points.
rownames(Sens) <- t[-(1:500)]

## show examples of sensitivities at arbitrary time points
format(as.data.frame(Sens[c(1, 61, 121, 181, 241),]), digits=2)
##      a20  a30 a12   a21  a31   a23  a02 a03    K A B
## 500 1342 3548 737 -2105 3331 -4403 -730 -34 1152 0 0
## 560 1402 3713 769 -2250 3634 -4773 -762 -36 1357 0 0
## 620 1443 3834 790 -2365 3932 -5127 -784 -38 1579 0 0
## 680 1466 3913 802 -2450 4224 -5464 -797 -40 1817 0 0
## 740 1473 3953 805 -2506 4507 -5785 -800 -41 2070 0 0
## make a picture
matplot(as.numeric(row.names(Sens)), Sens, type = 'l', 
        xlab = "Days", ylab = "Sensitivities")
legend("right", legend = colnames(Sens), lty =  1:5, col = 1:6  )

## Calculate quanititative summaries
mabs <- colMeans( abs(Sens) ) # or apply( Sens, 2, function(x) mean( abs( x ) ) )
msqr <- sqrt( colSums( Sens * Sens ) / nout ) 
format(data.frame(msqr,mabs),digits=2)
##     msqr mabs
## a20 1429 1428
## a30 3855 3854
## a12  781  781
## a21 2432 2429
## a31 4576 4528
## a23 5833 5785
## a02  776  776
## a03   40   40
## K   2243 2153
## A      0    0
## B      0    0
## Make a picture of the summed quantities
One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

##Optimized model for accurate representation of system
library(deSolve)
source("BormannLogistic.R")
bormann.logistic
## function(t, y, p) {
##   
##   with(as.list( c(y, p) ), {
##     ### ODEs
##     ## We hypothesize that the growth function is all that Bormann et al. included. 
##     ## Therefore, our logistic inhibition term acts on the entire forest.
##     dV.dt <- (a12 * A * V  - a21 * V - a31 * V) * (1 - V/K )
##     ## 
##     
##     ## A still loses N to vegetation, so we leave the loss function the same.
##     dA.dt <- ( a20 + a23 * B ) - ( a12 * V * A + a02 * A)
##     
##     ## 
##     dB.dt <- ( a30 + a31 * V ) - ( a23 * B + a03 * B )
##     loss <- a02*A + a03*B
##     # returning a list whose first (and only) element 
##     # is a vector of the ODE values
##     return( 
##       list(
##         c( dV.dt, dA.dt, dB.dt ), loss=loss
##       )
##     )
##   }
##   )
## }
p <- c( a20 = 6.5, a30 = 14.2, a12 = 79.6/(532*26), a21 = (6.6 + 0.8)/532,
        a31 = (54.2 + 2.7 + 6.2 + 0.1)/532,
        a23 = 69.6/4700, a02 = 3.9/26, a03 = 0.1/4700,
        K = 600)
y <- c(V = 532, A = 26, B = 4700)
t <- seq(from = 0, to = 500, by = 1)
out6 <- ode(y = y, times = t, func = bormann.logistic, parms = p)
plot( out6 )
One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

sse.bormann.mineralization <- function(params, data) {
  p['a23'] <- params
  ## run the model
  out7 <- ode(y = y, times = t, func = bormann.logistic, parms = p)
  ## store the last values of the state variables
  nr <- nrow(out7)
  model.output <- out7[nr,c("V", "A", "B")]
  ## Calculate the sum of the squared differences
  ## squaring the differences makes them positives and
  ## weights big differences even more heavily
  diffs <- model.output - data
  diffs2 <- diffs^2
  sse <- sum( diffs2 )
  ## Return the SSE
  sse
}
data = c(V = 532, A=26, B=4700)
fit0 <- optimize(f = sse.bormann.mineralization, interval = c(0.01, 0.5), data=data)
fit0
## $minimum
## [1] 0.018149
## 
## $objective
## [1] 4625.243
## $minimum
## [1] 0.018149
##
## $objective
## [1] 4625.243
p.opt <- p
p.opt['a23'] <- fit0$minimum
out.original <- as.data.frame( ode(y = y, times = 0:500, func = bormann.logistic, parms = p))
out.opt <- as.data.frame( ode(y = y, times = 0:500, func = bormann.logistic, parms = p.opt))
layout(matrix(1:4, nrow=2) )
plot(out.original$time, out.original$V, type="l")
lines(out.opt[,1], out.opt[,2], lty=2, lwd=1, col=2)
abline(h=data['V'], lty=3, lwd=1, col=4)
plot(out.original$time, out.original$A, type="l", ylim = c(23,33))
lines(out.opt[,1], out.opt[,3], lty=2, lwd=1, col=2)
abline(h=data['A'], lty=3, lwd=1, col=4)
plot(out.original$time, out.original$B, type="l", ylim=c(1000, 6000))
lines(out.opt[,1], out.opt[,4], lty=2, lwd=1, col=2)
abline(h=data['B'], lty=3, lwd=1, col=4)
sse.bormann.mineralization2 <- function(params, data) {
  ## an objective function whose arguments are
  ## params - the parameter of interest
  ## data - data from Bormannm et al.
  ## Assign a new value of the parameter to our set of parameters
  p['a23'] <- params[1]
  ## run the model
  out8 <- ode(y = y, times = t, func = bormann.logistic, parms = p)
  ## store the last values of the state variables
  nr <- nrow(out8)
  model.output <- out8[nr,c("V", "A", "B")]
  ## Calculate the sum of the squared differences
  ## squaring the differences makes them positives and
  ## weights big differences even more heavily
  diffs <- log(model.output, 10) - log(data, 10)
  diffs2 <- diffs^2
  sse <- sum( diffs2 )
  ## Return the SSE
  sse
}
fit0.log <- optimize(f = sse.bormann.mineralization2,
                     interval = c(0.001, 0.1), data=data)
fit0.log
## $minimum
## [1] 0.0181761
## 
## $objective
## [1] 0.002796935
## $minimum
## [1] 0.0181761
##
## $objective
## [1] 0.002796935
fit0
## $minimum
## [1] 0.018149
## 
## $objective
## [1] 4625.243
## $minimum
## [1] 0.018149
##
## $objective
## [1] 4625.243
p.opt.log <- p
p.opt.log['a23'] <- fit0.log$minimum
out.opt.log <- as.data.frame( ode(y = y, times = 0:500, func = bormann.logistic, parms = p.opt.log))
rbind(data=data, Bormann = out.opt[501, 2:4], raw=out.opt[501,2:4], log=out.opt.log[501,2:4])
##                V        A        B
## data    532.0000 26.00000 4700.000
## Bormann 599.9441 25.49684 4702.931
## raw     599.9441 25.49684 4702.931
## log     599.9447 25.49695 4695.946
## V A B
## data 532.0000 26.00000 4700.000
## Bormann 599.7254 25.46203 5753.202
## raw 599.9441 25.49684 4702.931
## log 599.9447 25.49695 4695.946

sse.bormann.mineralization3 <- function(params, data, vars) {
  
  p['a23'] <- params[1]
  
  ## run the model 
  out9 <- ode(y = y, times = t, func = bormann.logistic, parms = p)
  
  ## store the last values of the state variables
  nr <- nrow(out9)
  model.output <- out9[nr,c("V", "A", "B")]
  
  ## Calculate the sum of the squared differences
  ## squaring the differences makes them positives and 
  ## weights big differences even more heavily
  diffs <- log(model.output, 10) - log(data, 10)
  diffs2 <- diffs^2 
  wdiffs2 <- diffs2/vars
  sse <- sum( wdiffs2 )
  
  ## Return the SSE
  sse
}
vars <- c(532/10, 26/10, 4700*10)
fitv <- optimize(f = sse.bormann.mineralization3, interval = c(0.001, 0.1), data=data, vars=vars)
cbind(fit0, fit0.log, fitv)
##           fit0     fit0.log    fitv        
## minimum   0.018149 0.0181761   0.02453208  
## objective 4625.243 0.002796935 7.808268e-05
##           fit0     fit0.log    fitv        
## minimum   0.018149 0.0181761   0.02453208  
## objective 4625.243 0.002796935 7.808268e-05

######### Multiple Parameters ####
sse.b.m.p2 <- function(params, data) {
  ## an objective function whose arguments are 
  ## params - the parameter of interest
  ## data - data from Bormannm et al.
  
  ## Assign a new value of the parameter to our set of parameters
  p['a23'] <- params[1]
  p['a31'] <- params[2]
  ## run the model 
  outnp <- ode(y = y, times = t, func = bormann.logistic, parms = p)
  
  ## store the last values of the state variables
  nr <- nrow(outnp)
  model.output <- outnp[nr,c("V", "A", "B")]
  
  ## Calculate the sum of the squared differences
  ## squaring the differences makes them positives and 
  ## weights big differences even more heavily
  diffs <- log(model.output, 10) - log(data, 10)
  diffs2 <- diffs^2 
  sse <- sum( diffs2 )
  
  ## Return the SSE
  sse
}
params <- c(a32 = 69.6/4700, a31 = 0.19)
fit2 <- optim(par = params, fn = sse.b.m.p2, data=data)
fit2
## $par
##        a32        a31 
## 0.01854462 0.12182748 
## 
## $value
## [1] 0.002724496
## 
## $counts
## function gradient 
##       65       NA 
## 
## $convergence
## [1] 0
## 
## $message
## NULL
## $par
##        a32        a31 
## 0.01854462 0.12182748 
## 
## $value
## [1] 0.002724496
## 
## $counts
## function gradient 
##       65       NA 
## 
## $convergence
## [1] 0
## 
## $message
## NULL

########Variance weighted
sse.bormann.mineralization3 <- function(params, data, vars) {
  ## an objective function whose arguments are 
  ## params - the parameter of interest
  ## data - data from Bormannm et al.
  
  ## Assign a new value of the parameter to our set of parameters
  p['a23'] <- params[1]
  
  ## run the model 
  outm <- ode(y = y, times = t, func = bormann.logistic, parms = p)
  
  ## store the last values of the state variables
  nr <- nrow(outm)
  model.output <- outm[nr,c("V", "A", "B")]
  
  ## Calculate the sum of the squared differences
  ## squaring the differences makes them positives and 
  ## weights big differences even more heavily
  diffs <- log(model.output, 10) - log(data, 10)
  diffs2 <- diffs^2 
  wdiffs2 <- diffs2/vars
  sse <- sum( wdiffs2 )
  
  ## Return the SSE
  sse
}
vars <- c(532, 26, 4700)
fitv <- optimize(f = sse.bormann.mineralization3, 
                 interval = c(0.001, 0.1), data=data, vars=vars)
cbind(fit0, fit0.log, fitv)
##           fit0     fit0.log    fitv        
## minimum   0.018149 0.0181761   0.01831603  
## objective 4625.243 0.002796935 7.888169e-06
##           fit0     fit0.log    fitv        
## minimum   0.018149 0.0181761   0.01831603  
## objective 4625.243 0.002796935 7.888169e-06
One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

## Finally added in fire as disturbance
source("BormannLogistic.R")
p <- c( a20 = 6.5, a30 = 14.2, a12 = 79.6/(532*26), a21 = (6.6 + 0.8)/532, 
        a31 =  0.12182485, 
        a23 = 0.01823085, # previously optimized
        a02 = 3.9/26, a03 = 0.1/4700,
        K = 600)

y <- c(V = 532, A = 26, B = 4700)
t <- seq(from = 0, to = 500, by = 1)
outf <- ode(y = y, times = t, func = bormann.logistic, parms = p)
plot( outf )
One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

## event with data frame, 1 way to add fire
rows <- 5
eventdat <- data.frame(var = rep("V", rows), 
                       time = seq(100, 500, length=rows),
                       value = rep(100, rows),
                       method = rep("rep", rows)
)
eventdat
##   var time value method
## 1   V  100   100    rep
## 2   V  200   100    rep
## 3   V  300   100    rep
## 4   V  400   100    rep
## 5   V  500   100    rep
##Integrate the above
t <- 0:max(eventdat$time)
initial = y
outt <- ode(initial, times=t, fun=bormann.logistic, parms=p, 
            events=list(data=eventdat) # EVENTS as a data frame
)
plot(outt)
One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

##different plot of the above
matplot(out1[,1], out1[,-1], type="l", xlab="Time", ylab="N", log="y")
legend('right', colnames(out1[,-1]), lty=1:5, col=1:5, bty="n") 

## Add fire as an event
event.func <- function (t,y,p){
  V.burn <- y[1]*3/5*0.5 
  V.air <- V.burn/3
  V.bound <- V.burn/3
  V.avail <- V.burn/3
  B.burn <- y[3]*1/3*0.5
  B.avail <- B.burn/3
  A.burn <- y[2]*0.5
  A.air <- A.burn/1
  y[1] <- y[1] - V.burn
  y[2] <- y[2] + V.avail + B.avail - A.air
  y[3] <- y[3] + V.bound - B.burn
  return(y)}
fire <- round( seq(from=200, to=1000, by=200),0)
t <- 0:1000
initial <- c(V=100, A=30, B=4700)
outevent <- ode(initial, times=t, fun=bormann.logistic, parms=p,events=list(func=event.func, time=fire))
plot(outevent)
One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

One percent deviations in some parameters cause substantial variation.

#Conclusion
#The key mechanisms of N dynamics in the study site revealed the different fluxes among the pools and also check for balance of N for a steady state ecosystem