Accessing Netcon.weight from NMODL before first Netcon event

Moderator: wwlytton

Post Reply
sec6
Posts: 39
Joined: Sat Mar 15, 2008 10:32 am

Accessing Netcon.weight from NMODL before first Netcon event

Post by sec6 »

In a synaptic mechanism I wrote in NMODL, I attempted to read the value of the connecting Netcon.weight at intialization time. Specifically, my NET_RECEIVE block contained this line:

Code: Select all

gmax = weight
which executed *before* the first Netcon event occurred.
This didn't work. If I understand correctly, that's because NET_RECEIVE is a *function* (or a procedure, or a function-with-side-effects) and weight is a parameter passed to it when NEURON calls the function, which happens at the time of an event associated with the connecting Netcon. Prior to that, the weight parameter is undefined. The following code (Example 1) ...

Code: Select all

...
INITIAL {
	...
	net_send(0,99)
	...
}
...
NET_RECEIVE (weight) {
	...
	IF (flag == 99) {
		gmax = weight
	}
}
... results in a bus error.
Seems to me NET_RECEIVE is both a function and not a function, depending on how one enters the code block. So, my first question is: Have I understood this correctly so far?

I next tried the following (Example 2):

Code: Select all

NET_RECEIVE (weight) {
	INITIAL {
	gmax = weight
	}
	...
}
This also doesn't work.
However, it fails *silently* No bus error -- it's just that the value of gmax is unaffected by this code as of t=0. The fact that there's no bus error makes me wonder if I've either 1) misunderstood what was going on in the previous example or 2) there's something about the nested NET_RECEIVE{INITIAL{}} block I don't understand.
So, my second question is: What exactly is going on here? When, exactly does the NET_RECEIVE{INITIAL{}} block execute (and does it execute once for each separate input stream?).

Finally, my third, broadest question is: can I, using only NMODL code, read the value of a connecting Netcon.weight *before* the first event associated with that Netcon (specificially, can I read it at initialization time)? (The mechanism must be compatable with existing, unmodified model in NEURON, so solutions involving modifications to the NEURON code are considered 'cheating.')
ted
Site Admin
Posts: 6289
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Accessing Netcon.weight from NMODL before first Netcon event

Post by ted »

Good questions, but not relevant to NEURON + Python, so this thread is now in Modeling networks.
hines
Site Admin
Posts: 1682
Joined: Wed May 18, 2005 3:32 pm

Re: Accessing Netcon.weight from NMODL before first Netcon event

Post by hines »

The bus error for example 1 is appropriate. A net_send from INITIAL supplies only a flag, not a NetCon weight array.
Supplying a weight array from INITIAL is not supported.

Example 2 should work. At least the following
if1.mod

Code: Select all

NEURON {
        ARTIFICIAL_CELL IF1
        RANGE gmax
}
ASSIGNED { gmax }
INITIAL {
        gmax = .1
}
NET_RECEIVE (w) {
        INITIAL {
                printf("w=%g\n", w)
                gmax = w
                w = .2
        }
}
if1.hoc

Code: Select all

{load_file("nrngui.hoc")}
objref if1, nc, nil
if1 = new IF1()
nc = new NetCon(nil, if1)
nc.weight = .5
{finitialize()}
print "nc.weight = ", nc.weight
print "if1.gmax = ", if1.gmax
produces the output

Code: Select all

...
Additional mechanisms from files
 if1.mod
w=0.5
nc.weight = 0.2 
if1.gmax = 0.5 
oc>
sec6
Posts: 39
Joined: Sat Mar 15, 2008 10:32 am

Re: Accessing Netcon.weight from NMODL before first Netcon event

Post by sec6 »

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>
hines
Site Admin
Posts: 1682
Joined: Wed May 18, 2005 3:32 pm

Re: Accessing Netcon.weight from NMODL before first Netcon event

Post by hines »

The current standard distribution does not exhibit that bug and produces the output.
There have been so many changes from 6.2.3 that I'm not sure which changset fixed
the error.

Code: Select all

]$ nrngui init.hoc
NEURON -- Release 7.0 (280:70e5a78ac214) 2009-01-15
Duke, Yale, and the BlueBrain Project -- Copyright 1984-2008
See http://www.neuron.yale.edu/credits.html

loading membrane mechanisms from /home/hines/tmp/tmp2/x86_64/.libs/libnrnmech.so
Additional mechanisms from files
 test.mod
        0 
        0 
        0 
        0 
NMODL (initial): id=1   weight=0.6      ncType=1
NMODL (initial): id=2   weight=0.7      ncType=2
        1 
        1 
NMODL: gmax_fixed == weight
id = 1  t = 10  gmax_fixed = 0.6        weight=0.6      ncType=1
NMODL: gmax_fixed == weight
id = 2  t = 15  gmax_fixed = 0.7        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.6 
HOC: if2.gmax_fixed = 0.7 
oc>
Post Reply