diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a0f1f3dc22..704591c13d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - id: black-jupyter - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.239' + rev: 'v0.4.9' hooks: - id: ruff args: [ "--fix" ] diff --git a/examples/retrieve/hydrometeor-class.ipynb b/examples/retrieve/hydrometeor-class.ipynb deleted file mode 100644 index 5421256d03..0000000000 --- a/examples/retrieve/hydrometeor-class.ipynb +++ /dev/null @@ -1,167 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "be2d2ecb-8e76-432c-b7e1-3b92eb61e425", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import cartopy.crs as ccrs\n", - "import cartopy\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import pyart" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "40d2e0f8-4695-4f3c-9bad-c866536f260c", - "metadata": {}, - "outputs": [], - "source": [ - "# Read in some test data\n", - "filename = pyart.testing.get_test_data(\"swx_20120520_0641.nc\")\n", - "radar = pyart.io.read(filename)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3ecbd17b-8f94-43b7-84dd-a69f7e071646", - "metadata": {}, - "outputs": [], - "source": [ - "list(radar.fields)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "584a1954-c1f0-4690-8423-c0053d54b712", - "metadata": {}, - "outputs": [], - "source": [ - "radar.fields[\"diff_phase\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6cb555a2-549e-4e10-adb8-938be66aeb4e", - "metadata": {}, - "outputs": [], - "source": [ - "gatefilter = pyart.filters.moment_and_texture_based_gate_filter(\n", - " radar,\n", - " zdr_field=\"diff_reflectivity\",\n", - " rhv_field=\"copol_coeff\",\n", - " phi_field=\"dp_phase_shift\",\n", - " refl_field=\"corrected_reflectivity_horizontal\",\n", - ")\n", - "# gatefilter.exclude_below('signal_to_noise_ratio', 10)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3178c6c3-4b84-4712-af6c-27d04297599c", - "metadata": {}, - "outputs": [], - "source": [ - "fig, ax = plt.subplots(1, 2, figsize=(12, 6), sharex=True, sharey=True)\n", - "display = pyart.graph.RadarDisplay(radar)\n", - "display.plot_ppi(\n", - " \"corrected_reflectivity_horizontal\",\n", - " 0,\n", - " vmin=0,\n", - " vmax=60.0,\n", - " ax=ax[0],\n", - " colorbar_label=\"Raw Ref\",\n", - " cmap=\"pyart_HomeyerRainbow\",\n", - ")\n", - "display.plot_ppi(\n", - " \"corrected_reflectivity_horizontal\",\n", - " 0,\n", - " vmin=0,\n", - " vmax=60.0,\n", - " gatefilter=gtfilter,\n", - " cmap=\"pyart_HomeyerRainbow\",\n", - " ax=ax[1],\n", - " colorbar_label=\"Filtered Ref\",\n", - ")\n", - "ax[0].set_xlim([-50, 50])\n", - "ax[0].set_ylim([-50, 50])\n", - "ax[0].set_aspect(\"equal\", \"box\")\n", - "ax[1].set_aspect(\"equal\", \"box\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "74c8b2e9-dd8c-49de-a0fe-b49c9413f3a8", - "metadata": {}, - "outputs": [], - "source": [ - "list(radar.fields)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a4be687b-cf52-4e85-bd22-91b4054b7b20", - "metadata": {}, - "outputs": [], - "source": [ - "hydro = pyart.retrieve.hydroclass_semisupervised(\n", - " radar,\n", - " refl_field=\"corrected_reflectivity_horizontal\",\n", - " zdr_field=\"diff_reflectivity\",\n", - " kdp_field=\"diff_phase\",\n", - " rhv_field=\"copol_coeff\",\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "95d0f228-d999-4038-87e5-6cf06e622c9d", - "metadata": {}, - "outputs": [], - "source": [ - "radar.in" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6f232563-c9b1-4c75-875a-34a7bb51e9f5", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.5" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/retrieve/plot_hydrometeor_class_x_band.py b/examples/retrieve/plot_hydrometeor_class_x_band.py new file mode 100644 index 0000000000..1841562fd8 --- /dev/null +++ b/examples/retrieve/plot_hydrometeor_class_x_band.py @@ -0,0 +1,227 @@ +""" +Hydrometeor Classification with Custom Frequency Settings +=========================================================== + + This script shows how to use hydrometeor classification for X-band radar data. + We are reading radar data, plotting some variables of interest and applying the + classification to identify types of precipitation. + + .. note:: + The script initially attempts hydrometeor classification without specific radar frequency information for band selection. +""" + +import matplotlib.pyplot as plt +import numpy as np +from open_radar_data import DATASETS + +import pyart + +filename = DATASETS.fetch("gucxprecipradarcmacppiS2.c1.20220314.025840.nc") +radar = pyart.io.read_cfradial(filename) + +figure = plt.figure(figsize=(15, 4)) + +ax1 = plt.subplot(1, 3, 1) +display = pyart.graph.RadarDisplay(radar) +ax1 = display.plot("DBZ", vmin=0, vmax=50) # DBZ corrected_reflectivity +plt.xlim(-20, 20) +plt.ylim(-20, 20) + +ax2 = plt.subplot(1, 3, 2) +ax2 = display.plot("corrected_differential_reflectivity", cmap="pyart_Carbone42") # ZDR +plt.xlim(-20, 20) +plt.ylim(-20, 20) + +ax3 = plt.subplot(1, 3, 3) +ax3 = display.plot("corrected_specific_diff_phase", cmap="pyart_Carbone42") # KDP +plt.xlim(-20, 20) + +# ### When instrument parameters does not have radar frequency info. + +print(radar.instrument_parameters) + + +# This shows an issue where radar frequency information is missing. Without this hydrometeor classification will default to C-band. + +# Get classification +hydromet_class = pyart.retrieve.hydroclass_semisupervised( + radar, + refl_field="corrected_reflectivity", + zdr_field="corrected_differential_reflectivity", + kdp_field="filtered_corrected_specific_diff_phase", + rhv_field="RHOHV", + temp_field="sounding_temperature", +) + +radar.add_field("hydro_classification", hydromet_class, replace_existing=True) + + +# Use `radar_freq` parameters +# To address this issue, radar frequency information can be supplied to the function with `radar_freq` parameter. + + +# Get classification +hydromet_class = pyart.retrieve.hydroclass_semisupervised( + radar, + refl_field="corrected_reflectivity", + zdr_field="corrected_differential_reflectivity", + kdp_field="filtered_corrected_specific_diff_phase", + rhv_field="RHOHV", + temp_field="sounding_temperature", + radar_freq=9.2e9, +) + +radar.add_field("hydro_classification", hydromet_class, replace_existing=True) + + +# Add radar frequency to the radar object +# Incorporating radar frequency into the radar object enhances processing pipeline. + +# Add X-band frequency information to radar.instrument_parameters +radar.instrument_parameters["frequency"] = { + "long_name": "Radar frequency", + "units": "Hz", + "data": [9.2e9], +} + +radar.instrument_parameters + + +# Let's run the classification again and the warning should change telling the radar frequency from instrument parameters is used. + + +hydromet_class = pyart.retrieve.hydroclass_semisupervised( + radar, + refl_field="corrected_reflectivity", + zdr_field="corrected_differential_reflectivity", + kdp_field="filtered_corrected_specific_diff_phase", + rhv_field="RHOHV", + temp_field="sounding_temperature", + radar_freq=9.2e9, +) + +radar.add_field("hydro_classification", hydromet_class, replace_existing=True) + + +# Note that the frequency used here is from the radar object, not the user supplied. + + +# plotting + +import matplotlib.colors as colors + +hid_colors = [ + "White", + "LightBlue", + "MediumBlue", + "DarkOrange", + "LightPink", + "Cyan", + "DarkGray", + "Lime", + "Yellow", + "Red", + "Fuchsia", +] +cmaphid = colors.ListedColormap(hid_colors) +cmapmeth = colors.ListedColormap(hid_colors[0:6]) +cmapmeth_trop = colors.ListedColormap(hid_colors[0:7]) + + +def adjust_fhc_colorbar_for_pyart(cb): + cb.set_ticks(np.arange(1.4, 10, 0.9)) + cb.ax.set_yticklabels( + [ + "Drizzle", + "Rain", + "Ice Crystals", + "Aggregates", + "Wet Snow", + "Vertical Ice", + "LD Graupel", + "HD Graupel", + "Hail", + "Big Drops", + ] + ) + cb.ax.set_ylabel("") + cb.ax.tick_params(length=0) + return cb + + +def adjust_meth_colorbar_for_pyart(cb, tropical=False): + if not tropical: + cb.set_ticks(np.arange(1.25, 5, 0.833)) + cb.ax.set_yticklabels( + ["R(Kdp, Zdr)", "R(Kdp)", "R(Z, Zdr)", "R(Z)", "R(Zrain)"] + ) + else: + cb.set_ticks(np.arange(1.3, 6, 0.85)) + cb.ax.set_yticklabels( + ["R(Kdp, Zdr)", "R(Kdp)", "R(Z, Zdr)", "R(Z_all)", "R(Z_c)", "R(Z_s)"] + ) + cb.ax.set_ylabel("") + cb.ax.tick_params(length=0) + return cb + + +def two_panel_plot( + radar, + sweep=0, + var1="corrected_reflectivity", + vmin1=0, + vmax1=65, + cmap1="RdYlBu_r", + units1="dBZ", + var2="corrected_differential_reflectivity", + vmin2=-5, + vmax2=5, + cmap2="RdYlBu_r", + units2="dB", + return_flag=False, + xlim=[-150, 150], + ylim=[-150, 150], +): + display = pyart.graph.RadarDisplay(radar) + fig = plt.figure(figsize=(13, 5)) + ax1 = fig.add_subplot(121) + display.plot_ppi( + var1, + sweep=sweep, + vmin=vmin1, + vmax=vmax1, + cmap=cmap1, + colorbar_label=units1, + mask_outside=True, + ) + display.set_limits(xlim=xlim, ylim=ylim) + ax2 = fig.add_subplot(122) + display.plot_ppi( + var2, + sweep=sweep, + vmin=vmin2, + vmax=vmax2, + cmap=cmap2, + colorbar_label=units2, + mask_outside=True, + ) + display.set_limits(xlim=xlim, ylim=ylim) + if return_flag: + return fig, ax1, ax2, display + + +lim = [-20, 20] +fig, ax1, ax2, display = two_panel_plot( + radar, + sweep=0, + var1="corrected_reflectivity", + var2="hydro_classification", + vmin2=0, + vmax2=10, + cmap2=cmaphid, + units2="", + return_flag=True, + xlim=lim, + ylim=lim, +) +display.cbs[1] = adjust_fhc_colorbar_for_pyart(display.cbs[1]) diff --git a/pyart/aux_io/odim_h5.py b/pyart/aux_io/odim_h5.py index abaf0f4c3a..0c6469210e 100644 --- a/pyart/aux_io/odim_h5.py +++ b/pyart/aux_io/odim_h5.py @@ -374,10 +374,10 @@ def read_odim_h5( try: sweep_data = _get_odim_h5_sweep_data(hfile[dset][h_field_key]) except KeyError: - sweep_data = np.zeros((rays_in_sweep, max_nbins)) + np.NaN + sweep_data = np.zeros((rays_in_sweep, max_nbins)) + np.nan sweep_nbins = sweep_data.shape[1] fdata[start : start + rays_in_sweep, :sweep_nbins] = sweep_data[:] - # set data to NaN if its beyond the range of this sweep + # set data to nan if its beyond the range of this sweep fdata[start : start + rays_in_sweep, sweep_nbins:max_nbins] = np.nan start += rays_in_sweep # create field dictionary diff --git a/pyart/filters/gatefilter.py b/pyart/filters/gatefilter.py index 4d6218f837..975001cc87 100644 --- a/pyart/filters/gatefilter.py +++ b/pyart/filters/gatefilter.py @@ -637,6 +637,14 @@ def exclude_above( marked = self._get_fdata(field) > value return self._merge(marked, op, exclude_masked) + def exclude_above_toa(self, value, exclude_masked=True, op="or", inclusive=False): + """Exclude gates above a given toa value.""" + if inclusive: + marked = self._radar.gate_altitude["data"] >= value + else: + marked = self._radar.gate_altitude["data"] > value + return self._merge(marked, op, exclude_masked) + def exclude_inside( self, field, v1, v2, exclude_masked=True, op="or", inclusive=True ): diff --git a/pyart/graph/gridmapdisplay.py b/pyart/graph/gridmapdisplay.py index a2f38b7706..fc7dcaecb8 100644 --- a/pyart/graph/gridmapdisplay.py +++ b/pyart/graph/gridmapdisplay.py @@ -274,14 +274,14 @@ def plot_grid( if add_grid_lines: if lon_lines is None: lon_lines = np.linspace( - np.around(ds.lon.min() - 0.1, decimals=2), - np.around(ds.lon.max() + 0.1, decimals=2), + np.around(ds.lon.min() - 0.1, decimals=2).values, + np.around(ds.lon.max() + 0.1, decimals=2).values, 5, ) if lat_lines is None: lat_lines = np.linspace( - np.around(ds.lat.min() - 0.1, decimals=2), - np.around(ds.lat.max() + 0.1, decimals=2), + np.around(ds.lat.min() - 0.1, decimals=2).values, + np.around(ds.lat.max() + 0.1, decimals=2).values, 5, ) diff --git a/pyart/io/cfradial.py b/pyart/io/cfradial.py index 7fa26b6067..032ee6067a 100644 --- a/pyart/io/cfradial.py +++ b/pyart/io/cfradial.py @@ -922,7 +922,7 @@ def _calculate_scale_and_offset(dic, dtype, minimum=None, maximum=None): if "_FillValue" in dic: fillvalue = dic["_FillValue"] else: - fillvalue = np.NaN + fillvalue = np.nan data = dic["data"].copy() data = np.ma.array(data, mask=(~np.isfinite(data) | (data == fillvalue))) diff --git a/pyart/io/mdv_common.py b/pyart/io/mdv_common.py index 04fb6b4f36..244ff7d26b 100644 --- a/pyart/io/mdv_common.py +++ b/pyart/io/mdv_common.py @@ -597,7 +597,7 @@ def read_a_field(self, fnum, debug=False): sw_data = np.frombuffer(decompr_data, np_form).astype("float32") sw_data.shape = (ny, nx) mask = sw_data == field_header["bad_data_value"] - np.putmask(sw_data, mask, [np.NaN]) + np.putmask(sw_data, mask, [np.nan]) # scale and offset the data, store in field_data scale = field_header["scale"] diff --git a/pyart/retrieve/echo_class.py b/pyart/retrieve/echo_class.py index a084597171..ac19ce5e82 100644 --- a/pyart/retrieve/echo_class.py +++ b/pyart/retrieve/echo_class.py @@ -96,7 +96,7 @@ def steiner_conv_strat( # Get reflectivity data ze = np.ma.copy(grid.fields[refl_field]["data"]) - ze = ze.filled(np.NaN) + ze = ze.filled(np.nan) eclass = steiner_class_buff( ze, @@ -609,6 +609,7 @@ def hydroclass_semisupervised( kdp_field=None, temp_field=None, hydro_field=None, + radar_freq=None, ): """ Classifies precipitation echoes following the approach by Besic et al @@ -634,6 +635,9 @@ def hydroclass_semisupervised( Output. Field name which represents the hydrometeor class field. A value of None will use the default field name as defined in the Py-ART configuration file. + radar_freq : str, optional + Radar frequency in Hertz (Hz) used for classification. + This parameter will be ignored, if the radar object has frequency information. Returns ------- @@ -650,8 +654,9 @@ def hydroclass_semisupervised( Notes ----- The default hydrometeor classification is valid for C-band radars. For X-band radars, - if frequency information is not present in the `radar.instrument_parameters`, a warning that the - algorithm is defaulting to the C band is printed. + if frequency information is not present in the `radar.instrument_parameters`, the user-supplied + `radar_freq` will be used with a warning. If both `radar.instrument_parameters` and + `radar_freq` parameter are missing, the algorithm defaults to the C band. If the radar frequency information is missing from the radar object, you can add it in `radar.instrument_parameters`, as follows: @@ -668,22 +673,20 @@ def hydroclass_semisupervised( # select the centroids as a function of frequency band if mass_centers is None: # assign coefficients according to radar frequency - if radar.instrument_parameters is not None: - if "frequency" in radar.instrument_parameters: - mass_centers = _get_mass_centers( - radar.instrument_parameters["frequency"]["data"][0] - ) - else: - mass_centers = _mass_centers_table()["C"] - warn( - "Radar frequency unknown. " - "Default coefficients for C band will be applied." - ) + if radar.instrument_parameters and "frequency" in radar.instrument_parameters: + frequency = radar.instrument_parameters["frequency"]["data"][0] + mass_centers = _get_mass_centers(frequency) + warn(f"Using radar frequency from instrument parameters: {frequency}") + elif radar_freq is not None: + mass_centers = _get_mass_centers(radar_freq) + warn( + f"Radar instrument parameters are empty. Using user-supplied radar frequency: {radar_freq}" + ) else: mass_centers = _mass_centers_table()["C"] warn( - "Radar instrument parameters is empty. So frequency is " - "unknown. Default coefficients for C band will be applied." + "Radar instrument parameters and radar_freq param are empty." + "So frequency is unknown. Default coefficients for C band will be applied." ) # parse the field parameters diff --git a/pyart/retrieve/gate_id.py b/pyart/retrieve/gate_id.py index e46de9a4ce..a20c275126 100644 --- a/pyart/retrieve/gate_id.py +++ b/pyart/retrieve/gate_id.py @@ -54,7 +54,7 @@ def map_profile_to_gates( # Check that z is not a MaskedArray if isinstance(z, np.ma.MaskedArray): - z = z.filled(np.NaN) + z = z.filled(np.nan) # find toa is not provided if toa is None: diff --git a/pyproject.toml b/pyproject.toml index 4d1b5e2ee9..7f014cbc46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ exclude = [ "doc", ] +[tool.ruff.lint] # E402: module level import not at top of file # E501: line too long - let black worry about that # E731: do not assign a lambda expression, use a def diff --git a/scripts/anytocfradial b/scripts/anytocfradial index 3fc9952149..e1f058f6bb 100755 --- a/scripts/anytocfradial +++ b/scripts/anytocfradial @@ -68,7 +68,7 @@ if __name__ == "__main__": "-v", "--version", action="version", - version="Py-ART version %s" % (pyart.__version__), + version=f"Py-ART version {pyart.__version__}", ) args = parser.parse_args() diff --git a/scripts/check_cfradial b/scripts/check_cfradial index 998ef24bf6..fd12ae9032 100755 --- a/scripts/check_cfradial +++ b/scripts/check_cfradial @@ -67,13 +67,13 @@ class AttributeTable: # check for incorrect type if attr_obj.type_bad(type(attr)): tup = (self.text, attr_name, type(attr), attr_obj._type) - t = "%s '%s' has incorrect type: %s should be %s." % tup + t = "{} '{}' has incorrect type: {} should be {}.".format(*tup) log_error(self.section, t) # check for incorrect value if attr_obj.value_bad(attr): tup = (self.text, attr_name, attr, attr_obj.value) - t = "%s '%s' has incorrect value: %s should be %s." % tup + t = "{} '{}' has incorrect value: {} should be {}.".format(*tup) log_error(self.section, t) def check(self, test_var, verb=False): @@ -170,12 +170,12 @@ class VariableTable: # check for incorrect type if var_obj.dtype_bad(var.dtype): tup = (self.text, var_name, var.dtype, var_obj.dtype) - t = "%s '%s' has incorrect type: %s should be %s" % tup + t = "{} '{}' has incorrect type: {} should be {}".format(*tup) log_error(self.section, t) # check for incorrect dim if var_obj.dim_bad(var.dimensions): tup = (self.text, var_name, var.dimensions, var_obj.dim) - t = "%s '%s' has incorrect dimensions: %s should be %s" % tup + t = "{} '{}' has incorrect dimensions: {} should be {}".format(*tup) log_error(self.section, t) # check for bad units if "units" not in var.ncattrs() and var_obj.units is not None: @@ -184,7 +184,7 @@ class VariableTable: return if var_obj.units is not None and var_obj.units_bad(var.units): tup = (self.text, var_name, var.units, var_obj.units) - t = "%s '%s' has incorrect units: %s should be %s" % tup + t = "{} '{}' has incorrect units: {} should be {}".format(*tup) log_error(self.section, t) def check(self, dataset, verb=False): @@ -267,7 +267,7 @@ def check_attribute(section, obj, text, attr_name, valid_choices): attr = getattr(obj, attr_name) if attr not in valid_choices: tup = (text, attr_name, attr, " ".join(valid_choices)) - t = "%s '%s' has an invalid value: %s must be one of %s" % tup + t = "{} '{}' has an invalid value: {} must be one of {}".format(*tup) log_error(section, t) return @@ -279,7 +279,7 @@ def check_char_variable(section, dataset, text, var_name, valid_options): value = var.tostring().strip("\x00").strip() if value not in valid_options: tup = (text, var_name, value, " ".join(valid_options)) - t = "%s '%s' has an invalid value: %s must be one of %s" % tup + t = "{} '{}' has an invalid value: {} must be one of {}".format(*tup) log_error(section, t) return @@ -309,7 +309,7 @@ def check_valid_time_format(section, dataset, text, var_name): time.strptime(s, "%Y-%m-%dT%H:%M:%SZ") except: tup = (text, var_name, s, "yyyy-mm-ddThh:mm:ssZ") - t = "%s '%s' has an invalid format: %s should be %s" % (tup) + t = "{} '{}' has an invalid format: {} should be {}".format(*tup) log_error(section, t) @@ -322,14 +322,14 @@ def check_metagroup(section, dataset, meta_group_name, valid_meta_group_vars): var = dataset.variables[var_name] if "meta_group" not in var.ncattrs(): tup = (meta_group_name, var_name) - text = "%s %s does not have a `meta_group` attribute" % (tup) + text = "{} {} does not have a `meta_group` attribute".format(*tup) log_error(section, text) else: if var.meta_group != meta_group_name: tup = (meta_group_name, var_name, var.meta_group, meta_group_name) text = ( - "%s %s 'meta_group' attribute has incorrect " - "value: %s should be %s" % (tup) + "{} {} 'meta_group' attribute has incorrect " + "value: {} should be {}".format(*tup) ) log_error(section, text) @@ -339,8 +339,8 @@ def check_metagroup(section, dataset, meta_group_name, valid_meta_group_vars): for var_name in find_all_meta_group_vars(dataset, meta_group_name): if var_name not in valid_meta_group_vars: text = ( - "variable {} should not have its meta_group attribute " - "set to '{}'".format(var_name, meta_group_name) + f"variable {var_name} should not have its meta_group attribute " + f"set to '{meta_group_name}'" ) log_error(section, text) @@ -518,7 +518,7 @@ def check_cfradial_compliance(dataset, verb=False): tup = (time_str, "yyyy-mm-ddThh:mm:ssZ") t = ( "'time' attribute 'units' has an invalid formatted time" - "value: %s should be %s" % (tup) + "value: {} should be {}".format(*tup) ) log_error("4.4.1", t) @@ -530,7 +530,7 @@ def check_cfradial_compliance(dataset, verb=False): tup = (time_str, s) t = ( "time attribute 'units' does not match time in " - "time_reference variable: %s verses %s" % (tup) + "time_reference variable: {} verses {}".format(*tup) ) log_error("4.4.1", t) elif "time_coverage_start" in dataset.variables: @@ -540,7 +540,7 @@ def check_cfradial_compliance(dataset, verb=False): tup = (time_str, s) t = ( "time attribute 'units' does not match time in " - "time_coverage_start variable: %s verses %s" % (tup) + "time_coverage_start variable: {} verses {}".format(*tup) ) log_error("4.4.1", t) @@ -657,7 +657,7 @@ def check_cfradial_compliance(dataset, verb=False): else: # fixed platfrom for v in ["heading", "roll", "pitch", "drift", "rotation", "tilt"]: if v in dataset.variables: - t = "variable '%s' must be omitted for fixed platforms" % (v) + t = f"variable '{v}' must be omitted for fixed platforms" log_error("4.9", t) # 4.10 Moments field data variables @@ -669,7 +669,7 @@ def check_cfradial_compliance(dataset, verb=False): # check the data type if var.dtype not in [BYTE, SHORT, INT, FLOAT, DOUBLE]: tup = (var_name, var.dtype) - t = "field variable '%s' has invalid type: %s" % (tup) + t = "field variable '{}' has invalid type: {}".format(*tup) log_error("4.10", t) # check attributes @@ -682,7 +682,7 @@ def check_cfradial_compliance(dataset, verb=False): # TODO check standard_name, against variable name # TODO check units correct for given standard_name - text = "field variable %s" % var_name + text = f"field variable {var_name}" field_attrs = AttributeTable(text, "4.10") field_attrs.opt_attr("long_name", STRING) field_attrs.req_attr("standard_name", STRING) @@ -719,8 +719,8 @@ def check_cfradial_compliance(dataset, verb=False): dim_0 = dataset.variables[v].dimensions[0] if dim_0 != "sweep": text = ( - "instrument_parameters {} must have a first " - "dimension of sweep, not {}".format(v, dim_0) + f"instrument_parameters {v} must have a first " + f"dimension of sweep, not {dim_0}" ) log_error("5.1", text) @@ -917,7 +917,7 @@ def check_cfradial_compliance(dataset, verb=False): if dim_0 != "r_calib": text = ( "radar_calibration r_calib_time must have first " - "dimension of 'r_calib' not '%s'" % (dim_0) + f"dimension of 'r_calib' not '{dim_0}'" ) log_error("5.4.2", text) else: @@ -967,8 +967,9 @@ def check_cfradial_compliance(dataset, verb=False): else: for var_name in valid_pv_vars: if var_name in dataset.variables: - t = "variable %s should not exist as the platform is" "stationary" % ( - var_name + t = ( + f"variable {var_name} should not exist as the platform is" + "stationary" ) log_error("5.6", t) diff --git a/scripts/convert_legacy_grid b/scripts/convert_legacy_grid index 27f7ef98d2..4ea86ef788 100755 --- a/scripts/convert_legacy_grid +++ b/scripts/convert_legacy_grid @@ -89,7 +89,7 @@ def main(): print("Field:", field) legacy_var = dset_legacy.variables[field] if legacy_var.shape != field_shape_with_time: - warnings.warn("Field %s skipped due to incorrect shape" % (field)) + warnings.warn(f"Field {field} skipped due to incorrect shape") continue _transfer_var(dset_modern, field, legacy_var, ("time", "z", "y", "x")) diff --git a/scripts/radar_info b/scripts/radar_info index ec9c5deb91..e7187f5101 100755 --- a/scripts/radar_info +++ b/scripts/radar_info @@ -61,7 +61,7 @@ if __name__ == "__main__": "-v", "--version", action="version", - version="Py-ART version %s" % (pyart.__version__), + version=f"Py-ART version {pyart.__version__}", ) args = parser.parse_args() diff --git a/scripts/radar_plot b/scripts/radar_plot index b220e31f71..6285a3071d 100755 --- a/scripts/radar_plot +++ b/scripts/radar_plot @@ -74,7 +74,7 @@ if __name__ == "__main__": "-v", "--version", action="version", - version="Py-ART version %s" % (pyart.__version__), + version=f"Py-ART version {pyart.__version__}", ) # ingest arguments diff --git a/tests/filters/test_gatefilter.py b/tests/filters/test_gatefilter.py index d12561c347..c97ac0e065 100644 --- a/tests/filters/test_gatefilter.py +++ b/tests/filters/test_gatefilter.py @@ -15,9 +15,9 @@ # more fdata2 = np.ma.masked_array(fdata, copy=True) fdata2[2, 2] = np.ma.masked -fdata2[3, 3] = np.NAN -fdata2[4, 4] = np.PINF -fdata2[5, 5] = np.NINF +fdata2[3, 3] = np.nan +fdata2[4, 4] = np.inf +fdata2[5, 5] = -np.inf radar.add_field("test_field2", {"data": fdata2}) @@ -103,6 +103,17 @@ def test_gatefilter_exclude_above(): assert gfilter.gate_excluded[0, -1] is np.True_ +def test_gatefilter_exclude_above_toa(): + gfilter = pyart.correct.GateFilter(radar) + gfilter.exclude_above_toa(211.0) + assert gfilter.gate_excluded[0, 0] is np.False_ + assert gfilter.gate_excluded[0, -1] is np.True_ + + assert gfilter.gate_excluded[0, -2] is np.False_ + gfilter.exclude_above_toa(211.0, inclusive=True) + assert gfilter.gate_excluded[0, -2] is np.True_ + + def test_gatefilter_exclude_inside(): gfilter = pyart.correct.GateFilter(radar) gfilter.exclude_inside("test_field", 2, 5, inclusive=False) diff --git a/tests/io/test_mdv_grid.py b/tests/io/test_mdv_grid.py index bc07ea2ce6..d457561579 100644 --- a/tests/io/test_mdv_grid.py +++ b/tests/io/test_mdv_grid.py @@ -242,7 +242,6 @@ def test_mdv_degree_grid(): fdata = grid.fields["refl"]["data"] assert fdata.shape == (1, 1837, 3661) assert np.ma.is_masked(fdata[0, 0, 0]) - assert_almost_equal(fdata[0, 130, 2536], 20.0, 1) assert grid.x["units"] == "degree_E" assert_almost_equal(grid.x["data"][0], -129.99, 2) diff --git a/tests/io/test_sigmet.py b/tests/io/test_sigmet.py index 05fa7801ee..b32ae05014 100644 --- a/tests/io/test_sigmet.py +++ b/tests/io/test_sigmet.py @@ -392,4 +392,4 @@ def test_1byte_datatype(): def test_frequency(): frequency = radar.instrument_parameters["frequency"] frequency["units"] == "s-1" - assert_almost_equal(frequency["data"], 9.670725e09) + assert_almost_equal(frequency["data"][0], np.float32(9.670725e09))