From: Roland Schulz Date: Mon, 26 May 2014 08:49:25 +0000 (-0400) Subject: Merge release-4-6 into release-5-0 X-Git-Url: http://biod.pnpi.spb.ru/gitweb/?a=commitdiff_plain;h=074166a7ebad3bc5dc626d1958ec62c936f41ae0;p=alexxy%2Fgromacs.git Merge release-4-6 into release-5-0 Conflicts: CMakeLists.txt - fixed use of GMX_CPU_ACCELERATION src/gromacs/gmxana/gmx_lie.c src/gromacs/pulling/pull.c Change-Id: I45a70eee6f7ec4dd922b6c04c740c40f50159a45 --- 074166a7ebad3bc5dc626d1958ec62c936f41ae0 diff --cc CMakeLists.txt index 03f24b6f0c,e69ee1ef72..4f1f62b6dd --- a/CMakeLists.txt +++ b/CMakeLists.txt @@@ -316,14 -281,56 +316,26 @@@ include(gmxCFlags gmx_c_flags() # This variable should be used for additional compiler flags which are not -# generated in gmxCFlags nor are acceleration or MPI related. +# generated in gmxCFlags nor are SIMD or MPI related. set(EXTRA_C_FLAGS "") +set(EXTRA_CXX_FLAGS "") -# gcc 4.4.x is buggy and crashes when compiling some files with O3 and OpenMP on. -# Detect here whether applying a workaround is needed and will apply it later -# on the affected files. -include(gmxGCC44O3BugWorkaround) -gmx_check_gcc44_bug_workaround_needed(GMX_USE_GCC44_BUG_WORKAROUND) - -# clang 3.0 is buggy for some unknown reason detected during adding -# the SSE2 group kernels for GROMACS 4.6. If we ever work out what -# that is, we should replace these tests with a compiler feature test, -# update GROMACS Redmine task #1039 and perhaps report a clang bug. -# -# In the meantime, until we require CMake 2.8.10 we cannot rely on it to detect -# the compiler version for us. So we need a manual check for clang 3.0. -include(gmxDetectClang30) -gmx_detect_clang_3_0(COMPILER_IS_CLANG_3_0) -if(COMPILER_IS_CLANG_3_0) - message(FATAL_ERROR "Your compiler is clang version 3.0, which is known to be buggy for GROMACS. Use a different compiler.") -endif() - -# clang <=3.2 contains a bug that causes incorrect code to be generated for the -# vfmaddps instruction and therefore the bug is triggered with AVX_128_FMA. -# (see: http://llvm.org/bugs/show_bug.cgi?id=15040). -# We can work around this by not using the integrated assembler (except on OS X -# which has an outdated assembler that does not support AVX instructions). -if (${CMAKE_C_COMPILER_ID} MATCHES "Clang" AND C_COMPILER_VERSION VERSION_LESS "3.3") - set(GMX_USE_CLANG_FMA_BUG_WORKAROUND TRUE) -endif() - -# GMX_CPU_ACCELERATION will not be set automatically until the second +# Run through a number of tests for buggy compilers and other issues +include(gmxTestCompilerProblems) +gmx_test_compiler_problems() ++# GMX_SIMD will not be set automatically until the second + # pass (which is not strictly guaranteed to occur), so putting this + # check here among logically-related tests is inefficient, but the + # potential loss is likely zero. -if(GMX_CPU_ACCELERATION STREQUAL "AVX_256" ++if(GMX_SIMD STREQUAL "AVX_256" + AND CMAKE_COMPILER_IS_GNUCC + AND (C_COMPILER_VERSION VERSION_EQUAL "4.6.1" + OR CXX_COMPILER_VERSION VERSION_EQUAL "4.6.1")) - message(FATAL_ERROR "gcc 4.6.1 has buggy support for AVX, and GROMACS mdrun will not work. If you want simulation performance, use a more recent compiler. Otherwise, use GMX_CPU_ACCELERATION=SSE4.1") ++ message(FATAL_ERROR "gcc 4.6.1 has buggy support for AVX, and GROMACS mdrun will not work. If you want simulation performance, use a more recent compiler. Otherwise, use GMX_SIMD=SSE4.1") + # See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49002 + endif() + -if (CMAKE_C_COMPILER_ID STREQUAL "PGI") - message(WARNING "All tested PGI compiler versions (up to 12.9.0) generate binaries which produce incorrect results, or even fail to compile Gromacs. Highly recommended to use a different compiler. If you choose to use PGI, make sure to run the regressiontests.") -endif() -if(CMAKE_C_COMPILER_ID MATCHES "Intel" AND C_COMPILER_VERSION VERSION_LESS "12.0.0") - message(WARNING "Intel compilers before 12.0.0 are not routinely tested, so there may be problems. Version 11.1 with SSE4.1 is known to produce incorrect results. It is highly recommended to use a more up-to-date compiler. If you choose to use this version, make sure you run the regressiontests.") -endif() set(PKG_CFLAGS "") if(GMX_DOUBLE) diff --cc src/gromacs/gmxana/gmx_covar.c index 501597e9ac,0000000000..c9faa784de mode 100644,000000..100644 --- a/src/gromacs/gmxana/gmx_covar.c +++ b/src/gromacs/gmxana/gmx_covar.c @@@ -1,657 -1,0 +1,660 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include "gromacs/commandline/pargs.h" +#include "sysstuff.h" +#include "typedefs.h" +#include "gromacs/utility/smalloc.h" +#include "macros.h" +#include "vec.h" +#include "pbc.h" +#include "gromacs/fileio/futil.h" +#include "index.h" +#include "gromacs/fileio/confio.h" +#include "gromacs/fileio/trnio.h" +#include "mshift.h" +#include "xvgr.h" +#include "rmpbc.h" +#include "txtdump.h" +#include "gromacs/fileio/matio.h" +#include "eigio.h" +#include "physics.h" +#include "gmx_ana.h" +#include "gromacs/utility/cstringutil.h" +#include "gromacs/fileio/trxio.h" + +#include "gromacs/linearalgebra/eigensolver.h" +#include "gromacs/math/do_fit.h" +#include "gromacs/legacyheaders/gmx_fatal.h" + +int gmx_covar(int argc, char *argv[]) +{ + const char *desc[] = { + "[THISMODULE] calculates and diagonalizes the (mass-weighted)", + "covariance matrix.", + "All structures are fitted to the structure in the structure file.", + "When this is not a run input file periodicity will not be taken into", + "account. When the fit and analysis groups are identical and the analysis", + "is non mass-weighted, the fit will also be non mass-weighted.", + "[PAR]", + "The eigenvectors are written to a trajectory file ([TT]-v[tt]).", + "When the same atoms are used for the fit and the covariance analysis,", + "the reference structure for the fit is written first with t=-1.", + "The average (or reference when [TT]-ref[tt] is used) structure is", + "written with t=0, the eigenvectors", + "are written as frames with the eigenvector number as timestamp.", + "[PAR]", + "The eigenvectors can be analyzed with [gmx-anaeig].", + "[PAR]", + "Option [TT]-ascii[tt] writes the whole covariance matrix to", + "an ASCII file. The order of the elements is: x1x1, x1y1, x1z1, x1x2, ...", + "[PAR]", + "Option [TT]-xpm[tt] writes the whole covariance matrix to an [TT].xpm[tt] file.", + "[PAR]", + "Option [TT]-xpma[tt] writes the atomic covariance matrix to an [TT].xpm[tt] file,", + "i.e. for each atom pair the sum of the xx, yy and zz covariances is", + "written.", + "[PAR]", + "Note that the diagonalization of a matrix requires memory and time", + "that will increase at least as fast as than the square of the number", + "of atoms involved. It is easy to run out of memory, in which", + "case this tool will probably exit with a 'Segmentation fault'. You", + "should consider carefully whether a reduced set of atoms will meet", + "your needs for lower costs." + }; + static gmx_bool bFit = TRUE, bRef = FALSE, bM = FALSE, bPBC = TRUE; + static int end = -1; + t_pargs pa[] = { + { "-fit", FALSE, etBOOL, {&bFit}, + "Fit to a reference structure"}, + { "-ref", FALSE, etBOOL, {&bRef}, + "Use the deviation from the conformation in the structure file instead of from the average" }, + { "-mwa", FALSE, etBOOL, {&bM}, + "Mass-weighted covariance analysis"}, + { "-last", FALSE, etINT, {&end}, + "Last eigenvector to write away (-1 is till the last)" }, + { "-pbc", FALSE, etBOOL, {&bPBC}, + "Apply corrections for periodic boundary conditions" } + }; + FILE *out; + t_trxstatus *status; + t_trxstatus *trjout; + t_topology top; + int ePBC; + t_atoms *atoms; + rvec *x, *xread, *xref, *xav, *xproj; + matrix box, zerobox; + real *sqrtm, *mat, *eigenvalues, sum, trace, inv_nframes; + real t, tstart, tend, **mat2; + real xj, *w_rls = NULL; + real min, max, *axis; + int ntopatoms, step; + int natoms, nat, count, nframes0, nframes, nlevels; + gmx_int64_t ndim, i, j, k, l; + int WriteXref; + const char *fitfile, *trxfile, *ndxfile; + const char *eigvalfile, *eigvecfile, *averfile, *logfile; + const char *asciifile, *xpmfile, *xpmafile; + char str[STRLEN], *fitname, *ananame, *pcwd; + int d, dj, nfit; + atom_id *index, *ifit; + gmx_bool bDiffMass1, bDiffMass2; + time_t now; + char timebuf[STRLEN]; + t_rgb rlo, rmi, rhi; + real *eigenvectors; + output_env_t oenv; + gmx_rmpbc_t gpbc = NULL; + + t_filenm fnm[] = { + { efTRX, "-f", NULL, ffREAD }, + { efTPS, NULL, NULL, ffREAD }, + { efNDX, NULL, NULL, ffOPTRD }, + { efXVG, NULL, "eigenval", ffWRITE }, + { efTRN, "-v", "eigenvec", ffWRITE }, + { efSTO, "-av", "average.pdb", ffWRITE }, + { efLOG, NULL, "covar", ffWRITE }, + { efDAT, "-ascii", "covar", ffOPTWR }, + { efXPM, "-xpm", "covar", ffOPTWR }, + { efXPM, "-xpma", "covara", ffOPTWR } + }; +#define NFILE asize(fnm) + + if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE, + NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv)) + { + return 0; + } + + clear_mat(zerobox); + + fitfile = ftp2fn(efTPS, NFILE, fnm); + trxfile = ftp2fn(efTRX, NFILE, fnm); + ndxfile = ftp2fn_null(efNDX, NFILE, fnm); + eigvalfile = ftp2fn(efXVG, NFILE, fnm); + eigvecfile = ftp2fn(efTRN, NFILE, fnm); + averfile = ftp2fn(efSTO, NFILE, fnm); + logfile = ftp2fn(efLOG, NFILE, fnm); + asciifile = opt2fn_null("-ascii", NFILE, fnm); + xpmfile = opt2fn_null("-xpm", NFILE, fnm); + xpmafile = opt2fn_null("-xpma", NFILE, fnm); + + read_tps_conf(fitfile, str, &top, &ePBC, &xref, NULL, box, TRUE); + atoms = &top.atoms; + + if (bFit) + { + printf("\nChoose a group for the least squares fit\n"); + get_index(atoms, ndxfile, 1, &nfit, &ifit, &fitname); + if (nfit < 3) + { + gmx_fatal(FARGS, "Need >= 3 points to fit!\n"); + } + } + else + { + nfit = 0; + } + printf("\nChoose a group for the covariance analysis\n"); + get_index(atoms, ndxfile, 1, &natoms, &index, &ananame); + + bDiffMass1 = FALSE; + if (bFit) + { + snew(w_rls, atoms->nr); + for (i = 0; (i < nfit); i++) + { + w_rls[ifit[i]] = atoms->atom[ifit[i]].m; + if (i) + { + bDiffMass1 = bDiffMass1 || (w_rls[ifit[i]] != w_rls[ifit[i-1]]); + } + } + } + bDiffMass2 = FALSE; + snew(sqrtm, natoms); + for (i = 0; (i < natoms); i++) + { + if (bM) + { + sqrtm[i] = sqrt(atoms->atom[index[i]].m); + if (i) + { + bDiffMass2 = bDiffMass2 || (sqrtm[i] != sqrtm[i-1]); + } + } + else + { + sqrtm[i] = 1.0; + } + } + + if (bFit && bDiffMass1 && !bDiffMass2) + { + bDiffMass1 = natoms != nfit; + i = 0; + for (i = 0; (i < natoms) && !bDiffMass1; i++) + { + bDiffMass1 = index[i] != ifit[i]; + } + if (!bDiffMass1) + { + fprintf(stderr, "\n" + "Note: the fit and analysis group are identical,\n" + " while the fit is mass weighted and the analysis is not.\n" + " Making the fit non mass weighted.\n\n"); + for (i = 0; (i < nfit); i++) + { + w_rls[ifit[i]] = 1.0; + } + } + } + + /* Prepare reference frame */ + if (bPBC) + { + gpbc = gmx_rmpbc_init(&top.idef, ePBC, atoms->nr); + gmx_rmpbc(gpbc, atoms->nr, box, xref); + } + if (bFit) + { + reset_x(nfit, ifit, atoms->nr, NULL, xref, w_rls); + } + + snew(x, natoms); + snew(xav, natoms); + ndim = natoms*DIM; + if (sqrt(GMX_INT64_MAX) < ndim) + { + gmx_fatal(FARGS, "Number of degrees of freedoms to large for matrix.\n"); + } + snew(mat, ndim*ndim); + + fprintf(stderr, "Calculating the average structure ...\n"); + nframes0 = 0; + nat = read_first_x(oenv, &status, trxfile, &t, &xread, box); + if (nat != atoms->nr) + { + fprintf(stderr, "\nWARNING: number of atoms in tpx (%d) and trajectory (%d) do not match\n", natoms, nat); + } + do + { + nframes0++; + /* calculate x: a fitted struture of the selected atoms */ + if (bPBC) + { + gmx_rmpbc(gpbc, nat, box, xread); + } + if (bFit) + { + reset_x(nfit, ifit, nat, NULL, xread, w_rls); + do_fit(nat, w_rls, xref, xread); + } + for (i = 0; i < natoms; i++) + { + rvec_inc(xav[i], xread[index[i]]); + } + } + while (read_next_x(oenv, status, &t, xread, box)); + close_trj(status); + + inv_nframes = 1.0/nframes0; + for (i = 0; i < natoms; i++) + { + for (d = 0; d < DIM; d++) + { + xav[i][d] *= inv_nframes; + xread[index[i]][d] = xav[i][d]; + } + } + write_sto_conf_indexed(opt2fn("-av", NFILE, fnm), "Average structure", + atoms, xread, NULL, epbcNONE, zerobox, natoms, index); + sfree(xread); + + fprintf(stderr, "Constructing covariance matrix (%dx%d) ...\n", (int)ndim, (int)ndim); + nframes = 0; + nat = read_first_x(oenv, &status, trxfile, &t, &xread, box); + tstart = t; + do + { + nframes++; + tend = t; + /* calculate x: a (fitted) structure of the selected atoms */ + if (bPBC) + { + gmx_rmpbc(gpbc, nat, box, xread); + } + if (bFit) + { + reset_x(nfit, ifit, nat, NULL, xread, w_rls); + do_fit(nat, w_rls, xref, xread); + } + if (bRef) + { + for (i = 0; i < natoms; i++) + { + rvec_sub(xread[index[i]], xref[index[i]], x[i]); + } + } + else + { + for (i = 0; i < natoms; i++) + { + rvec_sub(xread[index[i]], xav[i], x[i]); + } + } + + for (j = 0; j < natoms; j++) + { + for (dj = 0; dj < DIM; dj++) + { + k = ndim*(DIM*j+dj); + xj = x[j][dj]; + for (i = j; i < natoms; i++) + { + l = k+DIM*i; + for (d = 0; d < DIM; d++) + { + mat[l+d] += x[i][d]*xj; + } + } + } + } + } + while (read_next_x(oenv, status, &t, xread, box) && + (bRef || nframes < nframes0)); + close_trj(status); + gmx_rmpbc_done(gpbc); + + fprintf(stderr, "Read %d frames\n", nframes); + + if (bRef) + { + /* copy the reference structure to the ouput array x */ + snew(xproj, natoms); + for (i = 0; i < natoms; i++) + { + copy_rvec(xref[index[i]], xproj[i]); + } + } + else + { + xproj = xav; + } + + /* correct the covariance matrix for the mass */ + inv_nframes = 1.0/nframes; + for (j = 0; j < natoms; j++) + { + for (dj = 0; dj < DIM; dj++) + { + for (i = j; i < natoms; i++) + { + k = ndim*(DIM*j+dj)+DIM*i; + for (d = 0; d < DIM; d++) + { + mat[k+d] = mat[k+d]*inv_nframes*sqrtm[i]*sqrtm[j]; + } + } + } + } + + /* symmetrize the matrix */ + for (j = 0; j < ndim; j++) + { + for (i = j; i < ndim; i++) + { + mat[ndim*i+j] = mat[ndim*j+i]; + } + } + + trace = 0; + for (i = 0; i < ndim; i++) + { + trace += mat[i*ndim+i]; + } + fprintf(stderr, "\nTrace of the covariance matrix: %g (%snm^2)\n", + trace, bM ? "u " : ""); + + if (asciifile) + { + out = gmx_ffopen(asciifile, "w"); + for (j = 0; j < ndim; j++) + { + for (i = 0; i < ndim; i += 3) + { + fprintf(out, "%g %g %g\n", + mat[ndim*j+i], mat[ndim*j+i+1], mat[ndim*j+i+2]); + } + } + gmx_ffclose(out); + } + + if (xpmfile) + { + min = 0; + max = 0; + snew(mat2, ndim); + for (j = 0; j < ndim; j++) + { + mat2[j] = &(mat[ndim*j]); + for (i = 0; i <= j; i++) + { + if (mat2[j][i] < min) + { + min = mat2[j][i]; + } + if (mat2[j][j] > max) + { + max = mat2[j][i]; + } + } + } + snew(axis, ndim); + for (i = 0; i < ndim; i++) + { + axis[i] = i+1; + } + rlo.r = 0; rlo.g = 0; rlo.b = 1; + rmi.r = 1; rmi.g = 1; rmi.b = 1; + rhi.r = 1; rhi.g = 0; rhi.b = 0; + out = gmx_ffopen(xpmfile, "w"); + nlevels = 80; + write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2", + "dim", "dim", ndim, ndim, axis, axis, + mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels); + gmx_ffclose(out); + sfree(axis); + sfree(mat2); + } + + if (xpmafile) + { + min = 0; + max = 0; + snew(mat2, ndim/DIM); + for (i = 0; i < ndim/DIM; i++) + { + snew(mat2[i], ndim/DIM); + } + for (j = 0; j < ndim/DIM; j++) + { + for (i = 0; i <= j; i++) + { + mat2[j][i] = 0; + for (d = 0; d < DIM; d++) + { + mat2[j][i] += mat[ndim*(DIM*j+d)+DIM*i+d]; + } + if (mat2[j][i] < min) + { + min = mat2[j][i]; + } + if (mat2[j][j] > max) + { + max = mat2[j][i]; + } + mat2[i][j] = mat2[j][i]; + } + } + snew(axis, ndim/DIM); + for (i = 0; i < ndim/DIM; i++) + { + axis[i] = i+1; + } + rlo.r = 0; rlo.g = 0; rlo.b = 1; + rmi.r = 1; rmi.g = 1; rmi.b = 1; + rhi.r = 1; rhi.g = 0; rhi.b = 0; + out = gmx_ffopen(xpmafile, "w"); + nlevels = 80; + write_xpm3(out, 0, "Covariance", bM ? "u nm^2" : "nm^2", + "atom", "atom", ndim/DIM, ndim/DIM, axis, axis, + mat2, min, 0.0, max, rlo, rmi, rhi, &nlevels); + gmx_ffclose(out); + sfree(axis); + for (i = 0; i < ndim/DIM; i++) + { + sfree(mat2[i]); + } + sfree(mat2); + } + + + /* call diagonalization routine */ + + snew(eigenvalues, ndim); + snew(eigenvectors, ndim*ndim); + + memcpy(eigenvectors, mat, ndim*ndim*sizeof(real)); + fprintf(stderr, "\nDiagonalizing ...\n"); + fflush(stderr); + eigensolver(eigenvectors, ndim, 0, ndim, eigenvalues, mat); + sfree(eigenvectors); + + /* now write the output */ + + sum = 0; + for (i = 0; i < ndim; i++) + { + sum += eigenvalues[i]; + } + fprintf(stderr, "\nSum of the eigenvalues: %g (%snm^2)\n", + sum, bM ? "u " : ""); + if (fabs(trace-sum) > 0.01*trace) + { + fprintf(stderr, "\nWARNING: eigenvalue sum deviates from the trace of the covariance matrix\n"); + } + + fprintf(stderr, "\nWriting eigenvalues to %s\n", eigvalfile); + + sprintf(str, "(%snm\\S2\\N)", bM ? "u " : ""); + out = xvgropen(eigvalfile, + "Eigenvalues of the covariance matrix", + "Eigenvector index", str, oenv); - for (i = 0; (i < ndim); i++) ++ for (i = 0; (i < end); i++) + { + fprintf (out, "%10d %g\n", (int)i+1, eigenvalues[ndim-1-i]); + } + gmx_ffclose(out); + + if (end == -1) + { + if (nframes-1 < ndim) + { + end = nframes-1; ++ fprintf(out, "WARNING: there are fewer frames in your trajectory than there are\n"); ++ fprintf(out, "degrees of freedom in your system. Only generating the first\n"); ++ fprintf(out, "%d out of %d eigenvectors and eigenvalues.\n", end, (int)ndim); + } + else + { + end = ndim; + } + } + if (bFit) + { + /* misuse lambda: 0/1 mass weighted analysis no/yes */ + if (nfit == natoms) + { + WriteXref = eWXR_YES; + for (i = 0; i < nfit; i++) + { + copy_rvec(xref[ifit[i]], x[i]); + } + } + else + { + WriteXref = eWXR_NO; + } + } + else + { + /* misuse lambda: -1 for no fit */ + WriteXref = eWXR_NOFIT; + } + + write_eigenvectors(eigvecfile, natoms, mat, TRUE, 1, end, + WriteXref, x, bDiffMass1, xproj, bM, eigenvalues); + + out = gmx_ffopen(logfile, "w"); + + time(&now); + gmx_ctime_r(&now, timebuf, STRLEN); + fprintf(out, "Covariance analysis log, written %s\n", timebuf); + + fprintf(out, "Program: %s\n", argv[0]); + gmx_getcwd(str, STRLEN); + + fprintf(out, "Working directory: %s\n\n", str); + + fprintf(out, "Read %d frames from %s (time %g to %g %s)\n", nframes, trxfile, + output_env_conv_time(oenv, tstart), output_env_conv_time(oenv, tend), output_env_get_time_unit(oenv)); + if (bFit) + { + fprintf(out, "Read reference structure for fit from %s\n", fitfile); + } + if (ndxfile) + { + fprintf(out, "Read index groups from %s\n", ndxfile); + } + fprintf(out, "\n"); + + fprintf(out, "Analysis group is '%s' (%d atoms)\n", ananame, natoms); + if (bFit) + { + fprintf(out, "Fit group is '%s' (%d atoms)\n", fitname, nfit); + } + else + { + fprintf(out, "No fit was used\n"); + } + fprintf(out, "Analysis is %smass weighted\n", bDiffMass2 ? "" : "non-"); + if (bFit) + { + fprintf(out, "Fit is %smass weighted\n", bDiffMass1 ? "" : "non-"); + } + fprintf(out, "Diagonalized the %dx%d covariance matrix\n", (int)ndim, (int)ndim); + fprintf(out, "Trace of the covariance matrix before diagonalizing: %g\n", + trace); + fprintf(out, "Trace of the covariance matrix after diagonalizing: %g\n\n", + sum); + - fprintf(out, "Wrote %d eigenvalues to %s\n", (int)ndim, eigvalfile); ++ fprintf(out, "Wrote %d eigenvalues to %s\n", (int)end, eigvalfile); + if (WriteXref == eWXR_YES) + { + fprintf(out, "Wrote reference structure to %s\n", eigvecfile); + } + fprintf(out, "Wrote average structure to %s and %s\n", averfile, eigvecfile); + fprintf(out, "Wrote eigenvectors %d to %d to %s\n", 1, end, eigvecfile); + + gmx_ffclose(out); + + fprintf(stderr, "Wrote the log to %s\n", logfile); + + return 0; +} diff --cc src/gromacs/gmxana/gmx_lie.c index 3e29541e9e,0000000000..7a9a689cc8 mode 100644,000000..100644 --- a/src/gromacs/gmxana/gmx_lie.c +++ b/src/gromacs/gmxana/gmx_lie.c @@@ -1,218 -1,0 +1,224 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "gromacs/commandline/pargs.h" +#include "sysstuff.h" +#include "typedefs.h" +#include "gromacs/utility/smalloc.h" +#include "macros.h" +#include "vec.h" +#include "gromacs/fileio/futil.h" +#include "txtdump.h" +#include "gromacs/fileio/enxio.h" +#include "gstat.h" +#include "xvgr.h" +#include "gmx_ana.h" +#include "gromacs/fileio/trxio.h" + +typedef struct { + int nlj, nqq; + int *lj; + int *qq; +} t_liedata; + +static t_liedata *analyze_names(int nre, gmx_enxnm_t *names, const char *ligand) +{ + int i; + t_liedata *ld; + char self[256]; + + /* Skip until we come to pressure */ + for (i = 0; (i < F_NRE); i++) + { + if (strcmp(names[i].name, interaction_function[F_PRES].longname) == 0) + { + break; + } + } + + /* Now real analysis: find components of energies */ + sprintf(self, "%s-%s", ligand, ligand); + snew(ld, 1); + for (; (i < nre); i++) + { + if ((strstr(names[i].name, ligand) != NULL) && + (strstr(names[i].name, self) == NULL)) + { + if (strstr(names[i].name, "LJ") != NULL) + { + ld->nlj++; + srenew(ld->lj, ld->nlj); + ld->lj[ld->nlj-1] = i; + } + else if (strstr(names[i].name, "Coul") != NULL) + { + ld->nqq++; + srenew(ld->qq, ld->nqq); + ld->qq[ld->nqq-1] = i; + } + } + } + printf("Using the following energy terms:\n"); + printf("LJ: "); + for (i = 0; (i < ld->nlj); i++) + { + printf(" %12s", names[ld->lj[i]].name); + } + printf("\nCoul:"); + for (i = 0; (i < ld->nqq); i++) + { + printf(" %12s", names[ld->qq[i]].name); + } + printf("\n"); + + return ld; +} + +real calc_lie(t_liedata *ld, t_energy ee[], real lie_lj, real lie_qq, + real fac_lj, real fac_qq) +{ + int i; + real lj_tot, qq_tot; + + lj_tot = 0; + for (i = 0; (i < ld->nlj); i++) + { + lj_tot += ee[ld->lj[i]].e; + } + qq_tot = 0; + for (i = 0; (i < ld->nqq); i++) + { + qq_tot += ee[ld->qq[i]].e; + } + + /* And now the great LIE formula: */ + return fac_lj*(lj_tot-lie_lj)+fac_qq*(qq_tot-lie_qq); +} + +int gmx_lie(int argc, char *argv[]) +{ + const char *desc[] = { + "[THISMODULE] computes a free energy estimate based on an energy analysis", - "from. One needs an energy file with the following components:", - "Coul (A-B) LJ-SR (A-B) etc." ++ "from nonbonded energies. One needs an energy file with the following components:", ++ "Coul-(A-B) LJ-SR (A-B) etc.[PAR]", ++ "To utilize [TT]g_lie[tt] correctly, two simulations are required: one with the", ++ "molecule of interest bound to its receptor and one with the molecule in water.", ++ "Both need to utilize [TT]energygrps[tt] such that Coul-SR(A-B), LJ-SR(A-B), etc. terms", ++ "are written to the [TT].edr[tt] file. Values from the molecule-in-water simulation", ++ "are necessary for supplying suitable values for -Elj and -Eqq." + }; + static real lie_lj = 0, lie_qq = 0, fac_lj = 0.181, fac_qq = 0.5; + static const char *ligand = "none"; + t_pargs pa[] = { + { "-Elj", FALSE, etREAL, {&lie_lj}, + "Lennard-Jones interaction between ligand and solvent" }, + { "-Eqq", FALSE, etREAL, {&lie_qq}, + "Coulomb interaction between ligand and solvent" }, + { "-Clj", FALSE, etREAL, {&fac_lj}, + "Factor in the LIE equation for Lennard-Jones component of energy" }, + { "-Cqq", FALSE, etREAL, {&fac_qq}, + "Factor in the LIE equation for Coulomb component of energy" }, + { "-ligand", FALSE, etSTR, {&ligand}, + "Name of the ligand in the energy file" } + }; +#define NPA asize(pa) + + FILE *out; + int nre, nframes = 0, ct = 0; + ener_file_t fp; + gmx_bool bCont; + t_liedata *ld; + gmx_enxnm_t *enm = NULL; + t_enxframe *fr; + real lie; + double lieaver = 0, lieav2 = 0; + output_env_t oenv; + + t_filenm fnm[] = { + { efEDR, "-f", "ener", ffREAD }, + { efXVG, "-o", "lie", ffWRITE } + }; +#define NFILE asize(fnm) + + if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE, + NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv)) + { + return 0; + } + + fp = open_enx(ftp2fn(efEDR, NFILE, fnm), "r"); + do_enxnms(fp, &nre, &enm); + + ld = analyze_names(nre, enm, ligand); + snew(fr, 1); ++ + out = xvgropen(ftp2fn(efXVG, NFILE, fnm), "LIE free energy estimate", + "Time (ps)", "DGbind (kJ/mol)", oenv); + while (do_enx(fp, fr)) + { + ct = check_times(fr->t); + if (ct == 0) + { + lie = calc_lie(ld, fr->ener, lie_lj, lie_qq, fac_lj, fac_qq); + lieaver += lie; + lieav2 += lie*lie; + nframes++; + fprintf(out, "%10g %10g\n", fr->t, lie); + } + } + close_enx(fp); + gmx_ffclose(out); + fprintf(stderr, "\n"); + + if (nframes > 0) + { + printf("DGbind = %.3f (%.3f)\n", lieaver/nframes, + sqrt(lieav2/nframes-sqr(lieaver/nframes))); + } + + do_view(oenv, ftp2fn(efXVG, NFILE, fnm), "-nxy"); + + return 0; +} diff --cc src/gromacs/gmxpreprocess/addconf.c index e2d7a17e61,0000000000..27a3b5b752 mode 100644,000000..100644 --- a/src/gromacs/gmxpreprocess/addconf.c +++ b/src/gromacs/gmxpreprocess/addconf.c @@@ -1,667 -1,0 +1,672 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#include "addconf.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "vec.h" +#include "macros.h" +#include "gromacs/utility/smalloc.h" +#include "types/commrec.h" +#include "force.h" +#include "names.h" +#include "nsgrid.h" +#include "mdatoms.h" +#include "nrnb.h" +#include "ns.h" +#include "mtop_util.h" +#include "chargegroup.h" + +static real box_margin; + +static real max_dist(rvec *x, real *r, int start, int end) +{ + real maxd; + int i, j; + + maxd = 0; + for (i = start; i < end; i++) + { + for (j = i+1; j < end; j++) + { + maxd = max(maxd, sqrt(distance2(x[i], x[j]))+0.5*(r[i]+r[j])); + } + } + + return 0.5*maxd; +} + +static gmx_bool outside_box_minus_margin2(rvec x, matrix box) +{ + return ( (x[XX] < 2*box_margin) || (x[XX] > box[XX][XX]-2*box_margin) || + (x[YY] < 2*box_margin) || (x[YY] > box[YY][YY]-2*box_margin) || + (x[ZZ] < 2*box_margin) || (x[ZZ] > box[ZZ][ZZ]-2*box_margin) ); +} + +static gmx_bool outside_box_plus_margin(rvec x, matrix box) +{ + return ( (x[XX] < -box_margin) || (x[XX] > box[XX][XX]+box_margin) || + (x[YY] < -box_margin) || (x[YY] > box[YY][YY]+box_margin) || + (x[ZZ] < -box_margin) || (x[ZZ] > box[ZZ][ZZ]+box_margin) ); +} + +static int mark_res(int at, gmx_bool *mark, int natoms, t_atom *atom, int *nmark) +{ + int resind; + + resind = atom[at].resind; + while ( (at > 0) && (resind == atom[at-1].resind) ) + { + at--; + } + while ( (at < natoms) && (resind == atom[at].resind) ) + { + if (!mark[at]) + { + mark[at] = TRUE; + (*nmark)++; + } + at++; + } + + return at; +} + +static real find_max_real(int n, real radius[]) +{ + int i; + real rmax; + + rmax = 0; + if (n > 0) + { + rmax = radius[0]; + for (i = 1; (i < n); i++) + { + rmax = max(rmax, radius[i]); + } + } + return rmax; +} + +static void combine_atoms(t_atoms *ap, t_atoms *as, + rvec xp[], rvec *vp, rvec xs[], rvec *vs, + t_atoms **a_comb, rvec **x_comb, rvec **v_comb) +{ + t_atoms *ac; + rvec *xc, *vc = NULL; + int i, j, natot, res0; + + /* Total number of atoms */ + natot = ap->nr+as->nr; + + snew(ac, 1); + init_t_atoms(ac, natot, FALSE); + + snew(xc, natot); + if (vp && vs) + { + snew(vc, natot); + } + + /* Fill the new structures */ + for (i = j = 0; (i < ap->nr); i++, j++) + { + copy_rvec(xp[i], xc[j]); + if (vc) + { + copy_rvec(vp[i], vc[j]); + } + memcpy(&(ac->atom[j]), &(ap->atom[i]), sizeof(ap->atom[i])); + ac->atom[j].type = 0; + } + res0 = ap->nres; + for (i = 0; (i < as->nr); i++, j++) + { + copy_rvec(xs[i], xc[j]); + if (vc) + { + copy_rvec(vs[i], vc[j]); + } + memcpy(&(ac->atom[j]), &(as->atom[i]), sizeof(as->atom[i])); + ac->atom[j].type = 0; + ac->atom[j].resind += res0; + } + ac->nr = j; + ac->nres = ac->atom[j-1].resind+1; + /* Fill all elements to prevent uninitialized memory */ + for (i = 0; i < ac->nr; i++) + { + ac->atom[i].m = 1; + ac->atom[i].q = 0; + ac->atom[i].mB = 1; + ac->atom[i].qB = 0; + ac->atom[i].type = 0; + ac->atom[i].typeB = 0; + ac->atom[i].ptype = eptAtom; + } + + /* Return values */ + *a_comb = ac; + *x_comb = xc; + *v_comb = vc; +} + +static t_forcerec *fr = NULL; + +static void do_nsgrid(FILE *fp, gmx_bool bVerbose, + matrix box, rvec x[], t_atoms *atoms, real rlong, + const output_env_t oenv) +{ + gmx_mtop_t *mtop; + gmx_localtop_t *top; + t_mdatoms *md; + t_block *cgs; + t_inputrec *ir; + t_nrnb nrnb; + t_commrec *cr; + int *cg_index; + gmx_moltype_t *molt; + gmx_ffparams_t *ffp; + ivec *nFreeze; + int i, m, natoms; + rvec box_size; + real *lambda, *dvdl; + + natoms = atoms->nr; + + /* Charge group index */ + snew(cg_index, natoms); + for (i = 0; (i < natoms); i++) + { + cg_index[i] = i; + } + + /* Topology needs charge groups and exclusions */ + snew(mtop, 1); + init_mtop(mtop); + mtop->natoms = natoms; + /* Make one moltype that contains the whol system */ + mtop->nmoltype = 1; + snew(mtop->moltype, mtop->nmoltype); + molt = &mtop->moltype[0]; + molt->name = mtop->name; + molt->atoms = *atoms; + stupid_fill_block(&molt->cgs, mtop->natoms, FALSE); + stupid_fill_blocka(&molt->excls, natoms); + /* Make one molblock for the whole system */ + mtop->nmolblock = 1; + snew(mtop->molblock, mtop->nmolblock); + mtop->molblock[0].type = 0; + mtop->molblock[0].nmol = 1; + mtop->molblock[0].natoms_mol = natoms; + /* Initialize a single energy group */ + mtop->groups.grps[egcENER].nr = 1; + mtop->groups.ngrpnr[egcENER] = 0; + mtop->groups.grpnr[egcENER] = NULL; + + ffp = &mtop->ffparams; + + ffp->ntypes = 1; + ffp->atnr = 1; + ffp->reppow = 12; + snew(ffp->functype, 1); + snew(ffp->iparams, 1); + ffp->iparams[0].lj.c6 = 1; + ffp->iparams[0].lj.c12 = 1; + + /* inputrec structure */ + snew(ir, 1); + ir->cutoff_scheme = ecutsGROUP; + ir->coulomb_modifier = eintmodNONE; + ir->vdw_modifier = eintmodNONE; + ir->coulombtype = eelCUT; + ir->vdwtype = evdwCUT; + ir->ndelta = 2; + ir->ns_type = ensGRID; + snew(ir->opts.egp_flags, 1); + + top = gmx_mtop_generate_local_top(mtop, ir); + + /* Some nasty shortcuts */ + cgs = &(top->cgs); + + /* mdatoms structure */ + snew(nFreeze, 2); + snew(md, 1); + md = init_mdatoms(fp, mtop, FALSE); + atoms2md(mtop, ir, 0, NULL, mtop->natoms, md); + sfree(nFreeze); + + /* forcerec structure */ + if (fr == NULL) + { + fr = mk_forcerec(); + } + snew(cr, 1); + cr->nnodes = 1; + /* cr->nthreads = 1; */ + + /* ir->rlist = ir->rcoulomb = ir->rvdw = rlong; + printf("Neighborsearching with a cut-off of %g\n",rlong); + init_forcerec(stdout,fr,ir,top,cr,md,box,FALSE,NULL,NULL,NULL,TRUE);*/ + fr->cg0 = 0; + fr->hcg = top->cgs.nr; + fr->nWatMol = 0; + + /* Prepare for neighboursearching */ + init_nrnb(&nrnb); + + /* Init things dependent on parameters */ + ir->rlistlong = ir->rlist = ir->rcoulomb = ir->rvdw = rlong; + /* create free energy data to avoid NULLs */ + snew(ir->fepvals, 1); + printf("Neighborsearching with a cut-off of %g\n", rlong); + init_forcerec(stdout, oenv, fr, NULL, ir, mtop, cr, box, + NULL, NULL, NULL, NULL, NULL, TRUE, -1); + if (debug) + { + pr_forcerec(debug, fr); + } + + /* Calculate new stuff dependent on coords and box */ + for (m = 0; (m < DIM); m++) + { + box_size[m] = box[m][m]; + } + calc_shifts(box, fr->shift_vec); + put_charge_groups_in_box(fp, 0, cgs->nr, fr->ePBC, box, cgs, x, fr->cg_cm); + + /* Do the actual neighboursearching */ + snew(lambda, efptNR); + snew(dvdl, efptNR); + init_neighbor_list(fp, fr, md->homenr); + search_neighbours(fp, fr, box, top, + &mtop->groups, cr, &nrnb, md, TRUE, FALSE); + + if (debug) + { + dump_nblist(debug, cr, fr, 0); + } + + if (bVerbose) + { + fprintf(stderr, "Successfully made neighbourlist\n"); + } +} + +static gmx_bool bXor(gmx_bool b1, gmx_bool b2) +{ + return (b1 && !b2) || (b2 && !b1); +} + +void add_conf(t_atoms *atoms, rvec **x, rvec **v, real **r, gmx_bool bSrenew, + int ePBC, matrix box, gmx_bool bInsert, + t_atoms *atoms_solvt, rvec *x_solvt, rvec *v_solvt, real *r_solvt, + gmx_bool bVerbose, real rshell, int max_sol, const output_env_t oenv) +{ + t_nblist *nlist; + t_atoms *atoms_all; + real max_vdw, *r_prot, *r_all, n2, r2, ib1, ib2; + int natoms_prot, natoms_solvt; + int i, j, jj, m, j0, j1, jjj, jnres, jnr, inr, iprot, is1, is2; + int prev, resnr, nresadd, d, k, ncells, maxincell; + int dx0, dx1, dy0, dy1, dz0, dz1; + int ntest, nremove, nkeep; + rvec dx, xi, xj, xpp, *x_all, *v_all; + gmx_bool *remove, *keep; + int bSolSol; + + natoms_prot = atoms->nr; + natoms_solvt = atoms_solvt->nr; + if (natoms_solvt <= 0) + { + fprintf(stderr, "WARNING: Nothing to add\n"); + return; + } + + if (ePBC == epbcSCREW) + { + gmx_fatal(FARGS, "Sorry, %s pbc is not yet supported", epbc_names[ePBC]); + } + + if (bVerbose) + { + fprintf(stderr, "Calculating Overlap...\n"); + } + + /* Set margin around box edges to largest solvent dimension. + * The maximum distance between atoms in a solvent molecule should + * be calculated. At the moment a fudge factor of 3 is used. + */ + r_prot = *r; + box_margin = 3*find_max_real(natoms_solvt, r_solvt); + max_vdw = max(3*find_max_real(natoms_prot, r_prot), box_margin); + fprintf(stderr, "box_margin = %g\n", box_margin); + + snew(remove, natoms_solvt); + + nremove = 0; + if (!bInsert) + { + for (i = 0; i < atoms_solvt->nr; i++) + { + if (outside_box_plus_margin(x_solvt[i], box) ) + { + i = mark_res(i, remove, atoms_solvt->nr, atoms_solvt->atom, &nremove); + } + } + fprintf(stderr, "Removed %d atoms that were outside the box\n", nremove); + } + + /* Define grid stuff */ + /* Largest VDW radius */ + snew(r_all, natoms_prot+natoms_solvt); + for (i = j = 0; i < natoms_prot; i++, j++) + { + r_all[j] = r_prot[i]; + } + for (i = 0; i < natoms_solvt; i++, j++) + { + r_all[j] = r_solvt[i]; + } + + /* Combine arrays */ + combine_atoms(atoms, atoms_solvt, *x, v ? *v : NULL, x_solvt, v_solvt, + &atoms_all, &x_all, &v_all); + + /* Do neighboursearching step */ + do_nsgrid(stdout, bVerbose, box, x_all, atoms_all, max_vdw, oenv); + + /* check solvent with solute */ + nlist = &(fr->nblists[0].nlist_sr[eNL_VDW]); + fprintf(stderr, "nri = %d, nrj = %d\n", nlist->nri, nlist->nrj); + for (bSolSol = 0; (bSolSol <= (bInsert ? 0 : 1)); bSolSol++) + { + ntest = nremove = 0; + fprintf(stderr, "Checking %s-Solvent overlap:", + bSolSol ? "Solvent" : "Protein"); + for (i = 0; (i < nlist->nri && nremove < natoms_solvt); i++) + { + inr = nlist->iinr[i]; + j0 = nlist->jindex[i]; + j1 = nlist->jindex[i+1]; + rvec_add(x_all[inr], fr->shift_vec[nlist->shift[i]], xi); + + for (j = j0; (j < j1 && nremove < natoms_solvt); j++) + { + jnr = nlist->jjnr[j]; ++ if (jnr < 0) ++ { ++ /* skip padding */ ++ continue; ++ } + copy_rvec(x_all[jnr], xj); + + /* Check solvent-protein and solvent-solvent */ + is1 = inr-natoms_prot; + is2 = jnr-natoms_prot; + + /* Check if at least one of the atoms is a solvent that is not yet + * listed for removal, and if both are solvent, that they are not in the + * same residue. + */ + if ((!bSolSol && + bXor((is1 >= 0), (is2 >= 0)) && /* One atom is protein */ + ((is1 < 0) || ((is1 >= 0) && !remove[is1])) && + ((is2 < 0) || ((is2 >= 0) && !remove[is2]))) || + + (bSolSol && + (is1 >= 0) && (!remove[is1]) && /* is1 is solvent */ + (is2 >= 0) && (!remove[is2]) && /* is2 is solvent */ + (bInsert || /* when inserting also check inside the box */ + (outside_box_minus_margin2(x_solvt[is1], box) && /* is1 on edge */ + outside_box_minus_margin2(x_solvt[is2], box)) /* is2 on edge */ + ) && + (atoms_solvt->atom[is1].resind != /* Not the same residue */ + atoms_solvt->atom[is2].resind))) + { + + ntest++; + rvec_sub(xi, xj, dx); + n2 = norm2(dx); + r2 = sqr(r_all[inr]+r_all[jnr]); + if (n2 < r2) + { + if (bInsert) + { + nremove = natoms_solvt; + for (k = 0; k < nremove; k++) + { + remove[k] = TRUE; + } + } + /* Need only remove one of the solvents... */ + if (is2 >= 0) + { + (void) mark_res(is2, remove, natoms_solvt, atoms_solvt->atom, + &nremove); + } + else if (is1 >= 0) + { + (void) mark_res(is1, remove, natoms_solvt, atoms_solvt->atom, + &nremove); + } + else + { + fprintf(stderr, "Neither atom is solvent%d %d\n", is1, is2); + } + } + } + } + } + if (!bInsert) + { + fprintf(stderr, " tested %d pairs, removed %d atoms.\n", ntest, nremove); + } + } + if (debug) + { + for (i = 0; i < natoms_solvt; i++) + { + fprintf(debug, "remove[%5d] = %s\n", i, bool_names[remove[i]]); + } + } + + /* Search again, now with another cut-off */ + if (rshell > 0) + { + do_nsgrid(stdout, bVerbose, box, x_all, atoms_all, rshell, oenv); + nlist = &(fr->nblists[0].nlist_sr[eNL_VDW]); + fprintf(stderr, "nri = %d, nrj = %d\n", nlist->nri, nlist->nrj); + nkeep = 0; + snew(keep, natoms_solvt); + for (i = 0; i < nlist->nri; i++) + { + inr = nlist->iinr[i]; + j0 = nlist->jindex[i]; + j1 = nlist->jindex[i+1]; + + for (j = j0; j < j1; j++) + { + jnr = nlist->jjnr[j]; + + /* Check solvent-protein and solvent-solvent */ + is1 = inr-natoms_prot; + is2 = jnr-natoms_prot; + + /* Check if at least one of the atoms is a solvent that is not yet + * listed for removal, and if both are solvent, that they are not in the + * same residue. + */ + if (is1 >= 0 && is2 < 0) + { + mark_res(is1, keep, natoms_solvt, atoms_solvt->atom, &nkeep); + } + else if (is1 < 0 && is2 >= 0) + { + mark_res(is2, keep, natoms_solvt, atoms_solvt->atom, &nkeep); + } + } + } + fprintf(stderr, "Keeping %d solvent atoms after proximity check\n", + nkeep); + for (i = 0; i < natoms_solvt; i++) + { + remove[i] = remove[i] || !keep[i]; + } + sfree(keep); + } + /* count how many atoms and residues will be added and make space */ + if (bInsert) + { + j = atoms_solvt->nr; + jnres = atoms_solvt->nres; + } + else + { + j = 0; + jnres = 0; + for (i = 0; ((i < atoms_solvt->nr) && + ((max_sol == 0) || (jnres < max_sol))); i++) + { + if (!remove[i]) + { + j++; + if ((i == 0) || + (atoms_solvt->atom[i].resind != atoms_solvt->atom[i-1].resind)) + { + jnres++; + } + } + } + } + if (debug) + { + fprintf(debug, "Will add %d atoms in %d residues\n", j, jnres); + } + if (!bInsert) + { + /* Flag the remaing solvent atoms to be removed */ + jjj = atoms_solvt->atom[i-1].resind; + for (; (i < atoms_solvt->nr); i++) + { + if (atoms_solvt->atom[i].resind > jjj) + { + remove[i] = TRUE; + } + else + { + j++; + } + } + } + + if (bSrenew) + { + srenew(atoms->resinfo, atoms->nres+jnres); + srenew(atoms->atomname, atoms->nr+j); + srenew(atoms->atom, atoms->nr+j); + srenew(*x, atoms->nr+j); + if (v) + { + srenew(*v, atoms->nr+j); + } + srenew(*r, atoms->nr+j); + } + + /* add the selected atoms_solvt to atoms */ + if (atoms->nr > 0) + { + resnr = atoms->resinfo[atoms->atom[atoms->nr-1].resind].nr; + } + else + { + resnr = 0; + } + prev = -1; + nresadd = 0; + for (i = 0; i < atoms_solvt->nr; i++) + { + if (!remove[i]) + { + if (prev == -1 || + atoms_solvt->atom[i].resind != atoms_solvt->atom[prev].resind) + { + nresadd++; + atoms->nres++; + resnr++; + atoms->resinfo[atoms->nres-1] = + atoms_solvt->resinfo[atoms_solvt->atom[i].resind]; + atoms->resinfo[atoms->nres-1].nr = resnr; + /* calculate shift of the solvent molecule using the first atom */ + copy_rvec(x_solvt[i], dx); + put_atoms_in_box(ePBC, box, 1, &dx); + rvec_dec(dx, x_solvt[i]); + } + atoms->atom[atoms->nr] = atoms_solvt->atom[i]; + atoms->atomname[atoms->nr] = atoms_solvt->atomname[i]; + rvec_add(x_solvt[i], dx, (*x)[atoms->nr]); + if (v) + { + copy_rvec(v_solvt[i], (*v)[atoms->nr]); + } + (*r)[atoms->nr] = r_solvt[i]; + atoms->atom[atoms->nr].resind = atoms->nres-1; + atoms->nr++; + prev = i; + } + } + if (bSrenew) + { + srenew(atoms->resinfo, atoms->nres+nresadd); + } + + if (bVerbose) + { + fprintf(stderr, "Added %d molecules\n", nresadd); + } + + sfree(remove); + done_atom(atoms_all); + sfree(x_all); + sfree(v_all); +} diff --cc src/gromacs/gmxpreprocess/grompp.c index 872c670990,0000000000..b562b5188f mode 100644,000000..100644 --- a/src/gromacs/gmxpreprocess/grompp.c +++ b/src/gromacs/gmxpreprocess/grompp.c @@@ -1,2054 -1,0 +1,2059 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#include "grompp.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "sysstuff.h" +#include "gromacs/utility/smalloc.h" +#include "macros.h" +#include "readir.h" +#include "toputil.h" +#include "topio.h" +#include "gromacs/fileio/confio.h" +#include "readir.h" +#include "symtab.h" +#include "names.h" +#include "grompp-impl.h" +#include "gromacs/random/random.h" +#include "gromacs/gmxpreprocess/gen_maxwell_velocities.h" +#include "vec.h" +#include "gromacs/fileio/futil.h" +#include "gromacs/commandline/pargs.h" +#include "splitter.h" +#include "gromacs/gmxpreprocess/sortwater.h" +#include "convparm.h" +#include "gmx_fatal.h" +#include "warninp.h" +#include "index.h" +#include "gromacs/fileio/gmxfio.h" +#include "gromacs/fileio/trnio.h" +#include "gromacs/fileio/tpxio.h" +#include "gromacs/fileio/trxio.h" +#include "vsite_parm.h" +#include "txtdump.h" +#include "calcgrid.h" +#include "add_par.h" +#include "gromacs/fileio/enxio.h" +#include "perf_est.h" +#include "compute_io.h" +#include "gpp_atomtype.h" +#include "mtop_util.h" +#include "genborn.h" +#include "calc_verletbuf.h" +#include "tomorse.h" +#include "gromacs/imd/imd.h" + + +static int rm_interactions(int ifunc, int nrmols, t_molinfo mols[]) +{ + int i, n; + + n = 0; + /* For all the molecule types */ + for (i = 0; i < nrmols; i++) + { + n += mols[i].plist[ifunc].nr; + mols[i].plist[ifunc].nr = 0; + } + return n; +} + +static int check_atom_names(const char *fn1, const char *fn2, + gmx_mtop_t *mtop, t_atoms *at) +{ + int mb, m, i, j, nmismatch; + t_atoms *tat; +#define MAXMISMATCH 20 + + if (mtop->natoms != at->nr) + { + gmx_incons("comparing atom names"); + } + + nmismatch = 0; + i = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + tat = &mtop->moltype[mtop->molblock[mb].type].atoms; + for (m = 0; m < mtop->molblock[mb].nmol; m++) + { + for (j = 0; j < tat->nr; j++) + { + if (strcmp( *(tat->atomname[j]), *(at->atomname[i]) ) != 0) + { + if (nmismatch < MAXMISMATCH) + { + fprintf(stderr, + "Warning: atom name %d in %s and %s does not match (%s - %s)\n", + i+1, fn1, fn2, *(tat->atomname[j]), *(at->atomname[i])); + } + else if (nmismatch == MAXMISMATCH) + { + fprintf(stderr, "(more than %d non-matching atom names)\n", MAXMISMATCH); + } + nmismatch++; + } + i++; + } + } + } + + return nmismatch; +} + +static void check_eg_vs_cg(gmx_mtop_t *mtop) +{ + int astart, mb, m, cg, j, firstj; + unsigned char firsteg, eg; + gmx_moltype_t *molt; + + /* Go through all the charge groups and make sure all their + * atoms are in the same energy group. + */ + + astart = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + molt = &mtop->moltype[mtop->molblock[mb].type]; + for (m = 0; m < mtop->molblock[mb].nmol; m++) + { + for (cg = 0; cg < molt->cgs.nr; cg++) + { + /* Get the energy group of the first atom in this charge group */ + firstj = astart + molt->cgs.index[cg]; + firsteg = ggrpnr(&mtop->groups, egcENER, firstj); + for (j = molt->cgs.index[cg]+1; j < molt->cgs.index[cg+1]; j++) + { + eg = ggrpnr(&mtop->groups, egcENER, astart+j); + if (eg != firsteg) + { + gmx_fatal(FARGS, "atoms %d and %d in charge group %d of molecule type '%s' are in different energy groups", + firstj+1, astart+j+1, cg+1, *molt->name); + } + } + } + astart += molt->atoms.nr; + } + } +} + +static void check_cg_sizes(const char *topfn, t_block *cgs, warninp_t wi) +{ + int maxsize, cg; + char warn_buf[STRLEN]; + + maxsize = 0; + for (cg = 0; cg < cgs->nr; cg++) + { + maxsize = max(maxsize, cgs->index[cg+1]-cgs->index[cg]); + } + + if (maxsize > MAX_CHARGEGROUP_SIZE) + { + gmx_fatal(FARGS, "The largest charge group contains %d atoms. The maximum is %d.", maxsize, MAX_CHARGEGROUP_SIZE); + } + else if (maxsize > 10) + { + set_warning_line(wi, topfn, -1); + sprintf(warn_buf, + "The largest charge group contains %d atoms.\n" + "Since atoms only see each other when the centers of geometry of the charge groups they belong to are within the cut-off distance, too large charge groups can lead to serious cut-off artifacts.\n" + "For efficiency and accuracy, charge group should consist of a few atoms.\n" + "For all-atom force fields use: CH3, CH2, CH, NH2, NH, OH, CO2, CO, etc.", + maxsize); + warning_note(wi, warn_buf); + } +} + +static void check_bonds_timestep(gmx_mtop_t *mtop, double dt, warninp_t wi) +{ + /* This check is not intended to ensure accurate integration, + * rather it is to signal mistakes in the mdp settings. + * A common mistake is to forget to turn on constraints + * for MD after energy minimization with flexible bonds. + * This check can also detect too large time steps for flexible water + * models, but such errors will often be masked by the constraints + * mdp options, which turns flexible water into water with bond constraints, + * but without an angle constraint. Unfortunately such incorrect use + * of water models can not easily be detected without checking + * for specific model names. + * + * The stability limit of leap-frog or velocity verlet is 4.44 steps + * per oscillational period. + * But accurate bonds distributions are lost far before that limit. + * To allow relatively common schemes (although not common with Gromacs) + * of dt=1 fs without constraints and dt=2 fs with only H-bond constraints + * we set the note limit to 10. + */ + int min_steps_warn = 5; + int min_steps_note = 10; + t_iparams *ip; + int molt; + gmx_moltype_t *moltype, *w_moltype; + t_atom *atom; + t_ilist *ilist, *ilb, *ilc, *ils; + int ftype; + int i, a1, a2, w_a1, w_a2, j; + real twopi2, limit2, fc, re, m1, m2, period2, w_period2; + gmx_bool bFound, bWater, bWarn; + char warn_buf[STRLEN]; + + ip = mtop->ffparams.iparams; + + twopi2 = sqr(2*M_PI); + + limit2 = sqr(min_steps_note*dt); + + w_a1 = w_a2 = -1; + w_period2 = -1.0; + + w_moltype = NULL; + for (molt = 0; molt < mtop->nmoltype; molt++) + { + moltype = &mtop->moltype[molt]; + atom = moltype->atoms.atom; + ilist = moltype->ilist; + ilc = &ilist[F_CONSTR]; + ils = &ilist[F_SETTLE]; + for (ftype = 0; ftype < F_NRE; ftype++) + { + if (!(ftype == F_BONDS || ftype == F_G96BONDS || ftype == F_HARMONIC)) + { + continue; + } + + ilb = &ilist[ftype]; + for (i = 0; i < ilb->nr; i += 3) + { + fc = ip[ilb->iatoms[i]].harmonic.krA; + re = ip[ilb->iatoms[i]].harmonic.rA; + if (ftype == F_G96BONDS) + { + /* Convert squared sqaure fc to harmonic fc */ + fc = 2*fc*re; + } + a1 = ilb->iatoms[i+1]; + a2 = ilb->iatoms[i+2]; + m1 = atom[a1].m; + m2 = atom[a2].m; + if (fc > 0 && m1 > 0 && m2 > 0) + { + period2 = twopi2*m1*m2/((m1 + m2)*fc); + } + else + { + period2 = GMX_FLOAT_MAX; + } + if (debug) + { + fprintf(debug, "fc %g m1 %g m2 %g period %g\n", + fc, m1, m2, sqrt(period2)); + } + if (period2 < limit2) + { + bFound = FALSE; + for (j = 0; j < ilc->nr; j += 3) + { + if ((ilc->iatoms[j+1] == a1 && ilc->iatoms[j+2] == a2) || + (ilc->iatoms[j+1] == a2 && ilc->iatoms[j+2] == a1)) + { + bFound = TRUE; + } + } + for (j = 0; j < ils->nr; j += 4) + { + if ((a1 == ils->iatoms[j+1] || a1 == ils->iatoms[j+2] || a1 == ils->iatoms[j+3]) && + (a2 == ils->iatoms[j+1] || a2 == ils->iatoms[j+2] || a2 == ils->iatoms[j+3])) + { + bFound = TRUE; + } + } + if (!bFound && + (w_moltype == NULL || period2 < w_period2)) + { + w_moltype = moltype; + w_a1 = a1; + w_a2 = a2; + w_period2 = period2; + } + } + } + } + } + + if (w_moltype != NULL) + { + bWarn = (w_period2 < sqr(min_steps_warn*dt)); + /* A check that would recognize most water models */ + bWater = ((*w_moltype->atoms.atomname[0])[0] == 'O' && + w_moltype->atoms.nr <= 5); + sprintf(warn_buf, "The bond in molecule-type %s between atoms %d %s and %d %s has an estimated oscillational period of %.1e ps, which is less than %d times the time step of %.1e ps.\n" + "%s", + *w_moltype->name, + w_a1+1, *w_moltype->atoms.atomname[w_a1], + w_a2+1, *w_moltype->atoms.atomname[w_a2], + sqrt(w_period2), bWarn ? min_steps_warn : min_steps_note, dt, + bWater ? + "Maybe you asked for fexible water." : + "Maybe you forgot to change the constraints mdp option."); + if (bWarn) + { + warning(wi, warn_buf); + } + else + { + warning_note(wi, warn_buf); + } + } +} + +static void check_vel(gmx_mtop_t *mtop, rvec v[]) +{ + gmx_mtop_atomloop_all_t aloop; + t_atom *atom; + int a; + + aloop = gmx_mtop_atomloop_all_init(mtop); + while (gmx_mtop_atomloop_all_next(aloop, &a, &atom)) + { + if (atom->ptype == eptShell || + atom->ptype == eptBond || + atom->ptype == eptVSite) + { + clear_rvec(v[a]); + } + } +} + +static gmx_bool nint_ftype(gmx_mtop_t *mtop, t_molinfo *mi, int ftype) +{ + int nint, mb; + + nint = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + nint += mtop->molblock[mb].nmol*mi[mtop->molblock[mb].type].plist[ftype].nr; + } + + return nint; +} + +/* This routine reorders the molecule type array + * in the order of use in the molblocks, + * unused molecule types are deleted. + */ +static void renumber_moltypes(gmx_mtop_t *sys, + int *nmolinfo, t_molinfo **molinfo) +{ + int *order, norder, i; + int mb, mi; + t_molinfo *minew; + + snew(order, *nmolinfo); + norder = 0; + for (mb = 0; mb < sys->nmolblock; mb++) + { + for (i = 0; i < norder; i++) + { + if (order[i] == sys->molblock[mb].type) + { + break; + } + } + if (i == norder) + { + /* This type did not occur yet, add it */ + order[norder] = sys->molblock[mb].type; + /* Renumber the moltype in the topology */ + norder++; + } + sys->molblock[mb].type = i; + } + + /* We still need to reorder the molinfo structs */ + snew(minew, norder); + for (mi = 0; mi < *nmolinfo; mi++) + { + for (i = 0; i < norder; i++) + { + if (order[i] == mi) + { + break; + } + } + if (i == norder) + { + done_mi(&(*molinfo)[mi]); + } + else + { + minew[i] = (*molinfo)[mi]; + } + } + sfree(*molinfo); + + *nmolinfo = norder; + *molinfo = minew; +} + +static void molinfo2mtop(int nmi, t_molinfo *mi, gmx_mtop_t *mtop) +{ + int m; + gmx_moltype_t *molt; + + mtop->nmoltype = nmi; + snew(mtop->moltype, nmi); + for (m = 0; m < nmi; m++) + { + molt = &mtop->moltype[m]; + molt->name = mi[m].name; + molt->atoms = mi[m].atoms; + /* ilists are copied later */ + molt->cgs = mi[m].cgs; + molt->excls = mi[m].excls; + } +} + +static void +new_status(const char *topfile, const char *topppfile, const char *confin, + t_gromppopts *opts, t_inputrec *ir, gmx_bool bZero, + gmx_bool bGenVel, gmx_bool bVerbose, t_state *state, + gpp_atomtype_t atype, gmx_mtop_t *sys, + int *nmi, t_molinfo **mi, t_params plist[], + int *comb, double *reppow, real *fudgeQQ, + gmx_bool bMorse, + warninp_t wi) +{ + t_molinfo *molinfo = NULL; + int nmolblock; + gmx_molblock_t *molblock, *molbs; + t_atoms *confat; + int mb, i, nrmols, nmismatch; + char buf[STRLEN]; + gmx_bool bGB = FALSE; + char warn_buf[STRLEN]; + + init_mtop(sys); + + /* Set gmx_boolean for GB */ + if (ir->implicit_solvent) + { + bGB = TRUE; + } + + /* TOPOLOGY processing */ + sys->name = do_top(bVerbose, topfile, topppfile, opts, bZero, &(sys->symtab), + plist, comb, reppow, fudgeQQ, + atype, &nrmols, &molinfo, ir, + &nmolblock, &molblock, bGB, + wi); + + sys->nmolblock = 0; + snew(sys->molblock, nmolblock); + + sys->natoms = 0; + for (mb = 0; mb < nmolblock; mb++) + { + if (sys->nmolblock > 0 && + molblock[mb].type == sys->molblock[sys->nmolblock-1].type) + { + /* Merge consecutive blocks with the same molecule type */ + sys->molblock[sys->nmolblock-1].nmol += molblock[mb].nmol; + sys->natoms += molblock[mb].nmol*sys->molblock[sys->nmolblock-1].natoms_mol; + } + else if (molblock[mb].nmol > 0) + { + /* Add a new molblock to the topology */ + molbs = &sys->molblock[sys->nmolblock]; + *molbs = molblock[mb]; + molbs->natoms_mol = molinfo[molbs->type].atoms.nr; + molbs->nposres_xA = 0; + molbs->nposres_xB = 0; + sys->natoms += molbs->nmol*molbs->natoms_mol; + sys->nmolblock++; + } + } + if (sys->nmolblock == 0) + { + gmx_fatal(FARGS, "No molecules were defined in the system"); + } + + renumber_moltypes(sys, &nrmols, &molinfo); + + if (bMorse) + { + convert_harmonics(nrmols, molinfo, atype); + } + + if (ir->eDisre == edrNone) + { + i = rm_interactions(F_DISRES, nrmols, molinfo); + if (i > 0) + { + set_warning_line(wi, "unknown", -1); + sprintf(warn_buf, "disre = no, removed %d distance restraints", i); + warning_note(wi, warn_buf); + } + } + if (opts->bOrire == FALSE) + { + i = rm_interactions(F_ORIRES, nrmols, molinfo); + if (i > 0) + { + set_warning_line(wi, "unknown", -1); + sprintf(warn_buf, "orire = no, removed %d orientation restraints", i); + warning_note(wi, warn_buf); + } + } + + /* Copy structures from msys to sys */ + molinfo2mtop(nrmols, molinfo, sys); + + gmx_mtop_finalize(sys); + + /* COORDINATE file processing */ + if (bVerbose) + { + fprintf(stderr, "processing coordinates...\n"); + } + + get_stx_coordnum(confin, &state->natoms); + if (state->natoms != sys->natoms) + { + gmx_fatal(FARGS, "number of coordinates in coordinate file (%s, %d)\n" + " does not match topology (%s, %d)", + confin, state->natoms, topfile, sys->natoms); + } + else + { + /* make space for coordinates and velocities */ + char title[STRLEN]; + snew(confat, 1); + init_t_atoms(confat, state->natoms, FALSE); + init_state(state, state->natoms, 0, 0, 0, 0); + read_stx_conf(confin, title, confat, state->x, state->v, NULL, state->box); + /* This call fixes the box shape for runs with pressure scaling */ + set_box_rel(ir, state); + + nmismatch = check_atom_names(topfile, confin, sys, confat); + free_t_atoms(confat, TRUE); + sfree(confat); + + if (nmismatch) + { + sprintf(buf, "%d non-matching atom name%s\n" + "atom names from %s will be used\n" + "atom names from %s will be ignored\n", + nmismatch, (nmismatch == 1) ? "" : "s", topfile, confin); + warning(wi, buf); + } + if (bVerbose) + { + fprintf(stderr, "double-checking input for internal consistency...\n"); + } + double_check(ir, state->box, nint_ftype(sys, molinfo, F_CONSTR), wi); + } + + if (bGenVel) + { + real *mass; + gmx_mtop_atomloop_all_t aloop; + t_atom *atom; + unsigned int useed; + + snew(mass, state->natoms); + aloop = gmx_mtop_atomloop_all_init(sys); + while (gmx_mtop_atomloop_all_next(aloop, &i, &atom)) + { + mass[i] = atom->m; + } + + useed = opts->seed; + if (opts->seed == -1) + { + useed = (int)gmx_rng_make_seed(); + fprintf(stderr, "Setting gen_seed to %u\n", useed); + } + maxwell_speed(opts->tempi, useed, sys, state->v); + + stop_cm(stdout, state->natoms, mass, state->x, state->v); + sfree(mass); + } + + *nmi = nrmols; + *mi = molinfo; +} + +static void copy_state(const char *slog, t_trxframe *fr, + gmx_bool bReadVel, t_state *state, + double *use_time) +{ + int i; + + if (fr->not_ok & FRAME_NOT_OK) + { + gmx_fatal(FARGS, "Can not start from an incomplete frame"); + } + if (!fr->bX) + { + gmx_fatal(FARGS, "Did not find a frame with coordinates in file %s", + slog); + } + + for (i = 0; i < state->natoms; i++) + { + copy_rvec(fr->x[i], state->x[i]); + } + if (bReadVel) + { + if (!fr->bV) + { + gmx_incons("Trajecory frame unexpectedly does not contain velocities"); + } + for (i = 0; i < state->natoms; i++) + { + copy_rvec(fr->v[i], state->v[i]); + } + } + if (fr->bBox) + { + copy_mat(fr->box, state->box); + } + + *use_time = fr->time; +} + +static void cont_status(const char *slog, const char *ener, + gmx_bool bNeedVel, gmx_bool bGenVel, real fr_time, + t_inputrec *ir, t_state *state, + gmx_mtop_t *sys, + const output_env_t oenv) +/* If fr_time == -1 read the last frame available which is complete */ +{ + gmx_bool bReadVel; + t_trxframe fr; + t_trxstatus *fp; + int i; + double use_time; + + bReadVel = (bNeedVel && !bGenVel); + + fprintf(stderr, + "Reading Coordinates%s and Box size from old trajectory\n", + bReadVel ? ", Velocities" : ""); + if (fr_time == -1) + { + fprintf(stderr, "Will read whole trajectory\n"); + } + else + { + fprintf(stderr, "Will read till time %g\n", fr_time); + } + if (!bReadVel) + { + if (bGenVel) + { + fprintf(stderr, "Velocities generated: " + "ignoring velocities in input trajectory\n"); + } + read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X); + } + else + { + read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X | TRX_NEED_V); + + if (!fr.bV) + { + fprintf(stderr, + "\n" + "WARNING: Did not find a frame with velocities in file %s,\n" + " all velocities will be set to zero!\n\n", slog); + for (i = 0; i < sys->natoms; i++) + { + clear_rvec(state->v[i]); + } + close_trj(fp); + /* Search for a frame without velocities */ + bReadVel = FALSE; + read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X); + } + } + + state->natoms = fr.natoms; + + if (sys->natoms != state->natoms) + { + gmx_fatal(FARGS, "Number of atoms in Topology " + "is not the same as in Trajectory"); + } + copy_state(slog, &fr, bReadVel, state, &use_time); + + /* Find the appropriate frame */ + while ((fr_time == -1 || fr.time < fr_time) && + read_next_frame(oenv, fp, &fr)) + { + copy_state(slog, &fr, bReadVel, state, &use_time); + } + + close_trj(fp); + + /* Set the relative box lengths for preserving the box shape. + * Note that this call can lead to differences in the last bit + * with respect to using gmx convert-tpr to create a [TT].tpx[tt] file. + */ + set_box_rel(ir, state); + + fprintf(stderr, "Using frame at t = %g ps\n", use_time); + fprintf(stderr, "Starting time for run is %g ps\n", ir->init_t); + + if ((ir->epc != epcNO || ir->etc == etcNOSEHOOVER) && ener) + { + get_enx_state(ener, use_time, &sys->groups, ir, state); + preserve_box_shape(ir, state->box_rel, state->boxv); + } +} + +static void read_posres(gmx_mtop_t *mtop, t_molinfo *molinfo, gmx_bool bTopB, + char *fn, + int rc_scaling, int ePBC, + rvec com, + warninp_t wi) +{ + gmx_bool bFirst = TRUE, *hadAtom; + rvec *x, *v, *xp; + dvec sum; + double totmass; + t_atoms dumat; + matrix box, invbox; + int natoms, npbcdim = 0; + char warn_buf[STRLEN], title[STRLEN]; + int a, i, ai, j, k, mb, nat_molb; + gmx_molblock_t *molb; + t_params *pr, *prfb; + t_atom *atom; + + get_stx_coordnum(fn, &natoms); + if (natoms != mtop->natoms) + { + sprintf(warn_buf, "The number of atoms in %s (%d) does not match the number of atoms in the topology (%d). Will assume that the first %d atoms in the topology and %s match.", fn, natoms, mtop->natoms, min(mtop->natoms, natoms), fn); + warning(wi, warn_buf); + } + snew(x, natoms); + snew(v, natoms); + init_t_atoms(&dumat, natoms, FALSE); + read_stx_conf(fn, title, &dumat, x, v, NULL, box); + + npbcdim = ePBC2npbcdim(ePBC); + clear_rvec(com); + if (rc_scaling != erscNO) + { + copy_mat(box, invbox); + for (j = npbcdim; j < DIM; j++) + { + clear_rvec(invbox[j]); + invbox[j][j] = 1; + } + m_inv_ur0(invbox, invbox); + } + + /* Copy the reference coordinates to mtop */ + clear_dvec(sum); + totmass = 0; + a = 0; + snew(hadAtom, natoms); + for (mb = 0; mb < mtop->nmolblock; mb++) + { + molb = &mtop->molblock[mb]; + nat_molb = molb->nmol*mtop->moltype[molb->type].atoms.nr; + pr = &(molinfo[molb->type].plist[F_POSRES]); + prfb = &(molinfo[molb->type].plist[F_FBPOSRES]); + if (pr->nr > 0 || prfb->nr > 0) + { + atom = mtop->moltype[molb->type].atoms.atom; + for (i = 0; (i < pr->nr); i++) + { + ai = pr->param[i].AI; + if (ai >= natoms) + { + gmx_fatal(FARGS, "Position restraint atom index (%d) in moltype '%s' is larger than number of atoms in %s (%d).\n", + ai+1, *molinfo[molb->type].name, fn, natoms); + } + hadAtom[ai] = TRUE; + if (rc_scaling == erscCOM) + { + /* Determine the center of mass of the posres reference coordinates */ + for (j = 0; j < npbcdim; j++) + { + sum[j] += atom[ai].m*x[a+ai][j]; + } + totmass += atom[ai].m; + } + } + /* Same for flat-bottomed posres, but do not count an atom twice for COM */ + for (i = 0; (i < prfb->nr); i++) + { + ai = prfb->param[i].AI; + if (ai >= natoms) + { + gmx_fatal(FARGS, "Position restraint atom index (%d) in moltype '%s' is larger than number of atoms in %s (%d).\n", + ai+1, *molinfo[molb->type].name, fn, natoms); + } + if (rc_scaling == erscCOM && hadAtom[ai] == FALSE) + { + /* Determine the center of mass of the posres reference coordinates */ + for (j = 0; j < npbcdim; j++) + { + sum[j] += atom[ai].m*x[a+ai][j]; + } + totmass += atom[ai].m; + } + } + if (!bTopB) + { + molb->nposres_xA = nat_molb; + snew(molb->posres_xA, molb->nposres_xA); + for (i = 0; i < nat_molb; i++) + { + copy_rvec(x[a+i], molb->posres_xA[i]); + } + } + else + { + molb->nposres_xB = nat_molb; + snew(molb->posres_xB, molb->nposres_xB); + for (i = 0; i < nat_molb; i++) + { + copy_rvec(x[a+i], molb->posres_xB[i]); + } + } + } + a += nat_molb; + } + if (rc_scaling == erscCOM) + { + if (totmass == 0) + { + gmx_fatal(FARGS, "The total mass of the position restraint atoms is 0"); + } + for (j = 0; j < npbcdim; j++) + { + com[j] = sum[j]/totmass; + } + fprintf(stderr, "The center of mass of the position restraint coord's is %6.3f %6.3f %6.3f\n", com[XX], com[YY], com[ZZ]); + } + + if (rc_scaling != erscNO) + { + assert(npbcdim <= DIM); + + for (mb = 0; mb < mtop->nmolblock; mb++) + { + molb = &mtop->molblock[mb]; + nat_molb = molb->nmol*mtop->moltype[molb->type].atoms.nr; + if (molb->nposres_xA > 0 || molb->nposres_xB > 0) + { + xp = (!bTopB ? molb->posres_xA : molb->posres_xB); + for (i = 0; i < nat_molb; i++) + { + for (j = 0; j < npbcdim; j++) + { + if (rc_scaling == erscALL) + { + /* Convert from Cartesian to crystal coordinates */ + xp[i][j] *= invbox[j][j]; + for (k = j+1; k < npbcdim; k++) + { + xp[i][j] += invbox[k][j]*xp[i][k]; + } + } + else if (rc_scaling == erscCOM) + { + /* Subtract the center of mass */ + xp[i][j] -= com[j]; + } + } + } + } + } + + if (rc_scaling == erscCOM) + { + /* Convert the COM from Cartesian to crystal coordinates */ + for (j = 0; j < npbcdim; j++) + { + com[j] *= invbox[j][j]; + for (k = j+1; k < npbcdim; k++) + { + com[j] += invbox[k][j]*com[k]; + } + } + } + } + + free_t_atoms(&dumat, TRUE); + sfree(x); + sfree(v); + sfree(hadAtom); +} + +static void gen_posres(gmx_mtop_t *mtop, t_molinfo *mi, + char *fnA, char *fnB, + int rc_scaling, int ePBC, + rvec com, rvec comB, + warninp_t wi) +{ + int i, j; + + read_posres (mtop, mi, FALSE, fnA, rc_scaling, ePBC, com, wi); + if (strcmp(fnA, fnB) != 0) + { + read_posres(mtop, mi, TRUE, fnB, rc_scaling, ePBC, comB, wi); + } +} + +static void set_wall_atomtype(gpp_atomtype_t at, t_gromppopts *opts, + t_inputrec *ir, warninp_t wi) +{ + int i; + char warn_buf[STRLEN]; + + if (ir->nwall > 0) + { + fprintf(stderr, "Searching the wall atom type(s)\n"); + } + for (i = 0; i < ir->nwall; i++) + { + ir->wall_atomtype[i] = get_atomtype_type(opts->wall_atomtype[i], at); + if (ir->wall_atomtype[i] == NOTSET) + { + sprintf(warn_buf, "Specified wall atom type %s is not defined", opts->wall_atomtype[i]); + warning_error(wi, warn_buf); + } + } +} + +static int nrdf_internal(t_atoms *atoms) +{ + int i, nmass, nrdf; + + nmass = 0; + for (i = 0; i < atoms->nr; i++) + { + /* Vsite ptype might not be set here yet, so also check the mass */ + if ((atoms->atom[i].ptype == eptAtom || + atoms->atom[i].ptype == eptNucleus) + && atoms->atom[i].m > 0) + { + nmass++; + } + } + switch (nmass) + { + case 0: nrdf = 0; break; + case 1: nrdf = 0; break; + case 2: nrdf = 1; break; + default: nrdf = nmass*3 - 6; break; + } + + return nrdf; +} + +void +spline1d( double dx, + double * y, + int n, + double * u, + double * y2 ) +{ + int i; + double p, q; + + y2[0] = 0.0; + u[0] = 0.0; + + for (i = 1; i < n-1; i++) + { + p = 0.5*y2[i-1]+2.0; + y2[i] = -0.5/p; + q = (y[i+1]-2.0*y[i]+y[i-1])/dx; + u[i] = (3.0*q/dx-0.5*u[i-1])/p; + } + + y2[n-1] = 0.0; + + for (i = n-2; i >= 0; i--) + { + y2[i] = y2[i]*y2[i+1]+u[i]; + } +} + + +void +interpolate1d( double xmin, + double dx, + double * ya, + double * y2a, + double x, + double * y, + double * y1) +{ + int ix; + double a, b; + + ix = (x-xmin)/dx; + + a = (xmin+(ix+1)*dx-x)/dx; + b = (x-xmin-ix*dx)/dx; + + *y = a*ya[ix]+b*ya[ix+1]+((a*a*a-a)*y2a[ix]+(b*b*b-b)*y2a[ix+1])*(dx*dx)/6.0; + *y1 = (ya[ix+1]-ya[ix])/dx-(3.0*a*a-1.0)/6.0*dx*y2a[ix]+(3.0*b*b-1.0)/6.0*dx*y2a[ix+1]; +} + + +void +setup_cmap (int grid_spacing, + int nc, + real * grid, + gmx_cmap_t * cmap_grid) +{ + double *tmp_u, *tmp_u2, *tmp_yy, *tmp_y1, *tmp_t2, *tmp_grid; + + int i, j, k, ii, jj, kk, idx; + int offset; + double dx, xmin, v, v1, v2, v12; + double phi, psi; + + snew(tmp_u, 2*grid_spacing); + snew(tmp_u2, 2*grid_spacing); + snew(tmp_yy, 2*grid_spacing); + snew(tmp_y1, 2*grid_spacing); + snew(tmp_t2, 2*grid_spacing*2*grid_spacing); + snew(tmp_grid, 2*grid_spacing*2*grid_spacing); + + dx = 360.0/grid_spacing; + xmin = -180.0-dx*grid_spacing/2; + + for (kk = 0; kk < nc; kk++) + { + /* Compute an offset depending on which cmap we are using + * Offset will be the map number multiplied with the + * grid_spacing * grid_spacing * 2 + */ + offset = kk * grid_spacing * grid_spacing * 2; + + for (i = 0; i < 2*grid_spacing; i++) + { + ii = (i+grid_spacing-grid_spacing/2)%grid_spacing; + + for (j = 0; j < 2*grid_spacing; j++) + { + jj = (j+grid_spacing-grid_spacing/2)%grid_spacing; + tmp_grid[i*grid_spacing*2+j] = grid[offset+ii*grid_spacing+jj]; + } + } + + for (i = 0; i < 2*grid_spacing; i++) + { + spline1d(dx, &(tmp_grid[2*grid_spacing*i]), 2*grid_spacing, tmp_u, &(tmp_t2[2*grid_spacing*i])); + } + + for (i = grid_spacing/2; i < grid_spacing+grid_spacing/2; i++) + { + ii = i-grid_spacing/2; + phi = ii*dx-180.0; + + for (j = grid_spacing/2; j < grid_spacing+grid_spacing/2; j++) + { + jj = j-grid_spacing/2; + psi = jj*dx-180.0; + + for (k = 0; k < 2*grid_spacing; k++) + { + interpolate1d(xmin, dx, &(tmp_grid[2*grid_spacing*k]), + &(tmp_t2[2*grid_spacing*k]), psi, &tmp_yy[k], &tmp_y1[k]); + } + + spline1d(dx, tmp_yy, 2*grid_spacing, tmp_u, tmp_u2); + interpolate1d(xmin, dx, tmp_yy, tmp_u2, phi, &v, &v1); + spline1d(dx, tmp_y1, 2*grid_spacing, tmp_u, tmp_u2); + interpolate1d(xmin, dx, tmp_y1, tmp_u2, phi, &v2, &v12); + + idx = ii*grid_spacing+jj; + cmap_grid->cmapdata[kk].cmap[idx*4] = grid[offset+ii*grid_spacing+jj]; + cmap_grid->cmapdata[kk].cmap[idx*4+1] = v1; + cmap_grid->cmapdata[kk].cmap[idx*4+2] = v2; + cmap_grid->cmapdata[kk].cmap[idx*4+3] = v12; + } + } + } +} + +void init_cmap_grid(gmx_cmap_t *cmap_grid, int ngrid, int grid_spacing) +{ + int i, k, nelem; + + cmap_grid->ngrid = ngrid; + cmap_grid->grid_spacing = grid_spacing; + nelem = cmap_grid->grid_spacing*cmap_grid->grid_spacing; + + snew(cmap_grid->cmapdata, ngrid); + + for (i = 0; i < cmap_grid->ngrid; i++) + { + snew(cmap_grid->cmapdata[i].cmap, 4*nelem); + } +} + + +static int count_constraints(gmx_mtop_t *mtop, t_molinfo *mi, warninp_t wi) +{ + int count, count_mol, i, mb; + gmx_molblock_t *molb; + t_params *plist; + char buf[STRLEN]; + + count = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + count_mol = 0; + molb = &mtop->molblock[mb]; + plist = mi[molb->type].plist; + + for (i = 0; i < F_NRE; i++) + { + if (i == F_SETTLE) + { + count_mol += 3*plist[i].nr; + } + else if (interaction_function[i].flags & IF_CONSTRAINT) + { + count_mol += plist[i].nr; + } + } + + if (count_mol > nrdf_internal(&mi[molb->type].atoms)) + { + sprintf(buf, + "Molecule type '%s' has %d constraints.\n" + "For stability and efficiency there should not be more constraints than internal number of degrees of freedom: %d.\n", + *mi[molb->type].name, count_mol, + nrdf_internal(&mi[molb->type].atoms)); + warning(wi, buf); + } + count += molb->nmol*count_mol; + } + + return count; +} + +static void check_gbsa_params_charged(gmx_mtop_t *sys, gpp_atomtype_t atype) +{ + int i, nmiss, natoms, mt; + real q; + const t_atoms *atoms; + + nmiss = 0; + for (mt = 0; mt < sys->nmoltype; mt++) + { + atoms = &sys->moltype[mt].atoms; + natoms = atoms->nr; + + for (i = 0; i < natoms; i++) + { + q = atoms->atom[i].q; + if ((get_atomtype_radius(atoms->atom[i].type, atype) == 0 || + get_atomtype_vol(atoms->atom[i].type, atype) == 0 || + get_atomtype_surftens(atoms->atom[i].type, atype) == 0 || + get_atomtype_gb_radius(atoms->atom[i].type, atype) == 0 || + get_atomtype_S_hct(atoms->atom[i].type, atype) == 0) && + q != 0) + { + fprintf(stderr, "\nGB parameter(s) zero for atom type '%s' while charge is %g\n", + get_atomtype_name(atoms->atom[i].type, atype), q); + nmiss++; + } + } + } + + if (nmiss > 0) + { + gmx_fatal(FARGS, "Can't do GB electrostatics; the implicit_genborn_params section of the forcefield has parameters with value zero for %d atomtypes that occur as charged atoms.", nmiss); + } +} + + +static void check_gbsa_params(gpp_atomtype_t atype) +{ + int nmiss, i; + + /* If we are doing GBSA, check that we got the parameters we need + * This checking is to see if there are GBSA paratmeters for all + * atoms in the force field. To go around this for testing purposes + * comment out the nerror++ counter temporarily + */ + nmiss = 0; + for (i = 0; i < get_atomtype_ntypes(atype); i++) + { + if (get_atomtype_radius(i, atype) < 0 || + get_atomtype_vol(i, atype) < 0 || + get_atomtype_surftens(i, atype) < 0 || + get_atomtype_gb_radius(i, atype) < 0 || + get_atomtype_S_hct(i, atype) < 0) + { + fprintf(stderr, "\nGB parameter(s) missing or negative for atom type '%s'\n", + get_atomtype_name(i, atype)); + nmiss++; + } + } + + if (nmiss > 0) + { + gmx_fatal(FARGS, "Can't do GB electrostatics; the implicit_genborn_params section of the forcefield is missing parameters for %d atomtypes or they might be negative.", nmiss); + } + +} + +static real calc_temp(const gmx_mtop_t *mtop, + const t_inputrec *ir, + rvec *v) +{ + double sum_mv2; + gmx_mtop_atomloop_all_t aloop; + t_atom *atom; + int a; + int nrdf, g; + + sum_mv2 = 0; + + aloop = gmx_mtop_atomloop_all_init(mtop); + while (gmx_mtop_atomloop_all_next(aloop, &a, &atom)) + { + sum_mv2 += atom->m*norm2(v[a]); + } + + nrdf = 0; + for (g = 0; g < ir->opts.ngtc; g++) + { + nrdf += ir->opts.nrdf[g]; + } + + return sum_mv2/(nrdf*BOLTZ); +} + +static real get_max_reference_temp(const t_inputrec *ir, + warninp_t wi) +{ + real ref_t; + int i; + gmx_bool bNoCoupl; + + ref_t = 0; + bNoCoupl = FALSE; + for (i = 0; i < ir->opts.ngtc; i++) + { + if (ir->opts.tau_t[i] < 0) + { + bNoCoupl = TRUE; + } + else + { + ref_t = max(ref_t, ir->opts.ref_t[i]); + } + } + + if (bNoCoupl) + { + char buf[STRLEN]; + + sprintf(buf, "Some temperature coupling groups do not use temperature coupling. We will assume their temperature is not more than %.3f K. If their temperature is higher, the energy error and the Verlet buffer might be underestimated.", + ref_t); + warning(wi, buf); + } + + return ref_t; +} + +static void set_verlet_buffer(const gmx_mtop_t *mtop, + t_inputrec *ir, + real buffer_temp, + matrix box, + warninp_t wi) +{ + int i; + verletbuf_list_setup_t ls; + real rlist_1x1; + int n_nonlin_vsite; + char warn_buf[STRLEN]; + + printf("Determining Verlet buffer for a tolerance of %g kJ/mol/ps at %g K\n", ir->verletbuf_tol, buffer_temp); + + /* Calculate the buffer size for simple atom vs atoms list */ + ls.cluster_size_i = 1; + ls.cluster_size_j = 1; + calc_verlet_buffer_size(mtop, det(box), ir, buffer_temp, + &ls, &n_nonlin_vsite, &rlist_1x1); + + /* Set the pair-list buffer size in ir */ + verletbuf_get_list_setup(FALSE, &ls); + calc_verlet_buffer_size(mtop, det(box), ir, buffer_temp, + &ls, &n_nonlin_vsite, &ir->rlist); + + if (n_nonlin_vsite > 0) + { + sprintf(warn_buf, "There are %d non-linear virtual site constructions. Their contribution to the energy error is approximated. In most cases this does not affect the error significantly.", n_nonlin_vsite); + warning_note(wi, warn_buf); + } + + printf("Calculated rlist for %dx%d atom pair-list as %.3f nm, buffer size %.3f nm\n", + 1, 1, rlist_1x1, rlist_1x1-max(ir->rvdw, ir->rcoulomb)); + + ir->rlistlong = ir->rlist; + printf("Set rlist, assuming %dx%d atom pair-list, to %.3f nm, buffer size %.3f nm\n", + ls.cluster_size_i, ls.cluster_size_j, + ir->rlist, ir->rlist-max(ir->rvdw, ir->rcoulomb)); + + if (sqr(ir->rlistlong) >= max_cutoff2(ir->ePBC, box)) + { + gmx_fatal(FARGS, "The pair-list cut-off (%g nm) is longer than half the shortest box vector or longer than the smallest box diagonal element (%g nm). Increase the box size or decrease nstlist or increase verlet-buffer-tolerance.", ir->rlistlong, sqrt(max_cutoff2(ir->ePBC, box))); + } +} + +int gmx_grompp(int argc, char *argv[]) +{ + static const char *desc[] = { + "[THISMODULE] (the gromacs preprocessor)", + "reads a molecular topology file, checks the validity of the", + "file, expands the topology from a molecular description to an atomic", + "description. The topology file contains information about", + "molecule types and the number of molecules, the preprocessor", + "copies each molecule as needed. ", + "There is no limitation on the number of molecule types. ", + "Bonds and bond-angles can be converted into constraints, separately", + "for hydrogens and heavy atoms.", + "Then a coordinate file is read and velocities can be generated", + "from a Maxwellian distribution if requested.", + "[THISMODULE] also reads parameters for [gmx-mdrun] ", + "(eg. number of MD steps, time step, cut-off), and others such as", + "NEMD parameters, which are corrected so that the net acceleration", + "is zero.", + "Eventually a binary file is produced that can serve as the sole input", + "file for the MD program.[PAR]", + + "[THISMODULE] uses the atom names from the topology file. The atom names", + "in the coordinate file (option [TT]-c[tt]) are only read to generate", + "warnings when they do not match the atom names in the topology.", + "Note that the atom names are irrelevant for the simulation as", + "only the atom types are used for generating interaction parameters.[PAR]", + + "[THISMODULE] uses a built-in preprocessor to resolve includes, macros, ", + "etc. The preprocessor supports the following keywords:[PAR]", + "#ifdef VARIABLE[BR]", + "#ifndef VARIABLE[BR]", + "#else[BR]", + "#endif[BR]", + "#define VARIABLE[BR]", + "#undef VARIABLE[BR]" + "#include \"filename\"[BR]", + "#include [PAR]", + "The functioning of these statements in your topology may be modulated by", + "using the following two flags in your [TT].mdp[tt] file:[PAR]", + "[TT]define = -DVARIABLE1 -DVARIABLE2[BR]", + "include = -I/home/john/doe[tt][BR]", + "For further information a C-programming textbook may help you out.", + "Specifying the [TT]-pp[tt] flag will get the pre-processed", + "topology file written out so that you can verify its contents.[PAR]", + + /* cpp has been unnecessary for some time, hasn't it? + "If your system does not have a C-preprocessor, you can still", + "use [TT]grompp[tt], but you do not have access to the features ", + "from the cpp. Command line options to the C-preprocessor can be given", + "in the [TT].mdp[tt] file. See your local manual (man cpp).[PAR]", + */ + + "When using position restraints a file with restraint coordinates", + "can be supplied with [TT]-r[tt], otherwise restraining will be done", + "with respect to the conformation from the [TT]-c[tt] option.", + "For free energy calculation the the coordinates for the B topology", + "can be supplied with [TT]-rb[tt], otherwise they will be equal to", + "those of the A topology.[PAR]", + + "Starting coordinates can be read from trajectory with [TT]-t[tt].", + "The last frame with coordinates and velocities will be read,", + "unless the [TT]-time[tt] option is used. Only if this information", + "is absent will the coordinates in the [TT]-c[tt] file be used.", + "Note that these velocities will not be used when [TT]gen_vel = yes[tt]", + "in your [TT].mdp[tt] file. An energy file can be supplied with", + "[TT]-e[tt] to read Nose-Hoover and/or Parrinello-Rahman coupling", + "variables.[PAR]", + + "[THISMODULE] can be used to restart simulations (preserving", + "continuity) by supplying just a checkpoint file with [TT]-t[tt].", + "However, for simply changing the number of run steps to extend", + "a run, using [gmx-convert-tpr] is more convenient than [THISMODULE].", + "You then supply the old checkpoint file directly to [gmx-mdrun]", + "with [TT]-cpi[tt]. If you wish to change the ensemble or things", + "like output frequency, then supplying the checkpoint file to", + "[THISMODULE] with [TT]-t[tt] along with a new [TT].mdp[tt] file", + "with [TT]-f[tt] is the recommended procedure.[PAR]", + + "By default, all bonded interactions which have constant energy due to", + "virtual site constructions will be removed. If this constant energy is", + "not zero, this will result in a shift in the total energy. All bonded", + "interactions can be kept by turning off [TT]-rmvsbds[tt]. Additionally,", + "all constraints for distances which will be constant anyway because", + "of virtual site constructions will be removed. If any constraints remain", + "which involve virtual sites, a fatal error will result.[PAR]" + + "To verify your run input file, please take note of all warnings", + "on the screen, and correct where necessary. Do also look at the contents", + "of the [TT]mdout.mdp[tt] file; this contains comment lines, as well as", + "the input that [THISMODULE] has read. If in doubt, you can start [THISMODULE]", + "with the [TT]-debug[tt] option which will give you more information", + "in a file called [TT]grompp.log[tt] (along with real debug info). You", + "can see the contents of the run input file with the [gmx-dump]", + "program. [gmx-check] can be used to compare the contents of two", + "run input files.[PAR]" + + "The [TT]-maxwarn[tt] option can be used to override warnings printed", + "by [THISMODULE] that otherwise halt output. In some cases, warnings are", + "harmless, but usually they are not. The user is advised to carefully", + "interpret the output messages before attempting to bypass them with", + "this option." + }; + t_gromppopts *opts; + gmx_mtop_t *sys; + int nmi; + t_molinfo *mi; + gpp_atomtype_t atype; + t_inputrec *ir; + int natoms, nvsite, comb, mt; + t_params *plist; + t_state state; + matrix box; + real max_spacing, fudgeQQ; + double reppow; + char fn[STRLEN], fnB[STRLEN]; + const char *mdparin; + int ntype; + gmx_bool bNeedVel, bGenVel; + gmx_bool have_atomnumber; + int n12, n13, n14; + t_params *gb_plist = NULL; + gmx_genborn_t *born = NULL; + output_env_t oenv; + gmx_bool bVerbose = FALSE; + warninp_t wi; + char warn_buf[STRLEN]; + unsigned int useed; + t_atoms IMDatoms; /* Atoms to be operated on interactively (IMD) */ + + t_filenm fnm[] = { + { efMDP, NULL, NULL, ffREAD }, + { efMDP, "-po", "mdout", ffWRITE }, + { efSTX, "-c", NULL, ffREAD }, + { efSTX, "-r", NULL, ffOPTRD }, + { efSTX, "-rb", NULL, ffOPTRD }, + { efNDX, NULL, NULL, ffOPTRD }, + { efTOP, NULL, NULL, ffREAD }, + { efTOP, "-pp", "processed", ffOPTWR }, + { efTPX, "-o", NULL, ffWRITE }, + { efTRN, "-t", NULL, ffOPTRD }, + { efEDR, "-e", NULL, ffOPTRD }, + /* This group is needed by the VMD viewer as the start configuration for IMD sessions: */ + { efGRO, "-imd", "imdgroup", ffOPTWR }, + { efTRN, "-ref", "rotref", ffOPTRW } + }; +#define NFILE asize(fnm) + + /* Command line options */ + static gmx_bool bRenum = TRUE; + static gmx_bool bRmVSBds = TRUE, bZero = FALSE; + static int i, maxwarn = 0; + static real fr_time = -1; + t_pargs pa[] = { + { "-v", FALSE, etBOOL, {&bVerbose}, + "Be loud and noisy" }, + { "-time", FALSE, etREAL, {&fr_time}, + "Take frame at or first after this time." }, + { "-rmvsbds", FALSE, etBOOL, {&bRmVSBds}, + "Remove constant bonded interactions with virtual sites" }, + { "-maxwarn", FALSE, etINT, {&maxwarn}, + "Number of allowed warnings during input processing. Not for normal use and may generate unstable systems" }, + { "-zero", FALSE, etBOOL, {&bZero}, + "Set parameters for bonded interactions without defaults to zero instead of generating an error" }, + { "-renum", FALSE, etBOOL, {&bRenum}, + "Renumber atomtypes and minimize number of atomtypes" } + }; + + /* Initiate some variables */ + snew(ir, 1); + snew(opts, 1); + init_ir(ir, opts); + + /* Parse the command line */ + if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa, + asize(desc), desc, 0, NULL, &oenv)) + { + return 0; + } + + wi = init_warning(TRUE, maxwarn); + + /* PARAMETER file processing */ + mdparin = opt2fn("-f", NFILE, fnm); + set_warning_line(wi, mdparin, -1); + get_ir(mdparin, opt2fn("-po", NFILE, fnm), ir, opts, wi); + + if (bVerbose) + { + fprintf(stderr, "checking input for internal consistency...\n"); + } + check_ir(mdparin, ir, opts, wi); + + if (ir->ld_seed == -1) + { + ir->ld_seed = (gmx_int64_t)gmx_rng_make_seed(); + fprintf(stderr, "Setting the LD random seed to %"GMX_PRId64 "\n", ir->ld_seed); + } + + if (ir->expandedvals->lmc_seed == -1) + { + ir->expandedvals->lmc_seed = (int)gmx_rng_make_seed(); + fprintf(stderr, "Setting the lambda MC random seed to %d\n", ir->expandedvals->lmc_seed); + } + + bNeedVel = EI_STATE_VELOCITY(ir->eI); + bGenVel = (bNeedVel && opts->bGenVel); + if (bGenVel && ir->bContinuation) + { + sprintf(warn_buf, + "Generating velocities is inconsistent with attempting " + "to continue a previous run. Choose only one of " + "gen-vel = yes and continuation = yes."); + warning_error(wi, warn_buf); + } + + snew(plist, F_NRE); + init_plist(plist); + snew(sys, 1); + atype = init_atomtype(); + if (debug) + { + pr_symtab(debug, 0, "Just opened", &sys->symtab); + } + + strcpy(fn, ftp2fn(efTOP, NFILE, fnm)); + if (!gmx_fexist(fn)) + { + gmx_fatal(FARGS, "%s does not exist", fn); + } + new_status(fn, opt2fn_null("-pp", NFILE, fnm), opt2fn("-c", NFILE, fnm), + opts, ir, bZero, bGenVel, bVerbose, &state, + atype, sys, &nmi, &mi, plist, &comb, &reppow, &fudgeQQ, + opts->bMorse, + wi); + + if (debug) + { + pr_symtab(debug, 0, "After new_status", &sys->symtab); + } + + nvsite = 0; + /* set parameters for virtual site construction (not for vsiten) */ + for (mt = 0; mt < sys->nmoltype; mt++) + { + nvsite += + set_vsites(bVerbose, &sys->moltype[mt].atoms, atype, mi[mt].plist); + } + /* now throw away all obsolete bonds, angles and dihedrals: */ + /* note: constraints are ALWAYS removed */ + if (nvsite) + { + for (mt = 0; mt < sys->nmoltype; mt++) + { + clean_vsite_bondeds(mi[mt].plist, sys->moltype[mt].atoms.nr, bRmVSBds); + } + } + ++ if (nvsite && ir->eI == eiNM) ++ { ++ gmx_fatal(FARGS, "Normal Mode analysis is not supported with virtual sites.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n"); ++ } ++ + if (ir->cutoff_scheme == ecutsVERLET) + { + fprintf(stderr, "Removing all charge groups because cutoff-scheme=%s\n", + ecutscheme_names[ir->cutoff_scheme]); + + /* Remove all charge groups */ + gmx_mtop_remove_chargegroups(sys); + } + + if (count_constraints(sys, mi, wi) && (ir->eConstrAlg == econtSHAKE)) + { + if (ir->eI == eiCG || ir->eI == eiLBFGS) + { + sprintf(warn_buf, "Can not do %s with %s, use %s", + EI(ir->eI), econstr_names[econtSHAKE], econstr_names[econtLINCS]); + warning_error(wi, warn_buf); + } + if (ir->bPeriodicMols) + { + sprintf(warn_buf, "Can not do periodic molecules with %s, use %s", + econstr_names[econtSHAKE], econstr_names[econtLINCS]); + warning_error(wi, warn_buf); + } + } + + if (EI_SD (ir->eI) && ir->etc != etcNO) + { + warning_note(wi, "Temperature coupling is ignored with SD integrators."); + } + + /* If we are doing QM/MM, check that we got the atom numbers */ + have_atomnumber = TRUE; + for (i = 0; i < get_atomtype_ntypes(atype); i++) + { + have_atomnumber = have_atomnumber && (get_atomtype_atomnumber(i, atype) >= 0); + } + if (!have_atomnumber && ir->bQMMM) + { + warning_error(wi, + "\n" + "It appears as if you are trying to run a QM/MM calculation, but the force\n" + "field you are using does not contain atom numbers fields. This is an\n" + "optional field (introduced in Gromacs 3.3) for general runs, but mandatory\n" + "for QM/MM. The good news is that it is easy to add - put the atom number as\n" + "an integer just before the mass column in ffXXXnb.itp.\n" + "NB: United atoms have the same atom numbers as normal ones.\n\n"); + } + + if (ir->bAdress) + { + if ((ir->adress->const_wf > 1) || (ir->adress->const_wf < 0)) + { + warning_error(wi, "AdResS contant weighting function should be between 0 and 1\n\n"); + } + /** TODO check size of ex+hy width against box size */ + } + + /* Check for errors in the input now, since they might cause problems + * during processing further down. + */ + check_warning_error(wi, FARGS); + + if (opt2bSet("-r", NFILE, fnm)) + { + sprintf(fn, "%s", opt2fn("-r", NFILE, fnm)); + } + else + { + sprintf(fn, "%s", opt2fn("-c", NFILE, fnm)); + } + if (opt2bSet("-rb", NFILE, fnm)) + { + sprintf(fnB, "%s", opt2fn("-rb", NFILE, fnm)); + } + else + { + strcpy(fnB, fn); + } + + if (nint_ftype(sys, mi, F_POSRES) > 0 || nint_ftype(sys, mi, F_FBPOSRES) > 0) + { + if (bVerbose) + { + fprintf(stderr, "Reading position restraint coords from %s", fn); + if (strcmp(fn, fnB) == 0) + { + fprintf(stderr, "\n"); + } + else + { + fprintf(stderr, " and %s\n", fnB); + } + } + gen_posres(sys, mi, fn, fnB, + ir->refcoord_scaling, ir->ePBC, + ir->posres_com, ir->posres_comB, + wi); + } + + /* If we are using CMAP, setup the pre-interpolation grid */ + if (plist->ncmap > 0) + { + init_cmap_grid(&sys->ffparams.cmap_grid, plist->nc, plist->grid_spacing); + setup_cmap(plist->grid_spacing, plist->nc, plist->cmap, &sys->ffparams.cmap_grid); + } + + set_wall_atomtype(atype, opts, ir, wi); + if (bRenum) + { + renum_atype(plist, sys, ir->wall_atomtype, atype, bVerbose); + ntype = get_atomtype_ntypes(atype); + } + + if (ir->implicit_solvent != eisNO) + { + /* Now we have renumbered the atom types, we can check the GBSA params */ + check_gbsa_params(atype); + + /* Check that all atoms that have charge and/or LJ-parameters also have + * sensible GB-parameters + */ + check_gbsa_params_charged(sys, atype); + } + + /* PELA: Copy the atomtype data to the topology atomtype list */ + copy_atomtype_atomtypes(atype, &(sys->atomtypes)); + + if (debug) + { + pr_symtab(debug, 0, "After renum_atype", &sys->symtab); + } + + if (bVerbose) + { + fprintf(stderr, "converting bonded parameters...\n"); + } + + ntype = get_atomtype_ntypes(atype); + convert_params(ntype, plist, mi, comb, reppow, fudgeQQ, sys); + + if (debug) + { + pr_symtab(debug, 0, "After convert_params", &sys->symtab); + } + + /* set ptype to VSite for virtual sites */ + for (mt = 0; mt < sys->nmoltype; mt++) + { + set_vsites_ptype(FALSE, &sys->moltype[mt]); + } + if (debug) + { + pr_symtab(debug, 0, "After virtual sites", &sys->symtab); + } + /* Check velocity for virtual sites and shells */ + if (bGenVel) + { + check_vel(sys, state.v); + } + + /* check masses */ + check_mol(sys, wi); + + for (i = 0; i < sys->nmoltype; i++) + { + check_cg_sizes(ftp2fn(efTOP, NFILE, fnm), &sys->moltype[i].cgs, wi); + } + + if (EI_DYNAMICS(ir->eI) && ir->eI != eiBD) + { + check_bonds_timestep(sys, ir->delta_t, wi); + } + + if (EI_ENERGY_MINIMIZATION(ir->eI) && 0 == ir->nsteps) + { + warning_note(wi, "Zero-step energy minimization will alter the coordinates before calculating the energy. If you just want the energy of a single point, try zero-step MD (with unconstrained_start = yes). To do multiple single-point energy evaluations of different configurations of the same topology, use mdrun -rerun."); + } + + check_warning_error(wi, FARGS); + + if (bVerbose) + { + fprintf(stderr, "initialising group options...\n"); + } + do_index(mdparin, ftp2fn_null(efNDX, NFILE, fnm), + sys, bVerbose, ir, + bGenVel ? state.v : NULL, + wi); + + if (ir->cutoff_scheme == ecutsVERLET && ir->verletbuf_tol > 0 && + ir->nstlist > 1) + { + if (EI_DYNAMICS(ir->eI) && inputrec2nboundeddim(ir) == 3) + { + real buffer_temp; + + if (EI_MD(ir->eI) && ir->etc == etcNO) + { + if (bGenVel) + { + buffer_temp = opts->tempi; + } + else + { + buffer_temp = calc_temp(sys, ir, state.v); + } + if (buffer_temp > 0) + { + sprintf(warn_buf, "NVE simulation: will use the initial temperature of %.3f K for determining the Verlet buffer size", buffer_temp); + warning_note(wi, warn_buf); + } + else + { + sprintf(warn_buf, "NVE simulation with an initial temperature of zero: will use a Verlet buffer of %d%%. Check your energy drift!", + (int)(verlet_buffer_ratio_NVE_T0*100 + 0.5)); + warning_note(wi, warn_buf); + } + } + else + { + buffer_temp = get_max_reference_temp(ir, wi); + } + + if (EI_MD(ir->eI) && ir->etc == etcNO && buffer_temp == 0) + { + /* NVE with initial T=0: we add a fixed ratio to rlist. + * Since we don't actually use verletbuf_tol, we set it to -1 + * so it can't be misused later. + */ + ir->rlist *= 1.0 + verlet_buffer_ratio_NVE_T0; + ir->verletbuf_tol = -1; + } + else + { + /* We warn for NVE simulations with >1(.1)% drift tolerance */ + const real drift_tol = 0.01; + real ener_runtime; + + /* We use 2 DOF per atom = 2kT pot+kin energy, to be on + * the safe side with constraints (without constraints: 3 DOF). + */ + ener_runtime = 2*BOLTZ*buffer_temp/(ir->nsteps*ir->delta_t); + + if (EI_MD(ir->eI) && ir->etc == etcNO && ir->nstlist > 1 && + ir->nsteps > 0 && + ir->verletbuf_tol > 1.1*drift_tol*ener_runtime) + { + sprintf(warn_buf, "You are using a Verlet buffer tolerance of %g kJ/mol/ps for an NVE simulation of length %g ps, which can give a final drift of %d%%. For conserving energy to %d%%, you might need to set verlet-buffer-tolerance to %.1e.", + ir->verletbuf_tol, ir->nsteps*ir->delta_t, + (int)(ir->verletbuf_tol/ener_runtime*100 + 0.5), + (int)(100*drift_tol + 0.5), + drift_tol*ener_runtime); + warning_note(wi, warn_buf); + } + + set_verlet_buffer(sys, ir, buffer_temp, state.box, wi); + } + } + } + + /* Init the temperature coupling state */ + init_gtc_state(&state, ir->opts.ngtc, 0, ir->opts.nhchainlength); /* need to add nnhpres here? */ + + if (bVerbose) + { + fprintf(stderr, "Checking consistency between energy and charge groups...\n"); + } + check_eg_vs_cg(sys); + + if (debug) + { + pr_symtab(debug, 0, "After index", &sys->symtab); + } + + triple_check(mdparin, ir, sys, wi); + close_symtab(&sys->symtab); + if (debug) + { + pr_symtab(debug, 0, "After close", &sys->symtab); + } + + /* make exclusions between QM atoms */ + if (ir->bQMMM) + { + if (ir->QMMMscheme == eQMMMschemenormal && ir->ns_type == ensSIMPLE) + { + gmx_fatal(FARGS, "electrostatic embedding only works with grid neighboursearching, use ns-type=grid instead\n"); + } + else + { + generate_qmexcl(sys, ir, wi); + } + } + + if (ftp2bSet(efTRN, NFILE, fnm)) + { + if (bVerbose) + { + fprintf(stderr, "getting data from old trajectory ...\n"); + } + cont_status(ftp2fn(efTRN, NFILE, fnm), ftp2fn_null(efEDR, NFILE, fnm), + bNeedVel, bGenVel, fr_time, ir, &state, sys, oenv); + } + + if (ir->ePBC == epbcXY && ir->nwall != 2) + { + clear_rvec(state.box[ZZ]); + } + + if (ir->cutoff_scheme != ecutsVERLET && ir->rlist > 0) + { + set_warning_line(wi, mdparin, -1); + check_chargegroup_radii(sys, ir, state.x, wi); + } + + if (EEL_FULL(ir->coulombtype) || EVDW_PME(ir->vdwtype)) + { + /* Calculate the optimal grid dimensions */ + copy_mat(state.box, box); + if (ir->ePBC == epbcXY && ir->nwall == 2) + { + svmul(ir->wall_ewald_zfac, box[ZZ], box[ZZ]); + } + if (ir->nkx > 0 && ir->nky > 0 && ir->nkz > 0) + { + /* Mark fourier_spacing as not used */ + ir->fourier_spacing = 0; + } + else if (ir->nkx != 0 && ir->nky != 0 && ir->nkz != 0) + { + set_warning_line(wi, mdparin, -1); + warning_error(wi, "Some of the Fourier grid sizes are set, but all of them need to be set."); + } + max_spacing = calc_grid(stdout, box, ir->fourier_spacing, + &(ir->nkx), &(ir->nky), &(ir->nkz)); + } + + /* MRS: eventually figure out better logic for initializing the fep + values that makes declaring the lambda and declaring the state not + potentially conflict if not handled correctly. */ + if (ir->efep != efepNO) + { + state.fep_state = ir->fepvals->init_fep_state; + for (i = 0; i < efptNR; i++) + { + /* init_lambda trumps state definitions*/ + if (ir->fepvals->init_lambda >= 0) + { + state.lambda[i] = ir->fepvals->init_lambda; + } + else + { + if (ir->fepvals->all_lambda[i] == NULL) + { + gmx_fatal(FARGS, "Values of lambda not set for a free energy calculation!"); + } + else + { + state.lambda[i] = ir->fepvals->all_lambda[i][state.fep_state]; + } + } + } + } + + if (ir->ePull != epullNO) + { + set_pull_init(ir, sys, state.x, state.box, state.lambda[efptMASS], oenv, opts->pull_start); + } + + if (ir->bRot) + { + set_reference_positions(ir->rot, state.x, state.box, + opt2fn("-ref", NFILE, fnm), opt2bSet("-ref", NFILE, fnm), + wi); + } + + /* reset_multinr(sys); */ + + if (EEL_PME(ir->coulombtype)) + { + float ratio = pme_load_estimate(sys, ir, state.box); + fprintf(stderr, "Estimate for the relative computational load of the PME mesh part: %.2f\n", ratio); + /* With free energy we might need to do PME both for the A and B state + * charges. This will double the cost, but the optimal performance will + * then probably be at a slightly larger cut-off and grid spacing. + */ + if ((ir->efep == efepNO && ratio > 1.0/2.0) || + (ir->efep != efepNO && ratio > 2.0/3.0)) + { + warning_note(wi, + "The optimal PME mesh load for parallel simulations is below 0.5\n" + "and for highly parallel simulations between 0.25 and 0.33,\n" + "for higher performance, increase the cut-off and the PME grid spacing.\n"); + if (ir->efep != efepNO) + { + warning_note(wi, + "For free energy simulations, the optimal load limit increases from 0.5 to 0.667\n"); + } + } + } + + { + char warn_buf[STRLEN]; + double cio = compute_io(ir, sys->natoms, &sys->groups, F_NRE, 1); + sprintf(warn_buf, "This run will generate roughly %.0f Mb of data", cio); + if (cio > 2000) + { + set_warning_line(wi, mdparin, -1); + warning_note(wi, warn_buf); + } + else + { + printf("%s\n", warn_buf); + } + } + + if (bVerbose) + { + fprintf(stderr, "writing run input file...\n"); + } + + done_warning(wi, FARGS); + write_tpx_state(ftp2fn(efTPX, NFILE, fnm), ir, &state, sys); + + /* Output IMD group, if bIMD is TRUE */ + write_IMDgroup_to_file(ir->bIMD, ir, &state, sys, NFILE, fnm); + + done_atomtype(atype); + done_mtop(sys, TRUE); + done_inputrec_strings(); + + return 0; +} diff --cc src/gromacs/gmxpreprocess/readir.c index 2d206d01df,0000000000..68a2a71d73 mode 100644,000000..100644 --- a/src/gromacs/gmxpreprocess/readir.c +++ b/src/gromacs/gmxpreprocess/readir.c @@@ -1,4390 -1,0 +1,4396 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include "sysstuff.h" +#include "gromacs/utility/smalloc.h" +#include "typedefs.h" +#include "physics.h" +#include "names.h" +#include "gmx_fatal.h" +#include "macros.h" +#include "index.h" +#include "symtab.h" +#include "gromacs/utility/cstringutil.h" +#include "readinp.h" +#include "warninp.h" +#include "readir.h" +#include "toputil.h" +#include "index.h" +#include "network.h" +#include "vec.h" +#include "pbc.h" +#include "mtop_util.h" +#include "chargegroup.h" +#include "inputrec.h" +#include "calc_verletbuf.h" + +#define MAXPTR 254 +#define NOGID 255 + +/* Resource parameters + * Do not change any of these until you read the instruction + * in readinp.h. Some cpp's do not take spaces after the backslash + * (like the c-shell), which will give you a very weird compiler + * message. + */ + +typedef struct t_inputrec_strings +{ + char tcgrps[STRLEN], tau_t[STRLEN], ref_t[STRLEN], + acc[STRLEN], accgrps[STRLEN], freeze[STRLEN], frdim[STRLEN], + energy[STRLEN], user1[STRLEN], user2[STRLEN], vcm[STRLEN], x_compressed_groups[STRLEN], + couple_moltype[STRLEN], orirefitgrp[STRLEN], egptable[STRLEN], egpexcl[STRLEN], + wall_atomtype[STRLEN], wall_density[STRLEN], deform[STRLEN], QMMM[STRLEN], + imd_grp[STRLEN]; + char fep_lambda[efptNR][STRLEN]; + char lambda_weights[STRLEN]; + char **pull_grp; + char **rot_grp; + char anneal[STRLEN], anneal_npoints[STRLEN], + anneal_time[STRLEN], anneal_temp[STRLEN]; + char QMmethod[STRLEN], QMbasis[STRLEN], QMcharge[STRLEN], QMmult[STRLEN], + bSH[STRLEN], CASorbitals[STRLEN], CASelectrons[STRLEN], SAon[STRLEN], + SAoff[STRLEN], SAsteps[STRLEN], bTS[STRLEN], bOPT[STRLEN]; + char efield_x[STRLEN], efield_xt[STRLEN], efield_y[STRLEN], + efield_yt[STRLEN], efield_z[STRLEN], efield_zt[STRLEN]; + +} gmx_inputrec_strings; + +static gmx_inputrec_strings *is = NULL; + +void init_inputrec_strings() +{ + if (is) + { + gmx_incons("Attempted to call init_inputrec_strings before calling done_inputrec_strings. Only one inputrec (i.e. .mdp file) can be parsed at a time."); + } + snew(is, 1); +} + +void done_inputrec_strings() +{ + sfree(is); + is = NULL; +} + +static char swapgrp[STRLEN], splitgrp0[STRLEN], splitgrp1[STRLEN], solgrp[STRLEN]; + +enum { + egrptpALL, /* All particles have to be a member of a group. */ + egrptpALL_GENREST, /* A rest group with name is generated for particles * + * that are not part of any group. */ + egrptpPART, /* As egrptpALL_GENREST, but no name is generated * + * for the rest group. */ + egrptpONE /* Merge all selected groups into one group, * + * make a rest group for the remaining particles. */ +}; + +static const char *constraints[eshNR+1] = { + "none", "h-bonds", "all-bonds", "h-angles", "all-angles", NULL +}; + +static const char *couple_lam[ecouplamNR+1] = { + "vdw-q", "vdw", "q", "none", NULL +}; + +void init_ir(t_inputrec *ir, t_gromppopts *opts) +{ + snew(opts->include, STRLEN); + snew(opts->define, STRLEN); + snew(ir->fepvals, 1); + snew(ir->expandedvals, 1); + snew(ir->simtempvals, 1); +} + +static void GetSimTemps(int ntemps, t_simtemp *simtemp, double *temperature_lambdas) +{ + + int i; + + for (i = 0; i < ntemps; i++) + { + /* simple linear scaling -- allows more control */ + if (simtemp->eSimTempScale == esimtempLINEAR) + { + simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*temperature_lambdas[i]; + } + else if (simtemp->eSimTempScale == esimtempGEOMETRIC) /* should give roughly equal acceptance for constant heat capacity . . . */ + { + simtemp->temperatures[i] = simtemp->simtemp_low * pow(simtemp->simtemp_high/simtemp->simtemp_low, (1.0*i)/(ntemps-1)); + } + else if (simtemp->eSimTempScale == esimtempEXPONENTIAL) + { + simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*((exp(temperature_lambdas[i])-1)/(exp(1.0)-1)); + } + else + { + char errorstr[128]; + sprintf(errorstr, "eSimTempScale=%d not defined", simtemp->eSimTempScale); + gmx_fatal(FARGS, errorstr); + } + } +} + + + +static void _low_check(gmx_bool b, char *s, warninp_t wi) +{ + if (b) + { + warning_error(wi, s); + } +} + +static void check_nst(const char *desc_nst, int nst, + const char *desc_p, int *p, + warninp_t wi) +{ + char buf[STRLEN]; + + if (*p > 0 && *p % nst != 0) + { + /* Round up to the next multiple of nst */ + *p = ((*p)/nst + 1)*nst; + sprintf(buf, "%s should be a multiple of %s, changing %s to %d\n", + desc_p, desc_nst, desc_p, *p); + warning(wi, buf); + } +} + +static gmx_bool ir_NVE(const t_inputrec *ir) +{ + return ((ir->eI == eiMD || EI_VV(ir->eI)) && ir->etc == etcNO); +} + +static int lcd(int n1, int n2) +{ + int d, i; + + d = 1; + for (i = 2; (i <= n1 && i <= n2); i++) + { + if (n1 % i == 0 && n2 % i == 0) + { + d = i; + } + } + + return d; +} + +static void process_interaction_modifier(const t_inputrec *ir, int *eintmod) +{ + if (*eintmod == eintmodPOTSHIFT_VERLET) + { + if (ir->cutoff_scheme == ecutsVERLET) + { + *eintmod = eintmodPOTSHIFT; + } + else + { + *eintmod = eintmodNONE; + } + } +} + +void check_ir(const char *mdparin, t_inputrec *ir, t_gromppopts *opts, + warninp_t wi) +/* Check internal consistency. + * NOTE: index groups are not set here yet, don't check things + * like temperature coupling group options here, but in triple_check + */ +{ + /* Strange macro: first one fills the err_buf, and then one can check + * the condition, which will print the message and increase the error + * counter. + */ +#define CHECK(b) _low_check(b, err_buf, wi) + char err_buf[256], warn_buf[STRLEN]; + int i, j; + int ns_type = 0; + real dt_coupl = 0; + real dt_pcoupl; + int nstcmin; + t_lambda *fep = ir->fepvals; + t_expanded *expand = ir->expandedvals; + + set_warning_line(wi, mdparin, -1); + + /* BASIC CUT-OFF STUFF */ + if (ir->rcoulomb < 0) + { + warning_error(wi, "rcoulomb should be >= 0"); + } + if (ir->rvdw < 0) + { + warning_error(wi, "rvdw should be >= 0"); + } + if (ir->rlist < 0 && + !(ir->cutoff_scheme == ecutsVERLET && ir->verletbuf_tol > 0)) + { + warning_error(wi, "rlist should be >= 0"); + } + + process_interaction_modifier(ir, &ir->coulomb_modifier); + process_interaction_modifier(ir, &ir->vdw_modifier); + + if (ir->cutoff_scheme == ecutsGROUP) + { + warning_note(wi, + "The group cutoff scheme is deprecated in Gromacs 5.0 and will be removed in a future " + "release when all interaction forms are supported for the verlet scheme. The verlet " + "scheme already scales better, and it is compatible with GPUs and other accelerators."); + + /* BASIC CUT-OFF STUFF */ + if (ir->rlist == 0 || + !((ir_coulomb_might_be_zero_at_cutoff(ir) && ir->rcoulomb > ir->rlist) || + (ir_vdw_might_be_zero_at_cutoff(ir) && ir->rvdw > ir->rlist))) + { + /* No switched potential and/or no twin-range: + * we can set the long-range cut-off to the maximum of the other cut-offs. + */ + ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb)); + } + else if (ir->rlistlong < 0) + { + ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb)); + sprintf(warn_buf, "rlistlong was not set, setting it to %g (no buffer)", + ir->rlistlong); + warning(wi, warn_buf); + } + if (ir->rlistlong == 0 && ir->ePBC != epbcNONE) + { + warning_error(wi, "Can not have an infinite cut-off with PBC"); + } + if (ir->rlistlong > 0 && (ir->rlist == 0 || ir->rlistlong < ir->rlist)) + { + warning_error(wi, "rlistlong can not be shorter than rlist"); + } + if (IR_TWINRANGE(*ir) && ir->nstlist <= 0) + { + warning_error(wi, "Can not have nstlist<=0 with twin-range interactions"); + } + } + + if (ir->rlistlong == ir->rlist) + { + ir->nstcalclr = 0; + } + else if (ir->rlistlong > ir->rlist && ir->nstcalclr == 0) + { + warning_error(wi, "With different cutoffs for electrostatics and VdW, nstcalclr must be -1 or a positive number"); + } + + if (ir->cutoff_scheme == ecutsVERLET) + { + real rc_max; + + /* Normal Verlet type neighbor-list, currently only limited feature support */ + if (inputrec2nboundeddim(ir) < 3) + { + warning_error(wi, "With Verlet lists only full pbc or pbc=xy with walls is supported"); + } + if (ir->rcoulomb != ir->rvdw) + { + warning_error(wi, "With Verlet lists rcoulomb!=rvdw is not supported"); + } + if (ir->vdwtype == evdwSHIFT || ir->vdwtype == evdwSWITCH) + { + if (ir->vdw_modifier == eintmodNONE || + ir->vdw_modifier == eintmodPOTSHIFT) + { + ir->vdw_modifier = (ir->vdwtype == evdwSHIFT ? eintmodFORCESWITCH : eintmodPOTSWITCH); + + sprintf(warn_buf, "Replacing vdwtype=%s by the equivalent combination of vdwtype=%s and vdw_modifier=%s", evdw_names[ir->vdwtype], evdw_names[evdwCUT], eintmod_names[ir->vdw_modifier]); + warning_note(wi, warn_buf); + + ir->vdwtype = evdwCUT; + } + else + { + sprintf(warn_buf, "Unsupported combination of vdwtype=%s and vdw_modifier=%s", evdw_names[ir->vdwtype], eintmod_names[ir->vdw_modifier]); + warning_error(wi, warn_buf); + } + } + + if (!(ir->vdwtype == evdwCUT || ir->vdwtype == evdwPME)) + { + warning_error(wi, "With Verlet lists only cut-off and PME LJ interactions are supported"); + } + if (!(ir->coulombtype == eelCUT || + (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC) || + EEL_PME(ir->coulombtype) || ir->coulombtype == eelEWALD)) + { + warning_error(wi, "With Verlet lists only cut-off, reaction-field, PME and Ewald electrostatics are supported"); + } + if (!(ir->coulomb_modifier == eintmodNONE || + ir->coulomb_modifier == eintmodPOTSHIFT)) + { + sprintf(warn_buf, "coulomb_modifier=%s is not supported with the Verlet cut-off scheme", eintmod_names[ir->coulomb_modifier]); + warning_error(wi, warn_buf); + } + + if (ir->nstlist <= 0) + { + warning_error(wi, "With Verlet lists nstlist should be larger than 0"); + } + + if (ir->nstlist < 10) + { + warning_note(wi, "With Verlet lists the optimal nstlist is >= 10, with GPUs >= 20. Note that with the Verlet scheme, nstlist has no effect on the accuracy of your simulation."); + } + + rc_max = max(ir->rvdw, ir->rcoulomb); + + if (ir->verletbuf_tol <= 0) + { + if (ir->verletbuf_tol == 0) + { + warning_error(wi, "Can not have Verlet buffer tolerance of exactly 0"); + } + + if (ir->rlist < rc_max) + { + warning_error(wi, "With verlet lists rlist can not be smaller than rvdw or rcoulomb"); + } + + if (ir->rlist == rc_max && ir->nstlist > 1) + { + warning_note(wi, "rlist is equal to rvdw and/or rcoulomb: there is no explicit Verlet buffer. The cluster pair list does have a buffering effect, but choosing a larger rlist might be necessary for good energy conservation."); + } + } + else + { + if (ir->rlist > rc_max) + { + warning_note(wi, "You have set rlist larger than the interaction cut-off, but you also have verlet-buffer-tolerance > 0. Will set rlist using verlet-buffer-tolerance."); + } + + if (ir->nstlist == 1) + { + /* No buffer required */ + ir->rlist = rc_max; + } + else + { + if (EI_DYNAMICS(ir->eI)) + { + if (inputrec2nboundeddim(ir) < 3) + { + warning_error(wi, "The box volume is required for calculating rlist from the energy drift with verlet-buffer-tolerance > 0. You are using at least one unbounded dimension, so no volume can be computed. Either use a finite box, or set rlist yourself together with verlet-buffer-tolerance = -1."); + } + /* Set rlist temporarily so we can continue processing */ + ir->rlist = rc_max; + } + else + { + /* Set the buffer to 5% of the cut-off */ + ir->rlist = (1.0 + verlet_buffer_ratio_nodynamics)*rc_max; + } + } + } + + /* No twin-range calculations with Verlet lists */ + ir->rlistlong = ir->rlist; + } + + if (ir->nstcalclr == -1) + { + /* if rlist=rlistlong, this will later be changed to nstcalclr=0 */ + ir->nstcalclr = ir->nstlist; + } + else if (ir->nstcalclr > 0) + { + if (ir->nstlist > 0 && (ir->nstlist % ir->nstcalclr != 0)) + { + warning_error(wi, "nstlist must be evenly divisible by nstcalclr. Use nstcalclr = -1 to automatically follow nstlist"); + } + } + else if (ir->nstcalclr < -1) + { + warning_error(wi, "nstcalclr must be a positive number (divisor of nstcalclr), or -1 to follow nstlist."); + } + + if (EEL_PME(ir->coulombtype) && ir->rcoulomb > ir->rvdw && ir->nstcalclr > 1) + { + warning_error(wi, "When used with PME, the long-range component of twin-range interactions must be updated every step (nstcalclr)"); + } + + /* GENERAL INTEGRATOR STUFF */ + if (!(ir->eI == eiMD || EI_VV(ir->eI))) + { + ir->etc = etcNO; + } + if (ir->eI == eiVVAK) + { + sprintf(warn_buf, "Integrator method %s is implemented primarily for validation purposes; for molecular dynamics, you should probably be using %s or %s", ei_names[eiVVAK], ei_names[eiMD], ei_names[eiVV]); + warning_note(wi, warn_buf); + } + if (!EI_DYNAMICS(ir->eI)) + { + ir->epc = epcNO; + } + if (EI_DYNAMICS(ir->eI)) + { + if (ir->nstcalcenergy < 0) + { + ir->nstcalcenergy = ir_optimal_nstcalcenergy(ir); + if (ir->nstenergy != 0 && ir->nstenergy < ir->nstcalcenergy) + { + /* nstcalcenergy larger than nstener does not make sense. + * We ideally want nstcalcenergy=nstener. + */ + if (ir->nstlist > 0) + { + ir->nstcalcenergy = lcd(ir->nstenergy, ir->nstlist); + } + else + { + ir->nstcalcenergy = ir->nstenergy; + } + } + } + else if ( (ir->nstenergy > 0 && ir->nstcalcenergy > ir->nstenergy) || + (ir->efep != efepNO && ir->fepvals->nstdhdl > 0 && + (ir->nstcalcenergy > ir->fepvals->nstdhdl) ) ) + + { + const char *nsten = "nstenergy"; + const char *nstdh = "nstdhdl"; + const char *min_name = nsten; + int min_nst = ir->nstenergy; + + /* find the smallest of ( nstenergy, nstdhdl ) */ + if (ir->efep != efepNO && ir->fepvals->nstdhdl > 0 && + (ir->nstenergy == 0 || ir->fepvals->nstdhdl < ir->nstenergy)) + { + min_nst = ir->fepvals->nstdhdl; + min_name = nstdh; + } + /* If the user sets nstenergy small, we should respect that */ + sprintf(warn_buf, + "Setting nstcalcenergy (%d) equal to %s (%d)", + ir->nstcalcenergy, min_name, min_nst); + warning_note(wi, warn_buf); + ir->nstcalcenergy = min_nst; + } + + if (ir->epc != epcNO) + { + if (ir->nstpcouple < 0) + { + ir->nstpcouple = ir_optimal_nstpcouple(ir); + } + } + if (IR_TWINRANGE(*ir)) + { + check_nst("nstlist", ir->nstlist, + "nstcalcenergy", &ir->nstcalcenergy, wi); + if (ir->epc != epcNO) + { + check_nst("nstlist", ir->nstlist, + "nstpcouple", &ir->nstpcouple, wi); + } + } + + if (ir->nstcalcenergy > 0) + { + if (ir->efep != efepNO) + { + /* nstdhdl should be a multiple of nstcalcenergy */ + check_nst("nstcalcenergy", ir->nstcalcenergy, + "nstdhdl", &ir->fepvals->nstdhdl, wi); + /* nstexpanded should be a multiple of nstcalcenergy */ + check_nst("nstcalcenergy", ir->nstcalcenergy, + "nstexpanded", &ir->expandedvals->nstexpanded, wi); + } + /* for storing exact averages nstenergy should be + * a multiple of nstcalcenergy + */ + check_nst("nstcalcenergy", ir->nstcalcenergy, + "nstenergy", &ir->nstenergy, wi); + } + } + + /* LD STUFF */ + if ((EI_SD(ir->eI) || ir->eI == eiBD) && + ir->bContinuation && ir->ld_seed != -1) + { + warning_note(wi, "You are doing a continuation with SD or BD, make sure that ld_seed is different from the previous run (using ld_seed=-1 will ensure this)"); + } + + /* TPI STUFF */ + if (EI_TPI(ir->eI)) + { + sprintf(err_buf, "TPI only works with pbc = %s", epbc_names[epbcXYZ]); + CHECK(ir->ePBC != epbcXYZ); + sprintf(err_buf, "TPI only works with ns = %s", ens_names[ensGRID]); + CHECK(ir->ns_type != ensGRID); + sprintf(err_buf, "with TPI nstlist should be larger than zero"); + CHECK(ir->nstlist <= 0); + sprintf(err_buf, "TPI does not work with full electrostatics other than PME"); + CHECK(EEL_FULL(ir->coulombtype) && !EEL_PME(ir->coulombtype)); + } + + /* SHAKE / LINCS */ + if ( (opts->nshake > 0) && (opts->bMorse) ) + { + sprintf(warn_buf, + "Using morse bond-potentials while constraining bonds is useless"); + warning(wi, warn_buf); + } + + if ((EI_SD(ir->eI) || ir->eI == eiBD) && + ir->bContinuation && ir->ld_seed != -1) + { + warning_note(wi, "You are doing a continuation with SD or BD, make sure that ld_seed is different from the previous run (using ld_seed=-1 will ensure this)"); + } + /* verify simulated tempering options */ + + if (ir->bSimTemp) + { + gmx_bool bAllTempZero = TRUE; + for (i = 0; i < fep->n_lambda; i++) + { + sprintf(err_buf, "Entry %d for %s must be between 0 and 1, instead is %g", i, efpt_names[efptTEMPERATURE], fep->all_lambda[efptTEMPERATURE][i]); + CHECK((fep->all_lambda[efptTEMPERATURE][i] < 0) || (fep->all_lambda[efptTEMPERATURE][i] > 1)); + if (fep->all_lambda[efptTEMPERATURE][i] > 0) + { + bAllTempZero = FALSE; + } + } + sprintf(err_buf, "if simulated tempering is on, temperature-lambdas may not be all zero"); + CHECK(bAllTempZero == TRUE); + + sprintf(err_buf, "Simulated tempering is currently only compatible with md-vv"); + CHECK(ir->eI != eiVV); + + /* check compatability of the temperature coupling with simulated tempering */ + + if (ir->etc == etcNOSEHOOVER) + { + sprintf(warn_buf, "Nose-Hoover based temperature control such as [%s] my not be entirelyconsistent with simulated tempering", etcoupl_names[ir->etc]); + warning_note(wi, warn_buf); + } + + /* check that the temperatures make sense */ + + sprintf(err_buf, "Higher simulated tempering temperature (%g) must be >= than the simulated tempering lower temperature (%g)", ir->simtempvals->simtemp_high, ir->simtempvals->simtemp_low); + CHECK(ir->simtempvals->simtemp_high <= ir->simtempvals->simtemp_low); + + sprintf(err_buf, "Higher simulated tempering temperature (%g) must be >= zero", ir->simtempvals->simtemp_high); + CHECK(ir->simtempvals->simtemp_high <= 0); + + sprintf(err_buf, "Lower simulated tempering temperature (%g) must be >= zero", ir->simtempvals->simtemp_low); + CHECK(ir->simtempvals->simtemp_low <= 0); + } + + /* verify free energy options */ + + if (ir->efep != efepNO) + { + fep = ir->fepvals; + sprintf(err_buf, "The soft-core power is %d and can only be 1 or 2", + fep->sc_power); + CHECK(fep->sc_alpha != 0 && fep->sc_power != 1 && fep->sc_power != 2); + + sprintf(err_buf, "The soft-core sc-r-power is %d and can only be 6 or 48", + (int)fep->sc_r_power); + CHECK(fep->sc_alpha != 0 && fep->sc_r_power != 6.0 && fep->sc_r_power != 48.0); + + sprintf(err_buf, "Can't use postive delta-lambda (%g) if initial state/lambda does not start at zero", fep->delta_lambda); + CHECK(fep->delta_lambda > 0 && ((fep->init_fep_state > 0) || (fep->init_lambda > 0))); + + sprintf(err_buf, "Can't use postive delta-lambda (%g) with expanded ensemble simulations", fep->delta_lambda); + CHECK(fep->delta_lambda > 0 && (ir->efep == efepEXPANDED)); + + sprintf(err_buf, "Can only use expanded ensemble with md-vv for now; should be supported for other integrators in 5.0"); + CHECK(!(EI_VV(ir->eI)) && (ir->efep == efepEXPANDED)); + + sprintf(err_buf, "Free-energy not implemented for Ewald"); + CHECK(ir->coulombtype == eelEWALD); + + /* check validty of lambda inputs */ + if (fep->n_lambda == 0) + { + /* Clear output in case of no states:*/ + sprintf(err_buf, "init-lambda-state set to %d: no lambda states are defined.", fep->init_fep_state); + CHECK((fep->init_fep_state >= 0) && (fep->n_lambda == 0)); + } + else + { + sprintf(err_buf, "initial thermodynamic state %d does not exist, only goes to %d", fep->init_fep_state, fep->n_lambda-1); + CHECK((fep->init_fep_state >= fep->n_lambda)); + } + + sprintf(err_buf, "Lambda state must be set, either with init-lambda-state or with init-lambda"); + CHECK((fep->init_fep_state < 0) && (fep->init_lambda < 0)); + + sprintf(err_buf, "init-lambda=%g while init-lambda-state=%d. Lambda state must be set either with init-lambda-state or with init-lambda, but not both", + fep->init_lambda, fep->init_fep_state); + CHECK((fep->init_fep_state >= 0) && (fep->init_lambda >= 0)); + + + + if ((fep->init_lambda >= 0) && (fep->delta_lambda == 0)) + { + int n_lambda_terms; + n_lambda_terms = 0; + for (i = 0; i < efptNR; i++) + { + if (fep->separate_dvdl[i]) + { + n_lambda_terms++; + } + } + if (n_lambda_terms > 1) + { + sprintf(warn_buf, "If lambda vector states (fep-lambdas, coul-lambdas etc.) are set, don't use init-lambda to set lambda state (except for slow growth). Use init-lambda-state instead."); + warning(wi, warn_buf); + } + + if (n_lambda_terms < 2 && fep->n_lambda > 0) + { + warning_note(wi, + "init-lambda is deprecated for setting lambda state (except for slow growth). Use init-lambda-state instead."); + } + } + + for (j = 0; j < efptNR; j++) + { + for (i = 0; i < fep->n_lambda; i++) + { + sprintf(err_buf, "Entry %d for %s must be between 0 and 1, instead is %g", i, efpt_names[j], fep->all_lambda[j][i]); + CHECK((fep->all_lambda[j][i] < 0) || (fep->all_lambda[j][i] > 1)); + } + } + + if ((fep->sc_alpha > 0) && (!fep->bScCoul)) + { + for (i = 0; i < fep->n_lambda; i++) + { + sprintf(err_buf, "For state %d, vdw-lambdas (%f) is changing with vdw softcore, while coul-lambdas (%f) is nonzero without coulomb softcore: this will lead to crashes, and is not supported.", i, fep->all_lambda[efptVDW][i], + fep->all_lambda[efptCOUL][i]); + CHECK((fep->sc_alpha > 0) && + (((fep->all_lambda[efptCOUL][i] > 0.0) && + (fep->all_lambda[efptCOUL][i] < 1.0)) && + ((fep->all_lambda[efptVDW][i] > 0.0) && + (fep->all_lambda[efptVDW][i] < 1.0)))); + } + } + + if ((fep->bScCoul) && (EEL_PME(ir->coulombtype))) + { + real sigma, lambda, r_sc; + + sigma = 0.34; + /* Maximum estimate for A and B charges equal with lambda power 1 */ + lambda = 0.5; + r_sc = pow(lambda*fep->sc_alpha*pow(sigma/ir->rcoulomb, fep->sc_r_power) + 1.0, 1.0/fep->sc_r_power); + sprintf(warn_buf, "With PME there is a minor soft core effect present at the cut-off, proportional to (LJsigma/rcoulomb)^%g. This could have a minor effect on energy conservation, but usually other effects dominate. With a common sigma value of %g nm the fraction of the particle-particle potential at the cut-off at lambda=%g is around %.1e, while ewald-rtol is %.1e.", + fep->sc_r_power, + sigma, lambda, r_sc - 1.0, ir->ewald_rtol); + warning_note(wi, warn_buf); + } + + /* Free Energy Checks -- In an ideal world, slow growth and FEP would + be treated differently, but that's the next step */ + + for (i = 0; i < efptNR; i++) + { + for (j = 0; j < fep->n_lambda; j++) + { + sprintf(err_buf, "%s[%d] must be between 0 and 1", efpt_names[i], j); + CHECK((fep->all_lambda[i][j] < 0) || (fep->all_lambda[i][j] > 1)); + } + } + } + + if ((ir->bSimTemp) || (ir->efep == efepEXPANDED)) + { + fep = ir->fepvals; + expand = ir->expandedvals; + + /* checking equilibration of weights inputs for validity */ + + sprintf(err_buf, "weight-equil-number-all-lambda (%d) is ignored if lmc-weights-equil is not equal to %s", + expand->equil_n_at_lam, elmceq_names[elmceqNUMATLAM]); + CHECK((expand->equil_n_at_lam > 0) && (expand->elmceq != elmceqNUMATLAM)); + + sprintf(err_buf, "weight-equil-number-samples (%d) is ignored if lmc-weights-equil is not equal to %s", + expand->equil_samples, elmceq_names[elmceqSAMPLES]); + CHECK((expand->equil_samples > 0) && (expand->elmceq != elmceqSAMPLES)); + + sprintf(err_buf, "weight-equil-number-steps (%d) is ignored if lmc-weights-equil is not equal to %s", + expand->equil_steps, elmceq_names[elmceqSTEPS]); + CHECK((expand->equil_steps > 0) && (expand->elmceq != elmceqSTEPS)); + + sprintf(err_buf, "weight-equil-wl-delta (%d) is ignored if lmc-weights-equil is not equal to %s", + expand->equil_samples, elmceq_names[elmceqWLDELTA]); + CHECK((expand->equil_wl_delta > 0) && (expand->elmceq != elmceqWLDELTA)); + + sprintf(err_buf, "weight-equil-count-ratio (%f) is ignored if lmc-weights-equil is not equal to %s", + expand->equil_ratio, elmceq_names[elmceqRATIO]); + CHECK((expand->equil_ratio > 0) && (expand->elmceq != elmceqRATIO)); + + sprintf(err_buf, "weight-equil-number-all-lambda (%d) must be a positive integer if lmc-weights-equil=%s", + expand->equil_n_at_lam, elmceq_names[elmceqNUMATLAM]); + CHECK((expand->equil_n_at_lam <= 0) && (expand->elmceq == elmceqNUMATLAM)); + + sprintf(err_buf, "weight-equil-number-samples (%d) must be a positive integer if lmc-weights-equil=%s", + expand->equil_samples, elmceq_names[elmceqSAMPLES]); + CHECK((expand->equil_samples <= 0) && (expand->elmceq == elmceqSAMPLES)); + + sprintf(err_buf, "weight-equil-number-steps (%d) must be a positive integer if lmc-weights-equil=%s", + expand->equil_steps, elmceq_names[elmceqSTEPS]); + CHECK((expand->equil_steps <= 0) && (expand->elmceq == elmceqSTEPS)); + + sprintf(err_buf, "weight-equil-wl-delta (%f) must be > 0 if lmc-weights-equil=%s", + expand->equil_wl_delta, elmceq_names[elmceqWLDELTA]); + CHECK((expand->equil_wl_delta <= 0) && (expand->elmceq == elmceqWLDELTA)); + + sprintf(err_buf, "weight-equil-count-ratio (%f) must be > 0 if lmc-weights-equil=%s", + expand->equil_ratio, elmceq_names[elmceqRATIO]); + CHECK((expand->equil_ratio <= 0) && (expand->elmceq == elmceqRATIO)); + + sprintf(err_buf, "lmc-weights-equil=%s only possible when lmc-stats = %s or lmc-stats %s", + elmceq_names[elmceqWLDELTA], elamstats_names[elamstatsWL], elamstats_names[elamstatsWWL]); + CHECK((expand->elmceq == elmceqWLDELTA) && (!EWL(expand->elamstats))); + + sprintf(err_buf, "lmc-repeats (%d) must be greater than 0", expand->lmc_repeats); + CHECK((expand->lmc_repeats <= 0)); + sprintf(err_buf, "minimum-var-min (%d) must be greater than 0", expand->minvarmin); + CHECK((expand->minvarmin <= 0)); + sprintf(err_buf, "weight-c-range (%d) must be greater or equal to 0", expand->c_range); + CHECK((expand->c_range < 0)); + sprintf(err_buf, "init-lambda-state (%d) must be zero if lmc-forced-nstart (%d)> 0 and lmc-move != 'no'", + fep->init_fep_state, expand->lmc_forced_nstart); + CHECK((fep->init_fep_state != 0) && (expand->lmc_forced_nstart > 0) && (expand->elmcmove != elmcmoveNO)); + sprintf(err_buf, "lmc-forced-nstart (%d) must not be negative", expand->lmc_forced_nstart); + CHECK((expand->lmc_forced_nstart < 0)); + sprintf(err_buf, "init-lambda-state (%d) must be in the interval [0,number of lambdas)", fep->init_fep_state); + CHECK((fep->init_fep_state < 0) || (fep->init_fep_state >= fep->n_lambda)); + + sprintf(err_buf, "init-wl-delta (%f) must be greater than or equal to 0", expand->init_wl_delta); + CHECK((expand->init_wl_delta < 0)); + sprintf(err_buf, "wl-ratio (%f) must be between 0 and 1", expand->wl_ratio); + CHECK((expand->wl_ratio <= 0) || (expand->wl_ratio >= 1)); + sprintf(err_buf, "wl-scale (%f) must be between 0 and 1", expand->wl_scale); + CHECK((expand->wl_scale <= 0) || (expand->wl_scale >= 1)); + + /* if there is no temperature control, we need to specify an MC temperature */ + sprintf(err_buf, "If there is no temperature control, and lmc-mcmove!= 'no',mc_temperature must be set to a positive number"); + if (expand->nstTij > 0) + { + sprintf(err_buf, "nst-transition-matrix (%d) must be an integer multiple of nstlog (%d)", + expand->nstTij, ir->nstlog); + CHECK((mod(expand->nstTij, ir->nstlog) != 0)); + } + } + + /* PBC/WALLS */ + sprintf(err_buf, "walls only work with pbc=%s", epbc_names[epbcXY]); + CHECK(ir->nwall && ir->ePBC != epbcXY); + + /* VACUUM STUFF */ + if (ir->ePBC != epbcXYZ && ir->nwall != 2) + { + if (ir->ePBC == epbcNONE) + { + if (ir->epc != epcNO) + { + warning(wi, "Turning off pressure coupling for vacuum system"); + ir->epc = epcNO; + } + } + else + { + sprintf(err_buf, "Can not have pressure coupling with pbc=%s", + epbc_names[ir->ePBC]); + CHECK(ir->epc != epcNO); + } + sprintf(err_buf, "Can not have Ewald with pbc=%s", epbc_names[ir->ePBC]); + CHECK(EEL_FULL(ir->coulombtype)); + + sprintf(err_buf, "Can not have dispersion correction with pbc=%s", + epbc_names[ir->ePBC]); + CHECK(ir->eDispCorr != edispcNO); + } + + if (ir->rlist == 0.0) + { + sprintf(err_buf, "can only have neighborlist cut-off zero (=infinite)\n" + "with coulombtype = %s or coulombtype = %s\n" + "without periodic boundary conditions (pbc = %s) and\n" + "rcoulomb and rvdw set to zero", + eel_names[eelCUT], eel_names[eelUSER], epbc_names[epbcNONE]); + CHECK(((ir->coulombtype != eelCUT) && (ir->coulombtype != eelUSER)) || + (ir->ePBC != epbcNONE) || + (ir->rcoulomb != 0.0) || (ir->rvdw != 0.0)); + + if (ir->nstlist < 0) + { + warning_error(wi, "Can not have heuristic neighborlist updates without cut-off"); + } + if (ir->nstlist > 0) + { + warning_note(wi, "Simulating without cut-offs can be (slightly) faster with nstlist=0, nstype=simple and only one MPI rank"); + } + } + + /* COMM STUFF */ + if (ir->nstcomm == 0) + { + ir->comm_mode = ecmNO; + } + if (ir->comm_mode != ecmNO) + { + if (ir->nstcomm < 0) + { + warning(wi, "If you want to remove the rotation around the center of mass, you should set comm_mode = Angular instead of setting nstcomm < 0. nstcomm is modified to its absolute value"); + ir->nstcomm = abs(ir->nstcomm); + } + + if (ir->nstcalcenergy > 0 && ir->nstcomm < ir->nstcalcenergy) + { + warning_note(wi, "nstcomm < nstcalcenergy defeats the purpose of nstcalcenergy, setting nstcomm to nstcalcenergy"); + ir->nstcomm = ir->nstcalcenergy; + } + + if (ir->comm_mode == ecmANGULAR) + { + sprintf(err_buf, "Can not remove the rotation around the center of mass with periodic molecules"); + CHECK(ir->bPeriodicMols); + if (ir->ePBC != epbcNONE) + { + warning(wi, "Removing the rotation around the center of mass in a periodic system (this is not a problem when you have only one molecule)."); + } + } + } + + if (EI_STATE_VELOCITY(ir->eI) && ir->ePBC == epbcNONE && ir->comm_mode != ecmANGULAR) + { + warning_note(wi, "Tumbling and or flying ice-cubes: We are not removing rotation around center of mass in a non-periodic system. You should probably set comm_mode = ANGULAR."); + } + + sprintf(err_buf, "Twin-range neighbour searching (NS) with simple NS" + " algorithm not implemented"); + CHECK(((ir->rcoulomb > ir->rlist) || (ir->rvdw > ir->rlist)) + && (ir->ns_type == ensSIMPLE)); + + /* TEMPERATURE COUPLING */ + if (ir->etc == etcYES) + { + ir->etc = etcBERENDSEN; + warning_note(wi, "Old option for temperature coupling given: " + "changing \"yes\" to \"Berendsen\"\n"); + } + + if ((ir->etc == etcNOSEHOOVER) || (ir->epc == epcMTTK)) + { + if (ir->opts.nhchainlength < 1) + { + sprintf(warn_buf, "number of Nose-Hoover chains (currently %d) cannot be less than 1,reset to 1\n", ir->opts.nhchainlength); + ir->opts.nhchainlength = 1; + warning(wi, warn_buf); + } + + if (ir->etc == etcNOSEHOOVER && !EI_VV(ir->eI) && ir->opts.nhchainlength > 1) + { + warning_note(wi, "leapfrog does not yet support Nose-Hoover chains, nhchainlength reset to 1"); + ir->opts.nhchainlength = 1; + } + } + else + { + ir->opts.nhchainlength = 0; + } + + if (ir->eI == eiVVAK) + { + sprintf(err_buf, "%s implemented primarily for validation, and requires nsttcouple = 1 and nstpcouple = 1.", + ei_names[eiVVAK]); + CHECK((ir->nsttcouple != 1) || (ir->nstpcouple != 1)); + } + + if (ETC_ANDERSEN(ir->etc)) + { + sprintf(err_buf, "%s temperature control not supported for integrator %s.", etcoupl_names[ir->etc], ei_names[ir->eI]); + CHECK(!(EI_VV(ir->eI))); + + if (ir->nstcomm > 0 && (ir->etc == etcANDERSEN)) + { + sprintf(warn_buf, "Center of mass removal not necessary for %s. All velocities of coupled groups are rerandomized periodically, so flying ice cube errors will not occur.", etcoupl_names[ir->etc]); + warning_note(wi, warn_buf); + } + + sprintf(err_buf, "nstcomm must be 1, not %d for %s, as velocities of atoms in coupled groups are randomized every time step", ir->nstcomm, etcoupl_names[ir->etc]); + CHECK(ir->nstcomm > 1 && (ir->etc == etcANDERSEN)); + } + + if (ir->etc == etcBERENDSEN) + { + sprintf(warn_buf, "The %s thermostat does not generate the correct kinetic energy distribution. You might want to consider using the %s thermostat.", + ETCOUPLTYPE(ir->etc), ETCOUPLTYPE(etcVRESCALE)); + warning_note(wi, warn_buf); + } + + if ((ir->etc == etcNOSEHOOVER || ETC_ANDERSEN(ir->etc)) + && ir->epc == epcBERENDSEN) + { + sprintf(warn_buf, "Using Berendsen pressure coupling invalidates the " + "true ensemble for the thermostat"); + warning(wi, warn_buf); + } + + /* PRESSURE COUPLING */ + if (ir->epc == epcISOTROPIC) + { + ir->epc = epcBERENDSEN; + warning_note(wi, "Old option for pressure coupling given: " + "changing \"Isotropic\" to \"Berendsen\"\n"); + } + + if (ir->epc != epcNO) + { + dt_pcoupl = ir->nstpcouple*ir->delta_t; + + sprintf(err_buf, "tau-p must be > 0 instead of %g\n", ir->tau_p); + CHECK(ir->tau_p <= 0); + + if (ir->tau_p/dt_pcoupl < pcouple_min_integration_steps(ir->epc)) + { + sprintf(warn_buf, "For proper integration of the %s barostat, tau-p (%g) should be at least %d times larger than nstpcouple*dt (%g)", + EPCOUPLTYPE(ir->epc), ir->tau_p, pcouple_min_integration_steps(ir->epc), dt_pcoupl); + warning(wi, warn_buf); + } + + sprintf(err_buf, "compressibility must be > 0 when using pressure" + " coupling %s\n", EPCOUPLTYPE(ir->epc)); + CHECK(ir->compress[XX][XX] < 0 || ir->compress[YY][YY] < 0 || + ir->compress[ZZ][ZZ] < 0 || + (trace(ir->compress) == 0 && ir->compress[YY][XX] <= 0 && + ir->compress[ZZ][XX] <= 0 && ir->compress[ZZ][YY] <= 0)); + + if (epcPARRINELLORAHMAN == ir->epc && opts->bGenVel) + { + sprintf(warn_buf, + "You are generating velocities so I am assuming you " + "are equilibrating a system. You are using " + "%s pressure coupling, but this can be " + "unstable for equilibration. If your system crashes, try " + "equilibrating first with Berendsen pressure coupling. If " + "you are not equilibrating the system, you can probably " + "ignore this warning.", + epcoupl_names[ir->epc]); + warning(wi, warn_buf); + } + } + + if (EI_VV(ir->eI)) + { + if (ir->epc > epcNO) + { + if ((ir->epc != epcBERENDSEN) && (ir->epc != epcMTTK)) + { + warning_error(wi, "for md-vv and md-vv-avek, can only use Berendsen and Martyna-Tuckerman-Tobias-Klein (MTTK) equations for pressure control; MTTK is equivalent to Parrinello-Rahman."); + } + } + } + else + { + if (ir->epc == epcMTTK) + { + warning_error(wi, "MTTK pressure coupling requires a Velocity-verlet integrator"); + } + } + + /* ELECTROSTATICS */ + /* More checks are in triple check (grompp.c) */ + + if (ir->coulombtype == eelSWITCH) + { + sprintf(warn_buf, "coulombtype = %s is only for testing purposes and can lead to serious " + "artifacts, advice: use coulombtype = %s", + eel_names[ir->coulombtype], + eel_names[eelRF_ZERO]); + warning(wi, warn_buf); + } + + if (ir->epsilon_r != 1 && ir->implicit_solvent == eisGBSA) + { + sprintf(warn_buf, "epsilon-r = %g with GB implicit solvent, will use this value for inner dielectric", ir->epsilon_r); + warning_note(wi, warn_buf); + } + + if (EEL_RF(ir->coulombtype) && ir->epsilon_rf == 1 && ir->epsilon_r != 1) + { + sprintf(warn_buf, "epsilon-r = %g and epsilon-rf = 1 with reaction field, proceeding assuming old format and exchanging epsilon-r and epsilon-rf", ir->epsilon_r); + warning(wi, warn_buf); + ir->epsilon_rf = ir->epsilon_r; + ir->epsilon_r = 1.0; + } + + if (getenv("GALACTIC_DYNAMICS") == NULL) + { + sprintf(err_buf, "epsilon-r must be >= 0 instead of %g\n", ir->epsilon_r); + CHECK(ir->epsilon_r < 0); + } + + if (EEL_RF(ir->coulombtype)) + { + /* reaction field (at the cut-off) */ + + if (ir->coulombtype == eelRF_ZERO) + { + sprintf(warn_buf, "With coulombtype = %s, epsilon-rf must be 0, assuming you meant epsilon_rf=0", + eel_names[ir->coulombtype]); + CHECK(ir->epsilon_rf != 0); + ir->epsilon_rf = 0.0; + } + + sprintf(err_buf, "epsilon-rf must be >= epsilon-r"); + CHECK((ir->epsilon_rf < ir->epsilon_r && ir->epsilon_rf != 0) || + (ir->epsilon_r == 0)); + if (ir->epsilon_rf == ir->epsilon_r) + { + sprintf(warn_buf, "Using epsilon-rf = epsilon-r with %s does not make sense", + eel_names[ir->coulombtype]); + warning(wi, warn_buf); + } + } + /* Allow rlist>rcoulomb for tabulated long range stuff. This just + * means the interaction is zero outside rcoulomb, but it helps to + * provide accurate energy conservation. + */ + if (ir_coulomb_might_be_zero_at_cutoff(ir)) + { + if (ir_coulomb_switched(ir)) + { + sprintf(err_buf, + "With coulombtype = %s rcoulomb_switch must be < rcoulomb. Or, better: Use the potential modifier options!", + eel_names[ir->coulombtype]); + CHECK(ir->rcoulomb_switch >= ir->rcoulomb); + } + } + else if (ir->coulombtype == eelCUT || EEL_RF(ir->coulombtype)) + { + if (ir->cutoff_scheme == ecutsGROUP && ir->coulomb_modifier == eintmodNONE) + { + sprintf(err_buf, "With coulombtype = %s, rcoulomb should be >= rlist unless you use a potential modifier", + eel_names[ir->coulombtype]); + CHECK(ir->rlist > ir->rcoulomb); + } + } + + if (ir->coulombtype == eelSWITCH || ir->coulombtype == eelSHIFT || + ir->vdwtype == evdwSWITCH || ir->vdwtype == evdwSHIFT) + { + sprintf(warn_buf, + "The switch/shift interaction settings are just for compatibility; you will get better " + "performance from applying potential modifiers to your interactions!\n"); + warning_note(wi, warn_buf); + } + + if (ir->coulombtype == eelPMESWITCH || ir->coulomb_modifier == eintmodPOTSWITCH) + { + if (ir->rcoulomb_switch/ir->rcoulomb < 0.9499) + { + real percentage = 100*(ir->rcoulomb-ir->rcoulomb_switch)/ir->rcoulomb; + sprintf(warn_buf, "The switching range for should be 5%% or less (currently %.2f%% using a switching range of %4f-%4f) for accurate electrostatic energies, energy conservation will be good regardless, since ewald_rtol = %g.", + percentage, ir->rcoulomb_switch, ir->rcoulomb, ir->ewald_rtol); + warning(wi, warn_buf); + } + } + + if (ir->vdwtype == evdwSWITCH || ir->vdw_modifier == eintmodPOTSWITCH) + { + if (ir->rvdw_switch == 0) + { + sprintf(warn_buf, "rvdw-switch is equal 0 even though you are using a switched Lennard-Jones potential. This suggests it was not set in the mdp, which can lead to large energy errors. In GROMACS, 0.05 to 0.1 nm is often a reasonable vdw switching range."); + warning(wi, warn_buf); + } + } + + if (EEL_FULL(ir->coulombtype)) + { + if (ir->coulombtype == eelPMESWITCH || ir->coulombtype == eelPMEUSER || + ir->coulombtype == eelPMEUSERSWITCH) + { + sprintf(err_buf, "With coulombtype = %s, rcoulomb must be <= rlist", + eel_names[ir->coulombtype]); + CHECK(ir->rcoulomb > ir->rlist); + } + else if (ir->cutoff_scheme == ecutsGROUP && ir->coulomb_modifier == eintmodNONE) + { + if (ir->coulombtype == eelPME || ir->coulombtype == eelP3M_AD) + { + sprintf(err_buf, + "With coulombtype = %s (without modifier), rcoulomb must be equal to rlist,\n" + "or rlistlong if nstcalclr=1. For optimal energy conservation,consider using\n" + "a potential modifier.", eel_names[ir->coulombtype]); + if (ir->nstcalclr == 1) + { + CHECK(ir->rcoulomb != ir->rlist && ir->rcoulomb != ir->rlistlong); + } + else + { + CHECK(ir->rcoulomb != ir->rlist); + } + } + } + } + + if (EEL_PME(ir->coulombtype) || EVDW_PME(ir->vdwtype)) + { + if (ir->pme_order < 3) + { + warning_error(wi, "pme-order can not be smaller than 3"); + } + } + + if (ir->nwall == 2 && EEL_FULL(ir->coulombtype)) + { + if (ir->ewald_geometry == eewg3D) + { + sprintf(warn_buf, "With pbc=%s you should use ewald-geometry=%s", + epbc_names[ir->ePBC], eewg_names[eewg3DC]); + warning(wi, warn_buf); + } + /* This check avoids extra pbc coding for exclusion corrections */ + sprintf(err_buf, "wall-ewald-zfac should be >= 2"); + CHECK(ir->wall_ewald_zfac < 2); + } + + if (ir_vdw_switched(ir)) + { + sprintf(err_buf, "With switched vdw forces or potentials, rvdw-switch must be < rvdw"); + CHECK(ir->rvdw_switch >= ir->rvdw); + + if (ir->rvdw_switch < 0.5*ir->rvdw) + { + sprintf(warn_buf, "You are applying a switch function to vdw forces or potentials from %g to %g nm, which is more than half the interaction range, whereas switch functions are intended to act only close to the cut-off.", + ir->rvdw_switch, ir->rvdw); + warning_note(wi, warn_buf); + } + } + else if (ir->vdwtype == evdwCUT || ir->vdwtype == evdwPME) + { + if (ir->cutoff_scheme == ecutsGROUP && ir->vdw_modifier == eintmodNONE) + { + sprintf(err_buf, "With vdwtype = %s, rvdw must be >= rlist unless you use a potential modifier", evdw_names[ir->vdwtype]); + CHECK(ir->rlist > ir->rvdw); + } + } + + if (ir->vdwtype == evdwPME) + { + if (!(ir->vdw_modifier == eintmodNONE || ir->vdw_modifier == eintmodPOTSHIFT)) + { + sprintf(err_buf, "With vdwtype = %s, the only supported modifiers are %s a\ +nd %s", + evdw_names[ir->vdwtype], + eintmod_names[eintmodPOTSHIFT], + eintmod_names[eintmodNONE]); + } + } + + if (ir->cutoff_scheme == ecutsGROUP) + { + if (((ir->coulomb_modifier != eintmodNONE && ir->rcoulomb == ir->rlist) || + (ir->vdw_modifier != eintmodNONE && ir->rvdw == ir->rlist)) && + ir->nstlist != 1) + { + warning_note(wi, "With exact cut-offs, rlist should be " + "larger than rcoulomb and rvdw, so that there " + "is a buffer region for particle motion " + "between neighborsearch steps"); + } + + if (ir_coulomb_is_zero_at_cutoff(ir) && ir->rlistlong <= ir->rcoulomb) + { + sprintf(warn_buf, "For energy conservation with switch/shift potentials, %s should be 0.1 to 0.3 nm larger than rcoulomb.", + IR_TWINRANGE(*ir) ? "rlistlong" : "rlist"); + warning_note(wi, warn_buf); + } + if (ir_vdw_switched(ir) && (ir->rlistlong <= ir->rvdw)) + { + sprintf(warn_buf, "For energy conservation with switch/shift potentials, %s should be 0.1 to 0.3 nm larger than rvdw.", + IR_TWINRANGE(*ir) ? "rlistlong" : "rlist"); + warning_note(wi, warn_buf); + } + } + + if (ir->vdwtype == evdwUSER && ir->eDispCorr != edispcNO) + { + warning_note(wi, "You have selected user tables with dispersion correction, the dispersion will be corrected to -C6/r^6 beyond rvdw_switch (the tabulated interaction between rvdw_switch and rvdw will not be double counted). Make sure that you really want dispersion correction to -C6/r^6."); + } + + if (ir->nstlist == -1) + { + sprintf(err_buf, "With nstlist=-1 rvdw and rcoulomb should be smaller than rlist to account for diffusion and possibly charge-group radii"); + CHECK(ir->rvdw >= ir->rlist || ir->rcoulomb >= ir->rlist); + } + sprintf(err_buf, "nstlist can not be smaller than -1"); + CHECK(ir->nstlist < -1); + + if (ir->eI == eiLBFGS && (ir->coulombtype == eelCUT || ir->vdwtype == evdwCUT) + && ir->rvdw != 0) + { + warning(wi, "For efficient BFGS minimization, use switch/shift/pme instead of cut-off."); + } + + if (ir->eI == eiLBFGS && ir->nbfgscorr <= 0) + { + warning(wi, "Using L-BFGS with nbfgscorr<=0 just gets you steepest descent."); + } + + /* ENERGY CONSERVATION */ + if (ir_NVE(ir) && ir->cutoff_scheme == ecutsGROUP) + { + if (!ir_vdw_might_be_zero_at_cutoff(ir) && ir->rvdw > 0 && ir->vdw_modifier == eintmodNONE) + { + sprintf(warn_buf, "You are using a cut-off for VdW interactions with NVE, for good energy conservation use vdwtype = %s (possibly with DispCorr)", + evdw_names[evdwSHIFT]); + warning_note(wi, warn_buf); + } + if (!ir_coulomb_might_be_zero_at_cutoff(ir) && ir->rcoulomb > 0) + { + sprintf(warn_buf, "You are using a cut-off for electrostatics with NVE, for good energy conservation use coulombtype = %s or %s", + eel_names[eelPMESWITCH], eel_names[eelRF_ZERO]); + warning_note(wi, warn_buf); + } + } + ++ if (EI_VV(ir->eI) && IR_TWINRANGE(*ir) && ir->nstlist > 1) ++ { ++ sprintf(warn_buf, "Twin-range multiple time stepping does not work with integrator %s.", ei_names[ir->eI]); ++ warning_error(wi, warn_buf); ++ } ++ + /* IMPLICIT SOLVENT */ + if (ir->coulombtype == eelGB_NOTUSED) + { + ir->coulombtype = eelCUT; + ir->implicit_solvent = eisGBSA; + fprintf(stderr, "Note: Old option for generalized born electrostatics given:\n" + "Changing coulombtype from \"generalized-born\" to \"cut-off\" and instead\n" + "setting implicit-solvent value to \"GBSA\" in input section.\n"); + } + + if (ir->sa_algorithm == esaSTILL) + { + sprintf(err_buf, "Still SA algorithm not available yet, use %s or %s instead\n", esa_names[esaAPPROX], esa_names[esaNO]); + CHECK(ir->sa_algorithm == esaSTILL); + } + + if (ir->implicit_solvent == eisGBSA) + { + sprintf(err_buf, "With GBSA implicit solvent, rgbradii must be equal to rlist."); + CHECK(ir->rgbradii != ir->rlist); + + if (ir->coulombtype != eelCUT) + { + sprintf(err_buf, "With GBSA, coulombtype must be equal to %s\n", eel_names[eelCUT]); + CHECK(ir->coulombtype != eelCUT); + } + if (ir->vdwtype != evdwCUT) + { + sprintf(err_buf, "With GBSA, vdw-type must be equal to %s\n", evdw_names[evdwCUT]); + CHECK(ir->vdwtype != evdwCUT); + } + if (ir->nstgbradii < 1) + { + sprintf(warn_buf, "Using GBSA with nstgbradii<1, setting nstgbradii=1"); + warning_note(wi, warn_buf); + ir->nstgbradii = 1; + } + if (ir->sa_algorithm == esaNO) + { + sprintf(warn_buf, "No SA (non-polar) calculation requested together with GB. Are you sure this is what you want?\n"); + warning_note(wi, warn_buf); + } + if (ir->sa_surface_tension < 0 && ir->sa_algorithm != esaNO) + { + sprintf(warn_buf, "Value of sa_surface_tension is < 0. Changing it to 2.05016 or 2.25936 kJ/nm^2/mol for Still and HCT/OBC respectively\n"); + warning_note(wi, warn_buf); + + if (ir->gb_algorithm == egbSTILL) + { + ir->sa_surface_tension = 0.0049 * CAL2JOULE * 100; + } + else + { + ir->sa_surface_tension = 0.0054 * CAL2JOULE * 100; + } + } + if (ir->sa_surface_tension == 0 && ir->sa_algorithm != esaNO) + { + sprintf(err_buf, "Surface tension set to 0 while SA-calculation requested\n"); + CHECK(ir->sa_surface_tension == 0 && ir->sa_algorithm != esaNO); + } + + } + + if (ir->bAdress) + { + if (ir->cutoff_scheme != ecutsGROUP) + { + warning_error(wi, "AdresS simulation supports only cutoff-scheme=group"); + } + if (!EI_SD(ir->eI)) + { + warning_error(wi, "AdresS simulation supports only stochastic dynamics"); + } + if (ir->epc != epcNO) + { + warning_error(wi, "AdresS simulation does not support pressure coupling"); + } + if (EEL_FULL(ir->coulombtype)) + { + warning_error(wi, "AdresS simulation does not support long-range electrostatics"); + } + } +} + +/* count the number of text elemets separated by whitespace in a string. + str = the input string + maxptr = the maximum number of allowed elements + ptr = the output array of pointers to the first character of each element + returns: the number of elements. */ +int str_nelem(const char *str, int maxptr, char *ptr[]) +{ + int np = 0; + char *copy0, *copy; + + copy0 = strdup(str); + copy = copy0; + ltrim(copy); + while (*copy != '\0') + { + if (np >= maxptr) + { + gmx_fatal(FARGS, "Too many groups on line: '%s' (max is %d)", + str, maxptr); + } + if (ptr) + { + ptr[np] = copy; + } + np++; + while ((*copy != '\0') && !isspace(*copy)) + { + copy++; + } + if (*copy != '\0') + { + *copy = '\0'; + copy++; + } + ltrim(copy); + } + if (ptr == NULL) + { + sfree(copy0); + } + + return np; +} + +/* interpret a number of doubles from a string and put them in an array, + after allocating space for them. + str = the input string + n = the (pre-allocated) number of doubles read + r = the output array of doubles. */ +static void parse_n_real(char *str, int *n, real **r) +{ + char *ptr[MAXPTR]; + int i; + + *n = str_nelem(str, MAXPTR, ptr); + + snew(*r, *n); + for (i = 0; i < *n; i++) + { + (*r)[i] = strtod(ptr[i], NULL); + } +} + +static void do_fep_params(t_inputrec *ir, char fep_lambda[][STRLEN], char weights[STRLEN]) +{ + + int i, j, max_n_lambda, nweights, nfep[efptNR]; + t_lambda *fep = ir->fepvals; + t_expanded *expand = ir->expandedvals; + real **count_fep_lambdas; + gmx_bool bOneLambda = TRUE; + + snew(count_fep_lambdas, efptNR); + + /* FEP input processing */ + /* first, identify the number of lambda values for each type. + All that are nonzero must have the same number */ + + for (i = 0; i < efptNR; i++) + { + parse_n_real(fep_lambda[i], &(nfep[i]), &(count_fep_lambdas[i])); + } + + /* now, determine the number of components. All must be either zero, or equal. */ + + max_n_lambda = 0; + for (i = 0; i < efptNR; i++) + { + if (nfep[i] > max_n_lambda) + { + max_n_lambda = nfep[i]; /* here's a nonzero one. All of them + must have the same number if its not zero.*/ + break; + } + } + + for (i = 0; i < efptNR; i++) + { + if (nfep[i] == 0) + { + ir->fepvals->separate_dvdl[i] = FALSE; + } + else if (nfep[i] == max_n_lambda) + { + if (i != efptTEMPERATURE) /* we treat this differently -- not really a reason to compute the derivative with + respect to the temperature currently */ + { + ir->fepvals->separate_dvdl[i] = TRUE; + } + } + else + { + gmx_fatal(FARGS, "Number of lambdas (%d) for FEP type %s not equal to number of other types (%d)", + nfep[i], efpt_names[i], max_n_lambda); + } + } + /* we don't print out dhdl if the temperature is changing, since we can't correctly define dhdl in this case */ + ir->fepvals->separate_dvdl[efptTEMPERATURE] = FALSE; + + /* the number of lambdas is the number we've read in, which is either zero + or the same for all */ + fep->n_lambda = max_n_lambda; + + /* allocate space for the array of lambda values */ + snew(fep->all_lambda, efptNR); + /* if init_lambda is defined, we need to set lambda */ + if ((fep->init_lambda > 0) && (fep->n_lambda == 0)) + { + ir->fepvals->separate_dvdl[efptFEP] = TRUE; + } + /* otherwise allocate the space for all of the lambdas, and transfer the data */ + for (i = 0; i < efptNR; i++) + { + snew(fep->all_lambda[i], fep->n_lambda); + if (nfep[i] > 0) /* if it's zero, then the count_fep_lambda arrays + are zero */ + { + for (j = 0; j < fep->n_lambda; j++) + { + fep->all_lambda[i][j] = (double)count_fep_lambdas[i][j]; + } + sfree(count_fep_lambdas[i]); + } + } + sfree(count_fep_lambdas); + + /* "fep-vals" is either zero or the full number. If zero, we'll need to define fep-lambdas for internal + bookkeeping -- for now, init_lambda */ + + if ((nfep[efptFEP] == 0) && (fep->init_lambda >= 0)) + { + for (i = 0; i < fep->n_lambda; i++) + { + fep->all_lambda[efptFEP][i] = fep->init_lambda; + } + } + + /* check to see if only a single component lambda is defined, and soft core is defined. + In this case, turn on coulomb soft core */ + + if (max_n_lambda == 0) + { + bOneLambda = TRUE; + } + else + { + for (i = 0; i < efptNR; i++) + { + if ((nfep[i] != 0) && (i != efptFEP)) + { + bOneLambda = FALSE; + } + } + } + if ((bOneLambda) && (fep->sc_alpha > 0)) + { + fep->bScCoul = TRUE; + } + + /* Fill in the others with the efptFEP if they are not explicitly + specified (i.e. nfep[i] == 0). This means if fep is not defined, + they are all zero. */ + + for (i = 0; i < efptNR; i++) + { + if ((nfep[i] == 0) && (i != efptFEP)) + { + for (j = 0; j < fep->n_lambda; j++) + { + fep->all_lambda[i][j] = fep->all_lambda[efptFEP][j]; + } + } + } + + + /* make it easier if sc_r_power = 48 by increasing it to the 4th power, to be in the right scale. */ + if (fep->sc_r_power == 48) + { + if (fep->sc_alpha > 0.1) + { + gmx_fatal(FARGS, "sc_alpha (%f) for sc_r_power = 48 should usually be between 0.001 and 0.004", fep->sc_alpha); + } + } + + expand = ir->expandedvals; + /* now read in the weights */ + parse_n_real(weights, &nweights, &(expand->init_lambda_weights)); + if (nweights == 0) + { + snew(expand->init_lambda_weights, fep->n_lambda); /* initialize to zero */ + } + else if (nweights != fep->n_lambda) + { + gmx_fatal(FARGS, "Number of weights (%d) is not equal to number of lambda values (%d)", + nweights, fep->n_lambda); + } + if ((expand->nstexpanded < 0) && (ir->efep != efepNO)) + { + expand->nstexpanded = fep->nstdhdl; + /* if you don't specify nstexpanded when doing expanded ensemble free energy calcs, it is set to nstdhdl */ + } + if ((expand->nstexpanded < 0) && ir->bSimTemp) + { + expand->nstexpanded = 2*(int)(ir->opts.tau_t[0]/ir->delta_t); + /* if you don't specify nstexpanded when doing expanded ensemble simulated tempering, it is set to + 2*tau_t just to be careful so it's not to frequent */ + } +} + + +static void do_simtemp_params(t_inputrec *ir) +{ + + snew(ir->simtempvals->temperatures, ir->fepvals->n_lambda); + GetSimTemps(ir->fepvals->n_lambda, ir->simtempvals, ir->fepvals->all_lambda[efptTEMPERATURE]); + + return; +} + +static void do_wall_params(t_inputrec *ir, + char *wall_atomtype, char *wall_density, + t_gromppopts *opts) +{ + int nstr, i; + char *names[MAXPTR]; + double dbl; + + opts->wall_atomtype[0] = NULL; + opts->wall_atomtype[1] = NULL; + + ir->wall_atomtype[0] = -1; + ir->wall_atomtype[1] = -1; + ir->wall_density[0] = 0; + ir->wall_density[1] = 0; + + if (ir->nwall > 0) + { + nstr = str_nelem(wall_atomtype, MAXPTR, names); + if (nstr != ir->nwall) + { + gmx_fatal(FARGS, "Expected %d elements for wall_atomtype, found %d", + ir->nwall, nstr); + } + for (i = 0; i < ir->nwall; i++) + { + opts->wall_atomtype[i] = strdup(names[i]); + } + + if (ir->wall_type == ewt93 || ir->wall_type == ewt104) + { + nstr = str_nelem(wall_density, MAXPTR, names); + if (nstr != ir->nwall) + { + gmx_fatal(FARGS, "Expected %d elements for wall-density, found %d", ir->nwall, nstr); + } + for (i = 0; i < ir->nwall; i++) + { + sscanf(names[i], "%lf", &dbl); + if (dbl <= 0) + { + gmx_fatal(FARGS, "wall-density[%d] = %f\n", i, dbl); + } + ir->wall_density[i] = dbl; + } + } + } +} + +static void add_wall_energrps(gmx_groups_t *groups, int nwall, t_symtab *symtab) +{ + int i; + t_grps *grps; + char str[STRLEN]; + + if (nwall > 0) + { + srenew(groups->grpname, groups->ngrpname+nwall); + grps = &(groups->grps[egcENER]); + srenew(grps->nm_ind, grps->nr+nwall); + for (i = 0; i < nwall; i++) + { + sprintf(str, "wall%d", i); + groups->grpname[groups->ngrpname] = put_symtab(symtab, str); + grps->nm_ind[grps->nr++] = groups->ngrpname++; + } + } +} + +void read_expandedparams(int *ninp_p, t_inpfile **inp_p, + t_expanded *expand, warninp_t wi) +{ + int ninp, nerror = 0; + t_inpfile *inp; + + ninp = *ninp_p; + inp = *inp_p; + + /* read expanded ensemble parameters */ + CCTYPE ("expanded ensemble variables"); + ITYPE ("nstexpanded", expand->nstexpanded, -1); + EETYPE("lmc-stats", expand->elamstats, elamstats_names); + EETYPE("lmc-move", expand->elmcmove, elmcmove_names); + EETYPE("lmc-weights-equil", expand->elmceq, elmceq_names); + ITYPE ("weight-equil-number-all-lambda", expand->equil_n_at_lam, -1); + ITYPE ("weight-equil-number-samples", expand->equil_samples, -1); + ITYPE ("weight-equil-number-steps", expand->equil_steps, -1); + RTYPE ("weight-equil-wl-delta", expand->equil_wl_delta, -1); + RTYPE ("weight-equil-count-ratio", expand->equil_ratio, -1); + CCTYPE("Seed for Monte Carlo in lambda space"); + ITYPE ("lmc-seed", expand->lmc_seed, -1); + RTYPE ("mc-temperature", expand->mc_temp, -1); + ITYPE ("lmc-repeats", expand->lmc_repeats, 1); + ITYPE ("lmc-gibbsdelta", expand->gibbsdeltalam, -1); + ITYPE ("lmc-forced-nstart", expand->lmc_forced_nstart, 0); + EETYPE("symmetrized-transition-matrix", expand->bSymmetrizedTMatrix, yesno_names); + ITYPE("nst-transition-matrix", expand->nstTij, -1); + ITYPE ("mininum-var-min", expand->minvarmin, 100); /*default is reasonable */ + ITYPE ("weight-c-range", expand->c_range, 0); /* default is just C=0 */ + RTYPE ("wl-scale", expand->wl_scale, 0.8); + RTYPE ("wl-ratio", expand->wl_ratio, 0.8); + RTYPE ("init-wl-delta", expand->init_wl_delta, 1.0); + EETYPE("wl-oneovert", expand->bWLoneovert, yesno_names); + + *ninp_p = ninp; + *inp_p = inp; + + return; +} + +void get_ir(const char *mdparin, const char *mdparout, + t_inputrec *ir, t_gromppopts *opts, + warninp_t wi) +{ + char *dumstr[2]; + double dumdub[2][6]; + t_inpfile *inp; + const char *tmp; + int i, j, m, ninp; + char warn_buf[STRLEN]; + t_lambda *fep = ir->fepvals; + t_expanded *expand = ir->expandedvals; + + init_inputrec_strings(); + inp = read_inpfile(mdparin, &ninp, wi); + + snew(dumstr[0], STRLEN); + snew(dumstr[1], STRLEN); + + if (-1 == search_einp(ninp, inp, "cutoff-scheme")) + { + sprintf(warn_buf, + "%s did not specify a value for the .mdp option " + "\"cutoff-scheme\". Probably it was first intended for use " + "with GROMACS before 4.6. In 4.6, the Verlet scheme was " + "introduced, but the group scheme was still the default. " + "The default is now the Verlet scheme, so you will observe " + "different behaviour.", mdparin); + warning_note(wi, warn_buf); + } + + /* remove the following deprecated commands */ + REM_TYPE("title"); + REM_TYPE("cpp"); + REM_TYPE("domain-decomposition"); + REM_TYPE("andersen-seed"); + REM_TYPE("dihre"); + REM_TYPE("dihre-fc"); + REM_TYPE("dihre-tau"); + REM_TYPE("nstdihreout"); + REM_TYPE("nstcheckpoint"); + + /* replace the following commands with the clearer new versions*/ + REPL_TYPE("unconstrained-start", "continuation"); + REPL_TYPE("foreign-lambda", "fep-lambdas"); + REPL_TYPE("verlet-buffer-drift", "verlet-buffer-tolerance"); + REPL_TYPE("nstxtcout", "nstxout-compressed"); + REPL_TYPE("xtc-grps", "compressed-x-grps"); + REPL_TYPE("xtc-precision", "compressed-x-precision"); + + CCTYPE ("VARIOUS PREPROCESSING OPTIONS"); + CTYPE ("Preprocessor information: use cpp syntax."); + CTYPE ("e.g.: -I/home/joe/doe -I/home/mary/roe"); + STYPE ("include", opts->include, NULL); + CTYPE ("e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)"); + STYPE ("define", opts->define, NULL); + + CCTYPE ("RUN CONTROL PARAMETERS"); + EETYPE("integrator", ir->eI, ei_names); + CTYPE ("Start time and timestep in ps"); + RTYPE ("tinit", ir->init_t, 0.0); + RTYPE ("dt", ir->delta_t, 0.001); + STEPTYPE ("nsteps", ir->nsteps, 0); + CTYPE ("For exact run continuation or redoing part of a run"); + STEPTYPE ("init-step", ir->init_step, 0); + CTYPE ("Part index is updated automatically on checkpointing (keeps files separate)"); + ITYPE ("simulation-part", ir->simulation_part, 1); + CTYPE ("mode for center of mass motion removal"); + EETYPE("comm-mode", ir->comm_mode, ecm_names); + CTYPE ("number of steps for center of mass motion removal"); + ITYPE ("nstcomm", ir->nstcomm, 100); + CTYPE ("group(s) for center of mass motion removal"); + STYPE ("comm-grps", is->vcm, NULL); + + CCTYPE ("LANGEVIN DYNAMICS OPTIONS"); + CTYPE ("Friction coefficient (amu/ps) and random seed"); + RTYPE ("bd-fric", ir->bd_fric, 0.0); + STEPTYPE ("ld-seed", ir->ld_seed, -1); + + /* Em stuff */ + CCTYPE ("ENERGY MINIMIZATION OPTIONS"); + CTYPE ("Force tolerance and initial step-size"); + RTYPE ("emtol", ir->em_tol, 10.0); + RTYPE ("emstep", ir->em_stepsize, 0.01); + CTYPE ("Max number of iterations in relax-shells"); + ITYPE ("niter", ir->niter, 20); + CTYPE ("Step size (ps^2) for minimization of flexible constraints"); + RTYPE ("fcstep", ir->fc_stepsize, 0); + CTYPE ("Frequency of steepest descents steps when doing CG"); + ITYPE ("nstcgsteep", ir->nstcgsteep, 1000); + ITYPE ("nbfgscorr", ir->nbfgscorr, 10); + + CCTYPE ("TEST PARTICLE INSERTION OPTIONS"); + RTYPE ("rtpi", ir->rtpi, 0.05); + + /* Output options */ + CCTYPE ("OUTPUT CONTROL OPTIONS"); + CTYPE ("Output frequency for coords (x), velocities (v) and forces (f)"); + ITYPE ("nstxout", ir->nstxout, 0); + ITYPE ("nstvout", ir->nstvout, 0); + ITYPE ("nstfout", ir->nstfout, 0); + ir->nstcheckpoint = 1000; + CTYPE ("Output frequency for energies to log file and energy file"); + ITYPE ("nstlog", ir->nstlog, 1000); + ITYPE ("nstcalcenergy", ir->nstcalcenergy, 100); + ITYPE ("nstenergy", ir->nstenergy, 1000); + CTYPE ("Output frequency and precision for .xtc file"); + ITYPE ("nstxout-compressed", ir->nstxout_compressed, 0); + RTYPE ("compressed-x-precision", ir->x_compression_precision, 1000.0); + CTYPE ("This selects the subset of atoms for the compressed"); + CTYPE ("trajectory file. You can select multiple groups. By"); + CTYPE ("default, all atoms will be written."); + STYPE ("compressed-x-grps", is->x_compressed_groups, NULL); + CTYPE ("Selection of energy groups"); + STYPE ("energygrps", is->energy, NULL); + + /* Neighbor searching */ + CCTYPE ("NEIGHBORSEARCHING PARAMETERS"); + CTYPE ("cut-off scheme (Verlet: particle based cut-offs, group: using charge groups)"); + EETYPE("cutoff-scheme", ir->cutoff_scheme, ecutscheme_names); + CTYPE ("nblist update frequency"); + ITYPE ("nstlist", ir->nstlist, 10); + CTYPE ("ns algorithm (simple or grid)"); + EETYPE("ns-type", ir->ns_type, ens_names); + /* set ndelta to the optimal value of 2 */ + ir->ndelta = 2; + CTYPE ("Periodic boundary conditions: xyz, no, xy"); + EETYPE("pbc", ir->ePBC, epbc_names); + EETYPE("periodic-molecules", ir->bPeriodicMols, yesno_names); + CTYPE ("Allowed energy error due to the Verlet buffer in kJ/mol/ps per atom,"); + CTYPE ("a value of -1 means: use rlist"); + RTYPE("verlet-buffer-tolerance", ir->verletbuf_tol, 0.005); + CTYPE ("nblist cut-off"); + RTYPE ("rlist", ir->rlist, 1.0); + CTYPE ("long-range cut-off for switched potentials"); + RTYPE ("rlistlong", ir->rlistlong, -1); + ITYPE ("nstcalclr", ir->nstcalclr, -1); + + /* Electrostatics */ + CCTYPE ("OPTIONS FOR ELECTROSTATICS AND VDW"); + CTYPE ("Method for doing electrostatics"); + EETYPE("coulombtype", ir->coulombtype, eel_names); + EETYPE("coulomb-modifier", ir->coulomb_modifier, eintmod_names); + CTYPE ("cut-off lengths"); + RTYPE ("rcoulomb-switch", ir->rcoulomb_switch, 0.0); + RTYPE ("rcoulomb", ir->rcoulomb, 1.0); + CTYPE ("Relative dielectric constant for the medium and the reaction field"); + RTYPE ("epsilon-r", ir->epsilon_r, 1.0); + RTYPE ("epsilon-rf", ir->epsilon_rf, 0.0); + CTYPE ("Method for doing Van der Waals"); + EETYPE("vdw-type", ir->vdwtype, evdw_names); + EETYPE("vdw-modifier", ir->vdw_modifier, eintmod_names); + CTYPE ("cut-off lengths"); + RTYPE ("rvdw-switch", ir->rvdw_switch, 0.0); + RTYPE ("rvdw", ir->rvdw, 1.0); + CTYPE ("Apply long range dispersion corrections for Energy and Pressure"); + EETYPE("DispCorr", ir->eDispCorr, edispc_names); + CTYPE ("Extension of the potential lookup tables beyond the cut-off"); + RTYPE ("table-extension", ir->tabext, 1.0); + CTYPE ("Separate tables between energy group pairs"); + STYPE ("energygrp-table", is->egptable, NULL); + CTYPE ("Spacing for the PME/PPPM FFT grid"); + RTYPE ("fourierspacing", ir->fourier_spacing, 0.12); + CTYPE ("FFT grid size, when a value is 0 fourierspacing will be used"); + ITYPE ("fourier-nx", ir->nkx, 0); + ITYPE ("fourier-ny", ir->nky, 0); + ITYPE ("fourier-nz", ir->nkz, 0); + CTYPE ("EWALD/PME/PPPM parameters"); + ITYPE ("pme-order", ir->pme_order, 4); + RTYPE ("ewald-rtol", ir->ewald_rtol, 0.00001); + RTYPE ("ewald-rtol-lj", ir->ewald_rtol_lj, 0.001); + EETYPE("lj-pme-comb-rule", ir->ljpme_combination_rule, eljpme_names); + EETYPE("ewald-geometry", ir->ewald_geometry, eewg_names); + RTYPE ("epsilon-surface", ir->epsilon_surface, 0.0); + EETYPE("optimize-fft", ir->bOptFFT, yesno_names); + + CCTYPE("IMPLICIT SOLVENT ALGORITHM"); + EETYPE("implicit-solvent", ir->implicit_solvent, eis_names); + + CCTYPE ("GENERALIZED BORN ELECTROSTATICS"); + CTYPE ("Algorithm for calculating Born radii"); + EETYPE("gb-algorithm", ir->gb_algorithm, egb_names); + CTYPE ("Frequency of calculating the Born radii inside rlist"); + ITYPE ("nstgbradii", ir->nstgbradii, 1); + CTYPE ("Cutoff for Born radii calculation; the contribution from atoms"); + CTYPE ("between rlist and rgbradii is updated every nstlist steps"); + RTYPE ("rgbradii", ir->rgbradii, 1.0); + CTYPE ("Dielectric coefficient of the implicit solvent"); + RTYPE ("gb-epsilon-solvent", ir->gb_epsilon_solvent, 80.0); + CTYPE ("Salt concentration in M for Generalized Born models"); + RTYPE ("gb-saltconc", ir->gb_saltconc, 0.0); + CTYPE ("Scaling factors used in the OBC GB model. Default values are OBC(II)"); + RTYPE ("gb-obc-alpha", ir->gb_obc_alpha, 1.0); + RTYPE ("gb-obc-beta", ir->gb_obc_beta, 0.8); + RTYPE ("gb-obc-gamma", ir->gb_obc_gamma, 4.85); + RTYPE ("gb-dielectric-offset", ir->gb_dielectric_offset, 0.009); + EETYPE("sa-algorithm", ir->sa_algorithm, esa_names); + CTYPE ("Surface tension (kJ/mol/nm^2) for the SA (nonpolar surface) part of GBSA"); + CTYPE ("The value -1 will set default value for Still/HCT/OBC GB-models."); + RTYPE ("sa-surface-tension", ir->sa_surface_tension, -1); + + /* Coupling stuff */ + CCTYPE ("OPTIONS FOR WEAK COUPLING ALGORITHMS"); + CTYPE ("Temperature coupling"); + EETYPE("tcoupl", ir->etc, etcoupl_names); + ITYPE ("nsttcouple", ir->nsttcouple, -1); + ITYPE("nh-chain-length", ir->opts.nhchainlength, 10); + EETYPE("print-nose-hoover-chain-variables", ir->bPrintNHChains, yesno_names); + CTYPE ("Groups to couple separately"); + STYPE ("tc-grps", is->tcgrps, NULL); + CTYPE ("Time constant (ps) and reference temperature (K)"); + STYPE ("tau-t", is->tau_t, NULL); + STYPE ("ref-t", is->ref_t, NULL); + CTYPE ("pressure coupling"); + EETYPE("pcoupl", ir->epc, epcoupl_names); + EETYPE("pcoupltype", ir->epct, epcoupltype_names); + ITYPE ("nstpcouple", ir->nstpcouple, -1); + CTYPE ("Time constant (ps), compressibility (1/bar) and reference P (bar)"); + RTYPE ("tau-p", ir->tau_p, 1.0); + STYPE ("compressibility", dumstr[0], NULL); + STYPE ("ref-p", dumstr[1], NULL); + CTYPE ("Scaling of reference coordinates, No, All or COM"); + EETYPE ("refcoord-scaling", ir->refcoord_scaling, erefscaling_names); + + /* QMMM */ + CCTYPE ("OPTIONS FOR QMMM calculations"); + EETYPE("QMMM", ir->bQMMM, yesno_names); + CTYPE ("Groups treated Quantum Mechanically"); + STYPE ("QMMM-grps", is->QMMM, NULL); + CTYPE ("QM method"); + STYPE("QMmethod", is->QMmethod, NULL); + CTYPE ("QMMM scheme"); + EETYPE("QMMMscheme", ir->QMMMscheme, eQMMMscheme_names); + CTYPE ("QM basisset"); + STYPE("QMbasis", is->QMbasis, NULL); + CTYPE ("QM charge"); + STYPE ("QMcharge", is->QMcharge, NULL); + CTYPE ("QM multiplicity"); + STYPE ("QMmult", is->QMmult, NULL); + CTYPE ("Surface Hopping"); + STYPE ("SH", is->bSH, NULL); + CTYPE ("CAS space options"); + STYPE ("CASorbitals", is->CASorbitals, NULL); + STYPE ("CASelectrons", is->CASelectrons, NULL); + STYPE ("SAon", is->SAon, NULL); + STYPE ("SAoff", is->SAoff, NULL); + STYPE ("SAsteps", is->SAsteps, NULL); + CTYPE ("Scale factor for MM charges"); + RTYPE ("MMChargeScaleFactor", ir->scalefactor, 1.0); + CTYPE ("Optimization of QM subsystem"); + STYPE ("bOPT", is->bOPT, NULL); + STYPE ("bTS", is->bTS, NULL); + + /* Simulated annealing */ + CCTYPE("SIMULATED ANNEALING"); + CTYPE ("Type of annealing for each temperature group (no/single/periodic)"); + STYPE ("annealing", is->anneal, NULL); + CTYPE ("Number of time points to use for specifying annealing in each group"); + STYPE ("annealing-npoints", is->anneal_npoints, NULL); + CTYPE ("List of times at the annealing points for each group"); + STYPE ("annealing-time", is->anneal_time, NULL); + CTYPE ("Temp. at each annealing point, for each group."); + STYPE ("annealing-temp", is->anneal_temp, NULL); + + /* Startup run */ + CCTYPE ("GENERATE VELOCITIES FOR STARTUP RUN"); + EETYPE("gen-vel", opts->bGenVel, yesno_names); + RTYPE ("gen-temp", opts->tempi, 300.0); + ITYPE ("gen-seed", opts->seed, -1); + + /* Shake stuff */ + CCTYPE ("OPTIONS FOR BONDS"); + EETYPE("constraints", opts->nshake, constraints); + CTYPE ("Type of constraint algorithm"); + EETYPE("constraint-algorithm", ir->eConstrAlg, econstr_names); + CTYPE ("Do not constrain the start configuration"); + EETYPE("continuation", ir->bContinuation, yesno_names); + CTYPE ("Use successive overrelaxation to reduce the number of shake iterations"); + EETYPE("Shake-SOR", ir->bShakeSOR, yesno_names); + CTYPE ("Relative tolerance of shake"); + RTYPE ("shake-tol", ir->shake_tol, 0.0001); + CTYPE ("Highest order in the expansion of the constraint coupling matrix"); + ITYPE ("lincs-order", ir->nProjOrder, 4); + CTYPE ("Number of iterations in the final step of LINCS. 1 is fine for"); + CTYPE ("normal simulations, but use 2 to conserve energy in NVE runs."); + CTYPE ("For energy minimization with constraints it should be 4 to 8."); + ITYPE ("lincs-iter", ir->nLincsIter, 1); + CTYPE ("Lincs will write a warning to the stderr if in one step a bond"); + CTYPE ("rotates over more degrees than"); + RTYPE ("lincs-warnangle", ir->LincsWarnAngle, 30.0); + CTYPE ("Convert harmonic bonds to morse potentials"); + EETYPE("morse", opts->bMorse, yesno_names); + + /* Energy group exclusions */ + CCTYPE ("ENERGY GROUP EXCLUSIONS"); + CTYPE ("Pairs of energy groups for which all non-bonded interactions are excluded"); + STYPE ("energygrp-excl", is->egpexcl, NULL); + + /* Walls */ + CCTYPE ("WALLS"); + CTYPE ("Number of walls, type, atom types, densities and box-z scale factor for Ewald"); + ITYPE ("nwall", ir->nwall, 0); + EETYPE("wall-type", ir->wall_type, ewt_names); + RTYPE ("wall-r-linpot", ir->wall_r_linpot, -1); + STYPE ("wall-atomtype", is->wall_atomtype, NULL); + STYPE ("wall-density", is->wall_density, NULL); + RTYPE ("wall-ewald-zfac", ir->wall_ewald_zfac, 3); + + /* COM pulling */ + CCTYPE("COM PULLING"); + CTYPE("Pull type: no, umbrella, constraint or constant-force"); + EETYPE("pull", ir->ePull, epull_names); + if (ir->ePull != epullNO) + { + snew(ir->pull, 1); + is->pull_grp = read_pullparams(&ninp, &inp, ir->pull, &opts->pull_start, wi); + } + + /* Enforced rotation */ + CCTYPE("ENFORCED ROTATION"); + CTYPE("Enforced rotation: No or Yes"); + EETYPE("rotation", ir->bRot, yesno_names); + if (ir->bRot) + { + snew(ir->rot, 1); + is->rot_grp = read_rotparams(&ninp, &inp, ir->rot, wi); + } + + /* Interactive MD */ + ir->bIMD = FALSE; + CCTYPE("Group to display and/or manipulate in interactive MD session"); + STYPE ("IMD-group", is->imd_grp, NULL); + if (is->imd_grp[0] != '\0') + { + snew(ir->imd, 1); + ir->bIMD = TRUE; + } + + /* Refinement */ + CCTYPE("NMR refinement stuff"); + CTYPE ("Distance restraints type: No, Simple or Ensemble"); + EETYPE("disre", ir->eDisre, edisre_names); + CTYPE ("Force weighting of pairs in one distance restraint: Conservative or Equal"); + EETYPE("disre-weighting", ir->eDisreWeighting, edisreweighting_names); + CTYPE ("Use sqrt of the time averaged times the instantaneous violation"); + EETYPE("disre-mixed", ir->bDisreMixed, yesno_names); + RTYPE ("disre-fc", ir->dr_fc, 1000.0); + RTYPE ("disre-tau", ir->dr_tau, 0.0); + CTYPE ("Output frequency for pair distances to energy file"); + ITYPE ("nstdisreout", ir->nstdisreout, 100); + CTYPE ("Orientation restraints: No or Yes"); + EETYPE("orire", opts->bOrire, yesno_names); + CTYPE ("Orientation restraints force constant and tau for time averaging"); + RTYPE ("orire-fc", ir->orires_fc, 0.0); + RTYPE ("orire-tau", ir->orires_tau, 0.0); + STYPE ("orire-fitgrp", is->orirefitgrp, NULL); + CTYPE ("Output frequency for trace(SD) and S to energy file"); + ITYPE ("nstorireout", ir->nstorireout, 100); + + /* free energy variables */ + CCTYPE ("Free energy variables"); + EETYPE("free-energy", ir->efep, efep_names); + STYPE ("couple-moltype", is->couple_moltype, NULL); + EETYPE("couple-lambda0", opts->couple_lam0, couple_lam); + EETYPE("couple-lambda1", opts->couple_lam1, couple_lam); + EETYPE("couple-intramol", opts->bCoupleIntra, yesno_names); + + RTYPE ("init-lambda", fep->init_lambda, -1); /* start with -1 so + we can recognize if + it was not entered */ + ITYPE ("init-lambda-state", fep->init_fep_state, -1); + RTYPE ("delta-lambda", fep->delta_lambda, 0.0); + ITYPE ("nstdhdl", fep->nstdhdl, 50); + STYPE ("fep-lambdas", is->fep_lambda[efptFEP], NULL); + STYPE ("mass-lambdas", is->fep_lambda[efptMASS], NULL); + STYPE ("coul-lambdas", is->fep_lambda[efptCOUL], NULL); + STYPE ("vdw-lambdas", is->fep_lambda[efptVDW], NULL); + STYPE ("bonded-lambdas", is->fep_lambda[efptBONDED], NULL); + STYPE ("restraint-lambdas", is->fep_lambda[efptRESTRAINT], NULL); + STYPE ("temperature-lambdas", is->fep_lambda[efptTEMPERATURE], NULL); + ITYPE ("calc-lambda-neighbors", fep->lambda_neighbors, 1); + STYPE ("init-lambda-weights", is->lambda_weights, NULL); + EETYPE("dhdl-print-energy", fep->bPrintEnergy, yesno_names); + RTYPE ("sc-alpha", fep->sc_alpha, 0.0); + ITYPE ("sc-power", fep->sc_power, 1); + RTYPE ("sc-r-power", fep->sc_r_power, 6.0); + RTYPE ("sc-sigma", fep->sc_sigma, 0.3); + EETYPE("sc-coul", fep->bScCoul, yesno_names); + ITYPE ("dh_hist_size", fep->dh_hist_size, 0); + RTYPE ("dh_hist_spacing", fep->dh_hist_spacing, 0.1); + EETYPE("separate-dhdl-file", fep->separate_dhdl_file, + separate_dhdl_file_names); + EETYPE("dhdl-derivatives", fep->dhdl_derivatives, dhdl_derivatives_names); + ITYPE ("dh_hist_size", fep->dh_hist_size, 0); + RTYPE ("dh_hist_spacing", fep->dh_hist_spacing, 0.1); + + /* Non-equilibrium MD stuff */ + CCTYPE("Non-equilibrium MD stuff"); + STYPE ("acc-grps", is->accgrps, NULL); + STYPE ("accelerate", is->acc, NULL); + STYPE ("freezegrps", is->freeze, NULL); + STYPE ("freezedim", is->frdim, NULL); + RTYPE ("cos-acceleration", ir->cos_accel, 0); + STYPE ("deform", is->deform, NULL); + + /* simulated tempering variables */ + CCTYPE("simulated tempering variables"); + EETYPE("simulated-tempering", ir->bSimTemp, yesno_names); + EETYPE("simulated-tempering-scaling", ir->simtempvals->eSimTempScale, esimtemp_names); + RTYPE("sim-temp-low", ir->simtempvals->simtemp_low, 300.0); + RTYPE("sim-temp-high", ir->simtempvals->simtemp_high, 300.0); + + /* expanded ensemble variables */ + if (ir->efep == efepEXPANDED || ir->bSimTemp) + { + read_expandedparams(&ninp, &inp, expand, wi); + } + + /* Electric fields */ + CCTYPE("Electric fields"); + CTYPE ("Format is number of terms (int) and for all terms an amplitude (real)"); + CTYPE ("and a phase angle (real)"); + STYPE ("E-x", is->efield_x, NULL); + STYPE ("E-xt", is->efield_xt, NULL); + STYPE ("E-y", is->efield_y, NULL); + STYPE ("E-yt", is->efield_yt, NULL); + STYPE ("E-z", is->efield_z, NULL); + STYPE ("E-zt", is->efield_zt, NULL); + + CCTYPE("Ion/water position swapping for computational electrophysiology setups"); + CTYPE("Swap positions along direction: no, X, Y, Z"); + EETYPE("swapcoords", ir->eSwapCoords, eSwapTypes_names); + if (ir->eSwapCoords != eswapNO) + { + snew(ir->swap, 1); + CTYPE("Swap attempt frequency"); + ITYPE("swap-frequency", ir->swap->nstswap, 1); + CTYPE("Two index groups that contain the compartment-partitioning atoms"); + STYPE("split-group0", splitgrp0, NULL); + STYPE("split-group1", splitgrp1, NULL); + CTYPE("Use center of mass of split groups (yes/no), otherwise center of geometry is used"); + EETYPE("massw-split0", ir->swap->massw_split[0], yesno_names); + EETYPE("massw-split1", ir->swap->massw_split[1], yesno_names); + + CTYPE("Group name of ions that can be exchanged with solvent molecules"); + STYPE("swap-group", swapgrp, NULL); + CTYPE("Group name of solvent molecules"); + STYPE("solvent-group", solgrp, NULL); + + CTYPE("Split cylinder: radius, upper and lower extension (nm) (this will define the channels)"); + CTYPE("Note that the split cylinder settings do not have an influence on the swapping protocol,"); + CTYPE("however, if correctly defined, the ion permeation events are counted per channel"); + RTYPE("cyl0-r", ir->swap->cyl0r, 2.0); + RTYPE("cyl0-up", ir->swap->cyl0u, 1.0); + RTYPE("cyl0-down", ir->swap->cyl0l, 1.0); + RTYPE("cyl1-r", ir->swap->cyl1r, 2.0); + RTYPE("cyl1-up", ir->swap->cyl1u, 1.0); + RTYPE("cyl1-down", ir->swap->cyl1l, 1.0); + + CTYPE("Average the number of ions per compartment over these many swap attempt steps"); + ITYPE("coupl-steps", ir->swap->nAverage, 10); + CTYPE("Requested number of anions and cations for each of the two compartments"); + CTYPE("-1 means fix the numbers as found in time step 0"); + ITYPE("anionsA", ir->swap->nanions[0], -1); + ITYPE("cationsA", ir->swap->ncations[0], -1); + ITYPE("anionsB", ir->swap->nanions[1], -1); + ITYPE("cationsB", ir->swap->ncations[1], -1); + CTYPE("Start to swap ions if threshold difference to requested count is reached"); + RTYPE("threshold", ir->swap->threshold, 1.0); + } + + /* AdResS defined thingies */ + CCTYPE ("AdResS parameters"); + EETYPE("adress", ir->bAdress, yesno_names); + if (ir->bAdress) + { + snew(ir->adress, 1); + read_adressparams(&ninp, &inp, ir->adress, wi); + } + + /* User defined thingies */ + CCTYPE ("User defined thingies"); + STYPE ("user1-grps", is->user1, NULL); + STYPE ("user2-grps", is->user2, NULL); + ITYPE ("userint1", ir->userint1, 0); + ITYPE ("userint2", ir->userint2, 0); + ITYPE ("userint3", ir->userint3, 0); + ITYPE ("userint4", ir->userint4, 0); + RTYPE ("userreal1", ir->userreal1, 0); + RTYPE ("userreal2", ir->userreal2, 0); + RTYPE ("userreal3", ir->userreal3, 0); + RTYPE ("userreal4", ir->userreal4, 0); +#undef CTYPE + + write_inpfile(mdparout, ninp, inp, FALSE, wi); + for (i = 0; (i < ninp); i++) + { + sfree(inp[i].name); + sfree(inp[i].value); + } + sfree(inp); + + /* Process options if necessary */ + for (m = 0; m < 2; m++) + { + for (i = 0; i < 2*DIM; i++) + { + dumdub[m][i] = 0.0; + } + if (ir->epc) + { + switch (ir->epct) + { + case epctISOTROPIC: + if (sscanf(dumstr[m], "%lf", &(dumdub[m][XX])) != 1) + { + warning_error(wi, "Pressure coupling not enough values (I need 1)"); + } + dumdub[m][YY] = dumdub[m][ZZ] = dumdub[m][XX]; + break; + case epctSEMIISOTROPIC: + case epctSURFACETENSION: + if (sscanf(dumstr[m], "%lf%lf", + &(dumdub[m][XX]), &(dumdub[m][ZZ])) != 2) + { + warning_error(wi, "Pressure coupling not enough values (I need 2)"); + } + dumdub[m][YY] = dumdub[m][XX]; + break; + case epctANISOTROPIC: + if (sscanf(dumstr[m], "%lf%lf%lf%lf%lf%lf", + &(dumdub[m][XX]), &(dumdub[m][YY]), &(dumdub[m][ZZ]), + &(dumdub[m][3]), &(dumdub[m][4]), &(dumdub[m][5])) != 6) + { + warning_error(wi, "Pressure coupling not enough values (I need 6)"); + } + break; + default: + gmx_fatal(FARGS, "Pressure coupling type %s not implemented yet", + epcoupltype_names[ir->epct]); + } + } + } + clear_mat(ir->ref_p); + clear_mat(ir->compress); + for (i = 0; i < DIM; i++) + { + ir->ref_p[i][i] = dumdub[1][i]; + ir->compress[i][i] = dumdub[0][i]; + } + if (ir->epct == epctANISOTROPIC) + { + ir->ref_p[XX][YY] = dumdub[1][3]; + ir->ref_p[XX][ZZ] = dumdub[1][4]; + ir->ref_p[YY][ZZ] = dumdub[1][5]; + if (ir->ref_p[XX][YY] != 0 && ir->ref_p[XX][ZZ] != 0 && ir->ref_p[YY][ZZ] != 0) + { + warning(wi, "All off-diagonal reference pressures are non-zero. Are you sure you want to apply a threefold shear stress?\n"); + } + ir->compress[XX][YY] = dumdub[0][3]; + ir->compress[XX][ZZ] = dumdub[0][4]; + ir->compress[YY][ZZ] = dumdub[0][5]; + for (i = 0; i < DIM; i++) + { + for (m = 0; m < i; m++) + { + ir->ref_p[i][m] = ir->ref_p[m][i]; + ir->compress[i][m] = ir->compress[m][i]; + } + } + } + + if (ir->comm_mode == ecmNO) + { + ir->nstcomm = 0; + } + + opts->couple_moltype = NULL; + if (strlen(is->couple_moltype) > 0) + { + if (ir->efep != efepNO) + { + opts->couple_moltype = strdup(is->couple_moltype); + if (opts->couple_lam0 == opts->couple_lam1) + { + warning(wi, "The lambda=0 and lambda=1 states for coupling are identical"); + } + if (ir->eI == eiMD && (opts->couple_lam0 == ecouplamNONE || + opts->couple_lam1 == ecouplamNONE)) + { + warning(wi, "For proper sampling of the (nearly) decoupled state, stochastic dynamics should be used"); + } + } + else + { + warning(wi, "Can not couple a molecule with free_energy = no"); + } + } + /* FREE ENERGY AND EXPANDED ENSEMBLE OPTIONS */ + if (ir->efep != efepNO) + { + if (fep->delta_lambda > 0) + { + ir->efep = efepSLOWGROWTH; + } + } + + if (ir->bSimTemp) + { + fep->bPrintEnergy = TRUE; + /* always print out the energy to dhdl if we are doing expanded ensemble, since we need the total energy + if the temperature is changing. */ + } + + if ((ir->efep != efepNO) || ir->bSimTemp) + { + ir->bExpanded = FALSE; + if ((ir->efep == efepEXPANDED) || ir->bSimTemp) + { + ir->bExpanded = TRUE; + } + do_fep_params(ir, is->fep_lambda, is->lambda_weights); + if (ir->bSimTemp) /* done after fep params */ + { + do_simtemp_params(ir); + } + } + else + { + ir->fepvals->n_lambda = 0; + } + + /* WALL PARAMETERS */ + + do_wall_params(ir, is->wall_atomtype, is->wall_density, opts); + + /* ORIENTATION RESTRAINT PARAMETERS */ + + if (opts->bOrire && str_nelem(is->orirefitgrp, MAXPTR, NULL) != 1) + { + warning_error(wi, "ERROR: Need one orientation restraint fit group\n"); + } + + /* DEFORMATION PARAMETERS */ + + clear_mat(ir->deform); + for (i = 0; i < 6; i++) + { + dumdub[0][i] = 0; + } + m = sscanf(is->deform, "%lf %lf %lf %lf %lf %lf", + &(dumdub[0][0]), &(dumdub[0][1]), &(dumdub[0][2]), + &(dumdub[0][3]), &(dumdub[0][4]), &(dumdub[0][5])); + for (i = 0; i < 3; i++) + { + ir->deform[i][i] = dumdub[0][i]; + } + ir->deform[YY][XX] = dumdub[0][3]; + ir->deform[ZZ][XX] = dumdub[0][4]; + ir->deform[ZZ][YY] = dumdub[0][5]; + if (ir->epc != epcNO) + { + for (i = 0; i < 3; i++) + { + for (j = 0; j <= i; j++) + { + if (ir->deform[i][j] != 0 && ir->compress[i][j] != 0) + { + warning_error(wi, "A box element has deform set and compressibility > 0"); + } + } + } + for (i = 0; i < 3; i++) + { + for (j = 0; j < i; j++) + { + if (ir->deform[i][j] != 0) + { + for (m = j; m < DIM; m++) + { + if (ir->compress[m][j] != 0) + { + sprintf(warn_buf, "An off-diagonal box element has deform set while compressibility > 0 for the same component of another box vector, this might lead to spurious periodicity effects."); + warning(wi, warn_buf); + } + } + } + } + } + } + + /* Ion/water position swapping checks */ + if (ir->eSwapCoords != eswapNO) + { + if (ir->swap->nstswap < 1) + { + warning_error(wi, "swap_frequency must be 1 or larger when ion swapping is requested"); + } + if (ir->swap->nAverage < 1) + { + warning_error(wi, "coupl_steps must be 1 or larger.\n"); + } + if (ir->swap->threshold < 1.0) + { + warning_error(wi, "Ion count threshold must be at least 1.\n"); + } + } + + sfree(dumstr[0]); + sfree(dumstr[1]); +} + +static int search_QMstring(const char *s, int ng, const char *gn[]) +{ + /* same as normal search_string, but this one searches QM strings */ + int i; + + for (i = 0; (i < ng); i++) + { + if (gmx_strcasecmp(s, gn[i]) == 0) + { + return i; + } + } + + gmx_fatal(FARGS, "this QM method or basisset (%s) is not implemented\n!", s); + + return -1; + +} /* search_QMstring */ + +/* We would like gn to be const as well, but C doesn't allow this */ +int search_string(const char *s, int ng, char *gn[]) +{ + int i; + + for (i = 0; (i < ng); i++) + { + if (gmx_strcasecmp(s, gn[i]) == 0) + { + return i; + } + } + + gmx_fatal(FARGS, + "Group %s referenced in the .mdp file was not found in the index file.\n" + "Group names must match either [moleculetype] names or custom index group\n" + "names, in which case you must supply an index file to the '-n' option\n" + "of grompp.", + s); + + return -1; +} + +static gmx_bool do_numbering(int natoms, gmx_groups_t *groups, int ng, char *ptrs[], + t_blocka *block, char *gnames[], + int gtype, int restnm, + int grptp, gmx_bool bVerbose, + warninp_t wi) +{ + unsigned short *cbuf; + t_grps *grps = &(groups->grps[gtype]); + int i, j, gid, aj, ognr, ntot = 0; + const char *title; + gmx_bool bRest; + char warn_buf[STRLEN]; + + if (debug) + { + fprintf(debug, "Starting numbering %d groups of type %d\n", ng, gtype); + } + + title = gtypes[gtype]; + + snew(cbuf, natoms); + /* Mark all id's as not set */ + for (i = 0; (i < natoms); i++) + { + cbuf[i] = NOGID; + } + + snew(grps->nm_ind, ng+1); /* +1 for possible rest group */ + for (i = 0; (i < ng); i++) + { + /* Lookup the group name in the block structure */ + gid = search_string(ptrs[i], block->nr, gnames); + if ((grptp != egrptpONE) || (i == 0)) + { + grps->nm_ind[grps->nr++] = gid; + } + if (debug) + { + fprintf(debug, "Found gid %d for group %s\n", gid, ptrs[i]); + } + + /* Now go over the atoms in the group */ + for (j = block->index[gid]; (j < block->index[gid+1]); j++) + { + + aj = block->a[j]; + + /* Range checking */ + if ((aj < 0) || (aj >= natoms)) + { + gmx_fatal(FARGS, "Invalid atom number %d in indexfile", aj); + } + /* Lookup up the old group number */ + ognr = cbuf[aj]; + if (ognr != NOGID) + { + gmx_fatal(FARGS, "Atom %d in multiple %s groups (%d and %d)", + aj+1, title, ognr+1, i+1); + } + else + { + /* Store the group number in buffer */ + if (grptp == egrptpONE) + { + cbuf[aj] = 0; + } + else + { + cbuf[aj] = i; + } + ntot++; + } + } + } + + /* Now check whether we have done all atoms */ + bRest = FALSE; + if (ntot != natoms) + { + if (grptp == egrptpALL) + { + gmx_fatal(FARGS, "%d atoms are not part of any of the %s groups", + natoms-ntot, title); + } + else if (grptp == egrptpPART) + { + sprintf(warn_buf, "%d atoms are not part of any of the %s groups", + natoms-ntot, title); + warning_note(wi, warn_buf); + } + /* Assign all atoms currently unassigned to a rest group */ + for (j = 0; (j < natoms); j++) + { + if (cbuf[j] == NOGID) + { + cbuf[j] = grps->nr; + bRest = TRUE; + } + } + if (grptp != egrptpPART) + { + if (bVerbose) + { + fprintf(stderr, + "Making dummy/rest group for %s containing %d elements\n", + title, natoms-ntot); + } + /* Add group name "rest" */ + grps->nm_ind[grps->nr] = restnm; + + /* Assign the rest name to all atoms not currently assigned to a group */ + for (j = 0; (j < natoms); j++) + { + if (cbuf[j] == NOGID) + { + cbuf[j] = grps->nr; + } + } + grps->nr++; + } + } + + if (grps->nr == 1 && (ntot == 0 || ntot == natoms)) + { + /* All atoms are part of one (or no) group, no index required */ + groups->ngrpnr[gtype] = 0; + groups->grpnr[gtype] = NULL; + } + else + { + groups->ngrpnr[gtype] = natoms; + snew(groups->grpnr[gtype], natoms); + for (j = 0; (j < natoms); j++) + { + groups->grpnr[gtype][j] = cbuf[j]; + } + } + + sfree(cbuf); + + return (bRest && grptp == egrptpPART); +} + +static void calc_nrdf(gmx_mtop_t *mtop, t_inputrec *ir, char **gnames) +{ + t_grpopts *opts; + gmx_groups_t *groups; + t_pull *pull; + int natoms, ai, aj, i, j, d, g, imin, jmin; + t_iatom *ia; + int *nrdf2, *na_vcm, na_tot; + double *nrdf_tc, *nrdf_vcm, nrdf_uc, n_sub = 0; + gmx_mtop_atomloop_all_t aloop; + t_atom *atom; + int mb, mol, ftype, as; + gmx_molblock_t *molb; + gmx_moltype_t *molt; + + /* Calculate nrdf. + * First calc 3xnr-atoms for each group + * then subtract half a degree of freedom for each constraint + * + * Only atoms and nuclei contribute to the degrees of freedom... + */ + + opts = &ir->opts; + + groups = &mtop->groups; + natoms = mtop->natoms; + + /* Allocate one more for a possible rest group */ + /* We need to sum degrees of freedom into doubles, + * since floats give too low nrdf's above 3 million atoms. + */ + snew(nrdf_tc, groups->grps[egcTC].nr+1); + snew(nrdf_vcm, groups->grps[egcVCM].nr+1); + snew(na_vcm, groups->grps[egcVCM].nr+1); + + for (i = 0; i < groups->grps[egcTC].nr; i++) + { + nrdf_tc[i] = 0; + } + for (i = 0; i < groups->grps[egcVCM].nr+1; i++) + { + nrdf_vcm[i] = 0; + } + + snew(nrdf2, natoms); + aloop = gmx_mtop_atomloop_all_init(mtop); + while (gmx_mtop_atomloop_all_next(aloop, &i, &atom)) + { + nrdf2[i] = 0; + if (atom->ptype == eptAtom || atom->ptype == eptNucleus) + { + g = ggrpnr(groups, egcFREEZE, i); + /* Double count nrdf for particle i */ + for (d = 0; d < DIM; d++) + { + if (opts->nFreeze[g][d] == 0) + { + nrdf2[i] += 2; + } + } + nrdf_tc [ggrpnr(groups, egcTC, i)] += 0.5*nrdf2[i]; + nrdf_vcm[ggrpnr(groups, egcVCM, i)] += 0.5*nrdf2[i]; + } + } + + as = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + molb = &mtop->molblock[mb]; + molt = &mtop->moltype[molb->type]; + atom = molt->atoms.atom; + for (mol = 0; mol < molb->nmol; mol++) + { + for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++) + { + ia = molt->ilist[ftype].iatoms; + for (i = 0; i < molt->ilist[ftype].nr; ) + { + /* Subtract degrees of freedom for the constraints, + * if the particles still have degrees of freedom left. + * If one of the particles is a vsite or a shell, then all + * constraint motion will go there, but since they do not + * contribute to the constraints the degrees of freedom do not + * change. + */ + ai = as + ia[1]; + aj = as + ia[2]; + if (((atom[ia[1]].ptype == eptNucleus) || + (atom[ia[1]].ptype == eptAtom)) && + ((atom[ia[2]].ptype == eptNucleus) || + (atom[ia[2]].ptype == eptAtom))) + { + if (nrdf2[ai] > 0) + { + jmin = 1; + } + else + { + jmin = 2; + } + if (nrdf2[aj] > 0) + { + imin = 1; + } + else + { + imin = 2; + } + imin = min(imin, nrdf2[ai]); + jmin = min(jmin, nrdf2[aj]); + nrdf2[ai] -= imin; + nrdf2[aj] -= jmin; + nrdf_tc [ggrpnr(groups, egcTC, ai)] -= 0.5*imin; + nrdf_tc [ggrpnr(groups, egcTC, aj)] -= 0.5*jmin; + nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin; + nrdf_vcm[ggrpnr(groups, egcVCM, aj)] -= 0.5*jmin; + } + ia += interaction_function[ftype].nratoms+1; + i += interaction_function[ftype].nratoms+1; + } + } + ia = molt->ilist[F_SETTLE].iatoms; + for (i = 0; i < molt->ilist[F_SETTLE].nr; ) + { + /* Subtract 1 dof from every atom in the SETTLE */ + for (j = 0; j < 3; j++) + { + ai = as + ia[1+j]; + imin = min(2, nrdf2[ai]); + nrdf2[ai] -= imin; + nrdf_tc [ggrpnr(groups, egcTC, ai)] -= 0.5*imin; + nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin; + } + ia += 4; + i += 4; + } + as += molt->atoms.nr; + } + } + + if (ir->ePull == epullCONSTRAINT) + { + /* Correct nrdf for the COM constraints. + * We correct using the TC and VCM group of the first atom + * in the reference and pull group. If atoms in one pull group + * belong to different TC or VCM groups it is anyhow difficult + * to determine the optimal nrdf assignment. + */ + pull = ir->pull; + + for (i = 0; i < pull->ncoord; i++) + { + imin = 1; + + for (j = 0; j < 2; j++) + { + const t_pull_group *pgrp; + + pgrp = &pull->group[pull->coord[i].group[j]]; + + if (pgrp->nat > 0) + { + /* Subtract 1/2 dof from each group */ + ai = pgrp->ind[0]; + nrdf_tc [ggrpnr(groups, egcTC, ai)] -= 0.5*imin; + nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin; + if (nrdf_tc[ggrpnr(groups, egcTC, ai)] < 0) + { + gmx_fatal(FARGS, "Center of mass pulling constraints caused the number of degrees of freedom for temperature coupling group %s to be negative", gnames[groups->grps[egcTC].nm_ind[ggrpnr(groups, egcTC, ai)]]); + } + } + else + { + /* We need to subtract the whole DOF from group j=1 */ + imin += 1; + } + } + } + } + + if (ir->nstcomm != 0) + { + /* Subtract 3 from the number of degrees of freedom in each vcm group + * when com translation is removed and 6 when rotation is removed + * as well. + */ + switch (ir->comm_mode) + { + case ecmLINEAR: + n_sub = ndof_com(ir); + break; + case ecmANGULAR: + n_sub = 6; + break; + default: + n_sub = 0; + gmx_incons("Checking comm_mode"); + } + + for (i = 0; i < groups->grps[egcTC].nr; i++) + { + /* Count the number of atoms of TC group i for every VCM group */ + for (j = 0; j < groups->grps[egcVCM].nr+1; j++) + { + na_vcm[j] = 0; + } + na_tot = 0; + for (ai = 0; ai < natoms; ai++) + { + if (ggrpnr(groups, egcTC, ai) == i) + { + na_vcm[ggrpnr(groups, egcVCM, ai)]++; + na_tot++; + } + } + /* Correct for VCM removal according to the fraction of each VCM + * group present in this TC group. + */ + nrdf_uc = nrdf_tc[i]; + if (debug) + { + fprintf(debug, "T-group[%d] nrdf_uc = %g, n_sub = %g\n", + i, nrdf_uc, n_sub); + } + nrdf_tc[i] = 0; + for (j = 0; j < groups->grps[egcVCM].nr+1; j++) + { + if (nrdf_vcm[j] > n_sub) + { + nrdf_tc[i] += nrdf_uc*((double)na_vcm[j]/(double)na_tot)* + (nrdf_vcm[j] - n_sub)/nrdf_vcm[j]; + } + if (debug) + { + fprintf(debug, " nrdf_vcm[%d] = %g, nrdf = %g\n", + j, nrdf_vcm[j], nrdf_tc[i]); + } + } + } + } + for (i = 0; (i < groups->grps[egcTC].nr); i++) + { + opts->nrdf[i] = nrdf_tc[i]; + if (opts->nrdf[i] < 0) + { + opts->nrdf[i] = 0; + } + fprintf(stderr, + "Number of degrees of freedom in T-Coupling group %s is %.2f\n", + gnames[groups->grps[egcTC].nm_ind[i]], opts->nrdf[i]); + } + + sfree(nrdf2); + sfree(nrdf_tc); + sfree(nrdf_vcm); + sfree(na_vcm); +} + +static void decode_cos(char *s, t_cosines *cosine) +{ + char *t; + char format[STRLEN], f1[STRLEN]; + double a, phi; + int i; + + t = strdup(s); + trim(t); + + cosine->n = 0; + cosine->a = NULL; + cosine->phi = NULL; + if (strlen(t)) + { + sscanf(t, "%d", &(cosine->n)); + if (cosine->n <= 0) + { + cosine->n = 0; + } + else + { + snew(cosine->a, cosine->n); + snew(cosine->phi, cosine->n); + + sprintf(format, "%%*d"); + for (i = 0; (i < cosine->n); i++) + { + strcpy(f1, format); + strcat(f1, "%lf%lf"); + if (sscanf(t, f1, &a, &phi) < 2) + { + gmx_fatal(FARGS, "Invalid input for electric field shift: '%s'", t); + } + cosine->a[i] = a; + cosine->phi[i] = phi; + strcat(format, "%*lf%*lf"); + } + } + } + sfree(t); +} + +static gmx_bool do_egp_flag(t_inputrec *ir, gmx_groups_t *groups, + const char *option, const char *val, int flag) +{ + /* The maximum number of energy group pairs would be MAXPTR*(MAXPTR+1)/2. + * But since this is much larger than STRLEN, such a line can not be parsed. + * The real maximum is the number of names that fit in a string: STRLEN/2. + */ +#define EGP_MAX (STRLEN/2) + int nelem, i, j, k, nr; + char *names[EGP_MAX]; + char ***gnames; + gmx_bool bSet; + + gnames = groups->grpname; + + nelem = str_nelem(val, EGP_MAX, names); + if (nelem % 2 != 0) + { + gmx_fatal(FARGS, "The number of groups for %s is odd", option); + } + nr = groups->grps[egcENER].nr; + bSet = FALSE; + for (i = 0; i < nelem/2; i++) + { + j = 0; + while ((j < nr) && + gmx_strcasecmp(names[2*i], *(gnames[groups->grps[egcENER].nm_ind[j]]))) + { + j++; + } + if (j == nr) + { + gmx_fatal(FARGS, "%s in %s is not an energy group\n", + names[2*i], option); + } + k = 0; + while ((k < nr) && + gmx_strcasecmp(names[2*i+1], *(gnames[groups->grps[egcENER].nm_ind[k]]))) + { + k++; + } + if (k == nr) + { + gmx_fatal(FARGS, "%s in %s is not an energy group\n", + names[2*i+1], option); + } + if ((j < nr) && (k < nr)) + { + ir->opts.egp_flags[nr*j+k] |= flag; + ir->opts.egp_flags[nr*k+j] |= flag; + bSet = TRUE; + } + } + + return bSet; +} + + +static void make_swap_groups( + t_swapcoords *swap, + char *swapgname, + char *splitg0name, + char *splitg1name, + char *solgname, + t_blocka *grps, + char **gnames) +{ + int ig = -1, i = 0, j; + char *splitg; + + + /* Just a quick check here, more thorough checks are in mdrun */ + if (strcmp(splitg0name, splitg1name) == 0) + { + gmx_fatal(FARGS, "The split groups can not both be '%s'.", splitg0name); + } + + /* First get the swap group index atoms */ + ig = search_string(swapgname, grps->nr, gnames); + swap->nat = grps->index[ig+1] - grps->index[ig]; + if (swap->nat > 0) + { + fprintf(stderr, "Swap group '%s' contains %d atoms.\n", swapgname, swap->nat); + snew(swap->ind, swap->nat); + for (i = 0; i < swap->nat; i++) + { + swap->ind[i] = grps->a[grps->index[ig]+i]; + } + } + else + { + gmx_fatal(FARGS, "You defined an empty group of atoms for swapping."); + } + + /* Now do so for the split groups */ + for (j = 0; j < 2; j++) + { + if (j == 0) + { + splitg = splitg0name; + } + else + { + splitg = splitg1name; + } + + ig = search_string(splitg, grps->nr, gnames); + swap->nat_split[j] = grps->index[ig+1] - grps->index[ig]; + if (swap->nat_split[j] > 0) + { + fprintf(stderr, "Split group %d '%s' contains %d atom%s.\n", + j, splitg, swap->nat_split[j], (swap->nat_split[j] > 1) ? "s" : ""); + snew(swap->ind_split[j], swap->nat_split[j]); + for (i = 0; i < swap->nat_split[j]; i++) + { + swap->ind_split[j][i] = grps->a[grps->index[ig]+i]; + } + } + else + { + gmx_fatal(FARGS, "Split group %d has to contain at least 1 atom!", j); + } + } + + /* Now get the solvent group index atoms */ + ig = search_string(solgname, grps->nr, gnames); + swap->nat_sol = grps->index[ig+1] - grps->index[ig]; + if (swap->nat_sol > 0) + { + fprintf(stderr, "Solvent group '%s' contains %d atoms.\n", solgname, swap->nat_sol); + snew(swap->ind_sol, swap->nat_sol); + for (i = 0; i < swap->nat_sol; i++) + { + swap->ind_sol[i] = grps->a[grps->index[ig]+i]; + } + } + else + { + gmx_fatal(FARGS, "You defined an empty group of solvent. Cannot exchange ions."); + } +} + + +void make_IMD_group(t_IMD *IMDgroup, char *IMDgname, t_blocka *grps, char **gnames) +{ + int ig = -1, i; + + + ig = search_string(IMDgname, grps->nr, gnames); + IMDgroup->nat = grps->index[ig+1] - grps->index[ig]; + + if (IMDgroup->nat > 0) + { + fprintf(stderr, "Group '%s' with %d atoms can be activated for interactive molecular dynamics (IMD).\n", + IMDgname, IMDgroup->nat); + snew(IMDgroup->ind, IMDgroup->nat); + for (i = 0; i < IMDgroup->nat; i++) + { + IMDgroup->ind[i] = grps->a[grps->index[ig]+i]; + } + } +} + + +void do_index(const char* mdparin, const char *ndx, + gmx_mtop_t *mtop, + gmx_bool bVerbose, + t_inputrec *ir, rvec *v, + warninp_t wi) +{ + t_blocka *grps; + gmx_groups_t *groups; + int natoms; + t_symtab *symtab; + t_atoms atoms_all; + char warnbuf[STRLEN], **gnames; + int nr, ntcg, ntau_t, nref_t, nacc, nofg, nSA, nSA_points, nSA_time, nSA_temp; + real tau_min; + int nstcmin; + int nacg, nfreeze, nfrdim, nenergy, nvcm, nuser; + char *ptr1[MAXPTR], *ptr2[MAXPTR], *ptr3[MAXPTR]; + int i, j, k, restnm; + real SAtime; + gmx_bool bExcl, bTable, bSetTCpar, bAnneal, bRest; + int nQMmethod, nQMbasis, nQMcharge, nQMmult, nbSH, nCASorb, nCASelec, + nSAon, nSAoff, nSAsteps, nQMg, nbOPT, nbTS; + char warn_buf[STRLEN]; + + if (bVerbose) + { + fprintf(stderr, "processing index file...\n"); + } + debug_gmx(); + if (ndx == NULL) + { + snew(grps, 1); + snew(grps->index, 1); + snew(gnames, 1); + atoms_all = gmx_mtop_global_atoms(mtop); + analyse(&atoms_all, grps, &gnames, FALSE, TRUE); + free_t_atoms(&atoms_all, FALSE); + } + else + { + grps = init_index(ndx, &gnames); + } + + groups = &mtop->groups; + natoms = mtop->natoms; + symtab = &mtop->symtab; + + snew(groups->grpname, grps->nr+1); + + for (i = 0; (i < grps->nr); i++) + { + groups->grpname[i] = put_symtab(symtab, gnames[i]); + } + groups->grpname[i] = put_symtab(symtab, "rest"); + restnm = i; + srenew(gnames, grps->nr+1); + gnames[restnm] = *(groups->grpname[i]); + groups->ngrpname = grps->nr+1; + + set_warning_line(wi, mdparin, -1); + + ntau_t = str_nelem(is->tau_t, MAXPTR, ptr1); + nref_t = str_nelem(is->ref_t, MAXPTR, ptr2); + ntcg = str_nelem(is->tcgrps, MAXPTR, ptr3); + if ((ntau_t != ntcg) || (nref_t != ntcg)) + { + gmx_fatal(FARGS, "Invalid T coupling input: %d groups, %d ref-t values and " + "%d tau-t values", ntcg, nref_t, ntau_t); + } + + bSetTCpar = (ir->etc || EI_SD(ir->eI) || ir->eI == eiBD || EI_TPI(ir->eI)); + do_numbering(natoms, groups, ntcg, ptr3, grps, gnames, egcTC, + restnm, bSetTCpar ? egrptpALL : egrptpALL_GENREST, bVerbose, wi); + nr = groups->grps[egcTC].nr; + ir->opts.ngtc = nr; + snew(ir->opts.nrdf, nr); + snew(ir->opts.tau_t, nr); + snew(ir->opts.ref_t, nr); + if (ir->eI == eiBD && ir->bd_fric == 0) + { + fprintf(stderr, "bd-fric=0, so tau-t will be used as the inverse friction constant(s)\n"); + } + + if (bSetTCpar) + { + if (nr != nref_t) + { + gmx_fatal(FARGS, "Not enough ref-t and tau-t values!"); + } + + tau_min = 1e20; + for (i = 0; (i < nr); i++) + { + ir->opts.tau_t[i] = strtod(ptr1[i], NULL); + if ((ir->eI == eiBD || ir->eI == eiSD2) && ir->opts.tau_t[i] <= 0) + { + sprintf(warn_buf, "With integrator %s tau-t should be larger than 0", ei_names[ir->eI]); + warning_error(wi, warn_buf); + } + + if (ir->etc != etcVRESCALE && ir->opts.tau_t[i] == 0) + { + warning_note(wi, "tau-t = -1 is the value to signal that a group should not have temperature coupling. Treating your use of tau-t = 0 as if you used -1."); + } + + if (ir->opts.tau_t[i] >= 0) + { + tau_min = min(tau_min, ir->opts.tau_t[i]); + } + } + if (ir->etc != etcNO && ir->nsttcouple == -1) + { + ir->nsttcouple = ir_optimal_nsttcouple(ir); + } + + if (EI_VV(ir->eI)) + { + if ((ir->etc == etcNOSEHOOVER) && (ir->epc == epcBERENDSEN)) + { + gmx_fatal(FARGS, "Cannot do Nose-Hoover temperature with Berendsen pressure control with md-vv; use either vrescale temperature with berendsen pressure or Nose-Hoover temperature with MTTK pressure"); + } + if ((ir->epc == epcMTTK) && (ir->etc > etcNO)) + { + if (ir->nstpcouple != ir->nsttcouple) + { + int mincouple = min(ir->nstpcouple, ir->nsttcouple); + ir->nstpcouple = ir->nsttcouple = mincouple; + sprintf(warn_buf, "for current Trotter decomposition methods with vv, nsttcouple and nstpcouple must be equal. Both have been reset to min(nsttcouple,nstpcouple) = %d", mincouple); + warning_note(wi, warn_buf); + } + } + } + /* velocity verlet with averaged kinetic energy KE = 0.5*(v(t+1/2) - v(t-1/2)) is implemented + primarily for testing purposes, and does not work with temperature coupling other than 1 */ + + if (ETC_ANDERSEN(ir->etc)) + { + if (ir->nsttcouple != 1) + { + ir->nsttcouple = 1; + sprintf(warn_buf, "Andersen temperature control methods assume nsttcouple = 1; there is no need for larger nsttcouple > 1, since no global parameters are computed. nsttcouple has been reset to 1"); + warning_note(wi, warn_buf); + } + } + nstcmin = tcouple_min_integration_steps(ir->etc); + if (nstcmin > 1) + { + if (tau_min/(ir->delta_t*ir->nsttcouple) < nstcmin) + { + sprintf(warn_buf, "For proper integration of the %s thermostat, tau-t (%g) should be at least %d times larger than nsttcouple*dt (%g)", + ETCOUPLTYPE(ir->etc), + tau_min, nstcmin, + ir->nsttcouple*ir->delta_t); + warning(wi, warn_buf); + } + } + for (i = 0; (i < nr); i++) + { + ir->opts.ref_t[i] = strtod(ptr2[i], NULL); + if (ir->opts.ref_t[i] < 0) + { + gmx_fatal(FARGS, "ref-t for group %d negative", i); + } + } + /* set the lambda mc temperature to the md integrator temperature (which should be defined + if we are in this conditional) if mc_temp is negative */ + if (ir->expandedvals->mc_temp < 0) + { + ir->expandedvals->mc_temp = ir->opts.ref_t[0]; /*for now, set to the first reft */ + } + } + + /* Simulated annealing for each group. There are nr groups */ + nSA = str_nelem(is->anneal, MAXPTR, ptr1); + if (nSA == 1 && (ptr1[0][0] == 'n' || ptr1[0][0] == 'N')) + { + nSA = 0; + } + if (nSA > 0 && nSA != nr) + { + gmx_fatal(FARGS, "Not enough annealing values: %d (for %d groups)\n", nSA, nr); + } + else + { + snew(ir->opts.annealing, nr); + snew(ir->opts.anneal_npoints, nr); + snew(ir->opts.anneal_time, nr); + snew(ir->opts.anneal_temp, nr); + for (i = 0; i < nr; i++) + { + ir->opts.annealing[i] = eannNO; + ir->opts.anneal_npoints[i] = 0; + ir->opts.anneal_time[i] = NULL; + ir->opts.anneal_temp[i] = NULL; + } + if (nSA > 0) + { + bAnneal = FALSE; + for (i = 0; i < nr; i++) + { + if (ptr1[i][0] == 'n' || ptr1[i][0] == 'N') + { + ir->opts.annealing[i] = eannNO; + } + else if (ptr1[i][0] == 's' || ptr1[i][0] == 'S') + { + ir->opts.annealing[i] = eannSINGLE; + bAnneal = TRUE; + } + else if (ptr1[i][0] == 'p' || ptr1[i][0] == 'P') + { + ir->opts.annealing[i] = eannPERIODIC; + bAnneal = TRUE; + } + } + if (bAnneal) + { + /* Read the other fields too */ + nSA_points = str_nelem(is->anneal_npoints, MAXPTR, ptr1); + if (nSA_points != nSA) + { + gmx_fatal(FARGS, "Found %d annealing-npoints values for %d groups\n", nSA_points, nSA); + } + for (k = 0, i = 0; i < nr; i++) + { + ir->opts.anneal_npoints[i] = strtol(ptr1[i], NULL, 10); + if (ir->opts.anneal_npoints[i] == 1) + { + gmx_fatal(FARGS, "Please specify at least a start and an end point for annealing\n"); + } + snew(ir->opts.anneal_time[i], ir->opts.anneal_npoints[i]); + snew(ir->opts.anneal_temp[i], ir->opts.anneal_npoints[i]); + k += ir->opts.anneal_npoints[i]; + } + + nSA_time = str_nelem(is->anneal_time, MAXPTR, ptr1); + if (nSA_time != k) + { + gmx_fatal(FARGS, "Found %d annealing-time values, wanter %d\n", nSA_time, k); + } + nSA_temp = str_nelem(is->anneal_temp, MAXPTR, ptr2); + if (nSA_temp != k) + { + gmx_fatal(FARGS, "Found %d annealing-temp values, wanted %d\n", nSA_temp, k); + } + + for (i = 0, k = 0; i < nr; i++) + { + + for (j = 0; j < ir->opts.anneal_npoints[i]; j++) + { + ir->opts.anneal_time[i][j] = strtod(ptr1[k], NULL); + ir->opts.anneal_temp[i][j] = strtod(ptr2[k], NULL); + if (j == 0) + { + if (ir->opts.anneal_time[i][0] > (ir->init_t+GMX_REAL_EPS)) + { + gmx_fatal(FARGS, "First time point for annealing > init_t.\n"); + } + } + else + { + /* j>0 */ + if (ir->opts.anneal_time[i][j] < ir->opts.anneal_time[i][j-1]) + { + gmx_fatal(FARGS, "Annealing timepoints out of order: t=%f comes after t=%f\n", + ir->opts.anneal_time[i][j], ir->opts.anneal_time[i][j-1]); + } + } + if (ir->opts.anneal_temp[i][j] < 0) + { + gmx_fatal(FARGS, "Found negative temperature in annealing: %f\n", ir->opts.anneal_temp[i][j]); + } + k++; + } + } + /* Print out some summary information, to make sure we got it right */ + for (i = 0, k = 0; i < nr; i++) + { + if (ir->opts.annealing[i] != eannNO) + { + j = groups->grps[egcTC].nm_ind[i]; + fprintf(stderr, "Simulated annealing for group %s: %s, %d timepoints\n", + *(groups->grpname[j]), eann_names[ir->opts.annealing[i]], + ir->opts.anneal_npoints[i]); + fprintf(stderr, "Time (ps) Temperature (K)\n"); + /* All terms except the last one */ + for (j = 0; j < (ir->opts.anneal_npoints[i]-1); j++) + { + fprintf(stderr, "%9.1f %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]); + } + + /* Finally the last one */ + j = ir->opts.anneal_npoints[i]-1; + if (ir->opts.annealing[i] == eannSINGLE) + { + fprintf(stderr, "%9.1f- %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]); + } + else + { + fprintf(stderr, "%9.1f %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]); + if (fabs(ir->opts.anneal_temp[i][j]-ir->opts.anneal_temp[i][0]) > GMX_REAL_EPS) + { + warning_note(wi, "There is a temperature jump when your annealing loops back.\n"); + } + } + } + } + } + } + } + + if (ir->ePull != epullNO) + { + make_pull_groups(ir->pull, is->pull_grp, grps, gnames); + + make_pull_coords(ir->pull); + } + + if (ir->bRot) + { + make_rotation_groups(ir->rot, is->rot_grp, grps, gnames); + } + + if (ir->eSwapCoords != eswapNO) + { + make_swap_groups(ir->swap, swapgrp, splitgrp0, splitgrp1, solgrp, grps, gnames); + } + + /* Make indices for IMD session */ + if (ir->bIMD) + { + make_IMD_group(ir->imd, is->imd_grp, grps, gnames); + } + + nacc = str_nelem(is->acc, MAXPTR, ptr1); + nacg = str_nelem(is->accgrps, MAXPTR, ptr2); + if (nacg*DIM != nacc) + { + gmx_fatal(FARGS, "Invalid Acceleration input: %d groups and %d acc. values", + nacg, nacc); + } + do_numbering(natoms, groups, nacg, ptr2, grps, gnames, egcACC, + restnm, egrptpALL_GENREST, bVerbose, wi); + nr = groups->grps[egcACC].nr; + snew(ir->opts.acc, nr); + ir->opts.ngacc = nr; + + for (i = k = 0; (i < nacg); i++) + { + for (j = 0; (j < DIM); j++, k++) + { + ir->opts.acc[i][j] = strtod(ptr1[k], NULL); + } + } + for (; (i < nr); i++) + { + for (j = 0; (j < DIM); j++) + { + ir->opts.acc[i][j] = 0; + } + } + + nfrdim = str_nelem(is->frdim, MAXPTR, ptr1); + nfreeze = str_nelem(is->freeze, MAXPTR, ptr2); + if (nfrdim != DIM*nfreeze) + { + gmx_fatal(FARGS, "Invalid Freezing input: %d groups and %d freeze values", + nfreeze, nfrdim); + } + do_numbering(natoms, groups, nfreeze, ptr2, grps, gnames, egcFREEZE, + restnm, egrptpALL_GENREST, bVerbose, wi); + nr = groups->grps[egcFREEZE].nr; + ir->opts.ngfrz = nr; + snew(ir->opts.nFreeze, nr); + for (i = k = 0; (i < nfreeze); i++) + { + for (j = 0; (j < DIM); j++, k++) + { + ir->opts.nFreeze[i][j] = (gmx_strncasecmp(ptr1[k], "Y", 1) == 0); + if (!ir->opts.nFreeze[i][j]) + { + if (gmx_strncasecmp(ptr1[k], "N", 1) != 0) + { + sprintf(warnbuf, "Please use Y(ES) or N(O) for freezedim only " + "(not %s)", ptr1[k]); + warning(wi, warn_buf); + } + } + } + } + for (; (i < nr); i++) + { + for (j = 0; (j < DIM); j++) + { + ir->opts.nFreeze[i][j] = 0; + } + } + + nenergy = str_nelem(is->energy, MAXPTR, ptr1); + do_numbering(natoms, groups, nenergy, ptr1, grps, gnames, egcENER, + restnm, egrptpALL_GENREST, bVerbose, wi); + add_wall_energrps(groups, ir->nwall, symtab); + ir->opts.ngener = groups->grps[egcENER].nr; + nvcm = str_nelem(is->vcm, MAXPTR, ptr1); + bRest = + do_numbering(natoms, groups, nvcm, ptr1, grps, gnames, egcVCM, + restnm, nvcm == 0 ? egrptpALL_GENREST : egrptpPART, bVerbose, wi); + if (bRest) + { + warning(wi, "Some atoms are not part of any center of mass motion removal group.\n" + "This may lead to artifacts.\n" + "In most cases one should use one group for the whole system."); + } + + /* Now we have filled the freeze struct, so we can calculate NRDF */ + calc_nrdf(mtop, ir, gnames); + + if (v && NULL) + { + real fac, ntot = 0; + + /* Must check per group! */ + for (i = 0; (i < ir->opts.ngtc); i++) + { + ntot += ir->opts.nrdf[i]; + } + if (ntot != (DIM*natoms)) + { + fac = sqrt(ntot/(DIM*natoms)); + if (bVerbose) + { + fprintf(stderr, "Scaling velocities by a factor of %.3f to account for constraints\n" + "and removal of center of mass motion\n", fac); + } + for (i = 0; (i < natoms); i++) + { + svmul(fac, v[i], v[i]); + } + } + } + + nuser = str_nelem(is->user1, MAXPTR, ptr1); + do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser1, + restnm, egrptpALL_GENREST, bVerbose, wi); + nuser = str_nelem(is->user2, MAXPTR, ptr1); + do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser2, + restnm, egrptpALL_GENREST, bVerbose, wi); + nuser = str_nelem(is->x_compressed_groups, MAXPTR, ptr1); + do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcCompressedX, + restnm, egrptpONE, bVerbose, wi); + nofg = str_nelem(is->orirefitgrp, MAXPTR, ptr1); + do_numbering(natoms, groups, nofg, ptr1, grps, gnames, egcORFIT, + restnm, egrptpALL_GENREST, bVerbose, wi); + + /* QMMM input processing */ + nQMg = str_nelem(is->QMMM, MAXPTR, ptr1); + nQMmethod = str_nelem(is->QMmethod, MAXPTR, ptr2); + nQMbasis = str_nelem(is->QMbasis, MAXPTR, ptr3); + if ((nQMmethod != nQMg) || (nQMbasis != nQMg)) + { + gmx_fatal(FARGS, "Invalid QMMM input: %d groups %d basissets" + " and %d methods\n", nQMg, nQMbasis, nQMmethod); + } + /* group rest, if any, is always MM! */ + do_numbering(natoms, groups, nQMg, ptr1, grps, gnames, egcQMMM, + restnm, egrptpALL_GENREST, bVerbose, wi); + nr = nQMg; /*atoms->grps[egcQMMM].nr;*/ + ir->opts.ngQM = nQMg; + snew(ir->opts.QMmethod, nr); + snew(ir->opts.QMbasis, nr); + for (i = 0; i < nr; i++) + { + /* input consists of strings: RHF CASSCF PM3 .. These need to be + * converted to the corresponding enum in names.c + */ + ir->opts.QMmethod[i] = search_QMstring(ptr2[i], eQMmethodNR, + eQMmethod_names); + ir->opts.QMbasis[i] = search_QMstring(ptr3[i], eQMbasisNR, + eQMbasis_names); + + } + nQMmult = str_nelem(is->QMmult, MAXPTR, ptr1); + nQMcharge = str_nelem(is->QMcharge, MAXPTR, ptr2); + nbSH = str_nelem(is->bSH, MAXPTR, ptr3); + snew(ir->opts.QMmult, nr); + snew(ir->opts.QMcharge, nr); + snew(ir->opts.bSH, nr); + + for (i = 0; i < nr; i++) + { + ir->opts.QMmult[i] = strtol(ptr1[i], NULL, 10); + ir->opts.QMcharge[i] = strtol(ptr2[i], NULL, 10); + ir->opts.bSH[i] = (gmx_strncasecmp(ptr3[i], "Y", 1) == 0); + } + + nCASelec = str_nelem(is->CASelectrons, MAXPTR, ptr1); + nCASorb = str_nelem(is->CASorbitals, MAXPTR, ptr2); + snew(ir->opts.CASelectrons, nr); + snew(ir->opts.CASorbitals, nr); + for (i = 0; i < nr; i++) + { + ir->opts.CASelectrons[i] = strtol(ptr1[i], NULL, 10); + ir->opts.CASorbitals[i] = strtol(ptr2[i], NULL, 10); + } + /* special optimization options */ + + nbOPT = str_nelem(is->bOPT, MAXPTR, ptr1); + nbTS = str_nelem(is->bTS, MAXPTR, ptr2); + snew(ir->opts.bOPT, nr); + snew(ir->opts.bTS, nr); + for (i = 0; i < nr; i++) + { + ir->opts.bOPT[i] = (gmx_strncasecmp(ptr1[i], "Y", 1) == 0); + ir->opts.bTS[i] = (gmx_strncasecmp(ptr2[i], "Y", 1) == 0); + } + nSAon = str_nelem(is->SAon, MAXPTR, ptr1); + nSAoff = str_nelem(is->SAoff, MAXPTR, ptr2); + nSAsteps = str_nelem(is->SAsteps, MAXPTR, ptr3); + snew(ir->opts.SAon, nr); + snew(ir->opts.SAoff, nr); + snew(ir->opts.SAsteps, nr); + + for (i = 0; i < nr; i++) + { + ir->opts.SAon[i] = strtod(ptr1[i], NULL); + ir->opts.SAoff[i] = strtod(ptr2[i], NULL); + ir->opts.SAsteps[i] = strtol(ptr3[i], NULL, 10); + } + /* end of QMMM input */ + + if (bVerbose) + { + for (i = 0; (i < egcNR); i++) + { + fprintf(stderr, "%-16s has %d element(s):", gtypes[i], groups->grps[i].nr); + for (j = 0; (j < groups->grps[i].nr); j++) + { + fprintf(stderr, " %s", *(groups->grpname[groups->grps[i].nm_ind[j]])); + } + fprintf(stderr, "\n"); + } + } + + nr = groups->grps[egcENER].nr; + snew(ir->opts.egp_flags, nr*nr); + + bExcl = do_egp_flag(ir, groups, "energygrp-excl", is->egpexcl, EGP_EXCL); + if (bExcl && ir->cutoff_scheme == ecutsVERLET) + { + warning_error(wi, "Energy group exclusions are not (yet) implemented for the Verlet scheme"); + } + if (bExcl && EEL_FULL(ir->coulombtype)) + { + warning(wi, "Can not exclude the lattice Coulomb energy between energy groups"); + } + + bTable = do_egp_flag(ir, groups, "energygrp-table", is->egptable, EGP_TABLE); + if (bTable && !(ir->vdwtype == evdwUSER) && + !(ir->coulombtype == eelUSER) && !(ir->coulombtype == eelPMEUSER) && + !(ir->coulombtype == eelPMEUSERSWITCH)) + { + gmx_fatal(FARGS, "Can only have energy group pair tables in combination with user tables for VdW and/or Coulomb"); + } + + decode_cos(is->efield_x, &(ir->ex[XX])); + decode_cos(is->efield_xt, &(ir->et[XX])); + decode_cos(is->efield_y, &(ir->ex[YY])); + decode_cos(is->efield_yt, &(ir->et[YY])); + decode_cos(is->efield_z, &(ir->ex[ZZ])); + decode_cos(is->efield_zt, &(ir->et[ZZ])); + + if (ir->bAdress) + { + do_adress_index(ir->adress, groups, gnames, &(ir->opts), wi); + } + + for (i = 0; (i < grps->nr); i++) + { + sfree(gnames[i]); + } + sfree(gnames); + done_blocka(grps); + sfree(grps); + +} + + + +static void check_disre(gmx_mtop_t *mtop) +{ + gmx_ffparams_t *ffparams; + t_functype *functype; + t_iparams *ip; + int i, ndouble, ftype; + int label, old_label; + + if (gmx_mtop_ftype_count(mtop, F_DISRES) > 0) + { + ffparams = &mtop->ffparams; + functype = ffparams->functype; + ip = ffparams->iparams; + ndouble = 0; + old_label = -1; + for (i = 0; i < ffparams->ntypes; i++) + { + ftype = functype[i]; + if (ftype == F_DISRES) + { + label = ip[i].disres.label; + if (label == old_label) + { + fprintf(stderr, "Distance restraint index %d occurs twice\n", label); + ndouble++; + } + old_label = label; + } + } + if (ndouble > 0) + { + gmx_fatal(FARGS, "Found %d double distance restraint indices,\n" + "probably the parameters for multiple pairs in one restraint " + "are not identical\n", ndouble); + } + } +} + +static gmx_bool absolute_reference(t_inputrec *ir, gmx_mtop_t *sys, + gmx_bool posres_only, + ivec AbsRef) +{ + int d, g, i; + gmx_mtop_ilistloop_t iloop; + t_ilist *ilist; + int nmol; + t_iparams *pr; + + clear_ivec(AbsRef); + + if (!posres_only) + { + /* Check the COM */ + for (d = 0; d < DIM; d++) + { + AbsRef[d] = (d < ndof_com(ir) ? 0 : 1); + } + /* Check for freeze groups */ + for (g = 0; g < ir->opts.ngfrz; g++) + { + for (d = 0; d < DIM; d++) + { + if (ir->opts.nFreeze[g][d] != 0) + { + AbsRef[d] = 1; + } + } + } + } + + /* Check for position restraints */ + iloop = gmx_mtop_ilistloop_init(sys); + while (gmx_mtop_ilistloop_next(iloop, &ilist, &nmol)) + { + if (nmol > 0 && + (AbsRef[XX] == 0 || AbsRef[YY] == 0 || AbsRef[ZZ] == 0)) + { + for (i = 0; i < ilist[F_POSRES].nr; i += 2) + { + pr = &sys->ffparams.iparams[ilist[F_POSRES].iatoms[i]]; + for (d = 0; d < DIM; d++) + { + if (pr->posres.fcA[d] != 0) + { + AbsRef[d] = 1; + } + } + } + for (i = 0; i < ilist[F_FBPOSRES].nr; i += 2) + { + /* Check for flat-bottom posres */ + pr = &sys->ffparams.iparams[ilist[F_FBPOSRES].iatoms[i]]; + if (pr->fbposres.k != 0) + { + switch (pr->fbposres.geom) + { + case efbposresSPHERE: + AbsRef[XX] = AbsRef[YY] = AbsRef[ZZ] = 1; + break; + case efbposresCYLINDER: + AbsRef[XX] = AbsRef[YY] = 1; + break; + case efbposresX: /* d=XX */ + case efbposresY: /* d=YY */ + case efbposresZ: /* d=ZZ */ + d = pr->fbposres.geom - efbposresX; + AbsRef[d] = 1; + break; + default: + gmx_fatal(FARGS, " Invalid geometry for flat-bottom position restraint.\n" + "Expected nr between 1 and %d. Found %d\n", efbposresNR-1, + pr->fbposres.geom); + } + } + } + } + } + + return (AbsRef[XX] != 0 && AbsRef[YY] != 0 && AbsRef[ZZ] != 0); +} + +static void +check_combination_rule_differences(const gmx_mtop_t *mtop, int state, + gmx_bool *bC6ParametersWorkWithGeometricRules, + gmx_bool *bC6ParametersWorkWithLBRules, + gmx_bool *bLBRulesPossible) +{ + int ntypes, tpi, tpj, thisLBdiff, thisgeomdiff; + int *typecount; + real tol; + double geometricdiff, LBdiff; + double c6i, c6j, c12i, c12j; + double c6, c6_geometric, c6_LB; + double sigmai, sigmaj, epsi, epsj; + gmx_bool bCanDoLBRules, bCanDoGeometricRules; + const char *ptr; + + /* A tolerance of 1e-5 seems reasonable for (possibly hand-typed) + * force-field floating point parameters. + */ + tol = 1e-5; + ptr = getenv("GMX_LJCOMB_TOL"); + if (ptr != NULL) + { + double dbl; + + sscanf(ptr, "%lf", &dbl); + tol = dbl; + } + + *bC6ParametersWorkWithLBRules = TRUE; + *bC6ParametersWorkWithGeometricRules = TRUE; + bCanDoLBRules = TRUE; + bCanDoGeometricRules = TRUE; + ntypes = mtop->ffparams.atnr; + snew(typecount, ntypes); + gmx_mtop_count_atomtypes(mtop, state, typecount); + geometricdiff = LBdiff = 0.0; + *bLBRulesPossible = TRUE; + for (tpi = 0; tpi < ntypes; ++tpi) + { + c6i = mtop->ffparams.iparams[(ntypes + 1) * tpi].lj.c6; + c12i = mtop->ffparams.iparams[(ntypes + 1) * tpi].lj.c12; + for (tpj = tpi; tpj < ntypes; ++tpj) + { + c6j = mtop->ffparams.iparams[(ntypes + 1) * tpj].lj.c6; + c12j = mtop->ffparams.iparams[(ntypes + 1) * tpj].lj.c12; + c6 = mtop->ffparams.iparams[ntypes * tpi + tpj].lj.c6; + c6_geometric = sqrt(c6i * c6j); + if (!gmx_numzero(c6_geometric)) + { + if (!gmx_numzero(c12i) && !gmx_numzero(c12j)) + { + sigmai = pow(c12i / c6i, 1.0/6.0); + sigmaj = pow(c12j / c6j, 1.0/6.0); + epsi = c6i * c6i /(4.0 * c12i); + epsj = c6j * c6j /(4.0 * c12j); + c6_LB = 4.0 * pow(epsi * epsj, 1.0/2.0) * pow(0.5 * (sigmai + sigmaj), 6); + } + else + { + *bLBRulesPossible = FALSE; + c6_LB = c6_geometric; + } + bCanDoLBRules = gmx_within_tol(c6_LB, c6, tol); + } + + if (FALSE == bCanDoLBRules) + { + *bC6ParametersWorkWithLBRules = FALSE; + } + + bCanDoGeometricRules = gmx_within_tol(c6_geometric, c6, tol); + + if (FALSE == bCanDoGeometricRules) + { + *bC6ParametersWorkWithGeometricRules = FALSE; + } + } + } + sfree(typecount); +} + +static void +check_combination_rules(const t_inputrec *ir, const gmx_mtop_t *mtop, + warninp_t wi) +{ + char err_buf[256]; + gmx_bool bLBRulesPossible, bC6ParametersWorkWithGeometricRules, bC6ParametersWorkWithLBRules; + + check_combination_rule_differences(mtop, 0, + &bC6ParametersWorkWithGeometricRules, + &bC6ParametersWorkWithLBRules, + &bLBRulesPossible); + if (ir->ljpme_combination_rule == eljpmeLB) + { + if (FALSE == bC6ParametersWorkWithLBRules || FALSE == bLBRulesPossible) + { + warning(wi, "You are using arithmetic-geometric combination rules " + "in LJ-PME, but your non-bonded C6 parameters do not " + "follow these rules."); + } + } + else + { + if (FALSE == bC6ParametersWorkWithGeometricRules) + { + if (ir->eDispCorr != edispcNO) + { + warning_note(wi, "You are using geometric combination rules in " + "LJ-PME, but your non-bonded C6 parameters do " + "not follow these rules. " + "This will introduce very small errors in the forces and energies in " + "your simulations. Dispersion correction will correct total energy " + "and/or pressure for isotropic systems, but not forces or surface tensions."); + } + else + { + warning_note(wi, "You are using geometric combination rules in " + "LJ-PME, but your non-bonded C6 parameters do " + "not follow these rules. " + "This will introduce very small errors in the forces and energies in " + "your simulations. If your system is homogeneous, consider using dispersion correction " + "for the total energy and pressure."); + } + } + } +} + +void triple_check(const char *mdparin, t_inputrec *ir, gmx_mtop_t *sys, + warninp_t wi) +{ + char err_buf[STRLEN]; + int i, m, c, nmol, npct; + gmx_bool bCharge, bAcc; + real gdt_max, *mgrp, mt; + rvec acc; + gmx_mtop_atomloop_block_t aloopb; + gmx_mtop_atomloop_all_t aloop; + t_atom *atom; + ivec AbsRef; + char warn_buf[STRLEN]; + + set_warning_line(wi, mdparin, -1); + + if (ir->cutoff_scheme == ecutsVERLET && + ir->verletbuf_tol > 0 && + ir->nstlist > 1 && + ((EI_MD(ir->eI) || EI_SD(ir->eI)) && + (ir->etc == etcVRESCALE || ir->etc == etcBERENDSEN))) + { + /* Check if a too small Verlet buffer might potentially + * cause more drift than the thermostat can couple off. + */ + /* Temperature error fraction for warning and suggestion */ + const real T_error_warn = 0.002; + const real T_error_suggest = 0.001; + /* For safety: 2 DOF per atom (typical with constraints) */ + const real nrdf_at = 2; + real T, tau, max_T_error; + int i; + + T = 0; + tau = 0; + for (i = 0; i < ir->opts.ngtc; i++) + { + T = max(T, ir->opts.ref_t[i]); + tau = max(tau, ir->opts.tau_t[i]); + } + if (T > 0) + { + /* This is a worst case estimate of the temperature error, + * assuming perfect buffer estimation and no cancelation + * of errors. The factor 0.5 is because energy distributes + * equally over Ekin and Epot. + */ + max_T_error = 0.5*tau*ir->verletbuf_tol/(nrdf_at*BOLTZ*T); + if (max_T_error > T_error_warn) + { + sprintf(warn_buf, "With a verlet-buffer-tolerance of %g kJ/mol/ps, a reference temperature of %g and a tau_t of %g, your temperature might be off by up to %.1f%%. To ensure the error is below %.1f%%, decrease verlet-buffer-tolerance to %.0e or decrease tau_t.", + ir->verletbuf_tol, T, tau, + 100*max_T_error, + 100*T_error_suggest, + ir->verletbuf_tol*T_error_suggest/max_T_error); + warning(wi, warn_buf); + } + } + } + + if (ETC_ANDERSEN(ir->etc)) + { + int i; + + for (i = 0; i < ir->opts.ngtc; i++) + { + sprintf(err_buf, "all tau_t must currently be equal using Andersen temperature control, violated for group %d", i); + CHECK(ir->opts.tau_t[0] != ir->opts.tau_t[i]); + sprintf(err_buf, "all tau_t must be postive using Andersen temperature control, tau_t[%d]=%10.6f", + i, ir->opts.tau_t[i]); + CHECK(ir->opts.tau_t[i] < 0); + } + + for (i = 0; i < ir->opts.ngtc; i++) + { + int nsteps = (int)(ir->opts.tau_t[i]/ir->delta_t); + sprintf(err_buf, "tau_t/delta_t for group %d for temperature control method %s must be a multiple of nstcomm (%d), as velocities of atoms in coupled groups are randomized every time step. The input tau_t (%8.3f) leads to %d steps per randomization", i, etcoupl_names[ir->etc], ir->nstcomm, ir->opts.tau_t[i], nsteps); + CHECK((nsteps % ir->nstcomm) && (ir->etc == etcANDERSENMASSIVE)); + } + } + + if (EI_DYNAMICS(ir->eI) && !EI_SD(ir->eI) && ir->eI != eiBD && + ir->comm_mode == ecmNO && + !(absolute_reference(ir, sys, FALSE, AbsRef) || ir->nsteps <= 10) && + !ETC_ANDERSEN(ir->etc)) + { + warning(wi, "You are not using center of mass motion removal (mdp option comm-mode), numerical rounding errors can lead to build up of kinetic energy of the center of mass"); + } + + /* Check for pressure coupling with absolute position restraints */ + if (ir->epc != epcNO && ir->refcoord_scaling == erscNO) + { + absolute_reference(ir, sys, TRUE, AbsRef); + { + for (m = 0; m < DIM; m++) + { + if (AbsRef[m] && norm2(ir->compress[m]) > 0) + { + warning(wi, "You are using pressure coupling with absolute position restraints, this will give artifacts. Use the refcoord_scaling option."); + break; + } + } + } + } + + bCharge = FALSE; + aloopb = gmx_mtop_atomloop_block_init(sys); + while (gmx_mtop_atomloop_block_next(aloopb, &atom, &nmol)) + { + if (atom->q != 0 || atom->qB != 0) + { + bCharge = TRUE; + } + } + + if (!bCharge) + { + if (EEL_FULL(ir->coulombtype)) + { + sprintf(err_buf, + "You are using full electrostatics treatment %s for a system without charges.\n" + "This costs a lot of performance for just processing zeros, consider using %s instead.\n", + EELTYPE(ir->coulombtype), EELTYPE(eelCUT)); + warning(wi, err_buf); + } + } + else + { + if (ir->coulombtype == eelCUT && ir->rcoulomb > 0 && !ir->implicit_solvent) + { + sprintf(err_buf, + "You are using a plain Coulomb cut-off, which might produce artifacts.\n" + "You might want to consider using %s electrostatics.\n", + EELTYPE(eelPME)); + warning_note(wi, err_buf); + } + } + + /* Check if combination rules used in LJ-PME are the same as in the force field */ + if (EVDW_PME(ir->vdwtype)) + { + check_combination_rules(ir, sys, wi); + } + + /* Generalized reaction field */ + if (ir->opts.ngtc == 0) + { + sprintf(err_buf, "No temperature coupling while using coulombtype %s", + eel_names[eelGRF]); + CHECK(ir->coulombtype == eelGRF); + } + else + { + sprintf(err_buf, "When using coulombtype = %s" + " ref-t for temperature coupling should be > 0", + eel_names[eelGRF]); + CHECK((ir->coulombtype == eelGRF) && (ir->opts.ref_t[0] <= 0)); + } + + if (ir->eI == eiSD1 && + (gmx_mtop_ftype_count(sys, F_CONSTR) > 0 || + gmx_mtop_ftype_count(sys, F_SETTLE) > 0)) + { + sprintf(warn_buf, "With constraints integrator %s is less accurate, consider using %s instead", ei_names[ir->eI], ei_names[eiSD2]); + warning_note(wi, warn_buf); + } + + bAcc = FALSE; + for (i = 0; (i < sys->groups.grps[egcACC].nr); i++) + { + for (m = 0; (m < DIM); m++) + { + if (fabs(ir->opts.acc[i][m]) > 1e-6) + { + bAcc = TRUE; + } + } + } + if (bAcc) + { + clear_rvec(acc); + snew(mgrp, sys->groups.grps[egcACC].nr); + aloop = gmx_mtop_atomloop_all_init(sys); + while (gmx_mtop_atomloop_all_next(aloop, &i, &atom)) + { + mgrp[ggrpnr(&sys->groups, egcACC, i)] += atom->m; + } + mt = 0.0; + for (i = 0; (i < sys->groups.grps[egcACC].nr); i++) + { + for (m = 0; (m < DIM); m++) + { + acc[m] += ir->opts.acc[i][m]*mgrp[i]; + } + mt += mgrp[i]; + } + for (m = 0; (m < DIM); m++) + { + if (fabs(acc[m]) > 1e-6) + { + const char *dim[DIM] = { "X", "Y", "Z" }; + fprintf(stderr, + "Net Acceleration in %s direction, will %s be corrected\n", + dim[m], ir->nstcomm != 0 ? "" : "not"); + if (ir->nstcomm != 0 && m < ndof_com(ir)) + { + acc[m] /= mt; + for (i = 0; (i < sys->groups.grps[egcACC].nr); i++) + { + ir->opts.acc[i][m] -= acc[m]; + } + } + } + } + sfree(mgrp); + } + + if (ir->efep != efepNO && ir->fepvals->sc_alpha != 0 && + !gmx_within_tol(sys->ffparams.reppow, 12.0, 10*GMX_DOUBLE_EPS)) + { + gmx_fatal(FARGS, "Soft-core interactions are only supported with VdW repulsion power 12"); + } + + if (ir->ePull != epullNO) + { + gmx_bool bPullAbsoluteRef; + + bPullAbsoluteRef = FALSE; + for (i = 0; i < ir->pull->ncoord; i++) + { + bPullAbsoluteRef = bPullAbsoluteRef || + ir->pull->coord[i].group[0] == 0 || + ir->pull->coord[i].group[1] == 0; + } + if (bPullAbsoluteRef) + { + absolute_reference(ir, sys, FALSE, AbsRef); + for (m = 0; m < DIM; m++) + { + if (ir->pull->dim[m] && !AbsRef[m]) + { + warning(wi, "You are using an absolute reference for pulling, but the rest of the system does not have an absolute reference. This will lead to artifacts."); + break; + } + } + } + + if (ir->pull->eGeom == epullgDIRPBC) + { + for (i = 0; i < 3; i++) + { + for (m = 0; m <= i; m++) + { + if ((ir->epc != epcNO && ir->compress[i][m] != 0) || + ir->deform[i][m] != 0) + { + for (c = 0; c < ir->pull->ncoord; c++) + { + if (ir->pull->coord[c].vec[m] != 0) + { + gmx_fatal(FARGS, "Can not have dynamic box while using pull geometry '%s' (dim %c)", EPULLGEOM(ir->pull->eGeom), 'x'+m); + } + } + } + } + } + } + } + + check_disre(sys); +} + +void double_check(t_inputrec *ir, matrix box, gmx_bool bConstr, warninp_t wi) +{ + real min_size; + gmx_bool bTWIN; + char warn_buf[STRLEN]; + const char *ptr; + + ptr = check_box(ir->ePBC, box); + if (ptr) + { + warning_error(wi, ptr); + } + + if (bConstr && ir->eConstrAlg == econtSHAKE) + { + if (ir->shake_tol <= 0.0) + { + sprintf(warn_buf, "ERROR: shake-tol must be > 0 instead of %g\n", + ir->shake_tol); + warning_error(wi, warn_buf); + } + + if (IR_TWINRANGE(*ir) && ir->nstlist > 1) + { + sprintf(warn_buf, "With twin-range cut-off's and SHAKE the virial and the pressure are incorrect."); + if (ir->epc == epcNO) + { + warning(wi, warn_buf); + } + else + { + warning_error(wi, warn_buf); + } + } + } + + if ( (ir->eConstrAlg == econtLINCS) && bConstr) + { + /* If we have Lincs constraints: */ + if (ir->eI == eiMD && ir->etc == etcNO && + ir->eConstrAlg == econtLINCS && ir->nLincsIter == 1) + { + sprintf(warn_buf, "For energy conservation with LINCS, lincs_iter should be 2 or larger.\n"); + warning_note(wi, warn_buf); + } + + if ((ir->eI == eiCG || ir->eI == eiLBFGS) && (ir->nProjOrder < 8)) + { + sprintf(warn_buf, "For accurate %s with LINCS constraints, lincs-order should be 8 or more.", ei_names[ir->eI]); + warning_note(wi, warn_buf); + } + if (ir->epc == epcMTTK) + { + warning_error(wi, "MTTK not compatible with lincs -- use shake instead."); + } + } + + if (bConstr && ir->epc == epcMTTK) + { + warning_note(wi, "MTTK with constraints is deprecated, and will be removed in GROMACS 5.1"); + } + + if (ir->LincsWarnAngle > 90.0) + { + sprintf(warn_buf, "lincs-warnangle can not be larger than 90 degrees, setting it to 90.\n"); + warning(wi, warn_buf); + ir->LincsWarnAngle = 90.0; + } + + if (ir->ePBC != epbcNONE) + { + if (ir->nstlist == 0) + { + warning(wi, "With nstlist=0 atoms are only put into the box at step 0, therefore drifting atoms might cause the simulation to crash."); + } + bTWIN = (ir->rlistlong > ir->rlist); + if (ir->ns_type == ensGRID) + { + if (sqr(ir->rlistlong) >= max_cutoff2(ir->ePBC, box)) + { + sprintf(warn_buf, "ERROR: The cut-off length is longer than half the shortest box vector or longer than the smallest box diagonal element. Increase the box size or decrease %s.\n", + bTWIN ? (ir->rcoulomb == ir->rlistlong ? "rcoulomb" : "rvdw") : "rlist"); + warning_error(wi, warn_buf); + } + } + else + { + min_size = min(box[XX][XX], min(box[YY][YY], box[ZZ][ZZ])); + if (2*ir->rlistlong >= min_size) + { + sprintf(warn_buf, "ERROR: One of the box lengths is smaller than twice the cut-off length. Increase the box size or decrease rlist."); + warning_error(wi, warn_buf); + if (TRICLINIC(box)) + { + fprintf(stderr, "Grid search might allow larger cut-off's than simple search with triclinic boxes."); + } + } + } + } +} + +void check_chargegroup_radii(const gmx_mtop_t *mtop, const t_inputrec *ir, + rvec *x, + warninp_t wi) +{ + real rvdw1, rvdw2, rcoul1, rcoul2; + char warn_buf[STRLEN]; + + calc_chargegroup_radii(mtop, x, &rvdw1, &rvdw2, &rcoul1, &rcoul2); + + if (rvdw1 > 0) + { + printf("Largest charge group radii for Van der Waals: %5.3f, %5.3f nm\n", + rvdw1, rvdw2); + } + if (rcoul1 > 0) + { + printf("Largest charge group radii for Coulomb: %5.3f, %5.3f nm\n", + rcoul1, rcoul2); + } + + if (ir->rlist > 0) + { + if (rvdw1 + rvdw2 > ir->rlist || + rcoul1 + rcoul2 > ir->rlist) + { + sprintf(warn_buf, + "The sum of the two largest charge group radii (%f) " + "is larger than rlist (%f)\n", + max(rvdw1+rvdw2, rcoul1+rcoul2), ir->rlist); + warning(wi, warn_buf); + } + else + { + /* Here we do not use the zero at cut-off macro, + * since user defined interactions might purposely + * not be zero at the cut-off. + */ + if (ir_vdw_is_zero_at_cutoff(ir) && + rvdw1 + rvdw2 > ir->rlistlong - ir->rvdw) + { + sprintf(warn_buf, "The sum of the two largest charge group " + "radii (%f) is larger than %s (%f) - rvdw (%f).\n" + "With exact cut-offs, better performance can be " + "obtained with cutoff-scheme = %s, because it " + "does not use charge groups at all.", + rvdw1+rvdw2, + ir->rlistlong > ir->rlist ? "rlistlong" : "rlist", + ir->rlistlong, ir->rvdw, + ecutscheme_names[ecutsVERLET]); + if (ir_NVE(ir)) + { + warning(wi, warn_buf); + } + else + { + warning_note(wi, warn_buf); + } + } + if (ir_coulomb_is_zero_at_cutoff(ir) && + rcoul1 + rcoul2 > ir->rlistlong - ir->rcoulomb) + { + sprintf(warn_buf, "The sum of the two largest charge group radii (%f) is larger than %s (%f) - rcoulomb (%f).\n" + "With exact cut-offs, better performance can be obtained with cutoff-scheme = %s, because it does not use charge groups at all.", + rcoul1+rcoul2, + ir->rlistlong > ir->rlist ? "rlistlong" : "rlist", + ir->rlistlong, ir->rcoulomb, + ecutscheme_names[ecutsVERLET]); + if (ir_NVE(ir)) + { + warning(wi, warn_buf); + } + else + { + warning_note(wi, warn_buf); + } + } + } + } +} diff --cc src/gromacs/mdlib/forcerec.c index 67cd36f341,0000000000..8db62ab92d mode 100644,000000..100644 --- a/src/gromacs/mdlib/forcerec.c +++ b/src/gromacs/mdlib/forcerec.c @@@ -1,3283 -1,0 +1,3288 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include "sysstuff.h" +#include "typedefs.h" +#include "types/commrec.h" +#include "vec.h" +#include "gromacs/math/utilities.h" +#include "macros.h" +#include "gromacs/utility/smalloc.h" +#include "macros.h" +#include "gmx_fatal.h" +#include "physics.h" +#include "force.h" +#include "tables.h" +#include "nonbonded.h" +#include "invblock.h" +#include "names.h" +#include "network.h" +#include "pbc.h" +#include "ns.h" +#include "mshift.h" +#include "txtdump.h" +#include "coulomb.h" +#include "md_support.h" +#include "md_logging.h" +#include "domdec.h" +#include "qmmm.h" +#include "copyrite.h" +#include "mtop_util.h" +#include "nbnxn_simd.h" +#include "nbnxn_search.h" +#include "nbnxn_atomdata.h" +#include "nbnxn_consts.h" +#include "gmx_omp_nthreads.h" +#include "gmx_detect_hardware.h" +#include "inputrec.h" + +#include "types/nbnxn_cuda_types_ext.h" +#include "gpu_utils.h" +#include "nbnxn_cuda_data_mgmt.h" +#include "pmalloc_cuda.h" + +t_forcerec *mk_forcerec(void) +{ + t_forcerec *fr; + + snew(fr, 1); + + return fr; +} + +#ifdef DEBUG +static void pr_nbfp(FILE *fp, real *nbfp, gmx_bool bBHAM, int atnr) +{ + int i, j; + + for (i = 0; (i < atnr); i++) + { + for (j = 0; (j < atnr); j++) + { + fprintf(fp, "%2d - %2d", i, j); + if (bBHAM) + { + fprintf(fp, " a=%10g, b=%10g, c=%10g\n", BHAMA(nbfp, atnr, i, j), + BHAMB(nbfp, atnr, i, j), BHAMC(nbfp, atnr, i, j)/6.0); + } + else + { + fprintf(fp, " c6=%10g, c12=%10g\n", C6(nbfp, atnr, i, j)/6.0, + C12(nbfp, atnr, i, j)/12.0); + } + } + } +} +#endif + +static real *mk_nbfp(const gmx_ffparams_t *idef, gmx_bool bBHAM) +{ + real *nbfp; + int i, j, k, atnr; + + atnr = idef->atnr; + if (bBHAM) + { + snew(nbfp, 3*atnr*atnr); + for (i = k = 0; (i < atnr); i++) + { + for (j = 0; (j < atnr); j++, k++) + { + BHAMA(nbfp, atnr, i, j) = idef->iparams[k].bham.a; + BHAMB(nbfp, atnr, i, j) = idef->iparams[k].bham.b; + /* nbfp now includes the 6.0 derivative prefactor */ + BHAMC(nbfp, atnr, i, j) = idef->iparams[k].bham.c*6.0; + } + } + } + else + { + snew(nbfp, 2*atnr*atnr); + for (i = k = 0; (i < atnr); i++) + { + for (j = 0; (j < atnr); j++, k++) + { + /* nbfp now includes the 6.0/12.0 derivative prefactors */ + C6(nbfp, atnr, i, j) = idef->iparams[k].lj.c6*6.0; + C12(nbfp, atnr, i, j) = idef->iparams[k].lj.c12*12.0; + } + } + } + + return nbfp; +} + +static real *make_ljpme_c6grid(const gmx_ffparams_t *idef, t_forcerec *fr) +{ + int i, j, k, atnr; + real c6, c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj; + real *grid; + + /* For LJ-PME simulations, we correct the energies with the reciprocal space + * inside of the cut-off. To do this the non-bonded kernels needs to have + * access to the C6-values used on the reciprocal grid in pme.c + */ + + atnr = idef->atnr; + snew(grid, 2*atnr*atnr); + for (i = k = 0; (i < atnr); i++) + { + for (j = 0; (j < atnr); j++, k++) + { + c6i = idef->iparams[i*(atnr+1)].lj.c6; + c12i = idef->iparams[i*(atnr+1)].lj.c12; + c6j = idef->iparams[j*(atnr+1)].lj.c6; + c12j = idef->iparams[j*(atnr+1)].lj.c12; + c6 = sqrt(c6i * c6j); + if (fr->ljpme_combination_rule == eljpmeLB + && !gmx_numzero(c6) && !gmx_numzero(c12i) && !gmx_numzero(c12j)) + { + sigmai = pow(c12i / c6i, 1.0/6.0); + sigmaj = pow(c12j / c6j, 1.0/6.0); + epsi = c6i * c6i / c12i; + epsj = c6j * c6j / c12j; + c6 = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 6); + } + /* Store the elements at the same relative positions as C6 in nbfp in order + * to simplify access in the kernels + */ + grid[2*(atnr*i+j)] = c6*6.0; + } + } + return grid; +} + +static real *mk_nbfp_combination_rule(const gmx_ffparams_t *idef, int comb_rule) +{ + real *nbfp; + int i, j, k, atnr; + real c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj; + real c6, c12; + + atnr = idef->atnr; + snew(nbfp, 2*atnr*atnr); + for (i = 0; i < atnr; ++i) + { + for (j = 0; j < atnr; ++j) + { + c6i = idef->iparams[i*(atnr+1)].lj.c6; + c12i = idef->iparams[i*(atnr+1)].lj.c12; + c6j = idef->iparams[j*(atnr+1)].lj.c6; + c12j = idef->iparams[j*(atnr+1)].lj.c12; + c6 = sqrt(c6i * c6j); + c12 = sqrt(c12i * c12j); + if (comb_rule == eCOMB_ARITHMETIC + && !gmx_numzero(c6) && !gmx_numzero(c12)) + { + sigmai = pow(c12i / c6i, 1.0/6.0); + sigmaj = pow(c12j / c6j, 1.0/6.0); + epsi = c6i * c6i / c12i; + epsj = c6j * c6j / c12j; + c6 = epsi * epsj * pow(0.5*(sigmai+sigmaj), 6); + c12 = epsi * epsj * pow(0.5*(sigmai+sigmaj), 12); + } + C6(nbfp, atnr, i, j) = c6*6.0; + C12(nbfp, atnr, i, j) = c12*12.0; + } + } + return nbfp; +} + +/* This routine sets fr->solvent_opt to the most common solvent in the + * system, e.g. esolSPC or esolTIP4P. It will also mark each charge group in + * the fr->solvent_type array with the correct type (or esolNO). + * + * Charge groups that fulfill the conditions but are not identical to the + * most common one will be marked as esolNO in the solvent_type array. + * + * TIP3p is identical to SPC for these purposes, so we call it + * SPC in the arrays (Apologies to Bill Jorgensen ;-) + * + * NOTE: QM particle should not + * become an optimized solvent. Not even if there is only one charge + * group in the Qm + */ + +typedef struct +{ + int model; + int count; + int vdwtype[4]; + real charge[4]; +} solvent_parameters_t; + +static void +check_solvent_cg(const gmx_moltype_t *molt, + int cg0, + int nmol, + const unsigned char *qm_grpnr, + const t_grps *qm_grps, + t_forcerec * fr, + int *n_solvent_parameters, + solvent_parameters_t **solvent_parameters_p, + int cginfo, + int *cg_sp) +{ + const t_blocka *excl; + t_atom *atom; + int j, k; + int j0, j1, nj; + gmx_bool perturbed; + gmx_bool has_vdw[4]; + gmx_bool match; + real tmp_charge[4] = { 0.0 }; /* init to zero to make gcc4.8 happy */ + int tmp_vdwtype[4] = { 0 }; /* init to zero to make gcc4.8 happy */ + int tjA; + gmx_bool qm; + solvent_parameters_t *solvent_parameters; + + /* We use a list with parameters for each solvent type. + * Every time we discover a new molecule that fulfills the basic + * conditions for a solvent we compare with the previous entries + * in these lists. If the parameters are the same we just increment + * the counter for that type, and otherwise we create a new type + * based on the current molecule. + * + * Once we've finished going through all molecules we check which + * solvent is most common, and mark all those molecules while we + * clear the flag on all others. + */ + + solvent_parameters = *solvent_parameters_p; + + /* Mark the cg first as non optimized */ + *cg_sp = -1; + + /* Check if this cg has no exclusions with atoms in other charge groups + * and all atoms inside the charge group excluded. + * We only have 3 or 4 atom solvent loops. + */ + if (GET_CGINFO_EXCL_INTER(cginfo) || + !GET_CGINFO_EXCL_INTRA(cginfo)) + { + return; + } + + /* Get the indices of the first atom in this charge group */ + j0 = molt->cgs.index[cg0]; + j1 = molt->cgs.index[cg0+1]; + + /* Number of atoms in our molecule */ + nj = j1 - j0; + + if (debug) + { + fprintf(debug, + "Moltype '%s': there are %d atoms in this charge group\n", + *molt->name, nj); + } + + /* Check if it could be an SPC (3 atoms) or TIP4p (4) water, + * otherwise skip it. + */ + if (nj < 3 || nj > 4) + { + return; + } + + /* Check if we are doing QM on this group */ + qm = FALSE; + if (qm_grpnr != NULL) + { + for (j = j0; j < j1 && !qm; j++) + { + qm = (qm_grpnr[j] < qm_grps->nr - 1); + } + } + /* Cannot use solvent optimization with QM */ + if (qm) + { + return; + } + + atom = molt->atoms.atom; + + /* Still looks like a solvent, time to check parameters */ + + /* If it is perturbed (free energy) we can't use the solvent loops, + * so then we just skip to the next molecule. + */ + perturbed = FALSE; + + for (j = j0; j < j1 && !perturbed; j++) + { + perturbed = PERTURBED(atom[j]); + } + + if (perturbed) + { + return; + } + + /* Now it's only a question if the VdW and charge parameters + * are OK. Before doing the check we compare and see if they are + * identical to a possible previous solvent type. + * First we assign the current types and charges. + */ + for (j = 0; j < nj; j++) + { + tmp_vdwtype[j] = atom[j0+j].type; + tmp_charge[j] = atom[j0+j].q; + } + + /* Does it match any previous solvent type? */ + for (k = 0; k < *n_solvent_parameters; k++) + { + match = TRUE; + + + /* We can only match SPC with 3 atoms and TIP4p with 4 atoms */ + if ( (solvent_parameters[k].model == esolSPC && nj != 3) || + (solvent_parameters[k].model == esolTIP4P && nj != 4) ) + { + match = FALSE; + } + + /* Check that types & charges match for all atoms in molecule */ + for (j = 0; j < nj && match == TRUE; j++) + { + if (tmp_vdwtype[j] != solvent_parameters[k].vdwtype[j]) + { + match = FALSE; + } + if (tmp_charge[j] != solvent_parameters[k].charge[j]) + { + match = FALSE; + } + } + if (match == TRUE) + { + /* Congratulations! We have a matched solvent. + * Flag it with this type for later processing. + */ + *cg_sp = k; + solvent_parameters[k].count += nmol; + + /* We are done with this charge group */ + return; + } + } + + /* If we get here, we have a tentative new solvent type. + * Before we add it we must check that it fulfills the requirements + * of the solvent optimized loops. First determine which atoms have + * VdW interactions. + */ + for (j = 0; j < nj; j++) + { + has_vdw[j] = FALSE; + tjA = tmp_vdwtype[j]; + + /* Go through all other tpes and see if any have non-zero + * VdW parameters when combined with this one. + */ + for (k = 0; k < fr->ntype && (has_vdw[j] == FALSE); k++) + { + /* We already checked that the atoms weren't perturbed, + * so we only need to check state A now. + */ + if (fr->bBHAM) + { + has_vdw[j] = (has_vdw[j] || + (BHAMA(fr->nbfp, fr->ntype, tjA, k) != 0.0) || + (BHAMB(fr->nbfp, fr->ntype, tjA, k) != 0.0) || + (BHAMC(fr->nbfp, fr->ntype, tjA, k) != 0.0)); + } + else + { + /* Standard LJ */ + has_vdw[j] = (has_vdw[j] || + (C6(fr->nbfp, fr->ntype, tjA, k) != 0.0) || + (C12(fr->nbfp, fr->ntype, tjA, k) != 0.0)); + } + } + } + + /* Now we know all we need to make the final check and assignment. */ + if (nj == 3) + { + /* So, is it an SPC? + * For this we require thatn all atoms have charge, + * the charges on atom 2 & 3 should be the same, and only + * atom 1 might have VdW. + */ + if (has_vdw[1] == FALSE && + has_vdw[2] == FALSE && + tmp_charge[0] != 0 && + tmp_charge[1] != 0 && + tmp_charge[2] == tmp_charge[1]) + { + srenew(solvent_parameters, *n_solvent_parameters+1); + solvent_parameters[*n_solvent_parameters].model = esolSPC; + solvent_parameters[*n_solvent_parameters].count = nmol; + for (k = 0; k < 3; k++) + { + solvent_parameters[*n_solvent_parameters].vdwtype[k] = tmp_vdwtype[k]; + solvent_parameters[*n_solvent_parameters].charge[k] = tmp_charge[k]; + } + + *cg_sp = *n_solvent_parameters; + (*n_solvent_parameters)++; + } + } + else if (nj == 4) + { + /* Or could it be a TIP4P? + * For this we require thatn atoms 2,3,4 have charge, but not atom 1. + * Only atom 1 mght have VdW. + */ + if (has_vdw[1] == FALSE && + has_vdw[2] == FALSE && + has_vdw[3] == FALSE && + tmp_charge[0] == 0 && + tmp_charge[1] != 0 && + tmp_charge[2] == tmp_charge[1] && + tmp_charge[3] != 0) + { + srenew(solvent_parameters, *n_solvent_parameters+1); + solvent_parameters[*n_solvent_parameters].model = esolTIP4P; + solvent_parameters[*n_solvent_parameters].count = nmol; + for (k = 0; k < 4; k++) + { + solvent_parameters[*n_solvent_parameters].vdwtype[k] = tmp_vdwtype[k]; + solvent_parameters[*n_solvent_parameters].charge[k] = tmp_charge[k]; + } + + *cg_sp = *n_solvent_parameters; + (*n_solvent_parameters)++; + } + } + + *solvent_parameters_p = solvent_parameters; +} + +static void +check_solvent(FILE * fp, + const gmx_mtop_t * mtop, + t_forcerec * fr, + cginfo_mb_t *cginfo_mb) +{ + const t_block * cgs; + const t_block * mols; + const gmx_moltype_t *molt; + int mb, mol, cg_mol, at_offset, cg_offset, am, cgm, i, nmol_ch, nmol; + int n_solvent_parameters; + solvent_parameters_t *solvent_parameters; + int **cg_sp; + int bestsp, bestsol; + + if (debug) + { + fprintf(debug, "Going to determine what solvent types we have.\n"); + } + + mols = &mtop->mols; + + n_solvent_parameters = 0; + solvent_parameters = NULL; + /* Allocate temporary array for solvent type */ + snew(cg_sp, mtop->nmolblock); + + cg_offset = 0; + at_offset = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + molt = &mtop->moltype[mtop->molblock[mb].type]; + cgs = &molt->cgs; + /* Here we have to loop over all individual molecules + * because we need to check for QMMM particles. + */ + snew(cg_sp[mb], cginfo_mb[mb].cg_mod); + nmol_ch = cginfo_mb[mb].cg_mod/cgs->nr; + nmol = mtop->molblock[mb].nmol/nmol_ch; + for (mol = 0; mol < nmol_ch; mol++) + { + cgm = mol*cgs->nr; + am = mol*cgs->index[cgs->nr]; + for (cg_mol = 0; cg_mol < cgs->nr; cg_mol++) + { + check_solvent_cg(molt, cg_mol, nmol, + mtop->groups.grpnr[egcQMMM] ? + mtop->groups.grpnr[egcQMMM]+at_offset+am : 0, + &mtop->groups.grps[egcQMMM], + fr, + &n_solvent_parameters, &solvent_parameters, + cginfo_mb[mb].cginfo[cgm+cg_mol], + &cg_sp[mb][cgm+cg_mol]); + } + } + cg_offset += cgs->nr; + at_offset += cgs->index[cgs->nr]; + } + + /* Puh! We finished going through all charge groups. + * Now find the most common solvent model. + */ + + /* Most common solvent this far */ + bestsp = -2; + for (i = 0; i < n_solvent_parameters; i++) + { + if (bestsp == -2 || + solvent_parameters[i].count > solvent_parameters[bestsp].count) + { + bestsp = i; + } + } + + if (bestsp >= 0) + { + bestsol = solvent_parameters[bestsp].model; + } + else + { + bestsol = esolNO; + } + +#ifdef DISABLE_WATER_NLIST + bestsol = esolNO; +#endif + + fr->nWatMol = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + cgs = &mtop->moltype[mtop->molblock[mb].type].cgs; + nmol = (mtop->molblock[mb].nmol*cgs->nr)/cginfo_mb[mb].cg_mod; + for (i = 0; i < cginfo_mb[mb].cg_mod; i++) + { + if (cg_sp[mb][i] == bestsp) + { + SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[i], bestsol); + fr->nWatMol += nmol; + } + else + { + SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[i], esolNO); + } + } + sfree(cg_sp[mb]); + } + sfree(cg_sp); + + if (bestsol != esolNO && fp != NULL) + { + fprintf(fp, "\nEnabling %s-like water optimization for %d molecules.\n\n", + esol_names[bestsol], + solvent_parameters[bestsp].count); + } + + sfree(solvent_parameters); + fr->solvent_opt = bestsol; +} + +enum { + acNONE = 0, acCONSTRAINT, acSETTLE +}; + +static cginfo_mb_t *init_cginfo_mb(FILE *fplog, const gmx_mtop_t *mtop, + t_forcerec *fr, gmx_bool bNoSolvOpt, + gmx_bool *bFEP_NonBonded, + gmx_bool *bExcl_IntraCGAll_InterCGNone) +{ + const t_block *cgs; + const t_blocka *excl; + const gmx_moltype_t *molt; + const gmx_molblock_t *molb; + cginfo_mb_t *cginfo_mb; + gmx_bool *type_VDW; + int *cginfo; + int cg_offset, a_offset, cgm, am; + int mb, m, ncg_tot, cg, a0, a1, gid, ai, j, aj, excl_nalloc; + int *a_con; + int ftype; + int ia; + gmx_bool bId, *bExcl, bExclIntraAll, bExclInter, bHaveVDW, bHaveQ, bHavePerturbedAtoms; + + ncg_tot = ncg_mtop(mtop); + snew(cginfo_mb, mtop->nmolblock); + + snew(type_VDW, fr->ntype); + for (ai = 0; ai < fr->ntype; ai++) + { + type_VDW[ai] = FALSE; + for (j = 0; j < fr->ntype; j++) + { + type_VDW[ai] = type_VDW[ai] || + fr->bBHAM || + C6(fr->nbfp, fr->ntype, ai, j) != 0 || + C12(fr->nbfp, fr->ntype, ai, j) != 0; + } + } + + *bFEP_NonBonded = FALSE; + *bExcl_IntraCGAll_InterCGNone = TRUE; + + excl_nalloc = 10; + snew(bExcl, excl_nalloc); + cg_offset = 0; + a_offset = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + molb = &mtop->molblock[mb]; + molt = &mtop->moltype[molb->type]; + cgs = &molt->cgs; + excl = &molt->excls; + + /* Check if the cginfo is identical for all molecules in this block. + * If so, we only need an array of the size of one molecule. + * Otherwise we make an array of #mol times #cgs per molecule. + */ + bId = TRUE; + am = 0; + for (m = 0; m < molb->nmol; m++) + { + am = m*cgs->index[cgs->nr]; + for (cg = 0; cg < cgs->nr; cg++) + { + a0 = cgs->index[cg]; + a1 = cgs->index[cg+1]; + if (ggrpnr(&mtop->groups, egcENER, a_offset+am+a0) != + ggrpnr(&mtop->groups, egcENER, a_offset +a0)) + { + bId = FALSE; + } + if (mtop->groups.grpnr[egcQMMM] != NULL) + { + for (ai = a0; ai < a1; ai++) + { + if (mtop->groups.grpnr[egcQMMM][a_offset+am+ai] != + mtop->groups.grpnr[egcQMMM][a_offset +ai]) + { + bId = FALSE; + } + } + } + } + } + + cginfo_mb[mb].cg_start = cg_offset; + cginfo_mb[mb].cg_end = cg_offset + molb->nmol*cgs->nr; + cginfo_mb[mb].cg_mod = (bId ? 1 : molb->nmol)*cgs->nr; + snew(cginfo_mb[mb].cginfo, cginfo_mb[mb].cg_mod); + cginfo = cginfo_mb[mb].cginfo; + + /* Set constraints flags for constrained atoms */ + snew(a_con, molt->atoms.nr); + for (ftype = 0; ftype < F_NRE; ftype++) + { + if (interaction_function[ftype].flags & IF_CONSTRAINT) + { + int nral; + + nral = NRAL(ftype); + for (ia = 0; ia < molt->ilist[ftype].nr; ia += 1+nral) + { + int a; + + for (a = 0; a < nral; a++) + { + a_con[molt->ilist[ftype].iatoms[ia+1+a]] = + (ftype == F_SETTLE ? acSETTLE : acCONSTRAINT); + } + } + } + } + + for (m = 0; m < (bId ? 1 : molb->nmol); m++) + { + cgm = m*cgs->nr; + am = m*cgs->index[cgs->nr]; + for (cg = 0; cg < cgs->nr; cg++) + { + a0 = cgs->index[cg]; + a1 = cgs->index[cg+1]; + + /* Store the energy group in cginfo */ + gid = ggrpnr(&mtop->groups, egcENER, a_offset+am+a0); + SET_CGINFO_GID(cginfo[cgm+cg], gid); + + /* Check the intra/inter charge group exclusions */ + if (a1-a0 > excl_nalloc) + { + excl_nalloc = a1 - a0; + srenew(bExcl, excl_nalloc); + } + /* bExclIntraAll: all intra cg interactions excluded + * bExclInter: any inter cg interactions excluded + */ + bExclIntraAll = TRUE; + bExclInter = FALSE; + bHaveVDW = FALSE; + bHaveQ = FALSE; + bHavePerturbedAtoms = FALSE; + for (ai = a0; ai < a1; ai++) + { + /* Check VDW and electrostatic interactions */ + bHaveVDW = bHaveVDW || (type_VDW[molt->atoms.atom[ai].type] || + type_VDW[molt->atoms.atom[ai].typeB]); + bHaveQ = bHaveQ || (molt->atoms.atom[ai].q != 0 || + molt->atoms.atom[ai].qB != 0); + + bHavePerturbedAtoms = bHavePerturbedAtoms || (PERTURBED(molt->atoms.atom[ai]) != 0); + + /* Clear the exclusion list for atom ai */ + for (aj = a0; aj < a1; aj++) + { + bExcl[aj-a0] = FALSE; + } + /* Loop over all the exclusions of atom ai */ + for (j = excl->index[ai]; j < excl->index[ai+1]; j++) + { + aj = excl->a[j]; + if (aj < a0 || aj >= a1) + { + bExclInter = TRUE; + } + else + { + bExcl[aj-a0] = TRUE; + } + } + /* Check if ai excludes a0 to a1 */ + for (aj = a0; aj < a1; aj++) + { + if (!bExcl[aj-a0]) + { + bExclIntraAll = FALSE; + } + } + + switch (a_con[ai]) + { + case acCONSTRAINT: + SET_CGINFO_CONSTR(cginfo[cgm+cg]); + break; + case acSETTLE: + SET_CGINFO_SETTLE(cginfo[cgm+cg]); + break; + default: + break; + } + } + if (bExclIntraAll) + { + SET_CGINFO_EXCL_INTRA(cginfo[cgm+cg]); + } + if (bExclInter) + { + SET_CGINFO_EXCL_INTER(cginfo[cgm+cg]); + } + if (a1 - a0 > MAX_CHARGEGROUP_SIZE) + { + /* The size in cginfo is currently only read with DD */ + gmx_fatal(FARGS, "A charge group has size %d which is larger than the limit of %d atoms", a1-a0, MAX_CHARGEGROUP_SIZE); + } + if (bHaveVDW) + { + SET_CGINFO_HAS_VDW(cginfo[cgm+cg]); + } + if (bHaveQ) + { + SET_CGINFO_HAS_Q(cginfo[cgm+cg]); + } + if (bHavePerturbedAtoms && fr->efep != efepNO) + { + SET_CGINFO_FEP(cginfo[cgm+cg]); + *bFEP_NonBonded = TRUE; + } + /* Store the charge group size */ + SET_CGINFO_NATOMS(cginfo[cgm+cg], a1-a0); + + if (!bExclIntraAll || bExclInter) + { + *bExcl_IntraCGAll_InterCGNone = FALSE; + } + } + } + + sfree(a_con); + + cg_offset += molb->nmol*cgs->nr; + a_offset += molb->nmol*cgs->index[cgs->nr]; + } + sfree(bExcl); + + /* the solvent optimizer is called after the QM is initialized, + * because we don't want to have the QM subsystemto become an + * optimized solvent + */ + + check_solvent(fplog, mtop, fr, cginfo_mb); + + if (getenv("GMX_NO_SOLV_OPT")) + { + if (fplog) + { + fprintf(fplog, "Found environment variable GMX_NO_SOLV_OPT.\n" + "Disabling all solvent optimization\n"); + } + fr->solvent_opt = esolNO; + } + if (bNoSolvOpt) + { + fr->solvent_opt = esolNO; + } + if (!fr->solvent_opt) + { + for (mb = 0; mb < mtop->nmolblock; mb++) + { + for (cg = 0; cg < cginfo_mb[mb].cg_mod; cg++) + { + SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[cg], esolNO); + } + } + } + + return cginfo_mb; +} + +static int *cginfo_expand(int nmb, cginfo_mb_t *cgi_mb) +{ + int ncg, mb, cg; + int *cginfo; + + ncg = cgi_mb[nmb-1].cg_end; + snew(cginfo, ncg); + mb = 0; + for (cg = 0; cg < ncg; cg++) + { + while (cg >= cgi_mb[mb].cg_end) + { + mb++; + } + cginfo[cg] = + cgi_mb[mb].cginfo[(cg - cgi_mb[mb].cg_start) % cgi_mb[mb].cg_mod]; + } + + return cginfo; +} + +static void set_chargesum(FILE *log, t_forcerec *fr, const gmx_mtop_t *mtop) +{ + /*This now calculates sum for q and c6*/ + double qsum, q2sum, q, c6sum, c6; + int mb, nmol, i; + const t_atoms *atoms; + + qsum = 0; + q2sum = 0; + c6sum = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + nmol = mtop->molblock[mb].nmol; + atoms = &mtop->moltype[mtop->molblock[mb].type].atoms; + for (i = 0; i < atoms->nr; i++) + { + q = atoms->atom[i].q; + qsum += nmol*q; + q2sum += nmol*q*q; + c6 = mtop->ffparams.iparams[atoms->atom[i].type*(mtop->ffparams.atnr+1)].lj.c6; + c6sum += nmol*c6; + } + } + fr->qsum[0] = qsum; + fr->q2sum[0] = q2sum; + fr->c6sum[0] = c6sum; + + if (fr->efep != efepNO) + { + qsum = 0; + q2sum = 0; + c6sum = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + nmol = mtop->molblock[mb].nmol; + atoms = &mtop->moltype[mtop->molblock[mb].type].atoms; + for (i = 0; i < atoms->nr; i++) + { + q = atoms->atom[i].qB; + qsum += nmol*q; + q2sum += nmol*q*q; + c6 = mtop->ffparams.iparams[atoms->atom[i].typeB*(mtop->ffparams.atnr+1)].lj.c6; + c6sum += nmol*c6; + } + fr->qsum[1] = qsum; + fr->q2sum[1] = q2sum; + fr->c6sum[1] = c6sum; + } + } + else + { + fr->qsum[1] = fr->qsum[0]; + fr->q2sum[1] = fr->q2sum[0]; + fr->c6sum[1] = fr->c6sum[0]; + } + if (log) + { + if (fr->efep == efepNO) + { + fprintf(log, "System total charge: %.3f\n", fr->qsum[0]); + } + else + { + fprintf(log, "System total charge, top. A: %.3f top. B: %.3f\n", + fr->qsum[0], fr->qsum[1]); + } + } +} + +void update_forcerec(t_forcerec *fr, matrix box) +{ + if (fr->eeltype == eelGRF) + { + calc_rffac(NULL, fr->eeltype, fr->epsilon_r, fr->epsilon_rf, + fr->rcoulomb, fr->temp, fr->zsquare, box, + &fr->kappa, &fr->k_rf, &fr->c_rf); + } +} + +void set_avcsixtwelve(FILE *fplog, t_forcerec *fr, const gmx_mtop_t *mtop) +{ + const t_atoms *atoms, *atoms_tpi; + const t_blocka *excl; + int mb, nmol, nmolc, i, j, tpi, tpj, j1, j2, k, n, nexcl, q; + gmx_int64_t npair, npair_ij, tmpi, tmpj; + double csix, ctwelve; + int ntp, *typecount; + gmx_bool bBHAM; + real *nbfp; + real *nbfp_comb = NULL; + + ntp = fr->ntype; + bBHAM = fr->bBHAM; + nbfp = fr->nbfp; + + /* For LJ-PME, we want to correct for the difference between the + * actual C6 values and the C6 values used by the LJ-PME based on + * combination rules. */ + + if (EVDW_PME(fr->vdwtype)) + { + nbfp_comb = mk_nbfp_combination_rule(&mtop->ffparams, + (fr->ljpme_combination_rule == eljpmeLB) ? eCOMB_ARITHMETIC : eCOMB_GEOMETRIC); + for (tpi = 0; tpi < ntp; ++tpi) + { + for (tpj = 0; tpj < ntp; ++tpj) + { + C6(nbfp_comb, ntp, tpi, tpj) = + C6(nbfp, ntp, tpi, tpj) - C6(nbfp_comb, ntp, tpi, tpj); + C12(nbfp_comb, ntp, tpi, tpj) = C12(nbfp, ntp, tpi, tpj); + } + } + nbfp = nbfp_comb; + } + for (q = 0; q < (fr->efep == efepNO ? 1 : 2); q++) + { + csix = 0; + ctwelve = 0; + npair = 0; + nexcl = 0; + if (!fr->n_tpi) + { + /* Count the types so we avoid natoms^2 operations */ + snew(typecount, ntp); + gmx_mtop_count_atomtypes(mtop, q, typecount); + + for (tpi = 0; tpi < ntp; tpi++) + { + for (tpj = tpi; tpj < ntp; tpj++) + { + tmpi = typecount[tpi]; + tmpj = typecount[tpj]; + if (tpi != tpj) + { + npair_ij = tmpi*tmpj; + } + else + { + npair_ij = tmpi*(tmpi - 1)/2; + } + if (bBHAM) + { + /* nbfp now includes the 6.0 derivative prefactor */ + csix += npair_ij*BHAMC(nbfp, ntp, tpi, tpj)/6.0; + } + else + { + /* nbfp now includes the 6.0/12.0 derivative prefactors */ + csix += npair_ij* C6(nbfp, ntp, tpi, tpj)/6.0; + ctwelve += npair_ij* C12(nbfp, ntp, tpi, tpj)/12.0; + } + npair += npair_ij; + } + } + sfree(typecount); + /* Subtract the excluded pairs. + * The main reason for substracting exclusions is that in some cases + * some combinations might never occur and the parameters could have + * any value. These unused values should not influence the dispersion + * correction. + */ + for (mb = 0; mb < mtop->nmolblock; mb++) + { + nmol = mtop->molblock[mb].nmol; + atoms = &mtop->moltype[mtop->molblock[mb].type].atoms; + excl = &mtop->moltype[mtop->molblock[mb].type].excls; + for (i = 0; (i < atoms->nr); i++) + { + if (q == 0) + { + tpi = atoms->atom[i].type; + } + else + { + tpi = atoms->atom[i].typeB; + } + j1 = excl->index[i]; + j2 = excl->index[i+1]; + for (j = j1; j < j2; j++) + { + k = excl->a[j]; + if (k > i) + { + if (q == 0) + { + tpj = atoms->atom[k].type; + } + else + { + tpj = atoms->atom[k].typeB; + } + if (bBHAM) + { + /* nbfp now includes the 6.0 derivative prefactor */ + csix -= nmol*BHAMC(nbfp, ntp, tpi, tpj)/6.0; + } + else + { + /* nbfp now includes the 6.0/12.0 derivative prefactors */ + csix -= nmol*C6 (nbfp, ntp, tpi, tpj)/6.0; + ctwelve -= nmol*C12(nbfp, ntp, tpi, tpj)/12.0; + } + nexcl += nmol; + } + } + } + } + } + else + { + /* Only correct for the interaction of the test particle + * with the rest of the system. + */ + atoms_tpi = + &mtop->moltype[mtop->molblock[mtop->nmolblock-1].type].atoms; + + npair = 0; + for (mb = 0; mb < mtop->nmolblock; mb++) + { + nmol = mtop->molblock[mb].nmol; + atoms = &mtop->moltype[mtop->molblock[mb].type].atoms; + for (j = 0; j < atoms->nr; j++) + { + nmolc = nmol; + /* Remove the interaction of the test charge group + * with itself. + */ + if (mb == mtop->nmolblock-1) + { + nmolc--; + + if (mb == 0 && nmol == 1) + { + gmx_fatal(FARGS, "Old format tpr with TPI, please generate a new tpr file"); + } + } + if (q == 0) + { + tpj = atoms->atom[j].type; + } + else + { + tpj = atoms->atom[j].typeB; + } + for (i = 0; i < fr->n_tpi; i++) + { + if (q == 0) + { + tpi = atoms_tpi->atom[i].type; + } + else + { + tpi = atoms_tpi->atom[i].typeB; + } + if (bBHAM) + { + /* nbfp now includes the 6.0 derivative prefactor */ + csix += nmolc*BHAMC(nbfp, ntp, tpi, tpj)/6.0; + } + else + { + /* nbfp now includes the 6.0/12.0 derivative prefactors */ + csix += nmolc*C6 (nbfp, ntp, tpi, tpj)/6.0; + ctwelve += nmolc*C12(nbfp, ntp, tpi, tpj)/12.0; + } + npair += nmolc; + } + } + } + } + if (npair - nexcl <= 0 && fplog) + { + fprintf(fplog, "\nWARNING: There are no atom pairs for dispersion correction\n\n"); + csix = 0; + ctwelve = 0; + } + else + { + csix /= npair - nexcl; + ctwelve /= npair - nexcl; + } + if (debug) + { + fprintf(debug, "Counted %d exclusions\n", nexcl); + fprintf(debug, "Average C6 parameter is: %10g\n", (double)csix); + fprintf(debug, "Average C12 parameter is: %10g\n", (double)ctwelve); + } + fr->avcsix[q] = csix; + fr->avctwelve[q] = ctwelve; + } + + if (EVDW_PME(fr->vdwtype)) + { + sfree(nbfp_comb); + } + + if (fplog != NULL) + { + if (fr->eDispCorr == edispcAllEner || + fr->eDispCorr == edispcAllEnerPres) + { + fprintf(fplog, "Long Range LJ corr.: %10.4e, %10.4e\n", + fr->avcsix[0], fr->avctwelve[0]); + } + else + { + fprintf(fplog, "Long Range LJ corr.: %10.4e\n", fr->avcsix[0]); + } + } +} + + +static void set_bham_b_max(FILE *fplog, t_forcerec *fr, + const gmx_mtop_t *mtop) +{ + const t_atoms *at1, *at2; + int mt1, mt2, i, j, tpi, tpj, ntypes; + real b, bmin; + real *nbfp; + + if (fplog) + { + fprintf(fplog, "Determining largest Buckingham b parameter for table\n"); + } + nbfp = fr->nbfp; + ntypes = fr->ntype; + + bmin = -1; + fr->bham_b_max = 0; + for (mt1 = 0; mt1 < mtop->nmoltype; mt1++) + { + at1 = &mtop->moltype[mt1].atoms; + for (i = 0; (i < at1->nr); i++) + { + tpi = at1->atom[i].type; + if (tpi >= ntypes) + { + gmx_fatal(FARGS, "Atomtype[%d] = %d, maximum = %d", i, tpi, ntypes); + } + + for (mt2 = mt1; mt2 < mtop->nmoltype; mt2++) + { + at2 = &mtop->moltype[mt2].atoms; + for (j = 0; (j < at2->nr); j++) + { + tpj = at2->atom[j].type; + if (tpj >= ntypes) + { + gmx_fatal(FARGS, "Atomtype[%d] = %d, maximum = %d", j, tpj, ntypes); + } + b = BHAMB(nbfp, ntypes, tpi, tpj); + if (b > fr->bham_b_max) + { + fr->bham_b_max = b; + } + if ((b < bmin) || (bmin == -1)) + { + bmin = b; + } + } + } + } + } + if (fplog) + { + fprintf(fplog, "Buckingham b parameters, min: %g, max: %g\n", + bmin, fr->bham_b_max); + } +} + +static void make_nbf_tables(FILE *fp, const output_env_t oenv, + t_forcerec *fr, real rtab, + const t_commrec *cr, + const char *tabfn, char *eg1, char *eg2, + t_nblists *nbl) +{ + char buf[STRLEN]; + int i, j; + + if (tabfn == NULL) + { + if (debug) + { + fprintf(debug, "No table file name passed, can not read table, can not do non-bonded interactions\n"); + } + return; + } + + sprintf(buf, "%s", tabfn); + if (eg1 && eg2) + { + /* Append the two energy group names */ + sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "_%s_%s.%s", + eg1, eg2, ftp2ext(efXVG)); + } + nbl->table_elec_vdw = make_tables(fp, oenv, fr, MASTER(cr), buf, rtab, 0); + /* Copy the contents of the table to separate coulomb and LJ tables too, + * to improve cache performance. + */ + /* For performance reasons we want + * the table data to be aligned to 16-byte. The pointers could be freed + * but currently aren't. + */ + nbl->table_elec.interaction = GMX_TABLE_INTERACTION_ELEC; + nbl->table_elec.format = nbl->table_elec_vdw.format; + nbl->table_elec.r = nbl->table_elec_vdw.r; + nbl->table_elec.n = nbl->table_elec_vdw.n; + nbl->table_elec.scale = nbl->table_elec_vdw.scale; + nbl->table_elec.scale_exp = nbl->table_elec_vdw.scale_exp; + nbl->table_elec.formatsize = nbl->table_elec_vdw.formatsize; + nbl->table_elec.ninteractions = 1; + nbl->table_elec.stride = nbl->table_elec.formatsize * nbl->table_elec.ninteractions; + snew_aligned(nbl->table_elec.data, nbl->table_elec.stride*(nbl->table_elec.n+1), 32); + + nbl->table_vdw.interaction = GMX_TABLE_INTERACTION_VDWREP_VDWDISP; + nbl->table_vdw.format = nbl->table_elec_vdw.format; + nbl->table_vdw.r = nbl->table_elec_vdw.r; + nbl->table_vdw.n = nbl->table_elec_vdw.n; + nbl->table_vdw.scale = nbl->table_elec_vdw.scale; + nbl->table_vdw.scale_exp = nbl->table_elec_vdw.scale_exp; + nbl->table_vdw.formatsize = nbl->table_elec_vdw.formatsize; + nbl->table_vdw.ninteractions = 2; + nbl->table_vdw.stride = nbl->table_vdw.formatsize * nbl->table_vdw.ninteractions; + snew_aligned(nbl->table_vdw.data, nbl->table_vdw.stride*(nbl->table_vdw.n+1), 32); + + for (i = 0; i <= nbl->table_elec_vdw.n; i++) + { + for (j = 0; j < 4; j++) + { + nbl->table_elec.data[4*i+j] = nbl->table_elec_vdw.data[12*i+j]; + } + for (j = 0; j < 8; j++) + { + nbl->table_vdw.data[8*i+j] = nbl->table_elec_vdw.data[12*i+4+j]; + } + } +} + +static void count_tables(int ftype1, int ftype2, const gmx_mtop_t *mtop, + int *ncount, int **count) +{ + const gmx_moltype_t *molt; + const t_ilist *il; + int mt, ftype, stride, i, j, tabnr; + + for (mt = 0; mt < mtop->nmoltype; mt++) + { + molt = &mtop->moltype[mt]; + for (ftype = 0; ftype < F_NRE; ftype++) + { + if (ftype == ftype1 || ftype == ftype2) + { + il = &molt->ilist[ftype]; + stride = 1 + NRAL(ftype); + for (i = 0; i < il->nr; i += stride) + { + tabnr = mtop->ffparams.iparams[il->iatoms[i]].tab.table; + if (tabnr < 0) + { + gmx_fatal(FARGS, "A bonded table number is smaller than 0: %d\n", tabnr); + } + if (tabnr >= *ncount) + { + srenew(*count, tabnr+1); + for (j = *ncount; j < tabnr+1; j++) + { + (*count)[j] = 0; + } + *ncount = tabnr+1; + } + (*count)[tabnr]++; + } + } + } + } +} + +static bondedtable_t *make_bonded_tables(FILE *fplog, + int ftype1, int ftype2, + const gmx_mtop_t *mtop, + const char *basefn, const char *tabext) +{ + int i, ncount, *count; + char tabfn[STRLEN]; + bondedtable_t *tab; + + tab = NULL; + + ncount = 0; + count = NULL; + count_tables(ftype1, ftype2, mtop, &ncount, &count); + + if (ncount > 0) + { + snew(tab, ncount); + for (i = 0; i < ncount; i++) + { + if (count[i] > 0) + { + sprintf(tabfn, "%s", basefn); + sprintf(tabfn + strlen(basefn) - strlen(ftp2ext(efXVG)) - 1, "_%s%d.%s", + tabext, i, ftp2ext(efXVG)); + tab[i] = make_bonded_table(fplog, tabfn, NRAL(ftype1)-2); + } + } + sfree(count); + } + + return tab; +} + +void forcerec_set_ranges(t_forcerec *fr, + int ncg_home, int ncg_force, + int natoms_force, + int natoms_force_constr, int natoms_f_novirsum) +{ + fr->cg0 = 0; + fr->hcg = ncg_home; + + /* fr->ncg_force is unused in the standard code, + * but it can be useful for modified code dealing with charge groups. + */ + fr->ncg_force = ncg_force; + fr->natoms_force = natoms_force; + fr->natoms_force_constr = natoms_force_constr; + + if (fr->natoms_force_constr > fr->nalloc_force) + { + fr->nalloc_force = over_alloc_dd(fr->natoms_force_constr); + + if (fr->bTwinRange) + { + srenew(fr->f_twin, fr->nalloc_force); + } + } + + if (fr->bF_NoVirSum) + { + fr->f_novirsum_n = natoms_f_novirsum; + if (fr->f_novirsum_n > fr->f_novirsum_nalloc) + { + fr->f_novirsum_nalloc = over_alloc_dd(fr->f_novirsum_n); + srenew(fr->f_novirsum_alloc, fr->f_novirsum_nalloc); + } + } + else + { + fr->f_novirsum_n = 0; + } +} + +static real cutoff_inf(real cutoff) +{ + if (cutoff == 0) + { + cutoff = GMX_CUTOFF_INF; + } + + return cutoff; +} + +static void make_adress_tf_tables(FILE *fp, const output_env_t oenv, + t_forcerec *fr, const t_inputrec *ir, + const char *tabfn, const gmx_mtop_t *mtop, + matrix box) +{ + char buf[STRLEN]; + int i, j; + + if (tabfn == NULL) + { + gmx_fatal(FARGS, "No thermoforce table file given. Use -tabletf to specify a file\n"); + return; + } + + snew(fr->atf_tabs, ir->adress->n_tf_grps); + + sprintf(buf, "%s", tabfn); + for (i = 0; i < ir->adress->n_tf_grps; i++) + { + j = ir->adress->tf_table_index[i]; /* get energy group index */ + sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "tf_%s.%s", + *(mtop->groups.grpname[mtop->groups.grps[egcENER].nm_ind[j]]), ftp2ext(efXVG)); + if (fp) + { + fprintf(fp, "loading tf table for energygrp index %d from %s\n", ir->adress->tf_table_index[i], buf); + } + fr->atf_tabs[i] = make_atf_table(fp, oenv, fr, buf, box); + } + +} + +gmx_bool can_use_allvsall(const t_inputrec *ir, gmx_bool bPrintNote, t_commrec *cr, FILE *fp) +{ + gmx_bool bAllvsAll; + + bAllvsAll = + ( + ir->rlist == 0 && + ir->rcoulomb == 0 && + ir->rvdw == 0 && + ir->ePBC == epbcNONE && + ir->vdwtype == evdwCUT && + ir->coulombtype == eelCUT && + ir->efep == efepNO && + (ir->implicit_solvent == eisNO || + (ir->implicit_solvent == eisGBSA && (ir->gb_algorithm == egbSTILL || + ir->gb_algorithm == egbHCT || + ir->gb_algorithm == egbOBC))) && + getenv("GMX_NO_ALLVSALL") == NULL + ); + + if (bAllvsAll && ir->opts.ngener > 1) + { + const char *note = "NOTE: Can not use all-vs-all force loops, because there are multiple energy monitor groups; you might get significantly higher performance when using only a single energy monitor group.\n"; + + if (bPrintNote) + { + if (MASTER(cr)) + { + fprintf(stderr, "\n%s\n", note); + } + if (fp != NULL) + { + fprintf(fp, "\n%s\n", note); + } + } + bAllvsAll = FALSE; + } + + if (bAllvsAll && fp && MASTER(cr)) + { + fprintf(fp, "\nUsing SIMD all-vs-all kernels.\n\n"); + } + + return bAllvsAll; +} + + +static void init_forcerec_f_threads(t_forcerec *fr, int nenergrp) +{ + int t, i; + + /* These thread local data structures are used for bondeds only */ + fr->nthreads = gmx_omp_nthreads_get(emntBonded); + + if (fr->nthreads > 1) + { + snew(fr->f_t, fr->nthreads); + /* Thread 0 uses the global force and energy arrays */ + for (t = 1; t < fr->nthreads; t++) + { + fr->f_t[t].f = NULL; + fr->f_t[t].f_nalloc = 0; + snew(fr->f_t[t].fshift, SHIFTS); + fr->f_t[t].grpp.nener = nenergrp*nenergrp; + for (i = 0; i < egNR; i++) + { + snew(fr->f_t[t].grpp.ener[i], fr->f_t[t].grpp.nener); + } + } + } +} + + +gmx_bool nbnxn_acceleration_supported(FILE *fplog, + const t_commrec *cr, + const t_inputrec *ir, + gmx_bool bGPU) +{ + if (!bGPU && (ir->vdwtype == evdwPME && ir->ljpme_combination_rule == eljpmeLB)) + { + md_print_warn(cr, fplog, "LJ-PME with Lorentz-Berthelot is not supported with %s, falling back to %s\n", + bGPU ? "GPUs" : "SIMD kernels", + bGPU ? "CPU only" : "plain-C kernels"); + return FALSE; + } + + return TRUE; +} + + +static void pick_nbnxn_kernel_cpu(const t_inputrec gmx_unused *ir, + int *kernel_type, + int *ewald_excl) +{ + *kernel_type = nbnxnk4x4_PlainC; + *ewald_excl = ewaldexclTable; + +#ifdef GMX_NBNXN_SIMD + { +#ifdef GMX_NBNXN_SIMD_4XN + *kernel_type = nbnxnk4xN_SIMD_4xN; +#endif +#ifdef GMX_NBNXN_SIMD_2XNN + *kernel_type = nbnxnk4xN_SIMD_2xNN; +#endif + +#if defined GMX_NBNXN_SIMD_2XNN && defined GMX_NBNXN_SIMD_4XN + /* We need to choose if we want 2x(N+N) or 4xN kernels. + * Currently this is based on the SIMD acceleration choice, + * but it might be better to decide this at runtime based on CPU. + * + * 4xN calculates more (zero) interactions, but has less pair-search + * work and much better kernel instruction scheduling. + * + * Up till now we have only seen that on Intel Sandy/Ivy Bridge, + * which doesn't have FMA, both the analytical and tabulated Ewald + * kernels have similar pair rates for 4x8 and 2x(4+4), so we choose + * 2x(4+4) because it results in significantly fewer pairs. + * For RF, the raw pair rate of the 4x8 kernel is higher than 2x(4+4), + * 10% with HT, 50% without HT. As we currently don't detect the actual + * use of HT, use 4x8 to avoid a potential performance hit. + * On Intel Haswell 4x8 is always faster. + */ + *kernel_type = nbnxnk4xN_SIMD_4xN; + +#ifndef GMX_SIMD_HAVE_FMA + if (EEL_PME_EWALD(ir->coulombtype) || + EVDW_PME(ir->vdwtype)) + { + /* We have Ewald kernels without FMA (Intel Sandy/Ivy Bridge). + * There are enough instructions to make 2x(4+4) efficient. + */ + *kernel_type = nbnxnk4xN_SIMD_2xNN; + } +#endif +#endif /* GMX_NBNXN_SIMD_2XNN && GMX_NBNXN_SIMD_4XN */ + + + if (getenv("GMX_NBNXN_SIMD_4XN") != NULL) + { +#ifdef GMX_NBNXN_SIMD_4XN + *kernel_type = nbnxnk4xN_SIMD_4xN; +#else + gmx_fatal(FARGS, "SIMD 4xN kernels requested, but Gromacs has been compiled without support for these kernels"); +#endif + } + if (getenv("GMX_NBNXN_SIMD_2XNN") != NULL) + { +#ifdef GMX_NBNXN_SIMD_2XNN + *kernel_type = nbnxnk4xN_SIMD_2xNN; +#else + gmx_fatal(FARGS, "SIMD 2x(N+N) kernels requested, but Gromacs has been compiled without support for these kernels"); +#endif + } + + /* Analytical Ewald exclusion correction is only an option in + * the SIMD kernel. + * Since table lookup's don't parallelize with SIMD, analytical + * will probably always be faster for a SIMD width of 8 or more. + * With FMA analytical is sometimes faster for a width if 4 as well. + * On BlueGene/Q, this is faster regardless of precision. + * In single precision, this is faster on Bulldozer. + */ +#if GMX_SIMD_REAL_WIDTH >= 8 || \ + (GMX_SIMD_REAL_WIDTH >= 4 && defined GMX_SIMD_HAVE_FMA && !defined GMX_DOUBLE) || \ + defined GMX_SIMD_IBM_QPX + *ewald_excl = ewaldexclAnalytical; +#endif + if (getenv("GMX_NBNXN_EWALD_TABLE") != NULL) + { + *ewald_excl = ewaldexclTable; + } + if (getenv("GMX_NBNXN_EWALD_ANALYTICAL") != NULL) + { + *ewald_excl = ewaldexclAnalytical; + } + + } +#endif /* GMX_NBNXN_SIMD */ +} + + +const char *lookup_nbnxn_kernel_name(int kernel_type) +{ + const char *returnvalue = NULL; + switch (kernel_type) + { + case nbnxnkNotSet: + returnvalue = "not set"; + break; + case nbnxnk4x4_PlainC: + returnvalue = "plain C"; + break; + case nbnxnk4xN_SIMD_4xN: + case nbnxnk4xN_SIMD_2xNN: +#ifdef GMX_NBNXN_SIMD +#if defined GMX_SIMD_X86_SSE2 + returnvalue = "SSE2"; +#elif defined GMX_SIMD_X86_SSE4_1 + returnvalue = "SSE4.1"; +#elif defined GMX_SIMD_X86_AVX_128_FMA + returnvalue = "AVX_128_FMA"; +#elif defined GMX_SIMD_X86_AVX_256 + returnvalue = "AVX_256"; +#elif defined GMX_SIMD_X86_AVX2_256 + returnvalue = "AVX2_256"; +#else + returnvalue = "SIMD"; +#endif +#else /* GMX_NBNXN_SIMD */ + returnvalue = "not available"; +#endif /* GMX_NBNXN_SIMD */ + break; + case nbnxnk8x8x8_CUDA: returnvalue = "CUDA"; break; + case nbnxnk8x8x8_PlainC: returnvalue = "plain C"; break; + + case nbnxnkNR: + default: + gmx_fatal(FARGS, "Illegal kernel type selected"); + returnvalue = NULL; + break; + } + return returnvalue; +}; + +static void pick_nbnxn_kernel(FILE *fp, + const t_commrec *cr, + gmx_bool use_simd_kernels, + gmx_bool bUseGPU, + gmx_bool bEmulateGPU, + const t_inputrec *ir, + int *kernel_type, + int *ewald_excl, + gmx_bool bDoNonbonded) +{ + assert(kernel_type); + + *kernel_type = nbnxnkNotSet; + *ewald_excl = ewaldexclTable; + + if (bEmulateGPU) + { + *kernel_type = nbnxnk8x8x8_PlainC; + + if (bDoNonbonded) + { + md_print_warn(cr, fp, "Emulating a GPU run on the CPU (slow)"); + } + } + else if (bUseGPU) + { + *kernel_type = nbnxnk8x8x8_CUDA; + } + + if (*kernel_type == nbnxnkNotSet) + { + /* LJ PME with LB combination rule does 7 mesh operations. + * This so slow that we don't compile SIMD non-bonded kernels for that. + */ + if (use_simd_kernels && + nbnxn_acceleration_supported(fp, cr, ir, FALSE)) + { + pick_nbnxn_kernel_cpu(ir, kernel_type, ewald_excl); + } + else + { + *kernel_type = nbnxnk4x4_PlainC; + } + } + + if (bDoNonbonded && fp != NULL) + { + fprintf(fp, "\nUsing %s %dx%d non-bonded kernels\n\n", + lookup_nbnxn_kernel_name(*kernel_type), + nbnxn_kernel_pairlist_simple(*kernel_type) ? NBNXN_CPU_CLUSTER_I_SIZE : NBNXN_GPU_CLUSTER_SIZE, + nbnxn_kernel_to_cj_size(*kernel_type)); + } +} + +static void pick_nbnxn_resources(const t_commrec *cr, + const gmx_hw_info_t *hwinfo, + gmx_bool bDoNonbonded, + gmx_bool *bUseGPU, + gmx_bool *bEmulateGPU, + const gmx_gpu_opt_t *gpu_opt) +{ + gmx_bool bEmulateGPUEnvVarSet; + char gpu_err_str[STRLEN]; + + *bUseGPU = FALSE; + + bEmulateGPUEnvVarSet = (getenv("GMX_EMULATE_GPU") != NULL); + + /* Run GPU emulation mode if GMX_EMULATE_GPU is defined. Because + * GPUs (currently) only handle non-bonded calculations, we will + * automatically switch to emulation if non-bonded calculations are + * turned off via GMX_NO_NONBONDED - this is the simple and elegant + * way to turn off GPU initialization, data movement, and cleanup. + * + * GPU emulation can be useful to assess the performance one can expect by + * adding GPU(s) to the machine. The conditional below allows this even + * if mdrun is compiled without GPU acceleration support. + * Note that you should freezing the system as otherwise it will explode. + */ + *bEmulateGPU = (bEmulateGPUEnvVarSet || + (!bDoNonbonded && + gpu_opt->ncuda_dev_use > 0)); + + /* Enable GPU mode when GPUs are available or no GPU emulation is requested. + */ + if (gpu_opt->ncuda_dev_use > 0 && !(*bEmulateGPU)) + { + /* Each PP node will use the intra-node id-th device from the + * list of detected/selected GPUs. */ + if (!init_gpu(cr->rank_pp_intranode, gpu_err_str, + &hwinfo->gpu_info, gpu_opt)) + { + /* At this point the init should never fail as we made sure that + * we have all the GPUs we need. If it still does, we'll bail. */ + gmx_fatal(FARGS, "On node %d failed to initialize GPU #%d: %s", + cr->nodeid, + get_gpu_device_id(&hwinfo->gpu_info, gpu_opt, + cr->rank_pp_intranode), + gpu_err_str); + } + + /* Here we actually turn on hardware GPU acceleration */ + *bUseGPU = TRUE; + } +} + +gmx_bool uses_simple_tables(int cutoff_scheme, + nonbonded_verlet_t *nbv, + int group) +{ + gmx_bool bUsesSimpleTables = TRUE; + int grp_index; + + switch (cutoff_scheme) + { + case ecutsGROUP: + bUsesSimpleTables = TRUE; + break; + case ecutsVERLET: + assert(NULL != nbv && NULL != nbv->grp); + grp_index = (group < 0) ? 0 : (nbv->ngrp - 1); + bUsesSimpleTables = nbnxn_kernel_pairlist_simple(nbv->grp[grp_index].kernel_type); + break; + default: + gmx_incons("unimplemented"); + } + return bUsesSimpleTables; +} + +static void init_ewald_f_table(interaction_const_t *ic, + gmx_bool bUsesSimpleTables, + real rtab) +{ + real maxr; + + if (bUsesSimpleTables) + { + /* With a spacing of 0.0005 we are at the force summation accuracy + * for the SSE kernels for "normal" atomistic simulations. + */ + ic->tabq_scale = ewald_spline3_table_scale(ic->ewaldcoeff_q, + ic->rcoulomb); + + maxr = (rtab > ic->rcoulomb) ? rtab : ic->rcoulomb; + ic->tabq_size = (int)(maxr*ic->tabq_scale) + 2; + } + else + { + ic->tabq_size = GPU_EWALD_COULOMB_FORCE_TABLE_SIZE; + /* Subtract 2 iso 1 to avoid access out of range due to rounding */ + ic->tabq_scale = (ic->tabq_size - 2)/ic->rcoulomb; + } + + sfree_aligned(ic->tabq_coul_FDV0); + sfree_aligned(ic->tabq_coul_F); + sfree_aligned(ic->tabq_coul_V); + + sfree_aligned(ic->tabq_vdw_FDV0); + sfree_aligned(ic->tabq_vdw_F); + sfree_aligned(ic->tabq_vdw_V); + + if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype)) + { + /* Create the original table data in FDV0 */ + snew_aligned(ic->tabq_coul_FDV0, ic->tabq_size*4, 32); + snew_aligned(ic->tabq_coul_F, ic->tabq_size, 32); + snew_aligned(ic->tabq_coul_V, ic->tabq_size, 32); + table_spline3_fill_ewald_lr(ic->tabq_coul_F, ic->tabq_coul_V, ic->tabq_coul_FDV0, + ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_q, v_q_ewald_lr); + } + + if (EVDW_PME(ic->vdwtype)) + { + snew_aligned(ic->tabq_vdw_FDV0, ic->tabq_size*4, 32); + snew_aligned(ic->tabq_vdw_F, ic->tabq_size, 32); + snew_aligned(ic->tabq_vdw_V, ic->tabq_size, 32); + table_spline3_fill_ewald_lr(ic->tabq_vdw_F, ic->tabq_vdw_V, ic->tabq_vdw_FDV0, + ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_lj, v_lj_ewald_lr); + } +} + +void init_interaction_const_tables(FILE *fp, + interaction_const_t *ic, + gmx_bool bUsesSimpleTables, + real rtab) +{ + real spacing; + + if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype) || EVDW_PME(ic->vdwtype)) + { + init_ewald_f_table(ic, bUsesSimpleTables, rtab); + + if (fp != NULL) + { + fprintf(fp, "Initialized non-bonded Ewald correction tables, spacing: %.2e size: %d\n\n", + 1/ic->tabq_scale, ic->tabq_size); + } + } +} + +static void clear_force_switch_constants(shift_consts_t *sc) +{ + sc->c2 = 0; + sc->c3 = 0; + sc->cpot = 0; +} + +static void force_switch_constants(real p, + real rsw, real rc, + shift_consts_t *sc) +{ + /* Here we determine the coefficient for shifting the force to zero + * between distance rsw and the cut-off rc. + * For a potential of r^-p, we have force p*r^-(p+1). + * But to save flops we absorb p in the coefficient. + * Thus we get: + * force/p = r^-(p+1) + c2*r^2 + c3*r^3 + * potential = r^-p + c2/3*r^3 + c3/4*r^4 + cpot + */ + sc->c2 = ((p + 1)*rsw - (p + 4)*rc)/(pow(rc, p + 2)*pow(rc - rsw, 2)); + sc->c3 = -((p + 1)*rsw - (p + 3)*rc)/(pow(rc, p + 2)*pow(rc - rsw, 3)); + sc->cpot = -pow(rc, -p) + p*sc->c2/3*pow(rc - rsw, 3) + p*sc->c3/4*pow(rc - rsw, 4); +} + +static void potential_switch_constants(real rsw, real rc, + switch_consts_t *sc) +{ + /* The switch function is 1 at rsw and 0 at rc. + * The derivative and second derivate are zero at both ends. + * rsw = max(r - r_switch, 0) + * sw = 1 + c3*rsw^3 + c4*rsw^4 + c5*rsw^5 + * dsw = 3*c3*rsw^2 + 4*c4*rsw^3 + 5*c5*rsw^4 + * force = force*dsw - potential*sw + * potential *= sw + */ + sc->c3 = -10*pow(rc - rsw, -3); + sc->c4 = 15*pow(rc - rsw, -4); + sc->c5 = -6*pow(rc - rsw, -5); +} + +static void +init_interaction_const(FILE *fp, + const t_commrec gmx_unused *cr, + interaction_const_t **interaction_const, + const t_forcerec *fr, + real rtab) +{ + interaction_const_t *ic; + gmx_bool bUsesSimpleTables = TRUE; + + snew(ic, 1); + + /* Just allocate something so we can free it */ + snew_aligned(ic->tabq_coul_FDV0, 16, 32); + snew_aligned(ic->tabq_coul_F, 16, 32); + snew_aligned(ic->tabq_coul_V, 16, 32); + + ic->rlist = fr->rlist; + ic->rlistlong = fr->rlistlong; + + /* Lennard-Jones */ + ic->vdwtype = fr->vdwtype; + ic->vdw_modifier = fr->vdw_modifier; + ic->rvdw = fr->rvdw; + ic->rvdw_switch = fr->rvdw_switch; + ic->ewaldcoeff_lj = fr->ewaldcoeff_lj; + ic->ljpme_comb_rule = fr->ljpme_combination_rule; + ic->sh_lj_ewald = 0; + clear_force_switch_constants(&ic->dispersion_shift); + clear_force_switch_constants(&ic->repulsion_shift); + + switch (ic->vdw_modifier) + { + case eintmodPOTSHIFT: + /* Only shift the potential, don't touch the force */ + ic->dispersion_shift.cpot = -pow(ic->rvdw, -6.0); + ic->repulsion_shift.cpot = -pow(ic->rvdw, -12.0); + if (EVDW_PME(ic->vdwtype)) + { + real crc2; + + crc2 = sqr(ic->ewaldcoeff_lj*ic->rvdw); + ic->sh_lj_ewald = (exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)*pow(ic->rvdw, -6.0); + } + break; + case eintmodFORCESWITCH: + /* Switch the force, switch and shift the potential */ + force_switch_constants(6.0, ic->rvdw_switch, ic->rvdw, + &ic->dispersion_shift); + force_switch_constants(12.0, ic->rvdw_switch, ic->rvdw, + &ic->repulsion_shift); + break; + case eintmodPOTSWITCH: + /* Switch the potential and force */ + potential_switch_constants(ic->rvdw_switch, ic->rvdw, + &ic->vdw_switch); + break; + case eintmodNONE: + case eintmodEXACTCUTOFF: + /* Nothing to do here */ + break; + default: + gmx_incons("unimplemented potential modifier"); + } + + ic->sh_invrc6 = -ic->dispersion_shift.cpot; + + /* Electrostatics */ + ic->eeltype = fr->eeltype; + ic->coulomb_modifier = fr->coulomb_modifier; + ic->rcoulomb = fr->rcoulomb; + ic->epsilon_r = fr->epsilon_r; + ic->epsfac = fr->epsfac; + ic->ewaldcoeff_q = fr->ewaldcoeff_q; + + if (fr->coulomb_modifier == eintmodPOTSHIFT) + { + ic->sh_ewald = gmx_erfc(ic->ewaldcoeff_q*ic->rcoulomb); + } + else + { + ic->sh_ewald = 0; + } + + /* Reaction-field */ + if (EEL_RF(ic->eeltype)) + { + ic->epsilon_rf = fr->epsilon_rf; + ic->k_rf = fr->k_rf; + ic->c_rf = fr->c_rf; + } + else + { + /* For plain cut-off we might use the reaction-field kernels */ + ic->epsilon_rf = ic->epsilon_r; + ic->k_rf = 0; + if (fr->coulomb_modifier == eintmodPOTSHIFT) + { + ic->c_rf = 1/ic->rcoulomb; + } + else + { + ic->c_rf = 0; + } + } + + if (fp != NULL) + { + real dispersion_shift; + + dispersion_shift = ic->dispersion_shift.cpot; + if (EVDW_PME(ic->vdwtype)) + { + dispersion_shift -= ic->sh_lj_ewald; + } + fprintf(fp, "Potential shift: LJ r^-12: %.3e r^-6: %.3e", + ic->repulsion_shift.cpot, dispersion_shift); + + if (ic->eeltype == eelCUT) + { + fprintf(fp, ", Coulomb %.e", -ic->c_rf); + } + else if (EEL_PME(ic->eeltype)) + { + fprintf(fp, ", Ewald %.3e", -ic->sh_ewald); + } + fprintf(fp, "\n"); + } + + *interaction_const = ic; + + if (fr->nbv != NULL && fr->nbv->bUseGPU) + { + nbnxn_cuda_init_const(fr->nbv->cu_nbv, ic, fr->nbv->grp); + + /* With tMPI + GPUs some ranks may be sharing GPU(s) and therefore + * also sharing texture references. To keep the code simple, we don't + * treat texture references as shared resources, but this means that + * the coulomb_tab and nbfp texture refs will get updated by multiple threads. + * Hence, to ensure that the non-bonded kernels don't start before all + * texture binding operations are finished, we need to wait for all ranks + * to arrive here before continuing. + * + * Note that we could omit this barrier if GPUs are not shared (or + * texture objects are used), but as this is initialization code, there + * is not point in complicating things. + */ +#ifdef GMX_THREAD_MPI + if (PAR(cr)) + { + gmx_barrier(cr); + } +#endif /* GMX_THREAD_MPI */ + } + + bUsesSimpleTables = uses_simple_tables(fr->cutoff_scheme, fr->nbv, -1); + init_interaction_const_tables(fp, ic, bUsesSimpleTables, rtab); +} + +static void init_nb_verlet(FILE *fp, + nonbonded_verlet_t **nb_verlet, + gmx_bool bFEP_NonBonded, + const t_inputrec *ir, + const t_forcerec *fr, + const t_commrec *cr, + const char *nbpu_opt) +{ + nonbonded_verlet_t *nbv; + int i; + char *env; + gmx_bool bEmulateGPU, bHybridGPURun = FALSE; + + nbnxn_alloc_t *nb_alloc; + nbnxn_free_t *nb_free; + + snew(nbv, 1); + + pick_nbnxn_resources(cr, fr->hwinfo, + fr->bNonbonded, + &nbv->bUseGPU, + &bEmulateGPU, + fr->gpu_opt); + + nbv->nbs = NULL; + + nbv->ngrp = (DOMAINDECOMP(cr) ? 2 : 1); + for (i = 0; i < nbv->ngrp; i++) + { + nbv->grp[i].nbl_lists.nnbl = 0; + nbv->grp[i].nbat = NULL; + nbv->grp[i].kernel_type = nbnxnkNotSet; + + if (i == 0) /* local */ + { + pick_nbnxn_kernel(fp, cr, fr->use_simd_kernels, + nbv->bUseGPU, bEmulateGPU, ir, + &nbv->grp[i].kernel_type, + &nbv->grp[i].ewald_excl, + fr->bNonbonded); + } + else /* non-local */ + { + if (nbpu_opt != NULL && strcmp(nbpu_opt, "gpu_cpu") == 0) + { + /* Use GPU for local, select a CPU kernel for non-local */ + pick_nbnxn_kernel(fp, cr, fr->use_simd_kernels, + FALSE, FALSE, ir, + &nbv->grp[i].kernel_type, + &nbv->grp[i].ewald_excl, + fr->bNonbonded); + + bHybridGPURun = TRUE; + } + else + { + /* Use the same kernel for local and non-local interactions */ + nbv->grp[i].kernel_type = nbv->grp[0].kernel_type; + nbv->grp[i].ewald_excl = nbv->grp[0].ewald_excl; + } + } + } + + if (nbv->bUseGPU) + { + /* init the NxN GPU data; the last argument tells whether we'll have + * both local and non-local NB calculation on GPU */ + nbnxn_cuda_init(fp, &nbv->cu_nbv, + &fr->hwinfo->gpu_info, fr->gpu_opt, + cr->rank_pp_intranode, + (nbv->ngrp > 1) && !bHybridGPURun); + + if ((env = getenv("GMX_NB_MIN_CI")) != NULL) + { + char *end; + + nbv->min_ci_balanced = strtol(env, &end, 10); + if (!end || (*end != 0) || nbv->min_ci_balanced <= 0) + { + gmx_fatal(FARGS, "Invalid value passed in GMX_NB_MIN_CI=%s, positive integer required", env); + } + + if (debug) + { + fprintf(debug, "Neighbor-list balancing parameter: %d (passed as env. var.)\n", + nbv->min_ci_balanced); + } + } + else + { + nbv->min_ci_balanced = nbnxn_cuda_min_ci_balanced(nbv->cu_nbv); + if (debug) + { + fprintf(debug, "Neighbor-list balancing parameter: %d (auto-adjusted to the number of GPU multi-processors)\n", + nbv->min_ci_balanced); + } + } + } + else + { + nbv->min_ci_balanced = 0; + } + + *nb_verlet = nbv; + + nbnxn_init_search(&nbv->nbs, + DOMAINDECOMP(cr) ? &cr->dd->nc : NULL, + DOMAINDECOMP(cr) ? domdec_zones(cr->dd) : NULL, + bFEP_NonBonded, + gmx_omp_nthreads_get(emntNonbonded)); + + for (i = 0; i < nbv->ngrp; i++) + { + if (nbv->grp[0].kernel_type == nbnxnk8x8x8_CUDA) + { + nb_alloc = &pmalloc; + nb_free = &pfree; + } + else + { + nb_alloc = NULL; + nb_free = NULL; + } + + nbnxn_init_pairlist_set(&nbv->grp[i].nbl_lists, + nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type), + /* 8x8x8 "non-simple" lists are ATM always combined */ + !nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type), + nb_alloc, nb_free); + + if (i == 0 || + nbv->grp[0].kernel_type != nbv->grp[i].kernel_type) + { + gmx_bool bSimpleList; + int enbnxninitcombrule; + + bSimpleList = nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type); + + if (bSimpleList && (fr->vdwtype == evdwCUT && (fr->vdw_modifier == eintmodNONE || fr->vdw_modifier == eintmodPOTSHIFT))) + { + /* Plain LJ cut-off: we can optimize with combination rules */ + enbnxninitcombrule = enbnxninitcombruleDETECT; + } + else if (fr->vdwtype == evdwPME) + { + /* LJ-PME: we need to use a combination rule for the grid */ + if (fr->ljpme_combination_rule == eljpmeGEOM) + { + enbnxninitcombrule = enbnxninitcombruleGEOM; + } + else + { + enbnxninitcombrule = enbnxninitcombruleLB; + } + } + else + { + /* We use a full combination matrix: no rule required */ + enbnxninitcombrule = enbnxninitcombruleNONE; + } + + + snew(nbv->grp[i].nbat, 1); + nbnxn_atomdata_init(fp, + nbv->grp[i].nbat, + nbv->grp[i].kernel_type, + enbnxninitcombrule, + fr->ntype, fr->nbfp, + ir->opts.ngener, + bSimpleList ? gmx_omp_nthreads_get(emntNonbonded) : 1, + nb_alloc, nb_free); + } + else + { + nbv->grp[i].nbat = nbv->grp[0].nbat; + } + } +} + +void init_forcerec(FILE *fp, + const output_env_t oenv, + t_forcerec *fr, + t_fcdata *fcd, + const t_inputrec *ir, + const gmx_mtop_t *mtop, + const t_commrec *cr, + matrix box, + const char *tabfn, + const char *tabafn, + const char *tabpfn, + const char *tabbfn, + const char *nbpu_opt, + gmx_bool bNoSolvOpt, + real print_force) +{ + int i, j, m, natoms, ngrp, negp_pp, negptable, egi, egj; + real rtab; + char *env; + double dbl; + const t_block *cgs; + gmx_bool bGenericKernelOnly; + gmx_bool bMakeTables, bMakeSeparate14Table, bSomeNormalNbListsAreInUse; + gmx_bool bFEP_NonBonded; + t_nblists *nbl; + int *nm_ind, egp_flags; + + if (fr->hwinfo == NULL) + { + /* Detect hardware, gather information. + * In mdrun, hwinfo has already been set before calling init_forcerec. + * Here we ignore GPUs, as tools will not use them anyhow. + */ + fr->hwinfo = gmx_detect_hardware(fp, cr, FALSE); + } + + /* By default we turn SIMD kernels on, but it might be turned off further down... */ + fr->use_simd_kernels = TRUE; + + fr->bDomDec = DOMAINDECOMP(cr); + + natoms = mtop->natoms; + + if (check_box(ir->ePBC, box)) + { + gmx_fatal(FARGS, check_box(ir->ePBC, box)); + } + + /* Test particle insertion ? */ + if (EI_TPI(ir->eI)) + { + /* Set to the size of the molecule to be inserted (the last one) */ + /* Because of old style topologies, we have to use the last cg + * instead of the last molecule type. + */ + cgs = &mtop->moltype[mtop->molblock[mtop->nmolblock-1].type].cgs; + fr->n_tpi = cgs->index[cgs->nr] - cgs->index[cgs->nr-1]; + if (fr->n_tpi != mtop->mols.index[mtop->mols.nr] - mtop->mols.index[mtop->mols.nr-1]) + { + gmx_fatal(FARGS, "The molecule to insert can not consist of multiple charge groups.\nMake it a single charge group."); + } + } + else + { + fr->n_tpi = 0; + } + + /* Copy AdResS parameters */ + if (ir->bAdress) + { + fr->adress_type = ir->adress->type; + fr->adress_const_wf = ir->adress->const_wf; + fr->adress_ex_width = ir->adress->ex_width; + fr->adress_hy_width = ir->adress->hy_width; + fr->adress_icor = ir->adress->icor; + fr->adress_site = ir->adress->site; + fr->adress_ex_forcecap = ir->adress->ex_forcecap; + fr->adress_do_hybridpairs = ir->adress->do_hybridpairs; + + + snew(fr->adress_group_explicit, ir->adress->n_energy_grps); + for (i = 0; i < ir->adress->n_energy_grps; i++) + { + fr->adress_group_explicit[i] = ir->adress->group_explicit[i]; + } + + fr->n_adress_tf_grps = ir->adress->n_tf_grps; + snew(fr->adress_tf_table_index, fr->n_adress_tf_grps); + for (i = 0; i < fr->n_adress_tf_grps; i++) + { + fr->adress_tf_table_index[i] = ir->adress->tf_table_index[i]; + } + copy_rvec(ir->adress->refs, fr->adress_refs); + } + else + { + fr->adress_type = eAdressOff; + fr->adress_do_hybridpairs = FALSE; + } + + /* Copy the user determined parameters */ + fr->userint1 = ir->userint1; + fr->userint2 = ir->userint2; + fr->userint3 = ir->userint3; + fr->userint4 = ir->userint4; + fr->userreal1 = ir->userreal1; + fr->userreal2 = ir->userreal2; + fr->userreal3 = ir->userreal3; + fr->userreal4 = ir->userreal4; + + /* Shell stuff */ + fr->fc_stepsize = ir->fc_stepsize; + + /* Free energy */ + fr->efep = ir->efep; + fr->sc_alphavdw = ir->fepvals->sc_alpha; + if (ir->fepvals->bScCoul) + { + fr->sc_alphacoul = ir->fepvals->sc_alpha; + fr->sc_sigma6_min = pow(ir->fepvals->sc_sigma_min, 6); + } + else + { + fr->sc_alphacoul = 0; + fr->sc_sigma6_min = 0; /* only needed when bScCoul is on */ + } + fr->sc_power = ir->fepvals->sc_power; + fr->sc_r_power = ir->fepvals->sc_r_power; + fr->sc_sigma6_def = pow(ir->fepvals->sc_sigma, 6); + + env = getenv("GMX_SCSIGMA_MIN"); + if (env != NULL) + { + dbl = 0; + sscanf(env, "%lf", &dbl); + fr->sc_sigma6_min = pow(dbl, 6); + if (fp) + { + fprintf(fp, "Setting the minimum soft core sigma to %g nm\n", dbl); + } + } + + fr->bNonbonded = TRUE; + if (getenv("GMX_NO_NONBONDED") != NULL) + { + /* turn off non-bonded calculations */ + fr->bNonbonded = FALSE; + md_print_warn(cr, fp, + "Found environment variable GMX_NO_NONBONDED.\n" + "Disabling nonbonded calculations.\n"); + } + + bGenericKernelOnly = FALSE; + + /* We now check in the NS code whether a particular combination of interactions + * can be used with water optimization, and disable it if that is not the case. + */ + + if (getenv("GMX_NB_GENERIC") != NULL) + { + if (fp != NULL) + { + fprintf(fp, + "Found environment variable GMX_NB_GENERIC.\n" + "Disabling all interaction-specific nonbonded kernels, will only\n" + "use the slow generic ones in src/gmxlib/nonbonded/nb_generic.c\n\n"); + } + bGenericKernelOnly = TRUE; + } + + if (bGenericKernelOnly == TRUE) + { + bNoSolvOpt = TRUE; + } + + if ( (getenv("GMX_DISABLE_SIMD_KERNELS") != NULL) || (getenv("GMX_NOOPTIMIZEDKERNELS") != NULL) ) + { + fr->use_simd_kernels = FALSE; + if (fp != NULL) + { + fprintf(fp, + "\nFound environment variable GMX_DISABLE_SIMD_KERNELS.\n" + "Disabling the usage of any SIMD-specific kernel routines (e.g. SSE2/SSE4.1/AVX).\n\n"); + } + } + + fr->bBHAM = (mtop->ffparams.functype[0] == F_BHAM); + + /* Check if we can/should do all-vs-all kernels */ + fr->bAllvsAll = can_use_allvsall(ir, FALSE, NULL, NULL); + fr->AllvsAll_work = NULL; + fr->AllvsAll_workgb = NULL; + + /* All-vs-all kernels have not been implemented in 4.6, and + * the SIMD group kernels are also buggy in this case. Non-SIMD + * group kernels are OK. See Redmine #1249. */ + if (fr->bAllvsAll) + { + fr->bAllvsAll = FALSE; + fr->use_simd_kernels = FALSE; + if (fp != NULL) + { + fprintf(fp, + "\nYour simulation settings would have triggered the efficient all-vs-all\n" + "kernels in GROMACS 4.5, but these have not been implemented in GROMACS\n" + "4.6. Also, we can't use the accelerated SIMD kernels here because\n" + "of an unfixed bug. The reference C kernels are correct, though, so\n" + "we are proceeding by disabling all CPU architecture-specific\n" + "(e.g. SSE2/SSE4/AVX) routines. If performance is important, please\n" + "use GROMACS 4.5.7 or try cutoff-scheme = Verlet.\n\n"); + } + } + + /* Neighbour searching stuff */ + fr->cutoff_scheme = ir->cutoff_scheme; + fr->bGrid = (ir->ns_type == ensGRID); + fr->ePBC = ir->ePBC; + + if (fr->cutoff_scheme == ecutsGROUP) + { + const char *note = "NOTE: This file uses the deprecated 'group' cutoff_scheme. This will be\n" + "removed in a future release when 'verlet' supports all interaction forms.\n"; + + if (MASTER(cr)) + { + fprintf(stderr, "\n%s\n", note); + } + if (fp != NULL) + { + fprintf(fp, "\n%s\n", note); + } + } + + /* Determine if we will do PBC for distances in bonded interactions */ + if (fr->ePBC == epbcNONE) + { + fr->bMolPBC = FALSE; + } + else + { + if (!DOMAINDECOMP(cr)) + { + /* The group cut-off scheme and SHAKE assume charge groups + * are whole, but not using molpbc is faster in most cases. + */ + if (fr->cutoff_scheme == ecutsGROUP || + (ir->eConstrAlg == econtSHAKE && + (gmx_mtop_ftype_count(mtop, F_CONSTR) > 0 || + gmx_mtop_ftype_count(mtop, F_CONSTRNC) > 0))) + { + fr->bMolPBC = ir->bPeriodicMols; + } + else + { + fr->bMolPBC = TRUE; + if (getenv("GMX_USE_GRAPH") != NULL) + { + fr->bMolPBC = FALSE; + if (fp) + { + fprintf(fp, "\nGMX_MOLPBC is set, using the graph for bonded interactions\n\n"); + } + } + } + } + else + { + fr->bMolPBC = dd_bonded_molpbc(cr->dd, fr->ePBC); + } + } + fr->bGB = (ir->implicit_solvent == eisGBSA); + + fr->rc_scaling = ir->refcoord_scaling; + copy_rvec(ir->posres_com, fr->posres_com); + copy_rvec(ir->posres_comB, fr->posres_comB); + fr->rlist = cutoff_inf(ir->rlist); + fr->rlistlong = cutoff_inf(ir->rlistlong); + fr->eeltype = ir->coulombtype; + fr->vdwtype = ir->vdwtype; + fr->ljpme_combination_rule = ir->ljpme_combination_rule; + + fr->coulomb_modifier = ir->coulomb_modifier; + fr->vdw_modifier = ir->vdw_modifier; + + /* Electrostatics: Translate from interaction-setting-in-mdp-file to kernel interaction format */ + switch (fr->eeltype) + { + case eelCUT: + fr->nbkernel_elec_interaction = (fr->bGB) ? GMX_NBKERNEL_ELEC_GENERALIZEDBORN : GMX_NBKERNEL_ELEC_COULOMB; + break; + + case eelRF: + case eelGRF: + case eelRF_NEC: + fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD; + break; + + case eelRF_ZERO: + fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD; + fr->coulomb_modifier = eintmodEXACTCUTOFF; + break; + + case eelSWITCH: + case eelSHIFT: + case eelUSER: + case eelENCADSHIFT: + case eelPMESWITCH: + case eelPMEUSER: + case eelPMEUSERSWITCH: + fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_CUBICSPLINETABLE; + break; + + case eelPME: + case eelEWALD: + fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_EWALD; + break; + + default: + gmx_fatal(FARGS, "Unsupported electrostatic interaction: %s", eel_names[fr->eeltype]); + break; + } + + /* Vdw: Translate from mdp settings to kernel format */ + switch (fr->vdwtype) + { + case evdwCUT: + if (fr->bBHAM) + { + fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_BUCKINGHAM; + } + else + { + fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LENNARDJONES; + } + break; + case evdwPME: + fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LJEWALD; + break; + + case evdwSWITCH: + case evdwSHIFT: + case evdwUSER: + case evdwENCADSHIFT: + fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_CUBICSPLINETABLE; + break; + + default: + gmx_fatal(FARGS, "Unsupported vdw interaction: %s", evdw_names[fr->vdwtype]); + break; + } + + /* These start out identical to ir, but might be altered if we e.g. tabulate the interaction in the kernel */ + fr->nbkernel_elec_modifier = fr->coulomb_modifier; + fr->nbkernel_vdw_modifier = fr->vdw_modifier; + + fr->bTwinRange = fr->rlistlong > fr->rlist; + fr->bEwald = (EEL_PME(fr->eeltype) || fr->eeltype == eelEWALD); + + fr->reppow = mtop->ffparams.reppow; + + if (ir->cutoff_scheme == ecutsGROUP) + { + fr->bvdwtab = ((fr->vdwtype != evdwCUT || !gmx_within_tol(fr->reppow, 12.0, 10*GMX_DOUBLE_EPS)) + && !EVDW_PME(fr->vdwtype)); + /* We have special kernels for standard Ewald and PME, but the pme-switch ones are tabulated above */ + fr->bcoultab = !(fr->eeltype == eelCUT || + fr->eeltype == eelEWALD || + fr->eeltype == eelPME || + fr->eeltype == eelRF || + fr->eeltype == eelRF_ZERO); + + /* If the user absolutely wants different switch/shift settings for coul/vdw, it is likely + * going to be faster to tabulate the interaction than calling the generic kernel. + */ + if (fr->nbkernel_elec_modifier == eintmodPOTSWITCH && fr->nbkernel_vdw_modifier == eintmodPOTSWITCH) + { + if ((fr->rcoulomb_switch != fr->rvdw_switch) || (fr->rcoulomb != fr->rvdw)) + { + fr->bcoultab = TRUE; + } + } + else if ((fr->nbkernel_elec_modifier == eintmodPOTSHIFT && fr->nbkernel_vdw_modifier == eintmodPOTSHIFT) || + ((fr->nbkernel_elec_interaction == GMX_NBKERNEL_ELEC_REACTIONFIELD && + fr->nbkernel_elec_modifier == eintmodEXACTCUTOFF && + (fr->nbkernel_vdw_modifier == eintmodPOTSWITCH || fr->nbkernel_vdw_modifier == eintmodPOTSHIFT)))) + { + if (fr->rcoulomb != fr->rvdw) + { + fr->bcoultab = TRUE; + } + } + + if (getenv("GMX_REQUIRE_TABLES")) + { + fr->bvdwtab = TRUE; + fr->bcoultab = TRUE; + } + + if (fp) + { + fprintf(fp, "Table routines are used for coulomb: %s\n", bool_names[fr->bcoultab]); + fprintf(fp, "Table routines are used for vdw: %s\n", bool_names[fr->bvdwtab ]); + } + + if (fr->bvdwtab == TRUE) + { + fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_CUBICSPLINETABLE; + fr->nbkernel_vdw_modifier = eintmodNONE; + } + if (fr->bcoultab == TRUE) + { + fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_CUBICSPLINETABLE; + fr->nbkernel_elec_modifier = eintmodNONE; + } + } + + if (ir->cutoff_scheme == ecutsVERLET) + { + if (!gmx_within_tol(fr->reppow, 12.0, 10*GMX_DOUBLE_EPS)) + { + gmx_fatal(FARGS, "Cut-off scheme %S only supports LJ repulsion power 12", ecutscheme_names[ir->cutoff_scheme]); + } + fr->bvdwtab = FALSE; + fr->bcoultab = FALSE; + } + + /* Tables are used for direct ewald sum */ + if (fr->bEwald) + { + if (EEL_PME(ir->coulombtype)) + { + if (fp) + { + fprintf(fp, "Will do PME sum in reciprocal space for electrostatic interactions.\n"); + } + if (ir->coulombtype == eelP3M_AD) + { + please_cite(fp, "Hockney1988"); + please_cite(fp, "Ballenegger2012"); + } + else + { + please_cite(fp, "Essmann95a"); + } + + if (ir->ewald_geometry == eewg3DC) + { + if (fp) + { + fprintf(fp, "Using the Ewald3DC correction for systems with a slab geometry.\n"); + } + please_cite(fp, "In-Chul99a"); + } + } + fr->ewaldcoeff_q = calc_ewaldcoeff_q(ir->rcoulomb, ir->ewald_rtol); + init_ewald_tab(&(fr->ewald_table), ir, fp); + if (fp) + { + fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for Ewald\n", + 1/fr->ewaldcoeff_q); + } + } + + if (EVDW_PME(ir->vdwtype)) + { + if (fp) + { + fprintf(fp, "Will do PME sum in reciprocal space for LJ dispersion interactions.\n"); + } + please_cite(fp, "Essmann95a"); + fr->ewaldcoeff_lj = calc_ewaldcoeff_lj(ir->rvdw, ir->ewald_rtol_lj); + if (fp) + { + fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for LJ Ewald\n", + 1/fr->ewaldcoeff_lj); + } + } + + /* Electrostatics */ + fr->epsilon_r = ir->epsilon_r; + fr->epsilon_rf = ir->epsilon_rf; + fr->fudgeQQ = mtop->ffparams.fudgeQQ; + fr->rcoulomb_switch = ir->rcoulomb_switch; + fr->rcoulomb = cutoff_inf(ir->rcoulomb); + + /* Parameters for generalized RF */ + fr->zsquare = 0.0; + fr->temp = 0.0; + + if (fr->eeltype == eelGRF) + { + init_generalized_rf(fp, mtop, ir, fr); + } + + fr->bF_NoVirSum = (EEL_FULL(fr->eeltype) || EVDW_PME(fr->vdwtype) || + gmx_mtop_ftype_count(mtop, F_POSRES) > 0 || + gmx_mtop_ftype_count(mtop, F_FBPOSRES) > 0 || + IR_ELEC_FIELD(*ir) || + (fr->adress_icor != eAdressICOff) + ); + + if (fr->cutoff_scheme == ecutsGROUP && + ncg_mtop(mtop) > fr->cg_nalloc && !DOMAINDECOMP(cr)) + { + /* Count the total number of charge groups */ + fr->cg_nalloc = ncg_mtop(mtop); + srenew(fr->cg_cm, fr->cg_nalloc); + } + if (fr->shift_vec == NULL) + { + snew(fr->shift_vec, SHIFTS); + } + + if (fr->fshift == NULL) + { + snew(fr->fshift, SHIFTS); + } + + if (fr->nbfp == NULL) + { + fr->ntype = mtop->ffparams.atnr; + fr->nbfp = mk_nbfp(&mtop->ffparams, fr->bBHAM); + if (EVDW_PME(fr->vdwtype)) + { + fr->ljpme_c6grid = make_ljpme_c6grid(&mtop->ffparams, fr); + } + } + + /* Copy the energy group exclusions */ + fr->egp_flags = ir->opts.egp_flags; + + /* Van der Waals stuff */ + fr->rvdw = cutoff_inf(ir->rvdw); + fr->rvdw_switch = ir->rvdw_switch; + if ((fr->vdwtype != evdwCUT) && (fr->vdwtype != evdwUSER) && !fr->bBHAM) + { + if (fr->rvdw_switch >= fr->rvdw) + { + gmx_fatal(FARGS, "rvdw_switch (%f) must be < rvdw (%f)", + fr->rvdw_switch, fr->rvdw); + } + if (fp) + { + fprintf(fp, "Using %s Lennard-Jones, switch between %g and %g nm\n", + (fr->eeltype == eelSWITCH) ? "switched" : "shifted", + fr->rvdw_switch, fr->rvdw); + } + } + + if (fr->bBHAM && EVDW_PME(fr->vdwtype)) + { + gmx_fatal(FARGS, "LJ PME not supported with Buckingham"); + } + + if (fr->bBHAM && (fr->vdwtype == evdwSHIFT || fr->vdwtype == evdwSWITCH)) + { + gmx_fatal(FARGS, "Switch/shift interaction not supported with Buckingham"); ++ } ++ ++ if (fr->bBHAM && fr->cutoff_scheme == ecutsVERLET) ++ { ++ gmx_fatal(FARGS, "Verlet cutoff-scheme is not supported with Buckingham"); + } + + if (fp) + { + fprintf(fp, "Cut-off's: NS: %g Coulomb: %g %s: %g\n", + fr->rlist, fr->rcoulomb, fr->bBHAM ? "BHAM" : "LJ", fr->rvdw); + } + + fr->eDispCorr = ir->eDispCorr; + if (ir->eDispCorr != edispcNO) + { + set_avcsixtwelve(fp, fr, mtop); + } + + if (fr->bBHAM) + { + set_bham_b_max(fp, fr, mtop); + } + + fr->gb_epsilon_solvent = ir->gb_epsilon_solvent; + + /* Copy the GBSA data (radius, volume and surftens for each + * atomtype) from the topology atomtype section to forcerec. + */ + snew(fr->atype_radius, fr->ntype); + snew(fr->atype_vol, fr->ntype); + snew(fr->atype_surftens, fr->ntype); + snew(fr->atype_gb_radius, fr->ntype); + snew(fr->atype_S_hct, fr->ntype); + + if (mtop->atomtypes.nr > 0) + { + for (i = 0; i < fr->ntype; i++) + { + fr->atype_radius[i] = mtop->atomtypes.radius[i]; + } + for (i = 0; i < fr->ntype; i++) + { + fr->atype_vol[i] = mtop->atomtypes.vol[i]; + } + for (i = 0; i < fr->ntype; i++) + { + fr->atype_surftens[i] = mtop->atomtypes.surftens[i]; + } + for (i = 0; i < fr->ntype; i++) + { + fr->atype_gb_radius[i] = mtop->atomtypes.gb_radius[i]; + } + for (i = 0; i < fr->ntype; i++) + { + fr->atype_S_hct[i] = mtop->atomtypes.S_hct[i]; + } + } + + /* Generate the GB table if needed */ + if (fr->bGB) + { +#ifdef GMX_DOUBLE + fr->gbtabscale = 2000; +#else + fr->gbtabscale = 500; +#endif + + fr->gbtabr = 100; + fr->gbtab = make_gb_table(oenv, fr); + + init_gb(&fr->born, fr, ir, mtop, ir->gb_algorithm); + + /* Copy local gb data (for dd, this is done in dd_partition_system) */ + if (!DOMAINDECOMP(cr)) + { + make_local_gb(cr, fr->born, ir->gb_algorithm); + } + } + + /* Set the charge scaling */ + if (fr->epsilon_r != 0) + { + fr->epsfac = ONE_4PI_EPS0/fr->epsilon_r; + } + else + { + /* eps = 0 is infinite dieletric: no coulomb interactions */ + fr->epsfac = 0; + } + + /* Reaction field constants */ + if (EEL_RF(fr->eeltype)) + { + calc_rffac(fp, fr->eeltype, fr->epsilon_r, fr->epsilon_rf, + fr->rcoulomb, fr->temp, fr->zsquare, box, + &fr->kappa, &fr->k_rf, &fr->c_rf); + } + + /*This now calculates sum for q and c6*/ + set_chargesum(fp, fr, mtop); + + /* if we are using LR electrostatics, and they are tabulated, + * the tables will contain modified coulomb interactions. + * Since we want to use the non-shifted ones for 1-4 + * coulombic interactions, we must have an extra set of tables. + */ + + /* Construct tables. + * A little unnecessary to make both vdw and coul tables sometimes, + * but what the heck... */ + + bMakeTables = fr->bcoultab || fr->bvdwtab || fr->bEwald || + (ir->eDispCorr != edispcNO && ir_vdw_switched(ir)); + + bMakeSeparate14Table = ((!bMakeTables || fr->eeltype != eelCUT || fr->vdwtype != evdwCUT || + fr->bBHAM || fr->bEwald) && + (gmx_mtop_ftype_count(mtop, F_LJ14) > 0 || + gmx_mtop_ftype_count(mtop, F_LJC14_Q) > 0 || + gmx_mtop_ftype_count(mtop, F_LJC_PAIRS_NB) > 0)); + + negp_pp = ir->opts.ngener - ir->nwall; + negptable = 0; + if (!bMakeTables) + { + bSomeNormalNbListsAreInUse = TRUE; + fr->nnblists = 1; + } + else + { + bSomeNormalNbListsAreInUse = (ir->eDispCorr != edispcNO); + for (egi = 0; egi < negp_pp; egi++) + { + for (egj = egi; egj < negp_pp; egj++) + { + egp_flags = ir->opts.egp_flags[GID(egi, egj, ir->opts.ngener)]; + if (!(egp_flags & EGP_EXCL)) + { + if (egp_flags & EGP_TABLE) + { + negptable++; + } + else + { + bSomeNormalNbListsAreInUse = TRUE; + } + } + } + } + if (bSomeNormalNbListsAreInUse) + { + fr->nnblists = negptable + 1; + } + else + { + fr->nnblists = negptable; + } + if (fr->nnblists > 1) + { + snew(fr->gid2nblists, ir->opts.ngener*ir->opts.ngener); + } + } + + if (ir->adress) + { + fr->nnblists *= 2; + } + + snew(fr->nblists, fr->nnblists); + + /* This code automatically gives table length tabext without cut-off's, + * in that case grompp should already have checked that we do not need + * normal tables and we only generate tables for 1-4 interactions. + */ + rtab = ir->rlistlong + ir->tabext; + + if (bMakeTables) + { + /* make tables for ordinary interactions */ + if (bSomeNormalNbListsAreInUse) + { + make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, NULL, NULL, &fr->nblists[0]); + if (ir->adress) + { + make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, NULL, NULL, &fr->nblists[fr->nnblists/2]); + } + if (!bMakeSeparate14Table) + { + fr->tab14 = fr->nblists[0].table_elec_vdw; + } + m = 1; + } + else + { + m = 0; + } + if (negptable > 0) + { + /* Read the special tables for certain energy group pairs */ + nm_ind = mtop->groups.grps[egcENER].nm_ind; + for (egi = 0; egi < negp_pp; egi++) + { + for (egj = egi; egj < negp_pp; egj++) + { + egp_flags = ir->opts.egp_flags[GID(egi, egj, ir->opts.ngener)]; + if ((egp_flags & EGP_TABLE) && !(egp_flags & EGP_EXCL)) + { + nbl = &(fr->nblists[m]); + if (fr->nnblists > 1) + { + fr->gid2nblists[GID(egi, egj, ir->opts.ngener)] = m; + } + /* Read the table file with the two energy groups names appended */ + make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, + *mtop->groups.grpname[nm_ind[egi]], + *mtop->groups.grpname[nm_ind[egj]], + &fr->nblists[m]); + if (ir->adress) + { + make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, + *mtop->groups.grpname[nm_ind[egi]], + *mtop->groups.grpname[nm_ind[egj]], + &fr->nblists[fr->nnblists/2+m]); + } + m++; + } + else if (fr->nnblists > 1) + { + fr->gid2nblists[GID(egi, egj, ir->opts.ngener)] = 0; + } + } + } + } + } + if (bMakeSeparate14Table) + { + /* generate extra tables with plain Coulomb for 1-4 interactions only */ + fr->tab14 = make_tables(fp, oenv, fr, MASTER(cr), tabpfn, rtab, + GMX_MAKETABLES_14ONLY); + } + + /* Read AdResS Thermo Force table if needed */ + if (fr->adress_icor == eAdressICThermoForce) + { + /* old todo replace */ + + if (ir->adress->n_tf_grps > 0) + { + make_adress_tf_tables(fp, oenv, fr, ir, tabfn, mtop, box); + + } + else + { + /* load the default table */ + snew(fr->atf_tabs, 1); + fr->atf_tabs[DEFAULT_TF_TABLE] = make_atf_table(fp, oenv, fr, tabafn, box); + } + } + + /* Wall stuff */ + fr->nwall = ir->nwall; + if (ir->nwall && ir->wall_type == ewtTABLE) + { + make_wall_tables(fp, oenv, ir, tabfn, &mtop->groups, fr); + } + + if (fcd && tabbfn) + { + fcd->bondtab = make_bonded_tables(fp, + F_TABBONDS, F_TABBONDSNC, + mtop, tabbfn, "b"); + fcd->angletab = make_bonded_tables(fp, + F_TABANGLES, -1, + mtop, tabbfn, "a"); + fcd->dihtab = make_bonded_tables(fp, + F_TABDIHS, -1, + mtop, tabbfn, "d"); + } + else + { + if (debug) + { + fprintf(debug, "No fcdata or table file name passed, can not read table, can not do bonded interactions\n"); + } + } + + /* QM/MM initialization if requested + */ + if (ir->bQMMM) + { + fprintf(stderr, "QM/MM calculation requested.\n"); + } + + fr->bQMMM = ir->bQMMM; + fr->qr = mk_QMMMrec(); + + /* Set all the static charge group info */ + fr->cginfo_mb = init_cginfo_mb(fp, mtop, fr, bNoSolvOpt, + &bFEP_NonBonded, + &fr->bExcl_IntraCGAll_InterCGNone); + if (DOMAINDECOMP(cr)) + { + fr->cginfo = NULL; + } + else + { + fr->cginfo = cginfo_expand(mtop->nmolblock, fr->cginfo_mb); + } + + if (!DOMAINDECOMP(cr)) + { + forcerec_set_ranges(fr, ncg_mtop(mtop), ncg_mtop(mtop), + mtop->natoms, mtop->natoms, mtop->natoms); + } + + fr->print_force = print_force; + + + /* coarse load balancing vars */ + fr->t_fnbf = 0.; + fr->t_wait = 0.; + fr->timesteps = 0; + + /* Initialize neighbor search */ + init_ns(fp, cr, &fr->ns, fr, mtop); + + if (cr->duty & DUTY_PP) + { + gmx_nonbonded_setup(fr, bGenericKernelOnly); + /* + if (ir->bAdress) + { + gmx_setup_adress_kernels(fp,bGenericKernelOnly); + } + */ + } + + /* Initialize the thread working data for bonded interactions */ + init_forcerec_f_threads(fr, mtop->groups.grps[egcENER].nr); + + snew(fr->excl_load, fr->nthreads+1); + + if (fr->cutoff_scheme == ecutsVERLET) + { + if (ir->rcoulomb != ir->rvdw) + { + gmx_fatal(FARGS, "With Verlet lists rcoulomb and rvdw should be identical"); + } + + init_nb_verlet(fp, &fr->nbv, bFEP_NonBonded, ir, fr, cr, nbpu_opt); + } + + /* fr->ic is used both by verlet and group kernels (to some extent) now */ + init_interaction_const(fp, cr, &fr->ic, fr, rtab); + + if (ir->eDispCorr != edispcNO) + { + calc_enervirdiff(fp, ir->eDispCorr, fr); + } +} + +#define pr_real(fp, r) fprintf(fp, "%s: %e\n",#r, r) +#define pr_int(fp, i) fprintf((fp), "%s: %d\n",#i, i) +#define pr_bool(fp, b) fprintf((fp), "%s: %s\n",#b, bool_names[b]) + +void pr_forcerec(FILE *fp, t_forcerec *fr) +{ + int i; + + pr_real(fp, fr->rlist); + pr_real(fp, fr->rcoulomb); + pr_real(fp, fr->fudgeQQ); + pr_bool(fp, fr->bGrid); + pr_bool(fp, fr->bTwinRange); + /*pr_int(fp,fr->cg0); + pr_int(fp,fr->hcg);*/ + for (i = 0; i < fr->nnblists; i++) + { + pr_int(fp, fr->nblists[i].table_elec_vdw.n); + } + pr_real(fp, fr->rcoulomb_switch); + pr_real(fp, fr->rcoulomb); + + fflush(fp); +} + +void forcerec_set_excl_load(t_forcerec *fr, + const gmx_localtop_t *top) +{ + const int *ind, *a; + int t, i, j, ntot, n, ntarget; + + ind = top->excls.index; + a = top->excls.a; + + ntot = 0; + for (i = 0; i < top->excls.nr; i++) + { + for (j = ind[i]; j < ind[i+1]; j++) + { + if (a[j] > i) + { + ntot++; + } + } + } + + fr->excl_load[0] = 0; + n = 0; + i = 0; + for (t = 1; t <= fr->nthreads; t++) + { + ntarget = (ntot*t)/fr->nthreads; + while (i < top->excls.nr && n < ntarget) + { + for (j = ind[i]; j < ind[i+1]; j++) + { + if (a[j] > i) + { + n++; + } + } + i++; + } + fr->excl_load[t] = i; + } +} diff --cc src/gromacs/mdlib/md_support.c index 1558e45fa4,0000000000..748768687b mode 100644,000000..100644 --- a/src/gromacs/mdlib/md_support.c +++ b/src/gromacs/mdlib/md_support.c @@@ -1,765 -1,0 +1,771 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "typedefs.h" +#include "gromacs/utility/cstringutil.h" +#include "gromacs/utility/smalloc.h" +#include "mdrun.h" +#include "domdec.h" +#include "mtop_util.h" +#include "vcm.h" +#include "nrnb.h" +#include "macros.h" +#include "md_logging.h" +#include "md_support.h" ++#include "names.h" + +#include "gromacs/timing/wallcycle.h" + +/* Is the signal in one simulation independent of other simulations? */ +gmx_bool gs_simlocal[eglsNR] = { TRUE, FALSE, FALSE, TRUE }; + +/* check which of the multisim simulations has the shortest number of + steps and return that number of nsteps */ +gmx_int64_t get_multisim_nsteps(const t_commrec *cr, + gmx_int64_t nsteps) +{ + gmx_int64_t steps_out; + + if (MASTER(cr)) + { + gmx_int64_t *buf; + int s; + + snew(buf, cr->ms->nsim); + + buf[cr->ms->sim] = nsteps; + gmx_sumli_sim(cr->ms->nsim, buf, cr->ms); + + steps_out = -1; + for (s = 0; s < cr->ms->nsim; s++) + { + /* find the smallest positive number */ + if (buf[s] >= 0 && ((steps_out < 0) || (buf[s] < steps_out)) ) + { + steps_out = buf[s]; + } + } + sfree(buf); + + /* if we're the limiting simulation, don't do anything */ + if (steps_out >= 0 && steps_out < nsteps) + { + char strbuf[255]; + snprintf(strbuf, 255, "Will stop simulation %%d after %s steps (another simulation will end then).\n", "%"GMX_PRId64); + fprintf(stderr, strbuf, cr->ms->sim, steps_out); + } + } + /* broadcast to non-masters */ + gmx_bcast(sizeof(gmx_int64_t), &steps_out, cr); + return steps_out; +} + +int multisim_min(const gmx_multisim_t *ms, int nmin, int n) +{ + int *buf; + gmx_bool bPos, bEqual; + int s, d; + + snew(buf, ms->nsim); + buf[ms->sim] = n; + gmx_sumi_sim(ms->nsim, buf, ms); + bPos = TRUE; + bEqual = TRUE; + for (s = 0; s < ms->nsim; s++) + { + bPos = bPos && (buf[s] > 0); + bEqual = bEqual && (buf[s] == buf[0]); + } + if (bPos) + { + if (bEqual) + { + nmin = min(nmin, buf[0]); + } + else + { + /* Find the least common multiple */ + for (d = 2; d < nmin; d++) + { + s = 0; + while (s < ms->nsim && d % buf[s] == 0) + { + s++; + } + if (s == ms->nsim) + { + /* We found the LCM and it is less than nmin */ + nmin = d; + break; + } + } + } + } + sfree(buf); + + return nmin; +} + +int multisim_nstsimsync(const t_commrec *cr, + const t_inputrec *ir, int repl_ex_nst) +{ + int nmin; + + if (MASTER(cr)) + { + nmin = INT_MAX; + nmin = multisim_min(cr->ms, nmin, ir->nstlist); + nmin = multisim_min(cr->ms, nmin, ir->nstcalcenergy); + nmin = multisim_min(cr->ms, nmin, repl_ex_nst); + if (nmin == INT_MAX) + { + gmx_fatal(FARGS, "Can not find an appropriate interval for inter-simulation communication, since nstlist, nstcalcenergy and -replex are all <= 0"); + } + /* Avoid inter-simulation communication at every (second) step */ + if (nmin <= 2) + { + nmin = 10; + } + } + + gmx_bcast(sizeof(int), &nmin, cr); + + return nmin; +} + +void init_global_signals(globsig_t *gs, const t_commrec *cr, + const t_inputrec *ir, int repl_ex_nst) +{ + int i; + + if (MULTISIM(cr)) + { + gs->nstms = multisim_nstsimsync(cr, ir, repl_ex_nst); + if (debug) + { + fprintf(debug, "Syncing simulations for checkpointing and termination every %d steps\n", gs->nstms); + } + } + else + { + gs->nstms = 1; + } + + for (i = 0; i < eglsNR; i++) + { + gs->sig[i] = 0; + gs->set[i] = 0; + } +} + +void copy_coupling_state(t_state *statea, t_state *stateb, + gmx_ekindata_t *ekinda, gmx_ekindata_t *ekindb, t_grpopts* opts) +{ + + /* MRS note -- might be able to get rid of some of the arguments. Look over it when it's all debugged */ + + int i, j, nc; + + /* Make sure we have enough space for x and v */ + if (statea->nalloc > stateb->nalloc) + { + stateb->nalloc = statea->nalloc; + srenew(stateb->x, stateb->nalloc); + srenew(stateb->v, stateb->nalloc); + } + + stateb->natoms = statea->natoms; + stateb->ngtc = statea->ngtc; + stateb->nnhpres = statea->nnhpres; + stateb->veta = statea->veta; + if (ekinda) + { + copy_mat(ekinda->ekin, ekindb->ekin); + for (i = 0; i < stateb->ngtc; i++) + { + ekindb->tcstat[i].T = ekinda->tcstat[i].T; + ekindb->tcstat[i].Th = ekinda->tcstat[i].Th; + copy_mat(ekinda->tcstat[i].ekinh, ekindb->tcstat[i].ekinh); + copy_mat(ekinda->tcstat[i].ekinf, ekindb->tcstat[i].ekinf); + ekindb->tcstat[i].ekinscalef_nhc = ekinda->tcstat[i].ekinscalef_nhc; + ekindb->tcstat[i].ekinscaleh_nhc = ekinda->tcstat[i].ekinscaleh_nhc; + ekindb->tcstat[i].vscale_nhc = ekinda->tcstat[i].vscale_nhc; + } + } + copy_rvecn(statea->x, stateb->x, 0, stateb->natoms); + copy_rvecn(statea->v, stateb->v, 0, stateb->natoms); + copy_mat(statea->box, stateb->box); + copy_mat(statea->box_rel, stateb->box_rel); + copy_mat(statea->boxv, stateb->boxv); + + for (i = 0; i < stateb->ngtc; i++) + { + nc = i*opts->nhchainlength; + for (j = 0; j < opts->nhchainlength; j++) + { + stateb->nosehoover_xi[nc+j] = statea->nosehoover_xi[nc+j]; + stateb->nosehoover_vxi[nc+j] = statea->nosehoover_vxi[nc+j]; + } + } + if (stateb->nhpres_xi != NULL) + { + for (i = 0; i < stateb->nnhpres; i++) + { + nc = i*opts->nhchainlength; + for (j = 0; j < opts->nhchainlength; j++) + { + stateb->nhpres_xi[nc+j] = statea->nhpres_xi[nc+j]; + stateb->nhpres_vxi[nc+j] = statea->nhpres_vxi[nc+j]; + } + } + } +} + +real compute_conserved_from_auxiliary(t_inputrec *ir, t_state *state, t_extmass *MassQ) +{ + real quantity = 0; + switch (ir->etc) + { + case etcNO: + break; + case etcBERENDSEN: + break; + case etcNOSEHOOVER: + quantity = NPT_energy(ir, state, MassQ); + break; + case etcVRESCALE: + quantity = vrescale_energy(&(ir->opts), state->therm_integral); + break; + default: + break; + } + return quantity; +} + +void compute_globals(FILE *fplog, gmx_global_stat_t gstat, t_commrec *cr, t_inputrec *ir, + t_forcerec *fr, gmx_ekindata_t *ekind, + t_state *state, t_state *state_global, t_mdatoms *mdatoms, + t_nrnb *nrnb, t_vcm *vcm, gmx_wallcycle_t wcycle, + gmx_enerdata_t *enerd, tensor force_vir, tensor shake_vir, tensor total_vir, + tensor pres, rvec mu_tot, gmx_constr_t constr, + globsig_t *gs, gmx_bool bInterSimGS, + matrix box, gmx_mtop_t *top_global, + gmx_bool *bSumEkinhOld, int flags) +{ + int i, gsi; + real gs_buf[eglsNR]; + tensor corr_vir, corr_pres; + gmx_bool bEner, bPres, bTemp, bVV; + gmx_bool bRerunMD, bStopCM, bGStat, bIterate, + bFirstIterate, bReadEkin, bEkinAveVel, bScaleEkin, bConstrain; + real ekin, temp, prescorr, enercorr, dvdlcorr, dvdl_ekin; + + /* translate CGLO flags to gmx_booleans */ + bRerunMD = flags & CGLO_RERUNMD; + bStopCM = flags & CGLO_STOPCM; + bGStat = flags & CGLO_GSTAT; + + bReadEkin = (flags & CGLO_READEKIN); + bScaleEkin = (flags & CGLO_SCALEEKIN); + bEner = flags & CGLO_ENERGY; + bTemp = flags & CGLO_TEMPERATURE; + bPres = (flags & CGLO_PRESSURE); + bConstrain = (flags & CGLO_CONSTRAINT); + bIterate = (flags & CGLO_ITERATE); + bFirstIterate = (flags & CGLO_FIRSTITERATE); + + /* we calculate a full state kinetic energy either with full-step velocity verlet + or half step where we need the pressure */ + + bEkinAveVel = (ir->eI == eiVV || (ir->eI == eiVVAK && bPres) || bReadEkin); + + /* in initalization, it sums the shake virial in vv, and to + sums ekinh_old in leapfrog (or if we are calculating ekinh_old) for other reasons */ + + /* ########## Kinetic energy ############## */ + + if (bTemp) + { + /* Non-equilibrium MD: this is parallellized, but only does communication + * when there really is NEMD. + */ + + if (PAR(cr) && (ekind->bNEMD)) + { + accumulate_u(cr, &(ir->opts), ekind); + } + debug_gmx(); + if (bReadEkin) + { + restore_ekinstate_from_state(cr, ekind, &state_global->ekinstate); + } + else + { + + calc_ke_part(state, &(ir->opts), mdatoms, ekind, nrnb, bEkinAveVel, bIterate); + } + + debug_gmx(); + } + + /* Calculate center of mass velocity if necessary, also parallellized */ + if (bStopCM) + { + calc_vcm_grp(0, mdatoms->homenr, mdatoms, + state->x, state->v, vcm); + } + + if (bTemp || bStopCM || bPres || bEner || bConstrain) + { + if (!bGStat) + { + /* We will not sum ekinh_old, + * so signal that we still have to do it. + */ + *bSumEkinhOld = TRUE; + + } + else + { + if (gs != NULL) + { + for (i = 0; i < eglsNR; i++) + { + gs_buf[i] = gs->sig[i]; + } + } + if (PAR(cr)) + { + wallcycle_start(wcycle, ewcMoveE); + global_stat(fplog, gstat, cr, enerd, force_vir, shake_vir, mu_tot, + ir, ekind, constr, bStopCM ? vcm : NULL, + gs != NULL ? eglsNR : 0, gs_buf, + top_global, state, + *bSumEkinhOld, flags); + wallcycle_stop(wcycle, ewcMoveE); + } + if (gs != NULL) + { + if (MULTISIM(cr) && bInterSimGS) + { + if (MASTER(cr)) + { + /* Communicate the signals between the simulations */ + gmx_sum_sim(eglsNR, gs_buf, cr->ms); + } + /* Communicate the signals form the master to the others */ + gmx_bcast(eglsNR*sizeof(gs_buf[0]), gs_buf, cr); + } + for (i = 0; i < eglsNR; i++) + { + if (bInterSimGS || gs_simlocal[i]) + { + /* Set the communicated signal only when it is non-zero, + * since signals might not be processed at each MD step. + */ + gsi = (gs_buf[i] >= 0 ? + (int)(gs_buf[i] + 0.5) : + (int)(gs_buf[i] - 0.5)); + if (gsi != 0) + { + gs->set[i] = gsi; + } + /* Turn off the local signal */ + gs->sig[i] = 0; + } + } + } + *bSumEkinhOld = FALSE; + } + } + + if (!ekind->bNEMD && debug && bTemp && (vcm->nr > 0)) + { + correct_ekin(debug, + 0, mdatoms->homenr, + state->v, vcm->group_p[0], + mdatoms->massT, mdatoms->tmass, ekind->ekin); + } + + /* Do center of mass motion removal */ + if (bStopCM) + { + check_cm_grp(fplog, vcm, ir, 1); + do_stopcm_grp(0, mdatoms->homenr, mdatoms->cVCM, + state->x, state->v, vcm); + inc_nrnb(nrnb, eNR_STOPCM, mdatoms->homenr); + } + + if (bEner) + { + /* Calculate the amplitude of the cosine velocity profile */ + ekind->cosacc.vcos = ekind->cosacc.mvcos/mdatoms->tmass; + } + + if (bTemp) + { + /* Sum the kinetic energies of the groups & calc temp */ + /* compute full step kinetic energies if vv, or if vv-avek and we are computing the pressure with IR_NPT_TROTTER */ + /* three maincase: VV with AveVel (md-vv), vv with AveEkin (md-vv-avek), leap with AveEkin (md). + Leap with AveVel is not supported; it's not clear that it will actually work. + bEkinAveVel: If TRUE, we simply multiply ekin by ekinscale to get a full step kinetic energy. + If FALSE, we average ekinh_old and ekinh*ekinscale_nhc to get an averaged half step kinetic energy. + bSaveEkinOld: If TRUE (in the case of iteration = bIterate is TRUE), we don't reset the ekinscale_nhc. + If FALSE, we go ahead and erase over it. + */ + enerd->term[F_TEMP] = sum_ekin(&(ir->opts), ekind, &dvdl_ekin, + bEkinAveVel, bScaleEkin); + enerd->dvdl_lin[efptMASS] = (double) dvdl_ekin; + + enerd->term[F_EKIN] = trace(ekind->ekin); + } + + /* ########## Long range energy information ###### */ + + if (bEner || bPres || bConstrain) + { + calc_dispcorr(fplog, ir, fr, 0, top_global->natoms, box, state->lambda[efptVDW], + corr_pres, corr_vir, &prescorr, &enercorr, &dvdlcorr); + } + + if (bEner && bFirstIterate) + { + enerd->term[F_DISPCORR] = enercorr; + enerd->term[F_EPOT] += enercorr; + enerd->term[F_DVDL_VDW] += dvdlcorr; + } + + /* ########## Now pressure ############## */ + if (bPres || bConstrain) + { + + m_add(force_vir, shake_vir, total_vir); + + /* Calculate pressure and apply LR correction if PPPM is used. + * Use the box from last timestep since we already called update(). + */ + + enerd->term[F_PRES] = calc_pres(fr->ePBC, ir->nwall, box, ekind->ekin, total_vir, pres); + + /* Calculate long range corrections to pressure and energy */ + /* this adds to enerd->term[F_PRES] and enerd->term[F_ETOT], + and computes enerd->term[F_DISPCORR]. Also modifies the + total_vir and pres tesors */ + + m_add(total_vir, corr_vir, total_vir); + m_add(pres, corr_pres, pres); + enerd->term[F_PDISPCORR] = prescorr; + enerd->term[F_PRES] += prescorr; + } +} + +void check_nst_param(FILE *fplog, t_commrec *cr, + const char *desc_nst, int nst, + const char *desc_p, int *p) +{ + if (*p > 0 && *p % nst != 0) + { + /* Round up to the next multiple of nst */ + *p = ((*p)/nst + 1)*nst; + md_print_warn(cr, fplog, + "NOTE: %s changes %s to %d\n", desc_nst, desc_p, *p); + } +} + +void set_current_lambdas(gmx_int64_t step, t_lambda *fepvals, gmx_bool bRerunMD, + t_trxframe *rerun_fr, t_state *state_global, t_state *state, double lam0[]) +/* find the current lambdas. If rerunning, we either read in a state, or a lambda value, + requiring different logic. */ +{ + real frac; + int i, fep_state = 0; + if (bRerunMD) + { + if (rerun_fr->bLambda) + { + if (fepvals->delta_lambda == 0) + { + state_global->lambda[efptFEP] = rerun_fr->lambda; + for (i = 0; i < efptNR; i++) + { + if (i != efptFEP) + { + state->lambda[i] = state_global->lambda[i]; + } + } + } + else + { + /* find out between which two value of lambda we should be */ + frac = (step*fepvals->delta_lambda); + fep_state = floor(frac*fepvals->n_lambda); + /* interpolate between this state and the next */ + /* this assumes that the initial lambda corresponds to lambda==0, which is verified in grompp */ + frac = (frac*fepvals->n_lambda)-fep_state; + for (i = 0; i < efptNR; i++) + { + state_global->lambda[i] = lam0[i] + (fepvals->all_lambda[i][fep_state]) + + frac*(fepvals->all_lambda[i][fep_state+1]-fepvals->all_lambda[i][fep_state]); + } + } + } + else if (rerun_fr->bFepState) + { + state_global->fep_state = rerun_fr->fep_state; + for (i = 0; i < efptNR; i++) + { + state_global->lambda[i] = fepvals->all_lambda[i][fep_state]; + } + } + } + else + { + if (fepvals->delta_lambda != 0) + { + /* find out between which two value of lambda we should be */ + frac = (step*fepvals->delta_lambda); + if (fepvals->n_lambda > 0) + { + fep_state = floor(frac*fepvals->n_lambda); + /* interpolate between this state and the next */ + /* this assumes that the initial lambda corresponds to lambda==0, which is verified in grompp */ + frac = (frac*fepvals->n_lambda)-fep_state; + for (i = 0; i < efptNR; i++) + { + state_global->lambda[i] = lam0[i] + (fepvals->all_lambda[i][fep_state]) + + frac*(fepvals->all_lambda[i][fep_state+1]-fepvals->all_lambda[i][fep_state]); + } + } + else + { + for (i = 0; i < efptNR; i++) + { + state_global->lambda[i] = lam0[i] + frac; + } + } + } + else + { + if (state->fep_state > 0) + { + state_global->fep_state = state->fep_state; /* state->fep is the one updated by bExpanded */ + for (i = 0; i < efptNR; i++) + { + state_global->lambda[i] = fepvals->all_lambda[i][state_global->fep_state]; + } + } + } + } + for (i = 0; i < efptNR; i++) + { + state->lambda[i] = state_global->lambda[i]; + } +} + +static void min_zero(int *n, int i) +{ + if (i > 0 && (*n == 0 || i < *n)) + { + *n = i; + } +} + +static int lcd4(int i1, int i2, int i3, int i4) +{ + int nst; + + nst = 0; + min_zero(&nst, i1); + min_zero(&nst, i2); + min_zero(&nst, i3); + min_zero(&nst, i4); + if (nst == 0) + { + gmx_incons("All 4 inputs for determininig nstglobalcomm are <= 0"); + } + + while (nst > 1 && ((i1 > 0 && i1 % nst != 0) || + (i2 > 0 && i2 % nst != 0) || + (i3 > 0 && i3 % nst != 0) || + (i4 > 0 && i4 % nst != 0))) + { + nst--; + } + + return nst; +} + +int check_nstglobalcomm(FILE *fplog, t_commrec *cr, + int nstglobalcomm, t_inputrec *ir) +{ + if (!EI_DYNAMICS(ir->eI)) + { + nstglobalcomm = 1; + } + + if (nstglobalcomm == -1) + { + if (!(ir->nstcalcenergy > 0 || + ir->nstlist > 0 || + ir->etc != etcNO || + ir->epc != epcNO)) + { + nstglobalcomm = 10; + if (ir->nstenergy > 0 && ir->nstenergy < nstglobalcomm) + { + nstglobalcomm = ir->nstenergy; + } + } + else + { + /* Ensure that we do timely global communication for + * (possibly) each of the four following options. + */ + nstglobalcomm = lcd4(ir->nstcalcenergy, + ir->nstlist, + ir->etc != etcNO ? ir->nsttcouple : 0, + ir->epc != epcNO ? ir->nstpcouple : 0); + } + } + else + { + if (ir->nstlist > 0 && + nstglobalcomm > ir->nstlist && nstglobalcomm % ir->nstlist != 0) + { + nstglobalcomm = (nstglobalcomm / ir->nstlist)*ir->nstlist; + md_print_warn(cr, fplog, "WARNING: nstglobalcomm is larger than nstlist, but not a multiple, setting it to %d\n", nstglobalcomm); + } + if (ir->nstcalcenergy > 0) + { + check_nst_param(fplog, cr, "-gcom", nstglobalcomm, + "nstcalcenergy", &ir->nstcalcenergy); + } + if (ir->etc != etcNO && ir->nsttcouple > 0) + { + check_nst_param(fplog, cr, "-gcom", nstglobalcomm, + "nsttcouple", &ir->nsttcouple); + } + if (ir->epc != epcNO && ir->nstpcouple > 0) + { + check_nst_param(fplog, cr, "-gcom", nstglobalcomm, + "nstpcouple", &ir->nstpcouple); + } + + check_nst_param(fplog, cr, "-gcom", nstglobalcomm, + "nstenergy", &ir->nstenergy); + + check_nst_param(fplog, cr, "-gcom", nstglobalcomm, + "nstlog", &ir->nstlog); + } + + if (ir->comm_mode != ecmNO && ir->nstcomm < nstglobalcomm) + { + md_print_warn(cr, fplog, "WARNING: Changing nstcomm from %d to %d\n", + ir->nstcomm, nstglobalcomm); + ir->nstcomm = nstglobalcomm; + } + + return nstglobalcomm; +} + +void check_ir_old_tpx_versions(t_commrec *cr, FILE *fplog, + t_inputrec *ir, gmx_mtop_t *mtop) +{ + /* Check required for old tpx files */ + if (IR_TWINRANGE(*ir) && ir->nstlist > 1 && + ir->nstcalcenergy % ir->nstlist != 0) + { + md_print_warn(cr, fplog, "Old tpr file with twin-range settings: modifying energy calculation and/or T/P-coupling frequencies\n"); + + if (gmx_mtop_ftype_count(mtop, F_CONSTR) + + gmx_mtop_ftype_count(mtop, F_CONSTRNC) > 0 && + ir->eConstrAlg == econtSHAKE) + { + md_print_warn(cr, fplog, "With twin-range cut-off's and SHAKE the virial and pressure are incorrect\n"); + if (ir->epc != epcNO) + { + gmx_fatal(FARGS, "Can not do pressure coupling with twin-range cut-off's and SHAKE"); + } + } + check_nst_param(fplog, cr, "nstlist", ir->nstlist, + "nstcalcenergy", &ir->nstcalcenergy); + if (ir->epc != epcNO) + { + check_nst_param(fplog, cr, "nstlist", ir->nstlist, + "nstpcouple", &ir->nstpcouple); + } + check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy, + "nstenergy", &ir->nstenergy); + check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy, + "nstlog", &ir->nstlog); + if (ir->efep != efepNO) + { + check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy, + "nstdhdl", &ir->fepvals->nstdhdl); + } + } ++ ++ if (EI_VV(ir->eI) && IR_TWINRANGE(*ir) && ir->nstlist > 1) ++ { ++ gmx_fatal(FARGS, "Twin-range multiple time stepping does not work with integrator %s.", ei_names[ir->eI]); ++ } +} + +void rerun_parallel_comm(t_commrec *cr, t_trxframe *fr, + gmx_bool *bNotLastFrame) +{ + gmx_bool bAlloc; + rvec *xp, *vp; + + bAlloc = (fr->natoms == 0); + + if (MASTER(cr) && !*bNotLastFrame) + { + fr->natoms = -1; + } + xp = fr->x; + vp = fr->v; + gmx_bcast(sizeof(*fr), fr, cr); + fr->x = xp; + fr->v = vp; + + *bNotLastFrame = (fr->natoms >= 0); + +} diff --cc src/gromacs/pulling/pull.c index b2200918f8,0000000000..a4500a4d4e mode 100644,000000..100644 --- a/src/gromacs/pulling/pull.c +++ b/src/gromacs/pulling/pull.c @@@ -1,1272 -1,0 +1,1272 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include +#include +#include +#include "gromacs/fileio/futil.h" +#include "index.h" +#include "gromacs/fileio/gmxfio.h" +#include "vec.h" +#include "typedefs.h" +#include "types/commrec.h" +#include "network.h" +#include "gromacs/fileio/filenm.h" +#include +#include "gromacs/utility/smalloc.h" +#include "pull.h" +#include "xvgr.h" +#include "names.h" +#include "pbc.h" +#include "mtop_util.h" +#include "mdrun.h" +#include "gmx_ga2la.h" +#include "copyrite.h" +#include "macros.h" +#include "vec.h" + +static void pull_print_group_x(FILE *out, ivec dim, const t_pull_group *pgrp) +{ + int m; + + for (m = 0; m < DIM; m++) + { + if (dim[m]) + { + fprintf(out, "\t%g", pgrp->x[m]); + } + } +} + +static void pull_print_coord_dr(FILE *out, ivec dim, const t_pull_coord *pcrd) +{ + int m; + + for (m = 0; m < DIM; m++) + { + if (dim[m]) + { + fprintf(out, "\t%g", pcrd->dr[m]); + } + } +} + +static void pull_print_x(FILE *out, t_pull *pull, double t) +{ + int c; + const t_pull_coord *pcrd; + + fprintf(out, "%.4f", t); + + for (c = 0; c < pull->ncoord; c++) + { + pcrd = &pull->coord[c]; + + if (pull->bPrintRef) + { + if (PULL_CYL(pull)) + { + pull_print_group_x(out, pull->dim, &pull->dyna[c]); + } + else + { + pull_print_group_x(out, pull->dim, &pull->group[pcrd->group[0]]); + } + } + pull_print_coord_dr(out, pull->dim, pcrd); + } + fprintf(out, "\n"); +} + +static void pull_print_f(FILE *out, t_pull *pull, double t) +{ + int c, d; + + fprintf(out, "%.4f", t); + + for (c = 0; c < pull->ncoord; c++) + { + fprintf(out, "\t%g", pull->coord[c].f_scal); + } + fprintf(out, "\n"); +} + +void pull_print_output(t_pull *pull, gmx_int64_t step, double time) +{ + if ((pull->nstxout != 0) && (step % pull->nstxout == 0)) + { + pull_print_x(pull->out_x, pull, time); + } + + if ((pull->nstfout != 0) && (step % pull->nstfout == 0)) + { + pull_print_f(pull->out_f, pull, time); + } +} + +static FILE *open_pull_out(const char *fn, t_pull *pull, const output_env_t oenv, + gmx_bool bCoord, unsigned long Flags) +{ + FILE *fp; + int nsets, c, m; + char **setname, buf[10]; + + if (Flags & MD_APPENDFILES) + { + fp = gmx_fio_fopen(fn, "a+"); + } + else + { + fp = gmx_fio_fopen(fn, "w+"); + if (bCoord) + { + xvgr_header(fp, "Pull COM", "Time (ps)", "Position (nm)", + exvggtXNY, oenv); + } + else + { + xvgr_header(fp, "Pull force", "Time (ps)", "Force (kJ/mol/nm)", + exvggtXNY, oenv); + } + + snew(setname, 2*pull->ncoord*DIM); + nsets = 0; + for (c = 0; c < pull->ncoord; c++) + { + if (bCoord) + { + if (pull->bPrintRef) + { + for (m = 0; m < DIM; m++) + { + if (pull->dim[m]) + { + sprintf(buf, "%d %s%c", c+1, "c", 'X'+m); + setname[nsets] = strdup(buf); + nsets++; + } + } + } + for (m = 0; m < DIM; m++) + { + if (pull->dim[m]) + { + sprintf(buf, "%d %s%c", c+1, "d", 'X'+m); + setname[nsets] = strdup(buf); + nsets++; + } + } + } + else + { + sprintf(buf, "%d", c+1); + setname[nsets] = strdup(buf); + nsets++; + } + } + if (nsets > 1) + { + xvgr_legend(fp, nsets, (const char**)setname, oenv); + } + for (c = 0; c < nsets; c++) + { + sfree(setname[c]); + } + sfree(setname); + } + + return fp; +} + +/* Apply forces in a mass weighted fashion */ +static void apply_forces_grp(const t_pull_group *pgrp, const t_mdatoms *md, + const dvec f_pull, int sign, rvec *f) +{ + int i, ii, m; + double wmass, inv_wm; + + inv_wm = pgrp->wscale*pgrp->invtm; + + for (i = 0; i < pgrp->nat_loc; i++) + { + ii = pgrp->ind_loc[i]; + wmass = md->massT[ii]; + if (pgrp->weight_loc) + { + wmass *= pgrp->weight_loc[i]; + } + + for (m = 0; m < DIM; m++) + { + f[ii][m] += sign * wmass * f_pull[m] * inv_wm; + } + } +} + +/* Apply forces in a mass weighted fashion */ +static void apply_forces(t_pull * pull, t_mdatoms * md, rvec *f) +{ + int c; + const t_pull_coord *pcrd; + + for (c = 0; c < pull->ncoord; c++) + { + pcrd = &pull->coord[c]; + + if (PULL_CYL(pull)) + { + apply_forces_grp(&pull->dyna[c], md, pcrd->f, -1, f); + } + else + { + if (pull->group[pcrd->group[0]].nat > 0) + { + apply_forces_grp(&pull->group[pcrd->group[0]], md, pcrd->f, -1, f); + } + } + apply_forces_grp(&pull->group[pcrd->group[1]], md, pcrd->f, 1, f); + } +} + +static double max_pull_distance2(const t_pull *pull, const t_pbc *pbc) +{ + double max_d2; + int m; + + max_d2 = GMX_DOUBLE_MAX; + + if (pull->eGeom != epullgDIRPBC) + { + for (m = 0; m < pbc->ndim_ePBC; m++) + { + if (pull->dim[m] != 0) + { + max_d2 = min(max_d2, norm2(pbc->box[m])); + } + } + } + + return 0.25*max_d2; +} + +static void low_get_pull_coord_dr(const t_pull *pull, + const t_pull_coord *pcrd, + const t_pbc *pbc, double t, + dvec xg, dvec xref, double max_dist2, + dvec dr) +{ + const t_pull_group *pgrp0, *pgrp1; + int m; + dvec xrefr, dref = {0, 0, 0}; + double dr2; + + pgrp0 = &pull->group[pcrd->group[0]]; + pgrp1 = &pull->group[pcrd->group[1]]; + + /* Only the first group can be an absolute reference, in that case nat=0 */ + if (pgrp0->nat == 0) + { + for (m = 0; m < DIM; m++) + { + xref[m] = pcrd->origin[m]; + } + } + + copy_dvec(xref, xrefr); + + if (pull->eGeom == epullgDIRPBC) + { + for (m = 0; m < DIM; m++) + { + dref[m] = (pcrd->init + pcrd->rate*t)*pcrd->vec[m]; + } + /* Add the reference position, so we use the correct periodic image */ + dvec_inc(xrefr, dref); + } + + pbc_dx_d(pbc, xg, xrefr, dr); + dr2 = 0; + for (m = 0; m < DIM; m++) + { + dr[m] *= pull->dim[m]; + dr2 += dr[m]*dr[m]; + } + if (max_dist2 >= 0 && dr2 > 0.98*0.98*max_dist2) + { - gmx_fatal(FARGS, "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the box size (%f)", ++ gmx_fatal(FARGS, "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the box size (%f).\nYou might want to consider using \"pull-geometry = direction-periodic\" instead.\n", + pcrd->group[0], pcrd->group[1], sqrt(dr2), sqrt(max_dist2)); + } + + if (pull->eGeom == epullgDIRPBC) + { + dvec_inc(dr, dref); + } +} + +static void get_pull_coord_dr(const t_pull *pull, + int coord_ind, + const t_pbc *pbc, double t, + dvec dr) +{ + double md2; + const t_pull_coord *pcrd; + + if (pull->eGeom == epullgDIRPBC) + { + md2 = -1; + } + else + { + md2 = max_pull_distance2(pull, pbc); + } + + pcrd = &pull->coord[coord_ind]; + + low_get_pull_coord_dr(pull, pcrd, pbc, t, + pull->group[pcrd->group[1]].x, + PULL_CYL(pull) ? pull->dyna[coord_ind].x : pull->group[pcrd->group[0]].x, + md2, + dr); +} + +void get_pull_coord_distance(const t_pull *pull, + int coord_ind, + const t_pbc *pbc, double t, + dvec dr, double *dev) +{ + static gmx_bool bWarned = FALSE; /* TODO: this should be fixed for thread-safety, + but is fairly benign */ + const t_pull_coord *pcrd; + int m; + double ref, drs, inpr; + + pcrd = &pull->coord[coord_ind]; + + get_pull_coord_dr(pull, coord_ind, pbc, t, dr); + + ref = pcrd->init + pcrd->rate*t; + + switch (pull->eGeom) + { + case epullgDIST: + /* Pull along the vector between the com's */ + if (ref < 0 && !bWarned) + { + fprintf(stderr, "\nPull reference distance for coordinate %d is negative (%f)\n", coord_ind+1, ref); + bWarned = TRUE; + } + drs = dnorm(dr); + if (drs == 0) + { + /* With no vector we can not determine the direction for the force, + * so we set the force to zero. + */ + *dev = 0; + } + else + { + /* Determine the deviation */ + *dev = drs - ref; + } + break; + case epullgDIR: + case epullgDIRPBC: + case epullgCYL: + /* Pull along vec */ + inpr = 0; + for (m = 0; m < DIM; m++) + { + inpr += pcrd->vec[m]*dr[m]; + } + *dev = inpr - ref; + break; + } +} + +void clear_pull_forces(t_pull *pull) +{ + int i; + + /* Zeroing the forces is only required for constraint pulling. + * It can happen that multiple constraint steps need to be applied + * and therefore the constraint forces need to be accumulated. + */ + for (i = 0; i < pull->ncoord; i++) + { + clear_dvec(pull->coord[i].f); + pull->coord[i].f_scal = 0; + } +} + +/* Apply constraint using SHAKE */ +static void do_constraint(t_pull *pull, t_pbc *pbc, + rvec *x, rvec *v, + gmx_bool bMaster, tensor vir, + double dt, double t) +{ + + dvec *r_ij; /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */ + dvec unc_ij; /* xp[i] com of i this step, before constr. -> unc_ij */ + dvec *rnew; /* current 'new' positions of the groups */ + double *dr_tot; /* the total update of the coords */ + double ref; + dvec vec; + double d0, inpr; + double lambda, rm, mass, invdt = 0; + gmx_bool bConverged_all, bConverged = FALSE; + int niter = 0, g, c, ii, j, m, max_iter = 100; + double a; + dvec f; /* the pull force */ + dvec tmp, tmp3; + t_pull_group *pdyna, *pgrp0, *pgrp1; + t_pull_coord *pcrd; + + snew(r_ij, pull->ncoord); + snew(dr_tot, pull->ncoord); + + snew(rnew, pull->ngroup); + + /* copy the current unconstrained positions for use in iterations. We + iterate until rinew[i] and rjnew[j] obey the constraints. Then + rinew - pull.x_unc[i] is the correction dr to group i */ + for (g = 0; g < pull->ngroup; g++) + { + copy_dvec(pull->group[g].xp, rnew[g]); + } + if (PULL_CYL(pull)) + { + /* There is only one pull coordinate and reference group */ + copy_dvec(pull->dyna[0].xp, rnew[pull->coord[0].group[0]]); + } + + /* Determine the constraint directions from the old positions */ + for (c = 0; c < pull->ncoord; c++) + { + get_pull_coord_dr(pull, c, pbc, t, r_ij[c]); + /* Store the difference vector at time t for printing */ + copy_dvec(r_ij[c], pull->coord[c].dr); + if (debug) + { + fprintf(debug, "Pull coord %d dr %f %f %f\n", + c, r_ij[c][XX], r_ij[c][YY], r_ij[c][ZZ]); + } + + if (pull->eGeom == epullgDIR || pull->eGeom == epullgDIRPBC) + { + /* Select the component along vec */ + a = 0; + for (m = 0; m < DIM; m++) + { + a += pull->coord[c].vec[m]*r_ij[c][m]; + } + for (m = 0; m < DIM; m++) + { + r_ij[c][m] = a*pull->coord[c].vec[m]; + } + } + } + + bConverged_all = FALSE; + while (!bConverged_all && niter < max_iter) + { + bConverged_all = TRUE; + + /* loop over all constraints */ + for (c = 0; c < pull->ncoord; c++) + { + dvec dr0, dr1; + + pcrd = &pull->coord[c]; + pgrp0 = &pull->group[pcrd->group[0]]; + pgrp1 = &pull->group[pcrd->group[1]]; + + /* Get the current difference vector */ + low_get_pull_coord_dr(pull, pcrd, pbc, t, + rnew[pcrd->group[1]], + rnew[pcrd->group[0]], + -1, unc_ij); + + ref = pcrd->init + pcrd->rate*t; + + if (debug) + { + fprintf(debug, "Pull coord %d, iteration %d\n", c, niter); + } + + rm = 1.0/(pgrp0->invtm + pgrp1->invtm); + + switch (pull->eGeom) + { + case epullgDIST: + if (ref <= 0) + { + gmx_fatal(FARGS, "The pull constraint reference distance for group %d is <= 0 (%f)", c, ref); + } + + { + double q, c_a, c_b, c_c; + + c_a = diprod(r_ij[c], r_ij[c]); + c_b = diprod(unc_ij, r_ij[c])*2; + c_c = diprod(unc_ij, unc_ij) - dsqr(ref); + + if (c_b < 0) + { + q = -0.5*(c_b - sqrt(c_b*c_b - 4*c_a*c_c)); + lambda = -q/c_a; + } + else + { + q = -0.5*(c_b + sqrt(c_b*c_b - 4*c_a*c_c)); + lambda = -c_c/q; + } + + if (debug) + { + fprintf(debug, + "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n", + c_a, c_b, c_c, lambda); + } + } + + /* The position corrections dr due to the constraints */ + dsvmul(-lambda*rm*pgrp1->invtm, r_ij[c], dr1); + dsvmul( lambda*rm*pgrp0->invtm, r_ij[c], dr0); + dr_tot[c] += -lambda*dnorm(r_ij[c]); + break; + case epullgDIR: + case epullgDIRPBC: + case epullgCYL: + /* A 1-dimensional constraint along a vector */ + a = 0; + for (m = 0; m < DIM; m++) + { + vec[m] = pcrd->vec[m]; + a += unc_ij[m]*vec[m]; + } + /* Select only the component along the vector */ + dsvmul(a, vec, unc_ij); + lambda = a - ref; + if (debug) + { + fprintf(debug, "Pull inpr %e lambda: %e\n", a, lambda); + } + + /* The position corrections dr due to the constraints */ + dsvmul(-lambda*rm*pgrp1->invtm, vec, dr1); + dsvmul( lambda*rm*pgrp0->invtm, vec, dr0); + dr_tot[c] += -lambda; + break; + } + + /* DEBUG */ + if (debug) + { + int g0, g1; + + g0 = pcrd->group[0]; + g1 = pcrd->group[1]; + low_get_pull_coord_dr(pull, pcrd, pbc, t, rnew[g1], rnew[g0], -1, tmp); + low_get_pull_coord_dr(pull, pcrd, pbc, t, dr1, dr0, -1, tmp3); + fprintf(debug, + "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n", + rnew[g0][0], rnew[g0][1], rnew[g0][2], + rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp)); + fprintf(debug, + "Pull ref %8s %8s %8s %8s %8s %8s d: %8.5f\n", + "", "", "", "", "", "", ref); + fprintf(debug, + "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n", + dr0[0], dr0[1], dr0[2], + dr1[0], dr1[1], dr1[2], + dnorm(tmp3)); + } /* END DEBUG */ + + /* Update the COMs with dr */ + dvec_inc(rnew[pcrd->group[1]], dr1); + dvec_inc(rnew[pcrd->group[0]], dr0); + } + + /* Check if all constraints are fullfilled now */ + for (c = 0; c < pull->ncoord; c++) + { + pcrd = &pull->coord[c]; + + low_get_pull_coord_dr(pull, pcrd, pbc, t, + rnew[pcrd->group[1]], + rnew[pcrd->group[0]], + -1, unc_ij); + + switch (pull->eGeom) + { + case epullgDIST: + bConverged = fabs(dnorm(unc_ij) - ref) < pull->constr_tol; + break; + case epullgDIR: + case epullgDIRPBC: + case epullgCYL: + for (m = 0; m < DIM; m++) + { + vec[m] = pcrd->vec[m]; + } + inpr = diprod(unc_ij, vec); + dsvmul(inpr, vec, unc_ij); + bConverged = + fabs(diprod(unc_ij, vec) - ref) < pull->constr_tol; + break; + } + + if (!bConverged) + { + if (debug) + { + fprintf(debug, "NOT CONVERGED YET: Group %d:" + "d_ref = %f, current d = %f\n", + g, ref, dnorm(unc_ij)); + } + + bConverged_all = FALSE; + } + } + + niter++; + /* if after all constraints are dealt with and bConverged is still TRUE + we're finished, if not we do another iteration */ + } + if (niter > max_iter) + { + gmx_fatal(FARGS, "Too many iterations for constraint run: %d", niter); + } + + /* DONE ITERATING, NOW UPDATE COORDINATES AND CALC. CONSTRAINT FORCES */ + + if (v) + { + invdt = 1/dt; + } + + /* update atoms in the groups */ + for (g = 0; g < pull->ngroup; g++) + { + const t_pull_group *pgrp; + dvec dr; + + if (PULL_CYL(pull) && g == pull->coord[0].group[0]) + { + pgrp = &pull->dyna[0]; + } + else + { + pgrp = &pull->group[g]; + } + + /* get the final constraint displacement dr for group g */ + dvec_sub(rnew[g], pgrp->xp, dr); + /* select components of dr */ + for (m = 0; m < DIM; m++) + { + dr[m] *= pull->dim[m]; + } + + /* update the atom positions */ + copy_dvec(dr, tmp); + for (j = 0; j < pgrp->nat_loc; j++) + { + ii = pgrp->ind_loc[j]; + if (pgrp->weight_loc) + { + dsvmul(pgrp->wscale*pgrp->weight_loc[j], dr, tmp); + } + for (m = 0; m < DIM; m++) + { + x[ii][m] += tmp[m]; + } + if (v) + { + for (m = 0; m < DIM; m++) + { + v[ii][m] += invdt*tmp[m]; + } + } + } + } + + /* calculate the constraint forces, used for output and virial only */ + for (c = 0; c < pull->ncoord; c++) + { + pcrd = &pull->coord[c]; + pcrd->f_scal = dr_tot[c]/((pull->group[pcrd->group[0]].invtm + pull->group[pcrd->group[1]].invtm)*dt*dt); + + if (vir && bMaster) + { + double f_invr; + + /* Add the pull contribution to the virial */ + f_invr = pcrd->f_scal/dnorm(r_ij[c]); + + for (j = 0; j < DIM; j++) + { + for (m = 0; m < DIM; m++) + { + vir[j][m] -= 0.5*f_invr*r_ij[c][j]*r_ij[c][m]; + } + } + } + } + + /* finished! I hope. Give back some memory */ + sfree(r_ij); + sfree(dr_tot); + sfree(rnew); +} + +/* Pulling with a harmonic umbrella potential or constant force */ +static void do_pull_pot(int ePull, + t_pull *pull, t_pbc *pbc, double t, real lambda, + real *V, tensor vir, real *dVdl) +{ + int c, j, m; + double dev, ndr, invdr; + real k, dkdl; + t_pull_coord *pcrd; + + /* loop over the pull coordinates */ + *V = 0; + *dVdl = 0; + for (c = 0; c < pull->ncoord; c++) + { + pcrd = &pull->coord[c]; + + get_pull_coord_distance(pull, c, pbc, t, pcrd->dr, &dev); + + k = (1.0 - lambda)*pcrd->k + lambda*pcrd->kB; + dkdl = pcrd->kB - pcrd->k; + + switch (pull->eGeom) + { + case epullgDIST: + ndr = dnorm(pcrd->dr); + invdr = 1/ndr; + if (ePull == epullUMBRELLA) + { + pcrd->f_scal = -k*dev; + *V += 0.5* k*dsqr(dev); + *dVdl += 0.5*dkdl*dsqr(dev); + } + else + { + pcrd->f_scal = -k; + *V += k*ndr; + *dVdl += dkdl*ndr; + } + for (m = 0; m < DIM; m++) + { + pcrd->f[m] = pcrd->f_scal*pcrd->dr[m]*invdr; + } + break; + case epullgDIR: + case epullgDIRPBC: + case epullgCYL: + if (ePull == epullUMBRELLA) + { + pcrd->f_scal = -k*dev; + *V += 0.5* k*dsqr(dev); + *dVdl += 0.5*dkdl*dsqr(dev); + } + else + { + ndr = 0; + for (m = 0; m < DIM; m++) + { + ndr += pcrd->vec[m]*pcrd->dr[m]; + } + pcrd->f_scal = -k; + *V += k*ndr; + *dVdl += dkdl*ndr; + } + for (m = 0; m < DIM; m++) + { + pcrd->f[m] = pcrd->f_scal*pcrd->vec[m]; + } + break; + } + + if (vir) + { + /* Add the pull contribution to the virial */ + for (j = 0; j < DIM; j++) + { + for (m = 0; m < DIM; m++) + { + vir[j][m] -= 0.5*pcrd->f[j]*pcrd->dr[m]; + } + } + } + } +} + +real pull_potential(int ePull, t_pull *pull, t_mdatoms *md, t_pbc *pbc, + t_commrec *cr, double t, real lambda, + rvec *x, rvec *f, tensor vir, real *dvdlambda) +{ + real V, dVdl; + + pull_calc_coms(cr, pull, md, pbc, t, x, NULL); + + do_pull_pot(ePull, pull, pbc, t, lambda, + &V, pull->bVirial && MASTER(cr) ? vir : NULL, &dVdl); + + /* Distribute forces over pulled groups */ + apply_forces(pull, md, f); + + if (MASTER(cr)) + { + *dvdlambda += dVdl; + } + + return (MASTER(cr) ? V : 0.0); +} + +void pull_constraint(t_pull *pull, t_mdatoms *md, t_pbc *pbc, + t_commrec *cr, double dt, double t, + rvec *x, rvec *xp, rvec *v, tensor vir) +{ + pull_calc_coms(cr, pull, md, pbc, t, x, xp); + + do_constraint(pull, pbc, xp, v, pull->bVirial && MASTER(cr), vir, dt, t); +} + +static void make_local_pull_group(gmx_ga2la_t ga2la, + t_pull_group *pg, int start, int end) +{ + int i, ii; + + pg->nat_loc = 0; + for (i = 0; i < pg->nat; i++) + { + ii = pg->ind[i]; + if (ga2la) + { + if (!ga2la_get_home(ga2la, ii, &ii)) + { + ii = -1; + } + } + if (ii >= start && ii < end) + { + /* This is a home atom, add it to the local pull group */ + if (pg->nat_loc >= pg->nalloc_loc) + { + pg->nalloc_loc = over_alloc_dd(pg->nat_loc+1); + srenew(pg->ind_loc, pg->nalloc_loc); + if (pg->epgrppbc == epgrppbcCOS || pg->weight) + { + srenew(pg->weight_loc, pg->nalloc_loc); + } + } + pg->ind_loc[pg->nat_loc] = ii; + if (pg->weight) + { + pg->weight_loc[pg->nat_loc] = pg->weight[i]; + } + pg->nat_loc++; + } + } +} + +void dd_make_local_pull_groups(gmx_domdec_t *dd, t_pull *pull, t_mdatoms *md) +{ + gmx_ga2la_t ga2la; + int g; + + if (dd) + { + ga2la = dd->ga2la; + } + else + { + ga2la = NULL; + } + + for (g = 0; g < pull->ngroup; g++) + { + make_local_pull_group(ga2la, &pull->group[g], + 0, md->homenr); + } +} + +static void init_pull_group_index(FILE *fplog, t_commrec *cr, + int g, t_pull_group *pg, ivec pulldims, + gmx_mtop_t *mtop, t_inputrec *ir, real lambda) +{ + int i, ii, d, nfrozen, ndim; + real m, w, mbd; + double tmass, wmass, wwmass; + gmx_groups_t *groups; + gmx_mtop_atomlookup_t alook; + t_atom *atom; + + if (EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD) + { + /* There are no masses in the integrator. + * But we still want to have the correct mass-weighted COMs. + * So we store the real masses in the weights. + * We do not set nweight, so these weights do not end up in the tpx file. + */ + if (pg->nweight == 0) + { + snew(pg->weight, pg->nat); + } + } + + if (cr && PAR(cr)) + { + pg->nat_loc = 0; + pg->nalloc_loc = 0; + pg->ind_loc = NULL; + pg->weight_loc = NULL; + } + else + { + pg->nat_loc = pg->nat; + pg->ind_loc = pg->ind; + if (pg->epgrppbc == epgrppbcCOS) + { + snew(pg->weight_loc, pg->nat); + } + else + { + pg->weight_loc = pg->weight; + } + } + + groups = &mtop->groups; + + alook = gmx_mtop_atomlookup_init(mtop); + + nfrozen = 0; + tmass = 0; + wmass = 0; + wwmass = 0; + for (i = 0; i < pg->nat; i++) + { + ii = pg->ind[i]; + gmx_mtop_atomnr_to_atom(alook, ii, &atom); + if (ir->opts.nFreeze) + { + for (d = 0; d < DIM; d++) + { + if (pulldims[d] && ir->opts.nFreeze[ggrpnr(groups, egcFREEZE, ii)][d]) + { + nfrozen++; + } + } + } + if (ir->efep == efepNO) + { + m = atom->m; + } + else + { + m = (1 - lambda)*atom->m + lambda*atom->mB; + } + if (pg->nweight > 0) + { + w = pg->weight[i]; + } + else + { + w = 1; + } + if (EI_ENERGY_MINIMIZATION(ir->eI)) + { + /* Move the mass to the weight */ + w *= m; + m = 1; + pg->weight[i] = w; + } + else if (ir->eI == eiBD) + { + if (ir->bd_fric) + { + mbd = ir->bd_fric*ir->delta_t; + } + else + { + if (groups->grpnr[egcTC] == NULL) + { + mbd = ir->delta_t/ir->opts.tau_t[0]; + } + else + { + mbd = ir->delta_t/ir->opts.tau_t[groups->grpnr[egcTC][ii]]; + } + } + w *= m/mbd; + m = mbd; + pg->weight[i] = w; + } + tmass += m; + wmass += m*w; + wwmass += m*w*w; + } + + gmx_mtop_atomlookup_destroy(alook); + + if (wmass == 0) + { + gmx_fatal(FARGS, "The total%s mass of pull group %d is zero", + pg->weight ? " weighted" : "", g); + } + if (fplog) + { + fprintf(fplog, + "Pull group %d: %5d atoms, mass %9.3f", g, pg->nat, tmass); + if (pg->weight || EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD) + { + fprintf(fplog, ", weighted mass %9.3f", wmass*wmass/wwmass); + } + if (pg->epgrppbc == epgrppbcCOS) + { + fprintf(fplog, ", cosine weighting will be used"); + } + fprintf(fplog, "\n"); + } + + if (nfrozen == 0) + { + /* A value > 0 signals not frozen, it is updated later */ + pg->invtm = 1.0; + } + else + { + ndim = 0; + for (d = 0; d < DIM; d++) + { + ndim += pulldims[d]*pg->nat; + } + if (fplog && nfrozen > 0 && nfrozen < ndim) + { + fprintf(fplog, + "\nWARNING: In pull group %d some, but not all of the degrees of freedom\n" + " that are subject to pulling are frozen.\n" + " For pulling the whole group will be frozen.\n\n", + g); + } + pg->invtm = 0.0; + pg->wscale = 1.0; + } +} + +void init_pull(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[], + gmx_mtop_t *mtop, t_commrec *cr, const output_env_t oenv, real lambda, + gmx_bool bOutFile, unsigned long Flags) +{ + t_pull *pull; + t_pull_group *pgrp; + int c, g, start = 0, end = 0, m; + + pull = ir->pull; + + pull->ePBC = ir->ePBC; + switch (pull->ePBC) + { + case epbcNONE: pull->npbcdim = 0; break; + case epbcXY: pull->npbcdim = 2; break; + default: pull->npbcdim = 3; break; + } + + if (fplog) + { + gmx_bool bAbs, bCos; + + bAbs = FALSE; + for (c = 0; c < pull->ncoord; c++) + { + if (pull->group[pull->coord[c].group[0]].nat == 0 || + pull->group[pull->coord[c].group[1]].nat == 0) + { + bAbs = TRUE; + } + } + + fprintf(fplog, "\nWill apply %s COM pulling in geometry '%s'\n", + EPULLTYPE(ir->ePull), EPULLGEOM(pull->eGeom)); + fprintf(fplog, "with %d pull coordinate%s and %d group%s\n", + pull->ncoord, pull->ncoord == 1 ? "" : "s", + pull->ngroup, pull->ngroup == 1 ? "" : "s"); + if (bAbs) + { + fprintf(fplog, "with an absolute reference\n"); + } + bCos = FALSE; + for (g = 0; g < pull->ngroup; g++) + { + if (pull->group[g].nat > 1 && + pull->group[g].pbcatom < 0) + { + /* We are using cosine weighting */ + fprintf(fplog, "Cosine weighting is used for group %d\n", g); + bCos = TRUE; + } + } + if (bCos) + { + please_cite(fplog, "Engin2010"); + } + } + + /* We always add the virial contribution, + * except for geometry = direction_periodic where this is impossible. + */ + pull->bVirial = (pull->eGeom != epullgDIRPBC); + if (getenv("GMX_NO_PULLVIR") != NULL) + { + if (fplog) + { + fprintf(fplog, "Found env. var., will not add the virial contribution of the COM pull forces\n"); + } + pull->bVirial = FALSE; + } + + pull->rbuf = NULL; + pull->dbuf = NULL; + pull->dbuf_cyl = NULL; + pull->bRefAt = FALSE; + pull->cosdim = -1; + for (g = 0; g < pull->ngroup; g++) + { + pgrp = &pull->group[g]; + pgrp->epgrppbc = epgrppbcNONE; + if (pgrp->nat > 0) + { + /* Determine if we need to take PBC into account for calculating + * the COM's of the pull groups. + */ + for (m = 0; m < pull->npbcdim; m++) + { + if (pull->dim[m] && pgrp->nat > 1) + { + if (pgrp->pbcatom >= 0) + { + pgrp->epgrppbc = epgrppbcREFAT; + pull->bRefAt = TRUE; + } + else + { + if (pgrp->weight) + { + gmx_fatal(FARGS, "Pull groups can not have relative weights and cosine weighting at same time"); + } + pgrp->epgrppbc = epgrppbcCOS; + if (pull->cosdim >= 0 && pull->cosdim != m) + { + gmx_fatal(FARGS, "Can only use cosine weighting with pulling in one dimension (use mdp option pull_dim)"); + } + pull->cosdim = m; + } + } + } + /* Set the indices */ + init_pull_group_index(fplog, cr, g, pgrp, pull->dim, mtop, ir, lambda); + if (PULL_CYL(pull) && pgrp->invtm == 0) + { + gmx_fatal(FARGS, "Can not have frozen atoms in a cylinder pull group"); + } + } + else + { + /* Absolute reference, set the inverse mass to zero */ + pgrp->invtm = 0; + pgrp->wscale = 1; + } + } + + /* if we use dynamic reference groups, do some initialising for them */ + if (PULL_CYL(pull)) + { + if (ir->ePull == epullCONSTRAINT && pull->ncoord > 1) + { + /* We can't easily update the single reference group with multiple + * constraints. This would require recalculating COMs. + */ + gmx_fatal(FARGS, "Constraint COM pulling supports only one coordinate with geometry=cylinder, you can use umbrella pulling with multiple coordinates"); + } + + for (c = 0; c < pull->ncoord; c++) + { + if (pull->group[pull->coord[c].group[0]].nat == 0) + { + gmx_fatal(FARGS, "Dynamic reference groups are not supported when using absolute reference!\n"); + } + } + + snew(pull->dyna, pull->ncoord); + } + + /* Only do I/O when we are doing dynamics and if we are the MASTER */ + pull->out_x = NULL; + pull->out_f = NULL; + if (bOutFile) + { + if (pull->nstxout > 0) + { + pull->out_x = open_pull_out(opt2fn("-px", nfile, fnm), pull, oenv, TRUE, Flags); + } + if (pull->nstfout > 0) + { + pull->out_f = open_pull_out(opt2fn("-pf", nfile, fnm), pull, oenv, + FALSE, Flags); + } + } +} + +void finish_pull(t_pull *pull) +{ + if (pull->out_x) + { + gmx_fio_fclose(pull->out_x); + } + if (pull->out_f) + { + gmx_fio_fclose(pull->out_f); + } +} diff --cc src/programs/mdrun/md.c index e49e19b2f2,0000000000..018daf5d0e mode 100644,000000..100644 --- a/src/programs/mdrun/md.c +++ b/src/programs/mdrun/md.c @@@ -1,1993 -1,0 +1,2009 @@@ +/* + * This file is part of the GROMACS molecular simulation package. + * + * Copyright (c) 1991-2000, University of Groningen, The Netherlands. + * Copyright (c) 2001-2004, The GROMACS development team. + * Copyright (c) 2011,2012,2013,2014, by the GROMACS development team, led by + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, + * and including many others, as listed in the AUTHORS file in the + * top-level source directory and at http://www.gromacs.org. + * + * GROMACS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * GROMACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GROMACS; if not, see + * http://www.gnu.org/licenses, or write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * If you want to redistribute modifications to GROMACS, please + * consider that scientific software is very special. Version + * control is crucial - bugs must be traceable. We will be happy to + * consider code for inclusion in the official distribution, but + * derived work must not be called official GROMACS. Details are found + * in the README & COPYING files - if they are missing, get the + * official version at http://www.gromacs.org. + * + * To help us fund GROMACS development, we humbly ask that you cite + * the research papers on the package. Check out http://www.gromacs.org. + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "typedefs.h" +#include "gromacs/utility/smalloc.h" +#include "sysstuff.h" +#include "vec.h" +#include "vcm.h" +#include "mdebin.h" +#include "nrnb.h" +#include "calcmu.h" +#include "index.h" +#include "vsite.h" +#include "update.h" +#include "ns.h" +#include "mdrun.h" +#include "md_support.h" +#include "md_logging.h" +#include "network.h" +#include "xvgr.h" +#include "physics.h" +#include "names.h" +#include "force.h" +#include "disre.h" +#include "orires.h" +#include "pme.h" +#include "mdatoms.h" +#include "repl_ex.h" +#include "deform.h" +#include "qmmm.h" +#include "domdec.h" +#include "domdec_network.h" +#include "gromacs/gmxlib/topsort.h" +#include "coulomb.h" +#include "constr.h" +#include "shellfc.h" +#include "gromacs/gmxpreprocess/compute_io.h" +#include "checkpoint.h" +#include "mtop_util.h" +#include "sighandler.h" +#include "txtdump.h" +#include "gromacs/utility/cstringutil.h" +#include "pme_loadbal.h" +#include "bondf.h" +#include "membed.h" +#include "types/nlistheuristics.h" +#include "types/iteratedconstraints.h" +#include "nbnxn_cuda_data_mgmt.h" + +#include "gromacs/utility/gmxmpi.h" +#include "gromacs/fileio/confio.h" +#include "gromacs/fileio/trajectory_writing.h" +#include "gromacs/fileio/trnio.h" +#include "gromacs/fileio/trxio.h" +#include "gromacs/fileio/xtcio.h" +#include "gromacs/timing/wallcycle.h" +#include "gromacs/timing/walltime_accounting.h" +#include "gromacs/pulling/pull.h" +#include "gromacs/swap/swapcoords.h" +#include "gromacs/imd/imd.h" + +#ifdef GMX_FAHCORE +#include "corewrap.h" +#endif + +static void reset_all_counters(FILE *fplog, t_commrec *cr, + gmx_int64_t step, + gmx_int64_t *step_rel, t_inputrec *ir, + gmx_wallcycle_t wcycle, t_nrnb *nrnb, + gmx_walltime_accounting_t walltime_accounting, + nbnxn_cuda_ptr_t cu_nbv) +{ + char sbuf[STEPSTRSIZE]; + + /* Reset all the counters related to performance over the run */ + md_print_warn(cr, fplog, "step %s: resetting all time and cycle counters\n", + gmx_step_str(step, sbuf)); + + if (cu_nbv) + { + nbnxn_cuda_reset_timings(cu_nbv); + } + + wallcycle_stop(wcycle, ewcRUN); + wallcycle_reset_all(wcycle); + if (DOMAINDECOMP(cr)) + { + reset_dd_statistics_counters(cr->dd); + } + init_nrnb(nrnb); + ir->init_step += *step_rel; + ir->nsteps -= *step_rel; + *step_rel = 0; + wallcycle_start(wcycle, ewcRUN); + walltime_accounting_start(walltime_accounting); + print_date_and_time(fplog, cr->nodeid, "Restarted time", gmx_gettime()); +} + +double do_md(FILE *fplog, t_commrec *cr, int nfile, const t_filenm fnm[], + const output_env_t oenv, gmx_bool bVerbose, gmx_bool bCompact, + int nstglobalcomm, + gmx_vsite_t *vsite, gmx_constr_t constr, + int stepout, t_inputrec *ir, + gmx_mtop_t *top_global, + t_fcdata *fcd, + t_state *state_global, + t_mdatoms *mdatoms, + t_nrnb *nrnb, gmx_wallcycle_t wcycle, + gmx_edsam_t ed, t_forcerec *fr, + int repl_ex_nst, int repl_ex_nex, int repl_ex_seed, gmx_membed_t membed, + real cpt_period, real max_hours, + const char gmx_unused *deviceOptions, + int imdport, + unsigned long Flags, + gmx_walltime_accounting_t walltime_accounting) +{ + gmx_mdoutf_t outf = NULL; + gmx_int64_t step, step_rel; + double elapsed_time; + double t, t0, lam0[efptNR]; + gmx_bool bGStatEveryStep, bGStat, bCalcVir, bCalcEner; + gmx_bool bNS, bNStList, bSimAnn, bStopCM, bRerunMD, bNotLastFrame = FALSE, + bFirstStep, bStateFromCP, bStateFromTPX, bInitStep, bLastStep, + bBornRadii, bStartingFromCpt; + gmx_bool bDoDHDL = FALSE, bDoFEP = FALSE, bDoExpanded = FALSE; + gmx_bool do_ene, do_log, do_verbose, bRerunWarnNoV = TRUE, + bForceUpdate = FALSE, bCPT; + gmx_bool bMasterState; + int force_flags, cglo_flags; + tensor force_vir, shake_vir, total_vir, tmp_vir, pres; + int i, m; + t_trxstatus *status; + rvec mu_tot; + t_vcm *vcm; + t_state *bufstate = NULL; + matrix *scale_tot, pcoupl_mu, M, ebox; + gmx_nlheur_t nlh; + t_trxframe rerun_fr; + gmx_repl_ex_t repl_ex = NULL; + int nchkpt = 1; + gmx_localtop_t *top; + t_mdebin *mdebin = NULL; + t_state *state = NULL; + rvec *f_global = NULL; + gmx_enerdata_t *enerd; + rvec *f = NULL; + gmx_global_stat_t gstat; + gmx_update_t upd = NULL; + t_graph *graph = NULL; + globsig_t gs; + gmx_groups_t *groups; + gmx_ekindata_t *ekind, *ekind_save; + gmx_shellfc_t shellfc; + int count, nconverged = 0; + real timestep = 0; + double tcount = 0; + gmx_bool bConverged = TRUE, bOK, bSumEkinhOld, bExchanged, bNeedRepartition; + gmx_bool bAppend; + gmx_bool bResetCountersHalfMaxH = FALSE; + gmx_bool bVV, bIterativeCase, bFirstIterate, bTemp, bPres, bTrotter; + gmx_bool bUpdateDoLR; + real dvdl_constr; + rvec *cbuf = NULL; + matrix lastbox; + real veta_save, scalevir, tracevir; + real vetanew = 0; + int lamnew = 0; + /* for FEP */ + int nstfep; + double cycles; + real saved_conserved_quantity = 0; + real last_ekin = 0; + int iter_i; + t_extmass MassQ; + int **trotter_seq; + char sbuf[STEPSTRSIZE], sbuf2[STEPSTRSIZE]; + int handled_stop_condition = gmx_stop_cond_none; /* compare to get_stop_condition*/ + gmx_iterate_t iterate; + gmx_int64_t multisim_nsteps = -1; /* number of steps to do before first multisim + simulation stops. If equal to zero, don't + communicate any more between multisims.*/ + /* PME load balancing data for GPU kernels */ + pme_load_balancing_t pme_loadbal = NULL; + double cycles_pmes; + gmx_bool bPMETuneTry = FALSE, bPMETuneRunning = FALSE; + + /* Interactive MD */ + gmx_bool bIMDstep = FALSE; + +#ifdef GMX_FAHCORE + /* Temporary addition for FAHCORE checkpointing */ + int chkpt_ret; +#endif + + /* Check for special mdrun options */ + bRerunMD = (Flags & MD_RERUN); + bAppend = (Flags & MD_APPENDFILES); + if (Flags & MD_RESETCOUNTERSHALFWAY) + { + if (ir->nsteps > 0) + { + /* Signal to reset the counters half the simulation steps. */ + wcycle_set_reset_counters(wcycle, ir->nsteps/2); + } + /* Signal to reset the counters halfway the simulation time. */ + bResetCountersHalfMaxH = (max_hours > 0); + } + + /* md-vv uses averaged full step velocities for T-control + md-vv-avek uses averaged half step velocities for T-control (but full step ekin for P control) + md uses averaged half step kinetic energies to determine temperature unless defined otherwise by GMX_EKIN_AVE_VEL; */ + bVV = EI_VV(ir->eI); + if (bVV) /* to store the initial velocities while computing virial */ + { + snew(cbuf, top_global->natoms); + } + /* all the iteratative cases - only if there are constraints */ + bIterativeCase = ((IR_NPH_TROTTER(ir) || IR_NPT_TROTTER(ir)) && (constr) && (!bRerunMD)); + gmx_iterate_init(&iterate, FALSE); /* The default value of iterate->bIterationActive is set to + false in this step. The correct value, true or false, + is set at each step, as it depends on the frequency of temperature + and pressure control.*/ + bTrotter = (bVV && (IR_NPT_TROTTER(ir) || IR_NPH_TROTTER(ir) || IR_NVT_TROTTER(ir))); + + if (bRerunMD) + { + /* Since we don't know if the frames read are related in any way, + * rebuild the neighborlist at every step. + */ + ir->nstlist = 1; + ir->nstcalcenergy = 1; + nstglobalcomm = 1; + } + + check_ir_old_tpx_versions(cr, fplog, ir, top_global); + + nstglobalcomm = check_nstglobalcomm(fplog, cr, nstglobalcomm, ir); + bGStatEveryStep = (nstglobalcomm == 1); + + if (!bGStatEveryStep && ir->nstlist == -1 && fplog != NULL) + { + fprintf(fplog, + "To reduce the energy communication with nstlist = -1\n" + "the neighbor list validity should not be checked at every step,\n" + "this means that exact integration is not guaranteed.\n" + "The neighbor list validity is checked after:\n" + " - 2*std.dev.(n.list life time) steps.\n" + "In most cases this will result in exact integration.\n" + "This reduces the energy communication by a factor of 2 to 3.\n" + "If you want less energy communication, set nstlist > 3.\n\n"); + } + + if (bRerunMD) + { + ir->nstxout_compressed = 0; + } + groups = &top_global->groups; + + /* Initial values */ + init_md(fplog, cr, ir, oenv, &t, &t0, state_global->lambda, + &(state_global->fep_state), lam0, + nrnb, top_global, &upd, + nfile, fnm, &outf, &mdebin, + force_vir, shake_vir, mu_tot, &bSimAnn, &vcm, Flags); + + clear_mat(total_vir); + clear_mat(pres); + /* Energy terms and groups */ + snew(enerd, 1); + init_enerdata(top_global->groups.grps[egcENER].nr, ir->fepvals->n_lambda, + enerd); + if (DOMAINDECOMP(cr)) + { + f = NULL; + } + else + { + snew(f, top_global->natoms); + } + + /* Kinetic energy data */ + snew(ekind, 1); + init_ekindata(fplog, top_global, &(ir->opts), ekind); + /* needed for iteration of constraints */ + snew(ekind_save, 1); + init_ekindata(fplog, top_global, &(ir->opts), ekind_save); + /* Copy the cos acceleration to the groups struct */ + ekind->cosacc.cos_accel = ir->cos_accel; + + gstat = global_stat_init(ir); + debug_gmx(); + + /* Check for polarizable models and flexible constraints */ + shellfc = init_shell_flexcon(fplog, fr->cutoff_scheme == ecutsVERLET, + top_global, n_flexible_constraints(constr), + (ir->bContinuation || + (DOMAINDECOMP(cr) && !MASTER(cr))) ? + NULL : state_global->x); + ++ if (shellfc && ir->eI == eiNM) ++ { ++ /* Currently shells don't work with Normal Modes */ ++ gmx_fatal(FARGS, "Normal Mode analysis is not supported with shells.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n"); ++ } ++ ++ if (vsite && ir->eI == eiNM) ++ { ++ /* Currently virtual sites don't work with Normal Modes */ ++ gmx_fatal(FARGS, "Normal Mode analysis is not supported with virtual sites.\nIf you'd like to help with adding support, we have an open discussion at http://redmine.gromacs.org/issues/879\n"); ++ } ++ + if (DEFORM(*ir)) + { + tMPI_Thread_mutex_lock(&deform_init_box_mutex); + set_deform_reference_box(upd, + deform_init_init_step_tpx, + deform_init_box_tpx); + tMPI_Thread_mutex_unlock(&deform_init_box_mutex); + } + + { + double io = compute_io(ir, top_global->natoms, groups, mdebin->ebin->nener, 1); + if ((io > 2000) && MASTER(cr)) + { + fprintf(stderr, + "\nWARNING: This run will generate roughly %.0f Mb of data\n\n", + io); + } + } + + if (DOMAINDECOMP(cr)) + { + top = dd_init_local_top(top_global); + + snew(state, 1); + dd_init_local_state(cr->dd, state_global, state); + + if (DDMASTER(cr->dd) && ir->nstfout) + { + snew(f_global, state_global->natoms); + } + } + else + { + top = gmx_mtop_generate_local_top(top_global, ir); + + forcerec_set_excl_load(fr, top); + + state = serial_init_local_state(state_global); + f_global = f; + + atoms2md(top_global, ir, 0, NULL, top_global->natoms, mdatoms); + + if (vsite) + { + set_vsite_top(vsite, top, mdatoms, cr); + } + + if (ir->ePBC != epbcNONE && !fr->bMolPBC) + { + graph = mk_graph(fplog, &(top->idef), 0, top_global->natoms, FALSE, FALSE); + } + + if (shellfc) + { + make_local_shells(cr, mdatoms, shellfc); + } + + setup_bonded_threading(fr, &top->idef); + } + + /* Set up interactive MD (IMD) */ + init_IMD(ir, cr, top_global, fplog, ir->nstcalcenergy, state_global->x, + nfile, fnm, oenv, imdport, Flags); + + if (DOMAINDECOMP(cr)) + { + /* Distribute the charge groups over the nodes from the master node */ + dd_partition_system(fplog, ir->init_step, cr, TRUE, 1, + state_global, top_global, ir, + state, &f, mdatoms, top, fr, + vsite, shellfc, constr, + nrnb, wcycle, FALSE); + + } + + update_mdatoms(mdatoms, state->lambda[efptMASS]); + + if (opt2bSet("-cpi", nfile, fnm)) + { + bStateFromCP = gmx_fexist_master(opt2fn_master("-cpi", nfile, fnm, cr), cr); + } + else + { + bStateFromCP = FALSE; + } + + if (ir->bExpanded) + { + init_expanded_ensemble(bStateFromCP, ir, &state->dfhist); + } + + if (MASTER(cr)) + { + if (bStateFromCP) + { + /* Update mdebin with energy history if appending to output files */ + if (Flags & MD_APPENDFILES) + { + restore_energyhistory_from_state(mdebin, &state_global->enerhist); + } + else + { + /* We might have read an energy history from checkpoint, + * free the allocated memory and reset the counts. + */ + done_energyhistory(&state_global->enerhist); + init_energyhistory(&state_global->enerhist); + } + } + /* Set the initial energy history in state by updating once */ + update_energyhistory(&state_global->enerhist, mdebin); + } + + /* Initialize constraints */ + if (constr && !DOMAINDECOMP(cr)) + { + set_constraints(constr, top, ir, mdatoms, cr); + } + + if (repl_ex_nst > 0) + { + /* We need to be sure replica exchange can only occur + * when the energies are current */ + check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy, + "repl_ex_nst", &repl_ex_nst); + /* This check needs to happen before inter-simulation + * signals are initialized, too */ + } + if (repl_ex_nst > 0 && MASTER(cr)) + { + repl_ex = init_replica_exchange(fplog, cr->ms, state_global, ir, + repl_ex_nst, repl_ex_nex, repl_ex_seed); + } + + /* PME tuning is only supported with GPUs or PME nodes and not with rerun. + * PME tuning is not supported with PME only for LJ and not for Coulomb. + */ + if ((Flags & MD_TUNEPME) && + EEL_PME(fr->eeltype) && + ( (fr->cutoff_scheme == ecutsVERLET && fr->nbv->bUseGPU) || !(cr->duty & DUTY_PME)) && + !bRerunMD) + { + pme_loadbal_init(&pme_loadbal, ir, state->box, fr->ic, fr->pmedata); + cycles_pmes = 0; + if (cr->duty & DUTY_PME) + { + /* Start tuning right away, as we can't measure the load */ + bPMETuneRunning = TRUE; + } + else + { + /* Separate PME nodes, we can measure the PP/PME load balance */ + bPMETuneTry = TRUE; + } + } + + if (!ir->bContinuation && !bRerunMD) + { + if (mdatoms->cFREEZE && (state->flags & (1<homenr; i++) + { + for (m = 0; m < DIM; m++) + { + if (ir->opts.nFreeze[mdatoms->cFREEZE[i]][m]) + { + state->v[i][m] = 0; + } + } + } + } + + if (constr) + { + /* Constrain the initial coordinates and velocities */ + do_constrain_first(fplog, constr, ir, mdatoms, state, + cr, nrnb, fr, top); + } + if (vsite) + { + /* Construct the virtual sites for the initial configuration */ + construct_vsites(vsite, state->x, ir->delta_t, NULL, + top->idef.iparams, top->idef.il, + fr->ePBC, fr->bMolPBC, cr, state->box); + } + } + + debug_gmx(); + + /* set free energy calculation frequency as the minimum + greatest common denominator of nstdhdl, nstexpanded, and repl_ex_nst*/ + nstfep = ir->fepvals->nstdhdl; + if (ir->bExpanded) + { + nstfep = gmx_greatest_common_divisor(ir->fepvals->nstdhdl, nstfep); + } + if (repl_ex_nst > 0) + { + nstfep = gmx_greatest_common_divisor(repl_ex_nst, nstfep); + } + + /* I'm assuming we need global communication the first time! MRS */ + cglo_flags = (CGLO_TEMPERATURE | CGLO_GSTAT + | ((ir->comm_mode != ecmNO) ? CGLO_STOPCM : 0) + | (bVV ? CGLO_PRESSURE : 0) + | (bVV ? CGLO_CONSTRAINT : 0) + | (bRerunMD ? CGLO_RERUNMD : 0) + | ((Flags & MD_READ_EKIN) ? CGLO_READEKIN : 0)); + + bSumEkinhOld = FALSE; + compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm, + NULL, enerd, force_vir, shake_vir, total_vir, pres, mu_tot, + constr, NULL, FALSE, state->box, + top_global, &bSumEkinhOld, cglo_flags); + if (ir->eI == eiVVAK) + { + /* a second call to get the half step temperature initialized as well */ + /* we do the same call as above, but turn the pressure off -- internally to + compute_globals, this is recognized as a velocity verlet half-step + kinetic energy calculation. This minimized excess variables, but + perhaps loses some logic?*/ + + compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm, + NULL, enerd, force_vir, shake_vir, total_vir, pres, mu_tot, + constr, NULL, FALSE, state->box, + top_global, &bSumEkinhOld, + cglo_flags &~(CGLO_STOPCM | CGLO_PRESSURE)); + } + + /* Calculate the initial half step temperature, and save the ekinh_old */ + if (!(Flags & MD_STARTFROMCPT)) + { + for (i = 0; (i < ir->opts.ngtc); i++) + { + copy_mat(ekind->tcstat[i].ekinh, ekind->tcstat[i].ekinh_old); + } + } + if (ir->eI != eiVV) + { + enerd->term[F_TEMP] *= 2; /* result of averages being done over previous and current step, + and there is no previous step */ + } + + /* if using an iterative algorithm, we need to create a working directory for the state. */ + if (bIterativeCase) + { + bufstate = init_bufstate(state); + } + + /* need to make an initiation call to get the Trotter variables set, as well as other constants for non-trotter + temperature control */ + trotter_seq = init_npt_vars(ir, state, &MassQ, bTrotter); + + if (MASTER(cr)) + { + if (constr && !ir->bContinuation && ir->eConstrAlg == econtLINCS) + { + fprintf(fplog, + "RMS relative constraint deviation after constraining: %.2e\n", + constr_rmsd(constr, FALSE)); + } + if (EI_STATE_VELOCITY(ir->eI)) + { + fprintf(fplog, "Initial temperature: %g K\n", enerd->term[F_TEMP]); + } + if (bRerunMD) + { + fprintf(stderr, "starting md rerun '%s', reading coordinates from" + " input trajectory '%s'\n\n", + *(top_global->name), opt2fn("-rerun", nfile, fnm)); + if (bVerbose) + { + fprintf(stderr, "Calculated time to finish depends on nsteps from " + "run input file,\nwhich may not correspond to the time " + "needed to process input trajectory.\n\n"); + } + } + else + { + char tbuf[20]; + fprintf(stderr, "starting mdrun '%s'\n", + *(top_global->name)); + if (ir->nsteps >= 0) + { + sprintf(tbuf, "%8.1f", (ir->init_step+ir->nsteps)*ir->delta_t); + } + else + { + sprintf(tbuf, "%s", "infinite"); + } + if (ir->init_step > 0) + { + fprintf(stderr, "%s steps, %s ps (continuing from step %s, %8.1f ps).\n", + gmx_step_str(ir->init_step+ir->nsteps, sbuf), tbuf, + gmx_step_str(ir->init_step, sbuf2), + ir->init_step*ir->delta_t); + } + else + { + fprintf(stderr, "%s steps, %s ps.\n", + gmx_step_str(ir->nsteps, sbuf), tbuf); + } + } + fprintf(fplog, "\n"); + } + + walltime_accounting_start(walltime_accounting); + wallcycle_start(wcycle, ewcRUN); + print_start(fplog, cr, walltime_accounting, "mdrun"); + + /* safest point to do file checkpointing is here. More general point would be immediately before integrator call */ +#ifdef GMX_FAHCORE + chkpt_ret = fcCheckPointParallel( cr->nodeid, + NULL, 0); + if (chkpt_ret == 0) + { + gmx_fatal( 3, __FILE__, __LINE__, "Checkpoint error on step %d\n", 0 ); + } +#endif + + debug_gmx(); + /*********************************************************** + * + * Loop over MD steps + * + ************************************************************/ + + /* if rerunMD then read coordinates and velocities from input trajectory */ + if (bRerunMD) + { + if (getenv("GMX_FORCE_UPDATE")) + { + bForceUpdate = TRUE; + } + + rerun_fr.natoms = 0; + if (MASTER(cr)) + { + bNotLastFrame = read_first_frame(oenv, &status, + opt2fn("-rerun", nfile, fnm), + &rerun_fr, TRX_NEED_X | TRX_READ_V); + if (rerun_fr.natoms != top_global->natoms) + { + gmx_fatal(FARGS, + "Number of atoms in trajectory (%d) does not match the " + "run input file (%d)\n", + rerun_fr.natoms, top_global->natoms); + } + if (ir->ePBC != epbcNONE) + { + if (!rerun_fr.bBox) + { + gmx_fatal(FARGS, "Rerun trajectory frame step %d time %f does not contain a box, while pbc is used", rerun_fr.step, rerun_fr.time); + } + if (max_cutoff2(ir->ePBC, rerun_fr.box) < sqr(fr->rlistlong)) + { + gmx_fatal(FARGS, "Rerun trajectory frame step %d time %f has too small box dimensions", rerun_fr.step, rerun_fr.time); + } + } + } + + if (PAR(cr)) + { + rerun_parallel_comm(cr, &rerun_fr, &bNotLastFrame); + } + + if (ir->ePBC != epbcNONE) + { + /* Set the shift vectors. + * Necessary here when have a static box different from the tpr box. + */ + calc_shifts(rerun_fr.box, fr->shift_vec); + } + } + + /* loop over MD steps or if rerunMD to end of input trajectory */ + bFirstStep = TRUE; + /* Skip the first Nose-Hoover integration when we get the state from tpx */ + bStateFromTPX = !bStateFromCP; + bInitStep = bFirstStep && (bStateFromTPX || bVV); + bStartingFromCpt = (Flags & MD_STARTFROMCPT) && bInitStep; + bLastStep = FALSE; + bSumEkinhOld = FALSE; + bExchanged = FALSE; + bNeedRepartition = FALSE; + + init_global_signals(&gs, cr, ir, repl_ex_nst); + + step = ir->init_step; + step_rel = 0; + + if (ir->nstlist == -1) + { + init_nlistheuristics(&nlh, bGStatEveryStep, step); + } + + if (MULTISIM(cr) && (repl_ex_nst <= 0 )) + { + /* check how many steps are left in other sims */ + multisim_nsteps = get_multisim_nsteps(cr, ir->nsteps); + } + + + /* and stop now if we should */ + bLastStep = (bRerunMD || (ir->nsteps >= 0 && step_rel > ir->nsteps) || + ((multisim_nsteps >= 0) && (step_rel >= multisim_nsteps ))); + while (!bLastStep || (bRerunMD && bNotLastFrame)) + { + + wallcycle_start(wcycle, ewcSTEP); + + if (bRerunMD) + { + if (rerun_fr.bStep) + { + step = rerun_fr.step; + step_rel = step - ir->init_step; + } + if (rerun_fr.bTime) + { + t = rerun_fr.time; + } + else + { + t = step; + } + } + else + { + bLastStep = (step_rel == ir->nsteps); + t = t0 + step*ir->delta_t; + } + + if (ir->efep != efepNO || ir->bSimTemp) + { + /* find and set the current lambdas. If rerunning, we either read in a state, or a lambda value, + requiring different logic. */ + + set_current_lambdas(step, ir->fepvals, bRerunMD, &rerun_fr, state_global, state, lam0); + bDoDHDL = do_per_step(step, ir->fepvals->nstdhdl); + bDoFEP = (do_per_step(step, nstfep) && (ir->efep != efepNO)); + bDoExpanded = (do_per_step(step, ir->expandedvals->nstexpanded) + && (ir->bExpanded) && (step > 0) && (!bStartingFromCpt)); + } + + if (bSimAnn) + { + update_annealing_target_temp(&(ir->opts), t); + } + + if (bRerunMD) + { + if (!DOMAINDECOMP(cr) || MASTER(cr)) + { + for (i = 0; i < state_global->natoms; i++) + { + copy_rvec(rerun_fr.x[i], state_global->x[i]); + } + if (rerun_fr.bV) + { + for (i = 0; i < state_global->natoms; i++) + { + copy_rvec(rerun_fr.v[i], state_global->v[i]); + } + } + else + { + for (i = 0; i < state_global->natoms; i++) + { + clear_rvec(state_global->v[i]); + } + if (bRerunWarnNoV) + { + fprintf(stderr, "\nWARNING: Some frames do not contain velocities.\n" + " Ekin, temperature and pressure are incorrect,\n" + " the virial will be incorrect when constraints are present.\n" + "\n"); + bRerunWarnNoV = FALSE; + } + } + } + copy_mat(rerun_fr.box, state_global->box); + copy_mat(state_global->box, state->box); + + if (vsite && (Flags & MD_RERUN_VSITE)) + { + if (DOMAINDECOMP(cr)) + { + gmx_fatal(FARGS, "Vsite recalculation with -rerun is not implemented with domain decomposition, use a single rank"); + } + if (graph) + { + /* Following is necessary because the graph may get out of sync + * with the coordinates if we only have every N'th coordinate set + */ + mk_mshift(fplog, graph, fr->ePBC, state->box, state->x); + shift_self(graph, state->box, state->x); + } + construct_vsites(vsite, state->x, ir->delta_t, state->v, + top->idef.iparams, top->idef.il, + fr->ePBC, fr->bMolPBC, cr, state->box); + if (graph) + { + unshift_self(graph, state->box, state->x); + } + } + } + + /* Stop Center of Mass motion */ + bStopCM = (ir->comm_mode != ecmNO && do_per_step(step, ir->nstcomm)); + + if (bRerunMD) + { + /* for rerun MD always do Neighbour Searching */ + bNS = (bFirstStep || ir->nstlist != 0); + bNStList = bNS; + } + else + { + /* Determine whether or not to do Neighbour Searching and LR */ + bNStList = (ir->nstlist > 0 && step % ir->nstlist == 0); + + bNS = (bFirstStep || bExchanged || bNeedRepartition || bNStList || bDoFEP || + (ir->nstlist == -1 && nlh.nabnsb > 0)); + + if (bNS && ir->nstlist == -1) + { + set_nlistheuristics(&nlh, bFirstStep || bExchanged || bNeedRepartition || bDoFEP, step); + } + } + + /* check whether we should stop because another simulation has + stopped. */ + if (MULTISIM(cr)) + { + if ( (multisim_nsteps >= 0) && (step_rel >= multisim_nsteps) && + (multisim_nsteps != ir->nsteps) ) + { + if (bNS) + { + if (MASTER(cr)) + { + fprintf(stderr, + "Stopping simulation %d because another one has finished\n", + cr->ms->sim); + } + bLastStep = TRUE; + gs.sig[eglsCHKPT] = 1; + } + } + } + + /* < 0 means stop at next step, > 0 means stop at next NS step */ + if ( (gs.set[eglsSTOPCOND] < 0) || + ( (gs.set[eglsSTOPCOND] > 0) && (bNStList || ir->nstlist == 0) ) ) + { + bLastStep = TRUE; + } + + /* Determine whether or not to update the Born radii if doing GB */ + bBornRadii = bFirstStep; + if (ir->implicit_solvent && (step % ir->nstgbradii == 0)) + { + bBornRadii = TRUE; + } + + do_log = do_per_step(step, ir->nstlog) || bFirstStep || bLastStep; + do_verbose = bVerbose && + (step % stepout == 0 || bFirstStep || bLastStep); + + if (bNS && !(bFirstStep && ir->bContinuation && !bRerunMD)) + { + if (bRerunMD) + { + bMasterState = TRUE; + } + else + { + bMasterState = FALSE; + /* Correct the new box if it is too skewed */ + if (DYNAMIC_BOX(*ir)) + { + if (correct_box(fplog, step, state->box, graph)) + { + bMasterState = TRUE; + } + } + if (DOMAINDECOMP(cr) && bMasterState) + { + dd_collect_state(cr->dd, state, state_global); + } + } + + if (DOMAINDECOMP(cr)) + { + /* Repartition the domain decomposition */ + wallcycle_start(wcycle, ewcDOMDEC); + dd_partition_system(fplog, step, cr, + bMasterState, nstglobalcomm, + state_global, top_global, ir, + state, &f, mdatoms, top, fr, + vsite, shellfc, constr, + nrnb, wcycle, + do_verbose && !bPMETuneRunning); + wallcycle_stop(wcycle, ewcDOMDEC); + /* If using an iterative integrator, reallocate space to match the decomposition */ + } + } + + if (MASTER(cr) && do_log) + { + print_ebin_header(fplog, step, t, state->lambda[efptFEP]); /* can we improve the information printed here? */ + } + + if (ir->efep != efepNO) + { + update_mdatoms(mdatoms, state->lambda[efptMASS]); + } + + if ((bRerunMD && rerun_fr.bV) || bExchanged) + { + + /* We need the kinetic energy at minus the half step for determining + * the full step kinetic energy and possibly for T-coupling.*/ + /* This may not be quite working correctly yet . . . . */ + compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm, + wcycle, enerd, NULL, NULL, NULL, NULL, mu_tot, + constr, NULL, FALSE, state->box, + top_global, &bSumEkinhOld, + CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE); + } + clear_mat(force_vir); + + /* We write a checkpoint at this MD step when: + * either at an NS step when we signalled through gs, + * or at the last step (but not when we do not want confout), + * but never at the first step or with rerun. + */ + bCPT = (((gs.set[eglsCHKPT] && (bNS || ir->nstlist == 0)) || + (bLastStep && (Flags & MD_CONFOUT))) && + step > ir->init_step && !bRerunMD); + if (bCPT) + { + gs.set[eglsCHKPT] = 0; + } + + /* Determine the energy and pressure: + * at nstcalcenergy steps and at energy output steps (set below). + */ + if (EI_VV(ir->eI) && (!bInitStep)) + { + /* for vv, the first half of the integration actually corresponds + to the previous step. bCalcEner is only required to be evaluated on the 'next' step, + but the virial needs to be calculated on both the current step and the 'next' step. Future + reorganization may be able to get rid of one of the bCalcVir=TRUE steps. */ + + bCalcEner = do_per_step(step-1, ir->nstcalcenergy); + bCalcVir = bCalcEner || + (ir->epc != epcNO && (do_per_step(step, ir->nstpcouple) || do_per_step(step-1, ir->nstpcouple))); + } + else + { + bCalcEner = do_per_step(step, ir->nstcalcenergy); + bCalcVir = bCalcEner || + (ir->epc != epcNO && do_per_step(step, ir->nstpcouple)); + } + + /* Do we need global communication ? */ + bGStat = (bCalcVir || bCalcEner || bStopCM || + do_per_step(step, nstglobalcomm) || (bVV && IR_NVT_TROTTER(ir) && do_per_step(step-1, nstglobalcomm)) || + (ir->nstlist == -1 && !bRerunMD && step >= nlh.step_nscheck)); + + do_ene = (do_per_step(step, ir->nstenergy) || bLastStep); + + if (do_ene || do_log) + { + bCalcVir = TRUE; + bCalcEner = TRUE; + bGStat = TRUE; + } + + /* these CGLO_ options remain the same throughout the iteration */ + cglo_flags = ((bRerunMD ? CGLO_RERUNMD : 0) | + (bGStat ? CGLO_GSTAT : 0) + ); + + force_flags = (GMX_FORCE_STATECHANGED | + ((DYNAMIC_BOX(*ir) || bRerunMD) ? GMX_FORCE_DYNAMICBOX : 0) | + GMX_FORCE_ALLFORCES | + GMX_FORCE_SEPLRF | + (bCalcVir ? GMX_FORCE_VIRIAL : 0) | + (bCalcEner ? GMX_FORCE_ENERGY : 0) | + (bDoFEP ? GMX_FORCE_DHDL : 0) + ); + + if (fr->bTwinRange) + { + if (do_per_step(step, ir->nstcalclr)) + { + force_flags |= GMX_FORCE_DO_LR; + } + } + + if (shellfc) + { + /* Now is the time to relax the shells */ + count = relax_shell_flexcon(fplog, cr, bVerbose, step, + ir, bNS, force_flags, + top, + constr, enerd, fcd, + state, f, force_vir, mdatoms, + nrnb, wcycle, graph, groups, + shellfc, fr, bBornRadii, t, mu_tot, + &bConverged, vsite, + mdoutf_get_fp_field(outf)); + tcount += count; + + if (bConverged) + { + nconverged++; + } + } + else + { + /* The coordinates (x) are shifted (to get whole molecules) + * in do_force. + * This is parallellized as well, and does communication too. + * Check comments in sim_util.c + */ + do_force(fplog, cr, ir, step, nrnb, wcycle, top, groups, + state->box, state->x, &state->hist, + f, force_vir, mdatoms, enerd, fcd, + state->lambda, graph, + fr, vsite, mu_tot, t, mdoutf_get_fp_field(outf), ed, bBornRadii, + (bNS ? GMX_FORCE_NS : 0) | force_flags); + } + + if (bVV && !bStartingFromCpt && !bRerunMD) + /* ############### START FIRST UPDATE HALF-STEP FOR VV METHODS############### */ + { + if (ir->eI == eiVV && bInitStep) + { + /* if using velocity verlet with full time step Ekin, + * take the first half step only to compute the + * virial for the first step. From there, + * revert back to the initial coordinates + * so that the input is actually the initial step. + */ + copy_rvecn(state->v, cbuf, 0, state->natoms); /* should make this better for parallelizing? */ + } + else + { + /* this is for NHC in the Ekin(t+dt/2) version of vv */ + trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ1); + } + + /* If we are using twin-range interactions where the long-range component + * is only evaluated every nstcalclr>1 steps, we should do a special update + * step to combine the long-range forces on these steps. + * For nstcalclr=1 this is not done, since the forces would have been added + * directly to the short-range forces already. ++ * ++ * TODO Remove various aspects of VV+twin-range in master ++ * branch, because VV integrators did not ever support ++ * twin-range multiple time stepping with constraints. + */ + bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr)); + + update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, + f, bUpdateDoLR, fr->f_twin, fcd, + ekind, M, upd, bInitStep, etrtVELOCITY1, + cr, nrnb, constr, &top->idef); + + if (bIterativeCase && do_per_step(step-1, ir->nstpcouple) && !bInitStep) + { + gmx_iterate_init(&iterate, TRUE); + } + /* for iterations, we save these vectors, as we will be self-consistently iterating + the calculations */ + + /*#### UPDATE EXTENDED VARIABLES IN TROTTER FORMULATION */ + + /* save the state */ + if (iterate.bIterationActive) + { + copy_coupling_state(state, bufstate, ekind, ekind_save, &(ir->opts)); + } + + bFirstIterate = TRUE; + while (bFirstIterate || iterate.bIterationActive) + { + if (iterate.bIterationActive) + { + copy_coupling_state(bufstate, state, ekind_save, ekind, &(ir->opts)); + if (bFirstIterate && bTrotter) + { + /* The first time through, we need a decent first estimate + of veta(t+dt) to compute the constraints. Do + this by computing the box volume part of the + trotter integration at this time. Nothing else + should be changed by this routine here. If + !(first time), we start with the previous value + of veta. */ + + veta_save = state->veta; + trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ0); + vetanew = state->veta; + state->veta = veta_save; + } + } + + bOK = TRUE; + if (!bRerunMD || rerun_fr.bV || bForceUpdate) /* Why is rerun_fr.bV here? Unclear. */ + { + update_constraints(fplog, step, NULL, ir, ekind, mdatoms, + state, fr->bMolPBC, graph, f, + &top->idef, shake_vir, + cr, nrnb, wcycle, upd, constr, + TRUE, bCalcVir, vetanew); + + if (!bOK) + { + gmx_fatal(FARGS, "Constraint error: Shake, Lincs or Settle could not solve the constrains"); + } + + } + else if (graph) + { + /* Need to unshift here if a do_force has been + called in the previous step */ + unshift_self(graph, state->box, state->x); + } + + /* if VV, compute the pressure and constraints */ + /* For VV2, we strictly only need this if using pressure + * control, but we really would like to have accurate pressures + * printed out. + * Think about ways around this in the future? + * For now, keep this choice in comments. + */ + /*bPres = (ir->eI==eiVV || IR_NPT_TROTTER(ir)); */ + /*bTemp = ((ir->eI==eiVV &&(!bInitStep)) || (ir->eI==eiVVAK && IR_NPT_TROTTER(ir)));*/ + bPres = TRUE; + bTemp = ((ir->eI == eiVV && (!bInitStep)) || (ir->eI == eiVVAK)); + if (bCalcEner && ir->eI == eiVVAK) /*MRS: 7/9/2010 -- this still doesn't fix it?*/ + { + bSumEkinhOld = TRUE; + } + /* for vv, the first half of the integration actually corresponds to the previous step. + So we need information from the last step in the first half of the integration */ + if (bGStat || do_per_step(step-1, nstglobalcomm)) + { + compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm, + wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot, + constr, NULL, FALSE, state->box, + top_global, &bSumEkinhOld, + cglo_flags + | CGLO_ENERGY + | (bTemp ? CGLO_TEMPERATURE : 0) + | (bPres ? CGLO_PRESSURE : 0) + | (bPres ? CGLO_CONSTRAINT : 0) + | ((iterate.bIterationActive) ? CGLO_ITERATE : 0) + | (bFirstIterate ? CGLO_FIRSTITERATE : 0) + | CGLO_SCALEEKIN + ); + /* explanation of above: + a) We compute Ekin at the full time step + if 1) we are using the AveVel Ekin, and it's not the + initial step, or 2) if we are using AveEkin, but need the full + time step kinetic energy for the pressure (always true now, since we want accurate statistics). + b) If we are using EkinAveEkin for the kinetic energy for the temperature control, we still feed in + EkinAveVel because it's needed for the pressure */ + } + /* temperature scaling and pressure scaling to produce the extended variables at t+dt */ + if (!bInitStep) + { + if (bTrotter) + { + m_add(force_vir, shake_vir, total_vir); /* we need the un-dispersion corrected total vir here */ + trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ2); + } + else + { + if (bExchanged) + { + + /* We need the kinetic energy at minus the half step for determining + * the full step kinetic energy and possibly for T-coupling.*/ + /* This may not be quite working correctly yet . . . . */ + compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm, + wcycle, enerd, NULL, NULL, NULL, NULL, mu_tot, + constr, NULL, FALSE, state->box, + top_global, &bSumEkinhOld, + CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE); + } + } + } + + if (iterate.bIterationActive && + done_iterating(cr, fplog, step, &iterate, bFirstIterate, + state->veta, &vetanew)) + { + break; + } + bFirstIterate = FALSE; + } + + if (bTrotter && !bInitStep) + { + copy_mat(shake_vir, state->svir_prev); + copy_mat(force_vir, state->fvir_prev); + if (IR_NVT_TROTTER(ir) && ir->eI == eiVV) + { + /* update temperature and kinetic energy now that step is over - this is the v(t+dt) point */ + enerd->term[F_TEMP] = sum_ekin(&(ir->opts), ekind, NULL, (ir->eI == eiVV), FALSE); + enerd->term[F_EKIN] = trace(ekind->ekin); + } + } + /* if it's the initial step, we performed this first step just to get the constraint virial */ + if (bInitStep && ir->eI == eiVV) + { + copy_rvecn(cbuf, state->v, 0, state->natoms); + } + } + + /* MRS -- now done iterating -- compute the conserved quantity */ + if (bVV) + { + saved_conserved_quantity = compute_conserved_from_auxiliary(ir, state, &MassQ); + if (ir->eI == eiVV) + { + last_ekin = enerd->term[F_EKIN]; + } + if ((ir->eDispCorr != edispcEnerPres) && (ir->eDispCorr != edispcAllEnerPres)) + { + saved_conserved_quantity -= enerd->term[F_DISPCORR]; + } + /* sum up the foreign energy and dhdl terms for vv. currently done every step so that dhdl is correct in the .edr */ + if (!bRerunMD) + { + sum_dhdl(enerd, state->lambda, ir->fepvals); + } + } + + /* ######## END FIRST UPDATE STEP ############## */ + /* ######## If doing VV, we now have v(dt) ###### */ + if (bDoExpanded) + { + /* perform extended ensemble sampling in lambda - we don't + actually move to the new state before outputting + statistics, but if performing simulated tempering, we + do update the velocities and the tau_t. */ + + lamnew = ExpandedEnsembleDynamics(fplog, ir, enerd, state, &MassQ, state->fep_state, &state->dfhist, step, state->v, mdatoms); + /* history is maintained in state->dfhist, but state_global is what is sent to trajectory and log output */ + copy_df_history(&state_global->dfhist, &state->dfhist); + } + + /* Now we have the energies and forces corresponding to the + * coordinates at time t. We must output all of this before + * the update. + */ + do_md_trajectory_writing(fplog, cr, nfile, fnm, step, step_rel, t, + ir, state, state_global, top_global, fr, + outf, mdebin, ekind, f, f_global, + wcycle, &nchkpt, + bCPT, bRerunMD, bLastStep, (Flags & MD_CONFOUT), + bSumEkinhOld); + /* Check if IMD step and do IMD communication, if bIMD is TRUE. */ + bIMDstep = do_IMD(ir->bIMD, step, cr, bNS, state->box, state->x, ir, t, wcycle); + + /* kludge -- virial is lost with restart for NPT control. Must restart */ + if (bStartingFromCpt && bVV) + { + copy_mat(state->svir_prev, shake_vir); + copy_mat(state->fvir_prev, force_vir); + } + + elapsed_time = walltime_accounting_get_current_elapsed_time(walltime_accounting); + + /* Check whether everything is still allright */ + if (((int)gmx_get_stop_condition() > handled_stop_condition) +#ifdef GMX_THREAD_MPI + && MASTER(cr) +#endif + ) + { + /* this is just make gs.sig compatible with the hack + of sending signals around by MPI_Reduce with together with + other floats */ + if (gmx_get_stop_condition() == gmx_stop_cond_next_ns) + { + gs.sig[eglsSTOPCOND] = 1; + } + if (gmx_get_stop_condition() == gmx_stop_cond_next) + { + gs.sig[eglsSTOPCOND] = -1; + } + /* < 0 means stop at next step, > 0 means stop at next NS step */ + if (fplog) + { + fprintf(fplog, + "\n\nReceived the %s signal, stopping at the next %sstep\n\n", + gmx_get_signal_name(), + gs.sig[eglsSTOPCOND] == 1 ? "NS " : ""); + fflush(fplog); + } + fprintf(stderr, + "\n\nReceived the %s signal, stopping at the next %sstep\n\n", + gmx_get_signal_name(), + gs.sig[eglsSTOPCOND] == 1 ? "NS " : ""); + fflush(stderr); + handled_stop_condition = (int)gmx_get_stop_condition(); + } + else if (MASTER(cr) && (bNS || ir->nstlist <= 0) && + (max_hours > 0 && elapsed_time > max_hours*60.0*60.0*0.99) && + gs.sig[eglsSTOPCOND] == 0 && gs.set[eglsSTOPCOND] == 0) + { + /* Signal to terminate the run */ + gs.sig[eglsSTOPCOND] = 1; + if (fplog) + { + fprintf(fplog, "\nStep %s: Run time exceeded %.3f hours, will terminate the run\n", gmx_step_str(step, sbuf), max_hours*0.99); + } + fprintf(stderr, "\nStep %s: Run time exceeded %.3f hours, will terminate the run\n", gmx_step_str(step, sbuf), max_hours*0.99); + } + + if (bResetCountersHalfMaxH && MASTER(cr) && + elapsed_time > max_hours*60.0*60.0*0.495) + { + gs.sig[eglsRESETCOUNTERS] = 1; + } + + if (ir->nstlist == -1 && !bRerunMD) + { + /* When bGStatEveryStep=FALSE, global_stat is only called + * when we check the atom displacements, not at NS steps. + * This means that also the bonded interaction count check is not + * performed immediately after NS. Therefore a few MD steps could + * be performed with missing interactions. + * But wrong energies are never written to file, + * since energies are only written after global_stat + * has been called. + */ + if (step >= nlh.step_nscheck) + { + nlh.nabnsb = natoms_beyond_ns_buffer(ir, fr, &top->cgs, + nlh.scale_tot, state->x); + } + else + { + /* This is not necessarily true, + * but step_nscheck is determined quite conservatively. + */ + nlh.nabnsb = 0; + } + } + + /* In parallel we only have to check for checkpointing in steps + * where we do global communication, + * otherwise the other nodes don't know. + */ + if (MASTER(cr) && ((bGStat || !PAR(cr)) && + cpt_period >= 0 && + (cpt_period == 0 || + elapsed_time >= nchkpt*cpt_period*60.0)) && + gs.set[eglsCHKPT] == 0) + { + gs.sig[eglsCHKPT] = 1; + } + + /* at the start of step, randomize or scale the velocities (trotter done elsewhere) */ + if (EI_VV(ir->eI)) + { + if (!bInitStep) + { + update_tcouple(step, ir, state, ekind, &MassQ, mdatoms); + } + if (ETC_ANDERSEN(ir->etc)) /* keep this outside of update_tcouple because of the extra info required to pass */ + { + gmx_bool bIfRandomize; + bIfRandomize = update_randomize_velocities(ir, step, cr, mdatoms, state, upd, constr); + /* if we have constraints, we have to remove the kinetic energy parallel to the bonds */ + if (constr && bIfRandomize) + { + update_constraints(fplog, step, NULL, ir, ekind, mdatoms, + state, fr->bMolPBC, graph, f, + &top->idef, tmp_vir, + cr, nrnb, wcycle, upd, constr, + TRUE, bCalcVir, vetanew); + } + } + } + + if (bIterativeCase && do_per_step(step, ir->nstpcouple)) + { + gmx_iterate_init(&iterate, TRUE); + /* for iterations, we save these vectors, as we will be redoing the calculations */ + copy_coupling_state(state, bufstate, ekind, ekind_save, &(ir->opts)); + } + + bFirstIterate = TRUE; + while (bFirstIterate || iterate.bIterationActive) + { + /* We now restore these vectors to redo the calculation with improved extended variables */ + if (iterate.bIterationActive) + { + copy_coupling_state(bufstate, state, ekind_save, ekind, &(ir->opts)); + } + + /* We make the decision to break or not -after- the calculation of Ekin and Pressure, + so scroll down for that logic */ + + /* ######### START SECOND UPDATE STEP ################# */ + /* Box is changed in update() when we do pressure coupling, + * but we should still use the old box for energy corrections and when + * writing it to the energy file, so it matches the trajectory files for + * the same timestep above. Make a copy in a separate array. + */ + copy_mat(state->box, lastbox); + + bOK = TRUE; + dvdl_constr = 0; + + if (!(bRerunMD && !rerun_fr.bV && !bForceUpdate)) + { + wallcycle_start(wcycle, ewcUPDATE); + /* UPDATE PRESSURE VARIABLES IN TROTTER FORMULATION WITH CONSTRAINTS */ + if (bTrotter) + { + if (iterate.bIterationActive) + { + if (bFirstIterate) + { + scalevir = 1; + } + else + { + /* we use a new value of scalevir to converge the iterations faster */ + scalevir = tracevir/trace(shake_vir); + } + msmul(shake_vir, scalevir, shake_vir); + m_add(force_vir, shake_vir, total_vir); + clear_mat(shake_vir); + } + trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ3); + /* We can only do Berendsen coupling after we have summed + * the kinetic energy or virial. Since the happens + * in global_state after update, we should only do it at + * step % nstlist = 1 with bGStatEveryStep=FALSE. + */ + } + else + { + update_tcouple(step, ir, state, ekind, &MassQ, mdatoms); + update_pcouple(fplog, step, ir, state, pcoupl_mu, M, bInitStep); + } + + if (bVV) + { + bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr)); + + /* velocity half-step update */ + update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f, + bUpdateDoLR, fr->f_twin, fcd, + ekind, M, upd, FALSE, etrtVELOCITY2, + cr, nrnb, constr, &top->idef); + } + + /* Above, initialize just copies ekinh into ekin, + * it doesn't copy position (for VV), + * and entire integrator for MD. + */ + + if (ir->eI == eiVVAK) + { + copy_rvecn(state->x, cbuf, 0, state->natoms); + } + bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr)); + + update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f, + bUpdateDoLR, fr->f_twin, fcd, + ekind, M, upd, bInitStep, etrtPOSITION, cr, nrnb, constr, &top->idef); + wallcycle_stop(wcycle, ewcUPDATE); + + update_constraints(fplog, step, &dvdl_constr, ir, ekind, mdatoms, state, + fr->bMolPBC, graph, f, + &top->idef, shake_vir, + cr, nrnb, wcycle, upd, constr, + FALSE, bCalcVir, state->veta); + + if (ir->eI == eiVVAK) + { + /* erase F_EKIN and F_TEMP here? */ + /* just compute the kinetic energy at the half step to perform a trotter step */ + compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm, + wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot, + constr, NULL, FALSE, lastbox, + top_global, &bSumEkinhOld, + cglo_flags | CGLO_TEMPERATURE + ); + wallcycle_start(wcycle, ewcUPDATE); + trotter_update(ir, step, ekind, enerd, state, total_vir, mdatoms, &MassQ, trotter_seq, ettTSEQ4); + /* now we know the scaling, we can compute the positions again again */ + copy_rvecn(cbuf, state->x, 0, state->natoms); + + bUpdateDoLR = (fr->bTwinRange && do_per_step(step, ir->nstcalclr)); + + update_coords(fplog, step, ir, mdatoms, state, fr->bMolPBC, f, + bUpdateDoLR, fr->f_twin, fcd, + ekind, M, upd, bInitStep, etrtPOSITION, cr, nrnb, constr, &top->idef); + wallcycle_stop(wcycle, ewcUPDATE); + + /* do we need an extra constraint here? just need to copy out of state->v to upd->xp? */ + /* are the small terms in the shake_vir here due + * to numerical errors, or are they important + * physically? I'm thinking they are just errors, but not completely sure. + * For now, will call without actually constraining, constr=NULL*/ + update_constraints(fplog, step, NULL, ir, ekind, mdatoms, + state, fr->bMolPBC, graph, f, + &top->idef, tmp_vir, + cr, nrnb, wcycle, upd, NULL, + FALSE, bCalcVir, + state->veta); + } + if (!bOK) + { + gmx_fatal(FARGS, "Constraint error: Shake, Lincs or Settle could not solve the constrains"); + } + + if (fr->bSepDVDL && fplog && do_log) + { + gmx_print_sepdvdl(fplog, "Constraint dV/dl", 0.0, dvdl_constr); + } + if (bVV) + { + /* this factor or 2 correction is necessary + because half of the constraint force is removed + in the vv step, so we have to double it. See + the Redmine issue #1255. It is not yet clear + if the factor of 2 is exact, or just a very + good approximation, and this will be + investigated. The next step is to see if this + can be done adding a dhdl contribution from the + rattle step, but this is somewhat more + complicated with the current code. Will be + investigated, hopefully for 4.6.3. However, + this current solution is much better than + having it completely wrong. + */ + enerd->term[F_DVDL_CONSTR] += 2*dvdl_constr; + } + else + { + enerd->term[F_DVDL_CONSTR] += dvdl_constr; + } + } + else if (graph) + { + /* Need to unshift here */ + unshift_self(graph, state->box, state->x); + } + + if (vsite != NULL) + { + wallcycle_start(wcycle, ewcVSITECONSTR); + if (graph != NULL) + { + shift_self(graph, state->box, state->x); + } + construct_vsites(vsite, state->x, ir->delta_t, state->v, + top->idef.iparams, top->idef.il, + fr->ePBC, fr->bMolPBC, cr, state->box); + + if (graph != NULL) + { + unshift_self(graph, state->box, state->x); + } + wallcycle_stop(wcycle, ewcVSITECONSTR); + } + + /* ############## IF NOT VV, Calculate globals HERE, also iterate constraints ############ */ + /* With Leap-Frog we can skip compute_globals at + * non-communication steps, but we need to calculate + * the kinetic energy one step before communication. + */ + if (bGStat || (!EI_VV(ir->eI) && do_per_step(step+1, nstglobalcomm))) + { + if (ir->nstlist == -1 && bFirstIterate) + { + gs.sig[eglsNABNSB] = nlh.nabnsb; + } + compute_globals(fplog, gstat, cr, ir, fr, ekind, state, state_global, mdatoms, nrnb, vcm, + wcycle, enerd, force_vir, shake_vir, total_vir, pres, mu_tot, + constr, + bFirstIterate ? &gs : NULL, + (step_rel % gs.nstms == 0) && + (multisim_nsteps < 0 || (step_rel < multisim_nsteps)), + lastbox, + top_global, &bSumEkinhOld, + cglo_flags + | (!EI_VV(ir->eI) || bRerunMD ? CGLO_ENERGY : 0) + | (!EI_VV(ir->eI) && bStopCM ? CGLO_STOPCM : 0) + | (!EI_VV(ir->eI) ? CGLO_TEMPERATURE : 0) + | (!EI_VV(ir->eI) || bRerunMD ? CGLO_PRESSURE : 0) + | (iterate.bIterationActive ? CGLO_ITERATE : 0) + | (bFirstIterate ? CGLO_FIRSTITERATE : 0) + | CGLO_CONSTRAINT + ); + if (ir->nstlist == -1 && bFirstIterate) + { + nlh.nabnsb = gs.set[eglsNABNSB]; + gs.set[eglsNABNSB] = 0; + } + } + /* bIterate is set to keep it from eliminating the old ekin kinetic energy terms */ + /* ############# END CALC EKIN AND PRESSURE ################# */ + + /* Note: this is OK, but there are some numerical precision issues with using the convergence of + the virial that should probably be addressed eventually. state->veta has better properies, + but what we actually need entering the new cycle is the new shake_vir value. Ideally, we could + generate the new shake_vir, but test the veta value for convergence. This will take some thought. */ + + if (iterate.bIterationActive && + done_iterating(cr, fplog, step, &iterate, bFirstIterate, + trace(shake_vir), &tracevir)) + { + break; + } + bFirstIterate = FALSE; + } + + if (!bVV || bRerunMD) + { + /* sum up the foreign energy and dhdl terms for md and sd. currently done every step so that dhdl is correct in the .edr */ + sum_dhdl(enerd, state->lambda, ir->fepvals); + } + update_box(fplog, step, ir, mdatoms, state, f, + ir->nstlist == -1 ? &nlh.scale_tot : NULL, pcoupl_mu, nrnb, upd); + + /* ################# END UPDATE STEP 2 ################# */ + /* #### We now have r(t+dt) and v(t+dt/2) ############# */ + + /* The coordinates (x) were unshifted in update */ + if (!bGStat) + { + /* We will not sum ekinh_old, + * so signal that we still have to do it. + */ + bSumEkinhOld = TRUE; + } + + /* ######### BEGIN PREPARING EDR OUTPUT ########### */ + + /* use the directly determined last velocity, not actually the averaged half steps */ + if (bTrotter && ir->eI == eiVV) + { + enerd->term[F_EKIN] = last_ekin; + } + enerd->term[F_ETOT] = enerd->term[F_EPOT] + enerd->term[F_EKIN]; + + if (bVV) + { + enerd->term[F_ECONSERVED] = enerd->term[F_ETOT] + saved_conserved_quantity; + } + else + { + enerd->term[F_ECONSERVED] = enerd->term[F_ETOT] + compute_conserved_from_auxiliary(ir, state, &MassQ); + } + /* ######### END PREPARING EDR OUTPUT ########### */ + + /* Output stuff */ + if (MASTER(cr)) + { + gmx_bool do_dr, do_or; + + if (fplog && do_log && bDoExpanded) + { + /* only needed if doing expanded ensemble */ + PrintFreeEnergyInfoToFile(fplog, ir->fepvals, ir->expandedvals, ir->bSimTemp ? ir->simtempvals : NULL, + &state_global->dfhist, state->fep_state, ir->nstlog, step); + } + if (!(bStartingFromCpt && (EI_VV(ir->eI)))) + { + if (bCalcEner) + { + upd_mdebin(mdebin, bDoDHDL, TRUE, + t, mdatoms->tmass, enerd, state, + ir->fepvals, ir->expandedvals, lastbox, + shake_vir, force_vir, total_vir, pres, + ekind, mu_tot, constr); + } + else + { + upd_mdebin_step(mdebin); + } + + do_dr = do_per_step(step, ir->nstdisreout); + do_or = do_per_step(step, ir->nstorireout); + + print_ebin(mdoutf_get_fp_ene(outf), do_ene, do_dr, do_or, do_log ? fplog : NULL, + step, t, + eprNORMAL, bCompact, mdebin, fcd, groups, &(ir->opts)); + } + if (ir->ePull != epullNO) + { + pull_print_output(ir->pull, step, t); + } + + if (do_per_step(step, ir->nstlog)) + { + if (fflush(fplog) != 0) + { + gmx_fatal(FARGS, "Cannot flush logfile - maybe you are out of disk space?"); + } + } + } + if (bDoExpanded) + { + /* Have to do this part _after_ outputting the logfile and the edr file */ + /* Gets written into the state at the beginning of next loop*/ + state->fep_state = lamnew; + } + /* Print the remaining wall clock time for the run */ + if (MULTIMASTER(cr) && (do_verbose || gmx_got_usr_signal()) && !bPMETuneRunning) + { + if (shellfc) + { + fprintf(stderr, "\n"); + } + print_time(stderr, walltime_accounting, step, ir, cr); + } + + /* Ion/water position swapping. + * Not done in last step since trajectory writing happens before this call + * in the MD loop and exchanges would be lost anyway. */ + bNeedRepartition = FALSE; + if ((ir->eSwapCoords != eswapNO) && (step > 0) && !bLastStep && + do_per_step(step, ir->swap->nstswap)) + { + bNeedRepartition = do_swapcoords(cr, step, t, ir, wcycle, + bRerunMD ? rerun_fr.x : state->x, + bRerunMD ? rerun_fr.box : state->box, + top_global, MASTER(cr) && bVerbose, bRerunMD); + + if (bNeedRepartition && DOMAINDECOMP(cr)) + { + dd_collect_state(cr->dd, state, state_global); + } + } + + /* Replica exchange */ + bExchanged = FALSE; + if ((repl_ex_nst > 0) && (step > 0) && !bLastStep && + do_per_step(step, repl_ex_nst)) + { + bExchanged = replica_exchange(fplog, cr, repl_ex, + state_global, enerd, + state, step, t); + } + + if ( (bExchanged || bNeedRepartition) && DOMAINDECOMP(cr) ) + { + dd_partition_system(fplog, step, cr, TRUE, 1, + state_global, top_global, ir, + state, &f, mdatoms, top, fr, + vsite, shellfc, constr, + nrnb, wcycle, FALSE); + } + + bFirstStep = FALSE; + bInitStep = FALSE; + bStartingFromCpt = FALSE; + + /* ####### SET VARIABLES FOR NEXT ITERATION IF THEY STILL NEED IT ###### */ + /* With all integrators, except VV, we need to retain the pressure + * at the current step for coupling at the next step. + */ + if ((state->flags & (1<nstpcouple > 0 && step % ir->nstpcouple == 0))) + { + /* Store the pressure in t_state for pressure coupling + * at the next MD step. + */ + copy_mat(pres, state->pres_prev); + } + + /* ####### END SET VARIABLES FOR NEXT ITERATION ###### */ + + if ( (membed != NULL) && (!bLastStep) ) + { + rescale_membed(step_rel, membed, state_global->x); + } + + if (bRerunMD) + { + if (MASTER(cr)) + { + /* read next frame from input trajectory */ + bNotLastFrame = read_next_frame(oenv, status, &rerun_fr); + } + + if (PAR(cr)) + { + rerun_parallel_comm(cr, &rerun_fr, &bNotLastFrame); + } + } + + if (!bRerunMD || !rerun_fr.bStep) + { + /* increase the MD step number */ + step++; + step_rel++; + } + + cycles = wallcycle_stop(wcycle, ewcSTEP); + if (DOMAINDECOMP(cr) && wcycle) + { + dd_cycles_add(cr->dd, cycles, ddCyclStep); + } + + if (bPMETuneRunning || bPMETuneTry) + { + /* PME grid + cut-off optimization with GPUs or PME nodes */ + + /* Count the total cycles over the last steps */ + cycles_pmes += cycles; + + /* We can only switch cut-off at NS steps */ + if (step % ir->nstlist == 0) + { + /* PME grid + cut-off optimization with GPUs or PME nodes */ + if (bPMETuneTry) + { + if (DDMASTER(cr->dd)) + { + /* PME node load is too high, start tuning */ + bPMETuneRunning = (dd_pme_f_ratio(cr->dd) >= 1.05); + } + dd_bcast(cr->dd, sizeof(gmx_bool), &bPMETuneRunning); + + if (bPMETuneRunning || step_rel > ir->nstlist*50) + { + bPMETuneTry = FALSE; + } + } + if (bPMETuneRunning) + { + /* init_step might not be a multiple of nstlist, + * but the first cycle is always skipped anyhow. + */ + bPMETuneRunning = + pme_load_balance(pme_loadbal, cr, + (bVerbose && MASTER(cr)) ? stderr : NULL, + fplog, + ir, state, cycles_pmes, + fr->ic, fr->nbv, &fr->pmedata, + step); + + /* Update constants in forcerec/inputrec to keep them in sync with fr->ic */ + fr->ewaldcoeff_q = fr->ic->ewaldcoeff_q; + fr->ewaldcoeff_lj = fr->ic->ewaldcoeff_lj; + fr->rlist = fr->ic->rlist; + fr->rlistlong = fr->ic->rlistlong; + fr->rcoulomb = fr->ic->rcoulomb; + fr->rvdw = fr->ic->rvdw; + + if (ir->eDispCorr != edispcNO) + { + calc_enervirdiff(NULL, ir->eDispCorr, fr); + } + } + cycles_pmes = 0; + } + } + + if (step_rel == wcycle_get_reset_counters(wcycle) || + gs.set[eglsRESETCOUNTERS] != 0) + { + /* Reset all the counters related to performance over the run */ + reset_all_counters(fplog, cr, step, &step_rel, ir, wcycle, nrnb, walltime_accounting, + fr->nbv != NULL && fr->nbv->bUseGPU ? fr->nbv->cu_nbv : NULL); + wcycle_set_reset_counters(wcycle, -1); + if (!(cr->duty & DUTY_PME)) + { + /* Tell our PME node to reset its counters */ + gmx_pme_send_resetcounters(cr, step); + } + /* Correct max_hours for the elapsed time */ + max_hours -= elapsed_time/(60.0*60.0); + bResetCountersHalfMaxH = FALSE; + gs.set[eglsRESETCOUNTERS] = 0; + } + + /* If bIMD is TRUE, the master updates the IMD energy record and sends positions to VMD client */ + IMD_prep_energies_send_positions(ir->bIMD && MASTER(cr), bIMDstep, ir->imd, enerd, step, bCalcEner, wcycle); + + } + /* End of main MD loop */ + debug_gmx(); + + /* Stop measuring walltime */ + walltime_accounting_end(walltime_accounting); + + if (bRerunMD && MASTER(cr)) + { + close_trj(status); + } + + if (!(cr->duty & DUTY_PME)) + { + /* Tell the PME only node to finish */ + gmx_pme_send_finish(cr); + } + + if (MASTER(cr)) + { + if (ir->nstcalcenergy > 0 && !bRerunMD) + { + print_ebin(mdoutf_get_fp_ene(outf), FALSE, FALSE, FALSE, fplog, step, t, + eprAVER, FALSE, mdebin, fcd, groups, &(ir->opts)); + } + } + + done_mdoutf(outf); + debug_gmx(); + + if (ir->nstlist == -1 && nlh.nns > 0 && fplog) + { + fprintf(fplog, "Average neighborlist lifetime: %.1f steps, std.dev.: %.1f steps\n", nlh.s1/nlh.nns, sqrt(nlh.s2/nlh.nns - sqr(nlh.s1/nlh.nns))); + fprintf(fplog, "Average number of atoms that crossed the half buffer length: %.1f\n\n", nlh.ab/nlh.nns); + } + + if (pme_loadbal != NULL) + { + pme_loadbal_done(pme_loadbal, cr, fplog, + fr->nbv != NULL && fr->nbv->bUseGPU); + } + + if (shellfc && fplog) + { + fprintf(fplog, "Fraction of iterations that converged: %.2f %%\n", + (nconverged*100.0)/step_rel); + fprintf(fplog, "Average number of force evaluations per MD step: %.2f\n\n", + tcount/step_rel); + } + + if (repl_ex_nst > 0 && MASTER(cr)) + { + print_replica_exchange_statistics(fplog, repl_ex); + } + + /* IMD cleanup, if bIMD is TRUE. */ + IMD_finalize(ir->bIMD, ir->imd); + + walltime_accounting_set_nsteps_done(walltime_accounting, step_rel); + + return 0; +}