PySDM_examples.Jensen_and_Nugent_2017.plotting

  1from matplotlib import pyplot
  2import numpy as np
  3from PySDM.physics import si, in_unit
  4from PySDM.physics.constants import PER_CENT
  5
  6CLOUD_BASE = 300 * si.m
  7
  8
  9def find_drop_ids_by_dry_size(plot_drops_with_dry_radii_um, simulation_r_dry):
 10    drop_ids = []
 11    for drop_size_um in plot_drops_with_dry_radii_um:
 12        drop_id = (np.abs(simulation_r_dry - drop_size_um * si.um)).argmin()
 13        drop_ids.append(drop_id)
 14    return drop_ids
 15
 16
 17def compute_table_values(height_above_cb_m, height_cb_m, products, ascent_mask):
 18    """constructing data for Table 4"""
 19    data = {"ascent": [], "descent": []}
 20    for level_idx, height_m in enumerate(products["z"]):
 21        rounded_height_above_cb_m = np.round(height_m - height_cb_m)
 22        if rounded_height_above_cb_m in height_above_cb_m:
 23            r_mean = products["r_mean_act"][level_idx]
 24            r_standard_deviation = products["r_std_act"][level_idx]
 25            r_relative_dispersion = r_standard_deviation / r_mean
 26
 27            data["ascent" if ascent_mask[level_idx] else "descent"].append(
 28                (
 29                    f"{rounded_height_above_cb_m:.0f}",
 30                    f"{in_unit(r_mean, si.um):.2f}",
 31                    f"{in_unit(r_standard_deviation, si.um):.2f}",
 32                    f"{r_relative_dispersion:.3f}",
 33                )
 34            )
 35    data["ascent"].append(data["descent"][0])
 36    return data
 37
 38
 39def figure(
 40    *,
 41    output,
 42    settings,
 43    simulation,
 44    plot_drops_with_dry_radii_um,
 45    xlim_r_um: tuple,
 46    xlim_S_percent: tuple,
 47    return_masks: bool = False,
 48):
 49    y_axis = np.asarray(output["products"]["z"]) - settings.z0 - CLOUD_BASE
 50
 51    masks = {}
 52    if settings.t_end_of_ascent is None:
 53        masks["ascent"] = np.full_like(output["products"]["t"], True, dtype=bool)
 54    else:
 55        masks["ascent"] = np.asarray(output["products"]["t"]) < settings.t_end_of_ascent
 56        masks["descent"] = np.logical_not(masks["ascent"])
 57
 58    colors = {"ascent": "r", "descent": "b"}
 59
 60    _, axs = pyplot.subplot_mosaic(
 61        mosaic=[["r", "S"]], width_ratios=[3, 1], sharey=True, tight_layout=True
 62    )
 63
 64    for label, mask in masks.items():
 65        axs["S"].plot(
 66            in_unit(np.asarray(output["products"]["S_max"]), PER_CENT)[mask],
 67            y_axis[mask],
 68            label=label,
 69            color=colors[label],
 70        )
 71    axs["S"].set_xlim(*xlim_S_percent)
 72    axs["S"].set_xlabel("S (%)", fontsize="15")
 73    axs["S"].legend(fontsize="10")
 74
 75    drop_ids = find_drop_ids_by_dry_size(
 76        plot_drops_with_dry_radii_um=plot_drops_with_dry_radii_um,
 77        simulation_r_dry=simulation.r_dry,
 78    )
 79
 80    for (
 81        drop_id
 82    ) in (
 83        drop_ids
 84    ):  # TODO #1266: bug! why rightmost drop is not 500 nm if size range is set to end at 500 nm???
 85        for label, mask in masks.items():
 86            axs["r"].plot(
 87                in_unit(np.asarray(output["attributes"]["radius"][drop_id]), si.um)[
 88                    mask
 89                ],
 90                y_axis[mask],
 91                label=(
 92                    f"{in_unit(simulation.r_dry[drop_id], si.um):.2} µm"
 93                    if label == "ascent"
 94                    else ""
 95                ),
 96                color=colors[label],
 97            )
 98    axs["r"].legend()
 99    axs["r"].set_xlim(*xlim_r_um)
100    axs["r"].set_xlabel("r$_c$ (µm)", fontsize="15")
101    axs["r"].set_ylabel("Height above cloud base (m)", fontsize="15")
102    # pyplot.xticks(fontsize=12)
103    # pyplot.yticks(fontsize=12)
104
105    for ax in axs.values():
106        ax.grid()
107
108    if return_masks:
109        return masks
110    return None
CLOUD_BASE = 300.0
def find_drop_ids_by_dry_size(plot_drops_with_dry_radii_um, simulation_r_dry):
10def find_drop_ids_by_dry_size(plot_drops_with_dry_radii_um, simulation_r_dry):
11    drop_ids = []
12    for drop_size_um in plot_drops_with_dry_radii_um:
13        drop_id = (np.abs(simulation_r_dry - drop_size_um * si.um)).argmin()
14        drop_ids.append(drop_id)
15    return drop_ids
def compute_table_values(height_above_cb_m, height_cb_m, products, ascent_mask):
18def compute_table_values(height_above_cb_m, height_cb_m, products, ascent_mask):
19    """constructing data for Table 4"""
20    data = {"ascent": [], "descent": []}
21    for level_idx, height_m in enumerate(products["z"]):
22        rounded_height_above_cb_m = np.round(height_m - height_cb_m)
23        if rounded_height_above_cb_m in height_above_cb_m:
24            r_mean = products["r_mean_act"][level_idx]
25            r_standard_deviation = products["r_std_act"][level_idx]
26            r_relative_dispersion = r_standard_deviation / r_mean
27
28            data["ascent" if ascent_mask[level_idx] else "descent"].append(
29                (
30                    f"{rounded_height_above_cb_m:.0f}",
31                    f"{in_unit(r_mean, si.um):.2f}",
32                    f"{in_unit(r_standard_deviation, si.um):.2f}",
33                    f"{r_relative_dispersion:.3f}",
34                )
35            )
36    data["ascent"].append(data["descent"][0])
37    return data

constructing data for Table 4

def figure( *, output, settings, simulation, plot_drops_with_dry_radii_um, xlim_r_um: tuple, xlim_S_percent: tuple, return_masks: bool = False):
 40def figure(
 41    *,
 42    output,
 43    settings,
 44    simulation,
 45    plot_drops_with_dry_radii_um,
 46    xlim_r_um: tuple,
 47    xlim_S_percent: tuple,
 48    return_masks: bool = False,
 49):
 50    y_axis = np.asarray(output["products"]["z"]) - settings.z0 - CLOUD_BASE
 51
 52    masks = {}
 53    if settings.t_end_of_ascent is None:
 54        masks["ascent"] = np.full_like(output["products"]["t"], True, dtype=bool)
 55    else:
 56        masks["ascent"] = np.asarray(output["products"]["t"]) < settings.t_end_of_ascent
 57        masks["descent"] = np.logical_not(masks["ascent"])
 58
 59    colors = {"ascent": "r", "descent": "b"}
 60
 61    _, axs = pyplot.subplot_mosaic(
 62        mosaic=[["r", "S"]], width_ratios=[3, 1], sharey=True, tight_layout=True
 63    )
 64
 65    for label, mask in masks.items():
 66        axs["S"].plot(
 67            in_unit(np.asarray(output["products"]["S_max"]), PER_CENT)[mask],
 68            y_axis[mask],
 69            label=label,
 70            color=colors[label],
 71        )
 72    axs["S"].set_xlim(*xlim_S_percent)
 73    axs["S"].set_xlabel("S (%)", fontsize="15")
 74    axs["S"].legend(fontsize="10")
 75
 76    drop_ids = find_drop_ids_by_dry_size(
 77        plot_drops_with_dry_radii_um=plot_drops_with_dry_radii_um,
 78        simulation_r_dry=simulation.r_dry,
 79    )
 80
 81    for (
 82        drop_id
 83    ) in (
 84        drop_ids
 85    ):  # TODO #1266: bug! why rightmost drop is not 500 nm if size range is set to end at 500 nm???
 86        for label, mask in masks.items():
 87            axs["r"].plot(
 88                in_unit(np.asarray(output["attributes"]["radius"][drop_id]), si.um)[
 89                    mask
 90                ],
 91                y_axis[mask],
 92                label=(
 93                    f"{in_unit(simulation.r_dry[drop_id], si.um):.2} µm"
 94                    if label == "ascent"
 95                    else ""
 96                ),
 97                color=colors[label],
 98            )
 99    axs["r"].legend()
100    axs["r"].set_xlim(*xlim_r_um)
101    axs["r"].set_xlabel("r$_c$ (µm)", fontsize="15")
102    axs["r"].set_ylabel("Height above cloud base (m)", fontsize="15")
103    # pyplot.xticks(fontsize=12)
104    # pyplot.yticks(fontsize=12)
105
106    for ax in axs.values():
107        ax.grid()
108
109    if return_masks:
110        return masks
111    return None