Foreword



The Coffee Cooling Problem

In [1]:
%pylab inline
Populating the interactive namespace from numpy and matplotlib

The coffee cooling problem

You get a cup of coffee at a coffee shop. Should you mix the cream into:

  1. the coffee at the shop?
  2. wait until you reach your office?

The goal is to have the coffee as hot as possible.

How the coffee is going to cool as you walk back to the office?

The choices:

  1. If you pour the cream at the shop, there is a sudden drop of temperature before the coffee starts to cool down as you walk back to the office,
  2. If you pour the cream after getting back to the office, the sudden drop occurs after the cooling period during the walk.

Model for the cooling process

The simplest such model is Newton's cooling law, which states that the rate of cooling is proportional to the temperature difference between the coffee in the cup and the ambient temperature. This reflects the intuitive notion that, for example, if the outside temperature is 40°F, the coffee cools faster than if it is 50°F. This assumption leads to a well-known formula for the way the temperature changes:

$$ T_{final} = T_{outside} + (T_{start} - T_{outside})r^{walk time} $$

The constant r is a number between 0 and 1, representing the heat exchange between the coffee cup and the outside environment. This constant depends on several factors, and may be hard to estimate without experimentation. We just chose it somewhat arbitrarily in this first example.

Outside temperature and the rate of cooling:

In [1]:
temp_out = 70.0
r = 0.9

Function representing the cooling law:

In [3]:
def cooling_law(temp_start, walk_time):
    return temp_out + (temp_start - temp_out) * r ** walk_time

For example, to compute the temperature of the coffee after 10 minutes, assuming the initial temperature to be 185°F:

In [9]:
cooling_law(185.0, 10.0)
Out[9]:
110.0980206115

For example, suppose that we want to compute the final temperature every 5 minutes # up to 20 minutes. This is where NumPy makes things easy:

In [10]:
times = arange(0., 21., 5.)
temperatures = cooling_law(185., times)
temperatures
Out[10]:
array([ 185.        ,  137.90635   ,  110.09802061,   93.67748019,
         83.98131528])
In [11]:
plot(times, temperatures, 'o')
Out[11]:
[<matplotlib.lines.Line2D at 0x71d4dd8>]

Should we mix the cream in at the coffee shop or wait until we get back to the office? When we mix the cream, there is a sudden drop in temperature. The temperature of the mixture is the average of the temperature of the two liquids, weighted by volume. The following code defines a function to compute the resulting temperature in a mix:

In [12]:
def temp_mixture(t1, v1, t2, v2):
    return (t1 * v1 + t2 * v2) / (v1 + v2)

1-When the cream is added at the coffee shop:

In [15]:
temp_coffee = 185
temp_cream = 40
vol_coffee = 8
vol_cream = 1

initial_temp_mix_at_shop = temp_mixture(temp_coffee, vol_coffee, temp_cream, vol_cream)

temperatures_mix_at_shop = cooling_law(initial_temp_mix_at_shop, times)

temperatures_mix_at_shop
Out[15]:
array([ 168.        ,  127.86802   ,  104.17048713,   90.17733095,
         81.91451215])

The times array specifies times from 0 to 20 with intervals of 5 minutes.

2-When the cream is mixed after walking back to our office:

In [16]:
temperatures_unmixed_coffee = cooling_law(temp_coffee, times)

temperatures_mix_at_office = temp_mixture(temperatures_unmixed_coffee, vol_coffee, temp_cream, vol_cream)

temperatures_mix_at_office
Out[16]:
array([ 168.88888889,  127.02786667,  102.30935165,   87.71331573,
         79.09450247])
In [17]:
plot(times, temperatures_mix_at_shop, 'o')
plot(times, temperatures_mix_at_office, 'D', color='r')
Out[17]:
[<matplotlib.lines.Line2D at 0x73e3dd8>]

As a conclusion, we can see that, for the data parameters used in this example, mixing the cream at the coffee shop is always better no matter what the walking time is.

In [18]:
plot(times, temperatures_mix_at_shop, 'o')
plot(times, temperatures_mix_at_office, 'D', color='r')
title('Coffee temperatures for different walking times')
xlabel('Waking time (min)')
ylabel('Temperature (F)')
legend(['Mix at shop', 'Mix at office'])
Out[18]:
<matplotlib.legend.Legend at 0x7adc630>

Should we use different strategies during summer and winter? In order to investigate this, we start by defining a function that accepts as input both the cream temperature and outside temperature.

In [19]:
temp_coffee = 185.
vol_coffee = 8.
vol_cream = 1.
walk_time = 10.0
r = 0.9

def temperature_difference(temp_cream, temp_out):
    temp_start = temp_mixture(temp_coffee,vol_coffee,temp_cream,vol_cream)
    temp_mix_at_shop = temp_out + (temp_start - temp_out) * r ** walk_time
    temp_start = temp_coffee
    temp_unmixed = temp_out + (temp_start - temp_out) * r ** walk_time
    temp_mix_at_office = temp_mixture(temp_unmixed, vol_coffee, temp_cream, vol_cream)
    return temp_mix_at_shop - temp_mix_at_office

Compute a matrix with the temperature differences for several different values of the cream temperature and outside temperature:

In [20]:
cream_temperatures = arange(40.,51.,1.)
outside_temperatures = arange(35.,60.,1.)

cream_values, outside_values = meshgrid(cream_temperatures, outside_temperatures)
temperature_differences = temperature_difference(cream_values, outside_values)

Three dimensional plot of the temperature differences:

In [21]:
from mpl_toolkits.mplot3d import Axes3D

fig = figure()
fig.set_size_inches(7,5)

ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(cream_values, outside_values, temperature_differences, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0)

# color map of the plot to cm.coolwarm.
# This color map conveniently uses a blue-red gradient for the function values.

xlabel('Cream temperature')
ylabel('Outside temperature')
title('Temperature difference')
Out[21]:
<matplotlib.text.Text at 0x8185358>

Notice that there seems to be a straight line that defines where the temperature difference transitions from negative to positive. This actually corresponds to values where the outside temperature and the cream temperature are equal. It turns out that if the cream temperature is lower than the outside temperature, we should mix the cream into the coffee at the coffee shop. Otherwise, the cream should be poured in the office.

Generate a contour plot of the temperature differences.

In [38]:
from matplotlib import cm

fig = figure()
fig.set_size_inches(7,5)

cs = plt.contourf(cream_values, outside_values, temperature_differences, cmap=cm.coolwarm)
cbar = plt.colorbar()

xlabel('Cream temperature')
ylabel('Outside temperature')
title('Temperature difference')
Out[38]:
<matplotlib.text.Text at 0x9edef60>

More...

In our example, we discussed how to determine the cooling rate r. Modify the example to plot the temperature evolution for several values of r, keeping all other variables fixed.

Our analysis ignores the fact that the cream will also change temperature as we walk. Change the notebook so that this factor is taken into account.