Files
openbabel/test/regressionstest.cpp
T
Geoff Hutchison c82abd1796 Merge pull request #2678 from fredrikw/gh_2677
Make CorrectForPH respect perceived chains.
2024-12-07 10:48:01 -05:00

792 lines
27 KiB
C++

#include "obtest.h"
#include <openbabel/mol.h>
#include <openbabel/obconversion.h>
#include <openbabel/phmodel.h>
#include <openbabel/elements.h>
#include <openbabel/atom.h>
#include <openbabel/obiter.h>
#include <openbabel/bond.h>
#include <openbabel/generic.h>
#include <openbabel/forcefield.h>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
using namespace OpenBabel;
void test_Fix1912_PDBReading()
{
// Reading from a PDB file should set the residues
// and mark chains as perceived
OBMolPtr mol = OBTestUtil::ReadFile("00T_ideal_het.pdb");
OB_ASSERT(mol->HasChainsPerceived());
OBAtom *atom = mol->GetAtom(1);
OBResidue *res = atom->GetResidue();
OB_REQUIRE(res != nullptr);
OB_COMPARE(res->GetAtomID(atom), " N19");
OB_COMPARE(res->GetChain(), 'A');
}
std::string remove_slashr(const char *smi)
{
// Remove \r if present to normalise across platforms
std::string ans;
const char *p = smi;
while (*p)
{
if (*p != '\r')
ans += *p;
p++;
}
return ans;
}
struct CdxData
{
const char *fname;
const char *smi;
};
// Some basic reading of ChemDraw files
// Note that we don't correctly read radicals - TODO
// Also, converting ChemDraw doesn't work with the Read() interface, only Convert()
void test_ChemDraw_Basic()
{
static const CdxData cdxData[] = {
{"ethanol.cdx", "CCO\t\n"},
// cyclohexane -> benzene reaction, plus another cyclohexane drawn on its own
{"molrxnmix.cdx", "C1CCCCC1>>c1ccccc1\t\nC1CCCCC1\t\n"},
{"MeCN.cdx", "CC#N\t\n"},
{"acetate.cdx", "CC(=O)[O-]\t\n"}};
ios_base::openmode imode = ios_base::in | ios_base::binary;
unsigned int size = sizeof(cdxData) / sizeof(CdxData);
OBConversion conv;
OB_REQUIRE(conv.SetInAndOutFormats("cdx", "smi"));
std::stringstream outs;
conv.SetOutStream(&outs);
for (int i = 0; i < size; ++i)
{
std::string fname = OBTestUtil::GetFilename(cdxData[i].fname);
std::ifstream ifs(fname.c_str(), imode);
OB_REQUIRE(ifs.good());
conv.SetInStream(&ifs);
outs.str("");
conv.Convert();
std::string out = outs.str();
OB_COMPARE(remove_slashr(out.c_str()), cdxData[i].smi);
}
}
// Very basic test for cdxml
void test_ChemDraw_XML_Basic()
{
static const CdxData cdxmlData[] = {
{"methanol.cdxml", "CO\t8\n"},
{"gh2696_mols.cdxml", "[C@H]1(C2C=CC(C1)C2)C\t840\n"
"c1ccccc1\t5\n"
"c1cc(cs1)[C@H](CC)C\t23\n"
"NC(C(=O)O)Cc1c[nH]c2c1cccc2\t523\n"
"NC(C(=O)O)Cc1nc[nH]c1\t263\n"
"C12CC3CC(C1)C1CC3CC2C1\t527\n"}};
ios_base::openmode imode = ios_base::in;
unsigned int size = sizeof(cdxmlData) / sizeof(CdxData);
OBConversion conv;
OB_REQUIRE(conv.SetInAndOutFormats("cdxml", "smi"));
std::stringstream outs;
conv.SetOutStream(&outs);
for (int i = 0; i < size; ++i)
{
std::string fname = OBTestUtil::GetFilename(cdxmlData[i].fname);
std::ifstream ifs(fname.c_str(), imode);
OB_REQUIRE(ifs.good());
conv.SetInStream(&ifs);
outs.str("");
conv.Convert();
std::string out = outs.str();
OB_COMPARE(remove_slashr(out.c_str()), cdxmlData[i].smi);
}
}
// A basic test of functionality
void test_OBChemTsfm()
{
OBMol mol;
OBConversion conv;
conv.SetInFormat("smi");
conv.SetOutFormat("smi");
// Notes to self: Problems with OBChemTsfm:
// tsfm.Init("Cl[C:1]-[C:2]", "[C:1]=[C:2]"); // TODO: Need to change the API to take const char
// Init should wipe the state so that OBChemTsfm can safely be reused
conv.ReadString(&mol, "NCCBr");
OBChemTsfm tsfm;
std::string start("[N:1]-C-C");
std::string end("[N+:1]");
tsfm.Init(start, end);
tsfm.Apply(mol);
std::string out = conv.WriteString(&mol, true);
OB_COMPARE(out, "[NH3+]CCBr");
conv.ReadString(&mol, "ClCCBr");
start = "Cl[C:1]-[C:2]";
end = "[C:1]=[C:2]";
OBChemTsfm b;
b.Init(start, end);
b.Apply(mol);
out = conv.WriteString(&mol, true);
OB_COMPARE(out, "ClC=CBr");
conv.ReadString(&mol, "ClC(=O)[O]");
start = "[#6]-[OD1:1]";
end = "[#6]-[O-1:1]";
OBChemTsfm c;
c.Init(start, end);
c.Apply(mol);
out = conv.WriteString(&mol, true);
OB_COMPARE(out, "ClC(=O)[O-]");
conv.ReadString(&mol, "Cl[C]CBr");
start = "Cl[C:1]-[C:2]";
end = "[C:1]=[C:2]";
OBChemTsfm d;
d.Init(start, end);
d.Apply(mol);
out = conv.WriteString(&mol, true);
OB_COMPARE(out, "Cl[C]=CBr");
}
// Open Babel was previously disappearing triple bonds when provided with SMILES
// containing a triple bond in an aromatic ring
void test_AromaticTripleBond()
{
const char *smis[] = {
"CCc1n[c]#[c]n1CC2CC(C(=O)O2)(c3ccccc3)c4ccccc4",
"CCc1nc#cn1CC2CC(C(=O)O2)(c3ccccc3)c4ccccc4"};
OBConversion conv;
conv.SetInFormat("smi");
for (int i = 0; i < 2; ++i)
{
OBMol mol;
conv.ReadString(&mol, smis[i]);
bool hasTripleBond = false;
FOR_BONDS_OF_MOL(bond, mol)
{
if (bond->GetBondOrder() == 3)
hasTripleBond = true;
}
OB_ASSERT(hasTripleBond);
}
}
// A segfault was occuring when a Universal SMILES was output after an InChIfied SMILES.
// This was due to short-circuit caching of InChIs on reading. The fix was to limit
// the situations when the cached value was used, but also to delete the cached value
// in this particular instance.
void test_Issue135_UniversalSmiles()
{
// Test writing U smiles after I smiles
OBConversion conv;
conv.SetInFormat("smi");
OBMol mol;
conv.ReadString(&mol, "C(=O)([O-])C(=O)O");
conv.SetOutFormat("smi");
conv.SetOptions("I", OBConversion::OUTOPTIONS);
std::string res = conv.WriteString(&mol, true);
OB_COMPARE(res, "C(=O)(C(=O)O)[O-]");
conv.SetOptions("U", OBConversion::OUTOPTIONS);
res = conv.WriteString(&mol, true);
OB_COMPARE(res, "C(=O)(C(=O)[O-])O");
}
// Reading an InChI and then adding hydrogens messed up the structure
void test_Issue134_InChI_addH()
{
OBConversion conv;
conv.SetInFormat("inchi");
OBMol mol;
conv.ReadString(&mol, "InChI=1S/C2H7NO/c1-2(3)4/h2,4H,3H2,1H3/t2-/m0/s1");
OB_ASSERT(!mol.HasData(OBGenericDataType::VirtualBondData));
mol.AddHydrogens();
conv.SetOutFormat("smi");
std::string res = conv.WriteString(&mol, true);
OB_COMPARE(res, "C[C@@H](N)O");
}
// Delete hydrogens should not remove charged or isotopic hydrogens or [H][H] or [Cu][H][Cu]
// or hydrogens with assigned atom classes
void test_Issue178_DeleteHydrogens()
{
OBConversion conv;
conv.SetInFormat("smi");
OBMol mol;
// Test DeleteHydrogens() and DeleteNonPolarHydrogens()
static const char *smi[] = {"C[H]", "[H][H]", "C[1H]", "C[H]C", "C[H+]"};
int numHs[] = {0, 2, 1, 1, 1};
for (int i = 0; i < 5; ++i)
{
for (int j = 0; j < 2; ++j)
{
conv.ReadString(&mol, smi[i]);
if (j == 0)
mol.DeleteHydrogens();
else
mol.DeleteNonPolarHydrogens();
int myNumHs = 0;
FOR_ATOMS_OF_MOL(atom, mol)
if (atom->GetAtomicNum() == OBElements::Hydrogen)
myNumHs++;
OB_COMPARE(myNumHs, numHs[i]);
}
}
// Test DeletePolarHydrogens()
static const char *smiB[] = {"N[H]", "[H][H]", "N[1H]", "N[H]C", "N[H+]"};
int numHsB[] = {0, 2, 1, 1, 1};
for (int i = 0; i < 5; ++i)
{
conv.ReadString(&mol, smiB[i]);
mol.DeletePolarHydrogens();
int myNumHs = 0;
FOR_ATOMS_OF_MOL(atom, mol)
if (atom->GetAtomicNum() == OBElements::Hydrogen)
myNumHs++;
OB_COMPARE(myNumHs, numHsB[i]);
}
// Test atom class
// Currently, the SMILES parser does not retain atom classes for hydrogens on reading so...
conv.ReadString(&mol, "C[H]");
OBPairInteger *ac = new OBPairInteger();
ac->SetAttribute("Atom Class");
ac->SetValue(99);
mol.GetAtom(2)->SetData(ac); // Assign the hydrogen (atom 2) a class of 99
mol.DeleteHydrogens();
int myNumHs = 0;
FOR_ATOMS_OF_MOL(atom, mol)
if (atom->GetAtomicNum() == OBElements::Hydrogen)
myNumHs++;
OB_COMPARE(myNumHs, 1);
}
void test_Issue305_NumRotors()
{
OBMolPtr mol = OBTestUtil::ReadFile("regressiontest_numrotors.mol");
OB_COMPARE(mol->NumRotors(), 9); // was returning 4
}
void test_PR329_Molfile_RGroups()
{
// There are several way to get an R Group into a mol file
// 1. The existing use of atom maps on dummy atoms in SMILES
OBConversion conv;
OBMol mol;
conv.SetInAndOutFormats("smi", "mol");
conv.ReadString(&mol, "C([*:1]CO[*:2]");
obErrorLog.SetOutputLevel(obError); // avoid warning about no 2D or 3D coords
std::string molfileWithRGP = conv.WriteString(&mol);
obErrorLog.SetOutputLevel(obWarning);
OB_ASSERT(molfileWithRGP.find("R#") != std::string::npos);
OB_ASSERT(molfileWithRGP.find("M RGP 2 2 1 5 2") != std::string::npos); // i.e. atom 2 is labelled R1, atom 5 is labelled R2
// Check negative case
conv.ReadString(&mol, "C([*]CO[*]");
std::string molfileb = conv.WriteString(&mol);
OB_ASSERT(molfileb.find("R#") == std::string::npos);
OB_ASSERT(molfileb.find("M RGP") == std::string::npos);
// 2. By reading a molfile that use the R#, RGP notation
conv.SetInAndOutFormats("mol", "mol");
conv.ReadString(&mol, molfileWithRGP);
molfileb = conv.WriteString(&mol);
OB_ASSERT(molfileb.find("R#") != std::string::npos);
OB_ASSERT(molfileb.find("M RGP 2 2 1 5 2") != std::string::npos); // i.e. atom 2 is labelled R1, atom 5 is labelled R2
// 3. By reading a molfile that specifies the atom alias as Rn, where n is an integer
std::string molfileWithAlias = "\n"
" OpenBabel07211621152D\n"
"\n"
" 2 1 0 0 0 0 0 0 0 0999 V2000\n"
" 1.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n"
" 0.0000 1.0000 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0\n"
" 1 2 1 0 0 0 0\n"
"A 2\n"
"R1\n"
"M END";
conv.SetInAndOutFormats("mol", "mol");
conv.ReadString(&mol, molfileWithAlias);
std::string molfile = conv.WriteString(&mol);
OB_ASSERT(molfile.find("R#") != std::string::npos);
OB_ASSERT(molfile.find("M RGP 1 2 1") != std::string::npos); // i.e. atom 2 is labelled R1
// Check negative case
molfileWithAlias = "\n"
" OpenBabel07211621152D\n"
"\n"
" 2 1 0 0 0 0 0 0 0 0999 V2000\n"
" 1.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n"
" 0.0000 1.0000 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0\n"
" 1 2 1 0 0 0 0\n"
"A 2\n"
"R\n"
"M END";
conv.SetInAndOutFormats("mol", "mol");
obErrorLog.SetOutputLevel(obError); // avoid warning "Alias R was not chemically interpreted"
conv.ReadString(&mol, molfileWithAlias);
obErrorLog.SetOutputLevel(obWarning);
molfile = conv.WriteString(&mol);
OB_ASSERT(molfile.find("R#") == std::string::npos);
OB_ASSERT(molfile.find("M RGP") == std::string::npos);
// 4. By reading a molfile that specifies the element name as R1, etc.
std::string molfileWithRGroupElementName = "\n"
" OpenBabel07211621152D\n"
"\n"
" 2 1 0 0 0 0 0 0 0 0999 V2000\n"
" 1.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n"
" 0.0000 1.0000 0.0000 R1 0 0 0 0 0 0 0 0 0 0 0 0\n"
" 1 2 1 0 0 0 0\n"
"M END";
conv.SetInAndOutFormats("mol", "mol");
conv.ReadString(&mol, molfileWithRGroupElementName);
molfile = conv.WriteString(&mol);
OB_ASSERT(molfile.find("R#") != std::string::npos);
OB_ASSERT(molfile.find("M RGP 1 2 1") != std::string::npos); // i.e. atom 2 is labelled R1
}
struct SmilesData
{
const char *inp;
const char *out;
const char *out_hoption; // if you specify -xh, i.e. "output explicit Hydrogens as such"
const char *out_addh_hoption; // if you add hydrogens and then specify "-xh"
const char *out_soption; // if you specify -xs, create SMARTS for substructure searching
};
void test_SMILES_Valence()
{
static const SmilesData smilesData[] = {
{"[H]", "", "", "", "[#1]"}, // SMARTS perhaps should be [*H], but not implemented at the moment
{"[H][H]", "", "", "", "[#1][#1]"},
{"[HH]", "", "", "[H][H]", "[#1]"},
{"C", "", "", "C([H])([H])([H])[H]", ""},
{"[C]", "", "", "", "C"},
{"[CH]", "", "", "[C][H]", "C"},
{"[CH3]", "", "", "[C]([H])([H])[H]", "C"},
{"[CH4]", "C", "C", "C([H])([H])([H])[H]", "C"},
{"[CH5]", "", "", "[C]([H])([H])([H])([H])[H]", "C"},
{"C[H]", "C", "", "C([H])([H])([H])[H]", "[C!H0]"},
{"[C][H]", "[CH]", "", "", "[C!H0]"},
{"[CH3][H]", "C", "C[H]", "C([H])([H])([H])[H]", "[C!H0]"},
{"[CH2]([H])[H]", "C", "C([H])[H]", "C([H])([H])([H])[H]", "[C!H0!H1]"},
{"[U][H]", "[UH]", "", "", "[U!H0]"},
{"[UH2]", "", "", "[U]([H])[H]", "[U]"},
{"[C@@H](Br)(Cl)I", "", "", "[C@](Br)(Cl)(I)[H]", "[C](Br)(Cl)I"}, // Note: if OB supported it, SMARTS should be [C@@?](Br)(Cl)I
{"Br[C@@H](Cl)I", "", "", "Br[C@@](Cl)(I)[H]", "Br[C](Cl)I"}, // Note: if OB supported it, SMARTS should be Br[C@@?](Cl)I
{"[C@@](F)(Br)(Cl)I", "", "", "", ""},
{"F[C@@](Br)(Cl)I", "", "", "", ""},
{"[H][C@@](Br)(Cl)I", "[C@@H](Br)(Cl)I", "", "", "[C@@H](Br)(Cl)I"},
{"C[H:1]", "C", "C[H]", "C([H])([H])([H])[H]", "[C!H0]"}, // atom class only shown with -xa
{"C[2H]", "", "", "C([2H])([H])([H])[H]", "C[2#1]"},
{"c1ccccc1", "", "", "c1(c(c(c(c(c1[H])[H])[H])[H])[H])[H]", ""},
{"c1cnccc1", "", "", "c1(c(nc(c(c1[H])[H])[H])[H])[H]", ""},
{"c1c[nH]cc1", "", "", "c1(c(n(c(c1[H])[H])[H])[H])[H]", "c1cncc1"},
{"F[I]F", "", "", "", "FIF"},
};
unsigned int size = (unsigned int)(sizeof(smilesData) / sizeof(smilesData[0]));
for (unsigned int rep = 0; rep < 4; ++rep)
{
printf("Rep: %d\n", rep);
OBConversion conv;
OB_ASSERT(conv.SetInAndOutFormats("smi", "smi"));
switch (rep)
{
case 1:
case 2:
conv.SetOptions("h", conv.OUTOPTIONS);
break;
case 3:
conv.SetOptions("s", conv.OUTOPTIONS);
break;
}
for (unsigned int i = 0; i < size; ++i)
{
OBMol mol;
OB_ASSERT(conv.ReadString(&mol, smilesData[i].inp));
if (rep == 2)
mol.AddHydrogens();
std::string out = conv.WriteString(&mol, true);
const char *mout;
switch (rep)
{
case 0:
mout = smilesData[i].out;
break;
case 1:
mout = smilesData[i].out_hoption;
break;
case 2:
mout = smilesData[i].out_addh_hoption;
break;
case 3:
mout = smilesData[i].out_soption;
break;
}
std::string ans = mout[0] ? mout : smilesData[i].inp;
printf(" %d %s --> %s (%s)\n", i, smilesData[i].inp, ans.c_str(), out.c_str());
OB_COMPARE(out, ans);
}
}
OBConversion conv;
OB_ASSERT(conv.SetInAndOutFormats("smi", "smi"));
conv.SetOptions("ah", conv.OUTOPTIONS); // write out alias explicitly
OBMol mol;
conv.ReadString(&mol, "C[H:1]");
OB_COMPARE(conv.WriteString(&mol, true), "C[H:1]");
}
// make sure insertion code gets copied (it wasn't)
void test_insertioncode()
{
const char *pdb = "ATOM 266 HB2 ASP L 14 -2.604 8.021 19.867 1.00 0.00 H\n\
ATOM 267 CG ASP L 14 -2.280 6.992 21.697 1.00 18.10 C\n\
ATOM 268 OD1 ASP L 14 -1.109 7.431 21.698 1.00 18.97 O\n\
ATOM 269 OD2 ASP L 14 -2.735 6.263 22.603 1.00 19.18 O\n\
ATOM 270 N LYS L 14A -5.804 6.060 21.469 1.00 20.85 N\n\
ATOM 271 H LYS L 14A -5.589 5.759 20.497 1.00 0.00 H\n\
ATOM 272 CA LYS L 14A -6.654 5.209 22.296 1.00 22.86 C\n\
ATOM 273 HA LYS L 14A -7.392 5.923 22.662 1.00 0.00 H\n\
ATOM 274 C LYS L 14A -6.108 4.607 23.591 1.00 21.70 C\n\
ATOM 275 O LYS L 14A -6.892 4.228 24.455 1.00 21.72 O\n";
OBConversion conv;
OB_ASSERT(conv.SetInAndOutFormats("pdb", "pdb"));
OBMol mol;
conv.ReadString(&mol, pdb);
OBMol mol2;
mol2 = mol;
char i = mol2.GetResidue(1)->GetInsertionCode();
OB_COMPARE(i, 'A');
}
// make sure icode is read by pdbqt
void test_insertioncode_pdbqt()
{
const char *pdb = "ATOM 266 HB2 ASP L 14 -2.604 8.021 19.867 1.00 0.00 H\n\
ATOM 267 CG ASP L 14 -2.280 6.992 21.697 1.00 18.10 C\n\
ATOM 268 OD1 ASP L 14 -1.109 7.431 21.698 1.00 18.97 O\n\
ATOM 269 OD2 ASP L 14 -2.735 6.263 22.603 1.00 19.18 O\n\
ATOM 270 N LYS L 14A -5.804 6.060 21.469 1.00 20.85 N\n\
ATOM 271 H LYS L 14A -5.589 5.759 20.497 1.00 0.00 H\n\
ATOM 272 CA LYS L 14A -6.654 5.209 22.296 1.00 22.86 C\n\
ATOM 273 HA LYS L 14A -7.392 5.923 22.662 1.00 0.00 H\n\
ATOM 274 C LYS L 14A -6.108 4.607 23.591 1.00 21.70 C\n\
ATOM 275 O LYS L 14A -6.892 4.228 24.455 1.00 21.72 O\n";
OBConversion conv;
OB_ASSERT(conv.SetInAndOutFormats("pdbqt", "pdbqt"));
OBMol mol;
conv.ReadString(&mol, pdb);
OBMol mol2;
mol2 = mol;
char i = mol2.GetResidue(1)->GetInsertionCode();
OB_COMPARE(i, 'A');
}
// https://github.com/openbabel/openbabel/issues/1794
void test_github_issue_1794()
{
OBMol mol;
OBConversion conv;
conv.SetInFormat("smi");
conv.ReadString(&mol, "CC[2H]");
OBForceField *pFF = OBForceField::FindForceField("UFF");
OB_REQUIRE(pFF);
OB_ASSERT(pFF->Setup(mol));
}
void test_github_issue_2111_impl(const std::string &smiles)
{
OBMol mol;
OBConversion conv;
conv.SetInAndOutFormats("smi", "inchi");
conv.ReadString(&mol, smiles);
mol.DeleteHydrogens();
conv.WriteString(&mol);
}
// https://github.com/openbabel/openbabel/issues/2111
void test_github_issue_2111()
{
test_github_issue_2111_impl("[H][C@@H](I)F"); // tetrahedral with 2 implicit refs
test_github_issue_2111_impl("[H]/N=C/F"); // cis/trans with 2 implicit refs on the left
test_github_issue_2111_impl("F/N=C/[H]"); // cis/trans with 2 implicit refs on the right
//
// Tetrahedral
//
// example from bug report
test_github_issue_2111_impl("[C@@H]12C[C@H](OCC3=CC=CC=C3)[C@@H](COCC3=CC=CC=C3)[C@]1([H])O2");
// implicit ref in all positions
test_github_issue_2111_impl("[H][C@](C)(F)I");
test_github_issue_2111_impl("C[C@]([H])(F)I");
test_github_issue_2111_impl("C[C@](F)([H])I");
test_github_issue_2111_impl("C[C@](F)(I)[H]");
//
// Cis/Trans
//
// implicit ref in all positions
test_github_issue_2111_impl("[H]/N(C)=C/F");
test_github_issue_2111_impl("C/N([H])=C/F");
test_github_issue_2111_impl("F/N=C(/[H])C");
test_github_issue_2111_impl("F/N=C(/C)[H]");
}
void test_github_issue_2428_data_in_png()
{
ios_base::openmode imode = ios_base::in | ios_base::binary;
OBConversion conv;
OB_REQUIRE(conv.SetInAndOutFormats("png", "can"));
std::stringstream outs;
conv.SetOutStream(&outs);
std::string fname = OBTestUtil::GetFilename("pyridine.png");
std::ifstream ifs(fname.c_str(), imode);
OB_REQUIRE(ifs.good());
conv.SetInStream(&ifs);
outs.str("");
conv.Convert();
std::string out = outs.str();
OB_COMPARE(remove_slashr(out.c_str()), "c1cccnc1\t\n");
}
void test_bad_bondorders() // make sure N in aromatic rings get right bond orders
{
// from PDB 1U71
const char *pdb = "REMARK 800 SITE_DESCRIPTION: BINDING SITE FOR RESIDUE MXA A 187\n\
HET MXA A 187 24 \n\
HETNAM MXA 6-(2,5-DIMETHOXY-BENZYL)-5-METHYL-PYRIDO[2,3- \n\
HETNAM 2 MXA D]PYRIMIDINE-2,4-DIAMINE \n\
FORMUL 4 MXA C17 H19 N5 O2 \n\
HETATM 1517 N1 MXA A 187 28.796 13.139 -4.319 0.50 2.21 N \n\
HETATM 1518 C2 MXA A 187 27.596 12.644 -4.455 0.50 2.00 C \n\
HETATM 1519 N2 MXA A 187 27.196 12.327 -5.700 0.50 2.00 N \n\
HETATM 1520 N3 MXA A 187 26.752 12.365 -3.424 0.50 2.00 N \n\
HETATM 1521 C4 MXA A 187 27.102 12.613 -2.212 0.50 2.38 C \n\
HETATM 1522 N4 MXA A 187 26.137 12.302 -1.332 0.50 2.06 N \n\
HETATM 1523 C4A MXA A 187 28.375 13.227 -1.915 0.50 3.60 C \n\
HETATM 1524 C5 MXA A 187 28.903 13.607 -0.674 0.50 5.54 C \n\
HETATM 1525 C5M MXA A 187 28.179 13.440 0.652 0.50 5.83 C \n\
HETATM 1526 C6 MXA A 187 30.171 14.172 -0.645 0.50 7.15 C \n\
HETATM 1527 C7 MXA A 187 30.848 14.357 -1.861 0.50 5.90 C \n\
HETATM 1528 N8 MXA A 187 30.407 14.021 -3.042 0.50 3.72 N \n\
HETATM 1529 C8A MXA A 187 29.188 13.455 -3.076 0.50 3.31 C \n\
HETATM 1530 C9 MXA A 187 30.823 14.676 0.605 0.50 10.20 C \n\
HETATM 1531 C1' MXA A 187 32.247 14.320 0.954 0.50 12.53 C \n\
HETATM 1532 C2' MXA A 187 33.277 15.250 0.897 0.50 13.42 C \n\
HETATM 1533 O2' MXA A 187 32.883 16.513 0.499 0.50 13.85 O \n\
HETATM 1534 C21 MXA A 187 33.905 17.478 0.251 0.50 13.61 C \n\
HETATM 1535 C3' MXA A 187 34.547 14.874 1.267 0.50 14.29 C \n\
HETATM 1536 C4' MXA A 187 34.808 13.570 1.706 0.50 14.41 C \n\
HETATM 1537 C5' MXA A 187 33.819 12.674 1.740 0.50 13.91 C \n\
HETATM 1538 O5' MXA A 187 33.800 11.326 2.099 0.50 16.10 O \n\
HETATM 1539 C51 MXA A 187 32.462 10.825 2.070 0.50 15.86 C \n\
HETATM 1540 C6' MXA A 187 32.533 13.060 1.396 0.50 13.52 C ";
OBConversion conv;
OB_ASSERT(conv.SetInAndOutFormats("pdb", "sdf"));
OBMol mol;
conv.ReadString(&mol, pdb);
// no nitrogens should have 4 bonds
for (OBAtomIterator it = mol.BeginAtoms(); it != mol.EndAtoms(); it++)
{
OBAtom *a = *it;
if (a->GetAtomicNum() == 7)
{
OB_ASSERT((a->GetTotalValence() < 4));
}
}
}
// https://github.com/openbabel/openbabel/issues/2646
void test_github_issue_2646()
{
const char *cml = "<?xml version=\"1.0\"?>\n\
<molecule xmlns=\"http://www.xml-cml.org/schema\">\n\
<atomArray>\n\
<atom id = \"a1\" elementType = \"C\" hydrogenCount = \"1\" />\n\
<atom id = \"a2\" elementType = \"C\" hydrogenCount = \"1\" />\n\
<atom id = \"a3\" elementType = \"C\" hydrogenCount = \"1\" />\n\
<atom id = \"a4\" elementType = \"C\" hydrogenCount = \"1\" />\n\
<atom id = \"a5\" elementType = \"C\" hydrogenCount = \"1\" />\n\
<atom id = \"a6\" elementType = \"C\" hydrogenCount = \"1\" />\n\
</atomArray>\n\
<bondArray>\n\
<bond atomRefs2 = \"a1 a2\" order = \"A\" />\n\
<bond atomRefs2 = \"a2 a3\" order = \"A\" />\n\
<bond atomRefs2 = \"a3 a4\" order = \"A\" />\n\
<bond atomRefs2 = \"a4 a5\" order = \"A\" />\n\
<bond atomRefs2 = \"a5 a6\" order = \"A\" />\n\
<bond atomRefs2 = \"a1 a6\" order = \"A\" />\n\
</bondArray>\n\
</molecule>";
OBMol mol;
OBConversion conv;
conv.SetInAndOutFormats("cml", "can");
conv.ReadString(&mol, cml);
std::string can = conv.WriteString(&mol, true);
OB_COMPARE(can, "c1ccccc1");
}
// https://github.com/openbabel/openbabel/issues/2677
void test_github_issue_2677()
{
// Make sure that CorrectForPH doesn't mess up residues in pdb
OBMolPtr mol = OBTestUtil::ReadFile("1DRF.pdb");
OB_ASSERT(mol->HasChainsPerceived());
OBAtom *atom_before = mol->GetAtom(1);
OBResidue *res_before = atom_before->GetResidue();
mol->AddNewHydrogens(PolarHydrogen, true);
OBAtom *atom_after = mol->GetAtom(1);
OBResidue *res_after = atom_after->GetResidue();
OB_REQUIRE(res_after != nullptr);
OB_COMPARE(res_after->GetIdx(), res_before->GetIdx());
}
void test_SegCopySubstructure()
{
// Invalid memory access (atom->GetIdx()) detected in valgrind and sometimes
// triggering a sefault.
OBConversion conv;
OB_ASSERT(conv.SetInFormat("smi"));
OBMol mol;
std::string smi = "C[C@@H]1CO1";
OB_ASSERT(conv.ReadString(&mol, smi));
OBBitVec atomsToCopy;
atomsToCopy.Clear();
atomsToCopy.SetBitOn(2);
atomsToCopy.SetBitOn(3);
atomsToCopy.SetBitOn(4);
OBBitVec bondsToExclude;
bondsToExclude.Clear();
bondsToExclude.SetBitOn(0);
OBMol copy;
std::vector<unsigned int> atomorder;
atomorder.clear();
bool ok = mol.CopySubstructure(copy, &atomsToCopy, &bondsToExclude, 2, &atomorder);
OB_ASSERT(ok);
OB_COMPARE(4, copy.NumAtoms());
OB_COMPARE(4, copy.NumBonds());
}
int regressionstest(int argc, char *argv[])
{
int defaultchoice = 1;
int choice = defaultchoice;
if (argc > 1)
{
if (sscanf(argv[1], "%d", &choice) != 1)
{
printf("Couldn't parse that input as a number\n");
return -1;
}
}
// Define location of file formats for testing
#ifdef FORMATDIR
char env[BUFF_SIZE];
snprintf(env, BUFF_SIZE, "BABEL_LIBDIR=%s", FORMATDIR);
putenv(env);
#endif
switch (choice)
{
case 1:
test_Issue135_UniversalSmiles();
break;
case 2:
test_SegCopySubstructure();
break;
case 221:
test_Issue134_InChI_addH();
break;
case 222:
test_Issue178_DeleteHydrogens();
break;
case 223:
test_Issue305_NumRotors();
break;
case 224:
test_PR329_Molfile_RGroups();
break;
case 225:
test_AromaticTripleBond();
break;
case 226:
test_SMILES_Valence();
break;
case 227:
test_OBChemTsfm();
break;
case 228:
test_ChemDraw_Basic();
break;
case 229:
test_ChemDraw_XML_Basic();
break;
case 240:
test_Fix1912_PDBReading();
break;
case 241:
test_insertioncode();
break;
case 242:
test_insertioncode_pdbqt();
break;
case 1794:
test_github_issue_1794();
break;
case 2111:
test_github_issue_2111();
break;
case 2428:
test_github_issue_2428_data_in_png();
break;
case 2384:
test_bad_bondorders();
break;
case 2646:
test_github_issue_2646();
break;
case 2677:
test_github_issue_2677();
break;
// case N:
// YOUR_TEST_HERE();
// Remember to update CMakeLists.txt with the number of your test
// break;
default:
cout << "Test number " << choice << " does not exist!\n";
return -1;
}
return 0;
}