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(
 56        backend=CPU(formulae),
 57        n_sd=n_sd,
 58        environment=env,
 59        dynamics=[AmbientThermodynamics(), Condensation()],
 60    )
 61    builder.request_attribute("critical saturation")
 62
 63    attributes = {
 64        k: np.empty(0) for k in ("dry volume", "kappa times dry volume", "multiplicity")
 65    }
 66    for i, mode in enumerate(aerosol.modes):
 67        kappa, spectrum = mode["kappa"]["CompressedFilmOvadnevaite"], mode["spectrum"]
 68        r_dry, concentration = ConstantMultiplicity(spectrum).sample_deterministic(
 69            n_sd_per_mode
 70        )
 71        v_dry = builder.formulae.trivia.volume(radius=r_dry)
 72        specific_concentration = concentration / builder.formulae.constants.rho_STP
 73        attributes["multiplicity"] = np.append(
 74            attributes["multiplicity"],
 75            specific_concentration * builder.particulator.environment.mass_of_dry_air,
 76        )
 77        attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
 78        attributes["kappa times dry volume"] = np.append(
 79            attributes["kappa times dry volume"], v_dry * kappa
 80        )
 81
 82    r_wet = equilibrate_wet_radii(
 83        r_dry=builder.formulae.trivia.radius(volume=attributes["dry volume"]),
 84        environment=builder.particulator.environment,
 85        kappa_times_dry_volume=attributes["kappa times dry volume"],
 86    )
 87    attributes["volume"] = builder.formulae.trivia.volume(radius=r_wet)
 88
 89    particulator = builder.build(attributes, products=products)
 90
 91    output = {product.name: [] for product in particulator.products.values()}
 92    output_attributes = {
 93        "multiplicity": tuple([] for _ in range(particulator.n_sd)),
 94        "volume": tuple([] for _ in range(particulator.n_sd)),
 95        "critical volume": tuple([] for _ in range(particulator.n_sd)),
 96        "critical saturation": tuple([] for _ in range(particulator.n_sd)),
 97    }
 98
 99    for _ in range(n_steps):
100        particulator.run(steps=1)
101        for product in particulator.products.values():
102            value = product.get()
103            output[product.name].append(value[0])
104        for key, attr in output_attributes.items():
105            attr_data = particulator.attributes[key].to_ndarray()
106            for drop_id in range(particulator.n_sd):
107                attr[drop_id].append(attr_data[drop_id])
108
109    error = np.zeros(len(aerosol.modes))
110    activated_fraction_S = np.zeros(len(aerosol.modes))
111    activated_fraction_V = np.zeros(len(aerosol.modes))
112    for j, mode in enumerate(aerosol.modes):
113        activated_drops_j_S = 0
114        activated_drops_j_V = 0
115        RHmax = np.nanmax(np.asarray(output["RH"]))
116        for i, volume in enumerate(output_attributes["volume"]):
117            if j * n_sd_per_mode <= i < (j + 1) * n_sd_per_mode:
118                if output_attributes["critical saturation"][i][-1] < RHmax:
119                    activated_drops_j_S += output_attributes["multiplicity"][i][-1]
120                if output_attributes["critical volume"][i][-1] < volume[-1]:
121                    activated_drops_j_V += output_attributes["multiplicity"][i][-1]
122        Nj = np.asarray(output_attributes["multiplicity"])[
123            j * n_sd_per_mode : (j + 1) * n_sd_per_mode, -1
124        ]
125        max_multiplicity_j = np.max(Nj)
126        sum_multiplicity_j = np.sum(Nj)
127        error[j] = max_multiplicity_j / sum_multiplicity_j
128        activated_fraction_S[j] = activated_drops_j_S / sum_multiplicity_j
129        activated_fraction_V[j] = activated_drops_j_V / sum_multiplicity_j
130
131    Output = namedtuple(
132        "Output",
133        [
134            "profile",
135            "attributes",
136            "aerosol",
137            "activated_fraction_S",
138            "activated_fraction_V",
139            "error",
140        ],
141    )
142    return Output(
143        profile=output,
144        attributes=output_attributes,
145        aerosol=aerosol,
146        activated_fraction_S=activated_fraction_S,
147        activated_fraction_V=activated_fraction_V,
148        error=error,
149    )
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(
 57        backend=CPU(formulae),
 58        n_sd=n_sd,
 59        environment=env,
 60        dynamics=[AmbientThermodynamics(), Condensation()],
 61    )
 62    builder.request_attribute("critical saturation")
 63
 64    attributes = {
 65        k: np.empty(0) for k in ("dry volume", "kappa times dry volume", "multiplicity")
 66    }
 67    for i, mode in enumerate(aerosol.modes):
 68        kappa, spectrum = mode["kappa"]["CompressedFilmOvadnevaite"], mode["spectrum"]
 69        r_dry, concentration = ConstantMultiplicity(spectrum).sample_deterministic(
 70            n_sd_per_mode
 71        )
 72        v_dry = builder.formulae.trivia.volume(radius=r_dry)
 73        specific_concentration = concentration / builder.formulae.constants.rho_STP
 74        attributes["multiplicity"] = np.append(
 75            attributes["multiplicity"],
 76            specific_concentration * builder.particulator.environment.mass_of_dry_air,
 77        )
 78        attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
 79        attributes["kappa times dry volume"] = np.append(
 80            attributes["kappa times dry volume"], v_dry * kappa
 81        )
 82
 83    r_wet = equilibrate_wet_radii(
 84        r_dry=builder.formulae.trivia.radius(volume=attributes["dry volume"]),
 85        environment=builder.particulator.environment,
 86        kappa_times_dry_volume=attributes["kappa times dry volume"],
 87    )
 88    attributes["volume"] = builder.formulae.trivia.volume(radius=r_wet)
 89
 90    particulator = builder.build(attributes, products=products)
 91
 92    output = {product.name: [] for product in particulator.products.values()}
 93    output_attributes = {
 94        "multiplicity": tuple([] for _ in range(particulator.n_sd)),
 95        "volume": tuple([] for _ in range(particulator.n_sd)),
 96        "critical volume": tuple([] for _ in range(particulator.n_sd)),
 97        "critical saturation": tuple([] for _ in range(particulator.n_sd)),
 98    }
 99
100    for _ in range(n_steps):
101        particulator.run(steps=1)
102        for product in particulator.products.values():
103            value = product.get()
104            output[product.name].append(value[0])
105        for key, attr in output_attributes.items():
106            attr_data = particulator.attributes[key].to_ndarray()
107            for drop_id in range(particulator.n_sd):
108                attr[drop_id].append(attr_data[drop_id])
109
110    error = np.zeros(len(aerosol.modes))
111    activated_fraction_S = np.zeros(len(aerosol.modes))
112    activated_fraction_V = np.zeros(len(aerosol.modes))
113    for j, mode in enumerate(aerosol.modes):
114        activated_drops_j_S = 0
115        activated_drops_j_V = 0
116        RHmax = np.nanmax(np.asarray(output["RH"]))
117        for i, volume in enumerate(output_attributes["volume"]):
118            if j * n_sd_per_mode <= i < (j + 1) * n_sd_per_mode:
119                if output_attributes["critical saturation"][i][-1] < RHmax:
120                    activated_drops_j_S += output_attributes["multiplicity"][i][-1]
121                if output_attributes["critical volume"][i][-1] < volume[-1]:
122                    activated_drops_j_V += output_attributes["multiplicity"][i][-1]
123        Nj = np.asarray(output_attributes["multiplicity"])[
124            j * n_sd_per_mode : (j + 1) * n_sd_per_mode, -1
125        ]
126        max_multiplicity_j = np.max(Nj)
127        sum_multiplicity_j = np.sum(Nj)
128        error[j] = max_multiplicity_j / sum_multiplicity_j
129        activated_fraction_S[j] = activated_drops_j_S / sum_multiplicity_j
130        activated_fraction_V[j] = activated_drops_j_V / sum_multiplicity_j
131
132    Output = namedtuple(
133        "Output",
134        [
135            "profile",
136            "attributes",
137            "aerosol",
138            "activated_fraction_S",
139            "activated_fraction_V",
140            "error",
141        ],
142    )
143    return Output(
144        profile=output,
145        attributes=output_attributes,
146        aerosol=aerosol,
147        activated_fraction_S=activated_fraction_S,
148        activated_fraction_V=activated_fraction_V,
149        error=error,
150    )