I wanted to compare my Anova circulator's reading to the reading from my new Thermapen.
Previously, I had used the Anova's app to adjust the offset so that the two readings matched at $135^\circ\mathrm{F}$ -- which required a hefty offset of $-5.5^\circ\mathrm{F}$. I believe that this is much better than the factory-original temperature calibration, because meats (and test eggs) are coming out much closer to what I expected. Now, to make my measurements, I just increased the temperature between $100^\circ\mathrm{F}$ and $180^\circ\mathrm{F}$, taking readings every $10^\circ\mathrm{F}$ or so (a couple extra around $130^\circ\mathrm{F}$). My pot of water was pretty nicely insulated, and the top was covered, so I don't think non-equilibrium effects are important. I waited a good long while for the system to equilibrate at the two extremes, but the wait made no difference in my measurement, so I don't think it would be worthwhile to wait at each point; I just waited for the Anova's measured temperature to settle down at each point.
Basically, my conclusion is that (now that I've set the offset) the readings are now precisely consistent right near where I set them to be, and the Anova and Thermapen measure the same temperature within the uncertainty across the range of useful cooking temperatures. The Thermapen reports an accuracy of $\pm 0.7^\circ\mathrm{F}$ across its range, which is reflected in the error bars in the plot below. At the higher end of the scale, the true temperature is slightly lower than the set temperature, but this looks acceptable -- especially since most such meals would be vegetables anyway, so precision isn't as crucial. At the lower end, things may be slightly overdone, but only fish really goes below $130^\circ\mathrm{F}$, and fish is unusually forgiving, apparently. Besides, all of this is within the uncertainty of my Thermapen, so presumably I'll just have to learn the temperatures I like, and aim for consistency.
So, just for consistency, with these results the easiest way to do this is to take my target temperature $T_{\mathrm{true}}$, subtract the equality temperature $T_{\mathrm{eq}} = 135^\circ\mathrm{F}$, multiply by $0.016$, and add that to my set point: \begin{equation*} \Delta T = 0.016\, (T_{\mathrm{true}} - 135^\circ\mathrm{F}). \end{equation*} Of course, since the Anova can only be set in increments of $0.5^\circ\mathrm{F}$, we have to just dial in the thing that will get us closest. For things I cook, this just means to move up one notch at temperatures above $\sim 151^\circ\mathrm{F}$.
All in all, then, the Anova (after calibrating for that huge the offset) is basically within the stated accuracy of my Thermapen for almost all my uses -- though the trend seems to indicate a significant effect. Either way, the need for that huge offset makes me very nervous. I've become neurotic about rechecking the water temperature. This is not the set-and-forget device I thought I was buying. And I certainly didn't expect to have to buy a Thermapen to believe the temperature.
import datetime
import numpy as np
# dataset measured 2015-02-16 with -5.5F offset in the Anova app
T_set, T_true = np.array([
[100.0, 100.45],
[110.0, 110.35],
[120.1, 120.4],
[125.0, 125.2],
[130.1, 130.2],
[135.0, 135.0],
[140.1, 140.1],
[150.0, 149.8],
[160.0, 159.5],
[170.0, 169.4],
[180.0, 179.3],]).transpose()
fit = np.poly1d(np.polyfit(T_set, T_true, 1))
T_equality = fit[0]/(1-fit[1])
T_set_precision = 0.5
T_correction_factor = (1.0/fit[1])-1.0
for j in range(-2,3):
T_lower = T_equality + T_set_precision*(j-0.5)/T_correction_factor
T_upper = min(212, T_equality + T_set_precision*(j+0.5)/T_correction_factor)
print(r'T ∈ [{0:.0f}°F, {1:.0f}°F): {2:+} notches'.format(T_lower, T_upper, j))
fig, axes = plt.subplots(figsize = (18.*0.7, 10.75*0.8));
annotate(r'Equality at $T_{{\mathrm{{eq}}}} = {0:.1f}^\circ\mathrm{{F}}$'.format(T_equality),
xy=[T_equality, T_equality],
xytext=[T_equality-4., T_equality-8.],
arrowprops=dict(arrowstyle="->"))
annotate(r'$T_{{\mathrm{{set}}}} - T_{{\mathrm{{true}}}} = {0:.1f}^\circ\mathrm{{F}}$'.format(T_set[-1]-T_true[-1]),
xy=[T_set[-1], (T_set[-1]+T_true[-1])/2],
xytext=[T_set[-1]-18., (T_set[-1]+T_true[-1])/2-2.5],
arrowprops=dict(arrowstyle="->"))
annotate(r'$T_{{\mathrm{{set}}}} - T_{{\mathrm{{true}}}} = {0:.1f}^\circ\mathrm{{F}}$'.format(T_set[0]-T_true[0]),
xy=[T_set[0], (T_set[0]+T_true[0])/2],
xytext=[T_set[0]+5., (T_set[0]+T_true[0])/2+1.5],
arrowprops=dict(arrowstyle="->"))
plot(T_set, T_set, label='expected', color='gray')
errorbar(T_set, T_true, yerr=[0.7]*len(T_true), fmt='-o', label='actual')
plot(T_set, fit(T_set), label='fit')
text(102.0, 155.0, r'$T_{{\mathrm{{true}}}} = {1:.2f}\, T_{{\mathrm{{set}}}} + {0:.2f}$'.format(fit[0], fit[1]))
text(102.0, 150.0, r'$T_{{\mathrm{{set}}}} = {1:.2f}\, T_{{\mathrm{{true}}}} - {0:.2f}$'.format(fit[0]/fit[1], 1.0/fit[1]))
text(102.0, 140.0, r'$T_{{\mathrm{{set}}}}-T_{{\mathrm{{true}}}} = '
r'{0:.3f} (T_{{\mathrm{{true}}}} - T_{{\mathrm{{eq}}}})$'.format((1.0/fit[1])-1.0))
text(0.99, 0.01, str(datetime.date.today()), ha='right', va='bottom', transform=axes.transAxes)
xlabel(r'Anova reading ($T_{\mathrm{set}}$; ${}^\circ\mathrm{F}$)')
ylabel(r'Thermapen reading ($T_{\mathrm{true}}$; ${}^\circ\mathrm{F}$)')
legend(loc='upper left');
figure();
fig, axes = plt.subplots(figsize = (18.*0.7, 10.75*0.4));
axhline(0.1, c='gray');
axhline(-0.1, c='gray');
plot(T_set, fit(T_set)-T_true);
ylim((-0.12,0.12));
xlabel(r'Anova reading ($T_{\mathrm{set}}$; ${}^\circ\mathrm{F}$)');
ylabel(r'Fit residuals (${}^\circ\mathrm{F}$)');
T ∈ [56°F, 88°F): -2 notches T ∈ [88°F, 119°F): -1 notches T ∈ [119°F, 150°F): +0 notches T ∈ [150°F, 182°F): +1 notches T ∈ [182°F, 212°F): +2 notches
<matplotlib.figure.Figure at 0x10d72d510>
The fit residuals are all within about $0.1^\circ\mathrm{F}$ of $0$. Considering that the precision of the Thermapen's display is only $0.1^\circ\mathrm{F}$, this means that the fit is essentially perfect. Obviously, the fit may be wrong, in the sense that the Thermapen may be wrong -- but the fit perfectly describes the data. Moreover, the fact that the residuals from a first-degree polynomial fit are so small suggests that both the Thermapen and Anova are behaving very linearly, but there's a simple offset and slope error in one (or both). The Thermapen is brand new and has such a great reputation, while this new-model Anova has been having quality-control problems. And since my meat and egg temperatures are coming out perfectly now that I'm basing temps on the Thermapen, I'm gonna have to say that the Anova is the source of the problem.