Skip to content

Commit c32fe45

Browse files
authored
FIX: Auto-detect non-linear frame spacing in MSD._conclude()
Previously, `_conclude()` only used `_conclude_non_linear()` if the user explicitly set `non_linear=True`. Now it also checks whether the time intervals between frames are non-uniform (e.g. when passing `frames=[0, 1, 3, 6]`) and automatically falls back to the non-linear path, preventing incorrect MSD results from unevenly-spaced trajectories.
1 parent 6d8cb87 commit c32fe45

3 files changed

Lines changed: 23 additions & 1 deletion

File tree

package/CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ The rules for this file:
2222
* 2.11.0
2323

2424
Fixes
25+
* Fixes msd for non-linear frames, when non_linear is not explicitly
26+
provided (Issue #5100, PR #5254)
2527
* Fixes TypeError with np.int64 indexing in GSD Reader (Issue #5224)
2628
* Fixed bug in add_transformations allowing non-callable transformations
2729
(Issue #2558, PR #2558)

package/MDAnalysis/analysis/msd.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,12 @@ def _single_frame(self):
427427
]
428428

429429
def _conclude(self):
430-
if self.non_linear:
430+
delta_time = np.diff(self.times)
431+
432+
if self.non_linear or (
433+
len(delta_time) > 1
434+
and not (np.allclose(delta_time, delta_time[0]))
435+
):
431436
self._conclude_non_linear()
432437
else:
433438
if self.fft:

testsuite/MDAnalysisTests/analysis/test_msd.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,3 +1848,18 @@ def test_start_stop_step(self, u_nonlinear):
18481848
assert_allclose(
18491849
result_msd_per_particle, expected_msd_per_particle, rtol=1e-5
18501850
)
1851+
1852+
def test_detect_non_linear_from_frames(self, step_traj):
1853+
msd_auto = MSD(step_traj, select="all", msd_type="xyz", fft=False)
1854+
res1 = msd_auto.run(frames=[0, 1, 3, 6])
1855+
1856+
msd_explicit = MSD(
1857+
step_traj, select="all", msd_type="xyz", non_linear=True
1858+
)
1859+
res2 = msd_explicit.run(frames=[0, 1, 3, 6])
1860+
1861+
assert_allclose(
1862+
res1.results.msds_by_particle,
1863+
res2.results.msds_by_particle,
1864+
rtol=1e-5,
1865+
)

0 commit comments

Comments
 (0)