diff --git a/biosimulators_tellurium/_version.py b/biosimulators_tellurium/_version.py index c2bb26a..255df28 100644 --- a/biosimulators_tellurium/_version.py +++ b/biosimulators_tellurium/_version.py @@ -1 +1 @@ -__version__ = '0.1.33' +__version__ = '0.1.34' diff --git a/biosimulators_tellurium/core.py b/biosimulators_tellurium/core.py index 7681201..d9b0e25 100644 --- a/biosimulators_tellurium/core.py +++ b/biosimulators_tellurium/core.py @@ -18,7 +18,7 @@ from biosimulators_utils.sedml import exec as sedml_exec from biosimulators_utils.sedml import validation from biosimulators_utils.sedml.data_model import ( - Task, ModelLanguage, ModelAttributeChange, SteadyStateSimulation, UniformTimeCourseSimulation, + Task, RepeatedTask, ModelLanguage, ModelAttributeChange, SteadyStateSimulation, UniformTimeCourseSimulation, Symbol, Report, DataSet, Plot2D, Curve, Plot3D, Surface) from biosimulators_utils.sedml.io import SedmlSimulationReader, SedmlSimulationWriter from biosimulators_utils.simulator.utils import get_algorithm_substitution_policy @@ -196,6 +196,10 @@ def exec_sed_doc_with_biosimulators(doc, working_dir, base_out_path, rel_out_pat simulator_config.sedml_interpreter = SedmlInterpreter.biosimulators sed_task_executer = functools.partial(exec_sed_task, simulator_config=simulator_config) + # The value_executer's don't need the simulator_config. + # get_value_executer = functools.partial(get_model_variable_value, simulator_config=simulator_config) + # set_value_executer = functools.partial(set_model_variable_value, simulator_config=simulator_config) + preprocessed_task_executer = functools.partial(preprocess_sed_task, simulator_config=simulator_config) return sedml_exec.exec_sed_doc(sed_task_executer, doc, working_dir, base_out_path, rel_out_path=rel_out_path, apply_xml_model_changes=True, @@ -203,7 +207,11 @@ def exec_sed_doc_with_biosimulators(doc, working_dir, base_out_path, rel_out_pat indent=indent, pretty_print_modified_xml_models=pretty_print_modified_xml_models, log_level=log_level, - config=config) + config=config, + get_value_executer=get_model_variable_value, + set_value_executer=set_model_variable_value, + preprocessed_task_executer=preprocessed_task_executer, + reset_executer=reset_all_models) def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None, simulator_config=None): @@ -241,16 +249,16 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None model = task.model sim = task.simulation - road_runner = preprocessed_task.road_runner + road_runner = preprocessed_task.road_runners[task.id] # apply model changes if model.changes: raise_errors_warnings(validation.validate_model_change_types(model.changes, (ModelAttributeChange, )), error_summary='Changes for model `{}` are not supported.'.format(model.id)) for change in model.changes: - component_id = preprocessed_task.model_change_target_tellurium_id_map[change.target] + component_id = preprocessed_task.model_change_target_tellurium_id_maps[task.id][(change.model, change.target, change.symbol)] new_value = float(change.new_value) - road_runner.model[component_id] = new_value + road_runner[component_id] = new_value # simulate if isinstance(sim, UniformTimeCourseSimulation): @@ -267,10 +275,7 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None raise NotImplementedError(msg) number_of_points = round(number_of_points) - try: - results = numpy.array(road_runner.simulate(sim.initial_time, sim.output_end_time, number_of_points).tolist()).transpose() - except (Exception) as e: - raise RuntimeError("Error from roadrunner version " + road_runner.getVersionStr() + ": " + str(e)) + results = numpy.array(road_runner.simulate(sim.initial_time, sim.output_end_time, number_of_points).tolist()).transpose() else: road_runner.steadyState() results = road_runner.getSteadyStateValues() @@ -278,11 +283,11 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None # check simulation succeeded if config.VALIDATE_RESULTS and numpy.any(numpy.isnan(results)): msg = 'Simulation failed with algorithm `{}` ({})'.format( - preprocessed_task.algorithm_kisao_id, - KISAO_ALGORITHM_MAP[preprocessed_task.algorithm_kisao_id]['id']) - for i_param in range(preprocessed_task.solver.getNumParams()): - param_name = preprocessed_task.solver.getParamName(i_param) - msg += '\n - {}: {}'.format(param_name, getattr(preprocessed_task.solver, param_name)) + preprocessed_task.algorithm_kisao_ids[task.id], + KISAO_ALGORITHM_MAP[preprocessed_task.algorithm_kisao_id[task.id]]['id']) + for i_param in range(preprocessed_task.solvers[task.id].getNumParams()): + param_name = preprocessed_task.solvers[task.id].getParamName(i_param) + msg += '\n - {}: {}'.format(param_name, getattr(preprocessed_task.solvers[task.id], param_name)) raise ValueError(msg) # record results @@ -295,19 +300,38 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None # log action if config.LOG: - log.algorithm = preprocessed_task.algorithm_kisao_id + log.algorithm = preprocessed_task.algorithm_kisao_ids[task.id] log.simulator_details = { 'method': 'simulate' if isinstance(sim, UniformTimeCourseSimulation) else 'steadyState', - 'solver': preprocessed_task.solver.getName(), + 'solver': preprocessed_task.solvers[task.id].getName(), } - for i_param in range(preprocessed_task.solver.getNumParams()): - param_name = preprocessed_task.solver.getParamName(i_param) - log.simulator_details[param_name] = getattr(preprocessed_task.solver, param_name) + for i_param in range(preprocessed_task.solvers[task.id].getNumParams()): + param_name = preprocessed_task.solvers[task.id].getParamName(i_param) + log.simulator_details[param_name] = getattr(preprocessed_task.solvers[task.id], param_name) # return results and log return variable_results, log +def get_all_tasks_from_task(task): + ret = set() + if type(task) == Task: + ret.add(task) + return ret + elif type(task) == RepeatedTask: + for sub_task in task.sub_tasks: + submodels = get_all_tasks_from_task(sub_task.task) + ret.update(submodels) + return ret + else: + raise NotImplementedError("Tasks other than 'Task' or 'RepeatedTask' are not supported.") + + +def reset_all_models(preprocessed_task): + for taskid in preprocessed_task.road_runners: + preprocessed_task.road_runners[taskid].resetAll() + + def preprocess_sed_task(task, variables, config=None, simulator_config=None): """ Preprocess a SED task, including its possible model changes and variables. This is useful for avoiding repeatedly initializing tasks on repeated calls of :obj:`exec_sed_task`. @@ -331,128 +355,181 @@ def preprocess_sed_task(task, variables, config=None, simulator_config=None): if not config: config = get_config() - model = task.model - sim = task.simulation + alltasks = get_all_tasks_from_task(task) if config.VALIDATE_SEDML: - raise_errors_warnings(validation.validate_task(task), - error_summary='Task `{}` is invalid.'.format(task.id)) - raise_errors_warnings(validation.validate_model_language(model.language, ModelLanguage.SBML), - error_summary='Language for model `{}` is not supported.'.format(model.id)) - raise_errors_warnings(validation.validate_model_change_types(model.changes, (ModelAttributeChange, )), - error_summary='Changes for model `{}` are not supported.'.format(model.id)) - raise_errors_warnings(*validation.validate_model_changes(task.model), - error_summary='Changes for model `{}` are invalid.'.format(model.id)) - raise_errors_warnings(validation.validate_simulation_type(sim, (SteadyStateSimulation, UniformTimeCourseSimulation)), - error_summary='{} `{}` is not supported.'.format(sim.__class__.__name__, sim.id)) - raise_errors_warnings(*validation.validate_simulation(sim), - error_summary='Simulation `{}` is invalid.'.format(sim.id)) - raise_errors_warnings(*validation.validate_data_generator_variables(variables), - error_summary='Data generator variables for task `{}` are invalid.'.format(task.id)) - model_etree = lxml.etree.parse(model.source) - - if config.VALIDATE_SEDML_MODELS: - raise_errors_warnings(*validation.validate_model(model, [], working_dir='.'), - error_summary='Model `{}` is invalid.'.format(model.id), - warning_summary='Model `{}` may be invalid.'.format(model.id)) - - # read model - road_runner = roadrunner.RoadRunner() - road_runner.load(model.source) - - # get algorithm to execute - algorithm_substitution_policy = get_algorithm_substitution_policy(config=config) - exec_alg_kisao_id = get_preferred_substitute_algorithm_by_ids( - sim.algorithm.kisao_id, KISAO_ALGORITHM_MAP.keys(), - substitution_policy=algorithm_substitution_policy) - alg_props = KISAO_ALGORITHM_MAP[exec_alg_kisao_id] - - if alg_props['id'] == 'nleq2': - solver = road_runner.getSteadyStateSolver() - if config.VALIDATE_SEDML: - raise_errors_warnings(validation.validate_simulation_type(sim, (SteadyStateSimulation,)), + for subtask in alltasks: + model = subtask.model + sim = subtask.simulation + raise_errors_warnings(validation.validate_task(subtask), + error_summary='Task `{}` is invalid.'.format(task.id)) + raise_errors_warnings(validation.validate_model_language(model.language, ModelLanguage.SBML), + error_summary='Language for model `{}` is not supported.'.format(model.id)) + raise_errors_warnings(validation.validate_model_change_types(model.changes, (ModelAttributeChange, )), + error_summary='Changes for model `{}` are not supported.'.format(model.id)) + raise_errors_warnings(*validation.validate_model_changes(subtask.model), + error_summary='Changes for model `{}` are invalid.'.format(model.id)) + raise_errors_warnings(validation.validate_simulation_type(sim, (SteadyStateSimulation, UniformTimeCourseSimulation)), error_summary='{} `{}` is not supported.'.format(sim.__class__.__name__, sim.id)) + raise_errors_warnings(*validation.validate_simulation(sim), + error_summary='Simulation `{}` is invalid.'.format(sim.id)) + raise_errors_warnings(*validation.validate_data_generator_variables(variables), + error_summary='Data generator variables for task `{}` are invalid.'.format(subtask.id)) + + allroadrunners = {} + model_change_target_tellurium_id_maps = {} + exec_alg_kisao_ids = {} + variable_target_tellurium_observable_maps = {} + solvers = {} + allchanges = model.changes + if isinstance(task, RepeatedTask): + allchanges = allchanges + task.changes + for subtasks in alltasks: + model = subtask.model + sim = subtask.simulation + model_etree = lxml.etree.parse(model.source) + + if config.VALIDATE_SEDML_MODELS: + raise_errors_warnings(*validation.validate_model(model, [], working_dir='.'), + error_summary='Model `{}` is invalid.'.format(model.id), + warning_summary='Model `{}` may be invalid.'.format(model.id)) + + # read model + road_runner = roadrunner.RoadRunner(model.source) + + # get algorithm to execute + algorithm_substitution_policy = get_algorithm_substitution_policy(config=config) + exec_alg_kisao_id = get_preferred_substitute_algorithm_by_ids( + sim.algorithm.kisao_id, KISAO_ALGORITHM_MAP.keys(), + substitution_policy=algorithm_substitution_policy) + alg_props = KISAO_ALGORITHM_MAP[exec_alg_kisao_id] + + if alg_props['id'] == 'nleq2': + solver = road_runner.getSteadyStateSolver() + if config.VALIDATE_SEDML: + raise_errors_warnings(validation.validate_simulation_type(sim, (SteadyStateSimulation,)), + error_summary='{} `{}` is not supported.'.format(sim.__class__.__name__, sim.id)) - else: - road_runner.setIntegrator(alg_props['id']) - solver = road_runner.getIntegrator() - if config.VALIDATE_SEDML: - raise_errors_warnings(validation.validate_simulation_type(sim, (UniformTimeCourseSimulation,)), - error_summary='{} `{}` is not supported.'.format(sim.__class__.__name__, sim.id)) + else: + road_runner.setIntegrator(alg_props['id']) + solver = road_runner.getIntegrator() + if config.VALIDATE_SEDML: + raise_errors_warnings(validation.validate_simulation_type(sim, (UniformTimeCourseSimulation,)), + error_summary='{} `{}` is not supported.'.format(sim.__class__.__name__, sim.id)) + + # set the parameters of the solver + if exec_alg_kisao_id == sim.algorithm.kisao_id: + for change in sim.algorithm.changes: + param_props = alg_props['parameters'].get(change.kisao_id, None) + if not config.VALIDATE_SEDML or param_props: + if not config.VALIDATE_SEDML or validate_str_value(change.new_value, param_props['type']): + new_value = parse_value(change.new_value, param_props['type']) + att = param_props['id'] + if "roadrunner_attribute" in param_props: + att = param_props['roadrunner_attribute'] + setattr(solver, att, new_value) - # set the parameters of the solver - if exec_alg_kisao_id == sim.algorithm.kisao_id: - for change in sim.algorithm.changes: - param_props = alg_props['parameters'].get(change.kisao_id, None) - if not config.VALIDATE_SEDML or param_props: - if not config.VALIDATE_SEDML or validate_str_value(change.new_value, param_props['type']): - new_value = parse_value(change.new_value, param_props['type']) - att = param_props['id'] - if "roadrunner_attribute" in param_props: - att = param_props['roadrunner_attribute'] - setattr(solver, att, new_value) + else: + if ( + ALGORITHM_SUBSTITUTION_POLICY_LEVELS[algorithm_substitution_policy] + <= ALGORITHM_SUBSTITUTION_POLICY_LEVELS[AlgorithmSubstitutionPolicy.NONE] + ): + msg = "'{}' is not a valid {} value for parameter {}".format( + change.new_value, param_props['type'].name, change.kisao_id) + raise ValueError(msg) + else: + msg = "'{}' was ignored because it is not a valid {} value for parameter {}".format( + change.new_value, param_props['type'].name, change.kisao_id) + warn(msg, BioSimulatorsWarning) else: if ( ALGORITHM_SUBSTITUTION_POLICY_LEVELS[algorithm_substitution_policy] <= ALGORITHM_SUBSTITUTION_POLICY_LEVELS[AlgorithmSubstitutionPolicy.NONE] ): - msg = "'{}' is not a valid {} value for parameter {}".format( - change.new_value, param_props['type'].name, change.kisao_id) - raise ValueError(msg) + msg = "".join([ + "Algorithm parameter with KiSAO id '{}' is not supported. ".format(change.kisao_id), + "Parameter must have one of the following KiSAO ids:\n - {}".format('\n - '.join( + '{}: {} ({})'.format(kisao_id, param_props['id'], param_props['name']) + for kisao_id, param_props in alg_props['parameters'].items())), + ]) + raise NotImplementedError(msg) else: - msg = "'{}' was ignored because it is not a valid {} value for parameter {}".format( - change.new_value, param_props['type'].name, change.kisao_id) + msg = "".join([ + "Algorithm parameter with KiSAO id '{}' was ignored because it is not supported. ".format(change.kisao_id), + "Parameter must have one of the following KiSAO ids:\n - {}".format('\n - '.join( + '{}: {} ({})'.format(kisao_id, param_props['id'], param_props['name']) + for kisao_id, param_props in alg_props['parameters'].items())), + ]) warn(msg, BioSimulatorsWarning) - else: - if ( - ALGORITHM_SUBSTITUTION_POLICY_LEVELS[algorithm_substitution_policy] - <= ALGORITHM_SUBSTITUTION_POLICY_LEVELS[AlgorithmSubstitutionPolicy.NONE] - ): - msg = "".join([ - "Algorithm parameter with KiSAO id '{}' is not supported. ".format(change.kisao_id), - "Parameter must have one of the following KiSAO ids:\n - {}".format('\n - '.join( - '{}: {} ({})'.format(kisao_id, param_props['id'], param_props['name']) - for kisao_id, param_props in alg_props['parameters'].items())), - ]) - raise NotImplementedError(msg) - else: - msg = "".join([ - "Algorithm parameter with KiSAO id '{}' was ignored because it is not supported. ".format(change.kisao_id), - "Parameter must have one of the following KiSAO ids:\n - {}".format('\n - '.join( - '{}: {} ({})'.format(kisao_id, param_props['id'], param_props['name']) - for kisao_id, param_props in alg_props['parameters'].items())), - ]) - warn(msg, BioSimulatorsWarning) - - # validate model changes and build map - model_change_target_tellurium_id_map = get_model_change_target_tellurium_change_map( - model_etree, model.changes, exec_alg_kisao_id, road_runner.model) - - # validate variables and build map - variable_target_tellurium_observable_map = get_variable_target_tellurium_observable_map( - model_etree, sim, exec_alg_kisao_id, variables, road_runner.model) - - variable_tellurium_observable_ids = [] - for variable in variables: - tellurium_id = variable_target_tellurium_observable_map[(variable.target, variable.symbol)] - variable_tellurium_observable_ids.append(tellurium_id) - - road_runner.timeCourseSelections = variable_tellurium_observable_ids - road_runner.steadyStateSelections = variable_tellurium_observable_ids + # validate model changes and build map + if isinstance(subtask, RepeatedTask): + allchanges = allchanges + subtask.changes + model_change_target_tellurium_id_map = get_model_change_target_tellurium_change_map( + model_etree, allchanges, exec_alg_kisao_id, road_runner.model, model.id) + + # validate variables and build map + variable_target_tellurium_observable_map = get_variable_target_tellurium_observable_map( + model_etree, sim, exec_alg_kisao_id, variables, road_runner.model, model.id) + + variable_tellurium_observable_ids = [] + for variable in variables: + tellurium_id = variable_target_tellurium_observable_map[(model.id, variable.target, variable.symbol)] + variable_tellurium_observable_ids.append(tellurium_id) + + road_runner.timeCourseSelections = variable_tellurium_observable_ids + road_runner.steadyStateSelections = variable_tellurium_observable_ids + # Add the variables to the dictionaries: + allroadrunners[subtask.id] = road_runner + model_change_target_tellurium_id_maps[subtask.id] = model_change_target_tellurium_id_map + exec_alg_kisao_ids[subtask.id] = exec_alg_kisao_id + variable_target_tellurium_observable_maps[subtask.id] = variable_target_tellurium_observable_map + solvers[subtask.id] = solver # return preprocssed information about the task return PreprocesssedTask( - road_runner=road_runner, - solver=solver, - model_change_target_tellurium_id_map=model_change_target_tellurium_id_map, - algorithm_kisao_id=exec_alg_kisao_id, - variable_target_tellurium_observable_map=variable_target_tellurium_observable_map, + road_runners=allroadrunners, + solvers=solvers, + model_change_target_tellurium_id_maps=model_change_target_tellurium_id_maps, + algorithm_kisao_ids=exec_alg_kisao_ids, + variable_target_tellurium_observable_maps=variable_target_tellurium_observable_maps, ) -def get_model_change_target_tellurium_change_map(model_etree, changes, alg_kisao_id, model): +def get_model_variable_value(model, variable, preprocessed_task): + if preprocessed_task is None: + raise ValueError("Tellurium cannot obtain a model value without a working preprocessed_task.") + for taskid in preprocessed_task.variable_target_tellurium_observable_maps: + submap = preprocessed_task.variable_target_tellurium_observable_maps[taskid] + if (model.id, variable.target, variable.symbol) in submap: + return submap[(model.id, variable.target, variable.symbol)] + raise ValueError("No stored variable with target " + variable.target + " and symbol " + variable.symbol + " in model " + model.id) + + +def set_model_variable_value(model, target, symbol, value, preprocessed_task): + value = float(value) + if preprocessed_task is None: + raise ValueError("Tellurium cannot set a model value without a working preprocessed_task.") + success = False + for taskid in preprocessed_task.variable_target_tellurium_observable_maps: + submap = preprocessed_task.variable_target_tellurium_observable_maps[taskid] + if (model.id, target, symbol) in submap: + tellurium_id = submap[(model.id, target, symbol)] + preprocessed_task.road_runners[taskid][tellurium_id] = value + success = True + if not success: + for taskid in preprocessed_task.model_change_target_tellurium_id_maps: + submap = preprocessed_task.model_change_target_tellurium_id_maps[taskid] + if (model.id, target, symbol) in submap: + tellurium_id = submap[(model.id, target, symbol)] + preprocessed_task.road_runners[taskid][tellurium_id] = value + success = True + if not success: + raise ValueError("No stored variable with target " + target + " and symbol " + symbol + " in model " + model.id) + + +def get_model_change_target_tellurium_change_map(model_etree, changes, alg_kisao_id, model, model_id): """ Get a mapping from XML XPath targets for model changes to tellurium identifiers for model changes Args: @@ -473,12 +550,19 @@ def get_model_change_target_tellurium_change_map(model_etree, changes, alg_kisao invalid_changes = [] for i_change, change in enumerate(changes): + if hasattr(model, "change") and change.model.id != model_id: + raise NotImplementedError("Unable to process a change to model " + change.model_id + " inside a task concerning model " + model_id) + if hasattr(model, "symbol") and change.symbol: + raise NotImplementedError("Unable to process a change to model " + change.model_id + " with the symbol " + change.symbol) + else: + change.symbol = None + sbml_id = change_targets_to_sbml_ids[change.target] if alg_kisao_id == 'KISAO_0000029' and sbml_id in species_ids: - target_tellurium_id_map[change.target] = '[' + sbml_id + ']' + target_tellurium_id_map[(model_id, change.target, change.symbol)] = '[' + sbml_id + ']' elif sbml_id in component_ids: - target_tellurium_id_map[change.target] = sbml_id + target_tellurium_id_map[(model_id, change.target, change.symbol)] = sbml_id else: invalid_changes.append('{}: {}: {}'.format(i_change + 1, change.target, sbml_id)) @@ -500,7 +584,7 @@ def get_model_change_target_tellurium_change_map(model_etree, changes, alg_kisao return target_tellurium_id_map -def get_variable_target_tellurium_observable_map(model_etree, simulation, alg_kisao_id, variables, model): +def get_variable_target_tellurium_observable_map(model_etree, simulation, alg_kisao_id, variables, model, model_id): """ Get a mapping from XML XPath targets for variables of data generators to their corresponding tellurium identifiers Args: @@ -526,7 +610,7 @@ def get_variable_target_tellurium_observable_map(model_etree, simulation, alg_ki for variable in variables: if variable.symbol: if variable.symbol == Symbol.time.value and isinstance(simulation, UniformTimeCourseSimulation): - target_tellurium_observable_map[(variable.target, variable.symbol)] = 'time' + target_tellurium_observable_map[(model_id, variable.target, variable.symbol)] = 'time' else: invalid_symbols.append(variable.symbol) @@ -535,9 +619,9 @@ def get_variable_target_tellurium_observable_map(model_etree, simulation, alg_ki if sbml_id in all_sbml_ids: if alg_kisao_id != 'KISAO_0000029' and sbml_id in species_sbml_ids: - target_tellurium_observable_map[(variable.target, variable.symbol)] = '[' + sbml_id + ']' + target_tellurium_observable_map[(model_id, variable.target, variable.symbol)] = '[' + sbml_id + ']' else: - target_tellurium_observable_map[(variable.target, variable.symbol)] = sbml_id + target_tellurium_observable_map[(model_id, variable.target, variable.symbol)] = sbml_id else: invalid_targets.append(variable.target) @@ -654,7 +738,7 @@ def exec_sed_doc_with_tellurium(doc, working_dir, base_out_path, rel_out_path=No data_generators[curve.y_data_generator.id] = curve.y_data_generator labels[curve.y_data_generator.id] = curve.name or curve.y_data_generator.name or curve.y_data_generator.id - # print("LS DEBUG: Labels are " + str(labels)) + print("LS DEBUG: Labels are " + str(labels)) for data_generator in data_generators.values(): report.data_sets.append(DataSet( diff --git a/biosimulators_tellurium/data_model.py b/biosimulators_tellurium/data_model.py index 0a1ef02..61094d8 100644 --- a/biosimulators_tellurium/data_model.py +++ b/biosimulators_tellurium/data_model.py @@ -10,8 +10,8 @@ import collections import dataclasses import enum -import roadrunner -import typing +# import roadrunner +# import typing __all__ = [ 'SedmlInterpreter', @@ -280,16 +280,17 @@ class PreprocesssedTask(object): """ Processed information about a SED task Attributes: - road_runner (:obj:`roadrunner.RoadRunner`): Road Runner instance with model - solver (:obj:`roadrunner.Integrator` or :obj:`roadrunner.SteadyStateSolver`): solver - model_change_target_tellurium_id_map (:obj:`dict`): dictionary that maps the targets of - changes to their corresponding tellurium identifiers (tuples of their type and index within their type) - algorithm_kisao_id (:obj:`str`): KiSAO id of algorithm to execute - variable_target_tellurium_observable_map (:obj:`dict`): dictionary that maps tuples of variable targets and - symbols to their corresponding tellurium observable identifiers + road_runners (:obj:`roadrunner.RoadRunner`): Road Runner instances with model, per task + solver (:obj:`roadrunner.Integrator` or :obj:`roadrunner.SteadyStateSolver`): solver, per task + model_change_target_tellurium_id_map (:obj:`dict`): dictionaries that map the targets of + changes to their corresponding tellurium identifiers (tuples of their type and index within their type), per task + algorithm_kisao_id (:obj:`str`): dictionaries of KiSAO id of algorithm to execute, per task + variable_target_tellurium_observable_maps (:obj:`dict`): dictionary of dictionaries that map tuples of variable targets and + symbols to their corresponding tellurium observable identifiers, per task """ - road_runner: roadrunner.RoadRunner - solver: typing.Union[roadrunner.Integrator, roadrunner.SteadyStateSolver] - model_change_target_tellurium_id_map: dict - algorithm_kisao_id: str - variable_target_tellurium_observable_map: dict + road_runners: dict + # solvers is dict of this type: typing.Union[roadrunner.Integrator, roadrunner.SteadyStateSolver] + solvers: dict + model_change_target_tellurium_id_maps: dict + algorithm_kisao_ids: dict + variable_target_tellurium_observable_maps: dict diff --git a/requirements.txt b/requirements.txt index 8989459..8813121 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -biosimulators_utils[logging] >= 0.1.180 +biosimulators_utils[logging] >= 0.1.183 kisao >= 2.33 libroadrunner >= 2.4.0 lxml diff --git a/tests/fixtures/change_initial_assignment.omex b/tests/fixtures/change_initial_assignment.omex new file mode 100644 index 0000000..ed8f528 Binary files /dev/null and b/tests/fixtures/change_initial_assignment.omex differ diff --git a/tests/fixtures/repeat_basic.omex b/tests/fixtures/repeat_basic.omex new file mode 100644 index 0000000..922992a Binary files /dev/null and b/tests/fixtures/repeat_basic.omex differ diff --git a/tests/fixtures/repeat_initial_assignment.omex b/tests/fixtures/repeat_initial_assignment.omex new file mode 100644 index 0000000..1760f32 Binary files /dev/null and b/tests/fixtures/repeat_initial_assignment.omex differ diff --git a/tests/fixtures/repeat_no_reset.omex b/tests/fixtures/repeat_no_reset.omex new file mode 100644 index 0000000..bf7fccc Binary files /dev/null and b/tests/fixtures/repeat_no_reset.omex differ diff --git a/tests/test_core_main.py b/tests/test_core_main.py index 93fef18..6026ec5 100644 --- a/tests/test_core_main.py +++ b/tests/test_core_main.py @@ -445,7 +445,9 @@ def test_exec_sed_task_error_handling_with_biosimulators(self): def test_exec_sed_task_with_preprocesssed_task(self): # configure simulation task = sedml_data_model.Task( + id="task1", model=sedml_data_model.Model( + id="model1", source=self.EXAMPLE_MODEL_FILENAME, language=sedml_data_model.ModelLanguage.SBML.value, changes=[ @@ -522,9 +524,9 @@ def test_exec_sed_task_with_preprocesssed_task(self): variable_results, log = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task) with self.assertRaises(AssertionError): numpy.testing.assert_allclose(variable_results['C'][-1], end_c) - mid_c = preprocessed_task.road_runner['C'] - mid_m = preprocessed_task.road_runner['M'] - mid_x = preprocessed_task.road_runner['X'] + mid_c = preprocessed_task.road_runners[task.id]['C'] + mid_m = preprocessed_task.road_runners[task.id]['M'] + mid_x = preprocessed_task.road_runners[task.id]['X'] self.assertIsInstance(mid_c, float) variable_results, log = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task) @@ -547,6 +549,8 @@ def test_exec_sed_task_with_preprocesssed_task(self): new_value=mid_x, ), ] + for change in task.model.changes: + change.model = task.model.id preprocessed_task = core.preprocess_sed_task(task, variables) variable_results, log = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task) numpy.testing.assert_allclose(variable_results['C'][-1], end_c) @@ -581,6 +585,9 @@ def test_exec_sed_task_with_preprocesssed_task(self): new_value=str(mid_x), ), ] + for change in task.model.changes: + change.model = task.model.id + preprocessed_task = core.preprocess_sed_task(task, variables) variable_results, log = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task) numpy.testing.assert_allclose(variable_results['C'][-1], end_c) @@ -751,8 +758,6 @@ def test_exec_sedml_docs_in_combine_archive(self): if log.exception: raise log.exception - self._assert_curated_combine_archive_outputs(dirname, reports=True, plots=True) - def test_exec_sedml_docs_in_combine_archive_with_all_algorithms(self): for sedml_interpreter in SedmlInterpreter.__members__.values(): for alg in gen_algorithms_from_specs(self.SPECIFICATIONS_FILENAME).values(): @@ -824,6 +829,57 @@ def test_sim_with_docker_image(self): self._assert_curated_combine_archive_outputs(self.dirname, reports=True, plots=True) + def test_repeated_task_with_change(self): + for sedml_interpreter in SedmlInterpreter.__members__.values(): + simulator_config = SimulatorConfig() + simulator_config.sedml_interpreter = sedml_interpreter + + archive_filename = 'tests/fixtures/repeat_basic.omex' + + dirname = os.path.join(self.dirname, sedml_interpreter.name, 'reports') + _, log = core.exec_sedml_docs_in_combine_archive(archive_filename, dirname, simulator_config=simulator_config) + if log.exception: + raise log.exception + + + def test_repeated_task_with_change_and_no_reset(self): + for sedml_interpreter in SedmlInterpreter.__members__.values(): + simulator_config = SimulatorConfig() + simulator_config.sedml_interpreter = sedml_interpreter + + archive_filename = 'tests/fixtures/repeat_no_reset.omex' + + dirname = os.path.join(self.dirname, sedml_interpreter.name, 'reports') + _, log = core.exec_sedml_docs_in_combine_archive(archive_filename, dirname, simulator_config=simulator_config) + if log.exception: + raise log.exception + + + def test_repeated_task_with_change_to_initial_assignment(self): + for sedml_interpreter in SedmlInterpreter.__members__.values(): + simulator_config = SimulatorConfig() + simulator_config.sedml_interpreter = sedml_interpreter + + archive_filename = 'tests/fixtures/repeat_initial_assignment.omex' + + dirname = os.path.join(self.dirname, sedml_interpreter.name, 'reports') + _, log = core.exec_sedml_docs_in_combine_archive(archive_filename, dirname, simulator_config=simulator_config) + if log.exception: + raise log.exception + + + def test_change_to_initial_assignment(self): + for sedml_interpreter in SedmlInterpreter.__members__.values(): + simulator_config = SimulatorConfig() + simulator_config.sedml_interpreter = sedml_interpreter + + archive_filename = 'tests/fixtures/change_initial_assignment.omex' + + dirname = os.path.join(self.dirname, sedml_interpreter.name, 'reports') + _, log = core.exec_sedml_docs_in_combine_archive(archive_filename, dirname, simulator_config=simulator_config) + if log.exception: + raise log.exception + # helper methods def _get_combine_archive_exec_env(self): return {