cloudnetpy 1.49.9__py3-none-any.whl → 1.87.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.
- cloudnetpy/categorize/__init__.py +1 -2
- cloudnetpy/categorize/atmos_utils.py +297 -67
- cloudnetpy/categorize/attenuation.py +31 -0
- cloudnetpy/categorize/attenuations/__init__.py +37 -0
- cloudnetpy/categorize/attenuations/gas_attenuation.py +30 -0
- cloudnetpy/categorize/attenuations/liquid_attenuation.py +84 -0
- cloudnetpy/categorize/attenuations/melting_attenuation.py +78 -0
- cloudnetpy/categorize/attenuations/rain_attenuation.py +84 -0
- cloudnetpy/categorize/categorize.py +332 -156
- cloudnetpy/categorize/classify.py +127 -125
- cloudnetpy/categorize/containers.py +107 -76
- cloudnetpy/categorize/disdrometer.py +40 -0
- cloudnetpy/categorize/droplet.py +23 -21
- cloudnetpy/categorize/falling.py +53 -24
- cloudnetpy/categorize/freezing.py +25 -12
- cloudnetpy/categorize/insects.py +35 -23
- cloudnetpy/categorize/itu.py +243 -0
- cloudnetpy/categorize/lidar.py +36 -41
- cloudnetpy/categorize/melting.py +34 -26
- cloudnetpy/categorize/model.py +84 -37
- cloudnetpy/categorize/mwr.py +18 -14
- cloudnetpy/categorize/radar.py +215 -102
- cloudnetpy/cli.py +578 -0
- cloudnetpy/cloudnetarray.py +43 -89
- cloudnetpy/concat_lib.py +218 -78
- cloudnetpy/constants.py +28 -10
- cloudnetpy/datasource.py +61 -86
- cloudnetpy/exceptions.py +49 -20
- cloudnetpy/instruments/__init__.py +5 -0
- cloudnetpy/instruments/basta.py +29 -12
- cloudnetpy/instruments/bowtie.py +135 -0
- cloudnetpy/instruments/ceilo.py +138 -115
- cloudnetpy/instruments/ceilometer.py +164 -80
- cloudnetpy/instruments/cl61d.py +21 -5
- cloudnetpy/instruments/cloudnet_instrument.py +74 -36
- cloudnetpy/instruments/copernicus.py +108 -30
- cloudnetpy/instruments/da10.py +54 -0
- cloudnetpy/instruments/disdrometer/common.py +126 -223
- cloudnetpy/instruments/disdrometer/parsivel.py +453 -94
- cloudnetpy/instruments/disdrometer/thies.py +254 -87
- cloudnetpy/instruments/fd12p.py +201 -0
- cloudnetpy/instruments/galileo.py +65 -23
- cloudnetpy/instruments/hatpro.py +123 -49
- cloudnetpy/instruments/instruments.py +113 -1
- cloudnetpy/instruments/lufft.py +39 -17
- cloudnetpy/instruments/mira.py +268 -61
- cloudnetpy/instruments/mrr.py +187 -0
- cloudnetpy/instruments/nc_lidar.py +19 -8
- cloudnetpy/instruments/nc_radar.py +109 -55
- cloudnetpy/instruments/pollyxt.py +135 -51
- cloudnetpy/instruments/radiometrics.py +313 -59
- cloudnetpy/instruments/rain_e_h3.py +171 -0
- cloudnetpy/instruments/rpg.py +321 -189
- cloudnetpy/instruments/rpg_reader.py +74 -40
- cloudnetpy/instruments/toa5.py +49 -0
- cloudnetpy/instruments/vaisala.py +95 -343
- cloudnetpy/instruments/weather_station.py +774 -105
- cloudnetpy/metadata.py +90 -19
- cloudnetpy/model_evaluation/file_handler.py +55 -52
- cloudnetpy/model_evaluation/metadata.py +46 -20
- cloudnetpy/model_evaluation/model_metadata.py +1 -1
- cloudnetpy/model_evaluation/plotting/plot_tools.py +32 -37
- cloudnetpy/model_evaluation/plotting/plotting.py +327 -117
- cloudnetpy/model_evaluation/products/advance_methods.py +92 -83
- cloudnetpy/model_evaluation/products/grid_methods.py +88 -63
- cloudnetpy/model_evaluation/products/model_products.py +43 -35
- cloudnetpy/model_evaluation/products/observation_products.py +41 -35
- cloudnetpy/model_evaluation/products/product_resampling.py +17 -7
- cloudnetpy/model_evaluation/products/tools.py +29 -20
- cloudnetpy/model_evaluation/statistics/statistical_methods.py +30 -20
- cloudnetpy/model_evaluation/tests/e2e/conftest.py +3 -3
- cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +9 -5
- cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +15 -14
- cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +9 -5
- cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +15 -14
- cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +9 -5
- cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +15 -14
- cloudnetpy/model_evaluation/tests/unit/conftest.py +42 -41
- cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +41 -48
- cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +216 -194
- cloudnetpy/model_evaluation/tests/unit/test_model_products.py +23 -21
- cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +37 -38
- cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +43 -40
- cloudnetpy/model_evaluation/tests/unit/test_plotting.py +30 -36
- cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +68 -31
- cloudnetpy/model_evaluation/tests/unit/test_tools.py +33 -26
- cloudnetpy/model_evaluation/utils.py +2 -1
- cloudnetpy/output.py +170 -111
- cloudnetpy/plotting/__init__.py +2 -1
- cloudnetpy/plotting/plot_meta.py +562 -822
- cloudnetpy/plotting/plotting.py +1142 -704
- cloudnetpy/products/__init__.py +1 -0
- cloudnetpy/products/classification.py +370 -88
- cloudnetpy/products/der.py +85 -55
- cloudnetpy/products/drizzle.py +77 -34
- cloudnetpy/products/drizzle_error.py +15 -11
- cloudnetpy/products/drizzle_tools.py +79 -59
- cloudnetpy/products/epsilon.py +211 -0
- cloudnetpy/products/ier.py +27 -50
- cloudnetpy/products/iwc.py +55 -48
- cloudnetpy/products/lwc.py +96 -70
- cloudnetpy/products/mwr_tools.py +186 -0
- cloudnetpy/products/product_tools.py +170 -128
- cloudnetpy/utils.py +455 -240
- cloudnetpy/version.py +2 -2
- {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.87.3.dist-info}/METADATA +44 -40
- cloudnetpy-1.87.3.dist-info/RECORD +127 -0
- {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.87.3.dist-info}/WHEEL +1 -1
- cloudnetpy-1.87.3.dist-info/entry_points.txt +2 -0
- docs/source/conf.py +2 -2
- cloudnetpy/categorize/atmos.py +0 -361
- cloudnetpy/products/mwr_multi.py +0 -68
- cloudnetpy/products/mwr_single.py +0 -75
- cloudnetpy-1.49.9.dist-info/RECORD +0 -112
- {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.87.3.dist-info/licenses}/LICENSE +0 -0
- {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.87.3.dist-info}/top_level.txt +0 -0
cloudnetpy/products/der.py
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
"""Module for creating Cloudnet droplet effective radius
|
|
2
2
|
using the Frisch et al. 2002 method.
|
|
3
3
|
"""
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
from os import PathLike
|
|
6
|
+
from typing import NamedTuple
|
|
7
|
+
from uuid import UUID
|
|
5
8
|
|
|
6
9
|
import numpy as np
|
|
10
|
+
import numpy.typing as npt
|
|
7
11
|
from numpy import ma
|
|
8
12
|
|
|
9
13
|
from cloudnetpy import output, utils
|
|
10
|
-
from cloudnetpy.categorize import
|
|
14
|
+
from cloudnetpy.categorize import atmos_utils
|
|
11
15
|
from cloudnetpy.datasource import DataSource
|
|
16
|
+
from cloudnetpy.exceptions import InvalidSourceFileError
|
|
12
17
|
from cloudnetpy.metadata import MetaData
|
|
13
18
|
from cloudnetpy.products.product_tools import (
|
|
14
19
|
CategorizeBits,
|
|
@@ -16,17 +21,24 @@ from cloudnetpy.products.product_tools import (
|
|
|
16
21
|
get_is_rain,
|
|
17
22
|
)
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
|
|
25
|
+
class Parameters(NamedTuple):
|
|
26
|
+
ddBZ: float
|
|
27
|
+
N: float
|
|
28
|
+
dN: float
|
|
29
|
+
sigma_x: float
|
|
30
|
+
dsigma_x: float
|
|
31
|
+
dQ: float
|
|
20
32
|
|
|
21
33
|
|
|
22
34
|
def generate_der(
|
|
23
|
-
categorize_file: str,
|
|
24
|
-
output_file: str,
|
|
25
|
-
uuid: str | None = None,
|
|
35
|
+
categorize_file: str | PathLike,
|
|
36
|
+
output_file: str | PathLike,
|
|
37
|
+
uuid: str | UUID | None = None,
|
|
26
38
|
parameters: Parameters | None = None,
|
|
27
|
-
) ->
|
|
39
|
+
) -> UUID:
|
|
28
40
|
"""Generates Cloudnet effective radius of liquid water droplets
|
|
29
|
-
product
|
|
41
|
+
product according to Frisch et al. 2002.
|
|
30
42
|
|
|
31
43
|
This function calculates liquid droplet effective radius def
|
|
32
44
|
using the Frisch method. In this method, def is calculated
|
|
@@ -37,7 +49,7 @@ def generate_der(
|
|
|
37
49
|
categorize_file: Categorize file name.
|
|
38
50
|
output_file: Output file name.
|
|
39
51
|
uuid: Set specific UUID for the file.
|
|
40
|
-
parameters: Tuple of specific fixed
|
|
52
|
+
parameters: Tuple of specific fixed parameters
|
|
41
53
|
(ddBZ, N, dN, sigma_x, dsigma_x, dQ)
|
|
42
54
|
used in Frisch approach.
|
|
43
55
|
|
|
@@ -60,16 +72,16 @@ def generate_der(
|
|
|
60
72
|
https://doi.org/10.1175/1520-0426(2002)019%3C0835:TROSCD%3E2.0.CO;2
|
|
61
73
|
|
|
62
74
|
"""
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
uuid = utils.get_uuid(uuid)
|
|
76
|
+
with DerSource(categorize_file, parameters) as der_source:
|
|
77
|
+
droplet_classification = DropletClassification(categorize_file)
|
|
78
|
+
der_source.append_der()
|
|
79
|
+
der_source.append_retrieval_status(droplet_classification)
|
|
80
|
+
date = der_source.get_date()
|
|
81
|
+
attributes = output.add_time_attribute(REFF_ATTRIBUTES, date)
|
|
82
|
+
attributes = _add_der_error_comment(attributes, der_source)
|
|
83
|
+
output.update_attributes(der_source.data, attributes)
|
|
84
|
+
output.save_product_file("der", der_source, output_file, uuid)
|
|
73
85
|
return uuid
|
|
74
86
|
|
|
75
87
|
|
|
@@ -78,49 +90,53 @@ class DropletClassification(ProductClassification):
|
|
|
78
90
|
Child of ProductClassification().
|
|
79
91
|
"""
|
|
80
92
|
|
|
81
|
-
def __init__(self, categorize_file: str):
|
|
93
|
+
def __init__(self, categorize_file: str | PathLike) -> None:
|
|
82
94
|
super().__init__(categorize_file)
|
|
83
95
|
self.is_mixed = self._find_mixed()
|
|
84
96
|
self.is_droplet = self._find_droplet()
|
|
85
97
|
self.is_ice = self._find_ice()
|
|
86
98
|
|
|
87
|
-
def _find_droplet(self) ->
|
|
88
|
-
return self.category_bits
|
|
99
|
+
def _find_droplet(self) -> npt.NDArray:
|
|
100
|
+
return self.category_bits.droplet
|
|
89
101
|
|
|
90
|
-
def _find_mixed(self) ->
|
|
91
|
-
return self.category_bits
|
|
102
|
+
def _find_mixed(self) -> npt.NDArray:
|
|
103
|
+
return self.category_bits.falling & self.category_bits.droplet
|
|
92
104
|
|
|
93
|
-
def _find_ice(self) ->
|
|
105
|
+
def _find_ice(self) -> npt.NDArray:
|
|
94
106
|
return (
|
|
95
|
-
self.category_bits
|
|
96
|
-
& self.category_bits
|
|
97
|
-
& ~self.category_bits
|
|
98
|
-
& ~self.category_bits
|
|
99
|
-
& ~self.category_bits
|
|
107
|
+
self.category_bits.falling
|
|
108
|
+
& self.category_bits.freezing
|
|
109
|
+
& ~self.category_bits.melting
|
|
110
|
+
& ~self.category_bits.droplet
|
|
111
|
+
& ~self.category_bits.insect
|
|
100
112
|
)
|
|
101
113
|
|
|
102
114
|
|
|
103
115
|
class DerSource(DataSource):
|
|
104
116
|
"""Data container for effective radius calculations."""
|
|
105
117
|
|
|
106
|
-
def __init__(
|
|
118
|
+
def __init__(
|
|
119
|
+
self, categorize_file: str | PathLike, parameters: Parameters | None = None
|
|
120
|
+
) -> None:
|
|
107
121
|
super().__init__(categorize_file)
|
|
122
|
+
if "lwp" not in self.dataset.variables:
|
|
123
|
+
msg = "Liquid water path missing from the categorize file."
|
|
124
|
+
raise InvalidSourceFileError(msg)
|
|
108
125
|
self.is_rain = get_is_rain(categorize_file)
|
|
109
126
|
self.categorize_bits = CategorizeBits(categorize_file)
|
|
127
|
+
self.height_agl: npt.NDArray
|
|
110
128
|
if parameters is None:
|
|
111
129
|
# Default parameters from Frisch et al. 2002
|
|
112
130
|
self.parameters = Parameters(2.0, 200.0e6, 200.0e6, 0.35, 0.1, 5.0e-3)
|
|
113
131
|
else:
|
|
114
132
|
self.parameters = parameters
|
|
115
133
|
|
|
116
|
-
def append_der(self):
|
|
134
|
+
def append_der(self) -> None:
|
|
117
135
|
"""Estimate liquid droplet effective radius using Frisch et al. 2002."""
|
|
118
|
-
|
|
119
136
|
params = self.parameters
|
|
120
137
|
rho_l = 1000 # density of liquid water(kg m-3)
|
|
121
138
|
|
|
122
139
|
var_x = params.sigma_x * params.sigma_x
|
|
123
|
-
dheight = utils.mdiff(self.getvar("height"))
|
|
124
140
|
|
|
125
141
|
Z = self.getvar("Z")
|
|
126
142
|
Z = utils.db2lin(Z)
|
|
@@ -135,11 +151,17 @@ class DerSource(DataSource):
|
|
|
135
151
|
der_scaled_error = np.zeros(Z.shape)
|
|
136
152
|
N_scaled = np.zeros(Z.shape)
|
|
137
153
|
|
|
138
|
-
is_droplet = self.categorize_bits.category_bits
|
|
139
|
-
liquid_bases =
|
|
140
|
-
liquid_tops =
|
|
154
|
+
is_droplet = self.categorize_bits.category_bits.droplet
|
|
155
|
+
liquid_bases = atmos_utils.find_cloud_bases(is_droplet)
|
|
156
|
+
liquid_tops = atmos_utils.find_cloud_tops(is_droplet)
|
|
141
157
|
|
|
142
|
-
|
|
158
|
+
path_lengths = utils.path_lengths_from_ground(self.height_agl)
|
|
159
|
+
|
|
160
|
+
for base, top in zip(
|
|
161
|
+
zip(*np.where(liquid_bases), strict=True),
|
|
162
|
+
zip(*np.where(liquid_tops), strict=True),
|
|
163
|
+
strict=True,
|
|
164
|
+
):
|
|
143
165
|
ind_t = base[0]
|
|
144
166
|
idx_layer = np.arange(base[1], top[1] + 1)
|
|
145
167
|
idx_layer = idx_layer[~Z[ind_t, idx_layer].mask]
|
|
@@ -147,7 +169,7 @@ class DerSource(DataSource):
|
|
|
147
169
|
if Z[ind_t, idx_layer].mask.all():
|
|
148
170
|
continue
|
|
149
171
|
|
|
150
|
-
integral = ma.sum(ma.sqrt(Z[ind_t, idx_layer])) *
|
|
172
|
+
integral = ma.sum(ma.sqrt(Z[ind_t, idx_layer])) * path_lengths[idx_layer]
|
|
151
173
|
|
|
152
174
|
# der formula (5)
|
|
153
175
|
A = (Z[ind_t, idx_layer] / params.N) ** (1 / 6)
|
|
@@ -159,7 +181,7 @@ class DerSource(DataSource):
|
|
|
159
181
|
B = params.sigma_x * params.dsigma_x
|
|
160
182
|
C = dZ[ind_t, idx_layer] / (6 * Z[ind_t, idx_layer])
|
|
161
183
|
der_error[ind_t, idx_layer] = der[ind_t, idx_layer] * ma.sqrt(
|
|
162
|
-
A * A + B * B + C * C
|
|
184
|
+
A * A + B * B + C * C,
|
|
163
185
|
)
|
|
164
186
|
|
|
165
187
|
# der scaled formula (6)
|
|
@@ -176,7 +198,7 @@ class DerSource(DataSource):
|
|
|
176
198
|
B = 4 * params.sigma_x * params.dsigma_x
|
|
177
199
|
C = params.dQ / (3 * lwp[ind_t])
|
|
178
200
|
der_scaled_error[ind_t, idx_layer] = der_scaled[ind_t, idx_layer] * ma.sqrt(
|
|
179
|
-
A * A + B * B + C * C
|
|
201
|
+
A * A + B * B + C * C,
|
|
180
202
|
)
|
|
181
203
|
|
|
182
204
|
N_scaled = ma.masked_less_equal(ma.masked_invalid(N_scaled), 0.0) * 1.0e-6
|
|
@@ -197,13 +219,14 @@ class DerSource(DataSource):
|
|
|
197
219
|
self.append_data(der_scaled_error, "der_scaled_error")
|
|
198
220
|
|
|
199
221
|
def append_retrieval_status(
|
|
200
|
-
self,
|
|
222
|
+
self,
|
|
223
|
+
droplet_classification: DropletClassification,
|
|
201
224
|
) -> None:
|
|
202
225
|
"""Returns information about the status of der retrieval."""
|
|
203
226
|
is_retrieved = ~self.data["der"][:].mask
|
|
204
227
|
is_mixed = droplet_classification.is_mixed
|
|
205
228
|
is_ice = droplet_classification.is_ice
|
|
206
|
-
is_rain = np.tile(self.is_rain, (is_retrieved.shape[1], 1)).T
|
|
229
|
+
is_rain = np.tile(self.is_rain == 1, (is_retrieved.shape[1], 1)).T
|
|
207
230
|
|
|
208
231
|
retrieval_status = np.zeros(is_retrieved.shape, dtype=int)
|
|
209
232
|
retrieval_status[is_ice] = 4
|
|
@@ -214,17 +237,18 @@ class DerSource(DataSource):
|
|
|
214
237
|
|
|
215
238
|
|
|
216
239
|
DEFINITIONS = {
|
|
217
|
-
"der_retrieval_status": (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
240
|
+
"der_retrieval_status": utils.status_field_definition(
|
|
241
|
+
{
|
|
242
|
+
0: """No data: No cloud observed.""",
|
|
243
|
+
1: """Reliable retrieval.""",
|
|
244
|
+
2: """Mix of drops and ice: Droplets and ice crystals coexist within
|
|
245
|
+
pixel. Z may be biased by large crystals.""",
|
|
246
|
+
3: """Precipitation in profile: Drizzle and rain affects LWP
|
|
247
|
+
retrieval of MWR but also the target reflectivity.""",
|
|
248
|
+
4: """Surrounding ice: Less crucial! Ice crystals in the vicinity of
|
|
249
|
+
a droplet pixel may also bias its reflectivity.""",
|
|
250
|
+
}
|
|
251
|
+
),
|
|
228
252
|
}
|
|
229
253
|
|
|
230
254
|
|
|
@@ -271,7 +295,7 @@ def _add_der_error_comment(attributes: dict, der_source: DerSource) -> dict:
|
|
|
271
295
|
comment="This variable is an estimate of the random error in effective\n"
|
|
272
296
|
f"radius assuming an error in Z of ddBZ = {params.ddBZ} in N of\n"
|
|
273
297
|
f"dN = {params.dN} and in the spectral width dsigma_x = {params.dsigma_x}\n"
|
|
274
|
-
f"and in the LWP Q of {params.dQ} kg m-3."
|
|
298
|
+
f"and in the LWP Q of {params.dQ} kg m-3.",
|
|
275
299
|
)
|
|
276
300
|
return attributes
|
|
277
301
|
|
|
@@ -283,32 +307,38 @@ REFF_ATTRIBUTES = {
|
|
|
283
307
|
units="m",
|
|
284
308
|
ancillary_variables="der_error",
|
|
285
309
|
comment=COMMENTS["der"],
|
|
310
|
+
dimensions=("time", "height"),
|
|
286
311
|
),
|
|
287
312
|
"der_error": MetaData(
|
|
288
313
|
long_name="Absolute error in droplet effective radius",
|
|
289
314
|
units="m",
|
|
290
315
|
comment="",
|
|
316
|
+
dimensions=("time", "height"),
|
|
291
317
|
),
|
|
292
318
|
"der_scaled": MetaData(
|
|
293
319
|
long_name="Droplet effective radius (scaled to LWP)",
|
|
294
320
|
units="m",
|
|
295
321
|
ancillary_variables="der_scaled_error",
|
|
296
322
|
comment=COMMENTS["der_scaled"],
|
|
323
|
+
dimensions=("time", "height"),
|
|
297
324
|
),
|
|
298
325
|
"der_scaled_error": MetaData(
|
|
299
326
|
long_name="Absolute error in droplet effective radius (scaled to LWP)",
|
|
300
327
|
units="m",
|
|
301
328
|
comment=COMMENTS["der_scaled_error"],
|
|
329
|
+
dimensions=("time", "height"),
|
|
302
330
|
),
|
|
303
331
|
"N_scaled": MetaData(
|
|
304
332
|
long_name="Cloud droplet number concentration",
|
|
305
333
|
units="1",
|
|
306
334
|
ancillary_variables="der_error der_scaled der_scaled_error",
|
|
307
335
|
comment=COMMENTS["N_scaled"],
|
|
336
|
+
dimensions=("time", "height"),
|
|
308
337
|
),
|
|
309
338
|
"der_retrieval_status": MetaData(
|
|
310
339
|
long_name="Droplet effective radius retrieval status",
|
|
311
340
|
definition=DEFINITIONS["der_retrieval_status"],
|
|
312
341
|
units="1",
|
|
342
|
+
dimensions=("time", "height"),
|
|
313
343
|
),
|
|
314
344
|
}
|
cloudnetpy/products/drizzle.py
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
"""Module for creating Cloudnet drizzle product.
|
|
2
|
-
|
|
1
|
+
"""Module for creating Cloudnet drizzle product."""
|
|
2
|
+
|
|
3
|
+
from os import PathLike
|
|
4
|
+
from uuid import UUID
|
|
5
|
+
|
|
3
6
|
import numpy as np
|
|
7
|
+
import numpy.typing as npt
|
|
4
8
|
from numpy import ma
|
|
5
9
|
from scipy.special import gamma
|
|
6
10
|
|
|
@@ -16,8 +20,10 @@ from cloudnetpy.products.drizzle_tools import (
|
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
def generate_drizzle(
|
|
19
|
-
categorize_file: str
|
|
20
|
-
|
|
23
|
+
categorize_file: str | PathLike,
|
|
24
|
+
output_file: str | PathLike,
|
|
25
|
+
uuid: str | UUID | None = None,
|
|
26
|
+
) -> UUID:
|
|
21
27
|
"""Generates Cloudnet drizzle product.
|
|
22
28
|
|
|
23
29
|
This function calculates different drizzle properties from
|
|
@@ -41,6 +47,7 @@ def generate_drizzle(
|
|
|
41
47
|
J. Appl. Meteor., 44, 14–27, https://doi.org/10.1175/JAM-2181.1
|
|
42
48
|
|
|
43
49
|
"""
|
|
50
|
+
uuid = utils.get_uuid(uuid)
|
|
44
51
|
with DrizzleSource(categorize_file) as drizzle_source:
|
|
45
52
|
drizzle_class = DrizzleClassification(categorize_file)
|
|
46
53
|
spectral_width = SpectralWidth(categorize_file)
|
|
@@ -59,8 +66,8 @@ def generate_drizzle(
|
|
|
59
66
|
date = drizzle_source.get_date()
|
|
60
67
|
attributes = output.add_time_attribute(DRIZZLE_ATTRIBUTES, date)
|
|
61
68
|
output.update_attributes(drizzle_source.data, attributes)
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
output.save_product_file("drizzle", drizzle_source, output_file, uuid)
|
|
70
|
+
return uuid
|
|
64
71
|
|
|
65
72
|
|
|
66
73
|
class DrizzleProducts:
|
|
@@ -76,13 +83,15 @@ class DrizzleProducts:
|
|
|
76
83
|
|
|
77
84
|
"""
|
|
78
85
|
|
|
79
|
-
def __init__(
|
|
86
|
+
def __init__(
|
|
87
|
+
self, drizzle_source: DrizzleSource, drizzle_solver: DrizzleSolver
|
|
88
|
+
) -> None:
|
|
80
89
|
self._data = drizzle_source
|
|
81
90
|
self._params = drizzle_solver.params
|
|
82
91
|
self._ind_drizzle, self._ind_lut = self._find_indices()
|
|
83
92
|
self.derived_products = self._calc_derived_products()
|
|
84
93
|
|
|
85
|
-
def _find_indices(self):
|
|
94
|
+
def _find_indices(self) -> tuple:
|
|
86
95
|
drizzle_ind = np.where(self._params["Do"])
|
|
87
96
|
ind_mu = np.searchsorted(self._data.mie["mu"], self._params["mu"][drizzle_ind])
|
|
88
97
|
ind_dia = np.searchsorted(self._data.mie["Do"], self._params["Do"][drizzle_ind])
|
|
@@ -91,7 +100,7 @@ class DrizzleProducts:
|
|
|
91
100
|
ind_dia[ind_dia >= n_dia] = n_dia - 1
|
|
92
101
|
return drizzle_ind, (ind_mu, ind_dia)
|
|
93
102
|
|
|
94
|
-
def _calc_derived_products(self):
|
|
103
|
+
def _calc_derived_products(self) -> dict:
|
|
95
104
|
density = self._calc_density()
|
|
96
105
|
lwc = self._calc_lwc()
|
|
97
106
|
lwf = self._calc_lwf(lwc)
|
|
@@ -105,23 +114,23 @@ class DrizzleProducts:
|
|
|
105
114
|
"v_air": v_air,
|
|
106
115
|
}
|
|
107
116
|
|
|
108
|
-
def _calc_density(self):
|
|
117
|
+
def _calc_density(self) -> npt.NDArray:
|
|
109
118
|
"""Calculates drizzle number density (m-3)."""
|
|
110
119
|
a = self._data.z * 3.67**6
|
|
111
120
|
b = self._params["Do"] ** 6
|
|
112
121
|
return np.divide(a, b, out=np.zeros_like(a), where=b != 0)
|
|
113
122
|
|
|
114
|
-
def _calc_lwc(self):
|
|
115
|
-
"""Calculates drizzle liquid water content (kg m-3)"""
|
|
123
|
+
def _calc_lwc(self) -> npt.NDArray:
|
|
124
|
+
"""Calculates drizzle liquid water content (kg m-3)."""
|
|
116
125
|
rho_water = 1000
|
|
117
|
-
dia, mu, s =
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
dia, mu, s = (self._params.get(key) for key in ("Do", "mu", "S"))
|
|
127
|
+
dia = ma.array(dia)
|
|
128
|
+
mu = ma.array(mu)
|
|
129
|
+
s = ma.array(s)
|
|
121
130
|
gamma_ratio = gamma(4 + mu) / gamma(3 + mu) / (3.67 + mu)
|
|
122
131
|
return rho_water / 3 * self._data.beta * s * dia * gamma_ratio
|
|
123
132
|
|
|
124
|
-
def _calc_lwf(self, lwc_in):
|
|
133
|
+
def _calc_lwf(self, lwc_in: npt.NDArray) -> npt.NDArray:
|
|
125
134
|
"""Calculates drizzle liquid water flux."""
|
|
126
135
|
flux = ma.copy(lwc_in)
|
|
127
136
|
flux[self._ind_drizzle] *= (
|
|
@@ -130,13 +139,13 @@ class DrizzleProducts:
|
|
|
130
139
|
)
|
|
131
140
|
return flux
|
|
132
141
|
|
|
133
|
-
def _calc_fall_velocity(self):
|
|
142
|
+
def _calc_fall_velocity(self) -> npt.NDArray:
|
|
134
143
|
"""Calculates drizzle droplet fall velocity (m s-1)."""
|
|
135
144
|
velocity = np.zeros_like(self._params["Do"])
|
|
136
145
|
velocity[self._ind_drizzle] = -self._data.mie["v"][self._ind_lut]
|
|
137
146
|
return velocity
|
|
138
147
|
|
|
139
|
-
def _calc_v_air(self, droplet_velocity):
|
|
148
|
+
def _calc_v_air(self, droplet_velocity: npt.NDArray) -> npt.NDArray:
|
|
140
149
|
"""Calculates vertical air velocity."""
|
|
141
150
|
velocity = -np.copy(droplet_velocity)
|
|
142
151
|
velocity[self._ind_drizzle] += self._data.v[self._ind_drizzle]
|
|
@@ -155,42 +164,44 @@ class RetrievalStatus:
|
|
|
155
164
|
status information.
|
|
156
165
|
"""
|
|
157
166
|
|
|
158
|
-
def __init__(self, drizzle_class: DrizzleClassification):
|
|
167
|
+
def __init__(self, drizzle_class: DrizzleClassification) -> None:
|
|
159
168
|
self.drizzle_class = drizzle_class
|
|
160
|
-
self.retrieval_status:
|
|
169
|
+
self.retrieval_status: npt.NDArray = np.array([])
|
|
161
170
|
self._get_retrieval_status()
|
|
162
171
|
|
|
163
|
-
def _get_retrieval_status(self):
|
|
172
|
+
def _get_retrieval_status(self) -> None:
|
|
164
173
|
self.retrieval_status = np.copy(self.drizzle_class.drizzle).astype(int)
|
|
165
174
|
self._find_retrieval_below_melting()
|
|
166
175
|
self.retrieval_status[self.drizzle_class.would_be_drizzle == 1] = 3
|
|
167
176
|
self._find_retrieval_in_warm_liquid()
|
|
168
177
|
self.retrieval_status[self.drizzle_class.is_rain == 1, :] = 5
|
|
169
178
|
|
|
170
|
-
def _find_retrieval_below_melting(self):
|
|
179
|
+
def _find_retrieval_below_melting(self) -> None:
|
|
171
180
|
cold_rain = utils.transpose(self.drizzle_class.cold_rain)
|
|
172
181
|
below_melting = cold_rain * self.drizzle_class.drizzle
|
|
173
182
|
self.retrieval_status[below_melting == 1] = 2
|
|
174
183
|
|
|
175
|
-
def _find_retrieval_in_warm_liquid(self):
|
|
184
|
+
def _find_retrieval_in_warm_liquid(self) -> None:
|
|
176
185
|
in_warm_liquid = (self.retrieval_status == 0) * self.drizzle_class.warm_liquid
|
|
177
186
|
self.retrieval_status[in_warm_liquid == 1] = 4
|
|
178
187
|
|
|
179
188
|
|
|
180
|
-
def _screen_rain(results: dict, classification: DrizzleClassification):
|
|
189
|
+
def _screen_rain(results: dict, classification: DrizzleClassification) -> dict:
|
|
181
190
|
"""Removes rainy profiles from drizzle variables.."""
|
|
182
|
-
for key in results
|
|
191
|
+
for key in results:
|
|
183
192
|
if not utils.isscalar(results[key]):
|
|
184
193
|
results[key][classification.is_rain, :] = 0
|
|
185
194
|
return results
|
|
186
195
|
|
|
187
196
|
|
|
188
|
-
def _append_data(drizzle_data: DrizzleSource, results: dict):
|
|
197
|
+
def _append_data(drizzle_data: DrizzleSource, results: dict) -> None:
|
|
189
198
|
"""Save retrieved fields to the drizzle_data object."""
|
|
190
199
|
for key, value in results.items():
|
|
191
200
|
if key != "drizzle_retrieval_status":
|
|
192
|
-
|
|
193
|
-
|
|
201
|
+
arr = ma.masked_where(value == 0, value)
|
|
202
|
+
else:
|
|
203
|
+
arr = value
|
|
204
|
+
drizzle_data.append_data(arr, key)
|
|
194
205
|
|
|
195
206
|
|
|
196
207
|
DRIZZLE_ATTRIBUTES = {
|
|
@@ -198,95 +209,127 @@ DRIZZLE_ATTRIBUTES = {
|
|
|
198
209
|
long_name="Drizzle number concentration",
|
|
199
210
|
units="m-3",
|
|
200
211
|
ancillary_variables="drizzle_N_error drizzle_N_bias",
|
|
212
|
+
dimensions=("time", "height"),
|
|
201
213
|
),
|
|
202
214
|
"drizzle_N_error": MetaData(
|
|
203
|
-
long_name="Random error in drizzle number concentration",
|
|
215
|
+
long_name="Random error in drizzle number concentration",
|
|
216
|
+
units="dB",
|
|
217
|
+
dimensions=("time", "height"),
|
|
204
218
|
),
|
|
205
219
|
"drizzle_N_bias": MetaData(
|
|
206
220
|
long_name="Possible bias in drizzle number concentration",
|
|
207
221
|
units="dB",
|
|
222
|
+
dimensions=None,
|
|
208
223
|
),
|
|
209
224
|
"drizzle_lwc": MetaData(
|
|
210
225
|
long_name="Drizzle liquid water content",
|
|
211
226
|
units="kg m-3",
|
|
212
227
|
ancillary_variables="drizzle_lwc_error drizzle_lwc_bias",
|
|
228
|
+
standard_name="mass_concentration_of_drizzle_in_air",
|
|
229
|
+
dimensions=("time", "height"),
|
|
213
230
|
),
|
|
214
231
|
"drizzle_lwc_error": MetaData(
|
|
215
232
|
long_name="Random error in drizzle liquid water content",
|
|
216
233
|
units="dB",
|
|
234
|
+
dimensions=("time", "height"),
|
|
217
235
|
),
|
|
218
236
|
"drizzle_lwc_bias": MetaData(
|
|
219
237
|
long_name="Possible bias in drizzle liquid water content",
|
|
220
238
|
units="dB",
|
|
239
|
+
dimensions=None,
|
|
221
240
|
),
|
|
222
241
|
"drizzle_lwf": MetaData(
|
|
223
242
|
long_name="Drizzle liquid water flux",
|
|
224
243
|
units="kg m-2 s-1",
|
|
225
244
|
ancillary_variables="drizzle_lwf_error drizzle_lwf_bias",
|
|
245
|
+
dimensions=("time", "height"),
|
|
226
246
|
),
|
|
227
247
|
"drizzle_lwf_error": MetaData(
|
|
228
248
|
long_name="Random error in drizzle liquid water flux",
|
|
229
249
|
units="dB",
|
|
250
|
+
dimensions=("time", "height"),
|
|
230
251
|
),
|
|
231
252
|
"drizzle_lwf_bias": MetaData(
|
|
232
253
|
long_name="Possible bias in drizzle liquid water flux",
|
|
233
254
|
units="dB",
|
|
255
|
+
dimensions=None,
|
|
234
256
|
),
|
|
235
257
|
"v_drizzle": MetaData(
|
|
236
258
|
long_name="Drizzle droplet fall velocity", # TODO: should include 'terminal' ?
|
|
237
259
|
units="m s-1",
|
|
238
260
|
ancillary_variables="v_drizzle_error v_drizzle_bias",
|
|
239
261
|
comment="Positive values are towards the ground.",
|
|
262
|
+
dimensions=("time", "height"),
|
|
240
263
|
),
|
|
241
264
|
"v_drizzle_error": MetaData(
|
|
242
|
-
long_name="Random error in drizzle droplet fall velocity",
|
|
265
|
+
long_name="Random error in drizzle droplet fall velocity",
|
|
266
|
+
units="dB",
|
|
267
|
+
dimensions=("time", "height"),
|
|
243
268
|
),
|
|
244
269
|
"v_drizzle_bias": MetaData(
|
|
245
270
|
long_name="Possible bias in drizzle droplet fall velocity",
|
|
246
271
|
units="dB",
|
|
272
|
+
dimensions=None,
|
|
247
273
|
),
|
|
248
274
|
"v_air": MetaData(
|
|
249
275
|
long_name="Vertical air velocity",
|
|
250
276
|
units="m s-1",
|
|
251
277
|
ancillary_variables="v_air_error",
|
|
252
278
|
comment="Positive values are towards the sky.",
|
|
279
|
+
standard_name="upward_air_velocity",
|
|
280
|
+
dimensions=("time", "height"),
|
|
253
281
|
),
|
|
254
282
|
"v_air_error": MetaData(
|
|
255
|
-
long_name="Random error in vertical air velocity",
|
|
283
|
+
long_name="Random error in vertical air velocity",
|
|
284
|
+
units="dB",
|
|
285
|
+
dimensions=("time", "height"),
|
|
256
286
|
),
|
|
257
287
|
"Do": MetaData(
|
|
258
288
|
long_name="Drizzle median diameter",
|
|
259
289
|
units="m",
|
|
260
290
|
ancillary_variables="Do_error Do_bias",
|
|
291
|
+
dimensions=("time", "height"),
|
|
261
292
|
),
|
|
262
293
|
"Do_error": MetaData(
|
|
263
294
|
long_name="Random error in drizzle median diameter",
|
|
264
295
|
units="dB",
|
|
296
|
+
dimensions=("time", "height"),
|
|
265
297
|
),
|
|
266
298
|
"Do_bias": MetaData(
|
|
267
299
|
long_name="Possible bias in drizzle median diameter",
|
|
268
300
|
units="dB",
|
|
301
|
+
dimensions=None,
|
|
269
302
|
),
|
|
270
303
|
"mu": MetaData(
|
|
271
304
|
long_name="Drizzle droplet size distribution shape parameter",
|
|
272
305
|
ancillary_variables="mu_error",
|
|
273
306
|
units="1",
|
|
307
|
+
dimensions=("time", "height"),
|
|
274
308
|
),
|
|
275
309
|
"mu_error": MetaData(
|
|
276
310
|
long_name="Random error in drizzle droplet size distribution shape parameter",
|
|
277
311
|
units="dB",
|
|
312
|
+
dimensions=("time", "height"),
|
|
278
313
|
),
|
|
279
314
|
"S": MetaData(
|
|
280
315
|
long_name="Lidar backscatter-to-extinction ratio",
|
|
281
316
|
ancillary_variables="S_error",
|
|
282
317
|
units="sr",
|
|
318
|
+
dimensions=("time", "height"),
|
|
283
319
|
),
|
|
284
320
|
"S_error": MetaData(
|
|
285
321
|
long_name="Random error in lidar backscatter-to-extinction ratio",
|
|
286
322
|
units="dB",
|
|
323
|
+
dimensions=("time", "height"),
|
|
324
|
+
),
|
|
325
|
+
"beta_corr": MetaData(
|
|
326
|
+
long_name="Lidar backscatter correction factor",
|
|
327
|
+
units="1",
|
|
328
|
+
dimensions=("time", "height"),
|
|
287
329
|
),
|
|
288
|
-
"beta_corr": MetaData(long_name="Lidar backscatter correction factor", units="1"),
|
|
289
330
|
"drizzle_retrieval_status": MetaData(
|
|
290
|
-
long_name="Drizzle parameter retrieval status",
|
|
331
|
+
long_name="Drizzle parameter retrieval status",
|
|
332
|
+
units="1",
|
|
333
|
+
dimensions=("time", "height"),
|
|
291
334
|
),
|
|
292
335
|
}
|