enabled 1 PP + 1 PME node
[alexxy/gromacs.git] / src / gromacs / domdec / domdec_network.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2008,2009,2010,2012,2013,2014,2015, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS 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 GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35
36 /*! \internal \file
37  *
38  * \brief This file defines functions for (mostly) the domdec module
39  * to use MPI functionality
40  *
41  * \author Berk Hess <hess@kth.se>
42  * \ingroup module_domdec
43  */
44
45 #include "gmxpre.h"
46
47 #include "domdec_network.h"
48
49 #include "config.h"
50
51 #include <string.h>
52
53 #include "gromacs/legacyheaders/types/commrec.h"
54 #include "gromacs/utility/gmxmpi.h"
55
56
57 /*! \brief Returns the MPI rank of the domain decomposition master rank */
58 #define DDMASTERRANK(dd)   (dd->masterrank)
59
60
61 void dd_sendrecv_int(const gmx_domdec_t gmx_unused *dd,
62                      int gmx_unused ddimind, int gmx_unused direction,
63                      int gmx_unused *buf_s, int gmx_unused n_s,
64                      int gmx_unused *buf_r, int gmx_unused n_r)
65 {
66 #ifdef GMX_MPI
67     int        rank_s, rank_r;
68     MPI_Status stat;
69
70     rank_s = dd->neighbor[ddimind][direction == dddirForward ? 0 : 1];
71     rank_r = dd->neighbor[ddimind][direction == dddirForward ? 1 : 0];
72
73     if (n_s && n_r)
74     {
75         MPI_Sendrecv(buf_s, n_s*sizeof(int), MPI_BYTE, rank_s, 0,
76                      buf_r, n_r*sizeof(int), MPI_BYTE, rank_r, 0,
77                      dd->mpi_comm_all, &stat);
78     }
79     else if (n_s)
80     {
81         MPI_Send(    buf_s, n_s*sizeof(int), MPI_BYTE, rank_s, 0,
82                      dd->mpi_comm_all);
83     }
84     else if (n_r)
85     {
86         MPI_Recv(    buf_r, n_r*sizeof(int), MPI_BYTE, rank_r, 0,
87                      dd->mpi_comm_all, &stat);
88     }
89
90 #endif
91 }
92
93 void dd_sendrecv_real(const gmx_domdec_t gmx_unused *dd,
94                       int gmx_unused ddimind, int gmx_unused direction,
95                       real gmx_unused *buf_s, int gmx_unused n_s,
96                       real gmx_unused *buf_r, int gmx_unused n_r)
97 {
98 #ifdef GMX_MPI
99     int        rank_s, rank_r;
100     MPI_Status stat;
101
102     rank_s = dd->neighbor[ddimind][direction == dddirForward ? 0 : 1];
103     rank_r = dd->neighbor[ddimind][direction == dddirForward ? 1 : 0];
104
105     if (n_s && n_r)
106     {
107         MPI_Sendrecv(buf_s, n_s*sizeof(real), MPI_BYTE, rank_s, 0,
108                      buf_r, n_r*sizeof(real), MPI_BYTE, rank_r, 0,
109                      dd->mpi_comm_all, &stat);
110     }
111     else if (n_s)
112     {
113         MPI_Send(    buf_s, n_s*sizeof(real), MPI_BYTE, rank_s, 0,
114                      dd->mpi_comm_all);
115     }
116     else if (n_r)
117     {
118         MPI_Recv(    buf_r, n_r*sizeof(real), MPI_BYTE, rank_r, 0,
119                      dd->mpi_comm_all, &stat);
120     }
121
122 #endif
123 }
124
125 void dd_sendrecv_rvec(const gmx_domdec_t gmx_unused *dd,
126                       int gmx_unused ddimind, int gmx_unused direction,
127                       rvec gmx_unused *buf_s, int gmx_unused n_s,
128                       rvec gmx_unused *buf_r, int gmx_unused n_r)
129 {
130 #ifdef GMX_MPI
131     int        rank_s, rank_r;
132     MPI_Status stat;
133
134     rank_s = dd->neighbor[ddimind][direction == dddirForward ? 0 : 1];
135     rank_r = dd->neighbor[ddimind][direction == dddirForward ? 1 : 0];
136
137     if (n_s && n_r)
138     {
139         MPI_Sendrecv(buf_s[0], n_s*sizeof(rvec), MPI_BYTE, rank_s, 0,
140                      buf_r[0], n_r*sizeof(rvec), MPI_BYTE, rank_r, 0,
141                      dd->mpi_comm_all, &stat);
142     }
143     else if (n_s)
144     {
145         MPI_Send(    buf_s[0], n_s*sizeof(rvec), MPI_BYTE, rank_s, 0,
146                      dd->mpi_comm_all);
147     }
148     else if (n_r)
149     {
150         MPI_Recv(    buf_r[0], n_r*sizeof(rvec), MPI_BYTE, rank_r, 0,
151                      dd->mpi_comm_all, &stat);
152     }
153
154 #endif
155 }
156
157 void dd_sendrecv2_rvec(const gmx_domdec_t gmx_unused *dd,
158                        int gmx_unused ddimind,
159                        rvec gmx_unused *buf_s_fw, int gmx_unused n_s_fw,
160                        rvec gmx_unused *buf_r_fw, int gmx_unused n_r_fw,
161                        rvec gmx_unused *buf_s_bw, int gmx_unused n_s_bw,
162                        rvec gmx_unused *buf_r_bw, int gmx_unused n_r_bw)
163 {
164 #ifdef GMX_MPI
165     int         rank_fw, rank_bw, nreq;
166     MPI_Request req[4];
167     MPI_Status  stat[4];
168
169     rank_fw = dd->neighbor[ddimind][0];
170     rank_bw = dd->neighbor[ddimind][1];
171
172     if (!dd->bSendRecv2)
173     {
174         /* Try to send and receive in two directions simultaneously.
175          * Should be faster, especially on machines
176          * with full 3D communication networks.
177          * However, it could be that communication libraries are
178          * optimized for MPI_Sendrecv and non-blocking MPI calls
179          * are slower.
180          * SendRecv2 can be turned on with the env.var. GMX_DD_SENDRECV2
181          */
182         nreq = 0;
183         if (n_r_fw)
184         {
185             MPI_Irecv(buf_r_fw[0], n_r_fw*sizeof(rvec), MPI_BYTE,
186                       rank_bw, 0, dd->mpi_comm_all, &req[nreq++]);
187         }
188         if (n_r_bw)
189         {
190             MPI_Irecv(buf_r_bw[0], n_r_bw*sizeof(rvec), MPI_BYTE,
191                       rank_fw, 1, dd->mpi_comm_all, &req[nreq++]);
192         }
193         if (n_s_fw)
194         {
195             MPI_Isend(buf_s_fw[0], n_s_fw*sizeof(rvec), MPI_BYTE,
196                       rank_fw, 0, dd->mpi_comm_all, &req[nreq++]);
197         }
198         if (n_s_bw)
199         {
200             MPI_Isend(buf_s_bw[0], n_s_bw*sizeof(rvec), MPI_BYTE,
201                       rank_bw, 1, dd->mpi_comm_all, &req[nreq++]);
202         }
203         if (nreq)
204         {
205             MPI_Waitall(nreq, req, stat);
206         }
207     }
208     else
209     {
210         /* Communicate in two ordered phases.
211          * This is slower, even on a dual-core Opteron cluster
212          * with a single full-duplex network connection per machine.
213          */
214         /* Forward */
215         MPI_Sendrecv(buf_s_fw[0], n_s_fw*sizeof(rvec), MPI_BYTE, rank_fw, 0,
216                      buf_r_fw[0], n_r_fw*sizeof(rvec), MPI_BYTE, rank_bw, 0,
217                      dd->mpi_comm_all, &stat[0]);
218         /* Backward */
219         MPI_Sendrecv(buf_s_bw[0], n_s_bw*sizeof(rvec), MPI_BYTE, rank_bw, 0,
220                      buf_r_bw[0], n_r_bw*sizeof(rvec), MPI_BYTE, rank_fw, 0,
221                      dd->mpi_comm_all, &stat[0]);
222     }
223 #endif
224 }
225
226 /* IBM's BlueGene(/L) MPI_Bcast dereferences the data pointer
227  * even when 0 == nbytes, so we protect calls to it on BlueGene.
228  * Fortunately dd_bcast() and dd_bcastc() are only
229  * called during DD setup and partition.
230  */
231
232 void dd_bcast(gmx_domdec_t gmx_unused *dd, int gmx_unused nbytes, void gmx_unused *data)
233 {
234 #ifdef GMX_MPI
235     if (dd->nnodes > 1)
236     {
237 #ifdef GMX_BLUEGENE
238         if (nbytes > 0)
239         {
240 #endif
241         MPI_Bcast(data, nbytes, MPI_BYTE,
242                   DDMASTERRANK(dd), dd->mpi_comm_all);
243 #ifdef GMX_BLUEGENE
244     }
245 #endif
246     }
247 #endif
248 }
249
250 void dd_bcastc(gmx_domdec_t *dd, int nbytes, void *src, void *dest)
251 {
252     if (DDMASTER(dd) || dd->nnodes == 1)
253     {
254         memcpy(dest, src, nbytes);
255     }
256 #ifdef GMX_MPI
257     if (dd->nnodes > 1)
258     {
259 #ifdef GMX_BLUEGENE
260         if (nbytes > 0)
261         {
262 #endif
263         MPI_Bcast(dest, nbytes, MPI_BYTE,
264                   DDMASTERRANK(dd), dd->mpi_comm_all);
265 #ifdef GMX_BLUEGENE
266     }
267 #endif
268     }
269 #endif
270 }
271
272 void dd_scatter(gmx_domdec_t gmx_unused *dd, int gmx_unused nbytes, void gmx_unused *src, void *dest)
273 {
274 #ifdef GMX_MPI
275     if (dd->nnodes > 1)
276     {
277         MPI_Scatter(src, nbytes, MPI_BYTE,
278                     dest, nbytes, MPI_BYTE,
279                     DDMASTERRANK(dd), dd->mpi_comm_all);
280     }
281     else
282 #endif
283     {
284         /* 1 rank, either we copy everything, or dest=src: nothing to do */
285         if (dest != src)
286         {
287             memcpy(dest, src, nbytes);
288         }
289     }
290 }
291
292 void dd_gather(gmx_domdec_t gmx_unused *dd, int gmx_unused nbytes, void gmx_unused *src, void gmx_unused *dest)
293 {
294 #ifdef GMX_MPI
295     MPI_Gather(src, nbytes, MPI_BYTE,
296                dest, nbytes, MPI_BYTE,
297                DDMASTERRANK(dd), dd->mpi_comm_all);
298 #endif
299 }
300
301 void dd_scatterv(gmx_domdec_t gmx_unused *dd,
302                  int gmx_unused *scounts, int gmx_unused *disps, void *sbuf,
303                  int rcount, void *rbuf)
304 {
305 #ifdef GMX_MPI
306     int dum;
307
308     if (dd->nnodes > 1)
309     {
310         if (rcount == 0)
311         {
312             /* MPI does not allow NULL pointers */
313             rbuf = &dum;
314         }
315         MPI_Scatterv(sbuf, scounts, disps, MPI_BYTE,
316                      rbuf, rcount, MPI_BYTE,
317                      DDMASTERRANK(dd), dd->mpi_comm_all);
318     }
319     else
320 #endif
321     {
322         /* 1 rank, either we copy everything, or rbuf=sbuf: nothing to do */
323         if (rbuf != sbuf)
324         {
325             memcpy(rbuf, sbuf, rcount);
326         }
327     }
328 }
329
330 void dd_gatherv(gmx_domdec_t gmx_unused *dd,
331                 int gmx_unused scount, void gmx_unused *sbuf,
332                 int gmx_unused *rcounts, int gmx_unused *disps, void gmx_unused *rbuf)
333 {
334 #ifdef GMX_MPI
335     int dum;
336
337     if (scount == 0)
338     {
339         /* MPI does not allow NULL pointers */
340         sbuf = &dum;
341     }
342     MPI_Gatherv(sbuf, scount, MPI_BYTE,
343                 rbuf, rcounts, disps, MPI_BYTE,
344                 DDMASTERRANK(dd), dd->mpi_comm_all);
345 #endif
346 }