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