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()); }
TEST(HydrogenToolsTest, adjustHydrogens_C2H7NO) { Molecule mol; Atom C1 = mol.addAtom(6); Atom C2 = mol.addAtom(6); Atom O1 = mol.addAtom(8); Atom N1 = mol.addAtom(7); mol.addBond(C1, C2, 1); mol.addBond(C2, O1, 1); mol.addBond(O1, N1, 1); HydrogenTools::adjustHydrogens(mol); EXPECT_EQ(11, mol.atomCount()); EXPECT_EQ(10, mol.bondCount()); EXPECT_EQ(std::string("C2H7NO"), mol.formula()); }
void AbbreviationExpander::attachBond (Molecule &m, AttPoint &attach_to, int idx) { if (attach_to.index != -1) m.addBond(attach_to.index, idx, attach_to.order); else input_index = idx; }
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); }
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); } }
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); } }
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(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, 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)); }
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)); }
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); } } }
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()); }
void MoleculeTest::removeBond() { // Should now to two atoms in the Molecule with ids 0 and 1. Bond *b = m_molecule->bond(1); m_molecule->removeBond(b); QVERIFY(m_molecule->numBonds() == 1); m_molecule->removeBond(0ul); QVERIFY(m_molecule->numBonds() == 0); // Check behavior of removing a bond that is owned by a different // molecule. Should not crash. Molecule mol; b = mol.addBond(0ul); m_molecule->removeBond(b); }
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()); }
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()); }
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++; }
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()); } }
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); } } }