// // Copyright (C) 2001-2024 Greg Landrum and other RDKit contributors // // @@ All Rights Reserved @@ // This file is part of the RDKit. // The contents are covered by the terms of the BSD license // which is included in the file license.txt, found at the root // of the RDKit source tree. // #include "Bond.h" #include "Atom.h" #include "ROMol.h" #include #include "Atropisomers.h" namespace RDKit { Bond::Bond() : RDProps() { initBond(); }; Bond::Bond(BondType bT) : RDProps() { initBond(); d_bondType = bT; }; Bond::Bond(const Bond &other) : RDProps(other) { // NOTE: we do *not* copy ownership! dp_mol = nullptr; d_bondType = other.d_bondType; d_beginAtomIdx = other.d_beginAtomIdx; d_endAtomIdx = other.d_endAtomIdx; d_dirTag = other.d_dirTag; d_stereo = other.d_stereo; if (other.dp_stereoAtoms) { dp_stereoAtoms = new INT_VECT(*other.dp_stereoAtoms); } else { dp_stereoAtoms = nullptr; } df_isAromatic = other.df_isAromatic; df_isConjugated = other.df_isConjugated; d_index = other.d_index; d_flags = other.d_flags; } Bond::~Bond() { delete dp_stereoAtoms; } Bond &Bond::operator=(const Bond &other) { if (this == &other) { return *this; } dp_mol = other.dp_mol; d_bondType = other.d_bondType; d_beginAtomIdx = other.d_beginAtomIdx; d_endAtomIdx = other.d_endAtomIdx; d_dirTag = other.d_dirTag; delete dp_stereoAtoms; if (other.dp_stereoAtoms) { dp_stereoAtoms = new INT_VECT(*other.dp_stereoAtoms); } else { dp_stereoAtoms = nullptr; } df_isAromatic = other.df_isAromatic; df_isConjugated = other.df_isConjugated; d_index = other.d_index; d_props = other.d_props; d_flags = other.d_flags; return *this; } Bond *Bond::copy() const { auto *res = new Bond(*this); return res; } void Bond::setOwningMol(ROMol *other) { // FIX: doesn't update topology dp_mol = other; } unsigned int Bond::getOtherAtomIdx(const unsigned int thisIdx) const { if (d_beginAtomIdx == thisIdx) { return d_endAtomIdx; } else if (d_endAtomIdx == thisIdx) { return d_beginAtomIdx; } // This "precondition" would check exactly the same that is checked // above, but no need to be redundant, so just throw. POSTCONDITION(false, "bad index"); } void Bond::setBeginAtomIdx(unsigned int what) { if (dp_mol) { URANGE_CHECK(what, getOwningMol().getNumAtoms()); } d_beginAtomIdx = what; }; void Bond::setEndAtomIdx(unsigned int what) { if (dp_mol) { URANGE_CHECK(what, getOwningMol().getNumAtoms()); } d_endAtomIdx = what; }; void Bond::setBeginAtom(Atom *at) { PRECONDITION(dp_mol != nullptr, "no owning molecule for bond"); setBeginAtomIdx(at->getIdx()); } void Bond::setEndAtom(Atom *at) { PRECONDITION(dp_mol != nullptr, "no owning molecule for bond"); setEndAtomIdx(at->getIdx()); } Atom *Bond::getBeginAtom() const { PRECONDITION(dp_mol != nullptr, "no owning molecule for bond"); return dp_mol->getAtomWithIdx(d_beginAtomIdx); }; Atom *Bond::getEndAtom() const { PRECONDITION(dp_mol != nullptr, "no owning molecule for bond"); return dp_mol->getAtomWithIdx(d_endAtomIdx); }; Atom *Bond::getOtherAtom(Atom const *what) const { PRECONDITION(dp_mol != nullptr, "no owning molecule for bond"); return dp_mol->getAtomWithIdx(getOtherAtomIdx(what->getIdx())); }; double Bond::getBondTypeAsDouble() const { double res; switch (getBondType()) { case UNSPECIFIED: case IONIC: case ZERO: res = 0; break; case SINGLE: res = 1; break; case DOUBLE: res = 2; break; case TRIPLE: res = 3; break; case QUADRUPLE: res = 4; break; case QUINTUPLE: res = 5; break; case HEXTUPLE: res = 6; break; case ONEANDAHALF: res = 1.5; break; case TWOANDAHALF: res = 2.5; break; case THREEANDAHALF: res = 3.5; break; case FOURANDAHALF: res = 4.5; break; case FIVEANDAHALF: res = 5.5; break; case AROMATIC: res = 1.5; break; case DATIVEONE: res = 1.0; break; // FIX: this should probably be different case DATIVE: res = 1.0; break; // FIX: again probably wrong case HYDROGEN: res = 0.0; break; default: UNDER_CONSTRUCTION("Bad bond type"); } return res; } double Bond::getValenceContrib(const Atom *atom) const { if (atom != getBeginAtom() && atom != getEndAtom()) { return 0.0; } double res; if ((getBondType() == DATIVE || getBondType() == DATIVEONE) && atom->getIdx() != getEndAtomIdx()) { res = 0.0; } else if (getBondType() == HYDROGEN) { res = 0.0; } else { res = getBondTypeAsDouble(); } return res; } void Bond::setQuery(QUERYBOND_QUERY *) { // Bonds don't have queries at the moment because I have not // yet figured out what a good base query should be. // It would be nice to be able to do substructure searches // using molecules alone, so it'd be nice if we got this // issue resolved ASAP. PRECONDITION(0, "plain bonds have no Query"); } Bond::QUERYBOND_QUERY *Bond::getQuery() const { PRECONDITION(0, "plain bonds have no Query"); return nullptr; }; bool Bond::Match(Bond const *what) const { bool res; if (getBondType() == Bond::UNSPECIFIED || what->getBondType() == Bond::UNSPECIFIED) { res = true; } else { res = getBondType() == what->getBondType(); } return res; }; void Bond::expandQuery(Bond::QUERYBOND_QUERY *, Queries::CompositeQueryType, bool) { PRECONDITION(0, "plain bonds have no query"); }; void Bond::initBond() { d_bondType = UNSPECIFIED; d_dirTag = NONE; d_stereo = STEREONONE; dp_mol = nullptr; d_beginAtomIdx = 0; d_endAtomIdx = 0; df_isAromatic = 0; d_index = 0; df_isConjugated = 0; dp_stereoAtoms = nullptr; }; void Bond::setStereoAtoms(unsigned int bgnIdx, unsigned int endIdx) { PRECONDITION( getOwningMol().getBondBetweenAtoms(getBeginAtomIdx(), bgnIdx) != nullptr, "bgnIdx not connected to begin atom of bond"); PRECONDITION( getOwningMol().getBondBetweenAtoms(getEndAtomIdx(), endIdx) != nullptr, "endIdx not connected to end atom of bond"); auto &atoms = getStereoAtoms(); atoms.clear(); atoms.push_back(bgnIdx); atoms.push_back(endIdx); }; uint8_t getTwiceBondType(const Bond &b) { switch (b.getBondType()) { case Bond::UNSPECIFIED: case Bond::IONIC: case Bond::ZERO: return 0; break; case Bond::SINGLE: return 2; break; case Bond::DOUBLE: return 4; break; case Bond::TRIPLE: return 6; break; case Bond::QUADRUPLE: return 8; break; case Bond::QUINTUPLE: return 10; break; case Bond::HEXTUPLE: return 12; break; case Bond::ONEANDAHALF: return 3; break; case Bond::TWOANDAHALF: return 5; break; case Bond::THREEANDAHALF: return 7; break; case Bond::FOURANDAHALF: return 9; break; case Bond::FIVEANDAHALF: return 11; break; case Bond::AROMATIC: return 3; break; case Bond::DATIVEONE: return 2; break; // FIX: this should probably be different case Bond::DATIVE: return 2; break; // FIX: again probably wrong case Bond::HYDROGEN: return 0; break; default: UNDER_CONSTRUCTION("Bad bond type"); } } bool Bond::invertChirality() { switch (getStereo()) { case STEREOATROPCW: setStereo(STEREOATROPCCW); return true; case STEREOATROPCCW: setStereo(STEREOATROPCW); return true; default: break; } return false; } }; // namespace RDKit namespace { constexpr const char *bondTypeToString(RDKit::Bond::BondType d) { switch (d) { case RDKit::Bond::BondType::UNSPECIFIED: return "?"; case RDKit::Bond::BondType::SINGLE: return "1"; case RDKit::Bond::BondType::DOUBLE: return "2"; case RDKit::Bond::BondType::TRIPLE: return "3"; case RDKit::Bond::BondType::QUADRUPLE: return "4"; case RDKit::Bond::BondType::QUINTUPLE: return "5"; case RDKit::Bond::BondType::HEXTUPLE: return "6"; case RDKit::Bond::BondType::ONEANDAHALF: return "1.5"; case RDKit::Bond::BondType::TWOANDAHALF: return "2.5"; case RDKit::Bond::BondType::THREEANDAHALF: return "3.5"; case RDKit::Bond::BondType::FOURANDAHALF: return "4.5"; case RDKit::Bond::BondType::FIVEANDAHALF: return "5.5"; case RDKit::Bond::BondType::AROMATIC: return "a"; case RDKit::Bond::BondType::IONIC: return "I"; case RDKit::Bond::BondType::HYDROGEN: return "H"; case RDKit::Bond::BondType::THREECENTER: return "3C"; case RDKit::Bond::BondType::DATIVEONE: return "D1"; case RDKit::Bond::BondType::DATIVE: return "D"; case RDKit::Bond::BondType::DATIVEL: return "DL"; case RDKit::Bond::BondType::DATIVER: return "DR"; case RDKit::Bond::BondType::OTHER: return "Other"; case RDKit::Bond::BondType::ZERO: return "0"; } return (""); } constexpr const char *bondDirToString(RDKit::Bond::BondDir d) { switch (d) { case RDKit::Bond::BondDir::NONE: return "NONE"; case RDKit::Bond::BondDir::BEGINWEDGE: return "wedge"; case RDKit::Bond::BondDir::BEGINDASH: return "dash"; case RDKit::Bond::BondDir::ENDDOWNRIGHT: return "\\"; case RDKit::Bond::BondDir::ENDUPRIGHT: return "/"; case RDKit::Bond::BondDir::EITHERDOUBLE: return "x"; case RDKit::Bond::BondDir::UNKNOWN: return "?"; } return (""); } constexpr const char *bondStereoToString(RDKit::Bond::BondStereo d) { switch (d) { case RDKit::Bond::BondStereo::STEREONONE: return "NONE"; case RDKit::Bond::BondStereo::STEREOANY: return "ANY"; case RDKit::Bond::BondStereo::STEREOZ: return "Z"; case RDKit::Bond::BondStereo::STEREOE: return "E"; case RDKit::Bond::BondStereo::STEREOCIS: return "CIS"; case RDKit::Bond::BondStereo::STEREOTRANS: return "TRANS"; case RDKit::Bond::BondStereo::STEREOATROPCW: return "CW"; case RDKit::Bond::BondStereo::STEREOATROPCCW: return "CCW"; } return (""); } } // namespace std::ostream &operator<<(std::ostream &target, const RDKit::Bond &bond) { target << bond.getIdx() << " "; target << bond.getBeginAtomIdx() << "->" << bond.getEndAtomIdx(); target << " order: " << bondTypeToString(bond.getBondType()); if (bond.getBondDir()) { target << " dir: " << bondDirToString(bond.getBondDir()); } if (bond.getStereo()) { target << " stereo: " << bondStereoToString(bond.getStereo()); if (bond.getStereoAtoms().size() == 2) { const auto &ats = bond.getStereoAtoms(); target << " ats: (" << ats[0] << " " << ats[1] << ")"; } if (bond.getStereo() == RDKit::Bond::BondStereo::STEREOATROPCCW || bond.getStereo() == RDKit::Bond::BondStereo::STEREOATROPCW) { RDKit::Atropisomers::AtropAtomAndBondVec atomAndBonds[2]; if (RDKit::Atropisomers::getAtropisomerAtomsAndBonds( &bond, atomAndBonds, bond.getOwningMol())) { target << " bonds: ("; for (auto i = 0u; i < atomAndBonds[0].second.size(); ++i) { if (i) { target << " "; } target << atomAndBonds[0].second[i]->getIdx(); } for (auto i = 0u; i < atomAndBonds[1].second.size(); ++i) { target << " " << atomAndBonds[1].second[i]->getIdx(); } target << ")"; } } } if (bond.getIsConjugated()) { target << " conj?: " << bond.getIsConjugated(); } if (bond.getIsAromatic()) { target << " aromatic?: " << bond.getIsAromatic(); } return target; }