Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Chapter 3: July 2025 Central TX Floods

Authors
Affiliations
City College of New York and NOAA/OAR National Severe Storms Laboratory
NSF National Center for Atmospheric Research
University at Albany (State University of New York)
Metropolitan State University of Denver
Jackson State University
Argonne National Laboratory

Overview

This notebook walks through how to access, visualize, and animate low-level composite reflectivity data from the Multi-Radar/Multi-Sensor (MRMS) system.

The case study focuses on the Central Texas flood event in July 2025, using reflectivity data hosted on AWS. The main steps include:

  • Selecting and downloading MRMS data for specific timestamps

  • Creating a static reflectivity map

  • Building an animation to show reflectivity changes over time

This notebook is intended for students, forecasters, or researchers looking to explore radar visualization techniques or build familiarity with remote sensing workflows using Python.

What is MRMS?

The Multi-Radar/Multi-Sensor (MRMS) system is a set of real-time analysis products developed by NOAA’s National Severe Storms Laboratory (NSSL). It brings together data from:

  • Dozens of NEXRAD radars

  • Surface observations

  • Satellites

  • Lightning detection networks

to create high-resolution snapshots of precipitation, severe weather, and related hazards.

MRMS updates every 2.5 minutes and is commonly used in operational forecasting, hydrology, aviation, and research.


Goal of This Notebook

The goal of this notebook is to walk through a simple, practical workflow for visualizing radar reflectivity data using Python. Specifically, we’ll:

  • Access MRMS Layer Composite Reflectivity Low data from AWS Open Data

  • Plot a single reflectivity frame as a static map

  • Animate a 6-frame sequence from July 4, 2025, during the Central Texas flood event

  • Demonstrate how to work with gridded radar data using open-source tools like MetPy, Cartopy, and xarray

Imports

below are the python packages that are used for this code

Define Timestamps and Colormap

To build the animation, we’ll use 6 hourly frames of MRMS data from the morning of July 4, 2025. Each timestamp matches a GRIB2 file available from the AWS MRMS archive.

We also define the standard NWS reflectivity colormap using MetPy, which gives us consistent color breaks every 5 dBZ which is a common setup for radar reflectivity plots.

Access and Load MRMS Data

MRMS data is stored as .grib2.gz files on the AWS S3 public data bucket. Each file represents a single timestamp and product type.

In this step:

  • We use urllib.request.urlopen() to download the compressed file directly from AWS

  • We decompress it using Python’s built-in gzip module

  • Then we load the GRIB2 file into an xarray.DataArray using the cfgrib engine

This approach lets us work with the data directly in Python without having to manually download or unzip anything ahead of time.

ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored

Set Up Reflectivity Colormap and Extract Data

This section gets the MRMS reflectivity data ready for plotting and builds a map to visualize it.

  • Colormap and Normalization:
    We use MetPy’s built-in NWSReflectivity colormap, which is designed for radar data in dBZ. The get_with_steps() function sets up color breaks every 5 dBZ — a common setup in operational radar displays.

  • Extract Coordinates and Data:
    We pull out the longitude, latitude, and reflectivity values from the data array. If the coordinates are in 1D (which happens in some MRMS products), we convert them to 2D using np.meshgrid() so they work with the plotting function.

  • Mask Low Reflectivity Values:
    Reflectivity values below 5 dBZ are masked out with ma.masked_where() to remove light noise and clutter from the map.

  • Set Up the Map:
    We create a static figure using matplotlib and Cartopy, with a PlateCarree projection centered over Texas. The domain is narrowed with set_extent() to focus on the region of interest.

  • Add Map Features:
    Coastlines, country borders, and U.S. state lines are added to give the plot geographic context.

  • Plot the Reflectivity:
    The reflectivity field is plotted using pcolormesh() with our defined colormap and normalization. A horizontal colorbar is added to show the dBZ scale.

  • Final Touches:
    We include a plot title and display the final figure with plt.show().

/home/runner/micromamba/envs/mrms-cookbook-dev/lib/python3.12/site-packages/cartopy/io/__init__.py:263: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/10m_physical/ne_10m_coastline.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/runner/micromamba/envs/mrms-cookbook-dev/lib/python3.12/site-packages/cartopy/io/__init__.py:263: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/10m_cultural/ne_10m_admin_0_boundary_lines_land.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/runner/micromamba/envs/mrms-cookbook-dev/lib/python3.12/site-packages/cartopy/io/__init__.py:263: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/10m_cultural/ne_10m_admin_1_states_provinces_lakes.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
<Figure size 1000x800 with 2 Axes>

Select Timestamps and Animate Reflectivity

This part of the notebook automates the process of pulling in multiple MRMS reflectivity files and creating an animation to show how low-level reflectivity changed over time.

  • Check for Available Timestamps:
    We define a time range from July 4 to July 7, 2025, and loop through it in 30-minute steps. For each time, we generate a file path from the AWS-hosted MRMS archive and try downloading it. If the file exists, we save that timestamp. For this demo, we stop after grabbing six valid files.

  • Set Up the Map and Colormap:
    After collecting the timestamps, we build a static map using Cartopy (Plate Carree projection), focused on Texas and surrounding areas. We also apply the MetPy NWSReflectivity colormap and mask out any reflectivity values below 5 dBZ to clean up the visualization.

  • Download and Plot Each Frame:
    For each timestamp:

    • The corresponding .grib2.gz file is downloaded and decompressed.

    • We extract the reflectivity data and coordinates using xarray.

    • If the coordinate arrays are 1D, we convert them to 2D for plotting.

    • The reflectivity data is plotted with pcolormesh(), and we add a dynamic title showing the UTC time.

    • Each frame (plot + title) is saved for the animation.

  • Build the Animation:
    We use ArtistAnimation from Matplotlib to stitch the frames together into an animation. plt.close(fig) is used beforehand to prevent Jupyter from displaying a static image under the animation.

  • Export as a GIF:
    The finished animation is saved as a .gif using Pillow so it can be easily shared or embedded in a presentation.

The result is a short radar loop showing how reflectivity evolved during the early hours of July 4, 2025, which is a period tied to widespread heavy rain and flash flooding across Central Texas.

Checking for available MRMS files...

 Found: 20250704-001040
 Missing: 20250704-004040
 Found: 20250704-011040
 Missing: 20250704-014040
 Missing: 20250704-021040
 Missing: 20250704-024040
 Found: 20250704-031040
 Missing: 20250704-034040
 Missing: 20250704-041040
 Missing: 20250704-044040
 Missing: 20250704-051040
 Found: 20250704-054040
 Missing: 20250704-061040
 Missing: 20250704-064040
 Found: 20250704-071040
 Missing: 20250704-074040
 Missing: 20250704-081040
 Missing: 20250704-084040
 Found: 20250704-091040

 Selected 6 timestamps:
20250704-001040
20250704-011040
20250704-031040
20250704-054040
20250704-071040
20250704-091040
Loading 20250704-001040...
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
Loading 20250704-011040...
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
Loading 20250704-031040...
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
Loading 20250704-054040...
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
Loading 20250704-071040...
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
Loading 20250704-091040...
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
ECCODES ERROR   :  Key dataTime (unpack_long): Truncating time: non-zero seconds(40) ignored
Loading...
Animation saved as 'mrms_reflectivity_animation.gif'

Reflectivity Animation: Summary

We demonstrated how to access and animate low-level composite reflectivity data from the MRMS system using open-source Python tools. We focused on a short sequence from the July 4, 2025, Central Texas flood event to highlight how reflectivity features evolved.

This workflow is a flexible starting point for working with radar data, especially for case studies or quick visual diagnostics. The next section will continue building on this analysis with more approaches to explore the MRMS dataset!

Comparison with ASOS Data

Loading...
/home/runner/micromamba/envs/mrms-cookbook-dev/lib/python3.12/site-packages/cartopy/io/__init__.py:263: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/10m_cultural/ne_10m_admin_2_counties.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
<Figure size 1000x800 with 2 Axes>
<Figure size 1000x800 with 3 Axes>

Compare MRMS Radar-Only to Pass 1 and Pass 2 QPE

/tmp/ipykernel_4400/3048795121.py:81: UserWarning: This figure includes Axes that are not compatible with tight_layout, so results might be incorrect.
  plt.tight_layout(rect=[0, 0.25, 1, 0.98])  # leave space for suptitle and colorbar
<Figure size 1500x600 with 4 Axes>
<Figure size 1200x600 with 3 Axes>