mirror of
https://github.com/openbabel/openbabel.git
synced 2026-08-09 12:38:03 -05:00
792 lines
27 KiB
C++
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;
|
|
}
|