Page 1 of 1

Making a netcon know what triggered it

Posted: Tue Sep 29, 2009 8:17 pm
by Bill Connelly
I am trying to make a cell that sets its sodium channel conductance to zero for a period of time after its action potential

Code: Select all

proc preprocess() { localobj, shutDownNetCon, nil
  for n=0, ncells-1 {
    cl.o(n).soma shutDownNetCon = new NetCon(&v(0.5), nil)
    shutDownNetCon.threshold = 0
    shutDownNetCon.record("shutdown(cl.o(n))")
  }
}

proc shutdown() {
 $o1.soma.gnbar_hh = 0
}
Now that obviously doesn't work, because the argument isn't evaluated until the netcon is actually triggered. But what can I do such that the receiving function knows what cell triggered the netcon?

Re: Making a netcon know what triggered it

Posted: Wed Sep 30, 2009 3:02 pm
by ted
Bill Connelly wrote:I am trying to make a cell that sets its sodium channel conductance to zero for a period of time after its action potential
. . .
what can I do such that the receiving function knows what cell triggered the netcon?
I'm not entirely sure about the connection between the question and your stated goal.

If NEURON's density mechanisms could have NET_RECEIVE blocks, the "natural" way to accomplish what you want to do would be with WATCH and net_send. The idea would be to use WATCH to detect spikes, and then use the event delivery system to switch the effective conductance density between 0 and gbar. But only artificial spiking cells and point processes can have NET_RECEIVE blocks.

You _could_ use NetCon's event() method to call a procedure that sets gbar to 0 and uses cvode_event() to call another procedure that restores gbar to its nonzero value. However, the supporting code gets awkward if you are dealing with a network. So maybe now I understand the connection between your goal and your question.

An alternative tactic is to use a sodium conductance mechanism written as a point process. In addition to the usual stuff, it will also need the following:

Code: Select all

PARAMETER {
  gbar = 0.12 (S/cm2) : or whatever
  thresh = 10 (mV) : or whatever you prefer
  toff = 1 (ms) : channel "off" duration
}

ASSIGNED {
  area (um2) : area of current segment (automatically available within NMODL, like v)
  gmax (uS) : set to gbar * segment area at initialization
  gm (uS) : will be 0 or gmax depending on recent spiking history
}

INITIAL {
  gmax = gbar*area : this will need a scale factor to reconcile units
    : because area will be in um2, but gmax is in uS and gbar in S/cm2
    : "left as an exercise to the reader"
  gm = gmax : because at t = 0 we assume that the cell has not yet spiked
   . . .
}

BREAKPOINT {
 . . .
 g = gm*h*m^3 : or whatever the gate names and exponents are
 . . .
}

NET_RECEIVE () {
  WATCH (v > thresh) 1 : detect spike
  if (flag==1) { : a spike has occurred
    if (gm > 0) { : conductance is on, so turn it off
      gm = 0
      net_send(2, toff) : to turn it back on
    }
    if (gm==0) { : it's already off, so delay the recovery time
      net_move(t + toff)
    }
  }
  if (flag==2) { : time to turn it back on
    gm = gmax
  }
}
I believe the above should work, but there could be a typo or syntax error, and there is at least one units inconsistency, so test and revise as necessary. An instance of this mechanism will have to be attached to each segment whose gna is supposed to drop to 0 when a spike occurs. You won't have to attach any NetCons to the segments, and you won't need any supporting hoc code other than what it takes to attach this point process mechanism to those segments that need it.

Re: Making a netcon know what triggered it

Posted: Wed Sep 30, 2009 9:28 pm
by Bill Connelly
MOD code is still a bit of a dark art to me. Why does the if (v > thresh) statement need to be in the NET_RECEIVE block? Couldn't it just be in the breakpoint block?
Just psuedo coding but something like

Code: Select all

if (v > thresh AND trigger == 0 ) {
gnabar = 0
trigger=1
t0=t
}

if (trigger == 1 AND t-t0>delay) {
trigger = 0
gnabar = 10
}
Or would those pair of if statements be a cruel thing to put in a breakpoint block?

Indeed, now that I think about it, why is the WATCH statement needed at all? Isn't the net_receive block called by (and only by) a netcon event?

Re: Making a netcon know what triggered it

Posted: Thu Oct 01, 2009 10:43 am
by ted
MOD code is still a bit of a dark art to me.
Not just to you. It's not exactly Pascal, or C++, or Perl, or Python, but there are some flashes of brilliance in NMODL (units, ODE and kinetic scheme notation, and most recently events).

The most efficient and straightforward way to implement the particular functionality required by the problem you posed is with a state machine that reactivates the conductance at a fixed time interval after the most recent local spike. There's nothing magic about detecting threshold crossing--that can be done just as easily in a BREAKPOINT block or a NET_RECEIVE block.

The difficult part is how to implement the processes that must happen after a threshold crossing has occurred. It is cumbersome to implement state machines, and especially anything that involves time delays, in the BREAKPOINT block. Time delays are a particular problem because conditional statements that test the value of t will not work properly with adaptive integration ("cvode"). That can be circumvented with at_time, but doing so leads to awkward logic constructs that can be hard to write and maintain.

The event delivery system makes it much easier to implement state machines, especially state machines that involve fixed delays. Almost trivial in this particular case.
Why does the if (v > thresh) statement need to be in the NET_RECEIVE block?
It's not a (v > thresh) statement--it's a WATCH statement. The (v > thresh) bit is the "condition" part of the WATCH statement. When the "condition" of a A WATCH statement is satisfied, a self-event associated with the specified flag value is launched). Like other statements that use the event delivery system, WATCH statements can only be written in a NET_RECEIVE block.

Re: Making a netcon know what triggered it

Posted: Thu Oct 01, 2009 10:34 pm
by Bill Connelly
Okay,

I got it to work apart from the important bit, i.e. the sodium current doesn't shut off. I had to give net_receive an argument, or else it would compile. I suspect that is where to problem is?

I don't need to set up any netcons do it? The WATCH statement is running the whole time, isn't it?

Code: Select all

TITLE nethhwbm.mod   interneuron sodium, potassium, and leak channels
 
 
UNITS {
        (mA) = (milliamp)
        (mV) = (millivolt)
}

NEURON {
  POINT_PROCESS hh_wbm_pnt
  NONSPECIFIC_CURRENT ina,ik,il

  RANGE gnabar,gna,egna,m, gkbar,gk,egk, gl,el
	GLOBAL hinf, ninf, htau, ntau

}
 
PARAMETER {
  gnabar = .08 (mho/cm2)	<0,1e9>
  egna = 55 (mV)	
  gkbar = .09 (mho/cm2)	<0,1e9>
 egk	= -90 (mV)	
  gl = .0001 (mho/cm2)	<0,1e9>
  el = -65 (mV)
	
  thresh = 0 (mV)
  toff = 50 (ms)
}
 
STATE {
        m h n
}
 
ASSIGNED {
  v (mV)
  celsius (degC)

  gna (mho/cm2)
  ina (mA/cm2)
  gk (mho/cm2)
  ik (mA/cm2)
  il (mA/cm2)
  minf hinf ninf
  htau (ms) ntau (ms)

  gnamax (uS)
  gkmax (uS)
  glmax (uS)
  area (um2)
		
  gnam (uS)
}
 
LOCAL mexp, hexp, nexp        
 
BREAKPOINT {
SOLVE states METHOD cnexp
  m = minf
  gna = gnam*m*m*m*h
  ina = gna*(v - egna)

  gk = gkmax*n*n*n*n
  ik = gk*(v - egk)      
  il = glmax*(v - el)
}
 
 
INITIAL {
	rates(v)
	m = minf
	h = hinf
	n = ninf
	
	gnamax = (gnabar*area)/100
	gkmax = (gkbar*area)/100
	glmax = (gl*area)/100

	gnam=gnamax
}


DERIVATIVE states {  
        rates(v)
        h' = (hinf-h)/htau
        n' = (ninf-n)/ntau
}
 
LOCAL q10


PROCEDURE rates(v(mV)) {  :Computes rate and other constants at current v.
                          :Call once from HOC to initialize inf at resting v.
		      
        LOCAL  alpha, beta, sum
        TABLE minf, hinf, htau, ninf, ntau DEPEND celsius FROM -100 TO 100 WITH 200


UNITSOFF
        q10 = 3^((celsius - 6.3)/10)

               :"m" sodium activation system
        alpha = .1 * vtrap(-(v+35),10)
        beta =  4 * exp(-(v+60)/18)
        sum = alpha + beta
        minf = alpha/sum

                :"h" sodium inactivation system
        alpha =.35 * exp(-(v+58)/20)
        beta = 5 / (exp(-(v+28)/10) + 1)
        sum = alpha + beta
	htau = 1/(q10*sum)
        hinf = alpha/sum

                :"n" potassium activation system
        alpha =.05*vtrap(-(v+34),10) 
        beta = .625*exp(-(v+44)/80)
	sum = alpha + beta
        ntau = 1/(q10*sum)
        ninf = alpha/sum
}
 
FUNCTION vtrap(x,y) {  :Traps for 0 in denominator of rate eqns.
        if (fabs(x/y) < 1e-6) {
                vtrap = y*(1 - x/y/2)
        }else{
                vtrap = x/(exp(x/y) - 1)
        }
}
 
UNITSON


NET_RECEIVE (null) {
  WATCH (v > thresh) 1 : detect spike
  if (flag==1) { : a spike has occurred
    if (gnam > 0) { : conductance is on, so turn it off
      gnam = 0
      net_send(2, toff) : to turn it back on
    }
    if (gnam==0) { : it's already off, so delay the recovery time
      net_move(t + toff)
    }
  }
  if (flag==2) { : time to turn it back on
    gnam = gnamax
  }
}
EDIT: Okay, I'm getting the feeling that I do need to set up a netcon, but now I'm getting and error:
No event with flag=1 for net_move in hh_wbm_pnt[0]

Re: Making a netcon know what triggered it

Posted: Fri Oct 02, 2009 12:40 pm
by ted
Sorry, my mistake. I forgot to properly initialize the WATCH statement. In order for the stuff in the NET_RECEIVE block to have any effect--including the WATCH statement--there must be a self-event at t==0 so that the WATCH statement is executed before the simulation takes off. An additional subtlety that tripped me up was that net_move operates only on an outstanding self-event that has flag==1. And not so subtle was that the arguments to net_send were out of sequence.

Thinking out loud before starting, here's what must be done: the mechanism needs to send events with 3 different flag values. 1 must be reserved for the "turn back on" signal, because this is the only flag whose event can be moved. So use 3 to initialize the WATCH statement, and let the WATCH statement generate an event with flag==2.

This necessitates several changes in the code I recommended. The first change is to insert
net_send(0, 3)
into the INITIAL block. This launches a self-event with flag==3 that will trigger execution of the WATCH statement.

The other changes are in the NET_RECEIVE block:

Code: Select all

NET_RECEIVE () {
  if (flag==2) { : a spike has occurred
    if (gm > 0) { : conductance is on, so turn it off
      gm = 0
      net_send(toff, 1) : to turn it back on
    }
    if (gm==0) { : it's already off, so delay the recovery time
      net_move(t + toff)
    }
  }
  if (flag==1) { : time to turn it back on
    gm = gmax
  }
  if (flag==3) {
  WATCH (v > thresh) 2 : detect spike
  }
}
That ought to do it, but my capacity for syntax and typographical errors is boundless, so I wouldn't be surprised if there are hiccups that need to be eliminated.

Re: Making a netcon know what triggered it

Posted: Fri Oct 02, 2009 11:12 pm
by Bill Connelly
So this flag variable, is it automatically available inside any NET_RECEIVE block? And it just represents the second argument passed by a net_send function? Is there a full reference manual of NMODL somewhere?

Re: Making a netcon know what triggered it

Posted: Sat Oct 03, 2009 12:20 am
by ted
flag is an implicit argument to NET_RECEIVE. It is an integer, zero by default but a nonzero value can be specified via the second argument to net_send()*. This is documented in chapter 11 of The NEURON Book (see the "Saturating synapses" example; it appears in the Index under
event / flag
and
event / external / distinguishing from a self-event

*--net_send() is used to launch self-events. A self-event comes back to the mechanism that launched it, no NetCon required. Self-events plus flags and a bit of logic make it very easy to set up state machines.
Unlike net_send(), net_event() takes only one argument (event time); it requires NetCons to convey its events to targets, and the associated flag is always 0.
Is there a full reference manual of NMODL somewhere?
Good question. Most of it is documented in The NEURON Book. Some new bits and pieces have been added since then, such as WATCH statements, and these will appear either in the next edition of the NEURON Book or in a second volume.

"What do you call a language whose vocabulary and syntax have stopped evolving?"
"Dead."

Re: Making a netcon know what triggered it

Posted: Sat Oct 03, 2009 7:47 am
by Bill Connelly
So I had to include a delay from when the membrane potential hits zero to when the sodium conductance is shut off, because otherwise the the AP is stunted, and the AHP takes on a very different shape. (I tried just setting the threshold higher, but it didn't seem to work very well). I just wanted to check that the way I put in the delay is a good way, or whether there is a better way, and just generally whether it gets the Ted seal of approval

Code: Select all

NET_RECEIVE (dummy) {
  if (flag==2) { : a spike has occurred
    net_send(delay, 4)
  }
  if (flag==4) {
    if (gnam > 0) { : conductance is on, so turn it off
      gnam = 0
      net_send(toff, 1) : to turn it back on
    }
    if (gnam==0) { : it's already off, so delay the recovery time
      net_move(t + toff)
    }
  }
  if (flag==1) { : time to turn it back on
    gnam = gnamax
  }
  if (flag==3) {
  WATCH (v > thresh) 2 : detect spike
  }
}

Re: Making a netcon know what triggered it

Posted: Sat Oct 03, 2009 12:20 pm
by ted
The true test is whether it runs without bugs. Somehow, the most pernicious errors are those that one writes oneself. I have the hardest time spotting my own bugs, but when examining someone else's code I can identify gross errors most of the time, and discover subtle errors much of the time. NEURON, however, will discover them all sooner or later.

So the question to you is, have you tested it, and does it work? Have you used an IClamp to force v above thresh while gnam was already 0, and does this delay the gnam reactivation time appropriately?

A further note: even though this code may work properly under all circumstances, it still invites a future error. Check out this excerpt:

Code: Select all

    if (gnam > 0) { : conductance is on, so turn it off
      gnam = 0
      net_send(toff, 1) : to turn it back on
    }
    if (gnam==0) { : it's already off, so delay the recovery time
      net_move(t + toff)
    }
Originally I had written the entire NET_RECEIVE block using "if ( ) { . . . } else if ( ) { . . . }" chains. However, this tends to be less readable, at least to me, so in posting to the Forum I broke it into separate if clauses.

Consider what happens when these statements are executed. The first if statement will set gnam to 0 and launch a self-event with flag 1 that returns at time t+toff. The second if statement will then call net_move to move this self event to t+toff. No harm done because the flag 1 event return time is unaltered, but (1) it wastes run time, and more importantly, (2) it is vulnerable to future error. Suppose at some future date you decide that a threshold crossing that occurs while gnam is already 0 should move the reactivation time to t+t2 where t2 != toff. Since the 2nd if statement's
net_move(t + t2)
is always executed, even the first threshold crossing will turn gnam off for t2 instead of toff ms. Not what you wanted at all.

So it's best to change

Code: Select all

    }
    if (gnam==0) { : it's already off, so delay the recovery time
to

Code: Select all

    } else if (gnam==0) { : it's already off, so delay the recovery time