disdrodb 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. disdrodb/__init__.py +64 -34
  2. disdrodb/_config.py +5 -4
  3. disdrodb/_version.py +16 -3
  4. disdrodb/accessor/__init__.py +20 -0
  5. disdrodb/accessor/methods.py +125 -0
  6. disdrodb/api/checks.py +139 -9
  7. disdrodb/api/configs.py +4 -2
  8. disdrodb/api/info.py +10 -10
  9. disdrodb/api/io.py +237 -18
  10. disdrodb/api/path.py +81 -75
  11. disdrodb/api/search.py +6 -6
  12. disdrodb/cli/disdrodb_create_summary_station.py +91 -0
  13. disdrodb/cli/disdrodb_run_l0.py +1 -1
  14. disdrodb/cli/disdrodb_run_l0_station.py +1 -1
  15. disdrodb/cli/disdrodb_run_l0b.py +1 -1
  16. disdrodb/cli/disdrodb_run_l0b_station.py +1 -1
  17. disdrodb/cli/disdrodb_run_l0c.py +1 -1
  18. disdrodb/cli/disdrodb_run_l0c_station.py +1 -1
  19. disdrodb/cli/disdrodb_run_l2e_station.py +1 -1
  20. disdrodb/configs.py +149 -4
  21. disdrodb/constants.py +61 -0
  22. disdrodb/data_transfer/download_data.py +145 -14
  23. disdrodb/etc/configs/attributes.yaml +339 -0
  24. disdrodb/etc/configs/encodings.yaml +473 -0
  25. disdrodb/etc/products/L1/global.yaml +13 -0
  26. disdrodb/etc/products/L2E/10MIN.yaml +12 -0
  27. disdrodb/etc/products/L2E/1MIN.yaml +1 -0
  28. disdrodb/etc/products/L2E/global.yaml +22 -0
  29. disdrodb/etc/products/L2M/10MIN.yaml +12 -0
  30. disdrodb/etc/products/L2M/GAMMA_ML.yaml +8 -0
  31. disdrodb/etc/products/L2M/NGAMMA_GS_LOG_ND_MAE.yaml +6 -0
  32. disdrodb/etc/products/L2M/NGAMMA_GS_ND_MAE.yaml +6 -0
  33. disdrodb/etc/products/L2M/NGAMMA_GS_Z_MAE.yaml +6 -0
  34. disdrodb/etc/products/L2M/global.yaml +26 -0
  35. disdrodb/l0/__init__.py +13 -0
  36. disdrodb/l0/configs/LPM/bins_diameter.yml +3 -3
  37. disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +4 -4
  38. disdrodb/l0/configs/PARSIVEL/l0b_cf_attrs.yml +1 -1
  39. disdrodb/l0/configs/PARSIVEL/l0b_encodings.yml +3 -3
  40. disdrodb/l0/configs/PARSIVEL/raw_data_format.yml +1 -1
  41. disdrodb/l0/configs/PARSIVEL2/l0a_encodings.yml +4 -0
  42. disdrodb/l0/configs/PARSIVEL2/l0b_cf_attrs.yml +20 -4
  43. disdrodb/l0/configs/PARSIVEL2/l0b_encodings.yml +44 -3
  44. disdrodb/l0/configs/PARSIVEL2/raw_data_format.yml +41 -1
  45. disdrodb/l0/configs/PWS100/l0b_cf_attrs.yml +4 -4
  46. disdrodb/l0/configs/PWS100/raw_data_format.yml +1 -1
  47. disdrodb/l0/l0a_processing.py +30 -30
  48. disdrodb/l0/l0b_nc_processing.py +108 -2
  49. disdrodb/l0/l0b_processing.py +4 -4
  50. disdrodb/l0/l0c_processing.py +5 -13
  51. disdrodb/l0/manuals/SWS250.pdf +0 -0
  52. disdrodb/l0/manuals/VPF730.pdf +0 -0
  53. disdrodb/l0/manuals/VPF750.pdf +0 -0
  54. disdrodb/l0/readers/LPM/NETHERLANDS/DELFT_LPM_NC.py +66 -0
  55. disdrodb/l0/readers/LPM/SLOVENIA/{CRNI_VRH.py → UL.py} +3 -0
  56. disdrodb/l0/readers/LPM/SWITZERLAND/INNERERIZ_LPM.py +195 -0
  57. disdrodb/l0/readers/PARSIVEL/GPM/PIERS.py +105 -0
  58. disdrodb/l0/readers/PARSIVEL/JAPAN/JMA.py +128 -0
  59. disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +1 -1
  60. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +1 -1
  61. disdrodb/l0/readers/PARSIVEL2/BELGIUM/ILVO.py +168 -0
  62. disdrodb/l0/readers/PARSIVEL2/DENMARK/DTU.py +165 -0
  63. disdrodb/l0/readers/PARSIVEL2/FINLAND/FMI_PARSIVEL2.py +69 -0
  64. disdrodb/l0/readers/PARSIVEL2/FRANCE/ENPC_PARSIVEL2.py +255 -134
  65. disdrodb/l0/readers/PARSIVEL2/FRANCE/OSUG.py +525 -0
  66. disdrodb/l0/readers/PARSIVEL2/FRANCE/SIRTA_PARSIVEL2.py +1 -1
  67. disdrodb/l0/readers/PARSIVEL2/GPM/GCPEX.py +9 -7
  68. disdrodb/l0/readers/{PARSIVEL → PARSIVEL2}/KIT/BURKINA_FASO.py +1 -1
  69. disdrodb/l0/readers/PARSIVEL2/KIT/TEAMX.py +123 -0
  70. disdrodb/l0/readers/PARSIVEL2/NASA/APU.py +120 -0
  71. disdrodb/l0/readers/PARSIVEL2/{NETHERLANDS/DELFT.py → NCAR/FARM_PARSIVEL2.py} +43 -70
  72. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +1 -1
  73. disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_MIPS.py +126 -0
  74. disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_PIPS.py +165 -0
  75. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +1 -1
  76. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +29 -12
  77. disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT_NC.py +69 -0
  78. disdrodb/l0/readers/PARSIVEL2/SPAIN/CENER.py +144 -0
  79. disdrodb/l0/readers/PARSIVEL2/SPAIN/CR1000DL.py +201 -0
  80. disdrodb/l0/readers/PARSIVEL2/SPAIN/LIAISE.py +137 -0
  81. disdrodb/l0/readers/PARSIVEL2/USA/C3WE.py +146 -0
  82. disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100.py +105 -99
  83. disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100_SIRTA.py +151 -0
  84. disdrodb/l0/readers/RD80/NOAA/PSL_RD80.py +31 -14
  85. disdrodb/l0/routines.py +105 -14
  86. disdrodb/l1/__init__.py +5 -0
  87. disdrodb/l1/filters.py +34 -20
  88. disdrodb/l1/processing.py +45 -44
  89. disdrodb/l1/resampling.py +77 -66
  90. disdrodb/l1/routines.py +35 -42
  91. disdrodb/l1_env/routines.py +18 -3
  92. disdrodb/l2/__init__.py +7 -0
  93. disdrodb/l2/empirical_dsd.py +58 -10
  94. disdrodb/l2/event.py +27 -120
  95. disdrodb/l2/processing.py +267 -116
  96. disdrodb/l2/routines.py +618 -254
  97. disdrodb/metadata/standards.py +3 -1
  98. disdrodb/psd/fitting.py +463 -144
  99. disdrodb/psd/models.py +8 -5
  100. disdrodb/routines.py +3 -3
  101. disdrodb/scattering/__init__.py +16 -4
  102. disdrodb/scattering/axis_ratio.py +56 -36
  103. disdrodb/scattering/permittivity.py +486 -0
  104. disdrodb/scattering/routines.py +701 -159
  105. disdrodb/summary/__init__.py +17 -0
  106. disdrodb/summary/routines.py +4120 -0
  107. disdrodb/utils/attrs.py +68 -125
  108. disdrodb/utils/compression.py +30 -1
  109. disdrodb/utils/dask.py +59 -8
  110. disdrodb/utils/dataframe.py +63 -9
  111. disdrodb/utils/directories.py +49 -17
  112. disdrodb/utils/encoding.py +33 -19
  113. disdrodb/utils/logger.py +13 -6
  114. disdrodb/utils/manipulations.py +71 -0
  115. disdrodb/utils/subsetting.py +214 -0
  116. disdrodb/utils/time.py +165 -19
  117. disdrodb/utils/writer.py +20 -7
  118. disdrodb/utils/xarray.py +85 -4
  119. disdrodb/viz/__init__.py +13 -0
  120. disdrodb/viz/plots.py +327 -0
  121. {disdrodb-0.1.1.dist-info → disdrodb-0.1.3.dist-info}/METADATA +3 -2
  122. {disdrodb-0.1.1.dist-info → disdrodb-0.1.3.dist-info}/RECORD +127 -87
  123. {disdrodb-0.1.1.dist-info → disdrodb-0.1.3.dist-info}/entry_points.txt +1 -0
  124. disdrodb/l1/encoding_attrs.py +0 -635
  125. disdrodb/l2/processing_options.py +0 -213
  126. /disdrodb/l0/readers/PARSIVEL/SLOVENIA/{UL_FGG.py → UL.py} +0 -0
  127. {disdrodb-0.1.1.dist-info → disdrodb-0.1.3.dist-info}/WHEEL +0 -0
  128. {disdrodb-0.1.1.dist-info → disdrodb-0.1.3.dist-info}/licenses/LICENSE +0 -0
  129. {disdrodb-0.1.1.dist-info → disdrodb-0.1.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,486 @@
1
+ # -----------------------------------------------------------------------------.
2
+ # Copyright (c) 2021-2023 DISDRODB developers
3
+ #
4
+ # temperaturehis program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # temperaturehis program is distributed in the hope that it will be useful,
10
+ # but WItemperatureHOUtemperature ANY WARRANtemperatureY; without even the implied warranty of
11
+ # MERCHANtemperatureABILItemperatureY or FItemperatureNESS FOR A PARtemperatureICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ # -----------------------------------------------------------------------------.
17
+ """Implement particle permittivity models."""
18
+ import numpy as np
19
+ import xarray as xr
20
+
21
+ # Definitions
22
+ # - Complex_refractive_index: m
23
+ # - Complex dielectric constant = complex relative permittivity: eps
24
+ # - Rayleigh dielectric factor: Kw_sqr
25
+
26
+ # Other useful codes for ice/snow future extension:
27
+ # - pytmatrix: https://github.com/ltelab/pytmatrix-lte/blob/main/pytmatrix/refractive.py#L66
28
+ # - pyradsim: https://github.com/wolfidan/pyradsim/blob/master/pyradsim/permittivity_models.py
29
+ # - cosmo_pol: https://github.com/wolfidan/cosmo_pol/blob/master/cosmo_pol/hydrometeors/dielectric.py#L49
30
+ # - m_func for snow, melting: https://github.com/wolfidan/cosmo_pol/blob/master/cosmo_pol/hydrometeors/hydrometeors.py#L544
31
+
32
+ ####-------------------------------------------------------------------------------------.
33
+ #### Wrappers
34
+
35
+
36
+ def available_permittivity_models():
37
+ """Return a list of the available raindrops complex refractive index models."""
38
+ return list(REFRACTIVE_INDEX_MODELS)
39
+
40
+
41
+ def get_refractive_index_function(permittivity_model):
42
+ """Return the specified model estimating the complex refractive index of rain drops.
43
+
44
+ The complex refractive index of a hydrometeor (e.g., water droplet, ice particle, graupel)
45
+ governs how radar waves interact with it.
46
+ The real part determines how much the radar wave slows down inside the particle (phase shift)
47
+ The imaginary part determines how much the radar wave is absorbed and attenuated by the particle
48
+ The imaginary part thus describes how much energy is lost as the wave travels through the particle.
49
+ The imaginary part thus controls radar attenuation and depolarization effects.
50
+ A large imaginary part leads to weaker returned signals, especially at shorter wavelengths.
51
+
52
+ The square root of the complex refractive index corresponds to the complex relative permittivity,
53
+ also known as the complex dielectric constant.
54
+
55
+ Parameters
56
+ ----------
57
+ model : str
58
+ The model to use for calculating the complex refractive index. Available models are:
59
+ 'Liebe1991', 'Liebe1991v2', 'Ellison2005', 'Turner2016', 'Turner2016SLC'.
60
+
61
+ Returns
62
+ -------
63
+ callable
64
+ A function which compute the complex refractive index for given temperature and frequency.
65
+
66
+ Notes
67
+ -----
68
+ This function serves as a wrapper to various complex refractive index models for raindrops.
69
+ It returns the appropriate model based on the `model` parameter.
70
+
71
+ """
72
+ permittivity_model = check_permittivity_model(permittivity_model)
73
+ return REFRACTIVE_INDEX_MODELS[permittivity_model]
74
+
75
+
76
+ def check_permittivity_model(permittivity_model):
77
+ """Check validity of the specified complex refractive index model."""
78
+ available_models = available_permittivity_models()
79
+ if permittivity_model not in available_models:
80
+ raise ValueError(f"{permittivity_model} is an invalid permittivity model. Valid models: {available_models}.")
81
+ return permittivity_model
82
+
83
+
84
+ def get_refractive_index(temperature, frequency, permittivity_model):
85
+ """
86
+ Compute the complex refractive index of raindrops using the specified permittivity model.
87
+
88
+ The complex refractive index of a hydrometeor (e.g., water droplet, ice particle, graupel)
89
+ governs how radar waves interact with it.
90
+ The real part determines how much the radar wave slows down inside the particle (phase shift)
91
+ The imaginary part determines how much the radar wave is absorbed and attenuated by the particle
92
+ The imaginary part thus describes how much energy is lost as the wave travels through the particle.
93
+ The imaginary part thus controls radar attenuation and depolarization effects.
94
+ A large imaginary part leads to weaker returned signals, especially at shorter wavelengths.
95
+
96
+ The square root of the complex refractive index corresponds to the complex relative permittivity,
97
+ also known as the complex dielectric constant.
98
+
99
+ Parameters
100
+ ----------
101
+ temperature : array-like
102
+ Temperature in degree Celsius.
103
+ frequency: float
104
+ Frequency in GHz.
105
+ permittivity_model : str
106
+ The permittivity model to use for calculating the complex refractive index.
107
+ Available models are: 'Liebe1991', 'Liebe1991v2', 'Ellison2005', 'Turner2016', 'Turner2016SLC'.
108
+ See available models with ``disdrodb.scattering.available_permittivity_models()``.
109
+
110
+ Returns
111
+ -------
112
+ m : array-like
113
+ Complex refractive index of raindrop at given temperature and frequency.
114
+
115
+ Notes
116
+ -----
117
+ This function serves as a wrapper to various permittivity models for raindrops.
118
+ It selects and applies the appropriate model based on the `permittivity_model` parameter.
119
+
120
+ Examples
121
+ --------
122
+ >>> temperature = np.array([0.5, 1.0, 2.0, 3.0])
123
+ >>> frequency = 5.6 # GhZ (C band)
124
+ >>> m = get_refractive_index(temperature=temperature, frequency=frequency, permittivity_model="Liebe1991")
125
+
126
+ """
127
+ # Retrieve refractive_index function
128
+ func = get_refractive_index_function(permittivity_model)
129
+
130
+ # Retrieve refractive_index
131
+ refractive_index = func(temperature=temperature, frequency=frequency)
132
+ return refractive_index
133
+
134
+
135
+ ####----------------------------------------------------------------------------------------
136
+ #### Liquid Water Refractive Index Models
137
+
138
+
139
+ def ensure_array(arr):
140
+ """Ensure data to be a numpy array or xarray DataArray."""
141
+ if isinstance(arr, xr.DataArray):
142
+ return arr
143
+ return np.asanyarray(arr)
144
+
145
+
146
+ def check_temperature_validity_range(temperature, vmin, vmax, permittivity_model):
147
+ """Check temperature validity range."""
148
+ if np.logical_or(temperature < vmin, temperature > vmax).any():
149
+ raise ValueError(
150
+ f"The {permittivity_model} refractive index model is only valid between {vmin} and {vmax} degree Celsius.",
151
+ )
152
+ return temperature
153
+
154
+
155
+ def check_frequency_validity_range(frequency, vmin, vmax, permittivity_model):
156
+ """Check frequency validity range."""
157
+ if np.logical_or(frequency < vmin, frequency > vmax).any():
158
+ raise ValueError(
159
+ f"The {permittivity_model} refractive index model is only valid between {vmin} and {vmax} GHz.",
160
+ )
161
+ return frequency
162
+
163
+
164
+ def get_rain_refractive_index_liebe1991_single(temperature, frequency):
165
+ """Compute the complex refractive index according to the single Debye model of Liebe et al. (1991).
166
+
167
+ Parameters
168
+ ----------
169
+ temperature : array-like
170
+ Temperature in degree Celsius.
171
+ frequency : array-like
172
+ Frequency in GHz.
173
+
174
+ Returns
175
+ -------
176
+ m : array-like
177
+ Complex refractive index at requested temperature and frequency.
178
+
179
+ Notes
180
+ -----
181
+ - The code of this function has been derived from RainSense code of Thomas van Leth available at
182
+ https://github.com/temperatureCvanLeth/RainSense/blob/master/rainsense/scattering.py#L149
183
+
184
+ References
185
+ ----------
186
+ H. J. Liebe, G. A. Hufford, and T. Manabe (1991).
187
+ A model for the complex permittivity of water at frequencies below 1 THz.
188
+ Journal of Atmospheric and Oceanic Technology, 27(2), 333-344.
189
+ Int. J. Infrared Millim. Waves, 12(7), 659-675.
190
+ https://doi.org/10.1007/BF01008897
191
+ """
192
+ # Ensure input is numpy array or xr.DataArray
193
+ frequency = ensure_array(frequency)
194
+ temperature = ensure_array(temperature)
195
+
196
+ # Check frequency and temperature within validity range
197
+ temperature = check_temperature_validity_range(temperature, vmin=0, vmax=100, permittivity_model="Liebe1991single")
198
+ frequency = check_frequency_validity_range(frequency, vmin=0, vmax=100, permittivity_model="Liebe1991single")
199
+
200
+ # Conversion of temperature to Kelvin
201
+ temperature = temperature + 273.15
202
+
203
+ # Compute static dielectric constant (eq. 1)
204
+ theta = 1 - 300 / temperature
205
+ eps_0 = 77.66 - 103.3 * theta
206
+
207
+ # Compute the complex dielectric constant (eq. 2)
208
+ eps_1 = 0.066 * eps_0
209
+ gamma_D = 20.27 + 146.5 * theta + 314 * theta**2
210
+ eps = (eps_0 - eps_1) / (1 - 1j * frequency / gamma_D) + eps_1
211
+
212
+ # Compute the refractive index
213
+ m = np.sqrt(eps)
214
+ return m
215
+
216
+
217
+ def get_rain_refractive_index_liebe1991(temperature, frequency):
218
+ """Compute the complex refractive index according to the double Debye model of Liebe et al. (1991).
219
+
220
+ Parameters
221
+ ----------
222
+ temperature : array-like
223
+ Temperature in degree Celsius.
224
+ frequency : array-like
225
+ Frequency in GHz.
226
+
227
+ Returns
228
+ -------
229
+ m : array-like
230
+ Complex refractive index at requested temperature and frequency.
231
+
232
+ Notes
233
+ -----
234
+ - The code of this function has been derived from pyradsim code of Daniel Wolfensberger available at
235
+ https://github.com/wolfidan/pyradsim/blob/master/pyradsim/permittivity_models.py#L37
236
+ - The Liebe et al. (1991) replaces the work of Ray et al. (1972).
237
+
238
+ References
239
+ ----------
240
+ H. J. Liebe, G. A. Hufford, and T. Manabe (1991).
241
+ A model for the complex permittivity of water at frequencies below 1 THz.
242
+ Journal of Atmospheric and Oceanic Technology, 27(2), 333-344.
243
+ Int. J. Infrared Millim. Waves, 12(7), 659-675.
244
+ https://doi.org/10.1007/BF01008897
245
+
246
+ Peter S. Ray (1972).
247
+ Broadband Complex Refractive Indices of Ice and Water.
248
+ Applied Optics, 11(8), 1836-1844.
249
+ https://doi.org/10.1364/AO.11.001836
250
+ """
251
+ # Ensure input is numpy array or xr.DataArray
252
+ frequency = ensure_array(frequency)
253
+ temperature = ensure_array(temperature)
254
+
255
+ # Check frequency and temperature within validity range
256
+ temperature = check_temperature_validity_range(temperature, vmin=0, vmax=40, permittivity_model="Liebe1991")
257
+ frequency = check_frequency_validity_range(frequency, vmin=0, vmax=1000, permittivity_model="Liebe1991")
258
+
259
+ # Conversion of temperature to Kelvin
260
+ temperature = temperature + 273.15
261
+
262
+ # Compute static dielectric constant (eq. 1)
263
+ theta = 1 - 300 / temperature
264
+ eps_0 = 77.66 - 103.3 * theta
265
+
266
+ # Compute the complex dielectric constant (e4, eq5)
267
+ eps_1 = 0.0671 * eps_0
268
+ eps_2 = 3.52 + 7.52 * theta
269
+ gamma_1 = 20.20 + 146.5 * theta + 316 * theta**2
270
+ gamma_2 = 39.8 * gamma_1
271
+
272
+ term1 = eps_0 - eps_1
273
+ term2 = 1 + (frequency / gamma_1) ** 2
274
+ term3 = 1 + (frequency / gamma_2) ** 2
275
+ term4 = eps_1 - eps_2
276
+ term5 = eps_2
277
+
278
+ eps_real = term1 / term2 + term4 / term3 + term5
279
+ eps_imag = (term1 / term2) * (frequency / gamma_1) + (term4 / term3) * (frequency / gamma_2)
280
+
281
+ eps = eps_real + 1j * eps_imag
282
+
283
+ # Compute the refractive index
284
+ m = np.sqrt(eps)
285
+ return m
286
+
287
+
288
+ def get_rain_refractive_index_ellison2005(temperature, frequency):
289
+ """Compute the complex refractive index according to Ellison (2005) model.
290
+
291
+ Parameters
292
+ ----------
293
+ temperature : array-like
294
+ Temperature in degree Celsius.
295
+ frequency : array-like
296
+ Frequency in GHz.
297
+
298
+ Returns
299
+ -------
300
+ m : array-like
301
+ Complex refractive index at requested temperature and frequency.
302
+
303
+ Notes
304
+ -----
305
+ - The model is designed to operate only up to 1000 GHz and temperature ranging from 0 degC to 100 degC.
306
+ - The code of this function has been derived from Davide Ori raincoat code available at
307
+ https://github.com/OPTIMICe-team/raincoat/blob/master/raincoat/scatTable/water.py#L160
308
+
309
+ References
310
+ ----------
311
+ W. J. Ellison (2007).
312
+ Permittivity of Pure Water, at Standard Atmospheric Pressure, over the
313
+ Frequency Range 0-25 THz and the Temperature Range 0-100 °C.
314
+ J. Phys. Chem. Ref. Data, 36, 1-18.
315
+ https://doi.org/10.1063/1.2360986
316
+ """
317
+ # Ensure input is numpy array or xr.DataArray
318
+ frequency = ensure_array(frequency)
319
+ temperature = ensure_array(temperature)
320
+
321
+ # Check frequency and temperature within validity range
322
+ temperature = check_temperature_validity_range(temperature, vmin=0, vmax=100, permittivity_model="Ellison2005")
323
+ frequency = check_frequency_validity_range(frequency, vmin=0, vmax=1000, permittivity_model="Ellison2005")
324
+
325
+ # Conversion of frequency to Hz
326
+ frequency = frequency / 1e-9
327
+
328
+ # Here below we assume temperature in Celsius, frequency in Hz
329
+ T = temperature
330
+
331
+ # Compute the complex dielectric constant
332
+ a0 = 5.7230
333
+ a1 = 2.2379e-2
334
+ a2 = -7.1237e-4
335
+ a3 = 5.0478
336
+ a4 = -7.0315e-2
337
+ a5 = 6.0059e-4
338
+ a6 = 3.6143
339
+ a7 = 2.8841e-2
340
+ a8 = 1.3652e-1
341
+ a9 = 1.4825e-3
342
+ a10 = 2.4166e-4
343
+
344
+ es = (37088.6 - 82.168 * T) / (421.854 + T)
345
+ einf = a6 + a7 * T
346
+ e1 = a0 + T * (a1 + T * a2) # a0+a1*T+a2*T*T
347
+ ni1 = (45.0 + T) / (a3 + T * (a4 + T * a5)) # (a3+a4*T+a5*T*T)
348
+ ni2 = (45.0 + T) / (a8 + T * (a9 + T * a10)) # (a8+a9*T+a10*T*T)
349
+ A1 = frequency * 1.0e-9 / ni1
350
+ A2 = frequency * 1.0e-9 / ni2
351
+
352
+ eps_real = (es - e1) / (1 + A1 * A1) + (e1 - einf) / (1 + A2 * A2) + einf
353
+ eps_imag = (es * A1 - e1 * A1) / (1 + A1 * A1) + (e1 * A2 - einf * A2) / (1 + A2 * A2)
354
+
355
+ eps = eps_real + 1j * eps_imag
356
+
357
+ # Compute the refractive index
358
+ m = np.sqrt(eps)
359
+ return m
360
+
361
+
362
+ def get_rain_refractive_index_turner2016(frequency, temperature):
363
+ """Compute the complex refractive index using the Turner-Kneifel-Cadeddu (TKC) model.
364
+
365
+ The TKC supercooled liquid water absorption model was built using both laboratory observations
366
+ (primarily at warm temperature) and field data observed by MWRs at multiple frequency at
367
+ supercool temperature. The field data were published in Kneifel et al. (2014).
368
+
369
+ The strength of the TKC model is the use of an optimal estimation framework to
370
+ determine the empirical coefficients of the double-Debye model.
371
+ A full description of this model is given in Turner et al. (2016).
372
+
373
+ Parameters
374
+ ----------
375
+ temperature : array-like
376
+ Temperature in degree Celsius.
377
+ frequency : array-like
378
+ Frequency in GHz.
379
+
380
+ Returns
381
+ -------
382
+ m : array-like
383
+ Complex refractive index at given temperature and frequency.
384
+
385
+ Notes
386
+ -----
387
+ - The code of this function has been checked against Joseph Hardin pyDSD and Davide Ori raincoat codes
388
+ available at:
389
+ https://github.com/josephhardinee/PyDSD/blob/main/pydsd/utility/dielectric.py#L36
390
+ https://github.com/OPTIMICe-team/raincoat/blob/master/raincoat/scatTable/water.py#L54
391
+
392
+ References
393
+ ----------
394
+ Turner, D.D., S. Kneifel, and M.P. Cadeddu (2016).
395
+ An improved liquid water absorption model in the microwave for supercooled liquid clouds.
396
+ J. Atmos. Oceanic Technol., 33(1), 33-44.
397
+ https://doi.org/10.1175/JTECH-D-15-0074.1.
398
+
399
+ Kneifel, S., S. Redl, E. Orlandi, U. Löhnert, M. P. Cadeddu, D. D. Turner, and M. Chen (2014).
400
+ Absorption Properties of Supercooled Liquid Water between 31 and 225 GHz:
401
+ Evaluation of Absorption Models Using Ground-Based Observations.
402
+ J. Appl. Meteor. Climatol., 53, 1028-1045.
403
+ https://doi.org/10.1175/JAMC-D-13-0214.1
404
+ """
405
+ # Ensure input is numpy array or xr.DataArray
406
+ frequency = ensure_array(frequency)
407
+ temperature = ensure_array(temperature)
408
+
409
+ # Check frequency and temperature within validity range
410
+ temperature = check_temperature_validity_range(temperature, vmin=-40, vmax=50, permittivity_model="Turner2016")
411
+ frequency = check_frequency_validity_range(frequency, vmin=0.5, vmax=500, permittivity_model="Turner2016")
412
+
413
+ # Conversion of frequency to Hz
414
+ frequency = frequency / 1e-9
415
+
416
+ # Define coefficients
417
+ a = [8.111e01, 2.025]
418
+ b = [4.434e-3, 1.073e-2]
419
+ c = [1.302e-13, 1.012e-14]
420
+ d = [6.627e02, 6.089e02]
421
+ tc = 1.342e2
422
+ s = [8.79144e1, -4.04399e-1, 9.58726e-4, -1.32802e-6]
423
+
424
+ def A_i(i, temperature, frequency):
425
+ """Compute the relaxation terms A_i (Eq 7) of the double Debye model."""
426
+ delta = a[i] * np.exp(-1 * b[i] * temperature) # (Eq 9)
427
+ tau = c[i] * np.exp(d[i] / (temperature + tc)) # (Eq 10)
428
+
429
+ return (tau**2 * delta) / (1 + (2 * np.pi * frequency * tau) ** 2) # (Eq 7)
430
+
431
+ def B_i(i, temperature, frequency):
432
+ """Compute the relaxation terms B_i (Eq 7) of the double Debye model."""
433
+ delta = a[i] * np.exp(-1 * b[i] * temperature) # (Eq 9)
434
+ tau = c[i] * np.exp(d[i] / (temperature + tc)) # (Eq 10)
435
+
436
+ return (tau * delta) / (1 + (2 * np.pi * frequency * tau) ** 2) # (Eq 8)
437
+
438
+ # Compute the static dielectric permittivity (Eq 6)
439
+ es = s[0] + s[1] * temperature + s[2] * temperature**2 + s[3] * temperature**3
440
+
441
+ # Compute the complex dielectric constant
442
+ eps_real = es - (2 * np.pi * frequency) ** 2 * (
443
+ A_i(0, temperature, frequency) + A_i(1, temperature, frequency)
444
+ ) # (Eq 4)
445
+ eps_imag = 2 * np.pi * frequency * (B_i(0, temperature, frequency) + B_i(1, temperature, frequency)) # (Eq 5)
446
+
447
+ eps = eps_real + 1j * eps_imag
448
+
449
+ # Compute the refractive index
450
+ m = np.sqrt(eps)
451
+ return m
452
+
453
+
454
+ ####----------------------------------------------------------------------------------------
455
+ def get_rayleigh_dielectric_factor(m):
456
+ """Compute the Rayleigh dielectric factor |K|**2 from the complex refractive index.
457
+
458
+ The magnitude squared of the complex dielectric contrast factor for liquid water,
459
+ relative to the surrounding medium (typically air).
460
+
461
+ This factor is used to compute the radar reflectivity.
462
+
463
+ Parameters
464
+ ----------
465
+ m : complex
466
+ Complex refractive index.
467
+
468
+ Returns
469
+ -------
470
+ float
471
+ Dielectric factor |K|^2 used in Rayleigh scattering.
472
+ Often also called the radar dieletric factor.
473
+ In pytmatrix, correspond to the Kw_sqr argument of the Scatterer object.
474
+ """
475
+ eps = m**2
476
+ K_complex = (eps - 1.0) / (eps + 2.0)
477
+ return np.abs(K_complex) ** 2
478
+
479
+
480
+ ####-------------------------------------------------------------------------------------.
481
+ REFRACTIVE_INDEX_MODELS = {
482
+ "Liebe1991": get_rain_refractive_index_liebe1991,
483
+ "Liebe1991single": get_rain_refractive_index_liebe1991_single,
484
+ "Ellison2005": get_rain_refractive_index_ellison2005,
485
+ "Turner2016": get_rain_refractive_index_turner2016,
486
+ }