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
8 changes: 8 additions & 0 deletions package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ Enhancements
checking if it can be used in parallel analysis. (Issue #2996, PR #2950)

Changes
* `analysis.hole2.HoleAnalysis` now stores ``sphpdbs``, ``outfiles``
and ``profiles`` in the `analysis.base.Results` class (Issues #3261, #3269)
* `helix_analysis.HELANAL` now uses the `analysis.base.Results` class to
store results attributes (Issue #3261, #3267)
* `analysis.diffusionmap.DistanceMatrix` class now stores `dist_matrix`
using the `analysis.base.Results` class (Issues #3288, #3290)
* `analysis.align.AlignTraj` and `analysis.align.AverageStructure` now store
Expand Down Expand Up @@ -243,6 +247,10 @@ Changes
* Added OpenMM coordinate and topology converters (Issue #2863, PR #2917)

Deprecations
* The ``sphpdbs``, ``outfiles`` and ``profiles`` attributes of
`analysis.hole2.HoleAnalysis` are now deprecated in favour of
``results.sphpdbs``, ``results.outfiles`` and
``results.profiles`` (Issues #3261, #3269)
* The `analysis.diffusionmap.DistanceMatrix.dist_matrix` is now deprecated in
favour of `analysis.diffusionmap.DistanceMatrix.results.dist_matrix`.
It will be removed in 3.0.0 (Issues #3288, #3290)
Expand Down
125 changes: 90 additions & 35 deletions package/MDAnalysis/analysis/hole2/hole.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@
The VMD surface created by the class updates the pore for each frame of the trajectory.
Use it as normal by loading your trajectory in VMD and sourcing the file in the Tk Console.

You can access the actual profiles generated in the ``results`` attribute::

print(ha.results.profiles)

Again, HOLE writes out files for each frame. If you would like to delete these files
after the analysis, you can call :meth:`~HoleAnalysis.delete_temporary_files`::

Expand Down Expand Up @@ -513,14 +517,52 @@ class HoleAnalysis(AnalysisBase):
Files are called `hole.inp`.


Returns
-------
dict
Attributes
----------
results.sphpdbs: numpy.ndarray
Array of sphpdb filenames

.. versionadded:: 2.0.0

results.outfiles: numpy.ndarray
Arrau of output filenames

.. versionadded:: 2.0.0

results.profiles: dict
Profiles generated by HOLE2.
A dictionary of :class:`numpy.recarray`\ s, indexed by frame.

.. versionadded:: 2.0.0

sphpdbs: numpy.ndarray
Alias of :attr:`results.sphpdbs`

.. deprecated:: 2.0.0
This will be removed in MDAnalysis 3.0.0. Please use
:attr:`results.sphpdbs` instead.

outfiles: numpy.ndarray
Alias of :attr:`results.outfiles`

.. deprecated:: 2.0.0
This will be removed in MDAnalysis 3.0.0. Please use
:attr:`results.outfiles` instead.

profiles: dict
Alias of :attr:`results.profiles`

.. deprecated:: 2.0.0
This will be removed in MDAnalysis 3.0.0. Please use
:attr:`results.profiles` instead.

.. versionadded:: 1.0

.. versionchanged:: 2.0.0
:attr:`sphpdbs`, :attr:`outfiles` and :attr:`profiles `
are now stored in a :class:`MDAnalysis.analysis.base.Results`
instance.

"""

input_file = '{prefix}hole{i:03d}.inp'
Expand Down Expand Up @@ -552,11 +594,6 @@ class HoleAnalysis(AnalysisBase):

_guess_cpoint = False

sphpdbs = None
outfiles = None
frames = None
profiles = None

def __init__(self, universe,
select='protein',
verbose=False,
Expand Down Expand Up @@ -680,13 +717,36 @@ def run(self, start=None, stop=None, step=None, verbose=None,
return super(HoleAnalysis, self).run(start=start, stop=stop,
step=step, verbose=verbose)

@property
def sphpdbs(self):
wmsg = ("The `sphpdbs` attribute was deprecated in "
"MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. "
"Please use `results.sphpdbs` instead.")
warnings.warn(wmsg, DeprecationWarning)
return self.results.sphpdbs

@property
def outfiles(self):
wmsg = ("The `outfiles` attribute was deprecated in "
"MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. "
"Please use `results.outfiles` instead.")
warnings.warn(wmsg, DeprecationWarning)
return self.results.outfiles

@property
def profiles(self):
wmsg = ("The `profiles` attribute was deprecated in "
"MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. "
"Please use `results.profiles` instead.")
warnings.warn(wmsg, DeprecationWarning)
return self.results.profiles

def _prepare(self):
"""Set up containers and generate input file text"""
# set up containers
self.sphpdbs = np.zeros(self.n_frames, dtype=object)
self.outfiles = np.zeros(self.n_frames, dtype=object)
self.frames = np.zeros(self.n_frames, dtype=int)
self.profiles = {}
self.results.sphpdbs = np.zeros(self.n_frames, dtype=object)
self.results.outfiles = np.zeros(self.n_frames, dtype=object)
self.results.profiles = {}

# generate input file
body = set_up_hole_input('',
Expand Down Expand Up @@ -733,7 +793,6 @@ def _single_frame(self):
self.tmp_files.append(sphpdb)
else:
self.tmp_files.append(sphpdb + '.old')
self.frames[i] = frame

# temp pdb
logger.info('HOLE analysis frame {}'.format(frame))
Expand Down Expand Up @@ -763,7 +822,7 @@ def _single_frame(self):

recarrays = collect_hole(outfile=outfile)
try:
self.profiles[frame] = recarrays[0]
self.results.profiles[frame] = recarrays[0]
except KeyError:
msg = 'No profile found in HOLE output. Output level: {}'
logger.info(msg.format(self.output_level))
Expand Down Expand Up @@ -821,11 +880,12 @@ def create_vmd_surface(self, filename='hole.vmd', dot_density=15,
``filename`` with the pore surfaces.

"""
if self.sphpdbs is None or len(self.sphpdbs) == 0:
if not np.any(self.results.get("sphpdbs", [])):
raise ValueError('No sphpdb files to read. Try calling run()')

frames = []
for i, sphpdb in zip(self.frames, self.sphpdbs[self.frames]):
for i in self.frames:
sphpdb = self.results.sphpdbs[i]
tmp_tri = create_vmd_surface(sphpdb=sphpdb,
sph_process=self.exe['sph_process'],
sos_triangle=self.exe['sos_triangle'],
Expand Down Expand Up @@ -870,15 +930,10 @@ def create_vmd_surface(self, filename='hole.vmd', dot_density=15,

def min_radius(self):
"""Return the minimum radius over all profiles as a function of q"""
if not self.profiles:
raise ValueError('No profiles available. Try calling run()')
return np.array([[q, p.radius.min()] for q, p in self.profiles.items()])

def min_radius(self):
"""Return the minimum radius over all profiles as a function of q"""
if not self.profiles:
profiles = self.results.get("profiles")
if not profiles:
raise ValueError('No profiles available. Try calling run()')
return np.array([[q, p.radius.min()] for q, p in self.profiles.items()])
return np.array([[q, p.radius.min()] for q, p in profiles.items()])

def delete_temporary_files(self):
"""Delete temporary files"""
Expand All @@ -888,8 +943,8 @@ def delete_temporary_files(self):
except OSError:
pass
self.tmp_files = []
self.outfiles = []
self.sphpdbs = []
self.results.outfiles = []
self.results.sphpdbs = []

def __enter__(self):
return self
Expand Down Expand Up @@ -987,17 +1042,17 @@ def plot(self, frames=None,

"""

if not self.profiles:
if not self.results.get("profiles"):
raise ValueError('No profiles available. Try calling run()')

if ax is None:
fig, ax = plt.subplots()

fcl = self._process_plot_kwargs(frames=frames,
color=color, cmap=cmap, linestyle=linestyle)
fcl = self._process_plot_kwargs(frames=frames, color=color,
cmap=cmap, linestyle=linestyle)

for i, (frame, c, ls) in enumerate(zip(*fcl)):
profile = self.profiles[frame]
profile = self.results.profiles[frame]
dy = i*y_shift
ax.plot(profile.rxn_coord, profile.radius+dy, color=c,
linestyle=ls, zorder=-frame, label=str(frame),
Expand Down Expand Up @@ -1051,7 +1106,7 @@ def plot3D(self, frames=None,

"""

if not self.profiles:
if not self.results.get("profiles"):
raise ValueError('No profiles available. Try calling run()')

from mpl_toolkits.mplot3d import Axes3D
Expand All @@ -1065,7 +1120,7 @@ def plot3D(self, frames=None,
linestyle=linestyle)

for frame, c, ls in zip(*fcl):
profile = self.profiles[frame]
profile = self.results.profiles[frame]
if r_max is None:
radius = profile.radius
rxn_coord = profile.rxn_coord
Expand Down Expand Up @@ -1104,7 +1159,7 @@ def over_order_parameters(self, order_parameters, frames=None):
sorted dictionary of {order_parameter:profile}

"""
if not self.profiles:
if not self.results.get("profiles"):
raise ValueError('No profiles available. Try calling run()')
if isinstance(order_parameters, str):
try:
Expand Down Expand Up @@ -1137,7 +1192,7 @@ def over_order_parameters(self, order_parameters, frames=None):

profiles = OrderedDict()
for frame in sorted_frames:
profiles[order_parameters[frame]] = self.profiles[frame]
profiles[order_parameters[frame]] = self.results.profiles[frame]

return profiles

Expand Down Expand Up @@ -1220,7 +1275,7 @@ def gather(self, frames=None, flat=False):
if frames is None:
frames = self.frames
frames = util.asiterable(frames)
profiles = [self.profiles[k] for k in frames]
profiles = [self.results.profiles[k] for k in frames]

rxncoords = [p.rxn_coord for p in profiles]
radii = [p.radius for p in profiles]
Expand Down
34 changes: 21 additions & 13 deletions testsuite/MDAnalysisTests/analysis/test_hole2.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,18 +252,26 @@ def frames(self, universe):

@pytest.fixture()
def profiles(self, hole, frames):
return [hole.profiles[f] for f in frames]
return [hole.results.profiles[f] for f in frames]

@pytest.mark.parametrize("attrname", ["sphpdbs", "outfiles", "profiles"])
def test_deprecated_warning(self, hole, attrname):
wmsg = (f"The `{attrname}` attribute was deprecated in "
"MDAnalysis 2.0.0 and will be removed in MDAnalysis 3.0.0. "
f"Please use `results.{attrname}` instead.")
with pytest.warns(DeprecationWarning, match=wmsg):
value = getattr(hole, attrname)
assert value is hole.results[attrname]

class TestHoleAnalysis(BaseTestHole):

def test_correct_profile_values(self, hole, frames):
assert_equal(sorted(hole.profiles.keys()), frames,
err_msg="hole.profiles.keys() should contain the frame numbers")
assert_equal(sorted(hole.results.profiles.keys()), frames,
err_msg="hole.results.profiles.keys() should contain the frame numbers")
assert_equal(list(hole.frames), frames,
err_msg="hole.frames should contain the frame numbers")
data = np.transpose([(len(p), p.rxn_coord.mean(), p.radius.min())
for p in hole.profiles.values()])
for p in hole.results.profiles.values()])
assert_equal(data[0], [401, 399], err_msg="incorrect profile lengths")
assert_almost_equal(data[1], [1.98767, 0.0878],
err_msg="wrong mean HOLE rxn_coord")
Expand Down Expand Up @@ -305,7 +313,7 @@ def test_output_level(self, tmpdir, universe):
stop=self.stop, random_seed=self.random_seed)

# no profiles
assert len(h.profiles) == 0
assert len(h.results.profiles) == 0

def test_cpoint_geometry(self, tmpdir, universe):
protein = universe.select_atoms('protein')
Expand Down Expand Up @@ -446,15 +454,15 @@ def order_parameter_keys_values(self, hole):

def test_gather(self, hole):
gd = hole.gather(flat=False)
for i, p in enumerate(hole.profiles.values()):
for i, p in enumerate(hole.results.profiles.values()):
assert_almost_equal(p.rxn_coord, gd['rxn_coord'][i])
assert_almost_equal(p.radius, gd['radius'][i])
assert_almost_equal(p.cen_line_D, gd['cen_line_D'][i])

def test_gather_flat(self, hole):
gd = hole.gather(flat=True)
i = 0
for p in hole.profiles.values():
for p in hole.results.profiles.values():
j = i+len(p.rxn_coord)
assert_almost_equal(p.rxn_coord, gd['rxn_coord'][i:j])
assert_almost_equal(p.radius, gd['radius'][i:j])
Expand All @@ -464,7 +472,7 @@ def test_gather_flat(self, hole):

def test_min_radius(self, hole):
rad = hole.min_radius()
for (f1, p), (f2, r) in zip(hole.profiles.items(), rad):
for (f1, p), (f2, r) in zip(hole.results.profiles.items(), rad):
assert_equal(f1, f2)
assert_almost_equal(min(p.radius), r)

Expand All @@ -477,7 +485,7 @@ def test_over_order_parameters(self, hole):
assert key == rmsd

idx = np.argsort(op)
arr = np.array(list(hole.profiles.values()), dtype=object)
arr = np.array(list(hole.results.profiles.values()), dtype=object)
for op_prof, arr_prof in zip(profiles.values(), arr[idx]):
assert op_prof is arr_prof

Expand All @@ -493,7 +501,7 @@ def test_over_order_parameters_file(self, hole, tmpdir):
assert key == rmsd

idx = np.argsort(op)
arr = np.array(list(hole.profiles.values()), dtype=object)
arr = np.array(list(hole.results.profiles.values()), dtype=object)
for op_prof, arr_prof in zip(profiles.values(), arr[idx]):
assert op_prof is arr_prof

Expand All @@ -516,7 +524,7 @@ def test_over_order_parameters_frames(self, hole):
assert key == rmsd

idx = np.argsort(op[:n_frames])
values = list(hole.profiles.values())[:n_frames]
values = list(hole.results.profiles.values())[:n_frames]
arr = np.array(values, dtype=object)
for op_prof, arr_prof in zip(profiles.values(), arr[idx]):
assert op_prof is arr_prof
Expand All @@ -532,7 +540,7 @@ def test_bin_radii(self, hole):
assert len(radii) == (len(bins)-1)

# check first frame profile
first = hole.profiles[0]
first = hole.results.profiles[0]
for row in first:
coord = row.rxn_coord
rad = row.radius
Expand All @@ -558,7 +566,7 @@ def test_bin_radii_range(self, hole, midpoint):
assert len(radii) == (len(bins)-1)

# check first frame profile
first = hole.profiles[0]
first = hole.results.profiles[0]
for row in first:
coord = row.rxn_coord
rad = row.radius
Expand Down