👨‍🍳Beginner’s SounderPy Cookbook

A collection of simple SounderPy recipies for accessing and analyzing vertical profile data.


  • Recipies in this cookbook:

    1. Integrated data sources & how to load them

    2. Plotting soundings

    3. Plotting hodographs

    4. Plotting composite soundings

  • Time to learn: 15 minutes

(C) Kyle J Gillett, University of North Dakota, 2024


Before we get started…

Definitions

  • Cookbook: a collection of ‘recipies’

  • Recipe: a set of steps that create a programmatic solution to a problem

  • Tool: SounderPy slang for a Python ‘function’ that completes a task; where a ‘function’ is the programmatic term for a block of reusable code that completes an operation

  • Library: a collection of pre-written, packaged code that users can install and use to perform specific tasks

  • Workspace: the medium by which we are writing code; in this case, a Jupyter Notebook

  • Environment: the set of rules, libraries, and packages, such as the code interpreter, that runs your code workspace

  • Argument: information that is passed into function

  • Alias: a naming shortcut to refer to a code library when its imported

Additional Resources

Pre-requisites

SounderPy must be installed in your environment in order to import and access it’s tools. This can be done easily with pip, conda, or mamba. Details are here: https://kylejgillett.github.io/sounderpy/about.html#installation

What is SounderPy?

SounderPy is an open-source atmospheric science Python library for vertical profile analysis. This library and its tools are designed to get data, ‘clean it up’ for simple use, and plot the data on advanced-sounding plots. SounderPy was developed with the goal in mind to keep the code simple and efficient for users of all experience levels and for reliability in all use cases.


Set Up

  • To use SounderPy and it’s tools, we need to import the SounderPy library and its tools into our workspace. Import it and use spy as a shorthand ‘alias’

[1]:
import sounderpy as spy

## ---------------------------------- SOUNDERPY ----------------------------------- ##
##          Vertical Profile Data Retrieval and Analysis Tool For Python            ##
##                      v3.0.6 | Dec 2024 | (C) Kyle J Gillett                      ##
##                 Docs: https://kylejgillett.github.io/sounderpy/                  ##
## --------------------- THANK YOU FOR USING THIS PACKAGE! ------------------------ ##



RECIPE 1: SounderPy’s integrated data sources & how to load them

Objectives

  1. Learn what data is already integrated into SounderPy

  2. Learn how to load integrated data types for analysis

Integrated Data Sources

  • Observations

    1. Radiosonde Observations (RAOB) from the University of Wyoming | (1970s-present)

    2. Radiosonde Observations (RAOB) from the Integrated Global Radiosonde Archive v2 | (1905-present)

    3. ACARS aircraft observations from the University of Oklahoma | (01-2019 to 06-2024)

  • Model Reanalysis

    1. RAP CONUS model reanalysis from NCEI | (2020-present)

    2. RUC CONUS model reanalysis from NCEI | (2005-2020)

    3. NCEP-FNL global model reanalysis from NCEI | (2011-present)

    4. ECMWF ERA5 global model reanalysis | (1940-present)

  • Model Forecasts

    1. BUFKIT model forecasts from Penn State University and Iowa State University | (2011-present)

    • HRRR, RAP, NAM, NAMNEST, GFS, SREF, HIRESW

  • SounderPy can handle custom data sources too! See the documentation for more details: https://kylejgillett.github.io/sounderpy/customdatasources.html

Loading data

Loading data in SounderPy is very simple. You need to know 4 things:

  • What data you want to investigate

  • The tool you need to use to do so

  • A date & time to investigate

  • A location to “pluck” the sounding from

  • SounderPy Data Access Tools

    The basic tools for accessing data are included below. ACARS data is slightly more complicated and is not included here. See: https://kylejgillett.github.io/sounderpy/acars_data_example.html

    • Observations: .get_obs_data('station', 'year', 'month', 'day', 'hour')

    • Model Reanalysis: .get_model_data('model', [lat, lon], 'year', 'month', 'day', 'hour')

    • BUFKIT Forecast Data: .get_bufkit_data('model', 'station', forecast_hr, 'run_year', 'run_month', 'run_day', 'run_hour')

Load Observation Data

[2]:
obs_data = spy.get_obs_data('OUN', '2024', '05', '07', '00')
> OBSERVED DATA ACCESS FUNCTION
  -----------------------------------
    > PROFILE FOUND: OUN on 05/07/2024 at 00z | From UW
    > COMPLETE --------
    > RUNTIME: 00:00:01
    > SUMMARY: 00Z Launch for KOUN, NORMAN at 05-07-2024-00Z

    > THERMODYNAMICS ---------------------------------------------
    --- SBCAPE: 3919.1 | MUCAPE: 3919.1 | MLCAPE: 3790.9 | MUECAPE: 3198.4
    --- MU 0-3: 127.4 | MU 0-6: 931.9 | SB 0-3: 127.4 | SB 0-6: 931.9

    > KINEMATICS -------------------------------------------------
    --- 0-500 SRW: 33.2 knot | 0-500 SWV: 0.028 | 0-500 SHEAR: 26.1 | 0-500 SRH: 225.0
    --- 1-3km SRW: 25.1 knot | 1-3km SWV: 0.004 | 1-3km SHEAR: 15.3 | | 1-3km SRH: 44.6
    ==============================================================

Load Reanalysis Data

[3]:
reanl_data = spy.get_model_data('rap', [35.22, -97.44], '2024', '05', '07', '00')
> RAP REANALYSIS DATA ACCESS FUNCTION
  -----------------------------------------
    > DATASET USED: RAP_25km_anl
    > COMPLETE --------
    > RUNTIME: 00:00:34
    > SUMMARY: 00Z RAP F00 for[35.22, -97.44] at 05-07-2024-00Z

    > THERMODYNAMICS ---------------------------------------------
    --- SBCAPE: 4030.2 | MUCAPE: 4045.7 | MLCAPE: 4036.5 | MUECAPE: 3287.5
    --- MU 0-3: 148.6 | MU 0-6: 1039.6 | SB 0-3: 147.0 | SB 0-6: 1035.2

    > KINEMATICS -------------------------------------------------
    --- 0-500 SRW: 32.7 knot | 0-500 SWV: 0.026 | 0-500 SHEAR: 23.4 | 0-500 SRH: 203.6
    --- 1-3km SRW: 22.9 knot | 1-3km SWV: 0.004 | 1-3km SHEAR: 21.2 | | 1-3km SRH: 69.2
    ==============================================================

Load BUFKIT forecast model data

[4]:
bufkit_data = spy.get_bufkit_data('hrrr', 'OUN', 0, '2024', '05', '07', '00')
> BUFKIT DATA ACCESS FUNCTION
   ---------------------------------
    > COMPLETE --------
    > RUNTIME: 00:00:00
    > SUMMARY: SUMMARY: 00Z HRRR F00 for OUN,NORMAN at 05-07-2024-00Z

    > THERMODYNAMICS ---------------------------------------------
    --- SBCAPE: 4308.0 | MUCAPE: 4308.0 | MLCAPE: 3868.4 | MUECAPE: 3492.4
    --- MU 0-3: 174.7 | MU 0-6: 1104.6 | SB 0-3: 174.7 | SB 0-6: 1104.6

    > KINEMATICS -------------------------------------------------
    --- 0-500 SRW: 31.9 knot | 0-500 SWV: 0.025 | 0-500 SHEAR: 24.2 | 0-500 SRH: 209.2
    --- 1-3km SRW: 24.2 knot | 1-3km SWV: 0.005 | 1-3km SHEAR: 25.6 | | 1-3km SRH: 97.8
    ==============================================================

Investigate the SounderPy ‘clean_data’ dictionary

  • Our data objects (obs_data, reanl_data, bufkit_data) are called “clean_data dictionaries” in SounderPy slang. These are python dictionaries that consist of the vertical profile data that the plotting tools require to build a plot.

  • Each data object will contain an array of pressure, height, temperature, dewpoint, u-wind, v-wind and omega (if its model data). Additionally, the dictionary will hold two more “nested” dictionaries of meta data and pre-configured plot titles for the plotting tools.

[5]:
obs_data
[5]:
{'p': <Quantity([960. 958. 925. 880. 867. 856. 850. 816. 813. 800. 769. 759. 747. 745.
  719. 700. 689. 686. 672. 651. 573. 567. 558. 553. 551. 528. 526. 521.
  518. 505. 502. 500. 495. 484. 483. 480. 459. 431. 424. 411. 400. 380.
  368. 335. 304. 300. 291. 282. 264. 250. 249. 224. 209. 204. 200. 196.
  188. 186. 180. 178. 177. 175. 173. 166. 158. 156. 151. 150. 145. 136.
  135. 134. 128. 122. 115. 113. 111. 110. 109. 105. 102. 100.], 'hectopascal')>,
 'z': <Quantity([  345.   363.   672.  1106.  1234.  1345.  1405.  1754.  1786.  1923.
   2257.  2367.  2501.  2524.  2822.  3046.  3178.  3214.  3384.  3645.
   4671.  4756.  4882.  4953.  4982.  5317.  5346.  5421.  5466.  5663.
   5709.  5740.  5817.  5990.  6006.  6054.  6394.  6866.  6989.  7220.
   7420.  7789.  8019.  8694.  9368.  9460.  9667.  9878. 10320. 10680.
  10706. 11399. 11842. 11995. 12120. 12246. 12505. 12571. 12774. 12843.
  12877. 12947. 13018. 13275. 13582. 13662. 13868. 13910. 14126. 14531.
  14578. 14625. 14910. 15210. 15578. 15686. 15797. 15852. 15909. 16140.
  16318. 16440.], 'meter')>,
 'T': <Quantity([ 25.6  25.4  23.   19.4  18.6  18.   17.8  15.2  15.8  14.   12.6  12.
   12.9  13.   11.4  10.    9.2   8.6   7.4   5.6  -3.   -3.7  -4.5  -3.9
   -3.9  -6.8  -7.1  -7.9  -7.9  -9.5  -9.7  -9.9 -10.3 -11.7 -11.7 -11.5
  -14.5 -18.5 -19.5 -20.3 -22.1 -25.2 -27.2 -32.9 -38.2 -38.9 -40.7 -42.
  -44.7 -47.9 -48.1 -53.9 -58.1 -58.9 -59.5 -60.3 -60.7 -60.7 -61.9 -62.3
  -62.5 -62.1 -61.7 -60.5 -59.1 -58.7 -54.5 -54.9 -56.9 -57.4 -57.5 -57.7
  -59.3 -61.  -63.1 -62.5 -62.  -61.7 -62.  -63.3 -62.9 -62.7], 'degree_Celsius')>,
 'Td': <Quantity([ 21.3  21.2  20.2  19.   18.5  18.   17.8  14.9  15.8  13.1  12.1  10.9
    7.6   7.    6.4   2.   -0.8  -0.4  -7.9 -19.4 -17.8 -17.7 -24.5 -37.9
  -33.9 -28.6 -28.1 -25.9 -17.9 -17.5 -20.7 -20.9 -20.3 -24.7 -24.7 -28.5
  -30.5 -35.3 -36.5 -50.3 -51.1 -53.1 -54.3 -57.9 -60.5 -60.9 -60.7 -62.
  -64.7 -64.9 -65.  -66.9 -66.1 -68.  -69.5 -68.3 -65.5 -65.7 -68.4 -69.3
  -70.5 -75.1 -75.7 -78.1 -81.  -81.7 -82.5 -82.9 -82.9 -84.3 -84.5 -84.7
  -85.4 -86.2 -87.1 -86.9 -86.8 -86.7 -86.8 -87.3 -87.5 -87.7], 'degree_Celsius')>,
 'u': <Quantity([-3.76222158 -1.04586891 -5.55674169  6.88311646 12.16449512 11.12921894
  10.61158085 14.33471798 14.98426374 15.86272908 19.57144685 20.69628333
  21.22232814 21.22232814 22.2566091  22.5        24.         24.5
  26.5        29.41050789 42.42640687 41.72170842 40.95580729 40.22445359
  39.49309989 36.77013327 37.53617771 39.63444403 40.97655919 46.92298567
  48.32997061 49.14912266 49.14912266 48.54101966 48.54101966 49.35003666
  48.06865597 47.49475547 50.96807065 57.91542407 62.95940559 64.83879083
  60.85332706 67.6578687  73.41093075 77.99448753 83.69297343 89.83110184
  82.10369523 75.34221445 75.34221445 86.66308227 93.48505816 95.63469102
  91.64991222 85.79050832 72.95553037 68.95796706 59.         58.56022295
  58.10365743 60.86088537 63.57959405 73.4103628  58.88972746 58.27454113
  56.01482559 55.44186463 53.55306633 49.24038765 48.09973199 45.97293723
  36.64801221 36.37306696 41.36192563 41.84017732 35.73925557 36.16017033
  35.50704156 45.8993464  54.55960044 52.56585165], 'knot')>,
 'v': <Quantity([1.03366188e+01 1.19543364e+01 3.15138481e+01 4.34582870e+01
  4.53985138e+01 4.15348105e+01 3.96029589e+01 3.73432171e+01
  3.70873542e+01 3.56282728e+01 3.25723574e+01 3.18694816e+01
  3.03086256e+01 3.03086256e+01 3.56180200e+01 3.89711432e+01
  4.15692194e+01 4.24352448e+01 4.58993464e+01 4.52882107e+01
  4.24264069e+01 4.02901855e+01 3.81919082e+01 3.75099098e+01
  3.68279114e+01 3.08538053e+01 3.14965929e+01 3.20953399e+01
  3.20143967e+01 3.40915446e+01 3.38410097e+01 3.44145862e+01
  3.44145862e+01 3.52671151e+01 3.52671151e+01 3.58549004e+01
  3.75553500e+01 3.98528318e+01 3.70304709e+01 2.95093825e+01
  2.29153496e+01 2.35993899e+01 1.63055998e+01 2.46254503e+01
  3.42320792e+01 2.83876719e+01 2.71934955e+01 2.40701712e+01
  2.19996188e+01 2.01878855e+01 2.01878855e+01 1.52810396e+01
  9.82567555e+00 8.36695130e+00 8.01832833e+00 5.99905674e+00
  2.54766326e+00 2.40806527e+00 1.08381242e-14 7.19029126e+00
  1.02452425e+01 1.18301577e+01 1.35142599e+01 1.96702474e+01
  3.40000000e+01 3.09851231e+01 2.15020770e+01 2.01791885e+01
  1.63728155e+01 8.68240888e+00 9.34964077e+00 9.77184947e+00
  1.33387856e+01 2.10000000e+01 7.29322346e+00 3.66054120e+00
  9.57630467e+00 1.46096571e+01 2.05000000e+01 2.65000000e+01
  3.15000000e+01 2.45118592e+01], 'knot')>,
 'site_info': {'site-id': 'KOUN',
  'site-name': 'NORMAN',
  'site-lctn': 'OK US',
  'site-latlon': [35.23, -97.47],
  'site-elv': np.float64(362.0),
  'source': 'RAOB OBSERVED PROFILE',
  'model': 'no-model',
  'fcst-hour': 'no-fcst-hour',
  'run-time': ['none', 'none', 'none', 'none'],
  'valid-time': ['2024', '05', '07', '00']},
 'titles': {'top_title': 'RAOB OBSERVED VERTICAL PROFILE',
  'left_title': 'VALID: 05-07-2024 00Z',
  'right_title': 'KOUN - NORMAN, OK US | 35.23, -97.47    '}}

Great! Now we have 3 different data types loaded into our workspace. The code above shows the most basic uses of the data access tools. Some tools, especially .get_model_data() have additional settings. See the documentation for details and examples.



RECIPE 2: Plotting soundings

Objectives

  1. Learn how to build sounding figures with the data we loaded above

  2. Learn how to customize sounding figures using a few basic settings

Building Soundings with SounderPy

This, of course, is SounderPy’s bread and butter. SounderPy offers advanced, easy-to-read, and easy-to-build full sounding figures.

The full sounding plot that SounderPy creates is a complex figure with unique design geared towards severe convective storm enviroment analysis.

  • To accomplish this task, we use the .build_sounding(clean_data) tool.

    • where clean_data is the data object we want to plot.

    • additional settings will be discussed below

  • You may notice that is takes a moment to build this plot. Thats due to some more complex processes taking place during plotting, but below we will introduce some setting to simplify the process and speed this up

Build a plot, with default settings, of our observations data

[6]:
spy.build_sounding(obs_data)
> SOUNDING PLOTTER FUNCTION
  ---------------------------------
- no radar data available -
    > COMPLETE --------
    > RUNTIME: 00:00:14
_images/beginners_sounderpy_cookbook_16_1.png

Its as easy as that! Making the plot above can be done with just two lines of code!!

Customizing sounding figures

  • The SounderPy full-sounding figure has a number of customization options that put the user in control, below we will investigate a few of the more basic ones that, like an extra pinch of salt, can really just add to the overall recipe.

  • SounderPy’s documentation has a complete discussion on what you can do to these figures: https://kylejgillett.github.io/sounderpy/plottingdata.html#building-soundings

  • In this recipe, we will focus on dark_mode, color_blind, map_zoom, and we’ll touch on the basics of special_parcels

    • These “settings” are more formally called “key-word-arguments” (or kwargs) in programming. These kwargs tell the tool how to behave.

Setting #1: special_parcels

  • Lets start with special_parcels. By default, this kwarg is set to True, meaning the “setting is set to on”. This is most of what’s causing the plot building to be a little slow.

  • This setting allows users to plot special parcel paths using Pseudoadiabatic or Irreversible Adiabatic, and Entraining or non-entraining parcels. Its a complex process and a little slow. We wont get too much into the weeds here on what all this means. You can read more here: https://kylejgillett.github.io/sounderpy/plottingdata.html#parcel-logic

  • For now, we can set this kwarg to 'simple' which will only process and plot basic MU/ML/SB parcel paths.

Build a sounding figure with special_parcels='simple' using our reanalysis data object

[7]:
spy.build_sounding(reanl_data, special_parcels='simple')
> SOUNDING PLOTTER FUNCTION
  ---------------------------------
- no radar data available -
    > COMPLETE --------
    > RUNTIME: 00:00:02
_images/beginners_sounderpy_cookbook_19_1.png

Much faster!

Setting #2: map_zoom

  • Now we can check out map_zoom, which allows users to change how ‘zoomed out’ or in the map is, or turn it off all together. This kwarg takes a number called a “zoom factor”, in SounderPy slang. By default the zoom factor is = 2. A higher number reveals more of the map (zooms out). If map_zoom=0, the map will be hidden and the code will run even faster.

Build a sounding figure with a zoomed-out map using our BUFKIT forecast data object

[8]:
spy.build_sounding(bufkit_data, special_parcels='simple', map_zoom=5)
> SOUNDING PLOTTER FUNCTION
  ---------------------------------
- no radar data available -
    > COMPLETE --------
    > RUNTIME: 00:00:02
_images/beginners_sounderpy_cookbook_21_1.png

Neat!

Setting #3: color_blind

  • Convention for Skew-T plots has always been a red temperature trace and a green dewpoint trace – this is not an ideal way to plot two side-by-side variables for colorblind or color deficient users and readers. As such, SounderPy offers the color_blind setting which changes the dewpoint trace color to blue.

Setting #4: dark_mode

  • Its 2024, everyone loves dark-mode everything. So, SounderPy offers a setting that sets the entire figure to ‘dark-mode’.

Build a dark-mode sounding with colorblind settings on using our observations again

[9]:
spy.build_sounding(obs_data, special_parcels='simple', map_zoom=5, color_blind=True, dark_mode=True)
> SOUNDING PLOTTER FUNCTION
  ---------------------------------
- no radar data available -
    > COMPLETE --------
    > RUNTIME: 00:00:02
_images/beginners_sounderpy_cookbook_23_1.png


RECIPE 3: Plotting hodographs

Objectives

  1. Learn how to build and customize hodograph figures

  • To accomplish this task, we use the .build_hodograph('clean_data') tool.

    • where clean_data is the data object we want to plot.

  • Settings for hodograph figures also includes dark_mode and sr_hodo

Build a hodograph plot using our reanalysis data

[10]:
spy.build_hodograph(reanl_data)
> HODOGRAPH PLOTTER FUNCTION --
-------------------------------
> RUNTIME: 00:00:02
_images/beginners_sounderpy_cookbook_25_1.png

Cool!

Hodograph settings: dark_mode & sr_hodo

  • The dark_mode kwarg works the same for the hodograph figure as it does for the sounding figure.

  • sr_hodo allows the user to “reorient the hodograph” into a “storm motion perspective”. This will set the storm motion at the origin of the hodograph. A storm relative hodograph…

“normalizes all of the seemingly-infinite hodograph shapes into what the supercell actually experiences”

Cameron J Nixon.

Build a storm-relative dark-mode hodograph figure using our BUFKIT forecast data

[11]:
spy.build_hodograph(bufkit_data, dark_mode=True, sr_hodo=True)
> HODOGRAPH PLOTTER FUNCTION --
-------------------------------
> RUNTIME: 00:00:01
_images/beginners_sounderpy_cookbook_27_1.png


RECIPE 4: Plotting sounding composites

Objectives

  1. Learn how to build and customize composite sounding figures using a few basic settings

Motivation: There are generally 2 downsides to sounding data analysis:

  1. Soundings represent a single time and point in space.

  2. Soundings can be very hard to compare to one another (in time, or in reference to the past, analogs, etc), and in meteorology, context is key.

These issues can be alleviated using SounderPy’s .build_composite(data_list) tool.

  • where data_list is a Python list of SounderPy data objects.

Some custom settings include…

  • colors_to_use: a list of color names, matching the size of data_list OR cmap: the colormap used to plot profiles. The colors of each profile.

  • lw_to_use: a list of linewidths, matching the size of data_list. The linewidth of ech profile.

  • ls_to_use: a list of matplotlib line styles, matching the size of data_list. The line style for each profile.

  • alphas_to_use: a list of alphas, matching the size of ‘data_list`. The alpha of each profile.

  • shade_between: whether to shade between the dewpoint and temperature traces

  • dark_mode

Build a dark mode composite sounding with custom colors to compare our observation data, reanalysis model data, and forecast model data, all valid for the same time & location

[12]:
# list of data objects
data_list = [obs_data, reanl_data, bufkit_data]

# colors
colors_to_use = ['yellow', 'cornflowerblue', 'orangered']

# linewidths
# here a list-comprehension trick!
# sets all linewidths to 4
lw_to_use = [4 for data in data_list]

# linestyles
ls_to_use = [':', '--', '-']

# alphas
alphas_to_use = [1 for data in data_list]

spy.build_composite(data_list, colors_to_use=colors_to_use, lw_to_use=lw_to_use,
                    ls_to_use=ls_to_use, alphas_to_use=alphas_to_use, dark_mode=True)

> COMPOSITE SOUNDING FUNCTION
  -------------------------------
> COMPLETE --------
> RUNTIME: 00:00:01
_images/beginners_sounderpy_cookbook_29_1.png

Congrats! You finished this Beginners SounderPy Cookbook!

Want to learn more and do more with SounderPy? Check out the documentation and examples incuded here: https://kylejgillett.github.io/sounderpy/index.html