Chapter 7 Chill models

Learning goals for this lesson

  • Learn how to run chill models using chillR
  • Learn how to produce your own temperature response model in chillR

7.1 Chill models in chillR

We already learned how to write a function to calculate Chilling Hours. Unfortunately, you may never have to do that, because chillR already contains such a function. It’s called Chilling_Hours(), and here’s what it looks like:

library(chillR)
Chilling_Hours
## function (HourTemp, summ = TRUE) 
## {
##     CH_range <- which(HourTemp <= 7.2 & HourTemp >= 0)
##     CH_weights <- rep(0, length(HourTemp))
##     CH_weights[CH_range] <- 1
##     if (summ == TRUE) 
##         return(cumsum(CH_weights))
##     else return(CH_weights)
## }
## <bytecode: 0x000001fbd0657c38>
## <environment: namespace:chillR>

This is a pretty basic function that takes an hourly temperature dataset (HourTemp) as input and determines for each hour if temperatures are below 7.2°C and above 0°C. If the argument summ is TRUE, the function returns the cumulative Chilling Hours that have accumulated by every hour of the record. If this argument is FALSE, we just get a list of 1s and 0s to indicate which hours are Chilling Hours and which ones aren’t. The default version of the function is to run it with summ==TRUE, as you can see in the first line of the function. So if you don’t specify anything for the summ argument, it will return the cumulative sum of Chilling Hours.

We can easily apply this now to our Winters_hours_gap dataset:

Chilling_Hours(Winters_hours_gaps$Temp)[1:100]
##   [1]  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  2  2  2  3  4  5  6
##  [23]  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6
##  [45]  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6  6  7  8  9 10 11
##  [67] 12 13 14 15 16 16 16 16 16 16 16 16 16 16 16 16 16 16 16 17 18 19
##  [89] 20 21 22 23 24 25 25 25 25 25 25 25

I only returned the first 100 values. The dataset contains 5974 more. The last number in this entire series is 203.

Chilling Hours are great for an entry-level tutorial on chill modeling, but they’re not a particularly credible metric, so I suggest you forget them right away. Let’s turn our eyes towards more credible models.

The Utah Model (Richardson et al., 1974) is somewhat more credible, since it assumes different weights for different temperatures. This model is also implemented in chillR:

Utah_Model
## function (HourTemp, summ = TRUE) 
## return(step_model(HourTemp, df = data.frame(lower = c(-1000, 
##     1.4, 2.4, 9.1, 12.4, 15.9, 18), upper = c(1.4, 2.4, 9.1, 
##     12.4, 15.9, 18, 1000), weight = c(0, 0.5, 1, 0.5, 0, -0.5, 
##     -1)), summ = summ))
## <bytecode: 0x000001fbd43ebb38>
## <environment: namespace:chillR>
Utah_Model(Winters_hours_gaps$Temp)[1:100]
##   [1]  0.0 -0.5 -1.5 -2.5 -3.5 -4.5 -5.5 -6.0 -6.0 -6.0 -5.5 -5.0 -4.0
##  [14] -3.0 -2.0 -1.0  0.0  0.5  1.5  2.5  3.5  4.5  5.0  5.0  5.0  4.0
##  [27]  3.0  2.0  1.0  0.0 -1.0 -2.0 -2.5 -2.5 -2.0 -1.5 -1.0 -0.5  0.5
##  [40]  1.0  1.5  2.0  2.0  2.5  3.0  3.5  4.0  4.0  4.0  3.5  2.5  1.5
##  [53]  0.5 -0.5 -1.5 -2.5 -3.0 -3.0 -2.5 -1.5 -0.5  0.5  1.5  2.5  3.5
##  [66]  4.5  5.5  6.5  7.5  8.5  9.5 10.0 10.0  9.5  9.0  8.5  8.0  7.5
##  [79]  7.0  6.5  6.5  7.0  7.5  8.5  9.5 10.5 11.5 12.5 13.5 14.5 15.5
##  [92] 16.5 17.5 18.5 19.0 19.0 19.0 18.5 17.5 16.5

In the definition of this model, you see another function called step_model(). This is also a chillR function, which allows you to define your own model, based on temperature thresholds and weights. You could, for example, use this function to implement various variations of the Utah Model that have been developed for different locations. The function takes as input a data.frame that contains the weights you want to apply to different temperature ranges.

Here’s an example of such a data.frame, a function called custom() that implements a chill model based on this, and the application of this function to the Winters_hours_gaps dataset:

df<-data.frame(
  lower= c(-1000, 1, 2, 3, 4, 5,    6),
  upper= c(    1, 2, 3, 4, 5, 6, 1000),
  weight=c(    0, 1, 2, 3, 2, 1,    0))

kable(df) %>%
  kable_styling("striped", position = "left", font_size = 10)
lower upper weight
-1000 1 0
1 2 1
2 3 2
3 4 3
4 5 2
5 6 1
6 1000 0
custom<-function(x) step_model(x,df)

custom(Winters_hours_gaps$Temp)[1:100]
##   [1]  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  4  7
##  [23]  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7
##  [45]  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  7  8 10
##  [67] 13 16 19 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 23
##  [89] 25 27 29 31 34 37 37 37 37 37 37 37

The Chilling Hours and Utah Models are fairly straightforward. We could probably have calculated these metrics without these functions, though of course the process gets easier with them. What has long been a much greater challenge to dormancy modelers is implementing the ‘Dynamic Model’, which involves pretty complicated equations. The original papers on this model were rather heavy on maths, leaving many horticultural researchers a bit lost. For a long time, the only version of the model that people could easily use was an Excel sheet that was put together a few decades ago. For chillR, I extracted all the equations from this Excel sheet, to make the Dynamic_Model() function. This is pretty complicated, and a bit of effort was needed to get this right. But the effort was worth it - now we’ll never have to deal with these equations again, because we have a function that does all the calculations for us.

Dynamic_Model(Winters_hours_gaps$Temp)[1:100]
##   [1] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##   [7] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##  [13] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##  [19] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##  [25] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##  [31] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##  [37] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##  [43] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##  [49] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##  [55] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##  [61] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000
##  [67] 0.0000000 0.0000000 0.0000000 0.0000000 0.0000000 0.9698435
##  [73] 0.9698435 0.9698435 0.9698435 0.9698435 0.9698435 0.9698435
##  [79] 0.9698435 0.9698435 0.9698435 0.9698435 0.9698435 0.9698435
##  [85] 0.9698435 0.9698435 0.9698435 0.9698435 0.9698435 0.9698435
##  [91] 0.9698435 0.9698435 0.9698435 0.9698435 0.9698435 0.9698435
##  [97] 0.9698435 0.9698435 0.9698435 0.9698435

So chillR has a few functions that can be applied to hourly temperature data. It also has wrapper functions that allow computing chill between specific start and end dates. The chilling() function automatically calculates a few basic metrics for us. Note that we have to use the chillR function make_JDay() here to add the Julian dates (counts the days of the year) to the dataset for this to work.

output<-chilling(make_JDay(Winters_hours_gaps),Start_JDay = 90, End_JDay = 100)

kable(output) %>%
  kable_styling("striped", position = "left", font_size = 10)
Season End_year Season_days Data_days Perc_complete Chilling_Hours Utah_Model Chill_portions GDH
2007/2008 2008 11 11 100 40 15.5 2.009147 2406.52

So the chilling() function implements the Chilling Hours, Utah and Dynamic Models, and it also calculates Growing Degree Days (GDH). But maybe we don’t want all of these, or we want other metrics. In that case, we can make use of the tempResponse function, which is somewhat similar to chilling(), but it takes as input a list of temperature models to be computed.

output<-tempResponse(make_JDay(Winters_hours_gaps),
                     Start_JDay = 90, End_JDay = 100,
                     models=list(Chill_Portions=Dynamic_Model, GDH=GDH))

kable(output) %>%
  kable_styling("striped", position = "left", font_size = 10)
Season End_year Season_days Data_days Perc_complete Chill_Portions GDH
2007/2008 2008 11 11 100 2.009147 2406.52

Exercises on chill models

Please document all results of the following assignments in your learning logbook.

  1. Run the chilling() function on the Winters_hours_gap dataset
  2. Create your own temperature-weighting chill model using the step_model() function
  3. Run this model on the Winters_hours_gaps dataset using the tempResponse() function.

References

Richardson E, Seeley S & Walker D. (1974). A model for estimating the completion of rest for "redhaven" and "elberta" peach trees. HortScience, 9(4), 331–332.