From c705173c254c7a1ace7594c20ad3f9cd6803ccdb Mon Sep 17 00:00:00 2001 From: "samira.vandenbogaard" Date: Fri, 6 Dec 2024 14:46:06 +0100 Subject: [PATCH] updated change kcat values to enable changing of kcats in only one direction --- src/PAModelpy/CatalyticEvent.py | 5 ++++- src/PAModelpy/Enzyme.py | 4 ++-- src/PAModelpy/EnzymeSectors.py | 10 +++++++++- src/PAModelpy/PAModel.py | 26 +++----------------------- 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/PAModelpy/CatalyticEvent.py b/src/PAModelpy/CatalyticEvent.py index f6c5c7a..c443118 100644 --- a/src/PAModelpy/CatalyticEvent.py +++ b/src/PAModelpy/CatalyticEvent.py @@ -12,6 +12,7 @@ from warnings import warn from copy import copy, deepcopy import re +from collections import defaultdict class CatalyticEvent(Object): @@ -368,9 +369,10 @@ def change_kcat_values(self, enzyme_kcat_dict : dict): # apply changes to internal dicts (one by one to avoid deleting kcat values) kcats_change = {} + kcats = defaultdict(dict, self.kcats) for enzyme, kcat_dict in enzyme_kcat_dict.items(): # save change in dict - self.kcats[enzyme] = kcat_dict + kcats[enzyme] = {**kcats[enzyme],**kcat_dict} for direction, kcat in kcat_dict.items(): if direction != "f" and direction != "b": warn( @@ -394,6 +396,7 @@ def change_kcat_values(self, enzyme_kcat_dict : dict): #change enzyme variable enzyme_var.kcats[ce_reaction.id][direction] = kcat self._model._change_kcat_in_enzyme_constraint(ce_reaction, enzyme, direction, kcat) + self.kcats = dict(kcats) @staticmethod def _extract_reaction_id_from_catalytic_reaction_id(input_str: str, diff --git a/src/PAModelpy/Enzyme.py b/src/PAModelpy/Enzyme.py index 5e569be..f754708 100644 --- a/src/PAModelpy/Enzyme.py +++ b/src/PAModelpy/Enzyme.py @@ -250,7 +250,7 @@ def change_kcat_values(self, rxn2kcat: Dict): else: catalytic_event_id = f"CE_{CatalyticEvent._extract_reaction_id_from_catalytic_reaction_id(rxn_id, self.enzyme_id_regex)}" # change rxn2kcat dictionary - self.rxn2kcat[rxn_id] = kcats + self.rxn2kcat[rxn_id] = {**self.rxn2kcat[rxn_id],**kcats} # is there already a link between enzyme and reaction? if catalytic_event_id not in self.catalytic_events: warn(f"Reaction {rxn_id} is not associated with enzyme {self.id}. Skip") @@ -912,7 +912,7 @@ def change_kcat_values(self, reaction_kcat_dict: dict): kcats_change = {} for rxn, kcat_dict in reaction_kcat_dict.items(): # save change in dict - self.kcats[rxn.id] = kcat_dict + self.kcats[rxn.id] = {**self.kcats[rxn.id],**kcat_dict} for direction, kcat in kcat_dict.items(): if direction != "f" and direction != "b": warn( diff --git a/src/PAModelpy/EnzymeSectors.py b/src/PAModelpy/EnzymeSectors.py index 1be0b92..1d796ec 100644 --- a/src/PAModelpy/EnzymeSectors.py +++ b/src/PAModelpy/EnzymeSectors.py @@ -1,7 +1,9 @@ from warnings import warn from copy import copy, deepcopy + +from black.trans import defaultdict from cobra import Object, Gene, Model -from typing import Union +from typing import Union, Literal from .Enzyme import Enzyme, EnzymeComplex from .configuration import Config @@ -375,6 +377,12 @@ def _get_model_genes_from_enzyme(self, enzyme_id: str, model: Model) -> list: # state = self.__dict__.copy() # # Handle any non-serializable attributes here # return state + def change_kcat_values(self, rxn_id:str, enzyme_id:str, + kcat_f_b:dict[Literal['f', 'b'], float] + ) -> None: + rxnid2protein = defaultdict(dict ,self.rxn2protein[rxn_id]) + rxnid2protein[enzyme_id] = {**rxnid2protein[enzyme_id],**kcat_f_b} + self.rxn2protein[rxn_id] = dict(rxnid2protein) def __setstate__(self, state): # Restore state from the unpickled state diff --git a/src/PAModelpy/PAModel.py b/src/PAModelpy/PAModel.py index 04c384c..d96b082 100644 --- a/src/PAModelpy/PAModel.py +++ b/src/PAModelpy/PAModel.py @@ -1,5 +1,6 @@ # cobra tools import cobra +from black.trans import defaultdict from cobra import Model, DictList, Reaction, Metabolite, Solution from cobra.io import load_model from cobra.util.context import get_context @@ -1483,7 +1484,6 @@ def change_kcat_value(self, enzyme_id: str, kcats: dict): {'R1': {'f': 10.0, 'b': 5.0}, 'R2': {'f': 7.0, 'b': 3.0}} ``` """ - if self.enzymes.has_id(enzyme_id): enzyme = self.enzymes.get_by_id(enzyme_id) # also change the active enzyme sector @@ -1493,8 +1493,8 @@ def change_kcat_value(self, enzyme_id: str, kcats: dict): for rxn, kcat_f_b in kcats.items(): # if a catalytic reaction is given, then extract the actual reaction id from it using the protein id convention from uniprot rxn2kcat, rxn_id = self._change_catalytic_reaction_to_reaction_id_in_kcatdict(rxn, rxn2kcat) - active_enzyme.rxn2protein[rxn_id] = {enzyme_id: kcat_f_b} - active_enzyme.rxn2protein[rxn] = {enzyme_id: kcat_f_b} + active_enzyme.change_kcat_values(rxn_id, enzyme_id, kcat_f_b) + active_enzyme.change_kcat_values(rxn, enzyme_id, kcat_f_b) enzyme.change_kcat_values(kcats) @@ -1510,26 +1510,6 @@ def _change_catalytic_reaction_to_reaction_id_in_kcatdict(self, rxn: str, rxn2kc return rxn2kcat,rxn - def _change_kcat_in_enzyme_constraint(self, rxn:Union[str, cobra.Reaction], enzyme_id: str, - direction: str, kcat: float): - constraint_id = f'EC_{enzyme_id}_{direction}' - if isinstance(rxn, str): - rxn = self.reactions.get_by_id(rxn) - # change kcat value in the constraint - if kcat == 0: - coeff = 0 - else: - coeff = 1 / (kcat * 3600 * 1e-6) #3600 to convert to /h to /s *1e-6 to make calculations more accurate - if direction == 'f': - self.constraints[constraint_id].set_linear_coefficients({ - rxn.forward_variable: coeff - }) - elif direction == 'b': - self.constraints[constraint_id].set_linear_coefficients({ - rxn.reverse_variable: coeff - }) - self.solver.update() - def _change_kcat_in_enzyme_constraint(self, rxn:Union[str, cobra.Reaction], enzyme_id: str, direction: str, kcat: float): constraint_id = f'EC_{enzyme_id}_{direction}'