CoreNEURON Compatibility
CoreNEURON can transparently handle all spiking network simulations, including gap junction coupling, with the fixed time step method. In this document we summarise NEURON features that are not supported by CoreNEURON and changes required to make a model compatible with CoreNEURON.
Unsupported Features
The below table summarises NEURON features that are presently not supported with CoreNEURON:
Feature |
NEURON |
CoreNEURON |
Remarks * |
|---|---|---|---|
Variable time step methods (i.e CVODE and IDA) |
✔ |
✖ |
|
Extracellular Mechanism |
✔ |
✖ * |
If i_membrane is of sole interest, then you can switch from extracellular to cvode.use_fast_imem(1) and make use of i_membrane_ |
Linear Mechanism |
✔ |
✖ |
|
RxD Module |
✔ |
✖ |
|
Per-timestep interpreter calculations during simulation |
✔ |
✖ * |
Generally, Vector.record and Vector.play are ok, as are the display of GUI variable trajectories. Anything requiring callbacks into the interpreter (Python or HOC) are not implemented. |
If you are using any of the above mentioned features in your model then CoreNEURON library can not be used for simulations. If you are interested in these features with CoreNEURON or performance improvement in general, feel free to provide feedback via a GitHub issue.
MOD File Compatibility
In order to use CoreNEURON, you need to build MOD files with CoreNEURON support i.e. use -coreneuron option as nrnivmodl -coreneuron <mod dir>. If this command fails during compilation or translating MOD files but nrnivmodl <mod dir> is working fine then most likely there are MOD files incompatibility issues. In this section we describe how you can address such incompatibility.
Thread Safe MOD Files
One of the important difference in CoreNEURON execution with respect to NEURON is that multiple instances of a specific channel or synapse are executed in parallel. This parallelism could be via threads or SIMD instructions on modern CPUs/GPUs. Most of the MOD files are thread safe for parallel execution but there are certain constructs like GLOBAL variables or VERBATIM blocks that are not compatible by default. In this case user has to make sure MOD files are
thread safe. NEURON provides a script mkthreadsafe that can provide some help to make your MOD files are thread safe. You can find more information here. Here are some additional examples that will help you to make a MOD file thread safe:
If you are using
GLOBALvariables in a mod file, make sure those are not updated during execution (e.g. inINITIAL,BREAKPOINTorDERIVATIVEblock etc). IfGLOBALvariable is writen then those variables are now needed to be defined asRANGE. You can find information aboutGLOBALandRANGEvariables in NMODL specification.As an example, below MOD file defines
minfvariable as aGLOBALand it is being updated in theDERIVATIVEblock whenPROCEDURE rates()is executed.NEURON { SUFFIX test GLOBAL minf } ... DERIVATIVE states { rates(v) m' = (minf-m)/1.5 } PROCEDURE rates(v (mV)) { minf = minf+1 }
As
GLOBALvariable is shared across multiple instances of a mechanism, parallel execution will result into a race condition whenminfis updated. In order to avoid this we need to simply convert it to aRANGEvariable:NEURON { SUFFIX test RANGE minf } ...
Note that if you are accessing such variable via HOC or Python scripting interface then syntax for accessing
GLOBALis different thanRANGEvariable. So you need to update your code accordingly.If you are using
VERBATIMblocks to overcome some limitations of NMODL language then such MOD file is by default treated as not thread safe. For example, in below case we are are usingVERBATIMblock to include some C header and return early fromINITIALblock:NEURON { SUFFIX test RANGE minf } VERBATIM #include <stdlib.h> ENDVERBATIM ASSIGNED { v (mV) minf } STATE { m } INITIAL { rate(v) m = minf VERBATIM return 0; ENDVERBATIM } ...
Technically, this mod file is thread safe as we don’t have any race condition. But due to
VERBATIMblock this mod file is assumed non thread safe and hence we have to explicitly specify THREADSAFE keywork in the beginning of theNEURONblock as:NEURON { THREADSAFE SUFFIX test RANGE minf } ...
Also, note that
NEURONblock needs to be before anyVERBATIMblock in the MOD file. So its safer to keepNEURONblock at the top of MOD file.Certain
SOLVEmethods likeeulerare not thread safe since the best practical methods arecnexpfor HH-like equations andderivimplicitfor all the others. If you have such a MOD file:SOLVE state METHOD euler
then replace
eulerwithcnexp.
TABLE Usage With GPU Execution
Currently TABLE constructs are not supported if you are building MOD files with GPU support. As TABLE constructs are used for efficiency reason (and not accuracy), you can safely comment out TABLE statement using : operator:
PROCEDURE rates(v(mV)) { : TABLE minf, mtau, hinf, htau, ninf, ntau DEPEND celsius FROM -100 TO 100 WITH 200
NEURON Only MOD Files
Certain MOD files are used for aspects like progress callbacks, reading inputs, etc. Often such MOD files are heavily depend on VERBATIM blocks and use internal data structure or functions provided by NEURON. Most likely such MOD files won’t be compiled by CoreNEURON as they are using internal, NEURON specific APIs in VERBATIM blocks. If such mod file is used for only a usability aspect like progress bar then you can exclude that from compilation. Other option is to conditionally compile all VERBATIM blocks using macro NRNBBCORE.
As an example, the code in below #ifndef NRNBBCORE block will be only compiled for NEURON.
VERBATIM
#ifndef NRNBBCORE
<code block to be executed only by NEURON>
#endif
ENDVERBATIM
This way you can hide NEURON specific code from CoreNEURON compilation process.
Explicit ION Variables Update
In some old MOD files ion currents are explicitly initialized in INITIAL blocks using VERBATIM construct as:
VERBATIM
cai = _ion_cai;
Cai = _ion_Cai;
ENDVERBATIM
Since such ion variables are implicitely updated by the code from NMODL transpiler, VERBATIM blocks like above are not required and must be deleted from the MOD files.
Random Number Generators: Random123 vs MCellRan4
Pseudo-random numbers from a variety of distributions can be generated using NEURON’s Random class. CoreNEURON only supports Random123 generator.
Memory Management for POINTER Variables
User-allocated data managed in NMODL is a complex topic. Using POINTER variables, users can reference data that has been allocated in HOC or in VERBATIM blocks. Using this end users can built more advanced data-structures that are not natively supported in NMODL. Another commonly used example is point processes / synapses where POINTER variables used for holding random number generator object.
Since NEURON itself has no knowledge of the layout and size of this user allocated data,
it cannot transfer POINTER data automatically to CoreNEURON.
Furtheremore, in many cases there is no need to transfer the data between the two instances.
In some cases, however, the programmer would like to transfer certain user-defined data into CoreNEURON.
The most prominent example are Random123 random number stream parameters used in synapse mechanisms.
In order to inform NEURON that such POINTER variable needs to be transferred, the BBCOREPOINTER type was introduced.
Variables that are declared as BBCOREPOINTER behave exactly the same as POINTER but are
additionally taken into account when NEURON is transferring model to CoreNEURON for simulation.
For NEURON to be able to write (and indeed CoreNEURON to be able to read) BBCOREPOINTER
data, the programmer has to additionally provide two C functions that are called as part
of the serialization/deserialization:
static void bbcore_write(double* x, int* d, int* d_offset, int* x_offset, _threadargsproto_);
static void bbcore_read(double* x, int* d, int* d_offset, int* x_offset, _threadargsproto_);
The implementation of bbcore_write and bbcore_read determines the serialization and
deserialization of the per-instance mechanism data referenced through the various
BBCOREPOINTER.
NEURON will call bbcore_write twice per mechanism instance.
In a first sweep, the call is used to determine the required memory to be allocated on the serialization arrays.
In the second sweep the call is used to fill in the data per mechanism instance.
Argument |
Description |
|---|---|
|
A |
|
An |
|
The offset in |
|
The offset in |
|
A macro placeholder for NEURON/CoreNEURON data-structure parameters. They are typically only used through generated defines and not by the programmer. The macro is defined as follows: #define _threadargsproto_ int _iml, int _cntml_padded, double *_p, Datum *_ppvar, \
ThreadDatum *_thread, NrnThread *_nt, double _v
|
Putting all of this together, the following is a minimal MOD using BBCOREPOINTER:
TITLE A BBCOREPOINTER Example
NEURON {
BBCOREPOINTER my_data : changed from POINTER
}
ASSIGNED {
my_data
}
: Do something interesting with my_data ...
VERBATIM
static void bbcore_write(double* x, int* d, int* x_offset, int* d_offset, _threadargsproto_) {
if (x) {
double* x_i = x + *x_offset;
x_i[0] = _p_my_data[0];
x_i[1] = _p_my_data[1];
}
*x_offset += 2; // reserve 2 doubles on serialization buffer x
}
static void bbcore_read(double* x, int* d, int* x_offset, int* d_offset, _threadargsproto_) {
assert(!_p_my_data);
double* x_i = x + *x_offset;
// my_data needs to be allocated somehow
_p_my_data = (double*)malloc(sizeof(double)*2);
_p_my_data[0] = x_i[0];
_p_my_data[1] = x_i[1];
*x_offset += 2;
}
ENDVERBATIM
If you have models with POINTER variables and user allocated memory then this requires due diligence. Below are some of the existing models adapted for CoreNEURON. These MOD files can act as a reference or you can simply reuse them if applicable:
Have a question?
If you have any questions to make your model compatible with CoreNEURON, reach out to us via GitHub issue.