cloudnetpy 1.79.0__py3-none-any.whl → 1.80.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.
@@ -1,8 +1,11 @@
1
1
  """Module for reading and processing Vaisala / Lufft ceilometers."""
2
2
 
3
+ import logging
3
4
  from itertools import islice
4
5
 
5
6
  import netCDF4
7
+ import numpy as np
8
+ from ceilopyter import read_ct25k
6
9
  from numpy import ma
7
10
 
8
11
  from cloudnetpy import output, utils
@@ -71,36 +74,51 @@ def ceilo2nc(
71
74
  ceilo_obj = _initialize_ceilo(full_path, site_meta, date)
72
75
  calibration_factor = site_meta.get("calibration_factor")
73
76
  range_corrected = site_meta.get("range_corrected", True)
74
- ceilo_obj.read_ceilometer_file(calibration_factor)
75
- ceilo_obj.check_beta_raw_shape()
76
- n_negatives = _get_n_negatives(ceilo_obj)
77
- ceilo_obj.data["beta"] = ceilo_obj.calc_screened_product(
78
- ceilo_obj.data["beta_raw"],
79
- snr_limit,
80
- range_corrected=range_corrected,
81
- n_negatives=n_negatives,
82
- )
83
- ceilo_obj.data["beta_smooth"] = ceilo_obj.calc_beta_smooth(
84
- ceilo_obj.data["beta"],
85
- snr_limit,
86
- range_corrected=range_corrected,
87
- n_negatives=n_negatives,
88
- )
89
- if ceilo_obj.instrument is None or ceilo_obj.instrument.model is None:
90
- msg = "Failed to read ceilometer model"
91
- raise RuntimeError(msg)
92
- if (
93
- any(
94
- model in ceilo_obj.instrument.model.lower()
95
- for model in ("cl61", "chm15k", "chm15kx", "cl51", "cl31")
77
+ if range_corrected is False:
78
+ logging.warning("Raw data not range-corrected.")
79
+ if isinstance(ceilo_obj, Ct25k):
80
+ c_obj = read_ct25k(full_path, calibration_factor, range_corrected)
81
+ ceilo_obj.data["beta"] = c_obj.beta
82
+ ceilo_obj.data["beta_raw"] = c_obj.beta_raw
83
+ ceilo_obj.data["time"] = c_obj.time
84
+ ceilo_obj.data["range"] = c_obj.range
85
+ if c_obj.zenith_angle is not None:
86
+ ceilo_obj.data["zenith_angle"] = np.median(c_obj.zenith_angle)
87
+ ceilo_obj.data["calibration_factor"] = c_obj.calibration_factor
88
+ ceilo_obj.sort_time()
89
+ ceilo_obj.screen_date()
90
+ ceilo_obj.convert_to_fraction_hour()
91
+ else:
92
+ ceilo_obj.read_ceilometer_file(calibration_factor)
93
+ ceilo_obj.check_beta_raw_shape()
94
+ n_negatives = _get_n_negatives(ceilo_obj)
95
+ ceilo_obj.data["beta"] = ceilo_obj.calc_screened_product(
96
+ ceilo_obj.data["beta_raw"],
97
+ snr_limit,
98
+ range_corrected=range_corrected,
99
+ n_negatives=n_negatives,
100
+ )
101
+ ceilo_obj.data["beta_smooth"] = ceilo_obj.calc_beta_smooth(
102
+ ceilo_obj.data["beta"],
103
+ snr_limit,
104
+ range_corrected=range_corrected,
105
+ n_negatives=n_negatives,
96
106
  )
97
- and range_corrected
98
- ):
99
- mask = ceilo_obj.data["beta_smooth"].mask
100
- ceilo_obj.data["beta"] = ma.masked_where(mask, ceilo_obj.data["beta_raw"])
101
- ceilo_obj.data["beta"][ceilo_obj.data["beta"] <= 0] = ma.masked
102
- if "depolarisation" in ceilo_obj.data:
103
- ceilo_obj.data["depolarisation"].mask = ceilo_obj.data["beta"].mask
107
+ if ceilo_obj.instrument is None or ceilo_obj.instrument.model is None:
108
+ msg = "Failed to read ceilometer model"
109
+ raise RuntimeError(msg)
110
+ if (
111
+ any(
112
+ model in ceilo_obj.instrument.model.lower()
113
+ for model in ("cl61", "chm15k", "chm15kx", "cl51", "cl31")
114
+ )
115
+ and range_corrected
116
+ ):
117
+ mask = ceilo_obj.data["beta_smooth"].mask
118
+ ceilo_obj.data["beta"] = ma.masked_where(mask, ceilo_obj.data["beta_raw"])
119
+ ceilo_obj.data["beta"][ceilo_obj.data["beta"] <= 0] = ma.masked
120
+ if "depolarisation" in ceilo_obj.data:
121
+ ceilo_obj.data["depolarisation"].mask = ceilo_obj.data["beta"].mask
104
122
  ceilo_obj.screen_depol()
105
123
  ceilo_obj.screen_invalid_values()
106
124
  ceilo_obj.prepare_data()
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import TYPE_CHECKING, NamedTuple
2
+ from typing import NamedTuple
3
3
 
4
4
  import numpy as np
5
5
  from numpy import ma
@@ -8,11 +8,9 @@ from scipy.ndimage import gaussian_filter
8
8
  from cloudnetpy import utils
9
9
  from cloudnetpy.cloudnetarray import CloudnetArray
10
10
  from cloudnetpy.exceptions import ValidTimeStampError
11
+ from cloudnetpy.instruments.instruments import Instrument
11
12
  from cloudnetpy.utils import Epoch
12
13
 
13
- if TYPE_CHECKING:
14
- from cloudnetpy.instruments.instruments import Instrument
15
-
16
14
 
17
15
  class NoiseParam(NamedTuple):
18
16
  """Noise parameters. Values are weakly instrument-dependent."""
@@ -47,6 +45,7 @@ class Ceilometer:
47
45
  self.data,
48
46
  self.noise_param,
49
47
  range_corrected=range_corrected,
48
+ instrument=self.instrument,
50
49
  )
51
50
  return noisy_data.screen_data(
52
51
  array,
@@ -66,6 +65,7 @@ class Ceilometer:
66
65
  self.data,
67
66
  self.noise_param,
68
67
  range_corrected=range_corrected,
68
+ instrument=self.instrument,
69
69
  )
70
70
  beta_raw = ma.copy(self.data["beta_raw"])
71
71
  cloud_ind, cloud_values, cloud_limit = _estimate_clouds_from_beta(beta)
@@ -145,10 +145,12 @@ class NoisyData:
145
145
  noise_param: NoiseParam,
146
146
  *,
147
147
  range_corrected: bool = True,
148
+ instrument: Instrument | None = None,
148
149
  ):
149
150
  self.data = data
150
151
  self.noise_param = noise_param
151
152
  self.range_corrected = range_corrected
153
+ self.instrument = instrument
152
154
 
153
155
  def screen_data(
154
156
  self,
@@ -268,14 +270,17 @@ class NoisyData:
268
270
  data[:, ind] = data[:, ind] * self._get_range_squared()[ind]
269
271
 
270
272
  def _get_altitude_ind(self) -> tuple:
271
- if self.range_corrected is False:
272
- alt_limit = 2400.0
273
- logging.warning(
274
- "Raw data not range-corrected, correcting below %s m",
275
- alt_limit,
276
- )
277
- else:
278
- alt_limit = 1e12
273
+ alt_limit = 1e12 # All altitudes
274
+ if (
275
+ self.range_corrected is False
276
+ and self.instrument is not None
277
+ and self.instrument.model is not None
278
+ ):
279
+ model = self.instrument.model.lower()
280
+ if model == "ct25k":
281
+ alt_limit = 0.0
282
+ elif model in ("cl31", "cl51"):
283
+ alt_limit = 2400.0
279
284
  return np.where(self.data["range"] < alt_limit)
280
285
 
281
286
  def _get_range_squared(self) -> np.ndarray:
@@ -46,18 +46,18 @@ class VaisalaCeilo(Ceilometer):
46
46
  self.data["calibration_factor"] = calibration_factor or 1.0
47
47
  self.data["beta_raw"] *= self.data["calibration_factor"]
48
48
  self.data["zenith_angle"] = np.median([d.tilt_angle for d in data])
49
- self._sort_time()
50
- self._screen_date()
51
- self._convert_to_fraction_hour()
49
+ self.sort_time()
50
+ self.screen_date()
51
+ self.convert_to_fraction_hour()
52
52
  self._store_ceilometer_info()
53
53
 
54
- def _sort_time(self):
54
+ def sort_time(self):
55
55
  """Sorts timestamps and removes duplicates."""
56
56
  time = self.data["time"]
57
57
  _time, ind = np.unique(time, return_index=True)
58
58
  self._screen_time_indices(ind)
59
59
 
60
- def _screen_date(self):
60
+ def screen_date(self):
61
61
  time = self.data["time"]
62
62
  if self.sane_date is None:
63
63
  self.sane_date = time[0].date()
@@ -79,7 +79,7 @@ class VaisalaCeilo(Ceilometer):
79
79
  if hasattr(array, "shape") and array.shape[:1] == (n_time,):
80
80
  self.data[key] = self.data[key][valid_indices]
81
81
 
82
- def _convert_to_fraction_hour(self):
82
+ def convert_to_fraction_hour(self):
83
83
  time = self.data["time"]
84
84
  midnight = time[0].replace(hour=0, minute=0, second=0, microsecond=0)
85
85
  hour = datetime.timedelta(hours=1)
@@ -113,6 +113,7 @@ class Ct25k(VaisalaCeilo):
113
113
 
114
114
  def __init__(self, full_path, site_meta, expected_date=None):
115
115
  super().__init__(read_ct_file, full_path, site_meta, expected_date)
116
+ self._store_ceilometer_info()
116
117
 
117
118
  def _store_ceilometer_info(self):
118
119
  self.instrument = instruments.CT25K
cloudnetpy/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
- MINOR = 79
2
+ MINOR = 80
3
3
  PATCH = 0
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.79.0
3
+ Version: 1.80.0
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -38,7 +38,7 @@ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
38
38
  Requires-Python: >=3.10
39
39
  Description-Content-Type: text/markdown
40
40
  License-File: LICENSE
41
- Requires-Dist: ceilopyter
41
+ Requires-Dist: ceilopyter>=0.2.0
42
42
  Requires-Dist: doppy>=0.5.0
43
43
  Requires-Dist: matplotlib
44
44
  Requires-Dist: mwrpy>=1.3.0
@@ -9,7 +9,7 @@ cloudnetpy/metadata.py,sha256=lO7BCbVAzFoH3Nq-VuezYX0f7MnbG1Zp11g5GSiuQwM,6189
9
9
  cloudnetpy/output.py,sha256=gupxt4f_-eUrFsWMto8tnknoV-p9QauC9L6CJAqBILU,15988
10
10
  cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  cloudnetpy/utils.py,sha256=WczDeGN408XSgGeaRLXFmlLjgAS67lK1osV0YEuKmwo,32027
12
- cloudnetpy/version.py,sha256=r_RTjOqOnzkut6bsKX5hTeLYlrSoHQYJsg8Ca85nAOQ,72
12
+ cloudnetpy/version.py,sha256=3SiH4gJlcuEOGDz63kQkJlurcgqt5RcwIPvPtt8WVKg,72
13
13
  cloudnetpy/categorize/__init__.py,sha256=s-SJaysvVpVVo5kidiruWQO6p3gv2TXwY1wEHYO5D6I,44
14
14
  cloudnetpy/categorize/atmos_utils.py,sha256=RcmbKxm2COkE7WEya0mK3yX5rzUbrewRVh3ekm01RtM,10598
15
15
  cloudnetpy/categorize/attenuation.py,sha256=Y_-fzmQTltWTqIZTulJhovC7a6ifpMcaAazDJcnMIOc,990
@@ -35,8 +35,8 @@ cloudnetpy/categorize/attenuations/rain_attenuation.py,sha256=qazJzRyXf9vbjJhh4y
35
35
  cloudnetpy/instruments/__init__.py,sha256=PEgrrQNoiOuN_ctYilmt4LV2QCLg1likPjJdWtuGlLs,528
36
36
  cloudnetpy/instruments/basta.py,sha256=Lb_EhQTI93S5Bd9osDbCE_tC8gZreRsHz7D2_dFOjmE,3793
37
37
  cloudnetpy/instruments/bowtie.py,sha256=EyE8HAE8rjO7JelJDbQte_rnwE3VoVJVc6TBpSNK3IU,3930
38
- cloudnetpy/instruments/ceilo.py,sha256=GnJYW6oadOApyQSKhzNyJXT4PhoQZlCY9QE4QcqLX8I,9559
39
- cloudnetpy/instruments/ceilometer.py,sha256=ati9-fUQ54K9tvynIPB-nlBYwtvBVaQtUCjVCLNB67w,12059
38
+ cloudnetpy/instruments/ceilo.py,sha256=gHCh8222Csso9JOaE2f9r5ydzq_DJv112M8qMBVBFcI,10442
39
+ cloudnetpy/instruments/ceilometer.py,sha256=XS2hVJ7rn9WOUKq19wpNL5MJr59fKSEWHC_1pOE_Bm4,12323
40
40
  cloudnetpy/instruments/cl61d.py,sha256=0QMqXHIy0hn2mksAwTdaKMOaEWjsZmj7QZ8hCbcHwxE,2225
41
41
  cloudnetpy/instruments/cloudnet_instrument.py,sha256=SGPsRYYoGPoRoDY7hHJcKUVX0A23X0Telc00Fu01PnY,4495
42
42
  cloudnetpy/instruments/copernicus.py,sha256=hCphEKyFCc3f1uLRdjL2435kuh64M5q-V1bI68bzGbA,6528
@@ -55,7 +55,7 @@ cloudnetpy/instruments/rain_e_h3.py,sha256=JEg4Ko7ZdfjAUJwJ1BWdTkm4K7r3s8WKrPb-H
55
55
  cloudnetpy/instruments/rpg.py,sha256=m3-xLJ-w2T7Ip7jBveWsGrts4tmNvdc-Lb4HebvHQjQ,17319
56
56
  cloudnetpy/instruments/rpg_reader.py,sha256=ThztFuVrWxhmWVAfZTfQDeUiKK1XMTbtv08IBe8GK98,11364
57
57
  cloudnetpy/instruments/toa5.py,sha256=CfmmBMv5iMGaWHIGBK01Rw24cuXC1R1RMNTXkmsm340,1760
58
- cloudnetpy/instruments/vaisala.py,sha256=RKAw_fVry4YOUF0i2_-2jLIc6_H85oL8USA4ji9rh0o,4583
58
+ cloudnetpy/instruments/vaisala.py,sha256=W_yu_f92cOq8RiiqDLj7bswxu9UMS3TITPWzP5xPdvA,4615
59
59
  cloudnetpy/instruments/weather_station.py,sha256=pZK7I5bk1USDRoTeIhZoWzbka9ciea5ypA3oIzZX-7g,24549
60
60
  cloudnetpy/instruments/disdrometer/__init__.py,sha256=lyjwttWvFvuwYxEkusoAvgRcbBmglmOp5HJOpXUqLWo,93
61
61
  cloudnetpy/instruments/disdrometer/common.py,sha256=g52iK2aNp3Z88kovUmGVpC54NZomPa9D871gzO0AmQ4,9267
@@ -117,10 +117,10 @@ cloudnetpy/products/lwc.py,sha256=sl6Al2tuH3KkCBrPbWTmuz3jlD5UQJ4D6qBsn1tt2CQ,18
117
117
  cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe55y9ob58,16637951
118
118
  cloudnetpy/products/mwr_tools.py,sha256=8HPZpQMTojKZP1JS1S83IE0sxmbDE9bxlaWoqmGnUZE,6199
119
119
  cloudnetpy/products/product_tools.py,sha256=uu4l6reuGbPcW3TgttbaSrqIKbyYGhBVTdnC7opKvmg,11101
120
- cloudnetpy-1.79.0.dist-info/licenses/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
120
+ cloudnetpy-1.80.0.dist-info/licenses/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
121
121
  docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
122
- cloudnetpy-1.79.0.dist-info/METADATA,sha256=Z5jTV-IAQDqCM5oV-TQuODjFGDm9K0Iy6itew-2qD4g,5796
123
- cloudnetpy-1.79.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
124
- cloudnetpy-1.79.0.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
125
- cloudnetpy-1.79.0.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
126
- cloudnetpy-1.79.0.dist-info/RECORD,,
122
+ cloudnetpy-1.80.0.dist-info/METADATA,sha256=fk5uizRi5azdU4nz8DQRzMMuOSvlEbR_ZW19xbXvOvI,5803
123
+ cloudnetpy-1.80.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
124
+ cloudnetpy-1.80.0.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
125
+ cloudnetpy-1.80.0.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
126
+ cloudnetpy-1.80.0.dist-info/RECORD,,