Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure NoJump handles jumps that occur on the second frame in NPT trajectories #4258

Merged
merged 42 commits into from
Sep 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
cf412d9
Ensure atoms are unwrapped correctly when they jump across boundaries…
p-j-smith Aug 26, 2023
0a835ad
Add testfiles for nojump transformation
p-j-smith Aug 26, 2023
4efdf20
Add tests for nojump transformation where the jump occurs at the seco…
p-j-smith Aug 26, 2023
4a2b8a5
Remove unncessary second assignment of L in NoJump initialisation
p-j-smith Aug 26, 2023
7245bd1
Temporarily put test datafiles in the test directory
p-j-smith Aug 26, 2023
1dae25a
Add transformation to set variable box dimensions
p-j-smith Aug 26, 2023
0ed530e
Use set_variable_dimensions transformation in test for NoJump
p-j-smith Aug 26, 2023
66ccb4a
Remove the test datafiles for the npt NoJump
p-j-smith Aug 26, 2023
c3e8366
Import set_variable_dimensions in the transformation sub-package
p-j-smith Aug 26, 2023
58a7567
Fix typos in docsttring of set_variable_dimensions transformation
p-j-smith Aug 26, 2023
6f02285
remove unncessary comments in docstring of npt tests for nojump
p-j-smith Aug 26, 2023
7d90706
Add tests for set_variable_dimensions transformation
p-j-smith Aug 27, 2023
1b94f84
Add test to check that set_variable_dimensions raises an error when d…
p-j-smith Aug 27, 2023
970663a
Tidy formatting of set_variable_dimensions test functions
p-j-smith Aug 27, 2023
7ffaa2e
Add docs for set_variable_dimensions
p-j-smith Aug 27, 2023
21fdf0a
Add versionchanged note to NoJump about adding 'ag' argument
p-j-smith Aug 27, 2023
aa86f88
Add test to check if the nojump transform always returns the correct …
p-j-smith Aug 27, 2023
55d3c34
ensure coordinates can be unwrapped correctly when iterating
p-j-smith Aug 27, 2023
18d2bfc
Use tmp_path_factory to write tmp trajectory for testing NoJump repea…
p-j-smith Aug 27, 2023
a786599
Fix typo in boxdimensions module docstring
p-j-smith Aug 28, 2023
8e06cf3
Fix typo in docstring of NoJump test
p-j-smith Aug 28, 2023
ae8cef2
remove unnecessary second calculation of Linverse from NoJump
p-j-smith Aug 28, 2023
7ffc77a
Add a versionadded note to the set_variable_dimensions docstring
p-j-smith Aug 28, 2023
243c9f0
Update CHANGELOG
p-j-smith Aug 28, 2023
d673021
Remove added set_variable_dimensions class
p-j-smith Aug 28, 2023
e2e0052
Update tests to reflect removal of set_variable_dimensions transforma…
p-j-smith Aug 28, 2023
206a51f
Update CHANGELOG
p-j-smith Aug 28, 2023
296cd6a
Update NoJump tests to reflect removal of set_variable_dimensions tra…
p-j-smith Aug 28, 2023
90c037a
Remove unnecessary whitespace from NoJump tests
p-j-smith Aug 28, 2023
21ba7c3
Use numpy array in docstring example of set_dimensions transformation
p-j-smith Aug 28, 2023
8b3dfaf
Update changelog
p-j-smith Aug 28, 2023
44e473a
Use ValueError rather than NoDataError in set_dimensions
p-j-smith Aug 28, 2023
0d51b3d
Set NoJump.older_frame to -1 when ts.frame == 0.
p-j-smith Aug 28, 2023
2a2d100
Add comment to explain how we ensure jumps on the second frame are ha…
p-j-smith Aug 28, 2023
d735d71
Make flake8 happy
p-j-smith Aug 28, 2023
7f87344
Remove unnecessary if-block 'if self.prev is None:' from NoJump
p-j-smith Aug 29, 2023
35dc77c
Add test to check warning is emitted by NoJump when iterating traject…
p-j-smith Aug 29, 2023
cd339b6
Merge branch 'develop' into fix/no-jump-first-frame
IAlibay Aug 30, 2023
b1c32dc
Fix formatting of set_dimensions docstring
p-j-smith Aug 31, 2023
9a24312
Merge branch 'develop' into fix/no-jump-first-frame
p-j-smith Sep 1, 2023
8926e4a
Merge branch 'develop' into fix/no-jump-first-frame
IAlibay Sep 2, 2023
de33028
Merge remote-tracking branch 'upstream/develop' into fix/no-jump-firs…
hmacdope Sep 3, 2023
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: 4 additions & 1 deletion package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ The rules for this file:
* release numbers follow "Semantic Versioning" http://semver.org

------------------------------------------------------------------------------
??/??/?? IAlibay, ianmkenney, PicoCentauri, pgbarletta
??/??/?? IAlibay, ianmkenney, PicoCentauri, pgbarletta, p-j-smith

* 2.7.0

Fixes
* Fix `NoJump` unwrapping for jumps on the second frame in a
trajectory (PR #4258, Issue #4257)
* Deprecated np.float_ and np.NaN aliases have been replaced with
their original type value (np.float64 and np.nan) (PR #4272)

Expand Down Expand Up @@ -45,6 +47,7 @@ Fixes
* Fix Atom type guessing error (PR #4168, Issue #4167)

Enhancements
* Allow `set_dimensions` transformation to handle NPT trajectories (PR #4258)

Changes
* Reverts PR #4108, builds are now again made using the oldest
Expand Down
69 changes: 52 additions & 17 deletions package/MDAnalysis/transformations/boxdimensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,44 +25,64 @@
Set box dimensions --- :mod:`MDAnalysis.transformations.boxdimensions`
=======================================================================

Set dimensions of the simulation box to a constant vector across all timesteps.

Set dimensions of the simulation box, either to a constant vector across
all timesteps or to a specified vector at each frame.

.. autoclass:: set_dimensions
"""
import numpy as np

from .base import TransformationBase


class set_dimensions(TransformationBase):
"""
Set simulation box dimensions.

Timestep dimensions are modified in place.

Example
-------
Examples
--------

e.g. set simulation box dimensions to a vector containing unit cell
dimensions [*a*, *b*, *c*, *alpha*, *beta*, *gamma*], lengths *a*,
*b*, *c* are in the MDAnalysis length unit (Å), and angles are in degrees.
The same dimensions will be used for every frame in the trajectory.

.. code-block:: python

dim = np.array([2, 2, 2, 90, 90, 90])
transform = mda.transformations.boxdimensions.set_dimensions(dim)
u.trajectory.add_transformations(transform)

Or e.g. set simulation box dimensions to a vector containing unit cell
dimensions [*a*, *b*, *c*, *alpha*, *beta*, *gamma*] at the first frame,
and [*2a*, *2b*, *2c*, *alpha*, *beta*, *gamma*] at the second frame.

.. code-block:: python

dim = [2, 2, 2, 90, 90, 90]
dim = np.array([
[2, 2, 2, 90, 90, 90],
[4, 4, 4, 90, 90, 90],
])
transform = mda.transformations.boxdimensions.set_dimensions(dim)
u.trajectory.add_transformations(transform)

Parameters
----------
dimensions: iterable of floats
dimensions: iterable of floats or two-dimensional np.typing.NDArrayLike
vector that contains unit cell lengths and angles.
Expected shapes are (6, 0) or (1, 6)
Expected shapes are (6, 0) or (1, 6) or (N, 6), where
N is the number of frames in the trajectory. If shape is (6, 0) or
(1, 6), the same dimensions will be used at every frame in the
trajectory.

Returns
-------
:class:`~MDAnalysis.coordinates.timestep.Timestep` object


.. versionchanged:: 2.7.0
p-j-smith marked this conversation as resolved.
Show resolved Hide resolved
Added the option to set varying box dimensions (i.e. an NPT trajectory).
"""

def __init__(self,
Expand All @@ -76,19 +96,34 @@ def __init__(self,
try:
self.dimensions = np.asarray(self.dimensions, np.float32)
hmacdope marked this conversation as resolved.
Show resolved Hide resolved
except ValueError:
errmsg = (f'{self.dimensions} cannot be converted into '
'np.float32 numpy.ndarray')
errmsg = (
f'{self.dimensions} cannot be converted into '
'np.float32 numpy.ndarray'
)
raise ValueError(errmsg)
try:
self.dimensions = self.dimensions.reshape(6, )
self.dimensions = self.dimensions.reshape(-1, 6)
except ValueError:
errmsg = (f'{self.dimensions} array does not have valid box '
'dimension shape.\nSimulation box dimensions are '
'given by an float array of shape (6, ), '
' containing 3 lengths and 3 angles: '
'[a, b, c, alpha, beta, gamma]')
errmsg = (
f'{self.dimensions} array does not have valid box '
'dimension shape.\nSimulation box dimensions are '
'given by an float array of shape (6, 0), (1, 6), '
'or (N, 6) where N is the number of frames in the '
'trajectory and the dimension vector(s) containing '
'3 lengths and 3 angles: '
'[a, b, c, alpha, beta, gamma]'
)
raise ValueError(errmsg)

def _transform(self, ts):
ts.dimensions = self.dimensions
try:
ts.dimensions = (
self.dimensions[0]
if self.dimensions.shape[0] == 1
else self.dimensions[ts.frame]
)
except IndexError as e:
raise ValueError(
f"Dimensions array has no data for frame {ts.frame}"
) from e
return ts
12 changes: 10 additions & 2 deletions package/MDAnalysis/transformations/nojump.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,17 @@ def _transform(self, ts):
except np.linalg.LinAlgError:
msg = f"Periodic box dimensions are not invertible at step {ts.frame}"
raise NoDataError(msg)
hmacdope marked this conversation as resolved.
Show resolved Hide resolved
if self.prev is None:
if ts.frame == 0:
IAlibay marked this conversation as resolved.
Show resolved Hide resolved
# We don't need to apply the transformation here. However, we need to
# ensure we have the 0th frame coordinates in reduced form. We also need to
# set an an appropriate value for self.older frame. This is so that on the
# following frame we don't return early when we check
# `self.older_frame != "A"`. If we return early, then the transformation is
# not applied, and any jumps across boundaries that occur at that frame will
# not be accounted for.
self.prev = ts.positions @ Linverse
self.old_frame = ts.frame
self.old_frame = 0
self.older_frame = -1
return ts
if (
self.check_c
Expand Down
1 change: 0 additions & 1 deletion testsuite/MDAnalysisTests/datafiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,5 @@
SURFACE_PDB = (_data_ref / 'surface.pdb.bz2').as_posix()
SURFACE_TRR = (_data_ref / 'surface.trr').as_posix()


# This should be the last line: clean up namespace
del resources
48 changes: 47 additions & 1 deletion testsuite/MDAnalysisTests/transformations/test_boxdimensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ def boxdimensions_universe():
return new_u


@pytest.fixture()
def variable_boxdimensions_universe():
# create Universe objects for tests
n_atoms = 1
n_frames = 3
u = mdanalysis.Universe.empty(n_atoms, trajectory=True)
coordinates = np.zeros((n_frames, u.atoms.n_atoms, 3))
u.load_new(coordinates, order="fac")
return u


def test_boxdimensions_dims(boxdimensions_universe):
new_dims = np.float32([2, 2, 2, 90, 90, 90])
set_dimensions(new_dims)(boxdimensions_universe.trajectory.ts)
Expand Down Expand Up @@ -72,7 +83,6 @@ def test_dimensions_vector_asarray(boxdimensions_universe,
with pytest.raises(ValueError, match='cannot be converted'):
set_dimensions(dim_vector_forms_dtypes)(ts)


def test_dimensions_transformations_api(boxdimensions_universe):
# test if transformation works with on-the-fly transformations API
new_dims = np.float32([2, 2, 2, 90, 90, 90])
Expand All @@ -81,3 +91,39 @@ def test_dimensions_transformations_api(boxdimensions_universe):
for ts in boxdimensions_universe.trajectory:
assert_array_almost_equal(boxdimensions_universe.dimensions,
new_dims, decimal=6)


def test_varying_dimensions_transformations_api(
variable_boxdimensions_universe,
):
"""
Test if transformation works with on-the-fly transformations API
when we have varying dimensions.
"""
new_dims = np.float32([
[2, 2, 2, 90, 90, 90],
[4, 4, 4, 90, 90, 90],
[8, 8, 8, 90, 90, 90],
])
transform = set_dimensions(new_dims)
variable_boxdimensions_universe.trajectory.add_transformations(transform)
for ts in variable_boxdimensions_universe.trajectory:
assert_array_almost_equal(variable_boxdimensions_universe.dimensions,
new_dims[ts.frame], decimal=6)

p-j-smith marked this conversation as resolved.
Show resolved Hide resolved

def test_varying_dimensions_no_data(
variable_boxdimensions_universe,
):
"""
Check error is raised when dimensions are missing for a frame
in a trajectory.
"""
# trjactory has three frames
new_dims = np.float32([
[2, 2, 2, 90, 90, 90],
[4, 4, 4, 90, 90, 90],
])
transform = set_dimensions(new_dims)
with pytest.raises(ValueError, match="Dimensions array has no data for frame 2"):
variable_boxdimensions_universe.trajectory.add_transformations(transform)
Loading