PySDM_examples.Abdul_Razzak_Ghan_2000.run_ARG_parcel

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