Schlenker and Roberts (2009) use daily minimum and maximum temperatures to calculate growing degrees, rather than daily mean temperatures. This is important when the effect of extreme temperatures is an issue, since these often will not show up in mean temperatures.

Growing degree days form a useful model of crop productivity. DMAS has examples of these for maize, soybeans, and cotton.

To do this, they use a sinusoidal approximation, integrating the area of a curve through the minimum and maximum temperatures: (adapted from here— but don’t use their calculations!)

The calculations aren’t very difficult, but require some careful math. I had a need to write them in python and translate them to R, so I’m providing them here for anyone’s benefit.

```import numpy as np
import warnings

warnings.simplefilter("ignore", RuntimeWarning)

def above_threshold(mins, maxs, threshold):
"""Use a sinusoidal approximation to estimate the number of Growing
Degree-Days above a given threshold, using daily minimum and maximum
temperatures.

mins and maxs are numpy arrays; threshold is in the same units."""

# Determine crossing points, as a fraction of the day
plus_over_2 = (mins + maxs)/2
minus_over_2 = (maxs - mins)/2
two_pi = 2*np.pi
# d0s is the times of crossing above; d1s is when cross below
d0s = np.arcsin((threshold - plus_over_2) / minus_over_2) / two_pi
d1s = .5 - d0s

# If always above or below threshold, set crossings accordingly
aboves = mins >= threshold
belows = maxs <= threshold

d0s[aboves] = 0
d1s[aboves] = 1
d0s[belows] = 0
d1s[belows] = 0

# Calculate integral
F1s = -minus_over_2 * np.cos(2*np.pi*d1s) / two_pi + plus_over_2 * d1s
F0s = -minus_over_2 * np.cos(2*np.pi*d0s) / two_pi + plus_over_2 * d0s
return np.sum(F1s - F0s - threshold * (d1s - d0s))

def get_gddkdd(mins, maxs, gdd_start, kdd_start):
"""Get the Growing Degree-Days, as degree-days between gdd_start and
kdd_start, and Killing Degree-Days, as the degree-days above
kdd_start.

mins and maxs are numpy arrays; threshold is in the same units."""

dd_lowup = above_threshold(mins, maxs, gdd_start)
dd_above = above_threshold(mins, maxs, kdd_start)
dd_lower = dd_lowup - dd_above

return (dd_lower, dd_above)
```