/* $RCSfile$
* $Author$
* $Date$
* $Revision$
*
* Copyright (C) 2003-2007 The Chemistry Development Kit (CDK) project
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.openscience.cdk.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.io.StringReader;
import javax.vecmath.Point3d;
import org.openscience.cdk.PhysicalConstants;
import org.openscience.cdk.annotations.TestClass;
import org.openscience.cdk.annotations.TestMethod;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IChemFile;
import org.openscience.cdk.interfaces.IChemModel;
import org.openscience.cdk.interfaces.IChemObject;
import org.openscience.cdk.interfaces.IChemSequence;
import org.openscience.cdk.interfaces.IMolecule;
import org.openscience.cdk.interfaces.IMoleculeSet;
import org.openscience.cdk.io.formats.GamessFormat;
import org.openscience.cdk.io.formats.IResourceFormat;
/**
* A reader for GAMESS log file.
*
* <p><b>Expected behaviour</b>:
* <br>The "GamessReader" object is able to read GAMESS output log file format.
*
* <p><b>Limitations</b>: <br>This reader was developed from a small set of
* example log files, and therefore, is not guaranteed to properly read all
* GAMESS output. If you have problems, please contact the author of this code,
* not the developers of GAMESS.
*
* <!-- <p><b>State information</b>: <br> [] -->
* <!-- <p><b>Dependencies</b>: <br> [all OS/Software/Hardware dependencies] -->
*
* <p><b>Implementation</b>
* <br>Available feature(s):
* <ul>
* <li><b>Molecular coordinates</b>: Each set of coordinates is added to the ChemFile in the order they are found.</li>
* </ul>
* Unavailable feature(s):
* <ul>
* <!-- <li><b>GAMESS version number</b>: The version number can be retrieved.</li> -->
* <!-- <li><b>Point group symetry information</b>: The point group is associated with the set of molecules.</li> -->
* <!-- <li><b>MOPAC charges</b>: The point group is associated with the set of molecules.</li> -->
* <li><b>Energies</b>: They are associated with the previously read set of coordinates.</li>
* <li><b>Normal coordinates of vibrations</b>: They are associated with the previously read set of coordinates.</li>
* </ul>
*
* <!-- <p><b>Security:</b> -->
*
* <p><b>References</b>:
* <br><a href="http://www.msg.ameslab.gov/GAMESS/GAMESS.html">GAMESS</a> is a
* quantum chemistry program by Gordon research group atIowa State University.
*
* @cdk.module extra
* @cdk.githash
* @cdk.keyword Gamess
* @cdk.keyword file format
* @cdk.keyword output
* @cdk.keyword log file
*
* @author Bradley A. Smith
*
* <!-- @see #GamessWriter(Reader) -->
*/
//TODO Update class comments with appropriate information.
//TODO Update "see" tag with reference to GamessWriter when it will be implemented.
//TODO Update "author" tag with appropriate information.
@TestClass("org.openscience.cdk.io.GamessReaderTest")
public class GamessReader extends DefaultChemObjectReader {
/**
* Boolean constant used to specify that the coordinates are given in Bohr units.
*/
public static final boolean BOHR_UNIT = true;
/**
* Double constant that contains the convertion factor from Bohr unit to
* Ångstrom unit.
*/
//TODO Check the accuracy of this comment.
public static final double BOHR_TO_ANGSTROM = 0.529177249;
/**
* Boolean constant used to specify that the coordinates are given in Ångstrom units.
*/
public static final boolean ANGSTROM_UNIT = false;
/**
* The "BufferedReader" object used to read data from the "file system" file.
*
* @see org.openscience.cdk.io.GamessReader#GamessReader(Reader)
*/
//TODO Improve field comment.
//TODO Answer the question : When is it opened and when is it closed?
private BufferedReader input;
/**
* Constructs a new "GamessReader" object given a "Reader" object as input.
*
* <p>The "Reader" object may be an instantiable object from the "Reader"
* hierarchy.
* <br>For more detail about the "Reader" objects that are really accepted
* by this "GamessReader" see <code>accepts(IChemObject)</code> method
* documentation.
*
* @param inputReader The "Reader" object given as input parameter.
*
* @see #accepts(Class)
* @see java.io.Reader
*
*/
public GamessReader(Reader inputReader) {
this.input = new BufferedReader(inputReader);
}
public GamessReader(InputStream input) {
this(new InputStreamReader(input));
}
public GamessReader() {
this(new StringReader(""));
}
/* (non-Javadoc) (Javadoc is automaticly inherited from the link below)
* @see org.openscience.cdk.io.ChemObjectIO#accepts(org.openscience.cdk.ChemObject)
*/
//TODO Update comment with appropriate information to comply Constructor's documentation.
@TestMethod("testGetFormat")
public IResourceFormat getFormat() {
return GamessFormat.getInstance();
}
@TestMethod("testSetReader_Reader")
public void setReader(Reader reader) throws CDKException {
this.input = new BufferedReader(input);
}
@TestMethod("testSetReader_InputStream")
public void setReader(InputStream input) throws CDKException {
setReader(new InputStreamReader(input));
}
@TestMethod("testAccepts")
public boolean accepts(Class classObject) {
Class[] interfaces = classObject.getInterfaces();
for (int i=0; i<interfaces.length; i++) {
if (IChemFile.class.equals(interfaces[i])) return true;
}
Class superClass = classObject.getSuperclass();
if (superClass != null) return this.accepts(superClass);
return false;
}
/* (non-Javadoc) (Javadoc is automaticly inherited from the link below)
* @see org.openscience.cdk.io.ChemObjectReader#read(org.openscience.cdk.ChemObject)
*/
public IChemObject read(IChemObject object) throws CDKException {
if (object instanceof IChemFile) {
try {
return (IChemObject) readChemFile((IChemFile)object);
} catch (IOException e) {
return null;
}
} else {
throw new CDKException("Only supported is reading of ChemFile objects.");
}
}
/**
* Reads data from the "file system" file through the use of the "input"
* field, parses data and feeds the ChemFile object with the extracted data.
*
* @return A ChemFile containing the data parsed from input.
*
* @throws IOException may be thrown buy the <code>this.input.readLine()</code> instruction.
*
* @see org.openscience.cdk.io.GamessReader#input
*/
//TODO Answer the question : Is this method's name appropriate (given the fact that it do not read a ChemFile object, but return it)?
private IChemFile readChemFile(IChemFile file) throws IOException {
IChemSequence sequence = file.getBuilder().newChemSequence(); // TODO Answer the question : Is this line needed ?
IChemModel model = file.getBuilder().newChemModel(); // TODO Answer the question : Is this line needed ?
IMoleculeSet moleculeSet = file.getBuilder().newMoleculeSet();
model.setMoleculeSet(moleculeSet); //TODO Answer the question : Should I do this?
sequence.addChemModel(model); //TODO Answer the question : Should I do this?
file.addChemSequence(sequence); //TODO Answer the question : Should I do this?
String currentReadLine = this.input.readLine();
while (this.input.ready() == true && (currentReadLine != null)) {
/*
* There are 2 types of coordinate sets:
* - bohr coordinates sets (if statement)
* - angstr???m coordinates sets (else statement)
*/
if (currentReadLine.indexOf("COORDINATES (BOHR)") >= 0) {
/*
* The following line do no contain data, so it is ignored.
*/
this.input.readLine();
moleculeSet.addMolecule(this.readCoordinates(
file.getBuilder().newMolecule(), GamessReader.BOHR_UNIT
));
//break; //<- stops when the first set of coordinates is found.
} else if (currentReadLine.indexOf(" COORDINATES OF ALL ATOMS ARE (ANGS)") >= 0) {
/*
* The following 2 lines do no contain data, so it are ignored.
*/
this.input.readLine();
this.input.readLine();
moleculeSet.addMolecule(this.readCoordinates(
file.getBuilder().newMolecule(), GamessReader.ANGSTROM_UNIT
));
//break; //<- stops when the first set of coordinates is found.
}
currentReadLine = this.input.readLine();
}
return file;
}
/**
* Reads a set of coordinates from the "file system" file through the use of
* the "input" field, scales coordinate to angstr???m unit, builds each atom with
* the right associated coordinates, builds a new molecule with these atoms
* and returns the complete molecule.
*
* <p><b>Implementation</b>:
* <br>Dummy atoms are ignored.
*
* @param coordinatesUnits The unit in which coordinates are given.
*
* @throws IOException may be thrown by the "input" object.
*
* @see org.openscience.cdk.io.GamessReader#input
*/
//TODO Update method comments with appropriate information.
private IMolecule readCoordinates(IMolecule molecule, boolean coordinatesUnits) throws IOException {
/*
* Coordinates must all be given in angstr???ms.
*/
double unitScaling = GamessReader.scalesCoordinatesUnits(coordinatesUnits);
String retrievedLineFromFile;
while (this.input.ready() == true) {
retrievedLineFromFile = this.input.readLine();
/*
* A coordinate set is followed by an empty line, so when this line
* is reached, there are no more coordinates to add to the current set.
*/
if ((retrievedLineFromFile == null) || (retrievedLineFromFile.trim().length() == 0)) {
break;
}
int atomicNumber;
String atomicSymbol;
//StringReader sr = new StringReader(retrievedLineFromFile);
StreamTokenizer token = new StreamTokenizer(new StringReader(retrievedLineFromFile));
/*
* The first token is ignored. It contains the atomic symbol and may
* be concatenated with a number.
*/
token.nextToken();
if (token.nextToken() == StreamTokenizer.TT_NUMBER) {
atomicNumber = (int) token.nval;
atomicSymbol = this.identifyAtomicSymbol(atomicNumber);
/*
* Dummy atoms are assumed to be given with an atomic number set
* to zero. We will do not add them to the molecule.
*/
if (atomicNumber == 0) {
continue;
}
} else {
throw new IOException("Error reading coordinates");
}
/*
* Atom's coordinates are stored in an array.
*/
double[] coordinates = new double[3];
for (int i = 0; i < coordinates.length; i++) {
if (token.nextToken() == StreamTokenizer.TT_NUMBER) {
coordinates[i] = token.nval * unitScaling;
} else {
throw new IOException("Error reading coordinates");
}
}
IAtom atom = molecule.getBuilder().newAtom(atomicSymbol, new Point3d(coordinates[0],coordinates[1],coordinates[2]));
molecule.addAtom(atom);
}
return molecule;
}
/**
* Identifies the atomic symbol of an atom given its default atomic number.
*
* <p><b>Implementation</b>:
* <br>This is not a definitive method. It will probably be replaced with a
* more appropriate one. Be advised that as it is not a definitive version,
* it only recognise atoms from Hydrogen (1) to Argon (18).
*
* @param atomicNumber The atomic number of an atom.
*
* @return The Symbol corresponding to the atom or "null" is the atom was not recognised.
*/
//TODO Update method comments with appropriate information.
private String identifyAtomicSymbol(int atomicNumber) {
String symbol;
switch (atomicNumber) {
case 1:
symbol = "H";
break;
case 2:
symbol = "He";
break;
case 3:
symbol = "Li";
break;
case 4:
symbol = "Be";
break;
case 5:
symbol = "B";
break;
case 6:
symbol = "C";
break;
case 7:
symbol = "N";
break;
case 8:
symbol = "O";
break;
case 9:
symbol = "F";
break;
case 10:
symbol = "Ne";
break;
case 11:
symbol = "Na";
break;
case 12:
symbol = "Mg";
break;
case 13:
symbol = "Al";
break;
case 14:
symbol = "Si";
break;
case 15:
symbol = "P";
break;
case 16:
symbol = "S";
break;
case 17:
symbol = "Cl";
break;
case 18:
symbol = "Ar";
break;
default:
symbol = null;
break;
}
return symbol;
}
/**
* Scales coordinates to Ångström unit if they are given in Bohr unit.
* If coordinates are already given in Ångström unit, then no modifications
* are performed.
*
* @param coordinatesUnits <code>BOHR_UNIT</code> if coordinates are given in Bohr unit and <code>ANGSTROM_UNIT</code>
* if they are given in Ångström unit.
*
* @return The scaling convertion factor: 1 if no scaling is needed and <code>BOHR_TO_ANGSTROM</code> if scaling has to be performed.
*
* @see org.openscience.cdk.PhysicalConstants#BOHR_TO_ANGSTROM
* @see org.openscience.cdk.io.GamessReader#BOHR_UNIT
* @see org.openscience.cdk.io.GamessReader#ANGSTROM_UNIT
*/
//TODO Update method comments with appropriate information.
private static double scalesCoordinatesUnits(boolean coordinatesUnits) {
if (coordinatesUnits == GamessReader.BOHR_UNIT) {
return PhysicalConstants.BOHR_TO_ANGSTROM;
} else { //condition is: (coordinatesUnits == GamessReader.ANGTROM_UNIT)
return (double) 1;
}
}
/* (non-Javadoc) (Javadoc is automaticly inherited from the link below)
* @see org.openscience.cdk.io.ChemObjectIO#close()
*/
//TODO Answer the question : What are all concerned ressources ?
@TestMethod("testClose")
public void close() throws IOException {
/*
* Closes the BufferedReader used to read the file content.
*/
input.close();
}
}