1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
|
# -*- Mode: python; tab-width: 4; indent-tabs-mode:nil; -*-
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
#
# MDAnalysis --- https://www.mdanalysis.org
# Copyright (c) 2006-2017 The MDAnalysis Development Team and contributors
# (see the file AUTHORS for the full list of names)
#
# Released under the Lesser GNU Public Licence, v2.1 or any higher version
#
# Please cite your use of MDAnalysis in published work:
#
# R. J. Gowers, M. Linke, J. Barnoud, T. J. E. Reddy, M. N. Melo, S. L. Seyler,
# D. L. Dotson, J. Domanski, S. Buchoux, I. M. Kenney, and O. Beckstein.
# MDAnalysis: A Python package for the rapid analysis of molecular dynamics
# simulations. In S. Benthall and S. Rostrup editors, Proceedings of the 15th
# Python in Science Conference, pages 102-109, Austin, TX, 2016. SciPy.
# doi: 10.25080/majora-629e541a-00e
#
# N. Michaud-Agrawal, E. J. Denning, T. B. Woolf, and O. Beckstein.
# MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations.
# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787
#
#
"""GAMESS trajectory reader --- :mod:`MDAnalysis.coordinates.GMS`
=================================================================
Resources: the GMS output format is a common output format for different
GAMESS distributions: US-GAMESS, Firefly (PC-GAMESS) and GAMESS-UK.
Current version was approbated with US-GAMESS & Firefly only.
There appears to be no rigid format definition so it is likely users
will need to tweak the :class:`GMSReader`.
.. autoclass:: GMSReader
:members:
"""
import os
import errno
import re
from . import base
import MDAnalysis.lib.util as util
from MDAnalysis.lib.util import store_init_arguments
class GMSReader(base.ReaderBase):
"""Reads from an GAMESS output file
:Data:
ts
Timestep object containing coordinates of current frame
:Methods:
``len(out)``
return number of frames in out
``for ts in out:``
iterate through trajectory
Note
----
:class:`GMSReader` can read both uncompressed (``foo.out``) and
compressed (``foo.out.bz2`` or ``foo.out.gz``) files;
decompression is handled on the fly
.. versionchanged:: 0.11.0
Frames now 0-based instead of 1-based.
Added `dt` and `time_offset` keywords (passed to :class:`Timestep`).
"""
format = "GMS"
# these are assumed!
units = {"time": "ps", "length": "Angstrom"}
@store_init_arguments
def __init__(self, outfilename, **kwargs):
super(GMSReader, self).__init__(outfilename, **kwargs)
# the filename has been parsed to be either b(g)zipped or not
self.outfile = util.anyopen(self.filename)
# note that, like for xtc and trr files, _n_atoms and _n_frames are used quasi-private variables
# to prevent the properties being recalculated
# this is because there is no indexing so the way it measures the number of frames is to read the whole file!
self._n_atoms = None
self._n_frames = None
self._runtyp = None
self.ts = self._Timestep(0) # need for properties initial calculations
# update runtyp property
self.runtyp
if not self.runtyp in ["optimize", "surface"]:
raise AttributeError("Wrong RUNTYP= " + self.runtyp)
self.ts = self._Timestep(self.n_atoms, **self._ts_kwargs)
# update n_frames property
self.n_frames
# Read in the first timestep
self._read_next_timestep()
@property
def runtyp(self):
"""RUNTYP property of the GAMESS run"""
if self._runtyp is not None: # return cached value
return self._runtyp
try:
self._runtyp = self._determine_runtyp()
except IOError:
return 0
else:
return self._runtyp
def _determine_runtyp(self):
with util.openany(self.filename) as out:
for line in out:
m = re.match(r"^.*RUNTYP=([A-Z]+)\s+.*", line)
if m is not None:
res = m.group(1).lower()
break
return res
@property
def n_atoms(self):
"""number of atoms in a frame"""
if self._n_atoms is not None: # return cached value
return self._n_atoms
try:
self._n_atoms = self._read_out_natoms()
except IOError:
return 0
else:
return self._n_atoms
def _read_out_natoms(self):
with util.openany(self.filename) as out:
for line in out:
m = re.match(
r"\s*TOTAL NUMBER OF ATOMS\s*=\s*([0-9]+)\s*", line
)
if m is not None:
res = int(m.group(1))
break
return res
@property
def n_frames(self):
if self._n_frames is not None: # return cached value
return self._n_frames
try:
self._n_frames = self._read_out_n_frames()
except IOError:
return 0
else:
return self._n_frames
def _read_out_n_frames(self):
if self.runtyp == "optimize":
trigger = re.compile(b"^.NSERCH=.*")
elif self.runtyp == "surface":
trigger = re.compile(b"^.COORD 1=.*")
self._offsets = offsets = []
with util.openany(self.filename, "rb") as out:
line = True
while not line == b"": # while not EOF
line = out.readline()
if re.match(trigger, line):
offsets.append(out.tell() - len(line))
return len(offsets)
def _read_frame(self, frame):
self.outfile.seek(self._offsets[frame])
self.ts.frame = frame - 1 # gets +1'd in _read_next
return self._read_next_timestep()
def _read_next_timestep(self, ts=None):
# check that the timestep object exists
if ts is None:
ts = self.ts
# check that the outfile object exists; if not reopen the trajectory
if self.outfile is None:
self.open_trajectory()
x = []
y = []
z = []
flag = 0
counter = 0
for line in self.outfile:
if self.runtyp == "optimize":
if (flag == 0) and (
re.match(r"^.NSERCH=.*", line) is not None
):
flag = 1
continue
if (flag == 1) and (
re.match(r"^ COORDINATES OF ALL ATOMS ARE ", line)
is not None
):
flag = 2
continue
if (flag == 2) and (re.match(r"^\s*-+\s*", line) is not None):
flag = 3
continue
if flag == 3 and counter < self.n_atoms:
words = line.split()
x.append(float(words[2]))
y.append(float(words[3]))
z.append(float(words[4]))
counter += 1
elif self.runtyp == "surface":
if (flag == 0) and (
re.match(r"^.COORD 1=\s*([-]?[0-9]+\.[0-9]+).*", line)
is not None
):
flag = 1
continue
if (flag == 1) and (
re.match(
r"^\s*HAS ENERGY VALUE\s*([-]?[0-9]+\.[0-9]+)\s*", line
)
is not None
):
flag = 3
continue
if flag == 3 and counter < self.n_atoms:
words = line.split()
x.append(float(words[1]))
y.append(float(words[2]))
z.append(float(words[3]))
counter += 1
# stop when the cursor has reached the end of that block
if counter == self._n_atoms:
ts._x[:] = (
x # more efficient to do it this way to avoid re-creating the numpy arrays
)
ts._y[:] = y
ts._z[:] = z
# print ts.frame
ts.frame += 1
return ts
raise EOFError
def _reopen(self):
self.close()
self.open_trajectory()
def open_trajectory(self):
if self.outfile is not None:
raise IOError(
errno.EALREADY, "GMS file already opened", self.filename
)
if not os.path.exists(self.filename):
# must check; otherwise might segmentation fault
raise IOError(errno.ENOENT, "GMS file not found", self.filename)
self.outfile = util.anyopen(self.filename)
# reset ts
ts = self.ts
ts.frame = -1
return self.outfile
def close(self):
"""Close out trajectory file if it was open."""
if self.outfile is None:
return
self.outfile.close()
self.outfile = None
|