Added NoJump on-the-fly example to MSD documentation (Issue #4169)
This PR updates the MSD documentation inside MDAnalysis.analysis.msd to show users how to correctly apply the NoJump transformation using on-the-fly trajectory transformations.
Issue #4169 noted that although NoJump exists, there was no example showing how to use it directly in MDAnalysis.
This PR adds:
- A minimal working example demonstrating NoJump usage
- Clarification that wrapped coordinates must be unwrapped
- A modern MDAnalysis-based workflow that does not require external tools
Fixes: #4169 I would appreciate any feedback or suggestions for improvement. Reviews are very welcome, and I’m happy to revise the PR based on your guidance.
📚 Documentation preview 📚: https://mdanalysis--5165.org.readthedocs.build/en/5165/
Thank you for pointing this out! I’ve updated the file to restore the correct license header (LGPL v2.1+).
Codecov Report
:white_check_mark: All modified and coverable lines are covered by tests.
:white_check_mark: Project coverage is 92.72%. Comparing base (bbcef1b) to head (66d7f12).
:warning: Report is 1 commits behind head on develop.
Additional details and impacted files
@@ Coverage Diff @@
## develop #5165 +/- ##
========================================
Coverage 92.72% 92.72%
========================================
Files 180 180
Lines 22472 22472
Branches 3188 3188
========================================
Hits 20837 20837
Misses 1177 1177
Partials 458 458
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
@orbeckst
- Added the example script used to verify the documentation change.
- Updated the AUTHORS file to follow the existing formatting.
- Updated the documentation to clarify that
NoJumprequires periodic boundary conditions, avoiding errors when PBC information is not present.
As input , I used the built-in MDAnalysis random walk test data:
RANDOM_WALK_TOPORANDOM_WALK
Below is the MSD plot produced by running the example script.
After applying NoJump, the MSD increases linearly with lag time, as
expected for normal diffusion, and follows the known 3D Brownian
behavior (≈ 6τ).
Is RANDOM_WALK a good example, namely, did you check that it was actually generated with periodic boundary conditions?
In order to demonstrate that the example is working, I'd expect to see a comparison between omitting and using NoJump.
Thanks for the clarification,
After checking more carefully, I realized that:-
- Relying on existing trajectories (e.g. RANDOM_WALK or other test data) is not reliable for demonstrating NoJump, since many of them are either not generated with periodic boundary conditions or do not exhibit explicit single-frame boundary crossings.
Instead of assuming wrapping is present in the input data, I will construct a deterministic test where a periodic box is defined and an artificial jump across the boundary is introduced between consecutive frames. This allows us to verify the intended behavior directly.
@orbeckst
Without NoJump, periodic wrapping causes an artificial discontinuity; applying NoJump restores the continuous trajectory across the boundary.
the code i used to demonstrate this --
import numpy as np
import matplotlib.pyplot as plt
import MDAnalysis as mda
from MDAnalysis.coordinates.memory import MemoryReader
from MDAnalysis.transformations import NoJump
n_atoms = 1
n_frames = 2
box_length = 10.0 # cubic box, Ã…
pos0 = np.array([[9.8, 5.0, 5.0]])
pos1 = np.array([[0.2, 5.0, 5.0]])
coords = np.array([pos0, pos1]) # shape (n_frames, n_atoms, 3)
dimensions = np.array([
[box_length, box_length, box_length, 90, 90, 90],
[box_length, box_length, box_length, 90, 90, 90],
])
# ----------------------------
# 2. Universe WITHOUT NoJump
# ----------------------------
u_plain = mda.Universe.empty(
n_atoms,
trajectory=True,
)
u_plain.load_new(
coords,
format=MemoryReader,
dimensions=dimensions,
)
x_plain = [u_plain.atoms.positions[0,0] for _ in u_plain.trajectory]
# ----------------------------
# 3. Universe WITH NoJump
# ----------------------------
u_nj = mda.Universe.empty(
n_atoms,
trajectory=True,
)
u_nj.load_new(
coords,
format=MemoryReader,
dimensions=dimensions,
)
u_nj.trajectory.add_transformations(NoJump())
x_nj = [u_nj.atoms.positions[0,0] for _ in u_nj.trajectory]
# ----------------------------
# 4. Plot
# ----------------------------
frames=[0,1]
plt.figure()
plt.plot(frames, x_plain, marker='o', label="Without nojump")
plt.plot(frames, x_nj,marker='o', label="With nojump")
plt.axhline(box_length, linestyle='--', linewidth=1)
plt.axhline(0, linestyle='--', linewidth=1)
plt.xlabel("Frame")
plt.ylabel("x position (A)")
plt.title("Effect of NoJump on Periodic Boundary Crossing")
plt.legend()
plt.show()