Apply clang-format to source tree
[alexxy/gromacs.git] / src / gromacs / domdec / domdec_setup.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2008,2009,2010,2011,2012,2013,2014,2015,2017,2018,2019, 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 used by the domdec module
39  * in its initial setup phase.
40  *
41  * \author Berk Hess <hess@kth.se>
42  * \ingroup module_domdec
43  */
44
45 #include "gmxpre.h"
46
47 #include "domdec_setup.h"
48
49 #include <cassert>
50 #include <cinttypes>
51 #include <cmath>
52 #include <cstdio>
53
54 #include "gromacs/domdec/domdec.h"
55 #include "gromacs/domdec/domdec_struct.h"
56 #include "gromacs/domdec/options.h"
57 #include "gromacs/ewald/pme.h"
58 #include "gromacs/gmxlib/network.h"
59 #include "gromacs/math/utilities.h"
60 #include "gromacs/math/vec.h"
61 #include "gromacs/mdlib/perf_est.h"
62 #include "gromacs/mdtypes/commrec.h"
63 #include "gromacs/mdtypes/inputrec.h"
64 #include "gromacs/mdtypes/md_enums.h"
65 #include "gromacs/pbcutil/pbc.h"
66 #include "gromacs/topology/topology.h"
67 #include "gromacs/utility/fatalerror.h"
68 #include "gromacs/utility/logger.h"
69 #include "gromacs/utility/stringutil.h"
70
71 #include "box.h"
72 #include "domdec_internal.h"
73 #include "utility.h"
74
75 // TODO remove this when moving domdec into gmx namespace
76 using gmx::DomdecOptions;
77
78 /*! \brief Margin for setting up the DD grid */
79 #define DD_GRID_MARGIN_PRES_SCALE 1.05
80
81 /*! \brief Factorize \p n.
82  *
83  * \param[in]    n     Value to factorize
84  * \param[out]   fac   Vector of factors (to be allocated in this function)
85  * \param[out]   mfac  Vector with the number of times each factor repeats in the factorization (to be allocated in this function)
86  */
87 static void factorize(int n, std::vector<int>* fac, std::vector<int>* mfac)
88 {
89     if (n <= 0)
90     {
91         gmx_fatal(FARGS, "Can only factorize positive integers.");
92     }
93
94     /* Decompose n in factors */
95     fac->clear();
96     mfac->clear();
97     int d = 2;
98     while (n > 1)
99     {
100         while (n % d == 0)
101         {
102             if (fac->empty() || fac->back() != d)
103             {
104                 fac->push_back(d);
105                 mfac->push_back(1);
106             }
107             else
108             {
109                 mfac->back()++;
110             }
111             n /= d;
112         }
113         d++;
114     }
115 }
116
117 /*! \brief Find largest divisor of \p n smaller than \p n*/
118 static int largest_divisor(int n)
119 {
120     std::vector<int> div;
121     std::vector<int> mdiv;
122     factorize(n, &div, &mdiv);
123
124     return div.back();
125 }
126
127 /*! \brief Compute largest common divisor of \p n1 and \b n2 */
128 static int lcd(int n1, int n2)
129 {
130     int d, i;
131
132     d = 1;
133     for (i = 2; (i <= n1 && i <= n2); i++)
134     {
135         if (n1 % i == 0 && n2 % i == 0)
136         {
137             d = i;
138         }
139     }
140
141     return d;
142 }
143
144 /*! \brief Returns TRUE when there are enough PME ranks for the ratio */
145 static gmx_bool fits_pme_ratio(int nrank_tot, int nrank_pme, float ratio)
146 {
147     return (static_cast<double>(nrank_pme) / static_cast<double>(nrank_tot) > 0.95 * ratio);
148 }
149
150 /*! \brief Returns TRUE when npme out of ntot ranks doing PME is expected to give reasonable performance */
151 static gmx_bool fits_pp_pme_perf(int ntot, int npme, float ratio)
152 {
153     std::vector<int> div;
154     std::vector<int> mdiv;
155     factorize(ntot - npme, &div, &mdiv);
156
157     int npp_root3  = gmx::roundToInt(std::cbrt(ntot - npme));
158     int npme_root2 = gmx::roundToInt(std::sqrt(static_cast<double>(npme)));
159
160     /* The check below gives a reasonable division:
161      * factor 5 allowed at 5 or more PP ranks,
162      * factor 7 allowed at 49 or more PP ranks.
163      */
164     if (div.back() > 3 + npp_root3)
165     {
166         return FALSE;
167     }
168
169     /* Check if the number of PP and PME ranks have a reasonable sized
170      * denominator in common, such that we can use 2D PME decomposition
171      * when required (which requires nx_pp == nx_pme).
172      * The factor of 2 allows for a maximum ratio of 2^2=4
173      * between nx_pme and ny_pme.
174      */
175     if (lcd(ntot - npme, npme) * 2 < npme_root2)
176     {
177         return FALSE;
178     }
179
180     /* Does this division gives a reasonable PME load? */
181     return fits_pme_ratio(ntot, npme, ratio);
182 }
183
184 /*! \brief Make a guess for the number of PME ranks to use. */
185 static int guess_npme(const gmx::MDLogger& mdlog,
186                       const gmx_mtop_t&    mtop,
187                       const t_inputrec&    ir,
188                       const matrix         box,
189                       int                  nrank_tot)
190 {
191     float ratio;
192     int   npme;
193
194     ratio = pme_load_estimate(mtop, ir, box);
195
196     GMX_LOG(mdlog.info).appendTextFormatted("Guess for relative PME load: %.2f", ratio);
197
198     /* We assume the optimal rank ratio is close to the load ratio.
199      * The communication load is neglected,
200      * but (hopefully) this will balance out between PP and PME.
201      */
202
203     if (!fits_pme_ratio(nrank_tot, nrank_tot / 2, ratio))
204     {
205         /* We would need more than nrank_tot/2 PME only nodes,
206          * which is not possible. Since the PME load is very high,
207          * we will not loose much performance when all ranks do PME.
208          */
209
210         return 0;
211     }
212
213     /* First try to find npme as a factor of nrank_tot up to nrank_tot/3.
214      * We start with a minimum PME node fraction of 1/16
215      * and avoid ratios which lead to large prime factors in nnodes-npme.
216      */
217     npme = (nrank_tot + 15) / 16;
218     while (npme <= nrank_tot / 3)
219     {
220         if (nrank_tot % npme == 0)
221         {
222             /* Note that fits_perf might change the PME grid,
223              * in the current implementation it does not.
224              */
225             if (fits_pp_pme_perf(nrank_tot, npme, ratio))
226             {
227                 break;
228             }
229         }
230         npme++;
231     }
232     if (npme > nrank_tot / 3)
233     {
234         /* Try any possible number for npme */
235         npme = 1;
236         while (npme <= nrank_tot / 2)
237         {
238             /* Note that fits_perf may change the PME grid */
239             if (fits_pp_pme_perf(nrank_tot, npme, ratio))
240             {
241                 break;
242             }
243             npme++;
244         }
245     }
246     if (npme > nrank_tot / 2)
247     {
248         gmx_fatal(FARGS,
249                   "Could not find an appropriate number of separate PME ranks. i.e. >= %5f*#ranks "
250                   "(%d) and <= #ranks/2 (%d) and reasonable performance wise (grid_x=%d, "
251                   "grid_y=%d).\n"
252                   "Use the -npme option of mdrun or change the number of ranks or the PME grid "
253                   "dimensions, see the manual for details.",
254                   ratio, gmx::roundToInt(0.95 * ratio * nrank_tot), nrank_tot / 2, ir.nkx, ir.nky);
255     }
256     else
257     {
258         GMX_LOG(mdlog.info)
259                 .appendTextFormatted(
260                         "Will use %d particle-particle and %d PME only ranks\n"
261                         "This is a guess, check the performance at the end of the log file",
262                         nrank_tot - npme, npme);
263     }
264
265     return npme;
266 }
267
268 /*! \brief Return \p n divided by \p f rounded up to the next integer. */
269 static int div_up(int n, int f)
270 {
271     return (n + f - 1) / f;
272 }
273
274 real comm_box_frac(const gmx::IVec& dd_nc, real cutoff, const gmx_ddbox_t& ddbox)
275 {
276     int  i, j, k;
277     rvec nw;
278     real comm_vol;
279
280     for (i = 0; i < DIM; i++)
281     {
282         real bt = ddbox.box_size[i] * ddbox.skew_fac[i];
283         nw[i]   = dd_nc[i] * cutoff / bt;
284     }
285
286     comm_vol = 0;
287     for (i = 0; i < DIM; i++)
288     {
289         if (dd_nc[i] > 1)
290         {
291             comm_vol += nw[i];
292             for (j = i + 1; j < DIM; j++)
293             {
294                 if (dd_nc[j] > 1)
295                 {
296                     comm_vol += nw[i] * nw[j] * M_PI / 4;
297                     for (k = j + 1; k < DIM; k++)
298                     {
299                         if (dd_nc[k] > 1)
300                         {
301                             comm_vol += nw[i] * nw[j] * nw[k] * M_PI / 6;
302                         }
303                     }
304                 }
305             }
306         }
307     }
308
309     return comm_vol;
310 }
311
312 /*! \brief Return whether the DD inhomogeneous in the z direction */
313 static gmx_bool inhomogeneous_z(const t_inputrec& ir)
314 {
315     return ((EEL_PME(ir.coulombtype) || ir.coulombtype == eelEWALD) && ir.ePBC == epbcXYZ
316             && ir.ewald_geometry == eewg3DC);
317 }
318
319 /*! \brief Estimate cost of PME FFT communication
320  *
321  * This only takes the communication into account and not imbalance
322  * in the calculation. But the imbalance in communication and calculation
323  * are similar and therefore these formulas also prefer load balance
324  * in the FFT and pme_solve calculation.
325  */
326 static float comm_pme_cost_vol(int npme, int a, int b, int c)
327 {
328     /* We use a float here, since an integer might overflow */
329     float comm_vol;
330
331     comm_vol = npme - 1;
332     comm_vol *= npme;
333     comm_vol *= div_up(a, npme);
334     comm_vol *= div_up(b, npme);
335     comm_vol *= c;
336
337     return comm_vol;
338 }
339
340 /*! \brief Estimate cost of communication for a possible domain decomposition. */
341 static float comm_cost_est(real               limit,
342                            real               cutoff,
343                            const matrix       box,
344                            const gmx_ddbox_t& ddbox,
345                            int                natoms,
346                            const t_inputrec&  ir,
347                            float              pbcdxr,
348                            int                npme_tot,
349                            const gmx::IVec&   nc)
350 {
351     gmx::IVec npme = { 1, 1, 1 };
352     int       i, j, nk, overlap;
353     rvec      bt;
354     float     comm_vol, comm_vol_xf, comm_pme, cost_pbcdx;
355     /* This is the cost of a pbc_dx call relative to the cost
356      * of communicating the coordinate and force of an atom.
357      * This will be machine dependent.
358      * These factors are for x86 with SMP or Infiniband.
359      */
360     float pbcdx_rect_fac = 0.1;
361     float pbcdx_tric_fac = 0.2;
362     float temp;
363
364     /* Check the DD algorithm restrictions */
365     if ((ir.ePBC == epbcXY && ir.nwall < 2 && nc[ZZ] > 1)
366         || (ir.ePBC == epbcSCREW && (nc[XX] == 1 || nc[YY] > 1 || nc[ZZ] > 1)))
367     {
368         return -1;
369     }
370
371     if (inhomogeneous_z(ir) && nc[ZZ] > 1)
372     {
373         return -1;
374     }
375
376     assert(ddbox.npbcdim <= DIM);
377
378     /* Check if the triclinic requirements are met */
379     for (i = 0; i < DIM; i++)
380     {
381         for (j = i + 1; j < ddbox.npbcdim; j++)
382         {
383             if (box[j][i] != 0 || ir.deform[j][i] != 0 || (ir.epc != epcNO && ir.compress[j][i] != 0))
384             {
385                 if (nc[j] > 1 && nc[i] == 1)
386                 {
387                     return -1;
388                 }
389             }
390         }
391     }
392
393     for (i = 0; i < DIM; i++)
394     {
395         bt[i] = ddbox.box_size[i] * ddbox.skew_fac[i];
396
397         /* Without PBC and with 2 cells, there are no lower limits on the cell size */
398         if (!(i >= ddbox.npbcdim && nc[i] <= 2) && bt[i] < nc[i] * limit)
399         {
400             return -1;
401         }
402         /* With PBC, check if the cut-off fits in nc[i]-1 cells */
403         if (i < ddbox.npbcdim && nc[i] > 1 && (nc[i] - 1) * bt[i] < nc[i] * cutoff)
404         {
405             return -1;
406         }
407     }
408
409     if (npme_tot > 1)
410     {
411         /* The following choices should match those
412          * in init_domain_decomposition in domdec.c.
413          */
414         if (nc[XX] == 1 && nc[YY] > 1)
415         {
416             npme[XX] = 1;
417             npme[YY] = npme_tot;
418         }
419         else if (nc[YY] == 1)
420         {
421             npme[XX] = npme_tot;
422             npme[YY] = 1;
423         }
424         else
425         {
426             /* Will we use 1D or 2D PME decomposition? */
427             npme[XX] = (npme_tot % nc[XX] == 0) ? nc[XX] : npme_tot;
428             npme[YY] = npme_tot / npme[XX];
429         }
430     }
431
432     if (EEL_PME(ir.coulombtype) || EVDW_PME(ir.vdwtype))
433     {
434         /* Check the PME grid restrictions.
435          * Currently these can only be invalid here with too few grid lines
436          * along the x dimension per rank doing PME.
437          */
438         int npme_x = (npme_tot > 1 ? npme[XX] : nc[XX]);
439
440         /* Currently we don't have the OpenMP thread count available here.
441          * But with threads we have only tighter restrictions and it's
442          * probably better anyhow to avoid settings where we need to reduce
443          * grid lines over multiple ranks, as the thread check will do.
444          */
445         bool useThreads     = true;
446         bool errorsAreFatal = false;
447         if (!gmx_pme_check_restrictions(ir.pme_order, ir.nkx, ir.nky, ir.nkz, npme_x, useThreads,
448                                         errorsAreFatal))
449         {
450             return -1;
451         }
452     }
453
454     /* When two dimensions are (nearly) equal, use more cells
455      * for the smallest index, so the decomposition does not
456      * depend sensitively on the rounding of the box elements.
457      */
458     for (i = 0; i < DIM; i++)
459     {
460         for (j = i + 1; j < DIM; j++)
461         {
462             /* Check if the box size is nearly identical,
463              * in that case we prefer nx > ny  and ny > nz.
464              */
465             if (std::fabs(bt[j] - bt[i]) < 0.01 * bt[i] && nc[j] > nc[i])
466             {
467                 /* The XX/YY check is a bit compact. If nc[YY]==npme[YY]
468                  * this means the swapped nc has nc[XX]==npme[XX],
469                  * and we can also swap X and Y for PME.
470                  */
471                 /* Check if dimension i and j are equivalent for PME.
472                  * For x/y: if nc[YY]!=npme[YY], we can not swap x/y
473                  * For y/z: we can not have PME decomposition in z
474                  */
475                 if (npme_tot <= 1
476                     || !((i == XX && j == YY && nc[YY] != npme[YY]) || (i == YY && j == ZZ && npme[YY] > 1)))
477                 {
478                     return -1;
479                 }
480             }
481         }
482     }
483
484     /* This function determines only half of the communication cost.
485      * All PP, PME and PP-PME communication is symmetric
486      * and the "back"-communication cost is identical to the forward cost.
487      */
488
489     comm_vol = comm_box_frac(nc, cutoff, ddbox);
490
491     comm_pme = 0;
492     for (i = 0; i < 2; i++)
493     {
494         /* Determine the largest volume for PME x/f redistribution */
495         if (nc[i] % npme[i] != 0)
496         {
497             if (nc[i] > npme[i])
498             {
499                 comm_vol_xf = (npme[i] == 2 ? 1.0 / 3.0 : 0.5);
500             }
501             else
502             {
503                 comm_vol_xf = 1.0 - lcd(nc[i], npme[i]) / static_cast<double>(npme[i]);
504             }
505             comm_pme += 3 * natoms * comm_vol_xf;
506         }
507
508         /* Grid overlap communication */
509         if (npme[i] > 1)
510         {
511             nk      = (i == 0 ? ir.nkx : ir.nky);
512             overlap = (nk % npme[i] == 0 ? ir.pme_order - 1 : ir.pme_order);
513             temp    = npme[i];
514             temp *= overlap;
515             temp *= ir.nkx;
516             temp *= ir.nky;
517             temp *= ir.nkz;
518             temp /= nk;
519             comm_pme += temp;
520             /* Old line comm_pme += npme[i]*overlap*ir.nkx*ir.nky*ir.nkz/nk; */
521         }
522     }
523
524     comm_pme += comm_pme_cost_vol(npme[YY], ir.nky, ir.nkz, ir.nkx);
525     comm_pme += comm_pme_cost_vol(npme[XX], ir.nkx, ir.nky, ir.nkz);
526
527     /* Add cost of pbc_dx for bondeds */
528     cost_pbcdx = 0;
529     if ((nc[XX] == 1 || nc[YY] == 1) || (nc[ZZ] == 1 && ir.ePBC != epbcXY))
530     {
531         if ((ddbox.tric_dir[XX] && nc[XX] == 1) || (ddbox.tric_dir[YY] && nc[YY] == 1))
532         {
533             cost_pbcdx = pbcdxr * pbcdx_tric_fac;
534         }
535         else
536         {
537             cost_pbcdx = pbcdxr * pbcdx_rect_fac;
538         }
539     }
540
541     if (debug)
542     {
543         fprintf(debug, "nc %2d %2d %2d %2d %2d vol pp %6.4f pbcdx %6.4f pme %9.3e tot %9.3e\n",
544                 nc[XX], nc[YY], nc[ZZ], npme[XX], npme[YY], comm_vol, cost_pbcdx,
545                 comm_pme / (3 * natoms), comm_vol + cost_pbcdx + comm_pme / (3 * natoms));
546     }
547
548     return 3 * natoms * (comm_vol + cost_pbcdx) + comm_pme;
549 }
550
551 /*! \brief Assign penalty factors to possible domain decompositions,
552  * based on the estimated communication costs. */
553 static void assign_factors(const real         limit,
554                            const bool         request1D,
555                            const real         cutoff,
556                            const matrix       box,
557                            const gmx_ddbox_t& ddbox,
558                            int                natoms,
559                            const t_inputrec&  ir,
560                            float              pbcdxr,
561                            int                npme,
562                            int                ndiv,
563                            const int*         div,
564                            const int*         mdiv,
565                            gmx::IVec*         irTryPtr,
566                            gmx::IVec*         opt)
567 {
568     int        x, y, i;
569     float      ce;
570     gmx::IVec& ir_try = *irTryPtr;
571
572     if (ndiv == 0)
573     {
574         const int  maxDimensionSize        = std::max(ir_try[XX], std::max(ir_try[YY], ir_try[ZZ]));
575         const int  productOfDimensionSizes = ir_try[XX] * ir_try[YY] * ir_try[ZZ];
576         const bool decompositionHasOneDimension = (maxDimensionSize == productOfDimensionSizes);
577         if (request1D && !decompositionHasOneDimension)
578         {
579             /* We requested 1D DD, but got multiple dimensions */
580             return;
581         }
582
583         ce = comm_cost_est(limit, cutoff, box, ddbox, natoms, ir, pbcdxr, npme, ir_try);
584         if (ce >= 0
585             && ((*opt)[XX] == 0
586                 || ce < comm_cost_est(limit, cutoff, box, ddbox, natoms, ir, pbcdxr, npme, *opt)))
587         {
588             *opt = ir_try;
589         }
590
591         return;
592     }
593
594     for (x = mdiv[0]; x >= 0; x--)
595     {
596         for (i = 0; i < x; i++)
597         {
598             ir_try[XX] *= div[0];
599         }
600         for (y = mdiv[0] - x; y >= 0; y--)
601         {
602             for (i = 0; i < y; i++)
603             {
604                 ir_try[YY] *= div[0];
605             }
606             for (i = 0; i < mdiv[0] - x - y; i++)
607             {
608                 ir_try[ZZ] *= div[0];
609             }
610
611             /* recurse */
612             assign_factors(limit, request1D, cutoff, box, ddbox, natoms, ir, pbcdxr, npme, ndiv - 1,
613                            div + 1, mdiv + 1, irTryPtr, opt);
614
615             for (i = 0; i < mdiv[0] - x - y; i++)
616             {
617                 ir_try[ZZ] /= div[0];
618             }
619             for (i = 0; i < y; i++)
620             {
621                 ir_try[YY] /= div[0];
622             }
623         }
624         for (i = 0; i < x; i++)
625         {
626             ir_try[XX] /= div[0];
627         }
628     }
629 }
630
631 /*! \brief Determine the optimal distribution of DD cells for the
632  * simulation system and number of MPI ranks
633  *
634  * \returns The optimal grid cell choice. The latter will contain all
635  *          zeros if no valid cell choice exists. */
636 static gmx::IVec optimizeDDCells(const gmx::MDLogger& mdlog,
637                                  const int            numRanksRequested,
638                                  const int            numPmeOnlyRanks,
639                                  const real           cellSizeLimit,
640                                  const bool           request1DAnd1Pulse,
641                                  const gmx_mtop_t&    mtop,
642                                  const matrix         box,
643                                  const gmx_ddbox_t&   ddbox,
644                                  const t_inputrec&    ir,
645                                  const DDSystemInfo&  systemInfo)
646 {
647     double pbcdxr;
648
649     const int numPPRanks = numRanksRequested - numPmeOnlyRanks;
650
651     GMX_LOG(mdlog.info)
652             .appendTextFormatted(
653                     "Optimizing the DD grid for %d cells with a minimum initial size of %.3f nm",
654                     numPPRanks, cellSizeLimit);
655     if (inhomogeneous_z(ir))
656     {
657         GMX_LOG(mdlog.info)
658                 .appendTextFormatted(
659                         "Ewald_geometry=%s: assuming inhomogeneous particle distribution in z, "
660                         "will not decompose in z.",
661                         eewg_names[ir.ewald_geometry]);
662     }
663
664
665     // For cost estimates, we need the number of ranks doing PME work,
666     // which is the number of PP ranks when not using separate
667     // PME-only ranks.
668     const int numRanksDoingPmeWork =
669             (EEL_PME(ir.coulombtype) ? ((numPmeOnlyRanks > 0) ? numPmeOnlyRanks : numPPRanks) : 0);
670
671     if (systemInfo.haveInterDomainBondeds)
672     {
673         /* If we can skip PBC for distance calculations in plain-C bondeds,
674          * we can save some time (e.g. 3D DD with pbc=xyz).
675          * Here we ignore SIMD bondeds as they always do (fast) PBC.
676          */
677         count_bonded_distances(mtop, ir, &pbcdxr, nullptr);
678         pbcdxr /= static_cast<double>(mtop.natoms);
679     }
680     else
681     {
682         /* Every molecule is a single charge group: no pbc required */
683         pbcdxr = 0;
684     }
685
686     if (cellSizeLimit > 0)
687     {
688         std::string maximumCells = "The maximum allowed number of cells is:";
689         for (int d = 0; d < DIM; d++)
690         {
691             int nmax = static_cast<int>(ddbox.box_size[d] * ddbox.skew_fac[d] / cellSizeLimit);
692             if (d >= ddbox.npbcdim && nmax < 2)
693             {
694                 nmax = 2;
695             }
696             if (d == ZZ && inhomogeneous_z(ir))
697             {
698                 nmax = 1;
699             }
700             maximumCells += gmx::formatString(" %c %d", 'X' + d, nmax);
701         }
702         GMX_LOG(mdlog.info).appendText(maximumCells);
703     }
704
705     if (debug)
706     {
707         fprintf(debug, "Average nr of pbc_dx calls per atom %.2f\n", pbcdxr);
708     }
709
710     /* Decompose numPPRanks in factors */
711     std::vector<int> div;
712     std::vector<int> mdiv;
713     factorize(numPPRanks, &div, &mdiv);
714
715     gmx::IVec itry       = { 1, 1, 1 };
716     gmx::IVec numDomains = { 0, 0, 0 };
717     assign_factors(cellSizeLimit, request1DAnd1Pulse, systemInfo.cutoff, box, ddbox, mtop.natoms, ir,
718                    pbcdxr, numRanksDoingPmeWork, div.size(), div.data(), mdiv.data(), &itry, &numDomains);
719
720     return numDomains;
721 }
722
723 real getDDGridSetupCellSizeLimit(const gmx::MDLogger& mdlog,
724                                  const bool           request1DAnd1Pulse,
725                                  const bool           bDynLoadBal,
726                                  const real           dlb_scale,
727                                  const t_inputrec&    ir,
728                                  const real           systemInfoCellSizeLimit)
729 {
730     real cellSizeLimit = systemInfoCellSizeLimit;
731     if (request1DAnd1Pulse)
732     {
733         cellSizeLimit = std::max(cellSizeLimit, ir.rlist);
734     }
735
736     /* Add a margin for DLB and/or pressure scaling */
737     if (bDynLoadBal)
738     {
739         if (dlb_scale >= 1.0)
740         {
741             gmx_fatal(FARGS, "The value for option -dds should be smaller than 1");
742         }
743         GMX_LOG(mdlog.info)
744                 .appendTextFormatted(
745                         "Scaling the initial minimum size with 1/%g (option -dds) = %g", dlb_scale,
746                         1 / dlb_scale);
747         cellSizeLimit /= dlb_scale;
748     }
749     else if (ir.epc != epcNO)
750     {
751         GMX_LOG(mdlog.info)
752                 .appendTextFormatted(
753                         "To account for pressure scaling, scaling the initial minimum size with %g",
754                         DD_GRID_MARGIN_PRES_SCALE);
755         cellSizeLimit *= DD_GRID_MARGIN_PRES_SCALE;
756     }
757
758     return cellSizeLimit;
759 }
760 void checkForValidRankCountRequests(const int numRanksRequested, const bool usingPme, const int numPmeRanksRequested)
761 {
762     int numPPRanksRequested = numRanksRequested;
763     if (usingPme && numPmeRanksRequested > 0)
764     {
765         numPPRanksRequested -= numPmeRanksRequested;
766         if (numPmeRanksRequested > numPPRanksRequested)
767         {
768             gmx_fatal(FARGS,
769                       "Cannot have %d separate PME ranks with only %d PP ranks, choose fewer or no "
770                       "separate PME ranks",
771                       numPmeRanksRequested, numPPRanksRequested);
772         }
773     }
774
775     // Once the rank count is large enough, it becomes worth
776     // suggesting improvements to the user.
777     const int minPPRankCountToCheckForLargePrimeFactors = 13;
778     if (numPPRanksRequested >= minPPRankCountToCheckForLargePrimeFactors)
779     {
780         const int largestDivisor = largest_divisor(numPPRanksRequested);
781         /* Check if the largest divisor is more than numPPRanks ^ (2/3) */
782         if (largestDivisor * largestDivisor * largestDivisor > numPPRanksRequested * numPPRanksRequested)
783         {
784             gmx_fatal(FARGS,
785                       "The number of ranks selected for particle-particle work (%d) "
786                       "contains a large prime factor %d. In most cases this will lead to "
787                       "bad performance. Choose a number with smaller prime factors or "
788                       "set the decomposition (option -dd) manually.",
789                       numPPRanksRequested, largestDivisor);
790         }
791     }
792 }
793
794 /*! \brief Return the number of PME-only ranks used by the simulation
795  *
796  * If the user did not choose a number, then decide for them. */
797 static int getNumPmeOnlyRanksToUse(const gmx::MDLogger& mdlog,
798                                    const DomdecOptions& options,
799                                    const gmx_mtop_t&    mtop,
800                                    const t_inputrec&    ir,
801                                    const matrix         box,
802                                    const int            numRanksRequested)
803 {
804     int         numPmeOnlyRanks;
805     const char* extraMessage = "";
806
807     if (options.numCells[XX] > 0)
808     {
809         if (options.numPmeRanks >= 0)
810         {
811             // TODO mdrun should give a fatal error with a non-PME input file and -npme > 0
812             numPmeOnlyRanks = options.numPmeRanks;
813         }
814         else
815         {
816             // When the DD grid is set explicitly and -npme is set to auto,
817             // don't use PME ranks. We check later if the DD grid is
818             // compatible with the total number of ranks.
819             numPmeOnlyRanks = 0;
820         }
821     }
822     else
823     {
824         if (!EEL_PME(ir.coulombtype))
825         {
826             // TODO mdrun should give a fatal error with a non-PME input file and -npme > 0
827             numPmeOnlyRanks = 0;
828         }
829         else
830         {
831             if (options.numPmeRanks >= 0)
832             {
833                 numPmeOnlyRanks = options.numPmeRanks;
834             }
835             else
836             {
837                 // Controls the automated choice of when to use separate PME-only ranks.
838                 const int minRankCountToDefaultToSeparatePmeRanks = 19;
839
840                 if (numRanksRequested < minRankCountToDefaultToSeparatePmeRanks)
841                 {
842                     numPmeOnlyRanks = 0;
843                     extraMessage =
844                             ", as there are too few total\n"
845                             " ranks for efficient splitting";
846                 }
847                 else
848                 {
849                     numPmeOnlyRanks = guess_npme(mdlog, mtop, ir, box, numRanksRequested);
850                     extraMessage    = ", as guessed by mdrun";
851                 }
852             }
853         }
854     }
855     GMX_RELEASE_ASSERT(numPmeOnlyRanks <= numRanksRequested,
856                        "Cannot have more PME ranks than total ranks");
857     if (EEL_PME(ir.coulombtype))
858     {
859         GMX_LOG(mdlog.info).appendTextFormatted("Using %d separate PME ranks%s", numPmeOnlyRanks, extraMessage);
860     }
861
862     return numPmeOnlyRanks;
863 }
864
865 /*! \brief Sets the order of the DD dimensions, returns the number of DD dimensions */
866 static int set_dd_dim(const gmx::IVec& numDDCells, const DDSettings& ddSettings, ivec* dims)
867 {
868     int ndim = 0;
869     if (ddSettings.useDDOrderZYX)
870     {
871         /* Decomposition order z,y,x */
872         for (int dim = DIM - 1; dim >= 0; dim--)
873         {
874             if (numDDCells[dim] > 1)
875             {
876                 (*dims)[ndim++] = dim;
877             }
878         }
879     }
880     else
881     {
882         /* Decomposition order x,y,z */
883         for (int dim = 0; dim < DIM; dim++)
884         {
885             if (numDDCells[dim] > 1)
886             {
887                 (*dims)[ndim++] = dim;
888             }
889         }
890     }
891
892     if (ndim == 0)
893     {
894         /* Set dim[0] to avoid extra checks on ndim in several places */
895         (*dims)[0] = XX;
896     }
897
898     return ndim;
899 }
900
901 DDGridSetup getDDGridSetup(const gmx::MDLogger&           mdlog,
902                            const t_commrec*               cr,
903                            const int                      numRanksRequested,
904                            const DomdecOptions&           options,
905                            const DDSettings&              ddSettings,
906                            const DDSystemInfo&            systemInfo,
907                            const real                     cellSizeLimit,
908                            const gmx_mtop_t&              mtop,
909                            const t_inputrec&              ir,
910                            const matrix                   box,
911                            gmx::ArrayRef<const gmx::RVec> xGlobal,
912                            gmx_ddbox_t*                   ddbox)
913 {
914     int numPmeOnlyRanks = getNumPmeOnlyRanksToUse(mdlog, options, mtop, ir, box, numRanksRequested);
915
916     if (ddSettings.request1DAnd1Pulse && (numRanksRequested - numPmeOnlyRanks == 1))
917     {
918         // With only one PP rank, there will not be a need for
919         // GPU-based halo exchange that wants to request that any DD
920         // has only 1 dimension and 1 pulse.
921         return DDGridSetup{};
922     }
923
924     gmx::IVec numDomains;
925     if (options.numCells[XX] > 0)
926     {
927         numDomains                      = gmx::IVec(options.numCells);
928         const ivec numDomainsLegacyIvec = { numDomains[XX], numDomains[YY], numDomains[ZZ] };
929         set_ddbox_cr(*cr, &numDomainsLegacyIvec, ir, box, xGlobal, ddbox);
930     }
931     else
932     {
933         set_ddbox_cr(*cr, nullptr, ir, box, xGlobal, ddbox);
934
935         if (MASTER(cr))
936         {
937             numDomains = optimizeDDCells(mdlog, numRanksRequested, numPmeOnlyRanks, cellSizeLimit,
938                                          ddSettings.request1DAnd1Pulse, mtop, box, *ddbox, ir, systemInfo);
939         }
940     }
941
942     /* Communicate the information set by the master to all ranks */
943     gmx_bcast(sizeof(numDomains), numDomains, cr);
944     if (EEL_PME(ir.coulombtype))
945     {
946         gmx_bcast(sizeof(numPmeOnlyRanks), &numPmeOnlyRanks, cr);
947     }
948
949     DDGridSetup ddGridSetup;
950     ddGridSetup.numPmeOnlyRanks = numPmeOnlyRanks;
951     ddGridSetup.numDomains[XX]  = numDomains[XX];
952     ddGridSetup.numDomains[YY]  = numDomains[YY];
953     ddGridSetup.numDomains[ZZ]  = numDomains[ZZ];
954     ddGridSetup.numDDDimensions = set_dd_dim(numDomains, ddSettings, &ddGridSetup.ddDimensions);
955
956     return ddGridSetup;
957 }