cloudnetpy 1.95.0__tar.gz → 1.95.2__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 (148) hide show
  1. {cloudnetpy-1.95.0/cloudnetpy.egg-info → cloudnetpy-1.95.2}/PKG-INFO +1 -1
  2. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/arm.py +5 -0
  3. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/model.py +2 -0
  4. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/__init__.py +1 -0
  5. cloudnetpy-1.95.2/cloudnetpy/instruments/arm_utils.py +79 -0
  6. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/instruments.py +7 -0
  7. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/kazr.py +11 -4
  8. cloudnetpy-1.95.2/cloudnetpy/instruments/mwr3c.py +139 -0
  9. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/mwrlos.py +3 -52
  10. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/plotting/plotting.py +1 -0
  11. cloudnetpy-1.95.2/cloudnetpy/products/iwc_weather_radar.py +106 -0
  12. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/version.py +1 -1
  13. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2/cloudnetpy.egg-info}/PKG-INFO +1 -1
  14. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy.egg-info/SOURCES.txt +2 -0
  15. cloudnetpy-1.95.0/cloudnetpy/instruments/arm_utils.py +0 -20
  16. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/LICENSE +0 -0
  17. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/MANIFEST.in +0 -0
  18. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/README.md +0 -0
  19. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/__init__.py +0 -0
  20. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/__init__.py +0 -0
  21. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/atmos_utils.py +0 -0
  22. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/attenuation.py +0 -0
  23. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/attenuations/__init__.py +0 -0
  24. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/attenuations/gas_attenuation.py +0 -0
  25. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/attenuations/liquid_attenuation.py +0 -0
  26. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/attenuations/melting_attenuation.py +0 -0
  27. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/attenuations/rain_attenuation.py +0 -0
  28. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/categorize.py +0 -0
  29. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/classify.py +0 -0
  30. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/containers.py +0 -0
  31. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/disdrometer.py +0 -0
  32. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/droplet.py +0 -0
  33. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/falling.py +0 -0
  34. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/freezing.py +0 -0
  35. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/insects.py +0 -0
  36. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/lidar.py +0 -0
  37. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/melting.py +0 -0
  38. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/mwr.py +0 -0
  39. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/categorize/radar.py +0 -0
  40. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/cli.py +0 -0
  41. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/cloudnetarray.py +0 -0
  42. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/concat_lib.py +0 -0
  43. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/constants.py +0 -0
  44. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/datasource.py +0 -0
  45. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/disdronator/__init__.py +0 -0
  46. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/disdronator/lpm.py +0 -0
  47. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/disdronator/parsivel.py +0 -0
  48. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/disdronator/process.py +0 -0
  49. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/disdronator/rd80.py +0 -0
  50. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/disdronator/utils.py +0 -0
  51. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/exceptions.py +0 -0
  52. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/arm_ceilo.py +0 -0
  53. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/arm_ld.py +0 -0
  54. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/basta.py +0 -0
  55. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/bowtie.py +0 -0
  56. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/ceilo.py +0 -0
  57. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/ceilometer.py +0 -0
  58. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/cl61d.py +0 -0
  59. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/cloudnet_instrument.py +0 -0
  60. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/copernicus.py +0 -0
  61. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/da10.py +0 -0
  62. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/dealias.py +0 -0
  63. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/disdrometer.py +0 -0
  64. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/fd12p.py +0 -0
  65. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/galileo.py +0 -0
  66. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/hatpro.py +0 -0
  67. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/lufft.py +0 -0
  68. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/mira.py +0 -0
  69. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/mmcr.py +0 -0
  70. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/mrr.py +0 -0
  71. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/nc_lidar.py +0 -0
  72. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/nc_radar.py +0 -0
  73. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/pollyxt.py +0 -0
  74. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/radiometrics.py +0 -0
  75. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/rain_e_h3.py +0 -0
  76. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/rpg.py +0 -0
  77. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/rpg_reader.py +0 -0
  78. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/toa5.py +0 -0
  79. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/vaisala.py +0 -0
  80. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/weather_radar.py +0 -0
  81. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/instruments/weather_station.py +0 -0
  82. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/metadata.py +0 -0
  83. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/__init__.py +0 -0
  84. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/file_handler.py +0 -0
  85. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/metadata.py +0 -0
  86. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/model_metadata.py +0 -0
  87. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/plotting/__init__.py +0 -0
  88. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/plotting/plot_meta.py +0 -0
  89. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/plotting/plot_tools.py +0 -0
  90. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/plotting/plotting.py +0 -0
  91. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/products/__init__.py +0 -0
  92. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/products/advance_methods.py +0 -0
  93. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/products/grid_methods.py +0 -0
  94. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/products/model_products.py +0 -0
  95. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/products/observation_products.py +0 -0
  96. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/products/product_resampling.py +0 -0
  97. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/products/tools.py +0 -0
  98. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/statistics/__init__.py +0 -0
  99. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/statistics/statistical_methods.py +0 -0
  100. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/__init__.py +0 -0
  101. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/__init__.py +0 -0
  102. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/conftest.py +0 -0
  103. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/process_cf/__init__.py +0 -0
  104. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +0 -0
  105. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +0 -0
  106. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/__init__.py +0 -0
  107. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +0 -0
  108. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +0 -0
  109. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/__init__.py +0 -0
  110. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +0 -0
  111. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +0 -0
  112. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/unit/__init__.py +0 -0
  113. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/unit/conftest.py +0 -0
  114. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +0 -0
  115. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +0 -0
  116. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/unit/test_model_products.py +0 -0
  117. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +0 -0
  118. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +0 -0
  119. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/unit/test_plotting.py +0 -0
  120. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +0 -0
  121. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/tests/unit/test_tools.py +0 -0
  122. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/model_evaluation/utils.py +0 -0
  123. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/output.py +0 -0
  124. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/plotting/__init__.py +0 -0
  125. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/plotting/plot_meta.py +0 -0
  126. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/__init__.py +0 -0
  127. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/classification.py +0 -0
  128. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/der.py +0 -0
  129. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/drizzle.py +0 -0
  130. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/drizzle_error.py +0 -0
  131. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/drizzle_tools.py +0 -0
  132. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/epsilon_lidar.py +0 -0
  133. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/epsilon_radar.py +0 -0
  134. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/ier.py +0 -0
  135. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/iwc.py +0 -0
  136. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/lwc.py +0 -0
  137. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/mie_lu_tables.nc +0 -0
  138. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/mwr_tools.py +0 -0
  139. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/products/product_tools.py +0 -0
  140. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/py.typed +0 -0
  141. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy/utils.py +0 -0
  142. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy.egg-info/dependency_links.txt +0 -0
  143. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy.egg-info/entry_points.txt +0 -0
  144. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy.egg-info/requires.txt +0 -0
  145. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/cloudnetpy.egg-info/top_level.txt +0 -0
  146. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/docs/source/conf.py +0 -0
  147. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/pyproject.toml +0 -0
  148. {cloudnetpy-1.95.0 → cloudnetpy-1.95.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.95.0
3
+ Version: 1.95.2
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -20,6 +20,7 @@ from cloudnetpy.instruments import (
20
20
  armld2nc,
21
21
  kazr2nc,
22
22
  mmcr2nc,
23
+ mwr3c2nc,
23
24
  mwrlos2nc,
24
25
  )
25
26
 
@@ -63,6 +64,8 @@ class ArmInstrument(NamedTuple):
63
64
  ARM_INSTRUMENTS: Final = (
64
65
  # KAZR2 (approx. 2019 onwards, hourly files)
65
66
  ArmInstrument("radar", "kazr", "kazrcfrge.a1", kazr2nc, multi_file=True),
67
+ # KAZR2 at ENA (Dec 2019 onwards, hourly files)
68
+ ArmInstrument("radar", "kazr", "kazr2cfrge.a1", kazr2nc, multi_file=True),
66
69
  # KAZR corrected moments (approx. 2011-2019)
67
70
  ArmInstrument("radar", "kazr", "kazrcorge.c1", kazr2nc, multi_file=True),
68
71
  # KAZR moments (approx. 2011-2019, e.g. mobile facilities)
@@ -71,6 +74,8 @@ ARM_INSTRUMENTS: Final = (
71
74
  ArmInstrument("radar", "mmcr", "mmcrmom.b1", mmcr2nc, multi_file=False),
72
75
  ArmInstrument("lidar", "cl31", "ceil.b1", armceilo2nc, multi_file=False),
73
76
  ArmInstrument("mwr", "wvr-1100", "mwrlos.b1", mwrlos2nc, multi_file=True),
77
+ # 3-channel MWR (e.g. ENA)
78
+ ArmInstrument("mwr", "mwr-3c", "mwr3c.b1", mwr3c2nc, multi_file=True),
74
79
  ArmInstrument("disdrometer", "parsivel", "ld.b1", armld2nc, multi_file=False),
75
80
  )
76
81
 
@@ -101,6 +101,8 @@ class Model(DataSource):
101
101
  """
102
102
  half_height = height_grid - np.diff(height_grid, prepend=self.alt_site) / 2
103
103
  for key in self.fields_dense + self.fields_atten:
104
+ if key not in self.data_sparse:
105
+ continue
104
106
  array = self.data_sparse[key][:]
105
107
  valid_profiles = _find_number_of_valid_profiles(array)
106
108
  if valid_profiles < 2:
@@ -13,6 +13,7 @@ from .kazr import kazr2nc
13
13
  from .mira import mira2nc
14
14
  from .mmcr import mmcr2nc
15
15
  from .mrr import mrr2nc
16
+ from .mwr3c import mwr3c2nc
16
17
  from .mwrlos import mwrlos2nc
17
18
  from .pollyxt import pollyxt2nc
18
19
  from .radiometrics import radiometrics2nc
@@ -0,0 +1,79 @@
1
+ """Helpers shared by the ARM instrument readers."""
2
+
3
+ import os
4
+ from collections.abc import Sequence
5
+ from os import PathLike
6
+ from pathlib import Path
7
+
8
+ import netCDF4
9
+
10
+ from cloudnetpy.exceptions import ValidTimeStampError
11
+
12
+
13
+ def read_geolocation(nc: netCDF4.Dataset, site_meta: dict) -> dict:
14
+ """Returns site_meta with lat/lon/alt filled from the file if missing."""
15
+ site_meta = {**site_meta}
16
+ for key, names in (
17
+ ("latitude", ("latitude", "lat")),
18
+ ("longitude", ("longitude", "lon")),
19
+ ("altitude", ("altitude", "alt")),
20
+ ):
21
+ if key in site_meta:
22
+ continue
23
+ for name in names:
24
+ if name in nc.variables:
25
+ site_meta[key] = float(nc[name][:])
26
+ break
27
+ return site_meta
28
+
29
+
30
+ def concatenate_files(
31
+ raw_files: str | PathLike | Sequence[str | PathLike],
32
+ temp_dir: str,
33
+ variables: Sequence[str],
34
+ ) -> str | PathLike:
35
+ """Concatenates time-dimensioned `variables` of several ARM files into one.
36
+
37
+ Returns the input file as is if only one file is given.
38
+ """
39
+ files: list[Path]
40
+ if isinstance(raw_files, (str, PathLike)):
41
+ if not os.path.isdir(raw_files):
42
+ return raw_files
43
+ files = [
44
+ Path(raw_files) / f
45
+ for f in os.listdir(raw_files)
46
+ if f.lower().endswith((".cdf", ".nc"))
47
+ ]
48
+ else:
49
+ files = [Path(f) for f in raw_files]
50
+ files = sorted(files, key=lambda f: f.name)
51
+ if len(files) == 1:
52
+ return files[0]
53
+ output_file = Path(temp_dir) / "concatenated.nc"
54
+ with netCDF4.Dataset(output_file, "w") as nc_out:
55
+ nc_out.createDimension("time", None)
56
+ for ind, file in enumerate(files):
57
+ with netCDF4.Dataset(file) as nc_in:
58
+ if ind == 0:
59
+ nc_out.setncatts({k: nc_in.getncattr(k) for k in nc_in.ncattrs()})
60
+ time_units = nc_in["time"].units
61
+ elif nc_in["time"].units != time_units:
62
+ msg = "Inconsistent time units in ARM files"
63
+ raise ValidTimeStampError(msg)
64
+ n_time = len(nc_out.dimensions["time"])
65
+ for key in nc_in.variables:
66
+ if key not in variables and nc_in[key].ndim != 0:
67
+ continue
68
+ if key not in nc_out.variables:
69
+ var = nc_out.createVariable(
70
+ key, nc_in[key].dtype, nc_in[key].dimensions
71
+ )
72
+ var.setncatts(
73
+ {k: nc_in[key].getncattr(k) for k in nc_in[key].ncattrs()}
74
+ )
75
+ if nc_in[key].ndim == 0:
76
+ var[:] = nc_in[key][:]
77
+ if nc_in[key].ndim != 0:
78
+ nc_out[key][n_time:] = nc_in[key][:]
79
+ return output_file
@@ -306,6 +306,13 @@ WVR1100 = Instrument(
306
306
  model="WVR-1100",
307
307
  )
308
308
 
309
+ MWR3C = Instrument(
310
+ manufacturer="Radiometrics",
311
+ domain="mwr",
312
+ category="microwave radiometer",
313
+ model="MWR-3C",
314
+ )
315
+
309
316
  KAZR = Instrument(
310
317
  manufacturer="ProSensing",
311
318
  domain="radar",
@@ -212,8 +212,11 @@ class Kazr(CloudnetInstrument):
212
212
  """Screens LDR using the cross-polar SNR.
213
213
 
214
214
  LDR is removed completely if the cross-polar channel shows signal in
215
- noise gates (broken channel). LDR calculated from reflectivities is
216
- masked in gates where the cross-polar signal is below the noise.
215
+ noise gates (broken channel). Otherwise LDR is masked where the
216
+ cross-polar signal is below the noise: there the reported LDR is only
217
+ an upper bound set by the noise floor, which at low SNR mimics the
218
+ high LDR of insects. Corrected files use the fixed `snrx_limit`, the
219
+ others a limit estimated from the noise in the top range gates.
217
220
  """
218
221
  if "ldr" not in self.data or "SNRx" not in self.data:
219
222
  return
@@ -223,8 +226,12 @@ class Kazr(CloudnetInstrument):
223
226
  if np.any(is_noise) and ma.median(snrx[is_noise]) > snrx_limit:
224
227
  logging.warning("Cross-polar channel unreliable, removing LDR")
225
228
  del self.data["ldr"]
226
- elif self.corrected:
227
- self.data["ldr"].mask_indices(np.where(snrx < snrx_limit))
229
+ else:
230
+ limit: float | np.ndarray = snrx_limit
231
+ if not self.corrected:
232
+ limit = estimate_snr_limit(snrx)[:, np.newaxis]
233
+ is_below = ma.filled(snrx < limit, fill_value=True)
234
+ self.data["ldr"].mask_indices(np.where(is_below))
228
235
  del self.data["SNRx"]
229
236
 
230
237
  def correct_ldr_leakage(self, min_ldr: float = -30, n_sigma: float = 3) -> None:
@@ -0,0 +1,139 @@
1
+ """Module for reading ARM 3-channel microwave radiometer (mwr3c) data."""
2
+
3
+ import datetime
4
+ import tempfile
5
+ from collections.abc import Sequence
6
+ from os import PathLike
7
+ from uuid import UUID
8
+
9
+ import numpy as np
10
+ from numpy import ma
11
+
12
+ from cloudnetpy import output, utils
13
+ from cloudnetpy.datasource import DataSource
14
+ from cloudnetpy.exceptions import ValidTimeStampError
15
+ from cloudnetpy.instruments.arm_utils import concatenate_files, read_geolocation
16
+ from cloudnetpy.instruments.cloudnet_instrument import CloudnetInstrument
17
+ from cloudnetpy.instruments.instruments import MWR3C
18
+ from cloudnetpy.metadata import COMMON_ATTRIBUTES
19
+
20
+ CM_TO_KG_M2 = 10
21
+ MISSING_VALUE = -9999
22
+ MAX_OFF_ZENITH = 1.0 # deg
23
+ RAW_VARIABLES = (
24
+ "time",
25
+ "lwp",
26
+ "pwv",
27
+ "elevation",
28
+ "rain_flag",
29
+ "qc_tbsky23",
30
+ "qc_tbsky31",
31
+ )
32
+
33
+
34
+ def mwr3c2nc(
35
+ raw_files: str | PathLike | Sequence[str | PathLike],
36
+ output_file: str | PathLike,
37
+ site_meta: dict,
38
+ uuid: str | UUID | None = None,
39
+ date: str | datetime.date | None = None,
40
+ ) -> UUID:
41
+ """Converts ARM 3-channel microwave radiometer retrievals (mwr3c) into
42
+ Cloudnet Level 1b netCDF file.
43
+
44
+ Args:
45
+ raw_files: ARM `mwr3c` netCDF file (e.g.
46
+ `enamwr3cC1.b1.20240315.000004.nc`), a sequence of files, or a
47
+ folder containing the files of one day.
48
+ output_file: Output filename.
49
+ site_meta: Dictionary containing information about the site. Required key
50
+ value pair is `name`. Optional are `latitude`, `longitude` and
51
+ `altitude` (taken from the raw file if missing).
52
+ uuid: Set specific UUID for the file.
53
+ date: Expected date as YYYY-MM-DD of all profiles in the file.
54
+
55
+ Returns:
56
+ UUID of the generated file.
57
+
58
+ Raises:
59
+ ValidTimeStampError: No valid timestamps found.
60
+
61
+ Examples:
62
+ >>> from cloudnetpy.instruments import mwr3c2nc
63
+ >>> site_meta = {'name': 'Graciosa'}
64
+ >>> mwr3c2nc('enamwr3cC1.b1.20240315.000004.nc', 'mwr.nc', site_meta)
65
+
66
+ """
67
+ if isinstance(date, str):
68
+ date = datetime.date.fromisoformat(date)
69
+ uuid = utils.get_uuid(uuid)
70
+ with tempfile.TemporaryDirectory() as temp_dir:
71
+ raw_file = concatenate_files(raw_files, temp_dir, RAW_VARIABLES)
72
+ with Mwr3c(raw_file, site_meta) as mwr:
73
+ if date is not None:
74
+ mwr.check_date(date)
75
+ mwr.init_data()
76
+ mwr.sort_timestamps()
77
+ mwr.remove_duplicate_timestamps()
78
+ mwr.screen_invalid_values()
79
+ mwr.add_zenith_angle()
80
+ mwr.add_site_geolocation()
81
+ attributes = output.add_time_attribute(ATTRIBUTES, mwr.date)
82
+ output.update_attributes(mwr.data, attributes)
83
+ output.save_level1b(mwr, output_file, uuid)
84
+ return uuid
85
+
86
+
87
+ class Mwr3c(DataSource, CloudnetInstrument):
88
+ """Class for ARM mwr3c data.
89
+
90
+ Args:
91
+ full_path: Filename of a daily ARM mwr3c netCDF file.
92
+ site_meta: Site properties in a dictionary. Required keys are: `name`.
93
+
94
+ """
95
+
96
+ def __init__(self, full_path: str | PathLike, site_meta: dict) -> None:
97
+ super().__init__(full_path)
98
+ self.site_meta = {**site_meta}
99
+ self.instrument = MWR3C
100
+ self.date = utils.get_epoch(self.dataset["time"].units).date()
101
+ serial_number = getattr(self.dataset, "serial_number", None)
102
+ self.serial_number = serial_number.strip() if serial_number else None
103
+ self._add_geolocation_from_file()
104
+
105
+ def init_data(self) -> None:
106
+ self.append_data(np.array(self.time), "time", dtype="f8")
107
+ for key, name, factor in (("lwp", "lwp", 1), ("pwv", "iwv", CM_TO_KG_M2)):
108
+ data = ma.masked_invalid(self.getvar(key))
109
+ data = ma.masked_equal(data, MISSING_VALUE) * factor
110
+ self.append_data(data, name)
111
+
112
+ def check_date(self, date: datetime.date) -> None:
113
+ if self.date != date:
114
+ raise ValidTimeStampError
115
+
116
+ def screen_invalid_values(self) -> None:
117
+ """Masks rainy, off-zenith and flagged retrievals."""
118
+ is_invalid = np.zeros(len(self.data["time"].data), dtype=bool)
119
+ if "rain_flag" in self.dataset.variables:
120
+ is_invalid |= ma.filled(self.getvar("rain_flag"), 0) > 0
121
+ if "elevation" in self.dataset.variables:
122
+ elevation = ma.filled(self.getvar("elevation"), MISSING_VALUE)
123
+ is_invalid |= np.abs(elevation - 90) > MAX_OFF_ZENITH
124
+ for key in ("qc_tbsky23", "qc_tbsky31"):
125
+ if key in self.dataset.variables:
126
+ is_invalid |= ma.filled(self.getvar(key), 0) != 0
127
+ for key in ("lwp", "iwv"):
128
+ self.data[key].data[is_invalid] = ma.masked
129
+
130
+ def add_zenith_angle(self) -> None:
131
+ self.append_data(0.0, "zenith_angle")
132
+
133
+ def _add_geolocation_from_file(self) -> None:
134
+ self.site_meta = read_geolocation(self.dataset, self.site_meta)
135
+
136
+
137
+ ATTRIBUTES = {
138
+ "zenith_angle": COMMON_ATTRIBUTES["zenith_angle"]._replace(dimensions=None),
139
+ }
@@ -1,26 +1,24 @@
1
1
  """Module for reading ARM microwave radiometer line-of-sight (mwrlos) data."""
2
2
 
3
3
  import datetime
4
- import os
5
4
  import tempfile
6
5
  from collections.abc import Sequence
7
6
  from os import PathLike
8
- from pathlib import Path
9
7
  from uuid import UUID
10
8
 
11
- import netCDF4
12
9
  import numpy as np
13
10
  from numpy import ma
14
11
 
15
12
  from cloudnetpy import output, utils
16
13
  from cloudnetpy.datasource import DataSource
17
14
  from cloudnetpy.exceptions import ValidTimeStampError
18
- from cloudnetpy.instruments.arm_utils import read_geolocation
15
+ from cloudnetpy.instruments.arm_utils import concatenate_files, read_geolocation
19
16
  from cloudnetpy.instruments.cloudnet_instrument import CloudnetInstrument
20
17
  from cloudnetpy.instruments.instruments import WVR1100
21
18
  from cloudnetpy.metadata import COMMON_ATTRIBUTES
22
19
 
23
20
  CM_TO_KG_M2 = 10
21
+ RAW_VARIABLES = ("time", "liq", "vap", "qc_liq", "qc_vap", "wet_window")
24
22
 
25
23
 
26
24
  def mwrlos2nc(
@@ -60,7 +58,7 @@ def mwrlos2nc(
60
58
  date = datetime.date.fromisoformat(date)
61
59
  uuid = utils.get_uuid(uuid)
62
60
  with tempfile.TemporaryDirectory() as temp_dir:
63
- raw_file = _concatenate(raw_files, temp_dir)
61
+ raw_file = concatenate_files(raw_files, temp_dir, RAW_VARIABLES)
64
62
  with MwrLos(raw_file, site_meta) as mwr:
65
63
  if date is not None:
66
64
  mwr.check_date(date)
@@ -76,53 +74,6 @@ def mwrlos2nc(
76
74
  return uuid
77
75
 
78
76
 
79
- def _concatenate(
80
- raw_files: str | PathLike | Sequence[str | PathLike], temp_dir: str
81
- ) -> str | PathLike:
82
- files: list[Path]
83
- if isinstance(raw_files, (str, PathLike)):
84
- if not os.path.isdir(raw_files):
85
- return raw_files
86
- files = [
87
- Path(raw_files) / f
88
- for f in os.listdir(raw_files)
89
- if f.lower().endswith((".cdf", ".nc"))
90
- ]
91
- else:
92
- files = [Path(f) for f in raw_files]
93
- files = sorted(files, key=lambda f: f.name)
94
- if len(files) == 1:
95
- return files[0]
96
- output_file = Path(temp_dir) / "mwrlos.nc"
97
- variables = ("time", "liq", "vap", "qc_liq", "qc_vap", "wet_window")
98
- with netCDF4.Dataset(output_file, "w") as nc_out:
99
- nc_out.createDimension("time", None)
100
- for ind, file in enumerate(files):
101
- with netCDF4.Dataset(file) as nc_in:
102
- if ind == 0:
103
- nc_out.setncatts({k: nc_in.getncattr(k) for k in nc_in.ncattrs()})
104
- time_units = nc_in["time"].units
105
- elif nc_in["time"].units != time_units:
106
- msg = "Inconsistent time units in mwrlos files"
107
- raise ValidTimeStampError(msg)
108
- n_time = len(nc_out.dimensions["time"])
109
- for key in nc_in.variables:
110
- if key not in variables and nc_in[key].ndim != 0:
111
- continue
112
- if key not in nc_out.variables:
113
- var = nc_out.createVariable(
114
- key, nc_in[key].dtype, nc_in[key].dimensions
115
- )
116
- var.setncatts(
117
- {k: nc_in[key].getncattr(k) for k in nc_in[key].ncattrs()}
118
- )
119
- if nc_in[key].ndim == 0:
120
- var[:] = nc_in[key][:]
121
- if nc_in[key].ndim != 0:
122
- nc_out[key][n_time:] = nc_in[key][:]
123
- return output_file
124
-
125
-
126
77
  class MwrLos(DataSource, CloudnetInstrument):
127
78
  """Class for ARM mwrlos data.
128
79
 
@@ -1293,6 +1293,7 @@ def _get_max_gap_in_minutes(figure_data: FigureData) -> float:
1293
1293
  "radar": 5,
1294
1294
  "cpr-simulation": 60,
1295
1295
  "weather-radar": 20,
1296
+ "iwc-weather-radar": 20,
1296
1297
  }
1297
1298
  return max_allowed_gap.get(file_type, 10)
1298
1299
 
@@ -0,0 +1,106 @@
1
+ from os import PathLike
2
+ from uuid import UUID
3
+
4
+ import numpy as np
5
+ from numpy import ma
6
+
7
+ from cloudnetpy import output, utils
8
+ from cloudnetpy.categorize.disdrometer import DataSource
9
+ from cloudnetpy.categorize.model import Model
10
+ from cloudnetpy.cloudnetarray import CloudnetArray
11
+ from cloudnetpy.constants import T0
12
+ from cloudnetpy.metadata import COMMON_ATTRIBUTES, MetaData
13
+
14
+
15
+ def generate_iwc_from_weather_radar(
16
+ weather_radar_file: str | PathLike,
17
+ model_file: str | PathLike,
18
+ output_file: str | PathLike,
19
+ uuid: str | UUID | None = None,
20
+ ) -> UUID:
21
+ uuid = utils.get_uuid(uuid)
22
+ radar = WrRadar(weather_radar_file)
23
+ if radar.altitude is None:
24
+ msg = "Altitude is missing"
25
+ raise ValueError(msg)
26
+ if radar.height is None or radar.height_agl is None:
27
+ msg = "Height is missing"
28
+ raise ValueError(msg)
29
+ model = Model(model_file, radar.altitude)
30
+ model.interpolate_to_common_height()
31
+ model.interpolate_to_grid(radar.time, radar.height)
32
+
33
+ height_agl = radar.height_agl
34
+ z = radar.data["Z"][:]
35
+ v = radar.data["v"][:]
36
+ rho_hv = radar.data["rho_hv"][:]
37
+ temp = model.data_dense["temperature"]
38
+
39
+ is_noise = (height_agl < 3000) & (rho_hv < 0.8) & (z < -20)
40
+ is_ice = (z > -30) & (np.abs(v) < 2) & (temp < T0)
41
+ is_rain = (z > 10) & (v < 0) & (temp > T0 - 10)
42
+
43
+ output_data = {
44
+ "time": CloudnetArray(radar.time, "time"),
45
+ "height": CloudnetArray(radar.height, "height"),
46
+ "radar_frequency": CloudnetArray(radar.radar_frequency, "radar_frequency"),
47
+ }
48
+
49
+ z_rain = ma.masked_where(is_noise | ~is_rain, z)
50
+ rainfall_rate = (utils.db2lin(z_rain) / 200) ** 0.625 # Marshall-Palmer
51
+ output_data["rainfall_rate"] = CloudnetArray(
52
+ rainfall_rate * 1e-3 / 3600, "rainfall_rate"
53
+ )
54
+
55
+ z_ice = ma.masked_where(is_noise | is_rain | ~is_ice, z)
56
+ iwc = ma.power(10, 0.060 * z_ice - 0.0197 * temp - 1.70) # Hogan et al. (2006)
57
+ output_data["iwc"] = CloudnetArray(1000 * iwc, "iwc")
58
+
59
+ date = radar.get_date()
60
+ attributes = output.add_time_attribute(WEATHER_RADAR_RET_ATTRIBUTES, date)
61
+ output.update_attributes(output_data, attributes)
62
+
63
+ dimensions = {"time": len(radar.time), "height": len(radar.height)}
64
+ with output.init_file(output_file, dimensions, output_data, uuid) as nc:
65
+ nc.cloudnet_file_type = "iwc-weather-radar"
66
+ vars_from_source = (
67
+ "altitude",
68
+ "latitude",
69
+ "longitude",
70
+ )
71
+ output.copy_variables(radar.dataset, nc, vars_from_source)
72
+ output.copy_global(radar.dataset, nc, ("year", "month", "day", "location"))
73
+ nc.title = f"Ice water content (weather radar) retrieval from {radar.location}"
74
+ nc.source = "\n".join(sorted([radar.source, model.source]))
75
+ nc.source_file_uuids = output.get_source_uuids([radar, model])
76
+ output.merge_history(nc, nc.cloudnet_file_type, radar, (model,))
77
+
78
+ return uuid
79
+
80
+
81
+ class WrRadar(DataSource):
82
+ def __init__(self, full_path: str | PathLike) -> None:
83
+ super().__init__(full_path, radar=True)
84
+ self.radar_frequency = float(self.getvar("radar_frequency"))
85
+ self.location = getattr(self.dataset, "location", "")
86
+ self._init_data()
87
+
88
+ def _init_data(self) -> None:
89
+ self.append_data(self.getvar("Zh"), "Z", units="dBZ")
90
+ self.append_data(self.getvar("v"), "v")
91
+ self.append_data(self.getvar("rho_hv"), "rho_hv")
92
+
93
+
94
+ WEATHER_RADAR_RET_ATTRIBUTES = {
95
+ "height": COMMON_ATTRIBUTES["height"]._replace(dimensions=("height",)),
96
+ "iwc": MetaData(
97
+ long_name="Ice water content",
98
+ units="kg m-3",
99
+ dimensions=("time", "height"),
100
+ ),
101
+ "rainfall_rate": MetaData(
102
+ long_name="Rainfall rate",
103
+ units="m s-1",
104
+ dimensions=("time", "height"),
105
+ ),
106
+ }
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
2
  MINOR = 95
3
- PATCH = 0
3
+ PATCH = 2
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.95.0
3
+ Version: 1.95.2
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -71,6 +71,7 @@ cloudnetpy/instruments/lufft.py
71
71
  cloudnetpy/instruments/mira.py
72
72
  cloudnetpy/instruments/mmcr.py
73
73
  cloudnetpy/instruments/mrr.py
74
+ cloudnetpy/instruments/mwr3c.py
74
75
  cloudnetpy/instruments/mwrlos.py
75
76
  cloudnetpy/instruments/nc_lidar.py
76
77
  cloudnetpy/instruments/nc_radar.py
@@ -136,6 +137,7 @@ cloudnetpy/products/epsilon_lidar.py
136
137
  cloudnetpy/products/epsilon_radar.py
137
138
  cloudnetpy/products/ier.py
138
139
  cloudnetpy/products/iwc.py
140
+ cloudnetpy/products/iwc_weather_radar.py
139
141
  cloudnetpy/products/lwc.py
140
142
  cloudnetpy/products/mie_lu_tables.nc
141
143
  cloudnetpy/products/mwr_tools.py
@@ -1,20 +0,0 @@
1
- """Helpers shared by the ARM instrument readers."""
2
-
3
- import netCDF4
4
-
5
-
6
- def read_geolocation(nc: netCDF4.Dataset, site_meta: dict) -> dict:
7
- """Returns site_meta with lat/lon/alt filled from the file if missing."""
8
- site_meta = {**site_meta}
9
- for key, names in (
10
- ("latitude", ("latitude", "lat")),
11
- ("longitude", ("longitude", "lon")),
12
- ("altitude", ("altitude", "alt")),
13
- ):
14
- if key in site_meta:
15
- continue
16
- for name in names:
17
- if name in nc.variables:
18
- site_meta[key] = float(nc[name][:])
19
- break
20
- return site_meta
File without changes
File without changes
File without changes
File without changes
File without changes