Example #1
0
def test_ambient_relative_humidity(backend_class):
    # arrange
    n_sd = 1
    builder = Builder(n_sd, backend=backend_class())
    env = Parcel(dt=np.nan,
                 mixed_phase=True,
                 mass_of_dry_air=np.nan,
                 p0=1000 * si.hPa,
                 q0=1 * si.g / si.kg,
                 T0=260 * si.K,
                 w=np.nan)
    builder.set_environment(env)
    attributes = {'n': np.ones(n_sd), 'volume': np.ones(n_sd)}
    particulator = builder.build(
        attributes=attributes,
        products=(
            products.AmbientRelativeHumidity(name='RHw', var='RH'),
            products.AmbientRelativeHumidity(name='RHi', var='RH', ice=True),
        ))

    # act
    values = {}
    for name, product in particulator.products.items():
        values[name] = product.get()[0]

    # assert
    assert values['RHw'] < values['RHi']
Example #2
0
    def __init__(self, settings, backend=CPU):
        t_half = settings.z_half / settings.w_avg

        dt_output = (2 * t_half) / settings.n_output
        self.n_substeps = 1
        while dt_output / self.n_substeps >= settings.dt_max:  # TODO #334 dt_max
            self.n_substeps += 1

        builder = Builder(backend=backend(formulae=settings.formulae), n_sd=1)
        builder.set_environment(
            Parcel(
                dt=dt_output / self.n_substeps,
                mass_of_dry_air=settings.mass_of_dry_air,
                p0=settings.p0,
                q0=settings.q0,
                T0=settings.T0,
                w=settings.w,
            ))

        builder.add_dynamic(AmbientThermodynamics())
        builder.add_dynamic(
            Condensation(
                rtol_x=settings.rtol_x,
                rtol_thd=settings.rtol_thd,
                dt_cond_range=settings.dt_cond_range,
            ))
        attributes = {}
        r_dry = np.array([settings.r_dry])
        attributes["dry volume"] = settings.formulae.trivia.volume(
            radius=r_dry)
        attributes["kappa times dry volume"] = attributes[
            "dry volume"] * settings.kappa
        attributes["n"] = np.array([settings.n_in_dv], dtype=np.int64)
        environment = builder.particulator.environment
        r_wet = equilibrate_wet_radii(
            r_dry=r_dry,
            environment=environment,
            kappa_times_dry_volume=attributes["kappa times dry volume"],
        )
        attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)
        products = [
            PySDM_products.MeanRadius(name="radius_m1", unit="um"),
            PySDM_products.CondensationTimestepMin(name="dt_cond_min"),
            PySDM_products.ParcelDisplacement(name="z"),
            PySDM_products.AmbientRelativeHumidity(name="RH", unit="%"),
            PySDM_products.Time(name="t"),
            PySDM_products.ActivatingRate(unit="s^-1 mg^-1",
                                          name="activating_rate"),
            PySDM_products.DeactivatingRate(unit="s^-1 mg^-1",
                                            name="deactivating_rate"),
            PySDM_products.RipeningRate(unit="s^-1 mg^-1",
                                        name="ripening_rate"),
            PySDM_products.PeakSupersaturation(unit="%", name="S_max"),
        ]

        self.particulator = builder.build(attributes, products)

        self.n_output = settings.n_output
Example #3
0
    def __init__(self, settings, products=None):
        env = Parcel(
            dt=settings.dt,
            mass_of_dry_air=settings.mass_of_dry_air,
            p0=settings.p0,
            q0=settings.q0,
            T0=settings.T0,
            w=settings.w,
        )
        n_sd = settings.n_sd_per_mode * len(settings.aerosol.modes)
        builder = Builder(n_sd=n_sd, backend=CPU(formulae=settings.formulae))
        builder.set_environment(env)

        attributes = {
            "dry volume": np.empty(0),
            "dry volume organic": np.empty(0),
            "kappa times dry volume": np.empty(0),
            "n": np.ndarray(0),
        }
        for mode in settings.aerosol.modes:
            r_dry, n_in_dv = settings.spectral_sampling(
                spectrum=mode["spectrum"]).sample(settings.n_sd_per_mode)
            V = settings.mass_of_dry_air / settings.rho0
            N = n_in_dv * V
            v_dry = settings.formulae.trivia.volume(radius=r_dry)
            attributes["n"] = np.append(attributes["n"], N)
            attributes["dry volume"] = np.append(attributes["dry volume"],
                                                 v_dry)
            attributes["dry volume organic"] = np.append(
                attributes["dry volume organic"], mode["f_org"] * v_dry)
            attributes["kappa times dry volume"] = np.append(
                attributes["kappa times dry volume"],
                v_dry * mode["kappa"][settings.model],
            )
        for attribute in attributes.values():
            assert attribute.shape[0] == n_sd

        np.testing.assert_approx_equal(
            np.sum(attributes["n"]) / V,
            Sum(
                tuple(
                    settings.aerosol.modes[i]["spectrum"]
                    for i in range(len(settings.aerosol.modes)))).norm_factor,
            significant=5,
        )
        r_wet = equilibrate_wet_radii(
            r_dry=settings.formulae.trivia.radius(
                volume=attributes["dry volume"]),
            environment=env,
            kappa_times_dry_volume=attributes["kappa times dry volume"],
            f_org=attributes["dry volume organic"] / attributes["dry volume"],
        )
        attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)

        if settings.model == "Constant":
            del attributes["dry volume organic"]

        builder.add_dynamic(AmbientThermodynamics())
        builder.add_dynamic(Condensation())

        products = products or (
            PySDM_products.ParcelDisplacement(name="z"),
            PySDM_products.Time(name="t"),
            PySDM_products.PeakSupersaturation(unit="%", name="S_max"),
            PySDM_products.AmbientRelativeHumidity(unit="%", name="RH"),
            PySDM_products.ParticleConcentration(
                name="n_c_cm3",
                unit="cm^-3",
                radius_range=settings.cloud_radius_range),
            PySDM_products.ParticleSizeSpectrumPerVolume(
                radius_bins_edges=settings.wet_radius_bins_edges),
            PySDM_products.ActivableFraction(),
        )

        particulator = builder.build(attributes=attributes, products=products)
        if settings.BDF:
            bdf.patch_particulator(particulator)
        self.settings = settings
        super().__init__(particulator=particulator)
Example #4
0
def make_default_product_collection(settings):
    cloud_range = (settings.aerosol_radius_threshold, settings.drizzle_radius_threshold)
    products = [
        # Note: consider better radius_bins_edges
        PySDM_products.ParticleSizeSpectrumPerMass(
            name="Particles Wet Size Spectrum",
            unit="mg^-1 um^-1",
            radius_bins_edges=settings.r_bins_edges,
        ),
        PySDM_products.ParticleSizeSpectrumPerMass(
            name="Particles Dry Size Spectrum",
            unit="mg^-1 um^-1",
            radius_bins_edges=settings.r_bins_edges,
            dry=True,
        ),
        PySDM_products.TotalParticleConcentration(),
        PySDM_products.TotalParticleSpecificConcentration(),
        PySDM_products.ParticleConcentration(
            radius_range=(0, settings.aerosol_radius_threshold)
        ),
        PySDM_products.ParticleConcentration(
            name="n_c_cm3", unit="cm^-3", radius_range=cloud_range
        ),
        PySDM_products.WaterMixingRatio(name="qc", radius_range=cloud_range),
        PySDM_products.WaterMixingRatio(
            name="qr", radius_range=(settings.drizzle_radius_threshold, np.inf)
        ),
        PySDM_products.ParticleConcentration(
            name="drizzle concentration",
            radius_range=(settings.drizzle_radius_threshold, np.inf),
            unit="cm^-3",
        ),
        PySDM_products.ParticleSpecificConcentration(
            name="aerosol specific concentration",
            radius_range=(0, settings.aerosol_radius_threshold),
            unit="mg^-1",
        ),
        PySDM_products.MeanRadius(unit="um"),
        PySDM_products.SuperDropletCountPerGridbox(),
        PySDM_products.AmbientRelativeHumidity(name="RH_env", var="RH"),
        PySDM_products.AmbientPressure(name="p_env", var="p"),
        PySDM_products.AmbientTemperature(name="T_env", var="T"),
        PySDM_products.AmbientWaterVapourMixingRatio(name="qv_env", var="qv"),
        PySDM_products.AmbientDryAirDensity(name="rhod_env", var="rhod"),
        PySDM_products.AmbientDryAirPotentialTemperature(name="thd_env", var="thd"),
        PySDM_products.CPUTime(),
        PySDM_products.WallTime(),
        PySDM_products.EffectiveRadius(unit="um", radius_range=cloud_range),
        PySDM_products.RadiusBinnedNumberAveragedTerminalVelocity(
            radius_bin_edges=settings.terminal_velocity_radius_bin_edges
        ),
    ]

    if settings.processes["fluid advection"]:
        products.append(PySDM_products.MaxCourantNumber())
        products.append(PySDM_products.CoolingRate())
    if settings.processes["condensation"]:
        products.append(PySDM_products.CondensationTimestepMin(name="dt_cond_min"))
        products.append(PySDM_products.CondensationTimestepMax(name="dt_cond_max"))
        products.append(PySDM_products.PeakSupersaturation(unit="%", name="S_max"))
        products.append(PySDM_products.ActivatingRate())
        products.append(PySDM_products.DeactivatingRate())
        products.append(PySDM_products.RipeningRate())
    if settings.processes["particle advection"]:
        products.append(
            PySDM_products.SurfacePrecipitation(name="surf_precip", unit="mm/day")
        )
    if settings.processes["coalescence"]:
        products.append(PySDM_products.CollisionTimestepMean(name="dt_coal_avg"))
        products.append(PySDM_products.CollisionTimestepMin(name="dt_coal_min"))
        products.append(PySDM_products.CollisionRatePerGridbox())
        products.append(PySDM_products.CollisionRateDeficitPerGridbox())
    if settings.processes["freezing"]:
        products.append(PySDM_products.IceWaterContent())
        if settings.freezing_singular:
            products.append(
                PySDM_products.FreezableSpecificConcentration(settings.T_bins_edges)
            )
        else:
            products.append(PySDM_products.TotalUnfrozenImmersedSurfaceArea())
            # TODO #599 immersed surf spec
        products.append(
            PySDM_products.ParticleSpecificConcentration(
                radius_range=(-np.inf, 0), name="n_ice"
            )
        )

    return products
Example #5
0
    def __init__(self, settings, products=None):
        env = Parcel(
            dt=settings.dt,
            mass_of_dry_air=settings.mass_of_dry_air,
            p0=settings.p0,
            q0=settings.q0,
            T0=settings.T0,
            w=settings.w,
        )

        builder = Builder(n_sd=settings.n_sd,
                          backend=CPU(formulae=settings.formulae))
        builder.set_environment(env)

        attributes = env.init_attributes(n_in_dv=settings.n_in_dv,
                                         kappa=settings.kappa,
                                         r_dry=settings.r_dry)
        attributes = {
            **attributes,
            **settings.starting_amounts,
            "pH": np.zeros(settings.n_sd),
        }

        builder.add_dynamic(AmbientThermodynamics())
        builder.add_dynamic(Condensation())
        builder.add_dynamic(
            AqueousChemistry(
                environment_mole_fractions=settings.ENVIRONMENT_MOLE_FRACTIONS,
                system_type=settings.system_type,
                n_substep=settings.n_substep,
                dry_rho=settings.DRY_RHO,
                dry_molar_mass=settings.dry_molar_mass,
            ))

        products = products or (
            PySDM_products.AmbientRelativeHumidity(name="RH", unit="%"),
            PySDM_products.WaterMixingRatio(
                name="ql", radius_range=[1 * si.um, np.inf], unit="g/kg"),
            PySDM_products.ParcelDisplacement(name="z"),
            PySDM_products.AmbientPressure(name="p"),
            PySDM_products.AmbientTemperature(name="T"),
            PySDM_products.AmbientDryAirDensity(name="rhod"),
            PySDM_products.AmbientWaterVapourMixingRatio(name="qv",
                                                         unit="g/kg"),
            PySDM_products.Time(name="t"),
            *(PySDM_products.AqueousMoleFraction(
                comp, unit="ppb", name=f"aq_{comp}_ppb")
              for comp in AQUEOUS_COMPOUNDS),
            *(PySDM_products.GaseousMoleFraction(
                comp, unit="ppb", name=f"gas_{comp}_ppb")
              for comp in GASEOUS_COMPOUNDS),
            PySDM_products.Acidity(
                name="pH_pH_number_weighted",
                radius_range=settings.cloud_radius_range,
                weighting="number",
                attr="pH",
            ),
            PySDM_products.Acidity(
                name="pH_pH_volume_weighted",
                radius_range=settings.cloud_radius_range,
                weighting="volume",
                attr="pH",
            ),
            PySDM_products.Acidity(
                name="pH_conc_H_number_weighted",
                radius_range=settings.cloud_radius_range,
                weighting="number",
                attr="conc_H",
            ),
            PySDM_products.Acidity(
                name="pH_conc_H_volume_weighted",
                radius_range=settings.cloud_radius_range,
                weighting="volume",
                attr="conc_H",
            ),
            PySDM_products.TotalDryMassMixingRatio(
                settings.DRY_RHO, name="q_dry", unit="ug/kg"),
            PySDM_products.PeakSupersaturation(unit="%", name="S_max"),
            PySDM_products.ParticleSpecificConcentration(
                radius_range=settings.cloud_radius_range,
                name="n_c_mg",
                unit="mg^-1"),
            PySDM_products.AqueousMassSpectrum(
                key="S_VI",
                dry_radius_bins_edges=settings.dry_radius_bins_edges,
                name="dm_S_VI/dlog_10(dry diameter)",
                unit='ug / m^3"',
            ),
        )

        particulator = builder.build(attributes=attributes, products=products)
        self.settings = settings
        super().__init__(particulator=particulator)
Example #6
0
def run_parcel(
    w,
    sol2,
    N2,
    rad2,
    n_sd_per_mode,
    RH0=1.0,
    T0=294 * si.K,
    p0=1e5 * si.Pa,
    n_steps=50,
    mass_of_dry_air=1e3 * si.kg,
    dt=2 * si.s,
):
    products = (
        PySDM_products.WaterMixingRatio(unit="g/kg", name="ql"),
        PySDM_products.PeakSupersaturation(name="S max"),
        PySDM_products.AmbientRelativeHumidity(name="RH"),
        PySDM_products.ParcelDisplacement(name="z"),
    )

    formulae = Formulae()
    const = formulae.constants
    pv0 = RH0 * formulae.saturation_vapour_pressure.pvs_Celsius(T0 - const.T0)
    q0 = const.eps * pv0 / (p0 - pv0)

    env = Parcel(dt=dt,
                 mass_of_dry_air=mass_of_dry_air,
                 p0=p0,
                 q0=q0,
                 w=w,
                 T0=T0)

    aerosol = AerosolARG(M2_sol=sol2, M2_N=N2, M2_rad=rad2)
    n_sd = n_sd_per_mode * len(aerosol.modes)

    builder = Builder(backend=CPU(), n_sd=n_sd)
    builder.set_environment(env)
    builder.add_dynamic(AmbientThermodynamics())
    builder.add_dynamic(Condensation())
    builder.add_dynamic(Magick())

    attributes = {
        k: np.empty(0)
        for k in ("dry volume", "kappa times dry volume", "n")
    }
    for i, mode in enumerate(aerosol.modes):
        kappa, spectrum = mode["kappa"]["CompressedFilmOvadnevaite"], mode[
            "spectrum"]
        r_dry, concentration = ConstantMultiplicity(spectrum).sample(
            n_sd_per_mode)
        v_dry = builder.formulae.trivia.volume(radius=r_dry)
        specific_concentration = concentration / builder.formulae.constants.rho_STP
        attributes["n"] = np.append(
            attributes["n"], specific_concentration * env.mass_of_dry_air)
        attributes["dry volume"] = np.append(attributes["dry volume"], v_dry)
        attributes["kappa times dry volume"] = np.append(
            attributes["kappa times dry volume"], v_dry * kappa)

    r_wet = equilibrate_wet_radii(
        r_dry=builder.formulae.trivia.radius(volume=attributes["dry volume"]),
        environment=env,
        kappa_times_dry_volume=attributes["kappa times dry volume"],
    )
    attributes["volume"] = builder.formulae.trivia.volume(radius=r_wet)

    particulator = builder.build(attributes, products=products)
    bdf.patch_particulator(particulator)

    output = {product.name: [] for product in particulator.products.values()}
    output_attributes = {
        "n": tuple([] for _ in range(particulator.n_sd)),
        "volume": tuple([] for _ in range(particulator.n_sd)),
        "critical volume": tuple([] for _ in range(particulator.n_sd)),
        "critical supersaturation":
        tuple([] for _ in range(particulator.n_sd)),
    }

    for _ in range(n_steps):
        particulator.run(steps=1)
        for product in particulator.products.values():
            value = product.get()
            output[product.name].append(value[0])
        for key, attr in output_attributes.items():
            attr_data = particulator.attributes[key].to_ndarray()
            for drop_id in range(particulator.n_sd):
                attr[drop_id].append(attr_data[drop_id])

    error = np.zeros(len(aerosol.modes))
    activated_fraction_S = np.zeros(len(aerosol.modes))
    activated_fraction_V = np.zeros(len(aerosol.modes))
    for j, mode in enumerate(aerosol.modes):
        activated_drops_j_S = 0
        activated_drops_j_V = 0
        RHmax = np.nanmax(np.asarray(output["RH"]))
        for i, volume in enumerate(output_attributes["volume"]):
            if j * n_sd_per_mode <= i < (j + 1) * n_sd_per_mode:
                if output_attributes["critical supersaturation"][i][-1] < RHmax:
                    activated_drops_j_S += output_attributes["n"][i][-1]
                if output_attributes["critical volume"][i][-1] < volume[-1]:
                    activated_drops_j_V += output_attributes["n"][i][-1]
        Nj = np.asarray(output_attributes["n"])[j * n_sd_per_mode:(j + 1) *
                                                n_sd_per_mode, -1]
        max_multiplicity_j = np.max(Nj)
        sum_multiplicity_j = np.sum(Nj)
        error[j] = max_multiplicity_j / sum_multiplicity_j
        activated_fraction_S[j] = activated_drops_j_S / sum_multiplicity_j
        activated_fraction_V[j] = activated_drops_j_V / sum_multiplicity_j

    Output = namedtuple(
        "Output",
        [
            "profile",
            "attributes",
            "aerosol",
            "activated_fraction_S",
            "activated_fraction_V",
            "error",
        ],
    )
    return Output(
        profile=output,
        attributes=output_attributes,
        aerosol=aerosol,
        activated_fraction_S=activated_fraction_S,
        activated_fraction_V=activated_fraction_V,
        error=error,
    )
Example #7
0
    def __init__(self, settings, backend=CPU):
        self.nt = settings.nt
        self.z0 = -settings.particle_reservoir_depth

        builder = Builder(n_sd=settings.n_sd,
                          backend=backend(formulae=settings.formulae))
        mesh = Mesh(
            grid=(settings.nz, ),
            size=(settings.z_max + settings.particle_reservoir_depth, ),
        )
        env = Kinematic1D(
            dt=settings.dt,
            mesh=mesh,
            thd_of_z=settings.thd,
            rhod_of_z=settings.rhod,
            z0=-settings.particle_reservoir_depth,
        )

        def zZ_to_z_above_reservoir(zZ):
            z_above_reservoir = zZ * (settings.nz * settings.dz) + self.z0
            return z_above_reservoir

        self.mpdata = MPDATA_1D(
            nz=settings.nz,
            dt=settings.dt,
            mpdata_settings=settings.mpdata_settings,
            advector_of_t=lambda t: settings.rho_times_w(t) * settings.dt /
            settings.dz,
            advectee_of_zZ_at_t0=lambda zZ: settings.qv(
                zZ_to_z_above_reservoir(zZ)),
            g_factor_of_zZ=lambda zZ: settings.rhod(zZ_to_z_above_reservoir(zZ)
                                                    ),
        )

        _extra_nz = settings.particle_reservoir_depth // settings.dz
        _z_vec = settings.dz * np.linspace(-_extra_nz, settings.nz - _extra_nz,
                                           settings.nz + 1)
        self.g_factor_vec = settings.rhod(_z_vec)

        builder.set_environment(env)
        builder.add_dynamic(AmbientThermodynamics())
        builder.add_dynamic(
            Condensation(
                adaptive=settings.condensation_adaptive,
                rtol_thd=settings.condensation_rtol_thd,
                rtol_x=settings.condensation_rtol_x,
            ))
        builder.add_dynamic(EulerianAdvection(self.mpdata))
        if settings.precip:
            if settings.breakup:
                builder.add_dynamic(
                    Collision(
                        collision_kernel=Geometric(collection_efficiency=1),
                        coalescence_efficiency=ConstEc(Ec=0.95),
                        breakup_efficiency=ConstEb(Eb=1.0),
                        fragmentation_function=ExponFrag(scale=100 * si.um),
                        adaptive=settings.coalescence_adaptive,
                    ))
            else:
                builder.add_dynamic(
                    Coalescence(
                        collision_kernel=Geometric(collection_efficiency=1),
                        adaptive=settings.coalescence_adaptive,
                    ))
        displacement = Displacement(
            enable_sedimentation=settings.precip,
            precipitation_counting_level_index=int(
                settings.particle_reservoir_depth / settings.dz),
        )
        builder.add_dynamic(displacement)
        attributes = env.init_attributes(
            spatial_discretisation=spatial_sampling.Pseudorandom(),
            spectral_discretisation=spectral_sampling.ConstantMultiplicity(
                spectrum=settings.wet_radius_spectrum_per_mass_of_dry_air),
            kappa=settings.kappa,
        )
        products = [
            PySDM_products.AmbientRelativeHumidity(name="RH", unit="%"),
            PySDM_products.AmbientPressure(name="p"),
            PySDM_products.AmbientTemperature(name="T"),
            PySDM_products.AmbientWaterVapourMixingRatio(name="qv"),
            PySDM_products.WaterMixingRatio(
                name="ql",
                unit="g/kg",
                radius_range=settings.cloud_water_radius_range),
            PySDM_products.WaterMixingRatio(
                name="qr",
                unit="g/kg",
                radius_range=settings.rain_water_radius_range),
            PySDM_products.AmbientDryAirDensity(name="rhod"),
            PySDM_products.AmbientDryAirPotentialTemperature(name="thd"),
            PySDM_products.ParticleSizeSpectrumPerVolume(
                name="dry spectrum",
                radius_bins_edges=settings.r_bins_edges,
                dry=True),
            PySDM_products.ParticleSizeSpectrumPerVolume(
                name="wet spectrum", radius_bins_edges=settings.r_bins_edges),
            PySDM_products.ParticleConcentration(
                name="nc", radius_range=settings.cloud_water_radius_range),
            PySDM_products.ParticleConcentration(
                name="na",
                radius_range=(0, settings.cloud_water_radius_range[0])),
            PySDM_products.MeanRadius(),
            PySDM_products.RipeningRate(),
            PySDM_products.ActivatingRate(),
            PySDM_products.DeactivatingRate(),
            PySDM_products.EffectiveRadius(
                radius_range=settings.cloud_water_radius_range),
            PySDM_products.PeakSupersaturation(unit="%"),
            PySDM_products.SuperDropletCountPerGridbox(),
        ]
        self.particulator = builder.build(attributes=attributes,
                                          products=products)