# IODATA is an input and output module for quantum chemistry.
# Copyright (C) 2011-2019 The IODATA Development Team
#
# This file is part of IODATA.
#
# IODATA 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; either version 3
# of the License, or (at your option) any later version.
#
# IODATA 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>
# --
"""GROMACS gro file format.
Files with the gro file extension contain a molecular structure in Gromos87 format.
GROMACS gro files can be used as trajectory by simply concatenating files.
http://manual.gromacs.org/current/reference-manual/file-formats.html#gro
"""
from collections.abc import Iterator
import numpy as np
from ..docstrings import document_load_many, document_load_one
from ..utils import LineIterator, nanometer, picosecond
__all__ = ()
PATTERNS = ["*.gro"]
[docs]
@document_load_one("GRO", ["atcoords", "atffparams", "cellvecs", "extra", "title"])
def load_one(lit: LineIterator) -> dict:
"""Do not edit this docstring. It will be overwritten."""
data = _helper_read_frame(lit)
title = data[0]
time = data[1]
resnums = np.array(data[2])
resnames = np.array(data[3])
attypes = np.array(data[4])
atcoords = data[5]
velocities = data[6]
cellvecs = data[7]
atffparams = {"attypes": attypes, "resnames": resnames, "resnums": resnums}
extra = {"time": time, "velocities": velocities}
return {
"atcoords": atcoords,
"atffparams": atffparams,
"cellvecs": cellvecs,
"extra": extra,
"title": title,
}
[docs]
@document_load_many("GRO", ["atcoords", "atffparams", "cellvecs", "extra", "title"])
def load_many(lit: LineIterator) -> Iterator[dict]:
"""Do not edit this docstring. It will be overwritten."""
# gro files can be used as trajectory by simply concatenating files,
# making it trivial to load many frames.
try:
while True:
yield load_one(lit)
except StopIteration:
return
def _helper_read_frame(lit: LineIterator) -> tuple:
"""Read one frame."""
# Read the first line, get the title and try to get the time.
# Time field is optional.
line = next(lit)
title = line.split(",")[0] if "t=" in line else line[:-1]
time = 0.0
if "t=" in line:
time = float(line.split("t=")[1]) * picosecond
# Read the second line for number of atoms.
natoms = int(next(lit))
# Read the atom lines
resnums = []
resnames = []
attypes = []
pos = np.zeros((natoms, 3), np.float32)
vel = np.zeros((natoms, 3), np.float32)
for i in range(natoms):
line = next(lit)
resnums.append(int(line[:5]))
resnames.append(line[5:10].split()[-1])
attypes.append(line[10:15].split()[-1])
words = line[22:].split()
pos[i, 0] = float(words[0])
pos[i, 1] = float(words[1])
pos[i, 2] = float(words[2])
vel[i, 0] = float(words[3])
vel[i, 1] = float(words[4])
vel[i, 2] = float(words[5])
pos *= nanometer # atom coordinates are in nanometers
vel *= nanometer / picosecond
# Read the cell line
cell = np.zeros((3, 3), np.float32)
words = next(lit).split()
if len(words) >= 3:
cell[0, 0] = float(words[0])
cell[1, 1] = float(words[1])
cell[2, 2] = float(words[2])
if len(words) == 9:
cell[1, 0] = float(words[3])
cell[2, 0] = float(words[4])
cell[0, 1] = float(words[5])
cell[2, 1] = float(words[6])
cell[0, 2] = float(words[7])
cell[1, 2] = float(words[8])
cell *= nanometer
return title, time, resnums, resnames, attypes, pos, vel, cell