{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Neuron MODelling Language" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Intro about NMODLanguage similar to https://nrn.readthedocs.io/en/latest/hoc/modelspec/programmatic/mechanisms/nmodl.html, https://nrn.readthedocs.io/en/latest/hoc/modelspec/programmatic/mechanisms/nmodl2.html\n", "\n", "\n", "Organization:\n", "- Top level code blocks (NEURON, BREAKPOINT, etc)\n", " - Code block specific keywords/operators/variable types\n", "- Other functionality" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Compiling example mod files" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:09.586283Z", "iopub.status.busy": "2025-08-18T03:35:09.585662Z", "iopub.status.idle": "2025-08-18T03:35:21.121813Z", "shell.execute_reply": "2025-08-18T03:35:21.121271Z" } }, "outputs": [], "source": [ "%%capture --no-display\n", "!nrnivmodl mod" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## NMODL Code Blocks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### DEFINE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### INCLUDE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### NEURON" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### SUFFIX" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### POINT_PROCESS" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### THREADSAFE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### ELECTRODE_CURRENT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Example mod file\n", "\n", "Following example showcases the IClamp mechanism. It makes use of the `ELECTRODE_CURRENT` and `IF-ELSE` statement." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:21.124977Z", "iopub.status.busy": "2025-08-18T03:35:21.124239Z", "iopub.status.idle": "2025-08-18T03:35:21.249264Z", "shell.execute_reply": "2025-08-18T03:35:21.248771Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "COMMENT\r\n", "Since this is an electrode current, positive values of i depolarize the cell\r\n", "and in the presence of the extracellular mechanism there will be a change\r\n", "in vext since i is not a transmembrane current but a current injected\r\n", "directly to the inside of the cell.\r\n", "ENDCOMMENT\r\n", "\r\n", "NEURON {\r\n", "\tPOINT_PROCESS IClamp\r\n", "\tRANGE del, dur, amp, i\r\n", "\tELECTRODE_CURRENT i\r\n", "}\r\n", "UNITS {\r\n", "\t(nA) = (nanoamp)\r\n", "}\r\n", "\r\n", "PARAMETER {\r\n", "\tdel (ms)\r\n", "\tdur (ms)\t<0,1e9>\r\n", "\tamp (nA)\r\n", "}\r\n", "ASSIGNED { i (nA) }\r\n", "\r\n", "INITIAL {\r\n", "\ti = 0\r\n", "}\r\n", "\r\n", "BREAKPOINT {\r\n", "\tat_time(del)\r\n", "\tat_time(del+dur)\r\n", "\r\n", "\tif (t < del + dur && t >= del) {\r\n", "\t\ti = amp\r\n", "\t}else{\r\n", "\t\ti = 0\r\n", "\t}\r\n", "}\r\n" ] } ], "source": [ "!cat ../../src/nrnoc/stim.mod" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Functionality example" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:21.252952Z", "iopub.status.busy": "2025-08-18T03:35:21.252785Z", "iopub.status.idle": "2025-08-18T03:35:21.371018Z", "shell.execute_reply": "2025-08-18T03:35:21.370565Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "from neuron import n\r\n", "\r\n", "from utils.cell import Cell\r\n", "\r\n", "\r\n", "class hhCellIClamp(Cell):\r\n", " def _create_cell(self):\r\n", " h(\"\"\"create soma\"\"\")\r\n", " n.load_file(\"stdrun.hoc\")\r\n", " n.soma.L = 5.6419\r\n", " n.soma.diam = 5.6419\r\n", " n.soma.insert(\"hh\")\r\n", " ic = n.IClamp(n.soma(0.5))\r\n", " ic.delay = 0.5\r\n", " ic.dur = 0.1\r\n", " ic.amp = 0.3\r\n", "\r\n", " def record(self):\r\n", " v = n.Vector()\r\n", " v.record(n.soma(0.5)._ref_v, sec=n.soma)\r\n", " tv = n.Vector()\r\n", " tv.record(n._ref_t, sec=n.soma)\r\n", " nc = n.NetCon(n.soma(0.5)._ref_v, None, sec=n.soma)\r\n", " spikestime = n.Vector()\r\n", " nc.record(spikestime)\r\n", " self.record_vectors[\"spikes\"] = spikestime.to_python()\r\n", "\r\n", "\r\n", "if __name__ == \"__main__\":\r\n", " hh_IClamp_cell = hhCellIClamp()\r\n", " hh_IClamp_cell.record()\r\n", " hh_IClamp_cell.simulate(1, 0.1)\r\n", " hh_IClamp_cell.output()\r\n", " n.delete_section(sec=n.soma)\r\n", " del hh_IClamp_cell\r\n" ] } ], "source": [ "!cat python_scripts/iclamp.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### NONSPECIFIC_CURRENT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### USEION" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### READ" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### WRITE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### VALENCE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##### REPRESENTS\n", "\n", "#### Example snippet\n", "\n", "```\n", "NEURON {\n", " SUFFIX hh\n", " REPRESENTS NCIT:C17145 : sodium channel\n", " REPRESENTS NCIT:C17008 : potassium channel\n", "...\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### NMODL Variable Types" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Example mod file\n", "\n", "Following example showcases all the various NMODL variable types (`GLOBAL`, `RANGE`, `POINTER`, `BBCOREPOINTER`, `EXTERNAL`, `PARAMETER`, `ASSIGNED`)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:21.375278Z", "iopub.status.busy": "2025-08-18T03:35:21.374796Z", "iopub.status.idle": "2025-08-18T03:35:21.486618Z", "shell.execute_reply": "2025-08-18T03:35:21.486152Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ": a test of the various Variable Types\r\n", ": (RANGE, GLOBAL, POINTER, BBCOREPOINTER, EXTERNAL, PARAMETER, ASSIGNED)\r\n", "\r\n", "NEURON {\r\n", "\tSUFFIX testvars\r\n", "\tTHREADSAFE\r\n", "\tGLOBAL global_var\r\n", "\tRANGE range_var, parameter_var\r\n", "\tPOINTER p1\r\n", "\tBBCOREPOINTER my_data : changed from POINTER\r\n", "}\r\n", "\r\n", "PARAMETER {\r\n", "\tparameter_var\r\n", "\tglobal_var\r\n", "}\r\n", "\r\n", "ASSIGNED {\r\n", "\trange_var\r\n", "\tp1\r\n", "\tmy_data\r\n", "}\r\n", "\r\n", "INITIAL {\r\n", "\trange_var = 42\r\n", "}\r\n", "\r\n", "FUNCTION f1() {\r\n", "\tif (nrn_pointing(p1)) {\r\n", "\t\tf1 = p1\r\n", "\t}else{\r\n", "\t\tf1 = 0\r\n", "\t\tprintf(\"p1 does not point anywhere\\n\")\r\n", "\t}\r\n", "}\r\n", "\r\n", ": Do something interesting with my_data ...\r\n", "VERBATIM\r\n", "static void bbcore_write(double* x, int* d, int* x_offset, int* d_offset, _threadargsproto_) {\r\n", " if (x) {\r\n", " double* x_i = x + *x_offset;\r\n", " x_i[0] = ((double*)_p_my_data)[0];\r\n", " x_i[1] = ((double*)_p_my_data)[1];\r\n", " }\r\n", " *x_offset += 2; // reserve 2 doubles on serialization buffer x\r\n", "}\r\n", "\r\n", "static void bbcore_read(double* x, int* d, int* x_offset, int* d_offset, _threadargsproto_) {\r\n", " assert(!_p_my_data);\r\n", " double* x_i = x + *x_offset;\r\n", " // my_data needs to be allocated somehow\r\n", " _p_my_data = (double*)malloc(sizeof(double)*2);\r\n", " ((double*)_p_my_data)[0] = x_i[0];\r\n", " ((double*)_p_my_data)[1] = x_i[1];\r\n", " *x_offset += 2;\r\n", "}\r\n", "ENDVERBATIM\r\n" ] } ], "source": [ "!cat mod/variabletypes.mod" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### UNITS" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### PARAMETER" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ASSIGNED" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### CONSTANT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### NET_RECEIVE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Example mod files\n", "\n", "Following example showcases the ExpSyn mechanism. It makes use of the `NET_RECEIVE` block and `WATCH` and `DEFINE` statements." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:21.488934Z", "iopub.status.busy": "2025-08-18T03:35:21.488650Z", "iopub.status.idle": "2025-08-18T03:35:21.599893Z", "shell.execute_reply": "2025-08-18T03:35:21.599422Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "NEURON {\r\n", " POINT_PROCESS hhwatch\r\n", " NONSPECIFIC_CURRENT i\r\n", " GLOBAL ena, ek, erev, gna, gk, gpas\r\n", " RANGE e, g\r\n", "}\r\n", "\r\n", "UNITS {\r\n", " (mV) = (millivolt)\r\n", " (nA) = (nanoamp)\r\n", " (umho) = (micromho)\r\n", "}\r\n", "\r\n", "PARAMETER {\r\n", " ena = 50 (mV)\r\n", " ek = -80 (mV)\r\n", " erev = -65 (mV)\r\n", " gna = 0.1 (umho)\r\n", " gk = 0.03 (umho)\r\n", " gpas = 0.0001 (umho)\r\n", "}\r\n", "\r\n", "ASSIGNED {\r\n", " v (mV)\r\n", " i (nA)\r\n", " e (mV)\r\n", " g (umho)\r\n", "}\r\n", "\r\n", "DEFINE init 1\r\n", "DEFINE rise 2\r\n", "DEFINE fall 3\r\n", "DEFINE off 4\r\n", "\r\n", "INITIAL {\r\n", " g = gpas\r\n", " e = erev\r\n", " net_send(0, init)\r\n", "}\r\n", "\r\n", "BREAKPOINT {\r\n", " i = g*(v - e)\r\n", "}\r\n", "\r\n", "NET_RECEIVE(w) {\r\n", " if (flag == init) {\r\n", " WATCH (v > -55) rise\r\n", " }else if (flag == rise) {\r\n", " g = gna\r\n", " e = ena\r\n", " WATCH (v > 10) fall\r\n", " }else if (flag == fall) {\r\n", " g = gk\r\n", " e = ek\r\n", " WATCH (v < -70) off\r\n", " }else if (flag == off) {\r\n", " g = gpas\r\n", " e = erev\r\n", " WATCH (v > -55) rise\r\n", " }\r\n", "}\r\n" ] } ], "source": [ "!cat mod/hhwatch.mod" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Functionality example\n", "\n", "A functionality example for the above mod file can be found [here](https://github.com/neuronsimulator/testcorenrn/blob/master/testwatch.hoc)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Following example showcases the usage of `NET_RECEIVE` with `FOR_NETCONS`" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:21.602215Z", "iopub.status.busy": "2025-08-18T03:35:21.602047Z", "iopub.status.idle": "2025-08-18T03:35:21.713605Z", "shell.execute_reply": "2025-08-18T03:35:21.713127Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ": Each NetCon maintains a count of external events.\r\n", ": And time of last external event\r\n", ": On each internal event all connecting NetCon get the internal event count.\r\n", ": And time of last external event\r\n", "\r\n", "NEURON {\r\n", " POINT_PROCESS ForNetConTest\r\n", " RANGE tbegin\r\n", "}\r\n", "\r\n", "UNITS {\r\n", "}\r\n", "\r\n", "PARAMETER {\r\n", " tbegin = 0 (ms)\r\n", "}\r\n", "\r\n", "INITIAL {\r\n", " net_send(tbegin, 1)\r\n", "}\r\n", "\r\n", "NET_RECEIVE(w, npre, tpre (ms), npost, tpost (ms)) {\r\n", " INITIAL {\r\n", " npre=0 tpre=-1 npost=0 tpost=-1\r\n", " }\r\n", "\r\n", " if (flag == 0) { : external (pre) event\r\n", " npre = npre + 1\r\n", " tpre = t\r\n", " }\r\n", "\r\n", " if (flag == 1) { : internal (post) event\r\n", " FOR_NETCONS(w, fnpre, ftpre (ms), fnpost, ftpost (ms)) {\r\n", " fnpost = fnpost + 1\r\n", " ftpost = t\r\n", " }\r\n", " net_send(3, 1) : in 3 ms another 1 event\r\n", " net_event(t)\r\n", " }\r\n", "}\r\n" ] } ], "source": [ "!cat mod/fornetcon.mod" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Functionality Example\n", "\n", "A functionality example for the above mod file can be found [here](https://github.com/neuronsimulator/nrn/blob/master/test/coreneuron/test_fornetcon.py)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### CONSTRUCTOR" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### DESTRUCTOR" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### INITIAL" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### STATE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### BREAKPOINT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### SOLVE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### METHOD" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### DERIVATIVE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### KINETIC\n", "\n", "See description in [NMODL documentation](https://neuronsimulator.github.io/nrn/hoc/modelspec/programmatic/mechanisms/nmodl.html#kinetic)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Example mod file\n", "\n", "Following example showcases calcium ion accumulation with longitudinal and radial diffusion. Speicifically showcases the use of `KINETIC`, `COMPARTMENT` and `LONGITUDINAL_DIFFUSION` keywords." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:21.716319Z", "iopub.status.busy": "2025-08-18T03:35:21.716154Z", "iopub.status.idle": "2025-08-18T03:35:21.827493Z", "shell.execute_reply": "2025-08-18T03:35:21.827033Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\r\n", "TITLE Calcium ion accumulation with longitudinal and radial diffusion\r\n", "\r\n", "COMMENT\r\n", "PROCEDURE factors_cadifus() sets up the scale factors \r\n", "needed to model radial diffusion.\r\n", "These scale factors do not have to be recomputed\r\n", "when diam or DFree is changed.\r\n", "The amount of calcium in an annulus is ca[i]*diam^2*vol[i] \r\n", "with ca[0] being the 2nd order correct concentration at the exact edge\r\n", "and ca[NANN-1] being the concentration at the exact center.\r\n", "Buffer concentration and rates are based on Yamada et al. 1989\r\n", "model of bullfrog sympathetic ganglion cell.\r\n", "ENDCOMMENT\r\n", "\r\n", "NEURON {\r\n", "\tSUFFIX cadifus\r\n", "\tUSEION ca READ cai, ica WRITE cai\r\n", "\tGLOBAL vol, TotalBuffer\r\n", "\tRANGE cai0\r\n", "\tTHREADSAFE\r\n", "}\r\n", "\r\n", "DEFINE NANN 4\r\n", "\r\n", "UNITS {\r\n", "\t(molar) =\t(1/liter)\r\n", "\t(mM) =\t(millimolar)\r\n", "\t(um) =\t(micron)\r\n", "\t(mA) =\t(milliamp)\r\n", "\tFARADAY =\t(faraday)\t(10000 coulomb)\r\n", "\tPI = (pi)\t(1)\r\n", "}\r\n", "\r\n", "PARAMETER {\r\n", "\tDCa = 0.6\t\t\t(um2/ms)\r\n", "\t: to change rate of buffering without disturbing equilibrium\r\n", "\t: multiply the following two by the same factor\r\n", "\tk1buf\t= 100\t\t\t(/mM-ms)\r\n", "\tk2buf\t= 0.1\t\t\t(/ms)\r\n", "\tTotalBuffer = 0.003\t(mM)\r\n", "\tcai0 = 50e-6 (mM)\t: Requires explicit use in INITIAL block\r\n", "}\r\n", "\r\n", "ASSIGNED {\r\n", "\tdiam\t\t(um)\r\n", "\tica\t\t(mA/cm2)\r\n", "\tcai\t\t(mM)\r\n", "\tvol[NANN]\t(1)\t: gets extra um2 when multiplied by diam^2\r\n", "\tKd\t\t(/mM)\r\n", "\tB0\t\t(mM)\r\n", "}\r\n", "\r\n", "STATE {\r\n", "\tca[NANN]\t\t(mM) <1e-6>\t: ca[0] is equivalent to cai\r\n", "\tCaBuffer[NANN]\t(mM)\r\n", "\tBuffer[NANN]\t(mM)\r\n", "}\r\n", "\r\n", "BREAKPOINT {\r\n", "\tSOLVE state METHOD sparse\r\n", "}\r\n", "\r\n", "LOCAL factors_done\r\n", "\r\n", "INITIAL {\r\n", "\tMUTEXLOCK\r\n", "\tif (factors_done == 0) {\r\n", "\t\tfactors_done = 1\r\n", "\t\tfactors()\r\n", "\t}\r\n", "\tMUTEXUNLOCK\r\n", "\r\n", "\tcai = cai0\r\n", "\tKd = k1buf/k2buf\r\n", "\tB0 = TotalBuffer/(1 + Kd*cai)\r\n", "\r\n", "\tFROM i=0 TO NANN-1 {\r\n", "\t\tca[i] = cai\r\n", "\t\tBuffer[i] = B0\r\n", "\t\tCaBuffer[i] = TotalBuffer - B0\r\n", "\t}\r\n", "}\r\n", "\r\n", "COMMENT\r\n", "factors() sets up factors needed for radial diffusion \r\n", "modeled by NANN concentric compartments.\r\n", "The outermost shell is half as thick as the other shells \r\n", "so the concentration is spatially second order correct \r\n", "at the surface of the cell.\r\n", "The radius of the cylindrical core \r\n", "equals the thickness of the outermost shell.\r\n", "The intervening NANN-2 shells each have thickness = r/(NANN-1)\r\n", "(NANN must be >= 2).\r\n", "\r\n", "ca[0] is at the edge of the cell, \r\n", "ca[NANN-1] is at the center of the cell, \r\n", "and ca[i] for 0 < i < NANN-1 is \r\n", "midway through the thickness of each annulus.\r\n", "ENDCOMMENT\r\n", "\r\n", "LOCAL frat[NANN]\r\n", "\r\n", "PROCEDURE factors() {\r\n", "\tLOCAL r, dr2\r\n", "\tr = 1/2\t\t\t:starts at edge (half diam)\r\n", "\tdr2 = r/(NANN-1)/2\t:half thickness of annulus\r\n", "\tvol[0] = 0\r\n", "\tfrat[0] = 2*r\r\n", "\tFROM i=0 TO NANN-2 {\r\n", "\t\tvol[i] = vol[i] + PI*(r-dr2/2)*2*dr2\t:interior half\r\n", "\t\tr = r - dr2\r\n", "\t\tfrat[i+1] = 2*PI*r/(2*dr2)\t:exterior edge of annulus\r\n", "\t\t\t\t\t: divided by distance between centers\r\n", "\t\tr = r - dr2\r\n", "\t\tvol[i+1] = PI*(r+dr2/2)*2*dr2\t:outer half of annulus\r\n", "\t}\r\n", "}\r\n", "\r\n", "LOCAL dsq, dsqvol\t: can't define local variable in KINETIC block \r\n", "\t\t\t: or use in COMPARTMENT\r\n", "\r\n", "KINETIC state {\r\n", "\tCOMPARTMENT i, diam*diam*vol[i] {ca CaBuffer Buffer}\r\n", "\tLONGITUDINAL_DIFFUSION i, DCa*diam*diam*vol[i] {ca}\r\n", "\t~ ca[0] << (-ica*PI*diam/(2*FARADAY))\r\n", "\tFROM i=0 TO NANN-2 {\r\n", "\t\t~ ca[i] <-> ca[i+1] (DCa*frat[i+1], DCa*frat[i+1])\r\n", "\t}\r\n", "\tdsq = diam*diam\r\n", "\tFROM i=0 TO NANN-1 {\r\n", "\t\tdsqvol = dsq*vol[i]\r\n", "\t\t~ ca[i] + Buffer[i] <-> CaBuffer[i] (k1buf*dsqvol, k2buf*dsqvol)\r\n", "\t}\r\n", "\tcai = ca[0]\r\n", "}\r\n", "\r\n" ] } ], "source": [ "!cat mod/cadif.mod" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Functionality example" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:21.830349Z", "iopub.status.busy": "2025-08-18T03:35:21.829729Z", "iopub.status.idle": "2025-08-18T03:35:21.941289Z", "shell.execute_reply": "2025-08-18T03:35:21.940823Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "from neuron import n\r\n", "\r\n", "from utils.cell import Cell\r\n", "\r\n", "\r\n", "class cadifusCell(Cell):\r\n", " def _create_cell(self):\r\n", " self.section = n.Section()\r\n", " self.section.insert(\"cadifus\")\r\n", " self.section(0.001).ca_cadifus[0] = 1e-2\r\n", "\r\n", " def record(self):\r\n", " tvec = n.Vector()\r\n", " tvec.record(n._ref_t, sec=self.section)\r\n", " cai_vec = n.Vector()\r\n", " cai_vec.record(self.section(0.5).cadifus._ref_ca[0], sec=self.section)\r\n", " self.record_vectors[\"ca_ion[0]\"] = cai_vec\r\n", "\r\n", "\r\n", "if __name__ == \"__main__\":\r\n", " cadifus_cell = cadifusCell()\r\n", " cadifus_cell.record()\r\n", " cadifus_cell.simulate(1, 0.1)\r\n", " cadifus_cell.output()\r\n", " del cadifus_cell\r\n" ] } ], "source": [ "!cat python_scripts/kinetic.py" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:21.943670Z", "iopub.status.busy": "2025-08-18T03:35:21.943506Z", "iopub.status.idle": "2025-08-18T03:35:22.297532Z", "shell.execute_reply": "2025-08-18T03:35:22.297139Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Values of variable ca_ion[0]:\n", "0 5e-05\n", "1 5e-05\n", "2 5e-05\n", "3 5e-05\n", "4 5e-05\n", "5 5e-05\n", "6 5e-05\n", "7 5e-05\n", "8 5e-05\n", "9 5e-05\n", "10 5e-05\n" ] } ], "source": [ "%run python_scripts/kinetic.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### CONSERVE\n", "\n", "TODO: add example" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### FUNCTION" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### PROCEDURE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### LINEAR" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### NONLINEAR" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### LOCAL" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Other NMODL constructs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### TABLE\n", "\n", "Short description for TABLE keyword" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### FROM" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### TO" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### WITH" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### DEPEND" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Example mod file" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:22.299780Z", "iopub.status.busy": "2025-08-18T03:35:22.299554Z", "iopub.status.idle": "2025-08-18T03:35:22.413392Z", "shell.execute_reply": "2025-08-18T03:35:22.412894Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ": simple first-order model of calcium dynamics\r\n", "\r\n", "DEFINE FOO 1\r\n", "\r\n", "NEURON {\r\n", " SUFFIX table\r\n", " USEION ca READ cai,ica WRITE cai \r\n", " RANGE ca \r\n", " GLOBAL depth,cainf,taur\r\n", " RANGE var\r\n", " RANGE ainf\r\n", " THREADSAFE\r\n", "}\r\n", "\r\n", "UNITS {\r\n", " (molar) = (1/liter)\t\t\r\n", " (mM) = (milli/liter)\r\n", "\t(um)\t= (micron) \r\n", " (mA) = (milliamp)\r\n", "\t(msM)\t= (ms mM) \r\n", " FARADAY = (faraday) (coul)\r\n", "}\r\n", "\r\n", "PARAMETER {\r\n", " depth\t= .1\t(um)\t\t\r\n", " taur = 200 (ms)\t: rate of calcium removal for stress conditions\r\n", "\tcainf\t= 50e-6(mM)\t:changed oct2\r\n", "\tcai\t\t(mM)\r\n", "}\r\n", "\r\n", "ASSIGNED {\r\n", "\tica\t\t(mA/cm2)\r\n", "\tdrive_channel\t(mM/ms)\r\n", " var (mV)\r\n", " ainf\r\n", "}\r\n", "\r\n", "STATE {\r\n", "\tca\t\t(mM) \r\n", "}\r\n", "\r\n", " \r\n", "BREAKPOINT {\r\n", "\tSOLVE state METHOD euler\r\n", "}\r\n", "\r\n", "DERIVATIVE state {\r\n", "\tdrive_channel = - (10000) * ica / (2 * FARADAY * depth)\r\n", "\tif (drive_channel <= 0.) { drive_channel = 0. } : cannot pump inward \r\n", " ca' = drive_channel/18 + (cainf -ca)/taur*11\r\n", "\tcai = ca\r\n", " if (FOO == 0) { }\r\n", "}\r\n", "FUNCTION test_table_f(br) {\r\n", " TABLE FROM 0 TO FOO WITH 1\r\n", " test_table_f = 1\r\n", "}\r\n", "PROCEDURE test_table_p(br) {\r\n", " TABLE ainf FROM 0 TO FOO WITH 1\r\n", " ainf = 1\r\n", "}\r\n", "INITIAL {\r\n", " ca = cainf\r\n", "}\r\n" ] } ], "source": [ "!cat mod/table.mod" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Functionality example " ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:22.415724Z", "iopub.status.busy": "2025-08-18T03:35:22.415563Z", "iopub.status.idle": "2025-08-18T03:35:22.528035Z", "shell.execute_reply": "2025-08-18T03:35:22.527559Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "from neuron import n\r\n", "\r\n", "from utils.cell import Cell\r\n", "\r\n", "\r\n", "class TableCell(Cell):\r\n", " def _create_cell(self):\r\n", " self.section = n.Section()\r\n", " self.section.insert(\"table\")\r\n", "\r\n", " def record(self):\r\n", " tvec = n.Vector()\r\n", " tvec.record(n._ref_t, sec=self.section)\r\n", " avec = n.Vector()\r\n", " avec.record(self.section(0.5)._ref_ainf_table, sec=self.section)\r\n", " self.record_vectors[\"ainf\"] = avec\r\n", "\r\n", "\r\n", "if __name__ == \"__main__\":\r\n", " table_cell = TableCell()\r\n", " table_cell.record()\r\n", " table_cell.simulate(1, 0.1)\r\n", " table_cell.output()\r\n", " del table_cell\r\n" ] } ], "source": [ "!cat python_scripts/table.py" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:22.530373Z", "iopub.status.busy": "2025-08-18T03:35:22.530212Z", "iopub.status.idle": "2025-08-18T03:35:22.535510Z", "shell.execute_reply": "2025-08-18T03:35:22.535131Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Changed dt\n", "Values of variable ainf:\n", "0 1.0\n", "1 1.0\n", "2 1.0\n", "3 1.0\n", "4 1.0\n", "5 1.0\n", "6 1.0\n", "7 1.0\n", "8 1.0\n", "9 1.0\n", "10 1.0\n" ] } ], "source": [ "%run python_scripts/table.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### FUNCTION_TABLE\n", "\n", "See description in [NMODL documentation](https://neuronsimulator.github.io/nrn/hoc/modelspec/programmatic/mechanisms/nmodl.html#function-table)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Example mod file" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:22.537597Z", "iopub.status.busy": "2025-08-18T03:35:22.536932Z", "iopub.status.idle": "2025-08-18T03:35:22.649319Z", "shell.execute_reply": "2025-08-18T03:35:22.648839Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ": Three state kinetic scheme for HH-like potassium channel\r\n", ": Steady-state v-dependent state transitions have been fit\r\n", ": Needs v-dependent time constants from tables created under hoc\r\n", "NEURON {\r\n", " SUFFIX k3st\r\n", " USEION k READ ek WRITE ik\r\n", " RANGE g, gbar\r\n", " RANGE tau1_rec, tau2_rec\r\n", "}\r\n", "UNITS { (mV) = (millivolt) }\r\n", "PARAMETER {\r\n", " gbar = 33 (millimho/cm2)\r\n", " d1 = -38 (mV)\r\n", " k1 = 0.151 (/mV)\r\n", " d2 = -25 (mV)\r\n", " k2 = 0.044 (/mV)\r\n", "}\r\n", "\r\n", "ASSIGNED {\r\n", " v (mV)\r\n", " ek (mV)\r\n", " g (millimho/cm2)\r\n", " ik (milliamp/cm2)\r\n", " kf1 (/ms)\r\n", " kb1 (/ms)\r\n", " kf2 (/ms)\r\n", " kb2 (/ms)\r\n", " tau1_rec\r\n", " tau2_rec\r\n", "}\r\n", "\r\n", "STATE { c1 c2 o }\r\n", "\r\n", "BREAKPOINT {\r\n", " SOLVE kin METHOD sparse\r\n", " g = gbar*o\r\n", " ik = g*(v - ek)*(1e-3)\r\n", "}\r\n", "\r\n", "INITIAL { SOLVE kin STEADYSTATE sparse }\r\n", "\r\n", "KINETIC kin {\r\n", " rates(v)\r\n", " ~ c1 <-> c2 (kf1, kb1)\r\n", " ~ c2 <-> o (kf2, kb2)\r\n", " CONSERVE c1 + c2 + o = 1\r\n", "}\r\n", "\r\n", "FUNCTION_TABLE tau1(v(mV)) (ms)\r\n", "FUNCTION_TABLE tau2(v(mV)) (ms)\r\n", "\r\n", "PROCEDURE rates(v(millivolt)) {\r\n", " LOCAL K1, K2\r\n", " K1 = exp(k2*(d2 - v) - k1*(d1 - v))\r\n", " kf1 = K1/(tau1(v)*(1+K1))\r\n", " kb1 = 1/(tau1(v)*(1+K1))\r\n", " K2 = exp(-k2*(d2 - v))\r\n", " kf2 = K2/(tau2(v)*(1+K2))\r\n", " kb2 = 1/(tau2(v)*(1+K2))\r\n", " tau1_rec = tau1(v)\r\n", " tau2_rec = tau2(v)\r\n", "}\r\n" ] } ], "source": [ "!cat mod/k3st.mod" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Functionality example " ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:22.651815Z", "iopub.status.busy": "2025-08-18T03:35:22.651653Z", "iopub.status.idle": "2025-08-18T03:35:22.764321Z", "shell.execute_reply": "2025-08-18T03:35:22.763849Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "from neuron import n\r\n", "\r\n", "from utils.cell import Cell\r\n", "\r\n", "\r\n", "class k3stCell(Cell):\r\n", " def _create_cell(self):\r\n", " self.section = n.Section()\r\n", " self.section.insert(\"k3st\")\r\n", " tau1_values = []\r\n", " voltage_values = []\r\n", " for i in range(10):\r\n", " tau1_values.append(i * 0.25)\r\n", " voltage_values.append(-70 + 10 * i)\r\n", " tau1_vector = n.Vector(tau1_values)\r\n", " voltage_vector = n.Vector(voltage_values)\r\n", " n.table_tau1_k3st(tau1_vector, voltage_vector)\r\n", " n.table_tau2_k3st(100)\r\n", "\r\n", " def record(self):\r\n", " tvec = n.Vector()\r\n", " tvec.record(n._ref_t, sec=self.section)\r\n", " tau1_vec = n.Vector()\r\n", " tau2_vec = n.Vector()\r\n", " tau1_vec.record(self.section(0.5).k3st._ref_tau1_rec, sec=self.section)\r\n", " tau2_vec.record(self.section(0.5).k3st._ref_tau2_rec, sec=self.section)\r\n", " self.record_vectors[\"tau1_rec\"] = tau1_vec\r\n", " self.record_vectors[\"tau2_rec\"] = tau2_vec\r\n", "\r\n", "\r\n", "if __name__ == \"__main__\":\r\n", " k3st_cell = k3stCell()\r\n", " k3st_cell.record()\r\n", " k3st_cell.simulate(1, 0.1)\r\n", " k3st_cell.output()\r\n", " del k3st_cell\r\n" ] } ], "source": [ "!cat python_scripts/function_table.py" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:22.766546Z", "iopub.status.busy": "2025-08-18T03:35:22.766371Z", "iopub.status.idle": "2025-08-18T03:35:22.772173Z", "shell.execute_reply": "2025-08-18T03:35:22.771857Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Changed dt\n", "Values of variable tau1_rec:\n", "0 0.125\n", "1 0.11066884059267537\n", "2 0.09702284544211288\n", "3 0.0840298714363211\n", "4 0.07165924609736188\n", "5 0.059881657072173765\n", "6 0.04866907889206899\n", "7 0.037994719804658315\n", "8 0.027832978105936233\n", "9 0.018159402051359665\n", "10 0.008950650468550592\n", "Values of variable tau2_rec:\n", "0 100.0\n", "1 100.0\n", "2 100.0\n", "3 100.0\n", "4 100.0\n", "5 100.0\n", "6 100.0\n", "7 100.0\n", "8 100.0\n", "9 100.0\n", "10 100.0\n" ] } ], "source": [ "%run python_scripts/function_table.py" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### BEFORE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### AFTER" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### FOR_NETCONS" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### PROTECT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### MUTEXLOCK / MUTEXUNLOCK" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### VERBATIM" ] } ], "metadata": { "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.10" } }, "nbformat": 4, "nbformat_minor": 2 }