From 9c0b8360ba6d78fef272b6e1a17899d587df9a2e Mon Sep 17 00:00:00 2001 From: lauratomkins Date: Tue, 7 Nov 2023 14:47:34 -0500 Subject: [PATCH 01/11] Create precip_rate.py --- pyart/retrieve/precip_rate.py | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 pyart/retrieve/precip_rate.py diff --git a/pyart/retrieve/precip_rate.py b/pyart/retrieve/precip_rate.py new file mode 100644 index 0000000000..9a4c1cefae --- /dev/null +++ b/pyart/retrieve/precip_rate.py @@ -0,0 +1,63 @@ +""" +Rescale reflectivity to precipitation rate + +""" + +import numpy as np + +from pyart.core import Radar + + +def ZtoR(radar, ref_field="reflectivity", a=300, b=1.4, save_name='NWS_primary_prate'): + """ + Convert reflectivity (dBZ) to precipitation rate (mm/hr) + + Author: Laura Tomkins + + Parameters + ---------- + radar : Radar + Radar object used. + ref_field : str + Reflectivity field name to use to look up reflectivity data. In the + radar object. Default field name is 'reflectivity'. Units are expected + to be dBZ. + a : float + a value (coefficient) in the Z-R relationship + b: float + b value (exponent) in the Z-R relationship + + Returns + ------- + radar : Radar + The radar object containing the precipitation rate field + + References + ---------- + American Meteorological Society, 2022: "Z-R relation". Glossary of Meteorology, + https://glossary.ametsoc.org/wiki/Z-r_relation + + """ + + # get reflectivity data + ref_data = radar.fields[ref_field]['data'] + ref_data = np.ma.masked_invalid(ref_data) + + # convert to linear reflectivity + ref_linear = 10 ** (ref_data / 10) + precip_rate = (ref_linear / a) ** (1 / b) + + # create dictionary + prate_dict = { + 'data': precip_rate, + 'standard_name': save_name, + 'long_name': "{} rescaled from linear reflectivity".format(save_name), + 'units': 'mm/hr', + 'valid_min': 0, + 'valid_max': 1000 + } + + # add field to radar object + radar.add_field(save_name, prate_dict, replace_existing=True) + + return radar From bb51e4b25eecf869d50b3ff3543cd298755802d3 Mon Sep 17 00:00:00 2001 From: lauratomkins Date: Tue, 7 Nov 2023 14:48:28 -0500 Subject: [PATCH 02/11] Update __init__.py --- pyart/retrieve/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyart/retrieve/__init__.py b/pyart/retrieve/__init__.py index 4666f6913d..216196f379 100644 --- a/pyart/retrieve/__init__.py +++ b/pyart/retrieve/__init__.py @@ -11,6 +11,7 @@ from .echo_class import steiner_conv_strat # noqa from .gate_id import fetch_radar_time_profile, map_profile_to_gates # noqa from .kdp_proc import kdp_maesaka, kdp_schneebeli, kdp_vulpiani # noqa +from .precip_rate import ZtoR # noqa from .qpe import est_rain_rate_a # noqa from .qpe import est_rain_rate_hydro # noqa from .qpe import est_rain_rate_kdp # noqa From 676113e4beb6d92b6bc8f3b4429a07ad843fd9e1 Mon Sep 17 00:00:00 2001 From: lauratomkins Date: Tue, 7 Nov 2023 15:08:56 -0500 Subject: [PATCH 03/11] Create test_precip_rate.py --- tests/retrieve/test_precip_rate.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/retrieve/test_precip_rate.py diff --git a/tests/retrieve/test_precip_rate.py b/tests/retrieve/test_precip_rate.py new file mode 100644 index 0000000000..312a05238b --- /dev/null +++ b/tests/retrieve/test_precip_rate.py @@ -0,0 +1,22 @@ +""" Unit Tests for Py-ART's retrieve/precip_rate.py module. """ + +import pyart +import math + + +def test_precip_rate(): + grid = pyart.testing.make_storm_grid() + dict = pyart.retrieve.ZtoR(grid) + + # check that field is in grid object + assert "NWS_primary_prate" in grid.fields.keys() + + # check calculations are within 10^-4 orders of magnitude + assert math.floor( + math.log10( + grid.fields['NWS_primary_prate']['data'][0,10,10] - + (((10**(grid.fields['reflectivity']['data'][0,10,10]/10)) / 300) ** (1 / 1.4)))) < -4 + + + + From 62e08a243513f8daadae64b3eaf21487a1d30094 Mon Sep 17 00:00:00 2001 From: lauratomkins Date: Tue, 7 Nov 2023 15:08:59 -0500 Subject: [PATCH 04/11] Update precip_rate.py --- pyart/retrieve/precip_rate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyart/retrieve/precip_rate.py b/pyart/retrieve/precip_rate.py index 9a4c1cefae..91a2fb76be 100644 --- a/pyart/retrieve/precip_rate.py +++ b/pyart/retrieve/precip_rate.py @@ -54,7 +54,7 @@ def ZtoR(radar, ref_field="reflectivity", a=300, b=1.4, save_name='NWS_primary_p 'long_name': "{} rescaled from linear reflectivity".format(save_name), 'units': 'mm/hr', 'valid_min': 0, - 'valid_max': 1000 + 'valid_max': 10000 } # add field to radar object From c171b6a97b7096983e36bb268743c59de3b13437 Mon Sep 17 00:00:00 2001 From: lauratomkins Date: Tue, 7 Nov 2023 15:14:22 -0500 Subject: [PATCH 05/11] Linting formatting --- pyart/retrieve/__init__.py | 2 +- pyart/retrieve/precip_rate.py | 18 ++++++++---------- tests/retrieve/test_precip_rate.py | 23 +++++++++++++++++------ 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/pyart/retrieve/__init__.py b/pyart/retrieve/__init__.py index 216196f379..b06dda6e53 100644 --- a/pyart/retrieve/__init__.py +++ b/pyart/retrieve/__init__.py @@ -11,7 +11,7 @@ from .echo_class import steiner_conv_strat # noqa from .gate_id import fetch_radar_time_profile, map_profile_to_gates # noqa from .kdp_proc import kdp_maesaka, kdp_schneebeli, kdp_vulpiani # noqa -from .precip_rate import ZtoR # noqa +from .precip_rate import ZtoR # noqa from .qpe import est_rain_rate_a # noqa from .qpe import est_rain_rate_hydro # noqa from .qpe import est_rain_rate_kdp # noqa diff --git a/pyart/retrieve/precip_rate.py b/pyart/retrieve/precip_rate.py index 91a2fb76be..c113019ff3 100644 --- a/pyart/retrieve/precip_rate.py +++ b/pyart/retrieve/precip_rate.py @@ -5,10 +5,8 @@ import numpy as np -from pyart.core import Radar - -def ZtoR(radar, ref_field="reflectivity", a=300, b=1.4, save_name='NWS_primary_prate'): +def ZtoR(radar, ref_field="reflectivity", a=300, b=1.4, save_name="NWS_primary_prate"): """ Convert reflectivity (dBZ) to precipitation rate (mm/hr) @@ -40,7 +38,7 @@ def ZtoR(radar, ref_field="reflectivity", a=300, b=1.4, save_name='NWS_primary_p """ # get reflectivity data - ref_data = radar.fields[ref_field]['data'] + ref_data = radar.fields[ref_field]["data"] ref_data = np.ma.masked_invalid(ref_data) # convert to linear reflectivity @@ -49,12 +47,12 @@ def ZtoR(radar, ref_field="reflectivity", a=300, b=1.4, save_name='NWS_primary_p # create dictionary prate_dict = { - 'data': precip_rate, - 'standard_name': save_name, - 'long_name': "{} rescaled from linear reflectivity".format(save_name), - 'units': 'mm/hr', - 'valid_min': 0, - 'valid_max': 10000 + "data": precip_rate, + "standard_name": save_name, + "long_name": f"{save_name} rescaled from linear reflectivity", + "units": "mm/hr", + "valid_min": 0, + "valid_max": 10000, } # add field to radar object diff --git a/tests/retrieve/test_precip_rate.py b/tests/retrieve/test_precip_rate.py index 312a05238b..164bd76233 100644 --- a/tests/retrieve/test_precip_rate.py +++ b/tests/retrieve/test_precip_rate.py @@ -1,21 +1,32 @@ """ Unit Tests for Py-ART's retrieve/precip_rate.py module. """ -import pyart import math +import pyart def test_precip_rate(): grid = pyart.testing.make_storm_grid() - dict = pyart.retrieve.ZtoR(grid) + grid = pyart.retrieve.ZtoR(grid) # check that field is in grid object assert "NWS_primary_prate" in grid.fields.keys() # check calculations are within 10^-4 orders of magnitude - assert math.floor( - math.log10( - grid.fields['NWS_primary_prate']['data'][0,10,10] - - (((10**(grid.fields['reflectivity']['data'][0,10,10]/10)) / 300) ** (1 / 1.4)))) < -4 + assert ( + math.floor( + math.log10( + grid.fields["NWS_primary_prate"]["data"][0, 10, 10] + - ( + ( + (10 ** (grid.fields["reflectivity"]["data"][0, 10, 10] / 10)) + / 300 + ) + ** (1 / 1.4) + ) + ) + ) + < -4 + ) From d8b55a257d4f5d7e2135c0d11fbe76df21596638 Mon Sep 17 00:00:00 2001 From: lauratomkins Date: Tue, 7 Nov 2023 15:18:38 -0500 Subject: [PATCH 06/11] Update test_precip_rate.py --- tests/retrieve/test_precip_rate.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/retrieve/test_precip_rate.py b/tests/retrieve/test_precip_rate.py index 164bd76233..9b755f46a9 100644 --- a/tests/retrieve/test_precip_rate.py +++ b/tests/retrieve/test_precip_rate.py @@ -4,6 +4,7 @@ import pyart + def test_precip_rate(): grid = pyart.testing.make_storm_grid() grid = pyart.retrieve.ZtoR(grid) @@ -26,8 +27,4 @@ def test_precip_rate(): ) ) < -4 - ) - - - - + ) \ No newline at end of file From d699b202dbe01e46987ded870cd915d48822014d Mon Sep 17 00:00:00 2001 From: lauratomkins Date: Tue, 7 Nov 2023 15:21:03 -0500 Subject: [PATCH 07/11] Update test_precip_rate.py --- tests/retrieve/test_precip_rate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/retrieve/test_precip_rate.py b/tests/retrieve/test_precip_rate.py index 9b755f46a9..48d63428f2 100644 --- a/tests/retrieve/test_precip_rate.py +++ b/tests/retrieve/test_precip_rate.py @@ -27,4 +27,4 @@ def test_precip_rate(): ) ) < -4 - ) \ No newline at end of file + ) From c365eb6dd016dac63035f08b035b3e1e44d42ab2 Mon Sep 17 00:00:00 2001 From: lauratomkins Date: Wed, 8 Nov 2023 11:03:28 -0500 Subject: [PATCH 08/11] Move functions and change math to numpy --- pyart/retrieve/__init__.py | 2 +- pyart/retrieve/precip_rate.py | 61 ------------------------------ pyart/retrieve/qpe.py | 54 ++++++++++++++++++++++++++ tests/retrieve/test_precip_rate.py | 30 --------------- tests/retrieve/test_qpe.py | 25 ++++++++++++ 5 files changed, 80 insertions(+), 92 deletions(-) delete mode 100644 pyart/retrieve/precip_rate.py delete mode 100644 tests/retrieve/test_precip_rate.py diff --git a/pyart/retrieve/__init__.py b/pyart/retrieve/__init__.py index b06dda6e53..1276e35038 100644 --- a/pyart/retrieve/__init__.py +++ b/pyart/retrieve/__init__.py @@ -11,7 +11,6 @@ from .echo_class import steiner_conv_strat # noqa from .gate_id import fetch_radar_time_profile, map_profile_to_gates # noqa from .kdp_proc import kdp_maesaka, kdp_schneebeli, kdp_vulpiani # noqa -from .precip_rate import ZtoR # noqa from .qpe import est_rain_rate_a # noqa from .qpe import est_rain_rate_hydro # noqa from .qpe import est_rain_rate_kdp # noqa @@ -19,6 +18,7 @@ from .qpe import est_rain_rate_za # noqa from .qpe import est_rain_rate_zkdp # noqa from .qpe import est_rain_rate_zpoly # noqa +from .qpe import ZtoR # noqa from .qvp import quasi_vertical_profile # noqa from .simple_moment_calculations import calculate_snr_from_reflectivity # noqa from .simple_moment_calculations import calculate_velocity_texture # noqa diff --git a/pyart/retrieve/precip_rate.py b/pyart/retrieve/precip_rate.py deleted file mode 100644 index c113019ff3..0000000000 --- a/pyart/retrieve/precip_rate.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Rescale reflectivity to precipitation rate - -""" - -import numpy as np - - -def ZtoR(radar, ref_field="reflectivity", a=300, b=1.4, save_name="NWS_primary_prate"): - """ - Convert reflectivity (dBZ) to precipitation rate (mm/hr) - - Author: Laura Tomkins - - Parameters - ---------- - radar : Radar - Radar object used. - ref_field : str - Reflectivity field name to use to look up reflectivity data. In the - radar object. Default field name is 'reflectivity'. Units are expected - to be dBZ. - a : float - a value (coefficient) in the Z-R relationship - b: float - b value (exponent) in the Z-R relationship - - Returns - ------- - radar : Radar - The radar object containing the precipitation rate field - - References - ---------- - American Meteorological Society, 2022: "Z-R relation". Glossary of Meteorology, - https://glossary.ametsoc.org/wiki/Z-r_relation - - """ - - # get reflectivity data - ref_data = radar.fields[ref_field]["data"] - ref_data = np.ma.masked_invalid(ref_data) - - # convert to linear reflectivity - ref_linear = 10 ** (ref_data / 10) - precip_rate = (ref_linear / a) ** (1 / b) - - # create dictionary - prate_dict = { - "data": precip_rate, - "standard_name": save_name, - "long_name": f"{save_name} rescaled from linear reflectivity", - "units": "mm/hr", - "valid_min": 0, - "valid_max": 10000, - } - - # add field to radar object - radar.add_field(save_name, prate_dict, replace_existing=True) - - return radar diff --git a/pyart/retrieve/qpe.py b/pyart/retrieve/qpe.py index 6a4405af64..82ede5ca98 100644 --- a/pyart/retrieve/qpe.py +++ b/pyart/retrieve/qpe.py @@ -684,3 +684,57 @@ def _coeff_ra_table(): coeff_ra_dict.update({"X": (45.5, 0.83)}) return coeff_ra_dict + +def ZtoR(radar, ref_field="reflectivity", a=300, b=1.4, save_name="NWS_primary_prate"): + """ + Convert reflectivity (dBZ) to precipitation rate (mm/hr) + + Author: Laura Tomkins + + Parameters + ---------- + radar : Radar + Radar object used. + ref_field : str + Reflectivity field name to use to look up reflectivity data. In the + radar object. Default field name is 'reflectivity'. Units are expected + to be dBZ. + a : float + a value (coefficient) in the Z-R relationship + b: float + b value (exponent) in the Z-R relationship + + Returns + ------- + radar : Radar + The radar object containing the precipitation rate field + + References + ---------- + American Meteorological Society, 2022: "Z-R relation". Glossary of Meteorology, + https://glossary.ametsoc.org/wiki/Z-r_relation + + """ + + # get reflectivity data + ref_data = radar.fields[ref_field]["data"] + ref_data = np.ma.masked_invalid(ref_data) + + # convert to linear reflectivity + ref_linear = 10 ** (ref_data / 10) + precip_rate = (ref_linear / a) ** (1 / b) + + # create dictionary + prate_dict = { + "data": precip_rate, + "standard_name": save_name, + "long_name": f"{save_name} rescaled from linear reflectivity", + "units": "mm/hr", + "valid_min": 0, + "valid_max": 10000, + } + + # add field to radar object + radar.add_field(save_name, prate_dict, replace_existing=True) + + return radar diff --git a/tests/retrieve/test_precip_rate.py b/tests/retrieve/test_precip_rate.py deleted file mode 100644 index 48d63428f2..0000000000 --- a/tests/retrieve/test_precip_rate.py +++ /dev/null @@ -1,30 +0,0 @@ -""" Unit Tests for Py-ART's retrieve/precip_rate.py module. """ - -import math - -import pyart - - -def test_precip_rate(): - grid = pyart.testing.make_storm_grid() - grid = pyart.retrieve.ZtoR(grid) - - # check that field is in grid object - assert "NWS_primary_prate" in grid.fields.keys() - - # check calculations are within 10^-4 orders of magnitude - assert ( - math.floor( - math.log10( - grid.fields["NWS_primary_prate"]["data"][0, 10, 10] - - ( - ( - (10 ** (grid.fields["reflectivity"]["data"][0, 10, 10] / 10)) - / 300 - ) - ** (1 / 1.4) - ) - ) - ) - < -4 - ) diff --git a/tests/retrieve/test_qpe.py b/tests/retrieve/test_qpe.py index e72ebb053c..fc750e2fe2 100644 --- a/tests/retrieve/test_qpe.py +++ b/tests/retrieve/test_qpe.py @@ -1,6 +1,7 @@ """ Unit tests for rainfall rate estimation module. """ from numpy.testing import assert_allclose +import numpy as np import pyart @@ -153,3 +154,27 @@ def test_get_coeff_rkdp(): coeff_rkdp_use_x = pyart.retrieve.qpe._get_coeff_rkdp(13e9) assert coeff_rkdp_use_x == (15.81, 0.7992) + +def test_precip_rate(): + grid = pyart.testing.make_storm_grid() + grid = pyart.retrieve.ZtoR(grid) + + # check that field is in grid object + assert "NWS_primary_prate" in grid.fields.keys() + + # check calculations are within 10^-4 orders of magnitude + assert ( + np.floor( + np.log10( + grid.fields["NWS_primary_prate"]["data"][0, 10, 10] + - ( + ( + (10 ** (grid.fields["reflectivity"]["data"][0, 10, 10] / 10)) + / 300 + ) + ** (1 / 1.4) + ) + ) + ) + < -4 + ) From d914fae3a0c4fab25ecc7eaea20820d554e9faf9 Mon Sep 17 00:00:00 2001 From: lauratomkins Date: Wed, 8 Nov 2023 11:07:27 -0500 Subject: [PATCH 09/11] Update formatting --- tests/retrieve/test_qpe.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/retrieve/test_qpe.py b/tests/retrieve/test_qpe.py index fc750e2fe2..a4741dca7f 100644 --- a/tests/retrieve/test_qpe.py +++ b/tests/retrieve/test_qpe.py @@ -1,7 +1,7 @@ """ Unit tests for rainfall rate estimation module. """ -from numpy.testing import assert_allclose import numpy as np +from numpy.testing import assert_allclose import pyart @@ -155,6 +155,7 @@ def test_get_coeff_rkdp(): coeff_rkdp_use_x = pyart.retrieve.qpe._get_coeff_rkdp(13e9) assert coeff_rkdp_use_x == (15.81, 0.7992) + def test_precip_rate(): grid = pyart.testing.make_storm_grid() grid = pyart.retrieve.ZtoR(grid) From 0b08be5c41b1ddcc5ef8afef4311750fc557a3cd Mon Sep 17 00:00:00 2001 From: lauratomkins Date: Wed, 8 Nov 2023 11:10:21 -0500 Subject: [PATCH 10/11] Update formatting --- pyart/retrieve/qpe.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyart/retrieve/qpe.py b/pyart/retrieve/qpe.py index 82ede5ca98..c657228d82 100644 --- a/pyart/retrieve/qpe.py +++ b/pyart/retrieve/qpe.py @@ -685,6 +685,7 @@ def _coeff_ra_table(): return coeff_ra_dict + def ZtoR(radar, ref_field="reflectivity", a=300, b=1.4, save_name="NWS_primary_prate"): """ Convert reflectivity (dBZ) to precipitation rate (mm/hr) From e7be22fb525d41f6641aae2d99ba6f384fe995f7 Mon Sep 17 00:00:00 2001 From: mgrover1 Date: Thu, 9 Nov 2023 08:41:02 -0600 Subject: [PATCH 11/11] FIX: Fix the testing suite to avoid nans --- tests/retrieve/test_qpe.py | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/tests/retrieve/test_qpe.py b/tests/retrieve/test_qpe.py index a4741dca7f..98b39503e9 100644 --- a/tests/retrieve/test_qpe.py +++ b/tests/retrieve/test_qpe.py @@ -1,6 +1,5 @@ """ Unit tests for rainfall rate estimation module. """ -import numpy as np from numpy.testing import assert_allclose import pyart @@ -163,19 +162,10 @@ def test_precip_rate(): # check that field is in grid object assert "NWS_primary_prate" in grid.fields.keys() - # check calculations are within 10^-4 orders of magnitude - assert ( - np.floor( - np.log10( - grid.fields["NWS_primary_prate"]["data"][0, 10, 10] - - ( - ( - (10 ** (grid.fields["reflectivity"]["data"][0, 10, 10] / 10)) - / 300 - ) - ** (1 / 1.4) - ) - ) - ) - < -4 - ) + # Calculate the estimated value + correct = ( + (10 ** (grid.fields["reflectivity"]["data"][0, 10, 10] / 10.0)) / 300.0 + ) ** (1 / 1.4) + + # Check for correctness + assert_allclose(grid.fields["NWS_primary_prate"]["data"][0, 10, 10], correct)