cloudnetpy 1.66.14__py3-none-any.whl → 1.66.16__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.
@@ -167,10 +167,11 @@ def generate_categorize(
167
167
  logging.warning("Unable to use disdrometer: %s", err)
168
168
  time, height = _define_dense_grid()
169
169
  valid_ind = _interpolate_to_cloudnet_grid()
170
- if not valid_ind:
171
- msg = "No overlapping radar and lidar timestamps found"
170
+ if len(valid_ind) < 2:
171
+ msg = "Less than 2 overlapping radar, lidar and model timestamps found"
172
172
  raise ValidTimeStampError(msg)
173
173
  _screen_bad_time_indices(valid_ind)
174
+
174
175
  if any(source in data.radar.source_type.lower() for source in ("rpg", "basta")):
175
176
  data.radar.filter_speckle_noise()
176
177
  data.radar.filter_1st_gate_artifact()
cloudnetpy/concat_lib.py CHANGED
@@ -192,6 +192,8 @@ class _Concat:
192
192
  ind0 = len(self.concatenated_file.variables[self.concat_dimension])
193
193
  ind1 = ind0 + len(file.variables[self.concat_dimension])
194
194
  for key in self.concatenated_file.variables:
195
+ if key not in file.variables:
196
+ continue
195
197
  array = file[key][:]
196
198
  if key in self.common_variables:
197
199
  if allow_vary is not None and key in allow_vary:
@@ -103,18 +103,26 @@ class NcRadar(DataSource, CloudnetInstrument):
103
103
  azimuth_reference = ma.median(azimuth)
104
104
  azimuth_tolerance = 0.1
105
105
 
106
- elevation = self.data["elevation"].data
107
- zenith = 90 - elevation
108
- is_stable_zenith = np.isclose(zenith, ma.median(zenith), atol=0.1)
106
+ zenith = 90 - self.data["elevation"].data
107
+
108
+ is_valid_zenith = np.abs(zenith) < 10
109
+ if not np.any(is_valid_zenith):
110
+ msg = "No valid zenith angles"
111
+ raise ValidTimeStampError(msg)
112
+
113
+ valid_zenith = zenith[is_valid_zenith]
114
+ is_stable_zenith = np.isclose(zenith, ma.median(valid_zenith), atol=0.1)
109
115
  is_stable_azimuth = np.isclose(
110
116
  azimuth,
111
117
  azimuth_reference,
112
118
  atol=azimuth_tolerance,
113
119
  )
114
120
  is_stable_profile = is_stable_zenith & is_stable_azimuth
121
+
115
122
  if ma.isMaskedArray(is_stable_profile):
116
123
  is_stable_profile[is_stable_profile.mask] = False
117
124
  n_removed = np.count_nonzero(~is_stable_profile)
125
+
118
126
  if n_removed >= len(zenith) - 1:
119
127
  msg = "Less than two profiles with valid zenith / azimuth angles"
120
128
  raise ValidTimeStampError(msg)
@@ -6,7 +6,7 @@ import logging
6
6
  import netCDF4
7
7
  import numpy as np
8
8
  from numpy import ma
9
- from numpy.testing import assert_array_equal
9
+ from numpy.testing import assert_almost_equal
10
10
 
11
11
  from cloudnetpy import output, utils
12
12
  from cloudnetpy.exceptions import InconsistentDataError, ValidTimeStampError
@@ -190,7 +190,9 @@ def _read_array_from_multiple_files(files1: list, files2: list, key) -> np.ndarr
190
190
  array1 = _read_array_from_file_pair(nc1, nc2, key)
191
191
  if ind == 0:
192
192
  array = array1
193
- assert_array_equal(array, array1, f"Inconsistent variable '{key}'")
193
+ assert_almost_equal(
194
+ array, array1, err_msg=f"Inconsistent variable '{key}'", decimal=2
195
+ )
194
196
  return np.array(array)
195
197
 
196
198
 
@@ -201,7 +203,9 @@ def _read_array_from_file_pair(
201
203
  ) -> np.ndarray:
202
204
  array1 = nc_file1.variables[key][:]
203
205
  array2 = nc_file2.variables[key][:]
204
- assert_array_equal(array1, array2, f"Inconsistent variable '{key}'")
206
+ assert_almost_equal(
207
+ array1, array2, err_msg=f"Inconsistent variable '{key}'", decimal=2
208
+ )
205
209
  return array1
206
210
 
207
211
 
@@ -61,6 +61,7 @@ def radiometrics2nc(
61
61
 
62
62
  radiometrics = RadiometricsCombined(objs, site_meta)
63
63
  radiometrics.screen_time(date)
64
+ radiometrics.sort_timestamps()
64
65
  radiometrics.time_to_fractional_hours()
65
66
  radiometrics.data_to_cloudnet_arrays()
66
67
  radiometrics.add_meta()
@@ -143,9 +144,13 @@ class Radiometrics:
143
144
  lwps = []
144
145
  iwvs = []
145
146
  irts = []
147
+ irt_times = []
146
148
  temps = []
149
+ temp_times = []
147
150
  rhs = []
151
+ rh_times = []
148
152
  ahs = []
153
+ ah_times = []
149
154
  block_titles = {}
150
155
  for record in self.raw_data:
151
156
  if record.block_type == 100:
@@ -154,12 +159,15 @@ class Radiometrics:
154
159
  block_titles[block_type] = title
155
160
  if title := block_titles.get(record.block_type + record.block_index):
156
161
  if title == "Temperature (K)":
162
+ temp_times.append(record.timestamp)
157
163
  temps.append(
158
164
  [float(record.values[column]) for column in self.ranges]
159
165
  )
160
166
  elif title == "Relative Humidity (%)":
167
+ rh_times.append(record.timestamp)
161
168
  rhs.append([float(record.values[column]) for column in self.ranges])
162
169
  elif title == "Vapor Density (g/m^3)":
170
+ ah_times.append(record.timestamp)
163
171
  ahs.append([float(record.values[column]) for column in self.ranges])
164
172
  elif record.block_type == 10:
165
173
  if record.block_index == 0:
@@ -169,15 +177,20 @@ class Radiometrics:
169
177
  times.append(record.timestamp)
170
178
  lwps.append(float(lwp))
171
179
  iwvs.append(float(iwv))
180
+ irt_times.append(record.timestamp)
172
181
  irts.append([float(irt)])
182
+ temp_times.append(record.timestamp)
173
183
  temps.append(
174
184
  [float(record.values[column]) for column in self.ranges]
175
185
  )
176
186
  elif record.block_index == 1:
187
+ ah_times.append(record.timestamp)
177
188
  ahs.append([float(record.values[column]) for column in self.ranges])
178
189
  elif record.block_index == 2:
190
+ rh_times.append(record.timestamp)
179
191
  rhs.append([float(record.values[column]) for column in self.ranges])
180
192
  elif record.block_type == 200:
193
+ irt_times.append(record.timestamp)
181
194
  irt = record.values["Tir(K)"]
182
195
  irts.append([float(irt)])
183
196
  elif record.block_type == 300:
@@ -186,16 +199,29 @@ class Radiometrics:
186
199
  times.append(record.timestamp)
187
200
  lwps.append(float(lwp))
188
201
  iwvs.append(float(iwv))
189
- n_time = len(times)
190
202
  self.data["time"] = np.array(times, dtype="datetime64[s]")
191
203
  self.data["lwp"] = np.array(lwps) # mm => kg m-2
192
204
  self.data["iwv"] = np.array(iwvs) * 10 # cm => kg m-2
193
- self.data["irt"] = np.array(irts[:n_time])
194
- self.data["temperature"] = np.array(temps[:n_time])
195
- self.data["relative_humidity"] = np.array(rhs[:n_time]) / 100 # % => 1
196
- self.data["absolute_humidity"] = (
197
- np.array(ahs[:n_time]) / 1000
198
- ) # g m-3 => kg m-3
205
+ self.data["irt"] = _find_closest(
206
+ np.array(irt_times, dtype="datetime64[s]"),
207
+ np.array(irts),
208
+ self.data["time"],
209
+ )
210
+ self.data["temperature"] = _find_closest(
211
+ np.array(temp_times, dtype="datetime64[s]"),
212
+ np.array(temps),
213
+ self.data["time"],
214
+ )
215
+ self.data["relative_humidity"] = _find_closest(
216
+ np.array(rh_times, dtype="datetime64[s]"),
217
+ np.array(rhs) / 100, # % => 1
218
+ self.data["time"],
219
+ )
220
+ self.data["absolute_humidity"] = _find_closest(
221
+ np.array(ah_times, dtype="datetime64[s]"),
222
+ np.array(ahs) / 1000, # g m-3 => kg m-3
223
+ self.data["time"],
224
+ )
199
225
 
200
226
 
201
227
  class RadiometricsCombined:
@@ -233,6 +259,13 @@ class RadiometricsCombined:
233
259
  continue
234
260
  self.data[key] = self.data[key][valid_mask]
235
261
 
262
+ def sort_timestamps(self):
263
+ ind = np.argsort(self.data["time"])
264
+ for key in self.data:
265
+ if key in ("range", "height"):
266
+ continue
267
+ self.data[key] = self.data[key][ind]
268
+
236
269
  def time_to_fractional_hours(self) -> None:
237
270
  base = self.data["time"][0].astype("datetime64[D]")
238
271
  self.data["time"] = (self.data["time"] - base) / np.timedelta64(1, "h")
@@ -272,6 +305,10 @@ def _parse_datetime(text: str) -> datetime.datetime:
272
305
  )
273
306
 
274
307
 
308
+ def _find_closest(x: np.ndarray, y: np.ndarray, x_new: np.ndarray) -> np.ndarray:
309
+ return y[np.argmin(np.abs(x_new - x[:, np.newaxis]), axis=0)]
310
+
311
+
275
312
  ATTRIBUTES = {
276
313
  "irt": MetaData(
277
314
  long_name="Infrared brightness temperatures",
cloudnetpy/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
2
  MINOR = 66
3
- PATCH = 14
3
+ PATCH = 16
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudnetpy
3
- Version: 1.66.14
3
+ Version: 1.66.16
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -1,7 +1,7 @@
1
1
  cloudnetpy/__init__.py,sha256=X_FqY-4yg5GUj5Edo14SToLEos6JIsC3fN-v1FUgQoA,43
2
2
  cloudnetpy/cli.py,sha256=lHkeAErmAijI-Ugpd4DHRHfbZP4SXOake0LIY5Ovv_Q,20782
3
3
  cloudnetpy/cloudnetarray.py,sha256=Ol1ha4RPAmFZANL__U5CaMKX4oYMXYR6OnjoCZ9w3eo,7077
4
- cloudnetpy/concat_lib.py,sha256=RiL6fgaKjX2YyXl0BonbCjLXV2voIKPcQcdR9ZkQ8QA,11888
4
+ cloudnetpy/concat_lib.py,sha256=lLjEC0ILPI1ghv8Wu9WVDiQoAkwwBKlW5jV8KoIde_8,11963
5
5
  cloudnetpy/constants.py,sha256=RDB9aqpBRztk3QVCFgsmi9fwhtLuit_0WJrt0D6sDcc,736
6
6
  cloudnetpy/datasource.py,sha256=FcWS77jz56gIzwnbafDLdj-HjAyu0P_VtY7gkeVZThU,7952
7
7
  cloudnetpy/exceptions.py,sha256=ns48useL9RN3mPh7CqIiLA19VI9OmVbyRsKTkwbThF8,1760
@@ -9,11 +9,11 @@ cloudnetpy/metadata.py,sha256=DOGt7EQLS-AVJEszrUrpXr3gHVQv655FzeCzKrOPvoU,5477
9
9
  cloudnetpy/output.py,sha256=lq4YSeMT_d-j4rlQkKm9KIZ8boupTBBBKV1eUawpmCI,15672
10
10
  cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  cloudnetpy/utils.py,sha256=JksYOwf9ORiR_QpoKrTe1JJwXpPYJj-wlwaZKCHoh3o,29744
12
- cloudnetpy/version.py,sha256=2QGNMZni3ixostxBEdUnnkW51Mi01KeZjZ44M5gaAgU,73
12
+ cloudnetpy/version.py,sha256=eHl2AuLZQX2QkNhffKVbnwbtG_WTP5AnXh-UB2mXFyM,73
13
13
  cloudnetpy/categorize/__init__.py,sha256=s-SJaysvVpVVo5kidiruWQO6p3gv2TXwY1wEHYO5D6I,44
14
14
  cloudnetpy/categorize/atmos_utils.py,sha256=9-ymI6i1xASf-XAFyO87FaTfvq6bF89N1i_27OkUp-M,10104
15
15
  cloudnetpy/categorize/attenuation.py,sha256=Y_-fzmQTltWTqIZTulJhovC7a6ifpMcaAazDJcnMIOc,990
16
- cloudnetpy/categorize/categorize.py,sha256=I5Bmj5xnolO9CQkkh9CEC5oMyQMGUW6IaRMsljoKsFc,20717
16
+ cloudnetpy/categorize/categorize.py,sha256=HtttmUlnLE4Cw3QLdz-DRfRpd8sVr_Tp4A62XI67x24,20739
17
17
  cloudnetpy/categorize/classify.py,sha256=qovHgHsMku5kpl3cJxKteNBsG8GAkfI3Zo8QhJwZSFQ,8512
18
18
  cloudnetpy/categorize/containers.py,sha256=evqVQOoUycBtaFeKpms7_NuM9z8jKWq37nLIhoDxa8k,5278
19
19
  cloudnetpy/categorize/disdrometer.py,sha256=keU3pFvKtk840A0oLwAyNDuqOCswBPJEKf2bV0YWyA8,2004
@@ -47,9 +47,9 @@ cloudnetpy/instruments/lufft.py,sha256=ugXF6pssHAAz1Y_hqPdpKuluAjxxHSR88xBmQuS6R
47
47
  cloudnetpy/instruments/mira.py,sha256=f679zjmIxLVVtUVSMeO5IWbEdaj6qOJu5Gf9MKQJSL8,9412
48
48
  cloudnetpy/instruments/mrr.py,sha256=eeAzCp3CiHGauywjwvMUAFwZ4vBOZMcd3IlF8KsrLQo,5711
49
49
  cloudnetpy/instruments/nc_lidar.py,sha256=5gQG9PApnNPrHmS9_zanl8HEYIQuGRpbnzC3wfTcOyQ,1705
50
- cloudnetpy/instruments/nc_radar.py,sha256=ctmb0tJHkRoz-Ic8UAw4_v4VfS27r22_4X_1s4mUAas,6990
51
- cloudnetpy/instruments/pollyxt.py,sha256=YuVEHr-BX31rtVOFsWGU-SQFAmcxpXL26eyCVMz_9hw,8933
52
- cloudnetpy/instruments/radiometrics.py,sha256=troxDHj7DDx04dIj0Qe8pRDWGz7W_qxmq7H620_jE-s,10481
50
+ cloudnetpy/instruments/nc_radar.py,sha256=dbhfI0uLNjDIQqg6RRCTGuInLjv_OC-43wvVV28gJYE,7189
51
+ cloudnetpy/instruments/pollyxt.py,sha256=lkiBu8ChvLd86eCkeIGxHKwraeDQFuVSgCVlsAYTYN8,9010
52
+ cloudnetpy/instruments/radiometrics.py,sha256=ySG4a042XkgjMTG8d20oAPNvFvw9bMwwiqS3zv-JF_w,11825
53
53
  cloudnetpy/instruments/rpg.py,sha256=IozvBJ8_qXTPqtp58FQwRsoI5_aI3-kycpXugZkS0d4,17462
54
54
  cloudnetpy/instruments/rpg_reader.py,sha256=ThztFuVrWxhmWVAfZTfQDeUiKK1XMTbtv08IBe8GK98,11364
55
55
  cloudnetpy/instruments/toa5.py,sha256=CfmmBMv5iMGaWHIGBK01Rw24cuXC1R1RMNTXkmsm340,1760
@@ -115,9 +115,9 @@ cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe5
115
115
  cloudnetpy/products/mwr_tools.py,sha256=rd7UC67O4fsIE5SaHVZ4qWvUJTj41ZGwgQWPwZzOM14,5377
116
116
  cloudnetpy/products/product_tools.py,sha256=01Zc6xV8CSuYcIcLpchFf5POL3_c629-YMNDZJ51udA,10853
117
117
  docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
118
- cloudnetpy-1.66.14.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
119
- cloudnetpy-1.66.14.dist-info/METADATA,sha256=EBvDEO2ZIXKdZkYX856_rst7Cy6S4QRThZSh9F75aMI,5806
120
- cloudnetpy-1.66.14.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
121
- cloudnetpy-1.66.14.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
122
- cloudnetpy-1.66.14.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
123
- cloudnetpy-1.66.14.dist-info/RECORD,,
118
+ cloudnetpy-1.66.16.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
119
+ cloudnetpy-1.66.16.dist-info/METADATA,sha256=-1JjOwxS5yjpcgomZnjK0GP5D8YWeD2HwgKtwwA7l2o,5806
120
+ cloudnetpy-1.66.16.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
121
+ cloudnetpy-1.66.16.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
122
+ cloudnetpy-1.66.16.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
123
+ cloudnetpy-1.66.16.dist-info/RECORD,,