From a2a27aa9ef44a1319d74326b4cdaf5bd05f7ac03 Mon Sep 17 00:00:00 2001 From: Kai Niklas Spauszus <100122494+kainszs@users.noreply.github.com> Date: Fri, 29 Mar 2024 22:39:54 +0100 Subject: [PATCH] dihedral selection (phi,psi,omega,chi1) methods can handle empty groups (#4529) * Fixes #2879 * The AtomGroup methods phi_selections(), psi_selections(), omega_selections(), chi1_selections() can now handle empty groups NOTE: Since the methods return a list of atom groups, the methods will also return an empty list * add tests * update AUTHORS and CHANGELOG --- package/AUTHORS | 2 ++ package/CHANGELOG | 6 +++--- package/MDAnalysis/core/topologyattrs.py | 15 +++++++++++++++ testsuite/MDAnalysisTests/core/test_atomgroup.py | 16 ++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/package/AUTHORS b/package/AUTHORS index 83a2d4e9905..fb5d5689468 100644 --- a/package/AUTHORS +++ b/package/AUTHORS @@ -236,11 +236,13 @@ Chronological list of authors 2024 - Aditya Keshari - Philipp Stärk + - Kai Niklas Spauszus - Sampurna Mukherjee - Leon Wehrhan - Valerij Talagayev + External code ------------- diff --git a/package/CHANGELOG b/package/CHANGELOG index 5a6e49e7db5..96c0f48bfdd 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -16,13 +16,13 @@ The rules for this file: ------------------------------------------------------------------------------- ??/??/?? IAlibay, HeetVekariya, marinegor, lilyminium, RMeli, ljwoods2, aditya292002, pstaerk, PicoCentauri, BFedder, - tyler.je.reddy, SampurnaM, leonwehrhan - + tyler.je.reddy, SampurnaM, leonwehrhan, kainszs * 2.8.0 Fixes - * Add support for TPR files produced by Gromacs 2024.1 + * Atomname methods can handle empty groups (Issue #2879, PR #4529) + * Add support for TPR files produced by Gromacs 2024.1 (PR #4523) * Remove mutable data from ``progressbar_kwargs`` argument in ``AnalysisBase.run()`` (PR #4459) * Fix ChainReader `__repr__()` method when sub-reader is MemoryReader diff --git a/package/MDAnalysis/core/topologyattrs.py b/package/MDAnalysis/core/topologyattrs.py index 6af35b389d7..92fa25a5e5d 100644 --- a/package/MDAnalysis/core/topologyattrs.py +++ b/package/MDAnalysis/core/topologyattrs.py @@ -866,6 +866,9 @@ def phi_selections(residues, c_name='C', n_name='N', ca_name='CA'): .. versionadded:: 1.0.0 """ + if not residues: + return [] + u = residues[0].universe prev = u.residues[residues.ix-1] # obv candidates first rsid = residues.segids @@ -1062,6 +1065,10 @@ def psi_selections(residues, c_name='C', n_name='N', ca_name='CA'): .. versionadded:: 1.0.0 """ + + if not residues: + return [] + results = np.array([None]*len(residues), dtype=object) nxtres = residues._get_next_residues_by_resid() rix = np.where(nxtres)[0] @@ -1170,6 +1177,10 @@ def omega_selections(residues, c_name='C', n_name='N', ca_name='CA'): .. versionadded:: 1.0.0 """ + + if not residues: + return [] + results = np.array([None]*len(residues), dtype=object) nxtres = residues._get_next_residues_by_resid() rix = np.where(nxtres)[0] @@ -1267,6 +1278,10 @@ def chi1_selections(residues, n_name='N', ca_name='CA', cb_name='CB', .. versionadded:: 1.0.0 """ + + if not residues: + return [] + results = np.array([None]*len(residues)) names = [n_name, ca_name, cb_name, cg_name] keep = [all(sum(np.isin(r.atoms.names, n.split())) == 1 diff --git a/testsuite/MDAnalysisTests/core/test_atomgroup.py b/testsuite/MDAnalysisTests/core/test_atomgroup.py index 2e3036bad35..6173ea76f5c 100644 --- a/testsuite/MDAnalysisTests/core/test_atomgroup.py +++ b/testsuite/MDAnalysisTests/core/test_atomgroup.py @@ -748,6 +748,10 @@ def test_phi_selections_single(self, GRO): assert_equal(phisel.residues.resids, [9, 10]) assert_equal(phisel.residues.resnames, ['PRO', 'GLY']) + def test_phi_selections_empty(self, GRO): + rgsel = GRO.segments[0].residues[[]].phi_selections() + assert len(rgsel) == 0 + def test_phi_selections(self, resgroup): rgsel = resgroup.phi_selections() rssel = [r.phi_selection() for r in resgroup] @@ -788,6 +792,10 @@ def test_psi_selections_single(self, GRO): assert_equal(psisel.residues.resids, [10, 11]) assert_equal(psisel.residues.resnames, ['GLY', 'ALA']) + def test_psi_selections_empty(self, GRO): + rgsel = GRO.segments[0].residues[[]].psi_selections() + assert len(rgsel) == 0 + def test_psi_selections(self, resgroup): rgsel = resgroup.psi_selections() rssel = [r.psi_selection() for r in resgroup] @@ -820,6 +828,10 @@ def test_omega_selection_name(self, GRO, kwargs, names): assert_equal(osel.residues.resids, [8, 9]) assert_equal(osel.residues.resnames, ['ALA', 'PRO']) + def test_omega_selections_empty(self, GRO): + rgsel = GRO.segments[0].residues[[]].omega_selections() + assert len(rgsel) == 0 + def test_omega_selections_single(self, GRO): rgsel = GRO.segments[0].residues[[7]].omega_selections() assert len(rgsel) == 1 @@ -869,6 +881,10 @@ def test_chi1_selections_single(self, GRO): assert_equal(sel.residues.resids, [13]) assert_equal(sel.residues.resnames, ['LYS']) + def test_chi1_selections_empty(self, GRO): + rgsel = GRO.segments[0].residues[[]].chi1_selections() + assert len(rgsel) == 0 + def test_chi1_selections(self, resgroup): rgsel = resgroup.chi1_selections() rssel = [r.chi1_selection() for r in resgroup]