From d873d367fcada2441cbad7b2f5060c8dc59772bd Mon Sep 17 00:00:00 2001 From: Rocco Meli Date: Wed, 12 Feb 2020 10:42:21 +0000 Subject: [PATCH] Minimum mass for hydrogen guess in HydrogenBondAnalysis (#2516) * added minimum hydrogen mass --- package/CHANGELOG | 2 + .../analysis/hydrogenbonds/hbond_analysis.py | 22 +++++-- .../analysis/test_hydrogenbonds_analysis.py | 58 ++++++++++++++++++- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/package/CHANGELOG b/package/CHANGELOG index 1914638f6d6..ab6301484ba 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -84,6 +84,8 @@ Enhancements * Improve the distance search in water bridge analysis with capped_distance (PR #2480) Changes + * Added `min_mass` parameter to `guess_hydrogens` function in `HydrogenBondAnalysis` + set to 0.9 by default (Issue #2472) * Removes `save()` function from contacts, diffusionmap, hole, LinearDensity, and rms (Issue #1745). * Removes; `save_table()` from :class:`HydrogenBondAnalysis`, diff --git a/package/MDAnalysis/analysis/hydrogenbonds/hbond_analysis.py b/package/MDAnalysis/analysis/hydrogenbonds/hbond_analysis.py index b5d0eb5a3fe..aa8fca2c5c7 100644 --- a/package/MDAnalysis/analysis/hydrogenbonds/hbond_analysis.py +++ b/package/MDAnalysis/analysis/hydrogenbonds/hbond_analysis.py @@ -169,7 +169,7 @@ """ from __future__ import absolute_import, division -import numpy as np +import numpy as np from .. import base from MDAnalysis.lib.distances import capped_distance, calc_angles @@ -234,7 +234,12 @@ def __init__(self, universe, donors_sel=None, hydrogens_sel=None, acceptors_sel= self.d_h_a_angle = d_h_a_angle_cutoff self.update_selections = update_selections - def guess_hydrogens(self, selection='all', max_mass=1.1, min_charge=0.3): + def guess_hydrogens(self, + selection='all', + max_mass=1.1, + min_charge=0.3, + min_mass=0.9 + ): """Guesses which hydrogen atoms should be used in the analysis. Parameters @@ -263,14 +268,21 @@ def guess_hydrogens(self, selection='all', max_mass=1.1, min_charge=0.3): Alternatively, this function may be used to quickly generate a :class:`str` of potential hydrogen atoms involved in hydrogen bonding. This str may then be modified before being used to set the attribute :attr:`hydrogens_sel`. + + .. versionchanged: 1.0.0 + Added `min_mass` parameter to specify minimum mass (Issue #2472) """ + if min_mass > max_mass: + raise ValueError("min_mass is higher than (or equal to) max_mass") + ag = self.u.select_atoms(selection) hydrogens_ag = ag[ - np.logical_and( + np.logical_and.reduce(( ag.masses < max_mass, - ag.charges > min_charge - ) + ag.charges > min_charge, + ag.masses > min_mass, + )) ] hydrogens_list = np.unique( diff --git a/testsuite/MDAnalysisTests/analysis/test_hydrogenbonds_analysis.py b/testsuite/MDAnalysisTests/analysis/test_hydrogenbonds_analysis.py index b4b1ecb5c2a..451b3590cf8 100644 --- a/testsuite/MDAnalysisTests/analysis/test_hydrogenbonds_analysis.py +++ b/testsuite/MDAnalysisTests/analysis/test_hydrogenbonds_analysis.py @@ -57,7 +57,7 @@ def h(self, universe): def test_hbond_analysis(self, h): - assert len(np.unique(h.hbonds[:,0])) == 10 + assert len(np.unique(h.hbonds[:, 0])) == 10 assert len(h.hbonds) == 32 reference = { @@ -98,6 +98,7 @@ def test_count_by_ids(self, h): assert_array_equal(counts, ref_counts) + class TestHydrogenBondAnalysisTIP3P_GuessAcceptors_GuessHydrogens_UseTopology_(TestHydrogenBondAnalysisTIP3P): """Uses the same distance and cutoff hydrogen bond criteria as :class:`TestHydrogenBondAnalysisTIP3P`, so the results are identical, but the hydrogens and acceptors are guessed whilst the donor-hydrogen pairs are determined @@ -111,6 +112,7 @@ class TestHydrogenBondAnalysisTIP3P_GuessAcceptors_GuessHydrogens_UseTopology_(T 'd_h_a_angle_cutoff': 120.0 } + class TestHydrogenBondAnalysisTIP3P_GuessDonors_NoTopology(object): """Guess the donor atoms involved in hydrogen bonds using the partial charges of the atoms. """ @@ -141,6 +143,58 @@ def test_guess_donors(self, h): assert donors == ref_donors +class TestHydrogenBondAnalysisTIP3P_GuessHydrogens_NoTopology(object): + """ + Guess the hydrogen atoms involved in hydrogen bonds using the mass and + partial charge of the atoms. + """ + + @staticmethod + @pytest.fixture(scope='class') + def universe(): + return MDAnalysis.Universe(waterPSF, waterDCD) + + kwargs = { + 'donors_sel': None, + 'hydrogens_sel': None, + 'acceptors_sel': None, + 'd_h_cutoff': 1.2, + 'd_a_cutoff': 3.0, + 'd_h_a_angle_cutoff': 120.0 + } + + @pytest.fixture(scope='class') + def h(self, universe): + h = HydrogenBondAnalysis(universe, **self.kwargs) + return h + + def test_guess_hydrogens(self, h): + + ref_hydrogens = "(resname TIP3 and name H1) or (resname TIP3 and name H2)" + hydrogens = h.guess_hydrogens(selection='all') + assert hydrogens == ref_hydrogens + + pytest.mark.parametrize( + "min_mass, max_mass, min_charge", + [ + (1.05, 1.10, 0.30), + (0.90, 0.95, 0.30), + (0.90, 1.10, 1.00) + ] + ) + def test_guess_hydrogens_empty_selection(self, h): + + hydrogens = h.guess_hydrogens(selection='all', min_charge=1.0) + assert hydrogens == "" + + def test_guess_hydrogens_min_max_mass(self, h): + + errmsg = "min_mass is higher than \(or equal to\) max_mass" + + with pytest.raises(ValueError, match=errmsg): + + h.guess_hydrogens(selection='all', min_mass=1.1, max_mass=0.9) + class TestHydrogenBondAnalysisTIP3PStartStep(object): """Uses the same distance and cutoff hydrogen bond criteria as :class:`TestHydrogenBondAnalysisTIP3P` but starting with the second frame and using every other frame in the analysis. @@ -168,7 +222,7 @@ def h(self, universe): def test_hbond_analysis(self, h): - assert len(np.unique(h.hbonds[:,0])) == 5 + assert len(np.unique(h.hbonds[:, 0])) == 5 assert len(h.hbonds) == 15 reference = {