First draft of a python wrapper for gromacs library
This commit is contained in:
9
pygmx/__init__.py
Normal file
9
pygmx/__init__.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
"""
|
||||||
|
Python wrapper for gromacs tools.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from . import tpxio
|
||||||
|
from . import xtcio
|
||||||
|
from . import tngio
|
||||||
|
|
||||||
|
from .tpxio import TPXReader
|
12
pygmx/fileio.pxd
Normal file
12
pygmx/fileio.pxd
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
from utility cimport *
|
||||||
|
|
||||||
|
cdef extern from "gromacs/fileio/gmxfio.h":
|
||||||
|
ctypedef struct t_fileio:
|
||||||
|
pass
|
||||||
|
|
||||||
|
int gmx_fio_seek(t_fileio *fio, gmx_off_t fpos)
|
||||||
|
|
||||||
|
gmx_off_t gmx_fio_ftell(t_fileio *fio)
|
||||||
|
|
||||||
|
int xtc_seek_time(t_fileio *fio, real time, int natoms, gmx_bool bSeekForwardOnly)
|
7
pygmx/math.pxd
Normal file
7
pygmx/math.pxd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
from utility cimport real
|
||||||
|
|
||||||
|
cdef extern from "gromacs/math/vectypes.h":
|
||||||
|
ctypedef real rvec[3]
|
||||||
|
ctypedef real matrix[3][3]
|
||||||
|
ctypedef real tensor[3][3]
|
175
pygmx/mdtypes.pxd
Normal file
175
pygmx/mdtypes.pxd
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
|
||||||
|
from utility cimport *
|
||||||
|
from math cimport *
|
||||||
|
|
||||||
|
cdef extern from "gromacs/mdtypes/inputrec.h":
|
||||||
|
ctypedef struct t_simtemp:
|
||||||
|
pass
|
||||||
|
ctypedef struct t_lambda:
|
||||||
|
pass
|
||||||
|
ctypedef struct t_expanded:
|
||||||
|
pass
|
||||||
|
ctypedef struct t_rot:
|
||||||
|
pass
|
||||||
|
ctypedef struct t_IMD:
|
||||||
|
pass
|
||||||
|
ctypedef struct t_grpopts:
|
||||||
|
int ngtc
|
||||||
|
real *ref_t # Coupling temperature per group */
|
||||||
|
|
||||||
|
ctypedef struct t_cosines:
|
||||||
|
pass
|
||||||
|
ctypedef struct t_swapcoords:
|
||||||
|
pass
|
||||||
|
|
||||||
|
ctypedef struct t_inputrec:
|
||||||
|
int eI # Integration method */
|
||||||
|
int nsteps # number of steps to be taken */
|
||||||
|
int simulation_part # Used in checkpointing to separate chunks */
|
||||||
|
int init_step # start at a stepcount >0 (used w. convert-tpr) */
|
||||||
|
int nstcalcenergy # frequency of energy calc. and T/P coupl. upd. */
|
||||||
|
int cutoff_scheme # group or verlet cutoffs */
|
||||||
|
int ns_type # which ns method should we use? */
|
||||||
|
int nstlist # number of steps before pairlist is generated */
|
||||||
|
int ndelta # number of cells per rlong */
|
||||||
|
int nstcomm # number of steps after which center of mass */
|
||||||
|
# motion is removed */
|
||||||
|
int comm_mode # Center of mass motion removal algorithm */
|
||||||
|
int nstlog # number of steps after which print to logfile */
|
||||||
|
int nstxout # number of steps after which X is output */
|
||||||
|
int nstvout # id. for V */
|
||||||
|
int nstfout # id. for F */
|
||||||
|
int nstenergy # number of steps after which energies printed */
|
||||||
|
int nstxout_compressed # id. for compressed trj (.xtc,.tng) */
|
||||||
|
double init_t # initial time (ps) */
|
||||||
|
double delta_t # time step (ps) */
|
||||||
|
double x_compression_precision # precision of x in compressed trajectory file */
|
||||||
|
double fourier_spacing # requested fourier_spacing, when nk? not set */
|
||||||
|
int nkx, nky, nkz # number of k vectors in each spatial dimension*/
|
||||||
|
# for fourier methods for long range electrost.*/
|
||||||
|
int pme_order # interpolation order for PME */
|
||||||
|
double ewald_rtol # double space tolerance for Ewald, determines */
|
||||||
|
# the double/reciprocal space relative weight */
|
||||||
|
double ewald_rtol_lj # double space tolerance for LJ-Ewald */
|
||||||
|
int ewald_geometry # normal/3d ewald, or pseudo-2d LR corrections */
|
||||||
|
double epsilon_surface # Epsilon for PME dipole correction */
|
||||||
|
int ljpme_combination_rule # Type of combination rule in LJ-PME */
|
||||||
|
int ePBC # Type of periodic boundary conditions */
|
||||||
|
int bPeriodicMols # Periodic molecules */
|
||||||
|
bint bContinuation # Continuation run: starting state is correct */
|
||||||
|
int etc # temperature coupling */
|
||||||
|
int nsttcouple # interval in steps for temperature coupling */
|
||||||
|
bint bPrintNHChains # whether to print nose-hoover chains */
|
||||||
|
int epc # pressure coupling */
|
||||||
|
int epct # pressure coupling type */
|
||||||
|
int nstpcouple # interval in steps for pressure coupling */
|
||||||
|
double tau_p # pressure coupling time (ps) */
|
||||||
|
tensor ref_p # reference pressure (kJ/(mol nm^3)) */
|
||||||
|
tensor compress # compressability ((mol nm^3)/kJ) */
|
||||||
|
int refcoord_scaling # How to scale absolute reference coordinates */
|
||||||
|
rvec posres_com # The COM of the posres atoms */
|
||||||
|
rvec posres_comB # The B-state COM of the posres atoms */
|
||||||
|
int andersen_seed # Random seed for Andersen thermostat (obsolete) */
|
||||||
|
double verletbuf_tol # Per atom pair energy drift tolerance (kJ/mol/ps/atom) for list buffer */
|
||||||
|
double rlist # short range pairlist cut-off (nm) */
|
||||||
|
double rtpi # Radius for test particle insertion */
|
||||||
|
int coulombtype # Type of electrostatics treatment */
|
||||||
|
int coulomb_modifier # Modify the Coulomb interaction */
|
||||||
|
double rcoulomb_switch # Coulomb switch range start (nm) */
|
||||||
|
double rcoulomb # Coulomb cutoff (nm) */
|
||||||
|
double epsilon_r # relative dielectric constant */
|
||||||
|
double epsilon_rf # relative dielectric constant of the RF */
|
||||||
|
int implicit_solvent # No (=explicit water), or GBSA solvent models */
|
||||||
|
int gb_algorithm # Algorithm to use for calculation Born radii */
|
||||||
|
int nstgbradii # Frequency of updating Generalized Born radii */
|
||||||
|
double rgbradii # Cutoff for GB radii calculation */
|
||||||
|
double gb_saltconc # Salt concentration (M) for GBSA models */
|
||||||
|
double gb_epsilon_solvent # dielectric coeff. of implicit solvent */
|
||||||
|
double gb_obc_alpha # 1st scaling factor for Bashford-Case GB */
|
||||||
|
double gb_obc_beta # 2nd scaling factor for Bashford-Case GB */
|
||||||
|
double gb_obc_gamma # 3rd scaling factor for Bashford-Case GB */
|
||||||
|
double gb_dielectric_offset # Dielectric offset for Still/HCT/OBC */
|
||||||
|
int sa_algorithm # Algorithm for SA part of GBSA */
|
||||||
|
double sa_surface_tension # Energy factor for SA part of GBSA */
|
||||||
|
int vdwtype # Type of Van der Waals treatment */
|
||||||
|
int vdw_modifier # Modify the VdW interaction */
|
||||||
|
double rvdw_switch # Van der Waals switch range start (nm) */
|
||||||
|
double rvdw # Van der Waals cutoff (nm) */
|
||||||
|
int eDispCorr # Perform Long range dispersion corrections */
|
||||||
|
double tabext # Extension of the table beyond the cut-off, *
|
||||||
|
# * as well as the table length for 1-4 interac. */
|
||||||
|
double shake_tol # tolerance for shake */
|
||||||
|
int efep # free energy calculations */
|
||||||
|
t_lambda *fepvals # Data for the FEP state */
|
||||||
|
bint bSimTemp # Whether to do simulated tempering */
|
||||||
|
t_simtemp *simtempvals # Variables for simulated tempering */
|
||||||
|
bint bExpanded # Whether expanded ensembles are used */
|
||||||
|
t_expanded *expandedvals # Expanded ensemble parameters */
|
||||||
|
int eDisre # Type of distance restraining */
|
||||||
|
double dr_fc # force constant for ta_disre */
|
||||||
|
int eDisreWeighting # type of weighting of pairs in one restraints */
|
||||||
|
bint bDisreMixed # Use comb of time averaged and instan. viol's */
|
||||||
|
int nstdisreout # frequency of writing pair distances to enx */
|
||||||
|
double dr_tau # time constant for memory function in disres */
|
||||||
|
double orires_fc # force constant for orientational restraints */
|
||||||
|
double orires_tau # time constant for memory function in orires */
|
||||||
|
int nstorireout # frequency of writing tr(SD) to enx */
|
||||||
|
double em_stepsize # The stepsize for updating */
|
||||||
|
double em_tol # The tolerance */
|
||||||
|
int niter # Number of iterations for convergence of */
|
||||||
|
# steepest descent in relax_shells */
|
||||||
|
double fc_stepsize # Stepsize for directional minimization */
|
||||||
|
# in relax_shells */
|
||||||
|
int nstcgsteep # number of steps after which a steepest */
|
||||||
|
# descents step is done while doing cg */
|
||||||
|
int nbfgscorr # Number of corrections to the hessian to keep */
|
||||||
|
int eConstrAlg # Type of constraint algorithm */
|
||||||
|
int nProjOrder # Order of the LINCS Projection Algorithm */
|
||||||
|
double LincsWarnAngle # If bond rotates more than %g degrees, warn */
|
||||||
|
int nLincsIter # Number of iterations in the final Lincs step */
|
||||||
|
bint bShakeSOR # Use successive overrelaxation for shake */
|
||||||
|
double bd_fric # Friction coefficient for BD (amu/ps) */
|
||||||
|
int ld_seed # Random seed for SD and BD */
|
||||||
|
int nwall # The number of walls */
|
||||||
|
int wall_type # The type of walls */
|
||||||
|
double wall_r_linpot # The potentail is linear for r<=wall_r_linpot */
|
||||||
|
int wall_atomtype[2] # The atom type for walls */
|
||||||
|
double wall_density[2] # Number density for walls */
|
||||||
|
double wall_ewald_zfac # Scaling factor for the box for Ewald */
|
||||||
|
|
||||||
|
# COM pulling data */
|
||||||
|
bint bPull # Do we do COM pulling? */
|
||||||
|
#struct pull_params_t *pull # The data for center of mass pulling */
|
||||||
|
#struct pull_t *pull_work # The COM pull force calculation data structure TODO this pointer should live somewhere else */
|
||||||
|
|
||||||
|
# Enforced rotation data */
|
||||||
|
bint bRot # Calculate enforced rotation potential(s)? */
|
||||||
|
t_rot *rot # The data for enforced rotation potentials */
|
||||||
|
|
||||||
|
int eSwapCoords # Do ion/water position exchanges (CompEL)? */
|
||||||
|
t_swapcoords *swap
|
||||||
|
|
||||||
|
bint bIMD # Allow interactive MD sessions for this .tpr? */
|
||||||
|
t_IMD *imd # Interactive molecular dynamics */
|
||||||
|
|
||||||
|
double cos_accel # Acceleration for viscosity calculation */
|
||||||
|
tensor deform # Triclinic deformation velocities (nm/ps) */
|
||||||
|
int userint1 # User determined parameters */
|
||||||
|
int userint2
|
||||||
|
int userint3
|
||||||
|
int userint4
|
||||||
|
double userdouble1
|
||||||
|
double userdouble2
|
||||||
|
double userdouble3
|
||||||
|
double userdouble4
|
||||||
|
t_grpopts opts # Group options */
|
||||||
|
t_cosines ex[0] # Electric field stuff (spatial part) */
|
||||||
|
t_cosines et[0] # Electric field stuff (time part) */
|
||||||
|
bint bQMMM # QM/MM calculation */
|
||||||
|
int QMconstraints # constraints on QM bonds */
|
||||||
|
int QMMMscheme # Scheme: ONIOM or normal */
|
||||||
|
double scalefactor # factor for scaling the MM charges in QM calc.*/
|
||||||
|
|
||||||
|
# Fields for removed features go here (better caching) */
|
||||||
|
bint bAdress # Whether AdResS is enabled - always false if a valid .tpr was read
|
||||||
|
bint useTwinRange # Whether twin-range scheme is active - always false if a valid .tpr was read
|
495
pygmx/reader.py
Normal file
495
pygmx/reader.py
Normal file
@ -0,0 +1,495 @@
|
|||||||
|
import xdrlib
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
# from .coordinates import decompress
|
||||||
|
from ..utils import hash_anything, merge_hashes
|
||||||
|
from functools import partial
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
TRR_MAGIC = 1993
|
||||||
|
XTC_MAGIC = 1995
|
||||||
|
INDEX_MAGIC = 2015
|
||||||
|
|
||||||
|
def index_filename_for_xtc(xtcfile):
|
||||||
|
"""
|
||||||
|
Get the filename for the index file of a xtc file.
|
||||||
|
|
||||||
|
In general, the index file is located in the same directory as the xtc file.
|
||||||
|
If the xtc file is located in a read-only directory (for the current user)
|
||||||
|
and does not exist, the index file will be instead located in a subdirectory
|
||||||
|
of ~/.xtcindex, of the current user.
|
||||||
|
|
||||||
|
The directory for user index files can be changed by setting the environment
|
||||||
|
variable 'XTCINDEXDIR'.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
xtcfile = '/data/niels/test.xtc'
|
||||||
|
|
||||||
|
# case 1: index file exists, or current user is niels
|
||||||
|
index_filename_for_xtc(xtcfile) == '/data/niels/.test.xtc.xtcindex'
|
||||||
|
|
||||||
|
# case 2: index file doesn't exist, and nor write permission in /data/niels
|
||||||
|
index_filename_for_xtc(xtcfile) == '~/.xtcindex/data/niels/.test.xtc.xtcindex'
|
||||||
|
|
||||||
|
Warning:
|
||||||
|
At this point, the index file is not checked for validity. If an invalid
|
||||||
|
index file is present in the xtc files directory, this will be used and an
|
||||||
|
error will be risen by XTCReader.
|
||||||
|
|
||||||
|
Warning:
|
||||||
|
On most systems, the home directory is on the local drive so that the indexing
|
||||||
|
has probably to do be done on every system if it can not be saved in the directory
|
||||||
|
of the xtc file.
|
||||||
|
"""
|
||||||
|
base = os.path.basename(xtcfile)
|
||||||
|
filename = "." + base + ".xtcindex"
|
||||||
|
|
||||||
|
dir = os.path.abspath(os.path.dirname(xtcfile))
|
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(dir, filename)) and not os.access(dir, os.W_OK):
|
||||||
|
if 'XTCINDEXDIR' in os.environ:
|
||||||
|
index_dir = os.environ['XTCINDEXDIR']
|
||||||
|
else:
|
||||||
|
index_dir = os.path.join(os.environ['HOME'], '.xtcindex')
|
||||||
|
dir = os.path.join(index_dir, dir.lstrip('/'))
|
||||||
|
os.makedirs(dir, exist_ok=True)
|
||||||
|
|
||||||
|
return os.path.join(dir, filename)
|
||||||
|
|
||||||
|
|
||||||
|
class NumpyUnpacker(xdrlib.Unpacker):
|
||||||
|
|
||||||
|
def unpack_float_array(self, n_items):
|
||||||
|
i = self.get_position()
|
||||||
|
j = i + 4 * n_items
|
||||||
|
self.set_position(j)
|
||||||
|
data = self.get_buffer()[i:j]
|
||||||
|
|
||||||
|
if len(data) < 4:
|
||||||
|
raise EOFError
|
||||||
|
|
||||||
|
ret = np.frombuffer(data, '>f')
|
||||||
|
if len(ret) != n_items:
|
||||||
|
raise EOFError
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def unpack_double_array(self, n_items):
|
||||||
|
i = self.get_position()
|
||||||
|
j = i + 8 * n_items
|
||||||
|
self.set_position(j)
|
||||||
|
data = self.get_buffer()[i:j]
|
||||||
|
|
||||||
|
if len(data) < 8:
|
||||||
|
raise EOFError
|
||||||
|
|
||||||
|
ret = np.frombuffer(data, '>d')
|
||||||
|
if len(ret) != n_items:
|
||||||
|
raise EOFError
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidMagicException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidIndexException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class UnknownLenError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SubscriptableReader:
|
||||||
|
|
||||||
|
def __init__(self, fd):
|
||||||
|
self.fd = fd
|
||||||
|
self.len = os.stat(self.fd.fileno()).st_size
|
||||||
|
|
||||||
|
def __getitem__(self, r):
|
||||||
|
if isinstance(r, slice):
|
||||||
|
if r.start is not None:
|
||||||
|
if r.start < 0:
|
||||||
|
self.fd.seek(r.start, io.SEEK_END)
|
||||||
|
else:
|
||||||
|
self.fd.seek(r.start, io.SEEK_SET)
|
||||||
|
|
||||||
|
if r.step is not None:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
return self.fd.read(r.stop - r.start)
|
||||||
|
else:
|
||||||
|
self.fd.seek(r, io.SEEK_SET)
|
||||||
|
return self.fd.read(1)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.len
|
||||||
|
|
||||||
|
|
||||||
|
class XTCFrame:
|
||||||
|
__slots__ = ['_coordinates', 'index', 'time', 'box', '_compressed_coordinates']
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._coordinates = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def coordinates(self):
|
||||||
|
if self._coordinates is None:
|
||||||
|
self._coordinates = self._compressed_coordinates()
|
||||||
|
return self._coordinates
|
||||||
|
|
||||||
|
|
||||||
|
class BaseReader:
|
||||||
|
|
||||||
|
def __init__(self, file):
|
||||||
|
self.fd = open(file, 'rb')
|
||||||
|
self.filename = file
|
||||||
|
|
||||||
|
self.reader = SubscriptableReader(self.fd)
|
||||||
|
self.xdr_reader = NumpyUnpacker(self.reader)
|
||||||
|
|
||||||
|
def __exit__(self, *_):
|
||||||
|
self.fd.close()
|
||||||
|
|
||||||
|
def __enter__(self, *_):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_position(self):
|
||||||
|
return self.fd.tell()
|
||||||
|
|
||||||
|
def set_position(self, pos):
|
||||||
|
return self.fd.seek(pos)
|
||||||
|
|
||||||
|
def skip_frames(self, num):
|
||||||
|
for i in range(num):
|
||||||
|
self.skip_frame()
|
||||||
|
|
||||||
|
def skip_frame(self):
|
||||||
|
self._read_header()
|
||||||
|
self._read_frame()
|
||||||
|
|
||||||
|
def dump_frame(self):
|
||||||
|
raise NotImplemented
|
||||||
|
|
||||||
|
def _read_header(self):
|
||||||
|
raise NotImplemented
|
||||||
|
|
||||||
|
def _read_frame(self):
|
||||||
|
raise NotImplemented
|
||||||
|
|
||||||
|
XTC_HEADER_SIZE = 4 * 4
|
||||||
|
XTC_FRAME_HEADER_SIZE = 9 * 4 + 10 * 4
|
||||||
|
|
||||||
|
|
||||||
|
class XTCReader(BaseReader):
|
||||||
|
|
||||||
|
len_available = False
|
||||||
|
|
||||||
|
def __init__(self, xtcfile, load_cache_file=True):
|
||||||
|
super().__init__(xtcfile)
|
||||||
|
self._cache = [0]
|
||||||
|
self._times = None
|
||||||
|
self.current_frame = 0
|
||||||
|
|
||||||
|
index_file_name = index_filename_for_xtc(xtcfile)
|
||||||
|
if load_cache_file:
|
||||||
|
self.load_index(index_file_name)
|
||||||
|
|
||||||
|
def load_index(self, filename):
|
||||||
|
xtc_stat = os.stat(self.filename)
|
||||||
|
c_time = int(xtc_stat.st_ctime)
|
||||||
|
m_time = int(xtc_stat.st_mtime)
|
||||||
|
size = xtc_stat.st_size
|
||||||
|
|
||||||
|
with open(filename, 'rb') as index_file_fd:
|
||||||
|
# TODO: Is NumpyUnpacker even necessary at this point?
|
||||||
|
# Seems like xdrlib.Unpacker would be sufficient here ...
|
||||||
|
reader = NumpyUnpacker(SubscriptableReader(index_file_fd))
|
||||||
|
|
||||||
|
if reader.unpack_hyper() != INDEX_MAGIC:
|
||||||
|
raise InvalidMagicException
|
||||||
|
if reader.unpack_hyper() != c_time:
|
||||||
|
raise InvalidIndexException
|
||||||
|
if reader.unpack_hyper() != m_time:
|
||||||
|
raise InvalidIndexException
|
||||||
|
if reader.unpack_hyper() != size:
|
||||||
|
raise InvalidIndexException
|
||||||
|
|
||||||
|
self._cache = []
|
||||||
|
self._times = []
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
self._cache.append(reader.unpack_hyper())
|
||||||
|
self._times.append(reader.unpack_float())
|
||||||
|
except EOFError:
|
||||||
|
pass
|
||||||
|
self.len_available = True
|
||||||
|
|
||||||
|
def _raw_header(self):
|
||||||
|
if len(self._cache) == self.current_frame:
|
||||||
|
self._cache.append(self.get_position())
|
||||||
|
return self.fd.read(XTC_HEADER_SIZE)
|
||||||
|
|
||||||
|
def _raw_frame(self):
|
||||||
|
frame_header = self.fd.read(XTC_FRAME_HEADER_SIZE)
|
||||||
|
blob_size = xdrlib.Unpacker(frame_header[-4:]).unpack_int()
|
||||||
|
blob_size = (blob_size + 3) // 4 * 4 # Padding to 4 bytes
|
||||||
|
frame_blob = self.fd.read(blob_size)
|
||||||
|
|
||||||
|
self.current_frame += 1
|
||||||
|
return frame_header + frame_blob
|
||||||
|
|
||||||
|
def _unpack_header(self, raw_header):
|
||||||
|
unpacker = xdrlib.Unpacker(raw_header)
|
||||||
|
|
||||||
|
magic = unpacker.unpack_int()
|
||||||
|
|
||||||
|
if magic != XTC_MAGIC:
|
||||||
|
raise InvalidMagicException
|
||||||
|
|
||||||
|
n_atoms = unpacker.unpack_int()
|
||||||
|
step = unpacker.unpack_int()
|
||||||
|
time = unpacker.unpack_float()
|
||||||
|
|
||||||
|
return n_atoms, step, time
|
||||||
|
|
||||||
|
def _read_header(self):
|
||||||
|
raw_header = self._raw_header()
|
||||||
|
return self._unpack_header(raw_header)
|
||||||
|
|
||||||
|
def _unpack_frame(self, raw_frame):
|
||||||
|
unpacker = xdrlib.Unpacker(raw_frame)
|
||||||
|
|
||||||
|
raw_box = unpacker.unpack_farray(9, unpacker.unpack_float)
|
||||||
|
box = np.array(raw_box).reshape(3, 3)
|
||||||
|
num_coords = unpacker.unpack_int()
|
||||||
|
precision = unpacker.unpack_float()
|
||||||
|
maxint = unpacker.unpack_farray(3, unpacker.unpack_int)
|
||||||
|
minint = unpacker.unpack_farray(3, unpacker.unpack_int)
|
||||||
|
smallindex = unpacker.unpack_int()
|
||||||
|
|
||||||
|
blob_len = unpacker.unpack_int()
|
||||||
|
blob = unpacker.unpack_fopaque(blob_len)
|
||||||
|
|
||||||
|
return box, precision, num_coords, minint, maxint, smallindex, blob
|
||||||
|
|
||||||
|
def _read_frame(self):
|
||||||
|
raw_frame = self._raw_frame()
|
||||||
|
return self._unpack_frame(raw_frame)
|
||||||
|
|
||||||
|
def decode_frame(self, header=None, body=None):
|
||||||
|
n_atoms, step, time = header or self._read_header()
|
||||||
|
box, precision, num_coords, minint, maxint, smallindex, blob = body or self._read_frame()
|
||||||
|
coordinates = partial(decompress, num_coords, precision, minint, maxint, smallindex, blob)
|
||||||
|
|
||||||
|
frame = XTCFrame()
|
||||||
|
frame._compressed_coordinates = coordinates
|
||||||
|
frame.index = step
|
||||||
|
frame.time = time
|
||||||
|
frame.box = box
|
||||||
|
return frame
|
||||||
|
|
||||||
|
def dump_frame(self):
|
||||||
|
"""
|
||||||
|
:return: Tuple: The binary data of the frame, the frame itself
|
||||||
|
"""
|
||||||
|
|
||||||
|
raw_header = self._raw_header()
|
||||||
|
header = self._unpack_header(raw_header)
|
||||||
|
|
||||||
|
raw_body = self._raw_frame()
|
||||||
|
body = self._unpack_frame(raw_body)
|
||||||
|
|
||||||
|
return (raw_header + raw_body, self.decode_frame(header, body))
|
||||||
|
|
||||||
|
def cached_position(self, item):
|
||||||
|
try:
|
||||||
|
return self._cache[item]
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
position = self.cached_position(item)
|
||||||
|
|
||||||
|
if position is not None:
|
||||||
|
self.set_position(position)
|
||||||
|
self.current_frame = item
|
||||||
|
return self.decode_frame()
|
||||||
|
# TODO: Use elif and one single return at the end
|
||||||
|
# Also: skip_frames is in fact not implemented at all!
|
||||||
|
# TODO: Catch EOFError and raise IndexError
|
||||||
|
if self.current_frame <= item:
|
||||||
|
self.skip_frames(item - self.current_frame)
|
||||||
|
return self.decode_frame()
|
||||||
|
else:
|
||||||
|
self.set_position(0)
|
||||||
|
self.current_frame = 0
|
||||||
|
self.skip_frames(item)
|
||||||
|
return self.decode_frame()
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
if self.len_available:
|
||||||
|
return len(self._cache)
|
||||||
|
raise UnknownLenError
|
||||||
|
|
||||||
|
def times_of_indices(self, indices):
|
||||||
|
return [self[i].time for i in indices]
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return merge_hashes(hash(self.filename), hash_anything(self._cache))
|
||||||
|
|
||||||
|
|
||||||
|
class TRRHeader:
|
||||||
|
__slots__ = ['ir_size', 'e_size', 'box_size', 'vir_size', 'pres_size', 'top_size', 'sym_size', 'x_size', 'v_size',
|
||||||
|
'f_size', 'n_atoms', 'step', 'nre', 't', 'λ', 'is_double']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def frame_size(self):
|
||||||
|
return self.ir_size + \
|
||||||
|
self.e_size + \
|
||||||
|
self.box_size + \
|
||||||
|
self.vir_size + \
|
||||||
|
self.top_size + \
|
||||||
|
self.sym_size + \
|
||||||
|
self.x_size + \
|
||||||
|
self.v_size + \
|
||||||
|
self.f_size
|
||||||
|
|
||||||
|
|
||||||
|
class TRRFrame:
|
||||||
|
__slots__ = ['header', 'box', 'x', 'v', 'f']
|
||||||
|
|
||||||
|
|
||||||
|
class TRRReader(BaseReader):
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return TRRIterator(self.filename)
|
||||||
|
|
||||||
|
def _unpack_float(self, is_double):
|
||||||
|
if is_double:
|
||||||
|
return self.xdr_reader.unpack_double()
|
||||||
|
else:
|
||||||
|
return self.xdr_reader.unpack_float()
|
||||||
|
|
||||||
|
def _unpack_np_float(self, is_double, *dim):
|
||||||
|
total = np.product(dim)
|
||||||
|
|
||||||
|
if is_double:
|
||||||
|
box = self.xdr_reader.unpack_double_array(total)
|
||||||
|
else:
|
||||||
|
box = self.xdr_reader.unpack_float_array(total)
|
||||||
|
|
||||||
|
box = box.reshape(*dim)
|
||||||
|
return box
|
||||||
|
|
||||||
|
def _read_header(self):
|
||||||
|
|
||||||
|
data = self.fd.read(8) # 4 Magic + 4 VersionStringLen
|
||||||
|
unpacker = NumpyUnpacker(data)
|
||||||
|
|
||||||
|
magic = unpacker.unpack_int()
|
||||||
|
|
||||||
|
if magic != TRR_MAGIC:
|
||||||
|
raise InvalidMagicException
|
||||||
|
|
||||||
|
version_string_len = unpacker.unpack_int()
|
||||||
|
data = self.fd.read(version_string_len + 55) # 4 Magic + 4 VersionStringLen
|
||||||
|
unpacker.reset(data)
|
||||||
|
|
||||||
|
version = unpacker.unpack_string()
|
||||||
|
assert version == b'GMX_trn_file'
|
||||||
|
|
||||||
|
header = TRRHeader()
|
||||||
|
|
||||||
|
header.ir_size = unpacker.unpack_int()
|
||||||
|
header.e_size = unpacker.unpack_int()
|
||||||
|
header.box_size = unpacker.unpack_int()
|
||||||
|
header.vir_size = unpacker.unpack_int()
|
||||||
|
header.pres_size = unpacker.unpack_int()
|
||||||
|
header.top_size = unpacker.unpack_int()
|
||||||
|
header.sym_size = unpacker.unpack_int()
|
||||||
|
header.x_size = unpacker.unpack_int()
|
||||||
|
header.v_size = unpacker.unpack_int()
|
||||||
|
header.f_size = unpacker.unpack_int()
|
||||||
|
header.n_atoms = unpacker.unpack_int()
|
||||||
|
header.step = unpacker.unpack_int()
|
||||||
|
header.nre = unpacker.unpack_int()
|
||||||
|
|
||||||
|
if header.x_size is not None:
|
||||||
|
header.is_double = (header.x_size // header.n_atoms) == 8
|
||||||
|
elif header.v_size is not None:
|
||||||
|
header.is_double = (header.v_size // header.n_atoms) == 8
|
||||||
|
elif header.f_size is not None:
|
||||||
|
header.is_double = (header.f_size // header.n_atoms) == 8
|
||||||
|
|
||||||
|
if header.is_double:
|
||||||
|
data = self.fd.read(16)
|
||||||
|
else:
|
||||||
|
data = self.fd.read(8)
|
||||||
|
unpacker.reset(data)
|
||||||
|
self.xdr_reader = unpacker
|
||||||
|
|
||||||
|
header.t = self._unpack_float(header.is_double)
|
||||||
|
header.λ = self._unpack_float(header.is_double)
|
||||||
|
|
||||||
|
return header
|
||||||
|
|
||||||
|
def _read_frame(self, header):
|
||||||
|
frame = TRRFrame()
|
||||||
|
frame.header = header
|
||||||
|
|
||||||
|
data = self.fd.read(header.frame_size)
|
||||||
|
self.xdr_reader = NumpyUnpacker(data)
|
||||||
|
|
||||||
|
frame.box = None
|
||||||
|
if header.box_size:
|
||||||
|
frame.box = self._unpack_np_float(header.is_double, 3, 3)
|
||||||
|
|
||||||
|
if header.vir_size:
|
||||||
|
for i in range(9):
|
||||||
|
self._unpack_float(header.is_double)
|
||||||
|
|
||||||
|
if header.pres_size:
|
||||||
|
for i in range(9):
|
||||||
|
self._unpack_float(header.is_double)
|
||||||
|
|
||||||
|
frame.x = None
|
||||||
|
if header.x_size:
|
||||||
|
frame.x = self._unpack_np_float(header.is_double, header.n_atoms, 3)
|
||||||
|
|
||||||
|
frame.v = None
|
||||||
|
if header.v_size:
|
||||||
|
frame.v = self._unpack_np_float(header.is_double, header.n_atoms, 3)
|
||||||
|
|
||||||
|
frame.f = None
|
||||||
|
if header.f_size:
|
||||||
|
frame.f = self._unpack_np_float(header.is_double, header.n_atoms, 3)
|
||||||
|
|
||||||
|
return frame
|
||||||
|
|
||||||
|
def decode_frame(self):
|
||||||
|
h = self._read_header()
|
||||||
|
return self._read_frame(h)
|
||||||
|
|
||||||
|
def get_position(self):
|
||||||
|
return self.fd.tell()
|
||||||
|
|
||||||
|
def set_position(self, pos):
|
||||||
|
self.fd.seek(pos)
|
||||||
|
|
||||||
|
|
||||||
|
class TRRIterator():
|
||||||
|
|
||||||
|
def __init__(self, file):
|
||||||
|
self.__reader = TRRReader(file)
|
||||||
|
self.__reader.__enter__()
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
try:
|
||||||
|
return self.__reader.decode_frame()
|
||||||
|
except EOFError as err:
|
||||||
|
self.__reader.__exit__()
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
8838
pygmx/tngio.c
Normal file
8838
pygmx/tngio.c
Normal file
File diff suppressed because it is too large
Load Diff
148
pygmx/tngio.pyx
Normal file
148
pygmx/tngio.pyx
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
cimport numpy as np
|
||||||
|
#from libc.stdlib cimport free
|
||||||
|
|
||||||
|
cdef extern from "tng/tng_io_fwd.h":
|
||||||
|
cdef struct tng_trajectory:
|
||||||
|
pass
|
||||||
|
|
||||||
|
ctypedef tng_trajectory *tng_trajectory_t
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "tng/tng_io.h":
|
||||||
|
ctypedef int int64_t
|
||||||
|
cdef int64_t TNG_TRAJ_POSITIONS "TNG_TRAJ_POSITIONS"
|
||||||
|
cdef int64_t TNG_TRAJ_VELOCITIES "TNG_TRAJ_VELOCITIES"
|
||||||
|
cdef int64_t TNG_TRAJ_FORCES "TNG_TRAJ_FORCES"
|
||||||
|
|
||||||
|
ctypedef enum tng_function_status:
|
||||||
|
TNG_SUCCESS, TNG_FAILURE, TNG_CRITICAL
|
||||||
|
|
||||||
|
tng_function_status tng_util_trajectory_open(const char *filename, const char mode, tng_trajectory_t *tng_data_p)
|
||||||
|
|
||||||
|
tng_function_status tng_util_trajectory_close(tng_trajectory_t *tng_data_p)
|
||||||
|
|
||||||
|
tng_function_status tng_util_time_of_frame_get(const tng_trajectory_t tng_data, const int64_t frame_nr, double *time)
|
||||||
|
|
||||||
|
tng_function_status tng_util_pos_read_range(const tng_trajectory_t tng_data,
|
||||||
|
const int64_t first_frame,
|
||||||
|
const int64_t last_frame,
|
||||||
|
float **positions,
|
||||||
|
int64_t *stride_length)
|
||||||
|
|
||||||
|
tng_function_status tng_util_vel_read_range(const tng_trajectory_t tng_data,
|
||||||
|
const int64_t first_frame,
|
||||||
|
const int64_t last_frame,
|
||||||
|
float **velocities,
|
||||||
|
int64_t *stride_length)
|
||||||
|
|
||||||
|
tng_function_status tng_util_force_read_range(const tng_trajectory_t tng_data,
|
||||||
|
const int64_t first_frame,
|
||||||
|
const int64_t last_frame,
|
||||||
|
float **forces,
|
||||||
|
int64_t *stride_length)
|
||||||
|
|
||||||
|
tng_function_status tng_util_box_shape_read_range(const tng_trajectory_t tng_data,
|
||||||
|
const int64_t first_frame,
|
||||||
|
const int64_t last_frame,
|
||||||
|
float **box_shape,
|
||||||
|
int64_t *stride_length)
|
||||||
|
|
||||||
|
tng_function_status tng_num_frames_get(const tng_trajectory_t tng_data, int64_t *n)
|
||||||
|
|
||||||
|
tng_function_status tng_num_particles_get(const tng_trajectory_t tng_data, int64_t *n);
|
||||||
|
|
||||||
|
tng_function_status tng_medium_stride_length_get(const tng_trajectory_t tng_data, int64_t *len);
|
||||||
|
|
||||||
|
tng_function_status tng_long_stride_length_get(const tng_trajectory_t tng_data, int64_t *len);
|
||||||
|
|
||||||
|
tng_function_status tng_util_trajectory_next_frame_present_data_blocks_find(
|
||||||
|
const tng_trajectory_t tng_data,
|
||||||
|
int64_t current_frame,
|
||||||
|
const int64_t n_requested_data_block_ids,
|
||||||
|
const int64_t *requested_data_block_ids,
|
||||||
|
int64_t *next_frame,
|
||||||
|
int64_t *n_data_blocks_in_next_frame,
|
||||||
|
int64_t **data_block_ids_in_next_frame)
|
||||||
|
|
||||||
|
tng_function_status tng_util_particle_data_next_frame_read(
|
||||||
|
const tng_trajectory_t tng_data,
|
||||||
|
const int64_t block_id,
|
||||||
|
void **values,
|
||||||
|
char *data_type,
|
||||||
|
int64_t *retrieved_frame_number,
|
||||||
|
double *retrieved_time)
|
||||||
|
|
||||||
|
|
||||||
|
cdef check_status(status, message=''):
|
||||||
|
if status != TNG_SUCCESS:
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
#cdef ptr_to_array(float *ptr, np.npy_intp *shape, int np):
|
||||||
|
# arr = np.PyArray_SimpleNewFromData(np, shape, np.NPY_FLOAT, ptr)
|
||||||
|
# return arr
|
||||||
|
|
||||||
|
cdef class TNGTrajectory:
|
||||||
|
cdef:
|
||||||
|
tng_trajectory_t trajectory
|
||||||
|
int nframes, natoms, stride
|
||||||
|
|
||||||
|
def get_position_interval(self):
|
||||||
|
cdef:
|
||||||
|
double time
|
||||||
|
char data_type
|
||||||
|
int frame1, frame2
|
||||||
|
|
||||||
|
cdef np.ndarray[float, ndim=2] positions = np.empty((self.natoms, 3), dtype=np.float32)
|
||||||
|
cdef void *data = <void *>positions.data
|
||||||
|
|
||||||
|
tng_util_particle_data_next_frame_read(self.trajectory, TNG_TRAJ_POSITIONS, &data, &data_type, &frame1, &time)
|
||||||
|
tng_util_particle_data_next_frame_read(self.trajectory, TNG_TRAJ_POSITIONS, &data, &data_type, &frame2, &time)
|
||||||
|
del positions
|
||||||
|
return frame2 - frame1
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self.nframes
|
||||||
|
|
||||||
|
def __getitem__(self, frame):
|
||||||
|
cdef np.ndarray[float, ndim=2] positions = np.empty((self.natoms, 3), dtype=np.float32)
|
||||||
|
cdef float *pos = <float *>positions.data
|
||||||
|
|
||||||
|
#check_status(
|
||||||
|
if (TNG_SUCCESS !=
|
||||||
|
tng_util_pos_read_range(self.trajectory, frame, frame, &pos, &self.stride)#,
|
||||||
|
#'Reading of frame not successful.'
|
||||||
|
):
|
||||||
|
raise IndexError()
|
||||||
|
|
||||||
|
return positions
|
||||||
|
|
||||||
|
def __init__(self, filename):
|
||||||
|
assert filename, 'Filename must not be null.'
|
||||||
|
if isinstance(filename, str):
|
||||||
|
filename = filename.encode()
|
||||||
|
|
||||||
|
if (tng_util_trajectory_open(filename, b'r', &self.trajectory) != TNG_SUCCESS):
|
||||||
|
raise OSError('Trajectory could not be opened.')
|
||||||
|
|
||||||
|
tng_num_frames_get(self.trajectory, &self.nframes)
|
||||||
|
tng_num_particles_get(self.trajectory, &self.natoms)
|
||||||
|
|
||||||
|
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
|
class TNGReader:
|
||||||
|
def __init__(self, filename):
|
||||||
|
self.trajectory = TNGTrajectory(filename)
|
||||||
|
self.nstxout = self.trajectory.get_position_interval()
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return ceil(len(self.trajectory) / self.nstxout)
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
try:
|
||||||
|
return self.trajectory[item * self.nstxout]
|
||||||
|
except IndexError:
|
||||||
|
raise IndexError('Trajectory frame {} out of range.'.format(item))
|
107
pygmx/topology.pxd
Normal file
107
pygmx/topology.pxd
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
|
||||||
|
from utility cimport *
|
||||||
|
from math cimport *
|
||||||
|
from mdtypes cimport *
|
||||||
|
|
||||||
|
cdef extern from "gromacs/topology/atoms.h":
|
||||||
|
ctypedef struct t_atom:
|
||||||
|
real m, q; # Mass and charge */
|
||||||
|
real mB, qB; # Mass and charge for Free Energy calc */
|
||||||
|
unsigned short type; # Atom type */
|
||||||
|
unsigned short typeB; # Atom type for Free Energy calc */
|
||||||
|
int ptype; # Particle type */
|
||||||
|
int resind; # Index into resinfo (in t_atoms) */
|
||||||
|
int atomnumber; # Atomic Number or 0 */
|
||||||
|
char elem[4]; # Element name */
|
||||||
|
|
||||||
|
ctypedef struct t_resinfo:
|
||||||
|
char **name; # Pointer to the residue name */
|
||||||
|
int nr; # Residue number */
|
||||||
|
unsigned char ic; # Code for insertion of residues */
|
||||||
|
int chainnum; # Iincremented at TER or new chain id */
|
||||||
|
char chainid; # Chain identifier written/read to pdb */
|
||||||
|
char **rtp; # rtp building block name (optional) */
|
||||||
|
|
||||||
|
ctypedef struct t_pdbinfo:
|
||||||
|
pass
|
||||||
|
|
||||||
|
ctypedef struct t_atoms:
|
||||||
|
int nr # Nr of atoms */
|
||||||
|
t_atom *atom # Array of atoms (dim: nr) */
|
||||||
|
# The following entries will not */
|
||||||
|
# always be used (nres==0) */
|
||||||
|
char ***atomname # Array of pointers to atom name */
|
||||||
|
# use: (*(atomname[i])) */
|
||||||
|
char ***atomtype # Array of pointers to atom types */
|
||||||
|
# use: (*(atomtype[i])) */
|
||||||
|
char ***atomtypeB # Array of pointers to B atom types */
|
||||||
|
# use: (*(atomtypeB[i])) */
|
||||||
|
int nres # The number of resinfo entries */
|
||||||
|
t_resinfo *resinfo # Array of residue names and numbers */
|
||||||
|
t_pdbinfo *pdbinfo # PDB Information, such as aniso. Bfac */
|
||||||
|
|
||||||
|
ctypedef struct t_atomtypes:
|
||||||
|
pass
|
||||||
|
|
||||||
|
#ctypedef t_atoms *t_atoms_ptr
|
||||||
|
|
||||||
|
cdef extern from "gromacs/topology/symtab.h":
|
||||||
|
ctypedef struct t_symtab:
|
||||||
|
pass
|
||||||
|
|
||||||
|
cdef extern from "gromacs/topology/block.h":
|
||||||
|
ctypedef struct t_block:
|
||||||
|
pass
|
||||||
|
ctypedef struct t_blocka:
|
||||||
|
pass
|
||||||
|
|
||||||
|
cdef extern from "gromacs/topology/idef.h":
|
||||||
|
ctypedef struct t_ilist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
ctypedef struct gmx_ffparams_t:
|
||||||
|
pass
|
||||||
|
|
||||||
|
cdef extern from "gromacs/topology/topology.h":
|
||||||
|
ctypedef struct gmx_moltype_t:
|
||||||
|
char **name; # Name of the molecule type */
|
||||||
|
t_atoms atoms; # The atoms in this molecule */
|
||||||
|
t_ilist ilist[0]; # Interaction list with local indices */
|
||||||
|
t_block cgs; # The charge groups */
|
||||||
|
t_blocka excls; # The exclusions */
|
||||||
|
|
||||||
|
ctypedef struct gmx_molblock_t:
|
||||||
|
int type; # The molcule type index in mtop.moltype */
|
||||||
|
int nmol; # The number of molecules in this block */
|
||||||
|
int natoms_mol; # The number of atoms in one molecule */
|
||||||
|
int nposres_xA; # The number of posres coords for top A */
|
||||||
|
rvec *posres_xA; # The posres coords for top A */
|
||||||
|
int nposres_xB; # The number of posres coords for top B */
|
||||||
|
rvec *posres_xB; # The posres coords for top B */
|
||||||
|
|
||||||
|
ctypedef struct gmx_groups_t:
|
||||||
|
pass
|
||||||
|
|
||||||
|
ctypedef struct gmx_mtop_t:
|
||||||
|
char **name # Name of the topology */
|
||||||
|
gmx_ffparams_t ffparams
|
||||||
|
int nmoltype
|
||||||
|
gmx_moltype_t *moltype
|
||||||
|
int nmolblock
|
||||||
|
gmx_molblock_t *molblock
|
||||||
|
bint bIntermolecularInteractions # Are there intermolecular
|
||||||
|
# interactions? */
|
||||||
|
t_ilist *intermolecular_ilist # List of intermolecular interactions
|
||||||
|
# using system wide atom indices,
|
||||||
|
# either NULL or size F_NRE */
|
||||||
|
int natoms
|
||||||
|
int maxres_renum # Parameter for residue numbering */
|
||||||
|
int maxresnr # The maximum residue number in moltype */
|
||||||
|
t_atomtypes atomtypes # Atomtype properties */
|
||||||
|
t_block mols # The molecules */
|
||||||
|
gmx_groups_t groups
|
||||||
|
t_symtab symtab # The symbol table */
|
||||||
|
|
||||||
|
# cdef extern from "gromacs/topology/topology.h":
|
||||||
|
# generate a t_atoms struct for the system from gmx_mtop_t
|
||||||
|
# t_atoms* mtop2atoms(gmx_mtop_t *mtop)
|
8530
pygmx/tpxio.c
Normal file
8530
pygmx/tpxio.c
Normal file
File diff suppressed because it is too large
Load Diff
191
pygmx/tpxio.pyx
Normal file
191
pygmx/tpxio.pyx
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
# cython: c_string_type=unicode, c_string_encoding=utf8
|
||||||
|
# Cython wrapper around tpxio.cpp
|
||||||
|
|
||||||
|
# from cython cimport view
|
||||||
|
# from array import array
|
||||||
|
import numpy as np
|
||||||
|
cimport numpy as np
|
||||||
|
|
||||||
|
from utility cimport *
|
||||||
|
from math cimport *
|
||||||
|
from mdtypes cimport *
|
||||||
|
from topology cimport *
|
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "gromacs/fileio/tpxio.h":
|
||||||
|
void read_tpxheader(const char *fn, t_tpxheader *tpx, bint TopOnlyOK)
|
||||||
|
|
||||||
|
cdef struct t_tpxheader:
|
||||||
|
bint bIr # Non zero if input_rec is present */
|
||||||
|
bint bBox # Non zero if a box is present */
|
||||||
|
bint bTop # Non zero if a topology is present */
|
||||||
|
bint bX # Non zero if coordinates are present */
|
||||||
|
bint bV # Non zero if velocities are present */
|
||||||
|
bint bF # Non zero if forces are present (no longer
|
||||||
|
# supported, but retained so old .tpr can be read) */
|
||||||
|
int natoms # The total number of atoms */
|
||||||
|
int ngtc # The number of temperature coupling groups */
|
||||||
|
real _lambda # Current value of lambda */
|
||||||
|
int fep_state # Current value of the alchemical state --
|
||||||
|
|
||||||
|
|
||||||
|
int read_tpx(const char *fname,
|
||||||
|
t_inputrec *ir,
|
||||||
|
double box[3][3],
|
||||||
|
int *natoms,
|
||||||
|
rvec *x,
|
||||||
|
rvec *v,
|
||||||
|
gmx_mtop_t *mtop)
|
||||||
|
|
||||||
|
|
||||||
|
def cstr(instr):
|
||||||
|
if isinstance(instr, str):
|
||||||
|
instr = instr.encode()
|
||||||
|
return instr
|
||||||
|
|
||||||
|
cdef atoms_from_topology(gmx_mtop_t *topology):
|
||||||
|
cdef:
|
||||||
|
t_atoms c_atoms
|
||||||
|
int moltype, resind
|
||||||
|
|
||||||
|
atoms = []
|
||||||
|
masses = []
|
||||||
|
charges = []
|
||||||
|
residues = []
|
||||||
|
for i_molblock in range(topology.nmolblock):
|
||||||
|
moltype = topology.molblock[i_molblock].type
|
||||||
|
|
||||||
|
c_atoms = topology.moltype[moltype].atoms
|
||||||
|
mol_atoms = []
|
||||||
|
mol_q = []
|
||||||
|
mol_m = []
|
||||||
|
for i_atom in range(c_atoms.nr):
|
||||||
|
resind = c_atoms.atom[i_atom].resind
|
||||||
|
resname = c_atoms.resinfo[resind].name[0]
|
||||||
|
if resname not in residues:
|
||||||
|
residues.append(resname)
|
||||||
|
resid = residues.index(resname) + 1
|
||||||
|
mol_atoms.append((
|
||||||
|
resid,
|
||||||
|
resname,
|
||||||
|
c_atoms.atomname[0][i_atom],
|
||||||
|
))
|
||||||
|
mol_q.append(c_atoms.atom[i_atom].q)
|
||||||
|
mol_m.append(c_atoms.atom[i_atom].m)
|
||||||
|
n_mol = topology.molblock[i_molblock].nmol
|
||||||
|
atoms += mol_atoms * n_mol
|
||||||
|
charges += mol_q * n_mol
|
||||||
|
masses += mol_m * n_mol
|
||||||
|
|
||||||
|
return np.array(atoms)
|
||||||
|
|
||||||
|
ctypedef object (*atom_func)(t_atom)
|
||||||
|
|
||||||
|
cdef atom_charge(t_atom atom):
|
||||||
|
return atom.q
|
||||||
|
|
||||||
|
|
||||||
|
cdef atom_mass(t_atom atom):
|
||||||
|
return atom.m
|
||||||
|
|
||||||
|
cdef class TPXReader:
|
||||||
|
cdef:
|
||||||
|
t_tpxheader header
|
||||||
|
t_inputrec input_record
|
||||||
|
gmx_mtop_t topology
|
||||||
|
real box[3][3]
|
||||||
|
readonly int n_atoms, n_tcouple_groups, n_mol
|
||||||
|
readonly char *topology_name
|
||||||
|
|
||||||
|
cdef _map_atoms(self, object (*func)(t_atom)):
|
||||||
|
cdef t_atoms atoms
|
||||||
|
map = []
|
||||||
|
for i_molblock in range(self.topology.nmolblock):
|
||||||
|
moltype = self.topology.molblock[i_molblock].type
|
||||||
|
nmol = self.topology.molblock[i_molblock].nmol
|
||||||
|
atoms = self.topology.moltype[moltype].atoms
|
||||||
|
mol_map = []
|
||||||
|
for i_atom in range(atoms.nr):
|
||||||
|
mol_map.append(func(atoms.atom[i_atom]))
|
||||||
|
map += mol_map * nmol
|
||||||
|
return map
|
||||||
|
|
||||||
|
property atoms:
|
||||||
|
"""
|
||||||
|
Get a list of tuples describing all atoms in the system:
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of tuples: (atom_name, residue_id, resiude_name)
|
||||||
|
"""
|
||||||
|
def __get__(self):
|
||||||
|
return atoms_from_topology(&self.topology)
|
||||||
|
|
||||||
|
property mass2:
|
||||||
|
def __get__(self):
|
||||||
|
return np.array(self._map_atoms(atom_mass))
|
||||||
|
|
||||||
|
property mass:
|
||||||
|
"Get the masses of all atoms in the system."
|
||||||
|
def __get__(self):
|
||||||
|
cdef t_atoms atoms
|
||||||
|
masses = []
|
||||||
|
for i_molblock in range(self.topology.nmolblock):
|
||||||
|
moltype = self.topology.molblock[i_molblock].type
|
||||||
|
nmol = self.topology.molblock[i_molblock].nmol
|
||||||
|
atoms = self.topology.moltype[moltype].atoms
|
||||||
|
mol_masses = []
|
||||||
|
for i_atom in range(atoms.nr):
|
||||||
|
mol_masses.append(atoms.atom[i_atom].m)
|
||||||
|
masses += mol_masses * nmol
|
||||||
|
return np.array(masses)
|
||||||
|
|
||||||
|
property charge:
|
||||||
|
"Get the partial charge of all atoms in the system."
|
||||||
|
def __get__(self):
|
||||||
|
cdef t_atoms atoms
|
||||||
|
charges = []
|
||||||
|
for i_molblock in range(self.topology.nmolblock):
|
||||||
|
moltype = self.topology.molblock[i_molblock].type
|
||||||
|
nmol = self.topology.molblock[i_molblock].nmol
|
||||||
|
atoms = self.topology.moltype[moltype].atoms
|
||||||
|
mol_charges = []
|
||||||
|
for i_atom in range(atoms.nr):
|
||||||
|
mol_charges.append(atoms.atom[i_atom].q)
|
||||||
|
charges += mol_charges * nmol
|
||||||
|
return np.array(charges)
|
||||||
|
|
||||||
|
property type:
|
||||||
|
"Get the type of all atoms in the system."
|
||||||
|
def __get__(self):
|
||||||
|
cdef t_atoms atoms
|
||||||
|
types = []
|
||||||
|
for i_molblock in range(self.topology.nmolblock):
|
||||||
|
moltype = self.topology.molblock[i_molblock].type
|
||||||
|
nmol = self.topology.molblock[i_molblock].nmol
|
||||||
|
atoms = self.topology.moltype[moltype].atoms
|
||||||
|
mol_type = []
|
||||||
|
for i_atom in range(atoms.nr):
|
||||||
|
mol_type.append(atoms.atom[i_atom].type)
|
||||||
|
types += mol_type * nmol
|
||||||
|
return np.array(types)
|
||||||
|
|
||||||
|
|
||||||
|
def __cinit__(self, filename):
|
||||||
|
filename = cstr(filename)
|
||||||
|
read_tpxheader(<char *>filename, &self.header, True)
|
||||||
|
self.n_atoms = self.header.natoms
|
||||||
|
self.n_tcouple_groups = self.header.ngtc
|
||||||
|
|
||||||
|
cdef np.ndarray[real, ndim=2] coordinates = np.empty((self.n_atoms, 3), dtype=np.float32)
|
||||||
|
cdef np.ndarray[real, ndim=2] velocites = np.empty((self.n_atoms, 3), dtype=np.float32)
|
||||||
|
|
||||||
|
return_code = read_tpx(
|
||||||
|
<char *>filename,
|
||||||
|
&self.input_record,
|
||||||
|
self.box,
|
||||||
|
&self.n_atoms,
|
||||||
|
<rvec *>coordinates.data,
|
||||||
|
<rvec *>velocites.data,
|
||||||
|
&self.topology
|
||||||
|
)
|
||||||
|
self.topology_name = self.topology.name[0]
|
14
pygmx/utility.pxd
Normal file
14
pygmx/utility.pxd
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# C-API in gromacs/utility
|
||||||
|
|
||||||
|
#cdef extern from "inttypes.h":
|
||||||
|
ctypedef int __int64
|
||||||
|
|
||||||
|
cdef extern from "gromacs/utility/basedefinitions.h":
|
||||||
|
ctypedef int gmx_bool
|
||||||
|
ctypedef __int64 gmx_int64_t
|
||||||
|
|
||||||
|
cdef extern from "gromacs/utility/real.h":
|
||||||
|
ctypedef double real
|
||||||
|
|
||||||
|
cdef extern from "gromacs/utility/futil.h":
|
||||||
|
ctypedef gmx_int64_t gmx_off_t
|
95
pygmx/xtcio.pyx
Normal file
95
pygmx/xtcio.pyx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Wrapper for xtcio.h: I/O for xtc files.
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
cimport numpy as np
|
||||||
|
|
||||||
|
from utility cimport *
|
||||||
|
from math cimport *
|
||||||
|
from fileio cimport *
|
||||||
|
|
||||||
|
cdef extern from "gromacs/fileio/xtcio.h":
|
||||||
|
t_fileio *open_xtc(const char *filename, const char *mode)
|
||||||
|
|
||||||
|
void close_xtc(t_fileio *fio)
|
||||||
|
|
||||||
|
int read_first_xtc(t_fileio *fio,
|
||||||
|
int *natoms, int *step, real *time,
|
||||||
|
matrix box, rvec **x, real *prec, gmx_bool *_bOK)
|
||||||
|
|
||||||
|
int read_next_xtc(t_fileio *fio,
|
||||||
|
int natoms, int *step, real *time,
|
||||||
|
matrix box, rvec *x, real *prec, gmx_bool *_bOK)
|
||||||
|
|
||||||
|
|
||||||
|
if sizeof(real) == 4:
|
||||||
|
np_real = np.float32
|
||||||
|
else:
|
||||||
|
np_real = np.float
|
||||||
|
|
||||||
|
|
||||||
|
cdef class XTCReader:
|
||||||
|
cdef:
|
||||||
|
t_fileio *fio
|
||||||
|
int natoms, cur_step
|
||||||
|
real start_time, timestep, prec, cur_time
|
||||||
|
dict _cache
|
||||||
|
#rvec *coords
|
||||||
|
|
||||||
|
def nearest_cache(self, index):
|
||||||
|
nearest = 0
|
||||||
|
cur_dist = 99999999
|
||||||
|
for ind in self._cache:
|
||||||
|
dist = abs(ind - index)
|
||||||
|
if dist < cur_dist:
|
||||||
|
nearest = ind
|
||||||
|
cur_dist = dist
|
||||||
|
return nearest
|
||||||
|
|
||||||
|
|
||||||
|
def seek(self, index):
|
||||||
|
if index in self._cache:
|
||||||
|
gmx_fio_seek(self.fio, self._cache[index])
|
||||||
|
else:
|
||||||
|
nearest = self.nearest_cache(index)
|
||||||
|
gmx_fio_seek(self.fio, self._cache[nearest])
|
||||||
|
self.cur_step = index
|
||||||
|
self.cur_time = index * self.timestep + self.start_time
|
||||||
|
xtc_seek_time(self.fio, self.cur_time, self.natoms, index > nearest)
|
||||||
|
self._cache[index] = gmx_fio_ftell(self.fio)
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, filename):
|
||||||
|
if isinstance(filename, str):
|
||||||
|
filename = filename.encode()
|
||||||
|
|
||||||
|
cdef:
|
||||||
|
matrix box
|
||||||
|
gmx_bool _bOK
|
||||||
|
real time
|
||||||
|
cdef rvec *coords # = np.empty((1,3), dtype=np.float32)
|
||||||
|
|
||||||
|
self._cache = {}
|
||||||
|
self.fio = open_xtc(filename, b'r')
|
||||||
|
read_first_xtc(self.fio, &self.natoms, &self.cur_step, &self.start_time, box,
|
||||||
|
&coords, &self.prec, &_bOK)
|
||||||
|
self._cache[self.cur_step] = gmx_fio_ftell(self.fio)
|
||||||
|
|
||||||
|
read_next_xtc(self.fio, self.natoms, &self.cur_step, &time, box,
|
||||||
|
coords, &self.prec, &_bOK)
|
||||||
|
|
||||||
|
self._cache[<int>self.cur_step] = gmx_fio_ftell(self.fio)
|
||||||
|
self.timestep = time - self.start_time
|
||||||
|
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
cdef matrix box
|
||||||
|
cdef gmx_bool _bOK
|
||||||
|
cdef real time
|
||||||
|
cdef np.ndarray[real, ndim=2] coords = np.empty((self.natoms, 3), dtype=np_real)
|
||||||
|
|
||||||
|
self.seek(item)
|
||||||
|
|
||||||
|
read_next_xtc(self.fio, self.natoms, &self.cur_step, &time, box,
|
||||||
|
<rvec *>coords.data, &self.prec, &_bOK)
|
||||||
|
|
||||||
|
return coords
|
Reference in New Issue
Block a user