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