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 μm

  • The radius a in μm

  • The menbrane capacitance C_m in μF/cm²

  • The longitunal resistance R_l in Ohm.cm

  • The 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]: