Source code for elastica.modules.damping
__doc__ = """
Damping
-------
(added in version 0.3.0)
Provides the damper interface to apply damping
on the rods. (see `dissipation.py`).
"""
from typing import Any, Type, List
from typing_extensions import Self
import functools
import numpy as np
from elastica.dissipation import DamperBase
from elastica.typing import RodType, SystemType, SystemIdxType
from .protocol import DampenedSystemCollectionProtocol, ModuleProtocol
[docs]
class Damping:
"""
The Damping class is a module for applying damping
on rod-like objects, the simulator class must be derived from
Damping class.
Attributes
----------
_dampers: list
List of damper classes defined for rod-like objects.
"""
def __init__(self: DampenedSystemCollectionProtocol) -> None:
self._damping_list: List[ModuleProtocol] = []
super().__init__()
self._feature_group_finalize.append(self._finalize_dampers)
[docs]
def dampen(
self: DampenedSystemCollectionProtocol, system: RodType
) -> ModuleProtocol:
"""
This method applies damping on relevant user-defined
system or rod-like object. You must input the system or rod-like
object that you want to apply damping on.
Parameters
----------
system: object
System is a rod-like object.
Returns
-------
"""
sys_idx = self.get_system_index(system)
# Create _Damper object, cache it and return to user
_damper: ModuleProtocol = _Damper(sys_idx)
self._damping_list.append(_damper)
self._feature_group_damping.append_id(_damper)
return _damper
def _finalize_dampers(self: DampenedSystemCollectionProtocol) -> None:
# From stored _Damping objects, instantiate the dissipation/damping
# inplace : https://stackoverflow.com/a/1208792
# Sort from lowest id to highest id for potentially better memory access
# _dampers contains list of tuples. First element of tuple is rod number and
# following elements are the type of damping.
# Thus using lambda we iterate over the list of tuples and use rod number (x[0])
# to sort dampers.
self._damping_list.sort(key=lambda x: x.id())
for damping in self._damping_list:
sys_id = damping.id()
damping_instance = damping.instantiate(self[sys_id])
dampen_rate = functools.partial(
damping_instance.dampen_rates, system=self[sys_id]
)
self._feature_group_damping.add_operators(damping, [dampen_rate])
self._damping_list = []
del self._damping_list
class _Damper:
"""
Damper module private class
Attributes
----------
_sys_idx: int
_damper_cls: list
*args
Variable length argument list.
**kwargs
Arbitrary keyword arguments.
"""
def __init__(self, sys_idx: SystemIdxType) -> None:
"""
Parameters
----------
sys_idx: int
"""
self._sys_idx = sys_idx
self._damper_cls: Type[DamperBase]
self._args: Any
self._kwargs: Any
def using(self, cls: Type[DamperBase], *args: Any, **kwargs: Any) -> Self:
"""
This method is a module to set which damper class is used to
enforce damping from user defined rod-like objects.
Parameters
----------
cls : Type[DamperBase]
User defined damper class.
*args: Any
Variable length argument list.
**kwargs: Any
Arbitrary keyword arguments.
Returns
-------
"""
assert issubclass(
cls, DamperBase
), "{} is not a valid damper. Damper must be driven from DamperBase.".format(
cls
)
self._damper_cls = cls
self._args = args
self._kwargs = kwargs
return self
def id(self) -> SystemIdxType:
return self._sys_idx
def instantiate(self, rod: SystemType) -> DamperBase:
"""Constructs a Damper class object after checks"""
if not hasattr(self, "_damper_cls"):
raise RuntimeError(
"No damper provided to dampen rod id {0} at {1},"
"but damping was intended. Did you"
"forget to call the `using` method?".format(self.id(), rod)
)
try:
damper = self._damper_cls(*self._args, _system=rod, **self._kwargs)
return damper
except (TypeError, IndexError):
raise TypeError("Unable to construct damping class.\n")