Two cells connected via a gap junction#

In this example, we will set up two cells connected via a gap junction. Each of the cells has a passive leak current as its only dynamics. This plus the gap junction will produce an equilibrium potential different from both the resting potentials. We will investigate how the equilibrium potentials of the two cells change due of the gap junction connection.

../_images/network_two_cells_gap_junctions_circuit.svg

The equivalent circuit used to calculate the equilibrium potentials.#

Note

Concepts covered in this example:

  1. Creating a simulation recipe for two cells.

  2. Placing probes.

  3. Running the simulation and extracting the results.

  4. Adding a gap junction connection.

We assume prior exposure to the concepts of cable cells, recipes, and simple networks.

Walk-through#

We set up a recipe for the simulation of two cells

    def __init__(self, Vms, length, radius, cm, rL, g, gj_g, max_extent):
        """
        Vms -- membrane leak potentials of the two cells
        length -- length of cable in μm
        radius -- radius of cable in μm
        cm -- membrane capacitance in F/m²
        rL -- axial resistivity in Ω·cm
        g -- membrane conductivity in S/cm²
        gj_g -- gap junction conductivity in μS
        max_extent -- maximum extent of control volume in μm
        """

        # Call base constructor first to ensure proper initialization
        A.recipe.__init__(self)

        self.Vms = [Vm * U.mV for Vm in Vms]
        self.length = length * U.um
        self.radius = radius * U.um
        self.area = self.length * 2 * np.pi * self.radius
        self.cm = cm * U.F / U.m2
        self.rL = rL * U.Ohm * U.cm
        self.g = g * U.S / U.cm2
        self.gj_g = gj_g * U.uS
        self.max_extent = max_extent
        self.the_props = A.neuron_cable_properties()

in which we store the relevant parameters for the two cells, all of which are shared except the equilibrium potentials in Vms. These are used to build the network.

Let’s quickly check some standard callbacks:

  • num_cells returns the number of cells in the network, fixed as 2

  • cell_kind specifies that we handle cable_cell exclusively.

  • global_properties returns a list of standard parameters based on the defaults of the NEURON simulator.

  • probes record the membrane potential at the cell mid.

The two remaining methods are:

cell_description#

We construct a basic, single segment morphology from the length and radius parameters. The decor sets the basic parameters and adds the passive leak current pas with the given resting value Vms[gid] and conductivity g.

            .place('"midpoint"', A.junction("gj", g=self.gj_g.value), "gj")

The only new item is the placement of the gap junction endpoint at midpoint with the basic, builtin gj dynamics type (other dynamics may be defined and used).

gap_junctions_on#

Similar to connections_on, this method returns a list of gap junction connections and these are defined in the same manner.

    def gap_junctions_on(self, gid):
        return [A.gap_junction_connection(((gid + 1) % 2, "gj"), "gj", 1)]

By (gid + 1) % 2 we define two connections, one per cell, between the cells. This is due to the uni-directional definition of gap junctions in Arbor.

Running the simulation#

To allow runtime configuration, we define a parser for command line arguments which are used to set parameters in the recipe

# parse the command line arguments
parser = ArgumentParser(description="Two cells connected via a gap junction")

parser.add_argument(
    "--Vms",
    help="membrane leak potentials [mV]",
    type=float,
    default=[-100, -60],
    nargs=2,
)
parser.add_argument("--length", help="cell length [μm]", type=float, default=100)
parser.add_argument("--radius", help="cell radius [μm]", type=float, default=3)
parser.add_argument(
    "--cm", help="membrane capacitance [F/m²]", type=float, default=0.005
)
parser.add_argument("--rL", help="axial resistivity [Ω·cm]", type=float, default=90)
parser.add_argument("--g", help="leak conductivity [S/cm²]", type=float, default=0.001)
parser.add_argument(
    "--gj_g", help="gap junction conductivity [μS]", type=float, default=0.01
)
parser.add_argument("--max-extent", help="discretization length [μm]", type=float)

args = parser.parse_args()

We then set up the simulation and configure sampling width equal to the timestep \(dt\). Now, we can run the network.

# set up membrane voltage probes at the position of the gap junction
rec = TwoCellsWithGapJunction(**vars(args))

# configure the simulation and handles for the probes
sim = A.simulation(rec)

T = 5 * U.ms
dt = 0.01 * U.ms

# generate handles for all probes and gids.
handles = [sim.sample((gid, "Um"), A.regular_schedule(dt)) for gid in [0, 1]]

All that is left to do is to put this into a plot. The output plot below shows how the potential of the two cells approaches their equilibrium potentials, which can be computed from the given parameters.

\[ \begin{align}\begin{aligned}\bar U_i = U_i + w(U_i - U_j)\\w = \frac{\rho + g}{2 \rho + g}\end{aligned}\end{align} \]
../_images/network_two_cells_gap_junctions_result.svg

The full code#

You can find the full code of the example at python/examples/network_two_cells_gap_junctions.py

Executing the script will run the simulation with default parameters.