{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Scripting NEURON basics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The objectives of this part of the tutorial are to get familiar with basic operations of NEURON using Python. In this worksheet we will:\n", "\n", "* Create a passive cell membrane in NEURON.\n", "* Create a synaptic stimulus onto the neuron.\n", "* Modify parameters of the membrane and stimulus.\n", "* Visualize results with bokeh." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What is NEURON?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The NEURON simulation environment is a powerful engine for performing simulations of neurons and biophysical neural networks. It permits the construction of biologically realistic membranes with active and passive ion channels, combined with virtual connectivity and electrophysiology tools to drive and measure neuron and network behaviors." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 1: Import the neuron module into Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Any code that is not part of Python's Built-in Functions must be imported. The Python interface to NEURON goes through the neuron module, especially the neuron.h submodule. The neuron module has additional submodules, including neuron.rxd for reaction-diffusion dynamics, neuron.gui2 for Jupyter-compatible PlotShape graphs, and neuron.gui for Interviews-based GUI tools. The neuron.h submodule also allows loading files, executing code, and calling functions written in HOC, an older scripting language supported by NEURON. This allows the continued use of HOC libraries in Python code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To import neuron, we could use:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:35:59.984091Z", "iopub.status.busy": "2025-08-18T03:35:59.983935Z", "iopub.status.idle": "2025-08-18T03:36:00.399663Z", "shell.execute_reply": "2025-08-18T03:36:00.399230Z" } }, "outputs": [], "source": [ "import neuron" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the above succeeded, it produces no output (in recent versions of NEURON), so how can we know what version of NEURON we have? Simple: ask for the `__version__`:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.401978Z", "iopub.status.busy": "2025-08-18T03:36:00.401780Z", "iopub.status.idle": "2025-08-18T03:36:00.407470Z", "shell.execute_reply": "2025-08-18T03:36:00.407145Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(neuron.__version__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are only a limited number of functions avaiable directly from the neuron module. In practice, we usually want to directly import the submodules we need; i.e. do something like the below instead:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.471051Z", "iopub.status.busy": "2025-08-18T03:36:00.470832Z", "iopub.status.idle": "2025-08-18T03:36:00.476117Z", "shell.execute_reply": "2025-08-18T03:36:00.475754Z" } }, "outputs": [], "source": [ "from neuron import n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When using NEURON, you will always want the n submodule. (With older code, you may have seen people use the h submodule. They are almost exactly equivalent, and h continues to work.) You may or may not need to import the additional submodules mentioned above. If you do, they can be imported separately or loaded in one line with a comma separated list, as in:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.478689Z", "iopub.status.busy": "2025-08-18T03:36:00.478538Z", "iopub.status.idle": "2025-08-18T03:36:00.507582Z", "shell.execute_reply": "2025-08-18T03:36:00.507185Z" } }, "outputs": [], "source": [ "from neuron import n, rxd" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "NB: When importing n, etc like this, there is usually no need for importing neuron separately." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
NEURON assumes certain default units (concentration in mM, time in ms, voltage in mV), but units can be specified explicitly by importing unit definitions from neuron.units. Even if you are using the default units, being explicit makes your code more readable by others. For example:
" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.509598Z", "iopub.status.busy": "2025-08-18T03:36:00.509447Z", "iopub.status.idle": "2025-08-18T03:36:00.514511Z", "shell.execute_reply": "2025-08-18T03:36:00.514139Z" } }, "outputs": [], "source": [ "from neuron.units import ms, mV" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the above gives you an error, then you are using a version of NEURON older than 7.7. Update before proceeding." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 2: Create a cell" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A Section is the basic morphological building-block in NEURON. We typically think of a Section as an unbranched cable, but it can also be used to represent a soma. Thus a simple model neuron with only a soma can be created as in:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.516236Z", "iopub.status.busy": "2025-08-18T03:36:00.516093Z", "iopub.status.idle": "2025-08-18T03:36:00.520504Z", "shell.execute_reply": "2025-08-18T03:36:00.520158Z" } }, "outputs": [], "source": [ "soma = n.Section(\"soma\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is no output, so how can we tell that we successfully created a Section?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Aside 1: NEURON's `n.topology` function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "NEURON's `n.topology()` function displays the topological structure of the entire model, indicating which sections are connected to which sections, where they are connected, and how many *segments* each section is divided into.\n", "\n", "If you're following along with our example, there's not much to see yet since there is only one section, but it does demonstrate that the soma has been created and has one segment (one dash is shown):" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.523609Z", "iopub.status.busy": "2025-08-18T03:36:00.522055Z", "iopub.status.idle": "2025-08-18T03:36:00.532423Z", "shell.execute_reply": "2025-08-18T03:36:00.532059Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "|-| soma(0-1)\n", "\n" ] }, { "data": { "text/plain": [ "1.0" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n.topology()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `n.topology()` function displays its data to screen and returns `1.0` indicating success (this function always succeeds). Note: This function is only for displaying data; other methods must be used to store the data in a variable for programmatic analysis." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Aside 2: The `psection` method" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Every NEURON section has a psection method (think: properties of the section) that returns a Python dictionary providing a structured data representation of the properties of the section.
For example, we can query the soma via:
" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.534679Z", "iopub.status.busy": "2025-08-18T03:36:00.533995Z", "iopub.status.idle": "2025-08-18T03:36:00.539064Z", "shell.execute_reply": "2025-08-18T03:36:00.538691Z" } }, "outputs": [ { "data": { "text/plain": [ "{'point_processes': {},\n", " 'density_mechs': {},\n", " 'ions': {},\n", " 'morphology': {'L': 100.0,\n", " 'diam': [500.0],\n", " 'pts3d': [],\n", " 'parent': None,\n", " 'trueparent': None},\n", " 'nseg': 1,\n", " 'Ra': 35.4,\n", " 'cm': [1.0],\n", " 'regions': set(),\n", " 'species': set(),\n", " 'name': 'soma',\n", " 'hoc_internal_name': '__nrnsec_0x59fadddf9cf0',\n", " 'cell': None}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soma.psection()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The results tell us the soma is a cylinder with length 100 microns, diameter 500 microns, axial resistivity 35.4 ohm*cm, and specific membrance capacitance 1 μF/cm2." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note: calling this method does not itself print anything to the screen. Instead it returns a dictionary. We see the contents of the dictionary only because we are running interactively; from a script, nothing would be printed unless we explicitly printed it with print, or, better pretty-printed it with pprint.pprint." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since this is a dictionary, we can extract any properties we want using square brackets. For example, the length of the section is:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.541334Z", "iopub.status.busy": "2025-08-18T03:36:00.540753Z", "iopub.status.idle": "2025-08-18T03:36:00.544925Z", "shell.execute_reply": "2025-08-18T03:36:00.544614Z" } }, "outputs": [ { "data": { "text/plain": [ "100.0" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soma.psection()[\"morphology\"][\"L\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All of these values can be individually accessed in more efficient ways, but psection provides an overview of the full properties of the section." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For example, the length of the soma is more efficiently available (and settable) via:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.547176Z", "iopub.status.busy": "2025-08-18T03:36:00.546576Z", "iopub.status.idle": "2025-08-18T03:36:00.550430Z", "shell.execute_reply": "2025-08-18T03:36:00.550098Z" } }, "outputs": [ { "data": { "text/plain": [ "100.0" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soma.L" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "soma.psection()['morphology']['diam'] is a list (of length 1 here), with each entry corresponding to the value for each segment. Similarly for soma.psection()['cm'], etc." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Important:** You may have noticed that the default diameter is 500 μm, which is excessively large for mammalian neurons. It's the default because it's appropriate for the squid giant axons studied by Hodgkin and Huxley. NEURON also uses squid-relevant values for axial resistivity (`soma.Ra`) and temperature (`n.celsius`). These should all be adjusted for mammalian models." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 3: Set the cell's morphological properties" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since we're simulating a soma, the default length of 100 μm and diameter of 500 μm are inappropriate. Let's set the length (L) and diameter (diam) to 20 μm instead:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.552096Z", "iopub.status.busy": "2025-08-18T03:36:00.551960Z", "iopub.status.idle": "2025-08-18T03:36:00.554673Z", "shell.execute_reply": "2025-08-18T03:36:00.554348Z" } }, "outputs": [], "source": [ "soma.L = 20\n", "soma.diam = 20" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In many models, you will have cells consisting of many connected sections. In brief, this can be done using the Section's connect method. That will be described in a subsequent part of the tutorial. For now though, we consider only the soma." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Aside 3: Python's `dir` function" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also probe objects with Python’s built-indir()
function. Let’s see what it says about soma."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.556842Z",
"iopub.status.busy": "2025-08-18T03:36:00.556245Z",
"iopub.status.idle": "2025-08-18T03:36:00.560535Z",
"shell.execute_reply": "2025-08-18T03:36:00.560215Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"['L',\n",
" 'Ra',\n",
" '__call__',\n",
" '__class__',\n",
" '__contains__',\n",
" '__delattr__',\n",
" '__dir__',\n",
" '__doc__',\n",
" '__eq__',\n",
" '__format__',\n",
" '__ge__',\n",
" '__getattribute__',\n",
" '__getstate__',\n",
" '__gt__',\n",
" '__hash__',\n",
" '__init__',\n",
" '__init_subclass__',\n",
" '__iter__',\n",
" '__le__',\n",
" '__lt__',\n",
" '__module__',\n",
" '__ne__',\n",
" '__new__',\n",
" '__reduce__',\n",
" '__reduce_ex__',\n",
" '__repr__',\n",
" '__setattr__',\n",
" '__sizeof__',\n",
" '__str__',\n",
" '__subclasshook__',\n",
" 'allseg',\n",
" 'arc3d',\n",
" 'cell',\n",
" 'children',\n",
" 'connect',\n",
" 'diam3d',\n",
" 'disconnect',\n",
" 'has_membrane',\n",
" 'hname',\n",
" 'hoc_internal_name',\n",
" 'insert',\n",
" 'is_pysec',\n",
" 'n3d',\n",
" 'name',\n",
" 'nseg',\n",
" 'orientation',\n",
" 'parentseg',\n",
" 'psection',\n",
" 'pt3dadd',\n",
" 'pt3dchange',\n",
" 'pt3dclear',\n",
" 'pt3dinsert',\n",
" 'pt3dremove',\n",
" 'pt3dstyle',\n",
" 'push',\n",
" 'rallbranch',\n",
" 'same',\n",
" 'spine3d',\n",
" 'subtree',\n",
" 'trueparentseg',\n",
" 'uninsert',\n",
" 'wholetree',\n",
" 'x3d',\n",
" 'y3d',\n",
" 'z3d']"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dir(soma)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This tells us all of the Python methods and variables associated with the object. Any methods with two leading and trailing underscores are reserved by Python. The other items in the list are additional members of soma that we can call. To see all of the functions, variables, etc available through NEURON's `n` submodule, try:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.562115Z",
"iopub.status.busy": "2025-08-18T03:36:00.561979Z",
"iopub.status.idle": "2025-08-18T03:36:00.566393Z",
"shell.execute_reply": "2025-08-18T03:36:00.566066Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"APCount, AlphaSynapse, Avogadro_constant, BBSaveState, CVode, DEG,\n",
"Deck, E, Exp2Syn, ExpSyn, FARADAY, FInitializeHandler, File, GAMMA,\n",
"GUIMath, Glyph, Graph, HBox, IClamp, Impedance, IntFire1, IntFire2,\n",
"IntFire4, KSChan, KSGate, KSState, KSTrans, L, LinearMechanism, List,\n",
"Matrix, MechanismStandard, MechanismType, NMODLRandom, NetCon,\n",
"NetStim, OClamp, PHI, PI, PPShape, PWManager, ParallelContext,\n",
"PatternStim, PlotShape, PointProcessMark, Pointer, PtrVector,\n",
"PythonObject, R, Ra, Random, RangeVarPlot, SEClamp, SaveState,\n",
"Section, SectionBrowser, SectionList, SectionRef, Shape,\n",
"StateTransitionEvent, StringFunctions, SymChooser, Timer, VBox,\n",
"VClamp, ValueFieldEditor, Vector, __abs__, __add__, __bool__,\n",
"__call__, __class__, __delattr__, __delitem__, __dir__, __doc__,\n",
"__eq__, __format__, __ge__, __getattribute__, __getitem__,\n",
"__getstate__, __gt__, __hash__, __init__, __init_subclass__, __iter__,\n",
"__le__, __len__, __lt__, __module__, __mul__, __ne__, __neg__,\n",
"__new__, __next__, __pos__, __radd__, __reduce__, __reduce_ex__,\n",
"__repr__, __rmul__, __rsub__, __rtruediv__, __setattr__, __setitem__,\n",
"__setstate__, __sizeof__, __str__, __sub__, __subclasshook__,\n",
"__truediv__, _pysec, abs, access, allobjects, allobjectvars, allsec,\n",
"arc3d, area, argtype, atan, atan2, attr_praxis, axis, baseattr,\n",
"batch_run, batch_save, begintemplate, boolean_dialog, break,\n",
"capacitance, cas, celsius, chdir, clamp_resist, cm, connect, continue,\n",
"continue_dialog, coredump_on_error, coreneuron_handle, cos, create,\n",
"debug, default_dll_loaded_, define_shape, delete, delete_section,\n",
"depvar, diam, diam3d, diam_changed, dik_dv_, dina_dv_, disconnect,\n",
"distance, doEvents, doNotify, double, dt, e_extracellular, e_fastpas,\n",
"e_pas, ek, el_hh, else, ena, endtemplate, eps_IntFire4, eqinit, eqn,\n",
"erf, erfc, execerror, execute, execute1, exp, external, extracellular,\n",
"fadvance, fastpas, fclamp, fclampi, fclampv, fcurrent, finitialize,\n",
"fit_praxis, float_epsilon, fmatrix, for, forall, forsec, fprint,\n",
"frecord_init, fscan, fstim, fstimi, fsyn, fsyng, fsyni, func,\n",
"g_fastpas, g_pas, getSpineArea, getcwd, getstr, ghk, gk_hh, gkbar_hh,\n",
"gl_hh, gna_hh, gnabar_hh, graph, graphmode, h_hh, help, hh, hinf_hh,\n",
"hname, hoc_ac_, hoc_cross_x_, hoc_cross_y_, hoc_obj_, hoc_pointer_,\n",
"hoc_stdout, hocobjptr, htau_hh, i_cap, i_membrane, i_membrane_, i_pas,\n",
"ib_IntFire4, if, ifsec, ik, il_hh, ina, initnrn, insert,\n",
"install_vector_fitness, int, ion_charge, ion_register, ion_style,\n",
"ismembrane, issection, iterator, iterator_statement, ivoc_style,\n",
"k_ion, keep_nseg_parm, ki, ki0_k_ion, ko, ko0_k_ion, libpython_path,\n",
"load_file, load_func, load_proc, load_template, local, localobj, log,\n",
"log10, lw, m_hh, machine_name, make_mechanism, make_pointprocess,\n",
"mcell_ran4, mcell_ran4_init, minf_hh, morphology, mtau_hh, n3d, n_hh,\n",
"na_ion, nai, nai0_na_ion, name_declared, nao, nao0_na_ion, nernst,\n",
"neuronhome, new, ninf_hh, nlayer_extracellular, nrn_feenableexcept,\n",
"nrn_get_config_key, nrn_get_config_val, nrn_load_dll, nrn_mallinfo,\n",
"nrn_netrec_state_adjust, nrn_num_config_keys, nrn_shape_changed_,\n",
"nrn_sparse_partrans, nrnallpointmenu, nrnallsectionmenu,\n",
"nrnglobalmechmenu, nrniv_bind_thread, nrnmechmenu, nrnmpi_init,\n",
"nrnpointmenu, nrnpython, nrnsecmenu, nrnunit_use_legacy, nrnversion,\n",
"nseg, ntau_hh, numarg, obfunc, object_id, object_pop, object_push,\n",
"object_pushed, objectvar, objref, parent_connection, parent_node,\n",
"parent_section, pas, plot, plotx, ploty, plt, pop_section, print,\n",
"print_local_memory_usage, print_session, printf, prmat, proc, prstim,\n",
"psection, pt3dadd, pt3dchange, pt3dclear, pt3dconst, pt3dinsert,\n",
"pt3dremove, pt3dstyle, public, push_section, pval_praxis, pwman_place,\n",
"quit, rallbranch, rates_hh, read, ref, regraph, retrieveaudit, return,\n",
"ri, ropen, same, sav_g, sav_rhs, save_session, saveaudit, secname,\n",
"secondorder, section_exists, section_orientation, section_owner,\n",
"sectionname, setSpineArea, setcolor, setdata_feature, setdata_hh,\n",
"setdata_pas, setpointer, show_errmess_always, show_winio, sin, solve,\n",
"spine3d, sprint, sqrt, sred, sscanf, startsw, stop, stop_praxis,\n",
"stoprun, stopsw, strcmp, strdef, string_dialog, symbols, system, t,\n",
"tanh, taueps_IntFire4, this_node, this_section, topology, uninsert,\n",
"units, unix_mac_pc, use_exp_pow_precision, use_mcell_ran4,\n",
"usetable_hh, v, variable_domain, vext, vtrap_hh, while, wopen, x3d,\n",
"xbutton, xc, xcheckbox, xfixedvalue, xg, xlabel, xmenu, xopen,\n",
"xopen_broadcast_, xpanel, xpvalue, xradiobutton, xraxial, xred,\n",
"xslider, xstatebutton, xvalue, xvarlabel, y3d, z3d\n"
]
}
],
"source": [
"import textwrap\n",
"\n",
"print(textwrap.fill(\", \".join(dir(n))))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(The ', '.join(...) tells Python to build a string out of the list returned by dir where the items are separated from each other with a comma and a space. The textwrap.fill(...) tells Python to split long lines into multiple lines, by default a maximum of 70 characters long.)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Aside 4: Getting more help"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In addition to probing objects with dir()
, help from docstrings is available using help()
."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For example, from dir(soma) above, we know that there is a connect method available. Let's inquire about that:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.568601Z",
"iopub.status.busy": "2025-08-18T03:36:00.568016Z",
"iopub.status.idle": "2025-08-18T03:36:00.573071Z",
"shell.execute_reply": "2025-08-18T03:36:00.572773Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Help on built-in function connect:\n",
"\n",
"connect(...) method of nrn.Section instance\n",
" childSection.connect(parentSection, [parentX], [childEnd]) or\n",
" childSection.connect(parentSegment, [childEnd])\n",
"\n"
]
}
],
"source": [
"help(soma.connect)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When running interactively in Jupyter, the same information is available in a window that can be popped out by prefacing the method/function/etc with a question mark; e.g."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.574723Z",
"iopub.status.busy": "2025-08-18T03:36:00.574587Z",
"iopub.status.idle": "2025-08-18T03:36:00.577608Z",
"shell.execute_reply": "2025-08-18T03:36:00.577275Z"
}
},
"outputs": [],
"source": [
"?soma.connect"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Biophysical mechanisms"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"NEURON comes with a few built in biophysical mechanisms that can be added to a model:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"pas | \n", "Passive (“leak”) channel. | \n", "
extracellular | \n", "For simulating effects of nonzero extracellular\n", "potential, as may happen with leaky patch clamps,\n", "or detailed propertes of the myelin sheath. | \n", "
hh | \n", "Hodgkin-Huxley sodium, potassium, and leakage channels. | \n", "
IClamp
object) into the center of the soma to induce some membrane dynamics."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.616883Z",
"iopub.status.busy": "2025-08-18T03:36:00.616238Z",
"iopub.status.idle": "2025-08-18T03:36:00.619170Z",
"shell.execute_reply": "2025-08-18T03:36:00.618841Z"
}
},
"outputs": [],
"source": [
"iclamp = n.IClamp(soma(0.5))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"An IClamp is a Point Process. Point processes are point sources of current. When making a new PointProcess, you pass the segment to which it will bind.\n",
"\n",
"Again, with the dir function, we can validate that iclamp is an object and contains some useful parameters. Let's look at some of those parameters. We use a list comprehension to ignore those elements of the dir that start with double underscores (and are thus Python magic methods and not functions/variables intended to be used directly). "
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.621226Z",
"iopub.status.busy": "2025-08-18T03:36:00.620505Z",
"iopub.status.idle": "2025-08-18T03:36:00.624254Z",
"shell.execute_reply": "2025-08-18T03:36:00.623929Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['amp', 'baseattr', 'delay', 'dur', 'get_loc', 'get_segment', 'has_loc', 'hname', 'hocobjptr', 'i', 'loc', 'same']\n"
]
}
],
"source": [
"print([item for item in dir(iclamp) if not item.startswith(\"__\")])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In particular, we notice three key properties of a current clamp: amp -- the amplitude (in nA), delay -- the time the current clamp switches on (in ms), and dur -- how long (in ms) the current clamp stays on. Let's set these values:"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.626328Z",
"iopub.status.busy": "2025-08-18T03:36:00.625607Z",
"iopub.status.idle": "2025-08-18T03:36:00.628487Z",
"shell.execute_reply": "2025-08-18T03:36:00.628162Z"
}
},
"outputs": [],
"source": [
"iclamp.delay = 2\n",
"iclamp.dur = 0.1\n",
"iclamp.amp = 0.9"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's use psection to get a representation of the soma model:"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.629960Z",
"iopub.status.busy": "2025-08-18T03:36:00.629828Z",
"iopub.status.idle": "2025-08-18T03:36:00.634708Z",
"shell.execute_reply": "2025-08-18T03:36:00.634388Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"{'point_processes': {'IClamp': {IClamp[0]}},\n",
" 'density_mechs': {'hh': {'gnabar': [0.12],\n",
" 'gkbar': [0.036],\n",
" 'gl': [0.0003],\n",
" 'el': [-54.3],\n",
" 'gna': [0.0],\n",
" 'gk': [0.0],\n",
" 'il': [0.0],\n",
" 'm': [0.0],\n",
" 'h': [0.0],\n",
" 'n': [0.0]}},\n",
" 'ions': {'na': {'ena': [50.0],\n",
" 'nai': [10.0],\n",
" 'nao': [140.0],\n",
" 'ina': [0.0],\n",
" 'dina_dv_': [0.0]},\n",
" 'k': {'ek': [-77.0],\n",
" 'ki': [54.4],\n",
" 'ko': [2.5],\n",
" 'ik': [0.0],\n",
" 'dik_dv_': [0.0]}},\n",
" 'morphology': {'L': 20.0,\n",
" 'diam': [20.0],\n",
" 'pts3d': [],\n",
" 'parent': None,\n",
" 'trueparent': None},\n",
" 'nseg': 1,\n",
" 'Ra': 35.4,\n",
" 'cm': [1.0],\n",
" 'regions': set(),\n",
" 'species': set(),\n",
" 'name': 'soma',\n",
" 'hoc_internal_name': '__nrnsec_0x59fadddf9cf0',\n",
" 'cell': None}"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"soma.psection()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 6: Set up recording variables"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The cell should be configured to run a simulation. However, we need to indicate which variables we wish to record; these will be stored in a NEURON Vector (n.Vector object). For now, we will record the membrane potential, which is soma(0.5).v and the corresponding time points (n.t). References to variables are available by preceding the last part of the variable name with a `_ref_`"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.636840Z",
"iopub.status.busy": "2025-08-18T03:36:00.636103Z",
"iopub.status.idle": "2025-08-18T03:36:00.639105Z",
"shell.execute_reply": "2025-08-18T03:36:00.638799Z"
}
},
"outputs": [],
"source": [
"v = n.Vector().record(soma(0.5)._ref_v) # Membrane potential vector\n",
"t = n.Vector().record(n._ref_t) # Time stamp vector"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 7: Run the simulation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"By default, the NEURON h module provides the low level fadvance function for advancing one time step. For higher-level simulation control specification, we load NEURON's stdrun library:"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.640665Z",
"iopub.status.busy": "2025-08-18T03:36:00.640528Z",
"iopub.status.idle": "2025-08-18T03:36:00.652853Z",
"shell.execute_reply": "2025-08-18T03:36:00.652055Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"1.0"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"n.load_file(\"stdrun.hoc\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can then initialize our simulation such that our cell has a resting membrane potential of -65 mV:"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.655891Z",
"iopub.status.busy": "2025-08-18T03:36:00.655633Z",
"iopub.status.idle": "2025-08-18T03:36:00.664662Z",
"shell.execute_reply": "2025-08-18T03:36:00.662010Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"1.0"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"n.finitialize(-65 * mV)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And now continue the simulation from the current time (0) until 40 ms:"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"execution": {
"iopub.execute_input": "2025-08-18T03:36:00.666486Z",
"iopub.status.busy": "2025-08-18T03:36:00.666066Z",
"iopub.status.idle": "2025-08-18T03:36:00.680897Z",
"shell.execute_reply": "2025-08-18T03:36:00.680558Z"
}
},
"outputs": [
{
"data": {
"text/plain": [
"0.0"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"n.continuerun(40 * ms)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(For those who are interested: we initialized to a resting membrane potential of -65 mV because that's the default reversal potential for the hh channels, the only channel (set) inserted in this model.)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(Strictly speaking, we didn't need to specify the units here -- recall they were defined above in the from neuron.units import ms, mV -- as they are the defaults assumed by NEURON, but it is good practice to be explicitly clear.)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Step 8: Plot the results"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Using bokeh\n",
"When working in Jupyter with an active internet connection, it is often convenient to use the bokeh module for plotting, as it provides interactive graphs that can be panned, zoomed, and saved from the Jupyter notebook.
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's load bokeh and tell it to output to the Jupyter notebook:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:00.683235Z", "iopub.status.busy": "2025-08-18T03:36:00.682548Z", "iopub.status.idle": "2025-08-18T03:36:01.206750Z", "shell.execute_reply": "2025-08-18T03:36:01.206345Z" } }, "outputs": [ { "data": { "text/html": [ " \n", " \n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/javascript": [ "'use strict';\n", "(function(root) {\n", " function now() {\n", " return new Date();\n", " }\n", "\n", " const force = true;\n", "\n", " if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n", " root._bokeh_onload_callbacks = [];\n", " root._bokeh_is_loading = undefined;\n", " }\n", "\n", "const JS_MIME_TYPE = 'application/javascript';\n", " const HTML_MIME_TYPE = 'text/html';\n", " const EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", " const CLASS_NAME = 'output_bokeh rendered_html';\n", "\n", " /**\n", " * Render data to the DOM node\n", " */\n", " function render(props, node) {\n", " const script = document.createElement(\"script\");\n", " node.appendChild(script);\n", " }\n", "\n", " /**\n", " * Handle when an output is cleared or removed\n", " */\n", " function handleClearOutput(event, handle) {\n", " function drop(id) {\n", " const view = Bokeh.index.get_by_id(id)\n", " if (view != null) {\n", " view.model.document.clear()\n", " Bokeh.index.delete(view)\n", " }\n", " }\n", "\n", " const cell = handle.cell;\n", "\n", " const id = cell.output_area._bokeh_element_id;\n", " const server_id = cell.output_area._bokeh_server_id;\n", "\n", " // Clean up Bokeh references\n", " if (id != null) {\n", " drop(id)\n", " }\n", "\n", " if (server_id !== undefined) {\n", " // Clean up Bokeh references\n", " const cmd_clean = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", " cell.notebook.kernel.execute(cmd_clean, {\n", " iopub: {\n", " output: function(msg) {\n", " const id = msg.content.text.trim()\n", " drop(id)\n", " }\n", " }\n", " });\n", " // Destroy server and session\n", " const cmd_destroy = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", " cell.notebook.kernel.execute(cmd_destroy);\n", " }\n", " }\n", "\n", " /**\n", " * Handle when a new output is added\n", " */\n", " function handleAddOutput(event, handle) {\n", " const output_area = handle.output_area;\n", " const output = handle.output;\n", "\n", " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", " if ((output.output_type != \"display_data\") || (!Object.prototype.hasOwnProperty.call(output.data, EXEC_MIME_TYPE))) {\n", " return\n", " }\n", "\n", " const toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", "\n", " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", " // store reference to embed id on output_area\n", " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", " }\n", " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", " const bk_div = document.createElement(\"div\");\n", " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", " const script_attrs = bk_div.children[0].attributes;\n", " for (let i = 0; i < script_attrs.length; i++) {\n", " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", " toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n", " }\n", " // store reference to server id on output_area\n", " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", " }\n", " }\n", "\n", " function register_renderer(events, OutputArea) {\n", "\n", " function append_mime(data, metadata, element) {\n", " // create a DOM node to render to\n", " const toinsert = this.create_output_subarea(\n", " metadata,\n", " CLASS_NAME,\n", " EXEC_MIME_TYPE\n", " );\n", " this.keyboard_manager.register_events(toinsert);\n", " // Render to node\n", " const props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", " render(props, toinsert[toinsert.length - 1]);\n", " element.append(toinsert);\n", " return toinsert\n", " }\n", "\n", " /* Handle when an output is cleared or removed */\n", " events.on('clear_output.CodeCell', handleClearOutput);\n", " events.on('delete.Cell', handleClearOutput);\n", "\n", " /* Handle when a new output is added */\n", " events.on('output_added.OutputArea', handleAddOutput);\n", "\n", " /**\n", " * Register the mime type and append_mime function with output_area\n", " */\n", " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", " /* Is output safe? */\n", " safe: true,\n", " /* Index of renderer in `output_area.display_order` */\n", " index: 0\n", " });\n", " }\n", "\n", " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", " if (root.Jupyter !== undefined) {\n", " const events = require('base/js/events');\n", " const OutputArea = require('notebook/js/outputarea').OutputArea;\n", "\n", " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", " register_renderer(events, OutputArea);\n", " }\n", " }\n", " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", " root._bokeh_timeout = Date.now() + 5000;\n", " root._bokeh_failed_load = false;\n", " }\n", "\n", " const NB_LOAD_WARNING = {'data': {'text/html':\n", " \"\\n\"+\n", " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", " \"
\\n\"+\n", " \"\\n\"+\n",
" \"from bokeh.resources import INLINE\\n\"+\n",
" \"output_notebook(resources=INLINE)\\n\"+\n",
" \"
\\n\"+\n",
" \"\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"
\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"
\\n\"+\n \"The csv (comma separated variables) file format is widely used for data interchange, and can be used to transfer data to MATLAB, Excel, etc without writing any special conversion code.
(Many Python distributions provide the pandas module which can do the same using a slightly simpler interface, but the code below works in all versions of Python... an example of reading CSV using pandas is shown below.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python provides the csv module to simplify reading and writing csv files. We load it via:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:02.487222Z", "iopub.status.busy": "2025-08-18T03:36:02.487062Z", "iopub.status.idle": "2025-08-18T03:36:02.489849Z", "shell.execute_reply": "2025-08-18T03:36:02.489484Z" } }, "outputs": [], "source": [ "import csv" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
JSON is used for structured data interchange. It is a newer but widely used format and libraries for reading and writing JSON exist for most programming languages.
Python provides the json module to simplify reading and writing JSON files. We load it via:
" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:03.341037Z", "iopub.status.busy": "2025-08-18T03:36:03.340306Z", "iopub.status.idle": "2025-08-18T03:36:03.345504Z", "shell.execute_reply": "2025-08-18T03:36:03.345123Z" } }, "outputs": [], "source": [ "import json" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pickles are a Python-specific data exchange format.
Python provides the pickle module to read and write pickled files. We load it via:" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "execution": { "iopub.execute_input": "2025-08-18T03:36:03.516499Z", "iopub.status.busy": "2025-08-18T03:36:03.516322Z", "iopub.status.idle": "2025-08-18T03:36:03.520508Z", "shell.execute_reply": "2025-08-18T03:36:03.520118Z" } }, "outputs": [], "source": [ "import pickle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "