NEURON
nrncore_callbacks.cpp
Go to the documentation of this file.
1 #include <vector>
2 #include <unordered_map>
3 #include "nrncore_callbacks.h"
4 #include "nrnconf.h"
5 #include "nrnmpi.h"
6 #include "section.h"
7 #include "netcon.h"
8 #include "nrncvode.h"
9 #include "nrniv_mf.h"
10 #include "hocdec.h"
14 #include "parse.hpp"
15 #include "nrnran123.h" // globalindex written to globals.
16 #include "netcvode.h" // for nrnbbcore_vecplay_write and PreSyn.flag_
18 #include "vrecitem.h" // for nrnbbcore_vecplay_write
19 
20 #include "nrnwrap_dlfcn.h"
21 #include "nrnsection_mapping.h"
22 
25 extern short* nrn_is_artificial_;
26 extern bool corenrn_direct;
27 extern int* bbcore_dparam_size;
28 extern double nrn_ion_charge(Symbol*);
29 extern CellGroup* cellgroups_;
30 extern NrnMappingInfo mapinfo;
32 extern char* pnt_map;
33 extern void* nrn_interthread_enqueue(NrnThread*);
34 
35 /** Populate function pointers by mapping function pointers for callback */
37  for (int i = 0; cnbs[i].name; ++i) {
38  void* sym = NULL;
39 #if defined(HAVE_DLFCN_H)
40  sym = dlsym(handle, cnbs[i].name);
41 #endif
42  if (!sym) {
43  fprintf(stderr, "Could not get symbol %s from CoreNEURON\n", cnbs[i].name);
44  hoc_execerror("dlsym returned NULL", NULL);
45  }
46  void** c = (void**) sym;
47  *c = (void*) (cnbs[i].f);
48  }
49 }
50 
51 void write_memb_mech_types_direct(std::ostream& s) {
52  // list of Memb_func names, types, point type info, is_ion
53  // and data, pdata instance sizes. If the mechanism is an eion type,
54  // the following line is the charge.
55  // Not all Memb_func are necessarily used in the model.
56  s << bbcore_write_version << std::endl;
57  s << n_memb_func << std::endl;
58  for (int type = 2; type < n_memb_func; ++type) {
59  const char* w = " ";
60  Memb_func& mf = memb_func[type];
61  Memb_list& ml = memb_list[type];
62  s << mf.sym->name << w << type << w << int(pnt_map[type])
63  << w // the pointtype, 0 means not a POINT_PROCESS
65  << w << bbcore_dparam_size[type] << w;
66 
67  int n_vars = ml.get_num_variables();
68  s << n_vars;
69  for (size_t i = 0; i < n_vars; ++i) {
70  s << w << ml.get_array_dims(i);
71  }
72  s << std::endl;
73 
74  if (nrn_is_ion(type)) {
75  s << nrn_ion_charge(mf.sym) << std::endl;
76  }
77  }
78 }
79 
80 
81 // just for secondorder and Random123_globalindex and legacy units flag
82 int get_global_int_item(const char* name) {
83  if (strcmp(name, "secondorder") == 0) {
84  return secondorder;
85  } else if (strcmp(name, "Random123_global_index") == 0) {
87  }
88  return 0;
89 }
90 
91 // successively return global double info. Begin with p==NULL.
92 // Done when return NULL.
93 void* get_global_dbl_item(void* p, const char*& name, int& size, double*& val) {
94  Symbol* sp = (Symbol*) p;
95  if (sp == NULL) {
97  }
98  for (; sp; sp = sp->next) {
99  if (sp->type == VAR && sp->subtype == USERDOUBLE) {
100  name = sp->name;
101  if (is_array(*sp)) {
102  Arrayinfo* a = sp->arayinfo;
103  if (a->nsub == 1) {
104  size = a->sub[0];
105  val = new double[size];
106  for (int i = 0; i < a->sub[0]; ++i) {
107  char n[256];
108  Sprintf(n, "%s[%d]", sp->name, i);
109  val[i] = *hoc_val_pointer(n);
110  }
111  }
112  } else {
113  size = 0;
114  val = new double[1];
115  val[0] = *sp->u.pval;
116  }
117  return sp->next;
118  }
119  }
120  return NULL;
121 }
122 
123 
124 /**
125  Copy weights from all coreneuron::NrnThread to NetCon instances.
126  This depends on the CoreNEURON weight order for each thread to be
127  the same as was originally sent from NEURON. See how that order
128  was constructed in CellGroup::mk_cgs_netcon_info.
129 **/
130 
131 void nrnthreads_all_weights_return(std::vector<double*>& weights) {
132  std::vector<int> iw(nrn_nthread); // index for each thread
133  Symbol* ncsym = hoc_lookup("NetCon");
134  hoc_List* ncl = ncsym->u.ctemplate->olist;
135  hoc_Item* q;
136  ITERATE(q, ncl) {
137  Object* ho = (Object*) VOIDITM(q);
138  NetCon* nc = (NetCon*) ho->u.this_pointer;
139  std::size_t ith = 0; // if no _vnt, put in thread 0
140  if (nc->target_ && nc->target_->_vnt) {
141  ith = std::size_t(((NrnThread*) (nc->target_->_vnt))->id);
142  }
143  for (int i = 0; i < nc->cnt_; ++i) {
144  nc->weight_[i] = weights[ith][iw[ith]++];
145  }
146  }
147 }
148 
149 /** @brief Return location for CoreNEURON to copy data into.
150  * The type is mechanism type or special negative type for voltage,
151  * i_membrane_, or time. See coreneuron/io/nrn_setup.cpp:legacy_index2pointer.
152  * We allow coreneuron to copy to NEURON's AoS data as CoreNEURON knows
153  * how its data is arranged (SoA and possibly permuted).
154  * This function figures out the size (for validation)
155  * and data pointer to be returned based on type and thread id.
156  * The ARTIFICIAL_CELL type case is special as there is no thread specific
157  * Memb_list for those.
158  */
159 size_t nrnthreads_type_return(int type, int tid, double*& data, std::vector<double*>& mdata) {
160  size_t n = 0;
161  data = NULL;
162  mdata.clear();
163  if (tid >= nrn_nthread) {
164  return n;
165  }
166  NrnThread& nt = nrn_threads[tid];
167  if (type == voltage) {
168  auto const cache_token = nrn_ensure_model_data_are_sorted();
169  data = nt.node_voltage_storage();
170  n = size_t(nt.end);
171  } else if (type == i_membrane_) { // i_membrane_
172  auto const cache_token = nrn_ensure_model_data_are_sorted();
173  data = nt.node_sav_rhs_storage();
174  n = size_t(nt.end);
175  } else if (type == 0) { // time
176  data = &nt._t;
177  n = 1;
178  } else if (type > 0 && type < n_memb_func) {
179  auto set_mdata = [&mdata](Memb_list* ml) -> size_t {
180  mdata = ml->data();
181  return ml->nodecount;
182  };
183 
184  Memb_list* ml = nt._ml_list[type];
185  if (ml) {
186  n = set_mdata(ml);
187  } else {
188  // The single thread case is easy
189  if (nrn_nthread == 1) {
190  ml = &memb_list[type];
191  n = set_mdata(ml);
192  } else {
193  // mk_tml_with_art() created a cgs[id].mlwithart which appended
194  // artificial cells to the end. Turns out that
195  // cellgroups_[tid].type2ml[type]
196  // is the Memb_list we need. Sadly, by the time we get here, cellgroups_
197  // has already been deleted. So we defer deletion of the necessary
198  // cellgroups_ portion (deleting it on return from nrncore_run).
200  n = set_mdata(ml);
201  }
202  }
203  }
204  return n;
205 }
206 
207 
208 void nrnthread_group_ids(int* grp) {
209  for (int i = 0; i < nrn_nthread; ++i) {
210  grp[i] = cellgroups_[i].group_id;
211  }
212 }
213 
214 
215 int nrnthread_dat1(int tid,
216  int& n_presyn,
217  int& n_netcon,
218  std::vector<int>& output_gid,
219  int*& netcon_srcgid,
220  std::vector<int>& netcon_negsrcgid_tid) {
221  if (tid >= nrn_nthread) {
222  return 0;
223  }
224  CellGroup& cg = cellgroups_[tid];
225  n_presyn = cg.n_presyn;
226  n_netcon = cg.n_netcon;
227  output_gid = std::move(cg.output_gid);
228  netcon_srcgid = cg.netcon_srcgid;
229  cg.netcon_srcgid = NULL;
230  netcon_negsrcgid_tid = cg.netcon_negsrcgid_tid;
231  return 1;
232 }
233 
234 void nrnthread_dat3_cell_count(int& cell_count) {
235  cell_count = mapinfo.size();
236 }
237 
238 void nrnthread_dat3_cellmapping(int i, int& gid, int& nsec, int& nseg, int& n_seclist) {
240  gid = c->gid;
241  nsec = c->num_sections();
242  nseg = c->num_segments();
243  n_seclist = c->size();
244 }
245 
247  int i_sec,
248  std::string& sclname,
249  int& nsec,
250  int& nseg,
251  size_t& total_lfp_factors,
252  std::vector<size_t>& electrode_offsets,
253  std::vector<int>& data_sec,
254  std::vector<int>& data_seg,
255  std::vector<double>& data_lfp) {
256  CellMapping* c = mapinfo.mapping[i_c];
257  SecMapping* s = c->secmapping[i_sec];
258  sclname = s->name;
259  nsec = s->nsec;
260  nseg = s->size();
261  total_lfp_factors = s->seglfp_factors.size();
262  if (!s->electrode_offsets.empty()) {
263  electrode_offsets = s->electrode_offsets;
264  }
265  data_sec = s->sections;
266  data_seg = s->segments;
267  data_lfp = s->seglfp_factors;
268 }
269 
270 // sizes and total data count
271 int nrnthread_dat2_1(int tid,
272  int& ncell,
273  int& ngid,
274  int& n_real_gid,
275  int& nnode,
276  int& ndiam,
277  int& nmech,
278  int*& tml_index,
279  int*& ml_nodecount,
280  int& nidata,
281  int& nvdata,
282  int& nweight) {
283  if (tid >= nrn_nthread) {
284  return 0;
285  }
286  CellGroup& cg = cellgroups_[tid];
287  NrnThread& nt = nrn_threads[tid];
288 
289  ncell = cg.n_real_cell;
290  ngid = cg.n_output;
291  n_real_gid = cg.n_real_output;
292  nnode = nt.end;
293  ndiam = cg.ndiam;
294  nmech = cg.n_mech;
295 
296  cg.ml_vdata_offset = new int[nmech];
297  int vdata_offset = 0;
298  tml_index = new int[nmech];
299  ml_nodecount = new int[nmech];
300  MlWithArt& mla = cg.mlwithart;
301  for (size_t j = 0; j < mla.size(); ++j) {
302  int type = mla[j].first;
303  Memb_list* ml = mla[j].second;
304  tml_index[j] = type;
305  ml_nodecount[j] = ml->nodecount;
306  cg.ml_vdata_offset[j] = vdata_offset;
307  int* ds = memb_func[type].dparam_semantics.get();
308  for (int psz = 0; psz < bbcore_dparam_size[type]; ++psz) {
309  if (ds[psz] == -4 || ds[psz] == -6 || ds[psz] == -7 || ds[psz] == -11 || ds[psz] == 0) {
310  // printf("%s ds[%d]=%d vdata_offset=%d\n", memb_func[type].sym->name, psz, ds[psz],
311  // vdata_offset);
312  vdata_offset += ml->nodecount;
313  }
314  }
315  }
316  nvdata = vdata_offset;
317  nidata = 0;
318  // printf("nidata=%d nvdata=%d nnetcon=%d\n", nidata, nvdata, cg.n_netcon);
319  nweight = 0;
320  for (int i = 0; i < cg.n_netcon; ++i) {
321  nweight += cg.netcons[i]->cnt_;
322  }
323 
324  return 1;
325 }
326 
327 int nrnthread_dat2_2(int tid,
328  int*& v_parent_index,
329  double*& a,
330  double*& b,
331  double*& area,
332  double*& v,
333  double*& diamvec) {
334  if (tid >= nrn_nthread) {
335  return 0;
336  }
337  CellGroup& cg = cellgroups_[tid];
338  NrnThread& nt = nrn_threads[tid];
339 
340  assert(cg.n_real_cell == nt.ncell);
341 
342  // If direct transfer, copy, because target space already allocated
343  bool copy = corenrn_direct;
344  if (copy) {
345  std::copy_n(nt.node_a_storage(), nt.end, a);
346  std::copy_n(nt.node_b_storage(), nt.end, b);
347  std::copy_n(nt.node_area_storage(), nt.end, area);
348  std::copy_n(nt.node_voltage_storage(), nt.end, v);
349  std::copy_n(nt._v_parent_index, nt.end, v_parent_index);
350  } else {
351  v_parent_index = nt._v_parent_index;
352  auto const cache_token = nrn_ensure_model_data_are_sorted();
353  a = nt.node_a_storage();
354  area = nt.node_area_storage();
355  b = nt.node_b_storage();
356  v = nt.node_voltage_storage();
357  }
358  if (cg.ndiam) {
359  if (!copy) {
360  diamvec = new double[nt.end];
361  }
362  for (int i = 0; i < nt.end; ++i) {
363  Node* nd = nt._v_node[i];
364  double diam = 0.0;
365  for (Prop* p = nd->prop; p; p = p->next) {
366  if (p->_type == MORPHOLOGY) {
367  diam = p->param(0);
368  break;
369  }
370  }
371  diamvec[i] = diam;
372  }
373  }
374  return 1;
375 }
376 
378  size_t i,
379  int dsz_inst,
380  int*& nodeindices,
381  double*& data,
382  int*& pdata,
383  std::vector<uint32_t>& nmodlrandom, // 5 uint32_t per var per instance
384  std::vector<int>& pointer2type) {
385  if (tid >= nrn_nthread) {
386  return 0;
387  }
388  CellGroup& cg = cellgroups_[tid];
389  NrnThread& nt = nrn_threads[tid];
390  MlWithArtItem& mlai = cg.mlwithart[i];
391  int type = mlai.first;
392  Memb_list* ml = mlai.second;
393  // for direct transfer, data=NULL means copy into passed space for nodeindices, data, and pdata
394  bool copy = data ? true : false;
395 
396  int vdata_offset = cg.ml_vdata_offset[i];
397  int isart = nrn_is_artificial_[type];
398  int n = ml->nodecount;
399  int n_vars = ml->get_num_variables();
400  int sz = nrn_prop_param_size_[type];
401 
402  // As the NEURON data is now transposed then for now always create a new
403  // copy in the format expected by CoreNEURON.
404  // TODO remove the need for this entirely
405  if (!copy) {
406  data = new double[n * sz];
407  }
408  for (auto instance = 0, k = 0; instance < n; ++instance) {
409  for (int variable = 0; variable < n_vars; ++variable) {
410  auto array_dim = ml->get_array_dims(variable);
411  for (int array_index = 0; array_index < array_dim; ++array_index) {
412  data[k++] = ml->data(instance, variable, array_index);
413  }
414  }
415  }
416 
417  if (isart) { // data may not be contiguous
418  nodeindices = NULL;
419  } else {
420  nodeindices = ml->nodeindices; // allocated below if copy
421  }
422  if (copy) {
423  if (!isart) {
424  nodeindices = (int*) emalloc(n * sizeof(int));
425  for (int i = 0; i < n; ++i) {
426  nodeindices[i] = ml->nodeindices[i];
427  }
428  }
429  }
430 
431  sz = bbcore_dparam_size[type]; // nrn_prop_dparam_size off by 1 if cvode_ieq.
432  if (sz) {
433  int* pdata1;
434  pdata1 = datum2int(type, ml, nt, cg, cg.datumindices[dsz_inst], vdata_offset, pointer2type);
435  if (copy) {
436  int nn = n * sz;
437  for (int i = 0; i < nn; ++i) {
438  pdata[i] = pdata1[i];
439  }
440  delete[] pdata1;
441  } else {
442  pdata = pdata1;
443  }
444  } else {
445  pdata = NULL;
446  }
447 
448  // nmodlrandom: reserve 5 uint32 for each var of each instance
449  // id1, id2, id3, seq, uint32_t(which)
450  // Header is number of random variables followed by dparam indices
451  // if no destructor, skip. There are no random variables.
452  if (nrn_mech_inst_destruct.count(type)) {
453  auto& indices = nrn_mech_random_indices(type);
454  nmodlrandom.reserve(1 + indices.size() + 5 * n * indices.size());
455  nmodlrandom.push_back(indices.size());
456  for (int ix: indices) {
457  nmodlrandom.push_back((uint32_t) ix);
458  }
459  for (int ix: indices) {
460  uint32_t data[5];
461  char which;
462  for (int i = 0; i < n; ++i) {
463  auto& datum = ml->pdata[i][ix];
464  nrnran123_State* r = (nrnran123_State*) datum.get<void*>();
465  nrnran123_getids3(r, &data[0], &data[1], &data[2]);
466  nrnran123_getseq(r, &data[3], &which);
467  data[4] = uint32_t(which);
468  for (auto j: data) {
469  nmodlrandom.push_back(j);
470  }
471  }
472  }
473  }
474  return 1;
475 }
476 
477 int nrnthread_dat2_3(int tid,
478  int nweight,
479  int*& output_vindex,
480  double*& output_threshold,
481  int*& netcon_pnttype,
482  int*& netcon_pntindex,
483  double*& weights,
484  double*& delays) {
485  if (tid >= nrn_nthread) {
486  return 0;
487  }
488  CellGroup& cg = cellgroups_[tid];
489 
490  output_vindex = new int[cg.n_presyn];
491  output_threshold = new double[cg.n_real_output];
492  for (int i = 0; i < cg.n_presyn; ++i) {
493  output_vindex[i] = cg.output_vindex[i];
494  }
495  for (int i = 0; i < cg.n_real_output; ++i) {
496  output_threshold[i] = cg.output_ps[i] ? cg.output_ps[i]->threshold_ : 0.0;
497  }
498 
499  // connections
500  int n = cg.n_netcon;
501  // printf("n_netcon=%d nweight=%d\n", n, nweight);
502  netcon_pnttype = cg.netcon_pnttype;
503  cg.netcon_pnttype = NULL;
504  netcon_pntindex = cg.netcon_pntindex;
505  cg.netcon_pntindex = NULL;
506  // alloc a weight array and write netcon weights
507  weights = new double[nweight];
508  int iw = 0;
509  for (int i = 0; i < n; ++i) {
510  NetCon* nc = cg.netcons[i];
511  for (int j = 0; j < nc->cnt_; ++j) {
512  weights[iw++] = nc->weight_[j];
513  }
514  }
515  // alloc a delay array and write netcon delays
516  delays = new double[n];
517  for (int i = 0; i < n; ++i) {
518  NetCon* nc = cg.netcons[i];
519  delays[i] = nc->delay_;
520  }
521 
522  return 1;
523 }
524 
525 int nrnthread_dat2_corepointer(int tid, int& n) {
526  if (tid >= nrn_nthread) {
527  return 0;
528  }
529 
530  n = 0;
531  MlWithArt& mla = cellgroups_[tid].mlwithart;
532  for (size_t i = 0; i < mla.size(); ++i) {
533  if (nrn_bbcore_write_[mla[i].first]) {
534  ++n;
535  }
536  }
537 
538  return 1;
539 }
540 
542  int type,
543  int& icnt,
544  int& dcnt,
545  int*& iArray,
546  double*& dArray) {
547  if (tid >= nrn_nthread) {
548  return 0;
549  }
550  NrnThread& nt = nrn_threads[tid];
551  CellGroup& cg = cellgroups_[tid];
552  Memb_list* ml = cg.type2ml[type];
553 
554  dcnt = 0;
555  icnt = 0;
556  // data size and allocate
557  for (int i = 0; i < ml->nodecount; ++i) {
559  nullptr, nullptr, &dcnt, &icnt, ml, i, ml->pdata[i], ml->_thread, nullptr, &nt);
560  }
561  dArray = nullptr;
562  iArray = nullptr;
563  if (icnt) {
564  iArray = new int[icnt];
565  }
566  if (dcnt) {
567  dArray = new double[dcnt];
568  }
569  icnt = dcnt = 0;
570  // data values
571  for (int i = 0; i < ml->nodecount; ++i) {
573  dArray, iArray, &dcnt, &icnt, ml, i, ml->pdata[i], ml->_thread, nullptr, &nt);
574  }
575 
576  return 1;
577 }
578 
579 
580 // primarily to return nrnran123 sequence info when psolve on the coreneuron
581 // side is finished so can either do another coreneuron psolve or
582 // continue on neuron side.
583 int core2nrn_corepointer_mech(int tid, int type, int icnt, int dcnt, int* iArray, double* dArray) {
584  if (tid >= nrn_nthread) {
585  return 0;
586  }
587  NrnThread& nt = nrn_threads[tid];
588  Memb_list* ml = nt._ml_list[type];
589  // ARTIFICIAL_CELL are not in nt.
590  if (!ml) {
592  assert(ml);
593  }
594 
595  int ik = 0;
596  int dk = 0;
597  // data values
598  for (int i = 0; i < ml->nodecount; ++i) {
600  dArray, iArray, &dk, &ik, ml, i, ml->pdata[i], ml->_thread, nullptr, &nt);
601  }
602  assert(dk == dcnt);
603  assert(ik == icnt);
604  return 1;
605 }
606 
607 // NMODL RANDOM seq34 data return from coreneuron
609  int type,
610  const std::vector<int>& indices,
611  const std::vector<double>& nmodlrandom) {
612  if (tid >= nrn_nthread) {
613  return 0;
614  }
615  NrnThread& nt = nrn_threads[tid];
616  Memb_list* ml = nt._ml_list[type];
617  // ARTIFICIAL_CELL are not in nt.
618  if (!ml) {
620  assert(ml);
621  }
622 
623  auto& nrnindices = nrn_mech_random_indices(type); // for validation
624  assert(nrnindices == indices);
625  assert(nmodlrandom.size() == indices.size() * ml->nodecount);
626 
627  int ir = 0; // into nmodlrandom
628  for (const auto ix: nrnindices) {
629  for (int i = 0; i < ml->nodecount; ++i) {
630  auto& datum = ml->pdata[i][ix];
631  nrnran123_State* state = (nrnran123_State*) datum.get<void*>();
632  nrnran123_setseq(state, nmodlrandom[ir++]);
633  }
634  }
635  return 1;
636 }
637 
638 int* datum2int(int type,
639  Memb_list* ml,
640  NrnThread& nt,
641  CellGroup& cg,
642  DatumIndices& di,
643  int ml_vdata_offset,
644  std::vector<int>& pointer2type) {
645  int isart = nrn_is_artificial_[di.type];
646  int sz = bbcore_dparam_size[type];
647  int* pdata = new int[ml->nodecount * sz];
648  int* semantics = memb_func[type].dparam_semantics.get();
649  for (int i = 0; i < ml->nodecount; ++i) {
650  int ioff = i * sz;
651  for (int j = 0; j < sz; ++j) {
652  int jj = ioff + j;
653  int etype = di.datum_type[jj];
654  int eindex = di.datum_index[jj];
655  const int seman = semantics[j];
656  // Would probably be more clear if use seman for as many as
657  // possible of the cases
658  // below and within each case deal with etype appropriately.
659  // datum_type and datum_index refer to mechanism type where the
660  // range variable lives (and otherwise is generally the same as
661  // seman). And datum_index refers to the index of the range variable
662  // within the mechanism (or voltage, area, etc.)
663  if (seman == -5) { // POINTER to range variable (e.g. voltage)
664  pdata[jj] = eindex;
665  pointer2type.push_back(etype);
666  } else if (etype == -1) {
667  if (isart) {
668  pdata[jj] = -1; // maybe save this space eventually. but not many of these in
669  // bb models
670  } else {
671  pdata[jj] = eindex;
672  }
673  } else if (etype == -9) {
674  pdata[jj] = eindex;
675  } else if (nrn_semantics_is_ion(etype)) { // ion pointer
676  pdata[jj] = eindex;
677  } else if (nrn_semantics_is_ionstyle(etype)) {
678  // ionstyle can be explicit instead of pointer to int*
679  pdata[jj] = eindex;
680  } else if (etype == -2) { // an ion and this is the iontype
681  pdata[jj] = eindex;
682  } else if (etype == -4) { // netsend (_tqitem)
683  pdata[jj] = ml_vdata_offset + eindex;
684  // printf("etype %d jj=%d eindex=%d pdata=%d\n", etype, jj, eindex, pdata[jj]);
685  } else if (etype == -6) { // pntproc
686  pdata[jj] = ml_vdata_offset + eindex;
687  // printf("etype %d jj=%d eindex=%d pdata=%d\n", etype, jj, eindex, pdata[jj]);
688  } else if (etype == -7) { // bbcorepointer
689  pdata[jj] = ml_vdata_offset + eindex;
690  // printf("etype %d jj=%d eindex=%d pdata=%d\n", etype, jj, eindex, pdata[jj]);
691  } else if (etype == -11) { // random
692  pdata[jj] = ml_vdata_offset + eindex;
693  } else { // uninterpreted
694  assert(eindex != -3); // avoided if last
695  pdata[jj] = 0;
696  }
697  }
698  }
699  return pdata;
700 }
701 
702 void part2_clean() {
704 
705  if (corenrn_direct) {
707  }
708 
709  delete[] cellgroups_;
710  cellgroups_ = NULL;
711 }
712 
713 std::vector<std::vector<NetCon*>> CellGroup::deferred_netcons;
714 
717  for (int tid = 0; tid < nrn_nthread; ++tid) {
718  deferred_netcons.push_back(std::move(cgs[tid].netcons));
719  }
720 }
721 
723  deferred_netcons.clear();
724 }
725 
726 // Vector.play information.
727 // Must play into a data element in this thread
728 // File format is # of play instances in this thread (generally VecPlayContinuous)
729 // For each Play instance
730 // VecPlayContinuousType (4), pd (index), y.size, yvec, tvec
731 // Other VecPlay instance types are possible, such as VecPlayContinuous with
732 // a discon vector or VecPlayStep with a DT or tvec, but are not implemented
733 // at present. Assertion errors are generated if not type 0 of if we
734 // cannot determine the index into the NrnThread._data .
735 
736 int nrnthread_dat2_vecplay(int tid, std::vector<int>& indices) {
737  if (tid >= nrn_nthread) {
738  return 0;
739  }
740  NrnThread& nt = nrn_threads[tid];
741 
742  // add the index of each instance in fixed_play_ for thread tid.
743  // error if not a VecPlayContinuous with no discon vector
744  int i = 0;
745  for (auto& item: *net_cvode_instance->fixed_play_) {
746  if (item->type() == VecPlayContinuousType) {
747  auto* vp = static_cast<VecPlayContinuous*>(item);
748  if (vp->discon_indices_ == NULL) {
749  if (vp->ith_ == nt.id) {
750  assert(vp->y_ && vp->t_);
751  indices.push_back(i);
752  }
753  } else {
754  assert(0);
755  }
756  } else {
757  assert(0);
758  }
759  ++i;
760  }
761 
762  return 1;
763 }
764 
766  int i,
767  int& vptype,
768  int& mtype,
769  int& ix,
770  int& sz,
771  double*& yvec,
772  double*& tvec,
773  int& last_index,
774  int& discon_index,
775  int& ubound_index) {
776  if (tid >= nrn_nthread) {
777  return 0;
778  }
779  NrnThread& nt = nrn_threads[tid];
780 
782  if (fp->at(i)->type() == VecPlayContinuousType) {
783  auto* const vp = static_cast<VecPlayContinuous*>(fp->at(i));
784  if (!vp->discon_indices_) {
785  if (vp->ith_ == nt.id) {
786  auto* pd = static_cast<double*>(vp->pd_);
787  int found = 0;
788  vptype = vp->type();
789  for (NrnThreadMembList* tml = nt.tml; tml; tml = tml->next) {
790  if (nrn_is_artificial_[tml->index]) {
791  continue;
792  }
793  Memb_list* ml = tml->ml;
794  auto const legacy_index = ml->legacy_index(pd);
795  if (legacy_index >= 0) {
796  mtype = tml->index;
797  ix = legacy_index;
798  sz = vector_capacity(vp->y_);
799  yvec = vector_vec(vp->y_);
800  tvec = vector_vec(vp->t_);
801  found = 1;
802  break;
803  }
804  }
805  assert(found);
806  // following 3 used for direct-mode.
807  last_index = vp->last_index_;
808  discon_index = vp->discon_index_;
809  ubound_index = vp->ubound_index_;
810  return 1;
811  }
812  }
813  }
814 
815  return 0;
816 }
817 
818 /** getting one item at a time from CoreNEURON **/
819 void core2nrn_vecplay(int tid, int i, int last_index, int discon_index, int ubound_index) {
820  if (tid >= nrn_nthread) {
821  return;
822  }
824  assert(fp->at(i)->type() == VecPlayContinuousType);
825  VecPlayContinuous* vp = (VecPlayContinuous*) fp->at(i);
826  vp->last_index_ = last_index;
827  vp->discon_index_ = discon_index;
828  vp->ubound_index_ = ubound_index;
829 }
830 
831 /** start the vecplay events **/
833  for (auto& item: *net_cvode_instance->fixed_play_) {
834  if (item->type() == VecPlayContinuousType) {
835  auto* vp = static_cast<VecPlayContinuous*>(item);
836  NrnThread* nt = nrn_threads + vp->ith_;
837  vp->e_->send(vp->t_->elem(vp->ubound_index_), net_cvode_instance, nt);
838  }
839  }
840 }
841 
842 /** getting one item at a time from nrn2core_transfer_WATCH **/
843 void nrn2core_transfer_WatchCondition(WatchCondition* wc, void (*cb)(int, int, int, int, int)) {
844  Point_process* pnt = wc->pnt_;
845  assert(pnt);
846  int tid = ((NrnThread*) (pnt->_vnt))->id;
847  int pnttype = pnt->prop->_type;
848  int watch_index = wc->watch_index_;
849  int triggered = wc->flag_ ? 1 : 0;
850  int pntindex = CellGroup::nrncore_pntindex_for_queue(pnt->prop, tid, pnttype);
851  (*cb)(tid, pnttype, pntindex, watch_index, triggered);
852 
853  // This transfers CvodeThreadData activated WatchCondition
854  // information. All WatchCondition stuff is implemented in netcvode.cpp.
855  // cvodeobj.h: HTList* CvodeThreadData.watch_list_
856  // netcon.h: WatchCondition
857  // On the NEURON side, WatchCondition is activated within a
858  // NET_RECEIVE block with the NMODL WATCH statement translated into a
859  // call to _nrn_watch_activate implmented as a function in netcvode.cpp.
860  // Note that on the CoreNEURON side, all the WATCH functionality is
861  // implemented within the mod file translation, and the info from this side
862  // is used to assign a value in the location specified by the
863  // _watch_array(flag) macro.
864  // The return from psolve must transfer back the correct conditions
865  // so that NEURON can continue with a classical psolve, or, if CoreNEURON
866  // continues, receive the correct transfer of conditions back from NEURON
867  // again.
868  // Note: the reason CoreNEURON does not already have the correct watch
869  // condition from phase2 setup is because, on the NEURON side,
870  // _nrn_watch_activate fills in the _watch_array[0] with a pointer to
871  // WatchList and _watch_array[i] with a pointer to WatchCondition.
872  // Activation consists of removing all conditions from a HTList (HeadTailList)
873  // and _watch_array[0] (only on the first _nrn_watch_activate call from a
874  // NET_RECEIVE delivery event). And appending to _watch_array[0] and
875  // Append to the HTList which is the CvodeThreadData.watch_list_;
876  // But on the CoreNEURON side, _watch_array[0] is unused and _watch_array[i]
877  // is a two bit integer. Bit 2 on means the WATCH is activated Bit 1
878  // is used to determine the transition from false to true for doing a
879  // net_send (immediate deliver).
880 }
881 
882 // for faster determination of the movable index given the type
883 static std::map<int, int> type2movable;
884 static void setup_type2semantics() {
885  if (type2movable.empty()) {
886  for (int type = 0; type < n_memb_func; ++type) {
887  int* ds = memb_func[type].dparam_semantics.get();
888  if (ds) {
889  for (int psz = 0; psz < bbcore_dparam_size[type]; ++psz) {
890  if (ds[psz] == -4) { // netsend semantics
891  type2movable[type] = psz;
892  }
893  }
894  }
895  }
896  }
897 }
898 
899 // Copying TQItem information for transfer to CoreNEURON has been factored
900 // out of nrn2core_transfer_tqueue since, if BinQ is being used, it involves
901 // iterating over the BinQ as well as the normal TQueue.
902 static void set_info(TQItem* tqi,
903  int tid,
904  NrnCoreTransferEvents* core_te,
905  std::unordered_map<NetCon*, std::vector<size_t>>& netcon2intdata,
906  std::unordered_map<PreSyn*, std::vector<size_t>>& presyn2intdata,
907  std::unordered_map<double*, std::vector<size_t>>& weight2intdata) {
908  DiscreteEvent* de = (DiscreteEvent*) (tqi->data_);
909  int type = de->type();
910  double tdeliver = tqi->t_;
911  core_te->type.push_back(type);
912  core_te->td.push_back(tdeliver);
913 
914  switch (type) {
915  case DiscreteEventType: { // 0
916  } break;
917  case NetConType: { // 2
918  NetCon* nc = (NetCon*) de;
919  // To find the i for cg.netcons[i] == nc
920  // and since there are generally very many fewer nc on the queue
921  // than netcons. Begin a nc2i map that we can fill in for i
922  // later in one sweep through the cg.netcons.
923  // Here is where it goes.
924  size_t iloc = core_te->intdata.size();
925  core_te->intdata.push_back(-1);
926  // But must take into account the rare situation where the same
927  // NetCon is on the queue more than once. Hence the std::vector
928  netcon2intdata[nc].push_back(iloc);
929  } break;
930  case SelfEventType: { // 3
931  SelfEvent* se = (SelfEvent*) de;
932  Point_process* pnt = se->target_;
933  int type = pnt->prop->_type;
934  int movable_index = type2movable[type];
935  double* wt = se->weight_;
936 
937  core_te->intdata.push_back(type);
938  core_te->dbldata.push_back(se->flag_);
939 
940  // All SelfEvent have a target. A SelfEvent only has a weight if
941  // it was issued in response to a NetCon event to the target
942  // NET_RECEIVE block. Determination of Point_process* target_ on the
943  // CoreNEURON side uses mechanism type and instance index from here
944  // on the NEURON side. And the latter can be determined now from pnt.
945  // On the other hand, if there is a non-null weight pointer, its index
946  // can only be determined by sweeping over all NetCon.
947 
948  // Introduced the public static method below because ARTIFICIAL_CELL
949  // are not located in NrnThread and are not cache efficient.
951  core_te->intdata.push_back(index);
952 
953  size_t iloc_wt = core_te->intdata.size();
954  if (wt) { // don't bother with NULL weights
955  weight2intdata[wt].push_back(iloc_wt);
956  }
957  core_te->intdata.push_back(-1); // If NULL weight this is the indicator
958  // Each of these holds a TQItem*
959  Datum* const movable = se->movable_;
960  Datum* const pnt_movable = pnt->prop->dparam + movable_index;
961  // Only one SelfEvent on the queue for a given point process can be
962  // movable
963  bool const condition = movable && (*movable).get<TQItem*>() == tqi;
964  core_te->intdata.push_back(condition);
965  if (condition) {
966  assert(pnt_movable && (*pnt_movable).get<TQItem*>() == tqi);
967  }
968 
969  } break;
970  case PreSynType: { // 4
971  PreSyn* ps = (PreSyn*) de;
972 
973  // NEURON puts PreSyn on every thread queue
974  // Skip if PreSyn not associated with this thread.
975  bool skip = (ps->nt_ && (ps->nt_->id != tid)) ? true : false;
976  // Skip if effectively an InputPresyn (ps->nt_ == NULL)
977  // and this is not thread 0.
978  skip = (!ps->nt_ && tid != 0) ? true : skip;
979  if (skip) {
980  // erase what was already added
981  core_te->type.pop_back();
982  core_te->td.pop_back();
983  break;
984  }
985  // Output PreSyn similar to NetCon but more data.
986  // Input PreSyn (ps->output_index = -1 and ps->gid >= 0)
987  // is distinquished from PreSyn (ps->output_index == ps->gid
988  // or both negative) by the first item of 0 or 1 respectively followed
989  // by gid or presyn index respectively.
990  // That is:
991  // Output PreSyn format is 0, presyn index
992  // initialized to -1 and figured out from presyn2intdata, and
993  // ps->delay_
994  // Input PreSyn format is 1, gid, and ps->delay_
995  if (ps->output_index_ < 0 && ps->gid_ >= 0) {
996  // InputPreSyn on the CoreNEURON side
997  core_te->intdata.push_back(1);
998  core_te->intdata.push_back(ps->gid_);
999  } else {
1000  // PreSyn on the NEURON side
1001  core_te->intdata.push_back(0);
1002  size_t iloc = core_te->intdata.size();
1003  core_te->intdata.push_back(-1);
1004  presyn2intdata[ps].push_back(iloc);
1005  }
1006  // CoreNEURON PreSyn has no notion of use_min_delay_ so if that
1007  // is in effect, then the send time is actually tt - nc->delay_
1008  // (Note there is no core2nrn inverse as PreSyn does not appear on
1009  // the CoreNEURON event queue).
1010  if (ps->use_min_delay_) {
1011  core_te->td.back() -= ps->delay_;
1012  }
1013  } break;
1014  case HocEventType: { // 5
1015  // Not supported in CoreNEURON, discard and print a warning.
1016  core_te->td.pop_back();
1017  core_te->type.pop_back();
1018  // Delivery time was often reduced by a quarter step to avoid
1019  // fixed step roundoff problems.
1020  Fprintf(stderr,
1021  "WARNING: CVode.event(...) for delivery at time step nearest %g discarded. "
1022  "CoreNEURON cannot presently handle interpreter events (rank %d, thread %d).\n",
1023  tdeliver,
1024  nrnmpi_myid,
1025  tid);
1026  } break;
1027  case PlayRecordEventType: { // 6
1028  } break;
1029  case NetParEventType: { // 7
1030  } break;
1031  default: {
1032  } break;
1033  }
1034 }
1035 
1037  if (tid >= nrn_nthread) {
1038  return NULL;
1039  }
1040 
1042 
1044 
1045  // see comments below about same object multiple times on queue
1046  // and single sweep fill
1047  std::unordered_map<NetCon*, std::vector<size_t>> netcon2intdata;
1048  std::unordered_map<PreSyn*, std::vector<size_t>> presyn2intdata;
1049  std::unordered_map<double*, std::vector<size_t>> weight2intdata;
1050 
1051  NrnThread& nt = nrn_threads[tid];
1053  TQItem* tqi;
1054  auto& cg = cellgroups_[tid];
1055  // make sure all buffered interthread events are on the queue
1057 
1058  // Iterate over all tqueue items to record info needed for transfer to
1059  // coreneuron. The atomic_dq removes items from the queue but misses
1060  // BinQ items if present. So need separate iteration for that (hence the
1061  // factoring out of the loop bodies into set_info.)
1062  while ((tqi = tq->atomic_dq(1e15)) != NULL) {
1063  set_info(tqi, tid, core_te, netcon2intdata, presyn2intdata, weight2intdata);
1064  }
1065  if (nrn_use_bin_queue_) {
1066  // does not remove items but the entire queue will be cleared
1067  // before using again.
1068  for (tqi = tq->binq()->first(); tqi; tqi = tq->binq()->next(tqi)) {
1069  set_info(tqi, tid, core_te, netcon2intdata, presyn2intdata, weight2intdata);
1070  }
1071  }
1072 
1073  // fill in the integers for the pointer translation
1074 
1075  // NEURON NetCon* to CoreNEURON index into nt.netcons
1076  for (int i = 0; i < cg.n_netcon; ++i) {
1077  NetCon* nc = cg.netcons[i];
1078  auto iter = netcon2intdata.find(nc);
1079  if (iter != netcon2intdata.end()) {
1080  for (auto iloc: iter->second) {
1081  core_te->intdata[iloc] = i;
1082  }
1083  }
1084  }
1085 
1086  // NEURON PreSyn* to CoreNEURON index into nt.presyns
1087 #define NRN_SENTINAL 100000000000
1088  for (int i = 0; i < cg.n_presyn; ++i) {
1089  PreSyn* ps = cg.output_ps[i];
1090  auto iter = presyn2intdata.find(ps);
1091  if (iter != presyn2intdata.end()) {
1092  // not visited twice
1093  assert(iter->second[0] < NRN_SENTINAL);
1094  for (auto iloc: iter->second) {
1095  core_te->intdata[iloc] = i;
1096  }
1097  presyn2intdata[ps][0] = i + NRN_SENTINAL;
1098  }
1099  }
1100  // all presyn2intdata should have been visited so all
1101  // presyn2intdata[ps][0] must be >= NRN_SENTINAL
1102  for (auto& iter: presyn2intdata) {
1103  assert(iter.second[0] >= NRN_SENTINAL);
1104  }
1105 
1106  // NEURON SelfEvent weight* into CoreNEURON index into nt.netcons
1107  // On the CoreNEURON side we find the NetCon and then the
1108  // nc.u.weight_index_
1109  for (int i = 0; i < cg.n_netcon; ++i) {
1110  NetCon* nc = cg.netcons[i];
1111  double* wt = nc->weight_;
1112  auto iter = weight2intdata.find(wt);
1113  if (iter != weight2intdata.end()) {
1114  for (auto iloc: iter->second) {
1115  core_te->intdata[iloc] = i;
1116  }
1117  }
1118  }
1119 
1120  return core_te;
1121 }
1122 
1123 /** @brief Initialize queues before transfer
1124  Probably aleady clear, but if binq then must be initialized to time.
1125  */
1126 void core2nrn_clear_queues(double time) {
1127  nrn_threads[0]._t = time; // used by clear_event_queue
1129 }
1130 
1131 /** @brief Called from CoreNEURON core2nrn_tqueue_item.
1132  */
1133 void core2nrn_NetCon_event(int tid, double td, size_t nc_index) {
1134  assert(tid < nrn_nthread);
1135  NrnThread& nt = nrn_threads[tid];
1136  // cellgroups_ has been deleted but deletion of cg.netcons was deferred
1137  // (and will be deleted on return from nrncore_run).
1138  // This is tragic for memory usage. There are more NetCon's than anything.
1139  // Would be better to save the memory at a cost of single iteration over
1140  // NetCon.
1141  NetCon* nc = CellGroup::deferred_netcons[tid][nc_index];
1142  nc->send(td, net_cvode_instance, &nt);
1143 }
1144 
1145 static void core2nrn_SelfEvent_helper(int tid,
1146  double td,
1147  int tar_type,
1148  int tar_index,
1149  double flag,
1150  double* weight,
1151  int is_movable) {
1152  if (type2movable.empty()) {
1154  }
1155  Memb_list* ml = nrn_threads[tid]._ml_list[tar_type];
1156  Point_process* pnt;
1157  if (ml) {
1158  pnt = ml->pdata[tar_index][1].get<Point_process*>();
1159  } else {
1160  // In NEURON world, ARTIFICIAL_CELLs do not live in NrnThread.
1161  // And the old deferred_type2artdata_ only gave us data, not pdata.
1162  // So this is where I decided to replace the more
1163  // expensive deferred_type2artml_.
1164  ml = CellGroup::deferred_type2artml_[tid][tar_type];
1165  pnt = ml->pdata[tar_index][1].get<Point_process*>();
1166  }
1167 
1168  // Needs to be tested when permuted on CoreNEURON side.
1169  assert(tar_type == pnt->prop->_type);
1170  assert(tar_index == CellGroup::nrncore_pntindex_for_queue(pnt->prop, tid, tar_type));
1171 
1172  int const movable_index = type2movable[tar_type];
1173  auto* const movable_arg = pnt->prop->dparam + movable_index;
1174  auto* const old_movable_arg = (*movable_arg).get<TQItem*>();
1175  nrn_net_send(movable_arg, weight, pnt, td, flag);
1176  if (!is_movable) {
1177  *movable_arg = old_movable_arg;
1178  }
1179 }
1180 
1182  double td,
1183  int tar_type,
1184  int tar_index,
1185  double flag,
1186  size_t nc_index,
1187  int is_movable) {
1188  assert(tid < nrn_nthread);
1189  NetCon* nc = CellGroup::deferred_netcons[tid][nc_index];
1190 
1191 #if 1
1192  // verify nc->target_ consistent with tar_type, tar_index.
1193  Memb_list* ml = nrn_threads[tid]._ml_list[tar_type];
1194  auto* pnt = ml->pdata[tar_index][1].get<Point_process*>();
1195  assert(nc->target_ == pnt);
1196 #endif
1197 
1198  double* weight = nc->weight_;
1199  core2nrn_SelfEvent_helper(tid, td, tar_type, tar_index, flag, weight, is_movable);
1200 }
1201 
1203  double td,
1204  int tar_type,
1205  int tar_index,
1206  double flag,
1207  int is_movable) {
1208  assert(tid < nrn_nthread);
1209  double* weight = NULL;
1210  core2nrn_SelfEvent_helper(tid, td, tar_type, tar_index, flag, weight, is_movable);
1211 }
1212 
1213 // Set of the voltage indices in which PreSyn.flag_ == true
1214 void core2nrn_PreSyn_flag(int tid, std::set<int> presyns_flag_true) {
1215  if (tid >= nrn_nthread) {
1216  return;
1217  }
1218  NetCvodeThreadData& nctd = net_cvode_instance->p[tid];
1219  hoc_Item* pth = nctd.psl_thr_;
1220  if (pth) {
1221  hoc_Item* q;
1222  // turn off all the PreSyn.flag_ as they might have been turned off
1223  // during the psolve on the coreneuron side.
1224  ITERATE(q, pth) {
1225  PreSyn* ps = (PreSyn*) VOIDITM(q);
1226  ps->flag_ = false;
1227  }
1228  if (presyns_flag_true.empty()) {
1229  return;
1230  }
1231  ITERATE(q, pth) {
1232  PreSyn* ps = (PreSyn*) VOIDITM(q);
1233  assert(ps->nt_ == (nrn_threads + tid));
1234  if (ps->thvar_) {
1235  int type = 0;
1236  int index_v = -1;
1237  nrn_dblpntr2nrncore(ps->thvar_, *ps->nt_, type, index_v);
1238  assert(type == voltage);
1239  if (presyns_flag_true.erase(index_v)) {
1240  ps->flag_ = true;
1241  if (presyns_flag_true.empty()) {
1242  break;
1243  }
1244  }
1245  }
1246  }
1247  }
1248 }
1249 
1250 // Add the voltage indices in which PreSyn.flag_ == true to the set.
1251 void nrn2core_PreSyn_flag(int tid, std::set<int>& presyns_flag_true) {
1252  if (tid >= nrn_nthread) {
1253  return;
1254  }
1255  NetCvodeThreadData& nctd = net_cvode_instance->p[tid];
1256  hoc_Item* pth = nctd.psl_thr_;
1257  if (pth) {
1258  hoc_Item* q;
1259  ITERATE(q, pth) {
1260  auto* ps = static_cast<PreSyn*>(VOIDITM(q));
1261  assert(ps->nt_ == (nrn_threads + tid));
1262  if (ps->flag_ && ps->thvar_) {
1263  int type = 0;
1264  int index_v = -1;
1265  nrn_dblpntr2nrncore(ps->thvar_, *ps->nt_, type, index_v);
1266  assert(type == voltage);
1267  presyns_flag_true.insert(index_v);
1268  }
1269  }
1270  }
1271 }
1272 
1273 // For each watch index, activate the WatchCondition
1274 void core2nrn_watch_activate(int tid, int type, int watch_begin, Core2NrnWatchInfo& wi) {
1275  if (tid >= nrn_nthread) {
1276  return;
1277  }
1278  NrnThread& nt = nrn_threads[tid];
1279  Memb_list* ml = nt._ml_list[type];
1280  for (size_t i = 0; i < wi.size(); ++i) {
1281  Core2NrnWatchInfoItem& active_watch_items = wi[i];
1282  Datum* pd = ml->pdata[i];
1283  int r = 0; // first activate removes formerly active from pd.
1284  for (auto watch_item: active_watch_items) {
1285  int watch_index = watch_item.first;
1286  bool above_thresh = watch_item.second;
1287  auto* wc = pd[watch_index].get<WatchCondition*>();
1288  if (!wc) { // if any do not exist in this instance, create them all
1289  // with proper callback and flag.
1290  (*(nrn_watch_allocate_[type]))(pd);
1291  wc = pd[watch_index].get<WatchCondition*>();
1292  }
1294  pd + watch_begin, wc->c_, watch_index - watch_begin, wc->pnt_, r++, wc->nrflag_);
1295  wc->flag_ = above_thresh ? 1 : 0;
1296  // If flag_ is 1
1297  // there will not be a (immediate) transition event
1298  // til the value() becomes negative again and then goes positive.
1299  }
1300  }
1301 }
1302 
1303 // nrn<->corenrn PatternStim
1304 
1306 static int patternstim_type;
1307 
1308 // Info from NEURON PatternStim at beginning of psolve.
1310  if (!patternstim_type) {
1311  for (int i = 3; i < n_memb_func; ++i) {
1312  if (strcmp(memb_func[i].sym->name, "PatternStim") == 0) {
1313  patternstim_type = i;
1314  break;
1315  }
1316  }
1317  }
1318 
1320  assert(ml.nodecount == 1);
1322 }
1323 
1324 
1325 // Info from NEURON subworlds at beginning of psolve.
1327  int& subworld_index,
1328  int& subworld_rank,
1329  int& numprocs_subworld,
1330  int& numprocs_world) {
1331 #ifdef NRNMPI
1332  nrnmpi_get_subworld_info(
1333  &cnt, &subworld_index, &subworld_rank, &numprocs_subworld, &numprocs_world);
1334 #else
1335  cnt = 0;
1336  subworld_index = -1;
1337  subworld_rank = 0;
1338  numprocs_subworld = 1;
1339  numprocs_world = 1;
1340 #endif
1341 }
std::vector< MlWithArtItem > MlWithArt
Definition: cell_group.h:15
std::pair< int, Memb_list * > MlWithArtItem
Definition: cell_group.h:12
TQItem * next(TQItem *)
Definition: tqueue.cpp:368
TQItem * first()
Iterate in ascending bin order starting at current bin.
Definition: tqueue.cpp:357
std::vector< NetCon * > netcons
Definition: cell_group.h:38
int group_id
Definition: cell_group.h:23
std::vector< int > output_vindex
Definition: cell_group.h:35
static void clean_deferred_netcons()
int n_real_output
Definition: cell_group.h:28
int * ml_vdata_offset
Definition: cell_group.h:31
std::vector< int > output_gid
Definition: cell_group.h:34
int n_mech
Definition: cell_group.h:30
static void defer_clean_netcons(CellGroup *)
static int nrncore_pntindex_for_queue(Prop *p, int tid, int type)
Definition: cell_group.h:86
int * netcon_srcgid
Definition: cell_group.h:39
static void clean_art(CellGroup *)
Definition: cell_group.cpp:596
Memb_list ** type2ml
Definition: cell_group.h:22
static std::vector< std::vector< NetCon * > > deferred_netcons
Definition: cell_group.h:75
std::vector< int > netcon_negsrcgid_tid
Definition: cell_group.h:41
int n_presyn
Definition: cell_group.h:26
MlWithArt mlwithart
Definition: cell_group.h:49
int n_real_cell
Definition: cell_group.h:24
std::vector< PreSyn * > output_ps
Definition: cell_group.h:33
int n_netcon
Definition: cell_group.h:37
static Deferred_Type2ArtMl deferred_type2artml_
Definition: cell_group.h:74
int * netcon_pnttype
Definition: cell_group.h:44
int * netcon_pntindex
Definition: cell_group.h:45
DatumIndices * datumindices
Definition: cell_group.h:48
int n_output
Definition: cell_group.h:27
int ndiam
Definition: cell_group.h:29
bool flag_
Definition: netcon.h:202
int * datum_index
Definition: datum_indices.h:20
int * datum_type
Definition: datum_indices.h:19
virtual int type()
Definition: netcon.h:72
Definition: netcon.h:87
double * weight_
Definition: netcon.h:116
int cnt_
Definition: netcon.h:118
virtual void send(double sendtime, NetCvode *, NrnThread *)
Definition: netcvode.cpp:2941
double delay_
Definition: netcon.h:113
Point_process * target_
Definition: netcon.h:115
NetCvodeThreadData * p
Definition: netcvode.h:249
std::vector< PlayRecord * > * fixed_play_
Definition: netcvode.h:129
hoc_Item * psl_thr_
Definition: netcvode.h:52
Definition: netcon.h:258
int output_index_
Definition: netcon.h:308
NrnThread * nt_
Definition: netcon.h:303
int gid_
Definition: netcon.h:309
double delay_
Definition: netcon.h:296
neuron::container::data_handle< double > thvar_
Definition: netcon.h:297
int use_min_delay_
Definition: netcon.h:306
Datum * movable_
Definition: netcon.h:171
double * weight_
Definition: netcon.h:170
Point_process * target_
Definition: netcon.h:169
double flag_
Definition: netcon.h:168
BinQ * binq()
Definition: tqueue.hpp:123
TQItem * atomic_dq(double til)
Definition: tqueue.cpp:245
virtual int type()
Definition: vrecitem.h:304
double(* c_)(Point_process *)
Definition: netcon.h:236
Point_process * pnt_
Definition: netcon.h:235
double nrflag_
Definition: netcon.h:234
int watch_index_
Definition: netcon.h:241
static Frame * fp
Definition: code.cpp:96
#define cnt
Definition: tqueue.hpp:44
#define nodeindices
Definition: md1redef.h:35
#define area
Definition: md1redef.h:12
#define v
Definition: md1redef.h:11
#define weights
Definition: md1redef.h:42
#define i
Definition: md1redef.h:19
#define pdata
Definition: md1redef.h:37
#define VecPlayContinuousType
Definition: vrecitem.h:17
#define PlayRecordEventType
Definition: vrecitem.h:18
DLFCN_NOINLINE DLFCN_EXPORT void * dlsym(void *handle, const char *name)
Definition: dlfcn.c:447
void nrnran123_getids3(nrnran123_State *s, std::uint32_t *id1, std::uint32_t *id2, std::uint32_t *id3)
Definition: nrnran123.cpp:97
static RNG::key_type k
Definition: nrnran123.cpp:9
void nrnran123_setseq(nrnran123_State *s, std::uint32_t seq, char which)
Set a Random123 sequence for a sequnece ID and which selector.
Definition: nrnran123.cpp:55
void nrnran123_getseq(nrnran123_State *s, std::uint32_t *seq, char *which)
Get sequence number and selector from an nrnran123_State object.
Definition: nrnran123.cpp:50
double * hoc_val_pointer(const char *s)
Definition: code2.cpp:728
Symbol * hoc_lookup(const char *)
Definition: symbol.cpp:59
static int c
Definition: hoc.cpp:169
#define assert(ex)
Definition: hocassrt.h:24
#define USERDOUBLE
Definition: hocdec.h:84
bool is_array(const Symbol &sym)
Definition: hocdec.h:136
#define VOIDITM(q)
Definition: hoclist.h:89
bool nrn_semantics_is_ion(int i)
Definition: ion_semantics.h:6
bool nrn_semantics_is_ionstyle(int i)
Definition: ion_semantics.h:9
#define MORPHOLOGY
Definition: membfunc.hpp:59
#define ITERATE(itm, lst)
Definition: model.h:18
const char * name
Definition: init.cpp:16
void move(Item *q1, Item *q2, Item *q3)
Definition: list.cpp:200
NrnThread * nrn_threads
Definition: multicore.cpp:56
void clear_event_queue()
Definition: cvodestb.cpp:47
double * vector_vec(IvocVect *v)
Definition: ivocvect.cpp:19
bool nrn_use_bin_queue_
Flag to use the bin queue.
Definition: netcvode.cpp:39
int nrn_nthread
Definition: multicore.cpp:55
std::vector< int > & nrn_mech_random_indices(int type)
void hoc_execerror(const char *s1, const char *s2)
Definition: nrnoc_aux.cpp:39
const char * bbcore_write_version
Definition: nrnoc_aux.cpp:24
static void * emalloc(size_t size)
Definition: mpispike.cpp:30
int nrn_is_ion(int)
int vector_capacity(IvocVect *v)
Definition: ivocvect.cpp:16
static int pntindex
Definition: prcellstate.cpp:24
uint32_t nrnran123_get_globalindex()
Definition: nrnran123.cpp:112
handle_interface< non_owning_identifier< storage > > handle
Non-owning handle to a Mechanism instance.
icycle< ncycle;++icycle) { int istride=stride[icycle];nrn_pragma_acc(loop vector) nrn_pragma_omp(loop bind(parallel)) for(int icore=0;icore< warpsize;++icore) { int i=ii+icore;if(icore< istride) { int ip=GPU_PARENT(i);GPU_RHS(i) -=GPU_B(i) *GPU_RHS(ip);GPU_RHS(i)/=GPU_D(i);} i+=istride;} ii+=istride;} }}void solve_interleaved2(int ith) { NrnThread *nt=nrn_threads+ith;InterleaveInfo &ii=interleave_info[ith];int nwarp=ii.nwarp;if(nwarp==0) return;int ncore=nwarp *warpsize;int *ncycles=ii.cellsize;int *stridedispl=ii.stridedispl;int *strides=ii.stride;int *rootbegin=ii.firstnode;int *nodebegin=ii.lastnode;if(0) { nrn_pragma_acc(parallel loop gang present(nt[0:1], strides[0:nstride], ncycles[0:nwarp], stridedispl[0:nwarp+1], rootbegin[0:nwarp+1], nodebegin[0:nwarp+1]) async(nt->stream_id)) nrn_pragma_omp(target teams loop map(present, alloc:nt[:1], strides[:nstride], ncycles[:nwarp], stridedispl[:nwarp+1], rootbegin[:nwarp+1], nodebegin[:nwarp+1])) for(int icore=0;icore< ncore;icore+=warpsize) { solve_interleaved2_loop_body(nt, icore, ncycles, strides, stridedispl, rootbegin, nodebegin);} nrn_pragma_acc(wait(nt->stream_id)) } else { for(int icore=0;icore< ncore;icore+=warpsize) { solve_interleaved2_loop_body(nt, icore, ncycles, strides, stridedispl, rootbegin, nodebegin);} }}void solve_interleaved1(int ith) { NrnThread *nt=nrn_threads+ith;int ncell=nt-> ncell
Definition: cellorder.cpp:784
int Sprintf(char(&buf)[N], const char *fmt, Args &&... args)
Type-safe sprintf replacement using snprintf with automatic buffer size deduction.
Definition: wrap_sprintf.h:28
if(ncell==0)
Definition: cellorder.cpp:785
#define HocEventType
Definition: netcon.h:48
#define NetParEventType
Definition: netcon.hpp:27
#define PreSynType
Definition: netcon.hpp:26
#define DiscreteEventType
Definition: netcon.hpp:22
#define SelfEventType
Definition: netcon.hpp:25
#define NetConType
Definition: netcon.hpp:24
static List * info
static int watch_index
Definition: nocpout.cpp:164
neuron::model_sorted_token nrn_ensure_model_data_are_sorted()
Ensure neuron::container::* data are sorted.
Definition: treeset.cpp:2182
short * nrn_is_artificial_
Definition: init.cpp:214
int nrnthread_dat1(int tid, int &n_presyn, int &n_netcon, std::vector< int > &output_gid, int *&netcon_srcgid, std::vector< int > &netcon_negsrcgid_tid)
CellGroup * cellgroups_
size_t nrnthreads_type_return(int type, int tid, double *&data, std::vector< double * > &mdata)
Return location for CoreNEURON to copy data into.
void nrn2core_transfer_WatchCondition(WatchCondition *wc, void(*cb)(int, int, int, int, int))
getting one item at a time from nrn2core_transfer_WATCH
void nrnthread_group_ids(int *grp)
NrnCoreTransferEvents * nrn2core_transfer_tqueue(int tid)
void nrnthread_dat3_cell_count(int &cell_count)
int nrnthread_dat2_corepointer_mech(int tid, int type, int &icnt, int &dcnt, int *&iArray, double *&dArray)
void core2nrn_NetCon_event(int tid, double td, size_t nc_index)
Called from CoreNEURON core2nrn_tqueue_item.
int nrnthread_dat2_3(int tid, int nweight, int *&output_vindex, double *&output_threshold, int *&netcon_pnttype, int *&netcon_pntindex, double *&weights, double *&delays)
static void core2nrn_SelfEvent_helper(int tid, double td, int tar_type, int tar_index, double flag, double *weight, int is_movable)
void map_coreneuron_callbacks(void *handle)
Populate function pointers by mapping function pointers for callback.
void nrnthread_dat3_cellmapping(int i, int &gid, int &nsec, int &nseg, int &n_seclist)
void * get_global_dbl_item(void *p, const char *&name, int &size, double *&val)
void nrn2core_PreSyn_flag(int tid, std::set< int > &presyns_flag_true)
static std::map< int, int > type2movable
bool corenrn_direct
int * datum2int(int type, Memb_list *ml, NrnThread &nt, CellGroup &cg, DatumIndices &di, int ml_vdata_offset, std::vector< int > &pointer2type)
void core2nrn_SelfEvent_event(int tid, double td, int tar_type, int tar_index, double flag, size_t nc_index, int is_movable)
void part2_clean()
int nrnthread_dat2_1(int tid, int &ncell, int &ngid, int &n_real_gid, int &nnode, int &ndiam, int &nmech, int *&tml_index, int *&ml_nodecount, int &nidata, int &nvdata, int &nweight)
int nrnthread_dat2_vecplay(int tid, std::vector< int > &indices)
void core2nrn_SelfEvent_event_noweight(int tid, double td, int tar_type, int tar_index, double flag, int is_movable)
void nrn2core_subworld_info(int &cnt, int &subworld_index, int &subworld_rank, int &numprocs_subworld, int &numprocs_world)
static void setup_type2semantics()
int nrnthread_dat2_vecplay_inst(int tid, int i, int &vptype, int &mtype, int &ix, int &sz, double *&yvec, double *&tvec, int &last_index, int &discon_index, int &ubound_index)
void core2nrn_vecplay_events()
start the vecplay events
int core2nrn_nmodlrandom(int tid, int type, const std::vector< int > &indices, const std::vector< double > &nmodlrandom)
void core2nrn_PreSyn_flag(int tid, std::set< int > presyns_flag_true)
void write_memb_mech_types_direct(std::ostream &s)
void nrnthreads_all_weights_return(std::vector< double * > &weights)
Copy weights from all coreneuron::NrnThread to NetCon instances.
int * bbcore_dparam_size
void * nrn_patternstim_info_ref(Datum *)
int get_global_int_item(const char *name)
int nrnthread_dat2_corepointer(int tid, int &n)
void core2nrn_watch_activate(int tid, int type, int watch_begin, Core2NrnWatchInfo &wi)
char * pnt_map
Definition: init.cpp:150
void core2nrn_vecplay(int tid, int i, int last_index, int discon_index, int ubound_index)
getting one item at a time from CoreNEURON
int nrnthread_dat2_2(int tid, int *&v_parent_index, double *&a, double *&b, double *&area, double *&v, double *&diamvec)
#define NRN_SENTINAL
bbcore_write_t * nrn_bbcore_write_
Definition: init.cpp:173
int core2nrn_corepointer_mech(int tid, int type, int icnt, int dcnt, int *iArray, double *dArray)
static int patternstim_type
void nrnthread_dat3_secmapping(int i_c, int i_sec, std::string &sclname, int &nsec, int &nseg, size_t &total_lfp_factors, std::vector< size_t > &electrode_offsets, std::vector< int > &data_sec, std::vector< int > &data_seg, std::vector< double > &data_lfp)
TQueue * net_cvode_instance_event_queue(NrnThread *)
Definition: netcvode.cpp:270
NetCvode * net_cvode_instance
Definition: cvodestb.cpp:26
int nrnthread_dat2_mech(int tid, size_t i, int dsz_inst, int *&nodeindices, double *&data, int *&pdata, std::vector< uint32_t > &nmodlrandom, std::vector< int > &pointer2type)
void nrn2core_patternstim(void **info)
bbcore_write_t * nrn_bbcore_read_
Definition: init.cpp:174
double nrn_ion_charge(Symbol *)
Definition: eion.cpp:60
void * nrn_interthread_enqueue(NrnThread *)
Definition: netcvode.cpp:6800
void core2nrn_clear_queues(double time)
Initialize queues before transfer Probably aleady clear, but if binq then must be initialized to time...
NrnMappingInfo mapinfo
mapping information
static void set_info(TQItem *tqi, int tid, NrnCoreTransferEvents *core_te, std::unordered_map< NetCon *, std::vector< size_t >> &netcon2intdata, std::unordered_map< PreSyn *, std::vector< size_t >> &presyn2intdata, std::unordered_map< double *, std::vector< size_t >> &weight2intdata)
static core2nrn_callback_t cnbs[]
std::vector< Core2NrnWatchInfoItem > Core2NrnWatchInfo
@ i_membrane_
@ voltage
std::vector< std::pair< int, bool > > Core2NrnWatchInfoItem
void(*)(double *, int *, int *, int *, Memb_list *, std::size_t, Datum *, Datum *, double *, NrnThread *) bbcore_write_t
Definition: nrncore_io.h:48
int nrn_dblpntr2nrncore(neuron::container::data_handle< double > dh, NrnThread &nt, int &type, int &index)
void nrn_net_send(Datum *v, double *weight, Point_process *pnt, double td, double flag)
Definition: netcvode.cpp:2257
void _nrn_watch_activate(Datum *d, double(*c)(Point_process *), int i, Point_process *pnt, int r, double flag)
Definition: netcvode.cpp:2340
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
int * nrn_prop_param_size_
Definition: init.cpp:162
short index
Definition: cabvars.h:11
std::vector< Memb_func > memb_func
Definition: init.cpp:145
short type
Definition: cabvars.h:10
NrnWatchAllocateFunc_t * nrn_watch_allocate_
Definition: init.cpp:166
std::vector< Memb_list > memb_list
Definition: init.cpp:146
std::unordered_map< int, void(*)(Prop *)> nrn_mech_inst_destruct
Definition: init.cpp:167
int n_memb_func
Definition: init.cpp:448
int nrnmpi_myid
#define NULL
Definition: spdefs.h:105
Symlist * hoc_built_in_symlist
Definition: symbol.cpp:28
int nsub
Definition: hocdec.h:61
int sub[1]
Definition: hocdec.h:63
Compartment mapping information for a cell.
Symbol * sym
Definition: membfunc.h:74
A view into a set of mechanism instances.
Definition: nrnoc_ml.h:34
int nodecount
Definition: nrnoc_ml.h:78
int * nodeindices
Definition: nrnoc_ml.h:74
std::ptrdiff_t legacy_index(double const *ptr) const
Calculate a legacy index of the given pointer in this mechanism data.
Definition: memblist.cpp:131
Datum ** pdata
Definition: nrnoc_ml.h:75
std::vector< double * > data()
Get a vector of double* representing the model data.
Definition: memblist.cpp:64
Datum * _thread
Definition: nrnoc_ml.h:77
int get_array_dims(int variable) const
Get the array_dims of field variable.
Definition: memblist.cpp:110
Definition: section.h:105
Prop * prop
Definition: section.h:190
std::vector< double > td
std::vector< double > dbldata
std::vector< int > intdata
Compartment mapping information for NrnThread.
size_t size()
number of cells
std::vector< CellMapping * > mapping
list of cells mapping
Represent main neuron object computed by single thread.
Definition: multicore.h:58
int * _v_parent_index
Definition: multicore.h:89
NrnThreadMembList * tml
Definition: multicore.h:62
int ncell
Definition: multicore.h:64
double * node_a_storage()
Definition: multicore.cpp:1054
int id
Definition: multicore.h:66
double * node_sav_rhs_storage()
Definition: multicore.cpp:1088
double * node_area_storage()
Definition: multicore.cpp:1059
int end
Definition: multicore.h:65
Memb_list ** _ml_list
Definition: multicore.h:63
Node ** _v_node
Definition: multicore.h:90
double * node_voltage_storage()
Definition: multicore.cpp:1098
double * node_b_storage()
Definition: multicore.cpp:1064
double _t
Definition: multicore.h:59
struct NrnThreadMembList * next
Definition: multicore.h:34
Definition: hocdec.h:173
void * this_pointer
Definition: hocdec.h:178
union Object::@47 u
A point process is computed just like regular mechanisms.
Definition: section_fwd.hpp:77
Definition: section.h:231
Datum * dparam
Definition: section.h:247
short _type
Definition: section.h:244
Section to segment mapping.
Definition: model.h:47
Symbol * next
Definition: hocdec.h:133
union Symbol::@28 u
short type
Definition: model.h:48
long subtype
Definition: model.h:49
cTemplate * ctemplate
Definition: hocdec.h:126
char * name
Definition: model.h:61
double * pval
Definition: hocdec.h:112
Arrayinfo * arayinfo
Definition: hocdec.h:130
Symbol * first
Definition: hocdec.h:76
Definition: tqitem.hpp:3
double t_
Definition: tqitem.hpp:8
void * data_
Definition: tqitem.hpp:7
hoc_List * olist
Definition: hocdec.h:155
Memb_list ** _ml_list
Definition: multicore.hpp:81
Non-template stable handle to a generic value.
T get() const
Explicit conversion to any T.
int Fprintf(FILE *stream, const char *fmt, Args... args)
Definition: logger.hpp:8