Make all thread-mpi threads release cr structure
[alexxy/gromacs.git] / src / gromacs / gmxlib / network.cpp
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,2015,2016,2017,2018, 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 "gmxpre.h"
38
39 #include "network.h"
40
41 #include "config.h"
42
43 #include <cctype>
44 #include <cstdarg>
45 #include <cstdlib>
46 #include <cstring>
47
48 #include "gromacs/commandline/filenm.h"
49 #include "gromacs/mdtypes/commrec.h"
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 *cr)
61 {
62 #if !GMX_MPI
63     gmx_call("gmx_fill_commrec_from_mpi");
64     GMX_UNUSED_VALUE(cr);
65 #else
66     if (!gmx_mpi_initialized())
67     {
68         gmx_comm("MPI has not been initialized properly");
69     }
70
71     cr->nnodes           = gmx_node_num();
72     cr->nodeid           = gmx_node_rank();
73     cr->sim_nodeid       = cr->nodeid;
74     cr->mpi_comm_mysim   = MPI_COMM_WORLD;
75     cr->mpi_comm_mygroup = MPI_COMM_WORLD;
76 #endif
77 }
78
79 t_commrec *init_commrec()
80 {
81     t_commrec    *cr;
82
83     snew(cr, 1);
84
85 #if GMX_LIB_MPI
86     gmx_fill_commrec_from_mpi(cr);
87 #else
88     cr->mpi_comm_mysim   = MPI_COMM_NULL;
89     cr->mpi_comm_mygroup = MPI_COMM_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 GMX_MPI && !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 void done_mpi_in_place_buf(mpi_in_place_buf_t *buf)
115 {
116     if (nullptr != buf)
117     {
118         sfree(buf->ibuf);
119         sfree(buf->libuf);
120         sfree(buf->fbuf);
121         sfree(buf->dbuf);
122         sfree(buf);
123     }
124 }
125
126 void done_commrec(t_commrec *cr)
127 {
128     if (MASTER(cr))
129     {
130         if (nullptr != cr->dd)
131         {
132             // TODO: implement
133             // done_domdec(cr->dd);
134         }
135         done_mpi_in_place_buf(cr->mpb);
136     }
137     sfree(cr);
138 }
139
140 t_commrec *reinitialize_commrec_for_this_thread(const t_commrec *cro)
141 {
142 #if GMX_THREAD_MPI
143     t_commrec *cr;
144
145     /* make a thread-specific commrec */
146     snew(cr, 1);
147     /* now copy the whole thing, so settings like the number of PME nodes
148        get propagated. */
149     *cr = *cro;
150
151     /* and we start setting our own thread-specific values for things */
152     gmx_fill_commrec_from_mpi(cr);
153
154     // TODO cr->duty should not be initialized here
155     cr->duty             = (DUTY_PP | DUTY_PME);
156
157     return cr;
158 #else
159     GMX_UNUSED_VALUE(cro);
160     return nullptr;
161 #endif
162 }
163
164 void gmx_setup_nodecomm(FILE gmx_unused *fplog, t_commrec *cr)
165 {
166     gmx_nodecomm_t *nc;
167
168     /* Many MPI implementations do not optimize MPI_Allreduce
169      * (and probably also other global communication calls)
170      * for multi-core nodes connected by a network.
171      * We can optimize such communication by using one MPI call
172      * within each node and one between the nodes.
173      * For MVAPICH2 and Intel MPI this reduces the time for
174      * the global_stat communication by 25%
175      * for 2x2-core 3 GHz Woodcrest connected by mixed DDR/SDR Infiniband.
176      * B. Hess, November 2007
177      */
178
179     nc = &cr->nc;
180
181     nc->bUse = FALSE;
182 #if !GMX_THREAD_MPI
183 #if GMX_MPI
184     int n, rank;
185
186     // TODO PhysicalNodeCommunicator could be extended/used to handle
187     // the need for per-node per-group communicators.
188     MPI_Comm_size(cr->mpi_comm_mygroup, &n);
189     MPI_Comm_rank(cr->mpi_comm_mygroup, &rank);
190
191     int nodehash = gmx_physicalnode_id_hash();
192
193     if (debug)
194     {
195         fprintf(debug, "In gmx_setup_nodecomm: splitting communicator of size %d\n", n);
196     }
197
198
199     /* The intra-node communicator, split on node number */
200     MPI_Comm_split(cr->mpi_comm_mygroup, nodehash, rank, &nc->comm_intra);
201     MPI_Comm_rank(nc->comm_intra, &nc->rank_intra);
202     if (debug)
203     {
204         fprintf(debug, "In gmx_setup_nodecomm: node ID %d rank within node %d\n",
205                 rank, nc->rank_intra);
206     }
207     /* The inter-node communicator, split on rank_intra.
208      * We actually only need the one for rank=0,
209      * but it is easier to create them all.
210      */
211     MPI_Comm_split(cr->mpi_comm_mygroup, nc->rank_intra, rank, &nc->comm_inter);
212     /* Check if this really created two step communication */
213     int ng, ni;
214
215     MPI_Comm_size(nc->comm_inter, &ng);
216     MPI_Comm_size(nc->comm_intra, &ni);
217     if (debug)
218     {
219         fprintf(debug, "In gmx_setup_nodecomm: groups %d, my group size %d\n",
220                 ng, ni);
221     }
222
223     if (getenv("GMX_NO_NODECOMM") == nullptr &&
224         ((ng > 1 && ng < n) || (ni > 1 && ni < n)))
225     {
226         nc->bUse = TRUE;
227         if (fplog)
228         {
229             fprintf(fplog, "Using two step summing over %d groups of on average %.1f ranks\n\n",
230                     ng, (real)n/(real)ng);
231         }
232         if (nc->rank_intra > 0)
233         {
234             MPI_Comm_free(&nc->comm_inter);
235         }
236     }
237     else
238     {
239         /* One group or all processes in a separate group, use normal summing */
240         MPI_Comm_free(&nc->comm_inter);
241         MPI_Comm_free(&nc->comm_intra);
242         if (debug)
243         {
244             fprintf(debug, "In gmx_setup_nodecomm: not unsing separate inter- and intra-node communicators.\n");
245         }
246     }
247 #endif
248 #else
249     /* tMPI runs only on a single node so just use the nodeid */
250     nc->rank_intra = cr->nodeid;
251 #endif
252 }
253
254 void gmx_barrier(const t_commrec gmx_unused *cr)
255 {
256 #if !GMX_MPI
257     gmx_call("gmx_barrier");
258 #else
259     MPI_Barrier(cr->mpi_comm_mygroup);
260 #endif
261 }
262
263 void gmx_bcast(int gmx_unused nbytes, void gmx_unused *b, const t_commrec gmx_unused *cr)
264 {
265 #if !GMX_MPI
266     gmx_call("gmx_bast");
267 #else
268     MPI_Bcast(b, nbytes, MPI_BYTE, MASTERRANK(cr), cr->mpi_comm_mygroup);
269 #endif
270 }
271
272 void gmx_bcast_sim(int gmx_unused nbytes, void gmx_unused *b, const t_commrec gmx_unused *cr)
273 {
274 #if !GMX_MPI
275     gmx_call("gmx_bast");
276 #else
277     MPI_Bcast(b, nbytes, MPI_BYTE, MASTERRANK(cr), cr->mpi_comm_mysim);
278 #endif
279 }
280
281 void gmx_sumd(int gmx_unused nr, double gmx_unused r[], const t_commrec gmx_unused *cr)
282 {
283 #if !GMX_MPI
284     gmx_call("gmx_sumd");
285 #else
286 #if MPI_IN_PLACE_EXISTS
287     if (cr->nc.bUse)
288     {
289         if (cr->nc.rank_intra == 0)
290         {
291             /* Use two step summing. */
292             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM, 0,
293                        cr->nc.comm_intra);
294             /* Sum the roots of the internal (intra) buffers. */
295             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM,
296                           cr->nc.comm_inter);
297         }
298         else
299         {
300             /* This is here because of the silly MPI specification
301                 that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
302             MPI_Reduce(r, nullptr, nr, MPI_DOUBLE, MPI_SUM, 0, cr->nc.comm_intra);
303         }
304         MPI_Bcast(r, nr, MPI_DOUBLE, 0, cr->nc.comm_intra);
305     }
306     else
307     {
308         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM,
309                       cr->mpi_comm_mygroup);
310     }
311 #else
312     int i;
313
314     if (nr > cr->mpb->dbuf_alloc)
315     {
316         cr->mpb->dbuf_alloc = nr;
317         srenew(cr->mpb->dbuf, cr->mpb->dbuf_alloc);
318     }
319     if (cr->nc.bUse)
320     {
321         /* Use two step summing */
322         MPI_Allreduce(r, cr->mpb->dbuf, nr, MPI_DOUBLE, MPI_SUM, cr->nc.comm_intra);
323         if (cr->nc.rank_intra == 0)
324         {
325             /* Sum with the buffers reversed */
326             MPI_Allreduce(cr->mpb->dbuf, r, nr, MPI_DOUBLE, MPI_SUM,
327                           cr->nc.comm_inter);
328         }
329         MPI_Bcast(r, nr, MPI_DOUBLE, 0, cr->nc.comm_intra);
330     }
331     else
332     {
333         MPI_Allreduce(r, cr->mpb->dbuf, nr, MPI_DOUBLE, MPI_SUM,
334                       cr->mpi_comm_mygroup);
335         for (i = 0; i < nr; i++)
336         {
337             r[i] = cr->mpb->dbuf[i];
338         }
339     }
340 #endif
341 #endif
342 }
343
344 void gmx_sumf(int gmx_unused nr, float gmx_unused r[], const t_commrec gmx_unused *cr)
345 {
346 #if !GMX_MPI
347     gmx_call("gmx_sumf");
348 #else
349 #if MPI_IN_PLACE_EXISTS
350     if (cr->nc.bUse)
351     {
352         /* Use two step summing.  */
353         if (cr->nc.rank_intra == 0)
354         {
355             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM, 0,
356                        cr->nc.comm_intra);
357             /* Sum the roots of the internal (intra) buffers */
358             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM,
359                           cr->nc.comm_inter);
360         }
361         else
362         {
363             /* This is here because of the silly MPI specification
364                 that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
365             MPI_Reduce(r, nullptr, nr, MPI_FLOAT, MPI_SUM, 0, cr->nc.comm_intra);
366         }
367         MPI_Bcast(r, nr, MPI_FLOAT, 0, cr->nc.comm_intra);
368     }
369     else
370     {
371         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM, cr->mpi_comm_mygroup);
372     }
373 #else
374     int i;
375
376     if (nr > cr->mpb->fbuf_alloc)
377     {
378         cr->mpb->fbuf_alloc = nr;
379         srenew(cr->mpb->fbuf, cr->mpb->fbuf_alloc);
380     }
381     if (cr->nc.bUse)
382     {
383         /* Use two step summing */
384         MPI_Allreduce(r, cr->mpb->fbuf, nr, MPI_FLOAT, MPI_SUM, cr->nc.comm_intra);
385         if (cr->nc.rank_intra == 0)
386         {
387             /* Sum with the buffers reversed */
388             MPI_Allreduce(cr->mpb->fbuf, r, nr, MPI_FLOAT, MPI_SUM,
389                           cr->nc.comm_inter);
390         }
391         MPI_Bcast(r, nr, MPI_FLOAT, 0, cr->nc.comm_intra);
392     }
393     else
394     {
395         MPI_Allreduce(r, cr->mpb->fbuf, nr, MPI_FLOAT, MPI_SUM,
396                       cr->mpi_comm_mygroup);
397         for (i = 0; i < nr; i++)
398         {
399             r[i] = cr->mpb->fbuf[i];
400         }
401     }
402 #endif
403 #endif
404 }
405
406 void gmx_sumi(int gmx_unused nr, int gmx_unused r[], const t_commrec gmx_unused *cr)
407 {
408 #if !GMX_MPI
409     gmx_call("gmx_sumi");
410 #else
411 #if MPI_IN_PLACE_EXISTS
412     if (cr->nc.bUse)
413     {
414         /* Use two step summing */
415         if (cr->nc.rank_intra == 0)
416         {
417             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, 0, cr->nc.comm_intra);
418             /* Sum with the buffers reversed */
419             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, cr->nc.comm_inter);
420         }
421         else
422         {
423             /* This is here because of the silly MPI specification
424                 that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
425             MPI_Reduce(r, nullptr, nr, MPI_INT, MPI_SUM, 0, cr->nc.comm_intra);
426         }
427         MPI_Bcast(r, nr, MPI_INT, 0, cr->nc.comm_intra);
428     }
429     else
430     {
431         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, cr->mpi_comm_mygroup);
432     }
433 #else
434     int i;
435
436     if (nr > cr->mpb->ibuf_alloc)
437     {
438         cr->mpb->ibuf_alloc = nr;
439         srenew(cr->mpb->ibuf, cr->mpb->ibuf_alloc);
440     }
441     if (cr->nc.bUse)
442     {
443         /* Use two step summing */
444         MPI_Allreduce(r, cr->mpb->ibuf, nr, MPI_INT, MPI_SUM, cr->nc.comm_intra);
445         if (cr->nc.rank_intra == 0)
446         {
447             /* Sum with the buffers reversed */
448             MPI_Allreduce(cr->mpb->ibuf, r, nr, MPI_INT, MPI_SUM, cr->nc.comm_inter);
449         }
450         MPI_Bcast(r, nr, MPI_INT, 0, cr->nc.comm_intra);
451     }
452     else
453     {
454         MPI_Allreduce(r, cr->mpb->ibuf, nr, MPI_INT, MPI_SUM, cr->mpi_comm_mygroup);
455         for (i = 0; i < nr; i++)
456         {
457             r[i] = cr->mpb->ibuf[i];
458         }
459     }
460 #endif
461 #endif
462 }
463
464 void gmx_sumli(int gmx_unused nr, int64_t gmx_unused r[], const t_commrec gmx_unused *cr)
465 {
466 #if !GMX_MPI
467     gmx_call("gmx_sumli");
468 #else
469 #if MPI_IN_PLACE_EXISTS
470     if (cr->nc.bUse)
471     {
472         /* Use two step summing */
473         if (cr->nc.rank_intra == 0)
474         {
475             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM, 0,
476                        cr->nc.comm_intra);
477             /* Sum with the buffers reversed */
478             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM,
479                           cr->nc.comm_inter);
480         }
481         else
482         {
483             /* This is here because of the silly MPI specification
484                 that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
485             MPI_Reduce(r, nullptr, nr, MPI_INT64_T, MPI_SUM, 0, cr->nc.comm_intra);
486         }
487         MPI_Bcast(r, nr, MPI_INT64_T, 0, cr->nc.comm_intra);
488     }
489     else
490     {
491         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM, cr->mpi_comm_mygroup);
492     }
493 #else
494     int i;
495
496     if (nr > cr->mpb->libuf_alloc)
497     {
498         cr->mpb->libuf_alloc = nr;
499         srenew(cr->mpb->libuf, cr->mpb->libuf_alloc);
500     }
501     if (cr->nc.bUse)
502     {
503         /* Use two step summing */
504         MPI_Allreduce(r, cr->mpb->libuf, nr, MPI_INT64_T, MPI_SUM,
505                       cr->nc.comm_intra);
506         if (cr->nc.rank_intra == 0)
507         {
508             /* Sum with the buffers reversed */
509             MPI_Allreduce(cr->mpb->libuf, r, nr, MPI_INT64_T, MPI_SUM,
510                           cr->nc.comm_inter);
511         }
512         MPI_Bcast(r, nr, MPI_INT64_T, 0, cr->nc.comm_intra);
513     }
514     else
515     {
516         MPI_Allreduce(r, cr->mpb->libuf, nr, MPI_INT64_T, MPI_SUM,
517                       cr->mpi_comm_mygroup);
518         for (i = 0; i < nr; i++)
519         {
520             r[i] = cr->mpb->libuf[i];
521         }
522     }
523 #endif
524 #endif
525 }
526
527
528
529 #if GMX_MPI
530 static void gmx_sumd_comm(int nr, double r[], MPI_Comm mpi_comm)
531 {
532 #if MPI_IN_PLACE_EXISTS
533     MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM, mpi_comm);
534 #else
535     /* this function is only used in code that is not performance critical,
536        (during setup, when comm_rec is not the appropriate communication
537        structure), so this isn't as bad as it looks. */
538     double *buf;
539     int     i;
540
541     snew(buf, nr);
542     MPI_Allreduce(r, buf, nr, MPI_DOUBLE, MPI_SUM, mpi_comm);
543     for (i = 0; i < nr; i++)
544     {
545         r[i] = buf[i];
546     }
547     sfree(buf);
548 #endif
549 }
550 #endif
551
552 #if GMX_MPI
553 static void gmx_sumf_comm(int nr, float r[], MPI_Comm mpi_comm)
554 {
555 #if MPI_IN_PLACE_EXISTS
556     MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM, mpi_comm);
557 #else
558     /* this function is only used in code that is not performance critical,
559        (during setup, when comm_rec is not the appropriate communication
560        structure), so this isn't as bad as it looks. */
561     float *buf;
562     int    i;
563
564     snew(buf, nr);
565     MPI_Allreduce(r, buf, nr, MPI_FLOAT, MPI_SUM, mpi_comm);
566     for (i = 0; i < nr; i++)
567     {
568         r[i] = buf[i];
569     }
570     sfree(buf);
571 #endif
572 }
573 #endif
574
575 void gmx_sumd_sim(int gmx_unused nr, double gmx_unused r[], const gmx_multisim_t gmx_unused *ms)
576 {
577 #if !GMX_MPI
578     gmx_call("gmx_sumd_sim");
579 #else
580     gmx_sumd_comm(nr, r, ms->mpi_comm_masters);
581 #endif
582 }
583
584 void gmx_sumf_sim(int gmx_unused nr, float gmx_unused r[], const gmx_multisim_t gmx_unused *ms)
585 {
586 #if !GMX_MPI
587     gmx_call("gmx_sumf_sim");
588 #else
589     gmx_sumf_comm(nr, r, ms->mpi_comm_masters);
590 #endif
591 }
592
593 void gmx_sumi_sim(int gmx_unused nr, int gmx_unused r[], const gmx_multisim_t gmx_unused *ms)
594 {
595 #if !GMX_MPI
596     gmx_call("gmx_sumi_sim");
597 #else
598 #if MPI_IN_PLACE_EXISTS
599     MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, ms->mpi_comm_masters);
600 #else
601     /* this is thread-unsafe, but it will do for now: */
602     int i;
603
604     if (nr > ms->mpb->ibuf_alloc)
605     {
606         ms->mpb->ibuf_alloc = nr;
607         srenew(ms->mpb->ibuf, ms->mpb->ibuf_alloc);
608     }
609     MPI_Allreduce(r, ms->mpb->ibuf, nr, MPI_INT, MPI_SUM, ms->mpi_comm_masters);
610     for (i = 0; i < nr; i++)
611     {
612         r[i] = ms->mpb->ibuf[i];
613     }
614 #endif
615 #endif
616 }
617
618 void gmx_sumli_sim(int gmx_unused nr, int64_t gmx_unused r[], const gmx_multisim_t gmx_unused *ms)
619 {
620 #if !GMX_MPI
621     gmx_call("gmx_sumli_sim");
622 #else
623 #if MPI_IN_PLACE_EXISTS
624     MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM,
625                   ms->mpi_comm_masters);
626 #else
627     /* this is thread-unsafe, but it will do for now: */
628     int i;
629
630     if (nr > ms->mpb->libuf_alloc)
631     {
632         ms->mpb->libuf_alloc = nr;
633         srenew(ms->mpb->libuf, ms->mpb->libuf_alloc);
634     }
635     MPI_Allreduce(r, ms->mpb->libuf, nr, MPI_INT64_T, MPI_SUM,
636                   ms->mpi_comm_masters);
637     for (i = 0; i < nr; i++)
638     {
639         r[i] = ms->mpb->libuf[i];
640     }
641 #endif
642 #endif
643 }
644
645 void check_multi_int(FILE *log, const gmx_multisim_t *ms, int val,
646                      const char *name,
647                      gmx_bool bQuiet)
648 {
649     int     *ibuf, p;
650     gmx_bool bCompatible;
651
652     if (nullptr != log && !bQuiet)
653     {
654         fprintf(log, "Multi-checking %s ... ", name);
655     }
656
657     if (ms == nullptr)
658     {
659         gmx_fatal(FARGS,
660                   "check_multi_int called with a NULL communication pointer");
661     }
662
663     snew(ibuf, ms->nsim);
664     ibuf[ms->sim] = val;
665     gmx_sumi_sim(ms->nsim, ibuf, ms);
666
667     bCompatible = TRUE;
668     for (p = 1; p < ms->nsim; p++)
669     {
670         bCompatible = bCompatible && (ibuf[p-1] == ibuf[p]);
671     }
672
673     if (bCompatible)
674     {
675         if (nullptr != log && !bQuiet)
676         {
677             fprintf(log, "OK\n");
678         }
679     }
680     else
681     {
682         if (nullptr != log)
683         {
684             fprintf(log, "\n%s is not equal for all subsystems\n", name);
685             for (p = 0; p < ms->nsim; p++)
686             {
687                 fprintf(log, "  subsystem %d: %d\n", p, ibuf[p]);
688             }
689         }
690         gmx_fatal(FARGS, "The %d subsystems are not compatible\n", ms->nsim);
691     }
692
693     sfree(ibuf);
694 }
695
696 void check_multi_int64(FILE *log, const gmx_multisim_t *ms,
697                        int64_t val, const char *name,
698                        gmx_bool bQuiet)
699 {
700     int64_t          *ibuf;
701     int               p;
702     gmx_bool          bCompatible;
703
704     if (nullptr != log && !bQuiet)
705     {
706         fprintf(log, "Multi-checking %s ... ", name);
707     }
708
709     if (ms == nullptr)
710     {
711         gmx_fatal(FARGS,
712                   "check_multi_int called with a NULL communication pointer");
713     }
714
715     snew(ibuf, ms->nsim);
716     ibuf[ms->sim] = val;
717     gmx_sumli_sim(ms->nsim, ibuf, ms);
718
719     bCompatible = TRUE;
720     for (p = 1; p < ms->nsim; p++)
721     {
722         bCompatible = bCompatible && (ibuf[p-1] == ibuf[p]);
723     }
724
725     if (bCompatible)
726     {
727         if (nullptr != log && !bQuiet)
728         {
729             fprintf(log, "OK\n");
730         }
731     }
732     else
733     {
734         // TODO Part of this error message would also be good to go to
735         // stderr (from one rank of one sim only)
736         if (nullptr != log)
737         {
738             fprintf(log, "\n%s is not equal for all subsystems\n", name);
739             for (p = 0; p < ms->nsim; p++)
740             {
741                 char strbuf[255];
742                 /* first make the format string */
743                 snprintf(strbuf, 255, "  subsystem %%d: %s\n",
744                          "%" PRId64);
745                 fprintf(log, strbuf, p, ibuf[p]);
746             }
747         }
748         gmx_fatal(FARGS, "The %d subsystems are not compatible\n", ms->nsim);
749     }
750
751     sfree(ibuf);
752 }
753
754 const char *opt2fn_master(const char *opt, int nfile, const t_filenm fnm[],
755                           t_commrec *cr)
756 {
757     return SIMMASTER(cr) ? opt2fn(opt, nfile, fnm) : nullptr;
758 }
759
760 void gmx_fatal_collective(int f_errno, const char *file, int line,
761                           MPI_Comm comm, gmx_bool bMaster,
762                           gmx_fmtstr const char *fmt, ...)
763 {
764     va_list  ap;
765     gmx_bool bFinalize;
766 #if GMX_MPI
767     int      result;
768     /* Check if we are calling on all processes in MPI_COMM_WORLD */
769     MPI_Comm_compare(comm, MPI_COMM_WORLD, &result);
770     /* Any result except MPI_UNEQUAL allows us to call MPI_Finalize */
771     bFinalize = (result != MPI_UNEQUAL);
772 #else
773     GMX_UNUSED_VALUE(comm);
774     bFinalize = TRUE;
775 #endif
776
777     va_start(ap, fmt);
778     gmx_fatal_mpi_va(f_errno, file, line, bMaster, bFinalize, fmt, ap);
779     va_end(ap);
780 }