cloudnetpy 1.89.2__tar.gz → 1.90.1__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 (140) hide show
  1. {cloudnetpy-1.89.2/cloudnetpy.egg-info → cloudnetpy-1.90.1}/PKG-INFO +1 -1
  2. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/categorize.py +1 -2
  3. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/classify.py +9 -3
  4. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/insects.py +1 -4
  5. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/lidar.py +2 -2
  6. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/cli.py +16 -1
  7. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/constants.py +2 -0
  8. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/__init__.py +1 -0
  9. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/instruments.py +11 -4
  10. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/rain_e_h3.py +1 -1
  11. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/rpg.py +0 -6
  12. cloudnetpy-1.90.1/cloudnetpy/instruments/weather_radar.py +163 -0
  13. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/weather_station.py +1 -1
  14. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/metadata.py +6 -0
  15. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/plotting/plotting.py +18 -18
  16. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/utils.py +1 -1
  17. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/version.py +2 -2
  18. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1/cloudnetpy.egg-info}/PKG-INFO +1 -1
  19. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy.egg-info/SOURCES.txt +1 -0
  20. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/LICENSE +0 -0
  21. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/MANIFEST.in +0 -0
  22. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/README.md +0 -0
  23. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/__init__.py +0 -0
  24. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/__init__.py +0 -0
  25. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/atmos_utils.py +0 -0
  26. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/attenuation.py +0 -0
  27. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/attenuations/__init__.py +0 -0
  28. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/attenuations/gas_attenuation.py +0 -0
  29. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/attenuations/liquid_attenuation.py +0 -0
  30. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/attenuations/melting_attenuation.py +0 -0
  31. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/attenuations/rain_attenuation.py +0 -0
  32. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/containers.py +0 -0
  33. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/disdrometer.py +0 -0
  34. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/droplet.py +0 -0
  35. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/falling.py +0 -0
  36. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/freezing.py +0 -0
  37. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/itu.py +0 -0
  38. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/melting.py +0 -0
  39. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/model.py +0 -0
  40. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/mwr.py +0 -0
  41. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/categorize/radar.py +0 -0
  42. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/cloudnetarray.py +0 -0
  43. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/concat_lib.py +0 -0
  44. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/datasource.py +0 -0
  45. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/disdronator/__init__.py +0 -0
  46. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/disdronator/lpm.py +0 -0
  47. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/disdronator/parsivel.py +0 -0
  48. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/disdronator/rd80.py +0 -0
  49. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/disdronator/utils.py +0 -0
  50. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/exceptions.py +0 -0
  51. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/basta.py +0 -0
  52. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/bowtie.py +0 -0
  53. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/ceilo.py +0 -0
  54. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/ceilometer.py +0 -0
  55. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/cl61d.py +0 -0
  56. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/cloudnet_instrument.py +0 -0
  57. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/copernicus.py +0 -0
  58. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/da10.py +0 -0
  59. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/disdrometer/__init__.py +0 -0
  60. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/disdrometer/common.py +0 -0
  61. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/disdrometer/parsivel.py +0 -0
  62. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/disdrometer/rd80.py +0 -0
  63. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/disdrometer/thies.py +0 -0
  64. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/fd12p.py +0 -0
  65. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/galileo.py +0 -0
  66. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/hatpro.py +0 -0
  67. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/lufft.py +0 -0
  68. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/mira.py +0 -0
  69. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/mrr.py +0 -0
  70. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/nc_lidar.py +0 -0
  71. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/nc_radar.py +0 -0
  72. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/pollyxt.py +0 -0
  73. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/radiometrics.py +0 -0
  74. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/rpg_reader.py +0 -0
  75. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/toa5.py +0 -0
  76. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/instruments/vaisala.py +0 -0
  77. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/__init__.py +0 -0
  78. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/file_handler.py +0 -0
  79. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/metadata.py +0 -0
  80. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/model_metadata.py +0 -0
  81. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/plotting/__init__.py +0 -0
  82. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/plotting/plot_meta.py +0 -0
  83. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/plotting/plot_tools.py +0 -0
  84. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/plotting/plotting.py +0 -0
  85. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/products/__init__.py +0 -0
  86. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/products/advance_methods.py +0 -0
  87. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/products/grid_methods.py +0 -0
  88. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/products/model_products.py +0 -0
  89. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/products/observation_products.py +0 -0
  90. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/products/product_resampling.py +0 -0
  91. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/products/tools.py +0 -0
  92. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/statistics/__init__.py +0 -0
  93. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/statistics/statistical_methods.py +0 -0
  94. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/__init__.py +0 -0
  95. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/__init__.py +0 -0
  96. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/conftest.py +0 -0
  97. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/process_cf/__init__.py +0 -0
  98. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +0 -0
  99. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +0 -0
  100. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/__init__.py +0 -0
  101. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +0 -0
  102. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +0 -0
  103. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/__init__.py +0 -0
  104. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +0 -0
  105. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +0 -0
  106. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/unit/__init__.py +0 -0
  107. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/unit/conftest.py +0 -0
  108. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +0 -0
  109. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +0 -0
  110. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/unit/test_model_products.py +0 -0
  111. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +0 -0
  112. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +0 -0
  113. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/unit/test_plotting.py +0 -0
  114. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +0 -0
  115. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/tests/unit/test_tools.py +0 -0
  116. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/model_evaluation/utils.py +0 -0
  117. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/output.py +0 -0
  118. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/plotting/__init__.py +0 -0
  119. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/plotting/plot_meta.py +0 -0
  120. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/__init__.py +0 -0
  121. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/classification.py +0 -0
  122. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/der.py +0 -0
  123. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/drizzle.py +0 -0
  124. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/drizzle_error.py +0 -0
  125. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/drizzle_tools.py +0 -0
  126. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/epsilon.py +0 -0
  127. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/ier.py +0 -0
  128. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/iwc.py +0 -0
  129. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/lwc.py +0 -0
  130. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/mie_lu_tables.nc +0 -0
  131. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/mwr_tools.py +0 -0
  132. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/products/product_tools.py +0 -0
  133. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy/py.typed +0 -0
  134. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy.egg-info/dependency_links.txt +0 -0
  135. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy.egg-info/entry_points.txt +0 -0
  136. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy.egg-info/requires.txt +0 -0
  137. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/cloudnetpy.egg-info/top_level.txt +0 -0
  138. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/docs/source/conf.py +0 -0
  139. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/pyproject.toml +0 -0
  140. {cloudnetpy-1.89.2 → cloudnetpy-1.90.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.89.2
3
+ Version: 1.90.1
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -22,7 +22,6 @@ from cloudnetpy.categorize.mwr import Mwr
22
22
  from cloudnetpy.categorize.radar import Radar
23
23
  from cloudnetpy.datasource import DataSource
24
24
  from cloudnetpy.exceptions import DisdrometerDataError, ValidTimeStampError
25
- from cloudnetpy.instruments.rpg import RPG_ATTRIBUTES
26
25
  from cloudnetpy.metadata import COMMON_ATTRIBUTES, MetaData
27
26
  from cloudnetpy.products.product_tools import CategoryBits, QualityBits
28
27
 
@@ -436,7 +435,7 @@ CATEGORIZE_ATTRIBUTES = {
436
435
  units="m s-1",
437
436
  dimensions=("time", "height"),
438
437
  ),
439
- "zdr": RPG_ATTRIBUTES["zdr"]._replace(dimensions=("time", "height")),
438
+ "zdr": COMMON_ATTRIBUTES["zdr"]._replace(dimensions=("time", "height")),
440
439
  "nyquist_velocity": COMMON_ATTRIBUTES["nyquist_velocity"]._replace(
441
440
  dimensions=("time", "height")
442
441
  ),
@@ -86,7 +86,7 @@ def classify_measurements(data: Observations) -> ClassificationResult:
86
86
  bits.insect, insect_prob = insects.find_insects(obs, bits.melting, bits.droplet)
87
87
  bits.falling = falling.find_falling_hydrometeors(obs, bits.droplet, bits.insect)
88
88
  for _ in range(5):
89
- _fix_undetected_melting_layer(bits)
89
+ _fix_undetected_melting_layer(bits, obs.lwp)
90
90
  _filter_insects(bits)
91
91
  bits.aerosol = _find_aerosols(obs, bits)
92
92
  _fix_super_cold_liquid(obs, bits)
@@ -223,10 +223,16 @@ def _find_aerosols(
223
223
  return is_beta & ~bits.falling & ~bits.droplet
224
224
 
225
225
 
226
- def _fix_undetected_melting_layer(bits: CategoryBits) -> None:
226
+ def _fix_undetected_melting_layer(
227
+ bits: CategoryBits,
228
+ lwp: npt.NDArray,
229
+ lwp_threshold: float = 0.005,
230
+ ) -> None:
227
231
  drizzle_and_falling = _find_drizzle_and_falling(bits)
228
232
  transition = ma.diff(drizzle_and_falling, axis=1) == -1
229
- bits.melting[:, 1:][transition] = True
233
+ masked = ma.getmaskarray(lwp)
234
+ allow = masked | (np.asarray(lwp) > lwp_threshold)
235
+ bits.melting[:, 1:][transition & allow[:, np.newaxis]] = True
230
236
 
231
237
 
232
238
  def _find_drizzle_and_falling(bits: CategoryBits) -> npt.NDArray:
@@ -151,10 +151,7 @@ def _fill_missing_pixels(
151
151
  prob_from_ldr: npt.NDArray,
152
152
  prob_from_others: npt.NDArray,
153
153
  ) -> npt.NDArray:
154
- prob_combined = np.copy(prob_from_ldr)
155
- no_ldr = np.where(prob_from_ldr == 0)
156
- prob_combined[no_ldr] = prob_from_others[no_ldr]
157
- return prob_combined
154
+ return np.maximum(prob_from_ldr, prob_from_others)
158
155
 
159
156
 
160
157
  def _screen_insects(
@@ -59,10 +59,10 @@ class Lidar(DataSource):
59
59
  ) -> None:
60
60
  prefix = f"Unable to interpolate lidar for {len(ind)}"
61
61
  if dim == "time" and ind:
62
- logging.warning("%s time steps", prefix)
62
+ logging.debug("%s time steps", prefix)
63
63
  data[ind, :] = ma.masked
64
64
  elif dim == "height" and ind:
65
- logging.warning("%s altitudes", prefix)
65
+ logging.debug("%s altitudes", prefix)
66
66
  data[:, ind] = ma.masked
67
67
 
68
68
  def _add_meta(self) -> None:
@@ -5,6 +5,7 @@ import datetime
5
5
  import gzip
6
6
  import hashlib
7
7
  import importlib
8
+ import json
8
9
  import logging
9
10
  import re
10
11
  import shutil
@@ -136,7 +137,11 @@ def _process_categorize(
136
137
 
137
138
  try:
138
139
  logging.info("Processing categorize...")
139
- generate_categorize(cast("CategorizeInput", input_files), cat_filepath)
140
+ generate_categorize(
141
+ cast("CategorizeInput", input_files),
142
+ cat_filepath,
143
+ options=args.options,
144
+ )
140
145
  logging.info("Processed categorize to %s", cat_filepath)
141
146
  except NameError:
142
147
  logging.info("No data available for this date.")
@@ -648,6 +653,16 @@ def main() -> None:
648
653
  help="Variables to plot (comma-separated), e.g. 'target_classification'",
649
654
  default=None,
650
655
  )
656
+ parser.add_argument(
657
+ "-o",
658
+ "--options",
659
+ type=json.loads,
660
+ help=(
661
+ "Options for categorize processing as a JSON string, "
662
+ "e.g. '{\"temperature_offset\": 3}'"
663
+ ),
664
+ default=None,
665
+ )
651
666
  args = parser.parse_args()
652
667
 
653
668
  logger = logging.getLogger()
@@ -27,9 +27,11 @@ G_TO_KG: Final = 1e-3
27
27
  M_TO_KM: Final = 1e-3
28
28
  KG_TO_G: Final = 1e3
29
29
  M_TO_MM: Final = 1e3
30
+ CM_TO_M: Final = 1e-2
30
31
  M_S_TO_MM_H: Final = SEC_IN_HOUR / MM_TO_M
31
32
  MM_H_TO_M_S: Final = 1 / M_S_TO_MM_H
32
33
  GHZ_TO_HZ: Final = 1e9
34
+ HZ_TO_GHZ: Final = 1e-9
33
35
  HPA_TO_PA: Final = 100
34
36
  PA_TO_HPA: Final = 1 / HPA_TO_PA
35
37
  KM_H_TO_M_S: Final = 1000 / SEC_IN_HOUR
@@ -13,4 +13,5 @@ from .pollyxt import pollyxt2nc
13
13
  from .radiometrics import radiometrics2nc
14
14
  from .rain_e_h3 import rain_e_h32nc
15
15
  from .rpg import rpg2nc
16
+ from .weather_radar import wr2nc
16
17
  from .weather_station import ws2nc
@@ -216,31 +216,38 @@ RD80 = Instrument(
216
216
  PLUVIO2 = Instrument(
217
217
  manufacturer="OTT HydroMet",
218
218
  domain="rain-gauge",
219
- category="rain-gauge",
219
+ category="rain gauge",
220
220
  model="Pluvio2",
221
221
  )
222
222
 
223
223
  PLUVIO2S = Instrument(
224
224
  manufacturer="OTT HydroMet",
225
225
  domain="rain-gauge",
226
- category="rain-gauge",
226
+ category="rain gauge",
227
227
  model="Pluvio2S",
228
228
  )
229
229
 
230
230
  THIES_PT = Instrument(
231
231
  manufacturer="Thies Clima",
232
232
  domain="rain-gauge",
233
- category="rain-gauge",
233
+ category="rain gauge",
234
234
  model="Precipitation Transmitter",
235
235
  )
236
236
 
237
237
  RAIN_E_H3 = Instrument(
238
238
  manufacturer="LAMBRECHT meteo GmbH",
239
239
  domain="rain-gauge",
240
- category="rain-gauge",
240
+ category="rain gauge",
241
241
  model="rain[e]H3",
242
242
  )
243
243
 
244
+ WRM200 = Instrument(
245
+ manufacturer="Vaisala",
246
+ domain="weather-radar",
247
+ category="weather radar",
248
+ model="WRM200",
249
+ )
250
+
244
251
  GENERIC_WEATHER_STATION = Instrument(
245
252
  domain="weather-station",
246
253
  category="weather station",
@@ -19,7 +19,7 @@ def rain_e_h32nc(
19
19
  uuid: str | UUID | None = None,
20
20
  date: str | datetime.date | None = None,
21
21
  ) -> UUID:
22
- """Converts rain_e_h3 rain-gauge into Cloudnet Level 1b netCDF file.
22
+ """Converts rain_e_h3 rain gauge into Cloudnet Level 1b netCDF file.
23
23
 
24
24
  Args:
25
25
  input_file: Filename of rain_e_h3 CSV file.
@@ -471,12 +471,6 @@ RPG_ATTRIBUTES = {
471
471
  dimensions=("time", "range"),
472
472
  ),
473
473
  # STSR-mode radars
474
- "zdr": MetaData(
475
- long_name="Differential reflectivity", units="dB", dimensions=("time", "range")
476
- ),
477
- "rho_hv": MetaData(
478
- long_name="Correlation coefficient", units="1", dimensions=("time", "range")
479
- ),
480
474
  "phi_dp": MetaData(
481
475
  long_name="Differential phase", units="rad", dimensions=("time", "range")
482
476
  ),
@@ -0,0 +1,163 @@
1
+ import datetime
2
+ from collections import defaultdict
3
+ from collections.abc import Iterable, Sequence
4
+ from os import PathLike
5
+ from uuid import UUID
6
+
7
+ import netCDF4
8
+ import numpy as np
9
+ import numpy.typing as npt
10
+ from numpy import ma
11
+
12
+ from cloudnetpy import output, utils
13
+ from cloudnetpy.cloudnetarray import CloudnetArray
14
+ from cloudnetpy.constants import CM_TO_M, HZ_TO_GHZ, SPEED_OF_LIGHT
15
+ from cloudnetpy.instruments import instruments
16
+ from cloudnetpy.instruments.cloudnet_instrument import CloudnetInstrument
17
+
18
+
19
+ def wr2nc(
20
+ input_files: str | PathLike | Sequence[str | PathLike],
21
+ output_file: str | PathLike,
22
+ site_meta: dict,
23
+ uuid: str | UUID | None = None,
24
+ date: str | datetime.date | None = None,
25
+ ) -> UUID:
26
+ """Converts OPERA HDF5 weather radar data into Cloudnet Level 1b netCDF file.
27
+
28
+ Args:
29
+ input_files: List of OPERA HDF5 files.
30
+ output_file: Output filename.
31
+ site_meta: Dictionary containing information about the site. Required
32
+ keys are `name`, `latitude`, `longitude` and `altitude`.
33
+ uuid: Set specific UUID for the file.
34
+ date: Expected date as YYYY-MM-DD.
35
+
36
+ Returns:
37
+ UUID of the generated file.
38
+ """
39
+ if isinstance(date, str):
40
+ date = datetime.date.fromisoformat(date)
41
+ uuid = utils.get_uuid(uuid)
42
+ if isinstance(input_files, str | PathLike):
43
+ input_files = [input_files]
44
+ wr = WeatherRadar(input_files, site_meta, date)
45
+ wr.sort_and_dedup_timestamps()
46
+ wr.convert_to_cloudnet_arrays()
47
+ wr.screen_noise()
48
+ wr.add_meta()
49
+ attributes = output.add_time_attribute({}, wr.date)
50
+ output.update_attributes(wr.data, attributes)
51
+ output.save_level1b(wr, output_file, uuid)
52
+ return uuid
53
+
54
+
55
+ class WeatherRadar(CloudnetInstrument):
56
+ def __init__(
57
+ self,
58
+ filenames: Iterable[str | PathLike],
59
+ site_meta: dict,
60
+ expected_date: datetime.date | None = None,
61
+ ) -> None:
62
+ super().__init__()
63
+ self.site_meta = site_meta
64
+ self._read_data(filenames)
65
+ self._screen_time(expected_date)
66
+ self.instrument = instruments.WRM200
67
+
68
+ def _read_data(self, filenames: Iterable[str | PathLike]) -> None:
69
+ times = []
70
+ data = defaultdict(list)
71
+ for filename in filenames:
72
+ file_time, file_range, file_data, file_freq = _read_opera_h5(filename)
73
+ times.append(file_time)
74
+ for key, value in file_data.items():
75
+ data[key].append(value)
76
+ self.raw_time = np.array(times)
77
+ self.raw_range = file_range
78
+ self.raw_data = {key: ma.concatenate(value) for key, value in data.items()}
79
+ self.radar_frequency = file_freq
80
+
81
+ def _screen_time(self, expected_date: datetime.date | None = None) -> None:
82
+ if expected_date is None:
83
+ self.date = self.raw_time[0].date()
84
+ else:
85
+ is_valid = [dt.date() == expected_date for dt in self.raw_time]
86
+ self.raw_time = self.raw_time[is_valid]
87
+ for key in self.raw_data:
88
+ self.raw_data[key] = self.raw_data[key][is_valid]
89
+ self.date = expected_date
90
+
91
+ def sort_and_dedup_timestamps(self) -> None:
92
+ self.raw_time, time_ind = np.unique(self.raw_time, return_index=True)
93
+ for key in self.raw_data:
94
+ self.raw_data[key] = self.raw_data[key][time_ind]
95
+
96
+ def add_meta(self) -> None:
97
+ valid_keys = ("latitude", "longitude", "altitude")
98
+ for key, value in self.site_meta.items():
99
+ name = key.lower()
100
+ if name in valid_keys:
101
+ self.data[name] = CloudnetArray(float(value), name)
102
+
103
+ def convert_to_cloudnet_arrays(self) -> None:
104
+ epoch = datetime.datetime.combine(self.date, datetime.time())
105
+ hour = (self.raw_time - epoch) / datetime.timedelta(hours=1)
106
+ height = self.site_meta["altitude"] + self.raw_range
107
+ self.data["time"] = CloudnetArray(hour.astype(np.float32), "time")
108
+ self.data["range"] = CloudnetArray(self.raw_range, "range")
109
+ self.data["height"] = CloudnetArray(height, "height")
110
+ self.data["SNR"] = CloudnetArray(self.raw_data["SNR"], "SNR")
111
+ self.data["v"] = CloudnetArray(self.raw_data["VRADH"], "v")
112
+ self.data["width"] = CloudnetArray(self.raw_data["WRADH"], "width")
113
+ self.data["zdr"] = CloudnetArray(self.raw_data["ZDR"], "zdr")
114
+ self.data["rho_hv"] = CloudnetArray(self.raw_data["RHOHV"], "rho_hv")
115
+ self.data["radar_frequency"] = CloudnetArray(
116
+ self.radar_frequency, "radar_frequency"
117
+ )
118
+
119
+ def screen_noise(self) -> None:
120
+ is_noise = self.data["SNR"].data < -5
121
+ for cloudnet_array in self.data.values():
122
+ if cloudnet_array.data.ndim == 2:
123
+ cloudnet_array.mask_indices(is_noise)
124
+
125
+
126
+ def _read_opera_h5(
127
+ file: str | PathLike,
128
+ ) -> tuple[datetime.datetime, npt.NDArray, dict[str, list], float]:
129
+ all_data = defaultdict(list)
130
+ with netCDF4.Dataset(file) as rootgrp:
131
+ date = rootgrp["what"].date
132
+ time = rootgrp["what"].time
133
+ dt = datetime.datetime.strptime(date + time, "%Y%m%d%H%M%S")
134
+
135
+ wavelength = rootgrp["how"].wavelength * CM_TO_M
136
+ frequency = HZ_TO_GHZ * SPEED_OF_LIGHT / wavelength
137
+
138
+ dataset = rootgrp["dataset1"]
139
+ nbins = dataset["where"].nbins
140
+ rstart = dataset["where"].nbins
141
+ rscale = dataset["where"].rscale
142
+ rng = rstart + rscale * np.arange(nbins)
143
+
144
+ grpnames = [group for group in dataset.groups if group.startswith("data")]
145
+ for grpname in grpnames:
146
+ grp = dataset[grpname]
147
+ quantity = grp["what"].quantity
148
+ is_db = quantity in ("ZDR", "SNR")
149
+ offset = grp["what"].offset
150
+ gain = grp["what"].gain
151
+ nodata = grp["what"].nodata
152
+ undetect = grp["what"].undetect
153
+ grpdata = grp["data"][:]
154
+ is_masked = (grpdata == nodata) | (grpdata == undetect)
155
+ grpdata = offset + gain * ma.masked_where(is_masked, grpdata)
156
+ if is_db:
157
+ grpdata = utils.db2lin(grpdata)
158
+ grpdata = ma.mean(grpdata, axis=0)
159
+ if is_db:
160
+ grpdata = utils.lin2db(grpdata)
161
+ all_data[quantity].append(grpdata)
162
+
163
+ return dt, rng, all_data, frequency
@@ -399,7 +399,7 @@ class KenttarovaWS(WS):
399
399
 
400
400
 
401
401
  class HyytialaWS(WS):
402
- """Hyytiälä rain-gauge variables: a = Pluvio400 and b = Pluvio200.
402
+ """Hyytiälä rain gauge variables: a = Pluvio400 and b = Pluvio200.
403
403
  E.g.
404
404
  - AaRNRT/mm = amount of non-real-time rain total (Pluvio400) [mm]
405
405
  - BbRT/mm = Bucket content in real-time (Pluvio200) [mm].
@@ -170,6 +170,12 @@ COMMON_ATTRIBUTES = {
170
170
  "SNR": MetaData(
171
171
  long_name="Signal-to-noise ratio", units="dB", dimensions=("time", "range")
172
172
  ),
173
+ "zdr": MetaData(
174
+ long_name="Differential reflectivity", units="dB", dimensions=("time", "range")
175
+ ),
176
+ "rho_hv": MetaData(
177
+ long_name="Correlation coefficient", units="1", dimensions=("time", "range")
178
+ ),
173
179
  "relative_humidity": MetaData(
174
180
  long_name="Relative humidity",
175
181
  standard_name="relative_humidity",
@@ -710,9 +710,9 @@ class Plot2D(Plot):
710
710
  cbar, clabel = _hide_segments()
711
711
  alt = self._screen_data_by_max_y(figure_data)
712
712
  image = self._ax.pcolorfast(
713
- figure_data.time_including_gaps,
714
- alt,
715
- self._data.T[:-1, :-1],
713
+ _make_edges(figure_data.time_including_gaps),
714
+ _make_edges(alt),
715
+ self._data.T,
716
716
  cmap=ListedColormap(cbar),
717
717
  vmin=-0.5,
718
718
  vmax=len(cbar) - 0.5,
@@ -752,21 +752,12 @@ class Plot2D(Plot):
752
752
  "zorder": _get_zorder("data"),
753
753
  }
754
754
  image: Any
755
- if getattr(figure_data.file, "cloudnet_file_type", "") == "model":
756
- image = self._ax.pcolor(
757
- figure_data.time_including_gaps,
758
- alt,
759
- self._data.T,
760
- **pcolor_kwargs,
761
- shading="nearest",
762
- )
763
- else:
764
- image = self._ax.pcolorfast(
765
- figure_data.time_including_gaps,
766
- alt,
767
- self._data.T[:-1, :-1],
768
- **pcolor_kwargs,
769
- )
755
+ image = self._ax.pcolorfast(
756
+ _make_edges(figure_data.time_including_gaps),
757
+ _make_edges(alt),
758
+ self._data.T,
759
+ **pcolor_kwargs,
760
+ )
770
761
  cbar = self._init_colorbar(image)
771
762
  cbar.set_label(str(self._plot_meta.clabel), fontsize=13)
772
763
 
@@ -1212,6 +1203,7 @@ def _get_max_gap_in_minutes(figure_data: FigureData) -> float:
1212
1203
  "epsilon-lidar": 75,
1213
1204
  "radar": 5,
1214
1205
  "cpr-simulation": 60,
1206
+ "weather-radar": 20,
1215
1207
  }
1216
1208
  return max_allowed_gap.get(file_type, 10)
1217
1209
 
@@ -1225,6 +1217,14 @@ def _get_zorder(name: str) -> int:
1225
1217
  return zorder.get(name, -1)
1226
1218
 
1227
1219
 
1220
+ def _make_edges(centers: npt.NDArray) -> npt.NDArray:
1221
+ edges = np.empty(len(centers) + 1)
1222
+ edges[0] = centers[0] - (centers[1] - centers[0]) / 2
1223
+ edges[1:-1] = (centers[:-1] + centers[1:]) / 2
1224
+ edges[-1] = centers[-1] + (centers[-1] - centers[-2]) / 2
1225
+ return edges
1226
+
1227
+
1228
1228
  def find_batches_of_ones(array: ndarray) -> list[tuple[int, int]]:
1229
1229
  """Find batches of ones in a binary array."""
1230
1230
  starts = np.where(np.diff(np.hstack(([0], array))) == 1)[0]
@@ -504,7 +504,7 @@ def interpolate_1d(
504
504
 
505
505
  if len(bad_idx) > 0:
506
506
  msg = f"Unable to interpolate for {len(bad_idx)} time steps"
507
- logging.warning(msg)
507
+ logging.debug(msg)
508
508
  interpolated[bad_idx] = ma.masked
509
509
 
510
510
  return interpolated
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
- MINOR = 89
3
- PATCH = 2
2
+ MINOR = 90
3
+ PATCH = 1
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.89.2
3
+ Version: 1.90.1
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -72,6 +72,7 @@ cloudnetpy/instruments/rpg.py
72
72
  cloudnetpy/instruments/rpg_reader.py
73
73
  cloudnetpy/instruments/toa5.py
74
74
  cloudnetpy/instruments/vaisala.py
75
+ cloudnetpy/instruments/weather_radar.py
75
76
  cloudnetpy/instruments/weather_station.py
76
77
  cloudnetpy/instruments/disdrometer/__init__.py
77
78
  cloudnetpy/instruments/disdrometer/common.py
File without changes
File without changes
File without changes
File without changes
File without changes