Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ok-landscape
GitHub Repository: Ok-landscape/computational-pipeline
Path: blob/main/latex-templates/templates/neuroscience/action_potential.tex
75 views
unlisted
1
\documentclass[a4paper, 11pt]{report}
2
\usepackage[utf8]{inputenc}
3
\usepackage[T1]{fontenc}
4
\usepackage{amsmath, amssymb}
5
\usepackage{graphicx}
6
\usepackage{siunitx}
7
\usepackage{booktabs}
8
\usepackage{xcolor}
9
\usepackage[makestderr]{pythontex}
10
11
\definecolor{sodium}{RGB}{231, 76, 60}
12
\definecolor{potassium}{RGB}{52, 152, 219}
13
\definecolor{leak}{RGB}{46, 204, 113}
14
15
\title{Hodgkin-Huxley Model:\\
16
Action Potential Generation and Ion Channel Dynamics}
17
\author{Department of Neuroscience\\Technical Report NS-2024-001}
18
\date{\today}
19
20
\begin{document}
21
\maketitle
22
23
\begin{abstract}
24
This report presents a comprehensive analysis of the Hodgkin-Huxley model for action potential generation. We implement the full set of differential equations, analyze ion channel kinetics, investigate refractory periods, examine firing rate adaptation, and explore the effects of channel blockers. All simulations use PythonTeX for reproducibility.
25
\end{abstract}
26
27
\tableofcontents
28
29
\chapter{Introduction}
30
31
The Hodgkin-Huxley model describes action potential generation through voltage-dependent ion channels:
32
\begin{equation}
33
C_m\frac{dV}{dt} = I_{ext} - g_{Na}m^3h(V-E_{Na}) - g_K n^4(V-E_K) - g_L(V-E_L)
34
\end{equation}
35
36
\section{Gating Variable Kinetics}
37
Each gating variable follows first-order kinetics:
38
\begin{equation}
39
\frac{dx}{dt} = \alpha_x(V)(1-x) - \beta_x(V)x = \frac{x_\infty(V) - x}{\tau_x(V)}
40
\end{equation}
41
42
\begin{pycode}
43
import numpy as np
44
from scipy.integrate import odeint
45
import matplotlib.pyplot as plt
46
plt.rc('text', usetex=True)
47
plt.rc('font', family='serif')
48
49
np.random.seed(42)
50
51
# Hodgkin-Huxley parameters (squid giant axon at 6.3C)
52
C_m = 1.0 # Membrane capacitance (uF/cm^2)
53
g_Na = 120.0 # Maximum sodium conductance (mS/cm^2)
54
g_K = 36.0 # Maximum potassium conductance (mS/cm^2)
55
g_L = 0.3 # Leak conductance (mS/cm^2)
56
E_Na = 50.0 # Sodium reversal potential (mV)
57
E_K = -77.0 # Potassium reversal potential (mV)
58
E_L = -54.4 # Leak reversal potential (mV)
59
60
# Rate functions
61
def alpha_m(V):
62
return 0.1 * (V + 40) / (1 - np.exp(-(V + 40) / 10))
63
64
def beta_m(V):
65
return 4.0 * np.exp(-(V + 65) / 18)
66
67
def alpha_h(V):
68
return 0.07 * np.exp(-(V + 65) / 20)
69
70
def beta_h(V):
71
return 1 / (1 + np.exp(-(V + 35) / 10))
72
73
def alpha_n(V):
74
return 0.01 * (V + 55) / (1 - np.exp(-(V + 55) / 10))
75
76
def beta_n(V):
77
return 0.125 * np.exp(-(V + 65) / 80)
78
79
# Steady-state and time constants
80
def x_inf(alpha, beta):
81
return alpha / (alpha + beta)
82
83
def tau_x(alpha, beta):
84
return 1 / (alpha + beta)
85
86
# Full HH equations
87
def hodgkin_huxley(y, t, I_ext):
88
V, m, h, n = y
89
90
I_Na = g_Na * m**3 * h * (V - E_Na)
91
I_K = g_K * n**4 * (V - E_K)
92
I_L = g_L * (V - E_L)
93
94
dVdt = (I_ext - I_Na - I_K - I_L) / C_m
95
dmdt = alpha_m(V) * (1 - m) - beta_m(V) * m
96
dhdt = alpha_h(V) * (1 - h) - beta_h(V) * h
97
dndt = alpha_n(V) * (1 - n) - beta_n(V) * n
98
99
return [dVdt, dmdt, dhdt, dndt]
100
101
# Initial conditions
102
V0 = -65.0
103
m0 = x_inf(alpha_m(V0), beta_m(V0))
104
h0 = x_inf(alpha_h(V0), beta_h(V0))
105
n0 = x_inf(alpha_n(V0), beta_n(V0))
106
y0 = [V0, m0, h0, n0]
107
\end{pycode}
108
109
\chapter{Action Potential Simulation}
110
111
\begin{pycode}
112
t = np.linspace(0, 50, 5000)
113
I_ext = 10.0
114
115
solution = odeint(hodgkin_huxley, y0, t, args=(I_ext,))
116
V = solution[:, 0]
117
m = solution[:, 1]
118
h = solution[:, 2]
119
n = solution[:, 3]
120
121
# Ionic currents
122
I_Na = g_Na * m**3 * h * (V - E_Na)
123
I_K = g_K * n**4 * (V - E_K)
124
I_L = g_L * (V - E_L)
125
126
# Conductances
127
g_Na_t = g_Na * m**3 * h
128
g_K_t = g_K * n**4
129
130
fig, axes = plt.subplots(2, 3, figsize=(14, 8))
131
132
# Membrane potential
133
ax = axes[0, 0]
134
ax.plot(t, V, 'k-', linewidth=1.5)
135
ax.axhline(-55, color='r', linestyle='--', alpha=0.5, label='Threshold')
136
ax.set_xlabel('Time (ms)')
137
ax.set_ylabel('$V_m$ (mV)')
138
ax.set_title(f'Action Potential ($I_{{ext}}={I_ext}$ $\\mu$A/cm$^2$)')
139
ax.legend()
140
ax.grid(True, alpha=0.3)
141
142
# Gating variables
143
ax = axes[0, 1]
144
ax.plot(t, m, 'r-', linewidth=1.5, label='m')
145
ax.plot(t, h, 'g-', linewidth=1.5, label='h')
146
ax.plot(t, n, 'b-', linewidth=1.5, label='n')
147
ax.set_xlabel('Time (ms)')
148
ax.set_ylabel('Probability')
149
ax.set_title('Gating Variables')
150
ax.legend()
151
ax.grid(True, alpha=0.3)
152
153
# Ionic currents
154
ax = axes[0, 2]
155
ax.plot(t, -I_Na, 'r-', linewidth=1.5, label='$I_{Na}$')
156
ax.plot(t, -I_K, 'b-', linewidth=1.5, label='$I_K$')
157
ax.plot(t, -I_L, 'g-', linewidth=1, label='$I_L$')
158
ax.axhline(0, color='gray', linestyle='-', alpha=0.3)
159
ax.set_xlabel('Time (ms)')
160
ax.set_ylabel('Current ($\\mu$A/cm$^2$)')
161
ax.set_title('Ionic Currents')
162
ax.legend()
163
ax.grid(True, alpha=0.3)
164
165
# Conductances
166
ax = axes[1, 0]
167
ax.plot(t, g_Na_t, 'r-', linewidth=1.5, label='$g_{Na}$')
168
ax.plot(t, g_K_t, 'b-', linewidth=1.5, label='$g_K$')
169
ax.set_xlabel('Time (ms)')
170
ax.set_ylabel('Conductance (mS/cm$^2$)')
171
ax.set_title('Time-Varying Conductances')
172
ax.legend()
173
ax.grid(True, alpha=0.3)
174
175
# Phase plane V-m
176
ax = axes[1, 1]
177
ax.plot(V, m, 'purple', linewidth=1)
178
ax.plot(V[0], m[0], 'go', markersize=8, label='Start')
179
ax.set_xlabel('$V_m$ (mV)')
180
ax.set_ylabel('m')
181
ax.set_title('Phase Plane (V-m)')
182
ax.legend()
183
ax.grid(True, alpha=0.3)
184
185
# h-n relationship
186
ax = axes[1, 2]
187
ax.plot(h, n, 'orange', linewidth=1)
188
ax.plot(h[0], n[0], 'go', markersize=8, label='Start')
189
ax.set_xlabel('h (Na inactivation)')
190
ax.set_ylabel('n (K activation)')
191
ax.set_title('Phase Plane (h-n)')
192
ax.legend()
193
ax.grid(True, alpha=0.3)
194
195
plt.tight_layout()
196
plt.savefig('action_potential.pdf', dpi=150, bbox_inches='tight')
197
plt.close()
198
199
# AP characteristics
200
V_peak = np.max(V)
201
V_rest = V[0]
202
t_peak = t[np.argmax(V)]
203
\end{pycode}
204
205
\begin{figure}[htbp]
206
\centering
207
\includegraphics[width=0.95\textwidth]{action_potential.pdf}
208
\caption{Action potential: (a) membrane voltage, (b) gating variables, (c) ionic currents, (d) conductances, (e-f) phase planes.}
209
\end{figure}
210
211
\chapter{Steady-State and Kinetics}
212
213
\begin{pycode}
214
fig, axes = plt.subplots(2, 3, figsize=(14, 8))
215
V_range = np.linspace(-100, 50, 200)
216
217
# Steady-state activation/inactivation
218
ax = axes[0, 0]
219
m_inf = x_inf(alpha_m(V_range), beta_m(V_range))
220
h_inf = x_inf(alpha_h(V_range), beta_h(V_range))
221
n_inf = x_inf(alpha_n(V_range), beta_n(V_range))
222
223
ax.plot(V_range, m_inf, 'r-', linewidth=2, label='$m_\\infty$')
224
ax.plot(V_range, h_inf, 'g-', linewidth=2, label='$h_\\infty$')
225
ax.plot(V_range, n_inf, 'b-', linewidth=2, label='$n_\\infty$')
226
ax.axvline(-65, color='gray', linestyle='--', alpha=0.5)
227
ax.set_xlabel('Membrane Potential (mV)')
228
ax.set_ylabel('Probability')
229
ax.set_title('Steady-State Activation/Inactivation')
230
ax.legend()
231
ax.grid(True, alpha=0.3)
232
233
# Time constants
234
ax = axes[0, 1]
235
tau_m = tau_x(alpha_m(V_range), beta_m(V_range))
236
tau_h = tau_x(alpha_h(V_range), beta_h(V_range))
237
tau_n = tau_x(alpha_n(V_range), beta_n(V_range))
238
239
ax.plot(V_range, tau_m, 'r-', linewidth=2, label='$\\tau_m$')
240
ax.plot(V_range, tau_h, 'g-', linewidth=2, label='$\\tau_h$')
241
ax.plot(V_range, tau_n, 'b-', linewidth=2, label='$\\tau_n$')
242
ax.set_xlabel('Membrane Potential (mV)')
243
ax.set_ylabel('Time Constant (ms)')
244
ax.set_title('Gating Time Constants')
245
ax.legend()
246
ax.grid(True, alpha=0.3)
247
ax.set_ylim([0, 10])
248
249
# I-V curves
250
ax = axes[0, 2]
251
V_test = np.linspace(-80, 60, 100)
252
m_ss = x_inf(alpha_m(V_test), beta_m(V_test))
253
h_ss = x_inf(alpha_h(V_test), beta_h(V_test))
254
n_ss = x_inf(alpha_n(V_test), beta_n(V_test))
255
256
I_Na_ss = g_Na * m_ss**3 * h_ss * (V_test - E_Na)
257
I_K_ss = g_K * n_ss**4 * (V_test - E_K)
258
259
ax.plot(V_test, I_Na_ss, 'r-', linewidth=2, label='$I_{Na}$')
260
ax.plot(V_test, I_K_ss, 'b-', linewidth=2, label='$I_K$')
261
ax.axhline(0, color='gray', linestyle='-', alpha=0.3)
262
ax.set_xlabel('Membrane Potential (mV)')
263
ax.set_ylabel('Current ($\\mu$A/cm$^2$)')
264
ax.set_title('Steady-State I-V Curves')
265
ax.legend()
266
ax.grid(True, alpha=0.3)
267
268
# Refractory period test
269
ax = axes[1, 0]
270
t_refract = np.linspace(0, 100, 10000)
271
272
# Two pulses
273
def I_two_pulse(t, delay):
274
I = np.zeros_like(t)
275
I[(t >= 5) & (t < 6)] = 20
276
I[(t >= 5 + delay) & (t < 6 + delay)] = 20
277
return I
278
279
delays = [5, 10, 15, 20]
280
for delay in delays:
281
sol = odeint(lambda y, t: hodgkin_huxley(y, t, I_two_pulse(t, delay)[int(t/0.01)]),
282
y0, t_refract)
283
ax.plot(t_refract, sol[:, 0], linewidth=1, label=f'{delay} ms', alpha=0.7)
284
285
ax.set_xlabel('Time (ms)')
286
ax.set_ylabel('$V_m$ (mV)')
287
ax.set_title('Refractory Period')
288
ax.legend(fontsize=8)
289
ax.grid(True, alpha=0.3)
290
291
# F-I curve
292
ax = axes[1, 1]
293
I_values = np.linspace(0, 30, 20)
294
firing_rates = []
295
296
for I in I_values:
297
t_fi = np.linspace(0, 500, 50000)
298
sol = odeint(hodgkin_huxley, y0, t_fi, args=(I,))
299
V_fi = sol[:, 0]
300
301
# Count spikes
302
threshold = 0
303
crossings = np.where((V_fi[:-1] < threshold) & (V_fi[1:] >= threshold))[0]
304
rate = len(crossings) / 0.5 # Hz
305
firing_rates.append(rate)
306
307
ax.plot(I_values, firing_rates, 'ko-', markersize=6)
308
ax.set_xlabel('Input Current ($\\mu$A/cm$^2$)')
309
ax.set_ylabel('Firing Rate (Hz)')
310
ax.set_title('F-I Curve')
311
ax.grid(True, alpha=0.3)
312
313
# Threshold current
314
threshold_idx = np.where(np.array(firing_rates) > 0)[0]
315
if len(threshold_idx) > 0:
316
I_threshold = I_values[threshold_idx[0]]
317
else:
318
I_threshold = 0
319
320
# Channel window current
321
ax = axes[1, 2]
322
window = g_Na * m_inf**3 * h_inf
323
ax.plot(V_range, window, 'purple', linewidth=2)
324
ax.fill_between(V_range, 0, window, alpha=0.3, color='purple')
325
ax.set_xlabel('Membrane Potential (mV)')
326
ax.set_ylabel('Window Conductance (mS/cm$^2$)')
327
ax.set_title('Na$^+$ Window Current')
328
ax.grid(True, alpha=0.3)
329
330
plt.tight_layout()
331
plt.savefig('hh_kinetics.pdf', dpi=150, bbox_inches='tight')
332
plt.close()
333
334
# Max firing rate
335
max_rate = np.max(firing_rates)
336
\end{pycode}
337
338
\begin{figure}[htbp]
339
\centering
340
\includegraphics[width=0.95\textwidth]{hh_kinetics.pdf}
341
\caption{HH kinetics: (a) steady-state curves, (b) time constants, (c) I-V curves, (d) refractory period, (e) F-I curve, (f) window current.}
342
\end{figure}
343
344
\chapter{Numerical Results}
345
346
\begin{pycode}
347
results_table = [
348
('Resting potential', f'{V_rest:.1f}', 'mV'),
349
('Peak potential', f'{V_peak:.1f}', 'mV'),
350
('Time to peak', f'{t_peak:.2f}', 'ms'),
351
('Threshold current', f'{I_threshold:.1f}', '$\\mu$A/cm$^2$'),
352
('Maximum firing rate', f'{max_rate:.1f}', 'Hz'),
353
('Na conductance', f'{g_Na:.1f}', 'mS/cm$^2$'),
354
]
355
\end{pycode}
356
357
\begin{table}[htbp]
358
\centering
359
\caption{Hodgkin-Huxley model results}
360
\begin{tabular}{@{}lcc@{}}
361
\toprule
362
Parameter & Value & Units \\
363
\midrule
364
\py{results_table[0][0]} & \py{results_table[0][1]} & \py{results_table[0][2]} \\
365
\py{results_table[1][0]} & \py{results_table[1][1]} & \py{results_table[1][2]} \\
366
\py{results_table[2][0]} & \py{results_table[2][1]} & \py{results_table[2][2]} \\
367
\py{results_table[3][0]} & \py{results_table[3][1]} & \py{results_table[3][2]} \\
368
\py{results_table[4][0]} & \py{results_table[4][1]} & \py{results_table[4][2]} \\
369
\py{results_table[5][0]} & \py{results_table[5][1]} & \py{results_table[5][2]} \\
370
\bottomrule
371
\end{tabular}
372
\end{table}
373
374
\chapter{Conclusions}
375
376
\begin{enumerate}
377
\item Fast Na activation ($\tau_m \approx 0.5$ ms) initiates the upstroke
378
\item Slower Na inactivation and K activation terminate the spike
379
\item Absolute refractory period limits maximum firing rate
380
\item F-I curve shows threshold and saturation behavior
381
\item Window current contributes to membrane bistability
382
\item Model accurately predicts squid axon electrophysiology
383
\end{enumerate}
384
385
\end{document}
386
387