NEURON
solve.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.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 #include <cstdlib>
9 
10 /* make it an error if 2 solve statements are called on a single call to
11 model() */
12 extern List* indeplist;
14 extern Symbol* indepsym;
15 extern char* current_line();
16 extern List* massage_list_;
17 extern List* nrnstate;
18 extern int vectorize;
19 extern int netrec_state_count;
20 extern int netrec_need_thread;
24 
25 void whileloop(Item*, long, int);
26 void check_ss_consist(Item*);
27 
28 /* Need list of solve statements. We impress the
29 general list structure to handle it. The element is a pointer to an
30 item which is the first item in the statement sequence in another list.
31 */
32 static List* solvq; /* list of the solve statement locations */
33 int numlist = 0; /* number of slist's */
34 
35 void solvequeue(Item* qName, Item* qMethod, int blocktype) /*solve NAME [using METHOD]*/
36 /* qMethod = nullptr means method wasn't there */
37 {
38  /* the solvq list is organized in groups of an item element
39  followed by the method symbol (null if default to be used) */
40  /* The itemtype field of the first is used to carry the blocktype*/
41  /* SOLVE and METHOD method are deleted */
42  if (!solvq) {
43  solvq = newlist();
44  }
45  /* if the blocktype is equation then move the solve statement to
46  the nrnstate function. Everything else stays in the
47  model function to be used as the nrncurrent function */
48  if (blocktype == BREAKPOINT) {
49  if (!nrnstate) {
50  nrnstate = newlist();
51  }
52  Lappendstr(nrnstate, "{");
53  if (qMethod) {
54  movelist(qName->prev, qMethod, nrnstate);
55  } else {
56  movelist(qName->prev, qName, nrnstate);
57  }
58  Lappendstr(nrnstate, "}");
59  }
60  /* verify that the block defintion for this SOLVE has not yet been seen */
61  if (massage_list_) {
62  Item* lq;
63  ITERATE(lq, massage_list_) {
64  if (strcmp(SYM(lq)->name, SYM(qName)->name) == 0) {
65  diag("The SOLVE statement must be before the DERIVATIVE block for ", SYM(lq)->name);
66  }
67  }
68  }
69  Item* lq = lappendsym(solvq, SYM0);
70  ITM(lq) = qName;
71  lq->itemtype = blocktype;
72  /* handle STEADYSTATE option */
73  if (qName->next->itemtype == SYMBOL && strcmp("STEADYSTATE", SYM(qName->next)->name) == 0) {
74  lq->itemtype = -blocktype; /* gets put back below */
75  }
76  if (qMethod) {
77  Lappendsym(solvq, SYM(qMethod));
78  if (strcmp(SYM(qMethod)->name, "derivimplicit") == 0) {
79  add_deriv_imp_list(SYM(qName)->name);
80  }
81  if (strcmp(SYM(qMethod)->name, "cnexp") == 0) {
82  SYM(qMethod)->name = stralloc("derivimplicit", SYM(qMethod)->name);
83  add_deriv_imp_list(SYM(qName)->name);
84  cvode_cnexp_solve = lq;
85  }
86  remove(qMethod->prev);
87  remove(qMethod);
88  } else {
90  }
91  remove(qName->prev);
92 
93  List* errstmt = newlist();
94  lq = lappendsym(solvq, SYM0);
95  LST(lq) = errstmt;
96  Sprintf(buf,
97  "if(error){\n"
98  " std_cerr_stream << \"%s\\n\";\n"
99  " std_cerr_stream << _ml << ' ' << _iml << '\\n';\n"
100  " abort_run(error);\n"
101  "}\n",
102  current_line());
103  insertstr(errstmt, buf);
104 }
105 
106 /* go through the solvq list and construct the proper while loop and calls*/
107 void solvhandler() {
108  Item *lq, *qsol, *follow;
109  List* errstmt;
110  Symbol *fun, *method;
111  int numeqn, listnum, btype, steadystate;
112  int cvodemethod_;
113 
114  if (!solvq)
115  solvq = newlist();
116  init_disc_vars(); /*why not here?*/
117  ITERATE(lq, solvq) { /* remember it goes by 3's */
118  steadystate = 0;
119  btype = lq->itemtype;
120  if (btype < 0) {
121  btype = lq->itemtype = -btype;
122  steadystate = 1;
123  check_ss_consist(lq);
124  }
125  qsol = ITM(lq);
126  lq = lq->next;
127  method = SYM(lq);
128  cvodemethod_ = 0;
129  if (method && strcmp(method->name, "after_cvode") == 0) {
130  method = (Symbol*) 0;
131  lq->element.sym = (Symbol*) 0;
132  cvodemethod_ = 1;
133  }
134  if (method && strcmp(method->name, "cvode_t") == 0) {
135  method = (Symbol*) 0;
136  lq->element.sym = (Symbol*) 0;
137  cvodemethod_ = 2;
138  }
139  if (method && strcmp(method->name, "cvode_v") == 0) {
140  method = (Symbol*) 0;
141  lq->element.sym = (Symbol*) 0;
142  cvodemethod_ = 3;
143  }
144  lq = lq->next;
145  errstmt = LST(lq);
146  /* err stmt handling assumes qsol->next is where it goes. */
147  fun = SYM(qsol);
148  numeqn = fun->used;
149  listnum = fun->u.i;
150  if (btype == BREAKPOINT && (fun->subtype == DERF || fun->subtype == KINF)) {
151  netrec_state_count = numeqn * 10 + listnum;
152  netrec_need_thread = 1;
153  }
154  follow = qsol->next; /* where p[0] gets updated */
155  /* Check consistency of method and block type */
156  if (method && !(method->subtype & fun->subtype)) {
157  Sprintf(buf, "Method %s can't be used with Block %s", method->name, fun->name);
158  diag(buf, (char*) 0);
159  }
160 
161  switch (fun->subtype) {
162  case DERF:
163  if (method == SYM0) {
164  method = lookup("derivimplicit");
165  }
166  if (btype == BREAKPOINT && !steadystate) {
167  /* derivatives recalculated after while loop */
168  if (strcmp(method->name, "cnexp") != 0 &&
169  strcmp(method->name, "derivimplicit") != 0 &&
170  strcmp(method->name, "euler") != 0) {
171  fprintf(stderr,
172  "Notice: %s is not thread safe. Complain to Hines\n",
173  method->name);
174  vectorize = 0;
175  Sprintf(buf, " %s();\n", fun->name);
176  Insertstr(follow, buf);
177  }
178  cvode_interface(fun, listnum, numeqn);
179  }
180  if (btype == BREAKPOINT)
181  whileloop(qsol, (long) DERF, steadystate);
182  solv_diffeq(qsol, fun, method, numeqn, listnum, steadystate, btype);
183  break;
184  case KINF:
185  if (method == SYM0) {
186  method = lookup("_advance");
187  }
188  if (btype == BREAKPOINT && (method->subtype & DERF)) {
189  fprintf(
190  stderr,
191  "Notice: KINETIC is thread safe only with METHOD sparse. Complain to Hines\n");
192  vectorize = 0;
193  /* derivatives recalculated after while loop */
194  Sprintf(buf, " %s();\n", fun->name);
195  Insertstr(follow, buf);
196  }
197  if (btype == BREAKPOINT) {
198  whileloop(qsol, (long) DERF, steadystate);
199  if (strcmp(method->name, "sparse") == 0) {
200  cvode_interface(fun, listnum, numeqn);
201  cvode_kinetic(qsol, fun, numeqn, listnum);
202  }
203  }
204  solv_diffeq(qsol, fun, method, numeqn, listnum, steadystate, btype);
205  break;
206  case NLINF:
207  fprintf(stderr, "Notice: NONLINEAR is not thread safe.\n");
208  vectorize = 0;
209  if (method == SYM0) {
210  method = lookup("newton");
211  }
212  solv_nonlin(qsol, fun, method, numeqn, listnum);
213  break;
214  case LINF:
215  fprintf(stderr, "Notice: LINEAR is not thread safe.\n");
216  vectorize = 0;
217  if (method == SYM0) {
218  method = lookup("simeq");
219  }
220  solv_lineq(qsol, fun, method, numeqn, listnum);
221  break;
222  case DISCF:
223  fprintf(stderr, "Notice: DISCRETE is not thread safe.\n");
224  vectorize = 0;
225  if (btype == BREAKPOINT)
226  whileloop(qsol, (long) DISCRETE, 0);
227  Sprintf(buf, "0; %s += d%s; %s();\n", indepsym->name, indepsym->name, fun->name);
228  replacstr(qsol, buf);
229  break;
230 #if 1 /* really wanted? */
231  case PROCED:
232  if (btype == BREAKPOINT) {
233  whileloop(qsol, (long) DERF, 0);
234  if (cvodemethod_ == 1) { /*after_cvode*/
235  cvode_interface(fun, listnum, 0);
236  }
237  if (cvodemethod_ == 2) { /*cvode_t*/
238  cvode_interface(fun, listnum, 0);
239  insertstr(qsol, "if (!cvode_active_)");
240  cvode_nrn_cur_solve_ = fun;
241  linsertstr(procfunc, "extern int cvode_active_;\n");
242  }
243  if (cvodemethod_ == 3) { /*cvode_t_v*/
244  cvode_interface(fun, listnum, 0);
245  insertstr(qsol, "if (!cvode_active_)");
247  linsertstr(procfunc, "extern int cvode_active_;\n");
248  }
249  }
250  Sprintf(buf, " %s();\n", fun->name);
251  replacstr(qsol, buf);
252  Sprintf(buf, "{ %s(_threadargs_); }\n", fun->name);
253  vectorize_substitute(qsol, buf);
254  break;
255 #endif
256  default:
257  diag("Illegal or unimplemented SOLVE type: ", fun->name);
258  break;
259  }
260  if (btype == BREAKPOINT) {
261  cvode_valid();
262  }
263  /* add the error check */
264  Insertstr(qsol, "error =");
265  move(errstmt->next, errstmt->prev, qsol->next);
266  if (errstmt->next == errstmt->prev) {
267  vectorize_substitute(qsol->next, "");
268  vectorize_substitute(qsol->prev, "");
269  } else {
270  fprintf(stderr, "Notice: SOLVE with ERROR is not thread safe.\n");
271  vectorize = 0;
272  }
273  freelist(&errstmt);
274  /* under all circumstances, on return from model,
275  p[0] = current indepvar */
276  /* obviously ok if indepvar is original; if it has been changed
277  away from time
278  then _sav_indep will be reset to starting value of original when
279  initmodel is called on every call to model */
280  }
281 }
282 
283 void save_dt(Item* q) /* save and restore the value of indepvar */
284 {
285  /*ARGSUSED*/
286 #if 0 /* integrators no longer increment time */
287  static int first=1;
288 
289  if (first) {
290  first = 0;
291  Linsertstr(procfunc, "double _savlocal;\n");
292  }
293  Sprintf(buf, "_savlocal = %s;\n", indepsym->name);
294  Insertstr(q, buf);
295  Sprintf(buf, "%s = _savlocal;\n", indepsym->name);
296  Insertstr(q->next, buf);
297 #endif
298 }
299 
300 const char* saveindep = "";
301 
302 void whileloop(Item* qsol, long type, int ss) {
303  /* no solve statement except this is allowed to
304  change the indepvar. Time passed to integrators
305  must therefore be saved and restored. */
306  /* Note that when the user changes the independent variable within Scop,
307  the while loop still uses the original independent variable. In this case
308  1) _break must be set to the entry value of the original independent variable
309  2) initmodel() must be called after each entry to model()
310  3) in initmodel() we are very careful not to destroy the entry value of
311  time-- on exit it has its original value. Note that _sav_indep gets
312  any value of time that is set within initmodel().
313  */
314  /* executing more that one for loop in a single call to model() is an error
315  which is trapped in scop */
316  static int called = 0, firstderf = 1;
317 
318  switch (type) {
319  case DERF:
320  case DISCRETE:
321  if (firstderf) { /* time dependent process so create a deltastep */
322  double d[3];
323  int i;
324  Item* q;
325  char sval[256];
326  Sprintf(buf, "delta_%s", indepsym->name);
327  for (i = 0, q = indeplist->next; i < 3; i++, q = q->next) {
328  d[i] = atof(STR(q));
329  }
330  if (type == DERF) {
331  Sprintf(sval, "%g", (d[1] - d[0]) / d[2]);
332  } else if (type == DISCRETE) {
333  Sprintf(sval, "1.0");
334  }
335  deltaindep = ifnew_parminstall(buf, sval, "", "");
336  firstderf = 0;
337  }
338  if (ss) {
339  return;
340  }
341  break;
342  default:
343  /*SUPPRESS 622*/
344  assert(0);
345  }
346 
347  if (called) {
348  Fprintf(stderr,
349  "Warning: More than one integrating SOLVE statement in an \
350 BREAKPOINT block.\nThe simulation will be incorrect if more than one is used \
351 at a time.\n");
352  }
353  if (!called) {
354  /* fix up initmodel as per 3) above.
355  In cout.c _save is declared */
356  Sprintf(buf, " _save = %s;\n %s = 0.0;\n", indepsym->name, indepsym->name);
357  saveindep = stralloc(buf, (char*) 0);
358  /* Assert no more additions to initfunc involving
359  the value of time */
360  Sprintf(buf, " _sav_indep = %s; %s = _save;\n", indepsym->name, indepsym->name);
363  }
364  called++;
365 }
366 
367 /* steady state consistency means that KINETIC blocks whenever solved must
368 invoke same integrator (default is advance) and DERIVATIVE blocks whenever
369 solved must invoke the derivimplicit method.
370 */
371 void check_ss_consist(Item* qchk) {
372  Item* q;
373  Symbol *fun, *method;
374 
375  ITERATE(q, solvq) {
376  fun = SYM(ITM(q));
377  if (fun == SYM(qchk)) {
378  method = SYM(q->next);
379  switch (q->itemtype) {
380  case DERF:
381  if (!method || strcmp("derivimplicit", method->name) != 0) {
382  diag(
383  "STEADYSTATE requires all SOLVE's of this DERIVATIVE block to use the\n\
384 `derivimplicit' method:",
385  fun->name);
386  }
387  break;
388  case KINF:
389  if (SYM(qchk->next) != method || (method && strcmp("sparse", method->name) != 0)) {
390  diag(
391  "STEADYSTATE requires all SOLVE's of this KINETIC block to use the\n\
392 same method (`advance' or `sparse'). :",
393  fun->name);
394  }
395  break;
396  default:
397  diag("STEADYSTATE only valid for SOLVEing a KINETIC or DERIVATIVE block:",
398  fun->name);
399  break;
400  }
401  }
402  q = q->next->next;
403  }
404 }
#define i
Definition: md1redef.h:19
void solv_diffeq(Item *qsol, Symbol *fun, Symbol *method, int numeqn, int listnum, int steadystate, int btype)
Definition: deriv.cpp:38
void add_deriv_imp_list(char *name)
Definition: deriv.cpp:435
void init_disc_vars()
Definition: discrete.cpp:78
Symbol * ifnew_parminstall(const char *name, const char *num, const char *units, const char *limits)
Definition: parsact.cpp:71
char buf[512]
Definition: init.cpp:13
static int follow(int expect, int ifyes, int ifno)
Definition: hoc.cpp:498
#define assert(ex)
Definition: hocassrt.h:24
void cvode_kinetic(Item *qsol, Symbol *fun, int numeqn, int listnum)
Definition: kinetic.cpp:1339
#define DERF
Definition: model.h:114
#define STR(q)
Definition: model.h:76
#define SYM0
Definition: model.h:63
#define LINF
Definition: model.h:115
#define ITERATE(itm, lst)
Definition: model.h:18
#define DISCF
Definition: model.h:117
#define SYMBOL
Definition: model.h:91
#define Linsertstr
Definition: model.h:220
#define Lappendstr
Definition: model.h:222
#define Insertstr
Definition: model.h:217
#define ITM(q)
Definition: model.h:77
#define SYM(q)
Definition: model.h:75
#define KINF
Definition: model.h:120
#define LST(q)
Definition: model.h:79
#define Lappendsym
Definition: model.h:221
Symbol * lookup(const char *)
#define NLINF
Definition: model.h:116
#define PROCED
Definition: model.h:109
List * initfunc
Definition: init.cpp:8
List * procfunc
Definition: init.cpp:9
const char * name
Definition: init.cpp:16
char * stralloc(const char *buf, char *rel)
Definition: list.cpp:178
void movelist(Item *q1, Item *q2, List *s)
Definition: list.cpp:214
void move(Item *q1, Item *q2, Item *q3)
Definition: list.cpp:200
Item * linsertstr(List *list, const char *str)
Definition: list.cpp:131
void replacstr(Item *q, const char *s)
Definition: list.cpp:219
Item * next(Item *item)
Definition: list.cpp:89
Item * insertstr(Item *item, const char *str)
Definition: list.cpp:99
Item * lappendsym(List *list, Symbol *sym)
Definition: list.cpp:143
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 cvode_valid()
Definition: nocpout.cpp:3005
void cvode_interface(Symbol *fun, int num, int neq)
Definition: nocpout.cpp:2983
void solv_lineq(Item *qsol, Symbol *fun, Symbol *method, int numeqn, int listnum)
Definition: simultan.cpp:30
void solv_nonlin(Item *qsol, Symbol *fun, Symbol *method, int numeqn, int listnum)
Definition: simultan.cpp:11
void vectorize_substitute(Item *q, const char *str)
Definition: noccout.cpp:748
NMODL parser global flags / functions.
Symbol * deltaindep
Definition: solve.cpp:13
void save_dt(Item *q)
Definition: solve.cpp:283
List * nrnstate
Definition: noccout.cpp:14
int vectorize
Definition: nocpout.cpp:78
Symbol * cvode_nrn_current_solve_
Definition: solve.cpp:23
char * current_line()
Definition: io.cpp:216
Item * cvode_cnexp_solve
Definition: solve.cpp:21
List * indeplist
Definition: parsact.cpp:15
Symbol * cvode_nrn_cur_solve_
Definition: solve.cpp:22
int numlist
Definition: solve.cpp:33
void check_ss_consist(Item *)
Definition: solve.cpp:371
const char * saveindep
Definition: solve.cpp:300
List * massage_list_
Definition: deriv.cpp:19
void solvhandler()
Definition: solve.cpp:107
void whileloop(Item *, long, int)
Definition: solve.cpp:302
void solvequeue(Item *qName, Item *qMethod, int blocktype)
Definition: solve.cpp:35
int netrec_state_count
static List * solvq
Definition: solve.cpp:32
Symbol * indepsym
Definition: declare.cpp:8
int netrec_need_thread
#define diag(s)
Definition: nonlin.cpp:19
size_t q
short type
Definition: cabvars.h:10
static double remove(void *v)
Definition: ocdeck.cpp:205
Definition: model.h:8
struct Item * prev
Definition: model.h:13
void * element
Definition: model.h:11
short itemtype
Definition: model.h:9
struct Item * next
Definition: model.h:12
Definition: model.h:47
union Symbol::@28 u
int i
Definition: model.h:52
long subtype
Definition: model.h:49
char * name
Definition: model.h:61
int used
Definition: model.h:55
int Fprintf(FILE *stream, const char *fmt, Args... args)
Definition: logger.hpp:8