cloudnetpy 1.56.4__py3-none-any.whl → 1.56.6__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.
cloudnetpy/exceptions.py CHANGED
@@ -23,6 +23,13 @@ class RadarDataError(CloudnetException):
23
23
  super().__init__(msg)
24
24
 
25
25
 
26
+ class PlottingError(CloudnetException):
27
+ """Internal exception class."""
28
+
29
+ def __init__(self, msg: str):
30
+ super().__init__(msg)
31
+
32
+
26
33
  class WeatherStationDataError(CloudnetException):
27
34
  """Internal exception class."""
28
35
 
@@ -4,10 +4,10 @@ import logging
4
4
  from collections import defaultdict
5
5
  from pathlib import Path
6
6
 
7
- import mwrpy
8
7
  import netCDF4
9
8
  import numpy as np
10
9
  from mwrpy.level1.lev1_meta_nc import ATTRIBUTES_1B01
10
+ from mwrpy.level1.write_lev1_nc import lev1_to_nc
11
11
  from mwrpy.version import __version__ as mwrpy_version
12
12
  from numpy import ma
13
13
 
@@ -45,7 +45,7 @@ def hatpro2l1c(
45
45
  """
46
46
  coeff_files = site_meta.get("coefficientFiles", None)
47
47
 
48
- hatpro_raw = mwrpy.lev1_to_nc(
48
+ hatpro_raw = lev1_to_nc(
49
49
  "1C01",
50
50
  mwr_dir,
51
51
  output_file=output_file,
@@ -99,7 +99,7 @@ def hatpro2l1c(
99
99
 
100
100
 
101
101
  class HatproL1c:
102
- def __init__(self, hatpro: mwrpy.Rpg, site_meta: dict):
102
+ def __init__(self, hatpro, site_meta: dict):
103
103
  self.raw_data = hatpro.raw_data
104
104
  self.data = hatpro.data
105
105
  self.date = hatpro.date.split("-")
@@ -224,12 +224,19 @@ ATTRIBUTES = {
224
224
  ),
225
225
  "potential_temperature": PlotMeta(
226
226
  cmap="RdBu_r",
227
- plot_range=(260, 330),
227
+ plot_range=(260, 320),
228
+ ),
229
+ "equivalent_potential_temperature": PlotMeta(
230
+ cmap="RdBu_r",
231
+ plot_range=(260, 320),
228
232
  ),
229
233
  "absolute_humidity": PlotMeta(
230
- plot_range=(1e-5, 1e-2),
234
+ plot_range=(1e-4, 1e-2),
231
235
  log_scale=True,
232
236
  ),
237
+ "relative_humidity": PlotMeta(
238
+ plot_range=(0, 120),
239
+ ),
233
240
  "cloud_fraction": PlotMeta(
234
241
  cmap="Blues",
235
242
  plot_range=(0, 1),
@@ -16,6 +16,7 @@ from matplotlib.transforms import Affine2D, Bbox
16
16
  from mpl_toolkits.axes_grid1 import make_axes_locatable
17
17
  from numpy import ma, ndarray
18
18
 
19
+ from cloudnetpy.exceptions import PlottingError
19
20
  from cloudnetpy.plotting.plot_meta import ATTRIBUTES, PlotMeta
20
21
 
21
22
 
@@ -129,7 +130,7 @@ class FigureData:
129
130
  variable_indices.append(extracted_ind)
130
131
  if not valid_variables:
131
132
  msg = f"None of the variables {requested_variables} found in the file."
132
- raise ValueError(msg)
133
+ raise PlottingError(msg)
133
134
  return valid_variables, variable_indices
134
135
 
135
136
  def _get_height(self) -> np.ndarray | None:
@@ -143,6 +144,10 @@ class FigureData:
143
144
  return self.file.variables["range"][:] * m2km
144
145
  return None
145
146
 
147
+ def is_mwrpy_product(self) -> bool:
148
+ cloudnet_file_type = getattr(self.file, "cloudnet_file_type", "")
149
+ return cloudnet_file_type in ("mwr-single", "mwr-multi")
150
+
146
151
  def __len__(self) -> int:
147
152
  return len(self.variables)
148
153
 
@@ -177,7 +182,7 @@ class SubPlot:
177
182
  ylabel: str | None = None,
178
183
  y_limits: tuple[float, float] | None = None,
179
184
  ) -> None:
180
- label = ylabel or "Height (km)"
185
+ label = ylabel if ylabel is not None else "Height (km)"
181
186
  self.ax.set_ylabel(label, fontsize=13)
182
187
  if y_limits is not None:
183
188
  self.ax.set_ylim(*y_limits)
@@ -236,9 +241,7 @@ class SubPlot:
236
241
  if self.options.plot_meta is not None:
237
242
  return self.options.plot_meta
238
243
  fallback = ATTRIBUTES["fallback"].get(self.variable.name, PlotMeta())
239
- if file_type is None:
240
- return fallback
241
- file_attributes = ATTRIBUTES.get(file_type, {})
244
+ file_attributes = ATTRIBUTES.get(file_type or "", {})
242
245
  plot_meta = file_attributes.get(self.variable.name, fallback)
243
246
  if plot_meta.clabel is None:
244
247
  plot_meta = plot_meta._replace(clabel=_reformat_units(self.variable.units))
@@ -249,10 +252,34 @@ class Plot:
249
252
  def __init__(self, sub_plot: SubPlot):
250
253
  self.sub_plot = sub_plot
251
254
  self._data = sub_plot.variable[:]
255
+ self._data_orig = self._data.copy()
252
256
  self._plot_meta = sub_plot.plot_meta
253
257
  self._is_log = sub_plot.plot_meta.log_scale
254
258
  self._ax = sub_plot.ax
255
259
 
260
+ def _convert_units(self) -> str | None:
261
+ multiply, add = "multiply", "add"
262
+ units_conversion = {
263
+ "rainfall_rate": (multiply, 360000, "mm h$^{-1}$"),
264
+ "air_pressure": (multiply, 0.01, "hPa"),
265
+ "relative_humidity": (multiply, 100, "%"),
266
+ "rainfall_amount": (multiply, 1000, "mm"),
267
+ "air_temperature": (add, -273.15, "\u00B0C"),
268
+ }
269
+ conversion_method, conversion, units = units_conversion.get(
270
+ self.sub_plot.variable.name, (multiply, 1, None)
271
+ )
272
+ if conversion_method == multiply:
273
+ self._data *= conversion
274
+ self._data_orig *= conversion
275
+ elif conversion_method == add:
276
+ self._data += conversion
277
+ self._data_orig += conversion
278
+ if units is not None:
279
+ return units
280
+ units = getattr(self.sub_plot.variable, "units", "")
281
+ return _reformat_units(units)
282
+
256
283
  def _get_y_limits(self) -> tuple[float, float]:
257
284
  return 0, self.sub_plot.options.max_y
258
285
 
@@ -273,6 +300,7 @@ class Plot:
273
300
  facecolor="grey",
274
301
  edgecolor="black",
275
302
  alpha=0.15,
303
+ label="_nolegend_",
276
304
  )
277
305
 
278
306
  def _mark_gaps(self, figure_data: FigureData) -> None:
@@ -282,6 +310,10 @@ class Plot:
282
310
  msg = "Time values outside the range 0-24."
283
311
  raise ValueError(msg)
284
312
  max_gap_fraction_hour = _get_max_gap_in_minutes(figure_data) / 60
313
+
314
+ if figure_data.file.cloudnet_file_type == "model":
315
+ time, data = screen_completely_masked_profiles(time, data)
316
+
285
317
  gap_indices = np.where(np.diff(time) > max_gap_fraction_hour)[0]
286
318
  if not ma.is_masked(data):
287
319
  mask_new = np.zeros(data.shape)
@@ -319,9 +351,21 @@ class Plot:
319
351
  self._data = data_new
320
352
  figure_data.time_including_gaps = time_new
321
353
 
354
+ def _read_flagged_data(self, figure_data: FigureData) -> np.ndarray:
355
+ flag_names = [
356
+ f"{self.sub_plot.variable.name}_quality_flag",
357
+ "temperature_quality_flag",
358
+ "quality_flag",
359
+ ]
360
+ for flag_name in flag_names:
361
+ if flag_name in figure_data.file.variables:
362
+ return figure_data.file.variables[flag_name][:] > 0
363
+ return np.array([])
364
+
322
365
 
323
366
  class Plot2D(Plot):
324
367
  def plot(self, figure_data: FigureData):
368
+ self._convert_units()
325
369
  self._mark_gaps(figure_data)
326
370
  if self.sub_plot.variable.name == "cloud_fraction":
327
371
  self._data[self._data == 0] = ma.masked
@@ -335,6 +379,25 @@ class Plot2D(Plot):
335
379
  if figure_data.options.mark_data_gaps:
336
380
  self._fill_between_data_gaps(figure_data)
337
381
 
382
+ if figure_data.is_mwrpy_product():
383
+ self._fill_flagged_data(figure_data)
384
+
385
+ def _fill_flagged_data(self, figure_data: FigureData) -> None:
386
+ flags = self._read_flagged_data(figure_data)
387
+ batches = find_batches_of_ones(flags)
388
+ for batch in batches:
389
+ if batch[0] == batch[1]:
390
+ continue
391
+ time_batch = figure_data.time[batch[0]], figure_data.time[batch[1]]
392
+ self._ax.fill_between(
393
+ time_batch,
394
+ *self._get_y_limits(),
395
+ facecolor="whitesmoke",
396
+ alpha=0.7,
397
+ edgecolor="grey",
398
+ label="_nolegend_",
399
+ )
400
+
338
401
  def _plot_segment_data(self, figure_data: FigureData) -> None:
339
402
  def _hide_segments(
340
403
  data_in: ma.MaskedArray,
@@ -403,22 +466,35 @@ class Plot2D(Plot):
403
466
 
404
467
 
405
468
  class Plot1D(Plot):
406
- def plot_tb(self, figure_data: FigureData, ind: int):
407
- flagged_data = self._pointing_filter(figure_data, ind)
469
+ def plot_tb(self, figure_data: FigureData, freq_ind: int):
470
+ self._data = self._data[:, freq_ind]
471
+ self._data_orig = self._data_orig[:, freq_ind]
472
+ is_bad_zenith = self._get_bad_zenith_profiles(figure_data)
473
+ self._data[is_bad_zenith] = ma.masked
474
+ self._data_orig[is_bad_zenith] = ma.masked
475
+ flags = self._read_flagged_data(figure_data)[:, freq_ind]
476
+ flags[is_bad_zenith] = False
477
+ if np.any(flags):
478
+ self.plot_flag_data(figure_data.time[flags], self._data_orig[flags])
479
+ self.add_legend()
480
+ self.plot(figure_data)
481
+
482
+ def plot_flag_data(self, time: np.ndarray, values: np.ndarray) -> None:
408
483
  self._ax.plot(
409
- figure_data.time,
410
- flagged_data,
484
+ time,
485
+ values,
411
486
  color="salmon",
412
487
  marker=".",
413
488
  lw=0,
414
489
  markersize=3,
490
+ zorder=10,
415
491
  )
416
- self.plot(figure_data)
492
+
493
+ def add_legend(self):
417
494
  self._ax.legend(
418
- ["Flagged data", "Valid data"],
495
+ ["Flagged data"],
419
496
  markerscale=3,
420
497
  numpoints=1,
421
- reverse=True,
422
498
  frameon=False,
423
499
  )
424
500
 
@@ -428,6 +504,7 @@ class Plot1D(Plot):
428
504
  self._ax.plot(
429
505
  figure_data.time_including_gaps,
430
506
  self._data,
507
+ label="_nolegend_",
431
508
  **self._get_plot_options(),
432
509
  )
433
510
  if self._plot_meta.moving_average:
@@ -436,6 +513,11 @@ class Plot1D(Plot):
436
513
  self.sub_plot.set_yax(ylabel=units, y_limits=self._get_y_limits())
437
514
  pos = self._ax.get_position()
438
515
  self._ax.set_position((pos.x0, pos.y0, pos.width * 0.965, pos.height))
516
+ if figure_data.is_mwrpy_product():
517
+ flags = self._read_flagged_data(figure_data)
518
+ if np.any(flags):
519
+ self.plot_flag_data(figure_data.time[flags], self._data_orig[flags])
520
+ self.add_legend()
439
521
 
440
522
  def _get_y_limits(self) -> tuple[float, float]:
441
523
  percent_gap = 0.05
@@ -452,26 +534,6 @@ class Plot1D(Plot):
452
534
  return fallback
453
535
  return min_y, max_y
454
536
 
455
- def _convert_units(self) -> str | None:
456
- multiply, add = "multiply", "add"
457
- units_conversion = {
458
- "rainfall_rate": (multiply, 360000, "mm h$^{-1}$"),
459
- "air_pressure": (multiply, 0.01, "hPa"),
460
- "relative_humidity": (multiply, 100, "%"),
461
- "rainfall_amount": (multiply, 1000, "mm"),
462
- "air_temperature": (add, -273.15, "\u00B0C"),
463
- }
464
- conversion_method, conversion, units = units_conversion.get(
465
- self.sub_plot.variable.name, (multiply, 1, None)
466
- )
467
- if conversion_method == multiply:
468
- self._data *= conversion
469
- elif conversion_method == add:
470
- self._data += conversion
471
- if units is not None:
472
- return units
473
- return _reformat_units(self.sub_plot.variable.units)
474
-
475
537
  def _get_plot_options(self) -> dict:
476
538
  default_options = {
477
539
  "color": "lightblue",
@@ -497,13 +559,14 @@ class Plot1D(Plot):
497
559
  return min(max(line_width, 0.25), 0.9)
498
560
 
499
561
  def _plot_moving_average(self, figure_data: FigureData):
500
- time = figure_data.time_including_gaps.copy()
501
- data, time = self._get_unmasked_values(self._data, time)
562
+ time = figure_data.time.copy()
563
+ data = self._data_orig.copy()
564
+ data, time = self._get_unmasked_values(data, time)
502
565
  sma = self._calculate_moving_average(data, time, window=5)
503
566
  gap_time = _get_max_gap_in_minutes(figure_data)
504
567
  gaps = self._find_time_gap_indices(time, max_gap_min=gap_time)
505
568
  sma[gaps] = np.nan
506
- self._ax.plot(time, sma, color="slateblue", lw=2)
569
+ self._ax.plot(time, sma, color="slateblue", lw=2, label="_nolegend_")
507
570
 
508
571
  @staticmethod
509
572
  def _get_unmasked_values(
@@ -515,24 +578,17 @@ class Plot1D(Plot):
515
578
  good_values = ~data.mask
516
579
  return data[good_values], time[good_values]
517
580
 
518
- def _pointing_filter(self, figure_data: FigureData, ind: int) -> ndarray:
581
+ @staticmethod
582
+ def _get_bad_zenith_profiles(figure_data: FigureData) -> np.ndarray:
519
583
  zenith_limit = 5
520
- status = 0
521
- self._data = self._data[:, ind]
522
- flagged_data = ma.masked_all_like(figure_data.time)
584
+ valid_pointing_status = 0
523
585
  if "pointing_flag" in figure_data.file.variables:
524
586
  pointing_flag = figure_data.file.variables["pointing_flag"][:]
525
587
  zenith_angle = figure_data.file.variables["zenith_angle"][:]
526
- quality_flag = figure_data.file.variables["quality_flag"][:, ind]
527
- # First mask bad zenith angle points
528
- self._data[np.abs(zenith_angle) > zenith_limit] = ma.masked
529
- self._data[pointing_flag != status] = ma.masked
530
- # Store flagged data points for visualization
531
- valid_ind = np.where(quality_flag != status)[0]
532
- if len(valid_ind) > 0:
533
- flagged_data[valid_ind] = self._data[valid_ind]
534
- self._data[quality_flag != status] = ma.masked
535
- return flagged_data
588
+ is_bad_zenith = np.abs(zenith_angle) > zenith_limit
589
+ is_bad_pointing = pointing_flag != valid_pointing_status
590
+ return is_bad_zenith | is_bad_pointing
591
+ return np.zeros_like(figure_data.time, dtype=bool)
536
592
 
537
593
  @staticmethod
538
594
  def _find_time_gap_indices(time: ndarray, max_gap_min: float) -> ndarray:
@@ -633,6 +689,8 @@ def _reformat_units(unit: str) -> str:
633
689
  "sr-1 m-1": "sr$^{-1}$ m$^{-1}$",
634
690
  "kg m-2": "kg m$^{-2}$",
635
691
  "kg m-3": "kg m$^{-3}$",
692
+ "g m-3": "g m$^{-3}$",
693
+ "g m-2": "g m$^{-2}$",
636
694
  "kg m-2 s-1": "kg m$^{-2}$ s$^{-1}$",
637
695
  "dB km-1": "dB km$^{-1}$",
638
696
  "rad km-1": "rad km$^{-1}$",
@@ -682,3 +740,22 @@ def plot_2d(
682
740
  if xlim is not None:
683
741
  plt.xlim(xlim)
684
742
  plt.show()
743
+
744
+
745
+ def find_batches_of_ones(array: np.ndarray) -> list[tuple[int, int]]:
746
+ """Find batches of ones in a binary array."""
747
+ starts = np.where(np.diff(np.hstack(([0], array))) == 1)[0]
748
+ stops = np.where(np.diff(np.hstack((array, [0]))) == -1)[0]
749
+ return list(zip(starts, stops, strict=True))
750
+
751
+
752
+ def screen_completely_masked_profiles(time: np.ndarray, data: ma.MaskedArray) -> tuple:
753
+ if not ma.is_masked(data):
754
+ return time, data
755
+ good_ind = np.where(np.any(~data.mask, axis=1))[0]
756
+ if len(good_ind) == 0:
757
+ msg = "All values masked in the file."
758
+ raise PlottingError(msg)
759
+ good_ind = np.append(good_ind, good_ind[-1] + 1)
760
+ good_ind = np.clip(good_ind, 0, len(time) - 1)
761
+ return time[good_ind], data[good_ind, :]
@@ -4,3 +4,4 @@ from .drizzle import generate_drizzle
4
4
  from .ier import generate_ier
5
5
  from .iwc import generate_iwc
6
6
  from .lwc import generate_lwc
7
+ from .mwr_tools import generate_mwr_multi, generate_mwr_single
@@ -0,0 +1,127 @@
1
+ import tempfile
2
+ from typing import Literal
3
+
4
+ import netCDF4
5
+ from mwrpy.level2.lev2_collocated import generate_lev2_multi as gen_multi
6
+ from mwrpy.level2.lev2_collocated import generate_lev2_single as gen_single
7
+ from mwrpy.version import __version__ as mwrpy_version
8
+
9
+ from cloudnetpy import output, utils
10
+ from cloudnetpy.products import product_tools
11
+
12
+
13
+ def generate_mwr_single(
14
+ mwr_l1c_file: str, output_file: str, uuid: str | None = None
15
+ ) -> str:
16
+ """
17
+ Generates MWR single-pointing product including liquid water path, integrated
18
+ water vapor, etc. from zenith measurements.
19
+
20
+ Args:
21
+ mwr_l1c_file: The Level 1C MWR file to be processed.
22
+ output_file: The file path where the output file should be saved.
23
+ uuid: The UUID, if any, associated with the output file. Defaults to None.
24
+
25
+ Returns:
26
+ UUID of generated file.
27
+
28
+ Example:
29
+ >>> generate_mwr_single('input_mwr_l1c_file', 'output_file', 'abcdefg1234567')
30
+ """
31
+ return _generate_product(mwr_l1c_file, output_file, uuid, "single")
32
+
33
+
34
+ def generate_mwr_multi(
35
+ mwr_l1c_file: str, output_file: str, uuid: str | None = None
36
+ ) -> str:
37
+ """
38
+ Generates MWR multiple-pointing product, including relative humidity profiles,
39
+ etc. from scanning measurements.
40
+
41
+ Args:
42
+ mwr_l1c_file: The input file in MWR L1C format.
43
+ output_file: The location where the output file should be generated.
44
+ uuid: The UUID for the MWR multi product, defaults to None if
45
+ not provided.
46
+
47
+ Returns:
48
+ UUID of generated file.
49
+ """
50
+ return _generate_product(mwr_l1c_file, output_file, uuid, "multi")
51
+
52
+
53
+ def _generate_product(
54
+ mwr_l1c_file: str,
55
+ output_file: str,
56
+ uuid: str | None,
57
+ product: Literal["multi", "single"],
58
+ ) -> str:
59
+ fun = gen_multi if product == "multi" else gen_single
60
+ with tempfile.TemporaryDirectory() as temp_dir:
61
+ coeffs = product_tools.get_read_mwrpy_coeffs(mwr_l1c_file, temp_dir)
62
+ fun(None, mwr_l1c_file, output_file, coeff_files=coeffs)
63
+ with (
64
+ netCDF4.Dataset(mwr_l1c_file, "r") as nc_input,
65
+ netCDF4.Dataset(output_file, "r+") as nc_output,
66
+ ):
67
+ mwr = Mwr(nc_input, nc_output, uuid)
68
+ return mwr.harmonize(product)
69
+
70
+
71
+ class Mwr:
72
+ def __init__(
73
+ self, nc_l1c: netCDF4.Dataset, nc_l2: netCDF4.Dataset, uuid: str | None
74
+ ):
75
+ self.nc_l1c = nc_l1c
76
+ self.nc_l2 = nc_l2
77
+ self.uuid = uuid if uuid is not None else utils.get_uuid()
78
+
79
+ def harmonize(self, product: Literal["multi", "single"]) -> str:
80
+ self._truncate_global_attributes()
81
+ self._copy_variable_values()
82
+ self._copy_global_attributes()
83
+ self._fix_variable_attributes()
84
+ self._write_missing_global_attributes(product)
85
+ return self.uuid
86
+
87
+ def _truncate_global_attributes(self):
88
+ for attr in self.nc_l2.ncattrs():
89
+ delattr(self.nc_l2, attr)
90
+
91
+ def _copy_variable_values(self):
92
+ keys = ("latitude", "longitude", "altitude")
93
+ for var in keys:
94
+ if var in self.nc_l2.variables:
95
+ self.nc_l2.variables[var][:] = self.nc_l1c.variables[var][:]
96
+
97
+ def _copy_global_attributes(self):
98
+ keys = ("year", "month", "day", "location", "source")
99
+ output.copy_global(self.nc_l1c, self.nc_l2, keys)
100
+
101
+ def _fix_variable_attributes(self):
102
+ output.replace_attribute_with_standard_value(
103
+ self.nc_l2,
104
+ (
105
+ "lwp",
106
+ "iwv",
107
+ "temperature",
108
+ "azimuth_angle",
109
+ "latitude",
110
+ "longitude",
111
+ "altitude",
112
+ ),
113
+ ("units", "long_name", "standard_name"),
114
+ )
115
+
116
+ def _write_missing_global_attributes(self, product: Literal["multi", "single"]):
117
+ output.add_standard_global_attributes(self.nc_l2, self.uuid)
118
+ product_type = "multiple-pointing" if product == "multi" else "single-pointing"
119
+ self.nc_l2.title = f"MWR {product_type} from {self.nc_l1c.location}"
120
+ self.nc_l2.cloudnet_file_type = f"mwr-{product}"
121
+ output.fix_time_attributes(self.nc_l2)
122
+ self.nc_l2.history = (
123
+ f"{utils.get_time()} - MWR {product_type} file created \n"
124
+ f"{self.nc_l1c.history}"
125
+ )
126
+ self.nc_l2.source_file_uuids = self.nc_l1c.file_uuid
127
+ self.nc_l2.mwrpy_version = mwrpy_version
cloudnetpy/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
2
  MINOR = 56
3
- PATCH = 4
3
+ PATCH = 6
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudnetpy
3
- Version: 1.56.4
3
+ Version: 1.56.6
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -3,12 +3,12 @@ cloudnetpy/cloudnetarray.py,sha256=vSI6hqozhS1ntNIgY2kqTt7N6BF_xJtUii_vsuyxTno,6
3
3
  cloudnetpy/concat_lib.py,sha256=YK5ho5msqwNxpPtPT8f2OewIJ8hTrbhCkaxHaBKLTI0,9809
4
4
  cloudnetpy/constants.py,sha256=OMp3pKHCZmdKyRvfO35E7vE3FrS_DHIs_GJuexJLCQk,600
5
5
  cloudnetpy/datasource.py,sha256=-6oLC5bsn9sIoaN0glV88owFyTeGRsW1ZVJSV8rM5rE,7813
6
- cloudnetpy/exceptions.py,sha256=NsuB5gTA8NvpmxykIJjHOWObTPpAerzpofs8xrFA1ow,1272
6
+ cloudnetpy/exceptions.py,sha256=nA6YieylwKSp5KQOh3DhhcTmUBsd0tBZnedgmlWck-w,1415
7
7
  cloudnetpy/metadata.py,sha256=Bcu1a9UyUq61jomuZ0_6hYIOzf61e5qCXeiwLm46ikw,5040
8
8
  cloudnetpy/output.py,sha256=jD1pfBb4OQhVOrlhPEk-8FAi4bUW7zjAL468r6BPkJg,14586
9
9
  cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  cloudnetpy/utils.py,sha256=yY5a5HLuAks2uzA4XbbqsGFEmXoyqECn_TjD3sMa0lI,27193
11
- cloudnetpy/version.py,sha256=lnEVFXHSKtSjA3iEu1JkOORO-IrhXQfth6GZQDdyhv4,72
11
+ cloudnetpy/version.py,sha256=KcLO5w70O8aXSbgK5nqMVq2YM5wAvubh8Uf9D1Q4FNc,72
12
12
  cloudnetpy/categorize/__init__.py,sha256=gP5q3Vis1y9u9OWgA_idlbjfWXYN_S0IBSWdwBhL_uU,69
13
13
  cloudnetpy/categorize/atmos.py,sha256=cax3iRmvr7S-VkUZqz0JCfAN3WEsUVbGfH4zSHy1APo,12384
14
14
  cloudnetpy/categorize/atmos_utils.py,sha256=wndpwJxc2-QnNTkV8tc8I11Vs_WkNz9sVMX1fuGgUC4,3777
@@ -33,7 +33,7 @@ cloudnetpy/instruments/cl61d.py,sha256=ycJGvUqNU2KHhECbrSehtWRnvg1vKFHhvMeQpjpdC
33
33
  cloudnetpy/instruments/cloudnet_instrument.py,sha256=RG5HJxGM6p0F-IGyr85fvOizcMmgx48OeD_XeIsrgSU,3367
34
34
  cloudnetpy/instruments/copernicus.py,sha256=AT0AtMhGSKzPWEqXsfAda6zeaw4g0Jr5dqIyfeu4FP0,6327
35
35
  cloudnetpy/instruments/galileo.py,sha256=FyFYh1JhWed1d6yvpsv3rdBzHUTjkfOPw5HEQz4Wssw,4545
36
- cloudnetpy/instruments/hatpro.py,sha256=TK-7KcpI7E1ErEq9XmQzJPEqoenjOePfkieD0ybEPW4,8099
36
+ cloudnetpy/instruments/hatpro.py,sha256=TEwPyUsT8J17uDPwcB5B8nPDo0f6UAvg5bDjG8tKLdA,8119
37
37
  cloudnetpy/instruments/instruments.py,sha256=GcUEbQFPHUhRTnp300AZ2PK0O2d2EPIGtHqCX6dy99M,3133
38
38
  cloudnetpy/instruments/lufft.py,sha256=nozeiMRMz7I6q_FwmlxDGhWeJlqTuNh6ru39-M4K3BI,3629
39
39
  cloudnetpy/instruments/mira.py,sha256=TfozpYivQAThZ_rV3gLzZpz2QyJFWOF0RXdzA4521rM,9332
@@ -91,9 +91,9 @@ cloudnetpy/model_evaluation/tests/unit/test_plotting.py,sha256=h9V8JKmrO4v9bOvv-
91
91
  cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py,sha256=Ra3r4V0qbqkpDuaTYvEIbaasl0nZ5gmTLR4eGC0glBQ,9724
92
92
  cloudnetpy/model_evaluation/tests/unit/test_tools.py,sha256=Ia_VrLdV2NstX5gbx_3AZTOAlrgLAy_xFZ8fHYVX0xI,3817
93
93
  cloudnetpy/plotting/__init__.py,sha256=2-8x8d1AfAhfU15RwWhusD0Wot_g6Ob_jJoywbrTC7A,95
94
- cloudnetpy/plotting/plot_meta.py,sha256=WNTeSEpACN5vV1omoZfXtRdIxrohYHJU3v2lHjspQRM,12823
95
- cloudnetpy/plotting/plotting.py,sha256=Br5rBWZtP14moM81LdMs0mb-NhDFVAcRizOYlg8edb8,23933
96
- cloudnetpy/products/__init__.py,sha256=hGkngQT-YAC5cmDiHkSkQw2ZBrg0hN2z40Fizz0QU5Y,210
94
+ cloudnetpy/plotting/plot_meta.py,sha256=xbiEU9Okx5L_WG7KtVnljxjpCYcgqyOLywBc49gibSQ,13033
95
+ cloudnetpy/plotting/plotting.py,sha256=NjeWF5u7fSvNVCwvOu8RyxQ7bApm7K74CF4I2sDCR40,26868
96
+ cloudnetpy/products/__init__.py,sha256=2hRb5HG9hNrxH1if5laJkLeFeaZCd5W1q3hh4ewsX0E,273
97
97
  cloudnetpy/products/classification.py,sha256=J_FOMUSyxvFaT-hvdKVVcKPtuQ0u3V9PsV5xaIKzMjg,7843
98
98
  cloudnetpy/products/der.py,sha256=HAdPvbJySEqkIwDrdZDPnli_wnN2qwm72_D1a82ZWIs,12398
99
99
  cloudnetpy/products/drizzle.py,sha256=BY2HvJeWt_ps6KKCGXwUUNRTy78q0cQM8bOCCoj8TWA,10803
@@ -103,12 +103,11 @@ cloudnetpy/products/ier.py,sha256=IcGPlQahbwJjp3vOOrxWSYW2FPzbSV0KQL5eYECc4kU,77
103
103
  cloudnetpy/products/iwc.py,sha256=MUPuVKWgqOuuLRCGk3QY74uBZB_7P1qlinlP8nEvz9o,10124
104
104
  cloudnetpy/products/lwc.py,sha256=TbIR6kMwjbm63ed5orB1pkqx9ZBm8C5TF2JmT8WKdKI,18794
105
105
  cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe55y9ob58,16637951
106
- cloudnetpy/products/mwr_multi.py,sha256=mRMzdDhTddhPLBcDiG_2kw_BSRL_01hA9-o4-cmaVaQ,3301
107
- cloudnetpy/products/mwr_single.py,sha256=4KyxeFg7AphEJg5P7ey8SXacyNAG3PGDOvnksvZj3R8,3168
106
+ cloudnetpy/products/mwr_tools.py,sha256=TsVEqNwzoDv90TgzUSnJjMuc3C1KQ-hwsIZ8t0IdDJ4,4407
108
107
  cloudnetpy/products/product_tools.py,sha256=E8CSijBY8cr70BH2JFa0lGQ-RzI9EcHQ0Fzt8CQ8rY4,10442
109
108
  docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
110
- cloudnetpy-1.56.4.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
111
- cloudnetpy-1.56.4.dist-info/METADATA,sha256=R6YZSD8wmlEcw9Oz2cnyWjGz5YAltO9g3OT0ZQblaH0,5733
112
- cloudnetpy-1.56.4.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
113
- cloudnetpy-1.56.4.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
114
- cloudnetpy-1.56.4.dist-info/RECORD,,
109
+ cloudnetpy-1.56.6.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
110
+ cloudnetpy-1.56.6.dist-info/METADATA,sha256=kr-kst-d8xZSZA6N5IpUhsoRdab_rPEEXAmWlPS1pXo,5733
111
+ cloudnetpy-1.56.6.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
112
+ cloudnetpy-1.56.6.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
113
+ cloudnetpy-1.56.6.dist-info/RECORD,,
@@ -1,82 +0,0 @@
1
- import tempfile
2
- from tempfile import NamedTemporaryFile
3
-
4
- import netCDF4
5
- from mwrpy.level2.write_lev2_nc import MissingInputData, lev2_to_nc
6
- from mwrpy.version import __version__ as mwrpy_version
7
-
8
- from cloudnetpy import output, utils
9
- from cloudnetpy.exceptions import MissingInputFileError
10
- from cloudnetpy.products import product_tools
11
-
12
-
13
- def generate_mwr_multi(
14
- mwr_l1c_file: str,
15
- output_file: str,
16
- uuid: str | None = None,
17
- ) -> str:
18
- file_uuid = uuid if uuid is not None else utils.get_uuid()
19
-
20
- with (
21
- NamedTemporaryFile() as temp_file,
22
- NamedTemporaryFile() as abs_hum_file,
23
- NamedTemporaryFile() as rel_hum_file,
24
- NamedTemporaryFile() as t_pot_file,
25
- NamedTemporaryFile() as eq_temp_file,
26
- tempfile.TemporaryDirectory() as temp_dir,
27
- ):
28
- coeffs = product_tools.get_read_mwrpy_coeffs(mwr_l1c_file, temp_dir)
29
-
30
- for prod, file in zip(
31
- ("2P02", "2P03", "2P04", "2P07", "2P08"),
32
- (temp_file, abs_hum_file, rel_hum_file, t_pot_file, eq_temp_file),
33
- strict=True,
34
- ):
35
- try:
36
- lev2_to_nc(
37
- prod,
38
- mwr_l1c_file,
39
- file.name,
40
- coeff_files=coeffs,
41
- temp_file=temp_file.name if prod not in ("2P02", "2P03") else None,
42
- hum_file=abs_hum_file.name
43
- if prod not in ("2P02", "2P03")
44
- else None,
45
- )
46
- except MissingInputData as err:
47
- raise MissingInputFileError from err
48
-
49
- with (
50
- netCDF4.Dataset(output_file, "w", format="NETCDF4_CLASSIC") as nc_output,
51
- netCDF4.Dataset(mwr_l1c_file, "r") as nc_l1c,
52
- netCDF4.Dataset(temp_file.name, "r") as nc_temp,
53
- netCDF4.Dataset(rel_hum_file.name, "r") as nc_rel_hum,
54
- netCDF4.Dataset(t_pot_file.name, "r") as nc_t_pot,
55
- netCDF4.Dataset(eq_temp_file.name, "r") as nc_eq_temp,
56
- ):
57
- nc_output.createDimension("time", len(nc_temp.variables["time"][:]))
58
- nc_output.createDimension("height", len(nc_temp.variables["height"][:]))
59
-
60
- for source, variables in (
61
- (nc_l1c, ("latitude", "longitude", "altitude")),
62
- (nc_temp, ("time", "height", "temperature")),
63
- (nc_rel_hum, ("relative_humidity",)),
64
- (nc_t_pot, ("potential_temperature",)),
65
- (nc_eq_temp, ("equivalent_potential_temperature",)),
66
- ):
67
- output.copy_variables(source, nc_output, variables)
68
-
69
- output.add_standard_global_attributes(nc_output, file_uuid)
70
- output.copy_global(nc_l1c, nc_output, ("year", "month", "day", "location"))
71
- nc_output.title = f"MWR multiple-pointing from {nc_l1c.location}"
72
- nc_output.cloudnet_file_type = "mwr-multi"
73
- nc_output.mwrpy_version = mwrpy_version
74
- output.fix_time_attributes(nc_output)
75
- nc_output.history = (
76
- f"{utils.get_time()} - MWR multiple-pointing product created \n"
77
- f"{nc_l1c.history}"
78
- )
79
- nc_output.source_file_uuids = nc_l1c.file_uuid
80
- nc_output.source = nc_l1c.source
81
-
82
- return file_uuid
@@ -1,83 +0,0 @@
1
- import tempfile
2
- from tempfile import NamedTemporaryFile
3
-
4
- import netCDF4
5
- from mwrpy.level2.write_lev2_nc import lev2_to_nc
6
- from mwrpy.version import __version__ as mwrpy_version
7
-
8
- from cloudnetpy import output, utils
9
- from cloudnetpy.products import product_tools
10
-
11
-
12
- def generate_mwr_single(
13
- mwr_l1c_file: str,
14
- output_file: str,
15
- uuid: str | None = None,
16
- ) -> str:
17
- file_uuid = uuid if uuid is not None else utils.get_uuid()
18
-
19
- with (
20
- NamedTemporaryFile() as lwp_file,
21
- NamedTemporaryFile() as iwv_file,
22
- NamedTemporaryFile() as t_prof_file,
23
- NamedTemporaryFile() as abs_hum_file,
24
- tempfile.TemporaryDirectory() as temp_dir,
25
- ):
26
- coeffs = product_tools.get_read_mwrpy_coeffs(mwr_l1c_file, temp_dir)
27
-
28
- for prod, file in zip(
29
- ("2I01", "2I02", "2P01", "2P03"),
30
- (lwp_file, iwv_file, t_prof_file, abs_hum_file),
31
- strict=True,
32
- ):
33
- lev2_to_nc(prod, mwr_l1c_file, file.name, coeff_files=coeffs)
34
-
35
- with (
36
- netCDF4.Dataset(output_file, "w", format="NETCDF4_CLASSIC") as nc_output,
37
- netCDF4.Dataset(lwp_file.name, "r") as nc_lwp,
38
- netCDF4.Dataset(iwv_file.name, "r") as nc_iwv,
39
- netCDF4.Dataset(abs_hum_file.name, "r") as nc_hum,
40
- netCDF4.Dataset(t_prof_file.name, "r") as nc_t_prof,
41
- netCDF4.Dataset(mwr_l1c_file, "r") as nc_l1c,
42
- ):
43
- nc_output.createDimension("height", len(nc_t_prof.variables["height"][:]))
44
- nc_output.createDimension("time", len(nc_lwp.variables["time"][:]))
45
-
46
- for source, variables in (
47
- (nc_iwv, ("iwv",)),
48
- (nc_hum, ("absolute_humidity",)),
49
- (nc_t_prof, ("temperature", "height")),
50
- (nc_l1c, ("latitude", "longitude", "altitude")),
51
- (
52
- nc_lwp,
53
- (
54
- "time",
55
- "lwp",
56
- "lwp_random_error",
57
- "lwp_offset",
58
- "lwp_systematic_error",
59
- "azimuth_angle",
60
- ),
61
- ),
62
- ):
63
- output.copy_variables(source, nc_output, variables)
64
-
65
- output.add_standard_global_attributes(nc_output, file_uuid)
66
- output.copy_global(nc_l1c, nc_output, ("year", "month", "day", "location"))
67
- nc_output.title = f"MWR single-pointing from {nc_l1c.location}"
68
- nc_output.cloudnet_file_type = "mwr-single"
69
- nc_output.mwrpy_version = mwrpy_version
70
- output.fix_time_attributes(nc_output)
71
- output.replace_attribute_with_standard_value(
72
- nc_output,
73
- ("lwp", "iwv", "temperature", "azimuth_angle"),
74
- ("units", "long_name", "standard_name"),
75
- )
76
- nc_output.history = (
77
- f"{utils.get_time()} - MWR single-pointing product created \n"
78
- f"{nc_l1c.history}"
79
- )
80
- nc_output.source_file_uuids = nc_l1c.file_uuid
81
- nc_output.source = nc_l1c.source
82
-
83
- return file_uuid