--- /dev/null
- prev.C = prev.O = -1;
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Green Red Orange Magenta Azure Cyan Skyblue
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include "string2.h"
+#include "smalloc.h"
+#include "gstat.h"
+#include "gmx_fatal.h"
+#include "index.h"
+
+t_dlist *mk_dlist(FILE *log,
+ t_atoms *atoms, int *nlist,
+ gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, gmx_bool bHChi,
+ int maxchi, int r0, gmx_residuetype_t rt)
+{
+ int ires, i, j, k, ii;
+ t_dihatms atm, prev;
+ int nl = 0, nc[edMax];
+ char *thisres;
+ t_dlist *dl;
+
+ snew(dl, atoms->nres+1);
- atm.minC = atm.H = atm.N = atm.C = atm.O = atm.minO = -1;
++ prev.C = prev.Cn[1] = -1; /* Keep the compiler quiet */
+ for (i = 0; (i < edMax); i++)
+ {
+ nc[i] = 0;
+ }
+ ires = -1;
+ i = 0;
+ while (i < atoms->nr)
+ {
+ ires = atoms->atom[i].resind;
+
+ /* Initiate all atom numbers to -1 */
- (strcmp(*(atoms->atomname[i]), "H1") == 0) )
++ atm.minC = atm.H = atm.N = atm.C = atm.O = atm.minCalpha = -1;
+ for (j = 0; (j < MAXCHI+3); j++)
+ {
+ atm.Cn[j] = -1;
+ }
+
+ /* Look for atoms in this residue */
+ /* maybe should allow for chis to hydrogens? */
+ while ((i < atoms->nr) && (atoms->atom[i].resind == ires))
+ {
+ if ((strcmp(*(atoms->atomname[i]), "H") == 0) ||
- if (prev.O != -1)
++ (strcmp(*(atoms->atomname[i]), "H1") == 0) ||
++ (strcmp(*(atoms->atomname[i]), "HN") == 0) )
+ {
+ atm.H = i;
+ }
+ else if (strcmp(*(atoms->atomname[i]), "N") == 0)
+ {
+ atm.N = i;
+ }
+ else if (strcmp(*(atoms->atomname[i]), "C") == 0)
+ {
+ atm.C = i;
+ }
+ else if ((strcmp(*(atoms->atomname[i]), "O") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "O1") == 0))
+ {
+ atm.O = i;
+ }
+ else if (strcmp(*(atoms->atomname[i]), "CA") == 0)
+ {
+ atm.Cn[1] = i;
+ }
+ else if (strcmp(*(atoms->atomname[i]), "CB") == 0)
+ {
+ atm.Cn[2] = i;
+ }
+ else if ((strcmp(*(atoms->atomname[i]), "CG") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "CG1") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "OG") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "OG1") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "SG") == 0))
+ {
+ atm.Cn[3] = i;
+ }
+ else if ((strcmp(*(atoms->atomname[i]), "CD") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "CD1") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "SD") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "OD1") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "ND1") == 0))
+ {
+ atm.Cn[4] = i;
+ }
+ /* by grs - split the Cn[4] into 2 bits to check allowing dih to H */
+ else if (bHChi && ((strcmp(*(atoms->atomname[i]), "HG") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "HG1") == 0)) )
+ {
+ atm.Cn[4] = i;
+ }
+ else if ((strcmp(*(atoms->atomname[i]), "CE") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "CE1") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "OE1") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "NE") == 0))
+ {
+ atm.Cn[5] = i;
+ }
+ else if ((strcmp(*(atoms->atomname[i]), "CZ") == 0) ||
+ (strcmp(*(atoms->atomname[i]), "NZ") == 0))
+ {
+ atm.Cn[6] = i;
+ }
+ /* HChi flag here too */
+ else if (bHChi && (strcmp(*(atoms->atomname[i]), "NH1") == 0))
+ {
+ atm.Cn[7] = i;
+ }
+ i++;
+ }
+
+ thisres = *(atoms->resinfo[ires].name);
+
+ /* added by grs - special case for aromatics, whose chis above 2 are
+ not real and produce rubbish output - so set back to -1 */
+ if (strcmp(thisres, "PHE") == 0 ||
+ strcmp(thisres, "TYR") == 0 ||
+ strcmp(thisres, "PTR") == 0 ||
+ strcmp(thisres, "TRP") == 0 ||
+ strcmp(thisres, "HIS") == 0 ||
+ strcmp(thisres, "HISA") == 0 ||
+ strcmp(thisres, "HISB") == 0)
+ {
+ for (ii = 5; ii <= 7; ii++)
+ {
+ atm.Cn[ii] = -1;
+ }
+ }
+ /* end fixing aromatics */
+
+ /* Special case for Pro, has no H */
+ if (strcmp(thisres, "PRO") == 0)
+ {
+ atm.H = atm.Cn[4];
+ }
+ /* Carbon from previous residue */
+ if (prev.C != -1)
+ {
+ atm.minC = prev.C;
+ }
- atm.minO = prev.O;
++ /* Alpha-carbon from previous residue */
++ if (prev.Cn[1] != -1)
+ {
- if ((atm.minC != -1) && (atm.minO != -1))
++ atm.minCalpha = prev.Cn[1];
+ }
+ prev = atm;
+
+ /* Check how many dihedrals we have */
+ if ((atm.N != -1) && (atm.Cn[1] != -1) && (atm.C != -1) &&
+ (atm.O != -1) && ((atm.H != -1) || (atm.minC != -1)))
+ {
+ dl[nl].resnr = ires+1;
+ dl[nl].atm = atm;
+ dl[nl].atm.Cn[0] = atm.N;
+ if ((atm.Cn[3] != -1) && (atm.Cn[2] != -1) && (atm.Cn[1] != -1))
+ {
+ nc[0]++;
+ if (atm.Cn[4] != -1)
+ {
+ nc[1]++;
+ if (atm.Cn[5] != -1)
+ {
+ nc[2]++;
+ if (atm.Cn[6] != -1)
+ {
+ nc[3]++;
+ if (atm.Cn[7] != -1)
+ {
+ nc[4]++;
+ if (atm.Cn[8] != -1)
+ {
+ nc[5]++;
+ }
+ }
+ }
+ }
+ }
+ }
- b = ((dl->atm.minO != -1) && (dl->atm.minC != -1) && (dl->atm.N != -1) && (dl->atm.Cn[1] != -1));
++ if ((atm.minC != -1) && (atm.minCalpha != -1))
+ {
+ nc[6]++;
+ }
+ dl[nl].index = gmx_residuetype_get_index(rt, thisres);
+
+ sprintf(dl[nl].name, "%s%d", thisres, ires+r0);
+ nl++;
+ }
+ else if (debug)
+ {
+ fprintf(debug, "Could not find N atom but could find other atoms"
+ " in residue %s%d\n", thisres, ires+r0);
+ }
+ }
+ fprintf(stderr, "\n");
+ fprintf(log, "\n");
+ fprintf(log, "There are %d residues with dihedrals\n", nl);
+ j = 0;
+ if (bPhi)
+ {
+ j += nl;
+ }
+ if (bPsi)
+ {
+ j += nl;
+ }
+ if (bChi)
+ {
+ for (i = 0; (i < maxchi); i++)
+ {
+ j += nc[i];
+ }
+ }
+ fprintf(log, "There are %d dihedrals\n", j);
+ fprintf(log, "Dihedral: ");
+ if (bPhi)
+ {
+ fprintf(log, " Phi ");
+ }
+ if (bPsi)
+ {
+ fprintf(log, " Psi ");
+ }
+ if (bChi)
+ {
+ for (i = 0; (i < maxchi); i++)
+ {
+ fprintf(log, "Chi%d ", i+1);
+ }
+ }
+ fprintf(log, "\nNumber: ");
+ if (bPhi)
+ {
+ fprintf(log, "%4d ", nl);
+ }
+ if (bPsi)
+ {
+ fprintf(log, "%4d ", nl);
+ }
+ if (bChi)
+ {
+ for (i = 0; (i < maxchi); i++)
+ {
+ fprintf(log, "%4d ", nc[i]);
+ }
+ }
+ fprintf(log, "\n");
+
+ *nlist = nl;
+
+ return dl;
+}
+
+gmx_bool has_dihedral(int Dih, t_dlist *dl)
+{
+ gmx_bool b = FALSE;
+ int ddd;
+
+ switch (Dih)
+ {
+ case edPhi:
+ b = ((dl->atm.H != -1) && (dl->atm.N != -1) && (dl->atm.Cn[1] != -1) && (dl->atm.C != -1));
+ break;
+ case edPsi:
+ b = ((dl->atm.N != -1) && (dl->atm.Cn[1] != -1) && (dl->atm.C != -1) && (dl->atm.O != -1));
+ break;
+ case edOmega:
- fprintf(fp, " Omega [%5d,%5d,%5d,%5d]", 1+dl[i].atm.minO, 1+dl[i].atm.minC,
++ b = ((dl->atm.minCalpha != -1) && (dl->atm.minC != -1) && (dl->atm.N != -1) && (dl->atm.Cn[1] != -1));
+ break;
+ case edChi1:
+ case edChi2:
+ case edChi3:
+ case edChi4:
+ case edChi5:
+ case edChi6:
+ ddd = Dih - edChi1;
+ b = ((dl->atm.Cn[ddd] != -1) && (dl->atm.Cn[ddd+1] != -1) &&
+ (dl->atm.Cn[ddd+2] != -1) && (dl->atm.Cn[ddd+3] != -1));
+ break;
+ default:
+ pr_dlist(stdout, 1, dl, 1, 0, TRUE, TRUE, TRUE, TRUE, MAXCHI);
+ gmx_fatal(FARGS, "Non existant dihedral %d in file %s, line %d",
+ Dih, __FILE__, __LINE__);
+ }
+ return b;
+}
+
+static void pr_one_ro(FILE *fp, t_dlist *dl, int nDih, real dt)
+{
+ int k;
+ for (k = 0; k < NROT; k++)
+ {
+ fprintf(fp, " %6.2f", dl->rot_occ[nDih][k]);
+ }
+ fprintf(fp, "\n");
+}
+
+static void pr_ntr_s2(FILE *fp, t_dlist *dl, int nDih, real dt)
+{
+ fprintf(fp, " %6.2f %6.2f\n", (dt == 0) ? 0 : dl->ntr[nDih]/dt, dl->S2[nDih]);
+}
+
+void pr_dlist(FILE *fp, int nl, t_dlist dl[], real dt, int printtype,
+ gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, gmx_bool bOmega, int maxchi)
+{
+ int i, Xi;
+
+ void (*pr_props)(FILE *, t_dlist *, int, real);
+
+ /* Analysis of dihedral transitions etc */
+
+ if (printtype == edPrintST)
+ {
+ pr_props = pr_ntr_s2;
+ fprintf(stderr, "Now printing out transitions and OPs...\n");
+ }
+ else
+ {
+ pr_props = pr_one_ro;
+ fprintf(stderr, "Now printing out rotamer occupancies...\n");
+ fprintf(fp, "\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\n");
+ }
+
+ /* change atom numbers from 0 based to 1 based */
+ for (i = 0; (i < nl); i++)
+ {
+ fprintf(fp, "Residue %s\n", dl[i].name);
+ if (printtype == edPrintST)
+ {
+ fprintf(fp, " Angle [ AI, AJ, AK, AL] #tr/ns S^2D \n"
+ "--------------------------------------------\n");
+ }
+ else
+ {
+ fprintf(fp, " Angle [ AI, AJ, AK, AL] rotamers 0 g(-) t g(+)\n"
+ "--------------------------------------------\n");
+ }
+ if (bPhi)
+ {
+ fprintf(fp, " Phi [%5d,%5d,%5d,%5d]",
+ (dl[i].atm.H == -1) ? 1+dl[i].atm.minC : 1+dl[i].atm.H,
+ 1+dl[i].atm.N, 1+dl[i].atm.Cn[1], 1+dl[i].atm.C);
+ pr_props(fp, &dl[i], edPhi, dt);
+ }
+ if (bPsi)
+ {
+ fprintf(fp, " Psi [%5d,%5d,%5d,%5d]", 1+dl[i].atm.N, 1+dl[i].atm.Cn[1],
+ 1+dl[i].atm.C, 1+dl[i].atm.O);
+ pr_props(fp, &dl[i], edPsi, dt);
+ }
+ if (bOmega && has_dihedral(edOmega, &(dl[i])))
+ {
++ fprintf(fp, " Omega [%5d,%5d,%5d,%5d]", 1+dl[i].atm.minCalpha, 1+dl[i].atm.minC,
+ 1+dl[i].atm.N, 1+dl[i].atm.Cn[1]);
+ pr_props(fp, &dl[i], edOmega, dt);
+ }
+ for (Xi = 0; Xi < MAXCHI; Xi++)
+ {
+ if (bChi && (Xi < maxchi) && (dl[i].atm.Cn[Xi+3] != -1) )
+ {
+ fprintf(fp, " Chi%d[%5d,%5d,%5d,%5d]", Xi+1, 1+dl[i].atm.Cn[Xi],
+ 1+dl[i].atm.Cn[Xi+1], 1+dl[i].atm.Cn[Xi+2],
+ 1+dl[i].atm.Cn[Xi+3]);
+ pr_props(fp, &dl[i], Xi+edChi1, dt); /* Xi+2 was wrong here */
+ }
+ }
+ fprintf(fp, "\n");
+ }
+}
+
+
+
+int pr_trans(FILE *fp, int nl, t_dlist dl[], real dt, int Xi)
+{
+ /* never called at the moment */
+
+ int i, nn, nz;
+
+ nz = 0;
+ fprintf(fp, "\\begin{table}[h]\n");
+ fprintf(fp, "\\caption{Number of dihedral transitions per nanosecond}\n");
+ fprintf(fp, "\\begin{tabular}{|l|l|}\n");
+ fprintf(fp, "\\hline\n");
+ fprintf(fp, "Residue\t&$\\chi_%d$\t\\\\\n", Xi+1);
+ for (i = 0; (i < nl); i++)
+ {
+ nn = dl[i].ntr[Xi]/dt;
+
+ if (nn == 0)
+ {
+ fprintf(fp, "%s\t&\\HL{%d}\t\\\\\n", dl[i].name, nn);
+ nz++;
+ }
+ else if (nn > 0)
+ {
+ fprintf(fp, "%s\t&\\%d\t\\\\\n", dl[i].name, nn);
+ }
+ }
+ fprintf(fp, "\\hline\n");
+ fprintf(fp, "\\end{tabular}\n");
+ fprintf(fp, "\\end{table}\n\n");
+
+ return nz;
+}
--- /dev/null
- for (i = 0; (i < nl); i++)
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Green Red Orange Magenta Azure Cyan Skyblue
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <math.h>
+
+#include "confio.h"
+#include "pdbio.h"
+#include "copyrite.h"
+#include "gmx_fatal.h"
+#include "futil.h"
+#include "gstat.h"
+#include "macros.h"
+#include "maths.h"
+#include "physics.h"
+#include "index.h"
+#include "smalloc.h"
+#include "statutil.h"
+#include "tpxio.h"
+#include <string.h>
+#include "sysstuff.h"
+#include "txtdump.h"
+#include "typedefs.h"
+#include "vec.h"
+#include "strdb.h"
+#include "xvgr.h"
+#include "matio.h"
+#include "gmx_ana.h"
+
+static gmx_bool bAllowed(real phi, real psi)
+{
+ static const char *map[] = {
+ "1100000000000000001111111000000000001111111111111111111111111",
+ "1100000000000000001111110000000000011111111111111111111111111",
+ "1100000000000000001111110000000000011111111111111111111111111",
+ "1100000000000000001111100000000000111111111111111111111111111",
+ "1100000000000000001111100000000000111111111111111111111111111",
+ "1100000000000000001111100000000001111111111111111111111111111",
+ "1100000000000000001111100000000001111111111111111111111111111",
+ "1100000000000000001111100000000011111111111111111111111111111",
+ "1110000000000000001111110000000111111111111111111111111111111",
+ "1110000000000000001111110000001111111111111111111111111111111",
+ "1110000000000000001111111000011111111111111111111111111111111",
+ "1110000000000000001111111100111111111111111111111111111111111",
+ "1110000000000000001111111111111111111111111111111111111111111",
+ "1110000000000000001111111111111111111111111111111111111111111",
+ "1110000000000000001111111111111111111111111111111111111111111",
+ "1110000000000000001111111111111111111111111111111111111111111",
+ "1110000000000000001111111111111110011111111111111111111111111",
+ "1110000000000000001111111111111100000111111111111111111111111",
+ "1110000000000000001111111111111000000000001111111111111111111",
+ "1100000000000000001111111111110000000000000011111111111111111",
+ "1100000000000000001111111111100000000000000011111111111111111",
+ "1000000000000000001111111111000000000000000001111111111111110",
+ "0000000000000000001111111110000000000000000000111111111111100",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000111111111111000000000000000",
+ "1100000000000000000000000000000001111111111111100000000000111",
+ "1100000000000000000000000000000001111111111111110000000000111",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000000000000000000000000"
+ };
+#define NPP asize(map)
+ int x, y;
+
+#define INDEX(ppp) ((((int) (360+ppp*RAD2DEG)) % 360)/6)
+ x = INDEX(phi);
+ y = INDEX(psi);
+#undef INDEX
+ return (gmx_bool) map[x][y];
+}
+
+atom_id *make_chi_ind(int nl, t_dlist dl[], int *ndih)
+{
+ atom_id *id;
+ int i, Xi, n;
+
+ /* There are nl residues with max edMax dihedrals with 4 atoms each */
+ snew(id, nl*edMax*4);
+
+ n = 0;
+ for (i = 0; (i < nl); i++)
+ {
+ /* Phi, fake the first one */
+ dl[i].j0[edPhi] = n/4;
+ if (dl[i].atm.minC >= 0)
+ {
+ id[n++] = dl[i].atm.minC;
+ }
+ else
+ {
+ id[n++] = dl[i].atm.H;
+ }
+ id[n++] = dl[i].atm.N;
+ id[n++] = dl[i].atm.Cn[1];
+ id[n++] = dl[i].atm.C;
+ }
+ for (i = 0; (i < nl); i++)
+ {
+ /* Psi, fake the last one */
+ dl[i].j0[edPsi] = n/4;
+ id[n++] = dl[i].atm.N;
+ id[n++] = dl[i].atm.Cn[1];
+ id[n++] = dl[i].atm.C;
+ if (i < (nl-1) )
+ {
+ id[n++] = dl[i+1].atm.N;
+ }
+ else
+ {
+ id[n++] = dl[i].atm.O;
+ }
+ }
- id[n++] = dl[i].atm.minO;
++ for (i = 1; (i < nl); i++)
+ {
+ /* Omega */
+ if (has_dihedral(edOmega, &(dl[i])))
+ {
+ dl[i].j0[edOmega] = n/4;
- id[n++] = dl[i].atm.H;
++ id[n++] = dl[i].atm.minCalpha;
+ id[n++] = dl[i].atm.minC;
+ id[n++] = dl[i].atm.N;
++ id[n++] = dl[i].atm.Cn[1];
+ }
+ }
+ for (Xi = 0; (Xi < MAXCHI); Xi++)
+ {
+ /* Chi# */
+ for (i = 0; (i < nl); i++)
+ {
+ if (dl[i].atm.Cn[Xi+3] != -1)
+ {
+ dl[i].j0[edChi1+Xi] = n/4;
+ id[n++] = dl[i].atm.Cn[Xi];
+ id[n++] = dl[i].atm.Cn[Xi+1];
+ id[n++] = dl[i].atm.Cn[Xi+2];
+ id[n++] = dl[i].atm.Cn[Xi+3];
+ }
+ }
+ }
+ *ndih = n/4;
+
+ return id;
+}
+
+int bin(real chi, int mult)
+{
+ mult = 3;
+
+ return (int) (chi*mult/360.0);
+}
+
+
+static void do_dihcorr(const char *fn, int nf, int ndih, real **dih, real dt,
+ int nlist, t_dlist dlist[], real time[], int maxchi,
+ gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, gmx_bool bOmega,
+ const output_env_t oenv)
+{
+ char name1[256], name2[256];
+ int i, j, Xi;
+
+ do_autocorr(fn, oenv, "Dihedral Autocorrelation Function",
+ nf, ndih, dih, dt, eacCos, FALSE);
+ /* Dump em all */
+ j = 0;
+ for (i = 0; (i < nlist); i++)
+ {
+ if (bPhi)
+ {
+ print_one(oenv, "corrphi", dlist[i].name, "Phi ACF for", "C(t)", nf/2, time,
+ dih[j]);
+ }
+ j++;
+ }
+ for (i = 0; (i < nlist); i++)
+ {
+ if (bPsi)
+ {
+ print_one(oenv, "corrpsi", dlist[i].name, "Psi ACF for", "C(t)", nf/2, time,
+ dih[j]);
+ }
+ j++;
+ }
+ for (i = 0; (i < nlist); i++)
+ {
+ if (has_dihedral(edOmega, &dlist[i]))
+ {
+ if (bOmega)
+ {
+ print_one(oenv, "corromega", dlist[i].name, "Omega ACF for", "C(t)",
+ nf/2, time, dih[j]);
+ }
+ j++;
+ }
+ }
+ for (Xi = 0; (Xi < maxchi); Xi++)
+ {
+ sprintf(name1, "corrchi%d", Xi+1);
+ sprintf(name2, "Chi%d ACF for", Xi+1);
+ for (i = 0; (i < nlist); i++)
+ {
+ if (dlist[i].atm.Cn[Xi+3] != -1)
+ {
+ if (bChi)
+ {
+ print_one(oenv, name1, dlist[i].name, name2, "C(t)", nf/2, time, dih[j]);
+ }
+ j++;
+ }
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+static void copy_dih_data(real in[], real out[], int nf, gmx_bool bLEAVE)
+{
+ /* if bLEAVE, do nothing to data in copying to out
+ * otherwise multiply by 180/pi to convert rad to deg */
+ int i;
+ real mult;
+ if (bLEAVE)
+ {
+ mult = 1;
+ }
+ else
+ {
+ mult = (180.0/M_PI);
+ }
+ for (i = 0; (i < nf); i++)
+ {
+ out[i] = in[i]*mult;
+ }
+}
+
+static void dump_em_all(int nlist, t_dlist dlist[], int nf, real time[],
+ real **dih, int maxchi,
+ gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, gmx_bool bOmega, gmx_bool bRAD,
+ const output_env_t oenv)
+{
+ char name[256], titlestr[256], ystr[256];
+ real *data;
+ int i, j, Xi;
+
+ snew(data, nf);
+ if (bRAD)
+ {
+ strcpy(ystr, "Angle (rad)");
+ }
+ else
+ {
+ strcpy(ystr, "Angle (degrees)");
+ }
+
+ /* Dump em all */
+ j = 0;
+ for (i = 0; (i < nlist); i++)
+ {
+ /* grs debug printf("OK i %d j %d\n", i, j) ; */
+ if (bPhi)
+ {
+ copy_dih_data(dih[j], data, nf, bRAD);
+ print_one(oenv, "phi", dlist[i].name, "\\xf\\f{}", ystr, nf, time, data);
+ }
+ j++;
+ }
+ for (i = 0; (i < nlist); i++)
+ {
+ if (bPsi)
+ {
+ copy_dih_data(dih[j], data, nf, bRAD);
+ print_one(oenv, "psi", dlist[i].name, "\\xy\\f{}", ystr, nf, time, data);
+ }
+ j++;
+ }
+ for (i = 0; (i < nlist); i++)
+ {
+ if (has_dihedral(edOmega, &(dlist[i])))
+ {
+ if (bOmega)
+ {
+ copy_dih_data(dih[j], data, nf, bRAD);
+ print_one(oenv, "omega", dlist[i].name, "\\xw\\f{}", ystr, nf, time, data);
+ }
+ j++;
+ }
+ }
+
+ for (Xi = 0; (Xi < maxchi); Xi++)
+ {
+ for (i = 0; (i < nlist); i++)
+ {
+ if (dlist[i].atm.Cn[Xi+3] != -1)
+ {
+ if (bChi)
+ {
+ sprintf(name, "chi%d", Xi+1);
+ sprintf(titlestr, "\\xc\\f{}\\s%d\\N", Xi+1);
+ copy_dih_data(dih[j], data, nf, bRAD);
+ print_one(oenv, name, dlist[i].name, titlestr, ystr, nf, time, data);
+ }
+ j++;
+ }
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+static void reset_one(real dih[], int nf, real phase)
+{
+ int j;
+
+ for (j = 0; (j < nf); j++)
+ {
+ dih[j] += phase;
+ while (dih[j] < -M_PI)
+ {
+ dih[j] += 2*M_PI;
+ }
+ while (dih[j] >= M_PI)
+ {
+ dih[j] -= 2*M_PI;
+ }
+ }
+}
+
+static int reset_em_all(int nlist, t_dlist dlist[], int nf,
+ real **dih, int maxchi)
+{
+ int i, j, Xi;
+
+ /* Reset em all */
+ j = 0;
+ /* Phi */
+ for (i = 0; (i < nlist); i++)
+ {
+ if (dlist[i].atm.minC == -1)
+ {
+ reset_one(dih[j++], nf, M_PI);
+ }
+ else
+ {
+ reset_one(dih[j++], nf, 0);
+ }
+ }
+ /* Psi */
+ for (i = 0; (i < nlist-1); i++)
+ {
+ reset_one(dih[j++], nf, 0);
+ }
+ /* last Psi is faked from O */
+ reset_one(dih[j++], nf, M_PI);
+
+ /* Omega */
+ for (i = 0; (i < nlist); i++)
+ {
+ if (has_dihedral(edOmega, &dlist[i]))
+ {
+ reset_one(dih[j++], nf, 0);
+ }
+ }
+ /* Chi 1 thru maxchi */
+ for (Xi = 0; (Xi < maxchi); Xi++)
+ {
+ for (i = 0; (i < nlist); i++)
+ {
+ if (dlist[i].atm.Cn[Xi+3] != -1)
+ {
+ reset_one(dih[j], nf, 0);
+ j++;
+ }
+ }
+ }
+ fprintf(stderr, "j after resetting (nr. active dihedrals) = %d\n", j);
+ return j;
+}
+
+static void histogramming(FILE *log, int nbin, gmx_residuetype_t rt,
+ int nf, int maxchi, real **dih,
+ int nlist, t_dlist dlist[],
+ atom_id index[],
+ gmx_bool bPhi, gmx_bool bPsi, gmx_bool bOmega, gmx_bool bChi,
+ gmx_bool bNormalize, gmx_bool bSSHisto, const char *ssdump,
+ real bfac_max, t_atoms *atoms,
+ gmx_bool bDo_jc, const char *fn,
+ const output_env_t oenv)
+{
+ /* also gets 3J couplings and order parameters S2 */
+ t_karplus kkkphi[] = {
+ { "J_NHa1", 6.51, -1.76, 1.6, -M_PI/3, 0.0, 0.0 },
+ { "J_NHa2", 6.51, -1.76, 1.6, M_PI/3, 0.0, 0.0 },
+ { "J_HaC'", 4.0, 1.1, 0.1, 0.0, 0.0, 0.0 },
+ { "J_NHCb", 4.7, -1.5, -0.2, M_PI/3, 0.0, 0.0 },
+ { "J_Ci-1Hai", 4.5, -1.3, -1.2, 2*M_PI/3, 0.0, 0.0 }
+ };
+ t_karplus kkkpsi[] = {
+ { "J_HaN", -0.88, -0.61, -0.27, M_PI/3, 0.0, 0.0 }
+ };
+ t_karplus kkkchi1[] = {
+ { "JHaHb2", 9.5, -1.6, 1.8, -M_PI/3, 0, 0.0 },
+ { "JHaHb3", 9.5, -1.6, 1.8, 0, 0, 0.0 }
+ };
+#define NKKKPHI asize(kkkphi)
+#define NKKKPSI asize(kkkpsi)
+#define NKKKCHI asize(kkkchi1)
+#define NJC (NKKKPHI+NKKKPSI+NKKKCHI)
+
+ FILE *fp, *ssfp[3] = {NULL, NULL, NULL};
+ const char *sss[3] = { "sheet", "helix", "coil" };
+ real S2;
+ real *normhisto;
+ real **Jc, **Jcsig;
+ int ****his_aa_ss = NULL;
+ int ***his_aa, **his_aa1, *histmp;
+ int i, j, k, m, n, nn, Dih, nres, hindex, angle;
+ gmx_bool bBfac, bOccup;
+ char hisfile[256], hhisfile[256], sshisfile[256], title[256], *ss_str = NULL;
+ char **leg;
+ const char *residue_name;
+ int rt_size;
+
+ rt_size = gmx_residuetype_get_size(rt);
+ if (bSSHisto)
+ {
+ fp = ffopen(ssdump, "r");
+ if (1 != fscanf(fp, "%d", &nres))
+ {
+ gmx_fatal(FARGS, "Error reading from file %s", ssdump);
+ }
+
+ snew(ss_str, nres+1);
+ if (1 != fscanf(fp, "%s", ss_str))
+ {
+ gmx_fatal(FARGS, "Error reading from file %s", ssdump);
+ }
+
+ ffclose(fp);
+ /* Four dimensional array... Very cool */
+ snew(his_aa_ss, 3);
+ for (i = 0; (i < 3); i++)
+ {
+ snew(his_aa_ss[i], rt_size+1);
+ for (j = 0; (j <= rt_size); j++)
+ {
+ snew(his_aa_ss[i][j], edMax);
+ for (Dih = 0; (Dih < edMax); Dih++)
+ {
+ snew(his_aa_ss[i][j][Dih], nbin+1);
+ }
+ }
+ }
+ }
+ snew(his_aa, edMax);
+ for (Dih = 0; (Dih < edMax); Dih++)
+ {
+ snew(his_aa[Dih], rt_size+1);
+ for (i = 0; (i <= rt_size); i++)
+ {
+ snew(his_aa[Dih][i], nbin+1);
+ }
+ }
+ snew(histmp, nbin);
+
+ snew(Jc, nlist);
+ snew(Jcsig, nlist);
+ for (i = 0; (i < nlist); i++)
+ {
+ snew(Jc[i], NJC);
+ snew(Jcsig[i], NJC);
+ }
+
+ j = 0;
+ n = 0;
+ for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
+ {
+ for (i = 0; (i < nlist); i++)
+ {
+ if (((Dih < edOmega) ) ||
+ ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i])))) ||
+ ((Dih > edOmega) && (dlist[i].atm.Cn[Dih-NONCHI+3] != -1)))
+ {
+ make_histo(log, nf, dih[j], nbin, histmp, -M_PI, M_PI);
+
+ if (bSSHisto)
+ {
+ /* Assume there is only one structure, the first.
+ * Compute index in histogram.
+ */
+ /* Check the atoms to see whether their B-factors are low enough
+ * Check atoms to see their occupancy is 1.
+ */
+ bBfac = bOccup = TRUE;
+ for (nn = 0; (nn < 4); nn++, n++)
+ {
+ bBfac = bBfac && (atoms->pdbinfo[index[n]].bfac <= bfac_max);
+ bOccup = bOccup && (atoms->pdbinfo[index[n]].occup == 1);
+ }
+ if (bOccup && ((bfac_max <= 0) || ((bfac_max > 0) && bBfac)))
+ {
+ hindex = ((dih[j][0]+M_PI)*nbin)/(2*M_PI);
+ range_check(hindex, 0, nbin);
+
+ /* Assign dihedral to either of the structure determined
+ * histograms
+ */
+ switch (ss_str[dlist[i].resnr])
+ {
+ case 'E':
+ his_aa_ss[0][dlist[i].index][Dih][hindex]++;
+ break;
+ case 'H':
+ his_aa_ss[1][dlist[i].index][Dih][hindex]++;
+ break;
+ default:
+ his_aa_ss[2][dlist[i].index][Dih][hindex]++;
+ break;
+ }
+ }
+ else if (debug)
+ {
+ fprintf(debug, "Res. %d has imcomplete occupancy or bfacs > %g\n",
+ dlist[i].resnr, bfac_max);
+ }
+ }
+ else
+ {
+ n += 4;
+ }
+
+ switch (Dih)
+ {
+ case edPhi:
+ calc_distribution_props(nbin, histmp, -M_PI, NKKKPHI, kkkphi, &S2);
+
+ for (m = 0; (m < NKKKPHI); m++)
+ {
+ Jc[i][m] = kkkphi[m].Jc;
+ Jcsig[i][m] = kkkphi[m].Jcsig;
+ }
+ break;
+ case edPsi:
+ calc_distribution_props(nbin, histmp, -M_PI, NKKKPSI, kkkpsi, &S2);
+
+ for (m = 0; (m < NKKKPSI); m++)
+ {
+ Jc[i][NKKKPHI+m] = kkkpsi[m].Jc;
+ Jcsig[i][NKKKPHI+m] = kkkpsi[m].Jcsig;
+ }
+ break;
+ case edChi1:
+ calc_distribution_props(nbin, histmp, -M_PI, NKKKCHI, kkkchi1, &S2);
+ for (m = 0; (m < NKKKCHI); m++)
+ {
+ Jc[i][NKKKPHI+NKKKPSI+m] = kkkchi1[m].Jc;
+ Jcsig[i][NKKKPHI+NKKKPSI+m] = kkkchi1[m].Jcsig;
+ }
+ break;
+ default: /* covers edOmega and higher Chis than Chi1 */
+ calc_distribution_props(nbin, histmp, -M_PI, 0, NULL, &S2);
+ break;
+ }
+ dlist[i].S2[Dih] = S2;
+
+ /* Sum distribution per amino acid type as well */
+ for (k = 0; (k < nbin); k++)
+ {
+ his_aa[Dih][dlist[i].index][k] += histmp[k];
+ histmp[k] = 0;
+ }
+ j++;
+ }
+ else /* dihed not defined */
+ {
+ dlist[i].S2[Dih] = 0.0;
+ }
+ }
+ }
+ sfree(histmp);
+
+ /* Print out Jcouplings */
+ fprintf(log, "\n *** J-Couplings from simulation (plus std. dev.) ***\n\n");
+ fprintf(log, "Residue ");
+ for (i = 0; (i < NKKKPHI); i++)
+ {
+ fprintf(log, "%7s SD", kkkphi[i].name);
+ }
+ for (i = 0; (i < NKKKPSI); i++)
+ {
+ fprintf(log, "%7s SD", kkkpsi[i].name);
+ }
+ for (i = 0; (i < NKKKCHI); i++)
+ {
+ fprintf(log, "%7s SD", kkkchi1[i].name);
+ }
+ fprintf(log, "\n");
+ for (i = 0; (i < NJC+1); i++)
+ {
+ fprintf(log, "------------");
+ }
+ fprintf(log, "\n");
+ for (i = 0; (i < nlist); i++)
+ {
+ fprintf(log, "%-10s", dlist[i].name);
+ for (j = 0; (j < NJC); j++)
+ {
+ fprintf(log, " %5.2f %4.2f", Jc[i][j], Jcsig[i][j]);
+ }
+ fprintf(log, "\n");
+ }
+ fprintf(log, "\n");
+
+ /* and to -jc file... */
+ if (bDo_jc)
+ {
+ fp = xvgropen(fn, "\\S3\\NJ-Couplings from Karplus Equation", "Residue",
+ "Coupling", oenv);
+ snew(leg, NJC);
+ for (i = 0; (i < NKKKPHI); i++)
+ {
+ leg[i] = strdup(kkkphi[i].name);
+ }
+ for (i = 0; (i < NKKKPSI); i++)
+ {
+ leg[i+NKKKPHI] = strdup(kkkpsi[i].name);
+ }
+ for (i = 0; (i < NKKKCHI); i++)
+ {
+ leg[i+NKKKPHI+NKKKPSI] = strdup(kkkchi1[i].name);
+ }
+ xvgr_legend(fp, NJC, (const char**)leg, oenv);
+ fprintf(fp, "%5s ", "#Res.");
+ for (i = 0; (i < NJC); i++)
+ {
+ fprintf(fp, "%10s ", leg[i]);
+ }
+ fprintf(fp, "\n");
+ for (i = 0; (i < nlist); i++)
+ {
+ fprintf(fp, "%5d ", dlist[i].resnr);
+ for (j = 0; (j < NJC); j++)
+ {
+ fprintf(fp, " %8.3f", Jc[i][j]);
+ }
+ fprintf(fp, "\n");
+ }
+ ffclose(fp);
+ for (i = 0; (i < NJC); i++)
+ {
+ sfree(leg[i]);
+ }
+ }
+ /* finished -jc stuff */
+
+ snew(normhisto, nbin);
+ for (i = 0; (i < rt_size); i++)
+ {
+ for (Dih = 0; (Dih < edMax); Dih++)
+ {
+ /* First check whether something is in there */
+ for (j = 0; (j < nbin); j++)
+ {
+ if (his_aa[Dih][i][j] != 0)
+ {
+ break;
+ }
+ }
+ if ((j < nbin) &&
+ ((bPhi && (Dih == edPhi)) ||
+ (bPsi && (Dih == edPsi)) ||
+ (bOmega && (Dih == edOmega)) ||
+ (bChi && (Dih >= edChi1))))
+ {
+ if (bNormalize)
+ {
+ normalize_histo(nbin, his_aa[Dih][i], (360.0/nbin), normhisto);
+ }
+
+ residue_name = gmx_residuetype_get_name(rt, i);
+ switch (Dih)
+ {
+ case edPhi:
+ sprintf(hisfile, "histo-phi%s", residue_name);
+ sprintf(title, "\\xf\\f{} Distribution for %s", residue_name);
+ break;
+ case edPsi:
+ sprintf(hisfile, "histo-psi%s", residue_name);
+ sprintf(title, "\\xy\\f{} Distribution for %s", residue_name);
+ break;
+ case edOmega:
+ sprintf(hisfile, "histo-omega%s", residue_name);
+ sprintf(title, "\\xw\\f{} Distribution for %s", residue_name);
+ break;
+ default:
+ sprintf(hisfile, "histo-chi%d%s", Dih-NONCHI+1, residue_name);
+ sprintf(title, "\\xc\\f{}\\s%d\\N Distribution for %s",
+ Dih-NONCHI+1, residue_name);
+ }
+ strcpy(hhisfile, hisfile);
+ strcat(hhisfile, ".xvg");
+ fp = xvgropen(hhisfile, title, "Degrees", "", oenv);
+ fprintf(fp, "@ with g0\n");
+ xvgr_world(fp, -180, 0, 180, 0.1, oenv);
+ fprintf(fp, "# this effort to set graph size fails unless you run with -autoscale none or -autoscale y flags\n");
+ fprintf(fp, "@ xaxis tick on\n");
+ fprintf(fp, "@ xaxis tick major 90\n");
+ fprintf(fp, "@ xaxis tick minor 30\n");
+ fprintf(fp, "@ xaxis ticklabel prec 0\n");
+ fprintf(fp, "@ yaxis tick off\n");
+ fprintf(fp, "@ yaxis ticklabel off\n");
+ fprintf(fp, "@ type xy\n");
+ if (bSSHisto)
+ {
+ for (k = 0; (k < 3); k++)
+ {
+ sprintf(sshisfile, "%s-%s.xvg", hisfile, sss[k]);
+ ssfp[k] = ffopen(sshisfile, "w");
+ }
+ }
+ for (j = 0; (j < nbin); j++)
+ {
+ angle = -180 + (360/nbin)*j;
+ if (bNormalize)
+ {
+ fprintf(fp, "%5d %10g\n", angle, normhisto[j]);
+ }
+ else
+ {
+ fprintf(fp, "%5d %10d\n", angle, his_aa[Dih][i][j]);
+ }
+ if (bSSHisto)
+ {
+ for (k = 0; (k < 3); k++)
+ {
+ fprintf(ssfp[k], "%5d %10d\n", angle,
+ his_aa_ss[k][i][Dih][j]);
+ }
+ }
+ }
+ fprintf(fp, "&\n");
+ ffclose(fp);
+ if (bSSHisto)
+ {
+ for (k = 0; (k < 3); k++)
+ {
+ fprintf(ssfp[k], "&\n");
+ ffclose(ssfp[k]);
+ }
+ }
+ }
+ }
+ }
+ sfree(normhisto);
+
+ if (bSSHisto)
+ {
+ /* Four dimensional array... Very cool */
+ for (i = 0; (i < 3); i++)
+ {
+ for (j = 0; (j <= rt_size); j++)
+ {
+ for (Dih = 0; (Dih < edMax); Dih++)
+ {
+ sfree(his_aa_ss[i][j][Dih]);
+ }
+ sfree(his_aa_ss[i][j]);
+ }
+ sfree(his_aa_ss[i]);
+ }
+ sfree(his_aa_ss);
+ sfree(ss_str);
+ }
+}
+
+static FILE *rama_file(const char *fn, const char *title, const char *xaxis,
+ const char *yaxis, const output_env_t oenv)
+{
+ FILE *fp;
+
+ fp = xvgropen(fn, title, xaxis, yaxis, oenv);
+ fprintf(fp, "@ with g0\n");
+ xvgr_world(fp, -180, -180, 180, 180, oenv);
+ fprintf(fp, "@ xaxis tick on\n");
+ fprintf(fp, "@ xaxis tick major 90\n");
+ fprintf(fp, "@ xaxis tick minor 30\n");
+ fprintf(fp, "@ xaxis ticklabel prec 0\n");
+ fprintf(fp, "@ yaxis tick on\n");
+ fprintf(fp, "@ yaxis tick major 90\n");
+ fprintf(fp, "@ yaxis tick minor 30\n");
+ fprintf(fp, "@ yaxis ticklabel prec 0\n");
+ fprintf(fp, "@ s0 type xy\n");
+ fprintf(fp, "@ s0 symbol 2\n");
+ fprintf(fp, "@ s0 symbol size 0.410000\n");
+ fprintf(fp, "@ s0 symbol fill 1\n");
+ fprintf(fp, "@ s0 symbol color 1\n");
+ fprintf(fp, "@ s0 symbol linewidth 1\n");
+ fprintf(fp, "@ s0 symbol linestyle 1\n");
+ fprintf(fp, "@ s0 symbol center false\n");
+ fprintf(fp, "@ s0 symbol char 0\n");
+ fprintf(fp, "@ s0 skip 0\n");
+ fprintf(fp, "@ s0 linestyle 0\n");
+ fprintf(fp, "@ s0 linewidth 1\n");
+ fprintf(fp, "@ type xy\n");
+
+ return fp;
+}
+
+static void do_rama(int nf, int nlist, t_dlist dlist[], real **dih,
+ gmx_bool bViol, gmx_bool bRamOmega, const output_env_t oenv)
+{
+ FILE *fp, *gp = NULL;
+ gmx_bool bOm;
+ char fn[256];
+ int i, j, k, Xi1, Xi2, Phi, Psi, Om = 0, nlevels;
+#define NMAT 120
+ real **mat = NULL, phi, psi, omega, axis[NMAT], lo, hi;
+ t_rgb rlo = { 1.0, 0.0, 0.0 };
+ t_rgb rmid = { 1.0, 1.0, 1.0 };
+ t_rgb rhi = { 0.0, 0.0, 1.0 };
+
+ for (i = 0; (i < nlist); i++)
+ {
+ if ((has_dihedral(edPhi, &(dlist[i]))) &&
+ (has_dihedral(edPsi, &(dlist[i]))))
+ {
+ sprintf(fn, "ramaPhiPsi%s.xvg", dlist[i].name);
+ fp = rama_file(fn, "Ramachandran Plot",
+ "\\8f\\4 (deg)", "\\8y\\4 (deg)", oenv);
+ bOm = bRamOmega && has_dihedral(edOmega, &(dlist[i]));
+ if (bOm)
+ {
+ Om = dlist[i].j0[edOmega];
+ snew(mat, NMAT);
+ for (j = 0; (j < NMAT); j++)
+ {
+ snew(mat[j], NMAT);
+ axis[j] = -180+(360*j)/NMAT;
+ }
+ }
+ if (bViol)
+ {
+ sprintf(fn, "violPhiPsi%s.xvg", dlist[i].name);
+ gp = ffopen(fn, "w");
+ }
+ Phi = dlist[i].j0[edPhi];
+ Psi = dlist[i].j0[edPsi];
+ for (j = 0; (j < nf); j++)
+ {
+ phi = RAD2DEG*dih[Phi][j];
+ psi = RAD2DEG*dih[Psi][j];
+ fprintf(fp, "%10g %10g\n", phi, psi);
+ if (bViol)
+ {
+ fprintf(gp, "%d\n", !bAllowed(dih[Phi][j], RAD2DEG*dih[Psi][j]));
+ }
+ if (bOm)
+ {
+ omega = RAD2DEG*dih[Om][j];
+ mat[(int)((phi*NMAT)/360)+NMAT/2][(int)((psi*NMAT)/360)+NMAT/2]
+ += omega;
+ }
+ }
+ if (bViol)
+ {
+ ffclose(gp);
+ }
+ ffclose(fp);
+ if (bOm)
+ {
+ sprintf(fn, "ramomega%s.xpm", dlist[i].name);
+ fp = ffopen(fn, "w");
+ lo = hi = 0;
+ for (j = 0; (j < NMAT); j++)
+ {
+ for (k = 0; (k < NMAT); k++)
+ {
+ mat[j][k] /= nf;
+ lo = min(mat[j][k], lo);
+ hi = max(mat[j][k], hi);
+ }
+ }
+ /* Symmetrise */
+ if (fabs(lo) > fabs(hi))
+ {
+ hi = -lo;
+ }
+ else
+ {
+ lo = -hi;
+ }
+ /* Add 180 */
+ for (j = 0; (j < NMAT); j++)
+ {
+ for (k = 0; (k < NMAT); k++)
+ {
+ mat[j][k] += 180;
+ }
+ }
+ lo += 180;
+ hi += 180;
+ nlevels = 20;
+ write_xpm3(fp, 0, "Omega/Ramachandran Plot", "Deg", "Phi", "Psi",
+ NMAT, NMAT, axis, axis, mat, lo, 180.0, hi, rlo, rmid, rhi, &nlevels);
+ ffclose(fp);
+ for (j = 0; (j < NMAT); j++)
+ {
+ sfree(mat[j]);
+ }
+ sfree(mat);
+ }
+ }
+ if ((has_dihedral(edChi1, &(dlist[i]))) &&
+ (has_dihedral(edChi2, &(dlist[i]))))
+ {
+ sprintf(fn, "ramaX1X2%s.xvg", dlist[i].name);
+ fp = rama_file(fn, "\\8c\\4\\s1\\N-\\8c\\4\\s2\\N Ramachandran Plot",
+ "\\8c\\4\\s1\\N (deg)", "\\8c\\4\\s2\\N (deg)", oenv);
+ Xi1 = dlist[i].j0[edChi1];
+ Xi2 = dlist[i].j0[edChi2];
+ for (j = 0; (j < nf); j++)
+ {
+ fprintf(fp, "%10g %10g\n", RAD2DEG*dih[Xi1][j], RAD2DEG*dih[Xi2][j]);
+ }
+ ffclose(fp);
+ }
+ else
+ {
+ fprintf(stderr, "No chi1 & chi2 angle for %s\n", dlist[i].name);
+ }
+ }
+}
+
+
+static void print_transitions(const char *fn, int maxchi, int nlist,
+ t_dlist dlist[], t_atoms *atoms, rvec x[],
+ matrix box, gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, real dt,
+ const output_env_t oenv)
+{
+ /* based on order_params below */
+ FILE *fp;
+ int nh[edMax];
+ int i, Dih, Xi;
+
+ /* must correspond with enum in pp2shift.h:38 */
+ char *leg[edMax];
+#define NLEG asize(leg)
+
+ leg[0] = strdup("Phi");
+ leg[1] = strdup("Psi");
+ leg[2] = strdup("Omega");
+ leg[3] = strdup("Chi1");
+ leg[4] = strdup("Chi2");
+ leg[5] = strdup("Chi3");
+ leg[6] = strdup("Chi4");
+ leg[7] = strdup("Chi5");
+ leg[8] = strdup("Chi6");
+
+ /* Print order parameters */
+ fp = xvgropen(fn, "Dihedral Rotamer Transitions", "Residue", "Transitions/ns",
+ oenv);
+ xvgr_legend(fp, NONCHI+maxchi, (const char**)leg, oenv);
+
+ for (Dih = 0; (Dih < edMax); Dih++)
+ {
+ nh[Dih] = 0;
+ }
+
+ fprintf(fp, "%5s ", "#Res.");
+ fprintf(fp, "%10s %10s %10s ", leg[edPhi], leg[edPsi], leg[edOmega]);
+ for (Xi = 0; Xi < maxchi; Xi++)
+ {
+ fprintf(fp, "%10s ", leg[NONCHI+Xi]);
+ }
+ fprintf(fp, "\n");
+
+ for (i = 0; (i < nlist); i++)
+ {
+ fprintf(fp, "%5d ", dlist[i].resnr);
+ for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
+ {
+ fprintf(fp, "%10.3f ", dlist[i].ntr[Dih]/dt);
+ }
+ /* fprintf(fp,"%12s\n",dlist[i].name); this confuses xmgrace */
+ fprintf(fp, "\n");
+ }
+ ffclose(fp);
+}
+
+static void order_params(FILE *log,
+ const char *fn, int maxchi, int nlist, t_dlist dlist[],
+ const char *pdbfn, real bfac_init,
+ t_atoms *atoms, rvec x[], int ePBC, matrix box,
+ gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, const output_env_t oenv)
+{
+ FILE *fp;
+ int nh[edMax];
+ char buf[STRLEN];
+ int i, Dih, Xi;
+ real S2Max, S2Min;
+
+ /* except for S2Min/Max, must correspond with enum in pp2shift.h:38 */
+ const char *const_leg[2+edMax] = {
+ "S2Min", "S2Max", "Phi", "Psi", "Omega",
+ "Chi1", "Chi2", "Chi3", "Chi4", "Chi5",
+ "Chi6"
+ };
+#define NLEG asize(leg)
+
+ char *leg[2+edMax];
+
+ for (i = 0; i < NLEG; i++)
+ {
+ leg[i] = strdup(const_leg[i]);
+ }
+
+ /* Print order parameters */
+ fp = xvgropen(fn, "Dihedral Order Parameters", "Residue", "S2", oenv);
+ xvgr_legend(fp, 2+NONCHI+maxchi, const_leg, oenv);
+
+ for (Dih = 0; (Dih < edMax); Dih++)
+ {
+ nh[Dih] = 0;
+ }
+
+ fprintf(fp, "%5s ", "#Res.");
+ fprintf(fp, "%10s %10s ", leg[0], leg[1]);
+ fprintf(fp, "%10s %10s %10s ", leg[2+edPhi], leg[2+edPsi], leg[2+edOmega]);
+ for (Xi = 0; Xi < maxchi; Xi++)
+ {
+ fprintf(fp, "%10s ", leg[2+NONCHI+Xi]);
+ }
+ fprintf(fp, "\n");
+
+ for (i = 0; (i < nlist); i++)
+ {
+ S2Max = -10;
+ S2Min = 10;
+ for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
+ {
+ if (dlist[i].S2[Dih] != 0)
+ {
+ if (dlist[i].S2[Dih] > S2Max)
+ {
+ S2Max = dlist[i].S2[Dih];
+ }
+ if (dlist[i].S2[Dih] < S2Min)
+ {
+ S2Min = dlist[i].S2[Dih];
+ }
+ }
+ if (dlist[i].S2[Dih] > 0.8)
+ {
+ nh[Dih]++;
+ }
+ }
+ fprintf(fp, "%5d ", dlist[i].resnr);
+ fprintf(fp, "%10.3f %10.3f ", S2Min, S2Max);
+ for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
+ {
+ fprintf(fp, "%10.3f ", dlist[i].S2[Dih]);
+ }
+ fprintf(fp, "\n");
+ /* fprintf(fp,"%12s\n",dlist[i].name); this confuses xmgrace */
+ }
+ ffclose(fp);
+
+ if (NULL != pdbfn)
+ {
+ real x0, y0, z0;
+
+ if (NULL == atoms->pdbinfo)
+ {
+ snew(atoms->pdbinfo, atoms->nr);
+ }
+ for (i = 0; (i < atoms->nr); i++)
+ {
+ atoms->pdbinfo[i].bfac = bfac_init;
+ }
+
+ for (i = 0; (i < nlist); i++)
+ {
+ atoms->pdbinfo[dlist[i].atm.N].bfac = -dlist[i].S2[0]; /* Phi */
+ atoms->pdbinfo[dlist[i].atm.H].bfac = -dlist[i].S2[0]; /* Phi */
+ atoms->pdbinfo[dlist[i].atm.C].bfac = -dlist[i].S2[1]; /* Psi */
+ atoms->pdbinfo[dlist[i].atm.O].bfac = -dlist[i].S2[1]; /* Psi */
+ for (Xi = 0; (Xi < maxchi); Xi++) /* Chi's */
+ {
+ if (dlist[i].atm.Cn[Xi+3] != -1)
+ {
+ atoms->pdbinfo[dlist[i].atm.Cn[Xi+1]].bfac = -dlist[i].S2[NONCHI+Xi];
+ }
+ }
+ }
+
+ fp = ffopen(pdbfn, "w");
+ fprintf(fp, "REMARK generated by g_chi\n");
+ fprintf(fp, "REMARK "
+ "B-factor field contains negative of dihedral order parameters\n");
+ write_pdbfile(fp, NULL, atoms, x, ePBC, box, ' ', 0, NULL, TRUE);
+ x0 = y0 = z0 = 1000.0;
+ for (i = 0; (i < atoms->nr); i++)
+ {
+ x0 = min(x0, x[i][XX]);
+ y0 = min(y0, x[i][YY]);
+ z0 = min(z0, x[i][ZZ]);
+ }
+ x0 *= 10.0; /* nm -> angstrom */
+ y0 *= 10.0; /* nm -> angstrom */
+ z0 *= 10.0; /* nm -> angstrom */
+ sprintf(buf, "%s%%6.f%%6.2f\n", get_pdbformat());
+ for (i = 0; (i < 10); i++)
+ {
+ fprintf(fp, buf, "ATOM ", atoms->nr+1+i, "CA", "LEG", ' ',
+ atoms->nres+1, ' ', x0, y0, z0+(1.2*i), 0.0, -0.1*i);
+ }
+ ffclose(fp);
+ }
+
+ fprintf(log, "Dihedrals with S2 > 0.8\n");
+ fprintf(log, "Dihedral: ");
+ if (bPhi)
+ {
+ fprintf(log, " Phi ");
+ }
+ if (bPsi)
+ {
+ fprintf(log, " Psi ");
+ }
+ if (bChi)
+ {
+ for (Xi = 0; (Xi < maxchi); Xi++)
+ {
+ fprintf(log, " %s ", leg[2+NONCHI+Xi]);
+ }
+ }
+ fprintf(log, "\nNumber: ");
+ if (bPhi)
+ {
+ fprintf(log, "%4d ", nh[0]);
+ }
+ if (bPsi)
+ {
+ fprintf(log, "%4d ", nh[1]);
+ }
+ if (bChi)
+ {
+ for (Xi = 0; (Xi < maxchi); Xi++)
+ {
+ fprintf(log, "%4d ", nh[NONCHI+Xi]);
+ }
+ }
+ fprintf(log, "\n");
+
+ for (i = 0; i < NLEG; i++)
+ {
+ sfree(leg[i]);
+ }
+
+}
+
+int gmx_chi(int argc, char *argv[])
+{
+ const char *desc[] = {
+ "[TT]g_chi[tt] computes [GRK]phi[grk], [GRK]psi[grk], [GRK]omega[grk], and [GRK]chi[grk] dihedrals for all your ",
+ "amino acid backbone and sidechains.",
+ "It can compute dihedral angle as a function of time, and as",
+ "histogram distributions.",
+ "The distributions [TT](histo-(dihedral)(RESIDUE).xvg[tt]) are cumulative over all residues of each type.[PAR]",
+ "If option [TT]-corr[tt] is given, the program will",
+ "calculate dihedral autocorrelation functions. The function used",
+ "is C(t) = [CHEVRON][COS][GRK]chi[grk]([GRK]tau[grk])[cos] [COS][GRK]chi[grk]([GRK]tau[grk]+t)[cos][chevron]. The use of cosines",
+ "rather than angles themselves, resolves the problem of periodicity.",
+ "(Van der Spoel & Berendsen (1997), Biophys. J. 72, 2032-2041).",
+ "Separate files for each dihedral of each residue",
+ "[TT](corr(dihedral)(RESIDUE)(nresnr).xvg[tt]) are output, as well as a",
+ "file containing the information for all residues (argument of [TT]-corr[tt]).[PAR]",
+ "With option [TT]-all[tt], the angles themselves as a function of time for",
+ "each residue are printed to separate files [TT](dihedral)(RESIDUE)(nresnr).xvg[tt].",
+ "These can be in radians or degrees.[PAR]",
+ "A log file (argument [TT]-g[tt]) is also written. This contains [BR]",
+ "(a) information about the number of residues of each type.[BR]",
+ "(b) The NMR ^3J coupling constants from the Karplus equation.[BR]",
+ "(c) a table for each residue of the number of transitions between ",
+ "rotamers per nanosecond, and the order parameter S^2 of each dihedral.[BR]",
+ "(d) a table for each residue of the rotamer occupancy.[PAR]",
+ "All rotamers are taken as 3-fold, except for [GRK]omega[grk] and [GRK]chi[grk] dihedrals",
+ "to planar groups (i.e. [GRK]chi[grk][SUB]2[sub] of aromatics, Asp and Asn; [GRK]chi[grk][SUB]3[sub] of Glu",
+ "and Gln; and [GRK]chi[grk][SUB]4[sub] of Arg), which are 2-fold. \"rotamer 0\" means ",
+ "that the dihedral was not in the core region of each rotamer. ",
+ "The width of the core region can be set with [TT]-core_rotamer[tt][PAR]",
+
+ "The S^2 order parameters are also output to an [TT].xvg[tt] file",
+ "(argument [TT]-o[tt] ) and optionally as a [TT].pdb[tt] file with",
+ "the S^2 values as B-factor (argument [TT]-p[tt]). ",
+ "The total number of rotamer transitions per timestep",
+ "(argument [TT]-ot[tt]), the number of transitions per rotamer",
+ "(argument [TT]-rt[tt]), and the ^3J couplings (argument [TT]-jc[tt]), ",
+ "can also be written to [TT].xvg[tt] files. Note that the analysis",
+ "of rotamer transitions assumes that the supplied trajectory frames",
+ "are equally spaced in time.[PAR]",
+
+ "If [TT]-chi_prod[tt] is set (and [TT]-maxchi[tt] > 0), cumulative rotamers, e.g.",
+ "1+9([GRK]chi[grk][SUB]1[sub]-1)+3([GRK]chi[grk][SUB]2[sub]-1)+([GRK]chi[grk][SUB]3[sub]-1) (if the residue has three 3-fold ",
+ "dihedrals and [TT]-maxchi[tt] >= 3)",
+ "are calculated. As before, if any dihedral is not in the core region,",
+ "the rotamer is taken to be 0. The occupancies of these cumulative ",
+ "rotamers (starting with rotamer 0) are written to the file",
+ "that is the argument of [TT]-cp[tt], and if the [TT]-all[tt] flag",
+ "is given, the rotamers as functions of time",
+ "are written to [TT]chiproduct(RESIDUE)(nresnr).xvg[tt] ",
+ "and their occupancies to [TT]histo-chiproduct(RESIDUE)(nresnr).xvg[tt].[PAR]",
+
+ "The option [TT]-r[tt] generates a contour plot of the average [GRK]omega[grk] angle",
+ "as a function of the [GRK]phi[grk] and [GRK]psi[grk] angles, that is, in a Ramachandran plot",
+ "the average [GRK]omega[grk] angle is plotted using color coding.",
+
+ };
+
+ const char *bugs[] = {
+ "Produces MANY output files (up to about 4 times the number of residues in the protein, twice that if autocorrelation functions are calculated). Typically several hundred files are output.",
+ "[GRK]phi[grk] and [GRK]psi[grk] dihedrals are calculated in a non-standard way, using H-N-CA-C for [GRK]phi[grk] instead of C(-)-N-CA-C, and N-CA-C-O for [GRK]psi[grk] instead of N-CA-C-N(+). This causes (usually small) discrepancies with the output of other tools like [TT]g_rama[tt].",
+ "[TT]-r0[tt] option does not work properly",
+ "Rotamers with multiplicity 2 are printed in [TT]chi.log[tt] as if they had multiplicity 3, with the 3rd (g(+)) always having probability 0"
+ };
+
+ /* defaults */
+ static int r0 = 1, ndeg = 1, maxchi = 2;
+ static gmx_bool bAll = FALSE;
+ static gmx_bool bPhi = FALSE, bPsi = FALSE, bOmega = FALSE;
+ static real bfac_init = -1.0, bfac_max = 0;
+ static const char *maxchistr[] = { NULL, "0", "1", "2", "3", "4", "5", "6", NULL };
+ static gmx_bool bRama = FALSE, bShift = FALSE, bViol = FALSE, bRamOmega = FALSE;
+ static gmx_bool bNormHisto = TRUE, bChiProduct = FALSE, bHChi = FALSE, bRAD = FALSE, bPBC = TRUE;
+ static real core_frac = 0.5;
+ t_pargs pa[] = {
+ { "-r0", FALSE, etINT, {&r0},
+ "starting residue" },
+ { "-phi", FALSE, etBOOL, {&bPhi},
+ "Output for [GRK]phi[grk] dihedral angles" },
+ { "-psi", FALSE, etBOOL, {&bPsi},
+ "Output for [GRK]psi[grk] dihedral angles" },
+ { "-omega", FALSE, etBOOL, {&bOmega},
+ "Output for [GRK]omega[grk] dihedrals (peptide bonds)" },
+ { "-rama", FALSE, etBOOL, {&bRama},
+ "Generate [GRK]phi[grk]/[GRK]psi[grk] and [GRK]chi[grk][SUB]1[sub]/[GRK]chi[grk][SUB]2[sub] Ramachandran plots" },
+ { "-viol", FALSE, etBOOL, {&bViol},
+ "Write a file that gives 0 or 1 for violated Ramachandran angles" },
+ { "-periodic", FALSE, etBOOL, {&bPBC},
+ "Print dihedral angles modulo 360 degrees" },
+ { "-all", FALSE, etBOOL, {&bAll},
+ "Output separate files for every dihedral." },
+ { "-rad", FALSE, etBOOL, {&bRAD},
+ "in angle vs time files, use radians rather than degrees."},
+ { "-shift", FALSE, etBOOL, {&bShift},
+ "Compute chemical shifts from [GRK]phi[grk]/[GRK]psi[grk] angles" },
+ { "-binwidth", FALSE, etINT, {&ndeg},
+ "bin width for histograms (degrees)" },
+ { "-core_rotamer", FALSE, etREAL, {&core_frac},
+ "only the central [TT]-core_rotamer[tt]*(360/multiplicity) belongs to each rotamer (the rest is assigned to rotamer 0)" },
+ { "-maxchi", FALSE, etENUM, {maxchistr},
+ "calculate first ndih [GRK]chi[grk] dihedrals" },
+ { "-normhisto", FALSE, etBOOL, {&bNormHisto},
+ "Normalize histograms" },
+ { "-ramomega", FALSE, etBOOL, {&bRamOmega},
+ "compute average omega as a function of [GRK]phi[grk]/[GRK]psi[grk] and plot it in an [TT].xpm[tt] plot" },
+ { "-bfact", FALSE, etREAL, {&bfac_init},
+ "B-factor value for [TT].pdb[tt] file for atoms with no calculated dihedral order parameter"},
+ { "-chi_prod", FALSE, etBOOL, {&bChiProduct},
+ "compute a single cumulative rotamer for each residue"},
+ { "-HChi", FALSE, etBOOL, {&bHChi},
+ "Include dihedrals to sidechain hydrogens"},
+ { "-bmax", FALSE, etREAL, {&bfac_max},
+ "Maximum B-factor on any of the atoms that make up a dihedral, for the dihedral angle to be considere in the statistics. Applies to database work where a number of X-Ray structures is analyzed. [TT]-bmax[tt] <= 0 means no limit." }
+ };
+
+ FILE *log;
+ int natoms, nlist, idum, nbin;
+ t_atoms atoms;
+ rvec *x;
+ int ePBC;
+ matrix box;
+ char title[256], grpname[256];
+ t_dlist *dlist;
+ gmx_bool bChi, bCorr, bSSHisto;
+ gmx_bool bDo_rt, bDo_oh, bDo_ot, bDo_jc;
+ real dt = 0, traj_t_ns;
+ output_env_t oenv;
+ gmx_residuetype_t rt;
+
+ atom_id isize, *index;
+ int ndih, nactdih, nf;
+ real **dih, *trans_frac, *aver_angle, *time;
+ int i, j, **chi_lookup, *multiplicity;
+
+ t_filenm fnm[] = {
+ { efSTX, "-s", NULL, ffREAD },
+ { efTRX, "-f", NULL, ffREAD },
+ { efXVG, "-o", "order", ffWRITE },
+ { efPDB, "-p", "order", ffOPTWR },
+ { efDAT, "-ss", "ssdump", ffOPTRD },
+ { efXVG, "-jc", "Jcoupling", ffWRITE },
+ { efXVG, "-corr", "dihcorr", ffOPTWR },
+ { efLOG, "-g", "chi", ffWRITE },
+ /* add two more arguments copying from g_angle */
+ { efXVG, "-ot", "dihtrans", ffOPTWR },
+ { efXVG, "-oh", "trhisto", ffOPTWR },
+ { efXVG, "-rt", "restrans", ffOPTWR },
+ { efXVG, "-cp", "chiprodhisto", ffOPTWR }
+ };
+#define NFILE asize(fnm)
+ int npargs;
+ t_pargs *ppa;
+
+ npargs = asize(pa);
+ ppa = add_acf_pargs(&npargs, pa);
+ parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
+ NFILE, fnm, npargs, ppa, asize(desc), desc, asize(bugs), bugs,
+ &oenv);
+
+ /* Handle result from enumerated type */
+ sscanf(maxchistr[0], "%d", &maxchi);
+ bChi = (maxchi > 0);
+
+ log = ffopen(ftp2fn(efLOG, NFILE, fnm), "w");
+
+ if (bRamOmega)
+ {
+ bOmega = TRUE;
+ bPhi = TRUE;
+ bPsi = TRUE;
+ }
+
+ /* set some options */
+ bDo_rt = (opt2bSet("-rt", NFILE, fnm));
+ bDo_oh = (opt2bSet("-oh", NFILE, fnm));
+ bDo_ot = (opt2bSet("-ot", NFILE, fnm));
+ bDo_jc = (opt2bSet("-jc", NFILE, fnm));
+ bCorr = (opt2bSet("-corr", NFILE, fnm));
+ if (bCorr)
+ {
+ fprintf(stderr, "Will calculate autocorrelation\n");
+ }
+
+ if (core_frac > 1.0)
+ {
+ fprintf(stderr, "core_rotamer fraction > 1.0 ; will use 1.0\n");
+ core_frac = 1.0;
+ }
+ if (core_frac < 0.0)
+ {
+ fprintf(stderr, "core_rotamer fraction < 0.0 ; will use 0.0\n");
+ core_frac = 0.0;
+ }
+
+ if (maxchi > MAXCHI)
+ {
+ fprintf(stderr,
+ "Will only calculate first %d Chi dihedrals in stead of %d.\n",
+ MAXCHI, maxchi);
+ maxchi = MAXCHI;
+ }
+ bSSHisto = ftp2bSet(efDAT, NFILE, fnm);
+ nbin = 360/ndeg;
+
+ /* Find the chi angles using atoms struct and a list of amino acids */
+ get_stx_coordnum(ftp2fn(efSTX, NFILE, fnm), &natoms);
+ init_t_atoms(&atoms, natoms, TRUE);
+ snew(x, natoms);
+ read_stx_conf(ftp2fn(efSTX, NFILE, fnm), title, &atoms, x, NULL, &ePBC, box);
+ fprintf(log, "Title: %s\n", title);
+
+ gmx_residuetype_init(&rt);
+ dlist = mk_dlist(log, &atoms, &nlist, bPhi, bPsi, bChi, bHChi, maxchi, r0, rt);
+ fprintf(stderr, "%d residues with dihedrals found\n", nlist);
+
+ if (nlist == 0)
+ {
+ gmx_fatal(FARGS, "No dihedrals in your structure!\n");
+ }
+
+ /* Make a linear index for reading all. */
+ index = make_chi_ind(nlist, dlist, &ndih);
+ isize = 4*ndih;
+ fprintf(stderr, "%d dihedrals found\n", ndih);
+
+ snew(dih, ndih);
+
+ /* COMPUTE ALL DIHEDRALS! */
+ read_ang_dih(ftp2fn(efTRX, NFILE, fnm), FALSE, TRUE, FALSE, bPBC, 1, &idum,
+ &nf, &time, isize, index, &trans_frac, &aver_angle, dih, oenv);
+
+ dt = (time[nf-1]-time[0])/(nf-1); /* might want this for corr or n. transit*/
+ if (bCorr)
+ {
+ if (nf < 2)
+ {
+ gmx_fatal(FARGS, "Need at least 2 frames for correlation");
+ }
+ }
+
+ /* put angles in -M_PI to M_PI ! and correct phase factor for phi and psi
+ * pass nactdih instead of ndih to low_ana_dih_trans and get_chi_product_traj
+ * to prevent accessing off end of arrays when maxchi < 5 or 6. */
+ nactdih = reset_em_all(nlist, dlist, nf, dih, maxchi);
+
+ if (bAll)
+ {
+ dump_em_all(nlist, dlist, nf, time, dih, maxchi, bPhi, bPsi, bChi, bOmega, bRAD, oenv);
+ }
+
+ /* Histogramming & J coupling constants & calc of S2 order params */
+ histogramming(log, nbin, rt, nf, maxchi, dih, nlist, dlist, index,
+ bPhi, bPsi, bOmega, bChi,
+ bNormHisto, bSSHisto, ftp2fn(efDAT, NFILE, fnm), bfac_max, &atoms,
+ bDo_jc, opt2fn("-jc", NFILE, fnm), oenv);
+
+ /* transitions
+ *
+ * added multiplicity */
+
+ snew(multiplicity, ndih);
+ mk_multiplicity_lookup(multiplicity, maxchi, dih, nlist, dlist, ndih);
+
+ strcpy(grpname, "All residues, ");
+ if (bPhi)
+ {
+ strcat(grpname, "Phi ");
+ }
+ if (bPsi)
+ {
+ strcat(grpname, "Psi ");
+ }
+ if (bOmega)
+ {
+ strcat(grpname, "Omega ");
+ }
+ if (bChi)
+ {
+ strcat(grpname, "Chi 1-");
+ sprintf(grpname + strlen(grpname), "%i", maxchi);
+ }
+
+
+ low_ana_dih_trans(bDo_ot, opt2fn("-ot", NFILE, fnm),
+ bDo_oh, opt2fn("-oh", NFILE, fnm), maxchi,
+ dih, nlist, dlist, nf, nactdih, grpname, multiplicity,
+ time, FALSE, core_frac, oenv);
+
+ /* Order parameters */
+ order_params(log, opt2fn("-o", NFILE, fnm), maxchi, nlist, dlist,
+ ftp2fn_null(efPDB, NFILE, fnm), bfac_init,
+ &atoms, x, ePBC, box, bPhi, bPsi, bChi, oenv);
+
+ /* Print ramachandran maps! */
+ if (bRama)
+ {
+ do_rama(nf, nlist, dlist, dih, bViol, bRamOmega, oenv);
+ }
+
+ if (bShift)
+ {
+ do_pp2shifts(log, nf, nlist, dlist, dih);
+ }
+
+ /* rprint S^2, transitions, and rotamer occupancies to log */
+ traj_t_ns = 0.001 * (time[nf-1]-time[0]);
+ pr_dlist(log, nlist, dlist, traj_t_ns, edPrintST, bPhi, bPsi, bChi, bOmega, maxchi);
+ pr_dlist(log, nlist, dlist, traj_t_ns, edPrintRO, bPhi, bPsi, bChi, bOmega, maxchi);
+ ffclose(log);
+ /* transitions to xvg */
+ if (bDo_rt)
+ {
+ print_transitions(opt2fn("-rt", NFILE, fnm), maxchi, nlist, dlist,
+ &atoms, x, box, bPhi, bPsi, bChi, traj_t_ns, oenv);
+ }
+
+ /* chi_product trajectories (ie one "rotamer number" for each residue) */
+ if (bChiProduct && bChi)
+ {
+ snew(chi_lookup, nlist);
+ for (i = 0; i < nlist; i++)
+ {
+ snew(chi_lookup[i], maxchi);
+ }
+ mk_chi_lookup(chi_lookup, maxchi, dih, nlist, dlist);
+
+ get_chi_product_traj(dih, nf, nactdih, nlist,
+ maxchi, dlist, time, chi_lookup, multiplicity,
+ FALSE, bNormHisto, core_frac, bAll,
+ opt2fn("-cp", NFILE, fnm), oenv);
+
+ for (i = 0; i < nlist; i++)
+ {
+ sfree(chi_lookup[i]);
+ }
+ }
+
+ /* Correlation comes last because it fucks up the angles */
+ if (bCorr)
+ {
+ do_dihcorr(opt2fn("-corr", NFILE, fnm), nf, ndih, dih, dt, nlist, dlist, time,
+ maxchi, bPhi, bPsi, bChi, bOmega, oenv);
+ }
+
+
+ do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
+ do_view(oenv, opt2fn("-jc", NFILE, fnm), "-nxy");
+ if (bCorr)
+ {
+ do_view(oenv, opt2fn("-corr", NFILE, fnm), "-nxy");
+ }
+
+ gmx_residuetype_destroy(rt);
+
+ thanx(stderr);
+
+ return 0;
+}
--- /dev/null
- "[TT]g_gyrate[tt] computes the radius of gyration of a group of atoms",
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Green Red Orange Magenta Azure Cyan Skyblue
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include "statutil.h"
+#include "sysstuff.h"
+#include "typedefs.h"
+#include "smalloc.h"
+#include "macros.h"
+#include "vec.h"
+#include "pbc.h"
+#include "copyrite.h"
+#include "futil.h"
+#include "statutil.h"
+#include "index.h"
+#include "mshift.h"
+#include "xvgr.h"
+#include "princ.h"
+#include "rmpbc.h"
+#include "txtdump.h"
+#include "tpxio.h"
+#include "gstat.h"
+#include "gmx_ana.h"
+
+
+real calc_gyro(rvec x[], int gnx, atom_id index[], t_atom atom[], real tm,
+ rvec gvec, rvec d, gmx_bool bQ, gmx_bool bRot, gmx_bool bMOI, matrix trans)
+{
+ int i, ii, m;
+ real gyro, dx2, m0, Itot;
+ rvec comp;
+
+ if (bRot)
+ {
+ principal_comp(gnx, index, atom, x, trans, d);
+ Itot = norm(d);
+ if (bMOI)
+ {
+ return Itot;
+ }
+ for (m = 0; (m < DIM); m++)
+ {
+ d[m] = sqrt(d[m]/tm);
+ }
+#ifdef DEBUG
+ pr_rvecs(stderr, 0, "trans", trans, DIM);
+#endif
+ /* rotate_atoms(gnx,index,x,trans); */
+ }
+ clear_rvec(comp);
+ for (i = 0; (i < gnx); i++)
+ {
+ ii = index[i];
+ if (bQ)
+ {
+ m0 = fabs(atom[ii].q);
+ }
+ else
+ {
+ m0 = atom[ii].m;
+ }
+ for (m = 0; (m < DIM); m++)
+ {
+ dx2 = x[ii][m]*x[ii][m];
+ comp[m] += dx2*m0;
+ }
+ }
+ gyro = comp[XX]+comp[YY]+comp[ZZ];
+
+ for (m = 0; (m < DIM); m++)
+ {
+ gvec[m] = sqrt((gyro-comp[m])/tm);
+ }
+
+ return sqrt(gyro/tm);
+}
+
+void calc_gyro_z(rvec x[], matrix box,
+ int gnx, atom_id index[], t_atom atom[],
+ int nz, real time, FILE *out)
+{
+ static dvec *inertia = NULL;
+ static double *tm = NULL;
+ int i, ii, j, zi;
+ real zf, w, sdet, e1, e2;
+
+ if (inertia == NULL)
+ {
+ snew(inertia, nz);
+ snew(tm, nz);
+ }
+
+ for (i = 0; i < nz; i++)
+ {
+ clear_dvec(inertia[i]);
+ tm[i] = 0;
+ }
+
+ for (i = 0; (i < gnx); i++)
+ {
+ ii = index[i];
+ zf = nz*x[ii][ZZ]/box[ZZ][ZZ];
+ if (zf >= nz)
+ {
+ zf -= nz;
+ }
+ if (zf < 0)
+ {
+ zf += nz;
+ }
+ for (j = 0; j < 2; j++)
+ {
+ zi = zf + j;
+ if (zi == nz)
+ {
+ zi = 0;
+ }
+ w = atom[ii].m*(1 + cos(M_PI*(zf - zi)));
+ inertia[zi][0] += w*sqr(x[ii][YY]);
+ inertia[zi][1] += w*sqr(x[ii][XX]);
+ inertia[zi][2] -= w*x[ii][XX]*x[ii][YY];
+ tm[zi] += w;
+ }
+ }
+ fprintf(out, "%10g", time);
+ for (j = 0; j < nz; j++)
+ {
+ for (i = 0; i < 3; i++)
+ {
+ inertia[j][i] /= tm[j];
+ }
+ sdet = sqrt(sqr(inertia[j][0] - inertia[j][1]) + 4*sqr(inertia[j][2]));
+ e1 = sqrt(0.5*(inertia[j][0] + inertia[j][1] + sdet));
+ e2 = sqrt(0.5*(inertia[j][0] + inertia[j][1] - sdet));
+ fprintf(out, " %5.3f %5.3f", e1, e2);
+ }
+ fprintf(out, "\n");
+}
+
+int gmx_gyrate(int argc, char *argv[])
+{
+ const char *desc[] = {
++ "[TT]g_gyrate[tt] computes the radius of gyration of a molecule",
+ "and the radii of gyration about the [IT]x[it]-, [IT]y[it]- and [IT]z[it]-axes,",
+ "as a function of time. The atoms are explicitly mass weighted.[PAR]",
+ "With the [TT]-nmol[tt] option the radius of gyration will be calculated",
+ "for multiple molecules by splitting the analysis group in equally",
+ "sized parts.[PAR]",
+ "With the option [TT]-nz[tt] 2D radii of gyration in the [IT]x-y[it] plane",
+ "of slices along the [IT]z[it]-axis are calculated."
+ };
+ static int nmol = 1, nz = 0;
+ static gmx_bool bQ = FALSE, bRot = FALSE, bMOI = FALSE;
+ t_pargs pa[] = {
+ { "-nmol", FALSE, etINT, {&nmol},
+ "The number of molecules to analyze" },
+ { "-q", FALSE, etBOOL, {&bQ},
+ "Use absolute value of the charge of an atom as weighting factor instead of mass" },
+ { "-p", FALSE, etBOOL, {&bRot},
+ "Calculate the radii of gyration about the principal axes." },
+ { "-moi", FALSE, etBOOL, {&bMOI},
+ "Calculate the moments of inertia (defined by the principal axes)." },
+ { "-nz", FALSE, etINT, {&nz},
+ "Calculate the 2D radii of gyration of this number of slices along the z-axis" },
+ };
+ FILE *out;
+ t_trxstatus *status;
+ t_topology top;
+ int ePBC;
+ rvec *x, *x_s;
+ rvec xcm, gvec, gvec1;
+ matrix box, trans;
+ gmx_bool bACF;
+ real **moi_trans = NULL;
+ int max_moi = 0, delta_moi = 100;
+ rvec d, d1; /* eigenvalues of inertia tensor */
+ real t, t0, tm, gyro;
+ int natoms;
+ char *grpname, title[256];
+ int i, j, m, gnx, nam, mol;
+ atom_id *index;
+ output_env_t oenv;
+ gmx_rmpbc_t gpbc = NULL;
+ const char *leg[] = { "Rg", "RgX", "RgY", "RgZ" };
+ const char *legI[] = { "Itot", "I1", "I2", "I3" };
+#define NLEG asize(leg)
+ t_filenm fnm[] = {
+ { efTRX, "-f", NULL, ffREAD },
+ { efTPS, NULL, NULL, ffREAD },
+ { efNDX, NULL, NULL, ffOPTRD },
+ { efXVG, NULL, "gyrate", ffWRITE },
+ { efXVG, "-acf", "moi-acf", ffOPTWR },
+ };
+#define NFILE asize(fnm)
+ int npargs;
+ t_pargs *ppa;
+
+ npargs = asize(pa);
+ ppa = add_acf_pargs(&npargs, pa);
+
+ parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW | PCA_BE_NICE,
+ NFILE, fnm, npargs, ppa, asize(desc), desc, 0, NULL, &oenv);
+ bACF = opt2bSet("-acf", NFILE, fnm);
+ if (bACF && nmol != 1)
+ {
+ gmx_fatal(FARGS, "Can only do acf with nmol=1");
+ }
+ bRot = bRot || bMOI || bACF;
+ /*
+ if (nz > 0)
+ bMOI = TRUE;
+ */
+ if (bRot)
+ {
+ printf("Will rotate system along principal axes\n");
+ snew(moi_trans, DIM);
+ }
+ if (bMOI)
+ {
+ printf("Will print moments of inertia\n");
+ bQ = FALSE;
+ }
+ if (bQ)
+ {
+ printf("Will print radius normalised by charge\n");
+ }
+
+ read_tps_conf(ftp2fn(efTPS, NFILE, fnm), title, &top, &ePBC, &x, NULL, box, TRUE);
+ get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &gnx, &index, &grpname);
+
+ if (nmol > gnx || gnx % nmol != 0)
+ {
+ gmx_fatal(FARGS, "The number of atoms in the group (%d) is not a multiple of nmol (%d)", gnx, nmol);
+ }
+ nam = gnx/nmol;
+
+ natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
+ snew(x_s, natoms);
+
+ j = 0;
+ t0 = t;
+ if (bQ)
+ {
+ out = xvgropen(ftp2fn(efXVG, NFILE, fnm),
+ "Radius of Charge", "Time (ps)", "Rg (nm)", oenv);
+ }
+ else if (bMOI)
+ {
+ out = xvgropen(ftp2fn(efXVG, NFILE, fnm),
+ "Moments of inertia", "Time (ps)", "I (a.m.u. nm\\S2\\N)", oenv);
+ }
+ else
+ {
+ out = xvgropen(ftp2fn(efXVG, NFILE, fnm),
+ "Radius of gyration", "Time (ps)", "Rg (nm)", oenv);
+ }
+ if (bMOI)
+ {
+ xvgr_legend(out, NLEG, legI, oenv);
+ }
+ else
+ {
+ if (bRot)
+ {
+ if (output_env_get_print_xvgr_codes(oenv))
+ {
+ fprintf(out, "@ subtitle \"Axes are principal component axes\"\n");
+ }
+ }
+ xvgr_legend(out, NLEG, leg, oenv);
+ }
+ if (nz == 0)
+ {
+ gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms, box);
+ }
+ do
+ {
+ if (nz == 0)
+ {
+ gmx_rmpbc_copy(gpbc, natoms, box, x, x_s);
+ }
+ gyro = 0;
+ clear_rvec(gvec);
+ clear_rvec(d);
+ for (mol = 0; mol < nmol; mol++)
+ {
+ tm = sub_xcm(nz == 0 ? x_s : x, nam, index+mol*nam, top.atoms.atom, xcm, bQ);
+ if (nz == 0)
+ {
+ gyro += calc_gyro(x_s, nam, index+mol*nam, top.atoms.atom,
+ tm, gvec1, d1, bQ, bRot, bMOI, trans);
+ }
+ else
+ {
+ calc_gyro_z(x, box, nam, index+mol*nam, top.atoms.atom, nz, t, out);
+ }
+ rvec_inc(gvec, gvec1);
+ rvec_inc(d, d1);
+ }
+ if (nmol > 0)
+ {
+ gyro /= nmol;
+ svmul(1.0/nmol, gvec, gvec);
+ svmul(1.0/nmol, d, d);
+ }
+
+ if (nz == 0)
+ {
+ if (bRot)
+ {
+ if (j >= max_moi)
+ {
+ max_moi += delta_moi;
+ for (m = 0; (m < DIM); m++)
+ {
+ srenew(moi_trans[m], max_moi*DIM);
+ }
+ }
+ for (m = 0; (m < DIM); m++)
+ {
+ copy_rvec(trans[m], moi_trans[m]+DIM*j);
+ }
+ fprintf(out, "%10g %10g %10g %10g %10g\n",
+ t, gyro, d[XX], d[YY], d[ZZ]);
+ }
+ else
+ {
+ fprintf(out, "%10g %10g %10g %10g %10g\n",
+ t, gyro, gvec[XX], gvec[YY], gvec[ZZ]);
+ }
+ }
+ j++;
+ }
+ while (read_next_x(oenv, status, &t, natoms, x, box));
+ close_trj(status);
+ if (nz == 0)
+ {
+ gmx_rmpbc_done(gpbc);
+ }
+
+ ffclose(out);
+
+ if (bACF)
+ {
+ int mode = eacVector;
+
+ do_autocorr(opt2fn("-acf", NFILE, fnm), oenv,
+ "Moment of inertia vector ACF",
+ j, 3, moi_trans, (t-t0)/j, mode, FALSE);
+ do_view(oenv, opt2fn("-acf", NFILE, fnm), "-nxy");
+ }
+
+ do_view(oenv, ftp2fn(efXVG, NFILE, fnm), "-nxy");
+
+ thanx(stderr);
+
+ return 0;
+}
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef GMX_THREAD_MPI
+#include <thread_mpi.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "typedefs.h"
+#include "txtdump.h"
+#include "smalloc.h"
+#include "ns.h"
+#include "vec.h"
+#include "maths.h"
+#include "macros.h"
+#include "string2.h"
+#include "force.h"
+#include "names.h"
+#include "main.h"
+#include "xvgr.h"
+#include "gmx_fatal.h"
+#include "physics.h"
+#include "force.h"
+#include "bondf.h"
+#include "nrnb.h"
+#include "smalloc.h"
+#include "nonbonded.h"
+
+#include "nb_kernel.h"
+#include "nb_free_energy.h"
+#include "nb_generic.h"
+#include "nb_generic_cg.h"
+#include "nb_generic_adress.h"
+
+/* Different default (c) and accelerated interaction-specific kernels */
+#include "nb_kernel_c/nb_kernel_c.h"
+
+#if (defined GMX_CPU_ACCELERATION_X86_SSE2) && !(defined GMX_DOUBLE)
+# include "nb_kernel_sse2_single/nb_kernel_sse2_single.h"
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_SSE4_1) && !(defined GMX_DOUBLE)
+# include "nb_kernel_sse4_1_single/nb_kernel_sse4_1_single.h"
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_128_FMA) && !(defined GMX_DOUBLE)
+# include "nb_kernel_avx_128_fma_single/nb_kernel_avx_128_fma_single.h"
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_256) && !(defined GMX_DOUBLE)
+# include "nb_kernel_avx_256_single/nb_kernel_avx_256_single.h"
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_SSE2 && defined GMX_DOUBLE)
+# include "nb_kernel_sse2_double/nb_kernel_sse2_double.h"
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_SSE4_1 && defined GMX_DOUBLE)
+# include "nb_kernel_sse4_1_double/nb_kernel_sse4_1_double.h"
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_128_FMA && defined GMX_DOUBLE)
+# include "nb_kernel_avx_128_fma_double/nb_kernel_avx_128_fma_double.h"
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_256 && defined GMX_DOUBLE)
+# include "nb_kernel_avx_256_double/nb_kernel_avx_256_double.h"
+#endif
+#if (defined GMX_CPU_ACCELERATION_SPARC64_HPC_ACE && defined GMX_DOUBLE)
+# include "nb_kernel_sparc64_hpc_ace_double/nb_kernel_sparc64_hpc_ace_double.h"
+#endif
+
+
+#ifdef GMX_THREAD_MPI
+static tMPI_Thread_mutex_t nonbonded_setup_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
+#endif
+static gmx_bool nonbonded_setup_done = FALSE;
+
+
+void
+gmx_nonbonded_setup(FILE * fplog,
+ t_forcerec * fr,
+ gmx_bool bGenericKernelOnly)
+{
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_lock(&nonbonded_setup_mutex);
+#endif
+ /* Here we are guaranteed only one thread made it. */
+ if (nonbonded_setup_done == FALSE)
+ {
+ if (bGenericKernelOnly == FALSE)
+ {
+ /* Add the generic kernels to the structure stored statically in nb_kernel.c */
+ nb_kernel_list_add_kernels(kernellist_c, kernellist_c_size);
+
+ if (!(fr != NULL && fr->use_cpu_acceleration == FALSE))
+ {
+ /* Add interaction-specific kernels for different architectures */
+ /* Single precision */
+#if (defined GMX_CPU_ACCELERATION_X86_SSE2) && !(defined GMX_DOUBLE)
+ nb_kernel_list_add_kernels(kernellist_sse2_single, kernellist_sse2_single_size);
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_SSE4_1) && !(defined GMX_DOUBLE)
+ nb_kernel_list_add_kernels(kernellist_sse4_1_single, kernellist_sse4_1_single_size);
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_128_FMA) && !(defined GMX_DOUBLE)
+ nb_kernel_list_add_kernels(kernellist_avx_128_fma_single, kernellist_avx_128_fma_single_size);
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_256) && !(defined GMX_DOUBLE)
+ nb_kernel_list_add_kernels(kernellist_avx_256_single, kernellist_avx_256_single_size);
+#endif
+ /* Double precision */
+#if (defined GMX_CPU_ACCELERATION_X86_SSE2 && defined GMX_DOUBLE)
+ nb_kernel_list_add_kernels(kernellist_sse2_double, kernellist_sse2_double_size);
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_SSE4_1 && defined GMX_DOUBLE)
+ nb_kernel_list_add_kernels(kernellist_sse4_1_double, kernellist_sse4_1_double_size);
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_128_FMA && defined GMX_DOUBLE)
+ nb_kernel_list_add_kernels(kernellist_avx_128_fma_double, kernellist_avx_128_fma_double_size);
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_256 && defined GMX_DOUBLE)
+ nb_kernel_list_add_kernels(kernellist_avx_256_double, kernellist_avx_256_double_size);
+#endif
+#if (defined GMX_CPU_ACCELERATION_SPARC64_HPC_ACE && defined GMX_DOUBLE)
+ nb_kernel_list_add_kernels(kernellist_sparc64_hpc_ace_double,kernellist_sparc64_hpc_ace_double_size);
+#endif
+ ; /* empty statement to avoid a completely empty block */
+ }
+ }
+ /* Create a hash for faster lookups */
+ nb_kernel_list_hash_init();
+
+ nonbonded_setup_done = TRUE;
+ }
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_unlock(&nonbonded_setup_mutex);
+#endif
+}
+
+
+
+void
+gmx_nonbonded_set_kernel_pointers(FILE *log, t_nblist *nl)
+{
+ const char * elec;
+ const char * elec_mod;
+ const char * vdw;
+ const char * vdw_mod;
+ const char * geom;
+ const char * other;
+ const char * vf;
+
+ struct
+ {
+ const char * arch;
+ int simd_padding_width;
+ }
+ arch_and_padding[] =
+ {
+ /* Single precision */
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_256) && !(defined GMX_DOUBLE)
+ { "avx_256_single", 8 },
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_128_FMA) && !(defined GMX_DOUBLE)
+ { "avx_128_fma_single", 4 },
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_SSE4_1) && !(defined GMX_DOUBLE)
+ { "sse4_1_single", 4 },
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_SSE2) && !(defined GMX_DOUBLE)
+ { "sse2_single", 4 },
+#endif
+ /* Double precision */
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_256 && defined GMX_DOUBLE)
+ { "avx_256_double", 4 },
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_AVX_128_FMA && defined GMX_DOUBLE)
+ /* Sic. Double precision 2-way SIMD does not require neighbor list padding,
+ * since the kernels execute a loop unrolled a factor 2, followed by
+ * a possible single odd-element epilogue.
+ */
+ { "avx_128_fma_double", 1 },
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_SSE2 && defined GMX_DOUBLE)
+ /* No padding - see comment above */
+ { "sse2_double", 1 },
+#endif
+#if (defined GMX_CPU_ACCELERATION_X86_SSE4_1 && defined GMX_DOUBLE)
+ /* No padding - see comment above */
+ { "sse4_1_double", 1 },
+#endif
+#if (defined GMX_CPU_ACCELERATION_SPARC64_HPC_ACE && defined GMX_DOUBLE)
+ /* No padding - see comment above */
+ { "sparc64_hpc_ace_double", 1 },
+#endif
+ { "c", 1 },
+ };
+ int narch = asize(arch_and_padding);
+ int i;
+
+ if (nonbonded_setup_done == FALSE)
+ {
+ /* We typically call this setup routine before starting timers,
+ * but if that has not been done for whatever reason we do it now.
+ */
+ gmx_nonbonded_setup(NULL, NULL, FALSE);
+ }
+
+ /* Not used yet */
+ other = "";
+
+ nl->kernelptr_vf = NULL;
+ nl->kernelptr_v = NULL;
+ nl->kernelptr_f = NULL;
+
+ elec = gmx_nbkernel_elec_names[nl->ielec];
+ elec_mod = eintmod_names[nl->ielecmod];
+ vdw = gmx_nbkernel_vdw_names[nl->ivdw];
+ vdw_mod = eintmod_names[nl->ivdwmod];
+ geom = gmx_nblist_geometry_names[nl->igeometry];
+
+ if (nl->type == GMX_NBLIST_INTERACTION_ADRESS)
+ {
+ nl->kernelptr_vf = (void *) gmx_nb_generic_adress_kernel;
+ nl->kernelptr_f = (void *) gmx_nb_generic_adress_kernel;
+ nl->simd_padding_width = 1;
+ return;
+ }
+
+ if (nl->type == GMX_NBLIST_INTERACTION_FREE_ENERGY)
+ {
+ nl->kernelptr_vf = (void *) gmx_nb_free_energy_kernel;
+ nl->kernelptr_f = (void *) gmx_nb_free_energy_kernel;
+ nl->simd_padding_width = 1;
+ }
+ else if (!gmx_strcasecmp_min(geom, "CG-CG"))
+ {
+ nl->kernelptr_vf = (void *) gmx_nb_generic_cg_kernel;
+ nl->kernelptr_f = (void *) gmx_nb_generic_cg_kernel;
+ nl->simd_padding_width = 1;
+ }
+ else
+ {
+ /* Try to find a specific kernel first */
+
+ for (i = 0; i < narch && nl->kernelptr_vf == NULL; i++)
+ {
+ nl->kernelptr_vf = (void *) nb_kernel_list_findkernel(log, arch_and_padding[i].arch, elec, elec_mod, vdw, vdw_mod, geom, other, "PotentialAndForce");
+ nl->simd_padding_width = arch_and_padding[i].simd_padding_width;
+ }
+ for (i = 0; i < narch && nl->kernelptr_f == NULL; i++)
+ {
+ nl->kernelptr_f = (void *) nb_kernel_list_findkernel(log, arch_and_padding[i].arch, elec, elec_mod, vdw, vdw_mod, geom, other, "Force");
+ nl->simd_padding_width = arch_and_padding[i].simd_padding_width;
+
+ /* If there is not force-only optimized kernel, is there a potential & force one? */
+ if (nl->kernelptr_f == NULL)
+ {
+ nl->kernelptr_f = (void *) nb_kernel_list_findkernel(NULL, arch_and_padding[i].arch, elec, elec_mod, vdw, vdw_mod, geom, other, "PotentialAndForce");
+ nl->simd_padding_width = arch_and_padding[i].simd_padding_width;
+ }
+ }
+
+ /* Give up, pick a generic one instead */
+ if (nl->kernelptr_vf == NULL)
+ {
+ nl->kernelptr_vf = (void *) gmx_nb_generic_kernel;
+ nl->kernelptr_f = (void *) gmx_nb_generic_kernel;
+ nl->simd_padding_width = 1;
+ if (debug)
+ {
+ fprintf(debug,
+ "WARNING - Slow generic NB kernel used for neighborlist with\n"
+ " Elec: '%s', Modifier: '%s'\n"
+ " Vdw: '%s', Modifier: '%s'\n"
+ " Geom: '%s', Other: '%s'\n\n",
+ elec, elec_mod, vdw, vdw_mod, geom, other);
+ }
+ }
+ }
+
+ return;
+}
+
+void do_nonbonded(t_commrec *cr, t_forcerec *fr,
+ rvec x[], rvec f_shortrange[], rvec f_longrange[], t_mdatoms *mdatoms, t_blocka *excl,
+ gmx_grppairener_t *grppener, rvec box_size,
+ t_nrnb *nrnb, real *lambda, real *dvdl,
+ int nls, int eNL, int flags)
+{
+ t_nblist * nlist;
+ int n, n0, n1, i, i0, i1, sz, range;
+ t_nblists * nblists;
+ nb_kernel_data_t kernel_data;
+ nb_kernel_t * kernelptr = NULL;
+ rvec * f;
+
+ kernel_data.flags = flags;
+ kernel_data.exclusions = excl;
+ kernel_data.lambda = lambda;
+ kernel_data.dvdl = dvdl;
+
+ if (fr->bAllvsAll)
+ {
++ gmx_incons("All-vs-all kernels have not been implemented in version 4.6");
+ return;
+ }
+
+ if (eNL >= 0)
+ {
+ i0 = eNL;
+ i1 = i0+1;
+ }
+ else
+ {
+ i0 = 0;
+ i1 = eNL_NR;
+ }
+
+ if (nls >= 0)
+ {
+ n0 = nls;
+ n1 = nls+1;
+ }
+ else
+ {
+ n0 = 0;
+ n1 = fr->nnblists;
+ }
+
+ for (n = n0; (n < n1); n++)
+ {
+ nblists = &fr->nblists[n];
+
+ kernel_data.table_elec = &nblists->table_elec;
+ kernel_data.table_vdw = &nblists->table_vdw;
+ kernel_data.table_elec_vdw = &nblists->table_elec_vdw;
+
+ for (range = 0; range < 2; range++)
+ {
+ /* Are we doing short/long-range? */
+ if (range == 0)
+ {
+ /* Short-range */
+ if (!(flags & GMX_NONBONDED_DO_SR))
+ {
+ continue;
+ }
+ kernel_data.energygrp_elec = grppener->ener[egCOULSR];
+ kernel_data.energygrp_vdw = grppener->ener[fr->bBHAM ? egBHAMSR : egLJSR];
+ kernel_data.energygrp_polarization = grppener->ener[egGB];
+ nlist = nblists->nlist_sr;
+ f = f_shortrange;
+ }
+ else if (range == 1)
+ {
+ /* Long-range */
+ if (!(flags & GMX_NONBONDED_DO_LR))
+ {
+ continue;
+ }
+ kernel_data.energygrp_elec = grppener->ener[egCOULLR];
+ kernel_data.energygrp_vdw = grppener->ener[fr->bBHAM ? egBHAMLR : egLJLR];
+ kernel_data.energygrp_polarization = grppener->ener[egGB];
+ nlist = nblists->nlist_lr;
+ f = f_longrange;
+ }
+
+ for (i = i0; (i < i1); i++)
+ {
+ if (nlist[i].nri > 0)
+ {
+ if (flags & GMX_NONBONDED_DO_POTENTIAL)
+ {
+ /* Potential and force */
+ kernelptr = (nb_kernel_t *)nlist[i].kernelptr_vf;
+ }
+ else
+ {
+ /* Force only, no potential */
+ kernelptr = (nb_kernel_t *)nlist[i].kernelptr_f;
+ }
+
+ if (nlist[i].type != GMX_NBLIST_INTERACTION_FREE_ENERGY && (flags & GMX_NONBONDED_DO_FOREIGNLAMBDA))
+ {
+ /* We don't need the non-perturbed interactions */
+ continue;
+ }
+ (*kernelptr)(&(nlist[i]), x, f, fr, mdatoms, &kernel_data, nrnb);
+ }
+ }
+ }
+ }
+}
+
+static void
+nb_listed_warning_rlimit(const rvec *x, int ai, int aj, int * global_atom_index, real r, real rlimit)
+{
+ gmx_warning("Listed nonbonded interaction between particles %d and %d\n"
+ "at distance %.3f which is larger than the table limit %.3f nm.\n\n"
+ "This is likely either a 1,4 interaction, or a listed interaction inside\n"
+ "a smaller molecule you are decoupling during a free energy calculation.\n"
+ "Since interactions at distances beyond the table cannot be computed,\n"
+ "they are skipped until they are inside the table limit again. You will\n"
+ "only see this message once, even if it occurs for several interactions.\n\n"
+ "IMPORTANT: This should not happen in a stable simulation, so there is\n"
+ "probably something wrong with your system. Only change the table-extension\n"
+ "distance in the mdp file if you are really sure that is the reason.\n",
+ glatnr(global_atom_index, ai), glatnr(global_atom_index, aj), r, rlimit);
+
+ if (debug)
+ {
+ fprintf(debug,
+ "%8f %8f %8f\n%8f %8f %8f\n1-4 (%d,%d) interaction not within cut-off! r=%g. Ignored\n",
+ x[ai][XX], x[ai][YY], x[ai][ZZ], x[aj][XX], x[aj][YY], x[aj][ZZ],
+ glatnr(global_atom_index, ai), glatnr(global_atom_index, aj), r);
+ }
+}
+
+
+
+/* This might logically belong better in the nb_generic.c module, but it is only
+ * used in do_nonbonded_listed(), and we want it to be inlined there to avoid an
+ * extra functional call for every single pair listed in the topology.
+ */
+static real
+nb_evaluate_single(real r2, real tabscale, real *vftab,
+ real qq, real c6, real c12, real *velec, real *vvdw)
+{
+ real rinv, r, rtab, eps, eps2, Y, F, Geps, Heps2, Fp, VVe, FFe, VVd, FFd, VVr, FFr, fscal;
+ int ntab;
+
+ /* Do the tabulated interactions - first table lookup */
+ rinv = gmx_invsqrt(r2);
+ r = r2*rinv;
+ rtab = r*tabscale;
+ ntab = rtab;
+ eps = rtab-ntab;
+ eps2 = eps*eps;
+ ntab = 12*ntab;
+ /* Electrostatics */
+ Y = vftab[ntab];
+ F = vftab[ntab+1];
+ Geps = eps*vftab[ntab+2];
+ Heps2 = eps2*vftab[ntab+3];
+ Fp = F+Geps+Heps2;
+ VVe = Y+eps*Fp;
+ FFe = Fp+Geps+2.0*Heps2;
+ /* Dispersion */
+ Y = vftab[ntab+4];
+ F = vftab[ntab+5];
+ Geps = eps*vftab[ntab+6];
+ Heps2 = eps2*vftab[ntab+7];
+ Fp = F+Geps+Heps2;
+ VVd = Y+eps*Fp;
+ FFd = Fp+Geps+2.0*Heps2;
+ /* Repulsion */
+ Y = vftab[ntab+8];
+ F = vftab[ntab+9];
+ Geps = eps*vftab[ntab+10];
+ Heps2 = eps2*vftab[ntab+11];
+ Fp = F+Geps+Heps2;
+ VVr = Y+eps*Fp;
+ FFr = Fp+Geps+2.0*Heps2;
+
+ *velec = qq*VVe;
+ *vvdw = c6*VVd+c12*VVr;
+
+ fscal = -(qq*FFe+c6*FFd+c12*FFr)*tabscale*rinv;
+
+ return fscal;
+}
+
+
+real
+do_nonbonded_listed(int ftype, int nbonds,
+ const t_iatom iatoms[], const t_iparams iparams[],
+ const rvec x[], rvec f[], rvec fshift[],
+ const t_pbc *pbc, const t_graph *g,
+ real *lambda, real *dvdl,
+ const t_mdatoms *md,
+ const t_forcerec *fr, gmx_grppairener_t *grppener,
+ int *global_atom_index)
+{
+ int ielec, ivdw;
+ real qq, c6, c12;
+ rvec dx;
+ ivec dt;
+ int i, j, itype, ai, aj, gid;
+ int fshift_index;
+ real r2, rinv;
+ real fscal, velec, vvdw;
+ real * energygrp_elec;
+ real * energygrp_vdw;
+ static gmx_bool warned_rlimit = FALSE;
+ /* Free energy stuff */
+ gmx_bool bFreeEnergy;
+ real LFC[2], LFV[2], DLF[2], lfac_coul[2], lfac_vdw[2], dlfac_coul[2], dlfac_vdw[2];
+ real qqB, c6B, c12B, sigma2_def, sigma2_min;
+
+
+ switch (ftype)
+ {
+ case F_LJ14:
+ case F_LJC14_Q:
+ energygrp_elec = grppener->ener[egCOUL14];
+ energygrp_vdw = grppener->ener[egLJ14];
+ break;
+ case F_LJC_PAIRS_NB:
+ energygrp_elec = grppener->ener[egCOULSR];
+ energygrp_vdw = grppener->ener[egLJSR];
+ break;
+ default:
+ energygrp_elec = NULL; /* Keep compiler happy */
+ energygrp_vdw = NULL; /* Keep compiler happy */
+ gmx_fatal(FARGS, "Unknown function type %d in do_nonbonded14", ftype);
+ break;
+ }
+
+ if (fr->efep != efepNO)
+ {
+ /* Lambda factor for state A=1-lambda and B=lambda */
+ LFC[0] = 1.0 - lambda[efptCOUL];
+ LFV[0] = 1.0 - lambda[efptVDW];
+ LFC[1] = lambda[efptCOUL];
+ LFV[1] = lambda[efptVDW];
+
+ /*derivative of the lambda factor for state A and B */
+ DLF[0] = -1;
+ DLF[1] = 1;
+
+ /* precalculate */
+ sigma2_def = pow(fr->sc_sigma6_def, 1.0/3.0);
+ sigma2_min = pow(fr->sc_sigma6_min, 1.0/3.0);
+
+ for (i = 0; i < 2; i++)
+ {
+ lfac_coul[i] = (fr->sc_power == 2 ? (1-LFC[i])*(1-LFC[i]) : (1-LFC[i]));
+ dlfac_coul[i] = DLF[i]*fr->sc_power/fr->sc_r_power*(fr->sc_power == 2 ? (1-LFC[i]) : 1);
+ lfac_vdw[i] = (fr->sc_power == 2 ? (1-LFV[i])*(1-LFV[i]) : (1-LFV[i]));
+ dlfac_vdw[i] = DLF[i]*fr->sc_power/fr->sc_r_power*(fr->sc_power == 2 ? (1-LFV[i]) : 1);
+ }
+ }
+ else
+ {
+ sigma2_min = sigma2_def = 0;
+ }
+
+ bFreeEnergy = FALSE;
+ for (i = 0; (i < nbonds); )
+ {
+ itype = iatoms[i++];
+ ai = iatoms[i++];
+ aj = iatoms[i++];
+ gid = GID(md->cENER[ai], md->cENER[aj], md->nenergrp);
+
+ /* Get parameters */
+ switch (ftype)
+ {
+ case F_LJ14:
+ bFreeEnergy =
+ (fr->efep != efepNO &&
+ ((md->nPerturbed && (md->bPerturbed[ai] || md->bPerturbed[aj])) ||
+ iparams[itype].lj14.c6A != iparams[itype].lj14.c6B ||
+ iparams[itype].lj14.c12A != iparams[itype].lj14.c12B));
+ qq = md->chargeA[ai]*md->chargeA[aj]*fr->epsfac*fr->fudgeQQ;
+ c6 = iparams[itype].lj14.c6A;
+ c12 = iparams[itype].lj14.c12A;
+ break;
+ case F_LJC14_Q:
+ qq = iparams[itype].ljc14.qi*iparams[itype].ljc14.qj*fr->epsfac*iparams[itype].ljc14.fqq;
+ c6 = iparams[itype].ljc14.c6;
+ c12 = iparams[itype].ljc14.c12;
+ break;
+ case F_LJC_PAIRS_NB:
+ qq = iparams[itype].ljcnb.qi*iparams[itype].ljcnb.qj*fr->epsfac;
+ c6 = iparams[itype].ljcnb.c6;
+ c12 = iparams[itype].ljcnb.c12;
+ break;
+ default:
+ /* Cannot happen since we called gmx_fatal() above in this case */
+ qq = c6 = c12 = 0; /* Keep compiler happy */
+ break;
+ }
+
+ /* To save flops in the optimized kernels, c6/c12 have 6.0/12.0 derivative prefactors
+ * included in the general nfbp array now. This means the tables are scaled down by the
+ * same factor, so when we use the original c6/c12 parameters from iparams[] they must
+ * be scaled up.
+ */
+ c6 *= 6.0;
+ c12 *= 12.0;
+
+ /* Do we need to apply full periodic boundary conditions? */
+ if (fr->bMolPBC == TRUE)
+ {
+ fshift_index = pbc_dx_aiuc(pbc, x[ai], x[aj], dx);
+ }
+ else
+ {
+ fshift_index = CENTRAL;
+ rvec_sub(x[ai], x[aj], dx);
+ }
+ r2 = norm2(dx);
+
+ if (r2 >= fr->tab14.r*fr->tab14.r)
+ {
+ if (warned_rlimit == FALSE)
+ {
+ nb_listed_warning_rlimit(x, ai, aj, global_atom_index, sqrt(r2), fr->tab14.r);
+ warned_rlimit = TRUE;
+ }
+ continue;
+ }
+
+ if (bFreeEnergy)
+ {
+ /* Currently free energy is only supported for F_LJ14, so no need to check for that if we got here */
+ qqB = md->chargeB[ai]*md->chargeB[aj]*fr->epsfac*fr->fudgeQQ;
+ c6B = iparams[itype].lj14.c6B*6.0;
+ c12B = iparams[itype].lj14.c12B*12.0;
+
+ fscal = nb_free_energy_evaluate_single(r2, fr->sc_r_power, fr->sc_alphacoul, fr->sc_alphavdw,
+ fr->tab14.scale, fr->tab14.data, qq, c6, c12, qqB, c6B, c12B,
+ LFC, LFV, DLF, lfac_coul, lfac_vdw, dlfac_coul, dlfac_vdw,
+ fr->sc_sigma6_def, fr->sc_sigma6_min, sigma2_def, sigma2_min, &velec, &vvdw, dvdl);
+ }
+ else
+ {
+ /* Evaluate tabulated interaction without free energy */
+ fscal = nb_evaluate_single(r2, fr->tab14.scale, fr->tab14.data, qq, c6, c12, &velec, &vvdw);
+ }
+
+ energygrp_elec[gid] += velec;
+ energygrp_vdw[gid] += vvdw;
+ svmul(fscal, dx, dx);
+
+ /* Add the forces */
+ rvec_inc(f[ai], dx);
+ rvec_dec(f[aj], dx);
+
+ if (g)
+ {
+ /* Correct the shift forces using the graph */
+ ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
+ fshift_index = IVEC2IS(dt);
+ }
+ if (fshift_index != CENTRAL)
+ {
+ rvec_inc(fshift[fshift_index], dx);
+ rvec_dec(fshift[CENTRAL], dx);
+ }
+ }
+ return 0.0;
+}
--- /dev/null
- if (file_version < 72)
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROningen Mixture of Alchemy and Childrens' Stories
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This file is completely threadsafe - keep it that way! */
+#ifdef GMX_THREAD_MPI
+#include <thread_mpi.h>
+#endif
+
+
+#include <ctype.h>
+#include "sysstuff.h"
+#include "smalloc.h"
+#include "string2.h"
+#include "gmx_fatal.h"
+#include "macros.h"
+#include "names.h"
+#include "symtab.h"
+#include "futil.h"
+#include "filenm.h"
+#include "gmxfio.h"
+#include "topsort.h"
+#include "tpxio.h"
+#include "txtdump.h"
+#include "confio.h"
+#include "atomprop.h"
+#include "copyrite.h"
+#include "vec.h"
+#include "mtop_util.h"
+
+#define TPX_TAG_RELEASE "release"
+
+/* This is the tag string which is stored in the tpx file.
+ * Change this if you want to change the tpx format in a feature branch.
+ * This ensures that there will not be different tpx formats around which
+ * can not be distinguished.
+ */
+static const char *tpx_tag = TPX_TAG_RELEASE;
+
+/* This number should be increased whenever the file format changes! */
+static const int tpx_version = 92;
+
+/* This number should only be increased when you edit the TOPOLOGY section
+ * or the HEADER of the tpx format.
+ * This way we can maintain forward compatibility too for all analysis tools
+ * and/or external programs that only need to know the atom/residue names,
+ * charges, and bond connectivity.
+ *
+ * It first appeared in tpx version 26, when I also moved the inputrecord
+ * to the end of the tpx file, so we can just skip it if we only
+ * want the topology.
+ */
+static const int tpx_generation = 25;
+
+/* This number should be the most recent backwards incompatible version
+ * I.e., if this number is 9, we cannot read tpx version 9 with this code.
+ */
+static const int tpx_incompatible_version = 9;
+
+
+
+/* Struct used to maintain tpx compatibility when function types are added */
+typedef struct {
+ int fvnr; /* file version number in which the function type first appeared */
+ int ftype; /* function type */
+} t_ftupd;
+
+/*
+ * The entries should be ordered in:
+ * 1. ascending file version number
+ * 2. ascending function type number
+ */
+/*static const t_ftupd ftupd[] = {
+ { 20, F_CUBICBONDS },
+ { 20, F_CONNBONDS },
+ { 20, F_HARMONIC },
+ { 20, F_EQM, },
+ { 22, F_DISRESVIOL },
+ { 22, F_ORIRES },
+ { 22, F_ORIRESDEV },
+ { 26, F_FOURDIHS },
+ { 26, F_PIDIHS },
+ { 26, F_DIHRES },
+ { 26, F_DIHRESVIOL },
+ { 30, F_CROSS_BOND_BONDS },
+ { 30, F_CROSS_BOND_ANGLES },
+ { 30, F_UREY_BRADLEY },
+ { 30, F_POLARIZATION },
+ { 54, F_DHDL_CON },
+ };*/
+/*
+ * The entries should be ordered in:
+ * 1. ascending function type number
+ * 2. ascending file version number
+ */
+/* question; what is the purpose of the commented code above? */
+static const t_ftupd ftupd[] = {
+ { 20, F_CUBICBONDS },
+ { 20, F_CONNBONDS },
+ { 20, F_HARMONIC },
+ { 34, F_FENEBONDS },
+ { 43, F_TABBONDS },
+ { 43, F_TABBONDSNC },
+ { 70, F_RESTRBONDS },
+ { 76, F_LINEAR_ANGLES },
+ { 30, F_CROSS_BOND_BONDS },
+ { 30, F_CROSS_BOND_ANGLES },
+ { 30, F_UREY_BRADLEY },
+ { 34, F_QUARTIC_ANGLES },
+ { 43, F_TABANGLES },
+ { 26, F_FOURDIHS },
+ { 26, F_PIDIHS },
+ { 43, F_TABDIHS },
+ { 65, F_CMAP },
+ { 60, F_GB12 },
+ { 61, F_GB13 },
+ { 61, F_GB14 },
+ { 72, F_GBPOL },
+ { 72, F_NPSOLVATION },
+ { 41, F_LJC14_Q },
+ { 41, F_LJC_PAIRS_NB },
+ { 32, F_BHAM_LR },
+ { 32, F_RF_EXCL },
+ { 32, F_COUL_RECIP },
+ { 46, F_DPD },
+ { 30, F_POLARIZATION },
+ { 36, F_THOLE_POL },
+ { 90, F_FBPOSRES },
+ { 22, F_DISRESVIOL },
+ { 22, F_ORIRES },
+ { 22, F_ORIRESDEV },
+ { 26, F_DIHRES },
+ { 26, F_DIHRESVIOL },
+ { 49, F_VSITE4FDN },
+ { 50, F_VSITEN },
+ { 46, F_COM_PULL },
+ { 20, F_EQM },
+ { 46, F_ECONSERVED },
+ { 69, F_VTEMP_NOLONGERUSED},
+ { 66, F_PDISPCORR },
+ { 54, F_DVDL_CONSTR },
+ { 76, F_ANHARM_POL },
+ { 79, F_DVDL_COUL },
+ { 79, F_DVDL_VDW, },
+ { 79, F_DVDL_BONDED, },
+ { 79, F_DVDL_RESTRAINT },
+ { 79, F_DVDL_TEMPERATURE },
+};
+#define NFTUPD asize(ftupd)
+
+/* Needed for backward compatibility */
+#define MAXNODES 256
+
+static void _do_section(t_fileio *fio, int key, gmx_bool bRead, const char *src,
+ int line)
+{
+ char buf[STRLEN];
+ gmx_bool bDbg;
+
+ if (gmx_fio_getftp(fio) == efTPA)
+ {
+ if (!bRead)
+ {
+ gmx_fio_write_string(fio, itemstr[key]);
+ bDbg = gmx_fio_getdebug(fio);
+ gmx_fio_setdebug(fio, FALSE);
+ gmx_fio_write_string(fio, comment_str[key]);
+ gmx_fio_setdebug(fio, bDbg);
+ }
+ else
+ {
+ if (gmx_fio_getdebug(fio))
+ {
+ fprintf(stderr, "Looking for section %s (%s, %d)",
+ itemstr[key], src, line);
+ }
+
+ do
+ {
+ gmx_fio_do_string(fio, buf);
+ }
+ while ((gmx_strcasecmp(buf, itemstr[key]) != 0));
+
+ if (gmx_strcasecmp(buf, itemstr[key]) != 0)
+ {
+ gmx_fatal(FARGS, "\nCould not find section heading %s", itemstr[key]);
+ }
+ else if (gmx_fio_getdebug(fio))
+ {
+ fprintf(stderr, " and found it\n");
+ }
+ }
+ }
+}
+
+#define do_section(fio, key, bRead) _do_section(fio, key, bRead, __FILE__, __LINE__)
+
+/**************************************************************
+ *
+ * Now the higer level routines that do io of the structures and arrays
+ *
+ **************************************************************/
+static void do_pullgrp(t_fileio *fio, t_pullgrp *pgrp, gmx_bool bRead,
+ int file_version)
+{
+ gmx_bool bDum = TRUE;
+ int i;
+
+ gmx_fio_do_int(fio, pgrp->nat);
+ if (bRead)
+ {
+ snew(pgrp->ind, pgrp->nat);
+ }
+ bDum = gmx_fio_ndo_int(fio, pgrp->ind, pgrp->nat);
+ gmx_fio_do_int(fio, pgrp->nweight);
+ if (bRead)
+ {
+ snew(pgrp->weight, pgrp->nweight);
+ }
+ bDum = gmx_fio_ndo_real(fio, pgrp->weight, pgrp->nweight);
+ gmx_fio_do_int(fio, pgrp->pbcatom);
+ gmx_fio_do_rvec(fio, pgrp->vec);
+ gmx_fio_do_rvec(fio, pgrp->init);
+ gmx_fio_do_real(fio, pgrp->rate);
+ gmx_fio_do_real(fio, pgrp->k);
+ if (file_version >= 56)
+ {
+ gmx_fio_do_real(fio, pgrp->kB);
+ }
+ else
+ {
+ pgrp->kB = pgrp->k;
+ }
+}
+
+static void do_expandedvals(t_fileio *fio, t_expanded *expand, t_lambda *fepvals, gmx_bool bRead, int file_version)
+{
+ /* i is used in the ndo_double macro*/
+ int i;
+ real fv;
+ gmx_bool bDum = TRUE;
+ real rdum;
+ int n_lambda = fepvals->n_lambda;
+
+ /* reset the lambda calculation window */
+ fepvals->lambda_start_n = 0;
+ fepvals->lambda_stop_n = n_lambda;
+ if (file_version >= 79)
+ {
+ if (n_lambda > 0)
+ {
+ if (bRead)
+ {
+ snew(expand->init_lambda_weights, n_lambda);
+ }
+ bDum = gmx_fio_ndo_real(fio, expand->init_lambda_weights, n_lambda);
+ gmx_fio_do_gmx_bool(fio, expand->bInit_weights);
+ }
+
+ gmx_fio_do_int(fio, expand->nstexpanded);
+ gmx_fio_do_int(fio, expand->elmcmove);
+ gmx_fio_do_int(fio, expand->elamstats);
+ gmx_fio_do_int(fio, expand->lmc_repeats);
+ gmx_fio_do_int(fio, expand->gibbsdeltalam);
+ gmx_fio_do_int(fio, expand->lmc_forced_nstart);
+ gmx_fio_do_int(fio, expand->lmc_seed);
+ gmx_fio_do_real(fio, expand->mc_temp);
+ gmx_fio_do_int(fio, expand->bSymmetrizedTMatrix);
+ gmx_fio_do_int(fio, expand->nstTij);
+ gmx_fio_do_int(fio, expand->minvarmin);
+ gmx_fio_do_int(fio, expand->c_range);
+ gmx_fio_do_real(fio, expand->wl_scale);
+ gmx_fio_do_real(fio, expand->wl_ratio);
+ gmx_fio_do_real(fio, expand->init_wl_delta);
+ gmx_fio_do_gmx_bool(fio, expand->bWLoneovert);
+ gmx_fio_do_int(fio, expand->elmceq);
+ gmx_fio_do_int(fio, expand->equil_steps);
+ gmx_fio_do_int(fio, expand->equil_samples);
+ gmx_fio_do_int(fio, expand->equil_n_at_lam);
+ gmx_fio_do_real(fio, expand->equil_wl_delta);
+ gmx_fio_do_real(fio, expand->equil_ratio);
+ }
+}
+
+static void do_simtempvals(t_fileio *fio, t_simtemp *simtemp, int n_lambda, gmx_bool bRead,
+ int file_version)
+{
+ gmx_bool bDum = TRUE;
+
+ if (file_version >= 79)
+ {
+ gmx_fio_do_int(fio, simtemp->eSimTempScale);
+ gmx_fio_do_real(fio, simtemp->simtemp_high);
+ gmx_fio_do_real(fio, simtemp->simtemp_low);
+ if (n_lambda > 0)
+ {
+ if (bRead)
+ {
+ snew(simtemp->temperatures, n_lambda);
+ }
+ bDum = gmx_fio_ndo_real(fio, simtemp->temperatures, n_lambda);
+ }
+ }
+}
+
+static void do_fepvals(t_fileio *fio, t_lambda *fepvals, gmx_bool bRead, int file_version)
+{
+ /* i is defined in the ndo_double macro; use g to iterate. */
+ int i, g;
+ real fv;
+ gmx_bool bDum = TRUE;
+ real rdum;
+
+ /* free energy values */
+
+ if (file_version >= 79)
+ {
+ gmx_fio_do_int(fio, fepvals->init_fep_state);
+ gmx_fio_do_double(fio, fepvals->init_lambda);
+ gmx_fio_do_double(fio, fepvals->delta_lambda);
+ }
+ else if (file_version >= 59)
+ {
+ gmx_fio_do_double(fio, fepvals->init_lambda);
+ gmx_fio_do_double(fio, fepvals->delta_lambda);
+ }
+ else
+ {
+ gmx_fio_do_real(fio, rdum);
+ fepvals->init_lambda = rdum;
+ gmx_fio_do_real(fio, rdum);
+ fepvals->delta_lambda = rdum;
+ }
+ if (file_version >= 79)
+ {
+ gmx_fio_do_int(fio, fepvals->n_lambda);
+ if (bRead)
+ {
+ snew(fepvals->all_lambda, efptNR);
+ }
+ for (g = 0; g < efptNR; g++)
+ {
+ if (fepvals->n_lambda > 0)
+ {
+ if (bRead)
+ {
+ snew(fepvals->all_lambda[g], fepvals->n_lambda);
+ }
+ bDum = gmx_fio_ndo_double(fio, fepvals->all_lambda[g], fepvals->n_lambda);
+ bDum = gmx_fio_ndo_int(fio, fepvals->separate_dvdl, efptNR);
+ }
+ else if (fepvals->init_lambda >= 0)
+ {
+ fepvals->separate_dvdl[efptFEP] = TRUE;
+ }
+ }
+ }
+ else if (file_version >= 64)
+ {
+ gmx_fio_do_int(fio, fepvals->n_lambda);
+ if (bRead)
+ {
+ int g;
+
+ snew(fepvals->all_lambda, efptNR);
+ /* still allocate the all_lambda array's contents. */
+ for (g = 0; g < efptNR; g++)
+ {
+ if (fepvals->n_lambda > 0)
+ {
+ snew(fepvals->all_lambda[g], fepvals->n_lambda);
+ }
+ }
+ }
+ bDum = gmx_fio_ndo_double(fio, fepvals->all_lambda[efptFEP],
+ fepvals->n_lambda);
+ if (fepvals->init_lambda >= 0)
+ {
+ int g, h;
+
+ fepvals->separate_dvdl[efptFEP] = TRUE;
+
+ if (bRead)
+ {
+ /* copy the contents of the efptFEP lambda component to all
+ the other components */
+ for (g = 0; g < efptNR; g++)
+ {
+ for (h = 0; h < fepvals->n_lambda; h++)
+ {
+ if (g != efptFEP)
+ {
+ fepvals->all_lambda[g][h] =
+ fepvals->all_lambda[efptFEP][h];
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ fepvals->n_lambda = 0;
+ fepvals->all_lambda = NULL;
+ if (fepvals->init_lambda >= 0)
+ {
+ fepvals->separate_dvdl[efptFEP] = TRUE;
+ }
+ }
+ if (file_version >= 13)
+ {
+ gmx_fio_do_real(fio, fepvals->sc_alpha);
+ }
+ else
+ {
+ fepvals->sc_alpha = 0;
+ }
+ if (file_version >= 38)
+ {
+ gmx_fio_do_int(fio, fepvals->sc_power);
+ }
+ else
+ {
+ fepvals->sc_power = 2;
+ }
+ if (file_version >= 79)
+ {
+ gmx_fio_do_real(fio, fepvals->sc_r_power);
+ }
+ else
+ {
+ fepvals->sc_r_power = 6.0;
+ }
+ if (file_version >= 15)
+ {
+ gmx_fio_do_real(fio, fepvals->sc_sigma);
+ }
+ else
+ {
+ fepvals->sc_sigma = 0.3;
+ }
+ if (bRead)
+ {
+ if (file_version >= 71)
+ {
+ fepvals->sc_sigma_min = fepvals->sc_sigma;
+ }
+ else
+ {
+ fepvals->sc_sigma_min = 0;
+ }
+ }
+ if (file_version >= 79)
+ {
+ gmx_fio_do_int(fio, fepvals->bScCoul);
+ }
+ else
+ {
+ fepvals->bScCoul = TRUE;
+ }
+ if (file_version >= 64)
+ {
+ gmx_fio_do_int(fio, fepvals->nstdhdl);
+ }
+ else
+ {
+ fepvals->nstdhdl = 1;
+ }
+
+ if (file_version >= 73)
+ {
+ gmx_fio_do_int(fio, fepvals->separate_dhdl_file);
+ gmx_fio_do_int(fio, fepvals->dhdl_derivatives);
+ }
+ else
+ {
+ fepvals->separate_dhdl_file = esepdhdlfileYES;
+ fepvals->dhdl_derivatives = edhdlderivativesYES;
+ }
+ if (file_version >= 71)
+ {
+ gmx_fio_do_int(fio, fepvals->dh_hist_size);
+ gmx_fio_do_double(fio, fepvals->dh_hist_spacing);
+ }
+ else
+ {
+ fepvals->dh_hist_size = 0;
+ fepvals->dh_hist_spacing = 0.1;
+ }
+ if (file_version >= 79)
+ {
+ gmx_fio_do_int(fio, fepvals->bPrintEnergy);
+ }
+ else
+ {
+ fepvals->bPrintEnergy = FALSE;
+ }
+
+ /* handle lambda_neighbors */
+ if ((file_version >= 83 && file_version < 90) || file_version >= 92)
+ {
+ gmx_fio_do_int(fio, fepvals->lambda_neighbors);
+ if ( (fepvals->lambda_neighbors >= 0) && (fepvals->init_fep_state >= 0) &&
+ (fepvals->init_lambda < 0) )
+ {
+ fepvals->lambda_start_n = (fepvals->init_fep_state -
+ fepvals->lambda_neighbors);
+ fepvals->lambda_stop_n = (fepvals->init_fep_state +
+ fepvals->lambda_neighbors + 1);
+ if (fepvals->lambda_start_n < 0)
+ {
+ fepvals->lambda_start_n = 0;;
+ }
+ if (fepvals->lambda_stop_n >= fepvals->n_lambda)
+ {
+ fepvals->lambda_stop_n = fepvals->n_lambda;
+ }
+ }
+ else
+ {
+ fepvals->lambda_start_n = 0;
+ fepvals->lambda_stop_n = fepvals->n_lambda;
+ }
+ }
+ else
+ {
+ fepvals->lambda_start_n = 0;
+ fepvals->lambda_stop_n = fepvals->n_lambda;
+ }
+}
+
+static void do_pull(t_fileio *fio, t_pull *pull, gmx_bool bRead, int file_version)
+{
+ int g;
+
+ gmx_fio_do_int(fio, pull->ngrp);
+ gmx_fio_do_int(fio, pull->eGeom);
+ gmx_fio_do_ivec(fio, pull->dim);
+ gmx_fio_do_real(fio, pull->cyl_r1);
+ gmx_fio_do_real(fio, pull->cyl_r0);
+ gmx_fio_do_real(fio, pull->constr_tol);
+ gmx_fio_do_int(fio, pull->nstxout);
+ gmx_fio_do_int(fio, pull->nstfout);
+ if (bRead)
+ {
+ snew(pull->grp, pull->ngrp+1);
+ }
+ for (g = 0; g < pull->ngrp+1; g++)
+ {
+ do_pullgrp(fio, &pull->grp[g], bRead, file_version);
+ }
+}
+
+
+static void do_rotgrp(t_fileio *fio, t_rotgrp *rotg, gmx_bool bRead, int file_version)
+{
+ gmx_bool bDum = TRUE;
+ int i;
+
+ gmx_fio_do_int(fio, rotg->eType);
+ gmx_fio_do_int(fio, rotg->bMassW);
+ gmx_fio_do_int(fio, rotg->nat);
+ if (bRead)
+ {
+ snew(rotg->ind, rotg->nat);
+ }
+ gmx_fio_ndo_int(fio, rotg->ind, rotg->nat);
+ if (bRead)
+ {
+ snew(rotg->x_ref, rotg->nat);
+ }
+ gmx_fio_ndo_rvec(fio, rotg->x_ref, rotg->nat);
+ gmx_fio_do_rvec(fio, rotg->vec);
+ gmx_fio_do_rvec(fio, rotg->pivot);
+ gmx_fio_do_real(fio, rotg->rate);
+ gmx_fio_do_real(fio, rotg->k);
+ gmx_fio_do_real(fio, rotg->slab_dist);
+ gmx_fio_do_real(fio, rotg->min_gaussian);
+ gmx_fio_do_real(fio, rotg->eps);
+ gmx_fio_do_int(fio, rotg->eFittype);
+ gmx_fio_do_int(fio, rotg->PotAngle_nstep);
+ gmx_fio_do_real(fio, rotg->PotAngle_step);
+}
+
+static void do_rot(t_fileio *fio, t_rot *rot, gmx_bool bRead, int file_version)
+{
+ int g;
+
+ gmx_fio_do_int(fio, rot->ngrp);
+ gmx_fio_do_int(fio, rot->nstrout);
+ gmx_fio_do_int(fio, rot->nstsout);
+ if (bRead)
+ {
+ snew(rot->grp, rot->ngrp);
+ }
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ do_rotgrp(fio, &rot->grp[g], bRead, file_version);
+ }
+}
+
+
+static void do_inputrec(t_fileio *fio, t_inputrec *ir, gmx_bool bRead,
+ int file_version, real *fudgeQQ)
+{
+ int i, j, k, *tmp, idum = 0;
+ gmx_bool bDum = TRUE;
+ real rdum, bd_temp;
+ rvec vdum;
+ gmx_bool bSimAnn;
+ real zerotemptime, finish_t, init_temp, finish_temp;
+
+ if (file_version != tpx_version)
+ {
+ /* Give a warning about features that are not accessible */
+ fprintf(stderr, "Note: file tpx version %d, software tpx version %d\n",
+ file_version, tpx_version);
+ }
+
+ if (bRead)
+ {
+ init_inputrec(ir);
+ }
+
+ if (file_version == 0)
+ {
+ return;
+ }
+
+ /* Basic inputrec stuff */
+ gmx_fio_do_int(fio, ir->eI);
+ if (file_version >= 62)
+ {
+ gmx_fio_do_gmx_large_int(fio, ir->nsteps);
+ }
+ else
+ {
+ gmx_fio_do_int(fio, idum);
+ ir->nsteps = idum;
+ }
+ if (file_version > 25)
+ {
+ if (file_version >= 62)
+ {
+ gmx_fio_do_gmx_large_int(fio, ir->init_step);
+ }
+ else
+ {
+ gmx_fio_do_int(fio, idum);
+ ir->init_step = idum;
+ }
+ }
+ else
+ {
+ ir->init_step = 0;
+ }
+
+ if (file_version >= 58)
+ {
+ gmx_fio_do_int(fio, ir->simulation_part);
+ }
+ else
+ {
+ ir->simulation_part = 1;
+ }
+
+ if (file_version >= 67)
+ {
+ gmx_fio_do_int(fio, ir->nstcalcenergy);
+ }
+ else
+ {
+ ir->nstcalcenergy = 1;
+ }
+ if (file_version < 53)
+ {
+ /* The pbc info has been moved out of do_inputrec,
+ * since we always want it, also without reading the inputrec.
+ */
+ gmx_fio_do_int(fio, ir->ePBC);
+ if ((file_version <= 15) && (ir->ePBC == 2))
+ {
+ ir->ePBC = epbcNONE;
+ }
+ if (file_version >= 45)
+ {
+ gmx_fio_do_int(fio, ir->bPeriodicMols);
+ }
+ else
+ {
+ if (ir->ePBC == 2)
+ {
+ ir->ePBC = epbcXYZ;
+ ir->bPeriodicMols = TRUE;
+ }
+ else
+ {
+ ir->bPeriodicMols = FALSE;
+ }
+ }
+ }
+ if (file_version >= 81)
+ {
+ gmx_fio_do_int(fio, ir->cutoff_scheme);
+ }
+ else
+ {
+ ir->cutoff_scheme = ecutsGROUP;
+ }
+ gmx_fio_do_int(fio, ir->ns_type);
+ gmx_fio_do_int(fio, ir->nstlist);
+ gmx_fio_do_int(fio, ir->ndelta);
+ if (file_version < 41)
+ {
+ gmx_fio_do_int(fio, idum);
+ gmx_fio_do_int(fio, idum);
+ }
+ if (file_version >= 45)
+ {
+ gmx_fio_do_real(fio, ir->rtpi);
+ }
+ else
+ {
+ ir->rtpi = 0.05;
+ }
+ gmx_fio_do_int(fio, ir->nstcomm);
+ if (file_version > 34)
+ {
+ gmx_fio_do_int(fio, ir->comm_mode);
+ }
+ else if (ir->nstcomm < 0)
+ {
+ ir->comm_mode = ecmANGULAR;
+ }
+ else
+ {
+ ir->comm_mode = ecmLINEAR;
+ }
+ ir->nstcomm = abs(ir->nstcomm);
+
+ if (file_version > 25)
+ {
+ gmx_fio_do_int(fio, ir->nstcheckpoint);
+ }
+ else
+ {
+ ir->nstcheckpoint = 0;
+ }
+
+ gmx_fio_do_int(fio, ir->nstcgsteep);
+
+ if (file_version >= 30)
+ {
+ gmx_fio_do_int(fio, ir->nbfgscorr);
+ }
+ else if (bRead)
+ {
+ ir->nbfgscorr = 10;
+ }
+
+ gmx_fio_do_int(fio, ir->nstlog);
+ gmx_fio_do_int(fio, ir->nstxout);
+ gmx_fio_do_int(fio, ir->nstvout);
+ gmx_fio_do_int(fio, ir->nstfout);
+ gmx_fio_do_int(fio, ir->nstenergy);
+ gmx_fio_do_int(fio, ir->nstxtcout);
+ if (file_version >= 59)
+ {
+ gmx_fio_do_double(fio, ir->init_t);
+ gmx_fio_do_double(fio, ir->delta_t);
+ }
+ else
+ {
+ gmx_fio_do_real(fio, rdum);
+ ir->init_t = rdum;
+ gmx_fio_do_real(fio, rdum);
+ ir->delta_t = rdum;
+ }
+ gmx_fio_do_real(fio, ir->xtcprec);
+ if (file_version < 19)
+ {
+ gmx_fio_do_int(fio, idum);
+ gmx_fio_do_int(fio, idum);
+ }
+ if (file_version < 18)
+ {
+ gmx_fio_do_int(fio, idum);
+ }
+ if (file_version >= 81)
+ {
+ gmx_fio_do_real(fio, ir->verletbuf_drift);
+ }
+ else
+ {
+ ir->verletbuf_drift = 0;
+ }
+ gmx_fio_do_real(fio, ir->rlist);
+ if (file_version >= 67)
+ {
+ gmx_fio_do_real(fio, ir->rlistlong);
+ }
+ if (file_version >= 82 && file_version != 90)
+ {
+ gmx_fio_do_int(fio, ir->nstcalclr);
+ }
+ else
+ {
+ /* Calculate at NS steps */
+ ir->nstcalclr = ir->nstlist;
+ }
+ gmx_fio_do_int(fio, ir->coulombtype);
+ if (file_version < 32 && ir->coulombtype == eelRF)
+ {
+ ir->coulombtype = eelRF_NEC;
+ }
+ if (file_version >= 81)
+ {
+ gmx_fio_do_int(fio, ir->coulomb_modifier);
+ }
+ else
+ {
+ ir->coulomb_modifier = (ir->cutoff_scheme == ecutsVERLET ? eintmodPOTSHIFT : eintmodNONE);
+ }
+ gmx_fio_do_real(fio, ir->rcoulomb_switch);
+ gmx_fio_do_real(fio, ir->rcoulomb);
+ gmx_fio_do_int(fio, ir->vdwtype);
+ if (file_version >= 81)
+ {
+ gmx_fio_do_int(fio, ir->vdw_modifier);
+ }
+ else
+ {
+ ir->vdw_modifier = (ir->cutoff_scheme == ecutsVERLET ? eintmodPOTSHIFT : eintmodNONE);
+ }
+ gmx_fio_do_real(fio, ir->rvdw_switch);
+ gmx_fio_do_real(fio, ir->rvdw);
+ if (file_version < 67)
+ {
+ ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb));
+ }
+ gmx_fio_do_int(fio, ir->eDispCorr);
+ gmx_fio_do_real(fio, ir->epsilon_r);
+ if (file_version >= 37)
+ {
+ gmx_fio_do_real(fio, ir->epsilon_rf);
+ }
+ else
+ {
+ if (EEL_RF(ir->coulombtype))
+ {
+ ir->epsilon_rf = ir->epsilon_r;
+ ir->epsilon_r = 1.0;
+ }
+ else
+ {
+ ir->epsilon_rf = 1.0;
+ }
+ }
+ if (file_version >= 29)
+ {
+ gmx_fio_do_real(fio, ir->tabext);
+ }
+ else
+ {
+ ir->tabext = 1.0;
+ }
+
+ if (file_version > 25)
+ {
+ gmx_fio_do_int(fio, ir->gb_algorithm);
+ gmx_fio_do_int(fio, ir->nstgbradii);
+ gmx_fio_do_real(fio, ir->rgbradii);
+ gmx_fio_do_real(fio, ir->gb_saltconc);
+ gmx_fio_do_int(fio, ir->implicit_solvent);
+ }
+ else
+ {
+ ir->gb_algorithm = egbSTILL;
+ ir->nstgbradii = 1;
+ ir->rgbradii = 1.0;
+ ir->gb_saltconc = 0;
+ ir->implicit_solvent = eisNO;
+ }
+ if (file_version >= 55)
+ {
+ gmx_fio_do_real(fio, ir->gb_epsilon_solvent);
+ gmx_fio_do_real(fio, ir->gb_obc_alpha);
+ gmx_fio_do_real(fio, ir->gb_obc_beta);
+ gmx_fio_do_real(fio, ir->gb_obc_gamma);
+ if (file_version >= 60)
+ {
+ gmx_fio_do_real(fio, ir->gb_dielectric_offset);
+ gmx_fio_do_int(fio, ir->sa_algorithm);
+ }
+ else
+ {
+ ir->gb_dielectric_offset = 0.009;
+ ir->sa_algorithm = esaAPPROX;
+ }
+ gmx_fio_do_real(fio, ir->sa_surface_tension);
+
+ /* Override sa_surface_tension if it is not changed in the mpd-file */
+ if (ir->sa_surface_tension < 0)
+ {
+ if (ir->gb_algorithm == egbSTILL)
+ {
+ ir->sa_surface_tension = 0.0049 * 100 * CAL2JOULE;
+ }
+ else if (ir->gb_algorithm == egbHCT || ir->gb_algorithm == egbOBC)
+ {
+ ir->sa_surface_tension = 0.0054 * 100 * CAL2JOULE;
+ }
+ }
+
+ }
+ else
+ {
+ /* Better use sensible values than insane (0.0) ones... */
+ ir->gb_epsilon_solvent = 80;
+ ir->gb_obc_alpha = 1.0;
+ ir->gb_obc_beta = 0.8;
+ ir->gb_obc_gamma = 4.85;
+ ir->sa_surface_tension = 2.092;
+ }
+
+
+ if (file_version >= 81)
+ {
+ gmx_fio_do_real(fio, ir->fourier_spacing);
+ }
+ else
+ {
+ ir->fourier_spacing = 0.0;
+ }
+ gmx_fio_do_int(fio, ir->nkx);
+ gmx_fio_do_int(fio, ir->nky);
+ gmx_fio_do_int(fio, ir->nkz);
+ gmx_fio_do_int(fio, ir->pme_order);
+ gmx_fio_do_real(fio, ir->ewald_rtol);
+
+ if (file_version >= 24)
+ {
+ gmx_fio_do_int(fio, ir->ewald_geometry);
+ }
+ else
+ {
+ ir->ewald_geometry = eewg3D;
+ }
+
+ if (file_version <= 17)
+ {
+ ir->epsilon_surface = 0;
+ if (file_version == 17)
+ {
+ gmx_fio_do_int(fio, idum);
+ }
+ }
+ else
+ {
+ gmx_fio_do_real(fio, ir->epsilon_surface);
+ }
+
+ gmx_fio_do_gmx_bool(fio, ir->bOptFFT);
+
+ gmx_fio_do_gmx_bool(fio, ir->bContinuation);
+ gmx_fio_do_int(fio, ir->etc);
+ /* before version 18, ir->etc was a gmx_bool (ir->btc),
+ * but the values 0 and 1 still mean no and
+ * berendsen temperature coupling, respectively.
+ */
+ if (file_version >= 79)
+ {
+ gmx_fio_do_gmx_bool(fio, ir->bPrintNHChains);
+ }
+ if (file_version >= 71)
+ {
+ gmx_fio_do_int(fio, ir->nsttcouple);
+ }
+ else
+ {
+ ir->nsttcouple = ir->nstcalcenergy;
+ }
+ if (file_version <= 15)
+ {
+ gmx_fio_do_int(fio, idum);
+ }
+ if (file_version <= 17)
+ {
+ gmx_fio_do_int(fio, ir->epct);
+ if (file_version <= 15)
+ {
+ if (ir->epct == 5)
+ {
+ ir->epct = epctSURFACETENSION;
+ }
+ gmx_fio_do_int(fio, idum);
+ }
+ ir->epct -= 1;
+ /* we have removed the NO alternative at the beginning */
+ if (ir->epct == -1)
+ {
+ ir->epc = epcNO;
+ ir->epct = epctISOTROPIC;
+ }
+ else
+ {
+ ir->epc = epcBERENDSEN;
+ }
+ }
+ else
+ {
+ gmx_fio_do_int(fio, ir->epc);
+ gmx_fio_do_int(fio, ir->epct);
+ }
+ if (file_version >= 71)
+ {
+ gmx_fio_do_int(fio, ir->nstpcouple);
+ }
+ else
+ {
+ ir->nstpcouple = ir->nstcalcenergy;
+ }
+ gmx_fio_do_real(fio, ir->tau_p);
+ if (file_version <= 15)
+ {
+ gmx_fio_do_rvec(fio, vdum);
+ clear_mat(ir->ref_p);
+ for (i = 0; i < DIM; i++)
+ {
+ ir->ref_p[i][i] = vdum[i];
+ }
+ }
+ else
+ {
+ gmx_fio_do_rvec(fio, ir->ref_p[XX]);
+ gmx_fio_do_rvec(fio, ir->ref_p[YY]);
+ gmx_fio_do_rvec(fio, ir->ref_p[ZZ]);
+ }
+ if (file_version <= 15)
+ {
+ gmx_fio_do_rvec(fio, vdum);
+ clear_mat(ir->compress);
+ for (i = 0; i < DIM; i++)
+ {
+ ir->compress[i][i] = vdum[i];
+ }
+ }
+ else
+ {
+ gmx_fio_do_rvec(fio, ir->compress[XX]);
+ gmx_fio_do_rvec(fio, ir->compress[YY]);
+ gmx_fio_do_rvec(fio, ir->compress[ZZ]);
+ }
+ if (file_version >= 47)
+ {
+ gmx_fio_do_int(fio, ir->refcoord_scaling);
+ gmx_fio_do_rvec(fio, ir->posres_com);
+ gmx_fio_do_rvec(fio, ir->posres_comB);
+ }
+ else
+ {
+ ir->refcoord_scaling = erscNO;
+ clear_rvec(ir->posres_com);
+ clear_rvec(ir->posres_comB);
+ }
+ if ((file_version > 25) && (file_version < 79))
+ {
+ gmx_fio_do_int(fio, ir->andersen_seed);
+ }
+ else
+ {
+ ir->andersen_seed = 0;
+ }
+ if (file_version < 26)
+ {
+ gmx_fio_do_gmx_bool(fio, bSimAnn);
+ gmx_fio_do_real(fio, zerotemptime);
+ }
+
+ if (file_version < 37)
+ {
+ gmx_fio_do_real(fio, rdum);
+ }
+
+ gmx_fio_do_real(fio, ir->shake_tol);
+ if (file_version < 54)
+ {
+ gmx_fio_do_real(fio, *fudgeQQ);
+ }
+
+ gmx_fio_do_int(fio, ir->efep);
+ if (file_version <= 14 && ir->efep != efepNO)
+ {
+ ir->efep = efepYES;
+ }
+ do_fepvals(fio, ir->fepvals, bRead, file_version);
+
+ if (file_version >= 79)
+ {
+ gmx_fio_do_gmx_bool(fio, ir->bSimTemp);
+ if (ir->bSimTemp)
+ {
+ ir->bSimTemp = TRUE;
+ }
+ }
+ else
+ {
+ ir->bSimTemp = FALSE;
+ }
+ if (ir->bSimTemp)
+ {
+ do_simtempvals(fio, ir->simtempvals, ir->fepvals->n_lambda, bRead, file_version);
+ }
+
+ if (file_version >= 79)
+ {
+ gmx_fio_do_gmx_bool(fio, ir->bExpanded);
+ if (ir->bExpanded)
+ {
+ ir->bExpanded = TRUE;
+ }
+ else
+ {
+ ir->bExpanded = FALSE;
+ }
+ }
+ if (ir->bExpanded)
+ {
+ do_expandedvals(fio, ir->expandedvals, ir->fepvals, bRead, file_version);
+ }
+ if (file_version >= 57)
+ {
+ gmx_fio_do_int(fio, ir->eDisre);
+ }
+ gmx_fio_do_int(fio, ir->eDisreWeighting);
+ if (file_version < 22)
+ {
+ if (ir->eDisreWeighting == 0)
+ {
+ ir->eDisreWeighting = edrwEqual;
+ }
+ else
+ {
+ ir->eDisreWeighting = edrwConservative;
+ }
+ }
+ gmx_fio_do_gmx_bool(fio, ir->bDisreMixed);
+ gmx_fio_do_real(fio, ir->dr_fc);
+ gmx_fio_do_real(fio, ir->dr_tau);
+ gmx_fio_do_int(fio, ir->nstdisreout);
+ if (file_version >= 22)
+ {
+ gmx_fio_do_real(fio, ir->orires_fc);
+ gmx_fio_do_real(fio, ir->orires_tau);
+ gmx_fio_do_int(fio, ir->nstorireout);
+ }
+ else
+ {
+ ir->orires_fc = 0;
+ ir->orires_tau = 0;
+ ir->nstorireout = 0;
+ }
+ if (file_version >= 26 && file_version < 79)
+ {
+ gmx_fio_do_real(fio, ir->dihre_fc);
+ if (file_version < 56)
+ {
+ gmx_fio_do_real(fio, rdum);
+ gmx_fio_do_int(fio, idum);
+ }
+ }
+ else
+ {
+ ir->dihre_fc = 0;
+ }
+
+ gmx_fio_do_real(fio, ir->em_stepsize);
+ gmx_fio_do_real(fio, ir->em_tol);
+ if (file_version >= 22)
+ {
+ gmx_fio_do_gmx_bool(fio, ir->bShakeSOR);
+ }
+ else if (bRead)
+ {
+ ir->bShakeSOR = TRUE;
+ }
+ if (file_version >= 11)
+ {
+ gmx_fio_do_int(fio, ir->niter);
+ }
+ else if (bRead)
+ {
+ ir->niter = 25;
+ fprintf(stderr, "Note: niter not in run input file, setting it to %d\n",
+ ir->niter);
+ }
+ if (file_version >= 21)
+ {
+ gmx_fio_do_real(fio, ir->fc_stepsize);
+ }
+ else
+ {
+ ir->fc_stepsize = 0;
+ }
+ gmx_fio_do_int(fio, ir->eConstrAlg);
+ gmx_fio_do_int(fio, ir->nProjOrder);
+ gmx_fio_do_real(fio, ir->LincsWarnAngle);
+ if (file_version <= 14)
+ {
+ gmx_fio_do_int(fio, idum);
+ }
+ if (file_version >= 26)
+ {
+ gmx_fio_do_int(fio, ir->nLincsIter);
+ }
+ else if (bRead)
+ {
+ ir->nLincsIter = 1;
+ fprintf(stderr, "Note: nLincsIter not in run input file, setting it to %d\n",
+ ir->nLincsIter);
+ }
+ if (file_version < 33)
+ {
+ gmx_fio_do_real(fio, bd_temp);
+ }
+ gmx_fio_do_real(fio, ir->bd_fric);
+ gmx_fio_do_int(fio, ir->ld_seed);
+ if (file_version >= 33)
+ {
+ for (i = 0; i < DIM; i++)
+ {
+ gmx_fio_do_rvec(fio, ir->deform[i]);
+ }
+ }
+ else
+ {
+ for (i = 0; i < DIM; i++)
+ {
+ clear_rvec(ir->deform[i]);
+ }
+ }
+ if (file_version >= 14)
+ {
+ gmx_fio_do_real(fio, ir->cos_accel);
+ }
+ else if (bRead)
+ {
+ ir->cos_accel = 0;
+ }
+ gmx_fio_do_int(fio, ir->userint1);
+ gmx_fio_do_int(fio, ir->userint2);
+ gmx_fio_do_int(fio, ir->userint3);
+ gmx_fio_do_int(fio, ir->userint4);
+ gmx_fio_do_real(fio, ir->userreal1);
+ gmx_fio_do_real(fio, ir->userreal2);
+ gmx_fio_do_real(fio, ir->userreal3);
+ gmx_fio_do_real(fio, ir->userreal4);
+
+ /* AdResS stuff */
+ if (file_version >= 77)
+ {
+ gmx_fio_do_gmx_bool(fio, ir->bAdress);
+ if (ir->bAdress)
+ {
+ if (bRead)
+ {
+ snew(ir->adress, 1);
+ }
+ gmx_fio_do_int(fio, ir->adress->type);
+ gmx_fio_do_real(fio, ir->adress->const_wf);
+ gmx_fio_do_real(fio, ir->adress->ex_width);
+ gmx_fio_do_real(fio, ir->adress->hy_width);
+ gmx_fio_do_int(fio, ir->adress->icor);
+ gmx_fio_do_int(fio, ir->adress->site);
+ gmx_fio_do_rvec(fio, ir->adress->refs);
+ gmx_fio_do_int(fio, ir->adress->n_tf_grps);
+ gmx_fio_do_real(fio, ir->adress->ex_forcecap);
+ gmx_fio_do_int(fio, ir->adress->n_energy_grps);
+ gmx_fio_do_int(fio, ir->adress->do_hybridpairs);
+
+ if (bRead)
+ {
+ snew(ir->adress->tf_table_index, ir->adress->n_tf_grps);
+ }
+ if (ir->adress->n_tf_grps > 0)
+ {
+ bDum = gmx_fio_ndo_int(fio, ir->adress->tf_table_index, ir->adress->n_tf_grps);
+ }
+ if (bRead)
+ {
+ snew(ir->adress->group_explicit, ir->adress->n_energy_grps);
+ }
+ if (ir->adress->n_energy_grps > 0)
+ {
+ bDum = gmx_fio_ndo_int(fio, ir->adress->group_explicit, ir->adress->n_energy_grps);
+ }
+ }
+ }
+ else
+ {
+ ir->bAdress = FALSE;
+ }
+
+ /* pull stuff */
+ if (file_version >= 48)
+ {
+ gmx_fio_do_int(fio, ir->ePull);
+ if (ir->ePull != epullNO)
+ {
+ if (bRead)
+ {
+ snew(ir->pull, 1);
+ }
+ do_pull(fio, ir->pull, bRead, file_version);
+ }
+ }
+ else
+ {
+ ir->ePull = epullNO;
+ }
+
+ /* Enforced rotation */
+ if (file_version >= 74)
+ {
+ gmx_fio_do_int(fio, ir->bRot);
+ if (ir->bRot == TRUE)
+ {
+ if (bRead)
+ {
+ snew(ir->rot, 1);
+ }
+ do_rot(fio, ir->rot, bRead, file_version);
+ }
+ }
+ else
+ {
+ ir->bRot = FALSE;
+ }
+
+ /* grpopts stuff */
+ gmx_fio_do_int(fio, ir->opts.ngtc);
+ if (file_version >= 69)
+ {
+ gmx_fio_do_int(fio, ir->opts.nhchainlength);
+ }
+ else
+ {
+ ir->opts.nhchainlength = 1;
+ }
+ gmx_fio_do_int(fio, ir->opts.ngacc);
+ gmx_fio_do_int(fio, ir->opts.ngfrz);
+ gmx_fio_do_int(fio, ir->opts.ngener);
+
+ if (bRead)
+ {
+ snew(ir->opts.nrdf, ir->opts.ngtc);
+ snew(ir->opts.ref_t, ir->opts.ngtc);
+ snew(ir->opts.annealing, ir->opts.ngtc);
+ snew(ir->opts.anneal_npoints, ir->opts.ngtc);
+ snew(ir->opts.anneal_time, ir->opts.ngtc);
+ snew(ir->opts.anneal_temp, ir->opts.ngtc);
+ snew(ir->opts.tau_t, ir->opts.ngtc);
+ snew(ir->opts.nFreeze, ir->opts.ngfrz);
+ snew(ir->opts.acc, ir->opts.ngacc);
+ snew(ir->opts.egp_flags, ir->opts.ngener*ir->opts.ngener);
+ }
+ if (ir->opts.ngtc > 0)
+ {
+ if (bRead && file_version < 13)
+ {
+ snew(tmp, ir->opts.ngtc);
+ bDum = gmx_fio_ndo_int(fio, tmp, ir->opts.ngtc);
+ for (i = 0; i < ir->opts.ngtc; i++)
+ {
+ ir->opts.nrdf[i] = tmp[i];
+ }
+ sfree(tmp);
+ }
+ else
+ {
+ bDum = gmx_fio_ndo_real(fio, ir->opts.nrdf, ir->opts.ngtc);
+ }
+ bDum = gmx_fio_ndo_real(fio, ir->opts.ref_t, ir->opts.ngtc);
+ bDum = gmx_fio_ndo_real(fio, ir->opts.tau_t, ir->opts.ngtc);
+ if (file_version < 33 && ir->eI == eiBD)
+ {
+ for (i = 0; i < ir->opts.ngtc; i++)
+ {
+ ir->opts.tau_t[i] = bd_temp;
+ }
+ }
+ }
+ if (ir->opts.ngfrz > 0)
+ {
+ bDum = gmx_fio_ndo_ivec(fio, ir->opts.nFreeze, ir->opts.ngfrz);
+ }
+ if (ir->opts.ngacc > 0)
+ {
+ gmx_fio_ndo_rvec(fio, ir->opts.acc, ir->opts.ngacc);
+ }
+ if (file_version >= 12)
+ {
+ bDum = gmx_fio_ndo_int(fio, ir->opts.egp_flags,
+ ir->opts.ngener*ir->opts.ngener);
+ }
+
+ if (bRead && file_version < 26)
+ {
+ for (i = 0; i < ir->opts.ngtc; i++)
+ {
+ if (bSimAnn)
+ {
+ ir->opts.annealing[i] = eannSINGLE;
+ ir->opts.anneal_npoints[i] = 2;
+ snew(ir->opts.anneal_time[i], 2);
+ snew(ir->opts.anneal_temp[i], 2);
+ /* calculate the starting/ending temperatures from reft, zerotemptime, and nsteps */
+ finish_t = ir->init_t + ir->nsteps * ir->delta_t;
+ init_temp = ir->opts.ref_t[i]*(1-ir->init_t/zerotemptime);
+ finish_temp = ir->opts.ref_t[i]*(1-finish_t/zerotemptime);
+ ir->opts.anneal_time[i][0] = ir->init_t;
+ ir->opts.anneal_time[i][1] = finish_t;
+ ir->opts.anneal_temp[i][0] = init_temp;
+ ir->opts.anneal_temp[i][1] = finish_temp;
+ }
+ else
+ {
+ ir->opts.annealing[i] = eannNO;
+ ir->opts.anneal_npoints[i] = 0;
+ }
+ }
+ }
+ else
+ {
+ /* file version 26 or later */
+ /* First read the lists with annealing and npoints for each group */
+ bDum = gmx_fio_ndo_int(fio, ir->opts.annealing, ir->opts.ngtc);
+ bDum = gmx_fio_ndo_int(fio, ir->opts.anneal_npoints, ir->opts.ngtc);
+ for (j = 0; j < (ir->opts.ngtc); j++)
+ {
+ k = ir->opts.anneal_npoints[j];
+ if (bRead)
+ {
+ snew(ir->opts.anneal_time[j], k);
+ snew(ir->opts.anneal_temp[j], k);
+ }
+ bDum = gmx_fio_ndo_real(fio, ir->opts.anneal_time[j], k);
+ bDum = gmx_fio_ndo_real(fio, ir->opts.anneal_temp[j], k);
+ }
+ }
+ /* Walls */
+ if (file_version >= 45)
+ {
+ gmx_fio_do_int(fio, ir->nwall);
+ gmx_fio_do_int(fio, ir->wall_type);
+ if (file_version >= 50)
+ {
+ gmx_fio_do_real(fio, ir->wall_r_linpot);
+ }
+ else
+ {
+ ir->wall_r_linpot = -1;
+ }
+ gmx_fio_do_int(fio, ir->wall_atomtype[0]);
+ gmx_fio_do_int(fio, ir->wall_atomtype[1]);
+ gmx_fio_do_real(fio, ir->wall_density[0]);
+ gmx_fio_do_real(fio, ir->wall_density[1]);
+ gmx_fio_do_real(fio, ir->wall_ewald_zfac);
+ }
+ else
+ {
+ ir->nwall = 0;
+ ir->wall_type = 0;
+ ir->wall_atomtype[0] = -1;
+ ir->wall_atomtype[1] = -1;
+ ir->wall_density[0] = 0;
+ ir->wall_density[1] = 0;
+ ir->wall_ewald_zfac = 3;
+ }
+ /* Cosine stuff for electric fields */
+ for (j = 0; (j < DIM); j++)
+ {
+ gmx_fio_do_int(fio, ir->ex[j].n);
+ gmx_fio_do_int(fio, ir->et[j].n);
+ if (bRead)
+ {
+ snew(ir->ex[j].a, ir->ex[j].n);
+ snew(ir->ex[j].phi, ir->ex[j].n);
+ snew(ir->et[j].a, ir->et[j].n);
+ snew(ir->et[j].phi, ir->et[j].n);
+ }
+ bDum = gmx_fio_ndo_real(fio, ir->ex[j].a, ir->ex[j].n);
+ bDum = gmx_fio_ndo_real(fio, ir->ex[j].phi, ir->ex[j].n);
+ bDum = gmx_fio_ndo_real(fio, ir->et[j].a, ir->et[j].n);
+ bDum = gmx_fio_ndo_real(fio, ir->et[j].phi, ir->et[j].n);
+ }
+
+ /* QMMM stuff */
+ if (file_version >= 39)
+ {
+ gmx_fio_do_gmx_bool(fio, ir->bQMMM);
+ gmx_fio_do_int(fio, ir->QMMMscheme);
+ gmx_fio_do_real(fio, ir->scalefactor);
+ gmx_fio_do_int(fio, ir->opts.ngQM);
+ if (bRead)
+ {
+ snew(ir->opts.QMmethod, ir->opts.ngQM);
+ snew(ir->opts.QMbasis, ir->opts.ngQM);
+ snew(ir->opts.QMcharge, ir->opts.ngQM);
+ snew(ir->opts.QMmult, ir->opts.ngQM);
+ snew(ir->opts.bSH, ir->opts.ngQM);
+ snew(ir->opts.CASorbitals, ir->opts.ngQM);
+ snew(ir->opts.CASelectrons, ir->opts.ngQM);
+ snew(ir->opts.SAon, ir->opts.ngQM);
+ snew(ir->opts.SAoff, ir->opts.ngQM);
+ snew(ir->opts.SAsteps, ir->opts.ngQM);
+ snew(ir->opts.bOPT, ir->opts.ngQM);
+ snew(ir->opts.bTS, ir->opts.ngQM);
+ }
+ if (ir->opts.ngQM > 0)
+ {
+ bDum = gmx_fio_ndo_int(fio, ir->opts.QMmethod, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_int(fio, ir->opts.QMbasis, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_int(fio, ir->opts.QMcharge, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_int(fio, ir->opts.QMmult, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_gmx_bool(fio, ir->opts.bSH, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_int(fio, ir->opts.CASorbitals, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_int(fio, ir->opts.CASelectrons, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_real(fio, ir->opts.SAon, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_real(fio, ir->opts.SAoff, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_int(fio, ir->opts.SAsteps, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_gmx_bool(fio, ir->opts.bOPT, ir->opts.ngQM);
+ bDum = gmx_fio_ndo_gmx_bool(fio, ir->opts.bTS, ir->opts.ngQM);
+ }
+ /* end of QMMM stuff */
+ }
+}
+
+
+static void do_harm(t_fileio *fio, t_iparams *iparams, gmx_bool bRead)
+{
+ gmx_fio_do_real(fio, iparams->harmonic.rA);
+ gmx_fio_do_real(fio, iparams->harmonic.krA);
+ gmx_fio_do_real(fio, iparams->harmonic.rB);
+ gmx_fio_do_real(fio, iparams->harmonic.krB);
+}
+
+void do_iparams(t_fileio *fio, t_functype ftype, t_iparams *iparams,
+ gmx_bool bRead, int file_version)
+{
+ int idum;
+ gmx_bool bDum;
+ real rdum;
+
+ if (!bRead)
+ {
+ gmx_fio_set_comment(fio, interaction_function[ftype].name);
+ }
+ switch (ftype)
+ {
+ case F_ANGLES:
+ case F_G96ANGLES:
+ case F_BONDS:
+ case F_G96BONDS:
+ case F_HARMONIC:
+ case F_IDIHS:
+ do_harm(fio, iparams, bRead);
+ if ((ftype == F_ANGRES || ftype == F_ANGRESZ) && bRead)
+ {
+ /* Correct incorrect storage of parameters */
+ iparams->pdihs.phiB = iparams->pdihs.phiA;
+ iparams->pdihs.cpB = iparams->pdihs.cpA;
+ }
+ break;
+ case F_LINEAR_ANGLES:
+ gmx_fio_do_real(fio, iparams->linangle.klinA);
+ gmx_fio_do_real(fio, iparams->linangle.aA);
+ gmx_fio_do_real(fio, iparams->linangle.klinB);
+ gmx_fio_do_real(fio, iparams->linangle.aB);
+ break;
+ case F_FENEBONDS:
+ gmx_fio_do_real(fio, iparams->fene.bm);
+ gmx_fio_do_real(fio, iparams->fene.kb);
+ break;
+ case F_RESTRBONDS:
+ gmx_fio_do_real(fio, iparams->restraint.lowA);
+ gmx_fio_do_real(fio, iparams->restraint.up1A);
+ gmx_fio_do_real(fio, iparams->restraint.up2A);
+ gmx_fio_do_real(fio, iparams->restraint.kA);
+ gmx_fio_do_real(fio, iparams->restraint.lowB);
+ gmx_fio_do_real(fio, iparams->restraint.up1B);
+ gmx_fio_do_real(fio, iparams->restraint.up2B);
+ gmx_fio_do_real(fio, iparams->restraint.kB);
+ break;
+ case F_TABBONDS:
+ case F_TABBONDSNC:
+ case F_TABANGLES:
+ case F_TABDIHS:
+ gmx_fio_do_real(fio, iparams->tab.kA);
+ gmx_fio_do_int(fio, iparams->tab.table);
+ gmx_fio_do_real(fio, iparams->tab.kB);
+ break;
+ case F_CROSS_BOND_BONDS:
+ gmx_fio_do_real(fio, iparams->cross_bb.r1e);
+ gmx_fio_do_real(fio, iparams->cross_bb.r2e);
+ gmx_fio_do_real(fio, iparams->cross_bb.krr);
+ break;
+ case F_CROSS_BOND_ANGLES:
+ gmx_fio_do_real(fio, iparams->cross_ba.r1e);
+ gmx_fio_do_real(fio, iparams->cross_ba.r2e);
+ gmx_fio_do_real(fio, iparams->cross_ba.r3e);
+ gmx_fio_do_real(fio, iparams->cross_ba.krt);
+ break;
+ case F_UREY_BRADLEY:
+ gmx_fio_do_real(fio, iparams->u_b.thetaA);
+ gmx_fio_do_real(fio, iparams->u_b.kthetaA);
+ gmx_fio_do_real(fio, iparams->u_b.r13A);
+ gmx_fio_do_real(fio, iparams->u_b.kUBA);
+ if (file_version >= 79)
+ {
+ gmx_fio_do_real(fio, iparams->u_b.thetaB);
+ gmx_fio_do_real(fio, iparams->u_b.kthetaB);
+ gmx_fio_do_real(fio, iparams->u_b.r13B);
+ gmx_fio_do_real(fio, iparams->u_b.kUBB);
+ }
+ else
+ {
+ iparams->u_b.thetaB = iparams->u_b.thetaA;
+ iparams->u_b.kthetaB = iparams->u_b.kthetaA;
+ iparams->u_b.r13B = iparams->u_b.r13A;
+ iparams->u_b.kUBB = iparams->u_b.kUBA;
+ }
+ break;
+ case F_QUARTIC_ANGLES:
+ gmx_fio_do_real(fio, iparams->qangle.theta);
+ bDum = gmx_fio_ndo_real(fio, iparams->qangle.c, 5);
+ break;
+ case F_BHAM:
+ gmx_fio_do_real(fio, iparams->bham.a);
+ gmx_fio_do_real(fio, iparams->bham.b);
+ gmx_fio_do_real(fio, iparams->bham.c);
+ break;
+ case F_MORSE:
+ gmx_fio_do_real(fio, iparams->morse.b0A);
+ gmx_fio_do_real(fio, iparams->morse.cbA);
+ gmx_fio_do_real(fio, iparams->morse.betaA);
+ if (file_version >= 79)
+ {
+ gmx_fio_do_real(fio, iparams->morse.b0B);
+ gmx_fio_do_real(fio, iparams->morse.cbB);
+ gmx_fio_do_real(fio, iparams->morse.betaB);
+ }
+ else
+ {
+ iparams->morse.b0B = iparams->morse.b0A;
+ iparams->morse.cbB = iparams->morse.cbA;
+ iparams->morse.betaB = iparams->morse.betaA;
+ }
+ break;
+ case F_CUBICBONDS:
+ gmx_fio_do_real(fio, iparams->cubic.b0);
+ gmx_fio_do_real(fio, iparams->cubic.kb);
+ gmx_fio_do_real(fio, iparams->cubic.kcub);
+ break;
+ case F_CONNBONDS:
+ break;
+ case F_POLARIZATION:
+ gmx_fio_do_real(fio, iparams->polarize.alpha);
+ break;
+ case F_ANHARM_POL:
+ gmx_fio_do_real(fio, iparams->anharm_polarize.alpha);
+ gmx_fio_do_real(fio, iparams->anharm_polarize.drcut);
+ gmx_fio_do_real(fio, iparams->anharm_polarize.khyp);
+ break;
+ case F_WATER_POL:
+ if (file_version < 31)
+ {
+ gmx_fatal(FARGS, "Old tpr files with water_polarization not supported. Make a new.");
+ }
+ gmx_fio_do_real(fio, iparams->wpol.al_x);
+ gmx_fio_do_real(fio, iparams->wpol.al_y);
+ gmx_fio_do_real(fio, iparams->wpol.al_z);
+ gmx_fio_do_real(fio, iparams->wpol.rOH);
+ gmx_fio_do_real(fio, iparams->wpol.rHH);
+ gmx_fio_do_real(fio, iparams->wpol.rOD);
+ break;
+ case F_THOLE_POL:
+ gmx_fio_do_real(fio, iparams->thole.a);
+ gmx_fio_do_real(fio, iparams->thole.alpha1);
+ gmx_fio_do_real(fio, iparams->thole.alpha2);
+ gmx_fio_do_real(fio, iparams->thole.rfac);
+ break;
+ case F_LJ:
+ gmx_fio_do_real(fio, iparams->lj.c6);
+ gmx_fio_do_real(fio, iparams->lj.c12);
+ break;
+ case F_LJ14:
+ gmx_fio_do_real(fio, iparams->lj14.c6A);
+ gmx_fio_do_real(fio, iparams->lj14.c12A);
+ gmx_fio_do_real(fio, iparams->lj14.c6B);
+ gmx_fio_do_real(fio, iparams->lj14.c12B);
+ break;
+ case F_LJC14_Q:
+ gmx_fio_do_real(fio, iparams->ljc14.fqq);
+ gmx_fio_do_real(fio, iparams->ljc14.qi);
+ gmx_fio_do_real(fio, iparams->ljc14.qj);
+ gmx_fio_do_real(fio, iparams->ljc14.c6);
+ gmx_fio_do_real(fio, iparams->ljc14.c12);
+ break;
+ case F_LJC_PAIRS_NB:
+ gmx_fio_do_real(fio, iparams->ljcnb.qi);
+ gmx_fio_do_real(fio, iparams->ljcnb.qj);
+ gmx_fio_do_real(fio, iparams->ljcnb.c6);
+ gmx_fio_do_real(fio, iparams->ljcnb.c12);
+ break;
+ case F_PDIHS:
+ case F_PIDIHS:
+ case F_ANGRES:
+ case F_ANGRESZ:
+ gmx_fio_do_real(fio, iparams->pdihs.phiA);
+ gmx_fio_do_real(fio, iparams->pdihs.cpA);
+ if ((ftype == F_ANGRES || ftype == F_ANGRESZ) && file_version < 42)
+ {
+ /* Read the incorrectly stored multiplicity */
+ gmx_fio_do_real(fio, iparams->harmonic.rB);
+ gmx_fio_do_real(fio, iparams->harmonic.krB);
+ iparams->pdihs.phiB = iparams->pdihs.phiA;
+ iparams->pdihs.cpB = iparams->pdihs.cpA;
+ }
+ else
+ {
+ gmx_fio_do_real(fio, iparams->pdihs.phiB);
+ gmx_fio_do_real(fio, iparams->pdihs.cpB);
+ gmx_fio_do_int(fio, iparams->pdihs.mult);
+ }
+ break;
+ case F_DISRES:
+ gmx_fio_do_int(fio, iparams->disres.label);
+ gmx_fio_do_int(fio, iparams->disres.type);
+ gmx_fio_do_real(fio, iparams->disres.low);
+ gmx_fio_do_real(fio, iparams->disres.up1);
+ gmx_fio_do_real(fio, iparams->disres.up2);
+ gmx_fio_do_real(fio, iparams->disres.kfac);
+ break;
+ case F_ORIRES:
+ gmx_fio_do_int(fio, iparams->orires.ex);
+ gmx_fio_do_int(fio, iparams->orires.label);
+ gmx_fio_do_int(fio, iparams->orires.power);
+ gmx_fio_do_real(fio, iparams->orires.c);
+ gmx_fio_do_real(fio, iparams->orires.obs);
+ gmx_fio_do_real(fio, iparams->orires.kfac);
+ break;
+ case F_DIHRES:
- if (file_version >= 72)
++ if (file_version < 82)
+ {
+ gmx_fio_do_int(fio, idum);
+ gmx_fio_do_int(fio, idum);
+ }
+ gmx_fio_do_real(fio, iparams->dihres.phiA);
+ gmx_fio_do_real(fio, iparams->dihres.dphiA);
+ gmx_fio_do_real(fio, iparams->dihres.kfacA);
++ if (file_version >= 82)
+ {
+ gmx_fio_do_real(fio, iparams->dihres.phiB);
+ gmx_fio_do_real(fio, iparams->dihres.dphiB);
+ gmx_fio_do_real(fio, iparams->dihres.kfacB);
+ }
+ else
+ {
+ iparams->dihres.phiB = iparams->dihres.phiA;
+ iparams->dihres.dphiB = iparams->dihres.dphiA;
+ iparams->dihres.kfacB = iparams->dihres.kfacA;
+ }
+ break;
+ case F_POSRES:
+ gmx_fio_do_rvec(fio, iparams->posres.pos0A);
+ gmx_fio_do_rvec(fio, iparams->posres.fcA);
+ if (bRead && file_version < 27)
+ {
+ copy_rvec(iparams->posres.pos0A, iparams->posres.pos0B);
+ copy_rvec(iparams->posres.fcA, iparams->posres.fcB);
+ }
+ else
+ {
+ gmx_fio_do_rvec(fio, iparams->posres.pos0B);
+ gmx_fio_do_rvec(fio, iparams->posres.fcB);
+ }
+ break;
+ case F_FBPOSRES:
+ gmx_fio_do_int(fio, iparams->fbposres.geom);
+ gmx_fio_do_rvec(fio, iparams->fbposres.pos0);
+ gmx_fio_do_real(fio, iparams->fbposres.r);
+ gmx_fio_do_real(fio, iparams->fbposres.k);
+ break;
+ case F_RBDIHS:
+ bDum = gmx_fio_ndo_real(fio, iparams->rbdihs.rbcA, NR_RBDIHS);
+ if (file_version >= 25)
+ {
+ bDum = gmx_fio_ndo_real(fio, iparams->rbdihs.rbcB, NR_RBDIHS);
+ }
+ break;
+ case F_FOURDIHS:
+ /* Fourier dihedrals are internally represented
+ * as Ryckaert-Bellemans since those are faster to compute.
+ */
+ bDum = gmx_fio_ndo_real(fio, iparams->rbdihs.rbcA, NR_RBDIHS);
+ bDum = gmx_fio_ndo_real(fio, iparams->rbdihs.rbcB, NR_RBDIHS);
+ break;
+ case F_CONSTR:
+ case F_CONSTRNC:
+ gmx_fio_do_real(fio, iparams->constr.dA);
+ gmx_fio_do_real(fio, iparams->constr.dB);
+ break;
+ case F_SETTLE:
+ gmx_fio_do_real(fio, iparams->settle.doh);
+ gmx_fio_do_real(fio, iparams->settle.dhh);
+ break;
+ case F_VSITE2:
+ gmx_fio_do_real(fio, iparams->vsite.a);
+ break;
+ case F_VSITE3:
+ case F_VSITE3FD:
+ case F_VSITE3FAD:
+ gmx_fio_do_real(fio, iparams->vsite.a);
+ gmx_fio_do_real(fio, iparams->vsite.b);
+ break;
+ case F_VSITE3OUT:
+ case F_VSITE4FD:
+ case F_VSITE4FDN:
+ gmx_fio_do_real(fio, iparams->vsite.a);
+ gmx_fio_do_real(fio, iparams->vsite.b);
+ gmx_fio_do_real(fio, iparams->vsite.c);
+ break;
+ case F_VSITEN:
+ gmx_fio_do_int(fio, iparams->vsiten.n);
+ gmx_fio_do_real(fio, iparams->vsiten.a);
+ break;
+ case F_GB12:
+ case F_GB13:
+ case F_GB14:
+ /* We got rid of some parameters in version 68 */
+ if (bRead && file_version < 68)
+ {
+ gmx_fio_do_real(fio, rdum);
+ gmx_fio_do_real(fio, rdum);
+ gmx_fio_do_real(fio, rdum);
+ gmx_fio_do_real(fio, rdum);
+ }
+ gmx_fio_do_real(fio, iparams->gb.sar);
+ gmx_fio_do_real(fio, iparams->gb.st);
+ gmx_fio_do_real(fio, iparams->gb.pi);
+ gmx_fio_do_real(fio, iparams->gb.gbr);
+ gmx_fio_do_real(fio, iparams->gb.bmlt);
+ break;
+ case F_CMAP:
+ gmx_fio_do_int(fio, iparams->cmap.cmapA);
+ gmx_fio_do_int(fio, iparams->cmap.cmapB);
+ break;
+ default:
+ gmx_fatal(FARGS, "unknown function type %d (%s) in %s line %d",
+ ftype, interaction_function[ftype].name, __FILE__, __LINE__);
+ }
+ if (!bRead)
+ {
+ gmx_fio_unset_comment(fio);
+ }
+}
+
+static void do_ilist(t_fileio *fio, t_ilist *ilist, gmx_bool bRead, int file_version,
+ int ftype)
+{
+ int i, k, idum;
+ gmx_bool bDum = TRUE;
+
+ if (!bRead)
+ {
+ gmx_fio_set_comment(fio, interaction_function[ftype].name);
+ }
+ if (file_version < 44)
+ {
+ for (i = 0; i < MAXNODES; i++)
+ {
+ gmx_fio_do_int(fio, idum);
+ }
+ }
+ gmx_fio_do_int(fio, ilist->nr);
+ if (bRead)
+ {
+ snew(ilist->iatoms, ilist->nr);
+ }
+ bDum = gmx_fio_ndo_int(fio, ilist->iatoms, ilist->nr);
+ if (!bRead)
+ {
+ gmx_fio_unset_comment(fio);
+ }
+}
+
+static void do_ffparams(t_fileio *fio, gmx_ffparams_t *ffparams,
+ gmx_bool bRead, int file_version)
+{
+ int idum, i, j;
+ gmx_bool bDum = TRUE;
+ unsigned int k;
+
+ gmx_fio_do_int(fio, ffparams->atnr);
+ if (file_version < 57)
+ {
+ gmx_fio_do_int(fio, idum);
+ }
+ gmx_fio_do_int(fio, ffparams->ntypes);
+ if (bRead && debug)
+ {
+ fprintf(debug, "ffparams->atnr = %d, ntypes = %d\n",
+ ffparams->atnr, ffparams->ntypes);
+ }
+ if (bRead)
+ {
+ snew(ffparams->functype, ffparams->ntypes);
+ snew(ffparams->iparams, ffparams->ntypes);
+ }
+ /* Read/write all the function types */
+ bDum = gmx_fio_ndo_int(fio, ffparams->functype, ffparams->ntypes);
+ if (bRead && debug)
+ {
+ pr_ivec(debug, 0, "functype", ffparams->functype, ffparams->ntypes, TRUE);
+ }
+
+ if (file_version >= 66)
+ {
+ gmx_fio_do_double(fio, ffparams->reppow);
+ }
+ else
+ {
+ ffparams->reppow = 12.0;
+ }
+
+ if (file_version >= 57)
+ {
+ gmx_fio_do_real(fio, ffparams->fudgeQQ);
+ }
+
+ /* Check whether all these function types are supported by the code.
+ * In practice the code is backwards compatible, which means that the
+ * numbering may have to be altered from old numbering to new numbering
+ */
+ for (i = 0; (i < ffparams->ntypes); i++)
+ {
+ if (bRead)
+ {
+ /* Loop over file versions */
+ for (k = 0; (k < NFTUPD); k++)
+ {
+ /* Compare the read file_version to the update table */
+ if ((file_version < ftupd[k].fvnr) &&
+ (ffparams->functype[i] >= ftupd[k].ftype))
+ {
+ ffparams->functype[i] += 1;
+ if (debug)
+ {
+ fprintf(debug, "Incrementing function type %d to %d (due to %s)\n",
+ i, ffparams->functype[i],
+ interaction_function[ftupd[k].ftype].longname);
+ fflush(debug);
+ }
+ }
+ }
+ }
+
+ do_iparams(fio, ffparams->functype[i], &ffparams->iparams[i], bRead,
+ file_version);
+ if (bRead && debug)
+ {
+ pr_iparams(debug, ffparams->functype[i], &ffparams->iparams[i]);
+ }
+ }
+}
+
+static void add_settle_atoms(t_ilist *ilist)
+{
+ int i;
+
+ /* Settle used to only store the first atom: add the other two */
+ srenew(ilist->iatoms, 2*ilist->nr);
+ for (i = ilist->nr/2-1; i >= 0; i--)
+ {
+ ilist->iatoms[4*i+0] = ilist->iatoms[2*i+0];
+ ilist->iatoms[4*i+1] = ilist->iatoms[2*i+1];
+ ilist->iatoms[4*i+2] = ilist->iatoms[2*i+1] + 1;
+ ilist->iatoms[4*i+3] = ilist->iatoms[2*i+1] + 2;
+ }
+ ilist->nr = 2*ilist->nr;
+}
+
+static void do_ilists(t_fileio *fio, t_ilist *ilist, gmx_bool bRead,
+ int file_version)
+{
+ int i, j, renum[F_NRE];
+ gmx_bool bDum = TRUE, bClear;
+ unsigned int k;
+
+ for (j = 0; (j < F_NRE); j++)
+ {
+ bClear = FALSE;
+ if (bRead)
+ {
+ for (k = 0; k < NFTUPD; k++)
+ {
+ if ((file_version < ftupd[k].fvnr) && (j == ftupd[k].ftype))
+ {
+ bClear = TRUE;
+ }
+ }
+ }
+ if (bClear)
+ {
+ ilist[j].nr = 0;
+ ilist[j].iatoms = NULL;
+ }
+ else
+ {
+ do_ilist(fio, &ilist[j], bRead, file_version, j);
+ if (file_version < 78 && j == F_SETTLE && ilist[j].nr > 0)
+ {
+ add_settle_atoms(&ilist[j]);
+ }
+ }
+ /*
+ if (bRead && gmx_debug_at)
+ pr_ilist(debug,0,interaction_function[j].longname,
+ functype,&ilist[j],TRUE);
+ */
+ }
+}
+
+static void do_idef(t_fileio *fio, gmx_ffparams_t *ffparams, gmx_moltype_t *molt,
+ gmx_bool bRead, int file_version)
+{
+ do_ffparams(fio, ffparams, bRead, file_version);
+
+ if (file_version >= 54)
+ {
+ gmx_fio_do_real(fio, ffparams->fudgeQQ);
+ }
+
+ do_ilists(fio, molt->ilist, bRead, file_version);
+}
+
+static void do_block(t_fileio *fio, t_block *block, gmx_bool bRead, int file_version)
+{
+ int i, idum, dum_nra, *dum_a;
+ gmx_bool bDum = TRUE;
+
+ if (file_version < 44)
+ {
+ for (i = 0; i < MAXNODES; i++)
+ {
+ gmx_fio_do_int(fio, idum);
+ }
+ }
+ gmx_fio_do_int(fio, block->nr);
+ if (file_version < 51)
+ {
+ gmx_fio_do_int(fio, dum_nra);
+ }
+ if (bRead)
+ {
+ if ((block->nalloc_index > 0) && (NULL != block->index))
+ {
+ sfree(block->index);
+ }
+ block->nalloc_index = block->nr+1;
+ snew(block->index, block->nalloc_index);
+ }
+ bDum = gmx_fio_ndo_int(fio, block->index, block->nr+1);
+
+ if (file_version < 51 && dum_nra > 0)
+ {
+ snew(dum_a, dum_nra);
+ bDum = gmx_fio_ndo_int(fio, dum_a, dum_nra);
+ sfree(dum_a);
+ }
+}
+
+static void do_blocka(t_fileio *fio, t_blocka *block, gmx_bool bRead,
+ int file_version)
+{
+ int i, idum;
+ gmx_bool bDum = TRUE;
+
+ if (file_version < 44)
+ {
+ for (i = 0; i < MAXNODES; i++)
+ {
+ gmx_fio_do_int(fio, idum);
+ }
+ }
+ gmx_fio_do_int(fio, block->nr);
+ gmx_fio_do_int(fio, block->nra);
+ if (bRead)
+ {
+ block->nalloc_index = block->nr+1;
+ snew(block->index, block->nalloc_index);
+ block->nalloc_a = block->nra;
+ snew(block->a, block->nalloc_a);
+ }
+ bDum = gmx_fio_ndo_int(fio, block->index, block->nr+1);
+ bDum = gmx_fio_ndo_int(fio, block->a, block->nra);
+}
+
+static void do_atom(t_fileio *fio, t_atom *atom, int ngrp, gmx_bool bRead,
+ int file_version, gmx_groups_t *groups, int atnr)
+{
+ int i, myngrp;
+
+ gmx_fio_do_real(fio, atom->m);
+ gmx_fio_do_real(fio, atom->q);
+ gmx_fio_do_real(fio, atom->mB);
+ gmx_fio_do_real(fio, atom->qB);
+ gmx_fio_do_ushort(fio, atom->type);
+ gmx_fio_do_ushort(fio, atom->typeB);
+ gmx_fio_do_int(fio, atom->ptype);
+ gmx_fio_do_int(fio, atom->resind);
+ if (file_version >= 52)
+ {
+ gmx_fio_do_int(fio, atom->atomnumber);
+ }
+ else if (bRead)
+ {
+ atom->atomnumber = NOTSET;
+ }
+ if (file_version < 23)
+ {
+ myngrp = 8;
+ }
+ else if (file_version < 39)
+ {
+ myngrp = 9;
+ }
+ else
+ {
+ myngrp = ngrp;
+ }
+
+ if (file_version < 57)
+ {
+ unsigned char uchar[egcNR];
+ gmx_fio_ndo_uchar(fio, uchar, myngrp);
+ for (i = myngrp; (i < ngrp); i++)
+ {
+ uchar[i] = 0;
+ }
+ /* Copy the old data format to the groups struct */
+ for (i = 0; i < ngrp; i++)
+ {
+ groups->grpnr[i][atnr] = uchar[i];
+ }
+ }
+}
+
+static void do_grps(t_fileio *fio, int ngrp, t_grps grps[], gmx_bool bRead,
+ int file_version)
+{
+ int i, j, myngrp;
+ gmx_bool bDum = TRUE;
+
+ if (file_version < 23)
+ {
+ myngrp = 8;
+ }
+ else if (file_version < 39)
+ {
+ myngrp = 9;
+ }
+ else
+ {
+ myngrp = ngrp;
+ }
+
+ for (j = 0; (j < ngrp); j++)
+ {
+ if (j < myngrp)
+ {
+ gmx_fio_do_int(fio, grps[j].nr);
+ if (bRead)
+ {
+ snew(grps[j].nm_ind, grps[j].nr);
+ }
+ bDum = gmx_fio_ndo_int(fio, grps[j].nm_ind, grps[j].nr);
+ }
+ else
+ {
+ grps[j].nr = 1;
+ snew(grps[j].nm_ind, grps[j].nr);
+ }
+ }
+}
+
+static void do_symstr(t_fileio *fio, char ***nm, gmx_bool bRead, t_symtab *symtab)
+{
+ int ls;
+
+ if (bRead)
+ {
+ gmx_fio_do_int(fio, ls);
+ *nm = get_symtab_handle(symtab, ls);
+ }
+ else
+ {
+ ls = lookup_symtab(symtab, *nm);
+ gmx_fio_do_int(fio, ls);
+ }
+}
+
+static void do_strstr(t_fileio *fio, int nstr, char ***nm, gmx_bool bRead,
+ t_symtab *symtab)
+{
+ int j;
+
+ for (j = 0; (j < nstr); j++)
+ {
+ do_symstr(fio, &(nm[j]), bRead, symtab);
+ }
+}
+
+static void do_resinfo(t_fileio *fio, int n, t_resinfo *ri, gmx_bool bRead,
+ t_symtab *symtab, int file_version)
+{
+ int j;
+
+ for (j = 0; (j < n); j++)
+ {
+ do_symstr(fio, &(ri[j].name), bRead, symtab);
+ if (file_version >= 63)
+ {
+ gmx_fio_do_int(fio, ri[j].nr);
+ gmx_fio_do_uchar(fio, ri[j].ic);
+ }
+ else
+ {
+ ri[j].nr = j + 1;
+ ri[j].ic = ' ';
+ }
+ }
+}
+
+static void do_atoms(t_fileio *fio, t_atoms *atoms, gmx_bool bRead, t_symtab *symtab,
+ int file_version,
+ gmx_groups_t *groups)
+{
+ int i;
+
+ gmx_fio_do_int(fio, atoms->nr);
+ gmx_fio_do_int(fio, atoms->nres);
+ if (file_version < 57)
+ {
+ gmx_fio_do_int(fio, groups->ngrpname);
+ for (i = 0; i < egcNR; i++)
+ {
+ groups->ngrpnr[i] = atoms->nr;
+ snew(groups->grpnr[i], groups->ngrpnr[i]);
+ }
+ }
+ if (bRead)
+ {
+ snew(atoms->atom, atoms->nr);
+ snew(atoms->atomname, atoms->nr);
+ snew(atoms->atomtype, atoms->nr);
+ snew(atoms->atomtypeB, atoms->nr);
+ snew(atoms->resinfo, atoms->nres);
+ if (file_version < 57)
+ {
+ snew(groups->grpname, groups->ngrpname);
+ }
+ atoms->pdbinfo = NULL;
+ }
+ for (i = 0; (i < atoms->nr); i++)
+ {
+ do_atom(fio, &atoms->atom[i], egcNR, bRead, file_version, groups, i);
+ }
+ do_strstr(fio, atoms->nr, atoms->atomname, bRead, symtab);
+ if (bRead && (file_version <= 20))
+ {
+ for (i = 0; i < atoms->nr; i++)
+ {
+ atoms->atomtype[i] = put_symtab(symtab, "?");
+ atoms->atomtypeB[i] = put_symtab(symtab, "?");
+ }
+ }
+ else
+ {
+ do_strstr(fio, atoms->nr, atoms->atomtype, bRead, symtab);
+ do_strstr(fio, atoms->nr, atoms->atomtypeB, bRead, symtab);
+ }
+ do_resinfo(fio, atoms->nres, atoms->resinfo, bRead, symtab, file_version);
+
+ if (file_version < 57)
+ {
+ do_strstr(fio, groups->ngrpname, groups->grpname, bRead, symtab);
+
+ do_grps(fio, egcNR, groups->grps, bRead, file_version);
+ }
+}
+
+static void do_groups(t_fileio *fio, gmx_groups_t *groups,
+ gmx_bool bRead, t_symtab *symtab,
+ int file_version)
+{
+ int g, n, i;
+ gmx_bool bDum = TRUE;
+
+ do_grps(fio, egcNR, groups->grps, bRead, file_version);
+ gmx_fio_do_int(fio, groups->ngrpname);
+ if (bRead)
+ {
+ snew(groups->grpname, groups->ngrpname);
+ }
+ do_strstr(fio, groups->ngrpname, groups->grpname, bRead, symtab);
+ for (g = 0; g < egcNR; g++)
+ {
+ gmx_fio_do_int(fio, groups->ngrpnr[g]);
+ if (groups->ngrpnr[g] == 0)
+ {
+ if (bRead)
+ {
+ groups->grpnr[g] = NULL;
+ }
+ }
+ else
+ {
+ if (bRead)
+ {
+ snew(groups->grpnr[g], groups->ngrpnr[g]);
+ }
+ bDum = gmx_fio_ndo_uchar(fio, groups->grpnr[g], groups->ngrpnr[g]);
+ }
+ }
+}
+
+static void do_atomtypes(t_fileio *fio, t_atomtypes *atomtypes, gmx_bool bRead,
+ t_symtab *symtab, int file_version)
+{
+ int i, j;
+ gmx_bool bDum = TRUE;
+
+ if (file_version > 25)
+ {
+ gmx_fio_do_int(fio, atomtypes->nr);
+ j = atomtypes->nr;
+ if (bRead)
+ {
+ snew(atomtypes->radius, j);
+ snew(atomtypes->vol, j);
+ snew(atomtypes->surftens, j);
+ snew(atomtypes->atomnumber, j);
+ snew(atomtypes->gb_radius, j);
+ snew(atomtypes->S_hct, j);
+ }
+ bDum = gmx_fio_ndo_real(fio, atomtypes->radius, j);
+ bDum = gmx_fio_ndo_real(fio, atomtypes->vol, j);
+ bDum = gmx_fio_ndo_real(fio, atomtypes->surftens, j);
+ if (file_version >= 40)
+ {
+ bDum = gmx_fio_ndo_int(fio, atomtypes->atomnumber, j);
+ }
+ if (file_version >= 60)
+ {
+ bDum = gmx_fio_ndo_real(fio, atomtypes->gb_radius, j);
+ bDum = gmx_fio_ndo_real(fio, atomtypes->S_hct, j);
+ }
+ }
+ else
+ {
+ /* File versions prior to 26 cannot do GBSA,
+ * so they dont use this structure
+ */
+ atomtypes->nr = 0;
+ atomtypes->radius = NULL;
+ atomtypes->vol = NULL;
+ atomtypes->surftens = NULL;
+ atomtypes->atomnumber = NULL;
+ atomtypes->gb_radius = NULL;
+ atomtypes->S_hct = NULL;
+ }
+}
+
+static void do_symtab(t_fileio *fio, t_symtab *symtab, gmx_bool bRead)
+{
+ int i, nr;
+ t_symbuf *symbuf;
+ char buf[STRLEN];
+
+ gmx_fio_do_int(fio, symtab->nr);
+ nr = symtab->nr;
+ if (bRead)
+ {
+ snew(symtab->symbuf, 1);
+ symbuf = symtab->symbuf;
+ symbuf->bufsize = nr;
+ snew(symbuf->buf, nr);
+ for (i = 0; (i < nr); i++)
+ {
+ gmx_fio_do_string(fio, buf);
+ symbuf->buf[i] = strdup(buf);
+ }
+ }
+ else
+ {
+ symbuf = symtab->symbuf;
+ while (symbuf != NULL)
+ {
+ for (i = 0; (i < symbuf->bufsize) && (i < nr); i++)
+ {
+ gmx_fio_do_string(fio, symbuf->buf[i]);
+ }
+ nr -= i;
+ symbuf = symbuf->next;
+ }
+ if (nr != 0)
+ {
+ gmx_fatal(FARGS, "nr of symtab strings left: %d", nr);
+ }
+ }
+}
+
+static void do_cmap(t_fileio *fio, gmx_cmap_t *cmap_grid, gmx_bool bRead)
+{
+ int i, j, ngrid, gs, nelem;
+
+ gmx_fio_do_int(fio, cmap_grid->ngrid);
+ gmx_fio_do_int(fio, cmap_grid->grid_spacing);
+
+ ngrid = cmap_grid->ngrid;
+ gs = cmap_grid->grid_spacing;
+ nelem = gs * gs;
+
+ if (bRead)
+ {
+ snew(cmap_grid->cmapdata, ngrid);
+
+ for (i = 0; i < cmap_grid->ngrid; i++)
+ {
+ snew(cmap_grid->cmapdata[i].cmap, 4*nelem);
+ }
+ }
+
+ for (i = 0; i < cmap_grid->ngrid; i++)
+ {
+ for (j = 0; j < nelem; j++)
+ {
+ gmx_fio_do_real(fio, cmap_grid->cmapdata[i].cmap[j*4]);
+ gmx_fio_do_real(fio, cmap_grid->cmapdata[i].cmap[j*4+1]);
+ gmx_fio_do_real(fio, cmap_grid->cmapdata[i].cmap[j*4+2]);
+ gmx_fio_do_real(fio, cmap_grid->cmapdata[i].cmap[j*4+3]);
+ }
+ }
+}
+
+
+void tpx_make_chain_identifiers(t_atoms *atoms, t_block *mols)
+{
+ int m, a, a0, a1, r;
+ char c, chainid;
+ int chainnum;
+
+ /* We always assign a new chain number, but save the chain id characters
+ * for larger molecules.
+ */
+#define CHAIN_MIN_ATOMS 15
+
+ chainnum = 0;
+ chainid = 'A';
+ for (m = 0; m < mols->nr; m++)
+ {
+ a0 = mols->index[m];
+ a1 = mols->index[m+1];
+ if ((a1-a0 >= CHAIN_MIN_ATOMS) && (chainid <= 'Z'))
+ {
+ c = chainid;
+ chainid++;
+ }
+ else
+ {
+ c = ' ';
+ }
+ for (a = a0; a < a1; a++)
+ {
+ atoms->resinfo[atoms->atom[a].resind].chainnum = chainnum;
+ atoms->resinfo[atoms->atom[a].resind].chainid = c;
+ }
+ chainnum++;
+ }
+
+ /* Blank out the chain id if there was only one chain */
+ if (chainid == 'B')
+ {
+ for (r = 0; r < atoms->nres; r++)
+ {
+ atoms->resinfo[r].chainid = ' ';
+ }
+ }
+}
+
+static void do_moltype(t_fileio *fio, gmx_moltype_t *molt, gmx_bool bRead,
+ t_symtab *symtab, int file_version,
+ gmx_groups_t *groups)
+{
+ int i;
+
+ if (file_version >= 57)
+ {
+ do_symstr(fio, &(molt->name), bRead, symtab);
+ }
+
+ do_atoms(fio, &molt->atoms, bRead, symtab, file_version, groups);
+
+ if (bRead && gmx_debug_at)
+ {
+ pr_atoms(debug, 0, "atoms", &molt->atoms, TRUE);
+ }
+
+ if (file_version >= 57)
+ {
+ do_ilists(fio, molt->ilist, bRead, file_version);
+
+ do_block(fio, &molt->cgs, bRead, file_version);
+ if (bRead && gmx_debug_at)
+ {
+ pr_block(debug, 0, "cgs", &molt->cgs, TRUE);
+ }
+ }
+
+ /* This used to be in the atoms struct */
+ do_blocka(fio, &molt->excls, bRead, file_version);
+}
+
+static void do_molblock(t_fileio *fio, gmx_molblock_t *molb, gmx_bool bRead,
+ int file_version)
+{
+ int i;
+
+ gmx_fio_do_int(fio, molb->type);
+ gmx_fio_do_int(fio, molb->nmol);
+ gmx_fio_do_int(fio, molb->natoms_mol);
+ /* Position restraint coordinates */
+ gmx_fio_do_int(fio, molb->nposres_xA);
+ if (molb->nposres_xA > 0)
+ {
+ if (bRead)
+ {
+ snew(molb->posres_xA, molb->nposres_xA);
+ }
+ gmx_fio_ndo_rvec(fio, molb->posres_xA, molb->nposres_xA);
+ }
+ gmx_fio_do_int(fio, molb->nposres_xB);
+ if (molb->nposres_xB > 0)
+ {
+ if (bRead)
+ {
+ snew(molb->posres_xB, molb->nposres_xB);
+ }
+ gmx_fio_ndo_rvec(fio, molb->posres_xB, molb->nposres_xB);
+ }
+
+}
+
+static t_block mtop_mols(gmx_mtop_t *mtop)
+{
+ int mb, m, a, mol;
+ t_block mols;
+
+ mols.nr = 0;
+ for (mb = 0; mb < mtop->nmolblock; mb++)
+ {
+ mols.nr += mtop->molblock[mb].nmol;
+ }
+ mols.nalloc_index = mols.nr + 1;
+ snew(mols.index, mols.nalloc_index);
+
+ a = 0;
+ m = 0;
+ mols.index[m] = a;
+ for (mb = 0; mb < mtop->nmolblock; mb++)
+ {
+ for (mol = 0; mol < mtop->molblock[mb].nmol; mol++)
+ {
+ a += mtop->molblock[mb].natoms_mol;
+ m++;
+ mols.index[m] = a;
+ }
+ }
+
+ return mols;
+}
+
+static void add_posres_molblock(gmx_mtop_t *mtop)
+{
+ t_ilist *il, *ilfb;
+ int am, i, mol, a;
+ gmx_bool bFE;
+ gmx_molblock_t *molb;
+ t_iparams *ip;
+
+ /* posres reference positions are stored in ip->posres (if present) and
+ in ip->fbposres (if present). If normal and flat-bottomed posres are present,
+ posres.pos0A are identical to fbposres.pos0. */
+ il = &mtop->moltype[0].ilist[F_POSRES];
+ ilfb = &mtop->moltype[0].ilist[F_FBPOSRES];
+ if (il->nr == 0 && ilfb->nr == 0)
+ {
+ return;
+ }
+ am = 0;
+ bFE = FALSE;
+ for (i = 0; i < il->nr; i += 2)
+ {
+ ip = &mtop->ffparams.iparams[il->iatoms[i]];
+ am = max(am, il->iatoms[i+1]);
+ if (ip->posres.pos0B[XX] != ip->posres.pos0A[XX] ||
+ ip->posres.pos0B[YY] != ip->posres.pos0A[YY] ||
+ ip->posres.pos0B[ZZ] != ip->posres.pos0A[ZZ])
+ {
+ bFE = TRUE;
+ }
+ }
+ /* This loop is required if we have only flat-bottomed posres:
+ - set am
+ - bFE == FALSE (no B-state for flat-bottomed posres) */
+ if (il->nr == 0)
+ {
+ for (i = 0; i < ilfb->nr; i += 2)
+ {
+ ip = &mtop->ffparams.iparams[ilfb->iatoms[i]];
+ am = max(am, ilfb->iatoms[i+1]);
+ }
+ }
+ /* Make the posres coordinate block end at a molecule end */
+ mol = 0;
+ while (am >= mtop->mols.index[mol+1])
+ {
+ mol++;
+ }
+ molb = &mtop->molblock[0];
+ molb->nposres_xA = mtop->mols.index[mol+1];
+ snew(molb->posres_xA, molb->nposres_xA);
+ if (bFE)
+ {
+ molb->nposres_xB = molb->nposres_xA;
+ snew(molb->posres_xB, molb->nposres_xB);
+ }
+ else
+ {
+ molb->nposres_xB = 0;
+ }
+ for (i = 0; i < il->nr; i += 2)
+ {
+ ip = &mtop->ffparams.iparams[il->iatoms[i]];
+ a = il->iatoms[i+1];
+ molb->posres_xA[a][XX] = ip->posres.pos0A[XX];
+ molb->posres_xA[a][YY] = ip->posres.pos0A[YY];
+ molb->posres_xA[a][ZZ] = ip->posres.pos0A[ZZ];
+ if (bFE)
+ {
+ molb->posres_xB[a][XX] = ip->posres.pos0B[XX];
+ molb->posres_xB[a][YY] = ip->posres.pos0B[YY];
+ molb->posres_xB[a][ZZ] = ip->posres.pos0B[ZZ];
+ }
+ }
+ if (il->nr == 0)
+ {
+ /* If only flat-bottomed posres are present, take reference pos from them.
+ Here: bFE == FALSE */
+ for (i = 0; i < ilfb->nr; i += 2)
+ {
+ ip = &mtop->ffparams.iparams[ilfb->iatoms[i]];
+ a = ilfb->iatoms[i+1];
+ molb->posres_xA[a][XX] = ip->fbposres.pos0[XX];
+ molb->posres_xA[a][YY] = ip->fbposres.pos0[YY];
+ molb->posres_xA[a][ZZ] = ip->fbposres.pos0[ZZ];
+ }
+ }
+}
+
+static void set_disres_npair(gmx_mtop_t *mtop)
+{
+ int mt, i, npair;
+ t_iparams *ip;
+ t_ilist *il;
+ t_iatom *a;
+
+ ip = mtop->ffparams.iparams;
+
+ for (mt = 0; mt < mtop->nmoltype; mt++)
+ {
+ il = &mtop->moltype[mt].ilist[F_DISRES];
+ if (il->nr > 0)
+ {
+ a = il->iatoms;
+ npair = 0;
+ for (i = 0; i < il->nr; i += 3)
+ {
+ npair++;
+ if (i+3 == il->nr || ip[a[i]].disres.label != ip[a[i+3]].disres.label)
+ {
+ ip[a[i]].disres.npair = npair;
+ npair = 0;
+ }
+ }
+ }
+ }
+}
+
+static void do_mtop(t_fileio *fio, gmx_mtop_t *mtop, gmx_bool bRead,
+ int file_version)
+{
+ int mt, mb, i;
+ t_blocka dumb;
+
+ if (bRead)
+ {
+ init_mtop(mtop);
+ }
+ do_symtab(fio, &(mtop->symtab), bRead);
+ if (bRead && debug)
+ {
+ pr_symtab(debug, 0, "symtab", &mtop->symtab);
+ }
+
+ do_symstr(fio, &(mtop->name), bRead, &(mtop->symtab));
+
+ if (file_version >= 57)
+ {
+ do_ffparams(fio, &mtop->ffparams, bRead, file_version);
+
+ gmx_fio_do_int(fio, mtop->nmoltype);
+ }
+ else
+ {
+ mtop->nmoltype = 1;
+ }
+ if (bRead)
+ {
+ snew(mtop->moltype, mtop->nmoltype);
+ if (file_version < 57)
+ {
+ mtop->moltype[0].name = mtop->name;
+ }
+ }
+ for (mt = 0; mt < mtop->nmoltype; mt++)
+ {
+ do_moltype(fio, &mtop->moltype[mt], bRead, &mtop->symtab, file_version,
+ &mtop->groups);
+ }
+
+ if (file_version >= 57)
+ {
+ gmx_fio_do_int(fio, mtop->nmolblock);
+ }
+ else
+ {
+ mtop->nmolblock = 1;
+ }
+ if (bRead)
+ {
+ snew(mtop->molblock, mtop->nmolblock);
+ }
+ if (file_version >= 57)
+ {
+ for (mb = 0; mb < mtop->nmolblock; mb++)
+ {
+ do_molblock(fio, &mtop->molblock[mb], bRead, file_version);
+ }
+ gmx_fio_do_int(fio, mtop->natoms);
+ }
+ else
+ {
+ mtop->molblock[0].type = 0;
+ mtop->molblock[0].nmol = 1;
+ mtop->molblock[0].natoms_mol = mtop->moltype[0].atoms.nr;
+ mtop->molblock[0].nposres_xA = 0;
+ mtop->molblock[0].nposres_xB = 0;
+ }
+
+ do_atomtypes (fio, &(mtop->atomtypes), bRead, &(mtop->symtab), file_version);
+ if (bRead && debug)
+ {
+ pr_atomtypes(debug, 0, "atomtypes", &mtop->atomtypes, TRUE);
+ }
+
+ if (file_version < 57)
+ {
+ /* Debug statements are inside do_idef */
+ do_idef (fio, &mtop->ffparams, &mtop->moltype[0], bRead, file_version);
+ mtop->natoms = mtop->moltype[0].atoms.nr;
+ }
+
+ if (file_version >= 65)
+ {
+ do_cmap(fio, &mtop->ffparams.cmap_grid, bRead);
+ }
+ else
+ {
+ mtop->ffparams.cmap_grid.ngrid = 0;
+ mtop->ffparams.cmap_grid.grid_spacing = 0;
+ mtop->ffparams.cmap_grid.cmapdata = NULL;
+ }
+
+ if (file_version >= 57)
+ {
+ do_groups(fio, &mtop->groups, bRead, &(mtop->symtab), file_version);
+ }
+
+ if (file_version < 57)
+ {
+ do_block(fio, &mtop->moltype[0].cgs, bRead, file_version);
+ if (bRead && gmx_debug_at)
+ {
+ pr_block(debug, 0, "cgs", &mtop->moltype[0].cgs, TRUE);
+ }
+ do_block(fio, &mtop->mols, bRead, file_version);
+ /* Add the posres coordinates to the molblock */
+ add_posres_molblock(mtop);
+ }
+ if (bRead)
+ {
+ if (file_version >= 57)
+ {
+ done_block(&mtop->mols);
+ mtop->mols = mtop_mols(mtop);
+ }
+ if (gmx_debug_at)
+ {
+ pr_block(debug, 0, "mols", &mtop->mols, TRUE);
+ }
+ }
+
+ if (file_version < 51)
+ {
+ /* Here used to be the shake blocks */
+ do_blocka(fio, &dumb, bRead, file_version);
+ if (dumb.nr > 0)
+ {
+ sfree(dumb.index);
+ }
+ if (dumb.nra > 0)
+ {
+ sfree(dumb.a);
+ }
+ }
+
+ if (bRead)
+ {
+ close_symtab(&(mtop->symtab));
+ }
+}
+
+/* If TopOnlyOK is TRUE then we can read even future versions
+ * of tpx files, provided the file_generation hasn't changed.
+ * If it is FALSE, we need the inputrecord too, and bail out
+ * if the file is newer than the program.
+ *
+ * The version and generation if the topology (see top of this file)
+ * are returned in the two last arguments.
+ *
+ * If possible, we will read the inputrec even when TopOnlyOK is TRUE.
+ */
+static void do_tpxheader(t_fileio *fio, gmx_bool bRead, t_tpxheader *tpx,
+ gmx_bool TopOnlyOK, int *file_version,
+ int *file_generation)
+{
+ char buf[STRLEN];
+ char file_tag[STRLEN];
+ gmx_bool bDouble;
+ int precision;
+ int fver, fgen;
+ int idum = 0;
+ real rdum = 0;
+
+ gmx_fio_checktype(fio);
+ gmx_fio_setdebug(fio, bDebugMode());
+
+ /* NEW! XDR tpb file */
+ precision = sizeof(real);
+ if (bRead)
+ {
+ gmx_fio_do_string(fio, buf);
+ if (strncmp(buf, "VERSION", 7))
+ {
+ gmx_fatal(FARGS, "Can not read file %s,\n"
+ " this file is from a Gromacs version which is older than 2.0\n"
+ " Make a new one with grompp or use a gro or pdb file, if possible",
+ gmx_fio_getname(fio));
+ }
+ gmx_fio_do_int(fio, precision);
+ bDouble = (precision == sizeof(double));
+ if ((precision != sizeof(float)) && !bDouble)
+ {
+ gmx_fatal(FARGS, "Unknown precision in file %s: real is %d bytes "
+ "instead of %d or %d",
+ gmx_fio_getname(fio), precision, sizeof(float), sizeof(double));
+ }
+ gmx_fio_setprecision(fio, bDouble);
+ fprintf(stderr, "Reading file %s, %s (%s precision)\n",
+ gmx_fio_getname(fio), buf, bDouble ? "double" : "single");
+ }
+ else
+ {
+ gmx_fio_write_string(fio, GromacsVersion());
+ bDouble = (precision == sizeof(double));
+ gmx_fio_setprecision(fio, bDouble);
+ gmx_fio_do_int(fio, precision);
+ fver = tpx_version;
+ sprintf(file_tag, "%s", tpx_tag);
+ fgen = tpx_generation;
+ }
+
+ /* Check versions! */
+ gmx_fio_do_int(fio, fver);
+
+ /* This is for backward compatibility with development versions 77-79
+ * where the tag was, mistakenly, placed before the generation,
+ * which would cause a segv instead of a proper error message
+ * when reading the topology only from tpx with <77 code.
+ */
+ if (fver >= 77 && fver <= 79)
+ {
+ gmx_fio_do_string(fio, file_tag);
+ }
+
+ if (fver >= 26)
+ {
+ gmx_fio_do_int(fio, fgen);
+ }
+ else
+ {
+ fgen = 0;
+ }
+
+ if (fver >= 81)
+ {
+ gmx_fio_do_string(fio, file_tag);
+ }
+ if (bRead)
+ {
+ if (fver < 77)
+ {
+ /* Versions before 77 don't have the tag, set it to release */
+ sprintf(file_tag, "%s", TPX_TAG_RELEASE);
+ }
+
+ if (strcmp(file_tag, tpx_tag) != 0)
+ {
+ fprintf(stderr, "Note: file tpx tag '%s', software tpx tag '%s'\n",
+ file_tag, tpx_tag);
+
+ /* We only support reading tpx files with the same tag as the code
+ * or tpx files with the release tag and with lower version number.
+ */
+ if (!strcmp(file_tag, TPX_TAG_RELEASE) == 0 && fver < tpx_version)
+ {
+ gmx_fatal(FARGS, "tpx tag/version mismatch: reading tpx file (%s) version %d, tag '%s' with program for tpx version %d, tag '%s'",
+ gmx_fio_getname(fio), fver, file_tag,
+ tpx_version, tpx_tag);
+ }
+ }
+ }
+
+ if (file_version != NULL)
+ {
+ *file_version = fver;
+ }
+ if (file_generation != NULL)
+ {
+ *file_generation = fgen;
+ }
+
+
+ if ((fver <= tpx_incompatible_version) ||
+ ((fver > tpx_version) && !TopOnlyOK) ||
+ (fgen > tpx_generation) ||
+ tpx_version == 80) /*80 was used by both 5.0-dev and 4.6-dev*/
+ {
+ gmx_fatal(FARGS, "reading tpx file (%s) version %d with version %d program",
+ gmx_fio_getname(fio), fver, tpx_version);
+ }
+
+ do_section(fio, eitemHEADER, bRead);
+ gmx_fio_do_int(fio, tpx->natoms);
+ if (fver >= 28)
+ {
+ gmx_fio_do_int(fio, tpx->ngtc);
+ }
+ else
+ {
+ tpx->ngtc = 0;
+ }
+ if (fver < 62)
+ {
+ gmx_fio_do_int(fio, idum);
+ gmx_fio_do_real(fio, rdum);
+ }
+ /*a better decision will eventually (5.0 or later) need to be made
+ on how to treat the alchemical state of the system, which can now
+ vary through a simulation, and cannot be completely described
+ though a single lambda variable, or even a single state
+ index. Eventually, should probably be a vector. MRS*/
+ if (fver >= 79)
+ {
+ gmx_fio_do_int(fio, tpx->fep_state);
+ }
+ gmx_fio_do_real(fio, tpx->lambda);
+ gmx_fio_do_int(fio, tpx->bIr);
+ gmx_fio_do_int(fio, tpx->bTop);
+ gmx_fio_do_int(fio, tpx->bX);
+ gmx_fio_do_int(fio, tpx->bV);
+ gmx_fio_do_int(fio, tpx->bF);
+ gmx_fio_do_int(fio, tpx->bBox);
+
+ if ((fgen > tpx_generation))
+ {
+ /* This can only happen if TopOnlyOK=TRUE */
+ tpx->bIr = FALSE;
+ }
+}
+
+static int do_tpx(t_fileio *fio, gmx_bool bRead,
+ t_inputrec *ir, t_state *state, rvec *f, gmx_mtop_t *mtop,
+ gmx_bool bXVallocated)
+{
+ t_tpxheader tpx;
+ t_inputrec dum_ir;
+ gmx_mtop_t dum_top;
+ gmx_bool TopOnlyOK, bDum = TRUE;
+ int file_version, file_generation;
+ int i;
+ rvec *xptr, *vptr;
+ int ePBC;
+ gmx_bool bPeriodicMols;
+
+ if (!bRead)
+ {
+ tpx.natoms = state->natoms;
+ tpx.ngtc = state->ngtc; /* need to add nnhpres here? */
+ tpx.fep_state = state->fep_state;
+ tpx.lambda = state->lambda[efptFEP];
+ tpx.bIr = (ir != NULL);
+ tpx.bTop = (mtop != NULL);
+ tpx.bX = (state->x != NULL);
+ tpx.bV = (state->v != NULL);
+ tpx.bF = (f != NULL);
+ tpx.bBox = TRUE;
+ }
+
+ TopOnlyOK = (ir == NULL);
+
+ do_tpxheader(fio, bRead, &tpx, TopOnlyOK, &file_version, &file_generation);
+
+ if (bRead)
+ {
+ state->flags = 0;
+ /* state->lambda = tpx.lambda;*/ /*remove this eventually? */
+ /* The init_state calls initialize the Nose-Hoover xi integrals to zero */
+ if (bXVallocated)
+ {
+ xptr = state->x;
+ vptr = state->v;
+ init_state(state, 0, tpx.ngtc, 0, 0, 0); /* nose-hoover chains */ /* eventually, need to add nnhpres here? */
+ state->natoms = tpx.natoms;
+ state->nalloc = tpx.natoms;
+ state->x = xptr;
+ state->v = vptr;
+ }
+ else
+ {
+ init_state(state, tpx.natoms, tpx.ngtc, 0, 0, 0); /* nose-hoover chains */
+ }
+ }
+
+#define do_test(fio, b, p) if (bRead && (p != NULL) && !b) gmx_fatal(FARGS, "No %s in %s",#p, gmx_fio_getname(fio))
+
+ do_test(fio, tpx.bBox, state->box);
+ do_section(fio, eitemBOX, bRead);
+ if (tpx.bBox)
+ {
+ gmx_fio_ndo_rvec(fio, state->box, DIM);
+ if (file_version >= 51)
+ {
+ gmx_fio_ndo_rvec(fio, state->box_rel, DIM);
+ }
+ else
+ {
+ /* We initialize box_rel after reading the inputrec */
+ clear_mat(state->box_rel);
+ }
+ if (file_version >= 28)
+ {
+ gmx_fio_ndo_rvec(fio, state->boxv, DIM);
+ if (file_version < 56)
+ {
+ matrix mdum;
+ gmx_fio_ndo_rvec(fio, mdum, DIM);
+ }
+ }
+ }
+
+ if (state->ngtc > 0 && file_version >= 28)
+ {
+ real *dumv;
+ /*ndo_double(state->nosehoover_xi,state->ngtc,bDum);*/
+ /*ndo_double(state->nosehoover_vxi,state->ngtc,bDum);*/
+ /*ndo_double(state->therm_integral,state->ngtc,bDum);*/
+ snew(dumv, state->ngtc);
+ if (file_version < 69)
+ {
+ bDum = gmx_fio_ndo_real(fio, dumv, state->ngtc);
+ }
+ /* These used to be the Berendsen tcoupl_lambda's */
+ bDum = gmx_fio_ndo_real(fio, dumv, state->ngtc);
+ sfree(dumv);
+ }
+
+ /* Prior to tpx version 26, the inputrec was here.
+ * I moved it to enable partial forward-compatibility
+ * for analysis/viewer programs.
+ */
+ if (file_version < 26)
+ {
+ do_test(fio, tpx.bIr, ir);
+ do_section(fio, eitemIR, bRead);
+ if (tpx.bIr)
+ {
+ if (ir)
+ {
+ do_inputrec(fio, ir, bRead, file_version,
+ mtop ? &mtop->ffparams.fudgeQQ : NULL);
+ if (bRead && debug)
+ {
+ pr_inputrec(debug, 0, "inputrec", ir, FALSE);
+ }
+ }
+ else
+ {
+ do_inputrec(fio, &dum_ir, bRead, file_version,
+ mtop ? &mtop->ffparams.fudgeQQ : NULL);
+ if (bRead && debug)
+ {
+ pr_inputrec(debug, 0, "inputrec", &dum_ir, FALSE);
+ }
+ done_inputrec(&dum_ir);
+ }
+
+ }
+ }
+
+ do_test(fio, tpx.bTop, mtop);
+ do_section(fio, eitemTOP, bRead);
+ if (tpx.bTop)
+ {
+ if (mtop)
+ {
+ do_mtop(fio, mtop, bRead, file_version);
+ }
+ else
+ {
+ do_mtop(fio, &dum_top, bRead, file_version);
+ done_mtop(&dum_top, TRUE);
+ }
+ }
+ do_test(fio, tpx.bX, state->x);
+ do_section(fio, eitemX, bRead);
+ if (tpx.bX)
+ {
+ if (bRead)
+ {
+ state->flags |= (1<<estX);
+ }
+ gmx_fio_ndo_rvec(fio, state->x, state->natoms);
+ }
+
+ do_test(fio, tpx.bV, state->v);
+ do_section(fio, eitemV, bRead);
+ if (tpx.bV)
+ {
+ if (bRead)
+ {
+ state->flags |= (1<<estV);
+ }
+ gmx_fio_ndo_rvec(fio, state->v, state->natoms);
+ }
+
+ do_test(fio, tpx.bF, f);
+ do_section(fio, eitemF, bRead);
+ if (tpx.bF)
+ {
+ gmx_fio_ndo_rvec(fio, f, state->natoms);
+ }
+
+ /* Starting with tpx version 26, we have the inputrec
+ * at the end of the file, so we can ignore it
+ * if the file is never than the software (but still the
+ * same generation - see comments at the top of this file.
+ *
+ *
+ */
+ ePBC = -1;
+ bPeriodicMols = FALSE;
+ if (file_version >= 26)
+ {
+ do_test(fio, tpx.bIr, ir);
+ do_section(fio, eitemIR, bRead);
+ if (tpx.bIr)
+ {
+ if (file_version >= 53)
+ {
+ /* Removed the pbc info from do_inputrec, since we always want it */
+ if (!bRead)
+ {
+ ePBC = ir->ePBC;
+ bPeriodicMols = ir->bPeriodicMols;
+ }
+ gmx_fio_do_int(fio, ePBC);
+ gmx_fio_do_gmx_bool(fio, bPeriodicMols);
+ }
+ if (file_generation <= tpx_generation && ir)
+ {
+ do_inputrec(fio, ir, bRead, file_version, mtop ? &mtop->ffparams.fudgeQQ : NULL);
+ if (bRead && debug)
+ {
+ pr_inputrec(debug, 0, "inputrec", ir, FALSE);
+ }
+ if (file_version < 51)
+ {
+ set_box_rel(ir, state);
+ }
+ if (file_version < 53)
+ {
+ ePBC = ir->ePBC;
+ bPeriodicMols = ir->bPeriodicMols;
+ }
+ }
+ if (bRead && ir && file_version >= 53)
+ {
+ /* We need to do this after do_inputrec, since that initializes ir */
+ ir->ePBC = ePBC;
+ ir->bPeriodicMols = bPeriodicMols;
+ }
+ }
+ }
+
+ if (bRead)
+ {
+ if (tpx.bIr && ir)
+ {
+ if (state->ngtc == 0)
+ {
+ /* Reading old version without tcoupl state data: set it */
+ init_gtc_state(state, ir->opts.ngtc, 0, ir->opts.nhchainlength);
+ }
+ if (tpx.bTop && mtop)
+ {
+ if (file_version < 57)
+ {
+ if (mtop->moltype[0].ilist[F_DISRES].nr > 0)
+ {
+ ir->eDisre = edrSimple;
+ }
+ else
+ {
+ ir->eDisre = edrNone;
+ }
+ }
+ set_disres_npair(mtop);
+ }
+ }
+
+ if (tpx.bTop && mtop)
+ {
+ gmx_mtop_finalize(mtop);
+ }
+
+ if (file_version >= 57)
+ {
+ char *env;
+ int ienv;
+ env = getenv("GMX_NOCHARGEGROUPS");
+ if (env != NULL)
+ {
+ sscanf(env, "%d", &ienv);
+ fprintf(stderr, "\nFound env.var. GMX_NOCHARGEGROUPS = %d\n",
+ ienv);
+ if (ienv > 0)
+ {
+ fprintf(stderr,
+ "Will make single atomic charge groups in non-solvent%s\n",
+ ienv > 1 ? " and solvent" : "");
+ gmx_mtop_make_atomic_charge_groups(mtop, ienv == 1);
+ }
+ fprintf(stderr, "\n");
+ }
+ }
+ }
+
+ return ePBC;
+}
+
+/************************************************************
+ *
+ * The following routines are the exported ones
+ *
+ ************************************************************/
+
+t_fileio *open_tpx(const char *fn, const char *mode)
+{
+ return gmx_fio_open(fn, mode);
+}
+
+void close_tpx(t_fileio *fio)
+{
+ gmx_fio_close(fio);
+}
+
+void read_tpxheader(const char *fn, t_tpxheader *tpx, gmx_bool TopOnlyOK,
+ int *file_version, int *file_generation)
+{
+ t_fileio *fio;
+
+ fio = open_tpx(fn, "r");
+ do_tpxheader(fio, TRUE, tpx, TopOnlyOK, file_version, file_generation);
+ close_tpx(fio);
+}
+
+void write_tpx_state(const char *fn,
+ t_inputrec *ir, t_state *state, gmx_mtop_t *mtop)
+{
+ t_fileio *fio;
+
+ fio = open_tpx(fn, "w");
+ do_tpx(fio, FALSE, ir, state, NULL, mtop, FALSE);
+ close_tpx(fio);
+}
+
+void read_tpx_state(const char *fn,
+ t_inputrec *ir, t_state *state, rvec *f, gmx_mtop_t *mtop)
+{
+ t_fileio *fio;
+
+ fio = open_tpx(fn, "r");
+ do_tpx(fio, TRUE, ir, state, f, mtop, FALSE);
+ close_tpx(fio);
+}
+
+int read_tpx(const char *fn,
+ t_inputrec *ir, matrix box, int *natoms,
+ rvec *x, rvec *v, rvec *f, gmx_mtop_t *mtop)
+{
+ t_fileio *fio;
+ t_state state;
+ int ePBC;
+
+ state.x = x;
+ state.v = v;
+ fio = open_tpx(fn, "r");
+ ePBC = do_tpx(fio, TRUE, ir, &state, f, mtop, TRUE);
+ close_tpx(fio);
+ *natoms = state.natoms;
+ if (box)
+ {
+ copy_mat(state.box, box);
+ }
+ state.x = NULL;
+ state.v = NULL;
+ done_state(&state);
+
+ return ePBC;
+}
+
+int read_tpx_top(const char *fn,
+ t_inputrec *ir, matrix box, int *natoms,
+ rvec *x, rvec *v, rvec *f, t_topology *top)
+{
+ gmx_mtop_t mtop;
+ t_topology *ltop;
+ int ePBC;
+
+ ePBC = read_tpx(fn, ir, box, natoms, x, v, f, &mtop);
+
+ *top = gmx_mtop_t_to_t_topology(&mtop);
+
+ return ePBC;
+}
+
+gmx_bool fn2bTPX(const char *file)
+{
+ switch (fn2ftp(file))
+ {
+ case efTPR:
+ case efTPB:
+ case efTPA:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static void done_gmx_groups_t(gmx_groups_t *g)
+{
+ int i;
+
+ for (i = 0; (i < egcNR); i++)
+ {
+ if (NULL != g->grps[i].nm_ind)
+ {
+ sfree(g->grps[i].nm_ind);
+ g->grps[i].nm_ind = NULL;
+ }
+ if (NULL != g->grpnr[i])
+ {
+ sfree(g->grpnr[i]);
+ g->grpnr[i] = NULL;
+ }
+ }
+ /* The contents of this array is in symtab, don't free it here */
+ sfree(g->grpname);
+}
+
+gmx_bool read_tps_conf(const char *infile, char *title, t_topology *top, int *ePBC,
+ rvec **x, rvec **v, matrix box, gmx_bool bMass)
+{
+ t_tpxheader header;
+ int natoms, i, version, generation;
+ gmx_bool bTop, bXNULL = FALSE;
+ gmx_mtop_t *mtop;
+ t_topology *topconv;
+ gmx_atomprop_t aps;
+
+ bTop = fn2bTPX(infile);
+ *ePBC = -1;
+ if (bTop)
+ {
+ read_tpxheader(infile, &header, TRUE, &version, &generation);
+ if (x)
+ {
+ snew(*x, header.natoms);
+ }
+ if (v)
+ {
+ snew(*v, header.natoms);
+ }
+ snew(mtop, 1);
+ *ePBC = read_tpx(infile, NULL, box, &natoms,
+ (x == NULL) ? NULL : *x, (v == NULL) ? NULL : *v, NULL, mtop);
+ *top = gmx_mtop_t_to_t_topology(mtop);
+ /* In this case we need to throw away the group data too */
+ done_gmx_groups_t(&mtop->groups);
+ sfree(mtop);
+ strcpy(title, *top->name);
+ tpx_make_chain_identifiers(&top->atoms, &top->mols);
+ }
+ else
+ {
+ get_stx_coordnum(infile, &natoms);
+ init_t_atoms(&top->atoms, natoms, (fn2ftp(infile) == efPDB));
+ if (x == NULL)
+ {
+ snew(x, 1);
+ bXNULL = TRUE;
+ }
+ snew(*x, natoms);
+ if (v)
+ {
+ snew(*v, natoms);
+ }
+ read_stx_conf(infile, title, &top->atoms, *x, (v == NULL) ? NULL : *v, ePBC, box);
+ if (bXNULL)
+ {
+ sfree(*x);
+ sfree(x);
+ }
+ if (bMass)
+ {
+ aps = gmx_atomprop_init();
+ for (i = 0; (i < natoms); i++)
+ {
+ if (!gmx_atomprop_query(aps, epropMass,
+ *top->atoms.resinfo[top->atoms.atom[i].resind].name,
+ *top->atoms.atomname[i],
+ &(top->atoms.atom[i].m)))
+ {
+ if (debug)
+ {
+ fprintf(debug, "Can not find mass for atom %s %d %s, setting to 1\n",
+ *top->atoms.resinfo[top->atoms.atom[i].resind].name,
+ top->atoms.resinfo[top->atoms.atom[i].resind].nr,
+ *top->atoms.atomname[i]);
+ }
+ }
+ }
+ gmx_atomprop_destroy(aps);
+ }
+ top->idef.ntypes = -1;
+ }
+
+ return bTop;
+}
--- /dev/null
- int minO, minC, H, N, C, O, Cn[MAXCHI+3];
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gromacs Runs On Most of All Computer Systems
+ */
+
+#ifndef _gstat_h
+#define _gstat_h
+
+#include "typedefs.h"
+#include "statutil.h"
+#include "mshift.h"
+#include "rmpbc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***********************************************
+ *
+ * A U T O C O R R E L A T I O N
+ *
+ ***********************************************/
+
+real LegendreP(real x, unsigned long m);
+
+#define eacNormal (1<<0)
+#define eacCos (1<<1)
+#define eacVector (1<<2)
+#define eacRcross (1<<3 | eacVector)
+#define eacP0 (1<<4 | eacVector)
+#define eacP1 (1<<5 | eacVector)
+#define eacP2 (1<<6 | eacVector)
+#define eacP3 (1<<7 | eacVector)
+#define eacP4 (1<<8 | eacVector)
+#define eacIden (1<<9)
+
+enum {
+ effnNONE, effnEXP1, effnEXP2, effnEXP3, effnVAC,
+ effnEXP5, effnEXP7, effnEXP9, effnERF, effnERREST, effnNR
+};
+
+/* must correspond with 'leg' g_chi.c:727 */
+enum {
+ edPhi = 0, edPsi, edOmega, edChi1, edChi2, edChi3, edChi4, edChi5, edChi6, edMax
+};
+
+enum {
+ edPrintST = 0, edPrintRO
+};
+
+#define NHISTO 360
+#define NONCHI 3
+#define MAXCHI edMax-NONCHI
+#define NROT 4 /* number of rotamers: 1=g(-), 2=t, 3=g(+), 0=other */
+
+typedef struct {
++ int minCalpha, minC, H, N, C, O, Cn[MAXCHI+3];
+} t_dihatms; /* Cn[0]=N, Cn[1]=Ca, Cn[2]=Cb etc. */
+
+typedef struct {
+ char name[12];
+ int resnr;
+ int index; /* Index for amino acids (histograms) */
+ int j0[edMax]; /* Index in dih array (phi angle is first...) */
+ t_dihatms atm;
+ int b[edMax];
+ int ntr[edMax];
+ real S2[edMax];
+ real rot_occ[edMax][NROT];
+
+} t_dlist;
+
+extern const int nfp_ffn[effnNR];
+
+extern const char *s_ffn[effnNR+2];
+
+extern const char *longs_ffn[effnNR];
+
+int sffn2effn(const char **sffn);
+/* Returns the ffn enum corresponding to the selected enum option in sffn */
+
+t_pargs *add_acf_pargs(int *npargs, t_pargs *pa);
+/* Add options for autocorr to the current set of options.
+ * *npargs must be initialised to the number of elements in pa,
+ * it will be incremented appropriately.
+ */
+
+void cross_corr(int n, real f[], real g[], real corr[]);
+/* Simple minded cross correlation algorithm */
+
+real fit_acf(int ncorr, int fitfn, const output_env_t oenv, gmx_bool bVerbose,
+ real tbeginfit, real tendfit, real dt, real c1[], real *fit);
+/* Fit an ACF to a given function */
+
+void do_autocorr(const char *fn, const output_env_t oenv,
+ const char *title,
+ int nframes, int nitem, real **c1,
+ real dt, unsigned long mode, gmx_bool bAver);
+/* Calls low_do_autocorr (see below). After calling add_acf_pargs */
+
+void low_do_autocorr(const char *fn, const output_env_t oenv,
+ const char *title, int nframes, int nitem,
+ int nout, real **c1, real dt, unsigned long mode,
+ int nrestart, gmx_bool bAver, gmx_bool bNormalize,
+ gmx_bool bVerbose, real tbeginfit, real tendfit,
+ int nfitparm, int nskip);
+/*
+ * do_autocorr calculates autocorrelation functions for many things.
+ * It takes a 2 d array containing nitem arrays of length nframes
+ * for each item the ACF is calculated.
+ *
+ * A number of "modes" exist for computation of the ACF
+ *
+ * if (mode == eacNormal) {
+ * C(t) = < X (tau) * X (tau+t) >
+ * }
+ * else if (mode == eacCos) {
+ * C(t) = < cos (X(tau) - X(tau+t)) >
+ * }
+ * else if (mode == eacIden) { **not fully supported yet**
+ * C(t) = < (X(tau) == X(tau+t)) >
+ * }
+ * else if (mode == eacVector) {
+ * C(t) = < X(tau) * X(tau+t)
+ * }
+ * else if (mode == eacP1) {
+ * C(t) = < cos (X(tau) * X(tau+t) >
+ * }
+ * else if (mode == eacP2) {
+ * C(t) = 1/2 * < 3 cos (X(tau) * X(tau+t) - 1 >
+ * }
+ * else if (mode == eacRcross) {
+ * C(t) = < ( X(tau) * X(tau+t) )^2 >
+ * }
+ *
+ * For modes eacVector, eacP1, eacP2 and eacRcross the input should be
+ * 3 x nframes long, where each triplet is taken as a 3D vector
+ *
+ * For mode eacCos inputdata must be in radians, not degrees!
+ *
+ * Other parameters are:
+ *
+ * fn is output filename (.xvg) where the correlation function(s) are printed
+ * title is the title in the output file
+ * nframes is the number of frames in the time series
+ * nitem is the number of items
+ * c1 is an array of dimension [ 0 .. nitem-1 ] [ 0 .. nframes-1 ]
+ * on output, this array is filled with the correlation function
+ * to reduce storage
+ * nrestart is the number of steps between restarts for direct ACFs
+ * (i.e. without FFT) When set to 1 all points are used as
+ * time origin for averaging
+ * dt is the time between frames
+ * bAver If set, all ndih C(t) functions are averaged into a single
+ * C(t)
+ * (bFour If set, will use fast fourier transform (FFT) for evaluating
+ * the ACF: removed option, now on the command line only)
+ * bNormalize If set, all ACFs will be normalized to start at 0
+ * nskip Determines whether steps a re skipped in the output
+ */
+
+typedef struct {
+ const char *name; /* Description of the J coupling constant */
+ real A, B, C; /* Karplus coefficients */
+ real offset; /* Offset for dihedral angle in histogram (e.g. -M_PI/3) */
+ real Jc; /* Resulting Jcoupling */
+ real Jcsig; /* Standard deviation in Jc */
+} t_karplus;
+
+void calc_distribution_props(int nh, int histo[],
+ real start, int nkkk, t_karplus kkk[],
+ real *S2);
+/* This routine takes a dihedral distribution and calculates
+ * coupling constants and dihedral order parameters of it.
+ *
+ * nh is the number of points
+ * histo is the array of datapoints which is assumed to span
+ * 2 M_PI radians
+ * start is the starting angle of the histogram, this can be either 0
+ * or -M_PI
+ * nkkk is the number of karplus sets (multiple coupling constants may be
+ * derived from a single angle)
+ * kkk are the constants for calculating J coupling constants using a
+ * Karplus equation according to
+ *
+ * 2
+ * J = A cos theta + B cos theta + C
+ *
+ * where theta is phi - offset (phi is the angle in the histogram)
+ * offset is subtracted from phi before substitution in the Karplus
+ * equation
+ * S2 is the resulting dihedral order parameter
+ *
+ */
+
+
+/***********************************************
+ *
+ * F I T R O U T I N E S
+ *
+ ***********************************************/
+void do_expfit(int ndata, real c1[], real dt,
+ real begintimefit, real endtimefit);
+
+void expfit(int n, real x[], real y[], real Dy[],
+ real *a, real *sa,
+ real *b, real *sb);
+/* This procedure fits y=exp(a+bx) for n (x,y) pairs to determine a and b.
+ * The uncertainties in the y values must be in the vector Dy.
+ * The standard deviations of a and b, sa and sb, are also calculated.
+ *
+ * Routine from Computers in physics, 7(3) (1993), p. 280-285.
+ */
+
+void ana_dih_trans(const char *fn_trans, const char *fn_histo,
+ real **dih, int nframes, int nangles,
+ const char *grpname, real *time, gmx_bool bRb,
+ const output_env_t oenv);
+/*
+ * Analyse dihedral transitions, by counting transitions per dihedral
+ * and per frame. The total number of transitions is printed to
+ * stderr, as well as the average time between transitions.
+ *
+ * is wrapper to low_ana_dih_trans, which also passes in and out the
+ number of transitions per dihedral per residue. that uses struc dlist
+ which is not external, so pp2shift.h must be included.
+
+ * Dihedrals are supposed to be in either of three minima,
+ * (trans, gauche+, gauche-)
+ *
+ * fn_trans output file name for #transitions per timeframe
+ * fn_histo output file name for transition time histogram
+ * dih the actual dihedral angles
+ * nframes number of times frames
+ * nangles number of angles
+ * grpname a string for the header of plots
+ * time array (size nframes) of times of trajectory frames
+ * bRb determines whether the polymer convention is used
+ * (trans = 0)
+ */
+
+void low_ana_dih_trans(gmx_bool bTrans, const char *fn_trans,
+ gmx_bool bHisto, const char *fn_histo, int maxchi,
+ real **dih, int nlist, t_dlist dlist[],
+ int nframes, int nangles, const char *grpname,
+ int multiplicity[], real *time, gmx_bool bRb,
+ real core_frac, const output_env_t oenv);
+/* as above but passes dlist so can copy occupancies into it, and multiplicity[]
+ * (1..nangles, corresp to dih[this][], so can have non-3 multiplicity of
+ * rotamers. Also production of xvg output files is conditional
+ * and the fractional width of each rotamer can be set ie for a 3 fold
+ * dihedral with core_frac = 0.5 only the central 60 degrees is assigned
+ * to each rotamer, the rest goes to rotamer zero */
+
+
+
+void read_ang_dih(const char *trj_fn,
+ gmx_bool bAngles, gmx_bool bSaveAll, gmx_bool bRb, gmx_bool bPBC,
+ int maxangstat, int angstat[],
+ int *nframes, real **time,
+ int isize, atom_id index[],
+ real **trans_frac,
+ real **aver_angle,
+ real *dih[],
+ const output_env_t oenv);
+/*
+ * Read a trajectory and calculate angles and dihedrals.
+ *
+ * trj_fn file name of trajectory
+ * tpb_fn file name of tpb file
+ * bAngles do we have to read angles or dihedrals
+ * bSaveAll do we have to store all in the dih array
+ * bRb do we have Ryckaert-Bellemans dihedrals (trans = 0)
+ * bPBC compute angles module 2 Pi
+ * maxangstat number of entries in distribution array
+ * angstat angle distribution
+ * *nframes number of frames read
+ * time simulation time at each time frame
+ * isize number of entries in the index, when angles 3*number of angles
+ * else 4*number of angles
+ * index atom numbers that define the angles or dihedrals
+ * (i,j,k) resp (i,j,k,l)
+ * trans_frac number of dihedrals in trans
+ * aver_angle average angle at each time frame
+ * dih all angles at each time frame
+ */
+
+void make_histo(FILE *log,
+ int ndata, real data[], int npoints, int histo[],
+ real minx, real maxx);
+/*
+ * Make a histogram from data. The min and max of the data array can
+ * be determined (if minx == 0 and maxx == 0)
+ * and the index in the histogram is computed from
+ * ind = npoints/(max(data) - min(data))
+ *
+ * log write error output to this file
+ * ndata number of points in data
+ * data data points
+ * npoints number of points in histogram
+ * histo histogram array. This is NOT set to zero, to allow you
+ * to add multiple histograms
+ * minx start of the histogram
+ * maxx end of the histogram
+ * if both are 0, these values are computed by the routine itself
+ */
+
+void normalize_histo(int npoints, int histo[], real dx, real normhisto[]);
+/*
+ * Normalize a histogram so that the integral over the histo is 1
+ *
+ * npoints number of points in the histo array
+ * histo input histogram
+ * dx distance between points on the X-axis
+ * normhisto normalized output histogram
+ */
+
+real fit_function(int eFitFn, real *parm, real x);
+/* Returns the value of fit function eFitFn at x */
+
+/* Use Levenberg-Marquardt method to fit to a nfitparm parameter exponential */
+/* or to a transverse current autocorrelation function */
+/* Or: "There is no KILL like OVERKILL", Dr. Ir. D. van der Spoel */
+real do_lmfit(int ndata, real c1[], real sig[], real dt, real *x,
+ real begintimefit, real endtimefit, const output_env_t oenv,
+ gmx_bool bVerbose, int eFitFn, real fitparms[], int fix);
+/* Returns integral.
+ * If x == NULL, the timestep dt will be used to create a time axis.
+ * fix fixes fit parameter i at it's starting value, when the i'th bit
+ * of fix is set.
+ */
+
+real evaluate_integral(int n, real x[], real y[], real dy[],
+ real aver_start, real *stddev);
+/* Integrate data in y, and, if given, use dy as weighting
+ * aver_start should be set to a value where the function has
+ * converged to 0.
+ */
+
+real print_and_integrate(FILE *fp, int n, real dt,
+ real c[], real *fit, int nskip);
+/* Integrate the data in c[] from 0 to n using trapezium rule.
+ * If fp != NULL output is written to it
+ * nskip determines whether all elements are written to the output file
+ * (written when i % nskip == 0)
+ * If fit != NULL the fit is also written.
+ */
+
+int get_acfnout(void);
+/* Return the output length for the correlation function
+ * Works only AFTER do_auto_corr has been called!
+ */
+
+int get_acffitfn(void);
+/* Return the fit function type.
+ * Works only AFTER do_auto_corr has been called!
+ */
+
+/* Routines from pp2shift (anadih.c etc.) */
+
+void do_pp2shifts(FILE *fp, int nframes,
+ int nlist, t_dlist dlist[], real **dih);
+
+gmx_bool has_dihedral(int Dih, t_dlist *dl);
+
+t_dlist *mk_dlist(FILE *log,
+ t_atoms *atoms, int *nlist,
+ gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, gmx_bool bHChi,
+ int maxchi, int r0, gmx_residuetype_t rt);
+
+void pr_dlist(FILE *fp, int nl, t_dlist dl[], real dt, int printtype,
+ gmx_bool bPhi, gmx_bool bPsi, gmx_bool bChi, gmx_bool bOmega, int maxchi);
+
+int pr_trans(FILE *fp, int nl, t_dlist dl[], real dt, int Xi);
+
+void mk_chi_lookup (int **lookup, int maxchi, real **dih,
+ int nlist, t_dlist dlist[]);
+
+void mk_multiplicity_lookup (int *multiplicity, int maxchi, real **dih,
+ int nlist, t_dlist dlist[], int nangle);
+
+void get_chi_product_traj (real **dih, int nframes, int nangles,
+ int nlist, int maxchi, t_dlist dlist[],
+ real time[], int **lookup, int *multiplicity,
+ gmx_bool bRb, gmx_bool bNormalize,
+ real core_frac, gmx_bool bAll, const char *fnall,
+ const output_env_t oenv);
+
+void print_one (const output_env_t oenv, const char *base,
+ const char *name,
+ const char *title, const char *ylabel, int nf,
+ real time[], real data[]);
+
+/* Routines from g_hbond */
+void analyse_corr(int n, real t[], real ct[], real nt[], real kt[],
+ real sigma_ct[], real sigma_nt[], real sigma_kt[],
+ real fit_start, real temp, real smooth_tail_start,
+ const output_env_t oenv);
+
+void compute_derivative(int nn, real x[], real y[], real dydx[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
- void nbnxn_cuda_init_const(nbnxn_cuda_ptr_t p_cu_nb,
- const interaction_const_t *ic,
- const nonbonded_verlet_t *nbv) FUNC_TERM
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2012, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+
+#ifndef NBNXN_CUDA_DATA_MGMT_H
+#define NBNXN_CUDA_DATA_MGMT_H
+
+#include "types/simple.h"
+#include "types/interaction_const.h"
+#include "types/nbnxn_cuda_types_ext.h"
+#include "types/hw_info.h"
+
+#ifdef GMX_GPU
+#define FUNC_TERM ;
+#define FUNC_QUALIFIER
+#else
+#define FUNC_TERM {}
+#define FUNC_QUALIFIER static
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*! Initializes the data structures related to CUDA nonbonded calculations. */
+FUNC_QUALIFIER
+void nbnxn_cuda_init(FILE *fplog,
+ nbnxn_cuda_ptr_t *p_cu_nb,
+ gmx_gpu_info_t *gpu_info, int my_gpu_index,
+ /* true of both local and non-local are don on GPU */
+ gmx_bool bLocalAndNonlocal) FUNC_TERM
+
+/*! Initializes simulation constant data. */
+FUNC_QUALIFIER
++void nbnxn_cuda_init_const(nbnxn_cuda_ptr_t cu_nb,
++ const interaction_const_t *ic,
++ const nonbonded_verlet_group_t *nbv_group) FUNC_TERM
+
+/*! Initializes pair-list data for GPU, called at every pair search step. */
+FUNC_QUALIFIER
+void nbnxn_cuda_init_pairlist(nbnxn_cuda_ptr_t cu_nb,
+ const nbnxn_pairlist_t *h_nblist,
+ int iloc) FUNC_TERM
+
+/*! Initializes atom-data on the GPU, called at every pair search step. */
+FUNC_QUALIFIER
+void nbnxn_cuda_init_atomdata(nbnxn_cuda_ptr_t cu_nb,
+ const nbnxn_atomdata_t *atomdata) FUNC_TERM
+
+/*! \brief Update parameters during PP-PME load balancing. */
+FUNC_QUALIFIER
+void nbnxn_cuda_pme_loadbal_update_param(nbnxn_cuda_ptr_t cu_nb,
+ const interaction_const_t *ic) FUNC_TERM
+
+/*! Uploads shift vector to the GPU if the box is dynamic (otherwise just returns). */
+FUNC_QUALIFIER
+void nbnxn_cuda_upload_shiftvec(nbnxn_cuda_ptr_t cu_nb,
+ const nbnxn_atomdata_t *nbatom) FUNC_TERM
+
+/*! Clears GPU outputs: nonbonded force, shift force and energy. */
+FUNC_QUALIFIER
+void nbnxn_cuda_clear_outputs(nbnxn_cuda_ptr_t cu_nb,
+ int flags) FUNC_TERM
+
+/*! Frees all GPU resources used for the nonbonded calculations. */
+FUNC_QUALIFIER
+void nbnxn_cuda_free(FILE *fplog,
+ nbnxn_cuda_ptr_t cu_nb) FUNC_TERM
+
+/*! Returns the GPU timings structure or NULL if GPU is not used or timing is off. */
+FUNC_QUALIFIER
+wallclock_gpu_t * nbnxn_cuda_get_timings(nbnxn_cuda_ptr_t cu_nb)
+#ifdef GMX_GPU
+;
+#else
+{
+ return NULL;
+}
+#endif
+
+/*! Resets nonbonded GPU timings. */
+FUNC_QUALIFIER
+void nbnxn_cuda_reset_timings(nbnxn_cuda_ptr_t cu_nb) FUNC_TERM
+
+/*! Calculates the minimum size of proximity lists to improve SM load balance
+ with CUDA non-bonded kernels. */
+FUNC_QUALIFIER
+int nbnxn_cuda_min_ci_balanced(nbnxn_cuda_ptr_t cu_nb)
+#ifdef GMX_GPU
+;
+#else
+{
+ return -1;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#undef FUNC_TERM
+#undef FUNC_QUALIFIER
+
+#endif /* NBNXN_CUDA_DATA_MGMT_H */
--- /dev/null
- * \param box Simulation box, needed to make group whole.
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+
+/*! \file pull_rotation.h
+ *
+ * @brief Enforced rotation of protein parts or other groups of particles.
+ *
+ * This file contains routines that are used to enforce rotational motion
+ * upon a subgroup of particles.
+ *
+ */
+
+#ifndef _pull_rotation_h
+#define _pull_rotation_h
+
+#include "vec.h"
+#include "typedefs.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*! \brief Initialize the enforced rotation groups.
+ *
+ * This routine does the memory allocation for various helper arrays, opens
+ * the output files etc.
+ *
+ * \param fplog General output file, normally md.log.
+ * \param ir Struct containing MD input parameters, among those
+ * also the enforced rotation parameters.
+ * \param nfile Number of entries in the fnm structure.
+ * \param fnm The filenames struct containing also the names
+ * of the rotation output files.
+ * \param cr Pointer to MPI communication data.
+ * \param x The positions of all MD particles.
- * \param fplog General output file, normally md.log.
++ * \param box The simulation box.
+ * \param mtop Molecular topology.
+ * \param oenv Needed to open the rotation output xvgr file.
+ * \param bVerbose Whether to print extra status information.
+ * \param Flags Flags passed over from main, used to determine
+ * whether or not we are doing a rerun.
+ */
+extern void init_rot(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
+ t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
+ gmx_bool bVerbose, unsigned long Flags);
+
+
+/*! \brief Make a selection of the home atoms for all enforced rotation groups.
+ *
+ * This routine is similar to dd_make_local_pull_groups, but works only with
+ * domain decomposition. It should be called at every domain decomposition.
+ *
+ * \param dd Structure containing domain decomposition data.
+ * \param rot Pointer to all the enforced rotation data.
+ */
+extern void dd_make_local_rotation_groups(gmx_domdec_t *dd, t_rot *rot);
+
+
+/*! \brief Calculation of the enforced rotation potential.
+ *
+ * This is the main enforced rotation module which is called during every time
+ * step. Here the rotation potential as well as the resulting forces are
+ * calculated.
+ *
+ * \param cr Pointer to MPI communication data.
+ * \param ir Struct containing MD input parameters, among those
+ * \param box Simulation box, needed to make group whole.
+ * \param x The positions of all the local particles.
+ * \param t Time.
+ * \param step The time step.
+ * \param wcycle During the potential calculation the wallcycles are
+ * counted. Later they enter the dynamic load balancing.
+ * \param bNS After domain decomposition / neighborsearching several
+ * local arrays have to be updated (masses, shifts)
+ */
+extern void do_rotation(t_commrec *cr, t_inputrec *ir, matrix box, rvec x[], real t,
+ gmx_large_int_t step, gmx_wallcycle_t wcycle, gmx_bool bNS);
+
+
+/*! \brief Add the enforced rotation forces to the official force array.
+ *
+ * Adds the forces from enforced rotation potential to the local forces and
+ * sums up the contributions to the rotation potential from all the nodes. Since
+ * this needs communication, this routine should be called after the SR forces
+ * have been evaluated (in order not to spoil cycle counts).
+ * This routine also outputs data to the various rotation output files (e.g.
+ * the potential, the angle of the group, torques and more).
+ *
+ * \param rot Pointer to all the enforced rotation data.
+ * \param f The local forces to which the rotational forces have
+ * to be added.
+ * \param cr Pointer to MPI communication data.
+ * \param step The time step, used for output.
+ * \param t Time, used for output.
+ */
+extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t);
+
+
+/*! \brief Close the enforced rotation output files.
+ *
- extern void finish_rot(FILE *fplog, t_rot *rot);
+ * \param rot Pointer to all the enforced rotation data.
+ */
++extern void finish_rot(t_rot *rot);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
--- /dev/null
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2012, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef GMX_CRAY_XT3
+#include <catamount/dclock.h>
+#endif
+
+
+#include <stdio.h>
+#include <time.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <math.h>
+#include "typedefs.h"
+#include "string2.h"
+#include "gmxfio.h"
+#include "smalloc.h"
+#include "names.h"
+#include "confio.h"
+#include "mvdata.h"
+#include "txtdump.h"
+#include "pbc.h"
+#include "chargegroup.h"
+#include "vec.h"
+#include "nrnb.h"
+#include "mshift.h"
+#include "mdrun.h"
+#include "update.h"
+#include "physics.h"
+#include "main.h"
+#include "mdatoms.h"
+#include "force.h"
+#include "bondf.h"
+#include "pme.h"
+#include "disre.h"
+#include "orires.h"
+#include "network.h"
+#include "calcmu.h"
+#include "constr.h"
+#include "xvgr.h"
+#include "trnio.h"
+#include "xtcio.h"
+#include "copyrite.h"
+#include "gmx_random.h"
+#include "domdec.h"
+#include "partdec.h"
+#include "gmx_wallcycle.h"
+#include "macros.h"
+
+#include "gromacs/utility/gmxmpi.h"
+
+void GenerateGibbsProbabilities(real *ene, real *p_k, real *pks, int minfep, int maxfep)
+{
+
+ int i;
+ real maxene;
+
+ *pks = 0.0;
+ maxene = ene[minfep];
+ /* find the maximum value */
+ for (i = minfep; i <= maxfep; i++)
+ {
+ if (ene[i] > maxene)
+ {
+ maxene = ene[i];
+ }
+ }
+ /* find the denominator */
+ for (i = minfep; i <= maxfep; i++)
+ {
+ *pks += exp(ene[i]-maxene);
+ }
+ /*numerators*/
+ for (i = minfep; i <= maxfep; i++)
+ {
+ p_k[i] = exp(ene[i]-maxene) / *pks;
+ }
+}
+
+void GenerateWeightedGibbsProbabilities(real *ene, real *p_k, real *pks, int nlim, real *nvals, real delta)
+{
+
+ int i;
+ real maxene;
+ real *nene;
+ *pks = 0.0;
+
+ snew(nene, nlim);
+ for (i = 0; i < nlim; i++)
+ {
+ if (nvals[i] == 0)
+ {
+ /* add the delta, since we need to make sure it's greater than zero, and
+ we need a non-arbitrary number? */
+ nene[i] = ene[i] + log(nvals[i]+delta);
+ }
+ else
+ {
+ nene[i] = ene[i] + log(nvals[i]);
+ }
+ }
+
+ /* find the maximum value */
+ maxene = nene[0];
+ for (i = 0; i < nlim; i++)
+ {
+ if (nene[i] > maxene)
+ {
+ maxene = nene[i];
+ }
+ }
+
+ /* subtract off the maximum, avoiding overflow */
+ for (i = 0; i < nlim; i++)
+ {
+ nene[i] -= maxene;
+ }
+
+ /* find the denominator */
+ for (i = 0; i < nlim; i++)
+ {
+ *pks += exp(nene[i]);
+ }
+
+ /*numerators*/
+ for (i = 0; i < nlim; i++)
+ {
+ p_k[i] = exp(nene[i]) / *pks;
+ }
+ sfree(nene);
+}
+
+real do_logsum(int N, real *a_n)
+{
+
+ /* RETURN VALUE */
+ /* log(\sum_{i=0}^(N-1) exp[a_n]) */
+ real maxarg;
+ real sum;
+ int i;
+ real logsum;
+ /* compute maximum argument to exp(.) */
+
+ maxarg = a_n[0];
+ for (i = 1; i < N; i++)
+ {
+ maxarg = max(maxarg, a_n[i]);
+ }
+
+ /* compute sum of exp(a_n - maxarg) */
+ sum = 0.0;
+ for (i = 0; i < N; i++)
+ {
+ sum = sum + exp(a_n[i] - maxarg);
+ }
+
+ /* compute log sum */
+ logsum = log(sum) + maxarg;
+ return logsum;
+}
+
+int FindMinimum(real *min_metric, int N)
+{
+
+ real min_val;
+ int min_nval, nval;
+
+ min_nval = 0;
+ min_val = min_metric[0];
+
+ for (nval = 0; nval < N; nval++)
+ {
+ if (min_metric[nval] < min_val)
+ {
+ min_val = min_metric[nval];
+ min_nval = nval;
+ }
+ }
+ return min_nval;
+}
+
+static gmx_bool CheckHistogramRatios(int nhisto, real *histo, real ratio)
+{
+
+ int i;
+ real nmean;
+ gmx_bool bIfFlat;
+
+ nmean = 0;
+ for (i = 0; i < nhisto; i++)
+ {
+ nmean += histo[i];
+ }
+
+ if (nmean == 0)
+ {
+ /* no samples! is bad!*/
+ bIfFlat = FALSE;
+ return bIfFlat;
+ }
+ nmean /= (real)nhisto;
+
+ bIfFlat = TRUE;
+ for (i = 0; i < nhisto; i++)
+ {
+ /* make sure that all points are in the ratio < x < 1/ratio range */
+ if (!((histo[i]/nmean < 1.0/ratio) && (histo[i]/nmean > ratio)))
+ {
+ bIfFlat = FALSE;
+ break;
+ }
+ }
+ return bIfFlat;
+}
+
+static gmx_bool CheckIfDoneEquilibrating(int nlim, t_expanded *expand, df_history_t *dfhist, gmx_large_int_t step)
+{
+
+ int i, totalsamples;
+ gmx_bool bDoneEquilibrating = TRUE;
+ gmx_bool bIfFlat;
+
+ /* assume we have equilibrated the weights, then check to see if any of the conditions are not met */
+
+ /* calculate the total number of samples */
+ switch (expand->elmceq)
+ {
+ case elmceqNO:
+ /* We have not equilibrated, and won't, ever. */
+ return FALSE;
+ case elmceqYES:
+ /* we have equilibrated -- we're done */
+ return TRUE;
+ case elmceqSTEPS:
+ /* first, check if we are equilibrating by steps, if we're still under */
+ if (step < expand->equil_steps)
+ {
+ bDoneEquilibrating = FALSE;
+ }
+ break;
+ case elmceqSAMPLES:
+ totalsamples = 0;
+ for (i = 0; i < nlim; i++)
+ {
+ totalsamples += dfhist->n_at_lam[i];
+ }
+ if (totalsamples < expand->equil_samples)
+ {
+ bDoneEquilibrating = FALSE;
+ }
+ break;
+ case elmceqNUMATLAM:
+ for (i = 0; i < nlim; i++)
+ {
+ if (dfhist->n_at_lam[i] < expand->equil_n_at_lam) /* we are still doing the initial sweep, so we're definitely not
+ done equilibrating*/
+ {
+ bDoneEquilibrating = FALSE;
+ break;
+ }
+ }
+ break;
+ case elmceqWLDELTA:
+ if (EWL(expand->elamstats)) /* This check is in readir as well, but
+ just to be sure */
+ {
+ if (dfhist->wl_delta > expand->equil_wl_delta)
+ {
+ bDoneEquilibrating = FALSE;
+ }
+ }
+ break;
+ case elmceqRATIO:
+ /* we can use the flatness as a judge of good weights, as long as
+ we're not doing minvar, or Wang-Landau.
+ But turn off for now until we figure out exactly how we do this.
+ */
+
+ if (!(EWL(expand->elamstats) || expand->elamstats == elamstatsMINVAR))
+ {
+ /* we want to use flatness -avoiding- the forced-through samples. Plus, we need to convert to
+ floats for this histogram function. */
+
+ real *modhisto;
+ snew(modhisto, nlim);
+ for (i = 0; i < nlim; i++)
+ {
+ modhisto[i] = 1.0*(dfhist->n_at_lam[i]-expand->lmc_forced_nstart);
+ }
+ bIfFlat = CheckHistogramRatios(nlim, modhisto, expand->equil_ratio);
+ sfree(modhisto);
+ if (!bIfFlat)
+ {
+ bDoneEquilibrating = FALSE;
+ }
+ }
+ default:
+ bDoneEquilibrating = TRUE;
+ }
+ /* one last case to go though, if we are doing slow growth to get initial values, we haven't finished equilibrating */
+
+ if (expand->lmc_forced_nstart > 0)
+ {
+ for (i = 0; i < nlim; i++)
+ {
+ if (dfhist->n_at_lam[i] < expand->lmc_forced_nstart) /* we are still doing the initial sweep, so we're definitely not
+ done equilibrating*/
+ {
+ bDoneEquilibrating = FALSE;
+ break;
+ }
+ }
+ }
+ return bDoneEquilibrating;
+}
+
+static gmx_bool UpdateWeights(int nlim, t_expanded *expand, df_history_t *dfhist,
+ int fep_state, real *scaled_lamee, real *weighted_lamee, gmx_large_int_t step)
+{
+ real maxdiff = 0.000000001;
+ gmx_bool bSufficientSamples;
+ int i, k, n, nz, indexi, indexk, min_n, max_n, nlam, totali;
+ int n0, np1, nm1, nval, min_nvalm, min_nvalp, maxc;
+ real omega_m1_0, omega_p1_m1, omega_m1_p1, omega_p1_0, clam_osum;
+ real de, de_function, dr, denom, maxdr, pks = 0;
+ real min_val, cnval, zero_sum_weights;
+ real *omegam_array, *weightsm_array, *omegap_array, *weightsp_array, *varm_array, *varp_array, *dwp_array, *dwm_array;
+ real clam_varm, clam_varp, clam_weightsm, clam_weightsp, clam_minvar;
+ real *lam_weights, *lam_minvar_corr, *lam_variance, *lam_dg, *p_k;
+ real *numweighted_lamee, *logfrac;
+ int *nonzero;
+ real chi_m1_0, chi_p1_0, chi_m2_0, chi_p2_0, chi_p1_m1, chi_p2_m1, chi_m1_p1, chi_m2_p1;
+
+ /* if we have equilibrated the weights, exit now */
+ if (dfhist->bEquil)
+ {
+ return FALSE;
+ }
+
+ if (CheckIfDoneEquilibrating(nlim, expand, dfhist, step))
+ {
+ dfhist->bEquil = TRUE;
+ /* zero out the visited states so we know how many equilibrated states we have
+ from here on out.*/
+ for (i = 0; i < nlim; i++)
+ {
+ dfhist->n_at_lam[i] = 0;
+ }
+ return TRUE;
+ }
+
+ /* If we reached this far, we have not equilibrated yet, keep on
+ going resetting the weights */
+
+ if (EWL(expand->elamstats))
+ {
+ if (expand->elamstats == elamstatsWL) /* Standard Wang-Landau */
+ {
+ dfhist->sum_weights[fep_state] -= dfhist->wl_delta;
+ dfhist->wl_histo[fep_state] += 1.0;
+ }
+ else if (expand->elamstats == elamstatsWWL) /* Weighted Wang-Landau */
+ {
+ snew(p_k, nlim);
+
+ /* first increment count */
+ GenerateGibbsProbabilities(weighted_lamee, p_k, &pks, 0, nlim-1);
+ for (i = 0; i < nlim; i++)
+ {
+ dfhist->wl_histo[i] += p_k[i];
+ }
+
+ /* then increment weights (uses count) */
+ pks = 0.0;
+ GenerateWeightedGibbsProbabilities(weighted_lamee, p_k, &pks, nlim, dfhist->wl_histo, dfhist->wl_delta);
+
+ for (i = 0; i < nlim; i++)
+ {
+ dfhist->sum_weights[i] -= dfhist->wl_delta*p_k[i];
+ }
+ /* Alternate definition, using logarithms. Shouldn't make very much difference! */
+ /*
+ real di;
+ for (i=0;i<nlim;i++)
+ {
+ di = 1+dfhist->wl_delta*p_k[i];
+ dfhist->sum_weights[i] -= log(di);
+ }
+ */
+ sfree(p_k);
+ }
+
+ zero_sum_weights = dfhist->sum_weights[0];
+ for (i = 0; i < nlim; i++)
+ {
+ dfhist->sum_weights[i] -= zero_sum_weights;
+ }
+ }
+
+ if (expand->elamstats == elamstatsBARKER || expand->elamstats == elamstatsMETROPOLIS || expand->elamstats == elamstatsMINVAR)
+ {
+
+ de_function = 0; /* to get rid of warnings, but this value will not be used because of the logic */
+ maxc = 2*expand->c_range+1;
+
+ snew(lam_dg, nlim);
+ snew(lam_variance, nlim);
+
+ snew(omegap_array, maxc);
+ snew(weightsp_array, maxc);
+ snew(varp_array, maxc);
+ snew(dwp_array, maxc);
+
+ snew(omegam_array, maxc);
+ snew(weightsm_array, maxc);
+ snew(varm_array, maxc);
+ snew(dwm_array, maxc);
+
+ /* unpack the current lambdas -- we will only update 2 of these */
+
+ for (i = 0; i < nlim-1; i++)
+ { /* only through the second to last */
+ lam_dg[i] = dfhist->sum_dg[i+1] - dfhist->sum_dg[i];
+ lam_variance[i] = pow(dfhist->sum_variance[i+1], 2) - pow(dfhist->sum_variance[i], 2);
+ }
+
+ /* accumulate running averages */
+ for (nval = 0; nval < maxc; nval++)
+ {
+ /* constants for later use */
+ cnval = (real)(nval-expand->c_range);
+ /* actually, should be able to rewrite it w/o exponential, for better numerical stability */
+ if (fep_state > 0)
+ {
+ de = exp(cnval - (scaled_lamee[fep_state]-scaled_lamee[fep_state-1]));
+ if (expand->elamstats == elamstatsBARKER || expand->elamstats == elamstatsMINVAR)
+ {
+ de_function = 1.0/(1.0+de);
+ }
+ else if (expand->elamstats == elamstatsMETROPOLIS)
+ {
+ if (de < 1.0)
+ {
+ de_function = 1.0;
+ }
+ else
+ {
+ de_function = 1.0/de;
+ }
+ }
+ dfhist->accum_m[fep_state][nval] += de_function;
+ dfhist->accum_m2[fep_state][nval] += de_function*de_function;
+ }
+
+ if (fep_state < nlim-1)
+ {
+ de = exp(-cnval + (scaled_lamee[fep_state+1]-scaled_lamee[fep_state]));
+ if (expand->elamstats == elamstatsBARKER || expand->elamstats == elamstatsMINVAR)
+ {
+ de_function = 1.0/(1.0+de);
+ }
+ else if (expand->elamstats == elamstatsMETROPOLIS)
+ {
+ if (de < 1.0)
+ {
+ de_function = 1.0;
+ }
+ else
+ {
+ de_function = 1.0/de;
+ }
+ }
+ dfhist->accum_p[fep_state][nval] += de_function;
+ dfhist->accum_p2[fep_state][nval] += de_function*de_function;
+ }
+
+ /* Metropolis transition and Barker transition (unoptimized Bennett) acceptance weight determination */
+
+ n0 = dfhist->n_at_lam[fep_state];
+ if (fep_state > 0)
+ {
+ nm1 = dfhist->n_at_lam[fep_state-1];
+ }
+ else
+ {
+ nm1 = 0;
+ }
+ if (fep_state < nlim-1)
+ {
+ np1 = dfhist->n_at_lam[fep_state+1];
+ }
+ else
+ {
+ np1 = 0;
+ }
+
+ /* logic SHOULD keep these all set correctly whatever the logic, but apparently it can't figure it out. */
+ chi_m1_0 = chi_p1_0 = chi_m2_0 = chi_p2_0 = chi_p1_m1 = chi_p2_m1 = chi_m1_p1 = chi_m2_p1 = 0;
+
+ if (n0 > 0)
+ {
+ chi_m1_0 = dfhist->accum_m[fep_state][nval]/n0;
+ chi_p1_0 = dfhist->accum_p[fep_state][nval]/n0;
+ chi_m2_0 = dfhist->accum_m2[fep_state][nval]/n0;
+ chi_p2_0 = dfhist->accum_p2[fep_state][nval]/n0;
+ }
+
+ if ((fep_state > 0 ) && (nm1 > 0))
+ {
+ chi_p1_m1 = dfhist->accum_p[fep_state-1][nval]/nm1;
+ chi_p2_m1 = dfhist->accum_p2[fep_state-1][nval]/nm1;
+ }
+
+ if ((fep_state < nlim-1) && (np1 > 0))
+ {
+ chi_m1_p1 = dfhist->accum_m[fep_state+1][nval]/np1;
+ chi_m2_p1 = dfhist->accum_m2[fep_state+1][nval]/np1;
+ }
+
+ omega_m1_0 = 0;
+ omega_p1_0 = 0;
+ clam_weightsm = 0;
+ clam_weightsp = 0;
+ clam_varm = 0;
+ clam_varp = 0;
+
+ if (fep_state > 0)
+ {
+ if (n0 > 0)
+ {
+ omega_m1_0 = chi_m2_0/(chi_m1_0*chi_m1_0) - 1.0;
+ }
+ if (nm1 > 0)
+ {
+ omega_p1_m1 = chi_p2_m1/(chi_p1_m1*chi_p1_m1) - 1.0;
+ }
+ if ((n0 > 0) && (nm1 > 0))
+ {
+ clam_weightsm = (log(chi_m1_0) - log(chi_p1_m1)) + cnval;
+ clam_varm = (1.0/n0)*(omega_m1_0) + (1.0/nm1)*(omega_p1_m1);
+ }
+ }
+
+ if (fep_state < nlim-1)
+ {
+ if (n0 > 0)
+ {
+ omega_p1_0 = chi_p2_0/(chi_p1_0*chi_p1_0) - 1.0;
+ }
+ if (np1 > 0)
+ {
+ omega_m1_p1 = chi_m2_p1/(chi_m1_p1*chi_m1_p1) - 1.0;
+ }
+ if ((n0 > 0) && (np1 > 0))
+ {
+ clam_weightsp = (log(chi_m1_p1) - log(chi_p1_0)) + cnval;
+ clam_varp = (1.0/np1)*(omega_m1_p1) + (1.0/n0)*(omega_p1_0);
+ }
+ }
+
+ if (n0 > 0)
+ {
+ omegam_array[nval] = omega_m1_0;
+ }
+ else
+ {
+ omegam_array[nval] = 0;
+ }
+ weightsm_array[nval] = clam_weightsm;
+ varm_array[nval] = clam_varm;
+ if (nm1 > 0)
+ {
+ dwm_array[nval] = fabs( (cnval + log((1.0*n0)/nm1)) - lam_dg[fep_state-1] );
+ }
+ else
+ {
+ dwm_array[nval] = fabs( cnval - lam_dg[fep_state-1] );
+ }
+
+ if (n0 > 0)
+ {
+ omegap_array[nval] = omega_p1_0;
+ }
+ else
+ {
+ omegap_array[nval] = 0;
+ }
+ weightsp_array[nval] = clam_weightsp;
+ varp_array[nval] = clam_varp;
+ if ((np1 > 0) && (n0 > 0))
+ {
+ dwp_array[nval] = fabs( (cnval + log((1.0*np1)/n0)) - lam_dg[fep_state] );
+ }
+ else
+ {
+ dwp_array[nval] = fabs( cnval - lam_dg[fep_state] );
+ }
+
+ }
+
+ /* find the C's closest to the old weights value */
+
+ min_nvalm = FindMinimum(dwm_array, maxc);
+ omega_m1_0 = omegam_array[min_nvalm];
+ clam_weightsm = weightsm_array[min_nvalm];
+ clam_varm = varm_array[min_nvalm];
+
+ min_nvalp = FindMinimum(dwp_array, maxc);
+ omega_p1_0 = omegap_array[min_nvalp];
+ clam_weightsp = weightsp_array[min_nvalp];
+ clam_varp = varp_array[min_nvalp];
+
+ clam_osum = omega_m1_0 + omega_p1_0;
+ clam_minvar = 0;
+ if (clam_osum > 0)
+ {
+ clam_minvar = 0.5*log(clam_osum);
+ }
+
+ if (fep_state > 0)
+ {
+ lam_dg[fep_state-1] = clam_weightsm;
+ lam_variance[fep_state-1] = clam_varm;
+ }
+
+ if (fep_state < nlim-1)
+ {
+ lam_dg[fep_state] = clam_weightsp;
+ lam_variance[fep_state] = clam_varp;
+ }
+
+ if (expand->elamstats == elamstatsMINVAR)
+ {
+ bSufficientSamples = TRUE;
+ /* make sure they are all past a threshold */
+ for (i = 0; i < nlim; i++)
+ {
+ if (dfhist->n_at_lam[i] < expand->minvarmin)
+ {
+ bSufficientSamples = FALSE;
+ }
+ }
+ if (bSufficientSamples)
+ {
+ dfhist->sum_minvar[fep_state] = clam_minvar;
+ if (fep_state == 0)
+ {
+ for (i = 0; i < nlim; i++)
+ {
+ dfhist->sum_minvar[i] += (expand->minvar_const-clam_minvar);
+ }
+ expand->minvar_const = clam_minvar;
+ dfhist->sum_minvar[fep_state] = 0.0;
+ }
+ else
+ {
+ dfhist->sum_minvar[fep_state] -= expand->minvar_const;
+ }
+ }
+ }
+
+ /* we need to rezero minvar now, since it could change at fep_state = 0 */
+ dfhist->sum_dg[0] = 0.0;
+ dfhist->sum_variance[0] = 0.0;
+ dfhist->sum_weights[0] = dfhist->sum_dg[0] + dfhist->sum_minvar[0]; /* should be zero */
+
+ for (i = 1; i < nlim; i++)
+ {
+ dfhist->sum_dg[i] = lam_dg[i-1] + dfhist->sum_dg[i-1];
+ dfhist->sum_variance[i] = sqrt(lam_variance[i-1] + pow(dfhist->sum_variance[i-1], 2));
+ dfhist->sum_weights[i] = dfhist->sum_dg[i] + dfhist->sum_minvar[i];
+ }
+
+ sfree(lam_dg);
+ sfree(lam_variance);
+
+ sfree(omegam_array);
+ sfree(weightsm_array);
+ sfree(varm_array);
+ sfree(dwm_array);
+
+ sfree(omegap_array);
+ sfree(weightsp_array);
+ sfree(varp_array);
+ sfree(dwp_array);
+ }
+ return FALSE;
+}
+
+static int ChooseNewLambda(FILE *log, int nlim, t_expanded *expand, df_history_t *dfhist, int fep_state, real *weighted_lamee, real *p_k, gmx_rng_t rng)
+{
+ /* Choose new lambda value, and update transition matrix */
+
+ int i, ifep, jfep, minfep, maxfep, lamnew, lamtrial, starting_fep_state;
+ real r1, r2, pks, de_old, de_new, de, trialprob, tprob = 0;
+ real **Tij;
+ real *propose, *accept, *remainder;
+ real sum, pnorm;
+ gmx_bool bRestricted;
+
+ starting_fep_state = fep_state;
+ lamnew = fep_state; /* so that there is a default setting -- stays the same */
+
+ if (!EWL(expand->elamstats)) /* ignore equilibrating the weights if using WL */
+ {
+ if ((expand->lmc_forced_nstart > 0) && (dfhist->n_at_lam[nlim-1] <= expand->lmc_forced_nstart))
+ {
+ /* Use a marching method to run through the lambdas and get preliminary free energy data,
+ before starting 'free' sampling. We start free sampling when we have enough at each lambda */
+
+ /* if we have enough at this lambda, move on to the next one */
+
+ if (dfhist->n_at_lam[fep_state] == expand->lmc_forced_nstart)
+ {
+ lamnew = fep_state+1;
+ if (lamnew == nlim) /* whoops, stepped too far! */
+ {
+ lamnew -= 1;
+ }
+ }
+ else
+ {
+ lamnew = fep_state;
+ }
+ return lamnew;
+ }
+ }
+
+ snew(propose, nlim);
+ snew(accept, nlim);
+ snew(remainder, nlim);
+
+ for (i = 0; i < expand->lmc_repeats; i++)
+ {
+
+ for (ifep = 0; ifep < nlim; ifep++)
+ {
+ propose[ifep] = 0;
+ accept[ifep] = 0;
+ }
+
+ if ((expand->elmcmove == elmcmoveGIBBS) || (expand->elmcmove == elmcmoveMETGIBBS))
+ {
+ bRestricted = TRUE;
+ /* use the Gibbs sampler, with restricted range */
+ if (expand->gibbsdeltalam < 0)
+ {
+ minfep = 0;
+ maxfep = nlim-1;
+ bRestricted = FALSE;
+ }
+ else
+ {
+ minfep = fep_state - expand->gibbsdeltalam;
+ maxfep = fep_state + expand->gibbsdeltalam;
+ if (minfep < 0)
+ {
+ minfep = 0;
+ }
+ if (maxfep > nlim-1)
+ {
+ maxfep = nlim-1;
+ }
+ }
+
+ GenerateGibbsProbabilities(weighted_lamee, p_k, &pks, minfep, maxfep);
+
+ if (expand->elmcmove == elmcmoveGIBBS)
+ {
+ for (ifep = minfep; ifep <= maxfep; ifep++)
+ {
+ propose[ifep] = p_k[ifep];
+ accept[ifep] = 1.0;
+ }
+ /* Gibbs sampling */
+ r1 = gmx_rng_uniform_real(rng);
+ for (lamnew = minfep; lamnew <= maxfep; lamnew++)
+ {
+ if (r1 <= p_k[lamnew])
+ {
+ break;
+ }
+ r1 -= p_k[lamnew];
+ }
+ }
+ else if (expand->elmcmove == elmcmoveMETGIBBS)
+ {
+
+ /* Metropolized Gibbs sampling */
+ for (ifep = minfep; ifep <= maxfep; ifep++)
+ {
+ remainder[ifep] = 1 - p_k[ifep];
+ }
+
+ /* find the proposal probabilities */
+
+ if (remainder[fep_state] == 0)
+ {
+ /* only the current state has any probability */
+ /* we have to stay at the current state */
+ lamnew = fep_state;
+ }
+ else
+ {
+ for (ifep = minfep; ifep <= maxfep; ifep++)
+ {
+ if (ifep != fep_state)
+ {
+ propose[ifep] = p_k[ifep]/remainder[fep_state];
+ }
+ else
+ {
+ propose[ifep] = 0;
+ }
+ }
+
+ r1 = gmx_rng_uniform_real(rng);
+ for (lamtrial = minfep; lamtrial <= maxfep; lamtrial++)
+ {
+ pnorm = p_k[lamtrial]/remainder[fep_state];
+ if (lamtrial != fep_state)
+ {
+ if (r1 <= pnorm)
+ {
+ break;
+ }
+ r1 -= pnorm;
+ }
+ }
+
+ /* we have now selected lamtrial according to p(lamtrial)/1-p(fep_state) */
+ tprob = 1.0;
+ /* trial probability is min{1,\frac{1 - p(old)}{1-p(new)} MRS 1/8/2008 */
+ trialprob = (remainder[fep_state])/(remainder[lamtrial]);
+ if (trialprob < tprob)
+ {
+ tprob = trialprob;
+ }
+ r2 = gmx_rng_uniform_real(rng);
+ if (r2 < tprob)
+ {
+ lamnew = lamtrial;
+ }
+ else
+ {
+ lamnew = fep_state;
+ }
+ }
+
+ /* now figure out the acceptance probability for each */
+ for (ifep = minfep; ifep <= maxfep; ifep++)
+ {
+ tprob = 1.0;
+ if (remainder[ifep] != 0)
+ {
+ trialprob = (remainder[fep_state])/(remainder[ifep]);
+ }
+ else
+ {
+ trialprob = 1.0; /* this state is the only choice! */
+ }
+ if (trialprob < tprob)
+ {
+ tprob = trialprob;
+ }
+ /* probability for fep_state=0, but that's fine, it's never proposed! */
+ accept[ifep] = tprob;
+ }
+ }
+
+ if (lamnew > maxfep)
+ {
+ /* it's possible some rounding is failing */
+ if (remainder[fep_state] < 2.0e-15)
+ {
+ /* probably numerical rounding error -- no state other than the original has weight */
+ lamnew = fep_state;
+ }
+ else
+ {
+ /* probably not a numerical issue */
+ int loc = 0;
+ int nerror = 200+(maxfep-minfep+1)*60;
+ char *errorstr;
+ snew(errorstr, nerror);
+ /* if its greater than maxfep, then something went wrong -- probably underflow in the calculation
+ of sum weights. Generated detailed info for failure */
+ loc += sprintf(errorstr, "Something wrong in choosing new lambda state with a Gibbs move -- probably underflow in weight determination.\nDenominator is: %3d%17.10e\n i dE numerator weights\n", 0, pks);
+ for (ifep = minfep; ifep <= maxfep; ifep++)
+ {
+ loc += sprintf(&errorstr[loc], "%3d %17.10e%17.10e%17.10e\n", ifep, weighted_lamee[ifep], p_k[ifep], dfhist->sum_weights[ifep]);
+ }
+ gmx_fatal(FARGS, errorstr);
+ }
+ }
+ }
+ else if ((expand->elmcmove == elmcmoveMETROPOLIS) || (expand->elmcmove == elmcmoveBARKER))
+ {
+ /* use the metropolis sampler with trial +/- 1 */
+ r1 = gmx_rng_uniform_real(rng);
+ if (r1 < 0.5)
+ {
+ if (fep_state == 0)
+ {
+ lamtrial = fep_state;
+ }
+ else
+ {
+ lamtrial = fep_state-1;
+ }
+ }
+ else
+ {
+ if (fep_state == nlim-1)
+ {
+ lamtrial = fep_state;
+ }
+ else
+ {
+ lamtrial = fep_state+1;
+ }
+ }
+
+ de = weighted_lamee[lamtrial] - weighted_lamee[fep_state];
+ if (expand->elmcmove == elmcmoveMETROPOLIS)
+ {
+ tprob = 1.0;
+ trialprob = exp(de);
+ if (trialprob < tprob)
+ {
+ tprob = trialprob;
+ }
+ propose[fep_state] = 0;
+ propose[lamtrial] = 1.0; /* note that this overwrites the above line if fep_state = ntrial, which only occurs at the ends */
+ accept[fep_state] = 1.0; /* doesn't actually matter, never proposed unless fep_state = ntrial, in which case it's 1.0 anyway */
+ accept[lamtrial] = tprob;
+
+ }
+ else if (expand->elmcmove == elmcmoveBARKER)
+ {
+ tprob = 1.0/(1.0+exp(-de));
+
+ propose[fep_state] = (1-tprob);
+ propose[lamtrial] += tprob; /* we add, to account for the fact that at the end, they might be the same point */
+ accept[fep_state] = 1.0;
+ accept[lamtrial] = 1.0;
+ }
+
+ r2 = gmx_rng_uniform_real(rng);
+ if (r2 < tprob)
+ {
+ lamnew = lamtrial;
+ }
+ else
+ {
+ lamnew = fep_state;
+ }
+ }
+
+ for (ifep = 0; ifep < nlim; ifep++)
+ {
+ dfhist->Tij[fep_state][ifep] += propose[ifep]*accept[ifep];
+ dfhist->Tij[fep_state][fep_state] += propose[ifep]*(1.0-accept[ifep]);
+ }
+ fep_state = lamnew;
+ }
+
+ dfhist->Tij_empirical[starting_fep_state][lamnew] += 1.0;
+
+ sfree(propose);
+ sfree(accept);
+ sfree(remainder);
+
+ return lamnew;
+}
+
+/* print out the weights to the log, along with current state */
+extern void PrintFreeEnergyInfoToFile(FILE *outfile, t_lambda *fep, t_expanded *expand, t_simtemp *simtemp, df_history_t *dfhist,
+ int nlam, int frequency, gmx_large_int_t step)
+{
+ int nlim, i, ifep, jfep;
+ real dw, dg, dv, dm, Tprint;
+ real *temps;
+ const char *print_names[efptNR] = {" FEPL", "MassL", "CoulL", " VdwL", "BondL", "RestT", "Temp.(K)"};
+ gmx_bool bSimTemp = FALSE;
+
+ nlim = fep->n_lambda;
+ if (simtemp != NULL)
+ {
+ bSimTemp = TRUE;
+ }
+
+ if (mod(step, frequency) == 0)
+ {
+ fprintf(outfile, " MC-lambda information\n");
+ if (EWL(expand->elamstats) && (!(dfhist->bEquil)))
+ {
+ fprintf(outfile, " Wang-Landau incrementor is: %11.5g\n", dfhist->wl_delta);
+ }
+ fprintf(outfile, " N");
+ for (i = 0; i < efptNR; i++)
+ {
+ if (fep->separate_dvdl[i])
+ {
+ fprintf(outfile, "%7s", print_names[i]);
+ }
+ else if ((i == efptTEMPERATURE) && bSimTemp)
+ {
+ fprintf(outfile, "%10s", print_names[i]); /* more space for temperature formats */
+ }
+ }
+ fprintf(outfile, " Count ");
+ if (expand->elamstats == elamstatsMINVAR)
+ {
+ fprintf(outfile, "W(in kT) G(in kT) dG(in kT) dV(in kT)\n");
+ }
+ else
+ {
+ fprintf(outfile, "G(in kT) dG(in kT)\n");
+ }
+ for (ifep = 0; ifep < nlim; ifep++)
+ {
+ if (ifep == nlim-1)
+ {
+ dw = 0.0;
+ dg = 0.0;
+ dv = 0.0;
+ dm = 0.0;
+ }
+ else
+ {
+ dw = dfhist->sum_weights[ifep+1] - dfhist->sum_weights[ifep];
+ dg = dfhist->sum_dg[ifep+1] - dfhist->sum_dg[ifep];
+ dv = sqrt(pow(dfhist->sum_variance[ifep+1], 2) - pow(dfhist->sum_variance[ifep], 2));
+ dm = dfhist->sum_minvar[ifep+1] - dfhist->sum_minvar[ifep];
+
+ }
+ fprintf(outfile, "%3d", (ifep+1));
+ for (i = 0; i < efptNR; i++)
+ {
+ if (fep->separate_dvdl[i])
+ {
+ fprintf(outfile, "%7.3f", fep->all_lambda[i][ifep]);
+ }
+ else if (i == efptTEMPERATURE && bSimTemp)
+ {
+ fprintf(outfile, "%9.3f", simtemp->temperatures[ifep]);
+ }
+ }
+ if (EWL(expand->elamstats) && (!(dfhist->bEquil))) /* if performing WL and still haven't equilibrated */
+ {
+ if (expand->elamstats == elamstatsWL)
+ {
+ fprintf(outfile, " %8d", (int)dfhist->wl_histo[ifep]);
+ }
+ else
+ {
+ fprintf(outfile, " %8.3f", dfhist->wl_histo[ifep]);
+ }
+ }
+ else /* we have equilibrated weights */
+ {
+ fprintf(outfile, " %8d", dfhist->n_at_lam[ifep]);
+ }
+ if (expand->elamstats == elamstatsMINVAR)
+ {
+ fprintf(outfile, " %10.5f %10.5f %10.5f %10.5f", dfhist->sum_weights[ifep], dfhist->sum_dg[ifep], dg, dv);
+ }
+ else
+ {
+ fprintf(outfile, " %10.5f %10.5f", dfhist->sum_weights[ifep], dw);
+ }
+ if (ifep == nlam)
+ {
+ fprintf(outfile, " <<\n");
+ }
+ else
+ {
+ fprintf(outfile, " \n");
+ }
+ }
+ fprintf(outfile, "\n");
+
+ if ((mod(step, expand->nstTij) == 0) && (expand->nstTij > 0) && (step > 0))
+ {
+ fprintf(outfile, " Transition Matrix\n");
+ for (ifep = 0; ifep < nlim; ifep++)
+ {
+ fprintf(outfile, "%12d", (ifep+1));
+ }
+ fprintf(outfile, "\n");
+ for (ifep = 0; ifep < nlim; ifep++)
+ {
+ for (jfep = 0; jfep < nlim; jfep++)
+ {
+ if (dfhist->n_at_lam[ifep] > 0)
+ {
+ if (expand->bSymmetrizedTMatrix)
+ {
+ Tprint = (dfhist->Tij[ifep][jfep]+dfhist->Tij[jfep][ifep])/(dfhist->n_at_lam[ifep]+dfhist->n_at_lam[jfep]);
+ }
+ else
+ {
+ Tprint = (dfhist->Tij[ifep][jfep])/(dfhist->n_at_lam[ifep]);
+ }
+ }
+ else
+ {
+ Tprint = 0.0;
+ }
+ fprintf(outfile, "%12.8f", Tprint);
+ }
+ fprintf(outfile, "%3d\n", (ifep+1));
+ }
+
+ fprintf(outfile, " Empirical Transition Matrix\n");
+ for (ifep = 0; ifep < nlim; ifep++)
+ {
+ fprintf(outfile, "%12d", (ifep+1));
+ }
+ fprintf(outfile, "\n");
+ for (ifep = 0; ifep < nlim; ifep++)
+ {
+ for (jfep = 0; jfep < nlim; jfep++)
+ {
+ if (dfhist->n_at_lam[ifep] > 0)
+ {
+ if (expand->bSymmetrizedTMatrix)
+ {
+ Tprint = (dfhist->Tij_empirical[ifep][jfep]+dfhist->Tij_empirical[jfep][ifep])/(dfhist->n_at_lam[ifep]+dfhist->n_at_lam[jfep]);
+ }
+ else
+ {
+ Tprint = dfhist->Tij_empirical[ifep][jfep]/(dfhist->n_at_lam[ifep]);
+ }
+ }
+ else
+ {
+ Tprint = 0.0;
+ }
+ fprintf(outfile, "%12.8f", Tprint);
+ }
+ fprintf(outfile, "%3d\n", (ifep+1));
+ }
+ }
+ }
+}
+
+extern void get_mc_state(gmx_rng_t rng, t_state *state)
+{
+ gmx_rng_get_state(rng, state->mc_rng, state->mc_rngi);
+}
+
+extern void set_mc_state(gmx_rng_t rng, t_state *state)
+{
+ gmx_rng_set_state(rng, state->mc_rng, state->mc_rngi[0]);
+}
+
+extern int ExpandedEnsembleDynamics(FILE *log, t_inputrec *ir, gmx_enerdata_t *enerd,
+ t_state *state, t_extmass *MassQ, df_history_t *dfhist,
+ gmx_large_int_t step, gmx_rng_t mcrng,
+ rvec *v, t_mdatoms *mdatoms)
+{
+ real *pfep_lamee, *p_k, *scaled_lamee, *weighted_lamee;
+ int i, nlam, nlim, lamnew, totalsamples;
+ real oneovert, maxscaled = 0, maxweighted = 0;
+ t_expanded *expand;
+ t_simtemp *simtemp;
+ double *temperature_lambdas;
+ gmx_bool bIfReset, bSwitchtoOneOverT, bDoneEquilibrating = FALSE;
+
+ expand = ir->expandedvals;
+ simtemp = ir->simtempvals;
+ nlim = ir->fepvals->n_lambda;
+ nlam = state->fep_state;
+
+ snew(scaled_lamee, nlim);
+ snew(weighted_lamee, nlim);
+ snew(pfep_lamee, nlim);
+ snew(p_k, nlim);
+
+ if (expand->bInit_weights) /* if initialized weights, we need to fill them in */
+ {
+ dfhist->wl_delta = expand->init_wl_delta; /* MRS -- this would fit better somewhere else? */
+ for (i = 0; i < nlim; i++)
+ {
+ dfhist->sum_weights[i] = expand->init_lambda_weights[i];
+ dfhist->sum_dg[i] = expand->init_lambda_weights[i];
+ }
+ expand->bInit_weights = FALSE;
+ }
+
+ /* update the count at the current lambda*/
+ dfhist->n_at_lam[nlam]++;
+
+ /* need to calculate the PV term somewhere, but not needed here? Not until there's a lambda state that's
+ pressure controlled.*/
+ /*
+ pVTerm = 0;
+ where does this PV term go?
+ for (i=0;i<nlim;i++)
+ {
+ fep_lamee[i] += pVTerm;
+ }
+ */
+
+ /* determine the minimum value to avoid overflow. Probably a better way to do this */
+ /* we don't need to include the pressure term, since the volume is the same between the two.
+ is there some term we are neglecting, however? */
+
+ if (ir->efep != efepNO)
+ {
+ for (i = 0; i < nlim; i++)
+ {
+ if (ir->bSimTemp)
+ {
+ /* Note -- this assumes no mass changes, since kinetic energy is not added . . . */
+ scaled_lamee[i] = (enerd->enerpart_lambda[i+1]-enerd->enerpart_lambda[0])/(simtemp->temperatures[i]*BOLTZ)
+ + enerd->term[F_EPOT]*(1.0/(simtemp->temperatures[i])- 1.0/(simtemp->temperatures[nlam]))/BOLTZ;
+ }
+ else
+ {
+ scaled_lamee[i] = (enerd->enerpart_lambda[i+1]-enerd->enerpart_lambda[0])/(expand->mc_temp*BOLTZ);
+ /* mc_temp is currently set to the system reft unless otherwise defined */
+ }
+
+ /* save these energies for printing, so they don't get overwritten by the next step */
+ /* they aren't overwritten in the non-free energy case, but we always print with these
+ for simplicity */
+ }
+ }
+ else
+ {
+ if (ir->bSimTemp)
+ {
+ for (i = 0; i < nlim; i++)
+ {
+ scaled_lamee[i] = enerd->term[F_EPOT]*(1.0/simtemp->temperatures[i] - 1.0/simtemp->temperatures[nlam])/BOLTZ;
+ }
+ }
+ }
+
+ for (i = 0; i < nlim; i++)
+ {
+ pfep_lamee[i] = scaled_lamee[i];
+
+ weighted_lamee[i] = dfhist->sum_weights[i] - scaled_lamee[i];
+ if (i == 0)
+ {
+ maxscaled = scaled_lamee[i];
+ maxweighted = weighted_lamee[i];
+ }
+ else
+ {
+ if (scaled_lamee[i] > maxscaled)
+ {
+ maxscaled = scaled_lamee[i];
+ }
+ if (weighted_lamee[i] > maxweighted)
+ {
+ maxweighted = weighted_lamee[i];
+ }
+ }
+ }
+
+ for (i = 0; i < nlim; i++)
+ {
+ scaled_lamee[i] -= maxscaled;
+ weighted_lamee[i] -= maxweighted;
+ }
+
+ /* update weights - we decide whether or not to actually do this inside */
+
+ bDoneEquilibrating = UpdateWeights(nlim, expand, dfhist, nlam, scaled_lamee, weighted_lamee, step);
+ if (bDoneEquilibrating)
+ {
+ if (log)
+ {
+ fprintf(log, "\nStep %d: Weights have equilibrated, using criteria: %s\n", (int)step, elmceq_names[expand->elmceq]);
+ }
+ }
+
+ lamnew = ChooseNewLambda(log, nlim, expand, dfhist, nlam, weighted_lamee, p_k, mcrng);
+ /* if using simulated tempering, we need to adjust the temperatures */
+ if (ir->bSimTemp && (lamnew != nlam)) /* only need to change the temperatures if we change the state */
+ {
+ int i, j, n, d;
+ real *buf_ngtc;
+ real told;
+ int nstart, nend, gt;
+
+ snew(buf_ngtc, ir->opts.ngtc);
+
+ for (i = 0; i < ir->opts.ngtc; i++)
+ {
+ if (ir->opts.ref_t[i] > 0)
+ {
+ told = ir->opts.ref_t[i];
+ ir->opts.ref_t[i] = simtemp->temperatures[lamnew];
+ buf_ngtc[i] = sqrt(ir->opts.ref_t[i]/told); /* using the buffer as temperature scaling */
+ }
+ }
+
+ /* we don't need to manipulate the ekind information, as it isn't due to be reset until the next step anyway */
+
+ nstart = mdatoms->start;
+ nend = nstart + mdatoms->homenr;
+ for (n = nstart; n < nend; n++)
+ {
+ gt = 0;
+ if (mdatoms->cTC)
+ {
+ gt = mdatoms->cTC[n];
+ }
+ for (d = 0; d < DIM; d++)
+ {
+ v[n][d] *= buf_ngtc[gt];
+ }
+ }
+
+ if (IR_NPT_TROTTER(ir) || IR_NPH_TROTTER(ir) || IR_NVT_TROTTER(ir))
+ {
+ /* we need to recalculate the masses if the temperature has changed */
+ init_npt_masses(ir, state, MassQ, FALSE);
+ for (i = 0; i < state->nnhpres; i++)
+ {
+ for (j = 0; j < ir->opts.nhchainlength; j++)
+ {
+ state->nhpres_vxi[i+j] *= buf_ngtc[i];
+ }
+ }
+ for (i = 0; i < ir->opts.ngtc; i++)
+ {
+ for (j = 0; j < ir->opts.nhchainlength; j++)
+ {
+ state->nosehoover_vxi[i+j] *= buf_ngtc[i];
+ }
+ }
+ }
+ sfree(buf_ngtc);
+ }
+
+ /* now check on the Wang-Landau updating critera */
+
+ if (EWL(expand->elamstats))
+ {
+ bSwitchtoOneOverT = FALSE;
+ if (expand->bWLoneovert)
+ {
+ totalsamples = 0;
+ for (i = 0; i < nlim; i++)
+ {
+ totalsamples += dfhist->n_at_lam[i];
+ }
+ oneovert = (1.0*nlim)/totalsamples;
+ /* oneovert has decreasd by a bit since last time, so we actually make sure its within one of this number */
+ /* switch to 1/t incrementing when wl_delta has decreased at least once, and wl_delta is now less than 1/t */
+ if ((dfhist->wl_delta <= ((totalsamples)/(totalsamples-1.00001))*oneovert) &&
+ (dfhist->wl_delta < expand->init_wl_delta))
+ {
+ bSwitchtoOneOverT = TRUE;
+ }
+ }
+ if (bSwitchtoOneOverT)
+ {
+ dfhist->wl_delta = oneovert; /* now we reduce by this each time, instead of only at flatness */
+ }
+ else
+ {
+ bIfReset = CheckHistogramRatios(nlim, dfhist->wl_histo, expand->wl_ratio);
+ if (bIfReset)
+ {
+ for (i = 0; i < nlim; i++)
+ {
+ dfhist->wl_histo[i] = 0;
+ }
+ dfhist->wl_delta *= expand->wl_scale;
+ if (log)
+ {
+ fprintf(log, "\nStep %d: weights are now:", (int)step);
+ for (i = 0; i < nlim; i++)
+ {
+ fprintf(log, " %.5f", dfhist->sum_weights[i]);
+ }
+ fprintf(log, "\n");
+ }
+ }
+ }
+ }
++ sfree(pfep_lamee);
+ sfree(scaled_lamee);
+ sfree(weighted_lamee);
+ sfree(p_k);
+
+ return lamnew;
+}
--- /dev/null
- enerd->term[F_DVDL_BONDED] += enerd->term[F_DVDL_CONSTR];
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+#include "sysstuff.h"
+#include "typedefs.h"
+#include "macros.h"
+#include "smalloc.h"
+#include "macros.h"
+#include "physics.h"
+#include "force.h"
+#include "nonbonded.h"
+#include "names.h"
+#include "network.h"
+#include "pbc.h"
+#include "ns.h"
+#include "nrnb.h"
+#include "bondf.h"
+#include "mshift.h"
+#include "txtdump.h"
+#include "coulomb.h"
+#include "pme.h"
+#include "mdrun.h"
+#include "domdec.h"
+#include "partdec.h"
+#include "qmmm.h"
+#include "gmx_omp_nthreads.h"
+
+
+void ns(FILE *fp,
+ t_forcerec *fr,
+ rvec x[],
+ matrix box,
+ gmx_groups_t *groups,
+ t_grpopts *opts,
+ gmx_localtop_t *top,
+ t_mdatoms *md,
+ t_commrec *cr,
+ t_nrnb *nrnb,
+ real *lambda,
+ real *dvdlambda,
+ gmx_grppairener_t *grppener,
+ gmx_bool bFillGrid,
+ gmx_bool bDoLongRangeNS)
+{
+ char *ptr;
+ int nsearch;
+
+
+ if (!fr->ns.nblist_initialized)
+ {
+ init_neighbor_list(fp, fr, md->homenr);
+ }
+
+ if (fr->bTwinRange)
+ {
+ fr->nlr = 0;
+ }
+
+ nsearch = search_neighbours(fp, fr, x, box, top, groups, cr, nrnb, md,
+ lambda, dvdlambda, grppener,
+ bFillGrid, bDoLongRangeNS, TRUE);
+ if (debug)
+ {
+ fprintf(debug, "nsearch = %d\n", nsearch);
+ }
+
+ /* Check whether we have to do dynamic load balancing */
+ /*if ((nsb->nstDlb > 0) && (mod(step,nsb->nstDlb) == 0))
+ count_nb(cr,nsb,&(top->blocks[ebCGS]),nns,fr->nlr,
+ &(top->idef),opts->ngener);
+ */
+ if (fr->ns.dump_nl > 0)
+ {
+ dump_nblist(fp, cr, fr, fr->ns.dump_nl);
+ }
+}
+
+static void reduce_thread_forces(int n, rvec *f,
+ tensor vir,
+ real *Vcorr,
+ int efpt_ind, real *dvdl,
+ int nthreads, f_thread_t *f_t)
+{
+ int t, i;
+
+ /* This reduction can run over any number of threads */
+#pragma omp parallel for num_threads(gmx_omp_nthreads_get(emntBonded)) private(t) schedule(static)
+ for (i = 0; i < n; i++)
+ {
+ for (t = 1; t < nthreads; t++)
+ {
+ rvec_inc(f[i], f_t[t].f[i]);
+ }
+ }
+ for (t = 1; t < nthreads; t++)
+ {
+ *Vcorr += f_t[t].Vcorr;
+ *dvdl += f_t[t].dvdl[efpt_ind];
+ m_add(vir, f_t[t].vir, vir);
+ }
+}
+
+void do_force_lowlevel(FILE *fplog, gmx_large_int_t step,
+ t_forcerec *fr, t_inputrec *ir,
+ t_idef *idef, t_commrec *cr,
+ t_nrnb *nrnb, gmx_wallcycle_t wcycle,
+ t_mdatoms *md,
+ t_grpopts *opts,
+ rvec x[], history_t *hist,
+ rvec f[],
+ rvec f_longrange[],
+ gmx_enerdata_t *enerd,
+ t_fcdata *fcd,
+ gmx_mtop_t *mtop,
+ gmx_localtop_t *top,
+ gmx_genborn_t *born,
+ t_atomtypes *atype,
+ gmx_bool bBornRadii,
+ matrix box,
+ t_lambda *fepvals,
+ real *lambda,
+ t_graph *graph,
+ t_blocka *excl,
+ rvec mu_tot[],
+ int flags,
+ float *cycles_pme)
+{
+ int i, j, status;
+ int donb_flags;
+ gmx_bool bDoEpot, bSepDVDL, bSB;
+ int pme_flags;
+ matrix boxs;
+ rvec box_size;
+ real Vsr, Vlr, Vcorr = 0;
+ t_pbc pbc;
+ real dvdgb;
+ char buf[22];
+ double clam_i, vlam_i;
+ real dvdl_dum[efptNR], dvdl, dvdl_nb[efptNR], lam_i[efptNR];
+ real dvdlsum;
+
+#ifdef GMX_MPI
+ double t0 = 0.0, t1, t2, t3; /* time measurement for coarse load balancing */
+#endif
+
+#define PRINT_SEPDVDL(s, v, dvdlambda) if (bSepDVDL) {fprintf(fplog, sepdvdlformat, s, v, dvdlambda); }
+
+
+ set_pbc(&pbc, fr->ePBC, box);
+
+ /* reset free energy components */
+ for (i = 0; i < efptNR; i++)
+ {
+ dvdl_nb[i] = 0;
+ dvdl_dum[i] = 0;
+ }
+
+ /* Reset box */
+ for (i = 0; (i < DIM); i++)
+ {
+ box_size[i] = box[i][i];
+ }
+
+ bSepDVDL = (fr->bSepDVDL && do_per_step(step, ir->nstlog));
+ debug_gmx();
+
+ /* do QMMM first if requested */
+ if (fr->bQMMM)
+ {
+ enerd->term[F_EQM] = calculate_QMMM(cr, x, f, fr, md);
+ }
+
+ if (bSepDVDL)
+ {
+ fprintf(fplog, "Step %s: non-bonded V and dVdl for node %d:\n",
+ gmx_step_str(step, buf), cr->nodeid);
+ }
+
+ /* Call the short range functions all in one go. */
+
+#ifdef GMX_MPI
+ /*#define TAKETIME ((cr->npmenodes) && (fr->timesteps < 12))*/
+#define TAKETIME FALSE
+ if (TAKETIME)
+ {
+ MPI_Barrier(cr->mpi_comm_mygroup);
+ t0 = MPI_Wtime();
+ }
+#endif
+
+ if (ir->nwall)
+ {
+ /* foreign lambda component for walls */
+ dvdl = do_walls(ir, fr, box, md, x, f, lambda[efptVDW],
+ enerd->grpp.ener[egLJSR], nrnb);
+ PRINT_SEPDVDL("Walls", 0.0, dvdl);
+ enerd->dvdl_lin[efptVDW] += dvdl;
+ }
+
+ /* If doing GB, reset dvda and calculate the Born radii */
+ if (ir->implicit_solvent)
+ {
+ wallcycle_sub_start(wcycle, ewcsNONBONDED);
+
+ for (i = 0; i < born->nr; i++)
+ {
+ fr->dvda[i] = 0;
+ }
+
+ if (bBornRadii)
+ {
+ calc_gb_rad(cr, fr, ir, top, atype, x, &(fr->gblist), born, md, nrnb);
+ }
+
+ wallcycle_sub_stop(wcycle, ewcsNONBONDED);
+ }
+
+ where();
+ /* We only do non-bonded calculation with group scheme here, the verlet
+ * calls are done from do_force_cutsVERLET(). */
+ if (fr->cutoff_scheme == ecutsGROUP && (flags & GMX_FORCE_NONBONDED))
+ {
+ donb_flags = 0;
+ /* Add short-range interactions */
+ donb_flags |= GMX_NONBONDED_DO_SR;
+
+ if (flags & GMX_FORCE_FORCES)
+ {
+ donb_flags |= GMX_NONBONDED_DO_FORCE;
+ }
+ if (flags & GMX_FORCE_ENERGY)
+ {
+ donb_flags |= GMX_NONBONDED_DO_POTENTIAL;
+ }
+ if (flags & GMX_FORCE_DO_LR)
+ {
+ donb_flags |= GMX_NONBONDED_DO_LR;
+ }
+
+ wallcycle_sub_start(wcycle, ewcsNONBONDED);
+ do_nonbonded(cr, fr, x, f, f_longrange, md, excl,
+ &enerd->grpp, box_size, nrnb,
+ lambda, dvdl_nb, -1, -1, donb_flags);
+
+ /* If we do foreign lambda and we have soft-core interactions
+ * we have to recalculate the (non-linear) energies contributions.
+ */
+ if (fepvals->n_lambda > 0 && (flags & GMX_FORCE_DHDL) && fepvals->sc_alpha != 0)
+ {
+ for (i = 0; i < enerd->n_lambda; i++)
+ {
+ for (j = 0; j < efptNR; j++)
+ {
+ lam_i[j] = (i == 0 ? lambda[j] : fepvals->all_lambda[j][i-1]);
+ }
+ reset_foreign_enerdata(enerd);
+ do_nonbonded(cr, fr, x, f, f_longrange, md, excl,
+ &(enerd->foreign_grpp), box_size, nrnb,
+ lam_i, dvdl_dum, -1, -1,
+ (donb_flags & ~GMX_NONBONDED_DO_FORCE) | GMX_NONBONDED_DO_FOREIGNLAMBDA);
+ sum_epot(&ir->opts, &(enerd->foreign_grpp), enerd->foreign_term);
+ enerd->enerpart_lambda[i] += enerd->foreign_term[F_EPOT];
+ }
+ }
+ wallcycle_sub_stop(wcycle, ewcsNONBONDED);
+ where();
+ }
+
+ /* If we are doing GB, calculate bonded forces and apply corrections
+ * to the solvation forces */
+ /* MRS: Eventually, many need to include free energy contribution here! */
+ if (ir->implicit_solvent)
+ {
+ wallcycle_sub_start(wcycle, ewcsBONDED);
+ calc_gb_forces(cr, md, born, top, atype, x, f, fr, idef,
+ ir->gb_algorithm, ir->sa_algorithm, nrnb, bBornRadii, &pbc, graph, enerd);
+ wallcycle_sub_stop(wcycle, ewcsBONDED);
+ }
+
+#ifdef GMX_MPI
+ if (TAKETIME)
+ {
+ t1 = MPI_Wtime();
+ fr->t_fnbf += t1-t0;
+ }
+#endif
+
+ if (fepvals->sc_alpha != 0)
+ {
+ enerd->dvdl_nonlin[efptVDW] += dvdl_nb[efptVDW];
+ }
+ else
+ {
+ enerd->dvdl_lin[efptVDW] += dvdl_nb[efptVDW];
+ }
+
+ if (fepvals->sc_alpha != 0)
+
+ /* even though coulomb part is linear, we already added it, beacuse we
+ need to go through the vdw calculation anyway */
+ {
+ enerd->dvdl_nonlin[efptCOUL] += dvdl_nb[efptCOUL];
+ }
+ else
+ {
+ enerd->dvdl_lin[efptCOUL] += dvdl_nb[efptCOUL];
+ }
+
+ Vsr = 0;
+ if (bSepDVDL)
+ {
+ for (i = 0; i < enerd->grpp.nener; i++)
+ {
+ Vsr +=
+ (fr->bBHAM ?
+ enerd->grpp.ener[egBHAMSR][i] :
+ enerd->grpp.ener[egLJSR][i])
+ + enerd->grpp.ener[egCOULSR][i] + enerd->grpp.ener[egGB][i];
+ }
+ dvdlsum = dvdl_nb[efptVDW] + dvdl_nb[efptCOUL];
+ PRINT_SEPDVDL("VdW and Coulomb SR particle-p.", Vsr, dvdlsum);
+ }
+ debug_gmx();
+
+
+ if (debug)
+ {
+ pr_rvecs(debug, 0, "fshift after SR", fr->fshift, SHIFTS);
+ }
+
+ /* Shift the coordinates. Must be done before bonded forces and PPPM,
+ * but is also necessary for SHAKE and update, therefore it can NOT
+ * go when no bonded forces have to be evaluated.
+ */
+
+ /* Here sometimes we would not need to shift with NBFonly,
+ * but we do so anyhow for consistency of the returned coordinates.
+ */
+ if (graph)
+ {
+ shift_self(graph, box, x);
+ if (TRICLINIC(box))
+ {
+ inc_nrnb(nrnb, eNR_SHIFTX, 2*graph->nnodes);
+ }
+ else
+ {
+ inc_nrnb(nrnb, eNR_SHIFTX, graph->nnodes);
+ }
+ }
+ /* Check whether we need to do bondeds or correct for exclusions */
+ if (fr->bMolPBC &&
+ ((flags & GMX_FORCE_BONDED)
+ || EEL_RF(fr->eeltype) || EEL_FULL(fr->eeltype)))
+ {
+ /* Since all atoms are in the rectangular or triclinic unit-cell,
+ * only single box vector shifts (2 in x) are required.
+ */
+ set_pbc_dd(&pbc, fr->ePBC, cr->dd, TRUE, box);
+ }
+ debug_gmx();
+
+ if (flags & GMX_FORCE_BONDED)
+ {
+ wallcycle_sub_start(wcycle, ewcsBONDED);
+ calc_bonds(fplog, cr->ms,
+ idef, x, hist, f, fr, &pbc, graph, enerd, nrnb, lambda, md, fcd,
+ DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL, atype, born,
+ flags,
+ fr->bSepDVDL && do_per_step(step, ir->nstlog), step);
+
+ /* Check if we have to determine energy differences
+ * at foreign lambda's.
+ */
+ if (fepvals->n_lambda > 0 && (flags & GMX_FORCE_DHDL) &&
+ idef->ilsort != ilsortNO_FE)
+ {
+ if (idef->ilsort != ilsortFE_SORTED)
+ {
+ gmx_incons("The bonded interactions are not sorted for free energy");
+ }
+ for (i = 0; i < enerd->n_lambda; i++)
+ {
+ reset_foreign_enerdata(enerd);
+ for (j = 0; j < efptNR; j++)
+ {
+ lam_i[j] = (i == 0 ? lambda[j] : fepvals->all_lambda[j][i-1]);
+ }
+ calc_bonds_lambda(fplog, idef, x, fr, &pbc, graph, &(enerd->foreign_grpp), enerd->foreign_term, nrnb, lam_i, md,
+ fcd, DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
+ sum_epot(&ir->opts, &(enerd->foreign_grpp), enerd->foreign_term);
+ enerd->enerpart_lambda[i] += enerd->foreign_term[F_EPOT];
+ }
+ }
+ debug_gmx();
+
+ wallcycle_sub_stop(wcycle, ewcsBONDED);
+ }
+
+ where();
+
+ *cycles_pme = 0;
+ if (EEL_FULL(fr->eeltype))
+ {
+ bSB = (ir->nwall == 2);
+ if (bSB)
+ {
+ copy_mat(box, boxs);
+ svmul(ir->wall_ewald_zfac, boxs[ZZ], boxs[ZZ]);
+ box_size[ZZ] *= ir->wall_ewald_zfac;
+ }
+
+ clear_mat(fr->vir_el_recip);
+
+ if (fr->bEwald)
+ {
+ Vcorr = 0;
+ dvdl = 0;
+
+ /* With the Verlet scheme exclusion forces are calculated
+ * in the non-bonded kernel.
+ */
+ /* The TPI molecule does not have exclusions with the rest
+ * of the system and no intra-molecular PME grid contributions
+ * will be calculated in gmx_pme_calc_energy.
+ */
+ if ((ir->cutoff_scheme == ecutsGROUP && fr->n_tpi == 0) ||
+ ir->ewald_geometry != eewg3D ||
+ ir->epsilon_surface != 0)
+ {
+ int nthreads, t;
+
+ wallcycle_sub_start(wcycle, ewcsEWALD_CORRECTION);
+
+ if (fr->n_tpi > 0)
+ {
+ gmx_fatal(FARGS, "TPI with PME currently only works in a 3D geometry with tin-foil boundary conditions");
+ }
+
+ nthreads = gmx_omp_nthreads_get(emntBonded);
+#pragma omp parallel for num_threads(nthreads) schedule(static)
+ for (t = 0; t < nthreads; t++)
+ {
+ int s, e, i;
+ rvec *fnv;
+ tensor *vir;
+ real *Vcorrt, *dvdlt;
+ if (t == 0)
+ {
+ fnv = fr->f_novirsum;
+ vir = &fr->vir_el_recip;
+ Vcorrt = &Vcorr;
+ dvdlt = &dvdl;
+ }
+ else
+ {
+ fnv = fr->f_t[t].f;
+ vir = &fr->f_t[t].vir;
+ Vcorrt = &fr->f_t[t].Vcorr;
+ dvdlt = &fr->f_t[t].dvdl[efptCOUL];
+ for (i = 0; i < fr->natoms_force; i++)
+ {
+ clear_rvec(fnv[i]);
+ }
+ clear_mat(*vir);
+ }
+ *dvdlt = 0;
+ *Vcorrt =
+ ewald_LRcorrection(fplog,
+ fr->excl_load[t], fr->excl_load[t+1],
+ cr, t, fr,
+ md->chargeA,
+ md->nChargePerturbed ? md->chargeB : NULL,
+ ir->cutoff_scheme != ecutsVERLET,
+ excl, x, bSB ? boxs : box, mu_tot,
+ ir->ewald_geometry,
+ ir->epsilon_surface,
+ fnv, *vir,
+ lambda[efptCOUL], dvdlt);
+ }
+ if (nthreads > 1)
+ {
+ reduce_thread_forces(fr->natoms_force, fr->f_novirsum,
+ fr->vir_el_recip,
+ &Vcorr, efptCOUL, &dvdl,
+ nthreads, fr->f_t);
+ }
+
+ wallcycle_sub_stop(wcycle, ewcsEWALD_CORRECTION);
+ }
+
+ if (fr->n_tpi == 0)
+ {
+ Vcorr += ewald_charge_correction(cr, fr, lambda[efptCOUL], box,
+ &dvdl, fr->vir_el_recip);
+ }
+
+ PRINT_SEPDVDL("Ewald excl./charge/dip. corr.", Vcorr, dvdl);
+ enerd->dvdl_lin[efptCOUL] += dvdl;
+ }
+
+ status = 0;
+ Vlr = 0;
+ dvdl = 0;
+ switch (fr->eeltype)
+ {
+ case eelPME:
+ case eelPMESWITCH:
+ case eelPMEUSER:
+ case eelPMEUSERSWITCH:
+ case eelP3M_AD:
+ if (cr->duty & DUTY_PME)
+ {
+ assert(fr->n_tpi >= 0);
+ if (fr->n_tpi == 0 || (flags & GMX_FORCE_STATECHANGED))
+ {
+ pme_flags = GMX_PME_SPREAD_Q | GMX_PME_SOLVE;
+ if (flags & GMX_FORCE_FORCES)
+ {
+ pme_flags |= GMX_PME_CALC_F;
+ }
+ if (flags & (GMX_FORCE_VIRIAL | GMX_FORCE_ENERGY))
+ {
+ pme_flags |= GMX_PME_CALC_ENER_VIR;
+ }
+ if (fr->n_tpi > 0)
+ {
+ /* We don't calculate f, but we do want the potential */
+ pme_flags |= GMX_PME_CALC_POT;
+ }
+ wallcycle_start(wcycle, ewcPMEMESH);
+ status = gmx_pme_do(fr->pmedata,
+ md->start, md->homenr - fr->n_tpi,
+ x, fr->f_novirsum,
+ md->chargeA, md->chargeB,
+ bSB ? boxs : box, cr,
+ DOMAINDECOMP(cr) ? dd_pme_maxshift_x(cr->dd) : 0,
+ DOMAINDECOMP(cr) ? dd_pme_maxshift_y(cr->dd) : 0,
+ nrnb, wcycle,
+ fr->vir_el_recip, fr->ewaldcoeff,
+ &Vlr, lambda[efptCOUL], &dvdl,
+ pme_flags);
+ *cycles_pme = wallcycle_stop(wcycle, ewcPMEMESH);
+
+ /* We should try to do as little computation after
+ * this as possible, because parallel PME synchronizes
+ * the nodes, so we want all load imbalance of the rest
+ * of the force calculation to be before the PME call.
+ * DD load balancing is done on the whole time of
+ * the force call (without PME).
+ */
+ }
+ if (fr->n_tpi > 0)
+ {
+ /* Determine the PME grid energy of the test molecule
+ * with the PME grid potential of the other charges.
+ */
+ gmx_pme_calc_energy(fr->pmedata, fr->n_tpi,
+ x + md->homenr - fr->n_tpi,
+ md->chargeA + md->homenr - fr->n_tpi,
+ &Vlr);
+ }
+ PRINT_SEPDVDL("PME mesh", Vlr, dvdl);
+ }
+ break;
+ case eelEWALD:
+ Vlr = do_ewald(fplog, FALSE, ir, x, fr->f_novirsum,
+ md->chargeA, md->chargeB,
+ box_size, cr, md->homenr,
+ fr->vir_el_recip, fr->ewaldcoeff,
+ lambda[efptCOUL], &dvdl, fr->ewald_table);
+ PRINT_SEPDVDL("Ewald long-range", Vlr, dvdl);
+ break;
+ default:
+ gmx_fatal(FARGS, "No such electrostatics method implemented %s",
+ eel_names[fr->eeltype]);
+ }
+ if (status != 0)
+ {
+ gmx_fatal(FARGS, "Error %d in long range electrostatics routine %s",
+ status, EELTYPE(fr->eeltype));
+ }
+ /* Note that with separate PME nodes we get the real energies later */
+ enerd->dvdl_lin[efptCOUL] += dvdl;
+ enerd->term[F_COUL_RECIP] = Vlr + Vcorr;
+ if (debug)
+ {
+ fprintf(debug, "Vlr = %g, Vcorr = %g, Vlr_corr = %g\n",
+ Vlr, Vcorr, enerd->term[F_COUL_RECIP]);
+ pr_rvecs(debug, 0, "vir_el_recip after corr", fr->vir_el_recip, DIM);
+ pr_rvecs(debug, 0, "fshift after LR Corrections", fr->fshift, SHIFTS);
+ }
+ }
+ else
+ {
+ if (EEL_RF(fr->eeltype))
+ {
+ /* With the Verlet scheme exclusion forces are calculated
+ * in the non-bonded kernel.
+ */
+ if (ir->cutoff_scheme != ecutsVERLET && fr->eeltype != eelRF_NEC)
+ {
+ dvdl = 0;
+ enerd->term[F_RF_EXCL] =
+ RF_excl_correction(fplog, fr, graph, md, excl, x, f,
+ fr->fshift, &pbc, lambda[efptCOUL], &dvdl);
+ }
+
+ enerd->dvdl_lin[efptCOUL] += dvdl;
+ PRINT_SEPDVDL("RF exclusion correction",
+ enerd->term[F_RF_EXCL], dvdl);
+ }
+ }
+ where();
+ debug_gmx();
+
+ if (debug)
+ {
+ print_nrnb(debug, nrnb);
+ }
+ debug_gmx();
+
+#ifdef GMX_MPI
+ if (TAKETIME)
+ {
+ t2 = MPI_Wtime();
+ MPI_Barrier(cr->mpi_comm_mygroup);
+ t3 = MPI_Wtime();
+ fr->t_wait += t3-t2;
+ if (fr->timesteps == 11)
+ {
+ fprintf(stderr, "* PP load balancing info: node %d, step %s, rel wait time=%3.0f%% , load string value: %7.2f\n",
+ cr->nodeid, gmx_step_str(fr->timesteps, buf),
+ 100*fr->t_wait/(fr->t_wait+fr->t_fnbf),
+ (fr->t_fnbf+fr->t_wait)/fr->t_fnbf);
+ }
+ fr->timesteps++;
+ }
+#endif
+
+ if (debug)
+ {
+ pr_rvecs(debug, 0, "fshift after bondeds", fr->fshift, SHIFTS);
+ }
+
+}
+
+void init_enerdata(int ngener, int n_lambda, gmx_enerdata_t *enerd)
+{
+ int i, n2;
+
+ for (i = 0; i < F_NRE; i++)
+ {
+ enerd->term[i] = 0;
+ enerd->foreign_term[i] = 0;
+ }
+
+
+ for (i = 0; i < efptNR; i++)
+ {
+ enerd->dvdl_lin[i] = 0;
+ enerd->dvdl_nonlin[i] = 0;
+ }
+
+ n2 = ngener*ngener;
+ if (debug)
+ {
+ fprintf(debug, "Creating %d sized group matrix for energies\n", n2);
+ }
+ enerd->grpp.nener = n2;
+ enerd->foreign_grpp.nener = n2;
+ for (i = 0; (i < egNR); i++)
+ {
+ snew(enerd->grpp.ener[i], n2);
+ snew(enerd->foreign_grpp.ener[i], n2);
+ }
+
+ if (n_lambda)
+ {
+ enerd->n_lambda = 1 + n_lambda;
+ snew(enerd->enerpart_lambda, enerd->n_lambda);
+ }
+ else
+ {
+ enerd->n_lambda = 0;
+ }
+}
+
+void destroy_enerdata(gmx_enerdata_t *enerd)
+{
+ int i;
+
+ for (i = 0; (i < egNR); i++)
+ {
+ sfree(enerd->grpp.ener[i]);
+ }
+
+ for (i = 0; (i < egNR); i++)
+ {
+ sfree(enerd->foreign_grpp.ener[i]);
+ }
+
+ if (enerd->n_lambda)
+ {
+ sfree(enerd->enerpart_lambda);
+ }
+}
+
+static real sum_v(int n, real v[])
+{
+ real t;
+ int i;
+
+ t = 0.0;
+ for (i = 0; (i < n); i++)
+ {
+ t = t + v[i];
+ }
+
+ return t;
+}
+
+void sum_epot(t_grpopts *opts, gmx_grppairener_t *grpp, real *epot)
+{
+ int i;
+
+ /* Accumulate energies */
+ epot[F_COUL_SR] = sum_v(grpp->nener, grpp->ener[egCOULSR]);
+ epot[F_LJ] = sum_v(grpp->nener, grpp->ener[egLJSR]);
+ epot[F_LJ14] = sum_v(grpp->nener, grpp->ener[egLJ14]);
+ epot[F_COUL14] = sum_v(grpp->nener, grpp->ener[egCOUL14]);
+ epot[F_COUL_LR] = sum_v(grpp->nener, grpp->ener[egCOULLR]);
+ epot[F_LJ_LR] = sum_v(grpp->nener, grpp->ener[egLJLR]);
+ /* We have already added 1-2,1-3, and 1-4 terms to F_GBPOL */
+ epot[F_GBPOL] += sum_v(grpp->nener, grpp->ener[egGB]);
+
+/* lattice part of LR doesnt belong to any group
+ * and has been added earlier
+ */
+ epot[F_BHAM] = sum_v(grpp->nener, grpp->ener[egBHAMSR]);
+ epot[F_BHAM_LR] = sum_v(grpp->nener, grpp->ener[egBHAMLR]);
+
+ epot[F_EPOT] = 0;
+ for (i = 0; (i < F_EPOT); i++)
+ {
+ if (i != F_DISRESVIOL && i != F_ORIRESDEV)
+ {
+ epot[F_EPOT] += epot[i];
+ }
+ }
+}
+
+void sum_dhdl(gmx_enerdata_t *enerd, real *lambda, t_lambda *fepvals)
+{
+ int i, j, index;
+ double dlam;
+
+ enerd->dvdl_lin[efptVDW] += enerd->term[F_DVDL_VDW]; /* include dispersion correction */
+ enerd->term[F_DVDL] = 0.0;
+ for (i = 0; i < efptNR; i++)
+ {
+ if (fepvals->separate_dvdl[i])
+ {
+ /* could this be done more readably/compactly? */
+ switch (i)
+ {
+ case (efptMASS):
+ index = F_DKDL;
+ break;
+ case (efptCOUL):
+ index = F_DVDL_COUL;
+ break;
+ case (efptVDW):
+ index = F_DVDL_VDW;
+ break;
+ case (efptBONDED):
+ index = F_DVDL_BONDED;
+ break;
+ case (efptRESTRAINT):
+ index = F_DVDL_RESTRAINT;
+ break;
+ default:
+ index = F_DVDL;
+ break;
+ }
+ enerd->term[index] = enerd->dvdl_lin[i] + enerd->dvdl_nonlin[i];
+ if (debug)
+ {
+ fprintf(debug, "dvdl-%s[%2d]: %f: non-linear %f + linear %f\n",
+ efpt_names[i], i, enerd->term[index], enerd->dvdl_nonlin[i], enerd->dvdl_lin[i]);
+ }
+ }
+ else
+ {
+ enerd->term[F_DVDL] += enerd->dvdl_lin[i] + enerd->dvdl_nonlin[i];
+ if (debug)
+ {
+ fprintf(debug, "dvd-%sl[%2d]: %f: non-linear %f + linear %f\n",
+ efpt_names[0], i, enerd->term[F_DVDL], enerd->dvdl_nonlin[i], enerd->dvdl_lin[i]);
+ }
+ }
+ }
+
+ /* Notes on the foreign lambda free energy difference evaluation:
+ * Adding the potential and ekin terms that depend linearly on lambda
+ * as delta lam * dvdl to the energy differences is exact.
+ * For the constraints this is not exact, but we have no other option
+ * without literally changing the lengths and reevaluating the energies at each step.
+ * (try to remedy this post 4.6 - MRS)
+ * For the non-bonded LR term we assume that the soft-core (if present)
+ * no longer affects the energy beyond the short-range cut-off,
+ * which is a very good approximation (except for exotic settings).
+ * (investigate how to overcome this post 4.6 - MRS)
+ */
++ if (fepvals->separate_dvdl[efptBONDED])
++ {
++ enerd->term[F_DVDL_BONDED] += enerd->term[F_DVDL_CONSTR];
++ }
++ else
++ {
++ enerd->term[F_DVDL] += enerd->term[F_DVDL_CONSTR];
++ }
+ enerd->term[F_DVDL_CONSTR] = 0;
+
+ for (i = 0; i < fepvals->n_lambda; i++)
+ { /* note we are iterating over fepvals here!
+ For the current lam, dlam = 0 automatically,
+ so we don't need to add anything to the
+ enerd->enerpart_lambda[0] */
+
+ /* we don't need to worry about dvdl_lin contributions to dE at
+ current lambda, because the contributions to the current
+ lambda are automatically zeroed */
+
+ for (j = 0; j < efptNR; j++)
+ {
+ /* Note that this loop is over all dhdl components, not just the separated ones */
+ dlam = (fepvals->all_lambda[j][i]-lambda[j]);
+ enerd->enerpart_lambda[i+1] += dlam*enerd->dvdl_lin[j];
+ if (debug)
+ {
+ fprintf(debug, "enerdiff lam %g: (%15s), non-linear %f linear %f*%f\n",
+ fepvals->all_lambda[j][i], efpt_names[j],
+ (enerd->enerpart_lambda[i+1] - enerd->enerpart_lambda[0]),
+ dlam, enerd->dvdl_lin[j]);
+ }
+ }
+ }
+}
+
+
+void reset_foreign_enerdata(gmx_enerdata_t *enerd)
+{
+ int i, j;
+
+ /* First reset all foreign energy components. Foreign energies always called on
+ neighbor search steps */
+ for (i = 0; (i < egNR); i++)
+ {
+ for (j = 0; (j < enerd->grpp.nener); j++)
+ {
+ enerd->foreign_grpp.ener[i][j] = 0.0;
+ }
+ }
+
+ /* potential energy components */
+ for (i = 0; (i <= F_EPOT); i++)
+ {
+ enerd->foreign_term[i] = 0.0;
+ }
+}
+
+void reset_enerdata(t_grpopts *opts,
+ t_forcerec *fr, gmx_bool bNS,
+ gmx_enerdata_t *enerd,
+ gmx_bool bMaster)
+{
+ gmx_bool bKeepLR;
+ int i, j;
+
+ /* First reset all energy components, except for the long range terms
+ * on the master at non neighbor search steps, since the long range
+ * terms have already been summed at the last neighbor search step.
+ */
+ bKeepLR = (fr->bTwinRange && !bNS);
+ for (i = 0; (i < egNR); i++)
+ {
+ if (!(bKeepLR && bMaster && (i == egCOULLR || i == egLJLR)))
+ {
+ for (j = 0; (j < enerd->grpp.nener); j++)
+ {
+ enerd->grpp.ener[i][j] = 0.0;
+ }
+ }
+ }
+ for (i = 0; i < efptNR; i++)
+ {
+ enerd->dvdl_lin[i] = 0.0;
+ enerd->dvdl_nonlin[i] = 0.0;
+ }
+
+ /* Normal potential energy components */
+ for (i = 0; (i <= F_EPOT); i++)
+ {
+ enerd->term[i] = 0.0;
+ }
+ /* Initialize the dVdlambda term with the long range contribution */
+ /* Initialize the dvdl term with the long range contribution */
+ enerd->term[F_DVDL] = 0.0;
+ enerd->term[F_DVDL_COUL] = 0.0;
+ enerd->term[F_DVDL_VDW] = 0.0;
+ enerd->term[F_DVDL_BONDED] = 0.0;
+ enerd->term[F_DVDL_RESTRAINT] = 0.0;
+ enerd->term[F_DKDL] = 0.0;
+ if (enerd->n_lambda > 0)
+ {
+ for (i = 0; i < enerd->n_lambda; i++)
+ {
+ enerd->enerpart_lambda[i] = 0.0;
+ }
+ }
+ /* reset foreign energy data - separate function since we also call it elsewhere */
+ reset_foreign_enerdata(enerd);
+}
--- /dev/null
- nbnxn_cuda_init_const(fr->nbv->cu_nbv, ic, fr->nbv);
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+#include "sysstuff.h"
+#include "typedefs.h"
+#include "vec.h"
+#include "maths.h"
+#include "macros.h"
+#include "smalloc.h"
+#include "macros.h"
+#include "gmx_fatal.h"
+#include "gmx_fatal_collective.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 "partdec.h"
+#include "qmmm.h"
+#include "copyrite.h"
+#include "mtop_util.h"
+#include "nbnxn_search.h"
+#include "nbnxn_atomdata.h"
+#include "nbnxn_consts.h"
+#include "statutil.h"
+#include "gmx_omp_nthreads.h"
+#include "gmx_detect_hardware.h"
+
+#ifdef _MSC_VER
+/* MSVC definition for __cpuid() */
+#include <intrin.h>
+#endif
+
+#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;
+}
+
+/* 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];
+ int tmp_vdwtype[4];
+ 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 *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;
+
+ 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;
+ }
+ }
+
+ *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;
+ 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);
+
+ /* 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]);
+ }
+ /* 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)
+{
+ double qsum, q2sum, q;
+ int mb, nmol, i;
+ const t_atoms *atoms;
+
+ qsum = 0;
+ q2sum = 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;
+ }
+ }
+ fr->qsum[0] = qsum;
+ fr->q2sum[0] = q2sum;
+ if (fr->efep != efepNO)
+ {
+ qsum = 0;
+ q2sum = 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;
+ }
+ fr->qsum[1] = qsum;
+ fr->q2sum[1] = q2sum;
+ }
+ }
+ else
+ {
+ fr->qsum[1] = fr->qsum[0];
+ fr->q2sum[1] = fr->q2sum[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(FILE *log, 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;
+#if (defined SIZEOF_LONG_LONG_INT) && (SIZEOF_LONG_LONG_INT >= 8)
+ long long int npair, npair_ij, tmpi, tmpj;
+#else
+ double npair, npair_ij, tmpi, tmpj;
+#endif
+ double csix, ctwelve;
+ int ntp, *typecount;
+ gmx_bool bBHAM;
+ real *nbfp;
+
+ ntp = fr->ntype;
+ bBHAM = fr->bBHAM;
+ nbfp = fr->nbfp;
+
+ 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);
+ 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++)
+ {
+ if (q == 0)
+ {
+ tpi = atoms->atom[i].type;
+ }
+ else
+ {
+ tpi = atoms->atom[i].typeB;
+ }
+ typecount[tpi] += nmol;
+ }
+ }
+ 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 (fplog != NULL)
+ {
+ if (fr->eDispCorr == edispcAllEner ||
+ fr->eDispCorr == edispcAllEnerPres)
+ {
+ fprintf(fplog, "Long Range LJ corr.: <C6> %10.4e, <C12> %10.4e\n",
+ fr->avcsix[0], fr->avctwelve[0]);
+ }
+ else
+ {
+ fprintf(fplog, "Long Range LJ corr.: <C6> %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, const gmx_mtop_t *mtop,
+ 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 accelerated 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);
+ }
+ }
+ }
+}
+
+
+static void pick_nbnxn_kernel_cpu(FILE *fp,
+ const t_commrec *cr,
+ const gmx_cpuid_t cpuid_info,
+ const t_inputrec *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
+ /* We expect the 2xNN kernels to be faster in most cases */
+ *kernel_type = nbnxnk4xN_SIMD_2xNN;
+#endif
+
+#if defined GMX_NBNXN_SIMD_4XN && defined GMX_X86_AVX_256
+ if (EEL_RF(ir->coulombtype) || ir->coulombtype == eelCUT)
+ {
+ /* The raw pair rate of the 4x8 kernel is higher than 2x(4+4),
+ * 10% with HT, 50% without HT, but extra zeros interactions
+ * can compensate. As we currently don't detect the actual use
+ * of HT, switch to 4x8 to avoid a potential performance hit.
+ */
+ *kernel_type = nbnxnk4xN_SIMD_4xN;
+ }
+#endif
+ 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
+ * x86 SIMD kernel. This is faster in single precision
+ * on Bulldozer and slightly faster on Sandy Bridge.
+ */
+#if (defined GMX_X86_AVX_128_FMA || defined GMX_X86_AVX_256) && !defined GMX_DOUBLE
+ *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_X86_SSE2 */
+}
+
+
+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;
+#ifndef GMX_NBNXN_SIMD
+ case nbnxnk4xN_SIMD_4xN: returnvalue = "not available"; break;
+ case nbnxnk4xN_SIMD_2xNN: returnvalue = "not available"; break;
+#else
+#ifdef GMX_X86_SSE2
+#if GMX_NBNXN_SIMD_BITWIDTH == 128
+ /* x86 SIMD intrinsics can be converted to either SSE or AVX depending
+ * on compiler flags. As we use nearly identical intrinsics, using an AVX
+ * compiler flag without an AVX macro effectively results in AVX kernels.
+ * For gcc we check for __AVX__
+ * At least a check for icc should be added (if there is a macro)
+ */
+#if !(defined GMX_X86_AVX_128_FMA || defined __AVX__)
+#ifndef GMX_X86_SSE4_1
+ case nbnxnk4xN_SIMD_4xN: returnvalue = "SSE2"; break;
+ case nbnxnk4xN_SIMD_2xNN: returnvalue = "SSE2"; break;
+#else
+ case nbnxnk4xN_SIMD_4xN: returnvalue = "SSE4.1"; break;
+ case nbnxnk4xN_SIMD_2xNN: returnvalue = "SSE4.1"; break;
+#endif
+#else
+ case nbnxnk4xN_SIMD_4xN: returnvalue = "AVX-128"; break;
+ case nbnxnk4xN_SIMD_2xNN: returnvalue = "AVX-128"; break;
+#endif
+#endif
+#if GMX_NBNXN_SIMD_BITWIDTH == 256
+ case nbnxnk4xN_SIMD_4xN: returnvalue = "AVX-256"; break;
+ case nbnxnk4xN_SIMD_2xNN: returnvalue = "AVX-256"; break;
+#endif
+#else /* not GMX_X86_SSE2 */
+ case nbnxnk4xN_SIMD_4xN: returnvalue = "SIMD"; break;
+ case nbnxnk4xN_SIMD_2xNN: returnvalue = "SIMD"; break;
+#endif
+#endif
+ 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,
+ const gmx_hw_info_t *hwinfo,
+ gmx_bool use_cpu_acceleration,
+ 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)
+ {
+ if (use_cpu_acceleration)
+ {
+ pick_nbnxn_kernel_cpu(fp, cr, hwinfo->cpuid_info, 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(FILE *fp,
+ const t_commrec *cr,
+ const gmx_hw_info_t *hwinfo,
+ gmx_bool bDoNonbonded,
+ gmx_bool *bUseGPU,
+ gmx_bool *bEmulateGPU)
+{
+ 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 && hwinfo->bCanUseGPU));
+
+ /* Enable GPU mode when GPUs are available or no GPU emulation is requested.
+ */
+ if (hwinfo->bCanUseGPU && !(*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))
+ {
+ /* 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, 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,
+ 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);
+
+ /* 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);
+}
+
+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))
+ {
+ 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);
+ }
+ }
+}
+
+void init_interaction_const(FILE *fp,
+ 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->rvdw = fr->rvdw;
+ if (fr->vdw_modifier == eintmodPOTSHIFT)
+ {
+ ic->sh_invrc6 = pow(ic->rvdw, -6.0);
+ }
+ else
+ {
+ ic->sh_invrc6 = 0;
+ }
+
+ /* Electrostatics */
+ ic->eeltype = fr->eeltype;
+ ic->rcoulomb = fr->rcoulomb;
+ ic->epsilon_r = fr->epsilon_r;
+ ic->epsfac = fr->epsfac;
+
+ /* Ewald */
+ ic->ewaldcoeff = fr->ewaldcoeff;
+ if (fr->coulomb_modifier == eintmodPOTSHIFT)
+ {
+ ic->sh_ewald = gmx_erfc(ic->ewaldcoeff*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)
+ {
+ fprintf(fp, "Potential shift: LJ r^-12: %.3f r^-6 %.3f",
+ sqr(ic->sh_invrc6), ic->sh_invrc6);
+ if (ic->eeltype == eelCUT)
+ {
+ fprintf(fp, ", Coulomb %.3f", 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);
+ }
+
+ 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,
+ 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(fp, cr, fr->hwinfo,
+ fr->bNonbonded,
+ &nbv->bUseGPU,
+ &bEmulateGPU);
+
+ 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->hwinfo, fr->use_cpu_acceleration,
+ 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->hwinfo, fr->use_cpu_acceleration,
+ 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, 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,
+ 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)
+ {
+ snew(nbv->grp[i].nbat, 1);
+ nbnxn_atomdata_init(fp,
+ nbv->grp[i].nbat,
+ nbv->grp[i].kernel_type,
+ fr->ntype, fr->nbfp,
+ ir->opts.ngener,
+ nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type) ? 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,
+ gmx_bool bMolEpot,
+ 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;
+ rvec box_size;
+ const t_block *cgs;
+ gmx_bool bGenericKernelOnly;
+ gmx_bool bTab, bSep14tab, bNormalnblists;
+ 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.
+ */
+ snew(fr->hwinfo, 1);
+ gmx_detect_hardware(fp, fr->hwinfo, cr,
+ FALSE, FALSE, NULL);
+ }
+
+ /* By default we turn acceleration on, but it might be turned off further down... */
+ fr->use_cpu_acceleration = 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_CPU_ACCELERATION") != NULL) || (getenv("GMX_NOOPTIMIZEDKERNELS") != NULL) )
+ {
+ fr->use_cpu_acceleration = FALSE;
+ if (fp != NULL)
+ {
+ fprintf(fp,
+ "\nFound environment variable GMX_DISABLE_CPU_ACCELERATION.\n"
+ "Disabling all CPU architecture-specific (e.g. SSE2/SSE4/AVX) routines.\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, mtop, 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-accelerated
++ * group kernels are OK. See Redmine #1249. */
++ if (fr->bAllvsAll)
++ {
++ fr->bAllvsAll = FALSE;
++ fr->use_cpu_acceleration = 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;
+
+ /* 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->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 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));
+ /* 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.\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 = calc_ewaldcoeff(ir->rcoulomb, ir->ewald_rtol);
+ init_ewald_tab(&(fr->ewald_table), cr, ir, fp);
+ if (fp)
+ {
+ fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for Ewald\n",
+ 1/fr->ewaldcoeff);
+ }
+ }
+
+ /* 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);
+ }
+ else if (fr->eeltype == eelSHIFT)
+ {
+ for (m = 0; (m < DIM); m++)
+ {
+ box_size[m] = box[m][m];
+ }
+
+ if ((fr->eeltype == eelSHIFT && fr->rcoulomb > fr->rcoulomb_switch))
+ {
+ set_shift_consts(fp, fr->rcoulomb_switch, fr->rcoulomb, box_size, fr);
+ }
+ }
+
+ fr->bF_NoVirSum = (EEL_FULL(fr->eeltype) ||
+ 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);
+ }
+
+ /* 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 && (fr->vdwtype == evdwSHIFT || fr->vdwtype == evdwSWITCH))
+ {
+ gmx_fatal(FARGS, "Switch/shift interaction 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(fp, oenv, fr, tabpfn, fr->gbtabscale);
+
+ init_gb(&fr->born, cr, fr, ir, mtop, ir->rgbradii, 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);
+ }
+
+ 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... */
+
+ bTab = fr->bcoultab || fr->bvdwtab || fr->bEwald;
+
+ bSep14tab = ((!bTab || 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 (!bTab)
+ {
+ bNormalnblists = TRUE;
+ fr->nnblists = 1;
+ }
+ else
+ {
+ bNormalnblists = (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
+ {
+ bNormalnblists = TRUE;
+ }
+ }
+ }
+ }
+ if (bNormalnblists)
+ {
+ 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 (bTab)
+ {
+ /* make tables for ordinary interactions */
+ if (bNormalnblists)
+ {
+ 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 (!bSep14tab)
+ {
+ 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 (bSep14tab)
+ {
+ /* 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,
+ &fr->bExcl_IntraCGAll_InterCGNone);
+ if (DOMAINDECOMP(cr))
+ {
+ fr->cginfo = NULL;
+ }
+ else
+ {
+ fr->cginfo = cginfo_expand(mtop->nmolblock, fr->cginfo_mb);
+ }
+
+ if (!DOMAINDECOMP(cr))
+ {
+ /* When using particle decomposition, the effect of the second argument,
+ * which sets fr->hcg, is corrected later in do_md and init_em.
+ */
+ 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, box);
+
+ if (cr->duty & DUTY_PP)
+ {
+ gmx_nonbonded_setup(fp, 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, ir, fr, cr, nbpu_opt);
+ }
+
+ /* fr->ic is used both by verlet and group kernels (to some extent) now */
+ init_interaction_const(fp, &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, t_commrec *cr)
+{
+ 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 t_commrec *cr)
+{
+ const int *ind, *a;
+ int t, i, j, ntot, n, ntarget;
+
+ if (cr != NULL && PARTDECOMP(cr))
+ {
+ /* No OpenMP with particle decomposition */
+ pd_at_range(cr,
+ &fr->excl_load[0],
+ &fr->excl_load[1]);
+
+ return;
+ }
+
+ 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;
+ }
+}
--- /dev/null
- const nonbonded_verlet_t *nbv,
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2012, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <cuda.h>
+
+#include "gmx_fatal.h"
+#include "smalloc.h"
+#include "tables.h"
+#include "typedefs.h"
+#include "types/nb_verlet.h"
+#include "types/interaction_const.h"
+#include "types/force_flags.h"
+#include "../nbnxn_consts.h"
+
+#include "nbnxn_cuda_types.h"
+#include "../../gmxlib/cuda_tools/cudautils.cuh"
+#include "nbnxn_cuda_data_mgmt.h"
+#include "pmalloc_cuda.h"
+#include "gpu_utils.h"
+
+static bool bUseCudaEventBlockingSync = false; /* makes the CPU thread block */
+
+/* This is a heuristically determined parameter for the Fermi architecture for
+ * the minimum size of ci lists by multiplying this constant with the # of
+ * multiprocessors on the current device.
+ */
+static unsigned int gpu_min_ci_balanced_factor = 40;
+
+/* Functions from nbnxn_cuda.cu */
+extern void nbnxn_cuda_set_cacheconfig(cuda_dev_info_t *devinfo);
+extern const struct texture<float, 1, cudaReadModeElementType>& nbnxn_cuda_get_nbfp_texref();
+extern const struct texture<float, 1, cudaReadModeElementType>& nbnxn_cuda_get_coulomb_tab_texref();
+
+/* We should actually be using md_print_warn in md_logging.c,
+ * but we can't include mpi.h in CUDA code.
+ */
+static void md_print_warn(FILE *fplog,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fplog != NULL)
+ {
+ /* We should only print to stderr on the master node,
+ * in most cases fplog is only set on the master node, so this works.
+ */
+ va_start(ap, fmt);
+ fprintf(stderr, "\n");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+
+ va_start(ap, fmt);
+ fprintf(fplog, "\n");
+ vfprintf(fplog, fmt, ap);
+ fprintf(fplog, "\n");
+ va_end(ap);
+ }
+}
+
+
+/* Fw. decl. */
+static void nbnxn_cuda_clear_e_fshift(nbnxn_cuda_ptr_t cu_nb);
+
+
+/*! Tabulates the Ewald Coulomb force and initializes the size/scale
+ and the table GPU array. If called with an already allocated table,
+ it just re-uploads the table.
+ */
+static void init_ewald_coulomb_force_table(cu_nbparam_t *nbp)
+{
+ float *ftmp, *coul_tab;
+ int tabsize;
+ double tabscale;
+ cudaError_t stat;
+
+ tabsize = GPU_EWALD_COULOMB_FORCE_TABLE_SIZE;
+ /* Subtract 2 iso 1 to avoid access out of range due to rounding */
+ tabscale = (tabsize - 2) / sqrt(nbp->rcoulomb_sq);
+
+ pmalloc((void**)&ftmp, tabsize*sizeof(*ftmp));
+
+ table_spline3_fill_ewald_lr(ftmp, NULL, NULL, tabsize,
+ 1/tabscale, nbp->ewald_beta);
+
+ /* If the table pointer == NULL the table is generated the first time =>
+ the array pointer will be saved to nbparam and the texture is bound.
+ */
+ coul_tab = nbp->coulomb_tab;
+ if (coul_tab == NULL)
+ {
+ stat = cudaMalloc((void **)&coul_tab, tabsize*sizeof(*coul_tab));
+ CU_RET_ERR(stat, "cudaMalloc failed on coul_tab");
+
+ nbp->coulomb_tab = coul_tab;
+
+ cudaChannelFormatDesc cd = cudaCreateChannelDesc<float>();
+ stat = cudaBindTexture(NULL, &nbnxn_cuda_get_coulomb_tab_texref(),
+ coul_tab, &cd, tabsize*sizeof(*coul_tab));
+ CU_RET_ERR(stat, "cudaBindTexture on coul_tab failed");
+ }
+
+ cu_copy_H2D(coul_tab, ftmp, tabsize*sizeof(*coul_tab));
+
+ nbp->coulomb_tab_size = tabsize;
+ nbp->coulomb_tab_scale = tabscale;
+
+ pfree(ftmp);
+}
+
+
+/*! Initializes the atomdata structure first time, it only gets filled at
+ pair-search. */
+static void init_atomdata_first(cu_atomdata_t *ad, int ntypes)
+{
+ cudaError_t stat;
+
+ ad->ntypes = ntypes;
+ stat = cudaMalloc((void**)&ad->shift_vec, SHIFTS*sizeof(*ad->shift_vec));
+ CU_RET_ERR(stat, "cudaMalloc failed on ad->shift_vec");
+ ad->bShiftVecUploaded = false;
+
+ stat = cudaMalloc((void**)&ad->fshift, SHIFTS*sizeof(*ad->fshift));
+ CU_RET_ERR(stat, "cudaMalloc failed on ad->fshift");
+
+ stat = cudaMalloc((void**)&ad->e_lj, sizeof(*ad->e_lj));
+ CU_RET_ERR(stat, "cudaMalloc failed on ad->e_lj");
+ stat = cudaMalloc((void**)&ad->e_el, sizeof(*ad->e_el));
+ CU_RET_ERR(stat, "cudaMalloc failed on ad->e_el");
+
+ /* initialize to NULL poiters to data that is not allocated here and will
+ need reallocation in nbnxn_cuda_init_atomdata */
+ ad->xq = NULL;
+ ad->f = NULL;
+
+ /* size -1 indicates that the respective array hasn't been initialized yet */
+ ad->natoms = -1;
+ ad->nalloc = -1;
+}
+
+/*! Selects the Ewald kernel type, analytical on SM 3.0 and later, tabulated on
+ earlier GPUs, single or twin cut-off. */
+static int pick_ewald_kernel_type(bool bTwinCut,
+ const cuda_dev_info_t *dev_info)
+{
+ bool bUseAnalyticalEwald, bForceAnalyticalEwald, bForceTabulatedEwald;
+ int kernel_type;
+
+ /* Benchmarking/development environment variables to force the use of
+ analytical or tabulated Ewald kernel. */
+ bForceAnalyticalEwald = (getenv("GMX_CUDA_NB_ANA_EWALD") != NULL);
+ bForceTabulatedEwald = (getenv("GMX_CUDA_NB_TAB_EWALD") != NULL);
+
+ if (bForceAnalyticalEwald && bForceTabulatedEwald)
+ {
+ gmx_incons("Both analytical and tabulated Ewald CUDA non-bonded kernels "
+ "requested through environment variables.");
+ }
+
+ /* By default, on SM 3.0 and later use analytical Ewald, on earlier tabulated. */
+ if ((dev_info->prop.major >= 3 || bForceAnalyticalEwald) && !bForceTabulatedEwald)
+ {
+ bUseAnalyticalEwald = true;
+
+ if (debug)
+ {
+ fprintf(debug, "Using analytical Ewald CUDA kernels\n");
+ }
+ }
+ else
+ {
+ bUseAnalyticalEwald = false;
+
+ if (debug)
+ {
+ fprintf(debug, "Using tabulated Ewald CUDA kernels\n");
+ }
+ }
+
+ /* Use twin cut-off kernels if requested by bTwinCut or the env. var.
+ forces it (use it for debugging/benchmarking only). */
+ if (!bTwinCut && (getenv("GMX_CUDA_NB_EWALD_TWINCUT") == NULL))
+ {
+ kernel_type = bUseAnalyticalEwald ? eelCuEWALD_ANA : eelCuEWALD_TAB;
+ }
+ else
+ {
+ kernel_type = bUseAnalyticalEwald ? eelCuEWALD_ANA_TWIN : eelCuEWALD_TAB_TWIN;
+ }
+
+ return kernel_type;
+}
+
+
+/*! Initializes the nonbonded parameter data structure. */
+static void init_nbparam(cu_nbparam_t *nbp,
+ const interaction_const_t *ic,
- ntypes = nbv->grp[0].nbat->ntype;
++ const nbnxn_atomdata_t *nbat,
+ const cuda_dev_info_t *dev_info)
+{
+ cudaError_t stat;
+ int ntypes, nnbfp;
+
- cu_copy_H2D(nbp->nbfp, nbv->grp[0].nbat->nbfp, nnbfp*sizeof(*nbp->nbfp));
++ ntypes = nbat->ntype;
+
+ nbp->ewald_beta = ic->ewaldcoeff;
+ nbp->sh_ewald = ic->sh_ewald;
+ nbp->epsfac = ic->epsfac;
+ nbp->two_k_rf = 2.0 * ic->k_rf;
+ nbp->c_rf = ic->c_rf;
+ nbp->rvdw_sq = ic->rvdw * ic->rvdw;
+ nbp->rcoulomb_sq= ic->rcoulomb * ic->rcoulomb;
+ nbp->rlist_sq = ic->rlist * ic->rlist;
+ nbp->sh_invrc6 = ic->sh_invrc6;
+
+ if (ic->eeltype == eelCUT)
+ {
+ nbp->eeltype = eelCuCUT;
+ }
+ else if (EEL_RF(ic->eeltype))
+ {
+ nbp->eeltype = eelCuRF;
+ }
+ else if ((EEL_PME(ic->eeltype) || ic->eeltype==eelEWALD))
+ {
+ /* Initially rcoulomb == rvdw, so it's surely not twin cut-off. */
+ nbp->eeltype = pick_ewald_kernel_type(false, dev_info);
+ }
+ else
+ {
+ /* Shouldn't happen, as this is checked when choosing Verlet-scheme */
+ gmx_incons("The requested electrostatics type is not implemented in the CUDA GPU accelerated kernels!");
+ }
+
+ /* generate table for PME */
+ nbp->coulomb_tab = NULL;
+ if (nbp->eeltype == eelCuEWALD_TAB || nbp->eeltype == eelCuEWALD_TAB_TWIN)
+ {
+ init_ewald_coulomb_force_table(nbp);
+ }
+
+ nnbfp = 2*ntypes*ntypes;
+ stat = cudaMalloc((void **)&nbp->nbfp, nnbfp*sizeof(*nbp->nbfp));
+ CU_RET_ERR(stat, "cudaMalloc failed on nbp->nbfp");
- if (nb->dev_info->prop.ECCEnabled == 1)
++ cu_copy_H2D(nbp->nbfp, nbat->nbfp, nnbfp*sizeof(*nbp->nbfp));
+
+ cudaChannelFormatDesc cd = cudaCreateChannelDesc<float>();
+ stat = cudaBindTexture(NULL, &nbnxn_cuda_get_nbfp_texref(),
+ nbp->nbfp, &cd, nnbfp*sizeof(*nbp->nbfp));
+ CU_RET_ERR(stat, "cudaBindTexture on nbfp failed");
+}
+
+/*! Re-generate the GPU Ewald force table, resets rlist, and update the
+ * electrostatic type switching to twin cut-off (or back) if needed. */
+void nbnxn_cuda_pme_loadbal_update_param(nbnxn_cuda_ptr_t cu_nb,
+ const interaction_const_t *ic)
+{
+ cu_nbparam_t *nbp = cu_nb->nbparam;
+
+ nbp->rlist_sq = ic->rlist * ic->rlist;
+ nbp->rcoulomb_sq = ic->rcoulomb * ic->rcoulomb;
+ nbp->ewald_beta = ic->ewaldcoeff;
+
+ nbp->eeltype = pick_ewald_kernel_type(ic->rcoulomb != ic->rvdw,
+ cu_nb->dev_info);
+
+ init_ewald_coulomb_force_table(cu_nb->nbparam);
+}
+
+/*! Initializes the pair list data structure. */
+static void init_plist(cu_plist_t *pl)
+{
+ /* initialize to NULL pointers to data that is not allocated here and will
+ need reallocation in nbnxn_cuda_init_pairlist */
+ pl->sci = NULL;
+ pl->cj4 = NULL;
+ pl->excl = NULL;
+
+ /* size -1 indicates that the respective array hasn't been initialized yet */
+ pl->na_c = -1;
+ pl->nsci = -1;
+ pl->sci_nalloc = -1;
+ pl->ncj4 = -1;
+ pl->cj4_nalloc = -1;
+ pl->nexcl = -1;
+ pl->excl_nalloc = -1;
+ pl->bDoPrune = false;
+}
+
+/*! Initializes the timer data structure. */
+static void init_timers(cu_timers_t *t, bool bUseTwoStreams)
+{
+ cudaError_t stat;
+ int eventflags = ( bUseCudaEventBlockingSync ? cudaEventBlockingSync: cudaEventDefault );
+
+ stat = cudaEventCreateWithFlags(&(t->start_atdat), eventflags);
+ CU_RET_ERR(stat, "cudaEventCreate on start_atdat failed");
+ stat = cudaEventCreateWithFlags(&(t->stop_atdat), eventflags);
+ CU_RET_ERR(stat, "cudaEventCreate on stop_atdat failed");
+
+ /* The non-local counters/stream (second in the array) are needed only with DD. */
+ for (int i = 0; i <= (bUseTwoStreams ? 1 : 0); i++)
+ {
+ stat = cudaEventCreateWithFlags(&(t->start_nb_k[i]), eventflags);
+ CU_RET_ERR(stat, "cudaEventCreate on start_nb_k failed");
+ stat = cudaEventCreateWithFlags(&(t->stop_nb_k[i]), eventflags);
+ CU_RET_ERR(stat, "cudaEventCreate on stop_nb_k failed");
+
+
+ stat = cudaEventCreateWithFlags(&(t->start_pl_h2d[i]), eventflags);
+ CU_RET_ERR(stat, "cudaEventCreate on start_pl_h2d failed");
+ stat = cudaEventCreateWithFlags(&(t->stop_pl_h2d[i]), eventflags);
+ CU_RET_ERR(stat, "cudaEventCreate on stop_pl_h2d failed");
+
+ stat = cudaEventCreateWithFlags(&(t->start_nb_h2d[i]), eventflags);
+ CU_RET_ERR(stat, "cudaEventCreate on start_nb_h2d failed");
+ stat = cudaEventCreateWithFlags(&(t->stop_nb_h2d[i]), eventflags);
+ CU_RET_ERR(stat, "cudaEventCreate on stop_nb_h2d failed");
+
+ stat = cudaEventCreateWithFlags(&(t->start_nb_d2h[i]), eventflags);
+ CU_RET_ERR(stat, "cudaEventCreate on start_nb_d2h failed");
+ stat = cudaEventCreateWithFlags(&(t->stop_nb_d2h[i]), eventflags);
+ CU_RET_ERR(stat, "cudaEventCreate on stop_nb_d2h failed");
+ }
+}
+
+/*! Initializes the timings data structure. */
+static void init_timings(wallclock_gpu_t *t)
+{
+ int i, j;
+
+ t->nb_h2d_t = 0.0;
+ t->nb_d2h_t = 0.0;
+ t->nb_c = 0;
+ t->pl_h2d_t = 0.0;
+ t->pl_h2d_c = 0;
+ for (i = 0; i < 2; i++)
+ {
+ for(j = 0; j < 2; j++)
+ {
+ t->ktime[i][j].t = 0.0;
+ t->ktime[i][j].c = 0;
+ }
+ }
+}
+
+/* Decide which kernel version to use (default or legacy) based on:
+ * - CUDA version used for compilation
+ * - non-bonded kernel selector environment variables
+ * - GPU architecture version
+ */
+static int pick_nbnxn_kernel_version(FILE *fplog,
+ cuda_dev_info_t *devinfo)
+{
+ bool bForceLegacyKernel, bForceDefaultKernel, bCUDA40, bCUDA32;
+ char sbuf[STRLEN];
+ int kver;
+
+ /* Legacy kernel (former k2), kept for backward compatibility as it is
+ faster than the default with CUDA 3.2/4.0 on Fermi (not on Kepler). */
+ bForceLegacyKernel = (getenv("GMX_CUDA_NB_LEGACY") != NULL);
+ /* default kernel (former k3). */
+ bForceDefaultKernel = (getenv("GMX_CUDA_NB_DEFAULT") != NULL);
+
+ if ((unsigned)(bForceLegacyKernel + bForceDefaultKernel) > 1)
+ {
+ gmx_fatal(FARGS, "Multiple CUDA non-bonded kernels requested; to manually pick a kernel set only one \n"
+ "of the following environment variables: \n"
+ "GMX_CUDA_NB_DEFAULT, GMX_CUDA_NB_LEGACY");
+ }
+
+ bCUDA32 = bCUDA40 = false;
+#if CUDA_VERSION == 3200
+ bCUDA32 = true;
+ sprintf(sbuf, "3.2");
+#elif CUDA_VERSION == 4000
+ bCUDA40 = true;
+ sprintf(sbuf, "4.0");
+#endif
+
+ /* default is default ;) */
+ kver = eNbnxnCuKDefault;
+
+ /* Consider switching to legacy kernels only on Fermi */
+ if (devinfo->prop.major < 3 && (bCUDA32 || bCUDA40))
+ {
+ /* use legacy kernel unless something else is forced by an env. var */
+ if (bForceDefaultKernel)
+ {
+ md_print_warn(fplog,
+ "NOTE: CUDA %s compilation detected; with this compiler version the legacy\n"
+ " non-bonded kernels perform best. However, the default kernels were\n"
+ " selected by the GMX_CUDA_NB_DEFAULT environment variable.\n"
+ " For best performance upgrade your CUDA toolkit.\n",
+ sbuf);
+ }
+ else
+ {
+ kver = eNbnxnCuKLegacy;
+ }
+ }
+ else
+ {
+ /* issue note if the non-default kernel is forced by an env. var */
+ if (bForceLegacyKernel)
+ {
+ md_print_warn(fplog,
+ "NOTE: Legacy non-bonded CUDA kernels selected by the GMX_CUDA_NB_LEGACY\n"
+ " env. var. Consider using using the default kernels which should be faster!\n");
+
+ kver = eNbnxnCuKLegacy;
+ }
+ }
+
+ return kver;
+}
+
+void nbnxn_cuda_init(FILE *fplog,
+ nbnxn_cuda_ptr_t *p_cu_nb,
+ gmx_gpu_info_t *gpu_info, int my_gpu_index,
+ gmx_bool bLocalAndNonlocal)
+{
+ cudaError_t stat;
+ nbnxn_cuda_ptr_t nb;
+ char sbuf[STRLEN];
+ bool bStreamSync, bNoStreamSync, bTMPIAtomics, bX86, bOldDriver;
+ int cuda_drv_ver;
+
+ assert(gpu_info);
+
+ if (p_cu_nb == NULL) return;
+
+ snew(nb, 1);
+ snew(nb->atdat, 1);
+ snew(nb->nbparam, 1);
+ snew(nb->plist[eintLocal], 1);
+ if (bLocalAndNonlocal)
+ {
+ snew(nb->plist[eintNonlocal], 1);
+ }
+
+ nb->bUseTwoStreams = bLocalAndNonlocal;
+
+ snew(nb->timers, 1);
+ snew(nb->timings, 1);
+
+ /* init nbst */
+ pmalloc((void**)&nb->nbst.e_lj, sizeof(*nb->nbst.e_lj));
+ pmalloc((void**)&nb->nbst.e_el, sizeof(*nb->nbst.e_el));
+ pmalloc((void**)&nb->nbst.fshift, SHIFTS * sizeof(*nb->nbst.fshift));
+
+ init_plist(nb->plist[eintLocal]);
+
+ /* local/non-local GPU streams */
+ stat = cudaStreamCreate(&nb->stream[eintLocal]);
+ CU_RET_ERR(stat, "cudaStreamCreate on stream[eintLocal] failed");
+ if (nb->bUseTwoStreams)
+ {
+ init_plist(nb->plist[eintNonlocal]);
+ stat = cudaStreamCreate(&nb->stream[eintNonlocal]);
+ CU_RET_ERR(stat, "cudaStreamCreate on stream[eintNonlocal] failed");
+ }
+
+ /* init events for sychronization (timing disabled for performance reasons!) */
+ stat = cudaEventCreateWithFlags(&nb->nonlocal_done, cudaEventDisableTiming);
+ CU_RET_ERR(stat, "cudaEventCreate on nonlocal_done failed");
+ stat = cudaEventCreateWithFlags(&nb->misc_ops_done, cudaEventDisableTiming);
+ CU_RET_ERR(stat, "cudaEventCreate on misc_ops_one failed");
+
+ /* set device info, just point it to the right GPU among the detected ones */
+ nb->dev_info = &gpu_info->cuda_dev[get_gpu_device_id(gpu_info, my_gpu_index)];
+
+ /* On GPUs with ECC enabled, cudaStreamSynchronize shows a large overhead
+ * (which increases with shorter time/step) caused by a known CUDA driver bug.
+ * To work around the issue we'll use an (admittedly fragile) memory polling
+ * waiting to preserve performance. This requires support for atomic
+ * operations and only works on x86/x86_64.
+ * With polling wait event-timing also needs to be disabled.
+ *
+ * The overhead is greatly reduced in API v5.0 drivers and the improvement
+ $ is independent of runtime version. Hence, with API v5.0 drivers and later
+ * we won't switch to polling.
+ *
+ * NOTE: Unfortunately, this is known to fail when GPUs are shared by (t)MPI,
+ * ranks so we will also disable it in that case.
+ */
+
+ bStreamSync = getenv("GMX_CUDA_STREAMSYNC") != NULL;
+ bNoStreamSync = getenv("GMX_NO_CUDA_STREAMSYNC") != NULL;
+
+#ifdef TMPI_ATOMICS
+ bTMPIAtomics = true;
+#else
+ bTMPIAtomics = false;
+#endif
+
+#if defined(i386) || defined(__x86_64__)
+ bX86 = true;
+#else
+ bX86 = false;
+#endif
+
+ if (bStreamSync && bNoStreamSync)
+ {
+ gmx_fatal(FARGS, "Conflicting environment variables: both GMX_CUDA_STREAMSYNC and GMX_NO_CUDA_STREAMSYNC defined");
+ }
+
+ stat = cudaDriverGetVersion(&cuda_drv_ver);
+ CU_RET_ERR(stat, "cudaDriverGetVersion failed");
++
+ bOldDriver = (cuda_drv_ver < 5000);
+
- if (bOldDriver && !gpu_info->bDevShare)
++ if ((nb->dev_info->prop.ECCEnabled == 1) && bOldDriver)
+ {
++ /* Polling wait should be used instead of cudaStreamSynchronize only if:
++ * - ECC is ON & driver is old (checked above),
++ * - we're on x86/x86_64,
++ * - atomics are available, and
++ * - GPUs are not being shared.
++ */
++ bool bShouldUsePollSync = (bX86 && bTMPIAtomics && !gpu_info->bDevShare);
++
+ if (bStreamSync)
+ {
+ nb->bUseStreamSync = true;
+
+ /* only warn if polling should be used */
- /* Can/should turn of cudaStreamSynchronize wait only if
- * - we're on x86/x86_64
- * - atomics are available
- * - GPUs are not being shared
- * - and driver is old. */
- nb->bUseStreamSync =
- (bX86 && bTMPIAtomics && !gpu_info->bDevShare && bOldDriver) ?
- true : false;
-
- if (nb->bUseStreamSync)
++ if (bShouldUsePollSync)
+ {
+ md_print_warn(fplog,
+ "NOTE: Using a GPU with ECC enabled and CUDA driver API version <5.0, but\n"
+ " cudaStreamSynchronize waiting is forced by the GMX_CUDA_STREAMSYNC env. var.\n");
+ }
+ }
+ else
+ {
- " cause performance loss. Switching to the alternative polling GPU waiting.\n"
++ nb->bUseStreamSync = !bShouldUsePollSync;
++
++ if (bShouldUsePollSync)
+ {
+ md_print_warn(fplog,
+ "NOTE: Using a GPU with ECC enabled and CUDA driver API version <5.0, known to\n"
- else if (bOldDriver)
++ " cause performance loss. Switching to the alternative polling GPU wait.\n"
+ " If you encounter issues, switch back to standard GPU waiting by setting\n"
+ " the GMX_CUDA_STREAMSYNC environment variable.\n");
+ }
- "NOTE: Using a GPU with ECC enabled and CUDA driver API version <5.0. A bug in this\n"
- " driver can cause performance loss.\n"
- " However, the polling waiting workaround can not be used because\n%s\n"
++ else
+ {
+ /* Tell the user that the ECC+old driver combination can be bad */
+ sprintf(sbuf,
- (!bX86 || !bTMPIAtomics) ?
- " atomic operations are not supported by the platform/CPU+compiler." :
- " GPU(s) are being oversubscribed.");
++ "NOTE: Using a GPU with ECC enabled and CUDA driver API version <5.0.\n"
++ " A known bug in this driver version can cause performance loss.\n"
++ " However, the polling wait workaround can not be used because\n%s\n"
+ " Consider updating the driver or turning ECC off.",
- void nbnxn_cuda_init_const(nbnxn_cuda_ptr_t cu_nb,
- const interaction_const_t *ic,
- const nonbonded_verlet_t *nbv)
++ (bX86 && bTMPIAtomics) ?
++ " GPU(s) are being oversubscribed." :
++ " atomic operations are not supported by the platform/CPU+compiler.");
+ md_print_warn(fplog, sbuf);
+ }
+ }
+ }
+ else
+ {
+ if (bNoStreamSync)
+ {
+ nb->bUseStreamSync = false;
+
+ md_print_warn(fplog,
+ "NOTE: Polling wait for GPU synchronization requested by GMX_NO_CUDA_STREAMSYNC\n");
+ }
+ else
+ {
+ /* no/off ECC, cudaStreamSynchronize not turned off by env. var. */
+ nb->bUseStreamSync = true;
+ }
+ }
+
+ /* CUDA timing disabled as event timers don't work:
+ - with multiple streams = domain-decomposition;
+ - with the polling waiting hack (without cudaStreamSynchronize);
+ - when turned off by GMX_DISABLE_CUDA_TIMING.
+ */
+ nb->bDoTime = (!nb->bUseTwoStreams && nb->bUseStreamSync &&
+ (getenv("GMX_DISABLE_CUDA_TIMING") == NULL));
+
+ if (nb->bDoTime)
+ {
+ init_timers(nb->timers, nb->bUseTwoStreams);
+ init_timings(nb->timings);
+ }
+
+ /* set the kernel type for the current GPU */
+ nb->kernel_ver = pick_nbnxn_kernel_version(fplog, nb->dev_info);
+ /* pick L1 cache configuration */
+ nbnxn_cuda_set_cacheconfig(nb->dev_info);
+
+ *p_cu_nb = nb;
+
+ if (debug)
+ {
+ fprintf(debug, "Initialized CUDA data structures.\n");
+ }
+}
+
- init_atomdata_first(cu_nb->atdat, nbv->grp[0].nbat->ntype);
- init_nbparam(cu_nb->nbparam, ic, nbv, cu_nb->dev_info);
++void nbnxn_cuda_init_const(nbnxn_cuda_ptr_t cu_nb,
++ const interaction_const_t *ic,
++ const nonbonded_verlet_group_t *nbv_group)
+{
++ init_atomdata_first(cu_nb->atdat, nbv_group[0].nbat->ntype);
++ init_nbparam(cu_nb->nbparam, ic, nbv_group[0].nbat, cu_nb->dev_info);
+
+ /* clear energy and shift force outputs */
+ nbnxn_cuda_clear_e_fshift(cu_nb);
+}
+
+void nbnxn_cuda_init_pairlist(nbnxn_cuda_ptr_t cu_nb,
+ const nbnxn_pairlist_t *h_plist,
+ int iloc)
+{
+ char sbuf[STRLEN];
+ cudaError_t stat;
+ bool bDoTime = cu_nb->bDoTime;
+ cudaStream_t stream = cu_nb->stream[iloc];
+ cu_plist_t *d_plist = cu_nb->plist[iloc];
+
+ if (d_plist->na_c < 0)
+ {
+ d_plist->na_c = h_plist->na_ci;
+ }
+ else
+ {
+ if (d_plist->na_c != h_plist->na_ci)
+ {
+ sprintf(sbuf, "In cu_init_plist: the #atoms per cell has changed (from %d to %d)",
+ d_plist->na_c, h_plist->na_ci);
+ gmx_incons(sbuf);
+ }
+ }
+
+ if (bDoTime)
+ {
+ stat = cudaEventRecord(cu_nb->timers->start_pl_h2d[iloc], stream);
+ CU_RET_ERR(stat, "cudaEventRecord failed");
+ }
+
+ cu_realloc_buffered((void **)&d_plist->sci, h_plist->sci, sizeof(*d_plist->sci),
+ &d_plist->nsci, &d_plist->sci_nalloc,
+ h_plist->nsci,
+ stream, true);
+
+ cu_realloc_buffered((void **)&d_plist->cj4, h_plist->cj4, sizeof(*d_plist->cj4),
+ &d_plist->ncj4, &d_plist->cj4_nalloc,
+ h_plist->ncj4,
+ stream, true);
+
+ cu_realloc_buffered((void **)&d_plist->excl, h_plist->excl, sizeof(*d_plist->excl),
+ &d_plist->nexcl, &d_plist->excl_nalloc,
+ h_plist->nexcl,
+ stream, true);
+
+ if (bDoTime)
+ {
+ stat = cudaEventRecord(cu_nb->timers->stop_pl_h2d[iloc], stream);
+ CU_RET_ERR(stat, "cudaEventRecord failed");
+ }
+
+ /* need to prune the pair list during the next step */
+ d_plist->bDoPrune = true;
+}
+
+void nbnxn_cuda_upload_shiftvec(nbnxn_cuda_ptr_t cu_nb,
+ const nbnxn_atomdata_t *nbatom)
+{
+ cu_atomdata_t *adat = cu_nb->atdat;
+ cudaStream_t ls = cu_nb->stream[eintLocal];
+
+ /* only if we have a dynamic box */
+ if (nbatom->bDynamicBox || !adat->bShiftVecUploaded)
+ {
+ cu_copy_H2D_async(adat->shift_vec, nbatom->shift_vec,
+ SHIFTS * sizeof(*adat->shift_vec), ls);
+ adat->bShiftVecUploaded = true;
+ }
+}
+
+/*! Clears the first natoms_clear elements of the GPU nonbonded force output array. */
+static void nbnxn_cuda_clear_f(nbnxn_cuda_ptr_t cu_nb, int natoms_clear)
+{
+ cudaError_t stat;
+ cu_atomdata_t *adat = cu_nb->atdat;
+ cudaStream_t ls = cu_nb->stream[eintLocal];
+
+ stat = cudaMemsetAsync(adat->f, 0, natoms_clear * sizeof(*adat->f), ls);
+ CU_RET_ERR(stat, "cudaMemsetAsync on f falied");
+}
+
+/*! Clears nonbonded shift force output array and energy outputs on the GPU. */
+static void nbnxn_cuda_clear_e_fshift(nbnxn_cuda_ptr_t cu_nb)
+{
+ cudaError_t stat;
+ cu_atomdata_t *adat = cu_nb->atdat;
+ cudaStream_t ls = cu_nb->stream[eintLocal];
+
+ stat = cudaMemsetAsync(adat->fshift, 0, SHIFTS * sizeof(*adat->fshift), ls);
+ CU_RET_ERR(stat, "cudaMemsetAsync on fshift falied");
+ stat = cudaMemsetAsync(adat->e_lj, 0, sizeof(*adat->e_lj), ls);
+ CU_RET_ERR(stat, "cudaMemsetAsync on e_lj falied");
+ stat = cudaMemsetAsync(adat->e_el, 0, sizeof(*adat->e_el), ls);
+ CU_RET_ERR(stat, "cudaMemsetAsync on e_el falied");
+}
+
+void nbnxn_cuda_clear_outputs(nbnxn_cuda_ptr_t cu_nb, int flags)
+{
+ nbnxn_cuda_clear_f(cu_nb, cu_nb->atdat->natoms);
+ /* clear shift force array and energies if the outputs were
+ used in the current step */
+ if (flags & GMX_FORCE_VIRIAL)
+ {
+ nbnxn_cuda_clear_e_fshift(cu_nb);
+ }
+}
+
+void nbnxn_cuda_init_atomdata(nbnxn_cuda_ptr_t cu_nb,
+ const nbnxn_atomdata_t *nbat)
+{
+ cudaError_t stat;
+ int nalloc, natoms;
+ bool realloced;
+ bool bDoTime = cu_nb->bDoTime;
+ cu_timers_t *timers = cu_nb->timers;
+ cu_atomdata_t *d_atdat = cu_nb->atdat;
+ cudaStream_t ls = cu_nb->stream[eintLocal];
+
+ natoms = nbat->natoms;
+ realloced = false;
+
+ if (bDoTime)
+ {
+ /* time async copy */
+ stat = cudaEventRecord(timers->start_atdat, ls);
+ CU_RET_ERR(stat, "cudaEventRecord failed");
+ }
+
+ /* need to reallocate if we have to copy more atoms than the amount of space
+ available and only allocate if we haven't initialized yet, i.e d_atdat->natoms == -1 */
+ if (natoms > d_atdat->nalloc)
+ {
+ nalloc = over_alloc_small(natoms);
+
+ /* free up first if the arrays have already been initialized */
+ if (d_atdat->nalloc != -1)
+ {
+ cu_free_buffered(d_atdat->f, &d_atdat->natoms, &d_atdat->nalloc);
+ cu_free_buffered(d_atdat->xq);
+ cu_free_buffered(d_atdat->atom_types);
+ }
+
+ stat = cudaMalloc((void **)&d_atdat->f, nalloc*sizeof(*d_atdat->f));
+ CU_RET_ERR(stat, "cudaMalloc failed on d_atdat->f");
+ stat = cudaMalloc((void **)&d_atdat->xq, nalloc*sizeof(*d_atdat->xq));
+ CU_RET_ERR(stat, "cudaMalloc failed on d_atdat->xq");
+
+ stat = cudaMalloc((void **)&d_atdat->atom_types, nalloc*sizeof(*d_atdat->atom_types));
+ CU_RET_ERR(stat, "cudaMalloc failed on d_atdat->atom_types");
+
+ d_atdat->nalloc = nalloc;
+ realloced = true;
+ }
+
+ d_atdat->natoms = natoms;
+ d_atdat->natoms_local = nbat->natoms_local;
+
+ /* need to clear GPU f output if realloc happened */
+ if (realloced)
+ {
+ nbnxn_cuda_clear_f(cu_nb, nalloc);
+ }
+
+ cu_copy_H2D_async(d_atdat->atom_types, nbat->type,
+ natoms*sizeof(*d_atdat->atom_types), ls);
+
+ if (bDoTime)
+ {
+ stat = cudaEventRecord(timers->stop_atdat, ls);
+ CU_RET_ERR(stat, "cudaEventRecord failed");
+ }
+}
+
+void nbnxn_cuda_free(FILE *fplog, nbnxn_cuda_ptr_t cu_nb)
+{
+ cudaError_t stat;
+ cu_atomdata_t *atdat;
+ cu_nbparam_t *nbparam;
+ cu_plist_t *plist, *plist_nl;
+ cu_timers_t *timers;
+
+ if (cu_nb == NULL) return;
+
+ atdat = cu_nb->atdat;
+ nbparam = cu_nb->nbparam;
+ plist = cu_nb->plist[eintLocal];
+ plist_nl = cu_nb->plist[eintNonlocal];
+ timers = cu_nb->timers;
+
+ if (nbparam->eeltype == eelCuEWALD_TAB || nbparam->eeltype == eelCuEWALD_TAB_TWIN)
+ {
+ stat = cudaUnbindTexture(nbnxn_cuda_get_coulomb_tab_texref());
+ CU_RET_ERR(stat, "cudaUnbindTexture on coulomb_tab failed");
+ cu_free_buffered(nbparam->coulomb_tab, &nbparam->coulomb_tab_size);
+ }
+
+ stat = cudaEventDestroy(cu_nb->nonlocal_done);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->nonlocal_done");
+ stat = cudaEventDestroy(cu_nb->misc_ops_done);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->misc_ops_done");
+
+ if (cu_nb->bDoTime)
+ {
+ stat = cudaEventDestroy(timers->start_atdat);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->start_atdat");
+ stat = cudaEventDestroy(timers->stop_atdat);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->stop_atdat");
+
+ /* The non-local counters/stream (second in the array) are needed only with DD. */
+ for (int i = 0; i <= (cu_nb->bUseTwoStreams ? 1 : 0); i++)
+ {
+ stat = cudaEventDestroy(timers->start_nb_k[i]);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->start_nb_k");
+ stat = cudaEventDestroy(timers->stop_nb_k[i]);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->stop_nb_k");
+
+ stat = cudaEventDestroy(timers->start_pl_h2d[i]);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->start_pl_h2d");
+ stat = cudaEventDestroy(timers->stop_pl_h2d[i]);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->stop_pl_h2d");
+
+ stat = cudaStreamDestroy(cu_nb->stream[i]);
+ CU_RET_ERR(stat, "cudaStreamDestroy failed on stream");
+
+ stat = cudaEventDestroy(timers->start_nb_h2d[i]);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->start_nb_h2d");
+ stat = cudaEventDestroy(timers->stop_nb_h2d[i]);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->stop_nb_h2d");
+
+ stat = cudaEventDestroy(timers->start_nb_d2h[i]);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->start_nb_d2h");
+ stat = cudaEventDestroy(timers->stop_nb_d2h[i]);
+ CU_RET_ERR(stat, "cudaEventDestroy failed on timers->stop_nb_d2h");
+ }
+ }
+
+ stat = cudaUnbindTexture(nbnxn_cuda_get_nbfp_texref());
+ CU_RET_ERR(stat, "cudaUnbindTexture on coulomb_tab failed");
+ cu_free_buffered(nbparam->nbfp);
+
+ stat = cudaFree(atdat->shift_vec);
+ CU_RET_ERR(stat, "cudaFree failed on atdat->shift_vec");
+ stat = cudaFree(atdat->fshift);
+ CU_RET_ERR(stat, "cudaFree failed on atdat->fshift");
+
+ stat = cudaFree(atdat->e_lj);
+ CU_RET_ERR(stat, "cudaFree failed on atdat->e_lj");
+ stat = cudaFree(atdat->e_el);
+ CU_RET_ERR(stat, "cudaFree failed on atdat->e_el");
+
+ cu_free_buffered(atdat->f, &atdat->natoms, &atdat->nalloc);
+ cu_free_buffered(atdat->xq);
+ cu_free_buffered(atdat->atom_types, &atdat->ntypes);
+
+ cu_free_buffered(plist->sci, &plist->nsci, &plist->sci_nalloc);
+ cu_free_buffered(plist->cj4, &plist->ncj4, &plist->cj4_nalloc);
+ cu_free_buffered(plist->excl, &plist->nexcl, &plist->excl_nalloc);
+ if (cu_nb->bUseTwoStreams)
+ {
+ cu_free_buffered(plist_nl->sci, &plist_nl->nsci, &plist_nl->sci_nalloc);
+ cu_free_buffered(plist_nl->cj4, &plist_nl->ncj4, &plist_nl->cj4_nalloc);
+ cu_free_buffered(plist_nl->excl, &plist_nl->nexcl, &plist->excl_nalloc);
+ }
+
+ sfree(atdat);
+ sfree(nbparam);
+ sfree(plist);
+ if (cu_nb->bUseTwoStreams)
+ {
+ sfree(plist_nl);
+ }
+ sfree(timers);
+ sfree(cu_nb->timings);
+ sfree(cu_nb);
+
+ if (debug)
+ {
+ fprintf(debug, "Cleaned up CUDA data structures.\n");
+ }
+}
+
+void cu_synchstream_atdat(nbnxn_cuda_ptr_t cu_nb, int iloc)
+{
+ cudaError_t stat;
+ cudaStream_t stream = cu_nb->stream[iloc];
+
+ stat = cudaStreamWaitEvent(stream, cu_nb->timers->stop_atdat, 0);
+ CU_RET_ERR(stat, "cudaStreamWaitEvent failed");
+}
+
+wallclock_gpu_t * nbnxn_cuda_get_timings(nbnxn_cuda_ptr_t cu_nb)
+{
+ return (cu_nb != NULL && cu_nb->bDoTime) ? cu_nb->timings : NULL;
+}
+
+void nbnxn_cuda_reset_timings(nbnxn_cuda_ptr_t cu_nb)
+{
+ if (cu_nb->bDoTime)
+ {
+ init_timings(cu_nb->timings);
+ }
+}
+
+int nbnxn_cuda_min_ci_balanced(nbnxn_cuda_ptr_t cu_nb)
+{
+ return cu_nb != NULL ?
+ gpu_min_ci_balanced_factor*cu_nb->dev_info->prop.multiProcessorCount : 0;
+
+}
--- /dev/null
- static FILE *open_slab_out(const char *fn, t_rot *rot, const output_env_t oenv)
+/*
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "domdec.h"
+#include "gmx_wallcycle.h"
+#include "gmx_cyclecounter.h"
+#include "trnio.h"
+#include "smalloc.h"
+#include "network.h"
+#include "pbc.h"
+#include "futil.h"
+#include "mdrun.h"
+#include "txtdump.h"
+#include "names.h"
+#include "mtop_util.h"
+#include "names.h"
+#include "nrjac.h"
+#include "vec.h"
+#include "gmx_ga2la.h"
+#include "xvgr.h"
+#include "gmxfio.h"
+#include "groupcoord.h"
+#include "pull_rotation.h"
+#include "gmx_sort.h"
+#include "copyrite.h"
+#include "macros.h"
+
+
+static char *RotStr = {"Enforced rotation:"};
+
+
+/* Set the minimum weight for the determination of the slab centers */
+#define WEIGHT_MIN (10*GMX_FLOAT_MIN)
+
+/* Helper structure for sorting positions along rotation vector */
+typedef struct {
+ real xcproj; /* Projection of xc on the rotation vector */
+ int ind; /* Index of xc */
+ real m; /* Mass */
+ rvec x; /* Position */
+ rvec x_ref; /* Reference position */
+} sort_along_vec_t;
+
+
+/* Enforced rotation / flexible: determine the angle of each slab */
+typedef struct gmx_slabdata
+{
+ int nat; /* Number of atoms belonging to this slab */
+ rvec *x; /* The positions belonging to this slab. In
+ general, this should be all positions of the
+ whole rotation group, but we leave those away
+ that have a small enough weight */
+ rvec *ref; /* Same for reference */
+ real *weight; /* The weight for each atom */
+} t_gmx_slabdata;
+
+
+/* Helper structure for potential fitting */
+typedef struct gmx_potfit
+{
+ real *degangle; /* Set of angles for which the potential is
+ calculated. The optimum fit is determined as
+ the angle for with the potential is minimal */
+ real *V; /* Potential for the different angles */
+ matrix *rotmat; /* Rotation matrix corresponding to the angles */
+} t_gmx_potfit;
+
+
+/* Enforced rotation data for all groups */
+typedef struct gmx_enfrot
+{
+ FILE *out_rot; /* Output file for rotation data */
+ FILE *out_torque; /* Output file for torque data */
+ FILE *out_angles; /* Output file for slab angles for flexible type */
+ FILE *out_slabs; /* Output file for slab centers */
+ int bufsize; /* Allocation size of buf */
+ rvec *xbuf; /* Coordinate buffer variable for sorting */
+ real *mbuf; /* Masses buffer variable for sorting */
+ sort_along_vec_t *data; /* Buffer variable needed for position sorting */
+ real *mpi_inbuf; /* MPI buffer */
+ real *mpi_outbuf; /* MPI buffer */
+ int mpi_bufsize; /* Allocation size of in & outbuf */
+ unsigned long Flags; /* mdrun flags */
+ gmx_bool bOut; /* Used to skip first output when appending to
+ * avoid duplicate entries in rotation outfiles */
+} t_gmx_enfrot;
+
+
+/* Global enforced rotation data for a single rotation group */
+typedef struct gmx_enfrotgrp
+{
+ real degangle; /* Rotation angle in degrees */
+ matrix rotmat; /* Rotation matrix */
+ atom_id *ind_loc; /* Local rotation indices */
+ int nat_loc; /* Number of local group atoms */
+ int nalloc_loc; /* Allocation size for ind_loc and weight_loc */
+
+ real V; /* Rotation potential for this rotation group */
+ rvec *f_rot_loc; /* Array to store the forces on the local atoms
+ resulting from enforced rotation potential */
+
+ /* Collective coordinates for the whole rotation group */
+ real *xc_ref_length; /* Length of each x_rotref vector after x_rotref
+ has been put into origin */
+ int *xc_ref_ind; /* Position of each local atom in the collective
+ array */
+ rvec xc_center; /* Center of the rotation group positions, may
+ be mass weighted */
+ rvec xc_ref_center; /* dito, for the reference positions */
+ rvec *xc; /* Current (collective) positions */
+ ivec *xc_shifts; /* Current (collective) shifts */
+ ivec *xc_eshifts; /* Extra shifts since last DD step */
+ rvec *xc_old; /* Old (collective) positions */
+ rvec *xc_norm; /* Normalized form of the current positions */
+ rvec *xc_ref_sorted; /* Reference positions (sorted in the same order
+ as xc when sorted) */
+ int *xc_sortind; /* Where is a position found after sorting? */
+ real *mc; /* Collective masses */
+ real *mc_sorted;
+ real invmass; /* one over the total mass of the rotation group */
+
+ real torque_v; /* Torque in the direction of rotation vector */
+ real angle_v; /* Actual angle of the whole rotation group */
+ /* Fixed rotation only */
+ real weight_v; /* Weights for angle determination */
+ rvec *xr_loc; /* Local reference coords, correctly rotated */
+ rvec *x_loc_pbc; /* Local current coords, correct PBC image */
+ real *m_loc; /* Masses of the current local atoms */
+
+ /* Flexible rotation only */
+ int nslabs_alloc; /* For this many slabs memory is allocated */
+ int slab_first; /* Lowermost slab for that the calculation needs
+ to be performed at a given time step */
+ int slab_last; /* Uppermost slab ... */
+ int slab_first_ref; /* First slab for which ref. center is stored */
+ int slab_last_ref; /* Last ... */
+ int slab_buffer; /* Slab buffer region around reference slabs */
+ int *firstatom; /* First relevant atom for a slab */
+ int *lastatom; /* Last relevant atom for a slab */
+ rvec *slab_center; /* Gaussian-weighted slab center */
+ rvec *slab_center_ref; /* Gaussian-weighted slab center for the
+ reference positions */
+ real *slab_weights; /* Sum of gaussian weights in a slab */
+ real *slab_torque_v; /* Torque T = r x f for each slab. */
+ /* torque_v = m.v = angular momentum in the
+ direction of v */
+ real max_beta; /* min_gaussian from inputrec->rotgrp is the
+ minimum value the gaussian must have so that
+ the force is actually evaluated max_beta is
+ just another way to put it */
+ real *gn_atom; /* Precalculated gaussians for a single atom */
+ int *gn_slabind; /* Tells to which slab each precalculated gaussian
+ belongs */
+ rvec *slab_innersumvec; /* Inner sum of the flexible2 potential per slab;
+ this is precalculated for optimization reasons */
+ t_gmx_slabdata *slab_data; /* Holds atom positions and gaussian weights
+ of atoms belonging to a slab */
+
+ /* For potential fits with varying angle: */
+ t_gmx_potfit *PotAngleFit; /* Used for fit type 'potential' */
+} t_gmx_enfrotgrp;
+
+
+/* Activate output of forces for correctness checks */
+/* #define PRINT_FORCES */
+#ifdef PRINT_FORCES
+#define PRINT_FORCE_J fprintf(stderr, "f%d = %15.8f %15.8f %15.8f\n", erg->xc_ref_ind[j], erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ]);
+#define PRINT_POT_TAU if (MASTER(cr)) { \
+ fprintf(stderr, "potential = %15.8f\n" "torque = %15.8f\n", erg->V, erg->torque_v); \
+}
+#else
+#define PRINT_FORCE_J
+#define PRINT_POT_TAU
+#endif
+
+/* Shortcuts for often used queries */
+#define ISFLEX(rg) ( (rg->eType == erotgFLEX) || (rg->eType == erotgFLEXT) || (rg->eType == erotgFLEX2) || (rg->eType == erotgFLEX2T) )
+#define ISCOLL(rg) ( (rg->eType == erotgFLEX) || (rg->eType == erotgFLEXT) || (rg->eType == erotgFLEX2) || (rg->eType == erotgFLEX2T) || (rg->eType == erotgRMPF) || (rg->eType == erotgRM2PF) )
+
+
+/* Does any of the rotation groups use slab decomposition? */
+static gmx_bool HaveFlexibleGroups(t_rot *rot)
+{
+ int g;
+ t_rotgrp *rotg;
+
+
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (ISFLEX(rotg))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/* Is for any group the fit angle determined by finding the minimum of the
+ * rotation potential? */
+static gmx_bool HavePotFitGroups(t_rot *rot)
+{
+ int g;
+ t_rotgrp *rotg;
+
+
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static double** allocate_square_matrix(int dim)
+{
+ int i;
+ double** mat = NULL;
+
+
+ snew(mat, dim);
+ for (i = 0; i < dim; i++)
+ {
+ snew(mat[i], dim);
+ }
+
+ return mat;
+}
+
+
+static void free_square_matrix(double** mat, int dim)
+{
+ int i;
+
+
+ for (i = 0; i < dim; i++)
+ {
+ sfree(mat[i]);
+ }
+ sfree(mat);
+}
+
+
+/* Return the angle for which the potential is minimal */
+static real get_fitangle(t_rotgrp *rotg, gmx_enfrotgrp_t erg)
+{
+ int i;
+ real fitangle = -999.9;
+ real pot_min = GMX_FLOAT_MAX;
+ t_gmx_potfit *fit;
+
+
+ fit = erg->PotAngleFit;
+
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ {
+ if (fit->V[i] < pot_min)
+ {
+ pot_min = fit->V[i];
+ fitangle = fit->degangle[i];
+ }
+ }
+
+ return fitangle;
+}
+
+
+/* Reduce potential angle fit data for this group at this time step? */
+static gmx_inline gmx_bool bPotAngle(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
+{
+ return ( (erotgFitPOT == rotg->eFittype) && (do_per_step(step, rot->nstsout) || do_per_step(step, rot->nstrout)) );
+}
+
+/* Reduce slab torqe data for this group at this time step? */
+static gmx_inline gmx_bool bSlabTau(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
+{
+ return ( (ISFLEX(rotg)) && do_per_step(step, rot->nstsout) );
+}
+
+/* Output rotation energy, torques, etc. for each rotation group */
+static void reduce_output(t_commrec *cr, t_rot *rot, real t, gmx_large_int_t step)
+{
+ int g, i, islab, nslabs = 0;
+ int count; /* MPI element counter */
+ t_rotgrp *rotg;
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ real fitangle;
+ gmx_bool bFlex;
+
+
+ er = rot->enfrot;
+
+ /* Fill the MPI buffer with stuff to reduce. If items are added for reduction
+ * here, the MPI buffer size has to be enlarged also in calc_mpi_bufsize() */
+ if (PAR(cr))
+ {
+ count = 0;
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+ nslabs = erg->slab_last - erg->slab_first + 1;
+ er->mpi_inbuf[count++] = erg->V;
+ er->mpi_inbuf[count++] = erg->torque_v;
+ er->mpi_inbuf[count++] = erg->angle_v;
+ er->mpi_inbuf[count++] = erg->weight_v; /* weights are not needed for flex types, but this is just a single value */
+
+ if (bPotAngle(rot, rotg, step))
+ {
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ {
+ er->mpi_inbuf[count++] = erg->PotAngleFit->V[i];
+ }
+ }
+ if (bSlabTau(rot, rotg, step))
+ {
+ for (i = 0; i < nslabs; i++)
+ {
+ er->mpi_inbuf[count++] = erg->slab_torque_v[i];
+ }
+ }
+ }
+ if (count > er->mpi_bufsize)
+ {
+ gmx_fatal(FARGS, "%s MPI buffer overflow, please report this error.", RotStr);
+ }
+
+#ifdef GMX_MPI
+ MPI_Reduce(er->mpi_inbuf, er->mpi_outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
+#endif
+
+ /* Copy back the reduced data from the buffer on the master */
+ if (MASTER(cr))
+ {
+ count = 0;
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+ nslabs = erg->slab_last - erg->slab_first + 1;
+ erg->V = er->mpi_outbuf[count++];
+ erg->torque_v = er->mpi_outbuf[count++];
+ erg->angle_v = er->mpi_outbuf[count++];
+ erg->weight_v = er->mpi_outbuf[count++];
+
+ if (bPotAngle(rot, rotg, step))
+ {
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ {
+ erg->PotAngleFit->V[i] = er->mpi_outbuf[count++];
+ }
+ }
+ if (bSlabTau(rot, rotg, step))
+ {
+ for (i = 0; i < nslabs; i++)
+ {
+ erg->slab_torque_v[i] = er->mpi_outbuf[count++];
+ }
+ }
+ }
+ }
+ }
+
+ /* Output */
+ if (MASTER(cr))
+ {
+ /* Angle and torque for each rotation group */
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ bFlex = ISFLEX(rotg);
+
+ erg = rotg->enfrotgrp;
+
+ /* Output to main rotation output file: */
+ if (do_per_step(step, rot->nstrout) )
+ {
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ fitangle = get_fitangle(rotg, erg);
+ }
+ else
+ {
+ if (bFlex)
+ {
+ fitangle = erg->angle_v; /* RMSD fit angle */
+ }
+ else
+ {
+ fitangle = (erg->angle_v/erg->weight_v)*180.0*M_1_PI;
+ }
+ }
+ fprintf(er->out_rot, "%12.4f", fitangle);
+ fprintf(er->out_rot, "%12.3e", erg->torque_v);
+ fprintf(er->out_rot, "%12.3e", erg->V);
+ }
+
+ if (do_per_step(step, rot->nstsout) )
+ {
+ /* Output to torque log file: */
+ if (bFlex)
+ {
+ fprintf(er->out_torque, "%12.3e%6d", t, g);
+ for (i = erg->slab_first; i <= erg->slab_last; i++)
+ {
+ islab = i - erg->slab_first; /* slab index */
+ /* Only output if enough weight is in slab */
+ if (erg->slab_weights[islab] > rotg->min_gaussian)
+ {
+ fprintf(er->out_torque, "%6d%12.3e", i, erg->slab_torque_v[islab]);
+ }
+ }
+ fprintf(er->out_torque, "\n");
+ }
+
+ /* Output to angles log file: */
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ fprintf(er->out_angles, "%12.3e%6d%12.4f", t, g, erg->degangle);
+ /* Output energies at a set of angles around the reference angle */
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ {
+ fprintf(er->out_angles, "%12.3e", erg->PotAngleFit->V[i]);
+ }
+ fprintf(er->out_angles, "\n");
+ }
+ }
+ }
+ if (do_per_step(step, rot->nstrout) )
+ {
+ fprintf(er->out_rot, "\n");
+ }
+ }
+}
+
+
+/* Add the forces from enforced rotation potential to the local forces.
+ * Should be called after the SR forces have been evaluated */
+extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t)
+{
+ int g, l, ii;
+ t_rotgrp *rotg;
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ real Vrot = 0.0; /* If more than one rotation group is present, Vrot
+ assembles the local parts from all groups */
+
+
+ er = rot->enfrot;
+
+ /* Loop over enforced rotation groups (usually 1, though)
+ * Apply the forces from rotation potentials */
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+ Vrot += erg->V; /* add the local parts from the nodes */
+ for (l = 0; l < erg->nat_loc; l++)
+ {
+ /* Get the right index of the local force */
+ ii = erg->ind_loc[l];
+ /* Add */
+ rvec_inc(f[ii], erg->f_rot_loc[l]);
+ }
+ }
+
+ /* Reduce energy,torque, angles etc. to get the sum values (per rotation group)
+ * on the master and output these values to file. */
+ if ( (do_per_step(step, rot->nstrout) || do_per_step(step, rot->nstsout)) && er->bOut)
+ {
+ reduce_output(cr, rot, t, step);
+ }
+
+ /* When appending, er->bOut is FALSE the first time to avoid duplicate entries */
+ er->bOut = TRUE;
+
+ PRINT_POT_TAU
+
+ return Vrot;
+}
+
+
+/* The Gaussian norm is chosen such that the sum of the gaussian functions
+ * over the slabs is approximately 1.0 everywhere */
+#define GAUSS_NORM 0.569917543430618
+
+
+/* Calculate the maximum beta that leads to a gaussian larger min_gaussian,
+ * also does some checks
+ */
+static double calc_beta_max(real min_gaussian, real slab_dist)
+{
+ double sigma;
+ double arg;
+
+
+ /* Actually the next two checks are already made in grompp */
+ if (slab_dist <= 0)
+ {
+ gmx_fatal(FARGS, "Slab distance of flexible rotation groups must be >=0 !");
+ }
+ if (min_gaussian <= 0)
+ {
+ gmx_fatal(FARGS, "Cutoff value for Gaussian must be > 0. (You requested %f)");
+ }
+
+ /* Define the sigma value */
+ sigma = 0.7*slab_dist;
+
+ /* Calculate the argument for the logarithm and check that the log() result is negative or 0 */
+ arg = min_gaussian/GAUSS_NORM;
+ if (arg > 1.0)
+ {
+ gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", GAUSS_NORM);
+ }
+
+ return sqrt(-2.0*sigma*sigma*log(min_gaussian/GAUSS_NORM));
+}
+
+
+static gmx_inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
+{
+ return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
+}
+
+
+static gmx_inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
+{
+ const real norm = GAUSS_NORM;
+ real sigma;
+
+
+ /* Define the sigma value */
+ sigma = 0.7*rotg->slab_dist;
+ /* Calculate the Gaussian value of slab n for position curr_x */
+ return norm * exp( -0.5 * sqr( calc_beta(curr_x, rotg, n)/sigma ) );
+}
+
+
+/* Returns the weight in a single slab, also calculates the Gaussian- and mass-
+ * weighted sum of positions for that slab */
+static real get_slab_weight(int j, t_rotgrp *rotg, rvec xc[], real mc[], rvec *x_weighted_sum)
+{
+ rvec curr_x; /* The position of an atom */
+ rvec curr_x_weighted; /* The gaussian-weighted position */
+ real gaussian; /* A single gaussian weight */
+ real wgauss; /* gaussian times current mass */
+ real slabweight = 0.0; /* The sum of weights in the slab */
+ int i, islab;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg = rotg->enfrotgrp;
+ clear_rvec(*x_weighted_sum);
+
+ /* Slab index */
+ islab = j - erg->slab_first;
+
+ /* Loop over all atoms in the rotation group */
+ for (i = 0; i < rotg->nat; i++)
+ {
+ copy_rvec(xc[i], curr_x);
+ gaussian = gaussian_weight(curr_x, rotg, j);
+ wgauss = gaussian * mc[i];
+ svmul(wgauss, curr_x, curr_x_weighted);
+ rvec_add(*x_weighted_sum, curr_x_weighted, *x_weighted_sum);
+ slabweight += wgauss;
+ } /* END of loop over rotation group atoms */
+
+ return slabweight;
+}
+
+
+static void get_slab_centers(
+ t_rotgrp *rotg, /* The rotation group information */
+ rvec *xc, /* The rotation group positions; will
+ typically be enfrotgrp->xc, but at first call
+ it is enfrotgrp->xc_ref */
+ real *mc, /* The masses of the rotation group atoms */
+ int g, /* The number of the rotation group */
+ real time, /* Used for output only */
+ FILE *out_slabs, /* For outputting center per slab information */
+ gmx_bool bOutStep, /* Is this an output step? */
+ gmx_bool bReference) /* If this routine is called from
+ init_rot_group we need to store
+ the reference slab centers */
+{
+ int j, islab;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg = rotg->enfrotgrp;
+
+ /* Loop over slabs */
+ for (j = erg->slab_first; j <= erg->slab_last; j++)
+ {
+ islab = j - erg->slab_first;
+ erg->slab_weights[islab] = get_slab_weight(j, rotg, xc, mc, &erg->slab_center[islab]);
+
+ /* We can do the calculations ONLY if there is weight in the slab! */
+ if (erg->slab_weights[islab] > WEIGHT_MIN)
+ {
+ svmul(1.0/erg->slab_weights[islab], erg->slab_center[islab], erg->slab_center[islab]);
+ }
+ else
+ {
+ /* We need to check this here, since we divide through slab_weights
+ * in the flexible low-level routines! */
+ gmx_fatal(FARGS, "Not enough weight in slab %d. Slab center cannot be determined!", j);
+ }
+
+ /* At first time step: save the centers of the reference structure */
+ if (bReference)
+ {
+ copy_rvec(erg->slab_center[islab], erg->slab_center_ref[islab]);
+ }
+ } /* END of loop over slabs */
+
+ /* Output on the master */
+ if ( (NULL != out_slabs) && bOutStep)
+ {
+ fprintf(out_slabs, "%12.3e%6d", time, g);
+ for (j = erg->slab_first; j <= erg->slab_last; j++)
+ {
+ islab = j - erg->slab_first;
+ fprintf(out_slabs, "%6d%12.3e%12.3e%12.3e",
+ j, erg->slab_center[islab][XX], erg->slab_center[islab][YY], erg->slab_center[islab][ZZ]);
+ }
+ fprintf(out_slabs, "\n");
+ }
+}
+
+
+static void calc_rotmat(
+ rvec vec,
+ real degangle, /* Angle alpha of rotation at time t in degrees */
+ matrix rotmat) /* Rotation matrix */
+{
+ real radangle; /* Rotation angle in radians */
+ real cosa; /* cosine alpha */
+ real sina; /* sine alpha */
+ real OMcosa; /* 1 - cos(alpha) */
+ real dumxy, dumxz, dumyz; /* save computations */
+ rvec rot_vec; /* Rotate around rot_vec ... */
+
+
+ radangle = degangle * M_PI/180.0;
+ copy_rvec(vec, rot_vec );
+
+ /* Precompute some variables: */
+ cosa = cos(radangle);
+ sina = sin(radangle);
+ OMcosa = 1.0 - cosa;
+ dumxy = rot_vec[XX]*rot_vec[YY]*OMcosa;
+ dumxz = rot_vec[XX]*rot_vec[ZZ]*OMcosa;
+ dumyz = rot_vec[YY]*rot_vec[ZZ]*OMcosa;
+
+ /* Construct the rotation matrix for this rotation group: */
+ /* 1st column: */
+ rotmat[XX][XX] = cosa + rot_vec[XX]*rot_vec[XX]*OMcosa;
+ rotmat[YY][XX] = dumxy + rot_vec[ZZ]*sina;
+ rotmat[ZZ][XX] = dumxz - rot_vec[YY]*sina;
+ /* 2nd column: */
+ rotmat[XX][YY] = dumxy - rot_vec[ZZ]*sina;
+ rotmat[YY][YY] = cosa + rot_vec[YY]*rot_vec[YY]*OMcosa;
+ rotmat[ZZ][YY] = dumyz + rot_vec[XX]*sina;
+ /* 3rd column: */
+ rotmat[XX][ZZ] = dumxz + rot_vec[YY]*sina;
+ rotmat[YY][ZZ] = dumyz - rot_vec[XX]*sina;
+ rotmat[ZZ][ZZ] = cosa + rot_vec[ZZ]*rot_vec[ZZ]*OMcosa;
+
+#ifdef PRINTMATRIX
+ int iii, jjj;
+
+ for (iii = 0; iii < 3; iii++)
+ {
+ for (jjj = 0; jjj < 3; jjj++)
+ {
+ fprintf(stderr, " %10.8f ", rotmat[iii][jjj]);
+ }
+ fprintf(stderr, "\n");
+ }
+#endif
+}
+
+
+/* Calculates torque on the rotation axis tau = position x force */
+static gmx_inline real torque(
+ rvec rotvec, /* rotation vector; MUST be normalized! */
+ rvec force, /* force */
+ rvec x, /* position of atom on which the force acts */
+ rvec pivot) /* pivot point of rotation axis */
+{
+ rvec vectmp, tau;
+
+
+ /* Subtract offset */
+ rvec_sub(x, pivot, vectmp);
+
+ /* position x force */
+ cprod(vectmp, force, tau);
+
+ /* Return the part of the torque which is parallel to the rotation vector */
+ return iprod(tau, rotvec);
+}
+
+
+/* Right-aligned output of value with standard width */
+static void print_aligned(FILE *fp, char *str)
+{
+ fprintf(fp, "%12s", str);
+}
+
+
+/* Right-aligned output of value with standard short width */
+static void print_aligned_short(FILE *fp, char *str)
+{
+ fprintf(fp, "%6s", str);
+}
+
+
+static FILE *open_output_file(const char *fn, int steps, const char what[])
+{
+ FILE *fp;
+
+
+ fp = ffopen(fn, "w");
+
+ fprintf(fp, "# Output of %s is written in intervals of %d time step%s.\n#\n",
+ what, steps, steps > 1 ? "s" : "");
+
+ return fp;
+}
+
+
+/* Open output file for slab center data. Call on master only */
- static FILE *open_angles_out(const char *fn, t_rot *rot, const output_env_t oenv)
++static FILE *open_slab_out(const char *fn, t_rot *rot)
+{
+ FILE *fp;
+ int g, i;
+ t_rotgrp *rotg;
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn, "a");
+ }
+ else
+ {
+ fp = open_output_file(fn, rot->nstsout, "gaussian weighted slab centers");
+
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (ISFLEX(rotg))
+ {
+ fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, %s.\n",
+ g, erotg_names[rotg->eType], rotg->slab_dist,
+ rotg->bMassW ? "centers of mass" : "geometrical centers");
+ }
+ }
+
+ fprintf(fp, "# Reference centers are listed first (t=-1).\n");
+ fprintf(fp, "# The following columns have the syntax:\n");
+ fprintf(fp, "# ");
+ print_aligned_short(fp, "t");
+ print_aligned_short(fp, "grp");
+ /* Print legend for the first two entries only ... */
+ for (i = 0; i < 2; i++)
+ {
+ print_aligned_short(fp, "slab");
+ print_aligned(fp, "X center");
+ print_aligned(fp, "Y center");
+ print_aligned(fp, "Z center");
+ }
+ fprintf(fp, " ...\n");
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+/* Adds 'buf' to 'str' */
+static void add_to_string(char **str, char *buf)
+{
+ int len;
+
+
+ len = strlen(*str) + strlen(buf) + 1;
+ srenew(*str, len);
+ strcat(*str, buf);
+}
+
+
+static void add_to_string_aligned(char **str, char *buf)
+{
+ char buf_aligned[STRLEN];
+
+ sprintf(buf_aligned, "%12s", buf);
+ add_to_string(str, buf_aligned);
+}
+
+
+/* Open output file and print some general information about the rotation groups.
+ * Call on master only */
+static FILE *open_rot_out(const char *fn, t_rot *rot, const output_env_t oenv)
+{
+ FILE *fp;
+ int g, nsets;
+ t_rotgrp *rotg;
+ const char **setname;
+ char buf[50], buf2[75];
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ gmx_bool bFlex;
+ char *LegendStr = NULL;
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn, "a");
+ }
+ else
+ {
+ fp = xvgropen(fn, "Rotation angles and energy", "Time (ps)", "angles (degrees) and energies (kJ/mol)", oenv);
+ fprintf(fp, "# Output of enforced rotation data is written in intervals of %d time step%s.\n#\n", rot->nstrout, rot->nstrout > 1 ? "s" : "");
+ fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector v.\n");
+ fprintf(fp, "# To obtain the vectorial torque, multiply tau with the group's rot_vec.\n");
+ fprintf(fp, "# For flexible groups, tau(t,n) from all slabs n have been summed in a single value tau(t) here.\n");
+ fprintf(fp, "# The torques tau(t,n) are found in the rottorque.log (-rt) output file\n");
+
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+ bFlex = ISFLEX(rotg);
+
+ fprintf(fp, "#\n");
+ fprintf(fp, "# ROTATION GROUP %d, potential type '%s':\n", g, erotg_names[rotg->eType]);
+ fprintf(fp, "# rot_massw%d %s\n", g, yesno_names[rotg->bMassW]);
+ fprintf(fp, "# rot_vec%d %12.5e %12.5e %12.5e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
+ fprintf(fp, "# rot_rate%d %12.5e degrees/ps\n", g, rotg->rate);
+ fprintf(fp, "# rot_k%d %12.5e kJ/(mol*nm^2)\n", g, rotg->k);
+ if (rotg->eType == erotgISO || rotg->eType == erotgPM || rotg->eType == erotgRM || rotg->eType == erotgRM2)
+ {
+ fprintf(fp, "# rot_pivot%d %12.5e %12.5e %12.5e nm\n", g, rotg->pivot[XX], rotg->pivot[YY], rotg->pivot[ZZ]);
+ }
+
+ if (bFlex)
+ {
+ fprintf(fp, "# rot_slab_distance%d %f nm\n", g, rotg->slab_dist);
+ fprintf(fp, "# rot_min_gaussian%d %12.5e\n", g, rotg->min_gaussian);
+ }
+
+ /* Output the centers of the rotation groups for the pivot-free potentials */
+ if ((rotg->eType == erotgISOPF) || (rotg->eType == erotgPMPF) || (rotg->eType == erotgRMPF) || (rotg->eType == erotgRM2PF
+ || (rotg->eType == erotgFLEXT) || (rotg->eType == erotgFLEX2T)) )
+ {
+ fprintf(fp, "# ref. grp. %d center %12.5e %12.5e %12.5e\n", g,
+ erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
+
+ fprintf(fp, "# grp. %d init.center %12.5e %12.5e %12.5e\n", g,
+ erg->xc_center[XX], erg->xc_center[YY], erg->xc_center[ZZ]);
+ }
+
+ if ( (rotg->eType == erotgRM2) || (rotg->eType == erotgFLEX2) || (rotg->eType == erotgFLEX2T) )
+ {
+ fprintf(fp, "# rot_eps%d %12.5e nm^2\n", g, rotg->eps);
+ }
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ fprintf(fp, "#\n");
+ fprintf(fp, "# theta_fit%d is determined by first evaluating the potential for %d angles around theta_ref%d.\n",
+ g, rotg->PotAngle_nstep, g);
+ fprintf(fp, "# The fit angle is the one with the smallest potential. It is given as the deviation\n");
+ fprintf(fp, "# from the reference angle, i.e. if theta_ref=X and theta_fit=Y, then the angle with\n");
+ fprintf(fp, "# minimal value of the potential is X+Y. Angular resolution is %g degrees.\n", rotg->PotAngle_step);
+ }
+ }
+
+ /* Print a nice legend */
+ snew(LegendStr, 1);
+ LegendStr[0] = '\0';
+ sprintf(buf, "# %6s", "time");
+ add_to_string_aligned(&LegendStr, buf);
+
+ nsets = 0;
+ snew(setname, 4*rot->ngrp);
+
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ sprintf(buf, "theta_ref%d", g);
+ add_to_string_aligned(&LegendStr, buf);
+
+ sprintf(buf2, "%s (degrees)", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+ }
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ bFlex = ISFLEX(rotg);
+
+ /* For flexible axis rotation we use RMSD fitting to determine the
+ * actual angle of the rotation group */
+ if (bFlex || erotgFitPOT == rotg->eFittype)
+ {
+ sprintf(buf, "theta_fit%d", g);
+ }
+ else
+ {
+ sprintf(buf, "theta_av%d", g);
+ }
+ add_to_string_aligned(&LegendStr, buf);
+ sprintf(buf2, "%s (degrees)", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+
+ sprintf(buf, "tau%d", g);
+ add_to_string_aligned(&LegendStr, buf);
+ sprintf(buf2, "%s (kJ/mol)", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+
+ sprintf(buf, "energy%d", g);
+ add_to_string_aligned(&LegendStr, buf);
+ sprintf(buf2, "%s (kJ/mol)", buf);
+ setname[nsets] = strdup(buf2);
+ nsets++;
+ }
+ fprintf(fp, "#\n");
+
+ if (nsets > 1)
+ {
+ xvgr_legend(fp, nsets, setname, oenv);
+ }
+ sfree(setname);
+
+ fprintf(fp, "#\n# Legend for the following data columns:\n");
+ fprintf(fp, "%s\n", LegendStr);
+ sfree(LegendStr);
+
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+/* Call on master only */
- static FILE *open_torque_out(const char *fn, t_rot *rot, const output_env_t oenv)
++static FILE *open_angles_out(const char *fn, t_rot *rot)
+{
+ int g, i;
+ FILE *fp;
+ t_rotgrp *rotg;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ char buf[100];
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn, "a");
+ }
+ else
+ {
+ /* Open output file and write some information about it's structure: */
+ fp = open_output_file(fn, rot->nstsout, "rotation group angles");
+ fprintf(fp, "# All angles given in degrees, time in ps.\n");
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+
+ /* Output for this group happens only if potential type is flexible or
+ * if fit type is potential! */
+ if (ISFLEX(rotg) || (erotgFitPOT == rotg->eFittype) )
+ {
+ if (ISFLEX(rotg))
+ {
+ sprintf(buf, " slab distance %f nm, ", rotg->slab_dist);
+ }
+ else
+ {
+ buf[0] = '\0';
+ }
+
+ fprintf(fp, "#\n# ROTATION GROUP %d '%s',%s fit type '%s'.\n",
+ g, erotg_names[rotg->eType], buf, erotg_fitnames[rotg->eFittype]);
+
+ /* Special type of fitting using the potential minimum. This is
+ * done for the whole group only, not for the individual slabs. */
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ fprintf(fp, "# To obtain theta_fit%d, the potential is evaluated for %d angles around theta_ref%d\n", g, rotg->PotAngle_nstep, g);
+ fprintf(fp, "# The fit angle in the rotation standard outfile is the one with minimal energy E(theta_fit) [kJ/mol].\n");
+ fprintf(fp, "#\n");
+ }
+
+ fprintf(fp, "# Legend for the group %d data columns:\n", g);
+ fprintf(fp, "# ");
+ print_aligned_short(fp, "time");
+ print_aligned_short(fp, "grp");
+ print_aligned(fp, "theta_ref");
+
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ /* Output the set of angles around the reference angle */
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ {
+ sprintf(buf, "E(%g)", erg->PotAngleFit->degangle[i]);
+ print_aligned(fp, buf);
+ }
+ }
+ else
+ {
+ /* Output fit angle for each slab */
+ print_aligned_short(fp, "slab");
+ print_aligned_short(fp, "atoms");
+ print_aligned(fp, "theta_fit");
+ print_aligned_short(fp, "slab");
+ print_aligned_short(fp, "atoms");
+ print_aligned(fp, "theta_fit");
+ fprintf(fp, " ...");
+ }
+ fprintf(fp, "\n");
+ }
+ }
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+/* Open torque output file and write some information about it's structure.
+ * Call on master only */
- /* Determine the max slab */
++static FILE *open_torque_out(const char *fn, t_rot *rot)
+{
+ FILE *fp;
+ int g;
+ t_rotgrp *rotg;
+
+
+ if (rot->enfrot->Flags & MD_APPENDFILES)
+ {
+ fp = gmx_fio_fopen(fn, "a");
+ }
+ else
+ {
+ fp = open_output_file(fn, rot->nstsout, "torques");
+
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ if (ISFLEX(rotg))
+ {
+ fprintf(fp, "# Rotation group %d (%s), slab distance %f nm.\n", g, erotg_names[rotg->eType], rotg->slab_dist);
+ fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector.\n");
+ fprintf(fp, "# To obtain the vectorial torque, multiply tau with\n");
+ fprintf(fp, "# rot_vec%d %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
+ fprintf(fp, "#\n");
+ }
+ }
+ fprintf(fp, "# Legend for the following data columns: (tau=torque for that slab):\n");
+ fprintf(fp, "# ");
+ print_aligned_short(fp, "t");
+ print_aligned_short(fp, "grp");
+ print_aligned_short(fp, "slab");
+ print_aligned(fp, "tau");
+ print_aligned_short(fp, "slab");
+ print_aligned(fp, "tau");
+ fprintf(fp, " ...\n");
+ fflush(fp);
+ }
+
+ return fp;
+}
+
+
+static void swap_val(double* vec, int i, int j)
+{
+ double tmp = vec[j];
+
+
+ vec[j] = vec[i];
+ vec[i] = tmp;
+}
+
+
+static void swap_col(double **mat, int i, int j)
+{
+ double tmp[3] = {mat[0][j], mat[1][j], mat[2][j]};
+
+
+ mat[0][j] = mat[0][i];
+ mat[1][j] = mat[1][i];
+ mat[2][j] = mat[2][i];
+
+ mat[0][i] = tmp[0];
+ mat[1][i] = tmp[1];
+ mat[2][i] = tmp[2];
+}
+
+
+/* Eigenvectors are stored in columns of eigen_vec */
+static void diagonalize_symmetric(
+ double **matrix,
+ double **eigen_vec,
+ double eigenval[3])
+{
+ int n_rot;
+
+
+ jacobi(matrix, 3, eigenval, eigen_vec, &n_rot);
+
+ /* sort in ascending order */
+ if (eigenval[0] > eigenval[1])
+ {
+ swap_val(eigenval, 0, 1);
+ swap_col(eigen_vec, 0, 1);
+ }
+ if (eigenval[1] > eigenval[2])
+ {
+ swap_val(eigenval, 1, 2);
+ swap_col(eigen_vec, 1, 2);
+ }
+ if (eigenval[0] > eigenval[1])
+ {
+ swap_val(eigenval, 0, 1);
+ swap_col(eigen_vec, 0, 1);
+ }
+}
+
+
+static void align_with_z(
+ rvec* s, /* Structure to align */
+ int natoms,
+ rvec axis)
+{
+ int i, j, k;
+ rvec zet = {0.0, 0.0, 1.0};
+ rvec rot_axis = {0.0, 0.0, 0.0};
+ rvec *rotated_str = NULL;
+ real ooanorm;
+ real angle;
+ matrix rotmat;
+
+
+ snew(rotated_str, natoms);
+
+ /* Normalize the axis */
+ ooanorm = 1.0/norm(axis);
+ svmul(ooanorm, axis, axis);
+
+ /* Calculate the angle for the fitting procedure */
+ cprod(axis, zet, rot_axis);
+ angle = acos(axis[2]);
+ if (angle < 0.0)
+ {
+ angle += M_PI;
+ }
+
+ /* Calculate the rotation matrix */
+ calc_rotmat(rot_axis, angle*180.0/M_PI, rotmat);
+
+ /* Apply the rotation matrix to s */
+ for (i = 0; i < natoms; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ rotated_str[i][j] += rotmat[j][k]*s[i][k];
+ }
+ }
+ }
+
+ /* Rewrite the rotated structure to s */
+ for (i = 0; i < natoms; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ s[i][j] = rotated_str[i][j];
+ }
+ }
+
+ sfree(rotated_str);
+}
+
+
+static void calc_correl_matrix(rvec* Xstr, rvec* Ystr, double** Rmat, int natoms)
+{
+ int i, j, k;
+
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ Rmat[i][j] = 0.0;
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ for (k = 0; k < natoms; k++)
+ {
+ Rmat[i][j] += Ystr[k][i] * Xstr[k][j];
+ }
+ }
+ }
+}
+
+
+static void weigh_coords(rvec* str, real* weight, int natoms)
+{
+ int i, j;
+
+
+ for (i = 0; i < natoms; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ str[i][j] *= sqrt(weight[i]);
+ }
+ }
+}
+
+
+static real opt_angle_analytic(
+ rvec* ref_s,
+ rvec* act_s,
+ real* weight,
+ int natoms,
+ rvec ref_com,
+ rvec act_com,
+ rvec axis)
+{
+ int i, j, k;
+ rvec *ref_s_1 = NULL;
+ rvec *act_s_1 = NULL;
+ rvec shift;
+ double **Rmat, **RtR, **eigvec;
+ double eigval[3];
+ double V[3][3], WS[3][3];
+ double rot_matrix[3][3];
+ double opt_angle;
+
+
+ /* Do not change the original coordinates */
+ snew(ref_s_1, natoms);
+ snew(act_s_1, natoms);
+ for (i = 0; i < natoms; i++)
+ {
+ copy_rvec(ref_s[i], ref_s_1[i]);
+ copy_rvec(act_s[i], act_s_1[i]);
+ }
+
+ /* Translate the structures to the origin */
+ shift[XX] = -ref_com[XX];
+ shift[YY] = -ref_com[YY];
+ shift[ZZ] = -ref_com[ZZ];
+ translate_x(ref_s_1, natoms, shift);
+
+ shift[XX] = -act_com[XX];
+ shift[YY] = -act_com[YY];
+ shift[ZZ] = -act_com[ZZ];
+ translate_x(act_s_1, natoms, shift);
+
+ /* Align rotation axis with z */
+ align_with_z(ref_s_1, natoms, axis);
+ align_with_z(act_s_1, natoms, axis);
+
+ /* Correlation matrix */
+ Rmat = allocate_square_matrix(3);
+
+ for (i = 0; i < natoms; i++)
+ {
+ ref_s_1[i][2] = 0.0;
+ act_s_1[i][2] = 0.0;
+ }
+
+ /* Weight positions with sqrt(weight) */
+ if (NULL != weight)
+ {
+ weigh_coords(ref_s_1, weight, natoms);
+ weigh_coords(act_s_1, weight, natoms);
+ }
+
+ /* Calculate correlation matrices R=YXt (X=ref_s; Y=act_s) */
+ calc_correl_matrix(ref_s_1, act_s_1, Rmat, natoms);
+
+ /* Calculate RtR */
+ RtR = allocate_square_matrix(3);
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ RtR[i][j] += Rmat[k][i] * Rmat[k][j];
+ }
+ }
+ }
+ /* Diagonalize RtR */
+ snew(eigvec, 3);
+ for (i = 0; i < 3; i++)
+ {
+ snew(eigvec[i], 3);
+ }
+
+ diagonalize_symmetric(RtR, eigvec, eigval);
+ swap_col(eigvec, 0, 1);
+ swap_col(eigvec, 1, 2);
+ swap_val(eigval, 0, 1);
+ swap_val(eigval, 1, 2);
+
+ /* Calculate V */
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ V[i][j] = 0.0;
+ WS[i][j] = 0.0;
+ }
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 2; j++)
+ {
+ WS[i][j] = eigvec[i][j] / sqrt(eigval[j]);
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ V[i][j] += Rmat[i][k]*WS[k][j];
+ }
+ }
+ }
+ free_square_matrix(Rmat, 3);
+
+ /* Calculate optimal rotation matrix */
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ rot_matrix[i][j] = 0.0;
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ for (k = 0; k < 3; k++)
+ {
+ rot_matrix[i][j] += eigvec[i][k]*V[j][k];
+ }
+ }
+ }
+ rot_matrix[2][2] = 1.0;
+
+ /* In some cases abs(rot_matrix[0][0]) can be slighly larger
+ * than unity due to numerical inacurracies. To be able to calculate
+ * the acos function, we put these values back in range. */
+ if (rot_matrix[0][0] > 1.0)
+ {
+ rot_matrix[0][0] = 1.0;
+ }
+ else if (rot_matrix[0][0] < -1.0)
+ {
+ rot_matrix[0][0] = -1.0;
+ }
+
+ /* Determine the optimal rotation angle: */
+ opt_angle = (-1.0)*acos(rot_matrix[0][0])*180.0/M_PI;
+ if (rot_matrix[0][1] < 0.0)
+ {
+ opt_angle = (-1.0)*opt_angle;
+ }
+
+ /* Give back some memory */
+ free_square_matrix(RtR, 3);
+ sfree(ref_s_1);
+ sfree(act_s_1);
+ for (i = 0; i < 3; i++)
+ {
+ sfree(eigvec[i]);
+ }
+ sfree(eigvec);
+
+ return (real) opt_angle;
+}
+
+
+/* Determine angle of the group by RMSD fit to the reference */
+/* Not parallelized, call this routine only on the master */
+static real flex_fit_angle(t_rotgrp *rotg)
+{
+ int i;
+ rvec *fitcoords = NULL;
+ rvec center; /* Center of positions passed to the fit routine */
+ real fitangle; /* Angle of the rotation group derived by fitting */
+ rvec coord;
+ real scal;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg = rotg->enfrotgrp;
+
+ /* Get the center of the rotation group.
+ * Note, again, erg->xc has been sorted in do_flexible */
+ get_center(erg->xc, erg->mc_sorted, rotg->nat, center);
+
+ /* === Determine the optimal fit angle for the rotation group === */
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ /* Normalize every position to it's reference length */
+ for (i = 0; i < rotg->nat; i++)
+ {
+ /* Put the center of the positions into the origin */
+ rvec_sub(erg->xc[i], center, coord);
+ /* Determine the scaling factor for the length: */
+ scal = erg->xc_ref_length[erg->xc_sortind[i]] / norm(coord);
+ /* Get position, multiply with the scaling factor and save */
+ svmul(scal, coord, erg->xc_norm[i]);
+ }
+ fitcoords = erg->xc_norm;
+ }
+ else
+ {
+ fitcoords = erg->xc;
+ }
+ /* From the point of view of the current positions, the reference has rotated
+ * backwards. Since we output the angle relative to the fixed reference,
+ * we need the minus sign. */
+ fitangle = -opt_angle_analytic(erg->xc_ref_sorted, fitcoords, erg->mc_sorted,
+ rotg->nat, erg->xc_ref_center, center, rotg->vec);
+
+ return fitangle;
+}
+
+
+/* Determine actual angle of each slab by RMSD fit to the reference */
+/* Not parallelized, call this routine only on the master */
+static void flex_fit_angle_perslab(
+ int g,
+ t_rotgrp *rotg,
+ double t,
+ real degangle,
+ FILE *fp)
+{
+ int i, l, n, islab, ind;
+ rvec curr_x, ref_x;
+ rvec act_center; /* Center of actual positions that are passed to the fit routine */
+ rvec ref_center; /* Same for the reference positions */
+ real fitangle; /* Angle of a slab derived from an RMSD fit to
+ * the reference structure at t=0 */
+ t_gmx_slabdata *sd;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ real OOm_av; /* 1/average_mass of a rotation group atom */
+ real m_rel; /* Relative mass of a rotation group atom */
+
+
+ erg = rotg->enfrotgrp;
+
+ /* Average mass of a rotation group atom: */
+ OOm_av = erg->invmass*rotg->nat;
+
+ /**********************************/
+ /* First collect the data we need */
+ /**********************************/
+
+ /* Collect the data for the individual slabs */
+ for (n = erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+ sd = &(rotg->enfrotgrp->slab_data[islab]);
+ sd->nat = erg->lastatom[islab]-erg->firstatom[islab]+1;
+ ind = 0;
+
+ /* Loop over the relevant atoms in the slab */
+ for (l = erg->firstatom[islab]; l <= erg->lastatom[islab]; l++)
+ {
+ /* Current position of this atom: x[ii][XX/YY/ZZ] */
+ copy_rvec(erg->xc[l], curr_x);
+
+ /* The (unrotated) reference position of this atom is copied to ref_x.
+ * Beware, the xc coords have been sorted in do_flexible */
+ copy_rvec(erg->xc_ref_sorted[l], ref_x);
+
+ /* Save data for doing angular RMSD fit later */
+ /* Save the current atom position */
+ copy_rvec(curr_x, sd->x[ind]);
+ /* Save the corresponding reference position */
+ copy_rvec(ref_x, sd->ref[ind]);
+
+ /* Maybe also mass-weighting was requested. If yes, additionally
+ * multiply the weights with the relative mass of the atom. If not,
+ * multiply with unity. */
+ m_rel = erg->mc_sorted[l]*OOm_av;
+
+ /* Save the weight for this atom in this slab */
+ sd->weight[ind] = gaussian_weight(curr_x, rotg, n) * m_rel;
+
+ /* Next atom in this slab */
+ ind++;
+ }
+ }
+
+ /******************************/
+ /* Now do the fit calculation */
+ /******************************/
+
+ fprintf(fp, "%12.3e%6d%12.3f", t, g, degangle);
+
+ /* === Now do RMSD fitting for each slab === */
+ /* We require at least SLAB_MIN_ATOMS in a slab, such that the fit makes sense. */
+#define SLAB_MIN_ATOMS 4
+
+ for (n = erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+ sd = &(rotg->enfrotgrp->slab_data[islab]);
+ if (sd->nat >= SLAB_MIN_ATOMS)
+ {
+ /* Get the center of the slabs reference and current positions */
+ get_center(sd->ref, sd->weight, sd->nat, ref_center);
+ get_center(sd->x, sd->weight, sd->nat, act_center);
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ /* Normalize every position to it's reference length
+ * prior to performing the fit */
+ for (i = 0; i < sd->nat; i++) /* Center */
+ {
+ rvec_dec(sd->ref[i], ref_center);
+ rvec_dec(sd->x[i], act_center);
+ /* Normalize x_i such that it gets the same length as ref_i */
+ svmul( norm(sd->ref[i])/norm(sd->x[i]), sd->x[i], sd->x[i] );
+ }
+ /* We already subtracted the centers */
+ clear_rvec(ref_center);
+ clear_rvec(act_center);
+ }
+ fitangle = -opt_angle_analytic(sd->ref, sd->x, sd->weight, sd->nat,
+ ref_center, act_center, rotg->vec);
+ fprintf(fp, "%6d%6d%12.3f", n, sd->nat, fitangle);
+ }
+ }
+ fprintf(fp, "\n");
+
+#undef SLAB_MIN_ATOMS
+}
+
+
+/* Shift x with is */
+static gmx_inline void shift_single_coord(matrix box, rvec x, const ivec is)
+{
+ int tx, ty, tz;
+
+
+ tx = is[XX];
+ ty = is[YY];
+ tz = is[ZZ];
+
+ if (TRICLINIC(box))
+ {
+ x[XX] += tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
+ x[YY] += ty*box[YY][YY]+tz*box[ZZ][YY];
+ x[ZZ] += tz*box[ZZ][ZZ];
+ }
+ else
+ {
+ x[XX] += tx*box[XX][XX];
+ x[YY] += ty*box[YY][YY];
+ x[ZZ] += tz*box[ZZ][ZZ];
+ }
+}
+
+
+/* Determine the 'home' slab of this atom which is the
+ * slab with the highest Gaussian weight of all */
+#define round(a) (int)(a+0.5)
+static gmx_inline int get_homeslab(
+ rvec curr_x, /* The position for which the home slab shall be determined */
+ rvec rotvec, /* The rotation vector */
+ real slabdist) /* The slab distance */
+{
+ real dist;
+
+
+ /* The distance of the atom to the coordinate center (where the
+ * slab with index 0) is */
+ dist = iprod(rotvec, curr_x);
+
+ return round(dist / slabdist);
+}
+
+
+/* For a local atom determine the relevant slabs, i.e. slabs in
+ * which the gaussian is larger than min_gaussian
+ */
+static int get_single_atom_gaussians(
+ rvec curr_x,
+ t_rotgrp *rotg)
+{
+ int slab, homeslab;
+ real g;
+ int count = 0;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg = rotg->enfrotgrp;
+
+ /* Determine the 'home' slab of this atom: */
+ homeslab = get_homeslab(curr_x, rotg->vec, rotg->slab_dist);
+
+ /* First determine the weight in the atoms home slab: */
+ g = gaussian_weight(curr_x, rotg, homeslab);
+
+ erg->gn_atom[count] = g;
+ erg->gn_slabind[count] = homeslab;
+ count++;
+
+
+ /* Determine the max slab */
+ slab = homeslab;
+ while (g > rotg->min_gaussian)
+ {
+ slab++;
+ g = gaussian_weight(curr_x, rotg, slab);
+ erg->gn_slabind[count] = slab;
+ erg->gn_atom[count] = g;
+ count++;
+ }
+ count--;
+
- rvec lastatom, /* Last atom along v */
- int g) /* The rotation group number */
++ /* Determine the min slab */
+ slab = homeslab;
+ do
+ {
+ slab--;
+ g = gaussian_weight(curr_x, rotg, slab);
+ erg->gn_slabind[count] = slab;
+ erg->gn_atom[count] = g;
+ count++;
+ }
+ while (g > rotg->min_gaussian);
+ count--;
+
+ return count;
+}
+
+
+static void flex2_precalc_inner_sum(t_rotgrp *rotg)
+{
+ int i, n, islab;
+ rvec xi; /* positions in the i-sum */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ real gaussian_xi;
+ rvec yi0;
+ rvec rin; /* Helper variables */
+ real fac, fac2;
+ rvec innersumvec;
+ real OOpsii, OOpsiistar;
+ real sin_rin; /* s_ii.r_ii */
+ rvec s_in, tmpvec, tmpvec2;
+ real mi, wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg = rotg->enfrotgrp;
+ N_M = rotg->nat * erg->invmass;
+
+ /* Loop over all slabs that contain something */
+ for (n = erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ /*** D. Calculate the whole inner sum used for second and third sum */
+ /* For slab n, we need to loop over all atoms i again. Since we sorted
+ * the atoms with respect to the rotation vector, we know that it is sufficient
+ * to calculate from firstatom to lastatom only. All other contributions will
+ * be very small. */
+ clear_rvec(innersumvec);
+ for (i = erg->firstatom[islab]; i <= erg->lastatom[islab]; i++)
+ {
+ /* Coordinate xi of this atom */
+ copy_rvec(erg->xc[i], xi);
+
+ /* The i-weights */
+ gaussian_xi = gaussian_weight(xi, rotg, n);
+ mi = erg->mc_sorted[i]; /* need the sorted mass here */
+ wi = N_M*mi;
+
+ /* Calculate rin */
+ copy_rvec(erg->xc_ref_sorted[i], yi0); /* Reference position yi0 */
+ rvec_sub(yi0, ycn, tmpvec2); /* tmpvec2 = yi0 - ycn */
+ mvmul(erg->rotmat, tmpvec2, rin); /* rin = Omega.(yi0 - ycn) */
+
+ /* Calculate psi_i* and sin */
+ rvec_sub(xi, xcn, tmpvec2); /* tmpvec2 = xi - xcn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x (xi - xcn) */
+ OOpsiistar = norm2(tmpvec)+rotg->eps; /* OOpsii* = 1/psii* = |v x (xi-xcn)|^2 + eps */
+ OOpsii = norm(tmpvec); /* OOpsii = 1 / psii = |v x (xi - xcn)| */
+
+ /* * v x (xi - xcn) */
+ unitv(tmpvec, s_in); /* sin = ---------------- */
+ /* |v x (xi - xcn)| */
+
+ sin_rin = iprod(s_in, rin); /* sin_rin = sin . rin */
+
+ /* Now the whole sum */
+ fac = OOpsii/OOpsiistar;
+ svmul(fac, rin, tmpvec);
+ fac2 = fac*fac*OOpsii;
+ svmul(fac2*sin_rin, s_in, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+
+ svmul(wi*gaussian_xi*sin_rin, tmpvec, tmpvec2);
+
+ rvec_inc(innersumvec, tmpvec2);
+ } /* now we have the inner sum, used both for sum2 and sum3 */
+
+ /* Save it to be used in do_flex2_lowlevel */
+ copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
+ } /* END of loop over slabs */
+}
+
+
+static void flex_precalc_inner_sum(t_rotgrp *rotg)
+{
+ int i, n, islab;
+ rvec xi; /* position */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ rvec qin, rin; /* q_i^n and r_i^n */
+ real bin;
+ rvec tmpvec;
+ rvec innersumvec; /* Inner part of sum_n2 */
+ real gaussian_xi; /* Gaussian weight gn(xi) */
+ real mi, wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg = rotg->enfrotgrp;
+ N_M = rotg->nat * erg->invmass;
+
+ /* Loop over all slabs that contain something */
+ for (n = erg->slab_first; n <= erg->slab_last; n++)
+ {
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ /* For slab n, we need to loop over all atoms i again. Since we sorted
+ * the atoms with respect to the rotation vector, we know that it is sufficient
+ * to calculate from firstatom to lastatom only. All other contributions will
+ * be very small. */
+ clear_rvec(innersumvec);
+ for (i = erg->firstatom[islab]; i <= erg->lastatom[islab]; i++)
+ {
+ /* Coordinate xi of this atom */
+ copy_rvec(erg->xc[i], xi);
+
+ /* The i-weights */
+ gaussian_xi = gaussian_weight(xi, rotg, n);
+ mi = erg->mc_sorted[i]; /* need the sorted mass here */
+ wi = N_M*mi;
+
+ /* Calculate rin and qin */
+ rvec_sub(erg->xc_ref_sorted[i], ycn, tmpvec); /* tmpvec = yi0-ycn */
+ mvmul(erg->rotmat, tmpvec, rin); /* rin = Omega.(yi0 - ycn) */
+ cprod(rotg->vec, rin, tmpvec); /* tmpvec = v x Omega*(yi0-ycn) */
+
+ /* * v x Omega*(yi0-ycn) */
+ unitv(tmpvec, qin); /* qin = --------------------- */
+ /* |v x Omega*(yi0-ycn)| */
+
+ /* Calculate bin */
+ rvec_sub(xi, xcn, tmpvec); /* tmpvec = xi-xcn */
+ bin = iprod(qin, tmpvec); /* bin = qin*(xi-xcn) */
+
+ svmul(wi*gaussian_xi*bin, qin, tmpvec);
+
+ /* Add this contribution to the inner sum: */
+ rvec_add(innersumvec, tmpvec, innersumvec);
+ } /* now we have the inner sum vector S^n for this slab */
+ /* Save it to be used in do_flex_lowlevel */
+ copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
+ }
+}
+
+
+static real do_flex2_lowlevel(
+ t_rotgrp *rotg,
+ real sigma, /* The Gaussian width sigma */
+ rvec x[],
+ gmx_bool bOutstepRot,
+ gmx_bool bOutstepSlab,
+ matrix box)
+{
+ int count, ic, ii, j, m, n, islab, iigrp, ifit;
+ rvec xj; /* position in the i-sum */
+ rvec yj0; /* the reference position in the j-sum */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ real V; /* This node's part of the rotation pot. energy */
+ real gaussian_xj; /* Gaussian weight */
+ real beta;
+
+ real numerator, fit_numerator;
+ rvec rjn, fit_rjn; /* Helper variables */
+ real fac, fac2;
+
+ real OOpsij, OOpsijstar;
+ real OOsigma2; /* 1/(sigma^2) */
+ real sjn_rjn;
+ real betasigpsi;
+ rvec sjn, tmpvec, tmpvec2, yj0_ycn;
+ rvec sum1vec_part, sum1vec, sum2vec_part, sum2vec, sum3vec, sum4vec, innersumvec;
+ real sum3, sum4;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ real mj, wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ real Wjn; /* g_n(x_j) m_j / Mjn */
+ gmx_bool bCalcPotFit;
+
+ /* To calculate the torque per slab */
+ rvec slab_force; /* Single force from slab n on one atom */
+ rvec slab_sum1vec_part;
+ real slab_sum3part, slab_sum4part;
+ rvec slab_sum1vec, slab_sum2vec, slab_sum3vec, slab_sum4vec;
+
+
+ erg = rotg->enfrotgrp;
+
+ /* Pre-calculate the inner sums, so that we do not have to calculate
+ * them again for every atom */
+ flex2_precalc_inner_sum(rotg);
+
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
+
+ /********************************************************/
+ /* Main loop over all local atoms of the rotation group */
+ /********************************************************/
+ N_M = rotg->nat * erg->invmass;
+ V = 0.0;
+ OOsigma2 = 1.0 / (sigma*sigma);
+ for (j = 0; j < erg->nat_loc; j++)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp]; /* need the unsorted mass here */
+ wj = N_M*mj;
+
+ /* Current position of this atom: x[ii][XX/YY/ZZ]
+ * Note that erg->xc_center contains the center of mass in case the flex2-t
+ * potential was chosen. For the flex2 potential erg->xc_center must be
+ * zero. */
+ rvec_sub(x[ii], erg->xc_center, xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* Determine the slabs to loop over, i.e. the ones with contributions
+ * larger than min_gaussian */
+ count = get_single_atom_gaussians(xj, rotg);
+
+ clear_rvec(sum1vec_part);
+ clear_rvec(sum2vec_part);
+ sum3 = 0.0;
+ sum4 = 0.0;
+ /* Loop over the relevant slabs for this atom */
+ for (ic = 0; ic < count; ic++)
+ {
+ n = erg->gn_slabind[ic];
+
+ /* Get the precomputed Gaussian value of curr_slab for curr_x */
+ gaussian_xj = erg->gn_atom[ic];
+
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The (unrotated) reference position of this atom is copied to yj0: */
+ copy_rvec(rotg->x_ref[iigrp], yj0);
+
+ beta = calc_beta(xj, rotg, n);
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ rvec_sub(yj0, ycn, yj0_ycn); /* yj0_ycn = yj0 - ycn */
+
+ /* Rotate: */
+ mvmul(erg->rotmat, yj0_ycn, rjn); /* rjn = Omega.(yj0 - ycn) */
+
+ /* Subtract the slab center from xj */
+ rvec_sub(xj, xcn, tmpvec2); /* tmpvec2 = xj - xcn */
++
++ /* In rare cases, when an atom position coincides with a slab center
++ * (tmpvec2 == 0) we cannot compute the vector product for sjn.
++ * However, since the atom is located directly on the pivot, this
++ * slab's contribution to the force on that atom will be zero
++ * anyway. Therefore, we directly move on to the next slab. */
++ if ( 0 == norm(tmpvec2) )
++ {
++ continue;
++ }
+
+ /* Calculate sjn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x (xj - xcn) */
+
+ OOpsijstar = norm2(tmpvec)+rotg->eps; /* OOpsij* = 1/psij* = |v x (xj-xcn)|^2 + eps */
+
+ numerator = sqr(iprod(tmpvec, rjn));
+
+ /*********************************/
+ /* Add to the rotation potential */
+ /*********************************/
+ V += 0.5*rotg->k*wj*gaussian_xj*numerator/OOpsijstar;
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, fit_rjn);
+ fit_numerator = sqr(iprod(tmpvec, fit_rjn));
+ erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*fit_numerator/OOpsijstar;
+ }
+ }
+
+ /*************************************/
+ /* Now calculate the force on atom j */
+ /*************************************/
+
+ OOpsij = norm(tmpvec); /* OOpsij = 1 / psij = |v x (xj - xcn)| */
+
+ /* * v x (xj - xcn) */
+ unitv(tmpvec, sjn); /* sjn = ---------------- */
+ /* |v x (xj - xcn)| */
+
+ sjn_rjn = iprod(sjn, rjn); /* sjn_rjn = sjn . rjn */
+
+
+ /*** A. Calculate the first of the four sum terms: ****************/
+ fac = OOpsij/OOpsijstar;
+ svmul(fac, rjn, tmpvec);
+ fac2 = fac*fac*OOpsij;
+ svmul(fac2*sjn_rjn, sjn, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+ fac2 = wj*gaussian_xj; /* also needed for sum4 */
+ svmul(fac2*sjn_rjn, tmpvec, slab_sum1vec_part);
+ /********************/
+ /*** Add to sum1: ***/
+ /********************/
+ rvec_inc(sum1vec_part, slab_sum1vec_part); /* sum1 still needs to vector multiplied with v */
+
+ /*** B. Calculate the forth of the four sum terms: ****************/
+ betasigpsi = beta*OOsigma2*OOpsij; /* this is also needed for sum3 */
+ /********************/
+ /*** Add to sum4: ***/
+ /********************/
+ slab_sum4part = fac2*betasigpsi*fac*sjn_rjn*sjn_rjn; /* Note that fac is still valid from above */
+ sum4 += slab_sum4part;
+
+ /*** C. Calculate Wjn for second and third sum */
+ /* Note that we can safely divide by slab_weights since we check in
+ * get_slab_centers that it is non-zero. */
+ Wjn = gaussian_xj*mj/erg->slab_weights[islab];
+
+ /* We already have precalculated the inner sum for slab n */
+ copy_rvec(erg->slab_innersumvec[islab], innersumvec);
+
+ /* Weigh the inner sum vector with Wjn */
+ svmul(Wjn, innersumvec, innersumvec);
+
+ /*** E. Calculate the second of the four sum terms: */
+ /********************/
+ /*** Add to sum2: ***/
+ /********************/
+ rvec_inc(sum2vec_part, innersumvec); /* sum2 still needs to be vector crossproduct'ed with v */
+
+ /*** F. Calculate the third of the four sum terms: */
+ slab_sum3part = betasigpsi * iprod(sjn, innersumvec);
+ sum3 += slab_sum3part; /* still needs to be multiplied with v */
+
+ /*** G. Calculate the torque on the local slab's axis: */
+ if (bOutstepRot)
+ {
+ /* Sum1 */
+ cprod(slab_sum1vec_part, rotg->vec, slab_sum1vec);
+ /* Sum2 */
+ cprod(innersumvec, rotg->vec, slab_sum2vec);
+ /* Sum3 */
+ svmul(slab_sum3part, rotg->vec, slab_sum3vec);
+ /* Sum4 */
+ svmul(slab_sum4part, rotg->vec, slab_sum4vec);
+
+ /* The force on atom ii from slab n only: */
+ for (m = 0; m < DIM; m++)
+ {
+ slab_force[m] = rotg->k * (-slab_sum1vec[m] + slab_sum2vec[m] - slab_sum3vec[m] + 0.5*slab_sum4vec[m]);
+ }
+
+ erg->slab_torque_v[islab] += torque(rotg->vec, slab_force, xj, xcn);
+ }
+ } /* END of loop over slabs */
+
+ /* Construct the four individual parts of the vector sum: */
+ cprod(sum1vec_part, rotg->vec, sum1vec); /* sum1vec = { } x v */
+ cprod(sum2vec_part, rotg->vec, sum2vec); /* sum2vec = { } x v */
+ svmul(sum3, rotg->vec, sum3vec); /* sum3vec = { } . v */
+ svmul(sum4, rotg->vec, sum4vec); /* sum4vec = { } . v */
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ for (m = 0; m < DIM; m++)
+ {
+ erg->f_rot_loc[j][m] = rotg->k * (-sum1vec[m] + sum2vec[m] - sum3vec[m] + 0.5*sum4vec[m]);
+ }
+
+#ifdef SUM_PARTS
+ fprintf(stderr, "sum1: %15.8f %15.8f %15.8f\n", -rotg->k*sum1vec[XX], -rotg->k*sum1vec[YY], -rotg->k*sum1vec[ZZ]);
+ fprintf(stderr, "sum2: %15.8f %15.8f %15.8f\n", rotg->k*sum2vec[XX], rotg->k*sum2vec[YY], rotg->k*sum2vec[ZZ]);
+ fprintf(stderr, "sum3: %15.8f %15.8f %15.8f\n", -rotg->k*sum3vec[XX], -rotg->k*sum3vec[YY], -rotg->k*sum3vec[ZZ]);
+ fprintf(stderr, "sum4: %15.8f %15.8f %15.8f\n", 0.5*rotg->k*sum4vec[XX], 0.5*rotg->k*sum4vec[YY], 0.5*rotg->k*sum4vec[ZZ]);
+#endif
+
+ PRINT_FORCE_J
+
+ } /* END of loop over local atoms */
+
+ return V;
+}
+
+
+static real do_flex_lowlevel(
+ t_rotgrp *rotg,
+ real sigma, /* The Gaussian width sigma */
+ rvec x[],
+ gmx_bool bOutstepRot,
+ gmx_bool bOutstepSlab,
+ matrix box)
+{
+ int count, ic, ifit, ii, j, m, n, islab, iigrp;
+ rvec xj, yj0; /* current and reference position */
+ rvec xcn, ycn; /* the current and the reference slab centers */
+ rvec yj0_ycn; /* yj0 - ycn */
+ rvec xj_xcn; /* xj - xcn */
+ rvec qjn, fit_qjn; /* q_i^n */
+ rvec sum_n1, sum_n2; /* Two contributions to the rotation force */
+ rvec innersumvec; /* Inner part of sum_n2 */
+ rvec s_n;
+ rvec force_n; /* Single force from slab n on one atom */
+ rvec force_n1, force_n2; /* First and second part of force_n */
+ rvec tmpvec, tmpvec2, tmp_f; /* Helper variables */
+ real V; /* The rotation potential energy */
+ real OOsigma2; /* 1/(sigma^2) */
+ real beta; /* beta_n(xj) */
+ real bjn, fit_bjn; /* b_j^n */
+ real gaussian_xj; /* Gaussian weight gn(xj) */
+ real betan_xj_sigma2;
+ real mj, wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ gmx_bool bCalcPotFit;
+
+
+ erg = rotg->enfrotgrp;
+
+ /* Pre-calculate the inner sums, so that we do not have to calculate
+ * them again for every atom */
+ flex_precalc_inner_sum(rotg);
+
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
+
+ /********************************************************/
+ /* Main loop over all local atoms of the rotation group */
+ /********************************************************/
+ OOsigma2 = 1.0/(sigma*sigma);
+ N_M = rotg->nat * erg->invmass;
+ V = 0.0;
+ for (j = 0; j < erg->nat_loc; j++)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp]; /* need the unsorted mass here */
+ wj = N_M*mj;
+
+ /* Current position of this atom: x[ii][XX/YY/ZZ]
+ * Note that erg->xc_center contains the center of mass in case the flex-t
+ * potential was chosen. For the flex potential erg->xc_center must be
+ * zero. */
+ rvec_sub(x[ii], erg->xc_center, xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* Determine the slabs to loop over, i.e. the ones with contributions
+ * larger than min_gaussian */
+ count = get_single_atom_gaussians(xj, rotg);
+
+ clear_rvec(sum_n1);
+ clear_rvec(sum_n2);
+
+ /* Loop over the relevant slabs for this atom */
+ for (ic = 0; ic < count; ic++)
+ {
+ n = erg->gn_slabind[ic];
+
+ /* Get the precomputed Gaussian for xj in slab n */
+ gaussian_xj = erg->gn_atom[ic];
+
+ islab = n - erg->slab_first; /* slab index */
+
+ /* The (unrotated) reference position of this atom is saved in yj0: */
+ copy_rvec(rotg->x_ref[iigrp], yj0);
+
+ beta = calc_beta(xj, rotg, n);
+
+ /* The current center of this slab is saved in xcn: */
+ copy_rvec(erg->slab_center[islab], xcn);
+ /* ... and the reference center in ycn: */
+ copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
+
+ rvec_sub(yj0, ycn, yj0_ycn); /* yj0_ycn = yj0 - ycn */
+
+ /* Rotate: */
+ mvmul(erg->rotmat, yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
+
+ /* Subtract the slab center from xj */
+ rvec_sub(xj, xcn, xj_xcn); /* xj_xcn = xj - xcn */
+
++ /* In rare cases, when an atom position coincides with a slab center
++ * (xj_xcn == 0) we cannot compute the vector product for qjn.
++ * However, since the atom is located directly on the pivot, this
++ * slab's contribution to the force on that atom will be zero
++ * anyway. Therefore, we directly move on to the next slab. */
++ if ( 0 == norm(xj_xcn) )
++ {
++ continue;
++ }
++
+ /* Calculate qjn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
+
+ /* * v x Omega.(yj0-ycn) */
+ unitv(tmpvec, qjn); /* qjn = --------------------- */
+ /* |v x Omega.(yj0-ycn)| */
+
+ bjn = iprod(qjn, xj_xcn); /* bjn = qjn * (xj - xcn) */
+
+ /*********************************/
+ /* Add to the rotation potential */
+ /*********************************/
+ V += 0.5*rotg->k*wj*gaussian_xj*sqr(bjn);
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ /* As above calculate Omega.(yj0-ycn), now for the other angles */
+ mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
+ /* As above calculate qjn */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
+ /* * v x Omega.(yj0-ycn) */
+ unitv(tmpvec, fit_qjn); /* fit_qjn = --------------------- */
+ /* |v x Omega.(yj0-ycn)| */
+ fit_bjn = iprod(fit_qjn, xj_xcn); /* fit_bjn = fit_qjn * (xj - xcn) */
+ /* Add to the rotation potential for this angle */
+ erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*sqr(fit_bjn);
+ }
+ }
+
+ /****************************************************************/
+ /* sum_n1 will typically be the main contribution to the force: */
+ /****************************************************************/
+ betan_xj_sigma2 = beta*OOsigma2; /* beta_n(xj)/sigma^2 */
+
+ /* The next lines calculate
+ * qjn - (bjn*beta(xj)/(2sigma^2))v */
+ svmul(bjn*0.5*betan_xj_sigma2, rotg->vec, tmpvec2);
+ rvec_sub(qjn, tmpvec2, tmpvec);
+
+ /* Multiply with gn(xj)*bjn: */
+ svmul(gaussian_xj*bjn, tmpvec, tmpvec2);
+
+ /* Sum over n: */
+ rvec_inc(sum_n1, tmpvec2);
+
+ /* We already have precalculated the Sn term for slab n */
+ copy_rvec(erg->slab_innersumvec[islab], s_n);
+ /* * beta_n(xj) */
+ svmul(betan_xj_sigma2*iprod(s_n, xj_xcn), rotg->vec, tmpvec); /* tmpvec = ---------- s_n (xj-xcn) */
+ /* sigma^2 */
+
+ rvec_sub(s_n, tmpvec, innersumvec);
+
+ /* We can safely divide by slab_weights since we check in get_slab_centers
+ * that it is non-zero. */
+ svmul(gaussian_xj/erg->slab_weights[islab], innersumvec, innersumvec);
+
+ rvec_add(sum_n2, innersumvec, sum_n2);
+
+ /* Calculate the torque: */
+ if (bOutstepRot)
+ {
+ /* The force on atom ii from slab n only: */
+ svmul(-rotg->k*wj, tmpvec2, force_n1); /* part 1 */
+ svmul( rotg->k*mj, innersumvec, force_n2); /* part 2 */
+ rvec_add(force_n1, force_n2, force_n);
+ erg->slab_torque_v[islab] += torque(rotg->vec, force_n, xj, xcn);
+ }
+ } /* END of loop over slabs */
+
+ /* Put both contributions together: */
+ svmul(wj, sum_n1, sum_n1);
+ svmul(mj, sum_n2, sum_n2);
+ rvec_sub(sum_n2, sum_n1, tmp_f); /* F = -grad V */
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ for (m = 0; m < DIM; m++)
+ {
+ erg->f_rot_loc[j][m] = rotg->k*tmp_f[m];
+ }
+
+ PRINT_FORCE_J
+
+ } /* END of loop over local atoms */
+
+ return V;
+}
+
+#ifdef PRINT_COORDS
+static void print_coordinates(t_rotgrp *rotg, rvec x[], matrix box, int step)
+{
+ int i;
+ static FILE *fp;
+ static char buf[STRLEN];
+ static gmx_bool bFirst = 1;
+
+
+ if (bFirst)
+ {
+ sprintf(buf, "coords%d.txt", cr->nodeid);
+ fp = fopen(buf, "w");
+ bFirst = 0;
+ }
+
+ fprintf(fp, "\nStep %d\n", step);
+ fprintf(fp, "box: %f %f %f %f %f %f %f %f %f\n",
+ box[XX][XX], box[XX][YY], box[XX][ZZ],
+ box[YY][XX], box[YY][YY], box[YY][ZZ],
+ box[ZZ][XX], box[ZZ][ZZ], box[ZZ][ZZ]);
+ for (i = 0; i < rotg->nat; i++)
+ {
+ fprintf(fp, "%4d %f %f %f\n", i,
+ erg->xc[i][XX], erg->xc[i][YY], erg->xc[i][ZZ]);
+ }
+ fflush(fp);
+
+}
+#endif
+
+
+static int projection_compare(const void *a, const void *b)
+{
+ sort_along_vec_t *xca, *xcb;
+
+
+ xca = (sort_along_vec_t *)a;
+ xcb = (sort_along_vec_t *)b;
+
+ if (xca->xcproj < xcb->xcproj)
+ {
+ return -1;
+ }
+ else if (xca->xcproj > xcb->xcproj)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+static void sort_collective_coordinates(
+ t_rotgrp *rotg, /* Rotation group */
+ sort_along_vec_t *data) /* Buffer for sorting the positions */
+{
+ int i;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg = rotg->enfrotgrp;
+
+ /* The projection of the position vector on the rotation vector is
+ * the relevant value for sorting. Fill the 'data' structure */
+ for (i = 0; i < rotg->nat; i++)
+ {
+ data[i].xcproj = iprod(erg->xc[i], rotg->vec); /* sort criterium */
+ data[i].m = erg->mc[i];
+ data[i].ind = i;
+ copy_rvec(erg->xc[i], data[i].x );
+ copy_rvec(rotg->x_ref[i], data[i].x_ref);
+ }
+ /* Sort the 'data' structure */
+ gmx_qsort(data, rotg->nat, sizeof(sort_along_vec_t), projection_compare);
+
+ /* Copy back the sorted values */
+ for (i = 0; i < rotg->nat; i++)
+ {
+ copy_rvec(data[i].x, erg->xc[i] );
+ copy_rvec(data[i].x_ref, erg->xc_ref_sorted[i]);
+ erg->mc_sorted[i] = data[i].m;
+ erg->xc_sortind[i] = data[i].ind;
+ }
+}
+
+
+/* For each slab, get the first and the last index of the sorted atom
+ * indices */
+static void get_firstlast_atom_per_slab(t_rotgrp *rotg)
+{
+ int i, islab, n;
+ real beta;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg = rotg->enfrotgrp;
+
+ /* Find the first atom that needs to enter the calculation for each slab */
+ n = erg->slab_first; /* slab */
+ i = 0; /* start with the first atom */
+ do
+ {
+ /* Find the first atom that significantly contributes to this slab */
+ do /* move forward in position until a large enough beta is found */
+ {
+ beta = calc_beta(erg->xc[i], rotg, n);
+ i++;
+ }
+ while ((beta < -erg->max_beta) && (i < rotg->nat));
+ i--;
+ islab = n - erg->slab_first; /* slab index */
+ erg->firstatom[islab] = i;
+ /* Proceed to the next slab */
+ n++;
+ }
+ while (n <= erg->slab_last);
+
+ /* Find the last atom for each slab */
+ n = erg->slab_last; /* start with last slab */
+ i = rotg->nat-1; /* start with the last atom */
+ do
+ {
+ do /* move backward in position until a large enough beta is found */
+ {
+ beta = calc_beta(erg->xc[i], rotg, n);
+ i--;
+ }
+ while ((beta > erg->max_beta) && (i > -1));
+ i++;
+ islab = n - erg->slab_first; /* slab index */
+ erg->lastatom[islab] = i;
+ /* Proceed to the next slab */
+ n--;
+ }
+ while (n >= erg->slab_first);
+}
+
+
+/* Determine the very first and very last slab that needs to be considered
+ * For the first slab that needs to be considered, we have to find the smallest
+ * n that obeys:
+ *
+ * x_first * v - n*Delta_x <= beta_max
+ *
+ * slab index n, slab distance Delta_x, rotation vector v. For the last slab we
+ * have to find the largest n that obeys
+ *
+ * x_last * v - n*Delta_x >= -beta_max
+ *
+ */
+static gmx_inline int get_first_slab(
+ t_rotgrp *rotg, /* The rotation group (inputrec data) */
+ real max_beta, /* The max_beta value, instead of min_gaussian */
+ rvec firstatom) /* First atom after sorting along the rotation vector v */
+{
+ /* Find the first slab for the first atom */
+ return ceil((iprod(firstatom, rotg->vec) - max_beta)/rotg->slab_dist);
+}
+
+
+static gmx_inline int get_last_slab(
+ t_rotgrp *rotg, /* The rotation group (inputrec data) */
+ real max_beta, /* The max_beta value, instead of min_gaussian */
+ rvec lastatom) /* Last atom along v */
+{
+ /* Find the last slab for the last atom */
+ return floor((iprod(lastatom, rotg->vec) + max_beta)/rotg->slab_dist);
+}
+
+
+static void get_firstlast_slab_check(
+ t_rotgrp *rotg, /* The rotation group (inputrec data) */
+ t_gmx_enfrotgrp *erg, /* The rotation group (data only accessible in this file) */
+ rvec firstatom, /* First atom after sorting along the rotation vector v */
- gmx_large_int_t step, /* The time step */
++ rvec lastatom) /* Last atom along v */
+{
+ erg->slab_first = get_first_slab(rotg, erg->max_beta, firstatom);
+ erg->slab_last = get_last_slab(rotg, erg->max_beta, lastatom);
+
++ /* Calculate the slab buffer size, which changes when slab_first changes */
++ erg->slab_buffer = erg->slab_first - erg->slab_first_ref;
++
+ /* Check whether we have reference data to compare against */
+ if (erg->slab_first < erg->slab_first_ref)
+ {
+ gmx_fatal(FARGS, "%s No reference data for first slab (n=%d), unable to proceed.",
+ RotStr, erg->slab_first);
+ }
+
+ /* Check whether we have reference data to compare against */
+ if (erg->slab_last > erg->slab_last_ref)
+ {
+ gmx_fatal(FARGS, "%s No reference data for last slab (n=%d), unable to proceed.",
+ RotStr, erg->slab_last);
+ }
+}
+
+
+/* Enforced rotation with a flexible axis */
+static void do_flexible(
+ gmx_bool bMaster,
+ gmx_enfrot_t enfrot, /* Other rotation data */
+ t_rotgrp *rotg, /* The rotation group */
+ int g, /* Group number */
+ rvec x[], /* The local positions */
+ matrix box,
+ double t, /* Time in picoseconds */
- get_firstlast_slab_check(rotg, erg, erg->xc[0], erg->xc[rotg->nat-1], g);
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int l, nslabs;
+ real sigma; /* The Gaussian width sigma */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ erg = rotg->enfrotgrp;
+
+ /* Define the sigma value */
+ sigma = 0.7*rotg->slab_dist;
+
+ /* Sort the collective coordinates erg->xc along the rotation vector. This is
+ * an optimization for the inner loop. */
+ sort_collective_coordinates(rotg, enfrot->data);
+
+ /* Determine the first relevant slab for the first atom and the last
+ * relevant slab for the last atom */
- rvec x[], /* The positions */
- matrix box, /* The simulation box */
- double t, /* Time in picoseconds */
- gmx_large_int_t step, /* The time step */
++ get_firstlast_slab_check(rotg, erg, erg->xc[0], erg->xc[rotg->nat-1]);
+
+ /* Determine for each slab depending on the min_gaussian cutoff criterium,
+ * a first and a last atom index inbetween stuff needs to be calculated */
+ get_firstlast_atom_per_slab(rotg);
+
+ /* Determine the gaussian-weighted center of positions for all slabs */
+ get_slab_centers(rotg, erg->xc, erg->mc_sorted, g, t, enfrot->out_slabs, bOutstepSlab, FALSE);
+
+ /* Clear the torque per slab from last time step: */
+ nslabs = erg->slab_last - erg->slab_first + 1;
+ for (l = 0; l < nslabs; l++)
+ {
+ erg->slab_torque_v[l] = 0.0;
+ }
+
+ /* Call the rotational forces kernel */
+ if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEXT)
+ {
+ erg->V = do_flex_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
+ }
+ else if (rotg->eType == erotgFLEX2 || rotg->eType == erotgFLEX2T)
+ {
+ erg->V = do_flex2_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
+ }
+ else
+ {
+ gmx_fatal(FARGS, "Unknown flexible rotation type");
+ }
+
+ /* Determine angle by RMSD fit to the reference - Let's hope this */
+ /* only happens once in a while, since this is not parallelized! */
+ if (bMaster && (erotgFitPOT != rotg->eFittype) )
+ {
+ if (bOutstepRot)
+ {
+ /* Fit angle of the whole rotation group */
+ erg->angle_v = flex_fit_angle(rotg);
+ }
+ if (bOutstepSlab)
+ {
+ /* Fit angle of each slab */
+ flex_fit_angle_perslab(g, rotg, t, erg->degangle, enfrot->out_angles);
+ }
+ }
+
+ /* Lump together the torques from all slabs: */
+ erg->torque_v = 0.0;
+ for (l = 0; l < nslabs; l++)
+ {
+ erg->torque_v += erg->slab_torque_v[l];
+ }
+}
+
+
+/* Calculate the angle between reference and actual rotation group atom,
+ * both projected into a plane perpendicular to the rotation vector: */
+static void angle(t_rotgrp *rotg,
+ rvec x_act,
+ rvec x_ref,
+ real *alpha,
+ real *weight) /* atoms near the rotation axis should count less than atoms far away */
+{
+ rvec xp, xrp; /* current and reference positions projected on a plane perpendicular to pg->vec */
+ rvec dum;
+
+
+ /* Project x_ref and x into a plane through the origin perpendicular to rot_vec: */
+ /* Project x_ref: xrp = x_ref - (vec * x_ref) * vec */
+ svmul(iprod(rotg->vec, x_ref), rotg->vec, dum);
+ rvec_sub(x_ref, dum, xrp);
+ /* Project x_act: */
+ svmul(iprod(rotg->vec, x_act), rotg->vec, dum);
+ rvec_sub(x_act, dum, xp);
+
+ /* Retrieve information about which vector precedes. gmx_angle always
+ * returns a positive angle. */
+ cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
+
+ if (iprod(rotg->vec, dum) >= 0)
+ {
+ *alpha = -gmx_angle(xrp, xp);
+ }
+ else
+ {
+ *alpha = +gmx_angle(xrp, xp);
+ }
+
+ /* Also return the weight */
+ *weight = norm(xp);
+}
+
+
+/* Project first vector onto a plane perpendicular to the second vector
+ * dr = dr - (dr.v)v
+ * Note that v must be of unit length.
+ */
+static gmx_inline void project_onto_plane(rvec dr, const rvec v)
+{
+ rvec tmp;
+
+
+ svmul(iprod(dr, v), v, tmp); /* tmp = (dr.v)v */
+ rvec_dec(dr, tmp); /* dr = dr - (dr.v)v */
+}
+
+
+/* Fixed rotation: The rotation reference group rotates around the v axis. */
+/* The atoms of the actual rotation group are attached with imaginary */
+/* springs to the reference atoms. */
+static void do_fixed(
+ t_rotgrp *rotg, /* The rotation group */
- rvec x[], /* The positions */
- matrix box, /* The simulation box */
- double t, /* Time in picoseconds */
- gmx_large_int_t step, /* The time step */
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int ifit, j, jj, m;
+ rvec dr;
+ rvec tmp_f; /* Force */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xi_xc; /* xi - xc */
+ gmx_bool bCalcPotFit;
+ rvec fit_xr_loc;
+
+ /* for mass weighting: */
+ real wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ real k_wi; /* k times wi */
+
+ gmx_bool bProject;
+
+
+ erg = rotg->enfrotgrp;
+ bProject = (rotg->eType == erotgPM) || (rotg->eType == erotgPMPF);
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Each process calculates the forces on its local atoms */
+ for (j = 0; j < erg->nat_loc; j++)
+ {
+ /* Calculate (x_i-x_c) resp. (x_i-u) */
+ rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xi_xc);
+
+ /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
+ rvec_sub(erg->xr_loc[j], xi_xc, dr);
+
+ if (bProject)
+ {
+ project_onto_plane(dr, rotg->vec);
+ }
+
+ /* Mass-weighting */
+ wi = N_M*erg->m_loc[j];
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ k_wi = rotg->k*wi;
+ for (m = 0; m < DIM; m++)
+ {
+ tmp_f[m] = k_wi*dr[m];
+ erg->f_rot_loc[j][m] = tmp_f[m];
+ erg->V += 0.5*k_wi*sqr(dr[m]);
+ }
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ /* Index of this rotation group atom with respect to the whole rotation group */
+ jj = erg->xc_ref_ind[j];
+
+ /* Rotate with the alternative angle. Like rotate_local_reference(),
+ * just for a single local atom */
+ mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_xr_loc); /* fit_xr_loc = Omega*(y_i-y_c) */
+
+ /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
+ rvec_sub(fit_xr_loc, xi_xc, dr);
+
+ if (bProject)
+ {
+ project_onto_plane(dr, rotg->vec);
+ }
+
+ /* Add to the rotation potential for this angle: */
+ erg->PotAngleFit->V[ifit] += 0.5*k_wi*norm2(dr);
+ }
+ }
+
+ if (bOutstepRot)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xi_xc, erg->xr_loc[j], &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+ /* If you want enforced rotation to contribute to the virial,
+ * activate the following lines:
+ if (MASTER(cr))
+ {
+ Add the rotation contribution to the virial
+ for(j=0; j<DIM; j++)
+ for(m=0;m<DIM;m++)
+ vir[j][m] += 0.5*f[ii][j]*dr[m];
+ }
+ */
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+}
+
+
+/* Calculate the radial motion potential and forces */
+static void do_radial_motion(
+ t_rotgrp *rotg, /* The rotation group */
- double t, /* Time in picoseconds */
- gmx_large_int_t step, /* The time step */
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int j, jj, ifit;
+ rvec tmp_f; /* Force */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xj_u; /* xj - u */
+ rvec tmpvec, fit_tmpvec;
+ real fac, fac2, sum = 0.0;
+ rvec pj;
+ gmx_bool bCalcPotFit;
+
+ /* For mass weighting: */
+ real wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+
+
+ erg = rotg->enfrotgrp;
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Each process calculates the forces on its local atoms */
+ for (j = 0; j < erg->nat_loc; j++)
+ {
+ /* Calculate (xj-u) */
+ rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xj_u); /* xj_u = xj-u */
+
+ /* Calculate Omega.(yj0-u) */
+ cprod(rotg->vec, erg->xr_loc[j], tmpvec); /* tmpvec = v x Omega.(yj0-u) */
+
+ /* * v x Omega.(yj0-u) */
+ unitv(tmpvec, pj); /* pj = --------------------- */
+ /* | v x Omega.(yj0-u) | */
+
+ fac = iprod(pj, xj_u); /* fac = pj.(xj-u) */
+ fac2 = fac*fac;
+
+ /* Mass-weighting */
+ wj = N_M*erg->m_loc[j];
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ svmul(-rotg->k*wj*fac, pj, tmp_f);
+ copy_rvec(tmp_f, erg->f_rot_loc[j]);
+ sum += wj*fac2;
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ /* Index of this rotation group atom with respect to the whole rotation group */
+ jj = erg->xc_ref_ind[j];
+
+ /* Rotate with the alternative angle. Like rotate_local_reference(),
+ * just for a single local atom */
+ mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_tmpvec); /* fit_tmpvec = Omega*(yj0-u) */
+
+ /* Calculate Omega.(yj0-u) */
+ cprod(rotg->vec, fit_tmpvec, tmpvec); /* tmpvec = v x Omega.(yj0-u) */
+ /* * v x Omega.(yj0-u) */
+ unitv(tmpvec, pj); /* pj = --------------------- */
+ /* | v x Omega.(yj0-u) | */
+
+ fac = iprod(pj, xj_u); /* fac = pj.(xj-u) */
+ fac2 = fac*fac;
+
+ /* Add to the rotation potential for this angle: */
+ erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
+ }
+ }
+
+ if (bOutstepRot)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xj_u, erg->xr_loc[j], &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+ erg->V = 0.5*rotg->k*sum;
+}
+
+
+/* Calculate the radial motion pivot-free potential and forces */
+static void do_radial_motion_pf(
+ t_rotgrp *rotg, /* The rotation group */
+ rvec x[], /* The positions */
+ matrix box, /* The simulation box */
- double t, /* Time in picoseconds */
- gmx_large_int_t step, /* The time step */
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int i, ii, iigrp, ifit, j;
+ rvec xj; /* Current position */
+ rvec xj_xc; /* xj - xc */
+ rvec yj0_yc0; /* yj0 - yc0 */
+ rvec tmp_f; /* Force */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec tmpvec, tmpvec2;
+ rvec innersumvec; /* Precalculation of the inner sum */
+ rvec innersumveckM;
+ real fac, fac2, V = 0.0;
+ rvec qi, qj;
+ gmx_bool bCalcPotFit;
+
+ /* For mass weighting: */
+ real mj, wi, wj; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+
+
+ erg = rotg->enfrotgrp;
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Get the current center of the rotation group: */
+ get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+
+ /* Precalculate Sum_i [ wi qi.(xi-xc) qi ] which is needed for every single j */
+ clear_rvec(innersumvec);
+ for (i = 0; i < rotg->nat; i++)
+ {
+ /* Mass-weighting */
+ wi = N_M*erg->mc[i];
+
+ /* Calculate qi. Note that xc_ref_center has already been subtracted from
+ * x_ref in init_rot_group.*/
+ mvmul(erg->rotmat, rotg->x_ref[i], tmpvec); /* tmpvec = Omega.(yi0-yc0) */
+
+ cprod(rotg->vec, tmpvec, tmpvec2); /* tmpvec2 = v x Omega.(yi0-yc0) */
+
+ /* * v x Omega.(yi0-yc0) */
+ unitv(tmpvec2, qi); /* qi = ----------------------- */
+ /* | v x Omega.(yi0-yc0) | */
+
+ rvec_sub(erg->xc[i], erg->xc_center, tmpvec); /* tmpvec = xi-xc */
+
+ svmul(wi*iprod(qi, tmpvec), qi, tmpvec2);
+
+ rvec_inc(innersumvec, tmpvec2);
+ }
+ svmul(rotg->k*erg->invmass, innersumvec, innersumveckM);
+
+ /* Each process calculates the forces on its local atoms */
+ for (j = 0; j < erg->nat_loc; j++)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp]; /* need the unsorted mass here */
+ wj = N_M*mj;
+
+ /* Current position of this atom: x[ii][XX/YY/ZZ] */
+ copy_rvec(x[ii], xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* The (unrotated) reference position is yj0. yc0 has already
+ * been subtracted in init_rot_group */
+ copy_rvec(rotg->x_ref[iigrp], yj0_yc0); /* yj0_yc0 = yj0 - yc0 */
+
+ /* Calculate Omega.(yj0-yc0) */
+ mvmul(erg->rotmat, yj0_yc0, tmpvec2); /* tmpvec2 = Omega.(yj0 - yc0) */
+
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x Omega.(yj0-yc0) */
+
+ /* * v x Omega.(yj0-yc0) */
+ unitv(tmpvec, qj); /* qj = ----------------------- */
+ /* | v x Omega.(yj0-yc0) | */
+
+ /* Calculate (xj-xc) */
+ rvec_sub(xj, erg->xc_center, xj_xc); /* xj_xc = xj-xc */
+
+ fac = iprod(qj, xj_xc); /* fac = qj.(xj-xc) */
+ fac2 = fac*fac;
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ svmul(-rotg->k*wj*fac, qj, tmp_f); /* part 1 of force */
+ svmul(mj, innersumveckM, tmpvec); /* part 2 of force */
+ rvec_inc(tmp_f, tmpvec);
+ copy_rvec(tmp_f, erg->f_rot_loc[j]);
+ V += wj*fac2;
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ /* Rotate with the alternative angle. Like rotate_local_reference(),
+ * just for a single local atom */
+ mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, tmpvec2); /* tmpvec2 = Omega*(yj0-yc0) */
+
+ /* Calculate Omega.(yj0-u) */
+ cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x Omega.(yj0-yc0) */
+ /* * v x Omega.(yj0-yc0) */
+ unitv(tmpvec, qj); /* qj = ----------------------- */
+ /* | v x Omega.(yj0-yc0) | */
+
+ fac = iprod(qj, xj_xc); /* fac = qj.(xj-xc) */
+ fac2 = fac*fac;
+
+ /* Add to the rotation potential for this angle: */
+ erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
+ }
+ }
+
+ if (bOutstepRot)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, tmp_f, xj, erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xj_xc, yj0_yc0, &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+ erg->V = 0.5*rotg->k*V;
+}
+
+
+/* Precalculate the inner sum for the radial motion 2 forces */
+static void radial_motion2_precalc_inner_sum(t_rotgrp *rotg, rvec innersumvec)
+{
+ int i;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xi_xc; /* xj - xc */
+ rvec tmpvec, tmpvec2;
+ real fac, fac2;
+ rvec ri, si;
+ real siri;
+ rvec v_xi_xc; /* v x (xj - u) */
+ real psii, psiistar;
+ real wi; /* Mass-weighting of the positions */
+ real N_M; /* N/M */
+ rvec sumvec;
+
+ erg = rotg->enfrotgrp;
+ N_M = rotg->nat * erg->invmass;
+
+ /* Loop over the collective set of positions */
+ clear_rvec(sumvec);
+ for (i = 0; i < rotg->nat; i++)
+ {
+ /* Mass-weighting */
+ wi = N_M*erg->mc[i];
+
+ rvec_sub(erg->xc[i], erg->xc_center, xi_xc); /* xi_xc = xi-xc */
+
+ /* Calculate ri. Note that xc_ref_center has already been subtracted from
+ * x_ref in init_rot_group.*/
+ mvmul(erg->rotmat, rotg->x_ref[i], ri); /* ri = Omega.(yi0-yc0) */
+
+ cprod(rotg->vec, xi_xc, v_xi_xc); /* v_xi_xc = v x (xi-u) */
+
+ fac = norm2(v_xi_xc);
+ /* * 1 */
+ psiistar = 1.0/(fac + rotg->eps); /* psiistar = --------------------- */
+ /* |v x (xi-xc)|^2 + eps */
+
+ psii = gmx_invsqrt(fac); /* 1 */
+ /* psii = ------------- */
+ /* |v x (xi-xc)| */
+
+ svmul(psii, v_xi_xc, si); /* si = psii * (v x (xi-xc) ) */
+
+ fac = iprod(v_xi_xc, ri); /* fac = (v x (xi-xc)).ri */
+ fac2 = fac*fac;
+
+ siri = iprod(si, ri); /* siri = si.ri */
+
+ svmul(psiistar/psii, ri, tmpvec);
+ svmul(psiistar*psiistar/(psii*psii*psii) * siri, si, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+ cprod(tmpvec, rotg->vec, tmpvec2);
+
+ svmul(wi*siri, tmpvec2, tmpvec);
+
+ rvec_inc(sumvec, tmpvec);
+ }
+ svmul(rotg->k*erg->invmass, sumvec, innersumvec);
+}
+
+
+/* Calculate the radial motion 2 potential and forces */
+static void do_radial_motion2(
+ t_rotgrp *rotg, /* The rotation group */
+ rvec x[], /* The positions */
+ matrix box, /* The simulation box */
- /* From the extreme coordinates of the reference group, determine the first
+ gmx_bool bOutstepRot, /* Output to main rotation output file */
+ gmx_bool bOutstepSlab) /* Output per-slab data */
+{
+ int ii, iigrp, ifit, j;
+ rvec xj; /* Position */
+ real alpha; /* a single angle between an actual and a reference position */
+ real weight; /* single weight for a single angle */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xj_u; /* xj - u */
+ rvec yj0_yc0; /* yj0 -yc0 */
+ rvec tmpvec, tmpvec2;
+ real fac, fit_fac, fac2, Vpart = 0.0;
+ rvec rj, fit_rj, sj;
+ real sjrj;
+ rvec v_xj_u; /* v x (xj - u) */
+ real psij, psijstar;
+ real mj, wj; /* For mass-weighting of the positions */
+ real N_M; /* N/M */
+ gmx_bool bPF;
+ rvec innersumvec;
+ gmx_bool bCalcPotFit;
+
+
+ erg = rotg->enfrotgrp;
+
+ bPF = rotg->eType == erotgRM2PF;
+ bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
+
+
+ clear_rvec(yj0_yc0); /* Make the compiler happy */
+
+ clear_rvec(innersumvec);
+ if (bPF)
+ {
+ /* For the pivot-free variant we have to use the current center of
+ * mass of the rotation group instead of the pivot u */
+ get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+
+ /* Also, we precalculate the second term of the forces that is identical
+ * (up to the weight factor mj) for all forces */
+ radial_motion2_precalc_inner_sum(rotg, innersumvec);
+ }
+
+ N_M = rotg->nat * erg->invmass;
+
+ /* Each process calculates the forces on its local atoms */
+ for (j = 0; j < erg->nat_loc; j++)
+ {
+ if (bPF)
+ {
+ /* Local index of a rotation group atom */
+ ii = erg->ind_loc[j];
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Mass-weighting */
+ mj = erg->mc[iigrp];
+
+ /* Current position of this atom: x[ii] */
+ copy_rvec(x[ii], xj);
+
+ /* Shift this atom such that it is near its reference */
+ shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
+
+ /* The (unrotated) reference position is yj0. yc0 has already
+ * been subtracted in init_rot_group */
+ copy_rvec(rotg->x_ref[iigrp], yj0_yc0); /* yj0_yc0 = yj0 - yc0 */
+
+ /* Calculate Omega.(yj0-yc0) */
+ mvmul(erg->rotmat, yj0_yc0, rj); /* rj = Omega.(yj0-yc0) */
+ }
+ else
+ {
+ mj = erg->m_loc[j];
+ copy_rvec(erg->x_loc_pbc[j], xj);
+ copy_rvec(erg->xr_loc[j], rj); /* rj = Omega.(yj0-u) */
+ }
+ /* Mass-weighting */
+ wj = N_M*mj;
+
+ /* Calculate (xj-u) resp. (xj-xc) */
+ rvec_sub(xj, erg->xc_center, xj_u); /* xj_u = xj-u */
+
+ cprod(rotg->vec, xj_u, v_xj_u); /* v_xj_u = v x (xj-u) */
+
+ fac = norm2(v_xj_u);
+ /* * 1 */
+ psijstar = 1.0/(fac + rotg->eps); /* psistar = -------------------- */
+ /* |v x (xj-u)|^2 + eps */
+
+ psij = gmx_invsqrt(fac); /* 1 */
+ /* psij = ------------ */
+ /* |v x (xj-u)| */
+
+ svmul(psij, v_xj_u, sj); /* sj = psij * (v x (xj-u) ) */
+
+ fac = iprod(v_xj_u, rj); /* fac = (v x (xj-u)).rj */
+ fac2 = fac*fac;
+
+ sjrj = iprod(sj, rj); /* sjrj = sj.rj */
+
+ svmul(psijstar/psij, rj, tmpvec);
+ svmul(psijstar*psijstar/(psij*psij*psij) * sjrj, sj, tmpvec2);
+ rvec_dec(tmpvec, tmpvec2);
+ cprod(tmpvec, rotg->vec, tmpvec2);
+
+ /* Store the additional force so that it can be added to the force
+ * array after the normal forces have been evaluated */
+ svmul(-rotg->k*wj*sjrj, tmpvec2, tmpvec);
+ svmul(mj, innersumvec, tmpvec2); /* This is != 0 only for the pivot-free variant */
+
+ rvec_add(tmpvec2, tmpvec, erg->f_rot_loc[j]);
+ Vpart += wj*psijstar*fac2;
+
+ /* If requested, also calculate the potential for a set of angles
+ * near the current reference angle */
+ if (bCalcPotFit)
+ {
+ for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
+ {
+ if (bPF)
+ {
+ mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, fit_rj); /* fit_rj = Omega.(yj0-yc0) */
+ }
+ else
+ {
+ /* Position of this atom in the collective array */
+ iigrp = erg->xc_ref_ind[j];
+ /* Rotate with the alternative angle. Like rotate_local_reference(),
+ * just for a single local atom */
+ mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[iigrp], fit_rj); /* fit_rj = Omega*(yj0-u) */
+ }
+ fit_fac = iprod(v_xj_u, fit_rj); /* fac = (v x (xj-u)).fit_rj */
+ /* Add to the rotation potential for this angle: */
+ erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*psijstar*fit_fac*fit_fac;
+ }
+ }
+
+ if (bOutstepRot)
+ {
+ /* Add to the torque of this rotation group */
+ erg->torque_v += torque(rotg->vec, erg->f_rot_loc[j], xj, erg->xc_center);
+
+ /* Calculate the angle between reference and actual rotation group atom. */
+ angle(rotg, xj_u, rj, &alpha, &weight); /* angle in rad, weighted */
+ erg->angle_v += alpha * weight;
+ erg->weight_v += weight;
+ }
+
+ PRINT_FORCE_J
+
+ } /* end of loop over local rotation group atoms */
+ erg->V = 0.5*rotg->k*Vpart;
+}
+
+
+/* Determine the smallest and largest position vector (with respect to the
+ * rotation vector) for the reference group */
+static void get_firstlast_atom_ref(
+ t_rotgrp *rotg,
+ int *firstindex,
+ int *lastindex)
+{
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int i;
+ real xcproj; /* The projection of a reference position on the
+ rotation vector */
+ real minproj, maxproj; /* Smallest and largest projection on v */
+
+
+
+ erg = rotg->enfrotgrp;
+
+ /* Start with some value */
+ minproj = iprod(rotg->x_ref[0], rotg->vec);
+ maxproj = minproj;
+
+ /* This is just to ensure that it still works if all the atoms of the
+ * reference structure are situated in a plane perpendicular to the rotation
+ * vector */
+ *firstindex = 0;
+ *lastindex = rotg->nat-1;
+
+ /* Loop over all atoms of the reference group,
+ * project them on the rotation vector to find the extremes */
+ for (i = 0; i < rotg->nat; i++)
+ {
+ xcproj = iprod(rotg->x_ref[i], rotg->vec);
+ if (xcproj < minproj)
+ {
+ minproj = xcproj;
+ *firstindex = i;
+ }
+ if (xcproj > maxproj)
+ {
+ maxproj = xcproj;
+ *lastindex = i;
+ }
+ }
+}
+
+
+/* Allocate memory for the slabs */
+static void allocate_slabs(
+ t_rotgrp *rotg,
+ FILE *fplog,
+ int g,
+ gmx_bool bVerbose)
+{
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int i, nslabs;
+
+
+ erg = rotg->enfrotgrp;
+
+ /* More slabs than are defined for the reference are never needed */
+ nslabs = erg->slab_last_ref - erg->slab_first_ref + 1;
+
+ /* Remember how many we allocated */
+ erg->nslabs_alloc = nslabs;
+
+ if ( (NULL != fplog) && bVerbose)
+ {
+ fprintf(fplog, "%s allocating memory to store data for %d slabs (rotation group %d).\n",
+ RotStr, nslabs, g);
+ }
+ snew(erg->slab_center, nslabs);
+ snew(erg->slab_center_ref, nslabs);
+ snew(erg->slab_weights, nslabs);
+ snew(erg->slab_torque_v, nslabs);
+ snew(erg->slab_data, nslabs);
+ snew(erg->gn_atom, nslabs);
+ snew(erg->gn_slabind, nslabs);
+ snew(erg->slab_innersumvec, nslabs);
+ for (i = 0; i < nslabs; i++)
+ {
+ snew(erg->slab_data[i].x, rotg->nat);
+ snew(erg->slab_data[i].ref, rotg->nat);
+ snew(erg->slab_data[i].weight, rotg->nat);
+ }
+ snew(erg->xc_ref_sorted, rotg->nat);
+ snew(erg->xc_sortind, rotg->nat);
+ snew(erg->firstatom, nslabs);
+ snew(erg->lastatom, nslabs);
+}
+
+
- int first, last, firststart;
++/* From the extreme positions of the reference group, determine the first
+ * and last slab of the reference. We can never have more slabs in the real
+ * simulation than calculated here for the reference.
+ */
+static void get_firstlast_slab_ref(t_rotgrp *rotg, real mc[], int ref_firstindex, int ref_lastindex)
+{
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
- firststart = first;
++ int first, last;
+ rvec dummy;
+
+
+ erg = rotg->enfrotgrp;
+ first = get_first_slab(rotg, erg->max_beta, rotg->x_ref[ref_firstindex]);
+ last = get_last_slab( rotg, erg->max_beta, rotg->x_ref[ref_lastindex ]);
-
- erg->slab_buffer = firststart - erg->slab_first_ref;
+
+ while (get_slab_weight(first, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
+ {
+ first--;
+ }
+ erg->slab_first_ref = first+1;
+ while (get_slab_weight(last, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
+ {
+ last++;
+ }
+ erg->slab_last_ref = last-1;
- /* From the extreme coordinates of the reference group, determine the first
+}
+
+
+/* Special version of copy_rvec:
+ * During the copy procedure of xcurr to b, the correct PBC image is chosen
+ * such that the copied vector ends up near its reference position xref */
+static inline void copy_correct_pbc_image(
+ const rvec xcurr, /* copy vector xcurr ... */
+ rvec b, /* ... to b ... */
+ const rvec xref, /* choosing the PBC image such that b ends up near xref */
+ matrix box,
+ int npbcdim)
+{
+ rvec dx;
+ int d, m;
+ ivec shift;
+
+
+ /* Shortest PBC distance between the atom and its reference */
+ rvec_sub(xcurr, xref, dx);
+
+ /* Determine the shift for this atom */
+ clear_ivec(shift);
+ for (m = npbcdim-1; m >= 0; m--)
+ {
+ while (dx[m] < -0.5*box[m][m])
+ {
+ for (d = 0; d < DIM; d++)
+ {
+ dx[d] += box[m][d];
+ }
+ shift[m]++;
+ }
+ while (dx[m] >= 0.5*box[m][m])
+ {
+ for (d = 0; d < DIM; d++)
+ {
+ dx[d] -= box[m][d];
+ }
+ shift[m]--;
+ }
+ }
+
+ /* Apply the shift to the position */
+ copy_rvec(xcurr, b);
+ shift_single_coord(box, b, shift);
+}
+
+
+static void init_rot_group(FILE *fplog, t_commrec *cr, int g, t_rotgrp *rotg,
+ rvec *x, gmx_mtop_t *mtop, gmx_bool bVerbose, FILE *out_slabs, matrix box,
+ t_inputrec *ir, gmx_bool bOutputCenters)
+{
+ int i, ii;
+ rvec coord, xref, *xdum;
+ gmx_bool bFlex, bColl;
+ t_atom *atom;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ int ref_firstindex, ref_lastindex;
+ gmx_mtop_atomlookup_t alook = NULL;
+ real mass, totalmass;
+ real start = 0.0;
+ double t_start;
+
+
+ /* Do we have a flexible axis? */
+ bFlex = ISFLEX(rotg);
+ /* Do we use a global set of coordinates? */
+ bColl = ISCOLL(rotg);
+
+ erg = rotg->enfrotgrp;
+
+ /* Allocate space for collective coordinates if needed */
+ if (bColl)
+ {
+ snew(erg->xc, rotg->nat);
+ snew(erg->xc_shifts, rotg->nat);
+ snew(erg->xc_eshifts, rotg->nat);
+ snew(erg->xc_old, rotg->nat);
+
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ snew(erg->xc_ref_length, rotg->nat); /* in case fit type NORM is chosen */
+ snew(erg->xc_norm, rotg->nat);
+ }
+ }
+ else
+ {
+ snew(erg->xr_loc, rotg->nat);
+ snew(erg->x_loc_pbc, rotg->nat);
+ }
+
+ snew(erg->f_rot_loc, rotg->nat);
+ snew(erg->xc_ref_ind, rotg->nat);
+
+ /* Make space for the calculation of the potential at other angles (used
+ * for fitting only) */
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ snew(erg->PotAngleFit, 1);
+ snew(erg->PotAngleFit->degangle, rotg->PotAngle_nstep);
+ snew(erg->PotAngleFit->V, rotg->PotAngle_nstep);
+ snew(erg->PotAngleFit->rotmat, rotg->PotAngle_nstep);
+
+ /* Get the set of angles around the reference angle */
+ start = -0.5 * (rotg->PotAngle_nstep - 1)*rotg->PotAngle_step;
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ {
+ erg->PotAngleFit->degangle[i] = start + i*rotg->PotAngle_step;
+ }
+ }
+ else
+ {
+ erg->PotAngleFit = NULL;
+ }
+
+ /* xc_ref_ind needs to be set to identity in the serial case */
+ if (!PAR(cr))
+ {
+ for (i = 0; i < rotg->nat; i++)
+ {
+ erg->xc_ref_ind[i] = i;
+ }
+ }
+
+ /* Copy the masses so that the center can be determined. For all types of
+ * enforced rotation, we store the masses in the erg->mc array. */
+ if (rotg->bMassW)
+ {
+ alook = gmx_mtop_atomlookup_init(mtop);
+ }
+ snew(erg->mc, rotg->nat);
+ if (bFlex)
+ {
+ snew(erg->mc_sorted, rotg->nat);
+ }
+ if (!bColl)
+ {
+ snew(erg->m_loc, rotg->nat);
+ }
+ totalmass = 0.0;
+ for (i = 0; i < rotg->nat; i++)
+ {
+ if (rotg->bMassW)
+ {
+ gmx_mtop_atomnr_to_atom(alook, rotg->ind[i], &atom);
+ mass = atom->m;
+ }
+ else
+ {
+ mass = 1.0;
+ }
+ erg->mc[i] = mass;
+ totalmass += mass;
+ }
+ erg->invmass = 1.0/totalmass;
+
+ if (rotg->bMassW)
+ {
+ gmx_mtop_atomlookup_destroy(alook);
+ }
+
+ /* Set xc_ref_center for any rotation potential */
+ if ((rotg->eType == erotgISO) || (rotg->eType == erotgPM) || (rotg->eType == erotgRM) || (rotg->eType == erotgRM2))
+ {
+ /* Set the pivot point for the fixed, stationary-axis potentials. This
+ * won't change during the simulation */
+ copy_rvec(rotg->pivot, erg->xc_ref_center);
+ copy_rvec(rotg->pivot, erg->xc_center );
+ }
+ else
+ {
+ /* Center of the reference positions */
+ get_center(rotg->x_ref, erg->mc, rotg->nat, erg->xc_ref_center);
+
+ /* Center of the actual positions */
+ if (MASTER(cr))
+ {
+ snew(xdum, rotg->nat);
+ for (i = 0; i < rotg->nat; i++)
+ {
+ ii = rotg->ind[i];
+ copy_rvec(x[ii], xdum[i]);
+ }
+ get_center(xdum, erg->mc, rotg->nat, erg->xc_center);
+ sfree(xdum);
+ }
+#ifdef GMX_MPI
+ if (PAR(cr))
+ {
+ gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
+ }
+#endif
+ }
+
+ if (bColl)
+ {
+ /* Save the original (whole) set of positions in xc_old such that at later
+ * steps the rotation group can always be made whole again. If the simulation is
+ * restarted, we compute the starting reference positions (given the time)
+ * and assume that the correct PBC image of each position is the one nearest
+ * to the current reference */
+ if (MASTER(cr))
+ {
+ /* Calculate the rotation matrix for this angle: */
+ t_start = ir->init_t + ir->init_step*ir->delta_t;
+ erg->degangle = rotg->rate * t_start;
+ calc_rotmat(rotg->vec, erg->degangle, erg->rotmat);
+
+ for (i = 0; i < rotg->nat; i++)
+ {
+ ii = rotg->ind[i];
+
+ /* Subtract pivot, rotate, and add pivot again. This will yield the
+ * reference position for time t */
+ rvec_sub(rotg->x_ref[i], erg->xc_ref_center, coord);
+ mvmul(erg->rotmat, coord, xref);
+ rvec_inc(xref, erg->xc_ref_center);
+
+ copy_correct_pbc_image(x[ii], erg->xc_old[i], xref, box, 3);
+ }
+ }
+#ifdef GMX_MPI
+ if (PAR(cr))
+ {
+ gmx_bcast(rotg->nat*sizeof(erg->xc_old[0]), erg->xc_old, cr);
+ }
+#endif
+ }
+
+ if ( (rotg->eType != erotgFLEX) && (rotg->eType != erotgFLEX2) )
+ {
+ /* Put the reference positions into origin: */
+ for (i = 0; i < rotg->nat; i++)
+ {
+ rvec_dec(rotg->x_ref[i], erg->xc_ref_center);
+ }
+ }
+
+ /* Enforced rotation with flexible axis */
+ if (bFlex)
+ {
+ /* Calculate maximum beta value from minimum gaussian (performance opt.) */
+ erg->max_beta = calc_beta_max(rotg->min_gaussian, rotg->slab_dist);
+
+ /* Determine the smallest and largest coordinate with respect to the rotation vector */
+ get_firstlast_atom_ref(rotg, &ref_firstindex, &ref_lastindex);
+
- er->out_slabs = open_slab_out(opt2fn("-rs", nfile, fnm), rot, oenv);
++ /* From the extreme positions of the reference group, determine the first
+ * and last slab of the reference. */
+ get_firstlast_slab_ref(rotg, erg->mc, ref_firstindex, ref_lastindex);
+
+ /* Allocate memory for the slabs */
+ allocate_slabs(rotg, fplog, g, bVerbose);
+
+ /* Flexible rotation: determine the reference centers for the rest of the simulation */
+ erg->slab_first = erg->slab_first_ref;
+ erg->slab_last = erg->slab_last_ref;
+ get_slab_centers(rotg, rotg->x_ref, erg->mc, g, -1, out_slabs, bOutputCenters, TRUE);
+
+ /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
+ if (rotg->eFittype == erotgFitNORM)
+ {
+ for (i = 0; i < rotg->nat; i++)
+ {
+ rvec_sub(rotg->x_ref[i], erg->xc_ref_center, coord);
+ erg->xc_ref_length[i] = norm(coord);
+ }
+ }
+ }
+}
+
+
+extern void dd_make_local_rotation_groups(gmx_domdec_t *dd, t_rot *rot)
+{
+ gmx_ga2la_t ga2la;
+ int g;
+ t_rotgrp *rotg;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+ ga2la = dd->ga2la;
+
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+
+
+ dd_make_local_group_indices(ga2la, rotg->nat, rotg->ind,
+ &erg->nat_loc, &erg->ind_loc, &erg->nalloc_loc, erg->xc_ref_ind);
+ }
+}
+
+
+/* Calculate the size of the MPI buffer needed in reduce_output() */
+static int calc_mpi_bufsize(t_rot *rot)
+{
+ int g;
+ int count_group, count_total;
+ t_rotgrp *rotg;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+
+
+ count_total = 0;
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+
+ /* Count the items that are transferred for this group: */
+ count_group = 4; /* V, torque, angle, weight */
+
+ /* Add the maximum number of slabs for flexible groups */
+ if (ISFLEX(rotg))
+ {
+ count_group += erg->slab_last_ref - erg->slab_first_ref + 1;
+ }
+
+ /* Add space for the potentials at different angles: */
+ if (erotgFitPOT == rotg->eFittype)
+ {
+ count_group += rotg->PotAngle_nstep;
+ }
+
+ /* Add to the total number: */
+ count_total += count_group;
+ }
+
+ return count_total;
+}
+
+
+extern void init_rot(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
+ t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
+ gmx_bool bVerbose, unsigned long Flags)
+{
+ t_rot *rot;
+ t_rotgrp *rotg;
+ int g;
+ int nat_max = 0; /* Size of biggest rotation group */
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec *x_pbc = NULL; /* Space for the pbc-correct atom positions */
+
+
+ if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
+ {
+ gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
+ }
+
+ if (MASTER(cr) && bVerbose)
+ {
+ fprintf(stdout, "%s Initializing ...\n", RotStr);
+ }
+
+ rot = ir->rot;
+ snew(rot->enfrot, 1);
+ er = rot->enfrot;
+ er->Flags = Flags;
+
+ /* When appending, skip first output to avoid duplicate entries in the data files */
+ if (er->Flags & MD_APPENDFILES)
+ {
+ er->bOut = FALSE;
+ }
+ else
+ {
+ er->bOut = TRUE;
+ }
+
+ if (MASTER(cr) && er->bOut)
+ {
+ please_cite(fplog, "Kutzner2011");
+ }
+
+ /* Output every step for reruns */
+ if (er->Flags & MD_RERUN)
+ {
+ if (NULL != fplog)
+ {
+ fprintf(fplog, "%s rerun - will write rotation output every available step.\n", RotStr);
+ }
+ rot->nstrout = 1;
+ rot->nstsout = 1;
+ }
+
+ er->out_slabs = NULL;
+ if (MASTER(cr) && HaveFlexibleGroups(rot) )
+ {
- er->out_angles = open_angles_out(opt2fn("-ra", nfile, fnm), rot, oenv);
++ er->out_slabs = open_slab_out(opt2fn("-rs", nfile, fnm), rot);
+ }
+
+ if (MASTER(cr))
+ {
+ /* Remove pbc, make molecule whole.
+ * When ir->bContinuation=TRUE this has already been done, but ok. */
+ snew(x_pbc, mtop->natoms);
+ m_rveccopy(mtop->natoms, x, x_pbc);
+ do_pbc_first_mtop(NULL, ir->ePBC, box, mtop, x_pbc);
+ /* All molecules will be whole now, but not necessarily in the home box.
+ * Additionally, if a rotation group consists of more than one molecule
+ * (e.g. two strands of DNA), each one of them can end up in a different
+ * periodic box. This is taken care of in init_rot_group. */
+ }
+
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+
+ if (NULL != fplog)
+ {
+ fprintf(fplog, "%s group %d type '%s'\n", RotStr, g, erotg_names[rotg->eType]);
+ }
+
+ if (rotg->nat > 0)
+ {
+ /* Allocate space for the rotation group's data: */
+ snew(rotg->enfrotgrp, 1);
+ erg = rotg->enfrotgrp;
+
+ nat_max = max(nat_max, rotg->nat);
+
+ if (PAR(cr))
+ {
+ erg->nat_loc = 0;
+ erg->nalloc_loc = 0;
+ erg->ind_loc = NULL;
+ }
+ else
+ {
+ erg->nat_loc = rotg->nat;
+ erg->ind_loc = rotg->ind;
+ }
+ init_rot_group(fplog, cr, g, rotg, x_pbc, mtop, bVerbose, er->out_slabs, box, ir,
+ !(er->Flags & MD_APPENDFILES) ); /* Do not output the reference centers
+ * again if we are appending */
+ }
+ }
+
+ /* Allocate space for enforced rotation buffer variables */
+ er->bufsize = nat_max;
+ snew(er->data, nat_max);
+ snew(er->xbuf, nat_max);
+ snew(er->mbuf, nat_max);
+
+ /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
+ if (PAR(cr))
+ {
+ er->mpi_bufsize = calc_mpi_bufsize(rot) + 100; /* larger to catch errors */
+ snew(er->mpi_inbuf, er->mpi_bufsize);
+ snew(er->mpi_outbuf, er->mpi_bufsize);
+ }
+ else
+ {
+ er->mpi_bufsize = 0;
+ er->mpi_inbuf = NULL;
+ er->mpi_outbuf = NULL;
+ }
+
+ /* Only do I/O on the MASTER */
+ er->out_angles = NULL;
+ er->out_rot = NULL;
+ er->out_torque = NULL;
+ if (MASTER(cr))
+ {
+ er->out_rot = open_rot_out(opt2fn("-ro", nfile, fnm), rot, oenv);
+
+ if (rot->nstsout > 0)
+ {
+ if (HaveFlexibleGroups(rot) || HavePotFitGroups(rot) )
+ {
- er->out_torque = open_torque_out(opt2fn("-rt", nfile, fnm), rot, oenv);
++ er->out_angles = open_angles_out(opt2fn("-ra", nfile, fnm), rot);
+ }
+ if (HaveFlexibleGroups(rot) )
+ {
- extern void finish_rot(FILE *fplog, t_rot *rot)
++ er->out_torque = open_torque_out(opt2fn("-rt", nfile, fnm), rot);
+ }
+ }
+
+ sfree(x_pbc);
+ }
+}
+
+
- do_fixed(rotg, x, box, t, step, outstep_rot, outstep_slab);
++extern void finish_rot(t_rot *rot)
+{
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+
+
+ er = rot->enfrot;
+ if (er->out_rot)
+ {
+ gmx_fio_fclose(er->out_rot);
+ }
+ if (er->out_slabs)
+ {
+ gmx_fio_fclose(er->out_slabs);
+ }
+ if (er->out_angles)
+ {
+ gmx_fio_fclose(er->out_angles);
+ }
+ if (er->out_torque)
+ {
+ gmx_fio_fclose(er->out_torque);
+ }
+}
+
+
+/* Rotate the local reference positions and store them in
+ * erg->xr_loc[0...(nat_loc-1)]
+ *
+ * Note that we already subtracted u or y_c from the reference positions
+ * in init_rot_group().
+ */
+static void rotate_local_reference(t_rotgrp *rotg)
+{
+ gmx_enfrotgrp_t erg;
+ int i, ii;
+
+
+ erg = rotg->enfrotgrp;
+
+ for (i = 0; i < erg->nat_loc; i++)
+ {
+ /* Index of this rotation group atom with respect to the whole rotation group */
+ ii = erg->xc_ref_ind[i];
+ /* Rotate */
+ mvmul(erg->rotmat, rotg->x_ref[ii], erg->xr_loc[i]);
+ }
+}
+
+
+/* Select the PBC representation for each local x position and store that
+ * for later usage. We assume the right PBC image of an x is the one nearest to
+ * its rotated reference */
+static void choose_pbc_image(rvec x[], t_rotgrp *rotg, matrix box, int npbcdim)
+{
+ int i, ii;
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec xref;
+
+
+ erg = rotg->enfrotgrp;
+
+ for (i = 0; i < erg->nat_loc; i++)
+ {
+ /* Index of a rotation group atom */
+ ii = erg->ind_loc[i];
+
+ /* Get the correctly rotated reference position. The pivot was already
+ * subtracted in init_rot_group() from the reference positions. Also,
+ * the reference positions have already been rotated in
+ * rotate_local_reference(). For the current reference position we thus
+ * only need to add the pivot again. */
+ copy_rvec(erg->xr_loc[i], xref);
+ rvec_inc(xref, erg->xc_ref_center);
+
+ copy_correct_pbc_image(x[ii], erg->x_loc_pbc[i], xref, box, npbcdim);
+ }
+}
+
+
+extern void do_rotation(
+ t_commrec *cr,
+ t_inputrec *ir,
+ matrix box,
+ rvec x[],
+ real t,
+ gmx_large_int_t step,
+ gmx_wallcycle_t wcycle,
+ gmx_bool bNS)
+{
+ int g, i, ii;
+ t_rot *rot;
+ t_rotgrp *rotg;
+ gmx_bool outstep_slab, outstep_rot;
+ gmx_bool bFlex, bColl;
+ gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
+ gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
+ rvec transvec;
+ t_gmx_potfit *fit = NULL; /* For fit type 'potential' determine the fit
+ angle via the potential minimum */
+
+ /* Enforced rotation cycle counting: */
+ gmx_cycles_t cycles_comp; /* Cycles for the enf. rotation computation
+ only, does not count communication. This
+ counter is used for load-balancing */
+
+#ifdef TAKETIME
+ double t0;
+#endif
+
+ rot = ir->rot;
+ er = rot->enfrot;
+
+ /* When to output in main rotation output file */
+ outstep_rot = do_per_step(step, rot->nstrout) && er->bOut;
+ /* When to output per-slab data */
+ outstep_slab = do_per_step(step, rot->nstsout) && er->bOut;
+
+ /* Output time into rotation output file */
+ if (outstep_rot && MASTER(cr))
+ {
+ fprintf(er->out_rot, "%12.3e", t);
+ }
+
+ /**************************************************************************/
+ /* First do ALL the communication! */
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+
+ /* Do we have a flexible axis? */
+ bFlex = ISFLEX(rotg);
+ /* Do we use a collective (global) set of coordinates? */
+ bColl = ISCOLL(rotg);
+
+ /* Calculate the rotation matrix for this angle: */
+ erg->degangle = rotg->rate * t;
+ calc_rotmat(rotg->vec, erg->degangle, erg->rotmat);
+
+ if (bColl)
+ {
+ /* Transfer the rotation group's positions such that every node has
+ * all of them. Every node contributes its local positions x and stores
+ * it in the collective erg->xc array. */
+ communicate_group_positions(cr, erg->xc, erg->xc_shifts, erg->xc_eshifts, bNS,
+ x, rotg->nat, erg->nat_loc, erg->ind_loc, erg->xc_ref_ind, erg->xc_old, box);
+ }
+ else
+ {
+ /* Fill the local masses array;
+ * this array changes in DD/neighborsearching steps */
+ if (bNS)
+ {
+ for (i = 0; i < erg->nat_loc; i++)
+ {
+ /* Index of local atom w.r.t. the collective rotation group */
+ ii = erg->xc_ref_ind[i];
+ erg->m_loc[i] = erg->mc[ii];
+ }
+ }
+
+ /* Calculate Omega*(y_i-y_c) for the local positions */
+ rotate_local_reference(rotg);
+
+ /* Choose the nearest PBC images of the group atoms with respect
+ * to the rotated reference positions */
+ choose_pbc_image(x, rotg, box, 3);
+
+ /* Get the center of the rotation group */
+ if ( (rotg->eType == erotgISOPF) || (rotg->eType == erotgPMPF) )
+ {
+ get_center_comm(cr, erg->x_loc_pbc, erg->m_loc, erg->nat_loc, rotg->nat, erg->xc_center);
+ }
+ }
+
+ } /* End of loop over rotation groups */
+
+ /**************************************************************************/
+ /* Done communicating, we can start to count cycles for the load balancing now ... */
+ cycles_comp = gmx_cycles_read();
+
+
+#ifdef TAKETIME
+ t0 = MPI_Wtime();
+#endif
+
+ for (g = 0; g < rot->ngrp; g++)
+ {
+ rotg = &rot->grp[g];
+ erg = rotg->enfrotgrp;
+
+ bFlex = ISFLEX(rotg);
+ bColl = ISCOLL(rotg);
+
+ if (outstep_rot && MASTER(cr))
+ {
+ fprintf(er->out_rot, "%12.4f", erg->degangle);
+ }
+
+ /* Calculate angles and rotation matrices for potential fitting: */
+ if ( (outstep_rot || outstep_slab) && (erotgFitPOT == rotg->eFittype) )
+ {
+ fit = erg->PotAngleFit;
+ for (i = 0; i < rotg->PotAngle_nstep; i++)
+ {
+ calc_rotmat(rotg->vec, erg->degangle + fit->degangle[i], fit->rotmat[i]);
+
+ /* Clear value from last step */
+ erg->PotAngleFit->V[i] = 0.0;
+ }
+ }
+
+ /* Clear values from last time step */
+ erg->V = 0.0;
+ erg->torque_v = 0.0;
+ erg->angle_v = 0.0;
+ erg->weight_v = 0.0;
+
+ switch (rotg->eType)
+ {
+ case erotgISO:
+ case erotgISOPF:
+ case erotgPM:
+ case erotgPMPF:
- do_radial_motion(rotg, x, box, t, step, outstep_rot, outstep_slab);
++ do_fixed(rotg, outstep_rot, outstep_slab);
+ break;
+ case erotgRM:
- do_radial_motion_pf(rotg, x, box, t, step, outstep_rot, outstep_slab);
++ do_radial_motion(rotg, outstep_rot, outstep_slab);
+ break;
+ case erotgRMPF:
- do_radial_motion2(rotg, x, box, t, step, outstep_rot, outstep_slab);
++ do_radial_motion_pf(rotg, x, box, outstep_rot, outstep_slab);
+ break;
+ case erotgRM2:
+ case erotgRM2PF:
- do_flexible(MASTER(cr), er, rotg, g, x, box, t, step, outstep_rot, outstep_slab);
++ do_radial_motion2(rotg, x, box, outstep_rot, outstep_slab);
+ break;
+ case erotgFLEXT:
+ case erotgFLEX2T:
+ /* Subtract the center of the rotation group from the collective positions array
+ * Also store the center in erg->xc_center since it needs to be subtracted
+ * in the low level routines from the local coordinates as well */
+ get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
+ svmul(-1.0, erg->xc_center, transvec);
+ translate_x(erg->xc, rotg->nat, transvec);
- do_flexible(MASTER(cr), er, rotg, g, x, box, t, step, outstep_rot, outstep_slab);
++ do_flexible(MASTER(cr), er, rotg, g, x, box, t, outstep_rot, outstep_slab);
+ break;
+ case erotgFLEX:
+ case erotgFLEX2:
+ /* Do NOT subtract the center of mass in the low level routines! */
+ clear_rvec(erg->xc_center);
++ do_flexible(MASTER(cr), er, rotg, g, x, box, t, outstep_rot, outstep_slab);
+ break;
+ default:
+ gmx_fatal(FARGS, "No such rotation potential.");
+ break;
+ }
+ }
+
+#ifdef TAKETIME
+ if (MASTER(cr))
+ {
+ fprintf(stderr, "%s calculation (step %d) took %g seconds.\n", RotStr, step, MPI_Wtime()-t0);
+ }
+#endif
+
+ /* Stop the enforced rotation cycle counter and add the computation-only
+ * cycles to the force cycles for load balancing */
+ cycles_comp = gmx_cycles_read() - cycles_comp;
+
+ if (DOMAINDECOMP(cr) && wcycle)
+ {
+ dd_cycles_add(cr->dd, cycles_comp, ddCyclF);
+ }
+}
--- /dev/null
- dvdl += lagr[i]*dt_2*
- (idef->iparams[type].constr.dB-idef->iparams[type].constr.dA);
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * GROwing Monsters And Cloning Shrimps
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include "sysstuff.h"
+#include "typedefs.h"
+#include "smalloc.h"
+#include "pbc.h"
+#include "txtdump.h"
+#include "vec.h"
+#include "nrnb.h"
+#include "constr.h"
+
+typedef struct gmx_shakedata
+{
+ rvec *rij;
+ real *M2;
+ real *tt;
+ real *dist2;
+ int nalloc;
+ /* SOR stuff */
+ real delta;
+ real omega;
+ real gamma;
+} t_gmx_shakedata;
+
+gmx_shakedata_t shake_init()
+{
+ gmx_shakedata_t d;
+
+ snew(d, 1);
+
+ d->nalloc = 0;
+ d->rij = NULL;
+ d->M2 = NULL;
+ d->tt = NULL;
+ d->dist2 = NULL;
+
+ /* SOR initialization */
+ d->delta = 0.1;
+ d->omega = 1.0;
+ d->gamma = 1000000;
+
+ return d;
+}
+
+static void pv(FILE *log, char *s, rvec x)
+{
+ int m;
+
+ fprintf(log, "%5s:", s);
+ for (m = 0; (m < DIM); m++)
+ {
+ fprintf(log, " %10.3f", x[m]);
+ }
+ fprintf(log, "\n");
+ fflush(log);
+}
+
+void cshake(atom_id iatom[], int ncon, int *nnit, int maxnit,
+ real dist2[], real xp[], real rij[], real m2[], real omega,
+ real invmass[], real tt[], real lagr[], int *nerror)
+{
+ /*
+ * r.c. van schaik and w.f. van gunsteren
+ * eth zuerich
+ * june 1992
+ * Adapted for use with Gromacs by David van der Spoel november 92 and later.
+ */
+ /* default should be increased! MRS 8/4/2009 */
+ const real mytol = 1e-10;
+
+ int ll, i, j, i3, j3, l3;
+ int ix, iy, iz, jx, jy, jz;
+ real toler, rpij2, rrpr, tx, ty, tz, diff, acor, im, jm;
+ real xh, yh, zh, rijx, rijy, rijz;
+ real tix, tiy, tiz;
+ real tjx, tjy, tjz;
+ int nit, error, nconv;
+ real iconvf;
+
+ error = 0;
+ nconv = 1;
+ for (nit = 0; (nit < maxnit) && (nconv != 0) && (error == 0); nit++)
+ {
+ nconv = 0;
+ for (ll = 0; (ll < ncon) && (error == 0); ll++)
+ {
+ l3 = 3*ll;
+ rijx = rij[l3+XX];
+ rijy = rij[l3+YY];
+ rijz = rij[l3+ZZ];
+ i = iatom[l3+1];
+ j = iatom[l3+2];
+ i3 = 3*i;
+ j3 = 3*j;
+ ix = i3+XX;
+ iy = i3+YY;
+ iz = i3+ZZ;
+ jx = j3+XX;
+ jy = j3+YY;
+ jz = j3+ZZ;
+
+ tx = xp[ix]-xp[jx];
+ ty = xp[iy]-xp[jy];
+ tz = xp[iz]-xp[jz];
+ rpij2 = tx*tx+ty*ty+tz*tz;
+ toler = dist2[ll];
+ diff = toler-rpij2;
+
+ /* iconvf is less than 1 when the error is smaller than a bound */
+ /* But if tt is too big, then it will result in looping in iconv */
+
+ iconvf = fabs(diff)*tt[ll];
+
+ if (iconvf > 1)
+ {
+ nconv = iconvf;
+ rrpr = rijx*tx+rijy*ty+rijz*tz;
+
+ if (rrpr < toler*mytol)
+ {
+ error = ll+1;
+ }
+ else
+ {
+ acor = omega*diff*m2[ll]/rrpr;
+ lagr[ll] += acor;
+ xh = rijx*acor;
+ yh = rijy*acor;
+ zh = rijz*acor;
+ im = invmass[i];
+ jm = invmass[j];
+ xp[ix] += xh*im;
+ xp[iy] += yh*im;
+ xp[iz] += zh*im;
+ xp[jx] -= xh*jm;
+ xp[jy] -= yh*jm;
+ xp[jz] -= zh*jm;
+ }
+ }
+ }
+ }
+ *nnit = nit;
+ *nerror = error;
+}
+
+int vec_shakef(FILE *fplog, gmx_shakedata_t shaked,
+ int natoms, real invmass[], int ncon,
+ t_iparams ip[], t_iatom *iatom,
+ real tol, rvec x[], rvec prime[], real omega,
+ gmx_bool bFEP, real lambda, real lagr[],
+ real invdt, rvec *v,
+ gmx_bool bCalcVir, tensor vir_r_m_dr, int econq,
+ t_vetavars *vetavar)
+{
+ rvec *rij;
+ real *M2, *tt, *dist2;
+ int maxnit = 1000;
+ int nit = 0, ll, i, j, type;
+ t_iatom *ia;
+ real L1, tol2, toler;
+ real mm = 0., tmp;
+ int error = 0;
+ real g, vscale, rscale, rvscale;
+
+ if (ncon > shaked->nalloc)
+ {
+ shaked->nalloc = over_alloc_dd(ncon);
+ srenew(shaked->rij, shaked->nalloc);
+ srenew(shaked->M2, shaked->nalloc);
+ srenew(shaked->tt, shaked->nalloc);
+ srenew(shaked->dist2, shaked->nalloc);
+ }
+ rij = shaked->rij;
+ M2 = shaked->M2;
+ tt = shaked->tt;
+ dist2 = shaked->dist2;
+
+ L1 = 1.0-lambda;
+ tol2 = 2.0*tol;
+ ia = iatom;
+ for (ll = 0; (ll < ncon); ll++, ia += 3)
+ {
+ type = ia[0];
+ i = ia[1];
+ j = ia[2];
+
+ mm = 2*(invmass[i]+invmass[j]);
+ rij[ll][XX] = x[i][XX]-x[j][XX];
+ rij[ll][YY] = x[i][YY]-x[j][YY];
+ rij[ll][ZZ] = x[i][ZZ]-x[j][ZZ];
+ M2[ll] = 1.0/mm;
+ if (bFEP)
+ {
+ toler = sqr(L1*ip[type].constr.dA + lambda*ip[type].constr.dB);
+ }
+ else
+ {
+ toler = sqr(ip[type].constr.dA);
+ }
+ dist2[ll] = toler;
+ tt[ll] = 1.0/(toler*tol2);
+ }
+
+ switch (econq)
+ {
+ case econqCoord:
+ cshake(iatom, ncon, &nit, maxnit, dist2, prime[0], rij[0], M2, omega, invmass, tt, lagr, &error);
+ break;
+ case econqVeloc:
+ crattle(iatom, ncon, &nit, maxnit, dist2, prime[0], rij[0], M2, omega, invmass, tt, lagr, &error, invdt, vetavar);
+ break;
+ }
+
+ if (nit >= maxnit)
+ {
+ if (fplog)
+ {
+ fprintf(fplog, "Shake did not converge in %d steps\n", maxnit);
+ }
+ fprintf(stderr, "Shake did not converge in %d steps\n", maxnit);
+ nit = 0;
+ }
+ else if (error != 0)
+ {
+ if (fplog)
+ {
+ fprintf(fplog, "Inner product between old and new vector <= 0.0!\n"
+ "constraint #%d atoms %u and %u\n",
+ error-1, iatom[3*(error-1)+1]+1, iatom[3*(error-1)+2]+1);
+ }
+ fprintf(stderr, "Inner product between old and new vector <= 0.0!\n"
+ "constraint #%d atoms %u and %u\n",
+ error-1, iatom[3*(error-1)+1]+1, iatom[3*(error-1)+2]+1);
+ nit = 0;
+ }
+
+ /* Constraint virial and correct the lagrange multipliers for the length */
+
+ ia = iatom;
+
+ for (ll = 0; (ll < ncon); ll++, ia += 3)
+ {
+
+ if ((econq == econqCoord) && v != NULL)
+ {
+ /* Correct the velocities */
+ mm = lagr[ll]*invmass[ia[1]]*invdt/vetavar->rscale;
+ for (i = 0; i < DIM; i++)
+ {
+ v[ia[1]][i] += mm*rij[ll][i];
+ }
+ mm = lagr[ll]*invmass[ia[2]]*invdt/vetavar->rscale;
+ for (i = 0; i < DIM; i++)
+ {
+ v[ia[2]][i] -= mm*rij[ll][i];
+ }
+ /* 16 flops */
+ }
+
+ /* constraint virial */
+ if (bCalcVir)
+ {
+ if (econq == econqCoord)
+ {
+ mm = lagr[ll]/vetavar->rvscale;
+ }
+ if (econq == econqVeloc)
+ {
+ mm = lagr[ll]/(vetavar->vscale*vetavar->vscale_nhc[0]);
+ }
+ for (i = 0; i < DIM; i++)
+ {
+ tmp = mm*rij[ll][i];
+ for (j = 0; j < DIM; j++)
+ {
+ vir_r_m_dr[i][j] -= tmp*rij[ll][j];
+ }
+ }
+ /* 21 flops */
+ }
+
+ /* Correct the lagrange multipliers for the length */
+ /* (more details would be useful here . . . )*/
+
+ type = ia[0];
+ if (bFEP)
+ {
+ toler = L1*ip[type].constr.dA + lambda*ip[type].constr.dB;
+ }
+ else
+ {
+ toler = ip[type].constr.dA;
+ lagr[ll] *= toler;
+ }
+ }
+
+ return nit;
+}
+
+static void check_cons(FILE *log, int nc, rvec x[], rvec prime[], rvec v[],
+ t_iparams ip[], t_iatom *iatom,
+ real invmass[], int econq)
+{
+ t_iatom *ia;
+ int ai, aj;
+ int i;
+ real d, dp;
+ rvec dx, dv;
+
+ fprintf(log,
+ " i mi j mj before after should be\n");
+ ia = iatom;
+ for (i = 0; (i < nc); i++, ia += 3)
+ {
+ ai = ia[1];
+ aj = ia[2];
+ rvec_sub(x[ai], x[aj], dx);
+ d = norm(dx);
+
+ switch (econq)
+ {
+ case econqCoord:
+ rvec_sub(prime[ai], prime[aj], dx);
+ dp = norm(dx);
+ fprintf(log, "%5d %5.2f %5d %5.2f %10.5f %10.5f %10.5f\n",
+ ai+1, 1.0/invmass[ai],
+ aj+1, 1.0/invmass[aj], d, dp, ip[ia[0]].constr.dA);
+ break;
+ case econqVeloc:
+ rvec_sub(v[ai], v[aj], dv);
+ d = iprod(dx, dv);
+ rvec_sub(prime[ai], prime[aj], dv);
+ dp = iprod(dx, dv);
+ fprintf(log, "%5d %5.2f %5d %5.2f %10.5f %10.5f %10.5f\n",
+ ai+1, 1.0/invmass[ai],
+ aj+1, 1.0/invmass[aj], d, dp, 0.);
+ break;
+ }
+ }
+}
+
+gmx_bool bshakef(FILE *log, gmx_shakedata_t shaked,
+ int natoms, real invmass[], int nblocks, int sblock[],
+ t_idef *idef, t_inputrec *ir, rvec x_s[], rvec prime[],
+ t_nrnb *nrnb, real *lagr, real lambda, real *dvdlambda,
+ real invdt, rvec *v, gmx_bool bCalcVir, tensor vir_r_m_dr,
+ gmx_bool bDumpOnError, int econq, t_vetavars *vetavar)
+{
+ t_iatom *iatoms;
+ real *lam, dt_2, dvdl;
+ int i, n0, ncons, blen, type;
+ int tnit = 0, trij = 0;
+
+#ifdef DEBUG
+ fprintf(log, "nblocks=%d, sblock[0]=%d\n", nblocks, sblock[0]);
+#endif
+
+ ncons = idef->il[F_CONSTR].nr/3;
+
+ for (i = 0; i < ncons; i++)
+ {
+ lagr[i] = 0;
+ }
+
+ iatoms = &(idef->il[F_CONSTR].iatoms[sblock[0]]);
+ lam = lagr;
+ for (i = 0; (i < nblocks); )
+ {
+ blen = (sblock[i+1]-sblock[i]);
+ blen /= 3;
+ n0 = vec_shakef(log, shaked, natoms, invmass, blen, idef->iparams,
+ iatoms, ir->shake_tol, x_s, prime, shaked->omega,
+ ir->efep != efepNO, lambda, lam, invdt, v, bCalcVir, vir_r_m_dr,
+ econq, vetavar);
+
+#ifdef DEBUGSHAKE
+ check_cons(log, blen, x_s, prime, v, idef->iparams, iatoms, invmass, econq);
+#endif
+
+ if (n0 == 0)
+ {
+ if (bDumpOnError && log)
+ {
+ {
+ check_cons(log, blen, x_s, prime, v, idef->iparams, iatoms, invmass, econq);
+ }
+ }
+ return FALSE;
+ }
+ tnit += n0*blen;
+ trij += blen;
+ iatoms += 3*blen; /* Increment pointer! */
+ lam += blen;
+ i++;
+ }
+ /* only for position part? */
+ if (econq == econqCoord)
+ {
+ if (ir->efep != efepNO)
+ {
++ real bondA,bondB;
+ dt_2 = 1/sqr(ir->delta_t);
+ dvdl = 0;
+ for (i = 0; i < ncons; i++)
+ {
+ type = idef->il[F_CONSTR].iatoms[3*i];
++
++ /* dh/dl contribution from constraint force is dh/dr (constraint force) dot dr/dl */
++ /* constraint force is -\sum_i lagr_i* d(constraint)/dr, with constrant = r^2-d^2 */
++ /* constraint force is -\sum_i lagr_i* 2 r */
++ /* so dh/dl = -\sum_i lagr_i* 2 r * dr/dl */
++ /* However, by comparison with lincs and with
++ comparison with a full thermodynamics cycle (see
++ redmine issue #1255), this is off by a factor of
++ two -- the 2r should apparently just be r. Further
++ investigation should be done at some point to
++ understand why and see if there is something deeper
++ we are missing */
++
++ bondA = idef->iparams[type].constr.dA;
++ bondB = idef->iparams[type].constr.dB;
++ dvdl += lagr[i] * dt_2 * ((1.0-lambda)*bondA + lambda*bondB) * (bondB-bondA);
+ }
+ *dvdlambda += dvdl;
+ }
+ }
+#ifdef DEBUG
+ fprintf(log, "tnit: %5d omega: %10.5f\n", tnit, omega);
+#endif
+ if (ir->bShakeSOR)
+ {
+ if (tnit > shaked->gamma)
+ {
+ shaked->delta *= -0.5;
+ }
+ shaked->omega += shaked->delta;
+ shaked->gamma = tnit;
+ }
+ inc_nrnb(nrnb, eNR_SHAKE, tnit);
+ inc_nrnb(nrnb, eNR_SHAKE_RIJ, trij);
+ if (v)
+ {
+ inc_nrnb(nrnb, eNR_CONSTR_V, trij*2);
+ }
+ if (bCalcVir)
+ {
+ inc_nrnb(nrnb, eNR_CONSTR_VIR, trij);
+ }
+
+ return TRUE;
+}
+
+void crattle(atom_id iatom[], int ncon, int *nnit, int maxnit,
+ real dist2[], real vp[], real rij[], real m2[], real omega,
+ real invmass[], real tt[], real lagr[], int *nerror, real invdt, t_vetavars *vetavar)
+{
+ /*
+ * r.c. van schaik and w.f. van gunsteren
+ * eth zuerich
+ * june 1992
+ * Adapted for use with Gromacs by David van der Spoel november 92 and later.
+ * rattle added by M.R. Shirts, April 2004, from code written by Jay Ponder in TINKER
+ * second part of rattle algorithm
+ */
+
+ const real mytol = 1e-10;
+
+ int ll, i, j, i3, j3, l3, ii;
+ int ix, iy, iz, jx, jy, jz;
+ real toler, rijd, vpijd, vx, vy, vz, diff, acor, xdotd, fac, im, jm, imdt, jmdt;
+ real xh, yh, zh, rijx, rijy, rijz;
+ real tix, tiy, tiz;
+ real tjx, tjy, tjz;
+ int nit, error, nconv;
+ real veta, vscale_nhc, iconvf;
+
+ veta = vetavar->veta;
+ vscale_nhc = vetavar->vscale_nhc[0]; /* for now, just use the first state */
+
+ error = 0;
+ nconv = 1;
+ for (nit = 0; (nit < maxnit) && (nconv != 0) && (error == 0); nit++)
+ {
+ nconv = 0;
+ for (ll = 0; (ll < ncon) && (error == 0); ll++)
+ {
+ l3 = 3*ll;
+ rijx = rij[l3+XX];
+ rijy = rij[l3+YY];
+ rijz = rij[l3+ZZ];
+ i = iatom[l3+1];
+ j = iatom[l3+2];
+ i3 = 3*i;
+ j3 = 3*j;
+ ix = i3+XX;
+ iy = i3+YY;
+ iz = i3+ZZ;
+ jx = j3+XX;
+ jy = j3+YY;
+ jz = j3+ZZ;
+ vx = vp[ix]-vp[jx];
+ vy = vp[iy]-vp[jy];
+ vz = vp[iz]-vp[jz];
+
+ vpijd = vx*rijx+vy*rijy+vz*rijz;
+ toler = dist2[ll];
+ /* this is r(t+dt) \dotproduct \dot{r}(t+dt) */
+ xdotd = vpijd*vscale_nhc + veta*toler;
+
+ /* iconv is zero when the error is smaller than a bound */
+ iconvf = fabs(xdotd)*(tt[ll]/invdt);
+
+ if (iconvf > 1)
+ {
+ nconv = iconvf;
+ fac = omega*2.0*m2[ll]/toler;
+ acor = -fac*xdotd;
+ lagr[ll] += acor;
+
+ xh = rijx*acor;
+ yh = rijy*acor;
+ zh = rijz*acor;
+
+ im = invmass[i]/vscale_nhc;
+ jm = invmass[j]/vscale_nhc;
+
+ vp[ix] += xh*im;
+ vp[iy] += yh*im;
+ vp[iz] += zh*im;
+ vp[jx] -= xh*jm;
+ vp[jy] -= yh*jm;
+ vp[jz] -= zh*jm;
+ }
+ }
+ }
+ *nnit = nit;
+ *nerror = error;
+}
--- /dev/null
- " not within 1 of (1.2, 3.1, 2.4)",
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2012, by the GROMACS development team, led by
+ * David van der Spoel, Berk Hess, 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.
+ */
+/*! \internal \file
+ * \brief
+ * Implements functions in selhelp.h.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#include <string>
+#include <vector>
+#include <utility>
+
+#include <boost/scoped_ptr.hpp>
+
+#include "gromacs/onlinehelp/helptopic.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/file.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "selhelp.h"
+#include "selmethod.h"
+#include "symrec.h"
+
+namespace
+{
+
+struct CommonHelpText
+{
+ static const char name[];
+ static const char title[];
+ static const char *const text[];
+};
+
+const char CommonHelpText::name[] = "selections";
+const char CommonHelpText::title[] =
+ "Selection syntax and usage";
+const char *const CommonHelpText::text[] = {
+ "Selections are used to select atoms/molecules/residues for analysis.",
+ "In contrast to traditional index files, selections can be dynamic, i.e.,",
+ "select different atoms for different trajectory frames.[PAR]",
+
+ "Each analysis tool requires a different number of selections and the",
+ "selections are interpreted differently. The general idea is still the",
+ "same: each selection evaluates to a set of positions, where a position",
+ "can be an atom position or center-of-mass or center-of-geometry of",
+ "a set of atoms. The tool then uses these positions for its analysis to",
+ "allow very flexible processing. Some analysis tools may have limitations",
+ "on the types of selections allowed.[PAR]",
+
+ "To get started with selections, run, e.g., [TT][PROGRAM] select[tt]",
+ "without specifying selections on the command-line and use the interactive",
+ "prompt to try out different selections.",
+ "This tool provides output options that allow one to see what is actually",
+ "selected by the given selections, and the interactive prompt reports",
+ "syntax errors immediately, allowing one to try again.",
+ "The subtopics listed below give more details on different aspects of",
+ "selections.",
+};
+
+struct ArithmeticHelpText
+{
+ static const char name[];
+ static const char title[];
+ static const char *const text[];
+};
+
+const char ArithmeticHelpText::name[] = "arithmetic";
+const char ArithmeticHelpText::title[] =
+ "Arithmetic expressions in selections";
+const char *const ArithmeticHelpText::text[] = {
+ "Basic arithmetic evaluation is supported for numeric expressions.",
+ "Supported operations are addition, subtraction, negation, multiplication,",
+ "division, and exponentiation (using ^).",
+ "Result of a division by zero or other illegal operations is undefined.",
+};
+
+struct CmdLineHelpText
+{
+ static const char name[];
+ static const char title[];
+ static const char *const text[];
+};
+
+const char CmdLineHelpText::name[] = "cmdline";
+const char CmdLineHelpText::title[] =
+ "Specifying selections from command line";
+const char *const CmdLineHelpText::text[] = {
+ "If no selections are provided on the command line, you are prompted to",
+ "type the selections interactively (a pipe can also be used to provide",
+ "the selections in this case for most tools). While this works well for",
+ "testing, it is easier to provide the selections from the command line",
+ "if they are complex or for scripting.[PAR]",
+
+ "Each tool has different command-line arguments for specifying selections",
+ "(listed by [TT][PROGRAM] help <tool>[tt]).",
+ "You can either pass a single string containing all selections (separated",
+ "by semicolons), or multiple strings, each containing one selection.",
+ "Note that you need to quote the selections to protect them from the",
+ "shell.[PAR]",
+
+ "If you set a selection command-line argument, but do not provide any",
+ "selections, you are prompted to type the selections for that argument",
+ "interactively. This is useful if that selection argument is optional,",
+ "in which case it is not normally prompted for.[PAR]",
+
+ "To provide selections from a file, use [TT]-sf file.dat[tt] in the place",
+ "of the selection for a selection argument (e.g.,",
+ "[TT]-select -sf file.dat[tt]). In general, the [TT]-sf[tt] argument reads",
+ "selections from the provided file and assigns them to selection arguments",
+ "that have been specified up to that point, but for which no selections",
+ "have been provided.",
+ "As a special case, [TT]-sf[tt] provided on its own, without preceding",
+ "selection arguments, assigns the selections to all (yet unset) required",
+ "selections (i.e., those that would be promted interactively if no",
+ "selections are provided on the command line).[PAR]",
+
+ "To use groups from a traditional index file, use argument [TT]-n[tt]",
+ "to provide a file. See the \"syntax\" subtopic for how to use them.",
+ "If this option is not provided, default groups are generated.",
+ "The default groups are generated by reading selections from a file",
+ "[TT]defselection.dat[tt]. If such a file is found in the current",
+ "directory, it is used instead of the one provided by default.[PAR]",
+
+ "Depending on the tool, two additional command-line arguments may be",
+ "available to control the behavior:[BR]",
+ "1. [TT]-seltype[tt] can be used to specify the default type of",
+ "positions to calculate for each selection.[BR]",
+ "2. [TT]-selrpos[tt] can be used to specify the default type of",
+ "positions used in selecting atoms by coordinates.[BR]",
+ "See the \"positions\" subtopic for more information on these options.",
+};
+
+struct EvaluationHelpText
+{
+ static const char name[];
+ static const char title[];
+ static const char *const text[];
+};
+
+const char EvaluationHelpText::name[] = "evaluation";
+const char EvaluationHelpText::title[] =
+ "Selection evaluation and optimization";
+const char *const EvaluationHelpText::text[] = {
+ "Boolean evaluation proceeds from left to right and is short-circuiting",
+ "i.e., as soon as it is known whether an atom will be selected, the",
+ "remaining expressions are not evaluated at all.",
+ "This can be used to optimize the selections: you should write the",
+ "most restrictive and/or the most inexpensive expressions first in",
+ "boolean expressions.",
+ "The relative ordering between dynamic and static expressions does not",
+ "matter: all static expressions are evaluated only once, before the first",
+ "frame, and the result becomes the leftmost expression.[PAR]",
+
+ "Another point for optimization is in common subexpressions: they are not",
+ "automatically recognized, but can be manually optimized by the use of",
+ "variables. This can have a big impact on the performance of complex",
+ "selections, in particular if you define several index groups like this:",
+ " [TT]rdist = distance from com of resnr 1 to 5;[tt][BR]",
+ " [TT]resname RES and rdist < 2;[tt][BR]",
+ " [TT]resname RES and rdist < 4;[tt][BR]",
+ " [TT]resname RES and rdist < 6;[tt][BR]",
+ "Without the variable assignment, the distances would be evaluated three",
+ "times, although they are exactly the same within each selection.",
+ "Anything assigned into a variable becomes a common subexpression that",
+ "is evaluated only once during a frame.",
+ "Currently, in some cases the use of variables can actually lead to a small",
+ "performance loss because of the checks necessary to determine for which",
+ "atoms the expression has already been evaluated, but this should not be",
+ "a major problem.",
+};
+
+struct ExamplesHelpText
+{
+ static const char name[];
+ static const char title[];
+ static const char *const text[];
+};
+
+const char ExamplesHelpText::name[] = "examples";
+const char ExamplesHelpText::title[] =
+ "Selection examples";
+const char *const ExamplesHelpText::text[] = {
+ // TODO: Once there are more tools available, use examples that invoke
+ // tools and explain what the selections do in those tools.
+ "Below, examples of increasingly complex selections are given.[PAR]",
+
+ "Selection of all water oxygens:[BR]",
+ " resname SOL and name OW",
+ "[PAR]",
+
+ "Centers of mass of residues 1 to 5 and 10:[BR]",
+ " res_com of resnr 1 to 5 10",
+ "[PAR]",
+
+ "All atoms farther than 1 nm of a fixed position:[BR]",
++ " not within 1 of [1.2, 3.1, 2.4]",
+ "[PAR]",
+
+ "All atoms of a residue LIG within 0.5 nm of a protein (with a custom name):[BR]",
+ " \"Close to protein\" resname LIG and within 0.5 of group \"Protein\"",
+ "[PAR]",
+
+ "All protein residues that have at least one atom within 0.5 nm of a residue LIG:[BR]",
+ " group \"Protein\" and same residue as within 0.5 of resname LIG",
+ "[PAR]",
+
+ "All RES residues whose COM is between 2 and 4 nm from the COM of all of them:[BR]",
+ " rdist = res_com distance from com of resname RES[BR]",
+ " resname RES and rdist >= 2 and rdist <= 4",
+ "[PAR]",
+
+ "Selection like C1 C2 C2 C3 C3 C4 ... C8 C9 (e.g., for g_bond):[BR]",
+ " name \"C[1-8]\" merge name \"C[2-9]\"",
+};
+
+struct KeywordsHelpText
+{
+ static const char name[];
+ static const char title[];
+ static const char *const text[];
+};
+
+const char KeywordsHelpText::name[] = "keywords";
+const char KeywordsHelpText::title[] =
+ "Selection keywords";
+const char *const KeywordsHelpText::text[] = {
+ "The following selection keywords are currently available.",
+ "For keywords marked with a star, additional help is available through",
+ "a subtopic KEYWORD, where KEYWORD is the name of the keyword.",
+};
+
+struct LimitationsHelpText
+{
+ static const char name[];
+ static const char title[];
+ static const char *const text[];
+};
+
+const char LimitationsHelpText::name[] = "limitations";
+const char LimitationsHelpText::title[] =
+ "Selection limitations";
+const char *const LimitationsHelpText::text[] = {
+ "Some analysis programs may require a special structure for the input",
+ "selections (e.g., [TT]g_angle[tt] requires the index group to be made",
+ "of groups of three or four atoms).",
+ "For such programs, it is up to the user to provide a proper selection",
+ "expression that always returns such positions.",
+ "[PAR]",
+
+ "Due to technical reasons, having a negative value as the first value in",
+ "expressions like[BR]",
+ "[TT]charge -1 to -0.7[tt][BR]",
+ "result in a syntax error. A workaround is to write[BR]",
+ "[TT]charge {-1 to -0.7}[tt][BR]",
+ "instead.[PAR]",
+
+ "When [TT]name[tt] selection keyword is used together with PDB input",
+ "files, the behavior may be unintuitive. When Gromacs reads in a PDB",
+ "file, 4 character atom names that start with a digit are transformed",
+ "such that, e.g., 1HG2 becomes HG21, and the latter is what is matched",
+ "by the [TT]name[tt] keyword. Use [TT]pdbname[tt] to match the atom name",
+ "as it appears in the input PDB file.",
+};
+
+struct PositionsHelpText
+{
+ static const char name[];
+ static const char title[];
+ static const char *const text[];
+};
+
+const char PositionsHelpText::name[] = "positions";
+const char PositionsHelpText::title[] =
+ "Specifying positions in selections";
+const char *const PositionsHelpText::text[] = {
+ "Possible ways of specifying positions in selections are:[PAR]",
+
+ "1. A constant position can be defined as [TT][XX, YY, ZZ][tt], where",
+ "[TT]XX[tt], [TT]YY[tt] and [TT]ZZ[tt] are real numbers.[PAR]",
+
+ "2. [TT]com of ATOM_EXPR [pbc][tt] or [TT]cog of ATOM_EXPR [pbc][tt]",
+ "calculate the center of mass/geometry of [TT]ATOM_EXPR[tt]. If",
+ "[TT]pbc[tt] is specified, the center is calculated iteratively to try",
+ "to deal with cases where [TT]ATOM_EXPR[tt] wraps around periodic",
+ "boundary conditions.[PAR]",
+
+ "3. [TT]POSTYPE of ATOM_EXPR[tt] calculates the specified positions for",
+ "the atoms in [TT]ATOM_EXPR[tt].",
+ "[TT]POSTYPE[tt] can be [TT]atom[tt], [TT]res_com[tt], [TT]res_cog[tt],",
+ "[TT]mol_com[tt] or [TT]mol_cog[tt], with an optional prefix [TT]whole_[tt]",
+ "[TT]part_[tt] or [TT]dyn_[tt].",
+ "[TT]whole_[tt] calculates the centers for the whole residue/molecule,",
+ "even if only part of it is selected.",
+ "[TT]part_[tt] prefix calculates the centers for the selected atoms, but",
+ "uses always the same atoms for the same residue/molecule. The used atoms",
+ "are determined from the the largest group allowed by the selection.",
+ "[TT]dyn_[tt] calculates the centers strictly only for the selected atoms.",
+ "If no prefix is specified, whole selections default to [TT]part_[tt] and",
+ "other places default to [TT]whole_[tt].",
+ "The latter is often desirable to select the same molecules in different",
+ "tools, while the first is a compromise between speed ([TT]dyn_[tt]",
+ "positions can be slower to evaluate than [TT]part_[tt]) and intuitive",
+ "behavior.[PAR]",
+
+ "4. [TT]ATOM_EXPR[tt], when given for whole selections, is handled as 3.",
+ "above, using the position type from the command-line argument",
+ "[TT]-seltype[tt].[PAR]",
+
+ "Selection keywords that select atoms based on their positions, such as",
+ "[TT]dist from[tt], use by default the positions defined by the",
+ "[TT]-selrpos[tt] command-line option.",
+ "This can be overridden by prepending a [TT]POSTYPE[tt] specifier to the",
+ "keyword. For example, [TT]res_com dist from POS[tt] evaluates the",
+ "residue center of mass distances. In the example, all atoms of a residue",
+ "are either selected or not, based on the single distance calculated.",
+};
+
+struct SyntaxHelpText
+{
+ static const char name[];
+ static const char title[];
+ static const char *const text[];
+};
+
+const char SyntaxHelpText::name[] = "syntax";
+const char SyntaxHelpText::title[] =
+ "Selection syntax";
+const char *const SyntaxHelpText::text[] = {
+ "A set of selections consists of one or more selections, separated by",
+ "semicolons. Each selection defines a set of positions for the analysis.",
+ "Each selection can also be preceded by a string that gives a name for",
+ "the selection for use in, e.g., graph legends.",
+ "If no name is provided, the string used for the selection is used",
+ "automatically as the name.[PAR]",
+
+ "For interactive input, the syntax is slightly altered: line breaks can",
+ "also be used to separate selections. \\ followed by a line break can",
+ "be used to continue a line if necessary.",
+ "Notice that the above only applies to real interactive input,",
+ "not if you provide the selections, e.g., from a pipe.[PAR]",
+
+ "It is possible to use variables to store selection expressions.",
+ "A variable is defined with the following syntax:[BR]",
+ "[TT]VARNAME = EXPR ;[tt][BR]",
+ "where [TT]EXPR[tt] is any valid selection expression.",
+ "After this, [TT]VARNAME[tt] can be used anywhere where [TT]EXPR[tt]",
+ "would be valid.[PAR]",
+
+ "Selections are composed of three main types of expressions, those that",
+ "define atoms ([TT]ATOM_EXPR[tt]s), those that define positions",
+ "([TT]POS_EXPR[tt]s), and those that evaluate to numeric values",
+ "([TT]NUM_EXPR[tt]s). Each selection should be a [TT]POS_EXPR[tt]",
+ "or a [TT]ATOM_EXPR[tt] (the latter is automatically converted to",
+ "positions). The basic rules are as follows:[BR]",
+ "1. An expression like [TT]NUM_EXPR1 < NUM_EXPR2[tt] evaluates to an",
+ "[TT]ATOM_EXPR[tt] that selects all the atoms for which the comparison",
+ "is true.[BR]",
+ "2. Atom expressions can be combined with boolean operations such as",
+ "[TT]not ATOM_EXPR[tt], [TT]ATOM_EXPR and ATOM_EXPR[tt], or",
+ "[TT]ATOM_EXPR or ATOM_EXPR[tt]. Parentheses can be used to alter the",
+ "evaluation order.[BR]",
+ "3. [TT]ATOM_EXPR[tt] expressions can be converted into [TT]POS_EXPR[tt]",
+ "expressions in various ways, see the \"positions\" subtopic for more",
+ "details.[PAR]",
+
+ "Some keywords select atoms based on string values such as the atom name.",
+ "For these keywords, it is possible to use wildcards ([TT]name \"C*\"[tt])",
+ "or regular expressions (e.g., [TT]resname \"R[AB]\"[tt]).",
+ "The match type is automatically guessed from the string: if it contains",
+ "other characters than letters, numbers, '*', or '?', it is interpreted",
+ "as a regular expression.",
+ "To force the matching to use literal string matching, use",
+ "[TT]name = \"C*\"[tt] to match a literal C*.",
+ "To force other type of matching, use '?' or '~' in place of '=' to force",
+ "wildcard or regular expression matching, respectively.[PAR]",
+
+ "Strings that contain non-alphanumeric characters should be enclosed in",
+ "double quotes as in the examples. For other strings, the quotes are",
+ "optional, but if the value conflicts with a reserved keyword, a syntax",
+ "error will occur. If your strings contain uppercase letters, this should",
+ "not happen.[PAR]",
+
+ "Index groups provided with the [TT]-n[tt] command-line option or",
+ "generated by default can be accessed with [TT]group NR[tt] or",
+ "[TT]group NAME[tt], where [TT]NR[tt] is a zero-based index of the group",
+ "and [TT]NAME[tt] is part of the name of the desired group.",
+ "The keyword [TT]group[tt] is optional if the whole selection is",
+ "provided from an index group.",
+ "To see a list of available groups in the interactive mode, press enter",
+ "in the beginning of a line.",
+};
+
+} // namespace
+
+namespace gmx
+{
+
+namespace
+{
+
+/*! \internal \brief
+ * Help topic implementation for an individual selection method.
+ *
+ * \ingroup module_selection
+ */
+class KeywordDetailsHelpTopic : public AbstractSimpleHelpTopic
+{
+ public:
+ //! Initialize help topic for the given selection method.
+ KeywordDetailsHelpTopic(const std::string &name,
+ const gmx_ana_selmethod_t &method)
+ : name_(name), method_(method)
+ {
+ }
+
+ virtual const char *name() const
+ {
+ return name_.c_str();
+ }
+ virtual const char *title() const
+ {
+ return NULL;
+ }
+
+ protected:
+ virtual std::string helpText() const
+ {
+ return concatenateStrings(method_.help.help, method_.help.nlhelp);
+ }
+
+ private:
+ std::string name_;
+ const gmx_ana_selmethod_t &method_;
+
+ GMX_DISALLOW_COPY_AND_ASSIGN(KeywordDetailsHelpTopic);
+};
+
+/*! \internal \brief
+ * Custom help topic for printing a list of selection keywords.
+ *
+ * \ingroup module_selection
+ */
+class KeywordsHelpTopic : public CompositeHelpTopic<KeywordsHelpText>
+{
+ public:
+ KeywordsHelpTopic();
+
+ virtual void writeHelp(const HelpWriterContext &context) const;
+
+ private:
+ /*! \brief
+ * Container for known selection methods.
+ *
+ * The first item in the pair is the name of the selection method, and
+ * the second points to the static data structure that describes the
+ * method.
+ * The name in the first item may differ from the name of the static
+ * data structure if an alias is defined for that method.
+ */
+ typedef std::vector<std::pair<std::string,
+ const gmx_ana_selmethod_t *> >
+ MethodList;
+
+ /*! \brief
+ * Prints a brief list of keywords (selection methods) available.
+ *
+ * \param[in] context Context for printing the help.
+ * \param[in] type Only methods that return this type are printed.
+ * \param[in] bModifiers If false, \ref SMETH_MODIFIER methods are
+ * excluded, otherwise only them are printed.
+ */
+ void printKeywordList(const HelpWriterContext &context,
+ e_selvalue_t type, bool bModifiers) const;
+
+ MethodList methods_;
+};
+
+KeywordsHelpTopic::KeywordsHelpTopic()
+{
+ // TODO: This is not a very elegant way of getting the list of selection
+ // methods, but this needs to be rewritten in any case if/when #652 is
+ // implemented.
+ boost::scoped_ptr<SelectionParserSymbolTable> symtab(
+ new SelectionParserSymbolTable);
+ gmx_ana_selmethod_register_defaults(symtab.get());
+
+ SelectionParserSymbolIterator symbol
+ = symtab->beginIterator(SelectionParserSymbol::MethodSymbol);
+ while (symbol != symtab->endIterator())
+ {
+ const std::string &symname = symbol->name();
+ const gmx_ana_selmethod_t *method = symbol->methodValue();
+ methods_.push_back(std::make_pair(std::string(symname), method));
+ if (method->help.nlhelp > 0 && method->help.help != NULL)
+ {
+ addSubTopic(HelpTopicPointer(
+ new KeywordDetailsHelpTopic(symname, *method)));
+ }
+ ++symbol;
+ }
+}
+
+void KeywordsHelpTopic::writeHelp(const HelpWriterContext &context) const
+{
+ if (context.outputFormat() != eHelpOutputFormat_Console)
+ {
+ GMX_THROW(NotImplementedError(
+ "Selection help is not implemented for this output format"));
+ }
+ // TODO: The markup here is not really appropriate, and printKeywordList()
+ // still prints raw text, but these are waiting for discussion of the
+ // markup format in #969.
+ writeBasicHelpTopic(context, *this, helpText());
+ context.writeTextBlock("[BR]");
+
+ // Print the list of keywords
+ context.writeTextBlock(
+ "Keywords that select atoms by an integer property:[BR]"
+ "(use in expressions or like \"atomnr 1 to 5 7 9\")[BR]");
+ printKeywordList(context, INT_VALUE, false);
+ context.writeTextBlock("[BR]");
+
+ context.writeTextBlock(
+ "Keywords that select atoms by a numeric property:[BR]"
+ "(use in expressions or like \"occupancy 0.5 to 1\")[BR]");
+ printKeywordList(context, REAL_VALUE, false);
+ context.writeTextBlock("[BR]");
+
+ context.writeTextBlock(
+ "Keywords that select atoms by a string property:[BR]"
+ "(use like \"name PATTERN [PATTERN] ...\")[BR]");
+ printKeywordList(context, STR_VALUE, false);
+ context.writeTextBlock("[BR]");
+
+ context.writeTextBlock(
+ "Additional keywords that directly select atoms:[BR]");
+ printKeywordList(context, GROUP_VALUE, false);
+ context.writeTextBlock("[BR]");
+
+ context.writeTextBlock(
+ "Keywords that directly evaluate to positions:[BR]"
+ "(see also \"positions\" subtopic)[BR]");
+ printKeywordList(context, POS_VALUE, false);
+ context.writeTextBlock("[BR]");
+
+ context.writeTextBlock("Additional keywords:[BR]");
+ printKeywordList(context, POS_VALUE, true);
+ printKeywordList(context, NO_VALUE, true);
+}
+
+void KeywordsHelpTopic::printKeywordList(const HelpWriterContext &context,
+ e_selvalue_t type,
+ bool bModifiers) const
+{
+ File &file = context.outputFile();
+ MethodList::const_iterator iter;
+ for (iter = methods_.begin(); iter != methods_.end(); ++iter)
+ {
+ const gmx_ana_selmethod_t &method = *iter->second;
+ bool bIsModifier = (method.flags & SMETH_MODIFIER) != 0;
+ if (method.type == type && bModifiers == bIsModifier)
+ {
+ bool bHasHelp = (method.help.nlhelp > 0 && method.help.help != NULL);
+ file.writeString(formatString(" %c ", bHasHelp ? '*' : ' '));
+ if (method.help.syntax != NULL)
+ {
+ file.writeLine(method.help.syntax);
+ }
+ else
+ {
+ std::string symname = iter->first;
+ if (symname != method.name)
+ {
+ symname.append(formatString(" (synonym for %s)", method.name));
+ }
+ file.writeLine(symname);
+ }
+ }
+ }
+}
+
+} // namespace
+
+/*! \cond internal */
+HelpTopicPointer createSelectionHelpTopic()
+{
+ CompositeHelpTopicPointer root(new CompositeHelpTopic<CommonHelpText>);
+ root->registerSubTopic<SimpleHelpTopic<ArithmeticHelpText> >();
+ root->registerSubTopic<SimpleHelpTopic<CmdLineHelpText> >();
+ root->registerSubTopic<SimpleHelpTopic<EvaluationHelpText> >();
+ root->registerSubTopic<SimpleHelpTopic<ExamplesHelpText> >();
+ root->registerSubTopic<KeywordsHelpTopic>();
+ root->registerSubTopic<SimpleHelpTopic<LimitationsHelpText> >();
+ root->registerSubTopic<SimpleHelpTopic<PositionsHelpText> >();
+ root->registerSubTopic<SimpleHelpTopic<SyntaxHelpText> >();
+ return move(root);
+}
+//! \endcond
+
+} // namespace gmx
--- /dev/null
- enerd->term[F_DVDL_CONSTR] += dvdl_constr;
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "typedefs.h"
+#include "smalloc.h"
+#include "sysstuff.h"
+#include "vec.h"
+#include "statutil.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 "trnio.h"
+#include "xtcio.h"
+#include "mdrun.h"
+#include "md_support.h"
+#include "md_logging.h"
+#include "confio.h"
+#include "network.h"
+#include "pull.h"
+#include "xvgr.h"
+#include "physics.h"
+#include "names.h"
+#include "xmdrun.h"
+#include "ionize.h"
+#include "disre.h"
+#include "orires.h"
+#include "pme.h"
+#include "mdatoms.h"
+#include "repl_ex.h"
+#include "qmmm.h"
+#include "domdec.h"
+#include "domdec_network.h"
+#include "partdec.h"
+#include "topsort.h"
+#include "coulomb.h"
+#include "constr.h"
+#include "shellfc.h"
+#include "compute_io.h"
+#include "mvdata.h"
+#include "checkpoint.h"
+#include "mtop_util.h"
+#include "sighandler.h"
+#include "txtdump.h"
+#include "string2.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"
+
+#ifdef GMX_FAHCORE
+#include "corewrap.h"
+#endif
+
+static void reset_all_counters(FILE *fplog, t_commrec *cr,
+ gmx_large_int_t step,
+ gmx_large_int_t *step_rel, t_inputrec *ir,
+ gmx_wallcycle_t wcycle, t_nrnb *nrnb,
+ gmx_runtime_t *runtime,
+ 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);
+ runtime_start(runtime);
+ print_date_and_time(fplog, cr->nodeid, "Restarted time", runtime);
+}
+
+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 *deviceOptions,
+ unsigned long Flags,
+ gmx_runtime_t *runtime)
+{
+ gmx_mdoutf_t *outf;
+ gmx_large_int_t step, step_rel;
+ double run_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;
+ int mdof_flags;
+ 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;
+ df_history_t df_history;
+ t_state *state = NULL;
+ rvec *f_global = NULL;
+ int n_xtc = -1;
+ rvec *x_xtc = 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_rng_t mcrng = NULL;
+ gmx_bool bFFscan;
+ 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 bIonize = FALSE;
+ gmx_bool bTCR = FALSE, bConverged = TRUE, bOK, bSumEkinhOld, bExchanged;
+ gmx_bool bAppend;
+ gmx_bool bResetCountersHalfMaxH = FALSE;
+ gmx_bool bVV, bIterativeCase, bFirstIterate, bTemp, bPres, bTrotter;
+ gmx_bool bUpdateDoLR;
+ real mu_aver = 0, dvdl_constr;
+ int a0, a1, gnx = 0, ii;
+ atom_id *grpindex = NULL;
+ char *grpname;
+ t_coupl_rec *tcr = NULL;
+ rvec *xcopy = NULL, *vcopy = NULL, *cbuf = NULL;
+ matrix boxcopy = {{0}}, lastbox;
+ tensor tmpvir;
+ real fom, oldfom, veta_save, pcurr, scalevir, tracevir;
+ real vetanew = 0;
+ int lamnew = 0;
+ /* for FEP */
+ int nstfep;
+ real rate;
+ 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_large_int_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;
+
+#ifdef GMX_FAHCORE
+ /* Temporary addition for FAHCORE checkpointing */
+ int chkpt_ret;
+#endif
+
+ /* Check for special mdrun options */
+ bRerunMD = (Flags & MD_RERUN);
+ bIonize = (Flags & MD_IONIZE);
+ bFFscan = (Flags & MD_FFSCAN);
+ 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"
+ " <n.list life time> - 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 || bFFscan)
+ {
+ ir->nstxtcout = 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, state_global, 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);
+ }
+
+ /* lambda Monte carlo random number generator */
+ if (ir->bExpanded)
+ {
+ mcrng = gmx_rng_init(ir->expandedvals->lmc_seed);
+ }
+ /* copy the state into df_history */
+ copy_df_history(&df_history, &state_global->dfhist);
+
+ /* 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,
+ top_global, n_flexible_constraints(constr),
+ (ir->bContinuation ||
+ (DOMAINDECOMP(cr) && !MASTER(cr))) ?
+ NULL : state_global->x);
+
+ if (DEFORM(*ir))
+ {
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_lock(&deform_init_box_mutex);
+#endif
+ set_deform_reference_box(upd,
+ deform_init_init_step_tpx,
+ deform_init_box_tpx);
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
+#endif
+ }
+
+ {
+ 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
+ {
+ if (PAR(cr))
+ {
+ /* Initialize the particle decomposition and split the topology */
+ top = split_system(fplog, top_global, ir, cr);
+
+ pd_cg_range(cr, &fr->cg0, &fr->hcg);
+ pd_at_range(cr, &a0, &a1);
+ }
+ else
+ {
+ top = gmx_mtop_generate_local_top(top_global, ir);
+
+ a0 = 0;
+ a1 = top_global->natoms;
+ }
+
+ forcerec_set_excl_load(fr, top, cr);
+
+ state = partdec_init_local_state(cr, state_global);
+ f_global = f;
+
+ atoms2md(top_global, ir, 0, NULL, a0, a1-a0, 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);
+ }
+
+ init_bonded_thread_force_reduction(fr, &top->idef);
+
+ if (ir->pull && PAR(cr))
+ {
+ dd_make_local_pull_groups(NULL, ir->pull, mdatoms);
+ }
+ }
+
+ 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 (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);
+ }
+
+ if ((state->flags & (1<<estLD_RNG)) && (Flags & MD_READ_RNG))
+ {
+ /* Set the random state if we read a checkpoint file */
+ set_stochd_state(upd, state);
+ }
+
+ if (state->flags & (1<<estMC_RNG))
+ {
+ set_mc_state(mcrng, state);
+ }
+
+ /* Initialize constraints */
+ if (constr)
+ {
+ if (!DOMAINDECOMP(cr))
+ {
+ set_constraints(constr, top, ir, mdatoms, cr);
+ }
+ }
+
+ /* Check whether we have to GCT stuff */
+ bTCR = ftp2bSet(efGCT, nfile, fnm);
+ if (bTCR)
+ {
+ if (MASTER(cr))
+ {
+ fprintf(stderr, "Will do General Coupling Theory!\n");
+ }
+ gnx = top_global->mols.nr;
+ snew(grpindex, gnx);
+ for (i = 0; (i < gnx); i++)
+ {
+ grpindex[i] = i;
+ }
+ }
+
+ 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.
+ * With perturbed charges with soft-core we should not change the cut-off.
+ */
+ if ((Flags & MD_TUNEPME) &&
+ EEL_PME(fr->eeltype) &&
+ ( (fr->cutoff_scheme == ecutsVERLET && fr->nbv->bUseGPU) || !(cr->duty & DUTY_PME)) &&
+ !(ir->efep != efepNO && mdatoms->nChargePerturbed > 0 && ir->fepvals->bScCoul) &&
+ !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<<estV)))
+ {
+ /* Set the velocities of frozen particles to zero */
+ for (i = mdatoms->start; i < mdatoms->start+mdatoms->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, f,
+ graph, cr, nrnb, fr, top, shake_vir);
+ }
+ if (vsite)
+ {
+ /* Construct the virtual sites for the initial configuration */
+ construct_vsites(fplog, vsite, state->x, nrnb, ir->delta_t, NULL,
+ top->idef.iparams, top->idef.il,
+ fr->ePBC, fr->bMolPBC, graph, cr, state->box);
+ }
+ }
+
+ debug_gmx();
+
+ /* set free energy calculation frequency as the minimum of nstdhdl, nstexpanded, and nstrepl_ex_nst*/
+ nstfep = ir->fepvals->nstdhdl;
+ if (ir->bExpanded && (nstfep > ir->expandedvals->nstexpanded))
+ {
+ nstfep = ir->expandedvals->nstexpanded;
+ }
+ if (repl_ex_nst > 0 && nstfep > repl_ex_nst)
+ {
+ nstfep = repl_ex_nst;
+ }
+
+ /* 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, &pcurr, top_global->natoms, &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, &pcurr, top_global->natoms, &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);
+ }
+ if (bFFscan)
+ {
+ snew(xcopy, state->natoms);
+ snew(vcopy, state->natoms);
+ copy_rvecn(state->x, xcopy, 0, state->natoms);
+ copy_rvecn(state->v, vcopy, 0, state->natoms);
+ copy_mat(state->box, boxcopy);
+ }
+
+ /* 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");
+ }
+
+ /* Set and write start time */
+ runtime_start(runtime);
+ print_date_and_time(fplog, cr->nodeid, "Started mdrun", runtime);
+ wallcycle_start(wcycle, ewcRUN);
+ if (fplog)
+ {
+ fprintf(fplog, "\n");
+ }
+
+ /* 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;
+
+ 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));
+ }
+
+ 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 for domain decomposition, use particle decomposition");
+ }
+ 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(fplog, vsite, state->x, nrnb, ir->delta_t, state->v,
+ top->idef.iparams, top->idef.il,
+ fr->ePBC, fr->bMolPBC, graph, 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));
+
+ /* Copy back starting coordinates in case we're doing a forcefield scan */
+ if (bFFscan)
+ {
+ for (ii = 0; (ii < state->natoms); ii++)
+ {
+ copy_rvec(xcopy[ii], state->x[ii]);
+ copy_rvec(vcopy[ii], state->v[ii]);
+ }
+ copy_mat(boxcopy, state->box);
+ }
+
+ 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 || bNStList || bDoFEP ||
+ (ir->nstlist == -1 && nlh.nabnsb > 0));
+
+ if (bNS && ir->nstlist == -1)
+ {
+ set_nlistheuristics(&nlh, bFirstStep || bExchanged || 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 ) && ( bNS || 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 && !bFFscan)
+ {
+ 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, &pcurr, top_global->natoms, &bSumEkinhOld,
+ CGLO_RERUNMD | CGLO_GSTAT | CGLO_TEMPERATURE);
+ }
+ clear_mat(force_vir);
+
+ /* Ionize the atoms if necessary */
+ if (bIonize)
+ {
+ ionize(fplog, oenv, mdatoms, top_global, t, ir, state->x, state->v,
+ mdatoms->start, mdatoms->start+mdatoms->homenr, state->box, cr);
+ }
+
+ /* Update force field in ffscan program */
+ if (bFFscan)
+ {
+ if (update_forcefield(fplog,
+ nfile, fnm, fr,
+ mdatoms->nr, state->x, state->box))
+ {
+ gmx_finalize_par();
+
+ exit(0);
+ }
+ }
+
+ /* 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, bFFscan ? step+1 : step,
+ ir, bNS, force_flags,
+ bStopCM, top, top_global,
+ constr, enerd, fcd,
+ state, f, force_vir, mdatoms,
+ nrnb, wcycle, graph, groups,
+ shellfc, fr, bBornRadii, t, mu_tot,
+ state->natoms, &bConverged, vsite,
+ outf->fp_field);
+ 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, top_global, groups,
+ state->box, state->x, &state->hist,
+ f, force_vir, mdatoms, enerd, fcd,
+ state->lambda, graph,
+ fr, vsite, mu_tot, t, outf->fp_field, ed, bBornRadii,
+ (bNS ? GMX_FORCE_NS : 0) | force_flags);
+ }
+
+ if (bTCR)
+ {
+ mu_aver = calc_mu_aver(cr, state->x, mdatoms->chargeA,
+ mu_tot, &top_global->mols, mdatoms, gnx, grpindex);
+ }
+
+ if (bTCR && bFirstStep)
+ {
+ tcr = init_coupling(fplog, nfile, fnm, cr, fr, mdatoms, &(top->idef));
+ fprintf(fplog, "Done init_coupling\n");
+ fflush(fplog);
+ }
+
+ 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.
+ */
+ 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, wcycle, 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, NULL,
+ cr, nrnb, wcycle, upd, constr,
+ bInitStep, TRUE, bCalcVir, vetanew);
+
+ if (!bOK && !bFFscan)
+ {
+ 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, &pcurr, top_global->natoms, &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, &pcurr, top_global->natoms, &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, 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, &df_history, step, mcrng, state->v, mdatoms);
+ }
+ /* ################## START TRAJECTORY OUTPUT ################# */
+
+ /* Now we have the energies and forces corresponding to the
+ * coordinates at time t. We must output all of this before
+ * the update.
+ * for RerunMD t is read from input trajectory
+ */
+ mdof_flags = 0;
+ if (do_per_step(step, ir->nstxout))
+ {
+ mdof_flags |= MDOF_X;
+ }
+ if (do_per_step(step, ir->nstvout))
+ {
+ mdof_flags |= MDOF_V;
+ }
+ if (do_per_step(step, ir->nstfout))
+ {
+ mdof_flags |= MDOF_F;
+ }
+ if (do_per_step(step, ir->nstxtcout))
+ {
+ mdof_flags |= MDOF_XTC;
+ }
+ if (bCPT)
+ {
+ mdof_flags |= MDOF_CPT;
+ }
+ ;
+
+#if defined(GMX_FAHCORE) || defined(GMX_WRITELASTSTEP)
+ if (bLastStep)
+ {
+ /* Enforce writing positions and velocities at end of run */
+ mdof_flags |= (MDOF_X | MDOF_V);
+ }
+#endif
+#ifdef GMX_FAHCORE
+ if (MASTER(cr))
+ {
+ fcReportProgress( ir->nsteps, step );
+ }
+
+ /* sync bCPT and fc record-keeping */
+ if (bCPT && MASTER(cr))
+ {
+ fcRequestCheckPoint();
+ }
+#endif
+
+ if (mdof_flags != 0)
+ {
+ wallcycle_start(wcycle, ewcTRAJ);
+ if (bCPT)
+ {
+ if (state->flags & (1<<estLD_RNG))
+ {
+ get_stochd_state(upd, state);
+ }
+ if (state->flags & (1<<estMC_RNG))
+ {
+ get_mc_state(mcrng, state);
+ }
+ if (MASTER(cr))
+ {
+ if (bSumEkinhOld)
+ {
+ state_global->ekinstate.bUpToDate = FALSE;
+ }
+ else
+ {
+ update_ekinstate(&state_global->ekinstate, ekind);
+ state_global->ekinstate.bUpToDate = TRUE;
+ }
+ update_energyhistory(&state_global->enerhist, mdebin);
+ if (ir->efep != efepNO || ir->bSimTemp)
+ {
+ state_global->fep_state = state->fep_state; /* MRS: seems kludgy. The code should be
+ structured so this isn't necessary.
+ Note this reassignment is only necessary
+ for single threads.*/
+ copy_df_history(&state_global->dfhist, &df_history);
+ }
+ }
+ }
+ write_traj(fplog, cr, outf, mdof_flags, top_global,
+ step, t, state, state_global, f, f_global, &n_xtc, &x_xtc);
+ if (bCPT)
+ {
+ nchkpt++;
+ bCPT = FALSE;
+ }
+ debug_gmx();
+ if (bLastStep && step_rel == ir->nsteps &&
+ (Flags & MD_CONFOUT) && MASTER(cr) &&
+ !bRerunMD && !bFFscan)
+ {
+ /* x and v have been collected in write_traj,
+ * because a checkpoint file will always be written
+ * at the last step.
+ */
+ fprintf(stderr, "\nWriting final coordinates.\n");
+ if (fr->bMolPBC)
+ {
+ /* Make molecules whole only for confout writing */
+ do_pbc_mtop(fplog, ir->ePBC, state->box, top_global, state_global->x);
+ }
+ write_sto_conf_mtop(ftp2fn(efSTO, nfile, fnm),
+ *top_global->name, top_global,
+ state_global->x, state_global->v,
+ ir->ePBC, state->box);
+ debug_gmx();
+ }
+ wallcycle_stop(wcycle, ewcTRAJ);
+ }
+
+ /* 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);
+ }
+ /* ################## END TRAJECTORY OUTPUT ################ */
+
+ /* Determine the wallclock run time up till now */
+ run_time = gmx_gettime() - (double)runtime->real;
+
+ /* 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 && run_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) &&
+ run_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 ||
+ run_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(fplog, step, ir, state, ekind, wcycle, upd, &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, mdatoms, state, upd, &top->idef, 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, NULL,
+ cr, nrnb, wcycle, upd, constr,
+ bInitStep, 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(fplog, step, ir, state, ekind, wcycle, upd, &MassQ, mdatoms);
+ update_pcouple(fplog, step, ir, state, pcoupl_mu, M, wcycle,
+ upd, 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, wcycle, 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, wcycle, 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, force_vir,
+ cr, nrnb, wcycle, upd, constr,
+ bInitStep, 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, &pcurr, top_global->natoms, &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, wcycle, 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, force_vir,
+ cr, nrnb, wcycle, upd, NULL,
+ bInitStep, FALSE, bCalcVir,
+ state->veta);
+ }
+ if (!bOK && !bFFscan)
+ {
+ gmx_fatal(FARGS, "Constraint error: Shake, Lincs or Settle could not solve the constrains");
+ }
+
+ if (fr->bSepDVDL && fplog && do_log)
+ {
+ fprintf(fplog, sepdvdlformat, "Constraint dV/dl", 0.0, dvdl_constr);
+ }
- /* only add constraint dvdl after constraints */
- enerd->term[F_DVDL_CONSTR] += 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(fplog, vsite, state->x, nrnb, ir->delta_t, state->v,
+ top->idef.iparams, top->idef.il,
+ fr->ePBC, fr->bMolPBC, graph, 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, &pcurr, top_global->natoms, &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, graph, f,
+ ir->nstlist == -1 ? &nlh.scale_tot : NULL, pcoupl_mu, nrnb, wcycle, upd, bInitStep, FALSE);
+
+ /* ################# END UPDATE STEP 2 ################# */
+ /* #### We now have r(t+dt) and v(t+dt/2) ############# */
+
+ /* The coordinates (x) were unshifted in update */
+ if (bFFscan && (shellfc == NULL || bConverged))
+ {
+ if (print_forcefield(fplog, enerd->term, mdatoms->homenr,
+ f, NULL, xcopy,
+ &(top_global->mols), mdatoms->massT, pres))
+ {
+ gmx_finalize_par();
+
+ fprintf(stderr, "\n");
+ exit(0);
+ }
+ }
+ if (!bGStat)
+ {
+ /* We will not sum ekinh_old,
+ * so signal that we still have to do it.
+ */
+ bSumEkinhOld = TRUE;
+ }
+
+ if (bTCR)
+ {
+ /* Only do GCT when the relaxation of shells (minimization) has converged,
+ * otherwise we might be coupling to bogus energies.
+ * In parallel we must always do this, because the other sims might
+ * update the FF.
+ */
+
+ /* Since this is called with the new coordinates state->x, I assume
+ * we want the new box state->box too. / EL 20040121
+ */
+ do_coupling(fplog, oenv, nfile, fnm, tcr, t, step, enerd->term, fr,
+ ir, MASTER(cr),
+ mdatoms, &(top->idef), mu_aver,
+ top_global->mols.nr, cr,
+ state->box, total_vir, pres,
+ mu_tot, state->x, f, bConverged);
+ debug_gmx();
+ }
+
+ /* ######### 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);
+ }
+ /* Check for excessively large energies */
+ if (bIonize)
+ {
+#ifdef GMX_DOUBLE
+ real etot_max = 1e200;
+#else
+ real etot_max = 1e30;
+#endif
+ if (fabs(enerd->term[F_ETOT]) > etot_max)
+ {
+ fprintf(stderr, "Energy too large (%g), giving up\n",
+ enerd->term[F_ETOT]);
+ }
+ }
+ /* ######### END PREPARING EDR OUTPUT ########### */
+
+ /* Time for performance */
+ if (((step % stepout) == 0) || bLastStep)
+ {
+ runtime_upd_proc(runtime);
+ }
+
+ /* 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,
+ &df_history, 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(outf->fp_ene, 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 */
+ state->fep_state = lamnew;
+ for (i = 0; i < efptNR; i++)
+ {
+ state_global->lambda[i] = ir->fepvals->all_lambda[i][lamnew];
+ }
+ }
+ /* Remaining runtime */
+ if (MULTIMASTER(cr) && (do_verbose || gmx_got_usr_signal()) && !bPMETuneRunning)
+ {
+ if (shellfc)
+ {
+ fprintf(stderr, "\n");
+ }
+ print_time(stderr, runtime, step, ir, cr);
+ }
+
+ /* 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 && 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<<estPRES_PREV)) &&
+ (bGStatEveryStep ||
+ (ir->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 = fr->ic->ewaldcoeff;
+ fr->rlist = fr->ic->rlist;
+ fr->rlistlong = fr->ic->rlistlong;
+ fr->rcoulomb = fr->ic->rcoulomb;
+ fr->rvdw = fr->ic->rvdw;
+ }
+ 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, runtime,
+ 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 -= run_time/(60.0*60.0);
+ bResetCountersHalfMaxH = FALSE;
+ gs.set[eglsRESETCOUNTERS] = 0;
+ }
+
+ }
+ /* End of main MD loop */
+ debug_gmx();
+
+ /* Stop the time */
+ runtime_end(runtime);
+
+ 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(outf->fp_ene, 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);
+ }
+
+ runtime->nsteps_done = step_rel;
+
+ return 0;
+}
--- /dev/null
- gmx_hw_opt_t *hw_opt,
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ *
+ * This source code is part of
+ *
+ * G R O M A C S
+ *
+ * GROningen MAchine for Chemical Simulations
+ *
+ * VERSION 3.2.0
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, 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 www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ *
+ * And Hey:
+ * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <signal.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <assert.h>
+
+#include "typedefs.h"
+#include "smalloc.h"
+#include "sysstuff.h"
+#include "statutil.h"
+#include "mdrun.h"
+#include "md_logging.h"
+#include "md_support.h"
+#include "network.h"
+#include "pull.h"
+#include "pull_rotation.h"
+#include "names.h"
+#include "disre.h"
+#include "orires.h"
+#include "pme.h"
+#include "mdatoms.h"
+#include "repl_ex.h"
+#include "qmmm.h"
+#include "domdec.h"
+#include "partdec.h"
+#include "coulomb.h"
+#include "constr.h"
+#include "mvdata.h"
+#include "checkpoint.h"
+#include "mtop_util.h"
+#include "sighandler.h"
+#include "tpxio.h"
+#include "txtdump.h"
+#include "gmx_detect_hardware.h"
+#include "gmx_omp_nthreads.h"
+#include "pull_rotation.h"
+#include "calc_verletbuf.h"
+#include "../mdlib/nbnxn_search.h"
+#include "../mdlib/nbnxn_consts.h"
+#include "gmx_fatal_collective.h"
+#include "membed.h"
+#include "macros.h"
+#include "gmx_omp.h"
+#include "gmx_thread_affinity.h"
+
+#include "gromacs/utility/gmxmpi.h"
+
+#ifdef GMX_FAHCORE
+#include "corewrap.h"
+#endif
+
+#include "gpu_utils.h"
+#include "nbnxn_cuda_data_mgmt.h"
+
+typedef struct {
+ gmx_integrator_t *func;
+} gmx_intp_t;
+
+/* The array should match the eI array in include/types/enums.h */
+const gmx_intp_t integrator[eiNR] = { {do_md}, {do_steep}, {do_cg}, {do_md}, {do_md}, {do_nm}, {do_lbfgs}, {do_tpi}, {do_tpi}, {do_md}, {do_md}, {do_md}};
+
+gmx_large_int_t deform_init_init_step_tpx;
+matrix deform_init_box_tpx;
+#ifdef GMX_THREAD_MPI
+tMPI_Thread_mutex_t deform_init_box_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
+#endif
+
+
+#ifdef GMX_THREAD_MPI
+struct mdrunner_arglist
+{
+ gmx_hw_opt_t *hw_opt;
+ FILE *fplog;
+ t_commrec *cr;
+ int nfile;
+ const t_filenm *fnm;
+ output_env_t oenv;
+ gmx_bool bVerbose;
+ gmx_bool bCompact;
+ int nstglobalcomm;
+ ivec ddxyz;
+ int dd_node_order;
+ real rdd;
+ real rconstr;
+ const char *dddlb_opt;
+ real dlb_scale;
+ const char *ddcsx;
+ const char *ddcsy;
+ const char *ddcsz;
+ const char *nbpu_opt;
+ gmx_large_int_t nsteps_cmdline;
+ int nstepout;
+ int resetstep;
+ int nmultisim;
+ int repl_ex_nst;
+ int repl_ex_nex;
+ int repl_ex_seed;
+ real pforce;
+ real cpt_period;
+ real max_hours;
+ const char *deviceOptions;
+ unsigned long Flags;
+ int ret; /* return value */
+};
+
+
+/* The function used for spawning threads. Extracts the mdrunner()
+ arguments from its one argument and calls mdrunner(), after making
+ a commrec. */
+static void mdrunner_start_fn(void *arg)
+{
+ struct mdrunner_arglist *mda = (struct mdrunner_arglist*)arg;
+ struct mdrunner_arglist mc = *mda; /* copy the arg list to make sure
+ that it's thread-local. This doesn't
+ copy pointed-to items, of course,
+ but those are all const. */
+ t_commrec *cr; /* we need a local version of this */
+ FILE *fplog = NULL;
+ t_filenm *fnm;
+
+ fnm = dup_tfn(mc.nfile, mc.fnm);
+
+ cr = init_par_threads(mc.cr);
+
+ if (MASTER(cr))
+ {
+ fplog = mc.fplog;
+ }
+
+ mda->ret = mdrunner(mc.hw_opt, fplog, cr, mc.nfile, fnm, mc.oenv,
+ mc.bVerbose, mc.bCompact, mc.nstglobalcomm,
+ mc.ddxyz, mc.dd_node_order, mc.rdd,
+ mc.rconstr, mc.dddlb_opt, mc.dlb_scale,
+ mc.ddcsx, mc.ddcsy, mc.ddcsz,
+ mc.nbpu_opt,
+ mc.nsteps_cmdline, mc.nstepout, mc.resetstep,
+ mc.nmultisim, mc.repl_ex_nst, mc.repl_ex_nex, mc.repl_ex_seed, mc.pforce,
+ mc.cpt_period, mc.max_hours, mc.deviceOptions, mc.Flags);
+}
+
+/* called by mdrunner() to start a specific number of threads (including
+ the main thread) for thread-parallel runs. This in turn calls mdrunner()
+ for each thread.
+ All options besides nthreads are the same as for mdrunner(). */
+static t_commrec *mdrunner_start_threads(gmx_hw_opt_t *hw_opt,
+ FILE *fplog, t_commrec *cr, int nfile,
+ const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
+ gmx_bool bCompact, int nstglobalcomm,
+ ivec ddxyz, int dd_node_order, real rdd, real rconstr,
+ const char *dddlb_opt, real dlb_scale,
+ const char *ddcsx, const char *ddcsy, const char *ddcsz,
+ const char *nbpu_opt,
+ gmx_large_int_t nsteps_cmdline,
+ int nstepout, int resetstep,
+ int nmultisim, int repl_ex_nst, int repl_ex_nex, int repl_ex_seed,
+ real pforce, real cpt_period, real max_hours,
+ const char *deviceOptions, unsigned long Flags)
+{
+ int ret;
+ struct mdrunner_arglist *mda;
+ t_commrec *crn; /* the new commrec */
+ t_filenm *fnmn;
+
+ /* first check whether we even need to start tMPI */
+ if (hw_opt->nthreads_tmpi < 2)
+ {
+ return cr;
+ }
+
+ /* a few small, one-time, almost unavoidable memory leaks: */
+ snew(mda, 1);
+ fnmn = dup_tfn(nfile, fnm);
+
+ /* fill the data structure to pass as void pointer to thread start fn */
+ mda->hw_opt = hw_opt;
+ mda->fplog = fplog;
+ mda->cr = cr;
+ mda->nfile = nfile;
+ mda->fnm = fnmn;
+ mda->oenv = oenv;
+ mda->bVerbose = bVerbose;
+ mda->bCompact = bCompact;
+ mda->nstglobalcomm = nstglobalcomm;
+ mda->ddxyz[XX] = ddxyz[XX];
+ mda->ddxyz[YY] = ddxyz[YY];
+ mda->ddxyz[ZZ] = ddxyz[ZZ];
+ mda->dd_node_order = dd_node_order;
+ mda->rdd = rdd;
+ mda->rconstr = rconstr;
+ mda->dddlb_opt = dddlb_opt;
+ mda->dlb_scale = dlb_scale;
+ mda->ddcsx = ddcsx;
+ mda->ddcsy = ddcsy;
+ mda->ddcsz = ddcsz;
+ mda->nbpu_opt = nbpu_opt;
+ mda->nsteps_cmdline = nsteps_cmdline;
+ mda->nstepout = nstepout;
+ mda->resetstep = resetstep;
+ mda->nmultisim = nmultisim;
+ mda->repl_ex_nst = repl_ex_nst;
+ mda->repl_ex_nex = repl_ex_nex;
+ mda->repl_ex_seed = repl_ex_seed;
+ mda->pforce = pforce;
+ mda->cpt_period = cpt_period;
+ mda->max_hours = max_hours;
+ mda->deviceOptions = deviceOptions;
+ mda->Flags = Flags;
+
+ /* now spawn new threads that start mdrunner_start_fn(), while
+ the main thread returns, we set thread affinity later */
+ ret = tMPI_Init_fn(TRUE, hw_opt->nthreads_tmpi, TMPI_AFFINITY_NONE,
+ mdrunner_start_fn, (void*)(mda) );
+ if (ret != TMPI_SUCCESS)
+ {
+ return NULL;
+ }
+
+ /* make a new comm_rec to reflect the new situation */
+ crn = init_par_threads(cr);
+ return crn;
+}
+
+
+static int get_tmpi_omp_thread_division(const gmx_hw_info_t *hwinfo,
+ const gmx_hw_opt_t *hw_opt,
+ int nthreads_tot,
+ int ngpu)
+{
+ int nthreads_tmpi;
+
+ /* There are no separate PME nodes here, as we ensured in
+ * check_and_update_hw_opt that nthreads_tmpi>0 with PME nodes
+ * and a conditional ensures we would not have ended up here.
+ * Note that separate PME nodes might be switched on later.
+ */
+ if (ngpu > 0)
+ {
+ nthreads_tmpi = ngpu;
+ if (nthreads_tot > 0 && nthreads_tot < nthreads_tmpi)
+ {
+ nthreads_tmpi = nthreads_tot;
+ }
+ }
+ else if (hw_opt->nthreads_omp > 0)
+ {
+ /* Here we could oversubscribe, when we do, we issue a warning later */
+ nthreads_tmpi = max(1, nthreads_tot/hw_opt->nthreads_omp);
+ }
+ else
+ {
+ /* TODO choose nthreads_omp based on hardware topology
+ when we have a hardware topology detection library */
+ /* In general, when running up to 4 threads, OpenMP should be faster.
+ * Note: on AMD Bulldozer we should avoid running OpenMP over two dies.
+ * On Intel>=Nehalem running OpenMP on a single CPU is always faster,
+ * even on two CPUs it's usually faster (but with many OpenMP threads
+ * it could be faster not to use HT, currently we always use HT).
+ * On Nehalem/Westmere we want to avoid running 16 threads over
+ * two CPUs with HT, so we need a limit<16; thus we use 12.
+ * A reasonable limit for Intel Sandy and Ivy bridge,
+ * not knowing the topology, is 16 threads.
+ */
+ const int nthreads_omp_always_faster = 4;
+ const int nthreads_omp_always_faster_Nehalem = 12;
+ const int nthreads_omp_always_faster_SandyBridge = 16;
+ const int first_model_Nehalem = 0x1A;
+ const int first_model_SandyBridge = 0x2A;
+ gmx_bool bIntel_Family6;
+
+ bIntel_Family6 =
+ (gmx_cpuid_vendor(hwinfo->cpuid_info) == GMX_CPUID_VENDOR_INTEL &&
+ gmx_cpuid_family(hwinfo->cpuid_info) == 6);
+
+ if (nthreads_tot <= nthreads_omp_always_faster ||
+ (bIntel_Family6 &&
+ ((gmx_cpuid_model(hwinfo->cpuid_info) >= nthreads_omp_always_faster_Nehalem && nthreads_tot <= nthreads_omp_always_faster_Nehalem) ||
+ (gmx_cpuid_model(hwinfo->cpuid_info) >= nthreads_omp_always_faster_SandyBridge && nthreads_tot <= nthreads_omp_always_faster_SandyBridge))))
+ {
+ /* Use pure OpenMP parallelization */
+ nthreads_tmpi = 1;
+ }
+ else
+ {
+ /* Don't use OpenMP parallelization */
+ nthreads_tmpi = nthreads_tot;
+ }
+ }
+
+ return nthreads_tmpi;
+}
+
+
+/* Get the number of threads to use for thread-MPI based on how many
+ * were requested, which algorithms we're using,
+ * and how many particles there are.
+ * At the point we have already called check_and_update_hw_opt.
+ * Thus all options should be internally consistent and consistent
+ * with the hardware, except that ntmpi could be larger than #GPU.
+ */
+static int get_nthreads_mpi(gmx_hw_info_t *hwinfo,
+ gmx_hw_opt_t *hw_opt,
+ t_inputrec *inputrec, gmx_mtop_t *mtop,
+ const t_commrec *cr,
+ FILE *fplog)
+{
+ int nthreads_hw, nthreads_tot_max, nthreads_tmpi, nthreads_new, ngpu;
+ int min_atoms_per_mpi_thread;
+ char *env;
+ char sbuf[STRLEN];
+ gmx_bool bCanUseGPU;
+
+ if (hw_opt->nthreads_tmpi > 0)
+ {
+ /* Trivial, return right away */
+ return hw_opt->nthreads_tmpi;
+ }
+
+ nthreads_hw = hwinfo->nthreads_hw_avail;
+
+ /* How many total (#tMPI*#OpenMP) threads can we start? */
+ if (hw_opt->nthreads_tot > 0)
+ {
+ nthreads_tot_max = hw_opt->nthreads_tot;
+ }
+ else
+ {
+ nthreads_tot_max = nthreads_hw;
+ }
+
+ bCanUseGPU = (inputrec->cutoff_scheme == ecutsVERLET && hwinfo->bCanUseGPU);
+ if (bCanUseGPU)
+ {
+ ngpu = hwinfo->gpu_info.ncuda_dev_use;
+ }
+ else
+ {
+ ngpu = 0;
+ }
+
+ nthreads_tmpi =
+ get_tmpi_omp_thread_division(hwinfo, hw_opt, nthreads_tot_max, ngpu);
+
+ if (inputrec->eI == eiNM || EI_TPI(inputrec->eI))
+ {
+ /* Steps are divided over the nodes iso splitting the atoms */
+ min_atoms_per_mpi_thread = 0;
+ }
+ else
+ {
+ if (bCanUseGPU)
+ {
+ min_atoms_per_mpi_thread = MIN_ATOMS_PER_GPU;
+ }
+ else
+ {
+ min_atoms_per_mpi_thread = MIN_ATOMS_PER_MPI_THREAD;
+ }
+ }
+
+ /* Check if an algorithm does not support parallel simulation. */
+ if (nthreads_tmpi != 1 &&
+ ( inputrec->eI == eiLBFGS ||
+ inputrec->coulombtype == eelEWALD ) )
+ {
+ nthreads_tmpi = 1;
+
+ md_print_warn(cr, fplog, "The integration or electrostatics algorithm doesn't support parallel runs. Using a single thread-MPI thread.\n");
+ if (hw_opt->nthreads_tmpi > nthreads_tmpi)
+ {
+ gmx_fatal(FARGS, "You asked for more than 1 thread-MPI thread, but an algorithm doesn't support that");
+ }
+ }
+ else if (mtop->natoms/nthreads_tmpi < min_atoms_per_mpi_thread)
+ {
+ /* the thread number was chosen automatically, but there are too many
+ threads (too few atoms per thread) */
+ nthreads_new = max(1, mtop->natoms/min_atoms_per_mpi_thread);
+
+ /* Avoid partial use of Hyper-Threading */
+ if (gmx_cpuid_x86_smt(hwinfo->cpuid_info) == GMX_CPUID_X86_SMT_ENABLED &&
+ nthreads_new > nthreads_hw/2 && nthreads_new < nthreads_hw)
+ {
+ nthreads_new = nthreads_hw/2;
+ }
+
+ /* Avoid large prime numbers in the thread count */
+ if (nthreads_new >= 6)
+ {
+ /* Use only 6,8,10 with additional factors of 2 */
+ int fac;
+
+ fac = 2;
+ while (3*fac*2 <= nthreads_new)
+ {
+ fac *= 2;
+ }
+
+ nthreads_new = (nthreads_new/fac)*fac;
+ }
+ else
+ {
+ /* Avoid 5 */
+ if (nthreads_new == 5)
+ {
+ nthreads_new = 4;
+ }
+ }
+
+ nthreads_tmpi = nthreads_new;
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "NOTE: Parallelization is limited by the small number of atoms,\n");
+ fprintf(stderr, " only starting %d thread-MPI threads.\n", nthreads_tmpi);
+ fprintf(stderr, " You can use the -nt and/or -ntmpi option to optimize the number of threads.\n\n");
+ }
+
+ return nthreads_tmpi;
+}
+#endif /* GMX_THREAD_MPI */
+
+
+/* Environment variable for setting nstlist */
+static const char* NSTLIST_ENVVAR = "GMX_NSTLIST";
+/* Try to increase nstlist when using a GPU with nstlist less than this */
+static const int NSTLIST_GPU_ENOUGH = 20;
+/* Increase nstlist until the non-bonded cost increases more than this factor */
+static const float NBNXN_GPU_LIST_OK_FAC = 1.25;
+/* Don't increase nstlist beyond a non-bonded cost increases of this factor */
+static const float NBNXN_GPU_LIST_MAX_FAC = 1.40;
+
+/* Try to increase nstlist when running on a GPU */
+static void increase_nstlist(FILE *fp, t_commrec *cr,
+ t_inputrec *ir, const gmx_mtop_t *mtop, matrix box)
+{
+ char *env;
+ int nstlist_orig, nstlist_prev;
+ verletbuf_list_setup_t ls;
+ real rlist_inc, rlist_ok, rlist_max, rlist_new, rlist_prev;
+ int i;
+ t_state state_tmp;
+ gmx_bool bBox, bDD, bCont;
+ const char *nstl_fmt = "\nFor optimal performance with a GPU nstlist (now %d) should be larger.\nThe optimum depends on your CPU and GPU resources.\nYou might want to try several nstlist values.\n";
+ const char *vbd_err = "Can not increase nstlist for GPU run because verlet-buffer-drift is not set or used";
+ const char *box_err = "Can not increase nstlist for GPU run because the box is too small";
+ const char *dd_err = "Can not increase nstlist for GPU run because of domain decomposition limitations";
+ char buf[STRLEN];
+
+ /* Number of + nstlist alternative values to try when switching */
+ const int nstl[] = { 20, 25, 40, 50 };
+#define NNSTL sizeof(nstl)/sizeof(nstl[0])
+
+ env = getenv(NSTLIST_ENVVAR);
+ if (env == NULL)
+ {
+ if (fp != NULL)
+ {
+ fprintf(fp, nstl_fmt, ir->nstlist);
+ }
+ }
+
+ if (ir->verletbuf_drift == 0)
+ {
+ gmx_fatal(FARGS, "You are using an old tpr file with a GPU, please generate a new tpr file with an up to date version of grompp");
+ }
+
+ if (ir->verletbuf_drift < 0)
+ {
+ if (MASTER(cr))
+ {
+ fprintf(stderr, "%s\n", vbd_err);
+ }
+ if (fp != NULL)
+ {
+ fprintf(fp, "%s\n", vbd_err);
+ }
+
+ return;
+ }
+
+ nstlist_orig = ir->nstlist;
+ if (env != NULL)
+ {
+ sprintf(buf, "Getting nstlist from environment variable GMX_NSTLIST=%s", env);
+ if (MASTER(cr))
+ {
+ fprintf(stderr, "%s\n", buf);
+ }
+ if (fp != NULL)
+ {
+ fprintf(fp, "%s\n", buf);
+ }
+ sscanf(env, "%d", &ir->nstlist);
+ }
+
+ verletbuf_get_list_setup(TRUE, &ls);
+
+ /* Allow rlist to make the list double the size of the cut-off sphere */
+ rlist_inc = nbnxn_get_rlist_effective_inc(NBNXN_GPU_CLUSTER_SIZE, mtop->natoms/det(box));
+ rlist_ok = (max(ir->rvdw, ir->rcoulomb) + rlist_inc)*pow(NBNXN_GPU_LIST_OK_FAC, 1.0/3.0) - rlist_inc;
+ rlist_max = (max(ir->rvdw, ir->rcoulomb) + rlist_inc)*pow(NBNXN_GPU_LIST_MAX_FAC, 1.0/3.0) - rlist_inc;
+ if (debug)
+ {
+ fprintf(debug, "GPU nstlist tuning: rlist_inc %.3f rlist_max %.3f\n",
+ rlist_inc, rlist_max);
+ }
+
+ i = 0;
+ nstlist_prev = nstlist_orig;
+ rlist_prev = ir->rlist;
+ do
+ {
+ if (env == NULL)
+ {
+ ir->nstlist = nstl[i];
+ }
+
+ /* Set the pair-list buffer size in ir */
+ calc_verlet_buffer_size(mtop, det(box), ir, ir->verletbuf_drift, &ls,
+ NULL, &rlist_new);
+
+ /* Does rlist fit in the box? */
+ bBox = (sqr(rlist_new) < max_cutoff2(ir->ePBC, box));
+ bDD = TRUE;
+ if (bBox && DOMAINDECOMP(cr))
+ {
+ /* Check if rlist fits in the domain decomposition */
+ if (inputrec2nboundeddim(ir) < DIM)
+ {
+ gmx_incons("Changing nstlist with domain decomposition and unbounded dimensions is not implemented yet");
+ }
+ copy_mat(box, state_tmp.box);
+ bDD = change_dd_cutoff(cr, &state_tmp, ir, rlist_new);
+ }
+
+ bCont = FALSE;
+
+ if (env == NULL)
+ {
+ if (bBox && bDD && rlist_new <= rlist_max)
+ {
+ /* Increase nstlist */
+ nstlist_prev = ir->nstlist;
+ rlist_prev = rlist_new;
+ bCont = (i+1 < NNSTL && rlist_new < rlist_ok);
+ }
+ else
+ {
+ /* Stick with the previous nstlist */
+ ir->nstlist = nstlist_prev;
+ rlist_new = rlist_prev;
+ bBox = TRUE;
+ bDD = TRUE;
+ }
+ }
+
+ i++;
+ }
+ while (bCont);
+
+ if (!bBox || !bDD)
+ {
+ gmx_warning(!bBox ? box_err : dd_err);
+ if (fp != NULL)
+ {
+ fprintf(fp, "\n%s\n", bBox ? box_err : dd_err);
+ }
+ ir->nstlist = nstlist_orig;
+ }
+ else if (ir->nstlist != nstlist_orig || rlist_new != ir->rlist)
+ {
+ sprintf(buf, "Changing nstlist from %d to %d, rlist from %g to %g",
+ nstlist_orig, ir->nstlist,
+ ir->rlist, rlist_new);
+ if (MASTER(cr))
+ {
+ fprintf(stderr, "%s\n\n", buf);
+ }
+ if (fp != NULL)
+ {
+ fprintf(fp, "%s\n\n", buf);
+ }
+ ir->rlist = rlist_new;
+ ir->rlistlong = rlist_new;
+ }
+}
+
+static void prepare_verlet_scheme(FILE *fplog,
+ gmx_hw_info_t *hwinfo,
+ t_commrec *cr,
- prepare_verlet_scheme(fplog, hwinfo, cr, hw_opt, nbpu_opt,
+ const char *nbpu_opt,
+ t_inputrec *ir,
+ const gmx_mtop_t *mtop,
+ matrix box,
+ gmx_bool *bUseGPU)
+{
+ /* Here we only check for GPU usage on the MPI master process,
+ * as here we don't know how many GPUs we will use yet.
+ * We check for a GPU on all processes later.
+ */
+ *bUseGPU = hwinfo->bCanUseGPU || (getenv("GMX_EMULATE_GPU") != NULL);
+
+ if (ir->verletbuf_drift > 0)
+ {
+ /* Update the Verlet buffer size for the current run setup */
+ verletbuf_list_setup_t ls;
+ real rlist_new;
+
+ /* Here we assume CPU acceleration is on. But as currently
+ * calc_verlet_buffer_size gives the same results for 4x8 and 4x4
+ * and 4x2 gives a larger buffer than 4x4, this is ok.
+ */
+ verletbuf_get_list_setup(*bUseGPU, &ls);
+
+ calc_verlet_buffer_size(mtop, det(box), ir,
+ ir->verletbuf_drift, &ls,
+ NULL, &rlist_new);
+ if (rlist_new != ir->rlist)
+ {
+ if (fplog != NULL)
+ {
+ fprintf(fplog, "\nChanging rlist from %g to %g for non-bonded %dx%d atom kernels\n\n",
+ ir->rlist, rlist_new,
+ ls.cluster_size_i, ls.cluster_size_j);
+ }
+ ir->rlist = rlist_new;
+ ir->rlistlong = rlist_new;
+ }
+ }
+
+ /* With GPU or emulation we should check nstlist for performance */
+ if ((EI_DYNAMICS(ir->eI) &&
+ *bUseGPU &&
+ ir->nstlist < NSTLIST_GPU_ENOUGH) ||
+ getenv(NSTLIST_ENVVAR) != NULL)
+ {
+ /* Choose a better nstlist */
+ increase_nstlist(fplog, cr, ir, mtop, box);
+ }
+}
+
+static void convert_to_verlet_scheme(FILE *fplog,
+ t_inputrec *ir,
+ gmx_mtop_t *mtop, real box_vol)
+{
+ char *conv_mesg = "Converting input file with group cut-off scheme to the Verlet cut-off scheme";
+
+ md_print_warn(NULL, fplog, "%s\n", conv_mesg);
+
+ ir->cutoff_scheme = ecutsVERLET;
+ ir->verletbuf_drift = 0.005;
+
+ if (ir->rcoulomb != ir->rvdw)
+ {
+ gmx_fatal(FARGS, "The VdW and Coulomb cut-offs are different, whereas the Verlet scheme only supports equal cut-offs");
+ }
+
+ if (ir->vdwtype == evdwUSER || EEL_USER(ir->coulombtype))
+ {
+ gmx_fatal(FARGS, "User non-bonded potentials are not (yet) supported with the Verlet scheme");
+ }
+ else if (EVDW_SWITCHED(ir->vdwtype) || EEL_SWITCHED(ir->coulombtype))
+ {
+ md_print_warn(NULL, fplog, "Converting switched or shifted interactions to a shifted potential (without force shift), this will lead to slightly different interaction potentials");
+
+ if (EVDW_SWITCHED(ir->vdwtype))
+ {
+ ir->vdwtype = evdwCUT;
+ }
+ if (EEL_SWITCHED(ir->coulombtype))
+ {
+ if (EEL_FULL(ir->coulombtype))
+ {
+ /* With full electrostatic only PME can be switched */
+ ir->coulombtype = eelPME;
+ }
+ else
+ {
+ md_print_warn(NULL, fplog, "NOTE: Replacing %s electrostatics with reaction-field with epsilon-rf=inf\n", eel_names[ir->coulombtype]);
+ ir->coulombtype = eelRF;
+ ir->epsilon_rf = 0.0;
+ }
+ }
+
+ /* We set the target energy drift to a small number.
+ * Note that this is only for testing. For production the user
+ * should think about this and set the mdp options.
+ */
+ ir->verletbuf_drift = 1e-4;
+ }
+
+ if (inputrec2nboundeddim(ir) != 3)
+ {
+ gmx_fatal(FARGS, "Can only convert old tpr files to the Verlet cut-off scheme with 3D pbc");
+ }
+
+ if (ir->efep != efepNO || ir->implicit_solvent != eisNO)
+ {
+ gmx_fatal(FARGS, "Will not convert old tpr files to the Verlet cut-off scheme with free-energy calculations or implicit solvent");
+ }
+
+ if (EI_DYNAMICS(ir->eI) && !(EI_MD(ir->eI) && ir->etc == etcNO))
+ {
+ verletbuf_list_setup_t ls;
+
+ verletbuf_get_list_setup(FALSE, &ls);
+ calc_verlet_buffer_size(mtop, box_vol, ir, ir->verletbuf_drift, &ls,
+ NULL, &ir->rlist);
+ }
+ else
+ {
+ ir->verletbuf_drift = -1;
+ ir->rlist = 1.05*max(ir->rvdw, ir->rcoulomb);
+ }
+
+ gmx_mtop_remove_chargegroups(mtop);
+}
+
+static void check_and_update_hw_opt(gmx_hw_opt_t *hw_opt,
+ int cutoff_scheme,
+ gmx_bool bIsSimMaster)
+{
+ gmx_omp_nthreads_read_env(&hw_opt->nthreads_omp, bIsSimMaster);
+
+#ifndef GMX_THREAD_MPI
+ if (hw_opt->nthreads_tot > 0)
+ {
+ gmx_fatal(FARGS, "Setting the total number of threads is only supported with thread-MPI and Gromacs was compiled without thread-MPI");
+ }
+ if (hw_opt->nthreads_tmpi > 0)
+ {
+ gmx_fatal(FARGS, "Setting the number of thread-MPI threads is only supported with thread-MPI and Gromacs was compiled without thread-MPI");
+ }
+#endif
+
+ if (hw_opt->nthreads_tot > 0 && hw_opt->nthreads_omp_pme <= 0)
+ {
+ /* We have the same number of OpenMP threads for PP and PME processes,
+ * thus we can perform several consistency checks.
+ */
+ if (hw_opt->nthreads_tmpi > 0 &&
+ hw_opt->nthreads_omp > 0 &&
+ hw_opt->nthreads_tot != hw_opt->nthreads_tmpi*hw_opt->nthreads_omp)
+ {
+ gmx_fatal(FARGS, "The total number of threads requested (%d) does not match the thread-MPI threads (%d) times the OpenMP threads (%d) requested",
+ hw_opt->nthreads_tot, hw_opt->nthreads_tmpi, hw_opt->nthreads_omp);
+ }
+
+ if (hw_opt->nthreads_tmpi > 0 &&
+ hw_opt->nthreads_tot % hw_opt->nthreads_tmpi != 0)
+ {
+ gmx_fatal(FARGS, "The total number of threads requested (%d) is not divisible by the number of thread-MPI threads requested (%d)",
+ hw_opt->nthreads_tot, hw_opt->nthreads_tmpi);
+ }
+
+ if (hw_opt->nthreads_omp > 0 &&
+ hw_opt->nthreads_tot % hw_opt->nthreads_omp != 0)
+ {
+ gmx_fatal(FARGS, "The total number of threads requested (%d) is not divisible by the number of OpenMP threads requested (%d)",
+ hw_opt->nthreads_tot, hw_opt->nthreads_omp);
+ }
+
+ if (hw_opt->nthreads_tmpi > 0 &&
+ hw_opt->nthreads_omp <= 0)
+ {
+ hw_opt->nthreads_omp = hw_opt->nthreads_tot/hw_opt->nthreads_tmpi;
+ }
+ }
+
+#ifndef GMX_OPENMP
+ if (hw_opt->nthreads_omp > 1)
+ {
+ gmx_fatal(FARGS, "OpenMP threads are requested, but Gromacs was compiled without OpenMP support");
+ }
+#endif
+
+ if (cutoff_scheme == ecutsGROUP)
+ {
+ /* We only have OpenMP support for PME only nodes */
+ if (hw_opt->nthreads_omp > 1)
+ {
+ gmx_fatal(FARGS, "OpenMP threads have been requested with cut-off scheme %s, but these are only supported with cut-off scheme %s",
+ ecutscheme_names[cutoff_scheme],
+ ecutscheme_names[ecutsVERLET]);
+ }
+ hw_opt->nthreads_omp = 1;
+ }
+
+ if (hw_opt->nthreads_omp_pme > 0 && hw_opt->nthreads_omp <= 0)
+ {
+ gmx_fatal(FARGS, "You need to specify -ntomp in addition to -ntomp_pme");
+ }
+
+ if (hw_opt->nthreads_tot == 1)
+ {
+ hw_opt->nthreads_tmpi = 1;
+
+ if (hw_opt->nthreads_omp > 1)
+ {
+ gmx_fatal(FARGS, "You requested %d OpenMP threads with %d total threads",
+ hw_opt->nthreads_tmpi, hw_opt->nthreads_tot);
+ }
+ hw_opt->nthreads_omp = 1;
+ }
+
+ if (hw_opt->nthreads_omp_pme <= 0 && hw_opt->nthreads_omp > 0)
+ {
+ hw_opt->nthreads_omp_pme = hw_opt->nthreads_omp;
+ }
+
+ if (debug)
+ {
+ fprintf(debug, "hw_opt: nt %d ntmpi %d ntomp %d ntomp_pme %d gpu_id '%s'\n",
+ hw_opt->nthreads_tot,
+ hw_opt->nthreads_tmpi,
+ hw_opt->nthreads_omp,
+ hw_opt->nthreads_omp_pme,
+ hw_opt->gpu_id != NULL ? hw_opt->gpu_id : "");
+
+ }
+}
+
+
+/* Override the value in inputrec with value passed on the command line (if any) */
+static void override_nsteps_cmdline(FILE *fplog,
+ gmx_large_int_t nsteps_cmdline,
+ t_inputrec *ir,
+ const t_commrec *cr)
+{
+ char sbuf[STEPSTRSIZE];
+
+ assert(ir);
+ assert(cr);
+
+ /* override with anything else than the default -2 */
+ if (nsteps_cmdline > -2)
+ {
+ char stmp[STRLEN];
+
+ ir->nsteps = nsteps_cmdline;
+ if (EI_DYNAMICS(ir->eI))
+ {
+ sprintf(stmp, "Overriding nsteps with value passed on the command line: %s steps, %.3f ps",
+ gmx_step_str(nsteps_cmdline, sbuf),
+ nsteps_cmdline*ir->delta_t);
+ }
+ else
+ {
+ sprintf(stmp, "Overriding nsteps with value passed on the command line: %s steps",
+ gmx_step_str(nsteps_cmdline, sbuf));
+ }
+
+ md_print_warn(cr, fplog, "%s\n", stmp);
+ }
+}
+
+/* Data structure set by SIMMASTER which needs to be passed to all nodes
+ * before the other nodes have read the tpx file and called gmx_detect_hardware.
+ */
+typedef struct {
+ int cutoff_scheme; /* The cutoff scheme from inputrec_t */
+ gmx_bool bUseGPU; /* Use GPU or GPU emulation */
+} master_inf_t;
+
+int mdrunner(gmx_hw_opt_t *hw_opt,
+ FILE *fplog, t_commrec *cr, int nfile,
+ const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
+ gmx_bool bCompact, int nstglobalcomm,
+ ivec ddxyz, int dd_node_order, real rdd, real rconstr,
+ const char *dddlb_opt, real dlb_scale,
+ const char *ddcsx, const char *ddcsy, const char *ddcsz,
+ const char *nbpu_opt,
+ gmx_large_int_t nsteps_cmdline, int nstepout, int resetstep,
+ int nmultisim, int repl_ex_nst, int repl_ex_nex,
+ int repl_ex_seed, real pforce, real cpt_period, real max_hours,
+ const char *deviceOptions, unsigned long Flags)
+{
+ gmx_bool bForceUseGPU, bTryUseGPU;
+ double nodetime = 0, realtime;
+ t_inputrec *inputrec;
+ t_state *state = NULL;
+ matrix box;
+ gmx_ddbox_t ddbox = {0};
+ int npme_major, npme_minor;
+ real tmpr1, tmpr2;
+ t_nrnb *nrnb;
+ gmx_mtop_t *mtop = NULL;
+ t_mdatoms *mdatoms = NULL;
+ t_forcerec *fr = NULL;
+ t_fcdata *fcd = NULL;
+ real ewaldcoeff = 0;
+ gmx_pme_t *pmedata = NULL;
+ gmx_vsite_t *vsite = NULL;
+ gmx_constr_t constr;
+ int i, m, nChargePerturbed = -1, status, nalloc;
+ char *gro;
+ gmx_wallcycle_t wcycle;
+ gmx_bool bReadRNG, bReadEkin;
+ int list;
+ gmx_runtime_t runtime;
+ int rc;
+ gmx_large_int_t reset_counters;
+ gmx_edsam_t ed = NULL;
+ t_commrec *cr_old = cr;
+ int nthreads_pme = 1;
+ int nthreads_pp = 1;
+ gmx_membed_t membed = NULL;
+ gmx_hw_info_t *hwinfo = NULL;
+ master_inf_t minf = {-1, FALSE};
+
+ /* CAUTION: threads may be started later on in this function, so
+ cr doesn't reflect the final parallel state right now */
+ snew(inputrec, 1);
+ snew(mtop, 1);
+
+ if (Flags & MD_APPENDFILES)
+ {
+ fplog = NULL;
+ }
+
+ bForceUseGPU = (strncmp(nbpu_opt, "gpu", 3) == 0);
+ bTryUseGPU = (strncmp(nbpu_opt, "auto", 4) == 0) || bForceUseGPU;
+
+ snew(state, 1);
+ if (SIMMASTER(cr))
+ {
+ /* Read (nearly) all data required for the simulation */
+ read_tpx_state(ftp2fn(efTPX, nfile, fnm), inputrec, state, NULL, mtop);
+
+ if (inputrec->cutoff_scheme != ecutsVERLET &&
+ ((Flags & MD_TESTVERLET) || getenv("GMX_VERLET_SCHEME") != NULL))
+ {
+ convert_to_verlet_scheme(fplog, inputrec, mtop, det(state->box));
+ }
+
+ /* Detect hardware, gather information. With tMPI only thread 0 does it
+ * and after threads are started broadcasts hwinfo around. */
+ snew(hwinfo, 1);
+ gmx_detect_hardware(fplog, hwinfo, cr,
+ bForceUseGPU, bTryUseGPU, hw_opt->gpu_id);
+
+ minf.cutoff_scheme = inputrec->cutoff_scheme;
+ minf.bUseGPU = FALSE;
+
+ if (inputrec->cutoff_scheme == ecutsVERLET)
+ {
- /* All-vs-all loops do not work with domain decomposition */
++ prepare_verlet_scheme(fplog, hwinfo, cr, nbpu_opt,
+ inputrec, mtop, state->box,
+ &minf.bUseGPU);
+ }
+ else if (hwinfo->bCanUseGPU)
+ {
+ md_print_warn(cr, fplog,
+ "NOTE: GPU(s) found, but the current simulation can not use GPUs\n"
+ " To use a GPU, set the mdp option: cutoff-scheme = Verlet\n"
+ " (for quick performance testing you can use the -testverlet option)\n");
+
+ if (bForceUseGPU)
+ {
+ gmx_fatal(FARGS, "GPU requested, but can't be used without cutoff-scheme=Verlet");
+ }
+ }
+ }
+#ifndef GMX_THREAD_MPI
+ if (PAR(cr))
+ {
+ gmx_bcast_sim(sizeof(minf), &minf, cr);
+ }
+#endif
+ if (minf.bUseGPU && cr->npmenodes == -1)
+ {
+ /* Don't automatically use PME-only nodes with GPUs */
+ cr->npmenodes = 0;
+ }
+
+ /* Check for externally set OpenMP affinity and turn off internal
+ * pinning if any is found. We need to do this check early to tell
+ * thread-MPI whether it should do pinning when spawning threads.
+ * TODO: the above no longer holds, we should move these checks down
+ */
+ gmx_omp_check_thread_affinity(fplog, cr, hw_opt);
+
+#ifdef GMX_THREAD_MPI
+ /* With thread-MPI inputrec is only set here on the master thread */
+ if (SIMMASTER(cr))
+#endif
+ {
+ check_and_update_hw_opt(hw_opt, minf.cutoff_scheme, SIMMASTER(cr));
+
+#ifdef GMX_THREAD_MPI
+ /* Early check for externally set process affinity. Can't do over all
+ * MPI processes because hwinfo is not available everywhere, but with
+ * thread-MPI it's needed as pinning might get turned off which needs
+ * to be known before starting thread-MPI. */
+ gmx_check_thread_affinity_set(fplog,
+ NULL,
+ hw_opt, hwinfo->nthreads_hw_avail, FALSE);
+#endif
+
+#ifdef GMX_THREAD_MPI
+ if (cr->npmenodes > 0 && hw_opt->nthreads_tmpi <= 0)
+ {
+ gmx_fatal(FARGS, "You need to explicitly specify the number of MPI threads (-ntmpi) when using separate PME nodes");
+ }
+#endif
+
+ if (hw_opt->nthreads_omp_pme != hw_opt->nthreads_omp &&
+ cr->npmenodes <= 0)
+ {
+ gmx_fatal(FARGS, "You need to explicitly specify the number of PME nodes (-npme) when using different number of OpenMP threads for PP and PME nodes");
+ }
+ }
+
+#ifdef GMX_THREAD_MPI
+ if (SIMMASTER(cr))
+ {
+ /* NOW the threads will be started: */
+ hw_opt->nthreads_tmpi = get_nthreads_mpi(hwinfo,
+ hw_opt,
+ inputrec, mtop,
+ cr, fplog);
+ if (hw_opt->nthreads_tot > 0 && hw_opt->nthreads_omp <= 0)
+ {
+ hw_opt->nthreads_omp = hw_opt->nthreads_tot/hw_opt->nthreads_tmpi;
+ }
+
+ if (hw_opt->nthreads_tmpi > 1)
+ {
+ /* now start the threads. */
+ cr = mdrunner_start_threads(hw_opt, fplog, cr_old, nfile, fnm,
+ oenv, bVerbose, bCompact, nstglobalcomm,
+ ddxyz, dd_node_order, rdd, rconstr,
+ dddlb_opt, dlb_scale, ddcsx, ddcsy, ddcsz,
+ nbpu_opt,
+ nsteps_cmdline, nstepout, resetstep, nmultisim,
+ repl_ex_nst, repl_ex_nex, repl_ex_seed, pforce,
+ cpt_period, max_hours, deviceOptions,
+ Flags);
+ /* the main thread continues here with a new cr. We don't deallocate
+ the old cr because other threads may still be reading it. */
+ if (cr == NULL)
+ {
+ gmx_comm("Failed to spawn threads");
+ }
+ }
+ }
+#endif
+ /* END OF CAUTION: cr is now reliable */
+
+ /* g_membed initialisation *
+ * Because we change the mtop, init_membed is called before the init_parallel *
+ * (in case we ever want to make it run in parallel) */
+ if (opt2bSet("-membed", nfile, fnm))
+ {
+ if (MASTER(cr))
+ {
+ fprintf(stderr, "Initializing membed");
+ }
+ membed = init_membed(fplog, nfile, fnm, mtop, inputrec, state, cr, &cpt_period);
+ }
+
+ if (PAR(cr))
+ {
+ /* now broadcast everything to the non-master nodes/threads: */
+ init_parallel(fplog, cr, inputrec, mtop);
+
+ /* This check needs to happen after get_nthreads_mpi() */
+ if (inputrec->cutoff_scheme == ecutsVERLET && (Flags & MD_PARTDEC))
+ {
+ gmx_fatal_collective(FARGS, cr, NULL,
+ "The Verlet cut-off scheme is not supported with particle decomposition.\n"
+ "You can achieve the same effect as particle decomposition by running in parallel using only OpenMP threads.");
+ }
+ }
+ if (fplog != NULL)
+ {
+ pr_inputrec(fplog, 0, "Input Parameters", inputrec, FALSE);
+ }
+
+#if defined GMX_THREAD_MPI
+ /* With tMPI we detected on thread 0 and we'll just pass the hwinfo pointer
+ * to the other threads -- slightly uncool, but works fine, just need to
+ * make sure that the data doesn't get freed twice. */
+ if (cr->nnodes > 1)
+ {
+ if (!SIMMASTER(cr))
+ {
+ snew(hwinfo, 1);
+ }
+ gmx_bcast(sizeof(&hwinfo), &hwinfo, cr);
+ }
+#else
+ if (PAR(cr) && !SIMMASTER(cr))
+ {
+ /* now we have inputrec on all nodes, can run the detection */
+ /* TODO: perhaps it's better to propagate within a node instead? */
+ snew(hwinfo, 1);
+ gmx_detect_hardware(fplog, hwinfo, cr,
+ bForceUseGPU, bTryUseGPU, hw_opt->gpu_id);
+ }
+
+ /* Now do the affinity check with MPI/no-MPI (done earlier with thread-MPI). */
+ gmx_check_thread_affinity_set(fplog, cr,
+ hw_opt, hwinfo->nthreads_hw_avail, FALSE);
+#endif
+
+ /* now make sure the state is initialized and propagated */
+ set_state_entries(state, inputrec, cr->nnodes);
+
+ /* A parallel command line option consistency check that we can
+ only do after any threads have started. */
+ if (!PAR(cr) &&
+ (ddxyz[XX] > 1 || ddxyz[YY] > 1 || ddxyz[ZZ] > 1 || cr->npmenodes > 0))
+ {
+ gmx_fatal(FARGS,
+ "The -dd or -npme option request a parallel simulation, "
+#ifndef GMX_MPI
+ "but %s was compiled without threads or MPI enabled"
+#else
+#ifdef GMX_THREAD_MPI
+ "but the number of threads (option -nt) is 1"
+#else
+ "but %s was not started through mpirun/mpiexec or only one process was requested through mpirun/mpiexec"
+#endif
+#endif
+ , ShortProgram()
+ );
+ }
+
+ if ((Flags & MD_RERUN) &&
+ (EI_ENERGY_MINIMIZATION(inputrec->eI) || eiNM == inputrec->eI))
+ {
+ gmx_fatal(FARGS, "The .mdp file specified an energy mininization or normal mode algorithm, and these are not compatible with mdrun -rerun");
+ }
+
+ if (can_use_allvsall(inputrec, mtop, TRUE, cr, fplog) && PAR(cr))
+ {
- finish_rot(fplog, inputrec->rot);
++ /* Simple neighbour searching and (also?) all-vs-all loops
++ * do not work with domain decomposition. */
+ Flags |= MD_PARTDEC;
+ }
+
+ if (!EEL_PME(inputrec->coulombtype) || (Flags & MD_PARTDEC))
+ {
+ if (cr->npmenodes > 0)
+ {
+ if (!EEL_PME(inputrec->coulombtype))
+ {
+ gmx_fatal_collective(FARGS, cr, NULL,
+ "PME nodes are requested, but the system does not use PME electrostatics");
+ }
+ if (Flags & MD_PARTDEC)
+ {
+ gmx_fatal_collective(FARGS, cr, NULL,
+ "PME nodes are requested, but particle decomposition does not support separate PME nodes");
+ }
+ }
+
+ cr->npmenodes = 0;
+ }
+
+#ifdef GMX_FAHCORE
+ fcRegisterSteps(inputrec->nsteps, inputrec->init_step);
+#endif
+
+ /* NMR restraints must be initialized before load_checkpoint,
+ * since with time averaging the history is added to t_state.
+ * For proper consistency check we therefore need to extend
+ * t_state here.
+ * So the PME-only nodes (if present) will also initialize
+ * the distance restraints.
+ */
+ snew(fcd, 1);
+
+ /* This needs to be called before read_checkpoint to extend the state */
+ init_disres(fplog, mtop, inputrec, cr, Flags & MD_PARTDEC, fcd, state, repl_ex_nst > 0);
+
+ if (gmx_mtop_ftype_count(mtop, F_ORIRES) > 0)
+ {
+ if (PAR(cr) && !(Flags & MD_PARTDEC))
+ {
+ gmx_fatal(FARGS, "Orientation restraints do not work (yet) with domain decomposition, use particle decomposition (mdrun option -pd)");
+ }
+ /* Orientation restraints */
+ if (MASTER(cr))
+ {
+ init_orires(fplog, mtop, state->x, inputrec, cr->ms, &(fcd->orires),
+ state);
+ }
+ }
+
+ if (DEFORM(*inputrec))
+ {
+ /* Store the deform reference box before reading the checkpoint */
+ if (SIMMASTER(cr))
+ {
+ copy_mat(state->box, box);
+ }
+ if (PAR(cr))
+ {
+ gmx_bcast(sizeof(box), box, cr);
+ }
+ /* Because we do not have the update struct available yet
+ * in which the reference values should be stored,
+ * we store them temporarily in static variables.
+ * This should be thread safe, since they are only written once
+ * and with identical values.
+ */
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_lock(&deform_init_box_mutex);
+#endif
+ deform_init_init_step_tpx = inputrec->init_step;
+ copy_mat(box, deform_init_box_tpx);
+#ifdef GMX_THREAD_MPI
+ tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
+#endif
+ }
+
+ if (opt2bSet("-cpi", nfile, fnm))
+ {
+ /* Check if checkpoint file exists before doing continuation.
+ * This way we can use identical input options for the first and subsequent runs...
+ */
+ if (gmx_fexist_master(opt2fn_master("-cpi", nfile, fnm, cr), cr) )
+ {
+ load_checkpoint(opt2fn_master("-cpi", nfile, fnm, cr), &fplog,
+ cr, Flags & MD_PARTDEC, ddxyz,
+ inputrec, state, &bReadRNG, &bReadEkin,
+ (Flags & MD_APPENDFILES),
+ (Flags & MD_APPENDFILESSET));
+
+ if (bReadRNG)
+ {
+ Flags |= MD_READ_RNG;
+ }
+ if (bReadEkin)
+ {
+ Flags |= MD_READ_EKIN;
+ }
+ }
+ }
+
+ if (((MASTER(cr) || (Flags & MD_SEPPOT)) && (Flags & MD_APPENDFILES))
+#ifdef GMX_THREAD_MPI
+ /* With thread MPI only the master node/thread exists in mdrun.c,
+ * therefore non-master nodes need to open the "seppot" log file here.
+ */
+ || (!MASTER(cr) && (Flags & MD_SEPPOT))
+#endif
+ )
+ {
+ gmx_log_open(ftp2fn(efLOG, nfile, fnm), cr, !(Flags & MD_SEPPOT),
+ Flags, &fplog);
+ }
+
+ /* override nsteps with value from cmdline */
+ override_nsteps_cmdline(fplog, nsteps_cmdline, inputrec, cr);
+
+ if (SIMMASTER(cr))
+ {
+ copy_mat(state->box, box);
+ }
+
+ if (PAR(cr))
+ {
+ gmx_bcast(sizeof(box), box, cr);
+ }
+
+ /* Essential dynamics */
+ if (opt2bSet("-ei", nfile, fnm))
+ {
+ /* Open input and output files, allocate space for ED data structure */
+ ed = ed_open(mtop->natoms, &state->edsamstate, nfile, fnm, Flags, oenv, cr);
+ }
+
+ if (PAR(cr) && !((Flags & MD_PARTDEC) ||
+ EI_TPI(inputrec->eI) ||
+ inputrec->eI == eiNM))
+ {
+ cr->dd = init_domain_decomposition(fplog, cr, Flags, ddxyz, rdd, rconstr,
+ dddlb_opt, dlb_scale,
+ ddcsx, ddcsy, ddcsz,
+ mtop, inputrec,
+ box, state->x,
+ &ddbox, &npme_major, &npme_minor);
+
+ make_dd_communicators(fplog, cr, dd_node_order);
+
+ /* Set overallocation to avoid frequent reallocation of arrays */
+ set_over_alloc_dd(TRUE);
+ }
+ else
+ {
+ /* PME, if used, is done on all nodes with 1D decomposition */
+ cr->npmenodes = 0;
+ cr->duty = (DUTY_PP | DUTY_PME);
+ npme_major = 1;
+ npme_minor = 1;
+ if (!EI_TPI(inputrec->eI))
+ {
+ npme_major = cr->nnodes;
+ }
+
+ if (inputrec->ePBC == epbcSCREW)
+ {
+ gmx_fatal(FARGS,
+ "pbc=%s is only implemented with domain decomposition",
+ epbc_names[inputrec->ePBC]);
+ }
+ }
+
+ if (PAR(cr))
+ {
+ /* After possible communicator splitting in make_dd_communicators.
+ * we can set up the intra/inter node communication.
+ */
+ gmx_setup_nodecomm(fplog, cr);
+ }
+
+ /* Initialize per-physical-node MPI process/thread ID and counters. */
+ gmx_init_intranode_counters(cr);
+
+#ifdef GMX_MPI
+ md_print_info(cr, fplog, "Using %d MPI %s\n",
+ cr->nnodes,
+#ifdef GMX_THREAD_MPI
+ cr->nnodes == 1 ? "thread" : "threads"
+#else
+ cr->nnodes == 1 ? "process" : "processes"
+#endif
+ );
+ fflush(stderr);
+#endif
+
+ gmx_omp_nthreads_init(fplog, cr,
+ hwinfo->nthreads_hw_avail,
+ hw_opt->nthreads_omp,
+ hw_opt->nthreads_omp_pme,
+ (cr->duty & DUTY_PP) == 0,
+ inputrec->cutoff_scheme == ecutsVERLET);
+
+ gmx_check_hw_runconf_consistency(fplog, hwinfo, cr, hw_opt->nthreads_tmpi, minf.bUseGPU);
+
+ /* getting number of PP/PME threads
+ PME: env variable should be read only on one node to make sure it is
+ identical everywhere;
+ */
+ /* TODO nthreads_pp is only used for pinning threads.
+ * This is a temporary solution until we have a hw topology library.
+ */
+ nthreads_pp = gmx_omp_nthreads_get(emntNonbonded);
+ nthreads_pme = gmx_omp_nthreads_get(emntPME);
+
+ wcycle = wallcycle_init(fplog, resetstep, cr, nthreads_pp, nthreads_pme);
+
+ if (PAR(cr))
+ {
+ /* Master synchronizes its value of reset_counters with all nodes
+ * including PME only nodes */
+ reset_counters = wcycle_get_reset_counters(wcycle);
+ gmx_bcast_sim(sizeof(reset_counters), &reset_counters, cr);
+ wcycle_set_reset_counters(wcycle, reset_counters);
+ }
+
+ snew(nrnb, 1);
+ if (cr->duty & DUTY_PP)
+ {
+ /* For domain decomposition we allocate dynamically
+ * in dd_partition_system.
+ */
+ if (DOMAINDECOMP(cr))
+ {
+ bcast_state_setup(cr, state);
+ }
+ else
+ {
+ if (PAR(cr))
+ {
+ bcast_state(cr, state, TRUE);
+ }
+ }
+
+ /* Initiate forcerecord */
+ fr = mk_forcerec();
+ fr->hwinfo = hwinfo;
+ init_forcerec(fplog, oenv, fr, fcd, inputrec, mtop, cr, box, FALSE,
+ opt2fn("-table", nfile, fnm),
+ opt2fn("-tabletf", nfile, fnm),
+ opt2fn("-tablep", nfile, fnm),
+ opt2fn("-tableb", nfile, fnm),
+ nbpu_opt,
+ FALSE, pforce);
+
+ /* version for PCA_NOT_READ_NODE (see md.c) */
+ /*init_forcerec(fplog,fr,fcd,inputrec,mtop,cr,box,FALSE,
+ "nofile","nofile","nofile","nofile",FALSE,pforce);
+ */
+ fr->bSepDVDL = ((Flags & MD_SEPPOT) == MD_SEPPOT);
+
+ /* Initialize QM-MM */
+ if (fr->bQMMM)
+ {
+ init_QMMMrec(cr, box, mtop, inputrec, fr);
+ }
+
+ /* Initialize the mdatoms structure.
+ * mdatoms is not filled with atom data,
+ * as this can not be done now with domain decomposition.
+ */
+ mdatoms = init_mdatoms(fplog, mtop, inputrec->efep != efepNO);
+
+ if (mdatoms->nPerturbed > 0 && inputrec->cutoff_scheme == ecutsVERLET)
+ {
+ gmx_fatal(FARGS, "The Verlet cut-off scheme does not (yet) support free-energy calculations with perturbed atoms, only perturbed interactions. This will be implemented soon. Use the group scheme for now.");
+ }
+
+ /* Initialize the virtual site communication */
+ vsite = init_vsite(mtop, cr, FALSE);
+
+ calc_shifts(box, fr->shift_vec);
+
+ /* With periodic molecules the charge groups should be whole at start up
+ * and the virtual sites should not be far from their proper positions.
+ */
+ if (!inputrec->bContinuation && MASTER(cr) &&
+ !(inputrec->ePBC != epbcNONE && inputrec->bPeriodicMols))
+ {
+ /* Make molecules whole at start of run */
+ if (fr->ePBC != epbcNONE)
+ {
+ do_pbc_first_mtop(fplog, inputrec->ePBC, box, mtop, state->x);
+ }
+ if (vsite)
+ {
+ /* Correct initial vsite positions are required
+ * for the initial distribution in the domain decomposition
+ * and for the initial shell prediction.
+ */
+ construct_vsites_mtop(fplog, vsite, mtop, state->x);
+ }
+ }
+
+ if (EEL_PME(fr->eeltype))
+ {
+ ewaldcoeff = fr->ewaldcoeff;
+ pmedata = &fr->pmedata;
+ }
+ else
+ {
+ pmedata = NULL;
+ }
+ }
+ else
+ {
+ /* This is a PME only node */
+
+ /* We don't need the state */
+ done_state(state);
+
+ ewaldcoeff = calc_ewaldcoeff(inputrec->rcoulomb, inputrec->ewald_rtol);
+ snew(pmedata, 1);
+ }
+
+ if (hw_opt->thread_affinity != threadaffOFF)
+ {
+ /* Before setting affinity, check whether the affinity has changed
+ * - which indicates that probably the OpenMP library has changed it
+ * since we first checked).
+ */
+ gmx_check_thread_affinity_set(fplog, cr,
+ hw_opt, hwinfo->nthreads_hw_avail, TRUE);
+
+ /* Set the CPU affinity */
+ gmx_set_thread_affinity(fplog, cr, hw_opt, nthreads_pme, hwinfo, inputrec);
+ }
+
+ /* Initiate PME if necessary,
+ * either on all nodes or on dedicated PME nodes only. */
+ if (EEL_PME(inputrec->coulombtype))
+ {
+ if (mdatoms)
+ {
+ nChargePerturbed = mdatoms->nChargePerturbed;
+ }
+ if (cr->npmenodes > 0)
+ {
+ /* The PME only nodes need to know nChargePerturbed */
+ gmx_bcast_sim(sizeof(nChargePerturbed), &nChargePerturbed, cr);
+ }
+
+ if (cr->duty & DUTY_PME)
+ {
+ status = gmx_pme_init(pmedata, cr, npme_major, npme_minor, inputrec,
+ mtop ? mtop->natoms : 0, nChargePerturbed,
+ (Flags & MD_REPRODUCIBLE), nthreads_pme);
+ if (status != 0)
+ {
+ gmx_fatal(FARGS, "Error %d initializing PME", status);
+ }
+ }
+ }
+
+
+ if (integrator[inputrec->eI].func == do_md)
+ {
+ /* Turn on signal handling on all nodes */
+ /*
+ * (A user signal from the PME nodes (if any)
+ * is communicated to the PP nodes.
+ */
+ signal_handler_install();
+ }
+
+ if (cr->duty & DUTY_PP)
+ {
+ if (inputrec->ePull != epullNO)
+ {
+ /* Initialize pull code */
+ init_pull(fplog, inputrec, nfile, fnm, mtop, cr, oenv, inputrec->fepvals->init_lambda,
+ EI_DYNAMICS(inputrec->eI) && MASTER(cr), Flags);
+ }
+
+ if (inputrec->bRot)
+ {
+ /* Initialize enforced rotation code */
+ init_rot(fplog, inputrec, nfile, fnm, cr, state->x, box, mtop, oenv,
+ bVerbose, Flags);
+ }
+
+ constr = init_constraints(fplog, mtop, inputrec, ed, state, cr);
+
+ if (DOMAINDECOMP(cr))
+ {
+ dd_init_bondeds(fplog, cr->dd, mtop, vsite, constr, inputrec,
+ Flags & MD_DDBONDCHECK, fr->cginfo_mb);
+
+ set_dd_parameters(fplog, cr->dd, dlb_scale, inputrec, fr, &ddbox);
+
+ setup_dd_grid(fplog, cr->dd);
+ }
+
+ /* Now do whatever the user wants us to do (how flexible...) */
+ integrator[inputrec->eI].func(fplog, cr, nfile, fnm,
+ oenv, bVerbose, bCompact,
+ nstglobalcomm,
+ vsite, constr,
+ nstepout, inputrec, mtop,
+ fcd, state,
+ mdatoms, nrnb, wcycle, ed, fr,
+ repl_ex_nst, repl_ex_nex, repl_ex_seed,
+ membed,
+ cpt_period, max_hours,
+ deviceOptions,
+ Flags,
+ &runtime);
+
+ if (inputrec->ePull != epullNO)
+ {
+ finish_pull(fplog, inputrec->pull);
+ }
+
+ if (inputrec->bRot)
+ {
++ finish_rot(inputrec->rot);
+ }
+
+ }
+ else
+ {
+ /* do PME only */
+ gmx_pmeonly(*pmedata, cr, nrnb, wcycle, ewaldcoeff, FALSE, inputrec);
+ }
+
+ if (EI_DYNAMICS(inputrec->eI) || EI_TPI(inputrec->eI))
+ {
+ /* Some timing stats */
+ if (SIMMASTER(cr))
+ {
+ if (runtime.proc == 0)
+ {
+ runtime.proc = runtime.real;
+ }
+ }
+ else
+ {
+ runtime.real = 0;
+ }
+ }
+
+ wallcycle_stop(wcycle, ewcRUN);
+
+ /* Finish up, write some stuff
+ * if rerunMD, don't write last frame again
+ */
+ finish_run(fplog, cr, ftp2fn(efSTO, nfile, fnm),
+ inputrec, nrnb, wcycle, &runtime,
+ fr != NULL && fr->nbv != NULL && fr->nbv->bUseGPU ?
+ nbnxn_cuda_get_timings(fr->nbv->cu_nbv) : NULL,
+ nthreads_pp,
+ EI_DYNAMICS(inputrec->eI) && !MULTISIM(cr));
+
+ if ((cr->duty & DUTY_PP) && fr->nbv != NULL && fr->nbv->bUseGPU)
+ {
+ char gpu_err_str[STRLEN];
+
+ /* free GPU memory and uninitialize GPU (by destroying the context) */
+ nbnxn_cuda_free(fplog, fr->nbv->cu_nbv);
+
+ if (!free_gpu(gpu_err_str))
+ {
+ gmx_warning("On node %d failed to free GPU #%d: %s",
+ cr->nodeid, get_current_gpu_device_id(), gpu_err_str);
+ }
+ }
+
+ if (opt2bSet("-membed", nfile, fnm))
+ {
+ sfree(membed);
+ }
+
+#ifdef GMX_THREAD_MPI
+ if (PAR(cr) && SIMMASTER(cr))
+#endif
+ {
+ gmx_hardware_info_free(hwinfo);
+ }
+
+ /* Does what it says */
+ print_date_and_time(fplog, cr->nodeid, "Finished mdrun", &runtime);
+
+ /* Close logfile already here if we were appending to it */
+ if (MASTER(cr) && (Flags & MD_APPENDFILES))
+ {
+ gmx_log_close(fplog);
+ }
+
+ rc = (int)gmx_get_stop_condition();
+
+#ifdef GMX_THREAD_MPI
+ /* we need to join all threads. The sub-threads join when they
+ exit this function, but the master thread needs to be told to
+ wait for that. */
+ if (PAR(cr) && MASTER(cr))
+ {
+ tMPI_Finalize();
+ }
+#endif
+
+ return rc;
+}