Skip to content

Commit e8c5c69

Browse files
committed
feat: enabled changing bounds in exec_sed_task
1 parent 1755a09 commit e8c5c69

File tree

5 files changed

+91
-38
lines changed

5 files changed

+91
-38
lines changed

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# Base OS
1515
FROM python:3.9-slim-buster
1616

17-
ARG VERSION="0.1.16"
17+
ARG VERSION="0.1.17"
1818
ARG SIMULATOR_VERSION="0.22.1"
1919

2020
# metadata

biosimulators_cobrapy/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.1.16'
1+
__version__ = '0.1.17'

biosimulators_cobrapy/core.py

+56-34
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from biosimulators_utils.sedml.data_model import (Task, ModelLanguage, ModelAttributeChange, SteadyStateSimulation, # noqa: F401
2020
Variable)
2121
from biosimulators_utils.sedml.exec import exec_sed_doc as base_exec_sed_doc
22-
from biosimulators_utils.sedml.utils import apply_changes_to_xml_model
2322
from biosimulators_utils.simulator.utils import get_algorithm_substitution_policy
2423
from biosimulators_utils.utils.core import raise_errors_warnings
2524
from biosimulators_utils.warnings import warn, BioSimulatorsWarning
@@ -30,7 +29,6 @@
3029
import cobra.io
3130
import copy
3231
import os
33-
import tempfile
3432

3533
__all__ = [
3634
'exec_sedml_docs_in_combine_archive',
@@ -139,40 +137,23 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None
139137
if preprocessed_task is None:
140138
preprocessed_task = preprocess_sed_task(task, variables, config=config)
141139

140+
# get model
141+
cobra_model = preprocessed_task['model']['model']
142+
142143
# modify model
143-
raise_errors_warnings(validation.validate_model_change_types(task.model.changes, (ModelAttributeChange, )),
144-
error_summary='Changes for model `{}` are not supported.'.format(task.model.id))
145144
if task.model.changes:
146-
model_etree = preprocessed_task['model']['etree']
147-
148-
model = copy.deepcopy(task.model)
149-
for change in model.changes:
150-
change.new_value = str(change.new_value)
151-
152-
apply_changes_to_xml_model(model, model_etree, sed_doc=None, working_dir=None)
153-
154-
model_file, model_filename = tempfile.mkstemp(suffix='.xml')
155-
os.close(model_file)
145+
raise_errors_warnings(validation.validate_model_change_types(task.model.changes, (ModelAttributeChange, )),
146+
error_summary='Changes for model `{}` are not supported.'.format(task.model.id))
156147

157-
model_etree.write(model_filename,
158-
xml_declaration=True,
159-
encoding="utf-8",
160-
standalone=False,
161-
pretty_print=False)
162-
else:
163-
model_filename = task.model.source
164-
165-
# get the model
166-
cobra_model = cobra.io.read_sbml_model(model_filename)
167-
if task.model.changes:
168-
os.remove(model_filename)
148+
model_change_obj_attr_map = preprocessed_task['model']['model_change_obj_attr_map']
149+
for change in task.model.changes:
150+
model_obj, attr_name = model_change_obj_attr_map[change.target]
151+
new_value = float(change.new_value)
152+
setattr(model_obj, attr_name, new_value)
169153

170154
variable_xpath_sbml_id_map = preprocessed_task['model']['variable_xpath_sbml_id_map']
171155
variable_xpath_sbml_fbc_id_map = preprocessed_task['model']['variable_xpath_sbml_fbc_id_map']
172156

173-
# set solver
174-
cobra_model.solver = preprocessed_task['simulation']['solver']
175-
176157
# Load the simulation method specified by ``sim.algorithm``
177158
method_props = preprocessed_task['simulation']['method_props']
178159
method_kw_args = copy.copy(preprocessed_task['simulation']['method_kw_args'])
@@ -241,6 +222,50 @@ def preprocess_sed_task(task, variables, config=None):
241222
model_etree = etree.parse(model.source)
242223
namespaces = get_namespaces_for_xml_doc(model_etree)
243224

225+
# Read the model
226+
cobra_model = cobra.io.read_sbml_model(model.source)
227+
228+
# preprocess model changes
229+
model_change_sbml_id_map = validation.validate_target_xpaths(
230+
model.changes, model_etree, attr='id')
231+
model_change_obj_attr_map = {}
232+
sbml_id_model_obj_map = {'R_' + reaction.id: reaction for reaction in cobra_model.reactions}
233+
namespaces_list = namespaces.values()
234+
invalid_changes = []
235+
for change in model.changes:
236+
sbml_id = model_change_sbml_id_map[change.target]
237+
model_obj = sbml_id_model_obj_map.get(sbml_id, None)
238+
239+
attr_name = None
240+
241+
if model_obj is not None:
242+
_, sep, attr = change.target.partition('/@')
243+
ns, _, attr = attr.partition(':')
244+
if change.target_namespaces.get(ns, None) in namespaces_list:
245+
if attr == 'lowerFluxBound':
246+
attr_name = 'lower_bound'
247+
elif attr == 'upperFluxBound':
248+
attr_name = 'upper_bound'
249+
250+
if attr_name:
251+
model_change_obj_attr_map[change.target] = (model_obj, attr_name)
252+
else:
253+
invalid_changes.append(change.target)
254+
255+
if invalid_changes:
256+
valid_changes = []
257+
for reaction in cobra_model.reactions:
258+
valid_changes.append(
259+
"/sbml:sbml/sbml:model/sbml:listOfReactions/sbml:reaction[@id='R_{}']/@fbc:lowerFluxBound".format(reaction.id))
260+
valid_changes.append(
261+
"/sbml:sbml/sbml:model/sbml:listOfReactions/sbml:reaction[@id='R_{}']/@fbc:upperFluxBound".format(reaction.id))
262+
263+
msg = 'The following changes are invalid:\n {}\n\nThe following targets are valid:\n {}'.format(
264+
'\n '.join(sorted(invalid_changes)),
265+
'\n '.join(sorted(valid_changes)),
266+
)
267+
raise ValueError(msg)
268+
244269
# preprocess variables
245270
variable_xpath_sbml_id_map = validation.validate_target_xpaths(
246271
variables, model_etree, attr='id')
@@ -256,9 +281,6 @@ def preprocess_sed_task(task, variables, config=None):
256281
}
257282
)
258283

259-
# Read the model
260-
cobra_model = cobra.io.read_sbml_model(model.source)
261-
262284
# get the SBML-FBC id of the active objective
263285
active_objective_sbml_fbc_id = get_active_objective_sbml_fbc_id(model.source)
264286

@@ -306,15 +328,15 @@ def preprocess_sed_task(task, variables, config=None):
306328
# Return processed information about the task
307329
return {
308330
'model': {
309-
'etree': model_etree,
331+
'model': cobra_model,
310332
'active_objective_sbml_fbc_id': active_objective_sbml_fbc_id,
333+
'model_change_obj_attr_map': model_change_obj_attr_map,
311334
'variable_xpath_sbml_id_map': variable_xpath_sbml_id_map,
312335
'variable_xpath_sbml_fbc_id_map': variable_xpath_sbml_fbc_id_map,
313336
},
314337
'simulation': {
315338
'algorithm_kisao_id': exec_kisao_id,
316339
'method_props': method_props,
317340
'method_kw_args': method_kw_args,
318-
'solver': cobra_model.solver,
319341
}
320342
}

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
biosimulators_utils[logging] >= 0.1.119
1+
biosimulators_utils[logging] >= 0.1.121
22
cobra
33
kisao
44
lxml

tests/test_core_main.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -226,13 +226,24 @@ def test_exec_sed_task_with_changes(self):
226226
task=task),
227227
]
228228

229+
task.model.changes.append(sedml_data_model.ModelAttributeChange(
230+
target="/sbml:sbml/sbml:model/sbml:listOfReactions/sbml:reaction[@id='R_EX_glc__D_e']/@fbc:lowerFluxBound",
231+
target_namespaces=self.NAMESPACES,
232+
new_value=-1,
233+
))
234+
task.model.changes.append(sedml_data_model.ModelAttributeChange(
235+
target="/sbml:sbml/sbml:model/sbml:listOfReactions/sbml:reaction[@id='R_EX_glc__D_e']/@fbc:upperFluxBound",
236+
target_namespaces=self.NAMESPACES,
237+
new_value=10,
238+
))
229239
preprocessed_task = core.preprocess_sed_task(task, variables)
230240

241+
task.model.changes = []
231242
results, _ = core.exec_sed_task(task, variables, preprocessed_task=preprocessed_task)
232243
numpy.testing.assert_allclose(results['active_objective'].tolist(), 0.8739215069684301, rtol=1e-4, atol=1e-8)
233244

234245
task.model.changes.append(sedml_data_model.ModelAttributeChange(
235-
target="/sbml:sbml/sbml:model/sbml:listOfParameters/sbml:parameter[@id='R_EX_glc__D_e_lower_bound']/@value",
246+
target="/sbml:sbml/sbml:model/sbml:listOfReactions/sbml:reaction[@id='R_EX_glc__D_e']/@fbc:lowerFluxBound",
236247
target_namespaces=self.NAMESPACES,
237248
new_value=-1,
238249
))
@@ -244,6 +255,26 @@ def test_exec_sed_task_with_changes(self):
244255
self.assertLess(results3['active_objective'].tolist(), results['active_objective'].tolist())
245256
self.assertGreater(results3['active_objective'].tolist(), results2['active_objective'].tolist())
246257

258+
task.model.changes = [
259+
sedml_data_model.ModelAttributeChange(
260+
target="/sbml:sbml",
261+
target_namespaces=self.NAMESPACES,
262+
new_value=10,
263+
),
264+
sedml_data_model.ModelAttributeChange(
265+
target="/sbml:sbml/sbml:model/sbml:listOfParameters/sbml:parameter[@id='R_EX_glc__D_e_lower_bound']/@value",
266+
target_namespaces=self.NAMESPACES,
267+
new_value=10,
268+
),
269+
sedml_data_model.ModelAttributeChange(
270+
target="/sbml:sbml/sbml:model/sbml:listOfReactions/sbml:reaction[@id='R_EX_glc__D_e']/@fbc:id",
271+
target_namespaces=self.NAMESPACES,
272+
new_value=10,
273+
),
274+
]
275+
with self.assertRaises(ValueError):
276+
core.preprocess_sed_task(task, variables)
277+
247278
task.model.source = 'not a file'
248279
with self.assertRaises(FileNotFoundError):
249280
core.preprocess_sed_task(task, variables)

0 commit comments

Comments
 (0)