#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Copyright (C) 2012 Computational Neuroscience Group, NMBU.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
"""
from __future__ import division
import numpy as np
[docs]def return_dist_from_segments(xstart, ystart, zstart, xend, yend, zend, p):
"""
Returns distance and closest point on line segments from point p
"""
px = xend-xstart
py = yend-ystart
pz = zend-zstart
delta = px*px + py*py + pz*pz
u = ((p[0] - xstart) * px + (p[1] - ystart) * py + (p[2] - zstart) * pz) / delta
u[u > 1] = 1.0
u[u < 0] = 0.0
closest_point = np.array([xstart + u * px,
ystart + u * py,
zstart + u * pz])
dist = np.sqrt(np.sum((closest_point.T - p)**2, axis=1))
return dist, closest_point
[docs]def calc_lfp_linesource_anisotropic(cell, x, y, z, sigma, r_limit):
"""Calculate electric field potential using the line-source method, all
compartments treated as line sources, even soma.
Parameters
----------
cell: obj
LFPy.Cell or LFPy.TemplateCell instance
x : float
extracellular position, x-axis
y : float
extracellular position, y-axis
z : float
extracellular position, z-axis
sigma : array
extracellular conductivity [sigma_x, sigma_y, sigma_z]
r_limit : np.ndarray
minimum distance to source current for each compartment
"""
#some variables for h, r2, r_soma calculations
xstart = cell.xstart
xend = cell.xend
ystart = cell.ystart
yend = cell.yend
zstart = cell.zstart
zend = cell.zend
l_vecs = np.array([xend - xstart,
yend - ystart,
zend - zstart])
pos = np.array([x, y, z])
rs, closest_points = return_dist_from_segments(xstart, ystart, zstart,
xend, yend, zend, pos)
dx2 = (xend - xstart)**2
dy2 = (yend - ystart)**2
dz2 = (zend - zstart)**2
a = (sigma[1] * sigma[2] * dx2 +
sigma[0] * sigma[2] * dy2 +
sigma[0] * sigma[1] * dz2)
b = -2 * (sigma[1] * sigma[2] * (x - xstart) * (xend - xstart) +
sigma[0] * sigma[2] * (y - ystart) * (yend - ystart) +
sigma[0] * sigma[1] * (z - zstart) * (zend - zstart))
c = (sigma[1] * sigma[2] * (x - xstart)**2 +
sigma[0] * sigma[2] * (y - ystart)**2 +
sigma[0] * sigma[1] * (z - zstart)**2)
for idx in np.where(rs < r_limit)[0]:
r, closest_point, l_vec = rs[idx], closest_points[:, idx], l_vecs[:, idx]
p_ = pos.copy()
if np.abs(r) < 1e-12:
if np.abs(l_vec[0]) < 1e-12:
p_[0] += r_limit[idx]
elif np.abs(l_vec[1]) < 1e-12:
p_[1] += r_limit[idx]
elif np.abs(l_vec[2]) < 1e-12:
p_[2] += r_limit[idx]
else:
displace_vec = np.array([-l_vec[1], l_vec[0], 0])
displace_vec = displace_vec / np.sqrt(np.sum(displace_vec**2)) * r_limit[idx]
p_[:] += displace_vec
else:
p_[:] = pos + (pos - closest_point) * (r_limit[idx] - r) / r
if np.sqrt(np.sum((p_ - closest_point)**2)) - r_limit[idx] > 1e-9:
print(p_, closest_point)
raise RuntimeError("Segment adjustment not working")
b[idx] = -2 * (sigma[1] * sigma[2] * (p_[0] - xstart[idx]) * (xend[idx] - xstart[idx]) +
sigma[0] * sigma[2] * (p_[1] - ystart[idx]) * (yend[idx] - ystart[idx]) +
sigma[0] * sigma[1] * (p_[2] - zstart[idx]) * (zend[idx] - zstart[idx]))
c[idx] = (sigma[1] * sigma[2] * (p_[0] - xstart[idx])**2 +
sigma[0] * sigma[2] * (p_[1] - ystart[idx])**2 +
sigma[0] * sigma[1] * (p_[2] - zstart[idx])**2)
[i] = np.where(np.abs(b) <= 1e-6)
[iia] = np.where(np.bitwise_and(np.abs(4 * a * c - b*b) < 1e-6, np.abs(a - c) < 1e-6))
[iib] = np.where(np.bitwise_and(np.abs(4 * a * c - b*b) < 1e-6, np.abs(a - c) >= 1e-6))
[iii] = np.where(np.bitwise_and(4 * a * c - b*b < -1e-6, np.abs(b) > 1e-6))
[iiii] = np.where(np.bitwise_and(4 * a * c - b*b > 1e-6, np.abs(b) > 1e-6))
if len(i) + len(iia) + len(iib) + len(iii) + len(iiii) != cell.totnsegs:
print(a, b, c)
print(i, iia, iib, iii, iiii)
raise RuntimeError
mapping = np.zeros(cell.totnsegs)
mapping[i] = _anisotropic_line_source_case_i(a[i], c[i])
mapping[iia] = _anisotropic_line_source_case_iia(a[iia], c[iia])
mapping[iib] = _anisotropic_line_source_case_iib(a[iib], b[iib], c[iib])
mapping[iii] = _anisotropic_line_source_case_iii(a[iii], b[iii], c[iii])
mapping[iiii] = _anisotropic_line_source_case_iiii(a[iiii], b[iiii], c[iiii])
if np.isnan(mapping).any():
raise RuntimeError("NaN")
return 1 / (4 * np.pi) * mapping / np.sqrt(a)
[docs]def calc_lfp_soma_as_point_anisotropic(cell, x, y, z, sigma, r_limit):
"""Calculate electric field potential, soma is treated as point source, all
compartments except soma are treated as line sources.
Parameters
----------
cell: obj
LFPy.Cell or LFPy.TemplateCell instance
x : float
extracellular position, x-axis
y : float
extracellular position, y-axis
z : float
extracellular position, z-axis
sigma : array
extracellular conductivity [sigma_x, sigma_y, sigma_z]
r_limit : np.ndarray
minimum distance to source current for each compartment
"""
xstart = cell.xstart
xend = cell.xend
ystart = cell.ystart
yend = cell.yend
zstart = cell.zstart
zend = cell.zend
l_vecs = np.array([xend - xstart, yend - ystart, zend - zstart])
pos = np.array([x, y, z])
rs, closest_points = return_dist_from_segments(xstart, ystart, zstart, xend, yend, zend, pos)
dx2 = (xend - xstart)**2
dy2 = (yend - ystart)**2
dz2 = (zend - zstart)**2
a = (sigma[1] * sigma[2] * dx2 +
sigma[0] * sigma[2] * dy2 +
sigma[0] * sigma[1] * dz2)
b = -2 * (sigma[1] * sigma[2] * (x - xstart) * (xend - xstart) +
sigma[0] * sigma[2] * (y - ystart) * (yend - ystart) +
sigma[0] * sigma[1] * (z - zstart) * (zend - zstart))
c = (sigma[1] * sigma[2] * (x - xstart)**2 +
sigma[0] * sigma[2] * (y - ystart)**2 +
sigma[0] * sigma[1] * (z - zstart)**2)
for idx in np.where(rs < r_limit)[0]:
r, closest_point, l_vec = rs[idx], closest_points[:, idx], l_vecs[:, idx]
p_ = pos.copy()
if np.abs(r) < 1e-12:
if np.abs(l_vec[0]) < 1e-12:
p_[0] += r_limit[idx]
elif np.abs(l_vec[1]) < 1e-12:
p_[1] += r_limit[idx]
elif np.abs(l_vec[2]) < 1e-12:
p_[2] += r_limit[idx]
else:
displace_vec = np.array([-l_vec[1], l_vec[0], 0])
displace_vec = displace_vec / np.sqrt(np.sum(displace_vec**2)) * r_limit[idx]
p_[:] += displace_vec
else:
p_[:] = pos + (pos - closest_point) * (r_limit[idx] - r) / r
if np.sqrt(np.sum((p_ - closest_point)**2)) - r_limit[idx] > 1e-9:
print(p_, closest_point)
raise RuntimeError("Segment adjustment not working")
b[idx] = -2 * (sigma[1] * sigma[2] * (p_[0] - xstart[idx]) * (xend[idx] - xstart[idx]) +
sigma[0] * sigma[2] * (p_[1] - ystart[idx]) * (yend[idx] - ystart[idx]) +
sigma[0] * sigma[1] * (p_[2] - zstart[idx]) * (zend[idx] - zstart[idx]))
c[idx] = (sigma[1] * sigma[2] * (p_[0] - xstart[idx])**2 +
sigma[0] * sigma[2] * (p_[1] - ystart[idx])**2 +
sigma[0] * sigma[1] * (p_[2] - zstart[idx])**2)
[i] = np.where(np.abs(b) <= 1e-6)
[iia] = np.where(np.bitwise_and(np.abs(4 * a * c - b*b) < 1e-6, np.abs(a - c) < 1e-6))
[iib] = np.where(np.bitwise_and(np.abs(4 * a * c - b*b) < 1e-6, np.abs(a - c) >= 1e-6))
[iii] = np.where(np.bitwise_and(4 * a * c - b*b < -1e-6, np.abs(b) > 1e-6))
[iiii] = np.where(np.bitwise_and(4 * a * c - b*b > 1e-6, np.abs(b) > 1e-6))
if len(i) + len(iia) + len(iib) + len(iii) + len(iiii) != cell.totnsegs:
print(a, b, c)
print(i, iia, iib, iii, iiii)
raise RuntimeError
mapping = np.zeros(cell.totnsegs)
mapping[i] = _anisotropic_line_source_case_i(a[i], c[i])
mapping[iia] = _anisotropic_line_source_case_iia(a[iia], c[iia])
mapping[iib] = _anisotropic_line_source_case_iib(a[iib], b[iib], c[iib])
mapping[iii] = _anisotropic_line_source_case_iii(a[iii], b[iii], c[iii])
mapping[iiii] = _anisotropic_line_source_case_iiii(a[iiii], b[iiii], c[iiii])
if np.isnan(mapping).any():
raise RuntimeError("NaN")
mapping /= np.sqrt(a)
# Get compartment indices for somatic compartments (to be treated as point
# sources)
try:
somainds = cell.get_idx("soma")
except Exception:
raise Exception('Call {}.get_idx("soma") failed in method LFPy.lfpcalc.calc_lfp_soma_as_point'.format(cell))
dx2_soma = (cell.xmid[somainds] - x)**2
dy2_soma = (cell.ymid[somainds] - y)**2
dz2_soma = (cell.zmid[somainds] - z)**2
r2_soma = dx2_soma + dy2_soma + dz2_soma
# Go through and correct all (if any) somatic idxs that are too close
for close_idx in np.where(np.abs(r2_soma) < 1e-6)[0]:
dx2_soma[close_idx] += 0.001
r2_soma[close_idx] += 0.001
for close_idx in np.where(r2_soma < r_limit[somainds]**2)[0]:
# For anisotropic media, the direction in which to move points matter.
# Radial distance between point source and electrode is scaled to r_limit
r2_scale_factor = r_limit[somainds[close_idx]]*r_limit[somainds[close_idx]] / r2_soma[close_idx]
dx2_soma[close_idx] *= r2_scale_factor
dy2_soma[close_idx] *= r2_scale_factor
dz2_soma[close_idx] *= r2_scale_factor
mapping[somainds] = 1/np.sqrt(sigma[1] * sigma[2] * dx2_soma
+ sigma[0] * sigma[2] * dy2_soma
+ sigma[0] * sigma[1] * dz2_soma)
return 1. / (4 * np.pi) * mapping
def _anisotropic_line_source_case_i(a, c):
return np.log(np.sqrt(a / c) + np.sqrt(a / c + 1))
def _anisotropic_line_source_case_iia(a, c):
return np.log(np.abs(1 + np.sqrt(a / c)))
def _anisotropic_line_source_case_iib(a, b, c):
return np.abs(np.log(np.abs(np.sign(b) * np.sqrt(a/c) + 1)))
def _anisotropic_line_source_case_iii(a, b, c):
return np.log(np.abs((2 * a + b + 2 * np.sqrt(a * (a + b + c)))
/ (b + 2 * np.sqrt(a * c))))
def _anisotropic_line_source_case_iiii(a, b, c):
return (np.arcsinh((2 * a + b) / np.sqrt(4 * a * c - b*b)) -
np.arcsinh(b / np.sqrt(4 * a * c - b*b)))
[docs]def calc_lfp_linesource(cell, x, y, z, sigma, r_limit):
"""Calculate electric field potential using the line-source method, all
compartments treated as line sources, including soma.
Parameters
----------
cell: obj
LFPy.Cell or LFPy.TemplateCell like instance
x : float
extracellular position, x-axis
y : float
extracellular position, y-axis
z : float
extracellular position, z-axis
sigma : float
extracellular conductivity
r_limit : np.ndarray
minimum distance to source current for each compartment
"""
#some variables for h, r2, r_soma calculations
xstart = cell.xstart
xend = cell.xend
ystart = cell.ystart
yend = cell.yend
zstart = cell.zstart
zend = cell.zend
deltaS = _deltaS_calc(xstart, xend, ystart, yend, zstart, zend)
h = _h_calc(xstart, xend, ystart, yend, zstart, zend, deltaS, x, y, z)
r2 = _r2_calc(xend, yend, zend, x, y, z, h)
too_close_idxs = np.where(r2 < r_limit*r_limit)[0]
r2[too_close_idxs] = r_limit[too_close_idxs]**2
l = h + deltaS
hnegi = h < 0
hposi = h >= 0
lnegi = l < 0
lposi = l >= 0
mapping = np.zeros(len(cell.xstart))
#case i, h < 0, l < 0
[i] = np.where(hnegi & lnegi)
#case ii, h < 0, l >= 0
[ii] = np.where(hnegi & lposi)
#case iii, h >= 0, l >= 0
[iii] = np.where(hposi & lposi)
mapping[i] = _linesource_calc_case1(l[i], r2[i], h[i])
mapping[ii] = _linesource_calc_case2(l[ii], r2[ii], h[ii])
mapping[iii] = _linesource_calc_case3(l[iii], r2[iii], h[iii])
return 1 / (4 * np.pi * sigma * deltaS) * mapping
[docs]def calc_lfp_soma_as_point(cell, x, y, z, sigma, r_limit):
"""Calculate electric field potential using the line-source method,
soma is treated as point/sphere source
Parameters
----------
cell: obj
`LFPy.Cell` or `LFPy.TemplateCell` like instance
x : float
extracellular position, x-axis
y : float
extracellular position, y-axis
z : float
extracellular position, z-axis
sigma : float
extracellular conductivity in S/m
r_limit : np.ndarray
minimum distance to source current for each compartment.
"""
# get compartment indices for somatic compartments (to be treated as point
# sources)
try:
somainds = cell.get_idx("soma")
except Exception:
raise Exception('Call {}.get_idx("soma") failed in method LFPy.lfpcalc.calc_lfp_soma_as_point'.format(cell))
#some variables for h, r2, r_soma calculations
xstart = cell.xstart
xmid = cell.xmid[somainds]
xend = cell.xend
ystart = cell.ystart
ymid = cell.ymid[somainds]
yend = cell.yend
zstart = cell.zstart
zmid = cell.zmid[somainds]
zend = cell.zend
deltaS = _deltaS_calc(xstart, xend, ystart, yend, zstart, zend)
h = _h_calc(xstart, xend, ystart, yend, zstart, zend, deltaS, x, y, z)
r2 = _r2_calc(xend, yend, zend, x, y, z, h)
r_soma = _r_soma_calc(xmid, ymid, zmid, x, y, z)
if np.any(r_soma < r_limit[somainds]):
print('Adjusting r-distance to soma segments')
r_soma[r_soma < r_limit[somainds]
] = r_limit[somainds][r_soma < r_limit[somainds]]
too_close_idxs = np.where(r2 < r_limit*r_limit)[0]
r2[too_close_idxs] = r_limit[too_close_idxs]**2
l = h + deltaS
hnegi = h < 0
hposi = h >= 0
lnegi = l < 0
lposi = l >= 0
# Ensuring that soma is not treated as line-source
hnegi[somainds] = hposi[somainds] = lnegi[somainds] = lposi[somainds] = False
#Line sources
#case i, h < 0, l < 0
i = np.where(hnegi & lnegi)
#case ii, h < 0, l >= 0
ii = np.where(hnegi & lposi)
#case iii, h >= 0, l >= 0
iii = np.where(hposi & lposi)
#Summarizing all potential contributions
mapping = np.zeros(cell.totnsegs)
mapping[somainds] = 1 / r_soma
deltaS[somainds] = 1.
mapping[i] = _linesource_calc_case1(l[i], r2[i], h[i])
mapping[ii] = _linesource_calc_case2(l[ii], r2[ii], h[ii])
mapping[iii] = _linesource_calc_case3(l[iii], r2[iii], h[iii])
return 1 / (4 * np.pi * sigma * deltaS) * mapping
def _linesource_calc_case1(l_i, r2_i, h_i):
"""Calculates linesource contribution for case i"""
bb = np.sqrt(h_i*h_i + r2_i) - h_i
cc = np.sqrt(l_i*l_i + r2_i) - l_i
dd = np.log(bb / cc)
return dd
def _linesource_calc_case2(l_ii, r2_ii, h_ii):
"""Calculates linesource contribution for case ii"""
bb = np.sqrt(h_ii*h_ii + r2_ii) - h_ii
cc = (l_ii + np.sqrt(l_ii*l_ii + r2_ii)) / r2_ii
dd = np.log(bb * cc)
return dd
def _linesource_calc_case3(l_iii, r2_iii, h_iii):
"""Calculates linesource contribution for case iii"""
bb = np.sqrt(l_iii*l_iii + r2_iii) + l_iii
cc = np.sqrt(h_iii*h_iii + r2_iii) + h_iii
dd = np.log(bb / cc)
return dd
def _deltaS_calc(xstart, xend, ystart, yend, zstart, zend):
"""Returns length of each segment"""
deltaS = np.sqrt((xstart - xend)**2 + (ystart - yend)**2 +
(zstart-zend)**2)
return deltaS
def _h_calc(xstart, xend, ystart, yend, zstart, zend, deltaS, x, y, z):
"""Subroutine used by calc_lfp_*()"""
aa = np.array([x - xend, y - yend, z-zend])
bb = np.array([xend - xstart, yend - ystart, zend - zstart])
cc = np.sum(aa*bb, axis=0)
hh = cc / deltaS
return hh
def _r2_calc(xend, yend, zend, x, y, z, h):
"""Subroutine used by calc_lfp_*()"""
r2 = (x-xend)**2 + (y-yend)**2 + (z-zend)**2 - h**2
return abs(r2)
def _r_soma_calc(xmid, ymid, zmid, x, y, z):
"""calculate the distance to soma midpoint"""
r_soma = np.sqrt((x - xmid)**2 + (y - ymid)**2 + (z - zmid)**2)
return r_soma
[docs]def calc_lfp_pointsource(cell, x, y, z, sigma, r_limit):
"""Calculate extracellular potentials using the point-source
equation on all compartments
Parameters
----------
cell: obj
LFPy.Cell or LFPy.TemplateCell like instance
x : float
extracellular position, x-axis
y : float
extracellular position, y-axis
z : float
extracellular position, z-axis
sigma : float
extracellular conductivity
r_limit : np.ndarray
minimum distance to source current for each compartment
"""
r2 = (cell.xmid - x)**2 + (cell.ymid - y)**2 + (cell.zmid - z)**2
r2 = _check_rlimit_point(r2, r_limit)
mapping = 1 / (4 * np.pi * sigma * np.sqrt(r2))
return mapping
[docs]def calc_lfp_pointsource_anisotropic(cell, x, y, z, sigma, r_limit):
"""Calculate extracellular potentials using the anisotropic point-source
equation on all compartments
Parameters
----------
cell: obj
LFPy.Cell or LFPy.TemplateCell instance
x : float
extracellular position, x-axis
y : float
extracellular position, y-axis
z : float
extracellular position, z-axis
sigma : array
extracellular conductivity in [x,y,z]-direction
r_limit : np.ndarray
minimum distance to source current for each compartment
"""
dx2 = (cell.xmid - x)**2
dy2 = (cell.ymid - y)**2
dz2 = (cell.zmid - z)**2
r2 = dx2 + dy2 + dz2
if (np.abs(r2) < 1e-6).any():
dx2[np.abs(r2) < 1e-6] += 0.001
r2[np.abs(r2) < 1e-6] += 0.001
close_idxs = r2 < r_limit*r_limit
# For anisotropic media, the direction in which to move points matter.
# Radial distance between point source and electrode is scaled to r_limit
r2_scale_factor = r_limit[close_idxs]*r_limit[close_idxs] / r2[close_idxs]
dx2[close_idxs] *= r2_scale_factor
dy2[close_idxs] *= r2_scale_factor
dz2[close_idxs] *= r2_scale_factor
sigma_r = np.sqrt(sigma[1] * sigma[2] * dx2
+ sigma[0] * sigma[2] * dy2
+ sigma[0] * sigma[1] * dz2)
mapping = 1 / (4 * np.pi * sigma_r)
return mapping
def _check_rlimit_point(r2, r_limit):
"""Correct r2 so that r2 >= r_limit**2 for all values"""
inds = r2 < r_limit*r_limit
r2[inds] = r_limit[inds]*r_limit[inds]
return r2
[docs]def calc_lfp_pointsource_moi(cell, x, y, z, sigma_T, sigma_S, sigma_G,
steps, h, r_limit, **kwargs):
"""Calculate extracellular potentials using the point-source
equation on all compartments for in vitro Microelectrode Array (MEA) slices
Parameters
----------
cell: obj
LFPy.Cell or LFPy.TemplateCell like instance
x : float
extracellular position, x-axis
y : float
extracellular position, y-axis
z : float
extracellular position, z-axis
sigma_T : float
extracellular conductivity in tissue slice
sigma_G : float
Conductivity of MEA glass electrode plane.
Should normally be zero for MEA set up.
sigma_S : float
Conductivity of saline bath that tissue slice is immersed in
steps : int
Number of steps to average over the in technically infinite sum
h : float
Slice thickness in um.
r_limit : np.ndarray
minimum distance to source current for each compartment
"""
dx2 = (x - cell.xmid)**2
dy2 = (y - cell.ymid)**2
dz2 = (z - cell.zmid)**2
dL2 = dx2 + dy2
inds = np.where(dL2 + dz2 < r_limit*r_limit)[0]
dL2[inds] = r_limit[inds]*r_limit[inds] - dz2[inds]
def _omega(dz):
return 1/np.sqrt(dL2 + dz*dz)
WTS = (sigma_T - sigma_S)/(sigma_T + sigma_S)
WTG = (sigma_T - sigma_G)/(sigma_T + sigma_G)
mapping = _omega(z - cell.zmid)
mapping += (WTS * _omega(z + cell.zmid - 2*h) +
WTG * _omega(z + cell.zmid))
n = np.arange(1, steps)
a = (WTS*WTG)**n[:, None] * (WTS * _omega(z + cell.zmid - 2*(n[:, None] + 1)*h) +
WTG * _omega(z + cell.zmid + 2*n[:, None]*h) +
_omega(z - cell.zmid + 2*n[:, None]*h) +
_omega(z - cell.zmid - 2*n[:, None]*h))
mapping += np.sum(a, axis=0)
mapping *= 1/(4*np.pi*sigma_T)
return mapping
[docs]def calc_lfp_linesource_moi(cell, x, y, z, sigma_T, sigma_S, sigma_G,
steps, h, r_limit, **kwargs):
"""Calculate extracellular potentials using the line-source
equation on all compartments for in vitro Microelectrode Array (MEA) slices
Parameters
----------
cell: obj
LFPy.Cell or LFPy.TemplateCell like instance
x : float
extracellular position, x-axis
y : float
extracellular position, y-axis
z : float
extracellular position, z-axis
sigma_T : float
extracellular conductivity in tissue slice
sigma_G : float
Conductivity of MEA glass electrode plane.
Should normally be zero for MEA set up, and for this method,
only zero valued sigma_G is supported.
sigma_S : float
Conductivity of saline bath that tissue slice is immersed in
steps : int
Number of steps to average over the in technically infinite sum
h : float
Slice thickness in um.
r_limit : np.ndarray
minimum distance to source current for each compartment
"""
if np.abs(z) > 1e-9:
raise RuntimeError("This method can only handle electrodes "
"at the MEA plane z=0")
if np.abs(sigma_G) > 1e-9:
raise RuntimeError("This method can only handle sigma_G=0, i.e.,"
"a non-conducting MEA glass electrode plane.")
xstart = cell.xstart
xend = cell.xend
ystart = cell.ystart
yend = cell.yend
zstart = cell.zstart
zend = cell.zend
x0, y0, z0 = cell.xstart, cell.ystart, cell.zstart
x1, y1, z1 = cell.xend, cell.yend, cell.zend
pos = np.array([x, y, z])
rs, closest_points = return_dist_from_segments(xstart, ystart, zstart,
xend, yend, zend, pos)
z0_ = z0.copy()
z0_[np.where(rs < r_limit)] = r_limit[np.where(rs < r_limit)]
ds = _deltaS_calc(xstart, xend, ystart, yend, zstart, zend)
factor_a = ds*ds
dx = x1 - x0
dy = y1 - y0
dz = z1 - z0
a_x = x - x0
a_y = y - y0
W = (sigma_T - sigma_S)/(sigma_T + sigma_S)
num = np.zeros(factor_a.shape)
den = np.zeros(factor_a.shape)
def _omega(a_z):
#See Rottman integration formula 46) page 137 for explanation
factor_b = - a_x*dx - a_y*dy - a_z*dz
factor_c = a_x*a_x + a_y*a_y + a_z*a_z
b_2_ac = factor_b*factor_b - factor_a * factor_c
case1_idxs = np.where(np.abs(b_2_ac) <= 1e-12)
case2_idxs = np.where(np.abs(b_2_ac) > 1e-12)
if not len(case1_idxs) == 0:
num[case1_idxs] = factor_a[case1_idxs] + factor_b[case1_idxs]
den[case1_idxs] = factor_b[case1_idxs]
if not len(case2_idxs) == 0:
num[case2_idxs] = (factor_a[case2_idxs] + factor_b[case2_idxs] +
ds[case2_idxs]*np.sqrt(factor_a[case2_idxs] +
2*factor_b[case2_idxs] + factor_c[case2_idxs]))
den[case2_idxs] = (factor_b[case2_idxs] +
ds[case2_idxs]*np.sqrt(factor_c[case2_idxs]))
return np.log(num/den)
mapping = _omega(-z0_)
n = 1
while n < steps:
mapping += W**n * (_omega(2*n*h - z0_) + _omega(-2*n*h - z0_))
n += 1
mapping *= 2/(4*np.pi*sigma_T * ds)
return mapping
[docs]def calc_lfp_soma_as_point_moi(cell, x, y, z, sigma_T, sigma_S, sigma_G,
steps, h, r_limit, **kwargs):
"""Calculate extracellular potentials for in vitro
Microelectrode Array (MEA) slices, where soma (compartment zero) is
treated as a point source, and all other compartments as line sources.
Parameters
----------
cell: obj
LFPy.Cell or LFPy.TemplateCell like instance
x : float
extracellular position, x-axis
y : float
extracellular position, y-axis
z : float
extracellular position, z-axis
sigma_T : float
extracellular conductivity in tissue slice
sigma_G : float
Conductivity of MEA glass electrode plane.
Should normally be zero for MEA set up, and for this method,
only zero valued sigma_G is supported.
sigma_S : float
Conductivity of saline bath that tissue slice is immersed in
steps : int
Number of steps to average over the in technically infinite sum
h : float
Slice thickness in um.
r_limit : np.ndarray
minimum distance to source current for each compartment
"""
if np.abs(z) > 1e-9:
raise RuntimeError("This method can only handle electrodes "
"at the MEA plane z=0")
if np.abs(sigma_G) > 1e-9:
raise RuntimeError("This method can only handle sigma_G=0, i.e.,"
"a non-conducting MEA glass electrode plane.")
xstart = cell.xstart
xend = cell.xend
ystart = cell.ystart
yend = cell.yend
zstart = cell.zstart
zend = cell.zend
x0, y0, z0 = cell.xstart, cell.ystart, cell.zstart
x1, y1, z1 = cell.xend, cell.yend, cell.zend
pos = np.array([x, y, z])
rs, closest_points = return_dist_from_segments(xstart, ystart, zstart,
xend, yend, zend, pos)
z0_ = np.array(z0)
if np.any(rs < r_limit):
z0_[rs < r_limit] = r_limit
ds = _deltaS_calc(xstart, xend, ystart, yend, zstart, zend)
factor_a = ds*ds
dx = x1 - x0
dy = y1 - y0
dz = z1 - z0
a_x = x - x0
a_y = y - y0
W = (sigma_T - sigma_S)/(sigma_T + sigma_S)
num = np.zeros(factor_a.shape)
den = np.zeros(factor_a.shape)
def _omega(a_z):
#See Rottman integration formula 46) page 137 for explanation
factor_b = - a_x*dx - a_y*dy - a_z*dz
factor_c = a_x*a_x + a_y*a_y + a_z*a_z
b_2_ac = factor_b*factor_b - factor_a * factor_c
case1_idxs = np.where(np.abs(b_2_ac) <= 1e-12)
case2_idxs = np.where(np.abs(b_2_ac) > 1e-12)
if not len(case1_idxs) == 0:
num[case1_idxs] = factor_a[case1_idxs] + factor_b[case1_idxs]
den[case1_idxs] = factor_b[case1_idxs]
if not len(case2_idxs) == 0:
num[case2_idxs] = (factor_a[case2_idxs] + factor_b[case2_idxs] +
ds[case2_idxs]*np.sqrt(factor_a[case2_idxs] +
2*factor_b[case2_idxs] + factor_c[case2_idxs]))
den[case2_idxs] = (factor_b[case2_idxs] +
ds[case2_idxs]*np.sqrt(factor_c[case2_idxs]))
return np.log(num/den)
mapping = _omega(-z0_)
n = 1
while n < steps:
mapping += W**n * (_omega(2*n*h - z0) + _omega(-2*n*h - z0))
n += 1
mapping *= 2/(4*np.pi*sigma_T * ds)
# NOW DOING SOMA
# get compartment indices for somatic compartments (to be treated as point
# sources)
try:
somainds = cell.get_idx("soma")
except Exception:
raise Exception('Call {}.get_idx("soma") failed in method LFPy.lfpcalc.calc_lfp_soma_as_point'.format(cell))
dx2 = (x - cell.xmid[somainds])**2
dy2 = (y - cell.ymid[somainds])**2
dz2 = (z - cell.zmid[somainds])**2
dL2 = dx2 + dy2
inds = np.where(dL2 + dz2 < r_limit[somainds]*r_limit[somainds])[0]
dL2[inds] = r_limit[inds]*r_limit[inds] - dz2[inds]
def _omega(dz):
return 1/np.sqrt(dL2 + dz*dz)
mapping[somainds] = _omega(z - cell.zmid[somainds])
mapping[somainds] += (W * _omega(cell.zmid[somainds] - 2*h) +
_omega(cell.zmid[somainds]))
n = np.arange(1, steps)
a = (W)**n[:, None] * (W * _omega(+ cell.zmid[somainds] - 2*(n[:, None] + 1)*h) +
2 * _omega(+ cell.zmid[somainds] + 2*n[:, None]*h) +
_omega(+ cell.zmid[somainds] - 2*n[:, None]*h))
mapping[somainds] += np.sum(a, axis=0)
mapping[somainds] *= 1/(4*np.pi*sigma_T)
return mapping