| Title: | Realistic EEG Head Models and Forward Solvers for the Physio Ecosystem |
|---|---|
| Description: | Provides EEG forward-model building blocks for the Physio ecosystem: an analytic multi-concentric-sphere leadfield, a boundary element method (BEM) solver with the double-layer operator for custom head meshes, cortical source-space helpers with surface normals, and on-demand fetchers for large published head models (the New York Head and FreeSurfer fsaverage) that are downloaded to a per-user cache rather than shipped. |
| Authors: | Yusuke Matsui [aut, cre] |
| Maintainer: | Yusuke Matsui <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.1 |
| Built: | 2026-10-03 13:41:38 UTC |
| Source: | https://github.com/x-biosignal/PhysioHeadModels |
Solves the piecewise-homogeneous EEG forward problem with the classic collocation double-layer boundary element method (Geselowitz boundary integral, van Oosterom-Strackee solid angles, average-reference deflation) on one or more nested triangulated surfaces. Validated against the analytic single-sphere solution: on a sphere mesh the leadfield topography matches to a small relative difference measure (RDM); absolute amplitude carries the usual coarse-collocation bias, so use RDM (topography) for validation and note that source localization is invariant to a global leadfield scale.
bemLeadfield(surfaces, conductivities, electrodes, sources)bemLeadfield(surfaces, conductivities, electrodes, sources)
surfaces |
A single mesh (list with |
conductivities |
Conductivity of each region from innermost to the
exterior; length |
electrodes |
An n_electrodes x 3 matrix of electrode positions. |
sources |
An n_sources x 3 matrix of dipole positions (in the innermost region). |
An n_electrodes x (n_sources * 3) leadfield matrix.
Geselowitz, D. B. (1967). On bioelectric potentials in an inhomogeneous volume conductor. Biophysical Journal, 7(1), 1-11.
Oostendorp, T. F., & van Oosterom, A. (1989). Source parameter estimation in inhomogeneous volume conductors of arbitrary shape. IEEE Transactions on Biomedical Engineering, 36(3), 382-391.
sphereLeadfield(), solidAngle()
m <- icosphere(2) e <- m$vertices[1:19, ] s <- rbind(c(0, 0, 0.5), c(0.3, 0, 0.2)) dim(bemLeadfield(m, c(0.33, 0), e, s))m <- icosphere(2) e <- m$vertices[1:19, ] s <- rbind(c(0, 0, 0.5), c(0.3, 0, 0.2)) dim(bemLeadfield(m, c(0.33, 0), e, s))
Collapses the three orientation columns of each source to a single surface-normal-oriented column, giving an n_electrodes x n_sources leadfield.
constrainOrientation(leadfield, normals)constrainOrientation(leadfield, normals)
leadfield |
An n_electrodes x (n_sources * 3) free-orientation leadfield. |
normals |
An n_sources x 3 matrix of unit source normals. |
An n_electrodes x n_sources orientation-constrained leadfield.
ss <- sphericalSourceSpace(40, radius = 0.6) e <- icosphere(2)$vertices[1:19, ] lf <- sphereLeadfield(e, ss$positions) lc <- constrainOrientation(lf, ss$normals) dim(lc)ss <- sphericalSourceSpace(40, radius = 0.6) e <- icosphere(2)$vertices[1:19, ] lf <- sphereLeadfield(e, ss$positions) lc <- constrainOrientation(lf, ss$normals) dim(lc)
A thin placeholder fetcher for the FreeSurfer / MNE fsaverage template
surfaces. Because fsaverage is distributed through FreeSurfer / MNE
(with its own licence and download mechanism), this returns the cache
directory into which the user should place (or a future version will fetch)
the surfaces, and errors informatively if they are absent.
fetchFsaverage(dest = NULL)fetchFsaverage(dest = NULL)
dest |
Optional destination directory (defaults to the cache). |
The path to the fsaverage directory in the cache.
# With the surfaces already in place the fetcher just resolves their path. # Here a temporary stand-in directory shows that resolution offline (a real # run needs the FreeSurfer/MNE fsaverage surfaces placed in the directory). dir <- file.path(tempdir(), "fsaverage") dir.create(dir, showWarnings = FALSE) fetchFsaverage(dest = dir)# With the surfaces already in place the fetcher just resolves their path. # Here a temporary stand-in directory shows that resolution offline (a real # run needs the FreeSurfer/MNE fsaverage surfaces placed in the directory). dir <- file.path(tempdir(), "fsaverage") dir.create(dir, showWarnings = FALSE) fetchFsaverage(dest = dir)
Downloads the New York Head leadfield (sa_nyhead.mat, about 678 MB,
Huang, Parra & Haufe 2016) to the per-user cache the first time it is needed.
The file is a MATLAB v7.3 (HDF5) file and requires the hdf5r package to
read; it is deliberately not shipped with this package.
fetchNYHead(dest = NULL, quiet = FALSE, timeout = 3600)fetchNYHead(dest = NULL, quiet = FALSE, timeout = 3600)
dest |
Optional destination path (defaults to the cache). |
quiet |
Suppress the download progress bar (default: |
timeout |
Download timeout in seconds (default: 3600). |
The path to the downloaded .mat file.
Huang, Y., Parra, L. C., & Haufe, S. (2016). The New York Head - a precise standardized volume conductor model for EEG source localization and tES targeting. NeuroImage, 140, 150-162.
nyHeadLeadfield(), fetchFsaverage()
## Not run: path <- fetchNYHead() ## End(Not run)## Not run: path <- fetchNYHead() ## End(Not run)
Cache directory for head-model downloads
headModelCache()headModelCache()
The per-user cache path (created if needed), from
tools::R_user_dir().
# The cache normally lives under tools::R_user_dir(); redirect it to a # temporary directory here so the example leaves nothing behind. old <- Sys.getenv("R_USER_CACHE_DIR", unset = NA) Sys.setenv(R_USER_CACHE_DIR = tempdir()) headModelCache() if (is.na(old)) Sys.unsetenv("R_USER_CACHE_DIR") else Sys.setenv(R_USER_CACHE_DIR = old)# The cache normally lives under tools::R_user_dir(); redirect it to a # temporary directory here so the example leaves nothing behind. old <- Sys.getenv("R_USER_CACHE_DIR", unset = NA) Sys.setenv(R_USER_CACHE_DIR = tempdir()) headModelCache() if (is.na(old)) Sys.unsetenv("R_USER_CACHE_DIR") else Sys.setenv(R_USER_CACHE_DIR = old)
Builds a geodesic sphere mesh by recursively subdividing an icosahedron, giving nearly uniform triangles - a convenient scalp/skull/brain surface for the BEM solver or for placing electrodes.
icosphere(subdivisions = 2L, radius = 1)icosphere(subdivisions = 2L, radius = 1)
subdivisions |
Number of subdivision passes (default: 2; 0 gives the 20-face icosahedron, each pass multiplies faces by 4). |
radius |
Sphere radius (default: 1). |
A list with vertices (n x 3) and faces (m x 3 integer
vertex indices, outward winding).
m <- icosphere(2) nrow(m$faces)m <- icosphere(2) nrow(m$faces)
Standard BEM validation metrics on average-referenced potentials: RDM (topography error, 0 = identical) and MAG (amplitude ratio, 1 = identical).
leadfieldRDM(test, reference)leadfieldRDM(test, reference)
test, reference
|
Leadfield matrices of the same shape. |
A named numeric vector c(RDM, MAG) (means over columns).
e <- icosphere(2)$vertices[1:19, ] s <- rbind(c(0, 0, 0.5), c(0.3, 0, 0.2)) lf <- sphereLeadfield(e, s) # A leadfield compared with itself is identical: RDM 0, MAG 1. leadfieldRDM(lf, lf)e <- icosphere(2)$vertices[1:19, ] s <- rbind(c(0, 0, 0.5), c(0.3, 0, 0.2)) lf <- sphereLeadfield(e, s) # A leadfield compared with itself is identical: RDM 0, MAG 1. leadfieldRDM(lf, lf)
Reads the New York Head leadfield (fetched by fetchNYHead()) and extracts
the sub-leadfield for a set of electrode labels, in the
n_electrodes x (n_sources * 3) convention used across the ecosystem. Requires
the hdf5r package and the downloaded .mat; both are optional, so
this errors informatively when either is missing.
nyHeadLeadfield(montage_labels, path = NULL, normal_only = FALSE)nyHeadLeadfield(montage_labels, path = NULL, normal_only = FALSE)
montage_labels |
Character vector of electrode labels to extract. |
path |
Path to |
normal_only |
Return the surface-normal-constrained leadfield
(n_electrodes x n_sources) instead of the free-orientation one
(default: |
A list with leadfield, source_positions,
source_normals, electrode_labels, and electrode_index.
## Not run: lf <- nyHeadLeadfield(c("C3", "C4", "Cz")) dim(lf$leadfield) ## End(Not run)## Not run: lf <- nyHeadLeadfield(c("C3", "C4", "Cz")) dim(lf$leadfield) ## End(Not run)
Solid angle subtended by a triangle at a point (van Oosterom-Strackee)
solidAngle(v1, v2, v3, r)solidAngle(v1, v2, v3, r)
v1, v2, v3
|
Numeric length-3 triangle vertices. |
r |
Numeric length-3 observation point. |
The signed solid angle in steradians.
# One face of the unit octahedron, seen from the centre, spans an eighth # of the full sphere: pi / 2 steradians. solidAngle(c(1, 0, 0), c(0, 1, 0), c(0, 0, 1), c(0, 0, 0))# One face of the unit octahedron, seen from the centre, spans an eighth # of the full sphere: pi / 2 steradians. solidAngle(c(1, 0, 0), c(0, 1, 0), c(0, 0, 1), c(0, 0, 0))
Computes the leadfield for a homogeneous conducting sphere with an insulating outer boundary, using the Legendre-series solution (the classic analytic forward model, e.g. FieldTrip's single sphere). Each source contributes three contiguous columns (x, y, z unit dipoles).
sphereLeadfield(electrodes, sources, radius = 1, sigma = 0.33, n_terms = 60L)sphereLeadfield(electrodes, sources, radius = 1, sigma = 0.33, n_terms = 60L)
electrodes |
An n_electrodes x 3 matrix of electrode positions (on the
sphere of radius |
sources |
An n_sources x 3 matrix of dipole positions (inside the sphere). |
radius |
Sphere radius (default: 1). |
sigma |
Conductivity in S/m (default: 0.33). |
n_terms |
Legendre series truncation (default: 60). |
An n_electrodes x (n_sources * 3) leadfield matrix.
e <- icosphere(2)$vertices[1:19, ] s <- rbind(c(0, 0, 0.5), c(0.4, 0, 0.2)) dim(sphereLeadfield(e, s))e <- icosphere(2)$vertices[1:19, ] s <- rbind(c(0, 0, 0.5), c(0.4, 0, 0.2)) dim(sphereLeadfield(e, s))
Places dipole sources on a sphere (a spherical-cortex approximation), returning vertex positions and outward surface normals - the structured source space a realistic forward model uses in place of a random source cloud.
sphericalSourceSpace(n_sources = 400L, radius = 0.7)sphericalSourceSpace(n_sources = 400L, radius = 0.7)
n_sources |
Approximate number of sources (default: 400). |
radius |
Source-shell radius (default: 0.7). |
A list with positions (n x 3) and normals (n x 3, unit
radial).
ss <- sphericalSourceSpace(200) nrow(ss$positions)ss <- sphericalSourceSpace(200) nrow(ss$positions)