mirror of
https://github.com/rdkit/rdkit.git
synced 2026-06-04 21:54:27 +08:00
* Make kekulization deterministic * Add tautomer order-independence regression (python) * Adjust tautomer tests for deterministic kekulization * Update graphmol wedged-bond kekulization checks * SmilesParse: update aromatic bond index expectations * SmilesParse: refresh cxsmilesTest expected files * Depictor: update testDepictor expected MolBlocks * Depictor: update depictorCatch expectations * Depictor Wrap: update expected MolBlock for pyDepictor * MarvinParse: update testMrvToMol expected outputs * FileParsers: refresh testAtropisomers expected outputs * FileParsers: update tests for deterministic kekulization * MolDraw2D: refresh brittle bond assertions * RascalMCES: update expected cluster size * MinimalLib: make cffi wedging check order-independent * documentation fix * MinimalLib: update Kekulé bond table in aligned-coords test * Hoist duplicated lambdas to TEST_CASE scope * Remove unused originalWedges variable * Remove redundant bounds check; clarify wedge-end preference * Pre-sort allAtms by wedge-end + rank * Use mol.atomNeighbors() for neighbor iteration * Check inAllAtms before linear-scanning done * Drop redundant optsV/wedgedOptsV sorts * Remove unused Canon.h include * Add canonical parameter to Kekulize; skip ranking during sanitization * Test canonical re-kekulization preserves stereo across atom orderings * MinimalLib: update Kekulé bond orders in invertedWedges * Change Kekulize canonical default to false, expose in Python wrappers * keep rank order, push_back * Revert "RascalMCES: update expected cluster size" This reverts commita81bb39495. * docstring change * expose new flag to python wrapper * document changes in ReleaseNotes.md * revert minimallib test changes again * canonical = true defaults * Revert "revert minimallib test changes again" This reverts commit039e1d84da. * Reapply "RascalMCES: update expected cluster size" This reverts commit7b83a7a3e8. --------- Co-authored-by: greg landrum <greg.landrum@gmail.com>
622 lines
24 KiB
C++
622 lines
24 KiB
C++
//
|
|
// Copyright (C) 2002-2021 Collaboartive Drug Discovery 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/RDLog.h>
|
|
#include <GraphMol/RDKitBase.h>
|
|
#include <GraphMol/FileParsers/FileParsers.h>
|
|
#include <GraphMol/FileParsers/SequenceParsers.h>
|
|
#include <GraphMol/FileParsers/SequenceWriters.h>
|
|
#include <GraphMol/FileParsers/MolFileStereochem.h>
|
|
#include <GraphMol/Depictor/RDDepictor.h>
|
|
|
|
#include <GraphMol/ChemReactions/Reaction.h>
|
|
#include <GraphMol/ChemReactions/ReactionParser.h>
|
|
#include <GraphMol/SmilesParse/SmilesParse.h>
|
|
#include <GraphMol/SmilesParse/SmilesWrite.h>
|
|
#include <GraphMol/SmilesParse/SmilesWrite.h>
|
|
#include <GraphMol/Atropisomers.h>
|
|
#include <GraphMol/Chirality.h>
|
|
#include <GraphMol/test_fixtures.h>
|
|
|
|
#include <string>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <filesystem>
|
|
using namespace RDKit;
|
|
|
|
class MolAtropTest {
|
|
public:
|
|
public:
|
|
std::string testToRun;
|
|
bool generateExpectedFiles;
|
|
|
|
MolAtropTest() {
|
|
testToRun = "";
|
|
generateExpectedFiles = false;
|
|
}
|
|
|
|
public:
|
|
class MolTest {
|
|
public:
|
|
unsigned int atomCount;
|
|
unsigned int bondCount;
|
|
std::string fileName;
|
|
bool expectedResult;
|
|
|
|
MolTest(std::string fileNameInit, bool expectedResultInit,
|
|
int atomCountInit, int bondCountInit)
|
|
: atomCount(atomCountInit),
|
|
bondCount(bondCountInit),
|
|
fileName(fileNameInit),
|
|
expectedResult(expectedResultInit) {};
|
|
};
|
|
|
|
class KekuleTest {
|
|
public:
|
|
unsigned int atomCount;
|
|
unsigned int bondCount;
|
|
std::string smiles;
|
|
std::string expectedOutput;
|
|
bool expectedResult;
|
|
|
|
KekuleTest(std::string smilesInit, std::string nameInit,
|
|
bool expectedResultInit, int atomCountInit, int bondCountInit)
|
|
: atomCount(atomCountInit),
|
|
bondCount(bondCountInit),
|
|
smiles(smilesInit),
|
|
expectedOutput(nameInit),
|
|
expectedResult(expectedResultInit) {};
|
|
};
|
|
|
|
void generateNewExpectedFilesIfSoSpecified(std::string filename,
|
|
std::string dataToWrite) {
|
|
if (generateExpectedFiles) {
|
|
std::ofstream out;
|
|
out.open(filename);
|
|
out << dataToWrite;
|
|
}
|
|
}
|
|
|
|
std::string GetExpectedValue(std::string expectedFileName) {
|
|
std::stringstream expectedMolStr;
|
|
std::ifstream in;
|
|
in.open(expectedFileName);
|
|
expectedMolStr << in.rdbuf();
|
|
return expectedMolStr.str();
|
|
}
|
|
|
|
void testMolFiles(const MolTest *molFileTest) {
|
|
BOOST_LOG(rdInfoLog) << "testing mol files with atropisomers" << std::endl;
|
|
|
|
std::string rdbase = getenv("RDBASE");
|
|
std::string fName = rdbase +
|
|
"/Code/GraphMol/FileParsers/test_data/atropisomers/" +
|
|
molFileTest->fileName;
|
|
|
|
// first pass - no sanitiaton, but reapplyMolBlockWedging is ON
|
|
|
|
try {
|
|
std::unique_ptr<RWMol> mol(MolFileToMol(fName, false, false, false));
|
|
RDKit::Chirality::removeNonExplicit3DChirality(*mol);
|
|
|
|
TEST_ASSERT(mol != nullptr);
|
|
TEST_ASSERT(mol->getNumAtoms() == molFileTest->atomCount)
|
|
TEST_ASSERT(mol->getNumBonds() == molFileTest->bondCount)
|
|
|
|
{
|
|
MolOps::Kekulize(*mol);
|
|
RDKit::Chirality::reapplyMolBlockWedging(*mol);
|
|
std::string outMolStr = MolToMolBlock(*mol, true, 0, true, true);
|
|
|
|
generateNewExpectedFilesIfSoSpecified(fName + ".NEW.sdf", outMolStr);
|
|
|
|
TEST_ASSERT(GetExpectedValue(fName + ".expected.sdf") == outMolStr);
|
|
}
|
|
|
|
// 2nd pass without reapplying the mol block wedging -
|
|
// the itropisomers will be marked automatically
|
|
// SANITIZATION IS ON
|
|
|
|
mol = std::unique_ptr<RWMol>(MolFileToMol(fName, true, false, false));
|
|
RDKit::Chirality::removeNonExplicit3DChirality(*mol);
|
|
|
|
{
|
|
MolOps::Kekulize(*mol);
|
|
std::string outMolStr = MolToMolBlock(*mol, true, 0, true, true);
|
|
|
|
generateNewExpectedFilesIfSoSpecified(fName + ".NEW2.sdf", outMolStr);
|
|
|
|
TEST_ASSERT(GetExpectedValue(fName + ".expected2.sdf") == outMolStr);
|
|
}
|
|
|
|
// CXSMILES
|
|
|
|
mol = std::unique_ptr<RWMol>(MolFileToMol(fName, false, false, false));
|
|
RDKit::Chirality::removeNonExplicit3DChirality(*mol);
|
|
|
|
{
|
|
std::string expectedFileName = fName + ".expected.cxsmi";
|
|
SmilesWriteParams ps;
|
|
ps.canonical = false;
|
|
ps.doKekule = true;
|
|
|
|
unsigned int flags = SmilesWrite::CXSmilesFields::CX_COORDS |
|
|
SmilesWrite::CXSmilesFields::CX_MOLFILE_VALUES |
|
|
SmilesWrite::CXSmilesFields::CX_ATOM_PROPS |
|
|
SmilesWrite::CXSmilesFields::CX_BOND_CFG |
|
|
SmilesWrite::CXSmilesFields::CX_ENHANCEDSTEREO
|
|
//| SmilesWrite::CXSmilesFields::CX_ALL
|
|
;
|
|
|
|
std::string smilesOut =
|
|
MolToCXSmiles(*mol, ps, flags, RestoreBondDirOptionTrue);
|
|
|
|
generateNewExpectedFilesIfSoSpecified(fName + ".NEW.cxsmi", smilesOut);
|
|
|
|
TEST_ASSERT(GetExpectedValue(fName + ".expected.cxsmi") == smilesOut);
|
|
}
|
|
|
|
BOOST_LOG(rdInfoLog) << "done" << std::endl;
|
|
} catch (const std::exception &e) {
|
|
if (molFileTest->expectedResult != false) {
|
|
throw;
|
|
}
|
|
return;
|
|
}
|
|
TEST_ASSERT(molFileTest->expectedResult == true);
|
|
|
|
return;
|
|
}
|
|
|
|
void testAromAtropMolFile(const MolTest *molFileTest) {
|
|
BOOST_LOG(rdInfoLog) << "testing aromtizing mol files with atropisomers"
|
|
<< std::endl;
|
|
|
|
std::string rdbase = getenv("RDBASE");
|
|
std::string fName = rdbase +
|
|
"/Code/GraphMol/FileParsers/test_data/atropisomers/" +
|
|
molFileTest->fileName;
|
|
|
|
UseLegacyStereoPerceptionFixture useLegacy(false);
|
|
|
|
std::unique_ptr<RWMol> mol =
|
|
std::unique_ptr<RWMol>(MolFileToMol(fName, false, false, false));
|
|
|
|
TEST_ASSERT(mol->getNumAtoms() == molFileTest->atomCount)
|
|
TEST_ASSERT(mol->getNumBonds() == molFileTest->bondCount)
|
|
|
|
testAromAtropMol(mol.get(), molFileTest->expectedResult, fName);
|
|
}
|
|
|
|
void testAromAtropMol(RWMol *mol, bool expectedResult, std::string fName) {
|
|
try {
|
|
RDKit::Chirality::removeNonExplicit3DChirality(*mol);
|
|
|
|
auto sanitizeOps =
|
|
MolOps::SANITIZE_SETAROMATICITY + MolOps::SANITIZE_CLEANUPCHIRALITY +
|
|
MolOps::SANITIZE_SETHYBRIDIZATION + MolOps::SANITIZE_SETCONJUGATION;
|
|
|
|
unsigned int operationThatFailed = 0;
|
|
RDKit::MolOps::sanitizeMol(*mol, operationThatFailed, sanitizeOps);
|
|
for (const auto &atom : mol->atoms()) {
|
|
if (atom->getChiralTag() != Atom::CHI_UNSPECIFIED) {
|
|
BOOST_LOG(rdInfoLog) << "atom with stereo" << std::endl;
|
|
}
|
|
}
|
|
for (const auto &bond : mol->bonds()) {
|
|
if (bond->getBondDir() != Bond::BondDir::NONE) {
|
|
BOOST_LOG(rdInfoLog) << "bond with wedging" << std::endl;
|
|
}
|
|
}
|
|
{
|
|
SmilesWriteParams ps;
|
|
ps.canonical = true;
|
|
ps.doKekule = false;
|
|
ps.allBondsExplicit = false;
|
|
ps.allHsExplicit = false;
|
|
ps.doIsomericSmiles = true;
|
|
ps.doKekule = false;
|
|
ps.doRandom = false;
|
|
ps.rootedAtAtom = -1;
|
|
|
|
unsigned int flags = SmilesWrite::CXSmilesFields::CX_COORDS |
|
|
SmilesWrite::CXSmilesFields::CX_MOLFILE_VALUES |
|
|
SmilesWrite::CXSmilesFields::CX_ATOM_PROPS |
|
|
SmilesWrite::CXSmilesFields::CX_BOND_CFG |
|
|
SmilesWrite::CXSmilesFields::CX_ENHANCEDSTEREO;
|
|
|
|
std::string smilesOut =
|
|
MolToCXSmiles(*mol, ps, flags, RestoreBondDirOptionClear);
|
|
std::string expectedFileName = fName + ".expectedArom.cxsmi";
|
|
|
|
generateNewExpectedFilesIfSoSpecified(fName + ".NEWArom.cxsmi",
|
|
smilesOut);
|
|
|
|
TEST_ASSERT(GetExpectedValue(fName + ".expectedArom.cxsmi") ==
|
|
smilesOut);
|
|
}
|
|
|
|
BOOST_LOG(rdInfoLog) << "done" << std::endl;
|
|
} catch (const std::exception &e) {
|
|
if (expectedResult != false) {
|
|
throw;
|
|
}
|
|
return;
|
|
}
|
|
TEST_ASSERT(expectedResult == true);
|
|
}
|
|
|
|
void testKekuleWedgeError(RWMol *mol, std::string expectedSmi,
|
|
bool expectedResult, unsigned int expectedAtomCount,
|
|
unsigned int expectedBondCount) {
|
|
BOOST_LOG(rdInfoLog) << "testing aromatic atropisomers" << std::endl;
|
|
|
|
TEST_ASSERT(mol != nullptr);
|
|
TEST_ASSERT(mol->getNumAtoms() == expectedAtomCount)
|
|
TEST_ASSERT(mol->getNumBonds() == expectedBondCount)
|
|
|
|
try {
|
|
SmilesWriteParams ps;
|
|
ps.canonical = true;
|
|
ps.doKekule = true;
|
|
ps.allBondsExplicit = false;
|
|
ps.allHsExplicit = false;
|
|
ps.doIsomericSmiles = true;
|
|
ps.doRandom = false;
|
|
ps.rootedAtAtom = -1;
|
|
|
|
unsigned int flags = SmilesWrite::CXSmilesFields::CX_MOLFILE_VALUES |
|
|
SmilesWrite::CXSmilesFields::CX_ATOM_PROPS |
|
|
SmilesWrite::CXSmilesFields::CX_BOND_CFG |
|
|
SmilesWrite::CXSmilesFields::CX_ENHANCEDSTEREO;
|
|
|
|
std::string smilesOut =
|
|
MolToCXSmiles(*mol, ps, flags, RestoreBondDirOptionClear);
|
|
TEST_ASSERT(expectedSmi == smilesOut);
|
|
|
|
} catch (const std::exception &e) {
|
|
if (expectedResult != false) {
|
|
throw;
|
|
}
|
|
return;
|
|
}
|
|
TEST_ASSERT(expectedResult == true);
|
|
|
|
return;
|
|
}
|
|
|
|
void testKekuleWedgeErrorSmiles(const KekuleTest *kekuleTest) {
|
|
BOOST_LOG(rdInfoLog) << "testing aromatic atropisomers" << std::endl;
|
|
|
|
UseLegacyStereoPerceptionFixture useLegacy(false);
|
|
|
|
RDKit::SmilesParserParams params;
|
|
params.allowCXSMILES = true; // recognize and parse CXSMILES
|
|
params.debugParse = false; // disable debugging in the SMILES parser
|
|
params.parseName = true; // parse (and set) the molecule name as well
|
|
params.removeHs = false; // do not remove Hs
|
|
params.replacements = nullptr; // no SMILES "macros"
|
|
params.sanitize = true; // do not sanitize the molecule
|
|
params.skipCleanup =
|
|
false; // do not skip the final cleanup stage (for internal RDKit use)
|
|
params.strictCXSMILES =
|
|
false; // do not throw an exception if the CXSMILES parsing fails
|
|
|
|
std::unique_ptr<RWMol> mol =
|
|
std::unique_ptr<RWMol>(SmilesToMol(kekuleTest->smiles, params));
|
|
|
|
testKekuleWedgeError(mol.get(), kekuleTest->expectedOutput,
|
|
kekuleTest->expectedResult, kekuleTest->atomCount,
|
|
kekuleTest->bondCount);
|
|
}
|
|
|
|
void testKekuleWedgeErrorMol(const MolTest *kekuleTest) {
|
|
BOOST_LOG(rdInfoLog) << "testing aromatic atropisomers" << std::endl;
|
|
|
|
UseLegacyStereoPerceptionFixture useLegacy(false);
|
|
std::string rdbase = getenv("RDBASE");
|
|
std::string fName = rdbase +
|
|
"/Code/GraphMol/FileParsers/test_data/atropisomers/" +
|
|
kekuleTest->fileName;
|
|
|
|
std::unique_ptr<RWMol> mol(MolFileToMol(fName, true, false, false));
|
|
|
|
testKekuleWedgeError(mol.get(), GetExpectedValue(fName + ".expected.cxsmi"),
|
|
kekuleTest->expectedResult, kekuleTest->atomCount,
|
|
kekuleTest->bondCount);
|
|
}
|
|
|
|
void RunTests() {
|
|
// the molecule tests
|
|
|
|
if (testToRun == "" || testToRun == "sdfTests") {
|
|
std::list<MolTest> sdfTests{
|
|
MolTest("atropWedgeTest.sdf", true, 16, 16),
|
|
MolTest("AtropTest.sdf", true, 38, 41),
|
|
MolTest("AtropManyChiralsEnhanced.sdf", true, 20, 20),
|
|
MolTest("AtropManyChiralsEnhanced2.sdf", true, 20, 20),
|
|
MolTest("AtropManyChirals.sdf", true, 20, 20),
|
|
MolTest("BMS-986142.sdf", true, 42, 47),
|
|
MolTest("BMS-986142_3d_chiral.sdf", true, 72, 77),
|
|
MolTest("BMS-986142_3d.sdf", true, 72, 77),
|
|
MolTest("BMS-986142_atrop1.sdf", true, 42, 47),
|
|
MolTest("BMS-986142_atrop2.sdf", true, 42, 47),
|
|
MolTest("BMS-986142_atrop3.sdf", true, 42, 47),
|
|
MolTest("BMS-986142_atrop4.sdf", true, 42, 47),
|
|
MolTest("BMS-986142_atrop5.sdf", true, 42, 47),
|
|
MolTest("BMS-986142_atrop6.sdf", true, 42, 47),
|
|
MolTest("BMS-986142_atrop7.sdf", true, 42, 47),
|
|
MolTest("BMS-986142_atrop8.sdf", true, 42, 47),
|
|
MolTest("BMS-986142_atropBad2.sdf", true, 42, 47),
|
|
MolTest("JDQ443.sdf", true, 38, 44),
|
|
MolTest("JDQ443_3d.sdf", true, 66, 72),
|
|
MolTest("JDQ443_atrop1.sdf", true, 38, 44),
|
|
MolTest("JDQ443_atrop2.sdf", true, 38, 44),
|
|
MolTest("JDQ443_atrop3.sdf", true, 38, 44),
|
|
MolTest("JDQ443_atropBad1.sdf", true, 38, 44),
|
|
MolTest("RP-6306.sdf", true, 24, 26),
|
|
MolTest("RP-6306_atrop1.sdf", true, 24, 26),
|
|
MolTest("RP-6306_atrop2.sdf", true, 24, 26),
|
|
MolTest("RP-6306_atrop3.sdf", true, 24, 26),
|
|
MolTest("RP-6306_atrop4.sdf", true, 24, 26),
|
|
MolTest("RP-6306_atrop5.sdf", true, 24, 26),
|
|
MolTest("RP-6306_atropBad1.sdf", true, 24, 26),
|
|
MolTest("RP-6306_atropBad2.sdf", true, 24, 26),
|
|
// note the rp-6306_3d.sdf is backwards from the 2D versions
|
|
// the 2D version were based on images from drug hunter
|
|
// the 3D version came from PUBCHEM
|
|
MolTest("RP-6306_3d.sdf", true, 44, 46),
|
|
MolTest("Sotorasib.sdf", true, 41, 45),
|
|
MolTest("Sotorasib_atrop1.sdf", true, 41, 45),
|
|
MolTest("Sotorasib_atrop2.sdf", true, 41, 45),
|
|
MolTest("Sotorasib_atrop3.sdf", true, 41, 45),
|
|
MolTest("Sotorasib_atrop4.sdf", true, 41, 45),
|
|
MolTest("Sotorasib_atrop5.sdf", true, 41, 45),
|
|
MolTest("Sotorasib_atropBad1.sdf", true, 41, 45),
|
|
MolTest("Sotorasib_atropBad2.sdf", true, 41, 45),
|
|
// note the sotorasib_3d.sdf is backwards from the 2D versions
|
|
// the 2D version were based on images from drug hunter
|
|
// the 3D version came from PUBCHEM
|
|
MolTest("Sotorasib_3d.sdf", true, 71, 75),
|
|
MolTest("ZM374979.sdf", true, 45, 49),
|
|
MolTest("ZM374979_atrop1.sdf", true, 45, 49),
|
|
MolTest("ZM374979_atrop2.sdf", true, 45, 49),
|
|
MolTest("ZM374979_atrop3.sdf", true, 45, 49),
|
|
MolTest("ZM374979_atropBad1.sdf", true, 45, 49),
|
|
MolTest("Mrtx1719.sdf", true, 33, 37),
|
|
// note the Mrtx1719_3d.sdf is backwards from the 2D versions
|
|
// the 2D version were based on images from drug hunter
|
|
// the 3D version came from PUBCHEM
|
|
MolTest("Mrtx1719_3d.sdf", true, 51, 55),
|
|
MolTest("Mrtx1719_atrop1.sdf", true, 33, 37),
|
|
MolTest("Mrtx1719_atrop2.sdf", true, 33, 37),
|
|
MolTest("Mrtx1719_atrop3.sdf", true, 33, 37),
|
|
MolTest("Mrtx1719_atropBad1.sdf", true, 33, 37),
|
|
// macrocycles
|
|
MolTest("macrocycle-9-meta-wedge.sdf", true, 24, 26),
|
|
MolTest("macrocycle-9-ortho-wedge.sdf", true, 24, 26),
|
|
MolTest("macrocycle-8-meta-wedge.sdf", true, 23, 25),
|
|
MolTest("macrocycle-8-ortho-wedge.sdf", true, 23, 25),
|
|
MolTest("macrocycle-7-meta-wedge.sdf", true, 22, 24),
|
|
MolTest("macrocycle-7-ortho-wedge.sdf", true, 22, 24),
|
|
MolTest("macrocycle-6-meta-wedge.sdf", true, 21, 23),
|
|
MolTest("macrocycle-6-ortho-wedge.sdf", true, 21, 23),
|
|
MolTest("macrocycle-5-meta-wedge.sdf", true, 20, 22),
|
|
MolTest("macrocycle-5-ortho-wedge.sdf", true, 20, 22),
|
|
MolTest("macrocycle-9-meta-Cl-ortho-wedge.sdf", true, 25, 27),
|
|
MolTest("macrocycle-8-meta-Cl-ortho-wedge.sdf", true, 24, 26),
|
|
MolTest("macrocycle-7-meta-Cl-ortho-wedge.sdf", true, 23, 25),
|
|
MolTest("macrocycle-6-meta-Cl-ortho-wedge.sdf", true, 22, 24),
|
|
MolTest("macrocycle-5-meta-Cl-ortho-wedge.sdf", true, 21, 23),
|
|
MolTest("macrocycle-9-meta-broken-wedge.sdf", true, 24, 25),
|
|
MolTest("macrocycle-9-ortho-broken-wedge.sdf", true, 24, 25),
|
|
MolTest("macrocycle-8-meta-broken-wedge.sdf", true, 23, 24),
|
|
MolTest("macrocycle-8-ortho-broken-wedge.sdf", true, 23, 24),
|
|
MolTest("macrocycle-7-meta-broken-wedge.sdf", true, 22, 23),
|
|
MolTest("macrocycle-7-ortho-broken-wedge.sdf", true, 22, 23),
|
|
MolTest("macrocycle-6-meta-broken-wedge.sdf", true, 21, 22),
|
|
MolTest("macrocycle-6-ortho-broken-wedge.sdf", true, 21, 22),
|
|
MolTest("macrocycle-5-meta-broken-wedge.sdf", true, 20, 21),
|
|
MolTest("macrocycle-5-ortho-broken-wedge.sdf", true, 20, 21),
|
|
MolTest("macrocycle-9-meta-hash.sdf", true, 24, 26),
|
|
MolTest("macrocycle-9-ortho-hash.sdf", true, 24, 26),
|
|
MolTest("macrocycle-8-meta-hash.sdf", true, 23, 25),
|
|
MolTest("macrocycle-8-ortho-hash.sdf", true, 23, 25),
|
|
MolTest("macrocycle-7-meta-hash.sdf", true, 22, 24),
|
|
MolTest("macrocycle-7-ortho-hash.sdf", true, 22, 24),
|
|
MolTest("macrocycle-6-meta-hash.sdf", true, 21, 23),
|
|
MolTest("macrocycle-6-ortho-hash.sdf", true, 21, 23),
|
|
MolTest("macrocycle-5-meta-hash.sdf", true, 20, 22),
|
|
MolTest("macrocycle-5-ortho-hash.sdf", true, 20, 22),
|
|
MolTest("macrocycle-9-meta-Cl-ortho-hash.sdf", true, 25, 27),
|
|
MolTest("macrocycle-8-meta-Cl-ortho-hash.sdf", true, 24, 26),
|
|
MolTest("macrocycle-7-meta-Cl-ortho-hash.sdf", true, 23, 25),
|
|
MolTest("macrocycle-6-meta-Cl-ortho-hash.sdf", true, 22, 24),
|
|
MolTest("macrocycle-5-meta-Cl-ortho-hash.sdf", true, 21, 23),
|
|
MolTest("macrocycle-9-meta-broken-hash.sdf", true, 24, 25),
|
|
MolTest("macrocycle-9-ortho-broken-hash.sdf", true, 24, 25),
|
|
MolTest("macrocycle-8-meta-broken-hash.sdf", true, 23, 24),
|
|
MolTest("macrocycle-8-ortho-broken-hash.sdf", true, 23, 24),
|
|
MolTest("macrocycle-7-meta-broken-hash.sdf", true, 22, 23),
|
|
MolTest("macrocycle-7-ortho-broken-hash.sdf", true, 22, 23),
|
|
MolTest("macrocycle-6-meta-broken-hash.sdf", true, 21, 22),
|
|
MolTest("macrocycle-6-ortho-broken-hash.sdf", true, 21, 22),
|
|
MolTest("macrocycle-5-meta-broken-hash.sdf", true, 20, 21),
|
|
MolTest("macrocycle-5-ortho-broken-hash.sdf", true, 20, 21),
|
|
};
|
|
|
|
for (auto sdfTest : sdfTests) {
|
|
BOOST_LOG(rdInfoLog) << "Test: " << sdfTest.fileName << std::endl;
|
|
|
|
testMolFiles(&sdfTest);
|
|
}
|
|
}
|
|
|
|
if (testToRun == "" || testToRun == "AromAtropMol") {
|
|
std::list<MolTest> sdfTests{
|
|
MolTest("BMS-986142_atrop1.sdf", true, 42, 47),
|
|
MolTest("BMS-986142_3d_chiral.sdf", true, 72, 77),
|
|
MolTest("BMS-986142_3d.sdf", true, 72, 77),
|
|
};
|
|
|
|
for (auto sdfTest : sdfTests) {
|
|
BOOST_LOG(rdInfoLog) << "Test: " << sdfTest.fileName << std::endl;
|
|
|
|
testAromAtropMolFile(&sdfTest);
|
|
}
|
|
}
|
|
|
|
if (testToRun == "" || testToRun == "KekuleWedgeError") {
|
|
std::list<KekuleTest> kekuleTests{
|
|
KekuleTest(
|
|
"CC1C(C2C(Cl)=CC=CC=2C)=C(Cl)C=CC=1 |wU:3.3,(17.57,-4.18,;17.57,-5.18,;16.71,-5.67,;15.84,-5.18,;15.84,-4.18,;16.71,-3.67,;14.98,-3.67,;14.11,-4.18,;14.11,-5.18,;14.98,-5.67,;14.98,-6.67,;16.71,-6.67,;15.84,-7.18,;17.57,-7.18,;18.44,-6.67,;18.44,-5.67,)|",
|
|
"CC1=C(C2=C(C)C=CC=C2Cl)C(Cl)=CC=C1 |wU:2.11|", true, 16, 17),
|
|
KekuleTest("CC1C(C2C(Cl)=CC=CC=2C)=C(Cl)C=CC=1 |wD:3.3|",
|
|
"CC1=C(C2=C(C)C=CC=C2Cl)C(Cl)=CC=C1 |wU:2.11|", true, 16,
|
|
17),
|
|
KekuleTest(
|
|
"CC1C=CC(O)=C(C)C=1N1C2C(=NC=CC=2OC2C=NC=CC=2)C(C(=O)N)=C1N |wD:8.7|",
|
|
"CC1=C(O)C=CC(C)=C1N1C(N)=C(C(N)=O)C2=NC=CC(OC3=CC=CN=C3)=C21 |wD:9.10|",
|
|
true, 29, 32),
|
|
};
|
|
|
|
for (auto kekuleTest : kekuleTests) {
|
|
BOOST_LOG(rdInfoLog) << "Test: " << kekuleTest.smiles << std::endl;
|
|
|
|
testKekuleWedgeErrorSmiles(&kekuleTest);
|
|
}
|
|
}
|
|
|
|
if (testToRun == "" || testToRun == "KekuleWedgeErrorMol") {
|
|
std::list<MolTest> kekuleTests{
|
|
MolTest("atropWedgeError2.mol", true, 16, 17),
|
|
MolTest("atropWedgeError.mol", true, 29, 32),
|
|
};
|
|
|
|
for (auto kekuleTest : kekuleTests) {
|
|
BOOST_LOG(rdInfoLog) << "Test: " << kekuleTest.fileName << std::endl;
|
|
|
|
testKekuleWedgeErrorMol(&kekuleTest);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
void testLookForAtropisomersInSDdfFiles(std::string fileName,
|
|
unsigned int expectedHits,
|
|
unsigned int expectedMisses) {
|
|
BOOST_LOG(rdInfoLog) << "Looking for atropisomers in " << fileName
|
|
<< std::endl;
|
|
|
|
std::string rdbase = getenv("RDBASE");
|
|
std::string fName =
|
|
rdbase + "/Code/GraphMol/FileParsers/test_data/atropisomers/" + fileName;
|
|
|
|
std::ifstream in;
|
|
in.open(fName);
|
|
std::string line;
|
|
unsigned int foundCount = 0;
|
|
unsigned int notFoundCount = 0;
|
|
while (!in.eof()) {
|
|
std::string molBlock = "";
|
|
while (std::getline(in, line)) {
|
|
if (line.find("$$$$") != std::string::npos) {
|
|
break;
|
|
}
|
|
|
|
molBlock += line + "\n";
|
|
}
|
|
|
|
if (molBlock.length() < 10) {
|
|
continue; // try for another;
|
|
}
|
|
|
|
std::unique_ptr<RWMol> mol(MolBlockToMol(molBlock, false, false, false));
|
|
TEST_ASSERT(mol != nullptr);
|
|
|
|
auto hasAtropisomers = RDKit::Atropisomers::doesMolHaveAtropisomers(*mol);
|
|
|
|
if (hasAtropisomers) {
|
|
BOOST_LOG(rdInfoLog) << "Found atropisomers in " << fileName << std::endl;
|
|
foundCount++;
|
|
printf("Atropisomers- %d hits %d misses\r", foundCount, notFoundCount);
|
|
std::flush(std::cout);
|
|
std::ofstream out;
|
|
out.open(fName + "_" + std::to_string(foundCount) + ".sdf");
|
|
out << molBlock << std::endl;
|
|
} else {
|
|
notFoundCount++;
|
|
if (notFoundCount % 100 == 0) {
|
|
printf("Atropisomers- %d hits %d misses\r", foundCount,
|
|
notFoundCount);
|
|
std::flush(std::cout);
|
|
}
|
|
}
|
|
}
|
|
printf("\nFinal results:\nFound atropisomers in %s - %d hits %d misses\n",
|
|
fileName.c_str(), foundCount, notFoundCount);
|
|
|
|
TEST_ASSERT(foundCount == expectedHits);
|
|
TEST_ASSERT(notFoundCount == expectedMisses);
|
|
}
|
|
|
|
void testSulfinamideExamplesHaveNoAtropisomers() {
|
|
const std::vector<std::string> controlFiles = {
|
|
"sulfinamide-double-bond-O-R.mol",
|
|
"sulfinamide-single-bond-O-R.mol",
|
|
"sulfinamide-single-bond-O-S.mol",
|
|
};
|
|
std::string rdbase = getenv("RDBASE");
|
|
std::stringstream warningCapture;
|
|
rdWarningLog->SetTee(warningCapture);
|
|
for (const auto &file : controlFiles) {
|
|
auto fName = rdbase +
|
|
"/Code/GraphMol/FileParsers/test_data/atropisomers/" +
|
|
file;
|
|
BOOST_LOG(rdInfoLog) << "Validating absence of atropisomers in " << file
|
|
<< std::endl;
|
|
auto mol = std::unique_ptr<RWMol>(MolFileToMol(fName, true, false, false));
|
|
TEST_ASSERT(mol);
|
|
Atropisomers::detectAtropisomerChirality(*mol, &mol->getConformer());
|
|
TEST_ASSERT(!Atropisomers::doesMolHaveAtropisomers(*mol));
|
|
}
|
|
rdWarningLog->ClearTee();
|
|
TEST_ASSERT(warningCapture.str().empty());
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
(void)argc;
|
|
(void)argv;
|
|
RDLog::InitLogs();
|
|
boost::logging::enable_logs("rdApp.info");
|
|
BOOST_LOG(rdInfoLog) << " ---- Running with POSIX locale ----- " << std::endl;
|
|
|
|
MolAtropTest molAtropTest;
|
|
|
|
if (argc > 1) {
|
|
molAtropTest.testToRun = argv[1];
|
|
}
|
|
|
|
if (argc > 2 && std::string(argv[2]) == "generate") {
|
|
molAtropTest.generateExpectedFiles = true;
|
|
}
|
|
|
|
RDLog::InitLogs();
|
|
BOOST_LOG(rdInfoLog) << " ---- Running with POSIX locale ----- " << std::endl;
|
|
|
|
molAtropTest.RunTests();
|
|
testLookForAtropisomersInSDdfFiles("TestMultInSDF.sdf", 1, 4);
|
|
testSulfinamideExamplesHaveNoAtropisomers();
|
|
|
|
return 0;
|
|
}
|