Working With Data From The CROCUS Doppler LIDAR Deployed During AGES+#

The CROCUS Scanning Doppler LIDAR was deployed to the NEIU Level Three rooftop node for July and August for NASA/NOAA AGES+. We have been running a crude ingest for the verticaly pointing data. This notebook gives an example of how to use and access this data.

For enquires contact Scott Collis scollis@anl.gov Paytsar Muradyan pmuradyan@anl.gov Max Grover mgrover@anl.gov

alternatvie text

import numpy as np
from netCDF4 import Dataset
import os
import datetime
import xarray as xr
import pandas as pd
import matplotlib.dates as mdates
import matplotlib.dates as mdates
from matplotlib import pyplot as plt
from matplotlib.colors import LogNorm
import pyart #This will be replaced by cmweather in the future
import act
import glob

%matplotlib inline
## You are using the Python ARM Radar Toolkit (Py-ART), an open source
## library for working with weather radar data. Py-ART is partly
## supported by the U.S. Department of Energy as part of the Atmospheric
## Radiation Measurement (ARM) Climate Research Facility, an Office of
## Science user facility.
##
## If you use this software to prepare a publication, please cite:
##
##     JJ Helmus and SM Collis, JORS 2016, doi: 10.5334/jors.119
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
Cell In[1], line 11
      9 from matplotlib import pyplot as plt
     10 from matplotlib.colors import LogNorm
---> 11 import pyart #This will be replaced by cmweather in the future
     12 import act
     13 import glob

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/pyart/__init__.py:29
     25 import importlib.metadata as _importlib_metadata
     27 # import subpackages
     28 # print out helpful message if build fails or importing from source tree
---> 29 from . import (
     30     __check_build,  # noqa
     31     aux_io,  # noqa
     32     bridge,  # noqa
     33     config,  # noqa
     34     core,  # noqa
     35     correct,  # noqa
     36     filters,  # noqa
     37     graph,  # noqa
     38     io,  # noqa
     39     map,  # noqa
     40     retrieve,  # noqa
     41     testing,  # noqa
     42     util,  # noqa
     43     xradar,  # noqa
     44 )
     45 from ._debug_info import _debug_info  # noqa
     47 # root level functions

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/pyart/xradar/__init__.py:1
----> 1 from .accessor import Xradar, Xgrid  # noqa

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/pyart/xradar/accessor.py:8
      1 """
      2 Utilities for interfacing between xradar and Py-ART
      3 
      4 """
      6 import copy
----> 8 import datatree
      9 import numpy as np
     10 import pandas as pd

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/datatree/__init__.py:2
      1 # import public API
----> 2 from .datatree import DataTree
      3 from .extensions import register_datatree_accessor
      4 from .io import open_datatree

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/datatree/datatree.py:33
     31 from xarray.core.merge import dataset_update_method
     32 from xarray.core.options import OPTIONS as XR_OPTS
---> 33 from xarray.core.utils import (
     34     Default,
     35     Frozen,
     36     HybridMappingProxy,
     37     _default,
     38     either_dict_or_kwargs,
     39     maybe_wrap_array,
     40 )
     41 from xarray.core.variable import Variable
     43 from . import formatting, formatting_html

ImportError: cannot import name 'HybridMappingProxy' from 'xarray.core.utils' (/home/runner/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/xarray/core/utils.py)

Open the dataset - This will be replaced with a THREDDS call

ds_ingested = act.io.read_arm_netcdf('/Volumes/T7/data/DL_ingested/crocus-neiu-dlvpt-a1-202308020000.nc')

Lets see what we have!

ds_ingested
/Users/scollis/miniconda3/envs/crocus/lib/python3.11/site-packages/xarray/core/formatting_html.py:22: DeprecationWarning: read_binary is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
  read_binary(package, resource).decode("utf-8")
/Users/scollis/miniconda3/envs/crocus/lib/python3.11/site-packages/xarray/core/formatting_html.py:22: DeprecationWarning: read_binary is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice.
  read_binary(package, resource).decode("utf-8")
<xarray.Dataset>
Dimensions:          (range: 500, time: 144257)
Coordinates:
  * range            (range) float64 12.0 36.0 60.0 ... 1.196e+04 1.199e+04
  * time             (time) datetime64[ns] 2023-08-02T00:00:44 ... 2023-08-02...
Data variables:
    radial_velocity  (range, time) float64 dask.array<chunksize=(500, 144257), meta=np.ndarray>
    beta             (range, time) float64 dask.array<chunksize=(500, 144257), meta=np.ndarray>
    intensity        (range, time) float64 dask.array<chunksize=(500, 144257), meta=np.ndarray>
Attributes:
    conventions:          CF 1.10
    site_ID:              NEIU
    CAMS_tag:             CMS-SDL-001
    datastream:           neiu-dopplerlidar-vpt-a1
    datalevel:            a1
    latitude:             41.9804526
    longitude:            -87.7196038
    _file_dates:          ['20230802']
    _file_times:          ['000044']
    _datastream:          neiu-dopplerlidar-vpt-a1
    _arm_standards_flag:  1
ds_ingested.intensity
<xarray.DataArray 'intensity' (range: 500, time: 144257)>
dask.array<open_dataset-0c64766fbbc55c4b84caad558b84b479intensity, shape=(500, 144257), dtype=float64, chunksize=(500, 144257), chunktype=numpy.ndarray>
Coordinates:
  * range    (range) float64 12.0 36.0 60.0 ... 1.194e+04 1.196e+04 1.199e+04
  * time     (time) datetime64[ns] 2023-08-02T00:00:44 ... 2023-08-02T23:58:42
ds_ingested.intensity.plot()
<matplotlib.collections.QuadMesh at 0x1a32d0990>
../../_images/d527cb80ed306868e69cef88e3226437ee4a0df8a3525f45b3682ac024682ade.png

Hmmm There is some data there but a a lot of data gathered around “6”

#time ranges
time1 = pd.Timestamp("2023-08-02 01:00:00")
time2 = pd.Timestamp("2023-08-02 02:00:00")

#Top of atmosphere
toa = 4000.0

#What ranges for Doppler Velocity
ds_ingested.intensity.sel(time=slice(time1,time2)).plot.pcolormesh(cmap=pyart.graph.cm_colorblind.ChaseSpectral)


my_axis = plt.gca()
my_axis.set_ylim([0,toa])
(0.0, 4000.0)
../../_images/6823c2093cd6e406e41ba958d33572bbd743fe87a0f73621745e99cc43b99938.png

Lets look at possible values… Lets do a histogram

ds_ingested.intensity.plot.hist()
plt.yscale("log")
../../_images/ba0c54b4a663e16a923d7f39a5ecd5a5d425c4eeb997fbbdc150c628e2228c83.png

ok! So looking into the data for some reason the DL reports as Intensity + 1… likely to avoid issues with negative logs… so lets simply subtract one and scale the colormap to log

#time ranges
time1 = pd.Timestamp("2023-08-02 01:00:00")
time2 = pd.Timestamp("2023-08-02 02:00:00")

#Top of atmosphere
toa = 4000.0

#What ranges for Doppler Velocity
vrange = 5
fig, axs = plt.subplots( ncols=1, nrows=2, figsize=[20,8], constrained_layout=True)

#Units in the DL data are odd..
(ds_ingested.intensity -1.).sel(time=slice(time1,time2)).plot.pcolormesh(norm=LogNorm(vmax=3, vmin=0.001),
                                     cmap=pyart.graph.cm_colorblind.ChaseSpectral, ax=axs[0])


plt.ylabel("Height")
axs[0].set_ylim([0,toa])


ds_ingested.radial_velocity.sel(time=slice(time1,time2)).plot.pcolormesh( cmap=pyart.graph.cm_colorblind.balance, 
                                                                   vmin=-vrange, vmax=vrange, ax=axs[1])
axs[1].set_ylim([0,toa])
(0.0, 4000.0)
../../_images/50a46695d64c5264a17819ba9774ceaa62885782cb7504e1cce428a59d8566eb.png

Lovely… now how about some insight#

We know PBL heights are a key ask of the thematic groups. So lets play with looking at aggreating temporal data

This is inspired by the most excellent workshop run at the 2023 ARM-ASR PI meeting See: https://github.com/ARM-Development/ARM-Notebooks/blob/main/Tutorials/arm-asr-pi-meeting-2023/pandas_xarray_tutorial/Pandas_Xarray_intro.ipynb

Lets look at stats on an hour by hour window

window = 'time.hour'

#NB: For non standard intervals look at resample functions from the same ARM ASR Tutorial

new_ds = ds_ingested.groupby(window).mean()
(new_ds.intensity-1.).transpose().plot()
my_axis = plt.gca()
my_axis.set_ylim([0,toa])
(0.0, 4000.0)
../../_images/7bf5dfd75fc99f9e452d9f16746035e2d11fd27ce2057bb4b178bc9f2b749421.png
new_ds.radial_velocity.transpose().plot(vmin=-2, vmax=2,  cmap=pyart.graph.cm_colorblind.balance)
my_axis = plt.gca()
my_axis.set_ylim([0,toa])
(0.0, 4000.0)
../../_images/4dba72a8afe86450f9f16e852ae221238e5141eca55546183210ee88db2e89d3.png

Cool!#

But now lets look at standard deviation over the hour…

new_ds_stdev = ds_ingested.groupby(window).std()
new_ds_stdev.radial_velocity.transpose().plot()
my_axis = plt.gca()
my_axis.set_ylim([0,toa])
(0.0, 4000.0)
../../_images/2ce485a6b0a5a1d2ec893d387adb1074a84ab691efa656aa0cc29af992fa1557.png

Ok… there is something there but the signal is swamped by the “texture” from when we are below SNR thresholds.

new_ds_stdev.radial_velocity.transpose().plot(vmax=3)
my_axis = plt.gca()
my_axis.set_ylim([0,toa])
(0.0, 4000.0)
../../_images/bc085e3c0ce1fcaf55659ad17f3e8211ec144ffa92d102f433761d83cf2bdb66.png

The idea: Conditionally sample based on intensity…#

new_ds.intensity.plot.hist()
plt.yscale("log")
../../_images/037a2d5479feaa3d731f70b19a22e389794da75a98a0fcd25d1ee11597d85b17.png

Woo Ho! Now lets make a data array which sets STDEV to zero when we think we are below detection limits…

filtered_da = new_ds_stdev.radial_velocity.where(new_ds.intensity > 1.01, 0)
filtered_da.transpose().plot(vmax=2)
my_axis = plt.gca()
my_axis.set_ylim([0,toa])
(0.0, 4000.0)
../../_images/d447383305fb4a1d9689b7ac990790d604255a4a0d274a66dda9a3d3f85b1e42.png
#This will take a LONG time to run

#Top of atmosphere
toa = 4000.0

#What ranges for Doppler Velocity
vrange = 5
fig, axs = plt.subplots( ncols=1, nrows=3, figsize=[20,12], constrained_layout=True)

#Units in the DL data are odd..
(ds_ingested.intensity -1.).plot.pcolormesh(norm=LogNorm(vmax=3, vmin=0.001),
                                     cmap=pyart.graph.cm_colorblind.ChaseSpectral, ax=axs[0])


plt.ylabel("Height")
axs[0].set_ylim([0,toa])


ds_ingested.radial_velocity.plot.pcolormesh( cmap=pyart.graph.cm_colorblind.balance, 
                                                                   vmin=-vrange, vmax=vrange, ax=axs[1])
axs[1].set_ylim([0,toa])


filtered_da.transpose().plot.pcolormesh(vmax=2, ax=axs[2])

axs[2].set_ylim([0,toa])

Of course these profiles can now be a nice input to a ML model :)#