From 266d05786ae1433ebdd41531a127b36bf7c781ef Mon Sep 17 00:00:00 2001 From: barlowa124 Date: Thu, 1 Oct 2026 19:45:58 -0400 Subject: [PATCH 1/2] Fix AnalysisBase.run misreading numpy boolean frame masks The boolean-mask detection in _setup_computation_groups checked isinstance(obj, bool), which misses np.bool_ elements. A numpy boolean array passed via frames= was therefore used as integer indices and the analysis silently ran on the wrong frames. Include np.bool_ in the type check so numpy masks are handled like Python boolean lists. Fixes #5472. --- package/AUTHORS | 1 + package/CHANGELOG | 5 ++++- package/MDAnalysis/analysis/base.py | 4 +++- testsuite/MDAnalysisTests/analysis/test_base.py | 16 ++++++++++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/package/AUTHORS b/package/AUTHORS index c8ba87c062a..b8af571375f 100644 --- a/package/AUTHORS +++ b/package/AUTHORS @@ -281,6 +281,7 @@ Chronological list of authors - Amarendra Mohan - Shubham Mittal - Charity Grey + - barlowa124 - Sai Udayagiri - Apoorva Verma - Aryaman Chaudhri diff --git a/package/CHANGELOG b/package/CHANGELOG index 45647f3904e..55c10927bb0 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -18,7 +18,7 @@ The rules for this file: spyke7, talagayev, tanii1125, BradyAJohnston, hejamu, jeremyleung521, harshitgajjela-droid, kunjsinha, aygarwal, jauy123, Dreamstick9, ollyfutur, Amarendra22, charity-g, ParthUppal523, apoorva-01, RMeli, - raulloiscuns, Aryaman-Chaudhri, sici17 + raulloiscuns, Aryaman-Chaudhri, sici17, barlowa124 * 2.11.0 @@ -33,6 +33,9 @@ Fixes apply their fit via `AtomGroup.rotate()`. Note: this changes existing behavior, as velocities/forces were previously left untouched by rotation. (Issue #5421, PR #5452) + * `AnalysisBase.run()` no longer misinterprets NumPy boolean masks passed + via the ``frames`` argument as integer frame indices, which silently + selected the wrong frames (Issue #5472, PR #5480). * Fix FileLock tests for XTC and TRR: lock file is no longer removed (#5382) * InterRDF now correctly returns bins in parallel (PR #5344) * `Merge()` no longer raises a TypeError on Universes that have a `cmaps` diff --git a/package/MDAnalysis/analysis/base.py b/package/MDAnalysis/analysis/base.py index f18b866951f..48a3575930d 100644 --- a/package/MDAnalysis/analysis/base.py +++ b/package/MDAnalysis/analysis/base.py @@ -616,7 +616,9 @@ def _setup_computation_groups( else: used_frames = frames - if all(isinstance(obj, bool) for obj in used_frames): + if all( + isinstance(obj, (bool, np.bool_)) for obj in used_frames + ): arange = np.arange(len(used_frames)) used_frames = arange[used_frames] diff --git a/testsuite/MDAnalysisTests/analysis/test_base.py b/testsuite/MDAnalysisTests/analysis/test_base.py index 0dd872bde57..7c1765671d2 100644 --- a/testsuite/MDAnalysisTests/analysis/test_base.py +++ b/testsuite/MDAnalysisTests/analysis/test_base.py @@ -337,6 +337,14 @@ def test_start_stop_step(u, run_kwargs, frames): }, (0, 1, 3, 5, 6, 8), ), + ( + { + "frames": np.array( + [True, True, False, True, False, True, + True, False, True, False], dtype=bool) + }, + (0, 1, 3, 5, 6, 8), + ), ], ) def test_frame_slice(u_xtc, run_kwargs, frames): @@ -370,6 +378,14 @@ def test_frame_slice(u_xtc, run_kwargs, frames): }, (0, 1, 3, 5, 6, 8), ), + ( + { + "frames": np.array( + [True, True, False, True, False, True, + True, False, True, False], dtype=bool) + }, + (0, 1, 3, 5, 6, 8), + ), ], ) def test_frame_slice_parallel(run_kwargs, frames, client_FrameAnalysis): From 7c58dddc553a0817e603bfd0d24f4f5f09c075fe Mon Sep 17 00:00:00 2001 From: barlowa124 Date: Fri, 2 Oct 2026 00:13:35 -0400 Subject: [PATCH 2/2] Format touched files with black (line-length 79) --- package/MDAnalysis/analysis/base.py | 4 +-- .../MDAnalysisTests/analysis/test_base.py | 32 ++++++++++++++++--- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/package/MDAnalysis/analysis/base.py b/package/MDAnalysis/analysis/base.py index 48a3575930d..3a8824b84c4 100644 --- a/package/MDAnalysis/analysis/base.py +++ b/package/MDAnalysis/analysis/base.py @@ -616,9 +616,7 @@ def _setup_computation_groups( else: used_frames = frames - if all( - isinstance(obj, (bool, np.bool_)) for obj in used_frames - ): + if all(isinstance(obj, (bool, np.bool_)) for obj in used_frames): arange = np.arange(len(used_frames)) used_frames = arange[used_frames] diff --git a/testsuite/MDAnalysisTests/analysis/test_base.py b/testsuite/MDAnalysisTests/analysis/test_base.py index 7c1765671d2..64cee58d298 100644 --- a/testsuite/MDAnalysisTests/analysis/test_base.py +++ b/testsuite/MDAnalysisTests/analysis/test_base.py @@ -340,8 +340,20 @@ def test_start_stop_step(u, run_kwargs, frames): ( { "frames": np.array( - [True, True, False, True, False, True, - True, False, True, False], dtype=bool) + [ + True, + True, + False, + True, + False, + True, + True, + False, + True, + False, + ], + dtype=bool, + ) }, (0, 1, 3, 5, 6, 8), ), @@ -381,8 +393,20 @@ def test_frame_slice(u_xtc, run_kwargs, frames): ( { "frames": np.array( - [True, True, False, True, False, True, - True, False, True, False], dtype=bool) + [ + True, + True, + False, + True, + False, + True, + True, + False, + True, + False, + ], + dtype=bool, + ) }, (0, 1, 3, 5, 6, 8), ),