NEURON
splitcell.cpp
Go to the documentation of this file.
1 #include <../../nrnconf.h>
2 
3 #include <vector>
4 #include <errno.h>
5 #include "nrniv_mf.h"
6 #include <nrnoc2iv.h>
7 #include <nrnmpi.h>
8 
9 /*
10 Started out attempting a general implementation in which the subtrees could be
11 on any host and any number of them. That required a distinct gid for each
12 subtree. However, since the only purpose of cellsplit is for load balance
13 efficiency, we settled on a very restrictive but still adequate policy of
14 subtrees being on adjacent hosts and at most one cell split between i and i+1
15 and at most one cell split between i and i-1. We also disallow a split where
16 both subtrees are on the same host. This policy allows a very simple and direct
17 setting up and transfer of matrix information. Note that gid information about
18 the subtrees is no longer required by this implementation.
19 */
20 
21 #if NRNMPI
22 void nrnmpi_split_clear();
23 extern void (*nrnmpi_splitcell_compute_)();
24 extern void nrnmpi_send_doubles(double*, int cnt, int dest, int tag);
25 extern void nrnmpi_recv_doubles(double*, int cnt, int src, int tag);
26 extern double nrnmpi_splitcell_wait_;
27 
28 static int change_cnt_;
29 static void transfer();
30 static void set_structure();
31 static void splitcell_compute();
32 
33 struct SplitCell {
34  Section* rootsec_;
35  int that_host_;
36 };
37 
38 static std::vector<SplitCell> splitcell_list_;
39 
40 #define ip 0
41 #define im 2
42 static bool splitcell_connected_[2];
43 static double* transfer_p_[4];
44 
45 #endif
46 
47 // that_host must be adjacent to nrnmpi_myid
48 void nrnmpi_splitcell_connect(int that_host) {
49 #if NRNMPI
50  Section* rootsec = chk_access();
51  if (std::abs(nrnmpi_myid - that_host) != 1) {
52  hoc_execerror("cells may be split only on adjacent hosts", 0);
53  }
54  if (that_host < 0 || that_host >= nrnmpi_numprocs) {
55  hoc_execerror("adjacent host out of range", 0);
56  }
57  if (rootsec->parentsec) {
58  hoc_execerror(secname(rootsec), "is not a root section");
59  }
60  nrnmpi_splitcell_compute_ = splitcell_compute;
61  for (size_t i = 0; i < 2; ++i)
62  if (that_host == nrnmpi_myid + i * 2 - 1) {
63  if (splitcell_connected_[i]) {
64  char buf[100];
65  Sprintf(buf, "%d and %d", nrnmpi_myid, that_host);
66  hoc_execerror("splitcell connection already exists between hosts", buf);
67  }
68  splitcell_connected_[i] = true;
69  }
70  splitcell_list_.push_back({rootsec, that_host});
71 #else
72  if (nrnmpi_myid == 0) {
73  hoc_execerror("ParallelContext.splitcell not available.",
74  "NEURON not configured with --with-paranrn");
75  }
76 #endif
77 }
78 
79 #if NRNMPI
80 
81 void nrnmpi_split_clear() {
82  if (nrnmpi_splitcell_compute_ == splitcell_compute) {
83  if (nrnmpi_myid == 0) {
84  hoc_execerror("nrnmpi_split_clear ", "not implemented");
85  }
86  }
87 }
88 
89 void splitcell_compute() {
90  if (structure_change_cnt != change_cnt_) {
91  set_structure();
92  change_cnt_ = structure_change_cnt;
93  }
94  transfer();
95 }
96 
97 void transfer() {
98  double trans[2], trans_sav[2];
99  double wt = nrnmpi_wtime();
100  if (transfer_p_[ip]) {
101  trans[0] = *transfer_p_[ip + 0];
102  trans[1] = *transfer_p_[ip + 1];
103  nrnmpi_send_doubles(trans, 2, nrnmpi_myid + 1, 1);
104  }
105  if (transfer_p_[im]) {
106  nrnmpi_recv_doubles(trans_sav, 2, nrnmpi_myid - 1, 1);
107  // defer copying to the d and rhs variables since the
108  // old info needs to be transferred next
109  trans[0] = *transfer_p_[im + 0];
110  trans[1] = *transfer_p_[im + 1];
111  *transfer_p_[im + 0] += trans_sav[0];
112  *transfer_p_[im + 1] += trans_sav[1];
113  nrnmpi_send_doubles(trans, 2, nrnmpi_myid - 1, 1);
114  }
115  if (transfer_p_[ip]) {
116  nrnmpi_recv_doubles(trans, 2, nrnmpi_myid + 1, 1);
117  *transfer_p_[ip + 0] += trans[0];
118  *transfer_p_[ip + 1] += trans[1];
119  }
121  errno = 0;
122 }
123 
124 void set_structure() {
125  for (auto& sc: splitcell_list_) {
126  if (sc.that_host_ == nrnmpi_myid + 1) {
127  transfer_p_[ip + 0] = &NODED(sc.rootsec_->parentnode);
128  transfer_p_[ip + 1] = &NODERHS(sc.rootsec_->parentnode);
129  } else {
130  assert(sc.that_host_ == nrnmpi_myid - 1);
131  transfer_p_[im + 0] = &NODED(sc.rootsec_->parentnode);
132  transfer_p_[im + 1] = &NODERHS(sc.rootsec_->parentnode);
133  }
134  }
135 }
136 
137 #endif
Section * chk_access()
Definition: cabcode.cpp:449
const char * secname(Section *sec)
name of section (for use in error messages)
Definition: cabcode.cpp:1674
#define cnt
Definition: tqueue.hpp:44
#define i
Definition: md1redef.h:19
char buf[512]
Definition: init.cpp:13
#define assert(ex)
Definition: hocassrt.h:24
static double nrnmpi_splitcell_wait_
Definition: multisplit.cpp:36
static double nrnmpi_wtime()
Definition: multisplit.cpp:48
static void nrnmpi_send_doubles(double *, int, int, int)
Definition: multisplit.cpp:45
void hoc_execerror(const char *s1, const char *s2)
Definition: nrnoc_aux.cpp:39
int structure_change_cnt
int Sprintf(char(&buf)[N], const char *fmt, Args &&... args)
Redirect sprintf to snprintf if the buffer size can be deduced.
Definition: wrap_sprintf.h:14
int nrnmpi_myid
#define NODERHS(n)
Definition: section_fwd.hpp:55
#define NODED(n)
Definition: section_fwd.hpp:54
void nrnmpi_splitcell_connect(int that_host)
Definition: splitcell.cpp:48
Section * parentsec
Definition: section.h:53