Skip to content

Commit a1c5b9f

Browse files
authored
Merge pull request #8 from biosimulators/rules-and-initial-assignments
Add support for variables controlled by rules and initial assignments
2 parents dd0ec6c + 4ca8e5b commit a1c5b9f

File tree

3 files changed

+54
-30
lines changed

3 files changed

+54
-30
lines changed

biosimulators_pysces/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.1.27'
1+
__version__ = '0.1.32'

biosimulators_pysces/core.py

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
from kisao.utils import get_preferred_substitute_algorithm_by_ids
2626
from kisao.warnings import AlgorithmSubstitutedWarning
2727
import lxml.etree
28-
import numpy
2928
import os
3029
import pysces
3130
import tempfile
31+
import libsbml
3232

3333

3434
__all__ = ['exec_sedml_docs_in_combine_archive', 'exec_sed_doc', 'exec_sed_task', 'preprocess_sed_task']
@@ -155,15 +155,18 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None
155155
model.Simulate()
156156

157157
# extract results
158-
results = model.data_sim.getAllSimData(lbls=False)
158+
results, labels = model.data_sim.getAllSimData(lbls=True)
159159
variable_results = VariableResults()
160160
variable_results_model_attr_map = preprocessed_task['model']['variable_results_model_attr_map']
161161
for variable in variables:
162-
i_results, model_attr_name = variable_results_model_attr_map[(variable.target, variable.symbol)]
163-
if i_results is not None:
164-
variable_results[variable.id] = results[:, i_results][-(sim.number_of_points + 1):]
162+
label = variable_results_model_attr_map[(variable.target, variable.symbol)]
163+
index = labels.index(label)
164+
165+
if index is not None:
166+
variable_results[variable.id] = results[:, index][-(sim.number_of_points + 1):]
165167
else:
166-
variable_results[variable.id] = numpy.full((sim.number_of_points + 1,), getattr(model, model_attr_name))
168+
raise ValueError("No variable " + variable.id + " found in simulation output.")
169+
# variable_results[variable.id] = numpy.full((sim.number_of_points + 1,), getattr(model, model_attr_name))
167170

168171
# log action
169172
if config.LOG:
@@ -223,12 +226,24 @@ def preprocess_sed_task(task, variables, config=None):
223226
variable_target_sbml_id_map = validation.validate_target_xpaths(variables, model_etree, attr='id')
224227

225228
# Read the model
229+
sbml_converted_file, sbml_converted_filename = tempfile.mkstemp(suffix='.xml')
230+
os.close(sbml_converted_file)
231+
226232
sbml_model_filename = task.model.source
227-
pysces_interface = pysces.PyscesInterfaces.Core2interfaces()
233+
sbml_doc = libsbml.readSBMLFromFile(sbml_model_filename)
234+
ia_conv = libsbml.SBMLInitialAssignmentConverter()
235+
ia_conv.setDocument(sbml_doc)
236+
success = ia_conv.convert()
237+
if success != 0:
238+
warn("Initial assignment conversion failed: any initial assignments in this model will remain, which PySCeS may not be able to handle.",
239+
BioSimulatorsWarning)
240+
libsbml.writeSBMLToFile(sbml_doc, sbml_converted_filename)
241+
228242
pysces_model_file, pysces_model_filename = tempfile.mkstemp(suffix='.psc')
243+
pysces_interface = pysces.PyscesInterfaces.Core2interfaces()
229244
os.close(pysces_model_file)
230245
try:
231-
pysces_interface.convertSBML2PSC(sbmlfile=sbml_model_filename, pscfile=pysces_model_filename)
246+
pysces_interface.convertSBML2PSC(sbmlfile=sbml_converted_filename, pscfile=pysces_model_filename)
232247
except Exception as exception:
233248
os.remove(pysces_model_filename)
234249
raise ValueError('Model at {} could not be imported:\n {}'.format(
@@ -297,16 +312,29 @@ def preprocess_sed_task(task, variables, config=None):
297312
ALGORITHM_SUBSTITUTION_POLICY_LEVELS[substitution_policy]
298313
>= ALGORITHM_SUBSTITUTION_POLICY_LEVELS[AlgorithmSubstitutionPolicy.SIMILAR_VARIABLES]
299314
):
300-
warn('CVODE (KISAO_0000019) will be used rather than LSODA (KISAO_0000088) because the model has events',
315+
warn('CVODE (KISAO_0000019) will be used rather than LSODA (KISAO_0000088) because the model has events.',
316+
AlgorithmSubstitutedWarning)
317+
else:
318+
raise AlgorithmDoesNotSupportModelFeatureException('LSODA cannot execute the simulation because the model has events.')
319+
320+
# override algorithm choice if there are rules
321+
if integrator['id'] == 'LSODA' and model.__rules__:
322+
model.mode_integrator = 'CVODE'
323+
substitution_policy = get_algorithm_substitution_policy(config=config)
324+
if (
325+
ALGORITHM_SUBSTITUTION_POLICY_LEVELS[substitution_policy]
326+
>= ALGORITHM_SUBSTITUTION_POLICY_LEVELS[AlgorithmSubstitutionPolicy.SIMILAR_VARIABLES]
327+
):
328+
warn('CVODE (KISAO_0000019) will be used rather than LSODA (KISAO_0000088) because the model has rules.',
301329
AlgorithmSubstitutedWarning)
302330
else:
303-
raise AlgorithmDoesNotSupportModelFeatureException('LSODA cannot execute the simulation because the model has events')
331+
model.mode_integrator = 'LSODA'
304332

305333
if model.mode_integrator == 'CVODE':
306334
model.__settings__['cvode_return_event_timepoints'] = False
307335

308336
# validate and preprocess variables
309-
dynamic_ids = ['Time'] + list(model.species) + list(model.reactions)
337+
dynamic_ids = ['Time'] + list(set(model.species) | set(model.reactions) | set(model.__rules__.keys()))
310338
fixed_ids = (set(model.parameters) | set(model.__compartments__.keys())).difference(set(model.__rules__.keys()))
311339

312340
variable_results_model_attr_map = {}
@@ -317,18 +345,17 @@ def preprocess_sed_task(task, variables, config=None):
317345
for variable in variables:
318346
if variable.symbol:
319347
if variable.symbol == Symbol.time.value:
320-
variable_results_model_attr_map[(variable.target, variable.symbol)] = (0, None)
348+
variable_results_model_attr_map[(variable.target, variable.symbol)] = "Time"
321349
else:
322350
unpredicted_symbols.append(variable.symbol)
323351

324352
else:
325353
sbml_id = variable_target_sbml_id_map[variable.target]
326354
try:
327-
i_dynamic = dynamic_ids.index(sbml_id)
328-
variable_results_model_attr_map[(variable.target, variable.symbol)] = (i_dynamic, None)
355+
variable_results_model_attr_map[(variable.target, variable.symbol)] = sbml_id
329356
except ValueError:
330357
if sbml_id in fixed_ids:
331-
variable_results_model_attr_map[(variable.target, variable.symbol)] = (None, sbml_id)
358+
variable_results_model_attr_map[(variable.target, variable.symbol)] = sbml_id
332359
else:
333360
unpredicted_targets.append(variable.target)
334361

tests/test_core_main.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,12 @@ def test_exec_sed_task_with_changes(self):
174174
task=task,
175175
))
176176

177-
preprocessed_task = core.preprocess_sed_task(task, variables)
178-
179177
task.model.changes = []
180-
results, _ = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task)
178+
results, _ = core.exec_sed_task(task, variables)
181179

182180
task.simulation.output_end_time /= 2
183181
task.simulation.number_of_points = int(task.simulation.number_of_points / 2)
184-
results2, _ = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task)
182+
results2, _ = core.exec_sed_task(task, variables)
185183
for specie in species:
186184
numpy.testing.assert_allclose(results2[specie], results[specie][0:task.simulation.number_of_points + 1])
187185

@@ -191,7 +189,7 @@ def test_exec_sed_task_with_changes(self):
191189
target_namespaces=self.NAMESPACES,
192190
new_value=results2[specie][-1],
193191
))
194-
results3, _ = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task)
192+
results3, _ = core.exec_sed_task(task, variables)
195193
for specie in species:
196194
numpy.testing.assert_allclose(results3[specie], results[specie][task.simulation.number_of_points:], rtol=1e-4)
197195

@@ -201,10 +199,17 @@ def test_exec_sed_task_with_changes(self):
201199
target_namespaces=self.NAMESPACES,
202200
new_value=str(results2[specie][-1]),
203201
))
204-
results3, _ = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task)
202+
results3, _ = core.exec_sed_task(task, variables)
205203
for specie in species:
206204
numpy.testing.assert_allclose(results3[specie], results[specie][task.simulation.number_of_points:], rtol=1e-4)
207205

206+
task2 = copy.deepcopy(task)
207+
task2.model.changes = []
208+
task2.model.source = os.path.join(os.path.dirname(__file__), 'fixtures', 'biomd0000000678.xml')
209+
variables2 = copy.deepcopy(variables[1])
210+
variables2.target = "/sbml:sbml/sbml:model/sbml:listOfParameters/sbml:parameter[@id='dNFAT']"
211+
core.exec_sed_task(task2, [variables2])
212+
208213
def test_exec_sed_task_error_handling(self):
209214
with mock.patch.dict('os.environ', {'ALGORITHM_SUBSTITUTION_POLICY': 'NONE'}):
210215
task = sedml_data_model.Task(
@@ -287,14 +292,6 @@ def test_exec_sed_task_error_handling(self):
287292
core.exec_sed_task(task, variables)
288293
variables[0].symbol = sedml_data_model.Symbol.time
289294

290-
task2 = copy.deepcopy(task)
291-
task2.model.source = os.path.join(os.path.dirname(__file__), 'fixtures', 'biomd0000000678.xml')
292-
variables2 = copy.deepcopy(variables[1:2])
293-
variables2[0].target = "/sbml:sbml/sbml:model/sbml:listOfParameters/sbml:parameter[@id='dNFAT']"
294-
with self.assertRaisesRegex(ValueError, 'targets cannot not be recorded'):
295-
core.exec_sed_task(task2, variables2)
296-
variables[1].target = "/sbml:sbml/sbml:model/sbml:listOfSpecies/sbml:species[@id='AL']"
297-
298295
# algorithm substition
299296
task = sedml_data_model.Task(
300297
model=sedml_data_model.Model(

0 commit comments

Comments
 (0)