diff --git a/doc/changes/dev/13700.bugfix.rst b/doc/changes/dev/13700.bugfix.rst new file mode 100644 index 00000000000..ba07c461923 --- /dev/null +++ b/doc/changes/dev/13700.bugfix.rst @@ -0,0 +1 @@ +Fix bug where :func:`mne.channels.DigMontage.set_montage` raised an :exc:`IndexError` on MEG+EEG recordings when EEG reference positions were set to the placeholder value ``[1, 0, 0]`` (i.e., digitization was not performed), by :newcontrib:`Famous077`. \ No newline at end of file diff --git a/doc/changes/names.inc b/doc/changes/names.inc index 5191225daf3..ac53955a57f 100644 --- a/doc/changes/names.inc +++ b/doc/changes/names.inc @@ -97,7 +97,7 @@ .. _Ezequiel Mikulan: https://github.com/ezemikulan .. _Ezequiel Mikulan: https://github.com/ezemikulan .. _Fahimeh Mamashli: https://github.com/fmamashli -.. _Famous Raj Bhat: https://github.com/Famous077 +.. _Famous077: https://github.com/Famous077 .. _Farzin Negahbani: https://github.com/Farzin-Negahbani .. _Federico Raimondo: https://github.com/fraimondo .. _Federico Zamberlan: https://github.com/fzamberlan diff --git a/mne/channels/montage.py b/mne/channels/montage.py index 2f84b425fc0..04d454201f2 100644 --- a/mne/channels/montage.py +++ b/mne/channels/montage.py @@ -1256,7 +1256,12 @@ def _backcompat_value(pos, ref_pos): # keep reference location from EEG-like channels if they # already exist and are all the same. # Note: ref position is an empty list for fieldtrip data - if len(ref_pos) and ref_pos[0].any() and (ref_pos[0] == ref_pos).all(): + if ( + len(ref_pos) + and ref_pos[0].any() + and (ref_pos[0] == ref_pos).all() + and not np.array_equal(ref_pos[0], [1.0, 0.0, 0.0]) + ): eeg_ref_pos = ref_pos[0] # since we have an EEG reference position, we have # to add it into the info['dig'] as EEG000 diff --git a/mne/channels/tests/test_montage.py b/mne/channels/tests/test_montage.py index 0294cbe9d16..e5e16d985bb 100644 --- a/mne/channels/tests/test_montage.py +++ b/mne/channels/tests/test_montage.py @@ -21,13 +21,14 @@ import mne.channels.montage from mne import ( - __file__ as _mne_file, -) -from mne import ( + EpochsArray, create_info, pick_types, read_evokeds, ) +from mne import ( + __file__ as _mne_file, +) from mne._fiff._digitization import ( _count_points_by_type, _format_dig_points, @@ -2145,3 +2146,28 @@ def test_fnirs_montage(): raw.set_channel_types({ch_name: "eeg" for ch_name in raw.ch_names[-2:]}) with pytest.raises(ValueError, match="mix of fNIRS"): raw.get_montage() + + +def test_set_montage_meg_eeg_no_digitization(): + """Regression test for GH-12011. + + set_montage() must not crash when MEG+EEG info has EEG reference + positions set to the [1, 0, 0] sentinel (digitization was skipped). + """ + ch_names = [f"EEG{i:03d}" for i in range(1, 11)] + ["MEG0111"] + ch_types = ["eeg"] * 10 + ["grad"] + info = create_info(ch_names=ch_names, sfreq=1000.0, ch_types=ch_types) + + # Simulate MEG reader behaviour when digitization is skipped: + # EEG ref position (loc[3:6]) is set to the [1, 0, 0] sentinel + with info._unlock(): + for ch in info["chs"]: + if ch["ch_name"].startswith("EEG"): + ch["loc"][3:6] = [1.0, 0.0, 0.0] + + data = np.zeros((1, len(ch_names), 100)) + epochs = EpochsArray(data, info) + + # This must not raise IndexError (regression test for GH-12011) + montage = make_standard_montage("standard_1020") + epochs.set_montage(montage, on_missing="ignore")