c5edd4eb51eea884d5dc6f9ff190f4a0a5abae60
[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 by the GROMACS development team.
7  * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 #include "gmxpre.h"
39
40 #include "network.h"
41
42 #include "config.h"
43
44 #include <cctype>
45 #include <cstdarg>
46 #include <cstdlib>
47 #include <cstring>
48
49 #include "gromacs/commandline/filenm.h"
50 #include "gromacs/mdtypes/commrec.h"
51 #include "gromacs/utility/basenetwork.h"
52 #include "gromacs/utility/cstringutil.h"
53 #include "gromacs/utility/fatalerror.h"
54 #include "gromacs/utility/futil.h"
55 #include "gromacs/utility/gmxassert.h"
56 #include "gromacs/utility/gmxmpi.h"
57 #include "gromacs/utility/real.h"
58 #include "gromacs/utility/smalloc.h"
59
60 /* The source code in this file should be thread-safe.
61       Please keep it that way. */
62
63 CommrecHandle init_commrec(MPI_Comm communicator)
64 {
65     CommrecHandle handle;
66     t_commrec*    cr;
67
68     snew(cr, 1);
69     handle.reset(cr);
70
71     int rankInCommunicator, sizeOfCommunicator;
72 #if GMX_MPI
73 #    if GMX_LIB_MPI
74     GMX_RELEASE_ASSERT(gmx_mpi_initialized(), "Must have initialized MPI before building commrec");
75 #    endif
76     MPI_Comm_rank(communicator, &rankInCommunicator);
77     MPI_Comm_size(communicator, &sizeOfCommunicator);
78 #else
79     GMX_UNUSED_VALUE(communicator);
80     rankInCommunicator = 0;
81     sizeOfCommunicator = 1;
82 #endif
83
84     cr->mpiDefaultCommunicator    = communicator;
85     cr->sizeOfDefaultCommunicator = sizeOfCommunicator;
86     cr->rankInDefaultCommunicator = rankInCommunicator;
87
88     // For now, we want things to go horribly wrong if this is used too early...
89     // TODO: Remove when communicators are removed from commrec (#2395)
90     cr->nnodes                    = -1;
91     cr->sizeOfMyGroupCommunicator = -1;
92     cr->nodeid                    = -1;
93     cr->sim_nodeid                = -1;
94     cr->mpi_comm_mysim            = MPI_COMM_NULL;
95     cr->mpi_comm_mygroup          = MPI_COMM_NULL;
96
97     // TODO cr->duty should not be initialized here
98     cr->duty = (DUTY_PP | DUTY_PME);
99
100     return handle;
101 }
102
103 void done_commrec(t_commrec* cr)
104 {
105     if (MASTER(cr))
106     {
107         if (nullptr != cr->dd)
108         {
109             // TODO: implement
110             // done_domdec(cr->dd);
111         }
112     }
113 #if GMX_MPI
114     // TODO We need to be able to free communicators, but the
115     // structure of the commrec and domdec initialization code makes
116     // it hard to avoid both leaks and double frees.
117     bool mySimIsMyGroup = (cr->mpi_comm_mysim == cr->mpi_comm_mygroup);
118     if (cr->mpi_comm_mysim != MPI_COMM_NULL && cr->mpi_comm_mysim != MPI_COMM_WORLD)
119     {
120         // TODO see above
121         // MPI_Comm_free(&cr->mpi_comm_mysim);
122     }
123     if (!mySimIsMyGroup && cr->mpi_comm_mygroup != MPI_COMM_NULL && cr->mpi_comm_mygroup != MPI_COMM_WORLD)
124     {
125         // TODO see above
126         // MPI_Comm_free(&cr->mpi_comm_mygroup);
127     }
128 #endif
129     sfree(cr);
130 }
131
132 void gmx_setup_nodecomm(FILE gmx_unused* fplog, t_commrec* cr)
133 {
134     gmx_nodecomm_t* nc;
135
136     /* Many MPI implementations do not optimize MPI_Allreduce
137      * (and probably also other global communication calls)
138      * for multi-core nodes connected by a network.
139      * We can optimize such communication by using one MPI call
140      * within each node and one between the nodes.
141      * For MVAPICH2 and Intel MPI this reduces the time for
142      * the global_stat communication by 25%
143      * for 2x2-core 3 GHz Woodcrest connected by mixed DDR/SDR Infiniband.
144      * B. Hess, November 2007
145      */
146
147     nc = &cr->nc;
148
149     nc->bUse = FALSE;
150 #if !GMX_THREAD_MPI
151 #    if GMX_MPI
152     int n, rank;
153
154     // TODO PhysicalNodeCommunicator could be extended/used to handle
155     // the need for per-node per-group communicators.
156     MPI_Comm_size(cr->mpi_comm_mygroup, &n);
157     MPI_Comm_rank(cr->mpi_comm_mygroup, &rank);
158
159     int nodehash = gmx_physicalnode_id_hash();
160
161     if (debug)
162     {
163         fprintf(debug, "In gmx_setup_nodecomm: splitting communicator of size %d\n", n);
164     }
165
166
167     /* The intra-node communicator, split on node number */
168     MPI_Comm_split(cr->mpi_comm_mygroup, nodehash, rank, &nc->comm_intra);
169     MPI_Comm_rank(nc->comm_intra, &nc->rank_intra);
170     if (debug)
171     {
172         fprintf(debug, "In gmx_setup_nodecomm: node ID %d rank within node %d\n", rank, nc->rank_intra);
173     }
174     /* The inter-node communicator, split on rank_intra.
175      * We actually only need the one for rank=0,
176      * but it is easier to create them all.
177      */
178     MPI_Comm_split(cr->mpi_comm_mygroup, nc->rank_intra, rank, &nc->comm_inter);
179     /* Check if this really created two step communication */
180     int ng, ni;
181
182     MPI_Comm_size(nc->comm_inter, &ng);
183     MPI_Comm_size(nc->comm_intra, &ni);
184     if (debug)
185     {
186         fprintf(debug, "In gmx_setup_nodecomm: groups %d, my group size %d\n", ng, ni);
187     }
188
189     if (getenv("GMX_NO_NODECOMM") == nullptr && ((ng > 1 && ng < n) || (ni > 1 && ni < n)))
190     {
191         nc->bUse = TRUE;
192         if (fplog)
193         {
194             fprintf(fplog,
195                     "Using two step summing over %d groups of on average %.1f ranks\n\n",
196                     ng,
197                     real(n) / real(ng));
198         }
199         if (nc->rank_intra > 0)
200         {
201             MPI_Comm_free(&nc->comm_inter);
202         }
203     }
204     else
205     {
206         /* One group or all processes in a separate group, use normal summing */
207         MPI_Comm_free(&nc->comm_inter);
208         MPI_Comm_free(&nc->comm_intra);
209         if (debug)
210         {
211             fprintf(debug,
212                     "In gmx_setup_nodecomm: not unsing separate inter- and intra-node "
213                     "communicators.\n");
214         }
215     }
216 #    endif
217 #else
218     /* tMPI runs only on a single node so just use the nodeid */
219     nc->rank_intra = cr->nodeid;
220 #endif
221 }
222
223 void gmx_barrier(MPI_Comm gmx_unused communicator)
224 {
225     if (communicator == MPI_COMM_NULL)
226     {
227         return;
228     }
229 #if !GMX_MPI
230     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_barrier");
231 #else
232     MPI_Barrier(communicator);
233 #endif
234 }
235
236 void gmx_bcast(int gmx_unused nbytes, void gmx_unused* b, MPI_Comm gmx_unused communicator)
237 {
238 #if !GMX_MPI
239     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_bcast");
240 #else
241     MPI_Bcast(b, nbytes, MPI_BYTE, 0, communicator);
242 #endif
243 }
244
245 void gmx_sumd(int gmx_unused nr, double gmx_unused r[], const t_commrec gmx_unused* cr)
246 {
247     if (cr->sizeOfMyGroupCommunicator == 1)
248     {
249         return;
250     }
251
252 #if !GMX_MPI
253     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumd");
254 #else
255     if (cr->nc.bUse)
256     {
257         if (cr->nc.rank_intra == 0)
258         {
259             /* Use two step summing. */
260             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM, 0, cr->nc.comm_intra);
261             /* Sum the roots of the internal (intra) buffers. */
262             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM, cr->nc.comm_inter);
263         }
264         else
265         {
266             /* This is here because of the silly MPI specification
267                 that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
268             MPI_Reduce(r, nullptr, nr, MPI_DOUBLE, MPI_SUM, 0, cr->nc.comm_intra);
269         }
270         MPI_Bcast(r, nr, MPI_DOUBLE, 0, cr->nc.comm_intra);
271     }
272     else
273     {
274         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_DOUBLE, MPI_SUM, cr->mpi_comm_mygroup);
275     }
276 #endif
277 }
278
279 void gmx_sumf(int gmx_unused nr, float gmx_unused r[], const t_commrec gmx_unused* cr)
280 {
281     if (cr->sizeOfMyGroupCommunicator == 1)
282     {
283         return;
284     }
285
286 #if !GMX_MPI
287     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumf");
288 #else
289     if (cr->nc.bUse)
290     {
291         /* Use two step summing.  */
292         if (cr->nc.rank_intra == 0)
293         {
294             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM, 0, cr->nc.comm_intra);
295             /* Sum the roots of the internal (intra) buffers */
296             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM, 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_FLOAT, MPI_SUM, 0, cr->nc.comm_intra);
303         }
304         MPI_Bcast(r, nr, MPI_FLOAT, 0, cr->nc.comm_intra);
305     }
306     else
307     {
308         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_FLOAT, MPI_SUM, cr->mpi_comm_mygroup);
309     }
310 #endif
311 }
312
313 void gmx_sumi(int gmx_unused nr, int gmx_unused r[], const t_commrec gmx_unused* cr)
314 {
315     if (cr->sizeOfMyGroupCommunicator == 1)
316     {
317         return;
318     }
319
320 #if !GMX_MPI
321     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumi");
322 #else
323     if (cr->nc.bUse)
324     {
325         /* Use two step summing */
326         if (cr->nc.rank_intra == 0)
327         {
328             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, 0, cr->nc.comm_intra);
329             /* Sum with the buffers reversed */
330             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, cr->nc.comm_inter);
331         }
332         else
333         {
334             /* This is here because of the silly MPI specification
335                 that MPI_IN_PLACE should be put in sendbuf instead of recvbuf */
336             MPI_Reduce(r, nullptr, nr, MPI_INT, MPI_SUM, 0, cr->nc.comm_intra);
337         }
338         MPI_Bcast(r, nr, MPI_INT, 0, cr->nc.comm_intra);
339     }
340     else
341     {
342         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT, MPI_SUM, cr->mpi_comm_mygroup);
343     }
344 #endif
345 }
346
347 void gmx_sumli(int gmx_unused nr, int64_t gmx_unused r[], const t_commrec gmx_unused* cr)
348 {
349 #if !GMX_MPI
350     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumli");
351 #else
352     if (cr->nc.bUse)
353     {
354         /* Use two step summing */
355         if (cr->nc.rank_intra == 0)
356         {
357             MPI_Reduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM, 0, cr->nc.comm_intra);
358             /* Sum with the buffers reversed */
359             MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM, 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_INT64_T, MPI_SUM, 0, cr->nc.comm_intra);
366         }
367         MPI_Bcast(r, nr, MPI_INT64_T, 0, cr->nc.comm_intra);
368     }
369     else
370     {
371         MPI_Allreduce(MPI_IN_PLACE, r, nr, MPI_INT64_T, MPI_SUM, cr->mpi_comm_mygroup);
372     }
373 #endif
374 }
375
376 const char* opt2fn_master(const char* opt, int nfile, const t_filenm fnm[], t_commrec* cr)
377 {
378     return SIMMASTER(cr) ? opt2fn(opt, nfile, fnm) : nullptr;
379 }
380
381 void gmx_fatal_collective(int                    f_errno,
382                           const char*            file,
383                           int                    line,
384                           MPI_Comm               comm,
385                           gmx_bool               bMaster,
386                           gmx_fmtstr const char* fmt,
387                           ...)
388 {
389     va_list  ap;
390     gmx_bool bFinalize;
391 #if GMX_MPI
392     int result;
393     /* Check if we are calling on all processes in MPI_COMM_WORLD */
394     MPI_Comm_compare(comm, MPI_COMM_WORLD, &result);
395     /* Any result except MPI_UNEQUAL allows us to call MPI_Finalize */
396     bFinalize = (result != MPI_UNEQUAL);
397 #else
398     GMX_UNUSED_VALUE(comm);
399     bFinalize = TRUE;
400 #endif
401
402     va_start(ap, fmt);
403     gmx_fatal_mpi_va(f_errno, file, line, bMaster, bFinalize, fmt, ap);
404     va_end(ap);
405 }