Project Pythia Logo

National Center for Environmental Prediction (NCEP) Stage IV Precipitation: Spatial Averaging


Overview

Due to its high-resolution grid spacing, Hourly Stage IV Precipitation is a highly beneficial tool for analyzing precipitation observations throughout the contiguous United States. Stage IV data is plotted on a 4 km by 4 km polar-stereographic grid, allowing for identification of discontinuities as a result of the operational process. The data is produced by the National Weather Service (NWS) River Forecast Centers (RFCs) and the National Center for Environmental Predication (NCEP). More information on this dataset can be found at the following link.

This Pythia “cook book” will teach the following concepts:

  1. How to read in NCEP Stage IV Precipitation hourly accumulation data using Xarray from the Zarr archive hosted by Amazon Web Services.

  2. How to subset the Stage IV Precipitation data over a region of interest using Xarray and produce a “quick look” plot using cartopy to view a single timestep of hourly accumulated precipitation.

  3. How to average the hourly precipitation accumulation over the subsetted spatial domain desribed in teaching concept #2 to produce a hourly resolution timeseries over some spatial domain.

Prerequisites

This notebook will utilize functionality of numpy, Xarray, cartopy, and matplotlib. Having some familiarity with each of these packages will allow for a better learning experience. See the table below for links to Pythia Foundations lessons that cover relevant topics.

Concepts

Importance

Notes

Numpy Basics

Necessary

Matplotlib Basics

Necessary

Introduction to Cartopy

Necessary

Introduction to Xarray

Necessary

Understanding of NetCDF

Helpful

Familiarity with metadata structure

  • Time to learn: 30 Minutes


Imports

#entire packages
import fsspec
import numpy as np
import xarray as xr

#cartopy
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER

#matplotlib
import matplotlib.pyplot as plt
import matplotlib.colors as mplc
import matplotlib.patches as mpatches
from mpl_toolkits.axes_grid1 import make_axes_locatable

2: Data Indexing and “Quick Look” Plotting

2.1: Index the Stage IV Precipitation Data in space and time using Xarray

The following cell indexes the hourly accumulated variable that we pulled into Xarray in both the space (lon, lat) and time dimensions. This allows the user to focus on viewing a specific precipitation event for some location and some time range of their choice.

#Define the bounds for our spatial index
left_lon  = -75
right_lon = -70
upper_lat = 42
lower_lat = 39

#Define a boolean statement that we will use to index the precipitation data in space
#Note that the numbers used for this index statement are degress of longitude and latidude
space_index = (da_hourly_precip.lon >= left_lon) & (da_hourly_precip.lon <= right_lon) & (da_hourly_precip.lat >= lower_lat) & (da_hourly_precip.lat <= upper_lat)

#First index the data in space
da_hourly_precip_space_indexed = da_hourly_precip.where(space_index.compute(), drop=True)

#Then index the data in time based on some start and end times that we define below

#Define start and end time string (in UTC timezone)
start_time_str = '2021-09-01 09:00'
end_time_str   = '2021-09-02 15:00'

#Now do the time index. It seems the times are not sorted by default, so sort them before indexing.
da_hourly_precip_space_and_time_indexed = da_hourly_precip_space_indexed.sortby('time').sel(time=slice(start_time_str, end_time_str))

2.2: Produce a plot of the indexed precipitation data on a cartopy map

The following cell sets up a cartopy figure that is projection aware. It then plots a random timestep from our spatially and temporally subset 1-hour precipitation accumulation data on the cartopy figure. The colorbar is set to linearaly scale to the min and max values of the timestep that we are plotting using the numpy “linspace” function. In addition, we apply a mask to the precipitation timestep that we are plotting so values below an arbitrarily small value (in this case 0.01 mm) so that colors are not plotted where precipitation values are equal to zero.

#Define plot CRS
plot_crs = ccrs.PlateCarree()
data_crs = ccrs.PlateCarree()

#Define lat/lon extend, ticks, and number of ticks we want on the map
lon_lat_extent   = [left_lon, right_lon, lower_lat, upper_lat]
lon_lat_ticks    = [left_lon, right_lon, lower_lat, upper_lat]
lon_lat_tick_num = [5, 5]

#Define a random time index integer for plotting a "quick look"
time_index = 20

#Define plotting variable
plotting_variable = da_hourly_precip_space_and_time_indexed.isel(time=time_index)

#Define cartopy axis
fig, ax = plt.subplots(subplot_kw={'projection': plot_crs}, figsize=(10,5))

#Add map features
ax.add_feature(cfeature.NaturalEarthFeature(category='physical', name='coastline', scale='10m', facecolor='None', zorder=10))
ax.add_feature(cfeature.NaturalEarthFeature(category='physical', name='minor_islands', scale='10m', facecolor='None', zorder=10))
ax.add_feature(cfeature.NaturalEarthFeature(category='cultural', name='admin_1_states_provinces', scale='10m', facecolor='None', zorder=10))

#Set extent of map
ax.set_extent((lon_lat_extent[0], lon_lat_extent[1] , lon_lat_extent[2], lon_lat_extent[3]), crs=plot_crs)

#Define longitude and latitude ticks using "linspace"
map_xticks = np.linspace(lon_lat_ticks[0], lon_lat_ticks[1], num=lon_lat_tick_num[0], endpoint=True).round(decimals=2) 
map_yticks = np.linspace(lon_lat_ticks[2], lon_lat_ticks[3], num=lon_lat_tick_num[1], endpoint=True).round(decimals=2)

#Setting tick locations for lon/lat (degrees; projection = plot_crs)
ax.set_xticks(map_xticks, crs=plot_crs)
ax.set_yticks(map_yticks, crs=plot_crs)

#Setting tick locations and labels for lon/lat (degrees; projection = plot_crs)
ax.set_xticks(map_xticks, crs=plot_crs)
ax.set_yticks(map_yticks, crs=plot_crs)
ax.set_xticklabels(map_xticks)
ax.set_yticklabels(map_yticks)

#Add formatter to lon/lat ticks (degree symbol & direction label)
ax.xaxis.set_major_formatter(LONGITUDE_FORMATTER)
ax.yaxis.set_major_formatter(LATITUDE_FORMATTER)

#Turn on grid and set tick paramters for grid
ax.grid(True)
ax.tick_params(axis='both', which='major', pad=8, length=8, grid_linewidth=0.5, grid_linestyle='--', grid_color='black')

#Set title for this "quick look" plot
ax.set_title(f'1-Hour Precipitation Accumulation for {str(plotting_variable.time.values)[0:10]} {str(plotting_variable.time.values)[12:16]} UTC')

#Define colorbar levels, colorbar colormap, and colorbar norms for plotting
level_min  = plotting_variable.min()
level_max  = plotting_variable.max()
levels     = np.linspace(level_min, level_max, 30)
cmap       = plt.get_cmap('inferno_r').copy()
norm       = mplc.BoundaryNorm(levels, ncolors=cmap.N, clip=False)

#Plot pcolormesh. Note that in order to not plot precipitation values equal to zero, we mask values below some arbitrarily low threshold.
pcolormesh = ax.pcolormesh(plotting_variable.lon, plotting_variable.lat, np.ma.masked_less(plotting_variable.transpose(), 0.01),
                           cmap=cmap, norm=norm, shading='nearest', alpha=1, transform=data_crs)
#Create colorbar for values
divider = make_axes_locatable(ax)
cax     = divider.append_axes("right", size="2%", pad=0.3, axes_class=plt.Axes)
cbar    = plt.colorbar(pcolormesh, cax=cax, orientation='vertical', spacing='uniform', ticks=list(levels[::3]), drawedges=True)
cbar.set_label('Hourly Precipitation Accumulation (mm)', color='black', labelpad=20)
/home/runner/miniconda3/envs/cookbook-dev/lib/python3.11/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/10m_physical/ne_10m_minor_islands.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/runner/miniconda3/envs/cookbook-dev/lib/python3.11/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/10m_cultural/ne_10m_admin_1_states_provinces.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
../_images/9167b0141083c12585ed4e22ca5086c56c7a266b12375199498b81a29c82c983.png
left_lon_box  = -73.75
right_lon_box = -72.50
upper_lat_box = 41.00
lower_lat_box = 40.00

#Add rectangle around zoomed region
#https://scitools.org.uk/cartopy/docs/v0.5/matplotlib/introductory_examples/02.polygon.html
ax.add_patch(mpatches.Rectangle(xy=[left_lon_box, lower_lat_box], 
                                width=abs((left_lon_box - right_lon_box)), 
                                height=abs((lower_lat_box-upper_lat_box)), 
                                edgecolor='black', linewidth=2, fill=False, alpha=1, zorder=4, transform=plot_crs))

fig
../_images/d2c01f8aa37c3140502feb457238f0aa5bc2a2a9be96be3349e057c1745e9f2c.png

Subsection to the second section

a quick demonstration

of further and further
header levels

as well \(m = a * t / h\) text! Similarly, you have access to other \(\LaTeX\) equation functionality via MathJax (demo below from link),

(2)\[\begin{align} \dot{x} & = \sigma(y-x) \\ \dot{y} & = \rho x - y - xz \\ \dot{z} & = -\beta z + xy \end{align}\]

Check out any number of helpful Markdown resources for further customizing your notebooks and the Jupyter docs for Jupyter-specific formatting information. Don’t hesitate to ask questions if you have problems getting it to look just right.

Last Section

If you’re comfortable, and as we briefly used for our embedded logo up top, you can embed raw html into Jupyter Markdown cells (edit to see):

Info

Your relevant information here!

Feel free to copy this around and edit or play around with yourself. Some other admonitions you can put in:

Success

We got this done after all!

Warning

Be careful!

Danger

Scary stuff be here.

We also suggest checking out Jupyter Book’s brief demonstration on adding cell tags to your cells in Jupyter Notebook, Lab, or manually. Using these cell tags can allow you to customize how your code content is displayed and even demonstrate errors without altogether crashing our loyal army of machines!


Summary

Add one final --- marking the end of your body of content, and then conclude with a brief single paragraph summarizing at a high level the key pieces that were learned and how they tied to your objectives. Look to reiterate what the most important takeaways were.

What’s next?

Let Jupyter book tie this to the next (sequential) piece of content that people could move on to down below and in the sidebar. However, if this page uniquely enables your reader to tackle other nonsequential concepts throughout this book, or even external content, link to it here!

Resources and references

Finally, be rigorous in your citations and references as necessary. Give credit where credit is due. Also, feel free to link to relevant external material, further reading, documentation, etc. Then you’re done! Give yourself a quick review, a high five, and send us a pull request. A few final notes:

  • Kernel > Restart Kernel and Run All Cells... to confirm that your notebook will cleanly run from start to finish

  • Kernel > Restart Kernel and Clear All Outputs... before committing your notebook, our machines will do the heavy lifting

  • Take credit! Provide author contact information if you’d like; if so, consider adding information here at the bottom of your notebook

  • Give credit! Attribute appropriate authorship for referenced code, information, images, etc.

  • Only include what you’re legally allowed: no copyright infringement or plagiarism

Thank you for your contribution!