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.
Files changed (83) hide show
  1. cloudnetpy/categorize/__init__.py +1 -1
  2. cloudnetpy/categorize/atmos_utils.py +31 -27
  3. cloudnetpy/categorize/attenuations/__init__.py +4 -4
  4. cloudnetpy/categorize/attenuations/liquid_attenuation.py +7 -5
  5. cloudnetpy/categorize/attenuations/melting_attenuation.py +3 -3
  6. cloudnetpy/categorize/attenuations/rain_attenuation.py +4 -4
  7. cloudnetpy/categorize/categorize.py +25 -11
  8. cloudnetpy/categorize/classify.py +9 -8
  9. cloudnetpy/categorize/containers.py +13 -10
  10. cloudnetpy/categorize/disdrometer.py +5 -3
  11. cloudnetpy/categorize/droplet.py +12 -9
  12. cloudnetpy/categorize/falling.py +9 -8
  13. cloudnetpy/categorize/freezing.py +10 -7
  14. cloudnetpy/categorize/insects.py +18 -17
  15. cloudnetpy/categorize/lidar.py +7 -3
  16. cloudnetpy/categorize/melting.py +16 -15
  17. cloudnetpy/categorize/model.py +17 -10
  18. cloudnetpy/categorize/mwr.py +5 -3
  19. cloudnetpy/categorize/radar.py +15 -13
  20. cloudnetpy/cli.py +10 -8
  21. cloudnetpy/cloudnetarray.py +8 -7
  22. cloudnetpy/concat_lib.py +29 -20
  23. cloudnetpy/datasource.py +26 -21
  24. cloudnetpy/exceptions.py +12 -10
  25. cloudnetpy/instruments/basta.py +19 -9
  26. cloudnetpy/instruments/bowtie.py +18 -11
  27. cloudnetpy/instruments/ceilo.py +22 -10
  28. cloudnetpy/instruments/ceilometer.py +33 -34
  29. cloudnetpy/instruments/cl61d.py +5 -3
  30. cloudnetpy/instruments/cloudnet_instrument.py +7 -7
  31. cloudnetpy/instruments/copernicus.py +16 -7
  32. cloudnetpy/instruments/disdrometer/common.py +5 -4
  33. cloudnetpy/instruments/disdrometer/parsivel.py +14 -9
  34. cloudnetpy/instruments/disdrometer/thies.py +11 -7
  35. cloudnetpy/instruments/fd12p.py +7 -6
  36. cloudnetpy/instruments/galileo.py +16 -7
  37. cloudnetpy/instruments/hatpro.py +33 -24
  38. cloudnetpy/instruments/lufft.py +6 -4
  39. cloudnetpy/instruments/mira.py +33 -19
  40. cloudnetpy/instruments/mrr.py +12 -12
  41. cloudnetpy/instruments/nc_lidar.py +1 -1
  42. cloudnetpy/instruments/nc_radar.py +8 -8
  43. cloudnetpy/instruments/pollyxt.py +19 -12
  44. cloudnetpy/instruments/radiometrics.py +17 -10
  45. cloudnetpy/instruments/rain_e_h3.py +9 -5
  46. cloudnetpy/instruments/rpg.py +32 -21
  47. cloudnetpy/instruments/rpg_reader.py +15 -12
  48. cloudnetpy/instruments/vaisala.py +32 -24
  49. cloudnetpy/instruments/weather_station.py +22 -19
  50. cloudnetpy/model_evaluation/file_handler.py +27 -29
  51. cloudnetpy/model_evaluation/plotting/plot_tools.py +7 -5
  52. cloudnetpy/model_evaluation/plotting/plotting.py +41 -32
  53. cloudnetpy/model_evaluation/products/advance_methods.py +38 -34
  54. cloudnetpy/model_evaluation/products/grid_methods.py +10 -9
  55. cloudnetpy/model_evaluation/products/model_products.py +15 -9
  56. cloudnetpy/model_evaluation/products/observation_products.py +12 -10
  57. cloudnetpy/model_evaluation/products/product_resampling.py +11 -7
  58. cloudnetpy/model_evaluation/products/tools.py +18 -14
  59. cloudnetpy/model_evaluation/statistics/statistical_methods.py +6 -5
  60. cloudnetpy/model_evaluation/tests/unit/test_plotting.py +18 -25
  61. cloudnetpy/model_evaluation/utils.py +3 -3
  62. cloudnetpy/output.py +15 -32
  63. cloudnetpy/plotting/plotting.py +22 -12
  64. cloudnetpy/products/classification.py +15 -9
  65. cloudnetpy/products/der.py +24 -19
  66. cloudnetpy/products/drizzle.py +21 -13
  67. cloudnetpy/products/drizzle_error.py +8 -7
  68. cloudnetpy/products/drizzle_tools.py +27 -23
  69. cloudnetpy/products/epsilon.py +6 -5
  70. cloudnetpy/products/ier.py +11 -5
  71. cloudnetpy/products/iwc.py +18 -9
  72. cloudnetpy/products/lwc.py +41 -31
  73. cloudnetpy/products/mwr_tools.py +30 -19
  74. cloudnetpy/products/product_tools.py +23 -19
  75. cloudnetpy/utils.py +84 -98
  76. cloudnetpy/version.py +2 -2
  77. {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.0.dist-info}/METADATA +2 -1
  78. cloudnetpy-1.81.0.dist-info/RECORD +126 -0
  79. cloudnetpy-1.80.8.dist-info/RECORD +0 -126
  80. {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.0.dist-info}/WHEEL +0 -0
  81. {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.0.dist-info}/entry_points.txt +0 -0
  82. {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.0.dist-info}/licenses/LICENSE +0 -0
  83. {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: np.ndarray) -> np.ndarray:
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: np.ndarray,
64
- t0_alt: np.ndarray,
65
- height: np.ndarray,
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(obs: ClassData, melting_layer: np.ndarray) -> ma.MaskedArray:
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: np.ndarray, height: np.ndarray) -> np.ndarray:
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: np.ndarray = np.array([])
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:
@@ -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: np.ndarray,
15
- liquid_layers: np.ndarray,
15
+ melting_layer: npt.NDArray,
16
+ liquid_layers: npt.NDArray,
16
17
  prob_lim: float = 0.8,
17
- ) -> tuple[np.ndarray, np.ndarray]:
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[np.ndarray, np.ndarray]:
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) -> np.ndarray:
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) -> np.ndarray:
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: np.ndarray,
115
- ) -> np.ndarray:
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: np.ndarray,
124
- prob_from_others: np.ndarray,
125
- ) -> np.ndarray:
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
- ) -> np.ndarray:
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
 
@@ -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: np.ndarray, height_new: np.ndarray
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(grid: np.ndarray, new_grid: np.ndarray, threshold: float) -> list[int]:
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)
@@ -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) -> np.ndarray:
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: np.ndarray | None = None
53
- ldr_dprof: np.ndarray | None = None
54
- ldr_diff: np.ndarray | None = None
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: np.ndarray,
106
- ldr_dprof: np.ndarray,
107
- v_prof: np.ndarray,
108
- z_prof: np.ndarray,
109
- ) -> np.ndarray | None:
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: np.ndarray,
127
- width_prof: np.ndarray | None,
128
- height: np.ndarray,
129
- ) -> np.ndarray | None:
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: np.ndarray, pind: int) -> tuple[int, int]:
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: np.ndarray, t_range: tuple) -> np.ndarray:
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)
@@ -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__(self, model_file: str, alt_site: float, options: dict | None = None):
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: np.ndarray
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: np.ndarray,
87
- height_grid: np.ndarray,
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) -> np.ndarray:
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: np.ndarray) -> np.ndarray:
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 file_name:
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: np.ndarray) -> int:
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)
@@ -1,7 +1,9 @@
1
1
  """Mwr module, containing the :class:`Mwr` class."""
2
2
 
3
- import numpy as np
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: np.ndarray) -> None:
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:
@@ -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: np.ndarray
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: np.ndarray) -> list:
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: np.ndarray,
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: np.ndarray,
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() -> np.ndarray:
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() -> np.ndarray | float:
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: np.ndarray):
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: np.ndarray,
347
- time_new: np.ndarray,
348
- ) -> np.ndarray:
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) -> np.ndarray:
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: np.ndarray, radar_frequency: float) -> np.ndarray:
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(filepath: os.PathLike | str | None, product: str, args: argparse.Namespace):
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
  )
@@ -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 | np.ndarray | float,
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: np.ndarray, time_new: np.ndarray) -> np.ndarray:
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) -> np.ndarray:
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) -> np.ndarray:
148
+ def __getitem__(self, ind: tuple) -> npt.NDArray:
148
149
  return self.data[ind]