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.
- cloudnetpy/categorize/atmos.py +46 -14
- cloudnetpy/categorize/atmos_utils.py +11 -1
- cloudnetpy/categorize/categorize.py +38 -21
- cloudnetpy/categorize/classify.py +31 -9
- cloudnetpy/categorize/containers.py +19 -7
- cloudnetpy/categorize/droplet.py +24 -8
- cloudnetpy/categorize/falling.py +17 -7
- cloudnetpy/categorize/freezing.py +19 -5
- cloudnetpy/categorize/insects.py +27 -14
- cloudnetpy/categorize/lidar.py +38 -36
- cloudnetpy/categorize/melting.py +19 -9
- cloudnetpy/categorize/model.py +28 -9
- cloudnetpy/categorize/mwr.py +4 -2
- cloudnetpy/categorize/radar.py +58 -22
- cloudnetpy/cloudnetarray.py +15 -6
- cloudnetpy/concat_lib.py +39 -16
- cloudnetpy/constants.py +7 -0
- cloudnetpy/datasource.py +39 -19
- cloudnetpy/instruments/basta.py +6 -2
- cloudnetpy/instruments/campbell_scientific.py +33 -16
- cloudnetpy/instruments/ceilo.py +30 -13
- cloudnetpy/instruments/ceilometer.py +76 -37
- cloudnetpy/instruments/cl61d.py +8 -3
- cloudnetpy/instruments/cloudnet_instrument.py +2 -1
- cloudnetpy/instruments/copernicus.py +27 -14
- cloudnetpy/instruments/disdrometer/common.py +51 -32
- cloudnetpy/instruments/disdrometer/parsivel.py +79 -48
- cloudnetpy/instruments/disdrometer/thies.py +10 -6
- cloudnetpy/instruments/galileo.py +23 -12
- cloudnetpy/instruments/hatpro.py +27 -11
- cloudnetpy/instruments/instruments.py +4 -1
- cloudnetpy/instruments/lufft.py +20 -11
- cloudnetpy/instruments/mira.py +60 -49
- cloudnetpy/instruments/mrr.py +31 -20
- cloudnetpy/instruments/nc_lidar.py +15 -6
- cloudnetpy/instruments/nc_radar.py +31 -22
- cloudnetpy/instruments/pollyxt.py +36 -21
- cloudnetpy/instruments/radiometrics.py +32 -18
- cloudnetpy/instruments/rpg.py +48 -22
- cloudnetpy/instruments/rpg_reader.py +39 -30
- cloudnetpy/instruments/vaisala.py +39 -27
- cloudnetpy/instruments/weather_station.py +15 -11
- cloudnetpy/metadata.py +3 -1
- cloudnetpy/model_evaluation/file_handler.py +31 -21
- cloudnetpy/model_evaluation/metadata.py +3 -1
- cloudnetpy/model_evaluation/model_metadata.py +1 -1
- cloudnetpy/model_evaluation/plotting/plot_tools.py +20 -15
- cloudnetpy/model_evaluation/plotting/plotting.py +114 -64
- cloudnetpy/model_evaluation/products/advance_methods.py +48 -28
- cloudnetpy/model_evaluation/products/grid_methods.py +44 -19
- cloudnetpy/model_evaluation/products/model_products.py +22 -18
- cloudnetpy/model_evaluation/products/observation_products.py +15 -9
- cloudnetpy/model_evaluation/products/product_resampling.py +14 -4
- cloudnetpy/model_evaluation/products/tools.py +16 -7
- cloudnetpy/model_evaluation/statistics/statistical_methods.py +28 -15
- cloudnetpy/model_evaluation/tests/e2e/conftest.py +3 -3
- cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +9 -5
- cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +14 -13
- cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +9 -5
- cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +14 -13
- cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +9 -5
- cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +14 -13
- cloudnetpy/model_evaluation/tests/unit/conftest.py +11 -11
- cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +33 -27
- cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +83 -83
- cloudnetpy/model_evaluation/tests/unit/test_model_products.py +23 -21
- cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +24 -25
- cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +40 -39
- cloudnetpy/model_evaluation/tests/unit/test_plotting.py +12 -11
- cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +30 -30
- cloudnetpy/model_evaluation/tests/unit/test_tools.py +18 -17
- cloudnetpy/model_evaluation/utils.py +3 -2
- cloudnetpy/output.py +45 -19
- cloudnetpy/plotting/plot_meta.py +35 -11
- cloudnetpy/plotting/plotting.py +172 -104
- cloudnetpy/products/classification.py +20 -8
- cloudnetpy/products/der.py +25 -10
- cloudnetpy/products/drizzle.py +41 -26
- cloudnetpy/products/drizzle_error.py +10 -5
- cloudnetpy/products/drizzle_tools.py +43 -24
- cloudnetpy/products/ier.py +10 -5
- cloudnetpy/products/iwc.py +16 -9
- cloudnetpy/products/lwc.py +34 -12
- cloudnetpy/products/mwr_multi.py +4 -1
- cloudnetpy/products/mwr_single.py +4 -1
- cloudnetpy/products/product_tools.py +33 -10
- cloudnetpy/utils.py +175 -74
- cloudnetpy/version.py +1 -1
- {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/METADATA +11 -10
- cloudnetpy-1.55.22.dist-info/RECORD +114 -0
- docs/source/conf.py +2 -2
- cloudnetpy-1.55.20.dist-info/RECORD +0 -114
- {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/LICENSE +0 -0
- {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/WHEEL +0 -0
- {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
|
-
|
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
|
-
|
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)
|
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)
|
cloudnetpy/output.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"""
|
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,
|
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
|
-
|
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,
|
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,
|
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,
|
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,
|
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,
|
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
|
384
|
-
variable_size = variable_size
|
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
|
-
|
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,
|
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[
|
429
|
-
|
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,
|
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:
|
cloudnetpy/plotting/plot_meta.py
CHANGED
@@ -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
|
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",
|
782
|
+
name="Number of particles",
|
783
|
+
plot_type="bar",
|
784
|
+
source="disdrometer",
|
782
785
|
),
|
783
786
|
"air_temperature": PlotMeta(
|
784
|
-
name="Air temperature",
|
787
|
+
name="Air temperature",
|
788
|
+
plot_type="bar",
|
789
|
+
source="weather-station",
|
785
790
|
),
|
786
791
|
"wind_speed": PlotMeta(
|
787
|
-
name="Wind speed",
|
792
|
+
name="Wind speed",
|
793
|
+
plot_type="bar",
|
794
|
+
source="weather-station",
|
788
795
|
),
|
789
796
|
"wind_direction": PlotMeta(
|
790
|
-
name="Wind direction",
|
797
|
+
name="Wind direction",
|
798
|
+
plot_type="bar",
|
799
|
+
source="weather-station",
|
791
800
|
),
|
792
801
|
"relative_humidity": PlotMeta(
|
793
|
-
name="Relative humidity",
|
802
|
+
name="Relative humidity",
|
803
|
+
plot_type="bar",
|
804
|
+
source="weather-station",
|
794
805
|
),
|
795
806
|
"air_pressure": PlotMeta(
|
796
|
-
name="Air pressure",
|
807
|
+
name="Air pressure",
|
808
|
+
plot_type="bar",
|
809
|
+
source="weather-station",
|
797
810
|
),
|
798
811
|
"rainfall_amount": PlotMeta(
|
799
|
-
name="Rainfall amount",
|
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",
|
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",
|
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",
|
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",
|