// // Copyright (c) 2014-2024, Novartis Institutes for BioMedical Research and // other RDKit contributors // // @@ All Rights Reserved @@ // This file is part of the RDKit. // The contents are covered by the terms of the BSD license // which is included in the file license.txt, found at the root // of the RDKit source tree. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace RDGeom; using namespace RDKit; using namespace RDMIF; namespace RDMIF { class testfunctor { public: testfunctor() {} double operator()(const double &, const double &, const double &, double) const { return 1.0; } }; } // namespace RDMIF TEST_CASE("constructGrid") { std::string path = getenv("RDBASE"); path += "/Code/GraphMol/MolInteractionFields/test_data/"; // //Generate Molecule with 3D Coordinates and Partial Charges // RWMol mol=*SmilesToMol("Cl"); // MolOps::addHs(*mol); // DGeomHelpers::EmbedMolecule(*mol); // MolToMolFile(*mol, path + "HCl.mol"); auto fopts = v2::FileParsers::MolFileParserParams(); fopts.removeHs = false; auto mol = v2::FileParsers::MolFromMolFile(path + "HCl.mol", fopts); REQUIRE(mol); auto grd = *constructGrid(*mol, 0, 5.0, 0.5); Point3D bond = mol->getConformer().getAtomPos(1) - mol->getConformer().getAtomPos(0); CHECK(feq(grd.getSpacing(), 0.5)); CHECK(feq(grd.getNumX(), std::floor((fabs(bond.x) + 10.0) / 0.5 + 0.5))); CHECK(feq(grd.getNumY(), std::floor((fabs(bond.y) + 10.0) / 0.5 + 0.5))); CHECK(feq(grd.getNumZ(), std::floor((fabs(bond.z) + 10.0) / 0.5 + 0.5))); } TEST_CASE("CalculateDescriptors") { RealValueVect data(125, 0.0); Point3D o(0.0, 0.0, 0.0); UniformRealValueGrid3D grd(5.0, 5.0, 5.0, 1.0, &o, &data); calculateDescriptors(grd, testfunctor()); CHECK(grd.getOccupancyVect()->getTotalVal() == grd.getSize()); } TEST_CASE("CubeFiles") { std::string path = getenv("RDBASE"); path += "/Code/GraphMol/MolInteractionFields/test_data/"; auto fopts = v2::FileParsers::MolFileParserParams(); fopts.removeHs = false; auto mol = v2::FileParsers::MolFromMolFile(path + "HCl.mol", fopts); REQUIRE(mol); RealValueVect data(125, 0.0); Point3D o(0.0, 0.0, 0.0); UniformRealValueGrid3D grd(5.0, 5.0, 5.0, 1.0, &o, &data); for (unsigned int i = 0; i < grd.getSize(); i++) { grd.setVal(i, double(i / 10.0)); } writeToCubeFile(grd, path + "test3.cube", mol.get()); UniformRealValueGrid3D grd2; auto mol2 = readFromCubeFile(grd2, path + "test3.cube"); CHECK(grd.getSize() == grd2.getSize()); for (unsigned int i = 0; i < grd2.getSize(); i++) { CHECK(feq(grd2.getVal(i), double(i / 10.0))); } CHECK(grd.getNumX() == grd2.getNumX()); CHECK(grd.getNumY() == grd2.getNumY()); CHECK(grd.getNumZ() == grd2.getNumZ()); CHECK(feq(grd.getOffset().x, grd2.getOffset().x)); CHECK(feq(grd.getSpacing(), grd2.getSpacing())); CHECK(grd.compareVectors(grd2)); CHECK(grd.compareParams(grd2)); CHECK(grd.compareGrids(grd2)); CHECK(mol->getNumAtoms() == mol2->getNumAtoms()); for (unsigned int i = 0; i < mol->getNumAtoms(); i++) { CHECK(mol->getAtomWithIdx(i)->getAtomicNum() == mol2->getAtomWithIdx(i)->getAtomicNum()); CHECK(feq(mol->getConformer().getAtomPos(i).x, mol2->getConformer().getAtomPos(i).x)); CHECK(feq(mol->getConformer().getAtomPos(i).y, mol2->getConformer().getAtomPos(i).y)); CHECK(feq(mol->getConformer().getAtomPos(i).z, mol2->getConformer().getAtomPos(i).z)); } } TEST_CASE("Coulomb") { std::string path = getenv("RDBASE"); path += "/Code/GraphMol/MolInteractionFields/test_data/"; auto fopts = v2::FileParsers::MolFileParserParams(); fopts.removeHs = false; auto mol = v2::FileParsers::MolFromMolFile(path + "HCl.mol", fopts); REQUIRE(mol); computeGasteigerCharges(*mol); std::vector charges; std::vector pos; Conformer conf = mol->getConformer(0); for (auto i = 0u; i < mol->getNumAtoms(); ++i) { charges.push_back( mol->getAtomWithIdx(i)->getProp("_GasteigerCharge")); pos.push_back(conf.getAtomPos(i)); } auto grd = *constructGrid(*mol); auto grd2 = *constructGrid(*mol); Coulomb coul(*mol); calculateDescriptors(grd, coul); calculateDescriptors(grd2, Coulomb(charges, pos)); CHECK(grd.compareGrids(grd2)); CHECK(feq(coul(0.0, 0.0, 0.0, 1000), 0.0)); CHECK(coul(2.0, 0.0, 0.0, 1000) < 0); CHECK(coul(-2.0, 0.0, 0.0, 1000) > 0); CHECK(feq(coul(0.0, 0.0, 0.0, 0.1), 0.0)); calculateDescriptors(grd, Coulomb(*mol, 0, 1.0, true)); for (unsigned int i = 0; i < grd.getSize(); i++) { CHECK(grd.getVal(i) <= 0.0); } Coulomb coul1(*mol, 0, -1.0, false, "_GasteigerCharge", 0.0, 0.01); CHECK(coul1(-2.0, 0.0, 0.0, 1000) < 0); CHECK(coul1(2.0, 0.0, 0.0, 1000) > 0); Coulomb coul2 = Coulomb(*mol, 0, -.5, false, "_GasteigerCharge", 0.0, 0.01); CHECK(coul1(-2.0, 0.0, 0.0, 1000) < coul2(-2.0, 0.0, 0.0, 1000)); Coulomb coul3(*mol, 0, 1.0, false, "_GasteigerCharge", 0.01, 1.0); CHECK(coul3(0.0, 0.0, 0.0, 1000) > coul3(0.1, 0.0, 0.0, 1000)); CHECK(coul3(0.66, 0.0, 0.0, 1000) > coul3(0.68, 0.0, 0.0, 1000)); CHECK(coul3(0.70, 0.0, 0.0, 1000) > coul3(0.68, 0.0, 0.0, 1000)); CHECK(feq(coul3(0.0, 0.0, 0.0, 0.1), 0.0)); } TEST_CASE("CoulombDielectric") { std::string path = getenv("RDBASE"); path += "/Code/GraphMol/MolInteractionFields/test_data/"; auto fopts = v2::FileParsers::MolFileParserParams(); fopts.removeHs = false; auto mol = v2::FileParsers::MolFromMolFile(path + "HCl.mol", fopts); REQUIRE(mol); computeGasteigerCharges(*mol); std::vector charges; std::vector pos; Conformer conf = mol->getConformer(0); for (auto i = 0u; i < mol->getNumAtoms(); ++i) { charges.push_back( mol->getAtomWithIdx(i)->getProp("_GasteigerCharge")); pos.push_back(conf.getAtomPos(i)); } auto grd = *constructGrid(*mol); auto grd2 = *constructGrid(*mol); CoulombDielectric couldiele(*mol); calculateDescriptors(grd, couldiele); calculateDescriptors(grd2, CoulombDielectric(charges, pos)); CHECK(grd.compareGrids(grd2)); CHECK(feq(couldiele(0.0, 0.0, 0.0, 1000), 0.0)); CHECK(couldiele(2.0, 0.0, 0.0, 1000) < 0); CHECK(couldiele(-2.0, 0.0, 0.0, 1000) > 0); calculateDescriptors( grd, CoulombDielectric(*mol, 0, 1.0, true)); for (unsigned int i = 0; i < grd.getSize(); i++) { CHECK(grd.getVal(i) <= 0.0); } CHECK(feq(couldiele(0.0, 0.0, 0.0, 1000), 0.0)); CHECK(couldiele(2.0, 0.0, 0.0, 1000) < 0); CHECK(couldiele(-2.0, 0.0, 0.0, 1000) > 0); calculateDescriptors( grd, CoulombDielectric(*mol, 0, 1.0, true)); for (unsigned int i = 0; i < grd.getSize(); i++) { CHECK(grd.getVal(i) <= 0.0); } CoulombDielectric couldiele1(*mol, 0, -1.0); CHECK(couldiele1(-2.0, 0.0, 0.0, 1000) < 0); CHECK(couldiele1(2.0, 0.0, 0.0, 1000) > 0); CoulombDielectric couldiele2(*mol, 0, -.5); CHECK(couldiele1(-2.0, 0.0, 0.0, 1000) < couldiele2(-2.0, 0.0, 0.0, 1000)); CoulombDielectric couldiele3(*mol, 0, 1.0, false, "_GasteigerCharge", 0.01, 1.0); CHECK(couldiele3(0.0, 0.0, 0.0, 1000) > couldiele3(0.1, 0.0, 0.0, 1000)); CHECK(couldiele3(0.66, 0.0, 0.0, 1000) > couldiele3(0.68, 0.0, 0.0, 1000)); CHECK(couldiele3(0.70, 0.0, 0.0, 1000) > couldiele3(0.68, 0.0, 0.0, 1000)); mol = v2::FileParsers::MolFromMolFile(path + "glucose.mol", fopts); REQUIRE(mol); computeGasteigerCharges(*mol); CoulombDielectric couldiele4(*mol, 0, 1.0, false, "_GasteigerCharge", 0.01, 1.0, 80.0, 4.0); CoulombDielectric couldiele5(*mol, 0, 1.0, false, "_GasteigerCharge", 0.01, 1.0, 200.0, 4.0); CoulombDielectric couldiele6(*mol, 0, 1.0, false, "_GasteigerCharge", 0.01, 1.0, 80.0, 10.0); CHECK(couldiele5(-1.0, 0.0, 0.0, 1000) < couldiele4(-1.0, 0.0, 0.0, 1000)); CHECK(couldiele6(-1.0, 0.0, 0.0, 1000) < couldiele4(-1.0, 0.0, 0.0, 1000)); } TEST_CASE("VdWaals") { std::string path = getenv("RDBASE"); path += "/Code/GraphMol/MolInteractionFields/test_data/"; auto fopts = v2::FileParsers::MolFileParserParams(); fopts.removeHs = false; auto mol = v2::FileParsers::MolFromMolFile(path + "HCl.mol", fopts); REQUIRE(mol); try { MMFFVdWaals vdw(*mol, 0, 6, false, 1.0); } catch (ValueErrorException &dexp) { BOOST_LOG(rdInfoLog) << "Expected failure: " << dexp.what() << "\n"; } mol = v2::FileParsers::MolFromMolFile(path + "HCN.mol", fopts); REQUIRE(mol); { MMFFVdWaals vdw(*mol, 0, 6, false, 1.0); CHECK(vdw(-5.0, 0, 0, 1000) < 0); CHECK(vdw(-1.68, 0, 0, 1000) > vdw(-5.0, 0, 0, 1000)); CHECK(vdw(-5.0, 0, 0, 1000) < vdw(-10.0, 0, 0, 1000)); } auto mol2 = v2::FileParsers::MolFromMolFile(path + "h2o.mol", fopts); REQUIRE(mol2); MMFFVdWaals vdw(*mol2, 0, 6, false, 1.0); MMFFVdWaals vdw2(*mol2, 0, 6, true, 1.0); CHECK(fabs(vdw2(-3.0, 0, 0, 1000) - vdw(-3.0, 0, 0, 1000)) > 0.0001); UFFVdWaals vdw3(*mol, 0, "O_3", 1.0); CHECK(vdw3(-5.0, 0, 0, 1000) < 0); CHECK(vdw3(-1.68, 0, 0, 1000) > vdw3(-5.0, 0, 0, 1000)); CHECK(vdw3(-5.0, 0, 0, 1000) < vdw3(-10.0, 0, 0, 1000)); std::vector names{ "acetone", "aceticacid", "phenol", "phenolate", "serine", "threonine", "ethanol", "diethylether", "h2o", "ammonia", "ethylamine", "imine", "acetonitrile", "histidine", "phenylamine", "methylammonium", "fluoromethane", "chloromethane", "bromomethane", "glycine", "glyphe", "glysergly", "glythrgly", "glucose"}; for (const auto &name : names) { mol = v2::FileParsers::MolFromMolFile(path + name + ".mol", fopts); REQUIRE(mol); MMFFVdWaals mmffVdw(*mol); CHECK(mmffVdw(0.0, 0.0, 0.0, 1000)); UFFVdWaals uffVdw(*mol); CHECK(uffVdw(0.0, 0.0, 0.0, 1000)); } } TEST_CASE("HBond") { std::string path = getenv("RDBASE"); path += "/Code/GraphMol/MolInteractionFields/test_data/"; // Generate Molecule with 3D Coordinates and Partial Charges auto fopts = v2::FileParsers::MolFileParserParams(); fopts.removeHs = false; auto mol = v2::FileParsers::MolFromMolFile(path + "ethane.mol", fopts); // Ethane REQUIRE(mol); auto grd = *constructGrid(*mol, 0, 5.0, 1); for (unsigned int i = 0; i < grd.getSize(); i++) { grd.setVal(i, 1.0); } UniformRealValueGrid3D grd1(grd); HBond hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); calculateDescriptors(grd, hbonddes); TEST_ASSERT(!grd.compareGrids(grd1)); const RealValueVect *vect = grd.getOccupancyVect(); const RealValueVect *vect1 = grd1.getOccupancyVect(); CHECK((unsigned int)fabs(((*vect1 - *vect).getTotalVal())) == grd.getSize()); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); for (unsigned int i = 0; i < grd.getSize(); i++) { grd.setVal(i, 1.0); } calculateDescriptors(grd, hbonddes); TEST_ASSERT(!grd.compareGrids(grd1)); CHECK((unsigned int)fabs(((*vect1 - *vect).getTotalVal()))); mol = v2::FileParsers::MolFromMolFile(path + "aceticacid.mol", fopts); // Acetic Acid REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 2); calculateDescriptors(grd, hbonddes); HBond hbonddes1(*mol, 0, "O", true, 0.001); CHECK(hbonddes1.getNumInteractions() == 1); calculateDescriptors(grd, hbonddes1); HBond hbonddes2(*mol, 0, "O", false, 0.001); CHECK(hbonddes1(4.0, 0.0, 1.0, 1000) > hbonddes2(4.0, 0.0, 1.0, 1000)); HBond hbonddes3(*mol, 0, "NH", true, 0.001); CHECK(hbonddes3.getNumInteractions() == 2); CHECK(hbonddes(2.0, 2.0, 2.0, 1000) < hbonddes3(2.0, 2.0, 2.0, 1000)); HBond hbonddes4(*mol, 0, "N", true, 0.001); CHECK(hbonddes4.getNumInteractions() == 1); CHECK(hbonddes1(3.0, 0.0, 0.0, 1000) < hbonddes4(3.0, 0.0, 0.0, 1000)); mol = v2::FileParsers::MolFromMolFile(path + "acetone.mol", fopts); // Acetone REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 1); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "diethylether.mol", fopts); // Et2O REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 1); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "h2o.mol", fopts); // H2O REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 1); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 2); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "ammonia.mol", fopts); // ammonia REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 1); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 3); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "imine.mol", fopts); // imine REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 1); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "methylammonium.mol", fopts); // methylammonium REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 3); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "chloromethane.mol", fopts); // Chloromethane REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 1); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "phosphonate.mol", fopts); // Phosphonate REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 3); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "phosphatediester.mol", fopts); // Phosphatediester REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 4); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "hydrogenphosphatediester.mol", fopts); // Hydrogenphosphatediester REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 4); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 1); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "mustardgas.mol", fopts); // mustard gas REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 2); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "alicin.mol", fopts); // Alicin REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 1); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 0); calculateDescriptors(grd, hbonddes); mol = v2::FileParsers::MolFromMolFile(path + "sulfanilamide.mol", fopts); // Sulfanilamide REQUIRE(mol); grd = *constructGrid(*mol, 0, 5.0, 1); hbonddes = HBond(*mol, 0, "OH", true, 0.001); CHECK(hbonddes.getNumInteractions() == 3); calculateDescriptors(grd, hbonddes); hbonddes = HBond(*mol, 0, "O", true, 0.001); CHECK(hbonddes.getNumInteractions() == 4); calculateDescriptors(grd, hbonddes); } TEST_CASE("Hydrophilic") { std::string path = getenv("RDBASE"); path += "/Code/GraphMol/MolInteractionFields/test_data/"; auto fopts = v2::FileParsers::MolFileParserParams(); fopts.removeHs = false; auto mol = v2::FileParsers::MolFromMolFile(path + "h2o.mol", fopts); REQUIRE(mol); Hydrophilic hydro(*mol); HBond hbondOH(*mol, 0, "OH"); HBond hbondO(*mol, 0, "O"); double hyd = hydro(0.0, 0.0, 0.0, 1000), hOH = hbondOH(0.0, 0.0, 0.0, 1000), hO = hbondO(0.0, 0.0, 0.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); hyd = hydro(1.0, 1.5, 2.0, 1000); hOH = hbondOH(1.0, 1.5, 2.0, 1000); hO = hbondO(1.0, 1.5, 2.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); hyd = hydro(2.0, 1.5, -3.0, 1000); hOH = hbondOH(2.0, 1.5, -3.0, 1000); hO = hbondO(2.0, 1.5, -3.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); hyd = hydro(-2.5, 0.5, 3.0, 1000); hOH = hbondOH(-2.5, 0.5, 3.0, 1000); hO = hbondO(-2.5, 0.5, 3.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); hyd = hydro(10.0, 1.5, 1.0, 1000); hOH = hbondOH(10.0, 1.5, 1.0, 1000); hO = hbondO(10.0, 1.5, 1.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); hyd = hydro(6.0, -5.0, 0.0, 1000); hOH = hbondOH(6.0, -5.0, 0.0, 1000); hO = hbondO(6.0, -5.0, 0.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); hyd = hydro(-3.0, -3.0, 7.0, 1000); hOH = hbondOH(-3.0, -3.0, 7.0, 1000); hO = hbondO(-3.0, -3.0, 7.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); hyd = hydro(1.0, 0.0, 0.0, 1000); hOH = hbondOH(1.0, 0.0, 0.0, 1000); hO = hbondO(1.0, 0.0, 0.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); hyd = hydro(0.0, 2.0, 2.0, 1000); hOH = hbondOH(0.0, 2.0, 2.0, 1000); hO = hbondO(0.0, 2.0, 2.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); hyd = hydro(2.0, -2.0, 0.0, 1000); hOH = hbondOH(2.0, -2.0, 0.0, 1000); hO = hbondO(2.0, -2.0, 0.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); hyd = hydro(2.0, -2.0, -3.0, 1000); hOH = hbondOH(2.0, -2.0, -3.0, 1000); hO = hbondO(2.0, -2.0, -3.0, 1000); CHECK(feq(std::min(hOH, hO), hyd)); }