Hi! I posted patch #3311952 which adds Gromacs GRO file support to OpenBabel. That patch contains groformat.cpp and adds it to CMakeLists.txt.
Here I attach just the groformat.cpp file for the interested. Any comments and suggestions for improvements are welcome. Regards, Reinis Danne
/********************************************************************** Copyright (C) 2010 by Reinis Danne Some portions Copyright (C) 2004 by Chris Morley This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ***********************************************************************/ #include <openbabel/babelconfig.h> #include <openbabel/obmolecformat.h> #include <sstream> #include <iomanip> using namespace std; namespace OpenBabel { class GROFormat : public OBMoleculeFormat { public: //Register this format type ID in the constructor GROFormat() { /* GRO is the file extension and is case insensitive. A MIME type can be added as an optional third parameter. Multiple file extensions can be registered by adding extra statements.*/ OBConversion::RegisterFormat("gro",this); /* If there are any format specific options they should be registered here so that the commandline interface works properly. The first parameter is the option name. If it is a single letter it can be concatinated with other single letter options. For output options it can be multicharcter and is then written as --optionname on the command line. The third parameter is the number of parameters the option takes. Currently this is either 1 or 0 and if it is 0 can be omitted for output options. The parameter is always text and needs to be parsed to extract a number. Options can apply when writing - 4th parameter is OBConversion::OUTOPTIONS or can be omitted as shown. A single letter output option is preceded by -x on the command line. Or options can apply to the input format - the 4th parameter is OBConversion::INOPTIONS. They are then preceded by -a on the command line. Each option letter may be reused in other formats, but within the same group, INOPTIONS or OUTOPTIONS, must take the same number of parameters (0 or 1). There will be an error message when OpenBabel runs if there are conflicts between formats. A list of formats currently used (which may not be comprehensive) is in docs/options.html. */ OBConversion::RegisterOptionParam("f", this, 1); OBConversion::RegisterOptionParam("n", this); OBConversion::RegisterOptionParam("s", this, 0, OBConversion::INOPTIONS); } /* The first line of the description should be a brief identifier, <40 chars, because it is used in dropdown lists, etc. in some user interfaces. The rest is optional. Describe any format specific options here. This text is parsed to provide checkboxes, etc for the GUI (for details click the control menu), so please try to keep to a similar form. Write options are the most common, and the "Write" is optional. The option f takes a text parameter, so that it is essential that the option is registered in the constructor of the class. Finish the options with a blank line as shown, if there are more than one group of options, or if there are further comments after them. */ virtual const char* Description() //required { return "GRO format\n" "This is GRO file format as used in Gromacs.\n" "Right now there is only limited support for element perception. It works " "for \nelements with one letter symbols if the atomtype starts with the " "same letter.\n\n" "Read Options e.g. -as\n" " s Consider single bonds only\n" " b Disable bonding entierly\n" ; }; //Optional URL where the file format is specified virtual const char* SpecificationURL(){return "http://manual.gromacs.org/current/online/gro.html";}; /* This optional function is for formats which can contain more than one molecule. It is used to quickly position the input stream after the nth molecule without requiring to convert and discard all the n molecules. See obconversion.cpp for details.*/ virtual int SkipObjects(int n, OBConversion* pConv) { string line = ""; int natoms = 0; int nlines = 0; if (n == 0) n++; istream& ifs = *pConv->GetInStream(); getline(ifs, line); ifs >> natoms; nlines = (natoms+3)*n; while (ifs && --nlines) { getline(ifs, line); } return ifs.good() ? 1 : -1; }; //////////////////////////////////////////////////// /// Declarations for the "API" interface functions. Definitions are below virtual bool ReadMolecule(OBBase* pOb, OBConversion* pConv); virtual bool WriteMolecule(OBBase* pOb, OBConversion* pConv); }; //////////////////////////////////////////////////// //Make an instance of the format class GROFormat theGROFormat; ///////////////////////////////////////////////////////////////// bool GROFormat::ReadMolecule(OBBase* pOb, OBConversion* pConv) { OBMol* pmol = pOb->CastAndClear<OBMol>(); if(pmol==NULL) return false; istream& ifs = *pConv->GetInStream(); pmol->BeginModify(); /** Parse the input stream and use the OpenBabel API to populate the OBMol **/ char buffer[BUFF_SIZE]; string line = ""; stringstream errorMsg; int natoms = 0; string title = ""; long int resid = 0; // 5 string resname = ""; //5 string atomtype = ""; //5 long int atomid = 0; //5 double x = 0.0; // 8.3 double y = 0.0; // 8.3 double z = 0.0; // 8.3 double vx = 0.0; // 8.4 double vy = 0.0; // 8.4 double vz = 0.0; // 8.4 string tempstr = ""; long int residx = 0; OBAtom* atom; OBResidue* res; OBVectorData* velocity; if (!ifs || ifs.peek() == EOF) { return false; // Trying to read past end of the file } if (!ifs.getline(buffer, BUFF_SIZE)) { errorMsg << "Problems reading a GRO file: " << "Cannot read the first line!"; obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); return false; } // Get the title title.assign(buffer); if (title.size() < 1) { title = pConv->GetTitle(); pmol->SetTitle(title); } else { pmol->SetTitle(title); } if (!ifs.getline(buffer, BUFF_SIZE)) { errorMsg << "Problems reading a GRO file: " << "Cannot read the second line!"; obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); return false; } // Get the number of atoms stringstream(buffer) >> natoms; if (natoms < 1) { errorMsg << "Problems reading a GRO file: " << "There are no atoms in the file or the second line is" << " incorrectly written."; obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); return false; } pmol->ReserveAtoms(natoms); // Read all atom records for (int i=1; i<=natoms; i++) { if (!ifs.getline(buffer,BUFF_SIZE)) { errorMsg << "Problems reading a GRO file: " << "Could not read line #" << i+2 << ", file error." << endl << " According to the second line, there should be " << natoms << " atoms, and therefore " << natoms+3 << " lines in the file."; obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); return false; } // Get atom atom = pmol->NewAtom(); line = buffer; tempstr.assign(line,0,5); stringstream(tempstr) >> resid; resname.assign(line,5,5); Trim(resname); atomtype.assign(line,10,5); Trim(atomtype); // Not used, OB assigns its own indizes //tempstr.assign(line,15,5); //stringstream(tempstr) >> atomid; tempstr.assign(line,20,8); stringstream(tempstr) >> x; tempstr.assign(line,28,8); stringstream(tempstr) >> y; tempstr.assign(line,36,8); stringstream(tempstr) >> z; if (line.size() > 44) { tempstr.assign(line,44,8); stringstream(tempstr) >> vx; tempstr.assign(line,52,8); stringstream(tempstr) >> vy; tempstr.assign(line,60,8); stringstream(tempstr) >> vz; velocity = new OBVectorData(); velocity->SetData(vx, vy, vz); velocity->SetAttribute("Velocity"); velocity->SetOrigin(fileformatInput); atom->SetData(velocity); } // OB translates this and, e.g., OW1 turns into O3 // Type conversion should be done explicitly if that needs to be // controlled. atom->SetType(atomtype); // Set coordinates of the atom, multiply by 10 to convert from nm to // angstrom atom->SetVector(x*10, y*10, z*10); if (resid == residx) { // Add atom to an existing residue res->AddAtom(atom); } else { // Create new residue and use that res = pmol->NewResidue(); res->SetName(resname); res->SetNum(resid); res->AddAtom(atom); residx = resid; } // Atom type has to be set in residues as AtomID res->SetAtomID(atom, atomtype); // For determining the element this doesn't work: // atom->SetAtomicNum(etab.GetAtomicNum(atom->GetType())); // // So the element simbol should be found while reading in the file. It // could be possible to provide an option for external atomtype<->element // simbol map. Now such functionality is not implemented. // TODO: Make element perception optional and improve it string element = ""; if (atomtype[0] == 'C') { if (atomtype.find_first_of("aloru",1) == 1) { element.assign(atomtype,0,2); } else { element.assign(atomtype,0,1); } } else if (atomtype[0] == 'N') { if (atomtype.find_first_of("abei",1) == 1) { element.assign(atomtype,0,2); } else { element.assign(atomtype,0,1); } } else { element.assign(atomtype,0,1); } atom->SetAtomicNum(etab.GetAtomicNum(element.data())); } // Get periodic box if (!ifs.getline(buffer,BUFF_SIZE)) { errorMsg << "Problems reading a GRO file: " << "Could not read box vectors!" << endl; obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); return false; } double v1x=0.0, v2y=0.0, v3z=0.0; double v1y=0.0, v1z=0.0, v2x=0.0; double v2z=0.0, v3x=0.0, v3y=0.0; stringstream ss(buffer); if (!ss) { errorMsg << "Problems reading a GRO file: " << "Could not read box vectors!" << endl; obErrorLog.ThrowError(__FUNCTION__, errorMsg.str(), obWarning); return false; } ss >> v1x >> v2y >> v3z; if (ss) { ss >> v1y >> v1z >> v2x; ss >> v2z >> v3x >> v3y; } if (!(v1x == 0.0 and v2y == 0.0 and v3z == 0.0 and v1y == 0.0 and v1z == 0.0 and v2x == 0.0 and v2z == 0.0 and v3x == 0.0 and v3y == 0.0)) { // Set box vectors and convert nm to angstroms const vector3 v1(v1x*10,v1y*10,v1z*10); const vector3 v2(v2x*10,v2y*10,v2z*10); const vector3 v3(v3x*10,v3y*10,v3z*10); OBUnitCell* cell = new OBUnitCell(); cell->SetData(v1, v2, v3); pmol->SetData(cell); } pmol->EndModify(); // Input options -as and -ab if (!pConv->IsOption("b", OBConversion::INOPTIONS)) { pmol->ConnectTheDots(); if (!pConv->IsOption("s", OBConversion::INOPTIONS)) { pmol->PerceiveBondOrders(); } } /* For multi-molecule formats, leave the input stream at the start of the next molecule, ready for this routine to be called again. * Return true if ok. Returning false means discard the OBMol and stop converting, unless the -e option is set. With a multi-molecule inputstream this will skip the current molecule and continue with the next, if SkipObjects() has been defined. If it has not, and continuation after errors is still required, it is necessary to leave the input stream at the beginning of next object when returning false;*/ return true; } //////////////////////////////////////////////////////////////// bool GROFormat::WriteMolecule(OBBase* pOb, OBConversion* pConv) { OBMol* pmol = dynamic_cast<OBMol*>(pOb); if(pmol==NULL) return false; // Stop converting ostream& ofs = *pConv->GetOutStream(); /** Write the representation of the OBMol molecule to the output stream **/ OBVectorData* vector; vector3 v; OBAtom* atom; OBResidue* res; long int atIdx = 0; long int resIdx = 0; ofs << pmol->GetTitle() << endl; ofs << pmol->NumAtoms() << endl; ofs.setf(ios::fixed); FOR_ATOMS_OF_MOL(atom, pmol) { res = atom->GetResidue(); resIdx = res->GetNum(); // Check if residue index excedes the field width and should be wrapped if (resIdx > 99999) { ofs << setw(5) << resIdx - 100000*int(resIdx/100000); } else { ofs << setw(5) << res->GetNum(); } ofs << setw(5) << left << res->GetName(); ofs << setw(5) << right << res->GetAtomID(&(*atom)); atIdx = atom->GetIdx(); // Check if atom index excedes the field width and should be wrapped if (atIdx > 99999) { ofs << setw(5) << atIdx - 100000*int(atIdx/100000); } else { ofs << setw(5) << atIdx; } ofs.precision(3); // OpenBabel uses angstrom, so converting to nm by dividing by 10 ofs << setw(8) << atom->x()/10 << setw(8) << atom->y()/10 << setw(8) << atom->z()/10; if (atom->GetData("Velocity")) { vector = (OBVectorData*) atom->GetData("Velocity"); v = vector->GetData(); ofs.precision(4); ofs << setw(8) << v.x() << setw(8) << v.y() << setw(8) << v.z(); } ofs << endl; } // On the last line of the file goes periodic box specification if (pmol->HasData(OBGenericDataType::UnitCell)) { OBUnitCell* cell = (OBUnitCell*) pmol->GetData(OBGenericDataType::UnitCell); matrix3x3 m = cell->GetCellMatrix(); vector3 v1 = m.GetRow(0); vector3 v2 = m.GetRow(1); vector3 v3 = m.GetRow(2); // Gromacs itself uses precision of 5, so it should be fine ofs.precision(5); ofs << " " << v1.x()/10 << " " << v2.y()/10 << " " << v3.z()/10; // If there is any non-zero value among others, then write them all const double TRESHOLD = 1.0e-8; if (fabs(v1.y()) > TRESHOLD or fabs(v1.z()) > TRESHOLD or fabs(v2.x()) > TRESHOLD or fabs(v2.z()) > TRESHOLD or fabs(v3.x()) > TRESHOLD or fabs(v3.y()) > TRESHOLD) { ofs << " " << v1.y()/10 << " " << v1.z()/10 << " " << v2.x()/10 << " " << v2.z()/10 << " " << v3.x()/10 << " " << v3.y()/10; } } else { // Set to zero if there is no box data in the molecule ofs << " 0.00000 0.00000 0.00000"; } ofs << endl; return true; } } //namespace OpenBabel
------------------------------------------------------------------------------ Simplify data backup and recovery for your virtual environment with vRanger. Installation's a snap, and flexible recovery options mean your data is safe, secure and there when you need it. Discover what all the cheering's about. Get your free trial download today. http://p.sf.net/sfu/quest-dev2dev2
_______________________________________________ OpenBabel-Devel mailing list OpenBabel-Devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openbabel-devel