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.
- cloudnetpy/categorize/__init__.py +1 -1
- cloudnetpy/categorize/atmos_utils.py +31 -27
- cloudnetpy/categorize/attenuations/__init__.py +4 -4
- cloudnetpy/categorize/attenuations/liquid_attenuation.py +7 -5
- cloudnetpy/categorize/attenuations/melting_attenuation.py +3 -3
- cloudnetpy/categorize/attenuations/rain_attenuation.py +4 -4
- cloudnetpy/categorize/categorize.py +25 -11
- cloudnetpy/categorize/classify.py +9 -8
- cloudnetpy/categorize/containers.py +13 -10
- cloudnetpy/categorize/disdrometer.py +5 -3
- cloudnetpy/categorize/droplet.py +12 -9
- cloudnetpy/categorize/falling.py +9 -8
- cloudnetpy/categorize/freezing.py +10 -7
- cloudnetpy/categorize/insects.py +18 -17
- cloudnetpy/categorize/lidar.py +7 -3
- cloudnetpy/categorize/melting.py +16 -15
- cloudnetpy/categorize/model.py +17 -10
- cloudnetpy/categorize/mwr.py +5 -3
- cloudnetpy/categorize/radar.py +15 -13
- cloudnetpy/cli.py +10 -8
- cloudnetpy/cloudnetarray.py +8 -7
- cloudnetpy/concat_lib.py +29 -20
- cloudnetpy/datasource.py +26 -21
- cloudnetpy/exceptions.py +12 -10
- cloudnetpy/instruments/basta.py +19 -9
- cloudnetpy/instruments/bowtie.py +18 -11
- cloudnetpy/instruments/ceilo.py +22 -10
- cloudnetpy/instruments/ceilometer.py +33 -34
- cloudnetpy/instruments/cl61d.py +5 -3
- cloudnetpy/instruments/cloudnet_instrument.py +7 -7
- cloudnetpy/instruments/copernicus.py +16 -7
- cloudnetpy/instruments/disdrometer/common.py +5 -4
- cloudnetpy/instruments/disdrometer/parsivel.py +14 -9
- cloudnetpy/instruments/disdrometer/thies.py +11 -7
- cloudnetpy/instruments/fd12p.py +7 -6
- cloudnetpy/instruments/galileo.py +16 -7
- cloudnetpy/instruments/hatpro.py +33 -24
- cloudnetpy/instruments/lufft.py +6 -4
- cloudnetpy/instruments/mira.py +33 -19
- cloudnetpy/instruments/mrr.py +12 -12
- cloudnetpy/instruments/nc_lidar.py +1 -1
- cloudnetpy/instruments/nc_radar.py +8 -8
- cloudnetpy/instruments/pollyxt.py +19 -12
- cloudnetpy/instruments/radiometrics.py +17 -10
- cloudnetpy/instruments/rain_e_h3.py +9 -5
- cloudnetpy/instruments/rpg.py +32 -21
- cloudnetpy/instruments/rpg_reader.py +15 -12
- cloudnetpy/instruments/vaisala.py +32 -24
- cloudnetpy/instruments/weather_station.py +22 -19
- cloudnetpy/model_evaluation/file_handler.py +27 -29
- cloudnetpy/model_evaluation/plotting/plot_tools.py +7 -5
- cloudnetpy/model_evaluation/plotting/plotting.py +41 -32
- cloudnetpy/model_evaluation/products/advance_methods.py +38 -34
- cloudnetpy/model_evaluation/products/grid_methods.py +10 -9
- cloudnetpy/model_evaluation/products/model_products.py +15 -9
- cloudnetpy/model_evaluation/products/observation_products.py +12 -10
- cloudnetpy/model_evaluation/products/product_resampling.py +11 -7
- cloudnetpy/model_evaluation/products/tools.py +18 -14
- cloudnetpy/model_evaluation/statistics/statistical_methods.py +6 -5
- cloudnetpy/model_evaluation/tests/unit/test_plotting.py +18 -25
- cloudnetpy/model_evaluation/utils.py +3 -3
- cloudnetpy/output.py +15 -32
- cloudnetpy/plotting/plotting.py +23 -13
- cloudnetpy/products/classification.py +15 -9
- cloudnetpy/products/der.py +24 -19
- cloudnetpy/products/drizzle.py +21 -13
- cloudnetpy/products/drizzle_error.py +8 -7
- cloudnetpy/products/drizzle_tools.py +27 -23
- cloudnetpy/products/epsilon.py +6 -5
- cloudnetpy/products/ier.py +11 -5
- cloudnetpy/products/iwc.py +18 -9
- cloudnetpy/products/lwc.py +41 -31
- cloudnetpy/products/mwr_tools.py +30 -19
- cloudnetpy/products/product_tools.py +23 -19
- cloudnetpy/utils.py +84 -98
- cloudnetpy/version.py +2 -2
- {cloudnetpy-1.80.7.dist-info → cloudnetpy-1.81.0.dist-info}/METADATA +2 -1
- cloudnetpy-1.81.0.dist-info/RECORD +126 -0
- cloudnetpy-1.80.7.dist-info/RECORD +0 -126
- {cloudnetpy-1.80.7.dist-info → cloudnetpy-1.81.0.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.80.7.dist-info → cloudnetpy-1.81.0.dist-info}/entry_points.txt +0 -0
- {cloudnetpy-1.80.7.dist-info → cloudnetpy-1.81.0.dist-info}/licenses/LICENSE +0 -0
- {cloudnetpy-1.80.7.dist-info → cloudnetpy-1.81.0.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:
|
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) ->
|
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) ->
|
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:
|
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(
|
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
|
-
|
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(
|
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
|
-
|
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
|
-
|
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) ->
|
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:
|
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:
|
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:
|
133
|
-
iwc:
|
134
|
-
lwc:
|
135
|
-
) ->
|
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:
|
145
|
-
iwc:
|
146
|
-
lwc:
|
147
|
-
) ->
|
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:
|
157
|
-
iwc:
|
158
|
-
lwc:
|
159
|
-
) -> 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:
|
167
|
-
iwc:
|
168
|
-
lwc:
|
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:
|
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(
|
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:
|
191
|
-
v:
|
192
|
-
height:
|
193
|
-
) ->
|
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
|
-
) ->
|
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:
|
227
|
-
) ->
|
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:
|
235
|
-
obs_index:
|
236
|
-
cf_filtered:
|
237
|
-
) ->
|
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:
|
154
|
-
x_ind:
|
155
|
-
y_ind:
|
156
|
-
) ->
|
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:
|
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:
|
180
|
-
ind_no_rain:
|
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:
|
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
|
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) ->
|
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(
|
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:
|
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) ->
|
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")
|