Skip to content

Commit

Permalink
introducing @register_dynamic decorator to solve dynamic-reuse issue
Browse files Browse the repository at this point in the history
  • Loading branch information
slayoo committed Aug 2, 2024
1 parent 08d24f0 commit 96031dc
Show file tree
Hide file tree
Showing 13 changed files with 88 additions and 3 deletions.
4 changes: 2 additions & 2 deletions PySDM/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ def build(
self._resolve_attribute(attr_name)
self.req_attr_names = None

for dynamic in self.particulator.dynamics.values():
dynamic.register(self)
for key, dynamic in self.particulator.dynamics.items():
self.particulator.dynamics[key] = dynamic.instantiate(builder=self)

single_buffer_for_all_products = np.empty(self.particulator.mesh.grid)
for product in products:
Expand Down
3 changes: 3 additions & 0 deletions PySDM/dynamics/ambient_thermodynamics.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
environment-sync triggering class
"""

from PySDM.dynamics.impl import register_dynamic


@register_dynamic()
class AmbientThermodynamics:
def __init__(self):
self.particulator = None
Expand Down
2 changes: 2 additions & 0 deletions PySDM/dynamics/aqueous_chemistry.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
M,
SpecificGravities,
)
from PySDM.dynamics.impl import register_dynamic

DEFAULTS = namedtuple("_", ("pH_min", "pH_max", "pH_rtol", "ionic_strength_threshold"))(
pH_min=-1.0, pH_max=14.0, pH_rtol=1e-6, ionic_strength_threshold=0.02 * M
)


@register_dynamic()
class AqueousChemistry: # pylint: disable=too-many-instance-attributes
def __init__(
self,
Expand Down
4 changes: 4 additions & 0 deletions PySDM/dynamics/collisions/collision.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
RandomGeneratorOptimizerNoPair,
)
from PySDM.physics import si
from PySDM.dynamics.impl import register_dynamic

# pylint: disable=too-many-lines

Expand All @@ -36,6 +37,7 @@
)


@register_dynamic()
class Collision: # pylint: disable=too-many-instance-attributes
def __init__(
self,
Expand Down Expand Up @@ -288,6 +290,7 @@ def compute_gamma(self, prob, rand, is_first_in_pair, out):
)


@register_dynamic()
class Coalescence(Collision):
def __init__(
self,
Expand Down Expand Up @@ -316,6 +319,7 @@ def __init__(
)


@register_dynamic()
class Breakup(Collision):
def __init__(
self,
Expand Down
4 changes: 3 additions & 1 deletion PySDM/dynamics/condensation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

import numpy as np

from ..physics import si
from PySDM.physics import si
from PySDM.dynamics.impl import register_dynamic

DEFAULTS = namedtuple("_", ("rtol_x", "rtol_thd", "cond_range", "schedule"))(
rtol_x=1e-6,
Expand All @@ -17,6 +18,7 @@
)


@register_dynamic()
class Condensation: # pylint: disable=too-many-instance-attributes
def __init__(
self,
Expand Down
3 changes: 3 additions & 0 deletions PySDM/dynamics/displacement.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@

import numpy as np

from PySDM.dynamics.impl import register_dynamic

DEFAULTS = namedtuple("_", ("rtol", "adaptive"))(rtol=1e-2, adaptive=True)


@register_dynamic()
class Displacement: # pylint: disable=too-many-instance-attributes
def __init__(
self,
Expand Down
3 changes: 3 additions & 0 deletions PySDM/dynamics/eulerian_advection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
wrapper class for triggering integration in the Eulerian advection solver
"""

from PySDM.dynamics.impl import register_dynamic


@register_dynamic()
class EulerianAdvection:
def __init__(self, solvers):
self.solvers = solvers
Expand Down
2 changes: 2 additions & 0 deletions PySDM/dynamics/freezing.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
TimeDependentAttributes,
)
from PySDM.physics.heterogeneous_ice_nucleation_rate import Null
from PySDM.dynamics.impl import register_dynamic


@register_dynamic()
class Freezing:
def __init__(self, *, singular=True, record_freezing_temperature=False, thaw=False):
assert not (record_freezing_temperature and singular)
Expand Down
2 changes: 2 additions & 0 deletions PySDM/dynamics/impl/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
""" stuff not intended to be imported from user code """

from .register_dynamic import register_dynamic
21 changes: 21 additions & 0 deletions PySDM/dynamics/impl/register_dynamic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
""" decorator for dynamics classes
ensuring that their instances can be re-used with multiple builders """

from copy import deepcopy


def _instantiate(self, *, builder):
copy = deepcopy(self)
copy.register(builder=builder)
return copy


def register_dynamic():
def decorator(cls):
if hasattr(cls, "instantiate"):
assert cls.instantiate is _instantiate
else:
setattr(cls, "instantiate", _instantiate)
return cls

return decorator
2 changes: 2 additions & 0 deletions PySDM/dynamics/isotopic_fractionation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
"""

from PySDM.dynamics.condensation import Condensation
from PySDM.dynamics.impl import register_dynamic

LIGHT_ISOTOPES = ("1H", "16O")
HEAVY_ISOTOPES = ("2H", "3H", "17O", "18O")


@register_dynamic()
class IsotopicFractionation:
def __init__(self, isotopes: tuple = HEAVY_ISOTOPES):
self.isotopes = isotopes
Expand Down
2 changes: 2 additions & 0 deletions PySDM/dynamics/relaxed_velocity.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

from PySDM.attributes.impl.attribute import Attribute
from PySDM.particulator import Particulator
from PySDM.dynamics.impl import register_dynamic


@register_dynamic()
class RelaxedVelocity: # pylint: disable=too-many-instance-attributes
"""
A dynamic which updates the fall momentum according to a relaxation timescale
Expand Down
39 changes: 39 additions & 0 deletions tests/unit_tests/dynamics/test_impl_register_dynamic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
""" checks if @register_product makes dynamics instances reusable """

import numpy as np

from PySDM import Builder
from PySDM.backends import CPU
from PySDM.environments import Box
from PySDM.dynamics.impl import register_dynamic


def test_impl_register_dynamic():
# arrange
@register_dynamic()
class Dynamic:
def __init__(self):
self.particulator = None

def register(self, *, builder: Builder):
self.particulator = builder.particulator

dynamic = Dynamic()
kwargs = {"n_sd": 0, "backend": CPU(), "environment": Box(dt=0, dv=0)}
builders = [Builder(**kwargs), Builder(**kwargs)]

# act
for builder in builders:
builder.add_dynamic(dynamic)
builder.build(
attributes={"multiplicity": np.empty(0), "water mass": np.empty(0)}
)

# assert
assert dynamic.particulator is None
assert builders[0].particulator is not builders[1].particulator
for builder in builders:
assert (
builder.particulator.dynamics["Dynamic"].particulator
is builder.particulator
)

0 comments on commit 96031dc

Please sign in to comment.