def __init__(self, db, public_db, html_writer, thermo=None, thermodynamic_method='global', max_reactions=None, max_solutions=100, maximal_dG=0.0, update_file=None, output_kegg_file=None): """Initialize the Pathologic object. Args: db: the DB to read group contribution data from. html_writer: an HtmlWriter for writing output. thermodynamic_method: the analysis methods. Options are: "none", "pCr", "MTDF", "global" or "localized" max_reactions: the maximum number of reactions to find in a solution (use None for unlimited) max_solutions: the maximum number of solutions to find (use None for unlimited) maximal_dG: the maximum dG allowed. Use this to change the thermodynamic constraints to have a different MTDF. When set to 0, it is the usual feasibility measure. update_file: the file to read for KEGG updates. """ assert thermodynamic_method in OptimizationMethods.ALLOWED_METHODS util._mkdir('../res/pathologic') self.html_writer = html_writer self.thermodynamic_method = thermodynamic_method self.max_reactions = max_reactions self.max_solutions = max_solutions self.maximal_dG = maximal_dG self.db_public = public_db self.db = db self.thermo = thermo self.kegg_pathologic = KeggPathologic() if update_file is not None: self.kegg_pathologic.update_database(update_file, self.html_writer)
class Pathologic(object): def __init__(self, db, public_db, html_writer, thermo=None, thermodynamic_method='global', max_reactions=None, max_solutions=100, maximal_dG=0.0, update_file=None, output_kegg_file=None): """Initialize the Pathologic object. Args: db: the DB to read group contribution data from. html_writer: an HtmlWriter for writing output. thermodynamic_method: the analysis methods. Options are: "none", "pCr", "MTDF", "global" or "localized" max_reactions: the maximum number of reactions to find in a solution (use None for unlimited) max_solutions: the maximum number of solutions to find (use None for unlimited) maximal_dG: the maximum dG allowed. Use this to change the thermodynamic constraints to have a different MTDF. When set to 0, it is the usual feasibility measure. update_file: the file to read for KEGG updates. """ assert thermodynamic_method in OptimizationMethods.ALLOWED_METHODS util._mkdir('../res/pathologic') self.html_writer = html_writer self.thermodynamic_method = thermodynamic_method self.max_reactions = max_reactions self.max_solutions = max_solutions self.maximal_dG = maximal_dG self.db_public = public_db self.db = db self.thermo = thermo self.kegg_pathologic = KeggPathologic() if update_file is not None: self.kegg_pathologic.update_database(update_file, self.html_writer) def add_reaction(self, reaction, weight=1.0): self.kegg_pathologic.add_reaction(reaction, weight) def delete_reaction(self, rid): self.kegg_pathologic.delete_reaction(rid) def add_cofactor_reaction(self, reaction): self.kegg_pathologic.add_cofactor_reaction(reaction) def ban_compound(self, cid): """Deletes all reactions which involve this compounds as a reactant""" self.kegg_pathologic.banned_cids.add(cid) def find_path(self, experiment_name, net_reaction): """Find a pathway from the source to the target. Args: experiment_name: a name given to this experiment. net_reaction: a Reaction describing the net reaction for the desired paths """ dirname = os.path.join('../res/pathologic/', experiment_name) logging.info('Writing output to: %s' % dirname) util._mkdir(dirname) self.html_writer.write('<a href="pathologic/' + experiment_name + '.html">' + experiment_name + '</a><br>\n') exp_html = HtmlWriter('../res/pathologic/' + experiment_name + '.html') exp_html.write("<p><h1>%s</h1>\n" % experiment_name) exp_html.insert_toggle(div_id="__parameters__", start_here=True, label='Show Parameters') f, S, compounds, reactions = self.kegg_pathologic.get_unique_cids_and_reactions() exp_html.write('<h2>Conditions:</h2>\n') exp_html.write_ul(['Optimization method: %s' % self.thermodynamic_method, 'Concentration range: %g M < C < %g M' % (self.thermo.c_range[0], self.thermo.c_range[1]), "Max Δ<sub>r</sub>G' = %.1f" % self.maximal_dG, 'pH = %g' % self.thermo.pH, 'I = %g' % self.thermo.I, 'T = %g' % self.thermo.T, 'Max no. reactions: %d' % (self.max_reactions or -1), 'Max no. solutions: %d' % (self.max_solutions or -1), 'Overall Reaction: %s' % net_reaction.to_hypertext(), '%d reactions' % len(reactions), '%d unique compounds' % len(compounds)]) exp_html.div_end() exp_html.write('</br>\n') logging.debug("All compounds:") for i, compound in enumerate(compounds): logging.debug("%05d) C%05d = %s" % (i, compound.cid, compound.name)) logging.debug("All reactions:") for i, reaction in enumerate(reactions): logging.debug("%05d) R%05d = %s" % (i, reaction.rid, str(reaction))) output_kegg_file = open(dirname + '/kegg_pathway.txt', 'w') exp_html.write('<a href="%s/kegg_pathway.txt">All solutions in KEGG format</a></br>\n' % experiment_name) # Find a solution with a minimal total flux logging.info("Preparing LP solver for the minimal total flux problem") exp_html.write('<b>Minimum flux</b>') slip = Stoichiometric_LP("Pathologic") slip.add_stoichiometric_constraints(f, S, compounds, reactions, net_reaction) slip.export("../res/pathologic/%s/%03d_lp.txt" % (experiment_name, 0)) exp_html.write(' (<a href="%s/%03d_lp.txt">LP file</a>): ' % (experiment_name, 0)) logging.info("Solving") if not slip.solve(): exp_html.write("<b>There are no solutions!</b>") logging.warning("There are no solutions. Quitting!") return logging.info("writing solution") self.write_current_solution(exp_html, slip, experiment_name) logging.info("Preparing MILP solver") milp = Stoichiometric_LP("Pathologic") milp.solution_index = 1 milp.add_stoichiometric_constraints(f, S, compounds, reactions, net_reaction) milp.add_milp_variables() if self.max_reactions is not None: milp.add_reaction_num_constraint(self.max_reactions) if self.thermodynamic_method == OptimizationMethods.LOCALIZED: milp.add_localized_dGf_constraints(self.thermo) else: milp.add_dGr_constraints(self.thermo, optimization=self.thermodynamic_method, maximal_dG=self.maximal_dG) index = 0 while (self.max_solutions is None) or (index < self.max_solutions): index += 1 # create the MILP problem to constrain the previous solutions not to reappear again. logging.info("Round %03d, solving using MILP" % (milp.solution_index)) milp.export("../res/pathologic/%s/%03d_lp.txt" % (experiment_name, milp.solution_index)) exp_html.write('<b>Solution #%d</b> (<a href="%s/%03d_lp.txt">LP file</a>): ' % (index, experiment_name, index)) if not milp.solve(): exp_html.write("<b>No solution found</b>") logging.info("No more solutions. Quitting!") break logging.info("writing solution") self.write_current_solution(exp_html, milp, experiment_name, output_kegg_file) milp.ban_current_solution() output_kegg_file.close() exp_html.close() def write_current_solution(self, exp_html, lp, experiment_name, output_kegg_file=None): solution = lp.get_active_reaction_data() solution_id = '%03d' % lp.solution_index exp_html.write('%d reactions, flux = %g, \n' % (len(solution.reactions), float(solution.fluxes.sum(1)))) # draw network as a graph and link to it Gdot = self.kegg_pathologic.draw_pathway(solution.reactions, list(solution.fluxes.flat)) svg_fname = '%s/%s_graph' % (experiment_name, solution_id) exp_html.embed_dot_inline(Gdot, width=240, height=320, name=svg_fname) # write the solution for the concentrations in a table if solution.concentrations is not None: exp_html.insert_toggle(start_here=True) rowdicts = [] for c, compound in enumerate(solution.compounds): rowdict = {} rowdict['KEGG ID'] = '<a href="%s">C%05d</a>' % (compound.get_link(), compound.cid) rowdict['Compound'] = compound.name rowdict[symbol_df_G0_prime + "[kJ/mol]"] = solution.dG0_f[0, c] rowdict[symbol_df_G_prime + "[kJ/mol]"] = solution.dG_f[0, c] if np.isfinite(solution.concentrations[0, c]): rowdict['Conc. [M]'] = '%.1e' % solution.concentrations[0, c] else: rowdict['Conc. [M]'] = 'N/A' rowdicts.append(rowdict) headers=['KEGG ID', 'Compound', symbol_df_G0_prime + "[kJ/mol]", symbol_df_G_prime + "[kJ/mol]", 'Conc. [M]'] exp_html.write('Compound Concentrations<br>\n') exp_html.write_table(rowdicts, headers, decimal=1) total_reaction = Reaction('total', {}) rowdicts = [] for r, reaction in enumerate(solution.reactions): rowdict = {} flux = solution.fluxes[0, r] rowdict['KEGG ID'] = '<a href="%s">R%05d</a>' % (reaction.get_link(), reaction.rid) rowdict['Reaction'] = reaction.to_hypertext(show_cids=False) rowdict['Flux'] = flux rowdict[symbol_dr_Gc_prime + "[kJ/mol]"] = solution.dGc_r[0, r] rowdict[symbol_dr_G_prime + "[kJ/mol]"] = solution.dG_r[0, r] rowdicts.append(rowdict) total_reaction += (flux * reaction) rowdict = {} rowdict['KEGG ID'] = 'total' rowdict['Reaction'] = total_reaction.to_hypertext(show_cids=False) rowdict[symbol_dr_Gc_prime + "[kJ/mol]"] = float(solution.dGc_r.sum(1)) rowdict[symbol_dr_G_prime + "[kJ/mol]"] = float(solution.dG_r.sum(1)) rowdicts.append(rowdict) headers=['KEGG ID', 'Reaction', symbol_dr_Gc_prime + "[kJ/mol]", symbol_dr_G_prime + "[kJ/mol]", "Flux"] exp_html.write('Reaction Gibbs energies<br>\n') exp_html.write_table(rowdicts, headers, decimal=1) exp_html.div_end() # write the pathway in KEGG format if output_kegg_file is not None: write_kegg_pathway(output_kegg_file, entry=experiment_name + ' ' + solution_id, reactions=solution.reactions, fluxes=list(solution.fluxes.flat)) exp_html.write('<br>\n')