"""
This script shapes the class from which our neurons will be built.
Neurons will have the following attributes:
* **MorphoName**: Morphology name, it must be one of the following: `n128`, `sup1`, `n409` or `n127`
* **MorphoData**: Dataset containing all the morphological data
* **SomaList**: List of NEURON somatic ``Sections``
* **AxonList**: List of NEURON axonic ``Sections``
* **DendList**: List of NEURON Basal dendritic ``Sections``
* **ApicList**: List of NEURON apical dendritic ``Sections``
* **SomApicList**: List of somatoapical NEURON apical dendritic ``Sections``
* **SectionsList**: List of all NEURON ``Sections``, the union of :attr:`SomaList`, :attr:`AxonList`, :attr:`DendList`, :attr:`ApicList`
* **TopolDict**: Dictionary with all topological information in the form
* **CurrentObject**: List of NEURON objects containing all current clamps information
Access to all these properties is possible by:
.. code-block:: python
Eg.:
>>> Pyramidal = neuron_class(MorphoName = CELLPROP_MORPHOLOGY,
IntrinsicFactors = IntrinsicFactors,
SynapticFactors = SynapticFactors,
CurrentFactors = CurrentFactors,
DirLocation = DIR_LOCATION )
>>> print Pyramidal.MorphoName
sup1
The way the neuron is built is following these steps:
1. **Import and set morphological data** from ./LCNhm-neurondata/``CELLPROP_MORPHOLOGY``.swc
These .swcs cointain information about the 3D structure and width of the cell.
You can obtain more morphologies on the public databas `NeuroMorpho <http://neuromorpho.org/>`_
Classification into four main morphological categories (soma, axon, basal and apical dendrites).
Every category will be compartimentalized in multiple sections, each of which will have different and specific
ion channels, synaptic inputs, diameter, spines, etc... For instance, one apical dendrite that starts on the somatoapical trunk,
will be compartimentalized in 10 sections, where the nearest section to the trunk will be thicker than the furthest.
#. **Set biophysics** (ion channels, membrane capacitance, membrane resistivity, etc...)
What ion channels to use can be chosen, as well as a factor to increase/decrease the conductance density.
See :data:`LCNhm_configurationfile.CELLPROP_INTRINSIC_IONCHS` and :data:`LCNhm_configurationfile.CELLPROP_INTRINSIC_EXPERIMENT`:
for further details.
#. **Set synapses** (excitatory/inhibitory inputs, place of synaptic boutons, maximum conductance, etc...)
Synaptic inputs can be chosen, as well as a factor to increase/decrease the maximum conductance.
See :data:`LCNhm_configurationfile.CELLPROP_SYNAPTIC_INPUTS` and :data:`LCNhm_configurationfile.CELLPROP_SYNAPTIC_EXPERIMENT`:
for further details.
#. **Set current clamp** (place, duration, intensity, etc...)
A square-pulse clamp is set according to parameters written in :ref:`LCNhm-configuration-file`.
See :data:`LCNhm_configurationfile.CURRENT_DURATION`, :data:`LCNhm_configurationfile.CURRENT_DELAY`, :data:`LCNhm_configurationfile.CURRENT_AMPLITUDES`, :data:`LCNhm_configurationfile.CURRENT_SECTION`, :data:`LCNhm_configurationfile.CURRENT_LOCATION`, for further details.
"""
import sys
import math
import warnings
import numpy as np
import pandas as pd
from neuron import h, nrn, gui
from LCNhm_functions import *
class neuron_class(object):
"""
Neurons will be built as `neuron_class` objects.
Parameters
----------
MorphoName: String
Morphology name, it must be one of the following: `n128`, `sup1`, `n409` or `n127`
Defined in :const:`LCNhm_configurationfile.CELLPROP_MORPHOLOGY`
IntrinsicFactors: List
List of all current properties
Defined in :const:`LCNhm_main.IntrinsicFactors`
SynapticFactors: List
List of all intrinsic properties
Defined in :const:`LCNhm_main.SynapticFactors`
CurrentFactors: List
List of all synaptic properties
Defined in :const:`LCNhm_main.CurrentFactors`
DirLocation: String
Precise location of the main directory
Returns
-------
``neuron_class`` object: Class object
Attributes
----------
MorphoName: String
Morphology name, it must be one of the following: `n128`, `sup1`, `n409` or `n127`
Defined in :const:`LCNhm_configurationfile.CELLPROP_MORPHOLOGY`
It is one of the :class:`neuron_class` inputs
MorphoData: csv
Dataset containing all the morphological data
It describes each morphological point with six parameters:
* ``Type``: soma (1), axon (2), Basal (3) or apical (4) dendrite
* ``x`` , ``y`` , ``z``: Spatial coordinates (in micrometers)
* ``d``: diameter (in micrometers)
* ``IDFather``: Line number of the morphological point to which is connected
See :func:`make_geometry_dictionary` to see how it is used
Source file in *../LCNhm-neurondata/ <MorphoName> .swc*
SomaList: List
List of NEURON somatic ``Sections``
The number of ``Sections`` is derived from :attr:`MorphoData`
AxonList: List
List of NEURON axonic ``Sections``
The number of ``Sections`` is derived from :attr:`MorphoData`
DendList: List
List of NEURON Basal dendritic ``Sections``
The number of ``Sections`` is derived from :attr:`MorphoData`
ApicList: List
List of NEURON apical dendritic ``Sections``
The number of ``Sections`` is derived from :attr:`MorphoData`
SomApicList: List
List of somatoapical NEURON apical dendritic ``Sections``
List can be found in *./LCNhm-neurondata/somatoapical_sections.txt*
SectionsList: List
List of all NEURON ``Sections``, the union of :attr:`SomaList`, :attr:`AxonList`, :attr:`DendList`, :attr:`ApicList`
TopolDict: Dictionary
Dictionary with all topological information in the form
[ [ SecListName[SecNumber] , Who is connected to (IDFather) , x , y , z ] ] , [...], ... ]
Eg.:
.. code-block:: python
# NEURON object name ID of father x y z
TopolDict = { SomaList[0]: ['SomaList[0]', -1, 0, 0, 0],
SomaList[1]: ['SomaList[1]', ID of SomaList[0], 0, 0, -2],
...
ApicList[9]: ['ApicList[9]', ID of ApicList[2], 0, 0, -1000]}
CurrentObject: List
List of NEURON objects containing all current clamps information
[[ Clamp object 1, Duration 1, Delay 1, Amplitude 1 ],
[ Clamp object 2, Duration 2, Delay 2, Amplitude 2 ], [...], ...... ]
Eg.:
.. code-block:: python
# Object dur del amp
CurrentObject = [ [ NEURON object at soma, 100, 0, 0],
[ NEURON object at soma, 200, 300, 0],
...
[ NEURON object at soma, 200, 1200, 0]]
"""
# Atributes and callings to methods
def __init__(self, MorphoName, IntrinsicFactors, SynapticFactors, CurrentFactors, DirLocation):
global DirLoc; DirLoc = DirLocation
# IMPORTING DATA
# ==============
# Name of the morphology
self.MorphoName = MorphoName
# Dataset containing all the morphological data
self.MorphoData = pd.read_csv('%s/LCNhm-neurondata/%s.swc'%(DirLoc,MorphoName),
names = ['Type','x','y','z','d','IDFather'],
delimiter = ' ', index_col = 0)
# SECTIONS
# ========
# Initialization and group in lists of the four main class of sections: soma, axon, Basal (dend) and apical (apic) dendrites
self.SomaList, self.AxonList, self.DendList, self.ApicList, self.SomApicList = self.init_sections(self.MorphoData)
# All sections in one list
self.SectionsList = self.SomaList+self.AxonList+self.DendList+self.ApicList
# GEOMETRY AND TOPOLOGY
# =====================
# TopolDict is the dictionary with the geometry
self.CellPosition = [0, 0, 0, 0]
self.TopolDict = self.make_geometry_dictionary(self.CellPosition, self.MorphoData)
# Making the connections
self.set_geometry(self.TopolDict)
# BIOPHYSICS
# ==========
# We define the biophysics
self.set_intrinsic_properties(IntrinsicFactors)
# SYNAPSES
# ========
self.SynapticFactors = SynapticFactors
#self.SynDict, self.synObj, self.NetConObjects = self.SetSynapses(self.SynapticFactors)
if len(SynapticFactors[0])>0: self.SynDict, self.SynObjects, self.NetConObjects = self.set_synaptic_properties(SynapticFactors)
# ICLAMP
# ======
self.CurrentObject = self.set_current_clamp(CurrentFactors)
def init_sections(self, MorphoData):
"""
Initialization and group in lists of the four main class of sections: soma, axon, Basal (dend) and apical (apic) dendrites
Parameters
----------
MorphoData: csv
:class:`neuron_class`'s :attr:`MorphoData` attribute: Dataset containing all the morphological data
It describes each morphological point with six parameters:
* ``Type``: soma (1), axon (2), Basal (3) or apical (4) dendrite
* ``x`` , ``y`` , ``z``: Spatial coordinates (in micrometers)
* ``d``: diameter (in micrometers)
* ``IDFather``: Line number of the morphological point to which is connected
See :func:`make_geometry_dictionary` to see how it is used
Source file in *../LCNhm-neurondata/ <MorphoName> .swc*
Returns
-------
SomaList: List
class:`neuron_class`'s :attr:`SomaList` attribute: List of NEURON somatic ``Sections``
The number of ``Sections`` is derived from :attr:`MorphoData`
AxonList: List
class:`neuron_class`'s :attr:`AxonList` attribute: List of NEURON axonic ``Sections``
The number of ``Sections`` is derived from :attr:`MorphoData`
DendList: List
class:`neuron_class`'s :attr:`DendList` attribute: List of NEURON Basal dendritic ``Sections``
The number of ``Sections`` is derived from :attr:`MorphoData`
ApicList: List
class:`neuron_class`'s :attr:`ApicList` attribute: List of NEURON apical dendritic ``Sections``
The number of ``Sections`` is derived from :attr:`MorphoData`
SomApicList: List
class:`neuron_class`'s :attr:`SomApicList` attribute: List of somatoapical NEURON apical dendritic ``Sections``.
List can be found in `./LCNhm-neurondata/somatoapical_sections.txt`.
"""
# We count how many lines have Type 1 (SomaList)
numsoma = sum([1 for i in MorphoData.index if (MorphoData['Type'][i] == 1) and ((MorphoData['IDFather'][i] != i-1) or (MorphoData['Type'][MorphoData['IDFather'][i]] != MorphoData['Type'][i]))])
# We count how many lines have Type 2 (AxonList)
numaxon = sum([1 for i in MorphoData.index if (MorphoData['Type'][i] == 2) and ((MorphoData['IDFather'][i] != i-1) or (MorphoData['Type'][MorphoData['IDFather'][i]] != MorphoData['Type'][i]))])
# We count how many lines have Type 3 (DendList)
numdend = sum([1 for i in MorphoData.index if (MorphoData['Type'][i] == 3) and ((MorphoData['IDFather'][i] != i-1) or (MorphoData['Type'][MorphoData['IDFather'][i]] != MorphoData['Type'][i]))])
# We count how many lines have Type 4 (ApicList)
numapic = sum([1 for i in MorphoData.index if (MorphoData['Type'][i] == 4) and ((MorphoData['IDFather'][i] != i-1) or (MorphoData['Type'][MorphoData['IDFather'][i]] != MorphoData['Type'][i]))])
# We create four section lists: soma, axon, Basal and apical dendrites
SomaList = [h.Section(name = 'soma', cell = self) for i in range(numsoma)]
AxonList = [h.Section(name = 'axon', cell = self) for i in range(numaxon)]
DendList = [h.Section(name = 'dend', cell = self) for i in range(numdend)]
ApicList = [h.Section(name = 'apic', cell = self) for i in range(numapic)]
# We create a complmentary section list: somato-apical dendrite
with open(DirLoc+'/LCNhm-neurondata/somatoapical_sections.txt') as file:
SomApicList = [ApicList[[int(line.split()[1]) for line in file if (line.split()[0] == self.MorphoName)][0]]]
# Return
return SomaList, AxonList, DendList, ApicList, SomApicList
def make_geometry_dictionary(self, CellPosition, MorphoData):
"""
Make a legible dictionary from the swc ``MorphoData`` database
Parameters
----------
CellPosition: List of Floats
List of four elements
* x-, y-, z-: spatial position of the soma (in micrometers)
* angle: self-rotation around its main axis (in degrees)
MorphoData: csv
:class:`neuron_class`'s :attr:`MorphoData` attribute: Dataset containing all the morphological data
It describes each morphological point with six parameters:
* ``Type``: soma (1), axon (2), Basal (3) or apical (4) dendrite
* ``x`` , ``y`` , ``z``: Spatial coordinates (in micrometers)
* ``d``: diameter (in micrometers)
* ``IDFather``: Line number of the morphological point to which is connected
See :func:`make_geometry_dictionary` to see how it is used
Source file in *../LCNhm-neurondata/ <MorphoName> .swc*
Returns
-------
TopolDict: Dictionary
:class:`neuron_class`'s :attr:`TopolDict` attribute: Dictionary with all topological information in the form
[ [ SecListName[SecNumber] , Who is connected to (IDFather) , x , y , z ] ] , [...], ... ]
Eg.:
.. code-block:: python
# NEURON object name ID of father x y z
TopolDict = { SomaList[0]: ['SomaList[0]', -1, 0, 0, 0]
SomaList[1]: ['SomaList[1]', ID of SomaList[0], 0, 0, -2],
...
ApicList[9]: ['ApicList[9]', ID of ApicList[2], 0, 0, -1000]}
"""
# Unpack x,y,z,rotation parameters from CellPosition
X, Y, Z, SelfRotation = CellPosition
class Point(object):
"""
Each point (compartment) in the neuron will be a ``Point`` class
Attributes
----------
ID: Int
Line number in the MorphoData csv
Type: Int
NEURON ``Section`` Type: 1 (soma), 12 (axon), 3 (dend), 4 (apic)
coords:
Coordinates from the MorphoData csv, transformed by CellPosition
IDFather:
Line number in the MorphoData csv from which this compartment starts
"""
def __init__(self, ID, Type, coords, sect_num, IDFather, isFather):
self.ID = ID
# SomaList, AxonList, Basal Dendrite, Apical Dendrite
self.Type = Type
# Rotation
self.coords = np.array([math.cos(SelfRotation)*coords[0]-math.sin(SelfRotation)*coords[1],
math.sin(SelfRotation)*coords[0]+math.cos(SelfRotation)*coords[1],
coords[2],
coords[3]])
# Translation
self.coords += np.array([X, Y, Z, 0])
self.sect_num = sect_num
self.IDFather = IDFather
self.isFather = isFather
points = []
numsoma = -1
numaxon = -1
numdend = -1
numapic = -1
# List of all points
for ID in MorphoData.index:
# Type and [x,y,z,diam]
Type = MorphoData['Type'][ID]
coords = [MorphoData['x'][ID] , MorphoData['y'][ID] , MorphoData['z'][ID] , MorphoData['d'][ID]]
IDFather = MorphoData['IDFather'][ID]
# Is it a father?
isFather = True if (MorphoData['IDFather'][ID] != (ID-1)) or (MorphoData['Type'][MorphoData['IDFather'][ID]] != Type) else False
# If it is a father, we include in a new subsection of the section
if Type == 1: numsoma += 1*isFather; sect_num = numsoma # SomaList
if Type == 2: numaxon += 1*isFather; sect_num = numaxon # AxonList
if Type == 3: numdend += 1*isFather; sect_num = numdend # Basal Dendrites
if Type == 4: numapic += 1*isFather; sect_num = numapic # Apical Dendrites
# Add point to list
points.append(Point(ID, Type, coords, sect_num, IDFather, isFather ))
# Now we make the dictionary:
TopolDict = {}
# For each point:
for point in points:
# What section Type is it?
if point.Type == 1: sect = self.SomaList[point.sect_num]
elif point.Type == 2: sect = self.AxonList[point.sect_num]
elif point.Type == 3: sect = self.DendList[point.sect_num]
elif point.Type == 4: sect = self.ApicList[point.sect_num]
# If it is a father, we create a new key in the dictionary
if point.isFather:
# Point's section name
if point.Type == 1: TopolDict[sect] = ['SomaList[%d]'%point.sect_num]
elif point.Type == 2: TopolDict[sect] = ['AxonList[%d]'%point.sect_num]
elif point.Type == 3: TopolDict[sect] = ['DendList[%d]'%point.sect_num]
elif point.Type == 4: TopolDict[sect] = ['ApicList[%d]'%point.sect_num]
# The first line of the SWC has a "-1" IDFather
if point.IDFather == -1:
grandfather = False
type_grandfather = False
else:
# Where does it come from? As it is a father, grandfather is its father
ID_grandfather = point.IDFather-1 # "-1" because starts counting on 1
type_grandfather = points[ID_grandfather].Type
num_grandfather = points[ID_grandfather].sect_num
# Grandfather section
if type_grandfather == False: TopolDict[sect].append(self.SomaList[point.sect_num])
elif type_grandfather == 1:
TopolDict[sect].append(self.SomaList[num_grandfather])
TopolDict[sect].append([pt.ID for pt in points if (point.ID == pt.IDFather)][0])
elif type_grandfather == 2:
TopolDict[sect].append(self.AxonList[num_grandfather])
TopolDict[sect].append([pt.ID for pt in points if (point.ID == pt.IDFather)][0])
elif type_grandfather == 3:
TopolDict[sect].append(self.DendList[num_grandfather])
TopolDict[sect].append([pt.ID for pt in points if (point.ID == pt.IDFather)][0])
elif type_grandfather == 4:
TopolDict[sect].append(self.ApicList[num_grandfather])
TopolDict[sect].append([pt.ID for pt in points if (point.ID == pt.IDFather)][0])
# Coordinates of the Point
TopolDict[sect].append(point.coords)
else:
TopolDict[sect].append(point.coords)
# Once the dictionary is done, we erase the inapropiate dendrites listed in ./LCNhm-neurondata/dendrites_to_erase.txt
with open(DirLoc+'/LCNhm-neurondata/dendrites_to_erase.txt' ) as file:
Dend2Erase = [map(int, line.split()[1:]) for line in file if line.split()[0] == self.MorphoName][0]
self.DendList = [self.DendList[ii] for ii in range(len(self.DendList)) if ii not in Dend2Erase]
return TopolDict
def set_geometry(self, TopolDict):
"""
Set the topology and geometry from the :attr:`TopolDict` dictionary.
* First, the beginning of each ``Section`` is connected to the end of its "father" ``Section``, defined in the :attr:`MorphoData` csv
* Second, it sets each compartment position and diameter
Parameters
----------
TopolDict: Dictionary
:class:`neuron_class`'s :attr:`TopolDict` attribute: Dictionary with all topological information in the form
[ [ SecListName[SecNumber] , Who is connected to (IDFather) , x , y , z ] ] , [...], ... ]
Eg.:
.. code-block:: python
# NEURON object name ID of father x y z
TopolDict = { SomaList[0]: ['SomaList[0]', -1, 0, 0, 0]
SomaList[1]: ['SomaList[1]', ID of SomaList[0], 0, 0, -2],
...
ApicList[9]: ['ApicList[9]', ID of ApicList[2], 0, 0, -1000]}
"""
for key in TopolDict.keys():
# The first section (SomaList[0]) doesn't need to be connected
if key != self.SomaList[0]:
key.connect(TopolDict[key][1])
# Once all sections are connected, we define its position and diameter:
h.pop_section()
for key in TopolDict.keys():
if key in self.SomApicList:
numsegs = 15
key.push()
h.pt3dclear()
pointList = []
for i in range(3, len(TopolDict[key])):
# If it's a list, then add that 3d point
pointList.append([TopolDict[key][i][0], TopolDict[key][i][1], TopolDict[key][i][2], TopolDict[key][i][3]])
ID_compartment = np.array([float(i)/numsegs for i in range(numsegs) ])*len(pointList)
for idc in ID_compartment:
xi, yi, zi, di = pointList[int(idc)]
h.pt3dadd(xi, yi, zi, di)
h.pop_section()
key.nseg = numsegs
continue
else:
key.push()
h.pt3dclear()
pointList = []
for i in range(3, len(TopolDict[key])):
# If it's a list, then add that 3d point
pointList.append([TopolDict[key][i][0], TopolDict[key][i][1], TopolDict[key][i][2], TopolDict[key][i][3]])
if len(pointList) < 2: numsegs = 1
else: numsegs = 3
ID_compartment = np.array([float(i)/numsegs for i in range(numsegs) ])*len(pointList)
for idc in ID_compartment:
xi, yi, zi, di = pointList[int(idc)]
h.pt3dadd(xi, yi, zi, di)
h.pop_section()
key.nseg = numsegs
def set_intrinsic_properties(self, IntrinsicFactors):
"""
Set intrinsic properties: set axial resistance, fill the neuron's surface with ion channels, and set their dynamical parameters
Final factors are defined as the multiplication of the *individual* factor by the *experiment* factor, so that
.. code-block:: python
FinalFactors = FactorsIndividual * FactorsExperiment
fNa, fA, fAHPs, fC, fKDR, fM, fCaL, fCaT, fHCN, fL, Ra = FinalFactors
It is crucial to set the *individual* and *experiment* factors in the required order
Parameters
----------
IntrinsicFactors: List
:class:`neuron_class`'s :data:`LCNhm_main.IntrinsicFactors` input: List of all intrinsic properties
* :data:`LCNhm_configurationfile.CELLPROP_INTRINSIC_IONCHS`: Ion channels to include in the cell
* :data:`LCNhm_configurationfile.CELLPROP_INTRINSIC_EXPERIMENT`: Additional factor multiplying the following maximum conductances and axial resistance
* :data:`LCNhm_configurationfile.CELLPROP_INTRINSIC`: Number of the intrinsic genetic-algorithm `individual`
"""
# CELLPROP_INTRINSIC_IONCHS as IonChannels
# CELLPROP_INTRINSIC_EXPERIMENT as FactorsExperiment
# CELLPROP_INTRINSIC as FactorsNum
IonChannels, FactorsExperiment, FactorsNum = IntrinsicFactors
# Get 'individual' factors
with open(DirLoc+'/LCNhm-neurondata/intrinsic_individuals.txt') as file:
FactorsIndividual = map(float, [line.split() for k, line in enumerate(file) if k==(FactorsNum-1) ][0])
# Multiplication of the 'individual' factor by the 'experiment' factor
FinalFactors = [ FactorsIndividual[ii] * FactorsExperiment[ii] for ii in range(len(FactorsExperiment))]
# Separate all different factors
fNa, fA, fAHPs, fC, fKDR, fM, fCaL, fCaT, fHCN, fL, Ra = FinalFactors
# Somatic z-position (along radial axis)
zSoma = h.z3d(0,sec=self.SomaList[0])
# Intrinsic Passive Properties
for sect in self.SectionsList:
sect.insert('pas')
sect.insert('extracellular')
sect.insert('iL')
for seg in range(sect.nseg):
loc = float(seg+1)/(sect.nseg+1) # Location in segment
sect.Ra = Ra # Internal resistivity (Ra), in Ohm*cm
sect.e_pas = -50 # Membrane rest potential
sect(loc).gl_iL = 0.0025*fL # Leak conductance
SS = gradient_spine_scale(zDistance=h.z3d(seg,sec=sect)-zSoma, Diameter=h.diam3d(seg,sec=sect)) # Spine scale
sect.cm = 5 if sect in self.SomaList else 1.8*5*SS # Membrane capacitance (microFarad/cm2)
# Specific membrane resistivity (Rm), or passive conductivity (gpas)
if sect in self.SomaList:
sect(loc).g_pas = 1./gradient_membrane_resistance(0.0) # S/cm2
elif sect in self.DendList+self.AxonList:
sect(loc).g_pas = SS/gradient_membrane_resistance(0.0) # S/cm2
else:
sect.g_pas = SS/gradient_membrane_resistance(h.z3d(0,sec=sect)-zSoma) # S/cm2
# HCN channels
if 'hcn' in IonChannels:
for sect in self.ApicList:
sect.insert('hcn')
sect.ehcn_hcn = -30.
sect.Ft_hcn = 1.
zDistance = h.z3d(0,sec=sect)-zSoma
sect.ghcn_hcn = gradient_gHCN(zDistance)*fHCN # S/cm2
sect.V12_hcn = gradient_V12(zDistance) # mV
# A-Type K+ channels. Potassium conductance in S/cm2
if 'iA' in IonChannels:
for sect in self.SomaList+self.DendList+self.ApicList: sect.insert('iA')
for sect in self.SomaList: sect.gkbar_iA = 0.0025*fA
for sect in self.DendList: sect.gkbar_iA = 0.0600*fA
for sect in self.ApicList: sect.gkbar_iA = 0.0600*fA
# CA2+ dependent slow AHP K+ conductance. Potassium conductance in S/cm2
if 'iAHPs' in IonChannels:
for sect in self.SomaList+self.DendList+self.ApicList: sect.insert('iAHPs')
for sect in self.SomaList: sect.gkbar_iAHPs = 0.0005*5*fAHPs
for sect in self.DendList: sect.gkbar_iAHPs = 0.0005*fAHPs
for sect in self.ApicList: sect.gkbar_iAHPs = 0.0005*fAHPs
# short-duration [Ca]- and voltage-dependent current. Potassium conductance in S/cm2
if 'iC' in IonChannels:
for sect in self.SomaList+self.DendList+self.ApicList: sect.insert('iC')
for sect in self.SomaList: sect.gkbar_iC = 0.09075*fC
for sect in self.DendList: sect.gkbar_iC = 0.03300*fC
for sect in self.ApicList:
if h.z3d(0,sec=sect)-zSoma <= 200.: sect.gkbar_iC = 0.03300*fC
elif h.z3d(0,sec=sect)-zSoma <= 350.: sect.gkbar_iC = 0.00410*fC
# Short-duration [Ca]- and voltage-dependent current. Calcium conductance in S/cm2
if 'iCaL' in IonChannels:
for sect in self.SomaList: sect.insert('iCaLs')
for sect in self.DendList+self.ApicList: sect.insert('iCaLd')
for sect in self.SectionsList: sect.insert('cad')
for sect in self.SomaList: sect.gcalbar_iCaLs = 0.000700000*fCaL; sect.tau_cad = 50
for sect in self.DendList: sect.gcalbar_iCaLd = 0.000031635*fCaL; sect.tau_cad = 20
for sect in self.ApicList: sect.gcalbar_iCaLd = 0.000031635*fCaL; sect.tau_cad = 20
for sect in self.AxonList: sect.tau_cad = 20
# Short-duration [Ca]- and voltage-dependent current. Calcium conductance in S/cm2
if 'iCaT' in IonChannels:
for sect in self.SomaList+self.DendList+self.ApicList: sect.insert('iCaT')
for sect in self.SectionsList: sect.insert('cad')
for sect in self.SomaList: sect.gcatbar_iCaT = 0.00005*fCaT; sect.tau_cad = 50
for sect in self.DendList: sect.gcatbar_iCaT = 0.00001*fCaT; sect.tau_cad = 20
for sect in self.ApicList: sect.gcatbar_iCaT = 0.00001*fCaT; sect.tau_cad = 20
for sect in self.AxonList: sect.tau_cad = 20
# Delay rectifier current. Potassium conductance in S/cm2
if 'iKDR' in IonChannels:
for sect in self.SectionsList: sect.insert('iKDR')
for sect in self.SomaList: sect.gkbar_iKDR = 0.001400*fKDR
for sect in self.AxonList: sect.gkbar_iKDR = 0.020000*fKDR
for sect in self.DendList: sect.gkbar_iKDR = 0.000868*fKDR
for sect in self.ApicList: sect.gkbar_iKDR = 0.000868*fKDR
# Slowly activating voltage-dependent potassium current. Potassium conductance in S/cm2
if 'iM' in IonChannels:
for sect in self.SectionsList: sect.insert('iM')
for sect in self.SomaList: sect.gkbar_iM = 0.06*fM
for sect in self.AxonList: sect.gkbar_iM = 0.03*fM
for sect in self.DendList: sect.gkbar_iM = 0.06*fM
for sect in self.ApicList: sect.gkbar_iM = 0.06*fM
# Sodium conductance. Sodium conductance in S/cm2
if 'iNas' in IonChannels:
for sect in self.SectionsList: sect.insert('iNas')
for sect in self.SomaList: sect.gnabar_iNas = 0.007*10*fNa; sect.Frec_iNas = 1.0
for sect in self.AxonList: sect.gnabar_iNas = 0.007*10*fNa; sect.Frec_iNas = 1.0
for sect in self.DendList: sect.gnabar_iNas = 0.007*fNa; sect.Frec_iNas = 1.0
for sect in self.ApicList: sect.gnabar_iNas = 0.007*fNa; sect.Frec_iNas = 1.0
def set_synaptic_properties(self, SynapticFactors):
"""
Set synaptic properties written in
*../LCHhm-neurondata/synaptic_properties.txt* (Bezaire2016)
Each line of the file has a list of all the necessary
properties to configure synapses. Let's take a line as
an example:
Eg.: (1) **CA3** (2) **6209** (3) **4** (4) **50** (5) **300** (6) **1.5** (7) **0** (8) **0.5** (9) **3** (10) **0.0002** (11) **0.5** (12) **276** (13) **5** (14) **3**
These parameters are, in order:
1. Name of input.
Eg.: CA3
2. Number of NumBoutons.
Eg.: 6209
3. Type of ``Sections`` where to place NumBoutons.
Eg.: 4, that is apical dendrites
4. Minimum distance to soma (positive to apical, negative to Basal).
Eg.: 50 um from soma, that would be proximal SR
5. Maximum distance to soma (positive to apical, negative to Basal).
Eg.: 300 um from soma, that would be distal SR
6. Firing frequency (Hz).
Eg.: 1.5 Hz
7. Reversal potential (mV). Near 0mV would correspond to a glutamatergic input, while a -70mV to a GABAergic one.
Eg.: 0 mV, that is excitatory
8. Raising time constant (ms).
Eg.: 0.5 ms
9. Decaying time constant (ms).
Eg.: 3.0 ms
10. Maximum conductance (microsiemens, uS). This is later multiplied by the ``FinalFactors`` defined from the ``FactorsIndividual`` and the ``Experimental``.
Eg.: 0.0002 uS
11. Basal firing of the synaptic time probability distribution.
Eg.: 0.5
12. Theta Phase of maximum spiking probability.
Eg.: 276 deg
13. Left-shifting factor of the synaptic time probability distribution (A in the picture).
Eg.: 5
14. Right-shifting factor of the synaptic time probability distribution (B in the picture).
Eg.: 3
The last four parameters define the synaptic time probability distribution
through the :func:`LCNhm_functions.synaptic_time_probability_distribution`,
an asymmetric gaussian function. An example of the synaptic time probability
distribution alon 10 theta cycles, and a visual definition of the
``Basal``, ``A`` and ``B`` parameters are shown in the image:
.. image:: ../sphinx-docs/_images/SynapseDistribution.png
In the case that :func:`LCNhm_configurationfile.SIMPROP_THETA_MODE` is ``False``,
the distribution will be homogeneous along time, being (6), the firing frequency,
the only parameter that will be taken into account.
So for each excitatory/inhibitory input:
1. An amount of (2) boutons are placed randomly along the neuron surface from (5) to (6)
2. Synapse internal dynamics are set given (7), (8), (9), (10) and (11)
3. Synapses are activated randomly with the probability distribution given by :func:`LCNhm_functions.synaptic_time_probability_distribution`
Final factors are defined as the multiplication of the *individual* factor by the *experiment* factor, so that
.. code-block:: python
FinalFactors = FactorsIndividual * FactorsExperiment
fNa, fA, fAHPs, fC, fKDR, fM, fCaL, fCaT, fHCN, fL, Ra = FinalFactors
It is crucial to set the *individual* and *experiment* factors in the required order
Parameters
----------
SynapticFactors: List
* :const:`LCNhm_configurationfile.CELLPROP_SYNAPTIC_INPUTS` : Synaptic inputs to include in the cell
* :const:`LCNhm_configurationfile.CELLPROP_SYNAPTIC_EXPERIMENT` : Additional factor multiplying the following maximum conductances
* :const:`LCNhm_configurationfile.SIMPROP_THETA_MODE` : Set theta (``True``) or not (``False``)
* :const:`LCNhm_configurationfile.SIMPROP_THETA_PERIOD` : Theta period in milliseconds
* :const:`LCNhm_configurationfile.SIMPROP_START_TIME` : Lapse of time before starting the simulation in milliseconds
* :const:`LCNhm_configurationfile.SIMPROP_END_TIME` : Total duration of the simulation in milliseconds (:const:`LCNhm_configurationfile.SIMPROP_START_TIME` + :const:`LCNhm_configurationfile.SIMPROP_SIM_TIME`)
Returns
-------
SynDict: Dictionary
Dictionary with information of all synapses. For each input in :const:`LCNhm_configurationfile.CELLPROP_SYNAPTIC_INPUTS`,
* ``SynSection``: NEURON ``Section`` and ``Location`` for each bouton
* ``SynPlaces``: Three spatial coordinates for each bouton
* ``SynTimes``: Time releases for each bouton
is stored
SynObjects: List
NEURON synaptic ``Exp2Syn`` objects (go to `Exp2Syn <https://www.neuron.yale.edu/neuron/static/py_doc/modelspec/programmatic/mechanisms/mech.html?highlight=exp2syn#Exp2Syn>`_ for more information)
NetConObjects: List
NEURON synaptic ``NetCon`` objects (go to `NetCon <https://www.neuron.yale.edu/neuron/static/py_doc/modelspec/programmatic/network/netcon.html?highlight=netcon>`_ for more information)
"""
# CELLPROP_SYNAPTIC_INPUTS as SynInputs
# CELLPROP_SYNAPTIC_EXPERIMENT as FactorsExperiment
# SIMPROP_THETA_MODE as isTheta
# SIMPROP_THETA_PERIOD as ThetaPeriod
# SIMPROP_START_TIME as tini
# SIMPROP_END_TIME as tend
# Unpack list of synaptic properties
SynInputs, FactorsExperiment, isTheta, ThetaPeriod, tini, tend, dt, FactorsNum = SynapticFactors
if (tend-tini)<1.5*ThetaPeriod: warnings.warn('Warning: simulation lasts less than 1.5 times SIMPROP_THETA_PERIOD.')
# Get 'individual' factors
with open(DirLoc+'/LCNhm-neurondata/synaptic_individuals.txt') as file:
FactorsIndividual = map(float, [line.split()[1:] for line in file if line.split()[0]==str(FactorsNum) ][0])
# FinalFactors: Multiplication of the 'individual' factor by the 'experiment' factor
FinalFactors = { SynInputs[ii] : (FactorsIndividual[ii]*FactorsExperiment[ii]) for ii in range(len(SynInputs)) }
# Posible Sections into which set the synapses
OtherApical = [ii for ii in self.ApicList if ii not in self.SomApicList]
PosibleSections = [self.SomaList, self.AxonList, self.DendList, self.ApicList, self.SomApicList, OtherApical]
# Position of soma
xyzSoma = np.array([h.x3d(0,sec=self.SomaList[0]), h.y3d(0,sec=self.SomaList[0]), h.z3d(0,sec=self.SomaList[0])])
zSoma = xyzSoma[2]
# Initialize Returns
SynDict = {}
SynObjects = []
NetConObjects = []
if sum(FactorsIndividual)>0.:
# Reading properties of synapses from file
with open(DirLoc+'/LCNhm-neurondata/synaptic_properties.txt') as file:
for line in file:
# Get and set parameters
# ----------------------
# Get parameters of synapses
SynInput = line.split()[0]
if SynInput in SynInputs:
NumBoutons, SynSection, Zmin, Zmax, FiringFreq, Erev, Tau1, Tau2, Gmax, Basal, Phase, BetaA, BetaB = map( float, line.split()[1:] )
# Make new key
if SynInput not in SynDict.keys():
SynDict[SynInput] = {}
SynDict[SynInput]['SynSection'] = []
SynDict[SynInput]['SynPlaces'] = []
SynDict[SynInput]['SynTimes'] = []
# Make Int values
SynSection, NumBoutons = int(SynSection), int(NumBoutons)
# Number of synapse relases
NumSynapses = FiringFreq*((tend-tini)/1000.)
# Multiply maximal conductance by the FinalFactor
Gmax *= FinalFactors[SynInput]
# Selecting places
# ----------------
# PossiblePoints: list of all [Section, Location, 3D-coords, z-coord] of the neuron between Zmin and Zmax
PossiblePoints = []
for sec in PosibleSections[SynSection-1]:
for seg in range(sec.nseg):
loc = float(seg+1)/(sec.nseg+1)
if (h.z3d(seg,sec=sec)-zSoma)>=Zmin and (h.z3d(seg,sec=sec)-zSoma)<=Zmax:
xyz3d = np.array([h.x3d(seg,sec=sec), h.y3d(seg,sec=sec), h.z3d(seg,sec=sec)])
PossiblePoints.append([sec, loc, xyz3d, xyz3d[2]])
PossiblePoints = np.array(PossiblePoints)
if len(PossiblePoints)>0:
Zs = PossiblePoints[:,3].astype(float)
# Gaussian distribution around the mean ((Zmin+Zmax)/3.), and normalized to (Zmin+Zmax)/6.
PlaceDistribution = np.exp( -(Zs-(Zmin+Zmax)/3.)**2 / np.max([10, (Zmin+Zmax)/6.])**2)
PlaceDistribution /= sum(PlaceDistribution)
# Pick random NumBoutons PossiblePoints, according to PlaceDistribution
#SynPlaces = PossiblePoints[ np.in1d( Zs, np.random.choice(Zs, p=PlaceDistribution, size=NumBoutons)) ]
#SynPlaces = np.array(SynPlaces)
# Pick random NumBoutons PossiblePoints, according to the distribution, where to set the synapse (boutons)
SynPlaces = [PossiblePoints[ np.where( Zs==iz )[0][0]] for iz in np.random.choice(Zs, p=PlaceDistribution, size=NumBoutons)]
SynPlaces = np.array(SynPlaces)
# Selecting times
# ---------------
# Possible times between 'tini' and 'tend'
PossibleTimes = np.arange(tini, tend, dt)
# Synaptic time distribution
if isTheta:
# If we are in theta mode, no-homogeneous distribution (given by synaptic_time_probability_distribution)
TimeDistribution = synaptic_time_probability_distribution(PossibleTimes, Phase, [ThetaPeriod,BetaA,BetaB])
else:
# If we are not in theta mode, homogeneous distribution
TimeDistribution = np.ones(len(PossibleTimes))/len(PossibleTimes)
# Randomness in amount of number of synapses per bouton
NumSynRand = 2.*NumSynapses*np.random.random(len(SynPlaces))
# Setting synapses
# ----------------
for iBoutons in range(len(SynPlaces)):
# Synapse place
sec = SynPlaces[iBoutons][0] # Section
loc = SynPlaces[iBoutons][1] # Location
xyzSec = SynPlaces[iBoutons][2]
# Making GLUTAMATE/GABA A synapse at that place
SynObjects.append(h.Exp2Syn(sec(loc)))
SynObjects[-1].e = Erev # (mV)
SynObjects[-1].tau1 = Tau1 # (ms)
SynObjects[-1].tau2 = Tau2 # (ms)
# Taking spiking times from the above distribution
SynTimes = np.sort(np.random.choice(PossibleTimes, size=int(NumSynRand[iBoutons]), p=TimeDistribution))
# Making the synapse object
for tsyn in SynTimes:
# Connecting the synapse Object with the 'stimulator' NetCon
NetConObjects.append(h.NetCon(self.SomaList[0](0)._ref_v, SynObjects[-1], sec=self.SomaList[0]))
NetConObjects[-1].delay = tsyn # ms
NetConObjects[-1].threshold = -1000 # mV
NetConObjects[-1].weight[0] = Gmax*(1 + (np.linalg.norm(xyzSec - xyzSoma)>240)) # uS (double on distal locations)
# Saving for future writing
SynDict[SynInput]['SynSection'].append([sec,loc])
SynDict[SynInput]['SynPlaces'].append(xyzSec)
SynDict[SynInput]['SynTimes'].append(SynTimes)
return SynDict, SynObjects, NetConObjects
def set_current_clamp(self, CurrentFactors):
"""
Set current clamp
Parameters
----------
CurrentFactors: List
:class:`neuron_class`'s :data:`LCNhm_main.IntrinsicFactors` input: List of properties for all desired current pulses
* :const:`LCNhm_configurationfile.CURRENT_DURATION`
List of durations of each current pulse in milliseconds
* :const:`LCNhm_configurationfile.CURRENT_DELAY`
List of delays of each current pulse in milliseconds
* :const:`LCNhm_configurationfile.CURRENT_AMPLITUDES`
List of amplitudes of each current pulse in nanoampers (nA)
* :const:`LCNhm_configurationfile.CURRENT_SECTION`
List of sections of each current pulse
* :const:`LCNhm_configurationfile.CURRENT_LOCATION`
List of location along the defined :const:`LCNhm_configurationfile.CURRENT_SECTION` of each current pulse
Their length must be the same, the number of different current clamps
Click any of the links for further information
Returns
-------
CurrentObject: List
:class:`neuron_class`'s :attr:`CurrentObject` attribute: List of NEURON objects containing all current clamps information
[[ Clamp object 1, Duration 1, Delay 1, Amplitude 1 ],
[ Clamp object 2, Duration 2, Delay 2, Amplitude 2 ], [...], ...... ]
Eg.:
.. code-block:: python
# Object dur del amp
CurrentObject = [ [ NEURON object at soma, 100, 0, 0],
[ NEURON object at soma, 200, 300, 0],
...
[ NEURON object at soma, 200, 1200, 0]]
"""
# Unpack all current parameters
Durations, Delays, Amplitudes, Sections, Locations = CurrentFactors
current = []
for clamp in range(len(Durations)):
current.append([])
Section = Sections[clamp]
Location = Locations[clamp]
if Section[:8] == 'SomaList': Section = self.SomaList[int(Section[8:])]
elif Section[:8] == 'ApicList': Section = self.ApicList[int(Section[8:])]
elif Section[:8] == 'DendList': Section = self.DendList[int(Section[8:])]
elif Section[:8] == 'AxonList': Section = self.AxonList[int(Section[8:])]
current[-1] = h.IClamp(Section(Location))
current[-1].dur = Durations[clamp] # ms
current[-1].delay = Delays[clamp] # ms
current[-1].amp = Amplitudes[clamp] # nA
return current