Using multiple SOLVE statements, KINETIC and DERIVATIVE

NMODL and the Channel Builder.
Post Reply
fred

Using multiple SOLVE statements, KINETIC and DERIVATIVE

Post by fred »

Hi,

Is there any reason I can't use multiple KINETIC and DERIVATIVE blocks in one mechanism? I'm getting an "Illegal SOLVE statement" error upon compile (nrnivmodl) that's giving me trouble.

Below is an example. Should this work in general? (dogcatcher and nain are KINETIC schemes, while pursesnatcher and juicyfruit are DERIVATIVE schemes)

I'm implementing a cardiac myocyte model using NEURON, and I find it simplest to keep several of the schemes separated within one ".mod" file. Has anyone tried this before, or should I bring all the schemes together so that I only use one SOLVE statement?

Thanks,
Fred

Code: Select all

BREAKPOINT {
    :SOLVE EVERYTHING
        SOLVE dogcatcher METHOD sparse
    :calc ca sequestering
        SOLVE pursesnatcher METHOD cnexp
        SOLVE juicyfruit METHOD cnexp
        SOLVE nain METHOD sparse

:...block has been truncated for viewability
}
ted
Site Admin
Posts: 6303
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Post by ted »

Yes. It won't work. To quote Michael Hines:
only one SOLVE statement per BREAKPOINT block
only one BREAKPOINT block per mod file
Suggestion:
For the sake of simplifying debugging and code maintenance, each voltage- or ligand-gated
current should have its own mod file. The hh mechanism may seem to be an exception to
this guideline, but it really isn't because it's just a self-contained implementation of the
Hodgkin-Huxley membrane action potential model.
fred

Post by fred »

For the sake of simplifying debugging and code maintenance, each voltage- or ligand-gated current should have its own mod file.
The issue I face is how to share sub-intracellular compartment information between mechanisms.

Intra and extracellular ionic concentrations are automated by USEION for intracellular and extracellular compartments, but I also need to keep track of subcellular compartment spaces. Combining mechanisms into one .mod file eliminates this problem, unfortunately, I have a mix of kinetic and derivative schemes, so multiple solve statements would be required.

Other options are Pointer Communication, Global values, and using USEION to pass variables. Am I missing a better way?

Pointers seem to be overkill, although they would work, I think. It's not clear to me whether Globals can represent range variables (from the NMODL2NEURON document). USEION appears to be the best way to go here, "conceptually strained" or not.

Another question about "USEION x WRITE ix"- can multiple mechanisms WRITE the same current?
ted
Site Admin
Posts: 6303
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Post by ted »

With any programming language there may be dozens (or thousands) of ways of
implementing a representation of any particular mechanism, but, as with natural
languages, only a small subset falls into the category of "fluent" or "idomatic" expression.
In order to give you specific advice, I will need to know some details about the
mechanism you need to implement. Any proprietary aspects of such communication
can be handled by direct email to
ted dot carnevale at yale dot edu
I have a mix of kinetic and derivative schemes, so multiple solve statements would be required.
Linear first order ODEs can be recast in a kinetic scheme formalism. Kinetic schemes
can also be recast as ODEs but that can be more involved. Alternatively there may be
a way to split a complex mechanism into subsystems.
Another question about "USEION x WRITE ix"- can multiple mechanisms WRITE the same current?
Yes.
fred

Post by fred »

This is the model: Action Potential of Mouse Ventricular Myocytes, by Bondarenko et. al. 2004

http://www.cellml.org/examples/reposito ... 4_doc.html

I_CaL and I_Na are markov models, and everything else is described by ODE's. My problem area is in the subcellular compartments, e.g. in the SS space where I_CaL depends on [Ca] in SS; Jxfer and Jrel are Ca2+ fluxes into the SS space.

My thought on how to proceed is to create new "abstract ions" using USEION, which will contain state variables for the subcellular compartments (Ca_ss, Ca_JSR, Ca_NSR, Ca_buffer). This way, each mechanism (channel, pump, buffer, flux) can be described in its own mod file, as you suggested.

The problem I see with this method is that only one mechanism is allowed to WRITE to the "abstract ions." I see no workaround to this problem because each current and flux needs to WRITE to [Ca] in the SS space.

Another idea is to convert all of the ODE's in the model to kinetic descriptions, and put them in one big ugly mod file.

Is there an idiomatic implementation here?

Thanks so much for your help.
ted
Site Admin
Posts: 6303
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Post by ted »

Looks like a case of egregious complexity to me, but who am I to judge?

For the moment let us assume that the model specification downloadable from
http://www.cellml.org is complete and correct (chances are slim to none for this being
the case with the specification in the article itself--there are just too many parameters and
equations with fancy notation to escape the inevitable typo).

Regardless of what simulation environment you use, the way to implement this model is
to start small and add complexities incrementally, testing at every step. It doesn't matter
how smart you are, how facile you may be with programming, or how hard you work--
frustration and debugging nightmares are guaranteed if you try to do it all in one pass.

Start by ignoring all the ion accumulation stuff. Do the basic spike currents first:
1. the fast ina
2. pick whichever k current seems most likely to terminate the spike
3. the L current
For the moment ignore complexities such as eq. A41 (ENa depending on {K] as well as
[Na]). You can add that later. At this point, just get the voltage-dependence of the gating
right. Do voltage clamp experiments on each voltage-gated channel and compare the
results with the figures in the paper. Fix any discrepancy you discover.

Add the other ionic currents one at a time. Give each one its own mod file. Make your
life easy by using economical nomenclature--e.g. there are a bunch of K currents, each
of which will have a descriptive suffix. So for IKs,

Code: Select all

NEURON {
  SUFFIX ks
  USEION k READ ek WRITE ik
  RANGE gbar, g, i
  . . .
BREAKPOINT {
  . . .
  g = gbar*etc.
  i = g*(v - ek)
  ik = i
}
lets you use gbar_ks, g_ks, i_ks instead of the slightly redundant gkbar_ks, gk_ks,ik_ks.

The authors assume specific membrane capacitance of 1 uf/cm2, so their conductance
densities in mS/uF are numerically equal to mS/cm2. You can use the same units for
gbar and g, but be sure to use modlunit to check for units consistency (if your currents
are in mA/cm2, it will tell you that you need to write i = (0.001)*g*(v-ek) ). Comparing
your current plots to theirs will require scaling. Figure 16 uses pA/pF, which is the same
as uA/cm2, i.e. numerically 10^3 times bigger than NEURON's default units for current
density (mA/cm2).

If there are Ca-dependent ion channels, you will need to add them before implementing
a Ca accumulation mechanism. Try to figure out what the "mean" cai and cao are
supposed to be, and assign the values to cao_ca_ion and cai_ca_ion in hoc. Just
remember that eventually you will have to eliminate these assignments (well, at least
the assignment of cai_ca_ion).

Once you have the ionic currents working, it will be time to add ion accumulation.
Electrically, this is a single compartment model, so you'll be using a section with
nseg = 1 and must choose an L and diam to give the surface area and "myoplasmic
volume" specified in table 2.

Add Na and K accumulation first. This can be done with a single mod file that
READs ina and ik, and WRITEs nai and ki (I assume that there is no extracellular
ion accumulation). Then add an Na-K pump. This will be in another mod file that READs
nai and ki (maybe also nao and ko) and WRITEs ina and ik (the pump's fluxes).

Now it makes sense to incorporate the mixed ionic dependence of the driving force for
ina. To that mod file add

Code: Select all

FUNCTION enax() (/mv) {
  enax = (R*(celsius+273.15)/FARADAY) * log((0.9*nao + 0.1*ko)/(0.9*nak + 0.1*ki))
}
(I may have omitted a conversion factor here--check with modlunit!),
and in the BREAKPOINT block change

Code: Select all

  i = g*(v - ena)
to

Code: Select all

:  i = g*(v - ena)
  i = g*(v - enax())
Don't forget that this mechanism must now READ nai, nao, ki, and ko, and that you must
be sure to propertly initialize all of these concentrations.

Finally begin to work on Ca accumulation. The first thing to do here is to take care of
ordinary buffering and the Ca pump. You'll have to read the paper to figure out what
all those arrows and boxes mean, and determine what is the basic buffering equation.
Then use one of the ca*mod files that NEURON comes with as a starting point, and
change it accordingly (under MSWin they're in c/nrn58/examples/nrniv/nmodl/ , and on
my Linux box they're in /usr/local/src/nrn-5.9/share/examples/nrniv/nmodl/ ). Be sure
to test the pump and buffering to verify that it works the same as the one described in
the paper. This mechanism will WRITE cai.

Then add the NaCa exchange in its own mod file. This will READ cai and WRITE ina and
ica (the exchanger's fluxes).

Finally tackle the sarcoplasmic reticulum. This can be done in part by elaborating the
basic Ca accumulation mechanism. However, it appears from Fig 1 that icaL enters a
confined region where it accumulates as "Ca_ss" and is subject to exchange with free
Ca in the cytosol and the JSR. Is this interpretation correct?
fred

Post by fred »

First of all- Wow. Thanks a ton for looking into this. I think your roadmap will prove very helpful.
... t appears from Fig 1 that icaL enters a confined region where it accumulates as "Ca_ss" and is subject to exchange with free Ca in the cytosol and the JSR. Is this interpretation correct?


You're correct. "Ca_ss" is a local accumulation that has a gating function on I_CaL, the L-type Ca current. This appears to be a complicated situation in NEURON, but I think I have a simple solution to value passing:

In the originating mod file:

Code: Select all

USEION dummy WRITE idummy, dummyi VALENCE 0
And in the receiving mod file:

Code: Select all

USEION dummy READ idummy, dummyi
This should prevent any currents from causing a shift in membrane potential, while allowing arbitrary values to be passed between mod codes, I think.
Looks like a case of egregious complexity to me, but who am I to judge?
Anyhow, I've been working on this aspect of the model- doing the hard part first- but your intro has convinced me to start at the beginning. Chances are that, when I get there, I'll be back to ask for "pointers" (haha). Thanks again, Ted.

-fred
ted
Site Admin
Posts: 6303
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Post by ted »

Those suggestions are based on hard-won experience.

I should have mentioned that it would make sense to implement any "leak" (constant
conductance) currents at the very start, especially anything that is likely to govern
"resting" potential (the complete model probably fires spontaneously, hence the quotes).

You should probably think of the overall implementation as really being a series of
implementations of different models. Every time you add a new mechanism, that's a
new model. It might be helpful to give each model a descriptive name, or at least a
name that lets you discover what mechanisms it contains.

Here's a good way to debug as you go along, and also to gain a deeper understanding
of how each mechanism contributes to the operation of the whole thing:
Every time you add a new mechanism, make your best guess as to how the firing
properties of the model will be affected. Then test your hypotheses by running a couple
of simulations to check for spontaneous firing, and to evaluate excitability by injecting a
current pulse.

Initialization will be a problem, especially after you have added ion accumulation
mechanisms. For models that lack ion accumulation mechanisms, first try the basic
initialization style offered by the RunControl panel, i.e. in hoc,

Code: Select all

v_init = whatever you think it should start at
stdinit()
You may find it useful to initialize to a specific _resting_ potential by adding a constant
current source whose magnitude is automatically adjusted to balance out the ionic
currents--see pages 195-197 in The NEURON Book. After you add ion accumulation
mechanisms, you will find it helpful to employ the strategies described in chapter 8 to
ensure correct concentrations.
fred wrote:"Ca_ss" is a local accumulation that has a gating function on I_CaL, the L-type Ca current.
Does it also affect the driving force for calcium entry, or does ECa depend on the
myoplasmic free Ca instead?
In the originating mod file:

Code: Select all

USEION dummy WRITE idummy, dummyi VALENCE 0
Won't work. VALENCE must be nonzero. The best strategy depends on the answer
to my question about ECa.

I am still concerned about this:
For the moment let us assume that the model specification downloadable from
http://www.cellml.org is complete and correct (chances are slim to none for this being
the case with the specification in the article itself--there are just too many parameters and
equations with fancy notation to escape the inevitable typo).
Are you working from the article, or from something you picked up from CellML's WWW
site? Model entries at CellML are supposed to be curated in the sense that there is a
notation that in effect says whether or not the model entry is an accurate replica of
what the authors used (i.e. whether it reproduces the published results). If you are
using something from CellML's WWW site, what do they have to say about it?
fred

Post by fred »

Are you working from the article, or from something you picked up from CellML's WWW site?
I'm working from the paper- Computer model of action potential of mouse ventricular myocytes- Bonderenko et al, Amer J Physiol, 2004. I believe the model is available in either C or Matlab from the ModelDB site, though I haven't downloaded and tested those files.
Initialization will be a problem
The paper has a complete set of initialization values for the complete model, i.e. membrane voltage, kinetic states of channels, inactivation parameters, etc. I'll look into implementing a current source to offset leakage currents in my intermediate (incomplete) models. For now, I can initialize to the given value, -84 mV, because I have no leakage currents inserted. I've implemented the fastNa channel this way, and V-clamp protocols are giving mostly good results.
Does it also affect the driving force for calcium entry, or does ECa depend on the myoplasmic free Ca instead?
ECa for this channel (E_CaL) is a constant parameter in this formulation, however, inactivation of I_CaL is linked to Ca in the ss compartment (Ca_ss). With this information, does a strategy jump out at you?

Thanks again- Fred
ted
Site Admin
Posts: 6303
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Post by ted »

AFAIK it's not in ModelDB. If you know otherwise, please correct me. If not, then it would
be a very good idea to get source code from the CellML site, or from the authors.
Published articles are as a rule incomplete or afflicted by typographical errors, even in
the case of simple models.
The paper has a complete set of initialization values for the complete model, i.e. membrane voltage, kinetic states of channels, inactivation parameters, etc.
Those values may be "correct" for the complete model* (assuming no typos), but they
won't be for the partial models that you create in the course of developing your own
implementation.
*--the authors do note that they let it run for a while before doing anything
Does it also affect the driving force for calcium entry, or does ECa depend on the myoplasmic free Ca instead?
ECa for this channel (E_CaL) is a constant parameter in this formulation,
I see. The "calcium background" current depends on eca, but the L current assumes a
fixed equilibrium potential. Seems odd, but it makes your job easier.

When first developing the L current mechanism, ignore the subcompartmentalization of
Ca. Just do

Code: Select all

NEURON
  SUFFIX caL
  USEION ca READ cai WRITE ica VALENCE 2
  RANGE gbar, g, i, es
  . . .
PARAMETER {
  gbar = . . .
  es = whatever : s for "static" i.e. unchanging, a reminder to you and others
    : that it isn't automatically computed from the Nernst equation
  . . .
ASSIGNED {
  v (mV)
  g . . .
  i . . .
  ica . . .
  cai . . .
}
BREAKPOINT {
  . . .
  g = gbar * . . .
  i = g * (v - es)
  ica = i
}
When it finally comes time to take the subcompartmentalization of Ca into account,
change the USEION statement to
USEION caL READ caLi WRITE icaL VALENCE 2
and make the corresponding changes to the ASSIGNED and BREAKPOINT blocks

Code: Select all

ASSIGNED {
  . . .
  i . . .
  icaL . . .
  caLi . . .
. . .
BREAKPOINT {
  . . .
  i = g * (v - es)
  icaL = i
}
The first implementations of your Ca accumulation mechanism will ignore all aspects of
subcompartmentalization. No Ca_ss, no SR or NSR. All calcium currents are dumped
into a single compartment
~ cai << (sum of all ca currents)*PI*diam/(2*FARADAY)

Add subcompartmentalization one step at a time. The first step is to add the effect of [Ca]ss; later steps add the SR and NSR.

The first step actually starts when you make the caL mechanism USEION caL. This
requires you add a new USEION statement to the Ca accumulation mechanism.
USEION caL READ icaL WRITE caLi VALENCE 2
caLi, which must be declared in the STATE block, corresponds to the [Ca]ss of B. et al..
If you want something that you can refer to as cass, you might add an ASSIGNED
variable called cass and at the end of the BREAKPOINT block have the statement
cass = caLi.

Ignoring the effect of the SR and NSR, the only changes to the KINETIC block are

Code: Select all

: without subcompartmentalization
: ~ cai << (icaL + sum of non-caL currents)*PI*diam/(2*FARADAY)
: with subcompartmentalization
~ cai << (sum of non-caL currents)*PI*diam/(2*FARADAY)
~ caLi << icaL*PI*diam/(2*FARADAY)
~ caLi <-> cai
That last statement will need appropriate rates.

By now you're probably discerning a pattern that suggests how to incorporate the SR
and NSR--when the time comes, and in an incremental manner, testing at every step.
fred

Post by fred »

I can see this taking shape now.

The entire intracellular scheme will, in the completed model, be described in one .mod file as a rather complicated kinetic scheme. In this scheme, each compartment is defined by 1 or 2 lines of code in the kinetic block: 1) direct definitions of fluxes between compartments and 2) buffering reactions within that compartment (when applicable). This should jump out from the model diagram (all those boxes and arrows). http://www.cellml.org/examples/reposito ... 4_doc.html

The single mod file is necessary because passing intracellular compartment concentrations is difficult in NEURON, and it's compact because the kinetic block is such.

But will this work? I think it should, but I'd like your opinion.

"J" variables are fluxes, which will be calculated in a procedure block, called at the top of the kinetic block.

Code: Select all

~ cai << -(sum of outward non-caL, currents)*PI*diam/(2*FARADAY) +Jleak
~ cai <-> buffer_cai      //this takes Jtrpn into account
~ caLi << -icaL*PI*diam/(2*FARADAY) + Jrel
~ caLi <-> cai      //Jxfer
~ caJSR << Jtr - Jrel
~ caJSR <-> Calsequestrin  //buffering within the JSR
~ caNSR << Jup - Jleak - Jtr
Clearly, I need to move one step at a time, but I'm trying to keep the end in sight..
ted
Site Admin
Posts: 6303
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Cicero said it.

Post by ted »

The entire intracellular scheme
I'd prefer to call it the ca accumulation mechanism, but you're starting to get the idea.

The key to getting it right is to take it one step at a time. As Cicero wrote, "nihil est enim
simul et inventum et perfectum" ("nothing is invented and perfected at the same time").

First do the myoplasmic pool, with its intrinsic buffering.
When that's working properly, add the caLi subcompartment, with its exchange with the
myoplasmic pool.
After that is working, add the part of the SR that communicates with the caLi
subcompartment.
Once you have that correct, add the other part of the SR that communicates with the first
part and with the myoplasmic pool.

Every time you add a new elaboration, test it. Make your best guess about what effect it
should have on the various intracellular concentrations, and do a simulation designed to
test that guess. If the simulation agrees with your guess, fine. If it doesn't, try to decide
whether the disagreement means that you just didn't have the correct intuition about
what would happen, or whether it means that your implementation is incorrect.

This sounds tedious and it is. But it's the only way to ensure a close match between the
conceptual model (the equations) and the computational implementation. It will help you
identify and fix errors of implementation much more quickly than if you just threw
everything into a big bag all at once, and then had to wrestle with the mess that is
guaranteed to happen. Believe me, the methodical approach will save you time in the
long run.
Post Reply