Source code for etfba.core.reaction

'''Difine the Reaction class.'''


from collections.abc import Iterable
from ..io.results import PrettyDict


[docs] DEFAULT_MW = 40 # Default enzyme molecular weight in kDa
[docs] DEFAULT_KCAT = 200 # Default reaction catalytic rate constant in 1/s
[docs] DEFAULT_KM = 0.2 # Default reactant Michaelis constant in mM
[docs] DEFAULT_DGPM = 0 # Default reaction standard Gibbs energy in KJ/mol
[docs] class ReactantDict(PrettyDict): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)
[docs] self.caller = None # caller is Reaction
[docs] self.role = None # {'substrate', 'product'}
[docs] def __getitem__(self, key): item = super().__getitem__(key) # item is Metabolite if self.caller is not None: item.host = self.caller if self.role is not None: item.role = self.role return item
[docs] class Reaction(): ''' Attributes ---------- substrates: PrettyDict Key is the metabolite ID, value is the Metabolite instances as substrates. products: PrettyDict Key is the metabolite ID, value is the Metabolite instances as products. forward_kcat/fkcat: float Catalytic rate constant in the forward direction. backward_kcat/bkcat: float Catalytic rate constant in the backward direction . molecular_weight/mw: float Molecular weight of the catalytic enzyme. standard_gibbs_energy/dgpm: float Standard Gibbs energy of the reaction. standard_gibbs_energy_error/dgpm_error: float Standard error of the standard reaction Gibbs energy. reversible/rev: bool Reaction reversibility. is_biomass_formation: bool Indicates if it's the biomass formation reaction. is_exch_reaction: bool Indicates if it's an exchange reaction. is_h_transport: bool Indicates if it's a proton transport reaction. is_h2o_transport: bool Indicates if it's a water transport reaction. is_constrained_by_thermodynamics: bool Indicates if it's constrained by thermodynamics. ''' def __init__( self, rxnid, enzyme_name=None, category=None, *, forward_kcat=None, backward_kcat=None, molecular_weight=None, standard_gibbs_energy=None, standard_gibbs_energy_error=None, reversible=True, is_biomass_formation=False, is_exch_reaction=False, is_h_transport=False, is_h2o_transport=False ): ''' Parameters ---------- rxnid: str Reaction ID. enzyme_name: str Enzyme name. category: str Reaction category. forward_kcat: float Kcat value (1/s) in the forward direction. backward_kcat: float Kcat value (1/s) in the backward direction. molecular_weight: float Enzyme molecular weight in kDa. standard_gibbs_energy: float Standard reaction Gibbs energy in kJ/mol with metabolite concentrations in mM. standard_gibbs_energy_error: float Standard error of the standard Gibbs energy. reversible: bool Reaction reversibility. is_biomass_formation: bool Indicates if it's the biomass formation reaction. is_exch_reaction: bool Indicates if it's an exchange reaction. is_h_transport: bool Indicates if it's a proton transport reaction. is_h2o_transport: bool Indicates if it's a water transport reaction. '''
[docs] self.rxnid = rxnid
[docs] self.enzyme = enzyme_name
[docs] self.category = category
[docs] self.fkcat = forward_kcat
[docs] self.bkcat = backward_kcat
[docs] self.mw = molecular_weight
[docs] self.dgpm = standard_gibbs_energy
[docs] self.dgpm_error = standard_gibbs_energy_error
[docs] self.rev = reversible
[docs] self.is_biomass_formation = is_biomass_formation
[docs] self.is_exch_reaction = is_exch_reaction
[docs] self.is_h_transport = is_h_transport
[docs] self.is_h2o_transport = is_h2o_transport
[docs] self._substrates = ReactantDict()
[docs] self._products = ReactantDict()
[docs] self.is_constrained_by_thermodynamics = False
[docs] def _add_reactants(self, coes, kms, label): if label == 'substrate': for reac, coe in coes.items(): reac.coes[self.rxnid] = -coe self.substrates[reac.metabid] = reac elif label == 'products': for reac, coe in coes.items(): reac.coes[self.rxnid] = coe self.products[reac.metabid] = reac if not self.is_biomass_formation and not self.is_exch_reaction: if kms is None: for reac in coes.keys(): reac.kms[self.rxnid] = DEFAULT_KM else: if set(kms).issubset(coes): for reac in coes.keys(): if reac in kms: reac.kms[self.rxnid] = kms[reac] else: reac.kms[self.rxnid] = DEFAULT_KM else: raise ValueError('keys in kms should be subset of those in coes') else: # reactants in biomass formation and exchange reactions don't have Km # values for reac in coes.keys(): reac.kms[self.rxnid] = None
[docs] def add_substrates(self, coes, kms=None): ''' Parameters ---------- coes: dict Dictionary mapping substrates to their corresponding stoichiometric coefficients (positive values). All substrates should have a provided stoichiometric coefficient. kms: dict Dictionary mapping substrates to their corresponding Km values. Km values should only be provided as a subset of substrates present in the 'coes' dictionary. An empty dict is acceptable if no Km values are available. ''' self._add_reactants(coes, kms, 'substrate')
[docs] def add_products(self, coes, kms = None): ''' Parameters ---------- coes: dict Dictionary mapping products to their corresponding stoichiometric coefficients (positive values). All products should have a provided stoichiometric coefficient. kms: dict Dictionary mapping products to their corresponding Km values. Km values should only be provided as a subset of products present in the 'coes' dictionary. An empty dict is acceptable if no Km values are available. ''' self._add_reactants(coes, kms, 'products')
[docs] def _remove_reactants(self, reactants, label): if not isinstance(reactants, Iterable): reactants = [reactants] for reac in reactants: del reac.coes[self.rxnid] del reac.kms[self.rxnid] if label == 'substrate': for reac in reactants: del self.substrates[reac.metabid] elif label == 'product': for reac in reactants: del self.products[reac.metabid]
[docs] def remove_substrates(self, substrates): ''' Parameters ---------- substrates: list of Metabolite List of substrates to be removed from the current reaction. ''' self._remove_reactants(substrates, 'substrate')
[docs] def remove_products(self, products): ''' Parameters ---------- products: list of Metabolites List of products to be removed from current reaction. ''' self._remove_reactants(products, 'product')
@property
[docs] def substrates(self): self._substrates.caller = self self._substrates.role = 'substrate' return self._substrates
@property
[docs] def products(self): self._products.caller = self self._products.role = 'product' return self._products
@property
[docs] def forward_kcat(self): return self.fkcat
@forward_kcat.setter def forward_kcat(self, value): self.fkcat = value @property
[docs] def backward_kcat(self): return self.bkcat
@backward_kcat.setter def backward_kcat(self, value): self.bkcat = value @property
[docs] def molecular_weight(self): return self.mw
@molecular_weight.setter def molecular_weight(self, value): self.mw = value @property
[docs] def standard_gibbs_energy(self): return self.dgpm
@standard_gibbs_energy.setter def standard_gibbs_energy(self, value): self.dgpm = value @property
[docs] def standard_gibbs_energy_error(self): return self.dgpm_error
@standard_gibbs_energy_error.setter def standard_gibbs_energy_error(self, value): self.dgpm_error = value @property
[docs] def reversible(self): return self.rev
@reversible.setter def reversible(self, value): self.rev = value
[docs] def __repr__(self): if self.substrates or self.products: coe_subs = [] for subid, sub in sorted(self.substrates.items()): coe = -sub.coes[self.rxnid] coe_subs.append(f'{coe} {subid}') subsStr = ' + '.join(coe_subs) coe_pros = [] for proid, pro in sorted(self.products.items()): coe = pro.coes[self.rxnid] coe_pros.append(f'{coe} {proid}') prosStr = ' + '.join(sorted(coe_pros)) if self.rev: return f'{subsStr} <=> {prosStr}' else: return f'{subsStr} => {prosStr}' else: return 'reaction not constructed'