arbor-sim / arbor

The Arbor multi-compartment neural network simulation library.
https://arbor-sim.org
BSD 3-Clause "New" or "Revised" License
108 stars 60 forks source link

modcc: ion concentration writers #1532

Closed noraabiakar closed 2 years ago

noraabiakar commented 3 years ago

Note: the internal concentration of "ca" is used here, but the issue applies for all ions and for both internal and external concentrations

How do nrnivmodl and modcc interpret this nmodl code?

NEURON {
    SUFFIX ca_write
    USEION ca READ ica WRITE cai VALENCE 2
}
ASSIGNED { ica }
STATE    { concentration cai }
INITIAL  { concentration = cai }

nrnivmodl

modcc

NEURON {
    SUFFIX ca_write
    USEION ca READ ica WRITE cai VALENCE 2
}
STATE    { concentration cai }
INITIAL  { concentration = cai }
BREAKPOINT {}
void init(mechanism_cpu_ca_write_pp_* pp) {
    int n_ = pp->width_;
    for (int i_ = 0; i_ < n_; ++i_) {
        pp->concentration[i_] = pp->cai[i_];
    }
}

Summary of modcc's behavior:

Expected behavior of modcc:

noraabiakar commented 3 years ago

modcc's current behavior has been designed to support common nmodl patterns:

If these are the only patterns we want to support, we need to properly document this and ideally also raise exceptions if users attempt to do something different.

thorstenhater commented 3 years ago

Points to make clear in the documentation (at least for me, these were confusing. If I got some of them still wrong, all the more reason to document them) (also sorry for repeating some from above)

Suggestions/Thoughts:

halfflat commented 3 years ago
  • If Xi is WRITE within the mechanism, this is added to the actual Xi
  • If Xi is READ however, we will give you the actual value
  • Multiple mechanisms might execute in arbitrary order and/or concurrently.

It's only added (with a weight factor) in order to handle partial CV coverage by a concentration-writing mechanism. We explicitly test to ensure that no two mechanisms writing to the same ionic concentration overlap (see fvm_layout.cpp lines 920-937).

This means that once we make it past the initialization phase, there should be no order-dependence issues in ion concentration writes.

Note that for each concentration, we compute in fvm_build_mechanism_data two values: an 'initial concentration', and a 'reset concentration'. In the integration loop, when it comes time to update the concentrations, we set it first to the 'initial' value, and then add the weighted contributions from the mechanisms. This initial value is the contribution from the concentration values painted on the cell (or from the defaults) in a CV, wherever there isn't some mechanism writing it. The reset value on the other hand is what the concentration would be without any mechanisms writing it all. This is used to set the CV concentration values before mechanism initialization (and after a call to reset()), so that mechanisms that try to read the concentration in their INITIAL block see something sensible.

  • Additive contributions should be zero-init'ed

This is what we're doing with the process outlined above.

halfflat commented 3 years ago

Just to clarify the semantics as implemented: for Arbor, a concentration-writing mechanism doesn't 'see' the effects of any other concentration-writing process, or averaging, etc. It is responsible purely for governing the evolution of the concentration as a function of ionic currents and any internal state it might hold.

Consequently, the only way the mechanism should be able to read that ionic concentration is at mechanism initialization time, and the value it should see at initialization is the 'reset concentration' described above.

thorstenhater commented 3 years ago

The request for zeroing these contributions was motivated by the idea that a user -- knowing it is an additive component -- might not explicitly initialise Xi and read from it later on in the mechanism. C++ people might be aware that anything can happen here, but that might not be true of scientists writing models.

halfflat commented 3 years ago

Inasmuch as Xi is a state variable that can be read in some contexts, it should be explicitly initialized to something useful (like the external Xi init value). Without checking the code — yes, I know this is a sin — I thought all our mechanism state variables were initialized to NaN before dealing with the INITIAL block, so we shouldn't be seeing uninitialized values in the state Xi, just NaN values.

The issue is, in part, NMODL. When we see Xi in a mechanism, depending on context it can mean the external ion concentration, a random state variable or parameter unrelated to an ion, or a state variable that represents an ion concentration, from which the external ion concentration is taken. If we want concentration writers to be able to read the default ion concentration value too, then Xi can mean both the state and the external in different spots within the same mechanism. It's just ugly.

But again, we should determine what semantics we want and proceed accordingly.

thorstenhater commented 2 years ago

As we have found out (and documented) over implementing the #1729 feature, this is part of the cable model, if a different model is required, one can use the diffusive concentration Xd, without actual diffusion as needed.