{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Extracellular reaction-diffusion tutorial\n", "We have expanded the capabilities the NEURON reaction diffusion module to support a macroscopic model of the extracellular space, described in a recent paper. Here is brief a tutorial that provides an overview of the Python interface." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Download files\n", "\n", "
This tutorial uses two files. You need to have them in your working directory:
\n", "\n", "The first is simple mechanism written for this tutorial that releases potassium at a constant rate per surface area. The second is a morphology from NeuroMorpho.Org.
\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:42.361112Z", "iopub.status.busy": "2025-08-18T03:36:42.360965Z", "iopub.status.idle": "2025-08-18T03:36:42.366960Z", "shell.execute_reply": "2025-08-18T03:36:42.366614Z" } }, "outputs": [], "source": [ "# Let's create the first one:\n", "steady_k_mod = r\"\"\"\n", "NEURON {\n", " SUFFIX steady_k\n", " USEION k WRITE ik VALENCE 1\n", " RANGE ik, rate\n", "}\n", "\n", "PARAMETER {\n", " rate (mA/cm2)\n", "}\n", "\n", "INITIAL {\n", " rate = 1\n", "}\n", " \n", "ASSIGNED {\n", " ik (mA/cm2)\n", "}\n", " \n", "BREAKPOINT {\n", " ik = rate\n", "}\n", "\n", "\"\"\"\n", "\n", "with open(\"steady_k.mod\", \"w\") as skmod:\n", " skmod.writelines(steady_k_mod)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:42.369161Z", "iopub.status.busy": "2025-08-18T03:36:42.368569Z", "iopub.status.idle": "2025-08-18T03:36:42.398772Z", "shell.execute_reply": "2025-08-18T03:36:42.398407Z" } }, "outputs": [ { "data": { "text/plain": [ "('c91662.swc',Begin by compiling the mod file. On Linux and Mac, this can be done by running nrnivmodl from the command line. The same works for Windows beginning with NEURON 7.7; alternatively, on Windows regardless of NEURON version, one can use the graphical tool mknrndll.
\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:42.461975Z", "iopub.status.busy": "2025-08-18T03:36:42.461560Z", "iopub.status.idle": "2025-08-18T03:36:45.344170Z", "shell.execute_reply": "2025-08-18T03:36:45.343690Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/home/docs/checkouts/readthedocs.org/user_builds/nrn/checkouts/3466/docs/rxd-tutorials\r\n", "cfiles =\r\n", "Mod files: \"steady_k.mod\"\r\n", "\r\n", "Creating 'x86_64' directory for .o files.\r\n", "\r\n", "MODOBJS= ./steady_k.o\r\n", "make[4]: warning: -j4 forced in submake: resetting jobserver mode.\r\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " -> \u001b[32mCompiling\u001b[0m mod_func.cpp\r\n", " -> \u001b[32mNMODL\u001b[0m ../steady_k.mod\r\n", "Translating steady_k.mod into /home/docs/checkouts/readthedocs.org/user_builds/nrn/checkouts/3466/docs/rxd-tutorials/x86_64/steady_k.cpp\r\n", "Thread Safe\r\n", " -> \u001b[32mCompiling\u001b[0m /home/docs/checkouts/readthedocs.org/user_builds/nrn/checkouts/3466/docs/rxd-tutorials/x86_64/steady_k.cpp\r\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " => \u001b[32mLINKING\u001b[0m shared library \"/home/docs/checkouts/readthedocs.org/user_builds/nrn/checkouts/3466/docs/rxd-tutorials/x86_64/./libnrnmech.so\"\r\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " => \u001b[32mLINKING\u001b[0m executable \"/home/docs/checkouts/readthedocs.org/user_builds/nrn/checkouts/3466/docs/rxd-tutorials/x86_64/./special\" LDFLAGS are: \r\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Successfully created x86_64/special\r\n" ] } ], "source": [ "!nrnivmodl" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Getting started" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:45.347925Z", "iopub.status.busy": "2025-08-18T03:36:45.347753Z", "iopub.status.idle": "2025-08-18T03:36:45.728591Z", "shell.execute_reply": "2025-08-18T03:36:45.726622Z" } }, "outputs": [], "source": [ "from neuron import n, rxd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For reaction diffusion in the extracellular space (ECS) you have to import the rxd from neuron. The first example we will place two single compartment neurons in a closed box of extracellular space." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:45.731073Z", "iopub.status.busy": "2025-08-18T03:36:45.730827Z", "iopub.status.idle": "2025-08-18T03:36:45.736022Z", "shell.execute_reply": "2025-08-18T03:36:45.735677Z" } }, "outputs": [], "source": [ "rxd.options.enable.extracellular = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To use extracellular reaction diffusion it must be enabled, it is enabled by default in NEURON 7.7 or later." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:45.737708Z", "iopub.status.busy": "2025-08-18T03:36:45.737564Z", "iopub.status.idle": "2025-08-18T03:36:45.742770Z", "shell.execute_reply": "2025-08-18T03:36:45.742412Z" } }, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rxd.nthread(4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Extracellular rxd supports multithreaded parallelization." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:45.745937Z", "iopub.status.busy": "2025-08-18T03:36:45.745795Z", "iopub.status.idle": "2025-08-18T03:36:45.805600Z", "shell.execute_reply": "2025-08-18T03:36:45.805197Z" } }, "outputs": [], "source": [ "n.load_file(\"stdrun.hoc\")\n", "n.load_file(\"import3d.hoc\")\n", "\n", "\n", "class Cell:\n", " def __init__(self, filename):\n", " \"\"\"Read geometry from a given SWC file and create a cell with a K+ source\"\"\"\n", " cell = n.Import3d_SWC_read()\n", " cell.input(filename)\n", " n.Import3d_GUI(cell, 0)\n", " i3d = n.Import3d_GUI(cell, 0)\n", " i3d.instantiate(self)\n", " for sec in self.all:\n", " sec.nseg = 1 + 10 * int(sec.L / 5)\n", " sec.insert(\"steady_k\")\n", "\n", " def extrema(self):\n", " \"\"\"Give the bounding box that contains the cell\"\"\"\n", " xlo = ylo = zlo = xhi = yhi = zhi = None\n", " for sec in self.all:\n", " n3d = sec.n3d()\n", " xs = [sec.x3d(i) for i in range(n3d)]\n", " ys = [sec.y3d(i) for i in range(n3d)]\n", " zs = [sec.z3d(i) for i in range(n3d)]\n", " my_xlo, my_ylo, my_zlo = min(xs), min(ys), min(zs)\n", " my_xhi, my_yhi, my_zhi = max(xs), max(ys), max(zs)\n", " if xlo is None:\n", " xlo, ylo, zlo = my_xlo, my_ylo, my_zlo\n", " xhi, yhi, zhi = my_xhi, my_yhi, my_zhi\n", " else:\n", " xlo, ylo, zlo = min(xlo, my_xlo), min(ylo, my_ylo), min(zlo, my_zlo)\n", " xhi, yhi, zhi = max(xhi, my_xhi), max(yhi, my_yhi), max(zhi, my_zhi)\n", " return (xlo, ylo, zlo, xhi, yhi, zhi)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:45.807578Z", "iopub.status.busy": "2025-08-18T03:36:45.807432Z", "iopub.status.idle": "2025-08-18T03:36:45.961666Z", "shell.execute_reply": "2025-08-18T03:36:45.961220Z" } }, "outputs": [], "source": [ "mycell = Cell(\"c91662.swc\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create a cell from the given SWC file, in this example we use a CA1 pyramidal cell taken from NeuroMorpho.Org. We insert a current source `steady_k` into all sections as a simple proof of concept example." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:45.965740Z", "iopub.status.busy": "2025-08-18T03:36:45.965565Z", "iopub.status.idle": "2025-08-18T03:36:45.974762Z", "shell.execute_reply": "2025-08-18T03:36:45.974390Z" } }, "outputs": [], "source": [ "xlo, ylo, zlo, xhi, yhi, zhi = mycell.extrema()\n", "padding = 50\n", "ecs = rxd.Extracellular(\n", " xlo - padding,\n", " ylo - padding,\n", " zlo - padding,\n", " xhi + padding,\n", " yhi + padding,\n", " zhi + padding,\n", " dx=(20, 20, 50),\n", " volume_fraction=0.2,\n", " tortuosity=1.6,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We define the extracellular region by specifying the left-bottom-back corner and right-upper-front corner of a box with voxel size dx$\\times$dy$\\times$dz, (if only one value is given the voxels are cubic).\n", "We use a macroscopic volume average approach, where the volume fraction or porosity is the free space in which particles can diffuse (typically 0.2 in the brain) and the tortuosity is the increase in path length particles take due to obstacles." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:45.976997Z", "iopub.status.busy": "2025-08-18T03:36:45.976310Z", "iopub.status.idle": "2025-08-18T03:36:45.981450Z", "shell.execute_reply": "2025-08-18T03:36:45.981103Z" } }, "outputs": [], "source": [ "k = rxd.Species(ecs, d=2.62, name=\"k\", charge=1, initial=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The species diffusing in the ECS are define in the same was as intracellular species." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The extracellular concentrations are accessible by `states3d`. Here we animate the concentrations during a 20s simulation." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:45.983283Z", "iopub.status.busy": "2025-08-18T03:36:45.983054Z", "iopub.status.idle": "2025-08-18T03:36:46.550588Z", "shell.execute_reply": "2025-08-18T03:36:46.550199Z" } }, "outputs": [], "source": [ "from matplotlib import pyplot, animation\n", "from IPython.display import HTML\n", "\n", "# use a large timestep as this model only has slow diffusive dynamics.\n", "n.dt = 20\n", "\n", "# Create an animation of average (over depth) of the concentrations\n", "def runsim(species, min_conc=3, max_conc=4, frames=1000):\n", " n.finitialize()\n", " fig = pyplot.figure()\n", " im = pyplot.imshow(species[ecs].states3d.mean(2), vmin=min_conc, vmax=max_conc)\n", " pyplot.axis(\"off\")\n", "\n", " def init():\n", " im.set_data(species[ecs].states3d.mean(2))\n", " return [im]\n", "\n", " def animate(i):\n", " n.fadvance()\n", " im.set_data(species[ecs].states3d.mean(2))\n", " return [im]\n", "\n", " anim = animation.FuncAnimation(\n", " fig, animate, init_func=init, frames=frames, interval=10\n", " )\n", " ret = HTML(anim.to_html5_video())\n", " pyplot.close()\n", " return ret" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:46.552714Z", "iopub.status.busy": "2025-08-18T03:36:46.552491Z", "iopub.status.idle": "2025-08-18T03:37:25.247891Z", "shell.execute_reply": "2025-08-18T03:37:25.247421Z" } }, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "