Source code for myoconverter.conversion_steps.O2MSteps

from loguru import logger
from myoconverter.conversion_steps.O2MStep1 import BasicModelConvert
from myoconverter.conversion_steps.O2MStep2 import MomentArmOpt
from myoconverter.conversion_steps.O2MStep3 import MuscleForceOpt
from myoconverter.utils.generate_pdf import generate_pdf
import os
from fpdf import Template

[docs]class O2MSteps: """ Class to coordinate all conversion steps """ def __init__(self, osim_model_file, geom_folder, mjc_target_folder,\ convert_steps = [1, 2, 3], muscle_list = None,\ osim_data_overwrite = False, convert = False,\ validation = False, generate_pdf = False, speedy = False,\ add_ground_geom = False,\ treat_as_normal_path_point = False): """ Parameters: osim_model_file: string The OpenSim model file. Only works with Osim 4.0+. Osim 3.0 models can be saved into Osim 4.0+ by opening and saving in OpenSim 4.0+ software. geo_folder: string The geometry folder of the given Osim model. mjc_target_folder: string The target folder where the converted mjc model and validation results will be saved. convert_steps: list of integer numbers [1, 2, 3] Corresponding to the three major steps, can be customized. For instance, '[1]' refers to the 'general model conversion step'; '[1, 2]' refers to the 'general model conversion' & 'moment arm optimzation step'; '[3]' refers to the 'muscle force optimzation step', but will require that the [1, 2] has been ran before (model are saved). muscle_list: list of string If not NONE, the list of selected muscles that will be processed only. osim_data_overwrite: boolean If True, overwrite the extracted data of Osim states. convert: boolean If True, run convert processed of the selected steps. validation: boolean If True, perform validation (generate plots) for the selected steps. generate_pdf: boolean If True, generate the pdf report for the converted model This generation only reads the generated comparison plots from the validation steps. Therefore, suggest to run after all the validation steps were preformed. speedy: boolean If True, the optimization process will select a low number of instances, checking nodes, as well iterations to speed up the process. This may result less optimal results, however for complex models, this is suggested. Then regular optimization process (slower) can be done on selected muscles, if detected issues. add_ground_geom: boolean If True, add ground to the converted model. treat_as_normal_path_point: boolean If True, treat all moving and conditional points as normal fix points. """ # # set logging # MODEL_NAME = os.path.split(osim_model_file)[1][:-5] # OUTPUT_LOG_FILE = os.path.join(mjc_target_folder, f"{MODEL_NAME}-conversion.log") # # If there is an existing log file, remove it # if os.path.exists(OUTPUT_LOG_FILE): # os.remove(OUTPUT_LOG_FILE) # # Set the log file # logger.add(OUTPUT_LOG_FILE) # Output file infos logger.info(f"Given OpenSim model file: {osim_model_file}") logger.info(f"Reading mesh files from folder {geom_folder}") logger.info(f"Converted model will be saved in folder {mjc_target_folder}") # transfer variables self.osim_model_file = osim_model_file self.geom_folder = geom_folder self.mjc_target_folder = mjc_target_folder self.convert_steps = convert_steps self.muscle_list = muscle_list self.osim_data_overwrite = osim_data_overwrite self.convert = convert self.validation = validation self.generate_pdf = generate_pdf self.speedy = speedy self.add_ground_geom = add_ground_geom self.treat_as_normal_path_point = treat_as_normal_path_point
[docs] def PipelineExecution(self): # create the mjc_target_folder os.makedirs(self.mjc_target_folder, exist_ok = True) ############################################################################## # STEP 1: GEOMETRY CONVERTING # convert the geomerties # check if the first step is required if 1 in self.convert_steps: # create the first step folder if not exist path_step1 = self.mjc_target_folder + '/Step1_xmlConvert' os.makedirs(path_step1, exist_ok = True) # load the first step converting class Step1 = BasicModelConvert(self.osim_model_file, self.geom_folder, path_step1,\ add_ground_geom = self.add_ground_geom,\ treat_as_normal_path_point = self.treat_as_normal_path_point) if self.convert: # General model conversion, generate xxxx_cvt1.xml model logger.info("Started step 1 conversion - cvt1.") mjcModel_Cvt1_path = Step1.cvt1_ModelConvert() logger.info("Finished step 1 conversion - cvt1.") else: logger.info("convert is set to False, skipped step 1 conversion - cvt1.") if self.validation: # validation of the general model conversion, forward kinematics check logger.info("Started step 1 validation - val1.") if self.convert: Step1.vlt1_forwardKinematicsValidation(speedy = self.speedy) else: mjcModel_Cvt1_path = self.mjc_target_folder + '/' +\ os.path.split(self.osim_model_file)[1][:-5] +\ '_cvt1.xml' Step1.vlt1_forwardKinematicsValidation(mjc_model_path = mjcModel_Cvt1_path, speedy = self.speedy) logger.info("Finished step 1 validation - val1.") else: logger.info("validation is set to False, skipped step 1 validation - vlt1.") else: logger.info("[1] is not selected, skipped step 1 process.") ############################################################################## if 2 in self.convert_steps: ############################################################################### # STEP 2: MOMENT ARM OPTIMIZATION # The cvt1 model name path(after the 1st step general conversion) from default setting. mjcModel_Cvt1_path = self.mjc_target_folder + '/' +\ os.path.split(self.osim_model_file)[1][:-5] +\ '_cvt1.xml' # The cvt2 model name path(after the 2nd step convert) from default setting. mjcModel_Cvt2_path = self.mjc_target_folder + '/' +\ os.path.split(self.osim_model_file)[1][:-5] +\ '_cvt2.xml' # First check if the cvt2 model is already exist. # If so, then directly optimize on top of it. # Otherwise, optimize from cvt1 model and generate cvt2 model at the end. # If cvt1 model does not exist either, then raise an error. if os.path.exists(mjcModel_Cvt2_path): logger.info("xxx_cvt2 model already exist, process from it") cvt2_model_path = mjcModel_Cvt2_path else: logger.info("xxx_cvt2 model does not exist, start from xxx_cvt1") if os.path.exists(mjcModel_Cvt1_path): cvt2_model_path = mjcModel_Cvt1_path else: logger.debug("xxx_cvt1 model file does not exist either, stopped!") logger.debug("Please run [1] first to get the xxx_cvt1 model") raise('xxx_cvt1 model file does not exist, cannot process following' + 'steps .. \n') # step 2 saving path path_step2 = self.mjc_target_folder + '/Step2_muscleKinematics' os.makedirs(path_step2, exist_ok = True) # apply moment arm convert MA_Opt = MomentArmOpt(cvt2_model_path, self.osim_model_file, path_step2,\ muscle_list = self.muscle_list, osim_data_overwrite = self.osim_data_overwrite,\ speedy = self.speedy) if self.convert: # optimize the side sites to get better moment arm fits logger.info("Started step 2 conversion - cvt2.") mjcModel_Cvt2_path = MA_Opt.optMomentArms() logger.info("Finished step 2 conversion - cvt2.") else: logger.info("convert is set to False, skipped step 2 conversion - cvt2.") if self.validation: # then validate (plot) the convert results logger.info("Started step 2 validation - val2.") MA_Opt.compMomentArmResults() logger.info("Finished step 2 validation - val2.") else: logger.info("validation is set to False, skipped step 2 validation - val2.") ############################################################################### if 3 in self.convert_steps: ############################################################################### # STEP 3: MUSCULE FORCE OPTIMIZATION # The cvt2 model name path(after the 2nd step convert) from default setting. mjcModel_Cvt2_path = self.mjc_target_folder + '/' +\ os.path.split(self.osim_model_file)[1][:-5] +\ '_cvt2.xml' # The cvt3 model name path(after the 3rd step convert) from default setting. mjcModel_Cvt3_path = self.mjc_target_folder + '/' +\ os.path.split(self.osim_model_file)[1][:-5] +\ '_cvt3.xml' # First check if the cvt2 model is already exist. # If so, then directly optimize on top of it. # Otherwise, optimize from cvt1 model and generate cvt2 model at the end. # If cvt1 model does not exist either, then raise an error. if os.path.exists(mjcModel_Cvt3_path): logger.info("xxx_cvt3 model already exist, process from it") cvt3_model_path = mjcModel_Cvt3_path else: logger.info("xxx_cvt3 model does not exist, start from xxx_cvt2") if os.path.exists(mjcModel_Cvt2_path): cvt3_model_path = mjcModel_Cvt2_path else: logger.debug("xxx_cvt2 model file does not exist either, stopped!") logger.debug("Please run [2] first to get the xxx_cvt2 model") raise('xxx_cvt2 model file does not exist, cannot process following' + 'steps .. \n') # step 3 saving path path_step3 = self.mjc_target_folder + '/Step3_muscleKinetics' os.makedirs(path_step3, exist_ok = True) # create an instance of the muscle force optimization musForceOpt = MuscleForceOpt(cvt3_model_path, self.osim_model_file, path_step3,\ muscle_list = self.muscle_list,\ osim_data_overwrite = self.osim_data_overwrite,\ speedy = self.speedy) if self.convert: # optimize muscle parameters and save optimized model as cvt3 logger.info("Started step 3 conversion - cvt3.") mjcModel_Cvt3_path = musForceOpt.optMuscleForce() logger.info("Finished step 3 conversion - cvt3.") else: logger.info("Skipped step 3 conversion - cvt3.") if self.validation: # validate the muscle forces logger.info("Started step 3 validation - val3.") musForceOpt.compMuscleForceResults() logger.info("Finished step 3 validation - val3.") else: logger.info("Skipped step 3 validation - val3.") # ############################################################################### # if pdf report required, then generate it from the plots that generated in the # converted folder. ############################################################################### if self.generate_pdf: logger.info("Generating pdf report based on the validation results.") logger.info(" This may take a while, suggest to do this after all validation steps...") mjcModel_vlt1_path = self.mjc_target_folder + "/Step1_xmlConvert" mjcModel_vlt2_path = self.mjc_target_folder + "/Step2_muscleKinematics" mjcModel_vlt3_path = self.mjc_target_folder + "/Step3_muscleKinetics" model_name = os.path.split(self.osim_model_file)[1][:-5] pdf = generate_pdf(mjcModel_vlt1_path, mjcModel_vlt2_path, mjcModel_vlt3_path, model_name, self.mjc_target_folder) # generate pdf pdf.render(self.mjc_target_folder + '/' + model_name + '.pdf') logger