def generate_merged_model(self): def _id_pattern(object_id, organism_id): return "{}_{}".format(object_id, organism_id) def _name_pattern(object_name, organism_name): return "{} ({})".format(object_name, organism_name) def _copy_object(obj, org_id, compartment=None): new_obj = obj.copy() new_obj.id = _id_pattern(obj.id, org_id) new_obj.name = _name_pattern(obj.name, org_id) if compartment: new_obj.compartment = compartment return new_obj models_missing_extracelullar_compartment = [ m.id for m in self._organisms.itervalues() if self._extracellular_compartment not in m.compartments ] if models_missing_extracelullar_compartment: raise RuntimeError( "Extracellular compartment '{}' missing from models: '{}'". format(self._extracellular_compartment, "', '".join(models_missing_extracelullar_compartment))) models_missing_biomass = [ m.id for m in self._organisms.itervalues() if not m.biomass_reaction ] if models_missing_biomass: raise RuntimeError( "Biomass reaction not found in models: {}".format( "', '".join(models_missing_biomass))) merged_model = CBModel(self.id) merged_model.biomass_reaction = None organisms_biomass_metabolites = {} community_metabolite_exchange_lookup = {} for org_id, model in self._organisms.items(): self._organisms_reactions[org_id] = set() self._organisms_exchange_reactions[org_id] = {} self._organisms_biomass_reactions[org_id] = {} exchanged_metabolites = { m_id for r_id in model.get_exchange_reactions() for m_id in model.reactions[r_id].stoichiometry } # # Create additional extracellular compartment # if not self._merge_extracellular_compartments: pool_compartment = Compartment('pool', 'common pool') merged_model.add_compartment(pool_compartment) export_pool_compartment = Compartment( 'pool_blacklist', 'blacklisted metabolite pool') merged_model.add_compartment(export_pool_compartment) for c_id, comp in model.compartments.items(): if c_id != self._extracellular_compartment or not self._merge_extracellular_compartments: new_comp = _copy_object(comp, org_id) merged_model.add_compartment(new_comp) elif c_id not in merged_model.compartments: merged_model.add_compartment(deepcopy(comp)) for m_id, met in model.metabolites.items(): if met.compartment != self._extracellular_compartment or not self._merge_extracellular_compartments: new_met = _copy_object( met, org_id, _id_pattern(met.compartment, org_id)) merged_model.add_metabolite(new_met, clear_tmp=False) elif m_id not in merged_model.metabolites: merged_model.add_metabolite(deepcopy(met), clear_tmp=False) m_blacklisted = met.id in self._exchanged_metabolites_blacklist if met.id in exchanged_metabolites and not self._merge_extracellular_compartments: # # For blacklisted metabolites create a separate pool from which metabolites can not be reuptaken # if m_blacklisted and self._interacting: pool_id = _id_pattern(m_id, "pool_blacklist") if pool_id not in merged_model.metabolites: new_met = _copy_object(met, "pool_blacklist", "pool_blacklist") merged_model.add_metabolite(new_met, clear_tmp=False) exch_id = _id_pattern("R_EX_" + m_id, "pool_blacklist") exch_name = _name_pattern( met.name, "pool (blacklist) exchange") blk_rxn = CBReaction(exch_id, name=exch_name, reversible=False, is_exchange=False, is_sink=True) blk_rxn.stoichiometry[pool_id] = -1.0 community_metabolite_exchange_lookup[ new_met.id] = exch_id merged_model.add_reaction(blk_rxn) pool_id = _id_pattern(m_id, "pool") if pool_id not in merged_model.metabolites: new_met = _copy_object(met, "pool", "pool") merged_model.add_metabolite(new_met, clear_tmp=False) exch_id = _id_pattern("R_EX_" + m_id, "pool") exch_name = _name_pattern(met.name, "pool exchange") new_rxn = CBReaction(exch_id, name=exch_name, reversible=True, is_exchange=True) new_rxn.stoichiometry[pool_id] = -1.0 community_metabolite_exchange_lookup[ new_met.id] = exch_id merged_model.add_reaction(new_rxn) for r_id, rxn in model.reactions.items(): is_exchange = rxn.is_exchange if not is_exchange or not self._merge_extracellular_compartments: new_rxn = _copy_object(rxn, org_id) new_rxn.is_exchange = False for m_id, coeff in rxn.stoichiometry.items(): m_blacklisted = m_id in self._exchanged_metabolites_blacklist if model.metabolites[ m_id].compartment != self._extracellular_compartment or not self._merge_extracellular_compartments: del new_rxn.stoichiometry[m_id] new_id = _id_pattern(m_id, org_id) new_rxn.stoichiometry[new_id] = coeff if is_exchange: if model.metabolites[ m_id].compartment == self._extracellular_compartment and not self._merge_extracellular_compartments: # TODO: if m_id in self._exchanged_metabolites_blacklist: pool_id = _id_pattern(m_id, "pool") new_rxn.stoichiometry[pool_id] = -coeff cnm = CommunityNameMapping( organism_reaction=new_rxn.id, original_reaction=r_id, organism_metabolite=new_id, extracellular_metabolite=pool_id, original_metabolite=m_id, community_exchange_reaction= community_metabolite_exchange_lookup[ pool_id]) self._organisms_exchange_reactions[org_id][ new_rxn.id] = cnm if not self.interacting: sink_rxn = CBReaction( 'Sink_{}'.format(new_id), is_exchange=False, is_sink=True, reversible=False) sink_rxn.stoichiometry = {new_id: -1} sink_rxn.lb = 0.0 merged_model.add_reaction(sink_rxn) elif m_blacklisted: pool_blacklist_id = _id_pattern( m_id, "pool_blacklist") blacklist_export_rxn = CBReaction( 'R_EX_BLACKLIST_{}'.format(new_id), is_exchange=False, is_sink=False, reversible=False) blacklist_export_rxn.stoichiometry = { new_id: -1, pool_blacklist_id: 1 } blacklist_export_rxn.lb = 0.0 merged_model.add_reaction( blacklist_export_rxn) if is_exchange and not self._merge_extracellular_compartments: new_rxn.reversible = True new_rxn.lb = None new_rxn.ub = None if self.interacting and not m_blacklisted else 0.0 if rxn.id == model.biomass_reaction: new_rxn.reversible = False if self._create_biomass and rxn.id == model.biomass_reaction: new_rxn.objective = False # Add biomass metabolite to biomass equation m_id = _id_pattern('Biomass', org_id) name = _name_pattern('Framed biomass', org_id) comp = 'pool' if not self._merge_extracellular_compartments else self._extracellular_compartment biomass_met = Metabolite(m_id, name, comp) merged_model.add_metabolite(biomass_met, clear_tmp=False) new_rxn.stoichiometry[m_id] = 1 organisms_biomass_metabolites[org_id] = m_id self._organisms_reactions[org_id].add(new_rxn.id) merged_model.add_reaction(new_rxn) else: if is_exchange and self._merge_extracellular_compartments: self._organisms_exchange_reactions[org_id][ rxn.id] = CommunityNameMapping( organism_reaction=r_id, original_reaction=r_id, extracellular_metabolite=rxn.stoichiometry. keys()[0], original_metabolite=rxn.stoichiometry.keys() [0], organism_metabolite=None) self._organisms_reactions[org_id].add(rxn.id) if r_id in merged_model.reactions: continue new_rxn = deepcopy(rxn) new_rxn.is_exchange = True if rxn.id == model.biomass_reaction and self._create_biomass: new_rxn.reversible = False new_rxn.objective = False m_id = _id_pattern('Biomass', org_id) name = _name_pattern('Biomass', org_id) comp = 'pool' if not self._merge_extracellular_compartments else self._extracellular_compartment biomass_met = Metabolite(m_id, name, comp) merged_model.add_metabolite(biomass_met, clear_tmp=False) new_rxn.stoichiometry[m_id] = 1 organisms_biomass_metabolites[org_id] = m_id merged_model.add_reaction(new_rxn) if r_id == model.biomass_reaction: self._organisms_biomass_reactions[org_id] = new_rxn.id if self._create_biomass: biomass_rxn = CBReaction('R_Community_Growth', name="Community Growth", reversible=False, is_exchange=False, is_sink=True, objective=1.0) for org_biomass in organisms_biomass_metabolites.itervalues(): biomass_rxn.stoichiometry[org_biomass] = -1 merged_model.add_reaction(biomass_rxn) merged_model.biomass_reaction = biomass_rxn.id return merged_model
def merge_models(self): comm_model = CBModel(self.id) old_ext_comps = [] ext_mets = [] self.reaction_map = {} self.metabolite_map = {} # default IDs ext_comp_id = "ext" biomass_id = "community_biomass" comm_growth = "community_growth" # create external compartment comp = Compartment(ext_comp_id, "extracellular environment", external=True) comm_model.add_compartment(comp) # community biomass met = Metabolite(biomass_id, "Total community biomass", ext_comp_id) comm_model.add_metabolite(met, clear_tmp=False) rxn = CBReaction(comm_growth, name="Community growth rate", reversible=False, stoichiometry={biomass_id: -1}, lb=0, ub=None, objective=1) comm_model.add_reaction(rxn, clear_tmp=False) # add each organism for org_id, model in self.organisms.items(): def rename(old_id): return "{}_{}".format(old_id, org_id) # add internal compartments for c_id, comp in model.compartments.items(): if comp.external: old_ext_comps.append(c_id) else: new_comp = Compartment(rename(c_id), comp.name) comm_model.add_compartment(new_comp) # add metabolites for m_id, met in model.metabolites.items(): if met.compartment not in old_ext_comps: # if is internal new_id = rename(m_id) new_met = Metabolite(new_id, met.name, rename(met.compartment)) new_met.metadata = met.metadata.copy() comm_model.add_metabolite(new_met) self.metabolite_map[(org_id, m_id)] = new_id elif m_id not in comm_model.metabolites: # if is external but was not added yet new_met = Metabolite(m_id, met.name, ext_comp_id) new_met.metadata = met.metadata.copy() comm_model.add_metabolite(new_met, clear_tmp=False) ext_mets.append(new_met.id) # add internal reactions for r_id, rxn in model.reactions.items(): if rxn.reaction_type == ReactionType.EXCHANGE: continue new_id = rename(r_id) new_stoichiometry = { m_id if m_id in ext_mets else rename(m_id): coeff for m_id, coeff in rxn.stoichiometry.items() } if r_id == model.biomass_reaction: new_stoichiometry[biomass_id] = 1 new_rxn = CBReaction( new_id, name=rxn.name, reversible=rxn.reversible, stoichiometry=new_stoichiometry, reaction_type=rxn.reaction_type, lb=rxn.lb, ub=rxn.ub, ) comm_model.add_reaction(new_rxn, clear_tmp=False) new_rxn.metadata = rxn.metadata.copy() self.reaction_map[(org_id, r_id)] = new_id # Add exchange reactions for m_id in ext_mets: rxn = CBReaction("R_EX_{}".format(m_id), reversible=True, stoichiometry={m_id: -1}, reaction_type=ReactionType.EXCHANGE) comm_model.add_reaction(rxn, clear_tmp=False) return comm_model
def __parse(self, text, filename=None): text = text.replace("\r\n", "\n") text = text.replace("\r", "\n") re_long = re.compile("%.*?%", re.DOTALL | re.MULTILINE) text = re_long.sub("\n", text) re_short = re.compile("#.*?\n") text = re_short.sub("\n", text) sections = self.find_sections(text) model = CBModel(basename(filename)) react_name, react_text, react_line = self.__find_section( text, sections, lambda x: re.search(r"reac", x, re.I)) const_name, const_text, const_line = self.__find_section( text, sections, lambda x: re.search(r"cons", x, re.I)) ext_m_name, ext_m_text, ext_m_line = self.__find_section( text, sections, lambda x: re.search(r"ext", x, re.I)) obj_name, obj_text, obj_line = self.__find_section( text, sections, lambda x: re.search(r"obj", x, re.I) and not re.search( "des", x, re.I)) if react_text: reactions = self.__parse_reactions_section( react_text, filename=filename, section_start=react_line, strip_comments=False) for reaction, r_i in reactions: for m_id in reaction.stoichiometry: if m_id not in model.metabolites: model.add_metabolite(Metabolite(elem_id=m_id, compartment=None), clear_tmp=True) model.add_reaction(reaction, clear_tmp=True) else: warnings.warn("Could not find '-REACTIONS' section", BiooptParseWarning) if const_text: for (r_id, lb, ub), i in self.__parse_constraints_section( const_text, filename=filename, section_start=const_line, strip_comments=False): if r_id in model.reactions and lb < 0 and not model.reactions[ r_id].reversible: warnings.warn_explicit( "Reaction '{0}' from '{1}' has effective bounds not compatible with reaction direction in '{2}' section ({3} : [{4}, {5}])" .format(r_id, const_name, react_name, "<->" if reactions[r_id].reversibl else "->", lb, ub), BiooptParseWarning, filename=filename, lineno=const_line + i + 1) if r_id in model.reactions: model.reactions[r_id].lb = lb model.reactions[r_id].ub = ub elif react_text: warnings.warn_explicit( "Reaction '{0}' from '{1}' section is not present in '{2}' section" .format(r_id, const_name, react_name), BiooptParseWarning, filename=filename, lineno=const_line + i + 1) else: warnings.warn("Could not find '-CONSTRAINS' section", BiooptParseWarning) if ext_m_text: met2rxn = model.metabolite_reaction_lookup() for m_external, i in self.__parse_external_metabolites_section( ext_m_text, filename=filename, section_start=ext_m_line, strip_comments=False): if m_external.id in model.metabolites: model.metabolites[m_external.id].boundary = True for external_r_id, coef in met2rxn[m_external.id].items(): model.reactions[external_r_id].is_exchange = True del model.reactions[external_r_id].stoichiometry[ m_external.id] elif react_text: warnings.warn_explicit( "Metabolite '{0}' from '{1}' section is not present in any reaction from '{2}' section" .format(m_external.id, ext_m_name, react_name), BiooptParseWarning, filename=filename, lineno=ext_m_line + i + 1) else: warnings.warn("Could not find '-EXTERNAL METABOLITES' section", BiooptParseWarning) if obj_text: r_id, coef = self.parse_objective_section( obj_text, section_name=obj_name, reactions_section_name=react_name, filename=filename, section_start=obj_line, reactions=reactions, strip_comments=False) model.reactions[r_id].objective = coef else: warnings.warn("Could not find '-OBJECTIVE' section", BiooptParseWarning) return model