NEURON
simultan.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 #include "modl.h"
3 #include "parse1.hpp"
4 #include "symbol.h"
5 
6 extern int numlist;
7 static List* eqnq;
8 
9 int nonlin_common(Item*);
10 
11 void solv_nonlin(Item* qsol, Symbol* fun, Symbol* method, int numeqn, int listnum) {
12  // examples of method->name: newton
13  // note that the non-type template parameter used to be the first argument; if more tests are
14  // added so that method->name != "newton" then those methods may need to be modified as newton
15  // was
16  Sprintf(buf,
17  "%s<%d>(_slist%d, neuron::scopmath::row_view{_ml, _iml}, %s_wrapper_returning_int, "
18  "_dlist%d);\n",
19  method->name,
20  numeqn,
21  listnum,
22  fun->name,
23  listnum);
24  replacstr(qsol, buf);
25  /* if sens statement appeared in fun then the steadysens call list,
26  built during the massagenonlin phase
27  gets added after the call to newton */
28 }
29 
30 void solv_lineq(Item* qsol, Symbol* fun, Symbol* method, int numeqn, int listnum) {
31  // examples of method->name: simeq
32  Sprintf(buf,
33  " 0;\n"
34  " %s();\n"
35  " error = %s(%d, _coef%d, neuron::scopmath::row_view{_ml, _iml}, _slist%d);\n",
36  fun->name,
37  method->name,
38  numeqn,
39  listnum,
40  listnum);
41  replacstr(qsol, buf);
42 }
43 
44 void eqnqueue(Item* q1) {
45  Item* lq;
46 
47  if (!eqnq) {
48  eqnq = newlist();
49  }
50  lq = lappendsym(eqnq, SYM0);
51  ITM(lq) = q1;
52  return;
53 }
54 
55 static void freeqnqueue() {
56  freelist(&eqnq);
57 }
58 
59 /* args are --- nonlinblk: NONLINEAR NAME stmtlist '}' */
60 void massagenonlin(Item* q1, Item* q2, Item* q3, Item* q4) {
61  /* declare a special _counte variable to number the equations.
62  before each equation we increment it by 1 during run time. This
63  gives us a current equation number */
64  Symbol* nonfun;
65 
66  /* all this junk is still in the intoken list */
67  Sprintf(buf,
68  "static void %s();\nstatic int %s_wrapper_returning_int() { %s(); return 42; }",
69  SYM(q2)->name,
70  SYM(q2)->name,
71  SYM(q2)->name);
73  replacstr(q1, "\nstatic void");
74  Insertstr(q3, "()\n");
75  Insertstr(q3->next, " int _counte = -1;\n");
76  nonfun = SYM(q2);
77  if ((nonfun->subtype) & NLINF && nonfun->u.i) {
78  diag("NONLINEAR merging not implemented", (char*) 0);
79  }
80  numlist++;
81  nonfun->subtype |= NLINF;
82  nonfun->u.i = numlist;
83  nonfun->used = nonlin_common(q4);
84  movelist(q1, q4, procfunc);
85 }
86 
87 int nonlin_common(Item* q4) /* used by massagenonlin() and mixed_eqns() */
88 {
89  Item *lq, *qs, *q;
90  int i, counts = 0, counte = 0, using_array;
91  Symbol* s;
92 
93  using_array = 0;
94  SYMITER_STAT {
95  if (s->used) {
96  s->varnum = counts;
97  slist_data(s, counts, numlist);
98  if (s->subtype & ARRAY) {
99  int dim = s->araydim;
100  using_array = 1;
101  Sprintf(buf,
102  "for(_i=0;_i<%d;_i++){\n _slist%d[%d+_i] = {%s_columnindex, _i};}\n",
103  dim,
104  numlist,
105  counts,
106  s->name);
107  counts += dim;
108  } else {
109  Sprintf(buf, "_slist%d[%d] = {%s_columnindex, 0};\n", numlist, counts, s->name);
110  counts++;
111  }
113  s->used = 0;
114  }
115  }
116 
117  ITERATE(lq, eqnq) {
118  char* eqtype = SYM(ITM(lq))->name;
119  if (strcmp(eqtype, "~+") == 0) { /* add equation to previous */
120  if (counte == -1) {
121  diag("no previous equation for adding terms", (char*) 0);
122  }
123  Sprintf(buf, "_dlist%d[_counte] +=", numlist);
124  } else if (eqtype[0] == 'D') {
125  /* derivative equations using implicit method */
126  int count_deriv = SYM(ITM(lq))->araydim;
127  Sprintf(buf, "_dlist%d[++_counte] =", numlist);
128  counte += count_deriv;
129  } else {
130  Sprintf(buf, "_dlist%d[++_counte] =", numlist);
131  counte++;
132  }
133  replacstr(ITM(lq), buf);
134  }
135  if (!using_array) {
136  if (counte != counts) {
137  Sprintf(buf, "Number of equations, %d, does not equal number, %d", counte, counts);
138  diag(buf, "of states used");
139  }
140  } else {
141 #if 1 /* can give message when running */
142  Sprintf(buf,
143  "if(_counte != %d) printf( \"Number of equations, %%d,\
144  does not equal number of states, %d\", _counte + 1);\n",
145  counts - 1,
146  counts);
147  Insertstr(q4, buf);
148 #endif
149  }
150  if (counte == 0) {
151  diag("NONLINEAR contains no equations", (char*) 0);
152  }
153  freeqnqueue();
154  Sprintf(buf,
155  "static neuron::container::field_index _slist%d[%d]; static double _dlist%d[%d];\n",
156  numlist,
157  counts,
158  numlist,
159  counts);
160  q = linsertstr(procfunc, buf);
161  Sprintf(buf, "static neuron::container::field_index _slist%d[%d];\n", numlist, counts);
163  return counts;
164 }
165 
166 Item* mixed_eqns(Item* q2, Item* q3, Item* q4) /* name, '{', '}' */
167 {
168  int counts;
169  Item* qret;
170  Item* q;
171 
172  if (!eqnq) {
173  return ITEM0; /* no nonlinear algebraic equations */
174  }
175  /* makes use of old massagenonlin split into the guts and
176  the header stuff */
177  numlist++;
178  counts = nonlin_common(q4);
179  Insertstr(q4, "}");
180  q = insertstr(q3, "{ static int _recurse = 0;\n int _counte = -1;\n");
181  Sprintf(buf,
182  "{\n"
183  " auto* _savstate%d =_thread[_dith%d].get<double*>();\n"
184  " auto* _dlist%d = _thread[_dith%d].get<double*>() + %d;\n"
185  " int _counte = -1;\n",
186  numlist - 1,
187  numlist - 1,
188  numlist,
189  numlist - 1,
190  counts);
192  Insertstr(q3, "if (!_recurse) {\n _recurse = 1;\n");
193  // olupton 2023-01-19: this code does not appear to be covered by the test suite
194  Sprintf(buf,
195  "error = newton<%d>(_slist%d, neuron::scopmath::row_view{_ml, _iml}, %s, _dlist%d);\n",
196  counts,
197  numlist,
198  SYM(q2)->name,
199  numlist);
200  qret = insertstr(q3, buf);
201  Sprintf(buf,
202  "error = nrn_newton_thread(_newtonspace%d, %d, _slist%d, "
203  "neuron::scopmath::row_view{_ml, _iml}, %s, _dlist%d, _ml,"
204  " _iml, _ppvar, _thread, _globals, _nt);\n",
205  numlist - 1,
206  counts,
207  numlist,
208  SYM(q2)->name,
209  numlist);
210  vectorize_substitute(qret, buf);
211  Insertstr(q3, "_recurse = 0; if(error) {abort_run(error);}}\n");
212  return qret;
213 }
214 
215 /* linear simultaneous equations */
216 /* declare a _counte variable to dynamically contain the current
217 equation number. This is necessary to allow use of state vectors.
218 It is no longer necessary to count equations here but we do it
219 anyway in case of future move to named equations.
220 It is this change which requires a varnum field in Symbols for states
221 since the arraydim field cannot be messed with anymore.
222 */
223 static int nlineq = -1; /* actually the current index of the equation */
224  /* is only good if there are no arrays */
225 static int using_array; /* 1 if vector state in equations */
226 static int nstate = 0;
227 static Symbol* linblk;
228 static Symbol* statsym;
229 
230 void init_linblk(Item* q) /* NAME */
231 {
232  using_array = 0;
233  nlineq = -1;
234  nstate = 0;
235  linblk = SYM(q);
236  numlist++;
237 }
238 
239 void init_lineq(Item* q1) /* the colon */
240 {
241  if (strcmp(SYM(q1)->name, "~+") == 0) {
242  replacstr(q1, "");
243  } else {
244  nlineq++; /* current index will start at 0 */
245  replacstr(q1, " ++_counte;\n");
246  }
247 }
248 
249 static char* indexstr; /* set in lin_state_term, used in linterm */
250 
251 void lin_state_term(Item* q1, Item* q2) /* term last*/
252 {
253  char* qconcat(Item*, Item*); /* but puts extra ) at end */
254 
255  statsym = SYM(q1);
256  replacstr(q1, "1.0");
257  if (statsym->subtype & ARRAY) {
258  indexstr = qconcat(q1->next->next, q2->prev);
259  deltokens(q1->next, q2->prev); /*can't erase lastok*/
260  replacstr(q2, "");
261  }
262  if (statsym->used == 1) {
263  statsym->varnum = nstate;
264  if (statsym->subtype & ARRAY) {
265  int dim = statsym->araydim;
266  using_array = 1;
267  Sprintf(buf,
268  "for(_i=0;_i<%d;_i++){_slist%d[%d+_i] = {%s_columnindex, _i};}\n",
269  dim,
270  numlist,
271  nstate,
272  statsym->name);
273  nstate += dim;
274  } else {
275  Sprintf(buf, "_slist%d[%d] = {%s_columnindex, 0};\n", numlist, nstate, statsym->name);
276  nstate++;
277  }
279  }
280 }
281 
282 void linterm(Item* q1, Item* q2, int pstate, int sign) /*primary, last ,, */
283 {
284  const char* signstr;
285 
286  if (pstate == 0) {
287  sign *= -1;
288  }
289  if (sign == -1) {
290  signstr = " -= ";
291  } else {
292  signstr = " += ";
293  }
294 
295  if (pstate == 1) {
296  if (statsym->subtype & ARRAY) {
297  Sprintf(
298  buf, "_coef%d[_counte][%d + %s]%s", numlist, statsym->varnum, indexstr, signstr);
299  } else {
300  Sprintf(buf, "_coef%d[_counte][%d]%s", numlist, statsym->varnum, signstr);
301  }
302  Insertstr(q1, buf);
303  } else if (pstate == 0) {
304  Sprintf(buf, "_RHS%d(_counte)%s", numlist, signstr);
305  Insertstr(q1, buf);
306  } else {
307  diag("more than one state in preceding term", (char*) 0);
308  }
309  Insertstr(q2->next, ";\n");
310 }
311 
312 void massage_linblk(Item* q1, Item* q2, Item* q3, Item* q4) /* LINEAR NAME stmtlist
313  '}' */
314 {
315  Item* qs;
316  Symbol* s;
317  int i;
318 
319 #if LINT
320  assert(q2);
321 #endif
322  if (++nlineq == 0) {
323  diag(linblk->name, "has no equations");
324  }
325  Sprintf(buf, "static void %s();\n", SYM(q2)->name);
327  replacstr(q1, "\nstatic void");
328  Insertstr(q3, "()\n");
329  Insertstr(q3->next, " int _counte = -1;\n");
330  linblk->subtype |= LINF;
331  linblk->u.i = numlist;
332  SYMITER(NAME) {
333  if ((s->subtype & STAT) && s->used) {
334  s->used = 0;
335  }
336  }
337  if (!using_array) {
338  if (nlineq != nstate) {
339  Sprintf(buf, "Number states, %d, unequal to equations, %d in ", nstate, nlineq);
340  diag(buf, linblk->name);
341  }
342  } else {
343 #if 1 /* can give message when running */
344  Sprintf(buf,
345  "if(_counte != %d) printf( \"Number of equations, %%d,\
346  does not equal number of states, %d\", _counte + 1);\n",
347  nstate - 1,
348  nstate);
349  Insertstr(q4, buf);
350 #endif
351  }
352  linblk->used = nstate;
353  Sprintf(buf,
354  "static neuron::container::field_index _slist%d[%d];static double **_coef%d;\n",
355  numlist,
356  nstate,
357  numlist);
359  Sprintf(buf, "\n#define _RHS%d(arg) _coef%d[arg][%d]\n", numlist, numlist, nstate);
361  Sprintf(buf, "if (_first) _coef%d = makematrix(%d, %d);\n", numlist, nstate, nstate + 1);
363  Sprintf(buf, "zero_matrix(_coef%d, %d, %d);\n{\n", numlist, nstate, nstate + 1);
364  Insertstr(q3->next, buf);
365  Insertstr(q4, "\n}\n");
366  movelist(q1, q4, procfunc);
367  nstate = 0;
368  nlineq = 0;
369 }
370 
371 
372 /* It is sometimes convenient to not use some states in solving equations.
373  We use the SOLVEFOR statement to list the states in LINEAR, NONLINEAR,
374  and KINETIC blocks that are to be treated as states in fact. States
375  not listed are treated in that block as assigned variables.
376  If the SOLVEFOR statement is absent all states are assumed to be in the
377  list.
378 
379  Syntax is:
380  blocktype blockname [SOLVEFOR name, name, ...] { statement }
381 
382  The implementation uses the varname: production that marks the state->used
383  record. The old if statement was
384  if (inequation && (SYM($1)->subtype & STAT)) { then mark states}
385  now we add && in_solvefor() to indicate that it Really should be marked.
386  The hope is that no further change to diagnostics for LINEAR or NONLINEAR
387  will be required. Some more work on KINETIC is required since the checking
388  on whether a name is a STAT is done much later.
389  The solveforlist is freed at the end of each block.
390 */
391 
393 
395  Item* q;
396 
397  if (!solveforlist) {
398  return 1;
399  }
401  if (s == SYM(q)) {
402  return 1;
403  }
404  }
405  return 0;
406 }
#define i
Definition: md1redef.h:19
char buf[512]
Definition: init.cpp:13
#define assert(ex)
Definition: hocassrt.h:24
char * qconcat(Item *q1, Item *q2)
Definition: kinetic.cpp:85
#define SYM0
Definition: model.h:63
#define LINF
Definition: model.h:115
#define STAT
Definition: model.h:106
#define ITERATE(itm, lst)
Definition: model.h:18
#define Linsertstr
Definition: model.h:220
#define Lappendstr
Definition: model.h:222
#define ITEM0
Definition: model.h:15
#define Insertstr
Definition: model.h:217
#define ITM(q)
Definition: model.h:77
#define SYM(q)
Definition: model.h:75
#define NLINF
Definition: model.h:116
#define ARRAY
Definition: model.h:107
List * procfunc
Definition: init.cpp:9
const char * name
Definition: init.cpp:16
List * initlist
Definition: init.cpp:8
void movelist(Item *q1, Item *q2, List *s)
Definition: list.cpp:214
Item * linsertstr(List *list, const char *str)
Definition: list.cpp:131
void replacstr(Item *q, const char *s)
Definition: list.cpp:219
void deltokens(Item *q1, Item *q2)
Definition: list.cpp:189
Item * insertstr(Item *item, const char *str)
Definition: list.cpp:99
Item * lappendsym(List *list, Symbol *sym)
Definition: list.cpp:143
#define SYMITER(arg1)
Definition: symbol.h:13
#define SYMITER_STAT
Definition: symbol.h:22
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 slist_data(Symbol *s, int indx, int findx)
Definition: nocpout.cpp:2686
void vectorize_substitute(Item *q, const char *str)
Definition: noccout.cpp:748
NMODL parser global flags / functions.
#define diag(s)
Definition: nonlin.cpp:19
size_t q
s
Definition: multisend.cpp:521
void init_linblk(Item *q)
Definition: simultan.cpp:230
int in_solvefor(Symbol *s)
Definition: simultan.cpp:394
static Symbol * linblk
Definition: simultan.cpp:227
void lin_state_term(Item *q1, Item *q2)
Definition: simultan.cpp:251
void massage_linblk(Item *q1, Item *q2, Item *q3, Item *q4)
Definition: simultan.cpp:312
int nonlin_common(Item *)
Definition: simultan.cpp:87
Item * mixed_eqns(Item *q2, Item *q3, Item *q4)
Definition: simultan.cpp:166
static void freeqnqueue()
Definition: simultan.cpp:55
void massagenonlin(Item *q1, Item *q2, Item *q3, Item *q4)
Definition: simultan.cpp:60
static char * indexstr
Definition: simultan.cpp:249
int numlist
Definition: solve.cpp:33
static List * eqnq
Definition: simultan.cpp:7
void eqnqueue(Item *q1)
Definition: simultan.cpp:44
static int nlineq
Definition: simultan.cpp:223
List * solveforlist
Definition: simultan.cpp:392
void init_lineq(Item *q1)
Definition: simultan.cpp:239
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 linterm(Item *q1, Item *q2, int pstate, int sign)
Definition: simultan.cpp:282
static int using_array
Definition: simultan.cpp:225
static int nstate
Definition: simultan.cpp:226
static Symbol * statsym
Definition: simultan.cpp:228
Definition: model.h:8
struct Item * prev
Definition: model.h:13
struct Item * next
Definition: model.h:12
Definition: model.h:47
union Symbol::@28 u
int i
Definition: model.h:52
int araydim
Definition: model.h:57
long subtype
Definition: model.h:49
char * name
Definition: model.h:61
int used
Definition: model.h:55
int varnum
Definition: model.h:59