// // Copyright (C) 2013 Greg Landrum and NextMove Software // // @@ 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 "ProximityBonds.h" #include #include #include #include namespace RDKit { static const double EXTDIST = 0.45; // static const double MAXRAD = 2.50; // static const double MINDIST = 0.40; static const double MAXDIST = 5.45; // 2*MAXRAD + EXTDIST static const double MINDIST2 = 0.16; // MINDIST*MINDIST static const double MAXDIST2 = 29.7025; // MAXDIST*MAXDIST struct ProximityEntry { float x, y, z, r; int atm, hash, next; bool operator<(const ProximityEntry &p) const { return x < p.x; } }; static bool IsBonded(ProximityEntry *p, ProximityEntry *q) { double dx = (double)p->x - (double)q->x; double dist2 = dx * dx; if (dist2 > MAXDIST2) return false; double dy = (double)p->y - (double)q->y; dist2 += dy * dy; if (dist2 > MAXDIST2) return false; double dz = (double)p->z - (double)q->z; dist2 += dz * dz; if (dist2 > MAXDIST2 || dist2 < MINDIST2) return false; double radius = (double)p->r + (double)q->r + EXTDIST; return dist2 <= radius * radius; } /* static void ConnectTheDots_Small(RWMol *mol) { unsigned int count = mol->getNumAtoms(); ProximityEntry *tmp = (ProximityEntry*)malloc(count*sizeof(ProximityEntry)); PeriodicTable *table = PeriodicTable::getTable(); Conformer *conf = &mol->getConformer(); for (unsigned int i=0; igetAtomWithIdx(i); unsigned int elem = atom->getAtomicNum(); RDGeom::Point3D p = conf->getAtomPos(i); ProximityEntry *tmpi = tmp+i; tmpi->x = (float)p.x; tmpi->y = (float)p.y; tmpi->z = (float)p.z; tmpi->r = (float)table->getRcovalent(elem); for (unsigned int j=0; jgetBondBetweenAtoms(i,j)) mol->addBond(i,j,Bond::SINGLE); } } free(tmp); } static void ConnectTheDots_Medium(RWMol *mol) { int count = mol->getNumAtoms(); std::vector tmp(count); PeriodicTable *table = PeriodicTable::getTable(); Conformer *conf = &mol->getConformer(); for (int i=0; igetAtomWithIdx(i); unsigned int elem = atom->getAtomicNum(); RDGeom::Point3D p = conf->getAtomPos(i); ProximityEntry *tmpi = &tmp[i]; tmpi->x = (float)p.x; tmpi->y = (float)p.y; tmpi->z = (float)p.z; tmpi->r = (float)table->getRcovalent(elem); tmpi->atm = i; } std::stable_sort(tmp.begin(),tmp.end()); for (int j=0; jx - MAXDIST; for (int k=j-1; k>=0; k--) { ProximityEntry *tmpk = &tmp[k]; if (tmpk->x < limit) break; if (IsBonded(tmpj,tmpk) && !mol->getBondBetweenAtoms(tmpj->atm,tmpk->atm)) mol->addBond(tmpj->atm,tmpk->atm,Bond::SINGLE); } } } */ #define HASHSIZE 1024 #define HASHMASK 1023 #define HASHX 571 #define HASHY 127 #define HASHZ 3 static void ConnectTheDots_Large(RWMol *mol) { int HashTable[HASHSIZE]; memset(HashTable, -1, sizeof(HashTable)); unsigned int count = mol->getNumAtoms(); ProximityEntry *tmp = (ProximityEntry *)malloc(count * sizeof(ProximityEntry)); PeriodicTable *table = PeriodicTable::getTable(); Conformer *conf = &mol->getConformer(); for (unsigned int i = 0; i < count; i++) { Atom *atom = mol->getAtomWithIdx(i); unsigned int elem = atom->getAtomicNum(); RDGeom::Point3D p = conf->getAtomPos(i); ProximityEntry *tmpi = tmp + i; tmpi->x = (float)p.x; tmpi->y = (float)p.y; tmpi->z = (float)p.z; tmpi->r = (float)table->getRcovalent(elem); tmpi->atm = i; int hash = HASHX * (int)(p.x / MAXDIST) + HASHY * (int)(p.y / MAXDIST) + HASHZ * (int)(p.z / MAXDIST); for (int dx = -HASHX; dx <= HASHX; dx += HASHX) for (int dy = -HASHY; dy <= HASHY; dy += HASHY) for (int dz = -HASHZ; dz <= HASHZ; dz += HASHZ) { int probe = hash + dx + dy + dz; int list = HashTable[probe & HASHMASK]; while (list != -1) { ProximityEntry *tmpj = &tmp[list]; if (tmpj->hash == probe && IsBonded(tmpi, tmpj) && !mol->getBondBetweenAtoms(tmpi->atm, tmpj->atm)) mol->addBond(tmpi->atm, tmpj->atm, Bond::SINGLE); list = tmpj->next; } } int list = hash & HASHMASK; tmpi->next = HashTable[list]; HashTable[list] = i; tmpi->hash = hash; } free(tmp); } void ConnectTheDots(RWMol *mol) { if (!mol || !mol->getNumConformers()) return; // Determine optimal algorithm to use by getNumAtoms()? ConnectTheDots_Large(mol); } bool SamePDBResidue(AtomPDBResidueInfo *p, AtomPDBResidueInfo *q) { return p->getResidueNumber() == q->getResidueNumber() && p->getResidueName() == q->getResidueName() && p->getChainId() == q->getChainId() && p->getInsertionCode() == q->getInsertionCode(); } // These are macros to allow their use in C++ constants #define BCNAM(A, B, C) (((A) << 16) | ((B) << 8) | (C)) #define BCATM(A, B, C, D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D)) static bool StandardPDBDoubleBond(unsigned int rescode, unsigned int atm1, unsigned int atm2) { if (atm1 > atm2) { unsigned int tmp = atm1; atm1 = atm2; atm2 = tmp; } switch (rescode) { case BCNAM('A', 'L', 'A'): case BCNAM('C', 'Y', 'S'): case BCNAM('G', 'L', 'Y'): case BCNAM('I', 'L', 'E'): case BCNAM('L', 'E', 'U'): case BCNAM('L', 'Y', 'S'): case BCNAM('M', 'E', 'T'): case BCNAM('P', 'R', 'O'): case BCNAM('S', 'E', 'R'): case BCNAM('T', 'H', 'R'): case BCNAM('V', 'A', 'L'): if (atm1 == BCATM(' ', 'C', ' ', ' ') && atm2 == BCATM(' ', 'O', ' ', ' ')) return true; break; case BCNAM('A', 'R', 'G'): if (atm1 == BCATM(' ', 'C', ' ', ' ') && atm2 == BCATM(' ', 'O', ' ', ' ')) return true; if (atm1 == BCATM(' ', 'C', 'Z', ' ') && atm2 == BCATM(' ', 'N', 'H', '2')) return true; break; case BCNAM('A', 'S', 'N'): case BCNAM('A', 'S', 'P'): if (atm1 == BCATM(' ', 'C', ' ', ' ') && atm2 == BCATM(' ', 'O', ' ', ' ')) return true; if (atm1 == BCATM(' ', 'C', 'G', ' ') && atm2 == BCATM(' ', 'O', 'D', '1')) return true; break; case BCNAM('G', 'L', 'N'): case BCNAM('G', 'L', 'U'): if (atm1 == BCATM(' ', 'C', ' ', ' ') && atm2 == BCATM(' ', 'O', ' ', ' ')) return true; if (atm1 == BCATM(' ', 'C', 'D', ' ') && atm2 == BCATM(' ', 'O', 'E', '1')) return true; break; case BCNAM('H', 'I', 'S'): if (atm1 == BCATM(' ', 'C', ' ', ' ') && atm2 == BCATM(' ', 'O', ' ', ' ')) return true; if (atm1 == BCATM(' ', 'C', 'D', '2') && atm2 == BCATM(' ', 'C', 'G', ' ')) return true; if (atm1 == BCATM(' ', 'C', 'E', '1') && atm2 == BCATM(' ', 'N', 'D', '1')) return true; break; case BCNAM('P', 'H', 'E'): case BCNAM('T', 'Y', 'R'): if (atm1 == BCATM(' ', 'C', ' ', ' ') && atm2 == BCATM(' ', 'O', ' ', ' ')) return true; if (atm1 == BCATM(' ', 'C', 'D', '1') && atm2 == BCATM(' ', 'C', 'G', ' ')) return true; if (atm1 == BCATM(' ', 'C', 'D', '2') && atm2 == BCATM(' ', 'C', 'E', '2')) return true; if (atm1 == BCATM(' ', 'C', 'E', '1') && atm2 == BCATM(' ', 'C', 'Z', ' ')) return true; break; case BCNAM('T', 'R', 'P'): if (atm1 == BCATM(' ', 'C', ' ', ' ') && atm2 == BCATM(' ', 'O', ' ', ' ')) return true; if (atm1 == BCATM(' ', 'C', 'D', '1') && atm2 == BCATM(' ', 'C', 'G', ' ')) return true; if (atm1 == BCATM(' ', 'C', 'D', '2') && atm2 == BCATM(' ', 'C', 'E', '2')) return true; if (atm1 == BCATM(' ', 'C', 'E', '3') && atm2 == BCATM(' ', 'C', 'Z', '3')) return true; if (atm1 == BCATM(' ', 'C', 'H', '2') && atm2 == BCATM(' ', 'C', 'Z', '2')) return true; break; } return false; } static bool StandardPDBDoubleBond(RWMol *mol, Atom *beg, Atom *end) { AtomPDBResidueInfo *bInfo = (AtomPDBResidueInfo *)beg->getMonomerInfo(); if (!bInfo || bInfo->getMonomerType() != AtomMonomerInfo::PDBRESIDUE) return false; AtomPDBResidueInfo *eInfo = (AtomPDBResidueInfo *)end->getMonomerInfo(); if (!eInfo || eInfo->getMonomerType() != AtomMonomerInfo::PDBRESIDUE) return false; if (!SamePDBResidue(bInfo, eInfo)) return false; if (bInfo->getIsHeteroAtom() || eInfo->getIsHeteroAtom()) return false; const char *ptr = bInfo->getResidueName().c_str(); unsigned int rescode = BCNAM(ptr[0], ptr[1], ptr[2]); ptr = bInfo->getName().c_str(); unsigned int atm1 = BCATM(ptr[0], ptr[1], ptr[2], ptr[3]); ptr = eInfo->getName().c_str(); unsigned int atm2 = BCATM(ptr[0], ptr[1], ptr[2], ptr[3]); if (!StandardPDBDoubleBond(rescode, atm1, atm2)) return false; // Check that neither end already has a double bond ROMol::OBOND_ITER_PAIR bp; for (bp = mol->getAtomBonds(beg); bp.first != bp.second; ++bp.first) if ((*mol)[*bp.first].get()->getBondType() == Bond::DOUBLE) return false; for (bp = mol->getAtomBonds(end); bp.first != bp.second; ++bp.first) if ((*mol)[*bp.first].get()->getBondType() == Bond::DOUBLE) return false; return true; } void StandardPDBResidueBondOrders(RWMol *mol) { RWMol::BondIterator bondIt; for (bondIt = mol->beginBonds(); bondIt != mol->endBonds(); ++bondIt) { Bond *bond = *bondIt; if (bond->getBondType() == Bond::SINGLE) { Atom *beg = bond->getBeginAtom(); Atom *end = bond->getEndAtom(); if (StandardPDBDoubleBond(mol, beg, end)) bond->setBondType(Bond::DOUBLE); } } } } // namespace RDKit