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