1 // 2 // Copyright (C) 2021 Greg Landrum 3 // 4 // @@ All Rights Reserved @@ 5 // This file is part of the RDKit. 6 // The contents are covered by the terms of the BSD license 7 // which is included in the file license.txt, found at the root 8 // of the RDKit source tree. 9 // 10 11 #include "catch.hpp" 12 13 #include <GraphMol/RDKitBase.h> 14 #include <GraphMol/SmilesParse/SmilesParse.h> 15 #include <GraphMol/FileParsers/MolSupplier.h> 16 #include <GraphMol/Descriptors/ConnectivityDescriptors.h> 17 #include <GraphMol/Descriptors/PMI.h> 18 19 using namespace RDKit; 20 21 TEST_CASE("Kier kappa2", "[2D]") { 22 SECTION("values from https://doi.org/10.1002/qsar.19860050103") { 23 std::vector<std::pair<std::string, double>> data = { 24 // Table 5 from the paper 25 {"c1ccccc15.Cl5", 1.987}, 26 {"c1ccccc15.F5", 1.735}, 27 {"c1ccccc15.[N+]5(=O)O", 2.259}, 28 {"c1ccccc15.C5(=O)C", 2.444}, 29 #if 0 30 // expected values from paper (differences are due to hybridization mismatches) 31 {"c1ccccc15.N5(C)C", 2.646}, 32 {"c1ccccc15.C5(=O)N", 2.416}, 33 {"c1ccccc15.C5(=O)O", 2.416}, 34 {"c1ccccc15.S5(=O)(=O)C", 2.617}, 35 {"c1ccccc15.O5", 1.756}, 36 #else 37 {"c1ccccc15.N5(C)C", 2.53}, 38 {"c1ccccc15.C5(=O)N", 2.31}, 39 {"c1ccccc15.C5(=O)O", 2.31}, 40 {"c1ccccc15.S5(=O)(=O)C", 2.42}, 41 {"c1ccccc15.O5", 1.65}, 42 #endif 43 }; 44 for (const auto &pr : data) { 45 std::unique_ptr<ROMol> m(SmilesToMol(pr.first)); 46 REQUIRE(m); 47 auto k2 = Descriptors::calcKappa2(*m); 48 CHECK(k2 == Approx(pr.second).epsilon(0.01)); 49 } 50 } 51 } 52 53 TEST_CASE("Kier Phi", "[2D]") { 54 SECTION("regression-test values from the paper") { 55 std::vector<std::pair<std::string, double>> data = { 56 // Table 1 from the paper 57 {"CCCCCC", 5.00}, 58 {"CCCCCCC", 6.00}, 59 {"CCCCCCCC", 7.00}, 60 {"CCC(C)CC", 3.20}, 61 {"CC(C)C(C)C", 2.22}, 62 {"CC(C)(C)CC", 1.63}, 63 {"C1CCCC1", 0.92}, 64 {"C1CCCCC1", 1.54}, 65 {"C1CCCCCC1", 2.25}, 66 {"CCCCC=C", 4.53}, 67 {"C=CCCC=C", 4.07}, 68 {"C#CCCCC", 4.21}, 69 {"c1ccccc1", 0.91}, 70 // additional from Table 2 71 {"C=CCC=CC", 4.09}, 72 {"CC=CC=CC", 4.09}, 73 {"C1=CCCCC1", 1.31}, 74 {"C1=CC=CCC1", 1.1}, 75 {"C1=CCC=CC1", 1.1}, 76 // additional from Table 3 77 {"CCCCCCCCCC", 9.00}, 78 {"CC(C)CCCCC", 5.14}, 79 {"CCC(C)CCCC", 5.14}, 80 {"CC(C)CCCC", 4.17}, 81 {"CCC(C)CCC", 4.17}, 82 {"CCC(CC)CCC", 5.14}, 83 {"CCC(CC)CC", 4.17}, 84 {"CC(C)(C)CCC", 2.34}, 85 {"CC(C)C(C)CC", 3.06}, 86 {"CCC(C)(C)CC", 2.34}, 87 {"CC(C)(C)C(C)C", 1.85}, 88 // additional from table 4 89 {"CCOCC", 3.93}, 90 {"CCC(=O)CC", 2.73}, 91 {"CCc1ccc(CC)cc1", 2.49}, 92 {"CCC(O)CC", 3.14}, 93 {"CCCC(Cl)(Cl)CCC", 4.69}, 94 {"CCC(F)C(F)CC", 3.75}, 95 #if 0 96 // expected values from paper (differences are due to hybridization mismatches) 97 {"CCOC(=O)CC", 3.61}, 98 {"CCC(=O)Nc1ccc(CC)cc1", 3.65}, 99 #else 100 {"CCOC(=O)CC", 3.38}, 101 {"CCC(=O)Nc1ccc(CC)cc1", 3.50}, 102 #endif 103 104 }; 105 for (const auto &pr : data) { 106 std::unique_ptr<ROMol> m(SmilesToMol(pr.first)); 107 REQUIRE(m); 108 auto val = Descriptors::calcPhi(*m); 109 CHECK(val == Approx(pr.second).epsilon(0.01)); 110 } 111 } 112 } 113 114 #ifdef RDK_BUILD_DESCRIPTORS3D 115 TEST_CASE( 116 "Github #4167: SpherocityIndex() not being recalculated for different " 117 "conformers", 118 "[3D]") { 119 std::string pathName = getenv("RDBASE"); 120 std::string sdfName = 121 pathName + "/Code/GraphMol/Descriptors/test_data/github4167.sdf"; 122 bool sanitize = true; 123 bool removeHs = false; 124 RDKit::SDMolSupplier reader(sdfName, sanitize, removeHs); 125 std::unique_ptr<ROMol> m1(reader.next()); 126 std::unique_ptr<ROMol> m2(reader.next()); 127 REQUIRE(m1); 128 REQUIRE(m2); 129 m1->addConformer(new RDKit::Conformer(m2->getConformer()), true); 130 REQUIRE(m1->getNumConformers() == 2); 131 132 { 133 int confId = 0; 134 auto v1_0 = RDKit::Descriptors::spherocityIndex(*m1, confId, true); 135 auto v2 = RDKit::Descriptors::spherocityIndex(*m2, confId, true); 136 confId = 1; 137 auto v1_1 = RDKit::Descriptors::spherocityIndex(*m1, confId, true); 138 CHECK(v1_0 != v1_1); 139 CHECK(v1_1 == v2); 140 } 141 { 142 std::vector<double (*)(const ROMol &, int, bool, bool)> funcs{ 143 RDKit::Descriptors::NPR1, 144 RDKit::Descriptors::NPR2, 145 RDKit::Descriptors::PMI1, 146 RDKit::Descriptors::PMI2, 147 RDKit::Descriptors::PMI3, 148 RDKit::Descriptors::radiusOfGyration, 149 RDKit::Descriptors::inertialShapeFactor, 150 RDKit::Descriptors::eccentricity, 151 RDKit::Descriptors::asphericity}; 152 for (const auto func : funcs) { 153 bool useAtomMasses = true; 154 bool force = true; 155 int confId = 0; 156 auto v1_0 = func(*m1, confId, useAtomMasses, force); 157 auto v2 = func(*m2, confId, useAtomMasses, force); 158 confId = 1; 159 auto v1_1 = func(*m1, confId, useAtomMasses, force); 160 CHECK(v1_0 != v1_1); 161 CHECK(v1_1 == v2); 162 } 163 } 164 // { // surrogate for NPR1, NPR2, PMI1, PM2, 165 // int confId = 0; 166 // auto v1_0 = RDKit::Descriptors::spherocityIndex(*m1, confId, true); 167 // auto v2 = RDKit::Descriptors::spherocityIndex(*m2, confId, true); 168 // confId = 1; 169 // auto v1_1 = RDKit::Descriptors::spherocityIndex(*m1, confId, true); 170 // CHECK(v1_0 != v1_1); 171 // CHECK(v1_1 == v2); 172 // } 173 } 174 #endif