Source code for pymuscle.muscle
"""
Contains base Muscle class and its immediate descendants.
"""
import numpy as np
from typing import Union
from .potvin_fuglevand_2017_muscle_fibers import PotvinFuglevand2017MuscleFibers
from .potvin_fuglevand_2017_motor_neuron_pool import PotvinFuglevand2017MotorNeuronPool
from .pymuscle_fibers import PyMuscleFibers
from .model import Model
[docs]class Muscle(object):
"""
A user-created :class:`Muscle <Muscle>` object.
Used to simulate the input-output relationship between motor neuron
excitation and muscle fibers contractile state over time.
:param motor_neuron_pool_model:
The motor neuron pool implementation to use with this muscle.
:param muscle_fibers_model:
The muscle fibers model implementation to use with this muscle.
:param motor_unit_count: How many motor units comprise this muscle.
Usage::
from pymuscle import (Muscle,
PotvinFuglevand2017MotorNeuronPool as Pool,
PotvinFuglevand2017MuscleFibers as Fibers)
motor_unit_count = 60
muscle = Muscle(
Pool(motor_unit_count),
Fibers(motor_unit_count),
)
excitation = 32.0
force = muscle.step(excitation, 1 / 50.0)
"""
def __init__(
self,
motor_neuron_pool_model: Model,
muscle_fibers_model: Model,
):
assert motor_neuron_pool_model.motor_unit_count == \
muscle_fibers_model.motor_unit_count
self._pool = motor_neuron_pool_model
self._fibers = muscle_fibers_model
@property
def motor_unit_count(self):
return self._pool.motor_unit_count
@property
def current_forces(self):
return self._fibers.current_forces
[docs] def step(
self,
motor_pool_input: Union[float, np.ndarray],
step_size: float
) -> float:
"""
Advances the muscle model one step.
:param motor_pool_input:
Either a single value or an array of values representing the
excitatory input to the motor neuron pool for this muscle
:param step_size:
How far to advance the simulation in time for this step.
"""
# Expand a single input to the muscle to a full array
if isinstance(motor_pool_input, float) or \
isinstance(motor_pool_input, int):
motor_pool_input = np.full(
self._pool.motor_unit_count,
motor_pool_input
)
motor_pool_output = self._pool.step(motor_pool_input, step_size)
return self._fibers.step(motor_pool_output)
[docs]class PotvinFuglevandMuscle(Muscle):
"""
A thin wrapper around :class:`Muscle <Muscle>` which pre-selects the
Potvin fiber and motor neuron models.
"""
def __init__(
self,
motor_unit_count: int,
apply_central_fatigue: bool = True,
apply_peripheral_fatigue: bool = True,
pre_calc_firing_rates: bool = False
):
pool = PotvinFuglevand2017MotorNeuronPool(
motor_unit_count,
apply_fatigue=apply_central_fatigue,
pre_calc_firing_rates=pre_calc_firing_rates
)
fibers = PotvinFuglevand2017MuscleFibers(
motor_unit_count,
apply_fatigue=apply_peripheral_fatigue
)
super().__init__(
motor_neuron_pool_model=pool,
muscle_fibers_model=fibers
)
class StandardMuscle(Muscle):
"""
A thin wrapper around :class:`Muscle <Muscle>` which pre-selects the
Potvin motor neuron model and the PyMuscle specific fiber model.
It is expected that this will use a motor neuron model specific to PyMuscle
(to be called the PyMuscleMotorNeuronPool) in the future.
This muscle does *not* include central (motor neuron) fatigue as the
equations for recovery are not yet available.
This muscle does include both peripheral fatigue and recovery.
"""
def __init__(
self,
motor_unit_count: int,
apply_central_fatigue: bool = False,
apply_peripheral_fatigue: bool = True,
pre_calc_firing_rates: bool = False
):
pool = PotvinFuglevand2017MotorNeuronPool(
motor_unit_count,
apply_fatigue=apply_central_fatigue,
pre_calc_firing_rates=pre_calc_firing_rates
)
fibers = PyMuscleFibers(
motor_unit_count,
apply_fatigue=apply_peripheral_fatigue
)
super().__init__(
motor_neuron_pool_model=pool,
muscle_fibers_model=fibers
)