"""
This script has all functions except from the :ref:`LCNhm-class` and its methods.
Within its miscellaneous purposes, there are:
* One function naming and making the folder: ``make_folder``
* Functions related to how to distribute intrinsic properties along the cell: ``gradient_membrane_resistance``, ``gradient_spine_scale``, ``gradient_V12`` and ``gradient_gHCN``
* Function of how to distribute the synaptic releases along a theta cycle: ``synaptic_time_probability_distribution``
* Functions that record and save data from the simulation: ``recordings``, ``save_spiking_times``, ``save_recordings`` and ``save_parameters``
Functions and descriptions
--------------------------
"""
import os
import sys
import datetime
import numpy as np
from neuron import h, nrn, gui
def make_folder(DIR_LOCATION, OPT_FOLDER_NAME):
"""
Make a new folder inside *DIR_LOCATION/LCNhm-results/* to save any new simulation.
``< D(ate)YearMonthDay _ T(ime)HourMinute _ OptionalFolderName >``
Parameters
----------
DIR_LOCATION: String
Name of main directory, where *LCNhm_main.py* is
Defined in :const:`LCNhm_configurationfile.DIR_LOCATION`
OPT_FOLDER_NAME: String
Optional folder name: additional description of the simulation.
Defined in :const:`LCNhm_configurationfile.OPT_FOLDER_NAME`
Eg.: *first_test*, *high_CA3*, *inhibition_suppression*
Returns
-------
FOLDER_NAME: String
Name of the folder just made.
"""
# Today's date and time
Date = [ datetime.datetime.today().year,
datetime.datetime.today().month,
datetime.datetime.today().day,
datetime.datetime.today().hour,
datetime.datetime.today().minute ]
# Name of folder: <D(ate)YearMonthDay_T(ime)HourMinute_OptionalFolderName>
FOLDER_NAME = 'D%.2d%.2d%.2d_T%.2d%.2d'%tuple(Date) + '_' * (len(OPT_FOLDER_NAME)>0) + OPT_FOLDER_NAME
FolderLen = len(FOLDER_NAME)
# Make a new directory
dirFolderName = DIR_LOCATION+'/LCNhm-results/'+FOLDER_NAME
# If folder doesnt exist, make a new one; if it exists, add a suffix
k = 1
while os.path.exists(dirFolderName):
FOLDER_NAME = FOLDER_NAME[:FolderLen] + '_%.3d'%k
dirFolderName = DIR_LOCATION+'/LCNhm-results/'+FOLDER_NAME
k += 1
# Make new folder
os.makedirs(dirFolderName)
return dirFolderName
def gradient_membrane_resistance(zDistance):
"""
Definition of the membrane resistivity along the *z* axis (radial axis)
Parameters
----------
zDistance: Float
Distance to soma on the *z* axis (radial axis)
Returns
-------
MembraneResistance: Float
Membrane resistance (ohm*cm2) at :data:`zDistance`
"""
# Conductance: gm = 1/Rm [S/cm2]
# Rm = 1/gm [Ohm*cm2]
kohm2ohm = 1000
MembraneResistance = kohm2ohm*(80. + (0.4-80.)/(1+np.exp((225-zDistance)/30.))) # ohm * cm2
return MembraneResistance
def gradient_spine_scale(zDistance, Diameter):
"""
Definition of the spine scale factor along the *z* axis (radial axis)
To take into account the spines intrinsic properties such as the
capacitance and the membrane resistance must be multiplied by a factor SS
Parameters
----------
zDistance: Float
Distance to soma on the *z* axis (radial axis)
Diameter: Float
Diameter of compartiment (um)
Returns
-------
SS: Float
Spine scale factor
"""
# Str Lacunosum Moleculare
if zDistance>350:
if (Diameter>0.35): SS = 2.10 # Medium
else: SS = 1.71 # Thin
# Str Radiatum
elif (zDistance<= 350) and (zDistance>0):
if Diameter>1.6: SS = 1.69 # Thick medial
elif (Diameter<=1.6) and (Diameter>0.55): SS = 1.60 # Thick distal
elif (Diameter<0.55): SS = 1.86 # Thin
else: SS = 1
# Str Oriens
elif zDistance<0: SS = 2.51
else: SS = 1
return SS
def gradient_V12(zDistance):
"""
Definition of the HCN half-maximal activation voltage (V1/2) factor along the *z* axis (radial axis) (Sinha2015)
Parameters
----------
zDistance: Float
Distance to soma on the *z* axis (radial axis)
Returns
-------
v12: Float
Half-maximal activation voltage (mV)
"""
v12 = -82*(zDistance<300) - (0.04*zDistance-0.04*100)*(zDistance>100 and zDistance<300) - 90*(zDistance>=300) # mV
return v12
def gradient_gHCN(zDistance):
"""
Definition of the HCN maximum conductance along the *z* axis (radial axis) (Sinha2015)
Parameters
----------
zDistance: Float
Distance to soma on the *z* axis (radial axis)
Returns
-------
gHCN: Float
Maximumn HCN conductance (S/cm2)
"""
ghcnBase = 85.e-6 # S/cm2
gHCN = ghcnBase*(1+120./(1. + np.exp((275.-zDistance)/50.)) ) # S/cm2
return gHCN
def synaptic_time_probability_distribution(Times, PhaseMax, Parameters):
"""
Definition of the synaptic probability distribution along theta phase.
In this case, the distribution chosen is the asymmetric gaussian (beta function),
described as:
.. code-block:: python
beta(x,A,B) = x^(A-1) * (1-x)^(B-1)
Parameters
----------
Times: Numpy array
Times to whom compute the firing probability function
PhaseMax: Float
Phase of maximum probability
Parameters: List
List of parameters.
In this case, ``Parameters = [A, B]`` will define the shift of the gaussian distribution
to the left or to the right.
Returns
-------
ProbDistribution: Numpy array
Spiking probability distribution for ``Times``
"""
# Unpack parameters
ThetaPeriod, A, B = Parameters
# Phases
Phases = np.mod(Times,ThetaPeriod)/ThetaPeriod
dPh = Phases[1]-Phases[0]
# Phase (desired position of the maximum)
PhaseMax = np.argwhere(np.abs(Phases-PhaseMax/360.)<=(Phases[1]-Phases[0]))[0,0]
# Actual position of the maximum
Phasesmax = np.argmax(Phases**(A-1.)*(1-Phases)**(B-1.))
while dPh*(Phasesmax-PhaseMax)>1:
Phasesmax -= int(1./dPh)
# Shifting the distribution
idx = 1+dPh*(Phasesmax-PhaseMax)
Phases2 = np.mod(np.array([Phases[-1]+i*dPh for i in np.arange(int(idx/dPh))]) , 1)
Phases = Phases[int(idx/dPh):]
ProbDistribution = np.append(Phases**(A-1.)*(1-Phases)**(B-1.), Phases2**(A-1.)*(1-Phases2)**(B-1.) )
ProbDistribution /= sum(ProbDistribution)
return ProbDistribution
def recordings(Pyramidal, RECORDING_MAGNITUDE, RECORDING_SECTION, RECORDING_LOCATION):
"""
Recordings are set.
Parameters
----------
Pyramidal: :class:`LCNhm_class.neuron_class` object
RECORDING_MAGNITUDE: List of `str`
Physical magnitudes to be measured and recorded
Defined in :const:`LCNhm_configurationfile.RECORDING_MAGNITUDE`
RECORDING_SECTION: List of `str`
Sites/Sections to be measured and recorded
Defined in :const:`LCNhm_configurationfile.RECORDING_SECTION`
RECORDING_LOCATION: List of Floats
Location along the defined :const:`RECORDING_SECTION` to be measured and recorded
Defined in :const:`LCNhm_configurationfile.RECORDING_LOCATION`
Returns
-------
Recordings: Dictionary
Dictionary with all recordings
Eg.: Given the following inputs, the Recordings Dictionary would be
.. code-block:: python
RECORDING_MAGNITUDE = ['Time','Vmem','Pos']
RECORDING_SECTION = ['SomaList0','ApicList0','ApicList0','ApicList0']
RECORDING_LOCATION = [0.0, 0.2, 0.5, 0.9]
Recordings = { 'Time': record object
'Vmem': { 'SomaList0_000': record object,
'ApicList0_020': record object,
'ApicList0_050': record object,
'ApicList0_090': record object},
'Imem': { 'SomaList0_000': record object,
'ApicList0_020': record object,
'ApicList0_050': record object,
'ApicList0_090': record object} }
"""
# If it's empty, record Soma for Spike times, but don't save it
if len(RECORDING_MAGNITUDE) == 0:
RECORDING_MAGNITUDE = ['Vmem']
RECORDING_SECTION = ['SomaList0']
RECORDING_LOCATION = [0.0]
# Initialize variable
Recordings = {}
for Mag in RECORDING_MAGNITUDE:
Recordings[Mag] = {}
# Time
Recordings['Time'] = h.Vector()
Recordings['Time'].record( h._ref_t )
# Magnitudes to be recorded
for Mag in RECORDING_MAGNITUDE:
# Sections defined in RECORDING_SECTION
for ii, Section in enumerate(RECORDING_SECTION):
# Get section object
sec = getattr(Pyramidal,Section[:8])[int(Section[8:])]
loc = RECORDING_LOCATION[ii]
seg = np.floor(loc*sec.nseg)
SecName = '%s_%.3d'%(Section,loc*100)
if Mag!='Time':
Recordings[Mag][SecName] = h.Vector()
if Mag=='Vmem': Recordings[Mag][SecName].record( sec( loc )._ref_v )
if Mag=='Imem': Recordings[Mag][SecName].record( sec( loc )._ref_i_membrane )
if Mag=='Pos': Recordings[Mag][SecName] = [h.x3d(seg,sec=sec), h.y3d(seg,sec=sec), h.z3d(seg,sec=sec), h.diam3d(seg,sec=sec)]
# Always record Vmem of SomaList0 and Time:
Mag = 'Vmem'
SecName = 'SomaList0_000'
if Mag not in Recordings.keys():
Recordings[Mag] = {}
Recordings[Mag][SecName] = h.Vector()
Recordings[Mag][SecName].record( sec( loc )._ref_v )
elif SecName not in Recordings[Mag].keys():
Recordings[Mag][SecName] = h.Vector()
Recordings[Mag][SecName].record( sec( loc )._ref_v )
return Recordings
def save_spiking_times(Recordings, FolderName):
"""
Time of spikes are detected and saved in :data:`LCNhm_main.FolderName` */TimeSpikes.txt*
If there are no spikes, it will write ``NaN``
Parameters
----------
Recordings: Dictionary
Dictionary with all recordings, output from :func:`recordings`
FolderName: String
Name of folder where recordings will be saved, output from :func:`make_folder`
"""
# Membrane potential of soma and time
VmemSoma = np.array(Recordings['Vmem']['SomaList0_000'].to_python())
Time = np.array(Recordings['Time'].to_python())
# Initialize variable
TimeSpikes = []
# Time of spikes
for i, iTime in enumerate(Time[Time>50.]):
if (VmemSoma[i]>VmemSoma[i-1]) and (VmemSoma[i]>VmemSoma[i+1]) and (VmemSoma[i]>-10):
TimeSpikes.append(iTime)
# If there are no spikes, 'nan'
if len(TimeSpikes)==0:
TimeSpikes.append(np.nan)
# Save
#with open('/'.join(FolderName.split('/')[:-1])+'/TimeSpikes.txt',"a") as file:
with open(FolderName+'/TimeSpikes.txt',"a") as file:
file.write(" ".join(map(str, TimeSpikes )))
file.write("\n")
def save_recordings(Recordings, FolderName, RECORDING_MAGNITUDE):
"""
Recordings of the magnitudes selected in :const:`LCNhm_configurationfile.RECORDING_MAGNITUDE`
are saved in a txt file in :data:`LCNhm_main.FolderName` subfolder, as:
Eg.: *Recordings_Vmem.txt*
Eg.: *Recordings_Pos.txt*
Membrane potentials (Vmem) and membrane currents (Imem) along time of each recording site
is saved in different rows
Eg.:
>>> vi LCNhm-results/20190723_1200_test/Recordings_Vmem.txt
>>> Time 0.000 0.025 0.050 ...
>>> SomaList0_000 -65.000 -65.300 -65.450 ...
>>> ApicList0_000 -55.000 -54.800 -54.600 ...
Position is saved in different rows for each recording site
Eg.:
>>> vi LCNhm-results/20190723_1200_test/Recordings_Pos.txt
>>> SomaList0_000 0.0 0.0 0.0
>>> ApicList0_000 0.0 0.0 -1000.0
Parameters
----------
Recordings: Dictionary
Dictionary with all recordings, output from :func:`recordings`
FolderName: String
Name of folder where recordings will be saved, output from :func:`make_folder`
"""
# If RECORDING_SECTION is empty, record Soma for Spike times, but don't save it
if len(RECORDING_MAGNITUDE)>0:
# Magnitudes to be recorded
for Mag in Recordings.keys():
if Mag!='Time':
if Mag=='Pos':
with open('%s/Recordings_%s.txt'%(FolderName,Mag),"a") as file:
# Sections
for SecName in Recordings[Mag].keys():
# Write magnitude of section 'SecName'
file.write("%s %.3f %.3f %.3f %.3f\n"%tuple([SecName]+Recordings[Mag][SecName]))
else:
with open('%s/Recordings_%s.txt'%(FolderName,Mag),"a") as file:
# Time
file.write("Time "+" ".join(map(str, Recordings['Time'].to_python() )))
file.write("\n")
# Sections
for SecName in Recordings[Mag].keys():
# Write magnitude of section 'SecName'
file.write(SecName+" "+" ".join(map(str, Recordings[Mag][SecName].to_python() )))
file.write("\n")
def save_parameters(Parameters, FolderName):
"""
Values of the :ref:`LCNhm-configuration-file` parameters are written in Parameters.txt
in :data:`LCNhm_main.FolderName` subfolder.
Eg.:
>>> vi LCNhm-results/20190723_1200_test/Parameters.txt
>>> DIR_LOCATION /home/andrea/Projects/HippoModel/LCNhippomodel
>>> OPT_FOLDER_NAME test
>>> SIMPROP_THETA_MODE True
>>> SIMPROP_THETA_PERIOD 166.000000
>>> SIMPROP_START_TIME 0.000000
>>> SIMPROP_SIM_TIME 1660.000000
>>> SIMPROP_END_TIME 1660.000000
>>> SIMPROP_DT 0.025000
>>> SIMPROP_TEMPERATURE 34.000000
>>> CELLPROP_MORPHOLOGY n128
>>> CELLPROP_INTRINSIC 0
>>> CELLPROP_INTRINSIC_IONCHS iNas iA iAHPs iC iCaL iCaT iKDR iM iHCN iL
>>> CELLPROP_INTRINSIC_EXPERIMENT 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000
>>> CELLPROP_SYNAPTIC_INPUTS CA3 CA2 EC3 EC2 Axo Bis CCK Ivy NGF OLM PV SCA
>>> CELLPROP_SYNAPTIC_EXPERIMENT 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000
>>> CURRENT_DURATION 0.000000
>>> CURRENT_DELAY 0.000000
>>> CURRENT_AMPLITUDES 0.000000
>>> CURRENT_SECTION SomaList0
>>> CURRENT_LOCATION 0.000000
>>> RECORDING_MAGNITUDE
>>> RECORDING_SECTION SomaList0
>>> RECORDING_LOCATION 0.0
Parameters
----------
Parameters: List
List with all :ref:`LCNhm-configuration-file` parameters
FolderName: String
Name of folder where recordings will be saved, output from :func:`make_folder`
"""
# Save configuration file
DIR_LOCATION, OPT_FOLDER_NAME, SIMPROP_THETA_MODE, SIMPROP_THETA_PERIOD, SIMPROP_START_TIME, SIMPROP_SIM_TIME, SIMPROP_END_TIME, SIMPROP_DT, SIMPROP_TEMPERATURE, CELLPROP_MORPHOLOGY, CELLPROP_INTRINSIC, CELLPROP_SYNAPTIC, CELLPROP_INTRINSIC_IONCHS, CELLPROP_INTRINSIC_EXPERIMENT, CELLPROP_SYNAPTIC_INPUTS, CELLPROP_SYNAPTIC_EXPERIMENT, CURRENT_DURATION, CURRENT_DELAY, CURRENT_AMPLITUDES, CURRENT_SECTION, CURRENT_LOCATION, RECORDING_MAGNITUDE, RECORDING_SECTION, RECORDING_LOCATION = Parameters
with open('%s/Parameters.txt'%FolderName,"a") as file:
file.write('DIR_LOCATION %s\n'%DIR_LOCATION)
file.write('OPT_FOLDER_NAME %s\n'%OPT_FOLDER_NAME)
file.write('SIMPROP_THETA_MODE %s\n'%SIMPROP_THETA_MODE)
file.write('SIMPROP_THETA_PERIOD %f\n'%SIMPROP_THETA_PERIOD)
file.write('SIMPROP_START_TIME %f\n'%SIMPROP_START_TIME)
file.write('SIMPROP_SIM_TIME %f\n'%SIMPROP_SIM_TIME)
file.write('SIMPROP_END_TIME %f\n'%SIMPROP_END_TIME)
file.write('SIMPROP_DT %f\n'%SIMPROP_DT)
file.write('SIMPROP_TEMPERATURE %f\n'%SIMPROP_TEMPERATURE)
file.write('CELLPROP_MORPHOLOGY %s\n'%CELLPROP_MORPHOLOGY)
file.write('CELLPROP_INTRINSIC %d\n'%CELLPROP_INTRINSIC)
file.write('CELLPROP_SYNAPTIC %d\n'%CELLPROP_SYNAPTIC)
file.write('CELLPROP_INTRINSIC_IONCHS'+' %s'*len(CELLPROP_INTRINSIC_IONCHS)%tuple(CELLPROP_INTRINSIC_IONCHS)+'\n')
file.write('CELLPROP_INTRINSIC_EXPERIMENT'+' %f'*len(CELLPROP_INTRINSIC_EXPERIMENT)%tuple(CELLPROP_INTRINSIC_EXPERIMENT)+'\n')
file.write('CELLPROP_SYNAPTIC_INPUTS'+' %s'*len(CELLPROP_SYNAPTIC_INPUTS)%tuple(CELLPROP_SYNAPTIC_INPUTS)+'\n')
file.write('CELLPROP_SYNAPTIC_EXPERIMENT'+' %f'*len(CELLPROP_SYNAPTIC_EXPERIMENT)%tuple(CELLPROP_SYNAPTIC_EXPERIMENT)+'\n')
file.write('CURRENT_DURATION'+' %f'*len(CURRENT_DURATION)%tuple(CURRENT_DURATION)+'\n')
file.write('CURRENT_DELAY'+' %f'*len(CURRENT_DELAY)%tuple(CURRENT_DELAY)+'\n')
file.write('CURRENT_AMPLITUDES'+' %f'*len(CURRENT_AMPLITUDES)%tuple(CURRENT_AMPLITUDES)+'\n')
file.write('CURRENT_SECTION'+' %s'*len(CURRENT_SECTION)%tuple(CURRENT_SECTION)+'\n')
file.write('CURRENT_LOCATION'+' %f'*len(CURRENT_LOCATION)%tuple(CURRENT_LOCATION)+'\n')
file.write('RECORDING_MAGNITUDE \n'%RECORDING_MAGNITUDE)
file.write('RECORDING_SECTION'+' %s'*len(RECORDING_SECTION)%tuple(RECORDING_SECTION)+'\n')
file.write('RECORDING_LOCATION'+' %s'*len(RECORDING_LOCATION)%tuple(RECORDING_LOCATION)+'\n')