initial commit
This commit is contained in:
610
mne/forward/_lead_dots.py
Normal file
610
mne/forward/_lead_dots.py
Normal file
@@ -0,0 +1,610 @@
|
||||
# Authors: The MNE-Python contributors.
|
||||
# License: BSD-3-Clause
|
||||
# Copyright the MNE-Python contributors.
|
||||
|
||||
# The computations in this code were primarily derived from Matti Hämäläinen's
|
||||
# C code.
|
||||
|
||||
import os
|
||||
import os.path as op
|
||||
|
||||
import numpy as np
|
||||
from numpy.polynomial import legendre
|
||||
|
||||
from ..parallel import parallel_func
|
||||
from ..utils import _get_extra_data_path, fill_doc, logger, verbose
|
||||
|
||||
##############################################################################
|
||||
# FAST LEGENDRE (DERIVATIVE) POLYNOMIALS USING LOOKUP TABLE
|
||||
|
||||
|
||||
def _next_legen_der(n, x, p0, p01, p0d, p0dd):
|
||||
"""Compute the next Legendre polynomial and its derivatives."""
|
||||
# only good for n > 1 !
|
||||
old_p0 = p0
|
||||
old_p0d = p0d
|
||||
p0 = ((2 * n - 1) * x * old_p0 - (n - 1) * p01) / n
|
||||
p0d = n * old_p0 + x * old_p0d
|
||||
p0dd = (n + 1) * old_p0d + x * p0dd
|
||||
return p0, p0d, p0dd
|
||||
|
||||
|
||||
def _get_legen(x, n_coeff=100):
|
||||
"""Get Legendre polynomials expanded about x."""
|
||||
return legendre.legvander(x, n_coeff - 1)
|
||||
|
||||
|
||||
def _get_legen_der(xx, n_coeff=100):
|
||||
"""Get Legendre polynomial derivatives expanded about x."""
|
||||
coeffs = np.empty((len(xx), n_coeff, 3))
|
||||
for c, x in zip(coeffs, xx):
|
||||
p0s, p0ds, p0dds = c[:, 0], c[:, 1], c[:, 2]
|
||||
p0s[:2] = [1.0, x]
|
||||
p0ds[:2] = [0.0, 1.0]
|
||||
p0dds[:2] = [0.0, 0.0]
|
||||
for n in range(2, n_coeff):
|
||||
p0s[n], p0ds[n], p0dds[n] = _next_legen_der(
|
||||
n, x, p0s[n - 1], p0s[n - 2], p0ds[n - 1], p0dds[n - 1]
|
||||
)
|
||||
return coeffs
|
||||
|
||||
|
||||
@verbose
|
||||
def _get_legen_table(
|
||||
ch_type,
|
||||
volume_integral=False,
|
||||
n_coeff=100,
|
||||
n_interp=20000,
|
||||
force_calc=False,
|
||||
verbose=None,
|
||||
):
|
||||
"""Return a (generated) LUT of Legendre (derivative) polynomial coeffs."""
|
||||
if n_interp % 2 != 0:
|
||||
raise RuntimeError("n_interp must be even")
|
||||
fname = op.join(_get_extra_data_path(), "tables")
|
||||
if not op.isdir(fname):
|
||||
# Updated due to API change (GH 1167)
|
||||
os.makedirs(fname)
|
||||
if ch_type == "meg":
|
||||
fname = op.join(fname, f"legder_{n_coeff}_{n_interp}.bin")
|
||||
leg_fun = _get_legen_der
|
||||
extra_str = " derivative"
|
||||
lut_shape = (n_interp + 1, n_coeff, 3)
|
||||
else: # 'eeg'
|
||||
fname = op.join(fname, f"legval_{n_coeff}_{n_interp}.bin")
|
||||
leg_fun = _get_legen
|
||||
extra_str = ""
|
||||
lut_shape = (n_interp + 1, n_coeff)
|
||||
if not op.isfile(fname) or force_calc:
|
||||
logger.info(f"Generating Legendre{extra_str} table...")
|
||||
x_interp = np.linspace(-1, 1, n_interp + 1)
|
||||
lut = leg_fun(x_interp, n_coeff).astype(np.float32)
|
||||
if not force_calc:
|
||||
with open(fname, "wb") as fid:
|
||||
fid.write(lut.tobytes())
|
||||
else:
|
||||
logger.info(f"Reading Legendre{extra_str} table...")
|
||||
with open(fname, "rb", buffering=0) as fid:
|
||||
lut = np.fromfile(fid, np.float32)
|
||||
lut.shape = lut_shape
|
||||
|
||||
# we need this for the integration step
|
||||
n_fact = np.arange(1, n_coeff, dtype=float)
|
||||
if ch_type == "meg":
|
||||
n_facts = list() # multn, then mult, then multn * (n + 1)
|
||||
if volume_integral:
|
||||
n_facts.append(n_fact / ((2.0 * n_fact + 1.0) * (2.0 * n_fact + 3.0)))
|
||||
else:
|
||||
n_facts.append(n_fact / (2.0 * n_fact + 1.0))
|
||||
n_facts.append(n_facts[0] / (n_fact + 1.0))
|
||||
n_facts.append(n_facts[0] * (n_fact + 1.0))
|
||||
# skip the first set of coefficients because they are not used
|
||||
lut = lut[:, 1:, [0, 1, 1, 2]] # for multiplicative convenience later
|
||||
# reshape this for convenience, too
|
||||
n_facts = np.array(n_facts)[[2, 0, 1, 1], :].T
|
||||
n_facts = np.ascontiguousarray(n_facts)
|
||||
n_fact = n_facts
|
||||
else: # 'eeg'
|
||||
n_fact = (2.0 * n_fact + 1.0) * (2.0 * n_fact + 1.0) / n_fact
|
||||
# skip the first set of coefficients because they are not used
|
||||
lut = lut[:, 1:].copy()
|
||||
return lut, n_fact
|
||||
|
||||
|
||||
def _comp_sum_eeg(beta, ctheta, lut_fun, n_fact):
|
||||
"""Lead field dot products using Legendre polynomial (P_n) series."""
|
||||
# Compute the sum occurring in the evaluation.
|
||||
# The result is
|
||||
# sums[:] (2n+1)^2/n beta^n P_n
|
||||
n_chunk = 50000000 // (8 * max(n_fact.shape) * 2)
|
||||
lims = np.concatenate([np.arange(0, beta.size, n_chunk), [beta.size]])
|
||||
s0 = np.empty(beta.shape)
|
||||
for start, stop in zip(lims[:-1], lims[1:]):
|
||||
coeffs = lut_fun(ctheta[start:stop])
|
||||
betans = np.tile(beta[start:stop][:, np.newaxis], (1, n_fact.shape[0]))
|
||||
np.cumprod(betans, axis=1, out=betans) # run inplace
|
||||
coeffs *= betans
|
||||
s0[start:stop] = np.dot(coeffs, n_fact) # == weighted sum across cols
|
||||
return s0
|
||||
|
||||
|
||||
def _comp_sums_meg(beta, ctheta, lut_fun, n_fact, volume_integral):
|
||||
"""Lead field dot products using Legendre polynomial (P_n) series.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
beta : array, shape (n_points * n_points, 1)
|
||||
Coefficients of the integration.
|
||||
ctheta : array, shape (n_points * n_points, 1)
|
||||
Cosine of the angle between the sensor integration points.
|
||||
lut_fun : callable
|
||||
Look-up table for evaluating Legendre polynomials.
|
||||
n_fact : array
|
||||
Coefficients in the integration sum.
|
||||
volume_integral : bool
|
||||
If True, compute volume integral.
|
||||
|
||||
Returns
|
||||
-------
|
||||
sums : array, shape (4, n_points * n_points)
|
||||
The results.
|
||||
"""
|
||||
# Compute the sums occurring in the evaluation.
|
||||
# Two point magnetometers on the xz plane are assumed.
|
||||
# The four sums are:
|
||||
# * sums[:, 0] n(n+1)/(2n+1) beta^(n+1) P_n
|
||||
# * sums[:, 1] n/(2n+1) beta^(n+1) P_n'
|
||||
# * sums[:, 2] n/((2n+1)(n+1)) beta^(n+1) P_n'
|
||||
# * sums[:, 3] n/((2n+1)(n+1)) beta^(n+1) P_n''
|
||||
|
||||
# This is equivalent, but slower:
|
||||
# sums = np.sum(bbeta[:, :, np.newaxis].T * n_fact * coeffs, axis=1)
|
||||
# sums = np.rollaxis(sums, 2)
|
||||
# or
|
||||
# sums = np.einsum('ji,jk,ijk->ki', bbeta, n_fact, lut_fun(ctheta)))
|
||||
sums = np.empty((n_fact.shape[1], len(beta)))
|
||||
# beta can be e.g. 3 million elements, which ends up using lots of memory
|
||||
# so we split up the computations into ~50 MB blocks
|
||||
n_chunk = 50000000 // (8 * max(n_fact.shape) * 2)
|
||||
lims = np.concatenate([np.arange(0, beta.size, n_chunk), [beta.size]])
|
||||
for start, stop in zip(lims[:-1], lims[1:]):
|
||||
bbeta = np.tile(beta[start:stop][np.newaxis], (n_fact.shape[0], 1))
|
||||
bbeta[0] *= beta[start:stop]
|
||||
np.cumprod(bbeta, axis=0, out=bbeta) # run inplace
|
||||
np.einsum(
|
||||
"ji,jk,ijk->ki",
|
||||
bbeta,
|
||||
n_fact,
|
||||
lut_fun(ctheta[start:stop]),
|
||||
out=sums[:, start:stop],
|
||||
)
|
||||
return sums
|
||||
|
||||
|
||||
###############################################################################
|
||||
# SPHERE DOTS
|
||||
|
||||
_meg_const = 4e-14 * np.pi # This is \mu_0^2/4\pi
|
||||
_eeg_const = 1.0 / (4.0 * np.pi)
|
||||
|
||||
|
||||
def _fast_sphere_dot_r0(
|
||||
r,
|
||||
rr1_orig,
|
||||
rr2s,
|
||||
lr1,
|
||||
lr2s,
|
||||
cosmags1,
|
||||
cosmags2s,
|
||||
w1,
|
||||
w2s,
|
||||
volume_integral,
|
||||
lut,
|
||||
n_fact,
|
||||
ch_type,
|
||||
):
|
||||
"""Lead field dot product computation for M/EEG in the sphere model.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
r : float
|
||||
The integration radius. It is used to calculate beta as:
|
||||
beta = (r * r) / (lr1 * lr2).
|
||||
rr1 : array, shape (n_points x 3)
|
||||
Normalized position vectors of integrations points in first sensor.
|
||||
rr2s : list
|
||||
Normalized position vector of integration points in second sensor.
|
||||
lr1 : array, shape (n_points x 1)
|
||||
Magnitude of position vector of integration points in first sensor.
|
||||
lr2s : list
|
||||
Magnitude of position vector of integration points in second sensor.
|
||||
cosmags1 : array, shape (n_points x 1)
|
||||
Direction of integration points in first sensor.
|
||||
cosmags2s : list
|
||||
Direction of integration points in second sensor.
|
||||
w1 : array, shape (n_points x 1) | None
|
||||
Weights of integration points in the first sensor.
|
||||
w2s : list
|
||||
Weights of integration points in the second sensor.
|
||||
volume_integral : bool
|
||||
If True, compute volume integral.
|
||||
lut : callable
|
||||
Look-up table for evaluating Legendre polynomials.
|
||||
n_fact : array
|
||||
Coefficients in the integration sum.
|
||||
ch_type : str
|
||||
The channel type. It can be 'meg' or 'eeg'.
|
||||
|
||||
Returns
|
||||
-------
|
||||
result : float
|
||||
The integration sum.
|
||||
"""
|
||||
if w1 is None: # operating on surface, treat independently
|
||||
out_shape = (len(rr2s), len(rr1_orig))
|
||||
sum_axis = 1 # operate along second axis only at the end
|
||||
else:
|
||||
out_shape = (len(rr2s),)
|
||||
sum_axis = None # operate on flattened array at the end
|
||||
out = np.empty(out_shape)
|
||||
rr2 = np.concatenate(rr2s)
|
||||
lr2 = np.concatenate(lr2s)
|
||||
cosmags2 = np.concatenate(cosmags2s)
|
||||
|
||||
# outer product, sum over coords
|
||||
ct = np.einsum("ik,jk->ij", rr1_orig, rr2)
|
||||
np.clip(ct, -1, 1, ct)
|
||||
|
||||
# expand axes
|
||||
rr1 = rr1_orig[:, np.newaxis, :] # (n_rr1, n_rr2, n_coord) e.g. 4x4x3
|
||||
rr2 = rr2[np.newaxis, :, :]
|
||||
lr1lr2 = lr1[:, np.newaxis] * lr2[np.newaxis, :]
|
||||
|
||||
beta = (r * r) / lr1lr2
|
||||
if ch_type == "meg":
|
||||
sums = _comp_sums_meg(
|
||||
beta.flatten(), ct.flatten(), lut, n_fact, volume_integral
|
||||
)
|
||||
sums.shape = (4,) + beta.shape
|
||||
|
||||
# Accumulate the result, a little bit streamlined version
|
||||
# cosmags1 = cosmags1[:, np.newaxis, :]
|
||||
# cosmags2 = cosmags2[np.newaxis, :, :]
|
||||
# n1c1 = np.sum(cosmags1 * rr1, axis=2)
|
||||
# n1c2 = np.sum(cosmags1 * rr2, axis=2)
|
||||
# n2c1 = np.sum(cosmags2 * rr1, axis=2)
|
||||
# n2c2 = np.sum(cosmags2 * rr2, axis=2)
|
||||
# n1n2 = np.sum(cosmags1 * cosmags2, axis=2)
|
||||
n1c1 = np.einsum("ik,ijk->ij", cosmags1, rr1)
|
||||
n1c2 = np.einsum("ik,ijk->ij", cosmags1, rr2)
|
||||
n2c1 = np.einsum("jk,ijk->ij", cosmags2, rr1)
|
||||
n2c2 = np.einsum("jk,ijk->ij", cosmags2, rr2)
|
||||
n1n2 = np.einsum("ik,jk->ij", cosmags1, cosmags2)
|
||||
part1 = ct * n1c1 * n2c2
|
||||
part2 = n1c1 * n2c1 + n1c2 * n2c2
|
||||
|
||||
result = (
|
||||
n1c1 * n2c2 * sums[0]
|
||||
+ (2.0 * part1 - part2) * sums[1]
|
||||
+ (n1n2 + part1 - part2) * sums[2]
|
||||
+ (n1c2 - ct * n1c1) * (n2c1 - ct * n2c2) * sums[3]
|
||||
)
|
||||
|
||||
# Give it a finishing touch!
|
||||
result *= _meg_const / lr1lr2
|
||||
if volume_integral:
|
||||
result *= r
|
||||
else: # 'eeg'
|
||||
result = _comp_sum_eeg(beta.flatten(), ct.flatten(), lut, n_fact)
|
||||
result.shape = beta.shape
|
||||
# Give it a finishing touch!
|
||||
result *= _eeg_const
|
||||
result /= lr1lr2
|
||||
# now we add them all up with weights
|
||||
offset = 0
|
||||
result *= np.concatenate(w2s)
|
||||
if w1 is not None:
|
||||
result *= w1[:, np.newaxis]
|
||||
for ii, w2 in enumerate(w2s):
|
||||
out[ii] = np.sum(result[:, offset : offset + len(w2)], axis=sum_axis)
|
||||
offset += len(w2)
|
||||
return out
|
||||
|
||||
|
||||
@fill_doc
|
||||
def _do_self_dots(intrad, volume, coils, r0, ch_type, lut, n_fact, n_jobs):
|
||||
"""Perform the lead field dot product integrations.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
intrad : float
|
||||
The integration radius. It is used to calculate beta as:
|
||||
beta = (intrad * intrad) / (r1 * r2).
|
||||
volume : bool
|
||||
If True, perform volume integral.
|
||||
coils : list of dict
|
||||
The coils.
|
||||
r0 : array, shape (3 x 1)
|
||||
The origin of the sphere.
|
||||
ch_type : str
|
||||
The channel type. It can be 'meg' or 'eeg'.
|
||||
lut : callable
|
||||
Look-up table for evaluating Legendre polynomials.
|
||||
n_fact : array
|
||||
Coefficients in the integration sum.
|
||||
%(n_jobs)s
|
||||
|
||||
Returns
|
||||
-------
|
||||
products : array, shape (n_coils, n_coils)
|
||||
The integration products.
|
||||
"""
|
||||
if ch_type == "eeg":
|
||||
intrad = intrad * 0.7
|
||||
# convert to normalized distances from expansion center
|
||||
rmags = [coil["rmag"] - r0[np.newaxis, :] for coil in coils]
|
||||
rlens = [np.sqrt(np.sum(r * r, axis=1)) for r in rmags]
|
||||
rmags = [r / rl[:, np.newaxis] for r, rl in zip(rmags, rlens)]
|
||||
cosmags = [coil["cosmag"] for coil in coils]
|
||||
ws = [coil["w"] for coil in coils]
|
||||
parallel, p_fun, n_jobs = parallel_func(_do_self_dots_subset, n_jobs)
|
||||
prods = parallel(
|
||||
p_fun(intrad, rmags, rlens, cosmags, ws, volume, lut, n_fact, ch_type, idx)
|
||||
for idx in np.array_split(np.arange(len(rmags)), n_jobs)
|
||||
)
|
||||
products = np.sum(prods, axis=0)
|
||||
return products
|
||||
|
||||
|
||||
def _do_self_dots_subset(
|
||||
intrad, rmags, rlens, cosmags, ws, volume, lut, n_fact, ch_type, idx
|
||||
):
|
||||
"""Parallelize."""
|
||||
# all possible combinations of two magnetometers
|
||||
products = np.zeros((len(rmags), len(rmags)))
|
||||
for ci1 in idx:
|
||||
ci2 = ci1 + 1
|
||||
res = _fast_sphere_dot_r0(
|
||||
intrad,
|
||||
rmags[ci1],
|
||||
rmags[:ci2],
|
||||
rlens[ci1],
|
||||
rlens[:ci2],
|
||||
cosmags[ci1],
|
||||
cosmags[:ci2],
|
||||
ws[ci1],
|
||||
ws[:ci2],
|
||||
volume,
|
||||
lut,
|
||||
n_fact,
|
||||
ch_type,
|
||||
)
|
||||
products[ci1, :ci2] = res
|
||||
products[:ci2, ci1] = res
|
||||
return products
|
||||
|
||||
|
||||
def _do_cross_dots(intrad, volume, coils1, coils2, r0, ch_type, lut, n_fact):
|
||||
"""Compute lead field dot product integrations between two coil sets.
|
||||
|
||||
The code is a direct translation of MNE-C code found in
|
||||
`mne_map_data/lead_dots.c`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
intrad : float
|
||||
The integration radius. It is used to calculate beta as:
|
||||
beta = (intrad * intrad) / (r1 * r2).
|
||||
volume : bool
|
||||
If True, compute volume integral.
|
||||
coils1 : list of dict
|
||||
The original coils.
|
||||
coils2 : list of dict
|
||||
The coils to which data is being mapped.
|
||||
r0 : array, shape (3 x 1).
|
||||
The origin of the sphere.
|
||||
ch_type : str
|
||||
The channel type. It can be 'meg' or 'eeg'
|
||||
lut : callable
|
||||
Look-up table for evaluating Legendre polynomials.
|
||||
n_fact : array
|
||||
Coefficients in the integration sum.
|
||||
|
||||
Returns
|
||||
-------
|
||||
products : array, shape (n_coils, n_coils)
|
||||
The integration products.
|
||||
"""
|
||||
if ch_type == "eeg":
|
||||
intrad = intrad * 0.7
|
||||
rmags1 = [coil["rmag"] - r0[np.newaxis, :] for coil in coils1]
|
||||
rmags2 = [coil["rmag"] - r0[np.newaxis, :] for coil in coils2]
|
||||
|
||||
rlens1 = [np.sqrt(np.sum(r * r, axis=1)) for r in rmags1]
|
||||
rlens2 = [np.sqrt(np.sum(r * r, axis=1)) for r in rmags2]
|
||||
|
||||
rmags1 = [r / rl[:, np.newaxis] for r, rl in zip(rmags1, rlens1)]
|
||||
rmags2 = [r / rl[:, np.newaxis] for r, rl in zip(rmags2, rlens2)]
|
||||
|
||||
ws1 = [coil["w"] for coil in coils1]
|
||||
ws2 = [coil["w"] for coil in coils2]
|
||||
|
||||
cosmags1 = [coil["cosmag"] for coil in coils1]
|
||||
cosmags2 = [coil["cosmag"] for coil in coils2]
|
||||
|
||||
products = np.zeros((len(rmags1), len(rmags2)))
|
||||
for ci1 in range(len(coils1)):
|
||||
res = _fast_sphere_dot_r0(
|
||||
intrad,
|
||||
rmags1[ci1],
|
||||
rmags2,
|
||||
rlens1[ci1],
|
||||
rlens2,
|
||||
cosmags1[ci1],
|
||||
cosmags2,
|
||||
ws1[ci1],
|
||||
ws2,
|
||||
volume,
|
||||
lut,
|
||||
n_fact,
|
||||
ch_type,
|
||||
)
|
||||
products[ci1, :] = res
|
||||
return products
|
||||
|
||||
|
||||
@fill_doc
|
||||
def _do_surface_dots(
|
||||
intrad, volume, coils, surf, sel, r0, ch_type, lut, n_fact, n_jobs
|
||||
):
|
||||
"""Compute the map construction products.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
intrad : float
|
||||
The integration radius. It is used to calculate beta as:
|
||||
beta = (intrad * intrad) / (r1 * r2)
|
||||
volume : bool
|
||||
If True, compute a volume integral.
|
||||
coils : list of dict
|
||||
The coils.
|
||||
surf : dict
|
||||
The surface on which the field is interpolated.
|
||||
sel : array
|
||||
Indices of the surface vertices to select.
|
||||
r0 : array, shape (3 x 1)
|
||||
The origin of the sphere.
|
||||
ch_type : str
|
||||
The channel type. It can be 'meg' or 'eeg'.
|
||||
lut : callable
|
||||
Look-up table for Legendre polynomials.
|
||||
n_fact : array
|
||||
Coefficients in the integration sum.
|
||||
%(n_jobs)s
|
||||
|
||||
Returns
|
||||
-------
|
||||
products : array, shape (n_coils, n_coils)
|
||||
The integration products.
|
||||
"""
|
||||
# convert to normalized distances from expansion center
|
||||
rmags = [coil["rmag"] - r0[np.newaxis, :] for coil in coils]
|
||||
rlens = [np.sqrt(np.sum(r * r, axis=1)) for r in rmags]
|
||||
rmags = [r / rl[:, np.newaxis] for r, rl in zip(rmags, rlens)]
|
||||
cosmags = [coil["cosmag"] for coil in coils]
|
||||
ws = [coil["w"] for coil in coils]
|
||||
rref = None
|
||||
refl = None
|
||||
# virt_ref = False
|
||||
if ch_type == "eeg":
|
||||
intrad = intrad * 0.7
|
||||
# The virtual ref code is untested and unused, so it is
|
||||
# commented out for now
|
||||
# if virt_ref:
|
||||
# rref = virt_ref[np.newaxis, :] - r0[np.newaxis, :]
|
||||
# refl = np.sqrt(np.sum(rref * rref, axis=1))
|
||||
# rref /= refl[:, np.newaxis]
|
||||
|
||||
rsurf = surf["rr"][sel] - r0[np.newaxis, :]
|
||||
lsurf = np.sqrt(np.sum(rsurf * rsurf, axis=1))
|
||||
rsurf /= lsurf[:, np.newaxis]
|
||||
this_nn = surf["nn"][sel]
|
||||
|
||||
# loop over the coils
|
||||
parallel, p_fun, n_jobs = parallel_func(_do_surface_dots_subset, n_jobs)
|
||||
prods = parallel(
|
||||
p_fun(
|
||||
intrad,
|
||||
rsurf,
|
||||
rmags,
|
||||
rref,
|
||||
refl,
|
||||
lsurf,
|
||||
rlens,
|
||||
this_nn,
|
||||
cosmags,
|
||||
ws,
|
||||
volume,
|
||||
lut,
|
||||
n_fact,
|
||||
ch_type,
|
||||
idx,
|
||||
)
|
||||
for idx in np.array_split(np.arange(len(rmags)), n_jobs)
|
||||
)
|
||||
products = np.sum(prods, axis=0)
|
||||
return products
|
||||
|
||||
|
||||
def _do_surface_dots_subset(
|
||||
intrad,
|
||||
rsurf,
|
||||
rmags,
|
||||
rref,
|
||||
refl,
|
||||
lsurf,
|
||||
rlens,
|
||||
this_nn,
|
||||
cosmags,
|
||||
ws,
|
||||
volume,
|
||||
lut,
|
||||
n_fact,
|
||||
ch_type,
|
||||
idx,
|
||||
):
|
||||
"""Parallelize.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
refl : array | None
|
||||
If ch_type is 'eeg', the magnitude of position vector of the
|
||||
virtual reference (never used).
|
||||
lsurf : array
|
||||
Magnitude of position vector of the surface points.
|
||||
rlens : list of arrays of length n_coils
|
||||
Magnitude of position vector.
|
||||
this_nn : array, shape (n_vertices, 3)
|
||||
Surface normals.
|
||||
cosmags : list of array.
|
||||
Direction of the integration points in the coils.
|
||||
ws : list of array
|
||||
Integration weights of the coils.
|
||||
volume : bool
|
||||
If True, compute volume integral.
|
||||
lut : callable
|
||||
Look-up table for evaluating Legendre polynomials.
|
||||
n_fact : array
|
||||
Coefficients in the integration sum.
|
||||
ch_type : str
|
||||
'meg' or 'eeg'
|
||||
idx : array, shape (n_coils x 1)
|
||||
Index of coil.
|
||||
|
||||
Returns
|
||||
-------
|
||||
products : array, shape (n_coils, n_coils)
|
||||
The integration products.
|
||||
"""
|
||||
products = _fast_sphere_dot_r0(
|
||||
intrad,
|
||||
rsurf,
|
||||
rmags,
|
||||
lsurf,
|
||||
rlens,
|
||||
this_nn,
|
||||
cosmags,
|
||||
None,
|
||||
ws,
|
||||
volume,
|
||||
lut,
|
||||
n_fact,
|
||||
ch_type,
|
||||
).T
|
||||
if rref is not None:
|
||||
raise NotImplementedError # we don't ever use this, isn't tested
|
||||
# vres = _fast_sphere_dot_r0(
|
||||
# intrad, rref, rmags, refl, rlens, this_nn, cosmags, None, ws,
|
||||
# volume, lut, n_fact, ch_type)
|
||||
# products -= vres
|
||||
return products
|
||||
Reference in New Issue
Block a user