From aeee860f6f67e44e26ddda3a2c2b12ba0bf24f88 Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Sat, 27 Apr 2024 09:42:13 +0200 Subject: [PATCH 1/3] raise helpful error message on missing channel dig --- mne_icalabel/iclabel/_utils.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mne_icalabel/iclabel/_utils.py b/mne_icalabel/iclabel/_utils.py index 94b75d2b0..4aa13da51 100644 --- a/mne_icalabel/iclabel/_utils.py +++ b/mne_icalabel/iclabel/_utils.py @@ -53,9 +53,16 @@ def _cart2sph(_x, _y, _z): return azimuth, elevation, r # get the channel position dictionary - montage = raw.copy().pick_channels(picks, ordered=True).get_montage() + montage = raw.copy().pick(picks).get_montage() positions = montage.get_positions() ch_pos = positions["ch_pos"] + # check that we do have a coordinate for every points + empty = [key for key in ch_pos if np.all(np.isnan(ch_pos[key]))] + if len(empty) != 0: + raise ValueError( + f"Channel position for {empty} is missing. Please check the montage set, " + "the channel names, and ensure that every channel has a position." + ) # get locations as a 2D array locs = np.vstack(list(ch_pos.values())) From 7846c7219332f7d61dc44a1d0894ef5db0a79178 Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Sat, 27 Apr 2024 09:44:16 +0200 Subject: [PATCH 2/3] add changelog entry --- doc/changes/latest.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/changes/latest.rst b/doc/changes/latest.rst index 4b05b789c..374472273 100644 --- a/doc/changes/latest.rst +++ b/doc/changes/latest.rst @@ -16,3 +16,5 @@ Version 0.7 =========== + +- Raise helpful error message when montage is incomplete (:pr:`181` by `Mathieu Scheltienne`_) From 77b0145abab5fb57edc7fb3e89b13345efd53859 Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Sat, 27 Apr 2024 10:05:19 +0200 Subject: [PATCH 3/3] add test and error message if montage is entirely absent --- mne_icalabel/iclabel/_utils.py | 5 +++++ mne_icalabel/iclabel/tests/test_features.py | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/mne_icalabel/iclabel/_utils.py b/mne_icalabel/iclabel/_utils.py index 4aa13da51..d550ddbfb 100644 --- a/mne_icalabel/iclabel/_utils.py +++ b/mne_icalabel/iclabel/_utils.py @@ -54,6 +54,11 @@ def _cart2sph(_x, _y, _z): # get the channel position dictionary montage = raw.copy().pick(picks).get_montage() + if montage is None: + raise ValueError( + "Montage is not set. Please set the montage to provide the electrode " + "positions." + ) positions = montage.get_positions() ch_pos = positions["ch_pos"] # check that we do have a coordinate for every points diff --git a/mne_icalabel/iclabel/tests/test_features.py b/mne_icalabel/iclabel/tests/test_features.py index 8769dd533..dbd6abec4 100644 --- a/mne_icalabel/iclabel/tests/test_features.py +++ b/mne_icalabel/iclabel/tests/test_features.py @@ -202,6 +202,21 @@ def test_eeg_topoplot(file, eeglab_result_file): assert np.allclose(topo, topo_eeglab, equal_nan=True) +def test_eeg_topoplot_invalid_montage(): + """Test that we raise an error if the montage is badly set.""" + raw = read_raw(raw_eeglab_path, preload=True) + ica = read_ica_eeglab(raw_eeglab_path) + # get icawinv + icawinv, _ = _retrieve_eeglab_icawinv(ica) + # compute feature + raw.info["chs"][0]["loc"] = np.array([np.nan] * 12) + with pytest.raises(ValueError, match="Channel position for .* is missing"): + _eeg_topoplot(raw, icawinv, ica.ch_names) + raw.set_montage(None) + with pytest.raises(ValueError, match="Montage is not set"): + _eeg_topoplot(raw, icawinv, ica.ch_names) + + # ---------------------------------------------------------------------------- @pytest.mark.filterwarnings("ignore:Estimated head radius.*:RuntimeWarning") @pytest.mark.parametrize(