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 <../../nmodlconf.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) { info = newlist(); }
77  lappenditem(info, varname);
78  lappenditem(info, equal);
79  lappenditem(info, expr);
80  lappenditem(info, lastok);
81  }
82 }
83 
84 static void netrec_discon1(Item* varname, Item* equal, Item* expr, Item* lastok) {
85  Item* q;
86  Symbol* sym = SYM(varname);
87  char* cnexp = NULL;
88  int case_cnexp = 1;
89 #if 0
90  printf("NET_RECEIVE discontinuity for %s\n", sym->name);
91 #endif
92  if (netrec_cnexp) ITERATE(q, netrec_cnexp) {
93  if (SYM(q) == sym) {
94  cnexp = STR(q->next);
95  break;
96  }
97  q = q->next;
98  }
99 
100  if (cnexp) {
101  /* if there is a local variable involved then use the general case */
102  if (strstr(cnexp, " _l")) { /* interior begin token */
103  case_cnexp = 0;
104  }
105  }else{
106  /* if not processed by cnexp then use the general case */
107  case_cnexp = 0;
108  }
109 
110 #if 0
111  printf("cnexp is |%s|\n", cnexp ? cnexp : "NULL");
112  if (cnexp && !case_cnexp) {
113  printf("because of local variable, use the general case\n");
114  }else if (!cnexp) {
115  printf("because not cnexp, use the general case\n");
116  }
117  for (q = varname; q != lastok; q = q->next) {
118  debugprintitem(q);
119  }
120 #endif
121 
122  if (case_cnexp) {
123  char* state = items_as_string(varname, equal);
124  char* e = items_as_string(expr, lastok->prev);
125  sprintf(buf,
126 " if (nrn_netrec_state_adjust && !cvode_active_){\n"
127 " /* discon state adjustment for cnexp case (rate uses no local variable) */\n"
128 " double __state = %s;\n"
129 " double __primary = (%s) - __state;\n"
130 " %s;\n"
131 " %s += __primary;\n"
132 " } else {\n", state, e, cnexp, state);
133  insertstr(varname, buf);
134  insertstr(lastok, " }\n");
135  free(state);
136  free(e);
137  } else { /* the general case */
138  general_discon_adjust(varname, equal, expr, lastok);
139  }
140 }
141 
143  Item* q;
144  if (info) {
145  ITERATE(q, info) {
146  Item* varname = ITM(q); q = q->next;
147  Item* equal = ITM(q); q = q->next;
148  Item* expr = ITM(q); q = q->next;
149  Item* lastok = ITM(q)->next->next;
150  netrec_discon1(varname, equal, expr, lastok);
151  }
152  }
153 }
154 
155 static void general_discon_adjust(Item* varname, Item* equal, Item* expr, Item* lastok) {
156  int listnum = netrec_state_count%10;
157  int neq = netrec_state_count/10;
158  int i;
159  Symbol* sym = SYM(varname);
160  int sindex;
161  if (cvode_not_allowed) {
162  fprintf(stderr, "Notice: %s discontinuity adjustment not available.\n", sym->name);
163  return;
164  }
165  sindex = slist_search(listnum, sym);
166 
167 #if 0
168  printf("general_discon_adjust listnum=%d sindex=%d neq=%d\n", listnum, sindex, neq);
169 #endif
170 
171  char* state = items_as_string(varname, equal);
172  char* e = items_as_string(expr, lastok->prev);
173  char* needv;
174  char* needthread;
175  if (netrec_need_v) {
176  needv = strdup(" v = NODEV(_pnt->node);\n");
177  }else{
178  needv = strdup("");
179  }
180  if (netrec_need_thread && vectorize) {
181  needthread = strdup("#if NRN_VECTORIZED\n _thread = _nt->_ml_list[_mechtype]->_thread;\n#endif\n");
182  }else{
183  needthread = strdup("");
184  }
185  sprintf(buf,
186 " if (nrn_netrec_state_adjust && !cvode_active_){\n"
187 " /* discon state adjustment for general derivimplicit and KINETIC case */\n"
188 " int __i, __neq = %d;\n"
189 " double __state = %s;\n"
190 " double __primary_delta = (%s) - __state;\n"
191 " double __dtsav = dt;\n"
192 " for (__i = 0; __i < __neq; ++__i) {\n"
193 " _p[_dlist%d[__i]] = 0.0;\n"
194 " }\n"
195 " _p[_dlist%d[%d]] = __primary_delta;\n"
196 " dt *= 0.5;\n"
197 "%s%s"
198 " _ode_matsol_instance%d(_threadargs_);\n"
199 " dt = __dtsav;\n"
200 " for (__i = 0; __i < __neq; ++__i) {\n"
201 " _p[_slist%d[__i]] += _p[_dlist%d[__i]];\n"
202 " }\n"
203 " } else {\n", neq, state, e, listnum, listnum, sindex, needv, needthread, listnum, listnum, listnum);
204  insertstr(varname, buf);
205  insertstr(lastok, " }\n");
206  free(state);
207  free(e);
208  free(needv);
209  free(needthread);
210 }
211 
char * strstr(cs, ct) char *cs
short type
Definition: model.h:58
void debugprintitem(Item *)
Definition: noccout.cpp:544
int cvode_not_allowed
#define ITERATE(itm, lst)
Definition: model.h:25
#define SYM(q)
Definition: model.h:86
int netrec_need_thread
List * netrec_cnexp
Definition: deriv.cpp:20
Item * lastok
Definition: io.cpp:13
char * name
Definition: model.h:72
void netrec_asgn(Item *varname, Item *equal, Item *expr, Item *lastok)
struct Item * prev
Definition: model.h:20
struct Item * next
Definition: model.h:19
sprintf(buf," if (secondorder) {\ " int _i;\" " for(_i=0;_i< %d;++_i) {\" " _p[_slist%d[_i]]+=dt *_p[_dlist%d[_i]];\" " }}\", numeqn, listnum, listnum)
#define e
Definition: passive0.cpp:24
static List * info
char * items_as_string(Item *begin, Item *last)
Definition: noccout.cpp:558
Definition: model.h:15
int netrec_need_v
static void netrec_discon1(Item *varname, Item *equal, Item *expr, Item *lastok)
int slist_search(int listnum, Symbol *s)
#define printf
Definition: mwprefix.h:26
static void general_discon_adjust(Item *varname, Item *equal, Item *expr, Item *lastok)
int netrec_state_count
fprintf(stderr, "Don't know the location of params at %p\, pp)
Definition: model.h:57
List * newlist()
Definition: list.cpp:50
NMODL parser global flags / functions.
long subtype
Definition: model.h:59
Item * lappenditem(List *list, Item *item)
Definition: list.cpp:167
#define STR(q)
Definition: model.h:87
int vectorize
static int equal(char *s1, char *s2)
Definition: units.cpp:869
Item * insertstr(Item *item, char *str)
Definition: list.cpp:108
#define i
Definition: md1redef.h:12
char buf[512]
Definition: init.cpp:13
#define STAT
Definition: model.h:117
void netrec_discon()
size_t q
return NULL
Definition: cabcode.cpp:461
#define ITM(q)
Definition: model.h:88