test_curve.py 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695
  1. """Tests for the ``sympy.physics.biomechanics.characteristic.py`` module."""
  2. import pytest
  3. from sympy.core.expr import UnevaluatedExpr
  4. from sympy.core.function import Function
  5. from sympy.core.numbers import Float, Integer
  6. from sympy.core.symbol import Symbol, symbols
  7. from sympy.external.importtools import import_module
  8. from sympy.functions.elementary.exponential import exp, log
  9. from sympy.functions.elementary.hyperbolic import cosh, sinh
  10. from sympy.functions.elementary.miscellaneous import sqrt
  11. from sympy.physics.biomechanics.curve import (
  12. CharacteristicCurveCollection,
  13. CharacteristicCurveFunction,
  14. FiberForceLengthActiveDeGroote2016,
  15. FiberForceLengthPassiveDeGroote2016,
  16. FiberForceLengthPassiveInverseDeGroote2016,
  17. FiberForceVelocityDeGroote2016,
  18. FiberForceVelocityInverseDeGroote2016,
  19. TendonForceLengthDeGroote2016,
  20. TendonForceLengthInverseDeGroote2016,
  21. )
  22. from sympy.printing.c import C89CodePrinter, C99CodePrinter, C11CodePrinter
  23. from sympy.printing.cxx import (
  24. CXX98CodePrinter,
  25. CXX11CodePrinter,
  26. CXX17CodePrinter,
  27. )
  28. from sympy.printing.fortran import FCodePrinter
  29. from sympy.printing.lambdarepr import LambdaPrinter
  30. from sympy.printing.latex import LatexPrinter
  31. from sympy.printing.octave import OctaveCodePrinter
  32. from sympy.printing.numpy import (
  33. CuPyPrinter,
  34. JaxPrinter,
  35. NumPyPrinter,
  36. SciPyPrinter,
  37. )
  38. from sympy.printing.pycode import MpmathPrinter, PythonCodePrinter
  39. from sympy.utilities.lambdify import lambdify
  40. jax = import_module('jax')
  41. numpy = import_module('numpy')
  42. if jax:
  43. jax.config.update('jax_enable_x64', True)
  44. class TestCharacteristicCurveFunction:
  45. @staticmethod
  46. @pytest.mark.parametrize(
  47. 'code_printer, expected',
  48. [
  49. (C89CodePrinter, '(a + b)*(c + d)*(e + f)'),
  50. (C99CodePrinter, '(a + b)*(c + d)*(e + f)'),
  51. (C11CodePrinter, '(a + b)*(c + d)*(e + f)'),
  52. (CXX98CodePrinter, '(a + b)*(c + d)*(e + f)'),
  53. (CXX11CodePrinter, '(a + b)*(c + d)*(e + f)'),
  54. (CXX17CodePrinter, '(a + b)*(c + d)*(e + f)'),
  55. (FCodePrinter, ' (a + b)*(c + d)*(e + f)'),
  56. (OctaveCodePrinter, '(a + b).*(c + d).*(e + f)'),
  57. (PythonCodePrinter, '(a + b)*(c + d)*(e + f)'),
  58. (NumPyPrinter, '(a + b)*(c + d)*(e + f)'),
  59. (SciPyPrinter, '(a + b)*(c + d)*(e + f)'),
  60. (CuPyPrinter, '(a + b)*(c + d)*(e + f)'),
  61. (JaxPrinter, '(a + b)*(c + d)*(e + f)'),
  62. (MpmathPrinter, '(a + b)*(c + d)*(e + f)'),
  63. (LambdaPrinter, '(a + b)*(c + d)*(e + f)'),
  64. ]
  65. )
  66. def test_print_code_parenthesize(code_printer, expected):
  67. class ExampleFunction(CharacteristicCurveFunction):
  68. @classmethod
  69. def eval(cls, a, b):
  70. pass
  71. def doit(self, **kwargs):
  72. a, b = self.args
  73. return a + b
  74. a, b, c, d, e, f = symbols('a, b, c, d, e, f')
  75. f1 = ExampleFunction(a, b)
  76. f2 = ExampleFunction(c, d)
  77. f3 = ExampleFunction(e, f)
  78. assert code_printer().doprint(f1*f2*f3) == expected
  79. class TestTendonForceLengthDeGroote2016:
  80. @pytest.fixture(autouse=True)
  81. def _tendon_force_length_arguments_fixture(self):
  82. self.l_T_tilde = Symbol('l_T_tilde')
  83. self.c0 = Symbol('c_0')
  84. self.c1 = Symbol('c_1')
  85. self.c2 = Symbol('c_2')
  86. self.c3 = Symbol('c_3')
  87. self.constants = (self.c0, self.c1, self.c2, self.c3)
  88. @staticmethod
  89. def test_class():
  90. assert issubclass(TendonForceLengthDeGroote2016, Function)
  91. assert issubclass(TendonForceLengthDeGroote2016, CharacteristicCurveFunction)
  92. assert TendonForceLengthDeGroote2016.__name__ == 'TendonForceLengthDeGroote2016'
  93. def test_instance(self):
  94. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants)
  95. assert isinstance(fl_T, TendonForceLengthDeGroote2016)
  96. assert str(fl_T) == 'TendonForceLengthDeGroote2016(l_T_tilde, c_0, c_1, c_2, c_3)'
  97. def test_doit(self):
  98. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants).doit()
  99. assert fl_T == self.c0*exp(self.c3*(self.l_T_tilde - self.c1)) - self.c2
  100. def test_doit_evaluate_false(self):
  101. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants).doit(evaluate=False)
  102. assert fl_T == self.c0*exp(self.c3*UnevaluatedExpr(self.l_T_tilde - self.c1)) - self.c2
  103. def test_with_defaults(self):
  104. constants = (
  105. Float('0.2'),
  106. Float('0.995'),
  107. Float('0.25'),
  108. Float('33.93669377311689'),
  109. )
  110. fl_T_manual = TendonForceLengthDeGroote2016(self.l_T_tilde, *constants)
  111. fl_T_constants = TendonForceLengthDeGroote2016.with_defaults(self.l_T_tilde)
  112. assert fl_T_manual == fl_T_constants
  113. def test_differentiate_wrt_l_T_tilde(self):
  114. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants)
  115. expected = self.c0*self.c3*exp(self.c3*UnevaluatedExpr(-self.c1 + self.l_T_tilde))
  116. assert fl_T.diff(self.l_T_tilde) == expected
  117. def test_differentiate_wrt_c0(self):
  118. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants)
  119. expected = exp(self.c3*UnevaluatedExpr(-self.c1 + self.l_T_tilde))
  120. assert fl_T.diff(self.c0) == expected
  121. def test_differentiate_wrt_c1(self):
  122. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants)
  123. expected = -self.c0*self.c3*exp(self.c3*UnevaluatedExpr(self.l_T_tilde - self.c1))
  124. assert fl_T.diff(self.c1) == expected
  125. def test_differentiate_wrt_c2(self):
  126. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants)
  127. expected = Integer(-1)
  128. assert fl_T.diff(self.c2) == expected
  129. def test_differentiate_wrt_c3(self):
  130. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants)
  131. expected = self.c0*(self.l_T_tilde - self.c1)*exp(self.c3*UnevaluatedExpr(self.l_T_tilde - self.c1))
  132. assert fl_T.diff(self.c3) == expected
  133. def test_inverse(self):
  134. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants)
  135. assert fl_T.inverse() is TendonForceLengthInverseDeGroote2016
  136. def test_function_print_latex(self):
  137. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants)
  138. expected = r'\operatorname{fl}^T \left( l_{T tilde} \right)'
  139. assert LatexPrinter().doprint(fl_T) == expected
  140. def test_expression_print_latex(self):
  141. fl_T = TendonForceLengthDeGroote2016(self.l_T_tilde, *self.constants)
  142. expected = r'c_{0} e^{c_{3} \left(- c_{1} + l_{T tilde}\right)} - c_{2}'
  143. assert LatexPrinter().doprint(fl_T.doit()) == expected
  144. @pytest.mark.parametrize(
  145. 'code_printer, expected',
  146. [
  147. (
  148. C89CodePrinter,
  149. '(-0.25 + 0.20000000000000001*exp(33.93669377311689*(l_T_tilde - 0.995)))',
  150. ),
  151. (
  152. C99CodePrinter,
  153. '(-0.25 + 0.20000000000000001*exp(33.93669377311689*(l_T_tilde - 0.995)))',
  154. ),
  155. (
  156. C11CodePrinter,
  157. '(-0.25 + 0.20000000000000001*exp(33.93669377311689*(l_T_tilde - 0.995)))',
  158. ),
  159. (
  160. CXX98CodePrinter,
  161. '(-0.25 + 0.20000000000000001*exp(33.93669377311689*(l_T_tilde - 0.995)))',
  162. ),
  163. (
  164. CXX11CodePrinter,
  165. '(-0.25 + 0.20000000000000001*std::exp(33.93669377311689*(l_T_tilde - 0.995)))',
  166. ),
  167. (
  168. CXX17CodePrinter,
  169. '(-0.25 + 0.20000000000000001*std::exp(33.93669377311689*(l_T_tilde - 0.995)))',
  170. ),
  171. (
  172. FCodePrinter,
  173. ' (-0.25d0 + 0.2d0*exp(33.93669377311689d0*(l_T_tilde - 0.995d0)))',
  174. ),
  175. (
  176. OctaveCodePrinter,
  177. '(-0.25 + 0.2*exp(33.93669377311689*(l_T_tilde - 0.995)))',
  178. ),
  179. (
  180. PythonCodePrinter,
  181. '(-0.25 + 0.2*math.exp(33.93669377311689*(l_T_tilde - 0.995)))',
  182. ),
  183. (
  184. NumPyPrinter,
  185. '(-0.25 + 0.2*numpy.exp(33.93669377311689*(l_T_tilde - 0.995)))',
  186. ),
  187. (
  188. SciPyPrinter,
  189. '(-0.25 + 0.2*numpy.exp(33.93669377311689*(l_T_tilde - 0.995)))',
  190. ),
  191. (
  192. CuPyPrinter,
  193. '(-0.25 + 0.2*cupy.exp(33.93669377311689*(l_T_tilde - 0.995)))',
  194. ),
  195. (
  196. JaxPrinter,
  197. '(-0.25 + 0.2*jax.numpy.exp(33.93669377311689*(l_T_tilde - 0.995)))',
  198. ),
  199. (
  200. MpmathPrinter,
  201. '(mpmath.mpf((1, 1, -2, 1)) + mpmath.mpf((0, 3602879701896397, -54, 52))'
  202. '*mpmath.exp(mpmath.mpf((0, 9552330089424741, -48, 54))*(l_T_tilde + '
  203. 'mpmath.mpf((1, 8962163258467287, -53, 53)))))',
  204. ),
  205. (
  206. LambdaPrinter,
  207. '(-0.25 + 0.2*math.exp(33.93669377311689*(l_T_tilde - 0.995)))',
  208. ),
  209. ]
  210. )
  211. def test_print_code(self, code_printer, expected):
  212. fl_T = TendonForceLengthDeGroote2016.with_defaults(self.l_T_tilde)
  213. assert code_printer().doprint(fl_T) == expected
  214. def test_derivative_print_code(self):
  215. fl_T = TendonForceLengthDeGroote2016.with_defaults(self.l_T_tilde)
  216. dfl_T_dl_T_tilde = fl_T.diff(self.l_T_tilde)
  217. expected = '6.787338754623378*math.exp(33.93669377311689*(l_T_tilde - 0.995))'
  218. assert PythonCodePrinter().doprint(dfl_T_dl_T_tilde) == expected
  219. def test_lambdify(self):
  220. fl_T = TendonForceLengthDeGroote2016.with_defaults(self.l_T_tilde)
  221. fl_T_callable = lambdify(self.l_T_tilde, fl_T)
  222. assert fl_T_callable(1.0) == pytest.approx(-0.013014055039221595)
  223. @pytest.mark.skipif(numpy is None, reason='NumPy not installed')
  224. def test_lambdify_numpy(self):
  225. fl_T = TendonForceLengthDeGroote2016.with_defaults(self.l_T_tilde)
  226. fl_T_callable = lambdify(self.l_T_tilde, fl_T, 'numpy')
  227. l_T_tilde = numpy.array([0.95, 1.0, 1.01, 1.05])
  228. expected = numpy.array([
  229. -0.2065693181344816,
  230. -0.0130140550392216,
  231. 0.0827421191989246,
  232. 1.04314889144172,
  233. ])
  234. numpy.testing.assert_allclose(fl_T_callable(l_T_tilde), expected)
  235. @pytest.mark.skipif(jax is None, reason='JAX not installed')
  236. def test_lambdify_jax(self):
  237. fl_T = TendonForceLengthDeGroote2016.with_defaults(self.l_T_tilde)
  238. fl_T_callable = jax.jit(lambdify(self.l_T_tilde, fl_T, 'jax'))
  239. l_T_tilde = jax.numpy.array([0.95, 1.0, 1.01, 1.05])
  240. expected = jax.numpy.array([
  241. -0.2065693181344816,
  242. -0.0130140550392216,
  243. 0.0827421191989246,
  244. 1.04314889144172,
  245. ])
  246. numpy.testing.assert_allclose(fl_T_callable(l_T_tilde), expected)
  247. class TestTendonForceLengthInverseDeGroote2016:
  248. @pytest.fixture(autouse=True)
  249. def _tendon_force_length_inverse_arguments_fixture(self):
  250. self.fl_T = Symbol('fl_T')
  251. self.c0 = Symbol('c_0')
  252. self.c1 = Symbol('c_1')
  253. self.c2 = Symbol('c_2')
  254. self.c3 = Symbol('c_3')
  255. self.constants = (self.c0, self.c1, self.c2, self.c3)
  256. @staticmethod
  257. def test_class():
  258. assert issubclass(TendonForceLengthInverseDeGroote2016, Function)
  259. assert issubclass(TendonForceLengthInverseDeGroote2016, CharacteristicCurveFunction)
  260. assert TendonForceLengthInverseDeGroote2016.__name__ == 'TendonForceLengthInverseDeGroote2016'
  261. def test_instance(self):
  262. fl_T_inv = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants)
  263. assert isinstance(fl_T_inv, TendonForceLengthInverseDeGroote2016)
  264. assert str(fl_T_inv) == 'TendonForceLengthInverseDeGroote2016(fl_T, c_0, c_1, c_2, c_3)'
  265. def test_doit(self):
  266. fl_T_inv = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants).doit()
  267. assert fl_T_inv == log((self.fl_T + self.c2)/self.c0)/self.c3 + self.c1
  268. def test_doit_evaluate_false(self):
  269. fl_T_inv = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants).doit(evaluate=False)
  270. assert fl_T_inv == log(UnevaluatedExpr((self.fl_T + self.c2)/self.c0))/self.c3 + self.c1
  271. def test_with_defaults(self):
  272. constants = (
  273. Float('0.2'),
  274. Float('0.995'),
  275. Float('0.25'),
  276. Float('33.93669377311689'),
  277. )
  278. fl_T_inv_manual = TendonForceLengthInverseDeGroote2016(self.fl_T, *constants)
  279. fl_T_inv_constants = TendonForceLengthInverseDeGroote2016.with_defaults(self.fl_T)
  280. assert fl_T_inv_manual == fl_T_inv_constants
  281. def test_differentiate_wrt_fl_T(self):
  282. fl_T_inv = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants)
  283. expected = 1/(self.c3*(self.fl_T + self.c2))
  284. assert fl_T_inv.diff(self.fl_T) == expected
  285. def test_differentiate_wrt_c0(self):
  286. fl_T_inv = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants)
  287. expected = -1/(self.c0*self.c3)
  288. assert fl_T_inv.diff(self.c0) == expected
  289. def test_differentiate_wrt_c1(self):
  290. fl_T_inv = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants)
  291. expected = Integer(1)
  292. assert fl_T_inv.diff(self.c1) == expected
  293. def test_differentiate_wrt_c2(self):
  294. fl_T_inv = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants)
  295. expected = 1/(self.c3*(self.fl_T + self.c2))
  296. assert fl_T_inv.diff(self.c2) == expected
  297. def test_differentiate_wrt_c3(self):
  298. fl_T_inv = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants)
  299. expected = -log(UnevaluatedExpr((self.fl_T + self.c2)/self.c0))/self.c3**2
  300. assert fl_T_inv.diff(self.c3) == expected
  301. def test_inverse(self):
  302. fl_T_inv = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants)
  303. assert fl_T_inv.inverse() is TendonForceLengthDeGroote2016
  304. def test_function_print_latex(self):
  305. fl_T_inv = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants)
  306. expected = r'\left( \operatorname{fl}^T \right)^{-1} \left( fl_{T} \right)'
  307. assert LatexPrinter().doprint(fl_T_inv) == expected
  308. def test_expression_print_latex(self):
  309. fl_T = TendonForceLengthInverseDeGroote2016(self.fl_T, *self.constants)
  310. expected = r'c_{1} + \frac{\log{\left(\frac{c_{2} + fl_{T}}{c_{0}} \right)}}{c_{3}}'
  311. assert LatexPrinter().doprint(fl_T.doit()) == expected
  312. @pytest.mark.parametrize(
  313. 'code_printer, expected',
  314. [
  315. (
  316. C89CodePrinter,
  317. '(0.995 + 0.029466630034306838*log(5.0*fl_T + 1.25))',
  318. ),
  319. (
  320. C99CodePrinter,
  321. '(0.995 + 0.029466630034306838*log(5.0*fl_T + 1.25))',
  322. ),
  323. (
  324. C11CodePrinter,
  325. '(0.995 + 0.029466630034306838*log(5.0*fl_T + 1.25))',
  326. ),
  327. (
  328. CXX98CodePrinter,
  329. '(0.995 + 0.029466630034306838*log(5.0*fl_T + 1.25))',
  330. ),
  331. (
  332. CXX11CodePrinter,
  333. '(0.995 + 0.029466630034306838*std::log(5.0*fl_T + 1.25))',
  334. ),
  335. (
  336. CXX17CodePrinter,
  337. '(0.995 + 0.029466630034306838*std::log(5.0*fl_T + 1.25))',
  338. ),
  339. (
  340. FCodePrinter,
  341. ' (0.995d0 + 0.02946663003430684d0*log(5.0d0*fl_T + 1.25d0))',
  342. ),
  343. (
  344. OctaveCodePrinter,
  345. '(0.995 + 0.02946663003430684*log(5.0*fl_T + 1.25))',
  346. ),
  347. (
  348. PythonCodePrinter,
  349. '(0.995 + 0.02946663003430684*math.log(5.0*fl_T + 1.25))',
  350. ),
  351. (
  352. NumPyPrinter,
  353. '(0.995 + 0.02946663003430684*numpy.log(5.0*fl_T + 1.25))',
  354. ),
  355. (
  356. SciPyPrinter,
  357. '(0.995 + 0.02946663003430684*numpy.log(5.0*fl_T + 1.25))',
  358. ),
  359. (
  360. CuPyPrinter,
  361. '(0.995 + 0.02946663003430684*cupy.log(5.0*fl_T + 1.25))',
  362. ),
  363. (
  364. JaxPrinter,
  365. '(0.995 + 0.02946663003430684*jax.numpy.log(5.0*fl_T + 1.25))',
  366. ),
  367. (
  368. MpmathPrinter,
  369. '(mpmath.mpf((0, 8962163258467287, -53, 53))'
  370. ' + mpmath.mpf((0, 33972711434846347, -60, 55))'
  371. '*mpmath.log(mpmath.mpf((0, 5, 0, 3))*fl_T + mpmath.mpf((0, 5, -2, 3))))',
  372. ),
  373. (
  374. LambdaPrinter,
  375. '(0.995 + 0.02946663003430684*math.log(5.0*fl_T + 1.25))',
  376. ),
  377. ]
  378. )
  379. def test_print_code(self, code_printer, expected):
  380. fl_T_inv = TendonForceLengthInverseDeGroote2016.with_defaults(self.fl_T)
  381. assert code_printer().doprint(fl_T_inv) == expected
  382. def test_derivative_print_code(self):
  383. fl_T_inv = TendonForceLengthInverseDeGroote2016.with_defaults(self.fl_T)
  384. dfl_T_inv_dfl_T = fl_T_inv.diff(self.fl_T)
  385. expected = '1/(33.93669377311689*fl_T + 8.484173443279222)'
  386. assert PythonCodePrinter().doprint(dfl_T_inv_dfl_T) == expected
  387. def test_lambdify(self):
  388. fl_T_inv = TendonForceLengthInverseDeGroote2016.with_defaults(self.fl_T)
  389. fl_T_inv_callable = lambdify(self.fl_T, fl_T_inv)
  390. assert fl_T_inv_callable(0.0) == pytest.approx(1.0015752885)
  391. @pytest.mark.skipif(numpy is None, reason='NumPy not installed')
  392. def test_lambdify_numpy(self):
  393. fl_T_inv = TendonForceLengthInverseDeGroote2016.with_defaults(self.fl_T)
  394. fl_T_inv_callable = lambdify(self.fl_T, fl_T_inv, 'numpy')
  395. fl_T = numpy.array([-0.2, -0.01, 0.0, 1.01, 1.02, 1.05])
  396. expected = numpy.array([
  397. 0.9541505769,
  398. 1.0003724019,
  399. 1.0015752885,
  400. 1.0492347951,
  401. 1.0494677341,
  402. 1.0501557022,
  403. ])
  404. numpy.testing.assert_allclose(fl_T_inv_callable(fl_T), expected)
  405. @pytest.mark.skipif(jax is None, reason='JAX not installed')
  406. def test_lambdify_jax(self):
  407. fl_T_inv = TendonForceLengthInverseDeGroote2016.with_defaults(self.fl_T)
  408. fl_T_inv_callable = jax.jit(lambdify(self.fl_T, fl_T_inv, 'jax'))
  409. fl_T = jax.numpy.array([-0.2, -0.01, 0.0, 1.01, 1.02, 1.05])
  410. expected = jax.numpy.array([
  411. 0.9541505769,
  412. 1.0003724019,
  413. 1.0015752885,
  414. 1.0492347951,
  415. 1.0494677341,
  416. 1.0501557022,
  417. ])
  418. numpy.testing.assert_allclose(fl_T_inv_callable(fl_T), expected)
  419. class TestFiberForceLengthPassiveDeGroote2016:
  420. @pytest.fixture(autouse=True)
  421. def _fiber_force_length_passive_arguments_fixture(self):
  422. self.l_M_tilde = Symbol('l_M_tilde')
  423. self.c0 = Symbol('c_0')
  424. self.c1 = Symbol('c_1')
  425. self.constants = (self.c0, self.c1)
  426. @staticmethod
  427. def test_class():
  428. assert issubclass(FiberForceLengthPassiveDeGroote2016, Function)
  429. assert issubclass(FiberForceLengthPassiveDeGroote2016, CharacteristicCurveFunction)
  430. assert FiberForceLengthPassiveDeGroote2016.__name__ == 'FiberForceLengthPassiveDeGroote2016'
  431. def test_instance(self):
  432. fl_M_pas = FiberForceLengthPassiveDeGroote2016(self.l_M_tilde, *self.constants)
  433. assert isinstance(fl_M_pas, FiberForceLengthPassiveDeGroote2016)
  434. assert str(fl_M_pas) == 'FiberForceLengthPassiveDeGroote2016(l_M_tilde, c_0, c_1)'
  435. def test_doit(self):
  436. fl_M_pas = FiberForceLengthPassiveDeGroote2016(self.l_M_tilde, *self.constants).doit()
  437. assert fl_M_pas == (exp((self.c1*(self.l_M_tilde - 1))/self.c0) - 1)/(exp(self.c1) - 1)
  438. def test_doit_evaluate_false(self):
  439. fl_M_pas = FiberForceLengthPassiveDeGroote2016(self.l_M_tilde, *self.constants).doit(evaluate=False)
  440. assert fl_M_pas == (exp((self.c1*UnevaluatedExpr(self.l_M_tilde - 1))/self.c0) - 1)/(exp(self.c1) - 1)
  441. def test_with_defaults(self):
  442. constants = (
  443. Float('0.6'),
  444. Float('4.0'),
  445. )
  446. fl_M_pas_manual = FiberForceLengthPassiveDeGroote2016(self.l_M_tilde, *constants)
  447. fl_M_pas_constants = FiberForceLengthPassiveDeGroote2016.with_defaults(self.l_M_tilde)
  448. assert fl_M_pas_manual == fl_M_pas_constants
  449. def test_differentiate_wrt_l_M_tilde(self):
  450. fl_M_pas = FiberForceLengthPassiveDeGroote2016(self.l_M_tilde, *self.constants)
  451. expected = self.c1*exp(self.c1*UnevaluatedExpr(self.l_M_tilde - 1)/self.c0)/(self.c0*(exp(self.c1) - 1))
  452. assert fl_M_pas.diff(self.l_M_tilde) == expected
  453. def test_differentiate_wrt_c0(self):
  454. fl_M_pas = FiberForceLengthPassiveDeGroote2016(self.l_M_tilde, *self.constants)
  455. expected = (
  456. -self.c1*exp(self.c1*UnevaluatedExpr(self.l_M_tilde - 1)/self.c0)
  457. *UnevaluatedExpr(self.l_M_tilde - 1)/(self.c0**2*(exp(self.c1) - 1))
  458. )
  459. assert fl_M_pas.diff(self.c0) == expected
  460. def test_differentiate_wrt_c1(self):
  461. fl_M_pas = FiberForceLengthPassiveDeGroote2016(self.l_M_tilde, *self.constants)
  462. expected = (
  463. -exp(self.c1)*(-1 + exp(self.c1*UnevaluatedExpr(self.l_M_tilde - 1)/self.c0))/(exp(self.c1) - 1)**2
  464. + exp(self.c1*UnevaluatedExpr(self.l_M_tilde - 1)/self.c0)*(self.l_M_tilde - 1)/(self.c0*(exp(self.c1) - 1))
  465. )
  466. assert fl_M_pas.diff(self.c1) == expected
  467. def test_inverse(self):
  468. fl_M_pas = FiberForceLengthPassiveDeGroote2016(self.l_M_tilde, *self.constants)
  469. assert fl_M_pas.inverse() is FiberForceLengthPassiveInverseDeGroote2016
  470. def test_function_print_latex(self):
  471. fl_M_pas = FiberForceLengthPassiveDeGroote2016(self.l_M_tilde, *self.constants)
  472. expected = r'\operatorname{fl}^M_{pas} \left( l_{M tilde} \right)'
  473. assert LatexPrinter().doprint(fl_M_pas) == expected
  474. def test_expression_print_latex(self):
  475. fl_M_pas = FiberForceLengthPassiveDeGroote2016(self.l_M_tilde, *self.constants)
  476. expected = r'\frac{e^{\frac{c_{1} \left(l_{M tilde} - 1\right)}{c_{0}}} - 1}{e^{c_{1}} - 1}'
  477. assert LatexPrinter().doprint(fl_M_pas.doit()) == expected
  478. @pytest.mark.parametrize(
  479. 'code_printer, expected',
  480. [
  481. (
  482. C89CodePrinter,
  483. '(0.01865736036377405*(-1 + exp(6.666666666666667*(l_M_tilde - 1))))',
  484. ),
  485. (
  486. C99CodePrinter,
  487. '(0.01865736036377405*(-1 + exp(6.666666666666667*(l_M_tilde - 1))))',
  488. ),
  489. (
  490. C11CodePrinter,
  491. '(0.01865736036377405*(-1 + exp(6.666666666666667*(l_M_tilde - 1))))',
  492. ),
  493. (
  494. CXX98CodePrinter,
  495. '(0.01865736036377405*(-1 + exp(6.666666666666667*(l_M_tilde - 1))))',
  496. ),
  497. (
  498. CXX11CodePrinter,
  499. '(0.01865736036377405*(-1 + std::exp(6.666666666666667*(l_M_tilde - 1))))',
  500. ),
  501. (
  502. CXX17CodePrinter,
  503. '(0.01865736036377405*(-1 + std::exp(6.666666666666667*(l_M_tilde - 1))))',
  504. ),
  505. (
  506. FCodePrinter,
  507. ' (0.0186573603637741d0*(-1 + exp(6.666666666666667d0*(l_M_tilde - 1\n'
  508. ' @ ))))',
  509. ),
  510. (
  511. OctaveCodePrinter,
  512. '(0.0186573603637741*(-1 + exp(6.66666666666667*(l_M_tilde - 1))))',
  513. ),
  514. (
  515. PythonCodePrinter,
  516. '(0.0186573603637741*(-1 + math.exp(6.66666666666667*(l_M_tilde - 1))))',
  517. ),
  518. (
  519. NumPyPrinter,
  520. '(0.0186573603637741*(-1 + numpy.exp(6.66666666666667*(l_M_tilde - 1))))',
  521. ),
  522. (
  523. SciPyPrinter,
  524. '(0.0186573603637741*(-1 + numpy.exp(6.66666666666667*(l_M_tilde - 1))))',
  525. ),
  526. (
  527. CuPyPrinter,
  528. '(0.0186573603637741*(-1 + cupy.exp(6.66666666666667*(l_M_tilde - 1))))',
  529. ),
  530. (
  531. JaxPrinter,
  532. '(0.0186573603637741*(-1 + jax.numpy.exp(6.66666666666667*(l_M_tilde - 1))))',
  533. ),
  534. (
  535. MpmathPrinter,
  536. '(mpmath.mpf((0, 672202249456079, -55, 50))*(-1 + mpmath.exp('
  537. 'mpmath.mpf((0, 7505999378950827, -50, 53))*(l_M_tilde - 1))))',
  538. ),
  539. (
  540. LambdaPrinter,
  541. '(0.0186573603637741*(-1 + math.exp(6.66666666666667*(l_M_tilde - 1))))',
  542. ),
  543. ]
  544. )
  545. def test_print_code(self, code_printer, expected):
  546. fl_M_pas = FiberForceLengthPassiveDeGroote2016.with_defaults(self.l_M_tilde)
  547. assert code_printer().doprint(fl_M_pas) == expected
  548. def test_derivative_print_code(self):
  549. fl_M_pas = FiberForceLengthPassiveDeGroote2016.with_defaults(self.l_M_tilde)
  550. fl_M_pas_dl_M_tilde = fl_M_pas.diff(self.l_M_tilde)
  551. expected = '0.12438240242516*math.exp(6.66666666666667*(l_M_tilde - 1))'
  552. assert PythonCodePrinter().doprint(fl_M_pas_dl_M_tilde) == expected
  553. def test_lambdify(self):
  554. fl_M_pas = FiberForceLengthPassiveDeGroote2016.with_defaults(self.l_M_tilde)
  555. fl_M_pas_callable = lambdify(self.l_M_tilde, fl_M_pas)
  556. assert fl_M_pas_callable(1.0) == pytest.approx(0.0)
  557. @pytest.mark.skipif(numpy is None, reason='NumPy not installed')
  558. def test_lambdify_numpy(self):
  559. fl_M_pas = FiberForceLengthPassiveDeGroote2016.with_defaults(self.l_M_tilde)
  560. fl_M_pas_callable = lambdify(self.l_M_tilde, fl_M_pas, 'numpy')
  561. l_M_tilde = numpy.array([0.5, 0.8, 0.9, 1.0, 1.1, 1.2, 1.5])
  562. expected = numpy.array([
  563. -0.0179917778,
  564. -0.0137393336,
  565. -0.0090783522,
  566. 0.0,
  567. 0.0176822155,
  568. 0.0521224686,
  569. 0.5043387669,
  570. ])
  571. numpy.testing.assert_allclose(fl_M_pas_callable(l_M_tilde), expected)
  572. @pytest.mark.skipif(jax is None, reason='JAX not installed')
  573. def test_lambdify_jax(self):
  574. fl_M_pas = FiberForceLengthPassiveDeGroote2016.with_defaults(self.l_M_tilde)
  575. fl_M_pas_callable = jax.jit(lambdify(self.l_M_tilde, fl_M_pas, 'jax'))
  576. l_M_tilde = jax.numpy.array([0.5, 0.8, 0.9, 1.0, 1.1, 1.2, 1.5])
  577. expected = jax.numpy.array([
  578. -0.0179917778,
  579. -0.0137393336,
  580. -0.0090783522,
  581. 0.0,
  582. 0.0176822155,
  583. 0.0521224686,
  584. 0.5043387669,
  585. ])
  586. numpy.testing.assert_allclose(fl_M_pas_callable(l_M_tilde), expected)
  587. class TestFiberForceLengthPassiveInverseDeGroote2016:
  588. @pytest.fixture(autouse=True)
  589. def _fiber_force_length_passive_arguments_fixture(self):
  590. self.fl_M_pas = Symbol('fl_M_pas')
  591. self.c0 = Symbol('c_0')
  592. self.c1 = Symbol('c_1')
  593. self.constants = (self.c0, self.c1)
  594. @staticmethod
  595. def test_class():
  596. assert issubclass(FiberForceLengthPassiveInverseDeGroote2016, Function)
  597. assert issubclass(FiberForceLengthPassiveInverseDeGroote2016, CharacteristicCurveFunction)
  598. assert FiberForceLengthPassiveInverseDeGroote2016.__name__ == 'FiberForceLengthPassiveInverseDeGroote2016'
  599. def test_instance(self):
  600. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016(self.fl_M_pas, *self.constants)
  601. assert isinstance(fl_M_pas_inv, FiberForceLengthPassiveInverseDeGroote2016)
  602. assert str(fl_M_pas_inv) == 'FiberForceLengthPassiveInverseDeGroote2016(fl_M_pas, c_0, c_1)'
  603. def test_doit(self):
  604. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016(self.fl_M_pas, *self.constants).doit()
  605. assert fl_M_pas_inv == self.c0*log(self.fl_M_pas*(exp(self.c1) - 1) + 1)/self.c1 + 1
  606. def test_doit_evaluate_false(self):
  607. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016(self.fl_M_pas, *self.constants).doit(evaluate=False)
  608. assert fl_M_pas_inv == self.c0*log(UnevaluatedExpr(self.fl_M_pas*(exp(self.c1) - 1)) + 1)/self.c1 + 1
  609. def test_with_defaults(self):
  610. constants = (
  611. Float('0.6'),
  612. Float('4.0'),
  613. )
  614. fl_M_pas_inv_manual = FiberForceLengthPassiveInverseDeGroote2016(self.fl_M_pas, *constants)
  615. fl_M_pas_inv_constants = FiberForceLengthPassiveInverseDeGroote2016.with_defaults(self.fl_M_pas)
  616. assert fl_M_pas_inv_manual == fl_M_pas_inv_constants
  617. def test_differentiate_wrt_fl_T(self):
  618. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016(self.fl_M_pas, *self.constants)
  619. expected = self.c0*(exp(self.c1) - 1)/(self.c1*(self.fl_M_pas*(exp(self.c1) - 1) + 1))
  620. assert fl_M_pas_inv.diff(self.fl_M_pas) == expected
  621. def test_differentiate_wrt_c0(self):
  622. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016(self.fl_M_pas, *self.constants)
  623. expected = log(self.fl_M_pas*(exp(self.c1) - 1) + 1)/self.c1
  624. assert fl_M_pas_inv.diff(self.c0) == expected
  625. def test_differentiate_wrt_c1(self):
  626. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016(self.fl_M_pas, *self.constants)
  627. expected = (
  628. self.c0*self.fl_M_pas*exp(self.c1)/(self.c1*(self.fl_M_pas*(exp(self.c1) - 1) + 1))
  629. - self.c0*log(self.fl_M_pas*(exp(self.c1) - 1) + 1)/self.c1**2
  630. )
  631. assert fl_M_pas_inv.diff(self.c1) == expected
  632. def test_inverse(self):
  633. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016(self.fl_M_pas, *self.constants)
  634. assert fl_M_pas_inv.inverse() is FiberForceLengthPassiveDeGroote2016
  635. def test_function_print_latex(self):
  636. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016(self.fl_M_pas, *self.constants)
  637. expected = r'\left( \operatorname{fl}^M_{pas} \right)^{-1} \left( fl_{M pas} \right)'
  638. assert LatexPrinter().doprint(fl_M_pas_inv) == expected
  639. def test_expression_print_latex(self):
  640. fl_T = FiberForceLengthPassiveInverseDeGroote2016(self.fl_M_pas, *self.constants)
  641. expected = r'\frac{c_{0} \log{\left(fl_{M pas} \left(e^{c_{1}} - 1\right) + 1 \right)}}{c_{1}} + 1'
  642. assert LatexPrinter().doprint(fl_T.doit()) == expected
  643. @pytest.mark.parametrize(
  644. 'code_printer, expected',
  645. [
  646. (
  647. C89CodePrinter,
  648. '(1 + 0.14999999999999999*log(1 + 53.598150033144236*fl_M_pas))',
  649. ),
  650. (
  651. C99CodePrinter,
  652. '(1 + 0.14999999999999999*log(1 + 53.598150033144236*fl_M_pas))',
  653. ),
  654. (
  655. C11CodePrinter,
  656. '(1 + 0.14999999999999999*log(1 + 53.598150033144236*fl_M_pas))',
  657. ),
  658. (
  659. CXX98CodePrinter,
  660. '(1 + 0.14999999999999999*log(1 + 53.598150033144236*fl_M_pas))',
  661. ),
  662. (
  663. CXX11CodePrinter,
  664. '(1 + 0.14999999999999999*std::log(1 + 53.598150033144236*fl_M_pas))',
  665. ),
  666. (
  667. CXX17CodePrinter,
  668. '(1 + 0.14999999999999999*std::log(1 + 53.598150033144236*fl_M_pas))',
  669. ),
  670. (
  671. FCodePrinter,
  672. ' (1 + 0.15d0*log(1.0d0 + 53.5981500331442d0*fl_M_pas))',
  673. ),
  674. (
  675. OctaveCodePrinter,
  676. '(1 + 0.15*log(1 + 53.5981500331442*fl_M_pas))',
  677. ),
  678. (
  679. PythonCodePrinter,
  680. '(1 + 0.15*math.log(1 + 53.5981500331442*fl_M_pas))',
  681. ),
  682. (
  683. NumPyPrinter,
  684. '(1 + 0.15*numpy.log(1 + 53.5981500331442*fl_M_pas))',
  685. ),
  686. (
  687. SciPyPrinter,
  688. '(1 + 0.15*numpy.log(1 + 53.5981500331442*fl_M_pas))',
  689. ),
  690. (
  691. CuPyPrinter,
  692. '(1 + 0.15*cupy.log(1 + 53.5981500331442*fl_M_pas))',
  693. ),
  694. (
  695. JaxPrinter,
  696. '(1 + 0.15*jax.numpy.log(1 + 53.5981500331442*fl_M_pas))',
  697. ),
  698. (
  699. MpmathPrinter,
  700. '(1 + mpmath.mpf((0, 5404319552844595, -55, 53))*mpmath.log(1 '
  701. '+ mpmath.mpf((0, 942908627019595, -44, 50))*fl_M_pas))',
  702. ),
  703. (
  704. LambdaPrinter,
  705. '(1 + 0.15*math.log(1 + 53.5981500331442*fl_M_pas))',
  706. ),
  707. ]
  708. )
  709. def test_print_code(self, code_printer, expected):
  710. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016.with_defaults(self.fl_M_pas)
  711. assert code_printer().doprint(fl_M_pas_inv) == expected
  712. def test_derivative_print_code(self):
  713. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016.with_defaults(self.fl_M_pas)
  714. dfl_M_pas_inv_dfl_T = fl_M_pas_inv.diff(self.fl_M_pas)
  715. expected = '32.1588900198865/(214.392600132577*fl_M_pas + 4.0)'
  716. assert PythonCodePrinter().doprint(dfl_M_pas_inv_dfl_T) == expected
  717. def test_lambdify(self):
  718. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016.with_defaults(self.fl_M_pas)
  719. fl_M_pas_inv_callable = lambdify(self.fl_M_pas, fl_M_pas_inv)
  720. assert fl_M_pas_inv_callable(0.0) == pytest.approx(1.0)
  721. @pytest.mark.skipif(numpy is None, reason='NumPy not installed')
  722. def test_lambdify_numpy(self):
  723. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016.with_defaults(self.fl_M_pas)
  724. fl_M_pas_inv_callable = lambdify(self.fl_M_pas, fl_M_pas_inv, 'numpy')
  725. fl_M_pas = numpy.array([-0.01, 0.0, 0.01, 0.02, 0.05, 0.1])
  726. expected = numpy.array([
  727. 0.8848253714,
  728. 1.0,
  729. 1.0643754386,
  730. 1.1092744701,
  731. 1.1954331425,
  732. 1.2774998934,
  733. ])
  734. numpy.testing.assert_allclose(fl_M_pas_inv_callable(fl_M_pas), expected)
  735. @pytest.mark.skipif(jax is None, reason='JAX not installed')
  736. def test_lambdify_jax(self):
  737. fl_M_pas_inv = FiberForceLengthPassiveInverseDeGroote2016.with_defaults(self.fl_M_pas)
  738. fl_M_pas_inv_callable = jax.jit(lambdify(self.fl_M_pas, fl_M_pas_inv, 'jax'))
  739. fl_M_pas = jax.numpy.array([-0.01, 0.0, 0.01, 0.02, 0.05, 0.1])
  740. expected = jax.numpy.array([
  741. 0.8848253714,
  742. 1.0,
  743. 1.0643754386,
  744. 1.1092744701,
  745. 1.1954331425,
  746. 1.2774998934,
  747. ])
  748. numpy.testing.assert_allclose(fl_M_pas_inv_callable(fl_M_pas), expected)
  749. class TestFiberForceLengthActiveDeGroote2016:
  750. @pytest.fixture(autouse=True)
  751. def _fiber_force_length_active_arguments_fixture(self):
  752. self.l_M_tilde = Symbol('l_M_tilde')
  753. self.c0 = Symbol('c_0')
  754. self.c1 = Symbol('c_1')
  755. self.c2 = Symbol('c_2')
  756. self.c3 = Symbol('c_3')
  757. self.c4 = Symbol('c_4')
  758. self.c5 = Symbol('c_5')
  759. self.c6 = Symbol('c_6')
  760. self.c7 = Symbol('c_7')
  761. self.c8 = Symbol('c_8')
  762. self.c9 = Symbol('c_9')
  763. self.c10 = Symbol('c_10')
  764. self.c11 = Symbol('c_11')
  765. self.constants = (
  766. self.c0, self.c1, self.c2, self.c3, self.c4, self.c5,
  767. self.c6, self.c7, self.c8, self.c9, self.c10, self.c11,
  768. )
  769. @staticmethod
  770. def test_class():
  771. assert issubclass(FiberForceLengthActiveDeGroote2016, Function)
  772. assert issubclass(FiberForceLengthActiveDeGroote2016, CharacteristicCurveFunction)
  773. assert FiberForceLengthActiveDeGroote2016.__name__ == 'FiberForceLengthActiveDeGroote2016'
  774. def test_instance(self):
  775. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  776. assert isinstance(fl_M_act, FiberForceLengthActiveDeGroote2016)
  777. assert str(fl_M_act) == (
  778. 'FiberForceLengthActiveDeGroote2016(l_M_tilde, c_0, c_1, c_2, c_3, '
  779. 'c_4, c_5, c_6, c_7, c_8, c_9, c_10, c_11)'
  780. )
  781. def test_doit(self):
  782. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants).doit()
  783. assert fl_M_act == (
  784. self.c0*exp(-(((self.l_M_tilde - self.c1)/(self.c2 + self.c3*self.l_M_tilde))**2)/2)
  785. + self.c4*exp(-(((self.l_M_tilde - self.c5)/(self.c6 + self.c7*self.l_M_tilde))**2)/2)
  786. + self.c8*exp(-(((self.l_M_tilde - self.c9)/(self.c10 + self.c11*self.l_M_tilde))**2)/2)
  787. )
  788. def test_doit_evaluate_false(self):
  789. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants).doit(evaluate=False)
  790. assert fl_M_act == (
  791. self.c0*exp(-((UnevaluatedExpr(self.l_M_tilde - self.c1)/(self.c2 + self.c3*self.l_M_tilde))**2)/2)
  792. + self.c4*exp(-((UnevaluatedExpr(self.l_M_tilde - self.c5)/(self.c6 + self.c7*self.l_M_tilde))**2)/2)
  793. + self.c8*exp(-((UnevaluatedExpr(self.l_M_tilde - self.c9)/(self.c10 + self.c11*self.l_M_tilde))**2)/2)
  794. )
  795. def test_with_defaults(self):
  796. constants = (
  797. Float('0.814'),
  798. Float('1.06'),
  799. Float('0.162'),
  800. Float('0.0633'),
  801. Float('0.433'),
  802. Float('0.717'),
  803. Float('-0.0299'),
  804. Float('0.2'),
  805. Float('0.1'),
  806. Float('1.0'),
  807. Float('0.354'),
  808. Float('0.0'),
  809. )
  810. fl_M_act_manual = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *constants)
  811. fl_M_act_constants = FiberForceLengthActiveDeGroote2016.with_defaults(self.l_M_tilde)
  812. assert fl_M_act_manual == fl_M_act_constants
  813. def test_differentiate_wrt_l_M_tilde(self):
  814. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  815. expected = (
  816. self.c0*(
  817. self.c3*(self.l_M_tilde - self.c1)**2/(self.c2 + self.c3*self.l_M_tilde)**3
  818. + (self.c1 - self.l_M_tilde)/((self.c2 + self.c3*self.l_M_tilde)**2)
  819. )*exp(-(self.l_M_tilde - self.c1)**2/(2*(self.c2 + self.c3*self.l_M_tilde)**2))
  820. + self.c4*(
  821. self.c7*(self.l_M_tilde - self.c5)**2/(self.c6 + self.c7*self.l_M_tilde)**3
  822. + (self.c5 - self.l_M_tilde)/((self.c6 + self.c7*self.l_M_tilde)**2)
  823. )*exp(-(self.l_M_tilde - self.c5)**2/(2*(self.c6 + self.c7*self.l_M_tilde)**2))
  824. + self.c8*(
  825. self.c11*(self.l_M_tilde - self.c9)**2/(self.c10 + self.c11*self.l_M_tilde)**3
  826. + (self.c9 - self.l_M_tilde)/((self.c10 + self.c11*self.l_M_tilde)**2)
  827. )*exp(-(self.l_M_tilde - self.c9)**2/(2*(self.c10 + self.c11*self.l_M_tilde)**2))
  828. )
  829. assert fl_M_act.diff(self.l_M_tilde) == expected
  830. def test_differentiate_wrt_c0(self):
  831. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  832. expected = exp(-(self.l_M_tilde - self.c1)**2/(2*(self.c2 + self.c3*self.l_M_tilde)**2))
  833. assert fl_M_act.doit().diff(self.c0) == expected
  834. def test_differentiate_wrt_c1(self):
  835. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  836. expected = (
  837. self.c0*(self.l_M_tilde - self.c1)/(self.c2 + self.c3*self.l_M_tilde)**2
  838. *exp(-(self.l_M_tilde - self.c1)**2/(2*(self.c2 + self.c3*self.l_M_tilde)**2))
  839. )
  840. assert fl_M_act.diff(self.c1) == expected
  841. def test_differentiate_wrt_c2(self):
  842. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  843. expected = (
  844. self.c0*(self.l_M_tilde - self.c1)**2/(self.c2 + self.c3*self.l_M_tilde)**3
  845. *exp(-(self.l_M_tilde - self.c1)**2/(2*(self.c2 + self.c3*self.l_M_tilde)**2))
  846. )
  847. assert fl_M_act.diff(self.c2) == expected
  848. def test_differentiate_wrt_c3(self):
  849. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  850. expected = (
  851. self.c0*self.l_M_tilde*(self.l_M_tilde - self.c1)**2/(self.c2 + self.c3*self.l_M_tilde)**3
  852. *exp(-(self.l_M_tilde - self.c1)**2/(2*(self.c2 + self.c3*self.l_M_tilde)**2))
  853. )
  854. assert fl_M_act.diff(self.c3) == expected
  855. def test_differentiate_wrt_c4(self):
  856. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  857. expected = exp(-(self.l_M_tilde - self.c5)**2/(2*(self.c6 + self.c7*self.l_M_tilde)**2))
  858. assert fl_M_act.diff(self.c4) == expected
  859. def test_differentiate_wrt_c5(self):
  860. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  861. expected = (
  862. self.c4*(self.l_M_tilde - self.c5)/(self.c6 + self.c7*self.l_M_tilde)**2
  863. *exp(-(self.l_M_tilde - self.c5)**2/(2*(self.c6 + self.c7*self.l_M_tilde)**2))
  864. )
  865. assert fl_M_act.diff(self.c5) == expected
  866. def test_differentiate_wrt_c6(self):
  867. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  868. expected = (
  869. self.c4*(self.l_M_tilde - self.c5)**2/(self.c6 + self.c7*self.l_M_tilde)**3
  870. *exp(-(self.l_M_tilde - self.c5)**2/(2*(self.c6 + self.c7*self.l_M_tilde)**2))
  871. )
  872. assert fl_M_act.diff(self.c6) == expected
  873. def test_differentiate_wrt_c7(self):
  874. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  875. expected = (
  876. self.c4*self.l_M_tilde*(self.l_M_tilde - self.c5)**2/(self.c6 + self.c7*self.l_M_tilde)**3
  877. *exp(-(self.l_M_tilde - self.c5)**2/(2*(self.c6 + self.c7*self.l_M_tilde)**2))
  878. )
  879. assert fl_M_act.diff(self.c7) == expected
  880. def test_differentiate_wrt_c8(self):
  881. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  882. expected = exp(-(self.l_M_tilde - self.c9)**2/(2*(self.c10 + self.c11*self.l_M_tilde)**2))
  883. assert fl_M_act.diff(self.c8) == expected
  884. def test_differentiate_wrt_c9(self):
  885. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  886. expected = (
  887. self.c8*(self.l_M_tilde - self.c9)/(self.c10 + self.c11*self.l_M_tilde)**2
  888. *exp(-(self.l_M_tilde - self.c9)**2/(2*(self.c10 + self.c11*self.l_M_tilde)**2))
  889. )
  890. assert fl_M_act.diff(self.c9) == expected
  891. def test_differentiate_wrt_c10(self):
  892. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  893. expected = (
  894. self.c8*(self.l_M_tilde - self.c9)**2/(self.c10 + self.c11*self.l_M_tilde)**3
  895. *exp(-(self.l_M_tilde - self.c9)**2/(2*(self.c10 + self.c11*self.l_M_tilde)**2))
  896. )
  897. assert fl_M_act.diff(self.c10) == expected
  898. def test_differentiate_wrt_c11(self):
  899. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  900. expected = (
  901. self.c8*self.l_M_tilde*(self.l_M_tilde - self.c9)**2/(self.c10 + self.c11*self.l_M_tilde)**3
  902. *exp(-(self.l_M_tilde - self.c9)**2/(2*(self.c10 + self.c11*self.l_M_tilde)**2))
  903. )
  904. assert fl_M_act.diff(self.c11) == expected
  905. def test_function_print_latex(self):
  906. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  907. expected = r'\operatorname{fl}^M_{act} \left( l_{M tilde} \right)'
  908. assert LatexPrinter().doprint(fl_M_act) == expected
  909. def test_expression_print_latex(self):
  910. fl_M_act = FiberForceLengthActiveDeGroote2016(self.l_M_tilde, *self.constants)
  911. expected = (
  912. r'c_{0} e^{- \frac{\left(- c_{1} + l_{M tilde}\right)^{2}}{2 \left(c_{2} + c_{3} l_{M tilde}\right)^{2}}} '
  913. r'+ c_{4} e^{- \frac{\left(- c_{5} + l_{M tilde}\right)^{2}}{2 \left(c_{6} + c_{7} l_{M tilde}\right)^{2}}} '
  914. r'+ c_{8} e^{- \frac{\left(- c_{9} + l_{M tilde}\right)^{2}}{2 \left(c_{10} + c_{11} l_{M tilde}\right)^{2}}}'
  915. )
  916. assert LatexPrinter().doprint(fl_M_act.doit()) == expected
  917. @pytest.mark.parametrize(
  918. 'code_printer, expected',
  919. [
  920. (
  921. C89CodePrinter,
  922. (
  923. '(0.81399999999999995*exp(-1.0/2.0*pow(l_M_tilde - 1.0600000000000001, 2)/pow(0.063299999999999995*l_M_tilde + 0.16200000000000001, 2)) + 0.433*exp(-1.0/2.0*pow(l_M_tilde - 0.71699999999999997, 2)/pow(0.20000000000000001*l_M_tilde - 0.029899999999999999, 2)) + 0.10000000000000001*exp(-3.9899134986753491*pow(l_M_tilde - 1.0, 2)))'
  924. ),
  925. ),
  926. (
  927. C99CodePrinter,
  928. (
  929. '(0.81399999999999995*exp(-1.0/2.0*pow(l_M_tilde - 1.0600000000000001, 2)/pow(0.063299999999999995*l_M_tilde + 0.16200000000000001, 2)) + 0.433*exp(-1.0/2.0*pow(l_M_tilde - 0.71699999999999997, 2)/pow(0.20000000000000001*l_M_tilde - 0.029899999999999999, 2)) + 0.10000000000000001*exp(-3.9899134986753491*pow(l_M_tilde - 1.0, 2)))'
  930. ),
  931. ),
  932. (
  933. C11CodePrinter,
  934. (
  935. '(0.81399999999999995*exp(-1.0/2.0*pow(l_M_tilde - 1.0600000000000001, 2)/pow(0.063299999999999995*l_M_tilde + 0.16200000000000001, 2)) + 0.433*exp(-1.0/2.0*pow(l_M_tilde - 0.71699999999999997, 2)/pow(0.20000000000000001*l_M_tilde - 0.029899999999999999, 2)) + 0.10000000000000001*exp(-3.9899134986753491*pow(l_M_tilde - 1.0, 2)))'
  936. ),
  937. ),
  938. (
  939. CXX98CodePrinter,
  940. (
  941. '(0.81399999999999995*exp(-1.0/2.0*std::pow(l_M_tilde - 1.0600000000000001, 2)/std::pow(0.063299999999999995*l_M_tilde + 0.16200000000000001, 2)) + 0.433*exp(-1.0/2.0*std::pow(l_M_tilde - 0.71699999999999997, 2)/std::pow(0.20000000000000001*l_M_tilde - 0.029899999999999999, 2)) + 0.10000000000000001*exp(-3.9899134986753491*std::pow(l_M_tilde - 1.0, 2)))'
  942. ),
  943. ),
  944. (
  945. CXX11CodePrinter,
  946. (
  947. '(0.81399999999999995*std::exp(-1.0/2.0*std::pow(l_M_tilde - 1.0600000000000001, 2)/std::pow(0.063299999999999995*l_M_tilde + 0.16200000000000001, 2)) + 0.433*std::exp(-1.0/2.0*std::pow(l_M_tilde - 0.71699999999999997, 2)/std::pow(0.20000000000000001*l_M_tilde - 0.029899999999999999, 2)) + 0.10000000000000001*std::exp(-3.9899134986753491*std::pow(l_M_tilde - 1.0, 2)))'
  948. ),
  949. ),
  950. (
  951. CXX17CodePrinter,
  952. (
  953. '(0.81399999999999995*std::exp(-1.0/2.0*std::pow(l_M_tilde - 1.0600000000000001, 2)/std::pow(0.063299999999999995*l_M_tilde + 0.16200000000000001, 2)) + 0.433*std::exp(-1.0/2.0*std::pow(l_M_tilde - 0.71699999999999997, 2)/std::pow(0.20000000000000001*l_M_tilde - 0.029899999999999999, 2)) + 0.10000000000000001*std::exp(-3.9899134986753491*std::pow(l_M_tilde - 1.0, 2)))'
  954. ),
  955. ),
  956. (
  957. FCodePrinter,
  958. (
  959. ' (0.814d0*exp(-0.5d0*(l_M_tilde - 1.06d0)**2/(\n'
  960. ' @ 0.063299999999999995d0*l_M_tilde + 0.16200000000000001d0)**2) +\n'
  961. ' @ 0.433d0*exp(-0.5d0*(l_M_tilde - 0.717d0)**2/(\n'
  962. ' @ 0.20000000000000001d0*l_M_tilde - 0.029899999999999999d0)**2) +\n'
  963. ' @ 0.1d0*exp(-3.9899134986753491d0*(l_M_tilde - 1.0d0)**2))'
  964. ),
  965. ),
  966. (
  967. OctaveCodePrinter,
  968. (
  969. '(0.814*exp(-(l_M_tilde - 1.06).^2./(2*(0.0633*l_M_tilde + 0.162).^2)) + 0.433*exp(-(l_M_tilde - 0.717).^2./(2*(0.2*l_M_tilde - 0.0299).^2)) + 0.1*exp(-3.98991349867535*(l_M_tilde - 1.0).^2))'
  970. ),
  971. ),
  972. (
  973. PythonCodePrinter,
  974. (
  975. '(0.814*math.exp(-1/2*(l_M_tilde - 1.06)**2/(0.0633*l_M_tilde + 0.162)**2) + 0.433*math.exp(-1/2*(l_M_tilde - 0.717)**2/(0.2*l_M_tilde - 0.0299)**2) + 0.1*math.exp(-3.98991349867535*(l_M_tilde - 1.0)**2))'
  976. ),
  977. ),
  978. (
  979. NumPyPrinter,
  980. (
  981. '(0.814*numpy.exp(-1/2*(l_M_tilde - 1.06)**2/(0.0633*l_M_tilde + 0.162)**2) + 0.433*numpy.exp(-1/2*(l_M_tilde - 0.717)**2/(0.2*l_M_tilde - 0.0299)**2) + 0.1*numpy.exp(-3.98991349867535*(l_M_tilde - 1.0)**2))'
  982. ),
  983. ),
  984. (
  985. SciPyPrinter,
  986. (
  987. '(0.814*numpy.exp(-1/2*(l_M_tilde - 1.06)**2/(0.0633*l_M_tilde + 0.162)**2) + 0.433*numpy.exp(-1/2*(l_M_tilde - 0.717)**2/(0.2*l_M_tilde - 0.0299)**2) + 0.1*numpy.exp(-3.98991349867535*(l_M_tilde - 1.0)**2))'
  988. ),
  989. ),
  990. (
  991. CuPyPrinter,
  992. (
  993. '(0.814*cupy.exp(-1/2*(l_M_tilde - 1.06)**2/(0.0633*l_M_tilde + 0.162)**2) + 0.433*cupy.exp(-1/2*(l_M_tilde - 0.717)**2/(0.2*l_M_tilde - 0.0299)**2) + 0.1*cupy.exp(-3.98991349867535*(l_M_tilde - 1.0)**2))'
  994. ),
  995. ),
  996. (
  997. JaxPrinter,
  998. (
  999. '(0.814*jax.numpy.exp(-1/2*(l_M_tilde - 1.06)**2/(0.0633*l_M_tilde + 0.162)**2) + 0.433*jax.numpy.exp(-1/2*(l_M_tilde - 0.717)**2/(0.2*l_M_tilde - 0.0299)**2) + 0.1*jax.numpy.exp(-3.98991349867535*(l_M_tilde - 1.0)**2))'
  1000. ),
  1001. ),
  1002. (
  1003. MpmathPrinter,
  1004. (
  1005. '(mpmath.mpf((0, 7331860193359167, -53, 53))*mpmath.exp(-mpmath.mpf(1)/mpmath.mpf(2)*(l_M_tilde + mpmath.mpf((1, 2386907802506363, -51, 52)))**2/(mpmath.mpf((0, 2280622851300419, -55, 52))*l_M_tilde + mpmath.mpf((0, 5836665117072163, -55, 53)))**2) + mpmath.mpf((0, 7800234554605699, -54, 53))*mpmath.exp(-mpmath.mpf(1)/mpmath.mpf(2)*(l_M_tilde + mpmath.mpf((1, 6458161865649291, -53, 53)))**2/(mpmath.mpf((0, 3602879701896397, -54, 52))*l_M_tilde + mpmath.mpf((1, 8618088246936181, -58, 53)))**2) + mpmath.mpf((0, 3602879701896397, -55, 52))*mpmath.exp(-mpmath.mpf((0, 8984486472937407, -51, 53))*(l_M_tilde + mpmath.mpf((1, 1, 0, 1)))**2))'
  1006. ),
  1007. ),
  1008. (
  1009. LambdaPrinter,
  1010. (
  1011. '(0.814*math.exp(-1/2*(l_M_tilde - 1.06)**2/(0.0633*l_M_tilde + 0.162)**2) + 0.433*math.exp(-1/2*(l_M_tilde - 0.717)**2/(0.2*l_M_tilde - 0.0299)**2) + 0.1*math.exp(-3.98991349867535*(l_M_tilde - 1.0)**2))'
  1012. ),
  1013. ),
  1014. ]
  1015. )
  1016. def test_print_code(self, code_printer, expected):
  1017. fl_M_act = FiberForceLengthActiveDeGroote2016.with_defaults(self.l_M_tilde)
  1018. assert code_printer().doprint(fl_M_act) == expected
  1019. def test_derivative_print_code(self):
  1020. fl_M_act = FiberForceLengthActiveDeGroote2016.with_defaults(self.l_M_tilde)
  1021. fl_M_act_dl_M_tilde = fl_M_act.diff(self.l_M_tilde)
  1022. expected = (
  1023. '(0.79798269973507 - 0.79798269973507*l_M_tilde)*math.exp(-3.98991349867535*(l_M_tilde - 1.0)**2) + (0.433*(0.717 - l_M_tilde)/(0.2*l_M_tilde - 0.0299)**2 + 0.0866*(l_M_tilde - 0.717)**2/(0.2*l_M_tilde - 0.0299)**3)*math.exp(-1/2*(l_M_tilde - 0.717)**2/(0.2*l_M_tilde - 0.0299)**2) + (0.814*(1.06 - l_M_tilde)/(0.0633*l_M_tilde + 0.162)**2 + 0.0515262*(l_M_tilde - 1.06)**2/(0.0633*l_M_tilde + 0.162)**3)*math.exp(-1/2*(l_M_tilde - 1.06)**2/(0.0633*l_M_tilde + 0.162)**2)'
  1024. )
  1025. assert PythonCodePrinter().doprint(fl_M_act_dl_M_tilde) == expected
  1026. def test_lambdify(self):
  1027. fl_M_act = FiberForceLengthActiveDeGroote2016.with_defaults(self.l_M_tilde)
  1028. fl_M_act_callable = lambdify(self.l_M_tilde, fl_M_act)
  1029. assert fl_M_act_callable(1.0) == pytest.approx(0.9941398866)
  1030. @pytest.mark.skipif(numpy is None, reason='NumPy not installed')
  1031. def test_lambdify_numpy(self):
  1032. fl_M_act = FiberForceLengthActiveDeGroote2016.with_defaults(self.l_M_tilde)
  1033. fl_M_act_callable = lambdify(self.l_M_tilde, fl_M_act, 'numpy')
  1034. l_M_tilde = numpy.array([0.0, 0.5, 1.0, 1.5, 2.0])
  1035. expected = numpy.array([
  1036. 0.0018501319,
  1037. 0.0529122812,
  1038. 0.9941398866,
  1039. 0.2312431531,
  1040. 0.0069595432,
  1041. ])
  1042. numpy.testing.assert_allclose(fl_M_act_callable(l_M_tilde), expected)
  1043. @pytest.mark.skipif(jax is None, reason='JAX not installed')
  1044. def test_lambdify_jax(self):
  1045. fl_M_act = FiberForceLengthActiveDeGroote2016.with_defaults(self.l_M_tilde)
  1046. fl_M_act_callable = jax.jit(lambdify(self.l_M_tilde, fl_M_act, 'jax'))
  1047. l_M_tilde = jax.numpy.array([0.0, 0.5, 1.0, 1.5, 2.0])
  1048. expected = jax.numpy.array([
  1049. 0.0018501319,
  1050. 0.0529122812,
  1051. 0.9941398866,
  1052. 0.2312431531,
  1053. 0.0069595432,
  1054. ])
  1055. numpy.testing.assert_allclose(fl_M_act_callable(l_M_tilde), expected)
  1056. class TestFiberForceVelocityDeGroote2016:
  1057. @pytest.fixture(autouse=True)
  1058. def _muscle_fiber_force_velocity_arguments_fixture(self):
  1059. self.v_M_tilde = Symbol('v_M_tilde')
  1060. self.c0 = Symbol('c_0')
  1061. self.c1 = Symbol('c_1')
  1062. self.c2 = Symbol('c_2')
  1063. self.c3 = Symbol('c_3')
  1064. self.constants = (self.c0, self.c1, self.c2, self.c3)
  1065. @staticmethod
  1066. def test_class():
  1067. assert issubclass(FiberForceVelocityDeGroote2016, Function)
  1068. assert issubclass(FiberForceVelocityDeGroote2016, CharacteristicCurveFunction)
  1069. assert FiberForceVelocityDeGroote2016.__name__ == 'FiberForceVelocityDeGroote2016'
  1070. def test_instance(self):
  1071. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants)
  1072. assert isinstance(fv_M, FiberForceVelocityDeGroote2016)
  1073. assert str(fv_M) == 'FiberForceVelocityDeGroote2016(v_M_tilde, c_0, c_1, c_2, c_3)'
  1074. def test_doit(self):
  1075. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants).doit()
  1076. expected = (
  1077. self.c0 * log((self.c1 * self.v_M_tilde + self.c2)
  1078. + sqrt((self.c1 * self.v_M_tilde + self.c2)**2 + 1)) + self.c3
  1079. )
  1080. assert fv_M == expected
  1081. def test_doit_evaluate_false(self):
  1082. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants).doit(evaluate=False)
  1083. expected = (
  1084. self.c0 * log((self.c1 * self.v_M_tilde + self.c2)
  1085. + sqrt(UnevaluatedExpr(self.c1 * self.v_M_tilde + self.c2)**2 + 1)) + self.c3
  1086. )
  1087. assert fv_M == expected
  1088. def test_with_defaults(self):
  1089. constants = (
  1090. Float('-0.318'),
  1091. Float('-8.149'),
  1092. Float('-0.374'),
  1093. Float('0.886'),
  1094. )
  1095. fv_M_manual = FiberForceVelocityDeGroote2016(self.v_M_tilde, *constants)
  1096. fv_M_constants = FiberForceVelocityDeGroote2016.with_defaults(self.v_M_tilde)
  1097. assert fv_M_manual == fv_M_constants
  1098. def test_differentiate_wrt_v_M_tilde(self):
  1099. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants)
  1100. expected = (
  1101. self.c0*self.c1
  1102. /sqrt(UnevaluatedExpr(self.c1*self.v_M_tilde + self.c2)**2 + 1)
  1103. )
  1104. assert fv_M.diff(self.v_M_tilde) == expected
  1105. def test_differentiate_wrt_c0(self):
  1106. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants)
  1107. expected = log(
  1108. self.c1*self.v_M_tilde + self.c2
  1109. + sqrt(UnevaluatedExpr(self.c1*self.v_M_tilde + self.c2)**2 + 1)
  1110. )
  1111. assert fv_M.diff(self.c0) == expected
  1112. def test_differentiate_wrt_c1(self):
  1113. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants)
  1114. expected = (
  1115. self.c0*self.v_M_tilde
  1116. /sqrt(UnevaluatedExpr(self.c1*self.v_M_tilde + self.c2)**2 + 1)
  1117. )
  1118. assert fv_M.diff(self.c1) == expected
  1119. def test_differentiate_wrt_c2(self):
  1120. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants)
  1121. expected = (
  1122. self.c0
  1123. /sqrt(UnevaluatedExpr(self.c1*self.v_M_tilde + self.c2)**2 + 1)
  1124. )
  1125. assert fv_M.diff(self.c2) == expected
  1126. def test_differentiate_wrt_c3(self):
  1127. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants)
  1128. expected = Integer(1)
  1129. assert fv_M.diff(self.c3) == expected
  1130. def test_inverse(self):
  1131. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants)
  1132. assert fv_M.inverse() is FiberForceVelocityInverseDeGroote2016
  1133. def test_function_print_latex(self):
  1134. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants)
  1135. expected = r'\operatorname{fv}^M \left( v_{M tilde} \right)'
  1136. assert LatexPrinter().doprint(fv_M) == expected
  1137. def test_expression_print_latex(self):
  1138. fv_M = FiberForceVelocityDeGroote2016(self.v_M_tilde, *self.constants)
  1139. expected = (
  1140. r'c_{0} \log{\left(c_{1} v_{M tilde} + c_{2} + \sqrt{\left(c_{1} '
  1141. r'v_{M tilde} + c_{2}\right)^{2} + 1} \right)} + c_{3}'
  1142. )
  1143. assert LatexPrinter().doprint(fv_M.doit()) == expected
  1144. @pytest.mark.parametrize(
  1145. 'code_printer, expected',
  1146. [
  1147. (
  1148. C89CodePrinter,
  1149. '(0.88600000000000001 - 0.318*log(-8.1489999999999991*v_M_tilde '
  1150. '- 0.374 + sqrt(1 + pow(-8.1489999999999991*v_M_tilde - 0.374, 2))))',
  1151. ),
  1152. (
  1153. C99CodePrinter,
  1154. '(0.88600000000000001 - 0.318*log(-8.1489999999999991*v_M_tilde '
  1155. '- 0.374 + sqrt(1 + pow(-8.1489999999999991*v_M_tilde - 0.374, 2))))',
  1156. ),
  1157. (
  1158. C11CodePrinter,
  1159. '(0.88600000000000001 - 0.318*log(-8.1489999999999991*v_M_tilde '
  1160. '- 0.374 + sqrt(1 + pow(-8.1489999999999991*v_M_tilde - 0.374, 2))))',
  1161. ),
  1162. (
  1163. CXX98CodePrinter,
  1164. '(0.88600000000000001 - 0.318*log(-8.1489999999999991*v_M_tilde '
  1165. '- 0.374 + std::sqrt(1 + std::pow(-8.1489999999999991*v_M_tilde - 0.374, 2))))',
  1166. ),
  1167. (
  1168. CXX11CodePrinter,
  1169. '(0.88600000000000001 - 0.318*std::log(-8.1489999999999991*v_M_tilde '
  1170. '- 0.374 + std::sqrt(1 + std::pow(-8.1489999999999991*v_M_tilde - 0.374, 2))))',
  1171. ),
  1172. (
  1173. CXX17CodePrinter,
  1174. '(0.88600000000000001 - 0.318*std::log(-8.1489999999999991*v_M_tilde '
  1175. '- 0.374 + std::sqrt(1 + std::pow(-8.1489999999999991*v_M_tilde - 0.374, 2))))',
  1176. ),
  1177. (
  1178. FCodePrinter,
  1179. ' (0.886d0 - 0.318d0*log(-8.1489999999999991d0*v_M_tilde - 0.374d0 +\n'
  1180. ' @ sqrt(1.0d0 + (-8.149d0*v_M_tilde - 0.374d0)**2)))',
  1181. ),
  1182. (
  1183. OctaveCodePrinter,
  1184. '(0.886 - 0.318*log(-8.149*v_M_tilde - 0.374 '
  1185. '+ sqrt(1 + (-8.149*v_M_tilde - 0.374).^2)))',
  1186. ),
  1187. (
  1188. PythonCodePrinter,
  1189. '(0.886 - 0.318*math.log(-8.149*v_M_tilde - 0.374 '
  1190. '+ math.sqrt(1 + (-8.149*v_M_tilde - 0.374)**2)))',
  1191. ),
  1192. (
  1193. NumPyPrinter,
  1194. '(0.886 - 0.318*numpy.log(-8.149*v_M_tilde - 0.374 '
  1195. '+ numpy.sqrt(1 + (-8.149*v_M_tilde - 0.374)**2)))',
  1196. ),
  1197. (
  1198. SciPyPrinter,
  1199. '(0.886 - 0.318*numpy.log(-8.149*v_M_tilde - 0.374 '
  1200. '+ numpy.sqrt(1 + (-8.149*v_M_tilde - 0.374)**2)))',
  1201. ),
  1202. (
  1203. CuPyPrinter,
  1204. '(0.886 - 0.318*cupy.log(-8.149*v_M_tilde - 0.374 '
  1205. '+ cupy.sqrt(1 + (-8.149*v_M_tilde - 0.374)**2)))',
  1206. ),
  1207. (
  1208. JaxPrinter,
  1209. '(0.886 - 0.318*jax.numpy.log(-8.149*v_M_tilde - 0.374 '
  1210. '+ jax.numpy.sqrt(1 + (-8.149*v_M_tilde - 0.374)**2)))',
  1211. ),
  1212. (
  1213. MpmathPrinter,
  1214. '(mpmath.mpf((0, 7980378539700519, -53, 53)) '
  1215. '- mpmath.mpf((0, 5728578726015271, -54, 53))'
  1216. '*mpmath.log(-mpmath.mpf((0, 4587479170430271, -49, 53))*v_M_tilde '
  1217. '+ mpmath.mpf((1, 3368692521273131, -53, 52)) '
  1218. '+ mpmath.sqrt(1 + (-mpmath.mpf((0, 4587479170430271, -49, 53))*v_M_tilde '
  1219. '+ mpmath.mpf((1, 3368692521273131, -53, 52)))**2)))',
  1220. ),
  1221. (
  1222. LambdaPrinter,
  1223. '(0.886 - 0.318*math.log(-8.149*v_M_tilde - 0.374 '
  1224. '+ sqrt(1 + (-8.149*v_M_tilde - 0.374)**2)))',
  1225. ),
  1226. ]
  1227. )
  1228. def test_print_code(self, code_printer, expected):
  1229. fv_M = FiberForceVelocityDeGroote2016.with_defaults(self.v_M_tilde)
  1230. assert code_printer().doprint(fv_M) == expected
  1231. def test_derivative_print_code(self):
  1232. fv_M = FiberForceVelocityDeGroote2016.with_defaults(self.v_M_tilde)
  1233. dfv_M_dv_M_tilde = fv_M.diff(self.v_M_tilde)
  1234. expected = '2.591382*(1 + (-8.149*v_M_tilde - 0.374)**2)**(-1/2)'
  1235. assert PythonCodePrinter().doprint(dfv_M_dv_M_tilde) == expected
  1236. def test_lambdify(self):
  1237. fv_M = FiberForceVelocityDeGroote2016.with_defaults(self.v_M_tilde)
  1238. fv_M_callable = lambdify(self.v_M_tilde, fv_M)
  1239. assert fv_M_callable(0.0) == pytest.approx(1.002320622548512)
  1240. @pytest.mark.skipif(numpy is None, reason='NumPy not installed')
  1241. def test_lambdify_numpy(self):
  1242. fv_M = FiberForceVelocityDeGroote2016.with_defaults(self.v_M_tilde)
  1243. fv_M_callable = lambdify(self.v_M_tilde, fv_M, 'numpy')
  1244. v_M_tilde = numpy.array([-1.0, -0.5, 0.0, 0.5])
  1245. expected = numpy.array([
  1246. 0.0120816781,
  1247. 0.2438336294,
  1248. 1.0023206225,
  1249. 1.5850003903,
  1250. ])
  1251. numpy.testing.assert_allclose(fv_M_callable(v_M_tilde), expected)
  1252. @pytest.mark.skipif(jax is None, reason='JAX not installed')
  1253. def test_lambdify_jax(self):
  1254. fv_M = FiberForceVelocityDeGroote2016.with_defaults(self.v_M_tilde)
  1255. fv_M_callable = jax.jit(lambdify(self.v_M_tilde, fv_M, 'jax'))
  1256. v_M_tilde = jax.numpy.array([-1.0, -0.5, 0.0, 0.5])
  1257. expected = jax.numpy.array([
  1258. 0.0120816781,
  1259. 0.2438336294,
  1260. 1.0023206225,
  1261. 1.5850003903,
  1262. ])
  1263. numpy.testing.assert_allclose(fv_M_callable(v_M_tilde), expected)
  1264. class TestFiberForceVelocityInverseDeGroote2016:
  1265. @pytest.fixture(autouse=True)
  1266. def _tendon_force_length_inverse_arguments_fixture(self):
  1267. self.fv_M = Symbol('fv_M')
  1268. self.c0 = Symbol('c_0')
  1269. self.c1 = Symbol('c_1')
  1270. self.c2 = Symbol('c_2')
  1271. self.c3 = Symbol('c_3')
  1272. self.constants = (self.c0, self.c1, self.c2, self.c3)
  1273. @staticmethod
  1274. def test_class():
  1275. assert issubclass(FiberForceVelocityInverseDeGroote2016, Function)
  1276. assert issubclass(FiberForceVelocityInverseDeGroote2016, CharacteristicCurveFunction)
  1277. assert FiberForceVelocityInverseDeGroote2016.__name__ == 'FiberForceVelocityInverseDeGroote2016'
  1278. def test_instance(self):
  1279. fv_M_inv = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants)
  1280. assert isinstance(fv_M_inv, FiberForceVelocityInverseDeGroote2016)
  1281. assert str(fv_M_inv) == 'FiberForceVelocityInverseDeGroote2016(fv_M, c_0, c_1, c_2, c_3)'
  1282. def test_doit(self):
  1283. fv_M_inv = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants).doit()
  1284. assert fv_M_inv == (sinh((self.fv_M - self.c3)/self.c0) - self.c2)/self.c1
  1285. def test_doit_evaluate_false(self):
  1286. fv_M_inv = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants).doit(evaluate=False)
  1287. assert fv_M_inv == (sinh(UnevaluatedExpr(self.fv_M - self.c3)/self.c0) - self.c2)/self.c1
  1288. def test_with_defaults(self):
  1289. constants = (
  1290. Float('-0.318'),
  1291. Float('-8.149'),
  1292. Float('-0.374'),
  1293. Float('0.886'),
  1294. )
  1295. fv_M_inv_manual = FiberForceVelocityInverseDeGroote2016(self.fv_M, *constants)
  1296. fv_M_inv_constants = FiberForceVelocityInverseDeGroote2016.with_defaults(self.fv_M)
  1297. assert fv_M_inv_manual == fv_M_inv_constants
  1298. def test_differentiate_wrt_fv_M(self):
  1299. fv_M_inv = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants)
  1300. expected = cosh((self.fv_M - self.c3)/self.c0)/(self.c0*self.c1)
  1301. assert fv_M_inv.diff(self.fv_M) == expected
  1302. def test_differentiate_wrt_c0(self):
  1303. fv_M_inv = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants)
  1304. expected = (self.c3 - self.fv_M)*cosh((self.fv_M - self.c3)/self.c0)/(self.c0**2*self.c1)
  1305. assert fv_M_inv.diff(self.c0) == expected
  1306. def test_differentiate_wrt_c1(self):
  1307. fv_M_inv = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants)
  1308. expected = (self.c2 - sinh((self.fv_M - self.c3)/self.c0))/self.c1**2
  1309. assert fv_M_inv.diff(self.c1) == expected
  1310. def test_differentiate_wrt_c2(self):
  1311. fv_M_inv = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants)
  1312. expected = -1/self.c1
  1313. assert fv_M_inv.diff(self.c2) == expected
  1314. def test_differentiate_wrt_c3(self):
  1315. fv_M_inv = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants)
  1316. expected = -cosh((self.fv_M - self.c3)/self.c0)/(self.c0*self.c1)
  1317. assert fv_M_inv.diff(self.c3) == expected
  1318. def test_inverse(self):
  1319. fv_M_inv = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants)
  1320. assert fv_M_inv.inverse() is FiberForceVelocityDeGroote2016
  1321. def test_function_print_latex(self):
  1322. fv_M_inv = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants)
  1323. expected = r'\left( \operatorname{fv}^M \right)^{-1} \left( fv_{M} \right)'
  1324. assert LatexPrinter().doprint(fv_M_inv) == expected
  1325. def test_expression_print_latex(self):
  1326. fv_M = FiberForceVelocityInverseDeGroote2016(self.fv_M, *self.constants)
  1327. expected = r'\frac{- c_{2} + \sinh{\left(\frac{- c_{3} + fv_{M}}{c_{0}} \right)}}{c_{1}}'
  1328. assert LatexPrinter().doprint(fv_M.doit()) == expected
  1329. @pytest.mark.parametrize(
  1330. 'code_printer, expected',
  1331. [
  1332. (
  1333. C89CodePrinter,
  1334. '(-0.12271444348999878*(0.374 - sinh(3.1446540880503142*(fv_M '
  1335. '- 0.88600000000000001))))',
  1336. ),
  1337. (
  1338. C99CodePrinter,
  1339. '(-0.12271444348999878*(0.374 - sinh(3.1446540880503142*(fv_M '
  1340. '- 0.88600000000000001))))',
  1341. ),
  1342. (
  1343. C11CodePrinter,
  1344. '(-0.12271444348999878*(0.374 - sinh(3.1446540880503142*(fv_M '
  1345. '- 0.88600000000000001))))',
  1346. ),
  1347. (
  1348. CXX98CodePrinter,
  1349. '(-0.12271444348999878*(0.374 - sinh(3.1446540880503142*(fv_M '
  1350. '- 0.88600000000000001))))',
  1351. ),
  1352. (
  1353. CXX11CodePrinter,
  1354. '(-0.12271444348999878*(0.374 - std::sinh(3.1446540880503142'
  1355. '*(fv_M - 0.88600000000000001))))',
  1356. ),
  1357. (
  1358. CXX17CodePrinter,
  1359. '(-0.12271444348999878*(0.374 - std::sinh(3.1446540880503142'
  1360. '*(fv_M - 0.88600000000000001))))',
  1361. ),
  1362. (
  1363. FCodePrinter,
  1364. ' (-0.122714443489999d0*(0.374d0 - sinh(3.1446540880503142d0*(fv_M -\n'
  1365. ' @ 0.886d0))))',
  1366. ),
  1367. (
  1368. OctaveCodePrinter,
  1369. '(-0.122714443489999*(0.374 - sinh(3.14465408805031*(fv_M '
  1370. '- 0.886))))',
  1371. ),
  1372. (
  1373. PythonCodePrinter,
  1374. '(-0.122714443489999*(0.374 - math.sinh(3.14465408805031*(fv_M '
  1375. '- 0.886))))',
  1376. ),
  1377. (
  1378. NumPyPrinter,
  1379. '(-0.122714443489999*(0.374 - numpy.sinh(3.14465408805031'
  1380. '*(fv_M - 0.886))))',
  1381. ),
  1382. (
  1383. SciPyPrinter,
  1384. '(-0.122714443489999*(0.374 - numpy.sinh(3.14465408805031'
  1385. '*(fv_M - 0.886))))',
  1386. ),
  1387. (
  1388. CuPyPrinter,
  1389. '(-0.122714443489999*(0.374 - cupy.sinh(3.14465408805031*(fv_M '
  1390. '- 0.886))))',
  1391. ),
  1392. (
  1393. JaxPrinter,
  1394. '(-0.122714443489999*(0.374 - jax.numpy.sinh(3.14465408805031'
  1395. '*(fv_M - 0.886))))',
  1396. ),
  1397. (
  1398. MpmathPrinter,
  1399. '(-mpmath.mpf((0, 8842507551592581, -56, 53))*(mpmath.mpf((0, '
  1400. '3368692521273131, -53, 52)) - mpmath.sinh(mpmath.mpf((0, '
  1401. '7081131489576251, -51, 53))*(fv_M + mpmath.mpf((1, '
  1402. '7980378539700519, -53, 53))))))',
  1403. ),
  1404. (
  1405. LambdaPrinter,
  1406. '(-0.122714443489999*(0.374 - math.sinh(3.14465408805031*(fv_M '
  1407. '- 0.886))))',
  1408. ),
  1409. ]
  1410. )
  1411. def test_print_code(self, code_printer, expected):
  1412. fv_M_inv = FiberForceVelocityInverseDeGroote2016.with_defaults(self.fv_M)
  1413. assert code_printer().doprint(fv_M_inv) == expected
  1414. def test_derivative_print_code(self):
  1415. fv_M_inv = FiberForceVelocityInverseDeGroote2016.with_defaults(self.fv_M)
  1416. dfv_M_inv_dfv_M = fv_M_inv.diff(self.fv_M)
  1417. expected = (
  1418. '0.385894476383644*math.cosh(3.14465408805031*fv_M '
  1419. '- 2.78616352201258)'
  1420. )
  1421. assert PythonCodePrinter().doprint(dfv_M_inv_dfv_M) == expected
  1422. def test_lambdify(self):
  1423. fv_M_inv = FiberForceVelocityInverseDeGroote2016.with_defaults(self.fv_M)
  1424. fv_M_inv_callable = lambdify(self.fv_M, fv_M_inv)
  1425. assert fv_M_inv_callable(1.0) == pytest.approx(-0.0009548832444487479)
  1426. @pytest.mark.skipif(numpy is None, reason='NumPy not installed')
  1427. def test_lambdify_numpy(self):
  1428. fv_M_inv = FiberForceVelocityInverseDeGroote2016.with_defaults(self.fv_M)
  1429. fv_M_inv_callable = lambdify(self.fv_M, fv_M_inv, 'numpy')
  1430. fv_M = numpy.array([0.8, 0.9, 1.0, 1.1, 1.2])
  1431. expected = numpy.array([
  1432. -0.0794881459,
  1433. -0.0404909338,
  1434. -0.0009548832,
  1435. 0.043061991,
  1436. 0.0959484397,
  1437. ])
  1438. numpy.testing.assert_allclose(fv_M_inv_callable(fv_M), expected)
  1439. @pytest.mark.skipif(jax is None, reason='JAX not installed')
  1440. def test_lambdify_jax(self):
  1441. fv_M_inv = FiberForceVelocityInverseDeGroote2016.with_defaults(self.fv_M)
  1442. fv_M_inv_callable = jax.jit(lambdify(self.fv_M, fv_M_inv, 'jax'))
  1443. fv_M = jax.numpy.array([0.8, 0.9, 1.0, 1.1, 1.2])
  1444. expected = jax.numpy.array([
  1445. -0.0794881459,
  1446. -0.0404909338,
  1447. -0.0009548832,
  1448. 0.043061991,
  1449. 0.0959484397,
  1450. ])
  1451. numpy.testing.assert_allclose(fv_M_inv_callable(fv_M), expected)
  1452. class TestCharacteristicCurveCollection:
  1453. @staticmethod
  1454. def test_valid_constructor():
  1455. curves = CharacteristicCurveCollection(
  1456. tendon_force_length=TendonForceLengthDeGroote2016,
  1457. tendon_force_length_inverse=TendonForceLengthInverseDeGroote2016,
  1458. fiber_force_length_passive=FiberForceLengthPassiveDeGroote2016,
  1459. fiber_force_length_passive_inverse=FiberForceLengthPassiveInverseDeGroote2016,
  1460. fiber_force_length_active=FiberForceLengthActiveDeGroote2016,
  1461. fiber_force_velocity=FiberForceVelocityDeGroote2016,
  1462. fiber_force_velocity_inverse=FiberForceVelocityInverseDeGroote2016,
  1463. )
  1464. assert curves.tendon_force_length is TendonForceLengthDeGroote2016
  1465. assert curves.tendon_force_length_inverse is TendonForceLengthInverseDeGroote2016
  1466. assert curves.fiber_force_length_passive is FiberForceLengthPassiveDeGroote2016
  1467. assert curves.fiber_force_length_passive_inverse is FiberForceLengthPassiveInverseDeGroote2016
  1468. assert curves.fiber_force_length_active is FiberForceLengthActiveDeGroote2016
  1469. assert curves.fiber_force_velocity is FiberForceVelocityDeGroote2016
  1470. assert curves.fiber_force_velocity_inverse is FiberForceVelocityInverseDeGroote2016
  1471. @staticmethod
  1472. @pytest.mark.skip(reason='kw_only dataclasses only valid in Python >3.10')
  1473. def test_invalid_constructor_keyword_only():
  1474. with pytest.raises(TypeError):
  1475. _ = CharacteristicCurveCollection(
  1476. TendonForceLengthDeGroote2016,
  1477. TendonForceLengthInverseDeGroote2016,
  1478. FiberForceLengthPassiveDeGroote2016,
  1479. FiberForceLengthPassiveInverseDeGroote2016,
  1480. FiberForceLengthActiveDeGroote2016,
  1481. FiberForceVelocityDeGroote2016,
  1482. FiberForceVelocityInverseDeGroote2016,
  1483. )
  1484. @staticmethod
  1485. @pytest.mark.parametrize(
  1486. 'kwargs',
  1487. [
  1488. {'tendon_force_length': TendonForceLengthDeGroote2016},
  1489. {
  1490. 'tendon_force_length': TendonForceLengthDeGroote2016,
  1491. 'tendon_force_length_inverse': TendonForceLengthInverseDeGroote2016,
  1492. 'fiber_force_length_passive': FiberForceLengthPassiveDeGroote2016,
  1493. 'fiber_force_length_passive_inverse': FiberForceLengthPassiveInverseDeGroote2016,
  1494. 'fiber_force_length_active': FiberForceLengthActiveDeGroote2016,
  1495. 'fiber_force_velocity': FiberForceVelocityDeGroote2016,
  1496. 'fiber_force_velocity_inverse': FiberForceVelocityInverseDeGroote2016,
  1497. 'extra_kwarg': None,
  1498. },
  1499. ]
  1500. )
  1501. def test_invalid_constructor_wrong_number_args(kwargs):
  1502. with pytest.raises(TypeError):
  1503. _ = CharacteristicCurveCollection(**kwargs)
  1504. @staticmethod
  1505. def test_instance_is_immutable():
  1506. curves = CharacteristicCurveCollection(
  1507. tendon_force_length=TendonForceLengthDeGroote2016,
  1508. tendon_force_length_inverse=TendonForceLengthInverseDeGroote2016,
  1509. fiber_force_length_passive=FiberForceLengthPassiveDeGroote2016,
  1510. fiber_force_length_passive_inverse=FiberForceLengthPassiveInverseDeGroote2016,
  1511. fiber_force_length_active=FiberForceLengthActiveDeGroote2016,
  1512. fiber_force_velocity=FiberForceVelocityDeGroote2016,
  1513. fiber_force_velocity_inverse=FiberForceVelocityInverseDeGroote2016,
  1514. )
  1515. with pytest.raises(AttributeError):
  1516. curves.tendon_force_length = None
  1517. with pytest.raises(AttributeError):
  1518. curves.tendon_force_length_inverse = None
  1519. with pytest.raises(AttributeError):
  1520. curves.fiber_force_length_passive = None
  1521. with pytest.raises(AttributeError):
  1522. curves.fiber_force_length_passive_inverse = None
  1523. with pytest.raises(AttributeError):
  1524. curves.fiber_force_length_active = None
  1525. with pytest.raises(AttributeError):
  1526. curves.fiber_force_velocity = None
  1527. with pytest.raises(AttributeError):
  1528. curves.fiber_force_velocity_inverse = None