Skip to content

Commit 4351e61

Browse files
larsonerdrammock
andauthored
DOC: Document Forward and SourceSpaces (#10906)
* DOC: Document Forward and SourceSpaces * FIX: Flake * FIX: One more * FIX: Better msg * FIX: Flake * Apply suggestions from code review Co-authored-by: Daniel McCloy <dan@mccloy.info>
1 parent 6c43b2d commit 4351e61

File tree

4 files changed

+204
-12
lines changed

4 files changed

+204
-12
lines changed

doc/glossary.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ general neuroimaging concepts. If you think a term is missing, please consider
174174
conductivity model of the head, which encapsulates the geometries and
175175
electrical conductivities of the different tissue compartments (see
176176
:term:`boundary element model` and :class:`bem.ConductorModel`).
177+
For information about the Forward object and the data it stores, see
178+
:class:`mne.Forward`.
177179

178180
GFP
179181
global field power
@@ -362,11 +364,12 @@ general neuroimaging concepts. If you think a term is missing, please consider
362364
source space
363365
A source space specifies where in the brain source amplitudes are
364366
estimated. It corresponds to locations of a set of
365-
candidate :term:`equivalent current dipoles<ECD>`. MNE-Python mostly works
366-
with source spaces defined on the cortical surfaces estimated
367+
candidate :term:`equivalent current dipoles<ECD>`. MNE-Python mostly
368+
works with source spaces defined on the cortical surfaces estimated
367369
by FreeSurfer from a T1-weighted MRI image. See :ref:`tut-forward`
368370
to read about how to compute a forward operator in a source space.
369-
See :class:`SourceSpaces` for the class definition.
371+
See :class:`SourceSpaces` for the class definition and information
372+
about the data it contains.
370373

371374
stim channel
372375
trigger channel

mne/datasets/_fetch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def fetch_dataset(
185185
# return empty string if outdated dataset and we don't want to download
186186
if (not force_update) and outdated and not download:
187187
logger.info(
188-
'Dataset out of date, force_upload=False, and download=False, '
188+
'Dataset out of date but force_update=False and download=False, '
189189
'returning empty data_path')
190190
return (empty, data_version) if return_version else empty
191191

mne/forward/forward.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,77 @@
5454
class Forward(dict):
5555
"""Forward class to represent info from forward solution.
5656
57+
Like :class:`mne.Info`, this data structure behaves like a dictionary.
58+
It contains all metadata necessary for a forward solution.
59+
60+
.. warning::
61+
This class should not be modified or created by users.
62+
Forward objects should be obtained using
63+
:func:`mne.make_forward_solution` or :func:`mne.read_forward_solution`.
64+
5765
Attributes
5866
----------
5967
ch_names : list of str
60-
List of channels' names.
68+
A convenience wrapper accessible as ``fwd.ch_names`` which wraps
69+
``fwd['info']['ch_names']``.
6170
62-
.. versionadded:: 0.20.0
71+
See Also
72+
--------
73+
mne.make_forward_solution
74+
mne.read_forward_solution
75+
76+
Notes
77+
-----
78+
Forward data is accessible via string keys using standard
79+
:class:`python:dict` access (e.g., ``fwd['nsource'] == 4096``):
80+
81+
source_ori : int
82+
The source orientation, either ``FIFF.FIFFV_MNE_FIXED_ORI`` or
83+
``FIFF.FIFFV_MNE_FREE_ORI``.
84+
coord_frame : int
85+
The coordinate frame of the forward solution, usually
86+
``FIFF.FIFFV_COORD_HEAD``.
87+
nsource : int
88+
The number of source locations.
89+
nchan : int
90+
The number of channels.
91+
sol : dict
92+
The forward solution, with entries:
93+
94+
``'data'`` : ndarray, shape (n_channels, nsource * n_ori)
95+
The forward solution data. The shape will be
96+
``(n_channels, nsource)`` for a fixed-orientation forward and
97+
``(n_channels, nsource * 3)`` for a free-orientation forward.
98+
``'row_names'`` : list of str
99+
The channel names.
100+
mri_head_t : instance of Transform
101+
The mri ↔ head transformation that was used.
102+
info : instance of :class:`~mne.Info`
103+
The measurement information (with contents reduced compared to that
104+
of the original data).
105+
src : instance of :class:`~mne.SourceSpaces`
106+
The source space used during forward computation. This can differ
107+
from the original source space as:
108+
109+
1. Source points are removed due to proximity to (or existing
110+
outside)
111+
the inner skull surface.
112+
2. The source space will be converted to the ``coord_frame`` of the
113+
forward solution, which typically means it gets converted from
114+
MRI to head coordinates.
115+
source_rr : ndarray, shape (n_sources, 3)
116+
The source locations.
117+
source_nn : ndarray, shape (n_sources, 3)
118+
The source normals. Will be all +Z (``(0, 0, 1.)``) for volume
119+
source spaces. For surface source spaces, these are normal to the
120+
cortical surface.
121+
surf_ori : int
122+
Whether ``sol`` is surface-oriented with the surface normal in the
123+
Z component (``FIFF.FIFFV_MNE_FIXED_ORI``) or +Z in the given
124+
``coord_frame`` in the Z component (``FIFF.FIFFV_MNE_FREE_ORI``).
125+
126+
Forward objects also have some attributes that are accessible via ``.``
127+
access, like ``fwd.ch_names``.
63128
"""
64129

65130
def copy(self):

mne/source_space.py

Lines changed: 130 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,14 @@
5555
class SourceSpaces(list):
5656
"""Represent a list of source space.
5757
58-
Currently implemented as a list of dictionaries containing the source
59-
space information
58+
This class acts like a list of dictionaries containing the source
59+
space information, one entry in the list per source space type. See
60+
Notes for details.
61+
62+
.. warning::
63+
This class should not be created or modified by the end user. Use
64+
:func:`mne.setup_source_space`, :func:`mne.setup_volume_source_space`,
65+
or :func:`mne.read_source_spaces` to create :class:`SourceSpaces`.
6066
6167
Parameters
6268
----------
@@ -68,9 +74,126 @@ class SourceSpaces(list):
6874
6975
Attributes
7076
----------
77+
kind : str
78+
The kind of source space, one of
79+
``{'surface', 'volume', 'discrete', 'mixed'}``.
7180
info : dict
7281
Dictionary with information about the creation of the source space
7382
file. Has keys 'working_dir' and 'command_line'.
83+
84+
See Also
85+
--------
86+
mne.setup_source_space : Setup a surface source space.
87+
mne.setup_volume_source_space : Setup a volume source space.
88+
mne.read_source_spaces : Read source spaces from a file.
89+
90+
Notes
91+
-----
92+
Each element in SourceSpaces (e.g., ``src[0]``) is a dictionary. For
93+
example, a surface source space will have ``len(src) == 2``, one entry for
94+
each hemisphere. A volume source space will have ``len(src) == 1`` if it
95+
uses a single monolithic grid, or ``len(src) == len(volume_label)`` when
96+
created with a list-of-atlas-labels. A mixed source space consists of both
97+
surface and volumetric source spaces in a single SourceSpaces object.
98+
99+
Each of those dictionaries can be accessed using standard Python
100+
:class:`python:dict` access using the string keys listed below (e.g.,
101+
``src[0]['type'] == 'surf'``). The relevant key/value pairs depend on
102+
the source space type:
103+
104+
**Relevant to all source spaces**
105+
106+
The following are always present:
107+
108+
id : int
109+
The FIF ID, either ``FIFF.FIFFV_MNE_SURF_LEFT_HEMI`` or
110+
``FIFF.FIFFV_MNE_SURF_RIGHT_HEMI`` for surfaces, or
111+
``FIFF.FIFFV_MNE_SURF_UNKNOWN`` for volume source spaces.
112+
type : str
113+
The type of source space, one of ``{'surf', 'vol', 'discrete'}``.
114+
np : int
115+
Number of vertices in the dense surface or complete volume.
116+
coord_frame : int
117+
The coordinate frame, usually ``FIFF.FIFFV_COORD_MRI``.
118+
rr : ndarray, shape (np, 3)
119+
The dense surface or complete volume vertex locations.
120+
nn : ndarray, shape (np, 3)
121+
The dense surface or complete volume normals.
122+
nuse : int
123+
The number of points in the subsampled surface.
124+
inuse : ndarray, shape (np,)
125+
An integer array defining whether each dense surface vertex is used
126+
(``1``) or unused (``0``).
127+
vertno : ndarray, shape (n_src,)
128+
The vertex numbers of the dense surface or complete volume that are
129+
used (i.e., ``np.where(src[0]['inuse'])[0]``).
130+
subject_his_id : str
131+
The FreeSurfer subject name.
132+
133+
**Surface source spaces**
134+
135+
Surface source spaces created using :func:`mne.setup_source_space` can have
136+
the following additional entries (which will be missing, or have values of
137+
``None`` or ``0`` for volumetric source spaces):
138+
139+
ntri : int
140+
Number of triangles in the dense surface triangulation.
141+
tris : ndarray, shape (ntri, 3)
142+
The dense surface triangulation.
143+
nuse_tri : int
144+
The number of triangles in the subsampled surface.
145+
use_tris : ndarray, shape (nuse_tri, 3)
146+
The subsampled surface triangulation.
147+
dist : scipy.sparse.csr_matrix, shape (n_src, n_src) | None
148+
The distances (euclidean for volume, along the cortical surface for
149+
surfaces) between source points.
150+
dist_limit : float
151+
The maximum distance allowed for inclusion in ``nearest``.
152+
pinfo : dict
153+
Information about the patch of cortex represented by a vertex in
154+
the subsampled surface.
155+
patch_inds : list of ndarray
156+
For each vertex in the subsampled surface, the indices of the
157+
vertices in the dense surface that it represents (i.e., is closest
158+
to of all subsampled indices).
159+
nearest : ndarray, shape (np,)
160+
For each vertex on the dense surface, this gives the vertex index
161+
on the subsampled surface that it's closest to.
162+
nearest_dist : ndarray, shape (np,)
163+
The distances corresponding to ``nearest``.
164+
165+
**Volume source spaces**
166+
167+
Volume source spaces created using :func:`mne.setup_volume_source_space`
168+
can have the following additional entries (which will be missing, or
169+
have values of ``None`` or ``0`` for surface source spaces):
170+
171+
mri_width, mri_height, mri_depth : int
172+
The MRI dimensions (in voxels).
173+
neighbor_vert : ndarray
174+
The 26-neighborhood information for each vertex.
175+
interpolator : scipy.sparse.csr_matrix | None
176+
The linear interpolator to go from the subsampled volume vertices
177+
to the high-resolution volume.
178+
shape : tuple of int
179+
The shape of the subsampled grid.
180+
mri_ras_t : instance of :class:`~mne.transforms.Transform`
181+
The transformation from MRI surface RAS (``FIFF.FIFFV_COORD_MRI``)
182+
to MRI scanner RAS (``FIFF.FIFFV_MNE_COORD_RAS``).
183+
src_mri_t : instance of :class:`~mne.transforms.Transform`
184+
The transformation from subsampled source space voxel to MRI
185+
surface RAS.
186+
vox_mri_t : instance of :class:`~mne.transforms.Transform`
187+
The transformation from the original MRI voxel
188+
(``FIFF.FIFFV_MNE_COORD_MRI_VOXEL``) space to MRI surface RAS.
189+
mri_volume_name : str
190+
The MRI volume name, e.g. ``'subjects_dir/subject/mri/T1.mgz'``.
191+
seg_name : str
192+
The MRI atlas segmentation name (e.g., ``'Left-Cerebellum-Cortex'``
193+
from the parameter ``volume_label``).
194+
195+
Source spaces also have some attributes that are accessible via ``.``
196+
access, like ``src.kind``.
74197
"""
75198

76199
def __init__(self, source_spaces, info=None): # noqa: D102
@@ -709,9 +832,9 @@ def _read_one_source_space(fid, this):
709832
tag = read_tag(fid, d.pos)
710833
trans = tag.data
711834
if trans['from'] == FIFF.FIFFV_MNE_COORD_MRI_VOXEL:
712-
res['vox_mri_t'] = tag.data
835+
res['vox_mri_t'] = trans
713836
if trans['to'] == FIFF.FIFFV_MNE_COORD_RAS:
714-
res['mri_ras_t'] = tag.data
837+
res['mri_ras_t'] = trans
715838

716839
tag = find_tag(fid, mri, FIFF.FIFF_MNE_SOURCE_SPACE_INTERPOLATOR)
717840
if tag is not None:
@@ -1669,7 +1792,7 @@ def _make_discrete_source_space(pos, coord_frame='mri'):
16691792
# Ready to make the source space
16701793
sp = dict(coord_frame=coord_frame, type='discrete', nuse=npts, np=npts,
16711794
inuse=np.ones(npts, int), vertno=np.arange(npts), rr=rr, nn=nn,
1672-
id=-1)
1795+
id=FIFF.FIFFV_MNE_SURF_UNKNOWN)
16731796
return sp
16741797

16751798

@@ -1721,7 +1844,8 @@ def _make_volume_source_space(surf, grid, exclude, mindist, mri=None,
17211844
rr = np.array([x * grid, y * grid, z * grid]).T
17221845
sp = dict(np=npts, nn=np.zeros((npts, 3)), rr=rr,
17231846
inuse=np.ones(npts, bool), type='vol', nuse=npts,
1724-
coord_frame=FIFF.FIFFV_COORD_MRI, id=-1, shape=ns)
1847+
coord_frame=FIFF.FIFFV_COORD_MRI, id=FIFF.FIFFV_MNE_SURF_UNKNOWN,
1848+
shape=ns)
17251849
sp['nn'][:, 2] = 1.0
17261850
assert sp['rr'].shape[0] == npts
17271851

0 commit comments

Comments
 (0)