Beispiel #1
0
    def test_moment_0d():
        # Arrange
        n_part = 10000
        v_mean = 2e-6
        d = 1.2

        v_min = 0.01e-6
        v_max = 10e-6
        n_sd = 32

        spectrum = Lognormal(n_part, v_mean, d)
        v, n = linear(n_sd, spectrum, (v_min, v_max))
        T = np.full_like(v, 300.)
        n = discretise_n(n)
        particles = DummyCore(backend, n_sd)
        attribute = {'n': n, 'volume': v, 'temperature': T}
        particles.build(attribute)
        state = particles.state

        true_mean, true_var = spectrum.stats(moments='mv')

        # TODO: add a moments_0 wrapper
        moment_0 = particles.backend.Storage.empty((1,), dtype=int)
        moments = particles.backend.Storage.empty((1, 1), dtype=float)

        # Act
        state.moments(moment_0, moments, specs={'volume': (0,)})
        discr_zero = moments[0, 0]

        state.moments(moment_0, moments, specs={'volume': (1,)})
        discr_mean = moments[0, 0]

        state.moments(moment_0, moments, specs={'volume': (2,)})
        discr_mean_radius_squared = moments[0, 0]

        state.moments(moment_0, moments, specs={'temperature': (0,)})
        discr_zero_T = moments[0, 0]

        state.moments(moment_0, moments, specs={'temperature': (1,)})
        discr_mean_T = moments[0, 0]

        state.moments(moment_0, moments, specs={'temperature': (2,)})
        discr_mean_T_squared = moments[0, 0]

        # Assert
        assert abs(discr_zero - 1) / 1 < 1e-3

        assert abs(discr_mean - true_mean) / true_mean < .01e-1

        true_mrsq = true_var + true_mean**2
        assert abs(discr_mean_radius_squared - true_mrsq) / true_mrsq < .05e-1
        
        assert discr_zero_T == discr_zero
        assert discr_mean_T == 300.
        assert discr_mean_T_squared == 300. ** 2
Beispiel #2
0
    def test_moment_0d(backend):
        # Arrange
        n_part = 100000
        v_mean = 2e-6
        d = 1.2
        n_sd = 32

        spectrum = Lognormal(n_part, v_mean, d)
        v, n = Linear(spectrum).sample(n_sd)
        T = np.full_like(v, 300.)
        n = discretise_n(n)
        particles = DummyCore(backend, n_sd)
        attribute = {'n': n, 'volume': v, 'temperature': T, 'heat': T * v}
        particles.build(attribute)
        state = particles.particles

        true_mean, true_var = spectrum.stats(moments='mv')

        # TODO #217 : add a moments_0 wrapper
        moment_0 = particles.backend.Storage.empty((1, ), dtype=int)
        moments = particles.backend.Storage.empty((1, 1), dtype=float)

        # Act
        state.moments(moment_0, moments, specs={'volume': (0, )})
        discr_zero = moments[0, 0]

        state.moments(moment_0, moments, specs={'volume': (1, )})
        discr_mean = moments[0, 0]

        state.moments(moment_0, moments, specs={'volume': (2, )})
        discr_mean_radius_squared = moments[0, 0]

        state.moments(moment_0, moments, specs={'temperature': (0, )})
        discr_zero_T = moments[0, 0]

        state.moments(moment_0, moments, specs={'temperature': (1, )})
        discr_mean_T = moments[0, 0]

        state.moments(moment_0, moments, specs={'temperature': (2, )})
        discr_mean_T_squared = moments[0, 0]

        # Assert
        assert abs(discr_zero - 1) / 1 < 1e-3

        assert abs(discr_mean - true_mean) / true_mean < .01e-1

        true_mrsq = true_var + true_mean**2
        assert abs(discr_mean_radius_squared - true_mrsq) / true_mrsq < .05e-1

        assert discr_zero_T == discr_zero
        assert discr_mean_T == 300.
        np.testing.assert_approx_equal(discr_mean_T_squared,
                                       300.**2,
                                       significant=6)
Beispiel #3
0
    def test_size_distribution_r_mode():
        # Arrange
        s = 1.001
        r_mode = 1e-6
        sut = Lognormal(1, r_mode, s)

        # Act
        m, _ = np.linspace(.01e-6, 100e-6, 10000, retstep=True)
        sd = sut.size_distribution(m)

        # Assert
        assert_approx_equal(m[sd == np.amax(sd)], r_mode, 2)
Beispiel #4
0
    def test_size_distribution_n_part():
        # Arrange
        s = 1.5
        n_part = 256
        sut = Lognormal(n_part, .5e-5, s)

        # Act
        m, dm = np.linspace(.1e-6, 100e-6, 100, retstep=True)
        sd = sut.size_distribution(m)

        # Assert
        assert_approx_equal(np.sum(sd) * dm, n_part, 4)
Beispiel #5
0
 def freezing_inp_spec(self):
     if self.ui_freezing["INP surface"].value == "as dry surface":
         return None
     if self.ui_freezing["INP surface"].value == "lognormal(A, sgm_g)":
         return Lognormal(
             norm_factor=1,
             m_mode=10 ** (self.ui_freezing["lognormal_log10_A_um2"].value)
             * si.um**2,
             s_geom=np.exp(self.ui_freezing["lognormal_ln_sgm_g"].value),
         )
     raise NotImplementedError()
def test_spectral_discretisation(discretisation):
    # Arrange
    n_sd = 100
    m_mode = .5e-5
    n_part = 256 * 16
    s_geom = 1.5
    spectrum = Lognormal(n_part, m_mode, s_geom)
    m_range = (.1e-6, 100e-6)

    # Act
    m, n = discretisation(n_sd, spectrum, m_range)

    # Assert
    assert m.shape == n.shape
    assert n.shape == (n_sd, )
    assert np.min(m) >= m_range[0]
    assert np.max(m) <= m_range[1]
    actual = np.sum(n)
    desired = spectrum.cumulative(m_range[1]) - spectrum.cumulative(m_range[0])
    quotient = actual / desired
    np.testing.assert_almost_equal(actual=quotient, desired=1.0, decimal=2)
Beispiel #7
0
    def test_supersaturation_and_temperature_profile(s_max, s_250m, T_250m):
        # arrange
        settings = Settings(
            dz = 1 * si.m,
            n_sd_per_mode = (5, 5),
            aerosol_modes_by_kappa = {
                .54: Lognormal(
                    norm_factor=850 / si.cm ** 3,
                    m_mode=15 * si.nm,
                    s_geom=1.6
                ),
                1.2: Lognormal(
                    norm_factor=10 / si.cm ** 3,
                    m_mode=850 * si.nm,
                    s_geom=1.2
                )
            },
            vertical_velocity = 1.0 * si.m / si.s,
            initial_pressure = 775 * si.mbar,
            initial_temperature = 274 * si.K,
            initial_relative_humidity = .98,
            displacement = 250 * si.m,
            formulae = Formulae(constants={'MAC': .3})
        )
        simulation = Simulation(settings, products=(
            ParcelDisplacement(
                name='z'),
            PeakSupersaturation(
                name='S_max', unit='%'),
            AmbientTemperature(
                name='T'),
        ))

        # act
        output = simulation.run()

        # assert
        np.testing.assert_approx_equal(np.nanmax(output['products']['S_max']), s_max, significant=2)
        np.testing.assert_approx_equal(output['products']['S_max'][-1], s_250m, significant=2)
        np.testing.assert_approx_equal(output['products']['T'][-1], T_250m, significant=2)
Beispiel #8
0
class TestSum:
    scale = 1
    n_part = 256
    exponential = Exponential(n_part, scale)

    s = 1.001
    r_mode = 1e-6
    lognormal = Lognormal(1, r_mode, s)

    @staticmethod
    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)

    @staticmethod
    def test_cumulative():
        # 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)

    @staticmethod
    @pytest.mark.parametrize("distributions", [
        pytest.param((exponential, ), id="single exponential"),
        pytest.param((lognormal, ), id="single lognormal"),
        pytest.param((exponential, exponential), id="2 exponentials")
    ])
    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)
Beispiel #9
0
    def __init__(self,
                 n_sd=100,
                 dt_output=1 * si.second,
                 dt_max=1 * si.second):
        self.n_steps = int(self.total_time /
                           (5 * si.second))  # TODO: rename to n_output
        self.n_sd = n_sd
        self.r_dry, self.n = spectral_sampling.Logarithmic(
            spectrum=Lognormal(norm_factor=1000 / si.milligram *
                               self.mass_of_dry_air,
                               m_mode=50 * si.nanometre,
                               s_geom=1.4),
            size_range=(10.633 * si.nanometre,
                        513.06 * si.nanometre)).sample(n_sd)
        self.dt_max = dt_max

        self.dt_output = dt_output
        self.r_bins_edges = np.linspace(0 * si.micrometre,
                                        20 * si.micrometre,
                                        101,
                                        endpoint=True)
Beispiel #10
0
def test_final_state(croupier):
    # Arrange
    n_part = 10000
    v_mean = 2e-6
    d = 1.2
    v_min = 0.01e-6
    v_max = 10e-6
    n_sd = 64
    x = 4
    y = 4

    attributes = {}
    spectrum = Lognormal(n_part, v_mean, d)
    attributes['volume'], attributes['n'] = linear(n_sd, spectrum,
                                                   (v_min, v_max))
    particles = DummyParticles(backend, n_sd)
    particles.set_environment(DummyEnvironment, {'grid': (x, y)})
    particles.croupier = croupier

    attributes['cell id'] = backend.array((n_sd, ), dtype=int)
    cell_origin_np = np.concatenate(
        [np.random.randint(0, x, n_sd),
         np.random.randint(0, y, n_sd)]).reshape((2, -1))
    attributes['cell origin'] = backend.from_ndarray(cell_origin_np)
    position_in_cell_np = np.concatenate(
        [np.random.rand(n_sd), np.random.rand(n_sd)]).reshape((2, -1))
    attributes['position in cell'] = backend.from_ndarray(position_in_cell_np)
    particles.get_particles(attributes)

    # Act
    u01 = backend.from_ndarray(np.random.random(n_sd))
    particles.permute(u01)
    _ = particles.state.cell_start

    # Assert
    assert (np.diff(particles.state['cell id'][particles.state._State__idx]) >=
            0).all()
Beispiel #11
0
def test_final_state(croupier, backend):
    from PySDM.backends import ThrustRTC
    if backend is ThrustRTC:
        return  # TODO

    # Arrange
    n_part = 10000
    v_mean = 2e-6
    d = 1.2
    v_min = 0.01e-6
    v_max = 10e-6
    n_sd = 64
    x = 4
    y = 4

    attributes = {}
    spectrum = Lognormal(n_part, v_mean, d)
    attributes['volume'], attributes['n'] = Linear(spectrum, (v_min, v_max)).sample(n_sd)
    core = DummyCore(backend, n_sd)
    core.environment = DummyEnvironment(grid=(x, y))
    core.croupier = croupier

    attributes['cell id'] = np.array((n_sd,), dtype=int)
    cell_origin_np = np.concatenate([np.random.randint(0, x, n_sd), np.random.randint(0, y, n_sd)]).reshape((2, -1))
    attributes['cell origin'] = cell_origin_np
    position_in_cell_np = np.concatenate([np.random.rand(n_sd), np.random.rand(n_sd)]).reshape((2, -1))
    attributes['position in cell'] = position_in_cell_np
    core.build(attributes)

    # Act
    u01 = backend.Storage.from_ndarray(np.random.random(n_sd))
    core.particles.permutation(u01)
    _ = core.particles.cell_start

    # Assert
    assert (np.diff(core.particles['cell id'][core.particles._Particles__idx]) >= 0).all()
Beispiel #12
0
class Setup:
    backend = Default
    condensation_coord = 'volume'

    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 = 3600
    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)

    # TODO: second mode
    spectrum_per_mass_of_dry_air = Lognormal(norm_factor=40 /
                                             si.centimetre**3 / const.rho_STP,
                                             m_mode=0.15 * si.micrometre,
                                             s_geom=1.6)

    processes = {
        "particle advection": True,
        "fluid advection": True,
        "coalescence": False,
        "condensation": True,
        "sedimentation": False,
        # "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)

        # np.testing.assert_array_less(p, Setup.p0)  # TODO: less or equal

        # 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 = .01 * si.micrometre
    r_max = 5 * si.micrometre

    kernel = Gravitational(collection_efficiency=1)  # [s-1]  # TODO!
    aerosol_radius_threshold = 1 * si.micrometre

    n_spin_up = 1 * si.hour / dt
Beispiel #13
0
 def __init__(self, *, volume=1 * si.cm**3):
     super().__init__(
         volume=volume,
         data={
             "Iso1": {
                 "ISA":
                 Lognormal(norm_factor=1000 / volume,
                           m_mode=1e-5 * si.cm**2,
                           s_geom=1),
                 "color":
                 "#298131",
                 "J_het":
                 1e3 / si.cm**2 / si.s,
             },
             "Iso2": {
                 "ISA":
                 Lognormal(norm_factor=30 / volume,
                           m_mode=1e-5 * si.cm**2,
                           s_geom=1),
                 "color":
                 "#9ACFA4",
                 "J_het":
                 1e3 / si.cm**2 / si.s,
             },
             "Iso3": {
                 "ISA":
                 Lognormal(norm_factor=1000 / volume,
                           m_mode=1e-5 * si.cm**2,
                           s_geom=10),
                 "color":
                 "#1A62B4",
                 "J_het":
                 1e3 / si.cm**2 / si.s,
             },
             "Iso4": {
                 "ISA":
                 Lognormal(norm_factor=30 / volume,
                           m_mode=1e-5 * si.cm**2,
                           s_geom=10),
                 "color":
                 "#95BDE1",
                 "J_het":
                 1e3 / si.cm**2 / si.s,
             },
             "IsoWR": {
                 "ISA":
                 Lognormal(
                     norm_factor=1000 / volume,
                     m_mode=6.4e-3 * si.cm**2,
                     s_geom=9.5,
                 ),
                 "color":
                 "#FED2B0",
                 "J_het":
                 6e-4 / si.cm**2 / si.s,
             },
             "IsoBR": {
                 "ISA":
                 TopHat(
                     norm_factor=63 / volume,
                     endpoints=(9.4e-8 * si.cm**2, 7.5e-7 * si.cm**2),
                 ),
                 "color":
                 "#FED2B0",
                 "J_het":
                 2.8e3 / si.cm**2 / si.s,
             },
             "IsoHE1": {
                 "ISA":
                 Lognormal(norm_factor=40 / volume,
                           m_mode=1.2 * si.cm**2,
                           s_geom=2.2),
                 "color":
                 "#FED2B0",
                 "J_het":
                 4.1e-3 / si.cm**2 / si.s,
             },
             "IsoHE2": {
                 "ISA":
                 Lognormal(norm_factor=40 / volume,
                           m_mode=2e-2 * si.cm**2,
                           s_geom=8.5),
                 "color":
                 "#FED2B0",
                 "J_het":
                 2e-2 / si.cm**2 / si.s,
             },
             "IsoDI1": {
                 "ISA":
                 Lognormal(norm_factor=45 / volume,
                           m_mode=5.1e-1 * si.cm**2,
                           s_geom=3.2),
                 "J_het":
                 1.8e-2 / si.cm**2 / si.s,
                 "color":
                 "#9ACFA4",
             },
             "IsoDI2": {
                 "ISA":
                 Lognormal(norm_factor=45 / volume,
                           m_mode=5.1e-2 * si.cm**2,
                           s_geom=3.2),
                 "J_het":
                 1 / si.cm**2 / si.s,
                 "color":
                 "#FED2B0",
             },
             "IsoDI3": {
                 "ISA":
                 Lognormal(norm_factor=45 / volume,
                           m_mode=5.1e-1 * si.cm**2,
                           s_geom=3.2),
                 "J_het":
                 1 / si.cm**2 / si.s,
                 "color":
                 "#95BDE1",
             },
         },
     )
Beispiel #14
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