When will Arctic be ice free during summer?

One can look at the sea ice extent (first part of this notebook) or the sea ice volume (second part of the notebook).

The least amount of ice during the year happens in September, so we plot the sea ice extent (in square km) between 1979 - 2014 in September

Original data with detailed description is available at: http://nsidc.org/data/docs/noaa/g02135_seaice_index/ We are following the section 3., subsection "Monthly Sea Ice Extent Anomaly Graphs".


In [1]:
%pylab inline


Populating the interactive namespace from numpy and matplotlib

In [2]:
import urllib2
from pandas import read_table, DataFrame
from IPython.display import Image
from scipy.signal import argrelextrema
from scipy.optimize import curve_fit

def running_average(temp, n=6):
    avg = empty(size(temp), dtype=temp.dtype)
    for i in range(n, size(temp)-n):
        avg[i] = average(temp[i-n:i+n+1])
    avg[:n] = NaN
    avg[-n:] = NaN
    return avg

In [3]:
open("data/N_09_area.txt", "w").write(urllib2.urlopen("ftp://sidads.colorado.edu/DATASETS/NOAA/G02135/Sep/N_09_area.txt").read())

In [4]:
data = read_table("data/N_09_area.txt", skipfooter=17, sep="\s+")
data


Out[4]:
year mo data_type region extent area
0 1979 9 Goddard N 7.19 4.53
1 1980 9 Goddard N 7.83 4.82
2 1981 9 Goddard N 7.24 4.37
3 1982 9 Goddard N 7.44 4.37
4 1983 9 Goddard N 7.51 4.63
5 1984 9 Goddard N 7.10 4.03
6 1985 9 Goddard N 6.91 4.17
7 1986 9 Goddard N 7.53 4.66
8 1987 9 Goddard N 7.47 5.59
9 1988 9 Goddard N 7.48 5.31
10 1989 9 Goddard N 7.03 4.81
11 1990 9 Goddard N 6.23 4.49
12 1991 9 Goddard N 6.54 4.45
13 1992 9 Goddard N 7.54 5.36
14 1993 9 Goddard N 6.50 4.52
15 1994 9 Goddard N 7.18 5.08
16 1995 9 Goddard N 6.12 4.38
17 1996 9 Goddard N 7.87 5.58
18 1997 9 Goddard N 6.73 4.84
19 1998 9 Goddard N 6.55 4.23
20 1999 9 Goddard N 6.23 4.22
21 2000 9 Goddard N 6.31 4.30
22 2001 9 Goddard N 6.74 4.54
23 2002 9 Goddard N 5.95 3.98
24 2003 9 Goddard N 6.13 4.00
25 2004 9 Goddard N 6.04 4.35
26 2005 9 Goddard N 5.56 4.03
27 2006 9 Goddard N 5.91 3.96
28 2007 9 Goddard N 4.29 2.78
29 2008 9 Goddard N 4.72 3.21
30 2009 9 Goddard N 5.38 3.71
31 2010 9 Goddard N 4.92 3.28
32 2011 9 Goddard N 4.61 3.17
33 2012 9 Goddard N 3.62 2.36
34 2013 9 Goddard N 5.35 3.74
35 2014 9 Goddard N 5.28 3.70

36 rows × 6 columns

Let's plot the extent directly for September together with a linear fit from 1996-2014. I chose the year 1996 because there was a local maximum in the ice extent that year. That way we get as large slope as possible, i.e. ice free summer as soon as possible, assuming linear relationship (which might not hold).


In [5]:
years = data.year.values
yvals = data.extent.values
Y0 = years[0]
def f(x, a, b):
    return a*(x-Y0)+b
(a, b), pcov = curve_fit(f, years[17:], yvals[17:])
adev = sqrt(pcov[0, 0])
bdev = sqrt(pcov[1, 1])
print "par              dev"
print a, adev
print b, bdev


par              dev
-0.148192982459 0.0240566109214
9.54722807019 0.639200042916

In [6]:
figure(figsize=(9, 6))
years2 = linspace(1978, 2050, 2)
plot(years2, a*(years2-Y0)+b-1.2, "r-", lw=1)
plot(years, yvals, "kd-", lw=2, label="ice extent")
plot(years2, a*(years2-Y0)+b, "b-", lw=2, label="average")
xlim([1978, 2050])
ylim([0, 9])
grid()
title("Average Monthly Arctic Sea Ice Extent\nSeptember 1979-2014")
ylabel("Extent (million square kilometers)")
xlabel("Year")
legend()
show()


Years at which the lines cross the $x$-axis:


In [7]:
print "blue line:", -b/a + Y0
print "red line:", -(b-1.2)/a + Y0


blue line: 2043.42429265
red line: 2035.32674322

The average (blue line) points to 2043. This is only the ice extent, which is different from the volume (that depends on the thickness), see below for a volume graph, which seems to have a faster slope. The relationship might be non linear, the fit is not a great one (the volume graph below seems to provide a much better model for the fit). Also, the first ice free summer will happen when there is a local minimum, so one should really fit the local minima instead (red line), which points to 2035.


In [8]:
data = read_table("data/PIOMAS.vol.daily.1979.2015.Current.v2.1.dat", sep="\s+")
data


Out[8]:
Year #day Vol
0 1979 1 26.405
1 1979 2 26.496
2 1979 3 26.582
3 1979 4 26.672
4 1979 5 26.770
5 1979 6 26.867
6 1979 7 26.940
7 1979 8 27.013
8 1979 9 27.095
9 1979 10 27.185
10 1979 11 27.286
11 1979 12 27.381
12 1979 13 27.463
13 1979 14 27.552
14 1979 15 27.649
15 1979 16 27.736
16 1979 17 27.818
17 1979 18 27.903
18 1979 19 27.987
19 1979 20 28.080
20 1979 21 28.182
21 1979 22 28.297
22 1979 23 28.398
23 1979 24 28.496
24 1979 25 28.595
25 1979 26 28.682
26 1979 27 28.751
27 1979 28 28.828
28 1979 29 28.921
29 1979 30 29.009
30 1979 31 29.091
31 1979 32 29.190
32 1979 33 29.248
33 1979 34 29.324
34 1979 35 29.418
35 1979 36 29.505
36 1979 37 29.594
37 1979 38 29.671
38 1979 39 29.744
39 1979 40 29.822
40 1979 41 29.911
41 1979 42 29.980
42 1979 43 30.070
43 1979 44 30.162
44 1979 45 30.255
45 1979 46 30.320
46 1979 47 30.364
47 1979 48 30.405
48 1979 49 30.452
49 1979 50 30.512
50 1979 51 30.576
51 1979 52 30.642
52 1979 53 30.718
53 1979 54 30.794
54 1979 55 30.875
55 1979 56 30.950
56 1979 57 31.023
57 1979 58 31.103
58 1979 59 31.177
59 1979 60 31.249
... ... ...

13230 rows × 3 columns


In [9]:
years = data.Year.values
days = data["#day"].values
yvals = data.Vol.values
time = years + (days - 1) / 365.

In [10]:
# Find local minima (smooth the function first, because it is noisy)
idx = argrelextrema(running_average(yvals, n=14), less)

In [11]:
Y0 = years[0]
def f(x, a, b):
    return a*(x-Y0)+b
(a, b), pcov = curve_fit(f, time[idx], yvals[idx])
adev = sqrt(pcov[0, 0])
bdev = sqrt(pcov[1, 1])
print "par              dev"
print a, adev
print b, bdev


par              dev
-0.321229688481 0.023868917945
17.2401126052 0.500268016092

In [12]:
figure(figsize=(9, 6))
years2 = linspace(1978, 2050, 2)
plot(years2, a*(years2-Y0)+b-4, "g-", lw=1)
plot(years2, a*(years2-Y0)+b-2.7, "r-", lw=1)
plot(years2, a*(years2-Y0)+b+2.5, "g-", lw=1)
plot(time, yvals, "k-", lw=1, label="ice volume")
plot(time, running_average(yvals, n=365*2), "c-", lw=2, label="ice volume (5 year central average)")
plot(time[idx], yvals[idx], "bo", label="yearly minima")
plot(years2, a*(years2-Y0)+b, "b-", lw=2, label="average of minima")
xlim([1978, 2050])
ylim([0, 35])
grid()
title("Average Daily Arctic Sea Ice Volume\n 1979-2015")
ylabel("Volume [$10^3 \mathrm{km}^3$]")
xlabel("Year")
legend()
show()


Years at which the lines cross the $x$-axis:


In [13]:
print "blue line:", -b/a + Y0
print "red line:", -(b-2.7)/a + Y0
print "green lines:", -(b-4)/a + Y0, -(b+2.5)/a + Y0


blue line: 2032.66911348
red line: 2024.26391279
green lines: 2020.21696431 2040.45170672

The blue line is the average, green lines are the envelope, and the red line is chosen so that it captures the few recent minima (i.e. all the minima that are the biggest anomalies, years 1982, 2010, 2011, 2012, except the 1981 minimum).

The envelope (green lines) points to years 2020 - 2040. The average (blue line) points to 2032. The red line (i.e. if there are some anomalies like in the years 2010-2012) points to 2024.