Source code for elastica.modules.forcing
__doc__ = """
Forcing
-------
Provides the forcing interface to apply forces and torques to rod-like objects
(external point force, muscle torques, etc).
"""
import logging
import functools
from typing import Any, Type, List
from typing_extensions import Self
import numpy as np
from elastica.external_forces import NoForces
from elastica.typing import SystemType, SystemIdxType
from .protocol import ForcedSystemCollectionProtocol, ModuleProtocol
logger = logging.getLogger(__name__)
[docs]
class Forcing:
"""
The Forcing class is a module for applying boundary conditions that
consist of applied external forces. To apply forcing on rod-like objects,
the simulator class must be derived from the Forcing class.
Attributes
----------
_ext_forces_torques: list
List of forcing class defined for rod-like objects.
"""
def __init__(self: ForcedSystemCollectionProtocol) -> None:
self._ext_forces_torques: List[ModuleProtocol] = []
super().__init__()
self._feature_group_finalize.append(self._finalize_forcing)
[docs]
def add_forcing_to(
self: ForcedSystemCollectionProtocol, system: SystemType
) -> ModuleProtocol:
"""
This method applies external forces and torques on the relevant
user-defined system or rod-like object. You must input the system
or rod-like object that you want to apply external forces and torques on.
Parameters
----------
system: object
System is a rod-like object.
Returns
-------
"""
sys_idx = self.get_system_index(system)
# Create _Constraint object, cache it and return to user
_ext_force_torque = _ExtForceTorque(sys_idx)
self._ext_forces_torques.append(_ext_force_torque)
self._feature_group_synchronize.append_id(_ext_force_torque)
return _ext_force_torque
def _finalize_forcing(self: ForcedSystemCollectionProtocol) -> None:
# From stored _ExtForceTorque objects, and instantiate a Force
# inplace : https://stackoverflow.com/a/1208792
# dev : the first index stores the rod index to apply the boundary condition to.
for external_force_and_torque in self._ext_forces_torques:
sys_id = external_force_and_torque.id()
forcing_instance = external_force_and_torque.instantiate()
apply_forces = functools.partial(
forcing_instance.apply_forces, system=self[sys_id]
)
apply_torques = functools.partial(
forcing_instance.apply_torques, system=self[sys_id]
)
self._feature_group_synchronize.add_operators(
external_force_and_torque, [apply_forces, apply_torques]
)
self._ext_forces_torques = []
del self._ext_forces_torques
class _ExtForceTorque:
"""
Forcing module private class
Attributes
----------
_sys_idx: int
_forcing_cls: Type[NoForces]
*args: Any
Variable length argument list.
**kwargs: Any
Arbitrary keyword arguments.
"""
def __init__(self, sys_idx: SystemIdxType) -> None:
"""
Parameters
----------
sys_idx: int
"""
self._sys_idx = sys_idx
self._forcing_cls: Type[NoForces]
self._args: Any
self._kwargs: Any
def using(self, cls: Type[NoForces], *args: Any, **kwargs: Any) -> Self:
"""
This method sets which forcing class is used to apply forcing
to user defined rod-like objects.
Parameters
----------
cls: Type[Any]
User defined forcing class.
*args: Any
Variable length argument list.
**kwargs: Any
Arbitrary keyword arguments.
Returns
-------
"""
assert issubclass(
cls, NoForces
), "{} is not a valid forcing. Did you forget to derive from NoForces?".format(
cls
)
self._forcing_cls = cls
self._args = args
self._kwargs = kwargs
return self
def id(self) -> SystemIdxType:
return self._sys_idx
def instantiate(self) -> NoForces:
"""Constructs a constraint after checks"""
if not hasattr(self, "_forcing_cls"):
raise RuntimeError(
"No forcing provided to act on rod id {0}"
"but a force was registered. Did you forget to call"
"the `using` method".format(self.id())
)
try:
return self._forcing_cls(*self._args, **self._kwargs)
except (TypeError, IndexError):
raise TypeError(
r"Unable to construct forcing class.\n"
r"Did you provide all necessary force properties?"
)