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.

Titan Pre-Processing Example


This interactive tutorial takes you through the steps of how to preprocess data in order to run the Thunderstorm Identification, Tracking, Analysis and Nowcasting (Titan) suite. The key preprocessing steps for Titan include: 1) removing noise and non-meteorological echoes (and correcting any biases), and 2) converting from polar to Cartesian grids in the NCAR MDV format. This notebook provides a few examples using LROSE applications, but feel free to use your favorite package to pre-process the data.

Titan was originally designed as an algorithm to objectively identify and track thunderstorms from weather radar data for a weather modification experiment in South Africa in the 1980s. Now, Titan includes forecasting, storm analysis, and climatological analysis. Titan now refers to the larger system in which the original application is one component.

Titan is described in more detail in Dixon and Wiener (1993).


Titan Background

Titan identifies storm objects as a contiguous region of echo that exceeds a user-defined reflectivity threshold and minimum volume. Dual thresholds are used to deal with storm objects that briefly touch, but do not merge. Storm tracking is performed by looking for regions of overlap between storm objects at successive time intervals. Short term storm extrapolation forecasts are used to identify instances of storm merging and splitting. Titan output includes storm tracks, polygons outlining the storm objects, and storm property information (e.g., volume, area, mass, precipitation flux).

The high-level workflow for Titan is shown in the graphic below. Key steps include quality controlling the data to remove any non-meteorological or compromised echoes and gridding the data to a Cartesian grid. Once Titan is run and the tracks are produced, those data need to be converted into more user-friendly file types.

A more detailed workflow for Titan that includes each step, application, and data type is shown in the graphic below.

Tutorial Overview

1. Setup

Download raw data and prepare parameter files

Raw data files that are provided:

It is a 10 cm (S-band) Gematronik dual polarization radars.

The data (as a .tgz file) has been provided in the form of a zipped tar file, which we will unzip create the following tree:

  ./data/titan/ERA5/20220521
  ./data/titan/radar/raw/derecho/20220521*.h5

2. Output data

After the full analysis has been run, the following derived data directories should exist:

  ./data/titan/ERA5/spdb/KingCity/20220521* (soundings from ERA5)
  ./data/titan/radar/cfradial/qc/KingCity/20220521/cfrad.20220521*nc (cfradial after QC)
  ./data/titan/radar/cart/qc/KingCity/20220521/ncf_202205216*nc (Cartesian MDC CF-compliant netcdf)

3. Note on task cells

This notebook uses two colored cells to indicate tasks.

File Task: modify parameters in text files.

These text blocks help the user modify the parameter files or other functions in external text files.

Cell Task: run a command in Jupyter notebook cell.

These text blocks instruct the users to run a command in a cell within the Jupyter notebook. If you prefer, you are welcome to copy the commands (minus the ! symbol) into a terminal window.


1. Setup

Environment and packages

First, we import the required python packages to run this notebook. Most of the LROSE processing can be done with the os package and shell commands.

import os
import warnings

warnings.filterwarnings("ignore")

1.1 Set up directories

We need to set up the required data directories. The raw radar data will be grabbed from the S3 bucket.

# make overall titan directory and application output directory
!mkdir -p ../data/titan/titan

# make directory for output ascii files from Titan
!mkdir -p ../data/titan/titan/ascii

1.2 Set up the environment

First, we’ll set some key variables we’ll need throughout the workflow.

# Set directory variable to call LROSE
os.environ["LROSE_DIR"] = "/usr/local/lrose/bin"
os.environ["DATA_DIR"] = "../data/titan"

1.3 Get data

Because some of the preprocessing requires ancillary data, we need to grab and untar that data.

import fsspec
import shutil

# Remote endpoint and path
URL = "https://js2.jetstream-cloud.org:8001/"
path = "pythia/radar/ams2025"

# Initialize filesystem
fs = fsspec.filesystem("s3", anon=True, client_kwargs=dict(endpoint_url=URL))

# Get list of files (example: Ontario Derecho)
files = fs.glob(f"{path}/OntarioDerecho2022/2022052115*.h5")

# Local base directory where you want to store files
local_base = "../data/titan/raw/derecho"

for remote_file in files:
    # Construct local file path, preserving the relative directory structure
    rel_path = os.path.relpath(remote_file, start=path)  
    local_file = os.path.join(local_base, rel_path)

    # Ensure parent directories exist
    os.makedirs(os.path.dirname(local_file), exist_ok=True)

    # Open remote and local files and copy
    with fs.open(remote_file, "rb") as fsrc, open(local_file, "wb") as fdst:
        shutil.copyfileobj(fsrc, fdst)

    print(f"Downloaded {remote_file} -> {local_file}")
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_00_ODIMH5_PVOL6S_VOL_CASET.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_00_ODIMH5_PVOL6S_VOL_CASET.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_00_ODIMH5_PVOL6S_VOL_CASKR.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_00_ODIMH5_PVOL6S_VOL_CASKR.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_06_ODIMH5_PVOL6S_VOL_CASET.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_06_ODIMH5_PVOL6S_VOL_CASET.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_06_ODIMH5_PVOL6S_VOL_CASKR.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_06_ODIMH5_PVOL6S_VOL_CASKR.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_12_ODIMH5_PVOL6S_VOL_CASET.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_12_ODIMH5_PVOL6S_VOL_CASET.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_12_ODIMH5_PVOL6S_VOL_CASKR.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_12_ODIMH5_PVOL6S_VOL_CASKR.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_18_ODIMH5_PVOL6S_VOL_CASET.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_18_ODIMH5_PVOL6S_VOL_CASET.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_18_ODIMH5_PVOL6S_VOL_CASKR.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_18_ODIMH5_PVOL6S_VOL_CASKR.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_24_ODIMH5_PVOL6S_VOL_CASET.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_24_ODIMH5_PVOL6S_VOL_CASET.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_24_ODIMH5_PVOL6S_VOL_CASKR.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_24_ODIMH5_PVOL6S_VOL_CASKR.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_30_ODIMH5_PVOL6S_VOL_CASET.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_30_ODIMH5_PVOL6S_VOL_CASET.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_30_ODIMH5_PVOL6S_VOL_CASKR.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_30_ODIMH5_PVOL6S_VOL_CASKR.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_36_ODIMH5_PVOL6S_VOL_CASET.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_36_ODIMH5_PVOL6S_VOL_CASET.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_36_ODIMH5_PVOL6S_VOL_CASKR.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_36_ODIMH5_PVOL6S_VOL_CASKR.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_42_ODIMH5_PVOL6S_VOL_CASET.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_42_ODIMH5_PVOL6S_VOL_CASET.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_42_ODIMH5_PVOL6S_VOL_CASKR.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_42_ODIMH5_PVOL6S_VOL_CASKR.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_48_ODIMH5_PVOL6S_VOL_CASET.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_48_ODIMH5_PVOL6S_VOL_CASET.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_48_ODIMH5_PVOL6S_VOL_CASKR.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_48_ODIMH5_PVOL6S_VOL_CASKR.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_54_ODIMH5_PVOL6S_VOL_CASET.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_54_ODIMH5_PVOL6S_VOL_CASET.h5
Downloaded pythia/radar/ams2025/OntarioDerecho2022/2022052115_54_ODIMH5_PVOL6S_VOL_CASKR.h5 -> ../data/titan/raw/derecho/OntarioDerecho2022/2022052115_54_ODIMH5_PVOL6S_VOL_CASKR.h5

2. Prepare data for analysis

The following sections describe two quality control setups and how to run the scripts.

2.1: Option 1 - Apply quality control (QC) on the raw radar data and convert to CfRadial format using RadxConvert

In the derecho case, considerable interference is present, appearing as radial spikes.

Closer inspection of these spikes shows that the interference sources are not coherent with the radars, as indicated by:

To address this, we use RadxConvert to censor data fields based on thresholds applied to the input fields. Specifically, data are removed at gates where both conditions are met:

Since later QC steps require signal-to-noise ratio (SNR), the SNR field is derived from reflectivity (DBZ) and added during processing.

Finally, the raw HDF5 files are converted to CfRadial format using RadxConvert with this simple quality control applied.

Cell Task: Run simple rules-based QC on derecho case data.

Run the derecho case RadxConvert QC script:

!$LROSE_DIR/RadxConvert -sort_rays_by_time -const_ngates -params ../params/titan/RadxConvert.qc.derecho -debug -f ../data/titan/raw/derecho/202205211*CASKR.h5
# Run QC on derecho case data
!$LROSE_DIR/RadxConvert -sort_rays_by_time -const_ngates -params ../params/titan/RadxConvert.qc.derecho -f ../data/titan/raw/derecho/OntarioDerecho2022/202205211*CASKR.h5
/usr/local/lrose/bin/RadxConvert: /srv/conda/envs/notebook/lib/libcurl.so.4: no version information available (required by /lib/x86_64-linux-gnu/libhdf5_serial.so.103)
======================================================================
Program 'RadxConvert'
Run-time 2026/08/22 07:09:40.

Copyright (c) 1992 - 2026
University Corporation for Atmospheric Research (UCAR)
National Center for Atmospheric Research (NCAR)
Boulder, Colorado, USA.

Redistribution and use in source and binary forms, with
or without modification, are permitted provided that the following
conditions are met:

1) Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2) Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3) Neither the name of UCAR, NCAR nor the names of its contributors, if
any, may be used to endorse or promote products derived from this
software without specific prior written permission.

4) If the software is modified to produce derivative works, such modified
software should be clearly marked, so as not to confuse it with the
version available from UCAR.

======================================================================
Cannot stat file for parsing
../params/titan/RadxConvert.qc.derecho: No such file or directory
ERROR: RadxConvert
Problem with TDRP parameters.
Error: Could not create RadxConvert object.

This simple QC removes some of the bad data, as shown by the screenshots below from the derecho case.

2.2: Option 2 - Computing PID as an alternative method of censoring using RadxPid

An alternative method for cleaning up interference is to run RadxPid, and censor non-meteorological echoes.

First, we have to quality control the data without censoring during the format conversion step.

Cell Task: Run RadxConvert again without the simple QC.

Run RadxConvert without censoring data:

!$LROSE_DIR/RadxConvert -sort_rays_by_time -const_ngates -params ../params/titan/RadxConvert.no_qc.derecho -f ../data/titan/raw/derecho/OntarioDerecho2022/202205211*CASKR.h5

Next, we will use the ERA5 reanalysis to extract model-based soundings:

Cell Task: Extract ERA5 sounding from preprocessed Spdb sounding data.

Run the Mdv2SoundingSpdb to extract the ERA5 sounding:

!$LROSE_DIR/Mdv2SoundingSpdb -debug -params ../params/titan/Mdv2SoundingSpdb.ERA5.derecho -f ../data/titan/ERA5/20220521/20220521_*

And we can then run RadxPid:

Cell Task: Run RadxPid to censor gates identified as not meteorological.

Run RadxPid to censor non-meteorological data:

!$LROSE_DIR/RadxPid -params ../params/titan/RadxPid.derecho -debug

The following shows the PID field for the derecho case:

The interference is identified as clutter in this case.

And the following images show the raw data and after using PID to clean up the reflectivity field:

2.3: Convert to Cartesian grid using Radx2Grid

Titan requires input data on a Cartesian grid, instead of the native polar grid. To perform this transformation, we run Radx2Grid to convert the data to Cartesian grid MDV format.

Cell Task: Convert the QC'd derecho case data to a Cartesian grid.

Run Radx2Grid to interpolate the data to a Cartesian grid:

!$LROSE_DIR/Radx2Grid -params ../params/titan/Radx2Grid.derecho

3: Now you’re ready to run Titan!

Use the Titan_tutorial notebook to learn more about setting up the parameter file, running Titan, and

References
  1. Dixon, M., & Wiener, G. (1993). TITAN: Thunderstorm Identification, Tracking, Analysis, and Nowcasting—A Radar-based Methodology. Journal of Atmospheric and Oceanic Technology, 10(6), 785–797. https://doi.org/10.1175/1520-0426(1993)010<;0785:ttitaa>2.0.co;2