#include <openbabel/babelconfig.h>
#include <openbabel/obmolecformat.h>
#include <openbabel/mol.h>
#include <openbabel/atom.h>
#include <openbabel/elements.h>
#include <openbabel/bond.h>
#include <openbabel/data.h>
#include <openbabel/generic.h>
#include <openbabel/forcefield.h>
#include <cstdlib>
using namespace std;
namespace OpenBabel
{
class TinkerFormat : public OBMoleculeFormat
{
public:
TinkerFormat()
{
OBConversion::RegisterFormat("txyz",this);
}
virtual const char* Description() {
return
"Tinker XYZ format\n"
"The cartesian XYZ file format used by the molecular mechanics package TINKER.\n"
"By default, the MM2 atom types are used for writing files but MM3 atom types\n"
"are provided as an option. Another option provides the ability to take the\n"
"atom type from the atom class (e.g. as used in SMILES, or set via the API).\n\n"
"Read Options e.g. -as\n"
" s Generate single bonds only\n\n"
"Write Options e.g. -xm\n"
" m Write an input file for the CNDO/INDO program.\n"
" c Write atom types using custom atom classes, if available\n"
" 3 Write atom types for the MM3 forcefield.\n\n";
};
virtual const char* SpecificationURL()
{return "http://dasher.wustl.edu/tinker/";};
virtual unsigned int Flags()
{
return READONEONLY | WRITEONEONLY;
};
virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv);
virtual bool WriteMolecule(OBBase* pOb, OBConversion* pConv);
};
TinkerFormat theTinkerFormat;
int SetMM3Type(OBAtom *atom);
bool TinkerFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
{
OBMol* pmol = pOb->CastAndClear<OBMol>();
if (pmol == nullptr)
return false;
istream &ifs = *pConv->GetInStream();
OBMol &mol = *pmol;
const char* title = pConv->GetTitle();
int natoms;
char buffer[BUFF_SIZE];
vector<string> vs;
ifs.getline(buffer, BUFF_SIZE);
tokenize(vs,buffer);
if (vs.size() < 2)
return false;
natoms = atoi(vs[0].c_str());
mol.SetTitle(vs[1]);
mol.ReserveAtoms(natoms);
mol.BeginModify();
string str;
double x,y,z;
OBAtom *atom;
for (int i = 1; i <= natoms; ++i)
{
if (!ifs.getline(buffer,BUFF_SIZE))
return(false);
tokenize(vs,buffer);
if (vs.size() < 5)
return(false);
atom = mol.NewAtom();
x = atof((char*)vs[2].c_str());
y = atof((char*)vs[3].c_str());
z = atof((char*)vs[4].c_str());
atom->SetVector(x,y,z);
atom->SetAtomicNum(OBElements::GetAtomicNum(vs[1].c_str()));
if (vs.size() > 6)
for (unsigned int j = 6; j < vs.size(); ++j)
mol.AddBond(mol.NumAtoms(), atoi((char *)vs[j].c_str()), 1);
}
if (!pConv->IsOption("s",OBConversion::INOPTIONS))
mol.PerceiveBondOrders();
std::streampos ipos;
do
{
ipos = ifs.tellg();
ifs.getline(buffer,BUFF_SIZE);
}
while(strlen(buffer) == 0 && !ifs.eof() );
ifs.seekg(ipos);
mol.EndModify();
mol.SetTitle(title);
return(true);
}
bool TinkerFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv)
{
OBMol* pmol = dynamic_cast<OBMol*>(pOb);
if (pmol == nullptr)
return false;
ostream &ofs = *pConv->GetOutStream();
OBMol &mol = *pmol;
bool mm2Types = false;
bool mmffTypes = pConv->IsOption("m", OBConversion::OUTOPTIONS) != nullptr;
bool mm3Types = pConv->IsOption("3", OBConversion::OUTOPTIONS) != nullptr;
bool classTypes = pConv->IsOption("c", OBConversion::OUTOPTIONS) != nullptr;
unsigned int i;
char buffer[BUFF_SIZE];
OBBond *bond;
vector<OBBond*>::iterator j;
OBForceField *ff = OpenBabel::OBForceField::FindForceField("MMFF94");
if (mmffTypes && ff && ff->Setup(mol))
mmffTypes = ff->GetAtomTypes(mol);
else
mmffTypes = false;
if (!mmffTypes && !mm3Types && !classTypes) {
snprintf(buffer, BUFF_SIZE, "%6d %-20s MM2 parameters\n",mol.NumAtoms(),mol.GetTitle());
mm2Types = true;
}
else if (mm3Types)
snprintf(buffer, BUFF_SIZE, "%6d %-20s MM3 parameters\n",mol.NumAtoms(),mol.GetTitle());
else if (classTypes)
snprintf(buffer, BUFF_SIZE, "%6d %-20s Custom parameters\n",mol.NumAtoms(),mol.GetTitle());
else
snprintf(buffer, BUFF_SIZE, "%6d %-20s MMFF94 parameters\n",mol.NumAtoms(),mol.GetTitle());
ofs << buffer;
ttab.SetFromType("INT");
OBAtom *atom;
string str,str1;
int atomType;
for(i = 1;i <= mol.NumAtoms(); i++)
{
atom = mol.GetAtom(i);
str = atom->GetType();
atomType = 0;
if (mm2Types) {
ttab.SetToType("MM2");
ttab.Translate(str1,str);
atomType = atoi((char*)str1.c_str());
}
if (mmffTypes) {
OBPairData *type = (OpenBabel::OBPairData*)atom->GetData("FFAtomType");
if (type) {
str1 = type->GetValue().c_str();
atomType = atoi((char*)str1.c_str());
}
}
if (mm3Types) {
atomType = SetMM3Type(atom);
}
if (classTypes) {
OBGenericData *data = atom->GetData("Atom Class");
if (data) {
OBPairInteger* acdata = dynamic_cast<OBPairInteger*>(data); if (acdata) {
int ac = acdata->GetGenericValue();
if (ac >= 0)
atomType = ac;
}
}
}
snprintf(buffer, BUFF_SIZE, "%6d %2s %12.6f%12.6f%12.6f %5d",
i,
OBElements::GetSymbol(atom->GetAtomicNum()),
atom->GetX(),
atom->GetY(),
atom->GetZ(),
atomType);
ofs << buffer;
for (bond = atom->BeginBond(j); bond; bond = atom->NextBond(j))
{
snprintf(buffer, BUFF_SIZE, "%6d", (bond->GetNbrAtom(atom))->GetIdx());
ofs << buffer;
}
ofs << endl;
}
return(true);
}
int SetMM3Type(OBAtom *atom)
{
OBAtom *b; OBBondIterator i, j;
int countNeighborO, countNeighborS, countNeighborN, countNeighborC;
countNeighborO = countNeighborS = countNeighborN = countNeighborC = 0;
switch (atom->GetAtomicNum()) {
case 1: b = atom->BeginNbrAtom(j);
if (b->IsCarboxylOxygen())
return 24;
if (b->GetAtomicNum() == OBElements::Sulfur)
return 44;
if (b->GetAtomicNum() == OBElements::Nitrogen) {
if (b->IsAmideNitrogen())
return 28;
if (b->GetExplicitDegree() > 3)
return 48; return 23; }
if (b->GetAtomicNum() == OBElements::Carbon && b->GetHyb() == 1)
return 124;
if (b->GetAtomicNum() == OBElements::Oxygen) {
if (b->HasAlphaBetaUnsat())
return 73; return 21; }
return 5; break;
case 2: return 51; break;
case 3: return 163; break;
case 5: if (atom->GetExplicitDegree() >= 4)
return 27; return 26; break;
case 6: if (atom->IsInRingSize(3)) { if (atom->GetHyb() == 3)
return 22;
if (atom->GetHyb() == 2) {
if (atom->CountFreeOxygens() == 1) return 67;
return 38; }
}
if (atom->IsInRingSize(4)) { if (atom->GetHyb() == 3)
return 56;
if (atom->GetHyb() == 2) {
if (atom->CountFreeOxygens() == 1) return 58;
return 57; }
}
if (atom->CountBondsOfOrder(2) == 2) { if (atom->CountFreeOxygens() == 1) return 106;
return 68;
}
if (atom->GetFormalCharge() == +1)
return 30;
if (atom->GetSpinMultiplicity() == 2)
return 29;
if (atom->GetHyb() == 3)
return 1;
else if (atom->GetHyb() == 2) {
if (atom->CountFreeOxygens() >= 1)
return 3;
return 2;
}
else if (atom->GetHyb() == 1)
return 4;
break;
case 7: if (atom->IsAmideNitrogen())
return 151;
if (atom->IsAromatic()) {
if (atom->GetFormalCharge() == 1)
return 111;
if (atom->IsInRingSize(5)) return 40;
if (atom->IsInRingSize(6)) return 37;
}
if (atom->CountFreeOxygens() == 2) return 46;
if (atom->GetHyb() == 3) {
if (atom->GetExplicitDegree() > 3)
return 39; return 8;
}
else if (atom->GetHyb() == 2)
return 9;
else if (atom->GetHyb() == 1)
return 10;
break;
case 8: if (atom->IsPhosphateOxygen())
return 159;
if (atom->IsCarboxylOxygen())
return 75;
if (atom->IsInRingSize(3))
return 49;
b = atom->BeginNbrAtom(j);
if (atom->HasBondOfOrder(2) && b->GetAtomicNum() == OBElements::Carbon) { return 7;
}
if (atom->IsAromatic())
return 41; return 6;
break;
case 9: return 11; break;
case 10: return 52; break;
case 12: return 59; break;
case 14: return 19; break;
case 15: if (atom->CountFreeOxygens() > 0)
return 153; if (atom->GetExplicitValence() > 3)
return 60; return 25; break;
case 16: if (atom->IsAromatic())
return 42; if (atom->GetFormalCharge() == 1)
return 16;
for (b = atom->BeginNbrAtom(j); b; b = atom->NextNbrAtom(j))
{
switch (b->GetAtomicNum()) {
case 6:
if (b->GetHyb() == 2) countNeighborC++; break;
case 7:
countNeighborN++; break;
case 8:
if (b->GetHvyDegree() == 1)
countNeighborO++;
break;
case 16:
countNeighborS++; break;
default:
continue;
}
}
if (countNeighborO == 1)
return 17; if (countNeighborO >= 2) {
if (countNeighborN)
return 154; return 18; }
if (countNeighborC)
return 74; if (countNeighborS == 1)
return 104; else if (countNeighborS > 1)
return 105;
return 15; break;
case 17: return 12; break;
case 18: return 153; break;
case 20: return 125; break;
case 26: if (atom->GetFormalCharge() == 2)
return 61;
return 62; break;
case 27: if (atom->GetFormalCharge() == 2)
return 65;
return 66; break;
case 28: if (atom->GetFormalCharge() == 2)
return 63;
return 64; break;
case 32: return 31; break;
case 34: return 34; break;
case 35: return 13; break;
case 36: return 54; break;
case 38: return 126; break;
case 50: return 32; break;
case 52: return 35; break;
case 53: return 14; break;
case 54: return 55; break;
case 56: return 127; break;
case 57:
return 128; break;
case 58:
return 129; break;
case 59:
return 130; break;
case 60:
return 131; break;
case 61:
return 132; break;
case 62:
return 133; break;
case 63:
return 134; break;
case 64:
return 135; break;
case 65:
return 136; break;
case 66:
return 137; break;
case 67:
return 138; break;
case 68:
return 139; break;
case 69:
return 140; break;
case 70:
return 141; break;
case 71:
return 142; break;
case 82: return 33; break;
default:
break;
}
return 0;
}
}