Merge release-5-0 into master
[alexxy/gromacs.git] / src / gromacs / gmxlib / network.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "network.h"
38
39 #include "config.h"
40
41 #include <ctype.h>
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "types/commrec.h"
47 #include "copyrite.h"
48 #include "macros.h"
49
50 #include "gromacs/utility/basenetwork.h"
51 #include "gromacs/utility/cstringutil.h"
52 #include "gromacs/utility/fatalerror.h"
53 #include "gromacs/utility/futil.h"
54 #include "gromacs/utility/gmxmpi.h"
55 #include "gromacs/utility/smalloc.h"
56
57 /* The source code in this file should be thread-safe.
58       Please keep it that way. */
59
60 void gmx_fill_commrec_from_mpi(t_commrec gmx_unused *cr)
61 {
62 #ifndef GMX_MPI
63     gmx_call("gmx_fill_commrec_from_mpi");
64 #else
65     if (!gmx_mpi_initialized())
66     {
67         gmx_comm("MPI has not been initialized properly");
68     }
69
70     cr->nnodes           = gmx_node_num();
71     cr->nodeid           = gmx_node_rank();
72     cr->sim_nodeid       = cr->nodeid;
73     cr->mpi_comm_mysim   = MPI_COMM_WORLD;
74     cr->mpi_comm_mygroup = MPI_COMM_WORLD;
75
76 #endif
77 }
78
79 t_commrec *init_commrec()
80 {
81     t_commrec    *cr;
82
83     snew(cr, 1);
84
85 #ifdef GMX_LIB_MPI
86     gmx_fill_commrec_from_mpi(cr);
87 #else
88     cr->mpi_comm_mysim   = NULL;
89     cr->mpi_comm_mygroup = NULL;
90     cr->nnodes           = 1;
91     cr->sim_nodeid       = 0;
92     cr->nodeid           = cr->sim_nodeid;
93 #endif
94
95     // TODO cr->duty should not be initialized here
96     cr->duty = (DUTY_PP | DUTY_PME);
97
98 #if defined GMX_MPI && !defined MPI_IN_PLACE_EXISTS
99     /* initialize the MPI_IN_PLACE replacement buffers */
100     snew(cr->mpb, 1);
101     cr->mpb->ibuf        = NULL;
102     cr->mpb->libuf       = NULL;
103     cr->mpb->fbuf        = NULL;
104     cr->mpb->dbuf        = NULL;
105     cr->mpb->ibuf_alloc  = 0;
106     cr->mpb->libuf_alloc = 0;
107     cr->mpb->fbuf_alloc  = 0;
108     cr->mpb->dbuf_alloc  = 0;
109 #endif
110
111     return cr;
112 }
113
114 t_commrec *reinitialize_commrec_for_this_thread(const t_commrec gmx_unused *cro)
115 {
116 #ifdef GMX_THREAD_MPI
117     t_commrec *cr;
118
119     /* make a thread-specific commrec */
120     snew(cr, 1);
121     /* now copy the whole thing, so settings like the number of PME nodes
122        get propagated. */
123     *cr = *cro;
124
125     /* and we start setting our own thread-specific values for things */
126     gmx_fill_commrec_from_mpi(cr);
127
128     // TODO cr->duty should not be initialized here
129     cr->duty             = (DUTY_PP | DUTY_PME);
130
131     return cr;
132 #else
133     return NULL;
134 #endif
135 }
136
137 void gmx_setup_nodecomm(FILE gmx_unused *fplog, t_commrec *cr)
138 {
139     gmx_nodecomm_t *nc;
140     int             n, rank, nodehash, ng, ni;
141
142     /* Many MPI implementations do not optimize MPI_Allreduce
143      * (and probably also other global communication calls)
144      * for multi-core nodes connected by a network.
145      * We can optimize such communication by using one MPI call
146      * within each node and one between the nodes.
147      * For MVAPICH2 and Intel MPI this reduces the time for
148      * the global_stat communication by 25%
149      * for 2x2-core 3 GHz Woodcrest connected by mixed DDR/SDR Infiniband.
150      * B. Hess, November 2007
151      */
152
153     nc = &cr->nc;
154
155     nc->bUse = FALSE;
156 #ifndef GMX_THREAD_MPI
157 #ifdef GMX_MPI
158     MPI_Comm_size(cr->mpi_comm_mygroup, &n);
159     MPI_Comm_rank(cr->mpi_comm_mygroup, &rank);
160
161     nodehash = gmx_physicalnode_id_hash();
162
163     if (debug)
164     {
165         fprintf(debug, "In gmx_setup_nodecomm: splitting communicator of size %d\n", n);
166     }
167
168
169     /* The intra-node communicator, split on node number */
170     MPI_Comm_split(cr->mpi_comm_mygroup, nodehash, rank, &nc->comm_intra);
171     MPI_Comm_rank(nc->comm_intra, &nc->rank_intra);
172     if (debug)
173     {
174         fprintf(debug, "In gmx_setup_nodecomm: node ID %d rank within node %d\n",
175                 rank, nc->rank_intra);
176     }
177     /* The inter-node communicator, split on rank_intra.
178      * We actually only need the one for rank=0,
179      * but it is easier to create them all.
180      */
181     MPI_Comm_split(cr->mpi_comm_mygroup, nc->rank_intra, rank, &nc->comm_inter);
182     /* Check if this really created two step communication */
183     MPI_Comm_size(nc->comm_inter, &ng);
184     MPI_Comm_size(nc->comm_intra, &ni);
185     if (debug)
186     {
187         fprintf(debug, "In gmx_setup_nodecomm: groups %d, my group size %d\n",
188                 ng, ni);
189     }
190
191     if (getenv("GMX_NO_NODECOMM") == NULL &&
192         ((ng > 1 && ng < n) || (ni > 1 && ni < n)))
193     {
194         nc->bUse = TRUE;
195         if (fplog)
196         {
197             fprintf(fplog, "Using two step summing over %d groups of on average %.1f ranks\n\n",
198                     ng, (real)n/(real)ng);
199         }
200         if (nc->rank_intra > 0)
201         {
202             MPI_Comm_free(&nc->comm_inter);
203         }
204     }
205     else
206     {
207         /* One group or all processes in a separate group, use normal summing */
208         MPI_Comm_free(&nc->comm_inter);
209         MPI_Comm_free(&nc->comm_intra);
210         if (debug)
211         {
212             fprintf(debug, "In gmx_setup_nodecomm: not unsing separate inter- and intra-node communicators.\n");
213         }
214     }
215 #endif
216 #else
217     /* tMPI runs only on a single node so just use the nodeid */
218     nc->rank_intra = cr->nodeid;
219 #endif
220 }
221
222 void gmx_init_intranode_counters(t_commrec *cr)
223 {
224     /* counters for PP+PME and PP-only processes on my physical node */
225     int nrank_intranode, rank_intranode;
226     int nrank_pp_intranode, rank_pp_intranode;
227     /* thread-MPI is not initialized when not running in parallel */
228 #if defined GMX_MPI && !defined GMX_THREAD_MPI
229     int nrank_world, rank_world;
230     int i, myhash, *hash, *hash_s, *hash_pp, *hash_pp_s;
231
232     MPI_Comm_size(MPI_COMM_WORLD, &nrank_world);
233     MPI_Comm_rank(MPI_COMM_WORLD, &rank_world);
234
235     /* Get a (hopefully unique) hash that identifies our physical node */
236     myhash = gmx_physicalnode_id_hash();
237
238     /* We can't rely on MPI_IN_PLACE, so we need send and receive buffers */
239     snew(hash,   nrank_world);
240     snew(hash_s, nrank_world);
241     snew(hash_pp,   nrank_world);
242     snew(hash_pp_s, nrank_world);
243
244     hash_s[rank_world]    = myhash;
245     hash_pp_s[rank_world] = (cr->duty & DUTY_PP) ? myhash : -1;
246
247     MPI_Allreduce(hash_s,    hash,    nrank_world, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
248     MPI_Allreduce(hash_pp_s, hash_pp, nrank_world, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
249
250     nrank_intranode    = 0;
251     rank_intranode     = 0;
252     nrank_pp_intranode = 0;
253     rank_pp_intranode  = 0;
254     for (i = 0; i < nrank_world; i++)
255     {
256         if (hash[i] == myhash)
257         {
258             nrank_intranode++;
259             if (i < rank_world)
260             {
261                 rank_intranode++;
262             }
263         }
264         if (hash_pp[i] == myhash)
265         {
266             nrank_pp_intranode++;
267             if ((cr->duty & DUTY_PP) && i < rank_world)
268             {
269                 rank_pp_intranode++;
270             }
271         }
272     }
273     sfree(hash);
274     sfree(hash_s);
275     sfree(hash_pp);
276     sfree(hash_pp_s);
277 #else
278     /* Serial or thread-MPI code: we run within a single physical node */
279     nrank_intranode    = cr->nnodes;
280     rank_intranode     = cr->sim_nodeid;
281     nrank_pp_intranode = cr->nnodes - cr->npmenodes;
282     rank_pp_intranode  = cr->nodeid;
283 #endif
284
285     if (debug)
286     {
287         char sbuf[STRLEN];
288         if (cr->duty & DUTY_PP && cr->duty & DUTY_PME)
289         {
290             sprintf(sbuf, "PP+PME");
291         }
292         else
293         {
294             sprintf(sbuf, "%s", cr->duty & DUTY_PP ? "PP" : "PME");
295         }
296         fprintf(debug, "On %3s rank %d: nrank_intranode=%d, rank_intranode=%d, "
297                 "nrank_pp_intranode=%d, rank_pp_intranode=%d\n",
298                 sbuf, cr->sim_nodeid,
299                 nrank_intranode, rank_intranode,
300                 nrank_pp_intranode, rank_pp_intranode);
301     }
302
303     cr->nrank_intranode    = nrank_intranode;
304     cr->rank_intranode     = rank_intranode;
305     cr->nrank_pp_intranode = nrank_pp_intranode;
306     cr->rank_pp_intranode  = rank_pp_intranode;
307 }
308
309
310 void gmx_barrier(const t_commrec gmx_unused *cr)
311 {
312 #ifndef GMX_MPI
313     gmx_call("gmx_barrier");
314 #else
315     MPI_Barrier(cr->mpi_comm_mygroup);
316 #endif
317 }
318
319 void gmx_bcast(int gmx_unused nbytes, void gmx_unused *b, const t_commrec gmx_unused *cr)
320 {
321 #ifndef GMX_MPI
322     gmx_call("gmx_bast");
323 #else
324     MPI_Bcast(b, nbytes, MPI_BYTE, MASTERRANK(cr), cr->mpi_comm_mygroup);
325 #endif
326 }
327
328 void gmx_bcast_sim(int gmx_unused nbytes, void gmx_unused *b, const t_commrec gmx_unused *cr)
329 {
330 #ifndef GMX_MPI
331     gmx_call("gmx_bast");
332 #else
333     MPI_Bcast(b, nbytes, MPI_BYTE, MASTERRANK(cr), cr->mpi_comm_mysim);
334 #endif
335 }
336
337 void gmx_sumd(int gmx_unused nr, double gmx_unused r[], const t_commrec gmx_unused *cr)
338 {
339 #ifndef GMX_MPI
340     gmx_call("gmx_sumd");
341 #else
342 #if defined(MPI_IN_PLACE_EXISTS)
343     if (cr->nc.bUse)
344     {
345         if (cr->nc.rank_intra == 0)
346         {
347             /* Use two step summing. */
348             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM, 0,
349                        cr->nc.comm_intra);
350             /* Sum the roots of the internal (intra) buffers. */
351             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM,
352                           cr->nc.comm_inter);
353         }
354         else
355         {
356             /* This is here because of the silly MPI specification
357                 that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
358             MPI_Reduce(r, NULL, nr, MPI_DOUBLE, MPI_SUM, 0, cr->nc.comm_intra);
359         }
360         MPI_Bcast(r, nr, MPI_DOUBLE, 0, cr->nc.comm_intra);
361     }
362     else
363     {
364         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM,
365                       cr->mpi_comm_mygroup);
366     }
367 #else
368     int i;
369
370     if (nr > cr->mpb->dbuf_alloc)
371     {
372         cr->mpb->dbuf_alloc = nr;
373         srenew(cr->mpb->dbuf, cr->mpb->dbuf_alloc);
374     }
375     if (cr->nc.bUse)
376     {
377         /* Use two step summing */
378         MPI_Allreduce(r, cr->mpb->dbuf, nr, MPI_DOUBLE, MPI_SUM, cr->nc.comm_intra);
379         if (cr->nc.rank_intra == 0)
380         {
381             /* Sum with the buffers reversed */
382             MPI_Allreduce(cr->mpb->dbuf, r, nr, MPI_DOUBLE, MPI_SUM,
383                           cr->nc.comm_inter);
384         }
385         MPI_Bcast(r, nr, MPI_DOUBLE, 0, cr->nc.comm_intra);
386     }
387     else
388     {
389         MPI_Allreduce(r, cr->mpb->dbuf, nr, MPI_DOUBLE, MPI_SUM,
390                       cr->mpi_comm_mygroup);
391         for (i = 0; i < nr; i++)
392         {
393             r[i] = cr->mpb->dbuf[i];
394         }
395     }
396 #endif
397 #endif
398 }
399
400 void gmx_sumf(int gmx_unused nr, float gmx_unused r[], const t_commrec gmx_unused *cr)
401 {
402 #ifndef GMX_MPI
403     gmx_call("gmx_sumf");
404 #else
405 #if defined(MPI_IN_PLACE_EXISTS)
406     if (cr->nc.bUse)
407     {
408         /* Use two step summing.  */
409         if (cr->nc.rank_intra == 0)
410         {
411             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM, 0,
412                        cr->nc.comm_intra);
413             /* Sum the roots of the internal (intra) buffers */
414             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM,
415                           cr->nc.comm_inter);
416         }
417         else
418         {
419             /* This is here because of the silly MPI specification
420                 that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
421             MPI_Reduce(r, NULL, nr, MPI_FLOAT, MPI_SUM, 0, cr->nc.comm_intra);
422         }
423         MPI_Bcast(r, nr, MPI_FLOAT, 0, cr->nc.comm_intra);
424     }
425     else
426     {
427         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM, cr->mpi_comm_mygroup);
428     }
429 #else
430     int i;
431
432     if (nr > cr->mpb->fbuf_alloc)
433     {
434         cr->mpb->fbuf_alloc = nr;
435         srenew(cr->mpb->fbuf, cr->mpb->fbuf_alloc);
436     }
437     if (cr->nc.bUse)
438     {
439         /* Use two step summing */
440         MPI_Allreduce(r, cr->mpb->fbuf, nr, MPI_FLOAT, MPI_SUM, cr->nc.comm_intra);
441         if (cr->nc.rank_intra == 0)
442         {
443             /* Sum with the buffers reversed */
444             MPI_Allreduce(cr->mpb->fbuf, r, nr, MPI_FLOAT, MPI_SUM,
445                           cr->nc.comm_inter);
446         }
447         MPI_Bcast(r, nr, MPI_FLOAT, 0, cr->nc.comm_intra);
448     }
449     else
450     {
451         MPI_Allreduce(r, cr->mpb->fbuf, nr, MPI_FLOAT, MPI_SUM,
452                       cr->mpi_comm_mygroup);
453         for (i = 0; i < nr; i++)
454         {
455             r[i] = cr->mpb->fbuf[i];
456         }
457     }
458 #endif
459 #endif
460 }
461
462 void gmx_sumi(int gmx_unused nr, int gmx_unused r[], const t_commrec gmx_unused *cr)
463 {
464 #ifndef GMX_MPI
465     gmx_call("gmx_sumi");
466 #else
467 #if defined(MPI_IN_PLACE_EXISTS)
468     if (cr->nc.bUse)
469     {
470         /* Use two step summing */
471         if (cr->nc.rank_intra == 0)
472         {
473             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, 0, cr->nc.comm_intra);
474             /* Sum with the buffers reversed */
475             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, cr->nc.comm_inter);
476         }
477         else
478         {
479             /* This is here because of the silly MPI specification
480                 that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
481             MPI_Reduce(r, NULL, nr, MPI_INT, MPI_SUM, 0, cr->nc.comm_intra);
482         }
483         MPI_Bcast(r, nr, MPI_INT, 0, cr->nc.comm_intra);
484     }
485     else
486     {
487         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, cr->mpi_comm_mygroup);
488     }
489 #else
490     int i;
491
492     if (nr > cr->mpb->ibuf_alloc)
493     {
494         cr->mpb->ibuf_alloc = nr;
495         srenew(cr->mpb->ibuf, cr->mpb->ibuf_alloc);
496     }
497     if (cr->nc.bUse)
498     {
499         /* Use two step summing */
500         MPI_Allreduce(r, cr->mpb->ibuf, nr, MPI_INT, MPI_SUM, cr->nc.comm_intra);
501         if (cr->nc.rank_intra == 0)
502         {
503             /* Sum with the buffers reversed */
504             MPI_Allreduce(cr->mpb->ibuf, r, nr, MPI_INT, MPI_SUM, cr->nc.comm_inter);
505         }
506         MPI_Bcast(r, nr, MPI_INT, 0, cr->nc.comm_intra);
507     }
508     else
509     {
510         MPI_Allreduce(r, cr->mpb->ibuf, nr, MPI_INT, MPI_SUM, cr->mpi_comm_mygroup);
511         for (i = 0; i < nr; i++)
512         {
513             r[i] = cr->mpb->ibuf[i];
514         }
515     }
516 #endif
517 #endif
518 }
519
520 void gmx_sumli(int gmx_unused nr, gmx_int64_t gmx_unused r[], const t_commrec gmx_unused *cr)
521 {
522 #ifndef GMX_MPI
523     gmx_call("gmx_sumli");
524 #else
525 #if defined(MPI_IN_PLACE_EXISTS)
526     if (cr->nc.bUse)
527     {
528         /* Use two step summing */
529         if (cr->nc.rank_intra == 0)
530         {
531             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM, 0,
532                        cr->nc.comm_intra);
533             /* Sum with the buffers reversed */
534             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM,
535                           cr->nc.comm_inter);
536         }
537         else
538         {
539             /* This is here because of the silly MPI specification
540                 that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
541             MPI_Reduce(r, NULL, nr, MPI_INT64_T, MPI_SUM, 0, cr->nc.comm_intra);
542         }
543         MPI_Bcast(r, nr, MPI_INT64_T, 0, cr->nc.comm_intra);
544     }
545     else
546     {
547         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM, cr->mpi_comm_mygroup);
548     }
549 #else
550     int i;
551
552     if (nr > cr->mpb->libuf_alloc)
553     {
554         cr->mpb->libuf_alloc = nr;
555         srenew(cr->mpb->libuf, cr->mpb->libuf_alloc);
556     }
557     if (cr->nc.bUse)
558     {
559         /* Use two step summing */
560         MPI_Allreduce(r, cr->mpb->libuf, nr, MPI_INT64_T, MPI_SUM,
561                       cr->nc.comm_intra);
562         if (cr->nc.rank_intra == 0)
563         {
564             /* Sum with the buffers reversed */
565             MPI_Allreduce(cr->mpb->libuf, r, nr, MPI_INT64_T, MPI_SUM,
566                           cr->nc.comm_inter);
567         }
568         MPI_Bcast(r, nr, MPI_INT64_T, 0, cr->nc.comm_intra);
569     }
570     else
571     {
572         MPI_Allreduce(r, cr->mpb->libuf, nr, MPI_INT64_T, MPI_SUM,
573                       cr->mpi_comm_mygroup);
574         for (i = 0; i < nr; i++)
575         {
576             r[i] = cr->mpb->libuf[i];
577         }
578     }
579 #endif
580 #endif
581 }
582
583
584
585 #ifdef GMX_MPI
586 static void gmx_sumd_comm(int nr, double r[], MPI_Comm mpi_comm)
587 {
588 #if defined(MPI_IN_PLACE_EXISTS)
589     MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM, mpi_comm);
590 #else
591     /* this function is only used in code that is not performance critical,
592        (during setup, when comm_rec is not the appropriate communication
593        structure), so this isn't as bad as it looks. */
594     double *buf;
595     int     i;
596
597     snew(buf, nr);
598     MPI_Allreduce(r, buf, nr, MPI_DOUBLE, MPI_SUM, mpi_comm);
599     for (i = 0; i < nr; i++)
600     {
601         r[i] = buf[i];
602     }
603     sfree(buf);
604 #endif
605 }
606 #endif
607
608 #ifdef GMX_MPI
609 static void gmx_sumf_comm(int nr, float r[], MPI_Comm mpi_comm)
610 {
611 #if defined(MPI_IN_PLACE_EXISTS)
612     MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM, mpi_comm);
613 #else
614     /* this function is only used in code that is not performance critical,
615        (during setup, when comm_rec is not the appropriate communication
616        structure), so this isn't as bad as it looks. */
617     float *buf;
618     int    i;
619
620     snew(buf, nr);
621     MPI_Allreduce(r, buf, nr, MPI_FLOAT, MPI_SUM, mpi_comm);
622     for (i = 0; i < nr; i++)
623     {
624         r[i] = buf[i];
625     }
626     sfree(buf);
627 #endif
628 }
629 #endif
630
631 void gmx_sumd_sim(int gmx_unused nr, double gmx_unused r[], const gmx_multisim_t gmx_unused *ms)
632 {
633 #ifndef GMX_MPI
634     gmx_call("gmx_sumd_sim");
635 #else
636     gmx_sumd_comm(nr, r, ms->mpi_comm_masters);
637 #endif
638 }
639
640 void gmx_sumf_sim(int gmx_unused nr, float gmx_unused r[], const gmx_multisim_t gmx_unused *ms)
641 {
642 #ifndef GMX_MPI
643     gmx_call("gmx_sumf_sim");
644 #else
645     gmx_sumf_comm(nr, r, ms->mpi_comm_masters);
646 #endif
647 }
648
649 void gmx_sumi_sim(int gmx_unused nr, int gmx_unused r[], const gmx_multisim_t gmx_unused *ms)
650 {
651 #ifndef GMX_MPI
652     gmx_call("gmx_sumi_sim");
653 #else
654 #if defined(MPI_IN_PLACE_EXISTS)
655     MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, ms->mpi_comm_masters);
656 #else
657     /* this is thread-unsafe, but it will do for now: */
658     int i;
659
660     if (nr > ms->mpb->ibuf_alloc)
661     {
662         ms->mpb->ibuf_alloc = nr;
663         srenew(ms->mpb->ibuf, ms->mpb->ibuf_alloc);
664     }
665     MPI_Allreduce(r, ms->mpb->ibuf, nr, MPI_INT, MPI_SUM, ms->mpi_comm_masters);
666     for (i = 0; i < nr; i++)
667     {
668         r[i] = ms->mpb->ibuf[i];
669     }
670 #endif
671 #endif
672 }
673
674 void gmx_sumli_sim(int gmx_unused nr, gmx_int64_t gmx_unused r[], const gmx_multisim_t gmx_unused *ms)
675 {
676 #ifndef GMX_MPI
677     gmx_call("gmx_sumli_sim");
678 #else
679 #if defined(MPI_IN_PLACE_EXISTS)
680     MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM,
681                   ms->mpi_comm_masters);
682 #else
683     /* this is thread-unsafe, but it will do for now: */
684     int i;
685
686     if (nr > ms->mpb->libuf_alloc)
687     {
688         ms->mpb->libuf_alloc = nr;
689         srenew(ms->mpb->libuf, ms->mpb->libuf_alloc);
690     }
691     MPI_Allreduce(r, ms->mpb->libuf, nr, MPI_INT64_T, MPI_SUM,
692                   ms->mpi_comm_masters);
693     for (i = 0; i < nr; i++)
694     {
695         r[i] = ms->mpb->libuf[i];
696     }
697 #endif
698 #endif
699 }
700
701 gmx_bool gmx_fexist_master(const char *fname, t_commrec *cr)
702 {
703     gmx_bool bExist;
704
705     if (SIMMASTER(cr))
706     {
707         bExist = gmx_fexist(fname);
708     }
709     if (PAR(cr))
710     {
711         gmx_bcast(sizeof(bExist), &bExist, cr);
712     }
713     return bExist;
714 }
715
716 void gmx_fatal_collective(int f_errno, const char *file, int line,
717                           const t_commrec *cr, gmx_domdec_t *dd,
718                           const char *fmt, ...)
719 {
720     va_list  ap;
721     gmx_bool bMaster, bFinalize;
722 #ifdef GMX_MPI
723     int      result;
724     /* Check if we are calling on all processes in MPI_COMM_WORLD */
725     if (cr != NULL)
726     {
727         MPI_Comm_compare(cr->mpi_comm_mysim, MPI_COMM_WORLD, &result);
728     }
729     else
730     {
731         MPI_Comm_compare(dd->mpi_comm_all, MPI_COMM_WORLD, &result);
732     }
733     /* Any result except MPI_UNEQUAL allows us to call MPI_Finalize */
734     bFinalize = (result != MPI_UNEQUAL);
735 #else
736     bFinalize = TRUE;
737 #endif
738     bMaster = (cr != NULL && MASTER(cr)) || (dd != NULL && DDMASTER(dd));
739
740     va_start(ap, fmt);
741     gmx_fatal_mpi_va(f_errno, file, line, bMaster, bFinalize, fmt, ap);
742     va_end(ap);
743 }