#!/usr/bin/env python # coding: utf-8 # # Py-ART's Data Model # # In this last notebook we saw that Py-ART reads data from files into **Radar** instances, which is Py-ART's data model for radar volumes. In this notebook we will examine this class in detail, looking at what is contained within and how they can be used to build custom processing methods. # # The layout of data in the **Radar** class is derived from the [CfRadial](http://www.ral.ucar.edu/projects/titan/docs/radial_formats/cfradial.html) specification. For those familar with this specification thinking of the **Radar** class as an in memory version of a CfRadial file is a good mental model. # First, the standand imports # In[1]: get_ipython().run_line_magic('matplotlib', 'inline') import matplotlib import matplotlib.pyplot as plt matplotlib.rcParams['figure.figsize'] = [12.0, 9.0] import pyart # We will use the volume from an ARM XSAPR radar that was examined in the last notebook. # In[2]: radar = pyart.io.read('data/XSW110520113537.RAW7HHL') # --- # This created a [**Radar**](http://arm-doe.github.io/pyart-docs-travis/user_reference/generated/pyart.core.Radar.html#pyart.core.Radar) instance which is the class Py-ART uses to store and process radar volume data. Python's built in help system can be used to provide information about this object. # In[3]: type(radar) # In[4]: help(radar) # We can also use Jupyter's help system. # In[5]: get_ipython().run_line_magic('pinfo', 'radar') # --- # This tells us about the Radar class in general. Use the [`info`](http://arm-doe.github.io/pyart-docs-travis/user_reference/generated/pyart.core.Radar.info.html#pyart.core.Radar.info) method to describe the data in the XSAPR file we have just read. # In[6]: radar.info('compact') # --- # Notice that from the above infomation we are looking at a volume consisting of Range Height Indicator or RHI scans. We can verify this by examining the `scan_type` attribute of the class. # In[7]: radar.scan_type # The information on the elevation angles is stored in the **elevation** attribute as a Python dictionary. The numerical values are stored as a NumPy array in the 'data' key. # In[8]: type(radar.elevation) # In[9]: radar.elevation.keys() # In[10]: print radar.elevation['long_name'] print radar.elevation['standard_name'] print radar.elevation['units'] # In[11]: radar.elevation['data'] # We can plot the elevation angles for all the sweeps in the volume. # In[12]: plt.plot(radar.elevation['data']) # By counting the local extrema in the graph, this volume appears to contain 6 sweeps. We can check to see that this agrees with the number of sweeps reported by the file: # In[13]: radar.nsweeps # The number of rays and gates in each ray is also available. # In[14]: print radar.nrays print radar.ngates # Other information about the radar volume is stored in dictionary attributes, with the numerical data stored under the 'data' key. # In[15]: radar.azimuth # In[16]: radar.latitude # also try longitude and latitude # In[17]: radar.time # In[18]: radar.time # try radar.range for the locations of the gates (bins) # Some data may not be available in the original file or not applicable to the volume. When this data is missing the attribute is set to `None` # In[19]: radar.target_scan_rate # In[20]: radar.target_scan_rate is None # In[21]: # XSAPR is stationary so geoference data does not apply print radar.rotation is None print radar.tilt is None print radar.roll is None print radar.drift is None # --- # The radar fields or moments are stored in the **fields** attribute as a dictionary of dictionaries. # In[22]: radar.fields.keys() # In[23]: type(radar.fields['reflectivity']) # In[24]: radar.fields['reflectivity'].keys() # In[25]: print radar.fields['reflectivity']['standard_name'] print radar.fields['reflectivity']['units'] print radar.fields['reflectivity']['coordinates'] # Field data is stored as a 2D array with dimensions of rays and gates. Masked points indicate that the gate was either not collected or below the detection threshold. # In[26]: print type(radar.fields['reflectivity']['data']) print radar.fields['reflectivity']['data'].shape print radar.fields['reflectivity']['data'].dtype # Instrument parameters are also stored as a dictionary of dictionaries # In[27]: radar.instrument_parameters.keys() # In[28]: radar.instrument_parameters['nyquist_velocity'] # --- # ## Accessing data from specific sweeps # # Often we want to work with a single sweep and therefore need to extact out the data from a particular sweep. The **Radar** class stores sweep specific information about the volume. # In[29]: radar.fixed_angle # In[30]: radar.sweep_number # In[31]: radar.sweep_mode # The indices which delinate a particular sweep are stored in the **sweep_start_ray_index** and **sweep_end_ray_index** attributes of the radar object. # In[32]: radar.sweep_start_ray_index # In[33]: radar.sweep_end_ray_index # The rays_per_sweep attribute provides a count of the number of rays in each sweep. # In[34]: radar.rays_per_sweep # The first sweep starts at ray 0 and the last ray in the sweep is ray 712. We can plot the elevation angles from just this sweep. # In[35]: plt.plot(radar.elevation['data'][0:713]) # These limits can also be detemined for a specific sweep using the [`get_start`](http://arm-doe.github.io/pyart-docs-travis/user_reference/generated/pyart.core.Radar.get_start.html#pyart.core.Radar.get_start), [`get_end`](http://arm-doe.github.io/pyart-docs-travis/user_reference/generated/pyart.core.Radar.get_end.html#pyart.core.Radar.get_end), or [`get_start_end`](http://arm-doe.github.io/pyart-docs-travis/user_reference/generated/pyart.core.Radar.get_start_end.html#pyart.core.Radar.get_start_end) methods. # In[36]: print radar.get_start(0) print radar.get_end(0) print radar.get_start_end(0) # Using these values to access sweep data can be cumbersome and care must be taken to avoid off-by-one errors. For convenience the Radar object provides a [`get_slice`](http://arm-doe.github.io/pyart-docs-travis/user_reference/generated/pyart.core.Radar.get_slice.html#pyart.core.Radar.get_slice) method which returns a slice object that can be used to select out the rays belonging to a particular sweep. # In[37]: get_ipython().run_line_magic('pinfo', 'radar.get_slice') # --- # Using this feature to plot the elevation angles from the first two sweeps in the volume. # In[38]: sweep_0_slice = radar.get_slice(0) plt.plot(radar.elevation['data'][sweep_0_slice]) # In[39]: sweep_1_slice = radar.get_slice(1) plt.plot(radar.elevation['data'][sweep_1_slice]) # --- # The radar class offers another method which further simplified this procedure, [`get_elevation`](http://arm-doe.github.io/pyart-docs-travis/user_reference/generated/pyart.core.Radar.get_elevation.html#pyart.core.Radar.get_elevation). # In[40]: help(radar.get_elevation) # In[41]: plt.plot(radar.get_elevation(0)) # The azimuth angles can extracted in the same manner using the [`get_azimuth`](http://arm-doe.github.io/pyart-docs-travis/user_reference/generated/pyart.core.Radar.get_azimuth.html#pyart.core.Radar.get_azimuth) method. # --- # Slice objects can also select out field data form a particular sweep. # # Plotting data from the entire volume in a b-scan representation. # In[42]: plt.imshow(radar.fields['reflectivity']['data'], aspect=0.5, origin='bottom') plt.xlabel('range gate') plt.ylabel('ray number') # And just the data from the first sweep. # In[43]: sweep_0_slice = radar.get_slice(0) # In[44]: plt.imshow(radar.fields['reflectivity']['data'][sweep_0_slice], aspect=0.5, origin='bottom') plt.xlabel('range gate') plt.ylabel('ray number') # The [`get_field`](http://arm-doe.github.io/pyart-docs-travis/user_reference/generated/pyart.core.Radar.get_field.html#pyart.core.Radar.get_field) method can simplify this even further. # In[45]: help(radar.get_field) # In[46]: refl_sweep_data = radar.get_field(sweep=0, field_name='reflectivity') # In[47]: plt.imshow(refl_sweep_data, aspect=0.5, origin='bottom') plt.xlabel('range gate') plt.ylabel('ray number') # --- # A final method for accessing a specific sweep is to use the [`extract_sweeps`](http://arm-doe.github.io/pyart-docs-travis/user_reference/generated/pyart.core.Radar.extract_sweeps.html#pyart.core.Radar.extract_sweeps) method to create a new **Radar** instance which contains only a single sweep. # In[48]: print radar.nrays print radar.nsweeps # In[49]: radar2 = radar.extract_sweeps([0]) # In[50]: print radar2.nrays print radar2.nsweeps # In[51]: plt.imshow(radar2.fields['reflectivity']['data'], aspect=0.5, origin='bottom') plt.xlabel('range gate') plt.ylabel('ray number') # ## Iterating over sweeps # Another common task is to loop over all sweeps in the volume. The **Radar** class contains a number of helpful methods to accomplish this task. # In[52]: for start in radar.iter_start(): print start # In[53]: for end in radar.iter_end(): print end # In[54]: for start, end in radar.iter_start_end(): print start, end # In[55]: fig = plt.figure() ax = fig.add_subplot(111) for i, sweep_slice in enumerate(radar.iter_slice()): ax.plot(radar.elevation['data'][sweep_slice] + i * 20) # In[56]: fig = plt.figure() ax = fig.add_subplot(111) for i, elev_data in enumerate(radar.iter_elevation()): ax.plot(elev_data + i * 20) # In[57]: fig = plt.figure() for i, refl_sweep_data in enumerate(radar.iter_field('reflectivity')): ax = fig.add_subplot(1, radar.nsweeps, i+1) ax.imshow(refl_sweep_data)