#include <openbabel/babelconfig.h>
#include <openbabel/obmolecformat.h>
#include <openbabel/mol.h>
#include <openbabel/atom.h>
#include <openbabel/bond.h>
#include <openbabel/obiter.h>
#include <openbabel/elements.h>
#include <openbabel/generic.h>
#include <cstdlib>
#define EV_TO_KCAL_PER_MOL 23.060538
#define GPA_A3_TO_KCAL_PER_MOL 0.14383639
using namespace std;
namespace OpenBabel {
class CASTEPFormat : public OBMoleculeFormat
{
public:
CASTEPFormat()
{
OBConversion::RegisterFormat("castep",this);
}
virtual const char* Description()
{
return
"CASTEP format\n"
"The format used by CASTEP.\n\n";
};
virtual const char* SpecificationURL(){return "http://www.castep.org/";};
virtual unsigned int Flags()
{
return READONEONLY | NOTWRITABLE;
};
virtual int SkipObjects(int n, OBConversion* pConv)
{
return 0;
};
virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv);
private:
};
CASTEPFormat theCASTEPFormat;
bool CASTEPFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv)
{
OBMol* pmol = pOb->CastAndClear<OBMol>();
if (pmol == nullptr)
return false;
istream &ifs = *pConv->GetInStream();
bool coordsAreFractional = false;
bool hasPressureData = false;
bool hasVolumeData = false;
bool hasEnthalpyData = false;
double pressure, volume, enthalpy;
char buffer[BUFF_SIZE], tag[BUFF_SIZE];
double x,y,z,a,b,c,alpha,beta,gamma;
vector<string> vs;
matrix3x3 ortho;
int atomicNum;
OBUnitCell *cell = new OBUnitCell();
pmol->BeginModify();
while (ifs.getline(buffer,BUFF_SIZE)) {
if (strstr(buffer, "Lattice parameters(A) Cell Angles")) {
ifs.getline(buffer,BUFF_SIZE); tokenize(vs, buffer);
a = atof(vs.at(2).c_str());
alpha = atof(vs.at(5).c_str());
ifs.getline(buffer,BUFF_SIZE); tokenize(vs, buffer);
b = atof(vs.at(2).c_str());
beta = atof(vs.at(5).c_str());
ifs.getline(buffer,BUFF_SIZE); tokenize(vs, buffer);
c = atof(vs.at(2).c_str());
gamma = atof(vs.at(5).c_str());
cell->SetData(a, b, c, alpha, beta, gamma);
}
if (strstr(buffer, "x Element Atom Fractional coordinates of atoms x")) {
coordsAreFractional = true;
vector<OBAtom*> toDelete;
FOR_ATOMS_OF_MOL(a, *pmol)
toDelete.push_back(&*a);
for (size_t i = 0; i < toDelete.size(); i++)
pmol->DeleteAtom(toDelete.at(i));
ifs.getline(buffer,BUFF_SIZE); ifs.getline(buffer,BUFF_SIZE);
ifs.getline(buffer,BUFF_SIZE); tokenize(vs, buffer);
int size = vs.size();
while (size == 7) {
atomicNum = OBElements::GetAtomicNum(vs[1].c_str());
x = atof((char*)vs[3].c_str());
y = atof((char*)vs[4].c_str());
z = atof((char*)vs[5].c_str());
OBAtom *atom = pmol->NewAtom();
atom->SetAtomicNum(atomicNum);
vector3 coords (x,y,z);
atom->SetVector(coords);
ifs.getline(buffer,BUFF_SIZE); tokenize(vs, buffer);
size = vs.size();
}
}
if (strstr(buffer, "Final Enthalpy")) {
hasEnthalpyData = true;
tokenize(vs, buffer);
enthalpy = atof(vs[4].c_str()) * EV_TO_KCAL_PER_MOL;
}
if (strstr(buffer, "Current cell volume =")) {
hasVolumeData = true;
tokenize(vs, buffer);
volume = atof(vs[4].c_str());
}
if (strstr(buffer, " * Pressure:")) {
hasPressureData = true;
tokenize(vs, buffer);
pressure = atof(vs[2].c_str());
}
}
if (coordsAreFractional) {
FOR_ATOMS_OF_MOL(atom, pmol) {
atom->SetVector(cell->FractionalToCartesian(cell->WrapFractionalCoordinate(atom->GetVector())));
}
}
if (hasEnthalpyData) {
if (hasVolumeData && hasPressureData) {
double energy, pv;
double enthalpy_ev, pv_ev;
pv = volume * pressure * GPA_A3_TO_KCAL_PER_MOL;
energy = enthalpy - pv;
pv_ev = pv / EV_TO_KCAL_PER_MOL;
enthalpy_ev = enthalpy / EV_TO_KCAL_PER_MOL;
OBPairData *opd_enthalpy = new OBPairData();
OBPairData *opd_enthalpy_pv = new OBPairData();
OBPairData *opd_enthalpy_ev = new OBPairData();
OBPairData *opd_enthalpy_pv_ev = new OBPairData();
opd_enthalpy->SetAttribute("Enthalpy (kcal/mol)");
opd_enthalpy_pv->SetAttribute("Enthalpy PV term (kcal/mol)");
opd_enthalpy_ev->SetAttribute("Enthalpy (eV)");
opd_enthalpy_pv_ev->SetAttribute("Enthalpy PV term (eV)");
snprintf(tag, BUFF_SIZE, "%f", enthalpy);
opd_enthalpy->SetValue(tag);
snprintf(tag, BUFF_SIZE, "%f", pv);
opd_enthalpy_pv->SetValue(tag);
snprintf(tag, BUFF_SIZE, "%f", enthalpy_ev);
opd_enthalpy_ev->SetValue(tag);
snprintf(tag, BUFF_SIZE, "%f", pv_ev);
opd_enthalpy_pv_ev->SetValue(tag);
pmol->SetData(opd_enthalpy);
pmol->SetData(opd_enthalpy_pv);
pmol->SetData(opd_enthalpy_ev);
pmol->SetData(opd_enthalpy_pv_ev);
pmol->SetEnergy(energy);
}
else { pmol->SetEnergy(enthalpy);
}
}
pmol->SetData(cell);
pmol->EndModify();
return true;
}
}