def figure3d(title): """Explore 3D model response to a faster hs rate for figure 3D. :param title: Plot title (panel label) :return: None """ # Compute a 10000ms simulation with i_app=0 at t=0 and then i_app=0.16 at t=2000 pattern = {0 * ureg.ms: 0 * uA_PER_CM2, 2000 * ureg.ms: 0.16 * uA_PER_CM2} ic = [-65 * ureg.mV, 1, 1] # Create curried function with partial to hide the scale kwargs and solve partial_ode = partial(ode_3d, scale=2) sol, t, stimulus = pulse( model=partial_ode, parameter_name="i_app", temporal_pattern=pattern, t_max=10000, ic=ic, ) # Plot voltage trace plt.plot(t, sol[:, 0], "k") plt.plot(t, 30 * stimulus - 80, "grey") # Plot properties set_properties( title, y_label="V (mV)", y_tick=[-60, -40, -20, 0, 20], y_limits=(-80, 20), x_label="Time (ms)", x_tick=[0, 5000, 10000], x_limits=(0, 10000), )
def figure2b(title, panel=0): """Plot nullclines for different model regimes in different panels for 2B. Model regimes are taken from before depolarization block and after :param title: Plot title (panel label) :param panel: Which plot to make, without current (panel=0) or without current (panel=1) :return: None """ # Select appropriate current regime depending on panel i_app = [0 * uA_PER_CM2, 3.5 * uA_PER_CM2][panel] # Compute nullcline and set the stability s = panel == 1 # panel==1 means 2nd panel is stable nullcline_figure(v_range=[-90 * ureg.mV, 50 * ureg.mV], i_app=i_app, stability=s) # Plot Properties y_label = "h" if panel == 0 else "" y_ticklabel = None if panel == 0 else [] set_properties( title, x_label="V (mV)", y_label=y_label, x_tick=[-50, 0, 50], y_tick=[0, 0.1, 0.2, 0.3, 0.4], x_limits=[-75, 50], y_limits=[0, 0.4], y_ticklabel=y_ticklabel, )
def figure4b(title, panel=0): """Perform bifurcation analysis of 2D and 3D system for 4B1/2. :param title: Plot title (panel label) :param panel: Which plot to make, 2D (panel=0) or 3d (panel=1) :return: None """ # Compute contunuation and plot bifurcation diagram depending on the panel if panel == 0: figure4b1_continuation() x_label = "" x_tick = [-6, 0, 6] else: figure4b2_continuation() x_label = r"I$_{app}$($\mu$A/cm$^2$)" x_tick = [-0.1, 0, 0.2, 0.1] set_properties( title, y_label="V(mV)", y_tick=[-80, 0, 30], x_label=x_label, x_tick=x_tick, x_limits=(min(x_tick), max(x_tick)), )
def figure3b(title, panel=0): """Plot nullclines for different model regimes in different panels for 3B. :param title: Plot title (panel label) :param panel: Which plot to make ix referes to the index if the below i_app_list and hs_list :return: None """ # different panels (ix) use a different parameters: set the appropriate one i_app = ([0, 0.16, 0.16, 0.16] * uA_PER_CM2)[panel] hs = [0.6, 0.6, 0.2, 0.05][panel] s = panel == 3 # 4th panel only is stable nullcline_figure( v_range=[-90 * ureg.mV, 50 * ureg.mV], i_app=i_app, stability=s, hs=hs ) y_label = "h" if panel == 0 else "" y_ticklabel = None if panel == 0 else [] set_properties( title, y_label=y_label, x_tick=[-40, 40], y_tick=[0, 0.2, 0.4, 0.6, 0.8], x_limits=(-80, 50), y_limits=(0, 0.6), y_ticklabel=y_ticklabel, x_label="V (mV)", )
def figure2a(title): """Compute 2d model response to step current input for figure 2A. :param title: Plot title (panel label) :return: None """ # Compute a 3000ms simulation with i_app=0 at t=0 and then i_app=3.5 at t=2000 pattern = {0 * ureg.ms: 0 * uA_PER_CM2, 2000 * ureg.ms: 3.5 * uA_PER_CM2} end_time = 3000 * ureg.ms initial_condition = [-35 * ureg.mV, 1] # Solve ode_2d for a current pulse with above parameters solution, t, waveform = pulse( model=ode_2d, parameter_name="i_app", temporal_pattern=pattern, t_max=end_time, ic=initial_condition, ) # since the model remains in depolarization block the last time step is sufficient v = solution[:, 0] block_potential = v[-1] plt.text( 2500, block_potential + 10, "{0:.1f}".format(block_potential), horizontalalignment="center", ) # Plot voltage trace plt.plot(t, v, "k") plt.plot(t, waveform - 70, "grey") # Add an inset for ringing between 2000ms and 2100ms inset_axes = plt.gca().inset_axes([0.75, 0.5, 0.2, 0.47]) inset_axes.plot(t, v, "k") inset_axes.set_xlim([2000, 2100]) inset_axes.set_ylim([-50, 45]) inset_axes.set_xticks([]) inset_axes.set_yticks([]) plt.gca().indicate_inset_zoom(inset_axes) # Plot properties set_properties( title, y_label="V (mV)", y_tick=[-60, -30, 0, 30], x_tick=[0, 1500, 3000], x_limits=[0, 3000], )
def figure4a(title, panel=0): """Plot nullclines for different model currents (ix). :param title: Plot title (panel label) :param panel: Which plot to make ix refers to the index if the below i_app_list and hs_list :return: None """ # Select appropriate i_app and hs for the panel used i_app_list = ([[0, 3.5], [0.16, 0.16, 0.16]] * uA_PER_CM2)[panel] hs_list = [[1, 1], [0.6, 0.2, 0.05]][panel] # Stability for each curve on each panel stability = [[False, True], [False, False, True]] # Iterate over the different v nullclines from the different i_app and hs values for iy, (i_app, hs) in enumerate(zip(i_app_list, hs_list)): nullcline_figure( v_range=[-90 * ureg.mV, 50 * ureg.mV], i_app=i_app, stability=stability[panel][iy], hs=hs, color_h="g", color_v="r", ) if panel == 0: set_properties( title, x_label="V (mV)", y_label="h", x_tick=[-40, 0], y_tick=[0, 0.05, 0.1, 0.15], x_limits=(-40, 5), y_limits=(0, 0.15), ) plt.annotate("", xy=(-15, 0.05), xytext=(-20, 0.07), arrowprops=dict(arrowstyle="->")) else: set_properties( title, x_label="V (mV)", x_tick=[-60, 20], y_tick=[0, 0.2, 0.4], x_limits=(-80, 20), y_limits=(0, 0.4), ) plt.annotate("", xy=(-25, 0.3), xytext=(-20, 0.2), arrowprops=dict(arrowstyle="->"))
def figure3a(title, ix=0): """Compute 3d model response into depolarization block for step current input for figure 3A. :param title: Plot title (panel label) :param ix: Which figure to run: voltage (fig_num=0) or h (fig_num=1) :return: None """ # Compute a 6000ms simulation with i_app=0 at t=0 and then i_app=0.16 at t=2000 pattern = {0 * ureg.ms: 0 * uA_PER_CM2, 2000 * ureg.ms: 0.16 * uA_PER_CM2} ic = [-55 * ureg.mV, 0, 0] # Solve 3d model for above parameters and compute frequency solution, t_solved, stimulus = pulse( model=ode_3d, parameter_name="i_app", temporal_pattern=pattern, t_max=6000 * ureg.ms, ic=ic, ) t_spike, f_spike = compute_instantaneous_frequency(solution[:, 0], t_solved) # Plot voltage data and add frequency axis for first panel if ix == 0: v = solution[:, 0] plot_secondary_frequency(t_spike, f_spike) plt.plot(t_solved, v, "k") plt.plot(t_solved, 10 * stimulus - 80, "grey") y_tick = [-60, -40, -20, 0, 20] else: h, hs = solution[:, 1], solution[:, 2] plt.plot(t_solved, h * hs, "k") plt.plot(t_solved, hs, "k--") plt.legend(["h$_{total}$", "h$_s$"], loc="upper right") y_tick = [0, 0.2, 0.4, 0.6, 0.8] xlabel = "" if ix == 0 else "Time (ms)" ylabel = "V (mV)" if ix == 0 else "h$_{total}$, h$_s$" x_ticklabel = [] if ix == 0 else None set_properties( title, y_label=ylabel, y_tick=y_tick, x_tick=[0, 3000, 6000], x_ticklabel=x_ticklabel, x_limits=[0, 6000], x_label=xlabel, )
def figure1d(title, panel=0, use_modified_tau_n=True): """Show waveforms for 5d (ix=0) or 3d (ix=1) models. :param title: Plot title (panel label) :param panel: Set the model to use 5d/3d (panel=0/1) :param use_modified_tau_n: Optional parameter to use the original tau_n which does not work. Defaults to our tau_n :return: None """ # Select appropriate model depending on the panel model = [ode_5d, ode_3d][panel] # Run the simulation for 4200ms and start at an arbitrary point "close" to the limit cycle initial_condition = [-55 * ureg.mV, 0, 0] initial_condition = resize_initial_condition(initial_condition, model, fill=0) # Solve 5d model with appropriate tau_n if model == ode_5d: model = partial(ode_5d, use_modified_tau_n=use_modified_tau_n) t, sol = solve_ode(model, initial_condition, t_max=4200 * ureg.ms, rtol=1e-3) # Throw away the first 1000ms of the simulation t_throw_away = np.where(t > 1000)[0][0] sol = sol[t_throw_away:, :] t = t[t_throw_away:] - t[t_throw_away] # set new t[0]=0 # Plot voltage trace plt.plot(t, sol[:, 0], "k") y_label = "V (mV)" if panel == 0 else "" y_tick_label = None if panel == 0 else [] # Plot properties set_properties( title, x_label="Time (ms)", y_label=y_label, y_tick=[-80, -40, 0], y_limits=[-80, 20], y_ticklabel=y_tick_label, x_tick=[0, 1000, 2000, 3000], x_limits=[0, 3000], )
def figure_1a(title, g_na=5.92 * mS_PER_CM2 * 0.514, v_reset=-120 * ureg.mV): """Compute IV curve for 2d and 3d ode model. Based on the paper's reference of Seutin and Engel 2010 we believe what should have happened is Vm is clamped to -120 mV then Vm is clamped to a series of voltages: being returned to Vm=-120 each time. For each jump the peak Ina is computed Using the Paper's g_na = 5.92 we find a peak IV at -311 uA/cm^2 whereas the target is ~-160 uA/cm^2 so we rescale g_na to 5.92*160/311 which gives a peak IV curve at ~-160 uA/cm^2 :param title: Plot title (panel label) :param g_na: Optional sodium conductance to use: defaults to working parameter :param v_reset: Optional reset potential: defaults to working parameter :return: None """ # Compute an initial condition that is the model clamped at v_reset initial_condition = steady_state_when_clamped(v_reset) # Perform the same voltage clamp experiment for multiple models for model in [ode_3d, ode_2d]: initial_condition = resize_initial_condition(initial_condition, model) # Set plot properties for the model color, linestyle = ("grey", "solid") if model is ode_3d else ("black", "--") # compute IV curve current, voltage = current_voltage_curve( model=model, clamp_range=[-90 * ureg.mV, 60 * ureg.mV], t_max=500 * ureg.ms, ic=initial_condition, g_na=g_na, ) # plot IV curve plt.plot(voltage, current, color=color, linestyle=linestyle) # plot settings plt.legend(["3D", "2D"], loc="center left", bbox_to_anchor=(0.4, 1.05)) set_properties( title, x_label="V (mV)", y_label=r"I$_{Na}$($\mu$A/cm$^2$)", x_tick=[-80, -40, 0, 40], y_tick=[-160, 0], )
def figure4c(title, panel=0): """Compute true IV curves for 2d and 3d model for figure 4C1/2. :param title: Plot title (panel label) :param panel: Which plot to make, 2D (label=0) or 3d (label=1) :return: None """ # Select appropriate model given model = [ode_2d, ode_3d][panel] # Set IC ic = [-100 * ureg.mV, 1] ic = resize_initial_condition(ic, model, fill=1) # Compute IV curve current, voltage = current_voltage_curve( model=model, clamp_range=[-100 * ureg.mV, 20 * ureg.mV], t_max=3000 * ureg.ms, ic=ic, follow=True, ) # plot IV curve plt.plot(voltage, current, "k") plt.plot(voltage, np.zeros(np.shape(voltage)), "--", color="grey") if panel == 0: set_properties( title, x_label="V (mV)", y_label=r"I$_{stim}$($\mu$A/cm$^2$)", x_tick=[-80, -40], y_tick=[-5, 0, 5], x_limits=(-100, -20), y_limits=(-5, 5), ) else: set_properties( title, x_label="V (mV)", x_tick=[-70, -60, -50], y_tick=[-0.1, 0, 0.1, 0.2], x_limits=(-70, -50), y_limits=(-0.1, 0.2), )
def figure_1b(title, g_na=5.92 * mS_PER_CM2, pulse_width=5 * ureg.ms): """Compute the periodic step current response from figure 1B. Clamp to membrane potential to -80 then depolarize and rest the membrane potential to 0mV and -70mV every 100ms with 5mV pulses to 0mV. In the original paper g_na is 9.12 mS/cm^2 and pulses are 5ms to reproduce their results we use g_na = 5.92 and a 5 ms pulse :param title: Plot title (panel label) :param g_na: Optional sodium conductance to use: defaults to working parameter :param pulse_width: Optional pulse width (ms) to use: defaults to working parameter :return: None """ # Create a 500ms simulation clamped at -80 which then goes through the 1b clamp pattern t_max = 500 * ureg.ms pattern = generate_clamp_pattern_1b(t_max, pulse_width=pulse_width) initial_condition = steady_state_when_clamped(v_clamp=-80 * ureg.mV) # Impose v_clamp according to pattern solution, time, waveform = pulse( model=ode_3d, parameter_name="v_clamp", temporal_pattern=pattern, t_max=t_max, ic=initial_condition, g_na=g_na, ) # Compute sodium current and plot i_na = sodium_current(solution.T, default_parameters(g_na=g_na)) # Plot Na function plt.plot(time, i_na, "k") # Plot properties set_properties( title, x_label="Time (ms)", y_label="", # this is actually uA/cm^2 x_tick=[0, 200, 400], y_tick=[-250, -200, 0], x_limits=[-50, 450], )
def figure_1c(title, use_modified_tau_n=True): """Compute limit cycle in n,h phase space for the 5d model and compute the approximation n=f(h) for 1C. :param title: Plot title (panel label) :param use_modified_tau_n: Optional parameter to use the original tau_n which does not work. Defaults to our tau_n :return: None """ initial_condition = [ -55 * ureg.mV, 0, 0, 0, 0, ] # Does not need to lie on limit cycle since we throw away transient # Solve 5d model with appropriate tau_n partial_5d = partial(ode_5d, use_modified_tau_n=use_modified_tau_n) t, sol = solve_ode(partial_5d, initial_condition, t_max=4200 * ureg.ms, rtol=1e-3) # Extract h and n and discard the first half due to transient ix_half_time = int(len(t) / 2) h = sol[ix_half_time:, 1] n = sol[ix_half_time:, 4] fit_f_approx(h, n) # Plot limit cycle in phase plane plt.plot(h, n, c="grey") plt.plot(h, f_approx(h), "k") # Plot properties plt.legend(["n", "n=f(h)"], loc="center left", bbox_to_anchor=(0.3, 1.05)) set_properties( title, x_label="h", y_label="n", x_tick=[0, 0.2, 0.4, 0.6], y_tick=np.arange(0, 1, 0.2), x_limits=[0, 0.7], )
def figure3c(title): """Perform bifurcation analysis of 3D system for 3C. :param title: Plot title (panel label) :return: None """ # Compute contunuation and plot bifurcation diagram figure3c_continuation() ic = [-60 * ureg.mV, 0, 1] # solve system and overlay hs,v trajectory - zorder plotting behind bifuraction diagram t, sol = solve_ode(model=ode_3d, ic=ic, t_max=10000, i_app=0.16) plt.plot(sol[:, 2], sol[:, 0], c="grey", zorder=-1e5, linewidth=0.5) set_properties( title, y_label="V (mV)", x_limits=[0, 1], x_tick=[0, 0.5, 1], y_tick=[-80, -40, 0, 40], x_label="hs", )
def figure6( title, channel, version, ampa_scale=0.001, nmda_scale=3.7e-5, extract_time=7500 * ureg.ms, ): """Apply a synaptic pulse to the 3d model to determine potential at which depolarization block occurs. Original values in the paper do not seem to provide an accurate replication manual scaling was used (ampa and nmda_scale) to bring them into a working range :param title: Plot title (panel label) :param channel: Name of the channel nmda, ampa, i_app :param version: Version 0 or 1 of the parameters :param ampa_scale: Optional scale factor for ampa conductance :param nmda_scale: Optional scale factor for nmda conductance :param extract_time: Time where block voltage is computed (defaults to 7500ms) :return: None """ # Containers for the function and parameter values for the different channels channel_types = {"nmda": nmda_current, "ampa": ampa_current, "i_app": None} all_parameters = { "nmda": [60 * nmda_scale, 60 * nmda_scale] * mS_PER_CM2, "ampa": [2.3 * ampa_scale, 7 * ampa_scale] * mS_PER_CM2, "i_app": [0.16, 0.32] * uA_PER_CM2, } # Initialize and set properties channel_function = channel_types[channel] on_value = all_parameters[channel][version] parameter_name = "i_app" if channel == "i_app" else "g_syn" pattern = { 0 * ureg.ms: 0 * on_value.units, # off at t=0 2000 * ureg.ms: on_value, # on at t=2000 8000 * ureg.ms: 0 * on_value.units, # off at t=8000 } ic = [-65 * ureg.mV, 1, 1] # Create a curried ode_3d to take the synapse and solve it synapse_model = partial(ode_3d, synapse=channel_function) solution, t_solved, stimulus = pulse( model=synapse_model, parameter_name=parameter_name, temporal_pattern=pattern, t_max=10000 * ureg.mV, ic=ic, ) # Plot voltage trace and extract block potential plt.plot(t_solved, solution[:, 0], "k") extract_ix = np.where(t_solved > strip_dimension(extract_time))[0][0] block_potential = solution[extract_ix, 0] plt.text( 7500, block_potential + 10, "{0:.1f}".format(block_potential), horizontalalignment="center", ) # Plot parameters y_ticklabel = None if version == 0 else [] y_label = "V (mV)" if version == 0 else "" x_ticklabel = None if title[1] == "3" else [] # if row 3 x_label = "Time (ms)" if title[1] == "3" else "" # if row 3 set_properties( title, y_label=y_label, y_tick=[-80, -40, 0], y_ticklabel=y_ticklabel, x_tick=[0, 5000, 10000], x_label=x_label, x_ticklabel=x_ticklabel, )