phenor
phenor copied to clipboard
Integrate Zani et al. 2020 models
Integrate Zani et al. 2020 models as the original authors fail or refuse to do so.
The paper is an dumpster fire for other reasons, but commiting code would have been a low standard. Alas, not so.
##----------------------------------------
## MODELS OF LEAF SENESCENCE (and drivers):
## First-generation:
# CDD (chilling temperature) - Dufrene et al. (2005)
# DM1 and DM2 (chilling temperature, autumn daylength) - Delpierre et al. (2009)
# TPM (chilling temperature, autumn daylength) - Lang et al. (2019)
## Second-generation:
# SIAM (chilling temperature, autumn daylength, spring anomaly) - Keenan and Richardson (2015)
# TDM and TPDM (chilling temperature, autumn daylength, growing season temperature / + water stress) - Liu et al. (2019)
## PIA:
# PIA_gsi (chilling temperature, autumn daylength, leaf flushing date, growing season mean temperature, daylength, vapour pressure deficit)
# PIA-/+ (chilling temperature, autumn daylength, leaf flushing date, growing season mean temperature, daylength, precipitation, net radiation, CO2 concentration, -/+ water stress)
# Define functions of Autumn phenology Models
# Modified from https://github.com/khufkens/phenor/blob/master/R/phenology_models.R
CDD.model = function(par, data){
# exit the routine as some parameters are missing
if (length(par) != 2){
stop("model parameter(s) out of range (too many, too few)")
}
# extract the parameter values from the
# par argument for readability
T_base = par[1]
F_crit = par[2]
# create forcing/chilling rate vector
Rf = data$Tmini - T_base
Rf[Rf > 0] = 0
# photoperiod-dependent start-date for chilling accumulation (t0)
# t0 is defined as the first day when daily minimum temperature is lower than a temperature threshold (T_base)
# after the date of the peak multiyear average daily minimum temperature, namely the 200th day of year
t0 <- vector()
for(c in 1:ncol(data$Tmini)) {
interval = 1:366
t0A = interval[which(data$Tmini[,c] < T_base)]
ind1 = min(which(t0A > 200))
t0A = t0A[ind1]
t0 = c(t0,t0A)
}
# nullify values before the t0
for(c in 1:ncol(data$Tmini)){
Rf[1:t0[c],c] = 0
}
# predict date of leaf.off according to optimized F_crit
doy = apply(Rf,2, function(xt){
doy = which(cumsum(xt) <= F_crit)[1]
})
return(doy)
}
DM1.model = function(par, data){
# exit the routine as some parameters are missing
if (length(par) != 3){
stop("model parameter(s) out of range (too many, too few)")
}
# extract the parameter values from the
# par argument for readability
T_base = par[1]
P_base = par[2]
F_crit = par[3]
# create forcing/chilling rate vector at the day level
Rf = (data$Tmini - T_base)*(data$Li/P_base) # lengthening photoperiod promoting leaf senescence
Rf[Rf > 0] = 0
# photoperiod-dependent start-date for chilling accumulation (t0)
# t0 is defined as the first day when photoperiod is shorter than the photoperiod threshold (P_base)
# after the date of the longest photoperiod (summer solstice), namely, the 173rd day of year
t0 <- vector()
for(c in 1:ncol(data$Tmini)) {
interval = 1:366
t0A = interval[which(data$Li[,c] < P_base)]
ind1 = min(which(t0A > 173))
t0A = t0A[ind1]
t0 = c(t0,t0A)
}
# nullify values before the t0
for(c in 1:ncol(data$Tmini)){
Rf[1:t0[c],c] = 0 #nullify values before the date of leaf.out
}
# calculate the summation along the year (interval = 1:366) and derive the date of leaf.off
# DOY of budburst criterium
doy = apply(Rf,2, function(xt){
doy = which(cumsum(xt) <= F_crit)[1]
})
return(doy)
}
DM2.model = function(par, data){
# exit the routine as some parameters are missing
if (length(par) != 3){
stop("model parameter(s) out of range (too many, too few)")
}
# extract the parameter values from the
# par argument for readability
T_base = par[1]
P_base = par[2]
F_crit = par[3]
# create forcing/chilling rate vector at the day level
Rf = (data$Tmini - T_base)*(1-(data$Li/P_base)) # shortening photoperiod promoting leaf senescence
Rf[Rf > 0] = 0
# photoperiod-dependent start-date for chilling accumulation (t0)
# t0 is defined as the first day when photoperiod is shorter than the photoperiod threshold (P_base)
# after the date of the longest photoperiod (summer solstice), namely, the 173rd day of year
t0 <- vector()
for(c in 1:ncol(data$Tmini)) {
interval = 1:366
t0A = interval[which(data$Li[,c] < P_base)]
ind1 = min(which(t0A > 173))
t0A = t0A[ind1]
t0 = c(t0,t0A)
}
# nullify values before the t0
for(c in 1:ncol(data$Tmini)){
Rf[1:t0[c],c] = 0 #nullify values before the date of leaf.out
}
# calculate the summation along the year (interval = 1:366) and derive the date of leaf.off
# DOY of budburst criterium
doy = apply(Rf,2, function(xt){
doy = which(cumsum(xt) <= F_crit)[1]
})
return(doy)
}
TPM.model = function(par, data){
# exit the routine as some parameters are missing
if (length(par) != 4){
stop("model parameter(s) out of range (too many, too few)")
}
# extract the parameter values from the par argument for readability
P_base = par[1]
a = par[2]
b = par[3]
F_crit = par[4]
# create forcing/chilling rate vector at the day level
Rf = 1/(1+exp(a*(data$Tmini*data$Li-b)))
# photoperiod-dependent start-date for chilling accumulation (t0)
# t0 is defined as the first day when photoperiod is shorter than the photoperiod threshold (P_base)
# after the date of the longest photoperiod (summer solstice), namely, the 173rd day of year
t0 <- vector()
for(c in 1:ncol(data$Tmini)) {
interval = 1:366
t0A = interval[which(data$Li[,c] < P_base)]
ind1 = min(which(t0A > 173))
t0A = t0A[ind1]
t0 = c(t0,t0A)
}
# nullify values before the t0
for(c in 1:ncol(data$Tmini)){
Rf[1:t0[c],c] = 0 #nullify values before the date of leaf.out
}
# calculate the summation along the year and derive the date of leaf.off
# DOY of budburst criterium
doy = apply(Rf,2, function(xt){
doy = which(cumsum(xt) >= F_crit)[1]
})
return(doy)
}
Ycrit = function(data, P_base, a, b){
# create forcing/chilling rate vector at the day level
Rf = 1/(1+exp(a*(data$Tmini*data$Li-b)))
# photoperiod-dependent start-date for chilling accumulation (t0)
# t0 is defined as the first day when photoperiod is shorter than the photoperiod threshold (P_base)
# after the date of the longest photoperiod (summer solstice), namely, the 173rd day of year
t0 <- vector()
for(c in 1:ncol(data$Tmini)) {
interval = 1:366
t0A = interval[which(data$Li[,c] < P_base)]
ind1 = min(which(t0A > 173))
t0A = t0A[ind1]
t0 = c(t0,t0A)
}
# nullify values before the t0
for(c in 1:ncol(data$Tmini)){
Rf[1:t0[c],c] = 0 #nullify values before the date of leaf.out
}
# add observed leaf-off dates at the end of the matrix-column
Rf = rbind(Rf,data$transition_dates)
# calculate the summation along the year and derive the date of leaf.off
# DOY of budburst criterium
F_crit = apply(Rf,2, function(xt){
F_crit = cumsum(xt[1:366])[xt[367]]
return(F_crit)
})
return(F_crit)
}
SecondGen_PIA.models = function(par, predictor, data) {
# exit the routine as some parameters are missing
if (length(par) != 5 & length(par) != 6){
stop("model parameter(s) out of range (too many, too few)")
}
# extract the parameter values from the
# par argument for readability
P_base = as.numeric(par[1])
a = as.numeric(par[2])
b = as.numeric(par[3])
c = as.numeric(par[4])
if(length(par)==5) {
d = as.numeric(par[5])
pred = predictor
}
if(length(par)==6) {
d = as.numeric(par[5])
e = as.numeric(par[6])
pred1 = predictor[1]
pred2 = predictor[2]
}
# create forcing/chilling rate vector at the day level
Rf = 1/(1+exp(a*(data$Tmini*data$Li-b)))
# photoperiod-dependent start-date for chilling accumulation (t0)
# t0 is defined as the first day when photoperiod is shorter than the photoperiod threshold (P_base)
# after the date of the longest photoperiod (summer solstice), namely, the 173rd day of year
t0 <- vector()
for(col in 1:ncol(data$Tmini)) {
interval = 1:366
t0A = interval[which(data$Li[,col] < 173)]
ind1 = min(which(t0A > 173))
t0A = t0A[ind1]
t0 = c(t0,t0A)
}
# nullify values before the t0
for(col in 1:ncol(data$Tmini)){
Rf[1:t0[col],col] = 0
}
if(length(par)==5) {
# add predictor at the end of the matrix-columns
Rf = rbind(Rf,predictor)
# predict date of leaf.off
doy = apply(Rf,2, function(xt){
doy = which(cumsum(xt[1:366]) >= c+d*xt[367])[1]
})
}
if(length(par)==6) {
# add predictors at the end of the matrix-columns
Rf = rbind(Rf,pred1)
Rf = rbind(Rf,pred2)
# predict date of leaf.off
doy = apply(Rf,2, function(xt){
doy = which(cumsum(xt[1:366]) >= c+d*xt[367]+e*xt[368])[1]
})
}
return(doy)
}