NEURON
partrans.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <errno.h>
6 #include <InterViews/resource.h>
7 #include <nrnoc2iv.h>
8 #include <nrniv_mf.h>
9 #include <nrnmpi.h>
10 #include <mymath.h>
11 #if defined(HAVE_STDINT_H)
12 #include <stdint.h>
13 #endif
14 
15 #include <vector>
16 #include <map> // Introduced for NonVSrcUpdateInfo
17 #include <unordered_map> // Replaces NrnHash for MapSgid2Int and MapNode2PDbl
18 #include "partrans.h" // sgid_t and SetupTransferInfo for CoreNEURON
19 
20 
21 #if NRNLONGSGID
22 #if PARANEURON
23 extern void sgid_alltoallv(sgid_t* s, int* scnt, int* sdispl, sgid_t* r, int* rcnt, int* rdispl) {
24  if (nrn_sparse_partrans > 0) {
25  nrnmpi_long_alltoallv_sparse(s, scnt, sdispl, r, rcnt, rdispl);
26  } else {
27  nrnmpi_long_alltoallv(s, scnt, sdispl, r, rcnt, rdispl);
28  }
29 }
30 #endif // PARANEURON
31 #else // not NRNLONGSGID
32 #if PARANEURON
33 extern void sgid_alltoallv(sgid_t* s, int* scnt, int* sdispl, sgid_t* r, int* rcnt, int* rdispl) {
34  if (nrn_sparse_partrans > 0) {
35  nrnmpi_int_alltoallv_sparse(s, scnt, sdispl, r, rcnt, rdispl);
36  } else {
37  nrnmpi_int_alltoallv(s, scnt, sdispl, r, rcnt, rdispl);
38  }
39 }
40 #endif // PARANEURON
41 #endif // not NRNLONGSGID
42 
43 void nrnmpi_source_var();
44 void nrnmpi_target_var();
46 void nrn_partrans_clear();
47 static void mpi_transfer();
48 static void thread_transfer(NrnThread*);
49 static void thread_vi_compute(NrnThread*);
50 static void mk_ttd();
51 extern double t;
52 extern "C" int v_structure_change;
53 extern "C" int structure_change_cnt;
54 extern int nrn_node_ptr_change_cnt_;
55 extern "C" double* nrn_recalc_ptr(double*);
56 extern const char* bbcore_write_version;
57 // see lengthy comment in ../nrnoc/fadvance.cpp
58 // nrnmpi_v_transfer requires existence of nrnthread_v_transfer even if there
59 // is only one thread.
60 // Thread 0 does the nrnmpi_v_transfer into incoming_src_buf.
61 // Data destined for targets in thread owned memory
62 // is copied to the proper place by each thread via nrnthread_v_transfer
63 // MPI_Alltoallv is used to transfer interprocessor data.
64 // The basic assumption is that this will be mostly used for gap junctions in which
65 // most often one source voltage goes to one target or at least only a few targets.
66 // Note that the source data for nrnthread_v_transfer is in incoming_src_buf,
67 // source locations owned by thread, and source locations owned by other threads.
68 
69 /*
70 
71 16-5-2014
72 Gap junctions with extracellular require that the voltage source be v + vext.
73 
74 A solution to the v+vext problem is to create a thread specific source
75 value buffer just for extracellular nodes.
76  NODEV(_nd) + _nd->extnode->v[0] .
77  That is, if there is no extracellular the ttd.sv and and poutsrc_
78 pointers stay exactly the same. Whereas, if there is extracellular,
79 those would point into the source value buffer of the correct thread.
80  Of course, it is necessary that the source value buffer be computed
81 prior to either parallel transfer or mpi_transfer. Note that some
82 sources that are needed by another thread may not be needed by mpi and
83 vice versa. For the fixed step method, mpi transfer occurs prior to
84 thread transfer. For global variable step methods (cvode and lvardt do
85 not work with extracellular anyway):
86  1) for multisplit, mpi between ms_part3 and ms_part4
87  2) not multisplit, mpi between transfer_part1 and transfer_part2
88  with thread transfer in ms_part4 and transfer_part2.
89  Therefore it is possible to do the v+vext computation at the beginning
90 of mpi transfer except that mpi_transfer is not called if there is only
91 one process. Also, this would be very cache inefficient for threads
92 since mpi transfer is done only by thread 0.
93 
94 Therefore we need yet another callback.
95  void* nrnthread_vi_compute(NrnThread*)
96  called, if needed, at the end of update(nt) and before mpi transfer in
97 nrn_finitialize.
98 
99 */
100 
101 /*
102 
103 29-9-2016
104 
105 Want to allow a source to be any range variable at a node or in a
106 Point_process. e.g an ion concentration. The v+vext change restricted
107 sources to voltage partly in order to simplify pointer recalculation for
108 cache efficiency. If the source is not voltage, the calling context of
109 pc.source_var must be sufficient to easily store enough info to update
110 the pointer for cache efficiency or changed number of threads. ie. in
111 general, (Node*, mechtype, parray_index). If the variable is a member
112 of a Point_process, it would be simple to pass it as the third arg and
113 store the reference. However, we reject that due to not handling the
114 typical case of concentration. So we impose the limitation that
115 structural changes that destroy the Node or move the pointer to another
116 Node generally require clearing and re-declaring the source, sid, target
117 relations, (nseg change, point process relocation). Which is, in fact,
118 the current limitation on voltage sources. The substantive
119 implementation change is to provide a safe pointer update for when we
120 know the Node*. An extra map<sgid_t ssid, pair<int mechtype, int
121 parray_index> > should do nicely. with voltages NOT being in the map.
122 Also a decent error message can be generated. Finally, it is fairly
123 clear how to make this work with coreneuron by perhaps adding a triple
124 of sid, mechtype, parray_index integer vectors to the <gidgroup>_gap.dat
125 file.
126 
127 */
128 
129 #if 1 || PARANEURON
130 extern void (*nrnthread_v_transfer_)(NrnThread*); // before nonvint and BEFORE INITIAL
132 extern void (*nrnmpi_v_transfer_)(); // before nrnthread_v_transfer and after update. Called by
133  // thread 0.
135 #endif
136 #if PARANEURON
137 extern double nrnmpi_transfer_wait_;
138 extern void nrnmpi_barrier();
139 extern void nrnmpi_int_allgather(int*, int*, int);
140 extern int nrnmpi_int_allmax(int);
141 extern void sgid_alltoallv(sgid_t*, int*, int*, sgid_t*, int*, int*);
142 extern void nrnmpi_int_alltoallv(int*, int*, int*, int*, int*, int*);
143 extern void nrnmpi_dbl_alltoallv(double*, int*, int*, double*, int*, int*);
144 extern void nrnmpi_dbl_alltoallv_sparse(double*, int*, int*, double*, int*, int*);
145 #endif
146 
148 
150  int cnt;
151  double** tv; // pointers to the ParallelContext.target_var
152  double** sv; // pointers to the ParallelContext.source_var (or into MPI target buffer)
153 };
156 
157 // for the case where we need vi = v + vext as the source voltage
158 struct SourceViBuf {
159  int cnt;
160  Node** nd;
161  double* val;
162 };
164 static int n_source_vi_buf_;
165 
166 typedef std::unordered_map<sgid_t, int> MapSgid2Int;
167 typedef std::unordered_map<Node*, double*> MapNode2PDbl;
168 typedef std::vector<double*> DblPList;
169 typedef std::vector<Node*> NodePList;
170 #define PPList partrans_PPList
171 typedef std::vector<Point_process*> PPList;
172 typedef std::vector<int> IntList;
173 typedef std::vector<sgid_t> SgidList;
174 
175 static double* insrc_buf_; // Receives the interprocessor data destined for other threads.
176 static double* outsrc_buf_;
177 static double** poutsrc_; // prior to mpi copy src value to proper place in outsrc_buf_
178 static int* poutsrc_indices_; // for recalc pointers
181 static MapSgid2Int sid2insrc_; // received interprocessor sid data is
182 // associated with which insrc_buf index. Created by nrnmpi_setup_transfer
183 // and used by mk_ttd
184 
185 // ordered by calls to nrnmpi_target_var()
186 static DblPList targets_; // list of target double*
187 static SgidList sgid2targets_; // list of target sgid
188 static PPList target_pntlist_; // list of target Point_process
189 static IntList target_parray_index_; // to recompute targets_ for cache_efficint
190 
191 // ordered by calls to nrnmpi_source_var()
192 typedef std::vector<double*> DblPVec;
193 static NodePList visources_; // list of source Node*, (multiples possible)
194 static SgidList sgids_; // source gids
195 static MapSgid2Int sgid2srcindex_; // sgid2srcindex[sgids[i]] == i
196 
197 typedef std::map<sgid_t, std::pair<int, int>> NonVSrcUpdateInfo;
198 static NonVSrcUpdateInfo non_vsrc_update_info_; // source ssid -> (type,parray_index)
199 
200 
201 static int max_targets_;
202 
203 static int target_ptr_update_cnt_ = 0;
205 static int vptr_change_cnt_ = 0;
206 
207 static bool is_setup_;
208 
209 // deleted when setup_transfer called
210 // defined persistently when pargap_jacobi_setup(0) called.
214 
215 static void delete_imped_info() {
218  delete[] imped_current_type_;
219  delete[] imped_current_ml_;
220  }
221 }
222 
223 // pv2node extended to any range variable in the section
224 // This helper searches over all the mechanisms in the node.
225 // If *pv exists, store mechtype and parray_index.
226 static bool non_vsrc_setinfo(sgid_t ssid, Node* nd, double* pv) {
227  for (Prop* p = nd->prop; p; p = p->next) {
228  if (pv >= p->param && pv < (p->param + p->param_size)) {
229  non_vsrc_update_info_[ssid] = std::pair<int, int>(p->type, pv - p->param);
230  // printf("non_vsrc_setinfo %p %d %ld %s\n", pv, p->type, pv-p->param,
231  // memb_func[p->type].sym->name);
232  return true;
233  }
234  }
235  return false;
236 }
237 
238 static double* non_vsrc_update(Node* nd, int type, int ix) {
239  for (Prop* p = nd->prop; p; p = p->next) {
240  if (type == p->type) {
241  return p->param + ix;
242  }
243  }
244  hoc_execerr_ext("partrans update: could not find parameter index %d of %s",
245  ix,
246  memb_func[type].sym->name);
247  return NULL; // avoid coverage false negative as hoc_execerror does not return.
248 }
249 
250 // Find the Node associated with the voltage.
251 // Easy if v in the currently accessed section.
252 // Extended to any pointer to range variable in the section.
253 // If not a voltage save pv associated with mechtype, p_array_index
254 // in non_vsrc_update_info_
255 static Node* pv2node(sgid_t ssid, double* pv) {
256  Section* sec = chk_access();
257  Node* nd = sec->parentnode;
258  if (nd) {
259  if (&NODEV(nd) == pv || non_vsrc_setinfo(ssid, nd, pv)) {
260  return nd;
261  }
262  }
263  for (int i = 0; i < sec->nnode; ++i) {
264  nd = sec->pnode[i];
265  if (&NODEV(nd) == pv || non_vsrc_setinfo(ssid, nd, pv)) {
266  return nd;
267  }
268  }
269 
270  hoc_execerr_ext("Pointer to src is not in the currently accessed section %s", secname(sec));
271  return NULL; // avoid coverage false negative.
272 }
273 
275  nrnthread_v_transfer_ = thread_transfer; // otherwise can't check is_setup_
276  is_setup_ = false;
277  double* psv = hoc_pgetarg(1); // but might not be a voltage
278  double x = *getarg(2);
279  if (x < 0) {
280  hoc_execerr_ext("source_var sgid must be >= 0: arg 2 is %g\n", x);
281  }
282  sgid_t sgid = (sgid_t) x;
283  if (sgid2srcindex_.find(sgid) != sgid2srcindex_.end()) {
284  hoc_execerr_ext("source var sgid %lld already in use.", (long long) sgid);
285  }
286  sgid2srcindex_[sgid] = visources_.size();
287  visources_.push_back(pv2node(sgid, psv));
288  sgids_.push_back(sgid);
289  // printf("nrnmpi_source_var %p source_val=%g sgid=%ld\n", psv, *psv, (long)sgid);
290 }
291 
292 static int compute_parray_index(Point_process* pp, double* ptv) {
293  if (!pp) {
294  return -1;
295  }
296  size_t i = ptv - pp->prop->param;
297  assert(i >= 0 && i < size_t(pp->prop->param_size));
298  return int(i);
299 }
300 static double* tar_ptr(Point_process* pp, int index) {
301  return pp->prop->param + index;
302 }
303 
304 static void target_ptr_update() {
305  // printf("target_ptr_update\n");
306  if (targets_.size()) {
307  int n = targets_.size();
308  for (int i = 0; i < n; ++i) {
310  if (!pp) {
312  "Do not know the POINT_PROCESS target for source id %zd (Hint: insert target "
313  "instance of the target ref as the first argument.",
314  size_t(sgid2targets_[i]));
315  }
317  targets_[i] = pd;
318  }
319  }
320  mk_ttd();
322 }
323 
325  Point_process* pp = NULL;
326  Object* ob = NULL;
327  int iarg = 1;
328  nrnthread_v_transfer_ = thread_transfer; // otherwise can't check is_setup_
329  is_setup_ = false;
330  if (hoc_is_object_arg(iarg)) {
331  ob = *hoc_objgetarg(iarg++);
332  pp = ob2pntproc(ob);
333  }
334  double* ptv = hoc_pgetarg(iarg++);
335  double x = *getarg(iarg++);
336  if (x < 0) {
337  hoc_execerr_ext("target_var sgid must be >= 0: arg %d is %g\n", iarg - 1, x);
338  }
339  if (pp && (ptv < pp->prop->param || ptv >= (pp->prop->param + pp->prop->param_size))) {
340  hoc_execerr_ext("Target ref not in %s", hoc_object_name(ob));
341  }
342  sgid_t sgid = (sgid_t) x;
343  targets_.push_back(ptv);
344  target_pntlist_.push_back(pp);
345  target_parray_index_.push_back(compute_parray_index(pp, ptv));
346  sgid2targets_.push_back(sgid);
347  // printf("nrnmpi_target_var %p target_val=%g sgid=%ld\n", ptv, *ptv, (long)sgid);
348 }
349 
351  // These pointer changes require that the targets be range variables
352  // of a point process and the sources be range variables
353 
354  // update the poutsrc that have no extracellular
355  for (int i = 0; i < outsrc_buf_size_; ++i) {
356  int isrc = poutsrc_indices_[i];
357  Node* nd = visources_[isrc];
358  auto it = non_vsrc_update_info_.find(sgids_[isrc]);
359  if (it != non_vsrc_update_info_.end()) {
360  poutsrc_[i] = non_vsrc_update(nd, it->second.first, it->second.second);
361  } else if (!nd->extnode) {
362  poutsrc_[i] = &(NODEV(nd));
363  } else {
364  // pointers into SourceViBuf updated when
365  // latter is (re-)created
366  }
367  }
369  // the target vgap pointers also need updating but they will not
370  // change til after this returns ... (verify this)
372 }
373 
374 // static FILE* xxxfile;
375 
376 static void rm_ttd() {
377  if (!transfer_thread_data_) {
378  return;
379  }
380  for (int i = 0; i < n_transfer_thread_data_; ++i) {
382  if (ttd.cnt) {
383  delete[] ttd.tv;
384  delete[] ttd.sv;
385  }
386  }
387  delete[] transfer_thread_data_;
391 }
392 
393 static void rm_svibuf() {
394  if (!source_vi_buf_) {
395  return;
396  }
397  for (int i = 0; i < n_source_vi_buf_; ++i) {
398  SourceViBuf& svib = source_vi_buf_[i];
399  if (svib.cnt) {
400  delete[] svib.nd;
401  delete[] svib.val;
402  }
403  }
404  delete[] source_vi_buf_;
405  source_vi_buf_ = 0;
406  n_source_vi_buf_ = 0;
408 }
409 
411  rm_svibuf();
412  if (visources_.empty()) {
413  return NULL;
414  }
415  // any use of extracellular?
416  int has_ecell = 0;
417  for (int tid = 0; tid < nrn_nthread; ++tid) {
418  if (nrn_threads[tid]._ecell_memb_list) {
419  has_ecell = 1;
420  break;
421  }
422  }
423  if (!has_ecell) {
424  return NULL;
425  }
426 
429  NonVSrcUpdateInfo::iterator it;
430 
431  for (int tid = 0; tid < nrn_nthread; ++tid) {
432  source_vi_buf_[tid].cnt = 0;
433  }
434  // count
435  for (size_t i = 0; i < visources_.size(); ++i) {
436  Node* nd = visources_[i];
437  it = non_vsrc_update_info_.find(sgids_[i]);
438  if (nd->extnode && it == non_vsrc_update_info_.end()) {
439  assert(nd->_nt >= nrn_threads && nd->_nt < (nrn_threads + nrn_nthread));
440  ++source_vi_buf_[nd->_nt->id].cnt;
441  }
442  }
443  // allocate
444  for (int tid = 0; tid < nrn_nthread; ++tid) {
445  SourceViBuf& svib = source_vi_buf_[tid];
446  if (svib.cnt) {
447  svib.nd = new Node*[svib.cnt];
448  svib.val = new double[svib.cnt];
449  }
450  svib.cnt = 0; // recount on fill
451  }
452  // fill
453  for (size_t i = 0; i < visources_.size(); ++i) {
454  Node* nd = visources_[i];
455  it = non_vsrc_update_info_.find(sgids_[i]);
456  if (nd->extnode && it == non_vsrc_update_info_.end()) {
457  int tid = nd->_nt->id;
458  SourceViBuf& svib = source_vi_buf_[tid];
459  svib.nd[svib.cnt] = nd;
460  ++svib.cnt;
461  }
462  }
463  // now the only problem is how to get TransferThreadData and poutsrc_
464  // to point to the proper SourceViBuf given that sgid2srcindex
465  // only gives us the Node* and we dont want to search linearly
466  // (during setup) everytime we we want to associate.
467  // We can do the poutsrc_ now by creating a temporary Node* to
468  // double* map .. The TransferThreadData can be done later
469  // in mk_ttd using the same map and then deleted.
470  MapNode2PDbl* ndvi2pd = new MapNode2PDbl(1000);
471  for (int tid = 0; tid < nrn_nthread; ++tid) {
472  SourceViBuf& svib = source_vi_buf_[tid];
473  for (int i = 0; i < svib.cnt; ++i) {
474  Node* nd = svib.nd[i];
475  (*ndvi2pd)[nd] = svib.val + i;
476  }
477  }
478  for (int i = 0; i < outsrc_buf_size_; ++i) {
479  int isrc = poutsrc_indices_[i];
480  Node* nd = visources_[isrc];
481  it = non_vsrc_update_info_.find(sgids_[isrc]);
482  if (nd->extnode && it == non_vsrc_update_info_.end()) {
483  auto search = ndvi2pd->find(nd);
484  nrn_assert(ndvi2pd->find(nd) != ndvi2pd->end());
485  poutsrc_[i] = search->second;
486  }
487  }
489  return ndvi2pd;
490 }
491 
492 static void mk_ttd() {
493  int i, j, tid, n;
494  MapNode2PDbl* ndvi2pd = mk_svibuf();
495  rm_ttd();
496  if (targets_.empty()) {
497  if (ndvi2pd) {
498  delete ndvi2pd;
499  }
500  // some MPI transfer code paths require that all ranks
501  // have a nrn_thread_v_transfer.
502  // As mentioned in http://static.msi.umn.edu/tutorial/scicomp/general/MPI/content3_new.html
503  // "Communications may, or may not, be synchronized,
504  // depending on how the vendor chose to implement them."
505  // In particular the BG/Q (and one other machine) is sychronizing.
506  // (but see: http://www-01.ibm.com/support/docview.wss?uid=isg1IZ58190 )
507  if (nrnmpi_numprocs > 1 && max_targets_) {
509  }
510  return;
511  }
512  n = targets_.size();
513  if (nrn_nthread > 1)
514  for (i = 0; i < n; ++i) {
516  int sgid = sgid2targets_[i];
517  if (!pp) {
519  "Do not know the POINT_PROCESS target for source id %lld\n"
520  "For multiple threads, the target pointer must reference a range variable\n"
521  "of a POINT_PROCESS. Note that even for a single thread, it is\n"
522  "fastest to supply a reference to the POINT_PROCESS as the first arg.",
523  (long long) sgid);
524  }
525  }
527  for (tid = 0; tid < nrn_nthread; ++tid) {
528  transfer_thread_data_[tid].cnt = 0;
529  }
531  // how many targets in each thread
532  if (nrn_nthread == 1) {
534  } else {
535  for (i = 0; i < n; ++i) {
537  tid = ((NrnThread*) target_pntlist_[i]->_vnt)->id;
538  ++transfer_thread_data_[tid].cnt;
539  }
540  }
541  // allocate
542  for (tid = 0; tid < nrn_nthread; ++tid) {
544  if (ttd.cnt) {
545  ttd.tv = new double*[ttd.cnt];
546  ttd.sv = new double*[ttd.cnt];
547  }
548  ttd.cnt = 0;
549  }
550  // count again and fill pointers
551  for (i = 0; i < n; ++i) {
552  if (nrn_nthread == 1) {
553  tid = 0;
554  } else {
555  tid = ((NrnThread*) target_pntlist_[i]->_vnt)->id;
556  }
558  j = ttd.cnt++;
559  ttd.tv[j] = targets_[i];
560  // perhaps inter- or intra-thread, perhaps interprocessor
561  // if inter- or intra-thread, perhaps SourceViBuf
562  sgid_t sid = sgid2targets_[i];
563  // cannot figure out how to get iterator and test within if
564  bool err = true;
565  auto search = sgid2srcindex_.find(sid);
566  if (search != sgid2srcindex_.end()) {
567  err = false;
568  Node* nd = visources_[search->second];
569  auto it = non_vsrc_update_info_.find(sid);
570  if (it != non_vsrc_update_info_.end()) {
571  ttd.sv[j] = non_vsrc_update(nd, it->second.first, it->second.second);
572  } else if (nd->extnode) {
573  auto search = ndvi2pd->find(nd);
574  nrn_assert(search != ndvi2pd->end());
575  ttd.sv[j] = search->second;
576  } else {
577  ttd.sv[j] = &(NODEV(nd));
578  }
579  } else {
580  auto search = sid2insrc_.find(sid);
581  if (search != sid2insrc_.end()) {
582  err = false;
583  ttd.sv[j] = insrc_buf_ + search->second;
584  }
585  }
586  if (err == true) {
587  hoc_execerr_ext("No source_var for target_var sid = %lld\n", (long long) sid);
588  }
589  }
590  if (ndvi2pd) {
591  delete ndvi2pd;
592  }
594 }
595 
597  // vi+vext needed by either mpi or thread transfer copied into
598  // the source value buffer for this thread. Note that relevant
599  // poutsrc_ and ttd[_nt->id].sv items
600  // point into this source value buffer
601  if (!source_vi_buf_) {
602  return;
603  }
604  SourceViBuf& svb = source_vi_buf_[_nt->id];
605  for (int i = 0; i < svb.cnt; ++i) {
606  Node* nd = svb.nd[i];
607  assert(nd->extnode);
608  svb.val[i] = NODEV(nd) + nd->extnode->v[0];
609  }
610 }
611 
612 void mpi_transfer() {
613  int i, n = outsrc_buf_size_;
616  }
617  for (i = 0; i < n; ++i) {
618  outsrc_buf_[i] = *poutsrc_[i];
619  }
620 #if PARANEURON
621  if (nrnmpi_numprocs > 1) {
622  double wt = nrnmpi_wtime();
623  if (nrn_sparse_partrans > 0) {
624  nrnmpi_dbl_alltoallv_sparse(
626  } else {
629  }
630  nrnmpi_transfer_wait_ += nrnmpi_wtime() - wt;
631  errno = 0;
632  }
633 #endif
634  // insrc_buf_ will get transferred to targets by thread_transfer
635 }
636 
638  if (!is_setup_) {
639  hoc_execerror("ParallelContext.setup_transfer()", "needs to be called.");
640  }
641  if (targets_.empty()) {
642  return;
643  }
644 
645  // fprintf(xxxfile, "%g\n", t);
646  // an edited old comment prior to allowing simultaneous threads and mpi.
647  // for threads we do direct transfers under the assumption
648  // that v is being transferred and they were set in a
649  // previous multithread job. For the fixed step method this
650  // call is from nonvint which in the same thread job as update
651  // and that is the case even with multisplit. So we really
652  // need to break the job between update and nonvint. Too bad.
653  // For global cvode, things are ok except if the source voltage
654  // is at a zero area node since nocap_v_part2 is a part
655  // of this job and in fact the v does not get updated til
656  // the next job in nocap_v_part3. Again, too bad. But it is
657  // quite ambiguous, stability wise,
658  // to have a gap junction in a zero area node, anyway, since
659  // the system is then truly a DAE.
660  // For now we presume we have dealt with these matters and
661  // do the transfer.
665  }
667  for (int i = 0; i < ttd.cnt; ++i) {
668  *(ttd.tv[i]) = *(ttd.sv[i]);
669  }
670 }
671 
672 // The simplest conceivable transfer is to use MPI_Allgatherv and send
673 // all sources to all machines. More complicated and possibly more efficient
674 // in terms of total received buffer size
675 // would be to use MPI_Alltoallv in which distinct data is sent and received.
676 // Most transfer are one to one, at most one to a few, so now we use alltoallv.
677 // The old comment read: "
678 // We begin with MPI_Allgatherv. We try
679 // to save a trivial copy by making
680 // outgoing_source_buf a pointer into the incoming_source_buf.
681 // " But this was a mistake as many mpi implementations do not allow overlap
682 // of send and receive buffers.
683 
684 // 22-08-2014 For setup of the All2allv pattern, use the rendezvous rank
685 // idiom.
686 #define HAVEWANT_t sgid_t
687 #define HAVEWANT_alltoallv sgid_alltoallv
688 #define HAVEWANT2Int MapSgid2Int
689 #if PARANEURON
690 #include "have2want.cpp"
691 #endif
692 
694 #if !PARANEURON
695  if (nrnmpi_numprocs > 1) {
697  "To use ParallelContext.setup_transfer when nhost > 1, NEURON must be configured with "
698  "--with-paranrn",
699  0);
700  }
701 #endif
702  int nhost = nrnmpi_numprocs;
703  // char ctmp[100];
704  // sprintf(ctmp, "vartrans%d", nrnmpi_myid);
705  // xxxfile = fopen(ctmp, "w");
706  is_setup_ = true;
707  // printf("nrnmpi_setup_transfer\n");
709  if (insrc_buf_) {
710  delete[] insrc_buf_;
711  insrc_buf_ = 0;
712  }
713  if (outsrc_buf_) {
714  delete[] outsrc_buf_;
715  outsrc_buf_ = 0;
716  }
717  sid2insrc_.clear();
718  if (poutsrc_) {
719  delete[] poutsrc_;
720  poutsrc_ = 0;
721  }
722  if (poutsrc_indices_) {
723  delete[] poutsrc_indices_;
724  poutsrc_indices_ = 0;
725  }
726 #if PARANEURON
727  // if there are no targets anywhere, we do not need to do anything
729  if (max_targets_ == 0) {
730  return;
731  }
732  if (nrnmpi_numprocs > 1) {
733  if (insrccnt_) {
734  delete[] insrccnt_;
735  insrccnt_ = NULL;
736  }
737  if (insrcdspl_) {
738  delete[] insrcdspl_;
739  insrcdspl_ = NULL;
740  }
741  if (outsrccnt_) {
742  delete[] outsrccnt_;
743  outsrccnt_ = NULL;
744  }
745  if (outsrcdspl_) {
746  delete[] outsrcdspl_;
747  outsrcdspl_ = NULL;
748  }
749 
750  // This is an old comment prior to using the want_to_have rendezvous
751  // rank function in want2have.cpp. The old method did not scale
752  // to more sgids than could fit on a single rank, because
753  // each rank sent its "need" list to every rank.
754  // <old comment>
755  // This machine needs to send which sources to which other machines.
756  // It does not need to send to itself.
757  // Which targets have sources on other machines.(none if nrnmpi_numprocs=1)
758  // 1) list sources needed that are on other machines.
759  // 2) send that info to all machines.
760  // 3) source machine can figure out which machines want its sids
761  // and therefore construct outsrc_buf, etc.
762  // 4) Notify target machines which sids the source machine will send
763  // 5) The target machines can figure out where the sids are coming from
764  // and therefore construct insrc_buf, etc.
765  // <new comment>
766  // 1) List sources needed by this rank and sources that this rank owns.
767  // 2) Call the have_to_want function. Returns two sets of three
768  // vectors. The first set of three vectors is an sgid buffer,
769  // along with counts and displacements. The sgids in the ith region
770  // of the buffer are the sgids from this rank that are
771  // wanted by the ith rank. For the second set, the sgids in the ith
772  // region are the sgids on the ith rank that are wanted by this rank.
773  // 3) First return triple creates the proper outsrc_buf_.
774  // 4) The second triple is creates the insrc_buf_.
775 
776  // 1)
777  // It will often be the case that multiple targets will need the
778  // same source. We count the needed sids only once regardless of
779  // how often they are used.
780  // At the end of this section, needsrc is an array of needsrc_cnt
781  // sids needed by this machine. The 'seen' table values are unused
782  // but the keys are all the (unique) sgid needed by this process.
783  // At the end seen is in fact what we want for sid2insrc_.
784  int needsrc_cnt = 0;
785  int szalloc = targets_.size();
786  szalloc = szalloc ? szalloc : 1;
787 
788  // At the moment sid2insrc_ is serving as 'seen'
789  sid2insrc_.clear();
790  sid2insrc_.reserve(szalloc); // for single counting
791  sgid_t* needsrc = new sgid_t[szalloc]; // more than we need
792  for (size_t i = 0; i < sgid2targets_.size(); ++i) {
793  sgid_t sid = sgid2targets_[i];
794  auto search = sid2insrc_.find(sid);
795  if (search == sid2insrc_.end()) {
796  sid2insrc_[sid] = 0; // at the moment, value does not matter
797  needsrc[needsrc_cnt++] = sid;
798  }
799  }
800 
801  // 1 continued) Create an array of sources this rank owns.
802  // This already exists as a vector in the SgidList sgids_ but
803  // that is private so go ahead and copy.
804  sgid_t* ownsrc = new sgid_t[sgids_.size() + 1]; // not 0 length if count is 0
805  for (size_t i = 0; i < sgids_.size(); ++i) {
806  ownsrc[i] = sgids_[i];
807  }
808 
809  // 2) Call the have_to_want function.
810  sgid_t* send_to_want;
811  int *send_to_want_cnt, *send_to_want_displ;
812  sgid_t* recv_from_have;
813  int *recv_from_have_cnt, *recv_from_have_displ;
814 
815  have_to_want(ownsrc,
816  sgids_.size(),
817  needsrc,
818  needsrc_cnt,
819  send_to_want,
820  send_to_want_cnt,
821  send_to_want_displ,
822  recv_from_have,
823  recv_from_have_cnt,
824  recv_from_have_displ,
826 
827  // sanity check. all the sgids we are asked to send, we actually have
828  int n = send_to_want_displ[nhost];
829 #if 0 // done in passing in step 3 below
830  for (int i=0; i < n; ++i) {
831  sgid_t sgid = send_to_want[i];
832  nrn_assert(sgid2srcindex_.find(sgid) != sgid2srcindex_.end());
833  }
834 #endif
835  // sanity check. all the sgids we receive, we actually need.
836  // also set the sid2insrc_ value to the proper recv_from_have index.
837  n = recv_from_have_displ[nhost];
838  for (int i = 0; i < n; ++i) {
839  sgid_t sgid = recv_from_have[i];
840  nrn_assert(sid2insrc_.find(sgid) != sid2insrc_.end());
841  sid2insrc_[sgid] = i;
842  }
843 
844  // clean up a little
845  delete[] ownsrc;
846  delete[] needsrc;
847  delete[] recv_from_have;
848 
849  // 3) First return triple creates the proper outsrc_buf_.
850  // Now that we know what machines are interested in our sids...
851  // construct outsrc_buf, outsrc_buf_size, outsrccnt_, outsrcdspl_
852  // and poutsrc_;
853  outsrccnt_ = send_to_want_cnt;
854  outsrcdspl_ = send_to_want_displ;
856  szalloc = outsrc_buf_size_ ? outsrc_buf_size_ : 1;
857  outsrc_buf_ = new double[szalloc];
858  poutsrc_ = new double*[szalloc];
859  poutsrc_indices_ = new int[szalloc];
860  for (int i = 0; i < outsrc_buf_size_; ++i) {
861  sgid_t sid = send_to_want[i];
862  auto search = sgid2srcindex_.find(sid);
863  nrn_assert(search != sgid2srcindex_.end());
864  Node* nd = visources_[search->second];
865  NonVSrcUpdateInfo::iterator it;
866  it = non_vsrc_update_info_.find(sid);
867  if (it != non_vsrc_update_info_.end()) {
868  poutsrc_[i] = non_vsrc_update(nd, it->second.first, it->second.second);
869  } else if (!nd->extnode) {
870  poutsrc_[i] = &(NODEV(nd));
871  } else {
872  // the v+vext case can only be done after mk_svib()
873  }
874  poutsrc_indices_[i] = search->second;
875  outsrc_buf_[i] = double(sid); // see step 5
876  }
877  delete[] send_to_want;
878 
879  // 4) The second triple is creates the insrc_buf_.
880  // From the recv_from_have and sid2insrc_ table, construct the insrc...
881  insrccnt_ = recv_from_have_cnt;
882  insrcdspl_ = recv_from_have_displ;
884  szalloc = insrc_buf_size_ ? insrc_buf_size_ : 1;
885  insrc_buf_ = new double[szalloc];
886  // from sid2insrc_, mk_ttd can construct the right pointer to the source.
887 
889  }
890 #endif // PARANEURON
892  if (!v_structure_change) {
893  mk_ttd();
894  }
895 }
896 
901  sgid2srcindex_.clear();
902  sgids_.resize(0);
903  visources_.resize(0);
904  sgid2targets_.resize(0);
905  target_pntlist_.resize(0);
906  target_parray_index_.resize(0);
907  targets_.resize(0);
908  max_targets_ = 0;
909  rm_svibuf();
910  rm_ttd();
911  if (insrc_buf_) {
912  delete[] insrc_buf_;
913  insrc_buf_ = NULL;
914  }
915  if (outsrc_buf_) {
916  delete[] outsrc_buf_;
917  outsrc_buf_ = NULL;
918  }
919  sid2insrc_.clear();
920  if (poutsrc_) {
921  delete[] poutsrc_;
922  poutsrc_ = NULL;
923  }
924  if (poutsrc_indices_) {
925  delete[] poutsrc_indices_;
927  }
928  non_vsrc_update_info_.clear();
930 }
931 
932 // assume one thread and no extracellular
933 
934 static double *vgap1, *vgap2;
935 static int imped_change_cnt;
936 
937 void pargap_jacobi_setup(int mode) {
938  if (!nrnthread_v_transfer_) {
939  return;
940  }
941 
942  // list of gap junction types and memb_list for each
943  if (mode == 0) {
947  }
948  if (imped_current_type_count_ == 0 && targets_.size() > 0) {
949  for (size_t i = 0; i < targets_.size(); ++i) {
951  if (!pp) {
953  "For impedance, pc.target_var requires that its first arg be a reference "
954  "to the POINT_PROCESS",
955  0);
956  }
957  int type = pp->prop->type;
958  if (imped_current_type_count_ == 0) {
960  imped_current_type_ = new int[5];
961  imped_current_ml_ = new Memb_list*[5];
963  }
964  int add = 1;
965  for (int k = 0; k < imped_current_type_count_; ++k) {
966  if (type == imped_current_type_[k]) {
967  add = 0;
968  break;
969  }
970  }
971  if (add) {
975  }
976  }
977  NrnThread* nt = nrn_threads;
978  for (int k = 0; k < imped_current_type_count_; ++k) {
979  for (NrnThreadMembList* tml = nt->tml; tml; tml = tml->next) {
980  if (imped_current_type_[k] == tml->index) {
981  imped_current_ml_[k] = tml->ml;
982  }
983  }
984  }
985  // are all the instances in use
986  size_t ninst = 0;
987  for (int k = 0; k < imped_current_type_count_; ++k) {
988  ninst += imped_current_ml_[k]->nodecount;
989  }
990  if (ninst != targets_.size()) {
992  "number of gap junctions, %zd, not equal to number of pc.transfer_var, %zd",
993  ninst,
994  targets_.size());
995  }
996  }
997  }
1000  }
1002  if (mode == 0) { // setup
1003  if (visources_.size()) {
1004  vgap1 = new double[visources_.size()];
1005  }
1006  if (ttd && ttd->cnt) {
1007  vgap2 = new double[ttd->cnt];
1008  }
1009  for (size_t i = 0; i < visources_.size(); ++i) {
1010  vgap1[i] = NODEV(visources_[i]);
1011  }
1012  if (ttd)
1013  for (int i = 0; i < ttd->cnt; ++i) {
1014  vgap2[i] = *(ttd->tv[i]);
1015  }
1016  } else { // tear down
1017  for (size_t i = 0; i < visources_.size(); ++i) {
1018  NODEV(visources_[i]) = vgap1[i];
1019  }
1020  if (ttd)
1021  for (int i = 0; i < ttd->cnt; ++i) {
1022  *(ttd->tv[i]) = vgap2[i];
1023  }
1024  if (vgap1) {
1025  delete[] vgap1;
1026  vgap1 = NULL;
1027  }
1028  if (vgap2) {
1029  delete[] vgap2;
1030  vgap2 = NULL;
1031  }
1032  }
1033 }
1034 
1035 void pargap_jacobi_rhs(double* b, double* x) {
1036  // helper for complex impedance with parallel gap junctions
1037  // b = b - R*x R are the off diagonal gap elements of the jacobian.
1038  // we presume 1 thread. First nrn_thread[0].end equations are in node order.
1039  if (!nrnthread_v_transfer_) {
1040  return;
1041  }
1042 
1043  NrnThread* _nt = nrn_threads;
1044 
1045  // transfer gap node voltages to gap vpre
1046  for (size_t i = 0; i < visources_.size(); ++i) {
1047  Node* nd = visources_[i];
1048  NODEV(nd) = x[nd->v_node_index];
1049  }
1050  mpi_transfer();
1051  thread_transfer(_nt);
1052 
1053  // set gap node voltages to 0 so we can use nrn_cur to set rhs
1054  for (size_t i = 0; i < visources_.size(); ++i) {
1055  Node* nd = visources_[i];
1056  NODEV(nd) = 0.0;
1057  }
1058  // Initialize rhs to 0.
1059  for (int i = 0; i < _nt->end; ++i) {
1060  VEC_RHS(i) = 0.0;
1061  }
1062 
1063  for (int k = 0; k < imped_current_type_count_; ++k) {
1064  int type = imped_current_type_[k];
1065  Memb_list* ml = imped_current_ml_[k];
1066  (*memb_func[type].current)(_nt, ml, type);
1067  }
1068 
1069  // possibly many gap junctions in same node (and possible even different
1070  // types) but rhs is the accumulation of all those instances at each node
1071  // so ... The only thing that can go wrong is if there are intances of
1072  // gap junctions that are not being used (not in the target list).
1073  for (int i = 0; i < _nt->end; ++i) {
1074  b[i] += VEC_RHS(i);
1075  }
1076 }
1077 
1078 extern size_t nrnbbcore_gap_write(const char* path, int* group_ids);
1079 
1080 /*
1081  file format for <path>/<group_id>_gap.dat
1082  All gap info for thread. Extracellular not allowed
1083  ntar // number of targets in this thread (vpre)
1084  nsrc // number of sources in this thread (v)
1085 
1086  Note: type, index is sufficient for CoreNEURON stdindex2ptr to determine
1087  double* in its NrnThread.data array.
1088 
1089  src_sid // nsrc of these
1090  src_type // nsrc mechanism type containing source variable, -1 is voltage.
1091  src_index // range variable index relative to beginning of first instance.
1092 
1093  tar_sid // ntar of these
1094  tar_type // ntar mechanism type containing target variable.
1095  tar_index // range variable index relative to beginning of first instance.
1096 
1097  Assert no extracellular.
1098 
1099 */
1100 
1101 /*
1102  The original file creation for each thread was accomplished by
1103  a serial function that:
1104  Verified the assertion constraints.
1105  Created an nthread array of BBCoreGapInfo.
1106  Wrote the <gid>_gap.dat files forall the threads.
1107  Cleaned up the BBCoreGapInfo (and gap_ml).
1108 
1109  So a simple factoring of the verify and create portions suffices
1110  for both files and direct memory transfer. Note that direct call
1111  returns pointer to SetupTransferInfo array.
1112  To cleanup, CoreNEURON should delete [] the return pointer.
1113 */
1114 
1116 
1117 SetupTransferInfo* nrn_get_partrans_setup_info(int ngroup, int cn_nthread, size_t cn_sidt_sz) {
1118  assert(cn_sidt_sz == sizeof(sgid_t));
1119  assert(ngroup == nrn_nthread);
1120  return nrncore_transfer_info(cn_nthread);
1121 }
1122 
1123 size_t nrnbbcore_gap_write(const char* path, int* group_ids) {
1124  auto gi = nrncore_transfer_info(nrn_nthread); // gi stood for gapinfo
1125  if (gi == nullptr) {
1126  return 0;
1127  }
1128 
1129  // print the files
1130  for (int tid = 0; tid < nrn_nthread; ++tid) {
1131  auto& g = gi[tid];
1132 
1133  if (g.src_sid.empty() && g.tar_sid.empty()) { // no file
1134  continue;
1135  }
1136 
1137  char fname[1000];
1138  sprintf(fname, "%s/%d_gap.dat", path, group_ids[tid]);
1139  FILE* f = fopen(fname, "wb");
1140  assert(f);
1141  fprintf(f, "%s\n", bbcore_write_version);
1142  fprintf(f, "%d sizeof_sid_t\n", int(sizeof(sgid_t)));
1143 
1144  int ntar = int(g.tar_sid.size());
1145  int nsrc = int(g.src_sid.size());
1146  fprintf(f, "%d ntar\n", ntar);
1147  fprintf(f, "%d nsrc\n", nsrc);
1148 
1149  int chkpnt = 0;
1150 #define CHKPNT fprintf(f, "chkpnt %d\n", chkpnt++);
1151 
1152  if (!g.src_sid.empty()) {
1153  CHKPNT fwrite(g.src_sid.data(), nsrc, sizeof(sgid_t), f);
1154  CHKPNT fwrite(g.src_type.data(), nsrc, sizeof(int), f);
1155  CHKPNT fwrite(g.src_index.data(), nsrc, sizeof(int), f);
1156  }
1157 
1158  if (!g.tar_sid.empty()) {
1159  CHKPNT fwrite(g.tar_sid.data(), ntar, sizeof(sgid_t), f);
1160  CHKPNT fwrite(g.tar_type.data(), ntar, sizeof(int), f);
1161  CHKPNT fwrite(g.tar_index.data(), ntar, sizeof(int), f);
1162  }
1163 
1164  fclose(f);
1165  }
1166 
1167  // cleanup
1168  delete[] gi;
1169  return 0;
1170 }
1171 
1172 static SetupTransferInfo* nrncore_transfer_info(int cn_nthread) {
1173  assert(target_pntlist_.size() == targets_.size());
1174 
1175  // space for the info
1176  auto gi = new SetupTransferInfo[cn_nthread];
1177 
1178  // info for targets, segregate into threads
1179  if (targets_.size()) {
1180  for (size_t i = 0; i < targets_.size(); ++i) {
1181  sgid_t sid = sgid2targets_[i];
1183  NrnThread* nt = (NrnThread*) pp->_vnt;
1184  int tid = nt ? nt->id : 0;
1185  int type = pp->prop->type;
1186  Memb_list& ml = *(nrn_threads[tid]._ml_list[type]);
1187  int ix = targets_[i] - ml.data[0];
1188 
1189  auto& g = gi[tid];
1190  g.tar_sid.push_back(sid);
1191  g.tar_type.push_back(type);
1192  g.tar_index.push_back(ix);
1193  }
1194  }
1195 
1196  // info for sources, segregate into threads.
1197  if (visources_.size()) {
1198  for (size_t i = 0; i < sgids_.size(); ++i) {
1199  sgid_t sid = sgids_[i];
1200  Node* nd = visources_[i];
1201  int tid = nd->_nt ? nd->_nt->id : 0;
1202  int type = -1; // default voltage
1203  int ix = 0; // fill below
1204  NonVSrcUpdateInfo::iterator it = non_vsrc_update_info_.find(sid);
1205  if (it != non_vsrc_update_info_.end()) { // not a voltage source
1206  type = it->second.first;
1207  ix = it->second.second;
1208  // this entire context needs to be reworked. If the source is a
1209  // point process, then if more than one in this nd, it is an error.
1210  double* d = non_vsrc_update(nd, type, ix);
1211  NrnThread* nt = nd->_nt ? nd->_nt : nrn_threads;
1212  Memb_list& ml = *nt->_ml_list[type];
1213  ix = d - ml.data[0];
1214  } else { // is a voltage source
1215  ix = nd->_v - nrn_threads[tid]._actual_v;
1216  assert(nd->extnode == NULL); // only if v
1217  assert(ix >= 0 && ix < nrn_threads[tid].end);
1218  }
1219 
1220  auto& g = gi[tid];
1221  g.src_sid.push_back(sid);
1222  g.src_type.push_back(type);
1223  g.src_index.push_back(ix);
1224  }
1225  }
1226  return gi;
1227 }
static void nrnmpi_int_alltoallv(int *s, int *scnt, int *sdispl, int *r, int *rcnt, int *rdispl)
static void nrnmpi_dbl_alltoallv(double *s, int *scnt, int *sdispl, double *r, int *rcnt, int *rdispl)
static void nrnmpi_int_allgather(int *s, int *r, int n)
static void nrnmpi_barrier()
static int nrnmpi_int_allmax(int x)
const char * secname(Section *sec)
Definition: cabcode.cpp:1776
Section * chk_access(void)
Definition: cabcode.cpp:444
short index
Definition: cabvars.h:10
Memb_func * memb_func
Definition: init.cpp:123
short type
Definition: cabvars.h:9
Point_process * ob2pntproc(Object *)
Definition: hocmech.cpp:88
sprintf(buf, " if (secondorder) {\n" " int _i;\n" " for (_i = 0; _i < %d; ++_i) {\n" " _p[_slist%d[_i]] += dt*_p[_dlist%d[_i]];\n" " }}\n", numeqn, listnum, listnum)
void hoc_execerror(const char *, const char *)
Definition: hoc.cpp:754
int hoc_is_object_arg(int narg)
Definition: code.cpp:756
void hoc_execerr_ext(const char *fmt,...)
printf style specification of hoc_execerror message.
Definition: fileio.cpp:931
char * hoc_object_name(Object *ob)
Definition: hoc_oop.cpp:72
double * hoc_pgetarg(int narg)
Definition: code.cpp:1623
static void have_to_want(HAVEWANT_t *have, int have_size, HAVEWANT_t *want, int want_size, HAVEWANT_t *&send_to_want, int *&send_to_want_cnt, int *&send_to_want_displ, HAVEWANT_t *&recv_from_have, int *&recv_from_have_cnt, int *&recv_from_have_displ, int(*rendezvous_rank)(HAVEWANT_t))
Definition: have2want.cpp:104
static int default_rendezvous(HAVEWANT_t key)
Definition: have2want.cpp:47
#define assert(ex)
Definition: hocassrt.h:32
#define getarg
Definition: hocdec.h:15
Object ** hoc_objgetarg(int)
Definition: code.cpp:1587
void
#define sec
Definition: md1redef.h:13
#define i
Definition: md1redef.h:12
#define prop
Definition: md1redef.h:29
int nrn_nthread
Definition: multicore.cpp:46
NrnThread * nrn_threads
Definition: multicore.cpp:47
static double nrnmpi_wtime()
Definition: multisplit.cpp:55
#define fprintf
Definition: mwprefix.h:30
#define nrn_assert(ex)
Definition: nrnassrt.h:53
int chkpnt
Definition: nrncore_io.cpp:24
int const size_t const size_t n
Definition: nrngsl.h:11
size_t p
size_t j
int nrnmpi_numprocs
int nrn_sparse_partrans
Definition: init.cpp:101
static philox4x32_key_t k
Definition: nrnran123.cpp:11
static double nhost(void *v)
Definition: ocbbs.cpp:232
static Symbol * pv[4]
Definition: partial.cpp:80
void nrn_partrans_clear()
Definition: partrans.cpp:897
static Memb_list ** imped_current_ml_
Definition: partrans.cpp:213
static SgidList sgids_
Definition: partrans.cpp:194
static MapSgid2Int sgid2srcindex_
Definition: partrans.cpp:195
static bool is_setup_
Definition: partrans.cpp:207
static double * vgap1
Definition: partrans.cpp:934
static int target_ptr_update_cnt_
Definition: partrans.cpp:203
static Node * pv2node(sgid_t ssid, double *pv)
Definition: partrans.cpp:255
void pargap_jacobi_rhs(double *b, double *x)
Definition: partrans.cpp:1035
size_t nrnbbcore_gap_write(const char *path, int *group_ids)
Definition: partrans.cpp:1123
void(* nrnthread_v_transfer_)(NrnThread *)
Definition: fadvance.cpp:153
static int * outsrcdspl_
Definition: partrans.cpp:180
void pargap_jacobi_setup(int mode)
Definition: partrans.cpp:937
static void thread_vi_compute(NrnThread *)
Definition: partrans.cpp:596
static SourceViBuf * source_vi_buf_
Definition: partrans.cpp:163
static int n_transfer_thread_data_
Definition: partrans.cpp:155
static int * insrccnt_
Definition: partrans.cpp:179
int nrn_node_ptr_change_cnt_
Definition: treeset.cpp:84
static int * imped_current_type_
Definition: partrans.cpp:212
static double * non_vsrc_update(Node *nd, int type, int ix)
Definition: partrans.cpp:238
int structure_change_cnt
Definition: partrans.cpp:53
static void rm_svibuf()
Definition: partrans.cpp:393
static int imped_current_type_count_
Definition: partrans.cpp:211
std::vector< double * > DblPList
Definition: partrans.cpp:168
static NodePList visources_
Definition: partrans.cpp:193
static void rm_ttd()
Definition: partrans.cpp:376
void nrn_partrans_update_ptrs()
Definition: partrans.cpp:350
double * nrn_recalc_ptr(double *)
Definition: treeset.cpp:2179
static int * outsrccnt_
Definition: partrans.cpp:180
static void mk_ttd()
Definition: partrans.cpp:492
static SgidList sgid2targets_
Definition: partrans.cpp:187
static void delete_imped_info()
Definition: partrans.cpp:215
static SetupTransferInfo * nrncore_transfer_info(int)
Definition: partrans.cpp:1172
std::vector< Node * > NodePList
Definition: partrans.cpp:169
static int compute_parray_index(Point_process *pp, double *ptv)
Definition: partrans.cpp:292
static IntList target_parray_index_
Definition: partrans.cpp:189
double t
Definition: cvodeobj.cpp:59
std::unordered_map< sgid_t, int > MapSgid2Int
Definition: partrans.cpp:166
static void mpi_transfer()
Definition: partrans.cpp:612
std::vector< double * > DblPVec
Definition: partrans.cpp:192
std::vector< sgid_t > SgidList
Definition: partrans.cpp:173
static TransferThreadData * transfer_thread_data_
Definition: partrans.cpp:154
static int n_source_vi_buf_
Definition: partrans.cpp:164
static NonVSrcUpdateInfo non_vsrc_update_info_
Definition: partrans.cpp:198
#define CHKPNT
static PPList target_pntlist_
Definition: partrans.cpp:188
static int outsrc_buf_size_
Definition: partrans.cpp:180
static double * vgap2
Definition: partrans.cpp:934
std::vector< int > IntList
Definition: partrans.cpp:172
static int * poutsrc_indices_
Definition: partrans.cpp:178
static int vptr_change_cnt_
Definition: partrans.cpp:205
static double ** poutsrc_
Definition: partrans.cpp:177
static int * insrcdspl_
Definition: partrans.cpp:179
static double * tar_ptr(Point_process *pp, int index)
Definition: partrans.cpp:300
void(* nrnthread_vi_compute_)(NrnThread *)
Definition: fadvance.cpp:155
static int imped_change_cnt
Definition: partrans.cpp:935
std::unordered_map< Node *, double * > MapNode2PDbl
Definition: partrans.cpp:167
#define PPList
Definition: partrans.cpp:170
static double * outsrc_buf_
Definition: partrans.cpp:176
void nrnmpi_target_var()
Definition: partrans.cpp:324
void nrnmpi_setup_transfer()
Definition: partrans.cpp:693
static MapNode2PDbl * mk_svibuf()
Definition: partrans.cpp:410
static void target_ptr_update()
Definition: partrans.cpp:304
static void thread_transfer(NrnThread *)
Definition: partrans.cpp:637
std::map< sgid_t, std::pair< int, int > > NonVSrcUpdateInfo
Definition: partrans.cpp:197
std::vector< Point_process * > PPList
Definition: partrans.cpp:171
const char * bbcore_write_version
Definition: nrncore_io.cpp:25
static int max_targets_
Definition: partrans.cpp:201
static MapSgid2Int sid2insrc_
Definition: partrans.cpp:181
static int target_ptr_need_update_cnt_
Definition: partrans.cpp:204
int v_structure_change
Definition: partrans.cpp:52
static int insrc_buf_size_
Definition: partrans.cpp:179
static DblPList targets_
Definition: partrans.cpp:186
void nrnmpi_source_var()
Definition: partrans.cpp:274
static double * insrc_buf_
Definition: partrans.cpp:175
SetupTransferInfo * nrn_get_partrans_setup_info(int ngroup, int cn_nthread, size_t cn_sidt_sz)
Definition: partrans.cpp:1117
void(* nrnmpi_v_transfer_)()
Definition: fadvance.cpp:152
static bool non_vsrc_setinfo(sgid_t ssid, Node *nd, double *pv)
Definition: partrans.cpp:226
void(* nrn_mk_transfer_thread_data_)()
Definition: multicore.cpp:48
int sgid_t
Definition: partrans.h:14
#define g
Definition: passive0.cpp:21
#define add
Definition: redef.h:24
#define VEC_RHS(i)
Definition: section.h:121
#define NODEV(n)
Definition: section.h:115
FILE * fopen()
#define NULL
Definition: sptree.h:16
double * v
Definition: section.h:196
Pvmi current
Definition: membfunc.h:33
int nodecount
Definition: nrnoc_ml.h:18
double ** data
Definition: nrnoc_ml.h:14
Definition: section.h:133
struct NrnThread * _nt
Definition: section.h:158
double * _v
Definition: section.h:140
struct Extnode * extnode
Definition: section.h:161
int v_node_index
Definition: section.h:175
struct Prop * prop
Definition: section.h:152
Represent main neuron object computed by single thread.
Definition: multicore.h:58
NrnThreadMembList * tml
Definition: multicore.h:62
int id
Definition: multicore.h:66
int end
Definition: multicore.h:65
double * _actual_v
Definition: multicore.h:74
Memb_list ** _ml_list
Definition: multicore.h:63
struct NrnThreadMembList * next
Definition: multicore.h:34
Definition: hocdec.h:227
void * _vnt
Definition: section.h:270
Prop * prop
Definition: section.h:265
Definition: section.h:214
double * param
Definition: section.h:219
int param_size
Definition: section.h:218
short type
Definition: section.h:216
Node ** nd
Definition: partrans.cpp:160
double * val
Definition: partrans.cpp:161
static const char * fname(const char *name)
Definition: nrnbbs.cpp:113