nmodl
nmodl copied to clipboard
legacy units and rounding constants
I report a cvf issue here as it is useful to decide how to tackle this problem:
In hippocampus/mod/cagk.mod
exposes a new, related, possible missmatch between nmodl and mod2c (or neuron) behaviors.
In that file we find:
UNITS {
FARADAY = (faraday) (kilocoulombs)
R = 8.313424 (joule/degC)
}
Faraday is taken from the internal library while R is set as custom value. If we use legacy units, in mod2c (or neuron) we get in the .c file:
#define FARADAY _nrnunit_FARADAY[_nrnunit_use_legacy_]
static double _nrnunit_FARADAY[2] = {0x1.81f0fae775426p+6, 96.4853}; /* 96.4853321233100303 */
static double R = 8.313424;
Let's notice that Faraday (96.4853) has 6 digits while R (8.313424) has 7 digits. This is due to the fact that faraday is hard-coded as exactly that number (96.4853) in legacy units and R is kept as a string and copy-pasted in the .c file.
Meanwhile, in nmodl, doubles are stored as doubles and printed using this line (from codegen_c_visitor
):
std::string format_string = "static const double {} = {:g};";
In the fmt documentation, for g we get:
'g'
General format. For a given precision p >= 1, this rounds the number to p significant digits and then formats the result in either fixed-point format or in scientific notation, depending on its magnitude.
As last, note that the standard legacy faraday constant is taken from this value:
static const double FARADAY = 96.4853321233100184
Now, let's check what happens for FARADAY in nmodl:
- the full value is loaded from the library (96.4853321233100184)
-
static const double {} = {:g}
chops it to 6 digits (default value) - we get the correct legacy value of 96.4853
For the custom R the process is similar:
- the value is loaded from the mod file (8.313424)
- {quote}static const double {} = {:g}{quote} chops it to 6 digits (default value)
- we get the incorrect legacy value of 8.31342
Unfortunately, this means that we cannot simply tamper with fmt precision like:
std::string format_string = "static const double {} = {:.18g};";
because faraday and R require different precisions: either we chop too much R or we chop too little faraday.
Storing R as a string would help but we need to find a way to chop faraday if legacy units is on