Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2012-2023 The plumed team
3 : (see the PEOPLE file at the root of the distribution for a list of names)
4 :
5 : See http://www.plumed.org for more information.
6 :
7 : This file is part of plumed, version 2.
8 :
9 : plumed is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : plumed is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 : #include "Communicator.h"
23 : #include "Exception.h"
24 : #include "AtomNumber.h"
25 : #include <cstdlib>
26 : #include <cstring>
27 : #include <cstdio>
28 :
29 : namespace PLMD {
30 :
31 228 : bool Communicator::plumedHasMPI() {
32 : #ifdef __PLUMED_HAS_MPI
33 228 : return true;
34 : #else
35 : return false;
36 : #endif
37 : }
38 :
39 1633593 : Communicator::Communicator()
40 : #ifdef __PLUMED_HAS_MPI
41 1633593 : : communicator(MPI_COMM_SELF)
42 : #endif
43 : {
44 1633593 : }
45 :
46 0 : Communicator::Communicator(const Communicator&pc)
47 : #ifdef __PLUMED_HAS_MPI
48 : // communicator must be initialized before calling Set_comm, which frees it if needed
49 0 : : communicator(MPI_COMM_SELF)
50 : #endif
51 : {
52 0 : Set_comm(pc.communicator);
53 0 : standIn=pc.standIn;
54 0 : }
55 :
56 : Communicator::Status Communicator::StatusIgnore;
57 :
58 0 : Communicator& Communicator::operator=(const Communicator&pc) {
59 0 : if (this != &pc) {
60 0 : Set_comm(pc.communicator);
61 0 : standIn=pc.standIn;
62 : }
63 0 : return *this;
64 : }
65 :
66 6174 : void Communicator::setStandIn(bool s) {
67 6174 : standIn=s;
68 6174 : }
69 :
70 4 : bool Communicator::isStandIn()const {
71 4 : return standIn;
72 : }
73 :
74 16953699 : void Communicator::checkNotStandIn()const {
75 : constexpr const char* msg="this communicator is a stand-in on this process: it was given to other processes but not to this one. "
76 : "For multi_sim_comm, this means that only the first process of each replica can use it. "
77 : "Use getNumberOfReplicas() and getReplicaIndex(), or use multi_sim_comm on the first process of comm and broadcast the result over comm.";
78 : #ifndef NDEBUG
79 : if(standIn) {
80 : // Typically only some processes get here. The log is written collectively,
81 : // so the exception message might never reach it: print it right away.
82 : std::fprintf(stderr,"\nPLUMED: ERROR (debug build): %s\n",msg);
83 : std::fflush(stderr);
84 : }
85 : #endif
86 : plumed_dbg_massert(!standIn,msg);
87 16953699 : }
88 :
89 8715065 : int Communicator::Get_rank()const {
90 8715065 : checkNotStandIn();
91 8715065 : int r=0;
92 : #ifdef __PLUMED_HAS_MPI
93 8715065 : if(initialized()) {
94 3097236 : MPI_Comm_rank(communicator,&r);
95 : }
96 : #endif
97 8715065 : return r;
98 : }
99 :
100 3206987 : int Communicator::Get_size()const {
101 3206987 : checkNotStandIn();
102 3206987 : int s=1;
103 : #ifdef __PLUMED_HAS_MPI
104 3206987 : if(initialized()) {
105 1192205 : MPI_Comm_size(communicator,&s);
106 : }
107 : #endif
108 3206987 : return s;
109 : }
110 :
111 5477 : void Communicator::Set_comm(MPI_Comm c) {
112 5477 : standIn=false;
113 : #ifdef __PLUMED_HAS_MPI
114 5477 : if(initialized()) {
115 4628 : if(communicator!=MPI_COMM_SELF && communicator!=MPI_COMM_WORLD && communicator!=MPI_COMM_NULL) {
116 0 : MPI_Comm_free(&communicator);
117 : }
118 4628 : if(c!=MPI_COMM_SELF) {
119 4560 : MPI_Comm_dup(c,&communicator);
120 : } else {
121 : // MPI_Comm_free sets the freed communicator to MPI_COMM_NULL
122 68 : communicator=MPI_COMM_SELF;
123 : }
124 : }
125 : #else
126 : (void) c;
127 : #endif
128 5477 : }
129 :
130 3261728 : Communicator::~Communicator() {
131 : #ifdef __PLUMED_HAS_MPI
132 1633593 : if(initialized() && communicator!=MPI_COMM_SELF && communicator!=MPI_COMM_WORLD && communicator!=MPI_COMM_NULL) {
133 5226 : MPI_Comm_free(&communicator);
134 : }
135 : #endif
136 3261728 : }
137 :
138 3389 : void Communicator::Set_comm(const TypesafePtr & val) {
139 : #ifdef __PLUMED_HAS_MPI
140 3389 : plumed_massert(initialized(),"you are trying to use an MPI function, but MPI is not initialized");
141 3389 : if(val) {
142 3389 : Set_comm(*(const MPI_Comm*)val.get<const void*>());
143 : }
144 : #else
145 : (void) val;
146 : plumed_merror("you are trying to use an MPI function, but PLUMED has been compiled without MPI support");
147 : #endif
148 3389 : }
149 :
150 0 : void Communicator::Set_fcomm(const TypesafePtr & val) {
151 : #ifdef __PLUMED_HAS_MPI
152 0 : plumed_massert(initialized(),"you are trying to use an MPI function, but MPI is not initialized");
153 0 : if(val) {
154 0 : MPI_Comm comm=MPI_Comm_f2c(*(const MPI_Fint*)val.get<const void*>());
155 0 : Set_comm(comm);
156 : }
157 : #else
158 : (void) val;
159 : plumed_merror("you are trying to use an MPI function, but PLUMED has been compiled without MPI support");
160 : #endif
161 0 : }
162 :
163 0 : void Communicator::Abort(int errorcode) {
164 : #ifdef __PLUMED_HAS_MPI
165 0 : if(initialized()) {
166 0 : MPI_Abort(communicator,errorcode);
167 : }
168 : #endif
169 0 : std::fprintf(stderr,"aborting with error code %d\n",errorcode);
170 0 : std::abort();
171 : }
172 :
173 403829 : void Communicator::Bcast(Data data,int root) {
174 403829 : checkNotStandIn();
175 : #if defined(__PLUMED_HAS_MPI)
176 403829 : if(initialized()) {
177 111845 : MPI_Bcast(data.pointer,data.size,data.type,root,communicator);
178 : }
179 : #else
180 : (void) data;
181 : (void) root;
182 : #endif
183 403829 : }
184 :
185 12 : void Communicator::Scatter(ConstData sendbuf,int sendcount,Data recvbuf,int recvcount,int root) {
186 12 : checkNotStandIn();
187 12 : void*s=const_cast<void*>((const void*)sendbuf.pointer);
188 : #if defined(__PLUMED_HAS_MPI)
189 12 : if(initialized()) {
190 11 : MPI_Scatter(s,sendcount,sendbuf.type,recvbuf.pointer,recvcount,recvbuf.type,root,communicator);
191 : } else {
192 : // Non-MPI case: just copy from sendbuf to recvbuf
193 1 : plumed_assert(sendbuf.nbytes==recvbuf.nbytes);
194 1 : plumed_assert(sendcount==recvcount);
195 1 : if(s) {
196 1 : std::memcpy(recvbuf.pointer,s,size_t(recvcount)*recvbuf.nbytes);
197 : }
198 : }
199 : #else
200 : plumed_assert(sendbuf.nbytes==recvbuf.nbytes);
201 : plumed_assert(sendcount==recvcount);
202 : if(s) {
203 : std::memcpy(recvbuf.pointer,s,size_t(recvcount)*recvbuf.nbytes);
204 : }
205 : (void) root;
206 : #endif
207 12 : }
208 :
209 12 : void Communicator::Gather(ConstData sendbuf,int sendcount,Data recvbuf,int recvcount,int root) {
210 12 : checkNotStandIn();
211 12 : void*s=const_cast<void*>((const void*)sendbuf.pointer);
212 : #if defined(__PLUMED_HAS_MPI)
213 12 : if(initialized()) {
214 11 : MPI_Gather(s,sendcount,sendbuf.type,recvbuf.pointer,recvcount,recvbuf.type,root,communicator);
215 : } else {
216 : // Non-MPI case: just copy from sendbuf to recvbuf
217 1 : plumed_assert(sendbuf.nbytes==recvbuf.nbytes);
218 1 : plumed_assert(sendcount==recvcount);
219 1 : if(s) {
220 1 : std::memcpy(recvbuf.pointer,s,size_t(recvcount)*recvbuf.nbytes);
221 : }
222 : }
223 : #else
224 : plumed_assert(sendbuf.nbytes==recvbuf.nbytes);
225 : plumed_assert(sendcount==recvcount);
226 : if(s) {
227 : std::memcpy(recvbuf.pointer,s,size_t(recvcount)*recvbuf.nbytes);
228 : }
229 : (void) root;
230 : #endif
231 12 : }
232 :
233 4566192 : void Communicator::Sum(Data data) {
234 4566192 : checkNotStandIn();
235 : #if defined(__PLUMED_HAS_MPI)
236 4566192 : if(initialized()) {
237 1939104 : MPI_Allreduce(MPI_IN_PLACE,data.pointer,data.size,data.type,MPI_SUM,communicator);
238 : }
239 : #else
240 : (void) data;
241 : #endif
242 4566192 : }
243 :
244 3 : void Communicator::Prod(Data data) {
245 3 : checkNotStandIn();
246 : #if defined(__PLUMED_HAS_MPI)
247 3 : if(initialized()) {
248 3 : MPI_Allreduce(MPI_IN_PLACE,data.pointer,data.size,data.type,MPI_PROD,communicator);
249 : }
250 : #else
251 : (void) data;
252 : #endif
253 3 : }
254 :
255 6393 : void Communicator::Max(Data data) {
256 6393 : checkNotStandIn();
257 : #if defined(__PLUMED_HAS_MPI)
258 6393 : if(initialized()) {
259 3937 : MPI_Allreduce(MPI_IN_PLACE,data.pointer,data.size,data.type,MPI_MAX,communicator);
260 : }
261 : #else
262 : (void) data;
263 : #endif
264 6393 : }
265 :
266 238 : void Communicator::Min(Data data) {
267 238 : checkNotStandIn();
268 : #if defined(__PLUMED_HAS_MPI)
269 238 : if(initialized()) {
270 167 : MPI_Allreduce(MPI_IN_PLACE,data.pointer,data.size,data.type,MPI_MIN,communicator);
271 : }
272 : #else
273 : (void) data;
274 : #endif
275 238 : }
276 :
277 16503 : Communicator::Request Communicator::Isend(ConstData data,int source,int tag) {
278 16503 : checkNotStandIn();
279 : Request req;
280 : #ifdef __PLUMED_HAS_MPI
281 16503 : plumed_massert(initialized(),"you are trying to use an MPI function, but MPI is not initialized");
282 16503 : void*s=const_cast<void*>((const void*)data.pointer);
283 16503 : MPI_Isend(s,data.size,data.type,source,tag,communicator,&req.r);
284 : #else
285 : (void) data;
286 : (void) source;
287 : (void) tag;
288 : plumed_merror("you are trying to use an MPI function, but PLUMED has been compiled without MPI support");
289 : #endif
290 16503 : return req;
291 : }
292 :
293 4385 : void Communicator::Allgatherv(ConstData in,Data out,const int*recvcounts,const int*displs) {
294 4385 : checkNotStandIn();
295 4385 : void*s=const_cast<void*>((const void*)in.pointer);
296 4385 : void*r=const_cast<void*>((const void*)out.pointer);
297 : int*rc=const_cast<int*>(recvcounts);
298 : int*di=const_cast<int*>(displs);
299 : #if defined(__PLUMED_HAS_MPI)
300 4385 : if(initialized()) {
301 4384 : if(s==NULL) {
302 : s=MPI_IN_PLACE;
303 : }
304 4384 : MPI_Allgatherv(s,in.size,in.type,r,rc,di,out.type,communicator);
305 : } else {
306 1 : plumed_assert(in.nbytes==out.nbytes);
307 1 : plumed_assert(in.size==out.size);
308 1 : plumed_assert(rc);
309 1 : plumed_assert(rc[0]==in.size);
310 1 : plumed_assert(di);
311 1 : if(s) {
312 1 : std::memcpy(static_cast<char*>(r)+displs[0]*in.nbytes,s,size_t(in.size)*in.nbytes);
313 : }
314 : }
315 : #else
316 : plumed_assert(in.nbytes==out.nbytes);
317 : plumed_assert(in.size==out.size);
318 : plumed_assert(rc);
319 : plumed_assert(rc[0]==in.size);
320 : plumed_assert(di);
321 : if(s) {
322 : std::memcpy(static_cast<char*>(r)+displs[0]*in.nbytes,s,size_t(in.size)*in.nbytes);
323 : }
324 : #endif
325 4385 : }
326 :
327 8353 : void Communicator::Allgather(ConstData in,Data out) {
328 8353 : checkNotStandIn();
329 8353 : void*s=const_cast<void*>((const void*)in.pointer);
330 8353 : void*r=const_cast<void*>((const void*)out.pointer);
331 : #if defined(__PLUMED_HAS_MPI)
332 8353 : if(initialized()) {
333 8352 : if(s==NULL) {
334 : s=MPI_IN_PLACE;
335 : }
336 8352 : MPI_Allgather(s,in.size,in.type,r,out.size/Get_size(),out.type,communicator);
337 : } else {
338 1 : plumed_assert(in.nbytes==out.nbytes);
339 1 : plumed_assert(in.size==out.size);
340 1 : if(s) {
341 1 : std::memcpy(r,s,size_t(in.size)*in.nbytes);
342 : }
343 : }
344 : #else
345 : plumed_assert(in.nbytes==out.nbytes);
346 : plumed_assert(in.size==out.size);
347 : if(s) {
348 : std::memcpy(r,s,size_t(in.size)*in.nbytes);
349 : }
350 : #endif
351 8353 : }
352 :
353 16503 : void Communicator::Recv(Data data,int source,int tag,Status&status) {
354 16503 : checkNotStandIn();
355 : #ifdef __PLUMED_HAS_MPI
356 16503 : plumed_massert(initialized(),"you are trying to use an MPI function, but MPI is not initialized");
357 16503 : if(&status==&StatusIgnore) {
358 8360 : MPI_Recv(data.pointer,data.size,data.type,source,tag,communicator,MPI_STATUS_IGNORE);
359 : } else {
360 8143 : MPI_Recv(data.pointer,data.size,data.type,source,tag,communicator,&status.s);
361 : }
362 : #else
363 : (void) data;
364 : (void) source;
365 : (void) tag;
366 : (void) status;
367 : plumed_merror("you are trying to use an MPI function, but PLUMED has been compiled without MPI support");
368 : #endif
369 16503 : }
370 :
371 8558 : void Communicator::Barrier()const {
372 8558 : checkNotStandIn();
373 : #ifdef __PLUMED_HAS_MPI
374 8558 : if(initialized()) {
375 3886 : MPI_Barrier(communicator);
376 : }
377 : #endif
378 8558 : }
379 :
380 3181 : MPI_Comm & Communicator::Get_comm() {
381 3181 : return communicator;
382 : }
383 :
384 18631351 : bool Communicator::initialized() {
385 : #if defined(__PLUMED_HAS_MPI)
386 18631351 : int flag=0;
387 18631351 : MPI_Initialized(&flag);
388 18631351 : if(flag) {
389 : return true;
390 : } else {
391 12190458 : return false;
392 : }
393 : #endif
394 : return false;
395 : }
396 :
397 15935 : void Communicator::Request::wait(Status&s) {
398 : #ifdef __PLUMED_HAS_MPI
399 15935 : plumed_massert(initialized(),"you are trying to use an MPI function, but MPI is not initialized");
400 15935 : if(&s==&StatusIgnore) {
401 15934 : MPI_Wait(&r,MPI_STATUS_IGNORE);
402 : } else {
403 1 : MPI_Wait(&r,&s.s);
404 : }
405 : #else
406 : (void) s;
407 : plumed_merror("you are trying to use an MPI function, but PLUMED has been compiled without MPI support");
408 : #endif
409 15935 : }
410 :
411 : #ifdef __PLUMED_HAS_MPI
412 4 : template<> MPI_Datatype Communicator::getMPIType<float>() {
413 4 : return MPI_FLOAT;
414 : }
415 4907392 : template<> MPI_Datatype Communicator::getMPIType<double>() {
416 4907392 : return MPI_DOUBLE;
417 : }
418 118135 : template<> MPI_Datatype Communicator::getMPIType<int>() {
419 118135 : return MPI_INT;
420 : }
421 469 : template<> MPI_Datatype Communicator::getMPIType<char>() {
422 469 : return MPI_CHAR;
423 : }
424 17274 : template<> MPI_Datatype Communicator::getMPIType<unsigned>() {
425 17274 : return MPI_UNSIGNED;
426 : }
427 24 : template<> MPI_Datatype Communicator::getMPIType<AtomNumber>() {
428 24 : return MPI_UNSIGNED;
429 : }
430 24 : template<> MPI_Datatype Communicator::getMPIType<long unsigned>() {
431 24 : return MPI_UNSIGNED_LONG;
432 : }
433 6 : template<> MPI_Datatype Communicator::getMPIType<long long unsigned>() {
434 6 : return MPI_UNSIGNED_LONG_LONG;
435 : }
436 0 : template<> MPI_Datatype Communicator::getMPIType<long double>() {
437 0 : return MPI_LONG_DOUBLE;
438 : }
439 : #else
440 : template<> MPI_Datatype Communicator::getMPIType<float>() {
441 : return MPI_Datatype();
442 : }
443 : template<> MPI_Datatype Communicator::getMPIType<double>() {
444 : return MPI_Datatype();
445 : }
446 : template<> MPI_Datatype Communicator::getMPIType<int>() {
447 : return MPI_Datatype();
448 : }
449 : template<> MPI_Datatype Communicator::getMPIType<char>() {
450 : return MPI_Datatype();
451 : }
452 : template<> MPI_Datatype Communicator::getMPIType<unsigned>() {
453 : return MPI_Datatype();
454 : }
455 : template<> MPI_Datatype Communicator::getMPIType<AtomNumber>() {
456 : return MPI_Datatype();
457 : }
458 : template<> MPI_Datatype Communicator::getMPIType<long unsigned>() {
459 : return MPI_Datatype();
460 : }
461 : template<> MPI_Datatype Communicator::getMPIType<long long unsigned>() {
462 : return MPI_Datatype();
463 : }
464 : template<> MPI_Datatype Communicator::getMPIType<long double>() {
465 : return MPI_Datatype();
466 : }
467 : #endif
468 :
469 666 : void Communicator::Split(int color,int key,Communicator&pc)const {
470 666 : checkNotStandIn();
471 : #ifdef __PLUMED_HAS_MPI
472 : // free the communicator previously held by pc (it was obtained with MPI_Comm_dup or MPI_Comm_split)
473 666 : if(pc.communicator!=MPI_COMM_SELF && pc.communicator!=MPI_COMM_WORLD && pc.communicator!=MPI_COMM_NULL) {
474 0 : MPI_Comm_free(&pc.communicator);
475 : }
476 666 : pc.standIn=false;
477 666 : MPI_Comm_split(communicator,color,key,&pc.communicator);
478 : #else
479 : (void) color;
480 : (void) key;
481 : (void) pc;
482 : plumed_merror("you are trying to use an MPI function, but PLUMED has been compiled without MPI support");
483 : #endif
484 666 : }
485 :
486 8143 : int Communicator::Status::Get_count(MPI_Datatype type)const {
487 : int i;
488 : #ifdef __PLUMED_HAS_MPI
489 8143 : plumed_massert(initialized(),"you are trying to use an MPI function, but MPI is not initialized");
490 8143 : MPI_Get_count(const_cast<MPI_Status*>(&s),type,&i);
491 : #else
492 : i=0;
493 : plumed_merror("you are trying to use an MPI function, but PLUMED has been compiled without MPI support");
494 : #endif
495 8143 : return i;
496 : }
497 :
498 : }
499 :
|