diff --git a/.github/actions/build-src/action.yaml b/.github/actions/build-src/action.yaml index 8abab0f13e9..95f4eb95401 100644 --- a/.github/actions/build-src/action.yaml +++ b/.github/actions/build-src/action.yaml @@ -66,6 +66,15 @@ runs: micromamba info micromamba list + - name: mda_deps + shell: bash -l {0} + run: | + # Install mdakit deps that depend on MDA + python -m pip install --no-deps \ + waterdynamics \ + pathsimanalysis \ + mdahole2 + - name: build_mda_main shell: bash -l {0} run: | @@ -84,6 +93,12 @@ runs: fi python -m pip install ${BUILD_FLAGS} -v -e ./testsuite + - name: post_build_env_check + shell: bash -l {0} + run: | + pip list + micromamba list + - name: build_docs if: ${{ inputs.build-docs == 'true' }} shell: bash -l {0} diff --git a/.github/actions/setup-deps/action.yaml b/.github/actions/setup-deps/action.yaml index cceae40f99c..97112b09159 100644 --- a/.github/actions/setup-deps/action.yaml +++ b/.github/actions/setup-deps/action.yaml @@ -31,8 +31,6 @@ inputs: default: 'hypothesis' matplotlib: default: 'matplotlib-base' - mdahole2: - default: 'mdahole2-base' mda_xdrlib: default: 'mda-xdrlib' mmtf-python: @@ -41,8 +39,6 @@ inputs: default: 'numpy' packaging: default: 'packaging' - pathsimanalysis: - default: 'pathsimanalysis' pip: default: 'pip' pytest: @@ -53,8 +49,6 @@ inputs: default: 'threadpoolctl' tqdm: default: 'tqdm>=4.43.0' - waterdynamics: - default: 'waterdynamics' # conda-installed optional dependencies biopython: default: 'biopython>=1.80' @@ -120,18 +114,15 @@ runs: ${{ inputs.griddataformats }} ${{ inputs.hypothesis }} ${{ inputs.matplotlib }} - ${{ inputs.mdahole2 }} ${{ inputs.mda_xdrlib }} ${{ inputs.mmtf-python }} ${{ inputs.numpy }} ${{ inputs.packaging }} - ${{ inputs.pathsimanalysis }} ${{ inputs.pip }} ${{ inputs.pytest }} ${{ inputs.scipy }} ${{ inputs.threadpoolctl }} ${{ inputs.tqdm }} - ${{ inputs.waterdynamics }} CONDA_OPT_DEPS: | ${{ inputs.biopython }} ${{ inputs.chemfiles-python }} diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 9ce7d4af839..377575ef8c1 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -38,10 +38,10 @@ jobs: matrix: buildplat: - [ubuntu-22.04, manylinux_x86_64, x86_64] - - [macos-11, macosx_*, x86_64] + - [macos-12, macosx_*, x86_64] - [windows-2019, win_amd64, AMD64] - [macos-14, macosx_*, arm64] - python: ["cp39", "cp310", "cp311", "cp312"] + python: ["cp310", "cp311", "cp312"] defaults: run: working-directory: ./package @@ -51,7 +51,7 @@ jobs: fetch-depth: 0 - name: Build wheels - uses: pypa/cibuildwheel@v2.16.5 + uses: pypa/cibuildwheel@v2.20.0 with: package-dir: package env: @@ -142,7 +142,7 @@ jobs: mv dist/MDAnalysisTests-* testsuite/dist - name: upload_source_and_wheels - uses: pypa/gh-action-pypi-publish@v1.9.0 + uses: pypa/gh-action-pypi-publish@v1.10.1 with: skip_existing: true repository_url: https://test.pypi.org/legacy/ @@ -171,7 +171,7 @@ jobs: mv dist/MDAnalysisTests-* testsuite/dist - name: upload_tests - uses: pypa/gh-action-pypi-publish@v1.9.0 + uses: pypa/gh-action-pypi-publish@v1.10.1 with: packages_dir: testsuite/dist skip_existing: true @@ -201,7 +201,7 @@ jobs: mv dist/MDAnalysisTests-* testsuite/dist - name: upload_source_and_wheels - uses: pypa/gh-action-pypi-publish@v1.9.0 + uses: pypa/gh-action-pypi-publish@v1.10.0 upload_pypi_mdanalysistests: if: | @@ -227,7 +227,7 @@ jobs: mv dist/MDAnalysisTests-* testsuite/dist - name: upload_tests - uses: pypa/gh-action-pypi-publish@v1.9.0 + uses: pypa/gh-action-pypi-publish@v1.10.0 with: packages_dir: testsuite/dist diff --git a/.github/workflows/gh-ci-cron.yaml b/.github/workflows/gh-ci-cron.yaml index 72c7e365385..4585fc4de71 100644 --- a/.github/workflows/gh-ci-cron.yaml +++ b/.github/workflows/gh-ci-cron.yaml @@ -4,6 +4,11 @@ on: # 3 am Tuesdays and Fridays - cron: "0 3 * * 2,5" workflow_dispatch: + # Uncomment when you need to test on a PR + # pull_request: + # branches: + # - develop + concurrency: # Probably overly cautious group naming. @@ -21,6 +26,7 @@ env: MPLBACKEND: agg jobs: + # a pip only, minimal deps install w/ scipy & numpy nightly upstream wheels numpy_and_scipy_dev: if: "github.repository == 'MDAnalysis/mdanalysis'" runs-on: ubuntu-latest @@ -34,45 +40,51 @@ jobs: with: os-type: "ubuntu" - - name: setup_micromamba - uses: mamba-org/setup-micromamba@v1 - with: - environment-name: mda - create-args: >- - python=3.11 - pip - # using jaime's shim to avoid pulling down the cudatoolkit - condarc: | - channels: - - jaimergp/label/unsupported-cudatoolkit-shim - - conda-forge - - bioconda - - - name: install_deps - uses: ./.github/actions/setup-deps + - uses: actions/setup-python@v4 with: - micromamba: true - full-deps: true + python-version: ${{ matrix.python-version }} - # overwrite installs by picking up nightly wheels + # minimally install nightly wheels & core deps - name: nightly_wheels run: | - pip install --pre -U -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple scipy numpy networkx matplotlib pandas + # Nightlies: add in networkx and matplotlib because we can + python -m pip install --pre -U --extra-index https://pypi.anaconda.org/scientific-python-nightly-wheels/simple \ + scipy \ + numpy \ + networkx \ + matplotlib \ + # Base deps + python -m pip install \ + "cython>=0.28" \ + packaging \ + "setuptools>69.4" \ + wheel \ + "griddataformats>=0.4.0" \ + "mmtf-python>=1.0" \ + "joblib>=0.12" \ + "tqdm>=4.43.0" \ + threadpoolctl \ + fasteners \ + mda-xdrlib \ + pytest \ + pytest-xdist \ + pytest-timeout + # deps that depend on MDA + python -m pip install --no-deps \ + waterdynamics \ + pathsimanalysis \ + mdahole2 + + - name: pre_install_list_deps + run: python -m pip list - - name: list_deps + - name: build_srcs run: | - micromamba list - pip list + python -m pip install --no-build-isolation -v -e ./package + python -m pip install --no-build-isolation -v -e ./testsuite - # Intentionally going with setup.py builds so we can build with latest - - name: build_srcs - uses: ./.github/actions/build-src - with: - build-tests: true - build-docs: false - # We don't use build isolation because we want to ensure that we - # test building with brand new versions of NumPy here. - isolation: false + - name: post_install_list_deps + run: python -m pip list - name: run_tests run: | @@ -136,7 +148,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, macos-11] + os: [ubuntu-20.04, macos-12] steps: - uses: actions/checkout@v4 @@ -151,7 +163,7 @@ jobs: with: environment-name: mda create-args: >- - python=3.9 + python=3.10 pip condarc: | channels: @@ -210,6 +222,9 @@ jobs: run: | pip install pytest-xdist pytest-timeout + - name: check env + run: pip list + - name: run_tests run: | pytest --timeout=200 -n auto testsuite/MDAnalysisTests --disable-pytest-warnings --durations=50 @@ -218,12 +233,14 @@ jobs: conda-latest-release: # A set of runner to check that the latest conda release works as expected if: "github.repository == 'MDAnalysis/mdanalysis'" - runs-on: ${{ matrix.os }}-latest + runs-on: ${{ matrix.os }} timeout-minutes: 60 strategy: fail-fast: false matrix: - os: [ubuntu, macos] + # Stick to macos-13 because some of our + # optional depss don't support arm64 (i.e. macos-14) + os: [ubuntu-latest, macos-13] python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 @@ -247,16 +264,16 @@ jobs: - conda-forge - bioconda + - name: install_mdanalysis + run: | + micromamba install mdanalysis mdanalysistests + - name: install_deps uses: ./.github/actions/setup-deps with: micromamba: true full-deps: true - - name: install_mdanalysis - run: | - micromamba install mdanalysis mdanalysistests - - name: run_tests run: | pytest --timeout=200 -n auto --pyargs MDAnalysisTests diff --git a/.github/workflows/gh-ci.yaml b/.github/workflows/gh-ci.yaml index 05bda86b3cd..8a45aec650f 100644 --- a/.github/workflows/gh-ci.yaml +++ b/.github/workflows/gh-ci.yaml @@ -85,6 +85,8 @@ jobs: with: micromamba: true full-deps: ${{ matrix.full-deps }} + # disable GSD because it occasionally introduce hanging in testing #4209 + gsd: '' # in most cases will just default to empty, i.e. pick up max version from other deps numpy: ${{ matrix.numpy }} extra-pip-deps: ${{ matrix.extra-pip-deps }} @@ -113,7 +115,7 @@ jobs: PYTEST_FLAGS="${PYTEST_FLAGS} --cov-config=.coveragerc --cov=MDAnalysis --cov-report=xml" fi echo $PYTEST_FLAGS - pytest -n auto --timeout=200 testsuite/MDAnalysisTests $PYTEST_FLAGS + pytest -n logical --timeout=200 testsuite/MDAnalysisTests $PYTEST_FLAGS - name: run_asv if: contains(matrix.name, 'asv_check') @@ -161,6 +163,7 @@ jobs: with: micromamba: true full-deps: true + gsd: '' extra-pip-deps: "docutils sphinx-sitemap sphinxcontrib-bibtex pybtex pybtex-docutils" extra-conda-deps: "mdanalysis-sphinx-theme>=1.3.0" @@ -276,6 +279,9 @@ jobs: python -m pip install mdanalysis-*.tar.gz python -m pip install mdanalysistests-*.tar.gz + - name: check install + run: pip list + - name: run tests working-directory: ./dist - run: python -m pytest --timeout=200 -n auto --pyargs MDAnalysisTests + run: python -m pytest --timeout=200 -n logical --pyargs MDAnalysisTests diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cace2be35ba..250f3ba63b5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -105,7 +105,6 @@ jobs: biopython "chemfiles>=0.10,<0.10.4" duecredit - "gsd>3.0.0" joblib GridDataFormats mmtf-python @@ -114,6 +113,8 @@ jobs: pytng>=0.2.3 rdkit>=2020.03.1 tidynamics>=1.0.0 + # remove from azure to avoid test hanging #4707 + # "gsd>3.0.0" displayName: 'Install additional dependencies for 64-bit tests' condition: and(succeeded(), eq(variables['PYTHON_ARCH'], 'x64')) - script: >- @@ -128,7 +129,7 @@ jobs: displayName: 'Check installed packages' - powershell: | cd testsuite - pytest MDAnalysisTests --disable-pytest-warnings -n auto --timeout=200 -rsx --cov=MDAnalysis + pytest MDAnalysisTests --disable-pytest-warnings -n logical --timeout=200 -rsx --cov=MDAnalysis displayName: 'Run MDAnalysis Test Suite' - script: | curl -s https://codecov.io/bash | bash diff --git a/package/CHANGELOG b/package/CHANGELOG index ede4ca29d8d..d799b52e4e0 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -17,12 +17,13 @@ The rules for this file: ??/??/?? IAlibay, HeetVekariya, marinegor, lilyminium, RMeli, ljwoods2, aditya292002, pstaerk, PicoCentauri, BFedder, tyler.je.reddy, SampurnaM, leonwehrhan, kainszs, orionarcher, - yuxuanzhuang, PythonFZ, laksh-krishna-sharma, orbeckst + yuxuanzhuang, PythonFZ, laksh-krishna-sharma, orbeckst, MattTDavies, + talagayev * 2.8.0 Fixes - * Catch higher dimensional indexing in GroupBase (Issue #4647) + * Catch higher dimensional indexing in GroupBase & ComponentBase (Issue #4647) * Do not raise an Error reading H5MD files with datasets like `observables//` (part of Issue #4598, PR #4615) * Fix failure in double-serialization of TextIOPicklable file reader. @@ -55,7 +56,9 @@ Fixes Enhancements * Introduce parallelization API to `AnalysisBase` and to `analysis.rms.RMSD` class (Issue #4158, PR #4304) + * Enables parallelization for analysis.gnm.GNMAnalysis (Issue #4672) * explicitly mark `analysis.pca.PCA` as not parallelizable (Issue #4680) + * enables parallelization for analysis.bat.BAT (Issue #4663) * Improve error message for `AtomGroup.unwrap()` when bonds are not present.(Issue #4436, PR #4642) * Add `analysis.DSSP` module for protein secondary structure assignment, based on [pydssp](https://github.com/ShintaroMinami/PyDSSP) * Added a tqdm progress bar for `MDAnalysis.analysis.pca.PCA.transform()` diff --git a/package/MDAnalysis/analysis/bat.py b/package/MDAnalysis/analysis/bat.py index 0c3e15a32e9..5186cb6c882 100644 --- a/package/MDAnalysis/analysis/bat.py +++ b/package/MDAnalysis/analysis/bat.py @@ -164,7 +164,7 @@ class to calculate dihedral angles for a given set of atoms or residues import copy import MDAnalysis as mda -from .base import AnalysisBase +from .base import AnalysisBase, ResultsGroup from MDAnalysis.lib.distances import calc_bonds, calc_angles, calc_dihedrals from MDAnalysis.lib.mdamath import make_whole @@ -253,10 +253,28 @@ class BAT(AnalysisBase): Bond-Angle-Torsions (BAT) internal coordinates will be computed for the group of atoms and all frame in the trajectory belonging to `ag`. + .. versionchanged:: 2.8.0 + Enabled **parallel execution** with the ``multiprocessing`` and ``dask`` + backends; use the new method :meth:`get_supported_backends` to see all + supported backends. + """ - @due.dcite(Doi("10.1002/jcc.26036"), - description="Bond-Angle-Torsions Coordinate Transformation", - path="MDAnalysis.analysis.bat.BAT") + _analysis_algorithm_is_parallelizable = True + + @classmethod + def get_supported_backends(cls): + return ( + "serial", + "multiprocessing", + "dask", + ) + + @due.dcite( + Doi("10.1002/jcc.26036"), + description="Bond-Angle-Torsions Coordinate Transformation", + path="MDAnalysis.analysis.bat.BAT", + ) + def __init__(self, ag, initial_atom=None, filename=None, **kwargs): r"""Parameters ---------- @@ -558,3 +576,6 @@ def Cartesian(self, bat_frame): def atoms(self): """The atomgroup for which BAT are computed (read-only property)""" return self._ag + + def _get_aggregator(self): + return ResultsGroup(lookup={'bat': ResultsGroup.ndarray_vstack}) diff --git a/package/MDAnalysis/analysis/gnm.py b/package/MDAnalysis/analysis/gnm.py index 86a62fa7b9f..510fb887d01 100644 --- a/package/MDAnalysis/analysis/gnm.py +++ b/package/MDAnalysis/analysis/gnm.py @@ -92,7 +92,7 @@ import numpy as np -from .base import AnalysisBase +from .base import AnalysisBase, ResultsGroup from MDAnalysis.analysis.base import Results @@ -245,8 +245,19 @@ class GNMAnalysis(AnalysisBase): Use :class:`~MDAnalysis.analysis.AnalysisBase` as parent class and store results as attributes ``times``, ``eigenvalues`` and ``eigenvectors`` of the ``results`` attribute. + + .. versionchanged:: 2.8.0 + Enabled **parallel execution** with the ``multiprocessing`` and ``dask`` + backends; use the new method :meth:`get_supported_backends` to see all + supported backends. """ + _analysis_algorithm_is_parallelizable = True + + @classmethod + def get_supported_backends(cls): + return ("serial", "multiprocessing", "dask") + def __init__(self, universe, select='protein and name CA', @@ -348,6 +359,15 @@ def _conclude(self): self.results.eigenvalues = np.asarray(self.results.eigenvalues) self.results.eigenvectors = np.asarray(self.results.eigenvectors) + def _get_aggregator(self): + return ResultsGroup( + lookup={ + "eigenvectors": ResultsGroup.ndarray_hstack, + "eigenvalues": ResultsGroup.ndarray_hstack, + "times": ResultsGroup.ndarray_hstack, + } + ) + class closeContactGNMAnalysis(GNMAnalysis): r"""GNMAnalysis only using close contacts. diff --git a/package/MDAnalysis/core/groups.py b/package/MDAnalysis/core/groups.py index e8304d3b042..91b2c779304 100644 --- a/package/MDAnalysis/core/groups.py +++ b/package/MDAnalysis/core/groups.py @@ -570,7 +570,10 @@ def __init__(self, *args): raise TypeError(errmsg) from None # indices for the objects I hold - self._ix = np.asarray(ix, dtype=np.intp) + ix = np.asarray(ix, dtype=np.intp) + if ix.ndim > 1: + raise IndexError('Group index must be 1d') + self._ix = ix self._u = u self._cache = dict() @@ -598,11 +601,6 @@ def __getitem__(self, item): # important for boolean slicing item = np.array(item) - if isinstance(item, np.ndarray) and item.ndim > 1: - # disallow high dimensional indexing. - # this doesnt stop the underlying issue - raise IndexError('Group index must be 1d') - # We specify _derived_class instead of self.__class__ to allow # subclasses, such as UpdatingAtomGroup, to control the class # resulting from slicing. @@ -4252,6 +4250,9 @@ class ComponentBase(_MutableBase): def __init__(self, ix, u): # index of component + if not isinstance(ix, numbers.Integral): + raise IndexError('Component can only be indexed by a single integer') + self._ix = ix self._u = u diff --git a/testsuite/MDAnalysisTests/analysis/conftest.py b/testsuite/MDAnalysisTests/analysis/conftest.py index 55bae7e6bd8..f53cdcfaac1 100644 --- a/testsuite/MDAnalysisTests/analysis/conftest.py +++ b/testsuite/MDAnalysisTests/analysis/conftest.py @@ -7,7 +7,9 @@ OldAPIAnalysis, ) from MDAnalysis.analysis.rms import RMSD, RMSF +from MDAnalysis.analysis.bat import BAT from MDAnalysis.lib.util import is_installed +from MDAnalysis.analysis.gnm import GNMAnalysis def params_for_cls(cls, exclude: list[str] = None): @@ -87,3 +89,13 @@ def client_RMSD(request): @pytest.fixture(scope='module', params=params_for_cls(RMSF)) def client_RMSF(request): return request.param + + +@pytest.fixture(scope='module', params=params_for_cls(GNMAnalysis)) +def client_GNMAnalysis(request): + return request.param + + +@pytest.fixture(scope='module', params=params_for_cls(BAT)) +def client_BAT(request): + return request.param \ No newline at end of file diff --git a/testsuite/MDAnalysisTests/analysis/test_bat.py b/testsuite/MDAnalysisTests/analysis/test_bat.py index c80353baff6..5fb2603df62 100644 --- a/testsuite/MDAnalysisTests/analysis/test_bat.py +++ b/testsuite/MDAnalysisTests/analysis/test_bat.py @@ -41,16 +41,16 @@ def selected_residues(self): return ag @pytest.fixture() - def bat(self, selected_residues): + def bat(self, selected_residues, client_BAT): R = BAT(selected_residues) - R.run() + R.run(**client_BAT) return R.results.bat @pytest.fixture - def bat_npz(self, tmpdir, selected_residues): + def bat_npz(self, tmpdir, selected_residues, client_BAT): filename = str(tmpdir / 'test_bat_IO.npy') R = BAT(selected_residues) - R.run() + R.run(**client_BAT) R.save(filename) return filename @@ -73,8 +73,8 @@ def test_bat_coordinates(self, bat): atol=1.5e-5, err_msg="error: BAT coordinates should match test values") - def test_bat_coordinates_single_frame(self, selected_residues): - bat = BAT(selected_residues).run(start=1, stop=2).results.bat + def test_bat_coordinates_single_frame(self, selected_residues, client_BAT): + bat = BAT(selected_residues).run(start=1, stop=2, **client_BAT).results.bat test_bat = [np.load(BATArray)[1]] assert_allclose( bat, diff --git a/testsuite/MDAnalysisTests/analysis/test_gnm.py b/testsuite/MDAnalysisTests/analysis/test_gnm.py index 6521c08eb86..d8a547a5428 100644 --- a/testsuite/MDAnalysisTests/analysis/test_gnm.py +++ b/testsuite/MDAnalysisTests/analysis/test_gnm.py @@ -38,10 +38,10 @@ def universe(): return mda.Universe(GRO, XTC) -def test_gnm(universe, tmpdir): +def test_gnm(universe, tmpdir, client_GNMAnalysis): output = os.path.join(str(tmpdir), 'output.txt') gnm = mda.analysis.gnm.GNMAnalysis(universe, ReportVector=output) - gnm.run() + gnm.run(**client_GNMAnalysis) result = gnm.results assert len(result.times) == 10 assert_almost_equal(gnm.results.times, np.arange(0, 1000, 100), decimal=4) @@ -51,9 +51,9 @@ def test_gnm(universe, tmpdir): 4.2058769e-15, 3.9839431e-15]) -def test_gnm_run_step(universe): +def test_gnm_run_step(universe, client_GNMAnalysis): gnm = mda.analysis.gnm.GNMAnalysis(universe) - gnm.run(step=3) + gnm.run(step=3, **client_GNMAnalysis) result = gnm.results assert len(result.times) == 4 assert_almost_equal(gnm.results.times, np.arange(0, 1200, 300), decimal=4) @@ -88,9 +88,9 @@ def test_gnm_SVD_fail(universe): mda.analysis.gnm.GNMAnalysis(universe).run(stop=1) -def test_closeContactGNMAnalysis(universe): +def test_closeContactGNMAnalysis(universe, client_GNMAnalysis): gnm = mda.analysis.gnm.closeContactGNMAnalysis(universe, weights="size") - gnm.run(stop=2) + gnm.run(stop=2, **client_GNMAnalysis) result = gnm.results assert len(result.times) == 2 assert_almost_equal(gnm.results.times, (0, 100), decimal=4) @@ -114,9 +114,9 @@ def test_closeContactGNMAnalysis(universe): 0.0, 0.0, -2.263157894736841, -0.24333213169614382]) -def test_closeContactGNMAnalysis_weights_None(universe): +def test_closeContactGNMAnalysis_weights_None(universe, client_GNMAnalysis): gnm = mda.analysis.gnm.closeContactGNMAnalysis(universe, weights=None) - gnm.run(stop=2) + gnm.run(stop=2, **client_GNMAnalysis) result = gnm.results assert len(result.times) == 2 assert_almost_equal(gnm.results.times, (0, 100), decimal=4) diff --git a/testsuite/MDAnalysisTests/core/test_atom.py b/testsuite/MDAnalysisTests/core/test_atom.py index 35d5d67477a..d63d0574f06 100644 --- a/testsuite/MDAnalysisTests/core/test_atom.py +++ b/testsuite/MDAnalysisTests/core/test_atom.py @@ -121,6 +121,11 @@ def test_atom_pickle(self, universe, ix): atm_in = pickle.loads(pickle.dumps(atm_out)) assert atm_in == atm_out + def test_improper_initialisation(self, universe): + with pytest.raises(IndexError): + indices = [0, 1] + mda.core.groups.Atom(indices, universe) + class TestAtomNoForceNoVel(object): @staticmethod diff --git a/testsuite/MDAnalysisTests/core/test_atomgroup.py b/testsuite/MDAnalysisTests/core/test_atomgroup.py index 1497456e2da..4456362c498 100644 --- a/testsuite/MDAnalysisTests/core/test_atomgroup.py +++ b/testsuite/MDAnalysisTests/core/test_atomgroup.py @@ -1237,6 +1237,12 @@ def test_bad_make(self): with pytest.raises(TypeError): mda.core.groups.AtomGroup(['these', 'are', 'not', 'atoms']) + def test_invalid_index_initialisation(self, universe): + indices = [[1, 2, 3], + [4, 5, 6]] + with pytest.raises(IndexError): + mda.core.groups.AtomGroup(indices, universe) + def test_n_atoms(self, ag): assert ag.n_atoms == 3341