Exemple #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']
Exemple #2
0
 def __init__(self,
              backend,
              n_sd=-1,
              max_iters=-1,
              multiplicity=-1,
              dt=np.nan,
              dv=np.nan,
              rhod=np.nan,
              thd=np.nan,
              qv=np.nan,
              T=np.nan,
              p=np.nan,
              RH=np.nan,
              dry_volume=np.nan,
              wet_radius=np.nan):
     builder = Builder(n_sd=n_sd, backend=backend())
     builder.set_environment(
         _TestEnv(dt=dt, dv=dv, rhod=rhod, thd=thd, qv=qv, T=T, p=p, RH=RH))
     builder.add_dynamic(Condensation(max_iters=max_iters))
     self.particulator = builder.build(
         attributes={
             'n': np.full(n_sd, multiplicity),
             'volume': np.full(n_sd, wet_radius),
             'dry volume': np.full(n_sd, dry_volume),
             'kappa times dry volume': np.ones(n_sd),
         })
def test_critical_supersaturation():
    # arrange
    T = 300 * si.K
    n_sd = 100
    S_max = .01
    vdry = np.linspace(.001, 1, n_sd) * si.um**3

    builder = Builder(n_sd=n_sd, backend=CPU())
    env = Box(dt=np.nan, dv=np.nan)
    builder.set_environment(env)
    env['T'] = T
    particulator = builder.build(attributes={
        'n':
        np.ones(n_sd),
        'volume':
        np.linspace(.01, 10, n_sd) * si.um**3,
        'dry volume':
        vdry,
        'kappa times dry volume':
        .9 * vdry,
        'dry volume organic':
        np.zeros(n_sd)
    },
                                 products=[ActivableFraction()])

    # act
    AF = particulator.products['activable fraction'].get(S_max)

    # assert
    assert 0 < AF < 1
Exemple #4
0
    def test_single_collision_bounce(params, backend_class = CPU):
        # Arrange
        n_sd = 2
        builder = Builder(n_sd, backend_class())
        builder.set_environment(Box(dv=np.NaN, dt=np.NaN))
        n_init = [1, 1]
        particulator = builder.build(attributes = {
                "n": np.asarray(n_init),
                "volume": np.asarray([100*si.um**3, 100*si.um**3])
            }, products = ())

        pairwise_zeros = particulator.PairwiseStorage.from_ndarray(np.array([0.0]))
        general_zeros = particulator.Storage.from_ndarray(np.array([0.0]))

        gamma = particulator.PairwiseStorage.from_ndarray(np.array([params["gamma"]]))
        rand = particulator.PairwiseStorage.from_ndarray(np.array([params["rand"]]))
        n_fragment = particulator.PairwiseStorage.from_ndarray(np.array([4]))
        is_first_in_pair = make_PairIndicator(backend_class)(n_sd)

        # Act
        particulator.collision_coalescence_breakup(
            enable_breakup=True,
            gamma=gamma, rand=rand, Ec=pairwise_zeros, Eb=pairwise_zeros,
            n_fragment=n_fragment, coalescence_rate=general_zeros,
            breakup_rate=general_zeros, is_first_in_pair=is_first_in_pair
        )

        # Assert
        assert (particulator.attributes['n'].to_ndarray() == n_init).all()
Exemple #5
0
    def test_multiplicity_overflow(backend = CPU()):
        # Arrange
        params = {"gamma": [100.0], "n_init": [1, 1], "v_init": [1, 1],
            "is_first_in_pair": [True, False], "n_fragment": [4]}
        n_init = params["n_init"]
        n_sd = len(n_init)
        builder = Builder(n_sd, backend)
        builder.set_environment(Box(dv=np.NaN, dt=np.NaN))
        particulator = builder.build(attributes = {
                "n": np.asarray(n_init),
                "volume": np.asarray(params["v_init"])
            }, products=())

        n_pairs = n_sd // 2
        rand = [1.0] * n_pairs
        Eb = [1.0] * n_pairs
        pairwise_zeros = particulator.PairwiseStorage.from_ndarray(np.array([0.0] * n_pairs))
        general_zeros = particulator.Storage.from_ndarray(np.array([0.0] * n_sd))

        gamma = particulator.PairwiseStorage.from_ndarray(np.array(params["gamma"]))
        rand = particulator.PairwiseStorage.from_ndarray(np.array(rand))
        Eb = particulator.PairwiseStorage.from_ndarray(np.array(Eb))
        breakup_rate = particulator.Storage.from_ndarray(np.array([0.0]))
        n_fragment = particulator.PairwiseStorage.from_ndarray(np.array(params["n_fragment"]))
        is_first_in_pair = particulator.PairIndicator(n_sd)
        is_first_in_pair.indicator[:] = particulator.Storage.from_ndarray(
            np.asarray(params["is_first_in_pair"]))

        # Act
        particulator.collision_coalescence_breakup(
            enable_breakup=True,
            gamma=gamma, rand=rand, Ec=pairwise_zeros, Eb=Eb,
            n_fragment=n_fragment, coalescence_rate=general_zeros,
            breakup_rate=breakup_rate, is_first_in_pair=is_first_in_pair
        )
def test_courant_product(courant_field):
    # arrange
    n_sd = 1
    builder = Builder(n_sd=n_sd, backend=CPU())
    builder.set_environment(Kinematic2D(
        dt=1,
        grid=GRID,
        size=(100, 100),
        rhod_of=lambda x: x*0+1
    ))
    builder.add_dynamic(Displacement())
    particulator = builder.build(
        attributes={
            'n': np.ones(n_sd),
            'volume': np.ones(n_sd),
            'cell id': np.zeros(n_sd, dtype=int)
        },
        products=(MaxCourantNumber(),)
    )
    sut = particulator.products['max courant number']

    # act
    particulator.dynamics['Displacement'].upload_courant_field(courant_field)
    max_courant = sut.get()

    # assert
    np.testing.assert_allclose(actual=max_courant, desired=np.maximum(
        np.maximum(abs(courant_field[0][1:, :]), abs(courant_field[0][:-1, :])),
        np.maximum(abs(courant_field[1][:, 1:]), abs(courant_field[1][:, :-1]))
    ))
    def test_freeze_time_dependent(plot=False):
        # Arrange
        cases = (
            {'dt': 5e5, 'N':  1},
            {'dt': 1e6, 'N':  1},
            {'dt': 5e5, 'N':  8},
            {'dt': 1e6, 'N':  8},
            {'dt': 5e5, 'N': 32},
            {'dt': 1e6, 'N': 32},
        )
        rate = 1e-9
        immersed_surface_area = 1
        constant.J_het = rate / immersed_surface_area

        number_of_real_droplets = 1024
        total_time = 2e9  # effectively interpretted here as seconds, i.e. cycle = 1 * si.s

        # dummy (but must-be-set) values
        vol = 44  # just to enable sign flipping (ice water uses negative volumes), actual value does not matter
        dv = 666  # products use concentration, just dividing there and multiplying back here, actual value does not matter

        hgh = lambda t: np.exp(-0.8 * rate * (t - total_time / 10))
        low = lambda t: np.exp(-1.2 * rate * (t + total_time / 10))

        # Act
        output = {}

        for case in cases:
            n_sd = int(number_of_real_droplets // case['N'])
            assert n_sd == number_of_real_droplets / case['N']
            assert total_time // case['dt'] == total_time / case['dt']

            key = f"{case['dt']}:{case['N']}"
            output[key] = {'unfrozen_fraction': [], 'dt': case['dt'], 'N': case['N']}

            formulae = Formulae(heterogeneous_ice_nucleation_rate='Constant')
            builder = Builder(n_sd=n_sd, backend=CPU(formulae=formulae))
            env = Box(dt=case['dt'], dv=dv)
            builder.set_environment(env)
            builder.add_dynamic(Freezing(singular=False))
            attributes = {
                'n': np.full(n_sd, int(case['N'])),
                'immersed surface area': np.full(n_sd, immersed_surface_area),
                'volume': np.full(n_sd, vol)
            }
            products = (IceWaterContent(specific=False),)
            particulator = builder.build(attributes=attributes, products=products)

            env['a_w_ice'] = np.nan

            cell_id = 0
            for i in range(int(total_time / case['dt']) + 1):
                particulator.run(0 if i == 0 else 1)

                ice_mass_per_volume = particulator.products['qi'].get()[cell_id]
                ice_mass = ice_mass_per_volume * dv
                ice_number = ice_mass / (const.rho_w * vol)
                unfrozen_fraction = 1 - ice_number / number_of_real_droplets
                output[key]['unfrozen_fraction'].append(unfrozen_fraction)
Exemple #8
0
 def _make_particulator():
     builder = Builder(n_sd=n_sd, backend=CPU())
     env = Box(dt=dt, dv=np.nan)
     builder.set_environment(env)
     env['T'] = T
     return builder.build(attributes={
         'n': np.ones(n_sd),
         'volume': np.linspace(.01, 10, n_sd) * si.um**3
     },
                          products=(CoolingRate(), ))
Exemple #9
0
def make_particulator(
    *,
    constants,
    n_sd,
    dt,
    initial_temperature,
    singular,
    seed,
    shima_T_fz,
    ABIFM_spec,
    droplet_volume,
    total_particle_number,
    volume
):
    attributes = {"volume": np.ones(n_sd) * droplet_volume}

    formulae_ctor_args = {"seed": seed, "constants": constants}
    if singular:
        formulae_ctor_args["freezing_temperature_spectrum"] = shima_T_fz
    else:
        formulae_ctor_args["heterogeneous_ice_nucleation_rate"] = "ABIFM"
    formulae = Formulae(**formulae_ctor_args)

    if singular:
        sampling = SpectroGlacialSampling(
            freezing_temperature_spectrum=formulae.freezing_temperature_spectrum,
            insoluble_surface_spectrum=ABIFM_spec,
            seed=formulae.seed,
        )
        attributes["freezing temperature"], _, attributes["n"] = sampling.sample(n_sd)
    else:
        sampling = ConstantMultiplicity(
            spectrum=ABIFM_spec,
            # seed=formulae.seed
        )
        attributes["immersed surface area"], attributes["n"] = sampling.sample(n_sd)
    attributes["n"] *= total_particle_number

    builder = Builder(n_sd, CPU(formulae))

    env = Box(dt, volume)
    builder.set_environment(env)
    env["T"] = initial_temperature
    env["RH"] = A_VALUE_LARGER_THAN_ONE

    builder.add_dynamic(Freezing(singular=singular))

    return builder.build(
        attributes=attributes,
        products=(
            PySDM_products.Time(name="t"),
            PySDM_products.AmbientTemperature(name="T"),
            PySDM_products.SpecificIceWaterContent(name="qi"),
        ),
    )
Exemple #10
0
    def __init__(self, settings, backend=CPU):
        dt_output = (settings.total_time / settings.n_steps
                     )  # TODO #334 overwritten in notebook
        self.n_substeps = 1  # TODO #334 use condensation substeps
        while dt_output / self.n_substeps >= settings.dt_max:
            self.n_substeps += 1
        self.formulae = Formulae(
            condensation_coordinate=settings.coord,
            saturation_vapour_pressure="AugustRocheMagnus",
        )
        self.bins_edges = self.formulae.trivia.volume(settings.r_bins_edges)
        builder = Builder(backend=backend(formulae=self.formulae),
                          n_sd=settings.n_sd)
        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,
                z0=settings.z0,
            ))

        environment = builder.particulator.environment
        builder.add_dynamic(AmbientThermodynamics())
        condensation = Condensation(
            adaptive=settings.adaptive,
            rtol_x=settings.rtol_x,
            rtol_thd=settings.rtol_thd,
            dt_cond_range=settings.dt_cond_range,
        )
        builder.add_dynamic(condensation)

        products = [
            PySDM_products.ParticleSizeSpectrumPerVolume(
                name="Particles Wet Size Spectrum",
                radius_bins_edges=settings.r_bins_edges,
            ),
            PySDM_products.CondensationTimestepMin(name="dt_cond_min"),
            PySDM_products.CondensationTimestepMax(name="dt_cond_max"),
            PySDM_products.RipeningRate(),
        ]

        attributes = environment.init_attributes(n_in_dv=settings.n,
                                                 kappa=settings.kappa,
                                                 r_dry=settings.r_dry)

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

        self.n_steps = settings.n_steps
Exemple #11
0
    def __init__(
        self, settings, products=None, scipy_solver=False, rtol_thd=1e-10, rtol_x=1e-10
    ):
        env = Parcel(
            dt=settings.timestep,
            p0=settings.initial_pressure,
            q0=settings.initial_vapour_mixing_ratio,
            T0=settings.initial_temperature,
            w=settings.vertical_velocity,
            mass_of_dry_air=44 * si.kg,
        )
        n_sd = sum(settings.n_sd_per_mode)
        builder = Builder(n_sd=n_sd, backend=CPU(formulae=settings.formulae))
        builder.set_environment(env)
        builder.add_dynamic(AmbientThermodynamics())
        builder.add_dynamic(Condensation(rtol_thd=rtol_thd, rtol_x=rtol_x))

        volume = env.mass_of_dry_air / settings.initial_air_density
        attributes = {
            k: np.empty(0) for k in ("dry volume", "kappa times dry volume", "n")
        }
        for i, (kappa, spectrum) in enumerate(settings.aerosol_modes_by_kappa.items()):
            sampling = ConstantMultiplicity(spectrum)
            r_dry, n_per_volume = sampling.sample(settings.n_sd_per_mode[i])
            v_dry = settings.formulae.trivia.volume(radius=r_dry)
            attributes["n"] = np.append(attributes["n"], n_per_volume * volume)
            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=settings.formulae.trivia.radius(volume=attributes["dry volume"]),
            environment=env,
            kappa_times_dry_volume=attributes["kappa times dry volume"],
        )
        attributes["volume"] = settings.formulae.trivia.volume(radius=r_wet)

        super().__init__(
            particulator=builder.build(attributes=attributes, products=products)
        )
        if scipy_solver:
            bdf.patch_particulator(self.particulator)

        self.output_attributes = {
            "volume": tuple([] for _ in range(self.particulator.n_sd))
        }
        self.settings = settings

        self.__sanity_checks(attributes, volume)
Exemple #12
0
    def test_noninteger_fragments(params, flag, backend_class = CPU):
        # Arrange
        n_init = params["n_init"]
        n_sd = len(n_init)
        builder = Builder(n_sd, backend_class())
        builder.set_environment(Box(dv=np.NaN, dt=np.NaN))
        particulator = builder.build(attributes = {
                "n": np.asarray(n_init),
                "volume": np.asarray(params["v_init"])
            }, products = ())

        n_pairs = n_sd // 2
        rand = [1.0] * n_pairs
        Eb = [1.0] * n_pairs
        pairwise_zeros = particulator.PairwiseStorage.from_ndarray(np.array([0.0] * n_pairs))
        general_zeros = particulator.Storage.from_ndarray(np.array([0.0] * n_sd))

        gamma = particulator.PairwiseStorage.from_ndarray(np.array(params["gamma"]))
        rand = particulator.PairwiseStorage.from_ndarray(np.array(rand))
        Eb = particulator.PairwiseStorage.from_ndarray(np.array(Eb))
        breakup_rate = particulator.Storage.from_ndarray(np.array([0.0]))
        n_fragment = particulator.PairwiseStorage.from_ndarray(np.array(params["n_fragment"]))
        is_first_in_pair = particulator.PairIndicator(n_sd)
        is_first_in_pair.indicator[:] = particulator.Storage.from_ndarray(
            np.asarray(params["is_first_in_pair"]))

        # Act
        particulator.collision_coalescence_breakup(
            enable_breakup=True, gamma=gamma, rand=rand, Ec=pairwise_zeros, Eb=Eb,
            n_fragment=n_fragment, coalescence_rate=general_zeros, breakup_rate=breakup_rate,
            is_first_in_pair=is_first_in_pair
        )

        # Assert
        {
            'n': lambda:
                np.testing.assert_array_equal(particulator.attributes['n'].to_ndarray(),
                np.array(params["n_expected"])),
            'v': lambda:
                np.testing.assert_array_almost_equal(particulator.attributes['volume'].to_ndarray(),
                np.array(params["v_expected"]), decimal=6),
            'conserve': lambda:
                np.testing.assert_almost_equal(np.sum(particulator.attributes['n'].to_ndarray() *
                    particulator.attributes['volume'].to_ndarray()),
                    np.sum(np.array(params["n_init"]) * np.array(params["v_init"])),
                    decimal=6)
        }[flag]()
Exemple #13
0
    def test_nonadaptive_same_results_regardless_of_dt(dt, backend_class = CPU):
        # Arrange
        attributes = {"n": np.asarray([1, 1]), "volume": np.asarray([100*si.um**3, 100*si.um**3])}
        breakup = Breakup(ConstantK(1 * si.cm**3 / si.s), AlwaysN(4), adaptive=False)
        nsteps = 10

        n_sd = len(attributes["n"])
        builder = Builder(n_sd, backend_class())
        builder.set_environment(Box(dv=1*si.cm**3, dt=dt))
        builder.add_dynamic(breakup)
        particulator = builder.build(attributes = attributes, products = ())

        # Act
        particulator.run(nsteps)

        # Assert
        assert (particulator.attributes['n'].to_ndarray() > 0).all()
        assert (particulator.attributes['n'].to_ndarray() != attributes['n']).any()
        assert (np.sum(particulator.attributes['n'].to_ndarray()) >= np.sum(attributes['n']))
        assert (particulator.attributes['n'].to_ndarray() == np.array([1024, 1024])).all()
Exemple #14
0
    def test_breakup_counters(params, backend_class = CPU):
        # Arrange
        n_init = params["n_init"]
        n_sd = len(n_init)
        builder = Builder(n_sd, backend_class())
        builder.set_environment(Box(dv=np.NaN, dt=np.NaN))
        particulator = builder.build(attributes = {
                "n": np.asarray(n_init),
                "volume": np.asarray([100*si.um**3] * n_sd)
            }, products = ())

        n_pairs = n_sd // 2
        pairwise_zeros = particulator.PairwiseStorage.from_ndarray(np.array([0.0] * n_pairs))
        general_zeros = particulator.Storage.from_ndarray(np.array([0.0] * n_sd))

        gamma = particulator.PairwiseStorage.from_ndarray(np.array([params["gamma"]] * n_pairs))
        rand = particulator.PairwiseStorage.from_ndarray(np.array([params["rand"]] * n_pairs))
        Eb = particulator.PairwiseStorage.from_ndarray(np.array([params["Eb"]] * n_pairs))
        breakup_rate = particulator.Storage.from_ndarray(np.array([0.0]))
        n_fragment = particulator.PairwiseStorage.from_ndarray(np.array([4] * n_pairs))
        is_first_in_pair = particulator.PairIndicator(n_sd)
        is_first_in_pair.indicator[:] = particulator.Storage.from_ndarray(
            np.asarray(params["is_first_in_pair"]))

        # Act
        particulator.collision_coalescence_breakup(
            enable_breakup=True,
            gamma=gamma, rand=rand, Ec=pairwise_zeros, Eb=Eb,
            n_fragment=n_fragment, coalescence_rate=general_zeros,
            breakup_rate=breakup_rate, is_first_in_pair=is_first_in_pair
        )

        # Assert
        cell_id = 0
        assert (breakup_rate.to_ndarray()[cell_id] == np.sum(params["gamma"] *
            get_smaller_of_pairs(is_first_in_pair, n_init)))
Exemple #15
0
    def __init__(self, settings, backend=CPU):
        self.nt = settings.nt

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

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

        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,
                         kappa=settings.kappa))
        builder.add_dynamic(EulerianAdvection(mpdata))
        if settings.precip:
            builder.add_dynamic(
                Coalescence(kernel=Geometric(collection_efficiency=1),
                            adaptive=settings.coalescence_adaptive))
            builder.add_dynamic(
                Displacement(enable_sedimentation=True,
                             courant_field=(np.zeros(settings.nz +
                                                     1), )))  # TODO #414
        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.RelativeHumidity(),
            PySDM_products.Pressure(),
            PySDM_products.Temperature(),
            PySDM_products.WaterVapourMixingRatio(),
            PySDM_products.WaterMixingRatio(
                name='ql',
                description_prefix='cloud',
                radius_range=settings.cloud_water_radius_range),
            PySDM_products.WaterMixingRatio(
                name='qr',
                description_prefix='rain',
                radius_range=settings.rain_water_radius_range),
            PySDM_products.DryAirDensity(),
            PySDM_products.DryAirPotentialTemperature(),
            PySDM_products.ParticlesDrySizeSpectrum(
                v_bins=settings.v_bin_edges),
            PySDM_products.ParticlesWetSizeSpectrum(
                v_bins=settings.v_bin_edges),
            PySDM_products.CloudDropletConcentration(
                radius_range=settings.cloud_water_radius_range),
            PySDM_products.AerosolConcentration(
                radius_threshold=settings.cloud_water_radius_range[0]),
            PySDM_products.ParticleMeanRadius(),
            PySDM_products.RipeningRate(),
            PySDM_products.ActivatingRate(),
            PySDM_products.DeactivatingRate(),
            PySDM_products.CloudDropletEffectiveRadius(
                radius_range=settings.cloud_water_radius_range),
            PySDM_products.PeakSupersaturation()
        ]
        self.core = builder.build(attributes=attributes, products=products)
Exemple #16
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)
Exemple #17
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,
                     g=settings.g)

        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(kappa=settings.kappa))
        builder.add_dynamic(
            AqueousChemistry(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.RelativeHumidity(),
            PySDM_products.WaterMixingRatio(name='ql',
                                            description_prefix='liquid',
                                            radius_range=[1 * si.um, np.inf]),
            PySDM_products.ParcelDisplacement(), PySDM_products.Pressure(),
            PySDM_products.Temperature(), PySDM_products.DryAirDensity(),
            PySDM_products.WaterVapourMixingRatio(), PySDM_products.Time(), *[
                PySDM_products.AqueousMoleFraction(compound)
                for compound in AQUEOUS_COMPOUNDS.keys()
            ], *[
                PySDM_products.GaseousMoleFraction(compound)
                for compound in GASEOUS_COMPOUNDS.keys()
            ],
            PySDM_products.pH(radius_range=settings.cloud_radius_range,
                              weighting='number',
                              attr='pH'),
            PySDM_products.pH(radius_range=settings.cloud_radius_range,
                              weighting='volume',
                              attr='pH'),
            PySDM_products.pH(radius_range=settings.cloud_radius_range,
                              weighting='number',
                              attr='conc_H'),
            PySDM_products.pH(radius_range=settings.cloud_radius_range,
                              weighting='volume',
                              attr='conc_H'),
            PySDM_products.TotalDryMassMixingRatio(
                settings.DRY_RHO), PySDM_products.PeakSupersaturation(),
            PySDM_products.CloudDropletConcentration(
                radius_range=settings.cloud_radius_range),
            PySDM_products.AqueousMassSpectrum("S_VI",
                                               settings.dry_radius_bins_edges))

        self.core = builder.build(attributes=attributes, products=products)
        self.settings = settings
Exemple #18
0
def simulation(
    *,
    constants,
    seed,
    n_sd,
    time_step,
    volume,
    spectrum,
    droplet_volume,
    multiplicity,
    total_time,
    number_of_real_droplets,
    cooling_rate=0,
    heterogeneous_ice_nucleation_rate="Constant",
    initial_temperature=np.nan,
):
    formulae = Formulae(
        seed=seed,
        heterogeneous_ice_nucleation_rate=heterogeneous_ice_nucleation_rate,
        constants=constants,
    )
    builder = Builder(n_sd=n_sd, backend=CPU(formulae=formulae))
    env = Box(dt=time_step, dv=volume)
    builder.set_environment(env)
    builder.add_dynamic(Freezing(singular=False))

    if hasattr(spectrum, "s_geom") and spectrum.s_geom == 1:
        _isa, _conc = np.full(n_sd, spectrum.m_mode), np.full(
            n_sd, multiplicity / volume)
    else:
        _isa, _conc = spectral_sampling.ConstantMultiplicity(spectrum).sample(
            n_sd)
    attributes = {
        "n": discretise_multiplicities(_conc * volume),
        "immersed surface area": _isa,
        "volume": np.full(n_sd, droplet_volume),
    }
    np.testing.assert_almost_equal(attributes["n"], multiplicity)
    products = (
        IceWaterContent(name="qi"),
        TotalUnfrozenImmersedSurfaceArea(name="A_tot"),
    )
    particulator = builder.build(attributes=attributes, products=products)

    temperature = initial_temperature
    env["a_w_ice"] = np.nan
    svp = particulator.formulae.saturation_vapour_pressure

    cell_id = 0
    f_ufz = []
    a_tot = []
    for i in range(int(total_time / time_step) + 1):
        if cooling_rate != 0:
            temperature -= cooling_rate * time_step / 2
            env["a_w_ice"] = svp.ice_Celsius(
                temperature - const.T0) / svp.pvs_Celsius(temperature -
                                                          const.T0)
        particulator.run(0 if i == 0 else 1)
        if cooling_rate != 0:
            temperature -= cooling_rate * time_step / 2

        ice_mass_per_volume = particulator.products["qi"].get()[cell_id]
        ice_mass = ice_mass_per_volume * volume
        ice_number = ice_mass / (formulae.constants.rho_w * droplet_volume)
        unfrozen_fraction = 1 - ice_number / number_of_real_droplets
        f_ufz.append(unfrozen_fraction)
        a_tot.append(particulator.products["A_tot"].get()[cell_id])
    return f_ufz, a_tot
Exemple #19
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)
Exemple #20
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)
Exemple #21
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,
    )