void ClearSingleBondDirFlags(ROMol &mol) { for (RWMol::BondIterator bondIt = mol.beginBonds(); bondIt != mol.endBonds(); ++bondIt) { if ((*bondIt)->getBondType() == Bond::SINGLE) { if ((*bondIt)->getBondDir() == Bond::UNKNOWN) (*bondIt)->setProp(common_properties::_UnknownStereo, 1); (*bondIt)->setBondDir(Bond::NONE); } } }
void WedgeMolBonds(ROMol &mol, const Conformer *conf) { PRECONDITION(conf, "no conformer"); INT_MAP_INT wedgeBonds = pickBondsToWedge(mol); for (ROMol::BondIterator bondIt = mol.beginBonds(); bondIt != mol.endBonds(); ++bondIt) { Bond *bond = *bondIt; if (bond->getBondType() == Bond::SINGLE) { Bond::BondDir dir = DetermineBondWedgeState(bond, wedgeBonds, conf); if (dir == Bond::BEGINWEDGE || dir == Bond::BEGINDASH) { bond->setBondDir(dir); } } } }
// ------------------------------------------------------------------------ // // the two-bit matrix returned by this contains: // 0: if atoms i and j are directly connected // 1: if atoms i and j are connected via an atom // 2: if atoms i and j are in a 1,4 relationship // 3: otherwise // // NOTE: the caller is responsible for calling delete [] // on the result // // ------------------------------------------------------------------------ boost::shared_array<boost::uint8_t> buildNeighborMatrix(const ROMol &mol) { const boost::uint8_t RELATION_1_X_INIT = RELATION_1_X | (RELATION_1_X << 2) | (RELATION_1_X << 4) | (RELATION_1_X << 6); unsigned int nAtoms = mol.getNumAtoms(); unsigned nTwoBitCells = (nAtoms * (nAtoms + 1) - 1) / 8 + 1; boost::shared_array<boost::uint8_t> res(new boost::uint8_t[nTwoBitCells]); std::memset(res.get(), RELATION_1_X_INIT, nTwoBitCells); for (ROMol::ConstBondIterator bondi = mol.beginBonds(); bondi != mol.endBonds(); ++bondi) { setTwoBitCell(res, twoBitCellPos(nAtoms, (*bondi)->getBeginAtomIdx(), (*bondi)->getEndAtomIdx()), RELATION_1_2); unsigned int bondiBeginAtomIdx = (*bondi)->getBeginAtomIdx(); unsigned int bondiEndAtomIdx = (*bondi)->getEndAtomIdx(); for (ROMol::ConstBondIterator bondj = bondi; ++bondj != mol.endBonds();) { int idx1 = -1; int idx3 = -1; unsigned int bondjBeginAtomIdx = (*bondj)->getBeginAtomIdx(); unsigned int bondjEndAtomIdx = (*bondj)->getEndAtomIdx(); if (bondiBeginAtomIdx == bondjBeginAtomIdx) { idx1 = bondiEndAtomIdx; idx3 = bondjEndAtomIdx; } else if (bondiBeginAtomIdx == bondjEndAtomIdx) { idx1 = bondiEndAtomIdx; idx3 = bondjBeginAtomIdx; } else if (bondiEndAtomIdx == bondjBeginAtomIdx) { idx1 = bondiBeginAtomIdx; idx3 = bondjEndAtomIdx; } else if (bondiEndAtomIdx == bondjEndAtomIdx) { idx1 = bondiBeginAtomIdx; idx3 = bondjBeginAtomIdx; } if (idx1 > -1) { setTwoBitCell(res, twoBitCellPos(nAtoms, idx1, idx3), RELATION_1_3); } } } return res; }
void setConjugation(ROMol &mol) { // start with all bonds being marked unconjugated // except for aromatic bonds ROMol::BondIterator bi; for (bi = mol.beginBonds(); bi != mol.endBonds(); bi++) { if ((*bi)->getIsAromatic()) { (*bi)->setIsConjugated(true); } else { (*bi)->setIsConjugated(false); } } ROMol::AtomIterator ai; // loop over each atom and check if the bonds connecting to it can // be conjugated for (ai = mol.beginAtoms(); ai != mol.endAtoms(); ai++) { markConjAtomBonds(*ai); } }
void getBtVectVect(ResonanceMolSupplier *resMolSuppl, std::vector<std::vector<unsigned int> > &btVect2) { while (!resMolSuppl->atEnd()) { ROMol *resMol = resMolSuppl->next(); std::vector<unsigned int> bt; bt.reserve(resMol->getNumBonds()); for (ROMol::BondIterator bi = resMol->beginBonds(); bi != resMol->endBonds(); ++bi) bt.push_back(static_cast<unsigned int>((*bi)->getBondTypeAsDouble())); btVect2.push_back(bt); delete resMol; } for (unsigned int i = 0; i < btVect2.size(); ++i) { bool same = true; for (unsigned int j = 0; same && (j < btVect2[i].size()); ++j) { if (!i) continue; if (same) same = (btVect2[i][j] == btVect2[i - 1][j]); } if (i) TEST_ASSERT(!same); } }
std::string MolToHELM(const ROMol &mol) { std::vector<AtomPDBResidueInfo*> seq[10]; std::string result; bool first = true; std::string chain; int id = 1; /* First pass: Monomers */ for (ROMol::ConstAtomIterator atomIt=mol.beginAtoms(); atomIt!=mol.endAtoms();++atomIt){ const Atom *atom = *atomIt; AtomPDBResidueInfo *info = (AtomPDBResidueInfo*)(atom->getMonomerInfo()); // We can only write HELM if all atoms have PDB residue information if (!info || info->getMonomerType()!=AtomMonomerInfo::PDBRESIDUE) return ""; if (info->getName() == " CA ") { const char *mono = getHELMMonomer(info); if (!mono) return ""; if (first) { chain = info->getChainId(); result = "PEPTIDE1{"; first = false; } else if (info->getChainId() != chain) { // Nine chains should be enough? if (id == 9) return ""; id++; chain = info->getChainId(); result += "}|PEPTIDE"; result += (char)(id+'0'); result += "{"; } else result += "."; result += mono; seq[id].push_back(info); } else if (info->getResidueName() == "NH2" && info->getName() == " N ") { if (first) return ""; result += ".[am]"; } else if (info->getResidueName() == "ACE" && info->getName() == " C ") { if (first) { chain = info->getChainId(); result = "PEPTIDE1{[ac]"; first = false; } else if (info->getChainId() != chain) { // Nine chains should be enough? if (id == 9) return ""; id++; chain = info->getChainId(); result += "}|PEPTIDE"; result += (char)(id+'0'); result += "{[ac]"; } else return ""; seq[id].push_back(info); } } if (first) return ""; result += "}$"; first = true; for (ROMol::ConstBondIterator bondIt=mol.beginBonds(); bondIt!=mol.endBonds(); ++bondIt){ const Bond *bond = *bondIt; Atom *beg = bond->getBeginAtom(); Atom *end = bond->getEndAtom(); if (!beg || !end) continue; AtomPDBResidueInfo *binfo = (AtomPDBResidueInfo*)(beg->getMonomerInfo()); AtomPDBResidueInfo *einfo = (AtomPDBResidueInfo*)(end->getMonomerInfo()); if (!binfo || !einfo) continue; // Test if this is an uninteresting intra-residue bond if (binfo->getResidueNumber() == einfo->getResidueNumber() && binfo->getResidueName() == einfo->getResidueName() && binfo->getChainId() == einfo->getChainId() && binfo->getInsertionCode() == einfo->getInsertionCode()) continue; if (bond->getBondType() != Bond::SINGLE) return ""; if (IsEupeptideBond(binfo,einfo)) continue; if (!IsSupportedHELMBond(binfo,einfo)) return ""; std::string tmp = NameHELMBond(seq,binfo,einfo); if (tmp.empty()) return ""; if (!first) result += "|"; else first = false; result += tmp; } result += "$$$"; return result; }
void DetectBondStereoChemistry(ROMol &mol, const Conformer *conf) { PRECONDITION(conf, "no conformer"); #if 0 std::cerr << ">>>>>>>>>>>>>>>>>>>>>*\n"; std::cerr << ">>>>>>>>>>>>>>>>>>>>>*\n"; std::cerr << ">>>>>>>>>>>>>>>>>>>>>*\n"; std::cerr << "DBSN: "<<"\n"; std::cerr << ">>>>>>>>>>>>>>>>>>>>>*\n"; std::cerr << ">>>>>>>>>>>>>>>>>>>>>*\n"; std::cerr << ">>>>>>>>>>>>>>>>>>>>>*\n"; #endif // used to store the number of single bonds a given // single bond is adjacent to std::vector<unsigned int> singleBondCounts(mol.getNumBonds(), 0); std::vector<Bond *> bondsInPlay; VECT_INT_VECT dblBondNbrs(mol.getNumBonds()); boost::dynamic_bitset<> needsDir(mol.getNumBonds()); // find double bonds that should be considered for // stereochemistry // NOTE that we are explicitly excluding double bonds in rings // with this test. bool resetRings = false; if (!mol.getRingInfo()->isInitialized()) { resetRings = true; MolOps::fastFindRings(mol); } for (RWMol::BondIterator bondIt = mol.beginBonds(); bondIt != mol.endBonds(); ++bondIt) { if ((*bondIt)->getBondType() == Bond::DOUBLE && (*bondIt)->getStereo() != Bond::STEREOANY && (*bondIt)->getBondDir() != Bond::EITHERDOUBLE && (*bondIt)->getBeginAtom()->getDegree() > 1 && (*bondIt)->getEndAtom()->getDegree() > 1 && !(mol.getRingInfo()->numBondRings((*bondIt)->getIdx()))) { const Atom *a1 = (*bondIt)->getBeginAtom(); const Atom *a2 = (*bondIt)->getEndAtom(); ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = mol.getAtomBonds(a1); while (beg != end) { const Bond *nbrBond = mol[*beg].get(); if (nbrBond->getBondType() == Bond::SINGLE || nbrBond->getBondType() == Bond::AROMATIC) { singleBondCounts[nbrBond->getIdx()] += 1; needsDir[nbrBond->getIdx()] = 1; dblBondNbrs[(*bondIt)->getIdx()].push_back(nbrBond->getIdx()); } ++beg; } boost::tie(beg, end) = mol.getAtomBonds(a2); while (beg != end) { const Bond *nbrBond = mol[*beg].get(); if (nbrBond->getBondType() == Bond::SINGLE || nbrBond->getBondType() == Bond::AROMATIC) { singleBondCounts[nbrBond->getIdx()] += 1; needsDir[nbrBond->getIdx()] = 1; dblBondNbrs[(*bondIt)->getIdx()].push_back(nbrBond->getIdx()); } ++beg; } bondsInPlay.push_back(*bondIt); } } if (!bondsInPlay.size()) { if (resetRings) mol.getRingInfo()->reset(); return; } // order the double bonds based on the singleBondCounts of their neighbors: std::vector<std::pair<unsigned int, Bond *> > orderedBondsInPlay; for (unsigned int i = 0; i < bondsInPlay.size(); ++i) { Bond *dblBond = bondsInPlay[i]; unsigned int countHere = std::accumulate(dblBondNbrs[dblBond->getIdx()].begin(), dblBondNbrs[dblBond->getIdx()].end(), 0); // and favor double bonds that are *not* in rings. The combination of using // the sum // above (instead of the max) and this ring-membershipt test seem to fix // sf.net issue 3009836 if (!(mol.getRingInfo()->numBondRings(dblBond->getIdx()))) countHere *= 10; orderedBondsInPlay.push_back(std::make_pair(countHere, dblBond)); } std::sort(orderedBondsInPlay.begin(), orderedBondsInPlay.end()); // oof, now loop over the double bonds in that order and // update their neighbor directionalities: std::vector<std::pair<unsigned int, Bond *> >::reverse_iterator pairIter; for (pairIter = orderedBondsInPlay.rbegin(); pairIter != orderedBondsInPlay.rend(); ++pairIter) { updateDoubleBondNeighbors(mol, pairIter->second, conf, needsDir, singleBondCounts); } if (resetRings) mol.getRingInfo()->reset(); }
INT_MAP_INT pickBondsToWedge(const ROMol &mol) { // we need ring information; make sure findSSSR has been called before // if not call now if (!mol.getRingInfo()->isInitialized()) { MolOps::findSSSR(mol); } static int noNbrs = 100; INT_VECT nChiralNbrs(mol.getNumAtoms(), noNbrs); // start by looking for bonds that are already wedged for (ROMol::ConstBondIterator cbi = mol.beginBonds(); cbi != mol.endBonds(); ++cbi) { const Bond *bond = *cbi; if (bond->getBondDir() == Bond::BEGINWEDGE || bond->getBondDir() == Bond::BEGINDASH || bond->getBondDir() == Bond::UNKNOWN) { nChiralNbrs[bond->getBeginAtomIdx()] = noNbrs + 1; // std::cerr<<"skip: "<<bond->getBeginAtomIdx()<<std::endl; } } // now rank atoms by the number of chiral neighbors or Hs they have: bool chiNbrs = false; for (ROMol::ConstAtomIterator cai = mol.beginAtoms(); cai != mol.endAtoms(); ++cai) { const Atom *at = *cai; if (nChiralNbrs[at->getIdx()] > noNbrs) { // std::cerr<<" SKIPPING1: "<<at->getIdx()<<std::endl; continue; } Atom::ChiralType type = at->getChiralTag(); if (type != Atom::CHI_TETRAHEDRAL_CW && type != Atom::CHI_TETRAHEDRAL_CCW) continue; nChiralNbrs[at->getIdx()] = 0; chiNbrs = true; ROMol::ADJ_ITER nbrIdx, endNbrs; boost::tie(nbrIdx, endNbrs) = mol.getAtomNeighbors(at); while (nbrIdx != endNbrs) { const ATOM_SPTR nat = mol[*nbrIdx]; ++nbrIdx; if (nat->getAtomicNum() == 1) { // special case: it's an H... we weight these especially high: nChiralNbrs[at->getIdx()] -= 10; continue; } type = nat->getChiralTag(); if (type != Atom::CHI_TETRAHEDRAL_CW && type != Atom::CHI_TETRAHEDRAL_CCW) continue; nChiralNbrs[at->getIdx()] -= 1; } } std::vector<unsigned int> indices(mol.getNumAtoms()); for (unsigned int i = 0; i < mol.getNumAtoms(); ++i) indices[i] = i; if (chiNbrs) { std::sort(indices.begin(), indices.end(), Rankers::argless<INT_VECT>(nChiralNbrs)); } #if 0 std::cerr<<" nbrs: "; std::copy(nChiralNbrs.begin(),nChiralNbrs.end(),std::ostream_iterator<int>(std::cerr," ")); std::cerr<<std::endl; std::cerr<<" order: "; std::copy(indices.begin(),indices.end(),std::ostream_iterator<int>(std::cerr," ")); std::cerr<<std::endl; #endif // picks a bond for each atom that we will wedge when we write the mol file // here is what we are going to do // - at each chiral center look for a bond that is begins at the atom and // is not yet picked to be wedged for a different chiral center, preferring // bonds to Hs // - if we do not find a bond that begins at the chiral center - we will take // the first bond that is not yet picked by any other chiral centers // we use the orders calculated above to determine which order to do the // wedging INT_MAP_INT res; BOOST_FOREACH (unsigned int idx, indices) { if (nChiralNbrs[idx] > noNbrs) { // std::cerr<<" SKIPPING2: "<<idx<<std::endl; continue; // already have a wedged bond here } const Atom *atom = mol.getAtomWithIdx(idx); Atom::ChiralType type = atom->getChiralTag(); // the indices are ordered such that all chiral atoms come first. If // this has no chiral flag, we can stop the whole loop: if (type != Atom::CHI_TETRAHEDRAL_CW && type != Atom::CHI_TETRAHEDRAL_CCW) break; RDKit::ROMol::OBOND_ITER_PAIR atomBonds = mol.getAtomBonds(atom); std::vector<std::pair<int, int> > nbrScores; while (atomBonds.first != atomBonds.second) { const Bond *bond = mol[*atomBonds.first].get(); ++atomBonds.first; // can only wedge single bonds: if (bond->getBondType() != Bond::SINGLE) continue; int bid = bond->getIdx(); if (res.find(bid) == res.end()) { // very strong preference for Hs: if (bond->getOtherAtom(atom)->getAtomicNum() == 1) { nbrScores.push_back( std::make_pair(-1000, bid)); // lower than anything else can be continue; } int nbrScore = 0; // prefer neighbors that are nonchiral or have as few chiral neighbors // as possible: int oIdx = bond->getOtherAtomIdx(idx); if (nChiralNbrs[oIdx] < noNbrs) { // the counts are negative, so we have to subtract them off nbrScore -= 10 * nChiralNbrs[oIdx]; } // prefer non-ring bonds; nbrScore += mol.getRingInfo()->numBondRings(bid); nbrScores.push_back(std::make_pair(nbrScore, bid)); } } // There's still one situation where this whole thing can fail: an unlucky // situation where all neighbors of all neighbors of an atom are chiral and // that atom ends up being the last one picked for stereochem assignment. // // We'll catch that as an error here and hope that it's as unlikely to occur // as it seems like it is. (I'm going into this knowing that it's bound to // happen; I'll kick myself and do the hard solution at that point.) CHECK_INVARIANT(nbrScores.size(), "no eligible neighbors for chiral center"); std::sort(nbrScores.begin(), nbrScores.end(), Rankers::pairLess<int, int>()); res[nbrScores[0].second] = idx; } return res; }
double getLabuteAtomContribs(const ROMol &mol, std::vector<double> &Vi, double &hContrib, bool includeHs, bool force) { TEST_ASSERT(Vi.size() == mol.getNumAtoms()); if (!force && mol.hasProp(common_properties::_labuteAtomContribs)) { mol.getProp(common_properties::_labuteAtomContribs, Vi); mol.getProp(common_properties::_labuteAtomHContrib, hContrib); double res; mol.getProp(common_properties::_labuteASA, res); return res; } unsigned int nAtoms = mol.getNumAtoms(); std::vector<double> rads(nAtoms); for (unsigned int i = 0; i < nAtoms; ++i) { rads[i] = PeriodicTable::getTable()->getRb0( mol.getAtomWithIdx(i)->getAtomicNum()); Vi[i] = 0.0; } for (ROMol::ConstBondIterator bondIt = mol.beginBonds(); bondIt != mol.endBonds(); ++bondIt) { const double bondScaleFacts[4] = {.1, 0, .2, .3}; double Ri = rads[(*bondIt)->getBeginAtomIdx()]; double Rj = rads[(*bondIt)->getEndAtomIdx()]; double bij = Ri + Rj; if (!(*bondIt)->getIsAromatic()) { if ((*bondIt)->getBondType() < 4) { bij -= bondScaleFacts[(*bondIt)->getBondType()]; } } else { bij -= bondScaleFacts[0]; } double dij = std::min(std::max(fabs(Ri - Rj), bij), Ri + Rj); Vi[(*bondIt)->getBeginAtomIdx()] += Rj * Rj - (Ri - dij) * (Ri - dij) / dij; Vi[(*bondIt)->getEndAtomIdx()] += Ri * Ri - (Rj - dij) * (Rj - dij) / dij; } hContrib = 0.0; if (includeHs) { double Rj = PeriodicTable::getTable()->getRb0(1); for (unsigned int i = 0; i < nAtoms; ++i) { double Ri = rads[i]; double bij = Ri + Rj; double dij = std::min(std::max(fabs(Ri - Rj), bij), Ri + Rj); Vi[i] += Rj * Rj - (Ri - dij) * (Ri - dij) / dij; hContrib += Ri * Ri - (Rj - dij) * (Rj - dij) / dij; } } double res = 0.0; for (unsigned int i = 0; i < nAtoms; ++i) { double Ri = rads[i]; Vi[i] = M_PI * Ri * (4. * Ri - Vi[i]); res += Vi[i]; } if (includeHs) { double Rj = PeriodicTable::getTable()->getRb0(1); hContrib = M_PI * Rj * (4. * Rj - hContrib); res += hContrib; } mol.setProp(common_properties::_labuteAtomContribs, Vi, true); mol.setProp(common_properties::_labuteAtomHContrib, hContrib, true); mol.setProp(common_properties::_labuteASA, res, true); return res; }
double getTPSAAtomContribs(const ROMol &mol, std::vector<double> &Vi, bool force) { TEST_ASSERT(Vi.size() >= mol.getNumAtoms()); double res = 0; if (!force && mol.hasProp(common_properties::_tpsaAtomContribs)) { mol.getProp(common_properties::_tpsaAtomContribs, Vi); mol.getProp(common_properties::_tpsa, res); return res; } unsigned int nAtoms = mol.getNumAtoms(); std::vector<int> nNbrs(nAtoms, 0), nSing(nAtoms, 0), nDoub(nAtoms, 0), nTrip(nAtoms, 0), nArom(nAtoms, 0), nHs(nAtoms, 0); for (ROMol::ConstBondIterator bIt = mol.beginBonds(); bIt != mol.endBonds(); ++bIt) { const Bond *bnd = (*bIt); if (bnd->getBeginAtom()->getAtomicNum() == 1) { nNbrs[bnd->getEndAtomIdx()] -= 1; nHs[bnd->getEndAtomIdx()] += 1; } else if (bnd->getEndAtom()->getAtomicNum() == 1) { nNbrs[bnd->getBeginAtomIdx()] -= 1; nHs[bnd->getBeginAtomIdx()] += 1; } else if (bnd->getIsAromatic()) { nArom[bnd->getBeginAtomIdx()] += 1; nArom[bnd->getEndAtomIdx()] += 1; } else { switch (bnd->getBondType()) { case Bond::SINGLE: nSing[bnd->getBeginAtomIdx()] += 1; nSing[bnd->getEndAtomIdx()] += 1; break; case Bond::DOUBLE: nDoub[bnd->getBeginAtomIdx()] += 1; nDoub[bnd->getEndAtomIdx()] += 1; break; case Bond::TRIPLE: nTrip[bnd->getBeginAtomIdx()] += 1; nTrip[bnd->getEndAtomIdx()] += 1; break; default: break; } } } for (unsigned int i = 0; i < nAtoms; ++i) { const Atom *atom = mol.getAtomWithIdx(i); int atNum = atom->getAtomicNum(); if (atNum != 7 && atNum != 8) continue; nHs[i] += atom->getTotalNumHs(); int chg = atom->getFormalCharge(); bool in3Ring = mol.getRingInfo()->isAtomInRingOfSize(i, 3); nNbrs[i] += atom->getDegree(); double tmp = -1; if (atNum == 7) { switch (nNbrs[i]) { case 1: if (nHs[i] == 0 && chg == 0 && nTrip[i] == 1) tmp = 23.79; else if (nHs[i] == 1 && chg == 0 && nDoub[i] == 1) tmp = 23.85; else if (nHs[i] == 2 && chg == 0 && nSing[i] == 1) tmp = 26.02; else if (nHs[i] == 2 && chg == 1 && nDoub[i] == 1) tmp = 25.59; else if (nHs[i] == 3 && chg == 1 && nSing[i] == 1) tmp = 27.64; break; case 2: if (nHs[i] == 0 && chg == 0 && nSing[i] == 1 && nDoub[i] == 1) tmp = 12.36; else if (nHs[i] == 0 && chg == 0 && nTrip[i] == 1 && nDoub[i] == 1) tmp = 13.60; else if (nHs[i] == 1 && chg == 0 && nSing[i] == 2 && in3Ring) tmp = 21.94; else if (nHs[i] == 1 && chg == 0 && nSing[i] == 2 && !in3Ring) tmp = 12.03; else if (nHs[i] == 0 && chg == 1 && nTrip[i] == 1 && nSing[i] == 1) tmp = 4.36; else if (nHs[i] == 1 && chg == 1 && nDoub[i] == 1 && nSing[i] == 1) tmp = 13.97; else if (nHs[i] == 2 && chg == 1 && nSing[i] == 2) tmp = 16.61; else if (nHs[i] == 0 && chg == 0 && nArom[i] == 2) tmp = 12.89; else if (nHs[i] == 1 && chg == 0 && nArom[i] == 2) tmp = 15.79; else if (nHs[i] == 1 && chg == 1 && nArom[i] == 2) tmp = 14.14; break; case 3: if (nHs[i] == 0 && chg == 0 && nSing[i] == 3 && in3Ring) tmp = 3.01; else if (nHs[i] == 0 && chg == 0 && nSing[i] == 3 && !in3Ring) tmp = 3.24; else if (nHs[i] == 0 && chg == 0 && nSing[i] == 1 && nDoub[i] == 2) tmp = 11.68; else if (nHs[i] == 0 && chg == 1 && nSing[i] == 2 && nDoub[i] == 1) tmp = 3.01; else if (nHs[i] == 1 && chg == 1 && nSing[i] == 3) tmp = 4.44; else if (nHs[i] == 0 && chg == 0 && nArom[i] == 3) tmp = 4.41; else if (nHs[i] == 0 && chg == 0 && nSing[i] == 1 && nArom[i] == 2) tmp = 4.93; else if (nHs[i] == 0 && chg == 0 && nDoub[i] == 1 && nArom[i] == 2) tmp = 8.39; else if (nHs[i] == 0 && chg == 1 && nArom[i] == 3) tmp = 4.10; else if (nHs[i] == 0 && chg == 1 && nSing[i] == 1 && nArom[i] == 2) tmp = 3.88; break; case 4: if (nHs[i] == 0 && nSing[i] == 4 && chg == 1) tmp = 0.0; break; } if (tmp < 0.0) { tmp = 30.5 - nNbrs[i] * 8.2 + nHs[i] * 1.5; if (tmp < 0) tmp = 0.0; } } else if (atNum == 8) { switch (nNbrs[i]) { case 1: if (nHs[i] == 0 && chg == 0 && nDoub[i] == 1) tmp = 17.07; else if (nHs[i] == 1 && chg == 0 && nSing[i] == 1) tmp = 20.23; else if (nHs[i] == 0 && chg == -1 && nSing[i] == 1) tmp = 23.06; break; case 2: if (nHs[i] == 0 && chg == 0 && nSing[i] == 2 && in3Ring) tmp = 12.53; else if (nHs[i] == 0 && chg == 0 && nSing[i] == 2 && !in3Ring) tmp = 9.23; else if (nHs[i] == 0 && chg == 0 && nArom[i] == 2) tmp = 13.14; break; } if (tmp < 0.0) { tmp = 28.5 - nNbrs[i] * 8.6 + nHs[i] * 1.5; if (tmp < 0) tmp = 0.0; } } Vi[i] = tmp; res += tmp; } mol.setProp(common_properties::_tpsaAtomContribs, Vi, true); mol.setProp(common_properties::_tpsa, res, true); return res; }