First, let me apologize for the delay in getting back to you. You were on what might be the right track, but there are many details that must be dealt with in setting up a mechanism with nmodl. Also, the "best" way to do something depends on the user's actual goal, which is not always apparent. For example, the question of whether you really need a synaptic mechanism that uses a POINTER to connect the pre- and postsynaptic cells, or whether you would be better off with something that accomplishes this coupling with events is yet to be answered.
Not knowing the answer, I had to assume something about the time course of glutamate concentration on the synaptic cleft in order to create and test a working mechanism, so I assumed that
(1) glutamate is either absent, or present at some constant concentration, e.g. 1 mM to start (eventually I ran tests with peak conc 0.1 mM, because 1 mM produced a large depolarization in my model cell)
(2) the transition between these conditions is rapid
(3) the duration of nonzero [glu] is 1 ms
What follows is the resulting mechanism, the core of a program that tests this mechanism, and some comments about the implementation.
Code: Select all
NEURON {
POINT_PROCESS AMPA5
POINTER C
USEION ca WRITE ica
GLOBAL Rb, Ru1, Ru2, Rd, Rr, Ro, Rc
RANGE g
GLOBAL Erev
RANGE gmax, gca
NONSPECIFIC_CURRENT i
RANGE ica
}
UNITS {
(nA) = (nanoamp)
(mV) = (millivolt)
(pS) = (picosiemens)
(umho) = (micromho)
(mM) = (milli/liter)
(uM) = (micro/liter)
(um) = (micron)
}
PARAMETER {
Erev = 0 (mV) : reversal potential
gmax = 500 (pS) : maximal conductance
gca = 0.5 (1) : a fractional conductance
Rb = 13 (/mM /ms): binding
: diffusion limited (DO NOT ADJUST)
Ru1 = 0.0059 (/ms) : unbinding (1st site)
Ru2 = 86 (/ms) : unbinding (2nd site)
Rd = 0.9 (/ms) : desensitization
Rr = 0.064 (/ms) : resensitization
Ro = 2.7 (/ms) : opening
Rc = 0.2 (/ms) : closing
}
ASSIGNED {
v (mV) : postsynaptic voltage
i (nA) : current = g*(v - Erev)
g (pS) : conductance
C (mM) : pointer to glutamate concentration
ica (nA) : so you can plot the calcium current generated by this mechanism
}
STATE {
: Channel states (all fractions)
C0 : unbound
C1 : single glu bound
C2 : double glu bound
D1 : single glu bound, desensitized
D2 : double glu bound, desensitized
O : open state 2
}
INITIAL {
C0=1
C1=0
C2=0
D1=0
D2=0
O=0
SOLVE kstates STEADYSTATE sparse
}
BREAKPOINT {
SOLVE kstates METHOD sparse
g = gmax * O
i = (1e-6) * g * (v - Erev)
: are you sure you don't want ica = gca*g*(v - eca) ?
: in which case you'll want to READ eca
ica = gca * i
}
KINETIC kstates {
COMPARTMENT 1 (m3) {C} : big so xmtr conc will not be affected by binding to receptor
~ C0 + C <-> C1 (Rb,Ru1)
~ C1 + C <-> C2 (Rb,Ru2)
~ C1 <-> D1 (Rd,Rr)
~ C2 <-> D2 (Rd,Rr)
~ C2 <-> O (Ro,Rc)
CONSERVE C0+C1+C2+D1+D2+O = 1
}
Comments:
The INDEPENDENT statement was omitted; INDEPENDENT statements are irrelevant to NEURON.
NEURON block
Unused stuff has been eliminated from the NEURON block and other blocks. Examples:
L and d. Synaptic mechanisms are implemented as point processes, and point processes generate currents in absolute units, not density units. I have never found it necessary to use L in any mod file. diam may be useful for mod files that specify accumulation mechanisms, but are rarely useful in point processes.
Declaration of the STATEs as being RANGE variables. STATEs are automatically RANGE variables and do not have to be declared as such.
vmin and vmax were unused.
rb is not useful and has been eliminated (see discussion of KINETIC block).
ica is declared RANGE so you can plot the calcium current that is generated by an instance of AMPA5. Otherwise, this variable would have been inaccessible to hoc.
INITIAL block
The assignment statements are fine if [glu] is initially supposed to be 0.
SOLVE kstates STEADYSTATE sparse
was inserted so that the mechanism can properly initialize itself even if initial [glu] is nonzero.
BREAKPOINT block
Are you sure you want to calculate ica as gca * i, rather than
ica = gca*g*(v - eca) ?
The latter would require you to change the USEION ca statement to
USEION ca READ eca WRITE ica
and also to do something that specifies the value of eca--e.g., if there is no mechanism that WRITEs cai, you could simply use a hoc statement such as
eca = 60 // or whatever you like
KINETIC block
The original formulation was
rb = Rb * C
~ C0 <-> C1 (rb,Ru1)
~ C1 <-> C2 (rb,Ru2)
but this might cause problems if you ever decide to allow receptor + C to consume C.
I rewrote these as
~ C0 + C <-> C1 (Rb,Ru1)
~ C1 + C <-> C2 (Rb,Ru2)
but that required a COMPARTMENT statement for consistency of units
COMPARTMENT 1 (m3) { C }
This particular statement makes C effectively constant (its volume of distribution is huge, so there's a lot more C than there is receptor).
modlunit passes this revised mechanism without complaint.
Here's the hoc file that I used to exercise this mechanism:
Code: Select all
load_file("nrngui.hoc")
START = 1 // time at which [glu] changes from 0 to CONC
DUR = 1 // duration of nonzero CONC
CONC = 0.1 // mM
create soma
access soma
soma {
L = 10
diam = 100/L/PI
insert pas
g_pas = 1/1e4
}
v_init = -70
objref syn
syn = new AMPA5(0.5)
glu = 0 // dummy variable--will be synaptic cleft [glu] in mM
setpointer syn.C, glu
// user interface
load_file("rig.ses") // a RunControl panel, plot of soma.v(0.5) vs.t,
// and graphs of AMPA5's g, i, ica, and O
// simulation flow control AKA "protocol"
objref fih
fih = new FInitializeHandler("initglu()") // [glu] = 0 at start
GLUON = 0
proc initglu() {
GLUON = 0
glu = 0
cvode.event(START, "setglu()")
}
proc setglu() {
// print "time is ", t
if (GLUON==0) {
GLUON = 1
glu = CONC
cvode.event(t + DUR, "setglu()")
} else {
GLUON = 0
glu = 0
}
// we have changed a parameter abruptly
// so we really should re-initialize cvode
if (cvode.active()) {
cvode.re_init()
} else {
fcurrent()
}
}
It sets up a single compartment model with 100 um2 surface area, g_pas = 0.0001 S/cm2 (tau_m = 10 ms). This model uses a dummy variable called glu, whose value is controlled by a state machine implemented with NEURON's event delivery system. The FInitializeHandler ensures that glu = 0 at the start of each simulation, and also launches an event that causes execution of setglu() when t = START. This first execution of setglu() increases glu to a nonzero value and launches another event that causes a second execution of setglu(). The second execution of setglu() cuts glu back to 0. With a little revision, you could easily make this state machine generate a series of pulses of glu--as many as you like.
I'll email you the whole thing in a zip file so you can try it for yourself. Unzip it, run nrnivmodl (or mknrndll), then execute
nrngui init.hoc
or double click on init.hoc, and finally click on the Init & Run button.
At this point, my question is: are you going to have something that generates a detailed time course of glu, i.e. not just a rectangular pulse? Or is something like a rectangular pulse (or the response of a set of ODEs or reactions to an impulse, e.g. presynaptic spike) adequate? (in which case something implemented with a NET_RECEIVE block, rather than a POINTER, may be more suitable)