Skip to content

Commit

Permalink
added functionality to remove the active enzyme sector + unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
SamiralVdB committed Jul 3, 2024
1 parent 26f0d39 commit 5b88d94
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 54 deletions.
1 change: 0 additions & 1 deletion src/PAModelpy/Enzyme.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
- Enzyme: Constraints relating enzymes to reactions. Including upper and lower bound enzyme constraints
- EnzymeVariable: Variable related to an enzyme. The value of this variable represent the concentration.
"""
import PAModelpy.Enzyme
import cobra.core
import cobra
from cobra import Reaction, Object
Expand Down
146 changes: 99 additions & 47 deletions src/PAModelpy/PAModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1290,23 +1290,24 @@ def remove_catalytic_events(self,
self.remove_cons_vars(enzyme._constraints)

def remove_sectors(self,
sectors: Union[str, Sector, ActiveEnzymeSector, List[Union[str, Sector, ActiveEnzymeSector]]]
sectors: Union[str, Sector, ActiveEnzymeSector]
)-> None:
"""Remove sections from the model.
Also removes associated CatalyticEvents if they exist.
Parameters
----------
secotrs : list or sector or str
Args:
sectors : list or sector or str
A list with sector (`PAModelpy.Sector` or `PAModelpy.ActiveEnzymeSector`), or their id's, to remove.
A single sector will be placed in a list. Str will be placed in a list and used to
find the secotr in the model.
find the sector in the model.
"""
if isinstance(sectors, str) or hasattr(sectors, "id"):
sectors = [sectors]

for sector in sectors:
if isinstance(sector, str): sector = self.sectors.get_by_id(sector)

print(f'Removing the following protein sector: {sector.id}\n')
# remove the connection to the model
sector._model = None
Expand All @@ -1317,50 +1318,101 @@ def remove_sectors(self,
except ValueError:
warnings.warn(f"{sector.id} not in {self}")

# remove the sector from the total protein if it is there
if self.TOTAL_PROTEIN_CONSTRAINT_ID in self.constraints.keys():
# remove parts of constraint corresponding to the enzyme sector from the total_protein_constraint
# 1. add the intercept value from the sum of protein (Total_protein == Etot)
tpc_ub = self.constraints[self.TOTAL_PROTEIN_CONSTRAINT_ID].ub
self.constraints[self.TOTAL_PROTEIN_CONSTRAINT_ID].ub = tpc_ub + sector.intercept

# 2. remove link between flux and enzyme concentration
# link enzyme concentration in the sector to the total enzyme concentration
lin_rxn = self.reactions.get_by_id(sector.id_list[0])
self.constraints[self.TOTAL_PROTEIN_CONSTRAINT_ID].set_linear_coefficients({
lin_rxn.forward_variable: 0,
lin_rxn.reverse_variable: 0
})

#check if the sector is the ActiveEnzymeSector, this needs to have a different removal mechanism
if isinstance(sector, ActiveEnzymeSector):
self.remove_active_enzymes_sector(sector)
else:
self.remove_linear_sector(sector)

def remove_active_enzymes_sector(self, sector: ActiveEnzymeSector) -> None:
"""
Remove an active enzyme sector from the model.
This function performs the following steps:
1. Removes all enzymes associated with the sector.
2. Removes all catalytic events associated with the sector.
3. If a total protein constraint exists, it removes this constraint.
4. Deletes the sector constraint from the model's easy lookup.
5. Removes the sector from the model and disconnects its link to the model.
Args:
sector (ActiveEnzymeSector): The active enzyme sector to be removed.
Returns:
None
"""

self.remove_enzymes(self.enzymes.copy())
self.remove_catalytic_events(self.catalytic_events)
if self.TOTAL_PROTEIN_CONSTRAINT_ID in self.constraints.keys():
# remove total_protein_constraint
self.remove_cons_vars(self.constraints[self.TOTAL_PROTEIN_CONSTRAINT_ID])

# remove the sector and its connection to the model
self.sectors.remove(sector)
sector._model = None

def remove_linear_sector(self, sector: Union[UnusedEnzymeSector, TransEnzymeSector, CustomSector]) -> None:
"""
Remove a linear sector from the model.
This function performs the following steps:
1. If a total protein constraint exists, it adjusts the constraint to remove the sector's contribution.
2. Removes the associated constraints and variables.
3. Deletes the sector constraint from the model's easy lookup.
4. Removes the sector from the model and disconnects its link to the model.
Args:
sector (Union[UnusedEnzymeSector, TransEnzymeSector, CustomSector]): The linear sector to be removed.
Returns:
None
"""
# remove the sector from the total protein if it is there
if self.TOTAL_PROTEIN_CONSTRAINT_ID in self.constraints.keys():
# remove parts of constraint corresponding to the enzyme sector from the total_protein_constraint
# 1. add the intercept value from the sum of protein (Total_protein == Etot)
tpc_ub = self.constraints[self.TOTAL_PROTEIN_CONSTRAINT_ID].ub
self.constraints[self.TOTAL_PROTEIN_CONSTRAINT_ID].ub = tpc_ub + sector.intercept

# 2. remove link between flux and enzyme concentration
# link enzyme concentration in the sector to the total enzyme concentration
lin_rxn = self.reactions.get_by_id(sector.id_list[0])
self.constraints[self.TOTAL_PROTEIN_CONSTRAINT_ID].set_linear_coefficients({
lin_rxn.forward_variable: 0,
lin_rxn.reverse_variable: 0
})

else:

# remove the associated constraints
for constraint in sector.constraints:
if isinstance(constraint, Enzyme):
self.remove_enzymes([constraint])
# check if constraint is in the solver
if constraint in self.constraints.values():
self.remove_cons_vars([constraint])
self.solver.update()

# remove the associated variables
for variable in sector.variables:
if isinstance(variable, CatalyticEvent):
self.remove_catalytic_events([variable])
else:
self.remove_cons_vars([variable])
self.solver.update()
# remove reference to the sector variables in all groups
associated_groups = self.get_associated_groups(variable)
for group in associated_groups:
group.remove_members(variable)

# remove sector constraint from model easy lookup:
del self.sector_constraints[sector.id]

# remove the sector and its connection to the model
self.sectors.remove(sector)
sector._model = None

#remove the associated constraints
for constraint in sector.constraints:
if isinstance(constraint, Enzyme):
self.remove_enzymes([constraint])
#check if constraint is in the solver
if constraint in self.constraints.values():
self.remove_cons_vars([constraint])
self.solver.update()

#remove the associated variables
for variable in sector.variables:
if isinstance(variable, CatalyticEvent):
self.remove_catalytic_events([variable])
else:
self.remove_cons_vars([variable])
self.solver.update()
# remove reference to the sector variables in all groups
associated_groups = self.get_associated_groups(variable)
for group in associated_groups:
group.remove_members(variable)

# remove sector constraint from model easy lookup:
del self.sector_constraints[sector.id]

#remove the sector and its connection to the model
self.sectors.remove(sector)
sector._model = None

def test(self, glc_flux: Union[int, float]=10):
"""
Expand Down
8 changes: 7 additions & 1 deletion src/PAModelpy/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
print('Loading PAModelpy modules version 0.0.3.4')
print('Loading PAModelpy modules version 0.0.3.11')

from .Enzyme import *
from .EnzymeSectors import *
from .CatalyticEvent import *
from .PAModel import *
from .configuration import *
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@
os.path.dirname( #testing dir
os.path.dirname(__file__))))) #this dir

from src.PAModelpy.PAModel import PAModel
from src.PAModelpy.configuration import Config
from src.PAModelpy.EnzymeSectors import ActiveEnzymeSector, UnusedEnzymeSector, TransEnzymeSector


from src.PAModelpy import PAModel,Config,ActiveEnzymeSector, UnusedEnzymeSector, TransEnzymeSector

def test_if_pamodel_change_kcat_function_works():
#arrange
Expand Down Expand Up @@ -132,6 +128,39 @@ def test_if_pamodel_enzyme_get_kcat_values_correctly():
# assert
assert kcat_to_return == kcat_returned['f']

def test_if_pamodel_remove_sectors_can_remove_active_enzyme_sector():
# Arrange
toy_pam = build_toy_pam(sensitivity=False)

# Act
toy_pam.remove_sectors('ActiveEnzymeSector')

# Assert
assert len(toy_pam.enzymes) == 0
assert all('EC_' not in key for key in toy_pam.constraints.keys())
assert toy_pam.TOTAL_PROTEIN_CONSTRAINT_ID not in toy_pam.constraints.keys()

def test_if_pamodel_remove_sectors_can_remove_translational_protein_sector():
# Arrange
toy_pam = build_toy_pam(sensitivity=False)
sector = toy_pam.sectors.get_by_id('TranslationalProteinSector')
lin_rxn = toy_pam.reactions.get_by_id(sector.id_list[0])

# get the old upperbound for the total protein constraint
tpc_ub = toy_pam.constraints[toy_pam.TOTAL_PROTEIN_CONSTRAINT_ID].ub

# Act
toy_pam.remove_sectors('TranslationalProteinSector')

#make sure the linear coefficients associated to the sector in the total protein constriant are 0
lin_coeff = toy_pam.constraints[toy_pam.TOTAL_PROTEIN_CONSTRAINT_ID].get_linear_coefficients([
lin_rxn.forward_variable,
lin_rxn.reverse_variable,
])

# Assert
assert toy_pam.constraints[toy_pam.TOTAL_PROTEIN_CONSTRAINT_ID].ub == tpc_ub + sector.intercept
assert all(coeff == 0 for coeff in lin_coeff.values())

#######################################################################################################
#HELPER METHODS
Expand Down

0 comments on commit 5b88d94

Please sign in to comment.