Ejemplo n.º 1
0
    def test_size_distribution():
        # Arrange
        sut = Sum((TestSum.exponential, ))

        # Act
        x = np.linspace(0, 1)
        sut_sd = sut.size_distribution(x)
        exp_sd = TestSum.exponential.size_distribution(x)

        # Assert
        np.testing.assert_array_equal(sut_sd, exp_sd)
Ejemplo n.º 2
0
    def test_percentiles(distributions):
        # Arrange
        sut = Sum(distributions)

        # Act
        cdf_values = np.linspace(*default_cdf_range, 100)
        sut_p = sut.percentiles(cdf_values)
        exp_p = distributions[0].percentiles(cdf_values)

        # Assert
        np.testing.assert_array_almost_equal(sut_p, exp_p, decimal=3)
Ejemplo n.º 3
0
    def test_cumulative(self):
        # Arrange
        sut = Sum((TestSum.exponential, ))

        # Act
        x = np.linspace(0, 1)
        sut_c = sut.cumulative(x)
        exp_c = TestSum.exponential.cumulative(x)

        # Assert
        np.testing.assert_array_equal(sut_c, exp_c)
Ejemplo n.º 4
0
    def test_percentiles(distributions):
        # Arrange
        sut = Sum(distributions)

        # Act
        cdf_values = default_interpolation_grid
        sut_p = sut.percentiles(cdf_values)
        exp_p = distributions[0].percentiles(cdf_values)

        # Assert
        np.testing.assert_array_almost_equal(sut_p, exp_p, decimal=3)
Ejemplo n.º 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,
        )
        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)
Ejemplo n.º 6
0
class Setup:
    backend = Default
    condensation_coord = 'volume logarithm'

    condensation_rtol_x = condensation.default_rtol_x
    condensation_rtol_thd = condensation.default_rtol_thd
    adaptive = True

    grid = (25, 25)
    size = (1500 * si.metres, 1500 * si.metres)
    n_sd_per_gridbox = 20
    rho_w_max = .6 * si.metres / si.seconds * (si.kilogram / si.metre**3)

    # output steps
    n_steps = 5400
    outfreq = 60
    dt = 1 * si.seconds

    v_bins = phys.volume(
        np.logspace(np.log10(0.01 * si.micrometre),
                    np.log10(100 * si.micrometre),
                    101,
                    endpoint=True))

    @property
    def steps(self):
        return np.arange(0, self.n_steps + 1, self.outfreq)

    mode_1 = Lognormal(norm_factor=60 / si.centimetre**3 / const.rho_STP,
                       m_mode=0.04 * si.micrometre,
                       s_geom=1.4)
    mode_2 = Lognormal(norm_factor=40 / si.centimetre**3 / const.rho_STP,
                       m_mode=0.15 * si.micrometre,
                       s_geom=1.6)
    spectrum_per_mass_of_dry_air = Sum((mode_1, mode_2))

    processes = {
        "particle advection": True,
        "fluid advection": True,
        "coalescence": True,
        "condensation": True,
        "sedimentation": True,
        # "relaxation": False  # TODO
    }

    enable_particle_temperatures = False

    mpdata_iters = 2
    mpdata_iga = True
    mpdata_fct = True
    mpdata_tot = True

    th_std0 = 289 * si.kelvins
    qv0 = 7.5 * si.grams / si.kilogram
    p0 = 1015 * si.hectopascals
    kappa = 1

    @property
    def field_values(self):
        return {'th': phys.th_dry(self.th_std0, self.qv0), 'qv': self.qv0}

    @property
    def n_sd(self):
        return self.grid[0] * self.grid[1] * self.n_sd_per_gridbox

    def stream_function(self, xX, zZ):
        X = self.size[0]
        return -self.rho_w_max * X / np.pi * np.sin(np.pi * zZ) * np.cos(
            2 * np.pi * xX)

    def rhod(self, zZ):
        Z = self.size[1]
        z = zZ * Z  # :)

        # hydrostatic profile
        kappa = const.Rd / const.c_pd
        arg = np.power(
            self.p0 / const.p1000,
            kappa) - z * kappa * const.g / self.th_std0 / phys.R(self.qv0)
        p = const.p1000 * np.power(arg, 1 / kappa)

        # density using "dry" potential temp.
        pd = p * (1 - self.qv0 / (self.qv0 + const.eps))
        rhod = pd / (np.power(p / const.p1000, kappa) * const.Rd *
                     self.th_std0)

        return rhod

    # initial dry radius discretisation range
    r_min = spectrum_per_mass_of_dry_air.percentiles(.01)
    r_max = spectrum_per_mass_of_dry_air.percentiles(.99)

    kernel = Geometric(collection_efficiency=.5 / si.s)
    aerosol_radius_threshold = .5 * si.micrometre
    drizzle_radius_threshold = 25 * si.micrometre

    n_spin_up = 1 * si.hour / dt
Ejemplo n.º 7
0
class Settings:
    def __dir__(self) -> Iterable[str]:
        return 'dt', 'grid', 'size', 'n_spin_up', 'versions', 'outfreq'

    def __init__(self):
        key_packages = (PySDM, numba, numpy, scipy)
        self.versions = str(
            {pkg.__name__: pkg.__version__
             for pkg in key_packages})

    # TODO: move all below into __init__ as self.* variables

    condensation_coord = 'volume logarithm'

    condensation_rtol_x = condensation.default_rtol_x
    condensation_rtol_thd = condensation.default_rtol_thd
    adaptive = True

    grid = (25, 25)
    size = (1500 * si.metres, 1500 * si.metres)
    n_sd_per_gridbox = 20
    rho_w_max = .6 * si.metres / si.seconds * (si.kilogram / si.metre**3)

    # output steps
    n_steps = 5400
    outfreq = 60
    dt = 1 * si.seconds

    n_spin_up = 1 * si.hour / dt

    v_bins = phys.volume(
        np.logspace(np.log10(0.01 * si.micrometre),
                    np.log10(100 * si.micrometre),
                    101,
                    endpoint=True))

    @property
    def steps(self):
        return np.arange(0, self.n_steps + 1, self.outfreq)

    mode_1 = Lognormal(norm_factor=60 / si.centimetre**3 / const.rho_STP,
                       m_mode=0.04 * si.micrometre,
                       s_geom=1.4)
    mode_2 = Lognormal(norm_factor=40 / si.centimetre**3 / const.rho_STP,
                       m_mode=0.15 * si.micrometre,
                       s_geom=1.6)
    spectrum_per_mass_of_dry_air = Sum((mode_1, mode_2))

    processes = {
        "particle advection": True,
        "fluid advection": True,
        "coalescence": True,
        "condensation": True,
        "sedimentation": True,
        # "relaxation": False  # TODO
    }

    enable_particle_temperatures = False

    mpdata_iters = 2
    mpdata_iga = True
    mpdata_fct = True
    mpdata_tot = True

    th_std0 = 289 * si.kelvins
    qv0 = 7.5 * si.grams / si.kilogram
    p0 = 1015 * si.hectopascals
    kappa = 1

    @property
    def field_values(self):
        return {'th': phys.th_dry(self.th_std0, self.qv0), 'qv': self.qv0}

    @property
    def n_sd(self):
        return self.grid[0] * self.grid[1] * self.n_sd_per_gridbox

    def stream_function(self, xX, zZ):
        X = self.size[0]
        return -self.rho_w_max * X / np.pi * np.sin(np.pi * zZ) * np.cos(
            2 * np.pi * xX)

    def rhod(self, zZ):
        Z = self.size[1]
        z = zZ * Z  # :(!

        # TODO: move to PySDM/physics
        # hydrostatic profile
        kappa = const.Rd / const.c_pd
        arg = np.power(
            self.p0 / const.p1000,
            kappa) - z * kappa * const.g / self.th_std0 / phys.R(self.qv0)
        p = const.p1000 * np.power(arg, 1 / kappa)

        # density using "dry" potential temp.
        pd = p * (1 - self.qv0 / (self.qv0 + const.eps))
        rhod = pd / (np.power(p / const.p1000, kappa) * const.Rd *
                     self.th_std0)

        return rhod

    kernel = Geometric(collection_efficiency=1)
    aerosol_radius_threshold = .5 * si.micrometre
    drizzle_radius_threshold = 25 * si.micrometre