codexsynaptic-conductance-and-shunting.py
synaptic-conductance-and-shunting.py
"""Passive conductance illustrations; writes basename.png in the CWD.
Tested with Python 3.14.4, NumPy 2.3.5 and Matplotlib 3.10.7.
Illustrative constant steps; no action-potential generation or reset.
Units: mV, ms, pF, nS and pA, so pF/nS = ms and nS*mV = pA.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
BLUE = "#23779a"
ORANGE = "#ba5418"
PURPLE = "#7950a1"
GREEN = "#328164"
INK = "#243446"
def voltage(t, ge=0., gi=0., ei=-65., current=0.):
"""Exact constant-coefficient solution, starting at leak rest."""
capacitance, gl, el, ee, v0 = 200., 10., -65., 0., -65.
total = gl+ge+gi
steady = (gl*el+ge*ee+gi*ei+current)/total
tau = capacitance/total
return steady+(v0-steady)*np.exp(-t/tau)
def main():
plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 10,
"axes.spines.top": False, "axes.spines.right": False})
fig = plt.figure(figsize=(11, 6.8), facecolor="white")
grid = fig.add_gridspec(2, 2, width_ratios=[1, 1.3], hspace=.49, wspace=.32)
currents = fig.add_subplot(grid[:, 0])
responses = fig.add_subplot(grid[0, 1])
probe = fig.add_subplot(grid[1, 1])
fig.suptitle("Synaptic conductance: driving force and shunting", fontsize=15, y=.97)
v = np.linspace(-85., 10., 600)
for e, color in [(0., BLUE), (-60., PURPLE), (-65., ORANGE), (-75., GREEN)]:
currents.plot(v, e-v, color=color, lw=2, label=f"E = {e:g} mV")
currents.plot(e, 0, "o", color=color, ms=5)
currents.axhline(0, color="#7b8590", lw=.8)
currents.axvline(-65, color="#8793a0", ls=":", lw=1)
currents.set(title="A. Current through 1 nS", xlabel="Membrane potential V (mV)",
ylabel="Inward-positive current g(E − V) (pA)", xlim=(-85, 10), ylim=(-90, 90))
currents.legend(loc="upper right", frameon=True, fontsize=9)
currents.text(-82, 82, "Positive: inward current", fontsize=9, color=INK)
currents.text(-82, -83, "Negative: outward current", fontsize=9, color=INK)
currents.annotate("Zero current at E\ncan still mean a shunt", xy=(-65, 0), xytext=(-82, -42),
arrowprops={"arrowstyle": "->", "color": INK}, fontsize=9, color=INK)
currents.grid(alpha=.16)
t = np.linspace(0, 60, 601)
curves = [
(dict(ge=2), BLUE, "-", "Excitatory alone"),
(dict(ge=2, gi=20), ORANGE, "-", "Excitatory + rest shunt"),
(dict(gi=20, ei=-60), PURPLE, "--", "Depolarizing alone"),
(dict(ge=2, gi=20, ei=-60), PURPLE, "-", "Excitatory + depolarizing"),
(dict(gi=20, ei=-75), GREEN, "-", "Hyperpolarizing alone"),
]
for params, color, style, name in curves:
responses.plot(t, voltage(t, **params), color=color, ls=style, lw=2, label=name)
responses.set_title("B. Synaptic conductance steps", loc="left", pad=62)
responses.axhline(-65, color="#8793a0", ls=":", lw=1)
responses.set(xlabel="Time after opening (ms)",
ylabel="V (mV)", xlim=(0, 60), ylim=(-73, -52))
responses.legend(loc="lower center", bbox_to_anchor=(.5, 1.14),
fontsize=8.3, frameon=False, ncol=2, borderaxespad=0)
responses.grid(alpha=.16)
probe.plot(t, voltage(t, current=100), color=BLUE, lw=2, label="100 pA; no shunt")
probe.plot(t, voltage(t, current=100, gi=20), color=ORANGE, lw=2,
label="100 pA + rest shunt")
probe.plot(t, voltage(t, gi=20), color=ORANGE, lw=1.6, ls="--", label="Rest shunt alone")
probe.set(title="C. Same test current, different voltage response", xlabel="Time after step (ms)",
ylabel="V (mV)", xlim=(0, 60), ylim=(-66, -54))
probe.text(59, -55.6, "No shunt: 10 mV steady rise; τ = 20 ms", color=BLUE, ha="right", fontsize=9)
probe.text(59, -60.8, "With shunt: 3.33 mV steady rise; τ = 6.67 ms", color=ORANGE, ha="right", fontsize=9)
probe.text(3, -64.65, "Rest shunt alone: no voltage change",
color=ORANGE, fontsize=9)
probe.grid(alpha=.16)
fig.text(.5, .025, "Illustrative passive model: C = 200 pF, leak = 10 nS at −65 mV; "
"synaptic steps: gₑ = 2 nS at 0 mV, gᵢ = 20 nS • no spike/reset",
ha="center", color=INK, fontsize=9)
fig.subplots_adjust(left=.075, right=.98, top=.78, bottom=.12)
fig.savefig(Path(__file__).with_suffix(".png").name, dpi=110, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()