From 029cedf78034d002fd2c34a833553f8a7c6b56b7 Mon Sep 17 00:00:00 2001 From: Kevin CO Date: Thu, 18 Apr 2024 18:55:31 -0400 Subject: [PATCH] Applied reviewable comments and doc string --- .../frequecy_optimization_minimize_fatigue.py | 4 +- ..._duration_optimization_minimize_fatigue.py | 4 +- ...intensity_optimization_minimize_fatigue.py | 4 +- ...force_velocity_relationships_comparison.py | 6 +-- .../reaching_task_frequency_optimization.py | 4 +- .../reaching_task_intensity_optimization.py | 4 +- ...aching_task_pulse_duration_optimization.py | 4 +- ...cy_optimization_musculoskeletal_dynamic.py | 4 +- ...on_optimization_musculoskeletal_dynamic.py | 4 +- ...ty_optimization_musculoskeletal_dynamic.py | 4 +- cocofest/integration/ivp_fes.py | 25 +---------- cocofest/models/dynamical_model.py | 33 ++++++++++---- cocofest/models/hmed2018.py | 2 +- cocofest/optimization/fes_ocp.py | 4 +- cocofest/optimization/fes_ocp_dynamics.py | 44 +++++++++---------- tests/shard2/test_fes_dynamics.py | 8 ++-- 16 files changed, 76 insertions(+), 82 deletions(-) diff --git a/cocofest/examples/dynamics/minimize_fatigue/frequecy_optimization_minimize_fatigue.py b/cocofest/examples/dynamics/minimize_fatigue/frequecy_optimization_minimize_fatigue.py index ef4cf839..bb4fc013 100644 --- a/cocofest/examples/dynamics/minimize_fatigue/frequecy_optimization_minimize_fatigue.py +++ b/cocofest/examples/dynamics/minimize_fatigue/frequecy_optimization_minimize_fatigue.py @@ -50,8 +50,8 @@ pulse_apparition_dict={"time_min": 0.01, "time_max": 1, "time_bimapping": False}, with_residual_torque=True, objective_dict={"custom_objective": objective_functions}, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=False, + activate_force_length_relationship=True, + activate_force_velocity_relationship=False, minimize_muscle_fatigue=True, ) diff --git a/cocofest/examples/dynamics/minimize_fatigue/pulse_duration_optimization_minimize_fatigue.py b/cocofest/examples/dynamics/minimize_fatigue/pulse_duration_optimization_minimize_fatigue.py index 5b6e7741..60d300e7 100644 --- a/cocofest/examples/dynamics/minimize_fatigue/pulse_duration_optimization_minimize_fatigue.py +++ b/cocofest/examples/dynamics/minimize_fatigue/pulse_duration_optimization_minimize_fatigue.py @@ -50,8 +50,8 @@ }, with_residual_torque=False, objective_dict={"custom_objective": objective_functions}, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=False, + activate_force_length_relationship=True, + activate_force_velocity_relationship=False, minimize_muscle_fatigue=True, ) diff --git a/cocofest/examples/dynamics/minimize_fatigue/pulse_intensity_optimization_minimize_fatigue.py b/cocofest/examples/dynamics/minimize_fatigue/pulse_intensity_optimization_minimize_fatigue.py index 5a74ac0c..ddc56c68 100644 --- a/cocofest/examples/dynamics/minimize_fatigue/pulse_intensity_optimization_minimize_fatigue.py +++ b/cocofest/examples/dynamics/minimize_fatigue/pulse_intensity_optimization_minimize_fatigue.py @@ -52,8 +52,8 @@ }, with_residual_torque=False, objective_dict={"custom_objective": objective_functions}, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=False, + activate_force_length_relationship=True, + activate_force_velocity_relationship=False, minimize_muscle_fatigue=True, ) diff --git a/cocofest/examples/dynamics/muscle_force_length_and_force_velocity_relationships_comparison.py b/cocofest/examples/dynamics/muscle_force_length_and_force_velocity_relationships_comparison.py index 289dab96..4f9b6f2e 100644 --- a/cocofest/examples/dynamics/muscle_force_length_and_force_velocity_relationships_comparison.py +++ b/cocofest/examples/dynamics/muscle_force_length_and_force_velocity_relationships_comparison.py @@ -17,7 +17,7 @@ sol_list = [] sol_time = [] -muscle_force_length_relationship = [False, True] +activate_force_length_relationship = [False, True] for i in range(2): ocp = OcpFesMsk.prepare_ocp( @@ -30,8 +30,8 @@ final_time=1, pulse_duration_dict={"pulse_duration": 0.00025}, with_residual_torque=False, - muscle_force_length_relationship=muscle_force_length_relationship[i], - muscle_force_velocity_relationship=muscle_force_length_relationship[i], + activate_force_length_relationship=activate_force_length_relationship[i], + activate_force_velocity_relationship=activate_force_length_relationship[i], use_sx=False, ) sol = ocp.solve(Solver.IPOPT(_max_iter=1000)) diff --git a/cocofest/examples/dynamics/reaching_task/reaching_task_frequency_optimization.py b/cocofest/examples/dynamics/reaching_task/reaching_task_frequency_optimization.py index eb40c186..df8524ed 100644 --- a/cocofest/examples/dynamics/reaching_task/reaching_task_frequency_optimization.py +++ b/cocofest/examples/dynamics/reaching_task/reaching_task_frequency_optimization.py @@ -92,8 +92,8 @@ pulse_apparition_dict={"time_min": 0.01, "time_max": 0.1, "time_bimapping": False}, with_residual_torque=False, custom_constraint=constraint, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=True, + activate_force_length_relationship=True, + activate_force_velocity_relationship=True, minimize_muscle_fatigue=True if pickle_file_list[i] == "minimize_muscle_fatigue.pkl" else False, minimize_muscle_force=True if pickle_file_list[i] == "minimize_muscle_force.pkl" else False, use_sx=False, diff --git a/cocofest/examples/dynamics/reaching_task/reaching_task_intensity_optimization.py b/cocofest/examples/dynamics/reaching_task/reaching_task_intensity_optimization.py index 79503f25..176ad323 100644 --- a/cocofest/examples/dynamics/reaching_task/reaching_task_intensity_optimization.py +++ b/cocofest/examples/dynamics/reaching_task/reaching_task_intensity_optimization.py @@ -99,8 +99,8 @@ }, with_residual_torque=False, custom_constraint=constraint, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=True, + activate_force_length_relationship=True, + activate_force_velocity_relationship=True, minimize_muscle_fatigue=True if pickle_file_list[i] == "minimize_muscle_fatigue.pkl" else False, minimize_muscle_force=True if pickle_file_list[i] == "minimize_muscle_force.pkl" else False, use_sx=False, diff --git a/cocofest/examples/dynamics/reaching_task/reaching_task_pulse_duration_optimization.py b/cocofest/examples/dynamics/reaching_task/reaching_task_pulse_duration_optimization.py index c9ad3785..68392749 100644 --- a/cocofest/examples/dynamics/reaching_task/reaching_task_pulse_duration_optimization.py +++ b/cocofest/examples/dynamics/reaching_task/reaching_task_pulse_duration_optimization.py @@ -93,8 +93,8 @@ }, with_residual_torque=False, custom_constraint=constraint, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=True, + activate_force_length_relationship=True, + activate_force_velocity_relationship=True, minimize_muscle_fatigue=True if pickle_file_list[i] == "minimize_muscle_fatigue.pkl" else False, minimize_muscle_force=True if pickle_file_list[i] == "minimize_muscle_force.pkl" else False, use_sx=False, diff --git a/cocofest/examples/getting_started/frequency_optimization_musculoskeletal_dynamic.py b/cocofest/examples/getting_started/frequency_optimization_musculoskeletal_dynamic.py index ffd45bfc..3d8cc669 100644 --- a/cocofest/examples/getting_started/frequency_optimization_musculoskeletal_dynamic.py +++ b/cocofest/examples/getting_started/frequency_optimization_musculoskeletal_dynamic.py @@ -30,8 +30,8 @@ pulse_apparition_dict={"time_min": 0.01, "time_max": 0.1, "time_bimapping": True}, objective_dict={"custom_objective": objective_functions}, with_residual_torque=True, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=True, + activate_force_length_relationship=True, + activate_force_velocity_relationship=True, ) sol = ocp.solve(Solver.IPOPT(_max_iter=1000)) diff --git a/cocofest/examples/getting_started/pulse_duration_optimization_musculoskeletal_dynamic.py b/cocofest/examples/getting_started/pulse_duration_optimization_musculoskeletal_dynamic.py index 576ee7ab..671376f0 100644 --- a/cocofest/examples/getting_started/pulse_duration_optimization_musculoskeletal_dynamic.py +++ b/cocofest/examples/getting_started/pulse_duration_optimization_musculoskeletal_dynamic.py @@ -36,8 +36,8 @@ }, objective_dict={"custom_objective": objective_functions}, with_residual_torque=True, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=True, + activate_force_length_relationship=True, + activate_force_velocity_relationship=True, ) sol = ocp.solve(Solver.IPOPT(_max_iter=2000)) diff --git a/cocofest/examples/getting_started/pulse_intensity_optimization_musculoskeletal_dynamic.py b/cocofest/examples/getting_started/pulse_intensity_optimization_musculoskeletal_dynamic.py index bbb116f7..e726f9b0 100644 --- a/cocofest/examples/getting_started/pulse_intensity_optimization_musculoskeletal_dynamic.py +++ b/cocofest/examples/getting_started/pulse_intensity_optimization_musculoskeletal_dynamic.py @@ -35,8 +35,8 @@ pulse_intensity_dict={"intensity_min": minimum_pulse_intensity, "intensity_max": 130, "intensity_bimapping": False}, objective_dict={"custom_objective": objective_functions}, with_residual_torque=True, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=True, + activate_force_length_relationship=True, + activate_force_velocity_relationship=True, ) sol = ocp.solve(Solver.IPOPT(_max_iter=1000)) diff --git a/cocofest/integration/ivp_fes.py b/cocofest/integration/ivp_fes.py index 822fcbf2..8b2c187c 100644 --- a/cocofest/integration/ivp_fes.py +++ b/cocofest/integration/ivp_fes.py @@ -29,27 +29,6 @@ class IvpFes: The main class to define an ivp. This class prepares the ivp and gives all the needed parameters to integrate a functional electrical stimulation problem. - Attributes - ---------- - model: FesModel - The model type used for the ivp - n_stim: int - Number of stimulation that will occur during the ivp, it is as well refer as phases - n_shooting: int - Number of shooting point for each individual phases - final_time: float - Refers to the final time of the ivp - pulse_duration: int | float - Setting a chosen pulse duration among phases - pulse_intensity: int | float - Setting a chosen pulse intensity among phases - ode_solver: OdeSolver - The ode solver to use - use_sx: bool - The nature of the casadi variables. MX are used if False. - n_threads: int - The number of thread to use while solving (multi-threading if > 1) - Methods ------- from_frequency_and_final_time(self, frequency: int | float, final_time: float, round_down: bool) @@ -70,10 +49,10 @@ def __init__( ---------- fes_parameters: dict The parameters for the fes configuration including : - model, n_stim, pulse_duration, pulse_intensity, pulse_mode + model (FesModel type), n_stim (int type), pulse_duration (float type), pulse_intensity (int | float type), pulse_mode (str type) ivp_parameters: dict The parameters for the ivp problem including : - n_shooting, final_time, extend_last_phase, ode_solver, use_sx, n_threads + n_shooting (int type), final_time (int | float type), extend_last_phase (int | float type), ode_solver (OdeSolver type), use_sx (bool type), n_threads (int type) """ self._fill_fes_dict(fes_parameters) diff --git a/cocofest/models/dynamical_model.py b/cocofest/models/dynamical_model.py index 43261a38..775696a1 100644 --- a/cocofest/models/dynamical_model.py +++ b/cocofest/models/dynamical_model.py @@ -21,21 +21,36 @@ def __init__( self, name: str = None, biorbd_path: str = None, - muscles_model: DingModelFrequency() = None, - muscle_force_length_relationship: bool = False, - muscle_force_velocity_relationship: bool = False, + muscles_model: list[FesModel] = None, + activate_force_length_relationship: bool = False, + activate_force_velocity_relationship: bool = False, ): + """ + The custom model that will be used in the optimal control program for the FES-MSK models + + Parameters + ---------- + name: str + The model name + biorbd_path: str + The path to the biorbd model + muscles_model: DingModelFrequency + The muscle model that will be used in the model + activate_force_length_relationship: bool + If the force-length relationship should be activated + activate_force_velocity_relationship: bool + If the force-velocity relationship should be activated + """ + super().__init__(biorbd_path) self._name = name self.bio_model = BiorbdModel(biorbd_path) - self.bounds_from_ranges_q = self.bio_model.bounds_from_ranges("q") - self.bounds_from_ranges_qdot = self.bio_model.bounds_from_ranges("qdot") self.muscles_dynamics_model = muscles_model self.bio_stim_model = [self.bio_model] + self.muscles_dynamics_model - self.muscle_force_length_relationship = muscle_force_length_relationship - self.muscle_force_velocity_relationship = muscle_force_velocity_relationship + self.activate_force_length_relationship = activate_force_length_relationship + self.activate_force_velocity_relationship = activate_force_velocity_relationship # ---- Absolutely needed methods ---- # def serialize(self, index: int = 0) -> tuple[Callable, dict]: @@ -149,7 +164,7 @@ def muscles_joint_torque( muscle_force_length_coefficient( model=nlp.model.bio_model.model, muscle=nlp.model.bio_model.model.muscle(muscle_idx), q=q ) - if nlp.model.muscle_force_velocity_relationship + if nlp.model.activate_force_velocity_relationship else 1 ) @@ -157,7 +172,7 @@ def muscles_joint_torque( muscle_force_velocity_coefficient( model=nlp.model.bio_model.model, muscle=nlp.model.bio_model.model.muscle(muscle_idx), q=q, qdot=qdot ) - if nlp.model.muscle_force_velocity_relationship + if nlp.model.activate_force_velocity_relationship else 1 ) diff --git a/cocofest/models/hmed2018.py b/cocofest/models/hmed2018.py index 287d8845..bd53b994 100644 --- a/cocofest/models/hmed2018.py +++ b/cocofest/models/hmed2018.py @@ -338,6 +338,6 @@ def min_pulse_intensity(self): """ Returns ------- - The minimum pulse intensity + The minimum pulse intensity threshold of the model """ return (np.arctanh(-self.cr) / self.bs) + self.Is diff --git a/cocofest/optimization/fes_ocp.py b/cocofest/optimization/fes_ocp.py index 8d474d37..0d81bd03 100644 --- a/cocofest/optimization/fes_ocp.py +++ b/cocofest/optimization/fes_ocp.py @@ -367,7 +367,7 @@ def _sanity_check( if not isinstance(time_bimapping, bool): raise TypeError("time_bimapping must be bool type") - if isinstance(model, DingModelPulseDurationFrequency): + if isinstance(model, DingModelPulseDurationFrequency | DingModelPulseDurationFrequencyWithFatigue): if pulse_duration is None and [pulse_duration_min, pulse_duration_max].count(None) != 0: raise ValueError("pulse duration or pulse duration min max bounds need to be set for this model") if all([pulse_duration, pulse_duration_min, pulse_duration_max]): @@ -412,7 +412,7 @@ def _sanity_check( if not isinstance(pulse_duration_bimapping, None | bool): raise NotImplementedError("If added, pulse duration parameter mapping must be a bool type") - if isinstance(model, DingModelIntensityFrequency): + if isinstance(model, DingModelIntensityFrequency | DingModelIntensityFrequencyWithFatigue): if pulse_intensity is None and [pulse_intensity_min, pulse_intensity_max].count(None) != 0: raise ValueError("Pulse intensity or pulse intensity min max bounds need to be set for this model") if all([pulse_intensity, pulse_intensity_min, pulse_intensity_max]): diff --git a/cocofest/optimization/fes_ocp_dynamics.py b/cocofest/optimization/fes_ocp_dynamics.py index 4a45f70d..ff262a86 100644 --- a/cocofest/optimization/fes_ocp_dynamics.py +++ b/cocofest/optimization/fes_ocp_dynamics.py @@ -46,8 +46,8 @@ def prepare_ocp( objective_dict: dict = None, custom_constraint: ConstraintList = None, with_residual_torque: bool = False, - muscle_force_length_relationship: bool = False, - muscle_force_velocity_relationship: bool = False, + activate_force_length_relationship: bool = False, + activate_force_velocity_relationship: bool = False, minimize_muscle_fatigue: bool = False, minimize_muscle_force: bool = False, use_sx: bool = True, @@ -87,25 +87,25 @@ def prepare_ocp( Optional if not using the Hmed2018 models objective_dict : dict Dictionary containing parameters related to the objective of the optimization. - custom_constraint : ConstraintList, optional + custom_constraint : ConstraintList, Custom constraints for the OCP. - with_residual_torque : bool, optional + with_residual_torque : bool If residual torque is used. - muscle_force_length_relationship : bool, optional + activate_force_length_relationship : bool If the force length relationship is used. - muscle_force_velocity_relationship : bool, optional + activate_force_velocity_relationship : bool If the force velocity relationship is used. - minimize_muscle_fatigue : bool, optional + minimize_muscle_fatigue : bool Minimize the muscle fatigue. - minimize_muscle_force : bool, optional + minimize_muscle_force : bool Minimize the muscle force. - use_sx : bool, optional + use_sx : bool The nature of the CasADi variables. MX are used if False. - ode_solver : OdeSolverBase, optional + ode_solver : OdeSolverBase The ODE solver to use. - control_type : ControlType, optional + control_type : ControlType The type of control to use. - n_threads : int, optional + n_threads : int The number of threads to use while solving (multi-threading if > 1). Returns @@ -182,8 +182,8 @@ def prepare_ocp( end_node_tracking=end_node_tracking, q_tracking=q_tracking, with_residual_torque=with_residual_torque, - muscle_force_length_relationship=muscle_force_length_relationship, - muscle_force_velocity_relationship=muscle_force_velocity_relationship, + activate_force_length_relationship=activate_force_length_relationship, + activate_force_velocity_relationship=activate_force_velocity_relationship, minimize_muscle_fatigue=minimize_muscle_fatigue, minimize_muscle_force=minimize_muscle_force, ) @@ -252,8 +252,8 @@ def prepare_ocp( name=None, biorbd_path=biorbd_model_path, muscles_model=fes_muscle_models, - muscle_force_length_relationship=muscle_force_length_relationship, - muscle_force_velocity_relationship=muscle_force_velocity_relationship, + activate_force_length_relationship=activate_force_length_relationship, + activate_force_velocity_relationship=activate_force_velocity_relationship, ) for i in range(n_stim) ] @@ -754,8 +754,8 @@ def _sanity_check_fes_models_inputs( end_node_tracking, q_tracking, with_residual_torque, - muscle_force_length_relationship, - muscle_force_velocity_relationship, + activate_force_length_relationship, + activate_force_velocity_relationship, minimize_muscle_fatigue, minimize_muscle_force, ): @@ -832,16 +832,16 @@ def _sanity_check_fes_models_inputs( list_to_check = [ with_residual_torque, - muscle_force_length_relationship, - muscle_force_velocity_relationship, + activate_force_length_relationship, + activate_force_velocity_relationship, minimize_muscle_fatigue, minimize_muscle_force, ] list_to_check_name = [ "with_residual_torque", - "muscle_force_length_relationship", - "muscle_force_velocity_relationship", + "activate_force_length_relationship", + "activate_force_velocity_relationship", "minimize_muscle_fatigue", "minimize_muscle_force", ] diff --git a/tests/shard2/test_fes_dynamics.py b/tests/shard2/test_fes_dynamics.py index 18cdc41c..fd6ea9ee 100644 --- a/tests/shard2/test_fes_dynamics.py +++ b/tests/shard2/test_fes_dynamics.py @@ -43,8 +43,8 @@ def test_pulse_duration_multi_muscle_fes_dynamics(): pulse_duration_dict={"pulse_duration_min": minimum_pulse_duration, "pulse_duration_max": 0.0006, "pulse_duration_bimapping": False}, objective_dict={"custom_objective": objective_functions}, with_residual_torque=True, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=True, + activate_force_length_relationship=True, + activate_force_velocity_relationship=True, use_sx=False, ) @@ -119,8 +119,8 @@ def test_pulse_intensity_multi_muscle_fes_dynamics(): pulse_intensity_dict={"pulse_intensity_min": minimum_pulse_intensity, "pulse_intensity_max": 130, "pulse_intensity_bimapping": False}, objective_dict={"force_tracking": track_forces}, with_residual_torque=False, - muscle_force_length_relationship=True, - muscle_force_velocity_relationship=True, + activate_force_length_relationship=True, + activate_force_velocity_relationship=True, use_sx=False, )