diff --git a/package/CHANGELOG b/package/CHANGELOG index ef0a8c5a631..144fda4853d 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -13,9 +13,9 @@ The rules for this file: * release numbers follow "Semantic Versioning" http://semver.org ------------------------------------------------------------------------------ -??/??/?? richardjgowers, IAlibay, tylerjereddy, xiki-tempula, +??/??/?? richardjgowers, IAlibay, tylerjereddy, xiki-tempula, lilyminium - * 1.0.2 + * 1.1.0 Fixes * Replaces decreated NumPy type aliases and fixes NEP 34 explicit ragged @@ -29,6 +29,13 @@ Changes Deprecations * hbonds.WaterBridgeAnalysis will be removed in 2.0.0 and replaced with hydrogenbonds.WaterBridgeAnalysis (#3111) + * TPRParser indexing resids from 0 by default is deprecated. + From 2.0 TPRParser will index resids from 1 by default. + +Enhancements + * Added `tpr_resid_from_one` keyword to select whether TPRParser + indexes resids from 0 or 1 (Issue #2364, PR #3153) + 01/17/21 richardjgowers, IAlibay, orbeckst, tylerjereddy, jbarnoud, yuxuanzhuang, lilyminium, VOD555, p-j-smith, bieniekmateusz, diff --git a/package/MDAnalysis/topology/TPRParser.py b/package/MDAnalysis/topology/TPRParser.py index 1ac58c6fae9..9d4b3855edf 100644 --- a/package/MDAnalysis/topology/TPRParser.py +++ b/package/MDAnalysis/topology/TPRParser.py @@ -171,12 +171,24 @@ class TPRParser(TopologyReaderBase): """ format = 'TPR' - def parse(self, **kwargs): + def parse(self, tpr_resid_from_one=False, **kwargs): """Parse a Gromacs TPR file into a MDAnalysis internal topology structure. + Parameters + ---------- + tpr_resid_from_one: bool (optional) + Toggle whether to index resids from 1 or 0 from TPR files. + TPR files index resids from 0 by default, even though GRO and ITP + files index from 1. + Returns ------- structure : dict + + + .. versionchanged:: 1.1.0 + Added the ``tpr_resid_from_one`` keyword to control if + resids are indexed from 0 or 1. Default ``False``. """ with openany(self.filename, mode='rb') as infile: tprf = infile.read() @@ -219,7 +231,8 @@ def parse(self, **kwargs): tpr_utils.fileVersion_err(V) if th.bTop: - tpr_top = tpr_utils.do_mtop(data, V) + tpr_top = tpr_utils.do_mtop(data, V, + tpr_resid_from_one=tpr_resid_from_one) else: msg = "{0}: No topology found in tpr file".format(self.filename) logger.critical(msg) diff --git a/package/MDAnalysis/topology/tpr/utils.py b/package/MDAnalysis/topology/tpr/utils.py index b6b78a0313a..9cc49474f83 100644 --- a/package/MDAnalysis/topology/tpr/utils.py +++ b/package/MDAnalysis/topology/tpr/utils.py @@ -49,6 +49,8 @@ from __future__ import absolute_import from six.moves import range +import warnings + import numpy as np import xdrlib import struct @@ -296,7 +298,7 @@ def extract_box_info(data, fileVersion): return obj.Box(box, box_rel, box_v) -def do_mtop(data, fver): +def do_mtop(data, fver, tpr_resid_from_one=False): # mtop: the topology of the whole system symtab = do_symtab(data) do_symstr(data, symtab) # system_name @@ -381,6 +383,16 @@ def do_mtop(data, fver): molnums = np.array(molnums, dtype=np.int32) segids = np.array(segids, dtype=object) resids = np.array(resids, dtype=np.int32) + if tpr_resid_from_one: + resids += 1 + else: + warnings.warn("TPR files index residues from 0. " + "From MDAnalysis version 2.0, resids will start " + "at 1 instead. If you wish to keep indexing " + "resids from 0, please set " + "`tpr_resid_from_one=False` as a keyword argument " + "when you create a new Topology or Universe.", + category=DeprecationWarning) resnames = np.array(resnames, dtype=object) (residx, new_resids, (new_resnames, diff --git a/testsuite/MDAnalysisTests/topology/test_tprparser.py b/testsuite/MDAnalysisTests/topology/test_tprparser.py index 2063ee70ebc..925b02420da 100644 --- a/testsuite/MDAnalysisTests/topology/test_tprparser.py +++ b/testsuite/MDAnalysisTests/topology/test_tprparser.py @@ -39,6 +39,7 @@ ) from MDAnalysisTests.topology.base import ParserBase import MDAnalysis.topology.TPRParser +import MDAnalysis as mda class TPRAttrs(ParserBase): @@ -248,3 +249,25 @@ def test_fail_for_gmx2020_beta(tpr_path): parser = MDAnalysis.topology.TPRParser.TPRParser(tpr_path) with pytest.raises(IOError): parser.parse() + + +@pytest.mark.parametrize("resid_from_one,resid_addition", [ + (False, 0), # status quo for 1.x + (True, 1), + ]) +def test_resids(resid_from_one, resid_addition): + u = mda.Universe(TPR, tpr_resid_from_one=resid_from_one) + resids = np.arange(len(u.residues)) + resid_addition + assert_equal(u.residues.resids, resids, + err_msg="tpr_resid_from_one kwarg not switching resids") + + +@pytest.mark.parametrize("kwargs", [ + {}, + {"tpr_resid_from_one": False}, +]) +def test_resid_false_deprecation_warning(kwargs): + err = ("TPR files index residues from 0. From " + "MDAnalysis version 2.0, resids will start at 1 instead.") + with pytest.warns(DeprecationWarning, match=err): + u = mda.Universe(TPR, **kwargs)