Using Iris to access NCEP CFSR 30-year Wave Hindcast

This demonstrates extracting data from a large aggregated archive via OPeNDAP, taking advantage of CF conventions to enable a plot without specification of lon,lat variables, and save extracted data to GRIB2


In [1]:
from IPython.core.display import HTML
HTML('<iframe src=http://scitools.org.uk/iris/ width=800 height=350></iframe>')


Out[1]:

In [2]:
import numpy
import matplotlib.pyplot as plt
import datetime as dt

import iris
import iris.plot as iplt
import cartopy.crs as ccrs

In [3]:
def time_near(cube,start):
    timevar=cube.coord('time')
    itime = timevar.nearest_neighbour_index(timevar.units.date2num(start))
    return timevar.points[itime]

In [4]:
def myplot(slice,model=None):
    # make the plot
    figure(figsize=(12,8))
    lat=slice.coord(axis='Y').points
    lon=slice.coord(axis='X').points
    time=slice.coord('time')[0]
    subplot(111,aspect=(1.0/cos(mean(lat)*pi/180.0)))
    pcolormesh(lon,lat,masked_invalid(slice.data));
    colorbar()
    grid()
    date_str=time.units.num2date(time.points)
    plt.title('%s: %s: %s' % (model,slice.long_name,date_str));

Extract some data using OPeNDAP


In [5]:
# DAP URL: 30 year East Coast wave hindcast (Wave Watch 3 driven by CFSR Winds) 
#cubes = iris.load('http://geoport.whoi.edu/thredds/dodsC/fmrc/NCEP/ww3/cfsr/4m/best'); # 4 arc minute resolution
#cubes = iris.load('http://geoport.whoi.edu/thredds/dodsC/fmrc/NCEP/ww3/cfsr/10m/best'); # 10 arc minute resolution
cubes = iris.load('/usgs/data2/notebook/data/colpex.pp')


/home/local/python27_epd/lib/python2.7/site-packages/Iris-1.5.0_dev-py2.7.egg/iris/unit.py:835: UserWarning: Unable to create instance of HybridHeightFactory. The file(s) ['/usgs/data2/notebook/data/colpex.pp'] don't contain field(s) for 'orography'.
  self._init(category, ut_unit, calendar_, unit)

In [6]:
print cubes


0: air_potential_temperature / (K)     (time: 6; model_level_number: 10; grid_latitude: 83; grid_longitude: 83)
1: air_pressure / (Pa)                 (time: 6; model_level_number: 10; grid_latitude: 83; grid_longitude: 83)

In [10]:
temp=cubes[0]

In [8]:
# use contraints to select geographic subset and nearest time
mytime=dt.datetime(1991,10,31,12)  #specified time...   Nov 1, 1991 was the "Perfect Storm"
#mytime=dt.datetime.utcnow()      # .... or now
slice=hsig.extract(iris.Constraint(time=time_near(hsig,mytime),
    longitude=lambda cell: -71.5 < cell < -64.0,
    latitude=lambda cell: 40.0 < cell < 46.0))

In [8]:
def var_lev_date(url=None,var=None,mytime=None,lev=0,subsample=1):
    time0=time.time()
    cubes = iris.load(url)
    cube = cubes.extract(iris.Constraint(name=var))[0]
    try:
        cube.coord(axis='T').rename('time')
    except:
        pass
    slice = cube.extract(iris.Constraint(time=time_near(cube,mytime)))
    slice = slice[lev,::subsample,::subsample]  
    print 'slice retrieved in %f seconds' % (time.time()-time0)
    return slice

In [9]:
def myplot(slice,model=None):
    # make the plot
    figure(figsize=(12,8))
    lat=slice.coord(axis='Y').points
    lon=slice.coord(axis='X').points
    time=slice.coord('time')[0]
    subplot(111,aspect=(1.0/cos(mean(lat)*pi/180.0)))
    pcolormesh(lon,lat,ma.masked_invalid(slice.data));
    colorbar()
    grid()
    date=time.units.num2date(time.points)
    date_str=date[0].strftime('%Y-%m-%d %H:%M:%S %Z')
    plt.title('%s: %s: %s' % (model,slice.long_name,date_str));

In [11]:
print temp


air_potential_temperature / (K)     (time: 6; model_level_number: 10; grid_latitude: 83; grid_longitude: 83)
     Dimension coordinates:
          time                           x                      -                  -                   -
          model_level_number             -                      x                  -                   -
          grid_latitude                  -                      -                  x                   -
          grid_longitude                 -                      -                  -                   x
     Auxiliary coordinates:
          forecast_period                x                      -                  -                   -
          level_height                   -                      x                  -                   -
          sigma                          -                      x                  -                   -
     Scalar coordinates:
          forecast_reference_time: 2009-09-09 22:00:00
     Attributes:
          STASH: m01s00i004
          source: Data from Met Office Unified Model 7.04

In [10]:
print[coord.name() for coord in slice.coords()]


['latitude', 'longitude', u'time']

Plot using Iris.plot

There is also Iris.quickplot, but I wanted to add my own title here and control the orientation of the colorbar, so I used the more flexible Iris.plot


In [11]:
figure(figsize=(12,8))

# set the projection
ax1 = plt.axes(projection=ccrs.Mercator())

# color filled contour plot
h = iplt.contourf(slice,64)

# add coastlines, colorbar and title
plt.gca().coastlines(resolution='10m')
colorbar(h,orientation='vertical');
title('WaveWatch 3 significant wave height driven by CFSR winds');


Save extracted Cube to GRIB2 file


In [12]:
# first add a Geographic Coord system  (required by GRIB2 writer)

# here we add a spherical earth with specified radius
slice.coord(axis='X').coord_system=iris.coord_systems.GeogCS(654321)
slice.coord(axis='Y').coord_system=iris.coord_systems.GeogCS(654321)

In [13]:
# add a forecast_period (required by GRIB2 writer)
slice.add_aux_coord(iris.coords.DimCoord(0, standard_name='forecast_period', units='hours'))

In [14]:
# add a vertical coordinate (required by GRIB2 writer)
slice.add_aux_coord(iris.coords.DimCoord(0, "height", units="m"))

In [15]:
# GRIB2 stores data in long values.  Do we have huge values that would create problems?
slice.data.max()


Out[15]:
nan

In [16]:
# ah, we have NaNs.  This causes problems for GRIB2 writer
# So convert to masked-array instead.
slice.data=ma.masked_invalid(slice.data)

In [17]:
# Finally... save slice as grib2
iris.save(slice,'hsig.grib2')


/home/local/python27_epd/lib/python2.7/site-packages/Iris-1.4.0_dev-py2.7.egg/iris/fileformats/grib_save_rules.py:186: UserWarning: Not yet translating standard name into grib param codes.
discipline, parameterCategory and parameterNumber have been zeroed.
  warnings.warn("Not yet translating standard name into grib param codes.\n"