Add grompp warning with use of GROMOS FF
[alexxy/gromacs.git] / src / gromacs / mdrun / runner.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) 2011,2012,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 /*! \internal \file
38  *
39  * \brief Implements the MD runner routine calling all integrators.
40  *
41  * \author David van der Spoel <david.vanderspoel@icm.uu.se>
42  * \ingroup module_mdrun
43  */
44 #include "gmxpre.h"
45
46 #include "runner.h"
47
48 #include "config.h"
49
50 #include <cassert>
51 #include <cinttypes>
52 #include <csignal>
53 #include <cstdlib>
54 #include <cstring>
55
56 #include <algorithm>
57 #include <memory>
58
59 #include "gromacs/commandline/filenm.h"
60 #include "gromacs/domdec/domdec.h"
61 #include "gromacs/domdec/domdec_struct.h"
62 #include "gromacs/domdec/localatomsetmanager.h"
63 #include "gromacs/domdec/partition.h"
64 #include "gromacs/ewald/ewald_utils.h"
65 #include "gromacs/ewald/pme.h"
66 #include "gromacs/ewald/pme_gpu_program.h"
67 #include "gromacs/fileio/checkpoint.h"
68 #include "gromacs/fileio/gmxfio.h"
69 #include "gromacs/fileio/oenv.h"
70 #include "gromacs/fileio/tpxio.h"
71 #include "gromacs/gmxlib/network.h"
72 #include "gromacs/gmxlib/nrnb.h"
73 #include "gromacs/gpu_utils/clfftinitializer.h"
74 #include "gromacs/gpu_utils/gpu_utils.h"
75 #include "gromacs/hardware/cpuinfo.h"
76 #include "gromacs/hardware/detecthardware.h"
77 #include "gromacs/hardware/printhardware.h"
78 #include "gromacs/imd/imd.h"
79 #include "gromacs/listed_forces/disre.h"
80 #include "gromacs/listed_forces/gpubonded.h"
81 #include "gromacs/listed_forces/orires.h"
82 #include "gromacs/math/functions.h"
83 #include "gromacs/math/utilities.h"
84 #include "gromacs/math/vec.h"
85 #include "gromacs/mdlib/boxdeformation.h"
86 #include "gromacs/mdlib/broadcaststructs.h"
87 #include "gromacs/mdlib/calc_verletbuf.h"
88 #include "gromacs/mdlib/dispersioncorrection.h"
89 #include "gromacs/mdlib/enerdata_utils.h"
90 #include "gromacs/mdlib/force.h"
91 #include "gromacs/mdlib/forcerec.h"
92 #include "gromacs/mdlib/gmx_omp_nthreads.h"
93 #include "gromacs/mdlib/makeconstraints.h"
94 #include "gromacs/mdlib/md_support.h"
95 #include "gromacs/mdlib/mdatoms.h"
96 #include "gromacs/mdlib/membed.h"
97 #include "gromacs/mdlib/ppforceworkload.h"
98 #include "gromacs/mdlib/qmmm.h"
99 #include "gromacs/mdlib/sighandler.h"
100 #include "gromacs/mdlib/stophandler.h"
101 #include "gromacs/mdrun/mdmodules.h"
102 #include "gromacs/mdrun/simulationcontext.h"
103 #include "gromacs/mdrunutility/handlerestart.h"
104 #include "gromacs/mdrunutility/logging.h"
105 #include "gromacs/mdrunutility/multisim.h"
106 #include "gromacs/mdrunutility/printtime.h"
107 #include "gromacs/mdrunutility/threadaffinity.h"
108 #include "gromacs/mdtypes/commrec.h"
109 #include "gromacs/mdtypes/enerdata.h"
110 #include "gromacs/mdtypes/fcdata.h"
111 #include "gromacs/mdtypes/inputrec.h"
112 #include "gromacs/mdtypes/md_enums.h"
113 #include "gromacs/mdtypes/mdrunoptions.h"
114 #include "gromacs/mdtypes/observableshistory.h"
115 #include "gromacs/mdtypes/state.h"
116 #include "gromacs/nbnxm/gpu_data_mgmt.h"
117 #include "gromacs/nbnxm/nbnxm.h"
118 #include "gromacs/nbnxm/pairlist_tuning.h"
119 #include "gromacs/pbcutil/pbc.h"
120 #include "gromacs/pulling/output.h"
121 #include "gromacs/pulling/pull.h"
122 #include "gromacs/pulling/pull_rotation.h"
123 #include "gromacs/restraint/manager.h"
124 #include "gromacs/restraint/restraintmdmodule.h"
125 #include "gromacs/restraint/restraintpotential.h"
126 #include "gromacs/swap/swapcoords.h"
127 #include "gromacs/taskassignment/decidegpuusage.h"
128 #include "gromacs/taskassignment/resourcedivision.h"
129 #include "gromacs/taskassignment/taskassignment.h"
130 #include "gromacs/taskassignment/usergpuids.h"
131 #include "gromacs/timing/gpu_timing.h"
132 #include "gromacs/timing/wallcycle.h"
133 #include "gromacs/timing/wallcyclereporting.h"
134 #include "gromacs/topology/mtop_util.h"
135 #include "gromacs/trajectory/trajectoryframe.h"
136 #include "gromacs/utility/basenetwork.h"
137 #include "gromacs/utility/cstringutil.h"
138 #include "gromacs/utility/exceptions.h"
139 #include "gromacs/utility/fatalerror.h"
140 #include "gromacs/utility/filestream.h"
141 #include "gromacs/utility/gmxassert.h"
142 #include "gromacs/utility/gmxmpi.h"
143 #include "gromacs/utility/logger.h"
144 #include "gromacs/utility/loggerbuilder.h"
145 #include "gromacs/utility/physicalnodecommunicator.h"
146 #include "gromacs/utility/pleasecite.h"
147 #include "gromacs/utility/programcontext.h"
148 #include "gromacs/utility/smalloc.h"
149 #include "gromacs/utility/stringutil.h"
150
151 #include "legacysimulator.h"
152 #include "replicaexchange.h"
153
154 #if GMX_FAHCORE
155 #include "corewrap.h"
156 #endif
157
158 namespace gmx
159 {
160
161 /*! \brief Log if development feature flags are encountered
162  *
163  * The use of dev features indicated by environment variables is logged
164  * in order to ensure that runs with such featrues enabled can be identified
165  * from their log and standard output.
166  *
167  * \param[in]  mdlog        Logger object.
168  */
169 static void reportDevelopmentFeatures(const gmx::MDLogger &mdlog)
170 {
171     const bool enableGpuBufOps       = (getenv("GMX_USE_GPU_BUFFER_OPS") != nullptr);
172     const bool useGpuUpdateConstrain = (getenv("GMX_UPDATE_CONSTRAIN_GPU") != nullptr);
173
174     if (enableGpuBufOps)
175     {
176         GMX_LOG(mdlog.warning).asParagraph().appendTextFormatted(
177                 "NOTE: This run uses the 'GPU buffer ops' feature, enabled by the GMX_USE_GPU_BUFFER_OPS environment variable.");
178     }
179
180     if (useGpuUpdateConstrain)
181     {
182         GMX_LOG(mdlog.warning).asParagraph().appendTextFormatted(
183                 "NOTE: This run uses the 'GPU update/constraints' feature, enabled by the GMX_UPDATE_CONSTRAIN_GPU environment variable.");
184     }
185 }
186
187 /*! \brief Barrier for safe simultaneous thread access to mdrunner data
188  *
189  * Used to ensure that the master thread does not modify mdrunner during copy
190  * on the spawned threads. */
191 static void threadMpiMdrunnerAccessBarrier()
192 {
193 #if GMX_THREAD_MPI
194     MPI_Barrier(MPI_COMM_WORLD);
195 #endif
196 }
197
198 Mdrunner Mdrunner::cloneOnSpawnedThread() const
199 {
200     auto newRunner = Mdrunner(std::make_unique<MDModules>());
201
202     // All runners in the same process share a restraint manager resource because it is
203     // part of the interface to the client code, which is associated only with the
204     // original thread. Handles to the same resources can be obtained by copy.
205     {
206         newRunner.restraintManager_ = std::make_unique<RestraintManager>(*restraintManager_);
207     }
208
209     // Copy original cr pointer before master thread can pass the thread barrier
210     newRunner.cr  = reinitialize_commrec_for_this_thread(cr);
211
212     // Copy members of master runner.
213     // \todo Replace with builder when Simulation context and/or runner phases are better defined.
214     // Ref https://redmine.gromacs.org/issues/2587 and https://redmine.gromacs.org/issues/2375
215     newRunner.hw_opt    = hw_opt;
216     newRunner.filenames = filenames;
217
218     newRunner.oenv                = oenv;
219     newRunner.mdrunOptions        = mdrunOptions;
220     newRunner.domdecOptions       = domdecOptions;
221     newRunner.nbpu_opt            = nbpu_opt;
222     newRunner.pme_opt             = pme_opt;
223     newRunner.pme_fft_opt         = pme_fft_opt;
224     newRunner.bonded_opt          = bonded_opt;
225     newRunner.nstlist_cmdline     = nstlist_cmdline;
226     newRunner.replExParams        = replExParams;
227     newRunner.pforce              = pforce;
228     newRunner.ms                  = ms;
229     newRunner.startingBehavior    = startingBehavior;
230     newRunner.stopHandlerBuilder_ = std::make_unique<StopHandlerBuilder>(*stopHandlerBuilder_);
231
232     threadMpiMdrunnerAccessBarrier();
233
234     GMX_RELEASE_ASSERT(!MASTER(newRunner.cr), "cloneOnSpawnedThread should only be called on spawned threads");
235
236     return newRunner;
237 }
238
239 /*! \brief The callback used for running on spawned threads.
240  *
241  * Obtains the pointer to the master mdrunner object from the one
242  * argument permitted to the thread-launch API call, copies it to make
243  * a new runner for this thread, reinitializes necessary data, and
244  * proceeds to the simulation. */
245 static void mdrunner_start_fn(const void *arg)
246 {
247     try
248     {
249         auto masterMdrunner = reinterpret_cast<const gmx::Mdrunner *>(arg);
250         /* copy the arg list to make sure that it's thread-local. This
251            doesn't copy pointed-to items, of course; fnm, cr and fplog
252            are reset in the call below, all others should be const. */
253         gmx::Mdrunner mdrunner = masterMdrunner->cloneOnSpawnedThread();
254         mdrunner.mdrunner();
255     }
256     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
257 }
258
259
260 /*! \brief Start thread-MPI threads.
261  *
262  * Called by mdrunner() to start a specific number of threads
263  * (including the main thread) for thread-parallel runs. This in turn
264  * calls mdrunner() for each thread. All options are the same as for
265  * mdrunner(). */
266 t_commrec *Mdrunner::spawnThreads(int numThreadsToLaunch) const
267 {
268
269     /* first check whether we even need to start tMPI */
270     if (numThreadsToLaunch < 2)
271     {
272         return cr;
273     }
274
275 #if GMX_THREAD_MPI
276     /* now spawn new threads that start mdrunner_start_fn(), while
277        the main thread returns. Thread affinity is handled later. */
278     if (tMPI_Init_fn(TRUE, numThreadsToLaunch, TMPI_AFFINITY_NONE,
279                      mdrunner_start_fn, static_cast<const void*>(this)) != TMPI_SUCCESS)
280     {
281         GMX_THROW(gmx::InternalError("Failed to spawn thread-MPI threads"));
282     }
283
284     threadMpiMdrunnerAccessBarrier();
285 #else
286     GMX_UNUSED_VALUE(mdrunner_start_fn);
287 #endif
288
289     return reinitialize_commrec_for_this_thread(cr);
290 }
291
292 }  // namespace gmx
293
294 /*! \brief Initialize variables for Verlet scheme simulation */
295 static void prepare_verlet_scheme(FILE                           *fplog,
296                                   t_commrec                      *cr,
297                                   t_inputrec                     *ir,
298                                   int                             nstlist_cmdline,
299                                   const gmx_mtop_t               *mtop,
300                                   const matrix                    box,
301                                   bool                            makeGpuPairList,
302                                   const gmx::CpuInfo             &cpuinfo)
303 {
304     /* For NVE simulations, we will retain the initial list buffer */
305     if (EI_DYNAMICS(ir->eI) &&
306         ir->verletbuf_tol > 0 &&
307         !(EI_MD(ir->eI) && ir->etc == etcNO))
308     {
309         /* Update the Verlet buffer size for the current run setup */
310
311         /* Here we assume SIMD-enabled kernels are being used. But as currently
312          * calc_verlet_buffer_size gives the same results for 4x8 and 4x4
313          * and 4x2 gives a larger buffer than 4x4, this is ok.
314          */
315         ListSetupType      listType  = (makeGpuPairList ? ListSetupType::Gpu : ListSetupType::CpuSimdWhenSupported);
316         VerletbufListSetup listSetup = verletbufGetSafeListSetup(listType);
317
318         const real         rlist_new =
319             calcVerletBufferSize(*mtop, det(box), *ir, ir->nstlist, ir->nstlist - 1, -1, listSetup);
320
321         if (rlist_new != ir->rlist)
322         {
323             if (fplog != nullptr)
324             {
325                 fprintf(fplog, "\nChanging rlist from %g to %g for non-bonded %dx%d atom kernels\n\n",
326                         ir->rlist, rlist_new,
327                         listSetup.cluster_size_i, listSetup.cluster_size_j);
328             }
329             ir->rlist     = rlist_new;
330         }
331     }
332
333     if (nstlist_cmdline > 0 && (!EI_DYNAMICS(ir->eI) || ir->verletbuf_tol <= 0))
334     {
335         gmx_fatal(FARGS, "Can not set nstlist without %s",
336                   !EI_DYNAMICS(ir->eI) ? "dynamics" : "verlet-buffer-tolerance");
337     }
338
339     if (EI_DYNAMICS(ir->eI))
340     {
341         /* Set or try nstlist values */
342         increaseNstlist(fplog, cr, ir, nstlist_cmdline, mtop, box, makeGpuPairList, cpuinfo);
343     }
344 }
345
346 /*! \brief Override the nslist value in inputrec
347  *
348  * with value passed on the command line (if any)
349  */
350 static void override_nsteps_cmdline(const gmx::MDLogger &mdlog,
351                                     int64_t              nsteps_cmdline,
352                                     t_inputrec          *ir)
353 {
354     assert(ir);
355
356     /* override with anything else than the default -2 */
357     if (nsteps_cmdline > -2)
358     {
359         char sbuf_steps[STEPSTRSIZE];
360         char sbuf_msg[STRLEN];
361
362         ir->nsteps = nsteps_cmdline;
363         if (EI_DYNAMICS(ir->eI) && nsteps_cmdline != -1)
364         {
365             sprintf(sbuf_msg, "Overriding nsteps with value passed on the command line: %s steps, %.3g ps",
366                     gmx_step_str(nsteps_cmdline, sbuf_steps),
367                     fabs(nsteps_cmdline*ir->delta_t));
368         }
369         else
370         {
371             sprintf(sbuf_msg, "Overriding nsteps with value passed on the command line: %s steps",
372                     gmx_step_str(nsteps_cmdline, sbuf_steps));
373         }
374
375         GMX_LOG(mdlog.warning).asParagraph().appendText(sbuf_msg);
376     }
377     else if (nsteps_cmdline < -2)
378     {
379         gmx_fatal(FARGS, "Invalid nsteps value passed on the command line: %" PRId64,
380                   nsteps_cmdline);
381     }
382     /* Do nothing if nsteps_cmdline == -2 */
383 }
384
385 namespace gmx
386 {
387
388 /*! \brief Return whether GPU acceleration of nonbondeds is supported with the given settings.
389  *
390  * If not, and if a warning may be issued, logs a warning about
391  * falling back to CPU code. With thread-MPI, only the first
392  * call to this function should have \c issueWarning true. */
393 static bool gpuAccelerationOfNonbondedIsUseful(const MDLogger   &mdlog,
394                                                const t_inputrec *ir,
395                                                bool              issueWarning)
396 {
397     if (ir->opts.ngener - ir->nwall > 1)
398     {
399         /* The GPU code does not support more than one energy group.
400          * If the user requested GPUs explicitly, a fatal error is given later.
401          */
402         if (issueWarning)
403         {
404             GMX_LOG(mdlog.warning).asParagraph()
405                 .appendText("Multiple energy groups is not implemented for GPUs, falling back to the CPU. "
406                             "For better performance, run on the GPU without energy groups and then do "
407                             "gmx mdrun -rerun option on the trajectory with an energy group .tpr file.");
408         }
409         return false;
410     }
411     return true;
412 }
413
414 //! Initializes the logger for mdrun.
415 static gmx::LoggerOwner buildLogger(FILE *fplog, const t_commrec *cr)
416 {
417     gmx::LoggerBuilder builder;
418     if (fplog != nullptr)
419     {
420         builder.addTargetFile(gmx::MDLogger::LogLevel::Info, fplog);
421     }
422     if (cr == nullptr || SIMMASTER(cr))
423     {
424         builder.addTargetStream(gmx::MDLogger::LogLevel::Warning,
425                                 &gmx::TextOutputFile::standardError());
426     }
427     return builder.build();
428 }
429
430 //! Make a TaskTarget from an mdrun argument string.
431 static TaskTarget findTaskTarget(const char *optionString)
432 {
433     TaskTarget returnValue = TaskTarget::Auto;
434
435     if (strncmp(optionString, "auto", 3) == 0)
436     {
437         returnValue = TaskTarget::Auto;
438     }
439     else if (strncmp(optionString, "cpu", 3) == 0)
440     {
441         returnValue = TaskTarget::Cpu;
442     }
443     else if (strncmp(optionString, "gpu", 3) == 0)
444     {
445         returnValue = TaskTarget::Gpu;
446     }
447     else
448     {
449         GMX_ASSERT(false, "Option string should have been checked for sanity already");
450     }
451
452     return returnValue;
453 }
454
455 //! Finish run, aggregate data to print performance info.
456 static void finish_run(FILE *fplog,
457                        const gmx::MDLogger &mdlog,
458                        const t_commrec *cr,
459                        const t_inputrec *inputrec,
460                        t_nrnb nrnb[], gmx_wallcycle_t wcycle,
461                        gmx_walltime_accounting_t walltime_accounting,
462                        nonbonded_verlet_t *nbv,
463                        const gmx_pme_t *pme,
464                        gmx_bool bWriteStat)
465 {
466     double  delta_t  = 0;
467     double  nbfs     = 0, mflop = 0;
468     double  elapsed_time,
469             elapsed_time_over_all_ranks,
470             elapsed_time_over_all_threads,
471             elapsed_time_over_all_threads_over_all_ranks;
472     /* Control whether it is valid to print a report. Only the
473        simulation master may print, but it should not do so if the run
474        terminated e.g. before a scheduled reset step. This is
475        complicated by the fact that PME ranks are unaware of the
476        reason why they were sent a pmerecvqxFINISH. To avoid
477        communication deadlocks, we always do the communication for the
478        report, even if we've decided not to write the report, because
479        how long it takes to finish the run is not important when we've
480        decided not to report on the simulation performance.
481
482        Further, we only report performance for dynamical integrators,
483        because those are the only ones for which we plan to
484        consider doing any optimizations. */
485     bool printReport = EI_DYNAMICS(inputrec->eI) && SIMMASTER(cr);
486
487     if (printReport && !walltime_accounting_get_valid_finish(walltime_accounting))
488     {
489         GMX_LOG(mdlog.warning).asParagraph().appendText("Simulation ended prematurely, no performance report will be written.");
490         printReport = false;
491     }
492
493     t_nrnb                  *nrnb_tot;
494     std::unique_ptr<t_nrnb>  nrnbTotalStorage;
495     if (cr->nnodes > 1)
496     {
497         nrnbTotalStorage = std::make_unique<t_nrnb>();
498         nrnb_tot         = nrnbTotalStorage.get();
499 #if GMX_MPI
500         MPI_Allreduce(nrnb->n, nrnb_tot->n, eNRNB, MPI_DOUBLE, MPI_SUM,
501                       cr->mpi_comm_mysim);
502 #endif
503     }
504     else
505     {
506         nrnb_tot = nrnb;
507     }
508
509     elapsed_time                  = walltime_accounting_get_time_since_reset(walltime_accounting);
510     elapsed_time_over_all_threads = walltime_accounting_get_time_since_reset_over_all_threads(walltime_accounting);
511     if (cr->nnodes > 1)
512     {
513 #if GMX_MPI
514         /* reduce elapsed_time over all MPI ranks in the current simulation */
515         MPI_Allreduce(&elapsed_time,
516                       &elapsed_time_over_all_ranks,
517                       1, MPI_DOUBLE, MPI_SUM,
518                       cr->mpi_comm_mysim);
519         elapsed_time_over_all_ranks /= cr->nnodes;
520         /* Reduce elapsed_time_over_all_threads over all MPI ranks in the
521          * current simulation. */
522         MPI_Allreduce(&elapsed_time_over_all_threads,
523                       &elapsed_time_over_all_threads_over_all_ranks,
524                       1, MPI_DOUBLE, MPI_SUM,
525                       cr->mpi_comm_mysim);
526 #endif
527     }
528     else
529     {
530         elapsed_time_over_all_ranks                  = elapsed_time;
531         elapsed_time_over_all_threads_over_all_ranks = elapsed_time_over_all_threads;
532     }
533
534     if (printReport)
535     {
536         print_flop(fplog, nrnb_tot, &nbfs, &mflop);
537     }
538
539     if (thisRankHasDuty(cr, DUTY_PP) && DOMAINDECOMP(cr))
540     {
541         print_dd_statistics(cr, inputrec, fplog);
542     }
543
544     /* TODO Move the responsibility for any scaling by thread counts
545      * to the code that handled the thread region, so that there's a
546      * mechanism to keep cycle counting working during the transition
547      * to task parallelism. */
548     int nthreads_pp  = gmx_omp_nthreads_get(emntNonbonded);
549     int nthreads_pme = gmx_omp_nthreads_get(emntPME);
550     wallcycle_scale_by_num_threads(wcycle, thisRankHasDuty(cr, DUTY_PME) && !thisRankHasDuty(cr, DUTY_PP), nthreads_pp, nthreads_pme);
551     auto cycle_sum(wallcycle_sum(cr, wcycle));
552
553     if (printReport)
554     {
555         auto                    nbnxn_gpu_timings = (nbv != nullptr && nbv->useGpu()) ? Nbnxm::gpu_get_timings(nbv->gpu_nbv) : nullptr;
556         gmx_wallclock_gpu_pme_t pme_gpu_timings   = {};
557
558         if (pme_gpu_task_enabled(pme))
559         {
560             pme_gpu_get_timings(pme, &pme_gpu_timings);
561         }
562         wallcycle_print(fplog, mdlog, cr->nnodes, cr->npmenodes, nthreads_pp, nthreads_pme,
563                         elapsed_time_over_all_ranks,
564                         wcycle, cycle_sum,
565                         nbnxn_gpu_timings,
566                         &pme_gpu_timings);
567
568         if (EI_DYNAMICS(inputrec->eI))
569         {
570             delta_t = inputrec->delta_t;
571         }
572
573         if (fplog)
574         {
575             print_perf(fplog, elapsed_time_over_all_threads_over_all_ranks,
576                        elapsed_time_over_all_ranks,
577                        walltime_accounting_get_nsteps_done_since_reset(walltime_accounting),
578                        delta_t, nbfs, mflop);
579         }
580         if (bWriteStat)
581         {
582             print_perf(stderr, elapsed_time_over_all_threads_over_all_ranks,
583                        elapsed_time_over_all_ranks,
584                        walltime_accounting_get_nsteps_done_since_reset(walltime_accounting),
585                        delta_t, nbfs, mflop);
586         }
587     }
588 }
589
590 int Mdrunner::mdrunner()
591 {
592     matrix                    box;
593     t_forcerec               *fr               = nullptr;
594     t_fcdata                 *fcd              = nullptr;
595     real                      ewaldcoeff_q     = 0;
596     real                      ewaldcoeff_lj    = 0;
597     int                       nChargePerturbed = -1, nTypePerturbed = 0;
598     gmx_wallcycle_t           wcycle;
599     gmx_walltime_accounting_t walltime_accounting = nullptr;
600     gmx_membed_t *            membed              = nullptr;
601     gmx_hw_info_t            *hwinfo              = nullptr;
602
603     /* CAUTION: threads may be started later on in this function, so
604        cr doesn't reflect the final parallel state right now */
605     t_inputrec                      inputrecInstance;
606     t_inputrec                     *inputrec = &inputrecInstance;
607     gmx_mtop_t                      mtop;
608
609     bool doMembed = opt2bSet("-membed", filenames.size(), filenames.data());
610     bool doRerun  = mdrunOptions.rerun;
611
612     // Handle task-assignment related user options.
613     EmulateGpuNonbonded emulateGpuNonbonded = (getenv("GMX_EMULATE_GPU") != nullptr ?
614                                                EmulateGpuNonbonded::Yes : EmulateGpuNonbonded::No);
615
616     std::vector<int> userGpuTaskAssignment;
617     try
618     {
619         userGpuTaskAssignment = parseUserTaskAssignmentString(hw_opt.userGpuTaskAssignment);
620     }
621     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
622     auto       nonbondedTarget = findTaskTarget(nbpu_opt);
623     auto       pmeTarget       = findTaskTarget(pme_opt);
624     auto       pmeFftTarget    = findTaskTarget(pme_fft_opt);
625     auto       bondedTarget    = findTaskTarget(bonded_opt);
626     PmeRunMode pmeRunMode      = PmeRunMode::None;
627
628     // Here we assume that SIMMASTER(cr) does not change even after the
629     // threads are started.
630
631     FILE *fplog = nullptr;
632     // If we are appending, we don't write log output because we need
633     // to check that the old log file matches what the checkpoint file
634     // expects. Otherwise, we should start to write log output now if
635     // there is a file ready for it.
636     if (logFileHandle != nullptr && startingBehavior != StartingBehavior::RestartWithAppending)
637     {
638         fplog = gmx_fio_getfp(logFileHandle);
639     }
640     gmx::LoggerOwner logOwner(buildLogger(fplog, cr));
641     gmx::MDLogger    mdlog(logOwner.logger());
642
643     // report any development features that may be enabled by environment variables
644     reportDevelopmentFeatures(mdlog);
645
646     // With thread-MPI, the communicator changes after threads are
647     // launched, so this is rebuilt for the master rank at that
648     // time. The non-master ranks are fine to keep the one made here.
649     PhysicalNodeCommunicator physicalNodeComm(MPI_COMM_WORLD, gmx_physicalnode_id_hash());
650     hwinfo = gmx_detect_hardware(mdlog, physicalNodeComm);
651
652     gmx_print_detected_hardware(fplog, isMasterSimMasterRank(ms, MASTER(cr)), mdlog, hwinfo);
653
654     std::vector<int> gpuIdsToUse = makeGpuIdsToUse(hwinfo->gpu_info, hw_opt.gpuIdsAvailable);
655
656     // Print citation requests after all software/hardware printing
657     pleaseCiteGromacs(fplog);
658
659     std::unique_ptr<t_state> globalState;
660
661     if (SIMMASTER(cr))
662     {
663         /* Only the master rank has the global state */
664         globalState = std::make_unique<t_state>();
665
666         /* Read (nearly) all data required for the simulation */
667         read_tpx_state(ftp2fn(efTPR, filenames.size(), filenames.data()), inputrec, globalState.get(), &mtop);
668     }
669
670     /* Check and update the hardware options for internal consistency */
671     checkAndUpdateHardwareOptions(mdlog, &hw_opt, SIMMASTER(cr), domdecOptions.numPmeRanks);
672
673     if (GMX_THREAD_MPI && SIMMASTER(cr))
674     {
675         bool useGpuForNonbonded = false;
676         bool useGpuForPme       = false;
677         try
678         {
679             // If the user specified the number of ranks, then we must
680             // respect that, but in default mode, we need to allow for
681             // the number of GPUs to choose the number of ranks.
682             auto canUseGpuForNonbonded = buildSupportsNonbondedOnGpu(nullptr);
683             useGpuForNonbonded = decideWhetherToUseGpusForNonbondedWithThreadMpi
684                     (nonbondedTarget, gpuIdsToUse, userGpuTaskAssignment, emulateGpuNonbonded,
685                     canUseGpuForNonbonded,
686                     gpuAccelerationOfNonbondedIsUseful(mdlog, inputrec, GMX_THREAD_MPI),
687                     hw_opt.nthreads_tmpi);
688             useGpuForPme = decideWhetherToUseGpusForPmeWithThreadMpi
689                     (useGpuForNonbonded, pmeTarget, gpuIdsToUse, userGpuTaskAssignment,
690                     *hwinfo, *inputrec, mtop, hw_opt.nthreads_tmpi, domdecOptions.numPmeRanks);
691
692         }
693         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
694
695         /* Determine how many thread-MPI ranks to start.
696          *
697          * TODO Over-writing the user-supplied value here does
698          * prevent any possible subsequent checks from working
699          * correctly. */
700         hw_opt.nthreads_tmpi = get_nthreads_mpi(hwinfo,
701                                                 &hw_opt,
702                                                 gpuIdsToUse,
703                                                 useGpuForNonbonded,
704                                                 useGpuForPme,
705                                                 inputrec, &mtop,
706                                                 mdlog,
707                                                 doMembed);
708
709         // Now start the threads for thread MPI.
710         cr = spawnThreads(hw_opt.nthreads_tmpi);
711         /* The main thread continues here with a new cr. We don't deallocate
712            the old cr because other threads may still be reading it. */
713         // TODO Both master and spawned threads call dup_tfn and
714         // reinitialize_commrec_for_this_thread. Find a way to express
715         // this better.
716         physicalNodeComm = PhysicalNodeCommunicator(MPI_COMM_WORLD, gmx_physicalnode_id_hash());
717     }
718     // END OF CAUTION: cr and physicalNodeComm are now reliable
719
720     if (PAR(cr))
721     {
722         /* now broadcast everything to the non-master nodes/threads: */
723         init_parallel(cr, inputrec, &mtop);
724     }
725
726     // Now each rank knows the inputrec that SIMMASTER read and used,
727     // and (if applicable) cr->nnodes has been assigned the number of
728     // thread-MPI ranks that have been chosen. The ranks can now all
729     // run the task-deciding functions and will agree on the result
730     // without needing to communicate.
731     //
732     // TODO Should we do the communication in debug mode to support
733     // having an assertion?
734     //
735     // Note that these variables describe only their own node.
736     //
737     // Note that when bonded interactions run on a GPU they always run
738     // alongside a nonbonded task, so do not influence task assignment
739     // even though they affect the force calculation workload.
740     bool useGpuForNonbonded = false;
741     bool useGpuForPme       = false;
742     bool useGpuForBonded    = false;
743     try
744     {
745         // It's possible that there are different numbers of GPUs on
746         // different nodes, which is the user's responsibilty to
747         // handle. If unsuitable, we will notice that during task
748         // assignment.
749         bool gpusWereDetected      = hwinfo->ngpu_compatible_tot > 0;
750         auto canUseGpuForNonbonded = buildSupportsNonbondedOnGpu(nullptr);
751         useGpuForNonbonded = decideWhetherToUseGpusForNonbonded(nonbondedTarget, userGpuTaskAssignment,
752                                                                 emulateGpuNonbonded,
753                                                                 canUseGpuForNonbonded,
754                                                                 gpuAccelerationOfNonbondedIsUseful(mdlog, inputrec, !GMX_THREAD_MPI),
755                                                                 gpusWereDetected);
756         useGpuForPme = decideWhetherToUseGpusForPme(useGpuForNonbonded, pmeTarget, userGpuTaskAssignment,
757                                                     *hwinfo, *inputrec, mtop,
758                                                     cr->nnodes, domdecOptions.numPmeRanks,
759                                                     gpusWereDetected);
760         auto canUseGpuForBonded = buildSupportsGpuBondeds(nullptr) && inputSupportsGpuBondeds(*inputrec, mtop, nullptr);
761         useGpuForBonded =
762             decideWhetherToUseGpusForBonded(useGpuForNonbonded, useGpuForPme,
763                                             bondedTarget, canUseGpuForBonded,
764                                             EVDW_PME(inputrec->vdwtype),
765                                             EEL_PME_EWALD(inputrec->coulombtype),
766                                             domdecOptions.numPmeRanks, gpusWereDetected);
767
768         pmeRunMode   = (useGpuForPme ? PmeRunMode::GPU : PmeRunMode::CPU);
769         if (pmeRunMode == PmeRunMode::GPU)
770         {
771             if (pmeFftTarget == TaskTarget::Cpu)
772             {
773                 pmeRunMode = PmeRunMode::Mixed;
774             }
775         }
776         else if (pmeFftTarget == TaskTarget::Gpu)
777         {
778             gmx_fatal(FARGS, "Assigning FFTs to GPU requires PME to be assigned to GPU as well. With PME on CPU you should not be using -pmefft.");
779         }
780     }
781     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
782
783     // Build restraints.
784     // TODO: hide restraint implementation details from Mdrunner.
785     // There is nothing unique about restraints at this point as far as the
786     // Mdrunner is concerned. The Mdrunner should just be getting a sequence of
787     // factory functions from the SimulationContext on which to call mdModules_->add().
788     // TODO: capture all restraints into a single RestraintModule, passed to the runner builder.
789     for (auto && restraint : restraintManager_->getRestraints())
790     {
791         auto module = RestraintMDModule::create(restraint,
792                                                 restraint->sites());
793         mdModules_->add(std::move(module));
794     }
795
796     // TODO: Error handling
797     mdModules_->assignOptionsToModules(*inputrec->params, nullptr);
798
799     if (fplog != nullptr)
800     {
801         pr_inputrec(fplog, 0, "Input Parameters", inputrec, FALSE);
802         fprintf(fplog, "\n");
803     }
804
805     if (SIMMASTER(cr))
806     {
807         /* In rerun, set velocities to zero if present */
808         if (doRerun && ((globalState->flags & (1 << estV)) != 0))
809         {
810             // rerun does not use velocities
811             GMX_LOG(mdlog.info).asParagraph().appendText(
812                     "Rerun trajectory contains velocities. Rerun does only evaluate "
813                     "potential energy and forces. The velocities will be ignored.");
814             for (int i = 0; i < globalState->natoms; i++)
815             {
816                 clear_rvec(globalState->v[i]);
817             }
818             globalState->flags &= ~(1 << estV);
819         }
820
821         /* now make sure the state is initialized and propagated */
822         set_state_entries(globalState.get(), inputrec);
823     }
824
825     /* NM and TPI parallelize over force/energy calculations, not atoms,
826      * so we need to initialize and broadcast the global state.
827      */
828     if (inputrec->eI == eiNM || inputrec->eI == eiTPI)
829     {
830         if (!MASTER(cr))
831         {
832             globalState = std::make_unique<t_state>();
833         }
834         broadcastStateWithoutDynamics(cr, globalState.get());
835     }
836
837     /* A parallel command line option consistency check that we can
838        only do after any threads have started. */
839     if (!PAR(cr) && (domdecOptions.numCells[XX] > 1 ||
840                      domdecOptions.numCells[YY] > 1 ||
841                      domdecOptions.numCells[ZZ] > 1 ||
842                      domdecOptions.numPmeRanks > 0))
843     {
844         gmx_fatal(FARGS,
845                   "The -dd or -npme option request a parallel simulation, "
846 #if !GMX_MPI
847                   "but %s was compiled without threads or MPI enabled", output_env_get_program_display_name(oenv));
848 #else
849 #if GMX_THREAD_MPI
850                   "but the number of MPI-threads (option -ntmpi) is not set or is 1");
851 #else
852                   "but %s was not started through mpirun/mpiexec or only one rank was requested through mpirun/mpiexec", output_env_get_program_display_name(oenv));
853 #endif
854 #endif
855     }
856
857     if (doRerun &&
858         (EI_ENERGY_MINIMIZATION(inputrec->eI) || eiNM == inputrec->eI))
859     {
860         gmx_fatal(FARGS, "The .mdp file specified an energy mininization or normal mode algorithm, and these are not compatible with mdrun -rerun");
861     }
862
863     if (!(EEL_PME(inputrec->coulombtype) || EVDW_PME(inputrec->vdwtype)))
864     {
865         if (domdecOptions.numPmeRanks > 0)
866         {
867             gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
868                                  "PME-only ranks are requested, but the system does not use PME for electrostatics or LJ");
869         }
870
871         domdecOptions.numPmeRanks = 0;
872     }
873
874     if (useGpuForNonbonded && domdecOptions.numPmeRanks < 0)
875     {
876         /* With NB GPUs we don't automatically use PME-only CPU ranks. PME ranks can
877          * improve performance with many threads per GPU, since our OpenMP
878          * scaling is bad, but it's difficult to automate the setup.
879          */
880         domdecOptions.numPmeRanks = 0;
881     }
882     if (useGpuForPme)
883     {
884         if (domdecOptions.numPmeRanks < 0)
885         {
886             domdecOptions.numPmeRanks = 0;
887             // TODO possibly print a note that one can opt-in for a separate PME GPU rank?
888         }
889         else
890         {
891             GMX_RELEASE_ASSERT(domdecOptions.numPmeRanks <= 1, "PME GPU decomposition is not supported");
892         }
893     }
894
895 #if GMX_FAHCORE
896     if (MASTER(cr))
897     {
898         fcRegisterSteps(inputrec->nsteps, inputrec->init_step);
899     }
900 #endif
901
902     /* NMR restraints must be initialized before load_checkpoint,
903      * since with time averaging the history is added to t_state.
904      * For proper consistency check we therefore need to extend
905      * t_state here.
906      * So the PME-only nodes (if present) will also initialize
907      * the distance restraints.
908      */
909     snew(fcd, 1);
910
911     /* This needs to be called before read_checkpoint to extend the state */
912     init_disres(fplog, &mtop, inputrec, cr, ms, fcd, globalState.get(), replExParams.exchangeInterval > 0);
913
914     init_orires(fplog, &mtop, inputrec, cr, ms, globalState.get(), &(fcd->orires));
915
916     auto                 deform = prepareBoxDeformation(globalState->box, cr, *inputrec);
917
918     ObservablesHistory   observablesHistory = {};
919
920     if (startingBehavior != StartingBehavior::NewSimulation)
921     {
922         load_checkpoint(opt2fn_master("-cpi", filenames.size(), filenames.data(), cr),
923                         logFileHandle,
924                         cr, domdecOptions.numCells,
925                         inputrec, globalState.get(),
926                         &observablesHistory,
927                         mdrunOptions.reproducible);
928
929         if (startingBehavior == StartingBehavior::RestartWithAppending && logFileHandle)
930         {
931             // Now we can start normal logging to the truncated log file.
932             fplog    = gmx_fio_getfp(logFileHandle);
933             prepareLogAppending(fplog);
934             logOwner = buildLogger(fplog, cr);
935             mdlog    = logOwner.logger();
936         }
937     }
938
939     if (mdrunOptions.numStepsCommandline > -2)
940     {
941         GMX_LOG(mdlog.info).asParagraph().
942             appendText("The -nsteps functionality is deprecated, and may be removed in a future version. "
943                        "Consider using gmx convert-tpr -nsteps or changing the appropriate .mdp file field.");
944     }
945     /* override nsteps with value set on the commamdline */
946     override_nsteps_cmdline(mdlog, mdrunOptions.numStepsCommandline, inputrec);
947
948     if (SIMMASTER(cr))
949     {
950         copy_mat(globalState->box, box);
951     }
952
953     if (PAR(cr))
954     {
955         gmx_bcast(sizeof(box), box, cr);
956     }
957
958     if (inputrec->cutoff_scheme != ecutsVERLET)
959     {
960         gmx_fatal(FARGS, "This group-scheme .tpr file can no longer be run by mdrun. Please update to the Verlet scheme, or use an earlier version of GROMACS if necessary.");
961     }
962     /* Update rlist and nstlist. */
963     prepare_verlet_scheme(fplog, cr, inputrec, nstlist_cmdline, &mtop, box,
964                           useGpuForNonbonded || (emulateGpuNonbonded == EmulateGpuNonbonded::Yes), *hwinfo->cpuInfo);
965
966     LocalAtomSetManager atomSets;
967
968     if (PAR(cr) && !(EI_TPI(inputrec->eI) ||
969                      inputrec->eI == eiNM))
970     {
971         cr->dd = init_domain_decomposition(mdlog, cr, domdecOptions, mdrunOptions,
972                                            &mtop, inputrec,
973                                            box, positionsFromStatePointer(globalState.get()),
974                                            &atomSets);
975         // Note that local state still does not exist yet.
976     }
977     else
978     {
979         /* PME, if used, is done on all nodes with 1D decomposition */
980         cr->npmenodes = 0;
981         cr->duty      = (DUTY_PP | DUTY_PME);
982
983         if (inputrec->ePBC == epbcSCREW)
984         {
985             gmx_fatal(FARGS,
986                       "pbc=screw is only implemented with domain decomposition");
987         }
988     }
989
990     if (PAR(cr))
991     {
992         /* After possible communicator splitting in make_dd_communicators.
993          * we can set up the intra/inter node communication.
994          */
995         gmx_setup_nodecomm(fplog, cr);
996     }
997
998 #if GMX_MPI
999     if (isMultiSim(ms))
1000     {
1001         GMX_LOG(mdlog.warning).asParagraph().appendTextFormatted(
1002                 "This is simulation %d out of %d running as a composite GROMACS\n"
1003                 "multi-simulation job. Setup for this simulation:\n",
1004                 ms->sim, ms->nsim);
1005     }
1006     GMX_LOG(mdlog.warning).appendTextFormatted(
1007             "Using %d MPI %s\n",
1008             cr->nnodes,
1009 #if GMX_THREAD_MPI
1010             cr->nnodes == 1 ? "thread" : "threads"
1011 #else
1012             cr->nnodes == 1 ? "process" : "processes"
1013 #endif
1014             );
1015     fflush(stderr);
1016 #endif
1017
1018     // If mdrun -pin auto honors any affinity setting that already
1019     // exists. If so, it is nice to provide feedback about whether
1020     // that existing affinity setting was from OpenMP or something
1021     // else, so we run this code both before and after we initialize
1022     // the OpenMP support.
1023     gmx_check_thread_affinity_set(mdlog,
1024                                   &hw_opt, hwinfo->nthreads_hw_avail, FALSE);
1025     /* Check and update the number of OpenMP threads requested */
1026     checkAndUpdateRequestedNumOpenmpThreads(&hw_opt, *hwinfo, cr, ms, physicalNodeComm.size_,
1027                                             pmeRunMode, mtop);
1028
1029     gmx_omp_nthreads_init(mdlog, cr,
1030                           hwinfo->nthreads_hw_avail,
1031                           physicalNodeComm.size_,
1032                           hw_opt.nthreads_omp,
1033                           hw_opt.nthreads_omp_pme,
1034                           !thisRankHasDuty(cr, DUTY_PP));
1035
1036     // Enable FP exception detection, but not in
1037     // Release mode and not for compilers with known buggy FP
1038     // exception support (clang with any optimization) or suspected
1039     // buggy FP exception support (gcc 7.* with optimization).
1040 #if !defined NDEBUG && \
1041     !((defined __clang__ || (defined(__GNUC__) && !defined(__ICC) && __GNUC__ == 7)) \
1042     && defined __OPTIMIZE__)
1043     const bool bEnableFPE = true;
1044 #else
1045     const bool bEnableFPE = false;
1046 #endif
1047     //FIXME - reconcile with gmx_feenableexcept() call from CommandLineModuleManager::run()
1048     if (bEnableFPE)
1049     {
1050         gmx_feenableexcept();
1051     }
1052
1053     // Build a data structure that expresses which kinds of non-bonded
1054     // task are handled by this rank.
1055     //
1056     // TODO Later, this might become a loop over all registered modules
1057     // relevant to the mdp inputs, to find those that have such tasks.
1058     //
1059     // TODO This could move before init_domain_decomposition() as part
1060     // of refactoring that separates the responsibility for duty
1061     // assignment from setup for communication between tasks, and
1062     // setup for tasks handled with a domain (ie including short-ranged
1063     // tasks, bonded tasks, etc.).
1064     //
1065     // Note that in general useGpuForNonbonded, etc. can have a value
1066     // that is inconsistent with the presence of actual GPUs on any
1067     // rank, and that is not known to be a problem until the
1068     // duty of the ranks on a node become known.
1069     //
1070     // TODO Later we might need the concept of computeTasksOnThisRank,
1071     // from which we construct gpuTasksOnThisRank.
1072     //
1073     // Currently the DD code assigns duty to ranks that can
1074     // include PP work that currently can be executed on a single
1075     // GPU, if present and compatible.  This has to be coordinated
1076     // across PP ranks on a node, with possible multiple devices
1077     // or sharing devices on a node, either from the user
1078     // selection, or automatically.
1079     auto                 haveGpus = !gpuIdsToUse.empty();
1080     std::vector<GpuTask> gpuTasksOnThisRank;
1081     if (thisRankHasDuty(cr, DUTY_PP))
1082     {
1083         if (useGpuForNonbonded)
1084         {
1085             // Note that any bonded tasks on a GPU always accompany a
1086             // non-bonded task.
1087             if (haveGpus)
1088             {
1089                 gpuTasksOnThisRank.push_back(GpuTask::Nonbonded);
1090             }
1091             else if (nonbondedTarget == TaskTarget::Gpu)
1092             {
1093                 gmx_fatal(FARGS, "Cannot run short-ranged nonbonded interactions on a GPU because no GPU is detected.");
1094             }
1095             else if (bondedTarget == TaskTarget::Gpu)
1096             {
1097                 gmx_fatal(FARGS, "Cannot run bonded interactions on a GPU because no GPU is detected.");
1098             }
1099         }
1100     }
1101     // TODO cr->duty & DUTY_PME should imply that a PME algorithm is active, but currently does not.
1102     if (EEL_PME(inputrec->coulombtype) && (thisRankHasDuty(cr, DUTY_PME)))
1103     {
1104         if (useGpuForPme)
1105         {
1106             if (haveGpus)
1107             {
1108                 gpuTasksOnThisRank.push_back(GpuTask::Pme);
1109             }
1110             else if (pmeTarget == TaskTarget::Gpu)
1111             {
1112                 gmx_fatal(FARGS, "Cannot run PME on a GPU because no GPU is detected.");
1113             }
1114         }
1115     }
1116
1117     GpuTaskAssignment gpuTaskAssignment;
1118     try
1119     {
1120         // Produce the task assignment for this rank.
1121         gpuTaskAssignment = runTaskAssignment(gpuIdsToUse, userGpuTaskAssignment, *hwinfo,
1122                                               mdlog, cr, ms, physicalNodeComm, gpuTasksOnThisRank,
1123                                               useGpuForBonded, pmeRunMode);
1124     }
1125     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
1126
1127     /* Prevent other ranks from continuing after an issue was found
1128      * and reported as a fatal error.
1129      *
1130      * TODO This function implements a barrier so that MPI runtimes
1131      * can organize an orderly shutdown if one of the ranks has had to
1132      * issue a fatal error in various code already run. When we have
1133      * MPI-aware error handling and reporting, this should be
1134      * improved. */
1135 #if GMX_MPI
1136     if (PAR(cr))
1137     {
1138         MPI_Barrier(cr->mpi_comm_mysim);
1139     }
1140     if (isMultiSim(ms))
1141     {
1142         if (SIMMASTER(cr))
1143         {
1144             MPI_Barrier(ms->mpi_comm_masters);
1145         }
1146         /* We need another barrier to prevent non-master ranks from contiuing
1147          * when an error occured in a different simulation.
1148          */
1149         MPI_Barrier(cr->mpi_comm_mysim);
1150     }
1151 #endif
1152
1153     /* Now that we know the setup is consistent, check for efficiency */
1154     check_resource_division_efficiency(hwinfo, !gpuTaskAssignment.empty(), mdrunOptions.ntompOptionIsSet,
1155                                        cr, mdlog);
1156
1157     gmx_device_info_t *nonbondedDeviceInfo = nullptr;
1158
1159     if (thisRankHasDuty(cr, DUTY_PP))
1160     {
1161         // This works because only one task of each type is currently permitted.
1162         auto nbGpuTaskMapping = std::find_if(gpuTaskAssignment.begin(), gpuTaskAssignment.end(),
1163                                              hasTaskType<GpuTask::Nonbonded>);
1164         if (nbGpuTaskMapping != gpuTaskAssignment.end())
1165         {
1166             int nonbondedDeviceId = nbGpuTaskMapping->deviceId_;
1167             nonbondedDeviceInfo = getDeviceInfo(hwinfo->gpu_info, nonbondedDeviceId);
1168             init_gpu(nonbondedDeviceInfo);
1169
1170             if (DOMAINDECOMP(cr))
1171             {
1172                 /* When we share GPUs over ranks, we need to know this for the DLB */
1173                 dd_setup_dlb_resource_sharing(cr, nonbondedDeviceId);
1174             }
1175
1176         }
1177     }
1178
1179     std::unique_ptr<ClfftInitializer> initializedClfftLibrary;
1180
1181     gmx_device_info_t                *pmeDeviceInfo = nullptr;
1182     // Later, this program could contain kernels that might be later
1183     // re-used as auto-tuning progresses, or subsequent simulations
1184     // are invoked.
1185     PmeGpuProgramStorage pmeGpuProgram;
1186     // This works because only one task of each type is currently permitted.
1187     auto                 pmeGpuTaskMapping     = std::find_if(gpuTaskAssignment.begin(), gpuTaskAssignment.end(), hasTaskType<GpuTask::Pme>);
1188     const bool           thisRankHasPmeGpuTask = (pmeGpuTaskMapping != gpuTaskAssignment.end());
1189     if (thisRankHasPmeGpuTask)
1190     {
1191         pmeDeviceInfo = getDeviceInfo(hwinfo->gpu_info, pmeGpuTaskMapping->deviceId_);
1192         init_gpu(pmeDeviceInfo);
1193         pmeGpuProgram = buildPmeGpuProgram(pmeDeviceInfo);
1194         // TODO It would be nice to move this logic into the factory
1195         // function. See Redmine #2535.
1196         bool isMasterThread = !GMX_THREAD_MPI || MASTER(cr);
1197         if (pmeRunMode == PmeRunMode::GPU && !initializedClfftLibrary && isMasterThread)
1198         {
1199             initializedClfftLibrary = initializeClfftLibrary();
1200         }
1201     }
1202
1203     /* getting number of PP/PME threads on this MPI / tMPI rank.
1204        PME: env variable should be read only on one node to make sure it is
1205        identical everywhere;
1206      */
1207     const int numThreadsOnThisRank =
1208         thisRankHasDuty(cr, DUTY_PP) ? gmx_omp_nthreads_get(emntNonbonded) : gmx_omp_nthreads_get(emntPME);
1209     checkHardwareOversubscription(numThreadsOnThisRank, cr->nodeid,
1210                                   *hwinfo->hardwareTopology,
1211                                   physicalNodeComm, mdlog);
1212
1213     if (hw_opt.threadAffinity != ThreadAffinity::Off)
1214     {
1215         /* Before setting affinity, check whether the affinity has changed
1216          * - which indicates that probably the OpenMP library has changed it
1217          * since we first checked).
1218          */
1219         gmx_check_thread_affinity_set(mdlog,
1220                                       &hw_opt, hwinfo->nthreads_hw_avail, TRUE);
1221
1222         int numThreadsOnThisNode, intraNodeThreadOffset;
1223         analyzeThreadsOnThisNode(physicalNodeComm, numThreadsOnThisRank, &numThreadsOnThisNode,
1224                                  &intraNodeThreadOffset);
1225
1226         /* Set the CPU affinity */
1227         gmx_set_thread_affinity(mdlog, cr, &hw_opt, *hwinfo->hardwareTopology,
1228                                 numThreadsOnThisRank, numThreadsOnThisNode,
1229                                 intraNodeThreadOffset, nullptr);
1230     }
1231
1232     if (mdrunOptions.timingOptions.resetStep > -1)
1233     {
1234         GMX_LOG(mdlog.info).asParagraph().
1235             appendText("The -resetstep functionality is deprecated, and may be removed in a future version.");
1236     }
1237     wcycle = wallcycle_init(fplog, mdrunOptions.timingOptions.resetStep, cr);
1238
1239     if (PAR(cr))
1240     {
1241         /* Master synchronizes its value of reset_counters with all nodes
1242          * including PME only nodes */
1243         int64_t reset_counters = wcycle_get_reset_counters(wcycle);
1244         gmx_bcast_sim(sizeof(reset_counters), &reset_counters, cr);
1245         wcycle_set_reset_counters(wcycle, reset_counters);
1246     }
1247
1248     // Membrane embedding must be initialized before we call init_forcerec()
1249     if (doMembed)
1250     {
1251         if (MASTER(cr))
1252         {
1253             fprintf(stderr, "Initializing membed");
1254         }
1255         /* Note that membed cannot work in parallel because mtop is
1256          * changed here. Fix this if we ever want to make it run with
1257          * multiple ranks. */
1258         membed = init_membed(fplog, filenames.size(), filenames.data(), &mtop, inputrec, globalState.get(), cr,
1259                              &mdrunOptions
1260                                  .checkpointOptions.period);
1261     }
1262
1263     std::unique_ptr<MDAtoms>     mdAtoms;
1264     std::unique_ptr<gmx_vsite_t> vsite;
1265
1266     t_nrnb nrnb;
1267     if (thisRankHasDuty(cr, DUTY_PP))
1268     {
1269         /* Initiate forcerecord */
1270         fr                 = new t_forcerec;
1271         fr->forceProviders = mdModules_->initForceProviders();
1272         init_forcerec(fplog, mdlog, fr, fcd,
1273                       inputrec, &mtop, cr, box,
1274                       opt2fn("-table", filenames.size(), filenames.data()),
1275                       opt2fn("-tablep", filenames.size(), filenames.data()),
1276                       opt2fns("-tableb", filenames.size(), filenames.data()),
1277                       *hwinfo, nonbondedDeviceInfo,
1278                       useGpuForBonded,
1279                       FALSE,
1280                       pforce,
1281                       wcycle);
1282
1283         /* Initialize the mdAtoms structure.
1284          * mdAtoms is not filled with atom data,
1285          * as this can not be done now with domain decomposition.
1286          */
1287         mdAtoms = makeMDAtoms(fplog, mtop, *inputrec, thisRankHasPmeGpuTask);
1288         if (globalState && thisRankHasPmeGpuTask)
1289         {
1290             // The pinning of coordinates in the global state object works, because we only use
1291             // PME on GPU without DD or on a separate PME rank, and because the local state pointer
1292             // points to the global state object without DD.
1293             // FIXME: MD and EM separately set up the local state - this should happen in the same function,
1294             // which should also perform the pinning.
1295             changePinningPolicy(&globalState->x, pme_get_pinning_policy());
1296         }
1297
1298         /* Initialize the virtual site communication */
1299         vsite = initVsite(mtop, cr);
1300
1301         calc_shifts(box, fr->shift_vec);
1302
1303         /* With periodic molecules the charge groups should be whole at start up
1304          * and the virtual sites should not be far from their proper positions.
1305          */
1306         if (!inputrec->bContinuation && MASTER(cr) &&
1307             !(inputrec->ePBC != epbcNONE && inputrec->bPeriodicMols))
1308         {
1309             /* Make molecules whole at start of run */
1310             if (fr->ePBC != epbcNONE)
1311             {
1312                 do_pbc_first_mtop(fplog, inputrec->ePBC, box, &mtop, globalState->x.rvec_array());
1313             }
1314             if (vsite)
1315             {
1316                 /* Correct initial vsite positions are required
1317                  * for the initial distribution in the domain decomposition
1318                  * and for the initial shell prediction.
1319                  */
1320                 constructVsitesGlobal(mtop, globalState->x);
1321             }
1322         }
1323
1324         if (EEL_PME(fr->ic->eeltype) || EVDW_PME(fr->ic->vdwtype))
1325         {
1326             ewaldcoeff_q  = fr->ic->ewaldcoeff_q;
1327             ewaldcoeff_lj = fr->ic->ewaldcoeff_lj;
1328         }
1329     }
1330     else
1331     {
1332         /* This is a PME only node */
1333
1334         GMX_ASSERT(globalState == nullptr, "We don't need the state on a PME only rank and expect it to be unitialized");
1335
1336         ewaldcoeff_q  = calc_ewaldcoeff_q(inputrec->rcoulomb, inputrec->ewald_rtol);
1337         ewaldcoeff_lj = calc_ewaldcoeff_lj(inputrec->rvdw, inputrec->ewald_rtol_lj);
1338     }
1339
1340     gmx_pme_t *sepPmeData = nullptr;
1341     // This reference hides the fact that PME data is owned by runner on PME-only ranks and by forcerec on other ranks
1342     GMX_ASSERT(thisRankHasDuty(cr, DUTY_PP) == (fr != nullptr), "Double-checking that only PME-only ranks have no forcerec");
1343     gmx_pme_t * &pmedata = fr ? fr->pmedata : sepPmeData;
1344
1345     /* Initiate PME if necessary,
1346      * either on all nodes or on dedicated PME nodes only. */
1347     if (EEL_PME(inputrec->coulombtype) || EVDW_PME(inputrec->vdwtype))
1348     {
1349         if (mdAtoms && mdAtoms->mdatoms())
1350         {
1351             nChargePerturbed = mdAtoms->mdatoms()->nChargePerturbed;
1352             if (EVDW_PME(inputrec->vdwtype))
1353             {
1354                 nTypePerturbed   = mdAtoms->mdatoms()->nTypePerturbed;
1355             }
1356         }
1357         if (cr->npmenodes > 0)
1358         {
1359             /* The PME only nodes need to know nChargePerturbed(FEP on Q) and nTypePerturbed(FEP on LJ)*/
1360             gmx_bcast_sim(sizeof(nChargePerturbed), &nChargePerturbed, cr);
1361             gmx_bcast_sim(sizeof(nTypePerturbed), &nTypePerturbed, cr);
1362         }
1363
1364         if (thisRankHasDuty(cr, DUTY_PME))
1365         {
1366             try
1367             {
1368                 pmedata = gmx_pme_init(cr,
1369                                        getNumPmeDomains(cr->dd),
1370                                        inputrec,
1371                                        nChargePerturbed != 0, nTypePerturbed != 0,
1372                                        mdrunOptions.reproducible,
1373                                        ewaldcoeff_q, ewaldcoeff_lj,
1374                                        gmx_omp_nthreads_get(emntPME),
1375                                        pmeRunMode, nullptr,
1376                                        pmeDeviceInfo, pmeGpuProgram.get(), mdlog);
1377             }
1378             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
1379         }
1380     }
1381
1382
1383     if (EI_DYNAMICS(inputrec->eI))
1384     {
1385         /* Turn on signal handling on all nodes */
1386         /*
1387          * (A user signal from the PME nodes (if any)
1388          * is communicated to the PP nodes.
1389          */
1390         signal_handler_install();
1391     }
1392
1393     pull_t *pull_work = nullptr;
1394     if (thisRankHasDuty(cr, DUTY_PP))
1395     {
1396         /* Assumes uniform use of the number of OpenMP threads */
1397         walltime_accounting = walltime_accounting_init(gmx_omp_nthreads_get(emntDefault));
1398
1399         if (inputrec->bPull)
1400         {
1401             /* Initialize pull code */
1402             pull_work =
1403                 init_pull(fplog, inputrec->pull, inputrec,
1404                           &mtop, cr, &atomSets, inputrec->fepvals->init_lambda);
1405             if (inputrec->pull->bXOutAverage || inputrec->pull->bFOutAverage)
1406             {
1407                 initPullHistory(pull_work, &observablesHistory);
1408             }
1409             if (EI_DYNAMICS(inputrec->eI) && MASTER(cr))
1410             {
1411                 init_pull_output_files(pull_work,
1412                                        filenames.size(), filenames.data(), oenv,
1413                                        startingBehavior);
1414             }
1415         }
1416
1417         std::unique_ptr<EnforcedRotation> enforcedRotation;
1418         if (inputrec->bRot)
1419         {
1420             /* Initialize enforced rotation code */
1421             enforcedRotation = init_rot(fplog,
1422                                         inputrec,
1423                                         filenames.size(),
1424                                         filenames.data(),
1425                                         cr,
1426                                         &atomSets,
1427                                         globalState.get(),
1428                                         &mtop,
1429                                         oenv,
1430                                         mdrunOptions,
1431                                         startingBehavior);
1432         }
1433
1434         t_swap *swap = nullptr;
1435         if (inputrec->eSwapCoords != eswapNO)
1436         {
1437             /* Initialize ion swapping code */
1438             swap = init_swapcoords(fplog, inputrec,
1439                                    opt2fn_master("-swap", filenames.size(), filenames.data(), cr),
1440                                    &mtop, globalState.get(), &observablesHistory,
1441                                    cr, &atomSets, oenv, mdrunOptions,
1442                                    startingBehavior);
1443         }
1444
1445         /* Let makeConstraints know whether we have essential dynamics constraints.
1446          * TODO: inputrec should tell us whether we use an algorithm, not a file option or the checkpoint
1447          */
1448         bool doEssentialDynamics = (opt2fn_null("-ei", filenames.size(), filenames.data()) != nullptr
1449                                     || observablesHistory.edsamHistory);
1450         auto constr              = makeConstraints(mtop, *inputrec, pull_work, doEssentialDynamics,
1451                                                    fplog, *mdAtoms->mdatoms(),
1452                                                    cr, ms, &nrnb, wcycle, fr->bMolPBC);
1453
1454         /* Energy terms and groups */
1455         gmx_enerdata_t enerd(mtop.groups.groups[SimulationAtomGroupType::EnergyOutput].size(), inputrec->fepvals->n_lambda);
1456
1457         /* Set up interactive MD (IMD) */
1458         auto imdSession = makeImdSession(inputrec, cr, wcycle, &enerd, ms, &mtop, mdlog,
1459                                          MASTER(cr) ? globalState->x.rvec_array() : nullptr,
1460                                          filenames.size(), filenames.data(), oenv, mdrunOptions.imdOptions,
1461                                          startingBehavior);
1462
1463         if (DOMAINDECOMP(cr))
1464         {
1465             GMX_RELEASE_ASSERT(fr, "fr was NULL while cr->duty was DUTY_PP");
1466             /* This call is not included in init_domain_decomposition mainly
1467              * because fr->cginfo_mb is set later.
1468              */
1469             dd_init_bondeds(fplog, cr->dd, &mtop, vsite.get(), inputrec,
1470                             domdecOptions.checkBondedInteractions,
1471                             fr->cginfo_mb);
1472         }
1473
1474         // TODO This is not the right place to manage the lifetime of
1475         // this data structure, but currently it's the easiest way to
1476         // make it work. Later, it should probably be made/updated
1477         // after the workload for the lifetime of a PP domain is
1478         // understood.
1479         PpForceWorkload ppForceWorkload;
1480
1481         GMX_ASSERT(stopHandlerBuilder_, "Runner must provide StopHandlerBuilder to simulator.");
1482         /* Now do whatever the user wants us to do (how flexible...) */
1483         LegacySimulator simulator {
1484             fplog, cr, ms, mdlog, static_cast<int>(filenames.size()), filenames.data(),
1485             oenv,
1486             mdrunOptions,
1487             startingBehavior,
1488             vsite.get(), constr.get(),
1489             enforcedRotation ? enforcedRotation->getLegacyEnfrot() : nullptr,
1490             deform.get(),
1491             mdModules_->outputProvider(),
1492             inputrec, imdSession.get(), pull_work, swap, &mtop,
1493             fcd,
1494             globalState.get(),
1495             &observablesHistory,
1496             mdAtoms.get(), &nrnb, wcycle, fr,
1497             &enerd,
1498             &ppForceWorkload,
1499             replExParams,
1500             membed,
1501             walltime_accounting,
1502             std::move(stopHandlerBuilder_)
1503         };
1504         simulator.run(inputrec->eI, doRerun);
1505
1506         if (inputrec->bPull)
1507         {
1508             finish_pull(pull_work);
1509         }
1510         finish_swapcoords(swap);
1511     }
1512     else
1513     {
1514         GMX_RELEASE_ASSERT(pmedata, "pmedata was NULL while cr->duty was not DUTY_PP");
1515         /* do PME only */
1516         walltime_accounting = walltime_accounting_init(gmx_omp_nthreads_get(emntPME));
1517         gmx_pmeonly(pmedata, cr, &nrnb, wcycle, walltime_accounting, inputrec, pmeRunMode);
1518     }
1519
1520     wallcycle_stop(wcycle, ewcRUN);
1521
1522     /* Finish up, write some stuff
1523      * if rerunMD, don't write last frame again
1524      */
1525     finish_run(fplog, mdlog, cr,
1526                inputrec, &nrnb, wcycle, walltime_accounting,
1527                fr ? fr->nbv.get() : nullptr,
1528                pmedata,
1529                EI_DYNAMICS(inputrec->eI) && !isMultiSim(ms));
1530
1531     // clean up cycle counter
1532     wallcycle_destroy(wcycle);
1533
1534 // Free PME data
1535     if (pmedata)
1536     {
1537         gmx_pme_destroy(pmedata);
1538         pmedata = nullptr;
1539     }
1540
1541     // FIXME: this is only here to manually unpin mdAtoms->chargeA_ and state->x,
1542     // before we destroy the GPU context(s) in free_gpu_resources().
1543     // Pinned buffers are associated with contexts in CUDA.
1544     // As soon as we destroy GPU contexts after mdrunner() exits, these lines should go.
1545     mdAtoms.reset(nullptr);
1546     globalState.reset(nullptr);
1547     mdModules_.reset(nullptr);   // destruct force providers here as they might also use the GPU
1548
1549     /* Free GPU memory and set a physical node tMPI barrier (which should eventually go away) */
1550     free_gpu_resources(fr, physicalNodeComm, hwinfo->gpu_info);
1551     free_gpu(nonbondedDeviceInfo);
1552     free_gpu(pmeDeviceInfo);
1553     done_forcerec(fr, mtop.molblock.size());
1554     sfree(fcd);
1555
1556     if (doMembed)
1557     {
1558         free_membed(membed);
1559     }
1560
1561     /* Does what it says */
1562     print_date_and_time(fplog, cr->nodeid, "Finished mdrun", gmx_gettime());
1563     walltime_accounting_destroy(walltime_accounting);
1564
1565     // Ensure log file content is written
1566     if (logFileHandle)
1567     {
1568         gmx_fio_flush(logFileHandle);
1569     }
1570
1571     /* Reset FPEs (important for unit tests) by disabling them. Assumes no
1572      * exceptions were enabled before function was called. */
1573     if (bEnableFPE)
1574     {
1575         gmx_fedisableexcept();
1576     }
1577
1578     auto rc = static_cast<int>(gmx_get_stop_condition());
1579
1580 #if GMX_THREAD_MPI
1581     /* we need to join all threads. The sub-threads join when they
1582        exit this function, but the master thread needs to be told to
1583        wait for that. */
1584     if (PAR(cr) && MASTER(cr))
1585     {
1586         tMPI_Finalize();
1587     }
1588     //TODO free commrec in MPI simulations
1589     done_commrec(cr);
1590 #endif
1591     return rc;
1592 }
1593
1594 Mdrunner::~Mdrunner()
1595 {
1596     // Clean up of the Manager.
1597     // This will end up getting called on every thread-MPI rank, which is unnecessary,
1598     // but okay as long as threads synchronize some time before adding or accessing
1599     // a new set of restraints.
1600     if (restraintManager_)
1601     {
1602         restraintManager_->clear();
1603         GMX_ASSERT(restraintManager_->countRestraints() == 0,
1604                    "restraints added during runner life time should be cleared at runner destruction.");
1605     }
1606 };
1607
1608 void Mdrunner::addPotential(std::shared_ptr<gmx::IRestraintPotential> puller,
1609                             std::string                               name)
1610 {
1611     GMX_ASSERT(restraintManager_, "Mdrunner must have a restraint manager.");
1612     // Not sure if this should be logged through the md logger or something else,
1613     // but it is helpful to have some sort of INFO level message sent somewhere.
1614     //    std::cout << "Registering restraint named " << name << std::endl;
1615
1616     // When multiple restraints are used, it may be wasteful to register them separately.
1617     // Maybe instead register an entire Restraint Manager as a force provider.
1618     restraintManager_->addToSpec(std::move(puller),
1619                                  std::move(name));
1620 }
1621
1622 Mdrunner::Mdrunner(std::unique_ptr<MDModules> mdModules)
1623     : mdModules_(std::move(mdModules))
1624 {
1625 }
1626
1627 Mdrunner::Mdrunner(Mdrunner &&) noexcept = default;
1628
1629 //NOLINTNEXTLINE(performance-noexcept-move-constructor) working around GCC bug 58265
1630 Mdrunner &Mdrunner::operator=(Mdrunner && /*handle*/) noexcept(BUGFREE_NOEXCEPT_STRING) = default;
1631
1632 class Mdrunner::BuilderImplementation
1633 {
1634     public:
1635         BuilderImplementation() = delete;
1636         BuilderImplementation(std::unique_ptr<MDModules> mdModules,
1637                               SimulationContext        * context);
1638         ~BuilderImplementation();
1639
1640         BuilderImplementation &setExtraMdrunOptions(const MdrunOptions &options,
1641                                                     real                forceWarningThreshold,
1642                                                     StartingBehavior    startingBehavior);
1643
1644         void addDomdec(const DomdecOptions &options);
1645
1646         void addVerletList(int nstlist);
1647
1648         void addReplicaExchange(const ReplicaExchangeParameters &params);
1649
1650         void addMultiSim(gmx_multisim_t* multisim);
1651
1652         void addNonBonded(const char* nbpu_opt);
1653
1654         void addPME(const char* pme_opt_, const char* pme_fft_opt_);
1655
1656         void addBondedTaskAssignment(const char* bonded_opt);
1657
1658         void addHardwareOptions(const gmx_hw_opt_t &hardwareOptions);
1659
1660         void addFilenames(ArrayRef <const t_filenm> filenames);
1661
1662         void addOutputEnvironment(gmx_output_env_t* outputEnvironment);
1663
1664         void addLogFile(t_fileio *logFileHandle);
1665
1666         void addStopHandlerBuilder(std::unique_ptr<StopHandlerBuilder> builder);
1667
1668         Mdrunner build();
1669
1670     private:
1671
1672         // Default parameters copied from runner.h
1673         // \todo Clarify source(s) of default parameters.
1674
1675         const char* nbpu_opt_    = nullptr;
1676         const char* pme_opt_     = nullptr;
1677         const char* pme_fft_opt_ = nullptr;
1678         const char *bonded_opt_  = nullptr;
1679
1680         MdrunOptions                          mdrunOptions_;
1681
1682         DomdecOptions                         domdecOptions_;
1683
1684         ReplicaExchangeParameters             replicaExchangeParameters_;
1685
1686         //! Command-line override for the duration of a neighbor list with the Verlet scheme.
1687         int         nstlist_ = 0;
1688
1689         //! Non-owning multisim communicator handle.
1690         std::unique_ptr<gmx_multisim_t*>      multisim_ = nullptr;
1691
1692         //! Print a warning if any force is larger than this (in kJ/mol nm).
1693         real forceWarningThreshold_ = -1;
1694
1695         //! Whether the simulation will start afresh, or restart with/without appending.
1696         StartingBehavior startingBehavior_ = StartingBehavior::NewSimulation;
1697
1698         //! The modules that comprise the functionality of mdrun.
1699         std::unique_ptr<MDModules> mdModules_;
1700
1701         /*! \brief  Non-owning pointer to SimulationContext (owned and managed by client)
1702          *
1703          * \internal
1704          * \todo Establish robust protocol to make sure resources remain valid.
1705          * SimulationContext will likely be separated into multiple layers for
1706          * different levels of access and different phases of execution. Ref
1707          * https://redmine.gromacs.org/issues/2375
1708          * https://redmine.gromacs.org/issues/2587
1709          */
1710         SimulationContext* context_ = nullptr;
1711
1712         //! \brief Parallelism information.
1713         gmx_hw_opt_t hardwareOptions_;
1714
1715         //! filename options for simulation.
1716         ArrayRef<const t_filenm> filenames_;
1717
1718         /*! \brief Handle to output environment.
1719          *
1720          * \todo gmx_output_env_t needs lifetime management.
1721          */
1722         gmx_output_env_t*    outputEnvironment_ = nullptr;
1723
1724         /*! \brief Non-owning handle to MD log file.
1725          *
1726          * \todo Context should own output facilities for client.
1727          * \todo Improve log file handle management.
1728          * \internal
1729          * Code managing the FILE* relies on the ability to set it to
1730          * nullptr to check whether the filehandle is valid.
1731          */
1732         t_fileio* logFileHandle_ = nullptr;
1733
1734         /*!
1735          * \brief Builder for simulation stop signal handler.
1736          */
1737         std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder_ = nullptr;
1738 };
1739
1740 Mdrunner::BuilderImplementation::BuilderImplementation(std::unique_ptr<MDModules> mdModules,
1741                                                        SimulationContext        * context) :
1742     mdModules_(std::move(mdModules)),
1743     context_(context)
1744 {
1745     GMX_ASSERT(context_, "Bug found. It should not be possible to construct builder without a valid context.");
1746 }
1747
1748 Mdrunner::BuilderImplementation::~BuilderImplementation() = default;
1749
1750 Mdrunner::BuilderImplementation &
1751 Mdrunner::BuilderImplementation::setExtraMdrunOptions(const MdrunOptions    &options,
1752                                                       const real             forceWarningThreshold,
1753                                                       const StartingBehavior startingBehavior)
1754 {
1755     mdrunOptions_          = options;
1756     forceWarningThreshold_ = forceWarningThreshold;
1757     startingBehavior_      = startingBehavior;
1758     return *this;
1759 }
1760
1761 void Mdrunner::BuilderImplementation::addDomdec(const DomdecOptions &options)
1762 {
1763     domdecOptions_ = options;
1764 }
1765
1766 void Mdrunner::BuilderImplementation::addVerletList(int nstlist)
1767 {
1768     nstlist_ = nstlist;
1769 }
1770
1771 void Mdrunner::BuilderImplementation::addReplicaExchange(const ReplicaExchangeParameters &params)
1772 {
1773     replicaExchangeParameters_ = params;
1774 }
1775
1776 void Mdrunner::BuilderImplementation::addMultiSim(gmx_multisim_t* multisim)
1777 {
1778     multisim_ = std::make_unique<gmx_multisim_t*>(multisim);
1779 }
1780
1781 Mdrunner Mdrunner::BuilderImplementation::build()
1782 {
1783     auto newRunner = Mdrunner(std::move(mdModules_));
1784
1785     GMX_ASSERT(context_, "Bug found. It should not be possible to call build() without a valid context.");
1786
1787     newRunner.mdrunOptions          = mdrunOptions_;
1788     newRunner.pforce                = forceWarningThreshold_;
1789     newRunner.startingBehavior      = startingBehavior_;
1790     newRunner.domdecOptions         = domdecOptions_;
1791
1792     // \todo determine an invariant to check or confirm that all gmx_hw_opt_t objects are valid
1793     newRunner.hw_opt          = hardwareOptions_;
1794
1795     // No invariant to check. This parameter exists to optionally override other behavior.
1796     newRunner.nstlist_cmdline = nstlist_;
1797
1798     newRunner.replExParams    = replicaExchangeParameters_;
1799
1800     newRunner.filenames = filenames_;
1801
1802     GMX_ASSERT(context_->communicationRecord_, "SimulationContext communications not initialized.");
1803     newRunner.cr = context_->communicationRecord_;
1804
1805     if (multisim_)
1806     {
1807         // nullptr is a valid value for the multisim handle, so we don't check the pointed-to pointer.
1808         newRunner.ms = *multisim_;
1809     }
1810     else
1811     {
1812         GMX_THROW(gmx::APIError("MdrunnerBuilder::addMultiSim() is required before build()"));
1813     }
1814
1815     // \todo Clarify ownership and lifetime management for gmx_output_env_t
1816     // \todo Update sanity checking when output environment has clearly specified invariants.
1817     // Initialization and default values for oenv are not well specified in the current version.
1818     if (outputEnvironment_)
1819     {
1820         newRunner.oenv  = outputEnvironment_;
1821     }
1822     else
1823     {
1824         GMX_THROW(gmx::APIError("MdrunnerBuilder::addOutputEnvironment() is required before build()"));
1825     }
1826
1827     newRunner.logFileHandle = logFileHandle_;
1828
1829     if (nbpu_opt_)
1830     {
1831         newRunner.nbpu_opt    = nbpu_opt_;
1832     }
1833     else
1834     {
1835         GMX_THROW(gmx::APIError("MdrunnerBuilder::addNonBonded() is required before build()"));
1836     }
1837
1838     if (pme_opt_ && pme_fft_opt_)
1839     {
1840         newRunner.pme_opt     = pme_opt_;
1841         newRunner.pme_fft_opt = pme_fft_opt_;
1842     }
1843     else
1844     {
1845         GMX_THROW(gmx::APIError("MdrunnerBuilder::addElectrostatics() is required before build()"));
1846     }
1847
1848     if (bonded_opt_)
1849     {
1850         newRunner.bonded_opt = bonded_opt_;
1851     }
1852     else
1853     {
1854         GMX_THROW(gmx::APIError("MdrunnerBuilder::addBondedTaskAssignment() is required before build()"));
1855     }
1856
1857     newRunner.restraintManager_ = std::make_unique<gmx::RestraintManager>();
1858
1859     if (stopHandlerBuilder_)
1860     {
1861         newRunner.stopHandlerBuilder_ = std::move(stopHandlerBuilder_);
1862     }
1863     else
1864     {
1865         newRunner.stopHandlerBuilder_ = std::make_unique<StopHandlerBuilder>();
1866     }
1867
1868     return newRunner;
1869 }
1870
1871 void Mdrunner::BuilderImplementation::addNonBonded(const char* nbpu_opt)
1872 {
1873     nbpu_opt_ = nbpu_opt;
1874 }
1875
1876 void Mdrunner::BuilderImplementation::addPME(const char* pme_opt,
1877                                              const char* pme_fft_opt)
1878 {
1879     pme_opt_     = pme_opt;
1880     pme_fft_opt_ = pme_fft_opt;
1881 }
1882
1883 void Mdrunner::BuilderImplementation::addBondedTaskAssignment(const char* bonded_opt)
1884 {
1885     bonded_opt_ = bonded_opt;
1886 }
1887
1888 void Mdrunner::BuilderImplementation::addHardwareOptions(const gmx_hw_opt_t &hardwareOptions)
1889 {
1890     hardwareOptions_ = hardwareOptions;
1891 }
1892
1893 void Mdrunner::BuilderImplementation::addFilenames(ArrayRef<const t_filenm> filenames)
1894 {
1895     filenames_ = filenames;
1896 }
1897
1898 void Mdrunner::BuilderImplementation::addOutputEnvironment(gmx_output_env_t* outputEnvironment)
1899 {
1900     outputEnvironment_ = outputEnvironment;
1901 }
1902
1903 void Mdrunner::BuilderImplementation::addLogFile(t_fileio *logFileHandle)
1904 {
1905     logFileHandle_ = logFileHandle;
1906 }
1907
1908 void Mdrunner::BuilderImplementation::addStopHandlerBuilder(std::unique_ptr<StopHandlerBuilder> builder)
1909 {
1910     stopHandlerBuilder_ = std::move(builder);
1911 }
1912
1913 MdrunnerBuilder::MdrunnerBuilder(std::unique_ptr<MDModules>           mdModules,
1914                                  compat::not_null<SimulationContext*> context) :
1915     impl_ {std::make_unique<Mdrunner::BuilderImplementation>(std::move(mdModules), context)}
1916 {
1917 }
1918
1919 MdrunnerBuilder::~MdrunnerBuilder() = default;
1920
1921 MdrunnerBuilder &MdrunnerBuilder::addSimulationMethod(const MdrunOptions    &options,
1922                                                       real                   forceWarningThreshold,
1923                                                       const StartingBehavior startingBehavior)
1924 {
1925     impl_->setExtraMdrunOptions(options, forceWarningThreshold, startingBehavior);
1926     return *this;
1927 }
1928
1929 MdrunnerBuilder &MdrunnerBuilder::addDomainDecomposition(const DomdecOptions &options)
1930 {
1931     impl_->addDomdec(options);
1932     return *this;
1933 }
1934
1935 MdrunnerBuilder &MdrunnerBuilder::addNeighborList(int nstlist)
1936 {
1937     impl_->addVerletList(nstlist);
1938     return *this;
1939 }
1940
1941 MdrunnerBuilder &MdrunnerBuilder::addReplicaExchange(const ReplicaExchangeParameters &params)
1942 {
1943     impl_->addReplicaExchange(params);
1944     return *this;
1945 }
1946
1947 MdrunnerBuilder &MdrunnerBuilder::addMultiSim(gmx_multisim_t* multisim)
1948 {
1949     impl_->addMultiSim(multisim);
1950     return *this;
1951 }
1952
1953 MdrunnerBuilder &MdrunnerBuilder::addNonBonded(const char* nbpu_opt)
1954 {
1955     impl_->addNonBonded(nbpu_opt);
1956     return *this;
1957 }
1958
1959 MdrunnerBuilder &MdrunnerBuilder::addElectrostatics(const char* pme_opt,
1960                                                     const char* pme_fft_opt)
1961 {
1962     // The builder method may become more general in the future, but in this version,
1963     // parameters for PME electrostatics are both required and the only parameters
1964     // available.
1965     if (pme_opt && pme_fft_opt)
1966     {
1967         impl_->addPME(pme_opt, pme_fft_opt);
1968     }
1969     else
1970     {
1971         GMX_THROW(gmx::InvalidInputError("addElectrostatics() arguments must be non-null pointers."));
1972     }
1973     return *this;
1974 }
1975
1976 MdrunnerBuilder &MdrunnerBuilder::addBondedTaskAssignment(const char* bonded_opt)
1977 {
1978     impl_->addBondedTaskAssignment(bonded_opt);
1979     return *this;
1980 }
1981
1982 Mdrunner MdrunnerBuilder::build()
1983 {
1984     return impl_->build();
1985 }
1986
1987 MdrunnerBuilder &MdrunnerBuilder::addHardwareOptions(const gmx_hw_opt_t &hardwareOptions)
1988 {
1989     impl_->addHardwareOptions(hardwareOptions);
1990     return *this;
1991 }
1992
1993 MdrunnerBuilder &MdrunnerBuilder::addFilenames(ArrayRef<const t_filenm> filenames)
1994 {
1995     impl_->addFilenames(filenames);
1996     return *this;
1997 }
1998
1999 MdrunnerBuilder &MdrunnerBuilder::addOutputEnvironment(gmx_output_env_t* outputEnvironment)
2000 {
2001     impl_->addOutputEnvironment(outputEnvironment);
2002     return *this;
2003 }
2004
2005 MdrunnerBuilder &MdrunnerBuilder::addLogFile(t_fileio *logFileHandle)
2006 {
2007     impl_->addLogFile(logFileHandle);
2008     return *this;
2009 }
2010
2011 MdrunnerBuilder &MdrunnerBuilder::addStopHandlerBuilder(std::unique_ptr<StopHandlerBuilder> builder)
2012 {
2013     impl_->addStopHandlerBuilder(std::move(builder));
2014     return *this;
2015 }
2016
2017 MdrunnerBuilder::MdrunnerBuilder(MdrunnerBuilder &&) noexcept = default;
2018
2019 MdrunnerBuilder &MdrunnerBuilder::operator=(MdrunnerBuilder &&) noexcept = default;
2020
2021 } // namespace gmx