TEST_F(MoleculeTest, removeBond)
{
  Molecule molecule;
  Atom a = molecule.addAtom(1);
  Atom b = molecule.addAtom(1);
  Bond bondAB = molecule.addBond(a, b);
  Atom c = molecule.addAtom(1);
  molecule.addBond(b, c, 2);

  EXPECT_EQ(3, molecule.atomCount());
  EXPECT_EQ(2, molecule.bondCount());
  EXPECT_TRUE(molecule.bond(a, b).isValid());
  EXPECT_TRUE(molecule.bond(b, c).isValid());

  molecule.removeBond(bondAB);

  EXPECT_EQ(3, molecule.atomCount());
  EXPECT_EQ(1, molecule.bondCount());
  EXPECT_FALSE(molecule.bond(a, b).isValid());
  EXPECT_TRUE(molecule.bond(b, c).isValid());

  molecule.clearBonds();

  EXPECT_EQ(0, molecule.bondCount());
}
TEST_F(MoleculeTest, removeAtom)
{
  Molecule molecule;
  Atom atom0 = molecule.addAtom(6);
  Atom atom1 = molecule.addAtom(1);
  Atom atom2 = molecule.addAtom(1);
  Atom atom3 = molecule.addAtom(1);
  Atom atom4 = molecule.addAtom(1);
  molecule.addBond(atom0, atom1, 1);
  molecule.addBond(atom0, atom2, 1);
  molecule.addBond(atom0, atom3, 1);
  molecule.addBond(atom0, atom4, 1);

  EXPECT_EQ(5, molecule.atomCount());
  EXPECT_EQ(4, molecule.bondCount());

  molecule.removeAtom(atom0);

  EXPECT_EQ(4, molecule.atomCount());
  EXPECT_EQ(0, molecule.bondCount());

  molecule.clearAtoms();

  EXPECT_EQ(0, molecule.atomCount());
}
示例#3
0
TEST(HydrogenToolsTest, valencyAdjustment_O)
{
  Molecule mol;
  Atom O = mol.addAtom(8);
  int expectedAdjustment = 2;
  for (int i = 0; i < 8; ++i, --expectedAdjustment) {
    EXPECT_EQ(expectedAdjustment, HydrogenTools::valencyAdjustment(O));
    mol.addBond(mol.addAtom(1), O, 1);
  }
}
示例#4
0
TEST(HydrogenToolsTest, valencyAdjustment_C)
{
  Molecule mol;
  Atom C = mol.addAtom(6);
  int expectedAdjustment = 4;
  for (int i = 0; i < 8; ++i, --expectedAdjustment) {
    EXPECT_EQ(expectedAdjustment, HydrogenTools::valencyAdjustment(C));
    mol.addBond(mol.addAtom(1), C, 1);
  }
}
示例#5
0
TEST(HydrogenToolsTest, valencyAdjustment_N)
{
  Molecule mol;
  Atom N = mol.addAtom(7);
  int expectedAdjustment = 3;
  for (int i = 0; i < 8; ++i, --expectedAdjustment) {
    if (i == 4) // neutral N can have 3 or 5 bonds in our valence model.
      expectedAdjustment += 2;
    EXPECT_EQ(expectedAdjustment, HydrogenTools::valencyAdjustment(N));
    mol.addBond(mol.addAtom(1), N, 1);
  }
}
TEST(RingPerceiverTest, ethanol)
{
  Molecule molecule;
  molecule.addAtom(6);
  molecule.addAtom(6);
  molecule.addAtom(8);
  molecule.addBond(molecule.atom(0), molecule.atom(1), 1);
  molecule.addBond(molecule.atom(1), molecule.atom(2), 1);

  RingPerceiver perceiver(&molecule);
  std::vector<std::vector<size_t>> rings = perceiver.rings();
  EXPECT_EQ(rings.size(), static_cast<size_t>(0));
}
示例#7
0
TEST(HydrogenToolsTest, adjustHydrogens_C2H4O)
{
  Molecule mol;
  Atom C1 = mol.addAtom(6);
  Atom C2 = mol.addAtom(6);
  Atom O1 = mol.addAtom(8);
  mol.addBond(C1, C2, 1);
  mol.addBond(C2, O1, 2);

  HydrogenTools::adjustHydrogens(mol);
  EXPECT_EQ(7, mol.atomCount());
  EXPECT_EQ(6, mol.bondCount());
  EXPECT_EQ(std::string("C2H4O"), mol.formula());
}
示例#8
0
TEST(HydrogenToolsTest, adjustHydrogens_C3H8)
{
  Molecule mol;
  Atom C1 = mol.addAtom(6);
  Atom C2 = mol.addAtom(6);
  Atom C3 = mol.addAtom(6);
  mol.addBond(C1, C2, 1);
  mol.addBond(C2, C3, 1);

  HydrogenTools::adjustHydrogens(mol);
  EXPECT_EQ(11, mol.atomCount());
  EXPECT_EQ(10, mol.bondCount());
  EXPECT_EQ(std::string("C3H8"), mol.formula());
}
TEST(UnitCellTest, wrapAtomsToUnitCell)
{
  Molecule mol = createCrystal(static_cast<Real>(3.0),
                               static_cast<Real>(4.0),
                               static_cast<Real>(5.0),
                               static_cast<Real>(90.0),
                               static_cast<Real>(120.0),
                               static_cast<Real>(77.0));
  for (int i = 0; i < 10; ++i)
    mol.addAtom(1).setPosition3d(Vector3::Zero());

  Array<Vector3> fcoords;
  for (int i = 0; i < 10; ++i) {
    fcoords.push_back(Vector3(static_cast<Real>(i + i / 10.),
                              static_cast<Real>(i + 2 * i / 10.),
                              static_cast<Real>(i + 3 * i / 10.)));
  }

  EXPECT_TRUE(CrystalTools::setFractionalCoordinates(mol, fcoords));
  EXPECT_TRUE(CrystalTools::wrapAtomsToUnitCell(mol));
  fcoords.clear();
  EXPECT_TRUE(CrystalTools::fractionalCoordinates(mol, fcoords));
  for (std::vector<Vector3>::const_iterator it = fcoords.begin(),
       itEnd = fcoords.end(); it != itEnd; ++it) {
    EXPECT_GE(it->x(), static_cast<Real>(0.0));
    EXPECT_LE(it->x(), static_cast<Real>(1.0));
    EXPECT_GE(it->y(), static_cast<Real>(0.0));
    EXPECT_LE(it->y(), static_cast<Real>(1.0));
    EXPECT_GE(it->z(), static_cast<Real>(0.0));
    EXPECT_LE(it->z(), static_cast<Real>(1.0));
  }
}
示例#10
0
void FileParser::readORCA()
{
    std::string tempString;
    QRegExp rx("", Qt::CaseInsensitive, QRegExp::RegExp2);

    while (1) {
        Molecule *molecule = new Molecule();

        // GOAL TO MATCH -
        //        ---------------------------------
        //        CARTESIAN COORDINATES (ANGSTROEM)
        //        ---------------------------------
        //          N     1.641610   -0.243155    1.175097
        //          C     2.719261   -0.702755    0.549942
        //          C     2.620982   -0.629038   -0.867321
        //          H     3.602940   -1.075919    1.062705
        //          N     1.452945   -0.127890   -1.286957
        //          H     3.405318   -0.967835   -1.537305
        //          N    -1.204954    0.202179    1.259883
        getline(infile, tempString);
        while (tempString.find("CARTESIAN COORDINATES (ANGSTROEM)") == string::npos &&
               infile.eof() == false) {
            getline(infile, tempString);
        }
        if (infile.eof()) {
            break;
        }
#ifdef QT_DEBUG
        std::cout << "readORCA: 'CARTESIAN COORDINATES (ANGSTROEM)' found.\n";
#endif

        // Geometry is reported in Angstroms
        myUnits = Angstrom;

        // Read in atom information
        rx.setPattern("(?:\\s*)(\\w+)(?:\\s+)(-?\\d+\\.\\d+)(?:\\s+)(-?\\d+\\.\\d+)(?:\\s+)(-?\\d+"
                      "\\.\\d+)(?:\\s*)");
        while (1) {
            getline(infile, tempString);
            if (rx.exactMatch(tempString.c_str()) == true) {
                AtomEntry *atom = new AtomEntry;
                atom->Label = rx.cap(1);
                atom->x = rx.cap(2).toDouble();
                atom->y = rx.cap(3).toDouble();
                atom->z = rx.cap(4).toDouble();
                molecule->addAtom(atom);
#ifdef QT_DEBUG
                std::cout << std::setw(5) << atom->Label.toStdString() << " " << std::setw(16)
                          << std::setprecision(10) << atom->x << " " << std::setw(16)
                          << std::setprecision(10) << atom->y << " " << std::setw(16)
                          << std::setprecision(10) << atom->z << std::endl;
#endif
            } else if (tempString.size() == 0) {
                myMoleculeList.push_back(molecule);
                break;
            }
        }
    }
}
示例#11
0
TEST_F(MoleculeTest, addAtom)
{
  Molecule molecule;
  EXPECT_EQ(molecule.atomCount(), static_cast<Index>(0));

  Avogadro::Core::Atom atom = molecule.addAtom(6);
  EXPECT_EQ(atom.isValid(), true);
  EXPECT_EQ(molecule.atomCount(), static_cast<Index>(1));
  EXPECT_EQ(atom.index(), 0);
  EXPECT_EQ(atom.atomicNumber(), static_cast<unsigned char>(6));

  Avogadro::Core::Atom atom2 = molecule.addAtom(1);
  EXPECT_EQ(atom2.isValid(), true);
  EXPECT_EQ(molecule.atomCount(), static_cast<Index>(2));
  EXPECT_EQ(atom2.index(), 1);
  EXPECT_EQ(atom2.atomicNumber(), static_cast<unsigned char>(1));
}
示例#12
0
TEST(RWMoleculeTest, MoleculeToRWMolecule)
{
  Molecule mol;
  typedef Molecule::AtomType Atom;
  typedef Molecule::BondType Bond;
  Atom a0 = mol.addAtom(1);
  Atom a1 = mol.addAtom(6);
  Atom a2 = mol.addAtom(9);
  Bond b0 = mol.addBond(a0, a2);
  a1.setPosition3d(Vector3(0, 6, 9));
  b0.setOrder(3);

  RWMolecule rwmol(mol, 0);
  EXPECT_EQ(rwmol.atomCount(), mol.atomCount());
  EXPECT_EQ(rwmol.bondCount(), mol.bondCount());
  EXPECT_EQ(rwmol.atom(2).atomicNumber(), mol.atom(2).atomicNumber());
  EXPECT_EQ(rwmol.bond(0).order(), mol.bond(0).order());
}
示例#13
0
TEST_F(MoleculeTest, perceiveBondsSimple)
{
  Molecule molecule;
  Atom o1 = molecule.addAtom(8);
  Atom h2 = molecule.addAtom(1);
  Atom h3 = molecule.addAtom(1);

  o1.setPosition3d(Vector3(0, 0, 0));
  h2.setPosition3d(Vector3(0.6, -0.5, 0));
  h3.setPosition3d(Vector3(-0.6, -0.5, 0));
  EXPECT_EQ(molecule.bondCount(), 0);

  molecule.perceiveBondsSimple();
  EXPECT_EQ(molecule.bondCount(), 2);
  EXPECT_TRUE(molecule.bond(o1, h2).isValid());
  EXPECT_TRUE(molecule.bond(o1, h3).isValid());
  EXPECT_FALSE(molecule.bond(h2, h3).isValid());
}
示例#14
0
TEST_F(MoleculeTest, findBond)
{
  Molecule molecule;
  Atom a1 = molecule.addAtom(5);
  Atom a2 = molecule.addAtom(6);
  Bond b = molecule.addBond(a1, a2, 1);

  EXPECT_EQ(molecule.bond(a1, a2).index(), b.index());
  EXPECT_EQ(molecule.bond(a2, a1).index(), b.index());

  Array<Bond> bonds = molecule.bonds(a1);
  EXPECT_EQ(bonds.size(), 1);

  Atom a3 = molecule.addAtom(7);
  molecule.addBond(a1, a3, 1);
  EXPECT_EQ(molecule.bonds(a1).size(), 2);
  EXPECT_EQ(molecule.bonds(a3).size(), 1);
}
示例#15
0
TEST_F(MoleculeTest, addBond)
{
  Molecule molecule;
  EXPECT_EQ(molecule.bondCount(), static_cast<Index>(0));

  Atom a = molecule.addAtom(1);
  Atom b = molecule.addAtom(1);
  Bond bondAB = molecule.addBond(a, b);
  EXPECT_TRUE(bondAB.isValid());
  EXPECT_EQ(bondAB.molecule(), &molecule);
  EXPECT_EQ(molecule.bondCount(), static_cast<Index>(1));
  EXPECT_EQ(bondAB.index(), static_cast<Index>(0));
  EXPECT_EQ(bondAB.atom1().index(), a.index());
  EXPECT_EQ(bondAB.atom2().index(), b.index());
  EXPECT_EQ(bondAB.order(), static_cast<unsigned char>(1));

  Atom c = molecule.addAtom(1);
  Bond bondBC = molecule.addBond(b, c, 2);
  EXPECT_TRUE(bondBC.isValid());
  EXPECT_EQ(molecule.bondCount(), static_cast<Index>(2));
  EXPECT_EQ(bondBC.index(), static_cast<Index>(1));
  EXPECT_EQ(bondBC.order(), static_cast<unsigned char>(2));

  // try to lookup nonexistant bond
  Bond bond = molecule.bond(a, c);
  EXPECT_FALSE(bond.isValid());

  // try to lookup bond between a and b
  bond = molecule.bond(a, b);
  EXPECT_TRUE(bond.isValid());
  EXPECT_EQ(bond.molecule(), &molecule);
  EXPECT_EQ(bond.atom1().index(), a.index());
  EXPECT_EQ(bond.atom2().index(), b.index());

  // try to lookup bond between b and c by index
  bond = molecule.bond(1);
  EXPECT_TRUE(bond.isValid());
  EXPECT_EQ(bond.molecule(), &molecule);
  EXPECT_EQ(bond.atom1().index(), b.index());
  EXPECT_EQ(bond.atom2().index(), c.index());
}
TEST(RingPerceiverTest, benzene)
{
  Molecule molecule;
  molecule.addAtom(6);
  molecule.addAtom(6);
  molecule.addAtom(6);
  molecule.addAtom(6);
  molecule.addAtom(6);
  molecule.addAtom(6);
  molecule.addBond(molecule.atom(0), molecule.atom(1), 1);
  molecule.addBond(molecule.atom(1), molecule.atom(2), 2);
  molecule.addBond(molecule.atom(2), molecule.atom(3), 1);
  molecule.addBond(molecule.atom(3), molecule.atom(4), 2);
  molecule.addBond(molecule.atom(4), molecule.atom(5), 1);
  molecule.addBond(molecule.atom(5), molecule.atom(0), 2);

  RingPerceiver perceiver(&molecule);
  std::vector<std::vector<size_t>> rings = perceiver.rings();
  EXPECT_EQ(rings.size(), static_cast<size_t>(1));
  EXPECT_EQ(rings[0].size(), static_cast<size_t>(6));
}
示例#17
0
bool GaussianOutputFile::readInputOrientationGeometries(vector<Molecule>& molecules)
{
    Atom atom;
    Molecule mol;
    string line;
    bool ok;

    ok = false;

    int i;

    i = 0;
    while(isStringInFile("Input orientation"))
    {
//		if(isStringInFile("Input orientation"))
//		{
        /*
         * Jumping four lines that contains the orientation header
         *
         */
        for(size_t i = 0; i < 4; i++)
            line = readLine();

        /*
         * Reading the atoms
         *
         */
        line = readLine();
        while(line.find("-------") == string::npos)
        {
            atom.clear();
            atom.setName(line.substr(13,16));
            atom.setX(atof(line.substr(33,46).c_str()));
            atom.setY(atof(line.substr(47,59).c_str()));
            atom.setZ(atof(line.substr(60,70).c_str()));
            mol.addAtom(atom);
            line = readLine();
        }

        molecules.push_back(mol);

        mol.clear();

        ok = true;
//		}
    }

    if(ok)
        return true;
    else
        return false;
}
void HydrogenTools::adjustHydrogens(Molecule &molecule, Adjustment adjustment)
{
  // This vector stores indices of hydrogens that need to be removed. Additions
  // are made first, followed by removals to keep indexing sane.
  std::vector<size_t> badHIndices;

  // Temporary container for calls to generateNewHydrogenPositions.
  std::vector<Vector3> newHPos;

  // Convert the adjustment option to a couple of booleans
  bool doAdd(adjustment == Add || adjustment == AddAndRemove);
  bool doRemove(adjustment == Remove || adjustment == AddAndRemove);

  // Limit to only the original atoms:
  const size_t numAtoms = molecule.atomCount();

  // Iterate through all atoms in the molecule, adding hydrogens as needed
  // and building up a list of hydrogens that should be removed.
  for (size_t atomIndex = 0; atomIndex < numAtoms; ++atomIndex) {
    const Atom atom(molecule.atom(atomIndex));
    int hDiff = valencyAdjustment(atom);
    // Add hydrogens:
    if (doAdd && hDiff > 0) {
      newHPos.clear();
      generateNewHydrogenPositions(atom, hDiff, newHPos);
      for (std::vector<Vector3>::const_iterator it = newHPos.begin(),
           itEnd = newHPos.end(); it != itEnd; ++it) {
        Atom newH(molecule.addAtom(1));
        newH.setPosition3d(*it);
        molecule.addBond(atom, newH, 1);
      }
    }
    // Add bad hydrogens to our list of hydrogens to remove:
    else if (doRemove && hDiff < 0) {
      extraHydrogenIndices(atom, -hDiff, badHIndices);
    }
  }

  // Remove dead hydrogens now. Remove them in reverse-index order to keep
  // indexing sane.
  if (doRemove && !badHIndices.empty()) {
    std::sort(badHIndices.begin(), badHIndices.end());
    std::vector<size_t>::iterator newEnd(std::unique(badHIndices.begin(),
                                                     badHIndices.end()));
    badHIndices.resize(std::distance(badHIndices.begin(), newEnd));
    for (std::vector<size_t>::const_reverse_iterator it = badHIndices.rbegin(),
         itEnd = badHIndices.rend(); it != itEnd; ++it) {
      molecule.removeAtom(*it);
    }
  }
}
示例#19
0
void MoleculeTest::removeAtom()
{
  // Should now to two atoms in the Molecule with ids 0 and 1.
  Atom *a = m_molecule->atom(1);
  m_molecule->removeAtom(a);
  QVERIFY(m_molecule->numAtoms() == 1);
  m_molecule->removeAtom(0ul);
  QVERIFY(m_molecule->numAtoms() == 0);

  // Check behavior of removing an atom that is owned by a different
  // molecule. Should not crash.
  Molecule mol;
  a = mol.addAtom();
  m_molecule->removeAtom(a);
}
示例#20
0
bool AbbreviationExpander::tryCarbonChain (TokenChain &tokens, size_t &offset, Molecule &m, AttPoint &attach_to)
{
   if (attach_to.order != 1)
      return false;

   Token &cur = tokens[offset];
   if (cur.type != Token::Element)
      return false;

   if (cur.multiplier == 1 || cur.index != ELEM_C || offset + 1 == tokens.size())
      return false;

   Token &next = tokens[offset + 1];
   // Check CnH(2n+1), CnH2n pattern
   if (next.multiplier > 1 && next.index == ELEM_H)
   {
      bool tail;
      if (next.multiplier == cur.multiplier * 2)
      {
         // Intermediate carbon chain
         tail = false;
      }
      else if (next.multiplier == cur.multiplier * 2 + 1)
      {
         // Terminator carbon chain
         tail = true;
      }
      else
         return false;

      for (int i = 0; i < cur.multiplier; i++)
      {
         int idx = m.addAtom(ELEM_C);
         attachBond(m, attach_to, idx);

         attach_to = AttPoint(idx, 1);
      }
      if (tail)
         attach_to = AttPoint(-1, 0);
      offset += 2;
      return true;
   }
   return false;
}
示例#21
0
TEST(HydrogenToolsTest, adjustHydrogens_adjustments)
{
  for (int i = 0; i < 3; ++i) {
    HydrogenTools::Adjustment adjustment;
    std::string expectedFormula;
    switch (i) {
    case 0:
      adjustment = HydrogenTools::Add;
      expectedFormula = "C2H14";
      break;
    case 1:
      adjustment = HydrogenTools::Remove;
      expectedFormula = "C2H5";
      break;
    case 2:
      adjustment = HydrogenTools::AddAndRemove;
      expectedFormula = "C2H8";
      break;
    }

    Molecule mol;
    Atom C1 = mol.addAtom(6); // Overbond this atom
    mol.addBond(C1, mol.addAtom(1));
    mol.addBond(C1, mol.addAtom(1));
    mol.addBond(C1, mol.addAtom(1));
    mol.addBond(C1, mol.addAtom(1));
    mol.addBond(C1, mol.addAtom(1));
    mol.addBond(C1, mol.addAtom(1));
    mol.addBond(C1, mol.addAtom(1));
    mol.addBond(C1, mol.addAtom(1));
    mol.addBond(C1, mol.addAtom(1));
    mol.addBond(C1, mol.addAtom(1));
    Atom C2 = mol.addAtom(6); // Underbond this atom
    mol.addBond(C2, mol.addAtom(1));

    EXPECT_EQ(std::string("C2H11"), mol.formula());
    HydrogenTools::adjustHydrogens(mol, adjustment);
    EXPECT_EQ(expectedFormula, mol.formula());
  }
}
示例#22
0
bool GaussianOutputFile::readFirstInputOrientationGeometry(Molecule& mol)
{
    Atom atom;
    string line;

    if(!rewind())
        return false;

    if(isStringInFile("Input orientation"))
    {
        /*
         * Jumping four lines that contains the orientation header
         *
         */
        for(size_t i = 0; i < 4; i++)
            line = readLine();

        /*
         * Reading the atoms
         *
         */
        line = readLine();
        while(line.find("-------") == string::npos)
        {
            atom.clear();
            atom.setName(line.substr(13,16));
            atom.setX(atof(line.substr(33,46).c_str()));
            atom.setY(atof(line.substr(47,59).c_str()));
            atom.setZ(atof(line.substr(60,70).c_str()));
            mol.addAtom(atom);
            line = readLine();


        }

    }
    else
        return false;

    return true;
}
示例#23
0
void MoleculeLayoutGraphSmart::saveDebug ()
{
   int i;
   Molecule mol;
   QS_DEF(Array<int>, mapping);

   mapping.clear_resize(vertexEnd());

   for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i))
   {
      if (getVertexType(i) == ELEMENT_NOT_DRAWN)
         continue;

      mapping[i] = mol.addAtom(ELEM_C);
      mol.setAtomXyz(mapping[i], getPos(i).x, getPos(i).y, 0);
   }

   for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i))
   {
      if (getEdgeType(i) == ELEMENT_NOT_DRAWN)
         continue;

      const Edge &edge = getEdge(i);

      mol.addBond(mapping[edge.beg], mapping[edge.end], BOND_SINGLE);
   }

   static int id = 0;
   char out_name[100];

   sprintf_s(out_name, "D:\\mf\\draw\\trace_my\\%03d.mol", id);

   FileOutput fo(out_name);
   MolfileSaver ms(fo);

   ms.saveMolecule(mol);

   id++;
}
示例#24
0
void CmfLoader::loadMolecule (Molecule &mol)
{
   int code;

   mol.clear();

   QS_DEF(Array<int>, cycle_numbers);
   QS_DEF(Array<int>, atom_stack);

   _atoms.clear();
   _bonds.clear();
   _pseudo_labels.clear();
   _attachments.clear();
   cycle_numbers.clear();
   atom_stack.clear();

   bool first_atom = true;

   if (!_getNextCode(code))
      return;

   bool has_ext_part = false;

   /* Main loop */
   do 
   {
      _BondDesc *bond = 0;

      if (code > CMF_ALPHABET_SIZE)
         throw Error("unexpected code");

      if (code == CMF_TERMINATOR)
         break;

      if (code == CMF_EXT)
      {
         has_ext_part = true;
         // Ext part has to be read till CMF_TERMINATOR
         break;
      }

      if (!first_atom)
      {
         int number;

         while (_readCycleNumber(code, number))
         {
            while (cycle_numbers.size() <= number)
               cycle_numbers.push(-1);

            if (cycle_numbers[number] >= 0)
               throw Error("cycle #%d already in use", number);
            
            cycle_numbers[number] = atom_stack.top();

            if (!_getNextCode(code))
               break;
         }
      }

      if (code == CMF_SEPARATOR)
      {
         atom_stack.pop();
         first_atom = true;

         if (!_getNextCode(code))
            break;

         continue;
      }

      if (code == CMF_OPEN_BRACKET)
      {
         atom_stack.push(atom_stack.top());

         if (!_getNextCode(code))
            break;

         continue;
      }

      if (code == CMF_CLOSE_BRACKET)
      {
         atom_stack.pop();

         if (!_getNextCode(code))
            break;

         continue;
      }

      if (!first_atom)
      {
         bond = &_bonds.push();
         bond->beg = atom_stack.top();
      }

      if (bond != 0)
      {
         _readBond(code, *bond);

         int number;

         if (_readCycleNumber(code, number))
         {
            if (cycle_numbers[number] < 0)
               throw Error("bad cycle number after bond symbol");

            bond->end = cycle_numbers[number];
            cycle_numbers[number] = -1;

            if (!_getNextCode(code))
               break;

            continue;
         }
      }

      _AtomDesc &atom = _atoms.push();

      if (!first_atom)
         atom_stack.pop();

      atom_stack.push(_atoms.size() - 1);

      first_atom = false;

      if (bond != 0)
         bond->end = _atoms.size() - 1;

      memset(&atom, 0, sizeof(_AtomDesc));
      atom.hydrogens = -1;
      atom.valence = -1;
      atom.pseudo_atom_idx = -1;
      atom.rsite = false;

      if (code > 0 && (code < ELEM_MAX || code == CMF_PSEUDOATOM || code == CMF_RSITE || code == CMF_RSITE_EXT))
      {
         if (!_readAtom(code, atom, _atoms.size() - 1))
            break;
         continue;
      }

      if (!_getNextCode(code))
         break;

   } while (true); 

   // if have internal decoder, finish it
/*   if (_decoder_obj.get() != 0)
      _decoder_obj->finish(); */

   /* Reading finished, filling molecule */

   int i;

   for (i = 0; i < _atoms.size(); i++)
   {
      mol.addAtom(_atoms[i].label);

      if (_atoms[i].pseudo_atom_idx >= 0)
         mol.setPseudoAtom(i, _pseudo_labels.at(_atoms[i].pseudo_atom_idx));

      if (_atoms[i].rsite_bits > 0)
         mol.setRSiteBits(i, _atoms[i].rsite_bits);

      mol.setAtomCharge(i, _atoms[i].charge);
      mol.setAtomIsotope(i, _atoms[i].isotope);
      if (_atoms[i].hydrogens >= 0)
         mol.setImplicitH(i, _atoms[i].hydrogens);
      mol.setAtomRadical(i, _atoms[i].radical);

      if (_atoms[i].highlighted)
         mol.highlightAtom(i);
   }

   for (i = 0; i < _bonds.size(); i++)
   {
      int type = _bonds[i].type;
      int beg = _bonds[i].beg;
      int end = _bonds[i].end;
      int tmp;

      if (_bonds[i].swap)
         __swap(beg, end, tmp);

      int idx = mol.addBond_Silent(beg, end, type);
      
      if (_bonds[i].in_ring)
         mol.setEdgeTopology(idx, TOPOLOGY_RING);
      else
         mol.setEdgeTopology(idx, TOPOLOGY_CHAIN);

      if (_bonds[i].direction != 0)
         mol.setBondDirection(idx, _bonds[i].direction);

      if (_bonds[i].highlighted)
         mol.highlightBond(idx);
   }

   for (i = 0; i < _attachments.size(); i++)
      mol.addAttachmentPoint(_attachments[i].index, _attachments[i].atom);

   mol.validateEdgeTopologies();

   if (has_ext_part)
      _readExtSection(mol);

   if (atom_flags != 0)
   {
      atom_flags->clear();

      for (i = 0; i < _atoms.size(); i++)
         atom_flags->push(_atoms[i].flags);
   }

   if (bond_flags != 0)
   {
      bond_flags->clear();

      for (i = 0; i < _bonds.size(); i++)
         bond_flags->push(_bonds[i].flags);
   }

   if (!skip_cistrans)
   {
      for (i = 0; i < _bonds.size(); i++)
      {
         if (_bonds[i].cis_trans != 0)
         {
            int parity = _bonds[i].cis_trans;
            if (parity > 0)
               mol.cis_trans.setParity(i, _bonds[i].cis_trans);
            else
               mol.cis_trans.ignore(i);
            mol.cis_trans.restoreSubstituents(i);
         }
      }
   }

   if (!skip_valence)
   {
      for (i = 0; i < _atoms.size(); i++)
      {
         if (_atoms[i].valence >= 0)
            mol.setValence(i, _atoms[i].valence);
      }
   }

   if (!skip_stereocenters)
   {
      for (i = 0; i < _atoms.size(); i++)
      {
         if (_atoms[i].stereo_type != 0)
            mol.stereocenters.add(i, _atoms[i].stereo_type, _atoms[i].stereo_group, _atoms[i].stereo_invert_pyramid);
      }
   }

   for (i = 0; i < _atoms.size(); i++)
   {
      if (_atoms[i].allene_stereo_parity != 0)
      {
         int left, right, subst[4];
         bool pure_h[4];
         int parity = _atoms[i].allene_stereo_parity;
         int tmp;

         if (!MoleculeAlleneStereo::possibleCenter(mol, i, left, right, subst, pure_h))
            throw Error("invalid molecule allene stereo marker");

         if (subst[1] != -1 && subst[1] < subst[0])
            __swap(subst[1], subst[0], tmp);

         if (subst[3] != -1 && subst[3] < subst[2])
            __swap(subst[3], subst[2], tmp);

         if (pure_h[0])
         {
            __swap(subst[1], subst[0], tmp);
            parity = 3 - parity;
         }
         if (pure_h[2])
         {
            __swap(subst[2], subst[3], tmp);
            parity = 3 - parity;
         }

         mol.allene_stereo.add(i, left, right, subst, parity);
      }
   }

   // for loadXyz()
   _mol = &mol;

   // Check if atom mapping was used
   if (has_mapping)
   {
      // Compute inv_atom_mapping_to_restore
      inv_atom_mapping_to_restore.clear_resize(atom_mapping_to_restore.size());
      for (int i = 0; i < atom_mapping_to_restore.size(); i++)
         inv_atom_mapping_to_restore[atom_mapping_to_restore[i]] = i;

      // Compute inv_bond_mapping_to_restore
      inv_bond_mapping_to_restore.clear_resize(bond_mapping_to_restore.size());
      for (int i = 0; i < bond_mapping_to_restore.size(); i++)
         inv_bond_mapping_to_restore[bond_mapping_to_restore[i]] = i;

      QS_DEF(Molecule, tmp);
      tmp.makeEdgeSubmolecule(mol, atom_mapping_to_restore, bond_mapping_to_restore, NULL);
      mol.clone(tmp, NULL, NULL);

   }
}
示例#25
0
void FileParser::readQchem31()
{
    std::string tempString;
    QRegExp rx("", Qt::CaseInsensitive, QRegExp::RegExp2);

    while (1) {
        Molecule *molecule = new Molecule();

        // GOAL TO MATCH -
        // Optimization Cycle:   1
        //
        //                     Coordinates (Angstroms)
        //   ATOM              X           Y           Z
        //  1  H           6.264255   -1.214463    0.001562
        //  2  N           0.992577    0.405079   -0.000713
        //  3  C           3.640581    0.699759   -0.000285
        //  4  H           5.692879    1.292403   -0.000233
        //  5  N           1.103169   -1.927005    0.000687
        //  6  C          -3.986741   -0.820146    0.000147
        //  7  C          -4.603474    0.385503    0.001374
        //  8  N           4.158387   -1.540250    0.001279
        //  9  C           5.256835   -0.819403    0.001066
        // 10  H          -5.684216    0.487120    0.002358
        // 11  N           2.932990    1.843103   -0.001138
        // 12  H          -4.383326    2.467645    0.002362
        // 13  H           0.959209    2.465806   -0.001902
        // 14  H           1.658401   -2.773307    0.001857
        // 15  C           1.728674   -0.733977    0.000168
        // 16  N           5.011372    0.541687    0.000124
        // 17  H          -4.452049   -2.729414    0.878462
        // 18  C           3.136252   -0.602051    0.000394
        // 19  H          -0.845397    0.417252   -0.001289
        // 20  H          -4.453040   -2.728702   -0.879157
        // 21  H          -5.802705   -1.976283    0.000726
        // 22  C          -4.719039   -2.131115    0.000042
        // 23  C           1.619390    1.601695   -0.001312
        // 24  H           0.079495   -1.982939    0.000872
        // 25  N          -3.902148    1.574898    0.001480
        // 26  C          -2.511246    1.648434    0.000286
        // 27  O          -1.910441    2.717732    0.000430
        // 28  N          -1.895228    0.410754   -0.001007
        // 29  C          -2.522920   -0.833703   -0.001123
        // 30  O          -1.848461   -1.874351   -0.002325

        getline(infile, tempString);
        while (tempString.find("Optimization Cycle:") == string::npos && infile.eof() == false) {
            getline(infile, tempString);
        }
        if (infile.eof()) {
            break;
        }
#ifdef QT_DEBUG
        std::cout << "readQchem31: 'Optimization Cycle:' found.\n";
#endif

        getline(infile, tempString);
        getline(infile, tempString);
        getline(infile, tempString);

        // qchem31 is reported in Angstroms
        myUnits = Angstrom;

        // Read in atom information
        rx.setPattern("(?:\\s*)(?:\\d+)(?:\\s+)(\\w+)(?:\\s+)(-?\\d+\\.\\d+)(?:\\s+)(-?\\d+\\.\\d+)"
                      "(?:\\s+)(-?\\d+\\.\\d+)");
        while (1) {
            getline(infile, tempString);
            if (rx.exactMatch(tempString.c_str()) == true) {
                AtomEntry *atom = new AtomEntry;
                atom->Label = rx.cap(1);
                atom->x = rx.cap(2).toDouble();
                atom->y = rx.cap(3).toDouble();
                atom->z = rx.cap(4).toDouble();
                molecule->addAtom(atom);
#ifdef QT_DEBUG
                std::cout << std::setw(5) << atom->Label.toStdString() << " " << std::setw(16)
                          << std::setprecision(10) << atom->x << " " << std::setw(16)
                          << std::setprecision(10) << atom->y << " " << std::setw(16)
                          << std::setprecision(10) << atom->z << std::endl;
#endif
            } else {
                myMoleculeList.push_back(molecule);
                break;
            }
        }
    }
}
TEST(InputGeneratorWidgetTest, exercise)
{
  // Fake a QApplication -- needed to instantiate widgets.
  int argc = 1;
  char argName[] = "FakeApp.exe";
  char *argv[2] = {argName, NULL};
  QApplication app(argc, argv);
  Q_UNUSED(app);

  // Setup the widget
  InputGeneratorWidget widget;
  QString scriptFilePath(AVOGADRO_DATA
                         "/tests/avogadro/scripts/inputgeneratortest.py");
  widget.setInputGeneratorScript(scriptFilePath);
  Molecule mol;
  mol.addAtom(6).setPosition3d(Avogadro::Vector3(1, 1, 1));
  mol.addAtom(1).setPosition3d(Avogadro::Vector3(2, 3, 4));
  mol.addAtom(8).setPosition3d(Avogadro::Vector3(-2, 3, -4));
  widget.setMolecule(&mol);

  // Check that the generator is configured properly.
  EXPECT_EQ(widget.inputGenerator().displayName().toStdString(),
            std::string("Input Generator Test"));

  // Verify that appropriate widgets are produced for each parameter type:
  EXPECT_TRUE(widget.findChild<QComboBox*>("Test StringList") != NULL);
  EXPECT_TRUE(widget.findChild<QLineEdit*>("Test String") != NULL);
  EXPECT_TRUE(widget.findChild<QSpinBox*>("Test Integer") != NULL);
  EXPECT_TRUE(widget.findChild<QCheckBox*>("Test Boolean") != NULL);
  EXPECT_TRUE(widget.findChild<FileBrowseWidget*>("Test FilePath") != NULL);

  // Set a test filepath
  FileBrowseWidget *testFilePathWidget(
        widget.findChild<FileBrowseWidget*>("Test FilePath"));
  QString testFilePath(AVOGADRO_DATA "/data/ethane.cml");
  testFilePathWidget->setFileName(testFilePath);

  // Show the widget so that events are processed
  widget.show();

  // Clear out the event queue so that the text edits are updated:
  flushEvents();

  // Check the contents of the filepath file:
  QTextEdit *filePathEdit = widget.findChild<QTextEdit*>("job.testFilePath");
  QFile testFile(testFilePath);
  EXPECT_TRUE(testFile.open(QFile::ReadOnly | QFile::Text));
  QByteArray refData(testFile.readAll());
  EXPECT_EQ(std::string(refData.constData()),
            filePathEdit->document()->toPlainText().toStdString());

  // Check the coords:
  QTextEdit *coordsEdit = widget.findChild<QTextEdit*>("job.coords");
  QString coords(coordsEdit->document()->toPlainText());
  EXPECT_TRUE(coords.contains(
                "C      1.000000 0    1.000000 1    1.000000 1 Carbon"));
  EXPECT_TRUE(coords.contains(
                "H      2.000000 0    3.000000 1    4.000000 1 Hydrogen"));
  EXPECT_TRUE(coords.contains(
                "O     -2.000000 0    3.000000 1   -4.000000 1 Oxygen"));

  // Test the default reset -- trigger a reset, then verify that testFilePath
  // is cleared (we set it earlier)
  QPushButton *defaultsButton(widget.findChild<QPushButton*>("defaultsButton"));
  defaultsButton->click();
  flushEvents();
  EXPECT_TRUE(testFilePathWidget->fileName().isEmpty());
  EXPECT_EQ(filePathEdit->document()->toPlainText().toStdString(),
            std::string("Reference file '' does not exist."));

  // Test the autogenerated title:
  QLineEdit *titleEdit = widget.findChild<QLineEdit*>("Title");
  EXPECT_EQ(titleEdit->placeholderText().toStdString(),
            std::string("CHO | Equilibrium Geometry | B3LYP/6-31G(d)"));
}
示例#27
0
TEST(UnitCellTest, fractionalCoordinates)
{
  Molecule mol = createCrystal(static_cast<Real>(3.0),
                               static_cast<Real>(4.0),
                               static_cast<Real>(5.0),
                               static_cast<Real>(90.0),
                               static_cast<Real>(120.0),
                               static_cast<Real>(77.0));
  mol.addAtom(1).setPosition3d(Vector3(static_cast<Real>(0),
                                       static_cast<Real>(0),
                                       static_cast<Real>(0)));
  mol.addAtom(1).setPosition3d(Vector3(static_cast<Real>(0.7),
                                       static_cast<Real>(2.23733),
                                       static_cast<Real>(2.14574)));
  mol.addAtom(1).setPosition3d(Vector3(static_cast<Real>(2.07490),
                                       static_cast<Real>(2.09303),
                                       static_cast<Real>(1.07287)));
  mol.addAtom(1).setPosition3d(Vector3(static_cast<Real>(3),
                                       static_cast<Real>(0),
                                       static_cast<Real>(0)));
  mol.addAtom(1).setPosition3d(Vector3(static_cast<Real>(0.89980),
                                       static_cast<Real>(3.89748),
                                       static_cast<Real>(0)));
  mol.addAtom(1).setPosition3d(Vector3(static_cast<Real>(-2.5),
                                       static_cast<Real>(0.57717),
                                       static_cast<Real>(4.29149)));
  Array<Vector3> ccoords_ref = mol.atomPositions3d();

  Array<Vector3> fcoords;
  EXPECT_TRUE(CrystalTools::fractionalCoordinates(mol, fcoords));
  EXPECT_EQ(mol.atomCount(), fcoords.size());
  EXPECT_TRUE(std::fabs(fcoords[0][0] - static_cast<Real>(0)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[0][1] - static_cast<Real>(0)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[0][2] - static_cast<Real>(0)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[1][0] - static_cast<Real>(0.5)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[1][1] - static_cast<Real>(0.5)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[1][2] - static_cast<Real>(0.5)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[2][0] - static_cast<Real>(0.75)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[2][1] - static_cast<Real>(0.5)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[2][2] - static_cast<Real>(0.25)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[3][0] - static_cast<Real>(1)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[3][1] - static_cast<Real>(0)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[3][2] - static_cast<Real>(0)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[4][0] - static_cast<Real>(0)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[4][1] - static_cast<Real>(1)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[4][2] - static_cast<Real>(0)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[5][0] - static_cast<Real>(0)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[5][1] - static_cast<Real>(0)) < 1e-4);
  EXPECT_TRUE(std::fabs(fcoords[5][2] - static_cast<Real>(1)) < 1e-4);

  mol.atomPositions3d().clear();
  EXPECT_TRUE(CrystalTools::setFractionalCoordinates(mol, fcoords));
  Array<Vector3> ccoords = mol.atomPositions3d();
  for (int i = 0; i < 6; ++i) {
    for (int j = 0; j < 3; ++j) {
      EXPECT_FLOAT_EQ(static_cast<float>(ccoords_ref[i][j]),
                      static_cast<float>(ccoords[i][j]))
          << " (i=" << i << "j=" << j << ")";
    }
  }
}
示例#28
0
bool AbbreviationExpander::tryExpandToken (TokenChain &tokens, size_t &offset, Molecule &m, AttPoint &attach_to)
{
   Token &cur = tokens[offset];

   if (cur.multiplier != 1)
      return false;

   Array<int> connection_points;
   if (cur.type == Token::Element)
   {
      if (cur.index == ELEM_H)
      {
         offset++;
         attach_to = AttPoint(-1, 0);
         return true;
      }
      int added = m.addAtom(cur.index);

      // Get the number of bonds to connect
      int valence, hyd;
      int conn = attach_to.order;
      if (offset + 1 < tokens.size())
      {
         Token &next = tokens[offset + 1];
         conn += next.multiplier;
      }

      if (!Element::calcValence(cur.index, 0, 0, conn, valence, hyd, false))
      {
         // Ignore next atom
         // Appear in the OH3C case when H3 is belong to C
         conn = attach_to.order;
         if (!Element::calcValence(cur.index, 0, 0, conn, valence, hyd, false))
            return false;
      }

      for (int i = 0; i < hyd + conn; i++)
         connection_points.push(added);
   }
   else if (cur.type == Token::Pattern)
   {
      // Add pattern
      BufferScanner scanner(abbreviations[cur.index]->expansion.c_str());
      SmilesLoader loader(scanner);

      Molecule abbr;
      loader.loadMolecule(abbr);

      Array<int> mapping;
      Array<int> rsites;
      m.mergeWithMolecule(abbr, &mapping);
      for (int v = abbr.vertexBegin(); v != abbr.vertexEnd(); v = abbr.vertexNext(v))
      {
         int mapped = mapping[v];
         if (m.isRSite(mapped))
         {
            dword bits = m.getRSiteBits(mapped);
            int id1 = bitGetOneHOIndex(bits);
            int id2 = bitGetOneHOIndex(bits);
            if (id1 != id2)
               throw Exception("Invalid abbreviations specification: %s", 
                  abbreviations[cur.index]->expansion.c_str());
            if (id1 != 0)
               id1--; // R == R1

            const Vertex &vertex = m.getVertex(mapped);
            int nei = vertex.neiBegin();

            connection_points.expandFill(id1 + 1, -1);
            connection_points[id1] = vertex.neiVertex(nei); // Point connected to the RSite

            rsites.push(mapped);
         }
      }
      m.removeAtoms(rsites);
   }
   else
      return false;

   bool rollback = false;
   int atom_bound = m.vertexCount();
   size_t offset2 = offset + 1;

   attachBond(m, attach_to, connection_points[0]);
   int i = attach_to.order;
   while (i < connection_points.size() - 1 && !rollback)
   {
      if (offset2 >= tokens.size())
      {
         // If we are at the end then there can be an implicit double bond
         // Example: -CH2CH=
         // When we read C H there are no more tokens
         break;
      }

      Token &next = tokens[offset2];
      for (int j = 0; j < next.multiplier; j++)
      {
         if (i >= connection_points.size())
         {
            rollback = true;
            break;
         }

         if (next.type == Token::Branch)
         {
            AttPoint point(connection_points[i], 1);

            if (!expandParsedTokensWithRev(next.branch, m, point) || point.index != -1)
            {
               rollback = true;
               break;
            }
         }
         else
         {
            TokenChain chain;
            chain.push_back(next);
            chain[0].multiplier = 1;
            size_t local_offset = 0;
            AttPoint point(connection_points[i], 1);
            if (!tryExpandToken(chain, local_offset, m, point) || point.index != -1)
            {
               rollback = true;
               break;
            }
         }
         i++;
      }
      offset2++;
   }

   if (i > connection_points.size())
      rollback = true;
   if (!rollback)
   {
      if (i == connection_points.size())
      {
         // This is terminal
         attach_to = AttPoint(-1, 0);
      }
      else if (i == connection_points.size() - 1)
         attach_to = AttPoint(connection_points[i], 1); // Last attachment point
      else
      {
         // Number of tokens are incomlete means that there are double bonds after
         attach_to = AttPoint(connection_points[i], connection_points.size() - i);
      }
   }

   if (rollback)
   {
      // Rollback
      Array<int> new_atoms;
      for (int v = m.vertexBegin(); v != m.vertexEnd(); v = m.vertexNext(v))
         if (v >= atom_bound)
            new_atoms.push(v);
      m.removeAtoms(new_atoms);
      return false;
   }
   offset = offset2;
   return true;
}
示例#29
0
TEST(HydrogenToolsTest, removeAllHydrogens)
{
  Molecule mol;
  mol.addAtom(1);
  HydrogenTools::removeAllHydrogens(mol);
  EXPECT_EQ(mol.atomCount(), 0);

  Atom C1 = mol.addAtom(6);
  Atom C2 = mol.addAtom(6);
  Atom C3 = mol.addAtom(6);

  mol.addBond(C1, C2, 1);
  mol.addBond(C2, C3, 1);

  Atom H = mol.addAtom(1);
  mol.addBond(C1, H);
  H = mol.addAtom(1);
  mol.addBond(C1, H);
  H = mol.addAtom(1);
  mol.addBond(C1, H);

  H = mol.addAtom(1);
  mol.addBond(C2, H);
  H = mol.addAtom(1);
  mol.addBond(C2, H);

  H = mol.addAtom(1);
  mol.addBond(C3, H);
  H = mol.addAtom(1);
  mol.addBond(C3, H);
  H = mol.addAtom(1);
  mol.addBond(C3, H);

  HydrogenTools::removeAllHydrogens(mol);
  EXPECT_EQ(std::string("C3"), mol.formula());
}
示例#30
0
void IndigoInchi::parseInchiOutput (const inchi_OutputStruct &inchi_output, Molecule &mol)
{
   mol.clear();

   Array<int> atom_indices;
   atom_indices.clear();
   
   // Add atoms
   for (AT_NUM i = 0; i < inchi_output.num_atoms; i ++)
   {
      const inchi_Atom &inchi_atom = inchi_output.atom[i];

      int idx = mol.addAtom(Element::fromString(inchi_atom.elname));
      atom_indices.push(idx);
   }

   // Add bonds
   for (AT_NUM i = 0; i < inchi_output.num_atoms; i ++)
   {
      const inchi_Atom &inchi_atom = inchi_output.atom[i];
      for (AT_NUM bi = 0; bi < inchi_atom.num_bonds; bi++)
      {
         AT_NUM nei = inchi_atom.neighbor[bi];
         if (i > nei)
            // Add bond only once
            continue;
         int bond_order = inchi_atom.bond_type[bi];
         if (bond_order == INCHI_BOND_TYPE_NONE)
            throw Molecule::Error("Indigo-InChI: NONE-typed bonds are not supported");
         if (bond_order >= INCHI_BOND_TYPE_ALTERN)
            throw Molecule::Error("Indigo-InChI: ALTERN-typed bonds are not supported");
         int bond = mol.addBond(atom_indices[i], atom_indices[nei], bond_order);
      }
   }

   // Add Hydrogen isotope atoms at the end to preserver 
   // the same atom ordering
   for (AT_NUM i = 0; i < inchi_output.num_atoms; i ++)
   {
      const inchi_Atom &inchi_atom = inchi_output.atom[i];

      int root_atom = atom_indices[i];
      for (int iso = 1; iso <= NUM_H_ISOTOPES; iso++)
      {
         int count = inchi_atom.num_iso_H[iso];
         while (count-- > 0)
         {
            int h = mol.addAtom(ELEM_H);
            mol.setAtomIsotope(h, iso);
            mol.addBond(root_atom, h, BOND_SINGLE);
         }
      }
   }

   // Set atom charges, radicals and etc.
   for (int i = 0; i < inchi_output.num_atoms; i++)
   {
      const inchi_Atom &inchi_atom = inchi_output.atom[i];

      int idx = atom_indices[i];
      mol.setAtomCharge(idx, inchi_atom.charge);
      if (inchi_atom.isotopic_mass)
         mol.setAtomIsotope(idx, inchi_atom.isotopic_mass);
      if (inchi_atom.radical)
         mol.setAtomRadical(idx, inchi_atom.radical);
      mol.setImplicitH(idx, inchi_atom.num_iso_H[0]);
   }

   neutralizeV5Nitrogen(mol);

   // Process stereoconfiguration
   for (int i = 0; i < inchi_output.num_stereo0D; i++)
   {
      inchi_Stereo0D &stereo0D = inchi_output.stereo0D[i];
      if (stereo0D.type == INCHI_StereoType_DoubleBond)
      {
         if (stereo0D.parity != INCHI_PARITY_ODD && stereo0D.parity != INCHI_PARITY_EVEN)
            continue;

         int bond = mol.findEdgeIndex(stereo0D.neighbor[1], stereo0D.neighbor[2]);

         bool valid = mol.cis_trans.registerBondAndSubstituents(bond);
         if (!valid)
            throw IndigoError("Indigo-InChI: Unsupported cis-trans configuration for "
               "bond %d (atoms %d-%d-%d-%d)", bond, stereo0D.neighbor[0], stereo0D.neighbor[1], 
               stereo0D.neighbor[2], stereo0D.neighbor[3]);

         int vb, ve;
         const Edge &edge = mol.getEdge(bond);
         if (edge.beg == stereo0D.neighbor[1])
         {
            vb = stereo0D.neighbor[0];
            ve = stereo0D.neighbor[3];
         }
         else if (edge.beg == stereo0D.neighbor[2])
         {
            vb = stereo0D.neighbor[3];
            ve = stereo0D.neighbor[0];
         }
         else
            throw IndigoError("Indigo-InChI: Internal error: cannot find cis-trans bond indices");

         const int *subst = mol.cis_trans.getSubstituents(bond);
         bool same_side;
         if (subst[0] == vb)
            same_side = (subst[2] == ve);
         else if (subst[1] == vb)
            same_side = (subst[3] == ve);
         else
            throw IndigoError("Indigo-InChI: Internal error: cannot find cis-trans bond indices (#2)");

         if (stereo0D.parity == INCHI_PARITY_EVEN)
            same_side = !same_side;

         mol.cis_trans.setParity(bond, same_side ? MoleculeCisTrans::CIS : MoleculeCisTrans::TRANS);
      }
      else if (stereo0D.type == INCHI_StereoType_Tetrahedral)
      {
         if (stereo0D.parity != INCHI_PARITY_ODD && stereo0D.parity != INCHI_PARITY_EVEN)
            continue;

         int pyramid[4];
         if (stereo0D.central_atom == stereo0D.neighbor[0])
         {
            pyramid[1] = stereo0D.neighbor[1];
            pyramid[0] = stereo0D.neighbor[2];
            pyramid[2] = stereo0D.neighbor[3];
            pyramid[3] = -1;
         }
         else
         {
            pyramid[0] = stereo0D.neighbor[0];
            pyramid[1] = stereo0D.neighbor[1];
            pyramid[2] = stereo0D.neighbor[2];
            pyramid[3] = stereo0D.neighbor[3];
         }
         if (stereo0D.parity == INCHI_PARITY_ODD)
            std::swap(pyramid[0], pyramid[1]);

         mol.stereocenters.add(stereo0D.central_atom, MoleculeStereocenters::ATOM_ABS, 0, pyramid);
      }
   }

}