From 851155753d559789a4c4f4bfff7bcc03988de75b Mon Sep 17 00:00:00 2001 From: "J.R. Leeman" Date: Sun, 24 Jul 2016 19:18:36 -0400 Subject: [PATCH] Upper air tutorial --- .gitignore | 1 + .landscape.yml | 1 + MANIFEST.in | 2 + docs/conf.py | 4 +- docs/index.rst | 1 + setup.cfg | 1 + tutorials/README.txt | 7 ++ tutorials/upperair_soundings.py | 211 ++++++++++++++++++++++++++++++++ 8 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 tutorials/README.txt create mode 100644 tutorials/upperair_soundings.py diff --git a/.gitignore b/.gitignore index 73b041cf43d..40b80317615 100644 --- a/.gitignore +++ b/.gitignore @@ -18,5 +18,6 @@ MANIFEST docs/build/ docs/examples +docs/tutorials docs/api/generated examples/scripts diff --git a/.landscape.yml b/.landscape.yml index c4ba5d90af1..67c60995cbd 100644 --- a/.landscape.yml +++ b/.landscape.yml @@ -4,6 +4,7 @@ ignore-paths: - versioneer.py - metpy/_version.py - examples + - tutorials max-line-length: 95 diff --git a/MANIFEST.in b/MANIFEST.in index a9d6ed68193..e28abb9c4b6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,9 +4,11 @@ include CONTRIBUTING.md include versioneer.py include metpy/_version.py recursive-include examples * +recursive-include tutorials * recursive-include docs * prune docs/build prune docs/examples +prune docs/tutorials prune docs/modules prune docs/source/__pycache__ exclude .ipynb_checkpoints/* diff --git a/docs/conf.py b/docs/conf.py index 1d259073bc7..4aeb470ff92 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,8 +49,8 @@ 'metpy': None, 'matplotlib': 'http://matplotlib.org', 'numpy': 'http://docs.scipy.org/doc/numpy-1.9.1'}, - 'examples_dirs': '../examples', - 'gallery_dirs': 'examples', + 'examples_dirs': ['../examples', '../tutorials'], + 'gallery_dirs': ['examples', 'tutorials'], 'filename_pattern': '/', 'mod_example_dir': 'api/generated' } diff --git a/docs/index.rst b/docs/index.rst index 889fb2bd3cd..a0195542608 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -35,6 +35,7 @@ Documentation installguide units examples/index + tutorials/index api/index developerguide diff --git a/setup.cfg b/setup.cfg index 6a9bcd23705..74a795693e4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,6 +11,7 @@ copyright-author = MetPy Developers norecursedirs = build docs flake8-ignore = *.py F405 examples/*.py D T003 + tutorials/*.py D T003 versioneer.py ALL flake8-max-line-length = 95 diff --git a/tutorials/README.txt b/tutorials/README.txt new file mode 100644 index 00000000000..810dc58ec4c --- /dev/null +++ b/tutorials/README.txt @@ -0,0 +1,7 @@ +.. _tutorials-index: + +MetPy Tutorials +=============== + +This collection of tutorials (under development) demonstrates the use of MetPy to perform +common meteorological tasks. diff --git a/tutorials/upperair_soundings.py b/tutorials/upperair_soundings.py new file mode 100644 index 00000000000..61612ef9833 --- /dev/null +++ b/tutorials/upperair_soundings.py @@ -0,0 +1,211 @@ +# Copyright (c) 2008-2016 MetPy Developers. +# Distributed under the terms of the BSD 3-Clause License. +# SPDX-License-Identifier: BSD-3-Clause +""" +=========================== +Upper Air Sounding Tutorial +=========================== + +Upper air analysis is a staple of many synoptic and mesoscale analysis +problems. In this tutorial we will gather weather balloon data, plot it, +perform a series of thermodynamic calculations, and summarize the results. +To learn more about the Skew-T diagram and its use in weather analysis and +forecasting, checkout `this `_ +air weather service guide. +""" + +from datetime import datetime + +import matplotlib.pyplot as plt +from mpl_toolkits.axes_grid1.inset_locator import inset_axes +import numpy as np + +import metpy.calc as mpcalc +from metpy.io import get_upper_air_data +from metpy.plots import Hodograph, SkewT +from metpy.units import concatenate + +######################################################################### +# Getting Data +# ------------ +# +# We will download data from the +# `University of Wyoming sounding data page `_ +# , which has an extensive archive of data available, as well as current data. +# +# In this case, we will download the sounding data from the Veterans Day +# tornado outbreak in 2002 by passing a ``datetime`` object and station name to the +# ``get_upper_air_data`` function. + +dataset = get_upper_air_data(datetime(2002, 11, 11, 0), 'BNA') + +########################################################################## + +# We can view the fields available in the dataset. We will create some simple +# variables to make the rest of the code more concise. + +print(dataset.variables.keys()) + +p = dataset.variables['pressure'][:] +T = dataset.variables['temperature'][:] +Td = dataset.variables['dewpoint'][:] +u = dataset.variables['u_wind'][:] +v = dataset.variables['v_wind'][:] + +########################################################################## +# Thermodynamic Calculations +# -------------------------- +# +# Often times we will want to calculate some thermodynamic parameters of a +# sounding. The MetPy calc module has many such calculations already implemented! +# +# * **Lifting Condensation Level (LCL)** - The level at which an air parcel's +# relative humidity becomes 100% when lifted along a dry adiabatic path. +# * **Parcel Path** - Path followed by a hypothetical parcel of air, beginning +# at the surface temperature/pressure and rising dry adiabatically until +# reaching the LCL, then rising moist adiabatially. + +# Calculate the LCL level +parcel_lcl = mpcalc.lcl(p[0], T[0], Td[0]) + +# Determine the LCL temperature by lifting a parcel from the surface +# pressure and temperature via a dry adiabatic process. +lcl_temperature = mpcalc.dry_lapse(concatenate((p[0], parcel_lcl)), T[0])[-1].to('degC') + +print(parcel_lcl, lcl_temperature) + +# Calculate the parcel profile. +parcel_prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') + +########################################################################## +# Basic Skew-T Plotting +# --------------------- +# +# The Skew-T (log-P) diagram is the standard way to view rawinsonde data. The +# y-axis is height in pressure coordinates and the x-axis is temperature. The +# y coordinates are plotted on a logarithmic scale and the x coordinate system +# is skewed. An explanation of skew-T interpretation is beyond the scope of this +# tutorial, but here we will plot one that can be used for analysis or +# publication. +# +# The most basic skew-T can be plotted with only five lines of Python. +# These lines perform the following tasks: +# +# 1. Create a ``Figure`` object and set the size of the figure. +# +# 2. Create a ``SkewT`` object +# +# 3. Plot the pressure and temperature (note that the pressure, +# the independent variable, is first even though it is plotted on the y-axis). +# +# 4. Plot the pressure and dewpoint temperature. +# +# 5. Plot the wind barbs at the appropriate pressure using the u and v wind +# components. + +# Create a new figure. The dimensions here give a good aspect ratio +fig = plt.figure(figsize=(9, 9)) +skew = SkewT(fig) + +# Plot the data using normal plotting functions, in this case using +# log scaling in Y, as dictated by the typical meteorological plot +skew.plot(p, T, 'r', linewidth=2) +skew.plot(p, Td, 'g', linewidth=2) +skew.plot_barbs(p, u, v) + +# Show the plot +plt.show() + +########################################################################## +# Advanced Skew-T Plotting +# ------------------------ +# +# Fiducial lines indicating dry adiabats, moist adiabats, and mixing ratio are +# useful when performing further analysis on the Skew-T diagram. Often the +# 0C isotherm is emphasized and areas of CAPE and CIN are shaded. + +# Create a new figure. The dimensions here give a good aspect ratio +fig = plt.figure(figsize=(9, 9)) +skew = SkewT(fig, rotation=30) + +# Plot the data using normal plotting functions, in this case using +# log scaling in Y, as dictated by the typical meteorological plot +skew.plot(p, T, 'r') +skew.plot(p, Td, 'g') +skew.plot_barbs(p, u, v) +skew.ax.set_ylim(1000, 100) +skew.ax.set_xlim(-40, 60) + +# Plot LCL temperature as black dot +skew.plot(parcel_lcl, lcl_temperature, 'ko', markerfacecolor='black') + +# Plot the parcel profile as a black line +skew.plot(p, parcel_prof, 'k', linewidth=2) + +# Color regions of CAPE and CIN (the area between the actual temperature and +# the parcel path). +skew.ax.fill_betweenx(p, T, parcel_prof, where=T >= parcel_prof, facecolor='blue', alpha=0.4) +skew.ax.fill_betweenx(p, T, parcel_prof, where=T < parcel_prof, facecolor='red', alpha=0.4) + +# Plot a zero degree isotherm +l = skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) + +# Add the relevant special lines +skew.plot_dry_adiabats() +skew.plot_moist_adiabats() +skew.plot_mixing_lines() + +# Show the plot +plt.show() + +########################################################################## +# Adding a Hodograph +# ------------------ +# +# A hodograph is a polar representation of the wind profile measured by the rawinsonde. +# Winds at different levels are plotted as vectors with their tails at the origin, the angle +# from the vertical axes representing the direction, and the length representing the speed. +# The line plotted on the hodograph is a line connecting the tips of these vectors, +# which are not drawn. + +# Create a new figure. The dimensions here give a good aspect ratio +fig = plt.figure(figsize=(9, 9)) +skew = SkewT(fig, rotation=30) + +# Plot the data using normal plotting functions, in this case using +# log scaling in Y, as dictated by the typical meteorological plot +skew.plot(p, T, 'r') +skew.plot(p, Td, 'g') +skew.plot_barbs(p, u, v) +skew.ax.set_ylim(1000, 100) +skew.ax.set_xlim(-40, 60) + +# Plot LCL as black dot +skew.plot(parcel_lcl, lcl_temperature, 'ko', markerfacecolor='black') + +# Plot the parcel profile as a black line +skew.plot(p, parcel_prof, 'k', linewidth=2) + +# Color regions of CAPE and CIN (the area between the actual temperature and +# the parcel path). +skew.ax.fill_betweenx(p, T, parcel_prof, where=T >= parcel_prof, facecolor='blue', alpha=0.4) +skew.ax.fill_betweenx(p, T, parcel_prof, where=T < parcel_prof, facecolor='red', alpha=0.4) + +# Plot a zero degree isotherm +l = skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) + +# Add the relevant special lines +skew.plot_dry_adiabats() +skew.plot_moist_adiabats() +skew.plot_mixing_lines() + +# Create a hodograph +# Create an inset axes object that is 40% width and height of the +# figure and put it in the upper right hand corner. +ax_hod = inset_axes(skew.ax, '40%', '40%', loc=1) +h = Hodograph(ax_hod, component_range=80.) +h.add_grid(increment=20) +h.plot_colormapped(u, v, np.hypot(u, v)) # Plot a line colored by wind speed + +# Show the plot +plt.show()