NEURON
solve.cpp
Go to the documentation of this file.
1 #include <../../nmodlconf.h>
2 /* /local/src/master/nrn/src/nmodl/solve.c,v 4.4 1998/08/20 21:07:34 hines Exp */
3 
4 #include "modl.h"
5 #include "parse1.hpp"
6 #include "symbol.h"
7 
8 /* make it an error if 2 solve statements are called on a single call to
9 model() */
10 extern List *indeplist;
12 extern Symbol *indepsym;
13 extern char* current_line();
14 extern List *massage_list_;
15 #if NMODL
16 extern List *nrnstate;
17 #if VECTORIZE
18 extern int vectorize;
19 extern char* cray_pragma();
20 extern int netrec_state_count;
21 extern int netrec_need_thread;
22 #endif
23 #endif
24 #if CVODE
25 Item* cvode_cnexp_solve;
26 Symbol* cvode_nrn_cur_solve_;
27 Symbol* cvode_nrn_current_solve_;
28 #endif
29 
30 void whileloop(Item*, long, int);
31 void check_ss_consist(Item*);
32 
33 /* Need list of solve statements. We impress the
34 general list structure to handle it. The element is a pointer to an
35 item which is the first item in the statement sequence in another list.
36 */
37 static List *solvq; /* list of the solve statement locations */
38 int numlist = 0; /* number of slist's */
39 
40 void solvequeue(Item* q1, Item* q2, int blocktype, Item* qerr) /*solve NAME=q1 [using METHOD=q2]*/
41  /* q2 = 0 means method wasn't there */
42  /* qerr in ITEM0 or else the closing
43  brace of an IFERROR stmt */
44 {
45  /* the solvq list is organized in groups of an item element
46  followed by the method symbol( null if default to be used) */
47  /* The itemtype field of the first is used to carry the blocktype*/
48  /* SOLVE and METHOD method are deleted */
49  /* The list now consists of triples in which the third element
50  is a list containing the complete IFERROR statement.
51  */
52  Item *lq, *qtemp;
53  List *errstmt;
54 
55  if (!solvq) {
56  solvq = newlist();
57  }
58 #if NMODL
59  /* if the blocktype is equation then move the solve statement to
60  the nrnstate function. Everything else stays in the
61  model function to be used as the nrncurrent function */
62  if (blocktype == BREAKPOINT) {
63  if (!nrnstate) {
64  nrnstate = newlist();
65  }
66  Lappendstr(nrnstate, "{");
67  if (qerr) {
68  movelist(q1->prev, qerr, nrnstate);
69  }else if (q2) {
70  movelist(q1->prev, q2, nrnstate);
71  }else{
72  movelist(q1->prev, q1, nrnstate);
73  }
74  Lappendstr(nrnstate, "}");
75  }
76  /* verify that the block defintion for this SOLVE has not yet been seen */
77  if (massage_list_) ITERATE(lq, massage_list_) {
78  if (strcmp(SYM(lq)->name, SYM(q1)->name) == 0) {
79 diag("The SOLVE statement must be before the DERIVATIVE block for ", SYM(lq)->name);
80  }
81  }
82 #endif
83  lq = lappendsym(solvq, SYM0);
84  ITM(lq) = q1;
85  lq->itemtype = blocktype;
86  /* handle STEADYSTATE option */
87  if (q1->next->itemtype == SYMBOL &&
88  strcmp("STEADYSTATE", SYM(q1->next)->name) == 0) {
89  lq->itemtype = -blocktype; /* gets put back below */
90  }
91  if (q2) {
92  qtemp = q2->next; /* The IFERROR location */
93  Lappendsym(solvq, SYM(q2));
94  if (strcmp(SYM(q2)->name, "derivimplicit") == 0) {
96  }
97  if (strcmp(SYM(q2)->name, "cnexp") == 0) {
98  SYM(q2)->name = stralloc("derivimplicit", SYM(q2)->name);
100 #if CVODE
101  cvode_cnexp_solve = lq;
102 #endif
103  }
104  remove(q2->prev);
105  remove(q2);
106  }else{
107  qtemp = q1->next;
108  Lappendsym(solvq, SYM0);
109  }
110  remove(q1->prev);
111 
112  /* handle the error statement */
113  /* put one in if it isn't already there */
114  if (qerr == ITEM0) {
115 #if NOCMODL
116 sprintf(buf, "if(error){fprintf(stderr,\"%s\\n\"); nrn_complain(_p); abort_run(error);}\n",
117 current_line());
118  qtemp = qerr = insertstr(qtemp, buf);
119 #else
120  qtemp = qerr = insertstr(qtemp,
121 "if(error){abort_run(error);}\n");
122 #endif
123  }else{
124  replacstr(qtemp, "if (error)");
125  }
126  errstmt = newlist();
127  lq = lappendsym(solvq, SYM0);
128  LST(lq) = errstmt;
129  movelist(qtemp, qerr, errstmt);
130 }
131 
132 /* go through the solvq list and construct the proper while loop and calls*/
134 {
135  Item *lq, *qsol, *follow;
136  List *errstmt;
137  Symbol *fun, *method;
138  int numeqn, listnum, btype, steadystate;
139  int cvodemethod_;
140 
141  if (!solvq)
142  solvq = newlist();
143  init_disc_vars(); /*why not here?*/
144  ITERATE(lq, solvq) { /* remember it goes by 3's */
145  steadystate=0;
146  btype = lq->itemtype;
147  if (btype < 0) {
148  btype = lq->itemtype = -btype;
149  steadystate = 1;
150  check_ss_consist(lq);
151  }
152  qsol = ITM(lq);
153  lq = lq->next;
154  method = SYM(lq);
155 #if CVODE
156  cvodemethod_ = 0;
157  if (method && strcmp(method->name, "after_cvode") == 0) {
158  method = (Symbol*)0;
159  lq->element.sym = (Symbol*)0;
160  cvodemethod_ = 1;
161  }
162  if (method && strcmp(method->name, "cvode_t") == 0) {
163  method = (Symbol*)0;
164  lq->element.sym = (Symbol*)0;
165  cvodemethod_ = 2;
166  }
167  if (method && strcmp(method->name, "cvode_v") == 0) {
168  method = (Symbol*)0;
169  lq->element.sym = (Symbol*)0;
170  cvodemethod_ = 3;
171  }
172 #endif
173  lq = lq->next;
174  errstmt = LST(lq);
175  /* err stmt handling assumes qsol->next is where it goes. */
176  fun = SYM(qsol);
177  numeqn = fun->used;
178  listnum =fun->u.i;
179  if (btype == BREAKPOINT && (fun->subtype == DERF || fun->subtype == KINF)) {
180  netrec_state_count = numeqn*10 + listnum;
181  netrec_need_thread = 1;
182  }
183  follow = qsol->next; /* where p[0] gets updated */
184  /* Check consistency of method and block type */
185  if (method && !(method->subtype & fun->subtype)) {
186  Sprintf(buf, "Method %s can't be used with Block %s",
187  method->name, fun->name);
188  diag(buf, (char *)0);
189  }
190 
191  switch (fun->subtype) {
192 
193  case DERF:
194  if (method == SYM0) {
195  method = lookup("adrunge");
196  }
197  if (btype == BREAKPOINT && !steadystate) {
198  /* derivatives recalculated after while loop */
199 if (strcmp(method->name, "cnexp") != 0 && strcmp(method->name, "derivimplicit") != 0 && strcmp(method->name, "euler") != 0) {
200 fprintf(stderr, "Notice: %s is not thread safe. Complain to Hines\n", method->name);
201  vectorize = 0;
202  Sprintf(buf, " %s();\n", fun->name);
203  Insertstr(follow, buf);
204  }
205  /* envelope calls go after the while loop */
206  sens_nonlin_out(follow, fun);
207 #if CVODE
208  cvode_interface(fun, listnum, numeqn);
209 #endif
210  }
211  if (btype == BREAKPOINT) whileloop(qsol, (long)DERF, steadystate);
212  solv_diffeq(qsol, fun, method, numeqn, listnum, steadystate, btype);
213  break;
214  case KINF:
215  if (method == SYM0) {
216  method = lookup("_advance");
217  }
218  if (btype == BREAKPOINT && (method->subtype & DERF)) {
219 #if VECTORIZE
220 fprintf(stderr, "Notice: KINETIC is thread safe only with METHOD sparse. Complain to Hines\n");
221  vectorize = 0;
222 #endif
223  /* derivatives recalculated after while loop */
224  Sprintf(buf, " %s();\n", fun->name);
225  Insertstr(follow, buf);
226  /* envelope calls go after the while loop */
227  sens_nonlin_out(follow, fun);
228  }
229  if (btype == BREAKPOINT) {
230  whileloop(qsol, (long)DERF, steadystate);
231 #if CVODE
232  if (strcmp(method->name, "sparse") == 0) {
233  cvode_interface(fun, listnum, numeqn);
234  cvode_kinetic(qsol, fun, numeqn, listnum);
235  single_channel(qsol, fun, numeqn, listnum);
236  }
237 #endif
238  }
239  solv_diffeq(qsol, fun, method, numeqn, listnum, steadystate, btype);
240  break;
241  case NLINF:
242 #if VECTORIZE
243 fprintf(stderr, "Notice: NONLINEAR is not thread safe.\n");
244  vectorize = 0;
245 #endif
246  if (method == SYM0) {
247  method = lookup("newton");
248  }
249  solv_nonlin(qsol, fun, method, numeqn, listnum);
250  break;
251  case LINF:
252 #if VECTORIZE
253 fprintf(stderr, "Notice: LINEAR is not thread safe.\n");
254  vectorize = 0;
255 #endif
256  if (method == SYM0) {
257  method = lookup("simeq");
258  }
259  solv_lineq(qsol, fun, method, numeqn, listnum);
260  break;
261  case DISCF:
262 #if VECTORIZE
263 fprintf(stderr, "Notice: DISCRETE is not thread safe.\n");
264  vectorize = 0;
265 #endif
266  if (btype == BREAKPOINT) whileloop(qsol, (long)DISCRETE, 0);
267  Sprintf(buf, "0; %s += d%s; %s();\n",
268  indepsym->name, indepsym->name, fun->name);
269  replacstr(qsol, buf);
270  break;
271 #if 1 /* really wanted? */
272  case PROCED:
273  if (btype == BREAKPOINT) {
274  whileloop(qsol, (long)DERF, 0);
275 #if CVODE
276  if (cvodemethod_ == 1) { /*after_cvode*/
277  cvode_interface(fun, listnum, 0);
278  }
279  if (cvodemethod_ == 2) { /*cvode_t*/
280  cvode_interface(fun, listnum, 0);
281  insertstr(qsol, "if (!cvode_active_)");
282  cvode_nrn_cur_solve_ = fun;
283  linsertstr(procfunc, "extern int cvode_active_;\n");
284  }
285  if (cvodemethod_ == 3) { /*cvode_t_v*/
286  cvode_interface(fun, listnum, 0);
287  insertstr(qsol, "if (!cvode_active_)");
288  cvode_nrn_current_solve_ = fun;
289  linsertstr(procfunc, "extern int cvode_active_;\n");
290  }
291 #endif
292  }
293  Sprintf(buf, " %s();\n", fun->name);
294  replacstr(qsol, buf);
295 #if VECTORIZE
296  Sprintf(buf, "{ %s(_p, _ppvar, _thread, _nt); }\n",
297  fun->name);
298  vectorize_substitute(qsol, buf);
299 #endif
300  break;
301 #endif
302  case PARF:
303 #if VECTORIZE
304 fprintf(stderr, "Notice: PARTIAL is not thread safe.\n");
305  vectorize = 0;
306 #endif
307  if (btype == BREAKPOINT) whileloop(qsol, (long)DERF, 0);
308  solv_partial(qsol, fun);
309  break;
310  default:
311  diag("Illegal or unimplemented SOLVE type: ", fun->name);
312  break;
313  }
314 #if CVODE
315  if (btype == BREAKPOINT) {
316  cvode_valid();
317  }
318 #endif
319  /* add the error check */
320  Insertstr(qsol, "error =");
321  move(errstmt->next, errstmt->prev, qsol->next);
322 #if VECTORIZE
323  if (errstmt->next == errstmt->prev) {
324  vectorize_substitute(qsol->next, "");
325  vectorize_substitute(qsol->prev, "");
326  }else{
327 fprintf(stderr, "Notice: SOLVE with ERROR is not thread safe.\n");
328  vectorize = 0;
329  }
330 #endif
331  freelist(&errstmt);
332  /* under all circumstances, on return from model,
333  p[0] = current indepvar */
334  /* obviously ok if indepvar is original; if it has been changed
335  away from time
336  then _sav_indep will be reset to starting value of original when
337  initmodel is called on every call to model */
338 #if NMODL
339 #else
340  if (btype == BREAKPOINT) {
341 #if SIMSYS
342  Sprintf(buf, "_sav_indep = %s;\n", indepsym->name);
343 #else
344  Sprintf(buf, "_sav_indep = _p[_indepindex];\n");
345 #endif
346  Insertstr(follow, buf);
347  }
348 #endif
349  }
350 }
351 
352 void save_dt(Item* q) /* save and restore the value of indepvar */
353 {
354  /*ARGSUSED*/
355 #if 0 /* integrators no longer increment time */
356  static int first=1;
357 
358  if (first) {
359  first = 0;
360  Linsertstr(procfunc, "double _savlocal;\n");
361  }
362  Sprintf(buf, "_savlocal = %s;\n", indepsym->name);
363  Insertstr(q, buf);
364  Sprintf(buf, "%s = _savlocal;\n", indepsym->name);
365  Insertstr(q->next, buf);
366 #endif
367 }
368 
369 char *saveindep = "";
370 
371 void whileloop(Item* qsol, long type, int ss)
372 {
373 /* no solve statement except this is allowed to
374 change the indepvar. Time passed to integrators
375 must therefore be saved and restored. */
376 /* Note that when the user changes the independent variable within Scop,
377 the while loop still uses the original independent variable. In this case
378 1) _break must be set to the entry value of the original independent variable
379 2) initmodel() must be called after each entry to model()
380 3) in initmodel() we are very careful not to destroy the entry value of
381  time-- on exit it has its original value. Note that _sav_indep gets
382  any value of time that is set within initmodel().
383 */
384 /* executing more that one for loop in a single call to model() is an error
385 which is trapped in scop */
386  static int called = 0, firstderf = 1;
387  char *cp=0;
388 
389  switch (type) {
390  case DERF:
391  case DISCRETE:
392  if (firstderf) { /* time dependent process so create a deltastep */
393  double d[3];
394  int i;
395  Item *q;
396  char sval[256];
397  Sprintf(buf, "delta_%s", indepsym->name);
398  for (i=0, q=indeplist->next; i<3; i++, q=q->next) {
399  d[i] = atof(STR(q));
400  }
401  if (type == DERF) {
402  Sprintf(sval, "%g", (d[1]-d[0])/d[2]);
403  }else if (type == DISCRETE) {
404  Sprintf(sval, "1.0");
405  }
406  deltaindep = ifnew_parminstall(buf, sval, "", "");
407  firstderf = 0;
408 #if NMODL
409 #else
410 Sprintf(buf, "_modl_set_dt(_dt) double _dt; { %s = _dt;}\n",
411  deltaindep->name);
413 #endif
414  }
415  if (type == DERF) {
416  cp = "dt";
417  }else if (type == DISCRETE) {
418  cp = "0.0";
419  }
420  if (ss) {
421  return;
422  }
423  break;
424  default:
425  /*SUPPRESS 622*/
426  assert(0);
427  }
428 #if NMODL
429  if (strcmp(indepsym->name, "t") != 0) {
430  diag("The independent variable name must be `t'", (char *)0);
431  }
432 #else
433  Sprintf(buf, "_save = _break = %s; %s = _sav_indep;\n",
434  indepsym->name, indepsym->name);
435  Insertstr(qsol, buf);
436 #if !SIMSYS
437  Sprintf(buf, "if (_p + _indepindex != &%s) {initmodel(_pp); %s = _sav_indep;}\n",
438  indepsym->name, indepsym->name);
439  Insertstr(qsol, buf);
440 #endif
441 #endif
442 
443  if (called) {
444 Fprintf(stderr, "Warning: More than one integrating SOLVE statement in an \
445 BREAKPOINT block.\nThe simulation will be incorrect if more than one is used \
446 at a time.\n");
447  }
448 #if NMODL
449 #else
450  Sprintf(buf, "if (%s < _break) {\n",indepsym->name);
451  Insertstr(qsol, buf);
452 
453 /* ensure that there are an integer number of steps / break */
454 if (type == DERF) {
455  Sprintf(buf, " { int _nstep; double _dt, _y;\n\
456  _y = _break - %s; _dt = %s;\n", indepsym->name, "dt");
457  Insertstr(qsol, buf);
458  Insertstr(qsol, "_nstep = (int)(_y/_dt + .9);\n if (_nstep==0) _nstep = 1;\n");
459  Sprintf(buf, "%s = _y/((double)_nstep);\n", "dt");
460  Insertstr(qsol, buf);
461  Sprintf(buf, "\n }\n");
462  Insertstr(qsol, buf);
463 }
464 
465  if (type == DERF) {
466  Sprintf(buf, "_break -= .5* %s;\n", "dt");
467  Insertstr(qsol, buf);
468  }
469 #endif
470 #if NMODL
471  /* no longer a for loop */
472 #else
473  Sprintf(buf, "for (; %s < _break; %s += %s) {\n",
474  indepsym->name,
475  indepsym->name, cp);
476  Insertstr(qsol, buf);
477  /* close the while loop; note that integrators have been called */
478  if (type == DERF) {
479  Sprintf(buf, "\n}}\n %s = _save;\n", indepsym->name);
480  }else if (type == DISCRETE) {
481  Sprintf(buf, "\n}}\n");
482  }
483  Insertstr(qsol->next,buf);
484 #endif
485  if (!called) {
486  /* fix up initmodel as per 3) above.
487  In cout.c _save is declared */
488 #if NMODL
489  Sprintf(buf, " _save = %s;\n %s = 0.0;\n",
490  indepsym->name, indepsym->name);
491  saveindep = stralloc(buf, (char *)0);
492 #else
493  Sprintf(buf, "%s0", indepsym->name);
494  IGNORE(ifnew_parminstall(buf, STR(indeplist->prev->prev),
495  STR(indeplist->prev), ""));
496  Sprintf(buf, " _save = %s;\n %s = %s0;\n",
497  indepsym->name, indepsym->name, indepsym->name);
498  saveindep = stralloc(buf, (char *)0);
499 #endif
500  /* Assert no more additions to initfunc involving
501  the value of time */
502  Sprintf(buf, " _sav_indep = %s; %s = _save;\n",
503  indepsym->name, indepsym->name);
505 #if VECTORIZE
507 #endif
508  }
509  called++;
510 }
511 
512 /* steady state consistency means that KINETIC blocks whenever solved must
513 invoke same integrator (default is advance) and DERIVATIVE blocks whenever
514 solved must invoke the derivimplicit method.
515 */
517 {
518  Item *q;
519  Symbol *fun, *method;
520 
521  ITERATE(q, solvq) {
522  fun = SYM(ITM(q));
523  if (fun == SYM(qchk)) {
524  method= SYM(q->next);
525  switch (q->itemtype) {
526  case DERF:
527  if (!method ||
528  strcmp("derivimplicit", method->name)!=0) {
529 diag("STEADYSTATE requires all SOLVE's of this DERIVATIVE block to use the\n\
530 `derivimplicit' method:", fun->name);
531  }
532  break;
533  case KINF:
534  if (SYM(qchk->next) != method
535  || (method &&
536  strcmp("sparse", method->name) != 0)){
537 diag("STEADYSTATE requires all SOLVE's of this KINETIC block to use the\n\
538 same method (`advance' or `sparse'). :", fun->name);
539  }
540  break;
541  default:
542 diag("STEADYSTATE only valid for SOLVEing a KINETIC or DERIVATIVE block:", fun->name);
543  break;
544  }
545  }
546  q = q->next->next;
547  }
548 }
assert(order==section_count)
char * stralloc(char *buf, char *rel)
Definition: list.cpp:208
#define Lappendsym
Definition: model.h:259
short itemtype
Definition: model.h:16
#define KINF
Definition: model.h:132
short type
Definition: cabvars.h:10
void add_deriv_imp_list(char *name)
Definition: deriv.cpp:391
#define DERF
Definition: model.h:125
void solv_diffeq(Item *qsol, Symbol *fun, Symbol *method, int numeqn, int listnum, int steadystate, int btype)
Definition: deriv.cpp:42
#define PROCED
Definition: model.h:120
List * initfunc
Definition: init.cpp:8
#define diag(s)
Definition: fmenu.cpp:188
#define ITERATE(itm, lst)
Definition: model.h:25
#define SYM(q)
Definition: model.h:86
int netrec_need_thread
#define follow
Definition: redef.h:61
#define ITEM0
Definition: model.h:22
Item * lappendsym(List *list, Symbol *sym)
Definition: list.cpp:161
void solvequeue(Item *q1, Item *q2, int blocktype, Item *qerr)
Definition: solve.cpp:40
static int first
Definition: fmenu.cpp:186
char * name
Definition: model.h:72
struct Item * prev
Definition: model.h:20
#define Fprintf
Definition: model.h:249
Item * next(Item *item)
Definition: list.cpp:95
#define SYMBOL
Definition: model.h:102
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)
int i
Definition: model.h:62
#define IGNORE(arg)
Definition: model.h:262
Definition: model.h:15
Symbol * ifnew_parminstall(char *name, char *num, char *units, char *limits)
Definition: parsact.cpp:177
void single_channel(Item *qsol, Symbol *fun, int numeqn, int listnum)
#define Insertstr
Definition: model.h:255
char * current_line()
Definition: io.cpp:223
void movelist(Item *q1, Item *q2, List *s)
Definition: list.cpp:245
#define DISCF
Definition: model.h:128
void cvode_interface(Symbol *fun, int num, int neq)
List * procfunc
Definition: init.cpp:9
void vectorize_substitute(Item *q, char *str)
void solvhandler()
Definition: solve.cpp:133
#define PARF
Definition: model.h:130
int numlist
Definition: solve.cpp:38
Item * linsertstr(List *list, char *str)
Definition: list.cpp:143
Symbol * indepsym
Definition: declare.cpp:11
int netrec_state_count
void init_disc_vars()
Definition: discrete.cpp:122
fprintf(stderr, "Don't know the location of params at %p\, pp)
Definition: model.h:57
void cvode_kinetic(Item *qsol, Symbol *fun, int numeqn, int listnum)
char * name
Definition: init.cpp:16
#define LST(q)
Definition: model.h:90
Symbol * deltaindep
Definition: solve.cpp:11
void * element
Definition: model.h:18
List * newlist()
Definition: list.cpp:50
void save_dt(Item *q)
Definition: solve.cpp:352
virtual void move(const Event &e)
Definition: ocinput.h:19
void cvode_valid()
#define NLINF
Definition: model.h:127
#define Lappendstr
Definition: model.h:260
NMODL parser global flags / functions.
#define lookup
Definition: redef.h:90
long subtype
Definition: model.h:59
void solv_lineq(Item *qsol, Symbol *fun, Symbol *method, int numeqn, int listnum)
Definition: simultan.cpp:23
void sens_nonlin_out(Item *q, Symbol *fun)
Definition: sens.cpp:337
int used
Definition: model.h:65
#define STR(q)
Definition: model.h:87
int vectorize
char * saveindep
Definition: solve.cpp:369
Item * insertstr(Item *item, char *str)
Definition: list.cpp:108
void freelist(List **plist)
Definition: list.cpp:61
#define LINF
Definition: model.h:126
void whileloop(Item *, long, int)
Definition: solve.cpp:371
#define i
Definition: md1redef.h:12
#define Linsertstr
Definition: model.h:258
char buf[512]
Definition: init.cpp:13
void replacstr(Item *q, char *s)
Definition: list.cpp:250
List * massage_list_
Definition: deriv.cpp:19
List * indeplist
Definition: parsact.cpp:15
void solv_partial(Item *qsol, Symbol *fun)
Definition: partial.cpp:84
#define Sprintf
Definition: model.h:248
union Symbol::@18 u
#define SYM0
Definition: model.h:74
size_t q
void check_ss_consist(Item *)
Definition: solve.cpp:516
static List * solvq
Definition: solve.cpp:37
#define ITM(q)
Definition: model.h:88
void solv_nonlin(Item *qsol, Symbol *fun, Symbol *method, int numeqn, int listnum)
Definition: simultan.cpp:12