Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ The rules for this file:
* release numbers follow "Semantic Versioning" http://semver.org

------------------------------------------------------------------------------
mm/dd/yy
mm/dd/17

* 0.16.2
* 0.16.2 richardjgowers

Enhancements

Fixes
* fixed GROWriter truncating long resids from the wrong end (Issue #1395)

Changes

Expand Down
6 changes: 3 additions & 3 deletions package/MDAnalysis/coordinates/CRD.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,9 @@ def write(self, selection, frame=None):
current_resid += 1

# Truncate numbers
serial = int(str(i + 1)[-serial_len:])
resid = int(str(resid)[-resid_len:])
current_resid = int(str(current_resid)[-totres_len:])
serial = util.ltruncate_int(i + 1, serial_len)
resid = util.ltruncate_int(resid, resid_len)
current_resid = util.ltruncate_int(current_resid, totres_len)

crd.write(at_fmt.format(
serial=serial, totRes=current_resid, resname=resname,
Expand Down
4 changes: 2 additions & 2 deletions package/MDAnalysis/coordinates/GRO.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,8 @@ def write(self, obj):
# all attributes could be infinite cycles!
for atom_index, resid, resname, name in zip(
range(ag_or_ts.n_atoms), resids, resnames, names):
truncated_atom_index = int(str(atom_index + 1)[-5:])
truncated_resid = int(str(resid)[:5])
truncated_atom_index = util.ltruncate_int(atom_index + 1, 5)
truncated_resid = util.ltruncate_int(resid, 5)
if has_velocities:
output_gro.write(self.fmt['xyz_v'].format(
resid=truncated_resid,
Expand Down
4 changes: 2 additions & 2 deletions package/MDAnalysis/coordinates/PDB.py
Original file line number Diff line number Diff line change
Expand Up @@ -902,12 +902,12 @@ def get_attr(attrname, default):

for i, atom in enumerate(atoms):
vals = {}
vals['serial'] = int(str(i + 1)[-5:]) # check for overflow here?
vals['serial'] = util.ltruncate_int(i + 1, 5) # check for overflow here?
vals['name'] = self._deduce_PDB_atom_name(atomnames[i], resnames[i])
vals['altLoc'] = altlocs[i][:1]
vals['resName'] = resnames[i][:4]
vals['chainID'] = segids[i][:1]
vals['resSeq'] = int(str(resids[i])[-4:])
vals['resSeq'] = util.ltruncate_int(resids[i], 4)
vals['iCode'] = icodes[i][:1]
vals['pos'] = pos[i] # don't take off atom so conversion works
vals['occupancy'] = occupancies[i]
Expand Down
4 changes: 2 additions & 2 deletions package/MDAnalysis/coordinates/PDBQT.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,12 +320,12 @@ def write(self, selection, frame=None):
attrs['resids'], attrs['icodes'], attrs['occupancies'],
attrs['tempfactors'], attrs['charges'], attrs['types']),
start=1):
serial = int(str(serial)[-5:]) # check for overflow here?
serial = util.ltruncate_int(serial, 5) # check for overflow here?
resid = util.ltruncate_int(resid, 4)
name = name[:4]
if len(name) < 4:
name = " " + name # customary to start in column 14
chainid = chainid.strip()[-1:] # take the last character
resid = int(str(resid)[-4:]) # check for overflow here?

self.pdb.write(self.fmt['ATOM'].format(
serial=serial,
Expand Down
25 changes: 25 additions & 0 deletions package/MDAnalysis/lib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -1574,3 +1574,28 @@ def __eq__(self, other):
except AssertionError:
return False
return True


def ltruncate_int(value, ndigits):
"""Truncate an integer, retaining least significant digits

Parameters
----------
value : int
value to truncate
ndigits : int
number of digits to keep

Returns
-------
truncated : int
only the `ndigits` least significant digits from `value`

Examples
--------
>>> ltruncate_int(123, 2)
23
>>> ltruncate_int(1234, 5)
1234
"""
return int(str(value)[-ndigits:])
45 changes: 34 additions & 11 deletions testsuite/MDAnalysisTests/coordinates/test_gro.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,33 @@
# J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787
#
from __future__ import absolute_import
from unittest import TestCase

import MDAnalysis as mda
import numpy as np
from MDAnalysis.coordinates.GRO import GROReader, GROWriter
from MDAnalysisTests import make_Universe
from MDAnalysisTests.coordinates.base import BaseReference, BaseReaderTest, BaseWriterTest, BaseTimestepTest
from MDAnalysisTests import make_Universe, tempdir
from MDAnalysisTests.coordinates.base import (
BaseReference, BaseReaderTest, BaseWriterTest, BaseTimestepTest,
)
from MDAnalysisTests.coordinates.reference import RefAdK
from MDAnalysisTests.datafiles import COORDINATES_GRO, COORDINATES_GRO_INCOMPLETE_VELOCITY, COORDINATES_GRO_BZ2, GRO, \
GRO_large
from MDAnalysisTests.datafiles import (
COORDINATES_GRO,
COORDINATES_GRO_INCOMPLETE_VELOCITY,
COORDINATES_GRO_BZ2,
GRO,
GRO_large,
)
from nose.plugins.attrib import attr
from numpy.testing import (assert_almost_equal, )
from numpy.testing import assert_array_almost_equal, dec, assert_equal, assert_raises


class TestGROReaderOld(TestCase, RefAdK):
from numpy.testing import (
assert_,
assert_almost_equal,
assert_array_almost_equal,
dec,
assert_equal,
assert_raises
)

class TestGROReaderOld(RefAdK):
def setUp(self):
self.universe = mda.Universe(GRO)
self.ts = self.universe.trajectory.ts
Expand Down Expand Up @@ -88,7 +99,7 @@ def test_unitcell(self):
err_msg="unit cell dimensions (rhombic dodecahedron)")


class TestGROReaderNoConversionOld(TestCase, RefAdK):
class TestGROReaderNoConversionOld(RefAdK):
def setUp(self):
self.universe = mda.Universe(GRO, convert_units=False)
self.ts = self.universe.trajectory.ts
Expand Down Expand Up @@ -407,6 +418,18 @@ def test_writer_large_residue_count(self):
err_msg="Writing GRO file with > 99 999 "
"resids does not truncate properly.")

@tempdir.run_in_tempdir()
def test_growriter_resid_truncation():
u = make_Universe(extras=['resids'], trajectory=True)
u.residues[0].resid = 123456789
u.atoms.write('out.gro')

with open('out.gro', 'r') as grofile:
grofile.readline()
grofile.readline()
line = grofile.readline()
# larger digits should get truncated
assert_(line.startswith('56789UNK'))

class TestGROTimestep(BaseTimestepTest):
Timestep = mda.coordinates.GRO.Timestep
Expand Down
16 changes: 16 additions & 0 deletions testsuite/MDAnalysisTests/lib/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -968,3 +968,19 @@ def test_iter(self):
seen.append(val)
for val in ['this', 'that', 'other']:
assert_(val in seen)


class TestTruncateInteger(object):
@staticmethod
def _check_vals(a, b):
assert_(util.ltruncate_int(*a) == b)

def test_ltruncate_int(self):
for vals, exp in (
((1234, 1), 4),
((1234, 2), 34),
((1234, 3), 234),
((1234, 4), 1234),
((1234, 5), 1234),
):
yield self._check_vals, vals, exp