From c9f698d0b6ec80f6a30500d01840fcf68a1de7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Fr=C3=B6hlich?= Date: Wed, 5 Feb 2025 16:10:45 +0000 Subject: [PATCH] fix #2642 (#2643) * fix #2642 * fixup * fixup * add issue as regression test * fix path --- python/sdist/amici/sbml_import.py | 26 +++- python/tests/sbml_models/regression_2642.xml | 130 +++++++++++++++++++ python/tests/test_sbml_import.py | 23 ++++ 3 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 python/tests/sbml_models/regression_2642.xml diff --git a/python/sdist/amici/sbml_import.py b/python/sdist/amici/sbml_import.py index 557ad02d0f..29a9608c4b 100644 --- a/python/sdist/amici/sbml_import.py +++ b/python/sdist/amici/sbml_import.py @@ -2292,9 +2292,29 @@ def _make_initial( sym_math, rateof_to_dummy = _rateof_to_dummy(sym_math) - for species_id, species in self.symbols[SymbolId.SPECIES].items(): - if "init" in species: - sym_math = smart_subs(sym_math, species_id, species["init"]) + # we can't rely on anything else being properly initialized at this point, so we need to + # compute all initial values from scratch, recursively + for var in sym_math.free_symbols: + element_id = str(var) + # already recursive since _get_element_initial_assignment calls _make_initial + if ( + ia := self._get_element_initial_assignment(element_id) + ) is not None: + sym_math = sym_math.subs(var, ia) + elif (species := self.sbml.getSpecies(element_id)) is not None: + # recursive! + init = self._make_initial(get_species_initial(species)) + sym_math = sym_math.subs(var, init) + elif var in self.symbols[SymbolId.SPECIES]: + sym_math = sym_math.subs( + var, self.symbols[SymbolId.SPECIES][var]["init"] + ) + elif ( + element := self.sbml.getElementBySId(element_id) + ) and self.is_rate_rule_target(element): + # no need to recurse here, as value is numeric + init = sp.Float(element.getValue()) + sym_math = sym_math.subs(var, init) sym_math = smart_subs(sym_math, sbml_time_symbol, sp.Float(0)) diff --git a/python/tests/sbml_models/regression_2642.xml b/python/tests/sbml_models/regression_2642.xml new file mode 100644 index 0000000000..63dd9602d4 --- /dev/null +++ b/python/tests/sbml_models/regression_2642.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MR_i_t0 + + + + + MR_m_t0 + + + + + + + 0.00585177319092733 + 0.00606 + + + + + + + + + + + + recycling + MR_i + + synthesis_MR + + MR_m + + + + + + + + + + + + synthesis_MR + + + + + + + + + + + + + + + recycling + MR_i + + + + + + + + + + + + + + + + binding + MR_m + + + + + + + diff --git a/python/tests/test_sbml_import.py b/python/tests/test_sbml_import.py index 87b18da92d..9047b76b4a 100644 --- a/python/tests/test_sbml_import.py +++ b/python/tests/test_sbml_import.py @@ -852,3 +852,26 @@ def test_import_same_model_name(): assert model_module_1c.get_model().getParameters()[0] == 1.0 assert model_module_1c.get_model().module is model_module_1c + + +@skip_on_valgrind +def test_regression_2642(): + sbml_file = Path(__file__).parent / "sbml_models" / "regression_2642.xml" + sbml_importer = amici.SbmlImporter(sbml_file) + model_name = "regression_2642" + with TemporaryDirectory(prefix="regression_2642") as outdir: + sbml_importer.sbml2amici( + model_name=model_name, + output_dir=outdir, + ) + module = amici.import_model_module( + module_name=model_name, module_path=outdir + ) + model = module.getModel() + solver = model.getSolver() + model.setTimepoints(np.linspace(0, 1, 3)) + r = amici.runAmiciSimulation(model, solver) + assert ( + len(np.unique(r.w[:, model.getExpressionIds().index("binding")])) + == 1 + )