""" .. _tut-forward: ================================== Head model and forward computation ================================== The aim of this tutorial is to be a getting started for forward computation. For more extensive details and presentation of the general concepts for forward modeling, see :ref:`ch_forward`. """ # Authors: The MNE-Python contributors. # License: BSD-3-Clause # Copyright the MNE-Python contributors. # %% import mne from mne.datasets import sample data_path = sample.data_path() # the raw file containing the channel location + types sample_dir = data_path / "MEG" / "sample" raw_fname = sample_dir / "sample_audvis_raw.fif" # The paths to Freesurfer reconstructions subjects_dir = data_path / "subjects" subject = "sample" # %% # Computing the forward operator # ------------------------------ # # To compute a forward operator we need: # # - a ``-trans.fif`` file that contains the coregistration info. # - a source space # - the :term:`BEM` surfaces # %% # Compute and visualize BEM surfaces # ---------------------------------- # # The :term:`BEM` surfaces are the triangulations of the interfaces between # different tissues needed for forward computation. These surfaces are for # example the inner skull surface, the outer skull surface and the outer skin # surface, a.k.a. scalp surface. # # Computing the BEM surfaces requires FreeSurfer and makes use of # the command-line tools :ref:`mne watershed_bem` or :ref:`mne flash_bem`, or # the related functions :func:`mne.bem.make_watershed_bem` or # :func:`mne.bem.make_flash_bem`. # # Here we'll assume it's already computed. It takes a few minutes per subject. # # For EEG we use 3 layers (inner skull, outer skull, and skin) while for # MEG 1 layer (inner skull) is enough. # # Let's look at these surfaces. The function :func:`mne.viz.plot_bem` # assumes that you have the ``bem`` folder of your subject's FreeSurfer # reconstruction, containing the necessary surface files. Here we use a smaller # than default subset of ``slices`` for speed. plot_bem_kwargs = dict( subject=subject, subjects_dir=subjects_dir, brain_surfaces="white", orientation="coronal", slices=[50, 100, 150, 200], ) mne.viz.plot_bem(**plot_bem_kwargs) # %% # Visualizing the coregistration # ------------------------------ # # The coregistration is the operation that allows to position the head and the # sensors in a common coordinate system. In the MNE software the transformation # to align the head and the sensors in stored in a so-called **trans file**. # It is a FIF file that ends with ``-trans.fif``. It can be obtained with # :func:`mne.gui.coregistration` (or its convenient command line # equivalent :ref:`mne coreg`), or mrilab if you're using a Neuromag # system. # # Here we assume the coregistration is done, so we just visually check the # alignment with the following code. See :ref:`creating-trans` for instructions # on creating the ``-trans.fif`` file interactively. # The transformation file obtained by coregistration trans = sample_dir / "sample_audvis_raw-trans.fif" info = mne.io.read_info(raw_fname) # Here we look at the dense head, which isn't used for BEM computations but # is useful for coregistration. mne.viz.plot_alignment( info, trans, subject=subject, dig=True, meg=["helmet", "sensors"], subjects_dir=subjects_dir, surfaces="head-dense", ) # %% # .. _plot_forward_source_space: # # Compute Source Space # -------------------- # # The source space defines the position and orientation of the candidate source # locations. There are two types of source spaces: # # - **surface-based** source space when the candidates are confined to a # surface. # # - **volumetric or discrete** source space when the candidates are discrete, # arbitrarily located source points bounded by the surface. # # **Surface-based** source space is computed using # :func:`mne.setup_source_space`, while **volumetric** source space is computed # using :func:`mne.setup_volume_source_space`. # # We will now compute a surface-based source space with an ``'oct4'`` # resolution. See :ref:`setting_up_source_space` for details on source space # definition and spacing parameter. # # .. warning:: # ``'oct4'`` is used here just for speed, for real analyses the recommended # spacing is ``'oct6'``. src = mne.setup_source_space( subject, spacing="oct4", add_dist="patch", subjects_dir=subjects_dir ) print(src) # %% # The surface based source space ``src`` contains two parts, one for the left # hemisphere (258 locations) and one for the right hemisphere (258 # locations). Sources can be visualized on top of the BEM surfaces in purple. mne.viz.plot_bem(src=src, **plot_bem_kwargs) # %% # To compute a volume based source space defined with a grid of candidate # dipoles inside a sphere of radius 90mm centered at (0.0, 0.0, 40.0) mm # you can use the following code. # Obviously here, the sphere is not perfect. It is not restricted to the # brain and it can miss some parts of the cortex. sphere = (0.0, 0.0, 0.04, 0.09) vol_src = mne.setup_volume_source_space( subject, subjects_dir=subjects_dir, sphere=sphere, sphere_units="m", add_interpolator=False, ) # just for speed! print(vol_src) mne.viz.plot_bem(src=vol_src, **plot_bem_kwargs) # %% # To compute a volume based source space defined with a grid of candidate # dipoles inside the brain (requires the :term:`BEM` surfaces) you can use the # following. surface = subjects_dir / subject / "bem" / "inner_skull.surf" vol_src = mne.setup_volume_source_space( subject, subjects_dir=subjects_dir, surface=surface, add_interpolator=False ) # Just for speed! print(vol_src) mne.viz.plot_bem(src=vol_src, **plot_bem_kwargs) # %% # .. note:: Some sources may appear to be outside the BEM inner skull contour. # This is because the ``slices`` are decimated for plotting here. # Each slice in the figure actually represents several MRI slices, # but only the MRI voxels and BEM boundaries for a single (midpoint # of the given slice range) slice are shown, whereas the source space # points plotted on that midpoint slice consist of all points # for which that slice (out of all slices shown) was the closest. # # Now let's see how to view all sources in 3D. fig = mne.viz.plot_alignment( subject=subject, subjects_dir=subjects_dir, surfaces="white", coord_frame="mri", src=src, ) mne.viz.set_3d_view( fig, azimuth=173.78, elevation=101.75, distance=0.30, focalpoint=(-0.03, -0.01, 0.03), ) # %% # .. _plot_forward_compute_forward_solution: # # Compute forward solution # ------------------------ # # We can now compute the forward solution. # To reduce computation we'll just compute a single layer BEM (just inner # skull) that can then be used for MEG (not EEG). # We specify if we want a one-layer or a three-layer BEM using the # ``conductivity`` parameter. # The BEM solution requires a BEM model which describes the geometry # of the head the conductivities of the different tissues. conductivity = (0.3,) # for single layer # conductivity = (0.3, 0.006, 0.3) # for three layers model = mne.make_bem_model( subject="sample", ico=4, conductivity=conductivity, subjects_dir=subjects_dir ) bem = mne.make_bem_solution(model) # %% # Note that the :term:`BEM` does not involve any use of the trans file. The BEM # only depends on the head geometry and conductivities. # It is therefore independent from the MEG data and the head position. # # Let's now compute the forward operator, commonly referred to as the # gain or leadfield matrix. # See :func:`mne.make_forward_solution` for details on the meaning of each # parameter. fwd = mne.make_forward_solution( raw_fname, trans=trans, src=src, bem=bem, meg=True, eeg=False, mindist=5.0, n_jobs=None, verbose=True, ) print(fwd) # %% # .. warning:: # Forward computation can remove vertices that are too close to (or outside) # the inner skull surface. For example, here we have gone from 516 to 474 # vertices in use. For many functions, such as # :func:`mne.compute_source_morph`, it is important to pass ``fwd['src']`` # or ``inv['src']`` so that this removal is adequately accounted for. print(f"Before: {src}") print(f'After: {fwd["src"]}') # %% # We can explore the content of ``fwd`` to access the numpy array that contains # the gain matrix. leadfield = fwd["sol"]["data"] print(f"Leadfield size : {leadfield.shape[0]} sensors x {leadfield.shape[1]} dipoles") # %% # To extract the numpy array containing the forward operator corresponding to # the source space ``fwd['src']`` with cortical orientation constraint # we can use the following: fwd_fixed = mne.convert_forward_solution( fwd, surf_ori=True, force_fixed=True, use_cps=True ) leadfield = fwd_fixed["sol"]["data"] print(f"Leadfield size : {leadfield.shape[0]} sensors x {leadfield.shape[1]} dipoles") # %% # This is equivalent to the following code that explicitly applies the # forward operator to a source estimate composed of the identity operator # (which we omit here because it uses a lot of memory):: # # >>> import numpy as np # >>> n_dipoles = leadfield.shape[1] # >>> vertices = [src_hemi['vertno'] for src_hemi in fwd_fixed['src']] # >>> stc = mne.SourceEstimate(1e-9 * np.eye(n_dipoles), vertices) # >>> leadfield = mne.apply_forward(fwd_fixed, stc, info).data / 1e-9 # # To save to disk a forward solution you can use # :func:`mne.write_forward_solution` and to read it back from disk # :func:`mne.read_forward_solution`. Don't forget that FIF files containing # forward solution should end with :file:`-fwd.fif`. # # To get a fixed-orientation forward solution, use # :func:`mne.convert_forward_solution` to convert the free-orientation # solution to (surface-oriented) fixed orientation. # # Exercise # -------- # # By looking at :ref:`ex-sensitivity-maps` # plot the sensitivity maps for EEG and compare it with the MEG, can you # justify the claims that: # # - MEG is not sensitive to radial sources # - EEG is more sensitive to deep sources # # How will the MEG sensitivity maps and histograms change if you use a free # instead if a fixed/surface oriented orientation? # # Try this changing the mode parameter in :func:`mne.sensitivity_map` # accordingly. Why don't we see any dipoles on the gyri?