cloudnetpy 1.83.0__tar.gz → 1.84.0__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 (133) hide show
  1. {cloudnetpy-1.83.0/cloudnetpy.egg-info → cloudnetpy-1.84.0}/PKG-INFO +1 -1
  2. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/categorize.py +1 -1
  3. cloudnetpy-1.84.0/cloudnetpy/categorize/disdrometer.py +37 -0
  4. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/lidar.py +1 -12
  5. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/mwr.py +6 -9
  6. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/disdrometer/parsivel.py +1 -0
  7. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/weather_station.py +63 -29
  8. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/utils.py +56 -1
  9. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/version.py +1 -1
  10. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0/cloudnetpy.egg-info}/PKG-INFO +1 -1
  11. cloudnetpy-1.83.0/cloudnetpy/categorize/disdrometer.py +0 -60
  12. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/LICENSE +0 -0
  13. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/MANIFEST.in +0 -0
  14. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/README.md +0 -0
  15. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/__init__.py +0 -0
  16. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/__init__.py +0 -0
  17. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/atmos_utils.py +0 -0
  18. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/attenuation.py +0 -0
  19. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/attenuations/__init__.py +0 -0
  20. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/attenuations/gas_attenuation.py +0 -0
  21. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/attenuations/liquid_attenuation.py +0 -0
  22. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/attenuations/melting_attenuation.py +0 -0
  23. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/attenuations/rain_attenuation.py +0 -0
  24. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/classify.py +0 -0
  25. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/containers.py +0 -0
  26. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/droplet.py +0 -0
  27. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/falling.py +0 -0
  28. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/freezing.py +0 -0
  29. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/insects.py +0 -0
  30. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/itu.py +0 -0
  31. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/melting.py +0 -0
  32. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/model.py +0 -0
  33. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/categorize/radar.py +0 -0
  34. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/cli.py +0 -0
  35. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/cloudnetarray.py +0 -0
  36. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/concat_lib.py +0 -0
  37. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/constants.py +0 -0
  38. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/datasource.py +0 -0
  39. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/exceptions.py +0 -0
  40. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/__init__.py +0 -0
  41. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/basta.py +0 -0
  42. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/bowtie.py +0 -0
  43. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/ceilo.py +0 -0
  44. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/ceilometer.py +0 -0
  45. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/cl61d.py +0 -0
  46. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/cloudnet_instrument.py +0 -0
  47. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/copernicus.py +0 -0
  48. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/disdrometer/__init__.py +0 -0
  49. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/disdrometer/common.py +0 -0
  50. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/disdrometer/thies.py +0 -0
  51. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/fd12p.py +0 -0
  52. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/galileo.py +0 -0
  53. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/hatpro.py +0 -0
  54. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/instruments.py +0 -0
  55. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/lufft.py +0 -0
  56. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/mira.py +0 -0
  57. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/mrr.py +0 -0
  58. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/nc_lidar.py +0 -0
  59. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/nc_radar.py +0 -0
  60. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/pollyxt.py +0 -0
  61. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/radiometrics.py +0 -0
  62. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/rain_e_h3.py +0 -0
  63. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/rpg.py +0 -0
  64. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/rpg_reader.py +0 -0
  65. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/toa5.py +0 -0
  66. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/instruments/vaisala.py +0 -0
  67. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/metadata.py +0 -0
  68. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/__init__.py +0 -0
  69. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/file_handler.py +0 -0
  70. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/metadata.py +0 -0
  71. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/model_metadata.py +0 -0
  72. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/plotting/__init__.py +0 -0
  73. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/plotting/plot_meta.py +0 -0
  74. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/plotting/plot_tools.py +0 -0
  75. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/plotting/plotting.py +0 -0
  76. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/products/__init__.py +0 -0
  77. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/products/advance_methods.py +0 -0
  78. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/products/grid_methods.py +0 -0
  79. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/products/model_products.py +0 -0
  80. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/products/observation_products.py +0 -0
  81. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/products/product_resampling.py +0 -0
  82. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/products/tools.py +0 -0
  83. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/statistics/__init__.py +0 -0
  84. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/statistics/statistical_methods.py +0 -0
  85. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/__init__.py +0 -0
  86. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/__init__.py +0 -0
  87. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/conftest.py +0 -0
  88. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/process_cf/__init__.py +0 -0
  89. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +0 -0
  90. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +0 -0
  91. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/__init__.py +0 -0
  92. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +0 -0
  93. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +0 -0
  94. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/__init__.py +0 -0
  95. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +0 -0
  96. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +0 -0
  97. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/unit/__init__.py +0 -0
  98. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/unit/conftest.py +0 -0
  99. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +0 -0
  100. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +0 -0
  101. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/unit/test_model_products.py +0 -0
  102. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +0 -0
  103. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +0 -0
  104. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/unit/test_plotting.py +0 -0
  105. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +0 -0
  106. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/tests/unit/test_tools.py +0 -0
  107. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/model_evaluation/utils.py +0 -0
  108. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/output.py +0 -0
  109. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/plotting/__init__.py +0 -0
  110. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/plotting/plot_meta.py +0 -0
  111. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/plotting/plotting.py +0 -0
  112. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/__init__.py +0 -0
  113. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/classification.py +0 -0
  114. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/der.py +0 -0
  115. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/drizzle.py +0 -0
  116. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/drizzle_error.py +0 -0
  117. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/drizzle_tools.py +0 -0
  118. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/epsilon.py +0 -0
  119. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/ier.py +0 -0
  120. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/iwc.py +0 -0
  121. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/lwc.py +0 -0
  122. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/mie_lu_tables.nc +0 -0
  123. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/mwr_tools.py +0 -0
  124. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/products/product_tools.py +0 -0
  125. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy/py.typed +0 -0
  126. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy.egg-info/SOURCES.txt +0 -0
  127. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy.egg-info/dependency_links.txt +0 -0
  128. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy.egg-info/entry_points.txt +0 -0
  129. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy.egg-info/requires.txt +0 -0
  130. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/cloudnetpy.egg-info/top_level.txt +0 -0
  131. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/docs/source/conf.py +0 -0
  132. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/pyproject.toml +0 -0
  133. {cloudnetpy-1.83.0 → cloudnetpy-1.84.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.83.0
3
+ Version: 1.84.0
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -93,7 +93,7 @@ def generate_categorize(
93
93
  if data.disdrometer is not None:
94
94
  data.disdrometer.interpolate_to_grid(time)
95
95
  if data.mwr is not None:
96
- data.mwr.rebin_to_grid(time)
96
+ data.mwr.interpolate_to_grid(time)
97
97
  data.model.calc_attenuations(data.radar.radar_frequency)
98
98
  data.model.interpolate_to_common_height()
99
99
  model_gap_ind = data.model.interpolate_to_grid(time, height)
@@ -0,0 +1,37 @@
1
+ """Mwr module, containing the :class:`Mwr` class."""
2
+
3
+ from os import PathLike
4
+
5
+ import numpy.typing as npt
6
+
7
+ from cloudnetpy.datasource import DataSource
8
+ from cloudnetpy.exceptions import DisdrometerDataError
9
+ from cloudnetpy.utils import interpolate_1d
10
+
11
+
12
+ class Disdrometer(DataSource):
13
+ """Disdrometer class, child of DataSource.
14
+
15
+ Args:
16
+ ----
17
+ full_path: Cloudnet Level 1b disdrometer file.
18
+
19
+ """
20
+
21
+ def __init__(self, full_path: str | PathLike) -> None:
22
+ super().__init__(full_path)
23
+ self._init_rainfall_rate()
24
+
25
+ def interpolate_to_grid(self, time_grid: npt.NDArray) -> None:
26
+ for key, array in self.data.items():
27
+ self.data[key].data = interpolate_1d(
28
+ self.time, array.data, time_grid, max_time=1
29
+ )
30
+
31
+ def _init_rainfall_rate(self) -> None:
32
+ keys = ("rainfall_rate", "n_particles")
33
+ for key in keys:
34
+ if key not in self.dataset.variables:
35
+ msg = f"variable {key} is missing"
36
+ raise DisdrometerDataError(msg)
37
+ self.append_data(self.dataset.variables[key][:], key)
@@ -4,12 +4,11 @@ import logging
4
4
  from os import PathLike
5
5
  from typing import Literal
6
6
 
7
- import numpy as np
8
7
  import numpy.typing as npt
9
8
  from numpy import ma
10
9
 
11
10
  from cloudnetpy.datasource import DataSource
12
- from cloudnetpy.utils import interpolate_2d_nearest
11
+ from cloudnetpy.utils import get_gap_ind, interpolate_2d_nearest
13
12
 
14
13
 
15
14
  class Lidar(DataSource):
@@ -70,13 +69,3 @@ class Lidar(DataSource):
70
69
  self.append_data(float(self.getvar("wavelength")), "lidar_wavelength")
71
70
  self.append_data(0.5, "beta_error")
72
71
  self.append_data(3.0, "beta_bias")
73
-
74
-
75
- def get_gap_ind(
76
- grid: npt.NDArray, new_grid: npt.NDArray, threshold: float
77
- ) -> list[int]:
78
- return [
79
- ind
80
- for ind, value in enumerate(new_grid)
81
- if np.min(np.abs(grid - value)) > threshold
82
- ]
@@ -8,6 +8,7 @@ import numpy.typing as npt
8
8
  from cloudnetpy import utils
9
9
  from cloudnetpy.constants import G_TO_KG
10
10
  from cloudnetpy.datasource import DataSource
11
+ from cloudnetpy.utils import interpolate_1d
11
12
 
12
13
 
13
14
  class Mwr(DataSource):
@@ -23,15 +24,11 @@ class Mwr(DataSource):
23
24
  self._init_lwp_data()
24
25
  self._init_lwp_error()
25
26
 
26
- def rebin_to_grid(self, time_grid: npt.NDArray) -> None:
27
- """Approximates lwp and its error in a grid using mean.
28
-
29
- Args:
30
- time_grid: 1D target time grid.
31
-
32
- """
33
- for array in self.data.values():
34
- array.rebin_data(self.time, time_grid)
27
+ def interpolate_to_grid(self, time_grid: npt.NDArray, max_time: float = 1) -> None:
28
+ for key, array in self.data.items():
29
+ self.data[key].data = interpolate_1d(
30
+ self.time, array.data, time_grid, max_time=max_time
31
+ )
35
32
 
36
33
  def _init_lwp_data(self) -> None:
37
34
  lwp = self.dataset.variables["lwp"][:]
@@ -205,6 +205,7 @@ TOA5_HEADERS = {
205
205
  "precipitation": "_rain_accum",
206
206
  "rain accum [mm]": "_rain_accum",
207
207
  "weatherCodeWaWa": "synop_WaWa",
208
+ "wawa": "synop_WaWa",
208
209
  "weather_code_wawa": "synop_WaWa",
209
210
  "radarReflectivity": "radar_reflectivity",
210
211
  "radar_reflectivity": "radar_reflectivity",
@@ -70,6 +70,8 @@ def ws2nc(
70
70
  ws = LimassolWS(weather_station_file, site_meta)
71
71
  elif site_meta["name"] == "L'Aquila":
72
72
  ws = LAquilaWS(weather_station_file, site_meta)
73
+ elif site_meta["name"] == "Maïdo Observatory":
74
+ ws = MaidoWS(weather_station_file, site_meta)
73
75
  else:
74
76
  msg = "Unsupported site"
75
77
  raise ValueError(msg)
@@ -162,6 +164,26 @@ class WS(CSVFile):
162
164
 
163
165
 
164
166
  class PalaiseauWS(WS):
167
+ expected_header_identifiers: tuple[str, ...] = (
168
+ "DateTime(yyyy-mm-ddThh:mm:ssZ)",
169
+ "Windspeed(m/s)",
170
+ "Winddirection(deg",
171
+ "Airtemperature",
172
+ "Relativehumidity(%)",
173
+ "Pressure(hPa)",
174
+ "Precipitationrate(mm/min)",
175
+ "precipitation",
176
+ )
177
+ keys: tuple[str, ...] = (
178
+ "wind_speed",
179
+ "wind_direction",
180
+ "air_temperature",
181
+ "relative_humidity",
182
+ "air_pressure",
183
+ "rainfall_rate",
184
+ "rainfall_amount",
185
+ )
186
+
165
187
  def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict) -> None:
166
188
  super().__init__(site_meta)
167
189
  self.filenames = filenames
@@ -175,16 +197,16 @@ class PalaiseauWS(WS):
175
197
  for row in data:
176
198
  if not (columns := row.split()):
177
199
  continue
178
- if row.startswith("#"):
179
- header_row = "".join(columns)
180
- if header_row not in header:
181
- header.append(header_row)
182
- else:
200
+ if re.match(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z", columns[0]):
183
201
  timestamp = datetime.datetime.strptime(
184
202
  columns[0], "%Y-%m-%dT%H:%M:%SZ"
185
203
  ).replace(tzinfo=datetime.timezone.utc)
186
204
  values.append([timestamp] + [float(x) for x in columns[1:]])
187
205
  timestamps.append(timestamp)
206
+ else:
207
+ header_row = "".join(columns)
208
+ if header_row not in header:
209
+ header.append(header_row)
188
210
 
189
211
  self._validate_header(header)
190
212
  return {"time": timestamps, "values": values}
@@ -204,16 +226,9 @@ class PalaiseauWS(WS):
204
226
  ]
205
227
 
206
228
  def add_data(self) -> None:
207
- keys = (
208
- "wind_speed",
209
- "wind_direction",
210
- "air_temperature",
211
- "relative_humidity",
212
- "air_pressure",
213
- "rainfall_rate",
214
- "rainfall_amount",
215
- )
216
- for ind, key in enumerate(keys):
229
+ for ind, key in enumerate(self.keys):
230
+ if key.startswith("_"):
231
+ continue
217
232
  array = [row[ind + 1] for row in self._data["values"]]
218
233
  array_masked = ma.masked_invalid(array)
219
234
  self.data[key] = CloudnetArray(array_masked, key)
@@ -223,27 +238,46 @@ class PalaiseauWS(WS):
223
238
  self.data["rainfall_amount"][:] / 1000
224
239
  ) # mm -> m
225
240
 
226
- @staticmethod
227
- def _validate_header(header: list[str]) -> None:
228
- expected_identifiers = [
229
- "DateTime(yyyy-mm-ddThh:mm:ssZ)",
230
- "Windspeed(m/s)",
231
- "Winddirection(deg",
232
- "Airtemperature",
233
- "Relativehumidity(%)",
234
- "Pressure(hPa)",
235
- "Precipitationrate(mm/min)",
236
- "precipitation",
237
- ]
241
+ def _validate_header(self, header: list[str]) -> None:
238
242
  column_titles = [row for row in header if "Col." in row]
239
243
  error_msg = "Unexpected weather station file format"
240
- if len(column_titles) != len(expected_identifiers):
244
+ if len(column_titles) != len(self.expected_header_identifiers):
241
245
  raise ValueError(error_msg)
242
- for title, identifier in zip(column_titles, expected_identifiers, strict=True):
246
+ for title, identifier in zip(
247
+ column_titles, self.expected_header_identifiers, strict=True
248
+ ):
243
249
  if identifier not in title:
244
250
  raise ValueError(error_msg)
245
251
 
246
252
 
253
+ class MaidoWS(PalaiseauWS):
254
+ expected_header_identifiers = (
255
+ "DateTimeyyyy-mm-ddThh:mm:ssZ",
256
+ "Winddirection-average",
257
+ "Windspeed-maximumvalue(m/s)",
258
+ "Windspeed-average(m/s)",
259
+ "Pressure-average(hPa)",
260
+ "Relativehumidity-maximumvalue(%)",
261
+ "Relativehumidity-average(%)",
262
+ "Airtemperature-minimumvalue",
263
+ "Airtemperature-average",
264
+ )
265
+
266
+ keys = (
267
+ "wind_direction",
268
+ "_wind_speed_max",
269
+ "wind_speed",
270
+ "air_pressure",
271
+ "_relative_humidity_max",
272
+ "relative_humidity",
273
+ "_air_temperature_min",
274
+ "air_temperature",
275
+ )
276
+
277
+ def convert_rainfall_amount(self) -> None:
278
+ pass
279
+
280
+
247
281
  class BucharestWS(PalaiseauWS):
248
282
  def convert_rainfall_rate(self) -> None:
249
283
  rainfall_rate = self.data["rainfall_rate"][:]
@@ -3,6 +3,7 @@
3
3
  import base64
4
4
  import datetime
5
5
  import hashlib
6
+ import logging
6
7
  import os
7
8
  import re
8
9
  import textwrap
@@ -17,7 +18,12 @@ import numpy as np
17
18
  import numpy.typing as npt
18
19
  from numpy import ma
19
20
  from scipy import ndimage, stats
20
- from scipy.interpolate import RectBivariateSpline, RegularGridInterpolator, griddata
21
+ from scipy.interpolate import (
22
+ RectBivariateSpline,
23
+ RegularGridInterpolator,
24
+ griddata,
25
+ interp1d,
26
+ )
21
27
 
22
28
  from cloudnetpy.cloudnetarray import CloudnetArray
23
29
  from cloudnetpy.constants import SEC_IN_DAY, SEC_IN_HOUR, SEC_IN_MINUTE
@@ -413,6 +419,55 @@ def interpolate_2d_nearest(
413
419
  return fun((xx, yy)).T
414
420
 
415
421
 
422
+ def interpolate_1d(
423
+ time: npt.NDArray, y: ma.MaskedArray, time_new: npt.NDArray, max_time: float
424
+ ) -> npt.NDArray:
425
+ """1D linear interpolation preserving the mask.
426
+
427
+ Args:
428
+ time: 1D array in fraction hour.
429
+ y: 1D masked array, data values.
430
+ time_new: 1D array, new time coordinates.
431
+ max_time: Maximum allowed gap in minutes. Values outside this gap will
432
+ be masked.
433
+ """
434
+ if np.max(time) > 24 or np.min(time) < 0:
435
+ msg = "Time vector must be in fraction hours between 0 and 24"
436
+ raise ValueError(msg)
437
+ if ma.is_masked(y):
438
+ if y.mask.all():
439
+ return ma.masked_all(time_new.shape)
440
+ time = time[~y.mask]
441
+ y = y[~y.mask]
442
+ fun = interp1d(time, y, fill_value=(y[0], y[-1]), bounds_error=False)
443
+ interpolated = ma.array(fun(time_new))
444
+ bad_idx = get_gap_ind(time, time_new, max_time / 60)
445
+
446
+ if len(bad_idx) > 0:
447
+ msg = f"Unable to interpolate for {len(bad_idx)} time steps"
448
+ logging.warning(msg)
449
+ interpolated[bad_idx] = ma.masked
450
+
451
+ return interpolated
452
+
453
+
454
+ def get_gap_ind(
455
+ grid: npt.NDArray, new_grid: npt.NDArray, threshold: float
456
+ ) -> list[int]:
457
+ """Finds indices in new_grid that are too far from grid."""
458
+ if grid.size == 0:
459
+ return list(range(len(new_grid)))
460
+ idxs = np.searchsorted(grid, new_grid)
461
+ left_dist = np.where(idxs > 0, np.abs(new_grid - grid[idxs - 1]), np.inf)
462
+ right_dist = np.where(
463
+ idxs < len(grid),
464
+ np.abs(new_grid - grid[np.clip(idxs, 0, len(grid) - 1)]),
465
+ np.inf,
466
+ )
467
+ nearest = np.minimum(left_dist, right_dist)
468
+ return np.where(nearest > threshold)[0].tolist()
469
+
470
+
416
471
  def calc_relative_error(reference: npt.NDArray, array: npt.NDArray) -> npt.NDArray:
417
472
  """Calculates relative error (%)."""
418
473
  return ((array - reference) / reference) * 100
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
- MINOR = 83
2
+ MINOR = 84
3
3
  PATCH = 0
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.83.0
3
+ Version: 1.84.0
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -1,60 +0,0 @@
1
- """Mwr module, containing the :class:`Mwr` class."""
2
-
3
- import logging
4
- from os import PathLike
5
-
6
- import numpy as np
7
- import numpy.typing as npt
8
- from numpy import ma
9
- from scipy.interpolate import interp1d
10
-
11
- from cloudnetpy.categorize.lidar import get_gap_ind
12
- from cloudnetpy.datasource import DataSource
13
- from cloudnetpy.exceptions import DisdrometerDataError
14
-
15
-
16
- class Disdrometer(DataSource):
17
- """Disdrometer class, child of DataSource.
18
-
19
- Args:
20
- ----
21
- full_path: Cloudnet Level 1b disdrometer file.
22
-
23
- """
24
-
25
- def __init__(self, full_path: str | PathLike) -> None:
26
- super().__init__(full_path)
27
- self._init_rainfall_rate()
28
-
29
- def interpolate_to_grid(self, time_grid: npt.NDArray) -> None:
30
- for key, array in self.data.items():
31
- self.data[key].data = self._interpolate(array.data, time_grid)
32
-
33
- def _init_rainfall_rate(self) -> None:
34
- keys = ("rainfall_rate", "n_particles")
35
- for key in keys:
36
- if key not in self.dataset.variables:
37
- msg = f"variable {key} is missing"
38
- raise DisdrometerDataError(msg)
39
- self.append_data(self.dataset.variables[key][:], key)
40
-
41
- def _interpolate(self, y: ma.MaskedArray, x_new: npt.NDArray) -> npt.NDArray:
42
- time = self.time
43
- mask = ma.getmask(y)
44
- if mask is not ma.nomask:
45
- if np.all(mask):
46
- return ma.masked_all(x_new.shape)
47
- not_masked = ~mask
48
- y = y[not_masked]
49
- time = time[not_masked]
50
- fun = interp1d(time, y, fill_value="extrapolate")
51
- interpolated_array = ma.array(fun(x_new))
52
- max_time = 1 / 60 # min -> fraction hour
53
- mask_ind = get_gap_ind(time, x_new, max_time)
54
-
55
- if len(mask_ind) > 0:
56
- msg = f"Unable to interpolate disdrometer for {len(mask_ind)} time steps"
57
- logging.warning(msg)
58
- interpolated_array[mask_ind] = ma.masked
59
-
60
- return interpolated_array
File without changes
File without changes
File without changes
File without changes
File without changes