cloudnetpy 1.82.3__py3-none-any.whl → 1.83.0__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/attenuations/melting_attenuation.py +7 -6
- cloudnetpy/datasource.py +2 -2
- cloudnetpy/instruments/nc_radar.py +2 -2
- cloudnetpy/model_evaluation/products/observation_products.py +1 -1
- cloudnetpy/plotting/plot_meta.py +24 -0
- cloudnetpy/plotting/plotting.py +68 -5
- cloudnetpy/products/classification.py +307 -71
- cloudnetpy/version.py +2 -2
- {cloudnetpy-1.82.3.dist-info → cloudnetpy-1.83.0.dist-info}/METADATA +1 -1
- {cloudnetpy-1.82.3.dist-info → cloudnetpy-1.83.0.dist-info}/RECORD +14 -14
- {cloudnetpy-1.82.3.dist-info → cloudnetpy-1.83.0.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.82.3.dist-info → cloudnetpy-1.83.0.dist-info}/entry_points.txt +0 -0
- {cloudnetpy-1.82.3.dist-info → cloudnetpy-1.83.0.dist-info}/licenses/LICENSE +0 -0
- {cloudnetpy-1.82.3.dist-info → cloudnetpy-1.83.0.dist-info}/top_level.txt +0 -0
@@ -13,14 +13,14 @@ def calc_melting_attenuation(
|
|
13
13
|
data: Observations, classification: ClassificationResult
|
14
14
|
) -> Attenuation:
|
15
15
|
shape = classification.category_bits.melting.shape
|
16
|
-
|
16
|
+
no_rain = classification.is_rain == 0
|
17
17
|
|
18
18
|
affected_region = classification.category_bits.freezing.copy()
|
19
19
|
|
20
20
|
if data.disdrometer is None:
|
21
|
-
affected_region[~is_rain, :] = False
|
22
21
|
above_melting = utils.ffill(classification.category_bits.melting)
|
23
22
|
affected_region[~above_melting] = False
|
23
|
+
affected_region[no_rain, :] = False
|
24
24
|
return Attenuation(
|
25
25
|
amount=ma.masked_all(shape),
|
26
26
|
error=ma.masked_all(shape),
|
@@ -29,22 +29,23 @@ def calc_melting_attenuation(
|
|
29
29
|
)
|
30
30
|
|
31
31
|
rainfall_rate = data.disdrometer.data["rainfall_rate"][:]
|
32
|
-
rainfall_rate
|
32
|
+
rainfall_rate = ma.where(no_rain, 0, rainfall_rate)
|
33
|
+
|
33
34
|
frequency = data.radar.radar_frequency
|
34
35
|
|
35
36
|
attenuation_array = _calc_melting_attenuation(rainfall_rate, frequency)
|
36
37
|
|
37
38
|
amount = affected_region * utils.transpose(attenuation_array)
|
38
39
|
|
39
|
-
|
40
|
+
no_attenuation = amount == 0
|
40
41
|
|
41
|
-
|
42
|
+
affected_region[no_attenuation] = False
|
43
|
+
amount[no_attenuation] = ma.masked
|
42
44
|
|
43
45
|
band = utils.get_wl_band(data.radar.radar_frequency)
|
44
46
|
error_factor = {"Ka": 0.2, "W": 0.1}[band]
|
45
47
|
|
46
48
|
error = amount * error_factor
|
47
|
-
error[~affected_region] = ma.masked
|
48
49
|
|
49
50
|
return Attenuation(
|
50
51
|
amount=amount,
|
cloudnetpy/datasource.py
CHANGED
@@ -74,14 +74,14 @@ class DataSource:
|
|
74
74
|
ndarray: The actual data.
|
75
75
|
|
76
76
|
Raises:
|
77
|
-
|
77
|
+
KeyError: The variable is not found.
|
78
78
|
|
79
79
|
"""
|
80
80
|
for arg in args:
|
81
81
|
if arg in self.dataset.variables:
|
82
82
|
return self.dataset.variables[arg][:]
|
83
83
|
msg = f"Missing variable {args[0]} in the input file."
|
84
|
-
raise
|
84
|
+
raise KeyError(msg)
|
85
85
|
|
86
86
|
def append_data(
|
87
87
|
self,
|
@@ -41,7 +41,7 @@ class NcRadar(DataSource, CloudnetInstrument):
|
|
41
41
|
name = keymap[key]
|
42
42
|
try:
|
43
43
|
array = self.getvar(key)
|
44
|
-
except
|
44
|
+
except KeyError:
|
45
45
|
logging.warning("Can not find variable %s from the input file", key)
|
46
46
|
continue
|
47
47
|
array = np.array(array) if utils.isscalar(array) else array
|
@@ -158,7 +158,7 @@ class NcRadar(DataSource, CloudnetInstrument):
|
|
158
158
|
self.data[key] = CloudnetArray(np.median(np.array(data)), key)
|
159
159
|
if "NyquistVelocity" in self.data:
|
160
160
|
del self.data["NyquistVelocity"]
|
161
|
-
except
|
161
|
+
except KeyError:
|
162
162
|
logging.warning("Unable to find nyquist_velocity")
|
163
163
|
|
164
164
|
def test_if_all_masked(self) -> None:
|
cloudnetpy/plotting/plot_meta.py
CHANGED
@@ -90,6 +90,12 @@ _CLABEL = {
|
|
90
90
|
("Clutter", _COLORS["shockred"]),
|
91
91
|
("_Lidar molecular scattering", _COLORS["pink"]),
|
92
92
|
),
|
93
|
+
"signal_source_status": (
|
94
|
+
("Clear sky", _COLORS["white"]),
|
95
|
+
("Radar & lidar", _COLORS["green"]),
|
96
|
+
("Radar only", _COLORS["lightsteel"]),
|
97
|
+
("Lidar only", _COLORS["yellow"]),
|
98
|
+
),
|
93
99
|
"ice_retrieval_status": (
|
94
100
|
("_No ice", _COLORS["white"]),
|
95
101
|
("Reliable", _COLORS["green"]),
|
@@ -124,6 +130,15 @@ _CLABEL = {
|
|
124
130
|
("Unfeasible", _COLORS["red"]),
|
125
131
|
("Surrounding ice", _COLORS["lightsteel"]),
|
126
132
|
),
|
133
|
+
"radar_attenuation_status": (
|
134
|
+
("_Clear sky", _COLORS["white"]),
|
135
|
+
("Negligible", _COLORS["green"]),
|
136
|
+
("Minor", _COLORS["lightgreen"]),
|
137
|
+
("Moderate", _COLORS["yellow"]),
|
138
|
+
("Severe", _COLORS["red"]),
|
139
|
+
("Unquantifiable", _COLORS["seaweed_roll"]),
|
140
|
+
("Undetected", _COLORS["skyblue"]),
|
141
|
+
),
|
127
142
|
}
|
128
143
|
|
129
144
|
|
@@ -213,6 +228,9 @@ ATTRIBUTES = {
|
|
213
228
|
),
|
214
229
|
},
|
215
230
|
"fallback": {
|
231
|
+
"cloud_top_height_agl": PlotMeta(
|
232
|
+
moving_average=False,
|
233
|
+
),
|
216
234
|
"nubf": PlotMeta(plot_range=(0, 5)),
|
217
235
|
"ze_sat": PlotMeta(
|
218
236
|
plot_range=(-40, 15),
|
@@ -281,6 +299,9 @@ ATTRIBUTES = {
|
|
281
299
|
"der_retrieval_status": PlotMeta(
|
282
300
|
clabel=_CLABEL["der_retrieval_status"],
|
283
301
|
),
|
302
|
+
"radar_attenuation_status": PlotMeta(
|
303
|
+
clabel=_CLABEL["radar_attenuation_status"],
|
304
|
+
),
|
284
305
|
"mu": PlotMeta(
|
285
306
|
plot_range=(0, 10),
|
286
307
|
),
|
@@ -554,6 +575,9 @@ ATTRIBUTES = {
|
|
554
575
|
"detection_status": PlotMeta(
|
555
576
|
clabel=_CLABEL["detection_status"],
|
556
577
|
),
|
578
|
+
"signal_source_status": PlotMeta(
|
579
|
+
clabel=_CLABEL["signal_source_status"],
|
580
|
+
),
|
557
581
|
"iwc": PlotMeta(
|
558
582
|
plot_range=(1e-7, 1e-3),
|
559
583
|
log_scale=True,
|
cloudnetpy/plotting/plotting.py
CHANGED
@@ -28,6 +28,7 @@ from cloudnetpy.categorize.atmos_utils import calc_altitude
|
|
28
28
|
from cloudnetpy.exceptions import PlottingError
|
29
29
|
from cloudnetpy.instruments.ceilometer import calc_sigma_units
|
30
30
|
from cloudnetpy.plotting.plot_meta import ATTRIBUTES, PlotMeta
|
31
|
+
from cloudnetpy.products.classification import TopStatus
|
31
32
|
|
32
33
|
EARTHCARE_MAX_X = 517.84
|
33
34
|
|
@@ -375,6 +376,7 @@ class Plot:
|
|
375
376
|
"air_temperature": (add, -273.15, "\u00b0C"),
|
376
377
|
"r_accum_RT": (multiply, 1000, "mm"),
|
377
378
|
"r_accum_NRT": (multiply, 1000, "mm"),
|
379
|
+
"cloud_top_height_agl": (multiply, con.M_TO_KM, "Height (km AGL)"),
|
378
380
|
}
|
379
381
|
conversion_method, conversion, units = units_conversion.get(
|
380
382
|
self.sub_plot.variable.name, (multiply, 1, None)
|
@@ -467,6 +469,12 @@ class Plot:
|
|
467
469
|
self._data = data_new
|
468
470
|
figure_data.time_including_gaps = time_new
|
469
471
|
|
472
|
+
def _read_cloud_top_flags(
|
473
|
+
self, figure_data: FigureData, flag_value: int | tuple[int, ...]
|
474
|
+
) -> ndarray:
|
475
|
+
status = figure_data.file.variables["cloud_top_height_status"][:]
|
476
|
+
return np.isin(status, flag_value)
|
477
|
+
|
470
478
|
def _read_flagged_data(self, figure_data: FigureData) -> ndarray:
|
471
479
|
flag_names = [
|
472
480
|
f"{self.sub_plot.variable.name}_quality_flag",
|
@@ -503,6 +511,32 @@ class Plot2D(Plot):
|
|
503
511
|
if figure_data.is_mwrpy_product():
|
504
512
|
self._fill_flagged_data(figure_data)
|
505
513
|
|
514
|
+
if figure_data.variables[0].name == "signal_source_status":
|
515
|
+
self._indicate_rainy_profiles(figure_data)
|
516
|
+
|
517
|
+
def _indicate_rainy_profiles(self, figure_data: FigureData) -> None:
|
518
|
+
if "rain_detected" not in figure_data.file.variables:
|
519
|
+
return
|
520
|
+
rain = figure_data.file.variables["rain_detected"][:]
|
521
|
+
is_rain: ma.MaskedArray = ma.masked_array(np.zeros_like(rain), mask=(rain == 0))
|
522
|
+
if is_rain.mask.all():
|
523
|
+
return
|
524
|
+
self._ax.plot(
|
525
|
+
figure_data.time,
|
526
|
+
is_rain,
|
527
|
+
color="red",
|
528
|
+
marker="|",
|
529
|
+
linestyle="None",
|
530
|
+
markersize=10,
|
531
|
+
zorder=-999,
|
532
|
+
label="Rain",
|
533
|
+
)
|
534
|
+
self._ax.legend(
|
535
|
+
markerscale=0.75,
|
536
|
+
numpoints=1,
|
537
|
+
frameon=False,
|
538
|
+
)
|
539
|
+
|
506
540
|
def _fill_flagged_data(self, figure_data: FigureData) -> None:
|
507
541
|
flags = self._read_flagged_data(figure_data)
|
508
542
|
batches = find_batches_of_ones(flags)
|
@@ -672,11 +706,35 @@ class Plot1D(Plot):
|
|
672
706
|
self.sub_plot.set_yax(ylabel=units, y_limits=self._get_y_limits())
|
673
707
|
pos = self._ax.get_position()
|
674
708
|
self._ax.set_position((pos.x0, pos.y0, pos.width * 0.965, pos.height))
|
709
|
+
self._plot_flags(figure_data)
|
710
|
+
|
711
|
+
def _plot_flags(self, figure_data: FigureData) -> None:
|
675
712
|
if figure_data.is_mwrpy_product():
|
676
713
|
flags = self._read_flagged_data(figure_data)
|
677
714
|
if np.any(flags):
|
678
715
|
self._plot_flag_data(figure_data.time[flags], self._data_orig[flags])
|
679
716
|
self._add_legend()
|
717
|
+
if (
|
718
|
+
figure_data.variables[0].name == "cloud_top_height_agl"
|
719
|
+
and "cloud_top_height_status" in figure_data.file.variables
|
720
|
+
):
|
721
|
+
legend: tuple = ()
|
722
|
+
flag_value = (TopStatus.MODERATE_ATT, TopStatus.UNCORR_ATT)
|
723
|
+
flags = self._read_cloud_top_flags(figure_data, flag_value)
|
724
|
+
if np.any(flags):
|
725
|
+
self._plot_flag_data(
|
726
|
+
figure_data.time[flags], self._data_orig[flags], color="orange"
|
727
|
+
)
|
728
|
+
legend += ("Suspicious",)
|
729
|
+
flag_value = (TopStatus.SEVERE_ATT, TopStatus.ABOVE_RANGE)
|
730
|
+
flags = self._read_cloud_top_flags(figure_data, flag_value)
|
731
|
+
if np.any(flags):
|
732
|
+
self._plot_flag_data(
|
733
|
+
figure_data.time[flags], self._data_orig[flags], color="red"
|
734
|
+
)
|
735
|
+
legend += ("Unreliable",)
|
736
|
+
if legend:
|
737
|
+
self._add_legend(name=legend)
|
680
738
|
|
681
739
|
def plot_tb(self, figure_data: FigureData, freq_ind: int) -> None:
|
682
740
|
if len(self._data.shape) != 2 or freq_ind >= self._data.shape[1]:
|
@@ -728,20 +786,22 @@ class Plot1D(Plot):
|
|
728
786
|
},
|
729
787
|
)
|
730
788
|
|
731
|
-
def _plot_flag_data(
|
789
|
+
def _plot_flag_data(
|
790
|
+
self, time: ndarray, values: ndarray, color: str = "salmon"
|
791
|
+
) -> None:
|
732
792
|
self._ax.plot(
|
733
793
|
time,
|
734
794
|
values,
|
735
|
-
color=
|
795
|
+
color=color,
|
736
796
|
marker=".",
|
737
797
|
lw=0,
|
738
798
|
markersize=3,
|
739
799
|
zorder=_get_zorder("flags"),
|
740
800
|
)
|
741
801
|
|
742
|
-
def _add_legend(self) -> None:
|
802
|
+
def _add_legend(self, name: str | tuple = ("Flagged data",)) -> None:
|
743
803
|
self._ax.legend(
|
744
|
-
|
804
|
+
name,
|
745
805
|
markerscale=3,
|
746
806
|
numpoints=1,
|
747
807
|
frameon=False,
|
@@ -772,7 +832,10 @@ class Plot1D(Plot):
|
|
772
832
|
custom_options = {
|
773
833
|
"tb": {
|
774
834
|
"color": "lightblue",
|
775
|
-
}
|
835
|
+
},
|
836
|
+
"cloud_top_height_agl": {
|
837
|
+
"color": "steelblue",
|
838
|
+
},
|
776
839
|
}
|
777
840
|
|
778
841
|
variable_name = self.sub_plot.variable.name
|
@@ -1,6 +1,8 @@
|
|
1
1
|
"""Module for creating classification file."""
|
2
2
|
|
3
|
+
from enum import IntEnum
|
3
4
|
from os import PathLike
|
5
|
+
from typing import NamedTuple
|
4
6
|
from uuid import UUID
|
5
7
|
|
6
8
|
import numpy as np
|
@@ -11,7 +13,7 @@ from cloudnetpy import output, utils
|
|
11
13
|
from cloudnetpy.categorize import atmos_utils
|
12
14
|
from cloudnetpy.datasource import DataSource
|
13
15
|
from cloudnetpy.metadata import MetaData
|
14
|
-
from cloudnetpy.products.product_tools import CategorizeBits
|
16
|
+
from cloudnetpy.products.product_tools import CategorizeBits, QualityBits
|
15
17
|
|
16
18
|
|
17
19
|
def generate_classification(
|
@@ -40,89 +42,248 @@ def generate_classification(
|
|
40
42
|
|
41
43
|
"""
|
42
44
|
uuid = utils.get_uuid(uuid)
|
43
|
-
|
44
|
-
|
45
|
+
categorize_bits = CategorizeBits(categorize_file)
|
46
|
+
with DataSource(categorize_file) as source:
|
45
47
|
classification = _get_target_classification(categorize_bits)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
source.append_data(classification, "target_classification")
|
49
|
+
|
50
|
+
detection_status = _get_detection_status(categorize_bits)
|
51
|
+
source.append_data(detection_status, "detection_status")
|
52
|
+
|
53
|
+
signal_source_status = _get_signal_source_status(categorize_bits)
|
54
|
+
source.append_data(signal_source_status, "signal_source_status")
|
55
|
+
|
56
|
+
att_status = _get_radar_attenuation_status(source, categorize_bits)
|
57
|
+
source.append_data(att_status, "radar_attenuation_status")
|
58
|
+
|
59
|
+
height = source.getvar("height")
|
60
|
+
bases, tops = _get_cloud_base_and_top_heights(classification, height)
|
61
|
+
source.append_data(bases, "cloud_base_height_amsl")
|
62
|
+
source.append_data(tops, "cloud_top_height_amsl")
|
63
|
+
source.append_data(
|
64
|
+
bases - source.altitude,
|
54
65
|
"cloud_base_height_agl",
|
55
66
|
)
|
56
|
-
|
57
|
-
tops -
|
67
|
+
source.append_data(
|
68
|
+
tops - source.altitude,
|
58
69
|
"cloud_top_height_agl",
|
59
70
|
)
|
60
|
-
|
71
|
+
|
72
|
+
cloud_top_status = _get_cloud_top_height_status(source, tops, att_status)
|
73
|
+
source.append_data(cloud_top_status, "cloud_top_height_status")
|
74
|
+
|
75
|
+
date = source.get_date()
|
61
76
|
attributes = output.add_time_attribute(CLASSIFICATION_ATTRIBUTES, date)
|
62
|
-
output.update_attributes(
|
77
|
+
output.update_attributes(source.data, attributes)
|
63
78
|
file_type = "classification"
|
64
|
-
if "liquid_prob" in
|
79
|
+
if "liquid_prob" in source.dataset.variables:
|
65
80
|
file_type += "-voodoo"
|
66
81
|
output.save_product_file(
|
67
|
-
file_type,
|
68
|
-
product_container,
|
69
|
-
output_file,
|
70
|
-
uuid,
|
82
|
+
file_type, source, output_file, uuid, copy_from_cat=("rain_detected",)
|
71
83
|
)
|
72
84
|
return uuid
|
73
85
|
|
74
86
|
|
87
|
+
class TopStatus(IntEnum):
|
88
|
+
RELIABLE = 0
|
89
|
+
MODERATE_ATT = 1
|
90
|
+
UNCORR_ATT = 2
|
91
|
+
SEVERE_ATT = 3
|
92
|
+
ABOVE_RANGE = 4
|
93
|
+
|
94
|
+
|
95
|
+
class AttStatus(IntEnum):
|
96
|
+
CLEAR = 0
|
97
|
+
NEGLIGIBLE = 1
|
98
|
+
SMALL = 2
|
99
|
+
MODERATE = 3
|
100
|
+
SEVERE = 4
|
101
|
+
UNCORRECTED = 5
|
102
|
+
UNDETECTED = 6
|
103
|
+
|
104
|
+
|
105
|
+
class SignalStatus(IntEnum):
|
106
|
+
CLEAR = 0
|
107
|
+
BOTH = 1
|
108
|
+
RADAR_ONLY = 2
|
109
|
+
LIDAR_ONLY = 3
|
110
|
+
|
111
|
+
|
112
|
+
class Target(IntEnum):
|
113
|
+
CLEAR = 0
|
114
|
+
DROPLET = 1
|
115
|
+
DRIZZLE_OR_RAIN = 2
|
116
|
+
DRIZZLE_OR_RAIN_AND_DROPLET = 3
|
117
|
+
ICE = 4
|
118
|
+
ICE_AND_SUPERCOOLED = 5
|
119
|
+
MELTING = 6
|
120
|
+
MELTING_AND_DROPLET = 7
|
121
|
+
AEROSOL = 8
|
122
|
+
INSECT = 9
|
123
|
+
INSECT_AND_AEROSOL = 10
|
124
|
+
|
125
|
+
|
126
|
+
class DetectionStatus(IntEnum):
|
127
|
+
CLEAR = 0
|
128
|
+
LIDAR_ONLY = 1
|
129
|
+
RADAR_UNCERTAIN_ATT = 2
|
130
|
+
RADAR_AND_LIDAR = 3
|
131
|
+
NO_RADAR_UNCERTAIN_ATT = 4
|
132
|
+
RADAR_ONLY = 5
|
133
|
+
NO_RADAR_KNOWN_ATT = 6
|
134
|
+
RADAR_ATT_CORRECTED = 7
|
135
|
+
CLUTTER = 8
|
136
|
+
MOLECULAR_SCATT = 9
|
137
|
+
|
138
|
+
|
139
|
+
class AttenuationClass(NamedTuple):
|
140
|
+
small: npt.NDArray
|
141
|
+
moderate: npt.NDArray
|
142
|
+
severe: npt.NDArray
|
143
|
+
|
144
|
+
|
145
|
+
def _get_cloud_top_height_status(
|
146
|
+
product_container: DataSource, tops: npt.NDArray, att_status: npt.NDArray
|
147
|
+
) -> npt.NDArray:
|
148
|
+
height = product_container.dataset.variables["height"][:]
|
149
|
+
dist = np.abs(height[None, :] - tops[:, None])
|
150
|
+
height_inds = dist.argmin(axis=1)
|
151
|
+
att_at_top = att_status[np.arange(att_status.shape[0]), height_inds]
|
152
|
+
status = np.zeros(att_at_top.size, dtype=int)
|
153
|
+
status[att_at_top == AttStatus.MODERATE] = TopStatus.MODERATE_ATT
|
154
|
+
status[att_at_top == AttStatus.SEVERE] = TopStatus.SEVERE_ATT
|
155
|
+
status[att_at_top == AttStatus.UNCORRECTED] = TopStatus.UNCORR_ATT
|
156
|
+
status[tops >= height[-1]] = TopStatus.ABOVE_RANGE
|
157
|
+
return status
|
158
|
+
|
159
|
+
|
160
|
+
def _get_radar_attenuation_status(
|
161
|
+
data_source: DataSource, categorize_bits: CategorizeBits
|
162
|
+
) -> npt.NDArray:
|
163
|
+
bits = categorize_bits.quality_bits
|
164
|
+
is_attenuated = _get_is_attenuated_mask(bits)
|
165
|
+
is_corrected = _get_is_corrected_mask(bits)
|
166
|
+
att = _get_attenuation_classes(data_source)
|
167
|
+
severity = np.zeros_like(att.small, dtype=int)
|
168
|
+
severity[bits.radar] = AttStatus.NEGLIGIBLE
|
169
|
+
severity[att.small & bits.radar] = AttStatus.SMALL
|
170
|
+
severity[att.moderate & bits.radar] = AttStatus.MODERATE
|
171
|
+
severity[att.severe & bits.radar] = AttStatus.SEVERE
|
172
|
+
severity[~is_corrected & is_attenuated & bits.radar] = AttStatus.UNCORRECTED
|
173
|
+
is_severe = severity == AttStatus.SEVERE
|
174
|
+
above_severe = utils.ffill(is_severe)
|
175
|
+
severity[above_severe & ~is_severe] = AttStatus.UNDETECTED
|
176
|
+
return severity
|
177
|
+
|
178
|
+
|
179
|
+
def _get_attenuation_classes(data_source: DataSource) -> AttenuationClass:
|
180
|
+
def _read_atten(key: str) -> npt.NDArray:
|
181
|
+
if key not in data_source.dataset.variables:
|
182
|
+
return np.zeros(data_source.time.shape)
|
183
|
+
data = data_source.getvar(key)
|
184
|
+
if isinstance(data, ma.MaskedArray):
|
185
|
+
return data.filled(0)
|
186
|
+
return data
|
187
|
+
|
188
|
+
liquid_atten = _read_atten("radar_liquid_atten")
|
189
|
+
rain_atten = _read_atten("radar_rain_atten")
|
190
|
+
melting_atten = _read_atten("radar_melting_atten")
|
191
|
+
|
192
|
+
if (
|
193
|
+
"lwp" not in data_source.dataset.variables
|
194
|
+
or data_source.getvar("radar_frequency") < 90
|
195
|
+
):
|
196
|
+
lwp = np.zeros(data_source.time.shape)
|
197
|
+
else:
|
198
|
+
lwp_data = data_source.getvar("lwp")
|
199
|
+
lwp = lwp_data.filled(0) if isinstance(lwp_data, ma.MaskedArray) else lwp_data
|
200
|
+
|
201
|
+
total_atten = liquid_atten + rain_atten + melting_atten
|
202
|
+
|
203
|
+
threshold_moderate = 10 # dB
|
204
|
+
threshold_severe = 15 # dB
|
205
|
+
threshold_lwp = 1 # kg/m2
|
206
|
+
|
207
|
+
small = total_atten > 0
|
208
|
+
moderate = total_atten >= threshold_moderate
|
209
|
+
severe = (total_atten > threshold_severe) | (lwp[:, np.newaxis] > threshold_lwp)
|
210
|
+
|
211
|
+
return AttenuationClass(small=small, moderate=moderate, severe=severe)
|
212
|
+
|
213
|
+
|
75
214
|
def _get_target_classification(
|
76
215
|
categorize_bits: CategorizeBits,
|
77
216
|
) -> ma.MaskedArray:
|
78
217
|
bits = categorize_bits.category_bits
|
79
218
|
clutter = categorize_bits.quality_bits.clutter
|
80
219
|
classification = ma.zeros(bits.freezing.shape, dtype=int)
|
81
|
-
classification[bits.droplet & ~bits.falling] =
|
82
|
-
classification[~bits.droplet & bits.falling] =
|
83
|
-
classification[bits.droplet & bits.falling] =
|
84
|
-
classification[~bits.droplet & bits.falling & bits.freezing] =
|
85
|
-
classification[bits.droplet & bits.falling & bits.freezing] =
|
86
|
-
|
87
|
-
|
88
|
-
classification[bits.
|
89
|
-
classification[bits.
|
90
|
-
classification[bits.aerosol
|
91
|
-
classification[
|
220
|
+
classification[bits.droplet & ~bits.falling] = Target.DROPLET
|
221
|
+
classification[~bits.droplet & bits.falling] = Target.DRIZZLE_OR_RAIN
|
222
|
+
classification[bits.droplet & bits.falling] = Target.DRIZZLE_OR_RAIN_AND_DROPLET
|
223
|
+
classification[~bits.droplet & bits.falling & bits.freezing] = Target.ICE
|
224
|
+
classification[bits.droplet & bits.falling & bits.freezing] = (
|
225
|
+
Target.ICE_AND_SUPERCOOLED
|
226
|
+
)
|
227
|
+
classification[bits.melting] = Target.MELTING
|
228
|
+
classification[bits.melting & bits.droplet] = Target.MELTING_AND_DROPLET
|
229
|
+
classification[bits.aerosol] = Target.AEROSOL
|
230
|
+
classification[bits.insect & ~clutter] = Target.INSECT
|
231
|
+
classification[bits.aerosol & bits.insect & ~clutter] = Target.INSECT_AND_AEROSOL
|
232
|
+
classification[clutter & ~bits.aerosol] = Target.CLEAR
|
92
233
|
return classification
|
93
234
|
|
94
235
|
|
95
236
|
def _get_detection_status(categorize_bits: CategorizeBits) -> npt.NDArray:
|
96
237
|
bits = categorize_bits.quality_bits
|
238
|
+
is_attenuated = _get_is_attenuated_mask(bits)
|
239
|
+
is_corrected = _get_is_corrected_mask(bits)
|
97
240
|
|
98
|
-
|
99
|
-
|
241
|
+
status = np.zeros(bits.radar.shape, dtype=int)
|
242
|
+
status[bits.lidar & ~bits.radar] = DetectionStatus.LIDAR_ONLY
|
243
|
+
status[bits.radar & bits.lidar] = DetectionStatus.RADAR_AND_LIDAR
|
244
|
+
status[~bits.radar & is_attenuated & ~is_corrected] = (
|
245
|
+
DetectionStatus.NO_RADAR_UNCERTAIN_ATT
|
246
|
+
)
|
247
|
+
status[bits.radar & ~bits.lidar & ~is_attenuated] = DetectionStatus.RADAR_ONLY
|
248
|
+
status[~bits.radar & is_attenuated & is_corrected] = (
|
249
|
+
DetectionStatus.NO_RADAR_KNOWN_ATT
|
250
|
+
)
|
251
|
+
status[bits.radar & is_corrected] = DetectionStatus.RADAR_ATT_CORRECTED
|
252
|
+
status[bits.radar & is_attenuated & ~is_corrected] = (
|
253
|
+
DetectionStatus.RADAR_UNCERTAIN_ATT
|
100
254
|
)
|
101
|
-
|
255
|
+
status[bits.clutter] = DetectionStatus.CLUTTER
|
256
|
+
status[bits.molecular & ~bits.radar] = DetectionStatus.MOLECULAR_SCATT
|
257
|
+
return status
|
258
|
+
|
259
|
+
|
260
|
+
def _get_is_corrected_mask(bits: QualityBits) -> npt.NDArray:
|
261
|
+
is_attenuated = _get_is_attenuated_mask(bits)
|
262
|
+
return (
|
102
263
|
is_attenuated
|
103
264
|
& (~bits.attenuated_liquid | bits.corrected_liquid)
|
104
265
|
& (~bits.attenuated_rain | bits.corrected_rain)
|
105
266
|
& (~bits.attenuated_melting | bits.corrected_melting)
|
106
267
|
)
|
107
268
|
|
269
|
+
|
270
|
+
def _get_is_attenuated_mask(bits: QualityBits) -> npt.NDArray:
|
271
|
+
return bits.attenuated_liquid | bits.attenuated_rain | bits.attenuated_melting
|
272
|
+
|
273
|
+
|
274
|
+
def _get_signal_source_status(categorize_bits: CategorizeBits) -> npt.NDArray:
|
275
|
+
bits = categorize_bits.quality_bits
|
108
276
|
status = np.zeros(bits.radar.shape, dtype=int)
|
109
|
-
status[bits.
|
110
|
-
status[bits.radar & bits.lidar] =
|
111
|
-
status[
|
112
|
-
status[bits.radar & ~bits.lidar & ~is_attenuated] = 5
|
113
|
-
status[~bits.radar & is_attenuated & is_corrected] = 6
|
114
|
-
status[bits.radar & is_corrected] = 7
|
115
|
-
status[bits.radar & is_attenuated & ~is_corrected] = 2
|
116
|
-
status[bits.clutter] = 8
|
117
|
-
status[bits.molecular & ~bits.radar] = 9
|
277
|
+
status[bits.radar & bits.lidar] = SignalStatus.BOTH
|
278
|
+
status[bits.radar & ~bits.lidar] = SignalStatus.RADAR_ONLY
|
279
|
+
status[bits.lidar & ~bits.radar] = SignalStatus.LIDAR_ONLY
|
118
280
|
return status
|
119
281
|
|
120
282
|
|
121
283
|
def _get_cloud_base_and_top_heights(
|
122
284
|
classification: npt.NDArray,
|
123
|
-
|
285
|
+
height: npt.NDArray,
|
124
286
|
) -> tuple[npt.NDArray, npt.NDArray]:
|
125
|
-
height = product_container.getvar("height")
|
126
287
|
cloud_mask = _find_cloud_mask(classification)
|
127
288
|
if not cloud_mask.any():
|
128
289
|
return ma.masked_all(cloud_mask.shape[0]), ma.masked_all(cloud_mask.shape[0])
|
@@ -136,7 +297,12 @@ def _get_cloud_base_and_top_heights(
|
|
136
297
|
|
137
298
|
def _find_cloud_mask(classification: npt.NDArray) -> npt.NDArray:
|
138
299
|
cloud_mask = np.zeros(classification.shape, dtype=int)
|
139
|
-
for value in [
|
300
|
+
for value in [
|
301
|
+
Target.DROPLET,
|
302
|
+
Target.DRIZZLE_OR_RAIN_AND_DROPLET,
|
303
|
+
Target.ICE,
|
304
|
+
Target.ICE_AND_SUPERCOOLED,
|
305
|
+
]:
|
140
306
|
cloud_mask[classification == value] = 1
|
141
307
|
return cloud_mask
|
142
308
|
|
@@ -154,42 +320,92 @@ COMMENTS = {
|
|
154
320
|
),
|
155
321
|
}
|
156
322
|
|
323
|
+
|
157
324
|
DEFINITIONS = {
|
158
325
|
"target_classification": utils.status_field_definition(
|
159
326
|
{
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
327
|
+
Target.CLEAR: """Clear sky.""",
|
328
|
+
Target.DROPLET: """Cloud liquid droplets only.""",
|
329
|
+
Target.DRIZZLE_OR_RAIN: """Drizzle or rain.""",
|
330
|
+
Target.DRIZZLE_OR_RAIN_AND_DROPLET: """Drizzle or rain
|
331
|
+
coexisting with cloud liquid droplets.""",
|
332
|
+
Target.ICE: """Ice particles.""",
|
333
|
+
Target.ICE_AND_SUPERCOOLED: """Ice coexisting with
|
334
|
+
supercooled liquid droplets.""",
|
335
|
+
Target.MELTING: """Melting ice particles.""",
|
336
|
+
Target.MELTING_AND_DROPLET: """Melting ice particles
|
337
|
+
coexisting with cloud liquid droplets.""",
|
338
|
+
Target.AEROSOL: """Aerosol particles, no cloud or precipitation.""",
|
339
|
+
Target.INSECT: """Insects, no cloud or precipitation.""",
|
340
|
+
Target.INSECT_AND_AEROSOL: """Aerosol coexisting
|
341
|
+
with insects, no cloud or precipitation.""",
|
171
342
|
}
|
172
343
|
),
|
173
344
|
"detection_status": utils.status_field_definition(
|
174
345
|
{
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
346
|
+
DetectionStatus.CLEAR: """Clear sky.""",
|
347
|
+
DetectionStatus.LIDAR_ONLY: """Lidar echo only.""",
|
348
|
+
DetectionStatus.RADAR_UNCERTAIN_ATT: """
|
349
|
+
Radar echo but reflectivity may be unreliable as attenuation
|
350
|
+
by rain, melting ice or liquid cloud has not been
|
351
|
+
corrected.""",
|
352
|
+
DetectionStatus.RADAR_AND_LIDAR: """Good radar and lidar echos.""",
|
353
|
+
DetectionStatus.NO_RADAR_UNCERTAIN_ATT: """
|
354
|
+
No radar echo but rain or liquid cloud beneath mean that
|
355
|
+
attenuation that would be experienced is unknown.""",
|
356
|
+
DetectionStatus.RADAR_ONLY: """
|
357
|
+
Good radar echo only.""",
|
358
|
+
DetectionStatus.NO_RADAR_KNOWN_ATT: """
|
359
|
+
No radar echo but known attenuation.""",
|
360
|
+
DetectionStatus.RADAR_ATT_CORRECTED: """
|
361
|
+
Radar echo corrected for liquid, rain or melting attenuation.""",
|
362
|
+
DetectionStatus.CLUTTER: """
|
363
|
+
Radar ground clutter.""",
|
364
|
+
DetectionStatus.MOLECULAR_SCATT: """
|
365
|
+
Lidar clear-air molecular scattering.""",
|
366
|
+
}
|
367
|
+
),
|
368
|
+
"cloud_top_height_status": utils.status_field_definition(
|
369
|
+
{
|
370
|
+
TopStatus.RELIABLE: """Reliable.""",
|
371
|
+
TopStatus.MODERATE_ATT: """Uncertain due to moderate
|
372
|
+
radar attenuation.""",
|
373
|
+
TopStatus.UNCORR_ATT: """Uncertain due to incomplete
|
374
|
+
radar attenuation correction.""",
|
375
|
+
TopStatus.SEVERE_ATT: """Likely erroneous due to
|
376
|
+
severe radar attenuation.""",
|
377
|
+
TopStatus.ABOVE_RANGE: """Cloud top above radar
|
378
|
+
measurement range.""",
|
379
|
+
}
|
380
|
+
),
|
381
|
+
"signal_source_status": utils.status_field_definition(
|
382
|
+
{
|
383
|
+
SignalStatus.CLEAR: """No signal from radar or lidar.""",
|
384
|
+
SignalStatus.BOTH: """Signal from both radar and lidar.""",
|
385
|
+
SignalStatus.RADAR_ONLY: """Signal from radar only.""",
|
386
|
+
SignalStatus.LIDAR_ONLY: """Signal from lidar only.""",
|
387
|
+
}
|
388
|
+
),
|
389
|
+
"radar_attenuation_status": utils.status_field_definition(
|
390
|
+
{
|
391
|
+
AttStatus.CLEAR: """No radar signal.""",
|
392
|
+
AttStatus.NEGLIGIBLE: """Radar signal,
|
393
|
+
negligible attenuation (corrected).""",
|
394
|
+
AttStatus.SMALL: """Radar signal,
|
395
|
+
small attenuation (corrected).""",
|
396
|
+
AttStatus.MODERATE: """Radar signal,
|
397
|
+
moderate attenuation (corrected).""",
|
398
|
+
AttStatus.SEVERE: """Radar signal,
|
399
|
+
severe attenuation (corrected).""",
|
400
|
+
AttStatus.UNCORRECTED: """Radar signal,
|
401
|
+
attenuation present but not corrected.""",
|
402
|
+
AttStatus.UNDETECTED: """No radar signal, cloud
|
403
|
+
may be undetected due to severe attenuation beneath.""",
|
189
404
|
}
|
190
405
|
),
|
191
406
|
}
|
192
407
|
|
408
|
+
|
193
409
|
CLASSIFICATION_ATTRIBUTES = {
|
194
410
|
"target_classification": MetaData(
|
195
411
|
long_name="Target classification",
|
@@ -205,10 +421,23 @@ CLASSIFICATION_ATTRIBUTES = {
|
|
205
421
|
units="1",
|
206
422
|
dimensions=("time", "height"),
|
207
423
|
),
|
424
|
+
"signal_source_status": MetaData(
|
425
|
+
long_name="Signal source status",
|
426
|
+
units="1",
|
427
|
+
dimensions=("time", "height"),
|
428
|
+
definition=DEFINITIONS["signal_source_status"],
|
429
|
+
),
|
430
|
+
"radar_attenuation_status": MetaData(
|
431
|
+
long_name="Radar attenuation status",
|
432
|
+
units="1",
|
433
|
+
dimensions=("time", "height"),
|
434
|
+
definition=DEFINITIONS["radar_attenuation_status"],
|
435
|
+
),
|
208
436
|
"cloud_top_height_amsl": MetaData(
|
209
437
|
long_name="Height of cloud top above mean sea level",
|
210
438
|
units="m",
|
211
439
|
dimensions=("time",),
|
440
|
+
ancillary_variables="cloud_top_height_status",
|
212
441
|
),
|
213
442
|
"cloud_base_height_amsl": MetaData(
|
214
443
|
long_name="Height of cloud base above mean sea level",
|
@@ -219,10 +448,17 @@ CLASSIFICATION_ATTRIBUTES = {
|
|
219
448
|
long_name="Height of cloud top above ground level",
|
220
449
|
units="m",
|
221
450
|
dimensions=("time",),
|
451
|
+
ancillary_variables="cloud_top_height_status",
|
222
452
|
),
|
223
453
|
"cloud_base_height_agl": MetaData(
|
224
454
|
long_name="Height of cloud base above ground level",
|
225
455
|
units="m",
|
226
456
|
dimensions=("time",),
|
227
457
|
),
|
458
|
+
"cloud_top_height_status": MetaData(
|
459
|
+
long_name="Cloud top height quality status",
|
460
|
+
units="1",
|
461
|
+
dimensions=("time",),
|
462
|
+
definition=DEFINITIONS["cloud_top_height_status"],
|
463
|
+
),
|
228
464
|
}
|
cloudnetpy/version.py
CHANGED
@@ -3,13 +3,13 @@ cloudnetpy/cli.py,sha256=kG48AI5wNK9MfxiUsQPiz56BQLgAZ3AyN0ehUO6MyuI,20892
|
|
3
3
|
cloudnetpy/cloudnetarray.py,sha256=I_U1W2rEXw8lbLwg3XBrr_qHGPqhQG9z_ouvVEg7p24,4908
|
4
4
|
cloudnetpy/concat_lib.py,sha256=u4UOjYzLnThaq-89iwA837OcOJpfmj_3RmRwBCFUh74,13218
|
5
5
|
cloudnetpy/constants.py,sha256=YnoSzZm35NDooJfhlulSJBc7g0eSchT3yGytRaTaJEI,845
|
6
|
-
cloudnetpy/datasource.py,sha256=
|
6
|
+
cloudnetpy/datasource.py,sha256=EMJ4UHD8Z-JJ9Q82S7RgU1I2q4Z0RcBzBMKUAIwnZBI,6356
|
7
7
|
cloudnetpy/exceptions.py,sha256=ZB3aUwjVRznR0CcZ5sZHrB0yz13URDf52Ksv7G7C7EA,1817
|
8
8
|
cloudnetpy/metadata.py,sha256=CFpXmdEkVPzvLPv2xHIR-aMMQ-TR26KfESYw-98j7sk,7213
|
9
9
|
cloudnetpy/output.py,sha256=0bybnILsgKHWIuw2GYkqTz2iMCJDZLUN25IQ9o_v3Cg,14968
|
10
10
|
cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
cloudnetpy/utils.py,sha256=Qv60_vxknB3f2S3EFtyoD2CBY3N6mgDRObNp2u1oYUc,31806
|
12
|
-
cloudnetpy/version.py,sha256=
|
12
|
+
cloudnetpy/version.py,sha256=eZHEAex9xrlQzhoqlO0MSLCFCdaJCCetQ0O35RSY7VM,72
|
13
13
|
cloudnetpy/categorize/__init__.py,sha256=gtvzWr0IDRn2oA6yHBvinEhTGTuub-JkrOv93lBsgrE,61
|
14
14
|
cloudnetpy/categorize/atmos_utils.py,sha256=uWc9TABVYPI0sn4H5Az9Jf6NVRaWyEKIi17f0pAJQxE,10679
|
15
15
|
cloudnetpy/categorize/attenuation.py,sha256=Y_-fzmQTltWTqIZTulJhovC7a6ifpMcaAazDJcnMIOc,990
|
@@ -30,7 +30,7 @@ cloudnetpy/categorize/radar.py,sha256=2mTDa9BLxQeaORm-YPQ1lJyjAKew6NYzjtUvjpIvBY
|
|
30
30
|
cloudnetpy/categorize/attenuations/__init__.py,sha256=kIyQEZ6VVO6jJOAndrt7jNU15pm0Cavh5GnDjFmIG1M,1040
|
31
31
|
cloudnetpy/categorize/attenuations/gas_attenuation.py,sha256=emr-RCxQT0i2N8k6eBNhRsmsCBPHJzQsWJfjC4fVSTo,975
|
32
32
|
cloudnetpy/categorize/attenuations/liquid_attenuation.py,sha256=bmqmPk_93J4njE16-VQ1bPI7oNSS8m9ACuUH7IErBs8,3069
|
33
|
-
cloudnetpy/categorize/attenuations/melting_attenuation.py,sha256=
|
33
|
+
cloudnetpy/categorize/attenuations/melting_attenuation.py,sha256=zmpF7Gek4W9cF-5tGoXqi8yAUCFC5jzK6DqyVndXUnQ,2403
|
34
34
|
cloudnetpy/categorize/attenuations/rain_attenuation.py,sha256=wJPyCiKWzsQDzMhqbA7mYwj9YRVcJIpXWhBnEYFy3uU,2843
|
35
35
|
cloudnetpy/instruments/__init__.py,sha256=PEgrrQNoiOuN_ctYilmt4LV2QCLg1likPjJdWtuGlLs,528
|
36
36
|
cloudnetpy/instruments/basta.py,sha256=N-kRgl5Vm52pXzr9umo4YsA0hn4zZCOa-0_zZTzhheY,4284
|
@@ -48,7 +48,7 @@ cloudnetpy/instruments/lufft.py,sha256=G6KeJOeltLUlGCHHEk8ns2K7WJ9ImAr25rSB2Jlta
|
|
48
48
|
cloudnetpy/instruments/mira.py,sha256=XqmbytpeCJ2-hNugxdsXSBUDB8SAUc97_6lo5mHFG8E,11840
|
49
49
|
cloudnetpy/instruments/mrr.py,sha256=z50VYLOBW2o7enU7FHZYNFQRW2goEQpeGe7-iCBRQtg,6020
|
50
50
|
cloudnetpy/instruments/nc_lidar.py,sha256=PtZauDdI3bX3bv4gIVvV6N53e2Co-ehBL_tByHM9hj8,1713
|
51
|
-
cloudnetpy/instruments/nc_radar.py,sha256=
|
51
|
+
cloudnetpy/instruments/nc_radar.py,sha256=9npjF9xfMY5DkDPpesqAIhrmv1QqPlKB9J-cySI2UbU,7360
|
52
52
|
cloudnetpy/instruments/pollyxt.py,sha256=IFq_RJrhgJ79OVyuo48PwYQK_zZ6VZFB_S5bEirRyzs,10566
|
53
53
|
cloudnetpy/instruments/radiometrics.py,sha256=QKfnrZlQ0sFcFjmv1ShnCMTJQv64w4akjK-JAIY4gCg,16116
|
54
54
|
cloudnetpy/instruments/rain_e_h3.py,sha256=fjv3SgeUNx9GisYqLrBnX9AjnO17VtouyoPh12VE9uo,5465
|
@@ -74,7 +74,7 @@ cloudnetpy/model_evaluation/products/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu
|
|
74
74
|
cloudnetpy/model_evaluation/products/advance_methods.py,sha256=-57z69YL8VVauypa0W1ZAZGHoNybna_CYcxQVORCV9c,8658
|
75
75
|
cloudnetpy/model_evaluation/products/grid_methods.py,sha256=gAbRjM8jL0FBmH1t6U9Hvi0-f8GbmmF9Ctrcckluwh8,9110
|
76
76
|
cloudnetpy/model_evaluation/products/model_products.py,sha256=vt104hKBM3KoLx-4r8E_LXKRR-FZU0X2gRS2TJ1sVII,7036
|
77
|
-
cloudnetpy/model_evaluation/products/observation_products.py,sha256=
|
77
|
+
cloudnetpy/model_evaluation/products/observation_products.py,sha256=4zmfSA8tantURcXAMh9vbjplWEEqj7wmhMt3hgdrAuI,5574
|
78
78
|
cloudnetpy/model_evaluation/products/product_resampling.py,sha256=PO9PIJFeh2Nhl1bJ5Vg0_mB2xR5Xvj-qMjfD8n28KvY,3781
|
79
79
|
cloudnetpy/model_evaluation/products/tools.py,sha256=pIF3cw3LDhdRfBTuknpzXdr7cxPuD0Reyb_lwLxR-v0,3075
|
80
80
|
cloudnetpy/model_evaluation/statistics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -102,10 +102,10 @@ cloudnetpy/model_evaluation/tests/unit/test_plotting.py,sha256=5hkhtqX-JQ8-Yy6DA
|
|
102
102
|
cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py,sha256=Ra3r4V0qbqkpDuaTYvEIbaasl0nZ5gmTLR4eGC0glBQ,9724
|
103
103
|
cloudnetpy/model_evaluation/tests/unit/test_tools.py,sha256=Ia_VrLdV2NstX5gbx_3AZTOAlrgLAy_xFZ8fHYVX0xI,3817
|
104
104
|
cloudnetpy/plotting/__init__.py,sha256=lg9Smn4BI0dVBgnDLC3JVJ4GmwoSnO-qoSd4ApvwV6Y,107
|
105
|
-
cloudnetpy/plotting/plot_meta.py,sha256=
|
106
|
-
cloudnetpy/plotting/plotting.py,sha256=
|
105
|
+
cloudnetpy/plotting/plot_meta.py,sha256=9d1OgPDysBDUUhncXdj-_EKmGdK7JutNBkzf8YV2lVg,18249
|
106
|
+
cloudnetpy/plotting/plotting.py,sha256=8NBqYC0RnBQarjFAmqTPYnFcfyRejfJWZ4TcR7EqVUI,41426
|
107
107
|
cloudnetpy/products/__init__.py,sha256=cBJdJBYltz5ZTKDqnRo-0StytAZK8gE3RYxxriFA4ak,295
|
108
|
-
cloudnetpy/products/classification.py,sha256=
|
108
|
+
cloudnetpy/products/classification.py,sha256=6WxiGGJXqlAPgJ9hVNTKm3f7iGsqA7e3L3xqVxFFs7w,16894
|
109
109
|
cloudnetpy/products/der.py,sha256=UXdAxmmwChVVWSI4QSGAXphfMnbymGRTtGdKWEvh-J4,13162
|
110
110
|
cloudnetpy/products/drizzle.py,sha256=0h1N_WVjC2GgIkAN-4ydOwl7WJn3psxeqmPHfX8WHhQ,11935
|
111
111
|
cloudnetpy/products/drizzle_error.py,sha256=QN98Io9UsBoEYxKBqfwoS88OGBiK5U5RYnVQjyTWHCI,6220
|
@@ -117,10 +117,10 @@ cloudnetpy/products/lwc.py,sha256=xsNiiG6dGKIkWaFk0xWTabc1bZ4ULf6SqcqHs7itAUk,19
|
|
117
117
|
cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe55y9ob58,16637951
|
118
118
|
cloudnetpy/products/mwr_tools.py,sha256=MMWnp68U7bv157-CPB2VeTQvaR6zl7sexbBT_kJ_pn8,6734
|
119
119
|
cloudnetpy/products/product_tools.py,sha256=eyqIw_0KhlpmmYQE69RpGdRIAOW7JVPlEgkTBp2kdps,11302
|
120
|
-
cloudnetpy-1.
|
120
|
+
cloudnetpy-1.83.0.dist-info/licenses/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
121
121
|
docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
|
122
|
-
cloudnetpy-1.
|
123
|
-
cloudnetpy-1.
|
124
|
-
cloudnetpy-1.
|
125
|
-
cloudnetpy-1.
|
126
|
-
cloudnetpy-1.
|
122
|
+
cloudnetpy-1.83.0.dist-info/METADATA,sha256=wDkaRhm_pjpqqiX-6MteJV6IvZeKpZ8XiqaxCehqnV4,5836
|
123
|
+
cloudnetpy-1.83.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
124
|
+
cloudnetpy-1.83.0.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
|
125
|
+
cloudnetpy-1.83.0.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
126
|
+
cloudnetpy-1.83.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|