Data structure
Contents
Data structure#
Follow a detailed description of the different features of the library.
[1]:
import sinaps as sn
Section#
The class Section
represents a section of neuron with uniform physical values
The characteristics of a section are :
The length
L
in μmThe radius
a
in μmThe menbrane capacitance
C_m
in μF/cm²The longitunal resistance
R_l
in Ohm.cmThe initial potential
V0
in mV
Default parameters#
[2]:
sec0 = sn.Section(name="Sample section 1")
sec0
[2]:
Section Sample section 1
L: 100 um
a: 1 um
C_m: 1 uF/cm² (c_m=62.832 fF/μm)
R_l: 150 Ω.cm (r_l=477.46 aΩ/μm)
channels:
point_channels:
Customized values#
Sinaps uses the param library. You can set custom parameters while creating the object:
[3]:
sec1 = sn.Section(L=50,a=2,name="Sample section 2")
sec1
[3]:
Section Sample section 2
L: 50 um
a: 2 um
C_m: 1 uF/cm² (c_m=125.66 fF/μm)
R_l: 150 Ω.cm (r_l=119.37 aΩ/μm)
channels:
point_channels:
You can also set the attribute values after the object is created:
[4]:
sec1.R_l=100
sec1
[4]:
Section Sample section 2
L: 50 um
a: 2 um
C_m: 1 uF/cm² (c_m=125.66 fF/μm)
R_l: 100 Ω.cm (r_l=79.577 aΩ/μm)
channels:
point_channels:
Channels#
Ion channels can be added to a section.
There are two types of channels
Point channels
Density channels
Density Channels#
Density channels are used to model channels that are distributed everywhere on a section. The current is given per unit of membrane surface.
Leak Channel#
[5]:
lc1=sn.channels.LeakChannel()
lc1
[5]:
LeakChannel(Veq=0 V, G_m=300 uS/cm²)
[6]:
lc2=sn.channels.LeakChannel(
Veq=10, #mV
G_m= 1 #mS/cm²
)
lc2
[6]:
LeakChannel(Veq=10 mV, G_m=1 mS/cm²)
[7]:
sec0.add_channel(lc1)
sec0
[7]:
Section Sample section 1
L: 100 um
a: 1 um
C_m: 1 uF/cm² (c_m=62.832 fF/μm)
R_l: 150 Ω.cm (r_l=477.46 aΩ/μm)
channels: LeakChannel(Veq=0 V, G_m=300 uS/cm²)
point_channels:
Point Channels#
Point channels are used to model channel in specific location of the section, the given current is in pA (not relative to the section membrane surface).
Constant current#
[8]:
pc=sn.channels.ConstantCurrent(1)
[9]:
sec0.add_channel(pc,x = 0) #x relative position inside the section (0-1)
sec0
[9]:
Section Sample section 1
L: 100 um
a: 1 um
C_m: 1 uF/cm² (c_m=62.832 fF/μm)
R_l: 150 Ω.cm (r_l=477.46 aΩ/μm)
channels: LeakChannel(Veq=0 V, G_m=300 uS/cm²)
point_channels: 0:ConstantCurrent(I=1 pA)
The exhaustive list of implemented channels is described in the API Reference
Neuron#
The class Neuron
represents a set of sections connected together
[10]:
nrn=sn.Neuron()
[11]:
nrn.add_section(sec0,0,1)
nrn.add_section(sec1,1,2)
The structure of the neuron is stored in the attribute sections
wich is a Dict with the section as keys and the nodes connected by the section as values (2-tuple) :
[12]:
nrn.sections
[12]:
{Section(C0={}, C_m=1, D={}, L=100, R_l=150, V0=0, a=1, dx=None, name='Sample section 1'): (0,
1),
Section(C0={}, C_m=1, D={}, L=50, R_l=100, V0=0, a=2, dx=None, name='Sample section 2'): (1,
2)}
Accessing the sections#
By node index in the neuron structure. For example nrn[i]
gives all the section connected to node i
[13]:
nrn[0]
[13]:
Section Sample section 1
L: 100 um
a: 1 um
C_m: 1 uF/cm² (c_m=62.832 fF/μm)
R_l: 150 Ω.cm (r_l=477.46 aΩ/μm)
channels: LeakChannel(Veq=0 V, G_m=300 uS/cm²)
point_channels: 0:ConstantCurrent(I=1 pA)
[14]:
nrn[1]
[14]:
[Section(C0={}, C_m=1, D={}, L=50, R_l=100, V0=0, a=2, dx=None, name='Sample section 2'),
Section(C0={}, C_m=1, D={}, L=100, R_l=150, V0=0, a=1, dx=None, name='Sample section 1')]
By name
[15]:
nrn['Sample section 2']
[15]:
Section Sample section 2
L: 50 um
a: 2 um
C_m: 1 uF/cm² (c_m=125.66 fF/μm)
R_l: 100 Ω.cm (r_l=79.577 aΩ/μm)
channels:
point_channels:
Note that if sections have same names (if a part of their name is similar to the used keyname), a list of sections will be returned:
[16]:
nrn['Sample section']
[16]:
[Section(C0={}, C_m=1, D={}, L=50, R_l=100, V0=0, a=2, dx=None, name='Sample section 2'),
Section(C0={}, C_m=1, D={}, L=100, R_l=150, V0=0, a=1, dx=None, name='Sample section 1')]
You can change the parameters of multiples section at once :
[17]:
nrn['Sample section'].C_m=1.5
Access all the sections :
[18]:
nrn[:]
[18]:
[Section(C0={}, C_m=1.5, D={}, L=50, R_l=100, V0=0, a=2, dx=None, name='Sample section 2'),
Section(C0={}, C_m=1.5, D={}, L=100, R_l=150, V0=0, a=1, dx=None, name='Sample section 1')]
[19]:
nrn.plot()
Calculating layout...[OK]
[19]:
Simulation#
The class simulation is linked to a specific neuron and is used to run voltage propagation simulation and electrodiffusion simulations with some custom spatial and time resolutions. The object is also storing the results of the simulation.
The solving is done using the solve_ivp function from the scipy library. Arguments from the solve_ivp function can be passed directly to sim.run.
Create the simulation with neuron nrn
and spatial resolution of 20
μm:
[20]:
sim=sn.Simulation(nrn,dx=20)
Run the simulation for timespan 0 - 300
ms:
[21]:
sim.run((0,300))
100%|██████████| 300.0/300 [00:00<00:00, 779.55ms/s]
Voltage#
Acces the results of the simulation, i.e. the potential at each time and position. They are stored in a pandas Dataframe:
[22]:
sim.V
[22]:
Section | Sample section 1 | Sample section 2 | ||||||
---|---|---|---|---|---|---|---|---|
Position (μm) | 10.000000 | 30.000000 | 50.000000 | 70.000000 | 90.000000 | 8.333333 | 25.000000 | 41.666667 |
Time | ||||||||
0.000000 | 0.000000 | 0.000000e+00 | 0.000000e+00 | 0.000000e+00 | 0.000000e+00 | 0.000000e+00 | 0.000000e+00 | 0.000000e+00 |
0.000003 | 0.000002 | 2.509324e-10 | 3.736438e-14 | 5.563639e-18 | 8.283449e-22 | 1.298956e-25 | 8.347389e-29 | 5.367669e-32 |
0.000006 | 0.000003 | 7.918423e-10 | 1.610918e-13 | 3.041723e-17 | 5.485955e-21 | 1.010326e-24 | 7.456403e-28 | 5.414901e-31 |
0.000038 | 0.000020 | 3.162004e-08 | 4.731016e-11 | 7.032639e-14 | 1.043331e-16 | 1.624481e-19 | 1.032034e-21 | 6.598164e-24 |
0.000070 | 0.000037 | 9.125399e-08 | 1.900242e-10 | 3.632524e-13 | 6.589674e-16 | 1.212339e-18 | 8.880078e-21 | 6.435300e-23 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
96.949480 | 0.541867 | 5.342687e-01 | 5.285935e-01 | 5.248213e-01 | 5.229385e-01 | 5.229384e-01 | 5.229383e-01 | 5.229383e-01 |
127.868232 | 0.541920 | 5.343218e-01 | 5.286471e-01 | 5.248754e-01 | 5.229932e-01 | 5.229935e-01 | 5.229935e-01 | 5.229935e-01 |
158.786984 | 0.541873 | 5.342744e-01 | 5.285996e-01 | 5.248277e-01 | 5.229451e-01 | 5.229452e-01 | 5.229452e-01 | 5.229452e-01 |
189.705736 | 0.541868 | 5.342691e-01 | 5.285941e-01 | 5.248220e-01 | 5.229392e-01 | 5.229392e-01 | 5.229392e-01 | 5.229392e-01 |
300.000000 | 0.541882 | 5.342834e-01 | 5.286083e-01 | 5.248363e-01 | 5.229536e-01 | 5.229536e-01 | 5.229536e-01 | 5.229536e-01 |
81 rows × 8 columns
[23]:
sim['Sample section 2'].plot()
[23]:
[24]:
sim[:].plot()
[24]:
Currents#
The currents computed by the simulation environment are stored in a pandas Dataframe. They are positive when they go toward inside.
The current can be accessed via their class:
[25]:
sim.current(sn.channels.ConstantCurrent)
[25]:
Section | Sample section 1 | Sample section 2 | ||||||
---|---|---|---|---|---|---|---|---|
Position (μm) | 10.000000 | 30.000000 | 50.000000 | 70.000000 | 90.000000 | 8.333333 | 25.000000 | 41.666667 |
Time | ||||||||
0.000000 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
0.000003 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
0.000006 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
0.000038 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
0.000070 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
96.949480 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
127.868232 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
158.786984 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
189.705736 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
300.000000 | 1.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
81 rows × 8 columns
[26]:
sim.current(sn.channels.ConstantCurrent)['Sample section 1'].hvplot()
[26]:
Getting all the channels in a simulation:
[27]:
[ch.__name__ for ch in sim.channels]
[27]:
['ConstantCurrent', 'LeakChannel']
Species#
The species are stored in the enum Class Species
. This specific class is used to make the link in channels definition between currents and species flux. To simulate the concentration of a specific species, one first need to add it to the neuron, specifying the initial concentration and the diffusion coeficient. Ions that can be added through the API are:
Ca: calcium
K: potassium
Na: sodium
Buffer: any species, not charged
Anion: a negatively charged ion
Cation: a positively charger ion
[28]:
nrn.add_species(sn.Species.Ca,C0=2E-4, D=0.2)
[29]:
sec0.C0
[29]:
{<Species Ca2+>: 0.0002}
[30]:
sec1.C0[sn.Species.Ca] = 1E-2
[31]:
sim2=sn.Simulation(nrn,dx=20)
Voltage simulation needs to be run first:
[32]:
sim2.run((0,100))
100%|██████████| 100.0/100 [00:00<00:00, 370.96ms/s]
Then you can run the electro-diffusion simulation. The same way than for voltage simulations, the electro-diffusion simulations are run using the solve-ivp function from the scipy library. solve-ivp arguments can be passed directly to the run_diff function.
[33]:
sim2.run_diff()
100%|██████████| 100.0/100 [00:00<00:00, 2466.80ms/s]
As for the potential, the result of the simulation is stored in a pandas Dataframe:
[34]:
sim2.C
[34]:
Species | Ca2+ | |||||||
---|---|---|---|---|---|---|---|---|
Section | Sample section 1 | Sample section 2 | ||||||
Position (μm) | 10.000000 | 30.000000 | 50.000000 | 70.000000 | 90.000000 | 8.333333 | 25.000000 | 41.666667 |
Time | ||||||||
0.000000 | 0.0002 | 0.0002 | 0.0002 | 0.000200 | 0.000200 | 0.010000 | 0.010000 | 0.01 |
0.026578 | 0.0002 | 0.0002 | 0.0002 | 0.000200 | 0.000200 | 0.010000 | 0.010000 | 0.01 |
0.053155 | 0.0002 | 0.0002 | 0.0002 | 0.000200 | 0.000200 | 0.010000 | 0.010000 | 0.01 |
0.318933 | 0.0002 | 0.0002 | 0.0002 | 0.000200 | 0.000203 | 0.009999 | 0.010000 | 0.01 |
0.584710 | 0.0002 | 0.0002 | 0.0002 | 0.000200 | 0.000205 | 0.009999 | 0.010000 | 0.01 |
3.242481 | 0.0002 | 0.0002 | 0.0002 | 0.000200 | 0.000228 | 0.009994 | 0.010000 | 0.01 |
5.900252 | 0.0002 | 0.0002 | 0.0002 | 0.000200 | 0.000251 | 0.009990 | 0.010000 | 0.01 |
8.558023 | 0.0002 | 0.0002 | 0.0002 | 0.000200 | 0.000273 | 0.009985 | 0.010000 | 0.01 |
35.135733 | 0.0002 | 0.0002 | 0.0002 | 0.000203 | 0.000494 | 0.009941 | 0.009999 | 0.01 |
61.713442 | 0.0002 | 0.0002 | 0.0002 | 0.000208 | 0.000707 | 0.009899 | 0.009998 | 0.01 |
88.291152 | 0.0002 | 0.0002 | 0.0002 | 0.000215 | 0.000910 | 0.009859 | 0.009996 | 0.01 |
100.000000 | 0.0002 | 0.0002 | 0.0002 | 0.000220 | 0.000998 | 0.009842 | 0.009994 | 0.01 |
[35]:
sim2.plot.C(sn.Species.Ca)
[35]:
If several channels of the same type are defined on a section, sim.plot.I will plot the sum of all the currents.
[36]:
sim2.plot.C_field(sn.Species.Ca)
[36]: