Alright, I've replicated the problem with a toy program. It boils down to an instance of unexpected behavior at the interface between NEURON and NMODL when combining NET_RECEIVE{INITIAL{}} with Netcon.event()
The elements are as follows:
One cell template
simpleCell
2 presynaptic cells, instances of
simpleCell numbered
preCell1 and
preCell2
2 postsynaptic cells, instances of
simpleCell, numbered
postCell1 and
postCell2
One postsynaptic point process, named
IF1
Two instances of
IF1, numbered
if1 &
if2, inserted in
postCell1 &
postCell2, respectively (
Warning: confusing nomenclature)
2 Netcons, named
nc1 &
nc2
2 synapses (pardon my pseudocode):
preCell1.soma.v(0.5) =>
postCell1.if1, via
nc1;
preCell2.soma.v(0.5) =>
postCell2.if2, via
nc2
The NMODL code for IF1 contains the following:
Code: Select all
NEURON{...RANGE{...gmax_fixed}}
...
ASSIGNED{...gmax_fixed}
...
NET_RECEIVE{INITIAL{...gmax_fixed = weight}...}
How it works:
in hoc, 2 different weights are assigned to the two netcons
nc1 &
nc2, then
finitialize() is called, then
nc1.event(t1) and
nc2.event(t2) are called, then
continuerun(tf) is called (tf > max(t1,t2))
What should happen:
The two instances of
if1 &
if2 each separately, execute their individual NET_RECEIVE{INITIAL{}} blocks; each reads the (different) weight of its connecting Netcon
nc1 or
nc2, respectively, and assigns it to its own separate instance of the
gmax_fixed ASSIGNED variable. At the conclusion of initialization, and while the simulation is running,
if1.gmax_fixed == nc1.weight[0] != if2.gmax_fixed == nc2.weight[0]
What actually happens:
*One* of the two instances
if1,if2 executes its NET_RECEIVE{INITIAL{}} block *twice* and the other instance executes it *zero* times. At the conclusion of initialization, and while the simulation is running
if1.gmax_fixed = nc2.weight[0] and
if2.gmax_fixed is uninitialized.
Perhaps this is related to the following, from the cvode documentation: "Many types of structure changes invalidate pointers used in the event queue." However, there are no "structure changes" following the call to finitialize(), only calls to Netcon.event().
Code and output follows.
Here is the hoc code:
Code: Select all
{load_file("nrngui.hoc")}
//Create postsynaptic cell #1
objref postCell1
postCell1 = new simpleCell()
postCell1.celldef()
//Insert synaptic mechanism instance #1 in postsynaptic cell #1
objref if1
postCell1.soma if1 = new IF1(0.5)
if1.id = 1
//Create postsynaptic cell #2
objref postCell2
postCell2 = new simpleCell()
postCell2.celldef()
//Insert synaptic mechanism instance #2 in postsynaptic cell #2
objref if2
postCell1.soma if2 = new IF1(0.5) //sic: IF1 not IF2
if2.id = 2
//Create presynaptic cell#1
objref preCell1
preCell1 = new simpleCell()
preCell1.celldef()
//Create presynaptic cell#2
objref preCell2
preCell2 = new simpleCell()
preCell2.celldef()
//Connect 1 Netcons from preCell1 to postCell1
objref nc1
preCell1.soma nc1 = new NetCon(&v(0.5), if1)
nc1.weight[0] = .6
nc1.weight[1] = 1
//Connect 1 Netcons from preCell2 to postCell2
objref nc2
preCell2.soma nc2 = new NetCon(&v(0.5), if2)
nc2.weight[0] = .7
nc2.weight[1] = 2
//Initialize, load event queue, and run.
{finitialize()}
nc1.event(10.0)
nc2.event(15.0)
continuerun(30)
print "HOC: nc1.weight[0] = ", nc1.weight[0]
print "HOC: nc1.weight[1] = ", nc1.weight[1]
print "HOC: nc2.weight[0] = ", nc2.weight[0]
print "HOC: nc2.weight[1] = ", nc2.weight[1]
print "HOC: if1.gmax_fixed = ", if1.gmax_fixed
print "HOC: if2.gmax_fixed = ", if2.gmax_fixed
Here is the NMODL code:
Code: Select all
NEURON {
POINT_PROCESS IF1
RANGE gmax_fixed,id
}
PARAMETER {
id = 0
}
ASSIGNED {
gmax_fixed
}
INITIAL {
gmax_fixed = 88
}
NET_RECEIVE (weight,ncType) {
INITIAL {
weight = weight
ncType = ncType
printf("NMODL (initial): id=%g\tweight=%g\tncType=%g\n", id,weight,ncType)
gmax_fixed = weight
}
IF (flag == 0) {
IF (gmax_fixed != weight) {
printf ("NMODL: gmax_fixed != weight at first presynaptic spike\n")
:sanityFailure()
} ELSE {
printf ("NMODL: gmax_fixed == weight\n")
}
printf("id = %g\tt = %g\tgmax_fixed = %g\tweight=%g\tncType=%g\n",id,t,gmax_fixed,weight,ncType)
}
}
Here is the simpleCell template definition (proabably not relevant, but included for completeness):
Code: Select all
//Define simpleCell class
begintemplate simpleCell
public celldef, position
public soma
proc celldef() {
topol()
subsets()
geom()
biophys()
geom_nseg()
}
create soma
proc topol() { local i
basic_shape()
}
proc basic_shape() {
soma {pt3dclear() pt3dadd(0, 0, 0, 1) pt3dadd(15, 0, 0, 1)}
}
proc position () {
soma {
netX = $1
netY = $2
netZ = $3
}
}
objref all
proc subsets() { local i
objref all
all = new SectionList()
soma all.append()
}
proc geom() {
soma { L = 30 diam = 30 }
}
proc geom_nseg() {
soma area(.5) // make sure diam reflects 3d points
}
proc biophys() {
soma {
Ra = 80
cm = 1
insert hh
gnabar_hh = 0.12
gkbar_hh = 0.036
gl_hh = 0.0003
el_hh = -54.3
}
}
access soma
endtemplate simpleCell
And here is the output:
Code: Select all
NEURON -- VERSION 6.2.1028 (2151) 2008-06-18
Duke, Yale, and the BlueBrain Project -- Copyright 1984-2007
See http://www.neuron.yale.edu/credits.html
loading membrane mechanisms from ...snip.../i686/.libs/libnrnmech.so
Additional mechanisms from files
...snip... if1.mod
oc>load_file("test3.hoc")
0
0
0
0
NMODL (initial): id=1 weight=0.6 ncType=1
NMODL (initial): id=1 weight=0.7 ncType=2
1
1
NMODL: gmax_fixed != weight at first presynaptic spike
id = 1 t = 10 gmax_fixed = 0.7 weight=0.6 ncType=1
NMODL: gmax_fixed != weight at first presynaptic spike
id = 2 t = 15 gmax_fixed = 88 weight=0.7 ncType=2
HOC: nc1.weight[0] = 0.6
HOC: nc1.weight[1] = 1
HOC: nc2.weight[0] = 0.7
HOC: nc2.weight[1] = 2
HOC: if1.gmax_fixed = 0.7
HOC: if2.gmax_fixed = 88
1
oc>