Source code for towerpy.qpe.qpe_algs

"""Towerpy: an open-source toolbox for processing polarimetric radar data."""

import numpy as np
from scipy.interpolate import interp1d
from ..utils import radutilities as rut
from ..utils import unit_conversion as tpuc


[docs] class RadarQPE: """ A class to calculate rain rates from radar variables. Attributes ---------- elev_angle : float Elevation angle at which the scan was taken, in deg. file_name : str Name of the file containing radar data. scandatetime : datetime Date and time of scan. site_name : str Name of the radar site. """ def __init__(self, radobj=None):
[docs] self.elev_angle = getattr(radobj, 'elev_angle', None) if radobj else None
[docs] self.file_name = getattr(radobj, 'file_name', None) if radobj else None
[docs] self.scandatetime = getattr(radobj, 'scandatetime', None) if radobj else None
[docs] self.site_name = getattr(radobj, 'site_name', None) if radobj else None
[docs] def adp_to_r(self, adp, rband='C', temp=20., a=None, b=None, mlyr=None, beam_height=None): r""" Compute rain rates using the :math:`R(A_{DP})` estimator [Eq.1]_. Parameters ---------- adp : float or array Floats that corresponds to the differential attenuation, in dB/km. rband: str Frequency band according to the wavelength of the radar. The string has to be one of 'S', 'C' or 'X'. The default is 'C'. temp: float Temperature, in :math:`^{\circ}C`, used to derive the coefficients a, b according to [1]_. The default is 20. a, b : floats Override the computed coefficients of the :math:`R(A_{DP})` relationship. The default are None. mlyr : MeltingLayer Class, optional Melting layer class containing the top of the melting layer, (i.e., the melting level) and its thickness. Only gates below the melting layer bottom (i.e. the rain region below the melting layer) are included in the computation; ml_top and ml_thickness can be either a single value (float, int), or an array (or list) of values corresponding to each azimuth angle of the scan. If None, the rainfall estimator is applied to the whole PPI scan. beam_height : array, optional Height of the centre of the radar beam, in km. Returns ------- R : dict Computed rain rates, in mm/h. Math ---- .. [Eq.1] .. math:: R = aA_{DP}^b where R in mm/h, ADP in dB/km Notes ----- Standard values according to [1]_. References ---------- .. [1] Ryzhkov, A., Diederich, M., Zhang, P., & Simmer, C. (2014). "Potential Utilization of Specific Attenuation for Rainfall Estimation, Mitigation of Partial Beam Blockage, and Radar Networking" Journal of Atmospheric and Oceanic Technology, 31(3), 599-619. https://doi.org/10.1175/JTECH-D-13-00038.1 """ if a is None and b is None: # Default values for the temp temps = np.array((0, 10, 20, 30)) # Default values for S-C and X-band radars coeffs_a = {'X': np.array((57.8, 53.3, 51.1, 51.0)), 'C': np.array((281, 326, 393, 483)), 'S': np.array((3.02e3, 4.12e3, 5.51e3, 7.19e3))} coeffs_b = {'X': np.array((0.89, 0.85, 0.81, 0.78)), 'C': np.array((0.95, 0.94, 0.93, 0.93)), 'S': np.array((1.06, 1.06, 1.06, 1.06))} # Interpolate the temp and coeffs to set coeffs a and b icoeff_a = interp1d(temps, coeffs_a.get(rband)) icoeff_b = interp1d(temps, coeffs_b.get(rband)) coeff_a = icoeff_a(temp).item() coeff_b = icoeff_b(temp).item() else: coeff_a = a coeff_b = b # print(f'{coeff_a}, {coeff_b}') adpr = np.zeros_like(adp)+adp if mlyr is not None and beam_height is not None: mlyr_bottom = mlyr.ml_top - mlyr.ml_thickness if isinstance(mlyr_bottom, (int, float)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom) for nbh in beam_height] elif isinstance(mlyr_bottom, (np.ndarray, list, tuple)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom[cnt]) for cnt, nbh in enumerate(beam_height)] for cnt, azi in enumerate(adpr): azi[mlb_idx[cnt]:] = 0 nanidx = np.where(np.isnan(adp)) adpr[nanidx] = np.nan r = {'Rainfall [mm/h]': coeff_a*adp**coeff_b} r['coeff_a'] = coeff_a r['coeff_b'] = coeff_b self.r_adp = r
[docs] def ah_to_r(self, ah, rband='C', temp=20., a=None, b=None, mlyr=None, beam_height=None): r""" Compute rain rates using the :math:`R(A_{H})` estimator [Eq.1]_. Parameters ---------- ah : float or array Floats that corresponds to the specific attenuation, in dB/km. rband: str Frequency band according to the wavelength of the radar. The string has to be one of 'S', 'C' or 'X'. The default is 'C'. temp: float Temperature, in :math:`^{\circ}C`, used to derive the coefficients a, b according to [1]_. The default is 20. a, b : floats Override the computed coefficients of the :math:`R(A_{H})` relationship. The default are None. mlyr : MeltingLayer Class, optional Melting layer class containing the top of the melting layer, (i.e., the melting level) and its thickness. Only gates below the melting layer bottom (i.e. the rain region below the melting layer) are included in the computation; ml_top and ml_thickness can be either a single value (float, int), or an array (or list) of values corresponding to each azimuth angle of the scan. If None, the rainfall estimator is applied to the whole PPI scan. beam_height : array, optional Height of the centre of the radar beam, in km. Returns ------- R : dict Computed rain rates, in mm/h. Math ---- .. [Eq.1] .. math:: R = aA_H^b where R in mm/h, AH in dB/km Notes ----- Standard values according to [1]_. References ---------- .. [1] Ryzhkov, A., Diederich, M., Zhang, P., & Simmer, C. (2014). "Potential Utilization of Specific Attenuation for Rainfall Estimation, Mitigation of Partial Beam Blockage, and Radar Networking" Journal of Atmospheric and Oceanic Technology, 31(3), 599-619. https://doi.org/10.1175/JTECH-D-13-00038.1 """ if a is None and b is None: # Default values for the temp temps = np.array((0, 10, 20, 30)) # Default values for S-C and X-band radars coeffs_a = {'X': np.array((49.1, 45.5, 43.5, 43.0)), 'C': np.array((221, 250, 294, 352)), 'S': np.array((2.23e3, 3.10e3, 4.12e3, 5.33e3))} coeffs_b = {'X': np.array((0.87, 0.83, 0.79, 0.76)), 'C': np.array((0.92, 0.91, 0.89, 0.89)), 'S': np.array((1.03, 1.03, 1.03, 1.03))} # Interpolate the temp and coeffs to set coeffs a and b icoeff_a = interp1d(temps, coeffs_a.get(rband)) icoeff_b = interp1d(temps, coeffs_b.get(rband)) coeff_a = icoeff_a(temp).item() coeff_b = icoeff_b(temp).item() else: coeff_a = a coeff_b = b # print(f'{coeff_a}, {coeff_b}') ahr = np.zeros_like(ah)+ah if mlyr is not None and beam_height is not None: mlyr_bottom = mlyr.ml_top - mlyr.ml_thickness if isinstance(mlyr_bottom, (int, float)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom) for nbh in beam_height] elif isinstance(mlyr_bottom, (np.ndarray, list, tuple)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom[cnt]) for cnt, nbh in enumerate(beam_height)] for cnt, azi in enumerate(ahr): azi[mlb_idx[cnt]:] = 0 nanidx = np.where(np.isnan(ah)) ahr[nanidx] = np.nan r = {'Rainfall [mm/h]': coeff_a*ahr**coeff_b} r['coeff_a'] = coeff_a r['coeff_b'] = coeff_b self.r_ah = r
[docs] def kdp_to_r(self, kdp, a=24.68, b=0.81, beam_height=None, mlyr=None): r""" Compute rain rates using the :math:`R(K_{DP})` estimator [Eq.1]_. Parameters ---------- kdp : float or array Floats that corresponds to specific differential phase, in deg/km. a, b : floats Parameters of the :math:`R(K_{DP})` relationship beam_height : array, optional Height of the centre of the radar beam, in km. mlyr : MeltingLayer Class, optional Melting layer class containing the top of the melting layer, (i.e., the melting level) and its thickness. Only gates below the melting layer bottom (i.e. the rain region below the melting layer) are included in the computation; ml_top and ml_thickness can be either a single value (float, int), or an array (or list) of values corresponding to each azimuth angle of the scan. If None, the rainfall estimator is applied to the whole PPI scan. Returns ------- R : dict Computed rain rates, in mm/h. Math ---- .. [Eq.1] .. math:: R(K_{DP}) = aK_{DP}^b where R in mm/h and :math:`K_{DP}` in deg/km. Notes ----- Standard values according to [1]_. References ---------- .. [1] Bringi, V.N., Rico-Ramirez, M.A., Thurai, M. (2011). "Rainfall estimation with an operational polarimetric C-band radar in the United Kingdom: Comparison with a gauge network and error analysis" Journal of Hydrometeorology 12, 935–954. https://doi.org/10.1175/JHM-D-10-05013.1 """ kdpr = np.zeros_like(kdp)+kdp if mlyr is not None and beam_height is not None: mlyr_bottom = mlyr.ml_top - mlyr.ml_thickness if isinstance(mlyr_bottom, (int, float)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom) for nbh in beam_height] elif isinstance(mlyr_bottom, (np.ndarray, list, tuple)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom[cnt]) for cnt, nbh in enumerate(beam_height)] for cnt, azi in enumerate(kdpr): azi[mlb_idx[cnt]:] = 0 nanidx = np.where(np.isnan(kdp)) kdpr[nanidx] = np.nan r = {'Rainfall [mm/h]': a*abs(kdpr)**b*np.sign(kdpr)} r['coeff_a'] = a r['coeff_b'] = b self.r_kdp = r
[docs] def kdp_zdr_to_r(self, kdp, zdr, a=37.9, b=0.89, c=-0.72, beam_height=None, mlyr=None): r""" Compute rain rates using the :math:`R(K_{DP}, Z_{dr})` estimator [Eq.1]_. Parameters ---------- kdp : float or array Floats that corresponds to specific differential phase, in deg/km. zdr : float or array Floats that corresponds to differential reflectivity, in dB. a, b, c : floats Parameters of the :math:`R(K_{DP}, Z_{dr})` relationship beam_height : array, optional Height of the centre of the radar beam, in km. mlyr : MeltingLayer Class, optional Melting layer class containing the top of the melting layer, (i.e., the melting level) and its thickness. Only gates below the melting layer bottom (i.e. the rain region below the melting layer) are included in the computation; ml_top and ml_thickness can be either a single value (float, int), or an array (or list) of values corresponding to each azimuth angle of the scan. If None, the rainfall estimator is applied to the whole PPI scan. Returns ------- R : dict Computed rain rates, in mm/h. Math ---- .. [Eq.1] .. math:: R = aK_{DP}^b Z_{dr}^c where R in mm/h, :math:`K_{DP}` in deg/km, :math:`Z_{dr} = 10^{0.1*Z_{DR}}` and :math:`Z_{DR}` in dB. Notes ----- Standard values according to [1]_ References ---------- .. [1] Bringi, V.N., Chandrasekar, V., (2001). "Polarimetric Doppler Weather Radar" Cambridge University Press, Cambridge ; New York. https://doi.org/10.1017/CBO9780511541094 """ kdpr = np.zeros_like(kdp)+kdp zdrl = tpuc.xdb2x(zdr) if mlyr is not None and beam_height is not None: mlyr_bottom = mlyr.ml_top - mlyr.ml_thickness if isinstance(mlyr_bottom, (int, float)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom) for nbh in beam_height] elif isinstance(mlyr_bottom, (np.ndarray, list, tuple)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom[cnt]) for cnt, nbh in enumerate(beam_height)] for cnt, azi in enumerate(kdpr): azi[mlb_idx[cnt]:] = 0 nanidx = np.where(np.isnan(kdp)) kdpr[nanidx] = np.nan r = {'Rainfall [mm/h]': (a*kdpr**b)*(zdrl**c)} r['coeff_a'] = a r['coeff_b'] = b r['coeff_c'] = c self.r_kdp_zdr = r
[docs] def z_to_r(self, zh, a=200, b=1.6, beam_height=None, mlyr=None): r""" Compute rain rates using the :math:`R(Z_h)` estimator [Eq.1]_. Parameters ---------- zh : float or array Floats that corresponds to reflectivity, in dBZ. a, b : float Parameters of the :math:`R(Z_h)` relationship. beam_height : array, optional Height of the centre of the radar beam, in km, corresponding to each azimuth angle of the scan. mlyr : MeltingLayer Class, optional Melting layer class containing the top of the melting layer, (i.e., the melting level) and its thickness. Only gates below the melting layer bottom (i.e. the rain region below the melting layer) are included in the computation; ml_top and ml_thickness can be either a single value (float, int), or an array (or list) of values corresponding to each azimuth angle of the scan. If None, the rainfall estimator is applied to the whole PPI scan. Returns ------- r_z : dict Computed rain rates, in mm/h. Math ---- .. [Eq.1] .. math:: Z_h = aR^b where R in mm/h, :math:`Z_h = 10^{0.1*Z_H}`, :math:`Z_h` in :math:`mm^6 \cdot m^{-3}` Notes ----- Standard values according to [1]_. References ---------- .. [1] Marshall, J.S., Palmer, W.M.K., 1948. "The distribution of raindrops with size. Journal of Meteorology 5, 165–166. https://doi.org/10.1175/1520-0469(1948)005<0165:TDORWS>2.0.CO;2 """ zhl = tpuc.xdb2x(zh) if mlyr is not None and beam_height is not None: mlyr_bottom = mlyr.ml_top - mlyr.ml_thickness if isinstance(mlyr_bottom, (int, float)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom) for nbh in beam_height] elif isinstance(mlyr_bottom, (np.ndarray, list, tuple)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom[cnt]) for cnt, nbh in enumerate(beam_height)] for cnt, azi in enumerate(zhl): azi[mlb_idx[cnt]:] = 0 nanidx = np.where(np.isnan(zh)) zhl[nanidx] = np.nan r = {'Rainfall [mm/h]': (zhl/a)**(1/b)} r['coeff_a'] = a r['coeff_b'] = b self.r_z = r
[docs] def z_zdr_to_r(self, zh, zdr, a=0.0058, b=0.91, c=-2.09, beam_height=None, mlyr=None): r""" Compute rain rates using the :math:`R(Z_h, Z_{dr})` estimator [Eq.1]_. Parameters ---------- zh : float or array Floats that corresponds to reflectivity, in dBZ. zdr : float or array Floats that corresponds to differential reflectivity, in dB. a, b, c : floats Parameters of the :math:`R(Z_h, Z_{dr})` relationship. beam_height : array, optional Height of the centre of the radar beam, in km. mlyr : MeltingLayer Class, optional Melting layer class containing the top of the melting layer, (i.e., the melting level) and its thickness. Only gates below the melting layer bottom (i.e. the rain region below the melting layer) are included in the computation; ml_top and ml_thickness can be either a single value (float, int), or an array (or list) of values corresponding to each azimuth angle of the scan. If None, the rainfall estimator is applied to the whole PPI scan. Returns ------- r_z_zdr : dict Computed rain rates, in mm/h. Math ---- .. [Eq.1] .. math:: R(Z_h, Z_{dr}) = aZ_h^b Z_{dr}^c where R in mm/h, :math:`Z_h = 10^{0.1*Z_H}`, :math:`Z_H` in dBZ, :math:`Z_h` in :math:`mm^6 m^{-3}`, :math:`Z_{dr} = 10^{0.1*Z_{DR}}` and :math:`Z_{DR}` in dB. Notes ----- Standard values according to [1]_. References ---------- .. [1] Bringi, V.N., Chandrasekar, V., 2001. Polarimetric Doppler Weather Radar. Cambridge University Press, Cambridge, New York, http://dx.doi.org/10.1017/cbo9780511541094. """ zhl = tpuc.xdb2x(zh) zdrl = tpuc.xdb2x(zdr) if mlyr is not None and beam_height is not None: mlyr_bottom = mlyr.ml_top - mlyr.ml_thickness if isinstance(mlyr_bottom, (int, float)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom) for nbh in beam_height] elif isinstance(mlyr_bottom, (np.ndarray, list, tuple)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom[cnt]) for cnt, nbh in enumerate(beam_height)] for cnt, azi in enumerate(zhl): azi[mlb_idx[cnt]:] = 0 nanidx = np.where(np.isnan(zh)) zhl[nanidx] = np.nan r = {'Rainfall [mm/h]': (a*zhl**b)*(zdrl**c)} r['coeff_a'] = a r['coeff_b'] = b r['coeff_c'] = c self.r_z_zdr = r
[docs] def z_ah_to_r(self, zh, ah, rz_a=200, rz_b=1.6, rah_a=None, rah_b=None, rband='C', temp=20., z_thld=40, beam_height=None, mlyr=None): r""" Compute rain rates using an hybrid estimator that combines :math:`R(Z_h)` [Eq.1]_ and :math:`R(A_H)` [Eq.2]_ for a given threshold in :math:`Z_H`. Parameters ---------- zh : float or array Floats that corresponds to reflectivity, in dBZ. ah : float or array Floats that corresponds to specific attenuation, in dB/km. rz_a, rz_b : float Parameters of the :math:`R(Z_h)` relationship. rah_a, rah_b : floats Override the computed coefficients of the :math:`R(A_{H})` relationship. The default are None. rband: str Frequency band according to the wavelength of the radar. The string has to be one of 'S', 'C' or 'X'. The default is 'C'. temp: float Temperature, in :math:`^{\circ}C`, used to derive the coefficients rah_a, rah_b according to [1]_. The default is 20. z_thld : float, optional :math:`Z_H` threshold used for the transition to :math:`R(A_{H})`. The default is 40 dBZ. beam_height : array, optional Height of the centre of the radar beam, in km. mlyr : MeltingLayer Class, optional Melting layer class containing the top of the melting layer, (i.e., the melting level) and its thickness. Only gates below the melting layer bottom (i.e. the rain region below the melting layer) are included in the computation; ml_top and ml_thickness can be either a single value (float, int), or an array (or list) of values corresponding to each azimuth angle of the scan. If None, the rainfall estimator is applied to the whole PPI scan. Returns ------- R : dict Computed rain rates, in mm/h. Math ---- .. [Eq.1] .. math:: Z_H < 40 dBZ \rightarrow Z_h = aR^b .. [Eq.2] .. math:: Z_H \geq 40 dBZ \rightarrow R = aA_{H}^b where R in mm/h, :math:`Z_h = 10^{0.1*Z_H}`, :math:`Z_h` in :math:`mm^6 \cdot m^{-3}`, :math:`A_H` in dB/km Notes ----- Standard values according to [1]_ and [2]_. References ---------- .. [1] Marshall, J.S., Palmer, W.M.K., 1948. "The distribution of raindrops with size. Journal of Meteorology 5, 165–166. https://doi.org/10.1175/1520-0469(1948)005<0165:TDORWS>2.0.CO;2 .. [2] Ryzhkov, A., Diederich, M., Zhang, P., & Simmer, C. (2014). "Potential Utilization of Specific Attenuation for Rainfall Estimation, Mitigation of Partial Beam Blockage, and Radar Networking" Journal of Atmospheric and Oceanic Technology, 31(3), 599-619. https://doi.org/10.1175/JTECH-D-13-00038.1 """ if rah_a is None and rah_b is None: # Default values for the temp temps = np.array((0, 10, 20, 30)) # Default values for S-C and X-band radars coeffs_a = {'X': np.array((49.1, 45.5, 43.5, 43.0)), 'C': np.array((221, 250, 294, 352)), 'S': np.array((2.23e3, 3.10e3, 4.12e3, 5.33e3))} coeffs_b = {'X': np.array((0.87, 0.83, 0.79, 0.76)), 'C': np.array((0.92, 0.91, 0.89, 0.89)), 'S': np.array((1.03, 1.03, 1.03, 1.03))} # Interpolate the temp and coeffs to set coeffs a and b icoeff_a = interp1d(temps, coeffs_a.get(rband)) icoeff_b = interp1d(temps, coeffs_b.get(rband)) coeff_a = icoeff_a(temp).item() coeff_b = icoeff_b(temp).item() else: coeff_a = rah_a coeff_b = rah_b zh = np.array(zh) zhl = tpuc.xdb2x(zh) ahr = np.zeros_like(ah)+ah if mlyr is not None and beam_height is not None: mlyr_bottom = mlyr.ml_top - mlyr.ml_thickness if isinstance(mlyr_bottom, (int, float)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom) for nbh in beam_height] elif isinstance(mlyr_bottom, (np.ndarray, list, tuple)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom[cnt]) for cnt, nbh in enumerate(beam_height)] for cnt, azi in enumerate(zhl): azi[mlb_idx[cnt]:] = 0 for cnt, azi in enumerate(ahr): azi[mlb_idx[cnt]:] = 0 nanidx = np.where(np.isnan(zh)) zhl[nanidx] = np.nan nanidx = np.where(np.isnan(ah)) ahr[nanidx] = np.nan rzh = (zhl/rz_a)**(1/rz_b) rah = coeff_a*ahr**coeff_b rzh[(zh >= z_thld)] = rah[(zh >= z_thld)] r = {'Rainfall [mm/h]': rzh} r['coeff_arz'] = rz_a r['coeff_brz'] = rz_b r['coeff_arah'] = coeff_a r['coeff_brah'] = coeff_b self.r_z_ah = r
[docs] def z_kdp_to_r(self, zh, kdp, rz_a=200, rz_b=1.6, rkdp_a=24.68, rkdp_b=0.81, z_thld=40, beam_height=None, mlyr=None): r""" Compute rain rates using an hybrid estimator that combines :math:`R(Z_h)` [Eq.1]_ and :math:`R(K_{DP})` [Eq.2]_ for a given threshold in :math:`Z_H`. Parameters ---------- zh : float or array Floats that corresponds to reflectivity, in dBZ. kdp : float or array Floats that corresponds to specific differential phase, in deg/km. rz_a, rz_ab : float Parameters of the :math:`R(Z_h)` relationship. rkdp_a, rkdp_b : floats Parameters of the :math:`R(K_{DP})` relationship. z_thld : float, optional :math:`Z_H` threshold used for the transition to :math:`R(K_{DP})`. The default is 40 dBZ. beam_height : array, optional Height of the centre of the radar beam, in km. mlyr : MeltingLayer Class, optional Melting layer class containing the top of the melting layer, (i.e., the melting level) and its thickness. Only gates below the melting layer bottom (i.e. the rain region below the melting layer) are included in the computation; ml_top and ml_thickness can be either a single value (float, int), or an array (or list) of values corresponding to each azimuth angle of the scan. If None, the rainfall estimator is applied to the whole PPI scan. Returns ------- R : dict Computed rain rates, in mm/h. Math ---- .. [Eq.1] .. math:: Z_H < 40 dBZ \rightarrow Z_h = aR^b .. [Eq.2] .. math:: Z_H \geq 40 dBZ \rightarrow R = aK_{DP}^b where R in mm/h, :math:`Z_h = 10^{0.1*Z_H}`, :math:`Z_h` in :math:`mm^6 \cdot m^{-3}`, :math:`K_{DP}` in deg/km Notes ----- Standard values according to [1]_ and [2]_. References ---------- .. [1] Marshall, J.S., Palmer, W.M.K., 1948. "The distribution of raindrops with size. Journal of Meteorology 5, 165–166. https://doi.org/10.1175/1520-0469(1948)005<0165:TDORWS>2.0.CO;2 .. [2] Bringi, V.N., Rico-Ramirez, M.A., Thurai, M. (2011). "Rainfall estimation with an operational polarimetric C-band radar in the United Kingdom: Comparison with a gauge network and error analysis" Journal of Hydrometeorology 12, 935–954. https://doi.org/10.1175/JHM-D-10-05013.1 """ zh = np.array(zh) zhl = tpuc.xdb2x(zh) kdpr = np.zeros_like(kdp)+kdp if mlyr is not None and beam_height is not None: mlyr_bottom = mlyr.ml_top - mlyr.ml_thickness if isinstance(mlyr_bottom, (int, float)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom) for nbh in beam_height] elif isinstance(mlyr_bottom, (np.ndarray, list, tuple)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom[cnt]) for cnt, nbh in enumerate(beam_height)] for cnt, azi in enumerate(zhl): azi[mlb_idx[cnt]:] = 0 for cnt, azi in enumerate(kdpr): azi[mlb_idx[cnt]:] = 0 nanidx = np.where(np.isnan(zh)) zhl[nanidx] = np.nan nanidx = np.where(np.isnan(kdp)) kdpr[nanidx] = np.nan rzh = (zhl/rz_a)**(1/rz_b) rkdp = rkdp_a*abs(kdpr)**rkdp_b*np.sign(kdpr) rzh[(zh >= z_thld)] = rkdp[(zh >= z_thld)] r = {'Rainfall [mm/h]': rzh} r['coeff_arz'] = rz_a r['coeff_brz'] = rz_b r['coeff_arkdp'] = rkdp_a r['coeff_brkdp'] = rkdp_b self.r_z_kdp = r
[docs] def ah_kdp_to_r(self, zh, ah, kdp, rah_a=None, rah_b=None, rkdp_a=24.68, rkdp_b=0.81, rband='C', temp=20., z_thld=40, beam_height=None, mlyr=None): r""" Compute rain rates using an hybrid estimator that combines :math:`R(A_H)` [Eq.1]_ and :math:`R(K_{DP})` [Eq.2]_ for a given threshold in :math:`Z_H`. Parameters ---------- zh : float or array Floats that corresponds to reflectivity, in dBZ. ah : float or array Floats that corresponds to specific attenuation, in dB/km. kdp : float or array Floats that corresponds to specific differential phase, in deg/km. rah_a, rah_b : floats Override the computed coefficients of the :math:`R(A_{H})` relationship. The default are None. rkdp_a, rkdp_b : floats Parameters of the :math:`R(K_{DP})` relationship. rband: str Frequency band according to the wavelength of the radar. The string has to be one of 'S', 'C' or 'X'. The default is 'C'. temp: float Temperature, in :math:`^{\circ}C`, used to derive the coefficients rah_a, rah_b according to [1]_. The default is 20. z_thld : float, optional :math:`Z_H` threshold used for the transition from :math:`R(A_{H})` to :math:`R(K_{DP})`. The default is 40 dBZ. beam_height : array, optional Height of the centre of the radar beam, in km. mlyr : MeltingLayer Class, optional Melting layer class containing the top of the melting layer, (i.e., the melting level) and its thickness. Only gates below the melting layer bottom (i.e. the rain region below the melting layer) are included in the computation; ml_top and ml_thickness can be either a single value (float, int), or an array (or list) of values corresponding to each azimuth angle of the scan. If None, the rainfall estimator is applied to the whole PPI scan. Returns ------- R : dict Computed rain rates, in mm/h. Math ---- .. [Eq.1] .. math:: Z_H < 40 dBZ \rightarrow R = aA_{H}^b .. [Eq.2] .. math:: Z_H \geq 40 dBZ \rightarrow R = aK_{DP}^b where R in mm/h, :math:`Z_H` in dBZ, :math:`A_H` in dB/km, :math:`K_{DP}` in deg/km Notes ----- Standard values according to [1]_ and [2]_. References ---------- .. [1] Ryzhkov, A., Diederich, M., Zhang, P., & Simmer, C. (2014). "Potential Utilization of Specific Attenuation for Rainfall Estimation, Mitigation of Partial Beam Blockage, and Radar Networking" Journal of Atmospheric and Oceanic Technology, 31(3), 599-619. https://doi.org/10.1175/JTECH-D-13-00038.1 .. [2] Bringi, V.N., Rico-Ramirez, M.A., Thurai, M. (2011). "Rainfall estimation with an operational polarimetric C-band radar in the United Kingdom: Comparison with a gauge network and error analysis" Journal of Hydrometeorology 12, 935–954. https://doi.org/10.1175/JHM-D-10-05013.1 """ if rah_a is None and rah_b is None: # Default values for the temp temps = np.array((0, 10, 20, 30)) # Default values for S-C and X-band radars coeffs_a = {'X': np.array((49.1, 45.5, 43.5, 43.0)), 'C': np.array((221, 250, 294, 352)), 'S': np.array((2.23e3, 3.10e3, 4.12e3, 5.33e3))} coeffs_b = {'X': np.array((0.87, 0.83, 0.79, 0.76)), 'C': np.array((0.92, 0.91, 0.89, 0.89)), 'S': np.array((1.03, 1.03, 1.03, 1.03))} # Interpolate the temp and coeffs to set coeffs a and b icoeff_a = interp1d(temps, coeffs_a.get(rband)) icoeff_b = interp1d(temps, coeffs_b.get(rband)) coeff_a = icoeff_a(temp).item() coeff_b = icoeff_b(temp).item() else: coeff_a = rah_a coeff_b = rah_b zh = np.array(zh) ahr = np.zeros_like(ah)+ah kdpr = np.zeros_like(kdp)+kdp if mlyr is not None and beam_height is not None: mlyr_bottom = mlyr.ml_top - mlyr.ml_thickness if isinstance(mlyr_bottom, (int, float)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom) for nbh in beam_height] elif isinstance(mlyr_bottom, (np.ndarray, list, tuple)): mlb_idx = [rut.find_nearest(nbh, mlyr_bottom[cnt]) for cnt, nbh in enumerate(beam_height)] for cnt, azi in enumerate(zh): azi[mlb_idx[cnt]:] = 0 nanidx = np.where(np.isnan(ah)) ahr[nanidx] = np.nan nanidx = np.where(np.isnan(kdp)) kdpr[nanidx] = np.nan rah = coeff_a*ahr**coeff_b rkdp = rkdp_a*abs(kdpr)**rkdp_b*np.sign(kdpr) rah[(zh >= z_thld)] = rkdp[(zh >= z_thld)] # rkdp[(zh < z_thld)] = rah[(zh < z_thld)] r = {'Rainfall [mm/h]': rah} # r = {'Rainfall [mm/h]': rkdp} r['coeff_arah'] = coeff_a r['coeff_brah'] = coeff_b r['coeff_arkdp'] = rkdp_a r['coeff_brkdp'] = rkdp_b self.r_ah_kdp = r