cloudnetpy 1.80.8__py3-none-any.whl → 1.81.1__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 +28 -21
  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 +22 -12
  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.8.dist-info → cloudnetpy-1.81.1.dist-info}/METADATA +3 -2
  78. cloudnetpy-1.81.1.dist-info/RECORD +126 -0
  79. cloudnetpy-1.80.8.dist-info/RECORD +0 -126
  80. {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.1.dist-info}/WHEEL +0 -0
  81. {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.1.dist-info}/entry_points.txt +0 -0
  82. {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.1.dist-info}/licenses/LICENSE +0 -0
  83. {cloudnetpy-1.80.8.dist-info → cloudnetpy-1.81.1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,7 @@
1
1
  import netCDF4
2
2
  import numpy as np
3
+ import numpy.typing as npt
4
+ from matplotlib.axes import Axes
3
5
  from numpy import ma
4
6
 
5
7
  from cloudnetpy.model_evaluation.model_metadata import MODELS
@@ -109,14 +111,14 @@ def mask_small_values(data: ma.MaskedArray, name: str) -> ma.MaskedArray:
109
111
  return data
110
112
 
111
113
 
112
- def reshape_1d2nd(one_d: np.ndarray, two_d: np.ndarray) -> np.ndarray:
114
+ def reshape_1d2nd(one_d: npt.NDArray, two_d: npt.NDArray) -> npt.NDArray:
113
115
  new_arr = np.zeros(two_d.shape)
114
116
  for i in range(len(two_d[0])):
115
117
  new_arr[:, i] = one_d
116
118
  return new_arr
117
119
 
118
120
 
119
- def create_segment_values(model: ma.MaskedArray, obs: ma.MaskedArray) -> np.ndarray:
121
+ def create_segment_values(model: ma.MaskedArray, obs: ma.MaskedArray) -> npt.NDArray:
120
122
  new_array = np.zeros(model.shape, dtype=int)
121
123
  new_array[model.mask & obs.mask] = 0 # No data
122
124
  new_array[~model.mask & obs.mask] = 1 # Only model
@@ -125,12 +127,12 @@ def create_segment_values(model: ma.MaskedArray, obs: ma.MaskedArray) -> np.ndar
125
127
  return new_array
126
128
 
127
129
 
128
- def set_yaxis(ax, max_y: float, min_y: float = 0.0) -> None:
130
+ def set_yaxis(ax: Axes, max_y: float, min_y: float = 0.0) -> None:
129
131
  ax.set_ylim(min_y, max_y)
130
132
  ax.set_ylabel("Height (km)", fontsize=13)
131
133
 
132
134
 
133
- def rolling_mean(data: ma.MaskedArray, n: int = 4) -> np.ndarray:
135
+ def rolling_mean(data: ma.MaskedArray, n: int = 4) -> npt.NDArray:
134
136
  mmr = []
135
137
  for i in range(len(data)):
136
138
  if not data[i : i + n].mask.all():
@@ -143,7 +145,7 @@ def rolling_mean(data: ma.MaskedArray, n: int = 4) -> np.ndarray:
143
145
  def change2one_dim_axes(
144
146
  x: ma.MaskedArray,
145
147
  y: ma.MaskedArray,
146
- data: np.ndarray,
148
+ data: npt.NDArray,
147
149
  ) -> tuple:
148
150
  # If any mask in x or y, change 2d to 1d axes values
149
151
  # Common shape need to match 2d data.
@@ -7,8 +7,10 @@ import matplotlib as mpl
7
7
  import matplotlib.pyplot as plt
8
8
  import netCDF4
9
9
  import numpy as np
10
+ import numpy.typing as npt
10
11
  from matplotlib.axes import Axes
11
12
  from matplotlib.colorbar import Colorbar
13
+ from matplotlib.colorizer import ColorizingArtist
12
14
  from matplotlib.colors import ListedColormap
13
15
  from matplotlib.patches import Patch
14
16
  from matplotlib.pyplot import Figure
@@ -17,7 +19,7 @@ from numpy import ma
17
19
 
18
20
  import cloudnetpy.model_evaluation.plotting.plot_tools as p_tools
19
21
  from cloudnetpy.model_evaluation.model_metadata import MODELS
20
- from cloudnetpy.model_evaluation.plotting.plot_meta import ATTRIBUTES
22
+ from cloudnetpy.model_evaluation.plotting.plot_meta import ATTRIBUTES, PlotMeta
21
23
  from cloudnetpy.model_evaluation.statistics.statistical_methods import DayStatistics
22
24
  from cloudnetpy.plotting.plotting import Dimensions, get_log_cbar_tick_labels, lin2log
23
25
 
@@ -87,7 +89,7 @@ def generate_L3_day_plots(
87
89
  >>> fig_type='statistic', stats=['error'])
88
90
  """
89
91
 
90
- def _check_cycle_names():
92
+ def _check_cycle_names() -> None:
91
93
  if not c_names:
92
94
  raise AttributeError
93
95
 
@@ -342,7 +344,9 @@ def get_single_plots(
342
344
  return figs, axes
343
345
 
344
346
 
345
- def plot_colormesh(ax, data: np.ndarray, axes: tuple, variable_info) -> None:
347
+ def plot_colormesh(
348
+ ax: Axes, data: npt.NDArray, axes: tuple, variable_info: PlotMeta
349
+ ) -> None:
346
350
  vmin, vmax = variable_info.plot_range
347
351
  if variable_info.plot_scale == "logarithmic":
348
352
  data, vmin, vmax = lin2log(data, vmin, vmax)
@@ -355,7 +359,8 @@ def plot_colormesh(ax, data: np.ndarray, axes: tuple, variable_info) -> None:
355
359
  colorbar.set_ticks(np.arange(vmin, vmax + 1).tolist()) # type: ignore[arg-type]
356
360
  colorbar.ax.set_yticklabels(tick_labels)
357
361
  ax.set_facecolor("white")
358
- colorbar.set_label(variable_info.clabel, fontsize=13)
362
+ if variable_info.clabel is not None:
363
+ colorbar.set_label(variable_info.clabel, fontsize=13)
359
364
 
360
365
 
361
366
  def get_statistic_plots(
@@ -398,15 +403,15 @@ def get_statistic_plots(
398
403
  name = ""
399
404
  j = 0
400
405
 
401
- def _check_data():
406
+ def _check_data() -> None:
402
407
  if model_missing and obs_missing:
403
408
  _raise()
404
409
 
405
- def _check_data2():
410
+ def _check_data2() -> None:
406
411
  if "error" in stat and np.all(day_stat.model_stat.mask is True):
407
412
  _raise()
408
413
 
409
- def _raise():
414
+ def _raise() -> None:
410
415
  err_msg = f"No data in {model_name} or observation"
411
416
  raise ValueError(err_msg)
412
417
 
@@ -476,13 +481,13 @@ def get_statistic_plots(
476
481
  def initialize_statistic_plots(
477
482
  j: int,
478
483
  max_len: int,
479
- ax,
484
+ ax: Axes,
480
485
  method: str,
481
486
  day_stat: DayStatistics,
482
487
  model: ma.MaskedArray,
483
488
  obs: ma.MaskedArray,
484
489
  args: tuple,
485
- variable_info,
490
+ variable_info: PlotMeta,
486
491
  *,
487
492
  title: bool = True,
488
493
  include_xlimits: bool = False,
@@ -542,7 +547,9 @@ def initialize_statistic_plots(
542
547
  )
543
548
 
544
549
 
545
- def plot_relative_error(ax, error: ma.MaskedArray, axes: tuple, method: str) -> None:
550
+ def plot_relative_error(
551
+ ax: Axes, error: ma.MaskedArray, axes: tuple, method: str
552
+ ) -> None:
546
553
  pl = ax.pcolormesh(*axes, error[:-1, :-1].T, cmap="RdBu_r", vmin=-50, vmax=50)
547
554
  colorbar = init_colorbar(pl, ax)
548
555
  colorbar.set_label("%", fontsize=13)
@@ -569,7 +576,7 @@ def plot_relative_error(ax, error: ma.MaskedArray, axes: tuple, method: str) ->
569
576
 
570
577
 
571
578
  def plot_data_area(
572
- ax,
579
+ ax: Axes,
573
580
  day_stat: DayStatistics,
574
581
  model: ma.MaskedArray,
575
582
  obs: ma.MaskedArray,
@@ -612,13 +619,13 @@ def plot_data_area(
612
619
  )
613
620
 
614
621
 
615
- def plot_histogram(ax, day_stat: DayStatistics, variable_info) -> None:
622
+ def plot_histogram(ax: Axes, day_stat: DayStatistics, variable_info: PlotMeta) -> None:
616
623
  weights = np.ones_like(day_stat.model_stat) / float(len(day_stat.model_stat))
617
624
  hist_bins = np.histogram(day_stat.observation_stat, density=True)[-1]
618
625
  ax.hist(
619
626
  day_stat.model_stat,
620
627
  weights=weights,
621
- bins=hist_bins,
628
+ bins=list(hist_bins),
622
629
  alpha=0.7,
623
630
  facecolor="khaki",
624
631
  edgecolor="k",
@@ -631,13 +638,14 @@ def plot_histogram(ax, day_stat: DayStatistics, variable_info) -> None:
631
638
  ax.hist(
632
639
  day_stat.observation_stat,
633
640
  weights=weights,
634
- bins=hist_bins,
641
+ bins=list(hist_bins),
635
642
  alpha=0.7,
636
643
  facecolor="steelblue",
637
644
  edgecolor="k",
638
645
  label="Observation",
639
646
  )
640
- ax.set_xlabel(variable_info.x_title, fontsize=13)
647
+ if variable_info.x_title is not None:
648
+ ax.set_xlabel(variable_info.x_title, fontsize=13)
641
649
  if variable_info.plot_scale == "logarithmic":
642
650
  ax.ticklabel_format(axis="x", style="sci", scilimits=(0, 0))
643
651
  ax.set_ylabel("Relative frequency %", fontsize=13)
@@ -646,10 +654,10 @@ def plot_histogram(ax, day_stat: DayStatistics, variable_info) -> None:
646
654
 
647
655
 
648
656
  def plot_vertical_profile(
649
- ax,
657
+ ax: Axes,
650
658
  day_stat: DayStatistics,
651
659
  axes: tuple,
652
- variable_info,
660
+ variable_info: PlotMeta,
653
661
  ) -> None:
654
662
  mrm = p_tools.rolling_mean(day_stat.model_stat)
655
663
  orm = p_tools.rolling_mean(day_stat.observation_stat)
@@ -686,7 +694,8 @@ def plot_vertical_profile(
686
694
  ax.plot(orm, axes, "-", color="green", lw=2, label="Mean of observation")
687
695
 
688
696
  ax.set_title(f"{day_stat.title[-1]}", fontsize=14)
689
- ax.set_xlabel(variable_info.x_title, fontsize=13)
697
+ if variable_info.x_title is not None:
698
+ ax.set_xlabel(variable_info.x_title, fontsize=13)
690
699
  if variable_info.plot_scale == "logarithmic":
691
700
  ax.ticklabel_format(axis="x", style="sci", scilimits=(0, 0))
692
701
  ax.yaxis.grid(which="major")
@@ -740,17 +749,17 @@ def initialize_figure(n_subplots: int, stat: str = "") -> tuple[Figure, list[Axe
740
749
  return fig, axes_list
741
750
 
742
751
 
743
- def init_colorbar(plot, axis) -> Colorbar:
752
+ def init_colorbar(plot: ColorizingArtist, axis: Axes) -> Colorbar:
744
753
  divider = make_axes_locatable(axis)
745
754
  cax = divider.append_axes("right", size="1%", pad=0.25)
746
755
  return plt.colorbar(plot, fraction=1.0, ax=axis, cax=cax)
747
756
 
748
757
 
749
758
  def _set_title(
750
- ax,
759
+ ax: Axes,
751
760
  field_name: str,
752
761
  product: str,
753
- variable_info,
762
+ variable_info: PlotMeta,
754
763
  model_name: str = "",
755
764
  ) -> None:
756
765
  """Generates subtitles for different product types."""
@@ -773,14 +782,14 @@ def _set_title(
773
782
  ax.set_title(f"Simulated {name}")
774
783
 
775
784
 
776
- def _get_cf_title(field_name: str, variable_info) -> str:
785
+ def _get_cf_title(field_name: str, variable_info: PlotMeta) -> str:
777
786
  title = f"{variable_info.name}, Area"
778
787
  if "V" in field_name:
779
788
  title = f"{variable_info.name}, Volume"
780
789
  return title
781
790
 
782
791
 
783
- def _get_iwc_title(field_name: str, variable_info) -> str:
792
+ def _get_iwc_title(field_name: str, variable_info: PlotMeta) -> str:
784
793
  name = variable_info.name
785
794
  if "att" in field_name:
786
795
  title = f"{name} with good attenuation"
@@ -791,11 +800,11 @@ def _get_iwc_title(field_name: str, variable_info) -> str:
791
800
  return title
792
801
 
793
802
 
794
- def _get_product_title(variable_info) -> str:
803
+ def _get_product_title(variable_info: PlotMeta) -> str:
795
804
  return f"{variable_info.name}"
796
805
 
797
806
 
798
- def _get_stat_titles(field_name: str, product: str, variable_info) -> str:
807
+ def _get_stat_titles(field_name: str, product: str, variable_info: PlotMeta) -> str:
799
808
  title = _get_product_title_stat(variable_info)
800
809
  if product == "cf":
801
810
  title = _get_cf_title_stat(field_name, variable_info)
@@ -807,7 +816,7 @@ def _get_stat_titles(field_name: str, product: str, variable_info) -> str:
807
816
  return title
808
817
 
809
818
 
810
- def _get_cf_title_stat(field_name: str, variable_info) -> str:
819
+ def _get_cf_title_stat(field_name: str, variable_info: PlotMeta) -> str:
811
820
  name = variable_info.name
812
821
  title = f"{name} area"
813
822
  if "V" in field_name:
@@ -815,7 +824,7 @@ def _get_cf_title_stat(field_name: str, variable_info) -> str:
815
824
  return title
816
825
 
817
826
 
818
- def _get_iwc_title_stat(field_name: str, variable_info) -> str:
827
+ def _get_iwc_title_stat(field_name: str, variable_info: PlotMeta) -> str:
819
828
  name = variable_info.name
820
829
  if "att" in field_name:
821
830
  title = f"{name} with good attenuation"
@@ -826,12 +835,12 @@ def _get_iwc_title_stat(field_name: str, variable_info) -> str:
826
835
  return title
827
836
 
828
837
 
829
- def _get_product_title_stat(variable_info) -> str:
838
+ def _get_product_title_stat(variable_info: PlotMeta) -> str:
830
839
  name = variable_info.name
831
840
  return f"{name}"
832
841
 
833
842
 
834
- def set_yax(ax, max_y: float, ylabel: str | None, min_y: float = 0.0) -> None:
843
+ def set_yax(ax: Axes, max_y: float, ylabel: str | None, min_y: float = 0.0) -> None:
835
844
  """Sets yticks, ylim and ylabel for yaxis of axis."""
836
845
  ax.set_ylim(min_y, max_y)
837
846
  ax.set_ylabel("Height (km)", fontsize=13)
@@ -839,7 +848,7 @@ def set_yax(ax, max_y: float, ylabel: str | None, min_y: float = 0.0) -> None:
839
848
  ax.set_ylabel(ylabel, fontsize=13)
840
849
 
841
850
 
842
- def set_xax(ax, *, include_xlimits: bool = False) -> None:
851
+ def set_xax(ax: Axes, *, include_xlimits: bool = False) -> None:
843
852
  """Sets xticks and xtick labels for plt.imshow()."""
844
853
  ticks_x_labels = _get_standard_time_ticks(include_xlimits=include_xlimits)
845
854
  ax.set_xticks(np.arange(0, 25, 4, dtype=int))
@@ -864,7 +873,7 @@ def _get_standard_time_ticks(
864
873
  ]
865
874
 
866
875
 
867
- def set_labels(fig, ax, nc_file: str, *, sub_title: bool = True) -> date:
876
+ def set_labels(fig: Figure, ax: Axes, nc_file: str, *, sub_title: bool = True) -> date:
868
877
  ax.set_xlabel("Time (UTC)", fontsize=13)
869
878
  case_date = read_date(nc_file)
870
879
  site_name = read_location(nc_file)
@@ -885,7 +894,7 @@ def read_date(nc_file: str) -> date:
885
894
  return date(int(nc.year), int(nc.month), int(nc.day))
886
895
 
887
896
 
888
- def add_subtitle(fig, case_date: date, site_name: str) -> None:
897
+ def add_subtitle(fig: Figure, case_date: date, site_name: str) -> None:
889
898
  """Adds subtitle into figure."""
890
899
  text = _get_subtitle_text(case_date, site_name)
891
900
  fig.suptitle(
@@ -1,7 +1,9 @@
1
1
  import importlib
2
2
  import logging
3
+ from os import PathLike
3
4
 
4
5
  import numpy as np
6
+ import numpy.typing as npt
5
7
  import scipy.special
6
8
  import scipy.stats
7
9
  from numpy import ma
@@ -25,9 +27,9 @@ class AdvanceProductMethods(DataSource):
25
27
  def __init__(
26
28
  self,
27
29
  model_obj: ModelManager,
28
- model_file: str,
30
+ model_file: str | PathLike,
29
31
  obs_obj: ObservationManager,
30
- ):
32
+ ) -> None:
31
33
  super().__init__(model_file)
32
34
  self._obs_obj = obs_obj
33
35
  self.product = obs_obj.obs
@@ -96,12 +98,12 @@ class AdvanceProductMethods(DataSource):
96
98
  f"{self._model_obj.model}{self._model_obj.cycle}_cf_cirrus",
97
99
  )
98
100
 
99
- def getvar_from_object(self, arg: str) -> np.ndarray:
101
+ def getvar_from_object(self, arg: str) -> npt.NDArray:
100
102
  v_name = arg if arg == "cf" else self._model_obj.get_model_var_names((arg,))[0]
101
103
  key = f"{self._model_obj.model}{self._model_obj.cycle}_{v_name}"
102
104
  return self._model_obj.data[key][:]
103
105
 
104
- def remove_extra_levels(self, arg: np.ndarray) -> np.ndarray:
106
+ def remove_extra_levels(self, arg: npt.NDArray) -> npt.NDArray:
105
107
  return self._model_obj.cut_off_extra_levels(arg)
106
108
 
107
109
  def set_frequency_parameters(self) -> tuple:
@@ -114,7 +116,7 @@ class AdvanceProductMethods(DataSource):
114
116
  return 0.00058, -0.00706, 0.0923, -0.992
115
117
  raise ValueError
116
118
 
117
- def fit_z_sensitivity(self, h: np.ndarray) -> np.ndarray:
119
+ def fit_z_sensitivity(self, h: npt.NDArray) -> npt.NDArray:
118
120
  if self._obs_obj.z_sensitivity is None:
119
121
  msg = "No z_sensitivity in observation file"
120
122
  raise ValueError(msg)
@@ -129,10 +131,10 @@ class AdvanceProductMethods(DataSource):
129
131
 
130
132
  def filter_high_iwc_low_cf(
131
133
  self,
132
- cf: np.ndarray,
133
- iwc: np.ndarray,
134
- lwc: np.ndarray,
135
- ) -> np.ndarray:
134
+ cf: npt.NDArray,
135
+ iwc: npt.NDArray,
136
+ lwc: npt.NDArray,
137
+ ) -> npt.NDArray:
136
138
  cf_filtered = self.mask_weird_indices(cf, iwc, lwc)
137
139
  if np.sum((iwc > 0) & (lwc < iwc / 10) & (cf_filtered > 0)) == 0:
138
140
  msg = "No ice clouds in a input data"
@@ -141,10 +143,10 @@ class AdvanceProductMethods(DataSource):
141
143
 
142
144
  @staticmethod
143
145
  def mask_weird_indices(
144
- cf: np.ndarray,
145
- iwc: np.ndarray,
146
- lwc: np.ndarray,
147
- ) -> np.ndarray:
146
+ cf: npt.NDArray,
147
+ iwc: npt.NDArray,
148
+ lwc: npt.NDArray,
149
+ ) -> npt.NDArray:
148
150
  cf_filtered = ma.copy(cf)
149
151
  weird_ind = (iwc / cf > 0.5e-3) & (cf < 0.001)
150
152
  weird_ind = weird_ind | (iwc == 0) & (lwc == 0) & (cf == 0)
@@ -153,23 +155,23 @@ class AdvanceProductMethods(DataSource):
153
155
 
154
156
  def find_ice_in_clouds(
155
157
  self,
156
- cf_filtered: np.ndarray,
157
- iwc: np.ndarray,
158
- lwc: np.ndarray,
159
- ) -> tuple[np.ndarray, tuple]:
158
+ cf_filtered: npt.NDArray,
159
+ iwc: npt.NDArray,
160
+ lwc: npt.NDArray,
161
+ ) -> tuple[npt.NDArray, tuple]:
160
162
  ice_ind = self.get_ice_indices(cf_filtered, iwc, lwc)
161
163
  cloud_iwc = iwc[ice_ind] / cf_filtered[ice_ind] * 1e3
162
164
  return cloud_iwc, ice_ind
163
165
 
164
166
  @staticmethod
165
167
  def get_ice_indices(
166
- cf_filtered: np.ndarray,
167
- iwc: np.ndarray,
168
- lwc: np.ndarray,
168
+ cf_filtered: npt.NDArray,
169
+ iwc: npt.NDArray,
170
+ lwc: npt.NDArray,
169
171
  ) -> tuple:
170
172
  return tuple(np.where((cf_filtered > 0) & (iwc > 0) & (lwc < iwc / 10)))
171
173
 
172
- def iwc_variance(self, height: np.ndarray, ice_ind: tuple) -> np.ndarray:
174
+ def iwc_variance(self, height: npt.NDArray, ice_ind: tuple) -> npt.NDArray:
173
175
  u = self.getvar("uwind")
174
176
  v = self.getvar("vwind")
175
177
  u = self.remove_extra_levels(u)
@@ -177,7 +179,9 @@ class AdvanceProductMethods(DataSource):
177
179
  w_shear = self.calculate_wind_shear(self._model_obj.wind, u, v, height)
178
180
  return self.calculate_variance_iwc(w_shear, ice_ind)
179
181
 
180
- def calculate_variance_iwc(self, w_shear: np.ndarray, ice_ind: tuple) -> np.ndarray:
182
+ def calculate_variance_iwc(
183
+ self, w_shear: npt.NDArray, ice_ind: tuple
184
+ ) -> npt.NDArray:
181
185
  return 10 ** (
182
186
  0.3 * np.log10(self._model_obj.resolution_h)
183
187
  - 0.04 * w_shear[ice_ind]
@@ -186,11 +190,11 @@ class AdvanceProductMethods(DataSource):
186
190
 
187
191
  @staticmethod
188
192
  def calculate_wind_shear(
189
- wind,
190
- u: np.ndarray,
191
- v: np.ndarray,
192
- height: np.ndarray,
193
- ) -> np.ndarray:
193
+ wind: npt.NDArray,
194
+ u: npt.NDArray,
195
+ v: npt.NDArray,
196
+ height: npt.NDArray,
197
+ ) -> npt.NDArray:
194
198
  grand_winds = []
195
199
  for w in (wind, u, v):
196
200
  grad_w = np.zeros(w.shape)
@@ -211,7 +215,7 @@ class AdvanceProductMethods(DataSource):
211
215
  f_variance_iwc: float,
212
216
  n_std: int = 5,
213
217
  n_dist: int = 250,
214
- ) -> np.ndarray:
218
+ ) -> npt.NDArray:
215
219
  finish = cloud_iwc + n_std * (ma.sqrt(f_variance_iwc) * cloud_iwc)
216
220
  if isinstance(finish, ma.MaskedArray) and finish.mask.all():
217
221
  raise ValueError
@@ -223,16 +227,16 @@ class AdvanceProductMethods(DataSource):
223
227
 
224
228
  @staticmethod
225
229
  def gamma_distribution(
226
- iwc_dist: np.ndarray, f_variance_iwc: float, cloud_iwc: float
227
- ) -> np.ndarray:
230
+ iwc_dist: npt.NDArray, f_variance_iwc: float, cloud_iwc: float
231
+ ) -> npt.NDArray:
228
232
  alpha = 1 / f_variance_iwc
229
233
  theta = cloud_iwc / alpha
230
234
  return scipy.stats.gamma.pdf(iwc_dist, a=alpha, scale=theta)
231
235
 
232
236
  @staticmethod
233
237
  def filter_cirrus(
234
- p_iwc: np.ndarray,
235
- obs_index: np.ndarray,
236
- cf_filtered: np.ndarray,
237
- ) -> np.ndarray:
238
+ p_iwc: npt.NDArray,
239
+ obs_index: npt.NDArray,
240
+ cf_filtered: npt.NDArray,
241
+ ) -> npt.NDArray:
238
242
  return (ma.sum(p_iwc * obs_index) / ma.sum(p_iwc)) * cf_filtered
@@ -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
@@ -19,7 +20,7 @@ class ProductGrid:
19
20
  object which is used for nc-file creation and writing
20
21
  """
21
22
 
22
- def __init__(self, model_obj: ModelManager, obs_obj: ObservationManager):
23
+ def __init__(self, model_obj: ModelManager, obs_obj: ObservationManager) -> None:
23
24
  self._obs_obj = obs_obj
24
25
  self._date = obs_obj.date
25
26
  self._obs_time = tl.time2datetime(obs_obj.time, self._date)
@@ -150,10 +151,10 @@ class ProductGrid:
150
151
 
151
152
  def _reshape_data_to_window(
152
153
  self,
153
- ind: np.ndarray,
154
- x_ind: np.ndarray,
155
- y_ind: np.ndarray,
156
- ) -> np.ndarray | None:
154
+ ind: npt.NDArray,
155
+ x_ind: npt.NDArray,
156
+ y_ind: npt.NDArray,
157
+ ) -> npt.NDArray | None:
157
158
  """Reshapes True observation values to windows shape."""
158
159
  window_size = tl.get_obs_window_size(x_ind, y_ind)
159
160
  if window_size is not None:
@@ -161,7 +162,7 @@ class ProductGrid:
161
162
  return None
162
163
 
163
164
  @staticmethod
164
- def _regrid_cf(storage: dict, i: int, j: int, data: np.ndarray) -> dict:
165
+ def _regrid_cf(storage: dict, i: int, j: int, data: npt.NDArray) -> dict:
165
166
  """Calculates average cloud fraction value to grid point."""
166
167
  data_ma = ma.array(data) if not isinstance(data, ma.MaskedArray) else data
167
168
  for key, downsample in storage.items():
@@ -176,8 +177,8 @@ class ProductGrid:
176
177
  storage: dict,
177
178
  i: int,
178
179
  j: int,
179
- ind_rain: np.ndarray,
180
- ind_no_rain: np.ndarray,
180
+ ind_rain: npt.NDArray,
181
+ ind_no_rain: npt.NDArray,
181
182
  ) -> dict:
182
183
  """Calculates average iwc value for each grid point."""
183
184
  for key, down_sample in storage.items():
@@ -197,7 +198,7 @@ class ProductGrid:
197
198
  storage[key] = down_sample
198
199
  return storage
199
200
 
200
- def _regrid_product(self, storage: dict, i: int, j: int, ind: np.ndarray) -> dict:
201
+ def _regrid_product(self, storage: dict, i: int, j: int, ind: npt.NDArray) -> dict:
201
202
  """Calculates average of standard product value for each grid point."""
202
203
  for key, down_sample in storage.items():
203
204
  obs_data_selected = ma.masked_invalid(self._obs_data[ind])
@@ -1,7 +1,10 @@
1
1
  import importlib
2
2
  import logging
3
+ import os.path
4
+ from os import PathLike
3
5
 
4
6
  import numpy as np
7
+ import numpy.typing as npt
5
8
  from numpy import ma
6
9
 
7
10
  from cloudnetpy.datasource import DataSource
@@ -32,13 +35,13 @@ class ModelManager(DataSource):
32
35
 
33
36
  def __init__(
34
37
  self,
35
- model_file: str,
38
+ model_file: str | PathLike,
36
39
  model: str,
37
- output_file: str,
40
+ output_file: str | PathLike,
38
41
  product: str,
39
42
  *,
40
43
  check_file: bool = True,
41
- ):
44
+ ) -> None:
42
45
  super().__init__(model_file)
43
46
  self.model = model
44
47
  self.model_info = MODELS[model]
@@ -53,15 +56,16 @@ class ModelManager(DataSource):
53
56
  self.wind = self._calculate_wind_speed()
54
57
  self.resolution_h = self._get_horizontal_resolution()
55
58
 
56
- def _read_cycle_name(self, model_file: str) -> str:
59
+ def _read_cycle_name(self, model_file: str | PathLike) -> str:
57
60
  """Get cycle name from model_metadata.py for saving variable name(s)."""
61
+ basename = os.path.basename(model_file)
58
62
  try:
59
63
  cycles = self.model_info.cycle
60
64
  if cycles is None:
61
65
  return ""
62
66
  cycles_split = [x.strip() for x in cycles.split(",")]
63
67
  for cycle in cycles_split:
64
- if cycle in model_file:
68
+ if cycle in basename:
65
69
  return f"_{cycle}"
66
70
  except AttributeError:
67
71
  return ""
@@ -106,7 +110,7 @@ class ModelManager(DataSource):
106
110
  var.append(VARIABLES[arg].long_name)
107
111
  return var
108
112
 
109
- def get_water_content(self, var: str) -> np.ndarray:
113
+ def get_water_content(self, var: str) -> npt.NDArray:
110
114
  p_name = self.get_model_var_names(("p",))[0]
111
115
  t_name = self.get_model_var_names(("T",))[0]
112
116
  lwc_name = self.get_model_var_names((var,))[0]
@@ -119,7 +123,9 @@ class ModelManager(DataSource):
119
123
  return wc
120
124
 
121
125
  @staticmethod
122
- def _calc_water_content(q: np.ndarray, p: np.ndarray, t: np.ndarray) -> np.ndarray:
126
+ def _calc_water_content(
127
+ q: npt.NDArray, p: npt.NDArray, t: npt.NDArray
128
+ ) -> npt.NDArray:
123
129
  return q * p / (287 * t)
124
130
 
125
131
  def _add_variables(self) -> None:
@@ -159,7 +165,7 @@ class ModelManager(DataSource):
159
165
  _add_common_variables()
160
166
  _add_cycle_variables()
161
167
 
162
- def cut_off_extra_levels(self, data: np.ndarray) -> np.ndarray:
168
+ def cut_off_extra_levels(self, data: npt.NDArray) -> npt.NDArray:
163
169
  """Remove unused levels (over 22km) from model data."""
164
170
  try:
165
171
  level = self.model_info.level
@@ -168,7 +174,7 @@ class ModelManager(DataSource):
168
174
 
169
175
  return data[:, :level] if data.ndim > 1 else data[:level]
170
176
 
171
- def _calculate_wind_speed(self) -> np.ndarray:
177
+ def _calculate_wind_speed(self) -> npt.NDArray:
172
178
  """Real wind from x- and y-components."""
173
179
  u = self.getvar("uwind")
174
180
  v = self.getvar("vwind")