def get_dGr0_prime(sparse): try: r = Reaction(sparse) if not r.check_full_reaction_balancing(): return np.nan, np.nan, 'unbalanbed reaction' dG0_prime, dG0_std = equilibrator.dG0_prime(r) return dG0_prime, dG0_std, '' except ValueError as e: return np.nan, np.nan, 'value error: ' + str(e) except KeyError as e: return np.nan, np.nan, 'key error: ' + str(e)
def test_reduction_potential(self): kegg_id_to_coeff = {'C00036': -1, 'C00149': 1} # oxaloacetate = malate reaction = Reaction(kegg_id_to_coeff) cc = ComponentContribution(pH=7.0, ionic_strength=0.1) E0_prime_mV, E0_uncertainty = cc.E0_prime(reaction) self.assertAlmostEqual(E0_prime_mV, -175.2, 1) self.assertAlmostEqual(E0_uncertainty, 5.3, 1)
def test_reaction_balancing(self): warnings.simplefilter('ignore', ResourceWarning) kegg_id_to_coeff = {'C00011' : -1, 'C00001' : -1, 'C01353' : 1} reaction = Reaction(kegg_id_to_coeff) self.assertTrue(reaction.check_full_reaction_balancing()) kegg_id_to_coeff = {'C00036' : -1, 'C00149' : 1} # oxaloacetate = malate reaction = Reaction(kegg_id_to_coeff) self.assertAlmostEqual(reaction.check_half_reaction_balancing(), 2.0) kegg_id_to_coeff = {'C00031' : -1, 'C00469' : 2} # missing two CO2 reaction = Reaction(kegg_id_to_coeff) self.assertDictEqual(reaction._get_reaction_atom_balance(), {'O': -4, 'C': -2, 'e-': -44})
def get_eQ_reaction_from_rid( self, r_id: str, pickaxe: Pickaxe = None, db_name: str = None ) -> Union[PhasedReaction, None]: """Get an eQuilibrator reaction object from an r_id. Parameters ---------- r_id : str Reaction id to get object for. pickaxe : Pickaxe pickaxe object to look for the compound in, by default None. db_name : str Database to look for reaction in. Returns ------- PhasedReaction eQuilibrator reactiono to calculate ∆Gr with. """ if pickaxe: if r_id in pickaxe.reactions: reaction_info = pickaxe.reactions[r_id] else: return None elif db_name: mine = self.client[db_name] reaction_info = mine.reactions.find_one({"_id": r_id}) if not reaction_info: return None else: return None reactants = reaction_info["Reactants"] products = reaction_info["Products"] lhs = " + ".join(f"{r[0]} {r[1]}" for r in reactants) rhs = " + ".join(f"{p[0]} {p[1]}" for p in products) reaction_string = " => ".join([lhs, rhs]) compounds = set([r[1] for r in reactants]) compounds.update(tuple(p[1] for p in products)) eQ_compound_dict = { c_id: self.get_eQ_compound_from_cid(c_id, pickaxe, db_name) for c_id in compounds } if not all(eQ_compound_dict.values()): return None if "X73bc8ef21db580aefe4dbc0af17d4013961d9d17" not in compounds: eQ_compound_dict["water"] = self._water eq_reaction = Reaction.parse_formula(eQ_compound_dict.get, reaction_string) return eq_reaction
def test_atp_hydrolysis(self): formula = ' C00002 + C00001 <= C00008 + C00009' kegg_ids = set(('C00002', 'C00001', 'C00008', 'C00009')) try: reaction = Reaction.parse_formula(formula) except ValueError as e: self.fail('unable to parse the formula\n' + str(e)) self.assertSetEqual(set(reaction.kegg_ids()), kegg_ids) for kegg_id in kegg_ids: self.assertIsNotNone(reaction.get_compound(kegg_id)) self.assertNotEqual(reaction.get_coeff(kegg_id), 0) self.assertIsNone(reaction.get_compound('C00003')) self.assertEqual(reaction.get_coeff('C00003'), 0)
def get_best_match(self, parsed_query): kegg_id_to_coeff = [] for coeff, name in parsed_query.substrates: comp_matches = self._compound_matcher.match(name) if comp_matches is None: raise ParseError('Cannot match this substrate at all: ' + name) kegg_id_to_coeff.append((comp_matches.CID.iat[0], -coeff)) for coeff, name in parsed_query.products: comp_matches = self._compound_matcher.match(name) if comp_matches is None: raise ParseError('Cannot match this product at all: ' + name) kegg_id_to_coeff.append((comp_matches.CID.iat[0], coeff)) return Reaction(dict(kegg_id_to_coeff))
def test_gibbs_energy(self): kegg_id_to_coeff = { 'C00002': -1, 'C00001': -1, 'C00008': 1, 'C00009': 1 } # ATP + H2O = ADP + Pi reaction = Reaction(kegg_id_to_coeff) cc = ComponentContribution(pH=7.0, ionic_strength=0.1) dG0_prime, dG0_uncertainty = cc.dG0_prime(reaction) self.assertAlmostEqual(dG0_prime, -26.4, 1) self.assertAlmostEqual(dG0_uncertainty, 0.6, 1)
def get_best_match(self, parsed_query): kegg_id_to_coeff = defaultdict(int) for coeff, name in parsed_query.substrates: comp_matches = self._compound_matcher.match(name) if comp_matches is None: raise ParseError('Cannot match this substrate at all: ' + name) kegg_id_to_coeff[comp_matches.CID.iat[0]] -= coeff for coeff, name in parsed_query.products: comp_matches = self._compound_matcher.match(name) if comp_matches is None: raise ParseError('Cannot match this product at all: ' + name) kegg_id_to_coeff[comp_matches.CID.iat[0]] += coeff # if compounds appear more than once (e.g. on both sides of a reaction # we need to sum up the coresponding coefficients) kegg_id_to_coeff = dict(filter(lambda x: x[1], kegg_id_to_coeff.items())) return Reaction(kegg_id_to_coeff)
def reaction_gibbs(equation, dfG_dict=None, pH=7.0, eq_api=None): """Calculate standard Gibbs reaction energy.""" # Remove compartment tags from compound IDs equation = re.sub("_\[[a-z]{3}\]", "", equation) equation = re.sub("_[a-z]{1}", "", equation) if dfG_dict: s = parse_equation(equation) if None in [dfG_dict[c[1]] for c in s[0] + s[1]]: # Cannot calculate drG when dfG is missing return None else: p = sum([c[0] * Decimal(str(dfG_dict[c[1]])) for c in s[1]]) r = sum([c[0] * Decimal(str(dfG_dict[c[1]])) for c in s[0]]) return float(p - r) else: # Use Equilibrator API if not eq_api: eq_api = ComponentContribution(pH=pH, ionic_strength=0.1) rxn = Reaction.parse_formula(equation) return round(eq_api.dG0_prime(rxn)[0], 1)
def test_gibbs_energy(self): warnings.simplefilter('ignore', ResourceWarning) kegg_id_to_coeff = {'C00002' : -1, 'C00001' : -1, 'C00008' : 1, 'C00009' : 1} # ATP + H2O = ADP + Pi kegg_id_to_conc = {'C00002' : 1e-3, 'C00009' : 1e-4} reaction = Reaction(kegg_id_to_coeff) cc = ComponentContribution(pH=7.0, ionic_strength=0.1) dG0_prime, dG0_uncertainty = cc.dG0_prime(reaction) self.assertAlmostEqual(dG0_prime, -26.4, 1) self.assertAlmostEqual(dG0_uncertainty, 0.6, 1) dG_prime, _ = cc.dG_prime(reaction, kegg_id_to_conc) self.assertAlmostEqual(dG_prime, -32.1, 1) dGm_prime, _ = cc.dGm_prime(reaction) self.assertAlmostEqual(dGm_prime, -43.5, 1)
def predict_dG(model, pH=7, ionic_strength=0.1, fn=None): eq_api = ComponentContribution(pH=7.0, ionic_strength=0.1) lst = [] for r in model.reactions: kegg_string = build_kegg_string(r) if kegg_string: try: rxn = Reaction.parse_formula(kegg_string) except KeyError: print("eQuilibrator could not predict dG for {0}".format(r.id)) continue # eq_api.dG0_prime(rxn) try: dgm = eq_api.dGm_prime(rxn) dg0 = eq_api.dG0_prime(rxn) except: print("eQuilibrator could not predict dG for {0}".format(r.id)) else: if dgm[-1] > dGMm_STD_THRESHOLD: print( "eQuilibrator could not predict dG for {0} with a reasonable uncertainty" .format(r.id)) continue print(r.id, dgm) lst.append([ r.id, r.name, r.annotation["kegg.reaction"], parse_reversibility(r), *dg0, *dgm ]) # Store as df df = pd.DataFrame(lst) df.columns = [ "Rxn", "Rxn Name", "Rxn KEGG ID", "Reversibility in model", "dG0_prime", "dG0_prime_std", "dGm_prime", "dGm_prime_std" ] if not fn: fn = "eQuilibrator_reversibility.csv" df.to_csv(fn, index=False, sep=",") print("Found the dG for {0} of {1} reactions".format( len(lst), len(model.reactions))) return lst
for rgt in rxn_cpds_array: if (rgt['compound'] not in seed_mnx_structural_map): OK = False else: mnx_id = seed_mnx_structural_map[rgt['compound']] if (rgt['coefficient'] < 0): lhs[mnx_id] = math.fabs(rgt['coefficient']) elif (rgt['coefficient'] > 0): rhs[mnx_id] = math.fabs(rgt['coefficient']) equation_str = ' + '.join([f'{value} {key}' for key, value in lhs.items()]) + \ " = " + \ ' + '.join([f'{value} {key}' for key, value in rhs.items()]) equilibrator_reaction = Reaction.parse_formula(ccache.get_compound, equation_str) try: result = equilibrator_calculator.standard_dg_prime( equilibrator_reaction) dG0_prime = str(result.value.to('kilocal / mole').magnitude) uncertainty = str(result.error.to('kilocal / mole').magnitude) ln_RI = equilibrator_calculator.ln_reversibility_index( equilibrator_reaction) if not type(ln_RI) == float: ln_RI = str(ln_RI.magnitude) output_handle.write("\t".join([rxn, dG0_prime, uncertainty, ln_RI]) + "\n") except Exception as e:
default=0.1) parser.add_argument('--ph', type=float, help='pH level', default=7.0) logging.getLogger().setLevel(logging.WARNING) args = parser.parse_args() sys.stderr.write('pH = %.1f\n' % args.ph) sys.stderr.write('I = %.1f M\n' % args.i) ids = [] reactions = [] with open('data/iJO1366_reactions.csv', 'r') as f: for row in csv.DictReader(f): ids.append(row['bigg.reaction']) try: reactions.append(Reaction.parse_formula(row['formula'])) except ValueError as e: print('warning: cannot parse reaction %s because of %s' % (row['bigg.reaction'], str(e))) reactions.append(Reaction({})) continue equilibrator = ComponentContribution(pH=args.ph, ionic_strength=args.i) dG0_prime, U = equilibrator.dG0_prime_multi(reactions) writer = csv.writer(args.outfile) header = [ 'reaction', 'pH', 'ionic strength [M]', 'dG\'0 [kJ/mol]', 'uncertainty [kJ/mol]', 'ln(Reversibility Index)', 'comment' ]
ionic_strength=Q_("0.25M"), temperature=Q_("298.15K")) structures_root = os.path.dirname(__file__) + "/../../Biochemistry/Structures/" thermodynamics_root = os.path.dirname( __file__) + "/../../Biochemistry/Thermodynamics/" file_name = structures_root + 'MetaNetX/Structures_in_ModelSEED_and_eQuilibrator.txt' output_name = thermodynamics_root + 'eQuilibrator/MetaNetX_Compound_Energies.tbl' output_handle = open(output_name, 'w') mnx_inchikey_dict = dict() with open(file_name) as file_handle: for line in file_handle.readlines(): line = line.strip() (mnx, inchikey) = line.split('\t') equilibrator_reaction = Reaction.parse_formula(ccache.get_compound, ' = ' + mnx) try: result = equilibrator_calculator.standard_dg_prime( equilibrator_reaction) dG0_prime = str(result.value.to('kilocal / mole').magnitude) uncertainty = str(result.error.to('kilocal / mole').magnitude) output_handle.write("\t".join([mnx, dG0_prime, uncertainty]) + "\n") except Exception as e: output_handle.write("\t".join([mnx, "Unable to retrieve energy"]) + "\n") print(e)
#Main Script eq_api = ComponentContribution(pH=7.5, ionic_strength=0.1) #Load model data file os.chdir(ECM_path+'/Models') data = pd.read_table('KEGGmodelScaffold.txt') os.chdir(ECM_path) i=0 output = '!!SBtab TableName=Parameter TableType=Quantity Document=S. cervisiae central carbon metabolism SBtabVersion=1.0'+'\n' output = output+ '!QuantityType'+'\t'+'!Reaction'+'\t'+'!Compound'+'\t'+'!Value'+'\t'+'!Unit'+'\t'+'!Reaction:Identifiers:kegg.reaction'+'\t'+'!Compound:Identifiers:kegg.compound'+'\t'+'!ID'+'\n' unit = 'dimensionless' compound = ' ' compoundID = ' ' type = 'equilibrium constant' for formula in data.ReactionFormula: rxn = Reaction.parse_formula(formula) name = str(data.ID[i]) KEGGID = str(data.KEGGIDs[i]) reactionStr = name#+'_'+KEGGID parameterID = 'kEQ_'+name print(name) #Check if reaction is atomically balanced if not rxn.check_full_reaction_balancing(): print('%s is not balanced' % name) #raw_input("Press the <ENTER> key to continue...") print(' ') i = i+1 #Get standard Gibbs free energy for the rxn dG0_prime, dG0_uncertainty = eq_api.dG0_prime(rxn) if name == 'RIP1': dG0_prime = -38.46 #Edda Klipp's work
############################################################################### parser = MakeParser() args = parser.parse_args() logging.getLogger().setLevel(logging.WARNING) sys.stderr.write('pH = %.1f\n' % args.ph) sys.stderr.write('I = %.1f M\n' % args.i) sys.stderr.write('Reaction: %s\n' % args.reaction) sys.stderr.flush() # parse the reaction try: reaction = Reaction.parse_formula(args.reaction) except ValueError as e: logging.error(str(e)) sys.exit(-1) equilibrator = ComponentContribution(pH=args.ph, ionic_strength=args.i) n_e = reaction.check_half_reaction_balancing() if n_e is None: logging.error('reaction is not chemically balanced') sys.exit(-1) elif n_e == 0: dG0_prime, dG0_uncertainty = equilibrator.dG0_prime(reaction) sys.stdout.write(u'\u0394G\'\u00B0 = %.1f \u00B1 %.1f kJ/mol\n' % (dG0_prime, dG0_uncertainty))
def find_thermodynamic_reversibility_index(reactions): u""" Return the reversibility index of the given reactions. To determine the reversibility index, we calculate the reversibility index ln_gamma (see [1]_ section 3.5) of each reaction using the eQuilibrator API [2]_. Parameters ---------- reactions: list of cobra.Reaction A list of reactions for which to calculate the reversibility index. Returns ------- tuple list of cobra.Reaction, index pairs A list of pairs of reactions and their reversibility indexes. list of cobra.Reaction A list of reactions which contain at least one metabolite that could not be mapped to KEGG on the basis of its annotation. list of cobra.Reaction A list of reactions for which it is not possible to calculate the standard change in Gibbs free energy potential. Reasons of failure include that participating metabolites cannot be broken down with the group contribution method. list of cobra.Reaction A list of reactions that are not chemically or redox balanced. References ---------- .. [1] Elad Noor, Arren Bar-Even, Avi Flamholz, Yaniv Lubling, Dan Davidi, Ron Milo; An integrated open framework for thermodynamics of reactions that combines accuracy and coverage, Bioinformatics, Volume 28, Issue 15, 1 August 2012, Pages 2037–2044, https://doi.org/10.1093/bioinformatics/bts317 .. [2] https://pypi.org/project/equilibrator-api/ """ incomplete_mapping = [] problematic_calculation = [] reversibility_indexes = [] unbalanced = [] metabolite_mapping = {} for rxn in reactions: stoich = translate_reaction(rxn, metabolite_mapping) if len(stoich) < len(rxn.metabolites): incomplete_mapping.append(rxn) continue try: # Remove protons from stoichiometry. if "C00080" in stoich: del stoich["C00080"] eq_rxn = Reaction(stoich, rxn.id) except KeyError: incomplete_mapping.append(rxn) continue if eq_rxn.check_full_reaction_balancing(): try: ln_rev_index = eq_rxn.reversibility_index() # TODO (Moritz Beber): Which exceptions can we expect here? except Exception: problematic_calculation.append(rxn) continue reversibility_indexes.append((rxn, ln_rev_index)) else: unbalanced.append(rxn) reversibility_indexes.sort(key=lambda p: abs(p[1]), reverse=True) return (reversibility_indexes, incomplete_mapping, problematic_calculation, unbalanced)
def find_thermodynamic_reversibility_index(reactions): u""" Return the reversibility index of the given reactions. To determine the reversibility index, we calculate the reversibility index ln_gamma (see [1]_ section 3.5) of each reaction using the eQuilibrator API [2]_. Parameters ---------- reactions: list of cobra.Reaction A list of reactions for which to calculate the reversibility index. Returns ------- tuple list of cobra.Reaction, index pairs A list of pairs of reactions and their reversibility indexes. list of cobra.Reaction A list of reactions which contain at least one metabolite that could not be mapped to KEGG on the basis of its annotation. list of cobra.Reaction A list of reactions for which it is not possible to calculate the standard change in Gibbs free energy potential. Reasons of failure include that participating metabolites cannot be broken down with the group contribution method. list of cobra.Reaction A list of reactions that are not chemically or redox balanced. References ---------- .. [1] Elad Noor, Arren Bar-Even, Avi Flamholz, Yaniv Lubling, Dan Davidi, Ron Milo; An integrated open framework for thermodynamics of reactions that combines accuracy and coverage, Bioinformatics, Volume 28, Issue 15, 1 August 2012, Pages 2037–2044, https://doi.org/10.1093/bioinformatics/bts317 .. [2] https://pypi.org/project/equilibrator-api/ """ incomplete_mapping = [] problematic_calculation = [] reversibility_indexes = [] unbalanced = [] metabolite_mapping = {} for rxn in reactions: stoich = translate_reaction(rxn, metabolite_mapping) if len(stoich) < len(rxn.metabolites): incomplete_mapping.append(rxn) continue try: # Remove protons from stoichiometry. if "C00080" in stoich: del stoich["C00080"] eq_rxn = Reaction(stoich, rxn.id) except KeyError: incomplete_mapping.append(rxn) continue if eq_rxn.check_full_reaction_balancing(): try: ln_rev_index = eq_rxn.reversibility_index() # TODO (Moritz Beber): Which exceptions can we expect here? except Exception: problematic_calculation.append(rxn) continue reversibility_indexes.append((rxn, ln_rev_index)) else: unbalanced.append(rxn) reversibility_indexes.sort(key=lambda p: abs(p[1]), reverse=True) return ( reversibility_indexes, incomplete_mapping, problematic_calculation, unbalanced )