NEURON
nocpout.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 
3 /* /local/src/master/nrn/src/nmodl/nocpout.c,v 4.1 1997/08/30 20:45:28 hines Exp */
4 
5 /*
6 nrnversion is a string that is passed via the _mechanism structure
7 as the first arg. It will be interpreted within neuron to determine if
8 that version is compatible with this version.
9 For now try to use something of the form d.d
10 If this is changed then also change nrnoc/init.c
11 */
12 const char* nmodl_version_ = "7.7.0";
13 
14 /* Point processes are now interfaced to nrnoc via objectvars.
15 Thus, p-array variables and functions accessible to hoc do not have
16 suffixes, and there is a constructor, destructor.
17 Also hoc interface functions always have a void* _vptr arg which is
18 always cast to (Point_process*) and the _p and _ppvar pointers set.
19 This makes the old setdata and create obsolete.
20 */
21 /* The strategy is to use as much of parout and hparout method as possible.
22 The bulk of the variables is in a p-array. The variables that don't belong
23 in this p-array are indicated by a flag with sym->subtype & NRNNOTP.
24 All other variables have values in that single array but not all those values
25 are available from HOC.
26 
27 Variables accessible to NEURON are variables that appear within
28 GLOBAL, and RANGE statements.
29 
30 Variables that do not appear in the p-array are:
31  1)externally declared variables such as celsius, t.
32  2)parameters and assigned not declared in the NEURON{RANGE list}
33  that are global with respect to sections.
34  3) variables static to this model, ie. v
35  4) read only variables like "diam"
36 States always are in the p-array.
37 
38 USEION variables in the p-array have connections to other places and
39 depending on the context may get their value from somewhere else, or
40 add their value to somewhere else, or place their value somewhere else.
41 The cases are:
42  NONSPECIFIC and USEION WRITE i... value added to proper ion current
43  and total current.
44  USEION READ entry value assigned to local copy.
45  USEION WRITE e.. ..o ..i exit value of local copy assigned to pointer..
46  It is an error for an ionic current or ionic variable
47  to be a STATE. Use another variable as the state, make the
48  ionic variable an ASSIGNED and just assign it at the
49  proper place.
50  Alternatively, if they are STATE's then they should not be READ
51  since their value comes from the p-array itself.
52 
53 POINTER variables are like USEION variables. Unfortunately, it is up to
54 the hoc user to make sure they point to the proper place with a connect
55 statement. At this time we only check for a null pointer. The pointers
56 are kept in the ppvar array.
57 
58 each model creates a setdata_suffix(x) (or setdata_suffix(i)) function
59 which sets up _p and _ppvar for use by functions in the model called
60 directly by hoc.
61 */
62 
63 #include "modl.h"
64 #include "parse1.hpp"
65 
66 #include <algorithm>
67 #include <iterator> // std::back_inserter
68 #include <stdlib.h>
69 #include <string>
70 #include <vector>
71 #ifdef HAVE_UNISTD_H
72 #include <unistd.h>
73 #endif
74 #include <filesystem>
75 namespace fs = std::filesystem;
76 #define GETWD(buf) getcwd(buf, NRN_BUFSIZE)
77 
78 int vectorize = 1;
79 /*
80 the idea is to put all variables into a vector of vectors so there
81 there is no static data. Every function has an implicit argument, underbar ix
82 which tells which set of data _p[ix][...] to use. There are going to have
83 to be limits on the kinds of scopmath functions called since many of them
84 need static data. We can have special versions of the most useful of these.
85 ie sparse.c.
86 Above is obsolete in detail , underbar ix is no longer used.
87 When vectorize = 1 then we believe the code is thread safe and most functions
88 pass around _p, _ppvar, _thread. When vectorize is 0 then we are definitely
89 not thread safe and _p and _ppvar are static.
90 */
91 
92 
93 #define IONEREV 0 /* Parameter */
94 #define IONIN 1
95 #define IONOUT 2
96 #define IONCUR 3 /* assigned */
97 #define IONDCUR 4
98 
99 extern int brkpnt_exists;
100 static const char* brkpnt_str_;
101 extern Symbol* indepsym;
102 extern List* symlist[];
103 extern List* ldifuslist;
104 extern int check_tables_threads(List*);
108 Item* defs_list_parm_default; // where we insert initializer defaults
114 extern int protect_;
115 extern int protect_include_;
116 extern List *set_ion_variables(int), *get_ion_variables(int);
117 extern int netrec_need_v;
118 
119 int decode_limits(Symbol* sym, double* pg1, double* pg2);
120 int decode_tolerance(Symbol* sym, double* pg1);
121 
122 
123 /* NEURON block information */
128 static List* rangeparm;
129 static List* rangedep;
130 static List* rangestate;
134 static int num_random_vars = 0;
135 static char suffix[256];
136 static const char* rsuffix; /* point process range and functions don't have suffix*/
137 static char* mechname;
138 int point_process; /* 1 if a point process model */
139 int artificial_cell; /* 1 if also explicitly declared an ARTIFICIAL_CELL */
140 static int diamdec = 0; /*1 if diam is declared*/
141 static int areadec = 0;
142 static int use_bbcorepointer = 0;
143 
144 void defs_h(Symbol*);
145 int iontype(char* s1, char* s2);
146 void nrndeclare();
147 void del_range(List*);
148 void declare_p();
149 int iondef(int*);
150 void ion_promote(Item*);
151 static int ppvar_cnt;
153 static void ppvar_semantics(int, const char* semantics, const char* name, const char* type);
154 static int for_netcons_; /* number of FOR_NETCONS statements */
157 static int ba_index_; /* BEFORE AFTER blocks. See bablk */
158 static List* ba_list_;
159 
163 static int tqitem_index;
164 static int watch_index;
170 int watch_seen_; /* number of WATCH statements + 1*/
171 extern List* watch_alloc;
172 static Item* net_send_delivered_; /* location for if flag is 1 then clear the
173  tqitem_ to allow an error message for net_move */
174 
175 #define SYMITER(arg) \
176  ITERATE(q, syminorder) { \
177  s = SYM(q); \
178  if (s->type == arg)
179 
180 #define SYMLISTITER \
181  for (i = 'A'; i <= 'z'; i++) \
182  ITERATE(q, symlist[i])
183 
184 /* varcount holds the index into the .var file and is saved in s->used
185  parraycount holds the index into the p array and is saved in s->varnum
186  pvarcount indexes pointers to variables such as ena
187 */
188 static int varcount, parraycount;
189 static int prop_size;
190 static std::vector<std::pair<int, std::string>> ppvar_data_field_strings;
191 static std::vector<std::string> data_field_strings;
192 
193 void nrninit() {
194  currents = newlist();
195  rangeparm = newlist();
196  rangedep = newlist();
197  rangestate = newlist();
198  useion = newlist();
199  nrnpointers = newlist();
200  indepinstall(install("t", NAME), "0", "1", "100", "ms");
201  debugging_ = 1;
204  nmodlrandoms = newlist();
205 }
206 
207 void parout() {
208  int i, ioncount, pointercount, gind, emit_check_table_thread;
209  Item *q, *q1;
210  Symbol *s, *sion;
211  double d1, d2;
212  extern char* modprefix;
213  char* modbase;
214 
215  defs_list = newlist(); /* relates hoc names to c-variables */
216  if (brkpnt_exists) {
217  brkpnt_str_ = "nrn_cur, nrn_jacob, nrn_state";
218  } else {
219  brkpnt_str_ = "nullptr, nullptr, nullptr";
220  }
221 
222  for (modbase = modprefix + strlen(modprefix); modbase != modprefix; modbase--) {
223  if (*modbase == '\\' || *modbase == '/') {
224  modbase++;
225  break;
226  }
227  }
228  if (!mechname) {
229  Sprintf(suffix, "_%s", modbase);
230  mechname = modbase;
231  } else if (strcmp(mechname, "nothing") == 0) {
232  vectorize = 0;
233  suffix[0] = '\0';
234  mechname = modbase;
235  nmodl_text = 0;
236  } else {
237  Sprintf(suffix, "_%s", mechname);
238  }
239 
240  func_needs_setdata(); // Do FUNCTION/PROCEDURE need prior call to setdata.
241 
243  fprintf(stderr,
244  "Notice: ARTIFICIAL_CELL models that would require thread specific data are not "
245  "thread safe.\n");
246  vectorize = 0;
247  }
248  if (point_process) {
249  rsuffix = "";
250  } else {
251  rsuffix = suffix;
252  }
253 
255  "\
256 \n#if !NRNGPU\
257 \n#undef exp\
258 \n#define exp hoc_Exp\
259 \n#if NRN_ENABLE_ARCH_INDEP_EXP_POW\
260 \n#undef pow\
261 \n#define pow hoc_pow\
262 \n#endif\
263 \n#endif\n\
264 ");
265  if (protect_include_) {
266  Lappendstr(defs_list, "\n#include \"nmodlmutex.h\"");
267  }
268 
269 #if 1
270  /* for easier profiling, give distinct names to otherwise reused static names */
271  Sprintf(buf,
272  "\n\
273 #define nrn_init _nrn_init_%s\n\
274 #define _nrn_initial _nrn_initial_%s\n\
275 #define nrn_cur _nrn_cur_%s\n\
276 #define _nrn_current _nrn_current_%s\n\
277 #define nrn_jacob _nrn_jacob_%s\n\
278 #define nrn_state _nrn_state_%s\n\
279 #define _net_receive _net_receive_%s\
280 ",
281  suffix,
282  suffix,
283  suffix,
284  suffix,
285  suffix,
286  suffix,
287  suffix);
289  SYMLISTITER {
290  Symbol* s = SYM(q);
291  /* note that FUNCT will be redefined anyway */
292  if (s->type == NAME && s->subtype & (PROCED | DERF | KINF)) {
293  Sprintf(buf, "\n#define %s %s_%s", s->name, s->name, suffix);
295  }
296  }
297  Lappendstr(defs_list, "\n");
298 #endif /* distinct names for easier profiling */
299 
300  if (vectorize) {
302  "\n\
303 #define _threadargscomma_ _ml, _iml, _ppvar, _thread, _globals, _nt,\n\
304 #define _threadargsprotocomma_ Memb_list* _ml, size_t _iml, Datum* _ppvar, Datum* _thread, double* _globals, NrnThread* _nt,\n\
305 #define _internalthreadargsprotocomma_ _nrn_mechanism_cache_range* _ml, size_t _iml, Datum* _ppvar, Datum* _thread, double* _globals, NrnThread* _nt,\n\
306 #define _threadargs_ _ml, _iml, _ppvar, _thread, _globals, _nt\n\
307 #define _threadargsproto_ Memb_list* _ml, size_t _iml, Datum* _ppvar, Datum* _thread, double* _globals, NrnThread* _nt\n\
308 #define _internalthreadargsproto_ _nrn_mechanism_cache_range* _ml, size_t _iml, Datum* _ppvar, Datum* _thread, double* _globals, NrnThread* _nt\n\
309 ");
310  } else {
312  "\n\
313 #define _threadargscomma_ /**/\n\
314 #define _threadargsprotocomma_ /**/\n\
315 #define _internalthreadargsprotocomma_ /**/\n\
316 #define _threadargs_ /**/\n\
317 #define _threadargsproto_ /**/\n\
318 #define _internalthreadargsproto_ /**/\n\
319 ");
320  }
322  "\
323  /*SUPPRESS 761*/\n\
324  /*SUPPRESS 762*/\n\
325  /*SUPPRESS 763*/\n\
326  /*SUPPRESS 765*/\n\
327  ");
328  Lappendstr(defs_list, "extern double *hoc_getarg(int);\n");
329 
330  nrndeclare();
332  declare_p();
333  // iondef defined _nrn_mechanism_cache_range
334  ioncount = iondef(&pointercount); /* first is _nd_area if point process */
335  if (vectorize) {
336  Sprintf(buf, "/* Thread safe. No static _ml, _iml or _ppvar. */\n");
337  } else {
338  Sprintf(buf,
339  "static _nrn_mechanism_cache_instance _ml_real{nullptr};\n"
340  "static _nrn_mechanism_cache_range *_ml{&_ml_real};\n"
341  "static size_t _iml{0};\n"
342  "static Datum *_ppvar;\n");
343  }
345  Lappendstr(defs_list, "static int hoc_nrnpointerindex = ");
346  if (pointercount) {
347  q = nrnpointers->next;
348  Sprintf(buf, "%d;\n", SYM(q)->used);
349  } else {
350  Sprintf(buf, "-1;\n");
351  }
353  /*above modified to also count and define pointers*/
354 
355  if (vectorize) {
356  Lappendstr(defs_list, "static _nrn_mechanism_std_vector<Datum> _extcall_thread;\n");
357  }
358  if (!point_process) {
359  Lappendstr(defs_list, "static Prop* _extcall_prop;\n");
361  "/* _prop_id kind of shadows _extcall_prop to allow validity checking. */\n");
362  Lappendstr(defs_list, "static _nrn_non_owning_id_without_container _prop_id{};\n");
363  }
364 
365  Lappendstr(defs_list, "/* external NEURON variables */\n");
366  SYMLISTITER {
367  s = SYM(q);
368  if (s->nrntype & NRNEXTRN) {
369  if (strcmp(s->name, "dt") == 0) {
370  continue;
371  }
372  if (strcmp(s->name, "t") == 0) {
373  continue;
374  }
375  if (s->subtype & ARRAY) {
376  Sprintf(buf, "extern double* %s;\n", s->name);
377  } else {
378  Sprintf(buf, "extern double %s;\n", s->name);
379  }
381  }
382  }
383 
384  Lappendstr(defs_list, "/* declaration of user functions */\n");
385  SYMLISTITER {
386  s = SYM(q);
387  if (s->subtype & (FUNCT | PROCED) && s->name[0] != '_') {
388  if (point_process) {
389  Sprintf(buf, "static double _hoc_%s(void*);\n", s->name);
390  } else {
391  Sprintf(buf, "static void _hoc_%s(void);\n", s->name);
392  }
394  }
395  }
396 
398  "static int _mechtype;\n\
399 extern void _nrn_cacheloop_reg(int, int);\n\
400 extern void hoc_register_limits(int, HocParmLimits*);\n\
401 extern void hoc_register_units(int, HocParmUnits*);\n\
402 extern void nrn_promote(Prop*, int, int);\n\
403 ");
404 
405  if (nmodl_text) {
407  "\n"
408  "#define NMODL_TEXT 1\n"
409  "#if NMODL_TEXT\n"
410  "static void register_nmodl_text_and_filename(int mechtype);\n"
411  "#endif\n");
412  }
413 
414  /**** create special point process functions */
415  if (point_process) {
416  Lappendstr(defs_list, "extern Prop* nrn_point_prop_;\n");
417  Lappendstr(defs_list, "static int _pointtype;\n");
419  "static void* _hoc_create_pnt(Object* _ho) { void* create_point_process(int, "
420  "Object*);\n");
421  Lappendstr(defs_list, "return create_point_process(_pointtype, _ho);\n}\n");
422  Lappendstr(defs_list, "static void _hoc_destroy_pnt(void*);\n");
423  Lappendstr(
424  defs_list,
425  "static double _hoc_loc_pnt(void* _vptr) {double loc_point_process(int, void*);\n");
426  Lappendstr(defs_list, "return loc_point_process(_pointtype, _vptr);\n}\n");
428  "static double _hoc_has_loc(void* _vptr) {double has_loc_point(void*);\n");
429  Lappendstr(defs_list, "return has_loc_point(_vptr);\n}\n");
430  Lappendstr(defs_list, "static double _hoc_get_loc_pnt(void* _vptr) {\n");
431  Lappendstr(
432  defs_list,
433  "double get_loc_point_process(void*); return (get_loc_point_process(_vptr));\n}\n");
434  }
435 
436  std::string hoc_setdata_arg = point_process ? "void*" : "";
437  Sprintf(buf, "static void _hoc_setdata(%s);\n", hoc_setdata_arg.c_str());
439 
440  /* functions */
441  Lappendstr(defs_list, "/* connect user functions to hoc names */\n");
442  Lappendstr(defs_list, "static VoidFunc hoc_intfunc[] = {\n");
443  if (point_process) {
444  Lappendstr(defs_list, "{0, 0}\n};\n");
445  Lappendstr(defs_list, "static Member_func _member_func[] = {\n");
446  Sprintf(buf, "{\"loc\", _hoc_loc_pnt},\n");
448  Sprintf(buf, "{\"has_loc\", _hoc_has_loc},\n");
450  Sprintf(buf, "{\"get_loc\", _hoc_get_loc_pnt},\n");
452  } else {
453  Sprintf(buf, "{\"setdata_%s\", _hoc_setdata},\n", mechname);
455  }
456 
457  SYMLISTITER {
458  s = SYM(q);
459  if ((s->subtype & (FUNCT | PROCED)) && s->name[0] != '_') {
460  Sprintf(buf, "{\"%s%s\", _hoc_%s},\n", s->name, rsuffix, s->name);
462  }
463  }
464  Lappendstr(defs_list, "{0, 0}\n};\n");
465 
466  /* Direct Python call wrappers to density mechanism functions. */
467  if (!point_process) {
469  "\n/* Direct Python call wrappers to density mechanism functions.*/\n");
470  SYMLISTITER {
471  s = SYM(q);
472  if ((s->subtype & (FUNCT | PROCED)) && s->name[0] != '_') {
473  Sprintf(buf, "static double _npy_%s(Prop*);\n", s->name, s->name);
475  }
476  }
478  "\n"
479  "static NPyDirectMechFunc npy_direct_func_proc[] = {\n");
480  SYMLISTITER {
481  s = SYM(q);
482  if ((s->subtype & (FUNCT | PROCED)) && s->name[0] != '_') {
483  Sprintf(buf, "{\"%s\", _npy_%s},\n", s->name, s->name);
485  }
486  }
487  Lappendstr(defs_list, "{0, 0}\n};\n");
488  }
489 
490  /* FUNCTION's are now global so callable from other models */
491  /* change name to namesuffix. This propagates everywhere except
492  to hoc_name*/
493  /* but don't do it if suffix is empty */
494  if (suffix[0])
495  SYMLISTITER {
496  s = SYM(q);
497  if ((s->subtype & FUNCT)) {
498  Sprintf(buf, "#define %s %s%s\n", s->name, s->name, suffix);
499  q1 = Lappendstr(defs_list, buf);
500  q1->itemtype = VERBATIM;
501  }
502  }
503  SYMLISTITER {
504  int j;
505  s = SYM(q);
506  if ((s->subtype & FUNCT)) {
507  Sprintf(buf, "extern double %s(", s->name);
509  if (vectorize && !s->no_threadargs) {
510  if (s->varnum) {
511  Lappendstr(defs_list, "_internalthreadargsprotocomma_");
512  } else {
513  Lappendstr(defs_list, "_internalthreadargsproto_");
514  }
515  }
516  for (j = 0; j < s->varnum; ++j) {
517  Lappendstr(defs_list, "double");
518  if (j + 1 < s->varnum) {
519  Lappendstr(defs_list, ",");
520  }
521  }
522  Lappendstr(defs_list, ");\n");
523  }
524  }
525 
526  /* per thread top LOCAL */
527  /* except those that are marked assigned_to_ == 2 stay static double */
528  if (vectorize && toplocal_) {
529  int cnt;
530  cnt = 0;
531  ITERATE(q, toplocal_) {
532  if (SYM(q)->assigned_to_ != 2) {
533  if (SYM(q)->subtype & ARRAY) {
534  cnt += SYM(q)->araydim;
535  } else {
536  ++cnt;
537  }
538  }
539  }
540  Sprintf(buf,
541  " _thread[%d] = {neuron::container::do_not_search, new double[%d]{}};\n",
543  cnt);
545  Sprintf(buf, " delete[] _thread[%d].get<double*>();\n", thread_data_index);
547  cnt = 0;
548  ITERATE(q, toplocal_) {
549  if (SYM(q)->assigned_to_ != 2) {
550  if (SYM(q)->subtype & ARRAY) {
551  Sprintf(buf,
552  "#define %s (_thread[%d].get<double*>() + %d)\n",
553  SYM(q)->name,
555  cnt);
556  cnt += SYM(q)->araydim;
557  } else {
558  Sprintf(buf,
559  "#define %s _thread[%d].get<double*>()[%d]\n",
560  SYM(q)->name,
562  cnt);
563  ++cnt;
564  }
565  } else { /* stay file static */
566  if (SYM(q)->subtype & ARRAY) {
567  Sprintf(buf, "static double %s[%d];\n", SYM(q)->name, SYM(q)->araydim);
568  } else {
569  Sprintf(buf, "static double %s;\n", SYM(q)->name);
570  }
571  }
573  }
575  }
576  /* per thread global data */
577  gind = 0;
578  if (vectorize) {
579  SYMLISTITER {
580  s = SYM(q);
581  if (s->nrntype & (NRNGLOBAL) && s->assigned_to_ == 1) {
582  if (s->subtype & ARRAY) {
583  gind += s->araydim;
584  } else {
585  ++gind;
586  }
587  }
588  }
589  }
590  /* double scalars declared internally */
591  Lappendstr(defs_list, "/* declare global and static user variables */\n");
592  Sprintf(buf, "#define gind %d\n", gind);
594  if (!gind) {
595  Sprintf(buf, "#define _gth 0\n");
597  }
598  if (gind) {
599  Sprintf(buf,
600  "static int _thread1data_inuse = 0;\nstatic double _thread1data[%d];\n#define _gth "
601  "%d\n",
602  gind,
605  Sprintf(buf,
606  "if (_thread1data_inuse) {\n"
607  " _thread[_gth] = {neuron::container::do_not_search, new double[%d]{}};\n"
608  "} else {\n"
609  " _thread[_gth] = {neuron::container::do_not_search, _thread1data};\n"
610  " _thread1data_inuse = 1;\n"
611  "}\n",
612  gind);
615  " if (_thread[_gth].get<double*>() == _thread1data) {\n "
616  "_thread1data_inuse = 0;\n "
617  "}else{\n delete[] _thread[_gth].get<double*>();\n }\n");
619  }
620  gind = 0;
621  SYMLISTITER { /* globals are now global with respect to C as well as hoc */
622  s = SYM(q);
623  if (s->nrntype & (NRNGLOBAL)) {
624  if (vectorize && s->assigned_to_ == 1) {
625  if (s->subtype & ARRAY) {
626  Sprintf(buf,
627  "#define %s%s (_thread1data + %d)\n\
628 #define %s (_globals + %d)\n",
629  s->name,
630  suffix,
631  gind,
632  s->name,
633  gind);
634  } else {
635  Sprintf(buf,
636  "#define %s%s _thread1data[%d]\n\
637 #define %s _globals[%d]\n",
638  s->name,
639  suffix,
640  gind,
641  s->name,
642  gind);
643  }
644  q1 = Lappendstr(defs_list, buf);
645  q1->itemtype = VERBATIM;
646  if (s->subtype & ARRAY) {
647  gind += s->araydim;
648  } else {
649  ++gind;
650  }
651  continue;
652  }
653  if (suffix[0]) {
654  Sprintf(buf, "#define %s %s%s\n", s->name, s->name, suffix);
655  q1 = Lappendstr(defs_list, buf);
656  q1->itemtype = VERBATIM;
657  }
658  decode_ustr(s, &d1, &d2, buf);
659  if (s->subtype & ARRAY) {
660  Sprintf(buf, "double %s[%d];\n", s->name, s->araydim);
661  } else {
662  Sprintf(buf, "double %s = %g;\n", s->name, d1);
663  }
665  }
666  }
667 
668  emit_check_table_thread = 0;
670  emit_check_table_thread = 1;
671  }
672 
673  Lappendstr(defs_list, "/* some parameters have upper and lower limits */\n");
674  Lappendstr(defs_list, "static HocParmLimits _hoc_parm_limits[] = {\n");
675  SYMLISTITER {
676  s = SYM(q);
677  if (s->subtype & PARM) {
678  double d1 = 0., d2 = 0.;
679  if (decode_limits(s, &d1, &d2)) {
680  if (s->nrntype & NRNGLOBAL || !point_process) {
681  Sprintf(buf, "{\"%s%s\", %g, %g},\n", s->name, suffix, d1, d2);
682  } else {
683  Sprintf(buf, "{\"%s\", %g, %g},\n", s->name, d1, d2);
684  }
686  }
687  }
688  }
689  Lappendstr(defs_list, "{0, 0, 0}\n};\n");
690 
691  units_reg();
692 
693  SYMLISTITER {
694  s = SYM(q);
695  if (s->nrntype & (NRNSTATIC)) {
696  decode_ustr(s, &d1, &d2, buf);
697  if (s->subtype & ARRAY) {
698  Sprintf(buf, "static double %s[%d];\n", s->name, s->araydim);
699  } else {
700  Sprintf(buf, "static double %s = %g;\n", s->name, d1);
701  }
703  }
704  }
705  Lappendstr(defs_list, "/* connect global user variables to hoc */\n");
706  Lappendstr(defs_list, "static DoubScal hoc_scdoub[] = {\n");
707  ITERATE(q, syminorder) {
708  s = SYM(q);
709  if (s->nrntype & NRNGLOBAL && !(s->subtype & ARRAY)) {
710  Sprintf(buf, "{\"%s%s\", &%s%s},\n", s->name, suffix, s->name, suffix);
712  }
713  }
714  Lappendstr(defs_list, "{0, 0}\n};\n");
715 
716  /* double vectors */
717  Lappendstr(defs_list, "static DoubVec hoc_vdoub[] = {\n");
718  ITERATE(q, syminorder) {
719  s = SYM(q);
720  if (s->nrntype & NRNGLOBAL && (s->subtype & ARRAY)) {
721  Sprintf(buf, "{\"%s%s\", %s%s, %d},\n", s->name, suffix, s->name, suffix, s->araydim);
723  }
724  }
725  Lappendstr(defs_list, "{0, 0, 0}\n};\n");
726  Lappendstr(defs_list, "static double _sav_indep;\n");
727  if (ba_index_ > 0) {
728  for (int i = 1; i <= ba_index_; ++i) {
729  Sprintf(buf,
730  "static void _ba%d(Node*_nd, Datum* _ppd, Datum* _thread, NrnThread* _nt, "
731  "Memb_list* _ml, size_t _iml, _nrn_model_sorted_token const&);\n",
732  i);
734  }
735  }
736 
737  /* function to set up _p and _ppvar */
738  Lappendstr(defs_list, "extern void _nrn_setdata_reg(int, void(*)(Prop*));\n");
739  Lappendstr(defs_list, "static void _setdata(Prop* _prop) {\n");
740  if (!point_process) {
741  Lappendstr(defs_list, "_extcall_prop = _prop;\n");
742  Lappendstr(defs_list, "_prop_id = _nrn_get_prop_id(_prop);\n");
743  }
744  if (!vectorize) {
746  "neuron::legacy::set_globals_from_prop(_prop, _ml_real, _ml, _iml);\n"
747  "_ppvar = _nrn_mechanism_access_dparam(_prop);\n");
748  if (!artificial_cell) {
750  "Node * _node = _nrn_mechanism_access_node(_prop);\n"
751  "v = _nrn_mechanism_access_voltage(_node);\n");
752  }
753  }
754  Lappendstr(defs_list, "}\n");
755 
756  if (point_process) {
757  Lappendstr(defs_list, "static void _hoc_setdata(void* _vptr) { Prop* _prop;\n");
758  Lappendstr(defs_list, "_prop = ((Point_process*)_vptr)->_prop;\n");
759  } else {
761  "static void _hoc_setdata() {\n Prop *_prop, *hoc_getdata_range(int);\n");
762  Sprintf(buf, "_prop = hoc_getdata_range(_mechtype);\n");
764  }
765  Lappendstr(defs_list, " _setdata(_prop);\n");
766  if (point_process) {
767  Lappendstr(defs_list, "}\n");
768  } else {
769  Lappendstr(defs_list, "hoc_retpushx(1.);\n}\n");
770  }
771 
772 
773  /******** what normally goes into cabvars.h structures */
774 
775  /*declaration of the range variables names to HOC */
776  Lappendstr(
777  defs_list,
778  "static void nrn_alloc(Prop*);\n"
779  "static void nrn_init(_nrn_model_sorted_token const&, NrnThread*, Memb_list*, int);\n"
780  "static void nrn_state(_nrn_model_sorted_token const&, NrnThread*, Memb_list*, int);\n");
781  if (brkpnt_exists) {
782  Lappendstr(
783  defs_list,
784  "static void nrn_cur(_nrn_model_sorted_token const&, NrnThread*, Memb_list*, int);\n"
785  "static void nrn_jacob(_nrn_model_sorted_token const&, NrnThread*, "
786  "Memb_list*, int);\n");
787  }
788  /* count the number of pointers needed */
789  num_random_vars = 0;
791  num_random_vars++;
792  }
793  ppvar_cnt = ioncount + diamdec + pointercount + num_random_vars + areadec;
794  if (net_send_seen_) {
797  ppvar_cnt,
798  "netsend",
799  "_tqitem",
800  "void*" /* TQItem* really, but that's not defined in translated MOD file code */);
801  ppvar_cnt++;
802  }
803  if (watch_seen_) {
805  for (i = 0; i < watch_seen_; ++i) {
806  // TODO: improve type safety by not using void* here
807  ppvar_semantics(i + ppvar_cnt, "watch", "_watch_array", "void*");
808  }
810  Sprintf(buf, "\n#define _watch_array _ppvar + %d", watch_index);
812  Lappendstr(defs_list, "\n");
813  Lappendstr(defs_list, "static void _watch_alloc(Datum*);\n");
814  Lappendstr(defs_list, "extern void hoc_reg_watch_allocate(int, void(*)(Datum*));");
815  Lappendstr(watch_alloc, "}\n\n");
817  }
818  if (for_netcons_) {
819  Sprintf(buf, "\n#define _fnc_index %d\n", ppvar_cnt);
821  // TODO: improve type safety by not using void* here
822  ppvar_semantics(ppvar_cnt, "fornetcon", "_fnc_index", "void*");
823  ppvar_cnt += 1;
824  }
825  if (point_process) {
826  Lappendstr(defs_list, "static void _hoc_destroy_pnt(void* _vptr) {\n");
827  if (watch_seen_ || for_netcons_) {
828  Lappendstr(defs_list, " Prop* _prop = ((Point_process*)_vptr)->_prop;\n");
829  }
830  if (watch_seen_) {
831  Sprintf(buf,
832  " if (_prop) { _nrn_free_watch(_nrn_mechanism_access_dparam(_prop), "
833  "%d, %d);}\n",
834  watch_index,
835  watch_seen_);
837  }
838  if (for_netcons_) {
839  Sprintf(buf,
840  " if (_prop) { "
841  "_nrn_free_fornetcon(&(_nrn_mechanism_access_dparam(_prop)[_fnc_index]"
842  ".literal_value<void*>()));}\n");
844  }
845  Lappendstr(defs_list, " destroy_point_process(_vptr);\n}\n");
846  }
847  if (cvode_emit) {
849  ppvar_semantics(ppvar_cnt, "cvodeieq", "_cvode_ieq", "int");
850  ppvar_cnt++;
851  }
854  if (!point_process) {
855  diag("DESTRUCTOR only permitted for POINT_PROCESS", (char*) 0);
856  }
857  Lappendstr(defs_list, "static void _destructor(Prop*);\n");
858  }
859 
861  Lappendstr(defs_list, "static void _constructor(Prop*);\n");
862  }
863 
865  "/* connect range variables in _p that hoc is supposed to know about */\n");
867  "\
868 static const char *_mechanism[] = {\n\
869 ");
870  Sprintf(buf, "\"%s\",\n\"%s\",\n", nmodl_version_, mechname);
872  ITERATE(q, rangeparm) {
873  s = SYM(q);
874  if (s->subtype & ARRAY) {
875  Sprintf(buf, "\"%s%s[%d]\",\n", s->name, rsuffix, s->araydim);
876  } else {
877  Sprintf(buf, "\"%s%s\",\n", s->name, rsuffix);
878  }
880  }
881  Lappendstr(defs_list, "0,\n");
882  ITERATE(q, rangedep) {
883  s = SYM(q);
884  if (s->subtype & ARRAY) {
885  Sprintf(buf, "\"%s%s[%d]\",\n", s->name, rsuffix, s->araydim);
886  } else {
887  Sprintf(buf, "\"%s%s\",\n", s->name, rsuffix);
888  }
890  }
891  Lappendstr(defs_list, "0,\n");
892  ITERATE(q, rangestate) {
893  s = SYM(q);
894  if (s->subtype & ARRAY) {
895  Sprintf(buf, "\"%s%s[%d]\",\n", s->name, rsuffix, s->araydim);
896  } else {
897  Sprintf(buf, "\"%s%s\",\n", s->name, rsuffix);
898  }
900  }
901  Lappendstr(defs_list, "0,\n");
902 
903  /* pointer variable names */
904  ITERATE(q, nrnpointers) {
905  s = SYM(q);
906  if (s->subtype & ARRAY) {
907  Sprintf(buf, "\"%s%s[%d]\",\n", s->name, rsuffix, s->araydim);
908  } else {
909  Sprintf(buf, "\"%s%s\",\n", s->name, rsuffix);
910  }
912  }
913 
914  Lappendstr(defs_list, "0};\n");
915 
916  /*********Creation of the allocation function*/
917 
918  if (diamdec) {
919  Lappendstr(defs_list, "static Symbol* _morphology_sym;\n");
920  }
921  if (areadec) {
922  Lappendstr(defs_list, "extern Node* nrn_alloc_node_;\n");
923  }
924  ITERATE(q, useion) {
925  sion = SYM(q);
926  Sprintf(buf, "static Symbol* _%s_sym;\n", sion->name);
928  if (ldifuslist) {
929  Sprintf(buf, "static int _type_i%s;\n", sion->name);
931  }
932  q = q->next->next->next;
933  }
934 
936  Item* before_nrn_alloc = lappendstr(defs_list, "\n");
937 
939  "\n"
940  "extern Prop* need_memb(Symbol*);\n"
941  "static void nrn_alloc(Prop* _prop) {\n"
942  " Prop *prop_ion{};\n"
943  " Datum *_ppvar{};\n");
944  if (point_process) {
946  " if (nrn_point_prop_) {\n"
947  " _nrn_mechanism_access_alloc_seq(_prop) = "
948  "_nrn_mechanism_access_alloc_seq(nrn_point_prop_);\n"
949  " _ppvar = _nrn_mechanism_access_dparam(nrn_point_prop_);\n"
950  " } else {\n");
951  }
952  // need to fill _prop->dparam before calling _nrn_mechanism_cache_range(Prop*)
953  if (ppvar_cnt) {
954  Sprintf(buf, " _ppvar = nrn_prop_datum_alloc(_mechtype, %d, _prop);\n", ppvar_cnt);
956  Lappendstr(defs_list, " _nrn_mechanism_access_dparam(_prop) = _ppvar;\n");
957  }
958  // seems that even in the old code and with vectorize == false that the global _p, _ppvar were
959  // shadowed, so don't worry about shadowing the global _ml and _iml here
960  Sprintf(buf,
961  " _nrn_mechanism_cache_instance _ml_real{_prop};\n"
962  " auto* const _ml = &_ml_real;\n"
963  " size_t const _iml{};\n"
964  " assert(_nrn_mechanism_get_num_vars(_prop) == %d);\n",
965  parraycount);
967  Lappendstr(defs_list, " /*initialize range parameters*/\n");
968 
969  // _parm_default allows implementation of a more robust NrnProperty
970  // that does not require a call to prop_alloc.
971  /* arrays have only a single default value of 0.0 */
972  i = 0;
973  insertstr(defs_list_parm_default, "\n /* Used by NrnProperty */\n");
974  insertstr(defs_list_parm_default, "static _nrn_mechanism_std_vector<double> _parm_default{\n");
975  ITERATE(q, rangeparm) {
976  s = SYM(q);
977  if (s->subtype & ARRAY) {
978  d1 = 0.0;
979  } else {
980  decode_ustr(s, &d1, &d2, buf);
981  Sprintf(buf, " %s = _parm_default[%d]; /* %g */\n", s->name, i, d1);
983  }
984  /* fill in the std::vector<double> _parm_default initializer */
985  Sprintf(buf, " %g, /* %s */\n", d1, s->name);
987  ++i;
988  }
990 
991  if (point_process) {
992  Lappendstr(defs_list, " }\n");
993  }
994  Sprintf(buf, "\t assert(_nrn_mechanism_get_num_vars(_prop) == %d);\n", parraycount);
996  if (ppvar_cnt) {
997  Lappendstr(defs_list, "\t_nrn_mechanism_access_dparam(_prop) = _ppvar;\n");
998  Lappendstr(defs_list, "\t/*connect ionic variables to this model*/\n");
999  }
1000  if (diamdec) {
1001  Sprintf(buf, "prop_ion = need_memb(_morphology_sym);\n");
1003  Sprintf(buf,
1004  "\t_ppvar[%d] = _nrn_mechanism_get_param_handle(prop_ion, 0); /* diam */\n",
1005  ioncount + pointercount),
1007  ppvar_semantics(ioncount + pointercount, "diam", "diam", "double*");
1008  }
1009  if (areadec) {
1010  Sprintf(buf,
1011  "\t_ppvar[%d] = _nrn_mechanism_get_area_handle(nrn_alloc_node_);\n",
1012  ioncount + pointercount + diamdec),
1014  ppvar_semantics(ioncount + pointercount + diamdec, "area", "area", "double*");
1015  }
1016 
1017  if (point_process) {
1018  ioncount = 2;
1019  } else {
1020  ioncount = 0;
1021  }
1022  ITERATE(q, useion) {
1023  int dcurdef = 0;
1024  int need_style = 0;
1025  sion = SYM(q);
1026  Sprintf(buf, "prop_ion = need_memb(_%s_sym);\n", sion->name);
1028  if (ldifuslist) {
1029  Sprintf(buf, " _type_i%s = _nrn_mechanism_get_type(prop_ion);\n", sion->name);
1031  }
1032  ion_promote(q);
1033  q = q->next;
1034  ITERATE(q1, LST(q)) {
1035  SYM(q1)->nrntype |= NRNIONFLAG;
1036  Sprintf(buf,
1037  "\t_ppvar[%d] = _nrn_mechanism_get_param_handle(prop_ion, %d); /* %s */\n",
1038  ioncount++,
1039  iontype(SYM(q1)->name, sion->name),
1040  SYM(q1)->name);
1042  }
1043  q = q->next;
1044  ITERATE(q1, LST(q)) {
1045  int itype = iontype(SYM(q1)->name, sion->name);
1046 
1047  if (SYM(q1)->nrntype & NRNIONFLAG) {
1048  SYM(q1)->nrntype &= ~NRNIONFLAG;
1049  } else {
1050  Sprintf(buf,
1051  "\t_ppvar[%d] = _nrn_mechanism_get_param_handle(prop_ion, %d); /* %s */\n",
1052  ioncount++,
1053  itype,
1054  SYM(q1)->name);
1056  }
1057  if (itype == IONCUR) {
1058  dcurdef = 1;
1059  Sprintf(buf,
1060  "\t_ppvar[%d] = _nrn_mechanism_get_param_handle(prop_ion, %d); /* "
1061  "_ion_di%sdv */\n",
1062  ioncount++,
1063  IONDCUR,
1064  sion->name);
1066  }
1067  if (itype == IONIN || itype == IONOUT) {
1068  need_style = 1;
1069  }
1070  }
1071  if (need_style) {
1072  Sprintf(buf,
1073  "\t_ppvar[%d] = _nrn_mechanism_get_param_handle(prop_ion, %d); // erev %s\n",
1074  ioncount++,
1075  IONEREV,
1076  sion->name);
1078  Sprintf(buf,
1079  "\t_ppvar[%d] = {neuron::container::do_not_search, "
1080  "&(_nrn_mechanism_access_dparam(prop_ion)[0].literal_value<int>())}; /* "
1081  "iontype for %s */\n",
1082  ioncount++,
1083  sion->name);
1085  }
1086  q = q->next;
1087  if (!dcurdef && ldifuslist) {
1088  Sprintf(
1089  buf,
1090  "\t_ppvar[%d] = _nrn_mechanism_get_param_handle(prop_ion, %d); /* _ion_di%sdv */\n",
1091  ioncount++,
1092  IONDCUR,
1093  sion->name);
1095  }
1096  }
1097 
1098 
1099  // I've put all the nrn_mech_inst_destruct here with nmodlrandoms allocation.
1100  // Refactor if ever things other than nmodlrandoms need it.
1102  ITERATE(q, nmodlrandoms) {
1103  Sprintf(buf, "_p_%s = (void*)nrnran123_newstream();\n", SYM(q)->name);
1105  Sprintf(buf, "nrnran123_deletestream(%s);\n", SYM(q)->name);
1107  }
1109  auto& list = nrn_mech_inst_destruct_list;
1110  // registration just means adding to nrn_mech_inst_destruct
1111  Lappendstr(defs_list, "nrn_mech_inst_destruct[_mechtype] = _mech_inst_destruct;\n");
1112  // boilerplate for _mech_inst_destruct
1113  Linsertstr(list,
1114  "\nstatic void _mech_inst_destruct(Prop* _prop) {\n"
1115  " Datum* _ppvar = _nrn_mechanism_access_dparam(_prop);\n");
1116  Lappendstr(list, "}\n");
1117  movelist(list->next, list->prev, procfunc);
1118  // need a forward declaration before nrn_alloc.
1119  insertstr(before_nrn_alloc, "\nstatic void _mech_inst_destruct(Prop* _prop);\n");
1120  }
1121 
1123  Lappendstr(defs_list, "if (!nrn_point_prop_) {_constructor(_prop);}\n");
1124  if (vectorize) {
1126  "\n"
1127  "static void _constructor(Prop* _prop) {\n"
1128  " _nrn_mechanism_cache_instance _ml_real{_prop};\n"
1129  " auto* const _ml = &_ml_real;\n"
1130  " size_t const _iml{};\n"
1131  " Datum *_ppvar{_nrn_mechanism_access_dparam(_prop)}, *_thread{};\n"
1132  " {\n");
1133  } else {
1135  "\n"
1136  "static void _constructor(Prop* _prop) {\n"
1137  " neuron::legacy::set_globals_from_prop(_prop, _ml_real, _ml, _iml);\n"
1138  " _ppvar = _nrn_mechanism_access_dparam(_prop);\n"
1139  " {\n");
1140  }
1142  Lappendstr(procfunc, "\n}\n}\n");
1143  }
1144  Lappendstr(defs_list, "\n}\n");
1145 
1146  Lappendstr(defs_list, "static void _initlists();\n");
1147  if (cvode_emit) {
1148  Lappendstr(defs_list, " /* some states have an absolute tolerance */\n");
1149  Lappendstr(defs_list, "static Symbol** _atollist;\n");
1150  Lappendstr(defs_list, "static HocStateTolerance _hoc_state_tol[] = {\n");
1151  ITERATE(q, rangestate) {
1152  double d1;
1153  s = SYM(q);
1154  if (decode_tolerance(s, &d1)) {
1155  if (!point_process) {
1156  Sprintf(buf, "{\"%s%s\", %g},\n", s->name, suffix, d1);
1157  } else {
1158  Sprintf(buf, "{\"%s\", %g},\n", s->name, d1);
1159  }
1161  }
1162  }
1163  Lappendstr(defs_list, "{0, 0}\n};\n");
1164  }
1165 
1166  if (net_send_seen_) {
1167  if (!net_receive_) {
1168  diag("can't use net_send if there is no NET_RECEIVE block", (char*) 0);
1169  }
1170  Sprintf(buf, "\n#define _tqitem &(_ppvar[%d])\n", tqitem_index);
1172  if (net_send_delivered_) {
1173  insertstr(net_send_delivered_, " if (_lflag == 1. ) {*(_tqitem) = nullptr;}\n");
1174  }
1175  }
1176  if (net_receive_) {
1177  Lappendstr(defs_list, "static void _net_receive(Point_process*, double*, double);\n");
1178  if (for_netcons_) {
1179  Lappendstr(defs_list, "extern int _nrn_netcon_args(void*, double***);\n");
1180  }
1181  if (net_init_q1_) {
1182  Lappendstr(defs_list, "static void _net_init(Point_process*, double*, double);\n");
1183  }
1184  }
1186  Lappendstr(defs_list, "static void _thread_mem_init(Datum*);\n");
1187  }
1189  Lappendstr(defs_list, "static void _thread_cleanup(Datum*);\n");
1190  }
1191  if (use_bbcorepointer) {
1193  "static void bbcore_write(double*, int*, int*, int*, _threadargsproto_);\n");
1195  "extern void hoc_reg_bbcore_write(int, void(*)(double*, int*, int*, int*, "
1196  "_threadargsproto_));\n");
1198  "static void bbcore_read(double*, int*, int*, int*, _threadargsproto_);\n");
1200  "extern void hoc_reg_bbcore_read(int, void(*)(double*, int*, int*, int*, "
1201  "_threadargsproto_));\n");
1202  }
1204  "\
1205 extern Symbol* hoc_lookup(const char*);\n\
1206 extern void _nrn_thread_reg(int, int, void(*)(Datum*));\n\
1207 void _nrn_thread_table_reg(int, nrn_thread_table_check_t);\n\
1208 extern void hoc_register_tolerance(int, HocStateTolerance*, Symbol***);\n\
1209 extern void _cvode_abstol( Symbol**, double*, int);\n\n\
1210 ");
1211  Sprintf(buf,
1212  "extern \"C\" void _%s_reg() {\n\
1213  int _vectorized = %d;\n",
1214  modbase,
1215  vectorize);
1217  q = lappendstr(defs_list, "");
1218  Lappendstr(defs_list, "_initlists();\n");
1219 
1220  if (suffix[0]) { /* not "nothing" */
1221 
1222  ITERATE(q, useion) {
1223  Sprintf(buf, "\tion_reg(\"%s\", %s);\n", SYM(q)->name, STR(q->next->next->next));
1225  q = q->next->next->next;
1226  }
1227  if (diamdec) {
1228  Lappendstr(defs_list, "\t_morphology_sym = hoc_lookup(\"morphology\");\n");
1229  }
1230  ITERATE(q, useion) {
1231  Sprintf(buf, "\t_%s_sym = hoc_lookup(\"%s_ion\");\n", SYM(q)->name, SYM(q)->name);
1233  q = q->next->next->next;
1234  }
1235  if (point_process) {
1236  Sprintf(buf,
1237  "\
1238  _pointtype = point_register_mech(_mechanism,\n\
1239  nrn_alloc,%s, nrn_init,\n\
1240  hoc_nrnpointerindex, %d,\n\
1241  _hoc_create_pnt, _hoc_destroy_pnt, _member_func);\n",
1242  brkpnt_str_,
1243  vectorize ? 1 + thread_data_index : 0);
1246  Lappendstr(defs_list, " register_destructor(_destructor);\n");
1247  }
1248  } else {
1249  Sprintf(buf,
1250  "\
1251  register_mech(_mechanism, nrn_alloc,%s, nrn_init, hoc_nrnpointerindex, %d);\n",
1252  brkpnt_str_,
1253  vectorize ? 1 + thread_data_index : 0);
1255  }
1256  if (vectorize && thread_data_index) {
1257  Sprintf(buf, " _extcall_thread.resize(%d);\n", thread_data_index);
1260  Lappendstr(defs_list, " _thread_mem_init(_extcall_thread.data());\n");
1261  if (gind) {
1262  Lappendstr(defs_list, " _thread1data_inuse = 0;\n");
1263  }
1264  }
1265  }
1266  Lappendstr(defs_list, "_mechtype = nrn_get_mechtype(_mechanism[1]);\n");
1267  Lappendstr(defs_list, "hoc_register_parm_default(_mechtype, &_parm_default);\n");
1268  if (!point_process) {
1270  " hoc_register_npy_direct(_mechtype, npy_direct_func_proc);\n");
1271  }
1272  lappendstr(defs_list, " _nrn_setdata_reg(_mechtype, _setdata);\n");
1274  lappendstr(defs_list, " _nrn_thread_reg(_mechtype, 1, _thread_mem_init);\n");
1275  }
1277  lappendstr(defs_list, " _nrn_thread_reg(_mechtype, 0, _thread_cleanup);\n");
1278  }
1279  if (emit_check_table_thread) {
1280  lappendstr(defs_list, " _nrn_thread_table_reg(_mechtype, _check_table_thread);\n");
1281  }
1282  if (use_bbcorepointer) {
1283  lappendstr(defs_list, " hoc_reg_bbcore_write(_mechtype, bbcore_write);\n");
1284  lappendstr(defs_list, " hoc_reg_bbcore_read(_mechtype, bbcore_read);\n");
1285  }
1286  if (nmodl_text) {
1288  "#if NMODL_TEXT\n register_nmodl_text_and_filename(_mechtype);\n#endif\n");
1289  }
1290  std::sort(ppvar_data_field_strings.begin(), ppvar_data_field_strings.end());
1293  std::back_inserter(data_field_strings),
1294  [](auto const& pair) { return pair.second; });
1295  std::string register_data_fields{" _nrn_mechanism_register_data_fields("};
1296  auto const prefix_length = register_data_fields.size() +
1297  1 /* defs_list handling adds this */;
1298  register_data_fields.append("_mechtype");
1299  for (auto const& data_field_str: data_field_strings) {
1300  register_data_fields.append(",\n");
1301  register_data_fields.append(prefix_length, ' ');
1302  register_data_fields.append(data_field_str);
1303  }
1304  register_data_fields.append(");\n");
1306  Sprintf(buf, " hoc_register_prop_size(_mechtype, %d, %d);\n", prop_size, ppvar_cnt);
1308  if (watch_seen_) {
1309  Lappendstr(defs_list, " hoc_reg_watch_allocate(_mechtype, _watch_alloc);\n");
1310  }
1311  if (ppvar_semantics_)
1313  Sprintf(buf,
1314  " hoc_register_dparam_semantics(_mechtype, %d, \"%s\");\n",
1315  (int) q->itemtype,
1316  q->element.str);
1318  }
1319  /* Models that write concentration need their INITIAL blocks called
1320  before those that read the concentration or reversal potential. */
1321  i = 0;
1322  ITERATE(q, useion) {
1323  ITERATE(q1, LST(q->next->next)) {
1324  int type;
1325  type = iontype(SYM(q1)->name, SYM(q)->name);
1326  if (type == IONIN || type == IONOUT) {
1327  i += 1;
1328  }
1329  }
1330  q = q->next->next->next;
1331  }
1332  if (i) {
1333  Lappendstr(defs_list, "\tnrn_writes_conc(_mechtype, 0);\n");
1334  }
1335 
1336  if (cvode_emit) {
1338  "\
1339  hoc_register_cvode(_mechtype, _ode_count, _ode_map, _ode_spec, _ode_matsol);\n");
1341  "\
1342  hoc_register_tolerance(_mechtype, _hoc_state_tol, &_atollist);\n");
1343  if (ion_synonym) {
1344  Lappendstr(defs_list, " hoc_register_synonym(_mechtype, _ode_synonym);\n");
1345  }
1346  } else if (cvode_not_allowed) {
1348  "\
1349  hoc_register_cvode(_mechtype, _ode_count, 0, 0, 0);\n");
1350  }
1351  if (artificial_cell) {
1353  useion->next != useion) {
1354  printf(
1355  "Notice: ARTIFICIAL_CELL is a synonym for POINT_PROCESS which hints that it\n\
1356 only affects and is affected by discrete events. As such it is not\n\
1357 located in a section and is not associated with an integrator\n");
1358  }
1359  Sprintf(buf, "add_nrn_artcell(_mechtype, %d);\n", tqitem_index);
1361  }
1362  if (net_event_seen_) {
1363  Lappendstr(defs_list, "add_nrn_has_net_event(_mechtype);\n");
1364  }
1365  if (net_receive_) {
1366  Lappendstr(defs_list, "pnt_receive[_mechtype] = _net_receive;\n");
1367  if (net_init_q1_) {
1368  Lappendstr(defs_list, "pnt_receive_init[_mechtype] = _net_init;\n");
1369  }
1370  Sprintf(buf, "pnt_receive_size[_mechtype] = %d;\n", net_receive_);
1372  }
1373  if (for_netcons_) {
1374  Sprintf(buf, "add_nrn_fornetcons(_mechtype, _fnc_index);\n");
1376  }
1377  q = ba_list_;
1378  for (i = 1; i <= ba_index_; ++i) {
1379  List* lst;
1380  q = q->next;
1381  if (electrode_current) {
1382  insertstr(ITM(q),
1383  " \
1384 #if EXTRACELLULAR\n\
1385 if (auto* const _extnode = _nrn_mechanism_access_extnode(_nd); _extnode) {\n\
1386  v = NODEV(_nd) + _extnode->_v[0];\n\
1387 }else\n\
1388 #endif\n\
1389 {\n\
1390  v = NODEV(_nd);\n\
1391 }\n");
1392  } else {
1393  insertstr(ITM(q), " v = NODEV(_nd);\n");
1394  }
1395  lst = get_ion_variables(0);
1396  if (lst->next != lst->prev) {
1397  move(lst->next, lst->prev, ITM(q));
1398  freelist(&lst);
1399  }
1400  q = q->next;
1401  lst = set_ion_variables(0);
1402  if (lst->next != lst->prev) {
1403  move(lst->next, lst->prev, ITM(q));
1404  freelist(&lst);
1405  }
1406  q = q->next;
1407  Sprintf(buf, "\thoc_reg_ba(_mechtype, _ba%d, %s);\n", i, STR(q));
1409  }
1410  if (ldifuslist) {
1411  Lappendstr(defs_list, "\thoc_register_ldifus1(_difusfunc);\n");
1412  // don't use _nrn_model_sorted_token here because this is being inserted at the start of
1413  // defs_list, before _nrn_model_sorted_token is defined
1415  "static void _difusfunc(ldifusfunc2_t, neuron::model_sorted_token const&, "
1416  "NrnThread&);\n");
1417  }
1418  } /* end of not "nothing" */
1420  "\n"
1421  " hoc_register_var(hoc_scdoub, hoc_vdoub, hoc_intfunc);\n");
1422  {
1423  char buf1[NRN_BUFSIZE];
1424 #if !defined(NRN_AVOID_ABSOLUTE_PATHS)
1425  Sprintf(buf1,
1426  "\tivoc_help(\"help ?1 %s %s\\n\");\n",
1427  mechname,
1428  fs::absolute(finname).c_str());
1429 #else
1430  Sprintf(buf1,
1431  "\tivoc_help(\"help ?1 %s %s\\n\");\n",
1432  mechname,
1433  fs::path(finname).filename().c_str());
1434 #endif
1435  Lappendstr(defs_list, buf1);
1436  }
1437  if (suffix[0]) {
1438  Lappendstr(defs_list, "hoc_register_limits(_mechtype, _hoc_parm_limits);\n");
1439  Lappendstr(defs_list, "hoc_register_units(_mechtype, _hoc_parm_units);\n");
1440  }
1441  Lappendstr(defs_list, "}\n"); /* end of _reg */
1443  Lappendstr(procfunc, "\nstatic void _thread_mem_init(Datum* _thread) {\n");
1445  Lappendstr(procfunc, "}\n");
1446  }
1448  Lappendstr(procfunc, "\nstatic void _thread_cleanup(Datum* _thread) {\n");
1450  Lappendstr(procfunc, "}\n");
1451  }
1453  if (vectorize) {
1455  "\n"
1456  "static void _destructor(Prop* _prop) {\n"
1457  " _nrn_mechanism_cache_instance _ml_real{_prop};\n"
1458  " auto* const _ml = &_ml_real;\n"
1459  " size_t const _iml{};\n"
1460  " Datum *_ppvar{_nrn_mechanism_access_dparam(_prop)}, *_thread{};\n"
1461  " {\n");
1462  } else {
1464  "\n"
1465  "static void _destructor(Prop* _prop) {\n"
1466  " neuron::legacy::set_globals_from_prop(_prop, _ml_real, _ml, _iml);\n"
1467  " _ppvar = _nrn_mechanism_access_dparam(_prop);\n"
1468  " {\n");
1469  }
1471  Lappendstr(procfunc, "\n}\n}\n");
1472  }
1473  if (ldifuslist) {
1474  ldifusreg();
1475  }
1476  SYMLISTITER {
1477  s = SYM(q);
1478  if ((s->subtype & PARM)) {
1479  warn_ignore(s);
1480  }
1481  }
1482 }
1483 
1484 
1485 // Check if read/write variable from USEION is declared in a
1486 // CONSTANT block. There are certain MOD files where this pattern
1487 // is used and it doesn't produce a desired code. Hence, we check
1488 // all ion variables and error if any variable is declared as CONSTANT.
1489 void check_ion_vars_as_constant(char* ion_name, const List* ion_var_list) {
1490  const Item* var;
1491  ITERATE(var, ion_var_list) {
1492  const Symbol* var_sym = SYM(var);
1493  int type = iontype(var_sym->name, ion_name);
1494  if (type == IONIN || type == IONOUT || type == IONCUR || type == IONCONC ||
1495  type == IONEREV) {
1496  if (var_sym->subtype & nmodlCONST) {
1497  diag(var_sym->name,
1498  " used in USEION statement can not be re-declared in a CONSTANT block");
1499  }
1500  }
1501  }
1502 }
1503 
1504 static void check_sufficient_ion_read_statements(std::string const& ion_name,
1505  List* read_variables,
1506  List* write_variables) {
1507  auto const have_type = [ion_name, read_variables, write_variables](int type) {
1508  for (auto* const ion_var_list: {read_variables, write_variables}) {
1509  Item* var;
1510  ITERATE(var, ion_var_list) {
1511  const Symbol* var_sym = SYM(var);
1512  if (iontype(var_sym->name, const_cast<char*>(ion_name.c_str())) == type) {
1513  return true;
1514  }
1515  }
1516  }
1517  return false;
1518  };
1519  auto const add_readion = [read_variables](std::string name) {
1520  auto* const sym = install(name.c_str(), NAME);
1521  sym->nrntype |= IONCONC;
1522  sym->nrntype |= IONCONC_IMPLICIT;
1523  lappendsym(read_variables, sym);
1524  };
1525  bool const have_ionin{have_type(IONIN)}, have_ionout{have_type(IONOUT)};
1526  if (have_ionin && !have_ionout) {
1527  add_readion(ion_name + "o");
1528  } else if (have_ionout && !have_ionin) {
1529  add_readion(ion_name + "i");
1530  }
1531 }
1532 
1533 // check semantics of read & write variables from USEION statements
1535  const Item* ion_var;
1536  ITERATE(ion_var, useion) {
1537  // with SoA data then if we emit any calls for nrn_wrote_conc then we need explicit READ
1538  // statements for all arguments
1540  LST(ion_var->next),
1541  LST(ion_var->next->next));
1542  // read variables
1543  check_ion_vars_as_constant(SYM(ion_var)->name, LST(ion_var->next));
1544  // write variables
1545  check_ion_vars_as_constant(SYM(ion_var)->name, LST(ion_var->next->next));
1546  ion_var = ion_var->next->next->next;
1547  }
1548 }
1549 
1550 
1552  int b;
1553  double d1, d2;
1554  b = 0;
1555  if (s->nrntype & (NRNEXTRN | NRNPRANGEIN | NRNPRANGEOUT))
1556  b = 1;
1557  if (strcmp(s->name, "v") == 0)
1558  b = 1;
1559 
1560  decode_ustr(s, &d1, &d2, buf);
1561  if (d1 == 0.0)
1562  b = 0;
1563  if (b) {
1564  printf("Warning: Default %g of PARAMETER %s will be ignored and set by NEURON.\n",
1565  d1,
1566  s->name);
1567  }
1568 }
1569 
1570 void ldifusreg() {
1571  Item *q, *qdexp, *qb1, *qvexp, *qb2, *q1;
1572  char *cfindex, *dfdcur;
1573  Symbol *s, *d;
1574  int n;
1575 
1576  /* ldifuslist format: series of symbol qdexp qb1 svexp qb2
1577  indexforflux dflux/dconc */
1578  n = 0;
1579  ITERATE(q, ldifuslist) {
1580  s = SYM(q);
1581  q = q->next;
1582  qdexp = ITM(q);
1583  q = q->next;
1584  qb1 = ITM(q);
1585  q = q->next;
1586  qvexp = ITM(q);
1587  q = q->next;
1588  qb2 = ITM(q);
1589  q = q->next;
1590  cfindex = STR(q);
1591  q = q->next;
1592  dfdcur = STR(q);
1593  ++n;
1594  Sprintf(
1595  buf,
1596  "static void* _difspace%d;\n"
1597  "extern double nrn_nernst_coef(int);\n"
1598  "static double _difcoef%d(int _i, Memb_list* _ml_arg, size_t _iml, Datum* _ppvar, "
1599  "double* _pdvol, double* _pdfcdc, Datum* _thread, NrnThread* _nt, "
1600  "_nrn_model_sorted_token const& _sorted_token) {\n"
1601  " _nrn_mechanism_cache_range _lmr{_sorted_token, *_nt, *_ml_arg, _ml_arg->_type()};\n"
1602  " auto* const _ml = &_lmr;\n"
1603  " double* _globals = nullptr;\n"
1604  " if (gind != 0 && _thread != nullptr) { _globals = _thread[_gth].get<double*>(); }\n"
1605  " *_pdvol = ",
1606  n,
1607  n);
1609  for (q1 = qvexp; q1 != qb2; q1 = q1->next) {
1610  lappenditem(procfunc, q1);
1611  }
1612  if (dfdcur[0]) {
1613  Sprintf(buf,
1614  ";\n\
1615  if (_i == %s) {\n *_pdfcdc = %s;\n }else{ *_pdfcdc=0.;}\n",
1616  cfindex,
1617  dfdcur);
1618  } else {
1619  Sprintf(buf, "; *_pdfcdc=0.;\n");
1620  }
1622  lappendstr(procfunc, " return");
1623  for (q1 = qdexp; q1 != qb1; q1 = q1->next) {
1624  lappenditem(procfunc, q1);
1625  }
1626  lappendstr(procfunc, ";\nreturn 0;\n}\n");
1627  }
1629  "static void _difusfunc(ldifusfunc2_t _f, _nrn_model_sorted_token const& "
1630  "sorted_token, NrnThread& _nt) {int _i;\n");
1631  n = 0;
1632  ITERATE(q, ldifuslist) {
1633  s = SYM(q);
1634  q = q->next;
1635  qdexp = ITM(q);
1636  q = q->next;
1637  qb1 = ITM(q);
1638  q = q->next;
1639  qvexp = ITM(q);
1640  q = q->next;
1641  qb2 = ITM(q);
1642  q = q->next;
1643  cfindex = STR(q);
1644  q = q->next;
1645  dfdcur = STR(q);
1646  ++n;
1647 
1648  if (s->subtype & ARRAY) {
1649  Sprintf(buf,
1650  " for (_i=0; _i < %d; ++_i) (*_f)(_mechtype, _difcoef%d, &_difspace%d, _i, ",
1651  s->araydim,
1652  n,
1653  n);
1654  } else {
1655  Sprintf(buf, " (*_f)(_mechtype, _difcoef%d, &_difspace%d, 0, ", n, n);
1656  }
1658 
1659  Sprintf(buf, "D%s", s->name);
1660  d = lookup(buf);
1661  assert(d);
1662  if (s->nrntype & IONCONC) {
1663  Sprintf(buf, "%d, %d", -(s->ioncount_ + 1), d->varnum);
1664  } else {
1665  Sprintf(buf, "%d, %d", s->varnum, d->varnum);
1666  }
1668  lappendstr(procfunc, ", sorted_token, _nt);\n");
1669  }
1670  lappendstr(procfunc, "}\n");
1671 }
1672 
1673 int decode_limits(Symbol* sym, double* pg1, double* pg2) {
1674  int i;
1675  if (sym->subtype & PARM) {
1676  char* cp;
1677  int n;
1678  assert(sym->u.str);
1679  for (n = 0, cp = sym->u.str; *cp; ++cp) {
1680  if (*cp == '\n') {
1681  ++n;
1682  if (n == 3) {
1683  ++cp;
1684  break;
1685  }
1686  }
1687  }
1688  i = sscanf(cp, "%lf %lf\n", pg1, pg2);
1689  if (i == 2) {
1690  return 1;
1691  }
1692  }
1693  return 0;
1694 }
1695 
1696 int decode_tolerance(Symbol* sym, double* pg1) {
1697  int i;
1698  if (sym->subtype & STAT) {
1699  char* cp;
1700  int n;
1701  for (n = 0, cp = sym->u.str; *cp; ++cp) {
1702  if (*cp == '\n') {
1703  ++n;
1704  if (n == 3) {
1705  ++cp;
1706  break;
1707  }
1708  }
1709  }
1710  i = sscanf(cp, "%lf\n", pg1);
1711  if (i == 1) {
1712  return 1;
1713  }
1714  }
1715  return 0;
1716 }
1717 
1718 void decode_ustr(Symbol* sym, double* pg1, double* pg2, char* s) /* decode sym->u.str */
1719 {
1720  int i, n;
1721  char *cp, *cp1;
1722 
1723  switch (sym->subtype & (INDEP | DEP | STAT | PARM)) {
1724  case INDEP: /* but doesnt get all info */
1725  case DEP:
1726  case STAT:
1727  assert(sym && sym->u.str);
1728  if (sym->subtype & ARRAY) { /* see parsact.c */
1729  i = sscanf(sym->u.str, "[%*d]\n%lf%*c%lf", pg1, pg2);
1730  } else {
1731  i = sscanf(sym->u.str, "%lf%*c%lf", pg1, pg2);
1732  }
1733  assert(i == 2);
1734  for (n = 0, cp = sym->u.str; n < 2;) {
1735  if (*cp++ == '\n') {
1736  n++;
1737  }
1738  }
1739  for (cp1 = s; *cp != '\n';) {
1740  *cp1++ = *cp++;
1741  }
1742  *cp1 = '\0';
1743  break;
1744 
1745  case PARM:
1746  assert(sym && sym->u.str);
1747  if (sym->subtype & ARRAY) { /* see parsact.c */
1748  i = sscanf(sym->u.str, "[%*d]\n%lf\n%s", pg1, s);
1749  } else {
1750  i = sscanf(sym->u.str, "%lf\n%s", pg1, s);
1751  }
1752  if (i == 1) {
1753  s[0] = '\0';
1754  i = 2;
1755  }
1756  assert(i == 2);
1757  break;
1758  default:
1759  diag(sym->name, " does not have a proper declaration");
1760  }
1761  if (s[0] == '0') {
1762  s[0] = '\0';
1763  }
1764 }
1765 
1766 void units_reg() {
1767  Symbol* s;
1768  Item* q;
1769  double d1, d2;
1770  char u[NRN_BUFSIZE];
1771 
1772  Lappendstr(defs_list, "static HocParmUnits _hoc_parm_units[] = {\n");
1773  ITERATE(q, syminorder) {
1774  s = SYM(q);
1775  if (s->nrntype & NRNGLOBAL) {
1776  decode_ustr(s, &d1, &d2, u);
1777  if (u[0]) {
1778  SprintfAsrt(buf, "{\"%s%s\", \"%s\"},\n", s->name, suffix, u);
1780  }
1781  }
1782  }
1783  ITERATE(q, rangeparm) {
1784  s = SYM(q);
1785  decode_ustr(s, &d1, &d2, u);
1786  if (u[0]) {
1787  SprintfAsrt(buf, "{\"%s%s\", \"%s\"},\n", s->name, rsuffix, u);
1789  }
1790  }
1791  ITERATE(q, rangestate) {
1792  s = SYM(q);
1793  decode_ustr(s, &d1, &d2, u);
1794  if (u[0]) {
1795  SprintfAsrt(buf, "{\"%s%s\", \"%s\"},\n", s->name, rsuffix, u);
1797  }
1798  }
1799  ITERATE(q, rangedep) {
1800  s = SYM(q);
1801  decode_ustr(s, &d1, &d2, u);
1802  if (u[0]) {
1803  SprintfAsrt(buf, "{\"%s%s\", \"%s\"},\n", s->name, rsuffix, u);
1805  }
1806  }
1807  ITERATE(q, nrnpointers) {
1808  s = SYM(q);
1809  decode_ustr(s, &d1, &d2, u);
1810  if (u[0]) {
1811  SprintfAsrt(buf, "{\"%s%s\", \"%s\"},\n", s->name, rsuffix, u);
1813  }
1814  }
1815  Lappendstr(defs_list, "{0, 0}\n};\n");
1816 }
1817 
1818 static void var_count(Symbol* s) {
1819  defs_h(s);
1820  s->used = varcount++;
1821  s->varnum = parraycount;
1822  std::string field{"_nrn_mechanism_field<double>{\""};
1823  field.append(s->name);
1824  field.append(1, '"');
1825  if (s->subtype & ARRAY) {
1826  field.append(", ");
1827  field.append(std::to_string(s->araydim));
1828  prop_size += s->araydim;
1829  } else {
1830  prop_size += 1;
1831  }
1832  // **ATTENTION** in AoS NEURON then parraycount was incremented by s->araydim if the variable
1833  // was an array. In SoA NEURON this is not done; the array dimension is communicated separately.
1834  ++parraycount;
1835  field.append("} /* ");
1836  field.append(std::to_string(s->varnum));
1837  field.append(" */");
1838  data_field_strings.push_back(std::move(field));
1839 }
1840 
1841 void defs_h(Symbol* s) {
1842  Item* q;
1843 
1844  if (s->subtype & ARRAY) {
1845  Sprintf(buf,
1846  "#define %s _ml->template data_array<%d, %d>(_iml)\n"
1847  "#define %s_columnindex %d\n",
1848  s->name,
1849  parraycount,
1850  s->araydim,
1851  s->name,
1852  parraycount);
1853  q = lappendstr(defs_list, buf);
1854  } else {
1855  Sprintf(buf,
1856  "#define %s _ml->template fpfield<%d>(_iml)\n"
1857  "#define %s_columnindex %d\n",
1858  s->name,
1859  parraycount,
1860  s->name,
1861  parraycount);
1862  q = lappendstr(defs_list, buf);
1863  }
1864  q->itemtype = VERBATIM;
1865 }
1866 
1867 void nrn_list(Item* q1, Item* q2) {
1868  List** plist = (List**) 0;
1869  Item* q;
1870 
1871  switch (SYM(q1)->type) {
1872  case RANGE:
1873  plist = (List**) 0;
1874  for (q = q1->next; q != q2->next; q = q->next) {
1875  SYM(q)->nrntype |= NRNRANGE;
1876  }
1877  break;
1878  case SUFFIX:
1879  plist = (List**) 0;
1880  mechname = SYM(q2)->name;
1881  if (strcmp(SYM(q1)->name, "POINT_PROCESS") == 0) {
1882  point_process = 1;
1883  } else if (strcmp(SYM(q1)->name, "ARTIFICIAL_CELL") == 0) {
1884  point_process = 1;
1885  artificial_cell = 1;
1886  }
1887  break;
1888  case ELECTRODE_CURRENT:
1889  electrode_current = 1;
1890  case NONSPECIFIC:
1891  plist = &currents;
1892  for (q = q1->next; q != q2->next; q = q->next) {
1893  SYM(q)->nrntype |= NRNRANGE;
1894  }
1895  break;
1896  case GLOBAL:
1897  for (q = q1->next; q != q2->next; q = q->next) {
1898  SYM(q)->nrntype |= NRNGLOBAL | NRNNOTP;
1899  }
1900  plist = (List**) 0;
1901  break;
1902  case EXTERNAL:
1903  threadsafe("Use of EXTERNAL is not thread safe.");
1904  for (q = q1->next; q != q2->next; q = q->next) {
1905  SYM(q)->nrntype |= NRNEXTRN | NRNNOTP;
1906  }
1907  plist = (List**) 0;
1908  break;
1909  case POINTER:
1910  threadsafe("Use of POINTER is not thread safe.");
1911  plist = &nrnpointers;
1912  for (q = q1->next; q != q2->next; q = q->next) {
1913  SYM(q)->nrntype |= NRNNOTP | NRNPOINTER;
1914  }
1915  break;
1916  case BBCOREPOINTER:
1917  threadsafe("Use of BBCOREPOINTER is not thread safe.");
1918  plist = &nrnpointers;
1919  for (q = q1->next; q != q2->next; q = q->next) {
1920  SYM(q)->nrntype |= NRNNOTP | NRNBBCOREPOINTER;
1921  }
1922  use_bbcorepointer = 1;
1923  break;
1924  case RANDOM:
1925  for (q = q1->next; q != q2->next; q = q->next) {
1926  Symbol* s = SYM(q);
1927  if (s->type != NAME || s->subtype || s->nrntype) {
1928  diag(s->name, " cannot be redeclared as RANDOM");
1929  }
1930  s->nrntype |= NRNNOTP | EXTDEF_RANDOM;
1931  s->type = RANDOMVAR;
1932  }
1933  plist = &nmodlrandoms;
1934  break;
1935  }
1936  if (plist) {
1937  if (!*plist) {
1938  *plist = newlist();
1939  }
1940  assert(q1 != q2);
1941  movelist(q1->next, q2, *plist);
1942  }
1943 }
1944 
1945 void bablk(int ba, int type, Item* q1, Item* q2) {
1946  Item *qb, *qv, *q;
1947  qb = insertstr(q1->prev->prev, "/*");
1948  insertstr(q1, "*/\n");
1949  if (!ba_list_) {
1950  ba_list_ = newlist();
1951  }
1952  Sprintf(buf,
1953  "static void _ba%d(Node*_nd, Datum* _ppd, Datum* _thread, NrnThread* _nt, Memb_list* "
1954  "_ml_arg, size_t _iml, _nrn_model_sorted_token const& _sorted_token) ",
1955  ++ba_index_);
1956  insertstr(q1, buf);
1957  q = q1->next;
1958  vectorize_substitute(insertstr(q, ""), "Datum* _ppvar;");
1959  qv = insertstr(
1960  q,
1961  "_nrn_mechanism_cache_range _lmr{_sorted_token, *_nt, *_ml_arg, "
1962  "_ml_arg->_type()}; auto* const "
1963  "_ml = &_lmr;\n"
1964  "double* _globals = nullptr;\n"
1965  "if (gind != 0 && _thread != nullptr) { _globals = _thread[_gth].get<double*>(); }\n");
1966  qv = insertstr(q, "_ppvar = _ppd;\n");
1967  movelist(qb, q2, procfunc);
1968 
1969  ba = (ba == BEFORE) ? 10 : 20; /* BEFORE or AFTER */
1970  ba += (type == BREAKPOINT) ? 1 : 0;
1971  ba += (type == SOLVE) ? 2 : 0;
1972  ba += (type == INITIAL1) ? 3 : 0;
1973  ba += (type == STEP) ? 4 : 0;
1974  lappenditem(ba_list_, qv->next);
1975  lappenditem(ba_list_, q2);
1976  Sprintf(buf, "%d", ba);
1978 }
1979 
1981  Item* q;
1982  int used = 0;
1983  ITERATE(q, useion) {
1984  if (SYM(q) == s) {
1985  used = 1;
1986  }
1987  q = q->next->next->next;
1988  }
1989  return used;
1990 }
1991 
1992 void nrn_use(Item* q1, Item* q2, Item* q3, Item* q4) {
1993  int used, i;
1994  Item *q, *qr, *qw;
1995  List *readlist, *writelist;
1996  Symbol* ion;
1997 
1998  ion = SYM(q1);
1999  /* is it already used */
2000  used = ion_declared(SYM(q1));
2001  if (used) { /* READ gets promoted to WRITE */
2002  diag("merging of neuron models not supported", (char*) 0);
2003  } else { /* create all the ionic variables */
2004  Lappendsym(useion, ion);
2005  readlist = newlist();
2006  writelist = newlist();
2007  qr = lappendsym(useion, SYM0);
2008  qw = lappendsym(useion, SYM0);
2009  if (q4) {
2010  lappendstr(useion, STR(q4));
2011  } else {
2012  lappendstr(useion, "-10000.");
2013  }
2014  LST(qr) = readlist;
2015  LST(qw) = writelist;
2016  if (q2) {
2017  Item* qt = q2->next;
2018  move(q1->next->next, q2, readlist);
2019  if (q3) {
2020  move(qt->next, q3, writelist);
2021  }
2022  } else if (q3) {
2023  move(q1->next->next, q3, writelist);
2024  }
2025  ITERATE(q, readlist) {
2026  i = iontype(SYM(q)->name, ion->name);
2027  if (i == IONCUR) {
2028  SYM(q)->nrntype |= NRNCURIN;
2029  } else {
2030  SYM(q)->nrntype |= NRNPRANGEIN;
2031  if (i == IONIN || i == IONOUT) {
2032  SYM(q)->nrntype |= IONCONC;
2033  }
2034  }
2035  }
2036  ITERATE(q, writelist) {
2037  i = iontype(SYM(q)->name, ion->name);
2038  if (i == IONCUR) {
2039  if (!currents) {
2040  currents = newlist();
2041  }
2042  Lappendsym(currents, SYM(q));
2043  SYM(q)->nrntype |= NRNCUROUT;
2044  } else {
2045  SYM(q)->nrntype |= NRNPRANGEOUT;
2046  if (i == IONIN || i == IONOUT) {
2047  SYM(q)->nrntype |= IONCONC;
2048  }
2049  }
2050  }
2051  }
2052 }
2053 
2054 int iontype(char* s1, char* s2) /* returns index of variable in ion mechanism */
2055 {
2056  Sprintf(buf, "i%s", s2);
2057  if (strcmp(buf, s1) == 0) {
2058  return IONCUR;
2059  }
2060  Sprintf(buf, "e%s", s2);
2061  if (strcmp(buf, s1) == 0) {
2062  return IONEREV;
2063  }
2064  Sprintf(buf, "%si", s2);
2065  if (strcmp(buf, s1) == 0) {
2066  return IONIN;
2067  }
2068  Sprintf(buf, "%so", s2);
2069  if (strcmp(buf, s1) == 0) {
2070  return IONOUT;
2071  }
2072  Sprintf(buf, "%s is not a valid ionic variable for %s", s1, s2);
2073  diag(buf, (char*) 0);
2074  return -1;
2075 }
2076 
2077 static Symbol* ifnew_install(const char* name) {
2078  Symbol* s;
2079 
2080  if ((s = lookup(name)) == SYM0) {
2081  s = install(name, NAME);
2082  parminstall(s, "0", "", "");
2083  }
2084  return s;
2085 }
2086 
2087 void nrndeclare() {
2088  Symbol* s;
2089  Item* q;
2090 
2091  s = lookup("diam");
2092  if (s) {
2093  if (s->nrntype & (NRNRANGE | NRNGLOBAL)) {
2094  diag(s->name, "cannot be a RANGE or GLOBAL variable for this mechanism");
2095  }
2096  s->nrntype |= NRNNOTP | NRNPRANGEIN;
2097  diamdec = 1;
2098  }
2099  s = lookup("area");
2100  if (s) {
2101  if (s->nrntype & (NRNRANGE | NRNGLOBAL)) {
2102  diag(s->name, "cannot be a RANGE or GLOBAL variable for this mechanism");
2103  }
2104  s->nrntype |= NRNNOTP | NRNPRANGEIN;
2105  areadec = 1;
2106  }
2107  if (vectorize) {
2108  s = ifnew_install("v");
2109  s->nrntype = NRNNOTP; /* this is a lie, it goes in at end specially */
2110  } else {
2111  s = ifnew_install("v");
2112  s->nrntype |= NRNSTATIC | NRNNOTP;
2113  }
2114  s = ifnew_install("t");
2115  s->nrntype |= NRNEXTRN | NRNNOTP;
2116  s = ifnew_install("dt");
2117  s->nrntype |= NRNEXTRN | NRNNOTP;
2119  "\n#define t nrn_threads->_t\n#define dt nrn_threads->_dt\n"),
2120  "\n#define t _nt->_t\n#define dt _nt->_dt\n");
2121 
2122  s = lookup("usetable");
2123  if (s) {
2124  s->nrntype |= NRNGLOBAL | NRNNOTP;
2125  }
2126  s = lookup("celsius");
2127  if (s) {
2128  s->nrntype |= NRNEXTRN | NRNNOTP;
2129  }
2130  s = lookup("celcius");
2131  if (s)
2132  diag("celcius should be spelled celsius", (char*) 0);
2133 
2134  ITERATE(q, syminorder) {
2135  s = SYM(q);
2136  if (s->type == NAME || s->type == PRIME) {
2137  if (s->subtype & PARM && s->nrntype & NRNRANGE) {
2139  } else if (s->subtype & STAT) {
2140  s->nrntype |= NRNRANGE;
2142  } else if (s->subtype & DEP && s->nrntype & NRNRANGE) {
2143  Lappendsym(rangedep, s);
2144  }
2145  if (s != indepsym && !s->nrntype) {
2146  if (s->subtype & PARM) {
2147  if (s->usage & EXPLICIT_DECL) {
2148  s->nrntype |= NRNGLOBAL;
2149  s->nrntype |= NRNNOTP;
2150  } else {
2151  s->nrntype |= NRNSTATIC;
2152  s->nrntype |= NRNNOTP;
2153  }
2154  }
2155  }
2156  }
2157  }
2158  /* some ionic variables don't need duplicates known to hoc */
2162 }
2163 
2165  Item *q, *q1;
2166  Symbol* s;
2167 
2168  for (q = ((Item*) range)->next; q != (Item*) range; q = q1) {
2169  q1 = q->next;
2170  s = SYM(q);
2171  if (s->nrntype & (NRNPRANGEIN | NRNPRANGEOUT)) {
2172  remove(q);
2173  }
2174  }
2175 }
2176 
2177 
2178 void declare_p() {
2179  Item* q;
2180  Symbol* s;
2181 
2182  ITERATE(q, syminorder) {
2183  SYM(q)->used = -1;
2184  }
2185  ITERATE(q, rangeparm) {
2186  var_count(SYM(q));
2187  }
2188  ITERATE(q, rangedep) {
2189  var_count(SYM(q));
2190  }
2191  ITERATE(q, rangestate) {
2192  var_count(SYM(q));
2193  }
2194  ITERATE(q, syminorder) {
2195  if (!(SYM(q)->nrntype & NRNNOTP) && SYM(q)->used < 0) {
2196  var_count(SYM(q));
2197  }
2198  }
2199  if (vectorize) {
2200  s = ifnew_install("v");
2201  var_count(s);
2202  }
2203  if (brkpnt_exists) {
2204  s = ifnew_install("_g");
2205  var_count(s);
2206  }
2207  if (debugging_ && net_receive_) {
2208  s = ifnew_install("_tsav");
2209  var_count(s);
2210  }
2212  "namespace {\n"
2213  "template <typename T>\n"
2214  "using _nrn_mechanism_std_vector = std::vector<T>;\n"
2215  "using _nrn_model_sorted_token = neuron::model_sorted_token;\n"
2216  "using _nrn_mechanism_cache_range = "
2217  "neuron::cache::MechanismRange<number_of_floating_point_variables, "
2218  "number_of_datum_variables>;\n"
2219  "using _nrn_mechanism_cache_instance = "
2220  "neuron::cache::MechanismInstance<number_of_floating_point_variables, "
2221  "number_of_datum_variables>;\n"
2222  "using _nrn_non_owning_id_without_container = "
2223  "neuron::container::non_owning_identifier_without_container;\n"
2224  "template <typename T>\n"
2225  "using _nrn_mechanism_field = neuron::mechanism::field<T>;\n"
2226  "template <typename... Args>\n"
2227  "void _nrn_mechanism_register_data_fields(Args&&... args) {\n"
2228  " neuron::mechanism::register_data_fields(std::forward<Args>(args)...);\n"
2229  "}\n"
2230  "}\n")
2231  ->itemtype = VERBATIM;
2232  Sprintf(buf, "static constexpr auto number_of_floating_point_variables = %d;\n", parraycount);
2233  linsertstr(defs_list, buf)->itemtype = VERBATIM;
2234 }
2235 
2237 /* 0 means equation block , 2 means initial block */
2238 {
2239  /*ARGSUSED*/
2240  Item *q, *q1, *qconc;
2241  char* in;
2242  static List* l;
2243 
2244  l = newlist();
2245  ITERATE(q, useion) {
2246  in = SYM(q)->name;
2247  q = q->next;
2248  q = q->next;
2249  qconc = (Item*) 0;
2250  ITERATE(q1, LST(q)) {
2251  if (SYM(q1)->nrntype & NRNCUROUT) {
2252  if (block == 0) {
2253  Sprintf(buf,
2254  " _ion_%s += %s",
2255  SYM(q1)->name,
2256  breakpoint_current(SYM(q1))->name);
2257  Lappendstr(l, buf);
2258  if (point_process) {
2259  Sprintf(buf, "* 1.e2/ (_nd_area);\n");
2260  } else {
2261  Sprintf(buf, ";\n");
2262  }
2263  } else {
2264  buf[0] = '\0';
2265  }
2266  } else {
2267  if (iontype(SYM(q1)->name, in) != IONEREV) {
2268  qconc = q1;
2269  }
2270  Sprintf(buf, " _ion_%s = %s;\n", SYM(q1)->name, SYM(q1)->name);
2271  }
2272  Lappendstr(l, buf);
2273  }
2274  q = q->next;
2275  /* when INITIAL block is called, if it modifies the concentrations
2276  then the reversal potential should be recomputed in case
2277  other mechanisms need the true initial value. This would be
2278  rare since most initial blocks do not depend on erev. Instead
2279  the right value will be present due to fcurrent or cvode f(y).
2280  However, this fastidiousness cant hurt. It just makes ion_style
2281  in effect always at least for initialization.
2282  */
2283  /* sure enough, someone needed to demote the ion_style so
2284  that erev is decoupled from concentrations. So we need
2285  another variable pointing to the ionstyle
2286  */
2287  if (block == 2 && qconc) {
2288  int const ic = iontype(SYM(qconc)->name, in);
2289  assert(ic == IONIN || ic == IONOUT);
2290  // first arg is just for the charge, last arg is the style. the old
2291  // code with a single double* as a 2nd parameter was problematic as
2292  // it implicitly assumed AoS format; now we require that explicit
2293  // names are defined for erev and the internal/external concentrations
2294  Sprintf(buf,
2295  " nrn_wrote_conc(_%s_sym, _ion_%s_erev, _ion_%si, _ion_%so, _style_%s);\n",
2296  in,
2297  in,
2298  in,
2299  in,
2300  in);
2301  Lappendstr(l, buf);
2302  }
2303  }
2304  return l;
2305 }
2306 
2308 /* 0 means equation block */
2309 /* 2 means ode_spec and ode_matsol blocks */
2310 {
2311  /*ARGSUSED*/
2312  Item *q, *q1;
2313  static List* l;
2314 
2315  l = newlist();
2316  ITERATE(q, useion) {
2317  q = q->next;
2318  ITERATE(q1, LST(q)) {
2319  if (SYM(q1)->nrntype & IONCONC_IMPLICIT) {
2320  continue;
2321  }
2322  if (block == 2 && (SYM(q1)->nrntype & IONCONC) && (SYM(q1)->subtype & STAT)) {
2323  continue;
2324  }
2325  Sprintf(buf, " %s = _ion_%s;\n", SYM(q1)->name, SYM(q1)->name);
2326  Lappendstr(l, buf);
2327  if (point_process && (SYM(q1)->nrntype & NRNCURIN)) {
2328  Fprintf(stderr,
2329  "WARNING: Dimensions may be wrong for READ %s with POINT_PROCESS\n",
2330  SYM(q1)->name);
2331  }
2332  }
2333  q = q->next;
2334  ITERATE(q1, LST(q)) {
2335  if (SYM(q1)->nrntype & IONCONC_IMPLICIT) {
2336  continue;
2337  }
2338  if (block == 2 && (SYM(q1)->nrntype & IONCONC) && (SYM(q1)->subtype & STAT)) {
2339  continue;
2340  }
2341  if (SYM(q1)->nrntype & IONCONC) {
2342  Sprintf(buf, " %s = _ion_%s;\n", SYM(q1)->name, SYM(q1)->name);
2343  Lappendstr(l, buf);
2344  }
2345  if (SYM(q1)->subtype & STAT) {
2346  if (SYM(q1)->nrntype & NRNCUROUT) {
2347  Fprintf(stderr,
2348  "WARNING: WRITE %s with it a STATE may not be translated correctly\n",
2349  SYM(q1)->name);
2350  }
2351  }
2352  }
2353  q = q->next;
2354  }
2355  return l;
2356 }
2357 
2358 int iondef(int* p_pointercount) {
2359  int ioncount, it, need_style;
2360  Item *q, *q1, *q2;
2361  Symbol* sion;
2362  char ionname[256];
2363 
2364  ioncount = 0;
2365  if (point_process) {
2366  ioncount = 2;
2367  q = lappendstr(defs_list, "#define _nd_area *_ml->dptr_field<0>(_iml)\n");
2368  q->itemtype = VERBATIM;
2369  ppvar_semantics(0, "area", "_nd_area", "double*");
2370  ppvar_semantics(1, "pntproc", "_pntproc" /* I made this up*/, "Point_process*");
2371  }
2372  ITERATE(q, useion) {
2373  int dcurdef = 0;
2374  need_style = 0;
2375  sion = SYM(q);
2376  Sprintf(ionname, "%s_ion", sion->name);
2377  q = q->next;
2378  ITERATE(q1, LST(q)) {
2379  SYM(q1)->nrntype |= NRNIONFLAG;
2380  std::string name{"_ion_"};
2381  name.append(SYM(q1)->name);
2382  Sprintf(
2383  buf,
2384  "#define %s *(_ml->dptr_field<%d>(_iml))\n"
2385  "#define _p%s static_cast<neuron::container::data_handle<double>>(_ppvar[%d])\n",
2386  name.c_str(),
2387  ioncount,
2388  name.c_str(),
2389  ioncount);
2390  q2 = lappendstr(defs_list, buf);
2391  q2->itemtype = VERBATIM;
2392  SYM(q1)->ioncount_ = ioncount;
2393  ppvar_semantics(ioncount,
2394  ionname,
2395  ("_ion_" + std::string{SYM(q1)->name}).c_str(),
2396  "double*");
2397  ioncount++;
2398  }
2399  q = q->next;
2400  ITERATE(q1, LST(q)) {
2401  if (SYM(q1)->nrntype & NRNIONFLAG) {
2402  SYM(q1)->nrntype &= ~NRNIONFLAG;
2403  } else {
2404  std::string name{"_ion_"};
2405  name.append(SYM(q1)->name);
2406  Sprintf(buf,
2407  "#define %s *(_ml->dptr_field<%d>(_iml))\n"
2408  "#define _p%s "
2409  "static_cast<neuron::container::data_handle<double>>(_ppvar[%d])\n",
2410  name.c_str(),
2411  ioncount,
2412  name.c_str(),
2413  ioncount);
2414  q2 = lappendstr(defs_list, buf);
2415  q2->itemtype = VERBATIM;
2416  SYM(q1)->ioncount_ = ioncount;
2417  ppvar_semantics(ioncount, ionname, name.c_str(), "double*");
2418  ioncount++;
2419  }
2420  it = iontype(SYM(q1)->name, sion->name);
2421  if (it == IONCUR) {
2422  dcurdef = 1;
2423  std::string name{"_ion_di"};
2424  name.append(sion->name);
2425  name.append("dv");
2426  Sprintf(buf, "#define %s *(_ml->dptr_field<%d>(_iml))\n", name.c_str(), ioncount);
2427  q2 = lappendstr(defs_list, buf);
2428  q2->itemtype = VERBATIM;
2429  ppvar_semantics(ioncount, ionname, name.c_str(), "double*");
2430  ioncount++;
2431  }
2432  if (it == IONIN || it == IONOUT) { /* would have wrote_ion_conc */
2433  need_style = 1;
2434  }
2435  }
2436  if (need_style) {
2437  // Need to be able to explicitly reference this when calling
2438  // nrn_wrote_conc, the old code navigated to this value via pointer
2439  // arithmetic that is not valid now the mechanism data are stored in
2440  // SOA format
2441  std::string name{"_ion_"};
2442  name.append(sion->name);
2443  name.append("_erev");
2444  Sprintf(buf, "#define %s *_ml->dptr_field<%d>(_iml)\n", name.c_str(), ioncount);
2445  q2 = lappendstr(defs_list, buf);
2446  q2->itemtype = VERBATIM;
2447  ppvar_semantics(ioncount, ionname, name.c_str(), "double*");
2448  ioncount++;
2449  std::string stylename{"_style_"};
2450  stylename.append(sion->name);
2451  Sprintf(buf, "#define %s\t*_ppvar[%d].get<int*>()\n", stylename.c_str(), ioncount);
2452  q2 = lappendstr(defs_list, buf);
2453  q2->itemtype = VERBATIM;
2454  Sprintf(buf, "#%s", ionname);
2455  ppvar_semantics(ioncount, buf, stylename.c_str(), "int*");
2456  ioncount++;
2457  }
2458  q = q->next;
2459  if (!dcurdef && ldifuslist) {
2460  std::string name{"_ion_di"};
2461  name.append(sion->name);
2462  name.append("dv");
2463  Sprintf(buf, "#define %s *_ml->dptr_field<%d>(_iml)\n", name.c_str(), ioncount);
2464  q2 = lappendstr(defs_list, buf);
2465  q2->itemtype = VERBATIM;
2466  ppvar_semantics(ioncount, ionname, name.c_str(), "double*");
2467  ioncount++;
2468  }
2469  }
2470  *p_pointercount = 0;
2471  ITERATE(q, nrnpointers) {
2472  sion = SYM(q);
2473  Sprintf(buf,
2474  "#define %s *_ppvar[%d].get<double*>()\n",
2475  sion->name,
2476  ioncount + *p_pointercount);
2477  sion->used = ioncount + *p_pointercount;
2478  q2 = lappendstr(defs_list, buf);
2479  q2->itemtype = VERBATIM;
2480  Sprintf(buf,
2481  "#define _p_%s _ppvar[%d].literal_value<void*>()\n",
2482  sion->name,
2483  ioncount + *p_pointercount);
2484  sion->used = ioncount + *p_pointercount;
2485  q2 = lappendstr(defs_list, buf);
2486  q2->itemtype = VERBATIM;
2487  if (sion->nrntype & NRNPOINTER) {
2488  ppvar_semantics(ioncount + *p_pointercount, "pointer", sion->name, "double*");
2489  } else {
2490  ppvar_semantics(ioncount + *p_pointercount, "bbcorepointer", sion->name, "double*");
2491  }
2492  (*p_pointercount)++;
2493  }
2494 
2495  // print all RANDOM variables
2496  num_random_vars = 0;
2497  ITERATE(q, nmodlrandoms) {
2498  num_random_vars++;
2499  }
2500  if (num_random_vars) {
2501  Sprintf(buf, "\n //RANDOM variables \n");
2503 
2504  int index = 0;
2505  ITERATE(q, nmodlrandoms) {
2506  Symbol* s = SYM(q);
2507  Sprintf(buf,
2508  "#define %s (nrnran123_State*)_ppvar[%d].get<void*>()\n",
2509  s->name,
2510  ioncount + *p_pointercount + index);
2512  Sprintf(buf,
2513  "#define _p_%s _ppvar[%d].literal_value<void*>()\n",
2514  s->name,
2515  ioncount + *p_pointercount + index);
2517  ppvar_semantics(ioncount + *p_pointercount + index, "random", s->name, "void*");
2518  index++;
2519  }
2520  lappendstr(defs_list, "\n");
2521  }
2522 
2523  if (diamdec) { /* must be last */
2524  Sprintf(buf,
2525  "#define diam (*(_ml->dptr_field<%d>(_iml)))\n",
2526  ioncount + *p_pointercount + num_random_vars);
2527  q2 = lappendstr(defs_list, buf);
2528  q2->itemtype = VERBATIM;
2529  } /* notice that ioncount is not incremented */
2530  if (areadec) { /* must be last, if we add any more the administrative
2531  procedures must be redone */
2532  Sprintf(buf,
2533  "#define area (*(_ml->dptr_field<%d>(_iml)))\n",
2534  ioncount + *p_pointercount + num_random_vars + diamdec);
2535  q2 = lappendstr(defs_list, buf);
2536  q2->itemtype = VERBATIM;
2537  } /* notice that ioncount is not incremented */
2538  Sprintf(buf,
2539  "static constexpr auto number_of_datum_variables = %d;\n",
2540  ioncount + *p_pointercount + num_random_vars + diamdec + areadec);
2541  linsertstr(defs_list, buf)->itemtype = VERBATIM;
2542  return ioncount;
2543 }
2544 
2545 void ppvar_semantics(int i, const char* semantics, const char* name, const char* type) {
2546  Item* q;
2547  if (!ppvar_semantics_) {
2549  }
2550  q = Lappendstr(ppvar_semantics_, const_cast<char*>(semantics)); // TODO - ugly but ok for now
2551  q->itemtype = (short) i;
2552  std::string field{"_nrn_mechanism_field<"};
2553  field.append(type);
2554  field.append(">{\"");
2555  field.append(name);
2556  field.append("\", \"");
2557  field.append(semantics);
2558  field.append("\"} /* ");
2559  field.append(std::to_string(i));
2560  field.append(" */");
2561  // track the index because ppvar_semantics(...) is not necessarily called in order
2562  ppvar_data_field_strings.emplace_back(i, std::move(field));
2563 }
2564 
2566  Item *q, *q1, *qbrak;
2567  static List* l;
2568  char* strion;
2569 
2570  l = newlist();
2571  qbrak = lappendstr(l, "\t{");
2572  ITERATE(q, useion) {
2573  strion = SYM(q)->name;
2574  q = q->next;
2575  q = q->next;
2576  ITERATE(q1, LST(q)) {
2577  if (SYM(q1)->nrntype & NRNCUROUT) {
2578  Sprintf(buf, " _di%s = %s;\n", strion, SYM(q1)->name);
2579  Lappendstr(l, buf);
2580  Sprintf(buf, "double _di%s;\n", strion);
2581  Insertstr(qbrak->next, buf);
2582  }
2583  }
2584  q = q->next;
2585  }
2586  return l;
2587 }
2588 
2589 List* end_dion_stmt(const char* strdel) {
2590  Item *q, *q1;
2591  static List* l;
2592  char* strion;
2593 
2594  l = newlist();
2595  ITERATE(q, useion) {
2596  strion = SYM(q)->name;
2597  q = q->next;
2598  q = q->next;
2599  ITERATE(q1, LST(q)) {
2600  if (SYM(q1)->nrntype & NRNCUROUT) {
2601  Sprintf(
2602  buf, " _ion_di%sdv += (_di%s - %s)/%s", strion, strion, SYM(q1)->name, strdel);
2603  Lappendstr(l, buf);
2604  if (point_process) {
2605  Lappendstr(l, "* 1.e2/ (_nd_area);\n");
2606  } else {
2607  Lappendstr(l, ";\n");
2608  }
2609  }
2610  }
2611  q = q->next;
2612  }
2613  Lappendstr(l, "\t}\n");
2614  return l;
2615 }
2616 
2617 void ion_promote(Item* qion) {
2618  Item* q;
2619  char* in;
2620  int conc, rev;
2621  int type;
2622  conc = 0;
2623  rev = 0;
2624  in = SYM(qion)->name;
2625  ITERATE(q, LST(qion->next)) { /* check READ */
2626  type = iontype(SYM(q)->name, in);
2627  if (type == IONIN || type == IONOUT) {
2628  conc = 1;
2629  }
2630  if (type == IONEREV) {
2631  rev = 1;
2632  }
2633  }
2634  ITERATE(q, LST(qion->next->next)) { /* promote if WRITE */
2635  type = iontype(SYM(q)->name, in);
2636  if (type == IONIN) {
2637  Lappendstr(defs_list, "nrn_check_conc_write(_prop, prop_ion, 1);\n");
2638  conc = 3;
2639  }
2640  if (type == IONOUT) {
2641  Lappendstr(defs_list, "nrn_check_conc_write(_prop, prop_ion, 0);\n");
2642  conc = 3;
2643  }
2644  if (type == IONEREV) {
2645  rev = 3;
2646  }
2647  }
2648  if (conc || rev) {
2649  Sprintf(buf, "nrn_promote(prop_ion, %d, %d);\n", conc, rev);
2651  }
2652 }
2653 
2654 #define NRNFIX(arg) \
2655  if (strcmp(n, arg) == 0) \
2656  e = 1;
2657 
2659  int e;
2660  char* n;
2661  if (s->assigned_to_ == 0) {
2662  s->assigned_to_ = 1;
2663  }
2664  if (protect_) {
2665  s->assigned_to_ = 2;
2666  }
2667  e = 0;
2668  n = s->name;
2669  NRNFIX("area");
2670  NRNFIX("diam");
2671  NRNFIX("t");
2672  NRNFIX("dt");
2673  NRNFIX("celsius");
2674  if (e) {
2675  diag(s->name,
2676  "is a special NEURON variable that should not be\n assigned a value\
2677  in a model description file\n");
2678  }
2679 }
2680 
2681 
2685 
2686 void slist_data(Symbol* s, int indx, int findx) {
2687  /* format: number of pairs, followed by findx, indx pairs */
2688  int* pi;
2689  int i, n;
2690  if (s->slist_info_) {
2691  /* i'd use realloc but to avoid portability problems */
2692  /* this probably will never get executed anyway */
2693  n = s->slist_info_[0] + 1;
2694  pi = (int*) emalloc((1 + 2 * n) * sizeof(int));
2695  for (i = 2 * (n - 1); i > 0; --i) {
2696  pi[i] = s->slist_info_[i];
2697  }
2698  free(s->slist_info_);
2699  s->slist_info_ = pi;
2700  pi[0] = n;
2701  pi[2 * n - 1] = findx;
2702  pi[2 * n] = indx;
2703  } else {
2704  s->slist_info_ = pi = (int*) emalloc(3 * sizeof(int));
2705  pi[0] = 1;
2706  pi[1] = findx;
2707  pi[2] = indx;
2708  }
2709 }
2710 
2711 int slist_search(int n, Symbol* s) {
2712  int i, *pi;
2713  pi = s->slist_info_;
2714  if (pi == (int*) 0) {
2715  diag(s->name, "not really a STATE; Ie. No differential equation for it.\n");
2716  }
2717  assert(pi);
2718  for (i = 0; i < pi[0]; ++i) {
2719  if (pi[1 + 2 * i] == n) {
2720  return pi[2 + 2 * i];
2721  }
2722  }
2723  assert(0);
2724  return 0;
2725 }
2726 
2727 static void cvode_conc_map() {
2728  /* pv index is slist index, ppd index is to the concentration
2729  pointer to the ion concentration is eg. &(ion_cai). Unfortunately
2730  the slist index has nothing to do with the _p array index.
2731  To recover the slist index, an slist_index list was made for
2732  every slist which consists of an slist ordered list of state symbols
2733  */
2734  /*
2735  also must handle case where user WRITE cai but cai is not a STATE
2736  since inefficiency occurs due to inability to set eca when
2737  states are predicted
2738  */
2739  Item *q, *q1, *q2, *q3;
2740  int sindex;
2741  ITERATE(q, useion) {
2742  q = q->next;
2743  q = q->next;
2744  ITERATE(q1, LST(q)) {
2745  if (SYM(q1)->nrntype & IONCONC) {
2746  if ((SYM(q1)->subtype & STAT)) {
2747  sindex = slist_search(cvode_num_, SYM(q1));
2748  Sprintf(buf, "\t_pv[%d] = _p_ion_%s;\n", sindex, SYM(q1)->name);
2750  } else { /* not a STATE but WRITE it*/
2751  /*its got to have an assignment in a SOLVE block and that assignment
2752  better not depend on intermediate variables that depend on states
2753  because we will assign cai using only that statement prior to
2754  calling the nernst equation code.
2755  */
2756  int b = 0;
2757  if (!ion_synonym) {
2758  ion_synonym = newlist();
2759  }
2760  ITERATE(q2, procfunc) {
2761  if (q2->itemtype == SYMBOL && SYM(q2) == SYM(q1)) {
2762  q3 = q2->next;
2763  if (q3->itemtype == SYMBOL && strcmp(SYM(q3)->name, "=") == 0) {
2764  /*printf(" found reference to %s = ...\n", SYM(q2)->name);*/
2765  Sprintf(buf, "_ion_%s = ", SYM(q2)->name);
2767  for (q3 = q3->next; q3 != procfunc->prev; q3 = q3->next) {
2768  lappenditem(ion_synonym, q3);
2769  if (q3->itemtype == SYMBOL && SYM(q3) == semi) {
2770 #if 0
2771  if (q3->itemtype == STRING && strchr(STR(q3), ';')) {
2772  char* e, *s = stralloc(STR(q3), (char*)0);
2773  e = strchr(s, ';');
2774  *e = '\0';
2775  Sprintf(buf, "%s;\n", s);
2776 printf("|%s||%s||%s|\n",STR(q3), s, buf);
2778 #endif
2779  b = 1;
2780  break;
2781  }
2782  }
2783  break;
2784  }
2785  }
2786  }
2787  if (b == 0) {
2788  diag(SYM(q1)->name,
2789  "is WRITE but is not a STATE and has no assignment statement");
2790  }
2791  }
2792  }
2793  }
2794  q = q->next;
2795  }
2796 }
2797 
2799  vectorize_substitute(lappendstr(p, ""), " Datum* _ppvar;\n");
2800  vectorize_substitute(lappendstr(p, ""), " size_t _iml;");
2801  vectorize_substitute(lappendstr(p, ""), " _nrn_mechanism_cache_range* _ml;");
2802  Lappendstr(
2803  p,
2804  " Node* _nd{};\n"
2805  " double _v{};\n"
2806  " int _cntml;\n"
2807  " _nrn_mechanism_cache_range _lmr{_sorted_token, *_nt, *_ml_arg, _type};\n"
2808  " _ml = &_lmr;\n"
2809  " _cntml = _ml_arg->_nodecount;\n"
2810  " Datum *_thread{_ml_arg->_thread};\n"
2811  " double* _globals = nullptr;\n"
2812  " if (gind != 0 && _thread != nullptr) { _globals = _thread[_gth].get<double*>(); }\n"
2813  " for (_iml = 0; _iml < _cntml; ++_iml) {\n"
2814  " _ppvar = _ml_arg->_pdata[_iml];\n"
2815  " _nd = _ml_arg->_nodelist[_iml];\n"
2816  " v = NODEV(_nd);\n");
2817 }
2818 
2820  List* lst;
2821  Item *q, *q1;
2822  if (cvode_not_allowed) {
2824  "\n\
2825 static int _ode_count(int);\n");
2826  Sprintf(buf,
2827  "\n\
2828 static int _ode_count(int _type){ hoc_execerror(\"%s\", \"cannot be used with CVODE\"); return 0;}\n",
2829  mechname);
2831  } else if (cvode_emit) {
2833  "\n\
2834 static int _ode_count(int);\n\
2835 static void _ode_map(Prop*, int, neuron::container::data_handle<double>*, neuron::container::data_handle<double>*, double*, int);\n\
2836 static void _ode_spec(_nrn_model_sorted_token const&, NrnThread*, Memb_list*, int);\n\
2837 static void _ode_matsol(_nrn_model_sorted_token const&, NrnThread*, Memb_list*, int);\n\
2838 ");
2839  Sprintf(buf,
2840  "\n\
2841 static int _ode_count(int _type){ return %d;}\n",
2842  cvode_neq_);
2844  Sprintf(buf, "\n#define _cvode_ieq _ppvar[%d].literal_value<int>()\n", cvode_ieq_index);
2846 
2847  if (cvode_fun_->subtype == PROCED) {
2849  } else {
2851  "\nstatic void _ode_spec(_nrn_model_sorted_token const& _sorted_token, "
2852  "NrnThread* _nt, Memb_list* _ml_arg, int _type) {\n");
2854  lst = get_ion_variables(1);
2855  if (lst->next->itemtype)
2856  movelist(lst->next, lst->prev, procfunc);
2857  Sprintf(buf, " _ode_spec%d", cvode_num_);
2859  vectorize_substitute(lappendstr(procfunc, "();\n"), "(_threadargs_);\n");
2860  lst = set_ion_variables(1);
2861  if (lst->next->itemtype)
2862  movelist(lst->next, lst->prev, procfunc);
2863  Lappendstr(procfunc, "}}\n");
2864 
2866  "\n\
2867 static void _ode_map(Prop* _prop, int _ieq, neuron::container::data_handle<double>* _pv, neuron::container::data_handle<double>* _pvdot, double* _atol, int _type) {");
2868  vectorize_substitute(lappendstr(procfunc, "\n"), "\n Datum* _ppvar;\n");
2869  Sprintf(buf,
2870  " _ppvar = _nrn_mechanism_access_dparam(_prop);\n"
2871  " _cvode_ieq = _ieq;\n"
2872  " for (int _i=0; _i < %d; ++_i) {\n"
2873  " _pv[_i] = _nrn_mechanism_get_param_handle(_prop, _slist%d[_i]);\n"
2874  " _pvdot[_i] = _nrn_mechanism_get_param_handle(_prop, _dlist%d[_i]);\n"
2875  " _cvode_abstol(_atollist, _atol, _i);\n"
2876  " }\n",
2877  cvode_neq_,
2878  cvode_num_,
2879  cvode_num_);
2881  /* need to take care of case where a state is an ion concentration. Replace
2882  the _pp pointer with a pointer to the actual ion model's concentration */
2883  cvode_conc_map();
2884  Lappendstr(procfunc, "}\n");
2885  if (ion_synonym) {
2887  "static void _ode_synonym(_nrn_model_sorted_token const&, "
2888  "NrnThread&, Memb_list&, int);\n");
2890  "static void _ode_synonym(_nrn_model_sorted_token const& "
2891  "_sorted_token, NrnThread& _nt, Memb_list& _ml_arg, int _type) {\n");
2893  "_nrn_mechanism_cache_range _lmr{_sorted_token, _nt, _ml_arg, _type};\n"
2894  "auto* const _ml = &_lmr;\n"
2895  "auto const _cnt = _ml_arg._nodecount;\n"
2896  "for (int _iml = 0; _iml < _cnt; ++_iml) {\n"
2897  " Datum* _ppvar = _ml_arg._pdata[_iml];\n");
2899  Lappendstr(procfunc, " }\n}\n");
2900  }
2901 
2902  Sprintf(buf,
2903  "static void _ode_matsol_instance%d(_internalthreadargsproto_);\n",
2904  cvode_num_);
2906  Sprintf(buf,
2907  "\nstatic void _ode_matsol_instance%d(_internalthreadargsproto_) {\n",
2908  cvode_num_);
2910  if (cvode_fun_->subtype == KINF) {
2911  int i = cvode_num_;
2912  Sprintf(buf,
2913  "_cvode_sparse(&_cvsparseobj%d, %d, _dlist%d, "
2914  "neuron::scopmath::row_view{_ml, _iml}, "
2915  "_ode_matsol%d, &_coef%d);\n",
2916  i,
2917  cvode_neq_,
2918  i,
2919  i,
2920  i);
2922  Sprintf(buf,
2923  "_cvode_sparse_thread(&(_thread[_cvspth%d].literal_value<void*>()), %d, "
2924  "_dlist%d, neuron::scopmath::row_view{_ml, _iml}, _ode_matsol%d, "
2925  "_threadargs_);\n",
2926  i,
2927  cvode_neq_,
2928  i,
2929  i);
2931  } else {
2932  Sprintf(buf, "_ode_matsol%d", cvode_num_);
2934  vectorize_substitute(lappendstr(procfunc, "();\n"), "(_threadargs_);\n");
2935  }
2936  Lappendstr(procfunc, "}\n");
2938  "\nstatic void _ode_matsol(_nrn_model_sorted_token const& _sorted_token, "
2939  "NrnThread* _nt, Memb_list* _ml_arg, int _type) {\n");
2941  lst = get_ion_variables(1);
2942  if (lst->next->itemtype)
2943  movelist(lst->next, lst->prev, procfunc);
2944  Sprintf(buf, "_ode_matsol_instance%d(_threadargs_);\n", cvode_num_);
2946  Lappendstr(procfunc, "}}\n");
2947  }
2948  /* handle the state_discontinuities (obsolete in NET_RECEIVE)*/
2949  if (state_discon_list_)
2951  Symbol* s;
2952  int sindex;
2953  q1 = ITM(q);
2954  s = SYM(q1);
2955  if (q1->itemtype == SYMBOL && (s->subtype & STAT)) {
2956  sindex = slist_search(cvode_num_, s);
2957  Sprintf(buf, "_cvode_ieq + %d, &", sindex);
2958  replacstr(q1->prev, buf);
2959  }
2960  }
2961  }
2962 }
2963 
2965  Sprintf(buf,
2966  "\n\
2967 static void _ode_spec(Node* _nd, double* _pp, Datum* _ppd) {\n\
2968  _p = _pp; _ppvar = _ppd; v = NODEV(_nd);\n\
2969  %s();\n}\n",
2970  cvode_fun_->name);
2971 
2973  Sprintf(buf,
2974  "\n\
2975 static void _ode_map(int _ieq, double** _pv, doubl** _pvdot, double* _pp){}\n");
2977 
2979  "\n\
2980 static void _ode_matsol(Node* _nd, double* _pp, Datum* _ppd){}\n");
2981 }
2982 
2983 void cvode_interface(Symbol* fun, int num, int neq) {
2984  /* if only one then allowed and emit */
2985  cvode_valid_ = 1;
2986  cvode_not_allowed = (using_cvode++) ? 1 : 0;
2988  cvode_num_ = num;
2989  cvode_neq_ = neq;
2990  cvode_fun_ = fun;
2991  if (cvode_fun_->subtype == PROCED) {
2992  cvode_emit = 0;
2993  return;
2994  }
2995  Sprintf(buf,
2996  "\n\
2997 static int _ode_spec%d(_internalthreadargsproto_);\n\
2998 /*static int _ode_matsol%d(_internalthreadargsproto_);*/\n\
2999 ",
3000  num,
3001  num);
3003 }
3004 
3005 void cvode_valid() {
3006  static int once;
3007  if (!cvode_valid_ && !once++) {
3008  Fprintf(stderr, "Notice: This mechanism cannot be used with CVODE\n");
3009  cvode_not_allowed = 1;
3010  }
3011  cvode_valid_ = 0;
3012 }
3013 
3014 void cvode_rw_cur(char (&b)[NRN_BUFSIZE]) {
3015  /* if a current is READ and WRITE then call the correct _ode_spec
3016  since it may compute some aspect of the current */
3017  Item *q, *q1;
3018  int type;
3019  b[0] = '\0';
3020  ITERATE(q, useion) {
3021  q = q->next;
3022  ITERATE(q1, LST(q)) {
3023  type = SYM(q1)->nrntype;
3024  if ((type & NRNCURIN) && (type & NRNCUROUT)) {
3025  if (!cvode_not_allowed && cvode_emit) {
3026  if (vectorize) {
3027  Sprintf(b, "if (_nt->_vcv) { _ode_spec%d(_threadargs_); }\n", cvode_num_);
3028  } else {
3029  Sprintf(b, "if (_nt->_vcv) { _ode_spec%d(); }\n", cvode_num_);
3030  }
3031  return;
3032  }
3033  }
3034  }
3035  q = q->next;
3036  q = q->next;
3037  }
3038 }
3039 
3040 void net_receive(Item* qarg, Item* qp1, Item* qp2, Item* qstmt, Item* qend) {
3041  Item *q, *q1;
3042  Symbol* s;
3043  int i;
3044  char snew[256];
3045  if (net_receive_) {
3046  diag("Only one NET_RECEIVE block allowed", (char*) 0);
3047  }
3048  if (!point_process) {
3049  diag("NET_RECEIVE can only exist in a POINT_PROCESS", (char*) 0);
3050  }
3051  net_receive_ = 1;
3052  deltokens(qp1, qp2);
3053  insertstr(qstmt, "(Point_process* _pnt, double* _args, double _lflag)");
3054  i = 0;
3055  ITERATE(q1, qarg) if (q1->next != qarg) { /* skip last "flag" arg */
3056  s = SYM(q1);
3057  Sprintf(snew, "_args[%d]", i);
3058  ++i;
3059  for (q = qstmt; q != qend; q = q->next) {
3060  if (q->itemtype == SYMBOL && SYM(q) == s) {
3061  replacstr(q, snew);
3062  }
3063  }
3064  }
3065  net_send_delivered_ = qstmt;
3066  q = insertstr(qstmt, "\n{");
3067  vectorize_substitute(q, "\n{ Prop* _p; Datum* _ppvar; Datum* _thread; NrnThread* _nt;\n");
3068  if (watch_seen_) {
3069  insertstr(qstmt, " int _watch_rm = 0;\n");
3070  }
3072  insertstr(qstmt,
3073  " neuron::legacy::set_globals_from_prop(_pnt->_prop, _ml_real, _ml, _iml);\n"),
3074  " _nrn_mechanism_cache_instance _ml_real{_pnt->_prop};\n"
3075  " auto* const _ml = &_ml_real;\n"
3076  " size_t const _iml{};\n");
3077  q = insertstr(qstmt, " _ppvar = _nrn_mechanism_access_dparam(_pnt->_prop);\n");
3079  insertstr(q, ""),
3080  " _thread = nullptr; double* _globals = nullptr; _nt = (NrnThread*)_pnt->_vnt;");
3081  if (debugging_) {
3082  if (0) {
3083  insertstr(qstmt, " assert(_tsav <= t); _tsav = t;");
3084  } else {
3085  insertstr(qstmt,
3086  " if (_tsav > t){ hoc_execerror(hoc_object_name(_pnt->ob), \":Event arrived "
3087  "out of order. Must call ParallelContext.set_maxstep AFTER assigning minimum "
3088  "NetCon.delay\");}\n _tsav = t;");
3089  }
3090  }
3091  insertstr(qend, "}");
3092  if (!artificial_cell) {
3093  Symbol* ions[10];
3094  int j, nion = 0;
3095  /* v can be changed in the NET_RECEIVE block since it is
3096  called between integrator steps and before a re_init
3097  But no need to do so if it is not used.
3098  */
3099  Symbol* vsym = lookup("v");
3100  netrec_need_v = 1;
3101  for (q = qstmt; q != qend; q = q->next) {
3102  if (q->itemtype == SYMBOL && SYM(q) == vsym) {
3103  insertstr(qstmt, " v = NODEV(_pnt->node);\n");
3104  insertstr(qend, "\n NODEV(_pnt->node) = v;\n");
3105  netrec_need_v = 0;
3106  break;
3107  }
3108  }
3109  /* if an ion concentration
3110  is mentioned then we need to get the relevant value
3111  on entry and possibly set a value on exit
3112  Do not allow mention of reversal potential or current
3113  */
3114  for (q = qstmt; q != qend; q = q->next) {
3115  if (q->itemtype == SYMBOL && SYM(q)->type == NAME) {
3116  s = SYM(q);
3117  if ((s->nrntype & (NRNPRANGEIN | NRNPRANGEOUT)) == 0) {
3118  continue;
3119  }
3120  if ((s->nrntype & IONCONC) == 0) {
3121  diag(s->name, ":only concentrations can be mentioned in a NET_RECEIVE block");
3122  }
3123  /* distinct only */
3124  for (j = 0; j < nion; ++j) {
3125  if (s == ions[j]) {
3126  break;
3127  }
3128  }
3129  if (j == nion) {
3130  if (nion >= 10) {
3131  diag("too many ions mentioned in NET_RECEIVE block (limit 10", (char*) 0);
3132  }
3133  ions[nion] = s;
3134  ++nion;
3135  }
3136  }
3137  }
3138  for (j = 0; j < nion; ++j) {
3139  Sprintf(
3140  buf, "%s %s = _ion_%s;\n", (j == 0) ? "\n" : "", ions[j]->name, ions[j]->name);
3141  insertstr(qstmt, buf);
3142  }
3143  for (j = 0; j < nion; ++j) {
3144  if (ions[j]->subtype & STAT) {
3145  Sprintf(buf,
3146  "%s _ion_%s = %s;\n",
3147  (j == 0) ? "\n" : "",
3148  ions[j]->name,
3149  ions[j]->name);
3150  insertstr(qend, buf);
3151  }
3152  }
3153  }
3154  if (i > 0) {
3155  net_receive_ = i;
3156  }
3157  if (net_init_q1_) {
3159  }
3160 }
3161 
3162 void net_init(Item* qinit, Item* qp2) {
3163  /* qinit=INITIAL { stmtlist qp2=} */
3164  replacstr(qinit, "\nstatic void _net_init(Point_process* _pnt, double* _args, double _lflag)");
3165  Sprintf(buf, " _ppvar = _nrn_mechanism_access_dparam(_pnt->_prop);\n");
3167  " _nrn_mechanism_cache_instance _ml_real{_pnt->_prop};\n"
3168  " auto* const _ml = &_ml_real;\n"
3169  " size_t const _iml{};\n"
3170  " Datum* _ppvar = _nrn_mechanism_access_dparam(_pnt->_prop);\n"
3171  " Datum* _thread = nullptr;\n"
3172  " double* _globals = nullptr;\n"
3173  " NrnThread* _nt = (NrnThread*)_pnt->_vnt;\n");
3174  if (net_init_q1_) {
3175  diag("NET_RECEIVE block can contain only one INITIAL block", (char*) 0);
3176  }
3177  net_init_q1_ = qinit;
3178  net_init_q2_ = qp2;
3179 }
3180 
3181 void fornetcon(Item* keyword, Item* par1, Item* args, Item* par2, Item* stmt, Item* qend) {
3182  Item *q, *q1;
3183  Symbol* s;
3184  char snew[256];
3185  int i;
3186  /* follows net_receive pretty closely */
3187  ++for_netcons_;
3188  deltokens(par1, par2);
3189  i = for_netcons_;
3190  Sprintf(buf,
3191  "{int _ifn%d, _nfn%d; double* _fnargs%d, **_fnargslist%d;\n\
3192 \t_nfn%d = _nrn_netcon_args(_ppvar[_fnc_index].get<void*>(), &_fnargslist%d);\n\
3193 \tfor (_ifn%d = 0; _ifn%d < _nfn%d; ++_ifn%d) {\n",
3194  i,
3195  i,
3196  i,
3197  i,
3198  i,
3199  i,
3200  i,
3201  i,
3202  i,
3203  i);
3204  replacstr(keyword, buf);
3205  Sprintf(buf, "\t _fnargs%d = _fnargslist%d[_ifn%d];\n", i, i, i);
3206  insertstr(keyword->next, buf);
3207  insertstr(qend->next, "\t}}\n");
3208  i = 0;
3209  ITERATE(q1, args) {
3210  s = SYM(q1);
3211  Sprintf(snew, "_fnargs%d[%d]", for_netcons_, i);
3212  ++i;
3213  for (q = stmt; q != qend; q = q->next) {
3214  if (q->itemtype == SYMBOL && SYM(q) == s) {
3215  replacstr(q, snew);
3216  }
3217  }
3218  }
3219 }
3220 
3222  Symbol* s;
3223  int i;
3224  Item* q;
3225  SYMLISTITER { /* globals are now global with respect to C as well as hoc */
3226  s = SYM(q);
3227  if (s->nrntype & (NRNGLOBAL) && s->assigned_to_ == 1) {
3228  Sprintf(buf, "Assignment to the GLOBAL variable, \"%s\", is not thread safe", s->name);
3229  threadsafe(buf);
3230  }
3231  }
3232 }
3233 
3234 
3236  int i;
3237  Item* q;
3238  SYMLISTITER {
3239  Symbol* s = SYM(q);
3240  if (s->nrntype & NRNGLOBAL && s->subtype & STAT) {
3241  diag(s->name, " is a STATE variable and hence cannot be declared as GLOBAL");
3242  }
3243  }
3244 }
3245 
3246 void conductance_hint(int blocktype, Item* q1, Item* q2) {
3247  if (blocktype != BREAKPOINT) {
3248  diag("CONDUCTANCE can only appear in BREAKPOINT block", (char*) 0);
3249  }
3250  if (!conductance_) {
3251  conductance_ = newlist();
3252  }
3254  if (q2 != q1->next) {
3255  Symbol* s = SYM(q2);
3256  if (!ion_declared(s)) {
3257  diag(s->name, "not declared as USEION in NEURON block");
3258  }
3260  } else {
3262  }
3263  deltokens(q1, q2);
3264 }
3265 
3266 void possible_local_current(int blocktype, List* symlist) {
3267  Item* q;
3268  Item* q2;
3269  if (blocktype != BREAKPOINT) {
3270  return;
3271  }
3272  ITERATE(q, currents) {
3273  ITERATE(q2, symlist) {
3274  char* n = SYM(q2)->name + 2; /* start after the _l */
3275  if (strcmp(SYM(q)->name, n) == 0) {
3278  }
3281  }
3282  }
3283  }
3284 }
3285 
3288  Item* q;
3290  if (SYM(q) == s) {
3291  return SYM(q->next);
3292  }
3293  }
3294  }
3295  return s;
3296 }
3297 
3298 // Determine if setdata is required to call FUNCTION or PROCEDURE
3299 // setdata is required if RANGE var used. For safety, also VERBATIM.
3300 // Deal with nested calls, via maintaining a list for each func.
3301 // Note that the nest can be recursive and called function may not
3302 // yet be defined til entire text is processed.
3303 
3304 #include <unordered_set>
3305 #include <unordered_map>
3306 
3307 struct Info {
3308  std::unordered_set<Symbol*> func_calls;
3309  bool need_setdata{false};
3310  bool is_being_looked_at{false}; // avoid recursion loops
3311  Item* q{nullptr}; // To be modified if need_setdata.
3312 };
3313 
3314 static std::unordered_map<Symbol*, Info> funcs;
3316 
3318  if (in_func_) {
3319  // If s is a RANGE variable or nullptr (VERBATIM)
3320  // then mark the current function as needing setdata
3321  // If s is FUNCTION or PROCEDURE, then add to list
3322  Info& i = funcs[in_func_];
3323  if (!s) { // VERBATIM
3324  i.need_setdata = true;
3325  } else if (s->nrntype & (NRNRANGE | NRNPOINTER)) {
3326  i.need_setdata = true;
3327  } else if (s->usage & FUNCT) {
3328  i.func_calls.insert(s);
3329  }
3330  }
3331 }
3332 
3334  in_func_ = s;
3335  if (s) {
3336  assert(funcs.count(s) == 0);
3337  funcs[s] = {};
3338  }
3339 }
3340 
3341 // Make sure need_setdata is properly marked for all funcs.
3342 // I.e on entry, only ones marked are those that use RANGE or VERBATIM.
3343 // Need to recursively look through func_calls but watch out for loops.
3344 // If there are no RANGE then VERBATIM is ok and set all need_setdata to false.
3345 
3346 static bool check_func(Symbol* s); // recursive
3347 
3349  if (suffix[0] == '\0') {
3350  return;
3351  }
3352  for (auto& f: funcs) {
3353  f.second.is_being_looked_at = false;
3354  }
3355 
3356  // if there are no RANGE then set all need_setdata to false.
3357  bool norange{true};
3358  Item* q;
3359  int i;
3360  SYMLISTITER {
3361  Symbol* s = SYM(q);
3362  if (s->type == NAME && s->nrntype & (NRNRANGE | NRNPOINTER)) {
3363  norange = false;
3364  break;
3365  }
3366  }
3367  if (norange) {
3368  for (auto& f: funcs) {
3369  f.second.need_setdata = false;
3370  }
3371  }
3372 
3373  for (auto& f: funcs) {
3374  check_func(f.first);
3375  }
3376  for (auto& f: funcs) { // update the hocfunc item if need_setdata
3377  auto& q = f.second.q;
3378  if (q && f.second.need_setdata) {
3379  // error if not valid id
3380  Symbol* s = f.first;
3381  Sprintf(buf,
3382  "\n"
3383  " if(!_prop_id) {\n"
3384  " hoc_execerror(\""
3385  "No data for %s_%s. Requires prior call to setdata_%s"
3386  " and that the specified mechanism instance still be in existence.\","
3387  " NULL);\n",
3388  s->name,
3389  mechname,
3390  mechname);
3391  insertstr(q, buf);
3392  if (vectorize) {
3393  insertstr(q,
3394  " }\n"
3395  " Prop* _local_prop = _extcall_prop;\n");
3396  } else {
3397  // ensure current instance matches _extcall_prop
3398  insertstr(q,
3399  " } else {\n"
3400  " _setdata(_extcall_prop);\n"
3401  " }\n");
3402  }
3403 
3404  } else if (q) {
3405  if (vectorize) {
3406  // if id not valid then _local_prop must be nullptr
3407  // because of later _ppvar = _local_prop ? ...
3408  insertstr(q, "\n Prop* _local_prop = _prop_id ? _extcall_prop : nullptr;\n");
3409  }
3410  }
3411  }
3412 }
3413 
3414 static bool check_func(Symbol* s) { // recursive
3415  if (funcs.count(s) == 0) {
3416  return false;
3417  }
3418  Info& i = funcs[s];
3419  if (i.need_setdata) {
3420  return true;
3421  }
3422  if (i.is_being_looked_at) {
3423  return false;
3424  }
3425  i.is_being_looked_at = true;
3426  for (auto& s1: i.func_calls) {
3427  if (check_func(s1)) {
3428  i.need_setdata = true;
3429  return true;
3430  }
3431  }
3432  return false;
3433 }
3434 
3435 // If the function needs setdata, then q can be changed to
3436 // perform the check on _extcall_prop
3437 // Not called for POINT_PROCESS functions.
3439  auto& i = funcs[s];
3440  i.q = q;
3441 }
#define STRING
Definition: bbslsrv.cpp:9
#define cnt
Definition: tqueue.hpp:44
#define i
Definition: md1redef.h:19
static int indx
Definition: deriv.cpp:289
constexpr auto range(T &&iterable)
Definition: enumerate.h:32
#define STEP
Definition: errcodes.h:33
#define RANGE
Definition: errcodes.h:62
#define NRNCUROUT
Definition: modl.h:214
#define NRNGLOBAL
Definition: modl.h:218
#define NRNPRANGEIN
Definition: modl.h:216
#define NRNPRANGEOUT
Definition: modl.h:217
#define PARM
Definition: modl.h:183
char buf[512]
Definition: init.cpp:13
#define IONCONC
Definition: modl.h:224
char * modprefix
Definition: modl.cpp:48
#define nmodlCONST
Definition: modl.h:203
#define NRNCURIN
Definition: modl.h:213
#define NRNIONFLAG
Definition: modl.h:221
#define IONCONC_IMPLICIT
Definition: modl.h:229
char finname[NRN_BUFSIZE]
Definition: model.cpp:38
#define NRNBBCOREPOINTER
Definition: modl.h:225
#define NRNRANGE
Definition: modl.h:215
#define NRNSTATIC
Definition: modl.h:219
Symbol * semi
Definition: init.cpp:11
#define NRNEXTRN
Definition: modl.h:212
#define NRNNOTP
Definition: modl.h:220
int nmodl_text
Definition: modl.cpp:58
#define EXTDEF_RANDOM
Definition: modl.h:208
#define assert(ex)
Definition: hocassrt.h:24
double var(InputIterator begin, InputIterator end)
Definition: ivocvect.h:108
#define NRNPOINTER
Definition: membfunc.hpp:83
#define DEP
Definition: membfunc.hpp:64
#define DERF
Definition: model.h:114
#define STR(q)
Definition: model.h:76
#define FUNCT
Definition: model.h:108
#define SYM0
Definition: model.h:63
#define STAT
Definition: model.h:106
#define ITERATE(itm, lst)
Definition: model.h:18
#define SYMBOL
Definition: model.h:91
#define EXPLICIT_DECL
Definition: model.h:126
#define Linsertstr
Definition: model.h:220
#define INDEP
Definition: model.h:104
#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 NRN_BUFSIZE
Definition: model.h:6
#define ARRAY
Definition: model.h:107
Symbol * install(const char *, int)
#define PROCED
Definition: model.h:109
printf
Definition: extdef.h:5
List * procfunc
Definition: init.cpp:9
const char * name
Definition: init.cpp:16
long subtype
Definition: init.cpp:107
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
void deltokens(Item *q1, Item *q2)
Definition: list.cpp:189
Item * lappenditem(List *list, Item *item)
Definition: list.cpp:147
Item * insertstr(Item *item, const char *str)
Definition: list.cpp:99
Item * lappendsym(List *list, Symbol *sym)
Definition: list.cpp:143
Item * lappendstr(List *list, const char *str)
Definition: list.cpp:135
List * newlist()
The following routines support the concept of a list.
static void * emalloc(size_t size)
Definition: mpispike.cpp:30
void register_data_fields(int mechtype, std::vector< std::pair< std::string, int >> const &param_info, std::vector< std::pair< std::string, std::string >> const &dparam_info)
Definition: init.cpp:851
void SprintfAsrt(char(&buf)[N], const char *fmt, Args &&... args)
assert if the Sprintf format data does not fit into buf
Definition: wrap_sprintf.h:27
int ic
Definition: cellorder.cpp:619
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
std::string to_string(const T &obj)
void threadsafe(const char *)
Definition: parsact.cpp:1037
void parminstall(Symbol *n, const char *num, const char *units, const char *limits)
Definition: parsact.cpp:58
void indepinstall(Symbol *n, const char *from, const char *to, const char *with, const char *units)
Definition: parsact.cpp:96
void vectorize_substitute(Item *q, const char *str)
Definition: noccout.cpp:748
List * destructorfunc
Definition: init.cpp:168
List * constructorfunc
Definition: init.cpp:168
NMODL parser global flags / functions.
void check_useion_variables()
Definition: nocpout.cpp:1534
static std::unordered_map< Symbol *, Info > funcs
Definition: nocpout.cpp:3314
static void var_count(Symbol *s)
Definition: nocpout.cpp:1818
void possible_local_current(int blocktype, List *symlist)
Definition: nocpout.cpp:3266
void ldifusreg()
Definition: nocpout.cpp:1570
void chk_thread_safe()
Definition: nocpout.cpp:3221
List * get_ion_variables(int)
Definition: nocpout.cpp:2307
#define IONCUR
Definition: nocpout.cpp:96
void slist_data(Symbol *s, int indx, int findx)
Definition: nocpout.cpp:2686
List * set_ion_variables(int)
Definition: nocpout.cpp:2236
int net_event_seen_
Definition: nocpout.cpp:169
void ion_promote(Item *)
Definition: nocpout.cpp:2617
static int use_bbcorepointer
Definition: nocpout.cpp:142
void defs_h(Symbol *)
Definition: nocpout.cpp:1841
#define IONOUT
Definition: nocpout.cpp:95
static int num_random_vars
Definition: nocpout.cpp:134
static int ba_index_
Definition: nocpout.cpp:157
void del_range(List *)
Definition: nocpout.cpp:2164
List * currents
Definition: nocpout.cpp:124
int decode_tolerance(Symbol *sym, double *pg1)
Definition: nocpout.cpp:1696
List * thread_cleanup_list
Definition: nocpout.cpp:111
int thread_data_index
Definition: nocpout.cpp:110
List * useion
Definition: nocpout.cpp:125
Item * defs_list_parm_default
Definition: nocpout.cpp:108
Symbol * breakpoint_current(Symbol *s)
Definition: nocpout.cpp:3286
static std::vector< std::pair< int, std::string > > ppvar_data_field_strings
Definition: nocpout.cpp:190
int watch_seen_
Definition: nocpout.cpp:170
int check_tables_threads(List *)
Definition: parsact.cpp:370
#define IONDCUR
Definition: nocpout.cpp:97
static void ppvar_semantics(int, const char *semantics, const char *name, const char *type)
Definition: nocpout.cpp:2545
List * conductance_
Definition: nocpout.cpp:126
List * toplocal_
Definition: nocpout.cpp:113
int vectorize
Definition: nocpout.cpp:78
void nrn_list(Item *q1, Item *q2)
Definition: nocpout.cpp:1867
void nrn_use(Item *q1, Item *q2, Item *q3, Item *q4)
Definition: nocpout.cpp:1992
#define IONEREV
Definition: nocpout.cpp:93
void func_needs_setdata()
Definition: nocpout.cpp:3348
void chk_global_state()
Definition: nocpout.cpp:3235
static const char * rsuffix
Definition: nocpout.cpp:136
static std::vector< std::string > data_field_strings
Definition: nocpout.cpp:191
int brkpnt_exists
void net_receive(Item *qarg, Item *qp1, Item *qp2, Item *qstmt, Item *qend)
Definition: nocpout.cpp:3040
static const char * brkpnt_str_
Definition: nocpout.cpp:100
void parout()
Definition: nocpout.cpp:207
static int for_netcons_
Definition: nocpout.cpp:154
int electrode_current
Definition: nocpout.cpp:109
static Symbol * cvode_fun_
Definition: nocpout.cpp:2684
List * state_discon_list_
Definition: nocpout.cpp:160
int iontype(char *s1, char *s2)
Definition: nocpout.cpp:2054
static List * rangestate
Definition: nocpout.cpp:130
static int varcount
Definition: nocpout.cpp:188
void cvode_valid()
Definition: nocpout.cpp:3005
List * begin_dion_stmt()
Definition: nocpout.cpp:2565
static int cvode_valid_
Definition: nocpout.cpp:2682
static int diamdec
Definition: nocpout.cpp:140
void hocfunc_setdata_item(Symbol *s, Item *q)
Definition: nocpout.cpp:3438
static Item * net_init_q1_
Definition: nocpout.cpp:155
List * end_dion_stmt(const char *strdel)
Definition: nocpout.cpp:2589
List * thread_mem_init_list
Definition: nocpout.cpp:112
const char * nmodl_version_
Definition: nocpout.cpp:12
List * defs_list
Definition: nocpout.cpp:107
int debugging_
Definition: nocpout.cpp:166
int protect_include_
Definition: parsact.cpp:26
static int parraycount
Definition: nocpout.cpp:188
int netrec_need_v
void check_range_in_func(Symbol *s)
Definition: nocpout.cpp:3317
void net_init(Item *qinit, Item *qp2)
Definition: nocpout.cpp:3162
#define SYMLISTITER
Definition: nocpout.cpp:180
int slist_search(int n, Symbol *s)
Definition: nocpout.cpp:2711
int protect_
Definition: parsact.cpp:25
static Item * net_send_delivered_
Definition: nocpout.cpp:172
static List * nmodlrandoms
Definition: nocpout.cpp:132
List * plotlist
Definition: nocpout.cpp:106
static Symbol * ifnew_install(const char *name)
Definition: nocpout.cpp:2077
void check_ion_vars_as_constant(char *ion_name, const List *ion_var_list)
Definition: nocpout.cpp:1489
void bablk(int ba, int type, Item *q1, Item *q2)
Definition: nocpout.cpp:1945
List * syminorder
Definition: nocpout.cpp:105
static char suffix[256]
Definition: nocpout.cpp:135
static int watch_index
Definition: nocpout.cpp:164
int ion_declared(Symbol *s)
Definition: nocpout.cpp:1980
void cvode_rw_cur(char(&b)[NRN_BUFSIZE])
Definition: nocpout.cpp:3014
int net_receive_
Definition: nocpout.cpp:167
static int ppvar_cnt
Definition: nocpout.cpp:151
void set_inside_func(Symbol *s)
Definition: nocpout.cpp:3333
static List * nrn_mech_inst_destruct_list
Definition: nocpout.cpp:133
void cvode_interface(Symbol *fun, int num, int neq)
Definition: nocpout.cpp:2983
void nrn_var_assigned(Symbol *s)
Definition: nocpout.cpp:2658
#define IONIN
Definition: nocpout.cpp:94
void nrndeclare()
Definition: nocpout.cpp:2087
static Item * net_init_q2_
Definition: nocpout.cpp:156
static int cvode_num_
Definition: nocpout.cpp:2683
static int using_cvode
Definition: nocpout.cpp:2682
static int cvode_ieq_index
Definition: nocpout.cpp:162
int iondef(int *)
Definition: nocpout.cpp:2358
#define NRNFIX(arg)
Definition: nocpout.cpp:2654
static List * rangeparm
Definition: nocpout.cpp:128
static int tqitem_index
Definition: nocpout.cpp:163
List * symlist[]
Definition: symbol.cpp:9
static List * rangedep
Definition: nocpout.cpp:129
static void cvode_conc_map()
Definition: nocpout.cpp:2727
void conductance_hint(int blocktype, Item *q1, Item *q2)
Definition: nocpout.cpp:3246
static char * mechname
Definition: nocpout.cpp:137
int point_process
Definition: nocpout.cpp:138
void nrninit()
Definition: nocpout.cpp:193
static List * ppvar_semantics_
Definition: nocpout.cpp:152
int cvode_not_allowed
Definition: nocpout.cpp:161
void decode_ustr(Symbol *sym, double *pg1, double *pg2, char *s)
Definition: nocpout.cpp:1718
static List * ba_list_
Definition: nocpout.cpp:158
int decode_limits(Symbol *sym, double *pg1, double *pg2)
Definition: nocpout.cpp:1673
static List * ion_synonym
Definition: nocpout.cpp:165
static void check_sufficient_ion_read_statements(std::string const &ion_name, List *read_variables, List *write_variables)
Definition: nocpout.cpp:1504
void units_reg()
Definition: nocpout.cpp:1766
static int areadec
Definition: nocpout.cpp:141
static List * nrnpointers
Definition: nocpout.cpp:131
int artificial_cell
Definition: nocpout.cpp:139
static Symbol * in_func_
Definition: nocpout.cpp:3315
List * watch_alloc
Definition: parsact.cpp:16
static int prop_size
Definition: nocpout.cpp:189
void cvode_proced_emit()
Definition: nocpout.cpp:2964
static int cvode_neq_
Definition: nocpout.cpp:2683
static bool check_func(Symbol *s)
Definition: nocpout.cpp:3414
void fornetcon(Item *keyword, Item *par1, Item *args, Item *par2, Item *stmt, Item *qend)
Definition: nocpout.cpp:3181
int net_send_seen_
Definition: nocpout.cpp:168
Symbol * indepsym
Definition: declare.cpp:8
void out_nt_ml_frag(List *p)
Definition: nocpout.cpp:2798
void cvode_emit_interface()
Definition: nocpout.cpp:2819
List * breakpoint_local_current_
Definition: nocpout.cpp:127
static int cvode_emit
Definition: nocpout.cpp:162
List * ldifuslist
Definition: kinetic.cpp:72
void declare_p()
Definition: nocpout.cpp:2178
void warn_ignore(Symbol *s)
Definition: nocpout.cpp:1551
data_handle< T > transform(data_handle< T > handle, Transform type)
Definition: node.cpp:32
#define diag(s)
Definition: nonlin.cpp:19
int const size_t const size_t n
Definition: nrngsl.h:10
size_t q
size_t p
size_t j
s
Definition: multisend.cpp:521
short index
Definition: cabvars.h:11
short type
Definition: cabvars.h:10
static Symbol * vsym
Definition: occvode.cpp:42
static double remove(void *v)
Definition: ocdeck.cpp:205
bool is_being_looked_at
Definition: nocpout.cpp:3310
bool need_setdata
Definition: nocpout.cpp:3309
std::unordered_set< Symbol * > func_calls
Definition: nocpout.cpp:3308
Item * q
Definition: nocpout.cpp:3311
Definition: model.h:8
struct Item * prev
Definition: model.h:13
short itemtype
Definition: model.h:9
struct Item * next
Definition: model.h:12
Definition: model.h:47
union Symbol::@28 u
short nrntype
Definition: modl.h:141
long subtype
Definition: model.h:49
const char * str
Definition: model.h:53
char * name
Definition: model.h:61
int used
Definition: model.h:55
int varnum
Definition: model.h:59
int Fprintf(FILE *stream, const char *fmt, Args... args)
Definition: logger.hpp:8