Source code for model_states.OsimMuscleStates

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 20 21:43:21 2022

@author: hwang
"""

from myoconverter.optimization.utils.UtilsOpensim import getJointsControlledByMuscle, getCouplingJoints,\
        getAllIndependentCoordinates, getJointRanges_dict, getJointRanges_array, computeMomentArm,\
        getMuscleLengthList, getMuscleForceMaps, getMuscleProperties
from loguru import logger
import pickle
        

[docs]class OsimMuscleStates: """ A class to extract the muscle states (moment arms and force properites) of the given OpenSim model """ def __init__(self, osim_model, muscle_list = None): """ osim_model: loaded opensim model muscle_list: specified muscles whose states will be extracted. if None, then all muscles inside the OsimModel will be calculated. """ # initilize the input osim model as the global variables self.osim_model = osim_model if type(muscle_list) != list: self.muscle_list = [muscle_list] else: self.muscle_list = muscle_list # dictionary for storing calculated moment arms and force properties self.moment_arms = dict() self.force_properties = dict()
[docs] def reset(self): # reset all calculated variables self.moment_arms = dict() self.force_properties = dict()
# get the wrapping coordinate of the given muscle list
[docs] def getWrapingCoords(self): """ Extract the wrapping coordinates of the given muscle list. In addition, the coupling coordinates when calculating moment arms were extracted. For a given muscle list, outputs of this function is a dictionary: Keywords || list of coordinates muscle 1: coordinate 1, coordinate 2 coordinate 3, muscle 2: coordinate 1 coordinate 3, coordinate 4 coordinate 5, coordinate 6, ... """ logger.info("Extract the joints that muscle wrapped over from Osim model") self.wrapping_coordinate = {} osim_muscles = self.osim_model.getMuscles() # osim muscle class # go through a for loop of each muscle to extract the wrapped coordinates for i_muscle in self.muscle_list: # get wrapping coordiantes joints = getJointsControlledByMuscle(self.osim_model, osim_muscles.get(i_muscle)) jointCP = getCouplingJoints(self.osim_model, osim_muscles.get(i_muscle), joints) # remove the duplicated coupling joints combinations wrap_list = [list(i) for i in {*[tuple(sorted(i)) for i in jointCP]}]; # remove the sub coupling joints and only save the largest one. # Also make sure they always have the same order in every calculations. # A dirty fix right now, must have better ways to do this.... i_wrap = 0 while i_wrap < len(wrap_list): j_wrap = 0 while j_wrap < len(wrap_list): if i_wrap == j_wrap: j_wrap = j_wrap + 1 else: if all(elem in wrap_list[j_wrap] for elem in wrap_list[i_wrap]): del wrap_list[i_wrap] break else: j_wrap = j_wrap + 1 else: i_wrap = i_wrap + 1 self.wrapping_coordinate[i_muscle] = wrap_list return self.wrapping_coordinate
# getting all coordinate ranges that wrapped by muscles
[docs] def getCoordRanges(self): """ Extract the motion ranges of given coordinates in an OpenSim model. Outputs of this function is a dictionary with the coordinate names as keywords Keywords || list coordinate 1: [lower bound, upper bound] coordinate 2: [lower bound, upper bound] coordinate 3: [lower bound, upper bound] ... """ logger.info("Extract joint motion range from Osim model") joint_names, DOF_Index = getAllIndependentCoordinates(self.osim_model) self.coordinate_range = getJointRanges_dict(self.osim_model, joint_names) return self.coordinate_range
[docs] def getMomentArms(self, save_path, speedy = False): """ calcualte moment arms of a muscle at different coordinates it wrapped. Parameters ---------- wrapping_coordinate : dictionary The dictionary that contains the muscles list and their wrapping coordinates. If not provide, will calculate from the above getWrapingCoord function. coordinate_range: dictionary The dictionary that contains the motion ranges of the joint lists that been wrapped by the muscles. if not provided, generate from the above getWrapingCoord function. save_path: string The path to save muscle-specific force map files, if not provided, not saving... speedy: boolean If True, select a lower number of particles, checking notes, iterations to speed up the optimization process. Returns ------- self.moment_arms: dictionary The dictionary that contains the moment arms that were calculated from the opensim model with the given inputs """ self.getWrapingCoords() # generate wrapping coordiantes # self.getCoordRanges() # extract joint ranges logger.info("Calculate moment arms from Osim model") # self.moment_arms = {} don't not export all the moment arm in one file anymore for muscle in self.wrapping_coordinate.keys(): # run through all muscles logger.info(f"Muscle: {muscle}") osim_muscle = self.osim_model.getMuscles().get(muscle) mus_para = {} mus_para['muscle_name'] = muscle muscle_moment_arms = [] mus_para['wrapping_coordinates'] = self.wrapping_coordinate[muscle] motion_ranges = [] moment_arms_joints = [] evalN_list = [] for joints in self.wrapping_coordinate[muscle]: # run through all joints if speedy: # speedy optimization with lower number of evaluation nodes # setup the maximum mesh points depends on the number of # joint coupling together, otherwise it will take too long to # compute them! if len(joints) == 1: evalN = 25 elif len(joints) == 2: evalN = 7 elif len(joints) == 3: evalN = 4 elif len(joints) == 4: evalN = 3 else: evalN = 2 else: # setup the maximum mesh points depends on the number of # joint coupling together, otherwise it will take too long to # compute them! if len(joints) == 1: evalN = 25 elif len(joints) == 2: evalN = 11 elif len(joints) == 3: evalN = 7 elif len(joints) == 4: evalN = 5 elif len(joints) == 5: evalN = 4 else: evalN = 3 motion_range = [] for joint in joints: # extract the motion range motion_range.append(getJointRanges_array(self.osim_model, joint)) # calculate moment arms for this muscle and joints # and save it to a list moment_arms_joints.append(computeMomentArm(self.osim_model,\ osim_muscle, joints, motion_range,\ evalN)) motion_ranges.append(motion_range) evalN_list.append(evalN) # save joint motion ranges and moment arm lists mus_para['osim_coordinate_ranges'] = motion_ranges mus_para['osim_ma_data'] = moment_arms_joints mus_para['evalN'] = evalN_list # save moment arm data if a saving path is provided if save_path: with open(save_path + '/' + muscle + '.pkl', 'wb') as muscle_file: pickle.dump(mus_para, muscle_file)
# save the moment arms of the muscle into a dictionary, NO LONGER NEEDED # self.moment_arms[muscle]= muscle_moment_arms # return self.moment_arms
[docs] def getMuscleForceMaps(self, save_path, speedy = False): """get the muscle force maps from OpenSim models. Parameters ---------- save_path: string The path to save muscle-specific force map files, if not provided, not saving... speedy: boolean If True, select a lower number of particles, checking notes, iterations to speed up the optimization process. Returns (NONE) ------- self.force_maps: dictionary The dictionary that contains the muscle force maps that were calculated from the opensim model with the given inputs """ # TODO: set condition to avoid run this again. self.getWrapingCoords() logger.info("Calculate muscle force curves from Osim model") # force_maps = {} for muscle in self.wrapping_coordinate.keys(): # run through each muscle logger.info(f"Muscle: {muscle}") osim_muscle = self.osim_model.getMuscles().get(muscle) # get a list of muscle lengths from minimal to maximum and the corresponding # joint angle list force_map = {} # extract the property parameters from the OpenSim models force_map['mtu_par_set'] = getMuscleProperties(osim_muscle) motion_range = [] joints_uniq = [] for joints in self.wrapping_coordinate[muscle]: # run through all joints for joint in joints: # extract the motion range if joint not in joints_uniq: # only go through the unique joint coordinates joints_uniq.append(joint) motion_range.append(getJointRanges_array(self.osim_model, joint)) mtu_len_set, jit_list_set = getMuscleLengthList(self.osim_model,\ osim_muscle, joints_uniq,\ motion_range, speedy) force_map['muscle_name'] = muscle force_map['mtu_length_osim'] = mtu_len_set force_map['jit_list_set'] = jit_list_set force_map['jit_uniq'] = joints_uniq force_map['jit_uniq_ranges'] = motion_range # set the muscle activation shift from 0 to 1 # only optimize when activation equal to 1 act_list = [1] # np.linspace(0, 1, 5) force_map['act_list'] = act_list # get muscle tendon force, and active/passive forces mtu_force_length, act_force_length, pas_force_length = \ getMuscleForceMaps(self.osim_model, osim_muscle, joints_uniq, jit_list_set, act_list) force_map['mtu_force_osim'] = mtu_force_length force_map['act_force_osim'] = act_force_length force_map['pas_force_osim'] = pas_force_length if save_path: with open(save_path + '/' + muscle + '.pkl', 'wb') as muscle_mf_file: pickle.dump(force_map, muscle_mf_file)
# return force_maps