Merge release-4-6 into master
authorRoland Schulz <roland@utk.edu>
Tue, 31 Dec 2013 00:18:23 +0000 (19:18 -0500)
committerRoland Schulz <roland@utk.edu>
Wed, 1 Jan 2014 02:17:20 +0000 (21:17 -0500)
Conflicts:
Only version:
CMakeLists.txt
admin/installguide/installguide.tex
share/html/online/mdp_opt.html
Other:
admin/mkhtml (deleted in master)
cmake/gmxCFlags.cmake (gcc 4.8 change ignored)

Changes other than conflicts:
        src/gromacs/gmxlib/gmx_detect_hardware.c (fix compile errors)

Change-Id: Id5845638857fd84ffaeac618d5286a2d23967f7f

1  2 
src/contrib/fftw/CMakeLists.txt
src/gromacs/gmxana/gmx_make_edi.c
src/gromacs/gmxlib/gmx_detect_hardware.c
src/gromacs/mdlib/edsam.c

Simple merge
index 9869237c72fce889f224fe32966ea8163f9acdc2,0000000000000000000000000000000000000000..a732f1e04b6789477277963f0a8f974a3038c41b
mode 100644,000000..100644
--- /dev/null
@@@ -1,989 -1,0 +1,989 @@@
-     static real        radfix   = 0.0;
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 2012,2013, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +/* The make_edi program was generously contributed by Oliver Lange, based
 + * on the code from g_anaeig. You can reach him as olange@gwdg.de. He
 + * probably also holds copyright to the following code.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <stdlib.h>
 +#include <ctype.h>
 +#include <string.h>
 +#include "readinp.h"
 +#include "gromacs/commandline/pargs.h"
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "smalloc.h"
 +#include "macros.h"
 +#include "gmx_fatal.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "gromacs/fileio/futil.h"
 +#include "gromacs/fileio/pdbio.h"
 +#include "gromacs/fileio/confio.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/matio.h"
 +#include "mshift.h"
 +#include "xvgr.h"
 +#include "do_fit.h"
 +#include "rmpbc.h"
 +#include "txtdump.h"
 +#include "eigio.h"
 +#include "index.h"
 +#include "string2.h"
 +
 +typedef struct
 +{
 +    real        deltaF0;
 +    gmx_bool    bHarmonic;
 +    gmx_bool    bConstForce;   /* Do constant force flooding instead of
 +                                  evaluating a flooding potential             */
 +    real        tau;
 +    real        deltaF;
 +    real        kT;
 +    real        constEfl;
 +    real        alpha2;
 +} t_edflood;
 +
 +
 +/* This type is for the average, reference, target, and origin structure   */
 +typedef struct edix
 +{
 +    int          nr;            /* number of atoms this structure contains */
 +    int         *anrs;          /* atom index numbers                      */
 +    rvec        *x;             /* positions                               */
 +    real        *sqrtm;         /* sqrt of the masses used for mass-
 +                                 * weighting of analysis                   */
 +} t_edix;
 +
 +
 +typedef struct edipar
 +{
 +    int         nini;           /* total Nr of atoms                    */
 +    gmx_bool    fitmas;         /* true if trans fit with cm            */
 +    gmx_bool    pcamas;         /* true if mass-weighted PCA            */
 +    int         presteps;       /* number of steps to run without any
 +                                 *    perturbations ... just monitoring */
 +    int         outfrq;         /* freq (in steps) of writing to edo    */
 +    int         maxedsteps;     /* max nr of steps per cycle            */
 +    struct edix sref;           /* reference positions, to these fitting
 +                                 * will be done                         */
 +    struct edix sav;            /* average positions                    */
 +    struct edix star;           /* target positions                     */
 +    struct edix sori;           /* origin positions                     */
 +    real        slope;          /* minimal slope in acceptance radexp   */
 +    int         ned;            /* Nr of atoms in essdyn buffer         */
 +    t_edflood   flood;          /* parameters especially for flooding   */
 +} t_edipar;
 +
 +
 +
 +void make_t_edx(struct edix *edx, int natoms, rvec *pos, atom_id index[])
 +{
 +    edx->nr   = natoms;
 +    edx->anrs = index;
 +    edx->x    = pos;
 +}
 +
 +void write_t_edx(FILE *fp, struct edix edx, const char *comment)
 +{
 +    /*here we copy only the pointers into the t_edx struct
 +       no data is copied and edx.box is ignored  */
 +    int i;
 +    fprintf(fp, "#%s \n %d \n", comment, edx.nr);
 +    for (i = 0; i < edx.nr; i++)
 +    {
 +        fprintf(fp, "%d  %f  %f  %f\n", (edx.anrs)[i]+1, (edx.x)[i][XX], (edx.x)[i][YY], (edx.x)[i][ZZ]);
 +    }
 +}
 +
 +int sscan_list(int *list[], const char *str, const char *listname)
 +{
 +    /*this routine scans a string of the form 1,3-6,9 and returns the
 +       selected numbers (in this case 1 3 4 5 6 9) in NULL-terminated array of integers.
 +       memory for this list will be allocated  in this routine -- sscan_list expects *list to
 +       be a NULL-Pointer
 +
 +       listname is a string used in the errormessage*/
 +
 +
 +    int   i, istep;
 +    char  c;
 +    char *pos, *startpos, *step;
 +    int   n = strlen(str);
 +
 +    /*enums to define the different lexical stati */
 +    enum {
 +        sBefore, sNumber, sMinus, sRange, sZero, sSmaller, sError, sSteppedRange
 +    };
 +
 +    int   status     = sBefore; /*status of the deterministic automat to scan str   */
 +    int   number     = 0;
 +    int   end_number = 0;
 +
 +    char *start = NULL; /*holds the string of the number behind a ','*/
 +    char *end   = NULL; /*holds the string of the number behind a '-' */
 +
 +    int   nvecs = 0;    /* counts the number of vectors in the list*/
 +
 +    step = NULL;
 +    snew(pos, n+4);
 +    startpos = pos;
 +    strcpy(pos, str);
 +    pos[n]   = ',';
 +    pos[n+1] = '1';
 +    pos[n+2] = '\0';
 +
 +    *list = NULL;
 +
 +    while ((c = *pos) != 0)
 +    {
 +        switch (status)
 +        {
 +            /* expect a number */
 +            case sBefore: if (isdigit(c))
 +                {
 +                    start  = pos;
 +                    status = sNumber;
 +                    break;
 +                }
 +                else
 +                {
 +                    status = sError;
 +                } break;
 +
 +            /* have read a number, expect ',' or '-' */
 +            case sNumber: if (c == ',')
 +                {
 +                    /*store number*/
 +                    srenew(*list, nvecs+1);
 +                    (*list)[nvecs++] = number = strtol(start, NULL, 10);
 +                    status           = sBefore;
 +                    if (number == 0)
 +                    {
 +                        status = sZero;
 +                    }
 +                    break;
 +                }
 +                else if (c == '-')
 +                {
 +                    status = sMinus; break;
 +                }
 +                else if (isdigit(c))
 +                {
 +                    break;
 +                }
 +                else
 +                {
 +                    status = sError;
 +                } break;
 +
 +            /* have read a '-' -> expect a number */
 +            case sMinus:
 +                if (isdigit(c))
 +                {
 +                    end    = pos;
 +                    status = sRange; break;
 +                }
 +                else
 +                {
 +                    status = sError;
 +                } break;
 +
 +            case sSteppedRange:
 +                if (isdigit(c))
 +                {
 +                    if (step)
 +                    {
 +                        status = sError; break;
 +                    }
 +                    else
 +                    {
 +                        step = pos;
 +                    }
 +                    status = sRange;
 +                    break;
 +                }
 +                else
 +                {
 +                    status = sError;
 +                } break;
 +
 +            /* have read the number after a minus, expect ',' or ':' */
 +            case sRange:
 +                if (c == ',')
 +                {
 +                    /*store numbers*/
 +                    end_number = strtol(end, NULL, 10);
 +                    number     = strtol(start, NULL, 10);
 +                    status     = sBefore;
 +                    if (number == 0)
 +                    {
 +                        status = sZero; break;
 +                    }
 +                    if (end_number <= number)
 +                    {
 +                        status = sSmaller; break;
 +                    }
 +                    srenew(*list, nvecs+end_number-number+1);
 +                    if (step)
 +                    {
 +                        istep = strtol(step, NULL, 10);
 +                        step  = NULL;
 +                    }
 +                    else
 +                    {
 +                        istep = 1;
 +                    }
 +                    for (i = number; i <= end_number; i += istep)
 +                    {
 +                        (*list)[nvecs++] = i;
 +                    }
 +                    break;
 +                }
 +                else if (c == ':')
 +                {
 +                    status = sSteppedRange;
 +                    break;
 +                }
 +                else if (isdigit(c))
 +                {
 +                    break;
 +                }
 +                else
 +                {
 +                    status = sError;
 +                } break;
 +
 +            /* format error occured */
 +            case sError:
 +                gmx_fatal(FARGS, "Error in the list of eigenvectors for %s at pos %d with char %c", listname, pos-startpos, *(pos-1));
 +                break;
 +            /* logical error occured */
 +            case sZero:
 +                gmx_fatal(FARGS, "Error in the list of eigenvectors for %s at pos %d: eigenvector 0 is not valid", listname, pos-startpos);
 +                break;
 +            case sSmaller:
 +                gmx_fatal(FARGS, "Error in the list of eigenvectors for %s at pos %d: second index %d is not bigger than %d", listname, pos-startpos, end_number, number);
 +                break;
 +        }
 +        ++pos; /* read next character */
 +    }          /*scanner has finished */
 +
 +    /* append zero to list of eigenvectors */
 +    srenew(*list, nvecs+1);
 +    (*list)[nvecs] = 0;
 +    sfree(startpos);
 +    return nvecs;
 +} /*sscan_list*/
 +
 +void write_eigvec(FILE* fp, int natoms, int eig_list[], rvec** eigvecs, int nvec, const char *grouptitle, real steps[])
 +{
 +/* eig_list is a zero-terminated list of indices into the eigvecs array.
 +   eigvecs are coordinates of eigenvectors
 +   grouptitle to write in the comment line
 +   steps  -- array with stepsizes for evLINFIX, evLINACC and evRADACC
 + */
 +
 +    int  n = 0, i; rvec x;
 +    real sum;
 +    while (eig_list[n++])
 +    {
 +        ;                 /*count selected eigenvecs*/
 +
 +    }
 +    fprintf(fp, "# NUMBER OF EIGENVECTORS + %s\n %d\n", grouptitle, n-1);
 +
 +    /* write list of eigenvector indicess */
 +    for (n = 0; eig_list[n]; n++)
 +    {
 +        if (steps)
 +        {
 +            fprintf(fp, "%8d   %g\n", eig_list[n], steps[n]);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%8d   %g\n", eig_list[n], 1.0);
 +        }
 +    }
 +    n = 0;
 +
 +    /* dump coordinates of the selected eigenvectors */
 +    while (eig_list[n])
 +    {
 +        sum = 0;
 +        for (i = 0; i < natoms; i++)
 +        {
 +            if (eig_list[n] > nvec)
 +            {
 +                gmx_fatal(FARGS, "Selected eigenvector %d is higher than maximum number %d of available eigenvectors", eig_list[n], nvec);
 +            }
 +            copy_rvec(eigvecs[eig_list[n]-1][i], x);
 +            sum += norm2(x);
 +            fprintf(fp, "%8.5f %8.5f %8.5f\n", x[XX], x[YY], x[ZZ]);
 +        }
 +        n++;
 +    }
 +}
 +
 +
 +/*enum referring to the different lists of eigenvectors*/
 +enum {
 +    evLINFIX, evLINACC, evFLOOD, evRADFIX, evRADACC, evRADCON, evMON,  evNr
 +};
 +#define oldMAGIC 666
 +#define MAGIC 670
 +
 +
 +void write_the_whole_thing(FILE* fp, t_edipar *edpars, rvec** eigvecs,
 +                           int nvec, int *eig_listen[], real* evStepList[])
 +{
 +/* write edi-file */
 +
 +    /*Header*/
 +    fprintf(fp, "#MAGIC\n %d \n#NINI\n %d\n#FITMAS\n %d\n#ANALYSIS_MAS\n %d\n",
 +            MAGIC, edpars->nini, edpars->fitmas, edpars->pcamas);
 +    fprintf(fp, "#OUTFRQ\n %d\n#MAXLEN\n %d\n#SLOPECRIT\n %f\n",
 +            edpars->outfrq, edpars->maxedsteps, edpars->slope);
 +    fprintf(fp, "#PRESTEPS\n %d\n#DELTA_F0\n %f\n#INIT_DELTA_F\n %f\n#TAU\n %f\n#EFL_NULL\n %f\n#ALPHA2\n %f\n#KT\n %f\n#HARMONIC\n %d\n#CONST_FORCE_FLOODING\n %d\n",
 +            edpars->presteps, edpars->flood.deltaF0, edpars->flood.deltaF, edpars->flood.tau, edpars->flood.constEfl,
 +            edpars->flood.alpha2, edpars->flood.kT, edpars->flood.bHarmonic, edpars->flood.bConstForce);
 +
 +    /* Average and reference positions */
 +    write_t_edx(fp, edpars->sref, "NREF, XREF");
 +    write_t_edx(fp, edpars->sav, "NAV, XAV");
 +
 +    /*Eigenvectors */
 +
 +    write_eigvec(fp, edpars->ned, eig_listen[evMON], eigvecs, nvec, "COMPONENTS GROUP 1", NULL);
 +    write_eigvec(fp, edpars->ned, eig_listen[evLINFIX], eigvecs, nvec, "COMPONENTS GROUP 2", evStepList[evLINFIX]);
 +    write_eigvec(fp, edpars->ned, eig_listen[evLINACC], eigvecs, nvec, "COMPONENTS GROUP 3", evStepList[evLINACC]);
 +    write_eigvec(fp, edpars->ned, eig_listen[evRADFIX], eigvecs, nvec, "COMPONENTS GROUP 4", evStepList[evRADFIX]);
 +    write_eigvec(fp, edpars->ned, eig_listen[evRADACC], eigvecs, nvec, "COMPONENTS GROUP 5", NULL);
 +    write_eigvec(fp, edpars->ned, eig_listen[evRADCON], eigvecs, nvec, "COMPONENTS GROUP 6", NULL);
 +    write_eigvec(fp, edpars->ned, eig_listen[evFLOOD], eigvecs, nvec, "COMPONENTS GROUP 7", evStepList[evFLOOD]);
 +
 +
 +    /*Target and Origin positions */
 +    write_t_edx(fp, edpars->star, "NTARGET, XTARGET");
 +    write_t_edx(fp, edpars->sori, "NORIGIN, XORIGIN");
 +}
 +
 +int read_conffile(const char *confin, char *title, rvec *x[])
 +{
 +/* read coordinates out of STX file  */
 +    int      natoms;
 +    t_atoms  confat;
 +    matrix   box;
 +    printf("read coordnumber from file %s\n", confin);
 +    get_stx_coordnum(confin, &natoms);
 +    printf("number of coordinates in file %d\n", natoms);
 +/*  if (natoms != ncoords)
 +     gmx_fatal(FARGS,"number of coordinates in coordinate file (%s, %d)\n"
 +           "             does not match topology (= %d)",
 +           confin,natoms,ncoords);
 +   else {*/
 +    /* make space for coordinates and velocities */
 +    init_t_atoms(&confat, natoms, FALSE);
 +    snew(*x, natoms);
 +    read_stx_conf(confin, title, &confat, *x, NULL, NULL, box);
 +    return natoms;
 +}
 +
 +
 +void read_eigenvalues(int vecs[], const char *eigfile, real values[],
 +                      gmx_bool bHesse, real kT)
 +{
 +    int      neig, nrow, i;
 +    double **eigval;
 +
 +    neig = read_xvg(eigfile, &eigval, &nrow);
 +
 +    fprintf(stderr, "Read %d eigenvalues\n", neig);
 +    for (i = bHesse ? 6 : 0; i < neig; i++)
 +    {
 +        if (eigval[1][i] < -0.001 && bHesse)
 +        {
 +            fprintf(stderr,
 +                    "WARNING: The Hessian Matrix has negative eigenvalue %f, we set it to zero (no flooding in this direction)\n\n", eigval[1][i]);
 +        }
 +
 +        if (eigval[1][i] < 0)
 +        {
 +            eigval[1][i] = 0;
 +        }
 +    }
 +    if (bHesse)
 +    {
 +        for (i = 0; vecs[i]; i++)
 +        {
 +            if (vecs[i] < 7)
 +            {
 +                gmx_fatal(FARGS, "ERROR: You have chosen one of the first 6 eigenvectors of the HESSE Matrix. That does not make sense, since they correspond to the 6 rotational and translational degrees of freedom.\n\n");
 +            }
 +            values[i] = eigval[1][vecs[i]-1]/kT;
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; vecs[i]; i++)
 +        {
 +            if (vecs[i] > (neig-6))
 +            {
 +                gmx_fatal(FARGS, "ERROR: You have chosen one of the last 6 eigenvectors of the COVARIANCE Matrix. That does not make sense, since they correspond to the 6 rotational and translational degrees of freedom.\n\n");
 +            }
 +            values[i] = 1/eigval[1][vecs[i]-1];
 +        }
 +    }
 +    /* free memory */
 +    for (i = 0; i < nrow; i++)
 +    {
 +        sfree(eigval[i]);
 +    }
 +    sfree(eigval);
 +}
 +
 +
 +static real *scan_vecparams(const char *str, const char * par, int nvecs)
 +{
 +    char    f0[256], f1[256];         /*format strings adapted every pass of the loop*/
 +    double  d, tcap = 0;
 +    int     i;
 +    real   *vec_params;
 +
 +    snew(vec_params, nvecs);
 +    if (str)
 +    {
 +        f0[0] = '\0';
 +        for (i = 0; (i < nvecs); i++)
 +        {
 +            strcpy(f1, f0);    /*f0 is the format string for the "to-be-ignored" numbers*/
 +            strcat(f1, "%lf"); /*and f1 to read the actual number in this pass of the loop*/
 +            if (sscanf(str, f1, &d) != 1)
 +            {
 +                gmx_fatal(FARGS, "Not enough elements for %s parameter (I need %d)", par, nvecs);
 +            }
 +            vec_params[i] = d;
 +            tcap         += d;
 +            strcat(f0, "%*s");
 +        }
 +    }
 +    return vec_params;
 +}
 +
 +
 +void init_edx(struct edix *edx)
 +{
 +    edx->nr = 0;
 +    snew(edx->x, 1);
 +    snew(edx->anrs, 1);
 +}
 +
 +void filter2edx(struct edix *edx, int nindex, atom_id index[], int ngro,
 +                atom_id igro[], rvec *x, const char* structure)
 +{
 +/* filter2edx copies coordinates from x to edx which are given in index
 + */
 +
 +    int pos, i;
 +    int ix = edx->nr;
 +    edx->nr += nindex;
 +    srenew(edx->x, edx->nr);
 +    srenew(edx->anrs, edx->nr);
 +    for (i = 0; i < nindex; i++, ix++)
 +    {
 +        for (pos = 0; pos < ngro-1 && igro[pos] != index[i]; ++pos)
 +        {
 +        }
 +        ;                                                            /*search element in igro*/
 +        if (igro[pos] != index[i])
 +        {
 +            gmx_fatal(FARGS, "Couldn't find atom with index %d in structure %s", index[i], structure);
 +        }
 +        edx->anrs[ix] = index[i];
 +        copy_rvec(x[pos], edx->x[ix]);
 +    }
 +}
 +
 +void get_structure(t_atoms *atoms, const char *IndexFile,
 +                   const char *StructureFile, struct edix *edx, int nfit,
 +                   atom_id ifit[], int nav, atom_id index[])
 +{
 +    atom_id *igro; /*index corresponding to target or origin structure*/
 +    int      ngro;
 +    int      ntar;
 +    rvec    *xtar;
 +    char     title[STRLEN];
 +    char   * grpname;
 +
 +
 +    ntar = read_conffile(StructureFile, title, &xtar);
 +    printf("Select an index group of %d elements that corresponds to the atoms in the structure file %s\n",
 +           ntar, StructureFile);
 +    get_index(atoms, IndexFile, 1, &ngro, &igro, &grpname);
 +    if (ngro != ntar)
 +    {
 +        gmx_fatal(FARGS, "You selected an index group with %d elements instead of %d", ngro, ntar);
 +    }
 +    init_edx(edx);
 +    filter2edx(edx, nfit, ifit, ngro, igro, xtar, StructureFile);
 +
 +    /* If average and reference/fitting structure differ, append the average structure as well */
 +    if (ifit != index) /*if fit structure is different append these coordinates, too -- don't mind duplicates*/
 +    {
 +        filter2edx(edx, nav, index, ngro, igro, xtar, StructureFile);
 +    }
 +}
 +
 +int gmx_make_edi(int argc, char *argv[])
 +{
 +
 +    static const char *desc[] = {
 +        "[THISMODULE] generates an essential dynamics (ED) sampling input file to be used with [TT]mdrun[tt]",
 +        "based on eigenvectors of a covariance matrix ([gmx-covar]) or from a",
 +        "normal modes analysis ([gmx-nmeig]).",
 +        "ED sampling can be used to manipulate the position along collective coordinates",
 +        "(eigenvectors) of (biological) macromolecules during a simulation. Particularly,",
 +        "it may be used to enhance the sampling efficiency of MD simulations by stimulating",
 +        "the system to explore new regions along these collective coordinates. A number",
 +        "of different algorithms are implemented to drive the system along the eigenvectors",
 +        "([TT]-linfix[tt], [TT]-linacc[tt], [TT]-radfix[tt], [TT]-radacc[tt], [TT]-radcon[tt]),",
 +        "to keep the position along a certain (set of) coordinate(s) fixed ([TT]-linfix[tt]),",
 +        "or to only monitor the projections of the positions onto",
 +        "these coordinates ([TT]-mon[tt]).[PAR]",
 +        "References:[BR]",
 +        "A. Amadei, A.B.M. Linssen, B.L. de Groot, D.M.F. van Aalten and ",
 +        "H.J.C. Berendsen; An efficient method for sampling the essential subspace ",
 +        "of proteins., J. Biomol. Struct. Dyn. 13:615-626 (1996)[BR]",
 +        "B.L. de Groot, A. Amadei, D.M.F. van Aalten and H.J.C. Berendsen; ",
 +        "Towards an exhaustive sampling of the configurational spaces of the ",
 +        "two forms of the peptide hormone guanylin,",
 +        "J. Biomol. Struct. Dyn. 13 : 741-751 (1996)[BR]",
 +        "B.L. de Groot, A.Amadei, R.M. Scheek, N.A.J. van Nuland and H.J.C. Berendsen; ",
 +        "An extended sampling of the configurational space of HPr from E. coli",
 +        "Proteins: Struct. Funct. Gen. 26: 314-322 (1996)",
 +        "[PAR]You will be prompted for one or more index groups that correspond to the eigenvectors,",
 +        "reference structure, target positions, etc.[PAR]",
 +
 +        "[TT]-mon[tt]: monitor projections of the coordinates onto selected eigenvectors.[PAR]",
 +        "[TT]-linfix[tt]: perform fixed-step linear expansion along selected eigenvectors.[PAR]",
 +        "[TT]-linacc[tt]: perform acceptance linear expansion along selected eigenvectors.",
 +        "(steps in the desired directions will be accepted, others will be rejected).[PAR]",
 +        "[TT]-radfix[tt]: perform fixed-step radius expansion along selected eigenvectors.[PAR]",
 +        "[TT]-radacc[tt]: perform acceptance radius expansion along selected eigenvectors.",
 +        "(steps in the desired direction will be accepted, others will be rejected).",
 +        "[BB]Note:[bb] by default the starting MD structure will be taken as origin of the first",
 +        "expansion cycle for radius expansion. If [TT]-ori[tt] is specified, you will be able",
 +        "to read in a structure file that defines an external origin.[PAR]",
 +        "[TT]-radcon[tt]: perform acceptance radius contraction along selected eigenvectors",
 +        "towards a target structure specified with [TT]-tar[tt].[PAR]",
 +        "NOTE: each eigenvector can be selected only once. [PAR]",
 +        "[TT]-outfrq[tt]: frequency (in steps) of writing out projections etc. to [TT].xvg[tt] file[PAR]",
 +        "[TT]-slope[tt]: minimal slope in acceptance radius expansion. A new expansion",
 +        "cycle will be started if the spontaneous increase of the radius (in nm/step)",
 +        "is less than the value specified.[PAR]",
 +        "[TT]-maxedsteps[tt]: maximum number of steps per cycle in radius expansion",
 +        "before a new cycle is started.[PAR]",
 +        "Note on the parallel implementation: since ED sampling is a 'global' thing",
 +        "(collective coordinates etc.), at least on the 'protein' side, ED sampling",
 +        "is not very parallel-friendly from an implementation point of view. Because",
 +        "parallel ED requires some extra communication, expect the performance to be",
 +        "lower as in a free MD simulation, especially on a large number of nodes and/or",
 +        "when the ED group contains a lot of atoms. [PAR]",
 +        "Please also note that if your ED group contains more than a single protein,",
 +        "then the [TT].tpr[tt] file must contain the correct PBC representation of the ED group.",
 +        "Take a look on the initial RMSD from the reference structure, which is printed",
 +        "out at the start of the simulation; if this is much higher than expected, one",
 +        "of the ED molecules might be shifted by a box vector. [PAR]",
 +        "All ED-related output of [TT]mdrun[tt] (specify with [TT]-eo[tt]) is written to a [TT].xvg[tt] file",
 +        "as a function of time in intervals of OUTFRQ steps.[PAR]",
 +        "[BB]Note[bb] that you can impose multiple ED constraints and flooding potentials in",
 +        "a single simulation (on different molecules) if several [TT].edi[tt] files were concatenated",
 +        "first. The constraints are applied in the order they appear in the [TT].edi[tt] file. ",
 +        "Depending on what was specified in the [TT].edi[tt] input file, the output file contains for each ED dataset[PAR]",
 +        "[TT]*[tt] the RMSD of the fitted molecule to the reference structure (for atoms involved in fitting prior to calculating the ED constraints)[BR]",
 +        "[TT]*[tt] projections of the positions onto selected eigenvectors[BR]",
 +        "[PAR][PAR]",
 +        "FLOODING:[PAR]",
 +        "with [TT]-flood[tt], you can specify which eigenvectors are used to compute a flooding potential,",
 +        "which will lead to extra forces expelling the structure out of the region described",
 +        "by the covariance matrix. If you switch -restrain the potential is inverted and the structure",
 +        "is kept in that region.",
 +        "[PAR]",
 +        "The origin is normally the average structure stored in the [TT]eigvec.trr[tt] file.",
 +        "It can be changed with [TT]-ori[tt] to an arbitrary position in configuration space.",
 +        "With [TT]-tau[tt], [TT]-deltaF0[tt], and [TT]-Eflnull[tt] you control the flooding behaviour.",
 +        "Efl is the flooding strength, it is updated according to the rule of adaptive flooding.",
 +        "Tau is the time constant of adaptive flooding, high [GRK]tau[grk] means slow adaption (i.e. growth). ",
 +        "DeltaF0 is the flooding strength you want to reach after tau ps of simulation.",
 +        "To use constant Efl set [TT]-tau[tt] to zero.",
 +        "[PAR]",
 +        "[TT]-alpha[tt] is a fudge parameter to control the width of the flooding potential. A value of 2 has been found",
 +        "to give good results for most standard cases in flooding of proteins.",
 +        "[GRK]alpha[grk] basically accounts for incomplete sampling, if you sampled further the width of the ensemble would",
 +        "increase, this is mimicked by [GRK]alpha[grk] > 1.",
 +        "For restraining, [GRK]alpha[grk] < 1 can give you smaller width in the restraining potential.",
 +        "[PAR]",
 +        "RESTART and FLOODING:",
 +        "If you want to restart a crashed flooding simulation please find the values deltaF and Efl in",
 +        "the output file and manually put them into the [TT].edi[tt] file under DELTA_F0 and EFL_NULL."
 +    };
 +
 +    /* Save all the params in this struct and then save it in an edi file.
 +     * ignoring fields nmass,massnrs,mass,tmass,nfit,fitnrs,edo
 +     */
 +    static t_edipar edi_params;
 +
 +    enum  {
 +        evStepNr = evRADFIX + 1
 +    };
 +    static const char* evSelections[evNr]      = {NULL, NULL, NULL, NULL, NULL, NULL};
 +    static const char* evOptions[evNr]         = {"-linfix", "-linacc", "-flood", "-radfix", "-radacc", "-radcon", "-mon"};
 +    static const char* evParams[evStepNr]      = {NULL, NULL};
 +    static const char* evStepOptions[evStepNr] = {"-linstep", "-accdir", "-not_used", "-radstep"};
 +    static const char* ConstForceStr;
 +    static real      * evStepList[evStepNr];
-         { "-radstep", FALSE, etREAL, {&radfix},
++    static real        radstep  = 0.0;
 +    static real        deltaF0  = 150;
 +    static real        deltaF   = 0;
 +    static real        tau      = .1;
 +    static real        constEfl = 0.0;
 +    static real        alpha    = 1;
 +    static int         eqSteps  = 0;
 +    static int       * listen[evNr];
 +    static real        T         = 300.0;
 +    const real         kB        = 2.5 / 300.0; /* k_boltzmann in MD units */
 +    static gmx_bool    bRestrain = FALSE;
 +    static gmx_bool    bHesse    = FALSE;
 +    static gmx_bool    bHarmonic = FALSE;
 +    t_pargs            pa[]      = {
 +        { "-mon", FALSE, etSTR, {&evSelections[evMON]},
 +          "Indices of eigenvectors for projections of x (e.g. 1,2-5,9) or 1-100:10 means 1 11 21 31 ... 91" },
 +        { "-linfix", FALSE, etSTR, {&evSelections[0]},
 +          "Indices of eigenvectors for fixed increment linear sampling" },
 +        { "-linacc", FALSE, etSTR, {&evSelections[1]},
 +          "Indices of eigenvectors for acceptance linear sampling" },
 +        { "-radfix", FALSE, etSTR, {&evSelections[3]},
 +          "Indices of eigenvectors for fixed increment radius expansion" },
 +        { "-radacc", FALSE, etSTR, {&evSelections[4]},
 +          "Indices of eigenvectors for acceptance radius expansion" },
 +        { "-radcon", FALSE, etSTR, {&evSelections[5]},
 +          "Indices of eigenvectors for acceptance radius contraction" },
 +        { "-flood",  FALSE, etSTR, {&evSelections[2]},
 +          "Indices of eigenvectors for flooding"},
 +        { "-outfrq", FALSE, etINT, {&edi_params.outfrq},
 +          "Freqency (in steps) of writing output in [TT].xvg[tt] file" },
 +        { "-slope", FALSE, etREAL, { &edi_params.slope},
 +          "Minimal slope in acceptance radius expansion"},
 +        { "-linstep", FALSE, etSTR, {&evParams[0]},
 +          "Stepsizes (nm/step) for fixed increment linear sampling (put in quotes! \"1.0 2.3 5.1 -3.1\")"},
 +        { "-accdir", FALSE, etSTR, {&evParams[1]},
 +          "Directions for acceptance linear sampling - only sign counts! (put in quotes! \"-1 +1 -1.1\")"},
-             else if (ev_class == evRADFIX && opt2parg_bSet(evStepOptions[ev_class], NPA, pa))
++        { "-radstep", FALSE, etREAL, {&radstep},
 +          "Stepsize (nm/step) for fixed increment radius expansion"},
 +        { "-maxedsteps", FALSE, etINT, {&edi_params.maxedsteps},
 +          "Maximum number of steps per cycle" },
 +        { "-eqsteps", FALSE, etINT, {&eqSteps},
 +          "Number of steps to run without any perturbations "},
 +        { "-deltaF0", FALSE, etREAL, {&deltaF0},
 +          "Target destabilization energy for flooding"},
 +        { "-deltaF", FALSE, etREAL, {&deltaF},
 +          "Start deltaF with this parameter - default 0, nonzero values only needed for restart"},
 +        { "-tau", FALSE, etREAL, {&tau},
 +          "Coupling constant for adaption of flooding strength according to deltaF0, 0 = infinity i.e. constant flooding strength"},
 +        { "-Eflnull", FALSE, etREAL, {&constEfl},
 +          "The starting value of the flooding strength. The flooding strength is updated "
 +          "according to the adaptive flooding scheme. For a constant flooding strength use [TT]-tau[tt] 0. "},
 +        { "-T", FALSE, etREAL, {&T},
 +          "T is temperature, the value is needed if you want to do flooding "},
 +        { "-alpha", FALSE, etREAL, {&alpha},
 +          "Scale width of gaussian flooding potential with alpha^2 "},
 +        { "-restrain", FALSE, etBOOL, {&bRestrain},
 +          "Use the flooding potential with inverted sign -> effects as quasiharmonic restraining potential"},
 +        { "-hessian", FALSE, etBOOL, {&bHesse},
 +          "The eigenvectors and eigenvalues are from a Hessian matrix"},
 +        { "-harmonic", FALSE, etBOOL, {&bHarmonic},
 +          "The eigenvalues are interpreted as spring constant"},
 +        { "-constF", FALSE, etSTR, {&ConstForceStr},
 +          "Constant force flooding: manually set the forces for the eigenvectors selected with -flood "
 +          "(put in quotes! \"1.0 2.3 5.1 -3.1\"). No other flooding parameters are needed when specifying the forces directly."}
 +    };
 +#define NPA asize(pa)
 +
 +    rvec        *xref1;
 +    int          nvec1, *eignr1 = NULL;
 +    rvec        *xav1, **eigvec1 = NULL;
 +    t_atoms     *atoms = NULL;
 +    int          nav; /* Number of atoms in the average structure */
 +    char        *grpname;
 +    const char  *indexfile;
 +    int          i;
 +    atom_id     *index, *ifit;
 +    int          nfit;           /* Number of atoms in the reference/fit structure */
 +    int          ev_class;       /* parameter _class i.e. evMON, evRADFIX etc. */
 +    int          nvecs;
 +    real        *eigval1 = NULL; /* in V3.3 this is parameter of read_eigenvectors */
 +
 +    const char  *EdiFile;
 +    const char  *TargetFile;
 +    const char  *OriginFile;
 +    const char  *EigvecFile;
 +
 +    output_env_t oenv;
 +
 +    /*to read topology file*/
 +    t_topology  top;
 +    int         ePBC;
 +    char        title[STRLEN];
 +    matrix      topbox;
 +    rvec       *xtop;
 +    gmx_bool    bTop, bFit1;
 +
 +    t_filenm    fnm[] = {
 +        { efTRN, "-f",    "eigenvec",    ffREAD  },
 +        { efXVG, "-eig",  "eigenval",    ffOPTRD },
 +        { efTPS, NULL,    NULL,          ffREAD },
 +        { efNDX, NULL,    NULL,  ffOPTRD },
 +        { efSTX, "-tar", "target", ffOPTRD},
 +        { efSTX, "-ori", "origin", ffOPTRD},
 +        { efEDI, "-o", "sam", ffWRITE }
 +    };
 +#define NFILE asize(fnm)
 +    edi_params.outfrq = 100; edi_params.slope = 0.0; edi_params.maxedsteps = 0;
 +    if (!parse_common_args(&argc, argv, 0,
 +                           NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv))
 +    {
 +        return 0;
 +    }
 +
 +    indexfile       = ftp2fn_null(efNDX, NFILE, fnm);
 +    EdiFile         = ftp2fn(efEDI, NFILE, fnm);
 +    TargetFile      = opt2fn_null("-tar", NFILE, fnm);
 +    OriginFile      = opt2fn_null("-ori", NFILE, fnm);
 +
 +
 +    for (ev_class = 0; ev_class < evNr; ++ev_class)
 +    {
 +        if (opt2parg_bSet(evOptions[ev_class], NPA, pa))
 +        {
 +            /*get list of eigenvectors*/
 +            nvecs = sscan_list(&(listen[ev_class]), opt2parg_str(evOptions[ev_class], NPA, pa), evOptions[ev_class]);
 +            if (ev_class < evStepNr-2)
 +            {
 +                /*if apropriate get list of stepsizes for these eigenvectors*/
 +                if (opt2parg_bSet(evStepOptions[ev_class], NPA, pa))
 +                {
 +                    evStepList[ev_class] =
 +                        scan_vecparams(opt2parg_str(evStepOptions[ev_class], NPA, pa), evStepOptions[ev_class], nvecs);
 +                }
 +                else   /*if list is not given fill with zeros */
 +                {
 +                    snew(evStepList[ev_class], nvecs);
 +                    for (i = 0; i < nvecs; i++)
 +                    {
 +                        evStepList[ev_class][i] = 0.0;
 +                    }
 +                }
 +            }
-                     evStepList[ev_class][i] = radfix;
++            else if (ev_class == evRADFIX)
 +            {
 +                snew(evStepList[ev_class], nvecs);
 +                for (i = 0; i < nvecs; i++)
 +                {
++                    evStepList[ev_class][i] = radstep;
 +                }
 +            }
 +            else if (ev_class == evFLOOD)
 +            {
 +                snew(evStepList[ev_class], nvecs);
 +
 +                /* Are we doing constant force flooding? In that case, we read in
 +                 * the fproj values from the command line */
 +                if (opt2parg_bSet("-constF", NPA, pa))
 +                {
 +                    evStepList[ev_class] = scan_vecparams(opt2parg_str("-constF", NPA, pa), "-constF", nvecs);
 +                }
 +            }
 +            else
 +            {
 +            };   /*to avoid ambiguity   */
 +        }
 +        else     /* if there are no eigenvectors for this option set list to zero */
 +        {
 +            listen[ev_class] = NULL;
 +            snew(listen[ev_class], 1);
 +            listen[ev_class][0] = 0;
 +        }
 +    }
 +
 +    /* print the interpreted list of eigenvectors - to give some feedback*/
 +    for (ev_class = 0; ev_class < evNr; ++ev_class)
 +    {
 +        printf("Eigenvector list %7s consists of the indices: ", evOptions[ev_class]);
 +        i = 0;
 +        while (listen[ev_class][i])
 +        {
 +            printf("%d ", listen[ev_class][i++]);
 +        }
 +        printf("\n");
 +    }
 +
 +    EigvecFile = NULL;
 +    EigvecFile = opt2fn("-f", NFILE, fnm);
 +
 +    /*read eigenvectors from eigvec.trr*/
 +    read_eigenvectors(EigvecFile, &nav, &bFit1,
 +                      &xref1, &edi_params.fitmas, &xav1, &edi_params.pcamas, &nvec1, &eignr1, &eigvec1, &eigval1);
 +
 +    bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm),
 +                         title, &top, &ePBC, &xtop, NULL, topbox, 0);
 +    atoms = &top.atoms;
 +
 +
 +    printf("\nSelect an index group of %d elements that corresponds to the eigenvectors\n", nav);
 +    get_index(atoms, indexfile, 1, &i, &index, &grpname); /*if indexfile != NULL parameter 'atoms' is ignored */
 +    if (i != nav)
 +    {
 +        gmx_fatal(FARGS, "you selected a group with %d elements instead of %d",
 +                  i, nav);
 +    }
 +    printf("\n");
 +
 +
 +    if (xref1 == NULL)
 +    {
 +        if (bFit1)
 +        {
 +            /* if g_covar used different coordinate groups to fit and to do the PCA */
 +            printf("\nNote: the structure in %s should be the same\n"
 +                   "      as the one used for the fit in g_covar\n", ftp2fn(efTPS, NFILE, fnm));
 +            printf("\nSelect the index group that was used for the least squares fit in g_covar\n");
 +        }
 +        else
 +        {
 +            printf("\nNote: Apparently no fitting was done in g_covar.\n"
 +                   "      However, you need to select a reference group for fitting in mdrun\n");
 +        }
 +        get_index(atoms, indexfile, 1, &nfit, &ifit, &grpname);
 +        snew(xref1, nfit);
 +        for (i = 0; i < nfit; i++)
 +        {
 +            copy_rvec(xtop[ifit[i]], xref1[i]);
 +        }
 +    }
 +    else
 +    {
 +        nfit = nav;
 +        ifit = index;
 +    }
 +
 +    if (opt2parg_bSet("-constF", NPA, pa))
 +    {
 +        /* Constant force flooding is special: Most of the normal flooding
 +         * options are not needed. */
 +        edi_params.flood.bConstForce = TRUE;
 +    }
 +    else
 +    {
 +        /* For normal flooding read eigenvalues and store them in evSteplist[evFLOOD] */
 +
 +        if (listen[evFLOOD][0] != 0)
 +        {
 +            read_eigenvalues(listen[evFLOOD], opt2fn("-eig", NFILE, fnm), evStepList[evFLOOD], bHesse, kB*T);
 +        }
 +
 +        edi_params.flood.tau       = tau;
 +        edi_params.flood.deltaF0   = deltaF0;
 +        edi_params.flood.deltaF    = deltaF;
 +        edi_params.presteps        = eqSteps;
 +        edi_params.flood.kT        = kB*T;
 +        edi_params.flood.bHarmonic = bHarmonic;
 +        if (bRestrain)
 +        {
 +            /* Trick: invert sign of Efl and alpha2 then this will give the same sign in the exponential and inverted sign outside */
 +            edi_params.flood.constEfl = -constEfl;
 +            edi_params.flood.alpha2   = -sqr(alpha);
 +        }
 +        else
 +        {
 +            edi_params.flood.constEfl = constEfl;
 +            edi_params.flood.alpha2   = sqr(alpha);
 +        }
 +    }
 +
 +    edi_params.ned = nav;
 +
 +    /*number of system atoms  */
 +    edi_params.nini = atoms->nr;
 +
 +
 +    /*store reference and average structure in edi_params*/
 +    make_t_edx(&edi_params.sref, nfit, xref1, ifit );
 +    make_t_edx(&edi_params.sav, nav, xav1, index);
 +
 +
 +    /* Store target positions in edi_params */
 +    if (opt2bSet("-tar", NFILE, fnm))
 +    {
 +        if (0 != listen[evFLOOD][0])
 +        {
 +            fprintf(stderr, "\nNote: Providing a TARGET structure has no effect when using flooding.\n"
 +                    "      You may want to use -ori to define the flooding potential center.\n\n");
 +        }
 +        get_structure(atoms, indexfile, TargetFile, &edi_params.star, nfit, ifit, nav, index);
 +    }
 +    else
 +    {
 +        make_t_edx(&edi_params.star, 0, NULL, index);
 +    }
 +
 +    /* Store origin positions */
 +    if (opt2bSet("-ori", NFILE, fnm))
 +    {
 +        get_structure(atoms, indexfile, OriginFile, &edi_params.sori, nfit, ifit, nav, index);
 +    }
 +    else
 +    {
 +        make_t_edx(&edi_params.sori, 0, NULL, index);
 +    }
 +
 +    /* Write edi-file */
 +    write_the_whole_thing(ffopen(EdiFile, "w"), &edi_params, eigvec1, nvec1, listen, evStepList);
 +
 +    return 0;
 +}
index 3cc1358633a0cd5cc28cca3d2a3f5161bd4a0656,0000000000000000000000000000000000000000..7274187626040991b722337dffa6b9295b5208c4
mode 100644,000000..100644
--- /dev/null
@@@ -1,835 -1,0 +1,836 @@@
- static int get_nthreads_hw_avail(const t_commrec gmx_unused *cr)
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 2012,2013, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdlib.h>
 +#include <assert.h>
 +#include <string.h>
 +
 +#ifdef HAVE_UNISTD_H
 +/* For sysconf */
 +#include <unistd.h>
 +#endif
 +
 +#include "types/enums.h"
 +#include "types/hw_info.h"
 +#include "types/commrec.h"
 +#include "gmx_fatal.h"
 +#include "gmx_fatal_collective.h"
 +#include "smalloc.h"
 +#include "gpu_utils.h"
 +#include "copyrite.h"
 +#include "gmx_detect_hardware.h"
 +#include "main.h"
 +#include "md_logging.h"
++#include "gromacs/utility/gmxomp.h"
 +
 +#include "thread_mpi/threads.h"
 +
 +#if ((defined(WIN32) || defined( _WIN32 ) || defined(WIN64) || defined( _WIN64 )) && !(defined (__CYGWIN__) || defined (__CYGWIN32__)))
 +#include "windows.h"
 +#endif
 +
 +#ifdef GMX_GPU
 +const gmx_bool bGPUBinary = TRUE;
 +#else
 +const gmx_bool bGPUBinary = FALSE;
 +#endif
 +
 +static const char * invalid_gpuid_hint =
 +    "A delimiter-free sequence of valid numeric IDs of available GPUs is expected.";
 +
 +/* The globally shared hwinfo structure. */
 +static gmx_hw_info_t      *hwinfo_g;
 +/* A reference counter for the hwinfo structure */
 +static int                 n_hwinfo = 0;
 +/* A lock to protect the hwinfo structure */
 +static tMPI_Thread_mutex_t hw_info_lock = TMPI_THREAD_MUTEX_INITIALIZER;
 +
 +
 +/* FW decl. */
 +static void limit_num_gpus_used(gmx_gpu_opt_t *gpu_opt, int count);
 +static int gmx_count_gpu_dev_unique(const gmx_gpu_info_t *gpu_info,
 +                                    const gmx_gpu_opt_t  *gpu_opt);
 +
 +static void sprint_gpus(char *sbuf, const gmx_gpu_info_t *gpu_info)
 +{
 +    int      i, ndev;
 +    char     stmp[STRLEN];
 +
 +    ndev = gpu_info->ncuda_dev;
 +
 +    sbuf[0] = '\0';
 +    for (i = 0; i < ndev; i++)
 +    {
 +        get_gpu_device_info_string(stmp, gpu_info, i);
 +        strcat(sbuf, "  ");
 +        strcat(sbuf, stmp);
 +        if (i < ndev - 1)
 +        {
 +            strcat(sbuf, "\n");
 +        }
 +    }
 +}
 +
 +static void print_gpu_detection_stats(FILE                 *fplog,
 +                                      const gmx_gpu_info_t *gpu_info,
 +                                      const t_commrec      *cr)
 +{
 +    char onhost[266], stmp[STRLEN];
 +    int  ngpu;
 +
 +    if (!gpu_info->bDetectGPUs)
 +    {
 +        /* We skipped the detection, so don't print detection stats */
 +        return;
 +    }
 +
 +    ngpu = gpu_info->ncuda_dev;
 +
 +#if defined GMX_MPI && !defined GMX_THREAD_MPI
 +    /* We only print the detection on one, of possibly multiple, nodes */
 +    strncpy(onhost, " on host ", 10);
 +    gmx_gethostname(onhost+9, 256);
 +#else
 +    /* We detect all relevant GPUs */
 +    strncpy(onhost, "", 1);
 +#endif
 +
 +    if (ngpu > 0)
 +    {
 +        sprint_gpus(stmp, gpu_info);
 +        md_print_warn(cr, fplog, "%d GPU%s detected%s:\n%s\n",
 +                      ngpu, (ngpu > 1) ? "s" : "", onhost, stmp);
 +    }
 +    else
 +    {
 +        md_print_warn(cr, fplog, "No GPUs detected%s\n", onhost);
 +    }
 +}
 +
 +static void print_gpu_use_stats(FILE                 *fplog,
 +                                const gmx_gpu_info_t *gpu_info,
 +                                const gmx_gpu_opt_t  *gpu_opt,
 +                                const t_commrec      *cr)
 +{
 +    char sbuf[STRLEN], stmp[STRLEN];
 +    int  i, ngpu_comp, ngpu_use;
 +
 +    ngpu_comp = gpu_info->ncuda_dev_compatible;
 +    ngpu_use  = gpu_opt->ncuda_dev_use;
 +
 +    /* Issue a note if GPUs are available but not used */
 +    if (ngpu_comp > 0 && ngpu_use < 1)
 +    {
 +        sprintf(sbuf,
 +                "%d compatible GPU%s detected in the system, but none will be used.\n"
 +                "Consider trying GPU acceleration with the Verlet scheme!",
 +                ngpu_comp, (ngpu_comp > 1) ? "s" : "");
 +    }
 +    else
 +    {
 +        int ngpu_use_uniq;
 +
 +        ngpu_use_uniq = gmx_count_gpu_dev_unique(gpu_info, gpu_opt);
 +
 +        sprintf(sbuf, "%d GPU%s %sselected for this run.\n"
 +                "Mapping of GPU%s to the %d PP rank%s in this node: ",
 +                ngpu_use_uniq, (ngpu_use_uniq > 1) ? "s" : "",
 +                gpu_opt->bUserSet ? "user-" : "auto-",
 +                (ngpu_use > 1) ? "s" : "",
 +                cr->nrank_pp_intranode,
 +                (cr->nrank_pp_intranode > 1) ? "s" : "");
 +
 +        for (i = 0; i < ngpu_use; i++)
 +        {
 +            sprintf(stmp, "#%d", get_gpu_device_id(gpu_info, gpu_opt, i));
 +            if (i < ngpu_use - 1)
 +            {
 +                strcat(stmp, ", ");
 +            }
 +            strcat(sbuf, stmp);
 +        }
 +    }
 +    md_print_info(cr, fplog, "%s\n\n", sbuf);
 +}
 +
 +/* Parse a "plain" GPU ID string which contains a sequence of digits corresponding
 + * to GPU IDs; the order will indicate the process/tMPI thread - GPU assignment. */
 +static void parse_gpu_id_plain_string(const char *idstr, int *nid, int **idlist)
 +{
 +    int i;
 +
 +    *nid = strlen(idstr);
 +
 +    snew(*idlist, *nid);
 +
 +    for (i = 0; i < *nid; i++)
 +    {
 +        if (idstr[i] < '0' || idstr[i] > '9')
 +        {
 +            gmx_fatal(FARGS, "Invalid character in GPU ID string: '%c'\n%s\n",
 +                      idstr[i], invalid_gpuid_hint);
 +        }
 +        (*idlist)[i] = idstr[i] - '0';
 +    }
 +}
 +
 +static void parse_gpu_id_csv_string(const char gmx_unused *idstr, int gmx_unused *nid, int gmx_unused *idlist)
 +{
 +    /* XXX implement cvs format to support more than 10 different GPUs in a box. */
 +    gmx_incons("Not implemented yet");
 +}
 +
 +void gmx_check_hw_runconf_consistency(FILE                *fplog,
 +                                      const gmx_hw_info_t *hwinfo,
 +                                      const t_commrec     *cr,
 +                                      const gmx_hw_opt_t  *hw_opt,
 +                                      gmx_bool             bUseGPU)
 +{
 +    int      npppn, ntmpi_pp;
 +    char     sbuf[STRLEN], th_or_proc[STRLEN], th_or_proc_plural[STRLEN], pernode[STRLEN];
 +    gmx_bool btMPI, bMPI, bMaxMpiThreadsSet, bNthreadsAuto, bEmulateGPU;
 +
 +    assert(hwinfo);
 +    assert(cr);
 +
 +    /* Below we only do consistency checks for PP and GPUs,
 +     * this is irrelevant for PME only nodes, so in that case we return
 +     * here.
 +     */
 +    if (!(cr->duty & DUTY_PP))
 +    {
 +        return;
 +    }
 +
 +    btMPI         = bMPI = FALSE;
 +    bNthreadsAuto = FALSE;
 +#if defined(GMX_THREAD_MPI)
 +    btMPI         = TRUE;
 +    bNthreadsAuto = (hw_opt->nthreads_tmpi < 1);
 +#elif defined(GMX_LIB_MPI)
 +    bMPI  = TRUE;
 +#endif
 +
 +    /* GPU emulation detection is done later, but we need here as well
 +     * -- uncool, but there's no elegant workaround */
 +    bEmulateGPU       = (getenv("GMX_EMULATE_GPU") != NULL);
 +    bMaxMpiThreadsSet = (getenv("GMX_MAX_MPI_THREADS") != NULL);
 +
 +    /* check the acceleration mdrun is compiled with against hardware
 +       capabilities */
 +    /* TODO: Here we assume homogeneous hardware which is not necessarily
 +             the case! Might not hurt to add an extra check over MPI. */
 +    gmx_cpuid_acceleration_check(hwinfo->cpuid_info, fplog, SIMMASTER(cr));
 +
 +    /* NOTE: this print is only for and on one physical node */
 +    print_gpu_detection_stats(fplog, &hwinfo->gpu_info, cr);
 +
 +    if (hwinfo->gpu_info.ncuda_dev_compatible > 0)
 +    {
 +        /* NOTE: this print is only for and on one physical node */
 +        print_gpu_use_stats(fplog, &hwinfo->gpu_info, &hw_opt->gpu_opt, cr);
 +    }
 +
 +    /* Need to ensure that we have enough GPUs:
 +     * - need one GPU per PP node
 +     * - no GPU oversubscription with tMPI
 +     * */
 +    /* number of PP processes per node */
 +    npppn = cr->nrank_pp_intranode;
 +
 +    pernode[0]           = '\0';
 +    th_or_proc_plural[0] = '\0';
 +    if (btMPI)
 +    {
 +        sprintf(th_or_proc, "thread-MPI thread");
 +        if (npppn > 1)
 +        {
 +            sprintf(th_or_proc_plural, "s");
 +        }
 +    }
 +    else if (bMPI)
 +    {
 +        sprintf(th_or_proc, "MPI process");
 +        if (npppn > 1)
 +        {
 +            sprintf(th_or_proc_plural, "es");
 +        }
 +        sprintf(pernode, " per node");
 +    }
 +    else
 +    {
 +        /* neither MPI nor tMPI */
 +        sprintf(th_or_proc, "process");
 +    }
 +
 +    if (bUseGPU && hwinfo->gpu_info.ncuda_dev_compatible > 0 &&
 +        !bEmulateGPU)
 +    {
 +        int  ngpu_comp, ngpu_use;
 +        char gpu_comp_plural[2], gpu_use_plural[2];
 +
 +        ngpu_comp = hwinfo->gpu_info.ncuda_dev_compatible;
 +        ngpu_use  = hw_opt->gpu_opt.ncuda_dev_use;
 +
 +        sprintf(gpu_comp_plural, "%s", (ngpu_comp> 1) ? "s" : "");
 +        sprintf(gpu_use_plural,  "%s", (ngpu_use > 1) ? "s" : "");
 +
 +        /* number of tMPI threads auto-adjusted */
 +        if (btMPI && bNthreadsAuto)
 +        {
 +            if (hw_opt->gpu_opt.bUserSet && npppn < ngpu_use)
 +            {
 +                /* The user manually provided more GPUs than threads we
 +                   could automatically start. */
 +                gmx_fatal(FARGS,
 +                          "%d GPU%s provided, but only %d PP thread-MPI thread%s coud be started.\n"
 +                          "%s requires one PP tread-MPI thread per GPU; use fewer GPUs%s.",
 +                          ngpu_use, gpu_use_plural,
 +                          npppn, th_or_proc_plural,
 +                          ShortProgram(), bMaxMpiThreadsSet ? "\nor allow more threads to be used" : "");
 +            }
 +
 +            if (!hw_opt->gpu_opt.bUserSet && npppn < ngpu_comp)
 +            {
 +                /* There are more GPUs than tMPI threads; we have
 +                   limited the number GPUs used. */
 +                md_print_warn(cr, fplog,
 +                              "NOTE: %d GPU%s were detected, but only %d PP thread-MPI thread%s can be started.\n"
 +                              "      %s can use one GPU per PP tread-MPI thread, so only %d GPU%s will be used.%s\n",
 +                              ngpu_comp, gpu_comp_plural,
 +                              npppn, th_or_proc_plural,
 +                              ShortProgram(), npppn,
 +                              npppn > 1 ? "s" : "",
 +                              bMaxMpiThreadsSet ? "\n      Also, you can allow more threads to be used by increasing GMX_MAX_MPI_THREADS" : "");
 +            }
 +        }
 +
 +        if (hw_opt->gpu_opt.bUserSet)
 +        {
 +            if (ngpu_use != npppn)
 +            {
 +                gmx_fatal(FARGS,
 +                          "Incorrect launch configuration: mismatching number of PP %s%s and GPUs%s.\n"
 +                          "%s was started with %d PP %s%s%s, but you provided %d GPU%s.",
 +                          th_or_proc, btMPI ? "s" : "es", pernode,
 +                          ShortProgram(), npppn, th_or_proc,
 +                          th_or_proc_plural, pernode,
 +                          ngpu_use, gpu_use_plural);
 +            }
 +        }
 +        else
 +        {
 +            if (ngpu_comp > npppn)
 +            {
 +                md_print_warn(cr, fplog,
 +                              "NOTE: potentially sub-optimal launch configuration, %s started with less\n"
 +                              "      PP %s%s%s than GPU%s available.\n"
 +                              "      Each PP %s can use only one GPU, %d GPU%s%s will be used.\n",
 +                              ShortProgram(), th_or_proc,
 +                              th_or_proc_plural, pernode, gpu_comp_plural,
 +                              th_or_proc, npppn, gpu_use_plural, pernode);
 +            }
 +
 +            if (ngpu_use != npppn)
 +            {
 +                /* Avoid duplicate error messages.
 +                 * Unfortunately we can only do this at the physical node
 +                 * level, since the hardware setup and MPI process count
 +                 * might differ between physical nodes.
 +                 */
 +                if (cr->rank_pp_intranode == 0)
 +                {
 +                    gmx_fatal(FARGS,
 +                              "Incorrect launch configuration: mismatching number of PP %s%s and GPUs%s.\n"
 +                              "%s was started with %d PP %s%s%s, but only %d GPU%s were detected.",
 +                              th_or_proc, btMPI ? "s" : "es", pernode,
 +                              ShortProgram(), npppn, th_or_proc,
 +                              th_or_proc_plural, pernode,
 +                              ngpu_use, gpu_use_plural);
 +                }
 +            }
 +        }
 +
 +        {
 +            int      same_count;
 +
 +            same_count = gmx_count_gpu_dev_shared(&hw_opt->gpu_opt);
 +
 +            if (same_count > 0)
 +            {
 +                md_print_info(cr, fplog,
 +                              "NOTE: You assigned %s to multiple %s%s.\n",
 +                              same_count > 1 ? "GPUs" : "a GPU", th_or_proc, btMPI ? "s" : "es");
 +            }
 +        }
 +    }
 +
 +#ifdef GMX_MPI
 +    if (PAR(cr))
 +    {
 +        /* Avoid other ranks to continue after
 +           inconsistency */
 +        MPI_Barrier(cr->mpi_comm_mygroup);
 +    }
 +#endif
 +
 +}
 +
 +/* Return 0 if none of the GPU (per node) are shared among PP ranks.
 + *
 + * Sharing GPUs among multiple PP ranks is possible when the user passes
 + * GPU IDs. Here we check for sharing and return a non-zero value when
 + * this is detected. Note that the return value represents the number of
 + * PP rank pairs that share a device.
 + */
 +int gmx_count_gpu_dev_shared(const gmx_gpu_opt_t *gpu_opt)
 +{
 +    int      same_count    = 0;
 +    int      ngpu          = gpu_opt->ncuda_dev_use;
 +
 +    if (gpu_opt->bUserSet)
 +    {
 +        int      i, j;
 +
 +        for (i = 0; i < ngpu - 1; i++)
 +        {
 +            for (j = i + 1; j < ngpu; j++)
 +            {
 +                same_count      += (gpu_opt->cuda_dev_use[i] ==
 +                                    gpu_opt->cuda_dev_use[j]);
 +            }
 +        }
 +    }
 +
 +    return same_count;
 +}
 +
 +/* Count and return the number of unique GPUs (per node) selected.
 + *
 + * As sharing GPUs among multiple PP ranks is possible when the user passes
 + * GPU IDs, the number of GPUs user (per node) can be different from the
 + * number of GPU IDs selected.
 + */
 +static int gmx_count_gpu_dev_unique(const gmx_gpu_info_t *gpu_info,
 +                                    const gmx_gpu_opt_t  *gpu_opt)
 +{
 +    int  i, uniq_count, ngpu;
 +    int *uniq_ids;
 +
 +    assert(gpu_info);
 +    assert(gpu_opt);
 +
 +    ngpu        = gpu_info->ncuda_dev;
 +    uniq_count  = 0;
 +
 +    snew(uniq_ids, ngpu);
 +
 +    /* Each element in uniq_ids will be set to 0 or 1. The n-th element set
 +     * to 1 indicates that the respective GPU was selected to be used. */
 +    for (i = 0; i < gpu_opt->ncuda_dev_use; i++)
 +    {
 +        uniq_ids[get_gpu_device_id(gpu_info, gpu_opt, i)] = 1;
 +    }
 +    /* Count the devices used. */
 +    for (i = 0; i < ngpu; i++)
 +    {
 +        uniq_count += uniq_ids[i];
 +    }
 +
 +    sfree(uniq_ids);
 +
 +    return uniq_count;
 +}
 +
 +
 +/* Return the number of hardware threads supported by the current CPU.
 + * We assume that this is equal with the number of CPUs reported to be
 + * online by the OS at the time of the call.
 + */
- #ifdef GMX_OMPENMP
++static int get_nthreads_hw_avail(FILE gmx_unused *fplog, const t_commrec gmx_unused *cr)
 +{
 +    int ret = 0;
 +
 +#if ((defined(WIN32) || defined( _WIN32 ) || defined(WIN64) || defined( _WIN64 )) && !(defined (__CYGWIN__) || defined (__CYGWIN32__)))
 +    /* Windows */
 +    SYSTEM_INFO sysinfo;
 +    GetSystemInfo( &sysinfo );
 +    ret = sysinfo.dwNumberOfProcessors;
 +#elif defined HAVE_SYSCONF
 +    /* We are probably on Unix.
 +     * Now check if we have the argument to use before executing the call
 +     */
 +#if defined(_SC_NPROCESSORS_ONLN)
 +    ret = sysconf(_SC_NPROCESSORS_ONLN);
 +#elif defined(_SC_NPROC_ONLN)
 +    ret = sysconf(_SC_NPROC_ONLN);
 +#elif defined(_SC_NPROCESSORS_CONF)
 +    ret = sysconf(_SC_NPROCESSORS_CONF);
 +#elif defined(_SC_NPROC_CONF)
 +    ret = sysconf(_SC_NPROC_CONF);
 +#else
 +#warning "No valid sysconf argument value found. Executables will not be able to determine the number of hardware threads: mdrun will use 1 thread by default!"
 +#endif /* End of check for sysconf argument values */
 +
 +#else
 +    /* Neither windows nor Unix. No fscking idea how many CPUs we have! */
 +    ret = -1;
 +#endif
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Detected %d processors, will use this as the number "
 +                "of supported hardware threads.\n", ret);
 +    }
 +
-         hwinfo_g->nthreads_hw_avail = get_nthreads_hw_avail(cr);
++#ifdef GMX_OPENMP
 +    if (ret != gmx_omp_get_num_procs())
 +    {
 +        md_print_warn(cr, fplog,
 +                      "Number of CPUs detected (%d) does not match the number reported by OpenMP (%d).\n"
 +                      "Consider setting the launch configuration manually!",
 +                      ret, gmx_omp_get_num_procs());
 +    }
 +#endif
 +
 +    return ret;
 +}
 +
 +static void gmx_detect_gpus(FILE *fplog, const t_commrec *cr)
 +{
 +#ifdef GMX_LIB_MPI
 +    int              rank_world;
 +    MPI_Comm         physicalnode_comm;
 +#endif
 +    int              rank_local;
 +
 +    /* Under certain circumstances MPI ranks on the same physical node
 +     * can not simultaneously access the same GPU(s). Therefore we run
 +     * the detection only on one MPI rank per node and broadcast the info.
 +     * Note that with thread-MPI only a single thread runs this code.
 +     *
 +     * TODO: We should also do CPU hardware detection only once on each
 +     * physical node and broadcast it, instead of do it on every MPI rank.
 +     */
 +#ifdef GMX_LIB_MPI
 +    /* A split of MPI_COMM_WORLD over physical nodes is only required here,
 +     * so we create and destroy it locally.
 +     */
 +    MPI_Comm_rank(MPI_COMM_WORLD, &rank_world);
 +    MPI_Comm_split(MPI_COMM_WORLD, gmx_physicalnode_id_hash(),
 +                   rank_world, &physicalnode_comm);
 +    MPI_Comm_rank(physicalnode_comm, &rank_local);
 +#else
 +    /* Here there should be only one process, check this */
 +    assert(cr->nnodes == 1 && cr->sim_nodeid == 0);
 +
 +    rank_local = 0;
 +#endif
 +
 +    if (rank_local == 0)
 +    {
 +        char detection_error[STRLEN], sbuf[STRLEN];
 +
 +        if (detect_cuda_gpus(&hwinfo_g->gpu_info, detection_error) != 0)
 +        {
 +            if (detection_error != NULL && detection_error[0] != '\0')
 +            {
 +                sprintf(sbuf, ":\n      %s\n", detection_error);
 +            }
 +            else
 +            {
 +                sprintf(sbuf, ".");
 +            }
 +            md_print_warn(cr, fplog,
 +                          "NOTE: Error occurred during GPU detection%s"
 +                          "      Can not use GPU acceleration, will fall back to CPU kernels.\n",
 +                          sbuf);
 +        }
 +    }
 +
 +#ifdef GMX_LIB_MPI
 +    /* Broadcast the GPU info to the other ranks within this node */
 +    MPI_Bcast(&hwinfo_g->gpu_info.ncuda_dev, 1, MPI_INT, 0, physicalnode_comm);
 +
 +    if (hwinfo_g->gpu_info.ncuda_dev > 0)
 +    {
 +        int cuda_dev_size;
 +
 +        cuda_dev_size = hwinfo_g->gpu_info.ncuda_dev*sizeof_cuda_dev_info();
 +
 +        if (rank_local > 0)
 +        {
 +            hwinfo_g->gpu_info.cuda_dev =
 +                (cuda_dev_info_ptr_t)malloc(cuda_dev_size);
 +        }
 +        MPI_Bcast(hwinfo_g->gpu_info.cuda_dev, cuda_dev_size, MPI_BYTE,
 +                  0, physicalnode_comm);
 +        MPI_Bcast(&hwinfo_g->gpu_info.ncuda_dev_compatible, 1, MPI_INT,
 +                  0, physicalnode_comm);
 +    }
 +
 +    MPI_Comm_free(&physicalnode_comm);
 +#endif
 +}
 +
 +gmx_hw_info_t *gmx_detect_hardware(FILE *fplog, const t_commrec *cr,
 +                                   gmx_bool bDetectGPUs)
 +{
 +    gmx_hw_info_t   *hw;
 +    int              ret;
 +
 +    /* make sure no one else is doing the same thing */
 +    ret = tMPI_Thread_mutex_lock(&hw_info_lock);
 +    if (ret != 0)
 +    {
 +        gmx_fatal(FARGS, "Error locking hwinfo mutex: %s", strerror(errno));
 +    }
 +
 +    /* only initialize the hwinfo structure if it is not already initalized */
 +    if (n_hwinfo == 0)
 +    {
 +        snew(hwinfo_g, 1);
 +
 +        /* detect CPUID info; no fuss, we don't detect system-wide
 +         * -- sloppy, but that's it for now */
 +        if (gmx_cpuid_init(&hwinfo_g->cpuid_info) != 0)
 +        {
 +            gmx_fatal_collective(FARGS, cr, NULL, "CPUID detection failed!");
 +        }
 +
 +        /* detect number of hardware threads */
++        hwinfo_g->nthreads_hw_avail = get_nthreads_hw_avail(fplog, cr);
 +
 +        /* detect GPUs */
 +        hwinfo_g->gpu_info.ncuda_dev            = 0;
 +        hwinfo_g->gpu_info.cuda_dev             = NULL;
 +        hwinfo_g->gpu_info.ncuda_dev_compatible = 0;
 +
 +        /* Run the detection if the binary was compiled with GPU support
 +         * and we requested detection.
 +         */
 +        hwinfo_g->gpu_info.bDetectGPUs =
 +            (bGPUBinary && bDetectGPUs &&
 +             getenv("GMX_DISABLE_GPU_DETECTION") == NULL);
 +        if (hwinfo_g->gpu_info.bDetectGPUs)
 +        {
 +            gmx_detect_gpus(fplog, cr);
 +        }
 +    }
 +    /* increase the reference counter */
 +    n_hwinfo++;
 +
 +    ret = tMPI_Thread_mutex_unlock(&hw_info_lock);
 +    if (ret != 0)
 +    {
 +        gmx_fatal(FARGS, "Error unlocking hwinfo mutex: %s", strerror(errno));
 +    }
 +
 +    return hwinfo_g;
 +}
 +
 +void gmx_parse_gpu_ids(gmx_gpu_opt_t *gpu_opt)
 +{
 +    char *env;
 +
 +    if (gpu_opt->gpu_id != NULL && !bGPUBinary)
 +    {
 +        gmx_fatal(FARGS, "GPU ID string set, but %s was compiled without GPU support!", ShortProgram());
 +    }
 +
 +    env = getenv("GMX_GPU_ID");
 +    if (env != NULL && gpu_opt->gpu_id != NULL)
 +    {
 +        gmx_fatal(FARGS, "GMX_GPU_ID and -gpu_id can not be used at the same time");
 +    }
 +    if (env == NULL)
 +    {
 +        env = gpu_opt->gpu_id;
 +    }
 +
 +    /* parse GPU IDs if the user passed any */
 +    if (env != NULL)
 +    {
 +        parse_gpu_id_plain_string(env,
 +                                  &gpu_opt->ncuda_dev_use,
 +                                  &gpu_opt->cuda_dev_use);
 +
 +        if (gpu_opt->ncuda_dev_use == 0)
 +        {
 +            gmx_fatal(FARGS, "Empty GPU ID string encountered.\n%s\n",
 +                      invalid_gpuid_hint);
 +        }
 +
 +        gpu_opt->bUserSet = TRUE;
 +    }
 +}
 +
 +void gmx_select_gpu_ids(FILE *fplog, const t_commrec *cr,
 +                        const gmx_gpu_info_t *gpu_info,
 +                        gmx_bool bForceUseGPU,
 +                        gmx_gpu_opt_t *gpu_opt)
 +{
 +    int              i;
 +    const char      *env;
 +    char             sbuf[STRLEN], stmp[STRLEN];
 +
 +    /* Bail if binary is not compiled with GPU acceleration, but this is either
 +     * explicitly (-nb gpu) or implicitly (gpu ID passed) requested. */
 +    if (bForceUseGPU && !bGPUBinary)
 +    {
 +        gmx_fatal(FARGS, "GPU acceleration requested, but %s was compiled without GPU support!", ShortProgram());
 +    }
 +
 +    if (gpu_opt->bUserSet)
 +    {
 +        /* Check the GPU IDs passed by the user.
 +         * (GPU IDs have been parsed by gmx_parse_gpu_ids before)
 +         */
 +        int *checkres;
 +        int  res;
 +
 +        snew(checkres, gpu_opt->ncuda_dev_use);
 +
 +        res = check_selected_cuda_gpus(checkres, gpu_info, gpu_opt);
 +
 +        if (!res)
 +        {
 +            print_gpu_detection_stats(fplog, gpu_info, cr);
 +
 +            sprintf(sbuf, "Some of the requested GPUs do not exist, behave strangely, or are not compatible:\n");
 +            for (i = 0; i < gpu_opt->ncuda_dev_use; i++)
 +            {
 +                if (checkres[i] != egpuCompatible)
 +                {
 +                    sprintf(stmp, "    GPU #%d: %s\n",
 +                            gpu_opt->cuda_dev_use[i],
 +                            gpu_detect_res_str[checkres[i]]);
 +                    strcat(sbuf, stmp);
 +                }
 +            }
 +            gmx_fatal(FARGS, "%s", sbuf);
 +        }
 +
 +        sfree(checkres);
 +    }
 +    else
 +    {
 +        pick_compatible_gpus(&hwinfo_g->gpu_info, gpu_opt);
 +
 +        if (gpu_opt->ncuda_dev_use > cr->nrank_pp_intranode)
 +        {
 +            /* We picked more GPUs than we can use: limit the number.
 +             * We print detailed messages about this later in
 +             * gmx_check_hw_runconf_consistency.
 +             */
 +            limit_num_gpus_used(gpu_opt, cr->nrank_pp_intranode);
 +        }
 +
 +        gpu_opt->bUserSet = FALSE;
 +    }
 +
 +    /* If the user asked for a GPU, check whether we have a GPU */
 +    if (bForceUseGPU && gpu_info->ncuda_dev_compatible == 0)
 +    {
 +        gmx_fatal(FARGS, "GPU acceleration requested, but no compatible GPUs were detected.");
 +    }
 +}
 +
 +static void limit_num_gpus_used(gmx_gpu_opt_t *gpu_opt, int count)
 +{
 +    int ndev_use;
 +
 +    assert(gpu_opt);
 +
 +    ndev_use = gpu_opt->ncuda_dev_use;
 +
 +    if (count > ndev_use)
 +    {
 +        /* won't increase the # of GPUs */
 +        return;
 +    }
 +
 +    if (count < 1)
 +    {
 +        char sbuf[STRLEN];
 +        sprintf(sbuf, "Limiting the number of GPUs to <1 doesn't make sense (detected %d, %d requested)!",
 +                ndev_use, count);
 +        gmx_incons(sbuf);
 +    }
 +
 +    /* TODO: improve this implementation: either sort GPUs or remove the weakest here */
 +    gpu_opt->ncuda_dev_use = count;
 +}
 +
 +void gmx_hardware_info_free(gmx_hw_info_t *hwinfo)
 +{
 +    int ret;
 +
 +    ret = tMPI_Thread_mutex_lock(&hw_info_lock);
 +    if (ret != 0)
 +    {
 +        gmx_fatal(FARGS, "Error locking hwinfo mutex: %s", strerror(errno));
 +    }
 +
 +    /* decrease the reference counter */
 +    n_hwinfo--;
 +
 +
 +    if (hwinfo != hwinfo_g)
 +    {
 +        gmx_incons("hwinfo < hwinfo_g");
 +    }
 +
 +    if (n_hwinfo < 0)
 +    {
 +        gmx_incons("n_hwinfo < 0");
 +    }
 +
 +    if (n_hwinfo == 0)
 +    {
 +        gmx_cpuid_done(hwinfo_g->cpuid_info);
 +        free_gpu_info(&hwinfo_g->gpu_info);
 +        sfree(hwinfo_g);
 +    }
 +
 +    ret = tMPI_Thread_mutex_unlock(&hw_info_lock);
 +    if (ret != 0)
 +    {
 +        gmx_fatal(FARGS, "Error unlocking hwinfo mutex: %s", strerror(errno));
 +    }
 +}
index d0bccbc811d4a9f84271e336d29e9df6b24b24f8,0000000000000000000000000000000000000000..737eecddd067041a153f45851592dc5ffbe5eba6
mode 100644,000000..100644
--- /dev/null
@@@ -1,3174 -1,0 +1,3189 @@@
-     real   *refproj; /* starting or target projecions  */
 +/* -*- 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 <stdio.h>
 +#include <time.h>
 +#include "typedefs.h"
 +#include "string2.h"
 +#include "smalloc.h"
 +#include "names.h"
 +#include "gromacs/fileio/confio.h"
 +#include "mvdata.h"
 +#include "txtdump.h"
 +#include "vec.h"
 +#include <time.h>
 +#include "nrnb.h"
 +#include "mshift.h"
 +#include "mdrun.h"
 +#include "update.h"
 +#include "physics.h"
 +#include "nrjac.h"
 +#include "mtop_util.h"
 +#include "edsam.h"
 +#include "gromacs/fileio/gmxfio.h"
 +#include "xvgr.h"
 +#include "groupcoord.h"
 +
 +
 +/* We use the same defines as in mvdata.c here */
 +#define  block_bc(cr,   d) gmx_bcast(     sizeof(d),     &(d), (cr))
 +#define nblock_bc(cr, nr, d) gmx_bcast((nr)*sizeof((d)[0]), (d), (cr))
 +#define   snew_bc(cr, d, nr) { if (!MASTER(cr)) {snew((d), (nr)); }}
 +
 +/* These macros determine the column width in the output file */
 +#define EDcol_sfmt "%17s"
 +#define EDcol_efmt "%17.5e"
 +#define EDcol_ffmt "%17f"
 +
 +/* enum to identify the type of ED: none, normal ED, flooding */
 +enum {
 +    eEDnone, eEDedsam, eEDflood, eEDnr
 +};
 +
 +/* enum to identify operations on reference, average, origin, target structures */
 +enum {
 +    eedREF, eedAV, eedORI, eedTAR, eedNR
 +};
 +
 +
 +typedef struct
 +{
 +    int     neig;    /* nr of eigenvectors             */
 +    int    *ieig;    /* index nrs of eigenvectors      */
 +    real   *stpsz;   /* stepsizes (per eigenvector)    */
 +    rvec  **vec;     /* eigenvector components         */
 +    real   *xproj;   /* instantaneous x projections    */
 +    real   *fproj;   /* instantaneous f projections    */
 +    real    radius;  /* instantaneous radius           */
-     gmx_bool            bNeedDoEdsam; /* if any of the options mon, linfix, ...
-                                        * is used (i.e. apart from flooding)   */
++    real   *refproj; /* starting or target projections */
 +    /* When using flooding as harmonic restraint: The current reference projection
 +     * is at each step calculated from the initial refproj0 and the slope. */
 +    real  *refproj0, *refprojslope;
 +} t_eigvec;
 +
 +
 +typedef struct
 +{
 +    t_eigvec      mon;            /* only monitored, no constraints       */
 +    t_eigvec      linfix;         /* fixed linear constraints             */
 +    t_eigvec      linacc;         /* acceptance linear constraints        */
 +    t_eigvec      radfix;         /* fixed radial constraints (exp)       */
 +    t_eigvec      radacc;         /* acceptance radial constraints (exp)  */
 +    t_eigvec      radcon;         /* acceptance rad. contraction constr.  */
 +} t_edvecs;
 +
 +
 +typedef struct
 +{
 +    real     deltaF0;
 +    gmx_bool bHarmonic;           /* Use flooding for harmonic restraint on
 +                                     the eigenvector                          */
 +    gmx_bool bConstForce;         /* Do not calculate a flooding potential,
 +                                     instead flood with a constant force      */
 +    real     tau;
 +    real     deltaF;
 +    real     Efl;
 +    real     kT;
 +    real     Vfl;
 +    real     dt;
 +    real     constEfl;
 +    real     alpha2;
 +    rvec    *forces_cartesian;
 +    t_eigvec vecs;         /* use flooding for these */
 +} t_edflood;
 +
 +
 +/* This type is for the average, reference, target, and origin structure    */
 +typedef struct gmx_edx
 +{
 +    int            nr;            /* number of atoms this structure contains  */
 +    int            nr_loc;        /* number of atoms on local node            */
 +    int           *anrs;          /* atom index numbers                       */
 +    int           *anrs_loc;      /* local atom index numbers                 */
 +    int            nalloc_loc;    /* allocation size of anrs_loc              */
 +    int           *c_ind;         /* at which position of the whole anrs
 +                                   * array is a local atom?, i.e.
 +                                   * c_ind[0...nr_loc-1] gives the atom index
 +                                   * with respect to the collective
 +                                   * anrs[0...nr-1] array                     */
 +    rvec          *x;             /* positions for this structure             */
 +    rvec          *x_old;         /* Last positions which have the correct PBC
 +                                     representation of the ED group. In
 +                                     combination with keeping track of the
 +                                     shift vectors, the ED group can always
 +                                     be made whole                            */
 +    real          *m;             /* masses                                   */
 +    real           mtot;          /* total mass (only used in sref)           */
 +    real          *sqrtm;         /* sqrt of the masses used for mass-
 +                                   * weighting of analysis (only used in sav) */
 +} t_gmx_edx;
 +
 +
 +typedef struct edpar
 +{
 +    int            nini;           /* total Nr of atoms                    */
 +    gmx_bool       fitmas;         /* true if trans fit with cm            */
 +    gmx_bool       pcamas;         /* true if mass-weighted PCA            */
 +    int            presteps;       /* number of steps to run without any
 +                                    *    perturbations ... just monitoring */
 +    int            outfrq;         /* freq (in steps) of writing to edo    */
 +    int            maxedsteps;     /* max nr of steps per cycle            */
 +
 +    /* all gmx_edx datasets are copied to all nodes in the parallel case   */
 +    struct gmx_edx      sref;         /* reference positions, to these fitting
 +                                       * will be done                         */
 +    gmx_bool            bRefEqAv;     /* If true, reference & average indices
 +                                       * are the same. Used for optimization  */
 +    struct gmx_edx      sav;          /* average positions                    */
 +    struct gmx_edx      star;         /* target positions                     */
 +    struct gmx_edx      sori;         /* origin positions                     */
 +
 +    t_edvecs            vecs;         /* eigenvectors                         */
 +    real                slope;        /* minimal slope in acceptance radexp   */
 +
-      * subroutine again, because these routines are rarely used simultanely */
 +    t_edflood           flood;        /* parameters especially for flooding   */
 +    struct t_ed_buffer *buf;          /* handle to local buffers              */
 +    struct edpar       *next_edi;     /* Pointer to another ED group          */
 +} t_edpar;
 +
 +
 +typedef struct gmx_edsam
 +{
 +    int            eEDtype;       /* Type of ED: see enums above          */
 +    FILE          *edo;           /* output file pointer                  */
 +    t_edpar       *edpar;
 +    gmx_bool       bFirst;
 +} t_gmx_edsam;
 +
 +
 +struct t_do_edsam
 +{
 +    matrix old_rotmat;
 +    real   oldrad;
 +    rvec   old_transvec, older_transvec, transvec_compact;
 +    rvec  *xcoll;                 /* Positions from all nodes, this is the
 +                                     collective set we work on.
 +                                     These are the positions of atoms with
 +                                     average structure indices */
 +    rvec    *xc_ref;              /* same but with reference structure indices */
 +    ivec    *shifts_xcoll;        /* Shifts for xcoll  */
 +    ivec    *extra_shifts_xcoll;  /* xcoll shift changes since last NS step */
 +    ivec    *shifts_xc_ref;       /* Shifts for xc_ref */
 +    ivec    *extra_shifts_xc_ref; /* xc_ref shift changes since last NS step */
 +    gmx_bool bUpdateShifts;       /* TRUE in NS steps to indicate that the
 +                                     ED shifts for this ED group need to
 +                                     be updated */
 +};
 +
 +
 +/* definition of ED buffer structure */
 +struct t_ed_buffer
 +{
 +    struct t_fit_to_ref *           fit_to_ref;
 +    struct t_do_edfit *             do_edfit;
 +    struct t_do_edsam *             do_edsam;
 +    struct t_do_radcon *            do_radcon;
 +};
 +
 +
 +/* Function declarations */
 +static void fit_to_reference(rvec *xcoll, rvec transvec, matrix rotmat, t_edpar *edi);
 +static void translate_and_rotate(rvec *x, int nat, rvec transvec, matrix rotmat);
 +static real rmsd_from_structure(rvec *x, struct gmx_edx *s);
 +static int read_edi_file(const char *fn, t_edpar *edi, int nr_mdatoms);
 +static void crosscheck_edi_file_vs_checkpoint(gmx_edsam_t ed, edsamstate_t *EDstate);
 +static void init_edsamstate(gmx_edsam_t ed, edsamstate_t *EDstate);
 +static void write_edo_legend(gmx_edsam_t ed, int nED, const output_env_t oenv);
 +/* End function declarations */
 +
 +
++/* Do we have to perform essential dynamics constraints or possibly only flooding
++ * for any of the ED groups? */
++static gmx_bool bNeedDoEdsam(t_edpar *edi)
++{
++    return     edi->vecs.mon.neig
++            || edi->vecs.linfix.neig
++            || edi->vecs.linacc.neig
++            || edi->vecs.radfix.neig
++            || edi->vecs.radacc.neig
++            || edi->vecs.radcon.neig;
++}
++
++
 +/* Multiple ED groups will be labeled with letters instead of numbers
 + * to avoid confusion with eigenvector indices */
 +static char get_EDgroupChar(int nr_edi, int nED)
 +{
 +    if (nED == 1)
 +    {
 +        return ' ';
 +    }
 +
 +    /* nr_edi = 1 -> A
 +     * nr_edi = 2 -> B ...
 +     */
 +    return 'A' + nr_edi - 1;
 +}
 +
 +
 +/* Does not subtract average positions, projection on single eigenvector is returned
 + * used by: do_linfix, do_linacc, do_radfix, do_radacc, do_radcon
 + * Average position is subtracted in ed_apply_constraints prior to calling projectx
 + */
 +static real projectx(t_edpar *edi, rvec *xcoll, rvec *vec)
 +{
 +    int  i;
 +    real proj = 0.0;
 +
 +
 +    for (i = 0; i < edi->sav.nr; i++)
 +    {
 +        proj += edi->sav.sqrtm[i]*iprod(vec[i], xcoll[i]);
 +    }
 +
 +    return proj;
 +}
 +
 +
 +/* Specialized: projection is stored in vec->refproj
 + * -> used for radacc, radfix, radcon  and center of flooding potential
 + * subtracts average positions, projects vector x */
 +static void rad_project(t_edpar *edi, rvec *x, t_eigvec *vec)
 +{
 +    int  i;
 +    real rad = 0.0;
 +
 +    /* Subtract average positions */
 +    for (i = 0; i < edi->sav.nr; i++)
 +    {
 +        rvec_dec(x[i], edi->sav.x[i]);
 +    }
 +
 +    for (i = 0; i < vec->neig; i++)
 +    {
 +        vec->refproj[i] = projectx(edi, x, vec->vec[i]);
 +        rad            += pow((vec->refproj[i]-vec->xproj[i]), 2);
 +    }
 +    vec->radius = sqrt(rad);
 +
 +    /* Add average positions */
 +    for (i = 0; i < edi->sav.nr; i++)
 +    {
 +        rvec_inc(x[i], edi->sav.x[i]);
 +    }
 +}
 +
 +
 +/* Project vector x, subtract average positions prior to projection and add
 + * them afterwards to retain the unchanged vector. Store in xproj. Mass-weighting
 + * is applied. */
 +static void project_to_eigvectors(rvec       *x,    /* The positions to project to an eigenvector */
 +                                  t_eigvec   *vec,  /* The eigenvectors */
 +                                  t_edpar    *edi)
 +{
 +    int  i;
 +
 +
 +    if (!vec->neig)
 +    {
 +        return;
 +    }
 +
 +    /* Subtract average positions */
 +    for (i = 0; i < edi->sav.nr; i++)
 +    {
 +        rvec_dec(x[i], edi->sav.x[i]);
 +    }
 +
 +    for (i = 0; i < vec->neig; i++)
 +    {
 +        vec->xproj[i] = projectx(edi, x, vec->vec[i]);
 +    }
 +
 +    /* Add average positions */
 +    for (i = 0; i < edi->sav.nr; i++)
 +    {
 +        rvec_inc(x[i], edi->sav.x[i]);
 +    }
 +}
 +
 +
 +/* Project vector x onto all edi->vecs (mon, linfix,...) */
 +static void project(rvec      *x,     /* positions to project */
 +                    t_edpar   *edi)   /* edi data set */
 +{
 +    /* It is not more work to subtract the average position in every
-         /* Remember for each ED group whether we have to do essential dynamics
-          * constraints or possibly only flooding */
-         edi->bNeedDoEdsam = edi->vecs.mon.neig
-             || edi->vecs.linfix.neig
-             || edi->vecs.linacc.neig
-             || edi->vecs.radfix.neig
-             || edi->vecs.radacc.neig
-             || edi->vecs.radcon.neig;
++     * subroutine again, because these routines are rarely used simultaneously */
 +    project_to_eigvectors(x, &edi->vecs.mon, edi);
 +    project_to_eigvectors(x, &edi->vecs.linfix, edi);
 +    project_to_eigvectors(x, &edi->vecs.linacc, edi);
 +    project_to_eigvectors(x, &edi->vecs.radfix, edi);
 +    project_to_eigvectors(x, &edi->vecs.radacc, edi);
 +    project_to_eigvectors(x, &edi->vecs.radcon, edi);
 +}
 +
 +
 +static real calc_radius(t_eigvec *vec)
 +{
 +    int  i;
 +    real rad = 0.0;
 +
 +
 +    for (i = 0; i < vec->neig; i++)
 +    {
 +        rad += pow((vec->refproj[i]-vec->xproj[i]), 2);
 +    }
 +
 +    return rad = sqrt(rad);
 +}
 +
 +
 +/* Debug helper */
 +#ifdef DEBUGHELPERS
 +static void dump_xcoll(t_edpar *edi, struct t_do_edsam *buf, t_commrec *cr,
 +                       int step)
 +{
 +    int   i;
 +    FILE *fp;
 +    char  fn[STRLEN];
 +    rvec *xcoll;
 +    ivec *shifts, *eshifts;
 +
 +
 +    if (!MASTER(cr))
 +    {
 +        return;
 +    }
 +
 +    xcoll   = buf->xcoll;
 +    shifts  = buf->shifts_xcoll;
 +    eshifts = buf->extra_shifts_xcoll;
 +
 +    sprintf(fn, "xcolldump_step%d.txt", step);
 +    fp = fopen(fn, "w");
 +
 +    for (i = 0; i < edi->sav.nr; i++)
 +    {
 +        fprintf(fp, "%d %9.5f %9.5f %9.5f   %d %d %d   %d %d %d\n",
 +                edi->sav.anrs[i]+1,
 +                xcoll[i][XX], xcoll[i][YY], xcoll[i][ZZ],
 +                shifts[i][XX], shifts[i][YY], shifts[i][ZZ],
 +                eshifts[i][XX], eshifts[i][YY], eshifts[i][ZZ]);
 +    }
 +
 +    fclose(fp);
 +}
 +
 +
 +/* Debug helper */
 +static void dump_edi_positions(FILE *out, struct gmx_edx *s, const char name[])
 +{
 +    int i;
 +
 +
 +    fprintf(out, "#%s positions:\n%d\n", name, s->nr);
 +    if (s->nr == 0)
 +    {
 +        return;
 +    }
 +
 +    fprintf(out, "#index, x, y, z");
 +    if (s->sqrtm)
 +    {
 +        fprintf(out, ", sqrt(m)");
 +    }
 +    for (i = 0; i < s->nr; i++)
 +    {
 +        fprintf(out, "\n%6d  %11.6f %11.6f %11.6f", s->anrs[i], s->x[i][XX], s->x[i][YY], s->x[i][ZZ]);
 +        if (s->sqrtm)
 +        {
 +            fprintf(out, "%9.3f", s->sqrtm[i]);
 +        }
 +    }
 +    fprintf(out, "\n");
 +}
 +
 +
 +/* Debug helper */
 +static void dump_edi_eigenvecs(FILE *out, t_eigvec *ev,
 +                               const char name[], int length)
 +{
 +    int i, j;
 +
 +
 +    fprintf(out, "#%s eigenvectors:\n%d\n", name, ev->neig);
 +    /* Dump the data for every eigenvector: */
 +    for (i = 0; i < ev->neig; i++)
 +    {
 +        fprintf(out, "EV %4d\ncomponents %d\nstepsize %f\nxproj %f\nfproj %f\nrefproj %f\nradius %f\nComponents:\n",
 +                ev->ieig[i], length, ev->stpsz[i], ev->xproj[i], ev->fproj[i], ev->refproj[i], ev->radius);
 +        for (j = 0; j < length; j++)
 +        {
 +            fprintf(out, "%11.6f %11.6f %11.6f\n", ev->vec[i][j][XX], ev->vec[i][j][YY], ev->vec[i][j][ZZ]);
 +        }
 +    }
 +}
 +
 +
 +/* Debug helper */
 +static void dump_edi(t_edpar *edpars, t_commrec *cr, int nr_edi)
 +{
 +    FILE  *out;
 +    char   fn[STRLEN];
 +
 +
 +    sprintf(fn, "EDdump_node%d_edi%d", cr->nodeid, nr_edi);
 +    out = ffopen(fn, "w");
 +
 +    fprintf(out, "#NINI\n %d\n#FITMAS\n %d\n#ANALYSIS_MAS\n %d\n",
 +            edpars->nini, edpars->fitmas, edpars->pcamas);
 +    fprintf(out, "#OUTFRQ\n %d\n#MAXLEN\n %d\n#SLOPECRIT\n %f\n",
 +            edpars->outfrq, edpars->maxedsteps, edpars->slope);
 +    fprintf(out, "#PRESTEPS\n %d\n#DELTA_F0\n %f\n#TAU\n %f\n#EFL_NULL\n %f\n#ALPHA2\n %f\n",
 +            edpars->presteps, edpars->flood.deltaF0, edpars->flood.tau,
 +            edpars->flood.constEfl, edpars->flood.alpha2);
 +
 +    /* Dump reference, average, target, origin positions */
 +    dump_edi_positions(out, &edpars->sref, "REFERENCE");
 +    dump_edi_positions(out, &edpars->sav, "AVERAGE"  );
 +    dump_edi_positions(out, &edpars->star, "TARGET"   );
 +    dump_edi_positions(out, &edpars->sori, "ORIGIN"   );
 +
 +    /* Dump eigenvectors */
 +    dump_edi_eigenvecs(out, &edpars->vecs.mon, "MONITORED", edpars->sav.nr);
 +    dump_edi_eigenvecs(out, &edpars->vecs.linfix, "LINFIX", edpars->sav.nr);
 +    dump_edi_eigenvecs(out, &edpars->vecs.linacc, "LINACC", edpars->sav.nr);
 +    dump_edi_eigenvecs(out, &edpars->vecs.radfix, "RADFIX", edpars->sav.nr);
 +    dump_edi_eigenvecs(out, &edpars->vecs.radacc, "RADACC", edpars->sav.nr);
 +    dump_edi_eigenvecs(out, &edpars->vecs.radcon, "RADCON", edpars->sav.nr);
 +
 +    /* Dump flooding eigenvectors */
 +    dump_edi_eigenvecs(out, &edpars->flood.vecs, "FLOODING", edpars->sav.nr);
 +
 +    /* Dump ed local buffer */
 +    fprintf(out, "buf->do_edfit         =%p\n", (void*)edpars->buf->do_edfit  );
 +    fprintf(out, "buf->do_edsam         =%p\n", (void*)edpars->buf->do_edsam  );
 +    fprintf(out, "buf->do_radcon        =%p\n", (void*)edpars->buf->do_radcon );
 +
 +    ffclose(out);
 +}
 +
 +
 +/* Debug helper */
 +static void dump_rotmat(FILE* out, matrix rotmat)
 +{
 +    fprintf(out, "ROTMAT: %12.8f %12.8f %12.8f\n", rotmat[XX][XX], rotmat[XX][YY], rotmat[XX][ZZ]);
 +    fprintf(out, "ROTMAT: %12.8f %12.8f %12.8f\n", rotmat[YY][XX], rotmat[YY][YY], rotmat[YY][ZZ]);
 +    fprintf(out, "ROTMAT: %12.8f %12.8f %12.8f\n", rotmat[ZZ][XX], rotmat[ZZ][YY], rotmat[ZZ][ZZ]);
 +}
 +
 +
 +/* Debug helper */
 +static void dump_rvec(FILE *out, int dim, rvec *x)
 +{
 +    int i;
 +
 +
 +    for (i = 0; i < dim; i++)
 +    {
 +        fprintf(out, "%4d   %f %f %f\n", i, x[i][XX], x[i][YY], x[i][ZZ]);
 +    }
 +}
 +
 +
 +/* Debug helper */
 +static void dump_mat(FILE* out, int dim, double** mat)
 +{
 +    int i, j;
 +
 +
 +    fprintf(out, "MATRIX:\n");
 +    for (i = 0; i < dim; i++)
 +    {
 +        for (j = 0; j < dim; j++)
 +        {
 +            fprintf(out, "%f ", mat[i][j]);
 +        }
 +        fprintf(out, "\n");
 +    }
 +}
 +#endif
 +
 +
 +struct t_do_edfit {
 +    double **omega;
 +    double **om;
 +};
 +
 +static void do_edfit(int natoms, rvec *xp, rvec *x, matrix R, t_edpar *edi)
 +{
 +    /* this is a copy of do_fit with some modifications */
 +    int                c, r, n, j, i, irot;
 +    double             d[6], xnr, xpc;
 +    matrix             vh, vk, u;
 +    int                index;
 +    real               max_d;
 +
 +    struct t_do_edfit *loc;
 +    gmx_bool           bFirst;
 +
 +    if (edi->buf->do_edfit != NULL)
 +    {
 +        bFirst = FALSE;
 +    }
 +    else
 +    {
 +        bFirst = TRUE;
 +        snew(edi->buf->do_edfit, 1);
 +    }
 +    loc = edi->buf->do_edfit;
 +
 +    if (bFirst)
 +    {
 +        snew(loc->omega, 2*DIM);
 +        snew(loc->om, 2*DIM);
 +        for (i = 0; i < 2*DIM; i++)
 +        {
 +            snew(loc->omega[i], 2*DIM);
 +            snew(loc->om[i], 2*DIM);
 +        }
 +    }
 +
 +    for (i = 0; (i < 6); i++)
 +    {
 +        d[i] = 0;
 +        for (j = 0; (j < 6); j++)
 +        {
 +            loc->omega[i][j] = 0;
 +            loc->om[i][j]    = 0;
 +        }
 +    }
 +
 +    /* calculate the matrix U */
 +    clear_mat(u);
 +    for (n = 0; (n < natoms); n++)
 +    {
 +        for (c = 0; (c < DIM); c++)
 +        {
 +            xpc = xp[n][c];
 +            for (r = 0; (r < DIM); r++)
 +            {
 +                xnr      = x[n][r];
 +                u[c][r] += xnr*xpc;
 +            }
 +        }
 +    }
 +
 +    /* construct loc->omega */
 +    /* loc->omega is symmetric -> loc->omega==loc->omega' */
 +    for (r = 0; (r < 6); r++)
 +    {
 +        for (c = 0; (c <= r); c++)
 +        {
 +            if ((r >= 3) && (c < 3))
 +            {
 +                loc->omega[r][c] = u[r-3][c];
 +                loc->omega[c][r] = u[r-3][c];
 +            }
 +            else
 +            {
 +                loc->omega[r][c] = 0;
 +                loc->omega[c][r] = 0;
 +            }
 +        }
 +    }
 +
 +    /* determine h and k */
 +#ifdef DEBUG
 +    {
 +        int i;
 +        dump_mat(stderr, 2*DIM, loc->omega);
 +        for (i = 0; i < 6; i++)
 +        {
 +            fprintf(stderr, "d[%d] = %f\n", i, d[i]);
 +        }
 +    }
 +#endif
 +    jacobi(loc->omega, 6, d, loc->om, &irot);
 +
 +    if (irot == 0)
 +    {
 +        fprintf(stderr, "IROT=0\n");
 +    }
 +
 +    index = 0; /* For the compiler only */
 +
 +    for (j = 0; (j < 3); j++)
 +    {
 +        max_d = -1000;
 +        for (i = 0; (i < 6); i++)
 +        {
 +            if (d[i] > max_d)
 +            {
 +                max_d = d[i];
 +                index = i;
 +            }
 +        }
 +        d[index] = -10000;
 +        for (i = 0; (i < 3); i++)
 +        {
 +            vh[j][i] = M_SQRT2*loc->om[i][index];
 +            vk[j][i] = M_SQRT2*loc->om[i+DIM][index];
 +        }
 +    }
 +
 +    /* determine R */
 +    for (c = 0; (c < 3); c++)
 +    {
 +        for (r = 0; (r < 3); r++)
 +        {
 +            R[c][r] = vk[0][r]*vh[0][c]+
 +                vk[1][r]*vh[1][c]+
 +                vk[2][r]*vh[2][c];
 +        }
 +    }
 +    if (det(R) < 0)
 +    {
 +        for (c = 0; (c < 3); c++)
 +        {
 +            for (r = 0; (r < 3); r++)
 +            {
 +                R[c][r] = vk[0][r]*vh[0][c]+
 +                    vk[1][r]*vh[1][c]-
 +                    vk[2][r]*vh[2][c];
 +            }
 +        }
 +    }
 +}
 +
 +
 +static void rmfit(int nat, rvec *xcoll, rvec transvec, matrix rotmat)
 +{
 +    rvec   vec;
 +    matrix tmat;
 +
 +
 +    /* Remove rotation.
 +     * The inverse rotation is described by the transposed rotation matrix */
 +    transpose(rotmat, tmat);
 +    rotate_x(xcoll, nat, tmat);
 +
 +    /* Remove translation */
 +    vec[XX] = -transvec[XX];
 +    vec[YY] = -transvec[YY];
 +    vec[ZZ] = -transvec[ZZ];
 +    translate_x(xcoll, nat, vec);
 +}
 +
 +
 +/**********************************************************************************
 + ******************** FLOODING ****************************************************
 + **********************************************************************************
 +
 +   The flooding ability was added later to edsam. Many of the edsam functionality could be reused for that purpose.
 +   The flooding covariance matrix, i.e. the selected eigenvectors and their corresponding eigenvalues are
 +   read as 7th Component Group. The eigenvalues are coded into the stepsize parameter (as used by -linfix or -linacc).
 +
 +   do_md clls right in the beginning the function init_edsam, which reads the edi file, saves all the necessary information in
 +   the edi structure and calls init_flood, to initialise some extra fields in the edi->flood structure.
 +
 +   since the flooding acts on forces do_flood is called from the function force() (force.c), while the other
 +   edsam functionality is hooked into md via the update() (update.c) function acting as constraint on positions.
 +
 +   do_flood makes a copy of the positions,
 +   fits them, projects them computes flooding_energy, and flooding forces. The forces are computed in the
 +   space of the eigenvectors and are then blown up to the full cartesian space and rotated back to remove the
 +   fit. Then do_flood adds these forces to the forcefield-forces
 +   (given as parameter) and updates the adaptive flooding parameters Efl and deltaF.
 +
 +   To center the flooding potential at a different location one can use the -ori option in make_edi. The ori
 +   structure is projected to the system of eigenvectors and then this position in the subspace is used as
 +   center of the flooding potential.   If the option is not used, the center will be zero in the subspace,
 +   i.e. the average structure as given in the make_edi file.
 +
 +   To use the flooding potential as restraint, make_edi has the option -restrain, which leads to inverted
 +   signs of alpha2 and Efl, such that the sign in the exponential of Vfl is not inverted but the sign of
 +   Vfl is inverted. Vfl = Efl * exp (- .../Efl/alpha2*x^2...) With tau>0 the negative Efl will grow slowly
 +   so that the restraint is switched off slowly. When Efl==0 and inverted flooding is ON is reached no
 +   further adaption is applied, Efl will stay constant at zero.
 +
 +   To use restraints with harmonic potentials switch -restrain and -harmonic. Then the eigenvalues are
 +   used as spring constants for the harmonic potential.
 +   Note that eq3 in the flooding paper (J. Comp. Chem. 2006, 27, 1693-1702) defines the parameter lambda \
 +   as the inverse of the spring constant, whereas the implementation uses lambda as the spring constant.
 +
 +   To use more than one flooding matrix just concatenate several .edi files (cat flood1.edi flood2.edi > flood_all.edi)
 +   the routine read_edi_file reads all of theses flooding files.
 +   The structure t_edi is now organized as a list of t_edis and the function do_flood cycles through the list
 +   calling the do_single_flood() routine for every single entry. Since every state variables have been kept in one
 +   edi there is no interdependence whatsoever. The forces are added together.
 +
 +   To write energies into the .edr file, call the function
 +        get_flood_enx_names(char**, int *nnames) to get the Header (Vfl1 Vfl2... Vfln)
 +   and call
 +        get_flood_energies(real Vfl[],int nnames);
 +
 +   TODO:
 +   - one could program the whole thing such that Efl, Vfl and deltaF is written to the .edr file. -- i dont know how to do that, yet.
 +
 +   Maybe one should give a range of atoms for which to remove motion, so that motion is removed with
 +   two edsam files from two peptide chains
 + */
 +
 +static void write_edo_flood(t_edpar *edi, FILE *fp, real rmsd)
 +{
 +    int i;
 +
 +
 +    /* Output how well we fit to the reference structure */
 +    fprintf(fp, EDcol_ffmt, rmsd);
 +
 +    for (i = 0; i < edi->flood.vecs.neig; i++)
 +    {
 +        fprintf(fp, EDcol_efmt, edi->flood.vecs.xproj[i]);
 +
 +        /* Check whether the reference projection changes with time (this can happen
 +         * in case flooding is used as harmonic restraint). If so, output the
 +         * current reference projection */
 +        if (edi->flood.bHarmonic && edi->flood.vecs.refprojslope[i] != 0.0)
 +        {
 +            fprintf(fp, EDcol_efmt, edi->flood.vecs.refproj[i]);
 +        }
 +
 +        /* Output Efl if we are doing adaptive flooding */
 +        if (0 != edi->flood.tau)
 +        {
 +            fprintf(fp, EDcol_efmt, edi->flood.Efl);
 +        }
 +        fprintf(fp, EDcol_efmt, edi->flood.Vfl);
 +
 +        /* Output deltaF if we are doing adaptive flooding */
 +        if (0 != edi->flood.tau)
 +        {
 +            fprintf(fp, EDcol_efmt, edi->flood.deltaF);
 +        }
 +        fprintf(fp, EDcol_efmt, edi->flood.vecs.fproj[i]);
 +    }
 +}
 +
 +
 +/* From flood.xproj compute the Vfl(x) at this point */
 +static real flood_energy(t_edpar *edi, gmx_int64_t step)
 +{
 +    /* compute flooding energy Vfl
 +       Vfl = Efl * exp( - \frac {kT} {2Efl alpha^2} * sum_i { \lambda_i c_i^2 } )
 +       \lambda_i is the reciprocal eigenvalue 1/\sigma_i
 +         it is already computed by make_edi and stored in stpsz[i]
 +       bHarmonic:
 +       Vfl = - Efl * 1/2(sum _i {\frac 1{\lambda_i} c_i^2})
 +     */
 +    real sum;
 +    real Vfl;
 +    int  i;
 +
 +
 +    /* Each time this routine is called (i.e. each time step), we add a small
 +     * value to the reference projection. This way a harmonic restraint towards
 +     * a moving reference is realized. If no value for the additive constant
 +     * is provided in the edi file, the reference will not change. */
 +    if (edi->flood.bHarmonic)
 +    {
 +        for (i = 0; i < edi->flood.vecs.neig; i++)
 +        {
 +            edi->flood.vecs.refproj[i] = edi->flood.vecs.refproj0[i] + step * edi->flood.vecs.refprojslope[i];
 +        }
 +    }
 +
 +    sum = 0.0;
 +    /* Compute sum which will be the exponent of the exponential */
 +    for (i = 0; i < edi->flood.vecs.neig; i++)
 +    {
 +        /* stpsz stores the reciprocal eigenvalue 1/sigma_i */
 +        sum += edi->flood.vecs.stpsz[i]*(edi->flood.vecs.xproj[i]-edi->flood.vecs.refproj[i])*(edi->flood.vecs.xproj[i]-edi->flood.vecs.refproj[i]);
 +    }
 +
 +    /* Compute the Gauss function*/
 +    if (edi->flood.bHarmonic)
 +    {
 +        Vfl = -0.5*edi->flood.Efl*sum;  /* minus sign because Efl is negative, if restrain is on. */
 +    }
 +    else
 +    {
 +        Vfl = edi->flood.Efl != 0 ? edi->flood.Efl*exp(-edi->flood.kT/2/edi->flood.Efl/edi->flood.alpha2*sum) : 0;
 +    }
 +
 +    return Vfl;
 +}
 +
 +
 +/* From the position and from Vfl compute forces in subspace -> store in edi->vec.flood.fproj */
 +static void flood_forces(t_edpar *edi)
 +{
 +    /* compute the forces in the subspace of the flooding eigenvectors
 +     * by the formula F_i= V_{fl}(c) * ( \frac {kT} {E_{fl}} \lambda_i c_i */
 +
 +    int  i;
 +    real energy = edi->flood.Vfl;
 +
 +
 +    if (edi->flood.bHarmonic)
 +    {
 +        for (i = 0; i < edi->flood.vecs.neig; i++)
 +        {
 +            edi->flood.vecs.fproj[i] = edi->flood.Efl* edi->flood.vecs.stpsz[i]*(edi->flood.vecs.xproj[i]-edi->flood.vecs.refproj[i]);
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; i < edi->flood.vecs.neig; i++)
 +        {
 +            /* if Efl is zero the forces are zero if not use the formula */
 +            edi->flood.vecs.fproj[i] = edi->flood.Efl != 0 ? edi->flood.kT/edi->flood.Efl/edi->flood.alpha2*energy*edi->flood.vecs.stpsz[i]*(edi->flood.vecs.xproj[i]-edi->flood.vecs.refproj[i]) : 0;
 +        }
 +    }
 +}
 +
 +
 +/* Raise forces from subspace into cartesian space */
 +static void flood_blowup(t_edpar *edi, rvec *forces_cart)
 +{
 +    /* this function lifts the forces from the subspace to the cartesian space
 +       all the values not contained in the subspace are assumed to be zero and then
 +       a coordinate transformation from eigenvector to cartesian vectors is performed
 +       The nonexistent values don't have to be set to zero explicitly, they would occur
 +       as zero valued summands, hence we just stop to compute this part of the sum.
 +
 +       for every atom we add all the contributions to this atom from all the different eigenvectors.
 +
 +       NOTE: one could add directly to the forcefield forces, would mean we wouldn't have to clear the
 +       field forces_cart prior the computation, but we compute the forces separately
 +       to have them accessible for diagnostics
 +     */
 +    int   j, eig;
 +    rvec  dum;
 +    real *forces_sub;
 +
 +
 +    forces_sub = edi->flood.vecs.fproj;
 +
 +
 +    /* Calculate the cartesian forces for the local atoms */
 +
 +    /* Clear forces first */
 +    for (j = 0; j < edi->sav.nr_loc; j++)
 +    {
 +        clear_rvec(forces_cart[j]);
 +    }
 +
 +    /* Now compute atomwise */
 +    for (j = 0; j < edi->sav.nr_loc; j++)
 +    {
 +        /* Compute forces_cart[edi->sav.anrs[j]] */
 +        for (eig = 0; eig < edi->flood.vecs.neig; eig++)
 +        {
 +            /* Force vector is force * eigenvector (compute only atom j) */
 +            svmul(forces_sub[eig], edi->flood.vecs.vec[eig][edi->sav.c_ind[j]], dum);
 +            /* Add this vector to the cartesian forces */
 +            rvec_inc(forces_cart[j], dum);
 +        }
 +    }
 +}
 +
 +
 +/* Update the values of Efl, deltaF depending on tau and Vfl */
 +static void update_adaption(t_edpar *edi)
 +{
 +    /* this function updates the parameter Efl and deltaF according to the rules given in
 +     * 'predicting unimolecular chemical reactions: chemical flooding' M Mueller et al,
 +     * J. chem Phys. */
 +
 +    if ((edi->flood.tau < 0 ? -edi->flood.tau : edi->flood.tau ) > 0.00000001)
 +    {
 +        edi->flood.Efl = edi->flood.Efl+edi->flood.dt/edi->flood.tau*(edi->flood.deltaF0-edi->flood.deltaF);
 +        /* check if restrain (inverted flooding) -> don't let EFL become positive */
 +        if (edi->flood.alpha2 < 0 && edi->flood.Efl > -0.00000001)
 +        {
 +            edi->flood.Efl = 0;
 +        }
 +
 +        edi->flood.deltaF = (1-edi->flood.dt/edi->flood.tau)*edi->flood.deltaF+edi->flood.dt/edi->flood.tau*edi->flood.Vfl;
 +    }
 +}
 +
 +
 +static void do_single_flood(
 +        FILE           *edo,
 +        rvec            x[],
 +        rvec            force[],
 +        t_edpar        *edi,
 +        gmx_int64_t     step,
 +        matrix          box,
 +        t_commrec      *cr,
 +        gmx_bool        bNS) /* Are we in a neighbor searching step? */
 +{
 +    int                i;
 +    matrix             rotmat;   /* rotation matrix */
 +    matrix             tmat;     /* inverse rotation */
 +    rvec               transvec; /* translation vector */
 +    real               rmsdev;
 +    struct t_do_edsam *buf;
 +
 +
 +    buf = edi->buf->do_edsam;
 +
 +
 +    /* Broadcast the positions of the AVERAGE structure such that they are known on
 +     * every processor. Each node contributes its local positions x and stores them in
 +     * the collective ED array buf->xcoll */
 +    communicate_group_positions(cr, buf->xcoll, buf->shifts_xcoll, buf->extra_shifts_xcoll, bNS, x,
 +                                edi->sav.nr, edi->sav.nr_loc, edi->sav.anrs_loc, edi->sav.c_ind, edi->sav.x_old, box);
 +
 +    /* Only assembly REFERENCE positions if their indices differ from the average ones */
 +    if (!edi->bRefEqAv)
 +    {
 +        communicate_group_positions(cr, buf->xc_ref, buf->shifts_xc_ref, buf->extra_shifts_xc_ref, bNS, x,
 +                                    edi->sref.nr, edi->sref.nr_loc, edi->sref.anrs_loc, edi->sref.c_ind, edi->sref.x_old, box);
 +    }
 +
 +    /* If bUpdateShifts was TRUE, the shifts have just been updated in get_positions.
 +     * We do not need to update the shifts until the next NS step */
 +    buf->bUpdateShifts = FALSE;
 +
 +    /* Now all nodes have all of the ED/flooding positions in edi->sav->xcoll,
 +     * as well as the indices in edi->sav.anrs */
 +
 +    /* Fit the reference indices to the reference structure */
 +    if (edi->bRefEqAv)
 +    {
 +        fit_to_reference(buf->xcoll, transvec, rotmat, edi);
 +    }
 +    else
 +    {
 +        fit_to_reference(buf->xc_ref, transvec, rotmat, edi);
 +    }
 +
 +    /* Now apply the translation and rotation to the ED structure */
 +    translate_and_rotate(buf->xcoll, edi->sav.nr, transvec, rotmat);
 +
 +    /* Project fitted structure onto supbspace -> store in edi->flood.vecs.xproj */
 +    project_to_eigvectors(buf->xcoll, &edi->flood.vecs, edi);
 +
 +    if (FALSE == edi->flood.bConstForce)
 +    {
 +        /* Compute Vfl(x) from flood.xproj */
 +        edi->flood.Vfl = flood_energy(edi, step);
 +
 +        update_adaption(edi);
 +
 +        /* Compute the flooding forces */
 +        flood_forces(edi);
 +    }
 +
 +    /* Translate them into cartesian positions */
 +    flood_blowup(edi, edi->flood.forces_cartesian);
 +
 +    /* Rotate forces back so that they correspond to the given structure and not to the fitted one */
 +    /* Each node rotates back its local forces */
 +    transpose(rotmat, tmat);
 +    rotate_x(edi->flood.forces_cartesian, edi->sav.nr_loc, tmat);
 +
 +    /* Finally add forces to the main force variable */
 +    for (i = 0; i < edi->sav.nr_loc; i++)
 +    {
 +        rvec_inc(force[edi->sav.anrs_loc[i]], edi->flood.forces_cartesian[i]);
 +    }
 +
 +    /* Output is written by the master process */
 +    if (do_per_step(step, edi->outfrq) && MASTER(cr))
 +    {
 +        /* Output how well we fit to the reference */
 +        if (edi->bRefEqAv)
 +        {
 +            /* Indices of reference and average structures are identical,
 +             * thus we can calculate the rmsd to SREF using xcoll */
 +            rmsdev = rmsd_from_structure(buf->xcoll, &edi->sref);
 +        }
 +        else
 +        {
 +            /* We have to translate & rotate the reference atoms first */
 +            translate_and_rotate(buf->xc_ref, edi->sref.nr, transvec, rotmat);
 +            rmsdev = rmsd_from_structure(buf->xc_ref, &edi->sref);
 +        }
 +
 +        write_edo_flood(edi, edo, rmsdev);
 +    }
 +}
 +
 +
 +/* Main flooding routine, called from do_force */
 +extern void do_flood(
 +        t_commrec       *cr,      /* Communication record */
 +        t_inputrec      *ir,      /* Input record */
 +        rvec             x[],     /* Positions on the local processor */
 +        rvec             force[], /* forcefield forces, to these the flooding forces are added */
 +        gmx_edsam_t      ed,      /* ed data structure contains all ED and flooding groups */
 +        matrix           box,     /* the box */
 +        gmx_int64_t      step,    /* The relative time step since ir->init_step is already subtracted */
 +        gmx_bool         bNS)     /* Are we in a neighbor searching step? */
 +{
 +    t_edpar *edi;
 +
 +
 +    edi = ed->edpar;
 +
 +    /* Write time to edo, when required. Output the time anyhow since we need
 +     * it in the output file for ED constraints. */
 +    if (MASTER(cr) && do_per_step(step, edi->outfrq))
 +    {
 +        fprintf(ed->edo, "\n%12f", ir->init_t + step*ir->delta_t);
 +    }
 +
 +    if (ed->eEDtype != eEDflood)
 +    {
 +        return;
 +    }
 +
 +    while (edi)
 +    {
 +        /* Call flooding for one matrix */
 +        if (edi->flood.vecs.neig)
 +        {
 +            do_single_flood(ed->edo, x, force, edi, step, box, cr, bNS);
 +        }
 +        edi = edi->next_edi;
 +    }
 +}
 +
 +
 +/* Called by init_edi, configure some flooding related variables and structures,
 + * print headers to output files */
 +static void init_flood(t_edpar *edi, gmx_edsam_t ed, real dt)
 +{
 +    int i;
 +
 +
 +    edi->flood.Efl = edi->flood.constEfl;
 +    edi->flood.Vfl = 0;
 +    edi->flood.dt  = dt;
 +
 +    if (edi->flood.vecs.neig)
 +    {
 +        /* If in any of the ED groups we find a flooding vector, flooding is turned on */
 +        ed->eEDtype = eEDflood;
 +
 +        fprintf(stderr, "ED: Flooding %d eigenvector%s.\n", edi->flood.vecs.neig, edi->flood.vecs.neig > 1 ? "s" : "");
 +
 +        if (edi->flood.bConstForce)
 +        {
 +            /* We have used stpsz as a vehicle to carry the fproj values for constant
 +             * force flooding. Now we copy that to flood.vecs.fproj. Note that
 +             * in const force flooding, fproj is never changed. */
 +            for (i = 0; i < edi->flood.vecs.neig; i++)
 +            {
 +                edi->flood.vecs.fproj[i] = edi->flood.vecs.stpsz[i];
 +
 +                fprintf(stderr, "ED: applying on eigenvector %d a constant force of %g\n",
 +                        edi->flood.vecs.ieig[i], edi->flood.vecs.fproj[i]);
 +            }
 +        }
 +    }
 +}
 +
 +
 +#ifdef DEBUGHELPERS
 +/*********** Energy book keeping ******/
 +static void get_flood_enx_names(t_edpar *edi, char** names, int *nnames)  /* get header of energies */
 +{
 +    t_edpar *actual;
 +    int      count;
 +    char     buf[STRLEN];
 +    actual = edi;
 +    count  = 1;
 +    while (actual)
 +    {
 +        srenew(names, count);
 +        sprintf(buf, "Vfl_%d", count);
 +        names[count-1] = strdup(buf);
 +        actual         = actual->next_edi;
 +        count++;
 +    }
 +    *nnames = count-1;
 +}
 +
 +
 +static void get_flood_energies(t_edpar *edi, real Vfl[], int nnames)
 +{
 +    /*fl has to be big enough to capture nnames-many entries*/
 +    t_edpar *actual;
 +    int      count;
 +
 +
 +    actual = edi;
 +    count  = 1;
 +    while (actual)
 +    {
 +        Vfl[count-1] = actual->flood.Vfl;
 +        actual       = actual->next_edi;
 +        count++;
 +    }
 +    if (nnames != count-1)
 +    {
 +        gmx_fatal(FARGS, "Number of energies is not consistent with t_edi structure");
 +    }
 +}
 +/************* END of FLOODING IMPLEMENTATION ****************************/
 +#endif
 +
 +
 +gmx_edsam_t ed_open(int natoms, edsamstate_t *EDstate, int nfile, const t_filenm fnm[], unsigned long Flags, const output_env_t oenv, t_commrec *cr)
 +{
 +    gmx_edsam_t ed;
 +    int         nED;
 +
 +
 +    /* Allocate space for the ED data structure */
 +    snew(ed, 1);
 +
 +    /* We want to perform ED (this switch might later be upgraded to eEDflood) */
 +    ed->eEDtype = eEDedsam;
 +
 +    if (MASTER(cr))
 +    {
 +        fprintf(stderr, "ED sampling will be performed!\n");
 +        snew(ed->edpar, 1);
 +
 +        /* Read the edi input file: */
 +        nED = read_edi_file(ftp2fn(efEDI, nfile, fnm), ed->edpar, natoms);
 +
 +        /* Make sure the checkpoint was produced in a run using this .edi file */
 +        if (EDstate->bFromCpt)
 +        {
 +            crosscheck_edi_file_vs_checkpoint(ed, EDstate);
 +        }
 +        else
 +        {
 +            EDstate->nED = nED;
 +        }
 +        init_edsamstate(ed, EDstate);
 +
 +        /* The master opens the ED output file */
 +        if (Flags & MD_APPENDFILES)
 +        {
 +            ed->edo = gmx_fio_fopen(opt2fn("-eo", nfile, fnm), "a+");
 +        }
 +        else
 +        {
 +            ed->edo = xvgropen(opt2fn("-eo", nfile, fnm),
 +                               "Essential dynamics / flooding output",
 +                               "Time (ps)",
 +                               "RMSDs (nm), projections on EVs (nm), ...", oenv);
 +
 +            /* Make a descriptive legend */
 +            write_edo_legend(ed, EDstate->nED, oenv);
 +        }
 +    }
 +    return ed;
 +}
 +
 +
 +/* Broadcasts the structure data */
 +static void bc_ed_positions(t_commrec *cr, struct gmx_edx *s, int stype)
 +{
 +    snew_bc(cr, s->anrs, s->nr   );    /* Index numbers     */
 +    snew_bc(cr, s->x, s->nr   );       /* Positions         */
 +    nblock_bc(cr, s->nr, s->anrs );
 +    nblock_bc(cr, s->nr, s->x    );
 +
 +    /* For the average & reference structures we need an array for the collective indices,
 +     * and we need to broadcast the masses as well */
 +    if (stype == eedAV || stype == eedREF)
 +    {
 +        /* We need these additional variables in the parallel case: */
 +        snew(s->c_ind, s->nr   );       /* Collective indices */
 +        /* Local atom indices get assigned in dd_make_local_group_indices.
 +         * There, also memory is allocated */
 +        s->nalloc_loc = 0;              /* allocation size of s->anrs_loc */
 +        snew_bc(cr, s->x_old, s->nr);   /* To be able to always make the ED molecule whole, ...        */
 +        nblock_bc(cr, s->nr, s->x_old); /* ... keep track of shift changes with the help of old coords */
 +    }
 +
 +    /* broadcast masses for the reference structure (for mass-weighted fitting) */
 +    if (stype == eedREF)
 +    {
 +        snew_bc(cr, s->m, s->nr);
 +        nblock_bc(cr, s->nr, s->m);
 +    }
 +
 +    /* For the average structure we might need the masses for mass-weighting */
 +    if (stype == eedAV)
 +    {
 +        snew_bc(cr, s->sqrtm, s->nr);
 +        nblock_bc(cr, s->nr, s->sqrtm);
 +        snew_bc(cr, s->m, s->nr);
 +        nblock_bc(cr, s->nr, s->m);
 +    }
 +}
 +
 +
 +/* Broadcasts the eigenvector data */
 +static void bc_ed_vecs(t_commrec *cr, t_eigvec *ev, int length, gmx_bool bHarmonic)
 +{
 +    int i;
 +
 +    snew_bc(cr, ev->ieig, ev->neig);     /* index numbers of eigenvector  */
 +    snew_bc(cr, ev->stpsz, ev->neig);    /* stepsizes per eigenvector     */
 +    snew_bc(cr, ev->xproj, ev->neig);    /* instantaneous x projection    */
 +    snew_bc(cr, ev->fproj, ev->neig);    /* instantaneous f projection    */
 +    snew_bc(cr, ev->refproj, ev->neig);  /* starting or target projection */
 +
 +    nblock_bc(cr, ev->neig, ev->ieig   );
 +    nblock_bc(cr, ev->neig, ev->stpsz  );
 +    nblock_bc(cr, ev->neig, ev->xproj  );
 +    nblock_bc(cr, ev->neig, ev->fproj  );
 +    nblock_bc(cr, ev->neig, ev->refproj);
 +
 +    snew_bc(cr, ev->vec, ev->neig);      /* Eigenvector components        */
 +    for (i = 0; i < ev->neig; i++)
 +    {
 +        snew_bc(cr, ev->vec[i], length);
 +        nblock_bc(cr, length, ev->vec[i]);
 +    }
 +
 +    /* For harmonic restraints the reference projections can change with time */
 +    if (bHarmonic)
 +    {
 +        snew_bc(cr, ev->refproj0, ev->neig);
 +        snew_bc(cr, ev->refprojslope, ev->neig);
 +        nblock_bc(cr, ev->neig, ev->refproj0    );
 +        nblock_bc(cr, ev->neig, ev->refprojslope);
 +    }
 +}
 +
 +
 +/* Broadcasts the ED / flooding data to other nodes
 + * and allocates memory where needed */
 +static void broadcast_ed_data(t_commrec *cr, gmx_edsam_t ed, int numedis)
 +{
 +    int      nr;
 +    t_edpar *edi;
 +
 +
 +    /* Master lets the other nodes know if its ED only or also flooding */
 +    gmx_bcast(sizeof(ed->eEDtype), &(ed->eEDtype), cr);
 +
 +    snew_bc(cr, ed->edpar, 1);
 +    /* Now transfer the ED data set(s) */
 +    edi = ed->edpar;
 +    for (nr = 0; nr < numedis; nr++)
 +    {
 +        /* Broadcast a single ED data set */
 +        block_bc(cr, *edi);
 +
 +        /* Broadcast positions */
 +        bc_ed_positions(cr, &(edi->sref), eedREF); /* reference positions (don't broadcast masses)    */
 +        bc_ed_positions(cr, &(edi->sav ), eedAV ); /* average positions (do broadcast masses as well) */
 +        bc_ed_positions(cr, &(edi->star), eedTAR); /* target positions                                */
 +        bc_ed_positions(cr, &(edi->sori), eedORI); /* origin positions                                */
 +
 +        /* Broadcast eigenvectors */
 +        bc_ed_vecs(cr, &edi->vecs.mon, edi->sav.nr, FALSE);
 +        bc_ed_vecs(cr, &edi->vecs.linfix, edi->sav.nr, FALSE);
 +        bc_ed_vecs(cr, &edi->vecs.linacc, edi->sav.nr, FALSE);
 +        bc_ed_vecs(cr, &edi->vecs.radfix, edi->sav.nr, FALSE);
 +        bc_ed_vecs(cr, &edi->vecs.radacc, edi->sav.nr, FALSE);
 +        bc_ed_vecs(cr, &edi->vecs.radcon, edi->sav.nr, FALSE);
 +        /* Broadcast flooding eigenvectors and, if needed, values for the moving reference */
 +        bc_ed_vecs(cr, &edi->flood.vecs,  edi->sav.nr, edi->flood.bHarmonic);
 +
 +        /* Set the pointer to the next ED group */
 +        if (edi->next_edi)
 +        {
 +            snew_bc(cr, edi->next_edi, 1);
 +            edi = edi->next_edi;
 +        }
 +    }
 +}
 +
 +
 +/* init-routine called for every *.edi-cycle, initialises t_edpar structure */
 +static void init_edi(gmx_mtop_t *mtop, t_edpar *edi)
 +{
 +    int                   i;
 +    real                  totalmass = 0.0;
 +    rvec                  com;
 +    gmx_mtop_atomlookup_t alook = NULL;
 +    t_atom               *atom;
 +
 +    /* NOTE Init_edi is executed on the master process only
 +     * The initialized data sets are then transmitted to the
 +     * other nodes in broadcast_ed_data */
 +
 +    alook = gmx_mtop_atomlookup_init(mtop);
 +
 +    /* evaluate masses (reference structure) */
 +    snew(edi->sref.m, edi->sref.nr);
 +    for (i = 0; i < edi->sref.nr; i++)
 +    {
 +        if (edi->fitmas)
 +        {
 +            gmx_mtop_atomnr_to_atom(alook, edi->sref.anrs[i], &atom);
 +            edi->sref.m[i] = atom->m;
 +        }
 +        else
 +        {
 +            edi->sref.m[i] = 1.0;
 +        }
 +
 +        /* Check that every m > 0. Bad things will happen otherwise. */
 +        if (edi->sref.m[i] <= 0.0)
 +        {
 +            gmx_fatal(FARGS, "Reference structure atom %d (sam.edi index %d) has a mass of %g.\n"
 +                      "For a mass-weighted fit, all reference structure atoms need to have a mass >0.\n"
 +                      "Either make the covariance analysis non-mass-weighted, or exclude massless\n"
 +                      "atoms from the reference structure by creating a proper index group.\n",
 +                      i, edi->sref.anrs[i]+1, edi->sref.m[i]);
 +        }
 +
 +        totalmass += edi->sref.m[i];
 +    }
 +    edi->sref.mtot = totalmass;
 +
 +    /* Masses m and sqrt(m) for the average structure. Note that m
 +     * is needed if forces have to be evaluated in do_edsam */
 +    snew(edi->sav.sqrtm, edi->sav.nr );
 +    snew(edi->sav.m, edi->sav.nr );
 +    for (i = 0; i < edi->sav.nr; i++)
 +    {
 +        gmx_mtop_atomnr_to_atom(alook, edi->sav.anrs[i], &atom);
 +        edi->sav.m[i] = atom->m;
 +        if (edi->pcamas)
 +        {
 +            edi->sav.sqrtm[i] = sqrt(atom->m);
 +        }
 +        else
 +        {
 +            edi->sav.sqrtm[i] = 1.0;
 +        }
 +
 +        /* Check that every m > 0. Bad things will happen otherwise. */
 +        if (edi->sav.sqrtm[i] <= 0.0)
 +        {
 +            gmx_fatal(FARGS, "Average structure atom %d (sam.edi index %d) has a mass of %g.\n"
 +                      "For ED with mass-weighting, all average structure atoms need to have a mass >0.\n"
 +                      "Either make the covariance analysis non-mass-weighted, or exclude massless\n"
 +                      "atoms from the average structure by creating a proper index group.\n",
 +                      i, edi->sav.anrs[i]+1, atom->m);
 +        }
 +    }
 +
 +    gmx_mtop_atomlookup_destroy(alook);
 +
 +    /* put reference structure in origin */
 +    get_center(edi->sref.x, edi->sref.m, edi->sref.nr, com);
 +    com[XX] = -com[XX];
 +    com[YY] = -com[YY];
 +    com[ZZ] = -com[ZZ];
 +    translate_x(edi->sref.x, edi->sref.nr, com);
 +
 +    /* Init ED buffer */
 +    snew(edi->buf, 1);
 +}
 +
 +
 +static void check(const char *line, const char *label)
 +{
 +    if (!strstr(line, label))
 +    {
 +        gmx_fatal(FARGS, "Could not find input parameter %s at expected position in edsam input-file (.edi)\nline read instead is %s", label, line);
 +    }
 +}
 +
 +
 +static int read_checked_edint(FILE *file, const char *label)
 +{
 +    char line[STRLEN+1];
 +    int  idum;
 +
 +
 +    fgets2 (line, STRLEN, file);
 +    check(line, label);
 +    fgets2 (line, STRLEN, file);
 +    sscanf (line, "%d", &idum);
 +    return idum;
 +}
 +
 +
 +static int read_edint(FILE *file, gmx_bool *bEOF)
 +{
 +    char  line[STRLEN+1];
 +    int   idum;
 +    char *eof;
 +
 +
 +    eof = fgets2 (line, STRLEN, file);
 +    if (eof == NULL)
 +    {
 +        *bEOF = TRUE;
 +        return -1;
 +    }
 +    eof = fgets2 (line, STRLEN, file);
 +    if (eof == NULL)
 +    {
 +        *bEOF = TRUE;
 +        return -1;
 +    }
 +    sscanf (line, "%d", &idum);
 +    *bEOF = FALSE;
 +    return idum;
 +}
 +
 +
 +static real read_checked_edreal(FILE *file, const char *label)
 +{
 +    char   line[STRLEN+1];
 +    double rdum;
 +
 +
 +    fgets2 (line, STRLEN, file);
 +    check(line, label);
 +    fgets2 (line, STRLEN, file);
 +    sscanf (line, "%lf", &rdum);
 +    return (real) rdum; /* always read as double and convert to single */
 +}
 +
 +
 +static void read_edx(FILE *file, int number, int *anrs, rvec *x)
 +{
 +    int    i, j;
 +    char   line[STRLEN+1];
 +    double d[3];
 +
 +
 +    for (i = 0; i < number; i++)
 +    {
 +        fgets2 (line, STRLEN, file);
 +        sscanf (line, "%d%lf%lf%lf", &anrs[i], &d[0], &d[1], &d[2]);
 +        anrs[i]--; /* we are reading FORTRAN indices */
 +        for (j = 0; j < 3; j++)
 +        {
 +            x[i][j] = d[j]; /* always read as double and convert to single */
 +        }
 +    }
 +}
 +
 +
 +static void scan_edvec(FILE *in, int nr, rvec *vec)
 +{
 +    char   line[STRLEN+1];
 +    int    i;
 +    double x, y, z;
 +
 +
 +    for (i = 0; (i < nr); i++)
 +    {
 +        fgets2 (line, STRLEN, in);
 +        sscanf (line, "%le%le%le", &x, &y, &z);
 +        vec[i][XX] = x;
 +        vec[i][YY] = y;
 +        vec[i][ZZ] = z;
 +    }
 +}
 +
 +
 +static void read_edvec(FILE *in, int nr, t_eigvec *tvec, gmx_bool bReadRefproj, gmx_bool *bHaveReference)
 +{
 +    int    i, idum, nscan;
 +    double rdum, refproj_dum = 0.0, refprojslope_dum = 0.0;
 +    char   line[STRLEN+1];
 +
 +
 +    tvec->neig = read_checked_edint(in, "NUMBER OF EIGENVECTORS");
 +    if (tvec->neig > 0)
 +    {
 +        snew(tvec->ieig, tvec->neig);
 +        snew(tvec->stpsz, tvec->neig);
 +        snew(tvec->vec, tvec->neig);
 +        snew(tvec->xproj, tvec->neig);
 +        snew(tvec->fproj, tvec->neig);
 +        snew(tvec->refproj, tvec->neig);
 +        if (bReadRefproj)
 +        {
 +            snew(tvec->refproj0, tvec->neig);
 +            snew(tvec->refprojslope, tvec->neig);
 +        }
 +
 +        for (i = 0; (i < tvec->neig); i++)
 +        {
 +            fgets2 (line, STRLEN, in);
 +            if (bReadRefproj) /* ONLY when using flooding as harmonic restraint */
 +            {
 +                nscan = sscanf(line, "%d%lf%lf%lf", &idum, &rdum, &refproj_dum, &refprojslope_dum);
 +                /* Zero out values which were not scanned */
 +                switch (nscan)
 +                {
 +                    case 4:
 +                        /* Every 4 values read, including reference position */
 +                        *bHaveReference = TRUE;
 +                        break;
 +                    case 3:
 +                        /* A reference position is provided */
 +                        *bHaveReference = TRUE;
 +                        /* No value for slope, set to 0 */
 +                        refprojslope_dum = 0.0;
 +                        break;
 +                    case 2:
 +                        /* No values for reference projection and slope, set to 0 */
 +                        refproj_dum      = 0.0;
 +                        refprojslope_dum = 0.0;
 +                        break;
 +                    default:
 +                        gmx_fatal(FARGS, "Expected 2 - 4 (not %d) values for flooding vec: <nr> <spring const> <refproj> <refproj-slope>\n", nscan);
 +                        break;
 +                }
 +                tvec->refproj[i]      = refproj_dum;
 +                tvec->refproj0[i]     = refproj_dum;
 +                tvec->refprojslope[i] = refprojslope_dum;
 +            }
 +            else /* Normal flooding */
 +            {
 +                nscan = sscanf(line, "%d%lf", &idum, &rdum);
 +                if (nscan != 2)
 +                {
 +                    gmx_fatal(FARGS, "Expected 2 values for flooding vec: <nr> <stpsz>\n");
 +                }
 +            }
 +            tvec->ieig[i]  = idum;
 +            tvec->stpsz[i] = rdum;
 +        } /* end of loop over eigenvectors */
 +
 +        for (i = 0; (i < tvec->neig); i++)
 +        {
 +            snew(tvec->vec[i], nr);
 +            scan_edvec(in, nr, tvec->vec[i]);
 +        }
 +    }
 +}
 +
 +
 +/* calls read_edvec for the vector groups, only for flooding there is an extra call */
 +static void read_edvecs(FILE *in, int nr, t_edvecs *vecs)
 +{
 +    gmx_bool bHaveReference = FALSE;
 +
 +
 +    read_edvec(in, nr, &vecs->mon, FALSE, &bHaveReference);
 +    read_edvec(in, nr, &vecs->linfix, FALSE, &bHaveReference);
 +    read_edvec(in, nr, &vecs->linacc, FALSE, &bHaveReference);
 +    read_edvec(in, nr, &vecs->radfix, FALSE, &bHaveReference);
 +    read_edvec(in, nr, &vecs->radacc, FALSE, &bHaveReference);
 +    read_edvec(in, nr, &vecs->radcon, FALSE, &bHaveReference);
 +}
 +
 +
 +/* Check if the same atom indices are used for reference and average positions */
 +static gmx_bool check_if_same(struct gmx_edx sref, struct gmx_edx sav)
 +{
 +    int i;
 +
 +
 +    /* If the number of atoms differs between the two structures,
 +     * they cannot be identical */
 +    if (sref.nr != sav.nr)
 +    {
 +        return FALSE;
 +    }
 +
 +    /* Now that we know that both stuctures have the same number of atoms,
 +     * check if also the indices are identical */
 +    for (i = 0; i < sav.nr; i++)
 +    {
 +        if (sref.anrs[i] != sav.anrs[i])
 +        {
 +            return FALSE;
 +        }
 +    }
 +    fprintf(stderr, "ED: Note: Reference and average structure are composed of the same atom indices.\n");
 +
 +    return TRUE;
 +}
 +
 +
 +static int read_edi(FILE* in, t_edpar *edi, int nr_mdatoms, const char *fn)
 +{
 +    int       readmagic;
 +    const int magic = 670;
 +    gmx_bool  bEOF;
 +
 +    /* Was a specific reference point for the flooding/umbrella potential provided in the edi file? */
 +    gmx_bool bHaveReference = FALSE;
 +
 +
 +    /* the edi file is not free format, so expect problems if the input is corrupt. */
 +
 +    /* check the magic number */
 +    readmagic = read_edint(in, &bEOF);
 +    /* Check whether we have reached the end of the input file */
 +    if (bEOF)
 +    {
 +        return 0;
 +    }
 +
 +    if (readmagic != magic)
 +    {
 +        if (readmagic == 666 || readmagic == 667 || readmagic == 668)
 +        {
 +            gmx_fatal(FARGS, "Wrong magic number: Use newest version of make_edi to produce edi file");
 +        }
 +        else if (readmagic != 669)
 +        {
 +            gmx_fatal(FARGS, "Wrong magic number %d in %s", readmagic, fn);
 +        }
 +    }
 +
 +    /* check the number of atoms */
 +    edi->nini = read_edint(in, &bEOF);
 +    if (edi->nini != nr_mdatoms)
 +    {
 +        gmx_fatal(FARGS, "Nr of atoms in %s (%d) does not match nr of md atoms (%d)", fn, edi->nini, nr_mdatoms);
 +    }
 +
 +    /* Done checking. For the rest we blindly trust the input */
 +    edi->fitmas          = read_checked_edint(in, "FITMAS");
 +    edi->pcamas          = read_checked_edint(in, "ANALYSIS_MAS");
 +    edi->outfrq          = read_checked_edint(in, "OUTFRQ");
 +    edi->maxedsteps      = read_checked_edint(in, "MAXLEN");
 +    edi->slope           = read_checked_edreal(in, "SLOPECRIT");
 +
 +    edi->presteps        = read_checked_edint(in, "PRESTEPS");
 +    edi->flood.deltaF0   = read_checked_edreal(in, "DELTA_F0");
 +    edi->flood.deltaF    = read_checked_edreal(in, "INIT_DELTA_F");
 +    edi->flood.tau       = read_checked_edreal(in, "TAU");
 +    edi->flood.constEfl  = read_checked_edreal(in, "EFL_NULL");
 +    edi->flood.alpha2    = read_checked_edreal(in, "ALPHA2");
 +    edi->flood.kT        = read_checked_edreal(in, "KT");
 +    edi->flood.bHarmonic = read_checked_edint(in, "HARMONIC");
 +    if (readmagic > 669)
 +    {
 +        edi->flood.bConstForce = read_checked_edint(in, "CONST_FORCE_FLOODING");
 +    }
 +    else
 +    {
 +        edi->flood.bConstForce = FALSE;
 +    }
 +    edi->sref.nr         = read_checked_edint(in, "NREF");
 +
 +    /* allocate space for reference positions and read them */
 +    snew(edi->sref.anrs, edi->sref.nr);
 +    snew(edi->sref.x, edi->sref.nr);
 +    snew(edi->sref.x_old, edi->sref.nr);
 +    edi->sref.sqrtm    = NULL;
 +    read_edx(in, edi->sref.nr, edi->sref.anrs, edi->sref.x);
 +
 +    /* average positions. they define which atoms will be used for ED sampling */
 +    edi->sav.nr = read_checked_edint(in, "NAV");
 +    snew(edi->sav.anrs, edi->sav.nr);
 +    snew(edi->sav.x, edi->sav.nr);
 +    snew(edi->sav.x_old, edi->sav.nr);
 +    read_edx(in, edi->sav.nr, edi->sav.anrs, edi->sav.x);
 +
 +    /* Check if the same atom indices are used for reference and average positions */
 +    edi->bRefEqAv = check_if_same(edi->sref, edi->sav);
 +
 +    /* eigenvectors */
 +    read_edvecs(in, edi->sav.nr, &edi->vecs);
 +    read_edvec(in, edi->sav.nr, &edi->flood.vecs, edi->flood.bHarmonic, &bHaveReference);
 +
 +    /* target positions */
 +    edi->star.nr = read_edint(in, &bEOF);
 +    if (edi->star.nr > 0)
 +    {
 +        snew(edi->star.anrs, edi->star.nr);
 +        snew(edi->star.x, edi->star.nr);
 +        edi->star.sqrtm    = NULL;
 +        read_edx(in, edi->star.nr, edi->star.anrs, edi->star.x);
 +    }
 +
 +    /* positions defining origin of expansion circle */
 +    edi->sori.nr = read_edint(in, &bEOF);
 +    if (edi->sori.nr > 0)
 +    {
 +        if (bHaveReference)
 +        {
 +            /* Both an -ori structure and a at least one manual reference point have been
 +             * specified. That's ambiguous and probably not intentional. */
 +            gmx_fatal(FARGS, "ED: An origin structure has been provided and a at least one (moving) reference\n"
 +                      "    point was manually specified in the edi file. That is ambiguous. Aborting.\n");
 +        }
 +        snew(edi->sori.anrs, edi->sori.nr);
 +        snew(edi->sori.x, edi->sori.nr);
 +        edi->sori.sqrtm    = NULL;
 +        read_edx(in, edi->sori.nr, edi->sori.anrs, edi->sori.x);
 +    }
 +
 +    /* all done */
 +    return 1;
 +}
 +
 +
 +
 +/* Read in the edi input file. Note that it may contain several ED data sets which were
 + * achieved by concatenating multiple edi files. The standard case would be a single ED
 + * data set, though. */
 +static int read_edi_file(const char *fn, t_edpar *edi, int nr_mdatoms)
 +{
 +    FILE    *in;
 +    t_edpar *curr_edi, *last_edi;
 +    t_edpar *edi_read;
 +    int      edi_nr = 0;
 +
 +
 +    /* This routine is executed on the master only */
 +
 +    /* Open the .edi parameter input file */
 +    in = gmx_fio_fopen(fn, "r");
 +    fprintf(stderr, "ED: Reading edi file %s\n", fn);
 +
 +    /* Now read a sequence of ED input parameter sets from the edi file */
 +    curr_edi = edi;
 +    last_edi = edi;
 +    while (read_edi(in, curr_edi, nr_mdatoms, fn) )
 +    {
 +        edi_nr++;
 +
 +        /* Since we arrived within this while loop we know that there is still another data set to be read in */
 +        /* We need to allocate space for the data: */
 +        snew(edi_read, 1);
 +        /* Point the 'next_edi' entry to the next edi: */
 +        curr_edi->next_edi = edi_read;
 +        /* Keep the curr_edi pointer for the case that the next group is empty: */
 +        last_edi = curr_edi;
 +        /* Let's prepare to read in the next edi data set: */
 +        curr_edi = edi_read;
 +    }
 +    if (edi_nr == 0)
 +    {
 +        gmx_fatal(FARGS, "No complete ED data set found in edi file %s.", fn);
 +    }
 +
 +    /* Terminate the edi group list with a NULL pointer: */
 +    last_edi->next_edi = NULL;
 +
 +    fprintf(stderr, "ED: Found %d ED group%s.\n", edi_nr, edi_nr > 1 ? "s" : "");
 +
 +    /* Close the .edi file again */
 +    gmx_fio_fclose(in);
 +
 +    return edi_nr;
 +}
 +
 +
 +struct t_fit_to_ref {
 +    rvec *xcopy;       /* Working copy of the positions in fit_to_reference */
 +};
 +
 +/* Fit the current positions to the reference positions
 + * Do not actually do the fit, just return rotation and translation.
 + * Note that the COM of the reference structure was already put into
 + * the origin by init_edi. */
 +static void fit_to_reference(rvec      *xcoll,    /* The positions to be fitted */
 +                             rvec       transvec, /* The translation vector */
 +                             matrix     rotmat,   /* The rotation matrix */
 +                             t_edpar   *edi)      /* Just needed for do_edfit */
 +{
 +    rvec                 com;                     /* center of mass */
 +    int                  i;
 +    struct t_fit_to_ref *loc;
 +
 +
 +    /* Allocate memory the first time this routine is called for each edi group */
 +    if (NULL == edi->buf->fit_to_ref)
 +    {
 +        snew(edi->buf->fit_to_ref, 1);
 +        snew(edi->buf->fit_to_ref->xcopy, edi->sref.nr);
 +    }
 +    loc = edi->buf->fit_to_ref;
 +
 +    /* We do not touch the original positions but work on a copy. */
 +    for (i = 0; i < edi->sref.nr; i++)
 +    {
 +        copy_rvec(xcoll[i], loc->xcopy[i]);
 +    }
 +
 +    /* Calculate the center of mass */
 +    get_center(loc->xcopy, edi->sref.m, edi->sref.nr, com);
 +
 +    transvec[XX] = -com[XX];
 +    transvec[YY] = -com[YY];
 +    transvec[ZZ] = -com[ZZ];
 +
 +    /* Subtract the center of mass from the copy */
 +    translate_x(loc->xcopy, edi->sref.nr, transvec);
 +
 +    /* Determine the rotation matrix */
 +    do_edfit(edi->sref.nr, edi->sref.x, loc->xcopy, rotmat, edi);
 +}
 +
 +
 +static void translate_and_rotate(rvec  *x,        /* The positions to be translated and rotated */
 +                                 int    nat,      /* How many positions are there? */
 +                                 rvec   transvec, /* The translation vector */
 +                                 matrix rotmat)   /* The rotation matrix */
 +{
 +    /* Translation */
 +    translate_x(x, nat, transvec);
 +
 +    /* Rotation */
 +    rotate_x(x, nat, rotmat);
 +}
 +
 +
 +/* Gets the rms deviation of the positions to the structure s */
 +/* fit_to_structure has to be called before calling this routine! */
 +static real rmsd_from_structure(rvec           *x,  /* The positions under consideration */
 +                                struct gmx_edx *s)  /* The structure from which the rmsd shall be computed */
 +{
 +    real  rmsd = 0.0;
 +    int   i;
 +
 +
 +    for (i = 0; i < s->nr; i++)
 +    {
 +        rmsd += distance2(s->x[i], x[i]);
 +    }
 +
 +    rmsd /= (real) s->nr;
 +    rmsd  = sqrt(rmsd);
 +
 +    return rmsd;
 +}
 +
 +
 +void dd_make_local_ed_indices(gmx_domdec_t *dd, struct gmx_edsam *ed)
 +{
 +    t_edpar *edi;
 +
 +
 +    if (ed->eEDtype != eEDnone)
 +    {
 +        /* Loop over ED groups */
 +        edi = ed->edpar;
 +        while (edi)
 +        {
 +            /* Local atoms of the reference structure (for fitting), need only be assembled
 +             * if their indices differ from the average ones */
 +            if (!edi->bRefEqAv)
 +            {
 +                dd_make_local_group_indices(dd->ga2la, edi->sref.nr, edi->sref.anrs,
 +                                            &edi->sref.nr_loc, &edi->sref.anrs_loc, &edi->sref.nalloc_loc, edi->sref.c_ind);
 +            }
 +
 +            /* Local atoms of the average structure (on these ED will be performed) */
 +            dd_make_local_group_indices(dd->ga2la, edi->sav.nr, edi->sav.anrs,
 +                                        &edi->sav.nr_loc, &edi->sav.anrs_loc, &edi->sav.nalloc_loc, edi->sav.c_ind);
 +
 +            /* Indicate that the ED shift vectors for this structure need to be updated
 +             * at the next call to communicate_group_positions, since obviously we are in a NS step */
 +            edi->buf->do_edsam->bUpdateShifts = TRUE;
 +
 +            /* Set the pointer to the next ED group (if any) */
 +            edi = edi->next_edi;
 +        }
 +    }
 +}
 +
 +
 +static inline void ed_unshift_single_coord(matrix box, const rvec x, const ivec is, rvec xu)
 +{
 +    int tx, ty, tz;
 +
 +
 +    tx = is[XX];
 +    ty = is[YY];
 +    tz = is[ZZ];
 +
 +    if (TRICLINIC(box))
 +    {
 +        xu[XX] = x[XX]-tx*box[XX][XX]-ty*box[YY][XX]-tz*box[ZZ][XX];
 +        xu[YY] = x[YY]-ty*box[YY][YY]-tz*box[ZZ][YY];
 +        xu[ZZ] = x[ZZ]-tz*box[ZZ][ZZ];
 +    }
 +    else
 +    {
 +        xu[XX] = x[XX]-tx*box[XX][XX];
 +        xu[YY] = x[YY]-ty*box[YY][YY];
 +        xu[ZZ] = x[ZZ]-tz*box[ZZ][ZZ];
 +    }
 +}
 +
 +
 +static void do_linfix(rvec *xcoll, t_edpar *edi, gmx_int64_t step)
 +{
 +    int  i, j;
 +    real proj, add;
 +    rvec vec_dum;
 +
 +
 +    /* loop over linfix vectors */
 +    for (i = 0; i < edi->vecs.linfix.neig; i++)
 +    {
 +        /* calculate the projection */
 +        proj = projectx(edi, xcoll, edi->vecs.linfix.vec[i]);
 +
 +        /* calculate the correction */
 +        add = edi->vecs.linfix.refproj[i] + step*edi->vecs.linfix.stpsz[i] - proj;
 +
 +        /* apply the correction */
 +        add /= edi->sav.sqrtm[i];
 +        for (j = 0; j < edi->sav.nr; j++)
 +        {
 +            svmul(add, edi->vecs.linfix.vec[i][j], vec_dum);
 +            rvec_inc(xcoll[j], vec_dum);
 +        }
 +    }
 +}
 +
 +
 +static void do_linacc(rvec *xcoll, t_edpar *edi)
 +{
 +    int  i, j;
 +    real proj, add;
 +    rvec vec_dum;
 +
 +
 +    /* loop over linacc vectors */
 +    for (i = 0; i < edi->vecs.linacc.neig; i++)
 +    {
 +        /* calculate the projection */
 +        proj = projectx(edi, xcoll, edi->vecs.linacc.vec[i]);
 +
 +        /* calculate the correction */
 +        add = 0.0;
 +        if (edi->vecs.linacc.stpsz[i] > 0.0)
 +        {
 +            if ((proj-edi->vecs.linacc.refproj[i]) < 0.0)
 +            {
 +                add = edi->vecs.linacc.refproj[i] - proj;
 +            }
 +        }
 +        if (edi->vecs.linacc.stpsz[i] < 0.0)
 +        {
 +            if ((proj-edi->vecs.linacc.refproj[i]) > 0.0)
 +            {
 +                add = edi->vecs.linacc.refproj[i] - proj;
 +            }
 +        }
 +
 +        /* apply the correction */
 +        add /= edi->sav.sqrtm[i];
 +        for (j = 0; j < edi->sav.nr; j++)
 +        {
 +            svmul(add, edi->vecs.linacc.vec[i][j], vec_dum);
 +            rvec_inc(xcoll[j], vec_dum);
 +        }
 +
 +        /* new positions will act as reference */
 +        edi->vecs.linacc.refproj[i] = proj + add;
 +    }
 +}
 +
 +
 +static void do_radfix(rvec *xcoll, t_edpar *edi)
 +{
 +    int   i, j;
 +    real *proj, rad = 0.0, ratio;
 +    rvec  vec_dum;
 +
 +
 +    if (edi->vecs.radfix.neig == 0)
 +    {
 +        return;
 +    }
 +
 +    snew(proj, edi->vecs.radfix.neig);
 +
 +    /* loop over radfix vectors */
 +    for (i = 0; i < edi->vecs.radfix.neig; i++)
 +    {
 +        /* calculate the projections, radius */
 +        proj[i] = projectx(edi, xcoll, edi->vecs.radfix.vec[i]);
 +        rad    += pow(proj[i] - edi->vecs.radfix.refproj[i], 2);
 +    }
 +
 +    rad                      = sqrt(rad);
 +    ratio                    = (edi->vecs.radfix.stpsz[0]+edi->vecs.radfix.radius)/rad - 1.0;
 +    edi->vecs.radfix.radius += edi->vecs.radfix.stpsz[0];
 +
 +    /* loop over radfix vectors */
 +    for (i = 0; i < edi->vecs.radfix.neig; i++)
 +    {
 +        proj[i] -= edi->vecs.radfix.refproj[i];
 +
 +        /* apply the correction */
 +        proj[i] /= edi->sav.sqrtm[i];
 +        proj[i] *= ratio;
 +        for (j = 0; j < edi->sav.nr; j++)
 +        {
 +            svmul(proj[i], edi->vecs.radfix.vec[i][j], vec_dum);
 +            rvec_inc(xcoll[j], vec_dum);
 +        }
 +    }
 +
 +    sfree(proj);
 +}
 +
 +
 +static void do_radacc(rvec *xcoll, t_edpar *edi)
 +{
 +    int   i, j;
 +    real *proj, rad = 0.0, ratio = 0.0;
 +    rvec  vec_dum;
 +
 +
 +    if (edi->vecs.radacc.neig == 0)
 +    {
 +        return;
 +    }
 +
 +    snew(proj, edi->vecs.radacc.neig);
 +
 +    /* loop over radacc vectors */
 +    for (i = 0; i < edi->vecs.radacc.neig; i++)
 +    {
 +        /* calculate the projections, radius */
 +        proj[i] = projectx(edi, xcoll, edi->vecs.radacc.vec[i]);
 +        rad    += pow(proj[i] - edi->vecs.radacc.refproj[i], 2);
 +    }
 +    rad = sqrt(rad);
 +
 +    /* only correct when radius decreased */
 +    if (rad < edi->vecs.radacc.radius)
 +    {
 +        ratio = edi->vecs.radacc.radius/rad - 1.0;
 +        rad   = edi->vecs.radacc.radius;
 +    }
 +    else
 +    {
 +        edi->vecs.radacc.radius = rad;
 +    }
 +
 +    /* loop over radacc vectors */
 +    for (i = 0; i < edi->vecs.radacc.neig; i++)
 +    {
 +        proj[i] -= edi->vecs.radacc.refproj[i];
 +
 +        /* apply the correction */
 +        proj[i] /= edi->sav.sqrtm[i];
 +        proj[i] *= ratio;
 +        for (j = 0; j < edi->sav.nr; j++)
 +        {
 +            svmul(proj[i], edi->vecs.radacc.vec[i][j], vec_dum);
 +            rvec_inc(xcoll[j], vec_dum);
 +        }
 +    }
 +    sfree(proj);
 +}
 +
 +
 +struct t_do_radcon {
 +    real *proj;
 +};
 +
 +static void do_radcon(rvec *xcoll, t_edpar *edi)
 +{
 +    int                 i, j;
 +    real                rad = 0.0, ratio = 0.0;
 +    struct t_do_radcon *loc;
 +    gmx_bool            bFirst;
 +    rvec                vec_dum;
 +
 +
 +    if (edi->buf->do_radcon != NULL)
 +    {
 +        bFirst = FALSE;
 +        loc    = edi->buf->do_radcon;
 +    }
 +    else
 +    {
 +        bFirst = TRUE;
 +        snew(edi->buf->do_radcon, 1);
 +    }
 +    loc = edi->buf->do_radcon;
 +
 +    if (edi->vecs.radcon.neig == 0)
 +    {
 +        return;
 +    }
 +
 +    if (bFirst)
 +    {
 +        snew(loc->proj, edi->vecs.radcon.neig);
 +    }
 +
 +    /* loop over radcon vectors */
 +    for (i = 0; i < edi->vecs.radcon.neig; i++)
 +    {
 +        /* calculate the projections, radius */
 +        loc->proj[i] = projectx(edi, xcoll, edi->vecs.radcon.vec[i]);
 +        rad         += pow(loc->proj[i] - edi->vecs.radcon.refproj[i], 2);
 +    }
 +    rad = sqrt(rad);
 +    /* only correct when radius increased */
 +    if (rad > edi->vecs.radcon.radius)
 +    {
 +        ratio = edi->vecs.radcon.radius/rad - 1.0;
 +
 +        /* loop over radcon vectors */
 +        for (i = 0; i < edi->vecs.radcon.neig; i++)
 +        {
 +            /* apply the correction */
 +            loc->proj[i] -= edi->vecs.radcon.refproj[i];
 +            loc->proj[i] /= edi->sav.sqrtm[i];
 +            loc->proj[i] *= ratio;
 +
 +            for (j = 0; j < edi->sav.nr; j++)
 +            {
 +                svmul(loc->proj[i], edi->vecs.radcon.vec[i][j], vec_dum);
 +                rvec_inc(xcoll[j], vec_dum);
 +            }
 +        }
 +    }
 +    else
 +    {
 +        edi->vecs.radcon.radius = rad;
 +    }
 +
 +    if (rad != edi->vecs.radcon.radius)
 +    {
 +        rad = 0.0;
 +        for (i = 0; i < edi->vecs.radcon.neig; i++)
 +        {
 +            /* calculate the projections, radius */
 +            loc->proj[i] = projectx(edi, xcoll, edi->vecs.radcon.vec[i]);
 +            rad         += pow(loc->proj[i] - edi->vecs.radcon.refproj[i], 2);
 +        }
 +        rad = sqrt(rad);
 +    }
 +}
 +
 +
 +static void ed_apply_constraints(rvec *xcoll, t_edpar *edi, gmx_int64_t step)
 +{
 +    int i;
 +
 +
 +    /* subtract the average positions */
 +    for (i = 0; i < edi->sav.nr; i++)
 +    {
 +        rvec_dec(xcoll[i], edi->sav.x[i]);
 +    }
 +
 +    /* apply the constraints */
 +    if (step >= 0)
 +    {
 +        do_linfix(xcoll, edi, step);
 +    }
 +    do_linacc(xcoll, edi);
 +    if (step >= 0)
 +    {
 +        do_radfix(xcoll, edi);
 +    }
 +    do_radacc(xcoll, edi);
 +    do_radcon(xcoll, edi);
 +
 +    /* add back the average positions */
 +    for (i = 0; i < edi->sav.nr; i++)
 +    {
 +        rvec_inc(xcoll[i], edi->sav.x[i]);
 +    }
 +}
 +
 +
 +/* Write out the projections onto the eigenvectors. The order of output
 + * corresponds to ed_output_legend() */
 +static void write_edo(t_edpar *edi, FILE *fp, real rmsd)
 +{
 +    int i;
 +
 +
 +    /* Output how well we fit to the reference structure */
 +    fprintf(fp, EDcol_ffmt, rmsd);
 +
 +    for (i = 0; i < edi->vecs.mon.neig; i++)
 +    {
 +        fprintf(fp, EDcol_efmt, edi->vecs.mon.xproj[i]);
 +    }
 +
 +    for (i = 0; i < edi->vecs.linfix.neig; i++)
 +    {
 +        fprintf(fp, EDcol_efmt, edi->vecs.linfix.xproj[i]);
 +    }
 +
 +    for (i = 0; i < edi->vecs.linacc.neig; i++)
 +    {
 +        fprintf(fp, EDcol_efmt, edi->vecs.linacc.xproj[i]);
 +    }
 +
 +    for (i = 0; i < edi->vecs.radfix.neig; i++)
 +    {
 +        fprintf(fp, EDcol_efmt, edi->vecs.radfix.xproj[i]);
 +    }
 +    if (edi->vecs.radfix.neig)
 +    {
 +        fprintf(fp, EDcol_ffmt, calc_radius(&edi->vecs.radfix)); /* fixed increment radius */
 +    }
 +
 +    for (i = 0; i < edi->vecs.radacc.neig; i++)
 +    {
 +        fprintf(fp, EDcol_efmt, edi->vecs.radacc.xproj[i]);
 +    }
 +    if (edi->vecs.radacc.neig)
 +    {
 +        fprintf(fp, EDcol_ffmt, calc_radius(&edi->vecs.radacc)); /* acceptance radius */
 +    }
 +
 +    for (i = 0; i < edi->vecs.radcon.neig; i++)
 +    {
 +        fprintf(fp, EDcol_efmt, edi->vecs.radcon.xproj[i]);
 +    }
 +    if (edi->vecs.radcon.neig)
 +    {
 +        fprintf(fp, EDcol_ffmt, calc_radius(&edi->vecs.radcon)); /* contracting radius */
 +    }
 +}
 +
 +/* Returns if any constraints are switched on */
 +static int ed_constraints(gmx_bool edtype, t_edpar *edi)
 +{
 +    if (edtype == eEDedsam || edtype == eEDflood)
 +    {
 +        return (edi->vecs.linfix.neig || edi->vecs.linacc.neig ||
 +                edi->vecs.radfix.neig || edi->vecs.radacc.neig ||
 +                edi->vecs.radcon.neig);
 +    }
 +    return 0;
 +}
 +
 +
 +/* Copies reference projection 'refproj' to fixed 'refproj0' variable for flooding/
 + * umbrella sampling simulations. */
 +static void copyEvecReference(t_eigvec* floodvecs)
 +{
 +    int i;
 +
 +
 +    if (NULL == floodvecs->refproj0)
 +    {
 +        snew(floodvecs->refproj0, floodvecs->neig);
 +    }
 +
 +    for (i = 0; i < floodvecs->neig; i++)
 +    {
 +        floodvecs->refproj0[i] = floodvecs->refproj[i];
 +    }
 +}
 +
 +
 +/* Call on MASTER only. Check whether the essential dynamics / flooding
 + * groups of the checkpoint file are consistent with the provided .edi file. */
 +static void crosscheck_edi_file_vs_checkpoint(gmx_edsam_t ed, edsamstate_t *EDstate)
 +{
 +    t_edpar *edi = NULL;    /* points to a single edi data set */
 +    int      edinum;
 +
 +
 +    if (NULL == EDstate->nref || NULL == EDstate->nav)
 +    {
 +        gmx_fatal(FARGS, "Essential dynamics and flooding can only be switched on (or off) at the\n"
 +                  "start of a new simulation. If a simulation runs with/without ED constraints,\n"
 +                  "it must also continue with/without ED constraints when checkpointing.\n"
 +                  "To switch on (or off) ED constraints, please prepare a new .tpr to start\n"
 +                  "from without a checkpoint.\n");
 +    }
 +
 +    edi    = ed->edpar;
 +    edinum = 0;
 +    while (edi != NULL)
 +    {
 +        /* Check number of atoms in the reference and average structures */
 +        if (EDstate->nref[edinum] != edi->sref.nr)
 +        {
 +            gmx_fatal(FARGS, "The number of reference structure atoms in ED group %c is\n"
 +                      "not the same in .cpt (NREF=%d) and .edi (NREF=%d) files!\n",
 +                      get_EDgroupChar(edinum+1, 0), EDstate->nref[edinum], edi->sref.nr);
 +        }
 +        if (EDstate->nav[edinum] != edi->sav.nr)
 +        {
 +            gmx_fatal(FARGS, "The number of average structure atoms in ED group %c is\n"
 +                      "not the same in .cpt (NREF=%d) and .edi (NREF=%d) files!\n",
 +                      get_EDgroupChar(edinum+1, 0), EDstate->nav[edinum], edi->sav.nr);
 +        }
 +        edi = edi->next_edi;
 +        edinum++;
 +    }
 +
 +    if (edinum != EDstate->nED)
 +    {
 +        gmx_fatal(FARGS, "The number of essential dynamics / flooding groups is not consistent.\n"
 +                  "There are %d ED groups in the .cpt file, but %d in the .edi file!\n"
 +                  "Are you sure this is the correct .edi file?\n", EDstate->nED, edinum);
 +    }
 +}
 +
 +
 +/* The edsamstate struct stores the information we need to make the ED group
 + * whole again after restarts from a checkpoint file. Here we do the following:
 + * a) If we did not start from .cpt, we prepare the struct for proper .cpt writing,
 + * b) if we did start from .cpt, we copy over the last whole structures from .cpt,
 + * c) in any case, for subsequent checkpoint writing, we set the pointers in
 + * edsamstate to the x_old arrays, which contain the correct PBC representation of
 + * all ED structures at the last time step. */
 +static void init_edsamstate(gmx_edsam_t ed, edsamstate_t *EDstate)
 +{
 +    int      i, nr_edi;
 +    t_edpar *edi;
 +
 +
 +    snew(EDstate->old_sref_p, EDstate->nED);
 +    snew(EDstate->old_sav_p, EDstate->nED);
 +
 +    /* If we did not read in a .cpt file, these arrays are not yet allocated */
 +    if (!EDstate->bFromCpt)
 +    {
 +        snew(EDstate->nref, EDstate->nED);
 +        snew(EDstate->nav, EDstate->nED);
 +    }
 +
 +    /* Loop over all ED/flooding data sets (usually only one, though) */
 +    edi = ed->edpar;
 +    for (nr_edi = 1; nr_edi <= EDstate->nED; nr_edi++)
 +    {
 +        /* We always need the last reference and average positions such that
 +         * in the next time step we can make the ED group whole again
 +         * if the atoms do not have the correct PBC representation */
 +        if (EDstate->bFromCpt)
 +        {
 +            /* Copy the last whole positions of reference and average group from .cpt */
 +            for (i = 0; i < edi->sref.nr; i++)
 +            {
 +                copy_rvec(EDstate->old_sref[nr_edi-1][i], edi->sref.x_old[i]);
 +            }
 +            for (i = 0; i < edi->sav.nr; i++)
 +            {
 +                copy_rvec(EDstate->old_sav [nr_edi-1][i], edi->sav.x_old [i]);
 +            }
 +        }
 +        else
 +        {
 +            EDstate->nref[nr_edi-1] = edi->sref.nr;
 +            EDstate->nav [nr_edi-1] = edi->sav.nr;
 +        }
 +
 +        /* For subsequent checkpoint writing, set the edsamstate pointers to the edi arrays: */
 +        EDstate->old_sref_p[nr_edi-1] = edi->sref.x_old;
 +        EDstate->old_sav_p [nr_edi-1] = edi->sav.x_old;
 +
 +        edi = edi->next_edi;
 +    }
 +}
 +
 +
 +/* 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, EDcol_sfmt, buf);
 +    add_to_string(str, buf_aligned);
 +}
 +
 +
 +static void nice_legend(const char ***setname, int *nsets, char **LegendStr, char *value, char *unit, char EDgroupchar)
 +{
 +    char tmp[STRLEN], tmp2[STRLEN];
 +
 +
 +    sprintf(tmp, "%c %s", EDgroupchar, value);
 +    add_to_string_aligned(LegendStr, tmp);
 +    sprintf(tmp2, "%s (%s)", tmp, unit);
 +    (*setname)[*nsets] = strdup(tmp2);
 +    (*nsets)++;
 +}
 +
 +
 +static void nice_legend_evec(const char ***setname, int *nsets, char **LegendStr, t_eigvec *evec, char EDgroupChar, const char *EDtype)
 +{
 +    int  i;
 +    char tmp[STRLEN];
 +
 +
 +    for (i = 0; i < evec->neig; i++)
 +    {
 +        sprintf(tmp, "EV%dprj%s", evec->ieig[i], EDtype);
 +        nice_legend(setname, nsets, LegendStr, tmp, "nm", EDgroupChar);
 +    }
 +}
 +
 +
 +/* Makes a legend for the xvg output file. Call on MASTER only! */
 +static void write_edo_legend(gmx_edsam_t ed, int nED, const output_env_t oenv)
 +{
 +    t_edpar     *edi = NULL;
 +    int          i;
 +    int          nr_edi, nsets, n_flood, n_edsam;
 +    const char **setname;
 +    char         buf[STRLEN];
 +    char        *LegendStr = NULL;
 +
 +
 +    edi         = ed->edpar;
 +
 +    fprintf(ed->edo, "# Output will be written every %d step%s\n", ed->edpar->outfrq, ed->edpar->outfrq != 1 ? "s" : "");
 +
 +    for (nr_edi = 1; nr_edi <= nED; nr_edi++)
 +    {
-         if (edi->bNeedDoEdsam) /* Only print ED legend if at least one ED option is on */
 +        fprintf(ed->edo, "#\n");
 +        fprintf(ed->edo, "# Summary of applied con/restraints for the ED group %c\n", get_EDgroupChar(nr_edi, nED));
 +        fprintf(ed->edo, "# Atoms in average structure: %d\n", edi->sav.nr);
 +        fprintf(ed->edo, "#    monitor  : %d vec%s\n", edi->vecs.mon.neig, edi->vecs.mon.neig    != 1 ? "s" : "");
 +        fprintf(ed->edo, "#    LINFIX   : %d vec%s\n", edi->vecs.linfix.neig, edi->vecs.linfix.neig != 1 ? "s" : "");
 +        fprintf(ed->edo, "#    LINACC   : %d vec%s\n", edi->vecs.linacc.neig, edi->vecs.linacc.neig != 1 ? "s" : "");
 +        fprintf(ed->edo, "#    RADFIX   : %d vec%s\n", edi->vecs.radfix.neig, edi->vecs.radfix.neig != 1 ? "s" : "");
 +        fprintf(ed->edo, "#    RADACC   : %d vec%s\n", edi->vecs.radacc.neig, edi->vecs.radacc.neig != 1 ? "s" : "");
 +        fprintf(ed->edo, "#    RADCON   : %d vec%s\n", edi->vecs.radcon.neig, edi->vecs.radcon.neig != 1 ? "s" : "");
 +        fprintf(ed->edo, "#    FLOODING : %d vec%s  ", edi->flood.vecs.neig, edi->flood.vecs.neig  != 1 ? "s" : "");
 +
 +        if (edi->flood.vecs.neig)
 +        {
 +            /* If in any of the groups we find a flooding vector, flooding is turned on */
 +            ed->eEDtype = eEDflood;
 +
 +            /* Print what flavor of flooding we will do */
 +            if (0 == edi->flood.tau) /* constant flooding strength */
 +            {
 +                fprintf(ed->edo, "Efl_null = %g", edi->flood.constEfl);
 +                if (edi->flood.bHarmonic)
 +                {
 +                    fprintf(ed->edo, ", harmonic");
 +                }
 +            }
 +            else /* adaptive flooding */
 +            {
 +                fprintf(ed->edo, ", adaptive");
 +            }
 +        }
 +        fprintf(ed->edo, "\n");
 +
 +        edi = edi->next_edi;
 +    }
 +
 +    /* Print a nice legend */
 +    snew(LegendStr, 1);
 +    LegendStr[0] = '\0';
 +    sprintf(buf, "#     %6s", "time");
 +    add_to_string(&LegendStr, buf);
 +
 +    /* Calculate the maximum number of columns we could end up with */
 +    edi     = ed->edpar;
 +    nsets   = 0;
 +    for (nr_edi = 1; nr_edi <= nED; nr_edi++)
 +    {
 +        nsets += 5 +edi->vecs.mon.neig
 +            +edi->vecs.linfix.neig
 +            +edi->vecs.linacc.neig
 +            +edi->vecs.radfix.neig
 +            +edi->vecs.radacc.neig
 +            +edi->vecs.radcon.neig
 +            + 6*edi->flood.vecs.neig;
 +        edi = edi->next_edi;
 +    }
 +    snew(setname, nsets);
 +
 +    /* In the mdrun time step in a first function call (do_flood()) the flooding
 +     * forces are calculated and in a second function call (do_edsam()) the
 +     * ED constraints. To get a corresponding legend, we need to loop twice
 +     * over the edi groups and output first the flooding, then the ED part */
 +
 +    /* The flooding-related legend entries, if flooding is done */
 +    nsets = 0;
 +    if (eEDflood == ed->eEDtype)
 +    {
 +        edi   = ed->edpar;
 +        for (nr_edi = 1; nr_edi <= nED; nr_edi++)
 +        {
 +            /* Always write out the projection on the flooding EVs. Of course, this can also
 +             * be achieved with the monitoring option in do_edsam() (if switched on by the
 +             * user), but in that case the positions need to be communicated in do_edsam(),
 +             * which is not necessary when doing flooding only. */
 +            nice_legend(&setname, &nsets, &LegendStr, "RMSD to ref", "nm", get_EDgroupChar(nr_edi, nED) );
 +
 +            for (i = 0; i < edi->flood.vecs.neig; i++)
 +            {
 +                sprintf(buf, "EV%dprjFLOOD", edi->flood.vecs.ieig[i]);
 +                nice_legend(&setname, &nsets, &LegendStr, buf, "nm", get_EDgroupChar(nr_edi, nED));
 +
 +                /* Output the current reference projection if it changes with time;
 +                 * this can happen when flooding is used as harmonic restraint */
 +                if (edi->flood.bHarmonic && edi->flood.vecs.refprojslope[i] != 0.0)
 +                {
 +                    sprintf(buf, "EV%d ref.prj.", edi->flood.vecs.ieig[i]);
 +                    nice_legend(&setname, &nsets, &LegendStr, buf, "nm", get_EDgroupChar(nr_edi, nED));
 +                }
 +
 +                /* For flooding we also output Efl, Vfl, deltaF, and the flooding forces */
 +                if (0 != edi->flood.tau) /* only output Efl for adaptive flooding (constant otherwise) */
 +                {
 +                    sprintf(buf, "EV%d-Efl", edi->flood.vecs.ieig[i]);
 +                    nice_legend(&setname, &nsets, &LegendStr, buf, "kJ/mol", get_EDgroupChar(nr_edi, nED));
 +                }
 +
 +                sprintf(buf, "EV%d-Vfl", edi->flood.vecs.ieig[i]);
 +                nice_legend(&setname, &nsets, &LegendStr, buf, "kJ/mol", get_EDgroupChar(nr_edi, nED));
 +
 +                if (0 != edi->flood.tau) /* only output deltaF for adaptive flooding (zero otherwise) */
 +                {
 +                    sprintf(buf, "EV%d-deltaF", edi->flood.vecs.ieig[i]);
 +                    nice_legend(&setname, &nsets, &LegendStr, buf, "kJ/mol", get_EDgroupChar(nr_edi, nED));
 +                }
 +
 +                sprintf(buf, "EV%d-FLforces", edi->flood.vecs.ieig[i]);
 +                nice_legend(&setname, &nsets, &LegendStr, buf, "kJ/mol/nm", get_EDgroupChar(nr_edi, nED));
 +            }
 +
 +            edi = edi->next_edi;
 +        } /* End of flooding-related legend entries */
 +    }
 +    n_flood = nsets;
 +
 +    /* Now the ED-related entries, if essential dynamics is done */
 +    edi         = ed->edpar;
 +    for (nr_edi = 1; nr_edi <= nED; nr_edi++)
 +    {
-         /* 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);
++        if ( bNeedDoEdsam(edi) ) /* Only print ED legend if at least one ED option is on */
 +        {
 +            nice_legend(&setname, &nsets, &LegendStr, "RMSD to ref", "nm", get_EDgroupChar(nr_edi, nED) );
 +
 +            /* Essential dynamics, projections on eigenvectors */
 +            nice_legend_evec(&setname, &nsets, &LegendStr, &edi->vecs.mon, get_EDgroupChar(nr_edi, nED), "MON"   );
 +            nice_legend_evec(&setname, &nsets, &LegendStr, &edi->vecs.linfix, get_EDgroupChar(nr_edi, nED), "LINFIX");
 +            nice_legend_evec(&setname, &nsets, &LegendStr, &edi->vecs.linacc, get_EDgroupChar(nr_edi, nED), "LINACC");
 +            nice_legend_evec(&setname, &nsets, &LegendStr, &edi->vecs.radfix, get_EDgroupChar(nr_edi, nED), "RADFIX");
 +            if (edi->vecs.radfix.neig)
 +            {
 +                nice_legend(&setname, &nsets, &LegendStr, "RADFIX radius", "nm", get_EDgroupChar(nr_edi, nED));
 +            }
 +            nice_legend_evec(&setname, &nsets, &LegendStr, &edi->vecs.radacc, get_EDgroupChar(nr_edi, nED), "RADACC");
 +            if (edi->vecs.radacc.neig)
 +            {
 +                nice_legend(&setname, &nsets, &LegendStr, "RADACC radius", "nm", get_EDgroupChar(nr_edi, nED));
 +            }
 +            nice_legend_evec(&setname, &nsets, &LegendStr, &edi->vecs.radcon, get_EDgroupChar(nr_edi, nED), "RADCON");
 +            if (edi->vecs.radcon.neig)
 +            {
 +                nice_legend(&setname, &nsets, &LegendStr, "RADCON radius", "nm", get_EDgroupChar(nr_edi, nED));
 +            }
 +        }
 +        edi = edi->next_edi;
 +    } /* end of 'pure' essential dynamics legend entries */
 +    n_edsam = nsets - n_flood;
 +
 +    xvgr_legend(ed->edo, nsets, setname, oenv);
 +    sfree(setname);
 +
 +    fprintf(ed->edo, "#\n"
 +            "# Legend for %d column%s of flooding plus %d column%s of essential dynamics data:\n",
 +            n_flood, 1 == n_flood ? "" : "s",
 +            n_edsam, 1 == n_edsam ? "" : "s");
 +    fprintf(ed->edo, "%s", LegendStr);
 +    sfree(LegendStr);
 +
 +    fflush(ed->edo);
 +}
 +
 +
 +void init_edsam(gmx_mtop_t   *mtop,  /* global topology                    */
 +                t_inputrec   *ir,    /* input record                       */
 +                t_commrec    *cr,    /* communication record               */
 +                gmx_edsam_t   ed,    /* contains all ED data               */
 +                rvec          x[],   /* positions of the whole MD system   */
 +                matrix        box,   /* the box                            */
 +                edsamstate_t *EDstate)
 +{
 +    t_edpar *edi = NULL;                    /* points to a single edi data set */
 +    int      i, nr_edi, avindex;
 +    rvec    *x_pbc  = NULL;                 /* positions of the whole MD system with pbc removed  */
 +    rvec    *xfit   = NULL, *xstart = NULL; /* dummy arrays to determine initial RMSDs  */
 +    rvec     fit_transvec;                  /* translation ... */
 +    matrix   fit_rotmat;                    /* ... and rotation from fit to reference structure */
++    rvec    *ref_x_old = NULL;              /* helper pointer */
 +
 +
 +    if (!DOMAINDECOMP(cr) && PAR(cr) && MASTER(cr))
 +    {
 +        gmx_fatal(FARGS, "Please switch on domain decomposition to use essential dynamics in parallel.");
 +    }
 +
 +    if (MASTER(cr))
 +    {
 +        fprintf(stderr, "ED: Initializing essential dynamics constraints.\n");
 +
 +        if (NULL == ed)
 +        {
 +            gmx_fatal(FARGS, "The checkpoint file you provided is from an essential dynamics or\n"
 +                      "flooding simulation. Please also provide the correct .edi file with -ei.\n");
 +        }
 +    }
 +
 +    /* Needed for initializing radacc radius in do_edsam */
 +    ed->bFirst = TRUE;
 +
 +    /* The input file is read by the master and the edi structures are
 +     * initialized here. Input is stored in ed->edpar. Then the edi
 +     * structures are transferred to the other nodes */
 +    if (MASTER(cr))
 +    {
 +        /* Initialization for every ED/flooding group. Flooding uses one edi group per
 +         * flooding vector, Essential dynamics can be applied to more than one structure
 +         * as well, but will be done in the order given in the edi file, so
 +         * expect different results for different order of edi file concatenation! */
 +        edi = ed->edpar;
 +        while (edi != NULL)
 +        {
 +            init_edi(mtop, edi);
 +            init_flood(edi, ed, ir->delta_t);
 +            edi = edi->next_edi;
 +        }
 +    }
 +
 +    /* The master does the work here. The other nodes get the positions
 +     * not before dd_partition_system which is called after init_edsam */
 +    if (MASTER(cr))
 +    {
-             copy_rvecn(edi->sref.x_old, xfit, 0, edi->sref.nr);
++        if (!EDstate->bFromCpt)
++        {
++            /* Remove PBC, make molecule(s) subject to ED whole. */
++            snew(x_pbc, mtop->natoms);
++            m_rveccopy(mtop->natoms, x, x_pbc);
++            do_pbc_first_mtop(NULL, ir->ePBC, box, mtop, x_pbc);
++        }
 +        /* Reset pointer to first ED data set which contains the actual ED data */
 +        edi = ed->edpar;
 +        /* Loop over all ED/flooding data sets (usually only one, though) */
 +        for (nr_edi = 1; nr_edi <= EDstate->nED; nr_edi++)
 +        {
 +            /* For multiple ED groups we use the output frequency that was specified
 +             * in the first set */
 +            if (nr_edi > 1)
 +            {
 +                edi->outfrq = ed->edpar->outfrq;
 +            }
 +
 +            /* Extract the initial reference and average positions. When starting
 +             * from .cpt, these have already been read into sref.x_old
 +             * in init_edsamstate() */
 +            if (!EDstate->bFromCpt)
 +            {
 +                /* If this is the first run (i.e. no checkpoint present) we assume
 +                 * that the starting positions give us the correct PBC representation */
 +                for (i = 0; i < edi->sref.nr; i++)
 +                {
 +                    copy_rvec(x_pbc[edi->sref.anrs[i]], edi->sref.x_old[i]);
 +                }
 +
 +                for (i = 0; i < edi->sav.nr; i++)
 +                {
 +                    copy_rvec(x_pbc[edi->sav.anrs[i]], edi->sav.x_old[i]);
 +                }
 +            }
 +
 +            /* Now we have the PBC-correct start positions of the reference and
 +               average structure. We copy that over to dummy arrays on which we
 +               can apply fitting to print out the RMSD. We srenew the memory since
 +               the size of the buffers is likely different for every ED group */
 +            srenew(xfit, edi->sref.nr );
 +            srenew(xstart, edi->sav.nr  );
-         sfree(x_pbc);
++            if (edi->bRefEqAv)
++            {
++                /* Reference indices are the same as average indices */
++                ref_x_old = edi->sav.x_old;
++            }
++            else
++            {
++                ref_x_old = edi->sref.x_old;
++            }
++            copy_rvecn(ref_x_old, xfit, 0, edi->sref.nr);
 +            copy_rvecn(edi->sav.x_old, xstart, 0, edi->sav.nr);
 +
 +            /* Make the fit to the REFERENCE structure, get translation and rotation */
 +            fit_to_reference(xfit, fit_transvec, fit_rotmat, edi);
 +
 +            /* Output how well we fit to the reference at the start */
 +            translate_and_rotate(xfit, edi->sref.nr, fit_transvec, fit_rotmat);
 +            fprintf(stderr, "ED: Initial RMSD from reference after fit = %f nm",
 +                    rmsd_from_structure(xfit, &edi->sref));
 +            if (EDstate->nED > 1)
 +            {
 +                fprintf(stderr, " (ED group %c)", get_EDgroupChar(nr_edi, EDstate->nED));
 +            }
 +            fprintf(stderr, "\n");
 +
 +            /* Now apply the translation and rotation to the atoms on which ED sampling will be performed */
 +            translate_and_rotate(xstart, edi->sav.nr, fit_transvec, fit_rotmat);
 +
 +            /* calculate initial projections */
 +            project(xstart, edi);
 +
 +            /* For the target and origin structure both a reference (fit) and an
 +             * average structure can be provided in make_edi. If both structures
 +             * are the same, make_edi only stores one of them in the .edi file.
 +             * If they differ, first the fit and then the average structure is stored
 +             * in star (or sor), thus the number of entries in star/sor is
 +             * (n_fit + n_av) with n_fit the size of the fitting group and n_av
 +             * the size of the average group. */
 +
 +            /* process target structure, if required */
 +            if (edi->star.nr > 0)
 +            {
 +                fprintf(stderr, "ED: Fitting target structure to reference structure\n");
 +
 +                /* get translation & rotation for fit of target structure to reference structure */
 +                fit_to_reference(edi->star.x, fit_transvec, fit_rotmat, edi);
 +                /* do the fit */
 +                translate_and_rotate(edi->star.x, edi->star.nr, fit_transvec, fit_rotmat);
 +                if (edi->star.nr == edi->sav.nr)
 +                {
 +                    avindex = 0;
 +                }
 +                else /* edi->star.nr = edi->sref.nr + edi->sav.nr */
 +                {
 +                    /* The last sav.nr indices of the target structure correspond to
 +                     * the average structure, which must be projected */
 +                    avindex = edi->star.nr - edi->sav.nr;
 +                }
 +                rad_project(edi, &edi->star.x[avindex], &edi->vecs.radcon);
 +            }
 +            else
 +            {
 +                rad_project(edi, xstart, &edi->vecs.radcon);
 +            }
 +
 +            /* process structure that will serve as origin of expansion circle */
 +            if ( (eEDflood == ed->eEDtype) && (FALSE == edi->flood.bConstForce) )
 +            {
 +                fprintf(stderr, "ED: Setting center of flooding potential (0 = average structure)\n");
 +            }
 +
 +            if (edi->sori.nr > 0)
 +            {
 +                fprintf(stderr, "ED: Fitting origin structure to reference structure\n");
 +
 +                /* fit this structure to reference structure */
 +                fit_to_reference(edi->sori.x, fit_transvec, fit_rotmat, edi);
 +                /* do the fit */
 +                translate_and_rotate(edi->sori.x, edi->sori.nr, fit_transvec, fit_rotmat);
 +                if (edi->sori.nr == edi->sav.nr)
 +                {
 +                    avindex = 0;
 +                }
 +                else /* edi->sori.nr = edi->sref.nr + edi->sav.nr */
 +                {
 +                    /* For the projection, we need the last sav.nr indices of sori */
 +                    avindex = edi->sori.nr - edi->sav.nr;
 +                }
 +
 +                rad_project(edi, &edi->sori.x[avindex], &edi->vecs.radacc);
 +                rad_project(edi, &edi->sori.x[avindex], &edi->vecs.radfix);
 +                if ( (eEDflood == ed->eEDtype) && (FALSE == edi->flood.bConstForce) )
 +                {
 +                    fprintf(stderr, "ED: The ORIGIN structure will define the flooding potential center.\n");
 +                    /* Set center of flooding potential to the ORIGIN structure */
 +                    rad_project(edi, &edi->sori.x[avindex], &edi->flood.vecs);
 +                    /* We already know that no (moving) reference position was provided,
 +                     * therefore we can overwrite refproj[0]*/
 +                    copyEvecReference(&edi->flood.vecs);
 +                }
 +            }
 +            else /* No origin structure given */
 +            {
 +                rad_project(edi, xstart, &edi->vecs.radacc);
 +                rad_project(edi, xstart, &edi->vecs.radfix);
 +                if ( (eEDflood == ed->eEDtype) && (FALSE == edi->flood.bConstForce) )
 +                {
 +                    if (edi->flood.bHarmonic)
 +                    {
 +                        fprintf(stderr, "ED: A (possibly changing) ref. projection will define the flooding potential center.\n");
 +                        for (i = 0; i < edi->flood.vecs.neig; i++)
 +                        {
 +                            edi->flood.vecs.refproj[i] = edi->flood.vecs.refproj0[i];
 +                        }
 +                    }
 +                    else
 +                    {
 +                        fprintf(stderr, "ED: The AVERAGE structure will define the flooding potential center.\n");
 +                        /* Set center of flooding potential to the center of the covariance matrix,
 +                         * i.e. the average structure, i.e. zero in the projected system */
 +                        for (i = 0; i < edi->flood.vecs.neig; i++)
 +                        {
 +                            edi->flood.vecs.refproj[i] = 0.0;
 +                        }
 +                    }
 +                }
 +            }
 +            /* For convenience, output the center of the flooding potential for the eigenvectors */
 +            if ( (eEDflood == ed->eEDtype) && (FALSE == edi->flood.bConstForce) )
 +            {
 +                for (i = 0; i < edi->flood.vecs.neig; i++)
 +                {
 +                    fprintf(stdout, "ED: EV %d flooding potential center: %11.4e", edi->flood.vecs.ieig[i], edi->flood.vecs.refproj[i]);
 +                    if (edi->flood.bHarmonic)
 +                    {
 +                        fprintf(stdout, " (adding %11.4e/timestep)", edi->flood.vecs.refprojslope[i]);
 +                    }
 +                    fprintf(stdout, "\n");
 +                }
 +            }
 +
 +            /* set starting projections for linsam */
 +            rad_project(edi, xstart, &edi->vecs.linacc);
 +            rad_project(edi, xstart, &edi->vecs.linfix);
 +
 +            /* Prepare for the next edi data set: */
 +            edi = edi->next_edi;
 +        }
 +        /* Cleaning up on the master node: */
-         /* Allocate space for ED buffer */
-         snew(edi->buf, 1);
++        if (!EDstate->bFromCpt)
++        {
++            sfree(x_pbc);
++        }
 +        sfree(xfit);
 +        sfree(xstart);
 +
 +    } /* end of MASTER only section */
 +
 +    if (PAR(cr))
 +    {
 +        /* First let everybody know how many ED data sets to expect */
 +        gmx_bcast(sizeof(EDstate->nED), &EDstate->nED, cr);
 +        /* Broadcast the essential dynamics / flooding data to all nodes */
 +        broadcast_ed_data(cr, ed, EDstate->nED);
 +    }
 +    else
 +    {
 +        /* In the single-CPU case, point the local atom numbers pointers to the global
 +         * one, so that we can use the same notation in serial and parallel case: */
 +
 +        /* Loop over all ED data sets (usually only one, though) */
 +        edi = ed->edpar;
 +        for (nr_edi = 1; nr_edi <= EDstate->nED; nr_edi++)
 +        {
 +            edi->sref.anrs_loc = edi->sref.anrs;
 +            edi->sav.anrs_loc  = edi->sav.anrs;
 +            edi->star.anrs_loc = edi->star.anrs;
 +            edi->sori.anrs_loc = edi->sori.anrs;
 +            /* For the same reason as above, make a dummy c_ind array: */
 +            snew(edi->sav.c_ind, edi->sav.nr);
 +            /* Initialize the array */
 +            for (i = 0; i < edi->sav.nr; i++)
 +            {
 +                edi->sav.c_ind[i] = i;
 +            }
 +            /* In the general case we will need a different-sized array for the reference indices: */
 +            if (!edi->bRefEqAv)
 +            {
 +                snew(edi->sref.c_ind, edi->sref.nr);
 +                for (i = 0; i < edi->sref.nr; i++)
 +                {
 +                    edi->sref.c_ind[i] = i;
 +                }
 +            }
 +            /* Point to the very same array in case of other structures: */
 +            edi->star.c_ind = edi->sav.c_ind;
 +            edi->sori.c_ind = edi->sav.c_ind;
 +            /* In the serial case, the local number of atoms is the global one: */
 +            edi->sref.nr_loc = edi->sref.nr;
 +            edi->sav.nr_loc  = edi->sav.nr;
 +            edi->star.nr_loc = edi->star.nr;
 +            edi->sori.nr_loc = edi->sori.nr;
 +
 +            /* An on we go to the next ED group */
 +            edi = edi->next_edi;
 +        }
 +    }
 +
 +    /* Allocate space for ED buffer variables */
 +    /* Again, loop over ED data sets */
 +    edi = ed->edpar;
 +    for (nr_edi = 1; nr_edi <= EDstate->nED; nr_edi++)
 +    {
-         if (edi->bNeedDoEdsam)
++        /* Allocate space for ED buffer variables */
++        snew_bc(cr, edi->buf, 1); /* MASTER has already allocated edi->buf in init_edi() */
 +        snew(edi->buf->do_edsam, 1);
 +
 +        /* Space for collective ED buffer variables */
 +
 +        /* Collective positions of atoms with the average indices */
 +        snew(edi->buf->do_edsam->xcoll, edi->sav.nr);
 +        snew(edi->buf->do_edsam->shifts_xcoll, edi->sav.nr);            /* buffer for xcoll shifts */
 +        snew(edi->buf->do_edsam->extra_shifts_xcoll, edi->sav.nr);
 +        /* Collective positions of atoms with the reference indices */
 +        if (!edi->bRefEqAv)
 +        {
 +            snew(edi->buf->do_edsam->xc_ref, edi->sref.nr);
 +            snew(edi->buf->do_edsam->shifts_xc_ref, edi->sref.nr);       /* To store the shifts in */
 +            snew(edi->buf->do_edsam->extra_shifts_xc_ref, edi->sref.nr);
 +        }
 +
 +        /* Get memory for flooding forces */
 +        snew(edi->flood.forces_cartesian, edi->sav.nr);
 +
 +#ifdef DUMPEDI
 +        /* Dump it all into one file per process */
 +        dump_edi(edi, cr, nr_edi);
 +#endif
 +
 +        /* Next ED group */
 +        edi = edi->next_edi;
 +    }
 +
 +    /* Flush the edo file so that the user can check some things
 +     * when the simulation has started */
 +    if (ed->edo)
 +    {
 +        fflush(ed->edo);
 +    }
 +}
 +
 +
 +void do_edsam(t_inputrec     *ir,
 +              gmx_int64_t     step,
 +              t_commrec      *cr,
 +              rvec            xs[], /* The local current positions on this processor */
 +              rvec            v[],  /* The velocities */
 +              matrix          box,
 +              gmx_edsam_t     ed)
 +{
 +    int                i, edinr, iupdate = 500;
 +    matrix             rotmat;         /* rotation matrix */
 +    rvec               transvec;       /* translation vector */
 +    rvec               dv, dx, x_unsh; /* tmp vectors for velocity, distance, unshifted x coordinate */
 +    real               dt_1;           /* 1/dt */
 +    struct t_do_edsam *buf;
 +    t_edpar           *edi;
 +    real               rmsdev    = -1;    /* RMSD from reference structure prior to applying the constraints */
 +    gmx_bool           bSuppress = FALSE; /* Write .xvg output file on master? */
 +
 +
 +    /* Check if ED sampling has to be performed */
 +    if (ed->eEDtype == eEDnone)
 +    {
 +        return;
 +    }
 +
 +    /* Suppress output on first call of do_edsam if
 +     * two-step sd2 integrator is used */
 +    if ( (ir->eI == eiSD2) && (v != NULL) )
 +    {
 +        bSuppress = TRUE;
 +    }
 +
 +    dt_1 = 1.0/ir->delta_t;
 +
 +    /* Loop over all ED groups (usually one) */
 +    edi   = ed->edpar;
 +    edinr = 0;
 +    while (edi != NULL)
 +    {
 +        edinr++;
-                 /* initialise radacc radius for slope criterion */
++        if ( bNeedDoEdsam(edi) )
 +        {
 +
 +            buf = edi->buf->do_edsam;
 +
 +            if (ed->bFirst)
 +            {
-         } /* END of if (edi->bNeedDoEdsam) */
++                /* initialize radacc radius for slope criterion */
 +                buf->oldrad = calc_radius(&edi->vecs.radacc);
 +            }
 +
 +            /* Copy the positions into buf->xc* arrays and after ED
 +             * feed back corrections to the official positions */
 +
 +            /* Broadcast the ED positions such that every node has all of them
 +             * Every node contributes its local positions xs and stores it in
 +             * the collective buf->xcoll array. Note that for edinr > 1
 +             * xs could already have been modified by an earlier ED */
 +
 +            communicate_group_positions(cr, buf->xcoll, buf->shifts_xcoll, buf->extra_shifts_xcoll, PAR(cr) ? buf->bUpdateShifts : TRUE, xs,
 +                                        edi->sav.nr, edi->sav.nr_loc, edi->sav.anrs_loc, edi->sav.c_ind, edi->sav.x_old,  box);
 +
 +            /* Only assembly reference positions if their indices differ from the average ones */
 +            if (!edi->bRefEqAv)
 +            {
 +                communicate_group_positions(cr, buf->xc_ref, buf->shifts_xc_ref, buf->extra_shifts_xc_ref, PAR(cr) ? buf->bUpdateShifts : TRUE, xs,
 +                                            edi->sref.nr, edi->sref.nr_loc, edi->sref.anrs_loc, edi->sref.c_ind, edi->sref.x_old, box);
 +            }
 +
 +            /* If bUpdateShifts was TRUE then the shifts have just been updated in communicate_group_positions.
 +             * We do not need to update the shifts until the next NS step. Note that dd_make_local_ed_indices
 +             * set bUpdateShifts=TRUE in the parallel case. */
 +            buf->bUpdateShifts = FALSE;
 +
 +            /* Now all nodes have all of the ED positions in edi->sav->xcoll,
 +             * as well as the indices in edi->sav.anrs */
 +
 +            /* Fit the reference indices to the reference structure */
 +            if (edi->bRefEqAv)
 +            {
 +                fit_to_reference(buf->xcoll, transvec, rotmat, edi);
 +            }
 +            else
 +            {
 +                fit_to_reference(buf->xc_ref, transvec, rotmat, edi);
 +            }
 +
 +            /* Now apply the translation and rotation to the ED structure */
 +            translate_and_rotate(buf->xcoll, edi->sav.nr, transvec, rotmat);
 +
 +            /* Find out how well we fit to the reference (just for output steps) */
 +            if (do_per_step(step, edi->outfrq) && MASTER(cr))
 +            {
 +                if (edi->bRefEqAv)
 +                {
 +                    /* Indices of reference and average structures are identical,
 +                     * thus we can calculate the rmsd to SREF using xcoll */
 +                    rmsdev = rmsd_from_structure(buf->xcoll, &edi->sref);
 +                }
 +                else
 +                {
 +                    /* We have to translate & rotate the reference atoms first */
 +                    translate_and_rotate(buf->xc_ref, edi->sref.nr, transvec, rotmat);
 +                    rmsdev = rmsd_from_structure(buf->xc_ref, &edi->sref);
 +                }
 +            }
 +
 +            /* update radsam references, when required */
 +            if (do_per_step(step, edi->maxedsteps) && step >= edi->presteps)
 +            {
 +                project(buf->xcoll, edi);
 +                rad_project(edi, buf->xcoll, &edi->vecs.radacc);
 +                rad_project(edi, buf->xcoll, &edi->vecs.radfix);
 +                buf->oldrad = -1.e5;
 +            }
 +
 +            /* update radacc references, when required */
 +            if (do_per_step(step, iupdate) && step >= edi->presteps)
 +            {
 +                edi->vecs.radacc.radius = calc_radius(&edi->vecs.radacc);
 +                if (edi->vecs.radacc.radius - buf->oldrad < edi->slope)
 +                {
 +                    project(buf->xcoll, edi);
 +                    rad_project(edi, buf->xcoll, &edi->vecs.radacc);
 +                    buf->oldrad = 0.0;
 +                }
 +                else
 +                {
 +                    buf->oldrad = edi->vecs.radacc.radius;
 +                }
 +            }
 +
 +            /* apply the constraints */
 +            if (step >= edi->presteps && ed_constraints(ed->eEDtype, edi))
 +            {
 +                /* ED constraints should be applied already in the first MD step
 +                 * (which is step 0), therefore we pass step+1 to the routine */
 +                ed_apply_constraints(buf->xcoll, edi, step+1 - ir->init_step);
 +            }
 +
 +            /* write to edo, when required */
 +            if (do_per_step(step, edi->outfrq))
 +            {
 +                project(buf->xcoll, edi);
 +                if (MASTER(cr) && !bSuppress)
 +                {
 +                    write_edo(edi, ed->edo, rmsdev);
 +                }
 +            }
 +
 +            /* Copy back the positions unless monitoring only */
 +            if (ed_constraints(ed->eEDtype, edi))
 +            {
 +                /* remove fitting */
 +                rmfit(edi->sav.nr, buf->xcoll, transvec, rotmat);
 +
 +                /* Copy the ED corrected positions into the coordinate array */
 +                /* Each node copies its local part. In the serial case, nat_loc is the
 +                 * total number of ED atoms */
 +                for (i = 0; i < edi->sav.nr_loc; i++)
 +                {
 +                    /* Unshift local ED coordinate and store in x_unsh */
 +                    ed_unshift_single_coord(box, buf->xcoll[edi->sav.c_ind[i]],
 +                                            buf->shifts_xcoll[edi->sav.c_ind[i]], x_unsh);
 +
 +                    /* dx is the ED correction to the positions: */
 +                    rvec_sub(x_unsh, xs[edi->sav.anrs_loc[i]], dx);
 +
 +                    if (v != NULL)
 +                    {
 +                        /* dv is the ED correction to the velocity: */
 +                        svmul(dt_1, dx, dv);
 +                        /* apply the velocity correction: */
 +                        rvec_inc(v[edi->sav.anrs_loc[i]], dv);
 +                    }
 +                    /* Finally apply the position correction due to ED: */
 +                    copy_rvec(x_unsh, xs[edi->sav.anrs_loc[i]]);
 +                }
 +            }
++        } /* END of if ( bNeedDoEdsam(edi) ) */
 +
 +        /* Prepare for the next ED group */
 +        edi = edi->next_edi;
 +
 +    } /* END of loop over ED groups */
 +
 +    ed->bFirst = FALSE;
 +}