// // Copyright (C) 2022 Greg Landrum 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 "RDGeneral/test.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace RDKit; TEST_CASE("Github #5863: failure in WedgeMolBonds") { SECTION("as reported") { auto mol = "C[C@]12C=CC(=O)C=C1CC[C@@H]1[C@@H]2[C@@H](O)C[C@@]2(C)[C@H]1CC[C@]2(O)C(=O)CO |(1.94354,1.43772,;2.70098,0.14301,;3.44351,1.44633,;4.94349,1.45494,;5.70093,0.160228,;7.20091,0.168838,;4.9584,-1.14309,;3.45842,-1.1517,;2.71589,-2.45502,;1.21592,-2.46363,;0.458474,-1.16892,;1.20101,0.1344,;0.443562,1.42911,;1.18609,2.73243,;-1.05641,1.4205,;-1.79895,0.117181,;-2.53364,1.42493,;-1.0415,-1.17753,;-2.03878,-2.29799,;-3.41258,-1.69576,;-3.26435,-0.203103,;-3.6102,1.25648,;-4.76433,-0.211712,;-5.50686,-1.51503,;-5.52177,1.083,;-7.02175,1.07439,)|"_smiles; REQUIRE(mol); unsigned int radius = 2; unsigned int atomId = 2; auto env = findAtomEnvironmentOfRadiusN(*mol, radius + 1, atomId); std::unique_ptr frag(Subgraphs::pathToSubmol(*mol, env)); REQUIRE(frag); Chirality::wedgeMolBonds(*frag, &frag->getConformer()); INFO(MolToV3KMolBlock(*frag)); CHECK(frag->getBondBetweenAtoms(9, 10)->getBondDir() != Bond::BondDir::NONE); } } TEST_CASE("translating the chiral flag to stereo groups") { SECTION("basics") { auto withFlag = R"CTAB( Mrv2211 03302308372D 5 4 0 0 1 0 999 V2000 -6.5625 3.9286 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -5.8480 4.3411 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 -5.1336 3.9286 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -6.1775 5.0826 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 -5.4384 5.0893 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 2 5 1 1 0 0 0 2 4 1 6 0 0 0 M END )CTAB"_ctab; REQUIRE(withFlag); int flag = 0; CHECK(withFlag->getPropIfPresent(common_properties::_MolFileChiralFlag, flag)); CHECK(flag == 1); RWMol zeroFlag(*withFlag); zeroFlag.setProp(common_properties::_MolFileChiralFlag, 0); RWMol noFlag(*withFlag); noFlag.clearProp(common_properties::_MolFileChiralFlag); CHECK(withFlag->getStereoGroups().empty()); translateChiralFlagToStereoGroups(*withFlag); CHECK(!withFlag->hasProp(common_properties::_MolFileChiralFlag)); auto sgs = withFlag->getStereoGroups(); REQUIRE(sgs.size() == 1); CHECK(sgs[0].getGroupType() == StereoGroupType::STEREO_ABSOLUTE); CHECK(sgs[0].getAtoms().size() == 1); CHECK(sgs[0].getAtoms()[0]->getIdx() == 1); { RWMol cp(zeroFlag); translateChiralFlagToStereoGroups(cp); CHECK(!cp.hasProp(common_properties::_MolFileChiralFlag)); auto sgs = cp.getStereoGroups(); REQUIRE(sgs.size() == 1); CHECK(sgs[0].getGroupType() == StereoGroupType::STEREO_AND); CHECK(sgs[0].getAtoms().size() == 1); CHECK(sgs[0].getAtoms()[0]->getIdx() == 1); } { RWMol cp(zeroFlag); translateChiralFlagToStereoGroups(cp, StereoGroupType::STEREO_OR); CHECK(!cp.hasProp(common_properties::_MolFileChiralFlag)); auto sgs = cp.getStereoGroups(); REQUIRE(sgs.size() == 1); CHECK(sgs[0].getGroupType() == StereoGroupType::STEREO_OR); CHECK(sgs[0].getAtoms().size() == 1); CHECK(sgs[0].getAtoms()[0]->getIdx() == 1); } translateChiralFlagToStereoGroups(noFlag); CHECK(!noFlag.hasProp(common_properties::_MolFileChiralFlag)); CHECK(noFlag.getStereoGroups().empty()); } SECTION("explicit zero chiral flag") { auto zeroFlag = R"CTAB( Mrv2211 03302308372D 5 4 0 0 o 0 999 V2000 -6.5625 3.9286 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 -5.8480 4.3411 0.0000 C 0 0 1 0 0 0 0 0 0 0 0 0 -5.1336 3.9286 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -6.1775 5.0826 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 -5.4384 5.0893 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 0 0 0 2 3 1 0 0 0 0 2 5 1 1 0 0 0 2 4 1 6 0 0 0 M END )CTAB"_ctab; REQUIRE(zeroFlag); int flag = 0; CHECK(zeroFlag->getPropIfPresent(common_properties::_MolFileChiralFlag, flag)); CHECK(flag == 0); { RWMol cp(*zeroFlag); translateChiralFlagToStereoGroups(cp); CHECK(!cp.hasProp(common_properties::_MolFileChiralFlag)); auto sgs = cp.getStereoGroups(); REQUIRE(sgs.size() == 1); CHECK(sgs[0].getGroupType() == StereoGroupType::STEREO_AND); CHECK(sgs[0].getAtoms().size() == 1); CHECK(sgs[0].getAtoms()[0]->getIdx() == 1); } } SECTION("multiple stereocenters") { auto noFlag = "C[C@@](N)(F)C[C@](C)(O)F"_smiles; REQUIRE(noFlag); RWMol withFlag(*noFlag); withFlag.setProp(common_properties::_MolFileChiralFlag, 1); CHECK(withFlag.getStereoGroups().empty()); translateChiralFlagToStereoGroups(withFlag); CHECK(!withFlag.hasProp(common_properties::_MolFileChiralFlag)); auto sgs = withFlag.getStereoGroups(); REQUIRE(sgs.size() == 1); CHECK(sgs[0].getGroupType() == StereoGroupType::STEREO_ABSOLUTE); CHECK(sgs[0].getAtoms().size() == 2); CHECK(sgs[0].getAtoms()[0]->getIdx() == 1); CHECK(sgs[0].getAtoms()[1]->getIdx() == 5); { RWMol zeroFlag(*noFlag); zeroFlag.setProp(common_properties::_MolFileChiralFlag, 0); translateChiralFlagToStereoGroups(zeroFlag); CHECK(!zeroFlag.hasProp(common_properties::_MolFileChiralFlag)); auto sgs = zeroFlag.getStereoGroups(); REQUIRE(sgs.size() == 1); CHECK(sgs[0].getGroupType() == StereoGroupType::STEREO_AND); CHECK(sgs[0].getAtoms().size() == 2); CHECK(sgs[0].getAtoms()[0]->getIdx() == 1); CHECK(sgs[0].getAtoms()[1]->getIdx() == 5); } } SECTION("pre-existing ABS stereogroup") { auto withFlag = "C[C@@](N)(F)C[C@](C)(O)F |a:1|"_smiles; REQUIRE(withFlag); withFlag->setProp(common_properties::_MolFileChiralFlag, 1); CHECK(withFlag->getStereoGroups().size() == 1); translateChiralFlagToStereoGroups(*withFlag); CHECK(!withFlag->hasProp(common_properties::_MolFileChiralFlag)); auto sgs = withFlag->getStereoGroups(); REQUIRE(sgs.size() == 1); CHECK(sgs[0].getGroupType() == StereoGroupType::STEREO_ABSOLUTE); CHECK(sgs[0].getAtoms().size() == 2); CHECK(sgs[0].getAtoms()[0]->getIdx() == 1); CHECK(sgs[0].getAtoms()[1]->getIdx() == 5); } } TEST_CASE("github #6310: crossed ring bonds not written to mol blocks") { SECTION("as reported") { std::vector smileses = {"C1C=CCCCCCCCCC1", "C1C=CCCCCC1"}; for (const auto &smiles : smileses) { auto m = std::unique_ptr(SmilesToMol(smiles)); REQUIRE(m); CHECK(m->getBondWithIdx(1)->getBondType() == Bond::BondType::DOUBLE); CHECK(m->getBondWithIdx(1)->getStereo() == Bond::BondStereo::STEREONONE); auto mb = MolToV3KMolBlock(*m); CHECK(mb.find("CFG=2") != std::string::npos); mb = MolToMolBlock(*m); CHECK(mb.find("2 3 2 3") != std::string::npos); // make sure we also write the crossed bond when it's explicitly crossed m->getBondWithIdx(1)->setStereo(Bond::BondStereo::STEREOANY); mb = MolToV3KMolBlock(*m); CHECK(mb.find("CFG=2") != std::string::npos); mb = MolToMolBlock(*m); CHECK(mb.find("2 3 2 3") != std::string::npos); } } SECTION("don't cross bonds in small rings") { auto m = "C1C=CCCCC1"_smiles; REQUIRE(m); CHECK(m->getBondWithIdx(1)->getBondType() == Bond::BondType::DOUBLE); CHECK(m->getBondWithIdx(1)->getStereo() == Bond::BondStereo::STEREONONE); auto mb = MolToV3KMolBlock(*m); CHECK(mb.find("CFG=2") == std::string::npos); mb = MolToMolBlock(*m); CHECK(mb.find("2 3 2 3") == std::string::npos); m->getBondWithIdx(1)->setStereo(Bond::BondStereo::STEREOANY); mb = MolToV3KMolBlock(*m); CHECK(mb.find("CFG=2") != std::string::npos); mb = MolToMolBlock(*m); CHECK(mb.find("2 3 2 3") != std::string::npos); } } void testStereoExample(const std::string &mb, unsigned int aidx, Atom::ChiralType expected, const std::string &expectedCIP) { INFO(mb); std::unique_ptr m(MolBlockToMol(mb)); REQUIRE(m); CHECK(m->getAtomWithIdx(aidx)->getChiralTag() == expected); CIPLabeler::assignCIPLabels(*m); std::string CIP = ""; CHECK(m->getAtomWithIdx(aidx)->getPropIfPresent(common_properties::_CIPCode, CIP)); CHECK(CIP == expectedCIP); } TEST_CASE("IUPAC recommendations") { SECTION("simple examples") { std::vector mbs = { R"CTAB( Mrv2211 06082308462D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 5 4 0 0 0 M V30 BEGIN ATOM M V30 1 C -7.875 5.3333 0 0 M V30 2 C -6.5413 6.1033 0 0 M V30 3 O -5.2076 5.3333 0 0 M V30 4 F -7.3292 7.3957 0 0 M V30 5 Cl -5.4654 7.2052 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 3 1 2 4 CFG=1 M V30 4 1 2 5 CFG=3 M V30 END BOND M V30 END CTAB M END )CTAB", R"CTAB( Mrv2211 06082309052D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 5 4 0 0 0 M V30 BEGIN ATOM M V30 1 C -7.875 5.3333 0 0 M V30 2 C -6.5413 6.1033 0 0 CFG=2 M V30 3 O -5.2076 5.3333 0 0 M V30 4 F -6.5413 8 0 0 M V30 5 Cl -5.4654 7.2052 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 3 1 2 4 CFG=1 M V30 4 1 2 5 CFG=3 M V30 END BOND M V30 END CTAB M END )CTAB", R"CTAB( Mrv2211 06082309052D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 5 4 0 0 0 M V30 BEGIN ATOM M V30 1 C -7.875 5.3333 0 0 M V30 2 C -6.5413 6.1033 0 0 CFG=2 M V30 3 O -5.2076 5.3333 0 0 M V30 4 F -4.9369 6.9639 0 0 M V30 5 Cl -6.5413 7.9602 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 3 1 2 4 CFG=1 M V30 4 1 2 5 CFG=3 M V30 END BOND M V30 END CTAB M END )CTAB", R"CTAB(IUPAC does not like this one Mrv2211 06082309142D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 5 4 0 0 0 M V30 BEGIN ATOM M V30 1 C -7.875 5.3333 0 0 M V30 2 C -6.5413 6.1033 0 0 M V30 3 O -5.2076 5.3333 0 0 M V30 4 F -6.5413 4.2897 0 0 M V30 5 Cl -6.5413 7.9602 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 3 1 2 4 CFG=1 M V30 4 1 2 5 M V30 END BOND M V30 END CTAB M END )CTAB", R"CTAB(IUPAC does not like this one2 Mrv2211 06082309142D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 5 4 0 0 0 M V30 BEGIN ATOM M V30 1 C -7.875 5.3333 0 0 M V30 2 C -6.5413 6.1033 0 0 M V30 3 O -5.2076 5.3333 0 0 M V30 4 F -6.5413 4.2897 0 0 M V30 5 Cl -6.5413 7.9602 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 1 2 3 M V30 3 1 2 4 M V30 4 1 2 5 CFG=1 M V30 END BOND M V30 END CTAB M END )CTAB", }; for (const auto &mb : mbs) { testStereoExample(mb, 1, Atom::ChiralType::CHI_TETRAHEDRAL_CCW, "S"); if (mb.find("CFG=3") != std::string::npos && mb.find("CFG=1") != std::string::npos) { std::string cp = mb; testStereoExample(cp.replace(mb.find("CFG=1"), 5, " "), 1, Atom::ChiralType::CHI_TETRAHEDRAL_CCW, "S"); cp = mb; testStereoExample(cp.replace(mb.find("CFG=3"), 5, " "), 1, Atom::ChiralType::CHI_TETRAHEDRAL_CCW, "S"); } } } SECTION("three coordinate") { auto m = R"CTAB( Mrv2108 01192209042D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 8 7 0 0 0 M V30 BEGIN ATOM M V30 1 C 2.31 4.001 0 0 M V30 2 C 1.54 2.6674 0 0 CFG=2 M V30 3 O -0 2.6674 0 0 M V30 4 C 2.31 1.3337 0 0 CFG=1 M V30 5 F 3.85 1.3337 0 0 M V30 6 C 1.54 0 0 0 CFG=2 M V30 7 C 2.31 -1.3337 0 0 M V30 8 O 0 0 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 2 1 M V30 2 1 2 3 CFG=3 M V30 3 1 4 2 M V30 4 1 4 5 CFG=1 M V30 5 1 4 6 M V30 6 1 6 7 M V30 7 1 6 8 CFG=3 M V30 END BOND M V30 END CTAB M END)CTAB"_ctab; REQUIRE(m); CHECK(m->getAtomWithIdx(1)->getChiralTag() == Atom::ChiralType::CHI_TETRAHEDRAL_CW); CHECK(m->getAtomWithIdx(3)->getChiralTag() == Atom::ChiralType::CHI_TETRAHEDRAL_CW); CHECK(m->getAtomWithIdx(5)->getChiralTag() == Atom::ChiralType::CHI_TETRAHEDRAL_CCW); } SECTION("this came up") { std::string rdbase = getenv("RDBASE"); std::string fName = rdbase + "/Code/GraphMol/test_data/github87.mol"; std::unique_ptr m{MolFileToMol(fName)}; REQUIRE(m); CHECK(m->getAtomWithIdx(0)->getChiralTag() == Atom::ChiralType::CHI_TETRAHEDRAL_CW); } SECTION("narrow angle") { auto m = R"CTAB( Mrv2211 06092305312D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 5 4 0 0 0 M V30 BEGIN ATOM M V30 1 C -1.4875 5.1589 0 0 M V30 2 C -1.5412 6.698 0 0 M V30 3 F -2.2685 4.0178 0 0 M V30 4 Br -0.7027 4.0359 0 0 M V30 5 Cl -1.4337 3.6198 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 CFG=1 M V30 2 1 1 3 M V30 3 1 1 4 M V30 4 1 1 5 M V30 END BOND M V30 END CTAB M END )CTAB"_ctab; REQUIRE(m); CHECK(m->getAtomWithIdx(0)->getChiralTag() == Atom::ChiralType::CHI_TETRAHEDRAL_CW); } SECTION("track bond starting points") { auto m = R"CTAB(blah RDKit 2D 7 7 0 0 0 0 0 0 0 0999 V2000 -3.1960 -20.3395 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -3.1960 -19.5114 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0 -3.1921 -21.7775 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -2.7763 -21.0619 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 -2.0577 -21.4777 0.0000 Cl 0 0 0 0 0 0 0 0 0 0 0 0 -4.0179 -20.3565 0.0000 Br 0 0 0 0 0 0 0 0 0 0 0 0 -2.3411 -20.6109 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 1 4 1 6 1 6 1 0 1 7 1 0 4 3 1 1 4 5 1 0 4 7 1 0 M END)CTAB"_ctab; REQUIRE(m); CHECK(m->getAtomWithIdx(0)->getChiralTag() != Atom::ChiralType::CHI_UNSPECIFIED); CHECK(m->getAtomWithIdx(3)->getChiralTag() != Atom::ChiralType::CHI_UNSPECIFIED); } SECTION("linear arrangements") { { auto m = R"CTAB( Mrv2211 06102314502D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 6 5 0 0 1 M V30 BEGIN ATOM M V30 1 O 9.0665 0.9156 0 0 M V30 2 N 9.6304 4.5593 0 0 M V30 3 C 8.2965 2.2493 0 0 MASS=14 M V30 4 C 6.9628 1.4792 0 0 M V30 5 C 9.6304 3.0193 0 0 M V30 6 H 7.8191 3.0761 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 1 CFG=1 M V30 2 1 2 5 M V30 3 1 3 4 M V30 4 1 3 5 M V30 5 1 3 6 M V30 END BOND M V30 END CTAB M END )CTAB"_ctab; REQUIRE(m); CHECK(m->getAtomWithIdx(2)->getChiralTag() == Atom::ChiralType::CHI_TETRAHEDRAL_CCW); } { auto m = R"CTAB(opposing stereo Mrv2211 06102314502D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 6 5 0 0 1 M V30 BEGIN ATOM M V30 1 O 9.0665 0.9156 0 0 M V30 2 N 9.6304 4.5593 0 0 M V30 3 C 8.2965 2.2493 0 0 MASS=14 M V30 4 C 6.9628 1.4792 0 0 M V30 5 C 9.6304 3.0193 0 0 M V30 6 H 7.8191 3.0761 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 1 CFG=1 M V30 2 1 2 5 M V30 3 1 3 4 M V30 4 1 3 5 M V30 5 1 3 6 CFG=3 M V30 END BOND M V30 END CTAB M END )CTAB"_ctab; REQUIRE(m); CHECK(m->getAtomWithIdx(2)->getChiralTag() == Atom::ChiralType::CHI_UNSPECIFIED); } { // std::cerr<<"11111111111111"<getAtomWithIdx(2)->getChiralTag() == Atom::ChiralType::CHI_UNSPECIFIED); } { // IUPAC (ST-1.2.12) says this one is wrong. It definitely requires making // an assumption about where the H is. auto m = R"CTAB(three-coordinate, T shaped, wedge in the middle Mrv2211 06102314502D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 5 4 0 0 1 M V30 BEGIN ATOM M V30 1 O 9.0665 0.9156 0 0 M V30 2 N 9.6304 4.5593 0 0 M V30 3 C 8.2965 2.2493 0 0 MASS=14 M V30 4 C 6.9628 1.4792 0 0 M V30 5 C 9.6304 3.0193 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 3 1 CFG=1 M V30 2 1 2 5 M V30 3 1 3 4 M V30 4 1 3 5 M V30 END BOND M V30 END CTAB M END )CTAB"_ctab; REQUIRE(m); CHECK(m->getAtomWithIdx(2)->getChiralTag() == Atom::ChiralType::CHI_TETRAHEDRAL_CCW); } } } TEST_CASE( "GitHub Issue #6502: MolToMolBlock writes \"either\" stereo for double bonds" "which shouldn't be stereo.", "[bug][molblock][stereo]") { auto m = "CP1(O)=NP(C)(O)=NP(C)(O)=NP(C)(O)=N1"_smiles; REQUIRE(m); auto mb = MolToV3KMolBlock(*m); CHECK(mb.find("CFG=2") == std::string::npos); } TEST_CASE("stereo in ring", "[molblock][stereo]") { SECTION("test 1") { auto molblock = R"CTAB( Mrv2311 10242314442D 0 0 0 0 0 999 V3000 M V30 BEGIN CTAB M V30 COUNTS 10 11 0 0 1 M V30 BEGIN ATOM M V30 1 C -2.6673 -0.77 0 0 M V30 2 C -2.6673 0.77 0 0 M V30 3 C -1.3337 1.54 0 0 M V30 4 C 0 0.77 0 0 M V30 5 C 1.3336 1.54 0 0 M V30 6 C 2.6673 0.77 0 0 M V30 7 C 2.6673 -0.77 0 0 M V30 8 C 1.3336 -1.54 0 0 M V30 9 C 0 -0.77 0 0 M V30 10 C -1.3337 -1.54 0 0 M V30 END ATOM M V30 BEGIN BOND M V30 1 1 1 2 M V30 2 2 2 3 M V30 3 1 3 4 M V30 4 1 4 5 M V30 5 1 5 6 M V30 6 1 6 7 M V30 7 1 7 8 M V30 8 1 8 9 M V30 9 1 4 9 M V30 10 1 9 10 M V30 11 1 1 10 M V30 END BOND M V30 END CTAB M END )CTAB"; std::unique_ptr m(MolBlockToMol(molblock, true, false, false)); REQUIRE(m); CHECK(m->getBondWithIdx(1)->getStereo() == Bond::BondStereo::STEREONONE); } } TEST_CASE("github #9020: implicit/explicit H labels depend on 3D conformers") { SECTION("as reported") { auto mb3 = R"CTAB( RDKit 3D 6 5 0 0 0 0 0 0 0 0999 V2000 -0.3698 0.0026 0.0028 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8980 -0.5748 -0.1191 O 0 0 0 0 0 0 0 0 0 0 0 0 -0.6741 -0.1194 1.0508 H 0 0 0 0 0 0 0 0 0 0 0 0 -0.3142 1.0665 -0.3155 H 0 0 0 0 0 0 0 0 0 0 0 0 -1.0830 -0.5246 -0.6430 H 0 0 0 0 0 0 0 0 0 0 0 0 1.5432 0.1497 0.0240 H 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 1 3 1 0 1 4 1 0 1 5 1 0 2 6 1 0 M END)CTAB"; { auto mol3 = v2::FileParsers::MolFromMolBlock(mb3); REQUIRE(mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 3); } { v2::FileParsers::MolFileParserParams params; params.removeHs = false; auto mol3 = v2::FileParsers::MolFromMolBlock(mb3, params); REQUIRE(mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 0); MolOps::removeHs(*mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 3); } } SECTION("With a quaternary N") { auto mb3 = R"CTAB( RDKit 3D 6 5 0 0 0 0 0 0 0 0999 V2000 -0.3698 0.0026 0.0028 N 0 0 0 0 0 0 0 0 0 0 0 0 0.8980 -0.5748 -0.1191 O 0 0 0 0 0 0 0 0 0 0 0 0 -0.6741 -0.1194 1.0508 H 0 0 0 0 0 0 0 0 0 0 0 0 -0.3142 1.0665 -0.3155 H 0 0 0 0 0 0 0 0 0 0 0 0 -1.0830 -0.5246 -0.6430 H 0 0 0 0 0 0 0 0 0 0 0 0 1.5432 0.1497 0.0240 H 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 1 3 1 0 1 4 1 0 1 5 1 0 2 6 1 0 M CHG 1 1 1 M END)CTAB"; { auto mol3 = v2::FileParsers::MolFromMolBlock(mb3); REQUIRE(mol3); // mol3->debugMol(std::cerr); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 3); CHECK(mol3->getAtomWithIdx(1)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(1)->getNumImplicitHs() == 1); } { v2::FileParsers::MolFileParserParams params; params.removeHs = false; auto mol3 = v2::FileParsers::MolFromMolBlock(mb3, params); REQUIRE(mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 0); MolOps::removeHs(*mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 3); CHECK(mol3->getAtomWithIdx(1)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(1)->getNumImplicitHs() == 1); } } SECTION("actually chiral") { auto mb3 = R"CTAB( RDKit 3D 6 5 0 0 0 0 0 0 0 0999 V2000 -0.3698 0.0026 0.0028 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8980 -0.5748 -0.1191 O 0 0 0 0 0 0 0 0 0 0 0 0 -0.6741 -0.1194 1.0508 H 0 0 0 0 0 0 0 0 0 0 0 0 -0.3142 1.0665 -0.3155 F 0 0 0 0 0 0 0 0 0 0 0 0 -1.0830 -0.5246 -0.6430 Cl 0 0 0 0 0 0 0 0 0 0 0 0 1.5432 0.1497 0.0240 H 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 1 3 1 0 1 4 1 0 1 5 1 0 2 6 1 0 M END)CTAB"; { auto mol3 = v2::FileParsers::MolFromMolBlock(mb3); REQUIRE(mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 1); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 0); } { v2::FileParsers::MolFileParserParams params; params.removeHs = false; auto mol3 = v2::FileParsers::MolFromMolBlock(mb3, params); REQUIRE(mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 0); MolOps::removeHs(*mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 1); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 0); } } SECTION("not chiral, one H") { auto mb3 = R"CTAB( RDKit 3D 6 5 0 0 0 0 0 0 0 0999 V2000 -0.3698 0.0026 0.0028 C 0 0 0 0 0 0 0 0 0 0 0 0 0.8980 -0.5748 -0.1191 O 0 0 0 0 0 0 0 0 0 0 0 0 -0.6741 -0.1194 1.0508 H 0 0 0 0 0 0 0 0 0 0 0 0 -0.3142 1.0665 -0.3155 F 0 0 0 0 0 0 0 0 0 0 0 0 -1.0830 -0.5246 -0.6430 F 0 0 0 0 0 0 0 0 0 0 0 0 1.5432 0.1497 0.0240 H 0 0 0 0 0 0 0 0 0 0 0 0 1 2 1 0 1 3 1 0 1 4 1 0 1 5 1 0 2 6 1 0 M END)CTAB"; { auto mol3 = v2::FileParsers::MolFromMolBlock(mb3); REQUIRE(mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 1); } { v2::FileParsers::MolFileParserParams params; params.removeHs = false; auto mol3 = v2::FileParsers::MolFromMolBlock(mb3, params); REQUIRE(mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 0); MolOps::removeHs(*mol3); CHECK(mol3->getAtomWithIdx(0)->getNumExplicitHs() == 0); CHECK(mol3->getAtomWithIdx(0)->getNumImplicitHs() == 1); } } } TEST_CASE("GitHub #9270: Segfault when calling MolToSmiles on submol") { SECTION("as reported") { UseLegacyStereoPerceptionFixture useLegacyFixture(false); auto mol = "CC1/C=C(/C)[O][Ir]23(<-[O]=1)([c]1ccccc1c1cncc[n]->21)[c]1ccccc1c1cncc[n]->31"_smiles; REQUIRE(mol); auto dblBond = mol->getBondWithIdx(2); REQUIRE(dblBond->getBondType() == Bond::BondType::DOUBLE); REQUIRE(dblBond->getStereo() == Bond::BondStereo::STEREOTRANS); REQUIRE(dblBond->getStereoAtoms() == std::vector{1, 4}); constexpr unsigned int radius = 3; constexpr unsigned int atomId = 7; auto env = findAtomEnvironmentOfRadiusN(*mol, radius, atomId); std::unique_ptr frag(Subgraphs::pathToSubmol(*mol, env)); REQUIRE(frag); REQUIRE_NOTHROW(MolToSmiles(*frag)); dblBond = frag->getBondWithIdx(2); REQUIRE(dblBond->getBondType() == Bond::BondType::DOUBLE); REQUIRE(dblBond->getStereo() == Bond::BondStereo::STEREOCIS); REQUIRE(dblBond->getStereoAtoms() == std::vector{1, 4}); } }