api/c/indigo/src/indigo_reaction.cpp (695 lines of code) (raw):

/**************************************************************************** * Copyright (C) from 2009 to Present EPAM Systems. * * This file is part of Indigo toolkit. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ #include "indigo_reaction.h" #include "base_cpp/output.h" #include "indigo_array.h" #include "indigo_io.h" #include "indigo_mapping.h" #include "indigo_molecule.h" #include "reaction/canonical_rsmiles_saver.h" #include "reaction/pathway_reaction.h" #include "reaction/reaction_auto_loader.h" #include "reaction/reaction_automapper.h" #include "reaction/rsmiles_loader.h" #include "reaction/rxnfile_saver.h" #include <memory> // // IndigoBaseReaction // IndigoBaseReaction::IndigoBaseReaction(int type_) : IndigoObject(type_) { } IndigoBaseReaction::~IndigoBaseReaction() { } bool IndigoBaseReaction::is(IndigoObject& obj) { int type = obj.type; if (type == REACTION || type == QUERY_REACTION || type == RDF_REACTION || type == SMILES_REACTION || type == CML_REACTION || type == JSON_REACTION || type == PATHWAY_REACTION) return true; if (type == ARRAY_ELEMENT) return is(((IndigoArrayElement&)obj).get()); return false; } const char* IndigoBaseReaction::debugInfo() const { return "<base reaction>"; } // // IndigoPathwayReaction // IndigoPathwayReaction::IndigoPathwayReaction() : IndigoBaseReaction(PATHWAY_REACTION) { init(); } const char* IndigoPathwayReaction::debugInfo() const { if (type == IndigoObject::PATHWAY_REACTION) return "<pathway reaction>"; return ""; } IndigoPathwayReaction::~IndigoPathwayReaction() { } void IndigoPathwayReaction::init(std::unique_ptr<BaseReaction>&& reaction) { rxn = reaction ? std::move(reaction) : std::make_unique<PathwayReaction>(); } BaseReaction& IndigoPathwayReaction::getBaseReaction() { assert(rxn); return *rxn; } PathwayReaction& IndigoPathwayReaction::getPathwayReaction() { assert(rxn); return dynamic_cast<PathwayReaction&>(*rxn); } const char* IndigoPathwayReaction::getName() { if (!rxn || rxn->name.ptr() == 0) return ""; return rxn->name.ptr(); } IndigoObject* IndigoPathwayReaction::clone() { return cloneFrom(*this); } IndigoPathwayReaction* IndigoPathwayReaction::cloneFrom(IndigoObject& obj) { Reaction& rxn = obj.getReaction(); std::unique_ptr<IndigoPathwayReaction> rxnptr = std::make_unique<IndigoPathwayReaction>(); rxnptr->rxn->clone(rxn, 0, 0, 0); auto& props = obj.getProperties(); rxnptr->copyProperties(props); return rxnptr.release(); } // // IndigoReaction // IndigoReaction::IndigoReaction() : IndigoBaseReaction(REACTION) { init(); } const char* IndigoReaction::debugInfo() const { if (type == IndigoObject::REACTION) return "<reaction>"; return ""; } IndigoReaction::~IndigoReaction() { } void IndigoReaction::init(std::unique_ptr<BaseReaction>&& reaction) { rxn = reaction ? std::move(reaction) : std::make_unique<Reaction>(); _properties.copy(rxn->properties()); } Reaction& IndigoReaction::getReaction() { assert(rxn); return dynamic_cast<Reaction&>(*rxn); } BaseReaction& IndigoReaction::getBaseReaction() { assert(rxn); return *rxn; } const char* IndigoReaction::getName() { if (!rxn || rxn->name.ptr() == 0) return ""; return rxn->name.ptr(); } // // IndigoQueryReaction // IndigoQueryReaction::IndigoQueryReaction() : IndigoBaseReaction(QUERY_REACTION) { } const char* IndigoQueryReaction::debugInfo() const { return "<query reaction>"; } IndigoQueryReaction::~IndigoQueryReaction() { } BaseReaction& IndigoQueryReaction::getBaseReaction() { return rxn; } QueryReaction& IndigoQueryReaction::getQueryReaction() { return rxn; } const char* IndigoQueryReaction::getName() { if (rxn.name.ptr() == 0) return ""; return rxn.name.ptr(); } // // IndigoReactionMolecule // IndigoReactionMolecule::IndigoReactionMolecule(BaseReaction& reaction, int index) : IndigoObject(REACTION_MOLECULE), rxn(reaction), idx(index) { } IndigoReactionMolecule::IndigoReactionMolecule(BaseReaction& reaction, MonomersProperties& map, int index) : IndigoObject(REACTION_MOLECULE), rxn(reaction), idx(index) { if (index < map.size()) { _properties.copy(map.at(index)); } } const char* IndigoReactionMolecule::debugInfo() const { return "<reaction molecule>"; } IndigoReactionMolecule::~IndigoReactionMolecule() { } BaseMolecule& IndigoReactionMolecule::getBaseMolecule() { return rxn.getBaseMolecule(idx); } Molecule& IndigoReactionMolecule::getMolecule() { return rxn.getBaseMolecule(idx).asMolecule(); } QueryMolecule& IndigoReactionMolecule::getQueryMolecule() { return rxn.getBaseMolecule(idx).asQueryMolecule(); } int IndigoReactionMolecule::getIndex() { return idx; } IndigoObject* IndigoReactionMolecule::clone() { if (rxn.isQueryReaction()) return IndigoQueryMolecule::cloneFrom(*this); else return IndigoMolecule::cloneFrom(*this); } void IndigoReactionMolecule::remove() { rxn.remove(idx); } // // IndigoReactionIter // IndigoReactionIter::IndigoReactionIter(BaseReaction& rxn, MonomersProperties& map, int subtype) : IndigoObject(REACTION_ITER), _rxn(rxn), _map(&map) { _subtype = subtype; _idx = -1; } IndigoReactionIter::IndigoReactionIter(BaseReaction& rxn, int subtype) : IndigoObject(REACTION_ITER), _rxn(rxn), _map(nullptr) { _subtype = subtype; _idx = -1; } const char* IndigoReactionIter::debugInfo() const { return "<reaction molecule iterator>"; } IndigoReactionIter::~IndigoReactionIter() { } int IndigoReactionIter::_begin() { switch (_subtype) { case REACTANTS: return _rxn.reactantBegin(); case PRODUCTS: return _rxn.productBegin(); case CATALYSTS: return _rxn.catalystBegin(); case REACTIONS: return _rxn.reactionBegin(); } return _rxn.begin(); } int IndigoReactionIter::_end() { switch (_subtype) { case REACTANTS: return _rxn.reactantEnd(); case PRODUCTS: return _rxn.productEnd(); case CATALYSTS: return _rxn.catalystEnd(); case REACTIONS: return _rxn.reactionEnd(); } return _rxn.end(); } int IndigoReactionIter::_next(int i) { switch (_subtype) { case REACTANTS: return _rxn.reactantNext(i); case PRODUCTS: return _rxn.productNext(i); case CATALYSTS: return _rxn.catalystNext(i); case REACTIONS: return _rxn.reactionNext(i); } return _rxn.next(i); } IndigoObject* IndigoReactionIter::next() { if (_idx == -1) { _idx = _begin(); } else _idx = _next(_idx); if (_idx == _end()) return 0; if (_subtype == REACTION) { auto reaction = new IndigoReaction(); reaction->init(_rxn.getBaseReaction(_idx)); return reaction; } else if (_map) { return new IndigoReactionMolecule(_rxn, *_map, _idx); } else { return new IndigoReactionMolecule(_rxn, _idx); } } bool IndigoReactionIter::hasNext() { if (_idx == -1) return _begin() != _end(); return _next(_idx) != _end(); } IndigoReaction* IndigoReaction::cloneFrom(IndigoObject& obj) { Reaction& rxn = obj.getReaction(); std::unique_ptr<IndigoReaction> rxnptr = std::make_unique<IndigoReaction>(); rxnptr->rxn->clone(rxn, 0, 0, 0); try { MonomersProperties& mprops = obj.getMonomersProperties(); for (auto i = 0; i < mprops.size(); i++) { rxnptr->_monomersProperties.push().copy(mprops[i]); } } catch (Exception&) { } auto& props = obj.getProperties(); rxnptr->copyProperties(props); return rxnptr.release(); } IndigoQueryReaction* IndigoQueryReaction::cloneFrom(IndigoObject& obj) { QueryReaction& rxn = obj.getQueryReaction(); std::unique_ptr<IndigoQueryReaction> rxnptr = std::make_unique<IndigoQueryReaction>(); rxnptr->rxn.clone(rxn, 0, 0, 0); try { MonomersProperties& mprops = obj.getMonomersProperties(); for (auto i = 0; i < mprops.size(); i++) { rxnptr->_monomersProperties.push().copy(mprops[i]); } } catch (Exception&) { } auto& props = obj.getProperties(); rxnptr->copyProperties(props); return rxnptr.release(); } IndigoObject* IndigoReaction::clone() { return cloneFrom(*this); } IndigoObject* IndigoQueryReaction::clone() { return cloneFrom(*this); } int _indigoIterateReaction(int reaction, int subtype) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(reaction); BaseReaction& rxn = obj.getBaseReaction(); try { MonomersProperties& map = obj.getMonomersProperties(); return self.addObject(new IndigoReactionIter(rxn, map, subtype)); } catch (Exception&) { return self.addObject(new IndigoReactionIter(rxn, subtype)); } } INDIGO_END(-1); } CEXPORT int indigoLoadReaction(int source) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(source); Scanner& scanner = IndigoScanner::get(obj); ReactionAutoLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.treat_x_as_pseudoatom = self.treat_x_as_pseudoatom; loader.ignore_noncritical_query_features = self.ignore_noncritical_query_features; loader.dearomatize_on_load = self.dearomatize_on_load; loader.arom_options = self.arom_options; loader.layout_options = self.layout_options; auto rxn = loader.loadReaction(false); std::unique_ptr<IndigoBaseReaction> rxnptr; if (rxn->isPathwayReaction()) { auto pwr = std::make_unique<IndigoPathwayReaction>(); pwr->init(std::move(rxn)); rxnptr = std::move(pwr); } else { auto reaction = std::make_unique<IndigoReaction>(); reaction->init(std::move(rxn)); rxnptr = std::move(reaction); } return self.addObject(rxnptr.release()); } INDIGO_END(-1); } CEXPORT int indigoLoadQueryReaction(int source) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(source); Scanner& scanner = IndigoScanner::get(obj); ReactionAutoLoader loader(scanner); loader.stereochemistry_options = self.stereochemistry_options; loader.treat_x_as_pseudoatom = self.treat_x_as_pseudoatom; loader.dearomatize_on_load = self.dearomatize_on_load; loader.arom_options = self.arom_options; std::unique_ptr<IndigoQueryReaction> rxnptr = std::make_unique<IndigoQueryReaction>(); loader.loadReaction(rxnptr->rxn); return self.addObject(rxnptr.release()); } INDIGO_END(-1); } CEXPORT int indigoIterateReactants(int reaction) { return _indigoIterateReaction(reaction, IndigoReactionIter::REACTANTS); } CEXPORT int indigoIterateProducts(int reaction) { return _indigoIterateReaction(reaction, IndigoReactionIter::PRODUCTS); } CEXPORT int indigoIterateCatalysts(int reaction) { return _indigoIterateReaction(reaction, IndigoReactionIter::CATALYSTS); } CEXPORT int indigoIterateMolecules(int reaction) { return _indigoIterateReaction(reaction, IndigoReactionIter::MOLECULES); } CEXPORT int indigoIterateReactions(int reaction) { return _indigoIterateReaction(reaction, IndigoReactionIter::REACTIONS); } CEXPORT int indigoCreateReaction(void) { INDIGO_BEGIN { return self.addObject(new IndigoReaction()); } INDIGO_END(-1); } CEXPORT int indigoCreateQueryReaction(void) { INDIGO_BEGIN { return self.addObject(new IndigoQueryReaction()); } INDIGO_END(-1); } CEXPORT int indigoAddReactant(int reaction, int molecule) { INDIGO_BEGIN { BaseReaction& rxn = self.getObject(reaction).getBaseReaction(); rxn.addReactantCopy(self.getObject(molecule).getBaseMolecule(), 0, 0); return 1; } INDIGO_END(-1); } CEXPORT int indigoAddProduct(int reaction, int molecule) { INDIGO_BEGIN { BaseReaction& rxn = self.getObject(reaction).getBaseReaction(); rxn.addProductCopy(self.getObject(molecule).getBaseMolecule(), 0, 0); return 1; } INDIGO_END(-1); } CEXPORT int indigoAddCatalyst(int reaction, int molecule) { INDIGO_BEGIN { BaseReaction& rxn = self.getObject(reaction).getBaseReaction(); rxn.addCatalystCopy(self.getObject(molecule).getBaseMolecule(), 0, 0); return 1; } INDIGO_END(-1); } CEXPORT int indigoCountReactants(int reaction) { INDIGO_BEGIN { return self.getObject(reaction).getBaseReaction().reactantsCount(); } INDIGO_END(-1); } CEXPORT int indigoCountProducts(int reaction) { INDIGO_BEGIN { return self.getObject(reaction).getBaseReaction().productsCount(); } INDIGO_END(-1); } CEXPORT int indigoCountCatalysts(int reaction) { INDIGO_BEGIN { return self.getObject(reaction).getBaseReaction().catalystCount(); } INDIGO_END(-1); } CEXPORT int indigoCountMolecules(int handle) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(handle); if (IndigoBaseReaction::is(obj)) return obj.getBaseReaction().count(); throw IndigoError("can not count molecules of %s", obj.debugInfo()); } INDIGO_END(-1); } CEXPORT int indigoGetMolecule(int reaction, int index) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(reaction); BaseReaction& rxn = obj.getBaseReaction(); try { MonomersProperties& map = obj.getMonomersProperties(); return self.addObject(new IndigoReactionMolecule(rxn, map, index)); } catch (Exception&) { return self.addObject(new IndigoReactionMolecule(rxn, index)); } } INDIGO_END(-1); } CEXPORT int indigoMapMolecule(int handle, int molecule) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(handle); if (obj.type != IndigoObject::REACTION_MAPPING) throw IndigoError("%s is not a reaction mapping object", obj.debugInfo()); IndigoReactionMapping& mapping = (IndigoReactionMapping&)obj; IndigoObject& mol_obj = self.getObject(molecule); if (mol_obj.type != IndigoObject::REACTION_MOLECULE) throw IndigoError("%s is not a reaction molecule object", mol_obj.debugInfo()); IndigoReactionMolecule& mol = (IndigoReactionMolecule&)mol_obj; if (&mol.rxn != &mapping.from) throw IndigoError("%s molecule doesn't correspond to a mapping %s", mol.debugInfo(), mapping.debugInfo()); int target_index = mapping.mol_mapping[mol.getIndex()]; return self.addObject(new IndigoReactionMolecule(mapping.to, target_index)); } INDIGO_END(-1); } static int readAAMOptions(const char* mode, ReactionAutomapper& ram) { int nmode = ReactionAutomapper::AAM_REGEN_DISCARD; if (mode == 0 || mode[0] == 0) return nmode; QS_DEF(Array<char>, word); BufferScanner scanner(mode); while (1) { scanner.skipSpace(); if (scanner.isEOF()) break; scanner.readWord(word, 0); if (strcasecmp(word.ptr(), "discard") == 0) nmode = ReactionAutomapper::AAM_REGEN_DISCARD; else if (strcasecmp(word.ptr(), "alter") == 0) nmode = ReactionAutomapper::AAM_REGEN_ALTER; else if (strcasecmp(word.ptr(), "keep") == 0) nmode = ReactionAutomapper::AAM_REGEN_KEEP; else if (strcasecmp(word.ptr(), "clear") == 0) nmode = ReactionAutomapper::AAM_REGEN_CLEAR; else if (strcasecmp(word.ptr(), "ignore_charges") == 0) ram.ignore_atom_charges = true; else if (strcasecmp(word.ptr(), "ignore_isotopes") == 0) ram.ignore_atom_isotopes = true; else if (strcasecmp(word.ptr(), "ignore_radicals") == 0) ram.ignore_atom_radicals = true; else if (strcasecmp(word.ptr(), "ignore_valence") == 0) ram.ignore_atom_valence = true; else throw IndigoError("indigoAutomap(): unknown mode: %s", word.ptr()); } return nmode; } CEXPORT int indigoAutomap(int reaction, const char* mode) { INDIGO_BEGIN { BaseReaction& rxn = self.getObject(reaction).getBaseReaction(); ReactionAutomapper ram(rxn); ram.arom_options = self.arom_options; /* * Read options */ int nmode = readAAMOptions(mode, ram); /* * Clear AAM if required */ if (nmode == ReactionAutomapper::AAM_REGEN_CLEAR) { rxn.clearAAM(); return 0; } /* * Set timeout */ std::shared_ptr<TimeoutCancellationHandler> timeout(nullptr); if (self.aam_cancellation_timeout > 0) { timeout = std::make_shared<TimeoutCancellationHandler>(self.aam_cancellation_timeout); } /* * Set cancellation handler */ AAMCancellationWrapper aam_timeout(timeout); /* * Launch automap */ ram.automap(nmode); aam_timeout.reset(); return 1; } INDIGO_END(-1); } CEXPORT int indigoGetAtomMappingNumber(int reaction, int reaction_atom) { INDIGO_BEGIN { IndigoAtom& ia = IndigoAtom::cast(self.getObject(reaction_atom)); BaseReaction& rxn = self.getObject(reaction).getBaseReaction(); int mol_idx = rxn.findMolecule(&ia.mol); if (mol_idx == -1) throw IndigoError("indigoGetAtomMapping(): input atom not found in the reaction"); return rxn.getAAM(mol_idx, ia.idx); } INDIGO_END(-1); } CEXPORT int indigoSetAtomMappingNumber(int reaction, int reaction_atom, int number) { INDIGO_BEGIN { IndigoAtom& ia = IndigoAtom::cast(self.getObject(reaction_atom)); BaseReaction& rxn = self.getObject(reaction).getBaseReaction(); int mol_idx = rxn.findMolecule(&ia.mol); if (mol_idx == -1) throw IndigoError("indigoSetAtomMapping(): input atom not found in the reaction"); if (number < 0) throw IndigoError("indigoSetAtomMapping(): mapping number cannot be negative"); rxn.getAAMArray(mol_idx).at(ia.idx) = number; return 0; } INDIGO_END(-1); } CEXPORT int indigoGetReactingCenter(int reaction, int reaction_bond, int* rc) { INDIGO_BEGIN { IndigoBond& ib = IndigoBond::cast(self.getObject(reaction_bond)); BaseReaction& rxn = self.getObject(reaction).getBaseReaction(); int mol_idx = rxn.findMolecule(&ib.mol); if (mol_idx == -1) throw IndigoError("indigoGetReactingCenter(): input bond not found in the reaction"); *rc = rxn.getReactingCenter(mol_idx, ib.idx); return 1; } INDIGO_END(-1); } CEXPORT int indigoSetReactingCenter(int reaction, int reaction_bond, int rc) { INDIGO_BEGIN { IndigoBond& ib = IndigoBond::cast(self.getObject(reaction_bond)); BaseReaction& rxn = self.getObject(reaction).getBaseReaction(); int mol_idx = rxn.findMolecule(&ib.mol); if (mol_idx == -1) throw IndigoError("indigoSetReactingCenter(): input bond not found in the reaction"); if (rc < -1 || rc > RC_TOTAL) throw IndigoError("indigoSetReactingCenter(): invalid or unsupported reacting center: %d", rc); rxn.getReactingCenterArray(mol_idx).at(ib.idx) = rc; return 1; } INDIGO_END(-1); } CEXPORT int indigoClearAAM(int reaction) { INDIGO_BEGIN { BaseReaction& rxn = self.getObject(reaction).getBaseReaction(); rxn.clearAAM(); return 0; } INDIGO_END(-1); } CEXPORT int indigoCorrectReactingCenters(int reaction) { INDIGO_BEGIN { BaseReaction& rxn = self.getObject(reaction).getBaseReaction(); ReactionAutomapper ram(rxn); ram.arom_options = self.arom_options; ram.correctReactingCenters(true); return 0; } INDIGO_END(-1); } CEXPORT int indigoLoadReactionSmarts(int source) { INDIGO_BEGIN { IndigoObject& obj = self.getObject(source); RSmilesLoader loader(IndigoScanner::get(obj)); std::unique_ptr<IndigoQueryReaction> rxnptr = std::make_unique<IndigoQueryReaction>(); QueryReaction& qrxn = rxnptr->rxn; loader.smarts_mode = true; loader.loadQueryReaction(qrxn); return self.addObject(rxnptr.release()); } INDIGO_END(-1); } CEXPORT const char* indigoCanonicalRSmiles(int reaction) { INDIGO_BEGIN { Reaction& react = self.getObject(reaction).getReaction(); auto& tmp = self.getThreadTmpData(); ArrayOutput output(tmp.string); CanonicalRSmilesSaver saver(output); saver.saveReaction(react); tmp.string.push(0); return tmp.string.ptr(); } INDIGO_END(0); }