Skip to content

Commit

Permalink
move backend tests to backends/. add test for seeding dynamic includi…
Browse files Browse the repository at this point in the history
…ng shuffle. update todos.
  • Loading branch information
claresinger committed Sep 12, 2024
1 parent 274a70e commit d19057f
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 123 deletions.
2 changes: 1 addition & 1 deletion PySDM/backends/impl_numba/methods/seeding_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def body( # pylint: disable=too-many-arguments
number_of_super_particles_to_inject: int,
):
number_of_super_particles_already_injected = 0
# TODO #1367 start enumerating from the end of valid particle set
# TODO #1387 start enumerating from the end of valid particle set
for i, mult in enumerate(multiplicity):
if (
number_of_super_particles_to_inject
Expand Down
2 changes: 1 addition & 1 deletion PySDM/dynamics/seeding.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def __call__(self):

if self.rnd is not None:
self.u01.urand(self.rnd)
# TODO make shuffle smarter
# TODO #1387 make shuffle smarter
# e.g. don't need to shuffle if only one type of seed particle
# or if the number of super particles to inject
# is equal to the number of possible seeds
Expand Down
142 changes: 142 additions & 0 deletions tests/unit_tests/backends/test_seeding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
""" Seeding backend tests of injection logic """

import numpy as np
import pytest

from PySDM import Builder
from PySDM.environments import Box
from PySDM.backends import CPU
from PySDM.physics import si


class TestSeeding:
max_number_to_inject = 4

@staticmethod
@pytest.mark.parametrize(
"n_sd, number_of_super_particles_to_inject",
(
(1, 1),
pytest.param(1, 2, marks=pytest.mark.xfail(strict=True)),
(max_number_to_inject, max_number_to_inject - 1),
(max_number_to_inject, max_number_to_inject),
pytest.param(
max_number_to_inject + 2,
max_number_to_inject + 1,
marks=pytest.mark.xfail(strict=True),
),
),
)
def test_number_of_super_particles_to_inject(
n_sd,
number_of_super_particles_to_inject,
dt=1,
dv=1,
):
"""unit test for injection logic on: number_of_super_particles_to_inject"""
# arrange
builder = Builder(n_sd, CPU(), Box(dt, dv))
particulator = builder.build(
attributes={
"multiplicity": np.full(n_sd, np.nan),
"water mass": np.zeros(n_sd),
},
)

seeded_particle_extensive_attributes = {
"water mass": [0.0001 * si.ng] * TestSeeding.max_number_to_inject,
}
seeded_particle_multiplicity = [1] * TestSeeding.max_number_to_inject

seeded_particle_index = particulator.Index.identity_index(
len(seeded_particle_multiplicity)
)
seeded_particle_multiplicity = particulator.IndexedStorage.from_ndarray(
seeded_particle_index,
np.asarray(seeded_particle_multiplicity),
)
seeded_particle_extensive_attributes = particulator.IndexedStorage.from_ndarray(
seeded_particle_index,
np.asarray(list(seeded_particle_extensive_attributes.values())),
)

# act
particulator.seeding(
seeded_particle_index=seeded_particle_index,
seeded_particle_multiplicity=seeded_particle_multiplicity,
seeded_particle_extensive_attributes=seeded_particle_extensive_attributes,
number_of_super_particles_to_inject=number_of_super_particles_to_inject,
)

# assert
assert (
number_of_super_particles_to_inject
== particulator.attributes.super_droplet_count
)

@staticmethod
@pytest.mark.parametrize(
"seeded_particle_index",
(
[0, 0, 0],
[0, 1, 2],
[2, 1, 0],
pytest.param([0], marks=pytest.mark.xfail(strict=True)),
),
)
def test_seeded_particle_index_multiplicity_extensive_attributes(
seeded_particle_index,
n_sd=3,
number_of_super_particles_to_inject=3,
dt=1,
dv=1,
):
"""unit test for injection logic on: seeded_particle_index, seeded_particle_multiplicity, seeded_particle_extensive_attributes"""

# arrange
builder = Builder(n_sd, CPU(), Box(dt, dv))
particulator = builder.build(
attributes={
"multiplicity": np.full(n_sd, np.nan),
"water mass": np.zeros(n_sd),
},
)

seeded_particle_extensive_attributes = {
"water mass": [0.0001, 0.0003, 0.0002],
}
seeded_particle_multiplicity = [1, 2, 3]

seeded_particle_index_impl = particulator.Index.from_ndarray(
np.asarray(seeded_particle_index)
)
seeded_particle_multiplicity_impl = particulator.IndexedStorage.from_ndarray(
seeded_particle_index_impl,
np.asarray(seeded_particle_multiplicity),
)
seeded_particle_extensive_attributes_impl = (
particulator.IndexedStorage.from_ndarray(
seeded_particle_index_impl,
np.asarray(list(seeded_particle_extensive_attributes.values())),
)
)

# act
particulator.seeding(
seeded_particle_index=seeded_particle_index_impl,
seeded_particle_multiplicity=seeded_particle_multiplicity_impl,
seeded_particle_extensive_attributes=seeded_particle_extensive_attributes_impl,
number_of_super_particles_to_inject=number_of_super_particles_to_inject,
)

# assert
np.testing.assert_array_equal(
particulator.attributes["multiplicity"].to_ndarray(),
np.asarray(seeded_particle_multiplicity)[seeded_particle_index],
)
np.testing.assert_array_equal(
particulator.attributes["water mass"].to_ndarray(),
np.asarray(seeded_particle_extensive_attributes["water mass"])[
seeded_particle_index
],
)
183 changes: 62 additions & 121 deletions tests/unit_tests/dynamics/test_seeding.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from PySDM import Builder
from PySDM.products import SuperDropletCountPerGridbox, Time
from PySDM.backends import CPU
from PySDM.backends.impl_common.index import make_Index
from PySDM.backends.impl_common.indexed_storage import make_IndexedStorage
from PySDM.dynamics import Seeding
from PySDM.environments import Box
from PySDM.physics import si
Expand Down Expand Up @@ -79,7 +81,7 @@ def simulation(*, dynamics):
dynamics=(
Seeding(
super_droplet_injection_rate=lambda time: 0,
**common_seeding_ctor_args
**common_seeding_ctor_args,
),
)
),
Expand All @@ -89,7 +91,7 @@ def simulation(*, dynamics):
super_droplet_injection_rate=lambda time: (
1 if 10 * si.min <= time < 15 * si.min else 0
),
**common_seeding_ctor_args
**common_seeding_ctor_args,
),
)
),
Expand Down Expand Up @@ -173,138 +175,77 @@ def test_attribute_set_match():
# assert
assert "do not match those used in particulator" in str(excinfo)

max_number_to_inject = 4

@staticmethod
@pytest.mark.parametrize(
"n_sd, number_of_super_particles_to_inject",
"super_droplet_injection_rate, reservoir_length",
(
(1, 1),
pytest.param(1, 2, marks=pytest.mark.xfail(strict=True)),
(max_number_to_inject, max_number_to_inject - 1),
(max_number_to_inject, max_number_to_inject),
pytest.param(
max_number_to_inject + 2,
max_number_to_inject + 1,
marks=pytest.mark.xfail(strict=True),
),
(0, 0), # shuffle not called
(0, 2),
(1, 10),
(2, 10),
),
)
def test_seeding_number_of_super_particles_to_inject(
n_sd,
number_of_super_particles_to_inject,
dt=1,
dv=1,
def test_seeded_particle_shuffle(
super_droplet_injection_rate, reservoir_length, backend=CPU()
):
"""unit test for injection logic on: number_of_super_particles_to_inject
FUTURE TESTS:
seeded_particle_multiplicity,
seeded_particle_extensive_attributes,
"""
# arrange
builder = Builder(n_sd, CPU(), Box(dt, dv))
particulator = builder.build(
attributes={
"multiplicity": np.full(n_sd, np.nan),
"water mass": np.zeros(n_sd),
},
)

seeded_particle_extensive_attributes = {
"water mass": [0.0001 * si.ng] * TestSeeding.max_number_to_inject,
}
seeded_particle_multiplicity = [1] * TestSeeding.max_number_to_inject

seeded_particle_index = particulator.Index.identity_index(
len(seeded_particle_multiplicity)
)
seeded_particle_multiplicity = particulator.IndexedStorage.from_ndarray(
seeded_particle_index,
np.asarray(seeded_particle_multiplicity),
)
seeded_particle_extensive_attributes = particulator.IndexedStorage.from_ndarray(
seeded_particle_index,
np.asarray(list(seeded_particle_extensive_attributes.values())),
)
extensive_attributes = ["a"]
seeding_attributes = ["a"]

class MockParticulator:
def __init__(self):
self.seeding_call_count = 0
self.indices = []

def seeding(
self,
*,
seeded_particle_index,
number_of_super_particles_to_inject,
seeded_particle_multiplicity,
seeded_particle_extensive_attributes,
):
self.seeding_call_count += 1
self.indices.append(seeded_particle_index.to_ndarray())

Index = make_Index(backend)
IndexedStorage = make_IndexedStorage(backend)
Random = None if reservoir_length == 0 else backend.Random
formulae = None if reservoir_length == 0 else backend.formulae
Storage = None if reservoir_length == 0 else backend.Storage
n_steps = 0
dt = np.nan
attributes = namedtuple(
typename="MockAttributes",
field_names=("get_extensive_attribute_keys",),
)(get_extensive_attribute_keys=lambda: extensive_attributes)

# act
particulator.seeding(
seeded_particle_index=seeded_particle_index,
seeded_particle_multiplicity=seeded_particle_multiplicity,
seeded_particle_extensive_attributes=seeded_particle_extensive_attributes,
number_of_super_particles_to_inject=number_of_super_particles_to_inject,
)

# assert
assert (
number_of_super_particles_to_inject
== particulator.attributes.super_droplet_count
builder = namedtuple(typename="MockBuilder", field_names=("particulator",))(
particulator=MockParticulator()
)

@staticmethod
@pytest.mark.parametrize(
"seeded_particle_index",
(
[0, 0, 0],
[0, 1, 2],
[2, 1, 0],
pytest.param([0], marks=pytest.mark.xfail(strict=True)),
),
)
def test_seeded_particle_index(
seeded_particle_index,
n_sd=3,
number_of_super_particles_to_inject=3,
dt=1,
dv=1,
):
"""unit test for injection logic on: seeded_particle_index"""

# arrange
builder = Builder(n_sd, CPU(), Box(dt, dv))
particulator = builder.build(
attributes={
"multiplicity": np.full(n_sd, np.nan),
"water mass": np.zeros(n_sd),
dynamic = Seeding(
super_droplet_injection_rate=lambda t: super_droplet_injection_rate,
seeded_particle_extensive_attributes={
k: [np.nan] * reservoir_length for k in seeding_attributes
},
seeded_particle_multiplicity=[1] * reservoir_length,
)

seeded_particle_extensive_attributes = {
"water mass": [0.0001, 0.0003, 0.0002],
}
seeded_particle_multiplicity = [1, 2, 3]

seeded_particle_index_impl = particulator.Index.from_ndarray(
np.asarray(seeded_particle_index)
)
seeded_particle_multiplicity_impl = particulator.IndexedStorage.from_ndarray(
seeded_particle_index_impl,
np.asarray(seeded_particle_multiplicity),
)
seeded_particle_extensive_attributes_impl = (
particulator.IndexedStorage.from_ndarray(
seeded_particle_index_impl,
np.asarray(list(seeded_particle_extensive_attributes.values())),
)
)
dynamic.register(builder)

# act
particulator.seeding(
seeded_particle_index=seeded_particle_index_impl,
seeded_particle_multiplicity=seeded_particle_multiplicity_impl,
seeded_particle_extensive_attributes=seeded_particle_extensive_attributes_impl,
number_of_super_particles_to_inject=number_of_super_particles_to_inject,
)
dynamic()
dynamic.particulator.n_steps += 1
dynamic()

# assert
np.testing.assert_array_equal(
particulator.attributes["multiplicity"].to_ndarray(),
np.asarray(seeded_particle_multiplicity)[seeded_particle_index],
)
np.testing.assert_array_equal(
particulator.attributes["water mass"].to_ndarray(),
np.asarray(seeded_particle_extensive_attributes["water mass"])[
seeded_particle_index
],
)
assert dynamic.particulator.seeding_call_count == (
2 if super_droplet_injection_rate > 0 else 0
)
if super_droplet_injection_rate > 0:
assert (
dynamic.particulator.indices[0] != dynamic.particulator.indices[1]
).any()
assert sorted(dynamic.particulator.indices[0]) == sorted(
dynamic.particulator.indices[1]
)

0 comments on commit d19057f

Please sign in to comment.