149c8d74eac265ad1c983c3c5b2799ae2c0c7752
[alexxy/gromacs.git] / src / gromacs / ewald / pme_only.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,2019, 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 /* IMPORTANT FOR DEVELOPERS:
38  *
39  * Triclinic pme stuff isn't entirely trivial, and we've experienced
40  * some bugs during development (many of them due to me). To avoid
41  * this in the future, please check the following things if you make
42  * changes in this file:
43  *
44  * 1. You should obtain identical (at least to the PME precision)
45  *    energies, forces, and virial for
46  *    a rectangular box and a triclinic one where the z (or y) axis is
47  *    tilted a whole box side. For instance you could use these boxes:
48  *
49  *    rectangular       triclinic
50  *     2  0  0           2  0  0
51  *     0  2  0           0  2  0
52  *     0  0  6           2  2  6
53  *
54  * 2. You should check the energy conservation in a triclinic box.
55  *
56  * It might seem an overkill, but better safe than sorry.
57  * /Erik 001109
58  */
59
60 #include "gmxpre.h"
61
62 #include "config.h"
63
64 #include <cassert>
65 #include <cmath>
66 #include <cstdio>
67 #include <cstdlib>
68 #include <cstring>
69
70 #include <memory>
71 #include <numeric>
72 #include <vector>
73
74 #include "gromacs/domdec/domdec.h"
75 #include "gromacs/ewald/pme.h"
76 #include "gromacs/ewald/pme_coordinate_receiver_gpu.h"
77 #include "gromacs/ewald/pme_force_sender_gpu.h"
78 #include "gromacs/fft/parallel_3dfft.h"
79 #include "gromacs/fileio/pdbio.h"
80 #include "gromacs/gmxlib/network.h"
81 #include "gromacs/gmxlib/nrnb.h"
82 #include "gromacs/gpu_utils/hostallocator.h"
83 #include "gromacs/math/gmxcomplex.h"
84 #include "gromacs/math/units.h"
85 #include "gromacs/math/vec.h"
86 #include "gromacs/mdtypes/commrec.h"
87 #include "gromacs/mdtypes/forceoutput.h"
88 #include "gromacs/mdtypes/inputrec.h"
89 #include "gromacs/mdtypes/state_propagator_data_gpu.h"
90 #include "gromacs/timing/cyclecounter.h"
91 #include "gromacs/timing/wallcycle.h"
92 #include "gromacs/utility/fatalerror.h"
93 #include "gromacs/utility/futil.h"
94 #include "gromacs/utility/gmxmpi.h"
95 #include "gromacs/utility/gmxomp.h"
96 #include "gromacs/utility/smalloc.h"
97
98 #include "pme_gpu_internal.h"
99 #include "pme_internal.h"
100 #include "pme_pp_communication.h"
101
102 /*! \brief environment variable to enable GPU P2P communication */
103 static const bool c_enableGpuPmePpComms = (getenv("GMX_GPU_PME_PP_COMMS") != nullptr)
104     && GMX_THREAD_MPI && (GMX_GPU == GMX_GPU_CUDA);
105
106 /*! \brief Master PP-PME communication data structure */
107 struct gmx_pme_pp {
108     MPI_Comm             mpi_comm_mysim; /**< MPI communicator for this simulation */
109     std::vector<PpRanks> ppRanks;        /**< The PP partner ranks                 */
110     int                  peerRankId;     /**< The peer PP rank id                  */
111     //@{
112     /**< Vectors of A- and B-state parameters used to transfer vectors to PME ranks  */
113     gmx::PaddedHostVector<real> chargeA;
114     std::vector<real>           chargeB;
115     std::vector<real>           sqrt_c6A;
116     std::vector<real>           sqrt_c6B;
117     std::vector<real>           sigmaA;
118     std::vector<real>           sigmaB;
119     //@}
120     gmx::HostVector<gmx::RVec>  x; /**< Vector of atom coordinates to transfer to PME ranks */
121     std::vector<gmx::RVec>      f; /**< Vector of atom forces received from PME ranks */
122     //@{
123     /**< Vectors of MPI objects used in non-blocking communication between multiple PP ranks per PME rank */
124     std::vector<MPI_Request> req;
125     std::vector<MPI_Status>  stat;
126     //@}
127
128     /*! \brief object for receiving coordinates using communications operating on GPU memory space */
129     std::unique_ptr<gmx::PmeCoordinateReceiverGpu> pmeCoordinateReceiverGpu;
130     /*! \brief object for sending PME force using communications operating on GPU memory space */
131     std::unique_ptr<gmx::PmeForceSenderGpu>        pmeForceSenderGpu;
132
133     /*! \brief whether GPU direct communications are active for PME-PP transfers */
134     bool useGpuDirectComm = false;
135 };
136
137 /*! \brief Initialize the PME-only side of the PME <-> PP communication */
138 static std::unique_ptr<gmx_pme_pp> gmx_pme_pp_init(const t_commrec *cr)
139 {
140     auto pme_pp = std::make_unique<gmx_pme_pp>();
141
142 #if GMX_MPI
143     int rank;
144
145     pme_pp->mpi_comm_mysim = cr->mpi_comm_mysim;
146     MPI_Comm_rank(cr->mpi_comm_mygroup, &rank);
147     auto ppRanks = get_pme_ddranks(cr, rank);
148     pme_pp->ppRanks.reserve(ppRanks.size());
149     for (const auto &ppRankId : ppRanks)
150     {
151         pme_pp->ppRanks.push_back({ppRankId, 0});
152     }
153     // The peer PP rank is the last one.
154     pme_pp->peerRankId = pme_pp->ppRanks.back().rankId;
155     pme_pp->req.resize(eCommType_NR*pme_pp->ppRanks.size());
156     pme_pp->stat.resize(eCommType_NR*pme_pp->ppRanks.size());
157 #else
158     GMX_UNUSED_VALUE(cr);
159 #endif
160
161     return pme_pp;
162 }
163
164 static void reset_pmeonly_counters(gmx_wallcycle_t           wcycle,
165                                    gmx_walltime_accounting_t walltime_accounting,
166                                    t_nrnb                   *nrnb,
167                                    int64_t                   step,
168                                    bool                      useGpuForPme)
169 {
170     /* Reset all the counters related to performance over the run */
171     wallcycle_stop(wcycle, ewcRUN);
172     wallcycle_reset_all(wcycle);
173     *nrnb = { 0 };
174     wallcycle_start(wcycle, ewcRUN);
175     walltime_accounting_reset_time(walltime_accounting, step);
176
177     if (useGpuForPme)
178     {
179         resetGpuProfiler();
180     }
181 }
182
183 static gmx_pme_t *gmx_pmeonly_switch(std::vector<gmx_pme_t *> *pmedata,
184                                      const ivec grid_size,
185                                      real ewaldcoeff_q, real ewaldcoeff_lj,
186                                      const t_commrec *cr, const t_inputrec *ir)
187 {
188     GMX_ASSERT(pmedata, "Bad PME tuning list pointer");
189     for (auto &pme : *pmedata)
190     {
191         GMX_ASSERT(pme, "Bad PME tuning list element pointer");
192         if (pme->nkx == grid_size[XX] &&
193             pme->nky == grid_size[YY] &&
194             pme->nkz == grid_size[ZZ])
195         {
196             /* Here we have found an existing PME data structure that suits us.
197              * However, in the GPU case, we have to reinitialize it - there's only one GPU structure.
198              * This should not cause actual GPU reallocations, at least (the allocated buffers are never shrunk).
199              * So, just some grid size updates in the GPU kernel parameters.
200              * TODO: this should be something like gmx_pme_update_split_params()
201              */
202             gmx_pme_reinit(&pme, cr, pme, ir, grid_size, ewaldcoeff_q, ewaldcoeff_lj);
203             return pme;
204         }
205     }
206
207     const auto &pme          = pmedata->back();
208     gmx_pme_t  *newStructure = nullptr;
209     // Copy last structure with new grid params
210     gmx_pme_reinit(&newStructure, cr, pme, ir, grid_size, ewaldcoeff_q, ewaldcoeff_lj);
211     pmedata->push_back(newStructure);
212     return newStructure;
213 }
214
215 /*! \brief Called by PME-only ranks to receive coefficients and coordinates
216  *
217  * \param[in] pme           PME data structure.
218  * \param[in,out] pme_pp    PME-PP communication structure.
219  * \param[out] natoms       Number of received atoms.
220  * \param[out] box        System box, if received.
221  * \param[out] maxshift_x        Maximum shift in X direction, if received.
222  * \param[out] maxshift_y        Maximum shift in Y direction, if received.
223  * \param[out] lambda_q         Free-energy lambda for electrostatics, if received.
224  * \param[out] lambda_lj         Free-energy lambda for Lennard-Jones, if received.
225  * \param[out] bEnerVir          Set to true if this is an energy/virial calculation step, otherwise set to false.
226  * \param[out] step              MD integration step number.
227  * \param[out] grid_size         PME grid size, if received.
228  * \param[out] ewaldcoeff_q         Ewald cut-off parameter for electrostatics, if received.
229  * \param[out] ewaldcoeff_lj         Ewald cut-off parameter for Lennard-Jones, if received.
230  * \param[in] useGpuForPme      flag on whether PME is on GPU
231  * \param[in] stateGpu          GPU state propagator object
232  * \param[in] runMode           PME run mode
233  *
234  * \retval pmerecvqxX             All parameters were set, chargeA and chargeB can be NULL.
235  * \retval pmerecvqxFINISH        No parameters were set.
236  * \retval pmerecvqxSWITCHGRID    Only grid_size and *ewaldcoeff were set.
237  * \retval pmerecvqxRESETCOUNTERS *step was set.
238  */
239 static int gmx_pme_recv_coeffs_coords(struct gmx_pme_t            *pme,
240                                       gmx_pme_pp                  *pme_pp,
241                                       int                         *natoms,
242                                       matrix                       box,
243                                       int                         *maxshift_x,
244                                       int                         *maxshift_y,
245                                       real                        *lambda_q,
246                                       real                        *lambda_lj,
247                                       gmx_bool                    *bEnerVir,
248                                       int64_t                     *step,
249                                       ivec                        *grid_size,
250                                       real                        *ewaldcoeff_q,
251                                       real                        *ewaldcoeff_lj,
252                                       bool                         useGpuForPme,
253                                       gmx::StatePropagatorDataGpu *stateGpu,
254                                       PmeRunMode gmx_unused        runMode)
255 {
256     int status = -1;
257     int nat    = 0;
258
259 #if GMX_MPI
260     unsigned int flags          = 0;
261     int          messages       = 0;
262     bool         atomSetChanged = false;
263
264     do
265     {
266         gmx_pme_comm_n_box_t cnb;
267         cnb.flags = 0;
268
269         /* Receive the send count, box and time step from the peer PP node */
270         MPI_Recv(&cnb, sizeof(cnb), MPI_BYTE,
271                  pme_pp->peerRankId, eCommType_CNB,
272                  pme_pp->mpi_comm_mysim, MPI_STATUS_IGNORE);
273
274         /* We accumulate all received flags */
275         flags |= cnb.flags;
276
277         *step  = cnb.step;
278
279         if (debug)
280         {
281             fprintf(debug, "PME only rank receiving:%s%s%s%s%s\n",
282                     (cnb.flags & PP_PME_CHARGE)        ? " charges" : "",
283                     (cnb.flags & PP_PME_COORD )        ? " coordinates" : "",
284                     (cnb.flags & PP_PME_FINISH)        ? " finish" : "",
285                     (cnb.flags & PP_PME_SWITCHGRID)    ? " switch grid" : "",
286                     (cnb.flags & PP_PME_RESETCOUNTERS) ? " reset counters" : "");
287         }
288
289         pme_pp->useGpuDirectComm = ((cnb.flags & PP_PME_GPUCOMMS) != 0);
290         GMX_ASSERT(!pme_pp->useGpuDirectComm || (pme_pp->pmeForceSenderGpu != nullptr),
291                    "The use of GPU direct communication for PME-PP is enabled, "
292                    "but the PME GPU force reciever object does not exist" );
293
294         if (cnb.flags & PP_PME_FINISH)
295         {
296             status = pmerecvqxFINISH;
297         }
298
299         if (cnb.flags & PP_PME_SWITCHGRID)
300         {
301             /* Special case, receive the new parameters and return */
302             copy_ivec(cnb.grid_size, *grid_size);
303             *ewaldcoeff_q  = cnb.ewaldcoeff_q;
304             *ewaldcoeff_lj = cnb.ewaldcoeff_lj;
305
306             status         = pmerecvqxSWITCHGRID;
307         }
308
309         if (cnb.flags & PP_PME_RESETCOUNTERS)
310         {
311             /* Special case, receive the step (set above) and return */
312             status = pmerecvqxRESETCOUNTERS;
313         }
314
315         if (cnb.flags & (PP_PME_CHARGE | PP_PME_SQRTC6 | PP_PME_SIGMA))
316         {
317             atomSetChanged = true;
318
319             /* Receive the send counts from the other PP nodes */
320             for (auto &sender : pme_pp->ppRanks)
321             {
322                 if (sender.rankId == pme_pp->peerRankId)
323                 {
324                     sender.numAtoms = cnb.natoms;
325                 }
326                 else
327                 {
328                     MPI_Irecv(&sender.numAtoms, sizeof(sender.numAtoms),
329                               MPI_BYTE,
330                               sender.rankId, eCommType_CNB,
331                               pme_pp->mpi_comm_mysim, &pme_pp->req[messages++]);
332                 }
333             }
334             MPI_Waitall(messages, pme_pp->req.data(), pme_pp->stat.data());
335             messages = 0;
336
337             nat = 0;
338             for (const auto &sender : pme_pp->ppRanks)
339             {
340                 nat += sender.numAtoms;
341             }
342
343             if (cnb.flags & PP_PME_CHARGE)
344             {
345                 pme_pp->chargeA.resizeWithPadding(nat);
346             }
347             if (cnb.flags & PP_PME_CHARGEB)
348             {
349                 pme_pp->chargeB.resize(nat);
350             }
351             if (cnb.flags & PP_PME_SQRTC6)
352             {
353                 pme_pp->sqrt_c6A.resize(nat);
354             }
355             if (cnb.flags & PP_PME_SQRTC6B)
356             {
357                 pme_pp->sqrt_c6B.resize(nat);
358             }
359             if (cnb.flags & PP_PME_SIGMA)
360             {
361                 pme_pp->sigmaA.resize(nat);
362             }
363             if (cnb.flags & PP_PME_SIGMAB)
364             {
365                 pme_pp->sigmaB.resize(nat);
366             }
367             pme_pp->x.resize(nat);
368             pme_pp->f.resize(nat);
369
370             /* maxshift is sent when the charges are sent */
371             *maxshift_x = cnb.maxshift_x;
372             *maxshift_y = cnb.maxshift_y;
373
374             /* Receive the charges in place */
375             for (int q = 0; q < eCommType_NR; q++)
376             {
377                 real *bufferPtr;
378
379                 if (!(cnb.flags & (PP_PME_CHARGE<<q)))
380                 {
381                     continue;
382                 }
383                 switch (q)
384                 {
385                     case eCommType_ChargeA: bufferPtr = pme_pp->chargeA.data();  break;
386                     case eCommType_ChargeB: bufferPtr = pme_pp->chargeB.data();  break;
387                     case eCommType_SQRTC6A: bufferPtr = pme_pp->sqrt_c6A.data(); break;
388                     case eCommType_SQRTC6B: bufferPtr = pme_pp->sqrt_c6B.data(); break;
389                     case eCommType_SigmaA:  bufferPtr = pme_pp->sigmaA.data();   break;
390                     case eCommType_SigmaB:  bufferPtr = pme_pp->sigmaB.data();   break;
391                     default: gmx_incons("Wrong eCommType");
392                 }
393                 nat = 0;
394                 for (const auto &sender : pme_pp->ppRanks)
395                 {
396                     if (sender.numAtoms > 0)
397                     {
398                         MPI_Irecv(bufferPtr+nat,
399                                   sender.numAtoms*sizeof(real),
400                                   MPI_BYTE,
401                                   sender.rankId, q,
402                                   pme_pp->mpi_comm_mysim,
403                                   &pme_pp->req[messages++]);
404                         nat += sender.numAtoms;
405                         if (debug)
406                         {
407                             fprintf(debug, "Received from PP rank %d: %d %s\n",
408                                     sender.rankId, sender.numAtoms,
409                                     (q == eCommType_ChargeA ||
410                                      q == eCommType_ChargeB) ? "charges" : "params");
411                         }
412                     }
413                 }
414             }
415         }
416
417         if (cnb.flags & PP_PME_COORD)
418         {
419             if (atomSetChanged)
420             {
421                 gmx_pme_reinit_atoms(pme, nat, pme_pp->chargeA.data());
422                 if (useGpuForPme)
423                 {
424                     stateGpu->reinit(nat, nat);
425                     pme_gpu_set_device_x(pme, stateGpu->getCoordinates());
426                 }
427                 if (pme_pp->useGpuDirectComm)
428                 {
429                     GMX_ASSERT(runMode == PmeRunMode::GPU, "GPU Direct PME-PP communication has been enabled, "
430                                "but PME run mode is not PmeRunMode::GPU\n");
431
432                     // This rank will have its data accessed directly by PP rank, so needs to send the remote addresses.
433                     rvec* d_x = nullptr;
434                     rvec* d_f = nullptr;
435 #if (GMX_GPU == GMX_GPU_CUDA) //avoid invalid cast for OpenCL
436                     d_x = reinterpret_cast<rvec*> (pme_gpu_get_device_x(pme));
437                     d_f = reinterpret_cast<rvec*> (pme_gpu_get_device_f(pme));
438 #endif
439                     pme_pp->pmeCoordinateReceiverGpu->sendCoordinateBufferAddressToPpRanks(d_x);
440                     pme_pp->pmeForceSenderGpu->sendForceBufferAddressToPpRanks(d_f);
441                 }
442             }
443
444
445             /* The box, FE flag and lambda are sent along with the coordinates
446              *  */
447             copy_mat(cnb.box, box);
448             *lambda_q       = cnb.lambda_q;
449             *lambda_lj      = cnb.lambda_lj;
450             *bEnerVir       = ((cnb.flags & PP_PME_ENER_VIR) != 0U);
451             *step           = cnb.step;
452
453             /* Receive the coordinates in place */
454             nat = 0;
455             for (const auto &sender : pme_pp->ppRanks)
456             {
457                 if (sender.numAtoms > 0)
458                 {
459                     if (pme_pp->useGpuDirectComm)
460                     {
461                         pme_pp->pmeCoordinateReceiverGpu->receiveCoordinatesFromPpCudaDirect(sender.rankId);
462                     }
463                     else
464                     {
465                         MPI_Irecv(pme_pp->x[nat],
466                                   sender.numAtoms*sizeof(rvec),
467                                   MPI_BYTE,
468                                   sender.rankId, eCommType_COORD,
469                                   pme_pp->mpi_comm_mysim, &pme_pp->req[messages++]);
470                     }
471                     nat += sender.numAtoms;
472                     if (debug)
473                     {
474                         fprintf(debug, "Received from PP rank %d: %d "
475                                 "coordinates\n",
476                                 sender.rankId, sender.numAtoms);
477                     }
478                 }
479             }
480
481             status = pmerecvqxX;
482         }
483
484         /* Wait for the coordinates and/or charges to arrive */
485         MPI_Waitall(messages, pme_pp->req.data(), pme_pp->stat.data());
486         messages = 0;
487     }
488     while (status == -1);
489 #else
490     GMX_UNUSED_VALUE(pme_pp);
491     GMX_UNUSED_VALUE(box);
492     GMX_UNUSED_VALUE(maxshift_x);
493     GMX_UNUSED_VALUE(maxshift_y);
494     GMX_UNUSED_VALUE(lambda_q);
495     GMX_UNUSED_VALUE(lambda_lj);
496     GMX_UNUSED_VALUE(bEnerVir);
497     GMX_UNUSED_VALUE(step);
498     GMX_UNUSED_VALUE(grid_size);
499     GMX_UNUSED_VALUE(ewaldcoeff_q);
500     GMX_UNUSED_VALUE(ewaldcoeff_lj);
501
502     status = pmerecvqxX;
503 #endif
504
505     if (status == pmerecvqxX)
506     {
507         *natoms   = nat;
508     }
509
510     return status;
511 }
512
513 /*! \brief Send force data to PP ranks */
514 static void sendFToPP(void* sendbuf, PpRanks receiver, gmx_pme_pp *pme_pp, int *messages)
515 {
516
517     if (pme_pp->useGpuDirectComm)
518     {
519         GMX_ASSERT((pme_pp->pmeForceSenderGpu != nullptr),
520                    "The use of GPU direct communication for PME-PP is enabled, "
521                    "but the PME GPU force reciever object does not exist" );
522
523         pme_pp->pmeForceSenderGpu->sendFToPpCudaDirect(receiver.rankId);
524     }
525     else
526     {
527 #if GMX_MPI
528         //Send using MPI
529         MPI_Isend(sendbuf, receiver.numAtoms*sizeof(rvec), MPI_BYTE, receiver.rankId,
530                   0, pme_pp->mpi_comm_mysim, &pme_pp->req[*messages]);
531         *messages = *messages+1;
532 #endif
533     }
534
535 }
536
537 /*! \brief Send the PME mesh force, virial and energy to the PP-only ranks. */
538 static void gmx_pme_send_force_vir_ener(const gmx_pme_t &pme,
539                                         gmx_pme_pp *pme_pp,
540                                         const PmeOutput &output,
541                                         real dvdlambda_q, real dvdlambda_lj,
542                                         float cycles)
543 {
544 #if GMX_MPI
545     gmx_pme_comm_vir_ene_t cve;
546     int                    messages, ind_start, ind_end;
547     cve.cycles = cycles;
548
549     /* Now the evaluated forces have to be transferred to the PP nodes */
550     messages = 0;
551     ind_end  = 0;
552     for (const auto &receiver : pme_pp->ppRanks)
553     {
554         ind_start = ind_end;
555         ind_end   = ind_start + receiver.numAtoms;
556         void *sendbuf = const_cast<void *>(static_cast<const void *>(output.forces_[ind_start]));
557         if (pme_pp->useGpuDirectComm)
558         {
559             //Data will be transferred directly from GPU.
560             rvec* d_f = reinterpret_cast<rvec*> (pme_gpu_get_device_f(&pme));
561             sendbuf = reinterpret_cast<void*> (&d_f[ind_start]);
562         }
563         sendFToPP(sendbuf, receiver, pme_pp, &messages);
564     }
565
566     /* send virial and energy to our last PP node */
567     copy_mat(output.coulombVirial_, cve.vir_q);
568     copy_mat(output.lennardJonesVirial_, cve.vir_lj);
569     cve.energy_q     = output.coulombEnergy_;
570     cve.energy_lj    = output.lennardJonesEnergy_;
571     cve.dvdlambda_q  = dvdlambda_q;
572     cve.dvdlambda_lj = dvdlambda_lj;
573     /* check for the signals to send back to a PP node */
574     cve.stop_cond = gmx_get_stop_condition();
575
576     cve.cycles = cycles;
577
578     if (debug)
579     {
580         fprintf(debug, "PME rank sending to PP rank %d: virial and energy\n",
581                 pme_pp->peerRankId);
582     }
583     MPI_Isend(&cve, sizeof(cve), MPI_BYTE,
584               pme_pp->peerRankId, 1,
585               pme_pp->mpi_comm_mysim, &pme_pp->req[messages++]);
586
587     /* Wait for the forces to arrive */
588     MPI_Waitall(messages, pme_pp->req.data(), pme_pp->stat.data());
589 #else
590     gmx_call("MPI not enabled");
591     GMX_UNUSED_VALUE(pme_pp);
592     GMX_UNUSED_VALUE(output);
593     GMX_UNUSED_VALUE(dvdlambda_q);
594     GMX_UNUSED_VALUE(dvdlambda_lj);
595     GMX_UNUSED_VALUE(cycles);
596 #endif
597 }
598
599 int gmx_pmeonly(struct gmx_pme_t *pme,
600                 const t_commrec *cr, t_nrnb *mynrnb,
601                 gmx_wallcycle  *wcycle,
602                 gmx_walltime_accounting_t walltime_accounting,
603                 t_inputrec *ir, PmeRunMode runMode)
604 {
605     int                ret;
606     int                natoms = 0;
607     matrix             box;
608     real               lambda_q   = 0;
609     real               lambda_lj  = 0;
610     int                maxshift_x = 0, maxshift_y = 0;
611     real               dvdlambda_q, dvdlambda_lj;
612     float              cycles;
613     int                count;
614     gmx_bool           bEnerVir = FALSE;
615     int64_t            step;
616
617     /* This data will only use with PME tuning, i.e. switching PME grids */
618     std::vector<gmx_pme_t *> pmedata;
619     pmedata.push_back(pme);
620
621     auto        pme_pp       = gmx_pme_pp_init(cr);
622     //TODO the variable below should be queried from the task assignment info
623     const bool  useGpuForPme   = (runMode == PmeRunMode::GPU) || (runMode == PmeRunMode::Mixed);
624     const void *commandStream  = useGpuForPme ? pme_gpu_get_device_stream(pme) : nullptr;
625     const void *deviceContext  = useGpuForPme ? pme_gpu_get_device_context(pme) : nullptr;
626     const int   paddingSize    = pme_gpu_get_padding_size(pme);
627     if (useGpuForPme)
628     {
629         changePinningPolicy(&pme_pp->chargeA, pme_get_pinning_policy());
630         changePinningPolicy(&pme_pp->x, pme_get_pinning_policy());
631         if (c_enableGpuPmePpComms)
632         {
633             pme_pp->pmeCoordinateReceiverGpu =
634                 std::make_unique<gmx::PmeCoordinateReceiverGpu>(pme_gpu_get_device_stream(pme),
635                                                                 pme_pp->mpi_comm_mysim,
636                                                                 pme_pp->ppRanks);
637             pme_pp->pmeForceSenderGpu =
638                 std::make_unique<gmx::PmeForceSenderGpu>(pme_gpu_get_device_stream(pme),
639                                                          pme_pp->mpi_comm_mysim,
640                                                          pme_pp->ppRanks);
641         }
642     }
643
644     std::unique_ptr<gmx::StatePropagatorDataGpu> stateGpu;
645     if (useGpuForPme)
646     {
647         // TODO: Special PME-only constructor is used here. There is no mechanism to prevent from using the other constructor here.
648         //       This should be made safer.
649         stateGpu = std::make_unique<gmx::StatePropagatorDataGpu>(commandStream, deviceContext, GpuApiCallBehavior::Async, paddingSize);
650     }
651
652
653     clear_nrnb(mynrnb);
654
655     count = 0;
656     do /****** this is a quasi-loop over time steps! */
657     {
658         /* The reason for having a loop here is PME grid tuning/switching */
659         do
660         {
661             /* Domain decomposition */
662             ivec newGridSize;
663             real ewaldcoeff_q   = 0, ewaldcoeff_lj = 0;
664             ret = gmx_pme_recv_coeffs_coords(pme,
665                                              pme_pp.get(),
666                                              &natoms,
667                                              box,
668                                              &maxshift_x, &maxshift_y,
669                                              &lambda_q, &lambda_lj,
670                                              &bEnerVir,
671                                              &step,
672                                              &newGridSize,
673                                              &ewaldcoeff_q,
674                                              &ewaldcoeff_lj,
675                                              useGpuForPme,
676                                              stateGpu.get(),
677                                              runMode);
678
679             if (ret == pmerecvqxSWITCHGRID)
680             {
681                 /* Switch the PME grid to newGridSize */
682                 pme = gmx_pmeonly_switch(&pmedata, newGridSize, ewaldcoeff_q, ewaldcoeff_lj, cr, ir);
683             }
684
685             if (ret == pmerecvqxRESETCOUNTERS)
686             {
687                 /* Reset the cycle and flop counters */
688                 reset_pmeonly_counters(wcycle, walltime_accounting, mynrnb, step, useGpuForPme);
689             }
690         }
691         while (ret == pmerecvqxSWITCHGRID || ret == pmerecvqxRESETCOUNTERS);
692
693         if (ret == pmerecvqxFINISH)
694         {
695             /* We should stop: break out of the loop */
696             break;
697         }
698
699         if (count == 0)
700         {
701             wallcycle_start(wcycle, ewcRUN);
702             walltime_accounting_start_time(walltime_accounting);
703         }
704
705         wallcycle_start(wcycle, ewcPMEMESH);
706
707         dvdlambda_q  = 0;
708         dvdlambda_lj = 0;
709
710         // TODO Make a struct of array refs onto these per-atom fields
711         // of pme_pp (maybe box, energy and virial, too; and likewise
712         // from mdatoms for the other call to gmx_pme_do), so we have
713         // fewer lines of code and less parameter passing.
714         const int pmeFlags = GMX_PME_DO_ALL_F | (bEnerVir ? GMX_PME_CALC_ENER_VIR : 0);
715         PmeOutput output;
716         if (useGpuForPme)
717         {
718             const bool boxChanged              = false;
719             const bool useGpuPmeForceReduction = pme_pp->useGpuDirectComm;
720             //TODO this should be set properly by gmx_pme_recv_coeffs_coords,
721             // or maybe use inputrecDynamicBox(ir), at the very least - change this when this codepath is tested!
722             pme_gpu_prepare_computation(pme, boxChanged, box, wcycle, pmeFlags, useGpuPmeForceReduction);
723             if (!pme_pp->useGpuDirectComm)
724             {
725                 stateGpu->copyCoordinatesToGpu(gmx::ArrayRef<gmx::RVec>(pme_pp->x), gmx::StatePropagatorDataGpu::AtomLocality::All);
726             }
727             // On the separate PME rank we do not need a synchronizer as we schedule everything in a single stream
728             // TODO: with pme on GPU the receive should make a list of synchronizers and pass it here #3157
729             auto xReadyOnDevice = nullptr;
730
731             pme_gpu_launch_spread(pme, xReadyOnDevice, wcycle);
732             pme_gpu_launch_complex_transforms(pme, wcycle);
733             pme_gpu_launch_gather(pme, wcycle, PmeForceOutputHandling::Set);
734             output = pme_gpu_wait_finish_task(pme, pmeFlags, wcycle);
735             pme_gpu_reinit_computation(pme, wcycle);
736         }
737         else
738         {
739             GMX_ASSERT(pme_pp->x.size() == static_cast<size_t>(natoms), "The coordinate buffer should have size natoms");
740
741             gmx_pme_do(pme, pme_pp->x, pme_pp->f,
742                        pme_pp->chargeA.data(), pme_pp->chargeB.data(),
743                        pme_pp->sqrt_c6A.data(), pme_pp->sqrt_c6B.data(),
744                        pme_pp->sigmaA.data(), pme_pp->sigmaB.data(), box,
745                        cr, maxshift_x, maxshift_y, mynrnb, wcycle,
746                        output.coulombVirial_, output.lennardJonesVirial_,
747                        &output.coulombEnergy_, &output.lennardJonesEnergy_,
748                        lambda_q, lambda_lj, &dvdlambda_q, &dvdlambda_lj,
749                        pmeFlags);
750             output.forces_ = pme_pp->f;
751         }
752
753         cycles = wallcycle_stop(wcycle, ewcPMEMESH);
754         gmx_pme_send_force_vir_ener(*pme, pme_pp.get(), output,
755                                     dvdlambda_q, dvdlambda_lj, cycles);
756
757         count++;
758     } /***** end of quasi-loop, we stop with the break above */
759     while (TRUE);
760
761     walltime_accounting_end_time(walltime_accounting);
762
763     return 0;
764 }