cloudnetpy 1.55.20__py3-none-any.whl → 1.55.22__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 (95) hide show
  1. cloudnetpy/categorize/atmos.py +46 -14
  2. cloudnetpy/categorize/atmos_utils.py +11 -1
  3. cloudnetpy/categorize/categorize.py +38 -21
  4. cloudnetpy/categorize/classify.py +31 -9
  5. cloudnetpy/categorize/containers.py +19 -7
  6. cloudnetpy/categorize/droplet.py +24 -8
  7. cloudnetpy/categorize/falling.py +17 -7
  8. cloudnetpy/categorize/freezing.py +19 -5
  9. cloudnetpy/categorize/insects.py +27 -14
  10. cloudnetpy/categorize/lidar.py +38 -36
  11. cloudnetpy/categorize/melting.py +19 -9
  12. cloudnetpy/categorize/model.py +28 -9
  13. cloudnetpy/categorize/mwr.py +4 -2
  14. cloudnetpy/categorize/radar.py +58 -22
  15. cloudnetpy/cloudnetarray.py +15 -6
  16. cloudnetpy/concat_lib.py +39 -16
  17. cloudnetpy/constants.py +7 -0
  18. cloudnetpy/datasource.py +39 -19
  19. cloudnetpy/instruments/basta.py +6 -2
  20. cloudnetpy/instruments/campbell_scientific.py +33 -16
  21. cloudnetpy/instruments/ceilo.py +30 -13
  22. cloudnetpy/instruments/ceilometer.py +76 -37
  23. cloudnetpy/instruments/cl61d.py +8 -3
  24. cloudnetpy/instruments/cloudnet_instrument.py +2 -1
  25. cloudnetpy/instruments/copernicus.py +27 -14
  26. cloudnetpy/instruments/disdrometer/common.py +51 -32
  27. cloudnetpy/instruments/disdrometer/parsivel.py +79 -48
  28. cloudnetpy/instruments/disdrometer/thies.py +10 -6
  29. cloudnetpy/instruments/galileo.py +23 -12
  30. cloudnetpy/instruments/hatpro.py +27 -11
  31. cloudnetpy/instruments/instruments.py +4 -1
  32. cloudnetpy/instruments/lufft.py +20 -11
  33. cloudnetpy/instruments/mira.py +60 -49
  34. cloudnetpy/instruments/mrr.py +31 -20
  35. cloudnetpy/instruments/nc_lidar.py +15 -6
  36. cloudnetpy/instruments/nc_radar.py +31 -22
  37. cloudnetpy/instruments/pollyxt.py +36 -21
  38. cloudnetpy/instruments/radiometrics.py +32 -18
  39. cloudnetpy/instruments/rpg.py +48 -22
  40. cloudnetpy/instruments/rpg_reader.py +39 -30
  41. cloudnetpy/instruments/vaisala.py +39 -27
  42. cloudnetpy/instruments/weather_station.py +15 -11
  43. cloudnetpy/metadata.py +3 -1
  44. cloudnetpy/model_evaluation/file_handler.py +31 -21
  45. cloudnetpy/model_evaluation/metadata.py +3 -1
  46. cloudnetpy/model_evaluation/model_metadata.py +1 -1
  47. cloudnetpy/model_evaluation/plotting/plot_tools.py +20 -15
  48. cloudnetpy/model_evaluation/plotting/plotting.py +114 -64
  49. cloudnetpy/model_evaluation/products/advance_methods.py +48 -28
  50. cloudnetpy/model_evaluation/products/grid_methods.py +44 -19
  51. cloudnetpy/model_evaluation/products/model_products.py +22 -18
  52. cloudnetpy/model_evaluation/products/observation_products.py +15 -9
  53. cloudnetpy/model_evaluation/products/product_resampling.py +14 -4
  54. cloudnetpy/model_evaluation/products/tools.py +16 -7
  55. cloudnetpy/model_evaluation/statistics/statistical_methods.py +28 -15
  56. cloudnetpy/model_evaluation/tests/e2e/conftest.py +3 -3
  57. cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +9 -5
  58. cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +14 -13
  59. cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +9 -5
  60. cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +14 -13
  61. cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +9 -5
  62. cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +14 -13
  63. cloudnetpy/model_evaluation/tests/unit/conftest.py +11 -11
  64. cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +33 -27
  65. cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +83 -83
  66. cloudnetpy/model_evaluation/tests/unit/test_model_products.py +23 -21
  67. cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +24 -25
  68. cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +40 -39
  69. cloudnetpy/model_evaluation/tests/unit/test_plotting.py +12 -11
  70. cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +30 -30
  71. cloudnetpy/model_evaluation/tests/unit/test_tools.py +18 -17
  72. cloudnetpy/model_evaluation/utils.py +3 -2
  73. cloudnetpy/output.py +45 -19
  74. cloudnetpy/plotting/plot_meta.py +35 -11
  75. cloudnetpy/plotting/plotting.py +172 -104
  76. cloudnetpy/products/classification.py +20 -8
  77. cloudnetpy/products/der.py +25 -10
  78. cloudnetpy/products/drizzle.py +41 -26
  79. cloudnetpy/products/drizzle_error.py +10 -5
  80. cloudnetpy/products/drizzle_tools.py +43 -24
  81. cloudnetpy/products/ier.py +10 -5
  82. cloudnetpy/products/iwc.py +16 -9
  83. cloudnetpy/products/lwc.py +34 -12
  84. cloudnetpy/products/mwr_multi.py +4 -1
  85. cloudnetpy/products/mwr_single.py +4 -1
  86. cloudnetpy/products/product_tools.py +33 -10
  87. cloudnetpy/utils.py +175 -74
  88. cloudnetpy/version.py +1 -1
  89. {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/METADATA +11 -10
  90. cloudnetpy-1.55.22.dist-info/RECORD +114 -0
  91. docs/source/conf.py +2 -2
  92. cloudnetpy-1.55.20.dist-info/RECORD +0 -114
  93. {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/LICENSE +0 -0
  94. {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/WHEEL +0 -0
  95. {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@ PRODUCT_cf = ["cf", "ECMWF", "Cloud fraction"]
8
8
  PRODUCT_iwc = ["iwc", "ECMWF", "Ice water content"]
9
9
 
10
10
 
11
- def test_relative_error():
11
+ def test_relative_error() -> None:
12
12
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
13
13
  observation = ma.array([[3, 2, 5, 4], [4, 6, 8, 4]])
14
14
  x, _ = sts.relative_error(model, observation)
@@ -16,7 +16,7 @@ def test_relative_error():
16
16
  testing.assert_array_almost_equal(x, compare)
17
17
 
18
18
 
19
- def test_relative_error_mask():
19
+ def test_relative_error_mask() -> None:
20
20
  model = ma.array([[1, 2, 2, 3], [2, 1, 10, 1]])
21
21
  model.mask = np.array([[0, 0, 0, 1], [0, 1, 0, 1]])
22
22
  observation = ma.array([[1, 2, 5, 4], [4, 6, 8, 1]])
@@ -26,12 +26,12 @@ def test_relative_error_mask():
26
26
  [
27
27
  ma.array([-99, 0.0, -60.0, -99], mask=[1, 0, 0, 1]),
28
28
  ma.array([-50.0, -83.33, 25.0, -99], mask=[0, 0, 0, 1]),
29
- ]
29
+ ],
30
30
  )
31
31
  testing.assert_array_almost_equal(x, compare)
32
32
 
33
33
 
34
- def test_relative_error_nan():
34
+ def test_relative_error_nan() -> None:
35
35
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
36
36
  observation = ma.array([[3, 2, 5, np.nan], [4, np.nan, 8, 4]])
37
37
  x, _ = sts.relative_error(model, observation)
@@ -39,12 +39,12 @@ def test_relative_error_nan():
39
39
  [
40
40
  ma.array([-66.67, 0.0, -60.0, -99], mask=[0, 0, 0, 1]),
41
41
  ma.array([-50.0, -99, 25.0, -75.0], mask=[0, 1, 0, 0]),
42
- ]
42
+ ],
43
43
  )
44
44
  testing.assert_array_almost_equal(x, compare)
45
45
 
46
46
 
47
- def test_absolute_error():
47
+ def test_absolute_error() -> None:
48
48
  model = ma.array([[0.1, 0.2, 0.2, 0.3], [0.2, 0.4, 1.0, 0.0]])
49
49
  observation = ma.array([[0.2, 0.2, 0.1, 0.4], [0.4, 0.6, 0.8, 0.2]])
50
50
  x, _ = sts.absolute_error(model, observation)
@@ -52,7 +52,7 @@ def test_absolute_error():
52
52
  testing.assert_array_almost_equal(x, compare)
53
53
 
54
54
 
55
- def test_absolute_error_nan():
55
+ def test_absolute_error_nan() -> None:
56
56
  model = ma.array([[0.1, 0.2, 0.2, 0.3], [0.2, 0.4, 1.0, 0.0]])
57
57
  observation = ma.array([[0.2, np.nan, 0.1, 0.4], [np.nan, 0.6, 0.8, 0.2]])
58
58
  x, _ = sts.absolute_error(model, observation)
@@ -60,12 +60,12 @@ def test_absolute_error_nan():
60
60
  [
61
61
  ma.array([10.0, -99, -10.0, 10.0], mask=[0, 1, 0, 0]),
62
62
  ma.array([-99, 20.0, -20.0, 20.0], mask=[1, 0, 0, 0]),
63
- ]
63
+ ],
64
64
  )
65
65
  testing.assert_array_almost_equal(x, compare)
66
66
 
67
67
 
68
- def test_absolute_error_mask():
68
+ def test_absolute_error_mask() -> None:
69
69
  model = ma.array([[0.1, 0.2, 0.2, 0.3], [0.2, 0.4, 1.0, 0.0]])
70
70
  model.mask = np.array([[0, 0, 0, 1], [0, 1, 0, 1]])
71
71
  observation = ma.array([[0.2, 0.2, 0.1, 0.4], [0.4, 0.6, 0.8, 0.2]])
@@ -75,12 +75,12 @@ def test_absolute_error_mask():
75
75
  [
76
76
  ma.array([10.0, 0.0, -10.0, -99], mask=[0, 0, 0, 1]),
77
77
  ma.array([20.0, -99, -20.0, -99], mask=[0, 1, 0, 1]),
78
- ]
78
+ ],
79
79
  )
80
80
  testing.assert_array_almost_equal(x, compare)
81
81
 
82
82
 
83
- def test_combine_masked_indices():
83
+ def test_combine_masked_indices() -> None:
84
84
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
85
85
  observation = ma.array([[3, 2, 5, 4], [4, 6, 8, 4]])
86
86
  x, y = sts.combine_masked_indices(model, observation)
@@ -90,7 +90,7 @@ def test_combine_masked_indices():
90
90
  testing.assert_array_almost_equal(y, compare_o)
91
91
 
92
92
 
93
- def test_combine_masked_indices_min():
93
+ def test_combine_masked_indices_min() -> None:
94
94
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
95
95
  observation = ma.array([[3, 2, 5, 4], [4, 6, 8, 4]])
96
96
  x, y = sts.combine_masked_indices(model, observation)
@@ -99,20 +99,20 @@ def test_combine_masked_indices_min():
99
99
  [
100
100
  ma.array([-99, 2, 2, 3], mask=[1, 0, 0, 0]),
101
101
  ma.array([2, 4, 10, -99], mask=[1, 0, 0, 1]),
102
- ]
102
+ ],
103
103
  )
104
104
  compare_o = ma.array(
105
105
  [
106
106
  ma.array([-99, 2, 5, 4], mask=[1, 0, 0, 0]),
107
107
  ma.array([4, 6, 8, -99], mask=[0, 0, 0, 1]),
108
- ]
108
+ ],
109
109
  )
110
110
 
111
111
  testing.assert_array_almost_equal(x, compare_m)
112
112
  testing.assert_array_almost_equal(y, compare_o)
113
113
 
114
114
 
115
- def test_combine_masked_indices_mask():
115
+ def test_combine_masked_indices_mask() -> None:
116
116
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
117
117
  model.mask = ma.array([[1, 0, 0, 0], [0, 0, 0, 1]])
118
118
  observation = ma.array([[3, 2, 1, 4], [4, 6, 8, 4]])
@@ -122,19 +122,19 @@ def test_combine_masked_indices_mask():
122
122
  [
123
123
  ma.array([-99, -99, -99, 3], mask=[1, 1, 1, 0]),
124
124
  ma.array([-99, 4, 10, -99], mask=[1, 0, 0, 1]),
125
- ]
125
+ ],
126
126
  )
127
127
  observation = ma.array(
128
128
  [
129
129
  ma.array([-99, -99, -99, 4], mask=[1, 1, 1, 0]),
130
130
  ma.array([-99, 6, 8, -99], mask=[1, 0, 0, 1]),
131
- ]
131
+ ],
132
132
  )
133
133
  testing.assert_array_almost_equal(x, model)
134
134
  testing.assert_array_almost_equal(y, observation)
135
135
 
136
136
 
137
- def test_combine_masked_indices_nan():
137
+ def test_combine_masked_indices_nan() -> None:
138
138
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
139
139
  observation = ma.array([[np.nan, 2, 5, 4], [4, 6, np.nan, 4]])
140
140
  x, y = sts.combine_masked_indices(model, observation)
@@ -142,20 +142,20 @@ def test_combine_masked_indices_nan():
142
142
  [
143
143
  ma.array([-99, 2, 2, 3], mask=[1, 0, 0, 0]),
144
144
  ma.array([2, 4, -99, 1], mask=[0, 0, 1, 0]),
145
- ]
145
+ ],
146
146
  )
147
147
  testing.assert_array_almost_equal(x, model)
148
148
  testing.assert_array_almost_equal(y, observation)
149
149
 
150
150
 
151
- def test_calc_common_area_sum():
151
+ def test_calc_common_area_sum() -> None:
152
152
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
153
153
  observation = ma.array([[3, 2, 1, 4], [4, 6, 8, 4]])
154
154
  x, _ = sts.calc_common_area_sum(model, observation)
155
155
  testing.assert_almost_equal(x, 100)
156
156
 
157
157
 
158
- def test_calc_common_area_sum_min():
158
+ def test_calc_common_area_sum_min() -> None:
159
159
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
160
160
  model.mask = ma.array([[1, 0, 0, 0], [0, 0, 0, 1]])
161
161
  observation = ma.array([[3, 2, 1, 4], [4, 6, 8, 4]])
@@ -164,7 +164,7 @@ def test_calc_common_area_sum_min():
164
164
  testing.assert_almost_equal(x, 60.0)
165
165
 
166
166
 
167
- def test_calc_common_area_sum_nan():
167
+ def test_calc_common_area_sum_nan() -> None:
168
168
  model = ma.array([[1, 2, 2, 3], [2, np.nan, 10, 1]])
169
169
  model.mask = ma.array([[1, 0, 0, 0], [0, 0, 0, 1]])
170
170
  observation = ma.array([[3, 2, 1, 4], [4, 6, np.nan, 4]])
@@ -173,7 +173,7 @@ def test_calc_common_area_sum_nan():
173
173
  testing.assert_almost_equal(x, 25.0)
174
174
 
175
175
 
176
- def test_calc_common_area_sum_mask():
176
+ def test_calc_common_area_sum_mask() -> None:
177
177
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
178
178
  model.mask = ma.array([[1, 0, 0, 0], [0, 0, 0, 0]])
179
179
  observation = ma.array([[3, 2, 1, 4], [4, 6, 8, 4]])
@@ -182,7 +182,7 @@ def test_calc_common_area_sum_mask():
182
182
  testing.assert_almost_equal(x, 50.0)
183
183
 
184
184
 
185
- def test_histogram():
185
+ def test_histogram() -> None:
186
186
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
187
187
  observation = ma.array([[3, 2, 1, 4], [4, 6, 8, 4]])
188
188
  compare_x = np.array([1, 2, 2, 3, 2, 4, 8, 1])
@@ -192,7 +192,7 @@ def test_histogram():
192
192
  testing.assert_array_almost_equal(y, compare_y)
193
193
 
194
194
 
195
- def test_histogram_mask():
195
+ def test_histogram_mask() -> None:
196
196
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
197
197
  model.mask = ma.array([[1, 0, 0, 0], [0, 0, 0, 1]])
198
198
  observation = ma.array([[3, 2, 1, 4], [4, 6, 8, 4]])
@@ -204,7 +204,7 @@ def test_histogram_mask():
204
204
  testing.assert_array_almost_equal(y, compare_y)
205
205
 
206
206
 
207
- def test_histogram_nan():
207
+ def test_histogram_nan() -> None:
208
208
  model = ma.array([[1, 2, 2, 3], [2, 4, 10, 1]])
209
209
  observation = ma.array([[3, np.nan, 1, 4], [np.nan, 6, np.nan, 4]])
210
210
  compare_x = np.array([1, 2, 2, 3, 2, 4, 6, 1])
@@ -214,7 +214,7 @@ def test_histogram_nan():
214
214
  testing.assert_array_almost_equal(y, compare_y)
215
215
 
216
216
 
217
- def test_vertical_profile():
217
+ def test_vertical_profile() -> None:
218
218
  model = ma.array([[0, 2, 2, 3], [2, 4, 10, 1], [4, 6, 6, 8]])
219
219
  observation = ma.array([[3, 1, 1, 4], [4, 3, 8, 0], [5, 5, 3, 2]])
220
220
  x, y = sts.vertical_profile(model, observation)
@@ -224,7 +224,7 @@ def test_vertical_profile():
224
224
  testing.assert_array_almost_equal(y, observation)
225
225
 
226
226
 
227
- def test_vertical_profile_nan():
227
+ def test_vertical_profile_nan() -> None:
228
228
  model = ma.array([[0, 2, 2, 3], [2, 4, 10, 1], [4, 6, 6, 8]])
229
229
  observation = ma.array([[3, 1, 1, np.nan], [np.nan, 3, 8, 0], [5, 5, 3, 2]])
230
230
  x, y = sts.vertical_profile(model, observation)
@@ -234,7 +234,7 @@ def test_vertical_profile_nan():
234
234
  testing.assert_array_almost_equal(y, observation)
235
235
 
236
236
 
237
- def test_vertical_profile_mask():
237
+ def test_vertical_profile_mask() -> None:
238
238
  model = ma.array([[0, 2, 2, 3], [2, 4, 10, 1], [4, 6, 6, 8]])
239
239
  model.mask = ma.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])
240
240
  observation = ma.array([[3, 1, 1, 4], [4, 3, 8, 0], [5, 5, 3, 2]])
@@ -253,6 +253,6 @@ def test_vertical_profile_mask():
253
253
  ("vertical", ("ECMWF", "Cloud fraction")),
254
254
  ],
255
255
  )
256
- def test_day_stat_title(method, title):
256
+ def test_day_stat_title(method, title) -> None:
257
257
  x = sts.day_stat_title(method, PRODUCT_cf)
258
258
  assert x == title
@@ -1,4 +1,4 @@
1
- from datetime import datetime, timedelta
1
+ from datetime import datetime, timedelta, timezone
2
2
 
3
3
  import numpy as np
4
4
  import pytest
@@ -12,37 +12,38 @@ OUTPUT_FILE = "/"
12
12
  PRODUCT = "iwc"
13
13
 
14
14
 
15
- def test_model_file_list():
15
+ def test_model_file_list() -> None:
16
16
  name = "ec"
17
17
  models = ["00_ec_1", "00_ec_2", "00_ec_3"]
18
18
  tools.check_model_file_list(name, models)
19
19
 
20
20
 
21
- @pytest.mark.xfail(raises=AttributeError)
22
- def test_model_file_list_fail():
21
+ def test_model_file_list_fail() -> None:
23
22
  name = "ec"
24
23
  models = ["00_ec_1", "ac_1", "00_ec_2", "00_ec_3"]
25
- tools.check_model_file_list(name, models)
24
+ with pytest.raises(AttributeError):
25
+ tools.check_model_file_list(name, models)
26
26
 
27
27
 
28
- def test_time2datetime():
28
+ def test_time2datetime() -> None:
29
29
  time_list = np.array(range(10))
30
- d = datetime(2020, 4, 7, 0, 0, 0)
30
+ d = datetime(2020, 4, 7, 0, 0, 0, tzinfo=timezone.utc)
31
31
  x = tools.time2datetime(time_list, d)
32
32
  compare = [
33
- datetime(2020, 4, 7, 0, 0, 0) + timedelta(hours=1 * x) for x in range(0, 10)
33
+ datetime(2020, 4, 7, 0, 0, 0, tzinfo=timezone.utc) + timedelta(hours=1 * x)
34
+ for x in range(10)
34
35
  ]
35
36
  assert all(a == b for a, b in zip(x, compare))
36
37
 
37
38
 
38
- def test_rebin_edges():
39
+ def test_rebin_edges() -> None:
39
40
  data = np.array([1, 3, 6, 10, 15, 21, 28])
40
41
  compare = np.array([-1, 2, 4.5, 8, 12.5, 18, 24.5, 35])
41
42
  x = tools.rebin_edges(data)
42
43
  testing.assert_array_almost_equal(x, compare)
43
44
 
44
45
 
45
- def test_calculate_advection_time_hour(model_file):
46
+ def test_calculate_advection_time_hour(model_file) -> None:
46
47
  obj = ModelManager(str(model_file), MODEL, OUTPUT_FILE, PRODUCT)
47
48
  h = obj.resolution_h
48
49
  v = ma.array([[1, 2, 3], [2, 3, 4], [3, 4, 5]])
@@ -54,7 +55,7 @@ def test_calculate_advection_time_hour(model_file):
54
55
  assert x.all() == compare.all()
55
56
 
56
57
 
57
- def test_calculate_advection_time_10min(model_file):
58
+ def test_calculate_advection_time_10min(model_file) -> None:
58
59
  obj = ModelManager(str(model_file), MODEL, OUTPUT_FILE, PRODUCT)
59
60
  h = obj.resolution_h
60
61
  v = ma.array([[1, 2, 3], [2, 3, 4], [3, 4, 5]])
@@ -66,7 +67,7 @@ def test_calculate_advection_time_10min(model_file):
66
67
  assert x.all() == compare.all()
67
68
 
68
69
 
69
- def test_get_1d_indices():
70
+ def test_get_1d_indices() -> None:
70
71
  w = (1, 5)
71
72
  d = ma.array([0, 1, 2, 3, 4, 5, 6, 7])
72
73
  compare = ma.array([0, 1, 1, 1, 1, 0, 0, 0])
@@ -74,7 +75,7 @@ def test_get_1d_indices():
74
75
  testing.assert_array_almost_equal(x, compare)
75
76
 
76
77
 
77
- def test_get_1d_indices_mask():
78
+ def test_get_1d_indices_mask() -> None:
78
79
  w = (1, 5)
79
80
  d = ma.array([0, 1, 2, 3, 4, 5, 6, 7])
80
81
  m = np.array([0, 0, 1, 0, 0, 1, 0, 1], dtype=bool)
@@ -84,7 +85,7 @@ def test_get_1d_indices_mask():
84
85
  testing.assert_array_almost_equal(x, compare)
85
86
 
86
87
 
87
- def test_get_adv_indices():
88
+ def test_get_adv_indices() -> None:
88
89
  mt = 3
89
90
  at = 4
90
91
  d = ma.array([0, 1, 2, 3, 4, 5, 6, 7])
@@ -93,7 +94,7 @@ def test_get_adv_indices():
93
94
  testing.assert_array_almost_equal(x, compare)
94
95
 
95
96
 
96
- def test_get_adv_indices_mask():
97
+ def test_get_adv_indices_mask() -> None:
97
98
  mt = 3
98
99
  at = 4
99
100
  d = ma.array([0, 1, 2, 3, 4, 5, 6, 7])
@@ -104,7 +105,7 @@ def test_get_adv_indices_mask():
104
105
  testing.assert_array_almost_equal(x, compare)
105
106
 
106
107
 
107
- def test_obs_windows_size():
108
+ def test_obs_windows_size() -> None:
108
109
  i = np.array([0, 0, 1, 1, 1, 1, 0], dtype=bool)
109
110
  j = np.array([0, 1, 1, 1, 0, 0, 0], dtype=bool)
110
111
  x = tools.get_obs_window_size(i, j)
@@ -112,7 +113,7 @@ def test_obs_windows_size():
112
113
  testing.assert_almost_equal(x, (4, 3))
113
114
 
114
115
 
115
- def test_obs_windows_size_none():
116
+ def test_obs_windows_size_none() -> None:
116
117
  i = np.array([0, 0, 1, 1, 1, 1, 0], dtype=bool)
117
118
  j = np.array([0, 0, 0, 0, 0, 0, 0], dtype=bool)
118
119
  x = tools.get_obs_window_size(i, j)
@@ -1,5 +1,6 @@
1
1
  import os
2
+ from pathlib import Path
2
3
 
3
4
 
4
- def file_exists(file_path: str):
5
- return os.path.isfile(file_path) and os.path.getsize(file_path) > 0
5
+ def file_exists(file_path: str) -> bool:
6
+ return Path.is_file(Path(file_path)) and os.path.getsize(file_path) > 0
cloudnetpy/output.py CHANGED
@@ -1,4 +1,4 @@
1
- """ Functions for file writing."""
1
+ """Functions for file writing."""
2
2
  import datetime
3
3
  import logging
4
4
  from os import PathLike
@@ -14,7 +14,9 @@ from cloudnetpy.metadata import COMMON_ATTRIBUTES, MetaData
14
14
 
15
15
 
16
16
  def save_level1b(
17
- obj, output_file: PathLike | str, uuid: UUID | str | None = None
17
+ obj,
18
+ output_file: PathLike | str,
19
+ uuid: UUID | str | None = None,
18
20
  ) -> str:
19
21
  """Saves Cloudnet Level 1b file."""
20
22
  dimensions = _get_netcdf_dimensions(obj)
@@ -74,6 +76,7 @@ def save_product_file(
74
76
  """Saves a standard Cloudnet product file.
75
77
 
76
78
  Args:
79
+ ----
77
80
  short_id: Short file identifier, e.g. 'lwc', 'iwc', 'drizzle', 'classification'.
78
81
  obj: Instance containing product specific attributes: `time`, `dataset`, `data`.
79
82
  file_name: Name of the output file to be generated.
@@ -95,7 +98,8 @@ def save_product_file(
95
98
  "longitude",
96
99
  "time",
97
100
  "height",
98
- ) + copy_from_cat
101
+ *copy_from_cat,
102
+ )
99
103
  copy_variables(obj.dataset, nc, vars_from_source)
100
104
  nc.title = (
101
105
  f"{human_readable_file_type.capitalize()} products from"
@@ -139,6 +143,7 @@ def get_references(identifier: str | None = None, extra: list | None = None) ->
139
143
  """ "Returns references.
140
144
 
141
145
  Args:
146
+ ----
142
147
  identifier: Cloudnet file type, e.g., 'iwc'.
143
148
 
144
149
  """
@@ -170,9 +175,11 @@ def get_source_uuids(*sources) -> str:
170
175
  """Returns file_uuid attributes of objects.
171
176
 
172
177
  Args:
178
+ ----
173
179
  *sources: Objects whose file_uuid attributes are read (if exist).
174
180
 
175
181
  Returns:
182
+ -------
176
183
  str: UUIDs separated by comma.
177
184
 
178
185
  """
@@ -189,6 +196,7 @@ def merge_history(nc: netCDF4.Dataset, file_type: str, data: dict) -> None:
189
196
  """Merges history fields from one or several files and creates a new record.
190
197
 
191
198
  Args:
199
+ ----
192
200
  nc: The netCDF Dataset instance.
193
201
  file_type: Long description of the file.
194
202
  data: Dictionary of objects with history attribute.
@@ -198,7 +206,7 @@ def merge_history(nc: netCDF4.Dataset, file_type: str, data: dict) -> None:
198
206
  histories = []
199
207
  for key, obj in data.items():
200
208
  if (
201
- not isinstance(obj, (str, list))
209
+ not isinstance(obj, str | list)
202
210
  and obj is not None
203
211
  and hasattr(obj.dataset, "history")
204
212
  ):
@@ -227,6 +235,7 @@ def init_file(
227
235
  """Initializes a Cloudnet file for writing.
228
236
 
229
237
  Args:
238
+ ----
230
239
  file_name: File name to be generated.
231
240
  dimensions: Dictionary containing dimension for this file.
232
241
  cloudnet_arrays: Dictionary containing :class:`CloudnetArray` instances.
@@ -242,11 +251,14 @@ def init_file(
242
251
 
243
252
 
244
253
  def copy_variables(
245
- source: netCDF4.Dataset, target: netCDF4.Dataset, keys: tuple
254
+ source: netCDF4.Dataset,
255
+ target: netCDF4.Dataset,
256
+ keys: tuple,
246
257
  ) -> None:
247
258
  """Copies variables (and their attributes) from one file to another.
248
259
 
249
260
  Args:
261
+ ----
250
262
  source: Source object.
251
263
  target: Target object.
252
264
  keys: Variable names to be copied.
@@ -267,17 +279,20 @@ def copy_variables(
267
279
  k: variable.getncattr(k)
268
280
  for k in variable.ncattrs()
269
281
  if k != "_FillValue"
270
- }
282
+ },
271
283
  )
272
284
  var_out[:] = variable[:]
273
285
 
274
286
 
275
287
  def copy_global(
276
- source: netCDF4.Dataset, target: netCDF4.Dataset, attributes: tuple
288
+ source: netCDF4.Dataset,
289
+ target: netCDF4.Dataset,
290
+ attributes: tuple,
277
291
  ) -> None:
278
292
  """Copies global attributes from one file to another.
279
293
 
280
294
  Args:
295
+ ----
281
296
  source: Source object.
282
297
  target: Target object.
283
298
  attributes: List of attributes to be copied.
@@ -290,7 +305,9 @@ def copy_global(
290
305
 
291
306
 
292
307
  def add_time_attribute(
293
- attributes: dict, date: list[str] | datetime.date, key: str = "time"
308
+ attributes: dict,
309
+ date: list[str] | datetime.date,
310
+ key: str = "time",
294
311
  ) -> dict:
295
312
  """Adds time attribute with correct units."""
296
313
  if isinstance(date, list):
@@ -307,7 +324,7 @@ def add_time_attribute(
307
324
  return attributes
308
325
 
309
326
 
310
- def add_source_attribute(attributes: dict, data: dict):
327
+ def add_source_attribute(attributes: dict, data: dict) -> dict:
311
328
  """Adds source attribute."""
312
329
  variables = {
313
330
  "radar": (
@@ -343,6 +360,7 @@ def update_attributes(cloudnet_variables: dict, attributes: dict) -> None:
343
360
  New attributes are added.
344
361
 
345
362
  Args:
363
+ ----
346
364
  cloudnet_variables: CloudnetArray instances.
347
365
  attributes: Product-specific attributes.
348
366
 
@@ -365,7 +383,11 @@ def _write_vars2nc(nc: netCDF4.Dataset, cloudnet_variables: dict) -> None:
365
383
  size = obj.dimensions or _get_dimensions(nc, obj.data)
366
384
 
367
385
  nc_variable = nc.createVariable(
368
- obj.name, obj.data_type, size, zlib=True, fill_value=fill_value
386
+ obj.name,
387
+ obj.data_type,
388
+ size,
389
+ zlib=True,
390
+ fill_value=fill_value,
369
391
  )
370
392
  nc_variable[:] = obj.data
371
393
  for attr in obj.fetch_attributes():
@@ -380,15 +402,16 @@ def _get_dimensions(nc: netCDF4.Dataset, data: np.ndarray) -> tuple:
380
402
  file_dims = nc.dimensions
381
403
  array_dims = data.shape
382
404
  for length in array_dims:
383
- dim = [key for key in file_dims.keys() if file_dims[key].size == length][0]
384
- variable_size = variable_size + [dim]
405
+ dim = [key for key in file_dims if file_dims[key].size == length][0] # noqa: RUF015
406
+ variable_size = [*variable_size, dim]
385
407
  return tuple(variable_size)
386
408
 
387
409
 
388
410
  def _get_identifier(short_id: str) -> str:
389
411
  valid_ids = ("lwc", "iwc", "drizzle", "classification", "der", "ier")
390
412
  if short_id not in valid_ids:
391
- raise ValueError("Invalid product id.")
413
+ msg = f"Invalid file identifier: {short_id}"
414
+ raise ValueError(msg)
392
415
  if short_id == "iwc":
393
416
  return "ice water content"
394
417
  if short_id == "lwc":
@@ -401,7 +424,8 @@ def _get_identifier(short_id: str) -> str:
401
424
 
402
425
 
403
426
  def add_standard_global_attributes(
404
- nc: netCDF4.Dataset, uuid: UUID | str | None = None
427
+ nc: netCDF4.Dataset,
428
+ uuid: UUID | str | None = None,
405
429
  ) -> None:
406
430
  nc.Conventions = "CF-1.8"
407
431
  nc.cloudnetpy_version = version.__version__
@@ -425,14 +449,16 @@ def fix_time_attributes(nc: netCDF4.Dataset) -> None:
425
449
  nc.variables["time"].standard_name = "time"
426
450
  nc.variables["time"].long_name = "Time UTC"
427
451
  nc.variables["time"].calendar = "standard"
428
- nc.variables["time"].units = (
429
- f"hours since " f"{nc.year}-{nc.month}-{nc.day} " f"00:00:00 +00:00"
430
- )
452
+ nc.variables[
453
+ "time"
454
+ ].units = f"hours since {nc.year}-{nc.month}-{nc.day} 00:00:00 +00:00"
431
455
 
432
456
 
433
457
  def replace_attribute_with_standard_value(
434
- nc: netCDF4.Dataset, variables: tuple, attributes: tuple
435
- ):
458
+ nc: netCDF4.Dataset,
459
+ variables: tuple,
460
+ attributes: tuple,
461
+ ) -> None:
436
462
  for key in variables:
437
463
  if key in COMMON_ATTRIBUTES and key in nc.variables:
438
464
  for attr in attributes:
@@ -1,6 +1,7 @@
1
1
  """Metadata for plotting module."""
2
+ from collections.abc import Sequence
2
3
  from enum import Enum
3
- from typing import NamedTuple, Sequence
4
+ from typing import NamedTuple
4
5
 
5
6
 
6
7
  class Scale(Enum):
@@ -778,25 +779,39 @@ ATTRIBUTES = {
778
779
  source="disdrometer", # Also in weather-station and rain-radar
779
780
  ),
780
781
  "n_particles": PlotMeta(
781
- name="Number of particles", plot_type="bar", source="disdrometer"
782
+ name="Number of particles",
783
+ plot_type="bar",
784
+ source="disdrometer",
782
785
  ),
783
786
  "air_temperature": PlotMeta(
784
- name="Air temperature", plot_type="bar", source="weather-station"
787
+ name="Air temperature",
788
+ plot_type="bar",
789
+ source="weather-station",
785
790
  ),
786
791
  "wind_speed": PlotMeta(
787
- name="Wind speed", plot_type="bar", source="weather-station"
792
+ name="Wind speed",
793
+ plot_type="bar",
794
+ source="weather-station",
788
795
  ),
789
796
  "wind_direction": PlotMeta(
790
- name="Wind direction", plot_type="bar", source="weather-station"
797
+ name="Wind direction",
798
+ plot_type="bar",
799
+ source="weather-station",
791
800
  ),
792
801
  "relative_humidity": PlotMeta(
793
- name="Relative humidity", plot_type="bar", source="weather-station"
802
+ name="Relative humidity",
803
+ plot_type="bar",
804
+ source="weather-station",
794
805
  ),
795
806
  "air_pressure": PlotMeta(
796
- name="Air pressure", plot_type="bar", source="weather-station"
807
+ name="Air pressure",
808
+ plot_type="bar",
809
+ source="weather-station",
797
810
  ),
798
811
  "rainfall_amount": PlotMeta(
799
- name="Rainfall amount", plot_type="bar", source="weather-station"
812
+ name="Rainfall amount",
813
+ plot_type="bar",
814
+ source="weather-station",
800
815
  ),
801
816
  "target_classification": PlotMeta(
802
817
  name="Target classification",
@@ -871,7 +886,10 @@ ATTRIBUTES = {
871
886
  plot_type="bit",
872
887
  ),
873
888
  "cold": PlotMeta(
874
- name="Cold bit", cbar=_CBAR["bit"], plot_range=(0, 1), plot_type="bit"
889
+ name="Cold bit",
890
+ cbar=_CBAR["bit"],
891
+ plot_range=(0, 1),
892
+ plot_type="bit",
875
893
  ),
876
894
  "melting": PlotMeta(
877
895
  name="Melting bit",
@@ -892,10 +910,16 @@ ATTRIBUTES = {
892
910
  plot_type="bit",
893
911
  ),
894
912
  "radar": PlotMeta(
895
- name="Radar bit", cbar=_CBAR["bit"], plot_range=(0, 1), plot_type="bit"
913
+ name="Radar bit",
914
+ cbar=_CBAR["bit"],
915
+ plot_range=(0, 1),
916
+ plot_type="bit",
896
917
  ),
897
918
  "lidar": PlotMeta(
898
- name="Lidar bit", cbar=_CBAR["bit"], plot_range=(0, 1), plot_type="bit"
919
+ name="Lidar bit",
920
+ cbar=_CBAR["bit"],
921
+ plot_range=(0, 1),
922
+ plot_type="bit",
899
923
  ),
900
924
  "clutter": PlotMeta(
901
925
  name="Clutter bit",