cloudnetpy 1.80.7__py3-none-any.whl → 1.81.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. cloudnetpy/categorize/__init__.py +1 -1
  2. cloudnetpy/categorize/atmos_utils.py +31 -27
  3. cloudnetpy/categorize/attenuations/__init__.py +4 -4
  4. cloudnetpy/categorize/attenuations/liquid_attenuation.py +7 -5
  5. cloudnetpy/categorize/attenuations/melting_attenuation.py +3 -3
  6. cloudnetpy/categorize/attenuations/rain_attenuation.py +4 -4
  7. cloudnetpy/categorize/categorize.py +25 -11
  8. cloudnetpy/categorize/classify.py +9 -8
  9. cloudnetpy/categorize/containers.py +13 -10
  10. cloudnetpy/categorize/disdrometer.py +5 -3
  11. cloudnetpy/categorize/droplet.py +12 -9
  12. cloudnetpy/categorize/falling.py +9 -8
  13. cloudnetpy/categorize/freezing.py +10 -7
  14. cloudnetpy/categorize/insects.py +18 -17
  15. cloudnetpy/categorize/lidar.py +7 -3
  16. cloudnetpy/categorize/melting.py +16 -15
  17. cloudnetpy/categorize/model.py +17 -10
  18. cloudnetpy/categorize/mwr.py +5 -3
  19. cloudnetpy/categorize/radar.py +15 -13
  20. cloudnetpy/cli.py +10 -8
  21. cloudnetpy/cloudnetarray.py +8 -7
  22. cloudnetpy/concat_lib.py +29 -20
  23. cloudnetpy/datasource.py +26 -21
  24. cloudnetpy/exceptions.py +12 -10
  25. cloudnetpy/instruments/basta.py +19 -9
  26. cloudnetpy/instruments/bowtie.py +18 -11
  27. cloudnetpy/instruments/ceilo.py +22 -10
  28. cloudnetpy/instruments/ceilometer.py +33 -34
  29. cloudnetpy/instruments/cl61d.py +5 -3
  30. cloudnetpy/instruments/cloudnet_instrument.py +7 -7
  31. cloudnetpy/instruments/copernicus.py +16 -7
  32. cloudnetpy/instruments/disdrometer/common.py +5 -4
  33. cloudnetpy/instruments/disdrometer/parsivel.py +14 -9
  34. cloudnetpy/instruments/disdrometer/thies.py +11 -7
  35. cloudnetpy/instruments/fd12p.py +7 -6
  36. cloudnetpy/instruments/galileo.py +16 -7
  37. cloudnetpy/instruments/hatpro.py +33 -24
  38. cloudnetpy/instruments/lufft.py +6 -4
  39. cloudnetpy/instruments/mira.py +33 -19
  40. cloudnetpy/instruments/mrr.py +12 -12
  41. cloudnetpy/instruments/nc_lidar.py +1 -1
  42. cloudnetpy/instruments/nc_radar.py +8 -8
  43. cloudnetpy/instruments/pollyxt.py +19 -12
  44. cloudnetpy/instruments/radiometrics.py +17 -10
  45. cloudnetpy/instruments/rain_e_h3.py +9 -5
  46. cloudnetpy/instruments/rpg.py +32 -21
  47. cloudnetpy/instruments/rpg_reader.py +15 -12
  48. cloudnetpy/instruments/vaisala.py +32 -24
  49. cloudnetpy/instruments/weather_station.py +22 -19
  50. cloudnetpy/model_evaluation/file_handler.py +27 -29
  51. cloudnetpy/model_evaluation/plotting/plot_tools.py +7 -5
  52. cloudnetpy/model_evaluation/plotting/plotting.py +41 -32
  53. cloudnetpy/model_evaluation/products/advance_methods.py +38 -34
  54. cloudnetpy/model_evaluation/products/grid_methods.py +10 -9
  55. cloudnetpy/model_evaluation/products/model_products.py +15 -9
  56. cloudnetpy/model_evaluation/products/observation_products.py +12 -10
  57. cloudnetpy/model_evaluation/products/product_resampling.py +11 -7
  58. cloudnetpy/model_evaluation/products/tools.py +18 -14
  59. cloudnetpy/model_evaluation/statistics/statistical_methods.py +6 -5
  60. cloudnetpy/model_evaluation/tests/unit/test_plotting.py +18 -25
  61. cloudnetpy/model_evaluation/utils.py +3 -3
  62. cloudnetpy/output.py +15 -32
  63. cloudnetpy/plotting/plotting.py +23 -13
  64. cloudnetpy/products/classification.py +15 -9
  65. cloudnetpy/products/der.py +24 -19
  66. cloudnetpy/products/drizzle.py +21 -13
  67. cloudnetpy/products/drizzle_error.py +8 -7
  68. cloudnetpy/products/drizzle_tools.py +27 -23
  69. cloudnetpy/products/epsilon.py +6 -5
  70. cloudnetpy/products/ier.py +11 -5
  71. cloudnetpy/products/iwc.py +18 -9
  72. cloudnetpy/products/lwc.py +41 -31
  73. cloudnetpy/products/mwr_tools.py +30 -19
  74. cloudnetpy/products/product_tools.py +23 -19
  75. cloudnetpy/utils.py +84 -98
  76. cloudnetpy/version.py +2 -2
  77. {cloudnetpy-1.80.7.dist-info → cloudnetpy-1.81.0.dist-info}/METADATA +2 -1
  78. cloudnetpy-1.81.0.dist-info/RECORD +126 -0
  79. cloudnetpy-1.80.7.dist-info/RECORD +0 -126
  80. {cloudnetpy-1.80.7.dist-info → cloudnetpy-1.81.0.dist-info}/WHEEL +0 -0
  81. {cloudnetpy-1.80.7.dist-info → cloudnetpy-1.81.0.dist-info}/entry_points.txt +0 -0
  82. {cloudnetpy-1.80.7.dist-info → cloudnetpy-1.81.0.dist-info}/licenses/LICENSE +0 -0
  83. {cloudnetpy-1.80.7.dist-info → cloudnetpy-1.81.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,10 @@
1
1
  """Module for creating classification file."""
2
2
 
3
+ from os import PathLike
4
+ from uuid import UUID
5
+
3
6
  import numpy as np
7
+ import numpy.typing as npt
4
8
  from numpy import ma
5
9
 
6
10
  from cloudnetpy import output, utils
@@ -11,10 +15,10 @@ from cloudnetpy.products.product_tools import CategorizeBits
11
15
 
12
16
 
13
17
  def generate_classification(
14
- categorize_file: str,
15
- output_file: str,
16
- uuid: str | None = None,
17
- ) -> str:
18
+ categorize_file: str | PathLike,
19
+ output_file: str | PathLike,
20
+ uuid: str | UUID | None = None,
21
+ ) -> UUID:
18
22
  """Generates Cloudnet classification product.
19
23
 
20
24
  This function reads the initial classification masks from a
@@ -35,6 +39,7 @@ def generate_classification(
35
39
  >>> generate_classification('categorize.nc', 'classification.nc')
36
40
 
37
41
  """
42
+ uuid = utils.get_uuid(uuid)
38
43
  with DataSource(categorize_file) as product_container:
39
44
  categorize_bits = CategorizeBits(categorize_file)
40
45
  classification = _get_target_classification(categorize_bits)
@@ -58,12 +63,13 @@ def generate_classification(
58
63
  file_type = "classification"
59
64
  if "liquid_prob" in product_container.dataset.variables:
60
65
  file_type += "-voodoo"
61
- return output.save_product_file(
66
+ output.save_product_file(
62
67
  file_type,
63
68
  product_container,
64
69
  output_file,
65
70
  uuid,
66
71
  )
72
+ return uuid
67
73
 
68
74
 
69
75
  def _get_target_classification(
@@ -86,7 +92,7 @@ def _get_target_classification(
86
92
  return classification
87
93
 
88
94
 
89
- def _get_detection_status(categorize_bits: CategorizeBits) -> np.ndarray:
95
+ def _get_detection_status(categorize_bits: CategorizeBits) -> npt.NDArray:
90
96
  bits = categorize_bits.quality_bits
91
97
 
92
98
  is_attenuated = (
@@ -113,9 +119,9 @@ def _get_detection_status(categorize_bits: CategorizeBits) -> np.ndarray:
113
119
 
114
120
 
115
121
  def _get_cloud_base_and_top_heights(
116
- classification: np.ndarray,
122
+ classification: npt.NDArray,
117
123
  product_container: DataSource,
118
- ) -> tuple[np.ndarray, np.ndarray]:
124
+ ) -> tuple[npt.NDArray, npt.NDArray]:
119
125
  height = product_container.getvar("height")
120
126
  cloud_mask = _find_cloud_mask(classification)
121
127
  if not cloud_mask.any():
@@ -128,7 +134,7 @@ def _get_cloud_base_and_top_heights(
128
134
  return lowest_bases, highest_tops
129
135
 
130
136
 
131
- def _find_cloud_mask(classification: np.ndarray) -> np.ndarray:
137
+ def _find_cloud_mask(classification: npt.NDArray) -> npt.NDArray:
132
138
  cloud_mask = np.zeros(classification.shape, dtype=int)
133
139
  for value in [1, 3, 4, 5]:
134
140
  cloud_mask[classification == value] = 1
@@ -2,9 +2,12 @@
2
2
  using the Frisch et al. 2002 method.
3
3
  """
4
4
 
5
+ from os import PathLike
5
6
  from typing import NamedTuple
7
+ from uuid import UUID
6
8
 
7
9
  import numpy as np
10
+ import numpy.typing as npt
8
11
  from numpy import ma
9
12
 
10
13
  from cloudnetpy import output, utils
@@ -29,11 +32,11 @@ class Parameters(NamedTuple):
29
32
 
30
33
 
31
34
  def generate_der(
32
- categorize_file: str,
33
- output_file: str,
34
- uuid: str | None = None,
35
+ categorize_file: str | PathLike,
36
+ output_file: str | PathLike,
37
+ uuid: str | UUID | None = None,
35
38
  parameters: Parameters | None = None,
36
- ) -> str:
39
+ ) -> UUID:
37
40
  """Generates Cloudnet effective radius of liquid water droplets
38
41
  product according to Frisch et al. 2002.
39
42
 
@@ -69,16 +72,16 @@ def generate_der(
69
72
  https://doi.org/10.1175/1520-0426(2002)019%3C0835:TROSCD%3E2.0.CO;2
70
73
 
71
74
  """
72
- der_source = DerSource(categorize_file, parameters)
73
- droplet_classification = DropletClassification(categorize_file)
74
- der_source.append_der()
75
- der_source.append_retrieval_status(droplet_classification)
76
- date = der_source.get_date()
77
- attributes = output.add_time_attribute(REFF_ATTRIBUTES, date)
78
- attributes = _add_der_error_comment(attributes, der_source)
79
- output.update_attributes(der_source.data, attributes)
80
- uuid = output.save_product_file("der", der_source, output_file, uuid)
81
- der_source.close()
75
+ uuid = utils.get_uuid(uuid)
76
+ with DerSource(categorize_file, parameters) as der_source:
77
+ droplet_classification = DropletClassification(categorize_file)
78
+ der_source.append_der()
79
+ der_source.append_retrieval_status(droplet_classification)
80
+ date = der_source.get_date()
81
+ attributes = output.add_time_attribute(REFF_ATTRIBUTES, date)
82
+ attributes = _add_der_error_comment(attributes, der_source)
83
+ output.update_attributes(der_source.data, attributes)
84
+ output.save_product_file("der", der_source, output_file, uuid)
82
85
  return uuid
83
86
 
84
87
 
@@ -87,19 +90,19 @@ class DropletClassification(ProductClassification):
87
90
  Child of ProductClassification().
88
91
  """
89
92
 
90
- def __init__(self, categorize_file: str):
93
+ def __init__(self, categorize_file: str | PathLike) -> None:
91
94
  super().__init__(categorize_file)
92
95
  self.is_mixed = self._find_mixed()
93
96
  self.is_droplet = self._find_droplet()
94
97
  self.is_ice = self._find_ice()
95
98
 
96
- def _find_droplet(self) -> np.ndarray:
99
+ def _find_droplet(self) -> npt.NDArray:
97
100
  return self.category_bits.droplet
98
101
 
99
- def _find_mixed(self) -> np.ndarray:
102
+ def _find_mixed(self) -> npt.NDArray:
100
103
  return self.category_bits.falling & self.category_bits.droplet
101
104
 
102
- def _find_ice(self) -> np.ndarray:
105
+ def _find_ice(self) -> npt.NDArray:
103
106
  return (
104
107
  self.category_bits.falling
105
108
  & self.category_bits.freezing
@@ -112,7 +115,9 @@ class DropletClassification(ProductClassification):
112
115
  class DerSource(DataSource):
113
116
  """Data container for effective radius calculations."""
114
117
 
115
- def __init__(self, categorize_file: str, parameters: Parameters | None = None):
118
+ def __init__(
119
+ self, categorize_file: str | PathLike, parameters: Parameters | None = None
120
+ ) -> None:
116
121
  super().__init__(categorize_file)
117
122
  if "lwp" not in self.dataset.variables:
118
123
  msg = "Liquid water path missing from the categorize file."
@@ -1,6 +1,10 @@
1
1
  """Module for creating Cloudnet drizzle product."""
2
2
 
3
+ from os import PathLike
4
+ from uuid import UUID
5
+
3
6
  import numpy as np
7
+ import numpy.typing as npt
4
8
  from numpy import ma
5
9
  from scipy.special import gamma
6
10
 
@@ -16,10 +20,10 @@ from cloudnetpy.products.drizzle_tools import (
16
20
 
17
21
 
18
22
  def generate_drizzle(
19
- categorize_file: str,
20
- output_file: str,
21
- uuid: str | None = None,
22
- ) -> str:
23
+ categorize_file: str | PathLike,
24
+ output_file: str | PathLike,
25
+ uuid: str | UUID | None = None,
26
+ ) -> UUID:
23
27
  """Generates Cloudnet drizzle product.
24
28
 
25
29
  This function calculates different drizzle properties from
@@ -43,6 +47,7 @@ def generate_drizzle(
43
47
  J. Appl. Meteor., 44, 14–27, https://doi.org/10.1175/JAM-2181.1
44
48
 
45
49
  """
50
+ uuid = utils.get_uuid(uuid)
46
51
  with DrizzleSource(categorize_file) as drizzle_source:
47
52
  drizzle_class = DrizzleClassification(categorize_file)
48
53
  spectral_width = SpectralWidth(categorize_file)
@@ -61,7 +66,8 @@ def generate_drizzle(
61
66
  date = drizzle_source.get_date()
62
67
  attributes = output.add_time_attribute(DRIZZLE_ATTRIBUTES, date)
63
68
  output.update_attributes(drizzle_source.data, attributes)
64
- return output.save_product_file("drizzle", drizzle_source, output_file, uuid)
69
+ output.save_product_file("drizzle", drizzle_source, output_file, uuid)
70
+ return uuid
65
71
 
66
72
 
67
73
  class DrizzleProducts:
@@ -77,7 +83,9 @@ class DrizzleProducts:
77
83
 
78
84
  """
79
85
 
80
- def __init__(self, drizzle_source: DrizzleSource, drizzle_solver: DrizzleSolver):
86
+ def __init__(
87
+ self, drizzle_source: DrizzleSource, drizzle_solver: DrizzleSolver
88
+ ) -> None:
81
89
  self._data = drizzle_source
82
90
  self._params = drizzle_solver.params
83
91
  self._ind_drizzle, self._ind_lut = self._find_indices()
@@ -106,13 +114,13 @@ class DrizzleProducts:
106
114
  "v_air": v_air,
107
115
  }
108
116
 
109
- def _calc_density(self) -> np.ndarray:
117
+ def _calc_density(self) -> npt.NDArray:
110
118
  """Calculates drizzle number density (m-3)."""
111
119
  a = self._data.z * 3.67**6
112
120
  b = self._params["Do"] ** 6
113
121
  return np.divide(a, b, out=np.zeros_like(a), where=b != 0)
114
122
 
115
- def _calc_lwc(self) -> np.ndarray:
123
+ def _calc_lwc(self) -> npt.NDArray:
116
124
  """Calculates drizzle liquid water content (kg m-3)."""
117
125
  rho_water = 1000
118
126
  dia, mu, s = (self._params.get(key) for key in ("Do", "mu", "S"))
@@ -122,7 +130,7 @@ class DrizzleProducts:
122
130
  gamma_ratio = gamma(4 + mu) / gamma(3 + mu) / (3.67 + mu)
123
131
  return rho_water / 3 * self._data.beta * s * dia * gamma_ratio
124
132
 
125
- def _calc_lwf(self, lwc_in) -> np.ndarray:
133
+ def _calc_lwf(self, lwc_in: npt.NDArray) -> npt.NDArray:
126
134
  """Calculates drizzle liquid water flux."""
127
135
  flux = ma.copy(lwc_in)
128
136
  flux[self._ind_drizzle] *= (
@@ -131,13 +139,13 @@ class DrizzleProducts:
131
139
  )
132
140
  return flux
133
141
 
134
- def _calc_fall_velocity(self) -> np.ndarray:
142
+ def _calc_fall_velocity(self) -> npt.NDArray:
135
143
  """Calculates drizzle droplet fall velocity (m s-1)."""
136
144
  velocity = np.zeros_like(self._params["Do"])
137
145
  velocity[self._ind_drizzle] = -self._data.mie["v"][self._ind_lut]
138
146
  return velocity
139
147
 
140
- def _calc_v_air(self, droplet_velocity) -> np.ndarray:
148
+ def _calc_v_air(self, droplet_velocity: npt.NDArray) -> npt.NDArray:
141
149
  """Calculates vertical air velocity."""
142
150
  velocity = -np.copy(droplet_velocity)
143
151
  velocity[self._ind_drizzle] += self._data.v[self._ind_drizzle]
@@ -156,9 +164,9 @@ class RetrievalStatus:
156
164
  status information.
157
165
  """
158
166
 
159
- def __init__(self, drizzle_class: DrizzleClassification):
167
+ def __init__(self, drizzle_class: DrizzleClassification) -> None:
160
168
  self.drizzle_class = drizzle_class
161
- self.retrieval_status: np.ndarray = np.array([])
169
+ self.retrieval_status: npt.NDArray = np.array([])
162
170
  self._get_retrieval_status()
163
171
 
164
172
  def _get_retrieval_status(self) -> None:
@@ -1,4 +1,5 @@
1
1
  import numpy as np
2
+ import numpy.typing as npt
2
3
  from numpy import ma
3
4
 
4
5
  from cloudnetpy import utils
@@ -33,7 +34,7 @@ def get_drizzle_error(
33
34
  return _calc_errors(drizzle_indices, error_input, bias_input)
34
35
 
35
36
 
36
- def _get_drizzle_indices(diameter: np.ndarray) -> dict:
37
+ def _get_drizzle_indices(diameter: npt.NDArray) -> dict:
37
38
  return {
38
39
  "drizzle": diameter > 0,
39
40
  "small": np.logical_and(diameter <= 1e-4, diameter > 1e-5),
@@ -154,12 +155,12 @@ def _calc_error(
154
155
 
155
156
 
156
157
  def _stack_errors(
157
- error_in: np.ndarray,
158
+ error_in: npt.NDArray,
158
159
  drizzle_indices: dict,
159
- error_small=None,
160
- error_tiny=None,
160
+ error_small: npt.NDArray | None = None,
161
+ error_tiny: npt.NDArray | None = None,
161
162
  ) -> ma.MaskedArray:
162
- def _add_error_component(source: np.ndarray, ind: tuple) -> None:
163
+ def _add_error_component(source: npt.NDArray, ind: tuple) -> None:
163
164
  error[ind] = source[ind]
164
165
 
165
166
  error = ma.zeros(error_in.shape)
@@ -174,14 +175,14 @@ def _stack_errors(
174
175
  COR = 10 / np.log(10)
175
176
 
176
177
 
177
- def db2lin(x_in: np.ndarray) -> ma.MaskedArray:
178
+ def db2lin(x_in: npt.NDArray) -> ma.MaskedArray:
178
179
  x = ma.copy(x_in)
179
180
  threshold = 100
180
181
  x[x > threshold] = threshold
181
182
  return ma.exp(x / COR) - 1
182
183
 
183
184
 
184
- def lin2db(x_in) -> ma.MaskedArray:
185
+ def lin2db(x_in: npt.NDArray) -> ma.MaskedArray:
185
186
  x = ma.copy(x_in)
186
187
  threshold = -0.9
187
188
  x[x < threshold] = threshold
@@ -1,9 +1,11 @@
1
1
  import logging
2
2
  import os
3
3
  from bisect import bisect_left
4
+ from os import PathLike
4
5
 
5
6
  import netCDF4
6
7
  import numpy as np
8
+ import numpy.typing as npt
7
9
  from numpy import ma
8
10
  from scipy.special import gamma
9
11
 
@@ -27,7 +29,7 @@ class DrizzleSource(DataSource):
27
29
 
28
30
  """
29
31
 
30
- def __init__(self, categorize_file: str):
32
+ def __init__(self, categorize_file: str | PathLike) -> None:
31
33
  super().__init__(categorize_file)
32
34
  self.mie = self._read_mie_lut()
33
35
  self.height_vector = self.getvar("height")
@@ -35,7 +37,7 @@ class DrizzleSource(DataSource):
35
37
  self.beta = self.getvar("beta")
36
38
  self.v = self.getvar("v")
37
39
 
38
- def _convert_z_units(self) -> np.ndarray:
40
+ def _convert_z_units(self) -> npt.NDArray:
39
41
  """Converts reflectivity factor to SI units."""
40
42
  z = self.getvar("Z") - 180
41
43
  z[z > 0.0] = 0.0
@@ -96,7 +98,7 @@ class DrizzleClassification(ProductClassification):
96
98
 
97
99
  """
98
100
 
99
- def __init__(self, categorize_file: str):
101
+ def __init__(self, categorize_file: str | PathLike) -> None:
100
102
  super().__init__(categorize_file)
101
103
  self.is_v_sigma = self._find_v_sigma(categorize_file)
102
104
  self.warm_liquid = self._find_warm_liquid()
@@ -105,14 +107,14 @@ class DrizzleClassification(ProductClassification):
105
107
  self.cold_rain = self._find_cold_rain()
106
108
 
107
109
  @staticmethod
108
- def _find_v_sigma(cat_file: str) -> np.ndarray:
110
+ def _find_v_sigma(cat_file: str | PathLike) -> npt.NDArray:
109
111
  v_sigma = product_tools.read_nc_field(cat_file, "v_sigma")
110
112
  return np.isfinite(v_sigma)
111
113
 
112
- def _find_warm_liquid(self) -> np.ndarray:
114
+ def _find_warm_liquid(self) -> npt.NDArray:
113
115
  return self.category_bits.droplet & ~self.category_bits.freezing
114
116
 
115
- def _find_drizzle(self) -> np.ndarray:
117
+ def _find_drizzle(self) -> npt.NDArray:
116
118
  return (
117
119
  ~utils.transpose(self.is_rain)
118
120
  & self.category_bits.falling
@@ -129,7 +131,7 @@ class DrizzleClassification(ProductClassification):
129
131
  & self.is_v_sigma
130
132
  )
131
133
 
132
- def _find_would_be_drizzle(self) -> np.ndarray:
134
+ def _find_would_be_drizzle(self) -> npt.NDArray:
133
135
  return (
134
136
  ~utils.transpose(self.is_rain)
135
137
  & self.warm_liquid
@@ -141,7 +143,7 @@ class DrizzleClassification(ProductClassification):
141
143
  & ~self.quality_bits.molecular
142
144
  )
143
145
 
144
- def _find_cold_rain(self) -> np.ndarray:
146
+ def _find_cold_rain(self) -> npt.NDArray:
145
147
  return np.any(self.category_bits.melting, axis=1)
146
148
 
147
149
 
@@ -161,11 +163,11 @@ class SpectralWidth:
161
163
 
162
164
  """
163
165
 
164
- def __init__(self, categorize_file: str):
166
+ def __init__(self, categorize_file: str | PathLike) -> None:
165
167
  self.cat_file = categorize_file
166
168
  self.width_ht = self._calculate_spectral_width()
167
169
 
168
- def _calculate_spectral_width(self) -> np.ndarray:
170
+ def _calculate_spectral_width(self) -> npt.NDArray:
169
171
  v_sigma = product_tools.read_nc_field(self.cat_file, "v_sigma")
170
172
  try:
171
173
  width = product_tools.read_nc_field(self.cat_file, "width")
@@ -175,19 +177,19 @@ class SpectralWidth:
175
177
  sigma_factor = self._calc_v_sigma_factor()
176
178
  return width - sigma_factor * v_sigma
177
179
 
178
- def _calc_v_sigma_factor(self) -> np.ndarray:
180
+ def _calc_v_sigma_factor(self) -> npt.NDArray:
179
181
  beam_divergence = self._calc_beam_divergence()
180
182
  wind = self._calc_horizontal_wind()
181
183
  actual_wind = (wind + beam_divergence) ** (2 / 3)
182
184
  scaled_wind = (30 * wind + beam_divergence) ** (2 / 3)
183
185
  return actual_wind / (scaled_wind - actual_wind)
184
186
 
185
- def _calc_beam_divergence(self) -> np.ndarray:
187
+ def _calc_beam_divergence(self) -> npt.NDArray:
186
188
  beam_width = 0.5
187
189
  height = product_tools.read_nc_field(self.cat_file, "height")
188
190
  return height * np.deg2rad(beam_width)
189
191
 
190
- def _calc_horizontal_wind(self) -> np.ndarray:
192
+ def _calc_horizontal_wind(self) -> npt.NDArray:
191
193
  """Calculates magnitude of horizontal wind.
192
194
 
193
195
  Returns:
@@ -219,7 +221,7 @@ class DrizzleSolver:
219
221
  drizzle_source: DrizzleSource,
220
222
  drizzle_class: DrizzleClassification,
221
223
  spectral_width: SpectralWidth,
222
- ):
224
+ ) -> None:
223
225
  self._data = drizzle_source
224
226
  self._drizzle_class = drizzle_class
225
227
  self._width_ht = spectral_width.width_ht
@@ -228,7 +230,7 @@ class DrizzleSolver:
228
230
  self._beta_z_ratio = self._calc_beta_z_ratio()
229
231
  self._solve_drizzle(self._dia_init)
230
232
 
231
- def _init_variables(self) -> tuple[dict, np.ndarray]:
233
+ def _init_variables(self) -> tuple[dict, npt.NDArray]:
232
234
  shape = self._data.z.shape
233
235
  res = {
234
236
  "Do": np.zeros(shape),
@@ -238,10 +240,12 @@ class DrizzleSolver:
238
240
  }
239
241
  return res, np.zeros(shape)
240
242
 
241
- def _calc_beta_z_ratio(self) -> np.ndarray:
243
+ def _calc_beta_z_ratio(self) -> npt.NDArray:
242
244
  return 2 / np.pi * self._data.beta / self._data.z
243
245
 
244
- def _find_lut_indices(self, ind, dia_init, n_dia, n_widths) -> tuple[int, int]:
246
+ def _find_lut_indices(
247
+ self, ind: tuple[int, ...], dia_init: npt.NDArray, n_dia: int, n_widths: int
248
+ ) -> tuple[int, int]:
245
249
  ind_dia = bisect_left(self._data.mie["Do"], dia_init[ind], hi=n_dia - 1)
246
250
  ind_width = bisect_left(
247
251
  self._width_lut[:, ind_dia],
@@ -250,7 +254,7 @@ class DrizzleSolver:
250
254
  )
251
255
  return ind_width, ind_dia
252
256
 
253
- def _solve_drizzle(self, dia_init: np.ndarray) -> None:
257
+ def _solve_drizzle(self, dia_init: npt.NDArray) -> None:
254
258
  drizzle_ind = np.where(self._drizzle_class.drizzle == 1)
255
259
  dia_init[drizzle_ind] = self._calc_dia(self._beta_z_ratio[drizzle_ind], k=18.8)
256
260
  n_widths, n_dia = self._width_lut.shape[0], len(self._data.mie["Do"])
@@ -277,7 +281,7 @@ class DrizzleSolver:
277
281
  def _update_result_tables(
278
282
  self,
279
283
  ind: tuple,
280
- dia: np.ndarray | float,
284
+ dia: npt.NDArray | float,
281
285
  lut_ind: tuple,
282
286
  ) -> None:
283
287
  self.params["Do"][ind] = dia
@@ -286,11 +290,11 @@ class DrizzleSolver:
286
290
 
287
291
  @staticmethod
288
292
  def _calc_dia(
289
- beta_z_ratio: np.ndarray | float,
293
+ beta_z_ratio: npt.NDArray | float,
290
294
  mu: float = 0.0,
291
295
  ray: float = 1.0,
292
296
  k: float = 1.0,
293
- ) -> np.ndarray | float:
297
+ ) -> npt.NDArray | float:
294
298
  """Drizzle diameter calculation.
295
299
 
296
300
  Args:
@@ -312,8 +316,8 @@ class DrizzleSolver:
312
316
  @staticmethod
313
317
  def _is_converged(
314
318
  ind: tuple,
315
- dia: np.ndarray | float,
316
- dia_init: np.ndarray,
319
+ dia: npt.NDArray | float,
320
+ dia_init: npt.NDArray,
317
321
  ) -> bool:
318
322
  threshold = 1e-3
319
323
  return abs((dia - dia_init[ind]) / dia_init[ind]) < threshold
@@ -1,5 +1,6 @@
1
1
  from os import PathLike
2
2
  from pathlib import Path
3
+ from uuid import UUID
3
4
 
4
5
  import doppy
5
6
  import doppy.netcdf
@@ -20,10 +21,10 @@ def generate_epsilon_from_lidar(
20
21
  doppler_lidar_file: str | PathLike,
21
22
  doppler_lidar_wind_file: str | PathLike,
22
23
  output_file: str | PathLike,
23
- uuid: str | None,
24
- ):
24
+ uuid: str | UUID | None = None,
25
+ ) -> UUID:
25
26
  sliding_window_in_seconds = 3 * 60
26
- uuid = uuid if uuid is not None else get_uuid()
27
+ uuid = get_uuid(uuid)
27
28
  opts = _get_options(doppler_lidar_file)
28
29
  opts.period = sliding_window_in_seconds
29
30
  vert = _vertical_wind_from_doppler_lidar_file(doppler_lidar_file)
@@ -77,7 +78,7 @@ def generate_epsilon_from_lidar(
77
78
  dtype="f4",
78
79
  )
79
80
 
80
- nc.add_attribute("file_uuid", uuid)
81
+ nc.add_attribute("file_uuid", str(uuid))
81
82
  nc.add_attribute("cloudnet_file_type", "epsilon-lidar")
82
83
  nc.add_attribute("doppy_version", doppy.__version__)
83
84
  nc.add_attribute("cloudnetpy_version", cloudnetpy.__version__)
@@ -163,7 +164,7 @@ def _get_options(doppler_lidar_file: str | PathLike) -> Options:
163
164
  raise ValueError(msg)
164
165
 
165
166
 
166
- def _infer_pulse_repetition_frequency(range_: npt.NDArray[np.float64]):
167
+ def _infer_pulse_repetition_frequency(range_: npt.NDArray[np.float64]) -> float:
167
168
  c = scipy.constants.c
168
169
  dist = range_.max() - range_.min()
169
170
  round_trip_time = 2 * dist / c
@@ -1,5 +1,8 @@
1
1
  """Module for creating Cloudnet ice effective radius file using Z-T method."""
2
2
 
3
+ from os import PathLike
4
+ from uuid import UUID
5
+
3
6
  import numpy as np
4
7
  from numpy import ma
5
8
 
@@ -7,13 +10,14 @@ from cloudnetpy import constants, output
7
10
  from cloudnetpy.metadata import MetaData
8
11
  from cloudnetpy.products.iwc import DEFINITIONS as IWC_DEFINITION
9
12
  from cloudnetpy.products.product_tools import IceClassification, IceSource
13
+ from cloudnetpy.utils import get_uuid
10
14
 
11
15
 
12
16
  def generate_ier(
13
- categorize_file: str,
14
- output_file: str,
15
- uuid: str | None = None,
16
- ) -> str:
17
+ categorize_file: str | PathLike,
18
+ output_file: str | PathLike,
19
+ uuid: str | UUID | None = None,
20
+ ) -> UUID:
17
21
  """Generates Cloudnet ice effective radius product.
18
22
 
19
23
  This function calculates ice particle effective radius using the Grieche
@@ -55,6 +59,7 @@ def generate_ier(
55
59
  from https://doi.org/10.5194/amt-13-5335-2020,
56
60
 
57
61
  """
62
+ uuid = get_uuid(uuid)
58
63
  product = "ier"
59
64
  with IerSource(categorize_file, product) as ier_source:
60
65
  ice_classification = IceClassification(categorize_file)
@@ -66,7 +71,8 @@ def generate_ier(
66
71
  attributes = output.add_time_attribute(IER_ATTRIBUTES, date)
67
72
  attributes = _add_ier_comment(attributes, ier_source)
68
73
  output.update_attributes(ier_source.data, attributes)
69
- return output.save_product_file(product, ier_source, output_file, uuid)
74
+ output.save_product_file(product, ier_source, output_file, uuid)
75
+ return uuid
70
76
 
71
77
 
72
78
  class IerSource(IceSource):
@@ -1,6 +1,9 @@
1
1
  """Module for creating Cloudnet ice water content file using Z-T method."""
2
2
 
3
- import numpy as np
3
+ from os import PathLike
4
+ from uuid import UUID
5
+
6
+ import numpy.typing as npt
4
7
  from numpy import ma
5
8
 
6
9
  from cloudnetpy import output, utils
@@ -10,10 +13,10 @@ from cloudnetpy.products.product_tools import IceClassification, IceSource
10
13
 
11
14
 
12
15
  def generate_iwc(
13
- categorize_file: str,
14
- output_file: str,
15
- uuid: str | None = None,
16
- ) -> str:
16
+ categorize_file: str | PathLike,
17
+ output_file: str | PathLike,
18
+ uuid: str | UUID | None = None,
19
+ ) -> UUID:
17
20
  """Generates Cloudnet ice water content product.
18
21
 
19
22
  This function calculates ice water content using the so-called Z-T method.
@@ -40,6 +43,7 @@ def generate_iwc(
40
43
  J. Appl. Meteor. Climatol., 45, 301–317, https://doi.org/10.1175/JAM2340.1
41
44
 
42
45
  """
46
+ uuid = utils.get_uuid(uuid)
43
47
  product = "iwc"
44
48
  with IwcSource(categorize_file, product) as iwc_source:
45
49
  ice_classification = IceClassification(categorize_file)
@@ -53,7 +57,8 @@ def generate_iwc(
53
57
  attributes = _add_iwc_comment(attributes, iwc_source)
54
58
  attributes = _add_iwc_error_comment(attributes, lwp_prior, bias)
55
59
  output.update_attributes(iwc_source.data, attributes)
56
- return output.save_product_file(product, iwc_source, output_file, uuid)
60
+ output.save_product_file(product, iwc_source, output_file, uuid)
61
+ return uuid
57
62
 
58
63
 
59
64
  class IwcSource(IceSource):
@@ -69,10 +74,12 @@ class IwcSource(IceSource):
69
74
  bias = self.getvar("Z_bias") * self.coefficients.Z * 10
70
75
  self.append_data(bias, f"{self.product}_bias")
71
76
 
72
- def append_error(self, ice_classification: IceClassification) -> tuple:
77
+ def append_error(
78
+ self, ice_classification: IceClassification
79
+ ) -> tuple[float, float]:
73
80
  """Estimates error of ice water content."""
74
81
 
75
- def _calc_random_error() -> np.ndarray:
82
+ def _calc_random_error() -> npt.NDArray:
76
83
  scaled_temperature = self.coefficients.ZT * self.temperature
77
84
  scaled_temperature += self.coefficients.Z
78
85
  return self.getvar("Z_error") * scaled_temperature * 10
@@ -98,7 +105,9 @@ class IwcSource(IceSource):
98
105
  return lwp_prior, retrieval_uncertainty
99
106
 
100
107
 
101
- def _add_iwc_error_comment(attributes: dict, lwp_prior, uncertainty: float) -> dict:
108
+ def _add_iwc_error_comment(
109
+ attributes: dict, lwp_prior: float, uncertainty: float
110
+ ) -> dict:
102
111
  attributes["iwc_error"] = attributes["iwc_error"]._replace(
103
112
  comment="This variable is an estimate of the one-standard-deviation random\n"
104
113
  "error in ice water content due to both the uncertainty of the retrieval\n"