NEURON
netrec_discon.cpp
Go to the documentation of this file.
1 /*
2 
3 NET_RECEIVE discontinuities for STATE variables are correct for the
4 variable time step method but, for better numerical accuracy, should be
5 adjusted for the fixed step method. That is, for secondorder=2, STATE
6 variable discontinuites should be calculated as the change in state
7 at t+dt/2 instead of the full weight discontinuity at t. For the implicit
8 method secondorder=0, the change in state should also be calculated at
9 t+dt/2. For the vast majority of cases, DERIVATIVE block with METHOD cnexp,
10 the performance cost is minimal and the
11 adjustment factor is just fac = 1 / (1 + rate*dt/2) where
12 rate = ds'/ds. Note that if the choice of cnexp is valid, s'
13 depends only on s and is linear. dt is just replaced
14 by dt/2. Problems with this in general are of 3 kinds.
15 1) If all parameters of the equations are constant, best performance
16 would be obtained by precalculating the factors. It is possible to
17 generalize this precalculation to linear coupled DERIVATIVE and KINETIC
18 equations. However, precalculation is not valid if the parameters change
19 during a simulation (e.g. voltage sensitive rates are common, though
20 not in synapse models).
21 2) Sometimes in DERIVATIVE blocks, state independent terms involving
22 functions or several factors use local variable assignment to simplify
23 the cnexp formula. Of course, the local value calculation that provides
24 the value of rate, is not available in the NET_RECEIVE block. Here again,
25 one can imagine voltage sensitive rate functions and also some equations
26 written in linearized uncoupled form so that cnexp can be used.
27 3) Coupled and or nonlinear equations should properly use a jacobian
28 formulation since a discontinuity of a single state at t may distribute
29 among several states when evaluated at t+dt.
30 
31 At present we do not take advantage of precalculation of factors.
32 
33 The standard case is cnexp with rate computable within the NET_RECEIVE block.
34 I.e rate involves only RANGE variables and no LOCAL variables. Note that
35 rate is already calculated in solve.c and is given in the cnexp_rate list
36 as a string. That has to be parsed to check that it satisfies our
37 requirements for computability in the NET_RECEIVE block
38 Although best performance for the cnexp case is comes from the rate factor,
39 cnexp in general allows s' = a + b*s which requires a different factor
40 if a is nonzero or b is zero. For simplicity, we use the full exponetial
41 expression to evaluate the discontinuity of s(t) at t+dt/2.
42 
43 For the general case, i.e. the standard case is insufficient, we evaluate
44 the dstate/dt vector for DERIVATIVE and KINETIC blocks (ie. via a call
45 to the model instance cvode specfic _ode_spec1 function)
46 at the current state and with a small change to the discontinuous state
47 and use ds'/ds to calculate the relevant change to the several states.
48 The typical case here is A -> B -> 0 where A is discontinuous on
49 receipt of an event. Note that no Jacobian inversion is envisioned so
50 that only dsi/dt that is affected by sj will change si.
51 
52 */
53 
54 #include <../../nrnconf.h>
55 
56 #include <stdlib.h>
57 #include <string.h>
58 #include "modl.h"
59 #include "parse1.hpp"
60 
61 extern int vectorize;
62 extern int cvode_not_allowed;
63 extern List* netrec_cnexp; /* STATE symbol and cnexp expr pairs */
64 static List* info;
65 static void general_discon_adjust(Item* varname, Item* equal, Item* expr, Item* lastok);
66 int netrec_state_count; /* 10*numeqn + listnum */
67 int netrec_need_v; /* if 1 then need it for the general case */
68 int netrec_need_thread; /* if 1 then _cvode_sparse_thread needs proper _thread */
69 
70 /* store statement info */
71 void netrec_asgn(Item* varname, Item* equal, Item* expr, Item* lastok) {
72  Symbol* sym;
73  sym = SYM(varname);
74  if (sym->type == NAME && sym->subtype & STAT) {
75  /* STATE discontinuity adjusted if fixed step method */
76  if (!info) {
77  info = newlist();
78  }
79  lappenditem(info, varname);
81  lappenditem(info, expr);
83  }
84 }
85 
86 static void netrec_discon1(Item* varname, Item* equal, Item* expr, Item* lastok) {
87  Item* q;
88  Symbol* sym = SYM(varname);
89  char* cnexp = NULL;
90  int case_cnexp = 1;
91 #if 0
92  printf("NET_RECEIVE discontinuity for %s\n", sym->name);
93 #endif
94  if (netrec_cnexp)
96  if (SYM(q) == sym) {
97  cnexp = STR(q->next);
98  break;
99  }
100  q = q->next;
101  }
102 
103  if (cnexp) {
104  /* if there is a local variable involved then use the general case */
105  if (strstr(cnexp, " _l")) { /* interior begin token */
106  case_cnexp = 0;
107  }
108  } else {
109  /* if not processed by cnexp then use the general case */
110  case_cnexp = 0;
111  }
112 
113 #if 0
114  printf("cnexp is |%s|\n", cnexp ? cnexp : "NULL");
115  if (cnexp && !case_cnexp) {
116  printf("because of local variable, use the general case\n");
117  }else if (!cnexp) {
118  printf("because not cnexp, use the general case\n");
119  }
120  for (q = varname; q != lastok; q = q->next) {
121  debugprintitem(q);
122  }
123 #endif
124 
125  if (case_cnexp) {
126  char* state = items_as_string(varname, equal);
127  char* e = items_as_string(expr, lastok->prev);
128  Sprintf(buf,
129  " if (nrn_netrec_state_adjust && !cvode_active_){\n"
130  " /* discon state adjustment for cnexp case (rate uses no local variable) */\n"
131  " double __state = %s;\n"
132  " double __primary = (%s) - __state;\n"
133  " %s;\n"
134  " %s += __primary;\n"
135  " } else {\n",
136  state,
137  e,
138  cnexp,
139  state);
140  insertstr(varname, buf);
141  insertstr(lastok, " }\n");
142  free(state);
143  free(e);
144  } else { /* the general case */
145  general_discon_adjust(varname, equal, expr, lastok);
146  }
147 }
148 
150  Item* q;
151  if (info) {
152  ITERATE(q, info) {
153  Item* varname = ITM(q);
154  q = q->next;
155  Item* equal = ITM(q);
156  q = q->next;
157  Item* expr = ITM(q);
158  q = q->next;
159  Item* lastok = ITM(q)->next->next;
160  netrec_discon1(varname, equal, expr, lastok);
161  }
162  }
163 }
164 
165 static void general_discon_adjust(Item* varname, Item* equal, Item* expr, Item* lastok) {
166  int listnum = netrec_state_count % 10;
167  int neq = netrec_state_count / 10;
168  Symbol* sym = SYM(varname);
169  int sindex;
170  if (cvode_not_allowed) {
171  fprintf(stderr, "Notice: %s discontinuity adjustment not available.\n", sym->name);
172  return;
173  }
174  sindex = slist_search(listnum, sym);
175 
176 #if 0
177  printf("general_discon_adjust listnum=%d sindex=%d neq=%d\n", listnum, sindex, neq);
178 #endif
179 
180  char* state = items_as_string(varname, equal);
181  char* e = items_as_string(expr, lastok->prev);
182  char* needv;
183  char* needthread;
184  if (netrec_need_v) {
185  needv = strdup(" v = NODEV(_pnt->node);\n");
186  } else {
187  needv = strdup("");
188  }
189  if (netrec_need_thread && vectorize) {
190  needthread = strdup(
191  "#if NRN_VECTORIZED\n _thread = _nt->_ml_list[_mechtype]->_thread;\n#endif\n");
192  } else {
193  needthread = strdup("");
194  }
195  Sprintf(buf,
196  " if (nrn_netrec_state_adjust && !cvode_active_){\n"
197  " /* discon state adjustment for general derivimplicit and KINETIC case */\n"
198  " int __i, __neq = %d;\n"
199  " double __state = %s;\n"
200  " double __primary_delta = (%s) - __state;\n"
201  " double __dtsav = dt;\n"
202  " for (__i = 0; __i < __neq; ++__i) {\n"
203  " _ml->data(_iml, _dlist%d[__i]) = 0.0;\n"
204  " }\n"
205  " _ml->data(_iml, _dlist%d[%d]) = __primary_delta;\n"
206  " dt *= 0.5;\n"
207  "%s%s"
208  " _ode_matsol_instance%d(_threadargs_);\n"
209  " dt = __dtsav;\n"
210  " for (__i = 0; __i < __neq; ++__i) {\n"
211  " _ml->data(_iml, _slist%d[__i]) += _ml->data(_iml, _dlist%d[__i]);\n"
212  " }\n"
213  " } else {\n",
214  neq,
215  state,
216  e,
217  listnum,
218  listnum,
219  sindex,
220  needv,
221  needthread,
222  listnum,
223  listnum,
224  listnum);
225  insertstr(varname, buf);
226  insertstr(lastok, " }\n");
227  free(state);
228  free(e);
229  free(needv);
230  free(needthread);
231 }
char buf[512]
Definition: init.cpp:13
#define STR(q)
Definition: model.h:76
#define STAT
Definition: model.h:106
#define ITERATE(itm, lst)
Definition: model.h:18
#define ITM(q)
Definition: model.h:77
#define SYM(q)
Definition: model.h:75
printf
Definition: extdef.h:5
Item * lastok
Definition: io.cpp:11
Item * lappenditem(List *list, Item *item)
Definition: list.cpp:147
Item * insertstr(Item *item, const char *str)
Definition: list.cpp:99
List * newlist()
The following routines support the concept of a list.
int Sprintf(char(&buf)[N], const char *fmt, Args &&... args)
Redirect sprintf to snprintf if the buffer size can be deduced.
Definition: wrap_sprintf.h:14
void netrec_discon()
static List * info
int vectorize
Definition: nocpout.cpp:78
static void general_discon_adjust(Item *varname, Item *equal, Item *expr, Item *lastok)
static void netrec_discon1(Item *varname, Item *equal, Item *expr, Item *lastok)
int netrec_need_v
int cvode_not_allowed
Definition: nocpout.cpp:161
void netrec_asgn(Item *varname, Item *equal, Item *expr, Item *lastok)
int netrec_state_count
List * netrec_cnexp
Definition: deriv.cpp:20
int netrec_need_thread
int slist_search(int listnum, Symbol *s)
Definition: nocpout.cpp:2711
char * items_as_string(Item *begin, Item *last)
Definition: noccout.cpp:403
void debugprintitem(Item *)
Definition: noccout.cpp:389
NMODL parser global flags / functions.
size_t q
#define NULL
Definition: spdefs.h:105
static int equal(const char *c1, const char *c2)
Definition: units.cpp:776
Definition: model.h:8
struct Item * prev
Definition: model.h:13
Definition: model.h:47
short type
Definition: model.h:48
long subtype
Definition: model.h:49
char * name
Definition: model.h:61