1 /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
4 * This source code is part of
8 * GROningen MAchine for Chemical Simulations
11 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
12 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
13 * Copyright (c) 2001-2004, The GROMACS development team,
14 * check out http://www.gromacs.org for more information.
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
21 * If you want to redistribute modifications, please consider that
22 * scientific software is very special. Version control is crucial -
23 * bugs must be traceable. We will be happy to consider code for
24 * inclusion in the official distribution, but derived work must not
25 * be called official GROMACS. Details are found in the README & COPYING
26 * files - if they are missing, get the official version at www.gromacs.org.
28 * To help us fund GROMACS development, we humbly ask that you cite
29 * the papers on the package - you can find them in the top README file.
31 * For more info, check our website at http://www.gromacs.org
34 * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
42 #include <sys/syscall.h>
47 #if ((defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64) && !defined __CYGWIN__ && !defined __CYGWIN32__)
68 #include "mpelogging.h"
74 #include "checkpoint.h"
75 #include "mtop_util.h"
76 #include "sighandler.h"
79 #include "pull_rotation.h"
80 #include "md_openmm.h"
94 #include "md_openmm.h"
103 gmx_integrator_t *func;
106 /* The array should match the eI array in include/types/enums.h */
107 #ifdef GMX_OPENMM /* FIXME do_md_openmm needs fixing */
108 const gmx_intp_t integrator[eiNR] = { {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm}, {do_md_openmm},{do_md_openmm}};
110 const gmx_intp_t integrator[eiNR] = { {do_md}, {do_steep}, {do_cg}, {do_md}, {do_md}, {do_nm}, {do_lbfgs}, {do_tpi}, {do_tpi}, {do_md}, {do_md},{do_md}};
113 gmx_large_int_t deform_init_init_step_tpx;
114 matrix deform_init_box_tpx;
116 tMPI_Thread_mutex_t deform_init_box_mutex=TMPI_THREAD_MUTEX_INITIALIZER;
121 struct mdrunner_arglist
135 const char *dddlb_opt;
148 const char *deviceOptions;
150 int ret; /* return value */
154 /* The function used for spawning threads. Extracts the mdrunner()
155 arguments from its one argument and calls mdrunner(), after making
157 static void mdrunner_start_fn(void *arg)
159 struct mdrunner_arglist *mda=(struct mdrunner_arglist*)arg;
160 struct mdrunner_arglist mc=*mda; /* copy the arg list to make sure
161 that it's thread-local. This doesn't
162 copy pointed-to items, of course,
163 but those are all const. */
164 t_commrec *cr; /* we need a local version of this */
168 fnm = dup_tfn(mc.nfile, mc.fnm);
170 cr = init_par_threads(mc.cr);
177 mda->ret=mdrunner(cr->nnodes, fplog, cr, mc.nfile, fnm, mc.oenv,
178 mc.bVerbose, mc.bCompact, mc.nstglobalcomm,
179 mc.ddxyz, mc.dd_node_order, mc.rdd,
180 mc.rconstr, mc.dddlb_opt, mc.dlb_scale,
181 mc.ddcsx, mc.ddcsy, mc.ddcsz, mc.nstepout, mc.resetstep,
182 mc.nmultisim, mc.repl_ex_nst, mc.repl_ex_seed, mc.pforce,
183 mc.cpt_period, mc.max_hours, mc.deviceOptions, mc.Flags);
186 /* called by mdrunner() to start a specific number of threads (including
187 the main thread) for thread-parallel runs. This in turn calls mdrunner()
189 All options besides nthreads are the same as for mdrunner(). */
190 static t_commrec *mdrunner_start_threads(int nthreads,
191 FILE *fplog,t_commrec *cr,int nfile,
192 const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
193 gmx_bool bCompact, int nstglobalcomm,
194 ivec ddxyz,int dd_node_order,real rdd,real rconstr,
195 const char *dddlb_opt,real dlb_scale,
196 const char *ddcsx,const char *ddcsy,const char *ddcsz,
197 int nstepout,int resetstep,int nmultisim,int repl_ex_nst,
198 int repl_ex_seed, real pforce,real cpt_period, real max_hours,
199 const char *deviceOptions, unsigned long Flags)
202 struct mdrunner_arglist *mda;
203 t_commrec *crn; /* the new commrec */
206 /* first check whether we even need to start tMPI */
210 /* a few small, one-time, almost unavoidable memory leaks: */
212 fnmn=dup_tfn(nfile, fnm);
214 /* fill the data structure to pass as void pointer to thread start fn */
220 mda->bVerbose=bVerbose;
221 mda->bCompact=bCompact;
222 mda->nstglobalcomm=nstglobalcomm;
223 mda->ddxyz[XX]=ddxyz[XX];
224 mda->ddxyz[YY]=ddxyz[YY];
225 mda->ddxyz[ZZ]=ddxyz[ZZ];
226 mda->dd_node_order=dd_node_order;
228 mda->rconstr=rconstr;
229 mda->dddlb_opt=dddlb_opt;
230 mda->dlb_scale=dlb_scale;
234 mda->nstepout=nstepout;
235 mda->resetstep=resetstep;
236 mda->nmultisim=nmultisim;
237 mda->repl_ex_nst=repl_ex_nst;
238 mda->repl_ex_seed=repl_ex_seed;
240 mda->cpt_period=cpt_period;
241 mda->max_hours=max_hours;
242 mda->deviceOptions=deviceOptions;
245 fprintf(stderr, "Starting %d threads\n",nthreads);
247 /* now spawn new threads that start mdrunner_start_fn(), while
248 the main thread returns */
249 ret=tMPI_Init_fn(TRUE, nthreads, mdrunner_start_fn, (void*)(mda) );
250 if (ret!=TMPI_SUCCESS)
253 /* make a new comm_rec to reflect the new situation */
254 crn=init_par_threads(cr);
259 /* Get the number of threads to use for thread-MPI based on how many
260 * were requested, which algorithms we're using,
261 * and how many particles there are.
263 static int get_nthreads_mpi(int nthreads_requested, t_inputrec *inputrec,
266 int nthreads,nthreads_new;
267 int min_atoms_per_thread;
270 nthreads = nthreads_requested;
272 /* determine # of hardware threads. */
273 if (nthreads_requested < 1)
275 if ((env = getenv("GMX_MAX_THREADS")) != NULL)
278 sscanf(env,"%d",&nthreads);
281 gmx_fatal(FARGS,"GMX_MAX_THREADS (%d) should be larger than 0",
287 nthreads = tMPI_Thread_get_hw_number();
291 if (inputrec->eI == eiNM || EI_TPI(inputrec->eI))
293 /* Steps are divided over the nodes iso splitting the atoms */
294 min_atoms_per_thread = 0;
298 min_atoms_per_thread = MIN_ATOMS_PER_THREAD;
301 /* Check if an algorithm does not support parallel simulation. */
303 ( inputrec->eI == eiLBFGS ||
304 inputrec->coulombtype == eelEWALD ) )
306 fprintf(stderr,"\nThe integration or electrostatics algorithm doesn't support parallel runs. Not starting any threads.\n");
309 else if (nthreads_requested < 1 &&
310 mtop->natoms/nthreads < min_atoms_per_thread)
312 /* the thread number was chosen automatically, but there are too many
313 threads (too few atoms per thread) */
314 nthreads_new = max(1,mtop->natoms/min_atoms_per_thread);
316 if (nthreads_new > 8 || (nthreads == 8 && nthreads_new > 4))
318 /* Use only multiples of 4 above 8 threads
319 * or with an 8-core processor
320 * (to avoid 6 threads on 8 core processors with 4 real cores).
322 nthreads_new = (nthreads_new/4)*4;
324 else if (nthreads_new > 4)
326 /* Avoid 5 or 7 threads */
327 nthreads_new = (nthreads_new/2)*2;
330 nthreads = nthreads_new;
332 fprintf(stderr,"\n");
333 fprintf(stderr,"NOTE: Parallelization is limited by the small number of atoms,\n");
334 fprintf(stderr," only starting %d threads.\n",nthreads);
335 fprintf(stderr," You can use the -nt option to optimize the number of threads.\n\n");
342 int mdrunner(int nthreads_requested, FILE *fplog,t_commrec *cr,int nfile,
343 const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
344 gmx_bool bCompact, int nstglobalcomm,
345 ivec ddxyz,int dd_node_order,real rdd,real rconstr,
346 const char *dddlb_opt,real dlb_scale,
347 const char *ddcsx,const char *ddcsy,const char *ddcsz,
348 int nstepout,int resetstep,int nmultisim,int repl_ex_nst,
349 int repl_ex_seed, real pforce,real cpt_period,real max_hours,
350 const char *deviceOptions, unsigned long Flags)
352 double nodetime=0,realtime;
353 t_inputrec *inputrec;
356 gmx_ddbox_t ddbox={0};
357 int npme_major,npme_minor;
360 gmx_mtop_t *mtop=NULL;
361 t_mdatoms *mdatoms=NULL;
365 gmx_pme_t *pmedata=NULL;
366 gmx_vsite_t *vsite=NULL;
368 int i,m,nChargePerturbed=-1,status,nalloc;
370 gmx_wallcycle_t wcycle;
371 gmx_bool bReadRNG,bReadEkin;
373 gmx_runtime_t runtime;
375 gmx_large_int_t reset_counters;
377 t_commrec *cr_old=cr;
381 /* CAUTION: threads may be started later on in this function, so
382 cr doesn't reflect the final parallel state right now */
386 if (bVerbose && SIMMASTER(cr))
388 fprintf(stderr,"Getting Loaded...\n");
391 if (Flags & MD_APPENDFILES)
399 /* Read (nearly) all data required for the simulation */
400 read_tpx_state(ftp2fn(efTPX,nfile,fnm),inputrec,state,NULL,mtop);
402 /* NOW the threads will be started: */
404 nthreads_mpi = get_nthreads_mpi(nthreads_requested, inputrec, mtop);
406 if (nthreads_mpi > 1)
408 /* now start the threads. */
409 cr=mdrunner_start_threads(nthreads_mpi, fplog, cr_old, nfile, fnm,
410 oenv, bVerbose, bCompact, nstglobalcomm,
411 ddxyz, dd_node_order, rdd, rconstr,
412 dddlb_opt, dlb_scale, ddcsx, ddcsy, ddcsz,
413 nstepout, resetstep, nmultisim,
414 repl_ex_nst, repl_ex_seed, pforce,
415 cpt_period, max_hours, deviceOptions,
417 /* the main thread continues here with a new cr. We don't deallocate
418 the old cr because other threads may still be reading it. */
421 gmx_comm("Failed to spawn threads");
426 /* END OF CAUTION: cr is now reliable */
430 /* now broadcast everything to the non-master nodes/threads: */
431 init_parallel(fplog, cr, inputrec, mtop);
435 pr_inputrec(fplog,0,"Input Parameters",inputrec,FALSE);
438 /* now make sure the state is initialized and propagated */
439 set_state_entries(state,inputrec,cr->nnodes);
441 /* A parallel command line option consistency check that we can
442 only do after any threads have started. */
444 (ddxyz[XX] > 1 || ddxyz[YY] > 1 || ddxyz[ZZ] > 1 || cr->npmenodes > 0))
447 "The -dd or -npme option request a parallel simulation, "
449 "but mdrun was compiled without threads or MPI enabled"
452 "but the number of threads (option -nt) is 1"
454 "but mdrun was not started through mpirun/mpiexec or only one process was requested through mpirun/mpiexec"
460 if ((Flags & MD_RERUN) &&
461 (EI_ENERGY_MINIMIZATION(inputrec->eI) || eiNM == inputrec->eI))
463 gmx_fatal(FARGS, "The .mdp file specified an energy mininization or normal mode algorithm, and these are not compatible with mdrun -rerun");
466 if (can_use_allvsall(inputrec,mtop,TRUE,cr,fplog))
468 /* All-vs-all loops do not work with domain decomposition */
472 if (!EEL_PME(inputrec->coulombtype) || (Flags & MD_PARTDEC))
478 fcRegisterSteps(inputrec->nsteps,inputrec->init_step);
481 /* NMR restraints must be initialized before load_checkpoint,
482 * since with time averaging the history is added to t_state.
483 * For proper consistency check we therefore need to extend
485 * So the PME-only nodes (if present) will also initialize
486 * the distance restraints.
490 /* This needs to be called before read_checkpoint to extend the state */
491 init_disres(fplog,mtop,inputrec,cr,Flags & MD_PARTDEC,fcd,state);
493 if (gmx_mtop_ftype_count(mtop,F_ORIRES) > 0)
495 if (PAR(cr) && !(Flags & MD_PARTDEC))
497 gmx_fatal(FARGS,"Orientation restraints do not work (yet) with domain decomposition, use particle decomposition (mdrun option -pd)");
499 /* Orientation restraints */
502 init_orires(fplog,mtop,state->x,inputrec,cr->ms,&(fcd->orires),
507 if (DEFORM(*inputrec))
509 /* Store the deform reference box before reading the checkpoint */
512 copy_mat(state->box,box);
516 gmx_bcast(sizeof(box),box,cr);
518 /* Because we do not have the update struct available yet
519 * in which the reference values should be stored,
520 * we store them temporarily in static variables.
521 * This should be thread safe, since they are only written once
522 * and with identical values.
525 tMPI_Thread_mutex_lock(&deform_init_box_mutex);
527 deform_init_init_step_tpx = inputrec->init_step;
528 copy_mat(box,deform_init_box_tpx);
530 tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
534 if (opt2bSet("-cpi",nfile,fnm))
536 /* Check if checkpoint file exists before doing continuation.
537 * This way we can use identical input options for the first and subsequent runs...
539 if( gmx_fexist_master(opt2fn_master("-cpi",nfile,fnm,cr),cr) )
541 load_checkpoint(opt2fn_master("-cpi",nfile,fnm,cr),&fplog,
542 cr,Flags & MD_PARTDEC,ddxyz,
543 inputrec,state,&bReadRNG,&bReadEkin,
544 (Flags & MD_APPENDFILES));
548 Flags |= MD_READ_RNG;
552 Flags |= MD_READ_EKIN;
557 if (((MASTER(cr) || (Flags & MD_SEPPOT)) && (Flags & MD_APPENDFILES))
559 /* With thread MPI only the master node/thread exists in mdrun.c,
560 * therefore non-master nodes need to open the "seppot" log file here.
562 || (!MASTER(cr) && (Flags & MD_SEPPOT))
566 gmx_log_open(ftp2fn(efLOG,nfile,fnm),cr,!(Flags & MD_SEPPOT),
572 copy_mat(state->box,box);
577 gmx_bcast(sizeof(box),box,cr);
580 /* Essential dynamics */
581 if (opt2bSet("-ei",nfile,fnm))
583 /* Open input and output files, allocate space for ED data structure */
584 ed = ed_open(nfile,fnm,Flags,cr);
587 if (bVerbose && SIMMASTER(cr))
589 fprintf(stderr,"Loaded with Money\n\n");
592 if (PAR(cr) && !((Flags & MD_PARTDEC) ||
593 EI_TPI(inputrec->eI) ||
594 inputrec->eI == eiNM))
596 cr->dd = init_domain_decomposition(fplog,cr,Flags,ddxyz,rdd,rconstr,
601 &ddbox,&npme_major,&npme_minor);
603 make_dd_communicators(fplog,cr,dd_node_order);
605 /* Set overallocation to avoid frequent reallocation of arrays */
606 set_over_alloc_dd(TRUE);
610 /* PME, if used, is done on all nodes with 1D decomposition */
612 cr->duty = (DUTY_PP | DUTY_PME);
615 if (!EI_TPI(inputrec->eI))
617 npme_major = cr->nnodes;
620 if (inputrec->ePBC == epbcSCREW)
623 "pbc=%s is only implemented with domain decomposition",
624 epbc_names[inputrec->ePBC]);
630 /* After possible communicator splitting in make_dd_communicators.
631 * we can set up the intra/inter node communication.
633 gmx_setup_nodecomm(fplog,cr);
636 /* get number of OpenMP/PME threads
637 * env variable should be read only on one node to make sure it is identical everywhere */
639 if (EEL_PME(inputrec->coulombtype))
644 if ((ptr=getenv("GMX_PME_NTHREADS")) != NULL)
646 sscanf(ptr,"%d",&nthreads_pme);
648 if (fplog != NULL && nthreads_pme > 1)
650 fprintf(fplog,"Using %d threads for PME\n",nthreads_pme);
655 gmx_bcast_sim(sizeof(nthreads_pme),&nthreads_pme,cr);
660 wcycle = wallcycle_init(fplog,resetstep,cr,nthreads_pme);
663 /* Master synchronizes its value of reset_counters with all nodes
664 * including PME only nodes */
665 reset_counters = wcycle_get_reset_counters(wcycle);
666 gmx_bcast_sim(sizeof(reset_counters),&reset_counters,cr);
667 wcycle_set_reset_counters(wcycle, reset_counters);
672 if (cr->duty & DUTY_PP)
674 /* For domain decomposition we allocate dynamically
675 * in dd_partition_system.
677 if (DOMAINDECOMP(cr))
679 bcast_state_setup(cr,state);
685 bcast_state(cr,state,TRUE);
689 /* Dihedral Restraints */
690 if (gmx_mtop_ftype_count(mtop,F_DIHRES) > 0)
692 init_dihres(fplog,mtop,inputrec,fcd);
695 /* Initiate forcerecord */
697 init_forcerec(fplog,oenv,fr,fcd,inputrec,mtop,cr,box,FALSE,
698 opt2fn("-table",nfile,fnm),
699 opt2fn("-tabletf",nfile,fnm),
700 opt2fn("-tablep",nfile,fnm),
701 opt2fn("-tableb",nfile,fnm),FALSE,pforce);
703 /* version for PCA_NOT_READ_NODE (see md.c) */
704 /*init_forcerec(fplog,fr,fcd,inputrec,mtop,cr,box,FALSE,
705 "nofile","nofile","nofile","nofile",FALSE,pforce);
707 fr->bSepDVDL = ((Flags & MD_SEPPOT) == MD_SEPPOT);
709 /* Initialize QM-MM */
712 init_QMMMrec(cr,box,mtop,inputrec,fr);
715 /* Initialize the mdatoms structure.
716 * mdatoms is not filled with atom data,
717 * as this can not be done now with domain decomposition.
719 mdatoms = init_mdatoms(fplog,mtop,inputrec->efep!=efepNO);
721 /* Initialize the virtual site communication */
722 vsite = init_vsite(mtop,cr);
724 calc_shifts(box,fr->shift_vec);
726 /* With periodic molecules the charge groups should be whole at start up
727 * and the virtual sites should not be far from their proper positions.
729 if (!inputrec->bContinuation && MASTER(cr) &&
730 !(inputrec->ePBC != epbcNONE && inputrec->bPeriodicMols))
732 /* Make molecules whole at start of run */
733 if (fr->ePBC != epbcNONE)
735 do_pbc_first_mtop(fplog,inputrec->ePBC,box,mtop,state->x);
739 /* Correct initial vsite positions are required
740 * for the initial distribution in the domain decomposition
741 * and for the initial shell prediction.
743 construct_vsites_mtop(fplog,vsite,mtop,state->x);
747 /* Initiate PPPM if necessary */
748 if (fr->eeltype == eelPPPM)
750 if (mdatoms->nChargePerturbed)
752 gmx_fatal(FARGS,"Free energy with %s is not implemented",
753 eel_names[fr->eeltype]);
755 status = gmx_pppm_init(fplog,cr,oenv,FALSE,TRUE,box,
756 getenv("GMXGHAT"),inputrec, (Flags & MD_REPRODUCIBLE));
759 gmx_fatal(FARGS,"Error %d initializing PPPM",status);
763 if (EEL_PME(fr->eeltype))
765 ewaldcoeff = fr->ewaldcoeff;
766 pmedata = &fr->pmedata;
775 /* This is a PME only node */
777 /* We don't need the state */
780 ewaldcoeff = calc_ewaldcoeff(inputrec->rcoulomb, inputrec->ewald_rtol);
784 /* Initiate PME if necessary,
785 * either on all nodes or on dedicated PME nodes only. */
786 if (EEL_PME(inputrec->coulombtype))
790 nChargePerturbed = mdatoms->nChargePerturbed;
792 if (cr->npmenodes > 0)
794 /* The PME only nodes need to know nChargePerturbed */
795 gmx_bcast_sim(sizeof(nChargePerturbed),&nChargePerturbed,cr);
799 /* Set CPU affinity. Can be important for performance.
800 On some systems (e.g. Cray) CPU Affinity is set by default.
801 But default assigning doesn't work (well) with only some ranks
802 having threads. This causes very low performance.
803 External tools have cumbersome syntax for setting affinity
804 in the case that only some ranks have threads.
805 Thus it is important that GROMACS sets the affinity internally at
806 if only PME is using threads.
814 MPI_Comm comm_intra; /* intra communicator (but different to nc.comm_intra includes PME nodes) */
815 MPI_Comm_split(MPI_COMM_WORLD,gmx_hostname_num(),gmx_node_rank(),&comm_intra);
816 int local_omp_nthreads = (cr->duty & DUTY_PME) ? nthreads_pme : 1; /* threads on this node */
817 MPI_Scan(&local_omp_nthreads,&core, 1, MPI_INT, MPI_SUM, comm_intra);
818 core-=local_omp_nthreads; /* make exclusive scan */
819 #pragma omp parallel firstprivate(core) num_threads(local_omp_nthreads)
823 core+=omp_get_thread_num();
825 sched_setaffinity((pid_t) syscall (SYS_gettid),sizeof(cpu_set_t),&mask);
830 #endif /*GMX_OPENMP*/
832 if (cr->duty & DUTY_PME)
834 status = gmx_pme_init(pmedata,cr,npme_major,npme_minor,inputrec,
835 mtop ? mtop->natoms : 0,nChargePerturbed,
836 (Flags & MD_REPRODUCIBLE),nthreads_pme);
839 gmx_fatal(FARGS,"Error %d initializing PME",status);
845 if (integrator[inputrec->eI].func == do_md
848 integrator[inputrec->eI].func == do_md_openmm
852 /* Turn on signal handling on all nodes */
854 * (A user signal from the PME nodes (if any)
855 * is communicated to the PP nodes.
857 signal_handler_install();
860 if (cr->duty & DUTY_PP)
862 if (inputrec->ePull != epullNO)
864 /* Initialize pull code */
865 init_pull(fplog,inputrec,nfile,fnm,mtop,cr,oenv,
866 EI_DYNAMICS(inputrec->eI) && MASTER(cr),Flags);
871 /* Initialize enforced rotation code */
872 init_rot(fplog,inputrec,nfile,fnm,cr,state->x,state->box,mtop,oenv,
876 constr = init_constraints(fplog,mtop,inputrec,ed,state,cr);
878 if (DOMAINDECOMP(cr))
880 dd_init_bondeds(fplog,cr->dd,mtop,vsite,constr,inputrec,
881 Flags & MD_DDBONDCHECK,fr->cginfo_mb);
883 set_dd_parameters(fplog,cr->dd,dlb_scale,inputrec,fr,&ddbox);
885 setup_dd_grid(fplog,cr->dd);
888 /* Now do whatever the user wants us to do (how flexible...) */
889 integrator[inputrec->eI].func(fplog,cr,nfile,fnm,
890 oenv,bVerbose,bCompact,
893 nstepout,inputrec,mtop,
895 mdatoms,nrnb,wcycle,ed,fr,
896 repl_ex_nst,repl_ex_seed,
897 cpt_period,max_hours,
902 if (inputrec->ePull != epullNO)
904 finish_pull(fplog,inputrec->pull);
909 finish_rot(fplog,inputrec->rot);
916 gmx_pmeonly(*pmedata,cr,nrnb,wcycle,ewaldcoeff,FALSE,inputrec);
919 if (EI_DYNAMICS(inputrec->eI) || EI_TPI(inputrec->eI))
921 /* Some timing stats */
924 if (runtime.proc == 0)
926 runtime.proc = runtime.real;
935 wallcycle_stop(wcycle,ewcRUN);
937 /* Finish up, write some stuff
938 * if rerunMD, don't write last frame again
940 finish_run(fplog,cr,ftp2fn(efSTO,nfile,fnm),
941 inputrec,nrnb,wcycle,&runtime,
942 EI_DYNAMICS(inputrec->eI) && !MULTISIM(cr));
944 /* Does what it says */
945 print_date_and_time(fplog,cr->nodeid,"Finished mdrun",&runtime);
947 /* Close logfile already here if we were appending to it */
948 if (MASTER(cr) && (Flags & MD_APPENDFILES))
950 gmx_log_close(fplog);
953 rc=(int)gmx_get_stop_condition();
956 /* we need to join all threads. The sub-threads join when they
957 exit this function, but the master thread needs to be told to
959 if (PAR(cr) && MASTER(cr))