| import scipy as sp |
| import numpy as np |
| import pylab as plt |
| from scipy.integrate import odeint |
| import sys |
|
|
| class HodgkinHuxley(): |
| """Full Hodgkin-Huxley Model implemented in Python""" |
|
|
| """ __init__ uses optional arguments """ |
| """ when no argument is passed default values are used """ |
|
|
| def __init__(self, C_m=1, gmax_Na=120, gmax_K=36, gmax_L=0.3, E_Na=50, |
| E_K=-77, E_L=-54.387, t_n=450, delta_t=0.01, |
| I_inj_amplitude=0, I_inj_duration=0, I_inj_delay=0, |
| vc_delay=10, vc_duration=30, vc_condVoltage=-65, |
| vc_testVoltage=10, vc_returnVoltage=-65, runMode='iclamp', |
| injected_current_plot=True, gating_plot=True, cond_scaling_plot=False, |
| cond_dens_plot=True, driving_force_plot=False, |
| current_plot=True, memb_pot_plot=True): |
|
|
| self.C_m = C_m |
| """ membrane capacitance, in uF/cm^2 """ |
|
|
| self.gmax_Na = gmax_Na |
| """ Sodium (Na) maximum conductances, in mS/cm^2 """ |
|
|
| self.gmax_K = gmax_K |
| """ Postassium (K) maximum conductances, in mS/cm^2 """ |
|
|
| self.gmax_L = gmax_L |
| """ Leak maximum conductances, in mS/cm^2 """ |
|
|
| self.E_Na = E_Na |
| """ Sodium (Na) Nernst reversal potentials, in mV """ |
|
|
| self.E_K = E_K |
| """ Postassium (K) Nernst reversal potentials, in mV """ |
|
|
| self.E_L = E_L |
| """ Leak Nernst reversal potentials, in mV """ |
|
|
| self.t = np.arange(0, t_n, delta_t) |
| """ The time to integrate over """ |
|
|
| """ Advanced input - injection current (single rectangular pulse only) """ |
|
|
| self.I_inj_amplitude = I_inj_amplitude |
| """ maximum value or amplitude of injection pulse """ |
|
|
| self.I_inj_duration = I_inj_duration |
| """ duration or width of injection pulse """ |
|
|
| self.I_inj_delay = I_inj_delay |
| """ start time of injection pulse """ |
|
|
| |
| self.run_mode = runMode |
| """default is current clamp""" |
|
|
| self.delay = vc_delay |
| """Delay before switching from conditioningVoltage to testingVoltage, in ms""" |
|
|
| self.duration = vc_duration |
| """Duration to hold at testingVoltage, in ms""" |
|
|
| self.conditioningVoltage = vc_condVoltage |
| """Target voltage before time delay, in mV""" |
|
|
| self.testingVoltage = vc_testVoltage |
| """Target voltage between times delay and delay + duration, in mV""" |
|
|
| self.returnVoltage = vc_returnVoltage |
| """Target voltage after time duration, in mV""" |
|
|
| self.simpleSeriesResistance = 1e7 |
| """Current will be calculated by the difference in voltage between the target and parent, divided by this value, in mOhm""" |
|
|
| |
| self.injected_current_plot = injected_current_plot |
| self.gating_plot = gating_plot |
| self.cond_scaling_plot = cond_scaling_plot |
| self.cond_dens_plot = cond_dens_plot |
| self.driving_force_plot = driving_force_plot |
| self.current_plot = current_plot |
| self.memb_pot_plot = memb_pot_plot |
|
|
| self.num_plots = (int(self.injected_current_plot) + |
| int(self.gating_plot)+ int(self.cond_scaling_plot) + |
| int(self.cond_dens_plot) + int(self.driving_force_plot) + |
| int(self.current_plot) + int(self.memb_pot_plot)) |
|
|
| self.plot_count = 0 |
|
|
| def alpha_m(self, V): |
| """Channel gating kinetics. Functions of membrane voltage""" |
| return 0.1*(V+40.0)/(1.0 - np.exp(-(V+40.0) / 10.0)) |
|
|
| def beta_m(self, V): |
| """Channel gating kinetics. Functions of membrane voltage""" |
| return 4.0*np.exp(-(V+65.0) / 18.0) |
|
|
| def alpha_h(self, V): |
| """Channel gating kinetics. Functions of membrane voltage""" |
| return 0.07*np.exp(-(V+65.0) / 20.0) |
|
|
| def beta_h(self, V): |
| """Channel gating kinetics. Functions of membrane voltage""" |
| return 1.0/(1.0 + np.exp(-(V+35.0) / 10.0)) |
|
|
| def alpha_n(self, V): |
| """Channel gating kinetics. Functions of membrane voltage""" |
| return 0.01*(V+55.0)/(1.0 - np.exp(-(V+55.0) / 10.0)) |
|
|
| def beta_n(self, V): |
| """Channel gating kinetics. Functions of membrane voltage""" |
| return 0.125*np.exp(-(V+65) / 80.0) |
|
|
| def g_Na(self, m, h): |
| """ |
| Conductance density (in mS/cm^2) |
| Sodium (Na = element name) |
| |
| | :param m: |
| | :param h: |
| | :return: |
| """ |
| return self.gmax_Na * m**3 * h |
|
|
| def I_Na(self, V, m, h): |
| """ |
| Membrane current (in uA/cm^2) |
| Sodium (Na = element name) |
| |
| | :param V: |
| | :param m: |
| | :param h: |
| | :return: |
| """ |
| return self.g_Na(m, h) * (V - self.E_Na) |
|
|
|
|
| def g_K(self, n): |
| """ |
| Conductance density (in mS/cm^2) |
| Potassium (K = element name) |
| |
| | :param n: |
| | :return: |
| """ |
| return self.gmax_K * n**4 |
|
|
| def I_K(self, V, n): |
| """ |
| Membrane current (in uA/cm^2) |
| Potassium (K = element name) |
| |
| | :param V: |
| | :param n: |
| | :return: |
| """ |
| return self.g_K(n) * (V - self.E_K) |
|
|
| |
| def I_L(self, V): |
| """ |
| Membrane current (in uA/cm^2) |
| Leak |
| |
| | :param V: |
| | :param h: |
| | :return: |
| """ |
| return self.gmax_L * (V - self.E_L) |
|
|
| def I_inj(self, t): |
| """ |
| External Current |
| |
| | :param t: time |
| | :return: step up to 10 uA/cm^2 at t>100 |
| | step down to 0 uA/cm^2 at t>200 |
| | step up to 35 uA/cm^2 at t>300 |
| | step down to 0 uA/cm^2 at t>400 |
| """ |
|
|
| """ running standalone python script """ |
| if __name__ == '__main__': |
| return 10*(t>100) - 10*(t>200) + 35*(t>300) - 35*(t>400) |
|
|
| |
| else: |
| return self.I_inj_amplitude*(t>self.I_inj_delay) - self.I_inj_amplitude*(t>self.I_inj_delay+self.I_inj_duration) |
|
|
| def I_inj_vclamp(self,t,v): |
| """ |
| External Current (vclamp) |
| |
| | :param t: time |
| | :return: injector current for voltage clamp |
| | |
| """ |
| if t > (self.delay + self.duration): |
| current_A = (self.returnVoltage - v) / self.simpleSeriesResistance |
| elif t >= self.delay: |
| current_A = (self.testingVoltage - v) / self.simpleSeriesResistance |
| elif t < self.delay: |
| current_A = (self.conditioningVoltage - v) / self.simpleSeriesResistance |
| else: |
| print('Problem in injection current calculation for voltage clamp...') |
| return 0 |
|
|
| |
| current_uA = current_A*10**6 |
| surface_area = 1000*10**-8 |
| current_density = current_uA/surface_area |
|
|
| return current_density |
|
|
| @staticmethod |
| def dALLdt(X, t, self): |
| """ |
| Integrate |
| |
| | :param X: |
| | :param t: |
| | :return: calculate membrane potential & activation variables |
| """ |
| V, m, h, n = X |
| if self.is_vclamp(): |
| dVdt = (self.I_inj_vclamp(t,V) - self.I_Na(V, m, h) - self.I_K(V, n) - self.I_L(V)) / self.C_m |
| else: |
| dVdt = (self.I_inj(t) - self.I_Na(V, m, h) - self.I_K(V, n) - self.I_L(V)) / self.C_m |
|
|
| dmdt = self.alpha_m(V)*(1.0-m) - self.beta_m(V)*m |
| dhdt = self.alpha_h(V)*(1.0-h) - self.beta_h(V)*h |
| dndt = self.alpha_n(V)*(1.0-n) - self.beta_n(V)*n |
| return dVdt, dmdt, dhdt, dndt |
|
|
| def is_vclamp(self): |
| return self.run_mode=='vclamp' or self.run_mode=='Voltage Clamp' |
|
|
| def simulate(self, init_values=[-64.99584, 0.05296, 0.59590, 0.31773]): |
| """ |
| Main simulate method for the Hodgkin Huxley neuron model |
| """ |
|
|
| |
| X = odeint(self.dALLdt, init_values, self.t, args=(self,)) |
| V = X[:,0] |
| m = X[:,1] |
| h = X[:,2] |
| n = X[:,3] |
| ina = self.I_Na(V, m, h) |
| ik = self.I_K(V, n) |
| il = self.I_L(V) |
| gna = self.g_Na(m, h) |
| gk = self.g_K(n) |
|
|
| |
| with open('hh_py_v.dat','w') as f: |
| for ti in range(len(self.t)): |
| f.write('%s\t%s\n'%(self.t[ti],V[ti])) |
|
|
| if not '-nogui' in sys.argv: |
| |
|
|
|
|
| if __name__ != '__main__': |
| plt.rcParams['figure.figsize'] = [7, 7] |
| |
| |
| plt.rcParams['legend.loc'] = "upper right" |
| |
| else: |
| plt.rcParams['figure.figsize'] = [10, 7] |
|
|
| plt.close() |
|
|
| fig=plt.figure(figsize=(7, self.num_plots * 2)) |
| fig.canvas.header_visible = False |
| |
|
|
| if self.injected_current_plot: |
| ax1 = plt.subplot(self.num_plots,1,self.plot_count + 1) |
| plt.title('Simulation of Hodgkin Huxley model neuron') |
| if self.is_vclamp(): |
| i_inj_values = [self.I_inj_vclamp(t,v) for t,v in zip(self.t,V)] |
| else: |
| i_inj_values = [self.I_inj(t) for t in self.t] |
|
|
| if self.is_vclamp(): plt.ylim(-2000,3000) |
|
|
| plt.plot(self.t, i_inj_values, 'k') |
| plt.ylabel('$I_{inj}$ ($\\mu{A}/cm^2$)') |
|
|
| self.plot_count += 1 |
|
|
|
|
| if self.gating_plot: |
| try: |
| plt.subplot(self.num_plots,1,self.plot_count+1, sharex = ax1) |
| except NameError: |
| ax1 = plt.subplot(self.num_plots,1,self.plot_count + 1) |
| plt.title('Simulation of Hodgkin Huxley model neuron') |
| plt.plot(self.t, m, 'r', label='$m$') |
| plt.plot(self.t, h, 'g', label='$h$') |
| plt.plot(self.t, n, 'b', label='$n$') |
| plt.ylabel('Gating variable') |
| plt.legend() |
| self.plot_count += 1 |
|
|
| if self.cond_scaling_plot: |
| try: |
| plt.subplot(self.num_plots,1,self.plot_count+1, sharex = ax1) |
| except NameError: |
| ax1 = plt.subplot(self.num_plots,1,self.plot_count + 1) |
| plt.title('Simulation of Hodgkin Huxley model neuron') |
| scale_na = m*m*m*h |
| scale_k = n*n*n*n |
| plt.plot(self.t, scale_na, 'c', label='$m^{3}h$') |
| plt.plot(self.t, scale_k, 'y', label='$n^{4}$') |
| plt.ylabel('Cond scaling') |
| plt.legend() |
| self.plot_count += 1 |
|
|
| if self.cond_dens_plot: |
| try: |
| plt.subplot(self.num_plots,1,self.plot_count+1, sharex = ax1) |
| except NameError: |
| ax1 = plt.subplot(self.num_plots,1,self.plot_count + 1) |
| plt.title('Simulation of Hodgkin Huxley model neuron') |
| plt.plot(self.t, gna, 'c', label='$g_{Na}$') |
| plt.plot(self.t, gk, 'y', label='$g_{K}$') |
| plt.ylabel('Cond dens ($mS/cm^2$)') |
| plt.legend() |
| self.plot_count += 1 |
|
|
|
|
| if self.driving_force_plot: |
| try: |
| ax_here = plt.subplot(self.num_plots,1,self.plot_count+1, sharex = ax1) |
| except NameError: |
| ax1 = plt.subplot(self.num_plots,1,self.plot_count + 1) |
| plt.title('Simulation of Hodgkin Huxley model neuron') |
| ax_here = ax1 |
|
|
| dna = V - self.E_Na |
| dk = V - self.E_K |
| zero = [0 for v in V] |
|
|
| |
| ax_here.fill_between(self.t, dna, color='c', alpha=0.5) |
| ax_here.fill_between(self.t, dk, color='y', alpha=0.5) |
|
|
| plt.plot(self.t, dna, 'c', label='$V_{m} - E_{Na}$', linewidth=0.8) |
| plt.plot(self.t, dk, 'y', label='$V_{m} - E_{K}$', linewidth=0.8) |
| plt.plot(self.t, zero, 'k', linestyle='dashed', linewidth=0.5) |
| plt.ylabel('Driving force (mV)') |
| plt.legend() |
| |
| |
| self.plot_count += 1 |
|
|
| if self.current_plot: |
| try: |
| plt.subplot(self.num_plots,1,self.plot_count+1, sharex = ax1) |
| except NameError: |
| ax1 = plt.subplot(self.num_plots,1,self.plot_count + 1) |
| plt.title('Simulation of Hodgkin Huxley model neuron') |
| plt.plot(self.t, ina, 'c', label='$I_{Na}$') |
| plt.plot(self.t, ik, 'y', label='$I_{K}$') |
| plt.plot(self.t, il, 'm', label='$I_{L}$') |
| plt.ylabel('Curr dens ($\\mu{A}/cm^2$)') |
| plt.legend() |
| self.plot_count += 1 |
|
|
| if self.memb_pot_plot: |
| try: |
| plt.subplot(self.num_plots,1,self.plot_count+1, sharex = ax1) |
| except NameError: |
| ax1 = plt.subplot(self.num_plots,1,self.plot_count + 1) |
| plt.title('Simulation of Hodgkin Huxley model neuron') |
| plt.plot(self.t, V, 'k') |
| plt.ylabel('$V_{m}$ (mV)') |
| plt.xlabel('Time (ms)') |
| if not self.is_vclamp(): plt.ylim(-85,60) |
| |
| self.plot_count += 1 |
|
|
| plt.tight_layout() |
| plt.show() |
|
|
| if __name__ == '__main__': |
|
|
| if '-vclamp' in sys.argv: |
| runner = HodgkinHuxley(runMode='vclamp', t_n=50, delta_t=0.0005) |
| else: |
| runner = HodgkinHuxley(runMode='iclamp', t_n=450, delta_t=0.01) |
| |
| runner.simulate() |
|
|