I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -0,0 +1,348 @@
"""Tests for the ``sympy.physics.biomechanics.activation.py`` module."""
import pytest
from sympy import Symbol
from sympy.core.numbers import Float, Integer, Rational
from sympy.functions.elementary.hyperbolic import tanh
from sympy.matrices import Matrix
from sympy.matrices.dense import zeros
from sympy.physics.mechanics import dynamicsymbols
from sympy.physics.biomechanics import (
ActivationBase,
FirstOrderActivationDeGroote2016,
ZerothOrderActivation,
)
from sympy.physics.biomechanics._mixin import _NamedMixin
from sympy.simplify.simplify import simplify
class TestZerothOrderActivation:
@staticmethod
def test_class():
assert issubclass(ZerothOrderActivation, ActivationBase)
assert issubclass(ZerothOrderActivation, _NamedMixin)
assert ZerothOrderActivation.__name__ == 'ZerothOrderActivation'
@pytest.fixture(autouse=True)
def _zeroth_order_activation_fixture(self):
self.name = 'name'
self.e = dynamicsymbols('e_name')
self.instance = ZerothOrderActivation(self.name)
def test_instance(self):
instance = ZerothOrderActivation(self.name)
assert isinstance(instance, ZerothOrderActivation)
def test_with_defaults(self):
instance = ZerothOrderActivation.with_defaults(self.name)
assert isinstance(instance, ZerothOrderActivation)
assert instance == ZerothOrderActivation(self.name)
def test_name(self):
assert hasattr(self.instance, 'name')
assert self.instance.name == self.name
def test_order(self):
assert hasattr(self.instance, 'order')
assert self.instance.order == 0
def test_excitation_attribute(self):
assert hasattr(self.instance, 'e')
assert hasattr(self.instance, 'excitation')
e_expected = dynamicsymbols('e_name')
assert self.instance.e == e_expected
assert self.instance.excitation == e_expected
assert self.instance.e is self.instance.excitation
def test_activation_attribute(self):
assert hasattr(self.instance, 'a')
assert hasattr(self.instance, 'activation')
a_expected = dynamicsymbols('e_name')
assert self.instance.a == a_expected
assert self.instance.activation == a_expected
assert self.instance.a is self.instance.activation is self.instance.e
def test_state_vars_attribute(self):
assert hasattr(self.instance, 'x')
assert hasattr(self.instance, 'state_vars')
assert self.instance.x == self.instance.state_vars
x_expected = zeros(0, 1)
assert self.instance.x == x_expected
assert self.instance.state_vars == x_expected
assert isinstance(self.instance.x, Matrix)
assert isinstance(self.instance.state_vars, Matrix)
assert self.instance.x.shape == (0, 1)
assert self.instance.state_vars.shape == (0, 1)
def test_input_vars_attribute(self):
assert hasattr(self.instance, 'r')
assert hasattr(self.instance, 'input_vars')
assert self.instance.r == self.instance.input_vars
r_expected = Matrix([self.e])
assert self.instance.r == r_expected
assert self.instance.input_vars == r_expected
assert isinstance(self.instance.r, Matrix)
assert isinstance(self.instance.input_vars, Matrix)
assert self.instance.r.shape == (1, 1)
assert self.instance.input_vars.shape == (1, 1)
def test_constants_attribute(self):
assert hasattr(self.instance, 'p')
assert hasattr(self.instance, 'constants')
assert self.instance.p == self.instance.constants
p_expected = zeros(0, 1)
assert self.instance.p == p_expected
assert self.instance.constants == p_expected
assert isinstance(self.instance.p, Matrix)
assert isinstance(self.instance.constants, Matrix)
assert self.instance.p.shape == (0, 1)
assert self.instance.constants.shape == (0, 1)
def test_M_attribute(self):
assert hasattr(self.instance, 'M')
M_expected = Matrix([])
assert self.instance.M == M_expected
assert isinstance(self.instance.M, Matrix)
assert self.instance.M.shape == (0, 0)
def test_F(self):
assert hasattr(self.instance, 'F')
F_expected = zeros(0, 1)
assert self.instance.F == F_expected
assert isinstance(self.instance.F, Matrix)
assert self.instance.F.shape == (0, 1)
def test_rhs(self):
assert hasattr(self.instance, 'rhs')
rhs_expected = zeros(0, 1)
rhs = self.instance.rhs()
assert rhs == rhs_expected
assert isinstance(rhs, Matrix)
assert rhs.shape == (0, 1)
def test_repr(self):
expected = 'ZerothOrderActivation(\'name\')'
assert repr(self.instance) == expected
class TestFirstOrderActivationDeGroote2016:
@staticmethod
def test_class():
assert issubclass(FirstOrderActivationDeGroote2016, ActivationBase)
assert issubclass(FirstOrderActivationDeGroote2016, _NamedMixin)
assert FirstOrderActivationDeGroote2016.__name__ == 'FirstOrderActivationDeGroote2016'
@pytest.fixture(autouse=True)
def _first_order_activation_de_groote_2016_fixture(self):
self.name = 'name'
self.e = dynamicsymbols('e_name')
self.a = dynamicsymbols('a_name')
self.tau_a = Symbol('tau_a')
self.tau_d = Symbol('tau_d')
self.b = Symbol('b')
self.instance = FirstOrderActivationDeGroote2016(
self.name,
self.tau_a,
self.tau_d,
self.b,
)
def test_instance(self):
instance = FirstOrderActivationDeGroote2016(self.name)
assert isinstance(instance, FirstOrderActivationDeGroote2016)
def test_with_defaults(self):
instance = FirstOrderActivationDeGroote2016.with_defaults(self.name)
assert isinstance(instance, FirstOrderActivationDeGroote2016)
assert instance.tau_a == Float('0.015')
assert instance.activation_time_constant == Float('0.015')
assert instance.tau_d == Float('0.060')
assert instance.deactivation_time_constant == Float('0.060')
assert instance.b == Float('10.0')
assert instance.smoothing_rate == Float('10.0')
def test_name(self):
assert hasattr(self.instance, 'name')
assert self.instance.name == self.name
def test_order(self):
assert hasattr(self.instance, 'order')
assert self.instance.order == 1
def test_excitation(self):
assert hasattr(self.instance, 'e')
assert hasattr(self.instance, 'excitation')
e_expected = dynamicsymbols('e_name')
assert self.instance.e == e_expected
assert self.instance.excitation == e_expected
assert self.instance.e is self.instance.excitation
def test_excitation_is_immutable(self):
with pytest.raises(AttributeError):
self.instance.e = None
with pytest.raises(AttributeError):
self.instance.excitation = None
def test_activation(self):
assert hasattr(self.instance, 'a')
assert hasattr(self.instance, 'activation')
a_expected = dynamicsymbols('a_name')
assert self.instance.a == a_expected
assert self.instance.activation == a_expected
def test_activation_is_immutable(self):
with pytest.raises(AttributeError):
self.instance.a = None
with pytest.raises(AttributeError):
self.instance.activation = None
@pytest.mark.parametrize(
'tau_a, expected',
[
(None, Symbol('tau_a_name')),
(Symbol('tau_a'), Symbol('tau_a')),
(Float('0.015'), Float('0.015')),
]
)
def test_activation_time_constant(self, tau_a, expected):
instance = FirstOrderActivationDeGroote2016(
'name', activation_time_constant=tau_a,
)
assert instance.tau_a == expected
assert instance.activation_time_constant == expected
assert instance.tau_a is instance.activation_time_constant
def test_activation_time_constant_is_immutable(self):
with pytest.raises(AttributeError):
self.instance.tau_a = None
with pytest.raises(AttributeError):
self.instance.activation_time_constant = None
@pytest.mark.parametrize(
'tau_d, expected',
[
(None, Symbol('tau_d_name')),
(Symbol('tau_d'), Symbol('tau_d')),
(Float('0.060'), Float('0.060')),
]
)
def test_deactivation_time_constant(self, tau_d, expected):
instance = FirstOrderActivationDeGroote2016(
'name', deactivation_time_constant=tau_d,
)
assert instance.tau_d == expected
assert instance.deactivation_time_constant == expected
assert instance.tau_d is instance.deactivation_time_constant
def test_deactivation_time_constant_is_immutable(self):
with pytest.raises(AttributeError):
self.instance.tau_d = None
with pytest.raises(AttributeError):
self.instance.deactivation_time_constant = None
@pytest.mark.parametrize(
'b, expected',
[
(None, Symbol('b_name')),
(Symbol('b'), Symbol('b')),
(Integer('10'), Integer('10')),
]
)
def test_smoothing_rate(self, b, expected):
instance = FirstOrderActivationDeGroote2016(
'name', smoothing_rate=b,
)
assert instance.b == expected
assert instance.smoothing_rate == expected
assert instance.b is instance.smoothing_rate
def test_smoothing_rate_is_immutable(self):
with pytest.raises(AttributeError):
self.instance.b = None
with pytest.raises(AttributeError):
self.instance.smoothing_rate = None
def test_state_vars(self):
assert hasattr(self.instance, 'x')
assert hasattr(self.instance, 'state_vars')
assert self.instance.x == self.instance.state_vars
x_expected = Matrix([self.a])
assert self.instance.x == x_expected
assert self.instance.state_vars == x_expected
assert isinstance(self.instance.x, Matrix)
assert isinstance(self.instance.state_vars, Matrix)
assert self.instance.x.shape == (1, 1)
assert self.instance.state_vars.shape == (1, 1)
def test_input_vars(self):
assert hasattr(self.instance, 'r')
assert hasattr(self.instance, 'input_vars')
assert self.instance.r == self.instance.input_vars
r_expected = Matrix([self.e])
assert self.instance.r == r_expected
assert self.instance.input_vars == r_expected
assert isinstance(self.instance.r, Matrix)
assert isinstance(self.instance.input_vars, Matrix)
assert self.instance.r.shape == (1, 1)
assert self.instance.input_vars.shape == (1, 1)
def test_constants(self):
assert hasattr(self.instance, 'p')
assert hasattr(self.instance, 'constants')
assert self.instance.p == self.instance.constants
p_expected = Matrix([self.tau_a, self.tau_d, self.b])
assert self.instance.p == p_expected
assert self.instance.constants == p_expected
assert isinstance(self.instance.p, Matrix)
assert isinstance(self.instance.constants, Matrix)
assert self.instance.p.shape == (3, 1)
assert self.instance.constants.shape == (3, 1)
def test_M(self):
assert hasattr(self.instance, 'M')
M_expected = Matrix([1])
assert self.instance.M == M_expected
assert isinstance(self.instance.M, Matrix)
assert self.instance.M.shape == (1, 1)
def test_F(self):
assert hasattr(self.instance, 'F')
da_expr = (
((1/(self.tau_a*(Rational(1, 2) + Rational(3, 2)*self.a)))
*(Rational(1, 2) + Rational(1, 2)*tanh(self.b*(self.e - self.a)))
+ ((Rational(1, 2) + Rational(3, 2)*self.a)/self.tau_d)
*(Rational(1, 2) - Rational(1, 2)*tanh(self.b*(self.e - self.a))))
*(self.e - self.a)
)
F_expected = Matrix([da_expr])
assert self.instance.F == F_expected
assert isinstance(self.instance.F, Matrix)
assert self.instance.F.shape == (1, 1)
def test_rhs(self):
assert hasattr(self.instance, 'rhs')
da_expr = (
((1/(self.tau_a*(Rational(1, 2) + Rational(3, 2)*self.a)))
*(Rational(1, 2) + Rational(1, 2)*tanh(self.b*(self.e - self.a)))
+ ((Rational(1, 2) + Rational(3, 2)*self.a)/self.tau_d)
*(Rational(1, 2) - Rational(1, 2)*tanh(self.b*(self.e - self.a))))
*(self.e - self.a)
)
rhs_expected = Matrix([da_expr])
rhs = self.instance.rhs()
assert rhs == rhs_expected
assert isinstance(rhs, Matrix)
assert rhs.shape == (1, 1)
assert simplify(self.instance.M.solve(self.instance.F) - rhs) == zeros(1)
def test_repr(self):
expected = (
'FirstOrderActivationDeGroote2016(\'name\', '
'activation_time_constant=tau_a, '
'deactivation_time_constant=tau_d, '
'smoothing_rate=b)'
)
assert repr(self.instance) == expected

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
"""Tests for the ``sympy.physics.biomechanics._mixin.py`` module."""
import pytest
from sympy.physics.biomechanics._mixin import _NamedMixin
class TestNamedMixin:
@staticmethod
def test_subclass():
class Subclass(_NamedMixin):
def __init__(self, name):
self.name = name
instance = Subclass('name')
assert instance.name == 'name'
@pytest.fixture(autouse=True)
def _named_mixin_fixture(self):
class Subclass(_NamedMixin):
def __init__(self, name):
self.name = name
self.Subclass = Subclass
@pytest.mark.parametrize('name', ['a', 'name', 'long_name'])
def test_valid_name_argument(self, name):
instance = self.Subclass(name)
assert instance.name == name
@pytest.mark.parametrize('invalid_name', [0, 0.0, None, False])
def test_invalid_name_argument_not_str(self, invalid_name):
with pytest.raises(TypeError):
_ = self.Subclass(invalid_name)
def test_invalid_name_argument_zero_length_str(self):
with pytest.raises(ValueError):
_ = self.Subclass('')
def test_name_attribute_is_immutable(self):
instance = self.Subclass('name')
with pytest.raises(AttributeError):
instance.name = 'new_name'

View File

@ -0,0 +1,837 @@
"""Tests for the ``sympy.physics.biomechanics.musculotendon.py`` module."""
import abc
import pytest
from sympy.core.expr import UnevaluatedExpr
from sympy.core.numbers import Float, Integer, Rational
from sympy.core.symbol import Symbol
from sympy.functions.elementary.exponential import exp
from sympy.functions.elementary.hyperbolic import tanh
from sympy.functions.elementary.miscellaneous import sqrt
from sympy.functions.elementary.trigonometric import sin
from sympy.matrices.dense import MutableDenseMatrix as Matrix, eye, zeros
from sympy.physics.biomechanics.activation import (
FirstOrderActivationDeGroote2016
)
from sympy.physics.biomechanics.curve import (
CharacteristicCurveCollection,
FiberForceLengthActiveDeGroote2016,
FiberForceLengthPassiveDeGroote2016,
FiberForceLengthPassiveInverseDeGroote2016,
FiberForceVelocityDeGroote2016,
FiberForceVelocityInverseDeGroote2016,
TendonForceLengthDeGroote2016,
TendonForceLengthInverseDeGroote2016,
)
from sympy.physics.biomechanics.musculotendon import (
MusculotendonBase,
MusculotendonDeGroote2016,
MusculotendonFormulation,
)
from sympy.physics.biomechanics._mixin import _NamedMixin
from sympy.physics.mechanics.actuator import ForceActuator
from sympy.physics.mechanics.pathway import LinearPathway
from sympy.physics.vector.frame import ReferenceFrame
from sympy.physics.vector.functions import dynamicsymbols
from sympy.physics.vector.point import Point
from sympy.simplify.simplify import simplify
class TestMusculotendonFormulation:
@staticmethod
def test_rigid_tendon_member():
assert MusculotendonFormulation(0) == 0
assert MusculotendonFormulation.RIGID_TENDON == 0
@staticmethod
def test_fiber_length_explicit_member():
assert MusculotendonFormulation(1) == 1
assert MusculotendonFormulation.FIBER_LENGTH_EXPLICIT == 1
@staticmethod
def test_tendon_force_explicit_member():
assert MusculotendonFormulation(2) == 2
assert MusculotendonFormulation.TENDON_FORCE_EXPLICIT == 2
@staticmethod
def test_fiber_length_implicit_member():
assert MusculotendonFormulation(3) == 3
assert MusculotendonFormulation.FIBER_LENGTH_IMPLICIT == 3
@staticmethod
def test_tendon_force_implicit_member():
assert MusculotendonFormulation(4) == 4
assert MusculotendonFormulation.TENDON_FORCE_IMPLICIT == 4
class TestMusculotendonBase:
@staticmethod
def test_is_abstract_base_class():
assert issubclass(MusculotendonBase, abc.ABC)
@staticmethod
def test_class():
assert issubclass(MusculotendonBase, ForceActuator)
assert issubclass(MusculotendonBase, _NamedMixin)
assert MusculotendonBase.__name__ == 'MusculotendonBase'
@staticmethod
def test_cannot_instantiate_directly():
with pytest.raises(TypeError):
_ = MusculotendonBase()
@pytest.mark.parametrize('musculotendon_concrete', [MusculotendonDeGroote2016])
class TestMusculotendonRigidTendon:
@pytest.fixture(autouse=True)
def _musculotendon_rigid_tendon_fixture(self, musculotendon_concrete):
self.name = 'name'
self.N = ReferenceFrame('N')
self.q = dynamicsymbols('q')
self.origin = Point('pO')
self.insertion = Point('pI')
self.insertion.set_pos(self.origin, self.q*self.N.x)
self.pathway = LinearPathway(self.origin, self.insertion)
self.activation = FirstOrderActivationDeGroote2016(self.name)
self.e = self.activation.excitation
self.a = self.activation.activation
self.tau_a = self.activation.activation_time_constant
self.tau_d = self.activation.deactivation_time_constant
self.b = self.activation.smoothing_rate
self.formulation = MusculotendonFormulation.RIGID_TENDON
self.l_T_slack = Symbol('l_T_slack')
self.F_M_max = Symbol('F_M_max')
self.l_M_opt = Symbol('l_M_opt')
self.v_M_max = Symbol('v_M_max')
self.alpha_opt = Symbol('alpha_opt')
self.beta = Symbol('beta')
self.instance = musculotendon_concrete(
self.name,
self.pathway,
self.activation,
musculotendon_dynamics=self.formulation,
tendon_slack_length=self.l_T_slack,
peak_isometric_force=self.F_M_max,
optimal_fiber_length=self.l_M_opt,
maximal_fiber_velocity=self.v_M_max,
optimal_pennation_angle=self.alpha_opt,
fiber_damping_coefficient=self.beta,
)
self.da_expr = (
(1/(self.tau_a*(Rational(1, 2) + Rational(3, 2)*self.a)))
*(Rational(1, 2) + Rational(1, 2)*tanh(self.b*(self.e - self.a)))
+ ((Rational(1, 2) + Rational(3, 2)*self.a)/self.tau_d)
*(Rational(1, 2) - Rational(1, 2)*tanh(self.b*(self.e - self.a)))
)*(self.e - self.a)
def test_state_vars(self):
assert hasattr(self.instance, 'x')
assert hasattr(self.instance, 'state_vars')
assert self.instance.x == self.instance.state_vars
x_expected = Matrix([self.a])
assert self.instance.x == x_expected
assert self.instance.state_vars == x_expected
assert isinstance(self.instance.x, Matrix)
assert isinstance(self.instance.state_vars, Matrix)
assert self.instance.x.shape == (1, 1)
assert self.instance.state_vars.shape == (1, 1)
def test_input_vars(self):
assert hasattr(self.instance, 'r')
assert hasattr(self.instance, 'input_vars')
assert self.instance.r == self.instance.input_vars
r_expected = Matrix([self.e])
assert self.instance.r == r_expected
assert self.instance.input_vars == r_expected
assert isinstance(self.instance.r, Matrix)
assert isinstance(self.instance.input_vars, Matrix)
assert self.instance.r.shape == (1, 1)
assert self.instance.input_vars.shape == (1, 1)
def test_constants(self):
assert hasattr(self.instance, 'p')
assert hasattr(self.instance, 'constants')
assert self.instance.p == self.instance.constants
p_expected = Matrix(
[
self.l_T_slack,
self.F_M_max,
self.l_M_opt,
self.v_M_max,
self.alpha_opt,
self.beta,
self.tau_a,
self.tau_d,
self.b,
Symbol('c_0_fl_T_name'),
Symbol('c_1_fl_T_name'),
Symbol('c_2_fl_T_name'),
Symbol('c_3_fl_T_name'),
Symbol('c_0_fl_M_pas_name'),
Symbol('c_1_fl_M_pas_name'),
Symbol('c_0_fl_M_act_name'),
Symbol('c_1_fl_M_act_name'),
Symbol('c_2_fl_M_act_name'),
Symbol('c_3_fl_M_act_name'),
Symbol('c_4_fl_M_act_name'),
Symbol('c_5_fl_M_act_name'),
Symbol('c_6_fl_M_act_name'),
Symbol('c_7_fl_M_act_name'),
Symbol('c_8_fl_M_act_name'),
Symbol('c_9_fl_M_act_name'),
Symbol('c_10_fl_M_act_name'),
Symbol('c_11_fl_M_act_name'),
Symbol('c_0_fv_M_name'),
Symbol('c_1_fv_M_name'),
Symbol('c_2_fv_M_name'),
Symbol('c_3_fv_M_name'),
]
)
assert self.instance.p == p_expected
assert self.instance.constants == p_expected
assert isinstance(self.instance.p, Matrix)
assert isinstance(self.instance.constants, Matrix)
assert self.instance.p.shape == (31, 1)
assert self.instance.constants.shape == (31, 1)
def test_M(self):
assert hasattr(self.instance, 'M')
M_expected = Matrix([1])
assert self.instance.M == M_expected
assert isinstance(self.instance.M, Matrix)
assert self.instance.M.shape == (1, 1)
def test_F(self):
assert hasattr(self.instance, 'F')
F_expected = Matrix([self.da_expr])
assert self.instance.F == F_expected
assert isinstance(self.instance.F, Matrix)
assert self.instance.F.shape == (1, 1)
def test_rhs(self):
assert hasattr(self.instance, 'rhs')
rhs_expected = Matrix([self.da_expr])
rhs = self.instance.rhs()
assert isinstance(rhs, Matrix)
assert rhs.shape == (1, 1)
assert simplify(rhs - rhs_expected) == zeros(1)
@pytest.mark.parametrize(
'musculotendon_concrete, curve',
[
(
MusculotendonDeGroote2016,
CharacteristicCurveCollection(
tendon_force_length=TendonForceLengthDeGroote2016,
tendon_force_length_inverse=TendonForceLengthInverseDeGroote2016,
fiber_force_length_passive=FiberForceLengthPassiveDeGroote2016,
fiber_force_length_passive_inverse=FiberForceLengthPassiveInverseDeGroote2016,
fiber_force_length_active=FiberForceLengthActiveDeGroote2016,
fiber_force_velocity=FiberForceVelocityDeGroote2016,
fiber_force_velocity_inverse=FiberForceVelocityInverseDeGroote2016,
),
)
],
)
class TestFiberLengthExplicit:
@pytest.fixture(autouse=True)
def _musculotendon_fiber_length_explicit_fixture(
self,
musculotendon_concrete,
curve,
):
self.name = 'name'
self.N = ReferenceFrame('N')
self.q = dynamicsymbols('q')
self.origin = Point('pO')
self.insertion = Point('pI')
self.insertion.set_pos(self.origin, self.q*self.N.x)
self.pathway = LinearPathway(self.origin, self.insertion)
self.activation = FirstOrderActivationDeGroote2016(self.name)
self.e = self.activation.excitation
self.a = self.activation.activation
self.tau_a = self.activation.activation_time_constant
self.tau_d = self.activation.deactivation_time_constant
self.b = self.activation.smoothing_rate
self.formulation = MusculotendonFormulation.FIBER_LENGTH_EXPLICIT
self.l_T_slack = Symbol('l_T_slack')
self.F_M_max = Symbol('F_M_max')
self.l_M_opt = Symbol('l_M_opt')
self.v_M_max = Symbol('v_M_max')
self.alpha_opt = Symbol('alpha_opt')
self.beta = Symbol('beta')
self.instance = musculotendon_concrete(
self.name,
self.pathway,
self.activation,
musculotendon_dynamics=self.formulation,
tendon_slack_length=self.l_T_slack,
peak_isometric_force=self.F_M_max,
optimal_fiber_length=self.l_M_opt,
maximal_fiber_velocity=self.v_M_max,
optimal_pennation_angle=self.alpha_opt,
fiber_damping_coefficient=self.beta,
with_defaults=True,
)
self.l_M_tilde = dynamicsymbols('l_M_tilde_name')
l_MT = self.pathway.length
l_M = self.l_M_tilde*self.l_M_opt
l_T = l_MT - sqrt(l_M**2 - (self.l_M_opt*sin(self.alpha_opt))**2)
fl_T = curve.tendon_force_length.with_defaults(l_T/self.l_T_slack)
fl_M_pas = curve.fiber_force_length_passive.with_defaults(self.l_M_tilde)
fl_M_act = curve.fiber_force_length_active.with_defaults(self.l_M_tilde)
v_M_tilde = curve.fiber_force_velocity_inverse.with_defaults(
((((fl_T*self.F_M_max)/((l_MT - l_T)/l_M))/self.F_M_max) - fl_M_pas)
/(self.a*fl_M_act)
)
self.dl_M_tilde_expr = (self.v_M_max/self.l_M_opt)*v_M_tilde
self.da_expr = (
(1/(self.tau_a*(Rational(1, 2) + Rational(3, 2)*self.a)))
*(Rational(1, 2) + Rational(1, 2)*tanh(self.b*(self.e - self.a)))
+ ((Rational(1, 2) + Rational(3, 2)*self.a)/self.tau_d)
*(Rational(1, 2) - Rational(1, 2)*tanh(self.b*(self.e - self.a)))
)*(self.e - self.a)
def test_state_vars(self):
assert hasattr(self.instance, 'x')
assert hasattr(self.instance, 'state_vars')
assert self.instance.x == self.instance.state_vars
x_expected = Matrix([self.l_M_tilde, self.a])
assert self.instance.x == x_expected
assert self.instance.state_vars == x_expected
assert isinstance(self.instance.x, Matrix)
assert isinstance(self.instance.state_vars, Matrix)
assert self.instance.x.shape == (2, 1)
assert self.instance.state_vars.shape == (2, 1)
def test_input_vars(self):
assert hasattr(self.instance, 'r')
assert hasattr(self.instance, 'input_vars')
assert self.instance.r == self.instance.input_vars
r_expected = Matrix([self.e])
assert self.instance.r == r_expected
assert self.instance.input_vars == r_expected
assert isinstance(self.instance.r, Matrix)
assert isinstance(self.instance.input_vars, Matrix)
assert self.instance.r.shape == (1, 1)
assert self.instance.input_vars.shape == (1, 1)
def test_constants(self):
assert hasattr(self.instance, 'p')
assert hasattr(self.instance, 'constants')
assert self.instance.p == self.instance.constants
p_expected = Matrix(
[
self.l_T_slack,
self.F_M_max,
self.l_M_opt,
self.v_M_max,
self.alpha_opt,
self.beta,
self.tau_a,
self.tau_d,
self.b,
]
)
assert self.instance.p == p_expected
assert self.instance.constants == p_expected
assert isinstance(self.instance.p, Matrix)
assert isinstance(self.instance.constants, Matrix)
assert self.instance.p.shape == (9, 1)
assert self.instance.constants.shape == (9, 1)
def test_M(self):
assert hasattr(self.instance, 'M')
M_expected = eye(2)
assert self.instance.M == M_expected
assert isinstance(self.instance.M, Matrix)
assert self.instance.M.shape == (2, 2)
def test_F(self):
assert hasattr(self.instance, 'F')
F_expected = Matrix([self.dl_M_tilde_expr, self.da_expr])
assert self.instance.F == F_expected
assert isinstance(self.instance.F, Matrix)
assert self.instance.F.shape == (2, 1)
def test_rhs(self):
assert hasattr(self.instance, 'rhs')
rhs_expected = Matrix([self.dl_M_tilde_expr, self.da_expr])
rhs = self.instance.rhs()
assert isinstance(rhs, Matrix)
assert rhs.shape == (2, 1)
assert simplify(rhs - rhs_expected) == zeros(2, 1)
@pytest.mark.parametrize(
'musculotendon_concrete, curve',
[
(
MusculotendonDeGroote2016,
CharacteristicCurveCollection(
tendon_force_length=TendonForceLengthDeGroote2016,
tendon_force_length_inverse=TendonForceLengthInverseDeGroote2016,
fiber_force_length_passive=FiberForceLengthPassiveDeGroote2016,
fiber_force_length_passive_inverse=FiberForceLengthPassiveInverseDeGroote2016,
fiber_force_length_active=FiberForceLengthActiveDeGroote2016,
fiber_force_velocity=FiberForceVelocityDeGroote2016,
fiber_force_velocity_inverse=FiberForceVelocityInverseDeGroote2016,
),
)
],
)
class TestTendonForceExplicit:
@pytest.fixture(autouse=True)
def _musculotendon_tendon_force_explicit_fixture(
self,
musculotendon_concrete,
curve,
):
self.name = 'name'
self.N = ReferenceFrame('N')
self.q = dynamicsymbols('q')
self.origin = Point('pO')
self.insertion = Point('pI')
self.insertion.set_pos(self.origin, self.q*self.N.x)
self.pathway = LinearPathway(self.origin, self.insertion)
self.activation = FirstOrderActivationDeGroote2016(self.name)
self.e = self.activation.excitation
self.a = self.activation.activation
self.tau_a = self.activation.activation_time_constant
self.tau_d = self.activation.deactivation_time_constant
self.b = self.activation.smoothing_rate
self.formulation = MusculotendonFormulation.TENDON_FORCE_EXPLICIT
self.l_T_slack = Symbol('l_T_slack')
self.F_M_max = Symbol('F_M_max')
self.l_M_opt = Symbol('l_M_opt')
self.v_M_max = Symbol('v_M_max')
self.alpha_opt = Symbol('alpha_opt')
self.beta = Symbol('beta')
self.instance = musculotendon_concrete(
self.name,
self.pathway,
self.activation,
musculotendon_dynamics=self.formulation,
tendon_slack_length=self.l_T_slack,
peak_isometric_force=self.F_M_max,
optimal_fiber_length=self.l_M_opt,
maximal_fiber_velocity=self.v_M_max,
optimal_pennation_angle=self.alpha_opt,
fiber_damping_coefficient=self.beta,
with_defaults=True,
)
self.F_T_tilde = dynamicsymbols('F_T_tilde_name')
l_T_tilde = curve.tendon_force_length_inverse.with_defaults(self.F_T_tilde)
l_MT = self.pathway.length
v_MT = self.pathway.extension_velocity
l_T = l_T_tilde*self.l_T_slack
l_M = sqrt((l_MT - l_T)**2 + (self.l_M_opt*sin(self.alpha_opt))**2)
l_M_tilde = l_M/self.l_M_opt
cos_alpha = (l_MT - l_T)/l_M
F_T = self.F_T_tilde*self.F_M_max
F_M = F_T/cos_alpha
F_M_tilde = F_M/self.F_M_max
fl_M_pas = curve.fiber_force_length_passive.with_defaults(l_M_tilde)
fl_M_act = curve.fiber_force_length_active.with_defaults(l_M_tilde)
fv_M = (F_M_tilde - fl_M_pas)/(self.a*fl_M_act)
v_M_tilde = curve.fiber_force_velocity_inverse.with_defaults(fv_M)
v_M = v_M_tilde*self.v_M_max
v_T = v_MT - v_M/cos_alpha
v_T_tilde = v_T/self.l_T_slack
self.dF_T_tilde_expr = (
Float('0.2')*Float('33.93669377311689')*exp(
Float('33.93669377311689')*UnevaluatedExpr(l_T_tilde - Float('0.995'))
)*v_T_tilde
)
self.da_expr = (
(1/(self.tau_a*(Rational(1, 2) + Rational(3, 2)*self.a)))
*(Rational(1, 2) + Rational(1, 2)*tanh(self.b*(self.e - self.a)))
+ ((Rational(1, 2) + Rational(3, 2)*self.a)/self.tau_d)
*(Rational(1, 2) - Rational(1, 2)*tanh(self.b*(self.e - self.a)))
)*(self.e - self.a)
def test_state_vars(self):
assert hasattr(self.instance, 'x')
assert hasattr(self.instance, 'state_vars')
assert self.instance.x == self.instance.state_vars
x_expected = Matrix([self.F_T_tilde, self.a])
assert self.instance.x == x_expected
assert self.instance.state_vars == x_expected
assert isinstance(self.instance.x, Matrix)
assert isinstance(self.instance.state_vars, Matrix)
assert self.instance.x.shape == (2, 1)
assert self.instance.state_vars.shape == (2, 1)
def test_input_vars(self):
assert hasattr(self.instance, 'r')
assert hasattr(self.instance, 'input_vars')
assert self.instance.r == self.instance.input_vars
r_expected = Matrix([self.e])
assert self.instance.r == r_expected
assert self.instance.input_vars == r_expected
assert isinstance(self.instance.r, Matrix)
assert isinstance(self.instance.input_vars, Matrix)
assert self.instance.r.shape == (1, 1)
assert self.instance.input_vars.shape == (1, 1)
def test_constants(self):
assert hasattr(self.instance, 'p')
assert hasattr(self.instance, 'constants')
assert self.instance.p == self.instance.constants
p_expected = Matrix(
[
self.l_T_slack,
self.F_M_max,
self.l_M_opt,
self.v_M_max,
self.alpha_opt,
self.beta,
self.tau_a,
self.tau_d,
self.b,
]
)
assert self.instance.p == p_expected
assert self.instance.constants == p_expected
assert isinstance(self.instance.p, Matrix)
assert isinstance(self.instance.constants, Matrix)
assert self.instance.p.shape == (9, 1)
assert self.instance.constants.shape == (9, 1)
def test_M(self):
assert hasattr(self.instance, 'M')
M_expected = eye(2)
assert self.instance.M == M_expected
assert isinstance(self.instance.M, Matrix)
assert self.instance.M.shape == (2, 2)
def test_F(self):
assert hasattr(self.instance, 'F')
F_expected = Matrix([self.dF_T_tilde_expr, self.da_expr])
assert self.instance.F == F_expected
assert isinstance(self.instance.F, Matrix)
assert self.instance.F.shape == (2, 1)
def test_rhs(self):
assert hasattr(self.instance, 'rhs')
rhs_expected = Matrix([self.dF_T_tilde_expr, self.da_expr])
rhs = self.instance.rhs()
assert isinstance(rhs, Matrix)
assert rhs.shape == (2, 1)
assert simplify(rhs - rhs_expected) == zeros(2, 1)
class TestMusculotendonDeGroote2016:
@staticmethod
def test_class():
assert issubclass(MusculotendonDeGroote2016, ForceActuator)
assert issubclass(MusculotendonDeGroote2016, _NamedMixin)
assert MusculotendonDeGroote2016.__name__ == 'MusculotendonDeGroote2016'
@staticmethod
def test_instance():
origin = Point('pO')
insertion = Point('pI')
insertion.set_pos(origin, dynamicsymbols('q')*ReferenceFrame('N').x)
pathway = LinearPathway(origin, insertion)
activation = FirstOrderActivationDeGroote2016('name')
l_T_slack = Symbol('l_T_slack')
F_M_max = Symbol('F_M_max')
l_M_opt = Symbol('l_M_opt')
v_M_max = Symbol('v_M_max')
alpha_opt = Symbol('alpha_opt')
beta = Symbol('beta')
instance = MusculotendonDeGroote2016(
'name',
pathway,
activation,
musculotendon_dynamics=MusculotendonFormulation.RIGID_TENDON,
tendon_slack_length=l_T_slack,
peak_isometric_force=F_M_max,
optimal_fiber_length=l_M_opt,
maximal_fiber_velocity=v_M_max,
optimal_pennation_angle=alpha_opt,
fiber_damping_coefficient=beta,
)
assert isinstance(instance, MusculotendonDeGroote2016)
@pytest.fixture(autouse=True)
def _musculotendon_fixture(self):
self.name = 'name'
self.N = ReferenceFrame('N')
self.q = dynamicsymbols('q')
self.origin = Point('pO')
self.insertion = Point('pI')
self.insertion.set_pos(self.origin, self.q*self.N.x)
self.pathway = LinearPathway(self.origin, self.insertion)
self.activation = FirstOrderActivationDeGroote2016(self.name)
self.l_T_slack = Symbol('l_T_slack')
self.F_M_max = Symbol('F_M_max')
self.l_M_opt = Symbol('l_M_opt')
self.v_M_max = Symbol('v_M_max')
self.alpha_opt = Symbol('alpha_opt')
self.beta = Symbol('beta')
def test_with_defaults(self):
origin = Point('pO')
insertion = Point('pI')
insertion.set_pos(origin, dynamicsymbols('q')*ReferenceFrame('N').x)
pathway = LinearPathway(origin, insertion)
activation = FirstOrderActivationDeGroote2016('name')
l_T_slack = Symbol('l_T_slack')
F_M_max = Symbol('F_M_max')
l_M_opt = Symbol('l_M_opt')
v_M_max = Float('10.0')
alpha_opt = Float('0.0')
beta = Float('0.1')
instance = MusculotendonDeGroote2016.with_defaults(
'name',
pathway,
activation,
musculotendon_dynamics=MusculotendonFormulation.RIGID_TENDON,
tendon_slack_length=l_T_slack,
peak_isometric_force=F_M_max,
optimal_fiber_length=l_M_opt,
)
assert instance.tendon_slack_length == l_T_slack
assert instance.peak_isometric_force == F_M_max
assert instance.optimal_fiber_length == l_M_opt
assert instance.maximal_fiber_velocity == v_M_max
assert instance.optimal_pennation_angle == alpha_opt
assert instance.fiber_damping_coefficient == beta
@pytest.mark.parametrize(
'l_T_slack, expected',
[
(None, Symbol('l_T_slack_name')),
(Symbol('l_T_slack'), Symbol('l_T_slack')),
(Rational(1, 2), Rational(1, 2)),
(Float('0.5'), Float('0.5')),
],
)
def test_tendon_slack_length(self, l_T_slack, expected):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
musculotendon_dynamics=MusculotendonFormulation.RIGID_TENDON,
tendon_slack_length=l_T_slack,
peak_isometric_force=self.F_M_max,
optimal_fiber_length=self.l_M_opt,
maximal_fiber_velocity=self.v_M_max,
optimal_pennation_angle=self.alpha_opt,
fiber_damping_coefficient=self.beta,
)
assert instance.l_T_slack == expected
assert instance.tendon_slack_length == expected
@pytest.mark.parametrize(
'F_M_max, expected',
[
(None, Symbol('F_M_max_name')),
(Symbol('F_M_max'), Symbol('F_M_max')),
(Integer(1000), Integer(1000)),
(Float('1000.0'), Float('1000.0')),
],
)
def test_peak_isometric_force(self, F_M_max, expected):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
musculotendon_dynamics=MusculotendonFormulation.RIGID_TENDON,
tendon_slack_length=self.l_T_slack,
peak_isometric_force=F_M_max,
optimal_fiber_length=self.l_M_opt,
maximal_fiber_velocity=self.v_M_max,
optimal_pennation_angle=self.alpha_opt,
fiber_damping_coefficient=self.beta,
)
assert instance.F_M_max == expected
assert instance.peak_isometric_force == expected
@pytest.mark.parametrize(
'l_M_opt, expected',
[
(None, Symbol('l_M_opt_name')),
(Symbol('l_M_opt'), Symbol('l_M_opt')),
(Rational(1, 2), Rational(1, 2)),
(Float('0.5'), Float('0.5')),
],
)
def test_optimal_fiber_length(self, l_M_opt, expected):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
musculotendon_dynamics=MusculotendonFormulation.RIGID_TENDON,
tendon_slack_length=self.l_T_slack,
peak_isometric_force=self.F_M_max,
optimal_fiber_length=l_M_opt,
maximal_fiber_velocity=self.v_M_max,
optimal_pennation_angle=self.alpha_opt,
fiber_damping_coefficient=self.beta,
)
assert instance.l_M_opt == expected
assert instance.optimal_fiber_length == expected
@pytest.mark.parametrize(
'v_M_max, expected',
[
(None, Symbol('v_M_max_name')),
(Symbol('v_M_max'), Symbol('v_M_max')),
(Integer(10), Integer(10)),
(Float('10.0'), Float('10.0')),
],
)
def test_maximal_fiber_velocity(self, v_M_max, expected):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
musculotendon_dynamics=MusculotendonFormulation.RIGID_TENDON,
tendon_slack_length=self.l_T_slack,
peak_isometric_force=self.F_M_max,
optimal_fiber_length=self.l_M_opt,
maximal_fiber_velocity=v_M_max,
optimal_pennation_angle=self.alpha_opt,
fiber_damping_coefficient=self.beta,
)
assert instance.v_M_max == expected
assert instance.maximal_fiber_velocity == expected
@pytest.mark.parametrize(
'alpha_opt, expected',
[
(None, Symbol('alpha_opt_name')),
(Symbol('alpha_opt'), Symbol('alpha_opt')),
(Integer(0), Integer(0)),
(Float('0.1'), Float('0.1')),
],
)
def test_optimal_pennation_angle(self, alpha_opt, expected):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
musculotendon_dynamics=MusculotendonFormulation.RIGID_TENDON,
tendon_slack_length=self.l_T_slack,
peak_isometric_force=self.F_M_max,
optimal_fiber_length=self.l_M_opt,
maximal_fiber_velocity=self.v_M_max,
optimal_pennation_angle=alpha_opt,
fiber_damping_coefficient=self.beta,
)
assert instance.alpha_opt == expected
assert instance.optimal_pennation_angle == expected
@pytest.mark.parametrize(
'beta, expected',
[
(None, Symbol('beta_name')),
(Symbol('beta'), Symbol('beta')),
(Integer(0), Integer(0)),
(Rational(1, 10), Rational(1, 10)),
(Float('0.1'), Float('0.1')),
],
)
def test_fiber_damping_coefficient(self, beta, expected):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
musculotendon_dynamics=MusculotendonFormulation.RIGID_TENDON,
tendon_slack_length=self.l_T_slack,
peak_isometric_force=self.F_M_max,
optimal_fiber_length=self.l_M_opt,
maximal_fiber_velocity=self.v_M_max,
optimal_pennation_angle=self.alpha_opt,
fiber_damping_coefficient=beta,
)
assert instance.beta == expected
assert instance.fiber_damping_coefficient == expected
def test_excitation(self):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
)
assert hasattr(instance, 'e')
assert hasattr(instance, 'excitation')
e_expected = dynamicsymbols('e_name')
assert instance.e == e_expected
assert instance.excitation == e_expected
assert instance.e is instance.excitation
def test_excitation_is_immutable(self):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
)
with pytest.raises(AttributeError):
instance.e = None
with pytest.raises(AttributeError):
instance.excitation = None
def test_activation(self):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
)
assert hasattr(instance, 'a')
assert hasattr(instance, 'activation')
a_expected = dynamicsymbols('a_name')
assert instance.a == a_expected
assert instance.activation == a_expected
def test_activation_is_immutable(self):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
)
with pytest.raises(AttributeError):
instance.a = None
with pytest.raises(AttributeError):
instance.activation = None
def test_repr(self):
instance = MusculotendonDeGroote2016(
self.name,
self.pathway,
self.activation,
musculotendon_dynamics=MusculotendonFormulation.RIGID_TENDON,
tendon_slack_length=self.l_T_slack,
peak_isometric_force=self.F_M_max,
optimal_fiber_length=self.l_M_opt,
maximal_fiber_velocity=self.v_M_max,
optimal_pennation_angle=self.alpha_opt,
fiber_damping_coefficient=self.beta,
)
expected = (
'MusculotendonDeGroote2016(\'name\', '
'pathway=LinearPathway(pO, pI), '
'activation_dynamics=FirstOrderActivationDeGroote2016(\'name\', '
'activation_time_constant=tau_a_name, '
'deactivation_time_constant=tau_d_name, '
'smoothing_rate=b_name), '
'musculotendon_dynamics=0, '
'tendon_slack_length=l_T_slack, '
'peak_isometric_force=F_M_max, '
'optimal_fiber_length=l_M_opt, '
'maximal_fiber_velocity=v_M_max, '
'optimal_pennation_angle=alpha_opt, '
'fiber_damping_coefficient=beta)'
)
assert repr(instance) == expected