PySDM_examples.featured.Abdul_Razzak_Ghan_2000.run_ARG_parcel

  1from collections import namedtuple
  2
  3import numpy as np
  4from PySDM_examples.featured.Abdul_Razzak_Ghan_2000.aerosol import (
  5    CONSTANTS_ARG,
  6    AerosolARG,
  7)
  8
  9from PySDM import Particulator, Formulae
 10from PySDM import products as PySDM_products
 11from PySDM.backends import CPU
 12from PySDM.dynamics import AmbientThermodynamics, Condensation
 13from PySDM.environments import Parcel
 14from PySDM.initialisation.hygroscopic_equilibrium import equilibrate_wet_radii
 15from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity
 16from PySDM.physics import si
 17
 18
 19def run_parcel(
 20    w,
 21    sol2,
 22    N2,
 23    rad2,
 24    n_sd_per_mode,
 25    *,
 26    RH0=1.0,
 27    T0=294 * si.K,
 28    p0=1e5 * si.Pa,
 29    n_steps=50,
 30    mass_of_dry_air=1e3 * si.kg,
 31    dt=2 * si.s,
 32):
 33    products = (
 34        PySDM_products.WaterMixingRatio(unit="g/kg", name="liquid water mixing ratio"),
 35        PySDM_products.PeakSaturation(name="S max"),
 36        PySDM_products.AmbientRelativeHumidity(name="RH"),
 37        PySDM_products.ParcelDisplacement(name="z"),
 38    )
 39
 40    formulae = Formulae(constants=CONSTANTS_ARG)
 41    const = formulae.constants
 42    pv0 = RH0 * formulae.saturation_vapour_pressure.pvs_water(T0)
 43
 44    env = Parcel(
 45        dt=dt,
 46        mass_of_dry_air=mass_of_dry_air,
 47        p0=p0,
 48        initial_water_vapour_mixing_ratio=const.eps * pv0 / (p0 - pv0),
 49        w=w,
 50        T0=T0,
 51        backend=CPU(formulae),
 52    )
 53
 54    aerosol = AerosolARG(
 55        M2_sol=sol2, M2_N=N2, M2_rad=rad2, water_molar_volume=const.Mv / const.rho_w
 56    )
 57    n_sd = n_sd_per_mode * len(aerosol.modes)
 58
 59    attributes = {
 60        k: np.empty(0) for k in ("dry volume", "kappa times dry volume", "multiplicity")
 61    }
 62    for i, mode in enumerate(aerosol.modes):
 63        kappa, spectrum = mode["kappa"]["CompressedFilmOvadnevaite"], mode["spectrum"]
 64        r_dry, concentration = ConstantMultiplicity(spectrum).sample_deterministic(
 65            n_sd_per_mode
 66        )
 67        v_dry = formulae.trivia.volume(radius=r_dry)
 68        specific_concentration = concentration / env.backend.formulae.constants.rho_STP
 69        attributes["multiplicity"] = np.append(
 70            attributes["multiplicity"],
 71            specific_concentration * env.mass_of_dry_air,
 72        )
 73        attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
 74        attributes["kappa times dry volume"] = np.append(
 75            attributes["kappa times dry volume"], v_dry * kappa
 76        )
 77
 78    r_wet = equilibrate_wet_radii(
 79        r_dry=formulae.trivia.radius(volume=attributes["dry volume"]),
 80        environment=env,
 81        kappa_times_dry_volume=attributes["kappa times dry volume"],
 82    )
 83    attributes["volume"] = env.backend.formulae.trivia.volume(radius=r_wet)
 84
 85    particulator = Particulator(
 86        n_sd=n_sd,
 87        environment=env,
 88        dynamics=[AmbientThermodynamics(), Condensation()],
 89        requested_attributes=("critical saturation",),
 90        attributes=attributes,
 91        products=products,
 92    )
 93
 94    output = {product.name: [] for product in particulator.products.values()}
 95    output_attributes = {
 96        "multiplicity": tuple([] for _ in range(particulator.n_sd)),
 97        "volume": tuple([] for _ in range(particulator.n_sd)),
 98        "critical volume": tuple([] for _ in range(particulator.n_sd)),
 99        "critical saturation": tuple([] for _ in range(particulator.n_sd)),
100    }
101
102    for _ in range(n_steps):
103        particulator.advance(n_steps=1)
104        for product in particulator.products.values():
105            value = product.get()
106            output[product.name].append(value[0])
107        for key, attr in output_attributes.items():
108            attr_data = particulator.attributes[key].to_ndarray()
109            for drop_id in range(particulator.n_sd):
110                attr[drop_id].append(attr_data[drop_id])
111
112    error = np.zeros(len(aerosol.modes))
113    activated_fraction_S = np.zeros(len(aerosol.modes))
114    activated_fraction_V = np.zeros(len(aerosol.modes))
115    for j, mode in enumerate(aerosol.modes):
116        activated_drops_j_S = 0
117        activated_drops_j_V = 0
118        RHmax = np.nanmax(np.asarray(output["RH"]))
119        for i, volume in enumerate(output_attributes["volume"]):
120            if j * n_sd_per_mode <= i < (j + 1) * n_sd_per_mode:
121                if output_attributes["critical saturation"][i][-1] < RHmax:
122                    activated_drops_j_S += output_attributes["multiplicity"][i][-1]
123                if output_attributes["critical volume"][i][-1] < volume[-1]:
124                    activated_drops_j_V += output_attributes["multiplicity"][i][-1]
125        Nj = np.asarray(output_attributes["multiplicity"])[
126            j * n_sd_per_mode : (j + 1) * n_sd_per_mode, -1
127        ]
128        max_multiplicity_j = np.max(Nj)
129        sum_multiplicity_j = np.sum(Nj)
130        error[j] = max_multiplicity_j / sum_multiplicity_j
131        activated_fraction_S[j] = activated_drops_j_S / sum_multiplicity_j
132        activated_fraction_V[j] = activated_drops_j_V / sum_multiplicity_j
133
134    Output = namedtuple(
135        "Output",
136        [
137            "profile",
138            "attributes",
139            "aerosol",
140            "activated_fraction_S",
141            "activated_fraction_V",
142            "error",
143        ],
144    )
145    return Output(
146        profile=output,
147        attributes=output_attributes,
148        aerosol=aerosol,
149        activated_fraction_S=activated_fraction_S,
150        activated_fraction_V=activated_fraction_V,
151        error=error,
152    )
def run_parcel( w, sol2, N2, rad2, n_sd_per_mode, *, RH0=1.0, T0=294.0, p0=100000.0, n_steps=50, mass_of_dry_air=1000.0, dt=2.0):
 20def run_parcel(
 21    w,
 22    sol2,
 23    N2,
 24    rad2,
 25    n_sd_per_mode,
 26    *,
 27    RH0=1.0,
 28    T0=294 * si.K,
 29    p0=1e5 * si.Pa,
 30    n_steps=50,
 31    mass_of_dry_air=1e3 * si.kg,
 32    dt=2 * si.s,
 33):
 34    products = (
 35        PySDM_products.WaterMixingRatio(unit="g/kg", name="liquid water mixing ratio"),
 36        PySDM_products.PeakSaturation(name="S max"),
 37        PySDM_products.AmbientRelativeHumidity(name="RH"),
 38        PySDM_products.ParcelDisplacement(name="z"),
 39    )
 40
 41    formulae = Formulae(constants=CONSTANTS_ARG)
 42    const = formulae.constants
 43    pv0 = RH0 * formulae.saturation_vapour_pressure.pvs_water(T0)
 44
 45    env = Parcel(
 46        dt=dt,
 47        mass_of_dry_air=mass_of_dry_air,
 48        p0=p0,
 49        initial_water_vapour_mixing_ratio=const.eps * pv0 / (p0 - pv0),
 50        w=w,
 51        T0=T0,
 52        backend=CPU(formulae),
 53    )
 54
 55    aerosol = AerosolARG(
 56        M2_sol=sol2, M2_N=N2, M2_rad=rad2, water_molar_volume=const.Mv / const.rho_w
 57    )
 58    n_sd = n_sd_per_mode * len(aerosol.modes)
 59
 60    attributes = {
 61        k: np.empty(0) for k in ("dry volume", "kappa times dry volume", "multiplicity")
 62    }
 63    for i, mode in enumerate(aerosol.modes):
 64        kappa, spectrum = mode["kappa"]["CompressedFilmOvadnevaite"], mode["spectrum"]
 65        r_dry, concentration = ConstantMultiplicity(spectrum).sample_deterministic(
 66            n_sd_per_mode
 67        )
 68        v_dry = formulae.trivia.volume(radius=r_dry)
 69        specific_concentration = concentration / env.backend.formulae.constants.rho_STP
 70        attributes["multiplicity"] = np.append(
 71            attributes["multiplicity"],
 72            specific_concentration * env.mass_of_dry_air,
 73        )
 74        attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
 75        attributes["kappa times dry volume"] = np.append(
 76            attributes["kappa times dry volume"], v_dry * kappa
 77        )
 78
 79    r_wet = equilibrate_wet_radii(
 80        r_dry=formulae.trivia.radius(volume=attributes["dry volume"]),
 81        environment=env,
 82        kappa_times_dry_volume=attributes["kappa times dry volume"],
 83    )
 84    attributes["volume"] = env.backend.formulae.trivia.volume(radius=r_wet)
 85
 86    particulator = Particulator(
 87        n_sd=n_sd,
 88        environment=env,
 89        dynamics=[AmbientThermodynamics(), Condensation()],
 90        requested_attributes=("critical saturation",),
 91        attributes=attributes,
 92        products=products,
 93    )
 94
 95    output = {product.name: [] for product in particulator.products.values()}
 96    output_attributes = {
 97        "multiplicity": tuple([] for _ in range(particulator.n_sd)),
 98        "volume": tuple([] for _ in range(particulator.n_sd)),
 99        "critical volume": tuple([] for _ in range(particulator.n_sd)),
100        "critical saturation": tuple([] for _ in range(particulator.n_sd)),
101    }
102
103    for _ in range(n_steps):
104        particulator.advance(n_steps=1)
105        for product in particulator.products.values():
106            value = product.get()
107            output[product.name].append(value[0])
108        for key, attr in output_attributes.items():
109            attr_data = particulator.attributes[key].to_ndarray()
110            for drop_id in range(particulator.n_sd):
111                attr[drop_id].append(attr_data[drop_id])
112
113    error = np.zeros(len(aerosol.modes))
114    activated_fraction_S = np.zeros(len(aerosol.modes))
115    activated_fraction_V = np.zeros(len(aerosol.modes))
116    for j, mode in enumerate(aerosol.modes):
117        activated_drops_j_S = 0
118        activated_drops_j_V = 0
119        RHmax = np.nanmax(np.asarray(output["RH"]))
120        for i, volume in enumerate(output_attributes["volume"]):
121            if j * n_sd_per_mode <= i < (j + 1) * n_sd_per_mode:
122                if output_attributes["critical saturation"][i][-1] < RHmax:
123                    activated_drops_j_S += output_attributes["multiplicity"][i][-1]
124                if output_attributes["critical volume"][i][-1] < volume[-1]:
125                    activated_drops_j_V += output_attributes["multiplicity"][i][-1]
126        Nj = np.asarray(output_attributes["multiplicity"])[
127            j * n_sd_per_mode : (j + 1) * n_sd_per_mode, -1
128        ]
129        max_multiplicity_j = np.max(Nj)
130        sum_multiplicity_j = np.sum(Nj)
131        error[j] = max_multiplicity_j / sum_multiplicity_j
132        activated_fraction_S[j] = activated_drops_j_S / sum_multiplicity_j
133        activated_fraction_V[j] = activated_drops_j_V / sum_multiplicity_j
134
135    Output = namedtuple(
136        "Output",
137        [
138            "profile",
139            "attributes",
140            "aerosol",
141            "activated_fraction_S",
142            "activated_fraction_V",
143            "error",
144        ],
145    )
146    return Output(
147        profile=output,
148        attributes=output_attributes,
149        aerosol=aerosol,
150        activated_fraction_S=activated_fraction_S,
151        activated_fraction_V=activated_fraction_V,
152        error=error,
153    )