From 54dd4a11ba67c770a01d62bb35c76d19fdd8c34b Mon Sep 17 00:00:00 2001 From: 1himan Date: Mon, 1 Dec 2025 19:38:02 +0530 Subject: [PATCH 01/25] Raising meaningful warnings/errors for interpolate_bads, when supplied loc field with missing or invalid data --- mne/channels/channels.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index ef98efd1731..4e4ba2a5234 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -714,6 +714,7 @@ def add_channels(self, add_list, force_update_info=False): _validate_type(add_list, (list, tuple), "Input") + # Object-specific checks for inst in add_list + [self]: _check_preload(inst, "adding channels") @@ -828,6 +829,7 @@ def interpolate_bads( method=None, exclude=(), verbose=None, + **kwargs ): """Interpolate bad MEG and EEG channels. @@ -896,6 +898,27 @@ def interpolate_bads( _check_preload(self, "interpolation") _validate_type(method, (dict, str, None), "method") + bads_to_check = self.info['bads'] + for ch_name in bads_to_check: + try: + ch_idx = self.ch_names.index(ch_name) + ch_loc = self.info['chs'][ch_idx]['loc'] + is_loc_invalid = np.allclose(ch_loc, 0.0, atol=1e-16) or np.any(np.isnan(ch_loc)) + except ValueError: + continue + + if is_loc_invalid: + msg = ( + f"Bad channel '{ch_name}' is missing valid sensor position (loc) information. " + "Interpolation cannot proceed correctly." + ) + _on_missing( + kwargs.get("on_no_position", "error"), + msg + + " If you want to continue despite missing positions, set " + "on_no_position='warn' or 'ignore'.", + ) + method = _handle_default("interpolation_method", method) ch_types = self.get_channel_types(unique=True) # figure out if we have "mag" for "meg", "hbo" for "fnirs", ... to filter the From be254c4fd3bc1e52a031ed5b41b96160cb3ab1e4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 14:10:44 +0000 Subject: [PATCH 02/25] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mne/channels/channels.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index 4e4ba2a5234..e57de953e9d 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -714,7 +714,6 @@ def add_channels(self, add_list, force_update_info=False): _validate_type(add_list, (list, tuple), "Input") - # Object-specific checks for inst in add_list + [self]: _check_preload(inst, "adding channels") @@ -829,7 +828,7 @@ def interpolate_bads( method=None, exclude=(), verbose=None, - **kwargs + **kwargs, ): """Interpolate bad MEG and EEG channels. @@ -898,12 +897,14 @@ def interpolate_bads( _check_preload(self, "interpolation") _validate_type(method, (dict, str, None), "method") - bads_to_check = self.info['bads'] + bads_to_check = self.info["bads"] for ch_name in bads_to_check: try: ch_idx = self.ch_names.index(ch_name) - ch_loc = self.info['chs'][ch_idx]['loc'] - is_loc_invalid = np.allclose(ch_loc, 0.0, atol=1e-16) or np.any(np.isnan(ch_loc)) + ch_loc = self.info["chs"][ch_idx]["loc"] + is_loc_invalid = np.allclose(ch_loc, 0.0, atol=1e-16) or np.any( + np.isnan(ch_loc) + ) except ValueError: continue @@ -914,8 +915,7 @@ def interpolate_bads( ) _on_missing( kwargs.get("on_no_position", "error"), - msg - + " If you want to continue despite missing positions, set " + msg + " If you want to continue despite missing positions, set " "on_no_position='warn' or 'ignore'.", ) From 7f5d63b5a457c110733d7ca45dd3a95d59c090e4 Mon Sep 17 00:00:00 2001 From: 1himan Date: Wed, 3 Dec 2025 11:31:22 +0530 Subject: [PATCH 03/25] Improved logic and error message --- app.py | 39 +++++++++++++++++++++++++++++++++++++++ mne/channels/channels.py | 37 +++++++++++++++---------------------- 2 files changed, 54 insertions(+), 22 deletions(-) create mode 100644 app.py diff --git a/app.py b/app.py new file mode 100644 index 00000000000..22d0de3631f --- /dev/null +++ b/app.py @@ -0,0 +1,39 @@ +import numpy as np +import mne +# 1. Load sample Raw data (ensures other channels have positions for interpolation) +# We crop the data to 1 second to make it fast. +print("Step 1: Loading sample MEG data...") +data_path = mne.datasets.sample.data_path() +raw_fname = data_path / 'MEG' / 'sample' / 'sample_audvis_raw.fif' +raw = mne.io.read_raw_fif(raw_fname, preload=True).pick_types(meg=True).crop(0, 1) + +sfreq = raw.info['sfreq'] +n_samples = raw.n_times + +# 2. Create a new "empty" channel that *lacks sensor positions* +new_ch_name = 'MEG_BAD_NO_POS' +empty_ch_data = np.zeros((1, n_samples)) + +# The key is here: The created info object for a new channel does NOT have sensor positions (loc). +info_empty = mne.create_info(ch_names=[new_ch_name], sfreq=sfreq, ch_types=['mag']) +raw_ch = mne.io.RawArray(data=empty_ch_data, info=info_empty) + +# 3. Add the new channel to the main Raw object +raw.add_channels([raw_ch], force_update_info=True) + +# 4. Mark the channel as bad +raw.info['bads'] = [new_ch_name] +idx = raw.ch_names.index(new_ch_name) +# 5. Interpolate bad channels (This runs *silently* without error/warning) +print("\nStep 5: Running raw.interpolate_bads()...") +raw_interp = raw.copy().interpolate_bads() + +# 6. Check the interpolated channel data (Replication of the error) +interp_data = raw_interp.get_data(picks=new_ch_name)[0] +is_nan = np.isnan(interp_data).all() +print(interp_data) +print("\n--- Replication Result ---") +print(f"Is the interpolated channel '{new_ch_name}' entirely NaN? {is_nan}") +if is_nan: + print("✅ The bug is replicated: Interpolation failed silently, resulting in NaN data.") +print(raw.info['chs'][idx]['loc']) \ No newline at end of file diff --git a/mne/channels/channels.py b/mne/channels/channels.py index e57de953e9d..50592cf234f 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -827,8 +827,8 @@ def interpolate_bads( origin="auto", method=None, exclude=(), + on_no_position="raise", verbose=None, - **kwargs, ): """Interpolate bad MEG and EEG channels. @@ -897,27 +897,20 @@ def interpolate_bads( _check_preload(self, "interpolation") _validate_type(method, (dict, str, None), "method") - bads_to_check = self.info["bads"] - for ch_name in bads_to_check: - try: - ch_idx = self.ch_names.index(ch_name) - ch_loc = self.info["chs"][ch_idx]["loc"] - is_loc_invalid = np.allclose(ch_loc, 0.0, atol=1e-16) or np.any( - np.isnan(ch_loc) - ) - except ValueError: - continue - - if is_loc_invalid: - msg = ( - f"Bad channel '{ch_name}' is missing valid sensor position (loc) information. " - "Interpolation cannot proceed correctly." - ) - _on_missing( - kwargs.get("on_no_position", "error"), - msg + " If you want to continue despite missing positions, set " - "on_no_position='warn' or 'ignore'.", - ) + + invalid_chs = [] + for ch in self.info['bads']: + loc = self.info['chs'][self.ch_names.index(ch)]['loc'] + if np.allclose(loc, 0.0, atol=1e-16) or np.isnan(loc).any(): + invalid_chs.append(ch) + + if invalid_chs: + msg = ( + f"Bad channel(s) '{invalid_chs}' have missing valid sensor position (loc) information." + "\nInterpolation cannot proceed correctly. If you want to continue despite missing positions, set " + "on_no_position='warn' or 'ignore', which outputs all NaN values(np.nan) for the interpolated channel(s)." + ) + _on_missing(on_no_position, msg) method = _handle_default("interpolation_method", method) ch_types = self.get_channel_types(unique=True) From 0fa2ba9c8807fbad3d7da9aa16b0c48bf6051784 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 06:01:54 +0000 Subject: [PATCH 04/25] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mne/channels/channels.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index 50592cf234f..5cdaf4fefac 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -897,10 +897,10 @@ def interpolate_bads( _check_preload(self, "interpolation") _validate_type(method, (dict, str, None), "method") - + invalid_chs = [] - for ch in self.info['bads']: - loc = self.info['chs'][self.ch_names.index(ch)]['loc'] + for ch in self.info["bads"]: + loc = self.info["chs"][self.ch_names.index(ch)]["loc"] if np.allclose(loc, 0.0, atol=1e-16) or np.isnan(loc).any(): invalid_chs.append(ch) From 272c38aa4d035a55e6ec21b3742aab63640d5fc6 Mon Sep 17 00:00:00 2001 From: 1himan Date: Wed, 3 Dec 2025 11:32:37 +0530 Subject: [PATCH 05/25] Removed my custome testing file --- app.py | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 app.py diff --git a/app.py b/app.py deleted file mode 100644 index 22d0de3631f..00000000000 --- a/app.py +++ /dev/null @@ -1,39 +0,0 @@ -import numpy as np -import mne -# 1. Load sample Raw data (ensures other channels have positions for interpolation) -# We crop the data to 1 second to make it fast. -print("Step 1: Loading sample MEG data...") -data_path = mne.datasets.sample.data_path() -raw_fname = data_path / 'MEG' / 'sample' / 'sample_audvis_raw.fif' -raw = mne.io.read_raw_fif(raw_fname, preload=True).pick_types(meg=True).crop(0, 1) - -sfreq = raw.info['sfreq'] -n_samples = raw.n_times - -# 2. Create a new "empty" channel that *lacks sensor positions* -new_ch_name = 'MEG_BAD_NO_POS' -empty_ch_data = np.zeros((1, n_samples)) - -# The key is here: The created info object for a new channel does NOT have sensor positions (loc). -info_empty = mne.create_info(ch_names=[new_ch_name], sfreq=sfreq, ch_types=['mag']) -raw_ch = mne.io.RawArray(data=empty_ch_data, info=info_empty) - -# 3. Add the new channel to the main Raw object -raw.add_channels([raw_ch], force_update_info=True) - -# 4. Mark the channel as bad -raw.info['bads'] = [new_ch_name] -idx = raw.ch_names.index(new_ch_name) -# 5. Interpolate bad channels (This runs *silently* without error/warning) -print("\nStep 5: Running raw.interpolate_bads()...") -raw_interp = raw.copy().interpolate_bads() - -# 6. Check the interpolated channel data (Replication of the error) -interp_data = raw_interp.get_data(picks=new_ch_name)[0] -is_nan = np.isnan(interp_data).all() -print(interp_data) -print("\n--- Replication Result ---") -print(f"Is the interpolated channel '{new_ch_name}' entirely NaN? {is_nan}") -if is_nan: - print("✅ The bug is replicated: Interpolation failed silently, resulting in NaN data.") -print(raw.info['chs'][idx]['loc']) \ No newline at end of file From 04d2f7ef5630777c8a93504de84862a7d9071207 Mon Sep 17 00:00:00 2001 From: 1himan Date: Wed, 3 Dec 2025 11:40:54 +0530 Subject: [PATCH 06/25] minor changes --- mne/channels/channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index 5cdaf4fefac..1fdc50fbec0 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -827,7 +827,7 @@ def interpolate_bads( origin="auto", method=None, exclude=(), - on_no_position="raise", + on_no_position="warn", verbose=None, ): """Interpolate bad MEG and EEG channels. From db1b9934ac1c820d76f3494932bc5a14c31b3e7c Mon Sep 17 00:00:00 2001 From: 1himan Date: Wed, 3 Dec 2025 12:05:41 +0530 Subject: [PATCH 07/25] Fixing CI tests --- mne/channels/channels.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index 1fdc50fbec0..f71d790bfb5 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -906,9 +906,12 @@ def interpolate_bads( if invalid_chs: msg = ( - f"Bad channel(s) '{invalid_chs}' have missing valid sensor position (loc) information." - "\nInterpolation cannot proceed correctly. If you want to continue despite missing positions, set " - "on_no_position='warn' or 'ignore', which outputs all NaN values(np.nan) for the interpolated channel(s)." + f"Bad channel(s) {invalid_chs} have missing valid sensor position " + "(loc) information.\n" + "Interpolation cannot proceed correctly. If you want to continue " + "despite missing positions, set on_no_position='warn' or 'ignore', " + "which outputs all NaN values (np.nan) for the interpolated " + "channel(s)." ) _on_missing(on_no_position, msg) From 63f8dd509463036fc55f675765a3c333b5b39625 Mon Sep 17 00:00:00 2001 From: 1himan Date: Sun, 21 Dec 2025 15:12:38 +0530 Subject: [PATCH 08/25] Validation warning while processing docstring for .interpolate_bads(), Parameters {on_no_position} not found - fix --- mne/channels/channels.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index f71d790bfb5..258fbcd5fd7 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -873,6 +873,9 @@ def interpolate_bads( exclude : list | tuple The channels to exclude from interpolation. If excluded a bad channel will stay in bads. + on_no_position : 'raise' | 'warn' | 'ignore' + Decides how to handle the case when sensor positions of input + channel(s) is invalid. %(verbose)s Returns From c1bcbc59a66fb9982e2f68495a2db8ff39188cad Mon Sep 17 00:00:00 2001 From: 1himan Date: Sun, 21 Dec 2025 15:22:18 +0530 Subject: [PATCH 09/25] Indentation fix --- mne/channels/channels.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index 258fbcd5fd7..8b07e615408 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -874,8 +874,8 @@ def interpolate_bads( The channels to exclude from interpolation. If excluded a bad channel will stay in bads. on_no_position : 'raise' | 'warn' | 'ignore' - Decides how to handle the case when sensor positions of input - channel(s) is invalid. + Decides how to handle the case when sensor positions of input + channel(s) is invalid. %(verbose)s Returns From fec6daf25b84a0892e499071c6174b017513b41f Mon Sep 17 00:00:00 2001 From: 1himan Date: Sun, 21 Dec 2025 15:34:30 +0530 Subject: [PATCH 10/25] CLI check fix --- mne/channels/channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index 8b07e615408..89e6133cb48 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -873,7 +873,7 @@ def interpolate_bads( exclude : list | tuple The channels to exclude from interpolation. If excluded a bad channel will stay in bads. - on_no_position : 'raise' | 'warn' | 'ignore' + on_no_position : "raise" | "warn" | "ignore" Decides how to handle the case when sensor positions of input channel(s) is invalid. %(verbose)s From d84c11fe4c96991bc2e7fa34123cbbbe5d99d7a1 Mon Sep 17 00:00:00 2001 From: 1himan Date: Fri, 2 Jan 2026 19:17:44 +0530 Subject: [PATCH 11/25] this fixes the cli tests --- mne/channels/channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index 89e6133cb48..0cfe86a502e 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -903,7 +903,7 @@ def interpolate_bads( invalid_chs = [] for ch in self.info["bads"]: - loc = self.info["chs"][self.ch_names.index(ch)]["loc"] + loc = self.info["chs"][self.ch_names.index(ch)]["loc"][:3] if np.allclose(loc, 0.0, atol=1e-16) or np.isnan(loc).any(): invalid_chs.append(ch) From 78ad836822aac617589f55ca9d7d0279ea705b76 Mon Sep 17 00:00:00 2001 From: 1himan Date: Wed, 7 Jan 2026 12:34:35 +0530 Subject: [PATCH 12/25] Made suggested changes --- mne/channels/channels.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index ed86fa2c472..13b228218e4 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -827,7 +827,7 @@ def interpolate_bads( origin="auto", method=None, exclude=(), - on_no_position="warn", + on_bad_position="warn", verbose=None, ): """Interpolate bad MEG and EEG channels. @@ -873,9 +873,10 @@ def interpolate_bads( exclude : list | tuple The channels to exclude from interpolation. If excluded a bad channel will stay in bads. - on_no_position : "raise" | "warn" | "ignore" - Decides how to handle the case when sensor positions of input - channel(s) is invalid. + on_bad_position : "raise" | "warn" | "ignore" + What to do when one or more sensor positions are invalid (zero or NaN). + If ``"warn"`` or ``"ignore"``, channels with invalid positions will be + filled with :data:`~numpy.nan`. %(verbose)s Returns @@ -909,14 +910,13 @@ def interpolate_bads( if invalid_chs: msg = ( - f"Bad channel(s) {invalid_chs} have missing valid sensor position " - "(loc) information.\n" + f"Channel(s) {invalid_chs} have invalid sensor position(s). " "Interpolation cannot proceed correctly. If you want to continue " - "despite missing positions, set on_no_position='warn' or 'ignore', " + "despite missing positions, set on_bad_position='warn' or 'ignore', " "which outputs all NaN values (np.nan) for the interpolated " "channel(s)." ) - _on_missing(on_no_position, msg) + _on_missing(on_bad_position, msg) method = _handle_default("interpolation_method", method) ch_types = self.get_channel_types(unique=True) From 93580513a5ff737bb204396e05ffb5a29c46395f Mon Sep 17 00:00:00 2001 From: 1himan Date: Wed, 7 Jan 2026 17:18:06 +0530 Subject: [PATCH 13/25] Improved tests --- mne/channels/tests/test_interpolation.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/mne/channels/tests/test_interpolation.py b/mne/channels/tests/test_interpolation.py index de09a97c306..dcdd8f73d57 100644 --- a/mne/channels/tests/test_interpolation.py +++ b/mne/channels/tests/test_interpolation.py @@ -420,6 +420,30 @@ def test_nan_interpolation(raw): raw.interpolate_bads(method="nan", reset_bads=False) assert raw.info["bads"] == ch_to_interp + store = raw.info["chs"][1]["loc"] + # for on_bad_position="warn" + with pytest.warns(RuntimeWarning): + raw.info["bads"] = ch_to_interp + # delibrately set loc elements to np.nan + raw.info["chs"][1]["loc"] = np.full(12, np.nan) + raw.interpolate_bads(on_bad_position="warn") + assert np.isnan(raw.info["chs"][1]["loc"]).any() == True + + # for on_bad_position="raise" + with pytest.raises(ValueError): + raw.info["bads"] = ch_to_interp + raw.info["chs"][1]["loc"] = np.full(12, np.nan) + raw.interpolate_bads(on_bad_position="raise") + assert np.isnan(raw.info["chs"][1]["loc"]).any() == True + + # for on_bad_position="ignore" + raw.info["bads"] = ch_to_interp + raw.info["chs"][1]["loc"] = np.full(12, np.nan) + raw.interpolate_bads(on_bad_position="ignore") + assert np.isnan(raw.info["chs"][1]["loc"]).any() == True + + raw.info["chs"][1]["loc"] = store + # make sure other channels are untouched raw.drop_channels(ch_to_interp) good_chs = raw.get_data() From 2d19ecbc674308dac0550d842fac7f458c03e9c7 Mon Sep 17 00:00:00 2001 From: 1himan Date: Wed, 7 Jan 2026 20:51:07 +0530 Subject: [PATCH 14/25] Fixed linting errors --- mne/channels/tests/test_interpolation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mne/channels/tests/test_interpolation.py b/mne/channels/tests/test_interpolation.py index dcdd8f73d57..299f67ce015 100644 --- a/mne/channels/tests/test_interpolation.py +++ b/mne/channels/tests/test_interpolation.py @@ -427,20 +427,20 @@ def test_nan_interpolation(raw): # delibrately set loc elements to np.nan raw.info["chs"][1]["loc"] = np.full(12, np.nan) raw.interpolate_bads(on_bad_position="warn") - assert np.isnan(raw.info["chs"][1]["loc"]).any() == True + assert np.isnan(raw.info["chs"][1]["loc"]).any() # for on_bad_position="raise" with pytest.raises(ValueError): raw.info["bads"] = ch_to_interp raw.info["chs"][1]["loc"] = np.full(12, np.nan) raw.interpolate_bads(on_bad_position="raise") - assert np.isnan(raw.info["chs"][1]["loc"]).any() == True + assert np.isnan(raw.info["chs"][1]["loc"]).any() # for on_bad_position="ignore" raw.info["bads"] = ch_to_interp raw.info["chs"][1]["loc"] = np.full(12, np.nan) raw.interpolate_bads(on_bad_position="ignore") - assert np.isnan(raw.info["chs"][1]["loc"]).any() == True + assert np.isnan(raw.info["chs"][1]["loc"]).any() raw.info["chs"][1]["loc"] = store From 4915ff00ec5b3e34a4d8760b27adc1850ca779ca Mon Sep 17 00:00:00 2001 From: 1himan Date: Thu, 8 Jan 2026 12:33:33 +0530 Subject: [PATCH 15/25] Rmoved redundancy --- mne/channels/tests/test_interpolation.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mne/channels/tests/test_interpolation.py b/mne/channels/tests/test_interpolation.py index 299f67ce015..3afbf9fd946 100644 --- a/mne/channels/tests/test_interpolation.py +++ b/mne/channels/tests/test_interpolation.py @@ -421,24 +421,24 @@ def test_nan_interpolation(raw): assert raw.info["bads"] == ch_to_interp store = raw.info["chs"][1]["loc"] - # for on_bad_position="warn" - with pytest.warns(RuntimeWarning): - raw.info["bads"] = ch_to_interp - # delibrately set loc elements to np.nan - raw.info["chs"][1]["loc"] = np.full(12, np.nan) - raw.interpolate_bads(on_bad_position="warn") - assert np.isnan(raw.info["chs"][1]["loc"]).any() - # for on_bad_position="raise" - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="have invalid sensor position"): raw.info["bads"] = ch_to_interp raw.info["chs"][1]["loc"] = np.full(12, np.nan) + # DOES NOT interpolates at all. So raw.info["bads"] remains as is raw.interpolate_bads(on_bad_position="raise") assert np.isnan(raw.info["chs"][1]["loc"]).any() + # for on_bad_position="warn" + with pytest.warns(RuntimeWarning, match="have invalid sensor position"): + # this DOES the interpolation BUT with a warning + # so raw.info["bad"] will be empty again, + # and interpolated channel with be all np.nan + raw.interpolate_bads(on_bad_position="warn") + assert np.isnan(raw.info["chs"][1]["loc"]).any() + # for on_bad_position="ignore" raw.info["bads"] = ch_to_interp - raw.info["chs"][1]["loc"] = np.full(12, np.nan) raw.interpolate_bads(on_bad_position="ignore") assert np.isnan(raw.info["chs"][1]["loc"]).any() From 68dbb2802d57019440a76584601a019108d8cfb8 Mon Sep 17 00:00:00 2001 From: 1himan Date: Tue, 13 Jan 2026 12:01:29 +0530 Subject: [PATCH 16/25] Added dev entries, and fixed test logic --- doc/changes/dev/13518.bugfix.rst | 1 + doc/changes/names.inc | 1 + mne/channels/tests/test_interpolation.py | 11 ++++------- 3 files changed, 6 insertions(+), 7 deletions(-) create mode 100644 doc/changes/dev/13518.bugfix.rst diff --git a/doc/changes/dev/13518.bugfix.rst b/doc/changes/dev/13518.bugfix.rst new file mode 100644 index 00000000000..c98ab2d728b --- /dev/null +++ b/doc/changes/dev/13518.bugfix.rst @@ -0,0 +1 @@ +Raise warning implementation in :meth:`mne.channels.InterpolationMixin.interpolate_bads` when interpolation of channels with invalid position is done, by :newcontrib:`Himanshu Mahor`_. diff --git a/doc/changes/names.inc b/doc/changes/names.inc index 5a5a1e98f89..6b58afd6960 100644 --- a/doc/changes/names.inc +++ b/doc/changes/names.inc @@ -117,6 +117,7 @@ .. _Harrison Ritz: https://github.com/harrisonritz .. _Hasrat Ali Arzoo: https://github.com/hasrat17 .. _Henrich Kolkhorst: https://github.com/hekolk +.. _Himanshu Mahor: https://github.com/1himan .. _Hongjiang Ye: https://github.com/ye-hongjiang .. _Hubert Banville: https://github.com/hubertjb .. _Hyonyoung Shin: https://github.com/mcvain diff --git a/mne/channels/tests/test_interpolation.py b/mne/channels/tests/test_interpolation.py index 3afbf9fd946..3f7699622ce 100644 --- a/mne/channels/tests/test_interpolation.py +++ b/mne/channels/tests/test_interpolation.py @@ -422,12 +422,11 @@ def test_nan_interpolation(raw): store = raw.info["chs"][1]["loc"] # for on_bad_position="raise" + raw.info["bads"] = ch_to_interp + raw.info["chs"][1]["loc"] = np.full(12, np.nan) with pytest.raises(ValueError, match="have invalid sensor position"): - raw.info["bads"] = ch_to_interp - raw.info["chs"][1]["loc"] = np.full(12, np.nan) # DOES NOT interpolates at all. So raw.info["bads"] remains as is raw.interpolate_bads(on_bad_position="raise") - assert np.isnan(raw.info["chs"][1]["loc"]).any() # for on_bad_position="warn" with pytest.warns(RuntimeWarning, match="have invalid sensor position"): @@ -435,13 +434,11 @@ def test_nan_interpolation(raw): # so raw.info["bad"] will be empty again, # and interpolated channel with be all np.nan raw.interpolate_bads(on_bad_position="warn") - assert np.isnan(raw.info["chs"][1]["loc"]).any() # for on_bad_position="ignore" raw.info["bads"] = ch_to_interp - raw.interpolate_bads(on_bad_position="ignore") - assert np.isnan(raw.info["chs"][1]["loc"]).any() - + assert raw.interpolate_bads(on_bad_position="ignore") + assert np.isnan(bad_chs).all, "Interpolated channel should be all NaN" raw.info["chs"][1]["loc"] = store # make sure other channels are untouched From a3f70e301c6299f64374a9f2293b82403c48f232 Mon Sep 17 00:00:00 2001 From: 1himan Date: Tue, 13 Jan 2026 13:28:11 +0530 Subject: [PATCH 17/25] Fixing ci tests --- doc/changes/dev/13518.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changes/dev/13518.bugfix.rst b/doc/changes/dev/13518.bugfix.rst index c98ab2d728b..dfcbd7eee73 100644 --- a/doc/changes/dev/13518.bugfix.rst +++ b/doc/changes/dev/13518.bugfix.rst @@ -1 +1 @@ -Raise warning implementation in :meth:`mne.channels.InterpolationMixin.interpolate_bads` when interpolation of channels with invalid position is done, by :newcontrib:`Himanshu Mahor`_. +Raise warning implementation in interpolate_bads() method when interpolation of channels with invalid position is done, by :newcontrib:`Himanshu Mahor`. From 3828f824daff466e6c6ab9bad63bc55bb5ee3d7d Mon Sep 17 00:00:00 2001 From: 1himan Date: Tue, 20 Jan 2026 20:14:57 +0530 Subject: [PATCH 18/25] Polish --- doc/changes/dev/13518.bugfix.rst | 2 +- mne/channels/channels.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/changes/dev/13518.bugfix.rst b/doc/changes/dev/13518.bugfix.rst index dfcbd7eee73..f7d73a2cb04 100644 --- a/doc/changes/dev/13518.bugfix.rst +++ b/doc/changes/dev/13518.bugfix.rst @@ -1 +1 @@ -Raise warning implementation in interpolate_bads() method when interpolation of channels with invalid position is done, by :newcontrib:`Himanshu Mahor`. +Raise warning implementation in interpolate_bads() method when interpolation of channels with invalid position is done, by `Himanshu Mahor`_. diff --git a/mne/channels/channels.py b/mne/channels/channels.py index 13b228218e4..4825fccd882 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -827,7 +827,7 @@ def interpolate_bads( origin="auto", method=None, exclude=(), - on_bad_position="warn", + on_bad_position="raise", verbose=None, ): """Interpolate bad MEG and EEG channels. From c8013345338844a3246a16e76b818ef2f1284bec Mon Sep 17 00:00:00 2001 From: Himanshu Mahor <140396762+1himan@users.noreply.github.com> Date: Mon, 23 Feb 2026 19:44:40 +0530 Subject: [PATCH 19/25] Change on_bad_position behavior from 'raise' to 'warn' --- mne/channels/channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index bf09fcf3308..a7b358998d1 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -829,7 +829,7 @@ def interpolate_bads( origin="auto", method=None, exclude=(), - on_bad_position="raise", + on_bad_position="warn", verbose=None, ): """Interpolate bad MEG and EEG channels. From 59072501449aadc9e0c379aa76e9beac59f41de5 Mon Sep 17 00:00:00 2001 From: 1himan Date: Mon, 23 Feb 2026 20:03:03 +0530 Subject: [PATCH 20/25] More meaningful error and warning message to the user --- mne/channels/channels.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index a7b358998d1..6f686a6df00 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -911,15 +911,19 @@ def interpolate_bads( invalid_chs.append(ch) if invalid_chs: - msg = ( - f"Channel(s) {invalid_chs} have invalid sensor position(s). " - "Interpolation cannot proceed correctly. If you want to continue " - "despite missing positions, set on_bad_position='warn' or 'ignore', " - "which outputs all NaN values (np.nan) for the interpolated " - "channel(s)." + _on_missing( + on_bad_position, + ( + f"Channel(s) {invalid_chs} have invalid sensor position(s). " + "Interpolation cannot proceed correctly. If you want to continue " + "despite missing positions, set on_bad_position='warn' or 'ignore', " + "which outputs all NaN values (np.nan) for the interpolated channel(s)." + if on_bad_position == "raise" + else f"Channel(s) {invalid_chs} have invalid sensor position(s) and " + "cannot be interpolated. The values of these channels will be all NaN. " + "To ignore this warning, pass on_bad_position='ignore'." + ), ) - _on_missing(on_bad_position, msg) - method = _handle_default("interpolation_method", method) ch_types = self.get_channel_types(unique=True) # figure out if we have "mag" for "meg", "hbo" for "fnirs", ... to filter the From 590861c8c1a96aaff696acacc4222ab4508bec06 Mon Sep 17 00:00:00 2001 From: 1himan Date: Mon, 23 Feb 2026 20:15:02 +0530 Subject: [PATCH 21/25] fixed ruff error --- mne/channels/channels.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index 6f686a6df00..d7c062e8bc3 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -914,14 +914,20 @@ def interpolate_bads( _on_missing( on_bad_position, ( - f"Channel(s) {invalid_chs} have invalid sensor position(s). " - "Interpolation cannot proceed correctly. If you want to continue " - "despite missing positions, set on_bad_position='warn' or 'ignore', " - "which outputs all NaN values (np.nan) for the interpolated channel(s)." + ( + f"Channel(s) {invalid_chs} have invalid sensor position(s). " + "Interpolation cannot proceed correctly. If you want to " + "continue despite missing positions, set " + "on_bad_position='warn' or 'ignore', which outputs all " + "NaN values (np.nan) for the interpolated channel(s)." + ) if on_bad_position == "raise" - else f"Channel(s) {invalid_chs} have invalid sensor position(s) and " - "cannot be interpolated. The values of these channels will be all NaN. " - "To ignore this warning, pass on_bad_position='ignore'." + else ( + f"Channel(s) {invalid_chs} have invalid sensor position(s) " + "and cannot be interpolated. The values of these channels " + "will be all NaN. To ignore this warning, pass " + "on_bad_position='ignore'." + ) ), ) method = _handle_default("interpolation_method", method) From a2ab8c9b26e5b5a5e0eabeb384c9ea8303e0c536 Mon Sep 17 00:00:00 2001 From: 1himan Date: Wed, 25 Feb 2026 15:11:30 +0530 Subject: [PATCH 22/25] Adding final suggestions --- doc/changes/dev/13518.bugfix.rst | 2 +- mne/channels/channels.py | 39 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/doc/changes/dev/13518.bugfix.rst b/doc/changes/dev/13518.bugfix.rst index f7d73a2cb04..7b74ebb4453 100644 --- a/doc/changes/dev/13518.bugfix.rst +++ b/doc/changes/dev/13518.bugfix.rst @@ -1 +1 @@ -Raise warning implementation in interpolate_bads() method when interpolation of channels with invalid position is done, by `Himanshu Mahor`_. +Make :meth:`~mne.io.Raw.interpolate_bads` method flexible (ignore, warn, raise) about how to handle interpolation of channels with invalid positions, by `Himanshu Mahor`_. diff --git a/mne/channels/channels.py b/mne/channels/channels.py index d7c062e8bc3..a90c200fa8f 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -904,32 +904,31 @@ def interpolate_bads( _check_preload(self, "interpolation") _validate_type(method, (dict, str, None), "method") + # check for channels with invalid position(s) invalid_chs = [] for ch in self.info["bads"]: loc = self.info["chs"][self.ch_names.index(ch)]["loc"][:3] - if np.allclose(loc, 0.0, atol=1e-16) or np.isnan(loc).any(): + if np.allclose(loc, 0.0, rtol=0, atol=1e-16) or np.isnan(loc).any(): invalid_chs.append(ch) if invalid_chs: - _on_missing( - on_bad_position, - ( - ( - f"Channel(s) {invalid_chs} have invalid sensor position(s). " - "Interpolation cannot proceed correctly. If you want to " - "continue despite missing positions, set " - "on_bad_position='warn' or 'ignore', which outputs all " - "NaN values (np.nan) for the interpolated channel(s)." - ) - if on_bad_position == "raise" - else ( - f"Channel(s) {invalid_chs} have invalid sensor position(s) " - "and cannot be interpolated. The values of these channels " - "will be all NaN. To ignore this warning, pass " - "on_bad_position='ignore'." - ) - ), - ) + if on_bad_position == "raise": + msg = ( + f"Channel(s) {invalid_chs} have invalid sensor position(s). " + "Interpolation cannot proceed correctly. If you want to " + "continue despite missing positions, set " + "on_bad_position='warn' or 'ignore', which outputs all " + "NaN values (np.nan) for the interpolated channel(s)." + ) + else: + msg = ( + f"Channel(s) {invalid_chs} have invalid sensor position(s) " + "and cannot be interpolated. The values of these channels " + "will be all NaN. To ignore this warning, pass " + "on_bad_position='ignore'." + ) + _on_missing(on_bad_position, msg) + method = _handle_default("interpolation_method", method) ch_types = self.get_channel_types(unique=True) # figure out if we have "mag" for "meg", "hbo" for "fnirs", ... to filter the From 9512936739f1242e6b478133aaf58e99a4f6386f Mon Sep 17 00:00:00 2001 From: 1himan Date: Thu, 12 Mar 2026 18:13:32 +0530 Subject: [PATCH 23/25] Adding version:1.12 --- mne/channels/channels.py | 2 + mne/report/report.py | 150 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/mne/channels/channels.py b/mne/channels/channels.py index 427e9e4f2e6..fbfb759cf92 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -879,6 +879,8 @@ def interpolate_bads( What to do when one or more sensor positions are invalid (zero or NaN). If ``"warn"`` or ``"ignore"``, channels with invalid positions will be filled with :data:`~numpy.nan`. + + .. versionadded:: 1.12 %(verbose)s Returns diff --git a/mne/report/report.py b/mne/report/report.py index 071d15e3cee..8edeb82e26d 100644 --- a/mne/report/report.py +++ b/mne/report/report.py @@ -40,7 +40,7 @@ from ..minimum_norm import InverseOperator, read_inverse_operator from ..parallel import parallel_func from ..preprocessing.ica import read_ica -from ..proj import read_proj +from ..proj import read_proj, sensitivity_map from ..source_estimate import SourceEstimate, read_source_estimate from ..source_space._source_space import _ensure_src from ..surface import dig_mri_distances @@ -1546,6 +1546,7 @@ def add_forward( subject=None, subjects_dir=None, plot=False, + sensitivity=False, tags=("forward-solution",), section=None, replace=False, @@ -1567,6 +1568,14 @@ def add_forward( If True, plot the source space of the forward solution. .. versionadded:: 1.10 + sensitivity : bool | list of str + If True, compute and plot sensitivity maps for all available + channel types (MEG gradiometers, MEG magnetometers, and EEG). + If a list, compute sensitivity maps for only the specified + channel types (e.g., ``['grad', 'mag']``). + Valid channel types are ``'grad'``, ``'mag'``, and ``'eeg'``. + + .. versionadded:: 1.12 %(tags_report)s %(section_report)s @@ -1589,6 +1598,7 @@ def add_forward( tags=tags, replace=replace, plot=plot, + sensitivity=sensitivity, ) @fill_doc @@ -3616,6 +3626,7 @@ def _add_forward( title, image_format, plot, + sensitivity, section, tags, replace, @@ -3626,10 +3637,143 @@ def _add_forward( subject = self.subject if subject is None else subject subject = forward["src"][0]["subject_his_id"] if subject is None else subject + subjects_dir = self.subjects_dir if subjects_dir is None else subjects_dir - # XXX Todo - # Render sensitivity maps sensitivity_maps_html = "" + if sensitivity: + if subjects_dir is None: + raise ValueError( + "subjects_dir must be provided to compute sensitivity maps" + ) + + info = forward["info"] + meg_info = info.get("meg", False) + has_grad = meg_info and ( + isinstance(meg_info, dict) + and meg_info.get("grad", False) + or meg_info is True + ) + has_mag = meg_info and ( + isinstance(meg_info, dict) + and meg_info.get("mag", False) + or meg_info is True + ) + has_eeg = info.get("eeg", False) + + all_ch_types = [] + if has_grad: + all_ch_types.append("grad") + if has_mag: + all_ch_types.append("mag") + if has_eeg: + all_ch_types.append("eeg") + + if not all_ch_types: + raise ValueError( + "No MEG or EEG channels found in forward solution. " + "Cannot compute sensitivity maps." + ) + + if sensitivity is True: + ch_types = all_ch_types + else: + ch_types = list(sensitivity) + for ch_type in ch_types: + _check_option("ch_type", ch_type, ["grad", "mag", "eeg"]) + if ch_type not in all_ch_types: + raise ValueError( + f"Channel type '{ch_type}' not found in forward solution. " + f"Available types are: {all_ch_types}" + ) + + html_parts = [] + for ch_type in ch_types: + _check_option("ch_type", ch_type, ["grad", "mag", "eeg"]) + + html_parts = [] + for ch_type in ch_types: + stc = sensitivity_map(forward, ch_type=ch_type, mode="fixed") + + stc_plot_kwargs = _handle_default("report_stc_plot_kwargs", dict()) + stc_plot_kwargs.update( + subject=subject, + subjects_dir=subjects_dir, + clim=dict(kind="value", lims=[0, 50, 100]), + colorbar=True, + ) + + import matplotlib.pyplot as plt + + if get_3d_backend() is not None: + brain = stc.plot(**stc_plot_kwargs) + brain._renderer.plotter.subplot(0, 0) + fig, ax = plt.subplots(figsize=(4.5, 4.5), layout="constrained") + ax.imshow(brain.screenshot(time_viewer=False, mode="rgb")) + ax.axis("off") + _constrain_fig_resolution( + fig, + max_width=stc_plot_kwargs.get("size", (800, 600))[0], + max_res=self.img_max_res, + ) + plt.close(fig) + brain.close() + else: + fig_lh = plt.figure(layout="constrained") + fig_rh = plt.figure(layout="constrained") + brain_lh = stc.plot( + views="lat", + hemi="lh", + initial_time=stc.times[0], + backend="matplotlib", + subject=subject, + subjects_dir=subjects_dir, + figure=fig_lh, + **stc_plot_kwargs, + ) + brain_rh = stc.plot( + views="lat", + hemi="rh", + initial_time=stc.times[0], + subject=subject, + subjects_dir=subjects_dir, + backend="matplotlib", + figure=fig_rh, + **stc_plot_kwargs, + ) + _constrain_fig_resolution( + fig_lh, + max_width=stc_plot_kwargs.get("size", (800, 600))[0], + max_res=self.img_max_res, + ) + _constrain_fig_resolution( + fig_rh, + max_width=stc_plot_kwargs.get("size", (800, 600))[0], + max_res=self.img_max_res, + ) + fig = fig_lh + plt.close(fig_rh) + brain_lh.close() + brain_rh.close() + + img = self._fig_to_img(fig=fig, image_format=image_format) + plt.close(fig) + + img_id = f"forward-sensitivity-{ch_type}" + img_html = _html_image_element( + id_=img_id, + img=img, + image_format=image_format, + caption=f"Sensitivity map ({ch_type})", + show=True, + div_klass="forward-sensitivity-map", + img_klass="forward-sensitivity-map", + title=f"Sensitivity Map - {ch_type.upper()}", + tags=(), + ) + html_parts.append(img_html) + + sensitivity_maps_html = "\n".join(html_parts) + source_space_html = "" if plot: source_space_html = self._src_html( From 7a79dd1e87f8500048c9908fb3c93402e5487fd2 Mon Sep 17 00:00:00 2001 From: 1himan Date: Thu, 12 Mar 2026 20:25:05 +0530 Subject: [PATCH 24/25] Reverting changes --- doc/changes/dev/13518.bugfix.rst | 1 - mne/report/report.py | 150 +------------------------------ 2 files changed, 3 insertions(+), 148 deletions(-) delete mode 100644 doc/changes/dev/13518.bugfix.rst diff --git a/doc/changes/dev/13518.bugfix.rst b/doc/changes/dev/13518.bugfix.rst deleted file mode 100644 index 7b74ebb4453..00000000000 --- a/doc/changes/dev/13518.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Make :meth:`~mne.io.Raw.interpolate_bads` method flexible (ignore, warn, raise) about how to handle interpolation of channels with invalid positions, by `Himanshu Mahor`_. diff --git a/mne/report/report.py b/mne/report/report.py index 8edeb82e26d..071d15e3cee 100644 --- a/mne/report/report.py +++ b/mne/report/report.py @@ -40,7 +40,7 @@ from ..minimum_norm import InverseOperator, read_inverse_operator from ..parallel import parallel_func from ..preprocessing.ica import read_ica -from ..proj import read_proj, sensitivity_map +from ..proj import read_proj from ..source_estimate import SourceEstimate, read_source_estimate from ..source_space._source_space import _ensure_src from ..surface import dig_mri_distances @@ -1546,7 +1546,6 @@ def add_forward( subject=None, subjects_dir=None, plot=False, - sensitivity=False, tags=("forward-solution",), section=None, replace=False, @@ -1568,14 +1567,6 @@ def add_forward( If True, plot the source space of the forward solution. .. versionadded:: 1.10 - sensitivity : bool | list of str - If True, compute and plot sensitivity maps for all available - channel types (MEG gradiometers, MEG magnetometers, and EEG). - If a list, compute sensitivity maps for only the specified - channel types (e.g., ``['grad', 'mag']``). - Valid channel types are ``'grad'``, ``'mag'``, and ``'eeg'``. - - .. versionadded:: 1.12 %(tags_report)s %(section_report)s @@ -1598,7 +1589,6 @@ def add_forward( tags=tags, replace=replace, plot=plot, - sensitivity=sensitivity, ) @fill_doc @@ -3626,7 +3616,6 @@ def _add_forward( title, image_format, plot, - sensitivity, section, tags, replace, @@ -3637,143 +3626,10 @@ def _add_forward( subject = self.subject if subject is None else subject subject = forward["src"][0]["subject_his_id"] if subject is None else subject - subjects_dir = self.subjects_dir if subjects_dir is None else subjects_dir + # XXX Todo + # Render sensitivity maps sensitivity_maps_html = "" - if sensitivity: - if subjects_dir is None: - raise ValueError( - "subjects_dir must be provided to compute sensitivity maps" - ) - - info = forward["info"] - meg_info = info.get("meg", False) - has_grad = meg_info and ( - isinstance(meg_info, dict) - and meg_info.get("grad", False) - or meg_info is True - ) - has_mag = meg_info and ( - isinstance(meg_info, dict) - and meg_info.get("mag", False) - or meg_info is True - ) - has_eeg = info.get("eeg", False) - - all_ch_types = [] - if has_grad: - all_ch_types.append("grad") - if has_mag: - all_ch_types.append("mag") - if has_eeg: - all_ch_types.append("eeg") - - if not all_ch_types: - raise ValueError( - "No MEG or EEG channels found in forward solution. " - "Cannot compute sensitivity maps." - ) - - if sensitivity is True: - ch_types = all_ch_types - else: - ch_types = list(sensitivity) - for ch_type in ch_types: - _check_option("ch_type", ch_type, ["grad", "mag", "eeg"]) - if ch_type not in all_ch_types: - raise ValueError( - f"Channel type '{ch_type}' not found in forward solution. " - f"Available types are: {all_ch_types}" - ) - - html_parts = [] - for ch_type in ch_types: - _check_option("ch_type", ch_type, ["grad", "mag", "eeg"]) - - html_parts = [] - for ch_type in ch_types: - stc = sensitivity_map(forward, ch_type=ch_type, mode="fixed") - - stc_plot_kwargs = _handle_default("report_stc_plot_kwargs", dict()) - stc_plot_kwargs.update( - subject=subject, - subjects_dir=subjects_dir, - clim=dict(kind="value", lims=[0, 50, 100]), - colorbar=True, - ) - - import matplotlib.pyplot as plt - - if get_3d_backend() is not None: - brain = stc.plot(**stc_plot_kwargs) - brain._renderer.plotter.subplot(0, 0) - fig, ax = plt.subplots(figsize=(4.5, 4.5), layout="constrained") - ax.imshow(brain.screenshot(time_viewer=False, mode="rgb")) - ax.axis("off") - _constrain_fig_resolution( - fig, - max_width=stc_plot_kwargs.get("size", (800, 600))[0], - max_res=self.img_max_res, - ) - plt.close(fig) - brain.close() - else: - fig_lh = plt.figure(layout="constrained") - fig_rh = plt.figure(layout="constrained") - brain_lh = stc.plot( - views="lat", - hemi="lh", - initial_time=stc.times[0], - backend="matplotlib", - subject=subject, - subjects_dir=subjects_dir, - figure=fig_lh, - **stc_plot_kwargs, - ) - brain_rh = stc.plot( - views="lat", - hemi="rh", - initial_time=stc.times[0], - subject=subject, - subjects_dir=subjects_dir, - backend="matplotlib", - figure=fig_rh, - **stc_plot_kwargs, - ) - _constrain_fig_resolution( - fig_lh, - max_width=stc_plot_kwargs.get("size", (800, 600))[0], - max_res=self.img_max_res, - ) - _constrain_fig_resolution( - fig_rh, - max_width=stc_plot_kwargs.get("size", (800, 600))[0], - max_res=self.img_max_res, - ) - fig = fig_lh - plt.close(fig_rh) - brain_lh.close() - brain_rh.close() - - img = self._fig_to_img(fig=fig, image_format=image_format) - plt.close(fig) - - img_id = f"forward-sensitivity-{ch_type}" - img_html = _html_image_element( - id_=img_id, - img=img, - image_format=image_format, - caption=f"Sensitivity map ({ch_type})", - show=True, - div_klass="forward-sensitivity-map", - img_klass="forward-sensitivity-map", - title=f"Sensitivity Map - {ch_type.upper()}", - tags=(), - ) - html_parts.append(img_html) - - sensitivity_maps_html = "\n".join(html_parts) - source_space_html = "" if plot: source_space_html = self._src_html( From 72aa5de4be9875da36ae74c9c537464cf48e28bd Mon Sep 17 00:00:00 2001 From: 1himan Date: Thu, 12 Mar 2026 20:28:08 +0530 Subject: [PATCH 25/25] Reverting changes --- doc/changes/dev/13518.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/changes/dev/13518.bugfix.rst diff --git a/doc/changes/dev/13518.bugfix.rst b/doc/changes/dev/13518.bugfix.rst new file mode 100644 index 00000000000..7b74ebb4453 --- /dev/null +++ b/doc/changes/dev/13518.bugfix.rst @@ -0,0 +1 @@ +Make :meth:`~mne.io.Raw.interpolate_bads` method flexible (ignore, warn, raise) about how to handle interpolation of channels with invalid positions, by `Himanshu Mahor`_.