Chapter 2 The Basic Hodgkin - Huxley Model:
The standard Hodgkin - Huxley model of an excitatory neuron consists
of the equation for the total membrane current, IM, obtained from
Ohm's law:
where V denotes the membrane voltage, IK is the potassium current,
INa is the sodium current and IL is leakage current carried by
other ions that move passively through the membrane. This
equation is derived by modeling the potassium,
sodium and leakage currents using a simple electrical
circuit model of the membrane. We think of a gate in the membrane as having
an intrinsic resistance and the cell membrane itself as
having an intrincis capacitance as shown in Figure 2.1:
Figure 2.1: The Membrane and Gate Circuit Model
Here we show an idealized cell with a small portion of the membrane
blown up into an idealized circuit. We see a small piece of the lipid
membrane with an inserted gate. We think of the gate as having
some intrinsic resistance and capacitance. Now for our simple Hodgkin -
Huxley model here, we want to model a sodium and potassium gate
as well as the cell capacitance. So we will have a resistance for both
the sodium and potassium. In addition, we know that other ions move
across the membrane due to pumps, other gates and so forth. We will
temporarily model this additional ion current as a leakage current
with its own resistance. We also know that each ion has its own
equilibrium potential which is determined by applying the Nernst
equation. The driving electomotive force or driving emf
is the difference between the ion equilibrium potential and the
voltage across the membrane itself. Hence, if Ec is the equilibrium
potential due to ion c and Vm is the membrane potential, the driving force
is Vc - Vm. In Figure 2.2, we see an electric
schematic that summarizes what we have just said. We model the membrane
as a parellel circuit with a
branch for the sodium and potassium ion, a branch for the leakage current
and a branch for the membrane capacitance.
Figure 2.2: The Simple Hodgkin - Huxley Membrane Circuit Model
From circuit theory, we know that the charge q across a
capacitator is q = C E, where C is the capacitance
and E is the voltage across the capicitor. Hence, if the
capacitance C is a constant, we see that the current
through the capacitor is given by the time rate of change
of the charge
If the voltage E was also space dependent, then
we would write E(z,t) to indicate its dependence on
both a space variable z and the time t. Then the
capacitive current would be
From Ohm's law, we know that voltage is current times
resistance; hence for each ion c, we can say
where we label the voltage, current and resistance due to this
ion with the subscript c. This implies
where gc is the reciprocal resistance or conductance of ion c.
Hence, we can model all of our ionic currents using a conductance
equation of the form above.
Of course, the potassium and sodium conductances
are nonlinear functions of the membrane voltage
V and time t. This reflects the fact that the amount of current that
flows through the membrane for these ions is dependent on the voltage
differential across
the membrane which in turn is also time dependent. The general functional
form for an ion c is thus
| Ic |
= |
gc(V,t) ( V(t) - Ec(t) ) |
where as we mentioned previously, the driving force, V - Ec,
is the difference between the voltage across
the membrane and the equilibrium value for the ion in question, Ec. Note,
the ion battery voltage
Ec itself might also change in time (for example, extracellular potassium
concentration changes over time ). Hence, the driving force is time dependent.
The conductance is modeled as the product of a activation, m,
and an inactivation, h, term that are essentially sigmoid nonlinearities.
The activation and inactivationa are functions of V and t also.
The conductance is assumed to have the form
| gc(V,t) |
= |
g0 mp(V,t) hq(V,t) |
where appropriate powers of p and q are found to match known data for
a given ion conductance.
We model the leakage current, IL, as
where the leakage battery voltage, EL, and the conductance gL are
constants that are data driven.
Hence, our full model would be
| Im |
= |
| Cm |
|
+ gK (V - EK)
+ gNa (V - ENa)
+ gL ( V - EL ) |
|
2.1 Activation and Inactivation Variables:
We assume that the voltage dependence of our
activation and inactivation has been
fitted from data. Hodgkin and Huxley modeled the
time dependence of these variables using first order
kinetics. They assumed a typical variable of this
type, say m, satisfies for each value of voltage,
V:
|
= |
ax(V) (1 - x(V))
+ bx(V) x(V) |
| x(V,0) |
= |
x0(V) |
For convenience of exposition, we usually drop the
functional dependence of x on V and just write:
|
= |
ax (1 - x)
+ bx x |
| x(0) |
= |
x0 |
Rewriting, we see
We let
allowing us to rewrite our rate equation as
This first order equation is easily solved to give
2.2 The Hodgkin-Huxley Sodium and Potassium Model:
Hodgkin and Huxley modeled the sodium and potassium gates
as
| gNa(V,t) |
= |
g0Na m3(V,t) h(V,t) |
| gK(V,t) |
= |
g0K n4(V,t) |
where the two activation variables, m and n, and the
one inactivation variable all satisfy first order
kinetics as we have discussed. Hence, if the
parameters tm and m¥ and so forth were constants,
we would know
with
| tm |
= |
|
| m¥ |
= |
|
| th |
= |
|
| h¥ |
= |
|
| tn |
= |
|
| n¥ |
= |
|
However, all of the coefficient functions, a and b for each
variable required data fits as functions of voltage.
These were determined to be
| am |
= |
| -0.10 |
| V + 35.0 |
|
| e -.1 (V +35.0) - 1.0 |
|
|
| bm |
= |
|
| ah |
= |
0.07 e-.05 (V+60.0) |
| bh |
= |
| 1.0 |
|
| (1.0 + e-.1 (V+30.0)) |
|
| an |
= |
| - |
| 0.01*(V+50.0 |
|
| (e -.1 (V+50.0) - 1.0) |
|
|
| bn |
= |
0.125 e-0.0125 (V+60.0 |
Of course these data fits were obtained at a certain temperature
and assumed values for all the other constants needed. These
other parameters are given in the units below
| voltage |
mV |
milli volts |
10-3 Volts |
| current |
na |
nano amps |
10-9 Amps |
| time |
ms |
milli seconds |
10-3 Seconds |
| concentration |
mM |
milli moles |
10-3 Moles |
| conductance |
µ S |
micro Siemens |
10-6 ohms-1 |
| capacitance |
nF |
nano Fahrads |
10-9 Fahrads |
Our model of the membrane dynamics here thus consists of the following
differential equations:
|
= |
m¥ - m |
|
= |
h¥ - h |
|
= |
n¥ - n |
|
= |
|
with initial conditions
| m(0) |
= |
m¥(V0,0) |
| h(0) |
= |
h¥(V0,0) |
| n(0) |
= |
m¥(V0,0) |
| V(0) |
= |
V0 |
We note that at equilibrium there is no current across the
membrane. Hence, the sodium and potassium currents are zero
and the activation and inactivation variables should achieve their
steady state values which would be m¥, h¥ and
n¥ computed at the equilibrium membrane potential which is
here denoted by V0.
2.2.1 Encoding The Dynamics:
Now these dynamics are more difficult to solve than you might think.
The sequence of steps is this:
-
Given the time t and voltage V compute
| am |
= |
| -0.10 |
| V + 35.0 |
|
| e -.1 (V +35.0) - 1.0 |
|
|
| bm |
= |
|
| ah |
= |
0.07 e-.05 (V+60.0) |
| bh |
= |
|
| an |
= |
| - |
| 0.01*(V+50.0 |
|
| (e -.1 (V+50.0) - 1.0) |
|
|
| bn |
= |
0.125 e-0.0125 (V+60.0 |
- Then compute the t and steady state activation and
inactivation variables
| tm |
= |
|
| m¥ |
= |
|
| th |
= |
|
| h¥ |
= |
|
| tn |
= |
|
| n¥ |
= |
|
- Then compute the sodium and potassium potentials.
In this model this is easy as each is set only once
since the internal and external ion concentrations
always stay the same and so Nernst's equation only has to
used one time. Here we use the concentrations
| [NA]o |
= |
491.0 |
| [NA]i |
= |
50.0 |
| [K]o |
= |
20.11 |
| [K]i |
= |
400.0 |
These computations are also dependent on the
temperature which is set to 9.3 degrees C.
- Next compute the conductances since we now know
m(V,t), h(V,t) and n(V,t).
| gNa(V,t) |
= |
g0Na m3(V,t) h(V,t) |
| gK(V,t) |
= |
g0K n4(V,t) |
Now here we will need the maximum sodium and potassium
conductances g0Na and g0K to finish the computation.
These values must be provided as data and in this model
are not time dependent. Here we use
- Then compute the ionic currents:
| INa |
= |
gNA(V,t) ( V(t) - ENA) |
| IK |
= |
gK(V,t) ( V(t) - EK) |
| IL |
= |
gL(V,t) ( V(t) - EL) |
where we use
- Finally compute the total current
- We can now compute the dynamics of our system at time t
and voltage V: we let IM denote the external
current to our system which we must supply.
where we use CM = 1.0.
The basic Hodgkin - Huxley model thus needs four independent variables
which we place in a four dimensional vector y which components assigned as
follows:
| y |
= |
é
ê
ê
ê
ë |
|
| y[0] |
= V |
| y[1] |
= m |
| y[2] |
= h |
| y[3] |
= n |
|
|
ù
ú
ú
ú
û |
|
We encode the dynamics calculated above into a four dimensional
vector f whose components are interpreted as follows:
| f = |
= |
é
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ë |
|
|
|
ù
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
û |
|
which in terms of our vector y becomes
é
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ë |
|
|
|
ù
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
û |
|
So to summarize, at each time t and V, we need to calculate the four
dimensional dynamics vector f for use in our choice of
ODE solver.
2.2.2 Computing the Solution Numerically:
As an example of what we might do to solve this kind of
a problem, we note that this system can now be written
in vector form as
where we have found that
| f |
= |
é
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ë |
|
|
|
ù
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
û |
|
with initial data
The hardest part is to encode all of this in the vector
form. The dynamic component f[0] is actually a complicated
function of m, h and n as determined by the encoding sequence
we have previously discussed. Hence, the calculation of the
dynamics vector f is not as simple as defining each component
of f as a functional expression.
Estimating The Solution Numerically:
If we want to solve the scalar equation (this means x is
not a vector)
one way we can attack it is to assume the solution x
has enough smoothness to allow us to expand it in
a Taylor series expansion about the base point
(t0,x0). Using the Taylor Remainder
theorem, this gives for a given time point t:
| x(t) |
= |
| x(t0) + |
|
(t0) (t - t0)
+ |
|
|
|
(t0) (t - t0)2
+ |
|
|
|
(ct) (t - t0)3 |
|
where ct is some point in the interval [t0,t]. We also know
by the chain rule that since x'
is f that
which implies, switching to a standard
subscript notation for partial derivatives
(yes, these calculations are indeed yucky2!)
|
= |
(ftt + ftx f) f
+
(fxt + fxx f) f
+
fx (ft + fx f) |
The important thing to note is that this third order derivative
is made up of algebraic combinations on f and its various
partial derivatives. We typically assume that on the
interval [t0,t], that all of these functions are continous
and bounded. Thus, letting ||g|| represent the maximum value of
the continous function g(s,u) on the interval [t0,t] × [x0,x1]
for suitable x1, we know
there is a constant C so that
|
£ |
(||ftt|| + ||ftx|| ||f||) ||f||
+
(||fxt|| + ||fxx|| ||f||) ||f||
+
||fx|| (||ft|| + ||fx|| ||f||) |
| |
= |
C |
Now using the standard abbreviations
| f0 |
= |
f(t0,x)) |
| ft0 |
= |
|
| fx0 |
= |
|
| ftt0 |
= |
|
| ftx0 |
= |
|
| fxt0 |
= |
|
| fxx0 |
= |
|
we see our solution can be written as
| x(t) |
= |
| x0 + f0 (t - t0)
+ |
|
(ft0 + fx0 f0) (t - t0)2
+ |
|
|
|
(ct) (t - t0)3 |
|
Some Detailed Error Estimates:
Now let's use the results we just derived as follows:
let's choose a time step h and set t to be t0 + h. Then
we have (t - t0) is h and our solution is
| x(t0+h) |
= |
| x0 + f0 h
+ |
|
(ft0 + fx0 f0) h2
+ |
|
|
|
(ct) h3 |
|
Now, note that using a Taylor expansion for the
function f, we can write for a base point (t0,x0)
|
= |
| f0 + ft0 |
|
h
+ fx0 |
|
h f0
+ |
|
H0(d,u) |
|
where H0(d,u) is what is called a Hessian term and is defined by
| H0(d,u) |
= |
é
ê
ê
ë |
|
|
|
ù
ú
ú
û |
|
é
ë |
|
| ftt(d,u) |
ftx(d,u) |
| fxt(d,u) |
fxx(d,u) |
|
|
ù
û |
|
é
ê
ê
ê
ê
ê
ê
ë |
|
|
|
ù
ú
ú
ú
ú
ú
ú
û |
|
The point (d,u) satisfies d is in the interval [t0,t0+ 1/2 h] and
u is in the interval [x0,x0+ 1/2 h f0]. Note that we can estimate the
effect of this term by
|
£ |
|
( ||ftt|| |
|
+ ||ftx|| |
|
|f0|
+ ||fxx|| |
|
|f0|2 |
|
| |
= |
|
( ||ftt|| + ||ftx|| |f0| + ||fxx|| |f0|2 ) |
|
Thus for suitable constant D we can say
From the above, we note we can write
| x(t0+h) |
= |
| x0 + h f(t0+ |
|
h,x0 + |
|
h f0)
+ H0(d,u)
+ |
|
|
|
(ct) h3 |
|
Finally, we see that if we take our maximum function values on
the domain [t0,t0+h] × [x0, x0 + h f0] for all the
terms in d3x/dt3, then there is a constant C so that
and so the
error in approximating the true solution x by the term
x0 + h f(t0+ 1/2h,x0 + 1/2 h f0)
can be written as e(t) where on [t0,t0+h]
Thus, we can build an approximate solution f to the true solution
x that uses the information we know at t0 with data x0 to
construct the approximation
| f(t) |
= |
| x0 + h f(t0+ |
|
h,x0 + |
|
h f0) |
|
which has a known error proportional to h3. Since this error
is due to the fact that we truncate the Taylor series expansions
for f earlier, this is known as truncation error.
We can follow the process above to build other sorts of
approximations that use more terms of the Taylor Series
expansions. We would call this approximation an order 3 method
because the local error is proportional to the third power of the
step size h. Note our simple approximation
-
Uses to function evaluations to build the approximate value:
f(t0,x0) and
f(t0 + 1/2 h, x0 + 1/2 h f(t0,x0)).
If we carried out our Taylor series approximations one more term,
that is we use remainder terms due to d4 x/dt4,
we would find our local error is proportional to h4 and we
would need three function values to build our approximation.
In general, to obtain a local order hp method, we need
p-1 function evaluations if we use this technique.
The example we have worked out here is called the
Runga - Kutte Order 2 Method. Now to solve a problem on
a long time interval, we do this approximation for each
step and consecutively build an approximation solution sequence
x0, x(t0+h1), x(t0+h1+h2) and so forth where
hi denotes the step size we use at the ith step.
Finally, if we solve a vector system, we basically apply the technique
above to each component of the vector solution. So if the problem
is four dimensional, we are gettting a constant C and D for each component.
This of course complicates our life as one component might have such
a fast growing error that the C and D for it is very large compared to the
others. To control this, we generally cut the step size h to bring down the
error. Hence, the component whose error grows the fastest controls how
big a step size we can use in this type of technique.
Runge-Kutta Methods:
As discussed above, these methods are based on multiple function
evaluations. Say you want to solve our problem on the
interval [0,tf]. If we divide this interval into
a collection of discrete points somehow, say
{ tn } for n ranging from 0 to a largest index N,
then at each such time point, we generate a new estimate of
the vector solution yn. So we start at the initial data y0 at time 0
and use an estimation formula to generate a new approximate solution
value y1 for time t1 and so forth until we hit the final
time tN with associated final approximate solution value yN.
If we want a solution on the interval [0,100] for example,
if we used a uniform step size of h = 1.0e-3, we would
generate 1000 y vectors for each unit of time.
Hence, our solution would be encoded in a vector of size
105 and yn would be the numerical solution to our problem
for time tn = n h.
Now in the theory of the numerical solution of ordinary differential
equations (ODEs) we find that there a several sources of error in this
process:
-
As discussed earlier, usually we approximate the function f using
a few terms of the Taylor series expansion of f around the point
(tn,yn) at
each iteration. This truncation of the Taylor series expansion is not
the same as the true
function f of course, so there is an error made. Depending on how many
terms we use in the Taylor series expansion, the error can be
large or small. If we let hn denote the difference tn+1 - tn,
a fourth order method is one where this error is of the from
C hn5 for some constant C. This means that is you use a
step size which is one half hn, the error decreases by a factor
of 32. For a Runga - Kutte method to have a local truncation error
of order 5, we would need to do 4 function evaluations.
Now this error is local to this time step, so
if we solve over a very long interval with say N being 100,000 or
more, the global error can grow quite large due to the addition of
so many small errors. So the numerical solution of an ODE
can be very acurrate locally but still have a lot of problems when
we try to solve over a long time interval. For example, if
we want to track a voltage pulse over 1000 milliseconds, if we
use a step size of 10-4 this amounts to 107 individual steps.
Even a fifth order method can begin to have problems.
This error is called truncation error.
- We can't represent numbers with perfect precision on any
computer system, so there is an error due to that which is called
round - off error which is significant over long computations.
- There is usually a modeling error also. The model we use
does not perfectly represent the physics, biology and so forth of
our problem and so this also introduces a mismatch between our
solution and the reality we might measure in a laboratory.
The solution scheme discussed above generates
a sequence { yn } starting at y0 using
the some sort of recursion equation:
| yn+1 |
= |
yn + hn × F(tn,yn,hn,f) |
| y0 |
= |
y0 |
where hn is the step size we use at time point tn.
and the function F is some function of
the previous approximate solution yn, the step size hn
and the dynamics vector f. We usually choose a numerical
method which allows us to estimate what a good step size
would be at each step n and then alter the step size
to that optimal choice. Our order two method discussed earlier used
| F(tn,yn,hn,f) |
= |
| f(tn + |
|
hn,
xn + |
|
hn f(tn,xn)) |
|
A typical numerical algorithm
which will do this is the Runge - Kutta - Fehlberg 45,
RKF45 algorithm which works like this:
-
Use a Runge - Kutta order 4 method to generate a
solution with local error proportional to h5.
This needs four function evaluations.
- Do one more function evaluation to
obtain a Runge - Kutta order 5 method which generates a
solution with local error proportional to h6.
- We now have to ways to approximate the true solution x
at t+h. This gives us a way to compare the two
approximations and to use one more function
evaluation to get an estimate of
the error we are making. We can then use that
error estimate to see if our step size h is too large
(that is the error is too big and we can compute a new
h using our informatio), just right (we keep h as it is)
or too small (we double the step size).
Since this algorithm uses siz function evaluations per
time step that is actually used, it is very costly.
Indeed, if we alter h, we go back and recompute all of the
solutions. There are better methods such as Adams - Bashford and
Adams - Moulton. However, for
our tutorial here, we will start with this method.
Finally, just to give you the flavor of what needs to be
computed,
here is a outline of the method: in what follows K is
a six dimensional vector which we use to store intermediate results.
Now recall that the vector function f we use in our
Hodgkin - Huxley simulations is forbiddingly complex and so these
six function evaluations really slow us down!!
-
Initialize the step size h to h0.
- Initialize the time t to t0.
- While the time t is less than the final time tf
-
Compute f(t,y)
-
Set K[0] to be h f.
- Set z to be y + 0.25 K[0]
- Compute f(t+1/4 h,z)
-
Set K[1] to be h f.
- Set z to be
y + 1.0/32.0( 3.0 K[0]+9.0 K[1] )
- Compute f(t+3.0/8.0h,z).
-
Set K[2] to be h f.
- Set z to be
y + 1.0/2197.0( 1932.0 K[0]-7200.0 K[1]+7296.0 K[2] )
- Compute f(t+12.0/13.0 h,z)
-
Set K[3] to be h*f.
- Set z to be
y + 439.0/216.0K[0]-8.0K[1]+3680.0/513.0 K[2]
- 845.0/4104.0K[3]
- Compute f(t+h,z)
-
Set K[4] to be h f
- Set z to be
y - 8.0/27.0K[0] + 2.0K[1] - 3544.0/2565.0K[2]
+ 1859.0/4104.0K[3] - 11.0/40.0K[4]
- Compute f(t+1/2 h,z)
- Compute the error vector e for this step size h
-
Set e to be
1.0/360.0K[0] - 128.0/4275.0K[2]
-2197.0/75240.0K[3] + 1.0/50.0K[4]
+2.0/55.0K[5]
- Set ||e||¥ to be the largest value in the vector e.
- Set ||y||¥ to be the largest value in the vector y.
- See if we like this step size
-
For a given e1, the amount of
error we are willing to make in our discretization
of the problem and a given e2, the amount of
weight we wish to place on the solution we previously
computed y, compute
h to be e1 + e2 ||y||¥
- If ||e||¥ is smaller than h,
then we like this step size and we use it to compute the next
value of y to be
y + d y where
delta y is set to
16.0/135.0K[0] + 6656.0/12825.0K[2]
+ 28561.0/56430.0K[3] - 9.0/50.0K[4]
+ 2.0/55.0K[5]
-
Since we like this stepsize h, use it to
reset the time to t+h.
- Now although we like this step size, it might be
smaller than we need, so we look at ||e||¥
and see if it is smaller than some fraction of
h. Here we see if ||e||¥ is smaller than
.3 h. If it is, we think the step size is too small
and we double the step size to 2 h.
- If ||e||¥ is not smaller than h,
it is possible that the step size is too big
-
We check to see if ||e||¥
is bigger than e1, the allowed tolerance
on our discretization error. If so, we
set h to be
.9 h h/||e||¥. This weird
sounding formula works like this: Suppose the
biggest error in the new computed solution is
5 times the allowed tolerance we seek -- e1.
Then this calculation gives
Now if the tolerance e2 is r times
e1, then
we would reset h to be
| hnew |
= |
.18 h (1 + r ||y||¥)
|
Hence the maximum component size of the solution
y influences how we reset the step size. If
||y||¥ was 1, then for r one, we would
have the new step size is .36 h. Of course,
the calculations are a bit more messy the
||y||¥ term is constantly changing, but this
should give you the idea.
2.3 The Koch Modified Sodium/ Potassium Model:
We will not use these data fits. Instead we will use
a more complicated model given in (Koch, 8) which
models not only Sodium and Potassium voltage dependent
gates but also Calcium gates, extracellular Potassium
concentration outside the cellular membrane, Calcium pumps
and even the diffusion of Calcium ions across the cellular
interior. You will probably want to look at this article
to get a better appreciation of how complex it is!
We will start with only Sodium, Potassium gates and leakage
current following Equation2.1. This means
we will not use many of the other gates and so forth in the
Koch model. Our simple model then consists of the transient
outward potassium current and one type of potassium current
called the transient outward Potassium Current. Since there
will eventually be three potassium currents, this particular
current will modeled using variables with
K1 subscripts while as usual the sodium current will be
labeled with variables using NA subscripts.
In addition, the Koch model uses a
somewhat diferent gate model from that of Hodgkin and Huxley:
| gNa(V,t) |
= |
g0Na mNA2(V,t) hNA(V,t) |
| gK1(V,t) |
= |
g0K1 mK1(V,t) hK1(V,t) |
where you will note that the Potassium gate now has an inactivation
component. Our previous discussion for the kinetics of the
activation and inactivation still applies though so we
know these variables satisfy
| mNA(V,t) |
= |
| (m0 - mNA¥(V))
e |
|
+ mNA¥(V) |
|
where the initial constant m0 should be chosen as follows:
we assume that the cell is at equilibrium voltage EM. Then
the steady state mNA value is obtained by letting
t go to infinity: hence we should choose
Hence, we have
| mNA(V,t) |
= |
| (mNA¥(EM) - mNA¥(V))
e |
|
+ mNA¥(V) |
|
A similar analysis shows
|
hNA(V,t) |
= |
| (hNA(EM) - hNA¥(V))
e |
|
+ hNA¥(V) |
|
| mK1(V,t) |
= |
| (mK1(EM) - mK1¥(V))
e |
|
+ mK1¥ |
|
| hK1(V,t) |
= |
| (hK1¥ - hK1¥(V))
e |
|
+ hK1¥ |
|
| |
The important steady state parameters ( m¥ types)
and time constants ( t types) are then typically given in terms
of the voltage dependent pairs of constants (a,b)
as previously discussed. Now these functions must be provided
from curve fit data, spline or neural net models and so forth.
Here we will provide nonlinear curve fits as given by
(Koch, 8). The t parameters in (Koch, 8)
are doubled from what would follow from our discussion of
the kinetics; hence there is a factor of two in the t
equations that follow:
| tNAm |
= |
|
| mNA¥ |
= |
|
| tNAh |
= |
|
| hNA¥ |
= |
|
The a and b curve fits are given to us as follows:
| aNAm |
= |
|
| bNAm |
= |
|
| aNAh |
= |
|
| bNAh |
= |
|
In Figure 2.3 we can see the a and
beta curves for the activation variable mNA plotted together.
Figure 2.3: Activation a and b Curves for Sodium:
Further, we can the the a and
beta curves plotted together for the inactivation variable
hNA in Figure 2.4
Figure 2.4: Inactivation a and b Curves for Sodium:
The (a,b) for the activation and inactivation
variables for the h parameters of the
K1 gate are not provided in (Koch, 8); instead
the mK1¥, tK1m, hK1¥
and tK1h are given to us directly.
| mK1¥ |
= |
|
| tK1m |
= |
1.38 |
| mK1h |
= |
|
| tK1h |
= |
|
If the voltage was a constant, we can actually integrate these
kinetics in closed form obtaining
| gNA(V,t) |
= |
gNA0 mNA2(V,t) hNA(V,t) |
| gK1(V,t) |
= |
gK10 mK1(V,t) hK1(V,t) |
Of course, the voltage is not constant, so we treat
mNA, hNA and so forth as independent variables in
our ODE integration scheme. We need to have five independent
variables here so we set up a five dimensional vector
y whose components will be interpeted as follows:
| y[0] |
= |
V |
| y[1] |
= |
mNA |
| y[2] |
= |
hNA |
| y[3] |
= |
mK1 |
| y[4] |
= |
hK1 |
where we use as starting conditions
| T |
= |
22.0 |
| gL |
= |
0.02 |
| EL |
= |
-49.0 |
| CM |
= |
0.15 |
| gNa0 |
= |
2.0 |
| gK10 |
= |
2.7708 |
with initial concentrations
| [NA]o |
= |
491.0 |
| [NA]i |
= |
50.0 |
| [K]o |
= |
7.859 |
| [K]i |
= |
140.0 |
The Koch model has many additional gates and so forth with
associated conductances. For this simple one gate Sodium and
one gate Potassium model we will lump all these other
conductances into the K1 term giving
the number 2.7708 obtained from
| g0K1 |
= |
0.120 + 1.17
+ 0.084
+ 1.2
+ 0.054
+ 0.02675
+ 0.116 |
| |
= |
2.7708 |
Note that we can clearly see that the Sodium conductance
activates early and the Potassium conductance lags from the
three plots in Figure 2.5 (gNA),
Figure 2.6 (gK1)
and Figure 2.7 (a surface plot of .10 gNA and
gK1 together).
Figure 2.5: Sodium Conductance:
Figure 2.6: Potassium Conductance:
Figure 2.7: Sodium and Potassium Conductance:
2.4 Hodgkin-Huxley Simulation Code:
We will use both Matlab and C++ to develop simulations
of a working cell.
2.4.1 C++ Simulation Code: Hodgkin - Huxley
We can use our standard ODE X Windows simulation package (see the
Chapter 6 ) to develop a working simulation which we
will be able to modify to handle calcium dynamics.
We begin with a simple Hodgkin - Huxley giant squid axon
simulation. The system to solve is encoded in the following
function:
function.c:
The giant squid axon system is encode here for use
in a standard Runga - Kutta - Fehlberg Order 5
method. We only use simple sodium and potassium gates here.
function.c
1 #include "simulation.h"
2
3 /*-------------------------------------------------------
4 This function models simple HH dynamics: Na and K gates
5 only.
6 -------------------------------------------------------*/
7
8 void funcp(double t,DOUBLE_VECTOR& y,DOUBLE_VECTOR& f,
9 DOUBLE_VECTOR& p)
10 {
11 double g_NA_bar = p[0];
12 double g_K1_bar = p[1];
13 double g_leak_bar = p[2];
14 double E_NA = p[3];
15 double E_K = p[4];
16 double E_L = p[5];
17
18 double sum;
19 double I_NA,I_K1;
20 //units
21 /*===============================================
22 Methods in Neuronal Modeling: Chapter 4:
23 Dynamic Channels by
24 Yamada, Koch and Adams
25 Their Units:
26 voltage mV
27 current na
28 time ms
29 concentration mM
30 conductance micro Siemens
31 capacitance nF
32 ===============================================*/
33 // y vector assignments
34 /*===============================================
35 y[0] = V
36 y[1] = m_NA
37 y[2] = h_NA
38 y[3] = m_K1
39 y[4] = h_K1
40 ===============================================*/
41 /*===============================================
42 Fast Sodium Current
43 ===============================================*/
44
45 // activation parameter for I_NA
46 double alpha_mNA;
47 double beta_mNA;
48 double m_NA_infinity;
49 double t_m_NA;
50
51 sum = y[0]+33.0;
52 if(sum>0){
53 alpha_mNA = 0.36*sum/(1.0 - exp(-sum/3.0));
54 }
55 else{
56 alpha_mNA = 0.36*exp( sum/ 3.0)*sum/(exp(sum/ 3.0) - 1.0);
57 }
58 sum = y[0]+42.0;
59 if(sum>0){
60 beta_mNA = -0.4*exp( -sum/20.0)*sum/
61 (exp( -sum/20.0) - 1.0);
62 }
63 else{
64 beta_mNA = -0.4*sum/
65 (1.0 - exp(sum/20.0));
66 }
67 m_NA_infinity = alpha_mNA/(alpha_mNA+beta_mNA);
68 t_m_NA = 2.0/(alpha_mNA+beta_mNA);
69 f[1] = (m_NA_infinity - y[1])/t_m_NA;
70
71 // inactivation parameter for I_NA
72 double alpha_hNA;
73 double beta_hNA;
74 double h_NA_infinity;
75 double t_h_NA;
76
77 sum = y[0]+55.0;
78 if(sum<0){
79 alpha_hNA = -0.1*sum/
80 (1.0 - exp(sum/ 6.0));
81 }
82 else{
83 alpha_hNA = -0.1*exp( -sum/ 6.0)*sum/
84 (exp( -sum/ 6.0) - 1.0);
85 }
86 if(y[0]>0){
87 beta_hNA = 4.5/(1.0 + exp(-y[0]/10.0));
88 }
89 else{
90 beta_hNA = 4.5*exp(y[0]/10.0)/(exp(y[0]/10.0) + 1.0);
91 }
92 h_NA_infinity = alpha_hNA/(alpha_hNA+beta_hNA);
93 t_h_NA = 2.0/(alpha_hNA+beta_hNA);
94 f[2] = (h_NA_infinity - y[2])/t_h_NA;
95
96 I_NA = g_NA_bar*(y[0]-E_NA)*y[1]*y[1]*y[2];
97
98 /*===============================================
99 Transient, Outward Potassium Current
100 ===============================================*/
101 // activation parameter for I_K
102 double t_m_K1;
103 double m_K1_infinity;
104
105 sum = y[0]+42.0;
106 if(sum<0){
107 m_K1_infinity = exp(sum/13.0)/(exp(sum/13.0)+1.0);
108 }
109 else{
110 m_K1_infinity = 1.0/(1.0+exp(-sum/13.0));
111 }
112 t_m_K1 = 1.38;
113 f[3] = (m_K1_infinity - y[3])/t_m_K1;
114
115 // inactivation parameter for I_K
116 double h_K1_infinity;
117 double t_h_K1;
118
119 sum = y[0]+110.0;
120 if(sum<0){
121 h_K1_infinity = 1.0/(1.0+exp( sum/18.0));
122 }
123 else{
124 h_K1_infinity = exp( -sum/18.0)/(exp(-sum/18.0)+1.0);
125 }
126 if(y[0]<-80.0)
127 t_h_K1 = 50.0;
128 else
129 t_h_K1 = 150.0;
130 f[4] = (h_K1_infinity - y[4])/t_h_K1;
131
132 I_K1 = g_K1_bar*(y[0]-E_K)*y[3]*y[4];
133
134 /*========================================================
135 leakage current
136 ========================================================*/
137 double I_leak = g_leak_bar*(y[0]-E_L);
138
139 //Cell Voltage equation
140 static double C_N = .15;
141 double external_current = 0.0;
142
143 static double delta_t = 1.0;
144 static double t_start = 10.0;
145 static double t_end = t_start + delta_t;
146 static double injection = 20.0;
147
148 if(t>= t_start && t<= t_end)
149 external_current = injection;
150
151 /*
152 for(int burst=2;burst<51;burst+=50){
153 for(int cnt=0;cnt<1;cnt+=1){
154 sum = burst+.05*cnt;
155 if(t>= sum && t<= sum+delta_t){
156 external_current = injection;
157 }
158 }
159 }
160 */
161
162 f[0] = (external_current- I_NA - I_K1 - I_leak)/C_N ;
163 }
syntax highlighted by Code2HTML, v. 0.8.8b
rest.c:
The initial conditions for the activationa and
inactivation variables for our gates are
a little tricky to caclulate as we discussed eaqrlier. We do this
is a startup function rest.c:
rest.c
1 #include "simulation.h"
2
3 double rest(double E_M,DOUBLE_VECTOR& p,DOUBLE_VECTOR& q)
4 {
5 double sum;
6
7 double g_NA_bar = p[0];
8 double g_K1_bar = p[1];
9 double g_leak_bar = p[2];
10 double E_NA = p[3];
11 double E_K = p[4];
12 //double E_L = p[5];
13
14 cout << "In rest calculation: parameters are p = " << p << endl;
15
16 /*----------------------------------------------------------
17 How do we choose E_L and g_L?
18
19 At equilibrium we must have:
20 g_total = g_NA_bar+g_K1_bar;
21 ratio_NA = g_NA_bar/(g_total+g_leak_bar);
22 ratio_K1 = g_K1_bar/(g_total+g_leak_bar);
23 ratio_L = g_leak_bar/(g_total+g_leak_bar);
24 E_M = ratio_NA*E_NA + ratio_K1*E_K + ratio_L*E_L;
25
26 For E_M we must also have
27
28 I_NA = g_NA_bar*(E_M-E_NA)*m_NA_infinity*m_NA_infinity
29 *h_NA_infinity;
30 I_K1 = g_K1_bar*(E_M-E_K)*m_K1_infinity*h_K1_infinity;
31 sum = I_NA + I_K1;
32 g_leak_bar = -sum/(E_M-E_L);
33
34 So
35 E_M*(g_total+g_leak_bar) = g_K*E_NA + g_K1*E_K1 + g_leak_bar*E_L;
36
37 Let
38
39 S1 = g_K*E_NA + g_K1*E_K1 ;
40 S2 = sum;
41
42 Then
43
44 g_leak_bar = -S2/(E_M-E_L);
45
46 yielding
47
48 E_M*(g_total -S2/(E_M-E_L)) = S1 + (-S2/(E_M-E_L)*E_L;
49 ----------------------------------------------------------*/
50
51 // activation parameter for I_NA
52 double alpha_mNA;
53 double beta_mNA;
54 double m_NA_infinity;
55
56 sum = E_M+33.0;
57 if(sum>0){
58 alpha_mNA = 0.36*sum/(1.0 - exp(-sum/3.0));
59 }
60 else{
61 alpha_mNA = 0.36*exp( sum/ 3.0)*sum/(exp(sum/ 3.0) - 1.0);
62 }
63 sum = E_M+42.0;
64 if(sum>0){
65 beta_mNA = -0.4*exp( -sum/20.0)*sum/
66 (exp( -sum/20.0) - 1.0);
67 }
68 else{
69 beta_mNA = -0.4*sum/
70 (1.0 - exp(sum/20.0));
71 }
72 m_NA_infinity = alpha_mNA/(alpha_mNA+beta_mNA);
73
74 // inactivation parameter for I_NA
75 double alpha_hNA;
76 double beta_hNA;
77 double h_NA_infinity;
78
79 sum = E_M+55.0;
80 if(sum<0){
81 alpha_hNA = -0.1*sum/
82 (1.0 - exp(sum/ 6.0));
83 }
84 else{
85 alpha_hNA = -0.1*exp( -sum/ 6.0)*sum/
86 (exp( -sum/ 6.0) - 1.0);
87 }
88 if(E_M>0){
89 beta_hNA = 4.5/(1.0 + exp(-E_M/10.0));
90 }
91 else{
92 beta_hNA = 4.5*exp(E_M/10.0)/(exp(E_M/10.0) + 1.0);
93 }
94 h_NA_infinity = alpha_hNA/(alpha_hNA+beta_hNA);
95
96 double I_NA = g_NA_bar*(E_M-E_NA)
97 *m_NA_infinity*m_NA_infinity
98 *h_NA_infinity;
99
100 /*===============================================
101 Transient, Outward Potassium Current
102 ===============================================*/
103
104 // activation parameter for I_K
105 double t_m_K1;
106 double m_K1_infinity;
107
108 sum = E_M+42.0;
109 if(sum<0){
110 m_K1_infinity = exp(sum/13.0)/(exp(sum/13.0)+1.0);
111 }
112 else{
113 m_K1_infinity = 1.0/(1.0+exp(-sum/13.0));
114 }
115
116 // inactivation parameter for I_K
117 double h_K1_infinity;
118 double t_h_K1;
119
120 sum = E_M+110.0;
121 if(sum<0){
122 h_K1_infinity = 1.0/(1.0+exp( sum/18.0));
123 }
124 else{
125 h_K1_infinity = exp( -sum/18.0)/(exp(-sum/18.0)+1.0);
126 }
127
128 double I_K1 = g_K1_bar*(E_M-E_K)*m_K1_infinity*h_K1_infinity;
129 sum = I_NA + I_K1;
130 //double g_leak_bar = -sum/(E_M-E_L);
131
132 cout << "Given g_leak_bar = 2.0 mu S find E_L" << endl;
133 double E_L = (2.0*E_M + sum)/2.0;
134 cout << "E_L = " << E_L << endl;
135
136
137 cout << "Let's check the equilibrium voltage:" << endl;
138 double g_NA_inf = g_NA_bar*m_NA_infinity*m_NA_infinity
139 *h_NA_infinity;
140 double g_K1_inf = g_K1_bar*m_K1_infinity*h_K1_infinity;
141 double g_total = g_NA_inf+g_K1_inf+g_leak_bar;
142 double ratio_NA = g_NA_inf/g_total;
143 double ratio_K1 = g_K1_inf/g_total;
144 double ratio_L = g_leak_bar/g_total;
145 double E_Mcalc = ratio_NA*E_NA + ratio_K1*E_K + ratio_L*E_L;
146
147 cout << "Calculated E_M = " << E_Mcalc << endl;
148
149 /*------------------------------------------------
150 initial activations and inactivations:
151 m_NA_infinity
152 h_NA_infinity
153 m_K1_infinity
154 h_K1_infinity
155 ------------------------------------------------*/
156
157 cout << "Initial activation and inactivations are" << endl;
158 cout << "m_NA(0) = " << m_NA_infinity << endl;
159 cout << "h_NA(0) = " << h_NA_infinity << endl;
160 cout << "m_K1(0) = " << m_K1_infinity << endl;
161 cout << "h_K1(0) = " << h_K1_infinity << endl;
162
163 q[0] = m_NA_infinity;
164 q[1] = h_NA_infinity;
165 q[2] = m_K1_infinity;
166 q[3] = h_K1_infinity;
167
168 E_L = -10.0;
169 return E_L;
170 }
syntax highlighted by Code2HTML, v. 0.8.8b
2.4.2 C++ Code: The Run-Time Code:
The simulation code is still, of course, not as user
friendly as we would like. As discussed in earlier
simulation chapters, we control our simulation by editing
the files run_plot.c and rkf5_plot.c
to choose variables to plot and so forth. Then,
we recompile the executible using standard unix make tools.
Typically, we manage all of this with a unix Makefile: here is the one
we use for this simulation:
MakeApp
1 # ------------------------- #
2 # X/ Motif Paths #
3 # ------------------------- #
4 MOTIFLIB = /usr/X11R6/lib
5 XLIB = /usr/X11R6/lib
6 # --------------------------------- #
7 # Define X libraries location #
8 # ----------------------------------#
9 XLIBS = -lXm -lXp -lXpm -lXaw -lXt -lXmu -lXext -lX11
10 # ---------------------------- #
11 # Define X include directories #
12 # ---------------------------- #
13 XINC = /usr/X11R6/include
14 MOTIFINC = /usr/X11R6/include
15 XINCLUDES = -I$(XINC) -I$(MOTIFINC)
16 #
17 MYMAKE = MakeApp
18 #
19 # -------------------------- #
20 # paths for this simulation #
21 # -------------------------- #
22 BASE_PATH = /home/petersj/ProgramsApp
23 SIM_PATH = $(BASE_PATH)/SimpleHH
24 #
25 MAIN_LIBRARY_PATH = /home/petersj/programs/JimLibs
26 MAIN_INCLUDE_PATH = /home/petersj/programs/JimIncludes
27 INCLUDE_PATH_APP = $(BASE_PATH)/sim_ode
28 INCLUDES = $(XINCLUDES) -I$(MAIN_INCLUDE_PATH) \
29 -I$(INCLUDE_PATH_XGRAPH) -I$(INCLUDE_PATH_APP)
30 #
31 LIBRARY_FLAGS_SOURCES = -L$(MAIN_LIBRARY_PATH)
32 LIBRARY_PATH_X = $(XLIB)
33 LIBRARY_PATH_MOTIF = $(MOTIFLIB)
34 LIBRARY_FLAGS_SOURCES = -L$(MAIN_LIBRARY_PATH) \
35 -L$(LIBRARY_PATH_MOTIF) -L$(LIBRARY_PATH_X)
36
37 SUNLIBS = -ll -lgen
38 LIBS = -lm
39 SHAPE_LIBS = -lbox -lshape
40 VECTOR_LIBS = -lrkf5parobjects -ldoublevec2 -ldoublevec1 -lintvec2 -lintvec1
41 SIMLIBS = $(VECTOR_LIBS) $(SHAPE_LIBS)
42 #
43 # ----------- #
44 # Definitions #
45 # ----------- #
46 DEFINES =
47 CC = g++
48 COMPILE_FLAGS_LINK = -g $(DEFINES) $(INCLUDES)
49 COMPILE_FLAGS_SOURCES = -c -g $(DEFINES) $(INCLUDES)
50 #
51 RANLIB = touch
52 # ------------- #
53 # Define target #
54 # ------------- #
55 TARGET = ode
56 OBJECTS = current.o \
57 sim.o \
58 popupdrawExposeCB.o \
59 popupdrawResizeCB.o \
60 setxcolor.o \
61 find_named_color.o \
62 update_color.o \
63 force_update.o \
64 popupquitCB.o \
65 graph_color.o \
66 axis_color.o \
67 check_points.o \
68 rkf5_plot.o \
69 function.o \
70 external_force.o \
71 run_plot.o \
72 set_plot.o \
73 handle_button_release.o \
74 imagepopupplot.o \
75 set_popupplot.o \
76 imagepopupcolor.o \
77 set_axiscolor.o \
78 set_graphcolor.o \
79 set_active.o \
80 expose.o \
81 input.o \
82 resize.o \
83 quit_application.o \
84 image.o \
85 rest.o\
86 gates.o
87 SOURCES = $(OBJECTS:.o=.c)
88
89 progs:
90 @make $(TARGET) -f $(MYMAKE)
91
92 $(TARGET): $(OBJECTS) simulation.h
93 @echo
94 @echo Creating the Executible $(TARGET)
95 @echo
96 rm -f $(TARGET)
97 $(CC) $(COMPILE_FLAGS_LINK) -o $(TARGET) $(OBJECTS) \
98 $(LIBRARY_FLAGS_SOURCES) $(SIMLIBS) $(XLIBS) $(LIBS)
99
100 # --------------------------- #
101 # 'make' template for OBJECTS #
102 # --------------------------- #
103 $(OBJECTS): $(@:.o=.c) simulation.h
104 @echo
105 @echo Compiling $@
106 @echo
107 $(CC) $(COMPILE_FLAGS_SOURCES) $(LIBS) $(@:.o=.c)
108 all:
109 @make progs -f $(MYMAKE)
110 #--------------------------------------------------------
111 # dependencies
112 #--------------------------------------------------------
113 current.o: current.c simulation.h
114 sim.o: sim.c simulation.h
115 graph_color.o: graph_color.c simulation.h
116 axis_color.o: axis_color.c simulation.h
117 check_points.o: check_points.c simulation.h
118 rkf5_plot.o: rkf5_plot.c simulation.h
119 function.o: function.c simulation.h
120 external_force.o: external_force.c simulation.h
121 run_plot.o: run_plot.c simulation.h
122 handle_button_release.o: handle_button_release.c simulation.h
123 set_axiscolor.o: set_axiscolor.c simulation.h
124 set_graphcolor.o: set_graphcolor.c simulation.h
125 set_popupplot.o: set_popupplot.c simulation.h
126 imagepopupcolor.o: imagepopupcolor.c simulation.h
127 imagepopupplot.o: imagepopupplot.c simulation.h
128 set_active.o: set_active.c simulation.h
129 set_plot.o: set_plot.c simulation.h
130 expose.o: expose.c simulation.h
131 input.o: input.c simulation.h
132 resize.o: resize.c simulation.h
133 quit_application.o: quit_application.c simulation.h
134 image.o: image.c simulation.h
135 scale.o: scale.c simulation.h
136 rotate.o: rotate.c simulation.h
137 popupdrawExposeCB.o: popupdrawExposeCB.c simulation.h
138 popupdrawResizeCB.o: popupdrawResizeCB.c simulation.h
139 popupquitCB.o: popupquitCB.c simulation.h
140 setxcolor.o: setxcolor.c simulation.h
141 find_named_color.o: find_named_color.c simulation.h
142 force_update.o: force_update.c simulation.h
143 update_color.o: update_color.c simulation.h
144 rest.o: rest.c simulation.h
145 gates.o: gates.c simulation.h
syntax highlighted by Code2HTML, v. 0.8.8b
Hence after editing these two files as you wish, you recompile
as follows:
run1.prompts
1 [petersj@dagmar SimpleHH]$ make -f MakeApp
2 make[1]: Entering directory `/home/petersj/ProgramApps/CellModels/SimpleHH'
3
4 Compiling run_plot.o
5
6 g++ -c -g -I/usr/X11R6/include -I/usr/X11R6/include -I/home/petersj/programs/JimIncludes -I -I/home/petersj/ProgramsApp/sim_ode -lm run_plot.c
7 g++: -lm: linker input file unused since linking not done
8
9 Creating the Executible ode
10
11 rm -f ode
12 g++ -g -I/usr/X11R6/include -I/usr/X11R6/include -I/home/petersj/programs/JimIncludes -I -I/home/petersj/ProgramsApp/sim_ode -o ode current.o sim.o popupdrawExposeCB.o popupdrawResizeCB.o setxcolor.o find_named_color.o update_color.o force_update.o popupquitCB.o graph_color.o axis_color.o check_points.o rkf5_plot.o function.o external_force.o run_plot.o set_plot.o handle_button_release.o imagepopupplot.o set_popupplot.o imagepopupcolor.o set_axiscolor.o set_graphcolor.o set_active.o expose.o input.o resize.o quit_application.o image.o rest.o gates.o \
13 -L/home/petersj/programs/JimLibs -L/usr/X11R6/lib -L/usr/X11R6/lib -lrkf5parobjects -ldoublevec2 -ldoublevec1 -lintvec2 -lintvec1 -lbox -lshape -lXm -lXp -lXpm -lXaw -lXt -lXmu -lXext -lX11 -lm
14 make[1]: Leaving directory `/home/petersj/ProgramApps/CellModels/SimpleHH'
15
16
syntax highlighted by Code2HTML, v. 0.8.8b
Then to run the simulation, you type ode at the prompt.
You will then see the popup windows we have discussed and see
some terminal output in the window you start the simulation in.
The output shown here discards some of the X Window diagnostic code
that is still present in the simulation for our debugging purposes!
run2.prompts
1 [petersj@dagmar SimpleHH]$ ode
2
3 Let's plot the ODE data:
4 Let's plot action potential:
5 In rest calculation: parameters are p = 2 2.77075 0.02 57.11 -71.9989 0
6
7 Given g_leak_bar = 2.0 mu S find E_L
8 E_L = -59.8051
9 Let's check the equilibrium voltage:
10 Calculated E_M = -67.352
11 Initial activation and inactivations are
12 m_NA(0) = 9.88698e-05
13 h_NA(0) = 0.987574
14 m_K1(0) = 0.200269
15 h_K1(0) = 0.0585369
16 E_NA = 57.11
17 E_K = -71.9989
18 E_M = -60
19 E_L = -10
20 g_leak_bar = 0.02
21 yinit = -60 9.88698e-05 0.987574 0.200269 0.0585369
22
23 Entering rkf5_plot code
24 parameters = 2 2.77075 0.02 57.11 -71.9989 -10
25
26 parameters = 2 2.77075 0.02 57.11 -71.9989 -10
27
28 (t,y[0]) = 0.05,-59.7984
29 (t,y[0]) = 0.1,-59.6003
30 (t,y[0]) = 0.15,-59.4057
31 (t,y[0]) = 0.2,-59.2148
32 (t,y[0]) = 0.25,-59.0273
33 (t,y[0]) = 10.05,-46.8455
34 (t,y[0]) = 20.05,-53.4617
35 (t,y[0]) = 30.05,-53.0657
36 (t,y[0]) = 40.05,-52.9331
37 (t,y[0]) = 50.05,-52.8046
38 (t,y[0]) = 60.05,-52.6794
39 (t,y[0]) = 70.05,-52.5575
40 Finished with PLOT loop
41 Returned from oderkf5_plot call
syntax highlighted by Code2HTML, v. 0.8.8b
run_plot.c:
The initialization of the simulation itself is handled
by the callback run_plot.c which
sets the parameters for the ODE solver, the initial
conditions for the ion concentrations and so forth.
run_plot.c
1 #include "simulation.h"
2
3 void run_plot(Widget w,XtPointer client_data,XtPointer call_data)
4 {
5 XmPushButtonCallbackStruct *P = (XmPushButtonCallbackStruct *)call_data;
6 application_data *T = (application_data *)client_data;
7
8 printf("Let's plot action potential:\n");
9 int i;
10
11 int size = 5;
12 double tol,tinit,tend,hinit;
13
14 /*======================================
15 Allocate memory for state variables
16 ======================================*/
17 DOUBLE_VECTOR yinit(size);
18 DOUBLE_VECTOR y(size);
19 double C = 14.28;
20
21 /*======================================
22 Allocate memory for dynamic parameters
23 p[0] g_NA_bar
24 p[1] g_K1_bar
25 p[2] g_leak_bar
26 p[3] E_NA
27 p[4] E_K
28 p[5] E_L
29
30 q[0] = m_NA(0);
31 q[1] = h_NA(0);
32 q[2] = m_K1(0);
33 q[3] = h_K1(0);
34 ======================================*/
35
36 DOUBLE_VECTOR p(6);
37 DOUBLE_VECTOR q(4);
38
39 /*----------------------------------------
40 Koch values
41 ----------------------------------------*/
42 double g_NA_bar = 2.0;
43 double g_K1_bar = 0.120+1.17+0.084+1.2+0.054
44 +0.02675+0.116; //2.7708
45 double g_leak_bar = .02;
46
47 double NA_O = 491.0;
48 double NA_I = 50.0;
49 double K_O = 7.859;
50 double K_I = 140.0;
51
52 double R = 8.31; //Rydberg's Constant
53 double DegK = 276.0 + C; //Kelvin Temperature; use C degrees Celsius
54 double F = 9.649e+4; //Faraday's constant
55 double RTF = R*DegK/F*1e+3;
56
57 double E_NA;
58 E_NA = RTF*log(NA_O/NA_I);
59 double E_K;
60 E_K = RTF*log(K_O/K_I);
61
62 double E_M = -60.0;
63
64 p[0] = g_NA_bar;
65 p[1] = g_K1_bar;
66 p[2] = g_leak_bar;
67 p[3] = E_NA;
68 p[4] = E_K;
69 p[5] = 0.0;
70
71 double E_L = rest(E_M,p,q);
72 p[5] = E_L;
73
74 cout << "E_NA = " << E_NA << endl;
75 cout << "E_K = " << E_K << endl;
76 cout << "E_M = " << E_M << endl;
77 cout << "E_L = " << E_L << endl;
78 cout << "g_leak_bar = " << g_leak_bar << endl;
79
80 /*======================================
81 Set up initial conditions
82 ======================================*/
83 T->initial = new DOUBLE_VECTOR(size);
84 DOUBLE_VECTOR& INIT = *(T->initial);
85
86 INIT[0] = E_M;
87 INIT[1] = q[0];
88 INIT[2] = q[1];
89 INIT[3] = q[2];
90 INIT[4] = q[3];
91
92 yinit = INIT;
93 y = INIT;
94 cout << "yinit = " << yinit << endl;
95
96 // y vector assignments
97 /*===============================================
98 y[0] = V
99 y[1] = m_NA
100 y[2] = h_NA
101 y[3] = m_K1
102 y[4] = h_K1
103
104 plot_type = 0 ==> y[0] range [-80,100]
105 plot_type = 1 ==> y[1] range [0,1]
106 plot_type = 2 ==> y[2] range [0,1]
107 plot_type = 3 ==> y[3] range [0,1]
108 plot_type = 4 ==> y[4] range [0,6.0e-2]
109
110 plot_type = 5 ==> alpha_m_NA range [0,50]
111 plot_type = 6 ==> beta_m_NA range [0,50]
112 plot_type = 7 ==> alpha_h_NA range [0,1]
113 plot_type = 8 ==> beta_h_NA range [0,8]
114 plot_type = 9 ==> m_NA_infinity range [0,1]
115 plot_type = 10 ==> h_NA_infinity range [0,1]
116 plot_type = 11 ==> m_K1_infinity range [0,1]
117 plot_type = 12 ==> h_K1_infinity range [0,0.08]
118 plot_type = 13 ==>t_m_NA range [0,1]
119 plot_type = 14 ==> t_h_NA range [0,1]
120 plot_type = 15 ==> t_m_K1 range [0,1]
121 plot_type = 16 ==> t_h_K1 range [0,1]
122 ===============================================*/
123 DOUBLE_VECTOR Low_Range(17);
124 DOUBLE_VECTOR High_Range(17);
125 Low_Range[0] = -80.0;
126 High_Range[0] = 100.0;
127 for(i=1;i<5;++i){
128 Low_Range[i] = 0.0;
129 High_Range[i] = 1.0;
130 }
131 High_Range[4] = 6.0e-2;
132 for(i=5;i<7;++i){
133 Low_Range[i] = 0.0;
134 High_Range[i] = 50.0;
135 }
136 for(i=7;i<17;++i){
137 Low_Range[i] = 0.0;
138 High_Range[i] = 1.0;
139 }
140 High_Range[8] = 8.0;
141 High_Range[12] = 0.08;
142 High_Range[14] = 1.0e+1;
143 int plot_type;
144 /*========================================
145 Set up parameters for ode integration
146 ========================================*/
147 hinit = 1.0e-6;
148 tinit = 0.00;
149 tend = 80.0;
150 tol = 1.0e-14;
151 plot_type = 0;
152 double ymin;
153 double ymax;
154 for(i=0;i<17;++i){
155 if(plot_type==i){
156 ymin = Low_Range[i];
157 ymax = High_Range[i];
158 }
159 }
160 T->PLOT->plot_type = plot_type;
161 T->PLOT->x_minimum = tinit;
162 T->PLOT->x_maximum = tend;
163 T->PLOT->y_minimum = ymin;
164 T->PLOT->y_maximum = ymax;
165
166 cout << "Entering rkf5_plot code" << endl;
167 cout << "parameters = " << p << endl;
168 oderkf5_plot(yinit,y,p,tol,tinit,tend,hinit,T,w);
169 cout << "Returned from oderkf5_plot call " << endl;
170
171 }
syntax highlighted by Code2HTML, v. 0.8.8b
rkf5_plot.c:
The actual call to the ODE solver is handled by the
utility function rkf5_plot.c which
as described in Chapter 6 splits
the integration interval into manageable chunks for
plotting purposes.
rkf5_plot.c
1 #include "simulation.h"
2 #include <iostream.h>
3 /*==========================================================*/
4 /* */
5 /* Runga-Fehlberg order 5 method for plotting */
6 /* */
7 /* yinit = initial state */
8 /* y = calculated state */
9 /* u = control parameter */
10 /* tinit = initial t */
11 /* tend = final t */
12 /* tol = tolerance for step size changing */
13 /* hinit = initial step size */
14 /*==========================================================*/
15 void oderkf5_plot(DOUBLE_VECTOR& yinit,
16 DOUBLE_VECTOR& y,DOUBLE_VECTOR& p,double tol,
17 double tinit,double tend,double hinit,
18 application_data *T,
19 Widget w)
20 {
21 //open a file for plot data
22 ofstream fd2;
23 fd2.open("mK1inf", ios::in);
24 DOUBLE_VECTOR GATES(14);
25 /*========================================
26 Gates[0] = alpha_m_NA;
27 Gates[1] = beta_m_NA;
28 Gates[2] = alpha_h_NA;
29 Gates[3] = beta_h_NA;
30 Gates[4] = m_NA_infinity;
31 Gates[5] = h_NA_infinity;
32 Gates[6] = m_K1_infinity;
33 Gates[7] = h_K1_infinity;
34 Gates[8] = t_m_NA;
35 Gates[9] = t_h_NA;
36 Gates[10] = t_m_K1;
37 Gates[11] = t_h_K1;
38 Gates[12] = I_NA;
39 Gates[13] = I_K1;
40 ============================================*/
41 int POINTS_PER_UNIT = 20;
42 int NUMBER_PLOT_POINTS = int(tend)*POINTS_PER_UNIT;
43 float HPLOT = (tend-tinit)/(float)NUMBER_PLOT_POINTS;
44 double tfinal;
45 int i,j,k,plot_diagnostic;
46 plot_data *PLOT = (plot_data *)(T->POP->Outside_Data);
47 int plot_type = PLOT->plot_type;
48 cout << "parameters = " << p << endl;
49 int FREQ = 200;
50
51 //set diagnostic status level
52 plot_diagnostic = 0;
53
54 PLOT->draw_axis = 1;
55 tfinal = tinit + HPLOT;
56 DOUBLE_VECTOR y0 = yinit;
57 gates(y[0],GATES,p);
58 PLOT->first_time = tinit;
59 if(0<=plot_type && plot_type<=4){
60 PLOT->first_ordinate = y[plot_type];
61 }
62 else if(5<=plot_type && plot_type<17){
63 PLOT->first_ordinate = GATES[plot_type-5];
64 }
65 fd2 << PLOT->first_time << " " << PLOT->first_ordinate << endl;
66 for(int count=0; count<NUMBER_PLOT_POINTS; ++ count){
67 int debug = 0;
68 int display_counter = 1;
69 int plot_counter = 1;
70
71 //integrate from tinit to tfinal
72 oderkf5_parobjects(y0,y,p,tol,tinit,tfinal,hinit);
73 gates(y[0],GATES,p);
74 if( count%FREQ==0 || count < 5){
75 if(0<=plot_type && plot_type<=4){
76 cout << "(t,y[" << plot_type << "]) = " << tfinal
77 << "," << y[plot_type] << endl;
78 }
79 else if(5<=plot_type && plot_type<17){
80 cout << "(t,GATES[" << plot_type-5 << "]) = " << tfinal
81 << "," << GATES[plot_type-5] << endl;
82 }
83 }
84
85 PLOT->second_time = tfinal;
86 if(0<=plot_type && plot_type<=4){
87 PLOT->second_ordinate = y[plot_type];
88 }
89 else if(5<=plot_type && plot_type<17){
90 PLOT->second_ordinate = GATES[plot_type-5];
91 }
92 if(plot_diagnostic==1){
93 printf("first_time = %8.2f first_ordinate = %8.2f\n",
94 PLOT->first_time,PLOT->first_ordinate);
95 printf("second_time = %8.2f second_ordinate = %8.2f\n",
96 PLOT->second_time,PLOT->second_ordinate);
97 }
98 fd2 << PLOT->second_time << " " << PLOT->second_ordinate << endl;
99 T->POP->Image(w,T->POP);
100 PLOT->draw_axis = 0;
101 PLOT->first_time = PLOT->second_time;
102 PLOT->first_ordinate = PLOT->second_ordinate;
103 //reset yinit
104 y0 = y;
105 tinit = tfinal;
106 tfinal += HPLOT;
107 }//plot points loop
108 fd2.close();
109 cout << "Finished with PLOT loop " << endl;
110 }
syntax highlighted by Code2HTML, v. 0.8.8b
Run-time Results:
We are now ready to begin our numerical simulations. We will use
standard numerical solution techniques for the equation
|
= |
- Im + IK1
+ INa + IL |
|
= |
mNA¥ - mNA |
|
= |
hNA¥ - hNA |
|
= |
mK1¥ - mK1 |
|
= |
hK1¥ - hK1 |
| |
with initial conditions
| V(0) |
= |
-60.00 |
| mNA(-60) |
= |
9.88698e-05 |
| hNA(-60) |
= |
0.987574 |
| mK1(-60) |
= |
0.200269 |
| hK1(-60) |
= |
0.0585369 |
For our numerical work, we let our variable vector be y and
use the assignments:
| y[0] |
= |
V |
| y[1] |
= |
mNA |
| y[2] |
= |
hNA |
| y[3] |
= |
mK1 |
| y[4] |
= |
hK1 |
If we run the simulation with the parameters as set in
run_plot.c and rest.c
we see the voltage traces of Figure 2.8
for three different starting membrane voltages.
Figure 2.8: Simple Hodgkin - Huxley Dynamics:
2.4.3 MatLab Simulation Code:
This MatLab is more complicated and it is worth your while to
have studied the previous chapter on how to solve Ordinary
Differential Equations in MatLab.
Simple code to manage our solution of the Hodgkin-Huxley equations is
given below. We simply integrate the equations using a fixed
time step repeatedly with a Runga-Kutte method of order 4.
This is not terribly accurate but it illustrates the results
nicely nevertheless.
SolveSimpleHH.m
The code to manage the solution of the ODE is given below:
SolveSimpleHH.m
1 function [tvals,g_NA,g_K,V] = SolveSimpleHH(fname,iename,t0,y0,h,k,n,...
2 g_Kmax,g_NAmax)
3
4 %
5 % We use a simple Runge-Kutta scheme
6 % using the Matlab function below:
7 %
8 %function [tvals,g_Na,g_K,V] = HHFixedRK(fname,iename,t0,y0,h,k,n)
9 %
10 % Gives approximate solution to
11 % y'(t) = f(t,y(t))
12 % y(t0) = y0
13 % using a kth order RK method
14 %
15 % t0 initial time
16 % y0 initial state
17 % h stepsize
18 % k RK order 1<= k <= 4
19 % n Number of steps to take
20 %
21 % tvals time values of form
22 % tvals(j) = t0 + (j-1)*h, 1 <= j <= n
23 %
24 % g_Na = sodium conductance
25 % g_K = potassium conductance
26 % V = membrane voltage
27 %
28 m_NA = zeros(1,n);
29 h_NA = zeros(1,n);
30 n_K = zeros(1,n);
31 g_Na = zeros(1,n);
32 g_K = zeros(1,n);
33 V = zeros(1,n);
34
35 [tvals,yvals] = HHFixedRK(fname,iename,t0,y0,h,k,n);
36
37 %
38 % store values in physical variables
39 %
40 V = yvals(1,1:n);
41 m_NA = yvals(2,1:n);
42 h_NA = yvals(3,1:n);
43 n_K = yvals(4,1:n);
44
45 %
46 % Compute g_Na and G_K
47 %
48 for i = 1:n
49 u = m_NA(i)*m_NA(i)*m_NA(i)*h_NA(i);
50 g_NA(i) = g_NAmax*u;
51 end
52
53 for i = 1:n
54 u = n_K(i)*n_K(i)*n_K(i)*n_K(i);
55 g_K(i) = g_Kmax * u;
56 end
syntax highlighted by Code2HTML, v. 0.8.8b
simpleHH.m
The Hodgkin-Huxley dynamics are encoded in the function below:
simpleHH.m
1 function f = simpleHH(t,y,IE)
2
3 % Standard Hodgkin - Huxley Model
4 % voltage mV
5 % current na
6 % time ms
7 % concentration mM
8 % conductance micro Siemens
9 % capacitance nF
10 % ===============================================
11 % y vector assignments
12 % ===============================================
13 % y(1) = V
14 % y(2) = m_NA
15 % y(3) = h_NA
16 % y(4) = m_K
17 %
18 % set size of f
19 %
20
21 f = zeros(1,4);
22
23 % ===============================================
24 % f vector assignments
25 % ===============================================
26 % y(1) = V dynamics
27 % y(2) = m_NA dynamics
28 % y(3) = h_NA dynamics
29 % y(4) = m_K dynamics
30 %
31 % ================================================
32 % Fast Sodium Current
33 % ===============================================
34 % ================================================
35 % Constants for Equilibrium Voltage Calculations
36 % ===============================================
37 %
38
39 % Rydberg's Constant
40 R = 8.31;
41 % Kelvin Temperature; use 9.3 degrees Celsius
42 T = 9.3;
43 % Faraday's constant
44 F = 9.649e+4;
45 %
46 % Compute Nernst voltages of E_K and E_Na
47 % voltage = Nernst(valence,Temperature,InConc,OutConc)
48 %
49
50 %
51 % Sodium
52 %
53 NA_O = 491.0;
54 NA_I = 50.0;
55 E_NA = Nernst(1,T,NA_I,NA_O);
56
57 %
58 % Potassium
59 %
60 K_O = 20.11;
61 K_I = 400.0;
62 E_K = Nernst(1,T,K_I,K_O);
63
64
65 % max conductance for NA
66 g_NA_bar = 120.0;
67
68 % max conductance for K
69 g_K_bar = 3.60;
70
71 %
72 % activation/inactivation parameters for NA
73 %
74 % alpha_mNA, beta_mNA
75 %
76 sum = y(1)+35.0;
77 if sum > 0
78 alpha_mNA = -0.10*sum/(exp(-sum/10.0) - 1.0);
79 else
80 alpha_mNA = -0.10*exp( sum/ 10.0)*sum/(1.0 - exp( sum/ 10.0));
81 end
82 %
83 sum = y(1)+60.0;
84 if sum > 0
85 beta_mNA = 4.0*exp( -sum/18.0);
86 else
87 beta_mNA = 4.0*exp( -sum/18.0);
88 end
89 %
90 m_NA_infinity = alpha_mNA/(alpha_mNA+beta_mNA);
91 t_m_NA = 1.0/(alpha_mNA+beta_mNA);
92 %
93 f(2) = (m_NA_infinity - y(2))/t_m_NA;
94
95 %
96 % activation, inactivation parameter for I_NA
97 %
98 % alpha_hNA, beta_mNA
99 %
100 sum = (y(1)+60.0)/20.0;
101 if sum < 0
102 alpha_hNA = 0.07*exp( -sum);
103 else
104 alpha_hNA = 0.07*exp( -sum);
105 end
106 %
107 sum = y(1)+30.0;
108 if sum>0
109 beta_hNA = 1.0/(1.0 + exp(-sum/10.0));
110 else
111 beta_hNA = exp( sum/10.0)/(exp( sum/10.0) + 1.0);
112 end
113 %
114 h_NA_infinity = alpha_hNA/(alpha_hNA+beta_hNA);
115 t_h_NA = 1.0/(alpha_hNA+beta_hNA);
116 f(3) = (h_NA_infinity - y(3))/t_h_NA;
117
118 %
119 % I_NA current
120 %
121 I_NA = g_NA_bar*(y(1)-E_NA)*y(2)*y(2)*y(2)*y(3);
122
123 %
124 % activation/inactivation parameters for K
125 %
126 % alpha_mK
127 %
128 sum = y(1)+50.0;
129 if sum > 0
130 alpha_mK = -0.01*sum/(exp(-sum/10.0) - 1.0);
131 else
132 alpha_mK = -0.01*exp(sum/10.0)*sum/(1.0 - exp(sum/10.0));
133 end
134 sum = (y(1)+60.0)*0.0125;
135 if sum > 0
136 beta_mK = 0.125*exp( -sum);
137 else
138 beta_mK = 0.125*exp( -sum);
139 end
140 t_m_K = 1.0/(alpha_mK+beta_mK);
141 m_K_infinity = alpha_mK/(alpha_mK+beta_mK);
142 f(4) = (m_K_infinity - y(4))/t_m_K;
143
144 %
145 % I_K current
146 %
147 I_K = g_K_bar*(y(1)-E_K)*y(4)*y(4)*y(4)*y(4);
148
149 %
150 % leakage current
151 %
152 g_leak = 0.30;
153 E_leak = -49.0;
154
155 %
156 % I_L current
157 %
158 I_leak = g_leak*(y(1)-E_leak);
159
160 %
161 % Cell Capacitance
162 %
163 C_M = 1.0;
164 s = feval(IE,t);
165 f(1) = (s - I_NA - I_K - I_leak)/C_M;
syntax highlighted by Code2HTML, v. 0.8.8b
HHFixedRK.m
The code that manages the call to
a fixed Runga-Kutte order method from an earlier chapter has
been modified to accept an argument which is the name
of the function which models the current injection.
HHFixedRK.m
1 function [tvals,yvals] = HHFixedRK(fname,iename,t0,y0,h,k,n)
2 %
3 % Gives approximate solution to
4 % y'(t) = f(t,y(t))
5 % y(t0) = y0
6 % using a kth order RK method
7 %
8 % t0 initial time
9 % y0 initial state
10 % h stepsize
11 % k RK order 1<= k <= 4
12 % n Number of steps to take
13 %
14 % tvals time values of form
15 % tvals(j) = t0 + (j-1)*h, 1 <= j <= n
16 % yvals approximate solution
17 % yvals(:j) = approximate solution at
18 % tvals(j), 1 <= j <= n
19 % fname name of dynamics function
20 % iename name of current injection function
21 %
22 tc = t0;
23 yc = y0;
24 tvals = tc;
25 yvals = zeros(4,n);
26 yvals(1:4,1) = transpose(yc);
27 fc = feval(fname,tc,yc,iename);
28 for j=1:n-1
29 [tc,yc,fc] = HHRKstep(fname,iename,tc,yc,fc,h,k);
30 yvals(1:4,j+1) = transpose(yc);
31 tvals = [tvals tc];
32 end
syntax highlighted by Code2HTML, v. 0.8.8b
HHRKstep.m
The actual Runga-Kutte code was also modified to allow the
name of the current injection function to be entered as an argument.
HHRKstep.m
1 function [tnew,ynew,fnew] = HHRKstep(fname,iename,tc,yc,fc,h,k)
2 %
3 % fname the name of the right hand side function f(t,y)
4 % t is a scalar usually called time and
5 % y is a vector of size d
6 % yc approximate solution to y'(t) = f(t,y(t)) at t=tc
7 % fc f(tc,yc)
8 % h The time step
9 % k The order of the Runge-Kutta Method 1<= k <= 4
10 %
11 % tnew tc+h
12 % ynew approximate solution at tnew
13 % fnew f(tnew,ynew)
14 %
15 if k==1
16 k1 = h*fc;
17 ynew = yc+k1;
18 elseif k==2
19 k1 = h*fc;
20 k2 = h*feval(fname,tc+(h/2),yc+(k1/2),iename);
21 ynew = yc + (k1+k2)/2;
22 elseif k==3
23 k1 = h*fc;
24 k2 = h*feval(fname,tc+(h/2),yc+(k1/2),iename);
25 k3 = h*feval(fname,tc+h,yc-k1+2*k2,iename);
26 ynew = yc+(k1+4*k2+k3)/6;
27 elseif k==4
28 k1 = h*fc;
29 k2 = h*feval(fname,tc+(h/2),yc+(k1/2),iename);
30 k3 = h*feval(fname,tc+(h/2),yc+(k2/2),iename);
31 k4 = h*feval(fname,tc+h,yc+k3,iename);
32 ynew = yc+(k1+2*k2+2*k3+k4)/6;
33 else
34 disp(sprintf('The RK method %2d order is not allowed!',k));
35 end
36 tnew = tc+h;
37 fnew = feval(fname,tnew,ynew,iename);
syntax highlighted by Code2HTML, v. 0.8.8b
RunTime Results:
We ran our simple simulation using this session:
runtime1.prompts
1 >> [tvals,g_NA,g_K,V] = SolveSimpleHH('simpleHH','IE',0,y,1.0e-3,4,...
2 10000,3.6,120);
3 >> plot(tvals,V);
4 >> plot(tvals,g_NA);
5 >> plot(tvals,g_K);
6 >> plot(tvals,g_NA,'g-',tvals,g_K,'g-.');
7 >>
syntax highlighted by Code2HTML, v. 0.8.8b
In Figure ( 2.9), we se the membrane voltage versus
time curve. This is not as smooth as the one we generated in
the X windows simulation because we are using a fixed
Runga-Kutte method wihtout adaptive step size control.
Figure 2.9: Membrane Voltage vs Time:
In Figure ( 2.10), we se the ion conductance versus
time curves. Note that gK is substantially smaller than
gNA.
Figure 2.10: Ion Conductances vs Time: