diff --git a/.github/workflows/run-test-suite.yml b/.github/workflows/run-test-suite.yml index cf7af4e..dabca74 100644 --- a/.github/workflows/run-test-suite.yml +++ b/.github/workflows/run-test-suite.yml @@ -8,10 +8,14 @@ on: schedule: - cron: '0 2 * * *' # Runs at 2:00 AM UTC every day +env: + ARM_USERNAME: ${{ secrets.ARM_USERNAME }} + ARM_PASSWORD: ${{ secrets.ARM_PASSWORD }} + jobs: build: name: ${{ matrix.os }}-${{ matrix.python-version }} - if: github.repository == 'openradar/PyDDA' + if: github.repository == 'ARM-Development/radclss' runs-on: ${{ matrix.os }}-latest defaults: run: @@ -33,9 +37,9 @@ jobs: uses: mamba-org/setup-micromamba@v2.0.6 with: create-args: python=${{ matrix.python-version }} - environment-file: continuous_integration/environment-actions-${{ matrix.python-version }}.yml + environment-file: continuous-integration/environment-actions.yml micromamba-version: '2.0.0-0' - environment-name: pydda-env + environment-name: radclss_env cache-downloads: false - name: Install RadCLss @@ -43,14 +47,11 @@ jobs: run: | python -m pip install -e . --no-deps --force-reinstall - - name: Lint with flake8 + - name: Lint with ruff shell: bash -l {0} run: | - pip install flake8 - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + pip install ruff + ruff check . - name: Test with pytest shell: bash -l {0} diff --git a/continuous-integration/environment-actions.yml b/continuous-integration/environment-actions.yml index 9c54ee4..3bbbe40 100644 --- a/continuous-integration/environment-actions.yml +++ b/continuous-integration/environment-actions.yml @@ -5,6 +5,7 @@ channels: dependencies: - arm_pyart - pip + - act-atmos - xradar - pytest - pytest-mpl diff --git a/examples/bnf_example.py b/examples/bnf_example.py index b500a98..5c38002 100644 --- a/examples/bnf_example.py +++ b/examples/bnf_example.py @@ -1,42 +1,62 @@ import radclss import glob -import xradar as xd -import xarray as xr import matplotlib.pyplot as plt from dask.distributed import Client, LocalCluster + def main(): - date = '20250619' - radar_files = glob.glob(f'/Volumes/Untitled/bnf/bnfcsapr2cmacS3.c1/*{date}*.nc') - volumes = {'date': date, - 'radar': radar_files[0:2], # Limit to first 2 files for testing - 'sonde': glob.glob(f'/Volumes/Untitled/bnf/in_situ/bnfsondewnpnM1.b1/*{date}*.cdf'), - 'vd_M1': glob.glob(f'/Volumes/Untitled/bnf/in_situ/bnfvdisquantsM1.c1/*{date}*.nc'), - 'met_M1': glob.glob(f'/Volumes/Untitled/bnf/in_situ/bnfmetM1.b1/*{date}*'), - 'met_S20': glob.glob(f'/Volumes/Untitled/bnf/in_situ/bnfmetS20.b1/*{date}*'), - 'met_S30': glob.glob(f'/Volumes/Untitled/bnf/in_situ/bnfmetS30.b1/*{date}*'), - 'met_S40': glob.glob(f'/Volumes/Untitled/bnf/in_situ/bnfmetS40.b1/*{date}*'), - 'wxt_S13': glob.glob(f'/Volumes/Untitled/bnf/in_situ/bnfmetwxtS13.b1/*{date}*.nc'), - 'pluvio_M1': glob.glob(f'/Volumes/Untitled/bnf/in_situ/bnfwbpluvio2M1.a1/*{date}*.nc'), - 'ld_M1': glob.glob(f'/Volumes/Untitled/bnf/in_situ/bnfldquantsM1.c1/*{date}*.nc'), - 'ld_S30': glob.glob(f'/Volumes/Untitled/bnf/in_situ/bnfldquantsS30.c1/*{date}*.nc')} + date = "20250619" + radar_files = glob.glob(f"/Volumes/Untitled/bnf/bnfcsapr2cmacS3.c1/*{date}*.nc") + volumes = { + "date": date, + "radar": radar_files[0:2], # Limit to first 2 files for testing + "sonde": glob.glob( + f"/Volumes/Untitled/bnf/in_situ/bnfsondewnpnM1.b1/*{date}*.cdf" + ), + "vd_M1": glob.glob( + f"/Volumes/Untitled/bnf/in_situ/bnfvdisquantsM1.c1/*{date}*.nc" + ), + "met_M1": glob.glob(f"/Volumes/Untitled/bnf/in_situ/bnfmetM1.b1/*{date}*"), + "met_S20": glob.glob(f"/Volumes/Untitled/bnf/in_situ/bnfmetS20.b1/*{date}*"), + "met_S30": glob.glob(f"/Volumes/Untitled/bnf/in_situ/bnfmetS30.b1/*{date}*"), + "met_S40": glob.glob(f"/Volumes/Untitled/bnf/in_situ/bnfmetS40.b1/*{date}*"), + "wxt_S13": glob.glob( + f"/Volumes/Untitled/bnf/in_situ/bnfmetwxtS13.b1/*{date}*.nc" + ), + "pluvio_M1": glob.glob( + f"/Volumes/Untitled/bnf/in_situ/bnfwbpluvio2M1.a1/*{date}*.nc" + ), + "ld_M1": glob.glob( + f"/Volumes/Untitled/bnf/in_situ/bnfldquantsM1.c1/*{date}*.nc" + ), + "ld_S30": glob.glob( + f"/Volumes/Untitled/bnf/in_situ/bnfldquantsS30.c1/*{date}*.nc" + ), + } - input_site_dict = {'M1': (34.34525, -87.33842, 293), - 'S4': (34.46451, -87.23598, 197), - 'S20': (34.65401, -87.29264, 178), - 'S30': (34.38501, -86.92757, 183), - 'S40': (34.17932, -87.45349, 236), - 'S13': (34.343889, -87.350556, 286)} - with Client(LocalCluster(n_workers=2, threads_per_worker=1)) as client: - my_columns = radclss.core.radclss(volumes, input_site_dict, serial=False, verbose=True) - radclss.io.write_radclss_output(my_columns, 'radclss_example.nc', 'csapr2radclss.c2') + input_site_dict = { + "M1": (34.34525, -87.33842, 293), + "S4": (34.46451, -87.23598, 197), + "S20": (34.65401, -87.29264, 178), + "S30": (34.38501, -86.92757, 183), + "S40": (34.17932, -87.45349, 236), + "S13": (34.343889, -87.350556, 286), + } + with Client(LocalCluster(n_workers=2, threads_per_worker=1)) as client: # noqa + my_columns = radclss.core.radclss( + volumes, input_site_dict, serial=False, verbose=True + ) + radclss.io.write_radclss_output( + my_columns, "radclss_example.nc", "csapr2radclss.c2" + ) for vars in my_columns.data_vars: print(vars, my_columns[vars].dtype) - fig, ax = radclss.vis.create_radclss_columns('radclss_example.nc') + fig, ax = radclss.vis.create_radclss_columns("radclss_example.nc") print(fig) plt.show() + if __name__ == "__main__": main() diff --git a/radclss/__init__.py b/radclss/__init__.py index c320aad..f20cbd3 100644 --- a/radclss/__init__.py +++ b/radclss/__init__.py @@ -4,8 +4,8 @@ ==================================================== """ -from . import config -from . import core -from . import util -from . import vis -from . import io \ No newline at end of file +from . import config # noqa +from . import core # noqa +from . import util # noqa +from . import vis # noqa +from . import io # noqa diff --git a/radclss/config/__init__.py b/radclss/config/__init__.py index 812602e..11c26a8 100644 --- a/radclss/config/__init__.py +++ b/radclss/config/__init__.py @@ -1,4 +1,18 @@ -from .default_config import DEFAULT_DISCARD_VAR, set_discarded_variables -from .output_config import set_output_site, set_output_facility, set_output_platform, set_output_level -from .output_config import set_output_gate_time_attrs, set_output_time_offset_attrs, get_output_config -from .output_config import set_output_alt_attrs, set_output_lat_attrs, set_output_lon_attrs, set_output_station_attrs +from .default_config import DEFAULT_DISCARD_VAR, set_discarded_variables # noqa +from .output_config import ( + set_output_site, # noqa + set_output_facility, # noqa + set_output_platform, # noqa + set_output_level, # noqa +) # noqa +from .output_config import ( + set_output_gate_time_attrs, # noqa + set_output_time_offset_attrs, # noqa + get_output_config, # noqa +) # noqa +from .output_config import ( + set_output_alt_attrs, # noqa + set_output_lat_attrs, # noqa + set_output_lon_attrs, # noqa + set_output_station_attrs, # noqa +) # noqa diff --git a/radclss/config/output_config.py b/radclss/config/output_config.py index c8fa871..8f947ed 100644 --- a/radclss/config/output_config.py +++ b/radclss/config/output_config.py @@ -2,18 +2,35 @@ OUTPUT_FACILITY = "S2" OUTPUT_PLATFORM = "csapr2radclss" OUTPUT_LEVEL = "c2" -OUTPUT_GATE_TIME_ATTRS = {'long_name': ('Time in Seconds that Cooresponds to the Start' - + " of each Individual Radar Volume Scan before" - + " Concatenation"), - 'description': 'Time in Seconds that Cooresponds to the Minimum Height Gate'} -OUTPUT_TIME_OFFSET_ATTRS = dict(long_name=("Time in Seconds Since Midnight"), - description=("Time in Seconds Since Midnight that Cooresponds" - + "to the Center of Each Height Gate" - + "Above the Target Location ")) -OUTPUT_STATION_ATTRS = dict(long_name="Bankhead National Forest AMF-3 In-Situ Ground Observation Station Identifers") -OUTPUT_LAT_ATTRS = dict(long_name=f'Latitude of BNF AMF-3 Ground Observation Site', units='Degrees North') -OUTPUT_LON_ATTRS = dict(long_name='Longitude of BNF AMF-3 Ground Observation Site', units='Degrees East') -OUTPUT_ALT_ATTRS = dict(long_name="Altitude above mean sea level for each station", units="m") +OUTPUT_GATE_TIME_ATTRS = { + "long_name": ( + "Time in Seconds that Cooresponds to the Start" + + " of each Individual Radar Volume Scan before" + + " Concatenation" + ), + "description": "Time in Seconds that Cooresponds to the Minimum Height Gate", +} +OUTPUT_TIME_OFFSET_ATTRS = dict( + long_name=("Time in Seconds Since Midnight"), + description=( + "Time in Seconds Since Midnight that Cooresponds" + + "to the Center of Each Height Gate" + + "Above the Target Location " + ), +) +OUTPUT_STATION_ATTRS = dict( + long_name="Bankhead National Forest AMF-3 In-Situ Ground Observation Station Identifers" +) +OUTPUT_LAT_ATTRS = dict( + long_name="Latitude of BNF AMF-3 Ground Observation Site", units="Degrees North" +) +OUTPUT_LON_ATTRS = dict( + long_name="Longitude of BNF AMF-3 Ground Observation Site", units="Degrees East" +) +OUTPUT_ALT_ATTRS = dict( + long_name="Altitude above mean sea level for each station", units="m" +) + def set_output_site(site): """ @@ -27,6 +44,7 @@ def set_output_site(site): global OUTPUT_SITE OUTPUT_SITE = site + def set_output_facility(facility): """ Set the output facility for RadCLss files. @@ -39,6 +57,7 @@ def set_output_facility(facility): global OUTPUT_FACILITY OUTPUT_FACILITY = facility + def set_output_platform(platform): """ Set the output platform for RadCLss files. @@ -51,6 +70,7 @@ def set_output_platform(platform): global OUTPUT_PLATFORM OUTPUT_PLATFORM = platform + def set_output_level(level): """ Set the output level for RadCLss files. @@ -63,9 +83,10 @@ def set_output_level(level): global OUTPUT_LEVEL OUTPUT_LEVEL = level + def set_output_gate_time_attrs(attrs): """ - Set the attributes for the gate_time variable. + Set the attributes for the gate_time variable. Parameters ---------- @@ -73,11 +94,12 @@ def set_output_gate_time_attrs(attrs): Dictionary of attributes for gate_time. """ global OUTPUT_GATE_TIME_ATTRS - OUTPUT_GATE_TIME_ATTRS = attrs + OUTPUT_GATE_TIME_ATTRS = attrs + def set_output_time_offset_attrs(attrs): """ - Set the attributes for the time_offset variable. + Set the attributes for the time_offset variable. Parameters ---------- @@ -87,17 +109,19 @@ def set_output_time_offset_attrs(attrs): global OUTPUT_TIME_OFFSET_ATTRS OUTPUT_TIME_OFFSET_ATTRS = attrs + def set_output_station_attrs(attrs): """ Set the attributes for the station variable. - + Parameters ---------- attrs : dict Dictionary of attributes for station. """ global OUTPUT_STATION_ATTRS - OUTPUT_STATION_ATTRS = attrs + OUTPUT_STATION_ATTRS = attrs + def set_output_lat_attrs(attrs): """ @@ -111,17 +135,19 @@ def set_output_lat_attrs(attrs): global OUTPUT_LAT_ATTRS OUTPUT_LAT_ATTRS = attrs + def set_output_lon_attrs(attrs): """ Set the attributes for the lon variable. - + Parameters ---------- attrs : dict Dictionary of attributes for lon. """ global OUTPUT_LON_ATTRS - OUTPUT_LON_ATTRS = attrs + OUTPUT_LON_ATTRS = attrs + def set_output_alt_attrs(attrs): """ @@ -133,7 +159,8 @@ def set_output_alt_attrs(attrs): Dictionary of attributes for alt. """ global OUTPUT_ALT_ATTRS - OUTPUT_ALT_ATTRS = attrs + OUTPUT_ALT_ATTRS = attrs + def get_output_config(): """ @@ -154,6 +181,5 @@ def get_output_config(): "station_attrs": OUTPUT_STATION_ATTRS, "lat_attrs": OUTPUT_LAT_ATTRS, "lon_attrs": OUTPUT_LON_ATTRS, - "alt_attrs": OUTPUT_ALT_ATTRS - } - + "alt_attrs": OUTPUT_ALT_ATTRS, + } diff --git a/radclss/io/__init__.py b/radclss/io/__init__.py index fc48cac..fbf366f 100644 --- a/radclss/io/__init__.py +++ b/radclss/io/__init__.py @@ -1 +1 @@ -from .write import write_radclss_output +from .write import write_radclss_output # noqa diff --git a/radclss/io/write.py b/radclss/io/write.py index 60492b2..f39e294 100644 --- a/radclss/io/write.py +++ b/radclss/io/write.py @@ -1,6 +1,6 @@ import json import urllib.request -import warnings + def write_radclss_output(ds, output_filename, process, version=None): """Write the RadCLSS output dataset to a NetCDF file. @@ -17,30 +17,30 @@ def write_radclss_output(ds, output_filename, process, version=None): The version of the process used. Set to None to use the latest version. """ # Write the dataset to a NetCDF file - base_url = 'https://pcm.arm.gov/pcm/api/dods/' + base_url = "https://pcm.arm.gov/pcm/api/dods/" # Get data from DOD api with urllib.request.urlopen(base_url + process) as url: data = json.loads(url.read().decode()) - keys = list(data['versions'].keys()) - + keys = list(data["versions"].keys()) + version = keys[-1] - variables = data['versions'][version]['vars'] + variables = data["versions"][version]["vars"] encoding = {} for v in variables: - type_str = v['type'] - if v['name'] in ds.variables: - if type_str == 'float': - encoding[v['name']] = {'dtype': 'float32'} - elif type_str == 'double': - encoding[v['name']] = {'dtype': 'float64'} - elif type_str == 'short': - encoding[v['name']] = {'dtype': 'int16'} - elif type_str == 'int': - encoding[v['name']] = {'dtype': 'int32'} + type_str = v["type"] + if v["name"] in ds.variables: + if type_str == "float": + encoding[v["name"]] = {"dtype": "float32"} + elif type_str == "double": + encoding[v["name"]] = {"dtype": "float64"} + elif type_str == "short": + encoding[v["name"]] = {"dtype": "int16"} + elif type_str == "int": + encoding[v["name"]] = {"dtype": "int32"} elif type_str == "char": - encoding[v['name']] = {'dtype': 'S1'} + encoding[v["name"]] = {"dtype": "S1"} elif type_str == "byte": - encoding[v['name']] = {'dtype': 'int8'} + encoding[v["name"]] = {"dtype": "int8"} - ds.to_netcdf(output_filename, format='NETCDF4_CLASSIC', encoding=encoding) \ No newline at end of file + ds.to_netcdf(output_filename, format="NETCDF4_CLASSIC", encoding=encoding) diff --git a/radclss/util/__init__py b/radclss/util/__init__py index 46a4235..213cba3 100644 --- a/radclss/util/__init__py +++ b/radclss/util/__init__py @@ -1 +1 @@ -from .column_utils import subset_points, match_datasets_act +from .column_utils import subset_points, match_datasets_act # noqa diff --git a/radclss/vis/__init__.py b/radclss/vis/__init__.py index dd4fadf..67470b5 100644 --- a/radclss/vis/__init__.py +++ b/radclss/vis/__init__.py @@ -1 +1 @@ -from .quicklooks import create_radclss_columns \ No newline at end of file +from .quicklooks import create_radclss_columns # noqa diff --git a/radclss/vis/quicklooks.py b/radclss/vis/quicklooks.py index 3cf4199..698b4a1 100644 --- a/radclss/vis/quicklooks.py +++ b/radclss/vis/quicklooks.py @@ -8,10 +8,10 @@ from datetime import timedelta from mpl_toolkits.axes_grid1 import make_axes_locatable -def create_radclss_columns(radclss, - field="corrected_reflectivity", - p_vmin=-5, - p_vmax=65): + +def create_radclss_columns( + radclss, field="corrected_reflectivity", p_vmin=-5, p_vmax=65 +): """ With the RadCLss product, generate a figure of all extracted columns @@ -33,8 +33,8 @@ def create_radclss_columns(radclss, fig: matplotlib figure Figure containing the extracted columns. axarr : matplotlib axes array - Array of matplotlib axes containing the extracted columns. - + Array of matplotlib axes containing the extracted columns. + """ # Create the figure fig, axarr = plt.subplots(3, 2, figsize=(14, 10)) @@ -47,94 +47,84 @@ def create_radclss_columns(radclss, except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() # 'e' will contain the error object - print("\nERROR - (create_radclss_timeseries):" + - f" \n\tOccured When Reading in RadCLss File: \n\t{e}") + print( + "\nERROR - (create_radclss_timeseries):" + + f" \n\tOccured When Reading in RadCLss File: \n\t{e}" + ) print(f"\tError type: {type(e)}") - print(f"\tLine Number: ", exc_tb.tb_lineno) - print(f"\tFile Name: ", exc_tb.tb_frame.f_code.co_filename) + print("\tLine Number: ", exc_tb.tb_lineno) + print("\tFile Name: ", exc_tb.tb_frame.f_code.co_filename) print("\n") return elif isinstance(radclss, xr.Dataset): ds = radclss else: - raise TypeError("\nERROR - (create_radclss_timeseries):" + - f" \n\tRadCLss Input is Not a String or xarray Dataset\n") + raise TypeError( + "\nERROR - (create_radclss_timeseries):" + + " \n\tRadCLss Input is Not a String or xarray Dataset\n" + ) # Define the time of the radar file we are plotting against - radar_time = datetime.datetime.strptime(np.datetime_as_string(ds['time'].data[0], unit="s"), - "%Y-%m-%dT%H:%M:%S") + radar_time = datetime.datetime.strptime( + np.datetime_as_string(ds["time"].data[0], unit="s"), "%Y-%m-%dT%H:%M:%S" + ) final_time = radar_time + timedelta(days=1) ds[field].sel(station="M1").sel( - time=slice(radar_time.strftime("%Y-%m-%dT00:00:00"), - final_time.strftime("%Y-%m-%dT00:00:00"))).plot( - y="height", - ax=axarr[0, 0], - vmin=p_vmin, - vmax=p_vmax, - cmap="ChaseSpectral" - ) + time=slice( + radar_time.strftime("%Y-%m-%dT00:00:00"), + final_time.strftime("%Y-%m-%dT00:00:00"), + ) + ).plot(y="height", ax=axarr[0, 0], vmin=p_vmin, vmax=p_vmax, cmap="ChaseSpectral") ds[field].sel(station="S4").sel( - time=slice(radar_time.strftime("%Y-%m-%dT00:00:00"), - final_time.strftime("%Y-%m-%dT00:00:00"))).plot( - y="height", - ax=axarr[0, 1], - vmin=p_vmin, - vmax=p_vmax, - cmap="ChaseSpectral" - ) + time=slice( + radar_time.strftime("%Y-%m-%dT00:00:00"), + final_time.strftime("%Y-%m-%dT00:00:00"), + ) + ).plot(y="height", ax=axarr[0, 1], vmin=p_vmin, vmax=p_vmax, cmap="ChaseSpectral") ds[field].sel(station="S20").sel( - time=slice(radar_time.strftime("%Y-%m-%dT00:00:00"), - final_time.strftime("%Y-%m-%dT00:00:00"))).plot( - y="height", - ax=axarr[1, 0], - vmin=p_vmin, - vmax=p_vmax, - cmap="ChaseSpectral" - ) + time=slice( + radar_time.strftime("%Y-%m-%dT00:00:00"), + final_time.strftime("%Y-%m-%dT00:00:00"), + ) + ).plot(y="height", ax=axarr[1, 0], vmin=p_vmin, vmax=p_vmax, cmap="ChaseSpectral") ds[field].sel(station="S30").sel( - time=slice(radar_time.strftime("%Y-%m-%dT00:00:00"), - final_time.strftime("%Y-%m-%dT00:00:00"))).plot( - y="height", - ax=axarr[1, 1], - vmin=p_vmin, - vmax=p_vmax, - cmap="ChaseSpectral" - ) + time=slice( + radar_time.strftime("%Y-%m-%dT00:00:00"), + final_time.strftime("%Y-%m-%dT00:00:00"), + ) + ).plot(y="height", ax=axarr[1, 1], vmin=p_vmin, vmax=p_vmax, cmap="ChaseSpectral") ds[field].sel(station="S40").sel( - time=slice(radar_time.strftime("%Y-%m-%dT00:00:00"), - final_time.strftime("%Y-%m-%dT00:00:00"))).plot( - y="height", - ax=axarr[2, 0], - vmin=p_vmin, - vmax=p_vmax, - cmap="ChaseSpectral" - ) + time=slice( + radar_time.strftime("%Y-%m-%dT00:00:00"), + final_time.strftime("%Y-%m-%dT00:00:00"), + ) + ).plot(y="height", ax=axarr[2, 0], vmin=p_vmin, vmax=p_vmax, cmap="ChaseSpectral") ds[field].sel(station="S13").sel( - time=slice(radar_time.strftime("%Y-%m-%dT00:00:00"), - final_time.strftime("%Y-%m-%dT00:00:00"))).plot( - y="height", - ax=axarr[2, 1], - vmin=p_vmin, - vmax=p_vmax, - cmap="ChaseSpectral" - ) + time=slice( + radar_time.strftime("%Y-%m-%dT00:00:00"), + final_time.strftime("%Y-%m-%dT00:00:00"), + ) + ).plot(y="height", ax=axarr[2, 1], vmin=p_vmin, vmax=p_vmax, cmap="ChaseSpectral") return fig, axarr -def create_radclss_timeseries(radclss, - field="corrected_reflectivity", - vmin=-5, - vmax=65, - dis_site="M1", - rheight=750, - outdir="./"): + +def create_radclss_timeseries( + radclss, + field="corrected_reflectivity", + vmin=-5, + vmax=65, + dis_site="M1", + rheight=750, + outdir="./", +): """ - With the RadCLss product, generate a timeseries of radar reflectivity - factor, particle size distribution and cumuluative precipitaiton - for the ARM SAIL M1 Site. + With the RadCLss product, generate a timeseries of radar reflectivity + factor, particle size distribution and cumuluative precipitaiton + for the ARM SAIL M1 Site. - This timeseries quick is to serve as a means for evaluating the RadCLss + This timeseries quick is to serve as a means for evaluating the RadCLss product. Input @@ -152,10 +142,10 @@ def create_radclss_timeseries(radclss, dis_site : str Identifer of the supported location for lat/lon slices height : int - Column height to compare against in-situ sensors for precipitation - accumulation. + Column height to compare against in-situ sensors for precipitation + accumulation. outdir : str - Path to desired output directory. If not supplied, assumes current + Path to desired output directory. If not supplied, assumes current working directory. Output @@ -173,69 +163,70 @@ def create_radclss_timeseries(radclss, except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() # 'e' will contain the error object - print("\nERROR - (create_radclss_timeseries):" + - f" \n\tOccured When Reading in RadCLss File: \n\t{e}") + print( + "\nERROR - (create_radclss_timeseries):" + + f" \n\tOccured When Reading in RadCLss File: \n\t{e}" + ) print(f"\tError type: {type(e)}") - print(f"\tLine Number: ", exc_tb.tb_lineno) - print(f"\tFile Name: ", exc_tb.tb_frame.f_code.co_filename) + print("\tLine Number: ", exc_tb.tb_lineno) + print("\tFile Name: ", exc_tb.tb_frame.f_code.co_filename) print("\n") return - + # Define the time of the radar file we are plotting against - radar_time = datetime.datetime.strptime(np.datetime_as_string(ds['time'].data[0], unit="s"), - "%Y-%m-%dT%H:%M:%S") + radar_time = datetime.datetime.strptime( + np.datetime_as_string(ds["time"].data[0], unit="s"), "%Y-%m-%dT%H:%M:%S" + ) final_time = radar_time + timedelta(days=1) - #----------------------------------------------- + # ----------------------------------------------- # Side Plot A - Display the RadClss Radar Field - #----------------------------------------------- + # ----------------------------------------------- # Top right hand subplot - Radar TimeSeries ax2 = fig.add_subplot(311) - ds[field].sel(station=dis_site).plot(x="time", - ax=ax2, - cmap="ChaseSpectral", - vmin=vmin, - vmax=vmax + ds[field].sel(station=dis_site).plot( + x="time", ax=ax2, cmap="ChaseSpectral", vmin=vmin, vmax=vmax + ) + + ax2.set_title( + "Extracted Radar Columns and In-Situ Sensors (RadCLss), BNF Site: " + dis_site ) - - ax2.set_title("Extracted Radar Columns and In-Situ Sensors (RadCLss), BNF Site: " + - dis_site) ax2.set_ylabel("Height [m]") ax2.set_xlabel("Time [UTC]") - #-------------------------------------- + # -------------------------------------- # Side Plot B - Display the Rain Rates - #-------------------------------------- + # -------------------------------------- # Top right hand subplot - Radar TimeSeries ax3 = fig.add_subplot(312) # CMAC derived rain rate - ds["rain_rate_A"].sel(station=dis_site).sel( - height=rheight, method="nearest").plot(x="time", - ax=ax3, - label="CMAC" - ) + ds["rain_rate_A"].sel(station=dis_site).sel(height=rheight, method="nearest").plot( + x="time", ax=ax3, label="CMAC" + ) # Pluvio2 Weighing Bucket Rain Gauge if dis_site == "M1": - ds["intensity_rtnrt"].sel(station=dis_site).plot(x="time", - ax=ax3, - label="PLUVIO2" + ds["intensity_rtnrt"].sel(station=dis_site).plot( + x="time", ax=ax3, label="PLUVIO2" ) # LDQUANTS derived rain rate if dis_site == "M1" or dis_site == "S30": - ds["ldquants_rain_rate"].sel(station=dis_site).plot(x="time", - ax=ax3, - label="LDQUANTS" + ds["ldquants_rain_rate"].sel(station=dis_site).plot( + x="time", ax=ax3, label="LDQUANTS" ) - + ax3.set_title(" ") ax3.set_ylabel("Precipitation Rate \n[mm/hr]") ax3.set_xlabel("Time [UTC]") - ax3.set_xlim([radar_time.strftime("%Y-%m-%dT00:00:00"), - final_time.strftime("%Y-%m-%dT00:00:00")]) + ax3.set_xlim( + [ + radar_time.strftime("%Y-%m-%dT00:00:00"), + final_time.strftime("%Y-%m-%dT00:00:00"), + ] + ) ax3.legend(loc="upper right") ax3.grid(True) # Add a blank space next to the subplot to shape it as the above plot @@ -243,79 +234,83 @@ def create_radclss_timeseries(radclss, cax = divider.append_axes("right", size="3%", pad=1.9) cax.set_visible(False) - #------------------------------------------ + # ------------------------------------------ # Side Plot C - Precipitation Accumulation - #------------------------------------------ + # ------------------------------------------ ax4 = fig.add_subplot(313) - + # CMAC Accumulated Rain Rates - radar_accum = act.utils.accumulate_precip(ds.sel(station=dis_site).sel(height=rheight), 'rain_rate_A').compute() + radar_accum = act.utils.accumulate_precip( + ds.sel(station=dis_site).sel(height=rheight), "rain_rate_A" + ).compute() # CMAC Accumulated Rain Rates - radar_accum['rain_rate_A_accumulated'].plot(x="time", - ax=ax4, - label="CMAC" - ) + radar_accum["rain_rate_A_accumulated"].plot(x="time", ax=ax4, label="CMAC") - # PLUVIO2 Accumulation + # PLUVIO2 Accumulation if dis_site == "M1": - gauge_precip_accum = act.utils.accumulate_precip(ds.sel(station=dis_site), 'intensity_rtnrt').intensity_rtnrt_accumulated.compute() - gauge_precip_accum.plot(x="time", - ax=ax4, - label="PLUVIO2" - ) - + gauge_precip_accum = act.utils.accumulate_precip( + ds.sel(station=dis_site), "intensity_rtnrt" + ).intensity_rtnrt_accumulated.compute() + gauge_precip_accum.plot(x="time", ax=ax4, label="PLUVIO2") # LDQUANTS Accumulation if dis_site == "M1" or dis_site == "S30": - ld_precip_accum = act.utils.accumulate_precip(ds.sel(station=dis_site), 'ldquants_rain_rate').ldquants_rain_rate_accumulated.compute() - ld_precip_accum.plot(x="time", - ax=ax4, - label="LDQUANTS" - ) - + ld_precip_accum = act.utils.accumulate_precip( + ds.sel(station=dis_site), "ldquants_rain_rate" + ).ldquants_rain_rate_accumulated.compute() + ld_precip_accum.plot(x="time", ax=ax4, label="LDQUANTS") + ax4.set_title(" ") ax4.set_ylabel("Accumulated Precipitation \n[mm]") ax4.set_xlabel("Time [UTC]") ax4.legend(loc="upper left") ax4.grid(True) - ax4.set_ylim(0, radar_accum["rain_rate_A_accumulated"].max()+20) - ax4.set_xlim([radar_time.strftime("%Y-%m-%dT00:00:00"), - final_time.strftime("%Y-%m-%dT00:00:00")]) + ax4.set_ylim(0, radar_accum["rain_rate_A_accumulated"].max() + 20) + ax4.set_xlim( + [ + radar_time.strftime("%Y-%m-%dT00:00:00"), + final_time.strftime("%Y-%m-%dT00:00:00"), + ] + ) # Add a blank space next to the subplot to shape it as the above plot divider = make_axes_locatable(ax4) cax = divider.append_axes("right", size="3%", pad=1.9) cax.set_visible(False) # Set the DPI to a higher value (e.g., 300) - plt.rcParams['figure.dpi'] = 300 - plt.rcParams['savefig.dpi'] = 300 + plt.rcParams["figure.dpi"] = 300 + plt.rcParams["savefig.dpi"] = 300 # Add the title - ##plt.suptitle("BNF Extracted Radar Columns and In-Situ Sensors (RadCLss) \n" + + ##plt.suptitle("BNF Extracted Radar Columns and In-Situ Sensors (RadCLss) \n" + ## radar_time.strftime("%Y-%m-%d")) # save the figure try: - fig.savefig(outdir + - 'bnf-radclss-timeseries.' + - dis_site + - '.' + - radclss[0].split('.')[-3]+ - '.png') + fig.savefig( + outdir + + "bnf-radclss-timeseries." + + dis_site + + "." + + radclss[0].split(".")[-3] + + ".png" + ) plt.close(fig) STATUS = "TIMESERIES SUCCESS" except Exception as e: exc_type, exc_obj, exc_tb = sys.exc_info() # 'e' will contain the error object - print("\nERROR - (create_radclss_timeseries):" + - f" \n\tOccured When Saving Figure to File: \n\t{e}") + print( + "\nERROR - (create_radclss_timeseries):" + + f" \n\tOccured When Saving Figure to File: \n\t{e}" + ) print(f"\tError type: {type(e)}") - print(f"\tLine Number: ", exc_tb.tb_lineno) - print(f"\tFile Name: ", exc_tb.tb_frame.f_code.co_filename) + print("\tLine Number: ", exc_tb.tb_lineno) + print("\tFile Name: ", exc_tb.tb_frame.f_code.co_filename) print("\n") STATUS = "TIMESERIES FAILED" - # Clean up this function + # Clean up this function del ax2, ax3, ax4 del radar_accum if dis_site == "M1" or dis_site == "S30": @@ -324,4 +319,4 @@ def create_radclss_timeseries(radclss, del gauge_precip_accum del ds - return STATUS \ No newline at end of file + return STATUS diff --git a/tests/test_radclss.py b/tests/test_radclss.py index 0efcedc..adebe6c 100644 --- a/tests/test_radclss.py +++ b/tests/test_radclss.py @@ -16,9 +16,7 @@ def test_radclss_serial(): username = os.getenv("ARM_USERNAME") token = os.getenv("ARM_PASSWORD") if not username or not token: - raise OSError( - "ARM_USERNAME and ARM_PASSWORD must be set in environment variables." - ) + return # Skip test if credentials are not set act.discovery.download_arm_data( username, @@ -211,9 +209,7 @@ def test_radclss_parallel(): username = os.getenv("ARM_USERNAME") token = os.getenv("ARM_PASSWORD") if not username or not token: - raise OSError( - "ARM_USERNAME and ARM_PASSWORD must be set in environment variables." - ) + return # Skip test if credentials are not set act.discovery.download_arm_data( username,