Source code for netpyne.support.recxelectrode

"""
Module with RecXElectrode from Allen Brain Institute

"""

# Allen Institute Software License - This software license is the 2-clause BSD license plus clause a third
# clause that prohibits redistribution for commercial purposes without further permission.
#
# Copyright 2017. Allen Institute. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Redistributions for commercial purposes are not permitted without the Allen Institute's written permission. For
# purposes of this license, commercial purposes is the incorporation of the Allen Institute's software into anything for
# which you will charge fees or other compensation. Contact terms@alleninstitute.org for commercial licensing
# opportunities.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Adapted to NetPyNE by salvadordura@gmail.com
#

import numpy as np
import math


[docs] class RecXElectrode(object): """Extracellular electrode""" def __init__(self, locations): try: self.pos = locations assert len(self.pos.shape) == 2 assert self.pos.shape[0] == 3 self.pos[1, :] *= -1 # invert y-axis since by convention assume it refers to depth (eg cortical depth) self.nsites = self.pos.shape[0] self.transferResistances = {} except: print('Error creating extracellular electrode: sim.cfg.recordLFP should contain a list of x,y,z locations') return None self.nsites = self.pos.shape[1] self.transferResistances = {} # V_e = transfer_resistance*Im
[docs] def getTransferResistance(self, gid): return self.transferResistances[gid]
[docs] def calcTransferResistance(self, gid, seg_coords): """Precompute mapping from segment to electrode locations""" sigma = 0.3 # mS/mm # Value used in NEURON extracellular recording example ("extracellular_stim_and_rec") # rho = 35.4 # ohm cm, squid axon cytoplasm = 2.8249e-2 S/cm = 0.028 S/cm = 0.0028 S/mm = 2.8 mS/mm # rho_um = 35.4 * 0.01 = 35.4 / 1e6 * 1e4 = 0.354 Mohm um ~= 3 uS / um = 3000 uS / mm = 3 mS /mm # equivalent sigma value (~3) is 10x larger than Allen (0.3) # if use same sigma value, results are consistent r05 = (seg_coords['p0'] + seg_coords['p1']) / 2 dl = seg_coords['p1'] - seg_coords['p0'] nseg = r05.shape[1] tr = np.zeros((self.nsites, nseg)) # tr_NEURON = np.zeros((self.nsites,nseg)) # used to compare with NEURON extracellular example for j in range(self.nsites): # calculate mapping for each site on the electrode rel = np.expand_dims(self.pos[:, j], axis=1) # coordinates of a j-th site on the electrode rel_05 = rel - r05 # distance between electrode and segment centers r2 = np.einsum( 'ij,ij->j', rel_05, rel_05 ) # compute dot product column-wise, the resulting array has as many columns as original rlldl = np.einsum( 'ij,ij->j', rel_05, dl ) # compute dot product column-wise, the resulting array has as many columns as original dlmag = np.linalg.norm(dl, axis=0) # length of each segment rll = abs(rlldl / dlmag) # component of r parallel to the segment axis it must be always positive rT2 = r2 - rll**2 # square of perpendicular component up = rll + dlmag / 2 low = rll - dlmag / 2 num = up + np.sqrt(up**2 + rT2) den = low + np.sqrt(low**2 + rT2) tr[j, :] = np.log(num / den) / dlmag # units of (1/um) use with imemb_ (total seg current) # Consistent with NEURON extracellular recording example # r = np.sqrt(rel_05[0,:]**2 + rel_05[1,:]**2 + rel_05[2,:]**2) # tr_NEURON[j, :] = (rho / 4 / math.pi)*(1/r)*0.01 tr *= 1 / (4 * math.pi * sigma) # units: 1/um / (mS/mm) = mm/um / mS = 1e3 * kOhm = MOhm self.transferResistances[gid] = tr
[docs] def fromConfig(cfg): # convert coordinates to ndarray, The first index is xyz and the second is the channel number return RecXElectrode(np.array(cfg.recordLFP).T)
[docs] def toJSON(self): pos = self.pos.tolist() resistances = {} for ind, res in self.transferResistances.items(): resistances[ind] = res.tolist() return {'pos': pos, 'resistances': resistances}
[docs] def fromJSON(raw): locs = np.array(raw['pos']) instance = RecXElectrode(locs) rawRes = raw.get('resistances', {}) for ind, res in rawRes.items(): instance.transferResistances[ind] = np.array(res) return instance