diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b78825d7c9a..b7df99ff386 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,7 +50,7 @@ jobs: pytest: name: '${{ matrix.os }} / ${{ matrix.kind }} / ${{ matrix.python }}' needs: style - timeout-minutes: 85 + timeout-minutes: 90 runs-on: ${{ matrix.os }} defaults: run: diff --git a/doc/changes/dev/13371.bugfix.rst b/doc/changes/dev/13371.bugfix.rst new file mode 100644 index 00000000000..cb2e7dc5926 --- /dev/null +++ b/doc/changes/dev/13371.bugfix.rst @@ -0,0 +1 @@ +Fix the check in :func:`mne.make_forward_solution` that all MEG sensors are outside a spherical BEM model, by `Marijn van Vliet`_ diff --git a/doc/changes/dev/13376.bugfix.rst b/doc/changes/dev/13376.bugfix.rst new file mode 100644 index 00000000000..d796d8cb382 --- /dev/null +++ b/doc/changes/dev/13376.bugfix.rst @@ -0,0 +1 @@ +Ensure auto-computed plot scalings are always non-zero, by `Daniel McCloy`_. \ No newline at end of file diff --git a/mne/forward/_make_forward.py b/mne/forward/_make_forward.py index 9b207c7c65a..421cb921c1d 100644 --- a/mne/forward/_make_forward.py +++ b/mne/forward/_make_forward.py @@ -553,9 +553,8 @@ def check_inside(x): else: def check_inside(x): - return ( - np.linalg.norm(x - bem["r0"], axis=1) < bem["layers"][-1]["rad"] - ) + r0 = apply_trans(invert_transform(mri_head_t), bem["r0"]) + return np.linalg.norm(x - r0, axis=1) < bem["layers"][-1]["rad"] if "meg" in sensors: meg_loc = apply_trans( diff --git a/mne/forward/tests/test_make_forward.py b/mne/forward/tests/test_make_forward.py index 6629dd0b3d1..d408df3b551 100644 --- a/mne/forward/tests/test_make_forward.py +++ b/mne/forward/tests/test_make_forward.py @@ -574,6 +574,7 @@ def test_make_forward_solution_sphere(tmp_path, fname_src_small): 1.0, rtol=1e-3, ) + # Number of layers in the sphere model doesn't matter for MEG # (as long as no sources are omitted due to distance) assert len(sphere["layers"]) == 4 @@ -592,6 +593,14 @@ def test_make_forward_solution_sphere(tmp_path, fname_src_small): with pytest.raises(RuntimeError, match="zero shells.*EEG"): make_forward_solution(fname_raw, fname_trans, src, sphere) + # Since the spherical model is defined in head space, the head->MRI transform should + # not matter for the check that MEG sensors are outside the sphere. + custom_trans = Transform("head", "mri") + custom_trans["trans"][0, 3] = 0.05 # move MEG sensors close to mesh + sphere = make_sphere_model() + fwd = make_forward_solution(fname_raw, custom_trans, src, sphere) + assert fwd["mri_head_t"]["trans"][0, 3] == -0.05 + @pytest.mark.slowtest @testing.requires_testing_data diff --git a/mne/viz/tests/test_utils.py b/mne/viz/tests/test_utils.py index e7730be97ac..a6fa5ba2b29 100644 --- a/mne/viz/tests/test_utils.py +++ b/mne/viz/tests/test_utils.py @@ -129,6 +129,10 @@ def test_auto_scale(): raw = read_raw_fif(raw_fname) epochs = Epochs(raw, read_events(ev_fname)) rand_data = np.random.randn(10, 100) + # make a stim channel all zeros (gh 13376) + ix = raw.get_channel_types().index("stim") + raw.load_data() + raw._data[ix] = 0.0 for inst in [raw, epochs]: scale_grad = 1e10 @@ -142,6 +146,8 @@ def test_auto_scale(): scalings_new = _compute_scalings(scalings_def, inst) assert scale_grad == scalings_new["grad"] assert scalings_new["eeg"] != "auto" + # make sure an all-zero channel doesn't cause scaling=0 (gh 13376) + assert scalings_new["stim"] > 0 with pytest.raises(ValueError, match="Must supply either Raw or Epochs"): _compute_scalings(scalings_def, rand_data) diff --git a/mne/viz/utils.py b/mne/viz/utils.py index 2c4e4c782f5..f40520dc882 100644 --- a/mne/viz/utils.py +++ b/mne/viz/utils.py @@ -1423,6 +1423,8 @@ def _compute_scalings(scalings, inst, remove_dc=False, duration=10): this_data = this_data[np.isfinite(this_data)] if this_data.size: iqr = np.diff(np.percentile(this_data, [25, 75]))[0] + if iqr == 0: # e.g. sparse stim channels, flat channels + iqr = 1.0 else: iqr = 1.0 scalings[key] = iqr diff --git a/tools/github_actions_test.sh b/tools/github_actions_test.sh index 4fe8756bd50..202b041bc79 100755 --- a/tools/github_actions_test.sh +++ b/tools/github_actions_test.sh @@ -21,14 +21,16 @@ if [[ ! -z "$CONDA_ENV" ]] && [[ "${RUNNER_OS}" != "Windows" ]] && [[ "${MNE_CI_ INSTALL_PATH=$(python -c "import mne, pathlib; print(str(pathlib.Path(mne.__file__).parents[1]))") echo "Copying tests from ${PROJ_PATH}/mne-python/mne/ to ${INSTALL_PATH}/mne/" echo "::group::rsync mne" - rsync -a --partial --progress --prune-empty-dirs --exclude="*.pyc" --include="**/" --include="**/tests/*" --include="**/tests/data/**" --exclude="**" ${PROJ_PATH}/mne/ ${INSTALL_PATH}/mne/ + set -x + rsync -a --partial --progress --prune-empty-dirs --exclude="*.pyc" --include="*/" --include="tests/**" --include="**/tests/**" --exclude="**" ${PROJ_PATH}/mne/ ${INSTALL_PATH}/mne/ echo "::endgroup::" echo "::group::rsync doc" mkdir -p ${INSTALL_PATH}/doc/ - rsync -a --partial --progress --prune-empty-dirs --include="**/" --include="**/api/*" --exclude="**" ${PROJ_PATH}/doc/ ${INSTALL_PATH}/doc/ + rsync -a --partial --progress --prune-empty-dirs --include="api/" --include="api/*.rst" --exclude="*" ${PROJ_PATH}/doc/ ${INSTALL_PATH}/doc/ test -f ${INSTALL_PATH}/doc/api/reading_raw_data.rst cd $INSTALL_PATH cp -av $PROJ_PATH/pyproject.toml . + set +x echo "::endgroup::" fi