Fix inconsistent OpenMP automation
[alexxy/gromacs.git] / src / programs / mdrun / resource-division.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 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 #include "gmxpre.h"
37
38 #include "resource-division.h"
39
40 #include "config.h"
41
42 #include <assert.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include <algorithm>
47
48 #include "gromacs/legacyheaders/gmx_detect_hardware.h"
49 #include "gromacs/legacyheaders/gmx_omp_nthreads.h"
50 #include "gromacs/legacyheaders/md_logging.h"
51 #include "gromacs/legacyheaders/names.h"
52 #include "gromacs/legacyheaders/types/commrec.h"
53 #include "gromacs/utility/fatalerror.h"
54
55
56 #ifdef GMX_THREAD_MPI
57 /* The minimum number of atoms per tMPI thread. With fewer atoms than this,
58  * the number of threads will get lowered.
59  */
60 static const int min_atoms_per_mpi_thread =  90;
61 static const int min_atoms_per_gpu        = 900;
62 #endif /* GMX_THREAD_MPI */
63
64 /* TODO choose nthreads_omp based on hardware topology
65    when we have a hardware topology detection library */
66 /* First we consider the case of no MPI (1 MPI rank).
67  * In general, when running up to 4 threads, OpenMP should be faster.
68  * Note: on AMD Bulldozer we should avoid running OpenMP over two dies.
69  * On Intel>=Nehalem running OpenMP on a single CPU is always faster,
70  * even on two CPUs it's usually faster (but with many OpenMP threads
71  * it could be faster not to use HT, currently we always use HT).
72  * On Nehalem/Westmere we want to avoid running 16 threads over
73  * two CPUs with HT, so we need a limit<16; thus we use 12.
74  * A reasonable limit for Intel Sandy and Ivy bridge,
75  * not knowing the topology, is 16 threads.
76  * Below we check for Intel and AVX, which for now includes
77  * Sandy/Ivy Bridge, Has/Broadwell. By checking for AVX instead of
78  * model numbers we ensure also future Intel CPUs are covered.
79  */
80 const int nthreads_omp_always_faster_default   =  8;
81 const int nthreads_omp_always_faster_Nehalem   = 12;
82 const int nthreads_omp_always_faster_Intel_AVX = 16;
83 /* For CPU only runs the fastest options are usually MPI or OpenMP only.
84  * With one GPU, using MPI only is almost never optimal, so we need to
85  * compare running pure OpenMP with combined MPI+OpenMP. This means higher
86  * OpenMP threads counts can still be ok. Multiplying the numbers above
87  * by a factor of 2 seems to be a good estimate.
88  */
89 const int nthreads_omp_always_faster_gpu_fac   =  2;
90
91 /* This is the case with MPI (2 or more MPI PP ranks).
92  * By default we will terminate with a fatal error when more than 8
93  * OpenMP thread are (indirectly) requested, since using less threads
94  * nearly always results in better performance.
95  * With thread-mpi and multiple GPUs or one GPU and too many threads
96  * we first try 6 OpenMP threads and then less until the number of MPI ranks
97  * is divisible by the number of GPUs.
98  */
99 #if defined GMX_OPENMP && defined GMX_MPI
100 const int nthreads_omp_mpi_ok_max              =  8;
101 const int nthreads_omp_mpi_ok_min_cpu          =  1;
102 #endif
103 const int nthreads_omp_mpi_ok_min_gpu          =  2;
104 const int nthreads_omp_mpi_target_max          =  6;
105
106
107 #ifdef GMX_USE_OPENCL
108 static const bool bGpuSharingSupported = false;
109 #else
110 static const bool bGpuSharingSupported = true;
111 #endif
112
113
114 static int nthreads_omp_always_faster(gmx_cpuid_t cpuid_info, gmx_bool bUseGPU)
115 {
116     int nth;
117
118     if (gmx_cpuid_vendor(cpuid_info) == GMX_CPUID_VENDOR_INTEL &&
119         gmx_cpuid_feature(cpuid_info, GMX_CPUID_FEATURE_X86_AVX))
120     {
121         nth = nthreads_omp_always_faster_Intel_AVX;
122     }
123     else if (gmx_cpuid_is_intel_nehalem(cpuid_info))
124     {
125         nth = nthreads_omp_always_faster_Nehalem;
126     }
127     else
128     {
129         nth = nthreads_omp_always_faster_default;
130     }
131
132     if (bUseGPU)
133     {
134         nth *= nthreads_omp_always_faster_gpu_fac;
135     }
136
137     nth = std::min(nth, GMX_OPENMP_MAX_THREADS);
138
139     return nth;
140 }
141
142 static int get_tmpi_omp_thread_division(const gmx_hw_info_t *hwinfo,
143                                         const gmx_hw_opt_t  *hw_opt,
144                                         int                  nthreads_tot,
145                                         int                  ngpu)
146 {
147     int nrank;
148
149     assert(nthreads_tot > 0);
150
151     /* There are no separate PME nodes here, as we ensured in
152      * check_and_update_hw_opt that nthreads_tmpi>0 with PME nodes
153      * and a conditional ensures we would not have ended up here.
154      * Note that separate PME nodes might be switched on later.
155      */
156     if (ngpu > 0)
157     {
158         nrank = ngpu;
159         if (nthreads_tot < nrank)
160         {
161             /* #thread < #gpu is very unlikely, but if so: waste gpu(s) */
162             nrank = nthreads_tot;
163         }
164         else if (bGpuSharingSupported &&
165                  (nthreads_tot > nthreads_omp_always_faster(hwinfo->cpuid_info,
166                                                             ngpu > 0) ||
167                   (ngpu > 1 && nthreads_tot/ngpu > nthreads_omp_mpi_target_max)))
168         {
169             /* The high OpenMP thread count will likely result in sub-optimal
170              * performance. Increase the rank count to reduce the thread count
171              * per rank. This will lead to GPU sharing by MPI ranks/threads.
172              */
173             int nshare;
174
175             /* Increase the rank count as long as have we more than 6 OpenMP
176              * threads per rank or the number of hardware threads is not
177              * divisible by the rank count. Don't go below 2 OpenMP threads.
178              */
179             nshare = 1;
180             do
181             {
182                 nshare++;
183                 nrank = ngpu*nshare;
184             }
185             while (nthreads_tot/nrank > nthreads_omp_mpi_target_max ||
186                    (nthreads_tot/(ngpu*(nshare + 1)) >= nthreads_omp_mpi_ok_min_gpu && nthreads_tot % nrank != 0));
187         }
188     }
189     else if (hw_opt->nthreads_omp > 0)
190     {
191         /* Here we could oversubscribe, when we do, we issue a warning later */
192         nrank = std::max(1, nthreads_tot/hw_opt->nthreads_omp);
193     }
194     else
195     {
196         if (nthreads_tot <= nthreads_omp_always_faster(hwinfo->cpuid_info,
197                                                        ngpu > 0))
198         {
199             /* Use pure OpenMP parallelization */
200             nrank = 1;
201         }
202         else
203         {
204             /* Don't use OpenMP parallelization */
205             nrank = nthreads_tot;
206         }
207     }
208
209     return nrank;
210 }
211
212
213 #ifdef GMX_THREAD_MPI
214 /* Get the number of MPI ranks to use for thread-MPI based on how many
215  * were requested, which algorithms we're using,
216  * and how many particles there are.
217  * At the point we have already called check_and_update_hw_opt.
218  * Thus all options should be internally consistent and consistent
219  * with the hardware, except that ntmpi could be larger than #GPU.
220  */
221 int get_nthreads_mpi(const gmx_hw_info_t *hwinfo,
222                      const gmx_hw_opt_t  *hw_opt,
223                      const t_inputrec    *inputrec,
224                      const gmx_mtop_t    *mtop,
225                      const t_commrec     *cr,
226                      FILE                *fplog)
227 {
228     int      nthreads_hw, nthreads_tot_max, nrank, ngpu;
229     int      min_atoms_per_mpi_rank;
230     gmx_bool bCanUseGPU;
231
232     /* Check if an algorithm does not support parallel simulation.  */
233     if (inputrec->eI == eiLBFGS ||
234         inputrec->coulombtype == eelEWALD)
235     {
236         md_print_warn(cr, fplog, "The integration or electrostatics algorithm doesn't support parallel runs. Using a single thread-MPI thread.\n");
237         if (hw_opt->nthreads_tmpi > 1)
238         {
239             gmx_fatal(FARGS, "You asked for more than 1 thread-MPI thread, but an algorithm doesn't support that");
240         }
241
242         return 1;
243     }
244
245     if (hw_opt->nthreads_tmpi > 0)
246     {
247         /* Trivial, return right away */
248         return hw_opt->nthreads_tmpi;
249     }
250
251     nthreads_hw = hwinfo->nthreads_hw_avail;
252
253     if (nthreads_hw <= 0)
254     {
255         /* This should normally not happen, but if it does, we handle it */
256         gmx_fatal(FARGS, "The number of available hardware threads can not be detected, please specify the number of MPI ranks and the number of OpenMP threads (if supported) manually with options -ntmpi and -ntomp, respectively");
257     }
258
259     /* How many total (#tMPI*#OpenMP) threads can we start? */
260     if (hw_opt->nthreads_tot > 0)
261     {
262         nthreads_tot_max = hw_opt->nthreads_tot;
263     }
264     else
265     {
266         nthreads_tot_max = nthreads_hw;
267     }
268
269     bCanUseGPU = (inputrec->cutoff_scheme == ecutsVERLET &&
270                   hwinfo->gpu_info.n_dev_compatible > 0);
271     if (bCanUseGPU)
272     {
273         ngpu = hwinfo->gpu_info.n_dev_compatible;
274     }
275     else
276     {
277         ngpu = 0;
278     }
279
280     if (inputrec->cutoff_scheme == ecutsGROUP)
281     {
282         /* We checked this before, but it doesn't hurt to do it once more */
283         assert(hw_opt->nthreads_omp == 1);
284     }
285
286     nrank =
287         get_tmpi_omp_thread_division(hwinfo, hw_opt, nthreads_tot_max, ngpu);
288
289     if (inputrec->eI == eiNM || EI_TPI(inputrec->eI))
290     {
291         /* Dims/steps are divided over the nodes iso splitting the atoms.
292          * With NM we can't have more ranks than #atoms*#dim. With TPI it's
293          * unlikely we have fewer atoms than ranks, and if so, communication
294          * would become a bottleneck, so we set the limit to 1 atom/rank.
295          */
296         min_atoms_per_mpi_rank = 1;
297     }
298     else
299     {
300         if (bCanUseGPU)
301         {
302             min_atoms_per_mpi_rank = min_atoms_per_gpu;
303         }
304         else
305         {
306             min_atoms_per_mpi_rank = min_atoms_per_mpi_thread;
307         }
308     }
309
310     if (mtop->natoms/nrank < min_atoms_per_mpi_rank)
311     {
312         int nrank_new;
313
314         /* the rank number was chosen automatically, but there are too few
315            atoms per rank, so we need to reduce the rank count */
316         nrank_new = std::max(1, mtop->natoms/min_atoms_per_mpi_rank);
317
318         /* Avoid partial use of Hyper-Threading */
319         if (gmx_cpuid_x86_smt(hwinfo->cpuid_info) == GMX_CPUID_X86_SMT_ENABLED &&
320             nrank_new > nthreads_hw/2 && nrank_new < nthreads_hw)
321         {
322             nrank_new = nthreads_hw/2;
323         }
324
325         /* If the user specified the total thread count, ensure this is
326          * divisible by the number of ranks.
327          * It is quite likely that we have too many total threads compared
328          * to the size of the system, but if the user asked for this many
329          * threads we should respect that.
330          */
331         while (hw_opt->nthreads_tot > 0 &&
332                hw_opt->nthreads_tot % nrank_new != 0)
333         {
334             nrank_new--;
335         }
336
337         /* Avoid large prime numbers in the rank count */
338         if (nrank_new >= 6)
339         {
340             /* Use only 6,8,10 with additional factors of 2 */
341             int fac;
342
343             fac = 2;
344             while (3*fac*2 <= nrank_new)
345             {
346                 fac *= 2;
347             }
348
349             nrank_new = (nrank_new/fac)*fac;
350         }
351         else
352         {
353             /* Avoid 5, since small system won't fit 5 domains along
354              * a dimension. This might lead to waisting some cores, but this
355              * will have a small impact in this regime of very small systems.
356              */
357             if (nrank_new == 5)
358             {
359                 nrank_new = 4;
360             }
361         }
362
363         nrank = nrank_new;
364
365         fprintf(stderr, "\n");
366         fprintf(stderr, "NOTE: Parallelization is limited by the small number of atoms,\n");
367         fprintf(stderr, "      only starting %d thread-MPI ranks.\n", nrank);
368         fprintf(stderr, "      You can use the -nt and/or -ntmpi option to optimize the number of threads.\n\n");
369     }
370
371     return nrank;
372 }
373 #endif /* GMX_THREAD_MPI */
374
375
376 void check_resource_division_efficiency(const gmx_hw_info_t *hwinfo,
377                                         const gmx_hw_opt_t  *hw_opt,
378                                         gmx_bool             bNTOptSet,
379                                         t_commrec           *cr,
380                                         FILE                *fplog)
381 {
382 #if defined GMX_OPENMP && defined GMX_MPI
383     int         nth_omp_min, nth_omp_max, ngpu;
384     char        buf[1000];
385 #ifdef GMX_THREAD_MPI
386     const char *mpi_option = " (option -ntmpi)";
387 #else
388     const char *mpi_option = "";
389 #endif
390
391     /* This function should be called after thread-MPI (when configured) and
392      * OpenMP have been initialized. Check that here.
393      */
394 #ifdef GMX_THREAD_MPI
395     assert(nthreads_omp_always_faster_default >= nthreads_omp_mpi_ok_max);
396     assert(hw_opt->nthreads_tmpi >= 1);
397 #endif
398     assert(gmx_omp_nthreads_get(emntDefault) >= 1);
399
400     nth_omp_min = gmx_omp_nthreads_get(emntDefault);
401     nth_omp_max = gmx_omp_nthreads_get(emntDefault);
402     ngpu        = hw_opt->gpu_opt.n_dev_use;
403
404     /* Thread-MPI seems to have a bug with reduce on 1 node, so use a cond. */
405     if (cr->nnodes + cr->npmenodes > 1)
406     {
407         int count[3], count_max[3];
408
409         count[0] = -nth_omp_min;
410         count[1] =  nth_omp_max;
411         count[2] =  ngpu;
412
413         MPI_Allreduce(count, count_max, 3, MPI_INT, MPI_MAX, cr->mpi_comm_mysim);
414
415         /* In case of an inhomogeneous run setup we use the maximum counts */
416         nth_omp_min = -count_max[0];
417         nth_omp_max =  count_max[1];
418         ngpu        =  count_max[2];
419     }
420
421     int nthreads_omp_mpi_ok_min;
422
423     if (ngpu == 0)
424     {
425         nthreads_omp_mpi_ok_min = nthreads_omp_mpi_ok_min_cpu;
426     }
427     else
428     {
429         /* With GPUs we set the minimum number of OpenMP threads to 2 to catch
430          * cases where the user specifies #ranks == #cores.
431          */
432         nthreads_omp_mpi_ok_min = nthreads_omp_mpi_ok_min_gpu;
433     }
434
435     if (DOMAINDECOMP(cr) && cr->nnodes > 1)
436     {
437         if (nth_omp_max < nthreads_omp_mpi_ok_min ||
438             (!(ngpu > 0 && !bGpuSharingSupported) &&
439              nth_omp_max > nthreads_omp_mpi_ok_max))
440         {
441             /* Note that we print target_max here, not ok_max */
442             sprintf(buf, "Your choice of number of MPI ranks and amount of resources results in using %d OpenMP threads per rank, which is most likely inefficient. The optimum is usually between %d and %d threads per rank.",
443                     nth_omp_max,
444                     nthreads_omp_mpi_ok_min,
445                     nthreads_omp_mpi_target_max);
446
447             if (bNTOptSet)
448             {
449                 md_print_warn(cr, fplog, "NOTE: %s\n", buf);
450             }
451             else
452             {
453                 /* This fatal error, and the one below, is nasty, but it's
454                  * probably the only way to ensure that all users don't waste
455                  * a lot of resources, since many users don't read logs/stderr.
456                  */
457                 gmx_fatal(FARGS, "%s If you want to run with this setup, specify the -ntomp option. But we suggest to change the number of MPI ranks%s.", buf, mpi_option);
458             }
459         }
460     }
461     else
462     {
463         /* No domain decomposition (or only one domain) */
464         if (!(ngpu > 0 && !bGpuSharingSupported) &&
465             nth_omp_max > nthreads_omp_always_faster(hwinfo->cpuid_info, ngpu > 0))
466         {
467             /* To arrive here, the user/system set #ranks and/or #OMPthreads */
468             gmx_bool bEnvSet;
469             char     buf2[256];
470
471             bEnvSet = (getenv("OMP_NUM_THREADS") != NULL);
472
473             if (bNTOptSet || bEnvSet)
474             {
475                 sprintf(buf2, "You requested %d OpenMP threads", nth_omp_max);
476             }
477             else
478             {
479                 sprintf(buf2, "Your choice of %d MPI rank%s and the use of %d total threads %sleads to the use of %d OpenMP threads",
480                         cr->nnodes + cr->npmenodes,
481                         cr->nnodes + cr->npmenodes == 1 ? "" : "s",
482                         hw_opt->nthreads_tot > 0 ? hw_opt->nthreads_tot : hwinfo->nthreads_hw_avail,
483                         hwinfo->nphysicalnode > 1 ? "on a node " : "",
484                         nth_omp_max);
485             }
486             sprintf(buf, "%s, whereas we expect the optimum to be with more MPI ranks with %d to %d OpenMP threads.",
487                     buf2, nthreads_omp_mpi_ok_min, nthreads_omp_mpi_target_max);
488
489             /* We can not quit with a fatal error when OMP_NUM_THREADS is set
490              * with different values per rank or node, since in that case
491              * the user can not set -ntomp to override the error.
492              */
493             if (bNTOptSet || (bEnvSet && nth_omp_min != nth_omp_max))
494             {
495                 md_print_warn(cr, fplog, "NOTE: %s\n", buf);
496             }
497             else
498             {
499                 gmx_fatal(FARGS, "%s If you want to run with this many OpenMP threads, specify the -ntomp option. But we suggest to increase the number of MPI ranks%s.", buf, mpi_option);
500             }
501         }
502     }
503 #else /* GMX_OPENMP && GMX_MPI */
504       /* No OpenMP and/or MPI: it doesn't make much sense to check */
505     GMX_UNUSED_VALUE(hw_opt);
506     GMX_UNUSED_VALUE(bNTOptSet);
507     /* Check if we have more than 1 physical core, if detected,
508      * or more than 1 hardware thread if physical cores were not detected.
509      */
510     if ((hwinfo->ncore > 1) ||
511         (hwinfo->ncore == 0 && hwinfo->nthreads_hw_avail > 1))
512     {
513         md_print_warn(cr, fplog, "NOTE: GROMACS was compiled without OpenMP and (thread-)MPI support, can only use a single CPU core\n");
514     }
515 #endif /* GMX_OPENMP && GMX_MPI */
516 }
517
518
519 static void print_hw_opt(FILE *fp, const gmx_hw_opt_t *hw_opt)
520 {
521     fprintf(fp, "hw_opt: nt %d ntmpi %d ntomp %d ntomp_pme %d gpu_id '%s'\n",
522             hw_opt->nthreads_tot,
523             hw_opt->nthreads_tmpi,
524             hw_opt->nthreads_omp,
525             hw_opt->nthreads_omp_pme,
526             hw_opt->gpu_opt.gpu_id != NULL ? hw_opt->gpu_opt.gpu_id : "");
527 }
528
529 /* Checks we can do when we don't (yet) know the cut-off scheme */
530 void check_and_update_hw_opt_1(gmx_hw_opt_t *hw_opt,
531                                gmx_bool      bIsSimMaster)
532 {
533     gmx_omp_nthreads_read_env(&hw_opt->nthreads_omp, bIsSimMaster);
534
535 #ifndef GMX_THREAD_MPI
536     if (hw_opt->nthreads_tot > 0)
537     {
538         gmx_fatal(FARGS, "Setting the total number of threads is only supported with thread-MPI and GROMACS was compiled without thread-MPI");
539     }
540     if (hw_opt->nthreads_tmpi > 0)
541     {
542         gmx_fatal(FARGS, "Setting the number of thread-MPI threads is only supported with thread-MPI and GROMACS was compiled without thread-MPI");
543     }
544 #endif
545
546 #ifndef GMX_OPENMP
547     if (hw_opt->nthreads_omp > 1)
548     {
549         gmx_fatal(FARGS, "More than 1 OpenMP thread requested, but GROMACS was compiled without OpenMP support");
550     }
551     hw_opt->nthreads_omp = 1;
552 #endif
553
554     if (hw_opt->nthreads_tot > 0 && hw_opt->nthreads_omp_pme <= 0)
555     {
556         /* We have the same number of OpenMP threads for PP and PME processes,
557          * thus we can perform several consistency checks.
558          */
559         if (hw_opt->nthreads_tmpi > 0 &&
560             hw_opt->nthreads_omp > 0 &&
561             hw_opt->nthreads_tot != hw_opt->nthreads_tmpi*hw_opt->nthreads_omp)
562         {
563             gmx_fatal(FARGS, "The total number of threads requested (%d) does not match the thread-MPI threads (%d) times the OpenMP threads (%d) requested",
564                       hw_opt->nthreads_tot, hw_opt->nthreads_tmpi, hw_opt->nthreads_omp);
565         }
566
567         if (hw_opt->nthreads_tmpi > 0 &&
568             hw_opt->nthreads_tot % hw_opt->nthreads_tmpi != 0)
569         {
570             gmx_fatal(FARGS, "The total number of threads requested (%d) is not divisible by the number of thread-MPI threads requested (%d)",
571                       hw_opt->nthreads_tot, hw_opt->nthreads_tmpi);
572         }
573
574         if (hw_opt->nthreads_omp > 0 &&
575             hw_opt->nthreads_tot % hw_opt->nthreads_omp != 0)
576         {
577             gmx_fatal(FARGS, "The total number of threads requested (%d) is not divisible by the number of OpenMP threads requested (%d)",
578                       hw_opt->nthreads_tot, hw_opt->nthreads_omp);
579         }
580
581         if (hw_opt->nthreads_tmpi > 0 &&
582             hw_opt->nthreads_omp <= 0)
583         {
584             hw_opt->nthreads_omp = hw_opt->nthreads_tot/hw_opt->nthreads_tmpi;
585         }
586     }
587
588 #ifndef GMX_OPENMP
589     if (hw_opt->nthreads_omp > 1)
590     {
591         gmx_fatal(FARGS, "OpenMP threads are requested, but GROMACS was compiled without OpenMP support");
592     }
593 #endif
594
595     if (hw_opt->nthreads_omp_pme > 0 && hw_opt->nthreads_omp <= 0)
596     {
597         gmx_fatal(FARGS, "You need to specify -ntomp in addition to -ntomp_pme");
598     }
599
600     if (hw_opt->nthreads_tot == 1)
601     {
602         hw_opt->nthreads_tmpi = 1;
603
604         if (hw_opt->nthreads_omp > 1)
605         {
606             gmx_fatal(FARGS, "You requested %d OpenMP threads with %d total threads",
607                       hw_opt->nthreads_tmpi, hw_opt->nthreads_tot);
608         }
609         hw_opt->nthreads_omp = 1;
610     }
611
612     if (hw_opt->nthreads_omp_pme <= 0 && hw_opt->nthreads_omp > 0)
613     {
614         hw_opt->nthreads_omp_pme = hw_opt->nthreads_omp;
615     }
616
617     /* Parse GPU IDs, if provided.
618      * We check consistency with the tMPI thread count later.
619      */
620     gmx_parse_gpu_ids(&hw_opt->gpu_opt);
621
622 #ifdef GMX_THREAD_MPI
623     if (hw_opt->gpu_opt.n_dev_use > 0 && hw_opt->nthreads_tmpi == 0)
624     {
625         /* Set the number of MPI threads equal to the number of GPUs */
626         hw_opt->nthreads_tmpi = hw_opt->gpu_opt.n_dev_use;
627
628         if (hw_opt->nthreads_tot > 0 &&
629             hw_opt->nthreads_tmpi > hw_opt->nthreads_tot)
630         {
631             /* We have more GPUs than total threads requested.
632              * We choose to (later) generate a mismatch error,
633              * instead of launching more threads than requested.
634              */
635             hw_opt->nthreads_tmpi = hw_opt->nthreads_tot;
636         }
637     }
638 #endif
639
640     if (debug)
641     {
642         print_hw_opt(debug, hw_opt);
643     }
644 }
645
646 /* Checks we can do when we know the cut-off scheme */
647 void check_and_update_hw_opt_2(gmx_hw_opt_t *hw_opt,
648                                int           cutoff_scheme)
649 {
650     if (cutoff_scheme == ecutsGROUP)
651     {
652         /* We only have OpenMP support for PME only nodes */
653         if (hw_opt->nthreads_omp > 1)
654         {
655             gmx_fatal(FARGS, "OpenMP threads have been requested with cut-off scheme %s, but these are only supported with cut-off scheme %s",
656                       ecutscheme_names[cutoff_scheme],
657                       ecutscheme_names[ecutsVERLET]);
658         }
659         hw_opt->nthreads_omp = 1;
660     }
661
662     if (hw_opt->nthreads_omp_pme <= 0 && hw_opt->nthreads_omp > 0)
663     {
664         hw_opt->nthreads_omp_pme = hw_opt->nthreads_omp;
665     }
666
667     if (debug)
668     {
669         print_hw_opt(debug, hw_opt);
670     }
671 }