From 6feddac64d67d7818f17e44d73dff0061897c676 Mon Sep 17 00:00:00 2001 From: 1himan Date: Thu, 5 Mar 2026 15:54:43 +0530 Subject: [PATCH 1/9] Adding sensitivity maps for forward models in mne.report --- mne/report/report.py | 115 ++++++++++++++++++++++++++++++++++- tutorials/intro/70_report.py | 4 +- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/mne/report/report.py b/mne/report/report.py index 071d15e3cee..205173fb5ae 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.11 %(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,108 @@ 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" + ) + + ch_types = ["grad", "mag", "eeg"] + if sensitivity is not True: + ch_types = list(sensitivity) + for ch_type in ch_types: + _check_option("ch_type", ch_type, ["grad", "mag", "eeg"]) + + html_parts = [] + for ch_type in ch_types: + try: + stc = sensitivity_map(forward, ch_type=ch_type, mode="fixed") + except Exception: + continue + + 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( diff --git a/tutorials/intro/70_report.py b/tutorials/intro/70_report.py index 31133610e2c..dbeb4a63eeb 100644 --- a/tutorials/intro/70_report.py +++ b/tutorials/intro/70_report.py @@ -318,8 +318,8 @@ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # Forward solutions ("leadfields") can be added by passing a `~mne.Forward` -# object or the path to a forward solution stored on disk to -# :meth:`mne.Report.add_forward`. +# object or the path to a forward solution stored on disk +# to :meth:`mne.Report.add_forward`. fwd_path = sample_dir / "sample_audvis-meg-oct-6-fwd.fif" From e5bac3f09e915297a46ccb37173897b2029b82c5 Mon Sep 17 00:00:00 2001 From: 1himan Date: Thu, 5 Mar 2026 17:13:14 +0530 Subject: [PATCH 2/9] Doc changes --- tutorials/intro/70_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/intro/70_report.py b/tutorials/intro/70_report.py index dbeb4a63eeb..31133610e2c 100644 --- a/tutorials/intro/70_report.py +++ b/tutorials/intro/70_report.py @@ -318,8 +318,8 @@ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # Forward solutions ("leadfields") can be added by passing a `~mne.Forward` -# object or the path to a forward solution stored on disk -# to :meth:`mne.Report.add_forward`. +# object or the path to a forward solution stored on disk to +# :meth:`mne.Report.add_forward`. fwd_path = sample_dir / "sample_audvis-meg-oct-6-fwd.fif" From 50fa6b6276f3c28b921ccea60836ed35422b82d3 Mon Sep 17 00:00:00 2001 From: 1himan Date: Sat, 7 Mar 2026 16:50:46 +0530 Subject: [PATCH 3/9] adding suggested changes and doc/changes/dev/13722.newfeature.rst. --- doc/changes/dev/13722.newfeature.rst | 1 + mne/report/report.py | 49 ++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 doc/changes/dev/13722.newfeature.rst diff --git a/doc/changes/dev/13722.newfeature.rst b/doc/changes/dev/13722.newfeature.rst new file mode 100644 index 00000000000..64b3f5caf17 --- /dev/null +++ b/doc/changes/dev/13722.newfeature.rst @@ -0,0 +1 @@ +Add `sensitiviy` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`_. diff --git a/mne/report/report.py b/mne/report/report.py index 205173fb5ae..8edeb82e26d 100644 --- a/mne/report/report.py +++ b/mne/report/report.py @@ -1575,7 +1575,7 @@ def add_forward( channel types (e.g., ``['grad', 'mag']``). Valid channel types are ``'grad'``, ``'mag'``, and ``'eeg'``. - .. versionadded:: 1.11 + .. versionadded:: 1.12 %(tags_report)s %(section_report)s @@ -3646,18 +3646,53 @@ def _add_forward( "subjects_dir must be provided to compute sensitivity maps" ) - ch_types = ["grad", "mag", "eeg"] - if sensitivity is not True: + 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: - try: - stc = sensitivity_map(forward, ch_type=ch_type, mode="fixed") - except Exception: - continue + _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( From 8451506ac32ab457a327700a1bd776b0eae3e28f Mon Sep 17 00:00:00 2001 From: 1himan Date: Sat, 7 Mar 2026 17:23:23 +0530 Subject: [PATCH 4/9] Fixing CI failure --- doc/changes/dev/13722.newfeature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changes/dev/13722.newfeature.rst b/doc/changes/dev/13722.newfeature.rst index 64b3f5caf17..8f22cf809f1 100644 --- a/doc/changes/dev/13722.newfeature.rst +++ b/doc/changes/dev/13722.newfeature.rst @@ -1 +1 @@ -Add `sensitiviy` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`_. +Add ``sensitiviy`` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`_. From a0763e418e62d610a4126ffbbdcf13722828c2bf Mon Sep 17 00:00:00 2001 From: 1himan Date: Wed, 1 Apr 2026 17:53:34 +0530 Subject: [PATCH 5/9] Add tutorial tweaks --- tutorials/intro/70_report.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tutorials/intro/70_report.py b/tutorials/intro/70_report.py index 31133610e2c..5bab60eabc4 100644 --- a/tutorials/intro/70_report.py +++ b/tutorials/intro/70_report.py @@ -329,6 +329,18 @@ ) report.save("report_forward_sol.html", overwrite=True) +# %% +# To ge the sensitivity maps of forward models you could use `sensitivity=True`. + +report.add_forward( + forward=fwd_path, + title="Forward solution", + plot=True, + subjects_dir=subjects_dir, + sensitivity=True, # right here +) +report.save("report_forward_sol.html", overwrite=True) + # %% # Adding an `~mne.minimum_norm.InverseOperator` # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From d6c49f8207220a176abc888d6fc60e670af524b2 Mon Sep 17 00:00:00 2001 From: 1himan Date: Mon, 6 Apr 2026 12:01:33 +0530 Subject: [PATCH 6/9] Fixing CI --- mne/report/report.py | 17 ++++---------- mne/report/tests/test_report.py | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/mne/report/report.py b/mne/report/report.py index 3f10ac87c24..e66327049ea 100644 --- a/mne/report/report.py +++ b/mne/report/report.py @@ -24,7 +24,7 @@ from .. import __version__ as MNE_VERSION from .._fiff.meas_info import Info, read_info -from .._fiff.pick import _DATA_CH_TYPES_SPLIT +from .._fiff.pick import _DATA_CH_TYPES_SPLIT, _contains_ch_type from .._freesurfer import _mri_orientation, _reorient_image from ..cov import Covariance, read_cov from ..defaults import _handle_default @@ -3645,18 +3645,9 @@ def _add_forward( ) 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) + has_grad = _contains_ch_type(info, "grad") + has_mag = _contains_ch_type(info, "mag") + has_eeg = _contains_ch_type(info, "eeg") all_ch_types = [] if has_grad: diff --git a/mne/report/tests/test_report.py b/mne/report/tests/test_report.py index 5fa47d63914..1c434305872 100644 --- a/mne/report/tests/test_report.py +++ b/mne/report/tests/test_report.py @@ -19,6 +19,7 @@ Epochs, create_info, pick_channels_cov, + pick_types_forward, read_cov, read_events, read_evokeds, @@ -28,6 +29,7 @@ from mne.epochs import make_metadata from mne.fixes import _reshape_view from mne.io import RawArray, read_info, read_raw_fif +from mne.forward import read_forward_solution, write_forward_solution from mne.preprocessing import ICA from mne.report import Report, _ReportScraper, open_report, report from mne.report import report as report_mod @@ -541,6 +543,44 @@ def test_add_forward(renderer_interactive_pyvistaqt): assert report.html[0].count(" Date: Mon, 6 Apr 2026 06:31:56 +0000 Subject: [PATCH 7/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mne/report/tests/test_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mne/report/tests/test_report.py b/mne/report/tests/test_report.py index 1c434305872..541351723c4 100644 --- a/mne/report/tests/test_report.py +++ b/mne/report/tests/test_report.py @@ -28,8 +28,8 @@ from mne.datasets import testing from mne.epochs import make_metadata from mne.fixes import _reshape_view -from mne.io import RawArray, read_info, read_raw_fif from mne.forward import read_forward_solution, write_forward_solution +from mne.io import RawArray, read_info, read_raw_fif from mne.preprocessing import ICA from mne.report import Report, _ReportScraper, open_report, report from mne.report import report as report_mod From 4c2c13c5e3216e6b7320830e785a7df7a06b1e68 Mon Sep 17 00:00:00 2001 From: 1himan Date: Mon, 6 Apr 2026 12:08:36 +0530 Subject: [PATCH 8/9] fix ci --- doc/changes/dev/13722.newfeature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changes/dev/13722.newfeature.rst b/doc/changes/dev/13722.newfeature.rst index 8f22cf809f1..abe50be324b 100644 --- a/doc/changes/dev/13722.newfeature.rst +++ b/doc/changes/dev/13722.newfeature.rst @@ -1 +1 @@ -Add ``sensitiviy`` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`_. +Add ``sensitiviy`` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`. From 577f3c44de7ccd1cad87b9a5a803d043c0bb2dcd Mon Sep 17 00:00:00 2001 From: 1himan Date: Mon, 6 Apr 2026 12:44:18 +0530 Subject: [PATCH 9/9] Fix CI --- doc/changes/dev/13722.newfeature.rst | 2 +- tutorials/intro/70_report.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/changes/dev/13722.newfeature.rst b/doc/changes/dev/13722.newfeature.rst index abe50be324b..8f22cf809f1 100644 --- a/doc/changes/dev/13722.newfeature.rst +++ b/doc/changes/dev/13722.newfeature.rst @@ -1 +1 @@ -Add ``sensitiviy`` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`. +Add ``sensitiviy`` kwarg in :meth:`mne.Report.add_forward` to get the sensitiviy map of the forward models, by `Himanshu Mahor`_. diff --git a/tutorials/intro/70_report.py b/tutorials/intro/70_report.py index 5bab60eabc4..b517c9dcffc 100644 --- a/tutorials/intro/70_report.py +++ b/tutorials/intro/70_report.py @@ -330,7 +330,7 @@ report.save("report_forward_sol.html", overwrite=True) # %% -# To ge the sensitivity maps of forward models you could use `sensitivity=True`. +# To ge the sensitivity maps of forward models you could use ``sensitivity=True``. report.add_forward( forward=fwd_path,