Files
rdkit/Code/GraphMol/catch_canon.cpp
Greg Landrum 9fa1df1cf6 Drop usage of CIP information from the canonicalization code (#5385)
* stop using CIP codes in the atomic canonicalization

this will change results in some cases, so lots of tests need to be updated.

* stop breaking string literals... ARGH

* tests now pass

* update double bond canonicalization

* update a python test

* some cleanup

* update expected results for cartridge
2022-07-13 05:40:23 +02:00

196 lines
7.7 KiB
C++

//
// 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 "catch.hpp"
#include <GraphMol/RDKitBase.h>
#include <GraphMol/StereoGroup.h>
#include <GraphMol/Chirality.h>
#include <GraphMol/new_canon.h>
#include <GraphMol/MolOps.h>
#include <GraphMol/FileParsers/FileParsers.h>
#include <GraphMol/FileParsers/MolFileStereochem.h>
#include <GraphMol/FileParsers/MolSupplier.h>
#include <GraphMol/SmilesParse/SmilesParse.h>
#include <GraphMol/SmilesParse/SmilesWrite.h>
using namespace RDKit;
TEST_CASE("chirality and canonicalization") {
SECTION("basics") {
auto mol = "F[C@](O)(Cl)C[C@](F)(O)Cl"_smiles;
REQUIRE(mol);
bool cleanIt = true;
bool force = true;
MolOps::assignStereochemistry(*mol, cleanIt, force);
std::string cip;
CHECK(mol->getAtomWithIdx(1)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "S");
CHECK(mol->getAtomWithIdx(5)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "R");
std::vector<unsigned int> ranks;
bool breakTies = false;
Canon::rankMolAtoms(*mol, ranks, breakTies);
CHECK(ranks[1] > ranks[5]);
mol->getAtomWithIdx(1)->clearProp(common_properties::_CIPCode);
mol->getAtomWithIdx(5)->clearProp(common_properties::_CIPCode);
Canon::rankMolAtoms(*mol, ranks, breakTies);
CHECK(ranks[1] > ranks[5]);
}
SECTION("same") {
auto mol = "F[C@](O)(Cl)C[C@](O)(F)Cl"_smiles;
REQUIRE(mol);
bool cleanIt = true;
bool force = true;
MolOps::assignStereochemistry(*mol, cleanIt, force);
std::string cip;
CHECK(mol->getAtomWithIdx(1)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "S");
CHECK(mol->getAtomWithIdx(5)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "S");
std::vector<unsigned int> ranks;
bool breakTies = false;
Canon::rankMolAtoms(*mol, ranks, breakTies);
CHECK(ranks[1] == ranks[5]);
mol->getAtomWithIdx(1)->clearProp(common_properties::_CIPCode);
mol->getAtomWithIdx(5)->clearProp(common_properties::_CIPCode);
Canon::rankMolAtoms(*mol, ranks, breakTies);
CHECK(ranks[1] == ranks[5]);
}
SECTION("dependent") {
auto mol = "F[C@](O)(Cl)[C@](F)(O)[C@](F)(O)Cl"_smiles;
REQUIRE(mol);
bool cleanIt = true;
bool force = true;
MolOps::assignStereochemistry(*mol, cleanIt, force);
std::string cip;
CHECK(mol->getAtomWithIdx(1)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "S");
CHECK(mol->getAtomWithIdx(7)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "R");
CHECK(mol->getAtomWithIdx(4)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "R");
std::vector<unsigned int> ranks;
bool breakTies = false;
Canon::rankMolAtoms(*mol, ranks, breakTies);
CHECK(ranks[1] > ranks[7]);
mol->getAtomWithIdx(1)->clearProp(common_properties::_CIPCode);
mol->getAtomWithIdx(4)->clearProp(common_properties::_CIPCode);
mol->getAtomWithIdx(7)->clearProp(common_properties::_CIPCode);
Canon::rankMolAtoms(*mol, ranks, breakTies);
CHECK(ranks[1] > ranks[7]);
}
SECTION("dependent non-chiral") {
auto mol = "F[C@](O)(Cl)[C@](F)(O)[C@](O)(F)Cl"_smiles;
REQUIRE(mol);
bool cleanIt = false;
bool force = true;
mol->getAtomWithIdx(4)->setChiralTag(Atom::ChiralType::CHI_TETRAHEDRAL_CCW);
MolOps::assignStereochemistry(*mol, cleanIt, force);
std::string cip;
CHECK(mol->getAtomWithIdx(1)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "S");
CHECK(mol->getAtomWithIdx(7)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "S");
std::vector<unsigned int> ranks;
bool breakTies = false;
Canon::rankMolAtoms(*mol, ranks, breakTies);
CHECK(ranks[1] == ranks[7]);
mol->getAtomWithIdx(1)->clearProp(common_properties::_CIPCode);
mol->getAtomWithIdx(7)->clearProp(common_properties::_CIPCode);
Canon::rankMolAtoms(*mol, ranks, breakTies);
CHECK(ranks[1] == ranks[7]);
}
SECTION("swap parity") {
auto mol = "F[C@](O)(Cl)C[C@@](O)(Cl)F"_smiles;
REQUIRE(mol);
bool cleanIt = false;
bool force = true;
MolOps::assignStereochemistry(*mol, cleanIt, force);
std::string cip;
CHECK(mol->getAtomWithIdx(1)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "S");
CHECK(mol->getAtomWithIdx(5)->getPropIfPresent(common_properties::_CIPCode,
cip));
CHECK(cip == "S");
std::vector<unsigned int> ranks;
bool breakTies = false;
Canon::rankMolAtoms(*mol, ranks, breakTies);
CHECK(ranks[1] == ranks[5]);
mol->getAtomWithIdx(1)->clearProp(common_properties::_CIPCode);
mol->getAtomWithIdx(5)->clearProp(common_properties::_CIPCode);
Canon::rankMolAtoms(*mol, ranks, breakTies);
CHECK(ranks[1] == ranks[5]);
}
}
TEST_CASE("double bond stereo and canonicalization") {
SECTION("basics") {
auto mol = "CC=C(F)C(B)C(F)=CC"_smiles;
REQUIRE(mol);
mol->getBondWithIdx(1)->setStereoAtoms(0, 4);
mol->getBondWithIdx(1)->setStereo(Bond::BondStereo::STEREOTRANS);
mol->getBondWithIdx(7)->setStereoAtoms(4, 9);
mol->getBondWithIdx(7)->setStereo(Bond::BondStereo::STEREOCIS);
bool breakTies = false;
std::vector<unsigned int> ranks;
Canon::rankMolAtoms(*mol, ranks, breakTies);
std::copy(ranks.begin(), ranks.end(),
std::ostream_iterator<unsigned int>(std::cerr, " "));
std::cerr << std::endl;
mol->getBondWithIdx(1)->setStereo(Bond::BondStereo::STEREOCIS);
mol->getBondWithIdx(7)->setStereo(Bond::BondStereo::STEREOTRANS);
std::vector<unsigned int> ranks2;
Canon::rankMolAtoms(*mol, ranks2, breakTies);
std::copy(ranks2.begin(), ranks2.end(),
std::ostream_iterator<unsigned int>(std::cerr, " "));
std::cerr << std::endl;
CHECK(ranks[0] == ranks2[9]);
CHECK(ranks[1] == ranks2[8]);
CHECK(ranks[2] == ranks2[6]);
CHECK(ranks[3] == ranks2[7]);
CHECK(ranks[4] == ranks2[4]);
CHECK(ranks[5] == ranks2[5]);
// same as previous example, different controlling atoms
mol->getBondWithIdx(7)->setStereoAtoms(7, 9);
mol->getBondWithIdx(7)->setStereo(Bond::BondStereo::STEREOCIS);
std::vector<unsigned int> ranks3;
Canon::rankMolAtoms(*mol, ranks3, breakTies);
std::copy(ranks3.begin(), ranks3.end(),
std::ostream_iterator<unsigned int>(std::cerr, " "));
std::cerr << std::endl;
CHECK(ranks2 == ranks3);
}
SECTION("STEREOANY is higher priority than STEREONONE") {
auto mol = "CC=C(F)C(B)C(F)=CC"_smiles;
REQUIRE(mol);
mol->getBondWithIdx(7)->setStereoAtoms(4, 9);
mol->getBondWithIdx(7)->setStereo(Bond::BondStereo::STEREOANY);
bool breakTies = false;
std::vector<unsigned int> ranks;
Canon::rankMolAtoms(*mol, ranks, breakTies);
std::copy(ranks.begin(), ranks.end(),
std::ostream_iterator<unsigned int>(std::cerr, " "));
std::cerr << std::endl;
CHECK(ranks[0] < ranks[9]);
}
}