From 071e1dfa8ab278c03209f3e868754d7825c4d080 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Mon, 14 Oct 2024 17:49:18 -0700 Subject: [PATCH 01/10] First try to allow models with assignment rules and/or initial assignments in PySCeS. --- biosimulators_pysces/core.py | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/biosimulators_pysces/core.py b/biosimulators_pysces/core.py index c972f47..01c04bd 100644 --- a/biosimulators_pysces/core.py +++ b/biosimulators_pysces/core.py @@ -29,6 +29,7 @@ import os import pysces import tempfile +import libsbml __all__ = ['exec_sedml_docs_in_combine_archive', 'exec_sed_doc', 'exec_sed_task', 'preprocess_sed_task'] @@ -223,12 +224,20 @@ def preprocess_sed_task(task, variables, config=None): variable_target_sbml_id_map = validation.validate_target_xpaths(variables, model_etree, attr='id') # Read the model + sbml_converted_file, sbml_converted_filename = tempfile.mkstemp(suffix='.xml') + os.close(sbml_converted_file) + sbml_model_filename = task.model.source - pysces_interface = pysces.PyscesInterfaces.Core2interfaces() + sbml_doc = libsbml.readSBMLFromFile(sbml_model_filename) + ia_conv = libsbml.SBMLInitialAssignmentConverter() + ia_conv.convert() + new_sbml_file = libsbml.writeSBMLToFile(sbml_doc, sbml_converted_filename) + pysces_model_file, pysces_model_filename = tempfile.mkstemp(suffix='.psc') + pysces_interface = pysces.PyscesInterfaces.Core2interfaces() os.close(pysces_model_file) try: - pysces_interface.convertSBML2PSC(sbmlfile=sbml_model_filename, pscfile=pysces_model_filename) + pysces_interface.convertSBML2PSC(sbmlfile=sbml_converted_filename, pscfile=pysces_model_filename) except Exception as exception: os.remove(pysces_model_filename) raise ValueError('Model at {} could not be imported:\n {}'.format( @@ -297,16 +306,29 @@ def preprocess_sed_task(task, variables, config=None): ALGORITHM_SUBSTITUTION_POLICY_LEVELS[substitution_policy] >= ALGORITHM_SUBSTITUTION_POLICY_LEVELS[AlgorithmSubstitutionPolicy.SIMILAR_VARIABLES] ): - warn('CVODE (KISAO_0000019) will be used rather than LSODA (KISAO_0000088) because the model has events', + warn('CVODE (KISAO_0000019) will be used rather than LSODA (KISAO_0000088) because the model has events.', + AlgorithmSubstitutedWarning) + else: + raise AlgorithmDoesNotSupportModelFeatureException('LSODA cannot execute the simulation because the model has events.') + + # override algorithm choice if there are rules + if integrator['id'] == 'LSODA' and model.__rules__: + model.mode_integrator = 'CVODE' + substitution_policy = get_algorithm_substitution_policy(config=config) + if ( + ALGORITHM_SUBSTITUTION_POLICY_LEVELS[substitution_policy] + >= ALGORITHM_SUBSTITUTION_POLICY_LEVELS[AlgorithmSubstitutionPolicy.SIMILAR_VARIABLES] + ): + warn('CVODE (KISAO_0000019) will be used rather than LSODA (KISAO_0000088) because the model has rules.', AlgorithmSubstitutedWarning) else: - raise AlgorithmDoesNotSupportModelFeatureException('LSODA cannot execute the simulation because the model has events') + model.mode_integrator = 'LSODA' if model.mode_integrator == 'CVODE': model.__settings__['cvode_return_event_timepoints'] = False # validate and preprocess variables - dynamic_ids = ['Time'] + list(model.species) + list(model.reactions) + dynamic_ids = ['Time'] + list(set(model.species) | set(model.reactions) | set(model.__rules__.keys())) fixed_ids = (set(model.parameters) | set(model.__compartments__.keys())).difference(set(model.__rules__.keys())) variable_results_model_attr_map = {} From be8bf2152f499854ab52ecd76a1cd0d45e7b78bc Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Mon, 14 Oct 2024 18:22:28 -0700 Subject: [PATCH 02/10] Fix lint. --- biosimulators_pysces/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biosimulators_pysces/core.py b/biosimulators_pysces/core.py index 01c04bd..1f6c90a 100644 --- a/biosimulators_pysces/core.py +++ b/biosimulators_pysces/core.py @@ -231,7 +231,7 @@ def preprocess_sed_task(task, variables, config=None): sbml_doc = libsbml.readSBMLFromFile(sbml_model_filename) ia_conv = libsbml.SBMLInitialAssignmentConverter() ia_conv.convert() - new_sbml_file = libsbml.writeSBMLToFile(sbml_doc, sbml_converted_filename) + libsbml.writeSBMLToFile(sbml_doc, sbml_converted_filename) pysces_model_file, pysces_model_filename = tempfile.mkstemp(suffix='.psc') pysces_interface = pysces.PyscesInterfaces.Core2interfaces() From 3598c4435c9860b2c8a75cebd058fe5bbb0c4506 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Thu, 17 Oct 2024 15:23:57 -0700 Subject: [PATCH 03/10] Update tests. * The 'preprocessed task' is no longer helping in the test where it was being used; get rid of it. * Now that parameters controlled by rules are valid, don't test to make sure it's invalid; move it to the 'make sure it's valid' section. --- tests/test_core_main.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/tests/test_core_main.py b/tests/test_core_main.py index e3dba92..224dbe4 100644 --- a/tests/test_core_main.py +++ b/tests/test_core_main.py @@ -174,14 +174,12 @@ def test_exec_sed_task_with_changes(self): task=task, )) - preprocessed_task = core.preprocess_sed_task(task, variables) - task.model.changes = [] - results, _ = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task) + results, _ = core.exec_sed_task(task, variables) task.simulation.output_end_time /= 2 task.simulation.number_of_points = int(task.simulation.number_of_points / 2) - results2, _ = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task) + results2, _ = core.exec_sed_task(task, variables) for specie in species: numpy.testing.assert_allclose(results2[specie], results[specie][0:task.simulation.number_of_points + 1]) @@ -191,7 +189,7 @@ def test_exec_sed_task_with_changes(self): target_namespaces=self.NAMESPACES, new_value=results2[specie][-1], )) - results3, _ = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task) + results3, _ = core.exec_sed_task(task, variables) for specie in species: numpy.testing.assert_allclose(results3[specie], results[specie][task.simulation.number_of_points:], rtol=1e-4) @@ -201,10 +199,16 @@ def test_exec_sed_task_with_changes(self): target_namespaces=self.NAMESPACES, new_value=str(results2[specie][-1]), )) - results3, _ = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task) + results3, _ = core.exec_sed_task(task, variables) for specie in species: numpy.testing.assert_allclose(results3[specie], results[specie][task.simulation.number_of_points:], rtol=1e-4) + task2 = copy.deepcopy(task) + task2.model.source = os.path.join(os.path.dirname(__file__), 'fixtures', 'biomd0000000678.xml') + variables2 = copy.deepcopy(variables[1:2]) + variables2[0].target = "/sbml:sbml/sbml:model/sbml:listOfParameters/sbml:parameter[@id='dNFAT']" + core.exec_sed_task(task2, variables2) + def test_exec_sed_task_error_handling(self): with mock.patch.dict('os.environ', {'ALGORITHM_SUBSTITUTION_POLICY': 'NONE'}): task = sedml_data_model.Task( @@ -287,14 +291,6 @@ def test_exec_sed_task_error_handling(self): core.exec_sed_task(task, variables) variables[0].symbol = sedml_data_model.Symbol.time - task2 = copy.deepcopy(task) - task2.model.source = os.path.join(os.path.dirname(__file__), 'fixtures', 'biomd0000000678.xml') - variables2 = copy.deepcopy(variables[1:2]) - variables2[0].target = "/sbml:sbml/sbml:model/sbml:listOfParameters/sbml:parameter[@id='dNFAT']" - with self.assertRaisesRegex(ValueError, 'targets cannot not be recorded'): - core.exec_sed_task(task2, variables2) - variables[1].target = "/sbml:sbml/sbml:model/sbml:listOfSpecies/sbml:species[@id='AL']" - # algorithm substition task = sedml_data_model.Task( model=sedml_data_model.Model( From 051cf944d6ce204b1204c3ca2131d4f1068314a2 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Fri, 18 Oct 2024 12:17:18 -0700 Subject: [PATCH 04/10] Fix label indexes. Seemingly, when we added the rules and converted things through sets, the indexes of the labels can change from the indexes of the results that PySCeS exports. Happily, the results come with labels already, so this switches things up to just use those labels. --- biosimulators_pysces/core.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/biosimulators_pysces/core.py b/biosimulators_pysces/core.py index 1f6c90a..84133c3 100644 --- a/biosimulators_pysces/core.py +++ b/biosimulators_pysces/core.py @@ -156,15 +156,18 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None model.Simulate() # extract results - results = model.data_sim.getAllSimData(lbls=False) + results, labels = model.data_sim.getAllSimData(lbls=True) variable_results = VariableResults() variable_results_model_attr_map = preprocessed_task['model']['variable_results_model_attr_map'] for variable in variables: - i_results, model_attr_name = variable_results_model_attr_map[(variable.target, variable.symbol)] - if i_results is not None: - variable_results[variable.id] = results[:, i_results][-(sim.number_of_points + 1):] + label = variable_results_model_attr_map[(variable.target, variable.symbol)] + index = labels.index(label) + + if index is not None: + variable_results[variable.id] = results[:, index][-(sim.number_of_points + 1):] else: - variable_results[variable.id] = numpy.full((sim.number_of_points + 1,), getattr(model, model_attr_name)) + raise ValueError("No variable " + variable.id + " found in simulation output.") + #variable_results[variable.id] = numpy.full((sim.number_of_points + 1,), getattr(model, model_attr_name)) # log action if config.LOG: @@ -339,7 +342,7 @@ def preprocess_sed_task(task, variables, config=None): for variable in variables: if variable.symbol: if variable.symbol == Symbol.time.value: - variable_results_model_attr_map[(variable.target, variable.symbol)] = (0, None) + variable_results_model_attr_map[(variable.target, variable.symbol)] = "Time" else: unpredicted_symbols.append(variable.symbol) @@ -347,10 +350,10 @@ def preprocess_sed_task(task, variables, config=None): sbml_id = variable_target_sbml_id_map[variable.target] try: i_dynamic = dynamic_ids.index(sbml_id) - variable_results_model_attr_map[(variable.target, variable.symbol)] = (i_dynamic, None) + variable_results_model_attr_map[(variable.target, variable.symbol)] = sbml_id except ValueError: if sbml_id in fixed_ids: - variable_results_model_attr_map[(variable.target, variable.symbol)] = (None, sbml_id) + variable_results_model_attr_map[(variable.target, variable.symbol)] = sbml_id else: unpredicted_targets.append(variable.target) From 0ba5fb0c3c38e4d8f6b257851072d4ababdcee65 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Fri, 18 Oct 2024 12:23:06 -0700 Subject: [PATCH 05/10] Lint fix. --- biosimulators_pysces/core.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/biosimulators_pysces/core.py b/biosimulators_pysces/core.py index 84133c3..b9ce23b 100644 --- a/biosimulators_pysces/core.py +++ b/biosimulators_pysces/core.py @@ -25,7 +25,6 @@ from kisao.utils import get_preferred_substitute_algorithm_by_ids from kisao.warnings import AlgorithmSubstitutedWarning import lxml.etree -import numpy import os import pysces import tempfile @@ -167,7 +166,7 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None variable_results[variable.id] = results[:, index][-(sim.number_of_points + 1):] else: raise ValueError("No variable " + variable.id + " found in simulation output.") - #variable_results[variable.id] = numpy.full((sim.number_of_points + 1,), getattr(model, model_attr_name)) + # variable_results[variable.id] = numpy.full((sim.number_of_points + 1,), getattr(model, model_attr_name)) # log action if config.LOG: @@ -349,7 +348,6 @@ def preprocess_sed_task(task, variables, config=None): else: sbml_id = variable_target_sbml_id_map[variable.target] try: - i_dynamic = dynamic_ids.index(sbml_id) variable_results_model_attr_map[(variable.target, variable.symbol)] = sbml_id except ValueError: if sbml_id in fixed_ids: From 74e4f2e315ccabd97cd3d2ba3a3bc974188a53e0 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Fri, 18 Oct 2024 13:45:44 -0700 Subject: [PATCH 06/10] Fix initial assignment converter; fix test. The initial assignment converter hadn't set the model (whoops), and the test hadn't cleared the old changes. --- biosimulators_pysces/core.py | 3 ++- tests/test_core_main.py | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/biosimulators_pysces/core.py b/biosimulators_pysces/core.py index b9ce23b..7baef04 100644 --- a/biosimulators_pysces/core.py +++ b/biosimulators_pysces/core.py @@ -232,7 +232,8 @@ def preprocess_sed_task(task, variables, config=None): sbml_model_filename = task.model.source sbml_doc = libsbml.readSBMLFromFile(sbml_model_filename) ia_conv = libsbml.SBMLInitialAssignmentConverter() - ia_conv.convert() + ia_conv.setDocument(sbml_doc) + success = ia_conv.convert() libsbml.writeSBMLToFile(sbml_doc, sbml_converted_filename) pysces_model_file, pysces_model_filename = tempfile.mkstemp(suffix='.psc') diff --git a/tests/test_core_main.py b/tests/test_core_main.py index 224dbe4..0ebf0f7 100644 --- a/tests/test_core_main.py +++ b/tests/test_core_main.py @@ -204,10 +204,11 @@ def test_exec_sed_task_with_changes(self): numpy.testing.assert_allclose(results3[specie], results[specie][task.simulation.number_of_points:], rtol=1e-4) task2 = copy.deepcopy(task) + task2.model.changes = [] task2.model.source = os.path.join(os.path.dirname(__file__), 'fixtures', 'biomd0000000678.xml') - variables2 = copy.deepcopy(variables[1:2]) - variables2[0].target = "/sbml:sbml/sbml:model/sbml:listOfParameters/sbml:parameter[@id='dNFAT']" - core.exec_sed_task(task2, variables2) + variables2 = copy.deepcopy(variables[1]) + variables2.target = "/sbml:sbml/sbml:model/sbml:listOfParameters/sbml:parameter[@id='dNFAT']" + core.exec_sed_task(task2, [variables2]) def test_exec_sed_task_error_handling(self): with mock.patch.dict('os.environ', {'ALGORITHM_SUBSTITUTION_POLICY': 'NONE'}): From c8e1a79edc90508fed7d3afafa49fdde8c99eda5 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Fri, 18 Oct 2024 13:58:33 -0700 Subject: [PATCH 07/10] Warn on IA conversion failure. --- biosimulators_pysces/core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/biosimulators_pysces/core.py b/biosimulators_pysces/core.py index 7baef04..337a750 100644 --- a/biosimulators_pysces/core.py +++ b/biosimulators_pysces/core.py @@ -234,6 +234,8 @@ def preprocess_sed_task(task, variables, config=None): ia_conv = libsbml.SBMLInitialAssignmentConverter() ia_conv.setDocument(sbml_doc) success = ia_conv.convert() + if success != 0: + warn("Initial assignment conversion failed: any initial assignments in this model will remain, which PySCeS may not be able to handle.") libsbml.writeSBMLToFile(sbml_doc, sbml_converted_filename) pysces_model_file, pysces_model_filename = tempfile.mkstemp(suffix='.psc') From 7772c7cb423ca7fed31fb4de38aae9cadc42636b Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Fri, 18 Oct 2024 14:34:43 -0700 Subject: [PATCH 08/10] Fix call to 'warn'. --- biosimulators_pysces/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biosimulators_pysces/core.py b/biosimulators_pysces/core.py index 337a750..931600f 100644 --- a/biosimulators_pysces/core.py +++ b/biosimulators_pysces/core.py @@ -235,7 +235,7 @@ def preprocess_sed_task(task, variables, config=None): ia_conv.setDocument(sbml_doc) success = ia_conv.convert() if success != 0: - warn("Initial assignment conversion failed: any initial assignments in this model will remain, which PySCeS may not be able to handle.") + warn("Initial assignment conversion failed: any initial assignments in this model will remain, which PySCeS may not be able to handle.", BioSimulatorsWarning) libsbml.writeSBMLToFile(sbml_doc, sbml_converted_filename) pysces_model_file, pysces_model_filename = tempfile.mkstemp(suffix='.psc') From 3c53723b72558ab27343311ac5fe3dbb5e30d782 Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Fri, 18 Oct 2024 14:39:09 -0700 Subject: [PATCH 09/10] Fix lint. --- biosimulators_pysces/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/biosimulators_pysces/core.py b/biosimulators_pysces/core.py index 931600f..84e1ce0 100644 --- a/biosimulators_pysces/core.py +++ b/biosimulators_pysces/core.py @@ -235,7 +235,8 @@ def preprocess_sed_task(task, variables, config=None): ia_conv.setDocument(sbml_doc) success = ia_conv.convert() if success != 0: - warn("Initial assignment conversion failed: any initial assignments in this model will remain, which PySCeS may not be able to handle.", BioSimulatorsWarning) + warn("Initial assignment conversion failed: any initial assignments in this model will remain, which PySCeS may not be able to handle.", + BioSimulatorsWarning) libsbml.writeSBMLToFile(sbml_doc, sbml_converted_filename) pysces_model_file, pysces_model_filename = tempfile.mkstemp(suffix='.psc') From 4ca8e5bf4e64bb5101d4d6421c7d666c5f5fde7d Mon Sep 17 00:00:00 2001 From: Lucian Smith Date: Fri, 18 Oct 2024 14:47:58 -0700 Subject: [PATCH 10/10] Update version. --- biosimulators_pysces/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biosimulators_pysces/_version.py b/biosimulators_pysces/_version.py index 692b2b8..8dcfa7d 100644 --- a/biosimulators_pysces/_version.py +++ b/biosimulators_pysces/_version.py @@ -1 +1 @@ -__version__ = '0.1.27' +__version__ = '0.1.32'