Skip to content

Commit

Permalink
feat: build integration layer for multitfa
Browse files Browse the repository at this point in the history
  • Loading branch information
carrascomj committed Oct 4, 2022
1 parent d327e4b commit 11b0a99
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 3 deletions.
2 changes: 1 addition & 1 deletion docs/03-thermodynamics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ or the thermodynamic constraint, see the
)
# constrain the model objective so that the feashibility relaxation recovers growth
tmodel_prot.reactions.BIOMASS_Ecoli_core_w_GAM.lower_bound = solution.objective_value
iis, status = geckopy.integration.relax_thermo_proteins(
iis, status = geckopy.integration.relaxation.relax_thermo_proteins(
tmodel_prot,
prot_candidates=[prot.id for prot in tmodel_prot.proteins],
objective_rule=geckopy.experimental.relaxation.Objective_rule.MIN_ELASTIC_SUM
Expand Down
1 change: 0 additions & 1 deletion geckopy/integration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .relaxation import relax_thermo_concentrations_proteins, relax_thermo_proteins
122 changes: 122 additions & 0 deletions geckopy/integration/multitfa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
from copy import copy, deepcopy

import geckopy
from cobra.core.dictlist import DictList

from multitfa.core import tmodel
from multitfa.core import Thermo_met, thermo_reaction


class ThermoProtReaction(thermo_reaction):
"""
Class representation of thermo reaction Object. We calculate the required thermodynamic constraints for performing tMFA. To do the constraints, we need Gibbs energy of reaction and transport.
Parameters
----------
cobra_rxn : cobra.core.Reaction
Cobra reaction object, to copy the attributes from. We copy metabolites and genes.
updated_model : core.tmodel, optional
tmodel object, with updated thermo properties, by default None
"""

def __init__(
self,
cobra_rxn,
updated_model=None,
):
self._model = updated_model
do_not_copy_by_ref = {"_model", "_metabolites", "_genes"}
for attr, value in cobra_rxn.__dict__.items():
if attr not in do_not_copy_by_ref:
self.__dict__[attr] = copy(value)

self._metabolites = {}
for met, stoic in cobra_rxn._metabolites.items():
# this is the only change; also account for proteins
new_met = self.model.metabolites.get_by_id(met.id) if met in self.model.metabolites else self.model.proteins.get_by_id(met.id)
self._metabolites[new_met] = stoic
new_met._reaction.add(self)
self._genes = set()
for gene in cobra_rxn._genes:
new_gene = self.model.genes.get_by_id(gene.id)
self._genes.add(new_gene)
new_gene._reaction.add(self)


class ThermoProtModel(tmodel):
def __init__(
self,
model,
Exclude_list=[],
tolerance_integral=1e-9,
compartment_info=None,
membrane_potential=None,
exclude_metabolites=[],
):

self.compartment_info = compartment_info
self.membrane_potential = membrane_potential

do_not_copy_by_ref = {
"metabolites",
"reactions",
"proteins",
"genes",
"notes",
"annotation",
}
for attr in model.__dict__:
if attr not in do_not_copy_by_ref:
self.__dict__[attr] = model.__dict__[attr]

self.metabolites = DictList()
do_not_copy_by_ref = {"_reaction", "_model"}
for metabolite in model.metabolites:
new_met = Thermo_met(
metabolite=metabolite,
updated_model=self,
)
self.metabolites.append(new_met)

self.genes = DictList()

for gene in model.genes:
new_gene = gene.__class__(None)
for attr, value in gene.__dict__.items():
if attr not in do_not_copy_by_ref:
new_gene.__dict__[attr] = (
copy(value) if attr == "formula" else value
)
new_gene._model = self
self.genes.append(new_gene)
self.proteins = DictList()
for protein in model.proteins:
new_prot = Thermo_met(
metabolite=protein,
updated_model=self,
)
# proteins do not participate in dGf calculations
new_prot.is_ignore = True
self.proteins.append(new_prot)
print(self.proteins)

self.reactions = DictList()
do_not_copy_by_ref = {"_model", "_metabolites", "_genes"}
for reaction in model.reactions:
# this is custom to make the reaction aware of proteins
new_reaction = ThermoProtReaction(
cobra_rxn=reaction,
updated_model=self,
)
self.reactions.append(new_reaction)

try:
self._solver = deepcopy(model.solver)
# Cplex has an issue with deep copies
except Exception: # pragma: no cover
self._solver = copy(model.solver) # pragma: no cover

self.Exclude_list = Exclude_list
self.solver.configuration.tolerances.integrality = tolerance_integral
self._var_update = False
15 changes: 15 additions & 0 deletions tests/test_integration/test_multitfa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import pytest
from optlang import available_solvers

from geckopy.integration.multitfa import ThermoProtModel


@pytest.mark.skipif(not (available_solvers["GUROBI"] or available_solvers["CPLEX"]))
def test_protein_constrain_affects_multitfa_solution(ec_model_core):
"""Check thermo model returns different solution when protein is constrained."""
thermo_model = ThermoProtModel(ec_model_core.copy())
tsol = thermo_model.slim_optimize()
ec_model_core.proteins.prot_P25516.add_concentration(2e-5)
thermo_model = ThermoProtModel(ec_model_core)
tsol_prot = thermo_model.slim_optimize()
assert pytest.approx(tsol) != tsol_prot
2 changes: 1 addition & 1 deletion tests/test_integration/test_relaxation.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from geckopy.experimental import from_copy_number
from geckopy.experimental.relaxation import Objective_rule
from geckopy.integration import (
from geckopy.integration.relaxation import (
relax_thermo_concentrations_proteins,
relax_thermo_proteins,
)
Expand Down

0 comments on commit 11b0a99

Please sign in to comment.