Skip to content

Commit

Permalink
fixed errors: CE list in enzymecomplexes were emptied upon initializa…
Browse files Browse the repository at this point in the history
…tion.
  • Loading branch information
SamiralVdB committed Sep 20, 2024
1 parent 619b379 commit bfd3f42
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 82 deletions.
50 changes: 28 additions & 22 deletions Scripts/pam_generation_uniprot_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,25 +435,31 @@ def filter_sublists(nested_list, target_string):
return [sublist for sublist in nested_list if target_string in sublist]

if __name__ == '__main__':
ecoli_pam = set_up_ecoli_pam(sensitivity=False)
# ecoli_pam.objective = ecoli_pam.BIOMASS_REACTION
ecoli_pam.change_reaction_bounds('EX_glc__D_e', -10, 0)
ecoli_pam.optimize()
# import pickle
#
# with open('path_to_your_pickle_file.pkl', 'wb') as file:
# pickle.dump(ecoli_pam, file)
# with open('path_to_your_pickle_file.pkl', 'rb') as file:
# ob = pickle.load(file)


# for enz_var in ecoli_pam.enzyme_variables:
# if enz_var not in ob.enzyme_variables:
# print(enz_var)
# for ce in ecoli_pam.catalytic_events:
# ce_ob = ob.catalytic_events.get_by_id(ce.id)
# for var in ce.enzyme_variables:
# if cobra.DictList(ce_ob.enzyme_variables).has_id(var.id):
# print(var, ce)
# print(ce.enzyme_variables)
# print(ce_ob.enzyme_variables)
VALID_DATA_PATH = os.path.join('Data', 'Ecoli_phenotypes', 'Ecoli_phenotypes_py_rev.xls')
valid_data_df = pd.read_excel(VALID_DATA_PATH, sheet_name='Yields').sort_values('BIOMASS_Ec_iML1515_core_75p37M',
ascending = False)
max_mu = valid_data_df.at[0,'BIOMASS_Ec_iML1515_core_75p37M']

init_kcat = 11

for i in range(1,4,2):
pam = set_up_ecoli_pam(sensitivity=False)
pam.change_reaction_bounds('EX_glc__D_e', -1e3, 0)
for enzyme in pam.enzymes:
kcats = enzyme.rxn2kcat.copy()
for rxn, kcat_dict in kcats.items():
if '3PEPTabcpp' in rxn:
print(enzyme, kcat_dict)
#if the enzyme is part of a complex it has only zero kcats
if all([val == 0 for val in kcat_dict.values()]):
continue
for dir, kcat in kcat_dict.items():
if kcat == 11:
kcats[rxn][dir] = init_kcat*i
pam.change_kcat_value(enzyme.id, kcats)
pam.optimize()
if pam.objective.value >= max_mu*0.9:
print(init_kcat*i, pam.objective.value)
break
else:
print(i, pam.objective.value)
53 changes: 47 additions & 6 deletions src/PAModelpy/CatalyticEvent.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ def model(self, model):
})
self.constraints[self.id] = ce_constraint

#add enzymes to the model if they are not there already
for enzyme in self.enzymes:
if enzyme in self._model.enzymes:
self.constraints = {**self.constraints, **enzyme._constraints}
Expand Down Expand Up @@ -296,6 +295,7 @@ def remove_enzymes(self, enzyme_list: list):
A list with PAModelpy.Package.Enzyme objects to be removed. If a list of identifiers (str)
is provided, the corresponding enzyme will be obtained from the CatalyticEvent.enzymes attribute.
"""

# return lists back to dictlist after unpickling
if isinstance(self.enzymes, list):
self.enzymes = DictList(self.enzymes)
Expand All @@ -318,7 +318,6 @@ def remove_enzymes(self, enzyme_list: list):
)
pass

for enz in enzyme_list:
# remove from kcat dict
del self.kcats[enz]
# remove from enzymes dictlist
Expand All @@ -329,15 +328,15 @@ def remove_enzymes(self, enzyme_list: list):
self.enzyme_variables.remove(enzyme_var)
# set coefficient in constraint to 0
for constraint in set(
[cons for name, cons in self.constraints.items() if enz.id in name]
[cons for name, cons in self.constraints.items() if (f'EC_{enz.id}' in name)|(f'{enz.id}_m' in name)]
):
if constraint in self._model.constraints.values():
self._model.remove_cons_vars([constraint])
# remove constraint from list of r=constraints
del self.constraints[constraint.name]

# if there are no enzymes associated to the reaction anymore, the reaction flux will be 0
if len(self.enzymes) == 0:
if len(self.enzymes) == 0 and self._model is not None:
no_enz_constraint_f = self._model.problem.Constraint(
Zero, name=f"{self.rxn_id}_no_enzyme_f", lb=0, ub=0
)
Expand Down Expand Up @@ -398,15 +397,57 @@ def change_kcat_values(self, enzyme_kcat_dict : dict):

@staticmethod
def _extract_reaction_id_from_catalytic_reaction_id(input_str: str,
protein_id_pattern:str = r'(?:[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2})'
protein_id_pattern:str = r'(?:[OPQ][0-9][A-Z0-9]{3}[0-9]|[A-NR-Z][0-9]([A-Z][A-Z0-9]{2}[0-9]){1,2})',
default_enzyme_id_pattern:str = r'E[0-9][0-9]*'
) -> str:

"""
Extracts the reaction ID from a catalytic reaction string by removing
any associated protein IDs in various formats.
The function identifies and strips off protein IDs from the input string.
Protein IDs can be in one of two formats:
1. Standard protein identifiers (by default Uniprot ids: e.g., 'P0ABJ1', 'Q1234X').
2. Protein IDs for unknown enzymes in the format 'E<number>' (e.g., 'E1', 'E10', 'E534').
If the string starts with 'CE_', the 'CE_' prefix is removed before processing.
Args:
input_str (str): The input string representing a catalytic reaction ID,
which may contain associated protein IDs.
protein_id_pattern (str): Regular expression pattern for matching
standard protein IDs. Defaults to a UniProt-based regex.
default_enzyme_id_pattern (str): Regular expression pattern for matching protein
IDs in the format 'E<number>' (e.g., 'E1', 'E99'). Defaults to 'E[0-9][0-9]*'.
Returns:
str: The extracted reaction ID with any protein IDs removed.
Examples:
>>> _extract_reaction_id_from_catalytic_reaction_id('CE_CYTBO3_4pp_P0ABJ1_P0ABJ5_E123')
'CYTBO3_4pp'
>>> _extract_reaction_id_from_catalytic_reaction_id('CE_REACTID_E1_E534')
'REACTID'
>>> _extract_reaction_id_from_catalytic_reaction_id('LPLIPAL2ATE140_E3')
'LPLIPAL2ATE140'
Notes:
- The function will strip protein IDs from both the standard format
(e.g., 'P0ABJ1', 'Q1234X') and the 'E<number>' format.
- If no valid protein ID is found, the reaction ID is returned as-is.
- The function assumes that valid reaction IDs are located at the
beginning of the string or after the 'CE_' prefix.
"""

# Remove the 'CE_' prefix if it exists
if input_str.startswith('CE_'):
input_str = input_str[3:]

# Define the regex pattern to match protein IDs
protein_id_regex = re.compile(r'_'+protein_id_pattern)
protein_id_regex = re.compile(r'_'+protein_id_pattern+ r'|' + r'_' + default_enzyme_id_pattern
)

# split off all protein ids from the reaction
reaction_id = protein_id_regex.split(input_str)[0]
Expand Down
62 changes: 33 additions & 29 deletions src/PAModelpy/Enzyme.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,13 @@

from .CatalyticEvent import CatalyticEvent
from typing import Dict, Union, Optional
from cobra import DictList
from warnings import warn


def _change_catalytic_event_list_to_dictlist_after_unpickling(self):
# return lists back to dictlist after unpickling
if not isinstance(self.catalytic_events,DictList):
self.enzymes = DictList(self.catalytic_events)


class Enzyme(Object):
"""Upper level Enzyme object containing information about the enzyme and links to the EnzymeVariables for each reaction the enzyme catalyzes.
Expand Down Expand Up @@ -303,16 +300,20 @@ def remove_catalytic_event(self, catalytic_event: Union[CatalyticEvent, str]):
catalytic_event (Union[CatalyticEvent, str]): CatalyticEvent or str, catalytic event or identifier to remove.
"""
_change_catalytic_event_list_to_dictlist_after_unpickling(self)
if isinstance(catalytic_event, str):
try:
catalytic_event = self.catalytic_events.get_by_id(catalytic_event)
except:
print(
f"Catalytic event {catalytic_event} is not related to this enzyme and can thus not be removed!"
)

# remove the event from the DictList
self.catalytic_events.remove(catalytic_event)
if catalytic_event in self.catalytic_events:
if isinstance(catalytic_event, str):
catalytic_event = self.catalytic_events.get_by_id(catalytic_event)

# remove kcat association
if catalytic_event.rxn_id in self.rxn2kcat:
del self.rxn2kcat[catalytic_event.rxn_id]

self.catalytic_events.remove(catalytic_event)
#remove enzyme from catalytic event
if self in catalytic_event.enzymes:
catalytic_event.remove_enzymes([self])


def __copy__(self) -> "Enzyme":
Expand Down Expand Up @@ -397,17 +398,28 @@ def __init__(

def add_enzymes(self, enzymes: DictList):
for enzyme in enzymes:
if enzyme not in self.enzymes:
if enzyme not in self.enzymes:# and not isinstance(enzyme, EnzymeComplex):
self.enzymes.append(enzyme)
enzyme.enzyme_complex.append(self.id)
self.molmass += enzyme.molmass
#remove associated constraints from the model
if self._model is not None:
if enzyme in self._model.enzymes:
for rxn in self.reactions:
self._model.remove_enzyme_reaction_association(enzyme, rxn)
else:
self._model.enzymes.append(enzyme)

#remove link between enzyme and reaction
#do not remove the relation with any enzyme complex
if not isinstance(enzyme, EnzymeComplex):
# remove link to enzyme
for rxn in self.reactions:
if rxn in enzyme.rxn2kcat:
del enzyme.rxn2kcat[rxn]

# remove associated constraints from the model
if self._model is not None:
if enzyme in self._model.enzymes:
for rxn in self.reactions:
self._model.remove_enzyme_reaction_association(enzyme, rxn)
else:
self._model.enzymes.append(enzyme)



def __copy__(self) -> "EnzymeComplex":
"""Copy the enzyme complex.
Expand Down Expand Up @@ -835,16 +847,8 @@ def remove_catalytic_event(self, catalytic_event: Union[CatalyticEvent, str]):
"""
_change_catalytic_event_list_to_dictlist_after_unpickling(self)

if isinstance(catalytic_event, str):
try:
catalytic_event = self.catalytic_events.get_by_id(catalytic_event)
except:
print(
f"Catalytic event {catalytic_event} is not related to this enzyme and can thus not be removed!"
)

# remove the event from the DictList
self.catalytic_events.remove(catalytic_event.id)
if catalytic_event in self.catalytic_events:
self.catalytic_events.remove(catalytic_event)

def remove_reactions(self, reaction_list: list):
"""Remove reactions from the enzyme variable and remove the reactions from the
Expand Down
5 changes: 3 additions & 2 deletions src/PAModelpy/EnzymeSectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,21 +219,21 @@ def add(self, model):
genes=gene_list,
enzymes=[enzyme]
)

model.add_enzymes([enzyme])
#add relation to rxn2protein dictionary

self.rxn2protein[rxn_id] = {**self.rxn2protein[rxn_id],
**{enzyme_complex_id:kcat}}
self.constraints += [enzyme]
self.variables.append(enzyme.enzyme_variable)

else:
enz_complex = model.enzymes.get_by_id(enzyme_complex_id)
self._add_reaction_to_enzyme(model, enz_complex, rxn_id, kcat)
enz_complex.reactions.append(rxn_id)
enz_complex.add_enzymes([enzyme])
else:

else:
model.add_enzymes([enzyme])

self.constraints += [enzyme]
Expand All @@ -244,6 +244,7 @@ def add(self, model):

# adding to the enzyme sector object for easy removal
model.tpc += 1

return model

def check_kcat_values(self, model, reaction, enzyme_dict):
Expand Down
Loading

0 comments on commit bfd3f42

Please sign in to comment.