On creating Time-Height Cross Section Plot with Py-ART and C-band Radar Files

Greetings!

I’m relatively new to using Py-ART and am currently working with several dual-pol C-band radar files from 00 to 12 UTC, in a universal format (uf). My goal is to create a time-height cross section plot to visualize the radar data over time, but I’m struggling to find documentation or an example that combines multiple files and allows for this type of plot.

I’ve explored the Py-ART documentation, but I can’t seem to find a straightforward example for this kind of task. Any guidance on how I can combine these radar files and generate a time-height cross section would be greatly appreciated. Additionally, if anyone has a sample Python notebook for this type of plot, that would be incredibly helpful!

import numpy as np
import xarray as xr
import pyart

def column_profile(radar: pyart.core.Radar,
                   latitude: float = 33.8,
                   longitude: float = -88.3,
                   azimuth_spread: float = 3,
                   spatial_spread: float = 3,
                   v_res: float = 100,
                   min_alt: float = None,
                   max_alt: float = None) -> xr.Dataset:
    """
    Computes a vertical column profile from radar data and interpolates it
    onto a uniform height grid.

    Parameters
    ----------
    radar : pyart.core.Radar
        Py-ART radar object containing the volume scan data.
    latitude : float, optional
        Latitude of the point of interest, by default 33.8.
    longitude : float, optional
        Longitude of the point of interest, by default -88.3.
    azimuth_spread : float, optional
        Azimuthal spread in degrees to consider around the point of interest, by default 3.
    spatial_spread : float, optional
        Horizontal spatial spread in kilometers for averaging, by default 3.
    v_res : float, optional
        Vertical resolution in meters, by default 100.
    min_alt : float
        Minimum altitude (height) in meters for the vertical profile.
    max_alt : float
        Maximum altitude (height) in meters for the vertical profile.

    Returns
    -------
    xr.Dataset
        Interpolated columnar vertical profile on a uniform height grid.
    """

    if min_alt is None or max_alt is None:
        raise ValueError("Both min_alt and max_alt must be specified.")

    # Compute the column profile using Py-ART
    col_prof = pyart.util.column_vertical_profile(
        radar, latitude=latitude, longitude=longitude,
        azimuth_spread=azimuth_spread, spatial_spread=spatial_spread
    )

    # Define new height levels for interpolation
    new_heights = xr.Dataset(
        coords={'height': ('height', np.arange(min_alt, max_alt + v_res, v_res))}
    )

    # Interpolate the column profile onto the uniform height grid
    col_prof_interp = col_prof.interp_like(new_heights)

    return col_prof_interp


def rain_rate_mp(dbz, a=200.0, b=1.6):
    """Calculates the inverse of input decibel values
    Convert to rain rate using Marshall Palmer Relation
    """
    Z = 10.0 ** (dbz / 10.0)
    return  (Z / a) ** (1.0 / b)



def rain_rate_categorized(dbz, conv_threshold=40.0, a_conv=300.0, b_conv=1.4,
                          a_strat=200.0, b_strat=1.6):
    """
    Calculates rain rate by categorizing reflectivity into convective and stratiform regions,
    then applying different coefficients for each category using the Power-law relation.

    Parameters:
    dbz (xarray.DataArray): Input reflectivity values in dBZ.
    conv_threshold (float): Threshold dBZ value for convective classification. Default is 40.0 dBZ.
    a_conv (float): Coefficient 'a' for convective rain. Default is 300.0.
    b_conv (float): Exponent 'b' for convective rain. Default is 1.4.
    a_strat (float): Coefficient 'a' for stratiform rain. Default is 200.0.
    b_strat (float): Exponent 'b' for stratiform rain. Default is 1.6.

    Returns:
    xarray.DataArray: Rain rate in mm/h, with the same dimensions and coordinates as the input dbz.
    """
    # Convert dBZ to reflectivity factor Z
    Z = 10.0 ** (dbz / 10.0)
    
    # Classify into convective and stratiform regions
    is_conv = dbz >= conv_threshold
    
    # Initialize rain rate array
    rr = xr.zeros_like(Z)
    
    # Calculate convective rain rate
    rr = xr.where(is_conv, (Z / a_conv) ** (1.0 / b_conv), rr)
    
    # Calculate stratiform rain rate
    rr = xr.where(~is_conv, (Z / a_strat) ** (1.0 / b_strat), rr)
    
    # Preserve metadata and coordinates
    rr.attrs['standard_name'] = "Rain rate"
    rr.name = "rain_rate"
    rr.attrs["units"] = "mm/h"
    rr.attrs["description"] = "Rain rate calculated using categorized reflectivity"
    
    return rr

import glob
files = sorted(glob.glob("path/to/your/data*.nc"))
print(len(files))

rain_rate_list = []
for file in files[-5:]:
    radar = pyart.io.read(file)
    col_prof_interp = column_profile(radar,
                                     latitude=33.5, longitude=-88.3,
                                     azimuth_spread=3, spatial_spread=3,
                                     v_res=100, min_alt=200, max_alt=8e3)

    rain_rate = rain_rate_categorized(col_prof_interp['REF'])
    rain_rate['time'] = col_prof_interp['base_time']
    rain_rate_list.append(rain_rate)
    
da_rain_rate = xr.concat(rain_rate_list, dim='time')

da_rain_rate.plot.contourf(x='time', levels=range(0, 55, 5))

import os
os.makedirs("~/Downloads/output_qpe_folder", exist_ok=True)
da_rain_rate.to_netcdf("~/Downloads/output_qpe_folder/rain_rates_vert.nc")
2 Likes

This is a great example! Any interest in contributing this to the Py-ART gallery?

@mgrover1 Let’s do it if you say so. Wait for my PR.

1 Like

Thank you for submitting the contribution! I just merged it in :rocket:

1 Like

You can see the example figures here

https://arm-doe.github.io/pyart/examples/retrieve/plot_qpe.html#sphx-glr-examples-retrieve-plot-qpe-py

2 Likes