cloudnetpy 1.80.1__tar.gz → 1.80.3__tar.gz

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 (132) hide show
  1. {cloudnetpy-1.80.1/cloudnetpy.egg-info → cloudnetpy-1.80.3}/PKG-INFO +1 -1
  2. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/categorize.py +5 -4
  3. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/radiometrics.py +24 -13
  4. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/weather_station.py +70 -18
  5. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/version.py +1 -1
  6. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3/cloudnetpy.egg-info}/PKG-INFO +1 -1
  7. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/LICENSE +0 -0
  8. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/MANIFEST.in +0 -0
  9. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/README.md +0 -0
  10. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/__init__.py +0 -0
  11. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/__init__.py +0 -0
  12. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/atmos_utils.py +0 -0
  13. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/attenuation.py +0 -0
  14. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/attenuations/__init__.py +0 -0
  15. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/attenuations/gas_attenuation.py +0 -0
  16. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/attenuations/liquid_attenuation.py +0 -0
  17. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/attenuations/melting_attenuation.py +0 -0
  18. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/attenuations/rain_attenuation.py +0 -0
  19. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/classify.py +0 -0
  20. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/containers.py +0 -0
  21. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/disdrometer.py +0 -0
  22. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/droplet.py +0 -0
  23. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/falling.py +0 -0
  24. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/freezing.py +0 -0
  25. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/insects.py +0 -0
  26. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/itu.py +0 -0
  27. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/lidar.py +0 -0
  28. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/melting.py +0 -0
  29. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/model.py +0 -0
  30. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/mwr.py +0 -0
  31. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/categorize/radar.py +0 -0
  32. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/cli.py +0 -0
  33. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/cloudnetarray.py +0 -0
  34. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/concat_lib.py +0 -0
  35. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/constants.py +0 -0
  36. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/datasource.py +0 -0
  37. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/exceptions.py +0 -0
  38. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/__init__.py +0 -0
  39. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/basta.py +0 -0
  40. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/bowtie.py +0 -0
  41. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/ceilo.py +0 -0
  42. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/ceilometer.py +0 -0
  43. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/cl61d.py +0 -0
  44. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/cloudnet_instrument.py +0 -0
  45. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/copernicus.py +0 -0
  46. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/disdrometer/__init__.py +0 -0
  47. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/disdrometer/common.py +0 -0
  48. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/disdrometer/parsivel.py +0 -0
  49. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/disdrometer/thies.py +0 -0
  50. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/fd12p.py +0 -0
  51. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/galileo.py +0 -0
  52. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/hatpro.py +0 -0
  53. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/instruments.py +0 -0
  54. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/lufft.py +0 -0
  55. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/mira.py +0 -0
  56. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/mrr.py +0 -0
  57. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/nc_lidar.py +0 -0
  58. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/nc_radar.py +0 -0
  59. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/pollyxt.py +0 -0
  60. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/rain_e_h3.py +0 -0
  61. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/rpg.py +0 -0
  62. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/rpg_reader.py +0 -0
  63. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/toa5.py +0 -0
  64. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/instruments/vaisala.py +0 -0
  65. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/metadata.py +0 -0
  66. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/__init__.py +0 -0
  67. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/file_handler.py +0 -0
  68. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/metadata.py +0 -0
  69. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/model_metadata.py +0 -0
  70. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/plotting/__init__.py +0 -0
  71. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/plotting/plot_meta.py +0 -0
  72. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/plotting/plot_tools.py +0 -0
  73. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/plotting/plotting.py +0 -0
  74. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/products/__init__.py +0 -0
  75. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/products/advance_methods.py +0 -0
  76. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/products/grid_methods.py +0 -0
  77. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/products/model_products.py +0 -0
  78. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/products/observation_products.py +0 -0
  79. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/products/product_resampling.py +0 -0
  80. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/products/tools.py +0 -0
  81. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/statistics/__init__.py +0 -0
  82. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/statistics/statistical_methods.py +0 -0
  83. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/__init__.py +0 -0
  84. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/__init__.py +0 -0
  85. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/conftest.py +0 -0
  86. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/process_cf/__init__.py +0 -0
  87. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +0 -0
  88. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +0 -0
  89. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/__init__.py +0 -0
  90. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +0 -0
  91. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +0 -0
  92. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/__init__.py +0 -0
  93. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +0 -0
  94. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +0 -0
  95. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/unit/__init__.py +0 -0
  96. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/unit/conftest.py +0 -0
  97. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +0 -0
  98. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +0 -0
  99. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/unit/test_model_products.py +0 -0
  100. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +0 -0
  101. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +0 -0
  102. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/unit/test_plotting.py +0 -0
  103. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +0 -0
  104. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/tests/unit/test_tools.py +0 -0
  105. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/model_evaluation/utils.py +0 -0
  106. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/output.py +0 -0
  107. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/plotting/__init__.py +0 -0
  108. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/plotting/plot_meta.py +0 -0
  109. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/plotting/plotting.py +0 -0
  110. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/__init__.py +0 -0
  111. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/classification.py +0 -0
  112. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/der.py +0 -0
  113. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/drizzle.py +0 -0
  114. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/drizzle_error.py +0 -0
  115. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/drizzle_tools.py +0 -0
  116. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/epsilon.py +0 -0
  117. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/ier.py +0 -0
  118. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/iwc.py +0 -0
  119. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/lwc.py +0 -0
  120. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/mie_lu_tables.nc +0 -0
  121. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/mwr_tools.py +0 -0
  122. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/products/product_tools.py +0 -0
  123. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/py.typed +0 -0
  124. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy/utils.py +0 -0
  125. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy.egg-info/SOURCES.txt +0 -0
  126. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy.egg-info/dependency_links.txt +0 -0
  127. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy.egg-info/entry_points.txt +0 -0
  128. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy.egg-info/requires.txt +0 -0
  129. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/cloudnetpy.egg-info/top_level.txt +0 -0
  130. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/docs/source/conf.py +0 -0
  131. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/pyproject.toml +0 -0
  132. {cloudnetpy-1.80.1 → cloudnetpy-1.80.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.80.1
3
+ Version: 1.80.3
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -145,10 +145,11 @@ def generate_categorize(
145
145
  return utils.time_grid(), data.radar.height
146
146
 
147
147
  def _close_all() -> None:
148
- for field in fields(data):
149
- obj = getattr(data, field.name)
150
- if isinstance(obj, DataSource):
151
- obj.close()
148
+ if "data" in locals():
149
+ for field in fields(data):
150
+ obj = getattr(data, field.name)
151
+ if isinstance(obj, DataSource):
152
+ obj.close()
152
153
 
153
154
  try:
154
155
  radar = Radar(input_files["radar"])
@@ -11,6 +11,7 @@ from pathlib import Path
11
11
  from typing import Any, NamedTuple
12
12
 
13
13
  import numpy as np
14
+ from numpy import ma
14
15
 
15
16
  from cloudnetpy import output, utils
16
17
  from cloudnetpy.cloudnetarray import CloudnetArray
@@ -122,12 +123,15 @@ class RadiometricsMP:
122
123
  logging.info("Skipping unknown record type %d", record_type)
123
124
  unknown_record_types.add(record_type)
124
125
  continue
126
+
127
+ row_trimmed = [value for value in row if value != ""]
128
+
125
129
  record = Record(
126
130
  row_number=int(row[0]),
127
131
  timestamp=_parse_datetime(row[1]),
128
132
  block_type=block_type,
129
133
  block_index=block_index,
130
- values=dict(zip(column_names, row[3:], strict=True)),
134
+ values=dict(zip(column_names, row_trimmed[3:], strict=True)),
131
135
  )
132
136
  rows.append(record)
133
137
 
@@ -148,6 +152,15 @@ class RadiometricsMP:
148
152
  ah_times = []
149
153
  block_titles = {}
150
154
  skip_procs = set()
155
+
156
+ def _parse_floats() -> list[float]:
157
+ return [
158
+ float(record.values[column])
159
+ if record.values[column].replace(".", "", 1).isdigit()
160
+ else math.nan
161
+ for column in self.ranges
162
+ ]
163
+
151
164
  for record in self.raw_data:
152
165
  if record.block_type == 100:
153
166
  block_type = int(record.values["Record Type"]) - 1
@@ -157,20 +170,18 @@ class RadiometricsMP:
157
170
  # "LV2 Processor" values "Zenith" and "0.00:90.00" should be OK
158
171
  # but "Angle20(N)" and similar should be skipped.
159
172
  proc = record.values["LV2 Processor"]
160
- if proc.startswith("Angle"):
173
+ if proc.startswith(("Angle", "Zenith26")):
161
174
  skip_procs.add(proc)
162
175
  continue
163
176
  if title == "Temperature (K)":
164
177
  temp_times.append(record.timestamp)
165
- temps.append(
166
- [float(record.values[column]) for column in self.ranges]
167
- )
178
+ temps.append(_parse_floats())
168
179
  elif title == "Relative Humidity (%)":
169
180
  rh_times.append(record.timestamp)
170
- rhs.append([float(record.values[column]) for column in self.ranges])
181
+ rhs.append(_parse_floats())
171
182
  elif title == "Vapor Density (g/m^3)":
172
183
  ah_times.append(record.timestamp)
173
- ahs.append([float(record.values[column]) for column in self.ranges])
184
+ ahs.append(_parse_floats())
174
185
  elif record.block_type == 10:
175
186
  if record.block_index == 0:
176
187
  lwp = record.values["Lqint(mm)"]
@@ -182,15 +193,13 @@ class RadiometricsMP:
182
193
  irt_times.append(record.timestamp)
183
194
  irts.append([float(irt)])
184
195
  temp_times.append(record.timestamp)
185
- temps.append(
186
- [float(record.values[column]) for column in self.ranges]
187
- )
196
+ temps.append(_parse_floats())
188
197
  elif record.block_index == 1:
189
198
  ah_times.append(record.timestamp)
190
- ahs.append([float(record.values[column]) for column in self.ranges])
199
+ ahs.append(_parse_floats())
191
200
  elif record.block_index == 2:
192
201
  rh_times.append(record.timestamp)
193
- rhs.append([float(record.values[column]) for column in self.ranges])
202
+ rhs.append(_parse_floats())
194
203
  elif record.block_type == 200:
195
204
  irt_times.append(record.timestamp)
196
205
  irt = record.values["Tir(K)"]
@@ -204,6 +213,7 @@ class RadiometricsMP:
204
213
  times.append(record.timestamp)
205
214
  lwps.append(float(lwp))
206
215
  iwvs.append(float(iwv))
216
+
207
217
  self.data["time"] = np.array(times, dtype="datetime64[s]")
208
218
  self.data["lwp"] = np.array(lwps) # mm => kg m-2
209
219
  self.data["iwv"] = np.array(iwvs) * 10 # cm => kg m-2
@@ -346,7 +356,8 @@ class RadiometricsCombined:
346
356
  if key in ("temperature", "relative_humidity", "absolute_humidity")
347
357
  else None
348
358
  )
349
- self.data[key] = CloudnetArray(array, key, dimensions=dimensions)
359
+ array_masked = ma.masked_invalid(array) if np.isnan(array).any() else array
360
+ self.data[key] = CloudnetArray(array_masked, key, dimensions=dimensions)
350
361
 
351
362
  def add_meta(self) -> None:
352
363
  """Adds some metadata."""
@@ -3,7 +3,7 @@ import datetime
3
3
  import math
4
4
  import re
5
5
  from collections import defaultdict
6
- from collections.abc import Iterable
6
+ from collections.abc import Iterable, Sequence
7
7
  from os import PathLike
8
8
 
9
9
  import numpy as np
@@ -21,11 +21,11 @@ from cloudnetpy.utils import datetime2decimal_hours
21
21
 
22
22
 
23
23
  def ws2nc(
24
- weather_station_file: str | list[str],
24
+ weather_station_file: str | PathLike | Sequence[str | PathLike],
25
25
  output_file: str,
26
26
  site_meta: dict,
27
27
  uuid: str | None = None,
28
- date: str | None = None,
28
+ date: str | datetime.date | None = None,
29
29
  ) -> str:
30
30
  """Converts weather station data into Cloudnet Level 1b netCDF file.
31
31
 
@@ -43,8 +43,10 @@ def ws2nc(
43
43
  Raises:
44
44
  ValidTimeStampError: No valid timestamps found.
45
45
  """
46
- if not isinstance(weather_station_file, list):
46
+ if isinstance(weather_station_file, str | PathLike):
47
47
  weather_station_file = [weather_station_file]
48
+ if isinstance(date, str):
49
+ date = datetime.date.fromisoformat(date)
48
50
  ws: WS
49
51
  if site_meta["name"] == "Palaiseau":
50
52
  ws = PalaiseauWS(weather_station_file, site_meta)
@@ -64,6 +66,8 @@ def ws2nc(
64
66
  ws = LampedusaWS(weather_station_file, site_meta)
65
67
  elif site_meta["name"] == "Limassol":
66
68
  ws = LimassolWS(weather_station_file, site_meta)
69
+ elif site_meta["name"] == "L'Aquila":
70
+ ws = LAquilaWS(weather_station_file, site_meta)
67
71
  else:
68
72
  msg = "Unsupported site"
69
73
  raise ValueError(msg)
@@ -98,8 +102,8 @@ class WS(CSVFile):
98
102
  rainfall_amount = ma.cumsum(self.data["rainfall_rate"].data * resolution)
99
103
  self.data["rainfall_amount"] = CloudnetArray(rainfall_amount, "rainfall_amount")
100
104
 
101
- def screen_timestamps(self, date: str) -> None:
102
- dates = np.array([str(d.date()) for d in self._data["time"]])
105
+ def screen_timestamps(self, date: datetime.date) -> None:
106
+ dates = np.array([d.date() for d in self._data["time"]])
103
107
  valid_mask = dates == date
104
108
  if not valid_mask.any():
105
109
  raise ValidTimeStampError
@@ -139,7 +143,7 @@ class WS(CSVFile):
139
143
 
140
144
 
141
145
  class PalaiseauWS(WS):
142
- def __init__(self, filenames: list[str], site_meta: dict):
146
+ def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict):
143
147
  super().__init__(site_meta)
144
148
  self.filenames = filenames
145
149
  self._data = self._read_data()
@@ -170,8 +174,8 @@ class PalaiseauWS(WS):
170
174
  decimal_hours = datetime2decimal_hours(self._data["time"])
171
175
  self.data["time"] = CloudnetArray(decimal_hours, "time")
172
176
 
173
- def screen_timestamps(self, date: str) -> None:
174
- dates = [str(d.date()) for d in self._data["time"]]
177
+ def screen_timestamps(self, date: datetime.date) -> None:
178
+ dates = [d.date() for d in self._data["time"]]
175
179
  valid_ind = [ind for ind, d in enumerate(dates) if d == date]
176
180
  if not valid_ind:
177
181
  raise ValidTimeStampError
@@ -228,7 +232,7 @@ class BucharestWS(PalaiseauWS):
228
232
 
229
233
 
230
234
  class GranadaWS(WS):
231
- def __init__(self, filenames: list[str], site_meta: dict):
235
+ def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict):
232
236
  if len(filenames) != 1:
233
237
  raise ValueError
234
238
  super().__init__(site_meta)
@@ -278,7 +282,7 @@ class GranadaWS(WS):
278
282
 
279
283
 
280
284
  class KenttarovaWS(WS):
281
- def __init__(self, filenames: list[str], site_meta: dict):
285
+ def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict):
282
286
  super().__init__(site_meta)
283
287
  self.filenames = filenames
284
288
  self._data = self._read_data()
@@ -334,7 +338,7 @@ class HyytialaWS(WS):
334
338
  - BbRT/mm = Bucket content in real-time (Pluvio200) [mm].
335
339
  """
336
340
 
337
- def __init__(self, filenames: list[str], site_meta: dict):
341
+ def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict):
338
342
  super().__init__(site_meta)
339
343
  self.filename = filenames[0]
340
344
  self._data = self._read_data()
@@ -395,7 +399,7 @@ class HyytialaWS(WS):
395
399
 
396
400
 
397
401
  class GalatiWS(WS):
398
- def __init__(self, filenames: list[str], site_meta: dict):
402
+ def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict):
399
403
  super().__init__(site_meta)
400
404
  self.filename = filenames[0]
401
405
  self._data = self._read_data()
@@ -450,7 +454,7 @@ class GalatiWS(WS):
450
454
 
451
455
 
452
456
  class JuelichWS(WS):
453
- def __init__(self, filenames: list[str], site_meta: dict):
457
+ def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict):
454
458
  super().__init__(site_meta)
455
459
  self.filename = filenames[0]
456
460
  self._data = self._read_data()
@@ -496,7 +500,7 @@ class JuelichWS(WS):
496
500
  class LampedusaWS(WS):
497
501
  """Read Lampedusa weather station data in ICOS format."""
498
502
 
499
- def __init__(self, filenames: list[str], site_meta: dict):
503
+ def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict):
500
504
  super().__init__(site_meta)
501
505
  self.filename = filenames[0]
502
506
  self._data = self._read_data()
@@ -549,7 +553,7 @@ class LampedusaWS(WS):
549
553
 
550
554
 
551
555
  class LimassolWS(WS):
552
- def __init__(self, filenames: list[str], site_meta: dict):
556
+ def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict):
553
557
  super().__init__(site_meta)
554
558
  self.filenames = filenames
555
559
  self._data = defaultdict(list)
@@ -562,8 +566,8 @@ class LimassolWS(WS):
562
566
  decimal_hours = datetime2decimal_hours(self._data["time"])
563
567
  self.data["time"] = CloudnetArray(decimal_hours, "time")
564
568
 
565
- def screen_timestamps(self, date: str) -> None:
566
- dates = [str(d.date()) for d in self._data["time"]]
569
+ def screen_timestamps(self, date: datetime.date) -> None:
570
+ dates = [d.date() for d in self._data["time"]]
567
571
  valid_ind = [ind for ind, d in enumerate(dates) if d == date]
568
572
  if not valid_ind:
569
573
  raise ValidTimeStampError
@@ -646,3 +650,51 @@ def _parse_sirta(filename: str | PathLike):
646
650
  parsed = float(value)
647
651
  output[column].append(parsed)
648
652
  return output
653
+
654
+
655
+ class LAquilaWS(WS):
656
+ def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict):
657
+ super().__init__(site_meta)
658
+ self.filenames = filenames
659
+ self._data = self._read_data()
660
+
661
+ def _read_data(self) -> dict:
662
+ data: dict[str, list] = {
663
+ key: []
664
+ for key in [
665
+ "time",
666
+ "air_temperature",
667
+ "air_pressure",
668
+ "relative_humidity",
669
+ "rainfall_rate",
670
+ "wind_speed",
671
+ "wind_direction",
672
+ ]
673
+ }
674
+ for filename in self.filenames:
675
+ with open(filename) as f:
676
+ for row in f:
677
+ if row.startswith("#"):
678
+ continue
679
+ columns = row.split(",")
680
+ if len(columns) != 7:
681
+ continue
682
+ timestamp = datetime.datetime.strptime(
683
+ columns[0], "%Y-%m-%dT%H:%M:%SZ"
684
+ ).replace(tzinfo=datetime.timezone.utc)
685
+ data["time"].append(timestamp)
686
+ data["air_temperature"].append(self._parse_value(columns[1]))
687
+ data["air_pressure"].append(self._parse_value(columns[2]))
688
+ data["relative_humidity"].append(self._parse_value(columns[3]))
689
+ data["rainfall_rate"].append(self._parse_value(columns[4]))
690
+ data["wind_speed"].append(self._parse_value(columns[5]))
691
+ data["wind_direction"].append(self._parse_value(columns[6]))
692
+ output = self.format_data(data)
693
+ _, time_ind = np.unique(output["time"], return_index=True)
694
+ for key in output:
695
+ output[key] = output[key][time_ind]
696
+ return output
697
+
698
+ def _parse_value(self, value: str) -> float:
699
+ value = value.strip()
700
+ return float(value) if value else math.nan
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
2
  MINOR = 80
3
- PATCH = 1
3
+ PATCH = 3
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.80.1
3
+ Version: 1.80.3
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
File without changes
File without changes
File without changes
File without changes
File without changes