cloudnetpy 1.80.8__py3-none-any.whl → 1.81.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/__init__.py +1 -1
- cloudnetpy/categorize/atmos_utils.py +31 -27
- cloudnetpy/categorize/attenuations/__init__.py +4 -4
- cloudnetpy/categorize/attenuations/liquid_attenuation.py +7 -5
- cloudnetpy/categorize/attenuations/melting_attenuation.py +3 -3
- cloudnetpy/categorize/attenuations/rain_attenuation.py +4 -4
- cloudnetpy/categorize/categorize.py +25 -11
- cloudnetpy/categorize/classify.py +9 -8
- cloudnetpy/categorize/containers.py +13 -10
- cloudnetpy/categorize/disdrometer.py +5 -3
- cloudnetpy/categorize/droplet.py +12 -9
- cloudnetpy/categorize/falling.py +9 -8
- cloudnetpy/categorize/freezing.py +10 -7
- cloudnetpy/categorize/insects.py +18 -17
- cloudnetpy/categorize/lidar.py +7 -3
- cloudnetpy/categorize/melting.py +16 -15
- cloudnetpy/categorize/model.py +17 -10
- cloudnetpy/categorize/mwr.py +5 -3
- cloudnetpy/categorize/radar.py +15 -13
- cloudnetpy/cli.py +10 -8
- cloudnetpy/cloudnetarray.py +8 -7
- cloudnetpy/concat_lib.py +29 -20
- cloudnetpy/datasource.py +26 -21
- cloudnetpy/exceptions.py +12 -10
- cloudnetpy/instruments/basta.py +19 -9
- cloudnetpy/instruments/bowtie.py +18 -11
- cloudnetpy/instruments/ceilo.py +22 -10
- cloudnetpy/instruments/ceilometer.py +33 -34
- cloudnetpy/instruments/cl61d.py +5 -3
- cloudnetpy/instruments/cloudnet_instrument.py +7 -7
- cloudnetpy/instruments/copernicus.py +16 -7
- cloudnetpy/instruments/disdrometer/common.py +5 -4
- cloudnetpy/instruments/disdrometer/parsivel.py +14 -9
- cloudnetpy/instruments/disdrometer/thies.py +11 -7
- cloudnetpy/instruments/fd12p.py +7 -6
- cloudnetpy/instruments/galileo.py +16 -7
- cloudnetpy/instruments/hatpro.py +33 -24
- cloudnetpy/instruments/lufft.py +6 -4
- cloudnetpy/instruments/mira.py +33 -19
- cloudnetpy/instruments/mrr.py +12 -12
- cloudnetpy/instruments/nc_lidar.py +1 -1
- cloudnetpy/instruments/nc_radar.py +8 -8
- cloudnetpy/instruments/pollyxt.py +19 -12
- cloudnetpy/instruments/radiometrics.py +17 -10
- cloudnetpy/instruments/rain_e_h3.py +9 -5
- cloudnetpy/instruments/rpg.py +32 -21
- cloudnetpy/instruments/rpg_reader.py +15 -12
- cloudnetpy/instruments/vaisala.py +32 -24
- cloudnetpy/instruments/weather_station.py +22 -19
- cloudnetpy/model_evaluation/file_handler.py +27 -29
- cloudnetpy/model_evaluation/plotting/plot_tools.py +7 -5
- cloudnetpy/model_evaluation/plotting/plotting.py +41 -32
- cloudnetpy/model_evaluation/products/advance_methods.py +38 -34
- cloudnetpy/model_evaluation/products/grid_methods.py +10 -9
- cloudnetpy/model_evaluation/products/model_products.py +15 -9
- cloudnetpy/model_evaluation/products/observation_products.py +12 -10
- cloudnetpy/model_evaluation/products/product_resampling.py +11 -7
- cloudnetpy/model_evaluation/products/tools.py +18 -14
- cloudnetpy/model_evaluation/statistics/statistical_methods.py +6 -5
- cloudnetpy/model_evaluation/tests/unit/test_plotting.py +18 -25
- cloudnetpy/model_evaluation/utils.py +3 -3
- cloudnetpy/output.py +15 -32
- cloudnetpy/plotting/plotting.py +22 -12
- cloudnetpy/products/classification.py +15 -9
- cloudnetpy/products/der.py +24 -19
- cloudnetpy/products/drizzle.py +21 -13
- cloudnetpy/products/drizzle_error.py +8 -7
- cloudnetpy/products/drizzle_tools.py +27 -23
- cloudnetpy/products/epsilon.py +6 -5
- cloudnetpy/products/ier.py +11 -5
- cloudnetpy/products/iwc.py +18 -9
- cloudnetpy/products/lwc.py +41 -31
- cloudnetpy/products/mwr_tools.py +30 -19
- cloudnetpy/products/product_tools.py +23 -19
- cloudnetpy/utils.py +84 -98
- cloudnetpy/version.py +2 -2
- {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.0.dist-info}/METADATA +2 -1
- cloudnetpy-1.81.0.dist-info/RECORD +126 -0
- cloudnetpy-1.80.8.dist-info/RECORD +0 -126
- {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.0.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.0.dist-info}/entry_points.txt +0 -0
- {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.0.dist-info}/licenses/LICENSE +0 -0
- {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.0.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@
|
|
3
3
|
import logging
|
4
4
|
|
5
5
|
import numpy as np
|
6
|
+
import numpy.typing as npt
|
6
7
|
from numpy import ma
|
7
8
|
from scipy.interpolate import interp1d
|
8
9
|
|
@@ -11,7 +12,7 @@ from cloudnetpy.categorize.containers import ClassData
|
|
11
12
|
from cloudnetpy.constants import T0
|
12
13
|
|
13
14
|
|
14
|
-
def find_freezing_region(obs: ClassData, melting_layer:
|
15
|
+
def find_freezing_region(obs: ClassData, melting_layer: npt.NDArray) -> npt.NDArray:
|
15
16
|
"""Finds freezing region using the model temperature and melting layer.
|
16
17
|
|
17
18
|
Every profile that contains melting layer, subzero region starts from
|
@@ -60,16 +61,18 @@ def find_freezing_region(obs: ClassData, melting_layer: np.ndarray) -> np.ndarra
|
|
60
61
|
|
61
62
|
|
62
63
|
def _is_all_freezing(
|
63
|
-
mean_melting_alt:
|
64
|
-
t0_alt:
|
65
|
-
height:
|
64
|
+
mean_melting_alt: npt.NDArray,
|
65
|
+
t0_alt: npt.NDArray,
|
66
|
+
height: npt.NDArray,
|
66
67
|
) -> bool:
|
67
68
|
no_detected_melting = mean_melting_alt.all() is ma.masked
|
68
69
|
all_temperatures_below_freezing = (t0_alt <= height[0]).all()
|
69
70
|
return no_detected_melting and all_temperatures_below_freezing
|
70
71
|
|
71
72
|
|
72
|
-
def _find_mean_melting_alt(
|
73
|
+
def _find_mean_melting_alt(
|
74
|
+
obs: ClassData, melting_layer: npt.NDArray
|
75
|
+
) -> ma.MaskedArray:
|
73
76
|
if melting_layer.dtype != bool:
|
74
77
|
msg = "melting_layer data type should be boolean"
|
75
78
|
raise ValueError(msg)
|
@@ -78,7 +81,7 @@ def _find_mean_melting_alt(obs: ClassData, melting_layer: np.ndarray) -> ma.Mask
|
|
78
81
|
return ma.median(melting_alts, axis=1)
|
79
82
|
|
80
83
|
|
81
|
-
def find_t0_alt(temperature:
|
84
|
+
def find_t0_alt(temperature: npt.NDArray, height: npt.NDArray) -> npt.NDArray:
|
82
85
|
"""Interpolates altitudes where temperature goes below freezing.
|
83
86
|
|
84
87
|
Args:
|
@@ -89,7 +92,7 @@ def find_t0_alt(temperature: np.ndarray, height: np.ndarray) -> np.ndarray:
|
|
89
92
|
1-D array denoting altitudes where the temperature drops below 0 deg C.
|
90
93
|
|
91
94
|
"""
|
92
|
-
alt:
|
95
|
+
alt: npt.NDArray = np.array([])
|
93
96
|
for prof in temperature:
|
94
97
|
ind = np.where(prof < T0)[0][0]
|
95
98
|
if ind == 0:
|
cloudnetpy/categorize/insects.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""Module to find insects from data."""
|
2
2
|
|
3
3
|
import numpy as np
|
4
|
+
import numpy.typing as npt
|
4
5
|
from numpy import ma
|
5
6
|
from scipy.ndimage import gaussian_filter
|
6
7
|
|
@@ -11,10 +12,10 @@ from cloudnetpy.categorize.containers import ClassData
|
|
11
12
|
|
12
13
|
def find_insects(
|
13
14
|
obs: ClassData,
|
14
|
-
melting_layer:
|
15
|
-
liquid_layers:
|
15
|
+
melting_layer: npt.NDArray,
|
16
|
+
liquid_layers: npt.NDArray,
|
16
17
|
prob_lim: float = 0.8,
|
17
|
-
) -> tuple[
|
18
|
+
) -> tuple[npt.NDArray, npt.NDArray]:
|
18
19
|
"""Returns insect probability and boolean array of insect presence.
|
19
20
|
|
20
21
|
Insects are classified by estimating heuristic probability
|
@@ -52,7 +53,7 @@ def find_insects(
|
|
52
53
|
return is_insects, ma.masked_where(insect_prob == 0, insect_prob)
|
53
54
|
|
54
55
|
|
55
|
-
def _insect_probability(obs: ClassData) -> tuple[
|
56
|
+
def _insect_probability(obs: ClassData) -> tuple[npt.NDArray, npt.NDArray]:
|
56
57
|
prob = _get_probabilities(obs)
|
57
58
|
prob_from_ldr = _calc_prob_from_ldr(prob)
|
58
59
|
prob_from_others = _calc_prob_from_all(prob)
|
@@ -87,7 +88,7 @@ def _get_smoothed_v(
|
|
87
88
|
return ma.masked_where(obs.v.mask, smoothed_v)
|
88
89
|
|
89
90
|
|
90
|
-
def _calc_prob_from_ldr(prob: dict) ->
|
91
|
+
def _calc_prob_from_ldr(prob: dict) -> npt.NDArray:
|
91
92
|
"""This is the most reliable proxy for insects."""
|
92
93
|
if prob["ldr"] is not None:
|
93
94
|
return prob["ldr"] * prob["temp_loose"]
|
@@ -100,7 +101,7 @@ def _calc_prob_from_ldr(prob: dict) -> np.ndarray:
|
|
100
101
|
return np.zeros(prob["z_strong"].shape)
|
101
102
|
|
102
103
|
|
103
|
-
def _calc_prob_from_all(prob: dict) ->
|
104
|
+
def _calc_prob_from_all(prob: dict) -> npt.NDArray:
|
104
105
|
"""This can be tried when LDR is not available. To detect insects without LDR
|
105
106
|
unambiguously is difficult and might result in many false positives and/or false
|
106
107
|
negatives.
|
@@ -111,8 +112,8 @@ def _calc_prob_from_all(prob: dict) -> np.ndarray:
|
|
111
112
|
def _adjust_for_radar(
|
112
113
|
obs: ClassData,
|
113
114
|
prob: dict,
|
114
|
-
prob_from_others:
|
115
|
-
) ->
|
115
|
+
prob_from_others: npt.NDArray,
|
116
|
+
) -> npt.NDArray:
|
116
117
|
"""Adds radar-specific weighting to insect probabilities."""
|
117
118
|
if "mira" in obs.radar_type.lower():
|
118
119
|
prob_from_others *= prob["lwp"]
|
@@ -120,9 +121,9 @@ def _adjust_for_radar(
|
|
120
121
|
|
121
122
|
|
122
123
|
def _fill_missing_pixels(
|
123
|
-
prob_from_ldr:
|
124
|
-
prob_from_others:
|
125
|
-
) ->
|
124
|
+
prob_from_ldr: npt.NDArray,
|
125
|
+
prob_from_others: npt.NDArray,
|
126
|
+
) -> npt.NDArray:
|
126
127
|
prob_combined = np.copy(prob_from_ldr)
|
127
128
|
no_ldr = np.where(prob_from_ldr == 0)
|
128
129
|
prob_combined[no_ldr] = prob_from_others[no_ldr]
|
@@ -130,12 +131,12 @@ def _fill_missing_pixels(
|
|
130
131
|
|
131
132
|
|
132
133
|
def _screen_insects(
|
133
|
-
insect_prob,
|
134
|
-
insect_prob_no_ldr,
|
135
|
-
melting_layer,
|
136
|
-
liquid_layers,
|
137
|
-
obs,
|
138
|
-
) ->
|
134
|
+
insect_prob: npt.NDArray,
|
135
|
+
insect_prob_no_ldr: npt.NDArray,
|
136
|
+
melting_layer: npt.NDArray,
|
137
|
+
liquid_layers: npt.NDArray,
|
138
|
+
obs: ClassData,
|
139
|
+
) -> npt.NDArray:
|
139
140
|
def _screen_liquid_layers() -> None:
|
140
141
|
prob[liquid_layers == 1] = 0
|
141
142
|
|
cloudnetpy/categorize/lidar.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
"""Lidar module, containing the :class:`Lidar` class."""
|
2
2
|
|
3
3
|
import logging
|
4
|
+
from os import PathLike
|
4
5
|
from typing import Literal
|
5
6
|
|
6
7
|
import numpy as np
|
8
|
+
import numpy.typing as npt
|
7
9
|
from numpy import ma
|
8
10
|
|
9
11
|
from cloudnetpy.datasource import DataSource
|
@@ -18,13 +20,13 @@ class Lidar(DataSource):
|
|
18
20
|
|
19
21
|
"""
|
20
22
|
|
21
|
-
def __init__(self, full_path: str):
|
23
|
+
def __init__(self, full_path: str | PathLike) -> None:
|
22
24
|
super().__init__(full_path)
|
23
25
|
self.append_data(self.getvar("beta"), "beta")
|
24
26
|
self._add_meta()
|
25
27
|
|
26
28
|
def interpolate_to_grid(
|
27
|
-
self, time_new:
|
29
|
+
self, time_new: npt.NDArray, height_new: npt.NDArray
|
28
30
|
) -> list[int]:
|
29
31
|
"""Interpolate beta using nearest neighbor."""
|
30
32
|
max_height = 100 # m
|
@@ -70,7 +72,9 @@ class Lidar(DataSource):
|
|
70
72
|
self.append_data(3.0, "beta_bias")
|
71
73
|
|
72
74
|
|
73
|
-
def get_gap_ind(
|
75
|
+
def get_gap_ind(
|
76
|
+
grid: npt.NDArray, new_grid: npt.NDArray, threshold: float
|
77
|
+
) -> list[int]:
|
74
78
|
return [
|
75
79
|
ind
|
76
80
|
for ind, value in enumerate(new_grid)
|
cloudnetpy/categorize/melting.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""Functions to find melting layer from data."""
|
2
2
|
|
3
3
|
import numpy as np
|
4
|
+
import numpy.typing as npt
|
4
5
|
from numpy import ma
|
5
6
|
from scipy.ndimage import gaussian_filter
|
6
7
|
|
@@ -10,7 +11,7 @@ from cloudnetpy.categorize.containers import ClassData
|
|
10
11
|
from cloudnetpy.constants import T0
|
11
12
|
|
12
13
|
|
13
|
-
def find_melting_layer(obs: ClassData, *, smooth: bool = True) ->
|
14
|
+
def find_melting_layer(obs: ClassData, *, smooth: bool = True) -> npt.NDArray:
|
14
15
|
"""Finds melting layer from model temperature, ldr, and velocity.
|
15
16
|
|
16
17
|
Melting layer is detected using linear depolarization ratio, *ldr*,
|
@@ -49,9 +50,9 @@ def find_melting_layer(obs: ClassData, *, smooth: bool = True) -> np.ndarray:
|
|
49
50
|
"""
|
50
51
|
melting_layer = np.zeros(obs.tw.shape, dtype=bool)
|
51
52
|
|
52
|
-
ldr_prof:
|
53
|
-
ldr_dprof:
|
54
|
-
ldr_diff:
|
53
|
+
ldr_prof: npt.NDArray | None = None
|
54
|
+
ldr_dprof: npt.NDArray | None = None
|
55
|
+
ldr_diff: npt.NDArray | None = None
|
55
56
|
width_prof = None
|
56
57
|
|
57
58
|
if hasattr(obs, "ldr"):
|
@@ -102,11 +103,11 @@ def find_melting_layer(obs: ClassData, *, smooth: bool = True) -> np.ndarray:
|
|
102
103
|
|
103
104
|
|
104
105
|
def _find_melting_layer_from_ldr(
|
105
|
-
ldr_prof:
|
106
|
-
ldr_dprof:
|
107
|
-
v_prof:
|
108
|
-
z_prof:
|
109
|
-
) ->
|
106
|
+
ldr_prof: npt.NDArray,
|
107
|
+
ldr_dprof: npt.NDArray,
|
108
|
+
v_prof: npt.NDArray,
|
109
|
+
z_prof: npt.NDArray,
|
110
|
+
) -> npt.NDArray | None:
|
110
111
|
peak = int(np.argmax(ldr_prof))
|
111
112
|
base, top = _basetop(ldr_dprof, peak)
|
112
113
|
conditions = (
|
@@ -123,10 +124,10 @@ def _find_melting_layer_from_ldr(
|
|
123
124
|
|
124
125
|
|
125
126
|
def _find_melting_layer_from_v(
|
126
|
-
v_prof:
|
127
|
-
width_prof:
|
128
|
-
height:
|
129
|
-
) ->
|
127
|
+
v_prof: npt.NDArray,
|
128
|
+
width_prof: npt.NDArray | None,
|
129
|
+
height: npt.NDArray,
|
130
|
+
) -> npt.NDArray | None:
|
130
131
|
v = np.copy(v_prof[:-1])
|
131
132
|
v_diff = np.diff(v_prof)
|
132
133
|
v[v_diff < 0] = 0
|
@@ -156,14 +157,14 @@ def _find_melting_layer_from_v(
|
|
156
157
|
return None
|
157
158
|
|
158
159
|
|
159
|
-
def _basetop(dprof:
|
160
|
+
def _basetop(dprof: npt.NDArray, pind: int) -> tuple[int, int]:
|
160
161
|
"""Finds the base and top of ldr peak."""
|
161
162
|
top = droplet.ind_top(dprof, pind, len(dprof), 10, 2)
|
162
163
|
base = droplet.ind_base(dprof, pind, 10, 2)
|
163
164
|
return base, top
|
164
165
|
|
165
166
|
|
166
|
-
def _get_temp_indices(t_prof:
|
167
|
+
def _get_temp_indices(t_prof: npt.NDArray, t_range: tuple) -> npt.NDArray:
|
167
168
|
"""Finds indices of temperature profile covering the given range."""
|
168
169
|
ind = np.where((t_prof > min(t_range) + T0) & (t_prof < max(t_range) + T0))[0]
|
169
170
|
return np.array([]) if len(ind) == 0 else np.arange(np.min(ind), np.max(ind) + 1)
|
cloudnetpy/categorize/model.py
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
"""Model module, containing the :class:`Model` class."""
|
2
2
|
|
3
|
+
import os.path
|
4
|
+
from os import PathLike
|
5
|
+
|
3
6
|
import numpy as np
|
7
|
+
import numpy.typing as npt
|
4
8
|
from numpy import ma
|
5
9
|
from scipy.interpolate import interp1d
|
6
10
|
|
@@ -49,13 +53,15 @@ class Model(DataSource):
|
|
49
53
|
"specific_liquid_atten",
|
50
54
|
)
|
51
55
|
|
52
|
-
def __init__(
|
56
|
+
def __init__(
|
57
|
+
self, model_file: str | PathLike, alt_site: float, options: dict | None = None
|
58
|
+
) -> None:
|
53
59
|
super().__init__(model_file)
|
54
60
|
self.options = options
|
55
61
|
self.source_type = _find_model_type(model_file)
|
56
62
|
self.model_heights = self._get_model_heights(alt_site)
|
57
63
|
self.mean_height = _calc_mean_height(self.model_heights)
|
58
|
-
self.height:
|
64
|
+
self.height: npt.NDArray
|
59
65
|
self.data_sparse: dict = {}
|
60
66
|
self.data_dense: dict = {}
|
61
67
|
self._append_grid()
|
@@ -83,8 +89,8 @@ class Model(DataSource):
|
|
83
89
|
|
84
90
|
def interpolate_to_grid(
|
85
91
|
self,
|
86
|
-
time_grid:
|
87
|
-
height_grid:
|
92
|
+
time_grid: npt.NDArray,
|
93
|
+
height_grid: npt.NDArray,
|
88
94
|
) -> list:
|
89
95
|
"""Interpolates model variables to Cloudnet's dense time / height grid.
|
90
96
|
|
@@ -130,7 +136,7 @@ class Model(DataSource):
|
|
130
136
|
self.append_data(np.array(self.time), "model_time")
|
131
137
|
self.append_data(self.mean_height, "model_height")
|
132
138
|
|
133
|
-
def _get_model_heights(self, alt_site: float) ->
|
139
|
+
def _get_model_heights(self, alt_site: float) -> npt.NDArray:
|
134
140
|
"""Returns model heights for each time step."""
|
135
141
|
try:
|
136
142
|
model_heights = self.dataset.variables["height"]
|
@@ -139,7 +145,7 @@ class Model(DataSource):
|
|
139
145
|
raise ModelDataError(msg) from err
|
140
146
|
return self.to_m(model_heights) + alt_site
|
141
147
|
|
142
|
-
def calc_attenuations(self, frequency: float):
|
148
|
+
def calc_attenuations(self, frequency: float) -> None:
|
143
149
|
temperature = self.getvar("temperature")
|
144
150
|
pressure = self.getvar("pressure")
|
145
151
|
specific_humidity = self.getvar("q")
|
@@ -157,22 +163,23 @@ class Model(DataSource):
|
|
157
163
|
)
|
158
164
|
|
159
165
|
|
160
|
-
def _calc_mean_height(model_heights:
|
166
|
+
def _calc_mean_height(model_heights: npt.NDArray) -> npt.NDArray:
|
161
167
|
mean_height = ma.mean(model_heights, axis=0)
|
162
168
|
return np.array(mean_height)
|
163
169
|
|
164
170
|
|
165
|
-
def _find_model_type(file_name: str) -> str:
|
171
|
+
def _find_model_type(file_name: str | PathLike) -> str:
|
166
172
|
"""Finds model type from the model filename."""
|
167
173
|
possible_keys = ("gdas1", "icon", "ecmwf", "harmonie", "era5", "arpege")
|
174
|
+
basename = os.path.basename(file_name)
|
168
175
|
for key in possible_keys:
|
169
|
-
if key in
|
176
|
+
if key in basename:
|
170
177
|
return key
|
171
178
|
msg = f"Unknown model type: {file_name}"
|
172
179
|
raise ValueError(msg)
|
173
180
|
|
174
181
|
|
175
|
-
def _find_number_of_valid_profiles(array:
|
182
|
+
def _find_number_of_valid_profiles(array: npt.NDArray) -> int:
|
176
183
|
mask = ma.getmaskarray(array)
|
177
184
|
all_masked_profiles = np.all(mask, axis=1)
|
178
185
|
return np.count_nonzero(~all_masked_profiles)
|
cloudnetpy/categorize/mwr.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
"""Mwr module, containing the :class:`Mwr` class."""
|
2
2
|
|
3
|
-
|
3
|
+
from os import PathLike
|
4
|
+
|
4
5
|
import numpy.ma as ma
|
6
|
+
import numpy.typing as npt
|
5
7
|
|
6
8
|
from cloudnetpy import utils
|
7
9
|
from cloudnetpy.constants import G_TO_KG
|
@@ -16,12 +18,12 @@ class Mwr(DataSource):
|
|
16
18
|
|
17
19
|
"""
|
18
20
|
|
19
|
-
def __init__(self, full_path: str):
|
21
|
+
def __init__(self, full_path: str | PathLike) -> None:
|
20
22
|
super().__init__(full_path)
|
21
23
|
self._init_lwp_data()
|
22
24
|
self._init_lwp_error()
|
23
25
|
|
24
|
-
def rebin_to_grid(self, time_grid:
|
26
|
+
def rebin_to_grid(self, time_grid: npt.NDArray) -> None:
|
25
27
|
"""Approximates lwp and its error in a grid using mean.
|
26
28
|
|
27
29
|
Args:
|
cloudnetpy/categorize/radar.py
CHANGED
@@ -2,8 +2,10 @@
|
|
2
2
|
|
3
3
|
import logging
|
4
4
|
import math
|
5
|
+
from os import PathLike
|
5
6
|
|
6
7
|
import numpy as np
|
8
|
+
import numpy.typing as npt
|
7
9
|
from numpy import ma
|
8
10
|
from scipy import constants
|
9
11
|
|
@@ -34,16 +36,16 @@ class Radar(DataSource):
|
|
34
36
|
|
35
37
|
"""
|
36
38
|
|
37
|
-
def __init__(self, full_path: str):
|
39
|
+
def __init__(self, full_path: str | PathLike) -> None:
|
38
40
|
super().__init__(full_path, radar=True)
|
39
41
|
self.radar_frequency = float(self.getvar("radar_frequency"))
|
40
42
|
self.location = getattr(self.dataset, "location", "")
|
41
43
|
self.source_type = getattr(self.dataset, "source", "")
|
42
|
-
self.height:
|
44
|
+
self.height: npt.NDArray
|
43
45
|
self.altitude: float
|
44
46
|
self._init_data()
|
45
47
|
|
46
|
-
def rebin_to_grid(self, time_new:
|
48
|
+
def rebin_to_grid(self, time_new: npt.NDArray) -> list:
|
47
49
|
"""Rebins radar data in time.
|
48
50
|
|
49
51
|
Args:
|
@@ -151,7 +153,7 @@ class Radar(DataSource):
|
|
151
153
|
|
152
154
|
def _filter(
|
153
155
|
self,
|
154
|
-
data:
|
156
|
+
data: npt.NDArray,
|
155
157
|
axis: int,
|
156
158
|
min_coverage: float,
|
157
159
|
z_limit: float,
|
@@ -224,7 +226,7 @@ class Radar(DataSource):
|
|
224
226
|
def calc_errors(
|
225
227
|
self,
|
226
228
|
attenuations: RadarAttenuation,
|
227
|
-
is_clutter:
|
229
|
+
is_clutter: npt.NDArray,
|
228
230
|
) -> None:
|
229
231
|
"""Calculates uncertainties of radar echo.
|
230
232
|
|
@@ -241,7 +243,7 @@ class Radar(DataSource):
|
|
241
243
|
|
242
244
|
"""
|
243
245
|
|
244
|
-
def _calc_sensitivity() ->
|
246
|
+
def _calc_sensitivity() -> npt.NDArray:
|
245
247
|
"""Returns sensitivity of radar as function of altitude."""
|
246
248
|
mean_gas_atten = ma.mean(attenuations.gas.amount, axis=0)
|
247
249
|
z_sensitivity = z_power_min + log_range + mean_gas_atten
|
@@ -250,7 +252,7 @@ class Radar(DataSource):
|
|
250
252
|
z_sensitivity[valid_values] = zc[valid_values]
|
251
253
|
return z_sensitivity
|
252
254
|
|
253
|
-
def _calc_error() ->
|
255
|
+
def _calc_error() -> npt.NDArray | float:
|
254
256
|
"""Returns error of radar as function of altitude.
|
255
257
|
|
256
258
|
References:
|
@@ -317,7 +319,7 @@ class Radar(DataSource):
|
|
317
319
|
for key in ("time", "height", "radar_frequency"):
|
318
320
|
self.append_data(np.array(getattr(self, key)), key)
|
319
321
|
|
320
|
-
def add_location(self, time_new:
|
322
|
+
def add_location(self, time_new: npt.NDArray) -> None:
|
321
323
|
"""Add latitude, longitude and altitude from nearest timestamp."""
|
322
324
|
idx = np.searchsorted(self.time, time_new)
|
323
325
|
idx_left = np.clip(idx - 1, 0, len(self.time) - 1)
|
@@ -343,9 +345,9 @@ class Radar(DataSource):
|
|
343
345
|
|
344
346
|
def _rebin_velocity(
|
345
347
|
self,
|
346
|
-
data:
|
347
|
-
time_new:
|
348
|
-
) ->
|
348
|
+
data: npt.NDArray,
|
349
|
+
time_new: npt.NDArray,
|
350
|
+
) -> npt.NDArray:
|
349
351
|
"""Rebins Doppler velocity in polar coordinates."""
|
350
352
|
folding_velocity = self._get_expanded_folding_velocity()
|
351
353
|
# with the new shape (maximum value in every bin)
|
@@ -384,7 +386,7 @@ class Radar(DataSource):
|
|
384
386
|
vel_scaled = ma.arctan2(vel_y_mean, vel_x_mean)
|
385
387
|
return vel_scaled / (np.pi / max_folding_binned)
|
386
388
|
|
387
|
-
def _get_expanded_folding_velocity(self) ->
|
389
|
+
def _get_expanded_folding_velocity(self) -> npt.NDArray:
|
388
390
|
if "nyquist_velocity" in self.dataset.variables:
|
389
391
|
fvel = self.getvar("nyquist_velocity")
|
390
392
|
elif "prf" in self.dataset.variables:
|
@@ -425,7 +427,7 @@ class Radar(DataSource):
|
|
425
427
|
return np.repeat(fvel.ravel(), chirp_size.ravel()).reshape((n_time, n_height))
|
426
428
|
|
427
429
|
|
428
|
-
def _prf_to_folding_velocity(prf:
|
430
|
+
def _prf_to_folding_velocity(prf: npt.NDArray, radar_frequency: float) -> npt.NDArray:
|
429
431
|
ghz_to_hz = 1e9
|
430
432
|
if len(prf) != 1:
|
431
433
|
msg = "Unable to determine folding velocity"
|
cloudnetpy/cli.py
CHANGED
@@ -2,19 +2,19 @@ import argparse
|
|
2
2
|
import gzip
|
3
3
|
import importlib
|
4
4
|
import logging
|
5
|
-
import os
|
6
5
|
import re
|
7
6
|
import shutil
|
8
7
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
9
8
|
from dataclasses import dataclass
|
9
|
+
from os import PathLike
|
10
10
|
from pathlib import Path
|
11
11
|
from tempfile import TemporaryDirectory
|
12
|
-
from typing import TYPE_CHECKING, Final
|
12
|
+
from typing import TYPE_CHECKING, Final, cast
|
13
13
|
|
14
14
|
import requests
|
15
15
|
|
16
16
|
from cloudnetpy import concat_lib, instruments
|
17
|
-
from cloudnetpy.categorize import generate_categorize
|
17
|
+
from cloudnetpy.categorize import CategorizeInput, generate_categorize
|
18
18
|
from cloudnetpy.exceptions import PlottingError
|
19
19
|
from cloudnetpy.plotting import generate_figure
|
20
20
|
from cloudnetpy.utils import md5sum
|
@@ -33,7 +33,7 @@ class Instrument:
|
|
33
33
|
name: str
|
34
34
|
|
35
35
|
|
36
|
-
def run(args: argparse.Namespace, tmpdir: str):
|
36
|
+
def run(args: argparse.Namespace, tmpdir: str) -> None:
|
37
37
|
cat_files = {}
|
38
38
|
|
39
39
|
# Instrument based products
|
@@ -104,7 +104,7 @@ def _process_categorize(input_files: dict, args: argparse.Namespace) -> str | No
|
|
104
104
|
|
105
105
|
try:
|
106
106
|
logging.info("Processing categorize...")
|
107
|
-
generate_categorize(input_files, cat_filepath)
|
107
|
+
generate_categorize(cast("CategorizeInput", input_files), cat_filepath)
|
108
108
|
logging.info("Processed categorize to %s", cat_filepath)
|
109
109
|
except NameError:
|
110
110
|
logging.info("No data available for this date.")
|
@@ -225,7 +225,7 @@ def _fetch_coefficient_files(calibration: dict, tmpdir: str) -> list:
|
|
225
225
|
return coefficient_paths
|
226
226
|
|
227
227
|
|
228
|
-
def _get_calibration(instrument: Instrument, args) -> dict:
|
228
|
+
def _get_calibration(instrument: Instrument, args: argparse.Namespace) -> dict:
|
229
229
|
params = {
|
230
230
|
"date": args.date,
|
231
231
|
"instrumentPid": instrument.pid,
|
@@ -484,7 +484,9 @@ def _check_input(files: list) -> None:
|
|
484
484
|
raise ValueError(msg)
|
485
485
|
|
486
486
|
|
487
|
-
def _plot(
|
487
|
+
def _plot(
|
488
|
+
filepath: PathLike | str | None, product: str, args: argparse.Namespace
|
489
|
+
) -> None:
|
488
490
|
if filepath is None or (not args.plot and not args.show):
|
489
491
|
return
|
490
492
|
res = requests.get(f"{cloudnet_api_url}products/variables", timeout=60)
|
@@ -543,7 +545,7 @@ def _parse_products(product_argument: str) -> list[str]:
|
|
543
545
|
return valid_products
|
544
546
|
|
545
547
|
|
546
|
-
def main():
|
548
|
+
def main() -> None:
|
547
549
|
parser = argparse.ArgumentParser(
|
548
550
|
description="Command line interface for running CloudnetPy."
|
549
551
|
)
|
cloudnetpy/cloudnetarray.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
"""CloudnetArray class."""
|
2
2
|
|
3
|
-
from collections.abc import Sequence
|
3
|
+
from collections.abc import Callable, Sequence
|
4
4
|
|
5
5
|
import netCDF4
|
6
6
|
import numpy as np
|
7
|
+
import numpy.typing as npt
|
7
8
|
from numpy import ma
|
8
9
|
|
9
10
|
from cloudnetpy import utils
|
@@ -26,13 +27,13 @@ class CloudnetArray:
|
|
26
27
|
|
27
28
|
def __init__(
|
28
29
|
self,
|
29
|
-
variable: netCDF4.Variable |
|
30
|
+
variable: netCDF4.Variable | npt.NDArray | float,
|
30
31
|
name: str,
|
31
32
|
units_from_user: str | None = None,
|
32
33
|
dimensions: Sequence[str] | None = None,
|
33
34
|
data_type: str | None = None,
|
34
35
|
source: str | None = None,
|
35
|
-
):
|
36
|
+
) -> None:
|
36
37
|
self.variable = variable
|
37
38
|
self.name = name
|
38
39
|
self.data = self._init_data()
|
@@ -57,7 +58,7 @@ class CloudnetArray:
|
|
57
58
|
"""Masks data from given indices."""
|
58
59
|
self.data[ind] = ma.masked
|
59
60
|
|
60
|
-
def rebin_data(self, time:
|
61
|
+
def rebin_data(self, time: npt.NDArray, time_new: npt.NDArray) -> npt.NDArray:
|
61
62
|
"""Rebins `data` in time.
|
62
63
|
|
63
64
|
Args:
|
@@ -108,14 +109,14 @@ class CloudnetArray:
|
|
108
109
|
"""Filters vertical artifacts from radar data."""
|
109
110
|
self._filter(utils.filter_x_pixels)
|
110
111
|
|
111
|
-
def _filter(self, fun) -> None:
|
112
|
+
def _filter(self, fun: Callable[[npt.NDArray], npt.NDArray]) -> None:
|
112
113
|
if not isinstance(self.data, ma.MaskedArray):
|
113
114
|
self.data = ma.masked_array(self.data)
|
114
115
|
is_data = (~self.data.mask).astype(int)
|
115
116
|
is_data_filtered = fun(is_data)
|
116
117
|
self.data[is_data_filtered == 0] = ma.masked
|
117
118
|
|
118
|
-
def _init_data(self) ->
|
119
|
+
def _init_data(self) -> npt.NDArray:
|
119
120
|
if isinstance(self.variable, netCDF4.Variable):
|
120
121
|
return self.variable[:]
|
121
122
|
if isinstance(self.variable, np.ndarray):
|
@@ -144,5 +145,5 @@ class CloudnetArray:
|
|
144
145
|
return "i2"
|
145
146
|
return "i4"
|
146
147
|
|
147
|
-
def __getitem__(self, ind: tuple) ->
|
148
|
+
def __getitem__(self, ind: tuple) -> npt.NDArray:
|
148
149
|
return self.data[ind]
|