From e6ab6207577a5fd77ccf02e71fdc048c75da788f Mon Sep 17 00:00:00 2001 From: sici17 Date: Thu, 24 Sep 2026 15:44:51 +0200 Subject: [PATCH 1/3] Fix numpy boolean frame masks Signed-off-by: sici17 --- package/CHANGELOG | 2 + package/MDAnalysis/analysis/base.py | 2 +- .../MDAnalysisTests/analysis/test_base.py | 42 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/package/CHANGELOG b/package/CHANGELOG index 45647f3904e..524f3918cba 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -23,6 +23,8 @@ The rules for this file: * 2.11.0 Fixes + * `AnalysisBase.run()` now interprets NumPy boolean arrays passed to `frames` + as frame masks instead of integer frame indices (Issue #5472) * Mass (u) has been added to MDAnalysis base units and clarified that physical constants use CODATA 2010 values (Issue #3944, PR #5439) * Added `.gitattributes` to enforce LF (\n) as line endings and renormalized diff --git a/package/MDAnalysis/analysis/base.py b/package/MDAnalysis/analysis/base.py index f18b866951f..3a8824b84c4 100644 --- a/package/MDAnalysis/analysis/base.py +++ b/package/MDAnalysis/analysis/base.py @@ -616,7 +616,7 @@ 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..7fea26f1d5e 100644 --- a/testsuite/MDAnalysisTests/analysis/test_base.py +++ b/testsuite/MDAnalysisTests/analysis/test_base.py @@ -337,6 +337,27 @@ def test_start_stop_step(u, run_kwargs, frames): }, (0, 1, 3, 5, 6, 8), ), + pytest.param( + { + "frames": np.array( + [ + True, + True, + False, + True, + False, + True, + True, + False, + True, + False, + ], + dtype=bool, + ) + }, + (0, 1, 3, 5, 6, 8), + id="numpy-bool-mask", + ), ], ) def test_frame_slice(u_xtc, run_kwargs, frames): @@ -370,6 +391,27 @@ def test_frame_slice(u_xtc, run_kwargs, frames): }, (0, 1, 3, 5, 6, 8), ), + pytest.param( + { + "frames": np.array( + [ + True, + True, + False, + True, + False, + True, + True, + False, + True, + False, + ], + dtype=bool, + ) + }, + (0, 1, 3, 5, 6, 8), + id="numpy-bool-mask", + ), ], ) def test_frame_slice_parallel(run_kwargs, frames, client_FrameAnalysis): From a52b23554e94feb28ccf05fa58acc61c9021b82b Mon Sep 17 00:00:00 2001 From: sici17 Date: Sat, 26 Sep 2026 20:16:57 +0200 Subject: [PATCH 2/3] Document boolean frame mask support Signed-off-by: sici17 --- package/MDAnalysis/analysis/base.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/package/MDAnalysis/analysis/base.py b/package/MDAnalysis/analysis/base.py index 3a8824b84c4..6904ac9bfcb 100644 --- a/package/MDAnalysis/analysis/base.py +++ b/package/MDAnalysis/analysis/base.py @@ -781,8 +781,10 @@ def run( step : int, optional number of frames to skip between each analysed frame frames : array_like, optional - array of integers or booleans to slice trajectory; ``frames`` can - only be used *instead* of ``start``, ``stop``, and ``step``. Setting + array of integers or booleans to slice trajectory. Boolean arrays, + including NumPy arrays, are used as masks for fancy indexing. + ``frames`` can only be used *instead* of ``start``, ``stop``, and + ``step``. Setting *both* ``frames`` and at least one of ``start``, ``stop``, ``step`` to a non-default value will raise a :exc:`ValueError`. @@ -831,6 +833,10 @@ def run( Introduced ``backend``, ``n_workers``, ``n_parts`` and ``unsupported_backend`` keywords, and refactored the method logic to support parallelizable execution. + + .. versionchanged:: 2.11.0 + NumPy boolean arrays passed to ``frames`` are now interpreted as + boolean masks. """ # default to serial execution backend = "serial" if backend is None else backend From 99d4270b15702a12ff357d5a1685d696db0a659d Mon Sep 17 00:00:00 2001 From: Francesco Siciliani Date: Mon, 28 Sep 2026 21:39:28 +0200 Subject: [PATCH 3/3] Fix wording in documentation for frames parameter --- package/MDAnalysis/analysis/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/MDAnalysis/analysis/base.py b/package/MDAnalysis/analysis/base.py index 6904ac9bfcb..8ab19be6dbf 100644 --- a/package/MDAnalysis/analysis/base.py +++ b/package/MDAnalysis/analysis/base.py @@ -782,7 +782,7 @@ def run( number of frames to skip between each analysed frame frames : array_like, optional array of integers or booleans to slice trajectory. Boolean arrays, - including NumPy arrays, are used as masks for fancy indexing. + including NumPy arrays, can be used as masks for fancy indexing. ``frames`` can only be used *instead* of ``start``, ``stop``, and ``step``. Setting *both* ``frames`` and at least one of ``start``, ``stop``, ``step``