Action Potential Tutorial

- from passive membrane to Hodgkin-Huxley model

Developed in the Neural Engineering Laboratory at the University of Missouri by Ben Latimer & Ziao Chen

Introduction videos of neurobiology basics

In [1]:
from IPython.display import HTML,YouTubeVideo
YouTubeVideo('PtKAeihnbv0')
Out[1]:
In [2]:
YouTubeVideo('RTRZNK9Aahc')
Out[2]:
In [3]:
YouTubeVideo('U0NpTdge3aw')
Out[3]:

In this tutorial, we'll explore the properties of the cell membrane and the mechanisms of the action potential. We'll introduce you how to simulate a cell in the NEURON + Python environment. If this is your first time coding, don't worry! Just click the "Run" button above to go through step-by-step. You don't need to change anything yet.

Before you start any project, you need to get the tools. In Python, we do this using the "import" statement in the cell below. Click "Run" to proceed.

In [4]:
from neuron import h

h.load_file('stdrun.hoc')
Out[4]:
1.0

1) Create the cell and define its geometry

NEURON defines a cell as a cylinder. Remember, cells in NEURON are simplified. We will make a one-compartment cell which is just a cylinder with length and diameter.

In [5]:
#Create the soma section and define the default parameters
soma = h.Section(name='soma')
soma.diam = 200 # micrometers
soma.L = 100 # micrometers

2) Define the cell's biophysics

Insert the Hodgkin-Huxley channels and define the conductance. First let's make a passive cell by setting the conductances of the active channels (gNa,gK) to 0.

In [6]:
soma.cm = 1.4884e-4/6.2832e-4 # membrane capacitance uF/cm2

soma.insert('hh')
soma.gnabar_hh = 0 # Sodium channel
soma.gkbar_hh = 0 # Potassium channel
soma.gl_hh = 2.0e-5 # leak channel S/cm2
soma.el_hh = -70 # reveral potential mV

h.v_init= -60

3) Inject Current

Neuroscientists call this experiment a "current clamp". We place an electrode into the cell and inject current.

In [7]:
# Inject current in the middle of the soma
stim = h.IClamp(soma(0.5))
stim.delay = 100.0 # delay in ms
stim.dur = 500.0 # duration in ms
stim.amp = 1.0 # amplitude in nA

4) Define simulation parameters and run!

In [8]:
h.tstop = tstop = 800 # how long to run the simulation in ms
h.dt = 0.025 # time step (resolution) of the simulation in ms

# define two vectors for recording variables
v0_vec = h.Vector()
t_vec = h.Vector()

# record the voltage (_ref_v) and time (_ref_t) into the vectors we just created
v0_vec.record(soma(0.5)._ref_v)
t_vec.record(h._ref_t)

h.run()
Out[8]:
0.0

5) Visualize the membrane potential

The data isn't going to visualize itself! We use a package called matplotlib to draw the vectors so we can see them.

In [9]:
# use inline plot
%matplotlib inline
# use interactive plot
# %matplotlib qt5
import matplotlib.pyplot as plt # We use this package for visualization

plt.figure()
plt.plot(t_vec, v0_vec,'b')
plt.xlim(0, tstop)
plt.xlabel('time (ms)')
plt.ylabel('mV')

plt.show()

6) Get some action potentials!

We will make the conductances of the active channels nonzero so that we will see some action potentials.

In [10]:
soma.gnabar_hh = 0.12 # Sodium channel S/cm2
soma.gkbar_hh = 0.012 # Potassium channel S/cm2

stim.delay = 100.0 # delay in ms
stim.dur = 150.0 # duration in ms
stim.amp = 0.03 # amplitude in nA

h.tstop = tstop = 350

# run the simulation!
h.run()

plt.figure(figsize=(10,5))
plt.plot(t_vec, v0_vec,'b')
plt.xlim(0, tstop)
plt.xlabel('time (ms)')
plt.ylabel('mV')

plt.show()

7) Record the gating variables

You should see some spikes in the plot above. If you don't, go back to the top of the notebook and run all the cells again. Neurons spike because of the voltage dependent proteins embedded in their membranes. Let's record those and then plot them just as we did for the membrane voltage.

In [11]:
m_na = h.Vector()
h_na = h.Vector()
n_k = h.Vector()

m_na.record(soma(0.5)._ref_m_hh)
h_na.record(soma(0.5)._ref_h_hh)
n_k.record(soma(0.5)._ref_n_hh)

h.run()
Out[11]:
0.0

8) Visualize membrane potential and gating variables together

This should look familiar. We're just going to plot all of the variables we just recorded at the same time so we can investigate how spikes occur.

In [12]:
plt.figure(figsize=(10,10))
plt.subplot(2,1,1)
plt.plot(t_vec, v0_vec,'b')
plt.xlim(0, tstop)
plt.ylabel('mV')

plt.subplot(2,1,2)
plt.plot(t_vec, m_na,'r')
plt.plot(t_vec, h_na,'b')
plt.plot(t_vec, n_k, 'g')
plt.xlim(0, tstop)
plt.xlabel('time (ms)')
plt.ylabel('Probability')
plt.legend(['m','h','n'])

plt.show()

You did it! Now you understand how a neuron is implemented in code. Time for the hard part... understanding the science and math behind those spikes. You can move on to the next section and change variables (such as the conductances of the Na and K channels) using the interactive controls to see the effects.

9) Interact with the model

So far, we've built the cell and simulated it with one set of parameters. But what if we want to change the parameters to see the effect on the output? In this part of the tutorial, we'll set the model up and then use sliders to interact with the parameters. Don't worry about all the code that's coming up, it's just a repeat of what we've already done.
In [13]:
from neuron import h
import ipywidgets as widgets
from ipywidgets import HBox,VBox,Label,Layout
from IPython.display import display
import matplotlib.pyplot as plt
%matplotlib inline

h.load_file('stdrun.hoc')

soma = h.Section(name='soma')
soma.L = 100 # um
soma.insert('hh')
stim = h.IClamp(soma(0.5))

v0_vec = h.Vector()
t_vec = h.Vector()
m_na = h.Vector()
h_na = h.Vector()
n_k = h.Vector()
m_na.record(soma(0.5)._ref_m_hh)
h_na.record(soma(0.5)._ref_h_hh)
n_k.record(soma(0.5)._ref_n_hh)
v0_vec.record(soma(0.5)._ref_v)
t_vec.record(h._ref_t)

def activemodel(diam,cm,el,gl,gna,gk,tstop,dur,amp,fig):
    soma.diam = diam
    soma.cm = cm*1.4884e-4/6.2832e-4
    soma.gnabar_hh = gna*1e-3
    soma.gkbar_hh = gk*1e-3
    soma.gl_hh = gl*1e-6
    soma.el_hh = el

    stim.delay = dur[0]
    stim.dur = dur[1]-dur[0]
    stim.amp = amp
    
    h.tstop = tstop
    h.v_init = el
    h.run()
    
    plt.close()
    plt.figure(figsize=(12,10))
    plt.subplot(2,1,1)
    plt.plot(t_vec, v0_vec,'b')
    plt.xlim(0, tstop)
    plt.ylabel('mV')
    plt.subplot(2,1,2)
    plt.plot(t_vec, m_na,'r')
    plt.plot(t_vec, h_na,'b')
    plt.plot(t_vec, n_k, 'g')
    plt.xlim(0, tstop)
    plt.xlabel('time (ms)')
    plt.ylabel('Probability')
    plt.legend(['m','h','n'])
    plt.show()
In [15]:
# default settings
diam = 200
cm = 1
el = -70
gl = 30
gna0 = 120
gk0 = 12
tstop = 500
dur = [100,400]
amp = 0.1

w_reset = widgets.Button(description='Reset',icon='history',button_style='primary')
w_fig = widgets.ToggleButton(value=False,description='Interactive plot',icon='window-restore',button_style='success')
w_pass = widgets.ToggleButton(value=False,icon='check',button_style='info')
w_el = widgets.FloatSlider(value=el,min=-80,max=-60,step=.2,continuous_update=False,readout_format='.1f')
w_gl = widgets.FloatSlider(value=gl,min=5,max=50,step=.2,continuous_update=False,readout_format='.1f')
w_gna = widgets.FloatSlider(value=gna0,min=0,max=200,step=.5,continuous_update=False,readout_format='.1f')
w_gk = widgets.FloatSlider(value=gk0,min=0,max=30,step=.1,continuous_update=False,readout_format='.1f')

w_tstop = widgets.FloatText(value=tstop)
w_dur = widgets.FloatRangeSlider(value=dur,min=0,max=500,step=5,continuous_update=False,readout_format='.0f')
w_amp = widgets.FloatLogSlider(value=amp,min=-3,max=1,step=.04,continuous_update=False,readout_format='.3f')
w_diam = widgets.FloatSlider(value=diam,min=100,max=300,step=2,continuous_update=False,readout_format='.0f')
w_cm = widgets.FloatLogSlider(value=cm,min=-1,max=1,step=.05,continuous_update=False,readout_format='.1f')

def reset_default(*args):
    w_pass.value = False
    w_el.value = el; w_gl.value = gl
    w_gna.value = gna0; w_gk.value = gk0
    w_gna.disabled = w_gk.disabled = False
    w_dur.max = w_tstop.value = tstop; w_dur.value = dur
    w_diam.value = diam; w_cm.value = cm
w_reset.on_click(reset_default)

def interactive_fig(*arg):
    if w_fig.value:
        w_fig.icon = 'window-maximize'; w_fig.description='Inline plot'
        %matplotlib qt
        %matplotlib qt
    else:
        w_fig.icon = 'window-restore'; w_fig.description='Interactive plot'
        %matplotlib inline
w_fig.observe(interactive_fig,'value')

def update_pass(*args):
    if w_pass.value:
        global gna,gk
        gna = w_gna.value
        gk = w_gk.value
        w_gna.value = w_gk.value = 0
        w_gna.disabled = w_gk.disabled = True
    else:
        w_gna.value = gna
        w_gk.value = gk
        w_gna.disabled = w_gk.disabled = False
w_pass.observe(update_pass,'value')

def update_dur(*args):
    w_dur.max = w_tstop.value
w_tstop.observe(update_dur,'value')


ui = VBox([HBox([w_reset,w_fig]), HBox([VBox([Label('Passive Cell'),Label(r'\( E_{l}\ (mV) \)'),Label(r'\( g_{leak}\ (\mu S/cm^2) \)'),
                 Label(r'\( g_{Na}\ (mS/cm^2) \)'),Label(r'\( g_{K}\ (mS/cm^2) \)')],layout=Layout(width='14%')),
           VBox([w_pass,w_el,w_gl,w_gna,w_gk],layout=Layout(width='36%')),
           VBox([Label(r'\( tstop\ (ms) \)'),Label(r'\( Injection\ duration\ (ms) \)'),Label(r'\( I_{inject}\ (nA) \)'),
                 Label(r'\( soma\ diameter\ (\mu m) \)'),Label(r'\( capacitance\ scale \)')],layout=Layout(width='14%')),
           VBox([w_tstop,w_dur,w_amp,w_diam,w_cm],layout=Layout(width='36%')) ]) ])

out = widgets.interactive_output(activemodel,{'el':w_el,'gl':w_gl,'gna':w_gna,'gk':w_gk,
                                              'tstop':w_tstop,'dur':w_dur,'amp':w_amp,'diam':w_diam,'cm':w_cm,'fig':w_fig})

display(ui,out)
In [ ]:
 

This website does not host notebooks, it only renders notebooks available on other websites.

Delivered by Fastly, Rendered by Rackspace

nbviewer GitHub repository.

nbviewer version: 8a8fd29

nbconvert version: 5.6.1

Rendered (Mon, 09 Dec 2019 02:07:12 UTC)