Files
rdkit/Code/GraphMol/resMolSupplierTest.cpp
2021-07-01 05:18:56 +02:00

991 lines
34 KiB
C++

#include <RDGeneral/test.h>
#include <iostream>
#include <string>
#include <map>
#include <GraphMol/RDKitBase.h>
#include <GraphMol/Substruct/SubstructMatch.h>
#include <GraphMol/SmilesParse/SmilesParse.h>
#include <GraphMol/FileParsers/FileParsers.h>
#include <GraphMol/FileParsers/MolWriters.h>
#include <GraphMol/MolOps.h>
#include <GraphMol/Resonance.h>
using namespace RDKit;
void addFormalChargeIndices(const ROMol *mol,
std::map<unsigned int, int> &fcMap) {
for (ROMol::ConstAtomIterator it = mol->beginAtoms(); it != mol->endAtoms();
++it) {
int fc = (*it)->getFormalCharge();
unsigned int idx = (*it)->getIdx();
if (fc) {
if (fcMap.find(idx) == fcMap.end()) {
fcMap[idx] = fc;
} else {
fcMap[idx] += fc;
}
}
}
}
int getTotalFormalCharge(const ROMol *mol) {
int totalFormalCharge = 0;
for (ROMol::ConstAtomIterator it = mol->beginAtoms(); it != mol->endAtoms();
++it) {
totalFormalCharge += (*it)->getFormalCharge();
}
return totalFormalCharge;
}
void cmpFormalChargeBondOrder(const ROMol *mol1, const ROMol *mol2) {
TEST_ASSERT(mol1->getNumAtoms() == mol2->getNumAtoms());
TEST_ASSERT(mol1->getNumBonds() == mol2->getNumBonds());
for (unsigned int i = 0; i < mol1->getNumAtoms(); ++i) {
TEST_ASSERT(mol1->getAtomWithIdx(i)->getFormalCharge() ==
mol2->getAtomWithIdx(i)->getFormalCharge());
}
for (unsigned int i = 0; i < mol1->getNumBonds(); ++i) {
TEST_ASSERT(mol1->getBondWithIdx(i)->getBondType() ==
mol2->getBondWithIdx(i)->getBondType());
}
}
void testBaseFunctionality() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testBaseFunctionality" << std::endl;
ResonanceMolSupplier *resMolSuppl;
RWMol *mol;
mol = SmilesToMol("CC");
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT((resMolSuppl->getAtomConjGrpIdx(0) == -1) &&
(resMolSuppl->getAtomConjGrpIdx(1) == -1))
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->getNumConjGrps() == 0);
TEST_ASSERT(resMolSuppl->length() == 1);
TEST_ASSERT(resMolSuppl->getNumConjGrps() == 0);
delete resMolSuppl;
delete mol;
mol = SmilesToMol("NC(=[NH2+])c1ccc(cc1)C(=O)[O-]");
int totalFormalCharge = getTotalFormalCharge(mol);
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(!resMolSuppl->getIsEnumerated());
TEST_ASSERT(resMolSuppl->length() == 4);
TEST_ASSERT(resMolSuppl->getIsEnumerated());
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(!resMolSuppl->getIsEnumerated());
resMolSuppl->enumerate();
TEST_ASSERT(resMolSuppl->getIsEnumerated());
auto *smol0 = (*resMolSuppl)[0];
auto *smol1 = (*resMolSuppl)[1];
TEST_ASSERT((smol0->getBondBetweenAtoms(0, 1)->getBondType() !=
smol1->getBondBetweenAtoms(0, 1)->getBondType()) ||
(smol0->getBondBetweenAtoms(9, 10)->getBondType() !=
smol1->getBondBetweenAtoms(9, 10)->getBondType()));
delete smol0;
delete smol1;
delete resMolSuppl;
resMolSuppl =
new ResonanceMolSupplier((ROMol &)*mol, ResonanceMolSupplier::KEKULE_ALL);
TEST_ASSERT(resMolSuppl->length() == 8);
std::set<Bond::BondType> bondTypeSet;
smol0 = (*resMolSuppl)[0];
smol1 = (*resMolSuppl)[1];
// check that we actually have two alternate Kekule structures
bondTypeSet.insert(smol0->getBondBetweenAtoms(3, 4)->getBondType());
bondTypeSet.insert(smol1->getBondBetweenAtoms(3, 4)->getBondType());
TEST_ASSERT(bondTypeSet.size() == 2);
bondTypeSet.clear();
delete smol0;
delete smol1;
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_CATIONS |
ResonanceMolSupplier::UNCONSTRAINED_ANIONS);
TEST_ASSERT(resMolSuppl->length() == 32);
for (unsigned int i = 0; i < resMolSuppl->length(); ++i) {
ROMol *resMol = (*resMolSuppl)[i];
TEST_ASSERT(getTotalFormalCharge(resMol) == totalFormalCharge);
delete resMol;
}
while (!resMolSuppl->atEnd()) {
ROMol *resMol = resMolSuppl->next();
TEST_ASSERT(getTotalFormalCharge(resMol) == totalFormalCharge);
delete resMol;
}
resMolSuppl->reset();
smol0 = (*resMolSuppl)[0];
smol1 = resMolSuppl->next();
cmpFormalChargeBondOrder(smol0, smol1);
delete smol0;
delete smol1;
resMolSuppl->moveTo(12);
smol0 = (*resMolSuppl)[12];
smol1 = resMolSuppl->next();
cmpFormalChargeBondOrder(smol0, smol1);
delete resMolSuppl;
delete smol0;
delete smol1;
resMolSuppl =
new ResonanceMolSupplier((ROMol &)*mol,
ResonanceMolSupplier::ALLOW_INCOMPLETE_OCTETS |
ResonanceMolSupplier::UNCONSTRAINED_CATIONS |
ResonanceMolSupplier::UNCONSTRAINED_ANIONS,
10);
TEST_ASSERT(resMolSuppl->length() == 10);
delete resMolSuppl;
resMolSuppl =
new ResonanceMolSupplier((ROMol &)*mol,
ResonanceMolSupplier::ALLOW_INCOMPLETE_OCTETS |
ResonanceMolSupplier::UNCONSTRAINED_CATIONS |
ResonanceMolSupplier::UNCONSTRAINED_ANIONS,
0);
TEST_ASSERT(resMolSuppl->length() == 0);
delete resMolSuppl;
delete mol;
mol = SmilesToMol("CC(C)C(C(=O)OC(C#N)c1cccc(Oc2ccccc2)c1)c3ccc(OC(F)F)cc3");
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol,
ResonanceMolSupplier::KEKULE_ALL, 3);
TEST_ASSERT(!resMolSuppl->getIsEnumerated());
TEST_ASSERT(resMolSuppl->length() == 3);
TEST_ASSERT(resMolSuppl->getIsEnumerated());
delete resMolSuppl;
delete mol;
}
void testBenzylCation() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testBenzylCation" << std::endl;
RWMol *mol = SmilesToMol("[CH2+]c1ccccc1");
ResonanceMolSupplier *resMolSuppl;
std::map<unsigned int, int> fcMap;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->length() == 4);
while (!resMolSuppl->atEnd()) {
ROMol *resMol = resMolSuppl->next();
addFormalChargeIndices(resMol, fcMap);
delete resMol;
}
unsigned int indices[] = {0, 2, 4, 6};
for (unsigned int &idx : indices) {
TEST_ASSERT(fcMap.find(idx) != fcMap.end());
TEST_ASSERT(fcMap[idx] == 1);
}
delete resMolSuppl;
delete mol;
}
void testBenzylAnion() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testBenzylAnion" << std::endl;
RWMol *mol = SmilesToMol("[CH2-]c1ccccc1");
ResonanceMolSupplier *resMolSuppl;
std::map<unsigned int, int> fcMap;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->length() == 4);
while (!resMolSuppl->atEnd()) {
ROMol *resMol = resMolSuppl->next();
addFormalChargeIndices(resMol, fcMap);
delete resMol;
}
unsigned int indices[] = {0, 2, 4, 6};
for (unsigned int &idx : indices) {
TEST_ASSERT(fcMap.find(idx) != fcMap.end());
TEST_ASSERT(fcMap[idx] == -1);
}
delete resMolSuppl;
delete mol;
}
void testButadiene() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testButadiene" << std::endl;
RWMol *mol = SmilesToMol("C=CC=C");
ResonanceMolSupplier *resMolSuppl;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->length() == 1);
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_CATIONS |
ResonanceMolSupplier::UNCONSTRAINED_ANIONS);
TEST_ASSERT(resMolSuppl->length() == 3);
std::map<unsigned int, int> fcMap;
while (!resMolSuppl->atEnd()) {
ROMol *resMol = resMolSuppl->next();
addFormalChargeIndices(resMol, fcMap);
delete resMol;
}
unsigned int indices[] = {0, 3};
for (unsigned int &idx : indices) {
TEST_ASSERT(fcMap.find(idx) != fcMap.end());
TEST_ASSERT(fcMap[idx] == 0);
}
delete resMolSuppl;
delete mol;
}
void testZwitterion() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testZwitterion" << std::endl;
RWMol *mol = SmilesToMol("NC=CC=CC=O");
ResonanceMolSupplier *resMolSuppl;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->length() == 1);
ROMol *mol0 = (*resMolSuppl)[0];
TEST_ASSERT(mol0->getAtomWithIdx(0)->getFormalCharge() == 0);
TEST_ASSERT(mol0->getAtomWithIdx(6)->getFormalCharge() == 0);
delete mol0;
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION);
TEST_ASSERT(resMolSuppl->length() == 2);
mol0 = (*resMolSuppl)[0];
ROMol *mol1 = (*resMolSuppl)[1];
TEST_ASSERT(mol1->getAtomWithIdx(0)->getFormalCharge() == 1);
TEST_ASSERT(mol1->getAtomWithIdx(6)->getFormalCharge() == -1);
delete mol0;
delete mol1;
delete resMolSuppl;
delete mol;
}
void testChargeMigration() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testChargeMigration" << std::endl;
RWMol *mol = SmilesToMol("C=CC=CC=C[CH2+]");
ResonanceMolSupplier *resMolSuppl;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->length() == 4);
std::map<unsigned int, int> fcMap;
while (!resMolSuppl->atEnd()) {
ROMol *resMol = resMolSuppl->next();
addFormalChargeIndices(resMol, fcMap);
delete resMol;
}
unsigned int indices[] = {0, 2, 4, 6};
for (unsigned int &idx : indices) {
TEST_ASSERT(fcMap.find(idx) != fcMap.end());
TEST_ASSERT(fcMap[idx] == 1);
}
delete resMolSuppl;
delete mol;
}
void testChargeSeparation1() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testChargeSeparation1" << std::endl;
RWMol *mol = SmilesToMol("[NH2+]=C1C=CC(C=C1)=CN=CN");
ResonanceMolSupplier *resMolSuppl;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->length() == 3);
std::map<unsigned int, int> fcMap;
for (unsigned int i = 0; i < 3; ++i) {
ROMol *resMol = (*resMolSuppl)[i];
addFormalChargeIndices(resMol, fcMap);
delete resMol;
}
unsigned int indices[] = {0, 8, 10};
for (unsigned int &idx : indices) {
TEST_ASSERT(fcMap.find(idx) != fcMap.end());
TEST_ASSERT(fcMap[idx] == 1);
}
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION);
TEST_ASSERT(resMolSuppl->length() == 4);
resMolSuppl->moveTo(3);
ROMol *resMol = resMolSuppl->next();
TEST_ASSERT(resMol->getAtomWithIdx(0)->getFormalCharge() == 1);
TEST_ASSERT(resMol->getAtomWithIdx(10)->getFormalCharge() == 1);
TEST_ASSERT(resMol->getAtomWithIdx(8)->getFormalCharge() == -1);
delete resMol;
delete resMolSuppl;
delete mol;
}
void testChargeSeparation2() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testChargeSeparation2" << std::endl;
RWMol *mol = SmilesToMol("NC(C(=O)[O-])=CC=CC=O");
ResonanceMolSupplier *resMolSuppl;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->length() == 2);
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION);
TEST_ASSERT(resMolSuppl->length() == 4);
// less charge separation in the first 2,
// more charge separation in the last 6
// the last 4 feature carbanions
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_ANIONS);
TEST_ASSERT(resMolSuppl->length() == 8);
std::map<unsigned int, int> fcMap;
for (unsigned int i = 0; i < 2; ++i) {
ROMol *resMol = (*resMolSuppl)[i];
addFormalChargeIndices(resMol, fcMap);
TEST_ASSERT(fcMap.size() == 1);
fcMap.clear();
delete resMol;
}
for (unsigned int i = 2; i < 8; ++i) {
ROMol *resMol = (*resMolSuppl)[i];
addFormalChargeIndices(resMol, fcMap);
TEST_ASSERT(fcMap.size() == 3);
fcMap.clear();
delete resMol;
}
for (unsigned int i = 0; i < 4; ++i) {
const ROMol *resMol = (*resMolSuppl)[i];
bool haveCarbanion = false;
for (ROMol::ConstAtomIterator it = resMol->beginAtoms();
it != resMol->endAtoms(); ++it) {
if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) {
haveCarbanion = true;
}
}
TEST_ASSERT(!haveCarbanion);
delete resMol;
}
for (unsigned int i = 4; i < 8; ++i) {
const ROMol *resMol = (*resMolSuppl)[i];
bool haveCarbanion = false;
for (ROMol::ConstAtomIterator it = resMol->beginAtoms();
it != resMol->endAtoms(); ++it) {
if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) {
haveCarbanion = true;
}
}
TEST_ASSERT(haveCarbanion);
delete resMol;
}
delete resMolSuppl;
delete mol;
}
void testMultipleConjGroups1() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testMultipleConjGroups1" << std::endl;
RWMol *mol = SmilesToMol("NC(C(=O)[O-])=CC=C(CCC(=O)[O-])C=O");
ResonanceMolSupplier *resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION);
TEST_ASSERT(resMolSuppl->length() == 8);
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_ANIONS);
TEST_ASSERT(resMolSuppl->length() == 16);
for (unsigned int i = 0; i < 8; ++i) {
const ROMol *resMol = (*resMolSuppl)[i];
bool haveCarbanion = false;
for (ROMol::ConstAtomIterator it = resMol->beginAtoms();
it != resMol->endAtoms(); ++it) {
if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) {
haveCarbanion = true;
}
}
TEST_ASSERT(!haveCarbanion);
delete resMol;
}
for (unsigned int i = 8; i < 16; ++i) {
const ROMol *resMol = (*resMolSuppl)[i];
bool haveCarbanion = false;
for (ROMol::ConstAtomIterator it = resMol->beginAtoms();
it != resMol->endAtoms(); ++it) {
if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) {
haveCarbanion = true;
}
}
TEST_ASSERT(haveCarbanion);
delete resMol;
}
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_ANIONS, 8);
TEST_ASSERT(resMolSuppl->length() == 8);
for (unsigned int i = 0; i < 8; ++i) {
const ROMol *resMol = (*resMolSuppl)[i];
bool haveCarbanion = false;
for (ROMol::ConstAtomIterator it = resMol->beginAtoms();
it != resMol->endAtoms(); ++it) {
if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) {
haveCarbanion = true;
}
}
TEST_ASSERT(!haveCarbanion);
delete resMol;
}
delete resMolSuppl;
delete mol;
}
void testMultipleConjGroups2() {
// if breadth-first resonance structure enumeration works properly
// all N(2-) should be at the end of the supplier
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testMultipleConjGroups2" << std::endl;
RWMol *mol = SmilesToMol("[N-]=[N+]=NCN=[N+]=[N-]");
ResonanceMolSupplier *resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION);
TEST_ASSERT(resMolSuppl->length() == 9);
bool haveFirstDoubleNeg = false;
for (unsigned int i = 0; i < resMolSuppl->length(); ++i) {
const ROMol *resMol = (*resMolSuppl)[i];
bool haveDoubleNeg = false;
for (ROMol::ConstAtomIterator it = resMol->beginAtoms();
it != resMol->endAtoms(); ++it) {
if (((*it)->getAtomicNum() == 7) && ((*it)->getFormalCharge() == -2)) {
haveDoubleNeg = true;
}
}
if (!haveFirstDoubleNeg && haveDoubleNeg) {
haveFirstDoubleNeg = true;
}
TEST_ASSERT(!haveFirstDoubleNeg || (haveFirstDoubleNeg && haveDoubleNeg));
delete resMol;
}
delete resMolSuppl;
delete mol;
}
void testDimethylMalonate() {
// carbanion should be last
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testDimethylMalonate" << std::endl;
RWMol *mol = SmilesToMol("COC(=O)[CH-]C(=O)OC");
ResonanceMolSupplier *resMolSuppl;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->length() == 3);
for (unsigned int i = 0; i < 3; ++i) {
const ROMol *resMol = (*resMolSuppl)[i];
bool haveCarbanion = false;
for (ROMol::ConstAtomIterator it = resMol->beginAtoms();
it != resMol->endAtoms(); ++it) {
if (((*it)->getAtomicNum() == 6) && ((*it)->getFormalCharge() == -1)) {
haveCarbanion = true;
}
}
TEST_ASSERT(((i < 2) && !haveCarbanion) || ((i == 2) && haveCarbanion));
delete resMol;
}
delete resMolSuppl;
delete mol;
}
void testMethylAcetate() {
// cation on oxygen should appear only when UNCONSTRAINED_CATIONS is set
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testMethylAcetate" << std::endl;
RWMol *mol = SmilesToMol("CC(=O)OC");
ResonanceMolSupplier *resMolSuppl;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->length() == 1);
const ROMol *resMol = (*resMolSuppl)[0];
bool haveCation = false;
for (ROMol::ConstAtomIterator it = resMol->beginAtoms();
it != resMol->endAtoms(); ++it) {
if (((*it)->getAtomicNum() == 8) && ((*it)->getFormalCharge() == 1)) {
haveCation = true;
}
}
delete resMol;
TEST_ASSERT(!haveCation);
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
(ROMol &)*mol, ResonanceMolSupplier::UNCONSTRAINED_CATIONS);
TEST_ASSERT(resMolSuppl->length() == 2);
for (unsigned int i = 0; i < 2; ++i) {
const ROMol *resMol = (*resMolSuppl)[i];
bool haveCation = false;
for (ROMol::ConstAtomIterator it = resMol->beginAtoms();
it != resMol->endAtoms(); ++it) {
if (((*it)->getAtomicNum() == 8) && ((*it)->getFormalCharge() == 1)) {
haveCation = true;
}
}
TEST_ASSERT(((i == 0) && !haveCation) || ((i == 1) && haveCation));
delete resMol;
}
delete resMolSuppl;
delete mol;
}
void testPyranium2_6dicarboxylate() {
// when there is no alternative cation on elements right of N
// should be accepted even though the total formal charge is not
// positive
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testPyranium2_6dicarboxylate" << std::endl;
RWMol *mol = SmilesToMol("[o+]1c(C(=O)[O-])cccc1C(=O)[O-]");
ResonanceMolSupplier *resMolSuppl;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
TEST_ASSERT(resMolSuppl->length() == 4);
for (unsigned int i = 0; i < 4; ++i) {
const ROMol *resMol = (*resMolSuppl)[i];
TEST_ASSERT(resMol->getAtomWithIdx(0)->getFormalCharge() == 1);
delete resMol;
}
delete resMolSuppl;
delete mol;
}
void testSubstructMatchAcetate() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testSubstructMatchAcetate" << std::endl;
RWMol *mol = SmilesToMol("CC(=O)[O-]");
RWMol *query = SmartsToMol("C(=O)[O-]");
ResonanceMolSupplier *resMolSuppl;
unsigned int n;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
std::vector<MatchVectType> matchVect;
n = SubstructMatch(*mol, *query, matchVect, false, true, false, false);
TEST_ASSERT(n == 1);
matchVect.clear();
n = SubstructMatch(*resMolSuppl, *query, matchVect, false, true, false,
false);
TEST_ASSERT(n == 2);
delete mol;
delete query;
delete resMolSuppl;
}
void testSubstructMatchDMAP() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testSubstructMatchDMAP" << std::endl;
RWMol *mol = SmilesToMol("C(C)Nc1cc[nH+]cc1");
RWMol *query = SmartsToMol("[#7+]");
ResonanceMolSupplier *resMolSuppl;
unsigned int n;
MatchVectType p;
resMolSuppl = new ResonanceMolSupplier((ROMol &)*mol);
std::vector<MatchVectType> matchVect;
n = SubstructMatch(*mol, *query, matchVect, false, true, false, false);
TEST_ASSERT((n == 1) && (matchVect.size() == 1));
p = matchVect[0];
TEST_ASSERT((p.size() == 1) && (p[0].second == 6));
matchVect.clear();
n = SubstructMatch(*resMolSuppl, *query, matchVect, false, true, false,
false);
TEST_ASSERT((n == 2) && (matchVect.size() == 2));
std::vector<unsigned int> v;
p = matchVect[0];
TEST_ASSERT(p.size() == 1);
v.push_back(p[0].second);
p = matchVect[1];
TEST_ASSERT(p.size() == 1);
v.push_back(p[0].second);
std::sort(v.begin(), v.end());
TEST_ASSERT(v[0] == 2);
TEST_ASSERT(v[1] == 6);
delete mol;
delete query;
delete resMolSuppl;
}
void setResidueFormalCharge(RWMol *mol, std::vector<RWMol *> &res, int fc) {
for (std::vector<RWMol *>::const_iterator it = res.begin(); it != res.end();
++it) {
std::vector<MatchVectType> matchVect;
SubstructMatch(*mol, *(*it), matchVect);
for (std::vector<MatchVectType>::const_iterator it = matchVect.begin();
it != matchVect.end(); ++it) {
mol->getAtomWithIdx((*it).back().second)->setFormalCharge(fc);
}
}
}
void getBtVectVect(ResonanceMolSupplier *resMolSuppl,
std::vector<std::vector<unsigned int>> &btVect2) {
while (!resMolSuppl->atEnd()) {
ROMol *resMol = resMolSuppl->next();
std::vector<unsigned int> bt;
bt.reserve(resMol->getNumBonds());
for (ROMol::BondIterator bi = resMol->beginBonds();
bi != resMol->endBonds(); ++bi) {
bt.push_back(static_cast<unsigned int>((*bi)->getBondTypeAsDouble()));
}
btVect2.push_back(bt);
delete resMol;
}
for (unsigned int i = 0; i < btVect2.size(); ++i) {
bool same = true;
for (unsigned int j = 0; same && (j < btVect2[i].size()); ++j) {
if (!i) {
continue;
}
if (same) {
same = (btVect2[i][j] == btVect2[i - 1][j]);
}
}
if (i) {
TEST_ASSERT(!same);
}
}
}
void testCrambin() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testCrambin" << std::endl;
std::string pathName = getenv("RDBASE");
pathName += "/Code/GraphMol/FileParsers/test_data/1CRN.pdb";
RWMol *crambin = PDBFileToMol(pathName);
TEST_ASSERT(crambin);
RWMol *query;
unsigned int n;
std::vector<RWMol *> res;
// protonate NH2
query = SmartsToMol("[Nh2][Ch;Ch2]");
TEST_ASSERT(query);
res.push_back(query);
// protonate Arg
query = SmartsToMol("[Nh][C]([Nh2])=[Nh]");
TEST_ASSERT(query);
res.push_back(query);
setResidueFormalCharge(crambin, res, 1);
for (std::vector<RWMol *>::const_iterator it = res.begin(); it != res.end();
++it) {
delete *it;
}
res.clear();
// deprotonate COOH
query = SmartsToMol("C(=O)[Oh]");
TEST_ASSERT(query);
res.push_back(query);
setResidueFormalCharge(crambin, res, -1);
for (std::vector<RWMol *>::const_iterator it = res.begin(); it != res.end();
++it) {
delete *it;
}
auto *resMolSupplST = new ResonanceMolSupplier((ROMol &)*crambin);
TEST_ASSERT(resMolSupplST);
// crambin has 2 Arg (3 resonance structures each); 1 Asp, 1 Glu
// and 1 terminal COO- (2 resonance structures each)
// so possible resonance structures are 3^2 * 2^3 = 72
TEST_ASSERT(resMolSupplST->length() == 72);
TEST_ASSERT(resMolSupplST->getNumConjGrps() == 56);
RWMol *carboxylateQuery = SmartsToMol("C(=O)[O-]");
RWMol *guanidiniumQuery = SmartsToMol("NC(=[NH2+])N");
std::vector<MatchVectType> matchVect;
n = SubstructMatch(*crambin, *carboxylateQuery, matchVect, false, true, false,
false, 1000);
TEST_ASSERT(n == 3);
n = SubstructMatch(*crambin, *carboxylateQuery, matchVect, true, true, false,
false, 1000);
TEST_ASSERT(n == 3);
n = SubstructMatch(*crambin, *guanidiniumQuery, matchVect, false, true, false,
false, 1000);
TEST_ASSERT(n == 0);
n = SubstructMatch(*crambin, *guanidiniumQuery, matchVect, true, true, false,
false, 1000);
TEST_ASSERT(n == 0);
n = SubstructMatch(*resMolSupplST, *carboxylateQuery, matchVect, false, true,
false, false, 1000);
TEST_ASSERT(n == 6);
n = SubstructMatch(*resMolSupplST, *carboxylateQuery, matchVect, true, true,
false, false, 1000);
TEST_ASSERT(n == 3);
n = SubstructMatch(*resMolSupplST, *guanidiniumQuery, matchVect, false, true,
false, false, 1000);
TEST_ASSERT(n == 8);
n = SubstructMatch(*resMolSupplST, *guanidiniumQuery, matchVect, true, true,
false, false, 1000);
TEST_ASSERT(n == 2);
#ifdef RDK_TEST_MULTITHREADED
std::vector<std::vector<unsigned int>> btVect2ST;
getBtVectVect(resMolSupplST, btVect2ST);
auto *resMolSupplMT = new ResonanceMolSupplier((ROMol &)*crambin, 0, 1000);
TEST_ASSERT(resMolSupplMT);
resMolSupplMT->setNumThreads(0);
TEST_ASSERT(resMolSupplST->length() == resMolSupplMT->length());
std::vector<std::vector<unsigned int>> btVect2MT;
getBtVectVect(resMolSupplMT, btVect2MT);
TEST_ASSERT(btVect2ST.size() == btVect2MT.size());
for (unsigned int i = 0; i < btVect2ST.size(); ++i) {
for (unsigned int j = 0; j < btVect2ST[i].size(); ++j) {
TEST_ASSERT(btVect2ST[i][j] == btVect2MT[i][j]);
}
}
ResonanceMolSupplier *ptr[2] = {resMolSupplST, resMolSupplMT};
for (auto &i : ptr) {
n = SubstructMatch(*i, *carboxylateQuery, matchVect, false, true, false,
false, 1000, 0);
TEST_ASSERT(n == 6);
n = SubstructMatch(*i, *carboxylateQuery, matchVect, true, true, false,
false, 1000, 0);
TEST_ASSERT(n == 3);
n = SubstructMatch(*i, *guanidiniumQuery, matchVect, false, true, false,
false, 1000, 0);
TEST_ASSERT(n == 8);
n = SubstructMatch(*i, *guanidiniumQuery, matchVect, true, true, false,
false, 1000, 0);
TEST_ASSERT(n == 2);
}
delete resMolSupplMT;
#endif
delete resMolSupplST;
delete carboxylateQuery;
delete guanidiniumQuery;
delete crambin;
}
void testConjGrpPerception() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testConjGrpPerception" << std::endl;
RWMol *mol1 = MolBlockToMol(R"SDF(
RDKit 2D
14 15 0 0 0 0 0 0 0 0999 V2000
3.7539 -1.2744 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.4317 -0.5660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1.1571 -1.3568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.1651 -0.6484 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.4397 -1.4393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.3921 -2.9385 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
-2.7619 -0.7309 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-2.8095 0.7684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-4.1316 1.4768 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
-1.5349 1.5592 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.2127 0.8508 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1.0619 1.6417 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
2.3841 0.9333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6587 1.7241 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0
2 3 4 0
3 4 4 0
4 5 4 0
5 6 1 0
5 7 4 0
7 8 4 0
8 9 1 0
8 10 4 0
10 11 4 0
11 12 4 0
12 13 4 0
13 14 1 0
13 2 4 0
11 4 4 0
M END
)SDF");
RWMol *mol2 = MolBlockToMol(R"SDF(
RDKit 2D
14 15 0 0 0 0 0 0 0 0999 V2000
1.0619 -1.6417 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0
-0.2127 -0.8508 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.5349 -1.5592 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-2.8095 -0.7684 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-2.7619 0.7309 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-1.4397 1.4393 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-0.1651 0.6484 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1.1571 1.3568 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.4317 0.5660 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.7539 1.2744 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.3841 -0.9333 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6587 -1.7241 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
-4.1316 -1.4768 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
-1.3921 2.9385 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
1 2 4 0
3 4 4 0
4 5 4 0
5 6 4 0
2 3 4 0
2 7 4 0
7 8 4 0
8 9 4 0
9 10 1 0
9 11 4 0
11 12 1 0
11 1 4 0
6 7 4 0
4 13 1 0
6 14 1 0
M END
)SDF");
auto *resMolSuppl1 = new ResonanceMolSupplier(
static_cast<ROMol &>(*mol1), ResonanceMolSupplier::KEKULE_ALL);
TEST_ASSERT(resMolSuppl1->length() == 3);
auto *resMolSuppl2 = new ResonanceMolSupplier(
static_cast<ROMol &>(*mol2), ResonanceMolSupplier::KEKULE_ALL);
TEST_ASSERT(resMolSuppl2->length() == 3);
delete resMolSuppl1;
delete resMolSuppl2;
delete mol1;
delete mol2;
}
void testGitHub1166() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testGitHub1166" << std::endl;
RWMol *mol = SmilesToMol("NC(=[NH2+])c1ccc(cc1)C(=O)[O-]");
auto *resMolSuppl = new ResonanceMolSupplier(
*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION |
ResonanceMolSupplier::KEKULE_ALL);
TEST_ASSERT(resMolSuppl->length() == 8);
// check that formal charges on odd indices are in the same position
// as on even indices
for (unsigned int i = 0; i < resMolSuppl->length(); i += 2) {
auto *smol0 = (*resMolSuppl)[i];
auto *smol1 = (*resMolSuppl)[i + 1];
TEST_ASSERT(smol0->getNumAtoms() == smol1->getNumAtoms());
for (unsigned int atomIdx = 0; atomIdx < smol0->getNumAtoms(); ++atomIdx) {
TEST_ASSERT(smol0->getAtomWithIdx(atomIdx)->getFormalCharge() ==
smol1->getAtomWithIdx(atomIdx)->getFormalCharge());
}
// check that bond orders are alternate on aromatic bonds between
// structures on odd indices and structures on even indices
TEST_ASSERT(smol0->getNumBonds() == smol1->getNumBonds());
for (unsigned int bondIdx = 0; bondIdx < smol0->getNumBonds(); ++bondIdx) {
TEST_ASSERT(
(!smol0->getBondWithIdx(bondIdx)->getIsAromatic() &&
!smol1->getBondWithIdx(bondIdx)->getIsAromatic() &&
(smol0->getBondWithIdx(bondIdx)->getBondType() ==
smol1->getBondWithIdx(bondIdx)->getBondType())) ||
(smol0->getBondWithIdx(bondIdx)->getIsAromatic() &&
smol1->getBondWithIdx(bondIdx)->getIsAromatic() &&
(static_cast<int>(
smol0->getBondWithIdx(bondIdx)->getBondTypeAsDouble() +
smol1->getBondWithIdx(bondIdx)->getBondTypeAsDouble()) == 3)));
}
delete smol0;
delete smol1;
}
delete resMolSuppl;
delete mol;
}
void testGitHub3048() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testGitHub3048" << std::endl;
RWMol *mol = SmilesToMol("C1CN3N(C1)c2ccccc2N=C3N");
auto *resMolSuppl =
new ResonanceMolSupplier(*mol, ResonanceMolSupplier::KEKULE_ALL);
// This caused a segfault due to a null ptr being accessed (#3048)
TEST_ASSERT(resMolSuppl->length() == 2);
delete resMolSuppl;
delete mol;
}
void testGitHub2597() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testGitHub2597" << std::endl;
{
class MyCallBack : public ResonanceMolSupplierCallback {
bool operator()() override {
TEST_ASSERT(getNumConjGrps() == 1);
return (getNumStructures(0) < 12);
}
};
class MyCallBack2 : public ResonanceMolSupplierCallback {
bool operator()() override {
TEST_ASSERT(getNumConjGrps() == 1);
return (getNumDiverseStructures(0) < 8);
}
};
RWMol *mol = SmilesToMol(
"ClC1=NC(NC2=CC=CC3=C2C(=O)C2=CC=CC=C2C3=O)=NC(NC2=CC=CC3=C2C(=O)C2=CC="
"CC=C2C3=O)=N1");
auto *resMolSuppl = new ResonanceMolSupplier(*mol);
TEST_ASSERT(resMolSuppl->length() == 1);
delete resMolSuppl;
resMolSuppl =
new ResonanceMolSupplier(*mol, ResonanceMolSupplier::KEKULE_ALL);
TEST_ASSERT(resMolSuppl->length() == 32);
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION, 10);
TEST_ASSERT(!resMolSuppl->getProgressCallback());
TEST_ASSERT(resMolSuppl->length() == 10);
TEST_ASSERT(!resMolSuppl->wasCanceled());
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION);
MyCallBack *callback = new MyCallBack();
resMolSuppl->setProgressCallback(callback);
TEST_ASSERT(resMolSuppl->getProgressCallback() == callback);
TEST_ASSERT(resMolSuppl->length() == 12);
TEST_ASSERT(resMolSuppl->wasCanceled());
delete resMolSuppl;
resMolSuppl = new ResonanceMolSupplier(
*mol, ResonanceMolSupplier::ALLOW_CHARGE_SEPARATION);
resMolSuppl->setProgressCallback(new MyCallBack2());
TEST_ASSERT(resMolSuppl->getProgressCallback());
resMolSuppl->setProgressCallback(nullptr);
TEST_ASSERT(!resMolSuppl->getProgressCallback());
resMolSuppl->setProgressCallback(new MyCallBack2());
TEST_ASSERT(resMolSuppl->length() == 9);
TEST_ASSERT(resMolSuppl->wasCanceled());
delete resMolSuppl;
delete mol;
}
}
void testGitHub3349() {
BOOST_LOG(rdInfoLog) << "-----------------------\n"
<< "testGitHub3349" << std::endl;
RWMol *mol = SmilesToMol("CC(=O)[O-]->[*]");
auto *resMolSuppl =
new ResonanceMolSupplier(*mol, ResonanceMolSupplier::KEKULE_ALL);
// This erroneously returned a single resonance structure
// as dative and zero-order bonds were not accounted for (#3349)
TEST_ASSERT(resMolSuppl->length() == 2);
delete resMolSuppl;
delete mol;
}
int main() {
RDLog::InitLogs();
#if 1
testBaseFunctionality();
testBenzylCation();
testBenzylAnion();
testButadiene();
testZwitterion();
testChargeMigration();
testChargeSeparation1();
testChargeSeparation2();
testMultipleConjGroups1();
testMultipleConjGroups2();
testDimethylMalonate();
testMethylAcetate();
testPyranium2_6dicarboxylate();
testSubstructMatchAcetate();
testSubstructMatchDMAP();
testCrambin();
testGitHub1166();
testConjGrpPerception();
testGitHub3048();
testGitHub2597();
testGitHub3349();
#endif
return 0;
}