Example #1
0
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);
        }
    }
}
Example #2
0
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);
            }
        }
    }
}
Example #3
0
// ------------------------------------------------------------------------
//
// 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;
}
Example #4
0
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);
  }
}
Example #5
0
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);
  }
}
Example #6
0
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;
}
Example #7
0
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();
}
Example #8
0
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;
}
Example #9
0
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;
}
Example #10
0
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;
}