balance_computation R function

balance_computation R function#

Below, you can find the R code for the function balance_computation. The details of the algorithm are explained in the previous section.

### Function to compute the balance
balance_computation <- function(ET, Rad, Temp, Rain, DOY,
                                start_flowering = 109,  DayFolAreaMax = 171, 
                                RAW = 32, net_rain_effect = .66)
{
  require(dplyr)
  # ET is the reference evapotranspiration
  # Rad is solar radiation in W/m^2
  # Temp is temperature in Celsius degrees
  # Rain is rainfall in mm
  # DOY is the day of the year
  
  # start_flowering is the date (in day of year) of the beggining of the flowering;
  # default value (109) corresponds approximately to 21.04 (in day of the year)
  
  # DayFolAreaMax is the date (in day of year) where the maximum leaf surface is reached;
  # default value (171) corresponds approximately to 20.06 (in day of the year)
  
  # net_rain_effect is the coefficient that impacts the rainfall due to the nets
  # RAW is the Readily Available Water.

  ## See equation (1) and paragraph below for constants
  ## alpha =1.26 is given in the end of the section 1
  alph = 1.26 #  is the Priestley–Taylor parameter/constant.
  gamm = 0.066 # [kPa/°C] is the psychrometric coefficient
  lam = 2.45#  [MJ/L] is the latent heat  of vaporization; 
  # A [MJ/ (tree * day)]  is the total amount of net  (all-wave) radiation absorbed by the leaf canopy; 
  
  # start_flowering = 
  # DayFolAreaMax = 171 # Day when the max of the leaf surface is reached, in day of the year
  
  
  # the slope of the saturation vapor pressure curve at average daily air temperature
  s = 4098*0.6108*exp( (17.27*Temp)/(Temp+237.3)) / (Temp+237.3)^2
  
  DFlowering = DOY - start_flowering + 1  # day counts starting from flowering
  Q = (-0.000881834215*DFlowering ^2 + 0.150793650794*DFlowering)/6 * 100 # in percent
  LAIMaxPercent = dplyr::case_when(
    DOY < start_flowering ~ DOY * 1.612903 - 175.806452, # linear interpolation through the
    # 2 points (109, 0) and (171, 100); 
    DOY >= start_flowering & DOY < DayFolAreaMax ~ Q,
    DOY >= DayFolAreaMax & DOY < 252 ~ 100,
    DOY >= 252 ~ DOY * -0.7843 + 297.65,
    .default = NULL
  )
  LAIMax <- LAIMaxPercent/100
  # Computation in 5 categories corresponding to LAI categories
  LA = outer(X= LAIMax, Y = 5 *seq(from = 0.5, to =2.5, by= .5)) |> data.frame()
  names(LA) = paste("LA_", 1:5 * 0.5, sep = "")
  # Conversion of units: see 
  Rs = Rad/(1000000/86400) # changing units of solar radiation
  # Transformation of global radiation into useful radiation
  # Rn = Rs *0.617-1.004 
  Rn = Rs *0.617-1.004 
  # See Pereira et al. (2007b)
  A = .303 * LA * Rn ## Rn * LA per tree
  names(A) = paste("A_", 1:5 * 0.5, sep = "")
  # daily sap flow of a tree [l/(TREE * DAY)], using Priestley–Taylor formula
  # Daily sap flow is denoted by Tc in the documentation
  Daily_sap_flow = alph*(s/(s+gamm)*(1/lam)) * A
  names(Daily_sap_flow) = paste("Tc_", 1:5 * 0.5, sep = "")
  Daily_sap_flow_unit = Daily_sap_flow/5
  names(Daily_sap_flow_unit) <- paste("Daily_sap_flow_unit_", 1:5 * 0.5, sep = "")
  
  # third order polynomial function
  DOY_05_fun <- function(x)  0.000000010*x^3 - 0.000008397*x^2 + 0.000772552*x + 0.381393786 
  DOY_1_fun  <- function(x)  0.000000030*x^3 - 0.000014633*x^2 + 0.000732618*x + 0.401501383
  DOY_15_fun <- function(x)  0.000000033*x^3 - 0.000014090*x^2 - 0.000000916*x + 0.443715597
  DOY_2_fun  <- function(x)  0.000000017*x^3 - 0.000003173*x^2 - 0.002420317*x + 0.551676009
  DOY_25_fun <- function(x) -0.000000030*x^3 + 0.000025772*x^2 - 0.007554528*x + 0.763455458 

  Evap <- lapply(list(DOY_05_fun,DOY_1_fun, DOY_15_fun,DOY_2_fun, DOY_25_fun), 
                 FUN = function(z)z(DOY) * ET)
  Evap <- do.call(cbind, args = Evap) |> data.frame()
  names(Evap) <- paste("Evap_", 1:5 * 0.5, sep = "")
  Tmat = LAIMax * Daily_sap_flow_unit
  Tmat[LAIMax < 0,] <- 0
  ETc_ETo <- (Tmat + Evap)/ET
  ETc_ETo[ET == 0,] <- 0
  # first, we create the condition to be verified:
  # to be usefull, rainfall must be at least of 5mm over the past 2 days.
  # Then, the useful rainfall by day is the rainfall provided that 
  # this condition (>5mm in the past 2 days) is met.
  RainfallThreshold<- c(Rain[1],
                     Rain[2:length(Rain)] + 
                       Rain[1:(length(Rain) - 1)] # sum of the daily and one day before rainfall
  )
  # Usefull rainfall
  RainfallUseful = ifelse(RainfallThreshold > 5, yes = Rain, no = 0)
  # We initialize the matrix Balance
  Balance = (RainfallUseful * net_rain_effect) - (Tmat + Evap)
  # We initialize the first value of the Balance, considering that we have
  # RAW is completely available
  Balance[1,] <- RAW - ET[1] * (Tmat + Evap)[1,]
  # we define a custom function to truncate values in a given interval
  truncation <- function(x, a = 0 , b = RAW)
  {
    ifelse(x > b, yes = b, no = ifelse(x<a, yes = 0, no = x))
  }
  # we iteratively update the values by adding the preceeding values and then
  # truncate it to the interval [0, RAW]
  for(i in 2:nrow(Balance))
  {
    Balance[i,] = truncation(Balance[i,] + Balance[i-1,])
  }
  names(Balance) <- paste("Balance_res_", 1:5 * 0.5, sep = "")
  # The irrigation is then given by
  Irrigation = Tmat + Evap
  Irrigation[Balance > 0] = 0
  names(Irrigation) <- paste("Irrigation_", 1:5 * 0.5, sep = "")
  # a simple mean of the past n values
  ma <- function(vec, n) sapply(5:length(vec), function(z)
    mean(vec[(z-n):(z-1)]))
  SmoothedIrrigation <- apply(Irrigation, 2, 
                              function(z) c(rep(NA, 4), ma(z, n = 4)))
  # we truncate it to the interval
  SmoothedIrrigation[Balance >3] <- 0
  l <- list(s = s, LAIMaxPercent = LAIMaxPercent, Rn = Rn, 
            Daily_sap_flow = Daily_sap_flow, Evap = Evap, Tmat = Tmat,
            RainfallUseful = RainfallUseful,
            Balance = Balance, Irrigation = Irrigation,
            SmoothedIrrigation = SmoothedIrrigation)
  class(l) = "Irrigation"
  return(l)
}

print.Irrigation <- function(x, ...){
  x = x[c("Irrigation")]
  NextMethod()
}