Merge release-4-6 into master
authorRoland Schulz <roland@utk.edu>
Tue, 21 May 2013 04:33:22 +0000 (00:33 -0400)
committerRoland Schulz <roland@utk.edu>
Tue, 21 May 2013 06:44:50 +0000 (02:44 -0400)
Conflicts:
CMakeLists.txt
cmake/FindMKL.cmake (deleted in 4.6)
src/gromacs/selection/compiler.cpp (was backport)
src/mdlib/nbnxn_cuda/nbnxn_cuda_types.h
src/ngmx/CMakeLists.txt
src/programs/grompp/gpp_tomorse.h

was incorrectly identified as rename target:
src/gromacs/trajectoryanalysis/tests/CMakeLists.txt
change instead belonged to:
        src/gromacs/mdlib/nbnxn_cuda/CMakeLists.txt

moved changes for OpenMM to src/programs/mdrun/CMakeLists.txt:
src/kernel/CMakeLists.txt
Deleted in 5.0 - only copyright changed:
share/template/template.c
share/template/template_doc.c
FFT_LIBRARIES change already in 5.0:
src/gmxlib/CMakeLists.txt
src/mdlib/CMakeLists.txt
src/tools/CMakeLists.txt
Removed GMX_*_EXPORT:
        src/gromacs/legacyheaders/domdec.h
        src/gromacs/legacyheaders/pme.h

Change-Id: Ib1d4abe66ea7b29c418dba687f8e7c1d645d39c4

73 files changed:
1  2 
CMakeLists.txt
COPYING
admin/gromacs.spec
admin/mkhtml
cmake/FindFFTW.cmake
cmake/FindVMD.cmake
src/contrib/BuildMdrunOpenMM.cmake
src/contrib/CMakeLists.txt
src/contrib/mdrun_openmm.c
src/gromacs/gmxana/gmx_anadock.c
src/gromacs/gmxana/gmx_do_dssp.c
src/gromacs/gmxana/gmx_hydorder.c
src/gromacs/gmxana/gmx_make_edi.c
src/gromacs/gmxana/gmx_make_ndx.c
src/gromacs/gmxana/gmx_mindist.c
src/gromacs/gmxana/gmx_mk_angndx.c
src/gromacs/gmxana/gmx_sgangle.c
src/gromacs/gmxana/gmx_sigeps.c
src/gromacs/gmxlib/bondfree.c
src/gromacs/gmxlib/copyrite.c
src/gromacs/gmxlib/gmx_system_xdr.c
src/gromacs/gmxlib/nonbonded/nb_free_energy.c
src/gromacs/gmxlib/topsort.c
src/gromacs/gmxlib/trxio.c
src/gromacs/gmxpreprocess/readir.c
src/gromacs/legacyheaders/domdec.h
src/gromacs/legacyheaders/gmx_simd_macros.h
src/gromacs/legacyheaders/gmx_system_xdr.h
src/gromacs/legacyheaders/mdrun.h
src/gromacs/legacyheaders/pme.h
src/gromacs/legacyheaders/types/group.h
src/gromacs/legacyheaders/types/nb_verlet.h
src/gromacs/legacyheaders/types/nbnxn_pairlist.h
src/gromacs/linearalgebra/gmx_blas/blas_copyright
src/gromacs/linearalgebra/gmx_lapack/lapack_copyright
src/gromacs/mdlib/coupling.c
src/gromacs/mdlib/domdec.c
src/gromacs/mdlib/edsam.c
src/gromacs/mdlib/force.c
src/gromacs/mdlib/gmx_wallcycle.c
src/gromacs/mdlib/md_support.c
src/gromacs/mdlib/mdebin.c
src/gromacs/mdlib/nbnxn_atomdata.c
src/gromacs/mdlib/nbnxn_cuda/CMakeLists.txt
src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda.cu
src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_data_mgmt.cu
src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernel.cuh
src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernel_legacy.cuh
src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernel_utils.cuh
src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_kernels.cuh
src/gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_types.h
src/gromacs/mdlib/nbnxn_internal.h
src/gromacs/mdlib/nbnxn_kernels/nbnxn_kernel_simd_2xnn_inner.h
src/gromacs/mdlib/nbnxn_kernels/nbnxn_kernel_simd_2xnn_outer.h
src/gromacs/mdlib/nbnxn_kernels/nbnxn_kernel_simd_4xn.c
src/gromacs/mdlib/nbnxn_kernels/nbnxn_kernel_simd_4xn_inner.h
src/gromacs/mdlib/nbnxn_kernels/nbnxn_kernel_simd_4xn_outer.h
src/gromacs/mdlib/nbnxn_kernels/nbnxn_kernel_simd_utils.h
src/gromacs/mdlib/nbnxn_search.c
src/gromacs/mdlib/nbnxn_search_simd_2xnn.h
src/gromacs/mdlib/nbnxn_search_simd_4xn.h
src/gromacs/mdlib/pme.c
src/gromacs/mdlib/pull_rotation.c
src/gromacs/mdlib/sim_util.c
src/gromacs/mdlib/tgroup.c
src/gromacs/mdlib/update.c
src/ngmx/CMakeLists.txt
src/programs/grompp/gpp_tomorse.h
src/programs/mdrun/CMakeLists.txt
src/programs/mdrun/mdrun.c
src/programs/mdrun/pme_loadbal.c
src/programs/mdrun/runner.c
tests/CMakeLists.txt

diff --cc CMakeLists.txt
index 3b120d671ac93810350c3cea7192dfc41ccf8cff,187fc992ab07a1bccb6afdbe2257621a1fcd9544..8db354047af569599ce12cf9bc8a4a265c94d662
@@@ -1038,18 -1104,20 +1087,18 @@@ endif(GMX_FAHCORE
  # these are set after everything else
  if (NOT GMX_SKIP_DEFAULT_CFLAGS)
      set(CMAKE_C_FLAGS "${ACCELERATION_C_FLAGS} ${MPI_COMPILE_FLAGS} ${CMAKE_C_FLAGS}")
 -    set(CMAKE_CXX_FLAGS "${ACCELERATION_CXX_FLAGS} ${MPI_COMPILE_FLAGS} ${CMAKE_CXX_FLAGS}")
 +    set(CMAKE_CXX_FLAGS "${ACCELERATION_CXX_FLAGS} ${MPI_COMPILE_FLAGS} ${GMX_CXX11_FLAGS} ${CMAKE_CXX_FLAGS}")
-     set(CMAKE_EXE_LINKER_FLAGS "${MPI_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
+     set(CMAKE_EXE_LINKER_FLAGS "${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
      set(CMAKE_SHARED_LINKER_FLAGS "${MPI_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
  else()
      message("Recommended flags which are not added because GMX_SKIP_DEFAULT_CFLAGS=yes:")
      message("CMAKE_C_FLAGS: ${ACCELERATION_C_FLAGS} ${MPI_COMPILE_FLAGS} ${GMXC_CFLAGS}")
      message("CMAKE_C_FLAGS_RELEASE: ${GMXC_CFLAGS_RELEASE}")
      message("CMAKE_C_FLAGS_DEBUG: ${GMXC_CFLAGS_DEBUG}")
 -    if(CMAKE_CXX_COMPILER_LOADED)
 -        message("CMAKE_CXX_FLAGS: ${ACCELERATION_CXX_FLAGS} ${MPI_COMPILE_FLAGS} ${GMXC_CXXFLAGS}")
 -        message("CMAKE_CXX_FLAGS_RELEASE: ${GMXC_CXXFLAGS_RELEASE}")
 -        message("CMAKE_CXX_FLAGS_DEBUG: ${GMXC_CXXFLAGS_DEBUG}")
 -    endif()
 +    message("CMAKE_CXX_FLAGS: ${ACCELERATION_CXX_FLAGS} ${MPI_COMPILE_FLAGS} ${GMX_CXX11_FLAGS} ${GMXC_CXXFLAGS}")
 +    message("CMAKE_CXX_FLAGS_RELEASE: ${GMXC_CXXFLAGS_RELEASE}")
 +    message("CMAKE_CXX_FLAGS_DEBUG: ${GMXC_CXXFLAGS_DEBUG}")
-     message("CMAKE_EXE_LINKER_FLAGS: ${MPI_LINKER_FLAGS}")
+     message("CMAKE_EXE_LINKER_FLAGS: ${FFT_LINKER_FLAGS} ${MPI_LINKER_FLAGS}")
      message("CMAKE_SHARED_LINKER_FLAGS: ${MPI_LINKER_FLAGS}")
  endif()
  
diff --cc COPYING
index 93293d42f28d0514cddbf4f3030a6f5487be992c,58175ae4b97521cc13378d4217763ff714482cb1..81a79a521fa7e61794050f4fd05c9063a6f404ac
+++ b/COPYING
@@@ -555,65 -1070,3 +555,65 @@@ in Gromacs (primarily full & sparse mat
  better idea to use the full reference implementation.
  
  Erik Lindahl, 2008-10-07.
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 +
 +
 +8. Subset of Boost C++ library
 +==============================
 +Files: src/external/boost/boost/*
 +
 +Boost Software License - Version 1.0 - August 17th, 2003
 +
 +Permission is hereby granted, free of charge, to any person or organization
 +obtaining a copy of the software and accompanying documentation covered by
 +this license (the "Software") to use, reproduce, display, distribute,
 +execute, and transmit the Software, and to prepare derivative works of the
 +Software, and to permit third-parties to whom the Software is furnished to
 +do so, all subject to the following:
 +
 +The copyright notices in the Software and this entire statement, including
 +the above license grant, this restriction and the following disclaimer,
 +must be included in all copies of the Software, in whole or in part, and
 +all derivative works of the Software, unless such copies or derivative
 +works are solely in the form of machine-executable object code generated by
 +a source language processor.
 +
 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 +DEALINGS IN THE SOFTWARE.
 +
 + 9. Google Test and Google Mock
 +===============================
 +Files: src/external/gmock-1.6.0/*
 +
 +Copyright 2008, Google Inc.
 +All rights reserved.
 +
 +Redistribution and use in source and binary forms, with or without
 +modification, are permitted provided that the following conditions are
 +met:
 +
 +    * Redistributions of source code must retain the above copyright
 +notice, this list of conditions and the following disclaimer.
 +    * Redistributions in binary form must reproduce the above
 +copyright notice, this list of conditions and the following disclaimer
 +in the documentation and/or other materials provided with the
 +distribution.
 +    * Neither the name of Google Inc. nor the names of its
 +contributors may be used to endorse or promote products derived from
 +this software without specific prior written permission.
 +
 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Simple merge
diff --cc admin/mkhtml
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 90ea0df0138cc4fb08c82f4af06845b5b4a60b78,0000000000000000000000000000000000000000..d43eb918e0b6eb3460d1b1b481f6c1dc65390afc
mode 100644,000000..100644
--- /dev/null
@@@ -1,397 -1,0 +1,400 @@@
-  *                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.
 +/*
++ * This file is part of the GROMACS molecular simulation package.
 + *
-  * 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
 + * 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.
-  * 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.
++ * Copyright (c) 2012, by the GROMACS development team, led by
++ * David van der Spoel, Berk Hess, Erik Lindahl, and including many
++ * others, as listed in the AUTHORS file in the top-level source
++ * directory and at http://www.gromacs.org.
++ *
++ * GROMACS is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public License
++ * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
-  * 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.
++ * 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.
 + *
-  * For more info, check our website at http://www.gromacs.org
++ * 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.
 + *
-  * And Hey:
-  * Green Red Orange Magenta Azure Cyan Skyblue
++ * 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 "confio.h"
 +#include "copyrite.h"
 +#include "futil.h"
 +#include "gmx_fatal.h"
 +#include "smalloc.h"
 +#include "string2.h"
 +#include "vec.h"
 +#include "gmx_statistics.h"
 +#include "statutil.h"
 +#include "typedefs.h"
 +#include "xvgr.h"
 +#include "macros.h"
 +
 +static const char *etitles[] = { "E-docked", "Free Energy" };
 +
 +typedef struct {
 +    real     edocked, efree;
 +    int      index, cluster_id;
 +    t_atoms  atoms;
 +    rvec    *x;
 +    int      ePBC;
 +    matrix   box;
 +} t_pdbfile;
 +
 +static t_pdbfile *read_pdbf(const char *fn)
 +{
 +    t_pdbfile *pdbf;
 +    double     e;
 +    char       buf[256], *ptr;
 +    int        natoms;
 +    FILE      *fp;
 +
 +    snew(pdbf, 1);
 +    get_stx_coordnum (fn, &natoms);
 +    init_t_atoms(&(pdbf->atoms), natoms, FALSE);
 +    snew(pdbf->x, natoms);
 +    read_stx_conf(fn, buf, &pdbf->atoms, pdbf->x, NULL, &pdbf->ePBC, pdbf->box);
 +    fp = ffopen(fn, "r");
 +    do
 +    {
 +        ptr = fgets2(buf, 255, fp);
 +        if (ptr)
 +        {
 +            if (strstr(buf, "Intermolecular") != NULL)
 +            {
 +                ptr = strchr(buf, '=');
 +                sscanf(ptr+1, "%lf", &e);
 +                pdbf->edocked = e;
 +            }
 +            else if (strstr(buf, "Estimated Free") != NULL)
 +            {
 +                ptr = strchr(buf, '=');
 +                sscanf(ptr+1, "%lf", &e);
 +                pdbf->efree = e;
 +            }
 +        }
 +    }
 +    while (ptr != NULL);
 +    ffclose(fp);
 +
 +    return pdbf;
 +}
 +
 +static t_pdbfile **read_em_all(const char *fn, int *npdbf)
 +{
 +    t_pdbfile **pdbf = 0;
 +    int         i, maxpdbf;
 +    char        buf[256], name[256];
 +    gmx_bool    bExist;
 +
 +    strcpy(buf, fn);
 +    buf[strlen(buf)-4] = '\0';
 +    maxpdbf            = 100;
 +    snew(pdbf, maxpdbf);
 +    i = 0;
 +    do
 +    {
 +        sprintf(name, "%s_%d.pdb", buf, i+1);
 +        if ((bExist = gmx_fexist(name)) == TRUE)
 +        {
 +            pdbf[i]        = read_pdbf(name);
 +            pdbf[i]->index = i+1;
 +            i++;
 +            if (i >= maxpdbf)
 +            {
 +                maxpdbf += 100;
 +                srenew(pdbf, maxpdbf);
 +            }
 +        }
 +    }
 +    while (bExist);
 +
 +    *npdbf = i;
 +
 +    printf("Found %d pdb files\n", i);
 +
 +    return pdbf;
 +}
 +
 +static gmx_bool bFreeSort = FALSE;
 +
 +static int pdbf_comp(const void *a, const void *b)
 +{
 +    t_pdbfile *pa, *pb;
 +    real       x;
 +    int        dc;
 +
 +    pa = *(t_pdbfile **)a;
 +    pb = *(t_pdbfile **)b;
 +
 +    dc = pa->cluster_id - pb->cluster_id;
 +
 +    if (dc == 0)
 +    {
 +        if (bFreeSort)
 +        {
 +            x = pa->efree   - pb->efree;
 +        }
 +        else
 +        {
 +            x = pa->edocked - pb->edocked;
 +        }
 +
 +        if (x < 0)
 +        {
 +            return -1;
 +        }
 +        else if (x > 0)
 +        {
 +            return 1;
 +        }
 +        else
 +        {
 +            return 0;
 +        }
 +    }
 +    else
 +    {
 +        return dc;
 +    }
 +}
 +
 +static void analyse_em_all(int npdb, t_pdbfile *pdbf[], const char *edocked,
 +                           const char *efree, const output_env_t oenv)
 +{
 +    FILE *fp;
 +    int   i;
 +
 +    for (bFreeSort = FALSE; (bFreeSort <= TRUE); bFreeSort++)
 +    {
 +        qsort(pdbf, npdb, sizeof(pdbf[0]), pdbf_comp);
 +        fp = xvgropen(bFreeSort ? efree : edocked,
 +                      etitles[bFreeSort], "()", "E (kJ/mol)", oenv);
 +        for (i = 0; (i < npdb); i++)
 +        {
 +            fprintf(fp, "%12lf\n", bFreeSort ? pdbf[i]->efree : pdbf[i]->edocked);
 +        }
 +        ffclose(fp);
 +    }
 +}
 +
 +static void clust_stat(FILE *fp, int start, int end, t_pdbfile *pdbf[])
 +{
 +    int         i;
 +    gmx_stats_t ed, ef;
 +    real        aver, sigma;
 +
 +    ed = gmx_stats_init();
 +    ef = gmx_stats_init();
 +    for (i = start; (i < end); i++)
 +    {
 +        gmx_stats_add_point(ed, i-start, pdbf[i]->edocked, 0, 0);
 +        gmx_stats_add_point(ef, i-start, pdbf[i]->efree, 0, 0);
 +    }
 +    gmx_stats_get_ase(ed, &aver, &sigma, NULL);
 +    fprintf(fp, "  <%12s> = %8.3f (+/- %6.3f)\n", etitles[FALSE], aver, sigma);
 +    gmx_stats_get_ase(ef, &aver, &sigma, NULL);
 +    fprintf(fp, "  <%12s> = %8.3f (+/- %6.3f)\n", etitles[TRUE], aver, sigma);
 +    gmx_stats_done(ed);
 +    gmx_stats_done(ef);
 +    sfree(ed);
 +    sfree(ef);
 +}
 +
 +static real rmsd_dist(t_pdbfile *pa, t_pdbfile *pb, gmx_bool bRMSD)
 +{
 +    int  i;
 +    real rmsd, dist;
 +    rvec acm, bcm, dx;
 +
 +    if (bRMSD)
 +    {
 +        rmsd = 0;
 +        for (i = 0; (i < pa->atoms.nr); i++)
 +        {
 +            rvec_sub(pa->x[i], pb->x[i], dx);
 +            rmsd += iprod(dx, dx);
 +        }
 +        rmsd = sqrt(rmsd/pa->atoms.nr);
 +    }
 +    else
 +    {
 +        dist = 0;
 +        clear_rvec(acm);
 +        clear_rvec(bcm);
 +        for (i = 0; (i < pa->atoms.nr); i++)
 +        {
 +            rvec_inc(acm, pa->x[i]);
 +            rvec_inc(bcm, pb->x[i]);
 +        }
 +        rvec_sub(acm, bcm, dx);
 +        for (i = 0; (i < DIM); i++)
 +        {
 +            dx[i] /= pa->atoms.nr;
 +        }
 +        rmsd = norm(dx);
 +    }
 +    return rmsd;
 +}
 +
 +static void line(FILE *fp)
 +{
 +    fprintf(fp, "                   - - - - - - - - - -\n");
 +}
 +
 +static void cluster_em_all(FILE *fp, int npdb, t_pdbfile *pdbf[],
 +                           const char *pdbout, gmx_bool bFree, gmx_bool bRMSD, real cutoff)
 +{
 +    int   i, j, k;
 +    int  *cndx, ncluster;
 +    real  rmsd;
 +
 +    bFreeSort = bFree;
 +    qsort(pdbf, npdb, sizeof(pdbf[0]), pdbf_comp);
 +
 +    fprintf(fp, "Statistics over all structures:\n");
 +    clust_stat(fp, 0, npdb, pdbf);
 +    line(fp);
 +
 +    /* Index to first structure in a cluster */
 +    snew(cndx, npdb);
 +    ncluster = 1;
 +
 +    for (i = 1; (i < npdb); i++)
 +    {
 +        for (j = 0; (j < ncluster); j++)
 +        {
 +            rmsd = rmsd_dist(pdbf[cndx[j]], pdbf[i], bRMSD);
 +            if (rmsd <= cutoff)
 +            {
 +                /* Structure i is in cluster j */
 +                pdbf[i]->cluster_id = pdbf[cndx[j]]->cluster_id;
 +                break;
 +            }
 +        }
 +        if (j == ncluster)
 +        {
 +            /* New cluster! Cool! */
 +            cndx[ncluster]      = i;
 +            pdbf[i]->cluster_id = ncluster;
 +            ncluster++;
 +        }
 +    }
 +    cndx[ncluster] = npdb;
 +    qsort(pdbf, npdb, sizeof(pdbf[0]), pdbf_comp);
 +
 +    j         = 0;
 +    cndx[j++] = 0;
 +    for (i = 1; (i < npdb); i++)
 +    {
 +        if (pdbf[i]->cluster_id != pdbf[i-1]->cluster_id)
 +        {
 +            cndx[j++] = i;
 +        }
 +    }
 +    cndx[j] = npdb;
 +    if (j != ncluster)
 +    {
 +        gmx_fatal(FARGS, "Consistency error: j = %d, ncluster = %d", j, ncluster);
 +    }
 +
 +    fprintf(fp, "I found %d clusters based on %s and %s with a %.3f nm cut-off\n",
 +            ncluster, etitles[bFree], bRMSD ? "RMSD" : "distance", cutoff);
 +    line(fp);
 +    for (j = 0; (j < ncluster); j++)
 +    {
 +        fprintf(fp, "Cluster: %3d  %s: %10.5f kJ/mol %3d elements\n",
 +                j, etitles[bFree],
 +                bFree ? pdbf[cndx[j]]->efree : pdbf[cndx[j]]->edocked,
 +                cndx[j+1]-cndx[j]);
 +        clust_stat(fp, cndx[j], cndx[j+1], pdbf);
 +        for (k = cndx[j]; (k < cndx[j+1]); k++)
 +        {
 +            fprintf(fp, "  %3d", pdbf[k]->index);
 +        }
 +        fprintf(fp, "\n");
 +        line(fp);
 +    }
 +    sfree(cndx);
 +}
 +
 +int gmx_anadock(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "[TT]g_anadock[tt] analyses the results of an Autodock run and clusters the",
 +        "structures together, based on distance or RMSD. The docked energy",
 +        "and free energy estimates are analysed, and for each cluster the",
 +        "energy statistics are printed.[PAR]",
 +        "An alternative approach to this is to cluster the structures first",
 +        "using [TT]g_cluster[tt] and then sort the clusters on either lowest",
 +        "energy or average energy."
 +    };
 +    t_filenm        fnm[] = {
 +        { efPDB, "-f", NULL,       ffREAD  },
 +        { efPDB, "-ox", "cluster", ffWRITE },
 +        { efXVG, "-od", "edocked", ffWRITE },
 +        { efXVG, "-of", "efree",   ffWRITE },
 +        { efLOG, "-g",  "anadock", ffWRITE }
 +    };
 +    output_env_t    oenv;
 +#define NFILE asize(fnm)
 +    static gmx_bool bFree  = FALSE, bRMS = TRUE;
 +    static real     cutoff = 0.2;
 +    t_pargs         pa[]   = {
 +        { "-free",   FALSE, etBOOL, {&bFree},
 +          "Use Free energy estimate from autodock for sorting the classes" },
 +        { "-rms",    FALSE, etBOOL, {&bRMS},
 +          "Cluster on RMS or distance" },
 +        { "-cutoff", FALSE, etREAL, {&cutoff},
 +          "Maximum RMSD/distance for belonging to the same cluster" }
 +    };
 +#define NPA asize(pa)
 +
 +    FILE       *fp;
 +    t_pdbfile **pdbf = NULL;
 +    int         npdbf;
 +
 +    parse_common_args(&argc, argv, 0, NFILE, fnm, NPA, pa, asize(desc), desc, 0,
 +                      NULL, &oenv);
 +
 +    fp = ffopen(opt2fn("-g", NFILE, fnm), "w");
 +    please_cite(stdout, "Hetenyi2002b");
 +    please_cite(fp, "Hetenyi2002b");
 +
 +    pdbf = read_em_all(opt2fn("-f", NFILE, fnm), &npdbf);
 +
 +    analyse_em_all(npdbf, pdbf, opt2fn("-od", NFILE, fnm), opt2fn("-of", NFILE, fnm),
 +                   oenv);
 +
 +    cluster_em_all(fp, npdbf, pdbf, opt2fn("-ox", NFILE, fnm),
 +                   bFree, bRMS, cutoff);
 +
 +    thanx(fp);
 +    ffclose(fp);
 +
 +    thanx(stdout);
 +
 +    return 0;
 +}
index 61c141c305fda8f2f9a7bad6d9d40ce0566237e1,0000000000000000000000000000000000000000..23a755db0c763b25a1e17a5ca993981b85d47ff6
mode 100644,000000..100644
--- /dev/null
@@@ -1,748 -1,0 +1,750 @@@
- /*  -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
++/*
++ * This file is part of the GROMACS molecular simulation package.
 + *
-  *
-  *                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
++ * Copyright (c) 2012, by the GROMACS development team, led by
++ * David van der Spoel, Berk Hess, Erik Lindahl, and including many
++ * others, as listed in the AUTHORS file in the top-level source
++ * directory and at http://www.gromacs.org.
++ *
++ * GROMACS is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public License
++ * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
-  * 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.
++ * 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.
 + *
-  * 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.
++ * 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.
 + *
-  * For more info, check our website at http://www.gromacs.org
++ * 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.
 + *
-  * And Hey:
-  * Green Red Orange Magenta Azure Cyan Skyblue
++ * 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 "sysstuff.h"
 +#include "typedefs.h"
 +#include "string2.h"
 +#include "strdb.h"
 +#include "macros.h"
 +#include "smalloc.h"
 +#include "mshift.h"
 +#include "statutil.h"
 +#include "copyrite.h"
 +#include "pdbio.h"
 +#include "gmx_fatal.h"
 +#include "xvgr.h"
 +#include "matio.h"
 +#include "index.h"
 +#include "gstat.h"
 +#include "tpxio.h"
 +#include "viewit.h"
 +
 +
 +static int strip_dssp(char *dsspfile, int nres,
 +                      gmx_bool bPhobres[], real t,
 +                      real *acc, FILE *fTArea,
 +                      t_matrix *mat, int average_area[],
 +                      const output_env_t oenv)
 +{
 +    static gmx_bool bFirst = TRUE;
 +    static char    *ssbuf;
 +    FILE           *tapeout;
 +    static int      xsize, frame;
 +    char            buf[STRLEN+1];
 +    char            SSTP;
 +    int             i, nr, iacc, nresidues;
 +    int             naccf, naccb; /* Count hydrophobic and hydrophilic residues */
 +    real            iaccf, iaccb;
 +    t_xpmelmt       c;
 +
 +    tapeout = ffopen(dsspfile, "r");
 +
 +    /* Skip header */
 +    do
 +    {
 +        fgets2(buf, STRLEN, tapeout);
 +    }
 +    while (strstr(buf, "KAPPA") == NULL);
 +    if (bFirst)
 +    {
 +        /* Since we also have empty lines in the dssp output (temp) file,
 +         * and some line content is saved to the ssbuf variable,
 +         * we need more memory than just nres elements. To be shure,
 +         * we allocate 2*nres-1, since for each chain there is a
 +         * separating line in the temp file. (At most each residue
 +         * could have been defined as a separate chain.) */
 +        snew(ssbuf, 2*nres-1);
 +    }
 +
 +    iaccb     = iaccf = 0;
 +    nresidues = 0;
 +    naccf     = 0;
 +    naccb     = 0;
 +    for (nr = 0; (fgets2(buf, STRLEN, tapeout) != NULL); nr++)
 +    {
 +        if (buf[13] == '!') /* Chain separator line has '!' at pos. 13 */
 +        {
 +            SSTP = '=';     /* Chain separator sign '=' */
 +        }
 +        else
 +        {
 +            SSTP = buf[16] == ' ' ? '~' : buf[16];
 +        }
 +        ssbuf[nr] = SSTP;
 +
 +        buf[39] = '\0';
 +
 +        /* Only calculate solvent accessible area if needed */
 +        if ((NULL != acc) && (buf[13] != '!'))
 +        {
 +            sscanf(&(buf[34]), "%d", &iacc);
 +            acc[nr] = iacc;
 +            /* average_area and bPhobres are counted from 0...nres-1 */
 +            average_area[nresidues] += iacc;
 +            if (bPhobres[nresidues])
 +            {
 +                naccb++;
 +                iaccb += iacc;
 +            }
 +            else
 +            {
 +                naccf++;
 +                iaccf += iacc;
 +            }
 +            /* Keep track of the residue number (do not count chain separator lines '!') */
 +            nresidues++;
 +        }
 +    }
 +    ssbuf[nr] = '\0';
 +
 +    if (bFirst)
 +    {
 +        if (0 != acc)
 +        {
 +            fprintf(stderr, "%d residues were classified as hydrophobic and %d as hydrophilic.\n", naccb, naccf);
 +        }
 +
 +        sprintf(mat->title, "Secondary structure");
 +        mat->legend[0] = 0;
 +        sprintf(mat->label_x, "%s", output_env_get_time_label(oenv));
 +        sprintf(mat->label_y, "Residue");
 +        mat->bDiscrete = TRUE;
 +        mat->ny        = nr;
 +        snew(mat->axis_y, nr);
 +        for (i = 0; i < nr; i++)
 +        {
 +            mat->axis_y[i] = i+1;
 +        }
 +        mat->axis_x = NULL;
 +        mat->matrix = NULL;
 +        xsize       = 0;
 +        frame       = 0;
 +        bFirst      = FALSE;
 +    }
 +    if (frame >= xsize)
 +    {
 +        xsize += 10;
 +        srenew(mat->axis_x, xsize);
 +        srenew(mat->matrix, xsize);
 +    }
 +    mat->axis_x[frame] = t;
 +    snew(mat->matrix[frame], nr);
 +    c.c2 = 0;
 +    for (i = 0; i < nr; i++)
 +    {
 +        c.c1                  = ssbuf[i];
 +        mat->matrix[frame][i] = max(0, searchcmap(mat->nmap, mat->map, c));
 +    }
 +    frame++;
 +    mat->nx = frame;
 +
 +    if (fTArea)
 +    {
 +        fprintf(fTArea, "%10g  %10g  %10g\n", t, 0.01*iaccb, 0.01*iaccf);
 +    }
 +    ffclose(tapeout);
 +
 +    /* Return the number of lines found in the dssp file (i.e. number
 +     * of redidues plus chain separator lines).
 +     * This is the number of y elements needed for the area xpm file */
 +    return nr;
 +}
 +
 +static gmx_bool *bPhobics(t_atoms *atoms)
 +{
 +    int         i, nb;
 +    char      **cb;
 +    gmx_bool   *bb;
 +
 +
 +    nb = get_strings("phbres.dat", &cb);
 +    snew(bb, atoms->nres);
 +
 +    for (i = 0; (i < atoms->nres); i++)
 +    {
 +        if (-1 != search_str(nb, cb, *atoms->resinfo[i].name) )
 +        {
 +            bb[i] = TRUE;
 +        }
 +    }
 +    return bb;
 +}
 +
 +static void check_oo(t_atoms *atoms)
 +{
 +    char *OOO;
 +
 +    int   i;
 +
 +    OOO = strdup("O");
 +
 +    for (i = 0; (i < atoms->nr); i++)
 +    {
 +        if (strcmp(*(atoms->atomname[i]), "OXT") == 0)
 +        {
 +            *atoms->atomname[i] = OOO;
 +        }
 +        else if (strcmp(*(atoms->atomname[i]), "O1") == 0)
 +        {
 +            *atoms->atomname[i] = OOO;
 +        }
 +        else if (strcmp(*(atoms->atomname[i]), "OC1") == 0)
 +        {
 +            *atoms->atomname[i] = OOO;
 +        }
 +    }
 +}
 +
 +static void norm_acc(t_atoms *atoms, int nres,
 +                     real av_area[], real norm_av_area[])
 +{
 +    int     i, n, n_surf;
 +
 +    char    surffn[] = "surface.dat";
 +    char  **surf_res, **surf_lines;
 +    double *surf;
 +
 +    n_surf = get_lines(surffn, &surf_lines);
 +    snew(surf, n_surf);
 +    snew(surf_res, n_surf);
 +    for (i = 0; (i < n_surf); i++)
 +    {
 +        snew(surf_res[i], 5);
 +        sscanf(surf_lines[i], "%s %lf", surf_res[i], &surf[i]);
 +    }
 +
 +    for (i = 0; (i < nres); i++)
 +    {
 +        n = search_str(n_surf, surf_res, *atoms->resinfo[i].name);
 +        if (n != -1)
 +        {
 +            norm_av_area[i] = av_area[i] / surf[n];
 +        }
 +        else
 +        {
 +            fprintf(stderr, "Residue %s not found in surface database (%s)\n",
 +                    *atoms->resinfo[i].name, surffn);
 +        }
 +    }
 +}
 +
 +void prune_ss_legend(t_matrix *mat)
 +{
 +    gmx_bool  *present;
 +    int       *newnum;
 +    int        i, r, f, newnmap;
 +    t_mapping *newmap;
 +
 +    snew(present, mat->nmap);
 +    snew(newnum, mat->nmap);
 +
 +    for (f = 0; f < mat->nx; f++)
 +    {
 +        for (r = 0; r < mat->ny; r++)
 +        {
 +            present[mat->matrix[f][r]] = TRUE;
 +        }
 +    }
 +
 +    newnmap = 0;
 +    newmap  = NULL;
 +    for (i = 0; i < mat->nmap; i++)
 +    {
 +        newnum[i] = -1;
 +        if (present[i])
 +        {
 +            newnum[i] = newnmap;
 +            newnmap++;
 +            srenew(newmap, newnmap);
 +            newmap[newnmap-1] = mat->map[i];
 +        }
 +    }
 +    if (newnmap != mat->nmap)
 +    {
 +        mat->nmap = newnmap;
 +        mat->map  = newmap;
 +        for (f = 0; f < mat->nx; f++)
 +        {
 +            for (r = 0; r < mat->ny; r++)
 +            {
 +                mat->matrix[f][r] = newnum[mat->matrix[f][r]];
 +            }
 +        }
 +    }
 +}
 +
 +void write_sas_mat(const char *fn, real **accr, int nframe, int nres, t_matrix *mat)
 +{
 +    real  lo, hi;
 +    int   i, j, nlev;
 +    t_rgb rlo = {1, 1, 1}, rhi = {0, 0, 0};
 +    FILE *fp;
 +
 +    if (fn)
 +    {
 +        hi = lo = accr[0][0];
 +        for (i = 0; i < nframe; i++)
 +        {
 +            for (j = 0; j < nres; j++)
 +            {
 +                lo = min(lo, accr[i][j]);
 +                hi = max(hi, accr[i][j]);
 +            }
 +        }
 +        fp   = ffopen(fn, "w");
 +        nlev = hi-lo+1;
 +        write_xpm(fp, 0, "Solvent Accessible Surface", "Surface (A^2)",
 +                  "Time", "Residue Index", nframe, nres,
 +                  mat->axis_x, mat->axis_y, accr, lo, hi, rlo, rhi, &nlev);
 +        ffclose(fp);
 +    }
 +}
 +
 +void analyse_ss(const char *outfile, t_matrix *mat, const char *ss_string,
 +                const output_env_t oenv)
 +{
 +    FILE        *fp;
 +    t_mapping   *map;
 +    int          f, r, *count, *total, ss_count, total_count;
 +    size_t       s;
 +    const char** leg;
 +
 +    map = mat->map;
 +    snew(count, mat->nmap);
 +    snew(total, mat->nmap);
 +    snew(leg, mat->nmap+1);
 +    leg[0] = "Structure";
 +    for (s = 0; s < (size_t)mat->nmap; s++)
 +    {
 +        leg[s+1] = strdup(map[s].desc);
 +    }
 +
 +    fp = xvgropen(outfile, "Secondary Structure",
 +                  output_env_get_xvgr_tlabel(oenv), "Number of Residues", oenv);
 +    if (output_env_get_print_xvgr_codes(oenv))
 +    {
 +        fprintf(fp, "@ subtitle \"Structure = ");
 +    }
 +    for (s = 0; s < strlen(ss_string); s++)
 +    {
 +        if (s > 0)
 +        {
 +            fprintf(fp, " + ");
 +        }
 +        for (f = 0; f < mat->nmap; f++)
 +        {
 +            if (ss_string[s] == map[f].code.c1)
 +            {
 +                fprintf(fp, "%s", map[f].desc);
 +            }
 +        }
 +    }
 +    fprintf(fp, "\"\n");
 +    xvgr_legend(fp, mat->nmap+1, leg, oenv);
 +
 +    total_count = 0;
 +    for (s = 0; s < (size_t)mat->nmap; s++)
 +    {
 +        total[s] = 0;
 +    }
 +    for (f = 0; f < mat->nx; f++)
 +    {
 +        ss_count = 0;
 +        for (s = 0; s < (size_t)mat->nmap; s++)
 +        {
 +            count[s] = 0;
 +        }
 +        for (r = 0; r < mat->ny; r++)
 +        {
 +            count[mat->matrix[f][r]]++;
 +            total[mat->matrix[f][r]]++;
 +        }
 +        for (s = 0; s < (size_t)mat->nmap; s++)
 +        {
 +            if (strchr(ss_string, map[s].code.c1))
 +            {
 +                ss_count    += count[s];
 +                total_count += count[s];
 +            }
 +        }
 +        fprintf(fp, "%8g %5d", mat->axis_x[f], ss_count);
 +        for (s = 0; s < (size_t)mat->nmap; s++)
 +        {
 +            fprintf(fp, " %5d", count[s]);
 +        }
 +        fprintf(fp, "\n");
 +    }
 +    /* now print column totals */
 +    fprintf(fp, "%-8s %5d", "# Totals", total_count);
 +    for (s = 0; s < (size_t)mat->nmap; s++)
 +    {
 +        fprintf(fp, " %5d", total[s]);
 +    }
 +    fprintf(fp, "\n");
 +
 +    /* now print percentages */
 +    fprintf(fp, "%-8s %5.2f", "# SS %", total_count / (real) (mat->nx * mat->ny));
 +    for (s = 0; s < (size_t)mat->nmap; s++)
 +    {
 +        fprintf(fp, " %5.2f", total[s] / (real) (mat->nx * mat->ny));
 +    }
 +    fprintf(fp, "\n");
 +
 +    ffclose(fp);
 +    sfree(leg);
 +    sfree(count);
 +}
 +
 +int gmx_do_dssp(int argc, char *argv[])
 +{
 +    const char        *desc[] = {
 +        "[TT]do_dssp[tt] ",
 +        "reads a trajectory file and computes the secondary structure for",
 +        "each time frame ",
 +        "calling the dssp program. If you do not have the dssp program,",
 +        "get it from http://swift.cmbi.ru.nl/gv/dssp. [TT]do_dssp[tt] assumes ",
 +        "that the dssp executable is located in ",
 +        "[TT]/usr/local/bin/dssp[tt]. If this is not the case, then you should",
 +        "set an environment variable [TT]DSSP[tt] pointing to the dssp",
 +        "executable, e.g.: [PAR]",
 +        "[TT]setenv DSSP /opt/dssp/bin/dssp[tt][PAR]",
 +        "Since version 2.0.0, dssp is invoked with a syntax that differs",
 +        "from earlier versions. If you have an older version of dssp,",
 +        "use the [TT]-ver[tt] option to direct do_dssp to use the older syntax.",
 +        "By default, do_dssp uses the syntax introduced with version 2.0.0.",
 +        "Even newer versions (which at the time of writing are not yet released)",
 +        "are assumed to have the same syntax as 2.0.0.[PAR]",
 +        "The structure assignment for each residue and time is written to an",
 +        "[TT].xpm[tt] matrix file. This file can be visualized with for instance",
 +        "[TT]xv[tt] and can be converted to postscript with [TT]xpm2ps[tt].",
 +        "Individual chains are separated by light grey lines in the [TT].xpm[tt] and",
 +        "postscript files.",
 +        "The number of residues with each secondary structure type and the",
 +        "total secondary structure ([TT]-sss[tt]) count as a function of",
 +        "time are also written to file ([TT]-sc[tt]).[PAR]",
 +        "Solvent accessible surface (SAS) per residue can be calculated, both in",
 +        "absolute values (A^2) and in fractions of the maximal accessible",
 +        "surface of a residue. The maximal accessible surface is defined as",
 +        "the accessible surface of a residue in a chain of glycines.",
 +        "[BB]Note[bb] that the program [TT]g_sas[tt] can also compute SAS",
 +        "and that is more efficient.[PAR]",
 +        "Finally, this program can dump the secondary structure in a special file",
 +        "[TT]ssdump.dat[tt] for usage in the program [TT]g_chi[tt]. Together",
 +        "these two programs can be used to analyze dihedral properties as a",
 +        "function of secondary structure type."
 +    };
 +    static gmx_bool    bVerbose;
 +    static const char *ss_string   = "HEBT";
 +    static int         dsspVersion = 2;
 +    t_pargs            pa[]        = {
 +        { "-v",  FALSE, etBOOL, {&bVerbose},
 +          "HIDDENGenerate miles of useless information" },
 +        { "-sss", FALSE, etSTR, {&ss_string},
 +          "Secondary structures for structure count"},
 +        { "-ver", FALSE, etINT, {&dsspVersion},
 +          "DSSP major version. Syntax changed with version 2"}
 +    };
 +
 +    t_trxstatus       *status;
 +    FILE              *tapein;
 +    FILE              *ss, *acc, *fTArea, *tmpf;
 +    const char        *fnSCount, *fnArea, *fnTArea, *fnAArea;
 +    const char        *leg[] = { "Phobic", "Phylic" };
 +    t_topology         top;
 +    int                ePBC;
 +    t_atoms           *atoms;
 +    t_matrix           mat;
 +    int                nres, nr0, naccr, nres_plus_separators;
 +    gmx_bool          *bPhbres, bDoAccSurf;
 +    real               t;
 +    int                i, j, natoms, nframe = 0;
 +    matrix             box;
 +    int                gnx;
 +    char              *grpnm, *ss_str;
 +    atom_id           *index;
 +    rvec              *xp, *x;
 +    int               *average_area;
 +    real             **accr, *accr_ptr = NULL, *av_area, *norm_av_area;
 +    char               pdbfile[32], tmpfile[32], title[256];
 +    char               dssp[256];
 +    const char        *dptr;
 +    output_env_t       oenv;
 +    gmx_rmpbc_t        gpbc = NULL;
 +
 +    t_filenm           fnm[] = {
 +        { efTRX, "-f",   NULL,      ffREAD },
 +        { efTPS, NULL,   NULL,      ffREAD },
 +        { efNDX, NULL,   NULL,      ffOPTRD },
 +        { efDAT, "-ssdump", "ssdump", ffOPTWR },
 +        { efMAP, "-map", "ss",      ffLIBRD },
 +        { efXPM, "-o",   "ss",      ffWRITE },
 +        { efXVG, "-sc",  "scount",  ffWRITE },
 +        { efXPM, "-a",   "area",    ffOPTWR },
 +        { efXVG, "-ta",  "totarea", ffOPTWR },
 +        { efXVG, "-aa",  "averarea", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    parse_common_args(&argc, argv,
 +                      PCA_CAN_TIME | PCA_CAN_VIEW | PCA_TIME_UNIT | PCA_BE_NICE,
 +                      NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv);
 +    fnSCount   = opt2fn("-sc", NFILE, fnm);
 +    fnArea     = opt2fn_null("-a", NFILE, fnm);
 +    fnTArea    = opt2fn_null("-ta", NFILE, fnm);
 +    fnAArea    = opt2fn_null("-aa", NFILE, fnm);
 +    bDoAccSurf = (fnArea || fnTArea || fnAArea);
 +
 +    read_tps_conf(ftp2fn(efTPS, NFILE, fnm), title, &top, &ePBC, &xp, NULL, box, FALSE);
 +    atoms = &(top.atoms);
 +    check_oo(atoms);
 +    bPhbres = bPhobics(atoms);
 +
 +    get_index(atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &gnx, &index, &grpnm);
 +    nres = 0;
 +    nr0  = -1;
 +    for (i = 0; (i < gnx); i++)
 +    {
 +        if (atoms->atom[index[i]].resind != nr0)
 +        {
 +            nr0 = atoms->atom[index[i]].resind;
 +            nres++;
 +        }
 +    }
 +    fprintf(stderr, "There are %d residues in your selected group\n", nres);
 +
 +    strcpy(pdbfile, "ddXXXXXX");
 +    gmx_tmpnam(pdbfile);
 +    if ((tmpf = fopen(pdbfile, "w")) == NULL)
 +    {
 +        sprintf(pdbfile, "%ctmp%cfilterXXXXXX", DIR_SEPARATOR, DIR_SEPARATOR);
 +        gmx_tmpnam(pdbfile);
 +        if ((tmpf = fopen(pdbfile, "w")) == NULL)
 +        {
 +            gmx_fatal(FARGS, "Can not open tmp file %s", pdbfile);
 +        }
 +    }
 +    else
 +    {
 +        fclose(tmpf);
 +    }
 +
 +    strcpy(tmpfile, "ddXXXXXX");
 +    gmx_tmpnam(tmpfile);
 +    if ((tmpf = fopen(tmpfile, "w")) == NULL)
 +    {
 +        sprintf(tmpfile, "%ctmp%cfilterXXXXXX", DIR_SEPARATOR, DIR_SEPARATOR);
 +        gmx_tmpnam(tmpfile);
 +        if ((tmpf = fopen(tmpfile, "w")) == NULL)
 +        {
 +            gmx_fatal(FARGS, "Can not open tmp file %s", tmpfile);
 +        }
 +    }
 +    else
 +    {
 +        fclose(tmpf);
 +    }
 +
 +    if ((dptr = getenv("DSSP")) == NULL)
 +    {
 +        dptr = "/usr/local/bin/dssp";
 +    }
 +    if (!gmx_fexist(dptr))
 +    {
 +        gmx_fatal(FARGS, "DSSP executable (%s) does not exist (use setenv DSSP)",
 +                  dptr);
 +    }
 +    if (dsspVersion >= 2)
 +    {
 +        if (dsspVersion > 2)
 +        {
 +            printf("\nWARNING: You use DSSP version %d, which is not explicitly\nsupported by do_dssp. Assuming version 2 syntax.\n\n", dsspVersion);
 +        }
 +
 +        sprintf(dssp, "%s -i %s -o %s > /dev/null %s",
 +                dptr, pdbfile, tmpfile, bVerbose ? "" : "2> /dev/null");
 +    }
 +    else
 +    {
 +        sprintf(dssp, "%s %s %s %s > /dev/null %s",
 +                dptr, bDoAccSurf ? "" : "-na", pdbfile, tmpfile, bVerbose ? "" : "2> /dev/null");
 +
 +    }
 +    fprintf(stderr, "dssp cmd='%s'\n", dssp);
 +
 +    if (fnTArea)
 +    {
 +        fTArea = xvgropen(fnTArea, "Solvent Accessible Surface Area",
 +                          output_env_get_xvgr_tlabel(oenv), "Area (nm\\S2\\N)", oenv);
 +        xvgr_legend(fTArea, 2, leg, oenv);
 +    }
 +    else
 +    {
 +        fTArea = NULL;
 +    }
 +
 +    mat.map  = NULL;
 +    mat.nmap = getcmap(libopen(opt2fn("-map", NFILE, fnm)),
 +                       opt2fn("-map", NFILE, fnm), &(mat.map));
 +
 +    natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
 +    if (natoms > atoms->nr)
 +    {
 +        gmx_fatal(FARGS, "\nTrajectory does not match topology!");
 +    }
 +    if (gnx > natoms)
 +    {
 +        gmx_fatal(FARGS, "\nTrajectory does not match selected group!");
 +    }
 +
 +    snew(average_area, atoms->nres);
 +    snew(av_area, atoms->nres);
 +    snew(norm_av_area, atoms->nres);
 +    accr  = NULL;
 +    naccr = 0;
 +
 +    gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms, box);
 +    do
 +    {
 +        t = output_env_conv_time(oenv, t);
 +        if (bDoAccSurf && nframe >= naccr)
 +        {
 +            naccr += 10;
 +            srenew(accr, naccr);
 +            for (i = naccr-10; i < naccr; i++)
 +            {
 +                snew(accr[i], 2*atoms->nres-1);
 +            }
 +        }
 +        gmx_rmpbc(gpbc, natoms, box, x);
 +        tapein = ffopen(pdbfile, "w");
 +        write_pdbfile_indexed(tapein, NULL, atoms, x, ePBC, box, ' ', -1, gnx, index, NULL, TRUE);
 +        ffclose(tapein);
 +
 +#ifdef GMX_NO_SYSTEM
 +        printf("Warning-- No calls to system(3) supported on this platform.");
 +        printf("Warning-- Skipping execution of 'system(\"%s\")'.", dssp);
 +        exit(1);
 +#else
 +        if (0 != system(dssp))
 +        {
 +            gmx_fatal(FARGS, "Failed to execute command: %s\n",
 +                      "Try specifying your dssp version with the -ver option.", dssp);
 +        }
 +#endif
 +
 +        /* strip_dssp returns the number of lines found in the dssp file, i.e.
 +         * the number of residues plus the separator lines */
 +
 +        if (bDoAccSurf)
 +        {
 +            accr_ptr = accr[nframe];
 +        }
 +
 +        nres_plus_separators = strip_dssp(tmpfile, nres, bPhbres, t,
 +                                          accr_ptr, fTArea, &mat, average_area, oenv);
 +        remove(tmpfile);
 +        remove(pdbfile);
 +        nframe++;
 +    }
 +    while (read_next_x(oenv, status, &t, natoms, x, box));
 +    fprintf(stderr, "\n");
 +    close_trj(status);
 +    if (fTArea)
 +    {
 +        ffclose(fTArea);
 +    }
 +    gmx_rmpbc_done(gpbc);
 +
 +    prune_ss_legend(&mat);
 +
 +    ss        = opt2FILE("-o", NFILE, fnm, "w");
 +    mat.flags = 0;
 +    write_xpm_m(ss, mat);
 +    ffclose(ss);
 +
 +    if (opt2bSet("-ssdump", NFILE, fnm))
 +    {
 +        ss = opt2FILE("-ssdump", NFILE, fnm, "w");
 +        snew(ss_str, nres+1);
 +        fprintf(ss, "%d\n", nres);
 +        for (j = 0; j < mat.nx; j++)
 +        {
 +            for (i = 0; (i < mat.ny); i++)
 +            {
 +                ss_str[i] = mat.map[mat.matrix[j][i]].code.c1;
 +            }
 +            ss_str[i] = '\0';
 +            fprintf(ss, "%s\n", ss_str);
 +        }
 +        ffclose(ss);
 +        sfree(ss_str);
 +    }
 +    analyse_ss(fnSCount, &mat, ss_string, oenv);
 +
 +    if (bDoAccSurf)
 +    {
 +        write_sas_mat(fnArea, accr, nframe, nres_plus_separators, &mat);
 +
 +        for (i = 0; i < atoms->nres; i++)
 +        {
 +            av_area[i] = (average_area[i] / (real)nframe);
 +        }
 +
 +        norm_acc(atoms, nres, av_area, norm_av_area);
 +
 +        if (fnAArea)
 +        {
 +            acc = xvgropen(fnAArea, "Average Accessible Area",
 +                           "Residue", "A\\S2", oenv);
 +            for (i = 0; (i < nres); i++)
 +            {
 +                fprintf(acc, "%5d  %10g %10g\n", i+1, av_area[i], norm_av_area[i]);
 +            }
 +            ffclose(acc);
 +        }
 +    }
 +
 +    view_all(oenv, NFILE, fnm);
 +
 +    thanx(stderr);
 +
 +    return 0;
 +}
index f873b03e41913d1ab8113ae6208fec54bb31b318,0000000000000000000000000000000000000000..3b6e9700040137164ca850edf314c3b422c26762
mode 100644,000000..100644
--- /dev/null
@@@ -1,726 -1,0 +1,727 @@@
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + * $Id: densorder.c,v 0.9
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.0
 + *
 + * Copyright (c) 1991-2001
 + * BIOSON Research Institute, Dept. of Biophysical Chemistry
 + * University of Groningen, The Netherlands
 + *
 + * 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.
 + *
 + * Do check out http://www.gromacs.org , or mail us at gromacs@gromacs.org .
 + *
 + * And Hey:
 + * Gyas ROwers Mature At Cryogenic Speed
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <ctype.h>
 +
 +#include "sysstuff.h"
 +#include <string.h>
 +#include "typedefs.h"
 +#include "statutil.h"
 +#include "smalloc.h"
 +#include "macros.h"
 +#include "gstat.h"
 +#include "vec.h"
 +#include "xvgr.h"
 +#include "pbc.h"
 +#include "copyrite.h"
 +#include "futil.h"
 +#include "statutil.h"
 +#include "index.h"
 +#include "tpxio.h"
 +#include "matio.h"
 +#include "binsearch.h"
 +#include "powerspect.h"
 +
 +/* Print name of first atom in all groups in index file */
 +static void print_types(atom_id index[], atom_id a[], int ngrps,
 +                        char *groups[], t_topology *top)
 +{
 +    int i;
 +
 +    fprintf(stderr, "Using following groups: \n");
 +    for (i = 0; i < ngrps; i++)
 +    {
 +        fprintf(stderr, "Groupname: %s First atomname: %s First atomnr %u\n",
 +                groups[i], *(top->atoms.atomname[a[index[i]]]), a[index[i]]);
 +    }
 +    fprintf(stderr, "\n");
 +}
 +
 +static void check_length(real length, int a, int b)
 +{
 +    if (length > 0.3)
 +    {
 +        fprintf(stderr, "WARNING: distance between atoms %d and "
 +                "%d > 0.3 nm (%f). Index file might be corrupt.\n",
 +                a, b, length);
 +    }
 +}
 +
 +static void find_tetra_order_grid(t_topology top, int ePBC,
 +                                  int natoms, matrix box,
 +                                  rvec x[], int maxidx, atom_id index[],
 +                                  real time, real *sgmean, real *skmean,
 +                                  int nslicex, int nslicey, int nslicez,
 +                                  real ***sggrid, real ***skgrid)
 +{
 +    int         ix, jx, i, j, k, l, n, *nn[4];
 +    rvec        dx, rj, rk, urk, urj;
 +    real        cost, cost2, *sgmol, *skmol, rmean, rmean2, r2, box2, *r_nn[4];
 +    t_pbc       pbc;
 +    int         slindex_x, slindex_y, slindex_z;
 +    int      ***sl_count;
 +    real        onethird = 1.0/3.0;
 +    gmx_rmpbc_t gpbc;
 +
 +    /*  dmat = init_mat(maxidx, FALSE); */
 +
 +    box2 = box[XX][XX] * box[XX][XX];
 +
 +    /* Initialize expanded sl_count array */
 +    snew(sl_count, nslicex);
 +    for (i = 0; i < nslicex; i++)
 +    {
 +        snew(sl_count[i], nslicey);
 +        for (j = 0; j < nslicey; j++)
 +        {
 +            snew(sl_count[i][j], nslicez);
 +        }
 +    }
 +
 +
 +    for (i = 0; (i < 4); i++)
 +    {
 +        snew(r_nn[i], natoms);
 +        snew(nn[i], natoms);
 +
 +        for (j = 0; (j < natoms); j++)
 +        {
 +            r_nn[i][j] = box2;
 +        }
 +    }
 +
 +    snew(sgmol, maxidx);
 +    snew(skmol, maxidx);
 +
 +    /* Must init pbc every step because of pressure coupling */
++    set_pbc(&pbc,ePBC,box);
 +    gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms, box);
 +    gmx_rmpbc(gpbc, natoms, box, x);
 +
 +    *sgmean = 0.0;
 +    *skmean = 0.0;
 +    l       = 0;
 +    for (i = 0; (i < maxidx); i++)
 +    {   /* loop over index file */
 +        ix = index[i];
 +        for (j = 0; (j < maxidx); j++)
 +        {
 +
 +            if (i == j)
 +            {
 +                continue;
 +            }
 +
 +            jx = index[j];
 +
 +            pbc_dx(&pbc, x[ix], x[jx], dx);
 +            r2 = iprod(dx, dx);
 +
 +            /* set_mat_entry(dmat,i,j,r2); */
 +
 +            /* determine the nearest neighbours */
 +            if (r2 < r_nn[0][i])
 +            {
 +                r_nn[3][i] = r_nn[2][i]; nn[3][i] = nn[2][i];
 +                r_nn[2][i] = r_nn[1][i]; nn[2][i] = nn[1][i];
 +                r_nn[1][i] = r_nn[0][i]; nn[1][i] = nn[0][i];
 +                r_nn[0][i] = r2;         nn[0][i] = j;
 +            }
 +            else if (r2 < r_nn[1][i])
 +            {
 +                r_nn[3][i] = r_nn[2][i]; nn[3][i] = nn[2][i];
 +                r_nn[2][i] = r_nn[1][i]; nn[2][i] = nn[1][i];
 +                r_nn[1][i] = r2;         nn[1][i] = j;
 +            }
 +            else if (r2 < r_nn[2][i])
 +            {
 +                r_nn[3][i] = r_nn[2][i]; nn[3][i] = nn[2][i];
 +                r_nn[2][i] = r2;         nn[2][i] = j;
 +            }
 +            else if (r2 < r_nn[3][i])
 +            {
 +                r_nn[3][i] = r2;         nn[3][i] = j;
 +            }
 +        }
 +
 +
 +        /* calculate mean distance between nearest neighbours */
 +        rmean = 0;
 +        for (j = 0; (j < 4); j++)
 +        {
 +            r_nn[j][i] = sqrt(r_nn[j][i]);
 +            rmean     += r_nn[j][i];
 +        }
 +        rmean /= 4;
 +
 +        n        = 0;
 +        sgmol[i] = 0.0;
 +        skmol[i] = 0.0;
 +
 +        /* Chau1998a eqn 3 */
 +        /* angular part tetrahedrality order parameter per atom */
 +        for (j = 0; (j < 3); j++)
 +        {
 +            for (k = j+1; (k < 4); k++)
 +            {
 +                pbc_dx(&pbc, x[ix], x[index[nn[k][i]]], rk);
 +                pbc_dx(&pbc, x[ix], x[index[nn[j][i]]], rj);
 +
 +                unitv(rk, urk);
 +                unitv(rj, urj);
 +
 +                cost  = iprod(urk, urj) + onethird;
 +                cost2 = cost * cost;
 +
 +                sgmol[i] += cost2;
 +                l++;
 +                n++;
 +            }
 +        }
 +        /* normalize sgmol between 0.0 and 1.0 */
 +        sgmol[i] = 3*sgmol[i]/32;
 +        *sgmean += sgmol[i];
 +
 +        /* distance part tetrahedrality order parameter per atom */
 +        rmean2 = 4 * 3 * rmean * rmean;
 +        for (j = 0; (j < 4); j++)
 +        {
 +            skmol[i] += (rmean - r_nn[j][i]) * (rmean - r_nn[j][i]) / rmean2;
 +            /*      printf("%d %f (%f %f %f %f) \n",
 +                    i, skmol[i], rmean, rmean2, r_nn[j][i], (rmean - r_nn[j][i]) );
 +             */
 +        }
 +
 +        *skmean += skmol[i];
 +
 +        /* Compute sliced stuff in x y z*/
 +        slindex_x = gmx_nint((1+x[i][XX]/box[XX][XX])*nslicex) % nslicex;
 +        slindex_y = gmx_nint((1+x[i][YY]/box[YY][YY])*nslicey) % nslicey;
 +        slindex_z = gmx_nint((1+x[i][ZZ]/box[ZZ][ZZ])*nslicez) % nslicez;
 +        sggrid[slindex_x][slindex_y][slindex_z] += sgmol[i];
 +        skgrid[slindex_x][slindex_y][slindex_z] += skmol[i];
 +        (sl_count[slindex_x][slindex_y][slindex_z])++;
 +    } /* loop over entries in index file */
 +
 +    *sgmean /= maxidx;
 +    *skmean /= maxidx;
 +
 +    for (i = 0; (i < nslicex); i++)
 +    {
 +        for (j = 0; j < nslicey; j++)
 +        {
 +            for (k = 0; k < nslicez; k++)
 +            {
 +                if (sl_count[i][j][k] > 0)
 +                {
 +                    sggrid[i][j][k] /= sl_count[i][j][k];
 +                    skgrid[i][j][k] /= sl_count[i][j][k];
 +                }
 +            }
 +        }
 +    }
 +
 +    sfree(sl_count);
 +    sfree(sgmol);
 +    sfree(skmol);
 +    for (i = 0; (i < 4); i++)
 +    {
 +        sfree(r_nn[i]);
 +        sfree(nn[i]);
 +    }
 +}
 +
 +/*Determines interface from tetrahedral order parameter in box with specified binwidth.  */
 +/*Outputs interface positions(bins), the number of timeframes, and the number of surface-mesh points in xy*/
 +
 +static void calc_tetra_order_interface(const char *fnNDX, const char *fnTPS, const char *fnTRX, real binw, int tblock,
 +                                       int *nframes,  int *nslicex, int *nslicey,
 +                                       real sgang1, real sgang2, real ****intfpos,
 +                                       output_env_t oenv)
 +{
 +    FILE         *fpsg   = NULL, *fpsk = NULL;
 +    char         *sgslfn = "sg_ang_mesh";  /* Hardcoded filenames for debugging*/
 +    char         *skslfn = "sk_dist_mesh";
 +    t_topology    top;
 +    int           ePBC;
 +    char          title[STRLEN], subtitle[STRLEN];
 +    t_trxstatus  *status;
 +    int           natoms;
 +    real          t;
 +    rvec         *xtop, *x;
 +    matrix        box;
 +    real          sg, sk, sgintf, pos;
 +    atom_id     **index   = NULL;
 +    char        **grpname = NULL;
 +    int           i, j, k, n, *isize, ng, nslicez, framenr;
 +    real       ***sg_grid = NULL, ***sk_grid = NULL, ***sg_fravg = NULL, ***sk_fravg = NULL, ****sk_4d = NULL, ****sg_4d = NULL;
 +    int          *perm;
 +    int           ndx1, ndx2;
 +    int           bins;
 +    const real    onehalf = 1.0/2.0;
 +    /* real   ***intfpos[2]; pointers to arrays of two interface positions zcoord(framenr,xbin,ybin): intfpos[interface_index][t][nslicey*x+y]
 +     * i.e 1D Row-major order in (t,x,y) */
 +
 +
 +    read_tps_conf(fnTPS, title, &top, &ePBC, &xtop, NULL, box, FALSE);
 +
 +    *nslicex = (int)(box[XX][XX]/binw + onehalf); /*Calculate slicenr from binwidth*/
 +    *nslicey = (int)(box[YY][YY]/binw + onehalf);
 +    nslicez  = (int)(box[ZZ][ZZ]/binw +  onehalf);
 +
 +
 +
 +    ng = 1;
 +    /* get index groups */
 +    printf("Select the group that contains the atoms you want to use for the tetrahedrality order parameter calculation:\n");
 +    snew(grpname, ng);
 +    snew(index, ng);
 +    snew(isize, ng);
 +    get_index(&top.atoms, fnNDX, ng, isize, index, grpname);
 +
 +    /* Analyze trajectory */
 +    natoms = read_first_x(oenv, &status, fnTRX, &t, &x, box);
 +    if (natoms > top.atoms.nr)
 +    {
 +        gmx_fatal(FARGS, "Topology (%d atoms) does not match trajectory (%d atoms)",
 +                  top.atoms.nr, natoms);
 +    }
 +    check_index(NULL, ng, index[0], NULL, natoms);
 +
 +
 +    /*Prepare structures for temporary storage of frame info*/
 +    snew(sg_grid, *nslicex);
 +    snew(sk_grid, *nslicex);
 +    for (i = 0; i < *nslicex; i++)
 +    {
 +        snew(sg_grid[i], *nslicey);
 +        snew(sk_grid[i], *nslicey);
 +        for (j = 0; j < *nslicey; j++)
 +        {
 +            snew(sg_grid[i][j], nslicez);
 +            snew(sk_grid[i][j], nslicez);
 +        }
 +    }
 +
 +    sg_4d    = NULL;
 +    sk_4d    = NULL;
 +    *nframes = 0;
 +    framenr  = 0;
 +
 +/* Loop over frames*/
 +    do
 +    {
 +        /*Initialize box meshes (temporary storage for each tblock frame -reinitialise every tblock steps */
 +        if (framenr%tblock == 0)
 +        {
 +            srenew(sk_4d, *nframes+1);
 +            srenew(sg_4d, *nframes+1);
 +            snew(sg_fravg, *nslicex);
 +            snew(sk_fravg, *nslicex);
 +            for (i = 0; i < *nslicex; i++)
 +            {
 +                snew(sg_fravg[i], *nslicey);
 +                snew(sk_fravg[i], *nslicey);
 +                for (j = 0; j < *nslicey; j++)
 +                {
 +                    snew(sg_fravg[i][j], nslicez);
 +                    snew(sk_fravg[i][j], nslicez);
 +                }
 +            }
 +        }
 +
 +        find_tetra_order_grid(top, ePBC, natoms, box, x, isize[0], index[0], t,
 +                              &sg, &sk, *nslicex, *nslicey, nslicez, sg_grid, sk_grid);
 +        for (i = 0; i < *nslicex; i++)
 +        {
 +            for (j = 0; j < *nslicey; j++)
 +            {
 +                for (k = 0; k < nslicez; k++)
 +                {
 +                    sk_fravg[i][j][k] += sk_grid[i][j][k]/tblock;
 +                    sg_fravg[i][j][k] += sg_grid[i][j][k]/tblock;
 +                }
 +            }
 +        }
 +
 +        framenr++;
 +
 +        if (framenr%tblock == 0)
 +        {
 +            sk_4d[*nframes] = sk_fravg;
 +            sg_4d[*nframes] = sg_fravg;
 +            (*nframes)++;
 +        }
 +
 +    }
 +    while (read_next_x(oenv, status, &t, natoms, x, box));
 +    close_trj(status);
 +
 +    sfree(grpname);
 +    sfree(index);
 +    sfree(isize);
 +
 +    /*Debugging for printing out the entire order parameter meshes.*/
 +    if (debug)
 +    {
 +        fpsg = xvgropen(sgslfn, "S\\sg\\N Angle Order Parameter / Meshpoint", "(nm)", "S\\sg\\N", oenv);
 +        fpsk = xvgropen(skslfn, "S\\sk\\N Distance Order Parameter / Meshpoint", "(nm)", "S\\sk\\N", oenv);
 +        for (n = 0; n < (*nframes); n++)
 +        {
 +            fprintf(fpsg, "%i\n", n);
 +            fprintf(fpsk, "%i\n", n);
 +            for (i = 0; (i < *nslicex); i++)
 +            {
 +                for (j = 0; j < *nslicey; j++)
 +                {
 +                    for (k = 0; k < nslicez; k++)
 +                    {
 +                        fprintf(fpsg, "%4f  %4f  %4f  %8f\n", (i+0.5)*box[XX][XX]/(*nslicex), (j+0.5)*box[YY][YY]/(*nslicey), (k+0.5)*box[ZZ][ZZ]/nslicez, sg_4d[n][i][j][k]);
 +                        fprintf(fpsk, "%4f  %4f  %4f  %8f\n", (i+0.5)*box[XX][XX]/(*nslicex), (j+0.5)*box[YY][YY]/(*nslicey), (k+0.5)*box[ZZ][ZZ]/nslicez, sk_4d[n][i][j][k]);
 +                    }
 +                }
 +            }
 +        }
 +        xvgrclose(fpsg);
 +        xvgrclose(fpsk);
 +    }
 +
 +
 +    /* Find positions of interface z by scanning orderparam for each frame and for each xy-mesh cylinder along z*/
 +
 +    /*Simple trial: assume interface is in the middle of -sgang1 and sgang2*/
 +    sgintf = 0.5*(sgang1+sgang2);
 +
 +
 +    /*Allocate memory for interface arrays; */
 +    snew((*intfpos), 2);
 +    snew((*intfpos)[0], *nframes);
 +    snew((*intfpos)[1], *nframes);
 +
 +    bins = (*nslicex)*(*nslicey);
 +
 +
 +    snew(perm, nslicez);  /*permutation array for sorting along normal coordinate*/
 +
 +
 +    for (n = 0; n < *nframes; n++)
 +    {
 +        snew((*intfpos)[0][n], bins);
 +        snew((*intfpos)[1][n], bins);
 +        for (i = 0; i < *nslicex; i++)
 +        {
 +            for (j = 0; j < *nslicey; j++)
 +            {
 +                rangeArray(perm, nslicez); /*reset permutation array to identity*/
 +                /*Binsearch returns 2 bin-numbers where the order param is  <= setpoint sgintf*/
 +                ndx1 = start_binsearch(sg_4d[n][i][j], perm, 0, nslicez/2-1, sgintf, 1);
 +                ndx2 = start_binsearch(sg_4d[n][i][j], perm, nslicez/2, nslicez-1, sgintf, -1);
 +                /*Use linear interpolation to smooth out the interface position*/
 +
 +                /*left interface (0)*/
 +                /*if((sg_4d[n][i][j][perm[ndx1+1]]-sg_4d[n][i][j][perm[ndx1]])/sg_4d[n][i][j][perm[ndx1]] > 0.01){
 +                   pos=( (sgintf-sg_4d[n][i][j][perm[ndx1]])*perm[ndx1+1]+(sg_4d[n][i][j][perm[ndx1+1]]-sgintf)*perm[ndx1 ])*/
 +                (*intfpos)[0][n][j+*nslicey*i] = (perm[ndx1]+onehalf)*binw;
 +                /*right interface (1)*/
 +                /*alpha=(sgintf-sg_4d[n][i][j][perm[ndx2]])/(sg_4d[n][i][j][perm[ndx2]+1]-sg_4d[n][i][j][perm[ndx2]]);*/
 +                /*(*intfpos)[1][n][j+*nslicey*i]=((1-alpha)*perm[ndx2]+alpha*(perm[ndx2]+1)+onehalf)*box[ZZ][ZZ]/nslicez;*/
 +                (*intfpos)[1][n][j+*nslicey*i] = (perm[ndx2]+onehalf)*binw;
 +            }
 +        }
 +    }
 +
 +
 +    /*sfree(perm);*/
 +    sfree(sk_4d);
 +    sfree(sg_4d);
 +    /*sfree(sg_grid);*/
 +    /*sfree(sk_grid);*/
 +
 +
 +}
 +
 +static void writesurftoxpms(real ***surf, int tblocks, int xbins, int ybins, real bw, char **outfiles, int maplevels )
 +{
 +
 +    char   numbuf[8];
 +    int    n, i, j;
 +    real **profile1, **profile2;
 +    real   max1, max2, min1, min2, *xticks, *yticks;
 +    t_rgb  lo = {1, 1, 1};
 +    t_rgb  hi = {0, 0, 0};
 +    FILE  *xpmfile1, *xpmfile2;
 +
 +/*Prepare xpm structures for output*/
 +
 +/*Allocate memory to tick's and matrices*/
 +    snew (xticks, xbins+1);
 +    snew (yticks, ybins+1);
 +
 +    profile1 = mk_matrix(xbins, ybins, FALSE);
 +    profile2 = mk_matrix(xbins, ybins, FALSE);
 +
 +    for (i = 0; i < xbins+1; i++)
 +    {
 +        xticks[i] += bw;
 +    }
 +    for (j = 0; j < ybins+1; j++)
 +    {
 +        yticks[j] += bw;
 +    }
 +
 +    xpmfile1 = ffopen(outfiles[0], "w");
 +    xpmfile2 = ffopen(outfiles[1], "w");
 +
 +    max1 = max2 = 0.0;
 +    min1 = min2 = 1000.00;
 +
 +    for (n = 0; n < tblocks; n++)
 +    {
 +        sprintf(numbuf, "%5d", n);
 +        /*Filling matrices for inclusion in xpm-files*/
 +        for (i = 0; i < xbins; i++)
 +        {
 +            for (j = 0; j < ybins; j++)
 +            {
 +                profile1[i][j] = (surf[0][n][j+ybins*i]);
 +                profile2[i][j] = (surf[1][n][j+ybins*i]);
 +                /*Finding max and min values*/
 +                if (profile1[i][j] > max1)
 +                {
 +                    max1 = profile1[i][j];
 +                }
 +                if (profile1[i][j] < min1)
 +                {
 +                    min1 = profile1[i][j];
 +                }
 +                if (profile2[i][j] > max2)
 +                {
 +                    max2 = profile2[i][j];
 +                }
 +                if (profile2[i][j] < min2)
 +                {
 +                    min2 = profile2[i][j];
 +                }
 +            }
 +        }
 +
 +        write_xpm(xpmfile1, 3, numbuf, "Height", "x[nm]", "y[nm]", xbins, ybins, xticks, yticks, profile1, min1, max1, lo, hi, &maplevels);
 +        write_xpm(xpmfile2, 3, numbuf, "Height", "x[nm]", "y[nm]", xbins, ybins, xticks, yticks, profile2, min2, max2, lo, hi, &maplevels);
 +    }
 +
 +    ffclose(xpmfile1);
 +    ffclose(xpmfile2);
 +
 +
 +
 +    sfree(profile1);
 +    sfree(profile2);
 +    sfree(xticks);
 +    sfree(yticks);
 +}
 +
 +
 +static void writeraw(real ***surf, int tblocks, int xbins, int ybins, char **fnms)
 +{
 +    FILE *raw1, *raw2;
 +    int   i, j, n;
 +
 +    raw1 = ffopen(fnms[0], "w");
 +    raw2 = ffopen(fnms[1], "w");
 +    fprintf(raw1, "#Legend\n#TBlock\n#Xbin Ybin Z t\n");
 +    fprintf(raw2, "#Legend\n#TBlock\n#Xbin Ybin Z t\n");
 +    for (n = 0; n < tblocks; n++)
 +    {
 +        fprintf(raw1, "%5d\n", n);
 +        fprintf(raw2, "%5d\n", n);
 +        for (i = 0; i < xbins; i++)
 +        {
 +            for (j = 0; j < ybins; j++)
 +            {
 +                fprintf(raw1, "%i  %i  %8.5f\n", i, j, (surf[0][n][j+ybins*i]));
 +                fprintf(raw2, "%i  %i  %8.5f\n", i, j, (surf[1][n][j+ybins*i]));
 +            }
 +        }
 +    }
 +
 +    ffclose(raw1);
 +    ffclose(raw2);
 +}
 +
 +
 +
 +int gmx_hydorder(int argc, char *argv[])
 +{
 +    static const char *desc[] = {
 +        "g_hydorder computes the tetrahedrality order parameters around a ",
 +        "given atom. Both angle an distance order parameters are calculated. See",
 +        "P.-L. Chau and A.J. Hardwick, Mol. Phys., 93, (1998), 511-518.",
 +        "for more details.[BR]"
 +        "This application calculates the orderparameter in a 3d-mesh in the box, and",
 +        "with 2 phases in the box gives the user the option to define a 2D interface in time",
 +        "separating the faces by specifying parameters -sgang1 and -sgang2 (It is important",
 +        "to select these judiciously)"
 +    };
 +
 +    int                axis      = 0;
 +    static int         nsttblock = 1;
 +    static int         nlevels   = 100;
 +    static real        binwidth  = 1.0; /* binwidth in mesh           */
 +    static real        sg1       = 1;
 +    static real        sg2       = 1;   /* order parameters for bulk phases */
 +    static gmx_bool    bFourier  = FALSE;
 +    static gmx_bool    bRawOut   = FALSE;
 +    int                frames, xslices, yslices; /* Dimensions of interface arrays*/
 +    real            ***intfpos;                  /* Interface arrays (intfnr,t,xy) -potentially large */
 +    static char       *normal_axis[] = { NULL, "z", "x", "y", NULL };
 +
 +    t_pargs            pa[] = {
 +        { "-d",   FALSE, etENUM, {normal_axis},
 +          "Direction of the normal on the membrane" },
 +        { "-bw",  FALSE, etREAL, {&binwidth},
 +          "Binwidth of box mesh" },
 +        { "-sgang1", FALSE, etREAL, {&sg1},
 +          "tetrahedral angle parameter in Phase 1 (bulk)" },
 +        { "-sgang2", FALSE, etREAL, {&sg2},
 +          "tetrahedral angle parameter in Phase 2 (bulk)" },
 +        { "-tblock", FALSE, etINT, {&nsttblock},
 +          "Number of frames in one time-block average"},
 +        { "-nlevel", FALSE, etINT, {&nlevels},
 +          "Number of Height levels in 2D - XPixMaps"}
 +    };
 +
 +    t_filenm           fnm[] = {                      /* files for g_order    */
 +        { efTRX, "-f", NULL,  ffREAD },               /* trajectory file              */
 +        { efNDX, "-n", NULL,  ffREAD },               /* index file           */
 +        { efTPX, "-s", NULL,  ffREAD },               /* topology file                */
 +        { efXPM, "-o", "intf",  ffWRMULT},            /* XPM- surface maps    */
 +        { efOUT, "-or", "raw", ffOPTWRMULT },         /* xvgr output file           */
 +        { efOUT, "-Spect", "intfspect", ffOPTWRMULT}, /* Fourier spectrum interfaces */
 +    };
 +#define NFILE asize(fnm)
 +
 +    /*Filenames*/
 +    const char  *ndxfnm, *tpsfnm, *trxfnm;
 +    char       **spectra, **intfn, **raw;
 +    int          nfspect, nfxpm, nfraw;
 +    output_env_t oenv;
 +
 +    parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                      NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv);
 +    bFourier = opt2bSet("-Spect", NFILE, fnm);
 +    bRawOut  = opt2bSet("-or", NFILE, fnm);
 +
 +    if (binwidth < 0.0)
 +    {
 +        gmx_fatal(FARGS, "Can not have binwidth < 0");
 +    }
 +
 +    ndxfnm = ftp2fn(efNDX, NFILE, fnm);
 +    tpsfnm = ftp2fn(efTPX, NFILE, fnm);
 +    trxfnm = ftp2fn(efTRX, NFILE, fnm);
 +
 +    /* Calculate axis */
 +    if (strcmp(normal_axis[0], "x") == 0)
 +    {
 +        axis = XX;
 +    }
 +    else if (strcmp(normal_axis[0], "y") == 0)
 +    {
 +        axis = YY;
 +    }
 +    else if (strcmp(normal_axis[0], "z") == 0)
 +    {
 +        axis = ZZ;
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "Invalid axis, use x, y or z");
 +    }
 +
 +    switch (axis)
 +    {
 +        case 0:
 +            fprintf(stderr, "Taking x axis as normal to the membrane\n");
 +            break;
 +        case 1:
 +            fprintf(stderr, "Taking y axis as normal to the membrane\n");
 +            break;
 +        case 2:
 +            fprintf(stderr, "Taking z axis as normal to the membrane\n");
 +            break;
 +    }
 +
 +    /* tetraheder order parameter */
 +    /* If either of the options is set we compute both */
 +    nfxpm = opt2fns(&intfn, "-o", NFILE, fnm);
 +    if (nfxpm != 2)
 +    {
 +        gmx_fatal(FARGS, "No or not correct number (2) of output-files: %d", nfxpm);
 +    }
 +    calc_tetra_order_interface(ndxfnm, tpsfnm, trxfnm, binwidth, nsttblock, &frames, &xslices, &yslices, sg1, sg2, &intfpos, oenv);
 +    writesurftoxpms(intfpos, frames, xslices, yslices, binwidth, intfn, nlevels);
 +
 +    if (bFourier)
 +    {
 +        nfspect = opt2fns(&spectra, "-Spect", NFILE, fnm);
 +        if (nfspect != 2)
 +        {
 +            gmx_fatal(FARGS, "No or not correct number (2) of output-files: %d", nfspect);
 +        }
 +        powerspectavg(intfpos, frames, xslices, yslices, spectra);
 +    }
 +
 +    if (bRawOut)
 +    {
 +        nfraw = opt2fns(&raw, "-or", NFILE, fnm);
 +        if (nfraw != 2)
 +        {
 +            gmx_fatal(FARGS, "No or not correct number (2) of output-files: %d", nfraw);
 +        }
 +        writeraw(intfpos, frames, xslices, yslices, raw);
 +    }
 +
 +
 +
 +    thanx(stderr);
 +
 +    return 0;
 +}
index 8555ef4fd66c517f0ff976e43d1d7fa5b1349aaa,0000000000000000000000000000000000000000..e54dc30400518b7884beef533b818d2aa1e95183
mode 100644,000000..100644
--- /dev/null
@@@ -1,984 -1,0 +1,989 @@@
-  *                This source code is part of
 +/*
++ * This file is part of the GROMACS molecular simulation package.
 + *
-  *                 G   R   O   M   A   C   S
-  *
-  *          GROningen MAchine for Chemical Simulations
-  *
-  *                        VERSION 3.2.0
-  *
-  * 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.
-  *
-  * 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
++ * Copyright (c) 2012, by the GROMACS development team, led by
++ * David van der Spoel, Berk Hess, Erik Lindahl, and including many
++ * others, as listed in the AUTHORS file in the top-level source
++ * directory and at http://www.gromacs.org.
 + *
-  * 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.
++ * 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.
 + *
-  * 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.
++ * 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.
 + *
-  * For more info, check our website at http://www.gromacs.org
++ * 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.
 + *
-  * And Hey:
-  * Gromacs Runs One Microsecond At Cannonball Speeds
++ * 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 "statutil.h"
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "smalloc.h"
 +#include "macros.h"
 +#include "gmx_fatal.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "copyrite.h"
 +#include "futil.h"
 +#include "statutil.h"
 +#include "pdbio.h"
 +#include "confio.h"
 +#include "tpxio.h"
 +#include "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[] = {
 +        "[TT]make_edi[tt] generates an essential dynamics (ED) sampling input file to be used with [TT]mdrun[tt]",
 +        "based on eigenvectors of a covariance matrix ([TT]g_covar[tt]) or from a",
 +        "normal modes analysis ([TT]g_nmeig[tt]).",
 +        "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];
 +    static real        radfix   = 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\")"},
 +        { "-radstep", FALSE, etREAL, {&radfix},
 +          "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;
 +    parse_common_args(&argc, argv, 0,
 +                      NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv);
 +
 +    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;
 +                    }
 +                }
 +            }
 +            else if (ev_class == evRADFIX && opt2parg_bSet(evStepOptions[ev_class], NPA, pa))
 +            {
 +                snew(evStepList[ev_class], nvecs);
 +                for (i = 0; i < nvecs; i++)
 +                {
 +                    evStepList[ev_class][i] = radfix;
 +                }
 +            }
 +            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);
 +    thanx(stderr);
 +
 +    return 0;
 +}
index 1cb884fcfdbc73f467c2ead0eba062b7b0448174,0000000000000000000000000000000000000000..0501465c6418351f90872746b5096db66c56644c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1627 -1,0 +1,1630 @@@
-  *                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.
 +/*
++ * This file is part of the GROMACS molecular simulation package.
 + *
-  * 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
 + * 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.
-  * 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.
++ * Copyright (c) 2012, by the GROMACS development team, led by
++ * David van der Spoel, Berk Hess, Erik Lindahl, and including many
++ * others, as listed in the AUTHORS file in the top-level source
++ * directory and at http://www.gromacs.org.
++ *
++ * GROMACS is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public License
++ * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
-  * 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.
++ * 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.
 + *
-  * For more info, check our website at http://www.gromacs.org
++ * 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.
 + *
-  * And Hey:
-  * Green Red Orange Magenta Azure Cyan Skyblue
++ * 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 <ctype.h>
 +#include "sysstuff.h"
 +#include "strdb.h"
 +#include "futil.h"
 +#include "macros.h"
 +#include "string2.h"
 +#include "statutil.h"
 +#include "confio.h"
 +#include "copyrite.h"
 +#include "typedefs.h"
 +#include "index.h"
 +#include "smalloc.h"
 +#include "vec.h"
 +#include "index.h"
 +
 +#define MAXNAMES 30
 +#define NAME_LEN 30
 +
 +gmx_bool bCase = FALSE;
 +
 +static int or_groups(atom_id nr1, atom_id *at1, atom_id nr2, atom_id *at2,
 +                     atom_id *nr, atom_id *at)
 +{
 +    atom_id  i1, i2, max = 0;
 +    gmx_bool bNotIncr;
 +
 +    *nr = 0;
 +
 +    bNotIncr = FALSE;
 +    for (i1 = 0; i1 < nr1; i1++)
 +    {
 +        if ((i1 > 0) && (at1[i1] <= max))
 +        {
 +            bNotIncr = TRUE;
 +        }
 +        max = at1[i1];
 +    }
 +    for (i1 = 0; i1 < nr2; i1++)
 +    {
 +        if ((i1 > 0) && (at2[i1] <= max))
 +        {
 +            bNotIncr = TRUE;
 +        }
 +        max = at2[i1];
 +    }
 +
 +    if (bNotIncr)
 +    {
 +        printf("One of your groups is not ascending\n");
 +    }
 +    else
 +    {
 +        i1  = 0;
 +        i2  = 0;
 +        *nr = 0;
 +        while ((i1 < nr1) || (i2 < nr2))
 +        {
 +            if ((i2 == nr2) || ((i1 < nr1) && (at1[i1] < at2[i2])))
 +            {
 +                at[*nr] = at1[i1];
 +                (*nr)++;
 +                i1++;
 +            }
 +            else
 +            {
 +                if ((i2 < nr2) && ((i1 == nr1) || (at1[i1] > at2[i2])))
 +                {
 +                    at[*nr] = at2[i2];
 +                    (*nr)++;
 +                }
 +                i2++;
 +            }
 +        }
 +
 +        printf("Merged two groups with OR: %u %u -> %u\n", nr1, nr2, *nr);
 +    }
 +
 +    return *nr;
 +}
 +
 +static int and_groups(atom_id nr1, atom_id *at1, atom_id nr2, atom_id *at2,
 +                      atom_id *nr, atom_id *at)
 +{
 +    atom_id i1, i2;
 +
 +    *nr = 0;
 +    for (i1 = 0; i1 < nr1; i1++)
 +    {
 +        for (i2 = 0; i2 < nr2; i2++)
 +        {
 +            if (at1[i1] == at2[i2])
 +            {
 +                at[*nr] = at1[i1];
 +                (*nr)++;
 +            }
 +        }
 +    }
 +
 +    printf("Merged two groups with AND: %u %u -> %u\n", nr1, nr2, *nr);
 +
 +    return *nr;
 +}
 +
 +static gmx_bool is_name_char(char c)
 +{
 +    /* This string should contain all characters that can not be
 +     * the first letter of a name due to the make_ndx syntax.
 +     */
 +    const char *spec = " !&|";
 +
 +    return (c != '\0' && strchr(spec, c) == NULL);
 +}
 +
 +static int parse_names(char **string, int *n_names, char **names)
 +{
 +    int i;
 +
 +    *n_names = 0;
 +    while (is_name_char((*string)[0]) || (*string)[0] == ' ')
 +    {
 +        if (is_name_char((*string)[0]))
 +        {
 +            if (*n_names >= MAXNAMES)
 +            {
 +                gmx_fatal(FARGS, "To many names: %d\n", *n_names+1);
 +            }
 +            i = 0;
 +            while (is_name_char((*string)[i]))
 +            {
 +                names[*n_names][i] = (*string)[i];
 +                i++;
 +                if (i > NAME_LEN)
 +                {
 +                    printf("Name is too long, the maximum is %d characters\n", NAME_LEN);
 +                    return 0;
 +                }
 +            }
 +            names[*n_names][i] = '\0';
 +            if (!bCase)
 +            {
 +                upstring(names[*n_names]);
 +            }
 +            *string += i;
 +            (*n_names)++;
 +        }
 +        else
 +        {
 +            (*string)++;
 +        }
 +    }
 +
 +    return *n_names;
 +}
 +
 +static gmx_bool parse_int_char(char **string, int *nr, char *c)
 +{
 +    char    *orig;
 +    gmx_bool bRet;
 +
 +    orig = *string;
 +
 +    while ((*string)[0] == ' ')
 +    {
 +        (*string)++;
 +    }
 +
 +    bRet = FALSE;
 +
 +    *c = ' ';
 +
 +    if (isdigit((*string)[0]))
 +    {
 +        *nr = (*string)[0]-'0';
 +        (*string)++;
 +        while (isdigit((*string)[0]))
 +        {
 +            *nr = (*nr)*10+(*string)[0]-'0';
 +            (*string)++;
 +        }
 +        if (isalpha((*string)[0]))
 +        {
 +            *c = (*string)[0];
 +            (*string)++;
 +        }
 +        /* Check if there is at most one non-digit character */
 +        if (!isalnum((*string)[0]))
 +        {
 +            bRet = TRUE;
 +        }
 +        else
 +        {
 +            *string = orig;
 +        }
 +    }
 +    else
 +    {
 +        *nr = NOTSET;
 +    }
 +
 +    return bRet;
 +}
 +
 +static gmx_bool parse_int(char **string, int *nr)
 +{
 +    char    *orig, c;
 +    gmx_bool bRet;
 +
 +    orig = *string;
 +    bRet = parse_int_char(string, nr, &c);
 +    if (bRet && c != ' ')
 +    {
 +        *string = orig;
 +        bRet    = FALSE;
 +    }
 +
 +    return bRet;
 +}
 +
 +static gmx_bool isquote(char c)
 +{
 +    return (c == '\"');
 +}
 +
 +static gmx_bool parse_string(char **string, int *nr, int ngrps, char **grpname)
 +{
 +    char *s, *sp;
 +    char  c;
 +
 +    while ((*string)[0] == ' ')
 +    {
 +        (*string)++;
 +    }
 +
 +    (*nr) = NOTSET;
 +    if (isquote((*string)[0]))
 +    {
 +        c = (*string)[0];
 +        (*string)++;
 +        s  = strdup((*string));
 +        sp = strchr(s, c);
 +        if (sp != NULL)
 +        {
 +            (*string) += sp-s + 1;
 +            sp[0]      = '\0';
 +            (*nr)      = find_group(s, ngrps, grpname);
 +        }
 +    }
 +
 +    return (*nr) != NOTSET;
 +}
 +
 +static int select_atomnumbers(char **string, t_atoms *atoms, atom_id n1,
 +                              atom_id *nr, atom_id *index, char *gname)
 +{
 +    char    buf[STRLEN];
 +    int     i, up;
 +
 +    *nr = 0;
 +    while ((*string)[0] == ' ')
 +    {
 +        (*string)++;
 +    }
 +    if ((*string)[0] == '-')
 +    {
 +        (*string)++;
 +        parse_int(string, &up);
 +        if ((n1 < 1) || (n1 > atoms->nr) || (up < 1) || (up > atoms->nr))
 +        {
 +            printf("Invalid atom range\n");
 +        }
 +        else
 +        {
 +            for (i = n1-1; i <= up-1; i++)
 +            {
 +                index[*nr] = i;
 +                (*nr)++;
 +            }
 +            printf("Found %u atom%s in range %u-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
 +            if (n1 == up)
 +            {
 +                sprintf(buf, "a_%u", n1);
 +            }
 +            else
 +            {
 +                sprintf(buf, "a_%u-%d", n1, up);
 +            }
 +            strcpy(gname, buf);
 +        }
 +    }
 +    else
 +    {
 +        i = n1;
 +        sprintf(gname, "a");
 +        do
 +        {
 +            if ((i-1 >= 0) && (i-1 < atoms->nr))
 +            {
 +                index[*nr] = i-1;
 +                (*nr)++;
 +                sprintf(buf, "_%d", i);
 +                strcat(gname, buf);
 +            }
 +            else
 +            {
 +                printf("Invalid atom number %d\n", i);
 +                *nr = 0;
 +            }
 +        }
 +        while ((*nr != 0) && (parse_int(string, &i)));
 +    }
 +
 +    return *nr;
 +}
 +
 +static int select_residuenumbers(char **string, t_atoms *atoms,
 +                                 atom_id n1, char c,
 +                                 atom_id *nr, atom_id *index, char *gname)
 +{
 +    char       buf[STRLEN];
 +    int        i, j, up;
 +    t_resinfo *ri;
 +
 +    *nr = 0;
 +    while ((*string)[0] == ' ')
 +    {
 +        (*string)++;
 +    }
 +    if ((*string)[0] == '-')
 +    {
 +        /* Residue number range selection */
 +        if (c != ' ')
 +        {
 +            printf("Error: residue insertion codes can not be used with residue range selection\n");
 +            return 0;
 +        }
 +        (*string)++;
 +        parse_int(string, &up);
 +
 +        for (i = 0; i < atoms->nr; i++)
 +        {
 +            ri = &atoms->resinfo[atoms->atom[i].resind];
 +            for (j = n1; (j <= up); j++)
 +            {
 +                if (ri->nr == j && (c == ' ' || ri->ic == c))
 +                {
 +                    index[*nr] = i;
 +                    (*nr)++;
 +                }
 +            }
 +        }
 +        printf("Found %u atom%s with res.nr. in range %u-%d\n",
 +               *nr, (*nr == 1) ? "" : "s", n1, up);
 +        if (n1 == up)
 +        {
 +            sprintf(buf, "r_%u", n1);
 +        }
 +        else
 +        {
 +            sprintf(buf, "r_%u-%d", n1, up);
 +        }
 +        strcpy(gname, buf);
 +    }
 +    else
 +    {
 +        /* Individual residue number/insertion code selection */
 +        j = n1;
 +        sprintf(gname, "r");
 +        do
 +        {
 +            for (i = 0; i < atoms->nr; i++)
 +            {
 +                ri = &atoms->resinfo[atoms->atom[i].resind];
 +                if (ri->nr == j && ri->ic == c)
 +                {
 +                    index[*nr] = i;
 +                    (*nr)++;
 +                }
 +            }
 +            sprintf(buf, "_%d", j);
 +            strcat(gname, buf);
 +        }
 +        while (parse_int_char(string, &j, &c));
 +    }
 +
 +    return *nr;
 +}
 +
 +static int select_residueindices(char **string, t_atoms *atoms,
 +                                 atom_id n1, char c,
 +                                 atom_id *nr, atom_id *index, char *gname)
 +{
 +    /*this should be similar to select_residuenumbers except select by index (sequential numbering in file)*/
 +    /*resind+1 for 1-indexing*/
 +    char       buf[STRLEN];
 +    int        i, j, up;
 +    t_resinfo *ri;
 +
 +    *nr = 0;
 +    while ((*string)[0] == ' ')
 +    {
 +        (*string)++;
 +    }
 +    if ((*string)[0] == '-')
 +    {
 +        /* Residue number range selection */
 +        if (c != ' ')
 +        {
 +            printf("Error: residue insertion codes can not be used with residue range selection\n");
 +            return 0;
 +        }
 +        (*string)++;
 +        parse_int(string, &up);
 +
 +        for (i = 0; i < atoms->nr; i++)
 +        {
 +            ri = &atoms->resinfo[atoms->atom[i].resind];
 +            for (j = n1; (j <= up); j++)
 +            {
 +                if (atoms->atom[i].resind+1 == j && (c == ' ' || ri->ic == c))
 +                {
 +                    index[*nr] = i;
 +                    (*nr)++;
 +                }
 +            }
 +        }
 +        printf("Found %u atom%s with resind.+1 in range %u-%d\n",
 +               *nr, (*nr == 1) ? "" : "s", n1, up);
 +        if (n1 == up)
 +        {
 +            sprintf(buf, "r_%u", n1);
 +        }
 +        else
 +        {
 +            sprintf(buf, "r_%u-%d", n1, up);
 +        }
 +        strcpy(gname, buf);
 +    }
 +    else
 +    {
 +        /* Individual residue number/insertion code selection */
 +        j = n1;
 +        sprintf(gname, "r");
 +        do
 +        {
 +            for (i = 0; i < atoms->nr; i++)
 +            {
 +                ri = &atoms->resinfo[atoms->atom[i].resind];
 +                if (atoms->atom[i].resind+1 == j && ri->ic == c)
 +                {
 +                    index[*nr] = i;
 +                    (*nr)++;
 +                }
 +            }
 +            sprintf(buf, "_%d", j);
 +            strcat(gname, buf);
 +        }
 +        while (parse_int_char(string, &j, &c));
 +    }
 +
 +    return *nr;
 +}
 +
 +
 +static gmx_bool atoms_from_residuenumbers(t_atoms *atoms, int group, t_blocka *block,
 +                                          atom_id *nr, atom_id *index, char *gname)
 +{
 +    int i, j, j0, j1, resnr, nres;
 +
 +    j0   = block->index[group];
 +    j1   = block->index[group+1];
 +    nres = atoms->nres;
 +    for (j = j0; j < j1; j++)
 +    {
 +        if (block->a[j] >= nres)
 +        {
 +            printf("Index %s contains number>nres (%d>%d)\n",
 +                   gname, block->a[j]+1, nres);
 +            return FALSE;
 +        }
 +    }
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        resnr = atoms->resinfo[atoms->atom[i].resind].nr;
 +        for (j = j0; j < j1; j++)
 +        {
 +            if (block->a[j]+1 == resnr)
 +            {
 +                index[*nr] = i;
 +                (*nr)++;
 +                break;
 +            }
 +        }
 +    }
 +    printf("Found %u atom%s in %d residues from group %s\n",
 +           *nr, (*nr == 1) ? "" : "s", j1-j0, gname);
 +    return *nr;
 +}
 +
 +static gmx_bool comp_name(char *name, char *search)
 +{
 +    while (name[0] != '\0' && search[0] != '\0')
 +    {
 +        switch (search[0])
 +        {
 +            case '?':
 +                /* Always matches */
 +                break;
 +            case '*':
 +                if (search[1] != '\0')
 +                {
 +                    printf("WARNING: Currently '*' is only supported at the end of an expression\n");
 +                    return FALSE;
 +                }
 +                else
 +                {
 +                    return TRUE;
 +                }
 +                break;
 +            default:
 +                /* Compare a single character */
 +                if (( bCase && strncmp(name, search, 1)) ||
 +                    (!bCase && gmx_strncasecmp(name, search, 1)))
 +                {
 +                    return FALSE;
 +                }
 +        }
 +        name++;
 +        search++;
 +    }
 +
 +    return (name[0] == '\0' && (search[0] == '\0' || search[0] == '*'));
 +}
 +
 +static int select_chainnames(t_atoms *atoms, int n_names, char **names,
 +                             atom_id *nr, atom_id *index)
 +{
 +    char    name[2];
 +    int     j;
 +    atom_id i;
 +
 +    name[1] = 0;
 +    *nr     = 0;
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        name[0] = atoms->resinfo[atoms->atom[i].resind].chainid;
 +        j       = 0;
 +        while (j < n_names && !comp_name(name, names[j]))
 +        {
 +            j++;
 +        }
 +        if (j < n_names)
 +        {
 +            index[*nr] = i;
 +            (*nr)++;
 +        }
 +    }
 +    printf("Found %u atom%s with chain identifier%s",
 +           *nr, (*nr == 1) ? "" : "s", (n_names == 1) ? "" : "s");
 +    for (j = 0; (j < n_names); j++)
 +    {
 +        printf(" %s", names[j]);
 +    }
 +    printf("\n");
 +
 +    return *nr;
 +}
 +
 +static int select_atomnames(t_atoms *atoms, int n_names, char **names,
 +                            atom_id *nr, atom_id *index, gmx_bool bType)
 +{
 +    char   *name;
 +    int     j;
 +    atom_id i;
 +
 +    *nr = 0;
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        if (bType)
 +        {
 +            name = *(atoms->atomtype[i]);
 +        }
 +        else
 +        {
 +            name = *(atoms->atomname[i]);
 +        }
 +        j = 0;
 +        while (j < n_names && !comp_name(name, names[j]))
 +        {
 +            j++;
 +        }
 +        if (j < n_names)
 +        {
 +            index[*nr] = i;
 +            (*nr)++;
 +        }
 +    }
 +    printf("Found %u atoms with %s%s",
 +           *nr, bType ? "type" : "name", (n_names == 1) ? "" : "s");
 +    for (j = 0; (j < n_names); j++)
 +    {
 +        printf(" %s", names[j]);
 +    }
 +    printf("\n");
 +
 +    return *nr;
 +}
 +
 +static int select_residuenames(t_atoms *atoms, int n_names, char **names,
 +                               atom_id *nr, atom_id *index)
 +{
 +    char   *name;
 +    int     j;
 +    atom_id i;
 +
 +    *nr = 0;
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        name = *(atoms->resinfo[atoms->atom[i].resind].name);
 +        j    = 0;
 +        while (j < n_names && !comp_name(name, names[j]))
 +        {
 +            j++;
 +        }
 +        if (j < n_names)
 +        {
 +            index[*nr] = i;
 +            (*nr)++;
 +        }
 +    }
 +    printf("Found %u atoms with residue name%s", *nr, (n_names == 1) ? "" : "s");
 +    for (j = 0; (j < n_names); j++)
 +    {
 +        printf(" %s", names[j]);
 +    }
 +    printf("\n");
 +
 +    return *nr;
 +}
 +
 +static void copy2block(int n, atom_id *index, t_blocka *block)
 +{
 +    int i, n0;
 +
 +    block->nr++;
 +    n0         = block->nra;
 +    block->nra = n0+n;
 +    srenew(block->index, block->nr+1);
 +    block->index[block->nr] = n0+n;
 +    srenew(block->a, n0+n);
 +    for (i = 0; (i < n); i++)
 +    {
 +        block->a[n0+i] = index[i];
 +    }
 +}
 +
 +static void make_gname(int n, char **names, char *gname)
 +{
 +    int i;
 +
 +    strcpy(gname, names[0]);
 +    for (i = 1; i < n; i++)
 +    {
 +        strcat(gname, "_");
 +        strcat(gname, names[i]);
 +    }
 +}
 +
 +static void copy_group(int g, t_blocka *block, atom_id *nr, atom_id *index)
 +{
 +    int i, i0;
 +
 +    i0  = block->index[g];
 +    *nr = block->index[g+1]-i0;
 +    for (i = 0; i < *nr; i++)
 +    {
 +        index[i] = block->a[i0+i];
 +    }
 +}
 +
 +static void remove_group(int nr, int nr2, t_blocka *block, char ***gn)
 +{
 +    int   i, j, shift;
 +    char *name;
 +
 +    if (nr2 == NOTSET)
 +    {
 +        nr2 = nr;
 +    }
 +
 +    for (j = 0; j <= nr2-nr; j++)
 +    {
 +        if ((nr < 0) || (nr >= block->nr))
 +        {
 +            printf("Group %d does not exist\n", nr+j);
 +        }
 +        else
 +        {
 +            shift = block->index[nr+1]-block->index[nr];
 +            for (i = block->index[nr+1]; i < block->nra; i++)
 +            {
 +                block->a[i-shift] = block->a[i];
 +            }
 +
 +            for (i = nr; i < block->nr; i++)
 +            {
 +                block->index[i] = block->index[i+1]-shift;
 +            }
 +            name = strdup((*gn)[nr]);
 +            sfree((*gn)[nr]);
 +            for (i = nr; i < block->nr-1; i++)
 +            {
 +                (*gn)[i] = (*gn)[i+1];
 +            }
 +            block->nr--;
 +            block->nra = block->index[block->nr];
 +            printf("Removed group %d '%s'\n", nr+j, name);
 +            sfree(name);
 +        }
 +    }
 +}
 +
 +static void split_group(t_atoms *atoms, int sel_nr, t_blocka *block, char ***gn,
 +                        gmx_bool bAtom)
 +{
 +    char    buf[STRLEN], *name;
 +    int     i, resind;
 +    atom_id a, n0, n1;
 +
 +    printf("Splitting group %d '%s' into %s\n", sel_nr, (*gn)[sel_nr],
 +           bAtom ? "atoms" : "residues");
 +
 +    n0 = block->index[sel_nr];
 +    n1 = block->index[sel_nr+1];
 +    srenew(block->a, block->nra+n1-n0);
 +    for (i = n0; i < n1; i++)
 +    {
 +        a      = block->a[i];
 +        resind = atoms->atom[a].resind;
 +        name   = *(atoms->resinfo[resind].name);
 +        if (bAtom || (i == n0) || (atoms->atom[block->a[i-1]].resind != resind))
 +        {
 +            if (i > n0)
 +            {
 +                block->index[block->nr] = block->nra;
 +            }
 +            block->nr++;
 +            srenew(block->index, block->nr+1);
 +            srenew(*gn, block->nr);
 +            if (bAtom)
 +            {
 +                sprintf(buf, "%s_%s_%u", (*gn)[sel_nr], *atoms->atomname[a], a+1);
 +            }
 +            else
 +            {
 +                sprintf(buf, "%s_%s_%d", (*gn)[sel_nr], name, atoms->resinfo[resind].nr);
 +            }
 +            (*gn)[block->nr-1] = strdup(buf);
 +        }
 +        block->a[block->nra] = a;
 +        block->nra++;
 +    }
 +    block->index[block->nr] = block->nra;
 +}
 +
 +static int split_chain(t_atoms *atoms, rvec *x,
 +                       int sel_nr, t_blocka *block, char ***gn)
 +{
 +    char    buf[STRLEN];
 +    int     j, nchain;
 +    atom_id i, a, natoms, *start = NULL, *end = NULL, ca_start, ca_end;
 +    rvec    vec;
 +
 +    natoms   = atoms->nr;
 +    nchain   = 0;
 +    ca_start = 0;
 +
 +    while (ca_start < natoms)
 +    {
 +        while ((ca_start < natoms) && strcmp(*atoms->atomname[ca_start], "CA"))
 +        {
 +            ca_start++;
 +        }
 +        if (ca_start < natoms)
 +        {
 +            srenew(start, nchain+1);
 +            srenew(end, nchain+1);
 +            start[nchain] = ca_start;
 +            while ((start[nchain] > 0) &&
 +                   (atoms->atom[start[nchain]-1].resind ==
 +                    atoms->atom[ca_start].resind))
 +            {
 +                start[nchain]--;
 +            }
 +
 +            i = ca_start;
 +            do
 +            {
 +                ca_end = i;
 +                do
 +                {
 +                    i++;
 +                }
 +                while ((i < natoms) && strcmp(*atoms->atomname[i], "CA"));
 +                if (i < natoms)
 +                {
 +                    rvec_sub(x[ca_end], x[i], vec);
 +                }
 +            }
 +            while ((i < natoms) && (norm(vec) < 0.45));
 +
 +            end[nchain] = ca_end;
 +            while ((end[nchain]+1 < natoms) &&
 +                   (atoms->atom[end[nchain]+1].resind == atoms->atom[ca_end].resind))
 +            {
 +                end[nchain]++;
 +            }
 +            ca_start = end[nchain]+1;
 +            nchain++;
 +        }
 +    }
 +    if (nchain == 1)
 +    {
 +        printf("Found 1 chain, will not split\n");
 +    }
 +    else
 +    {
 +        printf("Found %d chains\n", nchain);
 +    }
 +    for (j = 0; j < nchain; j++)
 +    {
 +        printf("%d:%6u atoms (%u to %u)\n",
 +               j+1, end[j]-start[j]+1, start[j]+1, end[j]+1);
 +    }
 +
 +    if (nchain > 1)
 +    {
 +        srenew(block->a, block->nra+block->index[sel_nr+1]-block->index[sel_nr]);
 +        for (j = 0; j < nchain; j++)
 +        {
 +            block->nr++;
 +            srenew(block->index, block->nr+1);
 +            srenew(*gn, block->nr);
 +            sprintf(buf, "%s_chain%d", (*gn)[sel_nr], j+1);
 +            (*gn)[block->nr-1] = strdup(buf);
 +            for (i = block->index[sel_nr]; i < block->index[sel_nr+1]; i++)
 +            {
 +                a = block->a[i];
 +                if ((a >= start[j]) && (a <= end[j]))
 +                {
 +                    block->a[block->nra] = a;
 +                    block->nra++;
 +                }
 +            }
 +            block->index[block->nr] = block->nra;
 +            if (block->index[block->nr-1] == block->index[block->nr])
 +            {
 +                remove_group(block->nr-1, NOTSET, block, gn);
 +            }
 +        }
 +    }
 +    sfree(start);
 +    sfree(end);
 +
 +    return nchain;
 +}
 +
 +static gmx_bool check_have_atoms(t_atoms *atoms, char *string)
 +{
 +    if (atoms == NULL)
 +    {
 +        printf("Can not process '%s' without atom info, use option -f\n", string);
 +        return FALSE;
 +    }
 +    else
 +    {
 +        return TRUE;
 +    }
 +}
 +
 +static gmx_bool parse_entry(char **string, int natoms, t_atoms *atoms,
 +                            t_blocka *block, char ***gn,
 +                            atom_id *nr, atom_id *index, char *gname)
 +{
 +    static char   **names, *ostring;
 +    static gmx_bool bFirst = TRUE;
 +    int             j, n_names, sel_nr1;
 +    atom_id         i, nr1, *index1;
 +    char            c;
 +    gmx_bool        bRet, bCompl;
 +
 +    if (bFirst)
 +    {
 +        bFirst = FALSE;
 +        snew(names, MAXNAMES);
 +        for (i = 0; i < MAXNAMES; i++)
 +        {
 +            snew(names[i], NAME_LEN+1);
 +        }
 +    }
 +
 +    bRet    = FALSE;
 +    sel_nr1 = NOTSET;
 +
 +    while (*string[0] == ' ')
 +    {
 +        (*string)++;
 +    }
 +
 +    if ((*string)[0] == '!')
 +    {
 +        bCompl = TRUE;
 +        (*string)++;
 +        while (*string[0] == ' ')
 +        {
 +            (*string)++;
 +        }
 +    }
 +    else
 +    {
 +        bCompl = FALSE;
 +    }
 +
 +    ostring = *string;
 +
 +    if (parse_int(string, &sel_nr1) ||
 +        parse_string(string, &sel_nr1, block->nr, *gn))
 +    {
 +        if ((sel_nr1 >= 0) && (sel_nr1 < block->nr))
 +        {
 +            copy_group(sel_nr1, block, nr, index);
 +            strcpy(gname, (*gn)[sel_nr1]);
 +            printf("Copied index group %d '%s'\n", sel_nr1, (*gn)[sel_nr1]);
 +            bRet = TRUE;
 +        }
 +        else
 +        {
 +            printf("Group %d does not exist\n", sel_nr1);
 +        }
 +    }
 +    else if ((*string)[0] == 'a')
 +    {
 +        (*string)++;
 +        if (check_have_atoms(atoms, ostring))
 +        {
 +            if (parse_int(string, &sel_nr1))
 +            {
 +                bRet = select_atomnumbers(string, atoms, sel_nr1, nr, index, gname);
 +            }
 +            else if (parse_names(string, &n_names, names))
 +            {
 +                bRet = select_atomnames(atoms, n_names, names, nr, index, FALSE);
 +                make_gname(n_names, names, gname);
 +            }
 +        }
 +    }
 +    else if ((*string)[0] == 't')
 +    {
 +        (*string)++;
 +        if (check_have_atoms(atoms, ostring) &&
 +            parse_names(string, &n_names, names))
 +        {
 +            if (atoms->atomtype == NULL)
 +            {
 +                printf("Need a run input file to select atom types\n");
 +            }
 +            else
 +            {
 +                bRet = select_atomnames(atoms, n_names, names, nr, index, TRUE);
 +                make_gname(n_names, names, gname);
 +            }
 +        }
 +    }
 +    else if (strncmp(*string, "res", 3) == 0)
 +    {
 +        (*string) += 3;
 +        if (check_have_atoms(atoms, ostring) &&
 +            parse_int(string, &sel_nr1) &&
 +            (sel_nr1 >= 0) && (sel_nr1 < block->nr) )
 +        {
 +            bRet = atoms_from_residuenumbers(atoms,
 +                                             sel_nr1, block, nr, index, (*gn)[sel_nr1]);
 +            sprintf(gname, "atom_%s", (*gn)[sel_nr1]);
 +        }
 +    }
 +    else if (strncmp(*string, "ri", 2) == 0)
 +    {
 +        (*string) += 2;
 +        if (check_have_atoms(atoms, ostring) &&
 +            parse_int_char(string, &sel_nr1, &c))
 +        {
 +            bRet = select_residueindices(string, atoms, sel_nr1, c, nr, index, gname);
 +        }
 +    }
 +    else if ((*string)[0] == 'r')
 +    {
 +        (*string)++;
 +        if (check_have_atoms(atoms, ostring))
 +        {
 +            if (parse_int_char(string, &sel_nr1, &c))
 +            {
 +                bRet = select_residuenumbers(string, atoms, sel_nr1, c, nr, index, gname);
 +            }
 +            else if (parse_names(string, &n_names, names))
 +            {
 +                bRet = select_residuenames(atoms, n_names, names, nr, index);
 +                make_gname(n_names, names, gname);
 +            }
 +        }
 +    }
 +    else if (strncmp(*string, "chain", 5) == 0)
 +    {
 +        (*string) += 5;
 +        if (check_have_atoms(atoms, ostring) &&
 +            parse_names(string, &n_names, names))
 +        {
 +            bRet = select_chainnames(atoms, n_names, names, nr, index);
 +            sprintf(gname, "ch%s", names[0]);
 +            for (i = 1; i < n_names; i++)
 +            {
 +                strcat(gname, names[i]);
 +            }
 +        }
 +    }
 +    if (bRet && bCompl)
 +    {
 +        snew(index1, natoms-*nr);
 +        nr1 = 0;
 +        for (i = 0; i < natoms; i++)
 +        {
 +            j = 0;
 +            while ((j < *nr) && (index[j] != i))
 +            {
 +                j++;
 +            }
 +            if (j == *nr)
 +            {
 +                if (nr1 >= natoms-*nr)
 +                {
 +                    printf("There are double atoms in your index group\n");
 +                    break;
 +                }
 +                index1[nr1] = i;
 +                nr1++;
 +            }
 +        }
 +        *nr = nr1;
 +        for (i = 0; i < nr1; i++)
 +        {
 +            index[i] = index1[i];
 +        }
 +        sfree(index1);
 +
 +        for (i = strlen(gname)+1; i > 0; i--)
 +        {
 +            gname[i] = gname[i-1];
 +        }
 +        gname[0] = '!';
 +        printf("Complemented group: %u atoms\n", *nr);
 +    }
 +
 +    return bRet;
 +}
 +
 +static void list_residues(t_atoms *atoms)
 +{
 +    int      i, j, start, end, prev_resind, resind;
 +    gmx_bool bDiff;
 +
 +    /* Print all the residues, assuming continuous resnr count */
 +    start       = atoms->atom[0].resind;
 +    prev_resind = start;
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        resind = atoms->atom[i].resind;
 +        if ((resind != prev_resind) || (i == atoms->nr-1))
 +        {
 +            if ((bDiff = strcmp(*atoms->resinfo[resind].name,
 +                                *atoms->resinfo[start].name)) ||
 +                (i == atoms->nr-1))
 +            {
 +                if (bDiff)
 +                {
 +                    end = prev_resind;
 +                }
 +                else
 +                {
 +                    end = resind;
 +                }
 +                if (end < start+3)
 +                {
 +                    for (j = start; j <= end; j++)
 +                    {
 +                        printf("%4d %-5s",
 +                               j+1, *(atoms->resinfo[j].name));
 +                    }
 +                }
 +                else
 +                {
 +                    printf(" %4d - %4d %-5s  ",
 +                           start+1, end+1, *(atoms->resinfo[start].name));
 +                }
 +                start = resind;
 +            }
 +        }
 +        prev_resind = resind;
 +    }
 +    printf("\n");
 +}
 +
 +static void edit_index(int natoms, t_atoms *atoms, rvec *x, t_blocka *block, char ***gn, gmx_bool bVerbose)
 +{
 +    static char   **atnames, *ostring;
 +    static gmx_bool bFirst = TRUE;
 +    char            inp_string[STRLEN], *string;
 +    char            gname[STRLEN], gname1[STRLEN], gname2[STRLEN];
 +    int             i, i0, i1, sel_nr, sel_nr2, newgroup;
 +    atom_id         nr, nr1, nr2, *index, *index1, *index2;
 +    gmx_bool        bAnd, bOr, bPrintOnce;
 +
 +    if (bFirst)
 +    {
 +        bFirst = FALSE;
 +        snew(atnames, MAXNAMES);
 +        for (i = 0; i < MAXNAMES; i++)
 +        {
 +            snew(atnames[i], NAME_LEN+1);
 +        }
 +    }
 +
 +    string = NULL;
 +
 +    snew(index, natoms);
 +    snew(index1, natoms);
 +    snew(index2, natoms);
 +
 +    newgroup   = NOTSET;
 +    bPrintOnce = TRUE;
 +    do
 +    {
 +        gname1[0] = '\0';
 +        if (bVerbose || bPrintOnce || newgroup != NOTSET)
 +        {
 +            printf("\n");
 +            if (bVerbose || bPrintOnce || newgroup == NOTSET)
 +            {
 +                i0 = 0;
 +                i1 = block->nr;
 +            }
 +            else
 +            {
 +                i0 = newgroup;
 +                i1 = newgroup+1;
 +            }
 +            for (i = i0; i < i1; i++)
 +            {
 +                printf("%3d %-20s: %5u atoms\n", i, (*gn)[i],
 +                       block->index[i+1]-block->index[i]);
 +            }
 +            newgroup = NOTSET;
 +        }
 +        if (bVerbose || bPrintOnce)
 +        {
 +            printf("\n");
 +            printf(" nr : group       !   'name' nr name   'splitch' nr    Enter: list groups\n");
 +            printf(" 'a': atom        &   'del' nr         'splitres' nr   'l': list residues\n");
 +            printf(" 't': atom type   |   'keep' nr        'splitat' nr    'h': help\n");
 +            printf(" 'r': residue         'res' nr         'chain' char\n");
 +            printf(" \"name\": group        'case': case %s         'q': save and quit\n",
 +                   bCase ? "insensitive" : "sensitive  ");
 +            printf(" 'ri': residue index\n");
 +            bPrintOnce = FALSE;
 +        }
 +        printf("\n");
 +        printf("> ");
 +        if (NULL == fgets(inp_string, STRLEN, stdin))
 +        {
 +            gmx_fatal(FARGS, "Error reading user input");
 +        }
 +        inp_string[strlen(inp_string)-1] = 0;
 +        printf("\n");
 +        string = inp_string;
 +        while (string[0] == ' ')
 +        {
 +            string++;
 +        }
 +
 +        ostring = string;
 +        nr      = 0;
 +        if (string[0] == 'h')
 +        {
 +            printf(" nr                : selects an index group by number or quoted string.\n");
 +            printf("                     The string is first matched against the whole group name,\n");
 +            printf("                     then against the beginning and finally against an\n");
 +            printf("                     arbitrary substring. A multiple match is an error.\n");
 +
 +            printf(" 'a' nr1 [nr2 ...] : selects atoms, atom numbering starts at 1.\n");
 +            printf(" 'a' nr1 - nr2     : selects atoms in the range from nr1 to nr2.\n");
 +            printf(" 'a' name1[*] [name2[*] ...] : selects atoms by name(s), '?' matches any char,\n");
 +            printf("                               wildcard '*' allowed at the end of a name.\n");
 +            printf(" 't' type1[*] [type2[*] ...] : as 'a', but for type, run input file required.\n");
 +            printf(" 'r' nr1[ic1] [nr2[ic2] ...] : selects residues by number and insertion code.\n");
 +            printf(" 'r' nr1 - nr2               : selects residues in the range from nr1 to nr2.\n");
 +            printf(" 'r' name1[*] [name2[*] ...] : as 'a', but for residue names.\n");
 +            printf(" 'ri' nr1 - nr2              : selects residue indices, 1-indexed, (as opposed to numbers) in the range from nr1 to nr2.\n");
 +            printf(" 'chain' ch1 [ch2 ...]       : selects atoms by chain identifier(s),\n");
 +            printf("                               not available with a .gro file as input.\n");
 +            printf(" !                 : takes the complement of a group with respect to all\n");
 +            printf("                     the atoms in the input file.\n");
 +            printf(" & |               : AND and OR, can be placed between any of the options\n");
 +            printf("                     above, the input is processed from left to right.\n");
 +            printf(" 'name' nr name    : rename group nr to name.\n");
 +            printf(" 'del' nr1 [- nr2] : deletes one group or groups in the range from nr1 to nr2.\n");
 +            printf(" 'keep' nr         : deletes all groups except nr.\n");
 +            printf(" 'case'            : make all name compares case (in)sensitive.\n");
 +            printf(" 'splitch' nr      : split group into chains using CA distances.\n");
 +            printf(" 'splitres' nr     : split group into residues.\n");
 +            printf(" 'splitat' nr      : split group into atoms.\n");
 +            printf(" 'res' nr          : interpret numbers in group as residue numbers\n");
 +            printf(" Enter             : list the currently defined groups and commands\n");
 +            printf(" 'l'               : list the residues.\n");
 +            printf(" 'h'               : show this help.\n");
 +            printf(" 'q'               : save and quit.\n");
 +            printf("\n");
 +            printf(" Examples:\n");
 +            printf(" > 2 | 4 & r 3-5\n");
 +            printf(" selects all atoms from group 2 and 4 that have residue numbers 3, 4 or 5\n");
 +            printf(" > a C* & !a C CA\n");
 +            printf(" selects all atoms starting with 'C' but not the atoms 'C' and 'CA'\n");
 +            printf(" > \"protein\" & ! \"backb\"\n");
 +            printf(" selects all atoms that are in group 'protein' and not in group 'backbone'\n");
 +            if (bVerbose)
 +            {
 +                printf("\npress Enter ");
 +                getchar();
 +            }
 +        }
 +        else if (strncmp(string, "del", 3) == 0)
 +        {
 +            string += 3;
 +            if (parse_int(&string, &sel_nr))
 +            {
 +                while (string[0] == ' ')
 +                {
 +                    string++;
 +                }
 +                if (string[0] == '-')
 +                {
 +                    string++;
 +                    parse_int(&string, &sel_nr2);
 +                }
 +                else
 +                {
 +                    sel_nr2 = NOTSET;
 +                }
 +                while (string[0] == ' ')
 +                {
 +                    string++;
 +                }
 +                if (string[0] == '\0')
 +                {
 +                    remove_group(sel_nr, sel_nr2, block, gn);
 +                }
 +                else
 +                {
 +                    printf("\nSyntax error: \"%s\"\n", string);
 +                }
 +            }
 +        }
 +        else if (strncmp(string, "keep", 4) == 0)
 +        {
 +            string += 4;
 +            if (parse_int(&string, &sel_nr))
 +            {
 +                remove_group(sel_nr+1, block->nr-1, block, gn);
 +                remove_group(0, sel_nr-1, block, gn);
 +            }
 +        }
 +        else if (strncmp(string, "name", 4) == 0)
 +        {
 +            string += 4;
 +            if (parse_int(&string, &sel_nr))
 +            {
 +                if ((sel_nr >= 0) && (sel_nr < block->nr))
 +                {
 +                    sscanf(string, "%s", gname);
 +                    sfree((*gn)[sel_nr]);
 +                    (*gn)[sel_nr] = strdup(gname);
 +                }
 +            }
 +        }
 +        else if (strncmp(string, "case", 4) == 0)
 +        {
 +            bCase = !bCase;
 +            printf("Switched to case %s\n", bCase ? "sensitive" : "insensitive");
 +        }
 +        else if (string[0] == 'v')
 +        {
 +            bVerbose = !bVerbose;
 +            printf("Turned verbose %s\n", bVerbose ? "on" : "off");
 +        }
 +        else if (string[0] == 'l')
 +        {
 +            if (check_have_atoms(atoms, ostring) )
 +            {
 +                list_residues(atoms);
 +            }
 +        }
 +        else if (strncmp(string, "splitch", 7) == 0)
 +        {
 +            string += 7;
 +            if (check_have_atoms(atoms, ostring) &&
 +                parse_int(&string, &sel_nr) &&
 +                (sel_nr >= 0) && (sel_nr < block->nr))
 +            {
 +                split_chain(atoms, x, sel_nr, block, gn);
 +            }
 +        }
 +        else if (strncmp(string, "splitres", 8) == 0)
 +        {
 +            string += 8;
 +            if (check_have_atoms(atoms, ostring) &&
 +                parse_int(&string, &sel_nr) &&
 +                (sel_nr >= 0) && (sel_nr < block->nr))
 +            {
 +                split_group(atoms, sel_nr, block, gn, FALSE);
 +            }
 +        }
 +        else if (strncmp(string, "splitat", 7) == 0)
 +        {
 +            string += 7;
 +            if (check_have_atoms(atoms, ostring) &&
 +                parse_int(&string, &sel_nr) &&
 +                (sel_nr >= 0) && (sel_nr < block->nr))
 +            {
 +                split_group(atoms, sel_nr, block, gn, TRUE);
 +            }
 +        }
 +        else if (string[0] == '\0')
 +        {
 +            bPrintOnce = TRUE;
 +        }
 +        else if (string[0] != 'q')
 +        {
 +            nr1 = -1;
 +            nr2 = -1;
 +            if (parse_entry(&string, natoms, atoms, block, gn, &nr, index, gname))
 +            {
 +                do
 +                {
 +                    while (string[0] == ' ')
 +                    {
 +                        string++;
 +                    }
 +
 +                    bAnd = FALSE;
 +                    bOr  = FALSE;
 +                    if (string[0] == '&')
 +                    {
 +                        bAnd = TRUE;
 +                    }
 +                    else if (string[0] == '|')
 +                    {
 +                        bOr = TRUE;
 +                    }
 +
 +                    if (bAnd || bOr)
 +                    {
 +                        string++;
 +                        nr1 = nr;
 +                        for (i = 0; i < nr; i++)
 +                        {
 +                            index1[i] = index[i];
 +                        }
 +                        strcpy(gname1, gname);
 +                        if (parse_entry(&string, natoms, atoms, block, gn, &nr2, index2, gname2))
 +                        {
 +                            if (bOr)
 +                            {
 +                                or_groups(nr1, index1, nr2, index2, &nr, index);
 +                                sprintf(gname, "%s_%s", gname1, gname2);
 +                            }
 +                            else
 +                            {
 +                                and_groups(nr1, index1, nr2, index2, &nr, index);
 +                                sprintf(gname, "%s_&_%s", gname1, gname2);
 +                            }
 +                        }
 +                    }
 +                }
 +                while (bAnd || bOr);
 +            }
 +            while (string[0] == ' ')
 +            {
 +                string++;
 +            }
 +            if (string[0])
 +            {
 +                printf("\nSyntax error: \"%s\"\n", string);
 +            }
 +            else if (nr > 0)
 +            {
 +                copy2block(nr, index, block);
 +                srenew(*gn, block->nr);
 +                newgroup        = block->nr-1;
 +                (*gn)[newgroup] = strdup(gname);
 +            }
 +            else
 +            {
 +                printf("Group is empty\n");
 +            }
 +        }
 +    }
 +    while (string[0] != 'q');
 +
 +    sfree(index);
 +    sfree(index1);
 +    sfree(index2);
 +}
 +
 +static int block2natoms(t_blocka *block)
 +{
 +    int i, natoms;
 +
 +    natoms = 0;
 +    for (i = 0; i < block->nra; i++)
 +    {
 +        natoms = max(natoms, block->a[i]);
 +    }
 +    natoms++;
 +
 +    return natoms;
 +}
 +
 +void merge_blocks(t_blocka *dest, t_blocka *source)
 +{
 +    int     i, nra0, i0;
 +
 +    /* count groups, srenew and fill */
 +    i0        = dest->nr;
 +    nra0      = dest->nra;
 +    dest->nr += source->nr;
 +    srenew(dest->index, dest->nr+1);
 +    for (i = 0; i < source->nr; i++)
 +    {
 +        dest->index[i0+i] = nra0 + source->index[i];
 +    }
 +    /* count atoms, srenew and fill */
 +    dest->nra += source->nra;
 +    srenew(dest->a, dest->nra);
 +    for (i = 0; i < source->nra; i++)
 +    {
 +        dest->a[nra0+i] = source->a[i];
 +    }
 +
 +    /* terminate list */
 +    dest->index[dest->nr] = dest->nra;
 +
 +}
 +
 +int gmx_make_ndx(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "Index groups are necessary for almost every gromacs program.",
 +        "All these programs can generate default index groups. You ONLY",
 +        "have to use [TT]make_ndx[tt] when you need SPECIAL index groups.",
 +        "There is a default index group for the whole system, 9 default",
 +        "index groups for proteins, and a default index group",
 +        "is generated for every other residue name.[PAR]",
 +        "When no index file is supplied, also [TT]make_ndx[tt] will generate the",
 +        "default groups.",
 +        "With the index editor you can select on atom, residue and chain names",
 +        "and numbers.",
 +        "When a run input file is supplied you can also select on atom type.",
 +        "You can use NOT, AND and OR, you can split groups",
 +        "into chains, residues or atoms. You can delete and rename groups.[PAR]",
 +        "The atom numbering in the editor and the index file starts at 1."
 +    };
 +
 +    static int      natoms   = 0;
 +    static gmx_bool bVerbose = FALSE;
 +    t_pargs         pa[]     = {
 +        { "-natoms",  FALSE, etINT, {&natoms},
 +          "set number of atoms (default: read from coordinate or index file)" },
 +        { "-verbose", FALSE, etBOOL, {&bVerbose},
 +          "HIDDENVerbose output" }
 +    };
 +#define NPA asize(pa)
 +
 +    output_env_t oenv;
 +    char         title[STRLEN];
 +    int          nndxin;
 +    const char  *stxfile;
 +    char       **ndxinfiles;
 +    const char  *ndxoutfile;
 +    gmx_bool     bNatoms;
 +    int          i, j;
 +    t_atoms     *atoms;
 +    rvec        *x, *v;
 +    int          ePBC;
 +    matrix       box;
 +    t_blocka    *block, *block2;
 +    char       **gnames, **gnames2;
 +    t_filenm     fnm[] = {
 +        { efSTX, "-f", NULL,     ffOPTRD  },
 +        { efNDX, "-n", NULL,     ffOPTRDMULT },
 +        { efNDX, "-o", NULL,     ffWRITE }
 +    };
 +#define NFILE asize(fnm)
 +
 +    parse_common_args(&argc, argv, 0, NFILE, fnm, NPA, pa, asize(desc), desc,
 +                      0, NULL, &oenv);
 +
 +    stxfile = ftp2fn_null(efSTX, NFILE, fnm);
 +    if (opt2bSet("-n", NFILE, fnm))
 +    {
 +        nndxin = opt2fns(&ndxinfiles, "-n", NFILE, fnm);
 +    }
 +    else
 +    {
 +        nndxin = 0;
 +    }
 +    ndxoutfile = opt2fn("-o", NFILE, fnm);
 +    bNatoms    = opt2parg_bSet("-natoms", NPA, pa);
 +
 +    if (!stxfile && !nndxin)
 +    {
 +        gmx_fatal(FARGS, "No input files (structure or index)");
 +    }
 +
 +    if (stxfile)
 +    {
 +        snew(atoms, 1);
 +        get_stx_coordnum(stxfile, &(atoms->nr));
 +        init_t_atoms(atoms, atoms->nr, TRUE);
 +        snew(x, atoms->nr);
 +        snew(v, atoms->nr);
 +        fprintf(stderr, "\nReading structure file\n");
 +        read_stx_conf(stxfile, title, atoms, x, v, &ePBC, box);
 +        natoms  = atoms->nr;
 +        bNatoms = TRUE;
 +    }
 +    else
 +    {
 +        atoms = NULL;
 +        x     = NULL;
 +    }
 +
 +    /* read input file(s) */
 +    block  = new_blocka();
 +    gnames = NULL;
 +    printf("Going to read %d old index file(s)\n", nndxin);
 +    if (nndxin)
 +    {
 +        for (i = 0; i < nndxin; i++)
 +        {
 +            block2 = init_index(ndxinfiles[i], &gnames2);
 +            srenew(gnames, block->nr+block2->nr);
 +            for (j = 0; j < block2->nr; j++)
 +            {
 +                gnames[block->nr+j] = gnames2[j];
 +            }
 +            sfree(gnames2);
 +            merge_blocks(block, block2);
 +            sfree(block2->a);
 +            sfree(block2->index);
 +/*       done_block(block2); */
 +            sfree(block2);
 +        }
 +    }
 +    else
 +    {
 +        snew(gnames, 1);
 +        analyse(atoms, block, &gnames, FALSE, TRUE);
 +    }
 +
 +    if (!bNatoms)
 +    {
 +        natoms = block2natoms(block);
 +        printf("Counted atom numbers up to %d in index file\n", natoms);
 +    }
 +
 +    edit_index(natoms, atoms, x, block, &gnames, bVerbose);
 +
 +    write_index(ndxoutfile, block, gnames);
 +
 +    thanx(stderr);
 +
 +    return 0;
 +}
index 9070b9864cae107b79303dfd55b30f28b8e66b3e,0000000000000000000000000000000000000000..f8342a7e5fb33fc3d44202161c2515618a72abbc
mode 100644,000000..100644
--- /dev/null
@@@ -1,797 -1,0 +1,797 @@@
-     sqr_box = sqr(min(box[XX][XX], min(box[YY][YY], box[ZZ][ZZ])));
 +/*
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Green Red Orange Magenta Azure Cyan Skyblue
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <stdlib.h>
 +
 +#include "sysstuff.h"
 +#include <string.h>
 +#include "typedefs.h"
 +#include "smalloc.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "xvgr.h"
 +#include "pbc.h"
 +#include "copyrite.h"
 +#include "futil.h"
 +#include "statutil.h"
 +#include "index.h"
 +#include "tpxio.h"
 +#include "rmpbc.h"
 +#include "xtcio.h"
 +#include "gmx_ana.h"
 +
 +
 +static void periodic_dist(matrix box, rvec x[], int n, atom_id index[],
 +                          real *rmin, real *rmax, int *min_ind)
 +{
 +#define NSHIFT 26
 +    int  sx, sy, sz, i, j, s;
 +    real sqr_box, r2min, r2max, r2;
 +    rvec shift[NSHIFT], d0, d;
 +
++    sqr_box = sqr(min(norm(box[XX]), min(norm(box[YY]), norm(box[ZZ]))));
 +
 +    s = 0;
 +    for (sz = -1; sz <= 1; sz++)
 +    {
 +        for (sy = -1; sy <= 1; sy++)
 +        {
 +            for (sx = -1; sx <= 1; sx++)
 +            {
 +                if (sx != 0 || sy != 0 || sz != 0)
 +                {
 +                    for (i = 0; i < DIM; i++)
 +                    {
 +                        shift[s][i] = sx*box[XX][i]+sy*box[YY][i]+sz*box[ZZ][i];
 +                    }
 +                    s++;
 +                }
 +            }
 +        }
 +    }
 +
 +    r2min = sqr_box;
 +    r2max = 0;
 +
 +    for (i = 0; i < n; i++)
 +    {
 +        for (j = i+1; j < n; j++)
 +        {
 +            rvec_sub(x[index[i]], x[index[j]], d0);
 +            r2 = norm2(d0);
 +            if (r2 > r2max)
 +            {
 +                r2max = r2;
 +            }
 +            for (s = 0; s < NSHIFT; s++)
 +            {
 +                rvec_add(d0, shift[s], d);
 +                r2 = norm2(d);
 +                if (r2 < r2min)
 +                {
 +                    r2min      = r2;
 +                    min_ind[0] = i;
 +                    min_ind[1] = j;
 +                }
 +            }
 +        }
 +    }
 +
 +    *rmin = sqrt(r2min);
 +    *rmax = sqrt(r2max);
 +}
 +
 +static void periodic_mindist_plot(const char *trxfn, const char *outfn,
 +                                  t_topology *top, int ePBC,
 +                                  int n, atom_id index[], gmx_bool bSplit,
 +                                  const output_env_t oenv)
 +{
 +    FILE        *out;
 +    const char  *leg[5] = { "min per.", "max int.", "box1", "box2", "box3" };
 +    t_trxstatus *status;
 +    real         t;
 +    rvec        *x;
 +    matrix       box;
 +    int          natoms, ind_min[2] = {0, 0}, ind_mini = 0, ind_minj = 0;
 +    real         r, rmin, rmax, rmint, tmint;
 +    gmx_bool     bFirst;
 +    gmx_rmpbc_t  gpbc = NULL;
 +
 +    natoms = read_first_x(oenv, &status, trxfn, &t, &x, box);
 +
 +    check_index(NULL, n, index, NULL, natoms);
 +
 +    out = xvgropen(outfn, "Minimum distance to periodic image",
 +                   output_env_get_time_label(oenv), "Distance (nm)", oenv);
 +    if (output_env_get_print_xvgr_codes(oenv))
 +    {
 +        fprintf(out, "@ subtitle \"and maximum internal distance\"\n");
 +    }
 +    xvgr_legend(out, 5, leg, oenv);
 +
 +    rmint = box[XX][XX];
 +    tmint = 0;
 +
 +    if (NULL != top)
 +    {
 +        gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms, box);
 +    }
 +
 +    bFirst = TRUE;
 +    do
 +    {
 +        if (NULL != top)
 +        {
 +            gmx_rmpbc(gpbc, natoms, box, x);
 +        }
 +
 +        periodic_dist(box, x, n, index, &rmin, &rmax, ind_min);
 +        if (rmin < rmint)
 +        {
 +            rmint    = rmin;
 +            tmint    = t;
 +            ind_mini = ind_min[0];
 +            ind_minj = ind_min[1];
 +        }
 +        if (bSplit && !bFirst && abs(t/output_env_get_time_factor(oenv)) < 1e-5)
 +        {
 +            fprintf(out, "&\n");
 +        }
 +        fprintf(out, "\t%g\t%6.3f %6.3f %6.3f %6.3f %6.3f\n",
 +                output_env_conv_time(oenv, t), rmin, rmax, norm(box[0]), norm(box[1]), norm(box[2]));
 +        bFirst = FALSE;
 +    }
 +    while (read_next_x(oenv, status, &t, natoms, x, box));
 +
 +    if (NULL != top)
 +    {
 +        gmx_rmpbc_done(gpbc);
 +    }
 +
 +    ffclose(out);
 +
 +    fprintf(stdout,
 +            "\nThe shortest periodic distance is %g (nm) at time %g (%s),\n"
 +            "between atoms %d and %d\n",
 +            rmint, output_env_conv_time(oenv, tmint), output_env_get_time_unit(oenv),
 +            index[ind_mini]+1, index[ind_minj]+1);
 +}
 +
 +static void calc_dist(real rcut, gmx_bool bPBC, int ePBC, matrix box, rvec x[],
 +                      int nx1, int nx2, atom_id index1[], atom_id index2[],
 +                      gmx_bool bGroup,
 +                      real *rmin, real *rmax, int *nmin, int *nmax,
 +                      int *ixmin, int *jxmin, int *ixmax, int *jxmax)
 +{
 +    int      i, j, i0 = 0, j1;
 +    int      ix, jx;
 +    atom_id *index3;
 +    rvec     dx;
 +    real     r2, rmin2, rmax2, rcut2;
 +    t_pbc    pbc;
 +    int      nmin_j, nmax_j;
 +
 +    *ixmin = -1;
 +    *jxmin = -1;
 +    *ixmax = -1;
 +    *jxmax = -1;
 +    *nmin  = 0;
 +    *nmax  = 0;
 +
 +    rcut2 = sqr(rcut);
 +
 +    /* Must init pbc every step because of pressure coupling */
 +    if (bPBC)
 +    {
 +        set_pbc(&pbc, ePBC, box);
 +    }
 +    if (index2)
 +    {
 +        i0     = 0;
 +        j1     = nx2;
 +        index3 = index2;
 +    }
 +    else
 +    {
 +        j1     = nx1;
 +        index3 = index1;
 +    }
 +
 +    rmin2 = 1e12;
 +    rmax2 = -1e12;
 +
 +    for (j = 0; (j < j1); j++)
 +    {
 +        jx = index3[j];
 +        if (index2 == NULL)
 +        {
 +            i0 = j + 1;
 +        }
 +        nmin_j = 0;
 +        nmax_j = 0;
 +        for (i = i0; (i < nx1); i++)
 +        {
 +            ix = index1[i];
 +            if (ix != jx)
 +            {
 +                if (bPBC)
 +                {
 +                    pbc_dx(&pbc, x[ix], x[jx], dx);
 +                }
 +                else
 +                {
 +                    rvec_sub(x[ix], x[jx], dx);
 +                }
 +                r2 = iprod(dx, dx);
 +                if (r2 < rmin2)
 +                {
 +                    rmin2  = r2;
 +                    *ixmin = ix;
 +                    *jxmin = jx;
 +                }
 +                if (r2 > rmax2)
 +                {
 +                    rmax2  = r2;
 +                    *ixmax = ix;
 +                    *jxmax = jx;
 +                }
 +                if (r2 <= rcut2)
 +                {
 +                    nmin_j++;
 +                }
 +                else if (r2 > rcut2)
 +                {
 +                    nmax_j++;
 +                }
 +            }
 +        }
 +        if (bGroup)
 +        {
 +            if (nmin_j > 0)
 +            {
 +                (*nmin)++;
 +            }
 +            if (nmax_j > 0)
 +            {
 +                (*nmax)++;
 +            }
 +        }
 +        else
 +        {
 +            *nmin += nmin_j;
 +            *nmax += nmax_j;
 +        }
 +    }
 +    *rmin = sqrt(rmin2);
 +    *rmax = sqrt(rmax2);
 +}
 +
 +void dist_plot(const char *fn, const char *afile, const char *dfile,
 +               const char *nfile, const char *rfile, const char *xfile,
 +               real rcut, gmx_bool bMat, t_atoms *atoms,
 +               int ng, atom_id *index[], int gnx[], char *grpn[], gmx_bool bSplit,
 +               gmx_bool bMin, int nres, atom_id *residue, gmx_bool bPBC, int ePBC,
 +               gmx_bool bGroup, gmx_bool bEachResEachTime, gmx_bool bPrintResName,
 +               const output_env_t oenv)
 +{
 +    FILE            *atm, *dist, *num;
 +    t_trxstatus     *trxout;
 +    char             buf[256];
 +    char           **leg;
 +    real             t, dmin, dmax, **mindres = NULL, **maxdres = NULL;
 +    int              nmin, nmax;
 +    t_trxstatus     *status;
 +    int              i = -1, j, k, natoms;
 +    int              min1, min2, max1, max2, min1r, min2r, max1r, max2r;
 +    atom_id          oindex[2];
 +    rvec            *x0;
 +    matrix           box;
 +    t_trxframe       frout;
 +    gmx_bool         bFirst;
 +    FILE            *respertime = NULL;
 +
 +    if ((natoms = read_first_x(oenv, &status, fn, &t, &x0, box)) == 0)
 +    {
 +        gmx_fatal(FARGS, "Could not read coordinates from statusfile\n");
 +    }
 +
 +    sprintf(buf, "%simum Distance", bMin ? "Min" : "Max");
 +    dist = xvgropen(dfile, buf, output_env_get_time_label(oenv), "Distance (nm)", oenv);
 +    sprintf(buf, "Number of Contacts %s %g nm", bMin ? "<" : ">", rcut);
 +    num    = nfile ? xvgropen(nfile, buf, output_env_get_time_label(oenv), "Number", oenv) : NULL;
 +    atm    = afile ? ffopen(afile, "w") : NULL;
 +    trxout = xfile ? open_trx(xfile, "w") : NULL;
 +
 +    if (bMat)
 +    {
 +        if (ng == 1)
 +        {
 +            snew(leg, 1);
 +            sprintf(buf, "Internal in %s", grpn[0]);
 +            leg[0] = strdup(buf);
 +            xvgr_legend(dist, 0, (const char**)leg, oenv);
 +            if (num)
 +            {
 +                xvgr_legend(num, 0, (const char**)leg, oenv);
 +            }
 +        }
 +        else
 +        {
 +            snew(leg, (ng*(ng-1))/2);
 +            for (i = j = 0; (i < ng-1); i++)
 +            {
 +                for (k = i+1; (k < ng); k++, j++)
 +                {
 +                    sprintf(buf, "%s-%s", grpn[i], grpn[k]);
 +                    leg[j] = strdup(buf);
 +                }
 +            }
 +            xvgr_legend(dist, j, (const char**)leg, oenv);
 +            if (num)
 +            {
 +                xvgr_legend(num, j, (const char**)leg, oenv);
 +            }
 +        }
 +    }
 +    else
 +    {
 +        snew(leg, ng-1);
 +        for (i = 0; (i < ng-1); i++)
 +        {
 +            sprintf(buf, "%s-%s", grpn[0], grpn[i+1]);
 +            leg[i] = strdup(buf);
 +        }
 +        xvgr_legend(dist, ng-1, (const char**)leg, oenv);
 +        if (num)
 +        {
 +            xvgr_legend(num, ng-1, (const char**)leg, oenv);
 +        }
 +    }
 +
 +    if (bEachResEachTime)
 +    {
 +        sprintf(buf, "%simum Distance", bMin ? "Min" : "Max");
 +        respertime = xvgropen(rfile, buf, output_env_get_time_label(oenv), "Distance (nm)", oenv);
 +        xvgr_legend(respertime, ng-1, (const char**)leg, oenv);
 +        if (bPrintResName)
 +        {
 +            fprintf(respertime, "# ");
 +        }
 +        for (j = 0; j < nres; j++)
 +        {
 +            fprintf(respertime, "%s%d ", *(atoms->resinfo[atoms->atom[index[0][residue[j]]].resind].name), atoms->atom[index[0][residue[j]]].resind);
 +        }
 +        fprintf(respertime, "\n");
 +
 +    }
 +
 +    j = 0;
 +    if (nres)
 +    {
 +        snew(mindres, ng-1);
 +        snew(maxdres, ng-1);
 +        for (i = 1; i < ng; i++)
 +        {
 +            snew(mindres[i-1], nres);
 +            snew(maxdres[i-1], nres);
 +            for (j = 0; j < nres; j++)
 +            {
 +                mindres[i-1][j] = 1e6;
 +            }
 +            /* maxdres[*][*] is already 0 */
 +        }
 +    }
 +    bFirst = TRUE;
 +    do
 +    {
 +        if (bSplit && !bFirst && abs(t/output_env_get_time_factor(oenv)) < 1e-5)
 +        {
 +            fprintf(dist, "&\n");
 +            if (num)
 +            {
 +                fprintf(num, "&\n");
 +            }
 +            if (atm)
 +            {
 +                fprintf(atm, "&\n");
 +            }
 +        }
 +        fprintf(dist, "%12e", output_env_conv_time(oenv, t));
 +        if (num)
 +        {
 +            fprintf(num, "%12e", output_env_conv_time(oenv, t));
 +        }
 +
 +        if (bMat)
 +        {
 +            if (ng == 1)
 +            {
 +                calc_dist(rcut, bPBC, ePBC, box, x0, gnx[0], gnx[0], index[0], index[0], bGroup,
 +                          &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
 +                fprintf(dist, "  %12e", bMin ? dmin : dmax);
 +                if (num)
 +                {
 +                    fprintf(num, "  %8d", bMin ? nmin : nmax);
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; (i < ng-1); i++)
 +                {
 +                    for (k = i+1; (k < ng); k++)
 +                    {
 +                        calc_dist(rcut, bPBC, ePBC, box, x0, gnx[i], gnx[k], index[i], index[k],
 +                                  bGroup, &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
 +                        fprintf(dist, "  %12e", bMin ? dmin : dmax);
 +                        if (num)
 +                        {
 +                            fprintf(num, "  %8d", bMin ? nmin : nmax);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        else
 +        {
 +            for (i = 1; (i < ng); i++)
 +            {
 +                calc_dist(rcut, bPBC, ePBC, box, x0, gnx[0], gnx[i], index[0], index[i], bGroup,
 +                          &dmin, &dmax, &nmin, &nmax, &min1, &min2, &max1, &max2);
 +                fprintf(dist, "  %12e", bMin ? dmin : dmax);
 +                if (num)
 +                {
 +                    fprintf(num, "  %8d", bMin ? nmin : nmax);
 +                }
 +                if (nres)
 +                {
 +                    for (j = 0; j < nres; j++)
 +                    {
 +                        calc_dist(rcut, bPBC, ePBC, box, x0, residue[j+1]-residue[j], gnx[i],
 +                                  &(index[0][residue[j]]), index[i], bGroup,
 +                                  &dmin, &dmax, &nmin, &nmax, &min1r, &min2r, &max1r, &max2r);
 +                        mindres[i-1][j] = min(mindres[i-1][j], dmin);
 +                        maxdres[i-1][j] = max(maxdres[i-1][j], dmax);
 +                    }
 +                }
 +            }
 +        }
 +        fprintf(dist, "\n");
 +        if (num)
 +        {
 +            fprintf(num, "\n");
 +        }
 +        if ( (bMin ? min1 : max1) != -1)
 +        {
 +            if (atm)
 +            {
 +                fprintf(atm, "%12e  %12d  %12d\n",
 +                        output_env_conv_time(oenv, t), 1+(bMin ? min1 : max1),
 +                        1+(bMin ? min2 : max2));
 +            }
 +        }
 +
 +        if (trxout)
 +        {
 +            oindex[0] = bMin ? min1 : max1;
 +            oindex[1] = bMin ? min2 : max2;
 +            write_trx(trxout, 2, oindex, atoms, i, t, box, x0, NULL, NULL);
 +        }
 +        bFirst = FALSE;
 +        /*dmin should be minimum distance for residue and group*/
 +        if (bEachResEachTime)
 +        {
 +            fprintf(respertime, "%12e", t);
 +            for (i = 1; i < ng; i++)
 +            {
 +                for (j = 0; j < nres; j++)
 +                {
 +                    fprintf(respertime, " %7g", bMin ? mindres[i-1][j] : maxdres[i-1][j]);
 +                    /*reset distances for next time point*/
 +                    mindres[i-1][j] = 1e6;
 +                    maxdres[i-1][j] = 0;
 +                }
 +            }
 +            fprintf(respertime, "\n");
 +        }
 +    }
 +    while (read_next_x(oenv, status, &t, natoms, x0, box));
 +
 +    close_trj(status);
 +    ffclose(dist);
 +    if (num)
 +    {
 +        ffclose(num);
 +    }
 +    if (atm)
 +    {
 +        ffclose(atm);
 +    }
 +    if (trxout)
 +    {
 +        close_trx(trxout);
 +    }
 +
 +    if (nres && !bEachResEachTime)
 +    {
 +        FILE *res;
 +
 +        sprintf(buf, "%simum Distance", bMin ? "Min" : "Max");
 +        res = xvgropen(rfile, buf, "Residue (#)", "Distance (nm)", oenv);
 +        xvgr_legend(res, ng-1, (const char**)leg, oenv);
 +        for (j = 0; j < nres; j++)
 +        {
 +            fprintf(res, "%4d", j+1);
 +            for (i = 1; i < ng; i++)
 +            {
 +                fprintf(res, " %7g", bMin ? mindres[i-1][j] : maxdres[i-1][j]);
 +            }
 +            fprintf(res, "\n");
 +        }
 +    }
 +
 +    sfree(x0);
 +}
 +
 +int find_residues(t_atoms *atoms, int n, atom_id index[], atom_id **resindex)
 +{
 +    int  i;
 +    int  nres = 0, resnr, presnr;
 +    int *residx;
 +
 +    /* build index of first atom numbers for each residue */
 +    presnr = NOTSET;
 +    snew(residx, atoms->nres+1);
 +    for (i = 0; i < n; i++)
 +    {
 +        resnr = atoms->atom[index[i]].resind;
 +        if (resnr != presnr)
 +        {
 +            residx[nres] = i;
 +            nres++;
 +            presnr = resnr;
 +        }
 +    }
 +    if (debug)
 +    {
 +        printf("Found %d residues out of %d (%d/%d atoms)\n",
 +               nres, atoms->nres, atoms->nr, n);
 +    }
 +    srenew(residx, nres+1);
 +    /* mark end of last residue */
 +    residx[nres] = n;
 +    *resindex    = residx;
 +    return nres;
 +}
 +
 +void dump_res(FILE *out, int nres, atom_id *resindex, int n, atom_id index[])
 +{
 +    int i, j;
 +
 +    for (i = 0; i < nres-1; i++)
 +    {
 +        fprintf(out, "Res %d (%d):", i, resindex[i+1]-resindex[i]);
 +        for (j = resindex[i]; j < resindex[i+1]; j++)
 +        {
 +            fprintf(out, " %d(%d)", j, index[j]);
 +        }
 +        fprintf(out, "\n");
 +    }
 +}
 +
 +int gmx_mindist(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "[TT]g_mindist[tt] computes the distance between one group and a number of",
 +        "other groups. Both the minimum distance",
 +        "(between any pair of atoms from the respective groups)",
 +        "and the number of contacts within a given",
 +        "distance are written to two separate output files.",
 +        "With the [TT]-group[tt] option a contact of an atom in another group",
 +        "with multiple atoms in the first group is counted as one contact",
 +        "instead of as multiple contacts.",
 +        "With [TT]-or[tt], minimum distances to each residue in the first",
 +        "group are determined and plotted as a function of residue number.[PAR]",
 +        "With option [TT]-pi[tt] the minimum distance of a group to its",
 +        "periodic image is plotted. This is useful for checking if a protein",
 +        "has seen its periodic image during a simulation. Only one shift in",
 +        "each direction is considered, giving a total of 26 shifts.",
 +        "It also plots the maximum distance within the group and the lengths",
 +        "of the three box vectors.[PAR]",
 +        "Other programs that calculate distances are [TT]g_dist[tt]",
 +        "and [TT]g_bond[tt]."
 +    };
 +    const char     *bugs[] = {
 +        "The [TT]-pi[tt] option is very slow."
 +    };
 +
 +    static gmx_bool bMat             = FALSE, bPI = FALSE, bSplit = FALSE, bMax = FALSE, bPBC = TRUE;
 +    static gmx_bool bGroup           = FALSE;
 +    static real     rcutoff          = 0.6;
 +    static int      ng               = 1;
 +    static gmx_bool bEachResEachTime = FALSE, bPrintResName = FALSE;
 +    t_pargs         pa[]             = {
 +        { "-matrix", FALSE, etBOOL, {&bMat},
 +          "Calculate half a matrix of group-group distances" },
 +        { "-max",    FALSE, etBOOL, {&bMax},
 +          "Calculate *maximum* distance instead of minimum" },
 +        { "-d",      FALSE, etREAL, {&rcutoff},
 +          "Distance for contacts" },
 +        { "-group",      FALSE, etBOOL, {&bGroup},
 +          "Count contacts with multiple atoms in the first group as one" },
 +        { "-pi",     FALSE, etBOOL, {&bPI},
 +          "Calculate minimum distance with periodic images" },
 +        { "-split",  FALSE, etBOOL, {&bSplit},
 +          "Split graph where time is zero" },
 +        { "-ng",       FALSE, etINT, {&ng},
 +          "Number of secondary groups to compute distance to a central group" },
 +        { "-pbc",    FALSE, etBOOL, {&bPBC},
 +          "Take periodic boundary conditions into account" },
 +        { "-respertime",  FALSE, etBOOL, {&bEachResEachTime},
 +          "When writing per-residue distances, write distance for each time point" },
 +        { "-printresname",  FALSE, etBOOL, {&bPrintResName},
 +          "Write residue names" }
 +    };
 +    output_env_t    oenv;
 +    t_topology     *top  = NULL;
 +    int             ePBC = -1;
 +    char            title[256];
 +    real            t;
 +    rvec           *x;
 +    matrix          box;
 +    gmx_bool        bTop = FALSE;
 +
 +    FILE           *atm;
 +    int             i, j, nres = 0;
 +    const char     *trxfnm, *tpsfnm, *ndxfnm, *distfnm, *numfnm, *atmfnm, *oxfnm, *resfnm;
 +    char          **grpname;
 +    int            *gnx;
 +    atom_id       **index, *residues = NULL;
 +    t_filenm        fnm[] = {
 +        { efTRX, "-f",  NULL,      ffREAD },
 +        { efTPS,  NULL, NULL,      ffOPTRD },
 +        { efNDX,  NULL, NULL,      ffOPTRD },
 +        { efXVG, "-od", "mindist",  ffWRITE },
 +        { efXVG, "-on", "numcont",  ffOPTWR },
 +        { efOUT, "-o", "atm-pair", ffOPTWR },
 +        { efTRO, "-ox", "mindist",  ffOPTWR },
 +        { efXVG, "-or", "mindistres", ffOPTWR }
 +    };
 +#define NFILE asize(fnm)
 +
 +    parse_common_args(&argc, argv,
 +                      PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
 +                      NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv);
 +
 +    trxfnm  = ftp2fn(efTRX, NFILE, fnm);
 +    ndxfnm  = ftp2fn_null(efNDX, NFILE, fnm);
 +    distfnm = opt2fn("-od", NFILE, fnm);
 +    numfnm  = opt2fn_null("-on", NFILE, fnm);
 +    atmfnm  = ftp2fn_null(efOUT, NFILE, fnm);
 +    oxfnm   = opt2fn_null("-ox", NFILE, fnm);
 +    resfnm  = opt2fn_null("-or", NFILE, fnm);
 +    if (bPI || resfnm != NULL)
 +    {
 +        /* We need a tps file */
 +        tpsfnm = ftp2fn(efTPS, NFILE, fnm);
 +    }
 +    else
 +    {
 +        tpsfnm = ftp2fn_null(efTPS, NFILE, fnm);
 +    }
 +
 +    if (!tpsfnm && !ndxfnm)
 +    {
 +        gmx_fatal(FARGS, "You have to specify either the index file or a tpr file");
 +    }
 +
 +    if (bPI)
 +    {
 +        ng = 1;
 +        fprintf(stderr, "Choose a group for distance calculation\n");
 +    }
 +    else if (!bMat)
 +    {
 +        ng++;
 +    }
 +
 +    snew(gnx, ng);
 +    snew(index, ng);
 +    snew(grpname, ng);
 +
 +    if (tpsfnm || resfnm || !ndxfnm)
 +    {
 +        snew(top, 1);
 +        bTop = read_tps_conf(tpsfnm, title, top, &ePBC, &x, NULL, box, FALSE);
 +        if (bPI && !bTop)
 +        {
 +            printf("\nWARNING: Without a run input file a trajectory with broken molecules will not give the correct periodic image distance\n\n");
 +        }
 +    }
 +    get_index(top ? &(top->atoms) : NULL, ndxfnm, ng, gnx, index, grpname);
 +
 +    if (bMat && (ng == 1))
 +    {
 +        ng = gnx[0];
 +        printf("Special case: making distance matrix between all atoms in group %s\n",
 +               grpname[0]);
 +        srenew(gnx, ng);
 +        srenew(index, ng);
 +        srenew(grpname, ng);
 +        for (i = 1; (i < ng); i++)
 +        {
 +            gnx[i]      = 1;
 +            grpname[i]  = grpname[0];
 +            snew(index[i], 1);
 +            index[i][0] = index[0][i];
 +        }
 +        gnx[0] = 1;
 +    }
 +
 +    if (resfnm)
 +    {
 +        nres = find_residues(top ? &(top->atoms) : NULL,
 +                             gnx[0], index[0], &residues);
 +        if (debug)
 +        {
 +            dump_res(debug, nres, residues, gnx[0], index[0]);
 +        }
 +    }
 +
 +    if (bPI)
 +    {
 +        periodic_mindist_plot(trxfnm, distfnm, top, ePBC, gnx[0], index[0], bSplit, oenv);
 +    }
 +    else
 +    {
 +        dist_plot(trxfnm, atmfnm, distfnm, numfnm, resfnm, oxfnm,
 +                  rcutoff, bMat, top ? &(top->atoms) : NULL,
 +                  ng, index, gnx, grpname, bSplit, !bMax, nres, residues, bPBC, ePBC,
 +                  bGroup, bEachResEachTime, bPrintResName, oenv);
 +    }
 +
 +    do_view(oenv, distfnm, "-nxy");
 +    if (!bPI)
 +    {
 +        do_view(oenv, numfnm, "-nxy");
 +    }
 +
 +    thanx(stderr);
 +
 +    return 0;
 +}
index 680507b7f1691b5657188c92d44487f36aa4de79,0000000000000000000000000000000000000000..07ecbccc8dc8a3f0dbd39811301fcb6ae67d41dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,308 -1,0 +1,311 @@@
-  *                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.
 +/*
++ * This file is part of the GROMACS molecular simulation package.
 + *
-  * 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
 + * 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.
-  * 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.
++ * Copyright (c) 2012, by the GROMACS development team, led by
++ * David van der Spoel, Berk Hess, Erik Lindahl, and including many
++ * others, as listed in the AUTHORS file in the top-level source
++ * directory and at http://www.gromacs.org.
++ *
++ * GROMACS is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public License
++ * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
-  * 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.
++ * 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.
 + *
-  * For more info, check our website at http://www.gromacs.org
++ * 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.
 + *
-  * And Hey:
-  * Green Red Orange Magenta Azure Cyan Skyblue
++ * 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 <math.h>
 +#include "typedefs.h"
 +#include "smalloc.h"
 +#include "copyrite.h"
 +#include "statutil.h"
 +#include "macros.h"
 +#include "string2.h"
 +#include "futil.h"
 +#include "gmx_fatal.h"
 +
 +static int calc_ntype(int nft, int *ft, t_idef *idef)
 +{
 +    int  i, f, nf = 0;
 +
 +    for (i = 0; (i < idef->ntypes); i++)
 +    {
 +        for (f = 0; f < nft; f++)
 +        {
 +            if (idef->functype[i] == ft[f])
 +            {
 +                nf++;
 +            }
 +        }
 +    }
 +
 +    return nf;
 +}
 +
 +static void fill_ft_ind(int nft, int *ft, t_idef *idef,
 +                        int ft_ind[], char *grpnames[])
 +{
 +    char buf[125];
 +    int  i, f, ftype, ind = 0;
 +
 +    /* Loop over all the function types in the topology */
 +    for (i = 0; (i < idef->ntypes); i++)
 +    {
 +        ft_ind[i] = -1;
 +        /* Check all the selected function types */
 +        for (f = 0; f < nft; f++)
 +        {
 +            ftype = ft[f];
 +            if (idef->functype[i] == ftype)
 +            {
 +                ft_ind[i] = ind;
 +                switch (ftype)
 +                {
 +                    case F_ANGLES:
 +                        sprintf(buf, "Theta=%.1f_%.2f", idef->iparams[i].harmonic.rA,
 +                                idef->iparams[i].harmonic.krA);
 +                        break;
 +                    case F_G96ANGLES:
 +                        sprintf(buf, "Cos_th=%.1f_%.2f", idef->iparams[i].harmonic.rA,
 +                                idef->iparams[i].harmonic.krA);
 +                        break;
 +                    case F_UREY_BRADLEY:
 +                        sprintf(buf, "UB_th=%.1f_%.2f2f", idef->iparams[i].u_b.thetaA,
 +                                idef->iparams[i].u_b.kthetaA);
 +                        break;
 +                    case F_QUARTIC_ANGLES:
 +                        sprintf(buf, "Q_th=%.1f_%.2f_%.2f", idef->iparams[i].qangle.theta,
 +                                idef->iparams[i].qangle.c[0], idef->iparams[i].qangle.c[1]);
 +                        break;
 +                    case F_TABANGLES:
 +                        sprintf(buf, "Table=%d_%.2f", idef->iparams[i].tab.table,
 +                                idef->iparams[i].tab.kA);
 +                        break;
 +                    case F_PDIHS:
 +                        sprintf(buf, "Phi=%.1f_%d_%.2f", idef->iparams[i].pdihs.phiA,
 +                                idef->iparams[i].pdihs.mult, idef->iparams[i].pdihs.cpA);
 +                        break;
 +                    case F_IDIHS:
 +                        sprintf(buf, "Xi=%.1f_%.2f", idef->iparams[i].harmonic.rA,
 +                                idef->iparams[i].harmonic.krA);
 +                        break;
 +                    case F_RBDIHS:
 +                        sprintf(buf, "RB-A1=%.2f", idef->iparams[i].rbdihs.rbcA[1]);
 +                        break;
 +                    default:
 +                        gmx_fatal(FARGS, "Unsupported function type '%s' selected",
 +                                  interaction_function[ftype].longname);
 +                }
 +                grpnames[ind] = strdup(buf);
 +                ind++;
 +            }
 +        }
 +    }
 +}
 +
 +static void fill_ang(int nft, int *ft, int fac,
 +                     int nr[], int *index[], int ft_ind[], t_topology *top,
 +                     gmx_bool bNoH, real hq)
 +{
 +    int         f, ftype, i, j, indg, nr_fac;
 +    gmx_bool    bUse;
 +    t_idef     *idef;
 +    t_atom     *atom;
 +    t_iatom    *ia;
 +
 +
 +    idef = &top->idef;
 +    atom = top->atoms.atom;
 +
 +    for (f = 0; f < nft; f++)
 +    {
 +        ftype = ft[f];
 +        ia    = idef->il[ftype].iatoms;
 +        for (i = 0; (i < idef->il[ftype].nr); )
 +        {
 +            indg = ft_ind[ia[0]];
 +            if (indg == -1)
 +            {
 +                gmx_incons("Routine fill_ang");
 +            }
 +            bUse = TRUE;
 +            if (bNoH)
 +            {
 +                for (j = 0; j < fac; j++)
 +                {
 +                    if (atom[ia[1+j]].m < 1.5)
 +                    {
 +                        bUse = FALSE;
 +                    }
 +                }
 +            }
 +            if (hq)
 +            {
 +                for (j = 0; j < fac; j++)
 +                {
 +                    if (atom[ia[1+j]].m < 1.5 && fabs(atom[ia[1+j]].q) < hq)
 +                    {
 +                        bUse = FALSE;
 +                    }
 +                }
 +            }
 +            if (bUse)
 +            {
 +                if (nr[indg] % 1000 == 0)
 +                {
 +                    srenew(index[indg], fac*(nr[indg]+1000));
 +                }
 +                nr_fac = fac*nr[indg];
 +                for (j = 0; (j < fac); j++)
 +                {
 +                    index[indg][nr_fac+j] = ia[j+1];
 +                }
 +                nr[indg]++;
 +            }
 +            ia += interaction_function[ftype].nratoms+1;
 +            i  += interaction_function[ftype].nratoms+1;
 +        }
 +    }
 +}
 +
 +static int *select_ftype(const char *opt, int *nft, int *mult)
 +{
 +    int *ft = NULL, ftype;
 +
 +    if (opt[0] == 'a')
 +    {
 +        *mult = 3;
 +        for (ftype = 0; ftype < F_NRE; ftype++)
 +        {
 +            if ((interaction_function[ftype].flags & IF_ATYPE) ||
 +                ftype == F_TABANGLES)
 +            {
 +                (*nft)++;
 +                srenew(ft, *nft);
 +                ft[*nft-1] = ftype;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        *mult = 4;
 +        *nft  = 1;
 +        snew(ft, *nft);
 +        switch (opt[0])
 +        {
 +            case 'd':
 +                ft[0] = F_PDIHS;
 +                break;
 +            case 'i':
 +                ft[0] = F_IDIHS;
 +                break;
 +            case 'r':
 +                ft[0] = F_RBDIHS;
 +                break;
 +            default:
 +                break;
 +        }
 +    }
 +
 +    return ft;
 +}
 +
 +int gmx_mk_angndx(int argc, char *argv[])
 +{
 +    static const char *desc[] = {
 +        "[TT]mk_angndx[tt] makes an index file for calculation of",
 +        "angle distributions etc. It uses a run input file ([TT].tpx[tt]) for the",
 +        "definitions of the angles, dihedrals etc."
 +    };
 +    static const char *opt[] = { NULL, "angle", "dihedral", "improper", "ryckaert-bellemans", NULL };
 +    static gmx_bool    bH    = TRUE;
 +    static real        hq    = -1;
 +    t_pargs            pa[]  = {
 +        { "-type", FALSE, etENUM, {opt},
 +          "Type of angle" },
 +        { "-hyd", FALSE, etBOOL, {&bH},
 +          "Include angles with atoms with mass < 1.5" },
 +        { "-hq", FALSE, etREAL, {&hq},
 +          "Ignore angles with atoms with mass < 1.5 and magnitude of their charge less than this value" }
 +    };
 +
 +    output_env_t       oenv;
 +    FILE              *out;
 +    t_topology        *top;
 +    int                i, j, ntype;
 +    int                nft = 0, *ft, mult = 0;
 +    int              **index;
 +    int               *ft_ind;
 +    int               *nr;
 +    char             **grpnames;
 +    t_filenm           fnm[] = {
 +        { efTPX, NULL, NULL, ffREAD  },
 +        { efNDX, NULL, "angle", ffWRITE }
 +    };
 +#define NFILE asize(fnm)
 +
 +    parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa,
 +                      asize(desc), desc, 0, NULL, &oenv);
 +
 +
 +    ft = select_ftype(opt[0], &nft, &mult);
 +
 +    top = read_top(ftp2fn(efTPX, NFILE, fnm), NULL);
 +
 +    ntype = calc_ntype(nft, ft, &(top->idef));
 +    snew(grpnames, ntype);
 +    snew(ft_ind, top->idef.ntypes);
 +    fill_ft_ind(nft, ft, &top->idef, ft_ind, grpnames);
 +
 +    snew(nr, ntype);
 +    snew(index, ntype);
 +    fill_ang(nft, ft, mult, nr, index, ft_ind, top, !bH, hq);
 +
 +    out = ftp2FILE(efNDX, NFILE, fnm, "w");
 +    for (i = 0; (i < ntype); i++)
 +    {
 +        if (nr[i] > 0)
 +        {
 +            fprintf(out, "[ %s ]\n", grpnames[i]);
 +            for (j = 0; (j < nr[i]*mult); j++)
 +            {
 +                fprintf(out, " %5d", index[i][j]+1);
 +                if ((j % 12) == 11)
 +                {
 +                    fprintf(out, "\n");
 +                }
 +            }
 +            fprintf(out, "\n");
 +        }
 +    }
 +    ffclose(out);
 +
 +    thanx(stderr);
 +
 +    return 0;
 +}
index 8914ecf53edcc4808b3e41335899a517857aec40,0000000000000000000000000000000000000000..0c7c37929eaf216fcff1774d1c0b474fa00e7557
mode 100644,000000..100644
--- /dev/null
@@@ -1,610 -1,0 +1,611 @@@
 +/*
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Green Red Orange Magenta Azure Cyan Skyblue
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <math.h>
 +
 +#include "sysstuff.h"
 +#include <string.h>
 +#include "typedefs.h"
 +#include "smalloc.h"
 +#include "macros.h"
 +#include "rmpbc.h"
 +#include "vec.h"
 +#include "xvgr.h"
 +#include "pbc.h"
 +#include "copyrite.h"
 +#include "futil.h"
 +#include "statutil.h"
 +#include "index.h"
 +#include "gmx_ana.h"
 +
 +
 +/* this version only works correctly if one of the entries in the index file
 +   is a plane (three atoms specified) and the other a vector. Distance
 +   is calculated from the center of the plane to both atoms of the vector */
 +
 +static void print_types(atom_id index1[], int gnx1, char *group1,
 +                        atom_id index2[], int gnx2, char *group2,
 +                        t_topology *top)
 +{
 +    int i, j;
 +
 +    fprintf(stderr, "\n");
 +    fprintf(stderr, "Group %s contains the following atoms: \n", group1);
 +    for (i = 0; i < gnx1; i++)
 +    {
 +        fprintf(stderr, "Atomname %d: %s\n", i, *(top->atoms.atomname[index1[i]]));
 +    }
 +    fprintf(stderr, "\n");
 +
 +    fprintf(stderr, "Group %s contains the following atoms: \n", group2);
 +    for (j = 0; j < gnx2; j++)
 +    {
 +        fprintf(stderr, "Atomname %d: %s\n", j, *(top->atoms.atomname[index2[j]]));
 +    }
 +    fprintf(stderr, "\n");
 +
 +    fprintf(stderr, "Careful: distance only makes sense in some situations.\n\n");
 +}
 +
 +static void calculate_normal(atom_id index[], rvec x[], rvec result, rvec center)
 +{
 +    rvec c1, c2;
 +    int  i;
 +
 +    /* calculate centroid of triangle spanned by the three points */
 +    for (i = 0; i < 3; i++)
 +    {
 +        center[i] = (x[index[0]][i] + x[index[1]][i] + x[index[2]][i])/3;
 +    }
 +
 +    /* use P1P2 x P1P3 to calculate normal, given three points P1-P3 */
 +    rvec_sub(x[index[1]], x[index[0]], c1); /* find two vectors */
 +    rvec_sub(x[index[2]], x[index[0]], c2);
 +
 +    cprod(c1, c2, result);                /* take crossproduct between these */
 +}
 +
 +/* calculate the angle and distance between the two groups */
 +static void calc_angle(int ePBC, matrix box, rvec x[], atom_id index1[],
 +                       atom_id index2[], int gnx1, int gnx2,
 +                       real *angle,      real *distance,
 +                       real *distance1,  real *distance2)
 +
 +/* distance is distance between centers, distance 1 between center of plane
 +   and atom one of vector, distance 2 same for atom two
 + */
 +
 +{
 +    rvec
 +        normal1, normal2,   /* normals on planes of interest */
 +        center1, center2,   /* center of triangle of points given to define plane,*/
 +                            /* or center of vector if a vector is given */
 +        h1, h2, h3, h4, h5; /* temp. vectors */
 +    t_pbc pbc;
 +
 +    set_pbc(&pbc, ePBC, box);
 +
 +    switch (gnx1)
 +    {
 +        case 3:       /* group 1 defines plane */
 +            calculate_normal(index1, x, normal1, center1);
 +            break;
 +        case 2:       /* group 1 defines vector */
 +            rvec_sub(x[index1[0]], x[index1[1]], normal1);
 +            rvec_add(x[index1[0]], x[index1[1]], h1);
 +            svmul(0.5, h1, center1); /* center is geometric mean */
 +            break;
 +        default:                     /* group 1 does none of the above */
 +            gmx_fatal(FARGS, "Something wrong with contents of index file. Groups should contain 2 or 3 atoms.\n");
 +    }
 +
 +    switch (gnx2)
 +    {
 +        case 3:      /* group 2 defines plane */
 +            calculate_normal(index2, x, normal2, center2);
 +            break;
 +        case 2:      /* group 2 defines vector */
 +            rvec_sub(x[index2[0]], x[index2[1]], normal2);
 +            rvec_add(x[index2[0]], x[index2[1]], h2);
 +            svmul(0.5, h2, center2); /* center is geometric mean */
 +            break;
 +        case 0:
 +            normal2[XX] = 0;
 +            normal2[YY] = 0;
 +            normal2[ZZ] = 1;
 +            center2[XX] = 0;
 +            center2[YY] = 0;
 +            center2[ZZ] = 0;
 +            break;
 +        default:     /* group 2 does none of the above */
 +            gmx_fatal(FARGS, "Something wrong with contents of index file.\n");
 +    }
 +
 +    *angle = cos_angle(normal1, normal2);
 +
 +    if (box)
 +    {
 +        pbc_dx(&pbc, center1, center2, h3);
 +    }
 +    else
 +    {
 +        rvec_sub(center1, center2, h3);
 +    }
 +    *distance = norm(h3);
 +
 +    if (gnx1 == 3 && gnx2 == 2)
 +    {
 +        if (box)
 +        {
 +            pbc_dx(&pbc, center1, x[index2[0]], h4);
 +            pbc_dx(&pbc, center1, x[index2[1]], h5);
 +        }
 +        else
 +        {
 +            rvec_sub(center1, x[index2[0]], h4);
 +            rvec_sub(center1, x[index2[1]], h5);
 +        }
 +        *distance1 = norm(h4);
 +        *distance2 = norm(h5);
 +    }
 +    else if (gnx1 == 2 && gnx2 == 3)
 +    {
 +        rvec_sub(center1, x[index1[0]], h4);
 +        rvec_sub(center1, x[index1[1]], h5);
 +        *distance1 = norm(h4);
 +        *distance2 = norm(h5);
 +    }
 +    else
 +    {
 +        *distance1 = 0; *distance2 = 0;
 +    }
 +}
 +
 +void sgangle_plot(const char *fn, const char *afile, const char *dfile,
 +                  const char *d1file, const char *d2file,
 +                  atom_id index1[], int gnx1, char *grpn1,
 +                  atom_id index2[], int gnx2, char *grpn2,
 +                  t_topology *top, int ePBC, const output_env_t oenv)
 +{
 +    FILE
 +    *sg_angle,            /* xvgr file with angles */
 +    *sg_distance  = NULL, /* xvgr file with distances */
 +    *sg_distance1 = NULL, /* xvgr file with distance between plane and atom */
 +    *sg_distance2 = NULL; /* xvgr file with distance between plane and atom2 */
 +    real
 +        t,                /* time */
 +        angle,            /* cosine of angle between two groups */
 +        distance,         /* distance between two groups. */
 +        distance1,        /* distance between plane and one of two atoms */
 +        distance2;        /* same for second of two atoms */
 +    t_trxstatus *status;
 +    int          natoms, teller = 0;
 +    rvec        *x0;       /* coordinates, and coordinates corrected for pb */
 +    matrix       box;
 +    char         buf[256]; /* for xvgr title */
 +    gmx_rmpbc_t  gpbc = NULL;
++    const char*  aleg[2] = { "cos(Angle)", "Angle (degrees)" };     /* legends for sg_angle output file */
 +
 +    if ((natoms = read_first_x(oenv, &status, fn, &t, &x0, box)) == 0)
 +    {
 +        gmx_fatal(FARGS, "Could not read coordinates from statusfile\n");
 +    }
 +
 +    sprintf(buf, "Angle between %s and %s", grpn1, grpn2);
 +    sg_angle = xvgropen(afile, buf, "Time (ps)", "Angle (degrees)", oenv);
++    xvgr_legend(sg_angle, 2, aleg, oenv);
 +
 +    if (dfile)
 +    {
 +        sprintf(buf, "Distance between %s and %s", grpn1, grpn2);
 +        sg_distance = xvgropen(dfile, buf, "Time (ps)", "Distance (nm)", oenv);
 +    }
 +
 +    if (d1file)
 +    {
 +        sprintf(buf, "Distance between plane and first atom of vector");
 +        sg_distance1 = xvgropen(d1file, buf, "Time (ps)", "Distance (nm)", oenv);
 +    }
 +
 +    if (d2file)
 +    {
 +        sprintf(buf, "Distance between plane and second atom of vector");
 +        sg_distance2 = xvgropen(d2file, buf, "Time (ps", "Distance (nm)", oenv);
 +    }
 +
 +    gpbc = gmx_rmpbc_init(&(top->idef), ePBC, natoms, box);
 +
 +    do
 +    {
 +        teller++;
 +
 +        gmx_rmpbc(gpbc, natoms, box, x0);
 +
 +        calc_angle(ePBC, box, x0, index1, index2, gnx1, gnx2, &angle,
 +                   &distance, &distance1, &distance2);
 +
 +        fprintf(sg_angle, "%12g  %12g  %12g\n", t, angle, acos(angle)*180.0/M_PI);
 +        if (dfile)
 +        {
 +            fprintf(sg_distance, "%12g  %12g\n", t, distance);
 +        }
 +        if (d1file)
 +        {
 +            fprintf(sg_distance1, "%12g  %12g\n", t, distance1);
 +        }
 +        if (d2file)
 +        {
 +            fprintf(sg_distance2, "%12g  %12g\n", t, distance1);
 +        }
 +
 +    }
 +    while (read_next_x(oenv, status, &t, natoms, x0, box));
 +
 +    gmx_rmpbc_done(gpbc);
 +
 +    fprintf(stderr, "\n");
 +    close_trj(status);
 +    ffclose(sg_angle);
 +    if (dfile)
 +    {
 +        ffclose(sg_distance);
 +    }
 +    if (d1file)
 +    {
 +        ffclose(sg_distance1);
 +    }
 +    if (d2file)
 +    {
 +        ffclose(sg_distance2);
 +    }
 +
 +    sfree(x0);
 +}
 +
 +static void calc_angle_single(int     ePBC,
 +                              matrix  box,
 +                              rvec    xzero[],
 +                              rvec    x[],
 +                              atom_id index1[],
 +                              atom_id index2[],
 +                              int     gnx1,
 +                              int     gnx2,
 +                              real   *angle,
 +                              real   *distance,
 +                              real   *distance1,
 +                              real   *distance2)
 +{
 +    t_pbc pbc;
 +
 +    /* distance is distance between centers, distance 1 between center of plane
 +       and atom one of vector, distance 2 same for atom two
 +     */
 +
 +    rvec  normal1, normal2; /* normals on planes of interest */
 +    rvec  center1, center2;
 +    /* center of triangle of pts to define plane,
 +     * or center of vector if a vector is given
 +     */
 +    rvec  h1, h2, h3, h4, h5; /* temp. vectors */
 +
 +    if (box)
 +    {
 +        set_pbc(&pbc, ePBC, box);
 +    }
 +
 +    switch (gnx1)
 +    {
 +        case 3:     /* group 1 defines plane */
 +            calculate_normal(index1, xzero, normal1, center1);
 +            break;
 +        case 2:     /* group 1 defines vector */
 +            rvec_sub(xzero[index1[0]], xzero[index1[1]], normal1);
 +            rvec_add(xzero[index1[0]], xzero[index1[1]], h1);
 +            svmul(0.5, h1, center1); /* center is geometric mean */
 +            break;
 +        default:                     /* group 1 does none of the above */
 +            gmx_fatal(FARGS, "Something wrong with contents of index file.\n");
 +    }
 +
 +    switch (gnx2)
 +    {
 +        case 3:    /* group 2 defines plane */
 +            calculate_normal(index2, x, normal2, center2);
 +            break;
 +        case 2:    /* group 2 defines vector */
 +            rvec_sub(x[index2[0]], x[index2[1]], normal2);
 +            rvec_add(x[index2[0]], x[index2[1]], h2);
 +            svmul(0.5, h2, center2); /* center is geometric mean */
 +            break;
 +        default:                     /* group 2 does none of the above */
 +            gmx_fatal(FARGS, "Something wrong with contents of index file.\n");
 +    }
 +
 +    *angle = cos_angle(normal1, normal2);
 +
 +    if (box)
 +    {
 +        pbc_dx(&pbc, center1, center2, h3);
 +    }
 +    else
 +    {
 +        rvec_sub(center1, center2, h3);
 +    }
 +    *distance = norm(h3);
 +
 +    if (gnx1 == 3 && gnx2 == 2)
 +    {
 +        if (box)
 +        {
 +            pbc_dx(&pbc, center1, x[index2[0]], h4);
 +            pbc_dx(&pbc, center1, x[index2[1]], h5);
 +        }
 +        else
 +        {
 +            rvec_sub(center1, x[index2[0]], h4);
 +            rvec_sub(center1, x[index2[1]], h5);
 +        }
 +        *distance1 = norm(h4);
 +        *distance2 = norm(h5);
 +    }
 +    else if (gnx1 == 2 && gnx2 == 3)
 +    {
 +        rvec_sub(center1, xzero[index1[0]], h4);
 +        rvec_sub(center1, xzero[index1[1]], h5);
 +        *distance1 = norm(h4);
 +        *distance2 = norm(h5);
 +    }
 +    else
 +    {
 +        *distance1 = 0; *distance2 = 0;
 +    }
 +}
 +
 +
 +void sgangle_plot_single(const char *fn, const char *afile, const char *dfile,
 +                         const char *d1file, const char *d2file,
 +                         atom_id index1[], int gnx1, char *grpn1,
 +                         atom_id index2[], int gnx2, char *grpn2,
 +                         t_topology *top, int ePBC, const output_env_t oenv)
 +{
 +    FILE
 +    *sg_angle,            /* xvgr file with angles */
 +    *sg_distance  = NULL, /* xvgr file with distances */
 +    *sg_distance1 = NULL, /* xvgr file with distance between plane and atom */
 +    *sg_distance2 = NULL; /* xvgr file with distance between plane and atom2 */
 +    real
 +        t,                /* time */
 +        angle,            /* cosine of angle between two groups */
 +        distance,         /* distance between two groups. */
 +        distance1,        /* distance between plane and one of two atoms */
 +        distance2;        /* same for second of two atoms */
 +    t_trxstatus *status;
 +    int          natoms, teller = 0;
 +    int          i;
 +    rvec        *x0; /* coordinates, and coordinates corrected for pb */
 +    rvec        *xzero;
 +    matrix       box;
 +    char         buf[256]; /* for xvgr title */
 +    gmx_rmpbc_t  gpbc = NULL;
 +
 +
 +    if ((natoms = read_first_x(oenv, &status, fn, &t, &x0, box)) == 0)
 +    {
 +        gmx_fatal(FARGS, "Could not read coordinates from statusfile\n");
 +    }
 +
 +    sprintf(buf, "Angle between %s and %s", grpn1, grpn2);
 +    sg_angle = xvgropen(afile, buf, "Time (ps)", "Cos(angle) ", oenv);
 +
 +    if (dfile)
 +    {
 +        sprintf(buf, "Distance between %s and %s", grpn1, grpn2);
 +        sg_distance = xvgropen(dfile, buf, "Time (ps)", "Distance (nm)", oenv);
 +    }
 +
 +    if (d1file)
 +    {
 +        sprintf(buf, "Distance between plane and first atom of vector");
 +        sg_distance1 = xvgropen(d1file, buf, "Time (ps)", "Distance (nm)", oenv);
 +    }
 +
 +    if (d2file)
 +    {
 +        sprintf(buf, "Distance between plane and second atom of vector");
 +        sg_distance2 = xvgropen(d2file, buf, "Time (ps", "Distance (nm)", oenv);
 +    }
 +
 +    snew(xzero, natoms);
 +    gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms, box);
 +
 +    do
 +    {
 +        teller++;
 +
 +        gmx_rmpbc(gpbc, natoms, box, x0);
 +        if (teller == 1)
 +        {
 +            for (i = 0; i < natoms; i++)
 +            {
 +                copy_rvec(x0[i], xzero[i]);
 +            }
 +        }
 +
 +
 +        calc_angle_single(ePBC, box,
 +                          xzero, x0, index1, index2, gnx1, gnx2, &angle,
 +                          &distance, &distance1, &distance2);
 +
 +        fprintf(sg_angle, "%12g  %12g  %12g\n", t, angle, acos(angle)*180.0/M_PI);
 +        if (dfile)
 +        {
 +            fprintf(sg_distance, "%12g  %12g\n", t, distance);
 +        }
 +        if (d1file)
 +        {
 +            fprintf(sg_distance1, "%12g  %12g\n", t, distance1);
 +        }
 +        if (d2file)
 +        {
 +            fprintf(sg_distance2, "%12g  %12g\n", t, distance1);
 +        }
 +
 +    }
 +    while (read_next_x(oenv, status, &t, natoms, x0, box));
 +    gmx_rmpbc_done(gpbc);
 +
 +    fprintf(stderr, "\n");
 +    close_trj(status);
 +    ffclose(sg_angle);
 +    if (dfile)
 +    {
 +        ffclose(sg_distance);
 +    }
 +    if (d1file)
 +    {
 +        ffclose(sg_distance1);
 +    }
 +    if (d2file)
 +    {
 +        ffclose(sg_distance2);
 +    }
 +
 +    sfree(x0);
 +}
 +
 +
 +
 +int gmx_sgangle(int argc, char *argv[])
 +{
 +    const char     *desc[] = {
 +        "Compute the angle and distance between two groups. ",
 +        "The groups are defined by a number of atoms given in an index file and",
 +        "may be two or three atoms in size.",
 +        "If [TT]-one[tt] is set, only one group should be specified in the index",
 +        "file and the angle between this group at time 0 and t will be computed.",
 +        "The angles calculated depend on the order in which the atoms are ",
 +        "given. Giving, for instance, 5 6 will rotate the vector 5-6 with ",
 +        "180 degrees compared to giving 6 5. [PAR]If three atoms are given, ",
 +        "the normal on the plane spanned by those three atoms will be",
 +        "calculated, using the formula  P1P2 x P1P3.",
 +        "The cos of the angle is calculated, using the inproduct of the two",
 +        "normalized vectors.[PAR]",
 +        "Here is what some of the file options do:[BR]",
 +        "[TT]-oa[tt]: Angle between the two groups specified in the index file. If a group contains three atoms the normal to the plane defined by those three atoms will be used. If a group contains two atoms, the vector defined by those two atoms will be used.[BR]",
 +        "[TT]-od[tt]: Distance between two groups. Distance is taken from the center of one group to the center of the other group.[BR]",
 +        "[TT]-od1[tt]: If one plane and one vector is given, the distances for each of the atoms from the center of the plane is given separately.[BR]",
 +        "[TT]-od2[tt]: For two planes this option has no meaning."
 +    };
 +
 +    output_env_t    oenv;
 +    const char     *fna, *fnd, *fnd1, *fnd2;
 +    char     *      grpname[2];         /* name of the two groups */
 +    int             gnx[2];             /* size of the two groups */
 +    t_topology     *top;                /* topology         */
 +    int             ePBC;
 +    atom_id        *index[2];
 +    static gmx_bool bOne = FALSE, bZ = FALSE;
 +    t_pargs         pa[] = {
 +        { "-one", FALSE, etBOOL, {&bOne},
 +          "Only one group compute angle between vector at time zero and time t"},
 +        { "-z", FALSE, etBOOL, {&bZ},
 +          "Use the [IT]z[it]-axis as reference" }
 +    };
 +#define NPA asize(pa)
 +
 +    t_filenm  fnm[] = {                         /* files for g_sgangle  */
 +        { efTRX, "-f", NULL,  ffREAD },         /* trajectory file  */
 +        { efNDX, NULL, NULL,  ffREAD },         /* index file       */
 +        { efTPX, NULL, NULL,  ffREAD },         /* topology file    */
 +        { efXVG, "-oa", "sg_angle", ffWRITE },  /* xvgr output file     */
 +        { efXVG, "-od", "sg_dist", ffOPTWR },   /* xvgr output file     */
 +        { efXVG, "-od1", "sg_dist1", ffOPTWR }, /* xvgr output file     */
 +        { efXVG, "-od2", "sg_dist2", ffOPTWR }  /* xvgr output file     */
 +    };
 +
 +#define NFILE asize(fnm)
 +
 +    parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
 +                      NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv);
 +
 +
 +    top = read_top(ftp2fn(efTPX, NFILE, fnm), &ePBC); /* read topology file */
 +
 +    fna  = opt2fn("-oa", NFILE, fnm);
 +    fnd  = opt2fn_null("-od", NFILE, fnm);
 +    fnd1 = opt2fn_null("-od1", NFILE, fnm);
 +    fnd2 = opt2fn_null("-od2", NFILE, fnm);
 +
 +    /* read index file. */
 +    if (bOne)
 +    {
 +        rd_index(ftp2fn(efNDX, NFILE, fnm), 1, gnx, index, grpname);
 +        print_types(index[0], gnx[0], grpname[0],
 +                    index[0], gnx[0], grpname[0], top);
 +
 +        sgangle_plot_single(ftp2fn(efTRX, NFILE, fnm), fna, fnd, fnd1, fnd2,
 +                            index[0], gnx[0], grpname[0],
 +                            index[0], gnx[0], grpname[0],
 +                            top, ePBC, oenv);
 +    }
 +    else
 +    {
 +        rd_index(ftp2fn(efNDX, NFILE, fnm), bZ ? 1 : 2, gnx, index, grpname);
 +        if (!bZ)
 +        {
 +            print_types(index[0], gnx[0], grpname[0],
 +                        index[1], gnx[1], grpname[1], top);
 +        }
 +        else
 +        {
 +            gnx[1]     = 0;
 +            grpname[1] = strdup("Z-axis");
 +        }
 +        sgangle_plot(ftp2fn(efTRX, NFILE, fnm), fna, fnd, fnd1, fnd2,
 +                     index[0], gnx[0], grpname[0],
 +                     index[1], gnx[1], grpname[1],
 +                     top, ePBC, oenv);
 +    }
 +
 +    do_view(oenv, fna, "-nxy");  /* view xvgr file */
 +    do_view(oenv, fnd, "-nxy");  /* view xvgr file */
 +    do_view(oenv, fnd1, "-nxy"); /* view xvgr file */
 +    do_view(oenv, fnd2, "-nxy"); /* view xvgr file */
 +
 +    thanx(stderr);
 +    return 0;
 +}
index 6f3042d2317319af2310960a71f9cf3594d77416,0000000000000000000000000000000000000000..c3507260738bd1956289d1227a47f82f9f5cbb1b
mode 100644,000000..100644
--- /dev/null
@@@ -1,196 -1,0 +1,199 @@@
-  *                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.
 +/*
++ * This file is part of the GROMACS molecular simulation package.
 + *
-  * 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
 + * 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.
-  * 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.
++ * Copyright (c) 2012, by the GROMACS development team, led by
++ * David van der Spoel, Berk Hess, Erik Lindahl, and including many
++ * others, as listed in the AUTHORS file in the top-level source
++ * directory and at http://www.gromacs.org.
++ *
++ * GROMACS is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public License
++ * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
-  * 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.
++ * 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.
 + *
-  * For more info, check our website at http://www.gromacs.org
++ * 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.
 + *
-  * And Hey:
-  * Good gRace! Old Maple Actually Chews Slate
++ * 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 <stdio.h>
 +#include <math.h>
 +#include "typedefs.h"
 +#include "statutil.h"
 +#include "copyrite.h"
 +#include "gmx_fatal.h"
 +#include "xvgr.h"
 +#include "pdbio.h"
 +#include "macros.h"
 +#include "smalloc.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "physics.h"
 +#include "names.h"
 +#include "txtdump.h"
 +#include "trnio.h"
 +#include "symtab.h"
 +#include "confio.h"
 +
 +real pot(real x, real qq, real c6, real cn, int npow)
 +{
 +    return cn*pow(x, -npow)-c6*pow(x, -6)+qq*ONE_4PI_EPS0/x;
 +}
 +
 +real bhpot(real x, real qq, real A, real B, real C)
 +{
 +    return A*exp(-B*x) - C*pow(x, -6.0);
 +}
 +
 +real dpot(real x, real qq, real c6, real cn, int npow)
 +{
 +    return -(npow*cn*pow(x, -npow-1)-6*c6*pow(x, -7)+qq*ONE_4PI_EPS0/sqr(x));
 +}
 +
 +int gmx_sigeps(int argc, char *argv[])
 +{
 +    const char   *desc[] = {
 +        "[TT]g_sigeps[tt] is a simple utility that converts C6/C12 or C6/Cn combinations",
 +        "to [GRK]sigma[grk] and [GRK]epsilon[grk], or vice versa. It can also plot the potential",
 +        "in  file. In addition, it makes an approximation of a Buckingham potential",
 +        "to a Lennard-Jones potential."
 +    };
 +    static real   c6   = 1.0e-3, cn = 1.0e-6, qi = 0, qj = 0, sig = 0.3, eps = 1, sigfac = 0.7;
 +    static real   Abh  = 1e5, Bbh = 32, Cbh = 1e-3;
 +    static int    npow = 12;
 +    t_pargs       pa[] = {
 +        { "-c6",   FALSE,  etREAL,  {&c6},  "C6"   },
 +        { "-cn",   FALSE,  etREAL,  {&cn},  "Constant for repulsion"   },
 +        { "-pow",  FALSE,  etINT,   {&npow}, "Power of the repulsion term" },
 +        { "-sig",  FALSE,  etREAL,  {&sig}, "[GRK]sigma[grk]"  },
 +        { "-eps",  FALSE,  etREAL,  {&eps}, "[GRK]epsilon[grk]"  },
 +        { "-A",    FALSE,  etREAL,  {&Abh}, "Buckingham A" },
 +        { "-B",    FALSE,  etREAL,  {&Bbh}, "Buckingham B" },
 +        { "-C",    FALSE,  etREAL,  {&Cbh}, "Buckingham C" },
 +        { "-qi",   FALSE,  etREAL,  {&qi},  "qi"   },
 +        { "-qj",   FALSE,  etREAL,  {&qj},  "qj"   },
 +        { "-sigfac", FALSE, etREAL, {&sigfac}, "Factor in front of [GRK]sigma[grk] for starting the plot" }
 +    };
 +    t_filenm      fnm[] = {
 +        { efXVG, "-o", "potje", ffWRITE }
 +    };
 +    output_env_t  oenv;
 +#define NFILE asize(fnm)
 +    const char   *legend[] = { "Lennard-Jones", "Buckingham" };
 +    FILE         *fp;
 +    int           i;
 +    gmx_bool      bBham;
 +    real          qq, x, oldx, minimum, mval, dp[2], pp[2];
 +    int           cur = 0;
 +#define next (1-cur)
 +
 +    parse_common_args(&argc, argv, PCA_CAN_VIEW,
 +                      NFILE, fnm, asize(pa), pa, asize(desc),
 +                      desc, 0, NULL, &oenv);
 +
 +    bBham = (opt2parg_bSet("-A", asize(pa), pa) ||
 +             opt2parg_bSet("-B", asize(pa), pa) ||
 +             opt2parg_bSet("-C", asize(pa), pa));
 +
 +    if (bBham)
 +    {
 +        c6  = Cbh;
 +        sig = pow((6.0/npow)*pow(npow/Bbh, npow-6.0), 1.0/(npow-6.0));
 +        eps = c6/(4*pow(sig, 6.0));
 +        cn  = 4*eps*pow(sig, npow);
 +    }
 +    else
 +    {
 +        if (opt2parg_bSet("-sig", asize(pa), pa) ||
 +            opt2parg_bSet("-eps", asize(pa), pa))
 +        {
 +            c6  = 4*eps*pow(sig, 6);
 +            cn  = 4*eps*pow(sig, npow);
 +        }
 +        else if (opt2parg_bSet("-c6", asize(pa), pa) ||
 +                 opt2parg_bSet("-cn", asize(pa), pa) ||
 +                 opt2parg_bSet("-pow", asize(pa), pa))
 +        {
 +            sig = pow(cn/c6, 1.0/(npow-6.0));
 +            eps = 0.25*c6*pow(sig, -6.0);
 +        }
 +        else
 +        {
 +            sig = eps = 0;
 +        }
 +        printf("c6    = %12.5e, c%d    = %12.5e\n", c6, npow, cn);
 +        printf("sigma = %12.5f, epsilon = %12.5f\n", sig, eps);
 +
 +        minimum = pow(npow/6.0*pow(sig, npow-6.0), 1.0/(npow-6));
 +        printf("Van der Waals minimum at %g, V = %g\n\n",
 +               minimum, pot(minimum, 0, c6, cn, npow));
 +        printf("Fit of Lennard Jones (%d-6) to Buckingham:\n", npow);
 +        Bbh = npow/minimum;
 +        Cbh = c6;
 +        Abh = 4*eps*pow(sig/minimum, npow)*exp(npow);
 +        printf("A = %g, B = %g, C = %g\n", Abh, Bbh, Cbh);
 +    }
 +    qq = qi*qj;
 +
 +    fp = xvgropen(ftp2fn(efXVG, NFILE, fnm), "Potential", "r (nm)", "E (kJ/mol)",
 +                  oenv);
 +    xvgr_legend(fp, asize(legend), legend,
 +                oenv);
 +    if (sig == 0)
 +    {
 +        sig = 0.25;
 +    }
 +    minimum = -1;
 +    mval    = 0;
 +    oldx    = 0;
 +    for (i = 0; (i < 100); i++)
 +    {
 +        x        = sigfac*sig+sig*i*0.02;
 +        dp[next] = dpot(x, qq, c6, cn, npow);
 +        fprintf(fp, "%10g  %10g  %10g\n", x, pot(x, qq, c6, cn, npow),
 +                bhpot(x, qq, Abh, Bbh, Cbh));
 +        if (qq != 0)
 +        {
 +            if ((i > 0) && (dp[cur]*dp[next] < 0))
 +            {
 +                minimum = oldx + dp[cur]*(x-oldx)/(dp[cur]-dp[next]);
 +                mval    = pot(minimum, qq, c6, cn, npow);
 +                printf("Van der Waals + Coulomb minimum at r = %g (nm). Value = %g (kJ/mol)\n",
 +                       minimum, mval);
 +            }
 +        }
 +        cur  = next;
 +        oldx = x;
 +
 +    }
 +    ffclose(fp);
 +
 +    do_view(oenv, ftp2fn(efXVG, NFILE, fnm), NULL);
 +
 +    thanx(stderr);
 +
 +    return 0;
 +}
index b3bc55dddfb18ecc27a26f4e03bcdd0d43825b53,0000000000000000000000000000000000000000..1aa794714c8c429139034e29112875be291bc72c
mode 100644,000000..100644
--- /dev/null
@@@ -1,4153 -1,0 +1,4452 @@@
- #if !defined GMX_DOUBLE && defined GMX_X86_SSE2
- #include "gmx_x86_simd_single.h"
- #define SSE_PROPER_DIHEDRALS
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * GROningen Mixture of Alchemy and Childrens' Stories
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include "physics.h"
 +#include "vec.h"
 +#include "maths.h"
 +#include "txtdump.h"
 +#include "bondf.h"
 +#include "smalloc.h"
 +#include "pbc.h"
 +#include "ns.h"
 +#include "macros.h"
 +#include "names.h"
 +#include "gmx_fatal.h"
 +#include "mshift.h"
 +#include "main.h"
 +#include "disre.h"
 +#include "orires.h"
 +#include "force.h"
 +#include "nonbonded.h"
 +
- #ifdef SSE_PROPER_DIHEDRALS
- /* x86 SIMD inner-product of 4 float vectors */
- #define GMX_MM_IPROD_PS(ax, ay, az, bx, by, bz)                 \
-     _mm_add_ps(_mm_add_ps(_mm_mul_ps(ax, bx), _mm_mul_ps(ay, by)), _mm_mul_ps(az, bz))
- /* x86 SIMD norm^2 of 4 float vectors */
- #define GMX_MM_NORM2_PS(ax, ay, az) GMX_MM_IPROD_PS(ax, ay, az, ax, ay, az)
++#ifdef GMX_X86_SSE2
++#define SIMD_BONDEDS
++
++#include "gmx_simd_macros.h"
 +#endif
 +
 +/* Find a better place for this? */
 +const int cmap_coeff_matrix[] = {
 +    1, 0, -3,  2, 0, 0,  0,  0, -3,  0,  9, -6,  2,  0, -6,  4,
 +    0, 0,  0,  0, 0, 0,  0,  0,  3,  0, -9,  6, -2,  0,  6, -4,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0,  0,  9, -6,  0,  0, -6,  4,
 +    0, 0,  3, -2, 0, 0,  0,  0,  0,  0, -9,  6,  0,  0,  6, -4,
 +    0, 0,  0,  0, 1, 0, -3,  2, -2,  0,  6, -4,  1,  0, -3,  2,
 +    0, 0,  0,  0, 0, 0,  0,  0, -1,  0,  3, -2,  1,  0, -3,  2,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0,  0, -3,  2,  0,  0,  3, -2,
 +    0, 0,  0,  0, 0, 0,  3, -2,  0,  0, -6,  4,  0,  0,  3, -2,
 +    0, 1, -2,  1, 0, 0,  0,  0,  0, -3,  6, -3,  0,  2, -4,  2,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0,  3, -6,  3,  0, -2,  4, -2,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0,  0, -3,  3,  0,  0,  2, -2,
 +    0, 0, -1,  1, 0, 0,  0,  0,  0,  0,  3, -3,  0,  0, -2,  2,
 +    0, 0,  0,  0, 0, 1, -2,  1,  0, -2,  4, -2,  0,  1, -2,  1,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0, -1,  2, -1,  0,  1, -2,  1,
 +    0, 0,  0,  0, 0, 0,  0,  0,  0,  0,  1, -1,  0,  0, -1,  1,
 +    0, 0,  0,  0, 0, 0, -1,  1,  0,  0,  2, -2,  0,  0, -1,  1
 +};
 +
 +
 +
 +int glatnr(int *global_atom_index, int i)
 +{
 +    int atnr;
 +
 +    if (global_atom_index == NULL)
 +    {
 +        atnr = i + 1;
 +    }
 +    else
 +    {
 +        atnr = global_atom_index[i] + 1;
 +    }
 +
 +    return atnr;
 +}
 +
 +static int pbc_rvec_sub(const t_pbc *pbc, const rvec xi, const rvec xj, rvec dx)
 +{
 +    if (pbc)
 +    {
 +        return pbc_dx_aiuc(pbc, xi, xj, dx);
 +    }
 +    else
 +    {
 +        rvec_sub(xi, xj, dx);
 +        return CENTRAL;
 +    }
 +}
 +
++#ifdef SIMD_BONDEDS
++
++/* Below are 3 SIMD vector operations.
++ * Currently these are only used here, but they should be moved to
++ * a general SIMD include file when used elsewhere.
++ */
++
++/* SIMD inner-product of multiple vectors */
++static gmx_inline gmx_mm_pr
++gmx_iprod_pr(gmx_mm_pr ax, gmx_mm_pr ay, gmx_mm_pr az,
++             gmx_mm_pr bx, gmx_mm_pr by, gmx_mm_pr bz)
++{
++    gmx_mm_pr ret;
++
++    ret = gmx_mul_pr(ax, bx);
++    ret = gmx_madd_pr(ay, by, ret);
++    ret = gmx_madd_pr(az, bz, ret);
++
++    return ret;
++}
++
++/* SIMD norm squared of multiple vectors */
++static gmx_inline gmx_mm_pr
++gmx_norm2_pr(gmx_mm_pr ax, gmx_mm_pr ay, gmx_mm_pr az)
++{
++    gmx_mm_pr ret;
++
++    ret = gmx_mul_pr(ax, ax);
++    ret = gmx_madd_pr(ay, ay, ret);
++    ret = gmx_madd_pr(az, az, ret);
++
++    return ret;
++}
++
++/* SIMD cross-product of multiple vectors */
++static gmx_inline void
++gmx_cprod_pr(gmx_mm_pr ax, gmx_mm_pr ay, gmx_mm_pr az,
++             gmx_mm_pr bx, gmx_mm_pr by, gmx_mm_pr bz,
++             gmx_mm_pr *cx, gmx_mm_pr *cy, gmx_mm_pr *cz)
++{
++    *cx = gmx_mul_pr(ay, bz);
++    *cx = gmx_nmsub_pr(az, by, *cx);
++
++    *cy = gmx_mul_pr(az, bx);
++    *cy = gmx_nmsub_pr(ax, bz, *cy);
++
++    *cz = gmx_mul_pr(ax, by);
++    *cz = gmx_nmsub_pr(ay, bx, *cz);
++}
++
++/* SIMD PBC data structure, containing 1/boxdiag and the box vectors */
++typedef struct {
++    gmx_mm_pr inv_bzz;
++    gmx_mm_pr inv_byy;
++    gmx_mm_pr inv_bxx;
++    gmx_mm_pr bzx;
++    gmx_mm_pr bzy;
++    gmx_mm_pr bzz;
++    gmx_mm_pr byx;
++    gmx_mm_pr byy;
++    gmx_mm_pr bxx;
++} pbc_simd_t;
++
++/* Set the SIMD pbc data from a normal t_pbc struct */
++static void set_pbc_simd(const t_pbc *pbc, pbc_simd_t *pbc_simd)
++{
++    rvec inv_bdiag;
++    int  d;
++
++    /* Setting inv_bdiag to 0 effectively turns off PBC */
++    clear_rvec(inv_bdiag);
++    if (pbc != NULL)
++    {
++        for (d = 0; d < pbc->ndim_ePBC; d++)
++        {
++            inv_bdiag[d] = 1.0/pbc->box[d][d];
++        }
++    }
++
++    pbc_simd->inv_bzz = gmx_set1_pr(inv_bdiag[ZZ]);
++    pbc_simd->inv_byy = gmx_set1_pr(inv_bdiag[YY]);
++    pbc_simd->inv_bxx = gmx_set1_pr(inv_bdiag[XX]);
++
++    if (pbc != NULL)
++    {
++        pbc_simd->bzx = gmx_set1_pr(pbc->box[ZZ][XX]);
++        pbc_simd->bzy = gmx_set1_pr(pbc->box[ZZ][YY]);
++        pbc_simd->bzz = gmx_set1_pr(pbc->box[ZZ][ZZ]);
++        pbc_simd->byx = gmx_set1_pr(pbc->box[YY][XX]);
++        pbc_simd->byy = gmx_set1_pr(pbc->box[YY][YY]);
++        pbc_simd->bxx = gmx_set1_pr(pbc->box[XX][XX]);
++    }
++    else
++    {
++        pbc_simd->bzx = gmx_setzero_pr();
++        pbc_simd->bzy = gmx_setzero_pr();
++        pbc_simd->bzz = gmx_setzero_pr();
++        pbc_simd->byx = gmx_setzero_pr();
++        pbc_simd->byy = gmx_setzero_pr();
++        pbc_simd->bxx = gmx_setzero_pr();
++    }
++}
++
++/* Correct distance vector *dx,*dy,*dz for PBC using SIMD */
++static gmx_inline void
++pbc_dx_simd(gmx_mm_pr *dx, gmx_mm_pr *dy, gmx_mm_pr *dz,
++            const pbc_simd_t *pbc)
++{
++    gmx_mm_pr sh;
++
++    sh  = gmx_round_pr(gmx_mul_pr(*dz, pbc->inv_bzz));
++    *dx = gmx_nmsub_pr(sh, pbc->bzx, *dx);
++    *dy = gmx_nmsub_pr(sh, pbc->bzy, *dy);
++    *dz = gmx_nmsub_pr(sh, pbc->bzz, *dz);
++
++    sh  = gmx_round_pr(gmx_mul_pr(*dy, pbc->inv_byy));
++    *dx = gmx_nmsub_pr(sh, pbc->byx, *dx);
++    *dy = gmx_nmsub_pr(sh, pbc->byy, *dy);
++
++    sh  = gmx_round_pr(gmx_mul_pr(*dx, pbc->inv_bxx));
++    *dx = gmx_nmsub_pr(sh, pbc->bxx, *dx);
++}
++
++#endif /* SIMD_BONDEDS */
++
 +/*
 + * Morse potential bond by Frank Everdij
 + *
 + * Three parameters needed:
 + *
 + * b0 = equilibrium distance in nm
 + * be = beta in nm^-1 (actually, it's nu_e*Sqrt(2*pi*pi*mu/D_e))
 + * cb = well depth in kJ/mol
 + *
 + * Note: the potential is referenced to be +cb at infinite separation
 + *       and zero at the equilibrium distance!
 + */
 +
 +real morse_bonds(int nbonds,
 +                 const t_iatom forceatoms[], const t_iparams forceparams[],
 +                 const rvec x[], rvec f[], rvec fshift[],
 +                 const t_pbc *pbc, const t_graph *g,
 +                 real lambda, real *dvdlambda,
 +                 const t_mdatoms *md, t_fcdata *fcd,
 +                 int *global_atom_index)
 +{
 +    const real one = 1.0;
 +    const real two = 2.0;
 +    real       dr, dr2, temp, omtemp, cbomtemp, fbond, vbond, fij, vtot;
 +    real       b0, be, cb, b0A, beA, cbA, b0B, beB, cbB, L1;
 +    rvec       dx;
 +    int        i, m, ki, type, ai, aj;
 +    ivec       dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        b0A   = forceparams[type].morse.b0A;
 +        beA   = forceparams[type].morse.betaA;
 +        cbA   = forceparams[type].morse.cbA;
 +
 +        b0B   = forceparams[type].morse.b0B;
 +        beB   = forceparams[type].morse.betaB;
 +        cbB   = forceparams[type].morse.cbB;
 +
 +        L1 = one-lambda;                            /* 1 */
 +        b0 = L1*b0A + lambda*b0B;                   /* 3 */
 +        be = L1*beA + lambda*beB;                   /* 3 */
 +        cb = L1*cbA + lambda*cbB;                   /* 3 */
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx); /*   3          */
 +        dr2  = iprod(dx, dx);                       /*   5          */
 +        dr   = dr2*gmx_invsqrt(dr2);                /*  10          */
 +        temp = exp(-be*(dr-b0));                    /*  12          */
 +
 +        if (temp == one)
 +        {
 +            /* bonds are constrainted. This may _not_ include bond constraints if they are lambda dependent */
 +            *dvdlambda += cbB-cbA;
 +            continue;
 +        }
 +
 +        omtemp    = one-temp;                                                                                        /*   1          */
 +        cbomtemp  = cb*omtemp;                                                                                       /*   1          */
 +        vbond     = cbomtemp*omtemp;                                                                                 /*   1          */
 +        fbond     = -two*be*temp*cbomtemp*gmx_invsqrt(dr2);                                                          /*   9          */
 +        vtot     += vbond;                                                                                           /*   1          */
 +
 +        *dvdlambda += (cbB - cbA) * omtemp * omtemp - (2-2*omtemp)*omtemp * cb * ((b0B-b0A)*be - (beB-beA)*(dr-b0)); /* 15 */
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +
 +        for (m = 0; (m < DIM); m++)                    /*  15          */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }                                         /*  83 TOTAL    */
 +    return vtot;
 +}
 +
 +real cubic_bonds(int nbonds,
 +                 const t_iatom forceatoms[], const t_iparams forceparams[],
 +                 const rvec x[], rvec f[], rvec fshift[],
 +                 const t_pbc *pbc, const t_graph *g,
 +                 real lambda, real *dvdlambda,
 +                 const t_mdatoms *md, t_fcdata *fcd,
 +                 int *global_atom_index)
 +{
 +    const real three = 3.0;
 +    const real two   = 2.0;
 +    real       kb, b0, kcub;
 +    real       dr, dr2, dist, kdist, kdist2, fbond, vbond, fij, vtot;
 +    rvec       dx;
 +    int        i, m, ki, type, ai, aj;
 +    ivec       dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        b0   = forceparams[type].cubic.b0;
 +        kb   = forceparams[type].cubic.kb;
 +        kcub = forceparams[type].cubic.kcub;
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx);     /*   3          */
 +        dr2  = iprod(dx, dx);                           /*   5          */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        dr         = dr2*gmx_invsqrt(dr2);                  /*  10          */
 +        dist       = dr-b0;
 +        kdist      = kb*dist;
 +        kdist2     = kdist*dist;
 +
 +        vbond      = kdist2 + kcub*kdist2*dist;
 +        fbond      = -(two*kdist + three*kdist2*kcub)/dr;
 +
 +        vtot      += vbond;   /* 21 */
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)                    /*  15          */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }                                         /*  54 TOTAL    */
 +    return vtot;
 +}
 +
 +real FENE_bonds(int nbonds,
 +                const t_iatom forceatoms[], const t_iparams forceparams[],
 +                const rvec x[], rvec f[], rvec fshift[],
 +                const t_pbc *pbc, const t_graph *g,
 +                real lambda, real *dvdlambda,
 +                const t_mdatoms *md, t_fcdata *fcd,
 +                int *global_atom_index)
 +{
 +    const real half = 0.5;
 +    const real one  = 1.0;
 +    real       bm, kb;
 +    real       dr, dr2, bm2, omdr2obm2, fbond, vbond, fij, vtot;
 +    rvec       dx;
 +    int        i, m, ki, type, ai, aj;
 +    ivec       dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        bm   = forceparams[type].fene.bm;
 +        kb   = forceparams[type].fene.kb;
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx);     /*   3          */
 +        dr2  = iprod(dx, dx);                           /*   5          */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        bm2 = bm*bm;
 +
 +        if (dr2 >= bm2)
 +        {
 +            gmx_fatal(FARGS,
 +                      "r^2 (%f) >= bm^2 (%f) in FENE bond between atoms %d and %d",
 +                      dr2, bm2,
 +                      glatnr(global_atom_index, ai),
 +                      glatnr(global_atom_index, aj));
 +        }
 +
 +        omdr2obm2  = one - dr2/bm2;
 +
 +        vbond      = -half*kb*bm2*log(omdr2obm2);
 +        fbond      = -kb/omdr2obm2;
 +
 +        vtot      += vbond;   /* 35 */
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)                    /*  15          */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }                                         /*  58 TOTAL    */
 +    return vtot;
 +}
 +
 +real harmonic(real kA, real kB, real xA, real xB, real x, real lambda,
 +              real *V, real *F)
 +{
 +    const real half = 0.5;
 +    real       L1, kk, x0, dx, dx2;
 +    real       v, f, dvdlambda;
 +
 +    L1    = 1.0-lambda;
 +    kk    = L1*kA+lambda*kB;
 +    x0    = L1*xA+lambda*xB;
 +
 +    dx    = x-x0;
 +    dx2   = dx*dx;
 +
 +    f          = -kk*dx;
 +    v          = half*kk*dx2;
 +    dvdlambda  = half*(kB-kA)*dx2 + (xA-xB)*kk*dx;
 +
 +    *F    = f;
 +    *V    = v;
 +
 +    return dvdlambda;
 +
 +    /* That was 19 flops */
 +}
 +
 +
 +real bonds(int nbonds,
 +           const t_iatom forceatoms[], const t_iparams forceparams[],
 +           const rvec x[], rvec f[], rvec fshift[],
 +           const t_pbc *pbc, const t_graph *g,
 +           real lambda, real *dvdlambda,
 +           const t_mdatoms *md, t_fcdata *fcd,
 +           int *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type;
 +    real dr, dr2, fbond, vbond, fij, vtot;
 +    rvec dx;
 +    ivec dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx); /*   3      */
 +        dr2  = iprod(dx, dx);                       /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                /*  10            */
 +
 +        *dvdlambda += harmonic(forceparams[type].harmonic.krA,
 +                               forceparams[type].harmonic.krB,
 +                               forceparams[type].harmonic.rA,
 +                               forceparams[type].harmonic.rB,
 +                               dr, lambda, &vbond, &fbond); /*  19  */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +
 +        vtot  += vbond;            /* 1*/
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "BONDS: dr = %10g  vbond = %10g  fbond = %10g\n",
 +                    dr, vbond, fbond);
 +        }
 +#endif
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }               /* 59 TOTAL       */
 +    return vtot;
 +}
 +
 +real restraint_bonds(int nbonds,
 +                     const t_iatom forceatoms[], const t_iparams forceparams[],
 +                     const rvec x[], rvec f[], rvec fshift[],
 +                     const t_pbc *pbc, const t_graph *g,
 +                     real lambda, real *dvdlambda,
 +                     const t_mdatoms *md, t_fcdata *fcd,
 +                     int *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type;
 +    real dr, dr2, fbond, vbond, fij, vtot;
 +    real L1;
 +    real low, dlow, up1, dup1, up2, dup2, k, dk;
 +    real drh, drh2;
 +    rvec dx;
 +    ivec dt;
 +
 +    L1   = 1.0 - lambda;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx); /*   3      */
 +        dr2  = iprod(dx, dx);                       /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                /*  10            */
 +
 +        low  = L1*forceparams[type].restraint.lowA + lambda*forceparams[type].restraint.lowB;
 +        dlow =   -forceparams[type].restraint.lowA +        forceparams[type].restraint.lowB;
 +        up1  = L1*forceparams[type].restraint.up1A + lambda*forceparams[type].restraint.up1B;
 +        dup1 =   -forceparams[type].restraint.up1A +        forceparams[type].restraint.up1B;
 +        up2  = L1*forceparams[type].restraint.up2A + lambda*forceparams[type].restraint.up2B;
 +        dup2 =   -forceparams[type].restraint.up2A +        forceparams[type].restraint.up2B;
 +        k    = L1*forceparams[type].restraint.kA   + lambda*forceparams[type].restraint.kB;
 +        dk   =   -forceparams[type].restraint.kA   +        forceparams[type].restraint.kB;
 +        /* 24 */
 +
 +        if (dr < low)
 +        {
 +            drh         = dr - low;
 +            drh2        = drh*drh;
 +            vbond       = 0.5*k*drh2;
 +            fbond       = -k*drh;
 +            *dvdlambda += 0.5*dk*drh2 - k*dlow*drh;
 +        } /* 11 */
 +        else if (dr <= up1)
 +        {
 +            vbond = 0;
 +            fbond = 0;
 +        }
 +        else if (dr <= up2)
 +        {
 +            drh         = dr - up1;
 +            drh2        = drh*drh;
 +            vbond       = 0.5*k*drh2;
 +            fbond       = -k*drh;
 +            *dvdlambda += 0.5*dk*drh2 - k*dup1*drh;
 +        } /* 11       */
 +        else
 +        {
 +            drh         = dr - up2;
 +            vbond       = k*(up2 - up1)*(0.5*(up2 - up1) + drh);
 +            fbond       = -k*(up2 - up1);
 +            *dvdlambda += dk*(up2 - up1)*(0.5*(up2 - up1) + drh)
 +                + k*(dup2 - dup1)*(up2 - up1 + drh)
 +                - k*(up2 - up1)*dup2;
 +        }
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        vtot  += vbond;            /* 1*/
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "BONDS: dr = %10g  vbond = %10g  fbond = %10g\n",
 +                    dr, vbond, fbond);
 +        }
 +#endif
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)             /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }                   /* 59 TOTAL   */
 +
 +    return vtot;
 +}
 +
 +real polarize(int nbonds,
 +              const t_iatom forceatoms[], const t_iparams forceparams[],
 +              const rvec x[], rvec f[], rvec fshift[],
 +              const t_pbc *pbc, const t_graph *g,
 +              real lambda, real *dvdlambda,
 +              const t_mdatoms *md, t_fcdata *fcd,
 +              int *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type;
 +    real dr, dr2, fbond, vbond, fij, vtot, ksh;
 +    rvec dx;
 +    ivec dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ksh  = sqr(md->chargeA[aj])*ONE_4PI_EPS0/forceparams[type].polarize.alpha;
 +        if (debug)
 +        {
 +            fprintf(debug, "POL: local ai = %d aj = %d ksh = %.3f\n", ai, aj, ksh);
 +        }
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx);                         /*   3      */
 +        dr2  = iprod(dx, dx);                                               /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                                        /*  10            */
 +
 +        *dvdlambda += harmonic(ksh, ksh, 0, 0, dr, lambda, &vbond, &fbond); /*  19  */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        vtot  += vbond;            /* 1*/
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }               /* 59 TOTAL       */
 +    return vtot;
 +}
 +
 +real anharm_polarize(int nbonds,
 +                     const t_iatom forceatoms[], const t_iparams forceparams[],
 +                     const rvec x[], rvec f[], rvec fshift[],
 +                     const t_pbc *pbc, const t_graph *g,
 +                     real lambda, real *dvdlambda,
 +                     const t_mdatoms *md, t_fcdata *fcd,
 +                     int *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type;
 +    real dr, dr2, fbond, vbond, fij, vtot, ksh, khyp, drcut, ddr, ddr3;
 +    rvec dx;
 +    ivec dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type  = forceatoms[i++];
 +        ai    = forceatoms[i++];
 +        aj    = forceatoms[i++];
 +        ksh   = sqr(md->chargeA[aj])*ONE_4PI_EPS0/forceparams[type].anharm_polarize.alpha; /* 7*/
 +        khyp  = forceparams[type].anharm_polarize.khyp;
 +        drcut = forceparams[type].anharm_polarize.drcut;
 +        if (debug)
 +        {
 +            fprintf(debug, "POL: local ai = %d aj = %d ksh = %.3f\n", ai, aj, ksh);
 +        }
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx);                         /*   3      */
 +        dr2  = iprod(dx, dx);                                               /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                                        /*  10            */
 +
 +        *dvdlambda += harmonic(ksh, ksh, 0, 0, dr, lambda, &vbond, &fbond); /*  19  */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        if (dr > drcut)
 +        {
 +            ddr    = dr-drcut;
 +            ddr3   = ddr*ddr*ddr;
 +            vbond += khyp*ddr*ddr3;
 +            fbond -= 4*khyp*ddr3;
 +        }
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +        vtot  += vbond;            /* 1*/
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }               /* 72 TOTAL       */
 +    return vtot;
 +}
 +
 +real water_pol(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const rvec x[], rvec f[], rvec fshift[],
 +               const t_pbc *pbc, const t_graph *g,
 +               real lambda, real *dvdlambda,
 +               const t_mdatoms *md, t_fcdata *fcd,
 +               int *global_atom_index)
 +{
 +    /* This routine implements anisotropic polarizibility for water, through
 +     * a shell connected to a dummy with spring constant that differ in the
 +     * three spatial dimensions in the molecular frame.
 +     */
 +    int  i, m, aO, aH1, aH2, aD, aS, type, type0;
 +    rvec dOH1, dOH2, dHH, dOD, dDS, nW, kk, dx, kdx, proj;
 +#ifdef DEBUG
 +    rvec df;
 +#endif
 +    real vtot, fij, r_HH, r_OD, r_nW, tx, ty, tz, qS;
 +
 +    vtot = 0.0;
 +    if (nbonds > 0)
 +    {
 +        type0  = forceatoms[0];
 +        aS     = forceatoms[5];
 +        qS     = md->chargeA[aS];
 +        kk[XX] = sqr(qS)*ONE_4PI_EPS0/forceparams[type0].wpol.al_x;
 +        kk[YY] = sqr(qS)*ONE_4PI_EPS0/forceparams[type0].wpol.al_y;
 +        kk[ZZ] = sqr(qS)*ONE_4PI_EPS0/forceparams[type0].wpol.al_z;
 +        r_HH   = 1.0/forceparams[type0].wpol.rHH;
 +        r_OD   = 1.0/forceparams[type0].wpol.rOD;
 +        if (debug)
 +        {
 +            fprintf(debug, "WPOL: qS  = %10.5f aS = %5d\n", qS, aS);
 +            fprintf(debug, "WPOL: kk  = %10.3f        %10.3f        %10.3f\n",
 +                    kk[XX], kk[YY], kk[ZZ]);
 +            fprintf(debug, "WPOL: rOH = %10.3f  rHH = %10.3f  rOD = %10.3f\n",
 +                    forceparams[type0].wpol.rOH,
 +                    forceparams[type0].wpol.rHH,
 +                    forceparams[type0].wpol.rOD);
 +        }
 +        for (i = 0; (i < nbonds); i += 6)
 +        {
 +            type = forceatoms[i];
 +            if (type != type0)
 +            {
 +                gmx_fatal(FARGS, "Sorry, type = %d, type0 = %d, file = %s, line = %d",
 +                          type, type0, __FILE__, __LINE__);
 +            }
 +            aO   = forceatoms[i+1];
 +            aH1  = forceatoms[i+2];
 +            aH2  = forceatoms[i+3];
 +            aD   = forceatoms[i+4];
 +            aS   = forceatoms[i+5];
 +
 +            /* Compute vectors describing the water frame */
 +            rvec_sub(x[aH1], x[aO], dOH1);
 +            rvec_sub(x[aH2], x[aO], dOH2);
 +            rvec_sub(x[aH2], x[aH1], dHH);
 +            rvec_sub(x[aD], x[aO], dOD);
 +            rvec_sub(x[aS], x[aD], dDS);
 +            cprod(dOH1, dOH2, nW);
 +
 +            /* Compute inverse length of normal vector
 +             * (this one could be precomputed, but I'm too lazy now)
 +             */
 +            r_nW = gmx_invsqrt(iprod(nW, nW));
 +            /* This is for precision, but does not make a big difference,
 +             * it can go later.
 +             */
 +            r_OD = gmx_invsqrt(iprod(dOD, dOD));
 +
 +            /* Normalize the vectors in the water frame */
 +            svmul(r_nW, nW, nW);
 +            svmul(r_HH, dHH, dHH);
 +            svmul(r_OD, dOD, dOD);
 +
 +            /* Compute displacement of shell along components of the vector */
 +            dx[ZZ] = iprod(dDS, dOD);
 +            /* Compute projection on the XY plane: dDS - dx[ZZ]*dOD */
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                proj[m] = dDS[m]-dx[ZZ]*dOD[m];
 +            }
 +
 +            /*dx[XX] = iprod(dDS,nW);
 +               dx[YY] = iprod(dDS,dHH);*/
 +            dx[XX] = iprod(proj, nW);
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                proj[m] -= dx[XX]*nW[m];
 +            }
 +            dx[YY] = iprod(proj, dHH);
 +            /*#define DEBUG*/
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "WPOL: dx2=%10g  dy2=%10g  dz2=%10g  sum=%10g  dDS^2=%10g\n",
 +                        sqr(dx[XX]), sqr(dx[YY]), sqr(dx[ZZ]), iprod(dx, dx), iprod(dDS, dDS));
 +                fprintf(debug, "WPOL: dHH=(%10g,%10g,%10g)\n", dHH[XX], dHH[YY], dHH[ZZ]);
 +                fprintf(debug, "WPOL: dOD=(%10g,%10g,%10g), 1/r_OD = %10g\n",
 +                        dOD[XX], dOD[YY], dOD[ZZ], 1/r_OD);
 +                fprintf(debug, "WPOL: nW =(%10g,%10g,%10g), 1/r_nW = %10g\n",
 +                        nW[XX], nW[YY], nW[ZZ], 1/r_nW);
 +                fprintf(debug, "WPOL: dx  =%10g, dy  =%10g, dz  =%10g\n",
 +                        dx[XX], dx[YY], dx[ZZ]);
 +                fprintf(debug, "WPOL: dDSx=%10g, dDSy=%10g, dDSz=%10g\n",
 +                        dDS[XX], dDS[YY], dDS[ZZ]);
 +            }
 +#endif
 +            /* Now compute the forces and energy */
 +            kdx[XX] = kk[XX]*dx[XX];
 +            kdx[YY] = kk[YY]*dx[YY];
 +            kdx[ZZ] = kk[ZZ]*dx[ZZ];
 +            vtot   += iprod(dx, kdx);
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                /* This is a tensor operation but written out for speed */
 +                tx        =  nW[m]*kdx[XX];
 +                ty        = dHH[m]*kdx[YY];
 +                tz        = dOD[m]*kdx[ZZ];
 +                fij       = -tx-ty-tz;
 +#ifdef DEBUG
 +                df[m] = fij;
 +#endif
 +                f[aS][m] += fij;
 +                f[aD][m] -= fij;
 +            }
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "WPOL: vwpol=%g\n", 0.5*iprod(dx, kdx));
 +                fprintf(debug, "WPOL: df = (%10g, %10g, %10g)\n", df[XX], df[YY], df[ZZ]);
 +            }
 +#endif
 +        }
 +    }
 +    return 0.5*vtot;
 +}
 +
 +static real do_1_thole(const rvec xi, const rvec xj, rvec fi, rvec fj,
 +                       const t_pbc *pbc, real qq,
 +                       rvec fshift[], real afac)
 +{
 +    rvec r12;
 +    real r12sq, r12_1, r12n, r12bar, v0, v1, fscal, ebar, fff;
 +    int  m, t;
 +
 +    t      = pbc_rvec_sub(pbc, xi, xj, r12);                      /*  3 */
 +
 +    r12sq  = iprod(r12, r12);                                     /*  5 */
 +    r12_1  = gmx_invsqrt(r12sq);                                  /*  5 */
 +    r12bar = afac/r12_1;                                          /*  5 */
 +    v0     = qq*ONE_4PI_EPS0*r12_1;                               /*  2 */
 +    ebar   = exp(-r12bar);                                        /*  5 */
 +    v1     = (1-(1+0.5*r12bar)*ebar);                             /*  4 */
 +    fscal  = ((v0*r12_1)*v1 - v0*0.5*afac*ebar*(r12bar+1))*r12_1; /* 9 */
 +    if (debug)
 +    {
 +        fprintf(debug, "THOLE: v0 = %.3f v1 = %.3f r12= % .3f r12bar = %.3f fscal = %.3f  ebar = %.3f\n", v0, v1, 1/r12_1, r12bar, fscal, ebar);
 +    }
 +
 +    for (m = 0; (m < DIM); m++)
 +    {
 +        fff                 = fscal*r12[m];
 +        fi[m]              += fff;
 +        fj[m]              -= fff;
 +        fshift[t][m]       += fff;
 +        fshift[CENTRAL][m] -= fff;
 +    }             /* 15 */
 +
 +    return v0*v1; /* 1 */
 +    /* 54 */
 +}
 +
 +real thole_pol(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const rvec x[], rvec f[], rvec fshift[],
 +               const t_pbc *pbc, const t_graph *g,
 +               real lambda, real *dvdlambda,
 +               const t_mdatoms *md, t_fcdata *fcd,
 +               int *global_atom_index)
 +{
 +    /* Interaction between two pairs of particles with opposite charge */
 +    int  i, type, a1, da1, a2, da2;
 +    real q1, q2, qq, a, al1, al2, afac;
 +    real V = 0;
 +
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type  = forceatoms[i++];
 +        a1    = forceatoms[i++];
 +        da1   = forceatoms[i++];
 +        a2    = forceatoms[i++];
 +        da2   = forceatoms[i++];
 +        q1    = md->chargeA[da1];
 +        q2    = md->chargeA[da2];
 +        a     = forceparams[type].thole.a;
 +        al1   = forceparams[type].thole.alpha1;
 +        al2   = forceparams[type].thole.alpha2;
 +        qq    = q1*q2;
 +        afac  = a*pow(al1*al2, -1.0/6.0);
 +        V    += do_1_thole(x[a1], x[a2], f[a1], f[a2], pbc, qq, fshift, afac);
 +        V    += do_1_thole(x[da1], x[a2], f[da1], f[a2], pbc, -qq, fshift, afac);
 +        V    += do_1_thole(x[a1], x[da2], f[a1], f[da2], pbc, -qq, fshift, afac);
 +        V    += do_1_thole(x[da1], x[da2], f[da1], f[da2], pbc, qq, fshift, afac);
 +    }
 +    /* 290 flops */
 +    return V;
 +}
 +
 +real bond_angle(const rvec xi, const rvec xj, const rvec xk, const t_pbc *pbc,
 +                rvec r_ij, rvec r_kj, real *costh,
 +                int *t1, int *t2)
 +/* Return value is the angle between the bonds i-j and j-k */
 +{
 +    /* 41 FLOPS */
 +    real th;
 +
 +    *t1 = pbc_rvec_sub(pbc, xi, xj, r_ij); /*  3              */
 +    *t2 = pbc_rvec_sub(pbc, xk, xj, r_kj); /*  3              */
 +
 +    *costh = cos_angle(r_ij, r_kj);        /* 25              */
 +    th     = acos(*costh);                 /* 10              */
 +    /* 41 TOTAL       */
 +    return th;
 +}
 +
 +real angles(int nbonds,
 +            const t_iatom forceatoms[], const t_iparams forceparams[],
 +            const rvec x[], rvec f[], rvec fshift[],
 +            const t_pbc *pbc, const t_graph *g,
 +            real lambda, real *dvdlambda,
 +            const t_mdatoms *md, t_fcdata *fcd,
 +            int *global_atom_index)
 +{
 +    int  i, ai, aj, ak, t1, t2, type;
 +    rvec r_ij, r_kj;
 +    real cos_theta, cos_theta2, theta, dVdt, va, vtot;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; i < nbonds; )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        theta  = bond_angle(x[ai], x[aj], x[ak], pbc,
 +                            r_ij, r_kj, &cos_theta, &t1, &t2);  /*  41                */
 +
 +        *dvdlambda += harmonic(forceparams[type].harmonic.krA,
 +                               forceparams[type].harmonic.krB,
 +                               forceparams[type].harmonic.rA*DEG2RAD,
 +                               forceparams[type].harmonic.rB*DEG2RAD,
 +                               theta, lambda, &va, &dVdt);  /*  21  */
 +        vtot += va;
 +
 +        cos_theta2 = sqr(cos_theta);
 +        if (cos_theta2 < 1)
 +        {
 +            int  m;
 +            real st, sth;
 +            real cik, cii, ckk;
 +            real nrkj2, nrij2;
 +            real nrkj_1, nrij_1;
 +            rvec f_i, f_j, f_k;
 +
 +            st  = dVdt*gmx_invsqrt(1 - cos_theta2); /*  12            */
 +            sth = st*cos_theta;                     /*   1            */
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "ANGLES: theta = %10g  vth = %10g  dV/dtheta = %10g\n",
 +                        theta*RAD2DEG, va, dVdt);
 +            }
 +#endif
 +            nrij2 = iprod(r_ij, r_ij);      /*   5            */
 +            nrkj2 = iprod(r_kj, r_kj);      /*   5            */
 +
 +            nrij_1 = gmx_invsqrt(nrij2);    /*  10            */
 +            nrkj_1 = gmx_invsqrt(nrkj2);    /*  10            */
 +
 +            cik = st*nrij_1*nrkj_1;         /*   2            */
 +            cii = sth*nrij_1*nrij_1;        /*   2            */
 +            ckk = sth*nrkj_1*nrkj_1;        /*   2            */
 +
 +            for (m = 0; m < DIM; m++)
 +            {           /*  39                */
 +                f_i[m]    = -(cik*r_kj[m] - cii*r_ij[m]);
 +                f_k[m]    = -(cik*r_ij[m] - ckk*r_kj[m]);
 +                f_j[m]    = -f_i[m] - f_k[m];
 +                f[ai][m] += f_i[m];
 +                f[aj][m] += f_j[m];
 +                f[ak][m] += f_k[m];
 +            }
 +            if (g != NULL)
 +            {
 +                copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +                t1 = IVEC2IS(dt_ij);
 +                t2 = IVEC2IS(dt_kj);
 +            }
 +            rvec_inc(fshift[t1], f_i);
 +            rvec_inc(fshift[CENTRAL], f_j);
 +            rvec_inc(fshift[t2], f_k);
 +        }                                           /* 161 TOTAL      */
 +    }
 +
 +    return vtot;
 +}
 +
++#ifdef SIMD_BONDEDS
++
++/* As angles, but using SIMD to calculate many dihedrals at once.
++ * This routines does not calculate energies and shift forces.
++ */
++static gmx_inline void
++angles_noener_simd(int nbonds,
++                   const t_iatom forceatoms[], const t_iparams forceparams[],
++                   const rvec x[], rvec f[],
++                   const t_pbc *pbc, const t_graph *g,
++                   real lambda,
++                   const t_mdatoms *md, t_fcdata *fcd,
++                   int *global_atom_index)
++{
++#define UNROLL GMX_SIMD_WIDTH_HERE
++    const int      nfa1 = 4;
++    int            i, iu, s, m;
++    int            type, ai[UNROLL], aj[UNROLL], ak[UNROLL];
++    real           coeff_array[2*UNROLL+UNROLL], *coeff;
++    real           dr_array[2*DIM*UNROLL+UNROLL], *dr;
++    real           f_buf_array[6*UNROLL+UNROLL], *f_buf;
++    gmx_mm_pr      k_S, theta0_S;
++    gmx_mm_pr      rijx_S, rijy_S, rijz_S;
++    gmx_mm_pr      rkjx_S, rkjy_S, rkjz_S;
++    gmx_mm_pr      one_S;
++    gmx_mm_pr      rij_rkj_S;
++    gmx_mm_pr      nrij2_S, nrij_1_S;
++    gmx_mm_pr      nrkj2_S, nrkj_1_S;
++    gmx_mm_pr      cos_S, sin_S;
++    gmx_mm_pr      theta_S;
++    gmx_mm_pr      st_S, sth_S;
++    gmx_mm_pr      cik_S, cii_S, ckk_S;
++    gmx_mm_pr      f_ix_S, f_iy_S, f_iz_S;
++    gmx_mm_pr      f_kx_S, f_ky_S, f_kz_S;
++    pbc_simd_t     pbc_simd;
++
++    /* Ensure register memory alignment */
++    coeff = gmx_simd_align_real(coeff_array);
++    dr    = gmx_simd_align_real(dr_array);
++    f_buf = gmx_simd_align_real(f_buf_array);
++
++    set_pbc_simd(pbc,&pbc_simd);
++
++    one_S = gmx_set1_pr(1.0);
++
++    /* nbonds is the number of angles times nfa1, here we step UNROLL angles */
++    for (i = 0; (i < nbonds); i += UNROLL*nfa1)
++    {
++        /* Collect atoms for UNROLL angles.
++         * iu indexes into forceatoms, we should not let iu go beyond nbonds.
++         */
++        iu = i;
++        for (s = 0; s < UNROLL; s++)
++        {
++            type  = forceatoms[iu];
++            ai[s] = forceatoms[iu+1];
++            aj[s] = forceatoms[iu+2];
++            ak[s] = forceatoms[iu+3];
++
++            coeff[s]        = forceparams[type].harmonic.krA;
++            coeff[UNROLL+s] = forceparams[type].harmonic.rA*DEG2RAD;
++
++            /* If you can't use pbc_dx_simd below for PBC, e.g. because
++             * you can't round in SIMD, use pbc_rvec_sub here.
++             */
++            /* Store the non PBC corrected distances packed and aligned */
++            for (m = 0; m < DIM; m++)
++            {
++                dr[s +      m *UNROLL] = x[ai[s]][m] - x[aj[s]][m];
++                dr[s + (DIM+m)*UNROLL] = x[ak[s]][m] - x[aj[s]][m];
++            }
++
++            /* At the end fill the arrays with identical entries */
++            if (iu + nfa1 < nbonds)
++            {
++                iu += nfa1;
++            }
++        }
++
++        k_S       = gmx_load_pr(coeff);
++        theta0_S  = gmx_load_pr(coeff+UNROLL);
++
++        rijx_S    = gmx_load_pr(dr + 0*UNROLL);
++        rijy_S    = gmx_load_pr(dr + 1*UNROLL);
++        rijz_S    = gmx_load_pr(dr + 2*UNROLL);
++        rkjx_S    = gmx_load_pr(dr + 3*UNROLL);
++        rkjy_S    = gmx_load_pr(dr + 4*UNROLL);
++        rkjz_S    = gmx_load_pr(dr + 5*UNROLL);
++
++        pbc_dx_simd(&rijx_S, &rijy_S, &rijz_S, &pbc_simd);
++        pbc_dx_simd(&rkjx_S, &rkjy_S, &rkjz_S, &pbc_simd);
++
++        rij_rkj_S = gmx_iprod_pr(rijx_S, rijy_S, rijz_S,
++                                 rkjx_S, rkjy_S, rkjz_S);
++
++        nrij2_S   = gmx_norm2_pr(rijx_S, rijy_S, rijz_S);
++        nrkj2_S   = gmx_norm2_pr(rkjx_S, rkjy_S, rkjz_S);
++
++        nrij_1_S  = gmx_invsqrt_pr(nrij2_S);
++        nrkj_1_S  = gmx_invsqrt_pr(nrkj2_S);
++
++        cos_S     = gmx_mul_pr(rij_rkj_S, gmx_mul_pr(nrij_1_S, nrkj_1_S));
++
++        theta_S   = gmx_acos_pr(cos_S);
++
++        sin_S     = gmx_invsqrt_pr(gmx_max_pr(gmx_sub_pr(one_S, gmx_mul_pr(cos_S, cos_S)),
++                                              gmx_setzero_pr()));
++        st_S      = gmx_mul_pr(gmx_mul_pr(k_S, gmx_sub_pr(theta0_S, theta_S)),
++                               sin_S);
++        sth_S     = gmx_mul_pr(st_S, cos_S);
++
++        cik_S     = gmx_mul_pr(st_S,  gmx_mul_pr(nrij_1_S, nrkj_1_S));
++        cii_S     = gmx_mul_pr(sth_S, gmx_mul_pr(nrij_1_S, nrij_1_S));
++        ckk_S     = gmx_mul_pr(sth_S, gmx_mul_pr(nrkj_1_S, nrkj_1_S));
++
++        f_ix_S    = gmx_mul_pr(cii_S, rijx_S);
++        f_ix_S    = gmx_nmsub_pr(cik_S, rkjx_S, f_ix_S);
++        f_iy_S    = gmx_mul_pr(cii_S, rijy_S);
++        f_iy_S    = gmx_nmsub_pr(cik_S, rkjy_S, f_iy_S);
++        f_iz_S    = gmx_mul_pr(cii_S, rijz_S);
++        f_iz_S    = gmx_nmsub_pr(cik_S, rkjz_S, f_iz_S);
++        f_kx_S    = gmx_mul_pr(ckk_S, rkjx_S);
++        f_kx_S    = gmx_nmsub_pr(cik_S, rijx_S, f_kx_S);
++        f_ky_S    = gmx_mul_pr(ckk_S, rkjy_S);
++        f_ky_S    = gmx_nmsub_pr(cik_S, rijy_S, f_ky_S);
++        f_kz_S    = gmx_mul_pr(ckk_S, rkjz_S);
++        f_kz_S    = gmx_nmsub_pr(cik_S, rijz_S, f_kz_S);
++
++        gmx_store_pr(f_buf + 0*UNROLL, f_ix_S);
++        gmx_store_pr(f_buf + 1*UNROLL, f_iy_S);
++        gmx_store_pr(f_buf + 2*UNROLL, f_iz_S);
++        gmx_store_pr(f_buf + 3*UNROLL, f_kx_S);
++        gmx_store_pr(f_buf + 4*UNROLL, f_ky_S);
++        gmx_store_pr(f_buf + 5*UNROLL, f_kz_S);
++
++        iu = i;
++        s  = 0;
++        do
++        {
++            for (m = 0; m < DIM; m++)
++            {
++                f[ai[s]][m] += f_buf[s + m*UNROLL];
++                f[aj[s]][m] -= f_buf[s + m*UNROLL] + f_buf[s + (DIM+m)*UNROLL];
++                f[ak[s]][m] += f_buf[s + (DIM+m)*UNROLL];
++            }
++            s++;
++            iu += nfa1;
++        }
++        while (s < UNROLL && iu < nbonds);
++    }
++#undef UNROLL
++}
++
++#endif /* SIMD_BONDEDS */
++
 +real linear_angles(int nbonds,
 +                   const t_iatom forceatoms[], const t_iparams forceparams[],
 +                   const rvec x[], rvec f[], rvec fshift[],
 +                   const t_pbc *pbc, const t_graph *g,
 +                   real lambda, real *dvdlambda,
 +                   const t_mdatoms *md, t_fcdata *fcd,
 +                   int *global_atom_index)
 +{
 +    int  i, m, ai, aj, ak, t1, t2, type;
 +    rvec f_i, f_j, f_k;
 +    real L1, kA, kB, aA, aB, dr, dr2, va, vtot, a, b, klin;
 +    ivec jt, dt_ij, dt_kj;
 +    rvec r_ij, r_kj, r_ik, dx;
 +
 +    L1   = 1-lambda;
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        kA   = forceparams[type].linangle.klinA;
 +        kB   = forceparams[type].linangle.klinB;
 +        klin = L1*kA + lambda*kB;
 +
 +        aA   = forceparams[type].linangle.aA;
 +        aB   = forceparams[type].linangle.aB;
 +        a    = L1*aA+lambda*aB;
 +        b    = 1-a;
 +
 +        t1 = pbc_rvec_sub(pbc, x[ai], x[aj], r_ij);
 +        t2 = pbc_rvec_sub(pbc, x[ak], x[aj], r_kj);
 +        rvec_sub(r_ij, r_kj, r_ik);
 +
 +        dr2 = 0;
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            dr        = -a * r_ij[m] - b * r_kj[m];
 +            dr2      += dr*dr;
 +            dx[m]     = dr;
 +            f_i[m]    = a*klin*dr;
 +            f_k[m]    = b*klin*dr;
 +            f_j[m]    = -(f_i[m]+f_k[m]);
 +            f[ai][m] += f_i[m];
 +            f[aj][m] += f_j[m];
 +            f[ak][m] += f_k[m];
 +        }
 +        va          = 0.5*klin*dr2;
 +        *dvdlambda += 0.5*(kB-kA)*dr2 + klin*(aB-aA)*iprod(dx, r_ik);
 +
 +        vtot += va;
 +
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +        }
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);
 +    }                                         /* 57 TOTAL     */
 +    return vtot;
 +}
 +
 +real urey_bradley(int nbonds,
 +                  const t_iatom forceatoms[], const t_iparams forceparams[],
 +                  const rvec x[], rvec f[], rvec fshift[],
 +                  const t_pbc *pbc, const t_graph *g,
 +                  real lambda, real *dvdlambda,
 +                  const t_mdatoms *md, t_fcdata *fcd,
 +                  int *global_atom_index)
 +{
 +    int  i, m, ai, aj, ak, t1, t2, type, ki;
 +    rvec r_ij, r_kj, r_ik;
 +    real cos_theta, cos_theta2, theta;
 +    real dVdt, va, vtot, dr, dr2, vbond, fbond, fik;
 +    real kthA, th0A, kUBA, r13A, kthB, th0B, kUBB, r13B;
 +    ivec jt, dt_ij, dt_kj, dt_ik;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type  = forceatoms[i++];
 +        ai    = forceatoms[i++];
 +        aj    = forceatoms[i++];
 +        ak    = forceatoms[i++];
 +        th0A  = forceparams[type].u_b.thetaA*DEG2RAD;
 +        kthA  = forceparams[type].u_b.kthetaA;
 +        r13A  = forceparams[type].u_b.r13A;
 +        kUBA  = forceparams[type].u_b.kUBA;
 +        th0B  = forceparams[type].u_b.thetaB*DEG2RAD;
 +        kthB  = forceparams[type].u_b.kthetaB;
 +        r13B  = forceparams[type].u_b.r13B;
 +        kUBB  = forceparams[type].u_b.kUBB;
 +
 +        theta  = bond_angle(x[ai], x[aj], x[ak], pbc,
 +                            r_ij, r_kj, &cos_theta, &t1, &t2);                     /*  41             */
 +
 +        *dvdlambda += harmonic(kthA, kthB, th0A, th0B, theta, lambda, &va, &dVdt); /*  21  */
 +        vtot       += va;
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[ak], r_ik);                               /*   3      */
 +        dr2  = iprod(r_ik, r_ik);                                                   /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                                                /*  10            */
 +
 +        *dvdlambda += harmonic(kUBA, kUBB, r13A, r13B, dr, lambda, &vbond, &fbond); /*  19  */
 +
 +        cos_theta2 = sqr(cos_theta);                                                /*   1            */
 +        if (cos_theta2 < 1)
 +        {
 +            real st, sth;
 +            real cik, cii, ckk;
 +            real nrkj2, nrij2;
 +            rvec f_i, f_j, f_k;
 +
 +            st  = dVdt*gmx_invsqrt(1 - cos_theta2); /*  12            */
 +            sth = st*cos_theta;                     /*   1            */
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "ANGLES: theta = %10g  vth = %10g  dV/dtheta = %10g\n",
 +                        theta*RAD2DEG, va, dVdt);
 +            }
 +#endif
 +            nrkj2 = iprod(r_kj, r_kj);  /*   5                */
 +            nrij2 = iprod(r_ij, r_ij);
 +
 +            cik = st*gmx_invsqrt(nrkj2*nrij2); /*  12         */
 +            cii = sth/nrij2;                   /*  10         */
 +            ckk = sth/nrkj2;                   /*  10         */
 +
 +            for (m = 0; (m < DIM); m++)        /*  39         */
 +            {
 +                f_i[m]    = -(cik*r_kj[m]-cii*r_ij[m]);
 +                f_k[m]    = -(cik*r_ij[m]-ckk*r_kj[m]);
 +                f_j[m]    = -f_i[m]-f_k[m];
 +                f[ai][m] += f_i[m];
 +                f[aj][m] += f_j[m];
 +                f[ak][m] += f_k[m];
 +            }
 +            if (g)
 +            {
 +                copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +                t1 = IVEC2IS(dt_ij);
 +                t2 = IVEC2IS(dt_kj);
 +            }
 +            rvec_inc(fshift[t1], f_i);
 +            rvec_inc(fshift[CENTRAL], f_j);
 +            rvec_inc(fshift[t2], f_k);
 +        }                                       /* 161 TOTAL  */
 +        /* Time for the bond calculations */
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +        vtot  += vbond;            /* 1*/
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, ak), dt_ik);
 +            ki = IVEC2IS(dt_ik);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fik                 = fbond*r_ik[m];
 +            f[ai][m]           += fik;
 +            f[ak][m]           -= fik;
 +            fshift[ki][m]      += fik;
 +            fshift[CENTRAL][m] -= fik;
 +        }
 +    }
 +    return vtot;
 +}
 +
 +real quartic_angles(int nbonds,
 +                    const t_iatom forceatoms[], const t_iparams forceparams[],
 +                    const rvec x[], rvec f[], rvec fshift[],
 +                    const t_pbc *pbc, const t_graph *g,
 +                    real lambda, real *dvdlambda,
 +                    const t_mdatoms *md, t_fcdata *fcd,
 +                    int *global_atom_index)
 +{
 +    int  i, j, ai, aj, ak, t1, t2, type;
 +    rvec r_ij, r_kj;
 +    real cos_theta, cos_theta2, theta, dt, dVdt, va, dtp, c, vtot;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        theta  = bond_angle(x[ai], x[aj], x[ak], pbc,
 +                            r_ij, r_kj, &cos_theta, &t1, &t2); /*  41         */
 +
 +        dt = theta - forceparams[type].qangle.theta*DEG2RAD;   /* 2          */
 +
 +        dVdt = 0;
 +        va   = forceparams[type].qangle.c[0];
 +        dtp  = 1.0;
 +        for (j = 1; j <= 4; j++)
 +        {
 +            c     = forceparams[type].qangle.c[j];
 +            dVdt -= j*c*dtp;
 +            dtp  *= dt;
 +            va   += c*dtp;
 +        }
 +        /* 20 */
 +
 +        vtot += va;
 +
 +        cos_theta2 = sqr(cos_theta);            /*   1                */
 +        if (cos_theta2 < 1)
 +        {
 +            int  m;
 +            real st, sth;
 +            real cik, cii, ckk;
 +            real nrkj2, nrij2;
 +            rvec f_i, f_j, f_k;
 +
 +            st  = dVdt*gmx_invsqrt(1 - cos_theta2); /*  12            */
 +            sth = st*cos_theta;                     /*   1            */
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "ANGLES: theta = %10g  vth = %10g  dV/dtheta = %10g\n",
 +                        theta*RAD2DEG, va, dVdt);
 +            }
 +#endif
 +            nrkj2 = iprod(r_kj, r_kj);  /*   5                */
 +            nrij2 = iprod(r_ij, r_ij);
 +
 +            cik = st*gmx_invsqrt(nrkj2*nrij2); /*  12         */
 +            cii = sth/nrij2;                   /*  10         */
 +            ckk = sth/nrkj2;                   /*  10         */
 +
 +            for (m = 0; (m < DIM); m++)        /*  39         */
 +            {
 +                f_i[m]    = -(cik*r_kj[m]-cii*r_ij[m]);
 +                f_k[m]    = -(cik*r_ij[m]-ckk*r_kj[m]);
 +                f_j[m]    = -f_i[m]-f_k[m];
 +                f[ai][m] += f_i[m];
 +                f[aj][m] += f_j[m];
 +                f[ak][m] += f_k[m];
 +            }
 +            if (g)
 +            {
 +                copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +                t1 = IVEC2IS(dt_ij);
 +                t2 = IVEC2IS(dt_kj);
 +            }
 +            rvec_inc(fshift[t1], f_i);
 +            rvec_inc(fshift[CENTRAL], f_j);
 +            rvec_inc(fshift[t2], f_k);
 +        }                                       /* 153 TOTAL  */
 +    }
 +    return vtot;
 +}
 +
 +real dih_angle(const rvec xi, const rvec xj, const rvec xk, const rvec xl,
 +               const t_pbc *pbc,
 +               rvec r_ij, rvec r_kj, rvec r_kl, rvec m, rvec n,
 +               real *sign, int *t1, int *t2, int *t3)
 +{
 +    real ipr, phi;
 +
 +    *t1 = pbc_rvec_sub(pbc, xi, xj, r_ij); /*  3        */
 +    *t2 = pbc_rvec_sub(pbc, xk, xj, r_kj); /*  3              */
 +    *t3 = pbc_rvec_sub(pbc, xk, xl, r_kl); /*  3              */
 +
 +    cprod(r_ij, r_kj, m);                  /*  9        */
 +    cprod(r_kj, r_kl, n);                  /*  9              */
 +    phi     = gmx_angle(m, n);             /* 49 (assuming 25 for atan2) */
 +    ipr     = iprod(r_ij, n);              /*  5        */
 +    (*sign) = (ipr < 0.0) ? -1.0 : 1.0;
 +    phi     = (*sign)*phi;                 /*  1              */
 +    /* 82 TOTAL       */
 +    return phi;
 +}
 +
 +
- /* x86 SIMD cross-product of 4 float vectors */
- #define GMX_MM_CPROD_PS(ax, ay, az, bx, by, bz, cx, cy, cz)        \
-     {                                                          \
-         cx = _mm_sub_ps(_mm_mul_ps(ay, bz), _mm_mul_ps(az, by));  \
-         cy = _mm_sub_ps(_mm_mul_ps(az, bx), _mm_mul_ps(ax, bz));  \
-         cz = _mm_sub_ps(_mm_mul_ps(ax, by), _mm_mul_ps(ay, bx));  \
-     }
- /* load 4 rvec's into 3 x86 SIMD float registers */
- #define load_rvec4(r0, r1, r2, r3, rx_SSE, ry_SSE, rz_SSE)          \
-     {                                                             \
-         __m128 tmp;                                               \
-         rx_SSE = _mm_load_ps(r0);                                 \
-         ry_SSE = _mm_load_ps(r1);                                 \
-         rz_SSE = _mm_load_ps(r2);                                 \
-         tmp    = _mm_load_ps(r3);                                 \
-         _MM_TRANSPOSE4_PS(rx_SSE, ry_SSE, rz_SSE, tmp);              \
-     }
- #define store_rvec4(rx_SSE, ry_SSE, rz_SSE, r0, r1, r2, r3)         \
-     {                                                             \
-         __m128 tmp = _mm_setzero_ps();                              \
-         _MM_TRANSPOSE4_PS(rx_SSE, ry_SSE, rz_SSE, tmp);              \
-         _mm_store_ps(r0, rx_SSE);                                  \
-         _mm_store_ps(r1, ry_SSE);                                  \
-         _mm_store_ps(r2, rz_SSE);                                  \
-         _mm_store_ps(r3, tmp   );                                  \
-     }
- /* An rvec in a structure which can be allocated 16-byte aligned */
- typedef struct {
-     rvec  v;
-     float f;
- } rvec_sse_t;
- /* As dih_angle above, but calculates 4 dihedral angles at once using SSE,
++#ifdef SIMD_BONDEDS
 +
-  * Note that bv and buf should be 16-byte aligned.
++/* As dih_angle above, but calculates 4 dihedral angles at once using SIMD,
 + * also calculates the pre-factor required for the dihedral force update.
- static void
- dih_angle_sse(const rvec *x,
-               int ai[4], int aj[4], int ak[4], int al[4],
-               const t_pbc *pbc,
-               int t1[4], int t2[4], int t3[4],
-               rvec_sse_t *bv,
-               real *buf)
- {
-     int    s;
-     __m128 rijx_SSE, rijy_SSE, rijz_SSE;
-     __m128 rkjx_SSE, rkjy_SSE, rkjz_SSE;
-     __m128 rklx_SSE, rkly_SSE, rklz_SSE;
-     __m128 mx_SSE, my_SSE, mz_SSE;
-     __m128 nx_SSE, ny_SSE, nz_SSE;
-     __m128 cx_SSE, cy_SSE, cz_SSE;
-     __m128 cn_SSE;
-     __m128 s_SSE;
-     __m128 phi_SSE;
-     __m128 ipr_SSE;
-     int    signs;
-     __m128 iprm_SSE, iprn_SSE;
-     __m128 nrkj2_SSE, nrkj_1_SSE, nrkj_2_SSE, nrkj_SSE;
-     __m128 nrkj_m2_SSE, nrkj_n2_SSE;
-     __m128 p_SSE, q_SSE;
-     __m128 fmin_SSE = _mm_set1_ps(GMX_FLOAT_MIN);
-     for (s = 0; s < 4; s++)
-     {
-         t1[s] = pbc_rvec_sub(pbc, x[ai[s]], x[aj[s]], bv[0+s].v);
-         t2[s] = pbc_rvec_sub(pbc, x[ak[s]], x[aj[s]], bv[4+s].v);
-         t3[s] = pbc_rvec_sub(pbc, x[ak[s]], x[al[s]], bv[8+s].v);
++ * Note that bv and buf should be register aligned.
 + */
-     load_rvec4(bv[0].v, bv[1].v, bv[2].v, bv[3].v, rijx_SSE, rijy_SSE, rijz_SSE);
-     load_rvec4(bv[4].v, bv[5].v, bv[6].v, bv[7].v, rkjx_SSE, rkjy_SSE, rkjz_SSE);
-     load_rvec4(bv[8].v, bv[9].v, bv[10].v, bv[11].v, rklx_SSE, rkly_SSE, rklz_SSE);
++static gmx_inline void
++dih_angle_simd(const rvec *x,
++               const int *ai, const int *aj, const int *ak, const int *al,
++               const pbc_simd_t *pbc,
++               real *dr,
++               gmx_mm_pr *phi_S,
++               gmx_mm_pr *mx_S, gmx_mm_pr *my_S, gmx_mm_pr *mz_S,
++               gmx_mm_pr *nx_S, gmx_mm_pr *ny_S, gmx_mm_pr *nz_S,
++               gmx_mm_pr *nrkj_m2_S,
++               gmx_mm_pr *nrkj_n2_S,
++               real *p,
++               real *q)
++{
++#define UNROLL GMX_SIMD_WIDTH_HERE
++    int       s, m;
++    gmx_mm_pr rijx_S, rijy_S, rijz_S;
++    gmx_mm_pr rkjx_S, rkjy_S, rkjz_S;
++    gmx_mm_pr rklx_S, rkly_S, rklz_S;
++    gmx_mm_pr cx_S, cy_S, cz_S;
++    gmx_mm_pr cn_S;
++    gmx_mm_pr s_S;
++    gmx_mm_pr ipr_S;
++    gmx_mm_pr iprm_S, iprn_S;
++    gmx_mm_pr nrkj2_S, nrkj_1_S, nrkj_2_S, nrkj_S;
++    gmx_mm_pr p_S, q_S;
++    gmx_mm_pr fmin_S = gmx_set1_pr(GMX_FLOAT_MIN);
++    /* Using -0.0 should lead to only the sign bit being set */
++    gmx_mm_pr sign_mask_S = gmx_set1_pr(-0.0);
++
++    for (s = 0; s < UNROLL; s++)
++    {
++        /* If you can't use pbc_dx_simd below for PBC, e.g. because
++         * you can't round in SIMD, use pbc_rvec_sub here.
++         */
++        for (m = 0; m < DIM; m++)
++        {
++            dr[s + (0*DIM + m)*UNROLL] = x[ai[s]][m] - x[aj[s]][m];
++            dr[s + (1*DIM + m)*UNROLL] = x[ak[s]][m] - x[aj[s]][m];
++            dr[s + (2*DIM + m)*UNROLL] = x[ak[s]][m] - x[al[s]][m];
++        }
 +    }
 +
-     GMX_MM_CPROD_PS(rijx_SSE, rijy_SSE, rijz_SSE,
-                     rkjx_SSE, rkjy_SSE, rkjz_SSE,
-                     mx_SSE, my_SSE, mz_SSE);
++    rijx_S = gmx_load_pr(dr + 0*UNROLL);
++    rijy_S = gmx_load_pr(dr + 1*UNROLL);
++    rijz_S = gmx_load_pr(dr + 2*UNROLL);
++    rkjx_S = gmx_load_pr(dr + 3*UNROLL);
++    rkjy_S = gmx_load_pr(dr + 4*UNROLL);
++    rkjz_S = gmx_load_pr(dr + 5*UNROLL);
++    rklx_S = gmx_load_pr(dr + 6*UNROLL);
++    rkly_S = gmx_load_pr(dr + 7*UNROLL);
++    rklz_S = gmx_load_pr(dr + 8*UNROLL);
 +
-     GMX_MM_CPROD_PS(rkjx_SSE, rkjy_SSE, rkjz_SSE,
-                     rklx_SSE, rkly_SSE, rklz_SSE,
-                     nx_SSE, ny_SSE, nz_SSE);
++    pbc_dx_simd(&rijx_S, &rijy_S, &rijz_S, pbc);
++    pbc_dx_simd(&rkjx_S, &rkjy_S, &rkjz_S, pbc);
++    pbc_dx_simd(&rklx_S, &rkly_S, &rklz_S, pbc);
 +
-     GMX_MM_CPROD_PS(mx_SSE, my_SSE, mz_SSE,
-                     nx_SSE, ny_SSE, nz_SSE,
-                     cx_SSE, cy_SSE, cz_SSE);
++    gmx_cprod_pr(rijx_S, rijy_S, rijz_S,
++                 rkjx_S, rkjy_S, rkjz_S,
++                 mx_S, my_S, mz_S);
 +
-     cn_SSE = gmx_mm_sqrt_ps(GMX_MM_NORM2_PS(cx_SSE, cy_SSE, cz_SSE));
++    gmx_cprod_pr(rkjx_S, rkjy_S, rkjz_S,
++                 rklx_S, rkly_S, rklz_S,
++                 nx_S, ny_S, nz_S);
 +
-     s_SSE = GMX_MM_IPROD_PS(mx_SSE, my_SSE, mz_SSE, nx_SSE, ny_SSE, nz_SSE);
++    gmx_cprod_pr(*mx_S, *my_S, *mz_S,
++                 *nx_S, *ny_S, *nz_S,
++                 &cx_S, &cy_S, &cz_S);
 +
-     phi_SSE = gmx_mm_atan2_ps(cn_SSE, s_SSE);
-     _mm_store_ps(buf+16, phi_SSE);
++    cn_S       = gmx_sqrt_pr(gmx_norm2_pr(cx_S, cy_S, cz_S));
 +
-     ipr_SSE = GMX_MM_IPROD_PS(rijx_SSE, rijy_SSE, rijz_SSE,
-                               nx_SSE, ny_SSE, nz_SSE);
++    s_S        = gmx_iprod_pr(*mx_S, *my_S, *mz_S, *nx_S, *ny_S, *nz_S);
 +
-     signs = _mm_movemask_ps(ipr_SSE);
++    /* Determine the dihedral angle, the sign might need correction */
++    *phi_S     = gmx_atan2_pr(cn_S, s_S);
 +
-     for (s = 0; s < 4; s++)
-     {
-         if (signs & (1<<s))
-         {
-             buf[16+s] = -buf[16+s];
-         }
-     }
++    ipr_S      = gmx_iprod_pr(rijx_S, rijy_S, rijz_S,
++                              *nx_S, *ny_S, *nz_S);
 +
-     iprm_SSE    = GMX_MM_NORM2_PS(mx_SSE, my_SSE, mz_SSE);
-     iprn_SSE    = GMX_MM_NORM2_PS(nx_SSE, ny_SSE, nz_SSE);
-     /* store_rvec4 messes with the input, don't use it after this! */
-     store_rvec4(mx_SSE, my_SSE, mz_SSE, bv[0].v, bv[1].v, bv[2].v, bv[3].v);
-     store_rvec4(nx_SSE, ny_SSE, nz_SSE, bv[4].v, bv[5].v, bv[6].v, bv[7].v);
-     nrkj2_SSE   = GMX_MM_NORM2_PS(rkjx_SSE, rkjy_SSE, rkjz_SSE);
++    iprm_S     = gmx_norm2_pr(*mx_S, *my_S, *mz_S);
++    iprn_S     = gmx_norm2_pr(*nx_S, *ny_S, *nz_S);
 +
-     nrkj2_SSE   = _mm_max_ps(nrkj2_SSE, fmin_SSE);
-     nrkj_1_SSE  = gmx_mm_invsqrt_ps(nrkj2_SSE);
-     nrkj_2_SSE  = _mm_mul_ps(nrkj_1_SSE, nrkj_1_SSE);
-     nrkj_SSE    = _mm_mul_ps(nrkj2_SSE, nrkj_1_SSE);
-     iprm_SSE    = _mm_max_ps(iprm_SSE, fmin_SSE);
-     iprn_SSE    = _mm_max_ps(iprn_SSE, fmin_SSE);
-     nrkj_m2_SSE = _mm_mul_ps(nrkj_SSE, gmx_mm_inv_ps(iprm_SSE));
-     nrkj_n2_SSE = _mm_mul_ps(nrkj_SSE, gmx_mm_inv_ps(iprn_SSE));
-     _mm_store_ps(buf+0, nrkj_m2_SSE);
-     _mm_store_ps(buf+4, nrkj_n2_SSE);
++    nrkj2_S    = gmx_norm2_pr(rkjx_S, rkjy_S, rkjz_S);
 +
 +    /* Avoid division by zero. When zero, the result is multiplied by 0
 +     * anyhow, so the 3 max below do not affect the final result.
 +     */
-     p_SSE       = GMX_MM_IPROD_PS(rijx_SSE, rijy_SSE, rijz_SSE,
-                                   rkjx_SSE, rkjy_SSE, rkjz_SSE);
-     p_SSE       = _mm_mul_ps(p_SSE, nrkj_2_SSE);
++    nrkj2_S    = gmx_max_pr(nrkj2_S, fmin_S);
++    nrkj_1_S   = gmx_invsqrt_pr(nrkj2_S);
++    nrkj_2_S   = gmx_mul_pr(nrkj_1_S, nrkj_1_S);
++    nrkj_S     = gmx_mul_pr(nrkj2_S, nrkj_1_S);
++
++    iprm_S     = gmx_max_pr(iprm_S, fmin_S);
++    iprn_S     = gmx_max_pr(iprn_S, fmin_S);
++    *nrkj_m2_S = gmx_mul_pr(nrkj_S, gmx_inv_pr(iprm_S));
++    *nrkj_n2_S = gmx_mul_pr(nrkj_S, gmx_inv_pr(iprn_S));
++
++    /* Set sign of the angle with the sign of ipr_S.
++     * Since phi is currently positive, we can use OR instead of XOR.
++     */
++    *phi_S     = gmx_or_pr(*phi_S, gmx_and_pr(ipr_S, sign_mask_S));
 +
-     q_SSE       = GMX_MM_IPROD_PS(rklx_SSE, rkly_SSE, rklz_SSE,
-                                   rkjx_SSE, rkjy_SSE, rkjz_SSE);
-     q_SSE       = _mm_mul_ps(q_SSE, nrkj_2_SSE);
++    p_S        = gmx_iprod_pr(rijx_S, rijy_S, rijz_S,
++                              rkjx_S, rkjy_S, rkjz_S);
++    p_S        = gmx_mul_pr(p_S, nrkj_2_S);
 +
-     _mm_store_ps(buf+8, p_SSE);
-     _mm_store_ps(buf+12, q_SSE);
++    q_S        = gmx_iprod_pr(rklx_S, rkly_S, rklz_S,
++                              rkjx_S, rkjy_S, rkjz_S);
++    q_S        = gmx_mul_pr(q_S, nrkj_2_S);
 +
- #endif /* SSE_PROPER_DIHEDRALS */
++    gmx_store_pr(p, p_S);
++    gmx_store_pr(q, q_S);
++#undef UNROLL
 +}
 +
- static void
- do_dih_fup_noshiftf_precalc(int i, int j, int k, int l, real ddphi,
-                             real nrkj_m2, real nrkj_n2,
++#endif /* SIMD_BONDEDS */
 +
 +
 +void do_dih_fup(int i, int j, int k, int l, real ddphi,
 +                rvec r_ij, rvec r_kj, rvec r_kl,
 +                rvec m, rvec n, rvec f[], rvec fshift[],
 +                const t_pbc *pbc, const t_graph *g,
 +                const rvec x[], int t1, int t2, int t3)
 +{
 +    /* 143 FLOPS */
 +    rvec f_i, f_j, f_k, f_l;
 +    rvec uvec, vvec, svec, dx_jl;
 +    real iprm, iprn, nrkj, nrkj2, nrkj_1, nrkj_2;
 +    real a, b, p, q, toler;
 +    ivec jt, dt_ij, dt_kj, dt_lj;
 +
 +    iprm  = iprod(m, m);       /*  5    */
 +    iprn  = iprod(n, n);       /*  5  */
 +    nrkj2 = iprod(r_kj, r_kj); /*  5  */
 +    toler = nrkj2*GMX_REAL_EPS;
 +    if ((iprm > toler) && (iprn > toler))
 +    {
 +        nrkj_1 = gmx_invsqrt(nrkj2); /* 10    */
 +        nrkj_2 = nrkj_1*nrkj_1;      /*  1    */
 +        nrkj   = nrkj2*nrkj_1;       /*  1    */
 +        a      = -ddphi*nrkj/iprm;   /* 11    */
 +        svmul(a, m, f_i);            /*  3    */
 +        b     = ddphi*nrkj/iprn;     /* 11    */
 +        svmul(b, n, f_l);            /*  3  */
 +        p     = iprod(r_ij, r_kj);   /*  5    */
 +        p    *= nrkj_2;              /*  1    */
 +        q     = iprod(r_kl, r_kj);   /*  5    */
 +        q    *= nrkj_2;              /*  1    */
 +        svmul(p, f_i, uvec);         /*  3    */
 +        svmul(q, f_l, vvec);         /*  3    */
 +        rvec_sub(uvec, vvec, svec);  /*  3    */
 +        rvec_sub(f_i, svec, f_j);    /*  3    */
 +        rvec_add(f_l, svec, f_k);    /*  3    */
 +        rvec_inc(f[i], f_i);         /*  3    */
 +        rvec_dec(f[j], f_j);         /*  3    */
 +        rvec_dec(f[k], f_k);         /*  3    */
 +        rvec_inc(f[l], f_l);         /*  3    */
 +
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, j), jt);
 +            ivec_sub(SHIFT_IVEC(g, i), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, k), jt, dt_kj);
 +            ivec_sub(SHIFT_IVEC(g, l), jt, dt_lj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +            t3 = IVEC2IS(dt_lj);
 +        }
 +        else if (pbc)
 +        {
 +            t3 = pbc_rvec_sub(pbc, x[l], x[j], dx_jl);
 +        }
 +        else
 +        {
 +            t3 = CENTRAL;
 +        }
 +
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_dec(fshift[CENTRAL], f_j);
 +        rvec_dec(fshift[t2], f_k);
 +        rvec_inc(fshift[t3], f_l);
 +    }
 +    /* 112 TOTAL    */
 +}
 +
 +/* As do_dih_fup above, but without shift forces */
 +static void
 +do_dih_fup_noshiftf(int i, int j, int k, int l, real ddphi,
 +                    rvec r_ij, rvec r_kj, rvec r_kl,
 +                    rvec m, rvec n, rvec f[])
 +{
 +    rvec f_i, f_j, f_k, f_l;
 +    rvec uvec, vvec, svec, dx_jl;
 +    real iprm, iprn, nrkj, nrkj2, nrkj_1, nrkj_2;
 +    real a, b, p, q, toler;
 +    ivec jt, dt_ij, dt_kj, dt_lj;
 +
 +    iprm  = iprod(m, m);       /*  5    */
 +    iprn  = iprod(n, n);       /*  5  */
 +    nrkj2 = iprod(r_kj, r_kj); /*  5  */
 +    toler = nrkj2*GMX_REAL_EPS;
 +    if ((iprm > toler) && (iprn > toler))
 +    {
 +        nrkj_1 = gmx_invsqrt(nrkj2); /* 10    */
 +        nrkj_2 = nrkj_1*nrkj_1;      /*  1    */
 +        nrkj   = nrkj2*nrkj_1;       /*  1    */
 +        a      = -ddphi*nrkj/iprm;   /* 11    */
 +        svmul(a, m, f_i);            /*  3    */
 +        b     = ddphi*nrkj/iprn;     /* 11    */
 +        svmul(b, n, f_l);            /*  3  */
 +        p     = iprod(r_ij, r_kj);   /*  5    */
 +        p    *= nrkj_2;              /*  1    */
 +        q     = iprod(r_kl, r_kj);   /*  5    */
 +        q    *= nrkj_2;              /*  1    */
 +        svmul(p, f_i, uvec);         /*  3    */
 +        svmul(q, f_l, vvec);         /*  3    */
 +        rvec_sub(uvec, vvec, svec);  /*  3    */
 +        rvec_sub(f_i, svec, f_j);    /*  3    */
 +        rvec_add(f_l, svec, f_k);    /*  3    */
 +        rvec_inc(f[i], f_i);         /*  3    */
 +        rvec_dec(f[j], f_j);         /*  3    */
 +        rvec_dec(f[k], f_k);         /*  3    */
 +        rvec_inc(f[l], f_l);         /*  3    */
 +    }
 +}
 +
 +/* As do_dih_fup_noshiftf above, but with pre-calculated pre-factors */
-                             rvec m, rvec n, rvec f[])
++static gmx_inline void
++do_dih_fup_noshiftf_precalc(int i, int j, int k, int l,
 +                            real p, real q,
-     rvec uvec, vvec, svec, dx_jl;
-     real a, b, toler;
-     ivec jt, dt_ij, dt_kj, dt_lj;
-     a = -ddphi*nrkj_m2;
-     svmul(a, m, f_i);
-     b =  ddphi*nrkj_n2;
-     svmul(b, n, f_l);
++                            real f_i_x, real f_i_y, real f_i_z,
++                            real mf_l_x, real mf_l_y, real mf_l_z,
++                            rvec f[])
 +{
 +    rvec f_i, f_j, f_k, f_l;
- #ifdef SSE_PROPER_DIHEDRALS
++    rvec uvec, vvec, svec;
++
++    f_i[XX] = f_i_x;
++    f_i[YY] = f_i_y;
++    f_i[ZZ] = f_i_z;
++    f_l[XX] = -mf_l_x;
++    f_l[YY] = -mf_l_y;
++    f_l[ZZ] = -mf_l_z;
 +    svmul(p, f_i, uvec);
 +    svmul(q, f_l, vvec);
 +    rvec_sub(uvec, vvec, svec);
 +    rvec_sub(f_i, svec, f_j);
 +    rvec_add(f_l, svec, f_k);
 +    rvec_inc(f[i], f_i);
 +    rvec_dec(f[j], f_j);
 +    rvec_dec(f[k], f_k);
 +    rvec_inc(f[l], f_l);
 +}
 +
 +
 +real dopdihs(real cpA, real cpB, real phiA, real phiB, int mult,
 +             real phi, real lambda, real *V, real *F)
 +{
 +    real v, dvdlambda, mdphi, v1, sdphi, ddphi;
 +    real L1   = 1.0 - lambda;
 +    real ph0  = (L1*phiA + lambda*phiB)*DEG2RAD;
 +    real dph0 = (phiB - phiA)*DEG2RAD;
 +    real cp   = L1*cpA + lambda*cpB;
 +
 +    mdphi =  mult*phi - ph0;
 +    sdphi = sin(mdphi);
 +    ddphi = -cp*mult*sdphi;
 +    v1    = 1.0 + cos(mdphi);
 +    v     = cp*v1;
 +
 +    dvdlambda  = (cpB - cpA)*v1 + cp*dph0*sdphi;
 +
 +    *V = v;
 +    *F = ddphi;
 +
 +    return dvdlambda;
 +
 +    /* That was 40 flops */
 +}
 +
 +static void
 +dopdihs_noener(real cpA, real cpB, real phiA, real phiB, int mult,
 +               real phi, real lambda, real *F)
 +{
 +    real mdphi, sdphi, ddphi;
 +    real L1   = 1.0 - lambda;
 +    real ph0  = (L1*phiA + lambda*phiB)*DEG2RAD;
 +    real cp   = L1*cpA + lambda*cpB;
 +
 +    mdphi = mult*phi - ph0;
 +    sdphi = sin(mdphi);
 +    ddphi = -cp*mult*sdphi;
 +
 +    *F = ddphi;
 +
 +    /* That was 20 flops */
 +}
 +
 +static void
 +dopdihs_mdphi(real cpA, real cpB, real phiA, real phiB, int mult,
 +              real phi, real lambda, real *cp, real *mdphi)
 +{
 +    real L1   = 1.0 - lambda;
 +    real ph0  = (L1*phiA + lambda*phiB)*DEG2RAD;
 +
 +    *cp    = L1*cpA + lambda*cpB;
 +
 +    *mdphi = mult*phi - ph0;
 +}
 +
 +static real dopdihs_min(real cpA, real cpB, real phiA, real phiB, int mult,
 +                        real phi, real lambda, real *V, real *F)
 +/* similar to dopdihs, except for a minus sign  *
 + * and a different treatment of mult/phi0       */
 +{
 +    real v, dvdlambda, mdphi, v1, sdphi, ddphi;
 +    real L1   = 1.0 - lambda;
 +    real ph0  = (L1*phiA + lambda*phiB)*DEG2RAD;
 +    real dph0 = (phiB - phiA)*DEG2RAD;
 +    real cp   = L1*cpA + lambda*cpB;
 +
 +    mdphi = mult*(phi-ph0);
 +    sdphi = sin(mdphi);
 +    ddphi = cp*mult*sdphi;
 +    v1    = 1.0-cos(mdphi);
 +    v     = cp*v1;
 +
 +    dvdlambda  = (cpB-cpA)*v1 + cp*dph0*sdphi;
 +
 +    *V = v;
 +    *F = ddphi;
 +
 +    return dvdlambda;
 +
 +    /* That was 40 flops */
 +}
 +
 +real pdihs(int nbonds,
 +           const t_iatom forceatoms[], const t_iparams forceparams[],
 +           const rvec x[], rvec f[], rvec fshift[],
 +           const t_pbc *pbc, const t_graph *g,
 +           real lambda, real *dvdlambda,
 +           const t_mdatoms *md, t_fcdata *fcd,
 +           int *global_atom_index)
 +{
 +    int  i, type, ai, aj, ak, al;
 +    int  t1, t2, t3;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +    real phi, sign, ddphi, vpd, vtot;
 +
 +    vtot = 0.0;
 +
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);  /*  84      */
 +        *dvdlambda += dopdihs(forceparams[type].pdihs.cpA,
 +                              forceparams[type].pdihs.cpB,
 +                              forceparams[type].pdihs.phiA,
 +                              forceparams[type].pdihs.phiB,
 +                              forceparams[type].pdihs.mult,
 +                              phi, lambda, &vpd, &ddphi);
 +
 +        vtot += vpd;
 +        do_dih_fup(ai, aj, ak, al, ddphi, r_ij, r_kj, r_kl, m, n,
 +                   f, fshift, pbc, g, x, t1, t2, t3); /* 112          */
 +
 +#ifdef DEBUG
 +        fprintf(debug, "pdih: (%d,%d,%d,%d) phi=%g\n",
 +                ai, aj, ak, al, phi);
 +#endif
 +    } /* 223 TOTAL  */
 +
 +    return vtot;
 +}
 +
 +void make_dp_periodic(real *dp)  /* 1 flop? */
 +{
 +    /* dp cannot be outside (-pi,pi) */
 +    if (*dp >= M_PI)
 +    {
 +        *dp -= 2*M_PI;
 +    }
 +    else if (*dp < -M_PI)
 +    {
 +        *dp += 2*M_PI;
 +    }
 +    return;
 +}
 +
 +/* As pdihs above, but without calculating energies and shift forces */
 +static void
 +pdihs_noener(int nbonds,
 +             const t_iatom forceatoms[], const t_iparams forceparams[],
 +             const rvec x[], rvec f[],
 +             const t_pbc *pbc, const t_graph *g,
 +             real lambda,
 +             const t_mdatoms *md, t_fcdata *fcd,
 +             int *global_atom_index)
 +{
 +    int  i, type, ai, aj, ak, al;
 +    int  t1, t2, t3;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +    real phi, sign, ddphi_tot, ddphi;
 +
 +    for (i = 0; (i < nbonds); )
 +    {
 +        ai   = forceatoms[i+1];
 +        aj   = forceatoms[i+2];
 +        ak   = forceatoms[i+3];
 +        al   = forceatoms[i+4];
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);
 +
 +        ddphi_tot = 0;
 +
 +        /* Loop over dihedrals working on the same atoms,
 +         * so we avoid recalculating angles and force distributions.
 +         */
 +        do
 +        {
 +            type = forceatoms[i];
 +            dopdihs_noener(forceparams[type].pdihs.cpA,
 +                           forceparams[type].pdihs.cpB,
 +                           forceparams[type].pdihs.phiA,
 +                           forceparams[type].pdihs.phiB,
 +                           forceparams[type].pdihs.mult,
 +                           phi, lambda, &ddphi);
 +            ddphi_tot += ddphi;
 +
 +            i += 5;
 +        }
 +        while (i < nbonds &&
 +               forceatoms[i+1] == ai &&
 +               forceatoms[i+2] == aj &&
 +               forceatoms[i+3] == ak &&
 +               forceatoms[i+4] == al);
 +
 +        do_dih_fup_noshiftf(ai, aj, ak, al, ddphi_tot, r_ij, r_kj, r_kl, m, n, f);
 +    }
 +}
 +
 +
- /* As pdihs_noner above, but using SSE to calculate 4 dihedrals at once */
++#ifdef SIMD_BONDEDS
 +
- pdihs_noener_sse(int nbonds,
-                  const t_iatom forceatoms[], const t_iparams forceparams[],
-                  const rvec x[], rvec f[],
-                  const t_pbc *pbc, const t_graph *g,
-                  real lambda,
-                  const t_mdatoms *md, t_fcdata *fcd,
-                  int *global_atom_index)
++/* As pdihs_noner above, but using SIMD to calculate many dihedrals at once */
 +static void
-     int        i, i4, s;
-     int        type, ai[4], aj[4], ak[4], al[4];
-     int        t1[4], t2[4], t3[4];
-     int        mult[4];
-     real       cp[4], mdphi[4];
-     real       ddphi;
-     rvec_sse_t rs_array[13], *rs;
-     real       buf_array[24], *buf;
-     __m128     mdphi_SSE, sin_SSE, cos_SSE;
-     /* Ensure 16-byte alignment */
-     rs  = (rvec_sse_t *)(((size_t)(rs_array +1)) & (~((size_t)15)));
-     buf =      (float *)(((size_t)(buf_array+3)) & (~((size_t)15)));
-     for (i = 0; (i < nbonds); i += 20)
-     {
-         /* Collect atoms quadruplets for 4 dihedrals */
-         i4 = i;
-         for (s = 0; s < 4; s++)
-         {
-             ai[s] = forceatoms[i4+1];
-             aj[s] = forceatoms[i4+2];
-             ak[s] = forceatoms[i4+3];
-             al[s] = forceatoms[i4+4];
-             /* At the end fill the arrays with identical entries */
-             if (i4 + 5 < nbonds)
-             {
-                 i4 += 5;
-             }
-         }
++pdihs_noener_simd(int nbonds,
++                  const t_iatom forceatoms[], const t_iparams forceparams[],
++                  const rvec x[], rvec f[],
++                  const t_pbc *pbc, const t_graph *g,
++                  real lambda,
++                  const t_mdatoms *md, t_fcdata *fcd,
++                  int *global_atom_index)
 +{
-         /* Caclulate 4 dihedral angles at once */
-         dih_angle_sse(x, ai, aj, ak, al, pbc, t1, t2, t3, rs, buf);
++#define UNROLL GMX_SIMD_WIDTH_HERE
++    const int      nfa1 = 5;
++    int            i, iu, s;
++    int            type, ai[UNROLL], aj[UNROLL], ak[UNROLL], al[UNROLL];
++    int            t1[UNROLL], t2[UNROLL], t3[UNROLL];
++    real           ddphi;
++    real           dr_array[3*DIM*UNROLL+UNROLL], *dr;
++    real           buf_array[7*UNROLL+UNROLL], *buf;
++    real           *cp, *phi0, *mult, *phi, *p, *q, *sf_i, *msf_l;
++    gmx_mm_pr      phi0_S, phi_S;
++    gmx_mm_pr      mx_S, my_S, mz_S;
++    gmx_mm_pr      nx_S, ny_S, nz_S;
++    gmx_mm_pr      nrkj_m2_S, nrkj_n2_S;
++    gmx_mm_pr      cp_S, mdphi_S, mult_S;
++    gmx_mm_pr      sin_S, cos_S;
++    gmx_mm_pr      mddphi_S;
++    gmx_mm_pr      sf_i_S, msf_l_S;
++    pbc_simd_t     pbc_simd;
++
++    /* Ensure SIMD register alignment */
++    dr  = gmx_simd_align_real(dr_array);
++    buf = gmx_simd_align_real(buf_array);
++
++    /* Extract aligned pointer for parameters and variables */
++    cp    = buf + 0*UNROLL;
++    phi0  = buf + 1*UNROLL;
++    mult  = buf + 2*UNROLL;
++    p     = buf + 3*UNROLL;
++    q     = buf + 4*UNROLL;
++    sf_i  = buf + 5*UNROLL;
++    msf_l = buf + 6*UNROLL;
++
++    set_pbc_simd(pbc, &pbc_simd);
++
++    /* nbonds is the number of dihedrals times nfa1, here we step UNROLL dihs */
++    for (i = 0; (i < nbonds); i += UNROLL*nfa1)
++    {
++        /* Collect atoms quadruplets for UNROLL dihedrals.
++         * iu indexes into forceatoms, we should not let iu go beyond nbonds.
++         */
++        iu = i;
++        for (s = 0; s < UNROLL; s++)
++        {
++            type  = forceatoms[iu];
++            ai[s] = forceatoms[iu+1];
++            aj[s] = forceatoms[iu+2];
++            ak[s] = forceatoms[iu+3];
++            al[s] = forceatoms[iu+4];
 +
-         i4 = i;
-         for (s = 0; s < 4; s++)
-         {
-             if (i4 < nbonds)
-             {
-                 /* Calculate the coefficient and angle deviation */
-                 type = forceatoms[i4];
-                 dopdihs_mdphi(forceparams[type].pdihs.cpA,
-                               forceparams[type].pdihs.cpB,
-                               forceparams[type].pdihs.phiA,
-                               forceparams[type].pdihs.phiB,
-                               forceparams[type].pdihs.mult,
-                               buf[16+s], lambda, &cp[s], &buf[16+s]);
-                 mult[s] = forceparams[type].pdihs.mult;
-             }
-             else
++            cp[s]   = forceparams[type].pdihs.cpA;
++            phi0[s] = forceparams[type].pdihs.phiA*DEG2RAD;
++            mult[s] = forceparams[type].pdihs.mult;
 +
-                 buf[16+s] = 0;
++            /* At the end fill the arrays with identical entries */
++            if (iu + nfa1 < nbonds)
 +            {
-             i4 += 5;
++                iu += nfa1;
 +            }
-         /* Calculate 4 sines at once */
-         mdphi_SSE = _mm_load_ps(buf+16);
-         gmx_mm_sincos_ps(mdphi_SSE, &sin_SSE, &cos_SSE);
-         _mm_store_ps(buf+16, sin_SSE);
-         i4 = i;
 +        }
 +
-             ddphi = -cp[s]*mult[s]*buf[16+s];
-             do_dih_fup_noshiftf_precalc(ai[s], aj[s], ak[s], al[s], ddphi,
-                                         buf[ 0+s], buf[ 4+s],
-                                         buf[ 8+s], buf[12+s],
-                                         rs[0+s].v, rs[4+s].v,
++        /* Caclulate UNROLL dihedral angles at once */
++        dih_angle_simd(x, ai, aj, ak, al, &pbc_simd,
++                       dr,
++                       &phi_S,
++                       &mx_S, &my_S, &mz_S,
++                       &nx_S, &ny_S, &nz_S,
++                       &nrkj_m2_S,
++                       &nrkj_n2_S,
++                       p, q);
++
++        cp_S     = gmx_load_pr(cp);
++        phi0_S   = gmx_load_pr(phi0);
++        mult_S   = gmx_load_pr(mult);
++
++        mdphi_S  = gmx_sub_pr(gmx_mul_pr(mult_S, phi_S), phi0_S);
++
++        /* Calculate UNROLL sines at once */
++        gmx_sincos_pr(mdphi_S, &sin_S, &cos_S);
++        mddphi_S = gmx_mul_pr(gmx_mul_pr(cp_S, mult_S), sin_S);
++        sf_i_S   = gmx_mul_pr(mddphi_S, nrkj_m2_S);
++        msf_l_S  = gmx_mul_pr(mddphi_S, nrkj_n2_S);
++
++        /* After this m?_S will contain f[i] */
++        mx_S     = gmx_mul_pr(sf_i_S, mx_S);
++        my_S     = gmx_mul_pr(sf_i_S, my_S);
++        mz_S     = gmx_mul_pr(sf_i_S, mz_S);
++
++        /* After this m?_S will contain -f[l] */
++        nx_S     = gmx_mul_pr(msf_l_S, nx_S);
++        ny_S     = gmx_mul_pr(msf_l_S, ny_S);
++        nz_S     = gmx_mul_pr(msf_l_S, nz_S);
++
++        gmx_store_pr(dr + 0*UNROLL, mx_S);
++        gmx_store_pr(dr + 1*UNROLL, my_S);
++        gmx_store_pr(dr + 2*UNROLL, mz_S);
++        gmx_store_pr(dr + 3*UNROLL, nx_S);
++        gmx_store_pr(dr + 4*UNROLL, ny_S);
++        gmx_store_pr(dr + 5*UNROLL, nz_S);
++
++        iu = i;
 +        s  = 0;
 +        do
 +        {
-             i4 += 5;
++            do_dih_fup_noshiftf_precalc(ai[s], aj[s], ak[s], al[s],
++                                        p[s], q[s],
++                                        dr[     XX *UNROLL+s],
++                                        dr[     YY *UNROLL+s],
++                                        dr[     ZZ *UNROLL+s],
++                                        dr[(DIM+XX)*UNROLL+s],
++                                        dr[(DIM+YY)*UNROLL+s],
++                                        dr[(DIM+ZZ)*UNROLL+s],
 +                                        f);
 +            s++;
-         while (s < 4 && i4 < nbonds);
++            iu += nfa1;
 +        }
- #endif /* SSE_PROPER_DIHEDRALS */
++        while (s < UNROLL && iu < nbonds);
 +    }
++#undef UNROLL
 +}
 +
- #ifndef SSE_PROPER_DIHEDRALS
++#endif /* SIMD_BONDEDS */
 +
 +
 +real idihs(int nbonds,
 +           const t_iatom forceatoms[], const t_iparams forceparams[],
 +           const rvec x[], rvec f[], rvec fshift[],
 +           const t_pbc *pbc, const t_graph *g,
 +           real lambda, real *dvdlambda,
 +           const t_mdatoms *md, t_fcdata *fcd,
 +           int *global_atom_index)
 +{
 +    int  i, type, ai, aj, ak, al;
 +    int  t1, t2, t3;
 +    real phi, phi0, dphi0, ddphi, sign, vtot;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +    real L1, kk, dp, dp2, kA, kB, pA, pB, dvdl_term;
 +
 +    L1        = 1.0-lambda;
 +    dvdl_term = 0;
 +    vtot      = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);  /*  84                */
 +
 +        /* phi can jump if phi0 is close to Pi/-Pi, which will cause huge
 +         * force changes if we just apply a normal harmonic.
 +         * Instead, we first calculate phi-phi0 and take it modulo (-Pi,Pi).
 +         * This means we will never have the periodicity problem, unless
 +         * the dihedral is Pi away from phiO, which is very unlikely due to
 +         * the potential.
 +         */
 +        kA = forceparams[type].harmonic.krA;
 +        kB = forceparams[type].harmonic.krB;
 +        pA = forceparams[type].harmonic.rA;
 +        pB = forceparams[type].harmonic.rB;
 +
 +        kk    = L1*kA + lambda*kB;
 +        phi0  = (L1*pA + lambda*pB)*DEG2RAD;
 +        dphi0 = (pB - pA)*DEG2RAD;
 +
 +        dp = phi-phi0;
 +
 +        make_dp_periodic(&dp);
 +
 +        dp2 = dp*dp;
 +
 +        vtot += 0.5*kk*dp2;
 +        ddphi = -kk*dp;
 +
 +        dvdl_term += 0.5*(kB - kA)*dp2 - kk*dphi0*dp;
 +
 +        do_dih_fup(ai, aj, ak, al, (real)(-ddphi), r_ij, r_kj, r_kl, m, n,
 +                   f, fshift, pbc, g, x, t1, t2, t3); /* 112          */
 +        /* 218 TOTAL  */
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "idih: (%d,%d,%d,%d) phi=%g\n",
 +                    ai, aj, ak, al, phi);
 +        }
 +#endif
 +    }
 +
 +    *dvdlambda += dvdl_term;
 +    return vtot;
 +}
 +
 +
 +/*! \brief returns dx, rdist, and dpdl for functions posres() and fbposres()
 + */
 +static void posres_dx(const rvec x, const rvec pos0A, const rvec pos0B,
 +                      const rvec comA_sc, const rvec comB_sc,
 +                      real lambda,
 +                      t_pbc *pbc, int refcoord_scaling, int npbcdim,
 +                      rvec dx, rvec rdist, rvec dpdl)
 +{
 +    int  m, d;
 +    real posA, posB, L1, ref = 0.;
 +    rvec pos;
 +
 +    L1 = 1.0-lambda;
 +
 +    for (m = 0; m < DIM; m++)
 +    {
 +        posA = pos0A[m];
 +        posB = pos0B[m];
 +        if (m < npbcdim)
 +        {
 +            switch (refcoord_scaling)
 +            {
 +                case erscNO:
 +                    ref      = 0;
 +                    rdist[m] = L1*posA + lambda*posB;
 +                    dpdl[m]  = posB - posA;
 +                    break;
 +                case erscALL:
 +                    /* Box relative coordinates are stored for dimensions with pbc */
 +                    posA *= pbc->box[m][m];
 +                    posB *= pbc->box[m][m];
 +                    for (d = m+1; d < npbcdim; d++)
 +                    {
 +                        posA += pos0A[d]*pbc->box[d][m];
 +                        posB += pos0B[d]*pbc->box[d][m];
 +                    }
 +                    ref      = L1*posA + lambda*posB;
 +                    rdist[m] = 0;
 +                    dpdl[m]  = posB - posA;
 +                    break;
 +                case erscCOM:
 +                    ref      = L1*comA_sc[m] + lambda*comB_sc[m];
 +                    rdist[m] = L1*posA       + lambda*posB;
 +                    dpdl[m]  = comB_sc[m] - comA_sc[m] + posB - posA;
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "No such scaling method implemented");
 +            }
 +        }
 +        else
 +        {
 +            ref      = L1*posA + lambda*posB;
 +            rdist[m] = 0;
 +            dpdl[m]  = posB - posA;
 +        }
 +
 +        /* We do pbc_dx with ref+rdist,
 +         * since with only ref we can be up to half a box vector wrong.
 +         */
 +        pos[m] = ref + rdist[m];
 +    }
 +
 +    if (pbc)
 +    {
 +        pbc_dx(pbc, x, pos, dx);
 +    }
 +    else
 +    {
 +        rvec_sub(x, pos, dx);
 +    }
 +}
 +
 +/*! \brief Adds forces of flat-bottomed positions restraints to f[]
 + *         and fixes vir_diag. Returns the flat-bottomed potential. */
 +real fbposres(int nbonds,
 +              const t_iatom forceatoms[], const t_iparams forceparams[],
 +              const rvec x[], rvec f[], rvec vir_diag,
 +              t_pbc *pbc,
 +              int refcoord_scaling, int ePBC, rvec com)
 +/* compute flat-bottomed positions restraints */
 +{
 +    int              i, ai, m, d, type, npbcdim = 0, fbdim;
 +    const t_iparams *pr;
 +    real             vtot, kk, v;
 +    real             ref = 0, dr, dr2, rpot, rfb, rfb2, fact, invdr;
 +    rvec             com_sc, rdist, pos, dx, dpdl, fm;
 +    gmx_bool         bInvert;
 +
 +    npbcdim = ePBC2npbcdim(ePBC);
 +
 +    if (refcoord_scaling == erscCOM)
 +    {
 +        clear_rvec(com_sc);
 +        for (m = 0; m < npbcdim; m++)
 +        {
 +            for (d = m; d < npbcdim; d++)
 +            {
 +                com_sc[m] += com[d]*pbc->box[d][m];
 +            }
 +        }
 +    }
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        pr   = &forceparams[type];
 +
 +        /* same calculation as for normal posres, but with identical A and B states, and lambda==0 */
 +        posres_dx(x[ai], forceparams[type].fbposres.pos0, forceparams[type].fbposres.pos0,
 +                  com_sc, com_sc, 0.0,
 +                  pbc, refcoord_scaling, npbcdim,
 +                  dx, rdist, dpdl);
 +
 +        clear_rvec(fm);
 +        v = 0.0;
 +
 +        kk   = pr->fbposres.k;
 +        rfb  = pr->fbposres.r;
 +        rfb2 = sqr(rfb);
 +
 +        /* with rfb<0, push particle out of the sphere/cylinder/layer */
 +        bInvert = FALSE;
 +        if (rfb < 0.)
 +        {
 +            bInvert = TRUE;
 +            rfb     = -rfb;
 +        }
 +
 +        switch (pr->fbposres.geom)
 +        {
 +            case efbposresSPHERE:
 +                /* spherical flat-bottom posres */
 +                dr2 = norm2(dx);
 +                if (dr2 > 0.0 &&
 +                    ( (dr2 > rfb2 && bInvert == FALSE ) || (dr2 < rfb2 && bInvert == TRUE ) )
 +                    )
 +                {
 +                    dr   = sqrt(dr2);
 +                    v    = 0.5*kk*sqr(dr - rfb);
 +                    fact = -kk*(dr-rfb)/dr; /* Force pointing to the center pos0 */
 +                    svmul(fact, dx, fm);
 +                }
 +                break;
 +            case efbposresCYLINDER:
 +                /* cylidrical flat-bottom posres in x-y plane. fm[ZZ] = 0. */
 +                dr2 = sqr(dx[XX])+sqr(dx[YY]);
 +                if  (dr2 > 0.0 &&
 +                     ( (dr2 > rfb2 && bInvert == FALSE ) || (dr2 < rfb2 && bInvert == TRUE ) )
 +                     )
 +                {
 +                    dr     = sqrt(dr2);
 +                    invdr  = 1./dr;
 +                    v      = 0.5*kk*sqr(dr - rfb);
 +                    fm[XX] = -kk*(dr-rfb)*dx[XX]*invdr; /* Force pointing to the center */
 +                    fm[YY] = -kk*(dr-rfb)*dx[YY]*invdr;
 +                }
 +                break;
 +            case efbposresX: /* fbdim=XX */
 +            case efbposresY: /* fbdim=YY */
 +            case efbposresZ: /* fbdim=ZZ */
 +                /* 1D flat-bottom potential */
 +                fbdim = pr->fbposres.geom - efbposresX;
 +                dr    = dx[fbdim];
 +                if ( ( dr > rfb && bInvert == FALSE ) || ( 0 < dr && dr < rfb && bInvert == TRUE )  )
 +                {
 +                    v         = 0.5*kk*sqr(dr - rfb);
 +                    fm[fbdim] = -kk*(dr - rfb);
 +                }
 +                else if ( (dr < (-rfb) && bInvert == FALSE ) || ( (-rfb) < dr && dr < 0 && bInvert == TRUE ))
 +                {
 +                    v         = 0.5*kk*sqr(dr + rfb);
 +                    fm[fbdim] = -kk*(dr + rfb);
 +                }
 +                break;
 +        }
 +
 +        vtot += v;
 +
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            f[ai][m]   += fm[m];
 +            /* Here we correct for the pbc_dx which included rdist */
 +            vir_diag[m] -= 0.5*(dx[m] + rdist[m])*fm[m];
 +        }
 +    }
 +
 +    return vtot;
 +}
 +
 +
 +real posres(int nbonds,
 +            const t_iatom forceatoms[], const t_iparams forceparams[],
 +            const rvec x[], rvec f[], rvec vir_diag,
 +            t_pbc *pbc,
 +            real lambda, real *dvdlambda,
 +            int refcoord_scaling, int ePBC, rvec comA, rvec comB)
 +{
 +    int              i, ai, m, d, type, ki, npbcdim = 0;
 +    const t_iparams *pr;
 +    real             L1;
 +    real             vtot, kk, fm;
 +    real             posA, posB, ref = 0;
 +    rvec             comA_sc, comB_sc, rdist, dpdl, pos, dx;
 +    gmx_bool         bForceValid = TRUE;
 +
 +    if ((f == NULL) || (vir_diag == NULL))    /* should both be null together! */
 +    {
 +        bForceValid = FALSE;
 +    }
 +
 +    npbcdim = ePBC2npbcdim(ePBC);
 +
 +    if (refcoord_scaling == erscCOM)
 +    {
 +        clear_rvec(comA_sc);
 +        clear_rvec(comB_sc);
 +        for (m = 0; m < npbcdim; m++)
 +        {
 +            for (d = m; d < npbcdim; d++)
 +            {
 +                comA_sc[m] += comA[d]*pbc->box[d][m];
 +                comB_sc[m] += comB[d]*pbc->box[d][m];
 +            }
 +        }
 +    }
 +
 +    L1 = 1.0 - lambda;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        pr   = &forceparams[type];
 +
 +        /* return dx, rdist, and dpdl */
 +        posres_dx(x[ai], forceparams[type].posres.pos0A, forceparams[type].posres.pos0B,
 +                  comA_sc, comB_sc, lambda,
 +                  pbc, refcoord_scaling, npbcdim,
 +                  dx, rdist, dpdl);
 +
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            kk          = L1*pr->posres.fcA[m] + lambda*pr->posres.fcB[m];
 +            fm          = -kk*dx[m];
 +            vtot       += 0.5*kk*dx[m]*dx[m];
 +            *dvdlambda +=
 +                0.5*(pr->posres.fcB[m] - pr->posres.fcA[m])*dx[m]*dx[m]
 +                -fm*dpdl[m];
 +
 +            /* Here we correct for the pbc_dx which included rdist */
 +            if (bForceValid)
 +            {
 +                f[ai][m]    += fm;
 +                vir_diag[m] -= 0.5*(dx[m] + rdist[m])*fm;
 +            }
 +        }
 +    }
 +
 +    return vtot;
 +}
 +
 +static real low_angres(int nbonds,
 +                       const t_iatom forceatoms[], const t_iparams forceparams[],
 +                       const rvec x[], rvec f[], rvec fshift[],
 +                       const t_pbc *pbc, const t_graph *g,
 +                       real lambda, real *dvdlambda,
 +                       gmx_bool bZAxis)
 +{
 +    int  i, m, type, ai, aj, ak, al;
 +    int  t1, t2;
 +    real phi, cos_phi, cos_phi2, vid, vtot, dVdphi;
 +    rvec r_ij, r_kl, f_i, f_k = {0, 0, 0};
 +    real st, sth, nrij2, nrkl2, c, cij, ckl;
 +
 +    ivec dt;
 +    t2 = 0; /* avoid warning with gcc-3.3. It is never used uninitialized */
 +
 +    vtot = 0.0;
 +    ak   = al = 0; /* to avoid warnings */
 +    for (i = 0; i < nbonds; )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        t1   = pbc_rvec_sub(pbc, x[aj], x[ai], r_ij);       /*  3             */
 +        if (!bZAxis)
 +        {
 +            ak   = forceatoms[i++];
 +            al   = forceatoms[i++];
 +            t2   = pbc_rvec_sub(pbc, x[al], x[ak], r_kl);  /*  3              */
 +        }
 +        else
 +        {
 +            r_kl[XX] = 0;
 +            r_kl[YY] = 0;
 +            r_kl[ZZ] = 1;
 +        }
 +
 +        cos_phi = cos_angle(r_ij, r_kl); /* 25                */
 +        phi     = acos(cos_phi);         /* 10           */
 +
 +        *dvdlambda += dopdihs_min(forceparams[type].pdihs.cpA,
 +                                  forceparams[type].pdihs.cpB,
 +                                  forceparams[type].pdihs.phiA,
 +                                  forceparams[type].pdihs.phiB,
 +                                  forceparams[type].pdihs.mult,
 +                                  phi, lambda, &vid, &dVdphi); /*  40  */
 +
 +        vtot += vid;
 +
 +        cos_phi2 = sqr(cos_phi);                /*   1                */
 +        if (cos_phi2 < 1)
 +        {
 +            st    = -dVdphi*gmx_invsqrt(1 - cos_phi2); /*  12         */
 +            sth   = st*cos_phi;                        /*   1         */
 +            nrij2 = iprod(r_ij, r_ij);                 /*   5         */
 +            nrkl2 = iprod(r_kl, r_kl);                 /*   5          */
 +
 +            c   = st*gmx_invsqrt(nrij2*nrkl2);         /*  11         */
 +            cij = sth/nrij2;                           /*  10         */
 +            ckl = sth/nrkl2;                           /*  10         */
 +
 +            for (m = 0; m < DIM; m++)                  /*  18+18       */
 +            {
 +                f_i[m]    = (c*r_kl[m]-cij*r_ij[m]);
 +                f[ai][m] += f_i[m];
 +                f[aj][m] -= f_i[m];
 +                if (!bZAxis)
 +                {
 +                    f_k[m]    = (c*r_ij[m]-ckl*r_kl[m]);
 +                    f[ak][m] += f_k[m];
 +                    f[al][m] -= f_k[m];
 +                }
 +            }
 +
 +            if (g)
 +            {
 +                ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +                t1 = IVEC2IS(dt);
 +            }
 +            rvec_inc(fshift[t1], f_i);
 +            rvec_dec(fshift[CENTRAL], f_i);
 +            if (!bZAxis)
 +            {
 +                if (g)
 +                {
 +                    ivec_sub(SHIFT_IVEC(g, ak), SHIFT_IVEC(g, al), dt);
 +                    t2 = IVEC2IS(dt);
 +                }
 +                rvec_inc(fshift[t2], f_k);
 +                rvec_dec(fshift[CENTRAL], f_k);
 +            }
 +        }
 +    }
 +
 +    return vtot; /*  184 / 157 (bZAxis)  total  */
 +}
 +
 +real angres(int nbonds,
 +            const t_iatom forceatoms[], const t_iparams forceparams[],
 +            const rvec x[], rvec f[], rvec fshift[],
 +            const t_pbc *pbc, const t_graph *g,
 +            real lambda, real *dvdlambda,
 +            const t_mdatoms *md, t_fcdata *fcd,
 +            int *global_atom_index)
 +{
 +    return low_angres(nbonds, forceatoms, forceparams, x, f, fshift, pbc, g,
 +                      lambda, dvdlambda, FALSE);
 +}
 +
 +real angresz(int nbonds,
 +             const t_iatom forceatoms[], const t_iparams forceparams[],
 +             const rvec x[], rvec f[], rvec fshift[],
 +             const t_pbc *pbc, const t_graph *g,
 +             real lambda, real *dvdlambda,
 +             const t_mdatoms *md, t_fcdata *fcd,
 +             int *global_atom_index)
 +{
 +    return low_angres(nbonds, forceatoms, forceparams, x, f, fshift, pbc, g,
 +                      lambda, dvdlambda, TRUE);
 +}
 +
 +real dihres(int nbonds,
 +            const t_iatom forceatoms[], const t_iparams forceparams[],
 +            const rvec x[], rvec f[], rvec fshift[],
 +            const t_pbc *pbc, const t_graph *g,
 +            real lambda, real *dvdlambda,
 +            const t_mdatoms *md, t_fcdata *fcd,
 +            int *global_atom_index)
 +{
 +    real vtot = 0;
 +    int  ai, aj, ak, al, i, k, type, t1, t2, t3;
 +    real phi0A, phi0B, dphiA, dphiB, kfacA, kfacB, phi0, dphi, kfac;
 +    real phi, ddphi, ddp, ddp2, dp, sign, d2r, fc, L1;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +
 +    L1 = 1.0-lambda;
 +
 +    d2r = DEG2RAD;
 +    k   = 0;
 +
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        phi0A  = forceparams[type].dihres.phiA*d2r;
 +        dphiA  = forceparams[type].dihres.dphiA*d2r;
 +        kfacA  = forceparams[type].dihres.kfacA;
 +
 +        phi0B  = forceparams[type].dihres.phiB*d2r;
 +        dphiB  = forceparams[type].dihres.dphiB*d2r;
 +        kfacB  = forceparams[type].dihres.kfacB;
 +
 +        phi0  = L1*phi0A + lambda*phi0B;
 +        dphi  = L1*dphiA + lambda*dphiB;
 +        kfac  = L1*kfacA + lambda*kfacB;
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);
 +        /* 84 flops */
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "dihres[%d]: %d %d %d %d : phi=%f, dphi=%f, kfac=%f\n",
 +                    k++, ai, aj, ak, al, phi0, dphi, kfac);
 +        }
 +        /* phi can jump if phi0 is close to Pi/-Pi, which will cause huge
 +         * force changes if we just apply a normal harmonic.
 +         * Instead, we first calculate phi-phi0 and take it modulo (-Pi,Pi).
 +         * This means we will never have the periodicity problem, unless
 +         * the dihedral is Pi away from phiO, which is very unlikely due to
 +         * the potential.
 +         */
 +        dp = phi-phi0;
 +        make_dp_periodic(&dp);
 +
 +        if (dp > dphi)
 +        {
 +            ddp = dp-dphi;
 +        }
 +        else if (dp < -dphi)
 +        {
 +            ddp = dp+dphi;
 +        }
 +        else
 +        {
 +            ddp = 0;
 +        }
 +
 +        if (ddp != 0.0)
 +        {
 +            ddp2  = ddp*ddp;
 +            vtot += 0.5*kfac*ddp2;
 +            ddphi = kfac*ddp;
 +
 +            *dvdlambda += 0.5*(kfacB - kfacA)*ddp2;
 +            /* lambda dependence from changing restraint distances */
 +            if (ddp > 0)
 +            {
 +                *dvdlambda -= kfac*ddp*((dphiB - dphiA)+(phi0B - phi0A));
 +            }
 +            else if (ddp < 0)
 +            {
 +                *dvdlambda += kfac*ddp*((dphiB - dphiA)-(phi0B - phi0A));
 +            }
 +            do_dih_fup(ai, aj, ak, al, ddphi, r_ij, r_kj, r_kl, m, n,
 +                       f, fshift, pbc, g, x, t1, t2, t3);      /* 112         */
 +        }
 +    }
 +    return vtot;
 +}
 +
 +
 +real unimplemented(int nbonds,
 +                   const t_iatom forceatoms[], const t_iparams forceparams[],
 +                   const rvec x[], rvec f[], rvec fshift[],
 +                   const t_pbc *pbc, const t_graph *g,
 +                   real lambda, real *dvdlambda,
 +                   const t_mdatoms *md, t_fcdata *fcd,
 +                   int *global_atom_index)
 +{
 +    gmx_impl("*** you are using a not implemented function");
 +
 +    return 0.0; /* To make the compiler happy */
 +}
 +
 +real rbdihs(int nbonds,
 +            const t_iatom forceatoms[], const t_iparams forceparams[],
 +            const rvec x[], rvec f[], rvec fshift[],
 +            const t_pbc *pbc, const t_graph *g,
 +            real lambda, real *dvdlambda,
 +            const t_mdatoms *md, t_fcdata *fcd,
 +            int *global_atom_index)
 +{
 +    const real c0 = 0.0, c1 = 1.0, c2 = 2.0, c3 = 3.0, c4 = 4.0, c5 = 5.0;
 +    int        type, ai, aj, ak, al, i, j;
 +    int        t1, t2, t3;
 +    rvec       r_ij, r_kj, r_kl, m, n;
 +    real       parmA[NR_RBDIHS];
 +    real       parmB[NR_RBDIHS];
 +    real       parm[NR_RBDIHS];
 +    real       cos_phi, phi, rbp, rbpBA;
 +    real       v, sign, ddphi, sin_phi;
 +    real       cosfac, vtot;
 +    real       L1        = 1.0-lambda;
 +    real       dvdl_term = 0;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);  /*  84                */
 +
 +        /* Change to polymer convention */
 +        if (phi < c0)
 +        {
 +            phi += M_PI;
 +        }
 +        else
 +        {
 +            phi -= M_PI;    /*   1            */
 +
 +        }
 +        cos_phi = cos(phi);
 +        /* Beware of accuracy loss, cannot use 1-sqrt(cos^2) ! */
 +        sin_phi = sin(phi);
 +
 +        for (j = 0; (j < NR_RBDIHS); j++)
 +        {
 +            parmA[j] = forceparams[type].rbdihs.rbcA[j];
 +            parmB[j] = forceparams[type].rbdihs.rbcB[j];
 +            parm[j]  = L1*parmA[j]+lambda*parmB[j];
 +        }
 +        /* Calculate cosine powers */
 +        /* Calculate the energy */
 +        /* Calculate the derivative */
 +
 +        v            = parm[0];
 +        dvdl_term   += (parmB[0]-parmA[0]);
 +        ddphi        = c0;
 +        cosfac       = c1;
 +
 +        rbp          = parm[1];
 +        rbpBA        = parmB[1]-parmA[1];
 +        ddphi       += rbp*cosfac;
 +        cosfac      *= cos_phi;
 +        v           += cosfac*rbp;
 +        dvdl_term   += cosfac*rbpBA;
 +        rbp          = parm[2];
 +        rbpBA        = parmB[2]-parmA[2];
 +        ddphi       += c2*rbp*cosfac;
 +        cosfac      *= cos_phi;
 +        v           += cosfac*rbp;
 +        dvdl_term   += cosfac*rbpBA;
 +        rbp          = parm[3];
 +        rbpBA        = parmB[3]-parmA[3];
 +        ddphi       += c3*rbp*cosfac;
 +        cosfac      *= cos_phi;
 +        v           += cosfac*rbp;
 +        dvdl_term   += cosfac*rbpBA;
 +        rbp          = parm[4];
 +        rbpBA        = parmB[4]-parmA[4];
 +        ddphi       += c4*rbp*cosfac;
 +        cosfac      *= cos_phi;
 +        v           += cosfac*rbp;
 +        dvdl_term   += cosfac*rbpBA;
 +        rbp          = parm[5];
 +        rbpBA        = parmB[5]-parmA[5];
 +        ddphi       += c5*rbp*cosfac;
 +        cosfac      *= cos_phi;
 +        v           += cosfac*rbp;
 +        dvdl_term   += cosfac*rbpBA;
 +
 +        ddphi = -ddphi*sin_phi;         /*  11                */
 +
 +        do_dih_fup(ai, aj, ak, al, ddphi, r_ij, r_kj, r_kl, m, n,
 +                   f, fshift, pbc, g, x, t1, t2, t3); /* 112          */
 +        vtot += v;
 +    }
 +    *dvdlambda += dvdl_term;
 +
 +    return vtot;
 +}
 +
 +int cmap_setup_grid_index(int ip, int grid_spacing, int *ipm1, int *ipp1, int *ipp2)
 +{
 +    int im1, ip1, ip2;
 +
 +    if (ip < 0)
 +    {
 +        ip = ip + grid_spacing - 1;
 +    }
 +    else if (ip > grid_spacing)
 +    {
 +        ip = ip - grid_spacing - 1;
 +    }
 +
 +    im1 = ip - 1;
 +    ip1 = ip + 1;
 +    ip2 = ip + 2;
 +
 +    if (ip == 0)
 +    {
 +        im1 = grid_spacing - 1;
 +    }
 +    else if (ip == grid_spacing-2)
 +    {
 +        ip2 = 0;
 +    }
 +    else if (ip == grid_spacing-1)
 +    {
 +        ip1 = 0;
 +        ip2 = 1;
 +    }
 +
 +    *ipm1 = im1;
 +    *ipp1 = ip1;
 +    *ipp2 = ip2;
 +
 +    return ip;
 +
 +}
 +
 +real cmap_dihs(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const gmx_cmap_t *cmap_grid,
 +               const rvec x[], rvec f[], rvec fshift[],
 +               const t_pbc *pbc, const t_graph *g,
 +               real lambda, real *dvdlambda,
 +               const t_mdatoms *md, t_fcdata *fcd,
 +               int *global_atom_index)
 +{
 +    int         i, j, k, n, idx;
 +    int         ai, aj, ak, al, am;
 +    int         a1i, a1j, a1k, a1l, a2i, a2j, a2k, a2l;
 +    int         type, cmapA;
 +    int         t11, t21, t31, t12, t22, t32;
 +    int         iphi1, ip1m1, ip1p1, ip1p2;
 +    int         iphi2, ip2m1, ip2p1, ip2p2;
 +    int         l1, l2, l3, l4;
 +    int         pos1, pos2, pos3, pos4, tmp;
 +
 +    real        ty[4], ty1[4], ty2[4], ty12[4], tc[16], tx[16];
 +    real        phi1, psi1, cos_phi1, sin_phi1, sign1, xphi1;
 +    real        phi2, psi2, cos_phi2, sin_phi2, sign2, xphi2;
 +    real        dx, xx, tt, tu, e, df1, df2, ddf1, ddf2, ddf12, vtot;
 +    real        ra21, rb21, rg21, rg1, rgr1, ra2r1, rb2r1, rabr1;
 +    real        ra22, rb22, rg22, rg2, rgr2, ra2r2, rb2r2, rabr2;
 +    real        fg1, hg1, fga1, hgb1, gaa1, gbb1;
 +    real        fg2, hg2, fga2, hgb2, gaa2, gbb2;
 +    real        fac;
 +
 +    rvec        r1_ij, r1_kj, r1_kl, m1, n1;
 +    rvec        r2_ij, r2_kj, r2_kl, m2, n2;
 +    rvec        f1_i, f1_j, f1_k, f1_l;
 +    rvec        f2_i, f2_j, f2_k, f2_l;
 +    rvec        a1, b1, a2, b2;
 +    rvec        f1, g1, h1, f2, g2, h2;
 +    rvec        dtf1, dtg1, dth1, dtf2, dtg2, dth2;
 +    ivec        jt1, dt1_ij, dt1_kj, dt1_lj;
 +    ivec        jt2, dt2_ij, dt2_kj, dt2_lj;
 +
 +    const real *cmapd;
 +
 +    int         loop_index[4][4] = {
 +        {0, 4, 8, 12},
 +        {1, 5, 9, 13},
 +        {2, 6, 10, 14},
 +        {3, 7, 11, 15}
 +    };
 +
 +    /* Total CMAP energy */
 +    vtot = 0;
 +
 +    for (n = 0; n < nbonds; )
 +    {
 +        /* Five atoms are involved in the two torsions */
 +        type   = forceatoms[n++];
 +        ai     = forceatoms[n++];
 +        aj     = forceatoms[n++];
 +        ak     = forceatoms[n++];
 +        al     = forceatoms[n++];
 +        am     = forceatoms[n++];
 +
 +        /* Which CMAP type is this */
 +        cmapA = forceparams[type].cmap.cmapA;
 +        cmapd = cmap_grid->cmapdata[cmapA].cmap;
 +
 +        /* First torsion */
 +        a1i   = ai;
 +        a1j   = aj;
 +        a1k   = ak;
 +        a1l   = al;
 +
 +        phi1  = dih_angle(x[a1i], x[a1j], x[a1k], x[a1l], pbc, r1_ij, r1_kj, r1_kl, m1, n1,
 +                          &sign1, &t11, &t21, &t31);  /* 84 */
 +
 +        cos_phi1 = cos(phi1);
 +
 +        a1[0] = r1_ij[1]*r1_kj[2]-r1_ij[2]*r1_kj[1];
 +        a1[1] = r1_ij[2]*r1_kj[0]-r1_ij[0]*r1_kj[2];
 +        a1[2] = r1_ij[0]*r1_kj[1]-r1_ij[1]*r1_kj[0]; /* 9 */
 +
 +        b1[0] = r1_kl[1]*r1_kj[2]-r1_kl[2]*r1_kj[1];
 +        b1[1] = r1_kl[2]*r1_kj[0]-r1_kl[0]*r1_kj[2];
 +        b1[2] = r1_kl[0]*r1_kj[1]-r1_kl[1]*r1_kj[0]; /* 9 */
 +
 +        tmp = pbc_rvec_sub(pbc, x[a1l], x[a1k], h1);
 +
 +        ra21  = iprod(a1, a1);       /* 5 */
 +        rb21  = iprod(b1, b1);       /* 5 */
 +        rg21  = iprod(r1_kj, r1_kj); /* 5 */
 +        rg1   = sqrt(rg21);
 +
 +        rgr1  = 1.0/rg1;
 +        ra2r1 = 1.0/ra21;
 +        rb2r1 = 1.0/rb21;
 +        rabr1 = sqrt(ra2r1*rb2r1);
 +
 +        sin_phi1 = rg1 * rabr1 * iprod(a1, h1) * (-1);
 +
 +        if (cos_phi1 < -0.5 || cos_phi1 > 0.5)
 +        {
 +            phi1 = asin(sin_phi1);
 +
 +            if (cos_phi1 < 0)
 +            {
 +                if (phi1 > 0)
 +                {
 +                    phi1 = M_PI - phi1;
 +                }
 +                else
 +                {
 +                    phi1 = -M_PI - phi1;
 +                }
 +            }
 +        }
 +        else
 +        {
 +            phi1 = acos(cos_phi1);
 +
 +            if (sin_phi1 < 0)
 +            {
 +                phi1 = -phi1;
 +            }
 +        }
 +
 +        xphi1 = phi1 + M_PI; /* 1 */
 +
 +        /* Second torsion */
 +        a2i   = aj;
 +        a2j   = ak;
 +        a2k   = al;
 +        a2l   = am;
 +
 +        phi2  = dih_angle(x[a2i], x[a2j], x[a2k], x[a2l], pbc, r2_ij, r2_kj, r2_kl, m2, n2,
 +                          &sign2, &t12, &t22, &t32); /* 84 */
 +
 +        cos_phi2 = cos(phi2);
 +
 +        a2[0] = r2_ij[1]*r2_kj[2]-r2_ij[2]*r2_kj[1];
 +        a2[1] = r2_ij[2]*r2_kj[0]-r2_ij[0]*r2_kj[2];
 +        a2[2] = r2_ij[0]*r2_kj[1]-r2_ij[1]*r2_kj[0]; /* 9 */
 +
 +        b2[0] = r2_kl[1]*r2_kj[2]-r2_kl[2]*r2_kj[1];
 +        b2[1] = r2_kl[2]*r2_kj[0]-r2_kl[0]*r2_kj[2];
 +        b2[2] = r2_kl[0]*r2_kj[1]-r2_kl[1]*r2_kj[0]; /* 9 */
 +
 +        tmp = pbc_rvec_sub(pbc, x[a2l], x[a2k], h2);
 +
 +        ra22  = iprod(a2, a2);         /* 5 */
 +        rb22  = iprod(b2, b2);         /* 5 */
 +        rg22  = iprod(r2_kj, r2_kj);   /* 5 */
 +        rg2   = sqrt(rg22);
 +
 +        rgr2  = 1.0/rg2;
 +        ra2r2 = 1.0/ra22;
 +        rb2r2 = 1.0/rb22;
 +        rabr2 = sqrt(ra2r2*rb2r2);
 +
 +        sin_phi2 = rg2 * rabr2 * iprod(a2, h2) * (-1);
 +
 +        if (cos_phi2 < -0.5 || cos_phi2 > 0.5)
 +        {
 +            phi2 = asin(sin_phi2);
 +
 +            if (cos_phi2 < 0)
 +            {
 +                if (phi2 > 0)
 +                {
 +                    phi2 = M_PI - phi2;
 +                }
 +                else
 +                {
 +                    phi2 = -M_PI - phi2;
 +                }
 +            }
 +        }
 +        else
 +        {
 +            phi2 = acos(cos_phi2);
 +
 +            if (sin_phi2 < 0)
 +            {
 +                phi2 = -phi2;
 +            }
 +        }
 +
 +        xphi2 = phi2 + M_PI; /* 1 */
 +
 +        /* Range mangling */
 +        if (xphi1 < 0)
 +        {
 +            xphi1 = xphi1 + 2*M_PI;
 +        }
 +        else if (xphi1 >= 2*M_PI)
 +        {
 +            xphi1 = xphi1 - 2*M_PI;
 +        }
 +
 +        if (xphi2 < 0)
 +        {
 +            xphi2 = xphi2 + 2*M_PI;
 +        }
 +        else if (xphi2 >= 2*M_PI)
 +        {
 +            xphi2 = xphi2 - 2*M_PI;
 +        }
 +
 +        /* Number of grid points */
 +        dx = 2*M_PI / cmap_grid->grid_spacing;
 +
 +        /* Where on the grid are we */
 +        iphi1 = (int)(xphi1/dx);
 +        iphi2 = (int)(xphi2/dx);
 +
 +        iphi1 = cmap_setup_grid_index(iphi1, cmap_grid->grid_spacing, &ip1m1, &ip1p1, &ip1p2);
 +        iphi2 = cmap_setup_grid_index(iphi2, cmap_grid->grid_spacing, &ip2m1, &ip2p1, &ip2p2);
 +
 +        pos1    = iphi1*cmap_grid->grid_spacing+iphi2;
 +        pos2    = ip1p1*cmap_grid->grid_spacing+iphi2;
 +        pos3    = ip1p1*cmap_grid->grid_spacing+ip2p1;
 +        pos4    = iphi1*cmap_grid->grid_spacing+ip2p1;
 +
 +        ty[0]   = cmapd[pos1*4];
 +        ty[1]   = cmapd[pos2*4];
 +        ty[2]   = cmapd[pos3*4];
 +        ty[3]   = cmapd[pos4*4];
 +
 +        ty1[0]   = cmapd[pos1*4+1];
 +        ty1[1]   = cmapd[pos2*4+1];
 +        ty1[2]   = cmapd[pos3*4+1];
 +        ty1[3]   = cmapd[pos4*4+1];
 +
 +        ty2[0]   = cmapd[pos1*4+2];
 +        ty2[1]   = cmapd[pos2*4+2];
 +        ty2[2]   = cmapd[pos3*4+2];
 +        ty2[3]   = cmapd[pos4*4+2];
 +
 +        ty12[0]   = cmapd[pos1*4+3];
 +        ty12[1]   = cmapd[pos2*4+3];
 +        ty12[2]   = cmapd[pos3*4+3];
 +        ty12[3]   = cmapd[pos4*4+3];
 +
 +        /* Switch to degrees */
 +        dx    = 360.0 / cmap_grid->grid_spacing;
 +        xphi1 = xphi1 * RAD2DEG;
 +        xphi2 = xphi2 * RAD2DEG;
 +
 +        for (i = 0; i < 4; i++) /* 16 */
 +        {
 +            tx[i]    = ty[i];
 +            tx[i+4]  = ty1[i]*dx;
 +            tx[i+8]  = ty2[i]*dx;
 +            tx[i+12] = ty12[i]*dx*dx;
 +        }
 +
 +        idx = 0;
 +        for (i = 0; i < 4; i++) /* 1056 */
 +        {
 +            for (j = 0; j < 4; j++)
 +            {
 +                xx = 0;
 +                for (k = 0; k < 16; k++)
 +                {
 +                    xx = xx + cmap_coeff_matrix[k*16+idx]*tx[k];
 +                }
 +
 +                idx++;
 +                tc[i*4+j] = xx;
 +            }
 +        }
 +
 +        tt    = (xphi1-iphi1*dx)/dx;
 +        tu    = (xphi2-iphi2*dx)/dx;
 +
 +        e     = 0;
 +        df1   = 0;
 +        df2   = 0;
 +        ddf1  = 0;
 +        ddf2  = 0;
 +        ddf12 = 0;
 +
 +        for (i = 3; i >= 0; i--)
 +        {
 +            l1 = loop_index[i][3];
 +            l2 = loop_index[i][2];
 +            l3 = loop_index[i][1];
 +
 +            e     = tt * e    + ((tc[i*4+3]*tu+tc[i*4+2])*tu + tc[i*4+1])*tu+tc[i*4];
 +            df1   = tu * df1  + (3.0*tc[l1]*tt+2.0*tc[l2])*tt+tc[l3];
 +            df2   = tt * df2  + (3.0*tc[i*4+3]*tu+2.0*tc[i*4+2])*tu+tc[i*4+1];
 +            ddf1  = tu * ddf1 + 2.0*3.0*tc[l1]*tt+2.0*tc[l2];
 +            ddf2  = tt * ddf2 + 2.0*3.0*tc[4*i+3]*tu+2.0*tc[4*i+2];
 +        }
 +
 +        ddf12 = tc[5] + 2.0*tc[9]*tt + 3.0*tc[13]*tt*tt + 2.0*tu*(tc[6]+2.0*tc[10]*tt+3.0*tc[14]*tt*tt) +
 +            3.0*tu*tu*(tc[7]+2.0*tc[11]*tt+3.0*tc[15]*tt*tt);
 +
 +        fac     = RAD2DEG/dx;
 +        df1     = df1   * fac;
 +        df2     = df2   * fac;
 +        ddf1    = ddf1  * fac * fac;
 +        ddf2    = ddf2  * fac * fac;
 +        ddf12   = ddf12 * fac * fac;
 +
 +        /* CMAP energy */
 +        vtot += e;
 +
 +        /* Do forces - first torsion */
 +        fg1       = iprod(r1_ij, r1_kj);
 +        hg1       = iprod(r1_kl, r1_kj);
 +        fga1      = fg1*ra2r1*rgr1;
 +        hgb1      = hg1*rb2r1*rgr1;
 +        gaa1      = -ra2r1*rg1;
 +        gbb1      = rb2r1*rg1;
 +
 +        for (i = 0; i < DIM; i++)
 +        {
 +            dtf1[i]   = gaa1 * a1[i];
 +            dtg1[i]   = fga1 * a1[i] - hgb1 * b1[i];
 +            dth1[i]   = gbb1 * b1[i];
 +
 +            f1[i]     = df1  * dtf1[i];
 +            g1[i]     = df1  * dtg1[i];
 +            h1[i]     = df1  * dth1[i];
 +
 +            f1_i[i]   =  f1[i];
 +            f1_j[i]   = -f1[i] - g1[i];
 +            f1_k[i]   =  h1[i] + g1[i];
 +            f1_l[i]   = -h1[i];
 +
 +            f[a1i][i] = f[a1i][i] + f1_i[i];
 +            f[a1j][i] = f[a1j][i] + f1_j[i]; /* - f1[i] - g1[i] */
 +            f[a1k][i] = f[a1k][i] + f1_k[i]; /* h1[i] + g1[i] */
 +            f[a1l][i] = f[a1l][i] + f1_l[i]; /* h1[i] */
 +        }
 +
 +        /* Do forces - second torsion */
 +        fg2       = iprod(r2_ij, r2_kj);
 +        hg2       = iprod(r2_kl, r2_kj);
 +        fga2      = fg2*ra2r2*rgr2;
 +        hgb2      = hg2*rb2r2*rgr2;
 +        gaa2      = -ra2r2*rg2;
 +        gbb2      = rb2r2*rg2;
 +
 +        for (i = 0; i < DIM; i++)
 +        {
 +            dtf2[i]   = gaa2 * a2[i];
 +            dtg2[i]   = fga2 * a2[i] - hgb2 * b2[i];
 +            dth2[i]   = gbb2 * b2[i];
 +
 +            f2[i]     = df2  * dtf2[i];
 +            g2[i]     = df2  * dtg2[i];
 +            h2[i]     = df2  * dth2[i];
 +
 +            f2_i[i]   =  f2[i];
 +            f2_j[i]   = -f2[i] - g2[i];
 +            f2_k[i]   =  h2[i] + g2[i];
 +            f2_l[i]   = -h2[i];
 +
 +            f[a2i][i] = f[a2i][i] + f2_i[i]; /* f2[i] */
 +            f[a2j][i] = f[a2j][i] + f2_j[i]; /* - f2[i] - g2[i] */
 +            f[a2k][i] = f[a2k][i] + f2_k[i]; /* h2[i] + g2[i] */
 +            f[a2l][i] = f[a2l][i] + f2_l[i]; /* - h2[i] */
 +        }
 +
 +        /* Shift forces */
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, a1j), jt1);
 +            ivec_sub(SHIFT_IVEC(g, a1i),  jt1, dt1_ij);
 +            ivec_sub(SHIFT_IVEC(g, a1k),  jt1, dt1_kj);
 +            ivec_sub(SHIFT_IVEC(g, a1l),  jt1, dt1_lj);
 +            t11 = IVEC2IS(dt1_ij);
 +            t21 = IVEC2IS(dt1_kj);
 +            t31 = IVEC2IS(dt1_lj);
 +
 +            copy_ivec(SHIFT_IVEC(g, a2j), jt2);
 +            ivec_sub(SHIFT_IVEC(g, a2i),  jt2, dt2_ij);
 +            ivec_sub(SHIFT_IVEC(g, a2k),  jt2, dt2_kj);
 +            ivec_sub(SHIFT_IVEC(g, a2l),  jt2, dt2_lj);
 +            t12 = IVEC2IS(dt2_ij);
 +            t22 = IVEC2IS(dt2_kj);
 +            t32 = IVEC2IS(dt2_lj);
 +        }
 +        else if (pbc)
 +        {
 +            t31 = pbc_rvec_sub(pbc, x[a1l], x[a1j], h1);
 +            t32 = pbc_rvec_sub(pbc, x[a2l], x[a2j], h2);
 +        }
 +        else
 +        {
 +            t31 = CENTRAL;
 +            t32 = CENTRAL;
 +        }
 +
 +        rvec_inc(fshift[t11], f1_i);
 +        rvec_inc(fshift[CENTRAL], f1_j);
 +        rvec_inc(fshift[t21], f1_k);
 +        rvec_inc(fshift[t31], f1_l);
 +
 +        rvec_inc(fshift[t21], f2_i);
 +        rvec_inc(fshift[CENTRAL], f2_j);
 +        rvec_inc(fshift[t22], f2_k);
 +        rvec_inc(fshift[t32], f2_l);
 +    }
 +    return vtot;
 +}
 +
 +
 +
 +/***********************************************************
 + *
 + *   G R O M O S  9 6   F U N C T I O N S
 + *
 + ***********************************************************/
 +real g96harmonic(real kA, real kB, real xA, real xB, real x, real lambda,
 +                 real *V, real *F)
 +{
 +    const real half = 0.5;
 +    real       L1, kk, x0, dx, dx2;
 +    real       v, f, dvdlambda;
 +
 +    L1    = 1.0-lambda;
 +    kk    = L1*kA+lambda*kB;
 +    x0    = L1*xA+lambda*xB;
 +
 +    dx    = x-x0;
 +    dx2   = dx*dx;
 +
 +    f          = -kk*dx;
 +    v          = half*kk*dx2;
 +    dvdlambda  = half*(kB-kA)*dx2 + (xA-xB)*kk*dx;
 +
 +    *F    = f;
 +    *V    = v;
 +
 +    return dvdlambda;
 +
 +    /* That was 21 flops */
 +}
 +
 +real g96bonds(int nbonds,
 +              const t_iatom forceatoms[], const t_iparams forceparams[],
 +              const rvec x[], rvec f[], rvec fshift[],
 +              const t_pbc *pbc, const t_graph *g,
 +              real lambda, real *dvdlambda,
 +              const t_mdatoms *md, t_fcdata *fcd,
 +              int *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type;
 +    real dr2, fbond, vbond, fij, vtot;
 +    rvec dx;
 +    ivec dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx); /*   3      */
 +        dr2  = iprod(dx, dx);                       /*   5            */
 +
 +        *dvdlambda += g96harmonic(forceparams[type].harmonic.krA,
 +                                  forceparams[type].harmonic.krB,
 +                                  forceparams[type].harmonic.rA,
 +                                  forceparams[type].harmonic.rB,
 +                                  dr2, lambda, &vbond, &fbond);
 +
 +        vtot  += 0.5*vbond;                         /* 1*/
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "G96-BONDS: dr = %10g  vbond = %10g  fbond = %10g\n",
 +                    sqrt(dr2), vbond, fbond);
 +        }
 +#endif
 +
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }               /* 44 TOTAL       */
 +    return vtot;
 +}
 +
 +real g96bond_angle(const rvec xi, const rvec xj, const rvec xk, const t_pbc *pbc,
 +                   rvec r_ij, rvec r_kj,
 +                   int *t1, int *t2)
 +/* Return value is the angle between the bonds i-j and j-k */
 +{
 +    real costh;
 +
 +    *t1 = pbc_rvec_sub(pbc, xi, xj, r_ij); /*  3              */
 +    *t2 = pbc_rvec_sub(pbc, xk, xj, r_kj); /*  3              */
 +
 +    costh = cos_angle(r_ij, r_kj);         /* 25              */
 +    /* 41 TOTAL       */
 +    return costh;
 +}
 +
 +real g96angles(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const rvec x[], rvec f[], rvec fshift[],
 +               const t_pbc *pbc, const t_graph *g,
 +               real lambda, real *dvdlambda,
 +               const t_mdatoms *md, t_fcdata *fcd,
 +               int *global_atom_index)
 +{
 +    int  i, ai, aj, ak, type, m, t1, t2;
 +    rvec r_ij, r_kj;
 +    real cos_theta, dVdt, va, vtot;
 +    real rij_1, rij_2, rkj_1, rkj_2, rijrkj_1;
 +    rvec f_i, f_j, f_k;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        cos_theta  = g96bond_angle(x[ai], x[aj], x[ak], pbc, r_ij, r_kj, &t1, &t2);
 +
 +        *dvdlambda += g96harmonic(forceparams[type].harmonic.krA,
 +                                  forceparams[type].harmonic.krB,
 +                                  forceparams[type].harmonic.rA,
 +                                  forceparams[type].harmonic.rB,
 +                                  cos_theta, lambda, &va, &dVdt);
 +        vtot    += va;
 +
 +        rij_1    = gmx_invsqrt(iprod(r_ij, r_ij));
 +        rkj_1    = gmx_invsqrt(iprod(r_kj, r_kj));
 +        rij_2    = rij_1*rij_1;
 +        rkj_2    = rkj_1*rkj_1;
 +        rijrkj_1 = rij_1*rkj_1;                 /* 23 */
 +
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "G96ANGLES: costheta = %10g  vth = %10g  dV/dct = %10g\n",
 +                    cos_theta, va, dVdt);
 +        }
 +#endif
 +        for (m = 0; (m < DIM); m++)     /*  42        */
 +        {
 +            f_i[m]    = dVdt*(r_kj[m]*rijrkj_1 - r_ij[m]*rij_2*cos_theta);
 +            f_k[m]    = dVdt*(r_ij[m]*rijrkj_1 - r_kj[m]*rkj_2*cos_theta);
 +            f_j[m]    = -f_i[m]-f_k[m];
 +            f[ai][m] += f_i[m];
 +            f[aj][m] += f_j[m];
 +            f[ak][m] += f_k[m];
 +        }
 +
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +        }
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);          /* 9 */
 +        /* 163 TOTAL  */
 +    }
 +    return vtot;
 +}
 +
 +real cross_bond_bond(int nbonds,
 +                     const t_iatom forceatoms[], const t_iparams forceparams[],
 +                     const rvec x[], rvec f[], rvec fshift[],
 +                     const t_pbc *pbc, const t_graph *g,
 +                     real lambda, real *dvdlambda,
 +                     const t_mdatoms *md, t_fcdata *fcd,
 +                     int *global_atom_index)
 +{
 +    /* Potential from Lawrence and Skimmer, Chem. Phys. Lett. 372 (2003)
 +     * pp. 842-847
 +     */
 +    int  i, ai, aj, ak, type, m, t1, t2;
 +    rvec r_ij, r_kj;
 +    real vtot, vrr, s1, s2, r1, r2, r1e, r2e, krr;
 +    rvec f_i, f_j, f_k;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        r1e  = forceparams[type].cross_bb.r1e;
 +        r2e  = forceparams[type].cross_bb.r2e;
 +        krr  = forceparams[type].cross_bb.krr;
 +
 +        /* Compute distance vectors ... */
 +        t1 = pbc_rvec_sub(pbc, x[ai], x[aj], r_ij);
 +        t2 = pbc_rvec_sub(pbc, x[ak], x[aj], r_kj);
 +
 +        /* ... and their lengths */
 +        r1 = norm(r_ij);
 +        r2 = norm(r_kj);
 +
 +        /* Deviations from ideality */
 +        s1 = r1-r1e;
 +        s2 = r2-r2e;
 +
 +        /* Energy (can be negative!) */
 +        vrr   = krr*s1*s2;
 +        vtot += vrr;
 +
 +        /* Forces */
 +        svmul(-krr*s2/r1, r_ij, f_i);
 +        svmul(-krr*s1/r2, r_kj, f_k);
 +
 +        for (m = 0; (m < DIM); m++)     /*  12        */
 +        {
 +            f_j[m]    = -f_i[m] - f_k[m];
 +            f[ai][m] += f_i[m];
 +            f[aj][m] += f_j[m];
 +            f[ak][m] += f_k[m];
 +        }
 +
 +        /* Virial stuff */
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +        }
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);          /* 9 */
 +        /* 163 TOTAL  */
 +    }
 +    return vtot;
 +}
 +
 +real cross_bond_angle(int nbonds,
 +                      const t_iatom forceatoms[], const t_iparams forceparams[],
 +                      const rvec x[], rvec f[], rvec fshift[],
 +                      const t_pbc *pbc, const t_graph *g,
 +                      real lambda, real *dvdlambda,
 +                      const t_mdatoms *md, t_fcdata *fcd,
 +                      int *global_atom_index)
 +{
 +    /* Potential from Lawrence and Skimmer, Chem. Phys. Lett. 372 (2003)
 +     * pp. 842-847
 +     */
 +    int  i, ai, aj, ak, type, m, t1, t2, t3;
 +    rvec r_ij, r_kj, r_ik;
 +    real vtot, vrt, s1, s2, s3, r1, r2, r3, r1e, r2e, r3e, krt, k1, k2, k3;
 +    rvec f_i, f_j, f_k;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        r1e  = forceparams[type].cross_ba.r1e;
 +        r2e  = forceparams[type].cross_ba.r2e;
 +        r3e  = forceparams[type].cross_ba.r3e;
 +        krt  = forceparams[type].cross_ba.krt;
 +
 +        /* Compute distance vectors ... */
 +        t1 = pbc_rvec_sub(pbc, x[ai], x[aj], r_ij);
 +        t2 = pbc_rvec_sub(pbc, x[ak], x[aj], r_kj);
 +        t3 = pbc_rvec_sub(pbc, x[ai], x[ak], r_ik);
 +
 +        /* ... and their lengths */
 +        r1 = norm(r_ij);
 +        r2 = norm(r_kj);
 +        r3 = norm(r_ik);
 +
 +        /* Deviations from ideality */
 +        s1 = r1-r1e;
 +        s2 = r2-r2e;
 +        s3 = r3-r3e;
 +
 +        /* Energy (can be negative!) */
 +        vrt   = krt*s3*(s1+s2);
 +        vtot += vrt;
 +
 +        /* Forces */
 +        k1 = -krt*(s3/r1);
 +        k2 = -krt*(s3/r2);
 +        k3 = -krt*(s1+s2)/r3;
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            f_i[m] = k1*r_ij[m] + k3*r_ik[m];
 +            f_k[m] = k2*r_kj[m] - k3*r_ik[m];
 +            f_j[m] = -f_i[m] - f_k[m];
 +        }
 +
 +        for (m = 0; (m < DIM); m++)     /*  12        */
 +        {
 +            f[ai][m] += f_i[m];
 +            f[aj][m] += f_j[m];
 +            f[ak][m] += f_k[m];
 +        }
 +
 +        /* Virial stuff */
 +        if (g)
 +        {
 +            copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +            ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +            ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +            t1 = IVEC2IS(dt_ij);
 +            t2 = IVEC2IS(dt_kj);
 +        }
 +        rvec_inc(fshift[t1], f_i);
 +        rvec_inc(fshift[CENTRAL], f_j);
 +        rvec_inc(fshift[t2], f_k);          /* 9 */
 +        /* 163 TOTAL  */
 +    }
 +    return vtot;
 +}
 +
 +static real bonded_tab(const char *type, int table_nr,
 +                       const bondedtable_t *table, real kA, real kB, real r,
 +                       real lambda, real *V, real *F)
 +{
 +    real k, tabscale, *VFtab, rt, eps, eps2, Yt, Ft, Geps, Heps2, Fp, VV, FF;
 +    int  n0, nnn;
 +    real v, f, dvdlambda;
 +
 +    k = (1.0 - lambda)*kA + lambda*kB;
 +
 +    tabscale = table->scale;
 +    VFtab    = table->data;
 +
 +    rt    = r*tabscale;
 +    n0    = rt;
 +    if (n0 >= table->n)
 +    {
 +        gmx_fatal(FARGS, "A tabulated %s interaction table number %d is out of the table range: r %f, between table indices %d and %d, table length %d",
 +                  type, table_nr, r, n0, n0+1, table->n);
 +    }
 +    eps   = rt - n0;
 +    eps2  = eps*eps;
 +    nnn   = 4*n0;
 +    Yt    = VFtab[nnn];
 +    Ft    = VFtab[nnn+1];
 +    Geps  = VFtab[nnn+2]*eps;
 +    Heps2 = VFtab[nnn+3]*eps2;
 +    Fp    = Ft + Geps + Heps2;
 +    VV    = Yt + Fp*eps;
 +    FF    = Fp + Geps + 2.0*Heps2;
 +
 +    *F         = -k*FF*tabscale;
 +    *V         = k*VV;
 +    dvdlambda  = (kB - kA)*VV;
 +
 +    return dvdlambda;
 +
 +    /* That was 22 flops */
 +}
 +
 +real tab_bonds(int nbonds,
 +               const t_iatom forceatoms[], const t_iparams forceparams[],
 +               const rvec x[], rvec f[], rvec fshift[],
 +               const t_pbc *pbc, const t_graph *g,
 +               real lambda, real *dvdlambda,
 +               const t_mdatoms *md, t_fcdata *fcd,
 +               int *global_atom_index)
 +{
 +    int  i, m, ki, ai, aj, type, table;
 +    real dr, dr2, fbond, vbond, fij, vtot;
 +    rvec dx;
 +    ivec dt;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +
 +        ki   = pbc_rvec_sub(pbc, x[ai], x[aj], dx); /*   3      */
 +        dr2  = iprod(dx, dx);                       /*   5            */
 +        dr   = dr2*gmx_invsqrt(dr2);                /*  10            */
 +
 +        table = forceparams[type].tab.table;
 +
 +        *dvdlambda += bonded_tab("bond", table,
 +                                 &fcd->bondtab[table],
 +                                 forceparams[type].tab.kA,
 +                                 forceparams[type].tab.kB,
 +                                 dr, lambda, &vbond, &fbond); /*  22 */
 +
 +        if (dr2 == 0.0)
 +        {
 +            continue;
 +        }
 +
 +
 +        vtot  += vbond;            /* 1*/
 +        fbond *= gmx_invsqrt(dr2); /*   6             */
 +#ifdef DEBUG
 +        if (debug)
 +        {
 +            fprintf(debug, "TABBONDS: dr = %10g  vbond = %10g  fbond = %10g\n",
 +                    dr, vbond, fbond);
 +        }
 +#endif
 +        if (g)
 +        {
 +            ivec_sub(SHIFT_IVEC(g, ai), SHIFT_IVEC(g, aj), dt);
 +            ki = IVEC2IS(dt);
 +        }
 +        for (m = 0; (m < DIM); m++)     /*  15                */
 +        {
 +            fij                 = fbond*dx[m];
 +            f[ai][m]           += fij;
 +            f[aj][m]           -= fij;
 +            fshift[ki][m]      += fij;
 +            fshift[CENTRAL][m] -= fij;
 +        }
 +    }               /* 62 TOTAL       */
 +    return vtot;
 +}
 +
 +real tab_angles(int nbonds,
 +                const t_iatom forceatoms[], const t_iparams forceparams[],
 +                const rvec x[], rvec f[], rvec fshift[],
 +                const t_pbc *pbc, const t_graph *g,
 +                real lambda, real *dvdlambda,
 +                const t_mdatoms *md, t_fcdata *fcd,
 +                int *global_atom_index)
 +{
 +    int  i, ai, aj, ak, t1, t2, type, table;
 +    rvec r_ij, r_kj;
 +    real cos_theta, cos_theta2, theta, dVdt, va, vtot;
 +    ivec jt, dt_ij, dt_kj;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +
 +        theta  = bond_angle(x[ai], x[aj], x[ak], pbc,
 +                            r_ij, r_kj, &cos_theta, &t1, &t2); /*  41         */
 +
 +        table = forceparams[type].tab.table;
 +
 +        *dvdlambda += bonded_tab("angle", table,
 +                                 &fcd->angletab[table],
 +                                 forceparams[type].tab.kA,
 +                                 forceparams[type].tab.kB,
 +                                 theta, lambda, &va, &dVdt); /*  22  */
 +        vtot += va;
 +
 +        cos_theta2 = sqr(cos_theta);            /*   1                */
 +        if (cos_theta2 < 1)
 +        {
 +            int  m;
 +            real snt, st, sth;
 +            real cik, cii, ckk;
 +            real nrkj2, nrij2;
 +            rvec f_i, f_j, f_k;
 +
 +            st  = dVdt*gmx_invsqrt(1 - cos_theta2); /*  12            */
 +            sth = st*cos_theta;                     /*   1            */
 +#ifdef DEBUG
 +            if (debug)
 +            {
 +                fprintf(debug, "ANGLES: theta = %10g  vth = %10g  dV/dtheta = %10g\n",
 +                        theta*RAD2DEG, va, dVdt);
 +            }
 +#endif
 +            nrkj2 = iprod(r_kj, r_kj);  /*   5                */
 +            nrij2 = iprod(r_ij, r_ij);
 +
 +            cik = st*gmx_invsqrt(nrkj2*nrij2); /*  12         */
 +            cii = sth/nrij2;                   /*  10         */
 +            ckk = sth/nrkj2;                   /*  10         */
 +
 +            for (m = 0; (m < DIM); m++)        /*  39         */
 +            {
 +                f_i[m]    = -(cik*r_kj[m]-cii*r_ij[m]);
 +                f_k[m]    = -(cik*r_ij[m]-ckk*r_kj[m]);
 +                f_j[m]    = -f_i[m]-f_k[m];
 +                f[ai][m] += f_i[m];
 +                f[aj][m] += f_j[m];
 +                f[ak][m] += f_k[m];
 +            }
 +            if (g)
 +            {
 +                copy_ivec(SHIFT_IVEC(g, aj), jt);
 +
 +                ivec_sub(SHIFT_IVEC(g, ai), jt, dt_ij);
 +                ivec_sub(SHIFT_IVEC(g, ak), jt, dt_kj);
 +                t1 = IVEC2IS(dt_ij);
 +                t2 = IVEC2IS(dt_kj);
 +            }
 +            rvec_inc(fshift[t1], f_i);
 +            rvec_inc(fshift[CENTRAL], f_j);
 +            rvec_inc(fshift[t2], f_k);
 +        }                                       /* 169 TOTAL  */
 +    }
 +    return vtot;
 +}
 +
 +real tab_dihs(int nbonds,
 +              const t_iatom forceatoms[], const t_iparams forceparams[],
 +              const rvec x[], rvec f[], rvec fshift[],
 +              const t_pbc *pbc, const t_graph *g,
 +              real lambda, real *dvdlambda,
 +              const t_mdatoms *md, t_fcdata *fcd,
 +              int *global_atom_index)
 +{
 +    int  i, type, ai, aj, ak, al, table;
 +    int  t1, t2, t3;
 +    rvec r_ij, r_kj, r_kl, m, n;
 +    real phi, sign, ddphi, vpd, vtot;
 +
 +    vtot = 0.0;
 +    for (i = 0; (i < nbonds); )
 +    {
 +        type = forceatoms[i++];
 +        ai   = forceatoms[i++];
 +        aj   = forceatoms[i++];
 +        ak   = forceatoms[i++];
 +        al   = forceatoms[i++];
 +
 +        phi = dih_angle(x[ai], x[aj], x[ak], x[al], pbc, r_ij, r_kj, r_kl, m, n,
 +                        &sign, &t1, &t2, &t3);  /*  84  */
 +
 +        table = forceparams[type].tab.table;
 +
 +        /* Hopefully phi+M_PI never results in values < 0 */
 +        *dvdlambda += bonded_tab("dihedral", table,
 +                                 &fcd->dihtab[table],
 +                                 forceparams[type].tab.kA,
 +                                 forceparams[type].tab.kB,
 +                                 phi+M_PI, lambda, &vpd, &ddphi);
 +
 +        vtot += vpd;
 +        do_dih_fup(ai, aj, ak, al, -ddphi, r_ij, r_kj, r_kl, m, n,
 +                   f, fshift, pbc, g, x, t1, t2, t3); /* 112  */
 +
 +#ifdef DEBUG
 +        fprintf(debug, "pdih: (%d,%d,%d,%d) phi=%g\n",
 +                ai, aj, ak, al, phi);
 +#endif
 +    } /* 227 TOTAL  */
 +
 +    return vtot;
 +}
 +
 +static unsigned
 +calc_bonded_reduction_mask(const t_idef *idef,
 +                           int shift,
 +                           int t, int nt)
 +{
 +    unsigned mask;
 +    int      ftype, nb, nat1, nb0, nb1, i, a;
 +
 +    mask = 0;
 +
 +    for (ftype = 0; ftype < F_NRE; ftype++)
 +    {
 +        if (interaction_function[ftype].flags & IF_BOND &&
 +            !(ftype == F_CONNBONDS || ftype == F_POSRES) &&
 +            (ftype<F_GB12 || ftype>F_GB14))
 +        {
 +            nb = idef->il[ftype].nr;
 +            if (nb > 0)
 +            {
 +                nat1 = interaction_function[ftype].nratoms + 1;
 +
 +                /* Divide this interaction equally over the threads.
 +                 * This is not stored: should match division in calc_bonds.
 +                 */
 +                nb0 = (((nb/nat1)* t   )/nt)*nat1;
 +                nb1 = (((nb/nat1)*(t+1))/nt)*nat1;
 +
 +                for (i = nb0; i < nb1; i += nat1)
 +                {
 +                    for (a = 1; a < nat1; a++)
 +                    {
 +                        mask |= (1U << (idef->il[ftype].iatoms[i+a]>>shift));
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    return mask;
 +}
 +
 +void init_bonded_thread_force_reduction(t_forcerec   *fr,
 +                                        const t_idef *idef)
 +{
 +#define MAX_BLOCK_BITS 32
 +    int t;
 +    int ctot, c, b;
 +
 +    if (fr->nthreads <= 1)
 +    {
 +        fr->red_nblock = 0;
 +
 +        return;
 +    }
 +
 +    /* We divide the force array in a maximum of 32 blocks.
 +     * Minimum force block reduction size is 2^6=64.
 +     */
 +    fr->red_ashift = 6;
 +    while (fr->natoms_force > (int)(MAX_BLOCK_BITS*(1U<<fr->red_ashift)))
 +    {
 +        fr->red_ashift++;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "bonded force buffer block atom shift %d bits\n",
 +                fr->red_ashift);
 +    }
 +
 +    /* Determine to which blocks each thread's bonded force calculation
 +     * contributes. Store this is a mask for each thread.
 +     */
 +#pragma omp parallel for num_threads(fr->nthreads) schedule(static)
 +    for (t = 1; t < fr->nthreads; t++)
 +    {
 +        fr->f_t[t].red_mask =
 +            calc_bonded_reduction_mask(idef, fr->red_ashift, t, fr->nthreads);
 +    }
 +
 +    /* Determine the maximum number of blocks we need to reduce over */
 +    fr->red_nblock = 0;
 +    ctot           = 0;
 +    for (t = 0; t < fr->nthreads; t++)
 +    {
 +        c = 0;
 +        for (b = 0; b < MAX_BLOCK_BITS; b++)
 +        {
 +            if (fr->f_t[t].red_mask & (1U<<b))
 +            {
 +                fr->red_nblock = max(fr->red_nblock, b+1);
 +                c++;
 +            }
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "thread %d flags %x count %d\n",
 +                    t, fr->f_t[t].red_mask, c);
 +        }
 +        ctot += c;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "Number of blocks to reduce: %d of size %d\n",
 +                fr->red_nblock, 1<<fr->red_ashift);
 +        fprintf(debug, "Reduction density %.2f density/#thread %.2f\n",
 +                ctot*(1<<fr->red_ashift)/(double)fr->natoms_force,
 +                ctot*(1<<fr->red_ashift)/(double)(fr->natoms_force*fr->nthreads));
 +    }
 +}
 +
 +static void zero_thread_forces(f_thread_t *f_t, int n,
 +                               int nblock, int blocksize)
 +{
 +    int b, a0, a1, a, i, j;
 +
 +    if (n > f_t->f_nalloc)
 +    {
 +        f_t->f_nalloc = over_alloc_large(n);
 +        srenew(f_t->f, f_t->f_nalloc);
 +    }
 +
 +    if (f_t->red_mask != 0)
 +    {
 +        for (b = 0; b < nblock; b++)
 +        {
 +            if (f_t->red_mask && (1U<<b))
 +            {
 +                a0 = b*blocksize;
 +                a1 = min((b+1)*blocksize, n);
 +                for (a = a0; a < a1; a++)
 +                {
 +                    clear_rvec(f_t->f[a]);
 +                }
 +            }
 +        }
 +    }
 +    for (i = 0; i < SHIFTS; i++)
 +    {
 +        clear_rvec(f_t->fshift[i]);
 +    }
 +    for (i = 0; i < F_NRE; i++)
 +    {
 +        f_t->ener[i] = 0;
 +    }
 +    for (i = 0; i < egNR; i++)
 +    {
 +        for (j = 0; j < f_t->grpp.nener; j++)
 +        {
 +            f_t->grpp.ener[i][j] = 0;
 +        }
 +    }
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        f_t->dvdl[i] = 0;
 +    }
 +}
 +
 +static void reduce_thread_force_buffer(int n, rvec *f,
 +                                       int nthreads, f_thread_t *f_t,
 +                                       int nblock, int block_size)
 +{
 +    /* The max thread number is arbitrary,
 +     * we used a fixed number to avoid memory management.
 +     * Using more than 16 threads is probably never useful performance wise.
 +     */
 +#define MAX_BONDED_THREADS 256
 +    int b;
 +
 +    if (nthreads > MAX_BONDED_THREADS)
 +    {
 +        gmx_fatal(FARGS, "Can not reduce bonded forces on more than %d threads",
 +                  MAX_BONDED_THREADS);
 +    }
 +
 +    /* This reduction can run on any number of threads,
 +     * independently of nthreads.
 +     */
 +#pragma omp parallel for num_threads(nthreads) schedule(static)
 +    for (b = 0; b < nblock; b++)
 +    {
 +        rvec *fp[MAX_BONDED_THREADS];
 +        int   nfb, ft, fb;
 +        int   a0, a1, a;
 +
 +        /* Determine which threads contribute to this block */
 +        nfb = 0;
 +        for (ft = 1; ft < nthreads; ft++)
 +        {
 +            if (f_t[ft].red_mask & (1U<<b))
 +            {
 +                fp[nfb++] = f_t[ft].f;
 +            }
 +        }
 +        if (nfb > 0)
 +        {
 +            /* Reduce force buffers for threads that contribute */
 +            a0 =  b   *block_size;
 +            a1 = (b+1)*block_size;
 +            a1 = min(a1, n);
 +            for (a = a0; a < a1; a++)
 +            {
 +                for (fb = 0; fb < nfb; fb++)
 +                {
 +                    rvec_inc(f[a], fp[fb][a]);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static void reduce_thread_forces(int n, rvec *f, rvec *fshift,
 +                                 real *ener, gmx_grppairener_t *grpp, real *dvdl,
 +                                 int nthreads, f_thread_t *f_t,
 +                                 int nblock, int block_size,
 +                                 gmx_bool bCalcEnerVir,
 +                                 gmx_bool bDHDL)
 +{
 +    if (nblock > 0)
 +    {
 +        /* Reduce the bonded force buffer */
 +        reduce_thread_force_buffer(n, f, nthreads, f_t, nblock, block_size);
 +    }
 +
 +    /* When necessary, reduce energy and virial using one thread only */
 +    if (bCalcEnerVir)
 +    {
 +        int t, i, j;
 +
 +        for (i = 0; i < SHIFTS; i++)
 +        {
 +            for (t = 1; t < nthreads; t++)
 +            {
 +                rvec_inc(fshift[i], f_t[t].fshift[i]);
 +            }
 +        }
 +        for (i = 0; i < F_NRE; i++)
 +        {
 +            for (t = 1; t < nthreads; t++)
 +            {
 +                ener[i] += f_t[t].ener[i];
 +            }
 +        }
 +        for (i = 0; i < egNR; i++)
 +        {
 +            for (j = 0; j < f_t[1].grpp.nener; j++)
 +            {
 +                for (t = 1; t < nthreads; t++)
 +                {
 +
 +                    grpp->ener[i][j] += f_t[t].grpp.ener[i][j];
 +                }
 +            }
 +        }
 +        if (bDHDL)
 +        {
 +            for (i = 0; i < efptNR; i++)
 +            {
 +
 +                for (t = 1; t < nthreads; t++)
 +                {
 +                    dvdl[i] += f_t[t].dvdl[i];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static real calc_one_bond(FILE *fplog, int thread,
 +                          int ftype, const t_idef *idef,
 +                          rvec x[], rvec f[], rvec fshift[],
 +                          t_forcerec *fr,
 +                          const t_pbc *pbc, const t_graph *g,
 +                          gmx_enerdata_t *enerd, gmx_grppairener_t *grpp,
 +                          t_nrnb *nrnb,
 +                          real *lambda, real *dvdl,
 +                          const t_mdatoms *md, t_fcdata *fcd,
 +                          gmx_bool bCalcEnerVir,
 +                          int *global_atom_index, gmx_bool bPrintSepPot)
 +{
 +    int      ind, nat1, nbonds, efptFTYPE;
 +    real     v = 0;
 +    t_iatom *iatoms;
 +    int      nb0, nbn;
 +
 +    if (IS_RESTRAINT_TYPE(ftype))
 +    {
 +        efptFTYPE = efptRESTRAINT;
 +    }
 +    else
 +    {
 +        efptFTYPE = efptBONDED;
 +    }
 +
 +    if (interaction_function[ftype].flags & IF_BOND &&
 +        !(ftype == F_CONNBONDS || ftype == F_POSRES))
 +    {
 +        ind       = interaction_function[ftype].nrnb_ind;
 +        nat1      = interaction_function[ftype].nratoms + 1;
 +        nbonds    = idef->il[ftype].nr/nat1;
 +        iatoms    = idef->il[ftype].iatoms;
 +
 +        nb0 = ((nbonds* thread   )/(fr->nthreads))*nat1;
 +        nbn = ((nbonds*(thread+1))/(fr->nthreads))*nat1 - nb0;
 +
 +        if (!IS_LISTED_LJ_C(ftype))
 +        {
 +            if (ftype == F_CMAP)
 +            {
 +                v = cmap_dihs(nbn, iatoms+nb0,
 +                              idef->iparams, &idef->cmap_grid,
 +                              (const rvec*)x, f, fshift,
 +                              pbc, g, lambda[efptFTYPE], &(dvdl[efptFTYPE]),
 +                              md, fcd, global_atom_index);
 +            }
++#ifdef SIMD_BONDEDS
++            else if (ftype == F_ANGLES &&
++                     !bCalcEnerVir && fr->efep == efepNO)
++            {
++                /* No energies, shift forces, dvdl */
++                angles_noener_simd(nbn, idef->il[ftype].iatoms+nb0,
++                                   idef->iparams,
++                                   (const rvec*)x, f,
++                                   pbc, g, lambda[efptFTYPE], md, fcd,
++                                   global_atom_index);
++                v = 0;
++            }
++#endif
 +            else if (ftype == F_PDIHS &&
 +                     !bCalcEnerVir && fr->efep == efepNO)
 +            {
 +                /* No energies, shift forces, dvdl */
-                 pdihs_noener_sse
++#ifndef SIMD_BONDEDS
 +                pdihs_noener
 +#else
-             enerd->dvdl_nonlin[efptCOUL] += dvdl[efptCOUL];
-             enerd->dvdl_nonlin[efptVDW]  += dvdl[efptVDW];
++                pdihs_noener_simd
 +#endif
 +                    (nbn, idef->il[ftype].iatoms+nb0,
 +                    idef->iparams,
 +                    (const rvec*)x, f,
 +                    pbc, g, lambda[efptFTYPE], md, fcd,
 +                    global_atom_index);
 +                v = 0;
 +            }
 +            else
 +            {
 +                v = interaction_function[ftype].ifunc(nbn, iatoms+nb0,
 +                                                      idef->iparams,
 +                                                      (const rvec*)x, f, fshift,
 +                                                      pbc, g, lambda[efptFTYPE], &(dvdl[efptFTYPE]),
 +                                                      md, fcd, global_atom_index);
 +            }
 +            if (bPrintSepPot)
 +            {
 +                fprintf(fplog, "  %-23s #%4d  V %12.5e  dVdl %12.5e\n",
 +                        interaction_function[ftype].longname,
 +                        nbonds/nat1, v, lambda[efptFTYPE]);
 +            }
 +        }
 +        else
 +        {
 +            v = do_nonbonded_listed(ftype, nbn, iatoms+nb0, idef->iparams, (const rvec*)x, f, fshift,
 +                                    pbc, g, lambda, dvdl, md, fr, grpp, global_atom_index);
 +
 +            if (bPrintSepPot)
 +            {
 +                fprintf(fplog, "  %-5s + %-15s #%4d                  dVdl %12.5e\n",
 +                        interaction_function[ftype].longname,
 +                        interaction_function[F_LJ14].longname, nbonds/nat1, dvdl[efptVDW]);
 +                fprintf(fplog, "  %-5s + %-15s #%4d                  dVdl %12.5e\n",
 +                        interaction_function[ftype].longname,
 +                        interaction_function[F_COUL14].longname, nbonds/nat1, dvdl[efptCOUL]);
 +            }
 +        }
 +        if (ind != -1 && thread == 0)
 +        {
 +            inc_nrnb(nrnb, ind, nbonds);
 +        }
 +    }
 +
 +    return v;
 +}
 +
 +/* WARNING!  THIS FUNCTION MUST EXACTLY TRACK THE calc
 +   function, or horrible things will happen when doing free energy
 +   calculations!  In a good coding world, this would not be a
 +   different function, but for speed reasons, it needs to be made a
 +   separate function.  TODO for 5.0 - figure out a way to reorganize
 +   to reduce duplication.
 + */
 +
 +static real calc_one_bond_foreign(FILE *fplog, int ftype, const t_idef *idef,
 +                                  rvec x[], rvec f[], t_forcerec *fr,
 +                                  const t_pbc *pbc, const t_graph *g,
 +                                  gmx_grppairener_t *grpp, t_nrnb *nrnb,
 +                                  real *lambda, real *dvdl,
 +                                  const t_mdatoms *md, t_fcdata *fcd,
 +                                  int *global_atom_index, gmx_bool bPrintSepPot)
 +{
 +    int      ind, nat1, nbonds, efptFTYPE, nbonds_np;
 +    real     v = 0;
 +    t_iatom *iatoms;
 +
 +    if (IS_RESTRAINT_TYPE(ftype))
 +    {
 +        efptFTYPE = efptRESTRAINT;
 +    }
 +    else
 +    {
 +        efptFTYPE = efptBONDED;
 +    }
 +
 +    if (ftype < F_GB12 || ftype > F_GB14)
 +    {
 +        if (interaction_function[ftype].flags & IF_BOND &&
 +            !(ftype == F_CONNBONDS || ftype == F_POSRES || ftype == F_FBPOSRES))
 +        {
 +            ind       = interaction_function[ftype].nrnb_ind;
 +            nat1      = interaction_function[ftype].nratoms+1;
 +            nbonds_np = idef->il[ftype].nr_nonperturbed;
 +            nbonds    = idef->il[ftype].nr - nbonds_np;
 +            iatoms    = idef->il[ftype].iatoms + nbonds_np;
 +            if (nbonds > 0)
 +            {
 +                if (!IS_LISTED_LJ_C(ftype))
 +                {
 +                    if (ftype == F_CMAP)
 +                    {
 +                        v = cmap_dihs(nbonds, iatoms,
 +                                      idef->iparams, &idef->cmap_grid,
 +                                      (const rvec*)x, f, fr->fshift,
 +                                      pbc, g, lambda[efptFTYPE], &(dvdl[efptFTYPE]), md, fcd,
 +                                      global_atom_index);
 +                    }
 +                    else
 +                    {
 +                        v =     interaction_function[ftype].ifunc(nbonds, iatoms,
 +                                                                  idef->iparams,
 +                                                                  (const rvec*)x, f, fr->fshift,
 +                                                                  pbc, g, lambda[efptFTYPE], &dvdl[efptFTYPE],
 +                                                                  md, fcd, global_atom_index);
 +                    }
 +                }
 +                else
 +                {
 +                    v = do_nonbonded_listed(ftype, nbonds, iatoms,
 +                                            idef->iparams,
 +                                            (const rvec*)x, f, fr->fshift,
 +                                            pbc, g, lambda, dvdl,
 +                                            md, fr, grpp, global_atom_index);
 +                }
 +                if (ind != -1)
 +                {
 +                    inc_nrnb(nrnb, ind, nbonds/nat1);
 +                }
 +            }
 +        }
 +    }
 +    return v;
 +}
 +
 +void calc_bonds(FILE *fplog, const gmx_multisim_t *ms,
 +                const t_idef *idef,
 +                rvec x[], history_t *hist,
 +                rvec f[], t_forcerec *fr,
 +                const t_pbc *pbc, const t_graph *g,
 +                gmx_enerdata_t *enerd, t_nrnb *nrnb,
 +                real *lambda,
 +                const t_mdatoms *md,
 +                t_fcdata *fcd, int *global_atom_index,
 +                t_atomtypes *atype, gmx_genborn_t *born,
 +                int force_flags,
 +                gmx_bool bPrintSepPot, gmx_large_int_t step)
 +{
 +    gmx_bool      bCalcEnerVir;
 +    int           i;
 +    real          v, dvdl[efptNR], dvdl_dum[efptNR]; /* The dummy array is to have a place to store the dhdl at other values
 +                                                        of lambda, which will be thrown away in the end*/
 +    const  t_pbc *pbc_null;
 +    char          buf[22];
 +    int           thread;
 +
 +    bCalcEnerVir = (force_flags & (GMX_FORCE_VIRIAL | GMX_FORCE_ENERGY));
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        dvdl[i] = 0.0;
 +    }
 +    if (fr->bMolPBC)
 +    {
 +        pbc_null = pbc;
 +    }
 +    else
 +    {
 +        pbc_null = NULL;
 +    }
 +    if (bPrintSepPot)
 +    {
 +        fprintf(fplog, "Step %s: bonded V and dVdl for this node\n",
 +                gmx_step_str(step, buf));
 +    }
 +
 +#ifdef DEBUG
 +    if (g && debug)
 +    {
 +        p_graph(debug, "Bondage is fun", g);
 +    }
 +#endif
 +
 +    /* Do pre force calculation stuff which might require communication */
 +    if (idef->il[F_ORIRES].nr)
 +    {
 +        enerd->term[F_ORIRESDEV] =
 +            calc_orires_dev(ms, idef->il[F_ORIRES].nr,
 +                            idef->il[F_ORIRES].iatoms,
 +                            idef->iparams, md, (const rvec*)x,
 +                            pbc_null, fcd, hist);
 +    }
 +    if (idef->il[F_DISRES].nr)
 +    {
 +        calc_disres_R_6(ms, idef->il[F_DISRES].nr,
 +                        idef->il[F_DISRES].iatoms,
 +                        idef->iparams, (const rvec*)x, pbc_null,
 +                        fcd, hist);
 +    }
 +
 +#pragma omp parallel for num_threads(fr->nthreads) schedule(static)
 +    for (thread = 0; thread < fr->nthreads; thread++)
 +    {
 +        int                ftype, nbonds, ind, nat1;
 +        real              *epot, v;
 +        /* thread stuff */
 +        rvec              *ft, *fshift;
 +        real              *dvdlt;
 +        gmx_grppairener_t *grpp;
 +        int                nb0, nbn;
 +
 +        if (thread == 0)
 +        {
 +            ft     = f;
 +            fshift = fr->fshift;
 +            epot   = enerd->term;
 +            grpp   = &enerd->grpp;
 +            dvdlt  = dvdl;
 +        }
 +        else
 +        {
 +            zero_thread_forces(&fr->f_t[thread], fr->natoms_force,
 +                               fr->red_nblock, 1<<fr->red_ashift);
 +
 +            ft     = fr->f_t[thread].f;
 +            fshift = fr->f_t[thread].fshift;
 +            epot   = fr->f_t[thread].ener;
 +            grpp   = &fr->f_t[thread].grpp;
 +            dvdlt  = fr->f_t[thread].dvdl;
 +        }
 +        /* Loop over all bonded force types to calculate the bonded forces */
 +        for (ftype = 0; (ftype < F_NRE); ftype++)
 +        {
 +            if (idef->il[ftype].nr > 0 &&
 +                (interaction_function[ftype].flags & IF_BOND) &&
 +                (ftype < F_GB12 || ftype > F_GB14) &&
 +                !(ftype == F_CONNBONDS || ftype == F_POSRES))
 +            {
 +                v = calc_one_bond(fplog, thread, ftype, idef, x,
 +                                  ft, fshift, fr, pbc_null, g, enerd, grpp,
 +                                  nrnb, lambda, dvdlt,
 +                                  md, fcd, bCalcEnerVir,
 +                                  global_atom_index, bPrintSepPot);
 +                epot[ftype]        += v;
 +            }
 +        }
 +    }
 +    if (fr->nthreads > 1)
 +    {
 +        reduce_thread_forces(fr->natoms_force, f, fr->fshift,
 +                             enerd->term, &enerd->grpp, dvdl,
 +                             fr->nthreads, fr->f_t,
 +                             fr->red_nblock, 1<<fr->red_ashift,
 +                             bCalcEnerVir,
 +                             force_flags & GMX_FORCE_DHDL);
 +    }
 +    if (force_flags & GMX_FORCE_DHDL)
 +    {
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            enerd->dvdl_nonlin[i] += dvdl[i];
 +        }
 +    }
 +
 +    /* Copy the sum of violations for the distance restraints from fcd */
 +    if (fcd)
 +    {
 +        enerd->term[F_DISRESVIOL] = fcd->disres.sumviol;
 +
 +    }
 +}
 +
 +void calc_bonds_lambda(FILE *fplog,
 +                       const t_idef *idef,
 +                       rvec x[],
 +                       t_forcerec *fr,
 +                       const t_pbc *pbc, const t_graph *g,
 +                       gmx_grppairener_t *grpp, real *epot, t_nrnb *nrnb,
 +                       real *lambda,
 +                       const t_mdatoms *md,
 +                       t_fcdata *fcd,
 +                       int *global_atom_index)
 +{
 +    int           i, ftype, nbonds_np, nbonds, ind, nat;
 +    real          v, dr, dr2;
 +    real          dvdl_dum[efptNR];
 +    rvec         *f, *fshift_orig;
 +    const  t_pbc *pbc_null;
 +    t_iatom      *iatom_fe;
 +
 +    if (fr->bMolPBC)
 +    {
 +        pbc_null = pbc;
 +    }
 +    else
 +    {
 +        pbc_null = NULL;
 +    }
 +
 +    snew(f, fr->natoms_force);
 +    /* We want to preserve the fshift array in forcerec */
 +    fshift_orig = fr->fshift;
 +    snew(fr->fshift, SHIFTS);
 +
 +    /* Loop over all bonded force types to calculate the bonded forces */
 +    for (ftype = 0; (ftype < F_NRE); ftype++)
 +    {
 +        v = calc_one_bond_foreign(fplog, ftype, idef, x,
 +                                  f, fr, pbc_null, g, grpp, nrnb, lambda, dvdl_dum,
 +                                  md, fcd, global_atom_index, FALSE);
 +        epot[ftype] += v;
 +    }
 +
 +    sfree(fr->fshift);
 +    fr->fshift = fshift_orig;
 +    sfree(f);
 +}
index 4915f238536998063d08d705ba720999aa06617f,0000000000000000000000000000000000000000..14529515449ece8ad0f5a5f8debcea46296d3f53
mode 100644,000000..100644
--- /dev/null
@@@ -1,790 -1,0 +1,790 @@@
- #define NLICENSE 0 /*FAH has an exception permission from GPL to allow digital signatures in Gromacs*/
 +/*
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * GROningen Mixture of Alchemy and Childrens' Stories
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#ifdef GMX_THREAD_MPI
 +#include <thread_mpi.h>
 +#endif
 +
 +#ifdef HAVE_LIBMKL
 +#include <mkl.h>
 +#endif
 +#ifdef GMX_FFT_FFTW3
 +#include <fftw3.h>
 +#endif
 +
 +/* This file is completely threadsafe - keep it that way! */
 +
 +#include <string.h>
 +#include <ctype.h>
 +#include "sysstuff.h"
 +#include "smalloc.h"
 +#include "string2.h"
 +#include "macros.h"
 +#include <time.h>
 +#include "random.h"
 +#include "statutil.h"
 +#include "copyrite.h"
 +#include "strdb.h"
 +#include "futil.h"
 +#include "vec.h"
 +#include "buildinfo.h"
 +#include "gmx_cpuid.h"
 +
 +static void pr_two(FILE *out, int c, int i)
 +{
 +    if (i < 10)
 +    {
 +        fprintf(out, "%c0%1d", c, i);
 +    }
 +    else
 +    {
 +        fprintf(out, "%c%2d", c, i);
 +    }
 +}
 +
 +void pr_difftime(FILE *out, double dt)
 +{
 +    int        ndays, nhours, nmins, nsecs;
 +    gmx_bool   bPrint, bPrinted;
 +
 +    ndays    = dt/(24*3600);
 +    dt       = dt-24*3600*ndays;
 +    nhours   = dt/3600;
 +    dt       = dt-3600*nhours;
 +    nmins    = dt/60;
 +    dt       = dt-nmins*60;
 +    nsecs    = dt;
 +    bPrint   = (ndays > 0);
 +    bPrinted = bPrint;
 +    if (bPrint)
 +    {
 +        fprintf(out, "%d", ndays);
 +    }
 +    bPrint = bPrint || (nhours > 0);
 +    if (bPrint)
 +    {
 +        if (bPrinted)
 +        {
 +            pr_two(out, 'd', nhours);
 +        }
 +        else
 +        {
 +            fprintf(out, "%d", nhours);
 +        }
 +    }
 +    bPrinted = bPrinted || bPrint;
 +    bPrint   = bPrint || (nmins > 0);
 +    if (bPrint)
 +    {
 +        if (bPrinted)
 +        {
 +            pr_two(out, 'h', nmins);
 +        }
 +        else
 +        {
 +            fprintf(out, "%d", nmins);
 +        }
 +    }
 +    bPrinted = bPrinted || bPrint;
 +    if (bPrinted)
 +    {
 +        pr_two(out, ':', nsecs);
 +    }
 +    else
 +    {
 +        fprintf(out, "%ds", nsecs);
 +    }
 +    fprintf(out, "\n");
 +}
 +
 +
 +gmx_bool be_cool(void)
 +{
 +    /* Yes, it is bad to check the environment variable every call,
 +     * but we dont call this routine often, and it avoids using
 +     * a mutex for locking the variable...
 +     */
 +#ifdef GMX_COOL_QUOTES
 +    return (getenv("GMX_NO_QUOTES") == NULL);
 +#else
 +    /*be uncool*/
 +    return FALSE;
 +#endif
 +}
 +
 +void space(FILE *out, int n)
 +{
 +    fprintf(out, "%*s", n, "");
 +}
 +
 +static void sp_print(FILE *out, const char *s)
 +{
 +    int slen;
 +
 +    slen = strlen(s);
 +    space(out, (80-slen)/2);
 +    fprintf(out, "%s\n", s);
 +}
 +
 +static void ster_print(FILE *out, const char *s)
 +{
 +    int  slen;
 +    char buf[128];
 +
 +    snprintf(buf, 128, ":-)  %s  (-:", s);
 +    slen = strlen(buf);
 +    space(out, (80-slen)/2);
 +    fprintf(out, "%s\n", buf);
 +}
 +
 +
 +static void pukeit(const char *db, const char *defstring, char *retstring,
 +                   int retsize, int *cqnum)
 +{
 +    FILE  *fp;
 +    char **help;
 +    int    i, nhlp;
 +    int    seed;
 +
 +    if (be_cool() && ((fp = low_libopen(db, FALSE)) != NULL))
 +    {
 +        nhlp = fget_lines(fp, &help);
 +        /* for libraries we can use the low-level close routines */
 +        ffclose(fp);
 +        seed   = time(NULL);
 +        *cqnum = nhlp*rando(&seed);
 +        if (strlen(help[*cqnum]) >= STRLEN)
 +        {
 +            help[*cqnum][STRLEN-1] = '\0';
 +        }
 +        strncpy(retstring, help[*cqnum], retsize);
 +        for (i = 0; (i < nhlp); i++)
 +        {
 +            sfree(help[i]);
 +        }
 +        sfree(help);
 +    }
 +    else
 +    {
 +        strncpy(retstring, defstring, retsize);
 +    }
 +}
 +
 +void bromacs(char *retstring, int retsize)
 +{
 +    int dum;
 +
 +    pukeit("bromacs.dat",
 +           "Groningen Machine for Chemical Simulation",
 +           retstring, retsize, &dum);
 +}
 +
 +void cool_quote(char *retstring, int retsize, int *cqnum)
 +{
 +    char *tmpstr;
 +    char *s, *ptr;
 +    int   tmpcq, *p;
 +
 +    if (cqnum != NULL)
 +    {
 +        p = cqnum;
 +    }
 +    else
 +    {
 +        p = &tmpcq;
 +    }
 +
 +    /* protect audience from explicit lyrics */
 +    snew(tmpstr, retsize+1);
 +    pukeit("gurgle.dat", "Thanx for Using GROMACS - Have a Nice Day",
 +           tmpstr, retsize-2, p);
 +
 +    if ((ptr = strchr(tmpstr, '_')) != NULL)
 +    {
 +        *ptr = '\0';
 +        ptr++;
 +        sprintf(retstring, "\"%s\" %s", tmpstr, ptr);
 +    }
 +    else
 +    {
 +        strcpy(retstring, tmpstr);
 +    }
 +    sfree(tmpstr);
 +}
 +
 +void CopyRight(FILE *out, const char *szProgram)
 +{
 +    static const char * CopyrightText[] = {
 +        "Written by Emile Apol, Rossen Apostolov, Herman J.C. Berendsen,",
 +        "Aldert van Buuren, Pär Bjelkmar, Rudi van Drunen, Anton Feenstra, ",
 +        "Gerrit Groenhof, Peter Kasson, Per Larsson, Pieter Meulenhoff, ",
 +        "Teemu Murtola, Szilard Pall, Sander Pronk, Roland Schulz, ",
 +        "Michael Shirts, Alfons Sijbers, Peter Tieleman,\n",
 +        "Berk Hess, David van der Spoel, and Erik Lindahl.\n",
 +        "Copyright (c) 1991-2000, University of Groningen, The Netherlands.",
 +        "Copyright (c) 2001-2010, The GROMACS development team at",
 +        "Uppsala University & The Royal Institute of Technology, Sweden.",
 +        "check out http://www.gromacs.org for more information.\n"
 +    };
 +
 +    static const char * LicenseText[] = {
 +        "This program 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."
 +    };
 +
 +    /* Dont change szProgram arbitrarily - it must be argv[0], i.e. the
 +     * name of a file. Otherwise, we won't be able to find the library dir.
 +     */
 +#define NCR (int)asize(CopyrightText)
 +/* TODO: Is this exception still needed? */
 +#ifdef GMX_FAHCORE
-     fprintf(fp, "Linked with Intel MKL version %s.%s.%s.\n",
++#define NLICENSE 0 /*FAH has an exception permission from LGPL to allow digital signatures in Gromacs*/
 +#else
 +#define NLICENSE (int)asize(LicenseText)
 +#endif
 +
 +    char buf[256], tmpstr[1024];
 +    int  i;
 +
 +#ifdef GMX_FAHCORE
 +    set_program_name("Gromacs");
 +#else
 +    set_program_name(szProgram);
 +#endif
 +
 +    ster_print(out, "G  R  O  M  A  C  S");
 +    fprintf(out, "\n");
 +
 +    bromacs(tmpstr, 1023);
 +    sp_print(out, tmpstr);
 +    fprintf(out, "\n");
 +
 +    ster_print(out, GromacsVersion());
 +    fprintf(out, "\n");
 +
 +    /* fprintf(out,"\n");*/
 +
 +    /* sp_print(out,"PLEASE NOTE: THIS IS A BETA VERSION\n");
 +
 +       fprintf(out,"\n"); */
 +
 +    for (i = 0; (i < NCR); i++)
 +    {
 +        sp_print(out, CopyrightText[i]);
 +    }
 +    for (i = 0; (i < NLICENSE); i++)
 +    {
 +        sp_print(out, LicenseText[i]);
 +    }
 +
 +    fprintf(out, "\n");
 +
 +    snprintf(buf, 256, "%s", Program());
 +#ifdef GMX_DOUBLE
 +    strcat(buf, " (double precision)");
 +#endif
 +    ster_print(out, buf);
 +    fprintf(out, "\n");
 +}
 +
 +
 +void thanx(FILE *fp)
 +{
 +    char cq[1024];
 +    int  cqnum;
 +
 +    /* protect the audience from suggestive discussions */
 +    cool_quote(cq, 1023, &cqnum);
 +
 +    if (be_cool())
 +    {
 +        fprintf(fp, "\ngcq#%d: %s\n\n", cqnum, cq);
 +    }
 +    else
 +    {
 +        fprintf(fp, "\n%s\n\n", cq);
 +    }
 +}
 +
 +typedef struct {
 +    const char *key;
 +    const char *author;
 +    const char *title;
 +    const char *journal;
 +    int         volume, year;
 +    const char *pages;
 +} t_citerec;
 +
 +void please_cite(FILE *fp, const char *key)
 +{
 +    static const t_citerec citedb[] = {
 +        { "Allen1987a",
 +          "M. P. Allen and D. J. Tildesley",
 +          "Computer simulation of liquids",
 +          "Oxford Science Publications",
 +          1, 1987, "1" },
 +        { "Berendsen95a",
 +          "H. J. C. Berendsen, D. van der Spoel and R. van Drunen",
 +          "GROMACS: A message-passing parallel molecular dynamics implementation",
 +          "Comp. Phys. Comm.",
 +          91, 1995, "43-56" },
 +        { "Berendsen84a",
 +          "H. J. C. Berendsen, J. P. M. Postma, A. DiNola and J. R. Haak",
 +          "Molecular dynamics with coupling to an external bath",
 +          "J. Chem. Phys.",
 +          81, 1984, "3684-3690" },
 +        { "Ryckaert77a",
 +          "J. P. Ryckaert and G. Ciccotti and H. J. C. Berendsen",
 +          "Numerical Integration of the Cartesian Equations of Motion of a System with Constraints; Molecular Dynamics of n-Alkanes",
 +          "J. Comp. Phys.",
 +          23, 1977, "327-341" },
 +        { "Miyamoto92a",
 +          "S. Miyamoto and P. A. Kollman",
 +          "SETTLE: An Analytical Version of the SHAKE and RATTLE Algorithms for Rigid Water Models",
 +          "J. Comp. Chem.",
 +          13, 1992, "952-962" },
 +        { "Cromer1968a",
 +          "D. T. Cromer & J. B. Mann",
 +          "X-ray scattering factors computed from numerical Hartree-Fock wave functions",
 +          "Acta Cryst. A",
 +          24, 1968, "321" },
 +        { "Barth95a",
 +          "E. Barth and K. Kuczera and B. Leimkuhler and R. D. Skeel",
 +          "Algorithms for Constrained Molecular Dynamics",
 +          "J. Comp. Chem.",
 +          16, 1995, "1192-1209" },
 +        { "Essmann95a",
 +          "U. Essmann, L. Perera, M. L. Berkowitz, T. Darden, H. Lee and L. G. Pedersen ",
 +          "A smooth particle mesh Ewald method",
 +          "J. Chem. Phys.",
 +          103, 1995, "8577-8592" },
 +        { "Torda89a",
 +          "A. E. Torda and R. M. Scheek and W. F. van Gunsteren",
 +          "Time-dependent distance restraints in molecular dynamics simulations",
 +          "Chem. Phys. Lett.",
 +          157, 1989, "289-294" },
 +        { "Tironi95a",
 +          "I. G. Tironi and R. Sperb and P. E. Smith and W. F. van Gunsteren",
 +          "Generalized reaction field method for molecular dynamics simulations",
 +          "J. Chem. Phys",
 +          102, 1995, "5451-5459" },
 +        { "Hess97a",
 +          "B. Hess and H. Bekker and H. J. C. Berendsen and J. G. E. M. Fraaije",
 +          "LINCS: A Linear Constraint Solver for molecular simulations",
 +          "J. Comp. Chem.",
 +          18, 1997, "1463-1472" },
 +        { "Hess2008a",
 +          "B. Hess",
 +          "P-LINCS: A Parallel Linear Constraint Solver for molecular simulation",
 +          "J. Chem. Theory Comput.",
 +          4, 2008, "116-122" },
 +        { "Hess2008b",
 +          "B. Hess and C. Kutzner and D. van der Spoel and E. Lindahl",
 +          "GROMACS 4: Algorithms for highly efficient, load-balanced, and scalable molecular simulation",
 +          "J. Chem. Theory Comput.",
 +          4, 2008, "435-447" },
 +        { "Hub2010",
 +          "J. S. Hub, B. L. de Groot and D. van der Spoel",
 +          "g_wham - A free weighted histogram analysis implementation including robust error and autocorrelation estimates",
 +          "J. Chem. Theory Comput.",
 +          6, 2010, "3713-3720"},
 +        { "In-Chul99a",
 +          "Y. In-Chul and M. L. Berkowitz",
 +          "Ewald summation for systems with slab geometry",
 +          "J. Chem. Phys.",
 +          111, 1999, "3155-3162" },
 +        { "DeGroot97a",
 +          "B. L. de Groot and D. M. F. van Aalten and R. M. Scheek and A. Amadei and G. Vriend and H. J. C. Berendsen",
 +          "Prediction of Protein Conformational Freedom From Distance Constrains",
 +          "Proteins",
 +          29, 1997, "240-251" },
 +        { "Spoel98a",
 +          "D. van der Spoel and P. J. van Maaren and H. J. C. Berendsen",
 +          "A systematic study of water models for molecular simulation. Derivation of models optimized for use with a reaction-field.",
 +          "J. Chem. Phys.",
 +          108, 1998, "10220-10230" },
 +        { "Wishart98a",
 +          "D. S. Wishart and A. M. Nip",
 +          "Protein Chemical Shift Analysis: A Practical Guide",
 +          "Biochem. Cell Biol.",
 +          76, 1998, "153-163" },
 +        { "Maiorov95",
 +          "V. N. Maiorov and G. M. Crippen",
 +          "Size-Independent Comparison of Protein Three-Dimensional Structures",
 +          "PROTEINS: Struct. Funct. Gen.",
 +          22, 1995, "273-283" },
 +        { "Feenstra99",
 +          "K. A. Feenstra and B. Hess and H. J. C. Berendsen",
 +          "Improving Efficiency of Large Time-scale Molecular Dynamics Simulations of Hydrogen-rich Systems",
 +          "J. Comput. Chem.",
 +          20, 1999, "786-798" },
 +        { "Timneanu2004a",
 +          "N. Timneanu and C. Caleman and J. Hajdu and D. van der Spoel",
 +          "Auger Electron Cascades in Water and Ice",
 +          "Chem. Phys.",
 +          299, 2004, "277-283" },
 +        { "Pascal2011a",
 +          "T. A. Pascal and S. T. Lin and W. A. Goddard III",
 +          "Thermodynamics of liquids: standard molar entropies and heat capacities of common solvents from 2PT molecular dynamics",
 +          "Phys. Chem. Chem. Phys.",
 +          13, 2011, "169-181" },
 +        { "Caleman2011b",
 +          "C. Caleman and P. J. van Maaren and M. Hong and J. S. Hub and L. T. da Costa and D. van der Spoel",
 +          "Force Field Benchmark of Organic Liquids: Density, Enthalpy of Vaporization, Heat Capacities, Surface Tension, Isothermal Compressibility, Volumetric Expansion Coefficient, and Dielectric Constant",
 +          "J. Chem. Theo. Comp.",
 +          8, 2012, "61" },
 +        { "Lindahl2001a",
 +          "E. Lindahl and B. Hess and D. van der Spoel",
 +          "GROMACS 3.0: A package for molecular simulation and trajectory analysis",
 +          "J. Mol. Mod.",
 +          7, 2001, "306-317" },
 +        { "Wang2001a",
 +          "J. Wang and W. Wang and S. Huo and M. Lee and P. A. Kollman",
 +          "Solvation model based on weighted solvent accessible surface area",
 +          "J. Phys. Chem. B",
 +          105, 2001, "5055-5067" },
 +        { "Eisenberg86a",
 +          "D. Eisenberg and A. D. McLachlan",
 +          "Solvation energy in protein folding and binding",
 +          "Nature",
 +          319, 1986, "199-203" },
 +        { "Bondi1964a",
 +          "A. Bondi",
 +          "van der Waals Volumes and Radii",
 +          "J. Phys. Chem.",
 +          68, 1964,"441-451" },
 +        { "Eisenhaber95",
 +          "Frank Eisenhaber and Philip Lijnzaad and Patrick Argos and Chris Sander and Michael Scharf",
 +          "The Double Cube Lattice Method: Efficient Approaches to Numerical Integration of Surface Area and Volume and to Dot Surface Contouring of Molecular Assemblies",
 +          "J. Comp. Chem.",
 +          16, 1995, "273-284" },
 +        { "Hess2002",
 +          "B. Hess, H. Saint-Martin and H.J.C. Berendsen",
 +          "Flexible constraints: an adiabatic treatment of quantum degrees of freedom, with application to the flexible and polarizable MCDHO model for water",
 +          "J. Chem. Phys.",
 +          116, 2002, "9602-9610" },
 +        { "Hetenyi2002b",
 +          "Csaba Hetenyi and David van der Spoel",
 +          "Efficient docking of peptides to proteins without prior knowledge of the binding site.",
 +          "Prot. Sci.",
 +          11, 2002, "1729-1737" },
 +        { "Hess2003",
 +          "B. Hess and R.M. Scheek",
 +          "Orientation restraints in molecular dynamics simulations using time and ensemble averaging",
 +          "J. Magn. Res.",
 +          164, 2003, "19-27" },
 +        { "Rappe1991a",
 +          "A. K. Rappe and W. A. Goddard III",
 +          "Charge Equillibration for Molecular Dynamics Simulations",
 +          "J. Phys. Chem.",
 +          95, 1991, "3358-3363" },
 +        { "Mu2005a",
 +          "Y. Mu, P. H. Nguyen and G. Stock",
 +          "Energy landscape of a small peptide revelaed by dihedral angle principal component analysis",
 +          "Prot. Struct. Funct. Bioinf.",
 +          58, 2005, "45-52" },
 +        { "Okabe2001a",
 +          "T. Okabe and M. Kawata and Y. Okamoto and M. Mikami",
 +          "Replica-exchange {M}onte {C}arlo method for the isobaric-isothermal ensemble",
 +          "Chem. Phys. Lett.",
 +          335, 2001, "435-439" },
 +        { "Hukushima96a",
 +          "K. Hukushima and K. Nemoto",
 +          "Exchange Monte Carlo Method and Application to Spin Glass Simulations",
 +          "J. Phys. Soc. Jpn.",
 +          65, 1996, "1604-1608" },
 +        { "Tropp80a",
 +          "J. Tropp",
 +          "Dipolar Relaxation and Nuclear Overhauser effects in nonrigid molecules: The effect of fluctuating internuclear distances",
 +          "J. Chem. Phys.",
 +          72, 1980, "6035-6043" },
 +        { "Bultinck2002a",
 +          "P. Bultinck and W. Langenaeker and P. Lahorte and F. De Proft and P. Geerlings and M. Waroquier and J. P. Tollenaere",
 +          "The electronegativity equalization method I: Parametrization and validation for atomic charge calculations",
 +          "J. Phys. Chem. A",
 +          106, 2002, "7887-7894" },
 +        { "Yang2006b",
 +          "Q. Y. Yang and K. A. Sharp",
 +          "Atomic charge parameters for the finite difference Poisson-Boltzmann method using electronegativity neutralization",
 +          "J. Chem. Theory Comput.",
 +          2, 2006, "1152-1167" },
 +        { "Spoel2005a",
 +          "D. van der Spoel, E. Lindahl, B. Hess, G. Groenhof, A. E. Mark and H. J. C. Berendsen",
 +          "GROMACS: Fast, Flexible and Free",
 +          "J. Comp. Chem.",
 +          26, 2005, "1701-1719" },
 +        { "Spoel2006b",
 +          "D. van der Spoel, P. J. van Maaren, P. Larsson and N. Timneanu",
 +          "Thermodynamics of hydrogen bonding in hydrophilic and hydrophobic media",
 +          "J. Phys. Chem. B",
 +          110, 2006, "4393-4398" },
 +        { "Spoel2006d",
 +          "D. van der Spoel and M. M. Seibert",
 +          "Protein folding kinetics and thermodynamics from atomistic simulations",
 +          "Phys. Rev. Letters",
 +          96, 2006, "238102" },
 +        { "Palmer94a",
 +          "B. J. Palmer",
 +          "Transverse-current autocorrelation-function calculations of the shear viscosity for molecular liquids",
 +          "Phys. Rev. E",
 +          49, 1994, "359-366" },
 +        { "Bussi2007a",
 +          "G. Bussi, D. Donadio and M. Parrinello",
 +          "Canonical sampling through velocity rescaling",
 +          "J. Chem. Phys.",
 +          126, 2007, "014101" },
 +        { "Hub2006",
 +          "J. S. Hub and B. L. de Groot",
 +          "Does CO2 permeate through Aquaporin-1?",
 +          "Biophys. J.",
 +          91, 2006, "842-848" },
 +        { "Hub2008",
 +          "J. S. Hub and B. L. de Groot",
 +          "Mechanism of selectivity in aquaporins and aquaglyceroporins",
 +          "PNAS",
 +          105, 2008, "1198-1203" },
 +        { "Friedrich2009",
 +          "M. S. Friedrichs, P. Eastman, V. Vaidyanathan, M. Houston, S. LeGrand, A. L. Beberg, D. L. Ensign, C. M. Bruns, and V. S. Pande",
 +          "Accelerating Molecular Dynamic Simulation on Graphics Processing Units",
 +          "J. Comp. Chem.",
 +          30, 2009, "864-872" },
 +        { "Engin2010",
 +          "O. Engin, A. Villa, M. Sayar and B. Hess",
 +          "Driving Forces for Adsorption of Amphiphilic Peptides to Air-Water Interface",
 +          "J. Phys. Chem. B",
 +          114, 2010, "11093" },
 +        { "Fritsch12",
 +          "S. Fritsch, C. Junghans and K. Kremer",
 +          "Adaptive molecular simulation study on structure formation of toluene around C60 using Gromacs",
 +          "J. Chem. Theo. Comp.",
 +          8, 2012, "398" },
 +        { "Junghans10",
 +          "C. Junghans and S. Poblete",
 +          "A reference implementation of the adaptive resolution scheme in ESPResSo",
 +          "Comp. Phys. Comm.",
 +          181, 2010, "1449" },
 +        { "Wang2010",
 +          "H. Wang, F. Dommert, C.Holm",
 +          "Optimizing working parameters of the smooth particle mesh Ewald algorithm in terms of accuracy and efficiency",
 +          "J. Chem. Phys. B",
 +          133, 2010, "034117" },
 +        { "Sugita1999a",
 +          "Y. Sugita, Y. Okamoto",
 +          "Replica-exchange molecular dynamics method for protein folding",
 +          "Chem. Phys. Lett.",
 +          314, 1999, "141-151" },
 +        { "Kutzner2011",
 +          "C. Kutzner and J. Czub and H. Grubmuller",
 +          "Keep it Flexible: Driving Macromolecular Rotary Motions in Atomistic Simulations with GROMACS",
 +          "J. Chem. Theory Comput.",
 +          7, 2011, "1381-1393" },
 +        { "Hoefling2011",
 +          "M. Hoefling, N. Lima, D. Haenni, C.A.M. Seidel, B. Schuler, H. Grubmuller",
 +          "Structural Heterogeneity and Quantitative FRET Efficiency Distributions of Polyprolines through a Hybrid Atomistic Simulation and Monte Carlo Approach",
 +          "PLoS ONE",
 +          6, 2011, "e19791" },
 +        { "Hockney1988",
 +          "R. W. Hockney and J. W. Eastwood",
 +          "Computer simulation using particles",
 +          "IOP, Bristol",
 +          1, 1988, "1" },
 +        { "Ballenegger2012",
 +          "V. Ballenegger, J.J. Cerda, and C. Holm",
 +          "How to Convert SPME to P3M: Influence Functions and Error Estimates",
 +          "J. Chem. Theory Comput.",
 +          8, 2012, "936-947" },
 +        { "Garmay2012",
 +          "Garmay Yu, Shvetsov A, Karelov D, Lebedev D, Radulescu A, Petukhov M, Isaev-Ivanov V",
 +          "Correlated motion of protein subdomains and large-scale conformational flexibility of RecA protein filament",
 +          "Journal of Physics: Conference Series",
 +          340, 2012, "012094" }
 +    };
 +#define NSTR (int)asize(citedb)
 +
 +    int   j, index;
 +    char *author;
 +    char *title;
 +#define LINE_WIDTH 79
 +
 +    if (fp == NULL)
 +    {
 +        return;
 +    }
 +
 +    for (index = 0; (index < NSTR) && (strcmp(citedb[index].key, key) != 0); index++)
 +    {
 +        ;
 +    }
 +
 +    fprintf(fp, "\n++++ PLEASE READ AND CITE THE FOLLOWING REFERENCE ++++\n");
 +    if (index < NSTR)
 +    {
 +        /* Insert newlines */
 +        author = wrap_lines(citedb[index].author, LINE_WIDTH, 0, FALSE);
 +        title  = wrap_lines(citedb[index].title, LINE_WIDTH, 0, FALSE);
 +        fprintf(fp, "%s\n%s\n%s %d (%d) pp. %s\n",
 +                author, title, citedb[index].journal,
 +                citedb[index].volume, citedb[index].year,
 +                citedb[index].pages);
 +        sfree(author);
 +        sfree(title);
 +    }
 +    else
 +    {
 +        fprintf(fp, "Entry %s not found in citation database\n", key);
 +    }
 +    fprintf(fp, "-------- -------- --- Thank You --- -------- --------\n\n");
 +    fflush(fp);
 +}
 +
 +#ifdef GMX_GIT_VERSION_INFO
 +/* Version information generated at compile time. */
 +#include "gromacs/utility/gitversion.h"
 +#else
 +/* Fall back to statically defined version. */
 +static const char _gmx_ver_string[] = "VERSION " VERSION;
 +#endif
 +
 +const char *GromacsVersion()
 +{
 +    return _gmx_ver_string;
 +}
 +
 +void gmx_print_version_info_gpu(FILE *fp);
 +
 +void gmx_print_version_info(FILE *fp)
 +{
 +    fprintf(fp, "Gromacs version:    %s\n", _gmx_ver_string);
 +#ifdef GMX_GIT_VERSION_INFO
 +    fprintf(fp, "GIT SHA1 hash:      %s\n", _gmx_full_git_hash);
 +    /* Only print out the branch information if present.
 +     * The generating script checks whether the branch point actually
 +     * coincides with the hash reported above, and produces an empty string
 +     * in such cases. */
 +    if (_gmx_central_base_hash[0] != 0)
 +    {
 +        fprintf(fp, "Branched from:      %s\n", _gmx_central_base_hash);
 +    }
 +#endif
 +
 +#ifdef GMX_DOUBLE
 +    fprintf(fp, "Precision:          double\n");
 +#else
 +    fprintf(fp, "Precision:          single\n");
 +#endif
 +    fprintf(fp, "Memory model:       %lu bit\n", 8*sizeof(void *));
 +
 +#ifdef GMX_THREAD_MPI
 +    fprintf(fp, "MPI library:        thread_mpi\n");
 +#elif defined(GMX_MPI)
 +    fprintf(fp, "MPI library:        MPI\n");
 +#else
 +    fprintf(fp, "MPI library:        none\n");
 +#endif
 +#ifdef GMX_OPENMP
 +    fprintf(fp, "OpenMP support:     enabled\n");
 +#else
 +    fprintf(fp, "OpenMP support:     disabled\n");
 +#endif
 +#ifdef GMX_GPU
 +    fprintf(fp, "GPU support:        enabled\n");
 +#else
 +    fprintf(fp, "GPU support:        disabled\n");
 +#endif
 +    /* A preprocessor trick to avoid duplicating logic from vec.h */
 +#define gmx_stringify2(x) #x
 +#define gmx_stringify(x) gmx_stringify2(x)
 +    fprintf(fp, "invsqrt routine:    %s\n", gmx_stringify(gmx_invsqrt(x)));
 +    fprintf(fp, "CPU acceleration:   %s\n", GMX_CPU_ACCELERATION_STRING);
 +
 +    /* TODO: Would be nicer to wrap this in a gmx_fft_version() call, but
 +     * since that is currently in mdlib, can wait for master. */
 +#ifdef GMX_FFT_FFTPACK
 +    fprintf(fp, "FFT library:        fftpack (built-in)\n");
 +#elif defined(GMX_FFT_FFTW3) && defined(GMX_NATIVE_WINDOWS)
 +    fprintf(fp, "FFT library:        %s\n", "fftw3");
 +#elif defined(GMX_FFT_FFTW3) && defined(GMX_DOUBLE)
 +    fprintf(fp, "FFT library:        %s\n", fftw_version);
 +#elif defined(GMX_FFT_FFTW3)
 +    fprintf(fp, "FFT library:        %s\n", fftwf_version);
 +#elif defined(GMX_FFT_MKL)
 +    fprintf(fp, "FFT library:        MKL\n");
 +#else
 +    fprintf(fp, "FFT library:        unknown\n");
 +#endif
 +#ifdef GMX_LARGEFILES
 +    fprintf(fp, "Large file support: enabled\n");
 +#else
 +    fprintf(fp, "Large file support: disabled\n");
 +#endif
 +#ifdef HAVE_RDTSCP
 +    fprintf(fp, "RDTSCP usage:       enabled\n");
 +#else
 +    fprintf(fp, "RDTSCP usage:       disabled\n");
 +#endif
 +
 +    fprintf(fp, "Built on:           %s\n", BUILD_TIME);
 +    fprintf(fp, "Built by:           %s\n", BUILD_USER);
 +    fprintf(fp, "Build OS/arch:      %s\n", BUILD_HOST);
 +    fprintf(fp, "Build CPU vendor:   %s\n", BUILD_CPU_VENDOR);
 +    fprintf(fp, "Build CPU brand:    %s\n", BUILD_CPU_BRAND);
 +    fprintf(fp, "Build CPU family:   %d   Model: %d   Stepping: %d\n",
 +            BUILD_CPU_FAMILY, BUILD_CPU_MODEL, BUILD_CPU_STEPPING);
 +    /* TODO: The below strings can be quite long, so it would be nice to wrap
 +     * them. Can wait for later, as the master branch has ready code to do all
 +     * that. */
 +    fprintf(fp, "Build CPU features: %s\n", BUILD_CPU_FEATURES);
 +    fprintf(fp, "C compiler:         %s\n", BUILD_C_COMPILER);
 +    fprintf(fp, "C compiler flags:   %s\n", BUILD_CFLAGS);
 +    if (BUILD_CXX_COMPILER[0] != '\0')
 +    {
 +        fprintf(fp, "C++ compiler:       %s\n", BUILD_CXX_COMPILER);
 +        fprintf(fp, "C++ compiler flags: %s\n", BUILD_CXXFLAGS);
 +    }
 +#ifdef HAVE_LIBMKL
 +    /* MKL might be used for LAPACK/BLAS even if FFTs use FFTW, so keep it separate */
++    fprintf(fp, "Linked with Intel MKL version %d.%d.%d.\n",
 +            __INTEL_MKL__, __INTEL_MKL_MINOR__, __INTEL_MKL_UPDATE__);
 +#endif
 +#ifdef GMX_GPU
 +    gmx_print_version_info_gpu(fp);
 +#endif
 +
 +}
index b9ebaaf4c1101401525f98b46a6eb309971ca599,0000000000000000000000000000000000000000..71b6abd5879f9cff0db214519e8bc01cfdeae873
mode 100644,000000..100644
--- /dev/null
@@@ -1,816 -1,0 +1,816 @@@
-  * Although the rest of Gromacs is GPL, you can copy and use the XDR
 +/*
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * GROningen Mixture of Alchemy and Childrens' Stories
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#ifdef GMX_INTERNAL_XDR
 +
 +
 +#include <stdlib.h>
 +#include <limits.h>
 +#include <string.h>
 +
 +#include "gmx_system_xdr.h"
 +
 +
 +/* NB - THIS FILE IS ONLY USED ON MICROSOFT WINDOWS, since that
 + * system doesn't provide any standard XDR system libraries. It will
 + * most probably work on other platforms too, but make sure you
 + * test that the xtc files produced are ok before using it.
 + *
 + * This header file contains Gromacs versions of the definitions for
 + * Sun External Data Representation (XDR) headers and routines.
 + *
 + * On most UNIX systems this is already present as part of your
 + * system libraries, but since we want to make Gromacs portable to
 + * platforms like Microsoft Windows we have created a private version
 + * of the necessary routines and distribute them with the Gromacs source.
 + *
++ * Although the rest of Gromacs is LGPL, you can copy and use the XDR
 + * routines in any way you want as long as you obey Sun's license:
 + *
 + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 + * unrestricted use provided that this legend is included on all tape
 + * media and as a part of the software program in whole or part.  Users
 + * may copy or modify Sun RPC without charge, but are not authorized
 + * to license or distribute it to anyone else except as part of a product or
 + * program developed by the user.
 + *
 + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 + *
 + * Sun RPC is provided with no support and without any obligation on the
 + * part of Sun Microsystems, Inc. to assist in its use, correction,
 + * modification or enhancement.
 + *
 + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 + * OR ANY PART THEREOF.
 + *
 + * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 + * or profits or other special, indirect and consequential damages, even if
 + * Sun has been advised of the possibility of such damages.
 + *
 + * Sun Microsystems, Inc.
 + * 2550 Garcia Avenue
 + * Mountain View, California  94043
 + */
 +
 +
 +
 +/*
 + * for unit alignment
 + */
 +static char xdr_zero[BYTES_PER_XDR_UNIT] = {0, 0, 0, 0};
 +
 +static xdr_uint32_t xdr_swapbytes(xdr_uint32_t x)
 +{
 +    xdr_uint32_t y;
 +    int          i;
 +    char        *px = (char *)&x;
 +    char        *py = (char *)&y;
 +
 +    for (i = 0; i < 4; i++)
 +    {
 +        py[i] = px[3-i];
 +    }
 +
 +    return y;
 +}
 +
 +static xdr_uint32_t xdr_htonl(xdr_uint32_t x)
 +{
 +    short s = 0x0F00;
 +    if (*((char *)&s) == (char)0x0F)
 +    {
 +        /* bigendian, do nothing */
 +        return x;
 +    }
 +    else
 +    {
 +        /* smallendian,swap bytes */
 +        return xdr_swapbytes(x);
 +    }
 +}
 +
 +static xdr_uint32_t xdr_ntohl(xdr_uint32_t x)
 +{
 +    short s = 0x0F00;
 +    if (*((char *)&s) == (char)0x0F)
 +    {
 +        /* bigendian, do nothing */
 +        return x;
 +    }
 +    else
 +    {
 +        /* smallendian, swap bytes */
 +        return xdr_swapbytes(x);
 +    }
 +}
 +
 +
 +/*
 + * Free a data structure using XDR
 + * Not a filter, but a convenient utility nonetheless
 + */
 +void
 +xdr_free (xdrproc_t proc, char *objp)
 +{
 +    XDR x;
 +
 +    x.x_op = XDR_FREE;
 +    (*proc) ( &x, objp);
 +}
 +
 +/*
 + * XDR nothing
 + */
 +bool_t
 +xdr_void (void)
 +{
 +    return TRUE;
 +}
 +
 +/*
 + * XDR integers
 + */
 +bool_t
 +xdr_int (XDR *xdrs, int *ip)
 +{
 +    xdr_int32_t l;
 +
 +    switch (xdrs->x_op)
 +    {
 +        case XDR_ENCODE:
 +            l = (xdr_int32_t) (*ip);
 +            return xdr_putint32 (xdrs, &l);
 +
 +        case XDR_DECODE:
 +            if (!xdr_getint32 (xdrs, &l))
 +            {
 +                return FALSE;
 +            }
 +            *ip = (int) l;
 +
 +        case XDR_FREE:
 +            return TRUE;
 +    }
 +    return FALSE;
 +}
 +
 +
 +/*
 + * XDR unsigned integers
 + */
 +bool_t
 +xdr_u_int (XDR *xdrs, unsigned int *up)
 +{
 +    xdr_uint32_t l;
 +
 +    switch (xdrs->x_op)
 +    {
 +        case XDR_ENCODE:
 +            l = (xdr_uint32_t) (*up);
 +            return xdr_putuint32 (xdrs, &l);
 +
 +        case XDR_DECODE:
 +            if (!xdr_getuint32 (xdrs, &l))
 +            {
 +                return FALSE;
 +            }
 +            *up = (unsigned int) l;
 +
 +        case XDR_FREE:
 +            return TRUE;
 +    }
 +    return FALSE;
 +}
 +
 +
 +
 +
 +/*
 + * XDR short integers
 + */
 +bool_t
 +xdr_short (XDR *xdrs, short *sp)
 +{
 +    xdr_int32_t l;
 +
 +    switch (xdrs->x_op)
 +    {
 +        case XDR_ENCODE:
 +            l = (xdr_int32_t) *sp;
 +            return xdr_putint32 (xdrs, &l);
 +
 +        case XDR_DECODE:
 +            if (!xdr_getint32 (xdrs, &l))
 +            {
 +                return FALSE;
 +            }
 +            *sp = (short) l;
 +            return TRUE;
 +
 +        case XDR_FREE:
 +            return TRUE;
 +    }
 +    return FALSE;
 +}
 +
 +
 +/*
 + * XDR unsigned short integers
 + */
 +bool_t
 +xdr_u_short (XDR *xdrs, unsigned short *usp)
 +{
 +    xdr_uint32_t l;
 +
 +    switch (xdrs->x_op)
 +    {
 +        case XDR_ENCODE:
 +            l = (xdr_uint32_t) *usp;
 +            return xdr_putuint32 (xdrs, &l);
 +
 +        case XDR_DECODE:
 +            if (!xdr_getuint32 (xdrs, &l))
 +            {
 +                return FALSE;
 +            }
 +            *usp = (unsigned short) l;
 +            return TRUE;
 +
 +        case XDR_FREE:
 +            return TRUE;
 +    }
 +    return FALSE;
 +}
 +
 +
 +/*
 + * XDR a char
 + */
 +bool_t
 +xdr_char (XDR *xdrs, char *cp)
 +{
 +    int i;
 +
 +    i = (*cp);
 +    if (!xdr_int (xdrs, &i))
 +    {
 +        return FALSE;
 +    }
 +    *cp = i;
 +    return TRUE;
 +}
 +
 +/*
 + * XDR an unsigned char
 + */
 +bool_t
 +xdr_u_char (XDR *xdrs, unsigned char *cp)
 +{
 +    unsigned int u;
 +
 +    u = (*cp);
 +    if (!xdr_u_int (xdrs, &u))
 +    {
 +        return FALSE;
 +    }
 +    *cp = u;
 +    return TRUE;
 +}
 +
 +/*
 + * XDR booleans
 + */
 +bool_t
 +xdr_bool (XDR *xdrs, int *bp)
 +{
 +#define XDR_FALSE   ((xdr_int32_t) 0)
 +#define XDR_TRUE    ((xdr_int32_t) 1)
 +
 +    xdr_int32_t lb;
 +
 +    switch (xdrs->x_op)
 +    {
 +        case XDR_ENCODE:
 +            lb = *bp ? XDR_TRUE : XDR_FALSE;
 +            return xdr_putint32 (xdrs, &lb);
 +
 +        case XDR_DECODE:
 +            if (!xdr_getint32 (xdrs, &lb))
 +            {
 +                return FALSE;
 +            }
 +            *bp = (lb == XDR_FALSE) ? FALSE : TRUE;
 +            return TRUE;
 +
 +        case XDR_FREE:
 +            return TRUE;
 +    }
 +    return FALSE;
 +#undef XDR_FALSE
 +#undef XDR_TRUE
 +}
 +
 +
 +
 +/*
 + * XDR opaque data
 + * Allows the specification of a fixed size sequence of opaque bytes.
 + * cp points to the opaque object and cnt gives the byte length.
 + */
 +bool_t
 +xdr_opaque (XDR *xdrs, char *cp, unsigned int cnt)
 +{
 +    unsigned int rndup;
 +    char         crud[BYTES_PER_XDR_UNIT];
 +
 +    /*
 +     * if no data we are done
 +     */
 +    if (cnt == 0)
 +    {
 +        return TRUE;
 +    }
 +
 +    /*
 +     * round byte count to full xdr units
 +     */
 +    rndup = cnt % BYTES_PER_XDR_UNIT;
 +    if (rndup > 0)
 +    {
 +        rndup = BYTES_PER_XDR_UNIT - rndup;
 +    }
 +
 +    switch (xdrs->x_op)
 +    {
 +        case XDR_DECODE:
 +            if (!xdr_getbytes (xdrs, cp, cnt))
 +            {
 +                return FALSE;
 +            }
 +            if (rndup == 0)
 +            {
 +                return TRUE;
 +            }
 +            return xdr_getbytes (xdrs, (char *)crud, rndup);
 +
 +        case XDR_ENCODE:
 +            if (!xdr_putbytes (xdrs, cp, cnt))
 +            {
 +                return FALSE;
 +            }
 +            if (rndup == 0)
 +            {
 +                return TRUE;
 +            }
 +            return xdr_putbytes (xdrs, xdr_zero, rndup);
 +
 +        case XDR_FREE:
 +            return TRUE;
 +    }
 +    return FALSE;
 +}
 +
 +
 +/*
 + * XDR null terminated ASCII strings
 + * xdr_string deals with "C strings" - arrays of bytes that are
 + * terminated by a NULL character.  The parameter cpp references a
 + * pointer to storage; If the pointer is null, then the necessary
 + * storage is allocated.  The last parameter is the max allowed length
 + * of the string as specified by a protocol.
 + */
 +bool_t
 +xdr_string (xdrs, cpp, maxsize)
 +XDR *xdrs;
 +char       **cpp;
 +unsigned int maxsize;
 +{
 +    char        *sp       = *cpp; /* sp is the actual string pointer */
 +    unsigned int size     = 0;
 +    unsigned int nodesize = 0;
 +
 +    /*
 +     * first deal with the length since xdr strings are counted-strings
 +     */
 +    switch (xdrs->x_op)
 +    {
 +        case XDR_FREE:
 +            if (sp == NULL)
 +            {
 +                return TRUE; /* already free */
 +            }
 +        /* fall through... */
 +        case XDR_ENCODE:
 +            if (sp == NULL)
 +            {
 +                return FALSE;
 +            }
 +            size = strlen (sp);
 +            break;
 +        case XDR_DECODE:
 +            break;
 +    }
 +
 +    if (!xdr_u_int (xdrs, &size))
 +    {
 +        return FALSE;
 +    }
 +    if (size > maxsize)
 +    {
 +        return FALSE;
 +    }
 +    nodesize = size + 1;
 +
 +    /*
 +     * now deal with the actual bytes
 +     */
 +    switch (xdrs->x_op)
 +    {
 +        case XDR_DECODE:
 +            if (nodesize == 0)
 +            {
 +                return TRUE;
 +            }
 +            if (sp == NULL)
 +            {
 +                *cpp = sp = (char *) malloc (nodesize);
 +            }
 +            if (sp == NULL)
 +            {
 +                (void) fputs ("xdr_string: out of memory\n", stderr);
 +                return FALSE;
 +            }
 +            sp[size] = 0;
 +        /* fall into ... */
 +
 +        case XDR_ENCODE:
 +            return xdr_opaque (xdrs, sp, size);
 +
 +        case XDR_FREE:
 +            free (sp);
 +            *cpp = NULL;
 +            return TRUE;
 +    }
 +    return FALSE;
 +}
 +
 +
 +
 +/* Floating-point stuff */
 +
 +bool_t
 +xdr_float(xdrs, fp)
 +XDR *xdrs;
 +float *fp;
 +{
 +    xdr_int32_t tmp;
 +
 +    switch (xdrs->x_op)
 +    {
 +
 +        case XDR_ENCODE:
 +            tmp = *(xdr_int32_t *)fp;
 +            return (xdr_putint32(xdrs, &tmp));
 +
 +            break;
 +
 +        case XDR_DECODE:
 +            if (xdr_getint32(xdrs, &tmp))
 +            {
 +                *(xdr_int32_t *)fp = tmp;
 +                return (TRUE);
 +            }
 +
 +            break;
 +
 +        case XDR_FREE:
 +            return (TRUE);
 +    }
 +    return (FALSE);
 +}
 +
 +
 +bool_t
 +xdr_double(xdrs, dp)
 +XDR *xdrs;
 +double *dp;
 +{
 +
 +    /* Windows and some other systems dont define double-precision
 +     * word order in the header files, so unfortunately we have
 +     * to calculate it!
 +     *
 +     * For thread safety, we calculate it every time: locking this would
 +     * be more expensive.
 +     */
 +    /*static int LSW=-1;*/ /* Least significant fp word */
 +    int LSW = -1; /* Least significant fp word */
 +
 +
 +    int        *ip;
 +    xdr_int32_t tmp[2];
 +
 +    if (LSW < 0)
 +    {
 +        double x = 0.987654321; /* Just a number */
 +
 +        /* Possible representations in IEEE double precision:
 +         * (S=small endian, B=big endian)
 +         *
 +         * Byte order, Word order, Hex
 +         *     S           S       b8 56 0e 3c dd 9a ef 3f
 +         *     B           S       3c 0e 56 b8 3f ef 9a dd
 +         *     S           B       dd 9a ef 3f b8 56 0e 3c
 +         *     B           B       3f ef 9a dd 3c 0e 56 b8
 +         */
 +
 +        unsigned char ix = *((char *)&x);
 +
 +        if (ix == 0xdd || ix == 0x3f)
 +        {
 +            LSW = 1; /* Big endian word order */
 +        }
 +        else if (ix == 0xb8 || ix == 0x3c)
 +        {
 +            LSW = 0; /* Small endian word order */
 +        }
 +        else         /* Catch strange errors */
 +        {
 +            printf("Error when detecting floating-point word order.\n"
 +                   "Do you have a non-IEEE system?\n"
 +                   "If possible, use the XDR libraries provided with your system,\n"
 +                   "instead of the Gromacs fallback XDR source.\n");
 +            exit(0);
 +        }
 +    }
 +
 +    switch (xdrs->x_op)
 +    {
 +
 +        case XDR_ENCODE:
 +            ip     = (int *)dp;
 +            tmp[0] = ip[!LSW];
 +            tmp[1] = ip[LSW];
 +            return (xdr_putint32(xdrs, tmp) &&
 +                    xdr_putint32(xdrs, tmp+1));
 +
 +            break;
 +
 +        case XDR_DECODE:
 +            ip = (int *)dp;
 +            if (xdr_getint32(xdrs, tmp+!LSW) &&
 +                xdr_getint32(xdrs, tmp+LSW))
 +            {
 +                ip[0] = tmp[0];
 +                ip[1] = tmp[1];
 +                return (TRUE);
 +            }
 +
 +            break;
 +
 +        case XDR_FREE:
 +            return (TRUE);
 +    }
 +    return (FALSE);
 +}
 +
 +
 +/* Array routines */
 +
 +/*
 + * xdr_vector():
 + *
 + * XDR a fixed length array. Unlike variable-length arrays,
 + * the storage of fixed length arrays is static and unfreeable.
 + * > basep: base of the array
 + * > size: size of the array
 + * > elemsize: size of each element
 + * > xdr_elem: routine to XDR each element
 + */
 +bool_t
 +xdr_vector (xdrs, basep, nelem, elemsize, xdr_elem)
 +XDR *xdrs;
 +char        *basep;
 +unsigned int nelem;
 +unsigned int elemsize;
 +xdrproc_t    xdr_elem;
 +{
 +#define LASTUNSIGNED    ((unsigned int)0-1)
 +    unsigned int i;
 +    char        *elptr;
 +
 +    elptr = basep;
 +    for (i = 0; i < nelem; i++)
 +    {
 +        if (!(*xdr_elem) (xdrs, elptr, LASTUNSIGNED))
 +        {
 +            return FALSE;
 +        }
 +        elptr += elemsize;
 +    }
 +    return TRUE;
 +#undef LASTUNSIGNED
 +}
 +
 +
 +
 +static bool_t xdrstdio_getbytes (XDR *, char *, unsigned int);
 +static bool_t xdrstdio_putbytes (XDR *, char *, unsigned int);
 +static unsigned int xdrstdio_getpos (XDR *);
 +static bool_t xdrstdio_setpos (XDR *, unsigned int);
 +static xdr_int32_t *xdrstdio_inline (XDR *, int);
 +static void xdrstdio_destroy (XDR *);
 +static bool_t xdrstdio_getint32 (XDR *, xdr_int32_t *);
 +static bool_t xdrstdio_putint32 (XDR *, xdr_int32_t *);
 +static bool_t xdrstdio_getuint32 (XDR *, xdr_uint32_t *);
 +static bool_t xdrstdio_putuint32 (XDR *, xdr_uint32_t *);
 +
 +/*
 + * Ops vector for stdio type XDR
 + */
 +static const struct xdr_ops xdrstdio_ops =
 +{
 +    xdrstdio_getbytes,  /* deserialize counted bytes */
 +    xdrstdio_putbytes,  /* serialize counted bytes */
 +    xdrstdio_getpos,    /* get offset in the stream */
 +    xdrstdio_setpos,    /* set offset in the stream */
 +    xdrstdio_inline,    /* prime stream for inline macros */
 +    xdrstdio_destroy,   /* destroy stream */
 +    xdrstdio_getint32,  /* deserialize a int */
 +    xdrstdio_putint32,  /* serialize a int */
 +    xdrstdio_getuint32, /* deserialize a int */
 +    xdrstdio_putuint32  /* serialize a int */
 +};
 +
 +/*
 + * Initialize a stdio xdr stream.
 + * Sets the xdr stream handle xdrs for use on the stream file.
 + * Operation flag is set to op.
 + */
 +void
 +xdrstdio_create (XDR *xdrs, FILE *file, enum xdr_op op)
 +{
 +    xdrs->x_op = op;
 +    /* We have to add the const since the `struct xdr_ops' in `struct XDR'
 +       is not `const'.  */
 +    xdrs->x_ops     = (struct xdr_ops *) &xdrstdio_ops;
 +    xdrs->x_private = (char *) file;
 +    xdrs->x_handy   = 0;
 +    xdrs->x_base    = 0;
 +}
 +
 +/*
 + * Destroy a stdio xdr stream.
 + * Cleans up the xdr stream handle xdrs previously set up by xdrstdio_create.
 + */
 +static void
 +xdrstdio_destroy (XDR *xdrs)
 +{
 +    (void) fflush ((FILE *) xdrs->x_private);
 +    /* xx should we close the file ?? */
 +}
 +
 +
 +static bool_t
 +xdrstdio_getbytes (XDR *xdrs, char *addr, unsigned int len)
 +{
 +    if ((len != 0) && (fread (addr, (int) len, 1,
 +                              (FILE *) xdrs->x_private) != 1))
 +    {
 +        return FALSE;
 +    }
 +    return TRUE;
 +}
 +
 +static bool_t
 +xdrstdio_putbytes (XDR *xdrs, char *addr, unsigned int len)
 +{
 +    if ((len != 0) && (fwrite (addr, (int) len, 1,
 +                               (FILE *) xdrs->x_private) != 1))
 +    {
 +        return FALSE;
 +    }
 +    return TRUE;
 +}
 +
 +static unsigned int
 +xdrstdio_getpos (XDR *xdrs)
 +{
 +    return (unsigned int) ftell ((FILE *) xdrs->x_private);
 +}
 +
 +static bool_t
 +xdrstdio_setpos (XDR *xdrs, unsigned int pos)
 +{
 +    return fseek ((FILE *) xdrs->x_private, (xdr_int32_t) pos, 0) < 0 ? FALSE : TRUE;
 +}
 +
 +static xdr_int32_t *
 +xdrstdio_inline (XDR *xdrs, int len)
 +{
 +    /*
 +     * Must do some work to implement this: must insure
 +     * enough data in the underlying stdio buffer,
 +     * that the buffer is aligned so that we can indirect through a
 +     * long *, and stuff this pointer in xdrs->x_buf.  Doing
 +     * a fread or fwrite to a scratch buffer would defeat
 +     * most of the gains to be had here and require storage
 +     * management on this buffer, so we don't do this.
 +     */
 +    return NULL;
 +}
 +
 +static bool_t
 +xdrstdio_getint32 (XDR *xdrs, xdr_int32_t *ip)
 +{
 +    xdr_int32_t mycopy;
 +
 +    if (fread ((char *) &mycopy, 4, 1, (FILE *) xdrs->x_private) != 1)
 +    {
 +        return FALSE;
 +    }
 +    *ip = xdr_ntohl (mycopy);
 +    return TRUE;
 +}
 +
 +static bool_t
 +xdrstdio_putint32 (XDR *xdrs, xdr_int32_t *ip)
 +{
 +    xdr_int32_t mycopy = xdr_htonl (*ip);
 +
 +    ip = &mycopy;
 +    if (fwrite ((char *) ip, 4, 1, (FILE *) xdrs->x_private) != 1)
 +    {
 +        return FALSE;
 +    }
 +    return TRUE;
 +}
 +
 +static bool_t
 +xdrstdio_getuint32 (XDR *xdrs, xdr_uint32_t *ip)
 +{
 +    xdr_uint32_t mycopy;
 +
 +    if (fread ((char *) &mycopy, 4, 1, (FILE *) xdrs->x_private) != 1)
 +    {
 +        return FALSE;
 +    }
 +    *ip = xdr_ntohl (mycopy);
 +    return TRUE;
 +}
 +
 +static bool_t
 +xdrstdio_putuint32 (XDR *xdrs, xdr_uint32_t *ip)
 +{
 +    xdr_uint32_t mycopy = xdr_htonl (*ip);
 +
 +    ip = &mycopy;
 +    if (fwrite ((char *) ip, 4, 1, (FILE *) xdrs->x_private) != 1)
 +    {
 +        return FALSE;
 +    }
 +    return TRUE;
 +}
 +
 +#else
 +int
 +    gmx_system_xdr_empty;
 +#endif /* GMX_SYSTEM_XDR */
index 9b17054dd9268bde48ee3d6ed3999f10e591998a,0000000000000000000000000000000000000000..8c0ec0b588456cf1e4bb6fa23808a73843f3997c
mode 100644,000000..100644
--- /dev/null
@@@ -1,790 -1,0 +1,802 @@@
-                             default:
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * GROningen Mixture of Alchemy and Childrens' Stories
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +
 +#include "vec.h"
 +#include "typedefs.h"
 +#include "nonbonded.h"
 +#include "nb_kernel.h"
 +#include "nrnb.h"
 +
 +void
 +gmx_nb_free_energy_kernel(t_nblist *                nlist,
 +                          rvec *                    xx,
 +                          rvec *                    ff,
 +                          t_forcerec *              fr,
 +                          t_mdatoms *               mdatoms,
 +                          nb_kernel_data_t *        kernel_data,
 +                          t_nrnb *                  nrnb)
 +{
 +
 +#define  STATE_A  0
 +#define  STATE_B  1
 +#define  NSTATES  2
 +    int           i, j, n, ii, is3, ii3, k, nj0, nj1, jnr, j3, ggid;
 +    real          shX, shY, shZ;
 +    real          Fscal, FscalC[NSTATES], FscalV[NSTATES], tx, ty, tz;
 +    real          Vcoul[NSTATES], Vvdw[NSTATES];
 +    real          rinv6, r, rt, rtC, rtV;
 +    real          iqA, iqB;
 +    real          qq[NSTATES], vctot, krsq;
 +    int           ntiA, ntiB, tj[NSTATES];
 +    real          Vvdw6, Vvdw12, vvtot;
 +    real          ix, iy, iz, fix, fiy, fiz;
 +    real          dx, dy, dz, rsq, rinv;
 +    real          c6[NSTATES], c12[NSTATES];
 +    real          LFC[NSTATES], LFV[NSTATES], DLF[NSTATES];
 +    double        dvdl_coul, dvdl_vdw;
 +    real          lfac_coul[NSTATES], dlfac_coul[NSTATES], lfac_vdw[NSTATES], dlfac_vdw[NSTATES];
 +    real          sigma6[NSTATES], alpha_vdw_eff, alpha_coul_eff, sigma2_def, sigma2_min;
 +    real          rp, rpm2, rC, rV, rinvC, rpinvC, rinvV, rpinvV;
 +    real          sigma2[NSTATES], sigma_pow[NSTATES], sigma_powm2[NSTATES], rs, rs2;
 +    int           do_coultab, do_vdwtab, do_tab, tab_elemsize;
 +    int           n0, n1C, n1V, nnn;
 +    real          Y, F, G, H, Fp, Geps, Heps2, epsC, eps2C, epsV, eps2V, VV, FF;
 +    int           icoul, ivdw;
 +    int           nri;
 +    int *         iinr;
 +    int *         jindex;
 +    int *         jjnr;
 +    int *         shift;
 +    int *         gid;
 +    int *         typeA;
 +    int *         typeB;
 +    int           ntype;
 +    real *        shiftvec;
 +    real          dvdl_part;
 +    real *        fshift;
 +    real          tabscale;
 +    real *        VFtab;
 +    real *        x;
 +    real *        f;
 +    real          facel, krf, crf;
 +    real *        chargeA;
 +    real *        chargeB;
 +    real          sigma6_min, sigma6_def, lam_power, sc_power, sc_r_power;
 +    real          alpha_coul, alpha_vdw, lambda_coul, lambda_vdw, ewc;
 +    real *        nbfp;
 +    real *        dvdl;
 +    real *        Vv;
 +    real *        Vc;
 +    gmx_bool      bDoForces;
 +    real          rcoulomb, rvdw, sh_invrc6;
 +    gmx_bool      bExactElecCutoff, bExactVdwCutoff;
 +    real          rcutoff, rcutoff2, rswitch, d, d2, swV3, swV4, swV5, swF2, swF3, swF4, sw, dsw, rinvcorr;
 +
 +    x                   = xx[0];
 +    f                   = ff[0];
 +
 +    fshift              = fr->fshift[0];
 +    Vc                  = kernel_data->energygrp_elec;
 +    Vv                  = kernel_data->energygrp_vdw;
 +    tabscale            = kernel_data->table_elec_vdw->scale;
 +    VFtab               = kernel_data->table_elec_vdw->data;
 +
 +    nri                 = nlist->nri;
 +    iinr                = nlist->iinr;
 +    jindex              = nlist->jindex;
 +    jjnr                = nlist->jjnr;
 +    icoul               = nlist->ielec;
 +    ivdw                = nlist->ivdw;
 +    shift               = nlist->shift;
 +    gid                 = nlist->gid;
 +
 +    shiftvec            = fr->shift_vec[0];
 +    chargeA             = mdatoms->chargeA;
 +    chargeB             = mdatoms->chargeB;
 +    facel               = fr->epsfac;
 +    krf                 = fr->k_rf;
 +    crf                 = fr->c_rf;
 +    ewc                 = fr->ewaldcoeff;
 +    Vc                  = kernel_data->energygrp_elec;
 +    typeA               = mdatoms->typeA;
 +    typeB               = mdatoms->typeB;
 +    ntype               = fr->ntype;
 +    nbfp                = fr->nbfp;
 +    Vv                  = kernel_data->energygrp_vdw;
 +    tabscale            = kernel_data->table_elec_vdw->scale;
 +    VFtab               = kernel_data->table_elec_vdw->data;
 +    lambda_coul         = kernel_data->lambda[efptCOUL];
 +    lambda_vdw          = kernel_data->lambda[efptVDW];
 +    dvdl                = kernel_data->dvdl;
 +    alpha_coul          = fr->sc_alphacoul;
 +    alpha_vdw           = fr->sc_alphavdw;
 +    lam_power           = fr->sc_power;
 +    sc_r_power          = fr->sc_r_power;
 +    sigma6_def          = fr->sc_sigma6_def;
 +    sigma6_min          = fr->sc_sigma6_min;
 +    bDoForces           = kernel_data->flags & GMX_NONBONDED_DO_FORCE;
 +
 +    rcoulomb            = fr->rcoulomb;
 +    rvdw                = fr->rvdw;
 +    sh_invrc6           = fr->ic->sh_invrc6;
 +
 +    if (fr->coulomb_modifier == eintmodPOTSWITCH || fr->vdw_modifier == eintmodPOTSWITCH)
 +    {
 +        rcutoff         = (fr->coulomb_modifier == eintmodPOTSWITCH) ? fr->rcoulomb : fr->rvdw;
 +        rcutoff2        = rcutoff*rcutoff;
 +        rswitch         = (fr->coulomb_modifier == eintmodPOTSWITCH) ? fr->rcoulomb_switch : fr->rvdw_switch;
 +        d               = rcutoff-rswitch;
 +        swV3            = -10.0/(d*d*d);
 +        swV4            =  15.0/(d*d*d*d);
 +        swV5            =  -6.0/(d*d*d*d*d);
 +        swF2            = -30.0/(d*d*d);
 +        swF3            =  60.0/(d*d*d*d);
 +        swF4            = -30.0/(d*d*d*d*d);
 +    }
 +    else
 +    {
 +        /* Stupid compilers dont realize these variables will not be used */
 +        rswitch         = 0.0;
 +        swV3            = 0.0;
 +        swV4            = 0.0;
 +        swV5            = 0.0;
 +        swF2            = 0.0;
 +        swF3            = 0.0;
 +        swF4            = 0.0;
 +    }
 +
 +    bExactElecCutoff    = (fr->coulomb_modifier != eintmodNONE) || fr->eeltype == eelRF_ZERO;
 +    bExactVdwCutoff     = (fr->vdw_modifier != eintmodNONE);
 +
 +    /* fix compiler warnings */
 +    nj1   = 0;
 +    n1C   = n1V   = 0;
 +    epsC  = epsV  = 0;
 +    eps2C = eps2V = 0;
 +
 +    dvdl_coul  = 0;
 +    dvdl_vdw   = 0;
 +
 +    /* Lambda factor for state A, 1-lambda*/
 +    LFC[STATE_A] = 1.0 - lambda_coul;
 +    LFV[STATE_A] = 1.0 - lambda_vdw;
 +
 +    /* Lambda factor for state B, lambda*/
 +    LFC[STATE_B] = lambda_coul;
 +    LFV[STATE_B] = lambda_vdw;
 +
 +    /*derivative of the lambda factor for state A and B */
 +    DLF[STATE_A] = -1;
 +    DLF[STATE_B] = 1;
 +
 +    for (i = 0; i < NSTATES; i++)
 +    {
 +        lfac_coul[i]  = (lam_power == 2 ? (1-LFC[i])*(1-LFC[i]) : (1-LFC[i]));
 +        dlfac_coul[i] = DLF[i]*lam_power/sc_r_power*(lam_power == 2 ? (1-LFC[i]) : 1);
 +        lfac_vdw[i]   = (lam_power == 2 ? (1-LFV[i])*(1-LFV[i]) : (1-LFV[i]));
 +        dlfac_vdw[i]  = DLF[i]*lam_power/sc_r_power*(lam_power == 2 ? (1-LFV[i]) : 1);
 +    }
 +    /* precalculate */
 +    sigma2_def = pow(sigma6_def, 1.0/3.0);
 +    sigma2_min = pow(sigma6_min, 1.0/3.0);
 +
 +    /* Ewald (not PME) table is special (icoul==enbcoulFEWALD) */
 +
 +    do_coultab = (icoul == GMX_NBKERNEL_ELEC_CUBICSPLINETABLE);
 +    do_vdwtab  = (ivdw == GMX_NBKERNEL_VDW_CUBICSPLINETABLE);
 +
 +    do_tab = do_coultab || do_vdwtab;
 +
 +    /* we always use the combined table here */
 +    tab_elemsize = 12;
 +
 +    for (n = 0; (n < nri); n++)
 +    {
 +        is3              = 3*shift[n];
 +        shX              = shiftvec[is3];
 +        shY              = shiftvec[is3+1];
 +        shZ              = shiftvec[is3+2];
 +        nj0              = jindex[n];
 +        nj1              = jindex[n+1];
 +        ii               = iinr[n];
 +        ii3              = 3*ii;
 +        ix               = shX + x[ii3+0];
 +        iy               = shY + x[ii3+1];
 +        iz               = shZ + x[ii3+2];
 +        iqA              = facel*chargeA[ii];
 +        iqB              = facel*chargeB[ii];
 +        ntiA             = 2*ntype*typeA[ii];
 +        ntiB             = 2*ntype*typeB[ii];
 +        vctot            = 0;
 +        vvtot            = 0;
 +        fix              = 0;
 +        fiy              = 0;
 +        fiz              = 0;
 +
 +        for (k = nj0; (k < nj1); k++)
 +        {
 +            jnr              = jjnr[k];
 +            j3               = 3*jnr;
 +            dx               = ix - x[j3];
 +            dy               = iy - x[j3+1];
 +            dz               = iz - x[j3+2];
 +            rsq              = dx*dx+dy*dy+dz*dz;
 +            rinv             = gmx_invsqrt(rsq);
 +            r                = rsq*rinv;
 +            if (sc_r_power == 6.0)
 +            {
 +                rpm2             = rsq*rsq;  /* r4 */
 +                rp               = rpm2*rsq; /* r6 */
 +            }
 +            else if (sc_r_power == 48.0)
 +            {
 +                rp               = rsq*rsq*rsq; /* r6 */
 +                rp               = rp*rp;       /* r12 */
 +                rp               = rp*rp;       /* r24 */
 +                rp               = rp*rp;       /* r48 */
 +                rpm2             = rp/rsq;      /* r46 */
 +            }
 +            else
 +            {
 +                rp             = pow(r, sc_r_power);  /* not currently supported as input, but can handle it */
 +                rpm2           = rp/rsq;
 +            }
 +
 +            tj[STATE_A]      = ntiA+2*typeA[jnr];
 +            tj[STATE_B]      = ntiB+2*typeB[jnr];
 +            qq[STATE_A]      = iqA*chargeA[jnr];
 +            qq[STATE_B]      = iqB*chargeB[jnr];
 +
 +            for (i = 0; i < NSTATES; i++)
 +            {
 +
 +                c6[i]              = nbfp[tj[i]];
 +                c12[i]             = nbfp[tj[i]+1];
 +                if ((c6[i] > 0) && (c12[i] > 0))
 +                {
 +                    /* c12 is stored scaled with 12.0 and c6 is scaled with 6.0 - correct for this */
 +                    sigma6[i]       = 0.5*c12[i]/c6[i];
 +                    sigma2[i]       = pow(sigma6[i], 1.0/3.0);
 +                    /* should be able to get rid of this ^^^ internal pow call eventually.  Will require agreement on
 +                       what data to store externally.  Can't be fixed without larger scale changes, so not 4.6 */
 +                    if (sigma6[i] < sigma6_min)   /* for disappearing coul and vdw with soft core at the same time */
 +                    {
 +                        sigma6[i] = sigma6_min;
 +                        sigma2[i] = sigma2_min;
 +                    }
 +                }
 +                else
 +                {
 +                    sigma6[i]       = sigma6_def;
 +                    sigma2[i]       = sigma2_def;
 +                }
 +                if (sc_r_power == 6.0)
 +                {
 +                    sigma_pow[i]    = sigma6[i];
 +                    sigma_powm2[i]  = sigma6[i]/sigma2[i];
 +                }
 +                else if (sc_r_power == 48.0)
 +                {
 +                    sigma_pow[i]    = sigma6[i]*sigma6[i];       /* sigma^12 */
 +                    sigma_pow[i]    = sigma_pow[i]*sigma_pow[i]; /* sigma^24 */
 +                    sigma_pow[i]    = sigma_pow[i]*sigma_pow[i]; /* sigma^48 */
 +                    sigma_powm2[i]  = sigma_pow[i]/sigma2[i];
 +                }
 +                else
 +                {    /* not really supported as input, but in here for testing the general case*/
 +                    sigma_pow[i]    = pow(sigma2[i], sc_r_power/2);
 +                    sigma_powm2[i]  = sigma_pow[i]/(sigma2[i]);
 +                }
 +            }
 +
 +            /* only use softcore if one of the states has a zero endstate - softcore is for avoiding infinities!*/
 +            if ((c12[STATE_A] > 0) && (c12[STATE_B] > 0))
 +            {
 +                alpha_vdw_eff    = 0;
 +                alpha_coul_eff   = 0;
 +            }
 +            else
 +            {
 +                alpha_vdw_eff    = alpha_vdw;
 +                alpha_coul_eff   = alpha_coul;
 +            }
 +
 +            for (i = 0; i < NSTATES; i++)
 +            {
 +                FscalC[i]    = 0;
 +                FscalV[i]    = 0;
 +                Vcoul[i]     = 0;
 +                Vvdw[i]      = 0;
 +
 +                /* Only spend time on A or B state if it is non-zero */
 +                if ( (qq[i] != 0) || (c6[i] != 0) || (c12[i] != 0) )
 +                {
 +
 +                    /* this section has to be inside the loop becaue of the dependence on sigma_pow */
 +                    rpinvC         = 1.0/(alpha_coul_eff*lfac_coul[i]*sigma_pow[i]+rp);
 +                    rinvC          = pow(rpinvC, 1.0/sc_r_power);
 +                    rC             = 1.0/rinvC;
 +
 +                    rpinvV         = 1.0/(alpha_vdw_eff*lfac_vdw[i]*sigma_pow[i]+rp);
 +                    rinvV          = pow(rpinvV, 1.0/sc_r_power);
 +                    rV             = 1.0/rinvV;
 +
 +                    if (do_tab)
 +                    {
 +                        rtC        = rC*tabscale;
 +                        n0         = rtC;
 +                        epsC       = rtC-n0;
 +                        eps2C      = epsC*epsC;
 +                        n1C        = tab_elemsize*n0;
 +
 +                        rtV        = rV*tabscale;
 +                        n0         = rtV;
 +                        epsV       = rtV-n0;
 +                        eps2V      = epsV*epsV;
 +                        n1V        = tab_elemsize*n0;
 +                    }
 +
 +                    /* With Ewald and soft-core we should put the cut-off on r,
 +                     * not on the soft-cored rC, as the real-space and
 +                     * reciprocal space contributions should (almost) cancel.
 +                     */
 +                    if (qq[i] != 0 &&
 +                        !(bExactElecCutoff &&
 +                          ((icoul != GMX_NBKERNEL_ELEC_EWALD && rC >= rcoulomb) ||
 +                           (icoul == GMX_NBKERNEL_ELEC_EWALD && r >= rcoulomb))))
 +                    {
 +                        switch (icoul)
 +                        {
 +                            case GMX_NBKERNEL_ELEC_COULOMB:
 +                            case GMX_NBKERNEL_ELEC_EWALD:
 +                                /* simple cutoff (yes, ewald is done all on direct space for free energy) */
 +                                Vcoul[i]   = qq[i]*rinvC;
 +                                FscalC[i]  = Vcoul[i]*rpinvC;
 +                                break;
 +
 +                            case GMX_NBKERNEL_ELEC_REACTIONFIELD:
 +                                /* reaction-field */
 +                                Vcoul[i]   = qq[i]*(rinvC+krf*rC*rC-crf);
 +                                FscalC[i]  = qq[i]*(rinvC*rpinvC-2.0*krf);
 +                                break;
 +
 +                            case GMX_NBKERNEL_ELEC_CUBICSPLINETABLE:
 +                                /* non-Ewald tabulated coulomb */
 +                                nnn        = n1C;
 +                                Y          = VFtab[nnn];
 +                                F          = VFtab[nnn+1];
 +                                Geps       = epsC*VFtab[nnn+2];
 +                                Heps2      = eps2C*VFtab[nnn+3];
 +                                Fp         = F+Geps+Heps2;
 +                                VV         = Y+epsC*Fp;
 +                                FF         = Fp+Geps+2.0*Heps2;
 +                                Vcoul[i]   = qq[i]*VV;
 +                                FscalC[i]  = -qq[i]*tabscale*FF*rC*rpinvC;
 +                                break;
 +
-                             default:
++                            case GMX_NBKERNEL_ELEC_GENERALIZEDBORN:
++                                gmx_fatal(FARGS, "Free energy and GB not implemented.\n");
++                                break;
++
++                            case GMX_NBKERNEL_ELEC_NONE:
 +                                FscalC[i]  = 0.0;
 +                                Vcoul[i]   = 0.0;
 +                                break;
++
++                            default:
++                                gmx_incons("Invalid icoul in free energy kernel");
++                                break;
 +                        }
 +
 +                        if (fr->coulomb_modifier == eintmodPOTSWITCH)
 +                        {
 +                            d                = rC-rswitch;
 +                            d                = (d > 0.0) ? d : 0.0;
 +                            d2               = d*d;
 +                            sw               = 1.0+d2*d*(swV3+d*(swV4+d*swV5));
 +                            dsw              = d2*(swF2+d*(swF3+d*swF4));
 +
 +                            Vcoul[i]        *= sw;
 +                            FscalC[i]        = FscalC[i]*sw + Vcoul[i]*dsw;
 +                        }
 +                    }
 +
 +                    if ((c6[i] != 0 || c12[i] != 0) &&
 +                        !(bExactVdwCutoff && rV >= rvdw))
 +                    {
 +                        switch (ivdw)
 +                        {
 +                            case GMX_NBKERNEL_VDW_LENNARDJONES:
 +                                /* cutoff LJ */
 +                                if (sc_r_power == 6.0)
 +                                {
 +                                    rinv6            = rpinvV;
 +                                }
 +                                else
 +                                {
 +                                    rinv6            = pow(rinvV, 6.0);
 +                                }
 +                                Vvdw6            = c6[i]*rinv6;
 +                                Vvdw12           = c12[i]*rinv6*rinv6;
 +                                if (fr->vdw_modifier == eintmodPOTSHIFT)
 +                                {
 +                                    Vvdw[i]          = ( (Vvdw12-c12[i]*sh_invrc6*sh_invrc6)*(1.0/12.0)
 +                                                         -(Vvdw6-c6[i]*sh_invrc6)*(1.0/6.0));
 +                                }
 +                                else
 +                                {
 +                                    Vvdw[i]          = Vvdw12*(1.0/12.0)-Vvdw6*(1.0/6.0);
 +                                }
 +                                FscalV[i]        = (Vvdw12-Vvdw6)*rpinvV;
 +                                break;
 +
 +                            case GMX_NBKERNEL_VDW_BUCKINGHAM:
 +                                gmx_fatal(FARGS, "Buckingham free energy not supported.");
 +                                break;
 +
 +                            case GMX_NBKERNEL_VDW_CUBICSPLINETABLE:
 +                                /* Table LJ */
 +                                nnn = n1V+4;
 +                                /* dispersion */
 +                                Y          = VFtab[nnn];
 +                                F          = VFtab[nnn+1];
 +                                Geps       = epsV*VFtab[nnn+2];
 +                                Heps2      = eps2V*VFtab[nnn+3];
 +                                Fp         = F+Geps+Heps2;
 +                                VV         = Y+epsV*Fp;
 +                                FF         = Fp+Geps+2.0*Heps2;
 +                                Vvdw[i]   += c6[i]*VV;
 +                                FscalV[i] -= c6[i]*tabscale*FF*rV*rpinvV;
 +
 +                                /* repulsion */
 +                                Y          = VFtab[nnn+4];
 +                                F          = VFtab[nnn+5];
 +                                Geps       = epsV*VFtab[nnn+6];
 +                                Heps2      = eps2V*VFtab[nnn+7];
 +                                Fp         = F+Geps+Heps2;
 +                                VV         = Y+epsV*Fp;
 +                                FF         = Fp+Geps+2.0*Heps2;
 +                                Vvdw[i]   += c12[i]*VV;
 +                                FscalV[i] -= c12[i]*tabscale*FF*rV*rpinvV;
 +                                break;
 +
++                            case GMX_NBKERNEL_VDW_NONE:
 +                                Vvdw[i]    = 0.0;
 +                                FscalV[i]  = 0.0;
 +                                break;
++
++                            default:
++                                gmx_incons("Invalid ivdw in free energy kernel");
++                                break;
 +                        }
 +
 +                        if (fr->vdw_modifier == eintmodPOTSWITCH)
 +                        {
 +                            d                = rV-rswitch;
 +                            d                = (d > 0.0) ? d : 0.0;
 +                            d2               = d*d;
 +                            sw               = 1.0+d2*d*(swV3+d*(swV4+d*swV5));
 +                            dsw              = d2*(swF2+d*(swF3+d*swF4));
 +
 +                            Vvdw[i]         *= sw;
 +                            FscalV[i]        = FscalV[i]*sw + Vvdw[i]*dsw;
 +
 +                            FscalV[i]        = (rV < rvdw) ? FscalV[i] : 0.0;
 +                            Vvdw[i]          = (rV < rvdw) ? Vvdw[i] : 0.0;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            Fscal = 0;
 +
 +            if (icoul == GMX_NBKERNEL_ELEC_EWALD &&
 +                !(bExactElecCutoff && r >= rcoulomb))
 +            {
 +                /* because we compute the softcore normally,
 +                   we have to remove the ewald short range portion. Done outside of
 +                   the states loop because this part doesn't depend on the scaled R */
 +
 +#ifdef GMX_DOUBLE
 +                /* Relative accuracy at R_ERF_R_INACC of 3e-10 */
 +#define         R_ERF_R_INACC 0.006
 +#else
 +                /* Relative accuracy at R_ERF_R_INACC of 2e-5 */
 +#define         R_ERF_R_INACC 0.1
 +#endif
 +                if (ewc*r > R_ERF_R_INACC)
 +                {
 +                    VV    = gmx_erf(ewc*r)*rinv;
 +                    FF    = rinv*rinv*(VV - ewc*M_2_SQRTPI*exp(-ewc*ewc*rsq));
 +                }
 +                else
 +                {
 +                    VV    = ewc*M_2_SQRTPI;
 +                    FF    = ewc*ewc*ewc*M_2_SQRTPI*(2.0/3.0 - 0.4*ewc*ewc*rsq);
 +                }
 +
 +                for (i = 0; i < NSTATES; i++)
 +                {
 +                    vctot      -= LFC[i]*qq[i]*VV;
 +                    Fscal      -= LFC[i]*qq[i]*FF;
 +                    dvdl_coul  -= (DLF[i]*qq[i])*VV;
 +                }
 +            }
 +
 +            /* Assemble A and B states */
 +            for (i = 0; i < NSTATES; i++)
 +            {
 +                vctot         += LFC[i]*Vcoul[i];
 +                vvtot         += LFV[i]*Vvdw[i];
 +
 +                Fscal         += LFC[i]*FscalC[i]*rpm2;
 +                Fscal         += LFV[i]*FscalV[i]*rpm2;
 +
 +                dvdl_coul     += Vcoul[i]*DLF[i] + LFC[i]*alpha_coul_eff*dlfac_coul[i]*FscalC[i]*sigma_pow[i];
 +                dvdl_vdw      += Vvdw[i]*DLF[i] + LFV[i]*alpha_vdw_eff*dlfac_vdw[i]*FscalV[i]*sigma_pow[i];
 +            }
 +
 +            if (bDoForces)
 +            {
 +                tx         = Fscal*dx;
 +                ty         = Fscal*dy;
 +                tz         = Fscal*dz;
 +                fix        = fix + tx;
 +                fiy        = fiy + ty;
 +                fiz        = fiz + tz;
 +                f[j3]      = f[j3]   - tx;
 +                f[j3+1]    = f[j3+1] - ty;
 +                f[j3+2]    = f[j3+2] - tz;
 +            }
 +        }
 +
 +        if (bDoForces)
 +        {
 +            f[ii3]         = f[ii3]        + fix;
 +            f[ii3+1]       = f[ii3+1]      + fiy;
 +            f[ii3+2]       = f[ii3+2]      + fiz;
 +            fshift[is3]    = fshift[is3]   + fix;
 +            fshift[is3+1]  = fshift[is3+1] + fiy;
 +            fshift[is3+2]  = fshift[is3+2] + fiz;
 +        }
 +        ggid               = gid[n];
 +        Vc[ggid]           = Vc[ggid] + vctot;
 +        Vv[ggid]           = Vv[ggid] + vvtot;
 +    }
 +
 +    dvdl[efptCOUL]     += dvdl_coul;
 +    dvdl[efptVDW]      += dvdl_vdw;
 +
 +    /* Estimate flops, average for free energy stuff:
 +     * 12  flops per outer iteration
 +     * 150 flops per inner iteration
 +     */
 +    inc_nrnb(nrnb, eNR_NBKERNEL_FREE_ENERGY, nlist->nri*12 + nlist->jindex[n]*150);
 +}
 +
 +real
 +nb_free_energy_evaluate_single(real r2, real sc_r_power, real alpha_coul, real alpha_vdw,
 +                               real tabscale, real *vftab,
 +                               real qqA, real c6A, real c12A, real qqB, real c6B, real c12B,
 +                               real LFC[2], real LFV[2], real DLF[2],
 +                               real lfac_coul[2], real lfac_vdw[2], real dlfac_coul[2], real dlfac_vdw[2],
 +                               real sigma6_def, real sigma6_min, real sigma2_def, real sigma2_min,
 +                               real *velectot, real *vvdwtot, real *dvdl)
 +{
 +    real       r, rp, rpm2, rtab, eps, eps2, Y, F, Geps, Heps2, Fp, VV, FF, fscal;
 +    real       qq[2], c6[2], c12[2], sigma6[2], sigma2[2], sigma_pow[2], sigma_powm2[2];
 +    real       alpha_coul_eff, alpha_vdw_eff, dvdl_coul, dvdl_vdw;
 +    real       rpinv, r_coul, r_vdw, velecsum, vvdwsum;
 +    real       fscal_vdw[2], fscal_elec[2];
 +    real       velec[2], vvdw[2];
 +    int        i, ntab;
 +
 +    qq[0]    = qqA;
 +    qq[1]    = qqB;
 +    c6[0]    = c6A;
 +    c6[1]    = c6B;
 +    c12[0]   = c12A;
 +    c12[1]   = c12B;
 +
 +    if (sc_r_power == 6.0)
 +    {
 +        rpm2             = r2*r2;   /* r4 */
 +        rp               = rpm2*r2; /* r6 */
 +    }
 +    else if (sc_r_power == 48.0)
 +    {
 +        rp               = r2*r2*r2; /* r6 */
 +        rp               = rp*rp;    /* r12 */
 +        rp               = rp*rp;    /* r24 */
 +        rp               = rp*rp;    /* r48 */
 +        rpm2             = rp/r2;    /* r46 */
 +    }
 +    else
 +    {
 +        rp             = pow(r2, 0.5*sc_r_power);  /* not currently supported as input, but can handle it */
 +        rpm2           = rp/r2;
 +    }
 +
 +    /* Loop over state A(0) and B(1) */
 +    for (i = 0; i < 2; i++)
 +    {
 +        if ((c6[i] > 0) && (c12[i] > 0))
 +        {
 +            /* The c6 & c12 coefficients now contain the constants 6.0 and 12.0, respectively.
 +             * Correct for this by multiplying with (1/12.0)/(1/6.0)=6.0/12.0=0.5.
 +             */
 +            sigma6[i]       = 0.5*c12[i]/c6[i];
 +            sigma2[i]       = pow(0.5*c12[i]/c6[i], 1.0/3.0);
 +            /* should be able to get rid of this ^^^ internal pow call eventually.  Will require agreement on
 +               what data to store externally.  Can't be fixed without larger scale changes, so not 4.6 */
 +            if (sigma6[i] < sigma6_min)   /* for disappearing coul and vdw with soft core at the same time */
 +            {
 +                sigma6[i] = sigma6_min;
 +                sigma2[i] = sigma2_min;
 +            }
 +        }
 +        else
 +        {
 +            sigma6[i]       = sigma6_def;
 +            sigma2[i]       = sigma2_def;
 +        }
 +        if (sc_r_power == 6.0)
 +        {
 +            sigma_pow[i]    = sigma6[i];
 +            sigma_powm2[i]  = sigma6[i]/sigma2[i];
 +        }
 +        else if (sc_r_power == 48.0)
 +        {
 +            sigma_pow[i]    = sigma6[i]*sigma6[i];       /* sigma^12 */
 +            sigma_pow[i]    = sigma_pow[i]*sigma_pow[i]; /* sigma^24 */
 +            sigma_pow[i]    = sigma_pow[i]*sigma_pow[i]; /* sigma^48 */
 +            sigma_powm2[i]  = sigma_pow[i]/sigma2[i];
 +        }
 +        else
 +        {    /* not really supported as input, but in here for testing the general case*/
 +            sigma_pow[i]    = pow(sigma2[i], sc_r_power/2);
 +            sigma_powm2[i]  = sigma_pow[i]/(sigma2[i]);
 +        }
 +    }
 +
 +    /* only use softcore if one of the states has a zero endstate - softcore is for avoiding infinities!*/
 +    if ((c12[0] > 0) && (c12[1] > 0))
 +    {
 +        alpha_vdw_eff    = 0;
 +        alpha_coul_eff   = 0;
 +    }
 +    else
 +    {
 +        alpha_vdw_eff    = alpha_vdw;
 +        alpha_coul_eff   = alpha_coul;
 +    }
 +
 +    /* Loop over A and B states again */
 +    for (i = 0; i < 2; i++)
 +    {
 +        fscal_elec[i] = 0;
 +        fscal_vdw[i]  = 0;
 +        velec[i]      = 0;
 +        vvdw[i]       = 0;
 +
 +        /* Only spend time on A or B state if it is non-zero */
 +        if ( (qq[i] != 0) || (c6[i] != 0) || (c12[i] != 0) )
 +        {
 +            /* Coulomb */
 +            rpinv            = 1.0/(alpha_coul_eff*lfac_coul[i]*sigma_pow[i]+rp);
 +            r_coul           = pow(rpinv, -1.0/sc_r_power);
 +
 +            /* Electrostatics table lookup data */
 +            rtab             = r_coul*tabscale;
 +            ntab             = rtab;
 +            eps              = rtab-ntab;
 +            eps2             = eps*eps;
 +            ntab             = 12*ntab;
 +            /* Electrostatics */
 +            Y                = vftab[ntab];
 +            F                = vftab[ntab+1];
 +            Geps             = eps*vftab[ntab+2];
 +            Heps2            = eps2*vftab[ntab+3];
 +            Fp               = F+Geps+Heps2;
 +            VV               = Y+eps*Fp;
 +            FF               = Fp+Geps+2.0*Heps2;
 +            velec[i]         = qq[i]*VV;
 +            fscal_elec[i]    = -qq[i]*FF*r_coul*rpinv*tabscale;
 +
 +            /* Vdw */
 +            rpinv            = 1.0/(alpha_vdw_eff*lfac_vdw[i]*sigma_pow[i]+rp);
 +            r_vdw            = pow(rpinv, -1.0/sc_r_power);
 +            /* Vdw table lookup data */
 +            rtab             = r_vdw*tabscale;
 +            ntab             = rtab;
 +            eps              = rtab-ntab;
 +            eps2             = eps*eps;
 +            ntab             = 12*ntab;
 +            /* Dispersion */
 +            Y                = vftab[ntab+4];
 +            F                = vftab[ntab+5];
 +            Geps             = eps*vftab[ntab+6];
 +            Heps2            = eps2*vftab[ntab+7];
 +            Fp               = F+Geps+Heps2;
 +            VV               = Y+eps*Fp;
 +            FF               = Fp+Geps+2.0*Heps2;
 +            vvdw[i]          = c6[i]*VV;
 +            fscal_vdw[i]     = -c6[i]*FF;
 +
 +            /* Repulsion */
 +            Y                = vftab[ntab+8];
 +            F                = vftab[ntab+9];
 +            Geps             = eps*vftab[ntab+10];
 +            Heps2            = eps2*vftab[ntab+11];
 +            Fp               = F+Geps+Heps2;
 +            VV               = Y+eps*Fp;
 +            FF               = Fp+Geps+2.0*Heps2;
 +            vvdw[i]         += c12[i]*VV;
 +            fscal_vdw[i]    -= c12[i]*FF;
 +            fscal_vdw[i]    *= r_vdw*rpinv*tabscale;
 +        }
 +    }
 +    /* Now we have velec[i], vvdw[i], and fscal[i] for both states */
 +    /* Assemble A and B states */
 +    velecsum  = 0;
 +    vvdwsum   = 0;
 +    dvdl_coul = 0;
 +    dvdl_vdw  = 0;
 +    fscal     = 0;
 +    for (i = 0; i < 2; i++)
 +    {
 +        velecsum      += LFC[i]*velec[i];
 +        vvdwsum       += LFV[i]*vvdw[i];
 +
 +        fscal         += (LFC[i]*fscal_elec[i]+LFV[i]*fscal_vdw[i])*rpm2;
 +
 +        dvdl_coul     += velec[i]*DLF[i] + LFC[i]*alpha_coul_eff*dlfac_coul[i]*fscal_elec[i]*sigma_pow[i];
 +        dvdl_vdw      += vvdw[i]*DLF[i] + LFV[i]*alpha_vdw_eff*dlfac_vdw[i]*fscal_vdw[i]*sigma_pow[i];
 +    }
 +
 +    dvdl[efptCOUL]     += dvdl_coul;
 +    dvdl[efptVDW]      += dvdl_vdw;
 +
 +    *velectot           = velecsum;
 +    *vvdwtot            = vvdwsum;
 +
 +    return fscal;
 +}
index 2320022568a2275777cb86fb9173f94821f71d10,0000000000000000000000000000000000000000..07858aaafbf6259427fc5ddce5c2353a45e5fcd6
mode 100644,000000..100644
--- /dev/null
@@@ -1,266 -1,0 +1,272 @@@
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-  */
 +
 +/*
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2008, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "typedefs.h"
 +#include "topsort.h"
 +#include "smalloc.h"
 +#include "gmx_fatal.h"
 +
 +static gmx_bool ip_pert(int ftype, const t_iparams *ip)
 +{
 +    gmx_bool bPert;
 +    int      i;
 +
 +    if (NRFPB(ftype) == 0)
 +    {
 +        return FALSE;
 +    }
 +
 +    switch (ftype)
 +    {
 +        case F_BONDS:
 +        case F_G96BONDS:
 +        case F_HARMONIC:
 +        case F_ANGLES:
 +        case F_G96ANGLES:
 +        case F_IDIHS:
 +            bPert = (ip->harmonic.rA  != ip->harmonic.rB ||
 +                     ip->harmonic.krA != ip->harmonic.krB);
 +            break;
 +        case F_MORSE:
 +            bPert = (ip->morse.b0A  != ip->morse.b0B ||
 +                     ip->morse.cbA  != ip->morse.cbB ||
 +                     ip->morse.betaA  != ip->morse.betaB);
 +            break;
 +        case F_RESTRBONDS:
 +            bPert = (ip->restraint.lowA  != ip->restraint.lowB ||
 +                     ip->restraint.up1A  != ip->restraint.up1B ||
 +                     ip->restraint.up2A  != ip->restraint.up2B ||
 +                     ip->restraint.kA    != ip->restraint.kB);
 +            break;
++        case F_UREY_BRADLEY:
++            bPert = (ip->u_b.thetaA  != ip->u_b.thetaB  ||
++                     ip->u_b.kthetaA != ip->u_b.kthetaB ||
++                     ip->u_b.r13A    != ip->u_b.r13B    ||
++                     ip->u_b.kUBA    != ip->u_b.kUBB);
++            break;
 +        case F_PDIHS:
 +        case F_PIDIHS:
 +        case F_ANGRES:
 +        case F_ANGRESZ:
 +            bPert = (ip->pdihs.phiA != ip->pdihs.phiB ||
 +                     ip->pdihs.cpA  != ip->pdihs.cpB);
 +            break;
 +        case F_RBDIHS:
 +            bPert = FALSE;
 +            for (i = 0; i < NR_RBDIHS; i++)
 +            {
 +                if (ip->rbdihs.rbcA[i] != ip->rbdihs.rbcB[i])
 +                {
 +                    bPert = TRUE;
 +                }
 +            }
 +            break;
 +        case F_TABBONDS:
 +        case F_TABBONDSNC:
 +        case F_TABANGLES:
 +        case F_TABDIHS:
 +            bPert = (ip->tab.kA != ip->tab.kB);
 +            break;
 +        case F_POSRES:
 +            bPert = FALSE;
 +            for (i = 0; i < DIM; i++)
 +            {
 +                if (ip->posres.pos0A[i] != ip->posres.pos0B[i] ||
 +                    ip->posres.fcA[i]   != ip->posres.fcB[i])
 +                {
 +                    bPert = TRUE;
 +                }
 +            }
 +            break;
 +        case F_DIHRES:
 +            bPert = ((ip->dihres.phiA != ip->dihres.phiB) ||
 +                     (ip->dihres.dphiA != ip->dihres.dphiB) ||
 +                     (ip->dihres.kfacA != ip->dihres.kfacB));
 +            break;
 +        case F_LJ14:
 +            bPert = (ip->lj14.c6A  != ip->lj14.c6B ||
 +                     ip->lj14.c12A != ip->lj14.c12B);
 +            break;
 +        case F_CMAP:
 +            bPert = FALSE;
 +            break;
 +        default:
 +            bPert = FALSE;
 +            gmx_fatal(FARGS, "Function type %s not implemented in ip_pert",
 +                      interaction_function[ftype].longname);
 +    }
 +
 +    return bPert;
 +}
 +
 +static gmx_bool ip_q_pert(int ftype, const t_iatom *ia,
 +                          const t_iparams *ip, const real *qA, const real *qB)
 +{
 +    /* 1-4 interactions do not have the charges stored in the iparams list,
 +     * so we need a separate check for those.
 +     */
 +    return (ip_pert(ftype, ip+ia[0]) ||
 +            (ftype == F_LJ14 && (qA[ia[1]] != qB[ia[1]] ||
 +                                 qA[ia[2]] != qB[ia[2]])));
 +}
 +
 +gmx_bool gmx_mtop_bondeds_free_energy(const gmx_mtop_t *mtop)
 +{
 +    const gmx_ffparams_t *ffparams;
 +    int                   i, ftype;
 +    int                   mb;
 +    t_atom               *atom;
 +    t_ilist              *il;
 +    t_iatom              *ia;
 +    gmx_bool              bPert;
 +
 +    ffparams = &mtop->ffparams;
 +
 +    /* Loop over all the function types and compare the A/B parameters */
 +    bPert = FALSE;
 +    for (i = 0; i < ffparams->ntypes; i++)
 +    {
 +        ftype = ffparams->functype[i];
 +        if (interaction_function[ftype].flags & IF_BOND)
 +        {
 +            if (ip_pert(ftype, &ffparams->iparams[i]))
 +            {
 +                bPert = TRUE;
 +            }
 +        }
 +    }
 +
 +    /* Check perturbed charges for 1-4 interactions */
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        atom = mtop->moltype[mtop->molblock[mb].type].atoms.atom;
 +        il   = &mtop->moltype[mtop->molblock[mb].type].ilist[F_LJ14];
 +        ia   = il->iatoms;
 +        for (i = 0; i < il->nr; i += 3)
 +        {
 +            if (atom[ia[i+1]].q != atom[ia[i+1]].qB ||
 +                atom[ia[i+2]].q != atom[ia[i+2]].qB)
 +            {
 +                bPert = TRUE;
 +            }
 +        }
 +    }
 +
 +    return (bPert ? ilsortFE_UNSORTED : ilsortNO_FE);
 +}
 +
 +void gmx_sort_ilist_fe(t_idef *idef, const real *qA, const real *qB)
 +{
 +    int        ftype, nral, i, ic, ib, a;
 +    t_iparams *iparams;
 +    t_ilist   *ilist;
 +    t_iatom   *iatoms;
 +    gmx_bool   bPert;
 +    t_iatom   *iabuf;
 +    int        iabuf_nalloc;
 +
 +    if (qB == NULL)
 +    {
 +        qB = qA;
 +    }
 +
 +    iabuf_nalloc = 0;
 +    iabuf        = NULL;
 +
 +    iparams = idef->iparams;
 +
 +    for (ftype = 0; ftype < F_NRE; ftype++)
 +    {
 +        if (interaction_function[ftype].flags & IF_BOND)
 +        {
 +            ilist  = &idef->il[ftype];
 +            iatoms = ilist->iatoms;
 +            nral   = NRAL(ftype);
 +            ic     = 0;
 +            ib     = 0;
 +            i      = 0;
 +            while (i < ilist->nr)
 +            {
 +                /* Check if this interaction is perturbed */
 +                if (ip_q_pert(ftype, iatoms+i, iparams, qA, qB))
 +                {
 +                    /* Copy to the perturbed buffer */
 +                    if (ib + 1 + nral > iabuf_nalloc)
 +                    {
 +                        iabuf_nalloc = over_alloc_large(ib+1+nral);
 +                        srenew(iabuf, iabuf_nalloc);
 +                    }
 +                    for (a = 0; a < 1+nral; a++)
 +                    {
 +                        iabuf[ib++] = iatoms[i++];
 +                    }
 +                }
 +                else
 +                {
 +                    /* Copy in place */
 +                    for (a = 0; a < 1+nral; a++)
 +                    {
 +                        iatoms[ic++] = iatoms[i++];
 +                    }
 +                }
 +            }
 +            /* Now we now the number of non-perturbed interactions */
 +            ilist->nr_nonperturbed = ic;
 +
 +            /* Copy the buffer with perturbed interactions to the ilist */
 +            for (a = 0; a < ib; a++)
 +            {
 +                iatoms[ic++] = iabuf[a];
 +            }
 +
 +            if (debug)
 +            {
 +                fprintf(debug, "%s non-pert %d pert %d\n",
 +                        interaction_function[ftype].longname,
 +                        ilist->nr_nonperturbed,
 +                        ilist->nr-ilist->nr_nonperturbed);
 +            }
 +        }
 +    }
 +
 +    sfree(iabuf);
 +
 +    idef->ilsort = ilsortFE_SORTED;
 +}
index 91ec7de1d5adcb970d6820937e3ab1fc60d449e2,0000000000000000000000000000000000000000..46c9d001afc3186043098b441f66a1276d336a6a
mode 100644,000000..100644
--- /dev/null
@@@ -1,1111 -1,0 +1,1113 @@@
 +/*
 + *
 + *                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 forxd
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * GROningen Mixture of Alchemy and Childrens' Stories
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <ctype.h>
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "vmdio.h"
 +#include "string2.h"
 +#include "smalloc.h"
 +#include "pbc.h"
 +#include "statutil.h"
 +#include "gmxfio.h"
 +#include "trnio.h"
 +#include "names.h"
 +#include "vec.h"
 +#include "futil.h"
 +#include "gmxfio.h"
 +#include "xtcio.h"
 +#include "pdbio.h"
 +#include "confio.h"
 +#include "checkpoint.h"
 +#include "wgms.h"
 +#include <math.h>
 +
 +/* defines for frame counter output */
 +#define SKIP1   10
 +#define SKIP2  100
 +#define SKIP3 1000
 +
 +/* Globals for gromos-87 input */
 +typedef enum {
 +    effXYZ, effXYZBox, effG87, effG87Box, effNR
 +} eFileFormat;
 +
 +struct t_trxstatus
 +{
 +    int             __frame;
 +    t_trxframe     *xframe;
 +    int             nxframe;
 +    t_fileio       *fio;
 +    eFileFormat     eFF;
 +    int             NATOMS;
 +    double          DT, BOX[3];
 +    gmx_bool        bReadBox;
 +    char           *persistent_line; /* Persistent line for reading g96 trajectories */
 +};
 +
 +static void initcount(t_trxstatus *status)
 +{
 +    status->__frame = -1;
 +}
 +
 +static void status_init(t_trxstatus *status)
 +{
 +    status->nxframe         = 0;
 +    status->xframe          = NULL;
 +    status->fio             = NULL;
 +    status->__frame         = -1;
 +    status->persistent_line = NULL;
 +}
 +
 +
 +int nframes_read(t_trxstatus *status)
 +{
 +    return status->__frame;
 +}
 +
 +static void printcount_(t_trxstatus *status, const output_env_t oenv,
 +                        const char *l, real t)
 +{
 +    if ((status->__frame < 2*SKIP1 || status->__frame % SKIP1 == 0) &&
 +        (status->__frame < 2*SKIP2 || status->__frame % SKIP2 == 0) &&
 +        (status->__frame < 2*SKIP3 || status->__frame % SKIP3 == 0))
 +    {
 +        fprintf(stderr, "\r%-14s %6d time %8.3f   ", l, status->__frame,
 +                output_env_conv_time(oenv, t));
 +    }
 +}
 +
 +static void printcount(t_trxstatus *status, const output_env_t oenv, real t,
 +                       gmx_bool bSkip)
 +{
 +    status->__frame++;
 +    printcount_(status, oenv, bSkip ? "Skipping frame" : "Reading frame", t);
 +}
 +
 +static void printlast(t_trxstatus *status, const output_env_t oenv, real t)
 +{
 +    printcount_(status, oenv, "Last frame", t);
 +    fprintf(stderr, "\n");
 +}
 +
 +static void printincomp(t_trxstatus *status, t_trxframe *fr)
 +{
 +    if (fr->not_ok & HEADER_NOT_OK)
 +    {
 +        fprintf(stderr, "WARNING: Incomplete header: nr %d time %g\n",
 +                status->__frame+1, fr->time);
 +    }
 +    else if (fr->not_ok)
 +    {
 +        fprintf(stderr, "WARNING: Incomplete frame: nr %d time %g\n",
 +                status->__frame+1, fr->time);
 +    }
 +}
 +
 +int prec2ndec(real prec)
 +{
 +    if (prec <= 0)
 +    {
 +        gmx_fatal(FARGS, "DEATH HORROR prec (%g) <= 0 in prec2ndec", prec);
 +    }
 +
 +    return (int)(log(prec)/log(10.0)+0.5);
 +}
 +
 +
 +t_fileio *trx_get_fileio(t_trxstatus *status)
 +{
 +    return status->fio;
 +}
 +
 +
 +
 +void clear_trxframe(t_trxframe *fr, gmx_bool bFirst)
 +{
 +    fr->not_ok  = 0;
 +    fr->bTitle  = FALSE;
 +    fr->bStep   = FALSE;
 +    fr->bTime   = FALSE;
 +    fr->bLambda = FALSE;
++    fr->bFepState = FALSE;
 +    fr->bAtoms  = FALSE;
 +    fr->bPrec   = FALSE;
 +    fr->bX      = FALSE;
 +    fr->bV      = FALSE;
 +    fr->bF      = FALSE;
 +    fr->bBox    = FALSE;
 +    if (bFirst)
 +    {
 +        fr->flags   = 0;
 +        fr->bDouble = FALSE;
 +        fr->natoms  = -1;
 +        fr->t0      = 0;
 +        fr->tpf     = 0;
 +        fr->tppf    = 0;
 +        fr->title   = NULL;
 +        fr->step    = 0;
 +        fr->time    = 0;
 +        fr->lambda  = 0;
++        fr->fep_state = 0;
 +        fr->atoms   = NULL;
 +        fr->prec    = 0;
 +        fr->x       = NULL;
 +        fr->v       = NULL;
 +        fr->f       = NULL;
 +        clear_mat(fr->box);
 +        fr->bPBC   = FALSE;
 +        fr->ePBC   = -1;
 +    }
 +}
 +
 +void set_trxframe_ePBC(t_trxframe *fr, int ePBC)
 +{
 +    fr->bPBC = (ePBC == -1);
 +    fr->ePBC = ePBC;
 +}
 +
 +int write_trxframe_indexed(t_trxstatus *status, t_trxframe *fr, int nind,
 +                           const atom_id *ind, gmx_conect gc)
 +{
 +    char  title[STRLEN];
 +    rvec *xout = NULL, *vout = NULL, *fout = NULL;
 +    int   i;
 +    real  prec;
 +
 +    if (fr->bPrec)
 +    {
 +        prec = fr->prec;
 +    }
 +    else
 +    {
 +        prec = 1000.0;
 +    }
 +
 +    switch (gmx_fio_getftp(status->fio))
 +    {
 +        case efTRJ:
 +        case efTRR:
 +            break;
 +        default:
 +            if (!fr->bX)
 +            {
 +                gmx_fatal(FARGS, "Need coordinates to write a %s trajectory",
 +                          ftp2ext(gmx_fio_getftp(status->fio)));
 +            }
 +            break;
 +    }
 +
 +    switch (gmx_fio_getftp(status->fio))
 +    {
 +        case efTRJ:
 +        case efTRR:
 +            if (fr->bV)
 +            {
 +                snew(vout, nind);
 +                for (i = 0; i < nind; i++)
 +                {
 +                    copy_rvec(fr->v[ind[i]], vout[i]);
 +                }
 +            }
 +            if (fr->bF)
 +            {
 +                snew(fout, nind);
 +                for (i = 0; i < nind; i++)
 +                {
 +                    copy_rvec(fr->f[ind[i]], fout[i]);
 +                }
 +            }
 +        /* no break */
 +        case efXTC:
 +        case efG87:
 +            if (fr->bX)
 +            {
 +                snew(xout, nind);
 +                for (i = 0; i < nind; i++)
 +                {
 +                    copy_rvec(fr->x[ind[i]], xout[i]);
 +                }
 +            }
 +            break;
 +        default:
 +            break;
 +    }
 +
 +    switch (gmx_fio_getftp(status->fio))
 +    {
 +        case efXTC:
 +            write_xtc(status->fio, nind, fr->step, fr->time, fr->box, xout, prec);
 +            break;
 +        case efTRJ:
 +        case efTRR:
 +            fwrite_trn(status->fio, nframes_read(status),
 +                       fr->time, fr->step, fr->box, nind, xout, vout, fout);
 +            break;
 +        case efGRO:
 +        case efPDB:
 +        case efBRK:
 +        case efENT:
 +            if (!fr->bAtoms)
 +            {
 +                gmx_fatal(FARGS, "Can not write a %s file without atom names",
 +                          ftp2ext(gmx_fio_getftp(status->fio)));
 +            }
 +            sprintf(title, "frame t= %.3f", fr->time);
 +            if (gmx_fio_getftp(status->fio) == efGRO)
 +            {
 +                write_hconf_indexed_p(gmx_fio_getfp(status->fio), title, fr->atoms, nind, ind,
 +                                      prec2ndec(prec),
 +                                      fr->x, fr->bV ? fr->v : NULL, fr->box);
 +            }
 +            else
 +            {
 +                write_pdbfile_indexed(gmx_fio_getfp(status->fio), title, fr->atoms,
 +                                      fr->x, -1, fr->box, ' ', fr->step, nind, ind, gc, TRUE);
 +            }
 +            break;
 +        case efG87:
 +            write_gms(gmx_fio_getfp(status->fio), nind, xout, fr->box);
 +            break;
 +        case efG96:
 +            write_g96_conf(gmx_fio_getfp(status->fio), fr, nind, ind);
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "Sorry, write_trxframe_indexed can not write %s",
 +                      ftp2ext(gmx_fio_getftp(status->fio)));
 +            break;
 +    }
 +
 +    switch (gmx_fio_getftp(status->fio))
 +    {
 +        case efTRN:
 +        case efTRJ:
 +        case efTRR:
 +            if (vout)
 +            {
 +                sfree(vout);
 +            }
 +            if (fout)
 +            {
 +                sfree(fout);
 +            }
 +        /* no break */
 +        case efXTC:
 +        case efG87:
 +            sfree(xout);
 +            break;
 +        default:
 +            break;
 +    }
 +
 +    return 0;
 +}
 +
 +int write_trxframe(t_trxstatus *status, t_trxframe *fr, gmx_conect gc)
 +{
 +    char title[STRLEN];
 +    real prec;
 +
 +    if (fr->bPrec)
 +    {
 +        prec = fr->prec;
 +    }
 +    else
 +    {
 +        prec = 1000.0;
 +    }
 +
 +    switch (gmx_fio_getftp(status->fio))
 +    {
 +        case efTRJ:
 +        case efTRR:
 +            break;
 +        default:
 +            if (!fr->bX)
 +            {
 +                gmx_fatal(FARGS, "Need coordinates to write a %s trajectory",
 +                          ftp2ext(gmx_fio_getftp(status->fio)));
 +            }
 +            break;
 +    }
 +
 +    switch (gmx_fio_getftp(status->fio))
 +    {
 +        case efXTC:
 +            write_xtc(status->fio, fr->natoms, fr->step, fr->time, fr->box, fr->x, prec);
 +            break;
 +        case efTRJ:
 +        case efTRR:
 +            fwrite_trn(status->fio, fr->step, fr->time, fr->lambda, fr->box, fr->natoms,
 +                       fr->bX ? fr->x : NULL, fr->bV ? fr->v : NULL, fr->bF ? fr->f : NULL);
 +            break;
 +        case efGRO:
 +        case efPDB:
 +        case efBRK:
 +        case efENT:
 +            if (!fr->bAtoms)
 +            {
 +                gmx_fatal(FARGS, "Can not write a %s file without atom names",
 +                          ftp2ext(gmx_fio_getftp(status->fio)));
 +            }
 +            sprintf(title, "frame t= %.3f", fr->time);
 +            if (gmx_fio_getftp(status->fio) == efGRO)
 +            {
 +                write_hconf_p(gmx_fio_getfp(status->fio), title, fr->atoms,
 +                              prec2ndec(prec), fr->x, fr->bV ? fr->v : NULL, fr->box);
 +            }
 +            else
 +            {
 +                write_pdbfile(gmx_fio_getfp(status->fio), title,
 +                              fr->atoms, fr->x, fr->bPBC ? fr->ePBC : -1, fr->box,
 +                              ' ', fr->step, gc, TRUE);
 +            }
 +            break;
 +        case efG87:
 +            write_gms(gmx_fio_getfp(status->fio), fr->natoms, fr->x, fr->box);
 +            break;
 +        case efG96:
 +            write_g96_conf(gmx_fio_getfp(status->fio), fr, -1, NULL);
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "Sorry, write_trxframe can not write %s",
 +                      ftp2ext(gmx_fio_getftp(status->fio)));
 +            break;
 +    }
 +
 +    return 0;
 +}
 +
 +int write_trx(t_trxstatus *status, int nind, const atom_id *ind, t_atoms *atoms,
 +              int step, real time, matrix box, rvec x[], rvec *v,
 +              gmx_conect gc)
 +{
 +    t_trxframe fr;
 +
 +    clear_trxframe(&fr, TRUE);
 +    fr.bStep  = TRUE;
 +    fr.step   = step;
 +    fr.bTime  = TRUE;
 +    fr.time   = time;
 +    fr.bAtoms = atoms != NULL;
 +    fr.atoms  = atoms;
 +    fr.bX     = TRUE;
 +    fr.x      = x;
 +    fr.bV     = v != NULL;
 +    fr.v      = v;
 +    fr.bBox   = TRUE;
 +    copy_mat(box, fr.box);
 +
 +    return write_trxframe_indexed(status, &fr, nind, ind, gc);
 +}
 +
 +void close_trx(t_trxstatus *status)
 +{
 +    gmx_fio_close(status->fio);
 +    sfree(status);
 +}
 +
 +t_trxstatus *open_trx(const char *outfile, const char *filemode)
 +{
 +    t_trxstatus *stat;
 +    if (filemode[0] != 'w' && filemode[0] != 'a' && filemode[1] != '+')
 +    {
 +        gmx_fatal(FARGS, "Sorry, write_trx can only write");
 +    }
 +
 +    snew(stat, 1);
 +    status_init(stat);
 +
 +    stat->fio = gmx_fio_open(outfile, filemode);
 +    return stat;
 +}
 +
 +static gmx_bool gmx_next_frame(t_trxstatus *status, t_trxframe *fr)
 +{
 +    t_trnheader sh;
 +    gmx_bool    bOK, bRet;
 +
 +    bRet = FALSE;
 +
 +    if (fread_trnheader(status->fio, &sh, &bOK))
 +    {
 +        fr->bDouble   = sh.bDouble;
 +        fr->natoms    = sh.natoms;
 +        fr->bStep     = TRUE;
 +        fr->step      = sh.step;
 +        fr->bTime     = TRUE;
 +        fr->time      = sh.t;
 +        fr->bLambda   = TRUE;
 +        fr->bFepState = TRUE;
 +        fr->lambda    = sh.lambda;
 +        fr->bBox      = sh.box_size > 0;
 +        if (fr->flags & (TRX_READ_X | TRX_NEED_X))
 +        {
 +            if (fr->x == NULL)
 +            {
 +                snew(fr->x, sh.natoms);
 +            }
 +            fr->bX = sh.x_size > 0;
 +        }
 +        if (fr->flags & (TRX_READ_V | TRX_NEED_V))
 +        {
 +            if (fr->v == NULL)
 +            {
 +                snew(fr->v, sh.natoms);
 +            }
 +            fr->bV = sh.v_size > 0;
 +        }
 +        if (fr->flags & (TRX_READ_F | TRX_NEED_F))
 +        {
 +            if (fr->f == NULL)
 +            {
 +                snew(fr->f, sh.natoms);
 +            }
 +            fr->bF = sh.f_size > 0;
 +        }
 +        if (fread_htrn(status->fio, &sh, fr->box, fr->x, fr->v, fr->f))
 +        {
 +            bRet = TRUE;
 +        }
 +        else
 +        {
 +            fr->not_ok = DATA_NOT_OK;
 +        }
 +    }
 +    else
 +    if (!bOK)
 +    {
 +        fr->not_ok = HEADER_NOT_OK;
 +    }
 +
 +    return bRet;
 +}
 +
 +static void choose_file_format(FILE *fp)
 +{
 +    int          i, m, c;
 +    int          rc;
 +    eFileFormat  eFF;
 +    t_trxstatus *stat;
 +
 +    printf("\n\n");
 +    printf("   Select File Format\n");
 +    printf("---------------------------\n");
 +    printf("1. XYZ File\n");
 +    printf("2. XYZ File with Box\n");
 +    printf("3. Gromos-87 Ascii Trajectory\n");
 +    printf("4. Gromos-87 Ascii Trajectory with Box\n");
 +
 +    snew(stat, 1);
 +    status_init(stat);
 +
 +    do
 +    {
 +        printf("\nChoice: ");
 +        fflush(stdout);
 +        do
 +        {
 +            rc = scanf("%d", &i);
 +        }
 +        while (rc != 1);
 +        i--;
 +    }
 +    while ((i < 0) || (i >= effNR));
 +    printf("\n");
 +
 +    stat->eFF = (eFileFormat) i;
 +
 +    for (m = 0; (m < DIM); m++)
 +    {
 +        stat->BOX[m] = 0;
 +    }
 +
 +    stat->bReadBox = (stat->eFF == effG87Box) || (stat->eFF == effXYZBox);
 +
 +    switch (stat->eFF)
 +    {
 +        case effXYZ:
 +        case effXYZBox:
 +            if (5 != fscanf(fp, "%d%lf%lf%lf%lf", &stat->NATOMS, &stat->BOX[XX], &stat->BOX[YY], &stat->BOX[ZZ], &stat->DT))
 +            {
 +                gmx_fatal(FARGS, "Error reading natoms/box in file");
 +            }
 +            break;
 +        case effG87:
 +        case effG87Box:
 +            printf("GROMOS! OH DEAR...\n\n");
 +            printf("Number of atoms ? ");
 +            fflush(stdout);
 +            if (1 != scanf("%d", &stat->NATOMS))
 +            {
 +                gmx_fatal(FARGS, "Error reading natoms in file");
 +            }
 +
 +            printf("Time between timeframes ? ");
 +            fflush(stdout);
 +            if (1 != scanf("%lf", &stat->DT))
 +            {
 +                gmx_fatal(FARGS, "Error reading dt from file");
 +            }
 +
 +            if (stat->eFF == effG87)
 +            {
 +                printf("Box X Y Z ? ");
 +                fflush(stdout);
 +                if (3 != scanf("%lf%lf%lf", &stat->BOX[XX], &stat->BOX[YY], &stat->BOX[ZZ]))
 +                {
 +                    gmx_fatal(FARGS, "Error reading box in file");
 +                }
 +            }
 +            do
 +            {
 +                c = fgetc(fp);
 +                printf("%c", c);
 +            }
 +            while (c != '\n');
 +            printf("\n");
 +            fflush(stdout);
 +            break;
 +        default:
 +            printf("Hellow World\n");
 +    }
 +}
 +
 +static gmx_bool do_read_xyz(t_trxstatus *status, FILE *fp, int natoms,
 +                            rvec x[], matrix box)
 +{
 +    int    i, m;
 +    double x0;
 +
 +    for (i = 0; (i < natoms); i++)
 +    {
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            if (fscanf(fp, "%lf", &x0) != 1)
 +            {
 +                if (i || m)
 +                {
 +                    fprintf(stderr, "error reading statusfile: x[%d][%d]\n", i, m);
 +                }
 +                /* else eof! */
 +                return FALSE;
 +            }
 +            x[i][m] = x0;
 +        }
 +    }
 +    if (status->bReadBox)
 +    {
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            if (fscanf(fp, "%lf", &x0) != 1)
 +            {
 +                return FALSE;
 +            }
 +            box[m][m] = x0;
 +        }
 +    }
 +    return TRUE;
 +}
 +
 +static gmx_bool xyz_next_x(t_trxstatus *status, FILE *fp, const output_env_t oenv,
 +                           real *t, int natoms, rvec x[], matrix box)
 +/* Reads until a new x can be found (return TRUE)
 + * or eof (return FALSE)
 + */
 +{
 +    real pt;
 +
 +    pt = *t;
 +    while (!bTimeSet(TBEGIN) || (*t < rTimeValue(TBEGIN)))
 +    {
 +        if (!do_read_xyz(status, fp, natoms, x, box))
 +        {
 +            return FALSE;
 +        }
 +        printcount(status, oenv, *t, FALSE);
 +        *t += status->DT;
 +        pt  = *t;
 +    }
 +    if (!bTimeSet(TEND) || (*t <= rTimeValue(TEND)))
 +    {
 +        if (!do_read_xyz(status, fp, natoms, x, box))
 +        {
 +            printlast(status, oenv, *t);
 +            return FALSE;
 +        }
 +        printcount(status, oenv, *t, FALSE);
 +        pt  = *t;
 +        *t += status->DT;
 +        return TRUE;
 +    }
 +    printlast(status, oenv, pt);
 +    return FALSE;
 +}
 +
 +static int xyz_first_x(t_trxstatus *status, FILE *fp, const output_env_t oenv,
 +                       real *t, rvec **x, matrix box)
 +/* Reads fp, mallocs x, and returns x and box
 + * Returns natoms when successful, FALSE otherwise
 + */
 +{
 +    int    m;
 +
 +    initcount(status);
 +
 +    clear_mat(box);
 +    choose_file_format(fp);
 +
 +    for (m = 0; (m < DIM); m++)
 +    {
 +        box[m][m] = status->BOX[m];
 +    }
 +
 +    snew(*x, status->NATOMS);
 +    *t = status->DT;
 +    if (!xyz_next_x(status, fp, oenv, t, status->NATOMS, *x, box))
 +    {
 +        return 0;
 +    }
 +    *t = 0.0;
 +
 +    return status->NATOMS;
 +}
 +
 +static gmx_bool pdb_next_x(t_trxstatus *status, FILE *fp, t_trxframe *fr)
 +{
 +    t_atoms   atoms;
 +    matrix    boxpdb;
 +    int       ePBC, model_nr, na;
 +    char      title[STRLEN], *time;
 +    double    dbl;
 +
 +    atoms.nr      = fr->natoms;
 +    atoms.atom    = NULL;
 +    atoms.pdbinfo = NULL;
 +    /* the other pointers in atoms should not be accessed if these are NULL */
 +    model_nr = NOTSET;
 +    na       = read_pdbfile(fp, title, &model_nr, &atoms, fr->x, &ePBC, boxpdb, TRUE, NULL);
 +    set_trxframe_ePBC(fr, ePBC);
 +    if (nframes_read(status) == 0)
 +    {
 +        fprintf(stderr, " '%s', %d atoms\n", title, fr->natoms);
 +    }
 +    fr->bPrec = TRUE;
 +    fr->prec  = 10000;
 +    fr->bX    = TRUE;
 +    fr->bBox  = (boxpdb[XX][XX] != 0.0);
 +    if (fr->bBox)
 +    {
 +        copy_mat(boxpdb, fr->box);
 +    }
 +
 +    if (model_nr != NOTSET)
 +    {
 +        fr->bStep = TRUE;
 +        fr->step  = model_nr;
 +    }
 +    time = strstr(title, " t= ");
 +    if (time)
 +    {
 +        fr->bTime = TRUE;
 +        sscanf(time+4, "%lf", &dbl);
 +        fr->time = (real)dbl;
 +    }
 +    else
 +    {
 +        fr->bTime = FALSE;
 +        /* this is a bit dirty, but it will work: if no time is read from
 +           comment line in pdb file, set time to current frame number */
 +        if (fr->bStep)
 +        {
 +            fr->time = (real)fr->step;
 +        }
 +        else
 +        {
 +            fr->time = (real)nframes_read(status);
 +        }
 +    }
 +    if (na == 0)
 +    {
 +        return FALSE;
 +    }
 +    else
 +    {
 +        if (na != fr->natoms)
 +        {
 +            gmx_fatal(FARGS, "Number of atoms in pdb frame %d is %d instead of %d",
 +                      nframes_read(status), na, fr->natoms);
 +        }
 +        return TRUE;
 +    }
 +}
 +
 +static int pdb_first_x(t_trxstatus *status, FILE *fp, t_trxframe *fr)
 +{
 +    initcount(status);
 +
 +    fprintf(stderr, "Reading frames from pdb file");
 +    frewind(fp);
 +    get_pdb_coordnum(fp, &fr->natoms);
 +    if (fr->natoms == 0)
 +    {
 +        gmx_fatal(FARGS, "\nNo coordinates in pdb file\n");
 +    }
 +    frewind(fp);
 +    snew(fr->x, fr->natoms);
 +    pdb_next_x(status, fp, fr);
 +
 +    return fr->natoms;
 +}
 +
 +gmx_bool read_next_frame(const output_env_t oenv, t_trxstatus *status, t_trxframe *fr)
 +{
 +    real     pt;
 +    int      ct;
 +    gmx_bool bOK, bRet, bMissingData = FALSE, bSkip = FALSE;
 +    int      dummy = 0;
 +
 +    bRet = FALSE;
 +    pt   = fr->time;
 +
 +    do
 +    {
 +        clear_trxframe(fr, FALSE);
 +        fr->tppf = fr->tpf;
 +        fr->tpf  = fr->time;
 +
 +        switch (gmx_fio_getftp(status->fio))
 +        {
 +            case efTRJ:
 +            case efTRR:
 +                bRet = gmx_next_frame(status, fr);
 +                break;
 +            case efCPT:
 +                /* Checkpoint files can not contain mulitple frames */
 +                break;
 +            case efG96:
 +                read_g96_conf(gmx_fio_getfp(status->fio), NULL, fr,
 +                              status->persistent_line);
 +                bRet = (fr->natoms > 0);
 +                break;
 +            case efG87:
 +                bRet = xyz_next_x(status, gmx_fio_getfp(status->fio), oenv, &fr->time,
 +                                  fr->natoms, fr->x, fr->box);
 +                fr->bTime = bRet;
 +                fr->bX    = bRet;
 +                fr->bBox  = bRet;
 +                break;
 +            case efXTC:
 +                /* B. Hess 2005-4-20
 +                 * Sometimes is off by one frame
 +                 * and sometimes reports frame not present/file not seekable
 +                 */
 +                /* DvdS 2005-05-31: this has been fixed along with the increased
 +                 * accuracy of the control over -b and -e options.
 +                 */
 +                if (bTimeSet(TBEGIN) && (fr->time < rTimeValue(TBEGIN)))
 +                {
 +                    if (xtc_seek_time(status->fio, rTimeValue(TBEGIN), fr->natoms, TRUE))
 +                    {
 +                        gmx_fatal(FARGS, "Specified frame (time %f) doesn't exist or file corrupt/inconsistent.",
 +                                  rTimeValue(TBEGIN));
 +                    }
 +                    initcount(status);
 +                }
 +                bRet = read_next_xtc(status->fio, fr->natoms, &fr->step, &fr->time, fr->box,
 +                                     fr->x, &fr->prec, &bOK);
 +                fr->bPrec = (bRet && fr->prec > 0);
 +                fr->bStep = bRet;
 +                fr->bTime = bRet;
 +                fr->bX    = bRet;
 +                fr->bBox  = bRet;
 +                if (!bOK)
 +                {
 +                    /* Actually the header could also be not ok,
 +                       but from bOK from read_next_xtc this can't be distinguished */
 +                    fr->not_ok = DATA_NOT_OK;
 +                }
 +                break;
 +            case efPDB:
 +                bRet = pdb_next_x(status, gmx_fio_getfp(status->fio), fr);
 +                break;
 +            case efGRO:
 +                bRet = gro_next_x_or_v(gmx_fio_getfp(status->fio), fr);
 +                break;
 +            default:
 +#ifdef GMX_USE_PLUGINS
 +                bRet = read_next_vmd_frame(dummy, fr);
 +#else
 +                gmx_fatal(FARGS, "DEATH HORROR in read_next_frame ftp=%s,status=%s",
 +                          ftp2ext(gmx_fio_getftp(status->fio)),
 +                          gmx_fio_getname(status->fio));
 +#endif
 +        }
 +
 +        if (bRet)
 +        {
 +            bMissingData = (((fr->flags & TRX_NEED_X) && !fr->bX) ||
 +                            ((fr->flags & TRX_NEED_V) && !fr->bV) ||
 +                            ((fr->flags & TRX_NEED_F) && !fr->bF));
 +            bSkip = FALSE;
 +            if (!bMissingData)
 +            {
 +                ct = check_times2(fr->time, fr->t0, fr->tpf, fr->tppf, fr->bDouble);
 +                if (ct == 0 || ((fr->flags & TRX_DONT_SKIP) && ct < 0))
 +                {
 +                    printcount(status, oenv, fr->time, FALSE);
 +                }
 +                else if (ct > 0)
 +                {
 +                    bRet = FALSE;
 +                }
 +                else
 +                {
 +                    printcount(status, oenv, fr->time, TRUE);
 +                    bSkip = TRUE;
 +                }
 +            }
 +        }
 +
 +    }
 +    while (bRet && (bMissingData || bSkip));
 +
 +    if (!bRet)
 +    {
 +        printlast(status, oenv, pt);
 +        if (fr->not_ok)
 +        {
 +            printincomp(status, fr);
 +        }
 +    }
 +
 +    return bRet;
 +}
 +
 +int read_first_frame(const output_env_t oenv, t_trxstatus **status,
 +                     const char *fn, t_trxframe *fr, int flags)
 +{
 +    t_fileio *fio;
 +    gmx_bool  bFirst, bOK;
 +    int       dummy = 0;
 +
 +    clear_trxframe(fr, TRUE);
 +    fr->flags = flags;
 +
 +    bFirst = TRUE;
 +
 +    snew((*status), 1);
 +
 +    status_init( *status );
 +    (*status)->nxframe = 1;
 +    initcount(*status);
 +
 +    fio = (*status)->fio = gmx_fio_open(fn, "r");
 +    switch (gmx_fio_getftp(fio))
 +    {
 +        case efTRJ:
 +        case efTRR:
 +            break;
 +        case efCPT:
 +            read_checkpoint_trxframe(fio, fr);
 +            bFirst = FALSE;
 +            break;
 +        case efG96:
 +            /* Can not rewind a compressed file, so open it twice */
 +            if (!(*status)->persistent_line)
 +            {
 +                /* allocate the persistent line */
 +                snew((*status)->persistent_line, STRLEN+1);
 +            }
 +            read_g96_conf(gmx_fio_getfp(fio), fn, fr, (*status)->persistent_line);
 +            gmx_fio_close(fio);
 +            clear_trxframe(fr, FALSE);
 +            if (flags & (TRX_READ_X | TRX_NEED_X))
 +            {
 +                snew(fr->x, fr->natoms);
 +            }
 +            if (flags & (TRX_READ_V | TRX_NEED_V))
 +            {
 +                snew(fr->v, fr->natoms);
 +            }
 +            fio = (*status)->fio = gmx_fio_open(fn, "r");
 +            break;
 +        case efG87:
 +            fr->natoms = xyz_first_x(*status, gmx_fio_getfp(fio), oenv, &fr->time,
 +                                     &fr->x, fr->box);
 +            if (fr->natoms)
 +            {
 +                fr->bTime = TRUE;
 +                fr->bX    = TRUE;
 +                fr->bBox  = TRUE;
 +                printcount(*status, oenv, fr->time, FALSE);
 +            }
 +            bFirst = FALSE;
 +            break;
 +        case efXTC:
 +            if (read_first_xtc(fio, &fr->natoms, &fr->step, &fr->time, fr->box, &fr->x,
 +                               &fr->prec, &bOK) == 0)
 +            {
 +                if (bOK)
 +                {
 +                    gmx_fatal(FARGS, "No XTC!\n");
 +                }
 +                else
 +                {
 +                    fr->not_ok = DATA_NOT_OK;
 +                }
 +            }
 +            if (fr->not_ok)
 +            {
 +                fr->natoms = 0;
 +                printincomp(*status, fr);
 +            }
 +            else
 +            {
 +                fr->bPrec = (fr->prec > 0);
 +                fr->bStep = TRUE;
 +                fr->bTime = TRUE;
 +                fr->bX    = TRUE;
 +                fr->bBox  = TRUE;
 +                printcount(*status, oenv, fr->time, FALSE);
 +            }
 +            bFirst = FALSE;
 +            break;
 +        case efPDB:
 +            pdb_first_x(*status, gmx_fio_getfp(fio), fr);
 +            if (fr->natoms)
 +            {
 +                printcount(*status, oenv, fr->time, FALSE);
 +            }
 +            bFirst = FALSE;
 +            break;
 +        case efGRO:
 +            if (gro_first_x_or_v(gmx_fio_getfp(fio), fr))
 +            {
 +                printcount(*status, oenv, fr->time, FALSE);
 +            }
 +            bFirst = FALSE;
 +            break;
 +        default:
 +#ifdef GMX_USE_PLUGINS
 +            fprintf(stderr, "The file format of %s is not a known trajectory format to GROMACS.\n"
 +                    "Please make sure that the file is a trajectory!\n"
 +                    "GROMACS will now assume it to be a trajectory and will try to open it using the VMD plug-ins.\n"
 +                    "This will only work in case the VMD plugins are found and it is a trajectory format supported by VMD.\n", fn);
 +            gmx_fio_fp_close(fio); /*only close the file without removing FIO entry*/
 +            if (!read_first_vmd_frame(&dummy, fn, fr, flags))
 +            {
 +                gmx_fatal(FARGS, "Not supported in read_first_frame: %s", fn);
 +            }
 +#else
 +            gmx_fatal(FARGS, "Not supported in read_first_frame: %s. Please make sure that the file is a trajectory.\n"
 +                      "GROMACS is not compiled with plug-in support. Thus it cannot read non-GROMACS trajectory formats using the VMD plug-ins.\n"
 +                      "Please compile with plug-in support if you want to read non-GROMACS trajectory formats.\n", fn);
 +#endif
 +            break;
 +    }
 +
 +    /* Return FALSE if we read a frame that's past the set ending time. */
 +    if (!bFirst && (!(fr->flags & TRX_DONT_SKIP) && check_times(fr->time) > 0))
 +    {
 +        fr->t0 = fr->time;
 +        return FALSE;
 +    }
 +
 +    if (bFirst ||
 +        (!(fr->flags & TRX_DONT_SKIP) && check_times(fr->time) < 0))
 +    {
 +        /* Read a frame when no frame was read or the first was skipped */
 +        if (!read_next_frame(oenv, *status, fr))
 +        {
 +            return FALSE;
 +        }
 +    }
 +    fr->t0 = fr->time;
 +
 +    return (fr->natoms > 0);
 +}
 +
 +/***** C O O R D I N A T E   S T U F F *****/
 +
 +int read_first_x(const output_env_t oenv, t_trxstatus **status, const char *fn,
 +                 real *t, rvec **x, matrix box)
 +{
 +    t_trxframe fr;
 +
 +    read_first_frame(oenv, status, fn, &fr, TRX_NEED_X);
 +
 +    snew((*status)->xframe, 1);
 +    (*status)->nxframe   = 1;
 +    (*(*status)->xframe) = fr;
 +    *t                   = (*status)->xframe->time;
 +    *x                   = (*status)->xframe->x;
 +    copy_mat((*status)->xframe->box, box);
 +
 +    return (*status)->xframe->natoms;
 +}
 +
 +gmx_bool read_next_x(const output_env_t oenv, t_trxstatus *status, real *t,
 +                     int natoms, rvec x[], matrix box)
 +{
 +    gmx_bool bRet;
 +
 +    status->xframe->x = x;
 +    /*xframe[status].x = x;*/
 +    bRet = read_next_frame(oenv, status, status->xframe);
 +    *t   = status->xframe->time;
 +    copy_mat(status->xframe->box, box);
 +
 +    return bRet;
 +}
 +
 +void close_trj(t_trxstatus *status)
 +{
 +    gmx_fio_close(status->fio);
 +    /* The memory in status->xframe is lost here,
 +     * but the read_first_x/read_next_x functions are deprecated anyhow.
 +     * read_first_frame/read_next_frame and close_trx should be used.
 +     */
 +    sfree(status);
 +}
 +
 +void rewind_trj(t_trxstatus *status)
 +{
 +    initcount(status);
 +
 +    gmx_fio_rewind(status->fio);
 +}
index 4ec13d321e3ad13183aeef39fe667d25ef7108de,0000000000000000000000000000000000000000..323309366d8fa49f5ff71197bd9e896804d59313
mode 100644,000000..100644
--- /dev/null
@@@ -1,3866 -1,0 +1,3867 @@@
-                 rvdw1 + rvdw2 > ir->rlist - ir->rvdw)
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <ctype.h>
 +#include <stdlib.h>
 +#include <limits.h>
 +#include "sysstuff.h"
 +#include "smalloc.h"
 +#include "typedefs.h"
 +#include "physics.h"
 +#include "names.h"
 +#include "gmx_fatal.h"
 +#include "macros.h"
 +#include "index.h"
 +#include "symtab.h"
 +#include "string2.h"
 +#include "readinp.h"
 +#include "warninp.h"
 +#include "readir.h"
 +#include "toputil.h"
 +#include "index.h"
 +#include "network.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "mtop_util.h"
 +#include "chargegroup.h"
 +#include "inputrec.h"
 +
 +#define MAXPTR 254
 +#define NOGID  255
 +#define MAXLAMBDAS 1024
 +
 +/* Resource parameters
 + * Do not change any of these until you read the instruction
 + * in readinp.h. Some cpp's do not take spaces after the backslash
 + * (like the c-shell), which will give you a very weird compiler
 + * message.
 + */
 +
 +static char tcgrps[STRLEN], tau_t[STRLEN], ref_t[STRLEN],
 +            acc[STRLEN], accgrps[STRLEN], freeze[STRLEN], frdim[STRLEN],
 +            energy[STRLEN], user1[STRLEN], user2[STRLEN], vcm[STRLEN], xtc_grps[STRLEN],
 +            couple_moltype[STRLEN], orirefitgrp[STRLEN], egptable[STRLEN], egpexcl[STRLEN],
 +            wall_atomtype[STRLEN], wall_density[STRLEN], deform[STRLEN], QMMM[STRLEN];
 +static char   fep_lambda[efptNR][STRLEN];
 +static char   lambda_weights[STRLEN];
 +static char **pull_grp;
 +static char **rot_grp;
 +static char   anneal[STRLEN], anneal_npoints[STRLEN],
 +              anneal_time[STRLEN], anneal_temp[STRLEN];
 +static char   QMmethod[STRLEN], QMbasis[STRLEN], QMcharge[STRLEN], QMmult[STRLEN],
 +              bSH[STRLEN], CASorbitals[STRLEN], CASelectrons[STRLEN], SAon[STRLEN],
 +              SAoff[STRLEN], SAsteps[STRLEN], bTS[STRLEN], bOPT[STRLEN];
 +static char efield_x[STRLEN], efield_xt[STRLEN], efield_y[STRLEN],
 +            efield_yt[STRLEN], efield_z[STRLEN], efield_zt[STRLEN];
 +
 +enum {
 +    egrptpALL,         /* All particles have to be a member of a group.     */
 +    egrptpALL_GENREST, /* A rest group with name is generated for particles *
 +                        * that are not part of any group.                   */
 +    egrptpPART,        /* As egrptpALL_GENREST, but no name is generated    *
 +                        * for the rest group.                               */
 +    egrptpONE          /* Merge all selected groups into one group,         *
 +                        * make a rest group for the remaining particles.    */
 +};
 +
 +
 +void init_ir(t_inputrec *ir, t_gromppopts *opts)
 +{
 +    snew(opts->include, STRLEN);
 +    snew(opts->define, STRLEN);
 +    snew(ir->fepvals, 1);
 +    snew(ir->expandedvals, 1);
 +    snew(ir->simtempvals, 1);
 +}
 +
 +static void GetSimTemps(int ntemps, t_simtemp *simtemp, double *temperature_lambdas)
 +{
 +
 +    int i;
 +
 +    for (i = 0; i < ntemps; i++)
 +    {
 +        /* simple linear scaling -- allows more control */
 +        if (simtemp->eSimTempScale == esimtempLINEAR)
 +        {
 +            simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*temperature_lambdas[i];
 +        }
 +        else if (simtemp->eSimTempScale == esimtempGEOMETRIC)  /* should give roughly equal acceptance for constant heat capacity . . . */
 +        {
 +            simtemp->temperatures[i] = simtemp->simtemp_low * pow(simtemp->simtemp_high/simtemp->simtemp_low, (1.0*i)/(ntemps-1));
 +        }
 +        else if (simtemp->eSimTempScale == esimtempEXPONENTIAL)
 +        {
 +            simtemp->temperatures[i] = simtemp->simtemp_low + (simtemp->simtemp_high-simtemp->simtemp_low)*((exp(temperature_lambdas[i])-1)/(exp(1.0)-1));
 +        }
 +        else
 +        {
 +            char errorstr[128];
 +            sprintf(errorstr, "eSimTempScale=%d not defined", simtemp->eSimTempScale);
 +            gmx_fatal(FARGS, errorstr);
 +        }
 +    }
 +}
 +
 +
 +
 +static void _low_check(gmx_bool b, char *s, warninp_t wi)
 +{
 +    if (b)
 +    {
 +        warning_error(wi, s);
 +    }
 +}
 +
 +static void check_nst(const char *desc_nst, int nst,
 +                      const char *desc_p, int *p,
 +                      warninp_t wi)
 +{
 +    char buf[STRLEN];
 +
 +    if (*p > 0 && *p % nst != 0)
 +    {
 +        /* Round up to the next multiple of nst */
 +        *p = ((*p)/nst + 1)*nst;
 +        sprintf(buf, "%s should be a multiple of %s, changing %s to %d\n",
 +                desc_p, desc_nst, desc_p, *p);
 +        warning(wi, buf);
 +    }
 +}
 +
 +static gmx_bool ir_NVE(const t_inputrec *ir)
 +{
 +    return ((ir->eI == eiMD || EI_VV(ir->eI)) && ir->etc == etcNO);
 +}
 +
 +static int lcd(int n1, int n2)
 +{
 +    int d, i;
 +
 +    d = 1;
 +    for (i = 2; (i <= n1 && i <= n2); i++)
 +    {
 +        if (n1 % i == 0 && n2 % i == 0)
 +        {
 +            d = i;
 +        }
 +    }
 +
 +    return d;
 +}
 +
 +static void process_interaction_modifier(const t_inputrec *ir, int *eintmod)
 +{
 +    if (*eintmod == eintmodPOTSHIFT_VERLET)
 +    {
 +        if (ir->cutoff_scheme == ecutsVERLET)
 +        {
 +            *eintmod = eintmodPOTSHIFT;
 +        }
 +        else
 +        {
 +            *eintmod = eintmodNONE;
 +        }
 +    }
 +}
 +
 +void check_ir(const char *mdparin, t_inputrec *ir, t_gromppopts *opts,
 +              warninp_t wi)
 +/* Check internal consistency */
 +{
 +    /* Strange macro: first one fills the err_buf, and then one can check
 +     * the condition, which will print the message and increase the error
 +     * counter.
 +     */
 +#define CHECK(b) _low_check(b, err_buf, wi)
 +    char        err_buf[256], warn_buf[STRLEN];
 +    int         i, j;
 +    int         ns_type  = 0;
 +    real        dt_coupl = 0;
 +    real        dt_pcoupl;
 +    int         nstcmin;
 +    t_lambda   *fep    = ir->fepvals;
 +    t_expanded *expand = ir->expandedvals;
 +
 +    set_warning_line(wi, mdparin, -1);
 +
 +    /* BASIC CUT-OFF STUFF */
 +    if (ir->rcoulomb < 0)
 +    {
 +        warning_error(wi, "rcoulomb should be >= 0");
 +    }
 +    if (ir->rvdw < 0)
 +    {
 +        warning_error(wi, "rvdw should be >= 0");
 +    }
 +    if (ir->rlist < 0 &&
 +        !(ir->cutoff_scheme == ecutsVERLET && ir->verletbuf_drift > 0))
 +    {
 +        warning_error(wi, "rlist should be >= 0");
 +    }
 +
 +    process_interaction_modifier(ir, &ir->coulomb_modifier);
 +    process_interaction_modifier(ir, &ir->vdw_modifier);
 +
 +    if (ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        /* BASIC CUT-OFF STUFF */
 +        if (ir->rlist == 0 ||
 +            !((EEL_MIGHT_BE_ZERO_AT_CUTOFF(ir->coulombtype) && ir->rcoulomb > ir->rlist) ||
 +              (EVDW_MIGHT_BE_ZERO_AT_CUTOFF(ir->vdwtype)    && ir->rvdw     > ir->rlist)))
 +        {
 +            /* No switched potential and/or no twin-range:
 +             * we can set the long-range cut-off to the maximum of the other cut-offs.
 +             */
 +            ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb));
 +        }
 +        else if (ir->rlistlong < 0)
 +        {
 +            ir->rlistlong = max_cutoff(ir->rlist, max_cutoff(ir->rvdw, ir->rcoulomb));
 +            sprintf(warn_buf, "rlistlong was not set, setting it to %g (no buffer)",
 +                    ir->rlistlong);
 +            warning(wi, warn_buf);
 +        }
 +        if (ir->rlistlong == 0 && ir->ePBC != epbcNONE)
 +        {
 +            warning_error(wi, "Can not have an infinite cut-off with PBC");
 +        }
 +        if (ir->rlistlong > 0 && (ir->rlist == 0 || ir->rlistlong < ir->rlist))
 +        {
 +            warning_error(wi, "rlistlong can not be shorter than rlist");
 +        }
 +        if (IR_TWINRANGE(*ir) && ir->nstlist <= 0)
 +        {
 +            warning_error(wi, "Can not have nstlist<=0 with twin-range interactions");
 +        }
 +    }
 +
 +    if (ir->rlistlong == ir->rlist)
 +    {
 +        ir->nstcalclr = 0;
 +    }
 +    else if (ir->rlistlong > ir->rlist && ir->nstcalclr == 0)
 +    {
 +        warning_error(wi, "With different cutoffs for electrostatics and VdW, nstcalclr must be -1 or a positive number");
 +    }
 +
 +    if (ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        real rc_max;
 +
 +        /* Normal Verlet type neighbor-list, currently only limited feature support */
 +        if (inputrec2nboundeddim(ir) < 3)
 +        {
 +            warning_error(wi, "With Verlet lists only full pbc or pbc=xy with walls is supported");
 +        }
 +        if (ir->rcoulomb != ir->rvdw)
 +        {
 +            warning_error(wi, "With Verlet lists rcoulomb!=rvdw is not supported");
 +        }
 +        if (ir->vdwtype != evdwCUT)
 +        {
 +            warning_error(wi, "With Verlet lists only cut-off LJ interactions are supported");
 +        }
 +        if (!(ir->coulombtype == eelCUT ||
 +              (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC) ||
 +              EEL_PME(ir->coulombtype) || ir->coulombtype == eelEWALD))
 +        {
 +            warning_error(wi, "With Verlet lists only cut-off, reaction-field, PME and Ewald electrostatics are supported");
 +        }
 +
 +        if (ir->nstlist <= 0)
 +        {
 +            warning_error(wi, "With Verlet lists nstlist should be larger than 0");
 +        }
 +
 +        if (ir->nstlist < 10)
 +        {
 +            warning_note(wi, "With Verlet lists the optimal nstlist is >= 10, with GPUs >= 20. Note that with the Verlet scheme, nstlist has no effect on the accuracy of your simulation.");
 +        }
 +
 +        rc_max = max(ir->rvdw, ir->rcoulomb);
 +
 +        if (ir->verletbuf_drift <= 0)
 +        {
 +            if (ir->verletbuf_drift == 0)
 +            {
 +                warning_error(wi, "Can not have an energy drift of exactly 0");
 +            }
 +
 +            if (ir->rlist < rc_max)
 +            {
 +                warning_error(wi, "With verlet lists rlist can not be smaller than rvdw or rcoulomb");
 +            }
 +
 +            if (ir->rlist == rc_max && ir->nstlist > 1)
 +            {
 +                warning_note(wi, "rlist is equal to rvdw and/or rcoulomb: there is no explicit Verlet buffer. The cluster pair list does have a buffering effect, but choosing a larger rlist might be necessary for good energy conservation.");
 +            }
 +        }
 +        else
 +        {
 +            if (ir->rlist > rc_max)
 +            {
 +                warning_note(wi, "You have set rlist larger than the interaction cut-off, but you also have verlet-buffer-drift > 0. Will set rlist using verlet-buffer-drift.");
 +            }
 +
 +            if (ir->nstlist == 1)
 +            {
 +                /* No buffer required */
 +                ir->rlist = rc_max;
 +            }
 +            else
 +            {
 +                if (EI_DYNAMICS(ir->eI))
 +                {
 +                    if (EI_MD(ir->eI) && ir->etc == etcNO)
 +                    {
 +                        warning_error(wi, "Temperature coupling is required for calculating rlist using the energy drift with verlet-buffer-drift > 0. Either use temperature coupling or set rlist yourself together with verlet-buffer-drift = -1.");
 +                    }
 +
 +                    if (inputrec2nboundeddim(ir) < 3)
 +                    {
 +                        warning_error(wi, "The box volume is required for calculating rlist from the energy drift with verlet-buffer-drift > 0. You are using at least one unbounded dimension, so no volume can be computed. Either use a finite box, or set rlist yourself together with verlet-buffer-drift = -1.");
 +                    }
 +                    /* Set rlist temporarily so we can continue processing */
 +                    ir->rlist = rc_max;
 +                }
 +                else
 +                {
 +                    /* Set the buffer to 5% of the cut-off */
 +                    ir->rlist = 1.05*rc_max;
 +                }
 +            }
 +        }
 +
 +        /* No twin-range calculations with Verlet lists */
 +        ir->rlistlong = ir->rlist;
 +    }
 +
 +    if (ir->nstcalclr == -1)
 +    {
 +        /* if rlist=rlistlong, this will later be changed to nstcalclr=0 */
 +        ir->nstcalclr = ir->nstlist;
 +    }
 +    else if (ir->nstcalclr > 0)
 +    {
 +        if (ir->nstlist > 0 && (ir->nstlist % ir->nstcalclr != 0))
 +        {
 +            warning_error(wi, "nstlist must be evenly divisible by nstcalclr. Use nstcalclr = -1 to automatically follow nstlist");
 +        }
 +    }
 +    else if (ir->nstcalclr < -1)
 +    {
 +        warning_error(wi, "nstcalclr must be a positive number (divisor of nstcalclr), or -1 to follow nstlist.");
 +    }
 +
 +    if (EEL_PME(ir->coulombtype) && ir->rcoulomb > ir->rvdw && ir->nstcalclr > 1)
 +    {
 +        warning_error(wi, "When used with PME, the long-range component of twin-range interactions must be updated every step (nstcalclr)");
 +    }
 +
 +    /* GENERAL INTEGRATOR STUFF */
 +    if (!(ir->eI == eiMD || EI_VV(ir->eI)))
 +    {
 +        ir->etc = etcNO;
 +    }
 +    if (ir->eI == eiVVAK)
 +    {
 +        sprintf(warn_buf, "Integrator method %s is implemented primarily for validation purposes; for molecular dynamics, you should probably be using %s or %s", ei_names[eiVVAK], ei_names[eiMD], ei_names[eiVV]);
 +        warning_note(wi, warn_buf);
 +    }
 +    if (!EI_DYNAMICS(ir->eI))
 +    {
 +        ir->epc = epcNO;
 +    }
 +    if (EI_DYNAMICS(ir->eI))
 +    {
 +        if (ir->nstcalcenergy < 0)
 +        {
 +            ir->nstcalcenergy = ir_optimal_nstcalcenergy(ir);
 +            if (ir->nstenergy != 0 && ir->nstenergy < ir->nstcalcenergy)
 +            {
 +                /* nstcalcenergy larger than nstener does not make sense.
 +                 * We ideally want nstcalcenergy=nstener.
 +                 */
 +                if (ir->nstlist > 0)
 +                {
 +                    ir->nstcalcenergy = lcd(ir->nstenergy, ir->nstlist);
 +                }
 +                else
 +                {
 +                    ir->nstcalcenergy = ir->nstenergy;
 +                }
 +            }
 +        }
 +        else if ( (ir->nstenergy > 0 && ir->nstcalcenergy > ir->nstenergy) ||
 +                  (ir->efep != efepNO && ir->fepvals->nstdhdl > 0 &&
 +                   (ir->nstcalcenergy > ir->fepvals->nstdhdl) ) )
 +
 +        {
 +            const char *nsten    = "nstenergy";
 +            const char *nstdh    = "nstdhdl";
 +            const char *min_name = nsten;
 +            int         min_nst  = ir->nstenergy;
 +
 +            /* find the smallest of ( nstenergy, nstdhdl ) */
 +            if (ir->efep != efepNO && ir->fepvals->nstdhdl > 0 &&
 +                (ir->fepvals->nstdhdl < ir->nstenergy) )
 +            {
 +                min_nst  = ir->fepvals->nstdhdl;
 +                min_name = nstdh;
 +            }
 +            /* If the user sets nstenergy small, we should respect that */
 +            sprintf(warn_buf,
 +                    "Setting nstcalcenergy (%d) equal to %s (%d)",
 +                    ir->nstcalcenergy, min_name, min_nst);
 +            warning_note(wi, warn_buf);
 +            ir->nstcalcenergy = min_nst;
 +        }
 +
 +        if (ir->epc != epcNO)
 +        {
 +            if (ir->nstpcouple < 0)
 +            {
 +                ir->nstpcouple = ir_optimal_nstpcouple(ir);
 +            }
 +        }
 +        if (IR_TWINRANGE(*ir))
 +        {
 +            check_nst("nstlist", ir->nstlist,
 +                      "nstcalcenergy", &ir->nstcalcenergy, wi);
 +            if (ir->epc != epcNO)
 +            {
 +                check_nst("nstlist", ir->nstlist,
 +                          "nstpcouple", &ir->nstpcouple, wi);
 +            }
 +        }
 +
 +        if (ir->nstcalcenergy > 0)
 +        {
 +            if (ir->efep != efepNO)
 +            {
 +                /* nstdhdl should be a multiple of nstcalcenergy */
 +                check_nst("nstcalcenergy", ir->nstcalcenergy,
 +                          "nstdhdl", &ir->fepvals->nstdhdl, wi);
 +                /* nstexpanded should be a multiple of nstcalcenergy */
 +                check_nst("nstcalcenergy", ir->nstcalcenergy,
 +                          "nstexpanded", &ir->expandedvals->nstexpanded, wi);
 +            }
 +            /* for storing exact averages nstenergy should be
 +             * a multiple of nstcalcenergy
 +             */
 +            check_nst("nstcalcenergy", ir->nstcalcenergy,
 +                      "nstenergy", &ir->nstenergy, wi);
 +        }
 +    }
 +
 +    /* LD STUFF */
 +    if ((EI_SD(ir->eI) || ir->eI == eiBD) &&
 +        ir->bContinuation && ir->ld_seed != -1)
 +    {
 +        warning_note(wi, "You are doing a continuation with SD or BD, make sure that ld_seed is different from the previous run (using ld_seed=-1 will ensure this)");
 +    }
 +
 +    /* TPI STUFF */
 +    if (EI_TPI(ir->eI))
 +    {
 +        sprintf(err_buf, "TPI only works with pbc = %s", epbc_names[epbcXYZ]);
 +        CHECK(ir->ePBC != epbcXYZ);
 +        sprintf(err_buf, "TPI only works with ns = %s", ens_names[ensGRID]);
 +        CHECK(ir->ns_type != ensGRID);
 +        sprintf(err_buf, "with TPI nstlist should be larger than zero");
 +        CHECK(ir->nstlist <= 0);
 +        sprintf(err_buf, "TPI does not work with full electrostatics other than PME");
 +        CHECK(EEL_FULL(ir->coulombtype) && !EEL_PME(ir->coulombtype));
 +    }
 +
 +    /* SHAKE / LINCS */
 +    if ( (opts->nshake > 0) && (opts->bMorse) )
 +    {
 +        sprintf(warn_buf,
 +                "Using morse bond-potentials while constraining bonds is useless");
 +        warning(wi, warn_buf);
 +    }
 +
 +    if ((EI_SD(ir->eI) || ir->eI == eiBD) &&
 +        ir->bContinuation && ir->ld_seed != -1)
 +    {
 +        warning_note(wi, "You are doing a continuation with SD or BD, make sure that ld_seed is different from the previous run (using ld_seed=-1 will ensure this)");
 +    }
 +    /* verify simulated tempering options */
 +
 +    if (ir->bSimTemp)
 +    {
 +        gmx_bool bAllTempZero = TRUE;
 +        for (i = 0; i < fep->n_lambda; i++)
 +        {
 +            sprintf(err_buf, "Entry %d for %s must be between 0 and 1, instead is %g", i, efpt_names[efptTEMPERATURE], fep->all_lambda[efptTEMPERATURE][i]);
 +            CHECK((fep->all_lambda[efptTEMPERATURE][i] < 0) || (fep->all_lambda[efptTEMPERATURE][i] > 1));
 +            if (fep->all_lambda[efptTEMPERATURE][i] > 0)
 +            {
 +                bAllTempZero = FALSE;
 +            }
 +        }
 +        sprintf(err_buf, "if simulated tempering is on, temperature-lambdas may not be all zero");
 +        CHECK(bAllTempZero == TRUE);
 +
 +        sprintf(err_buf, "Simulated tempering is currently only compatible with md-vv");
 +        CHECK(ir->eI != eiVV);
 +
 +        /* check compatability of the temperature coupling with simulated tempering */
 +
 +        if (ir->etc == etcNOSEHOOVER)
 +        {
 +            sprintf(warn_buf, "Nose-Hoover based temperature control such as [%s] my not be entirelyconsistent with simulated tempering", etcoupl_names[ir->etc]);
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        /* check that the temperatures make sense */
 +
 +        sprintf(err_buf, "Higher simulated tempering temperature (%g) must be >= than the simulated tempering lower temperature (%g)", ir->simtempvals->simtemp_high, ir->simtempvals->simtemp_low);
 +        CHECK(ir->simtempvals->simtemp_high <= ir->simtempvals->simtemp_low);
 +
 +        sprintf(err_buf, "Higher simulated tempering temperature (%g) must be >= zero", ir->simtempvals->simtemp_high);
 +        CHECK(ir->simtempvals->simtemp_high <= 0);
 +
 +        sprintf(err_buf, "Lower simulated tempering temperature (%g) must be >= zero", ir->simtempvals->simtemp_low);
 +        CHECK(ir->simtempvals->simtemp_low <= 0);
 +    }
 +
 +    /* verify free energy options */
 +
 +    if (ir->efep != efepNO)
 +    {
 +        fep = ir->fepvals;
 +        sprintf(err_buf, "The soft-core power is %d and can only be 1 or 2",
 +                fep->sc_power);
 +        CHECK(fep->sc_alpha != 0 && fep->sc_power != 1 && fep->sc_power != 2);
 +
 +        sprintf(err_buf, "The soft-core sc-r-power is %d and can only be 6 or 48",
 +                (int)fep->sc_r_power);
 +        CHECK(fep->sc_alpha != 0 && fep->sc_r_power != 6.0 && fep->sc_r_power != 48.0);
 +
 +        sprintf(err_buf, "Can't use postive delta-lambda (%g) if initial state/lambda does not start at zero", fep->delta_lambda);
 +        CHECK(fep->delta_lambda > 0 && ((fep->init_fep_state > 0) ||  (fep->init_lambda > 0)));
 +
 +        sprintf(err_buf, "Can't use postive delta-lambda (%g) with expanded ensemble simulations", fep->delta_lambda);
 +        CHECK(fep->delta_lambda > 0 && (ir->efep == efepEXPANDED));
 +
 +        sprintf(err_buf, "Free-energy not implemented for Ewald");
 +        CHECK(ir->coulombtype == eelEWALD);
 +
 +        /* check validty of lambda inputs */
 +        if (fep->n_lambda == 0)
 +        {
 +            /* Clear output in case of no states:*/
 +            sprintf(err_buf, "init-lambda-state set to %d: no lambda states are defined.", fep->init_fep_state);
 +            CHECK((fep->init_fep_state >= 0) && (fep->n_lambda == 0));
 +        }
 +        else
 +        {
 +            sprintf(err_buf, "initial thermodynamic state %d does not exist, only goes to %d", fep->init_fep_state, fep->n_lambda-1);
 +            CHECK((fep->init_fep_state >= fep->n_lambda));
 +        }
 +
 +        sprintf(err_buf, "Lambda state must be set, either with init-lambda-state or with init-lambda");
 +        CHECK((fep->init_fep_state < 0) && (fep->init_lambda < 0));
 +
 +        sprintf(err_buf, "init-lambda=%g while init-lambda-state=%d. Lambda state must be set either with init-lambda-state or with init-lambda, but not both",
 +                fep->init_lambda, fep->init_fep_state);
 +        CHECK((fep->init_fep_state >= 0) && (fep->init_lambda >= 0));
 +
 +
 +
 +        if ((fep->init_lambda >= 0) && (fep->delta_lambda == 0))
 +        {
 +            int n_lambda_terms;
 +            n_lambda_terms = 0;
 +            for (i = 0; i < efptNR; i++)
 +            {
 +                if (fep->separate_dvdl[i])
 +                {
 +                    n_lambda_terms++;
 +                }
 +            }
 +            if (n_lambda_terms > 1)
 +            {
 +                sprintf(warn_buf, "If lambda vector states (fep-lambdas, coul-lambdas etc.) are set, don't use init-lambda to set lambda state (except for slow growth). Use init-lambda-state instead.");
 +                warning(wi, warn_buf);
 +            }
 +
 +            if (n_lambda_terms < 2 && fep->n_lambda > 0)
 +            {
 +                warning_note(wi,
 +                             "init-lambda is deprecated for setting lambda state (except for slow growth). Use init-lambda-state instead.");
 +            }
 +        }
 +
 +        for (j = 0; j < efptNR; j++)
 +        {
 +            for (i = 0; i < fep->n_lambda; i++)
 +            {
 +                sprintf(err_buf, "Entry %d for %s must be between 0 and 1, instead is %g", i, efpt_names[j], fep->all_lambda[j][i]);
 +                CHECK((fep->all_lambda[j][i] < 0) || (fep->all_lambda[j][i] > 1));
 +            }
 +        }
 +
 +        if ((fep->sc_alpha > 0) && (!fep->bScCoul))
 +        {
 +            for (i = 0; i < fep->n_lambda; i++)
 +            {
 +                sprintf(err_buf, "For state %d, vdw-lambdas (%f) is changing with vdw softcore, while coul-lambdas (%f) is nonzero without coulomb softcore: this will lead to crashes, and is not supported.", i, fep->all_lambda[efptVDW][i],
 +                        fep->all_lambda[efptCOUL][i]);
 +                CHECK((fep->sc_alpha > 0) &&
 +                      (((fep->all_lambda[efptCOUL][i] > 0.0) &&
 +                        (fep->all_lambda[efptCOUL][i] < 1.0)) &&
 +                       ((fep->all_lambda[efptVDW][i] > 0.0) &&
 +                        (fep->all_lambda[efptVDW][i] < 1.0))));
 +            }
 +        }
 +
 +        if ((fep->bScCoul) && (EEL_PME(ir->coulombtype)))
 +        {
 +            real sigma, lambda, r_sc;
 +
 +            sigma  = 0.34;
 +            /* Maximum estimate for A and B charges equal with lambda power 1 */
 +            lambda = 0.5;
 +            r_sc   = pow(lambda*fep->sc_alpha*pow(sigma/ir->rcoulomb, fep->sc_r_power) + 1.0, 1.0/fep->sc_r_power);
 +            sprintf(warn_buf, "With PME there is a minor soft core effect present at the cut-off, proportional to (LJsigma/rcoulomb)^%g. This could have a minor effect on energy conservation, but usually other effects dominate. With a common sigma value of %g nm the fraction of the particle-particle potential at the cut-off at lambda=%g is around %.1e, while ewald-rtol is %.1e.",
 +                    fep->sc_r_power,
 +                    sigma, lambda, r_sc - 1.0, ir->ewald_rtol);
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        /*  Free Energy Checks -- In an ideal world, slow growth and FEP would
 +            be treated differently, but that's the next step */
 +
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            for (j = 0; j < fep->n_lambda; j++)
 +            {
 +                sprintf(err_buf, "%s[%d] must be between 0 and 1", efpt_names[i], j);
 +                CHECK((fep->all_lambda[i][j] < 0) || (fep->all_lambda[i][j] > 1));
 +            }
 +        }
 +    }
 +
 +    if ((ir->bSimTemp) || (ir->efep == efepEXPANDED))
 +    {
 +        fep    = ir->fepvals;
 +        expand = ir->expandedvals;
 +
 +        /* checking equilibration of weights inputs for validity */
 +
 +        sprintf(err_buf, "weight-equil-number-all-lambda (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_n_at_lam, elmceq_names[elmceqNUMATLAM]);
 +        CHECK((expand->equil_n_at_lam > 0) && (expand->elmceq != elmceqNUMATLAM));
 +
 +        sprintf(err_buf, "weight-equil-number-samples (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_samples, elmceq_names[elmceqSAMPLES]);
 +        CHECK((expand->equil_samples > 0) && (expand->elmceq != elmceqSAMPLES));
 +
 +        sprintf(err_buf, "weight-equil-number-steps (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_steps, elmceq_names[elmceqSTEPS]);
 +        CHECK((expand->equil_steps > 0) && (expand->elmceq != elmceqSTEPS));
 +
 +        sprintf(err_buf, "weight-equil-wl-delta (%d) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_samples, elmceq_names[elmceqWLDELTA]);
 +        CHECK((expand->equil_wl_delta > 0) && (expand->elmceq != elmceqWLDELTA));
 +
 +        sprintf(err_buf, "weight-equil-count-ratio (%f) is ignored if lmc-weights-equil is not equal to %s",
 +                expand->equil_ratio, elmceq_names[elmceqRATIO]);
 +        CHECK((expand->equil_ratio > 0) && (expand->elmceq != elmceqRATIO));
 +
 +        sprintf(err_buf, "weight-equil-number-all-lambda (%d) must be a positive integer if lmc-weights-equil=%s",
 +                expand->equil_n_at_lam, elmceq_names[elmceqNUMATLAM]);
 +        CHECK((expand->equil_n_at_lam <= 0) && (expand->elmceq == elmceqNUMATLAM));
 +
 +        sprintf(err_buf, "weight-equil-number-samples (%d) must be a positive integer if lmc-weights-equil=%s",
 +                expand->equil_samples, elmceq_names[elmceqSAMPLES]);
 +        CHECK((expand->equil_samples <= 0) && (expand->elmceq == elmceqSAMPLES));
 +
 +        sprintf(err_buf, "weight-equil-number-steps (%d) must be a positive integer if lmc-weights-equil=%s",
 +                expand->equil_steps, elmceq_names[elmceqSTEPS]);
 +        CHECK((expand->equil_steps <= 0) && (expand->elmceq == elmceqSTEPS));
 +
 +        sprintf(err_buf, "weight-equil-wl-delta (%f) must be > 0 if lmc-weights-equil=%s",
 +                expand->equil_wl_delta, elmceq_names[elmceqWLDELTA]);
 +        CHECK((expand->equil_wl_delta <= 0) && (expand->elmceq == elmceqWLDELTA));
 +
 +        sprintf(err_buf, "weight-equil-count-ratio (%f) must be > 0 if lmc-weights-equil=%s",
 +                expand->equil_ratio, elmceq_names[elmceqRATIO]);
 +        CHECK((expand->equil_ratio <= 0) && (expand->elmceq == elmceqRATIO));
 +
 +        sprintf(err_buf, "lmc-weights-equil=%s only possible when lmc-stats = %s or lmc-stats %s",
 +                elmceq_names[elmceqWLDELTA], elamstats_names[elamstatsWL], elamstats_names[elamstatsWWL]);
 +        CHECK((expand->elmceq == elmceqWLDELTA) && (!EWL(expand->elamstats)));
 +
 +        sprintf(err_buf, "lmc-repeats (%d) must be greater than 0", expand->lmc_repeats);
 +        CHECK((expand->lmc_repeats <= 0));
 +        sprintf(err_buf, "minimum-var-min (%d) must be greater than 0", expand->minvarmin);
 +        CHECK((expand->minvarmin <= 0));
 +        sprintf(err_buf, "weight-c-range (%d) must be greater or equal to 0", expand->c_range);
 +        CHECK((expand->c_range < 0));
 +        sprintf(err_buf, "init-lambda-state (%d) must be zero if lmc-forced-nstart (%d)> 0 and lmc-move != 'no'",
 +                fep->init_fep_state, expand->lmc_forced_nstart);
 +        CHECK((fep->init_fep_state != 0) && (expand->lmc_forced_nstart > 0) && (expand->elmcmove != elmcmoveNO));
 +        sprintf(err_buf, "lmc-forced-nstart (%d) must not be negative", expand->lmc_forced_nstart);
 +        CHECK((expand->lmc_forced_nstart < 0));
 +        sprintf(err_buf, "init-lambda-state (%d) must be in the interval [0,number of lambdas)", fep->init_fep_state);
 +        CHECK((fep->init_fep_state < 0) || (fep->init_fep_state >= fep->n_lambda));
 +
 +        sprintf(err_buf, "init-wl-delta (%f) must be greater than or equal to 0", expand->init_wl_delta);
 +        CHECK((expand->init_wl_delta < 0));
 +        sprintf(err_buf, "wl-ratio (%f) must be between 0 and 1", expand->wl_ratio);
 +        CHECK((expand->wl_ratio <= 0) || (expand->wl_ratio >= 1));
 +        sprintf(err_buf, "wl-scale (%f) must be between 0 and 1", expand->wl_scale);
 +        CHECK((expand->wl_scale <= 0) || (expand->wl_scale >= 1));
 +
 +        /* if there is no temperature control, we need to specify an MC temperature */
 +        sprintf(err_buf, "If there is no temperature control, and lmc-mcmove!= 'no',mc_temperature must be set to a positive number");
 +        if (expand->nstTij > 0)
 +        {
 +            sprintf(err_buf, "nst-transition-matrix (%d) must be an integer multiple of nstlog (%d)",
 +                    expand->nstTij, ir->nstlog);
 +            CHECK((mod(expand->nstTij, ir->nstlog) != 0));
 +        }
 +    }
 +
 +    /* PBC/WALLS */
 +    sprintf(err_buf, "walls only work with pbc=%s", epbc_names[epbcXY]);
 +    CHECK(ir->nwall && ir->ePBC != epbcXY);
 +
 +    /* VACUUM STUFF */
 +    if (ir->ePBC != epbcXYZ && ir->nwall != 2)
 +    {
 +        if (ir->ePBC == epbcNONE)
 +        {
 +            if (ir->epc != epcNO)
 +            {
 +                warning(wi, "Turning off pressure coupling for vacuum system");
 +                ir->epc = epcNO;
 +            }
 +        }
 +        else
 +        {
 +            sprintf(err_buf, "Can not have pressure coupling with pbc=%s",
 +                    epbc_names[ir->ePBC]);
 +            CHECK(ir->epc != epcNO);
 +        }
 +        sprintf(err_buf, "Can not have Ewald with pbc=%s", epbc_names[ir->ePBC]);
 +        CHECK(EEL_FULL(ir->coulombtype));
 +
 +        sprintf(err_buf, "Can not have dispersion correction with pbc=%s",
 +                epbc_names[ir->ePBC]);
 +        CHECK(ir->eDispCorr != edispcNO);
 +    }
 +
 +    if (ir->rlist == 0.0)
 +    {
 +        sprintf(err_buf, "can only have neighborlist cut-off zero (=infinite)\n"
 +                "with coulombtype = %s or coulombtype = %s\n"
 +                "without periodic boundary conditions (pbc = %s) and\n"
 +                "rcoulomb and rvdw set to zero",
 +                eel_names[eelCUT], eel_names[eelUSER], epbc_names[epbcNONE]);
 +        CHECK(((ir->coulombtype != eelCUT) && (ir->coulombtype != eelUSER)) ||
 +              (ir->ePBC     != epbcNONE) ||
 +              (ir->rcoulomb != 0.0)      || (ir->rvdw != 0.0));
 +
 +        if (ir->nstlist < 0)
 +        {
 +            warning_error(wi, "Can not have heuristic neighborlist updates without cut-off");
 +        }
 +        if (ir->nstlist > 0)
 +        {
 +            warning_note(wi, "Simulating without cut-offs is usually (slightly) faster with nstlist=0, nstype=simple and particle decomposition");
 +        }
 +    }
 +
 +    /* COMM STUFF */
 +    if (ir->nstcomm == 0)
 +    {
 +        ir->comm_mode = ecmNO;
 +    }
 +    if (ir->comm_mode != ecmNO)
 +    {
 +        if (ir->nstcomm < 0)
 +        {
 +            warning(wi, "If you want to remove the rotation around the center of mass, you should set comm_mode = Angular instead of setting nstcomm < 0. nstcomm is modified to its absolute value");
 +            ir->nstcomm = abs(ir->nstcomm);
 +        }
 +
 +        if (ir->nstcalcenergy > 0 && ir->nstcomm < ir->nstcalcenergy)
 +        {
 +            warning_note(wi, "nstcomm < nstcalcenergy defeats the purpose of nstcalcenergy, setting nstcomm to nstcalcenergy");
 +            ir->nstcomm = ir->nstcalcenergy;
 +        }
 +
 +        if (ir->comm_mode == ecmANGULAR)
 +        {
 +            sprintf(err_buf, "Can not remove the rotation around the center of mass with periodic molecules");
 +            CHECK(ir->bPeriodicMols);
 +            if (ir->ePBC != epbcNONE)
 +            {
 +                warning(wi, "Removing the rotation around the center of mass in a periodic system (this is not a problem when you have only one molecule).");
 +            }
 +        }
 +    }
 +
 +    if (EI_STATE_VELOCITY(ir->eI) && ir->ePBC == epbcNONE && ir->comm_mode != ecmANGULAR)
 +    {
 +        warning_note(wi, "Tumbling and or flying ice-cubes: We are not removing rotation around center of mass in a non-periodic system. You should probably set comm_mode = ANGULAR.");
 +    }
 +
 +    sprintf(err_buf, "Twin-range neighbour searching (NS) with simple NS"
 +            " algorithm not implemented");
 +    CHECK(((ir->rcoulomb > ir->rlist) || (ir->rvdw > ir->rlist))
 +          && (ir->ns_type == ensSIMPLE));
 +
 +    /* TEMPERATURE COUPLING */
 +    if (ir->etc == etcYES)
 +    {
 +        ir->etc = etcBERENDSEN;
 +        warning_note(wi, "Old option for temperature coupling given: "
 +                     "changing \"yes\" to \"Berendsen\"\n");
 +    }
 +
 +    if ((ir->etc == etcNOSEHOOVER) || (ir->epc == epcMTTK))
 +    {
 +        if (ir->opts.nhchainlength < 1)
 +        {
 +            sprintf(warn_buf, "number of Nose-Hoover chains (currently %d) cannot be less than 1,reset to 1\n", ir->opts.nhchainlength);
 +            ir->opts.nhchainlength = 1;
 +            warning(wi, warn_buf);
 +        }
 +
 +        if (ir->etc == etcNOSEHOOVER && !EI_VV(ir->eI) && ir->opts.nhchainlength > 1)
 +        {
 +            warning_note(wi, "leapfrog does not yet support Nose-Hoover chains, nhchainlength reset to 1");
 +            ir->opts.nhchainlength = 1;
 +        }
 +    }
 +    else
 +    {
 +        ir->opts.nhchainlength = 0;
 +    }
 +
 +    if (ir->eI == eiVVAK)
 +    {
 +        sprintf(err_buf, "%s implemented primarily for validation, and requires nsttcouple = 1 and nstpcouple = 1.",
 +                ei_names[eiVVAK]);
 +        CHECK((ir->nsttcouple != 1) || (ir->nstpcouple != 1));
 +    }
 +
 +    if (ETC_ANDERSEN(ir->etc))
 +    {
 +        sprintf(err_buf, "%s temperature control not supported for integrator %s.", etcoupl_names[ir->etc], ei_names[ir->eI]);
 +        CHECK(!(EI_VV(ir->eI)));
 +
 +        for (i = 0; i < ir->opts.ngtc; i++)
 +        {
 +            sprintf(err_buf, "all tau_t must currently be equal using Andersen temperature control, violated for group %d", i);
 +            CHECK(ir->opts.tau_t[0] != ir->opts.tau_t[i]);
 +            sprintf(err_buf, "all tau_t must be postive using Andersen temperature control, tau_t[%d]=%10.6f",
 +                    i, ir->opts.tau_t[i]);
 +            CHECK(ir->opts.tau_t[i] < 0);
 +        }
 +        if (ir->nstcomm > 0 && (ir->etc == etcANDERSEN))
 +        {
 +            sprintf(warn_buf, "Center of mass removal not necessary for %s.  All velocities of coupled groups are rerandomized periodically, so flying ice cube errors will not occur.", etcoupl_names[ir->etc]);
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        sprintf(err_buf, "nstcomm must be 1, not %d for %s, as velocities of atoms in coupled groups are randomized every time step", ir->nstcomm, etcoupl_names[ir->etc]);
 +        CHECK(ir->nstcomm > 1 && (ir->etc == etcANDERSEN));
 +
 +        for (i = 0; i < ir->opts.ngtc; i++)
 +        {
 +            int nsteps = (int)(ir->opts.tau_t[i]/ir->delta_t);
 +            sprintf(err_buf, "tau_t/delta_t for group %d for temperature control method %s must be a multiple of nstcomm (%d), as velocities of atoms in coupled groups are randomized every time step. The input tau_t (%8.3f) leads to %d steps per randomization", i, etcoupl_names[ir->etc], ir->nstcomm, ir->opts.tau_t[i], nsteps);
 +            CHECK((nsteps % ir->nstcomm) && (ir->etc == etcANDERSENMASSIVE));
 +        }
 +    }
 +    if (ir->etc == etcBERENDSEN)
 +    {
 +        sprintf(warn_buf, "The %s thermostat does not generate the correct kinetic energy distribution. You might want to consider using the %s thermostat.",
 +                ETCOUPLTYPE(ir->etc), ETCOUPLTYPE(etcVRESCALE));
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    if ((ir->etc == etcNOSEHOOVER || ETC_ANDERSEN(ir->etc))
 +        && ir->epc == epcBERENDSEN)
 +    {
 +        sprintf(warn_buf, "Using Berendsen pressure coupling invalidates the "
 +                "true ensemble for the thermostat");
 +        warning(wi, warn_buf);
 +    }
 +
 +    /* PRESSURE COUPLING */
 +    if (ir->epc == epcISOTROPIC)
 +    {
 +        ir->epc = epcBERENDSEN;
 +        warning_note(wi, "Old option for pressure coupling given: "
 +                     "changing \"Isotropic\" to \"Berendsen\"\n");
 +    }
 +
 +    if (ir->epc != epcNO)
 +    {
 +        dt_pcoupl = ir->nstpcouple*ir->delta_t;
 +
 +        sprintf(err_buf, "tau-p must be > 0 instead of %g\n", ir->tau_p);
 +        CHECK(ir->tau_p <= 0);
 +
 +        if (ir->tau_p/dt_pcoupl < pcouple_min_integration_steps(ir->epc))
 +        {
 +            sprintf(warn_buf, "For proper integration of the %s barostat, tau-p (%g) should be at least %d times larger than nstpcouple*dt (%g)",
 +                    EPCOUPLTYPE(ir->epc), ir->tau_p, pcouple_min_integration_steps(ir->epc), dt_pcoupl);
 +            warning(wi, warn_buf);
 +        }
 +
 +        sprintf(err_buf, "compressibility must be > 0 when using pressure"
 +                " coupling %s\n", EPCOUPLTYPE(ir->epc));
 +        CHECK(ir->compress[XX][XX] < 0 || ir->compress[YY][YY] < 0 ||
 +              ir->compress[ZZ][ZZ] < 0 ||
 +              (trace(ir->compress) == 0 && ir->compress[YY][XX] <= 0 &&
 +               ir->compress[ZZ][XX] <= 0 && ir->compress[ZZ][YY] <= 0));
 +
 +        if (epcPARRINELLORAHMAN == ir->epc && opts->bGenVel)
 +        {
 +            sprintf(warn_buf,
 +                    "You are generating velocities so I am assuming you "
 +                    "are equilibrating a system. You are using "
 +                    "%s pressure coupling, but this can be "
 +                    "unstable for equilibration. If your system crashes, try "
 +                    "equilibrating first with Berendsen pressure coupling. If "
 +                    "you are not equilibrating the system, you can probably "
 +                    "ignore this warning.",
 +                    epcoupl_names[ir->epc]);
 +            warning(wi, warn_buf);
 +        }
 +    }
 +
 +    if (EI_VV(ir->eI))
 +    {
 +        if (ir->epc > epcNO)
 +        {
 +            if ((ir->epc != epcBERENDSEN) && (ir->epc != epcMTTK))
 +            {
 +                warning_error(wi, "for md-vv and md-vv-avek, can only use Berendsen and Martyna-Tuckerman-Tobias-Klein (MTTK) equations for pressure control; MTTK is equivalent to Parrinello-Rahman.");
 +            }
 +        }
 +    }
 +
 +    /* ELECTROSTATICS */
 +    /* More checks are in triple check (grompp.c) */
 +
 +    if (ir->coulombtype == eelSWITCH)
 +    {
 +        sprintf(warn_buf, "coulombtype = %s is only for testing purposes and can lead to serious "
 +                "artifacts, advice: use coulombtype = %s",
 +                eel_names[ir->coulombtype],
 +                eel_names[eelRF_ZERO]);
 +        warning(wi, warn_buf);
 +    }
 +
 +    if (ir->epsilon_r != 1 && ir->implicit_solvent == eisGBSA)
 +    {
 +        sprintf(warn_buf, "epsilon-r = %g with GB implicit solvent, will use this value for inner dielectric", ir->epsilon_r);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    if (EEL_RF(ir->coulombtype) && ir->epsilon_rf == 1 && ir->epsilon_r != 1)
 +    {
 +        sprintf(warn_buf, "epsilon-r = %g and epsilon-rf = 1 with reaction field, proceeding assuming old format and exchanging epsilon-r and epsilon-rf", ir->epsilon_r);
 +        warning(wi, warn_buf);
 +        ir->epsilon_rf = ir->epsilon_r;
 +        ir->epsilon_r  = 1.0;
 +    }
 +
 +    if (getenv("GALACTIC_DYNAMICS") == NULL)
 +    {
 +        sprintf(err_buf, "epsilon-r must be >= 0 instead of %g\n", ir->epsilon_r);
 +        CHECK(ir->epsilon_r < 0);
 +    }
 +
 +    if (EEL_RF(ir->coulombtype))
 +    {
 +        /* reaction field (at the cut-off) */
 +
 +        if (ir->coulombtype == eelRF_ZERO)
 +        {
 +            sprintf(warn_buf, "With coulombtype = %s, epsilon-rf must be 0, assuming you meant epsilon_rf=0",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->epsilon_rf != 0);
 +            ir->epsilon_rf = 0.0;
 +        }
 +
 +        sprintf(err_buf, "epsilon-rf must be >= epsilon-r");
 +        CHECK((ir->epsilon_rf < ir->epsilon_r && ir->epsilon_rf != 0) ||
 +              (ir->epsilon_r == 0));
 +        if (ir->epsilon_rf == ir->epsilon_r)
 +        {
 +            sprintf(warn_buf, "Using epsilon-rf = epsilon-r with %s does not make sense",
 +                    eel_names[ir->coulombtype]);
 +            warning(wi, warn_buf);
 +        }
 +    }
 +    /* Allow rlist>rcoulomb for tabulated long range stuff. This just
 +     * means the interaction is zero outside rcoulomb, but it helps to
 +     * provide accurate energy conservation.
 +     */
 +    if (EEL_MIGHT_BE_ZERO_AT_CUTOFF(ir->coulombtype))
 +    {
 +        if (EEL_SWITCHED(ir->coulombtype))
 +        {
 +            sprintf(err_buf,
 +                    "With coulombtype = %s rcoulomb_switch must be < rcoulomb. Or, better: Use the potential modifier options!",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->rcoulomb_switch >= ir->rcoulomb);
 +        }
 +    }
 +    else if (ir->coulombtype == eelCUT || EEL_RF(ir->coulombtype))
 +    {
 +        if (ir->cutoff_scheme == ecutsGROUP && ir->coulomb_modifier == eintmodNONE)
 +        {
 +            sprintf(err_buf, "With coulombtype = %s, rcoulomb should be >= rlist unless you use a potential modifier",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->rlist > ir->rcoulomb);
 +        }
 +    }
 +
 +    if (ir->coulombtype == eelSWITCH || ir->coulombtype == eelSHIFT ||
 +        ir->vdwtype == evdwSWITCH || ir->vdwtype == evdwSHIFT)
 +    {
 +        sprintf(warn_buf,
 +                "The switch/shift interaction settings are just for compatibility; you will get better "
 +                "performance from applying potential modifiers to your interactions!\n");
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    if (EEL_FULL(ir->coulombtype))
 +    {
 +        if (ir->coulombtype == eelPMESWITCH || ir->coulombtype == eelPMEUSER ||
 +            ir->coulombtype == eelPMEUSERSWITCH)
 +        {
 +            sprintf(err_buf, "With coulombtype = %s, rcoulomb must be <= rlist",
 +                    eel_names[ir->coulombtype]);
 +            CHECK(ir->rcoulomb > ir->rlist);
 +        }
 +        else if (ir->cutoff_scheme == ecutsGROUP && ir->coulomb_modifier == eintmodNONE)
 +        {
 +            if (ir->coulombtype == eelPME || ir->coulombtype == eelP3M_AD)
 +            {
 +                sprintf(err_buf,
 +                        "With coulombtype = %s (without modifier), rcoulomb must be equal to rlist,\n"
 +                        "or rlistlong if nstcalclr=1. For optimal energy conservation,consider using\n"
 +                        "a potential modifier.", eel_names[ir->coulombtype]);
 +                if (ir->nstcalclr == 1)
 +                {
 +                    CHECK(ir->rcoulomb != ir->rlist && ir->rcoulomb != ir->rlistlong);
 +                }
 +                else
 +                {
 +                    CHECK(ir->rcoulomb != ir->rlist);
 +                }
 +            }
 +        }
 +    }
 +
 +    if (EEL_PME(ir->coulombtype))
 +    {
 +        if (ir->pme_order < 3)
 +        {
 +            warning_error(wi, "pme-order can not be smaller than 3");
 +        }
 +    }
 +
 +    if (ir->nwall == 2 && EEL_FULL(ir->coulombtype))
 +    {
 +        if (ir->ewald_geometry == eewg3D)
 +        {
 +            sprintf(warn_buf, "With pbc=%s you should use ewald-geometry=%s",
 +                    epbc_names[ir->ePBC], eewg_names[eewg3DC]);
 +            warning(wi, warn_buf);
 +        }
 +        /* This check avoids extra pbc coding for exclusion corrections */
 +        sprintf(err_buf, "wall-ewald-zfac should be >= 2");
 +        CHECK(ir->wall_ewald_zfac < 2);
 +    }
 +
 +    if (EVDW_SWITCHED(ir->vdwtype))
 +    {
 +        sprintf(err_buf, "With vdwtype = %s rvdw-switch must be < rvdw. Or, better - use a potential modifier.",
 +                evdw_names[ir->vdwtype]);
 +        CHECK(ir->rvdw_switch >= ir->rvdw);
 +    }
 +    else if (ir->vdwtype == evdwCUT)
 +    {
 +        if (ir->cutoff_scheme == ecutsGROUP && ir->vdw_modifier == eintmodNONE)
 +        {
 +            sprintf(err_buf, "With vdwtype = %s, rvdw must be >= rlist unless you use a potential modifier", evdw_names[ir->vdwtype]);
 +            CHECK(ir->rlist > ir->rvdw);
 +        }
 +    }
 +    if (ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        if (EEL_IS_ZERO_AT_CUTOFF(ir->coulombtype)
 +            && (ir->rlistlong <= ir->rcoulomb))
 +        {
 +            sprintf(warn_buf, "For energy conservation with switch/shift potentials, %s should be 0.1 to 0.3 nm larger than rcoulomb.",
 +                    IR_TWINRANGE(*ir) ? "rlistlong" : "rlist");
 +            warning_note(wi, warn_buf);
 +        }
 +        if (EVDW_SWITCHED(ir->vdwtype) && (ir->rlistlong <= ir->rvdw))
 +        {
 +            sprintf(warn_buf, "For energy conservation with switch/shift potentials, %s should be 0.1 to 0.3 nm larger than rvdw.",
 +                    IR_TWINRANGE(*ir) ? "rlistlong" : "rlist");
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
 +    if (ir->vdwtype == evdwUSER && ir->eDispCorr != edispcNO)
 +    {
 +        warning_note(wi, "You have selected user tables with dispersion correction, the dispersion will be corrected to -C6/r^6 beyond rvdw_switch (the tabulated interaction between rvdw_switch and rvdw will not be double counted). Make sure that you really want dispersion correction to -C6/r^6.");
 +    }
 +
 +    if (ir->nstlist == -1)
 +    {
 +        sprintf(err_buf, "With nstlist=-1 rvdw and rcoulomb should be smaller than rlist to account for diffusion and possibly charge-group radii");
 +        CHECK(ir->rvdw >= ir->rlist || ir->rcoulomb >= ir->rlist);
 +    }
 +    sprintf(err_buf, "nstlist can not be smaller than -1");
 +    CHECK(ir->nstlist < -1);
 +
 +    if (ir->eI == eiLBFGS && (ir->coulombtype == eelCUT || ir->vdwtype == evdwCUT)
 +        && ir->rvdw != 0)
 +    {
 +        warning(wi, "For efficient BFGS minimization, use switch/shift/pme instead of cut-off.");
 +    }
 +
 +    if (ir->eI == eiLBFGS && ir->nbfgscorr <= 0)
 +    {
 +        warning(wi, "Using L-BFGS with nbfgscorr<=0 just gets you steepest descent.");
 +    }
 +
 +    /* ENERGY CONSERVATION */
 +    if (ir_NVE(ir) && ir->cutoff_scheme == ecutsGROUP)
 +    {
 +        if (!EVDW_MIGHT_BE_ZERO_AT_CUTOFF(ir->vdwtype) && ir->rvdw > 0 && ir->vdw_modifier == eintmodNONE)
 +        {
 +            sprintf(warn_buf, "You are using a cut-off for VdW interactions with NVE, for good energy conservation use vdwtype = %s (possibly with DispCorr)",
 +                    evdw_names[evdwSHIFT]);
 +            warning_note(wi, warn_buf);
 +        }
 +        if (!EEL_MIGHT_BE_ZERO_AT_CUTOFF(ir->coulombtype) && ir->rcoulomb > 0 && ir->coulomb_modifier == eintmodNONE)
 +        {
 +            sprintf(warn_buf, "You are using a cut-off for electrostatics with NVE, for good energy conservation use coulombtype = %s or %s",
 +                    eel_names[eelPMESWITCH], eel_names[eelRF_ZERO]);
 +            warning_note(wi, warn_buf);
 +        }
 +    }
 +
 +    /* IMPLICIT SOLVENT */
 +    if (ir->coulombtype == eelGB_NOTUSED)
 +    {
 +        ir->coulombtype      = eelCUT;
 +        ir->implicit_solvent = eisGBSA;
 +        fprintf(stderr, "Note: Old option for generalized born electrostatics given:\n"
 +                "Changing coulombtype from \"generalized-born\" to \"cut-off\" and instead\n"
 +                "setting implicit-solvent value to \"GBSA\" in input section.\n");
 +    }
 +
 +    if (ir->sa_algorithm == esaSTILL)
 +    {
 +        sprintf(err_buf, "Still SA algorithm not available yet, use %s or %s instead\n", esa_names[esaAPPROX], esa_names[esaNO]);
 +        CHECK(ir->sa_algorithm == esaSTILL);
 +    }
 +
 +    if (ir->implicit_solvent == eisGBSA)
 +    {
 +        sprintf(err_buf, "With GBSA implicit solvent, rgbradii must be equal to rlist.");
 +        CHECK(ir->rgbradii != ir->rlist);
 +
 +        if (ir->coulombtype != eelCUT)
 +        {
 +            sprintf(err_buf, "With GBSA, coulombtype must be equal to %s\n", eel_names[eelCUT]);
 +            CHECK(ir->coulombtype != eelCUT);
 +        }
 +        if (ir->vdwtype != evdwCUT)
 +        {
 +            sprintf(err_buf, "With GBSA, vdw-type must be equal to %s\n", evdw_names[evdwCUT]);
 +            CHECK(ir->vdwtype != evdwCUT);
 +        }
 +        if (ir->nstgbradii < 1)
 +        {
 +            sprintf(warn_buf, "Using GBSA with nstgbradii<1, setting nstgbradii=1");
 +            warning_note(wi, warn_buf);
 +            ir->nstgbradii = 1;
 +        }
 +        if (ir->sa_algorithm == esaNO)
 +        {
 +            sprintf(warn_buf, "No SA (non-polar) calculation requested together with GB. Are you sure this is what you want?\n");
 +            warning_note(wi, warn_buf);
 +        }
 +        if (ir->sa_surface_tension < 0 && ir->sa_algorithm != esaNO)
 +        {
 +            sprintf(warn_buf, "Value of sa_surface_tension is < 0. Changing it to 2.05016 or 2.25936 kJ/nm^2/mol for Still and HCT/OBC respectively\n");
 +            warning_note(wi, warn_buf);
 +
 +            if (ir->gb_algorithm == egbSTILL)
 +            {
 +                ir->sa_surface_tension = 0.0049 * CAL2JOULE * 100;
 +            }
 +            else
 +            {
 +                ir->sa_surface_tension = 0.0054 * CAL2JOULE * 100;
 +            }
 +        }
 +        if (ir->sa_surface_tension == 0 && ir->sa_algorithm != esaNO)
 +        {
 +            sprintf(err_buf, "Surface tension set to 0 while SA-calculation requested\n");
 +            CHECK(ir->sa_surface_tension == 0 && ir->sa_algorithm != esaNO);
 +        }
 +
 +    }
 +
 +    if (ir->bAdress)
 +    {
 +        if (ir->cutoff_scheme != ecutsGROUP)
 +        {
 +            warning_error(wi, "AdresS simulation supports only cutoff-scheme=group");
 +        }
 +        if (!EI_SD(ir->eI))
 +        {
 +            warning_error(wi, "AdresS simulation supports only stochastic dynamics");
 +        }
 +        if (ir->epc != epcNO)
 +        {
 +            warning_error(wi, "AdresS simulation does not support pressure coupling");
 +        }
 +        if (EEL_FULL(ir->coulombtype))
 +        {
 +            warning_error(wi, "AdresS simulation does not support long-range electrostatics");
 +        }
 +    }
 +}
 +
 +/* count the number of text elemets separated by whitespace in a string.
 +    str = the input string
 +    maxptr = the maximum number of allowed elements
 +    ptr = the output array of pointers to the first character of each element
 +    returns: the number of elements. */
 +int str_nelem(const char *str, int maxptr, char *ptr[])
 +{
 +    int   np = 0;
 +    char *copy0, *copy;
 +
 +    copy0 = strdup(str);
 +    copy  = copy0;
 +    ltrim(copy);
 +    while (*copy != '\0')
 +    {
 +        if (np >= maxptr)
 +        {
 +            gmx_fatal(FARGS, "Too many groups on line: '%s' (max is %d)",
 +                      str, maxptr);
 +        }
 +        if (ptr)
 +        {
 +            ptr[np] = copy;
 +        }
 +        np++;
 +        while ((*copy != '\0') && !isspace(*copy))
 +        {
 +            copy++;
 +        }
 +        if (*copy != '\0')
 +        {
 +            *copy = '\0';
 +            copy++;
 +        }
 +        ltrim(copy);
 +    }
 +    if (ptr == NULL)
 +    {
 +        sfree(copy0);
 +    }
 +
 +    return np;
 +}
 +
 +/* interpret a number of doubles from a string and put them in an array,
 +   after allocating space for them.
 +   str = the input string
 +   n = the (pre-allocated) number of doubles read
 +   r = the output array of doubles. */
 +static void parse_n_real(char *str, int *n, real **r)
 +{
 +    char *ptr[MAXPTR];
 +    int   i;
 +
 +    *n = str_nelem(str, MAXPTR, ptr);
 +
 +    snew(*r, *n);
 +    for (i = 0; i < *n; i++)
 +    {
 +        (*r)[i] = strtod(ptr[i], NULL);
 +    }
 +}
 +
 +static void do_fep_params(t_inputrec *ir, char fep_lambda[][STRLEN], char weights[STRLEN])
 +{
 +
 +    int         i, j, max_n_lambda, nweights, nfep[efptNR];
 +    t_lambda   *fep    = ir->fepvals;
 +    t_expanded *expand = ir->expandedvals;
 +    real      **count_fep_lambdas;
 +    gmx_bool    bOneLambda = TRUE;
 +
 +    snew(count_fep_lambdas, efptNR);
 +
 +    /* FEP input processing */
 +    /* first, identify the number of lambda values for each type.
 +       All that are nonzero must have the same number */
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        parse_n_real(fep_lambda[i], &(nfep[i]), &(count_fep_lambdas[i]));
 +    }
 +
 +    /* now, determine the number of components.  All must be either zero, or equal. */
 +
 +    max_n_lambda = 0;
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if (nfep[i] > max_n_lambda)
 +        {
 +            max_n_lambda = nfep[i];  /* here's a nonzero one.  All of them
 +                                        must have the same number if its not zero.*/
 +            break;
 +        }
 +    }
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if (nfep[i] == 0)
 +        {
 +            ir->fepvals->separate_dvdl[i] = FALSE;
 +        }
 +        else if (nfep[i] == max_n_lambda)
 +        {
 +            if (i != efptTEMPERATURE)  /* we treat this differently -- not really a reason to compute the derivative with
 +                                          respect to the temperature currently */
 +            {
 +                ir->fepvals->separate_dvdl[i] = TRUE;
 +            }
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "Number of lambdas (%d) for FEP type %s not equal to number of other types (%d)",
 +                      nfep[i], efpt_names[i], max_n_lambda);
 +        }
 +    }
 +    /* we don't print out dhdl if the temperature is changing, since we can't correctly define dhdl in this case */
 +    ir->fepvals->separate_dvdl[efptTEMPERATURE] = FALSE;
 +
 +    /* the number of lambdas is the number we've read in, which is either zero
 +       or the same for all */
 +    fep->n_lambda = max_n_lambda;
 +
 +    /* allocate space for the array of lambda values */
 +    snew(fep->all_lambda, efptNR);
 +    /* if init_lambda is defined, we need to set lambda */
 +    if ((fep->init_lambda > 0) && (fep->n_lambda == 0))
 +    {
 +        ir->fepvals->separate_dvdl[efptFEP] = TRUE;
 +    }
 +    /* otherwise allocate the space for all of the lambdas, and transfer the data */
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        snew(fep->all_lambda[i], fep->n_lambda);
 +        if (nfep[i] > 0)  /* if it's zero, then the count_fep_lambda arrays
 +                             are zero */
 +        {
 +            for (j = 0; j < fep->n_lambda; j++)
 +            {
 +                fep->all_lambda[i][j] = (double)count_fep_lambdas[i][j];
 +            }
 +            sfree(count_fep_lambdas[i]);
 +        }
 +    }
 +    sfree(count_fep_lambdas);
 +
 +    /* "fep-vals" is either zero or the full number. If zero, we'll need to define fep-lambdas for internal
 +       bookkeeping -- for now, init_lambda */
 +
 +    if ((nfep[efptFEP] == 0) && (fep->init_lambda >= 0))
 +    {
 +        for (i = 0; i < fep->n_lambda; i++)
 +        {
 +            fep->all_lambda[efptFEP][i] = fep->init_lambda;
 +        }
 +    }
 +
 +    /* check to see if only a single component lambda is defined, and soft core is defined.
 +       In this case, turn on coulomb soft core */
 +
 +    if (max_n_lambda == 0)
 +    {
 +        bOneLambda = TRUE;
 +    }
 +    else
 +    {
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            if ((nfep[i] != 0) && (i != efptFEP))
 +            {
 +                bOneLambda = FALSE;
 +            }
 +        }
 +    }
 +    if ((bOneLambda) && (fep->sc_alpha > 0))
 +    {
 +        fep->bScCoul = TRUE;
 +    }
 +
 +    /* Fill in the others with the efptFEP if they are not explicitly
 +       specified (i.e. nfep[i] == 0).  This means if fep is not defined,
 +       they are all zero. */
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if ((nfep[i] == 0) && (i != efptFEP))
 +        {
 +            for (j = 0; j < fep->n_lambda; j++)
 +            {
 +                fep->all_lambda[i][j] = fep->all_lambda[efptFEP][j];
 +            }
 +        }
 +    }
 +
 +
 +    /* make it easier if sc_r_power = 48 by increasing it to the 4th power, to be in the right scale. */
 +    if (fep->sc_r_power == 48)
 +    {
 +        if (fep->sc_alpha > 0.1)
 +        {
 +            gmx_fatal(FARGS, "sc_alpha (%f) for sc_r_power = 48 should usually be between 0.001 and 0.004", fep->sc_alpha);
 +        }
 +    }
 +
 +    expand = ir->expandedvals;
 +    /* now read in the weights */
 +    parse_n_real(weights, &nweights, &(expand->init_lambda_weights));
 +    if (nweights == 0)
 +    {
 +        expand->bInit_weights = FALSE;
 +        snew(expand->init_lambda_weights, fep->n_lambda); /* initialize to zero */
 +    }
 +    else if (nweights != fep->n_lambda)
 +    {
 +        gmx_fatal(FARGS, "Number of weights (%d) is not equal to number of lambda values (%d)",
 +                  nweights, fep->n_lambda);
 +    }
 +    else
 +    {
 +        expand->bInit_weights = TRUE;
 +    }
 +    if ((expand->nstexpanded < 0) && (ir->efep != efepNO))
 +    {
 +        expand->nstexpanded = fep->nstdhdl;
 +        /* if you don't specify nstexpanded when doing expanded ensemble free energy calcs, it is set to nstdhdl */
 +    }
 +    if ((expand->nstexpanded < 0) && ir->bSimTemp)
 +    {
 +        expand->nstexpanded = 2*(int)(ir->opts.tau_t[0]/ir->delta_t);
 +        /* if you don't specify nstexpanded when doing expanded ensemble simulated tempering, it is set to
 +           2*tau_t just to be careful so it's not to frequent  */
 +    }
 +}
 +
 +
 +static void do_simtemp_params(t_inputrec *ir)
 +{
 +
 +    snew(ir->simtempvals->temperatures, ir->fepvals->n_lambda);
 +    GetSimTemps(ir->fepvals->n_lambda, ir->simtempvals, ir->fepvals->all_lambda[efptTEMPERATURE]);
 +
 +    return;
 +}
 +
 +static void do_wall_params(t_inputrec *ir,
 +                           char *wall_atomtype, char *wall_density,
 +                           t_gromppopts *opts)
 +{
 +    int    nstr, i;
 +    char  *names[MAXPTR];
 +    double dbl;
 +
 +    opts->wall_atomtype[0] = NULL;
 +    opts->wall_atomtype[1] = NULL;
 +
 +    ir->wall_atomtype[0] = -1;
 +    ir->wall_atomtype[1] = -1;
 +    ir->wall_density[0]  = 0;
 +    ir->wall_density[1]  = 0;
 +
 +    if (ir->nwall > 0)
 +    {
 +        nstr = str_nelem(wall_atomtype, MAXPTR, names);
 +        if (nstr != ir->nwall)
 +        {
 +            gmx_fatal(FARGS, "Expected %d elements for wall_atomtype, found %d",
 +                      ir->nwall, nstr);
 +        }
 +        for (i = 0; i < ir->nwall; i++)
 +        {
 +            opts->wall_atomtype[i] = strdup(names[i]);
 +        }
 +
 +        if (ir->wall_type == ewt93 || ir->wall_type == ewt104)
 +        {
 +            nstr = str_nelem(wall_density, MAXPTR, names);
 +            if (nstr != ir->nwall)
 +            {
 +                gmx_fatal(FARGS, "Expected %d elements for wall-density, found %d", ir->nwall, nstr);
 +            }
 +            for (i = 0; i < ir->nwall; i++)
 +            {
 +                sscanf(names[i], "%lf", &dbl);
 +                if (dbl <= 0)
 +                {
 +                    gmx_fatal(FARGS, "wall-density[%d] = %f\n", i, dbl);
 +                }
 +                ir->wall_density[i] = dbl;
 +            }
 +        }
 +    }
 +}
 +
 +static void add_wall_energrps(gmx_groups_t *groups, int nwall, t_symtab *symtab)
 +{
 +    int     i;
 +    t_grps *grps;
 +    char    str[STRLEN];
 +
 +    if (nwall > 0)
 +    {
 +        srenew(groups->grpname, groups->ngrpname+nwall);
 +        grps = &(groups->grps[egcENER]);
 +        srenew(grps->nm_ind, grps->nr+nwall);
 +        for (i = 0; i < nwall; i++)
 +        {
 +            sprintf(str, "wall%d", i);
 +            groups->grpname[groups->ngrpname] = put_symtab(symtab, str);
 +            grps->nm_ind[grps->nr++]          = groups->ngrpname++;
 +        }
 +    }
 +}
 +
 +void read_expandedparams(int *ninp_p, t_inpfile **inp_p,
 +                         t_expanded *expand, warninp_t wi)
 +{
 +    int        ninp, nerror = 0;
 +    t_inpfile *inp;
 +
 +    ninp   = *ninp_p;
 +    inp    = *inp_p;
 +
 +    /* read expanded ensemble parameters */
 +    CCTYPE ("expanded ensemble variables");
 +    ITYPE ("nstexpanded", expand->nstexpanded, -1);
 +    EETYPE("lmc-stats", expand->elamstats, elamstats_names);
 +    EETYPE("lmc-move", expand->elmcmove, elmcmove_names);
 +    EETYPE("lmc-weights-equil", expand->elmceq, elmceq_names);
 +    ITYPE ("weight-equil-number-all-lambda", expand->equil_n_at_lam, -1);
 +    ITYPE ("weight-equil-number-samples", expand->equil_samples, -1);
 +    ITYPE ("weight-equil-number-steps", expand->equil_steps, -1);
 +    RTYPE ("weight-equil-wl-delta", expand->equil_wl_delta, -1);
 +    RTYPE ("weight-equil-count-ratio", expand->equil_ratio, -1);
 +    CCTYPE("Seed for Monte Carlo in lambda space");
 +    ITYPE ("lmc-seed", expand->lmc_seed, -1);
 +    RTYPE ("mc-temperature", expand->mc_temp, -1);
 +    ITYPE ("lmc-repeats", expand->lmc_repeats, 1);
 +    ITYPE ("lmc-gibbsdelta", expand->gibbsdeltalam, -1);
 +    ITYPE ("lmc-forced-nstart", expand->lmc_forced_nstart, 0);
 +    EETYPE("symmetrized-transition-matrix", expand->bSymmetrizedTMatrix, yesno_names);
 +    ITYPE("nst-transition-matrix", expand->nstTij, -1);
 +    ITYPE ("mininum-var-min", expand->minvarmin, 100); /*default is reasonable */
 +    ITYPE ("weight-c-range", expand->c_range, 0);      /* default is just C=0 */
 +    RTYPE ("wl-scale", expand->wl_scale, 0.8);
 +    RTYPE ("wl-ratio", expand->wl_ratio, 0.8);
 +    RTYPE ("init-wl-delta", expand->init_wl_delta, 1.0);
 +    EETYPE("wl-oneovert", expand->bWLoneovert, yesno_names);
 +
 +    *ninp_p   = ninp;
 +    *inp_p    = inp;
 +
 +    return;
 +}
 +
 +void get_ir(const char *mdparin, const char *mdparout,
 +            t_inputrec *ir, t_gromppopts *opts,
 +            warninp_t wi)
 +{
 +    char       *dumstr[2];
 +    double      dumdub[2][6];
 +    t_inpfile  *inp;
 +    const char *tmp;
 +    int         i, j, m, ninp;
 +    char        warn_buf[STRLEN];
 +    t_lambda   *fep    = ir->fepvals;
 +    t_expanded *expand = ir->expandedvals;
 +
 +    inp = read_inpfile(mdparin, &ninp, NULL, wi);
 +
 +    snew(dumstr[0], STRLEN);
 +    snew(dumstr[1], STRLEN);
 +
 +    /* remove the following deprecated commands */
 +    REM_TYPE("title");
 +    REM_TYPE("cpp");
 +    REM_TYPE("domain-decomposition");
 +    REM_TYPE("andersen-seed");
 +    REM_TYPE("dihre");
 +    REM_TYPE("dihre-fc");
 +    REM_TYPE("dihre-tau");
 +    REM_TYPE("nstdihreout");
 +    REM_TYPE("nstcheckpoint");
 +
 +    /* replace the following commands with the clearer new versions*/
 +    REPL_TYPE("unconstrained-start", "continuation");
 +    REPL_TYPE("foreign-lambda", "fep-lambdas");
 +
 +    CCTYPE ("VARIOUS PREPROCESSING OPTIONS");
 +    CTYPE ("Preprocessor information: use cpp syntax.");
 +    CTYPE ("e.g.: -I/home/joe/doe -I/home/mary/roe");
 +    STYPE ("include", opts->include,  NULL);
 +    CTYPE ("e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)");
 +    STYPE ("define",  opts->define,   NULL);
 +
 +    CCTYPE ("RUN CONTROL PARAMETERS");
 +    EETYPE("integrator",  ir->eI,         ei_names);
 +    CTYPE ("Start time and timestep in ps");
 +    RTYPE ("tinit",   ir->init_t, 0.0);
 +    RTYPE ("dt",      ir->delta_t,    0.001);
 +    STEPTYPE ("nsteps",   ir->nsteps,     0);
 +    CTYPE ("For exact run continuation or redoing part of a run");
 +    STEPTYPE ("init-step", ir->init_step,  0);
 +    CTYPE ("Part index is updated automatically on checkpointing (keeps files separate)");
 +    ITYPE ("simulation-part", ir->simulation_part, 1);
 +    CTYPE ("mode for center of mass motion removal");
 +    EETYPE("comm-mode",   ir->comm_mode,  ecm_names);
 +    CTYPE ("number of steps for center of mass motion removal");
 +    ITYPE ("nstcomm", ir->nstcomm,    100);
 +    CTYPE ("group(s) for center of mass motion removal");
 +    STYPE ("comm-grps",   vcm,            NULL);
 +
 +    CCTYPE ("LANGEVIN DYNAMICS OPTIONS");
 +    CTYPE ("Friction coefficient (amu/ps) and random seed");
 +    RTYPE ("bd-fric",     ir->bd_fric,    0.0);
 +    ITYPE ("ld-seed",     ir->ld_seed,    1993);
 +
 +    /* Em stuff */
 +    CCTYPE ("ENERGY MINIMIZATION OPTIONS");
 +    CTYPE ("Force tolerance and initial step-size");
 +    RTYPE ("emtol",       ir->em_tol,     10.0);
 +    RTYPE ("emstep",      ir->em_stepsize, 0.01);
 +    CTYPE ("Max number of iterations in relax-shells");
 +    ITYPE ("niter",       ir->niter,      20);
 +    CTYPE ("Step size (ps^2) for minimization of flexible constraints");
 +    RTYPE ("fcstep",      ir->fc_stepsize, 0);
 +    CTYPE ("Frequency of steepest descents steps when doing CG");
 +    ITYPE ("nstcgsteep",  ir->nstcgsteep, 1000);
 +    ITYPE ("nbfgscorr",   ir->nbfgscorr,  10);
 +
 +    CCTYPE ("TEST PARTICLE INSERTION OPTIONS");
 +    RTYPE ("rtpi",    ir->rtpi,   0.05);
 +
 +    /* Output options */
 +    CCTYPE ("OUTPUT CONTROL OPTIONS");
 +    CTYPE ("Output frequency for coords (x), velocities (v) and forces (f)");
 +    ITYPE ("nstxout", ir->nstxout,    0);
 +    ITYPE ("nstvout", ir->nstvout,    0);
 +    ITYPE ("nstfout", ir->nstfout,    0);
 +    ir->nstcheckpoint = 1000;
 +    CTYPE ("Output frequency for energies to log file and energy file");
 +    ITYPE ("nstlog",  ir->nstlog, 1000);
 +    ITYPE ("nstcalcenergy", ir->nstcalcenergy, 100);
 +    ITYPE ("nstenergy",   ir->nstenergy,  1000);
 +    CTYPE ("Output frequency and precision for .xtc file");
 +    ITYPE ("nstxtcout",   ir->nstxtcout,  0);
 +    RTYPE ("xtc-precision", ir->xtcprec,   1000.0);
 +    CTYPE ("This selects the subset of atoms for the .xtc file. You can");
 +    CTYPE ("select multiple groups. By default all atoms will be written.");
 +    STYPE ("xtc-grps",    xtc_grps,       NULL);
 +    CTYPE ("Selection of energy groups");
 +    STYPE ("energygrps",  energy,         NULL);
 +
 +    /* Neighbor searching */
 +    CCTYPE ("NEIGHBORSEARCHING PARAMETERS");
 +    CTYPE ("cut-off scheme (group: using charge groups, Verlet: particle based cut-offs)");
 +    EETYPE("cutoff-scheme",     ir->cutoff_scheme,    ecutscheme_names);
 +    CTYPE ("nblist update frequency");
 +    ITYPE ("nstlist", ir->nstlist,    10);
 +    CTYPE ("ns algorithm (simple or grid)");
 +    EETYPE("ns-type",     ir->ns_type,    ens_names);
 +    /* set ndelta to the optimal value of 2 */
 +    ir->ndelta = 2;
 +    CTYPE ("Periodic boundary conditions: xyz, no, xy");
 +    EETYPE("pbc",         ir->ePBC,       epbc_names);
 +    EETYPE("periodic-molecules", ir->bPeriodicMols, yesno_names);
 +    CTYPE ("Allowed energy drift due to the Verlet buffer in kJ/mol/ps per atom,");
 +    CTYPE ("a value of -1 means: use rlist");
 +    RTYPE("verlet-buffer-drift", ir->verletbuf_drift,    0.005);
 +    CTYPE ("nblist cut-off");
 +    RTYPE ("rlist",   ir->rlist,  1.0);
 +    CTYPE ("long-range cut-off for switched potentials");
 +    RTYPE ("rlistlong",   ir->rlistlong,  -1);
 +    ITYPE ("nstcalclr",   ir->nstcalclr,  -1);
 +
 +    /* Electrostatics */
 +    CCTYPE ("OPTIONS FOR ELECTROSTATICS AND VDW");
 +    CTYPE ("Method for doing electrostatics");
 +    EETYPE("coulombtype", ir->coulombtype,    eel_names);
 +    EETYPE("coulomb-modifier",    ir->coulomb_modifier,    eintmod_names);
 +    CTYPE ("cut-off lengths");
 +    RTYPE ("rcoulomb-switch", ir->rcoulomb_switch,    0.0);
 +    RTYPE ("rcoulomb",    ir->rcoulomb,   1.0);
 +    CTYPE ("Relative dielectric constant for the medium and the reaction field");
 +    RTYPE ("epsilon-r",   ir->epsilon_r,  1.0);
 +    RTYPE ("epsilon-rf",  ir->epsilon_rf, 0.0);
 +    CTYPE ("Method for doing Van der Waals");
 +    EETYPE("vdw-type",    ir->vdwtype,    evdw_names);
 +    EETYPE("vdw-modifier",    ir->vdw_modifier,    eintmod_names);
 +    CTYPE ("cut-off lengths");
 +    RTYPE ("rvdw-switch", ir->rvdw_switch,    0.0);
 +    RTYPE ("rvdw",    ir->rvdw,   1.0);
 +    CTYPE ("Apply long range dispersion corrections for Energy and Pressure");
 +    EETYPE("DispCorr",    ir->eDispCorr,  edispc_names);
 +    CTYPE ("Extension of the potential lookup tables beyond the cut-off");
 +    RTYPE ("table-extension", ir->tabext, 1.0);
 +    CTYPE ("Separate tables between energy group pairs");
 +    STYPE ("energygrp-table", egptable,   NULL);
 +    CTYPE ("Spacing for the PME/PPPM FFT grid");
 +    RTYPE ("fourierspacing", ir->fourier_spacing, 0.12);
 +    CTYPE ("FFT grid size, when a value is 0 fourierspacing will be used");
 +    ITYPE ("fourier-nx",  ir->nkx,         0);
 +    ITYPE ("fourier-ny",  ir->nky,         0);
 +    ITYPE ("fourier-nz",  ir->nkz,         0);
 +    CTYPE ("EWALD/PME/PPPM parameters");
 +    ITYPE ("pme-order",   ir->pme_order,   4);
 +    RTYPE ("ewald-rtol",  ir->ewald_rtol, 0.00001);
 +    EETYPE("ewald-geometry", ir->ewald_geometry, eewg_names);
 +    RTYPE ("epsilon-surface", ir->epsilon_surface, 0.0);
 +    EETYPE("optimize-fft", ir->bOptFFT,  yesno_names);
 +
 +    CCTYPE("IMPLICIT SOLVENT ALGORITHM");
 +    EETYPE("implicit-solvent", ir->implicit_solvent, eis_names);
 +
 +    CCTYPE ("GENERALIZED BORN ELECTROSTATICS");
 +    CTYPE ("Algorithm for calculating Born radii");
 +    EETYPE("gb-algorithm", ir->gb_algorithm, egb_names);
 +    CTYPE ("Frequency of calculating the Born radii inside rlist");
 +    ITYPE ("nstgbradii", ir->nstgbradii, 1);
 +    CTYPE ("Cutoff for Born radii calculation; the contribution from atoms");
 +    CTYPE ("between rlist and rgbradii is updated every nstlist steps");
 +    RTYPE ("rgbradii",  ir->rgbradii, 1.0);
 +    CTYPE ("Dielectric coefficient of the implicit solvent");
 +    RTYPE ("gb-epsilon-solvent", ir->gb_epsilon_solvent, 80.0);
 +    CTYPE ("Salt concentration in M for Generalized Born models");
 +    RTYPE ("gb-saltconc",  ir->gb_saltconc, 0.0);
 +    CTYPE ("Scaling factors used in the OBC GB model. Default values are OBC(II)");
 +    RTYPE ("gb-obc-alpha", ir->gb_obc_alpha, 1.0);
 +    RTYPE ("gb-obc-beta", ir->gb_obc_beta, 0.8);
 +    RTYPE ("gb-obc-gamma", ir->gb_obc_gamma, 4.85);
 +    RTYPE ("gb-dielectric-offset", ir->gb_dielectric_offset, 0.009);
 +    EETYPE("sa-algorithm", ir->sa_algorithm, esa_names);
 +    CTYPE ("Surface tension (kJ/mol/nm^2) for the SA (nonpolar surface) part of GBSA");
 +    CTYPE ("The value -1 will set default value for Still/HCT/OBC GB-models.");
 +    RTYPE ("sa-surface-tension", ir->sa_surface_tension, -1);
 +
 +    /* Coupling stuff */
 +    CCTYPE ("OPTIONS FOR WEAK COUPLING ALGORITHMS");
 +    CTYPE ("Temperature coupling");
 +    EETYPE("tcoupl",  ir->etc,        etcoupl_names);
 +    ITYPE ("nsttcouple", ir->nsttcouple,  -1);
 +    ITYPE("nh-chain-length",     ir->opts.nhchainlength, NHCHAINLENGTH);
 +    EETYPE("print-nose-hoover-chain-variables", ir->bPrintNHChains, yesno_names);
 +    CTYPE ("Groups to couple separately");
 +    STYPE ("tc-grps",     tcgrps,         NULL);
 +    CTYPE ("Time constant (ps) and reference temperature (K)");
 +    STYPE ("tau-t",   tau_t,      NULL);
 +    STYPE ("ref-t",   ref_t,      NULL);
 +    CTYPE ("pressure coupling");
 +    EETYPE("pcoupl",  ir->epc,        epcoupl_names);
 +    EETYPE("pcoupltype",  ir->epct,       epcoupltype_names);
 +    ITYPE ("nstpcouple", ir->nstpcouple,  -1);
 +    CTYPE ("Time constant (ps), compressibility (1/bar) and reference P (bar)");
 +    RTYPE ("tau-p",   ir->tau_p,  1.0);
 +    STYPE ("compressibility", dumstr[0],  NULL);
 +    STYPE ("ref-p",       dumstr[1],      NULL);
 +    CTYPE ("Scaling of reference coordinates, No, All or COM");
 +    EETYPE ("refcoord-scaling", ir->refcoord_scaling, erefscaling_names);
 +
 +    /* QMMM */
 +    CCTYPE ("OPTIONS FOR QMMM calculations");
 +    EETYPE("QMMM", ir->bQMMM, yesno_names);
 +    CTYPE ("Groups treated Quantum Mechanically");
 +    STYPE ("QMMM-grps",  QMMM,          NULL);
 +    CTYPE ("QM method");
 +    STYPE("QMmethod",     QMmethod, NULL);
 +    CTYPE ("QMMM scheme");
 +    EETYPE("QMMMscheme",  ir->QMMMscheme,    eQMMMscheme_names);
 +    CTYPE ("QM basisset");
 +    STYPE("QMbasis",      QMbasis, NULL);
 +    CTYPE ("QM charge");
 +    STYPE ("QMcharge",    QMcharge, NULL);
 +    CTYPE ("QM multiplicity");
 +    STYPE ("QMmult",      QMmult, NULL);
 +    CTYPE ("Surface Hopping");
 +    STYPE ("SH",          bSH, NULL);
 +    CTYPE ("CAS space options");
 +    STYPE ("CASorbitals",      CASorbitals,   NULL);
 +    STYPE ("CASelectrons",     CASelectrons,  NULL);
 +    STYPE ("SAon", SAon, NULL);
 +    STYPE ("SAoff", SAoff, NULL);
 +    STYPE ("SAsteps",  SAsteps, NULL);
 +    CTYPE ("Scale factor for MM charges");
 +    RTYPE ("MMChargeScaleFactor", ir->scalefactor, 1.0);
 +    CTYPE ("Optimization of QM subsystem");
 +    STYPE ("bOPT",          bOPT, NULL);
 +    STYPE ("bTS",          bTS, NULL);
 +
 +    /* Simulated annealing */
 +    CCTYPE("SIMULATED ANNEALING");
 +    CTYPE ("Type of annealing for each temperature group (no/single/periodic)");
 +    STYPE ("annealing",   anneal,      NULL);
 +    CTYPE ("Number of time points to use for specifying annealing in each group");
 +    STYPE ("annealing-npoints", anneal_npoints, NULL);
 +    CTYPE ("List of times at the annealing points for each group");
 +    STYPE ("annealing-time",       anneal_time,       NULL);
 +    CTYPE ("Temp. at each annealing point, for each group.");
 +    STYPE ("annealing-temp",  anneal_temp,  NULL);
 +
 +    /* Startup run */
 +    CCTYPE ("GENERATE VELOCITIES FOR STARTUP RUN");
 +    EETYPE("gen-vel",     opts->bGenVel,  yesno_names);
 +    RTYPE ("gen-temp",    opts->tempi,    300.0);
 +    ITYPE ("gen-seed",    opts->seed,     173529);
 +
 +    /* Shake stuff */
 +    CCTYPE ("OPTIONS FOR BONDS");
 +    EETYPE("constraints", opts->nshake,   constraints);
 +    CTYPE ("Type of constraint algorithm");
 +    EETYPE("constraint-algorithm",  ir->eConstrAlg, econstr_names);
 +    CTYPE ("Do not constrain the start configuration");
 +    EETYPE("continuation", ir->bContinuation, yesno_names);
 +    CTYPE ("Use successive overrelaxation to reduce the number of shake iterations");
 +    EETYPE("Shake-SOR", ir->bShakeSOR, yesno_names);
 +    CTYPE ("Relative tolerance of shake");
 +    RTYPE ("shake-tol", ir->shake_tol, 0.0001);
 +    CTYPE ("Highest order in the expansion of the constraint coupling matrix");
 +    ITYPE ("lincs-order", ir->nProjOrder, 4);
 +    CTYPE ("Number of iterations in the final step of LINCS. 1 is fine for");
 +    CTYPE ("normal simulations, but use 2 to conserve energy in NVE runs.");
 +    CTYPE ("For energy minimization with constraints it should be 4 to 8.");
 +    ITYPE ("lincs-iter", ir->nLincsIter, 1);
 +    CTYPE ("Lincs will write a warning to the stderr if in one step a bond");
 +    CTYPE ("rotates over more degrees than");
 +    RTYPE ("lincs-warnangle", ir->LincsWarnAngle, 30.0);
 +    CTYPE ("Convert harmonic bonds to morse potentials");
 +    EETYPE("morse",       opts->bMorse, yesno_names);
 +
 +    /* Energy group exclusions */
 +    CCTYPE ("ENERGY GROUP EXCLUSIONS");
 +    CTYPE ("Pairs of energy groups for which all non-bonded interactions are excluded");
 +    STYPE ("energygrp-excl", egpexcl,     NULL);
 +
 +    /* Walls */
 +    CCTYPE ("WALLS");
 +    CTYPE ("Number of walls, type, atom types, densities and box-z scale factor for Ewald");
 +    ITYPE ("nwall", ir->nwall, 0);
 +    EETYPE("wall-type",     ir->wall_type,   ewt_names);
 +    RTYPE ("wall-r-linpot", ir->wall_r_linpot, -1);
 +    STYPE ("wall-atomtype", wall_atomtype, NULL);
 +    STYPE ("wall-density",  wall_density,  NULL);
 +    RTYPE ("wall-ewald-zfac", ir->wall_ewald_zfac, 3);
 +
 +    /* COM pulling */
 +    CCTYPE("COM PULLING");
 +    CTYPE("Pull type: no, umbrella, constraint or constant-force");
 +    EETYPE("pull",          ir->ePull, epull_names);
 +    if (ir->ePull != epullNO)
 +    {
 +        snew(ir->pull, 1);
 +        pull_grp = read_pullparams(&ninp, &inp, ir->pull, &opts->pull_start, wi);
 +    }
 +
 +    /* Enforced rotation */
 +    CCTYPE("ENFORCED ROTATION");
 +    CTYPE("Enforced rotation: No or Yes");
 +    EETYPE("rotation",       ir->bRot, yesno_names);
 +    if (ir->bRot)
 +    {
 +        snew(ir->rot, 1);
 +        rot_grp = read_rotparams(&ninp, &inp, ir->rot, wi);
 +    }
 +
 +    /* Refinement */
 +    CCTYPE("NMR refinement stuff");
 +    CTYPE ("Distance restraints type: No, Simple or Ensemble");
 +    EETYPE("disre",       ir->eDisre,     edisre_names);
 +    CTYPE ("Force weighting of pairs in one distance restraint: Conservative or Equal");
 +    EETYPE("disre-weighting", ir->eDisreWeighting, edisreweighting_names);
 +    CTYPE ("Use sqrt of the time averaged times the instantaneous violation");
 +    EETYPE("disre-mixed", ir->bDisreMixed, yesno_names);
 +    RTYPE ("disre-fc",    ir->dr_fc,  1000.0);
 +    RTYPE ("disre-tau",   ir->dr_tau, 0.0);
 +    CTYPE ("Output frequency for pair distances to energy file");
 +    ITYPE ("nstdisreout", ir->nstdisreout, 100);
 +    CTYPE ("Orientation restraints: No or Yes");
 +    EETYPE("orire",       opts->bOrire,   yesno_names);
 +    CTYPE ("Orientation restraints force constant and tau for time averaging");
 +    RTYPE ("orire-fc",    ir->orires_fc,  0.0);
 +    RTYPE ("orire-tau",   ir->orires_tau, 0.0);
 +    STYPE ("orire-fitgrp", orirefitgrp,    NULL);
 +    CTYPE ("Output frequency for trace(SD) and S to energy file");
 +    ITYPE ("nstorireout", ir->nstorireout, 100);
 +
 +    /* free energy variables */
 +    CCTYPE ("Free energy variables");
 +    EETYPE("free-energy", ir->efep, efep_names);
 +    STYPE ("couple-moltype",  couple_moltype,  NULL);
 +    EETYPE("couple-lambda0", opts->couple_lam0, couple_lam);
 +    EETYPE("couple-lambda1", opts->couple_lam1, couple_lam);
 +    EETYPE("couple-intramol", opts->bCoupleIntra, yesno_names);
 +
 +    RTYPE ("init-lambda", fep->init_lambda, -1); /* start with -1 so
 +                                                    we can recognize if
 +                                                    it was not entered */
 +    ITYPE ("init-lambda-state", fep->init_fep_state, -1);
 +    RTYPE ("delta-lambda", fep->delta_lambda, 0.0);
 +    ITYPE ("nstdhdl", fep->nstdhdl, 50);
 +    STYPE ("fep-lambdas", fep_lambda[efptFEP], NULL);
 +    STYPE ("mass-lambdas", fep_lambda[efptMASS], NULL);
 +    STYPE ("coul-lambdas", fep_lambda[efptCOUL], NULL);
 +    STYPE ("vdw-lambdas", fep_lambda[efptVDW], NULL);
 +    STYPE ("bonded-lambdas", fep_lambda[efptBONDED], NULL);
 +    STYPE ("restraint-lambdas", fep_lambda[efptRESTRAINT], NULL);
 +    STYPE ("temperature-lambdas", fep_lambda[efptTEMPERATURE], NULL);
 +    ITYPE ("calc-lambda-neighbors", fep->lambda_neighbors, 1);
 +    STYPE ("init-lambda-weights", lambda_weights, NULL);
 +    EETYPE("dhdl-print-energy", fep->bPrintEnergy, yesno_names);
 +    RTYPE ("sc-alpha", fep->sc_alpha, 0.0);
 +    ITYPE ("sc-power", fep->sc_power, 1);
 +    RTYPE ("sc-r-power", fep->sc_r_power, 6.0);
 +    RTYPE ("sc-sigma", fep->sc_sigma, 0.3);
 +    EETYPE("sc-coul", fep->bScCoul, yesno_names);
 +    ITYPE ("dh_hist_size", fep->dh_hist_size, 0);
 +    RTYPE ("dh_hist_spacing", fep->dh_hist_spacing, 0.1);
 +    EETYPE("separate-dhdl-file", fep->separate_dhdl_file,
 +           separate_dhdl_file_names);
 +    EETYPE("dhdl-derivatives", fep->dhdl_derivatives, dhdl_derivatives_names);
 +    ITYPE ("dh_hist_size", fep->dh_hist_size, 0);
 +    RTYPE ("dh_hist_spacing", fep->dh_hist_spacing, 0.1);
 +
 +    /* Non-equilibrium MD stuff */
 +    CCTYPE("Non-equilibrium MD stuff");
 +    STYPE ("acc-grps",    accgrps,        NULL);
 +    STYPE ("accelerate",  acc,            NULL);
 +    STYPE ("freezegrps",  freeze,         NULL);
 +    STYPE ("freezedim",   frdim,          NULL);
 +    RTYPE ("cos-acceleration", ir->cos_accel, 0);
 +    STYPE ("deform",      deform,         NULL);
 +
 +    /* simulated tempering variables */
 +    CCTYPE("simulated tempering variables");
 +    EETYPE("simulated-tempering", ir->bSimTemp, yesno_names);
 +    EETYPE("simulated-tempering-scaling", ir->simtempvals->eSimTempScale, esimtemp_names);
 +    RTYPE("sim-temp-low", ir->simtempvals->simtemp_low, 300.0);
 +    RTYPE("sim-temp-high", ir->simtempvals->simtemp_high, 300.0);
 +
 +    /* expanded ensemble variables */
 +    if (ir->efep == efepEXPANDED || ir->bSimTemp)
 +    {
 +        read_expandedparams(&ninp, &inp, expand, wi);
 +    }
 +
 +    /* Electric fields */
 +    CCTYPE("Electric fields");
 +    CTYPE ("Format is number of terms (int) and for all terms an amplitude (real)");
 +    CTYPE ("and a phase angle (real)");
 +    STYPE ("E-x",     efield_x,   NULL);
 +    STYPE ("E-xt",    efield_xt,  NULL);
 +    STYPE ("E-y",     efield_y,   NULL);
 +    STYPE ("E-yt",    efield_yt,  NULL);
 +    STYPE ("E-z",     efield_z,   NULL);
 +    STYPE ("E-zt",    efield_zt,  NULL);
 +
 +    /* AdResS defined thingies */
 +    CCTYPE ("AdResS parameters");
 +    EETYPE("adress",       ir->bAdress, yesno_names);
 +    if (ir->bAdress)
 +    {
 +        snew(ir->adress, 1);
 +        read_adressparams(&ninp, &inp, ir->adress, wi);
 +    }
 +
 +    /* User defined thingies */
 +    CCTYPE ("User defined thingies");
 +    STYPE ("user1-grps",  user1,          NULL);
 +    STYPE ("user2-grps",  user2,          NULL);
 +    ITYPE ("userint1",    ir->userint1,   0);
 +    ITYPE ("userint2",    ir->userint2,   0);
 +    ITYPE ("userint3",    ir->userint3,   0);
 +    ITYPE ("userint4",    ir->userint4,   0);
 +    RTYPE ("userreal1",   ir->userreal1,  0);
 +    RTYPE ("userreal2",   ir->userreal2,  0);
 +    RTYPE ("userreal3",   ir->userreal3,  0);
 +    RTYPE ("userreal4",   ir->userreal4,  0);
 +#undef CTYPE
 +
 +    write_inpfile(mdparout, ninp, inp, FALSE, wi);
 +    for (i = 0; (i < ninp); i++)
 +    {
 +        sfree(inp[i].name);
 +        sfree(inp[i].value);
 +    }
 +    sfree(inp);
 +
 +    /* Process options if necessary */
 +    for (m = 0; m < 2; m++)
 +    {
 +        for (i = 0; i < 2*DIM; i++)
 +        {
 +            dumdub[m][i] = 0.0;
 +        }
 +        if (ir->epc)
 +        {
 +            switch (ir->epct)
 +            {
 +                case epctISOTROPIC:
 +                    if (sscanf(dumstr[m], "%lf", &(dumdub[m][XX])) != 1)
 +                    {
 +                        warning_error(wi, "Pressure coupling not enough values (I need 1)");
 +                    }
 +                    dumdub[m][YY] = dumdub[m][ZZ] = dumdub[m][XX];
 +                    break;
 +                case epctSEMIISOTROPIC:
 +                case epctSURFACETENSION:
 +                    if (sscanf(dumstr[m], "%lf%lf",
 +                               &(dumdub[m][XX]), &(dumdub[m][ZZ])) != 2)
 +                    {
 +                        warning_error(wi, "Pressure coupling not enough values (I need 2)");
 +                    }
 +                    dumdub[m][YY] = dumdub[m][XX];
 +                    break;
 +                case epctANISOTROPIC:
 +                    if (sscanf(dumstr[m], "%lf%lf%lf%lf%lf%lf",
 +                               &(dumdub[m][XX]), &(dumdub[m][YY]), &(dumdub[m][ZZ]),
 +                               &(dumdub[m][3]), &(dumdub[m][4]), &(dumdub[m][5])) != 6)
 +                    {
 +                        warning_error(wi, "Pressure coupling not enough values (I need 6)");
 +                    }
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "Pressure coupling type %s not implemented yet",
 +                              epcoupltype_names[ir->epct]);
 +            }
 +        }
 +    }
 +    clear_mat(ir->ref_p);
 +    clear_mat(ir->compress);
 +    for (i = 0; i < DIM; i++)
 +    {
 +        ir->ref_p[i][i]    = dumdub[1][i];
 +        ir->compress[i][i] = dumdub[0][i];
 +    }
 +    if (ir->epct == epctANISOTROPIC)
 +    {
 +        ir->ref_p[XX][YY] = dumdub[1][3];
 +        ir->ref_p[XX][ZZ] = dumdub[1][4];
 +        ir->ref_p[YY][ZZ] = dumdub[1][5];
 +        if (ir->ref_p[XX][YY] != 0 && ir->ref_p[XX][ZZ] != 0 && ir->ref_p[YY][ZZ] != 0)
 +        {
 +            warning(wi, "All off-diagonal reference pressures are non-zero. Are you sure you want to apply a threefold shear stress?\n");
 +        }
 +        ir->compress[XX][YY] = dumdub[0][3];
 +        ir->compress[XX][ZZ] = dumdub[0][4];
 +        ir->compress[YY][ZZ] = dumdub[0][5];
 +        for (i = 0; i < DIM; i++)
 +        {
 +            for (m = 0; m < i; m++)
 +            {
 +                ir->ref_p[i][m]    = ir->ref_p[m][i];
 +                ir->compress[i][m] = ir->compress[m][i];
 +            }
 +        }
 +    }
 +
 +    if (ir->comm_mode == ecmNO)
 +    {
 +        ir->nstcomm = 0;
 +    }
 +
 +    opts->couple_moltype = NULL;
 +    if (strlen(couple_moltype) > 0)
 +    {
 +        if (ir->efep != efepNO)
 +        {
 +            opts->couple_moltype = strdup(couple_moltype);
 +            if (opts->couple_lam0 == opts->couple_lam1)
 +            {
 +                warning(wi, "The lambda=0 and lambda=1 states for coupling are identical");
 +            }
 +            if (ir->eI == eiMD && (opts->couple_lam0 == ecouplamNONE ||
 +                                   opts->couple_lam1 == ecouplamNONE))
 +            {
 +                warning(wi, "For proper sampling of the (nearly) decoupled state, stochastic dynamics should be used");
 +            }
 +        }
 +        else
 +        {
 +            warning(wi, "Can not couple a molecule with free_energy = no");
 +        }
 +    }
 +    /* FREE ENERGY AND EXPANDED ENSEMBLE OPTIONS */
 +    if (ir->efep != efepNO)
 +    {
 +        if (fep->delta_lambda > 0)
 +        {
 +            ir->efep = efepSLOWGROWTH;
 +        }
 +    }
 +
 +    if (ir->bSimTemp)
 +    {
 +        fep->bPrintEnergy = TRUE;
 +        /* always print out the energy to dhdl if we are doing expanded ensemble, since we need the total energy
 +           if the temperature is changing. */
 +    }
 +
 +    if ((ir->efep != efepNO) || ir->bSimTemp)
 +    {
 +        ir->bExpanded = FALSE;
 +        if ((ir->efep == efepEXPANDED) || ir->bSimTemp)
 +        {
 +            ir->bExpanded = TRUE;
 +        }
 +        do_fep_params(ir, fep_lambda, lambda_weights);
 +        if (ir->bSimTemp) /* done after fep params */
 +        {
 +            do_simtemp_params(ir);
 +        }
 +    }
 +    else
 +    {
 +        ir->fepvals->n_lambda = 0;
 +    }
 +
 +    /* WALL PARAMETERS */
 +
 +    do_wall_params(ir, wall_atomtype, wall_density, opts);
 +
 +    /* ORIENTATION RESTRAINT PARAMETERS */
 +
 +    if (opts->bOrire && str_nelem(orirefitgrp, MAXPTR, NULL) != 1)
 +    {
 +        warning_error(wi, "ERROR: Need one orientation restraint fit group\n");
 +    }
 +
 +    /* DEFORMATION PARAMETERS */
 +
 +    clear_mat(ir->deform);
 +    for (i = 0; i < 6; i++)
 +    {
 +        dumdub[0][i] = 0;
 +    }
 +    m = sscanf(deform, "%lf %lf %lf %lf %lf %lf",
 +               &(dumdub[0][0]), &(dumdub[0][1]), &(dumdub[0][2]),
 +               &(dumdub[0][3]), &(dumdub[0][4]), &(dumdub[0][5]));
 +    for (i = 0; i < 3; i++)
 +    {
 +        ir->deform[i][i] = dumdub[0][i];
 +    }
 +    ir->deform[YY][XX] = dumdub[0][3];
 +    ir->deform[ZZ][XX] = dumdub[0][4];
 +    ir->deform[ZZ][YY] = dumdub[0][5];
 +    if (ir->epc != epcNO)
 +    {
 +        for (i = 0; i < 3; i++)
 +        {
 +            for (j = 0; j <= i; j++)
 +            {
 +                if (ir->deform[i][j] != 0 && ir->compress[i][j] != 0)
 +                {
 +                    warning_error(wi, "A box element has deform set and compressibility > 0");
 +                }
 +            }
 +        }
 +        for (i = 0; i < 3; i++)
 +        {
 +            for (j = 0; j < i; j++)
 +            {
 +                if (ir->deform[i][j] != 0)
 +                {
 +                    for (m = j; m < DIM; m++)
 +                    {
 +                        if (ir->compress[m][j] != 0)
 +                        {
 +                            sprintf(warn_buf, "An off-diagonal box element has deform set while compressibility > 0 for the same component of another box vector, this might lead to spurious periodicity effects.");
 +                            warning(wi, warn_buf);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    sfree(dumstr[0]);
 +    sfree(dumstr[1]);
 +}
 +
 +static int search_QMstring(char *s, int ng, const char *gn[])
 +{
 +    /* same as normal search_string, but this one searches QM strings */
 +    int i;
 +
 +    for (i = 0; (i < ng); i++)
 +    {
 +        if (gmx_strcasecmp(s, gn[i]) == 0)
 +        {
 +            return i;
 +        }
 +    }
 +
 +    gmx_fatal(FARGS, "this QM method or basisset (%s) is not implemented\n!", s);
 +
 +    return -1;
 +
 +} /* search_QMstring */
 +
 +
 +int search_string(char *s, int ng, char *gn[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < ng); i++)
 +    {
 +        if (gmx_strcasecmp(s, gn[i]) == 0)
 +        {
 +            return i;
 +        }
 +    }
 +
 +    gmx_fatal(FARGS,
 +              "Group %s referenced in the .mdp file was not found in the index file.\n"
 +              "Group names must match either [moleculetype] names or custom index group\n"
 +              "names, in which case you must supply an index file to the '-n' option\n"
 +              "of grompp.",
 +              s);
 +
 +    return -1;
 +}
 +
 +static gmx_bool do_numbering(int natoms, gmx_groups_t *groups, int ng, char *ptrs[],
 +                             t_blocka *block, char *gnames[],
 +                             int gtype, int restnm,
 +                             int grptp, gmx_bool bVerbose,
 +                             warninp_t wi)
 +{
 +    unsigned short *cbuf;
 +    t_grps         *grps = &(groups->grps[gtype]);
 +    int             i, j, gid, aj, ognr, ntot = 0;
 +    const char     *title;
 +    gmx_bool        bRest;
 +    char            warn_buf[STRLEN];
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Starting numbering %d groups of type %d\n", ng, gtype);
 +    }
 +
 +    title = gtypes[gtype];
 +
 +    snew(cbuf, natoms);
 +    /* Mark all id's as not set */
 +    for (i = 0; (i < natoms); i++)
 +    {
 +        cbuf[i] = NOGID;
 +    }
 +
 +    snew(grps->nm_ind, ng+1); /* +1 for possible rest group */
 +    for (i = 0; (i < ng); i++)
 +    {
 +        /* Lookup the group name in the block structure */
 +        gid = search_string(ptrs[i], block->nr, gnames);
 +        if ((grptp != egrptpONE) || (i == 0))
 +        {
 +            grps->nm_ind[grps->nr++] = gid;
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "Found gid %d for group %s\n", gid, ptrs[i]);
 +        }
 +
 +        /* Now go over the atoms in the group */
 +        for (j = block->index[gid]; (j < block->index[gid+1]); j++)
 +        {
 +
 +            aj = block->a[j];
 +
 +            /* Range checking */
 +            if ((aj < 0) || (aj >= natoms))
 +            {
 +                gmx_fatal(FARGS, "Invalid atom number %d in indexfile", aj);
 +            }
 +            /* Lookup up the old group number */
 +            ognr = cbuf[aj];
 +            if (ognr != NOGID)
 +            {
 +                gmx_fatal(FARGS, "Atom %d in multiple %s groups (%d and %d)",
 +                          aj+1, title, ognr+1, i+1);
 +            }
 +            else
 +            {
 +                /* Store the group number in buffer */
 +                if (grptp == egrptpONE)
 +                {
 +                    cbuf[aj] = 0;
 +                }
 +                else
 +                {
 +                    cbuf[aj] = i;
 +                }
 +                ntot++;
 +            }
 +        }
 +    }
 +
 +    /* Now check whether we have done all atoms */
 +    bRest = FALSE;
 +    if (ntot != natoms)
 +    {
 +        if (grptp == egrptpALL)
 +        {
 +            gmx_fatal(FARGS, "%d atoms are not part of any of the %s groups",
 +                      natoms-ntot, title);
 +        }
 +        else if (grptp == egrptpPART)
 +        {
 +            sprintf(warn_buf, "%d atoms are not part of any of the %s groups",
 +                    natoms-ntot, title);
 +            warning_note(wi, warn_buf);
 +        }
 +        /* Assign all atoms currently unassigned to a rest group */
 +        for (j = 0; (j < natoms); j++)
 +        {
 +            if (cbuf[j] == NOGID)
 +            {
 +                cbuf[j] = grps->nr;
 +                bRest   = TRUE;
 +            }
 +        }
 +        if (grptp != egrptpPART)
 +        {
 +            if (bVerbose)
 +            {
 +                fprintf(stderr,
 +                        "Making dummy/rest group for %s containing %d elements\n",
 +                        title, natoms-ntot);
 +            }
 +            /* Add group name "rest" */
 +            grps->nm_ind[grps->nr] = restnm;
 +
 +            /* Assign the rest name to all atoms not currently assigned to a group */
 +            for (j = 0; (j < natoms); j++)
 +            {
 +                if (cbuf[j] == NOGID)
 +                {
 +                    cbuf[j] = grps->nr;
 +                }
 +            }
 +            grps->nr++;
 +        }
 +    }
 +
 +    if (grps->nr == 1 && (ntot == 0 || ntot == natoms))
 +    {
 +        /* All atoms are part of one (or no) group, no index required */
 +        groups->ngrpnr[gtype] = 0;
 +        groups->grpnr[gtype]  = NULL;
 +    }
 +    else
 +    {
 +        groups->ngrpnr[gtype] = natoms;
 +        snew(groups->grpnr[gtype], natoms);
 +        for (j = 0; (j < natoms); j++)
 +        {
 +            groups->grpnr[gtype][j] = cbuf[j];
 +        }
 +    }
 +
 +    sfree(cbuf);
 +
 +    return (bRest && grptp == egrptpPART);
 +}
 +
 +static void calc_nrdf(gmx_mtop_t *mtop, t_inputrec *ir, char **gnames)
 +{
 +    t_grpopts              *opts;
 +    gmx_groups_t           *groups;
 +    t_pull                 *pull;
 +    int                     natoms, ai, aj, i, j, d, g, imin, jmin, nc;
 +    t_iatom                *ia;
 +    int                    *nrdf2, *na_vcm, na_tot;
 +    double                 *nrdf_tc, *nrdf_vcm, nrdf_uc, n_sub = 0;
 +    gmx_mtop_atomloop_all_t aloop;
 +    t_atom                 *atom;
 +    int                     mb, mol, ftype, as;
 +    gmx_molblock_t         *molb;
 +    gmx_moltype_t          *molt;
 +
 +    /* Calculate nrdf.
 +     * First calc 3xnr-atoms for each group
 +     * then subtract half a degree of freedom for each constraint
 +     *
 +     * Only atoms and nuclei contribute to the degrees of freedom...
 +     */
 +
 +    opts = &ir->opts;
 +
 +    groups = &mtop->groups;
 +    natoms = mtop->natoms;
 +
 +    /* Allocate one more for a possible rest group */
 +    /* We need to sum degrees of freedom into doubles,
 +     * since floats give too low nrdf's above 3 million atoms.
 +     */
 +    snew(nrdf_tc, groups->grps[egcTC].nr+1);
 +    snew(nrdf_vcm, groups->grps[egcVCM].nr+1);
 +    snew(na_vcm, groups->grps[egcVCM].nr+1);
 +
 +    for (i = 0; i < groups->grps[egcTC].nr; i++)
 +    {
 +        nrdf_tc[i] = 0;
 +    }
 +    for (i = 0; i < groups->grps[egcVCM].nr+1; i++)
 +    {
 +        nrdf_vcm[i] = 0;
 +    }
 +
 +    snew(nrdf2, natoms);
 +    aloop = gmx_mtop_atomloop_all_init(mtop);
 +    while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
 +    {
 +        nrdf2[i] = 0;
 +        if (atom->ptype == eptAtom || atom->ptype == eptNucleus)
 +        {
 +            g = ggrpnr(groups, egcFREEZE, i);
 +            /* Double count nrdf for particle i */
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (opts->nFreeze[g][d] == 0)
 +                {
 +                    nrdf2[i] += 2;
 +                }
 +            }
 +            nrdf_tc [ggrpnr(groups, egcTC, i)]  += 0.5*nrdf2[i];
 +            nrdf_vcm[ggrpnr(groups, egcVCM, i)] += 0.5*nrdf2[i];
 +        }
 +    }
 +
 +    as = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molb = &mtop->molblock[mb];
 +        molt = &mtop->moltype[molb->type];
 +        atom = molt->atoms.atom;
 +        for (mol = 0; mol < molb->nmol; mol++)
 +        {
 +            for (ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
 +            {
 +                ia = molt->ilist[ftype].iatoms;
 +                for (i = 0; i < molt->ilist[ftype].nr; )
 +                {
 +                    /* Subtract degrees of freedom for the constraints,
 +                     * if the particles still have degrees of freedom left.
 +                     * If one of the particles is a vsite or a shell, then all
 +                     * constraint motion will go there, but since they do not
 +                     * contribute to the constraints the degrees of freedom do not
 +                     * change.
 +                     */
 +                    ai = as + ia[1];
 +                    aj = as + ia[2];
 +                    if (((atom[ia[1]].ptype == eptNucleus) ||
 +                         (atom[ia[1]].ptype == eptAtom)) &&
 +                        ((atom[ia[2]].ptype == eptNucleus) ||
 +                         (atom[ia[2]].ptype == eptAtom)))
 +                    {
 +                        if (nrdf2[ai] > 0)
 +                        {
 +                            jmin = 1;
 +                        }
 +                        else
 +                        {
 +                            jmin = 2;
 +                        }
 +                        if (nrdf2[aj] > 0)
 +                        {
 +                            imin = 1;
 +                        }
 +                        else
 +                        {
 +                            imin = 2;
 +                        }
 +                        imin       = min(imin, nrdf2[ai]);
 +                        jmin       = min(jmin, nrdf2[aj]);
 +                        nrdf2[ai] -= imin;
 +                        nrdf2[aj] -= jmin;
 +                        nrdf_tc [ggrpnr(groups, egcTC, ai)]  -= 0.5*imin;
 +                        nrdf_tc [ggrpnr(groups, egcTC, aj)]  -= 0.5*jmin;
 +                        nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin;
 +                        nrdf_vcm[ggrpnr(groups, egcVCM, aj)] -= 0.5*jmin;
 +                    }
 +                    ia += interaction_function[ftype].nratoms+1;
 +                    i  += interaction_function[ftype].nratoms+1;
 +                }
 +            }
 +            ia = molt->ilist[F_SETTLE].iatoms;
 +            for (i = 0; i < molt->ilist[F_SETTLE].nr; )
 +            {
 +                /* Subtract 1 dof from every atom in the SETTLE */
 +                for (j = 0; j < 3; j++)
 +                {
 +                    ai         = as + ia[1+j];
 +                    imin       = min(2, nrdf2[ai]);
 +                    nrdf2[ai] -= imin;
 +                    nrdf_tc [ggrpnr(groups, egcTC, ai)]  -= 0.5*imin;
 +                    nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin;
 +                }
 +                ia += 4;
 +                i  += 4;
 +            }
 +            as += molt->atoms.nr;
 +        }
 +    }
 +
 +    if (ir->ePull == epullCONSTRAINT)
 +    {
 +        /* Correct nrdf for the COM constraints.
 +         * We correct using the TC and VCM group of the first atom
 +         * in the reference and pull group. If atoms in one pull group
 +         * belong to different TC or VCM groups it is anyhow difficult
 +         * to determine the optimal nrdf assignment.
 +         */
 +        pull = ir->pull;
 +        if (pull->eGeom == epullgPOS)
 +        {
 +            nc = 0;
 +            for (i = 0; i < DIM; i++)
 +            {
 +                if (pull->dim[i])
 +                {
 +                    nc++;
 +                }
 +            }
 +        }
 +        else
 +        {
 +            nc = 1;
 +        }
 +        for (i = 0; i < pull->ngrp; i++)
 +        {
 +            imin = 2*nc;
 +            if (pull->grp[0].nat > 0)
 +            {
 +                /* Subtract 1/2 dof from the reference group */
 +                ai = pull->grp[0].ind[0];
 +                if (nrdf_tc[ggrpnr(groups, egcTC, ai)] > 1)
 +                {
 +                    nrdf_tc [ggrpnr(groups, egcTC, ai)]  -= 0.5;
 +                    nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5;
 +                    imin--;
 +                }
 +            }
 +            /* Subtract 1/2 dof from the pulled group */
 +            ai = pull->grp[1+i].ind[0];
 +            nrdf_tc [ggrpnr(groups, egcTC, ai)]  -= 0.5*imin;
 +            nrdf_vcm[ggrpnr(groups, egcVCM, ai)] -= 0.5*imin;
 +            if (nrdf_tc[ggrpnr(groups, egcTC, ai)] < 0)
 +            {
 +                gmx_fatal(FARGS, "Center of mass pulling constraints caused the number of degrees of freedom for temperature coupling group %s to be negative", gnames[groups->grps[egcTC].nm_ind[ggrpnr(groups, egcTC, ai)]]);
 +            }
 +        }
 +    }
 +
 +    if (ir->nstcomm != 0)
 +    {
 +        /* Subtract 3 from the number of degrees of freedom in each vcm group
 +         * when com translation is removed and 6 when rotation is removed
 +         * as well.
 +         */
 +        switch (ir->comm_mode)
 +        {
 +            case ecmLINEAR:
 +                n_sub = ndof_com(ir);
 +                break;
 +            case ecmANGULAR:
 +                n_sub = 6;
 +                break;
 +            default:
 +                n_sub = 0;
 +                gmx_incons("Checking comm_mode");
 +        }
 +
 +        for (i = 0; i < groups->grps[egcTC].nr; i++)
 +        {
 +            /* Count the number of atoms of TC group i for every VCM group */
 +            for (j = 0; j < groups->grps[egcVCM].nr+1; j++)
 +            {
 +                na_vcm[j] = 0;
 +            }
 +            na_tot = 0;
 +            for (ai = 0; ai < natoms; ai++)
 +            {
 +                if (ggrpnr(groups, egcTC, ai) == i)
 +                {
 +                    na_vcm[ggrpnr(groups, egcVCM, ai)]++;
 +                    na_tot++;
 +                }
 +            }
 +            /* Correct for VCM removal according to the fraction of each VCM
 +             * group present in this TC group.
 +             */
 +            nrdf_uc = nrdf_tc[i];
 +            if (debug)
 +            {
 +                fprintf(debug, "T-group[%d] nrdf_uc = %g, n_sub = %g\n",
 +                        i, nrdf_uc, n_sub);
 +            }
 +            nrdf_tc[i] = 0;
 +            for (j = 0; j < groups->grps[egcVCM].nr+1; j++)
 +            {
 +                if (nrdf_vcm[j] > n_sub)
 +                {
 +                    nrdf_tc[i] += nrdf_uc*((double)na_vcm[j]/(double)na_tot)*
 +                        (nrdf_vcm[j] - n_sub)/nrdf_vcm[j];
 +                }
 +                if (debug)
 +                {
 +                    fprintf(debug, "  nrdf_vcm[%d] = %g, nrdf = %g\n",
 +                            j, nrdf_vcm[j], nrdf_tc[i]);
 +                }
 +            }
 +        }
 +    }
 +    for (i = 0; (i < groups->grps[egcTC].nr); i++)
 +    {
 +        opts->nrdf[i] = nrdf_tc[i];
 +        if (opts->nrdf[i] < 0)
 +        {
 +            opts->nrdf[i] = 0;
 +        }
 +        fprintf(stderr,
 +                "Number of degrees of freedom in T-Coupling group %s is %.2f\n",
 +                gnames[groups->grps[egcTC].nm_ind[i]], opts->nrdf[i]);
 +    }
 +
 +    sfree(nrdf2);
 +    sfree(nrdf_tc);
 +    sfree(nrdf_vcm);
 +    sfree(na_vcm);
 +}
 +
 +static void decode_cos(char *s, t_cosines *cosine, gmx_bool bTime)
 +{
 +    char   *t;
 +    char    format[STRLEN], f1[STRLEN];
 +    double  a, phi;
 +    int     i;
 +
 +    t = strdup(s);
 +    trim(t);
 +
 +    cosine->n   = 0;
 +    cosine->a   = NULL;
 +    cosine->phi = NULL;
 +    if (strlen(t))
 +    {
 +        sscanf(t, "%d", &(cosine->n));
 +        if (cosine->n <= 0)
 +        {
 +            cosine->n = 0;
 +        }
 +        else
 +        {
 +            snew(cosine->a, cosine->n);
 +            snew(cosine->phi, cosine->n);
 +
 +            sprintf(format, "%%*d");
 +            for (i = 0; (i < cosine->n); i++)
 +            {
 +                strcpy(f1, format);
 +                strcat(f1, "%lf%lf");
 +                if (sscanf(t, f1, &a, &phi) < 2)
 +                {
 +                    gmx_fatal(FARGS, "Invalid input for electric field shift: '%s'", t);
 +                }
 +                cosine->a[i]   = a;
 +                cosine->phi[i] = phi;
 +                strcat(format, "%*lf%*lf");
 +            }
 +        }
 +    }
 +    sfree(t);
 +}
 +
 +static gmx_bool do_egp_flag(t_inputrec *ir, gmx_groups_t *groups,
 +                            const char *option, const char *val, int flag)
 +{
 +    /* The maximum number of energy group pairs would be MAXPTR*(MAXPTR+1)/2.
 +     * But since this is much larger than STRLEN, such a line can not be parsed.
 +     * The real maximum is the number of names that fit in a string: STRLEN/2.
 +     */
 +#define EGP_MAX (STRLEN/2)
 +    int      nelem, i, j, k, nr;
 +    char    *names[EGP_MAX];
 +    char  ***gnames;
 +    gmx_bool bSet;
 +
 +    gnames = groups->grpname;
 +
 +    nelem = str_nelem(val, EGP_MAX, names);
 +    if (nelem % 2 != 0)
 +    {
 +        gmx_fatal(FARGS, "The number of groups for %s is odd", option);
 +    }
 +    nr   = groups->grps[egcENER].nr;
 +    bSet = FALSE;
 +    for (i = 0; i < nelem/2; i++)
 +    {
 +        j = 0;
 +        while ((j < nr) &&
 +               gmx_strcasecmp(names[2*i], *(gnames[groups->grps[egcENER].nm_ind[j]])))
 +        {
 +            j++;
 +        }
 +        if (j == nr)
 +        {
 +            gmx_fatal(FARGS, "%s in %s is not an energy group\n",
 +                      names[2*i], option);
 +        }
 +        k = 0;
 +        while ((k < nr) &&
 +               gmx_strcasecmp(names[2*i+1], *(gnames[groups->grps[egcENER].nm_ind[k]])))
 +        {
 +            k++;
 +        }
 +        if (k == nr)
 +        {
 +            gmx_fatal(FARGS, "%s in %s is not an energy group\n",
 +                      names[2*i+1], option);
 +        }
 +        if ((j < nr) && (k < nr))
 +        {
 +            ir->opts.egp_flags[nr*j+k] |= flag;
 +            ir->opts.egp_flags[nr*k+j] |= flag;
 +            bSet = TRUE;
 +        }
 +    }
 +
 +    return bSet;
 +}
 +
 +void do_index(const char* mdparin, const char *ndx,
 +              gmx_mtop_t *mtop,
 +              gmx_bool bVerbose,
 +              t_inputrec *ir, rvec *v,
 +              warninp_t wi)
 +{
 +    t_blocka     *grps;
 +    gmx_groups_t *groups;
 +    int           natoms;
 +    t_symtab     *symtab;
 +    t_atoms       atoms_all;
 +    char          warnbuf[STRLEN], **gnames;
 +    int           nr, ntcg, ntau_t, nref_t, nacc, nofg, nSA, nSA_points, nSA_time, nSA_temp;
 +    real          tau_min;
 +    int           nstcmin;
 +    int           nacg, nfreeze, nfrdim, nenergy, nvcm, nuser;
 +    char         *ptr1[MAXPTR], *ptr2[MAXPTR], *ptr3[MAXPTR];
 +    int           i, j, k, restnm;
 +    real          SAtime;
 +    gmx_bool      bExcl, bTable, bSetTCpar, bAnneal, bRest;
 +    int           nQMmethod, nQMbasis, nQMcharge, nQMmult, nbSH, nCASorb, nCASelec,
 +                  nSAon, nSAoff, nSAsteps, nQMg, nbOPT, nbTS;
 +    char          warn_buf[STRLEN];
 +
 +    if (bVerbose)
 +    {
 +        fprintf(stderr, "processing index file...\n");
 +    }
 +    debug_gmx();
 +    if (ndx == NULL)
 +    {
 +        snew(grps, 1);
 +        snew(grps->index, 1);
 +        snew(gnames, 1);
 +        atoms_all = gmx_mtop_global_atoms(mtop);
 +        analyse(&atoms_all, grps, &gnames, FALSE, TRUE);
 +        free_t_atoms(&atoms_all, FALSE);
 +    }
 +    else
 +    {
 +        grps = init_index(ndx, &gnames);
 +    }
 +
 +    groups = &mtop->groups;
 +    natoms = mtop->natoms;
 +    symtab = &mtop->symtab;
 +
 +    snew(groups->grpname, grps->nr+1);
 +
 +    for (i = 0; (i < grps->nr); i++)
 +    {
 +        groups->grpname[i] = put_symtab(symtab, gnames[i]);
 +    }
 +    groups->grpname[i] = put_symtab(symtab, "rest");
 +    restnm             = i;
 +    srenew(gnames, grps->nr+1);
 +    gnames[restnm]   = *(groups->grpname[i]);
 +    groups->ngrpname = grps->nr+1;
 +
 +    set_warning_line(wi, mdparin, -1);
 +
 +    ntau_t = str_nelem(tau_t, MAXPTR, ptr1);
 +    nref_t = str_nelem(ref_t, MAXPTR, ptr2);
 +    ntcg   = str_nelem(tcgrps, MAXPTR, ptr3);
 +    if ((ntau_t != ntcg) || (nref_t != ntcg))
 +    {
 +        gmx_fatal(FARGS, "Invalid T coupling input: %d groups, %d ref-t values and "
 +                  "%d tau-t values", ntcg, nref_t, ntau_t);
 +    }
 +
 +    bSetTCpar = (ir->etc || EI_SD(ir->eI) || ir->eI == eiBD || EI_TPI(ir->eI));
 +    do_numbering(natoms, groups, ntcg, ptr3, grps, gnames, egcTC,
 +                 restnm, bSetTCpar ? egrptpALL : egrptpALL_GENREST, bVerbose, wi);
 +    nr            = groups->grps[egcTC].nr;
 +    ir->opts.ngtc = nr;
 +    snew(ir->opts.nrdf, nr);
 +    snew(ir->opts.tau_t, nr);
 +    snew(ir->opts.ref_t, nr);
 +    if (ir->eI == eiBD && ir->bd_fric == 0)
 +    {
 +        fprintf(stderr, "bd-fric=0, so tau-t will be used as the inverse friction constant(s)\n");
 +    }
 +
 +    if (bSetTCpar)
 +    {
 +        if (nr != nref_t)
 +        {
 +            gmx_fatal(FARGS, "Not enough ref-t and tau-t values!");
 +        }
 +
 +        tau_min = 1e20;
 +        for (i = 0; (i < nr); i++)
 +        {
 +            ir->opts.tau_t[i] = strtod(ptr1[i], NULL);
 +            if ((ir->eI == eiBD || ir->eI == eiSD2) && ir->opts.tau_t[i] <= 0)
 +            {
 +                sprintf(warn_buf, "With integrator %s tau-t should be larger than 0", ei_names[ir->eI]);
 +                warning_error(wi, warn_buf);
 +            }
 +
 +            if (ir->etc != etcVRESCALE && ir->opts.tau_t[i] == 0)
 +            {
 +                warning_note(wi, "tau-t = -1 is the value to signal that a group should not have temperature coupling. Treating your use of tau-t = 0 as if you used -1.");
 +            }
 +
 +            if (ir->opts.tau_t[i] >= 0)
 +            {
 +                tau_min = min(tau_min, ir->opts.tau_t[i]);
 +            }
 +        }
 +        if (ir->etc != etcNO && ir->nsttcouple == -1)
 +        {
 +            ir->nsttcouple = ir_optimal_nsttcouple(ir);
 +        }
 +
 +        if (EI_VV(ir->eI))
 +        {
 +            if ((ir->etc == etcNOSEHOOVER) && (ir->epc == epcBERENDSEN))
 +            {
 +                gmx_fatal(FARGS, "Cannot do Nose-Hoover temperature with Berendsen pressure control with md-vv; use either vrescale temperature with berendsen pressure or Nose-Hoover temperature with MTTK pressure");
 +            }
 +            if ((ir->epc == epcMTTK) && (ir->etc > etcNO))
 +            {
 +                if (ir->nstpcouple != ir->nsttcouple)
 +                {
 +                    int mincouple = min(ir->nstpcouple, ir->nsttcouple);
 +                    ir->nstpcouple = ir->nsttcouple = mincouple;
 +                    sprintf(warn_buf, "for current Trotter decomposition methods with vv, nsttcouple and nstpcouple must be equal.  Both have been reset to min(nsttcouple,nstpcouple) = %d", mincouple);
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +        }
 +        /* velocity verlet with averaged kinetic energy KE = 0.5*(v(t+1/2) - v(t-1/2)) is implemented
 +           primarily for testing purposes, and does not work with temperature coupling other than 1 */
 +
 +        if (ETC_ANDERSEN(ir->etc))
 +        {
 +            if (ir->nsttcouple != 1)
 +            {
 +                ir->nsttcouple = 1;
 +                sprintf(warn_buf, "Andersen temperature control methods assume nsttcouple = 1; there is no need for larger nsttcouple > 1, since no global parameters are computed. nsttcouple has been reset to 1");
 +                warning_note(wi, warn_buf);
 +            }
 +        }
 +        nstcmin = tcouple_min_integration_steps(ir->etc);
 +        if (nstcmin > 1)
 +        {
 +            if (tau_min/(ir->delta_t*ir->nsttcouple) < nstcmin)
 +            {
 +                sprintf(warn_buf, "For proper integration of the %s thermostat, tau-t (%g) should be at least %d times larger than nsttcouple*dt (%g)",
 +                        ETCOUPLTYPE(ir->etc),
 +                        tau_min, nstcmin,
 +                        ir->nsttcouple*ir->delta_t);
 +                warning(wi, warn_buf);
 +            }
 +        }
 +        for (i = 0; (i < nr); i++)
 +        {
 +            ir->opts.ref_t[i] = strtod(ptr2[i], NULL);
 +            if (ir->opts.ref_t[i] < 0)
 +            {
 +                gmx_fatal(FARGS, "ref-t for group %d negative", i);
 +            }
 +        }
 +        /* set the lambda mc temperature to the md integrator temperature (which should be defined
 +           if we are in this conditional) if mc_temp is negative */
 +        if (ir->expandedvals->mc_temp < 0)
 +        {
 +            ir->expandedvals->mc_temp = ir->opts.ref_t[0]; /*for now, set to the first reft */
 +        }
 +    }
 +
 +    /* Simulated annealing for each group. There are nr groups */
 +    nSA = str_nelem(anneal, MAXPTR, ptr1);
 +    if (nSA == 1 && (ptr1[0][0] == 'n' || ptr1[0][0] == 'N'))
 +    {
 +        nSA = 0;
 +    }
 +    if (nSA > 0 && nSA != nr)
 +    {
 +        gmx_fatal(FARGS, "Not enough annealing values: %d (for %d groups)\n", nSA, nr);
 +    }
 +    else
 +    {
 +        snew(ir->opts.annealing, nr);
 +        snew(ir->opts.anneal_npoints, nr);
 +        snew(ir->opts.anneal_time, nr);
 +        snew(ir->opts.anneal_temp, nr);
 +        for (i = 0; i < nr; i++)
 +        {
 +            ir->opts.annealing[i]      = eannNO;
 +            ir->opts.anneal_npoints[i] = 0;
 +            ir->opts.anneal_time[i]    = NULL;
 +            ir->opts.anneal_temp[i]    = NULL;
 +        }
 +        if (nSA > 0)
 +        {
 +            bAnneal = FALSE;
 +            for (i = 0; i < nr; i++)
 +            {
 +                if (ptr1[i][0] == 'n' || ptr1[i][0] == 'N')
 +                {
 +                    ir->opts.annealing[i] = eannNO;
 +                }
 +                else if (ptr1[i][0] == 's' || ptr1[i][0] == 'S')
 +                {
 +                    ir->opts.annealing[i] = eannSINGLE;
 +                    bAnneal               = TRUE;
 +                }
 +                else if (ptr1[i][0] == 'p' || ptr1[i][0] == 'P')
 +                {
 +                    ir->opts.annealing[i] = eannPERIODIC;
 +                    bAnneal               = TRUE;
 +                }
 +            }
 +            if (bAnneal)
 +            {
 +                /* Read the other fields too */
 +                nSA_points = str_nelem(anneal_npoints, MAXPTR, ptr1);
 +                if (nSA_points != nSA)
 +                {
 +                    gmx_fatal(FARGS, "Found %d annealing-npoints values for %d groups\n", nSA_points, nSA);
 +                }
 +                for (k = 0, i = 0; i < nr; i++)
 +                {
 +                    ir->opts.anneal_npoints[i] = strtol(ptr1[i], NULL, 10);
 +                    if (ir->opts.anneal_npoints[i] == 1)
 +                    {
 +                        gmx_fatal(FARGS, "Please specify at least a start and an end point for annealing\n");
 +                    }
 +                    snew(ir->opts.anneal_time[i], ir->opts.anneal_npoints[i]);
 +                    snew(ir->opts.anneal_temp[i], ir->opts.anneal_npoints[i]);
 +                    k += ir->opts.anneal_npoints[i];
 +                }
 +
 +                nSA_time = str_nelem(anneal_time, MAXPTR, ptr1);
 +                if (nSA_time != k)
 +                {
 +                    gmx_fatal(FARGS, "Found %d annealing-time values, wanter %d\n", nSA_time, k);
 +                }
 +                nSA_temp = str_nelem(anneal_temp, MAXPTR, ptr2);
 +                if (nSA_temp != k)
 +                {
 +                    gmx_fatal(FARGS, "Found %d annealing-temp values, wanted %d\n", nSA_temp, k);
 +                }
 +
 +                for (i = 0, k = 0; i < nr; i++)
 +                {
 +
 +                    for (j = 0; j < ir->opts.anneal_npoints[i]; j++)
 +                    {
 +                        ir->opts.anneal_time[i][j] = strtod(ptr1[k], NULL);
 +                        ir->opts.anneal_temp[i][j] = strtod(ptr2[k], NULL);
 +                        if (j == 0)
 +                        {
 +                            if (ir->opts.anneal_time[i][0] > (ir->init_t+GMX_REAL_EPS))
 +                            {
 +                                gmx_fatal(FARGS, "First time point for annealing > init_t.\n");
 +                            }
 +                        }
 +                        else
 +                        {
 +                            /* j>0 */
 +                            if (ir->opts.anneal_time[i][j] < ir->opts.anneal_time[i][j-1])
 +                            {
 +                                gmx_fatal(FARGS, "Annealing timepoints out of order: t=%f comes after t=%f\n",
 +                                          ir->opts.anneal_time[i][j], ir->opts.anneal_time[i][j-1]);
 +                            }
 +                        }
 +                        if (ir->opts.anneal_temp[i][j] < 0)
 +                        {
 +                            gmx_fatal(FARGS, "Found negative temperature in annealing: %f\n", ir->opts.anneal_temp[i][j]);
 +                        }
 +                        k++;
 +                    }
 +                }
 +                /* Print out some summary information, to make sure we got it right */
 +                for (i = 0, k = 0; i < nr; i++)
 +                {
 +                    if (ir->opts.annealing[i] != eannNO)
 +                    {
 +                        j = groups->grps[egcTC].nm_ind[i];
 +                        fprintf(stderr, "Simulated annealing for group %s: %s, %d timepoints\n",
 +                                *(groups->grpname[j]), eann_names[ir->opts.annealing[i]],
 +                                ir->opts.anneal_npoints[i]);
 +                        fprintf(stderr, "Time (ps)   Temperature (K)\n");
 +                        /* All terms except the last one */
 +                        for (j = 0; j < (ir->opts.anneal_npoints[i]-1); j++)
 +                        {
 +                            fprintf(stderr, "%9.1f      %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]);
 +                        }
 +
 +                        /* Finally the last one */
 +                        j = ir->opts.anneal_npoints[i]-1;
 +                        if (ir->opts.annealing[i] == eannSINGLE)
 +                        {
 +                            fprintf(stderr, "%9.1f-     %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]);
 +                        }
 +                        else
 +                        {
 +                            fprintf(stderr, "%9.1f      %5.1f\n", ir->opts.anneal_time[i][j], ir->opts.anneal_temp[i][j]);
 +                            if (fabs(ir->opts.anneal_temp[i][j]-ir->opts.anneal_temp[i][0]) > GMX_REAL_EPS)
 +                            {
 +                                warning_note(wi, "There is a temperature jump when your annealing loops back.\n");
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if (ir->ePull != epullNO)
 +    {
 +        make_pull_groups(ir->pull, pull_grp, grps, gnames);
 +    }
 +
 +    if (ir->bRot)
 +    {
 +        make_rotation_groups(ir->rot, rot_grp, grps, gnames);
 +    }
 +
 +    nacc = str_nelem(acc, MAXPTR, ptr1);
 +    nacg = str_nelem(accgrps, MAXPTR, ptr2);
 +    if (nacg*DIM != nacc)
 +    {
 +        gmx_fatal(FARGS, "Invalid Acceleration input: %d groups and %d acc. values",
 +                  nacg, nacc);
 +    }
 +    do_numbering(natoms, groups, nacg, ptr2, grps, gnames, egcACC,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nr = groups->grps[egcACC].nr;
 +    snew(ir->opts.acc, nr);
 +    ir->opts.ngacc = nr;
 +
 +    for (i = k = 0; (i < nacg); i++)
 +    {
 +        for (j = 0; (j < DIM); j++, k++)
 +        {
 +            ir->opts.acc[i][j] = strtod(ptr1[k], NULL);
 +        }
 +    }
 +    for (; (i < nr); i++)
 +    {
 +        for (j = 0; (j < DIM); j++)
 +        {
 +            ir->opts.acc[i][j] = 0;
 +        }
 +    }
 +
 +    nfrdim  = str_nelem(frdim, MAXPTR, ptr1);
 +    nfreeze = str_nelem(freeze, MAXPTR, ptr2);
 +    if (nfrdim != DIM*nfreeze)
 +    {
 +        gmx_fatal(FARGS, "Invalid Freezing input: %d groups and %d freeze values",
 +                  nfreeze, nfrdim);
 +    }
 +    do_numbering(natoms, groups, nfreeze, ptr2, grps, gnames, egcFREEZE,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nr             = groups->grps[egcFREEZE].nr;
 +    ir->opts.ngfrz = nr;
 +    snew(ir->opts.nFreeze, nr);
 +    for (i = k = 0; (i < nfreeze); i++)
 +    {
 +        for (j = 0; (j < DIM); j++, k++)
 +        {
 +            ir->opts.nFreeze[i][j] = (gmx_strncasecmp(ptr1[k], "Y", 1) == 0);
 +            if (!ir->opts.nFreeze[i][j])
 +            {
 +                if (gmx_strncasecmp(ptr1[k], "N", 1) != 0)
 +                {
 +                    sprintf(warnbuf, "Please use Y(ES) or N(O) for freezedim only "
 +                            "(not %s)", ptr1[k]);
 +                    warning(wi, warn_buf);
 +                }
 +            }
 +        }
 +    }
 +    for (; (i < nr); i++)
 +    {
 +        for (j = 0; (j < DIM); j++)
 +        {
 +            ir->opts.nFreeze[i][j] = 0;
 +        }
 +    }
 +
 +    nenergy = str_nelem(energy, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nenergy, ptr1, grps, gnames, egcENER,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    add_wall_energrps(groups, ir->nwall, symtab);
 +    ir->opts.ngener = groups->grps[egcENER].nr;
 +    nvcm            = str_nelem(vcm, MAXPTR, ptr1);
 +    bRest           =
 +        do_numbering(natoms, groups, nvcm, ptr1, grps, gnames, egcVCM,
 +                     restnm, nvcm == 0 ? egrptpALL_GENREST : egrptpPART, bVerbose, wi);
 +    if (bRest)
 +    {
 +        warning(wi, "Some atoms are not part of any center of mass motion removal group.\n"
 +                "This may lead to artifacts.\n"
 +                "In most cases one should use one group for the whole system.");
 +    }
 +
 +    /* Now we have filled the freeze struct, so we can calculate NRDF */
 +    calc_nrdf(mtop, ir, gnames);
 +
 +    if (v && NULL)
 +    {
 +        real fac, ntot = 0;
 +
 +        /* Must check per group! */
 +        for (i = 0; (i < ir->opts.ngtc); i++)
 +        {
 +            ntot += ir->opts.nrdf[i];
 +        }
 +        if (ntot != (DIM*natoms))
 +        {
 +            fac = sqrt(ntot/(DIM*natoms));
 +            if (bVerbose)
 +            {
 +                fprintf(stderr, "Scaling velocities by a factor of %.3f to account for constraints\n"
 +                        "and removal of center of mass motion\n", fac);
 +            }
 +            for (i = 0; (i < natoms); i++)
 +            {
 +                svmul(fac, v[i], v[i]);
 +            }
 +        }
 +    }
 +
 +    nuser = str_nelem(user1, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser1,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nuser = str_nelem(user2, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcUser2,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nuser = str_nelem(xtc_grps, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nuser, ptr1, grps, gnames, egcXTC,
 +                 restnm, egrptpONE, bVerbose, wi);
 +    nofg = str_nelem(orirefitgrp, MAXPTR, ptr1);
 +    do_numbering(natoms, groups, nofg, ptr1, grps, gnames, egcORFIT,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +
 +    /* QMMM input processing */
 +    nQMg          = str_nelem(QMMM, MAXPTR, ptr1);
 +    nQMmethod     = str_nelem(QMmethod, MAXPTR, ptr2);
 +    nQMbasis      = str_nelem(QMbasis, MAXPTR, ptr3);
 +    if ((nQMmethod != nQMg) || (nQMbasis != nQMg))
 +    {
 +        gmx_fatal(FARGS, "Invalid QMMM input: %d groups %d basissets"
 +                  " and %d methods\n", nQMg, nQMbasis, nQMmethod);
 +    }
 +    /* group rest, if any, is always MM! */
 +    do_numbering(natoms, groups, nQMg, ptr1, grps, gnames, egcQMMM,
 +                 restnm, egrptpALL_GENREST, bVerbose, wi);
 +    nr            = nQMg; /*atoms->grps[egcQMMM].nr;*/
 +    ir->opts.ngQM = nQMg;
 +    snew(ir->opts.QMmethod, nr);
 +    snew(ir->opts.QMbasis, nr);
 +    for (i = 0; i < nr; i++)
 +    {
 +        /* input consists of strings: RHF CASSCF PM3 .. These need to be
 +         * converted to the corresponding enum in names.c
 +         */
 +        ir->opts.QMmethod[i] = search_QMstring(ptr2[i], eQMmethodNR,
 +                                               eQMmethod_names);
 +        ir->opts.QMbasis[i]  = search_QMstring(ptr3[i], eQMbasisNR,
 +                                               eQMbasis_names);
 +
 +    }
 +    nQMmult   = str_nelem(QMmult, MAXPTR, ptr1);
 +    nQMcharge = str_nelem(QMcharge, MAXPTR, ptr2);
 +    nbSH      = str_nelem(bSH, MAXPTR, ptr3);
 +    snew(ir->opts.QMmult, nr);
 +    snew(ir->opts.QMcharge, nr);
 +    snew(ir->opts.bSH, nr);
 +
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.QMmult[i]   = strtol(ptr1[i], NULL, 10);
 +        ir->opts.QMcharge[i] = strtol(ptr2[i], NULL, 10);
 +        ir->opts.bSH[i]      = (gmx_strncasecmp(ptr3[i], "Y", 1) == 0);
 +    }
 +
 +    nCASelec  = str_nelem(CASelectrons, MAXPTR, ptr1);
 +    nCASorb   = str_nelem(CASorbitals, MAXPTR, ptr2);
 +    snew(ir->opts.CASelectrons, nr);
 +    snew(ir->opts.CASorbitals, nr);
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.CASelectrons[i] = strtol(ptr1[i], NULL, 10);
 +        ir->opts.CASorbitals[i]  = strtol(ptr2[i], NULL, 10);
 +    }
 +    /* special optimization options */
 +
 +    nbOPT = str_nelem(bOPT, MAXPTR, ptr1);
 +    nbTS  = str_nelem(bTS, MAXPTR, ptr2);
 +    snew(ir->opts.bOPT, nr);
 +    snew(ir->opts.bTS, nr);
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.bOPT[i] = (gmx_strncasecmp(ptr1[i], "Y", 1) == 0);
 +        ir->opts.bTS[i]  = (gmx_strncasecmp(ptr2[i], "Y", 1) == 0);
 +    }
 +    nSAon     = str_nelem(SAon, MAXPTR, ptr1);
 +    nSAoff    = str_nelem(SAoff, MAXPTR, ptr2);
 +    nSAsteps  = str_nelem(SAsteps, MAXPTR, ptr3);
 +    snew(ir->opts.SAon, nr);
 +    snew(ir->opts.SAoff, nr);
 +    snew(ir->opts.SAsteps, nr);
 +
 +    for (i = 0; i < nr; i++)
 +    {
 +        ir->opts.SAon[i]    = strtod(ptr1[i], NULL);
 +        ir->opts.SAoff[i]   = strtod(ptr2[i], NULL);
 +        ir->opts.SAsteps[i] = strtol(ptr3[i], NULL, 10);
 +    }
 +    /* end of QMMM input */
 +
 +    if (bVerbose)
 +    {
 +        for (i = 0; (i < egcNR); i++)
 +        {
 +            fprintf(stderr, "%-16s has %d element(s):", gtypes[i], groups->grps[i].nr);
 +            for (j = 0; (j < groups->grps[i].nr); j++)
 +            {
 +                fprintf(stderr, " %s", *(groups->grpname[groups->grps[i].nm_ind[j]]));
 +            }
 +            fprintf(stderr, "\n");
 +        }
 +    }
 +
 +    nr = groups->grps[egcENER].nr;
 +    snew(ir->opts.egp_flags, nr*nr);
 +
 +    bExcl = do_egp_flag(ir, groups, "energygrp-excl", egpexcl, EGP_EXCL);
 +    if (bExcl && ir->cutoff_scheme == ecutsVERLET)
 +    {
 +        warning_error(wi, "Energy group exclusions are not (yet) implemented for the Verlet scheme");
 +    }
 +    if (bExcl && EEL_FULL(ir->coulombtype))
 +    {
 +        warning(wi, "Can not exclude the lattice Coulomb energy between energy groups");
 +    }
 +
 +    bTable = do_egp_flag(ir, groups, "energygrp-table", egptable, EGP_TABLE);
 +    if (bTable && !(ir->vdwtype == evdwUSER) &&
 +        !(ir->coulombtype == eelUSER) && !(ir->coulombtype == eelPMEUSER) &&
 +        !(ir->coulombtype == eelPMEUSERSWITCH))
 +    {
 +        gmx_fatal(FARGS, "Can only have energy group pair tables in combination with user tables for VdW and/or Coulomb");
 +    }
 +
 +    decode_cos(efield_x, &(ir->ex[XX]), FALSE);
 +    decode_cos(efield_xt, &(ir->et[XX]), TRUE);
 +    decode_cos(efield_y, &(ir->ex[YY]), FALSE);
 +    decode_cos(efield_yt, &(ir->et[YY]), TRUE);
 +    decode_cos(efield_z, &(ir->ex[ZZ]), FALSE);
 +    decode_cos(efield_zt, &(ir->et[ZZ]), TRUE);
 +
 +    if (ir->bAdress)
 +    {
 +        do_adress_index(ir->adress, groups, gnames, &(ir->opts), wi);
 +    }
 +
 +    for (i = 0; (i < grps->nr); i++)
 +    {
 +        sfree(gnames[i]);
 +    }
 +    sfree(gnames);
 +    done_blocka(grps);
 +    sfree(grps);
 +
 +}
 +
 +
 +
 +static void check_disre(gmx_mtop_t *mtop)
 +{
 +    gmx_ffparams_t *ffparams;
 +    t_functype     *functype;
 +    t_iparams      *ip;
 +    int             i, ndouble, ftype;
 +    int             label, old_label;
 +
 +    if (gmx_mtop_ftype_count(mtop, F_DISRES) > 0)
 +    {
 +        ffparams  = &mtop->ffparams;
 +        functype  = ffparams->functype;
 +        ip        = ffparams->iparams;
 +        ndouble   = 0;
 +        old_label = -1;
 +        for (i = 0; i < ffparams->ntypes; i++)
 +        {
 +            ftype = functype[i];
 +            if (ftype == F_DISRES)
 +            {
 +                label = ip[i].disres.label;
 +                if (label == old_label)
 +                {
 +                    fprintf(stderr, "Distance restraint index %d occurs twice\n", label);
 +                    ndouble++;
 +                }
 +                old_label = label;
 +            }
 +        }
 +        if (ndouble > 0)
 +        {
 +            gmx_fatal(FARGS, "Found %d double distance restraint indices,\n"
 +                      "probably the parameters for multiple pairs in one restraint "
 +                      "are not identical\n", ndouble);
 +        }
 +    }
 +}
 +
 +static gmx_bool absolute_reference(t_inputrec *ir, gmx_mtop_t *sys,
 +                                   gmx_bool posres_only,
 +                                   ivec AbsRef)
 +{
 +    int                  d, g, i;
 +    gmx_mtop_ilistloop_t iloop;
 +    t_ilist             *ilist;
 +    int                  nmol;
 +    t_iparams           *pr;
 +
 +    clear_ivec(AbsRef);
 +
 +    if (!posres_only)
 +    {
 +        /* Check the COM */
 +        for (d = 0; d < DIM; d++)
 +        {
 +            AbsRef[d] = (d < ndof_com(ir) ? 0 : 1);
 +        }
 +        /* Check for freeze groups */
 +        for (g = 0; g < ir->opts.ngfrz; g++)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (ir->opts.nFreeze[g][d] != 0)
 +                {
 +                    AbsRef[d] = 1;
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Check for position restraints */
 +    iloop = gmx_mtop_ilistloop_init(sys);
 +    while (gmx_mtop_ilistloop_next(iloop, &ilist, &nmol))
 +    {
 +        if (nmol > 0 &&
 +            (AbsRef[XX] == 0 || AbsRef[YY] == 0 || AbsRef[ZZ] == 0))
 +        {
 +            for (i = 0; i < ilist[F_POSRES].nr; i += 2)
 +            {
 +                pr = &sys->ffparams.iparams[ilist[F_POSRES].iatoms[i]];
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    if (pr->posres.fcA[d] != 0)
 +                    {
 +                        AbsRef[d] = 1;
 +                    }
 +                }
 +            }
 +            for (i = 0; i < ilist[F_FBPOSRES].nr; i += 2)
 +            {
 +                /* Check for flat-bottom posres */
 +                pr = &sys->ffparams.iparams[ilist[F_FBPOSRES].iatoms[i]];
 +                if (pr->fbposres.k != 0)
 +                {
 +                    switch (pr->fbposres.geom)
 +                    {
 +                        case efbposresSPHERE:
 +                            AbsRef[XX] = AbsRef[YY] = AbsRef[ZZ] = 1;
 +                            break;
 +                        case efbposresCYLINDER:
 +                            AbsRef[XX] = AbsRef[YY] = 1;
 +                            break;
 +                        case efbposresX: /* d=XX */
 +                        case efbposresY: /* d=YY */
 +                        case efbposresZ: /* d=ZZ */
 +                            d         = pr->fbposres.geom - efbposresX;
 +                            AbsRef[d] = 1;
 +                            break;
 +                        default:
 +                            gmx_fatal(FARGS, " Invalid geometry for flat-bottom position restraint.\n"
 +                                      "Expected nr between 1 and %d. Found %d\n", efbposresNR-1,
 +                                      pr->fbposres.geom);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    return (AbsRef[XX] != 0 && AbsRef[YY] != 0 && AbsRef[ZZ] != 0);
 +}
 +
 +void triple_check(const char *mdparin, t_inputrec *ir, gmx_mtop_t *sys,
 +                  warninp_t wi)
 +{
 +    char                      err_buf[256];
 +    int                       i, m, g, nmol, npct;
 +    gmx_bool                  bCharge, bAcc;
 +    real                      gdt_max, *mgrp, mt;
 +    rvec                      acc;
 +    gmx_mtop_atomloop_block_t aloopb;
 +    gmx_mtop_atomloop_all_t   aloop;
 +    t_atom                   *atom;
 +    ivec                      AbsRef;
 +    char                      warn_buf[STRLEN];
 +
 +    set_warning_line(wi, mdparin, -1);
 +
 +    if (EI_DYNAMICS(ir->eI) && !EI_SD(ir->eI) && ir->eI != eiBD &&
 +        ir->comm_mode == ecmNO &&
 +        !(absolute_reference(ir, sys, FALSE, AbsRef) || ir->nsteps <= 10))
 +    {
 +        warning(wi, "You are not using center of mass motion removal (mdp option comm-mode), numerical rounding errors can lead to build up of kinetic energy of the center of mass");
 +    }
 +
 +    /* Check for pressure coupling with absolute position restraints */
 +    if (ir->epc != epcNO && ir->refcoord_scaling == erscNO)
 +    {
 +        absolute_reference(ir, sys, TRUE, AbsRef);
 +        {
 +            for (m = 0; m < DIM; m++)
 +            {
 +                if (AbsRef[m] && norm2(ir->compress[m]) > 0)
 +                {
 +                    warning(wi, "You are using pressure coupling with absolute position restraints, this will give artifacts. Use the refcoord_scaling option.");
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +
 +    bCharge = FALSE;
 +    aloopb  = gmx_mtop_atomloop_block_init(sys);
 +    while (gmx_mtop_atomloop_block_next(aloopb, &atom, &nmol))
 +    {
 +        if (atom->q != 0 || atom->qB != 0)
 +        {
 +            bCharge = TRUE;
 +        }
 +    }
 +
 +    if (!bCharge)
 +    {
 +        if (EEL_FULL(ir->coulombtype))
 +        {
 +            sprintf(err_buf,
 +                    "You are using full electrostatics treatment %s for a system without charges.\n"
 +                    "This costs a lot of performance for just processing zeros, consider using %s instead.\n",
 +                    EELTYPE(ir->coulombtype), EELTYPE(eelCUT));
 +            warning(wi, err_buf);
 +        }
 +    }
 +    else
 +    {
 +        if (ir->coulombtype == eelCUT && ir->rcoulomb > 0 && !ir->implicit_solvent)
 +        {
 +            sprintf(err_buf,
 +                    "You are using a plain Coulomb cut-off, which might produce artifacts.\n"
 +                    "You might want to consider using %s electrostatics.\n",
 +                    EELTYPE(eelPME));
 +            warning_note(wi, err_buf);
 +        }
 +    }
 +
 +    /* Generalized reaction field */
 +    if (ir->opts.ngtc == 0)
 +    {
 +        sprintf(err_buf, "No temperature coupling while using coulombtype %s",
 +                eel_names[eelGRF]);
 +        CHECK(ir->coulombtype == eelGRF);
 +    }
 +    else
 +    {
 +        sprintf(err_buf, "When using coulombtype = %s"
 +                " ref-t for temperature coupling should be > 0",
 +                eel_names[eelGRF]);
 +        CHECK((ir->coulombtype == eelGRF) && (ir->opts.ref_t[0] <= 0));
 +    }
 +
 +    if (ir->eI == eiSD1 &&
 +        (gmx_mtop_ftype_count(sys, F_CONSTR) > 0 ||
 +         gmx_mtop_ftype_count(sys, F_SETTLE) > 0))
 +    {
 +        sprintf(warn_buf, "With constraints integrator %s is less accurate, consider using %s instead", ei_names[ir->eI], ei_names[eiSD2]);
 +        warning_note(wi, warn_buf);
 +    }
 +
 +    bAcc = FALSE;
 +    for (i = 0; (i < sys->groups.grps[egcACC].nr); i++)
 +    {
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            if (fabs(ir->opts.acc[i][m]) > 1e-6)
 +            {
 +                bAcc = TRUE;
 +            }
 +        }
 +    }
 +    if (bAcc)
 +    {
 +        clear_rvec(acc);
 +        snew(mgrp, sys->groups.grps[egcACC].nr);
 +        aloop = gmx_mtop_atomloop_all_init(sys);
 +        while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
 +        {
 +            mgrp[ggrpnr(&sys->groups, egcACC, i)] += atom->m;
 +        }
 +        mt = 0.0;
 +        for (i = 0; (i < sys->groups.grps[egcACC].nr); i++)
 +        {
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                acc[m] += ir->opts.acc[i][m]*mgrp[i];
 +            }
 +            mt += mgrp[i];
 +        }
 +        for (m = 0; (m < DIM); m++)
 +        {
 +            if (fabs(acc[m]) > 1e-6)
 +            {
 +                const char *dim[DIM] = { "X", "Y", "Z" };
 +                fprintf(stderr,
 +                        "Net Acceleration in %s direction, will %s be corrected\n",
 +                        dim[m], ir->nstcomm != 0 ? "" : "not");
 +                if (ir->nstcomm != 0 && m < ndof_com(ir))
 +                {
 +                    acc[m] /= mt;
 +                    for (i = 0; (i < sys->groups.grps[egcACC].nr); i++)
 +                    {
 +                        ir->opts.acc[i][m] -= acc[m];
 +                    }
 +                }
 +            }
 +        }
 +        sfree(mgrp);
 +    }
 +
 +    if (ir->efep != efepNO && ir->fepvals->sc_alpha != 0 &&
 +        !gmx_within_tol(sys->ffparams.reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +    {
 +        gmx_fatal(FARGS, "Soft-core interactions are only supported with VdW repulsion power 12");
 +    }
 +
 +    if (ir->ePull != epullNO)
 +    {
 +        if (ir->pull->grp[0].nat == 0)
 +        {
 +            absolute_reference(ir, sys, FALSE, AbsRef);
 +            for (m = 0; m < DIM; m++)
 +            {
 +                if (ir->pull->dim[m] && !AbsRef[m])
 +                {
 +                    warning(wi, "You are using an absolute reference for pulling, but the rest of the system does not have an absolute reference. This will lead to artifacts.");
 +                    break;
 +                }
 +            }
 +        }
 +
 +        if (ir->pull->eGeom == epullgDIRPBC)
 +        {
 +            for (i = 0; i < 3; i++)
 +            {
 +                for (m = 0; m <= i; m++)
 +                {
 +                    if ((ir->epc != epcNO && ir->compress[i][m] != 0) ||
 +                        ir->deform[i][m] != 0)
 +                    {
 +                        for (g = 1; g < ir->pull->ngrp; g++)
 +                        {
 +                            if (ir->pull->grp[g].vec[m] != 0)
 +                            {
 +                                gmx_fatal(FARGS, "Can not have dynamic box while using pull geometry '%s' (dim %c)", EPULLGEOM(ir->pull->eGeom), 'x'+m);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    check_disre(sys);
 +}
 +
 +void double_check(t_inputrec *ir, matrix box, gmx_bool bConstr, warninp_t wi)
 +{
 +    real        min_size;
 +    gmx_bool    bTWIN;
 +    char        warn_buf[STRLEN];
 +    const char *ptr;
 +
 +    ptr = check_box(ir->ePBC, box);
 +    if (ptr)
 +    {
 +        warning_error(wi, ptr);
 +    }
 +
 +    if (bConstr && ir->eConstrAlg == econtSHAKE)
 +    {
 +        if (ir->shake_tol <= 0.0)
 +        {
 +            sprintf(warn_buf, "ERROR: shake-tol must be > 0 instead of %g\n",
 +                    ir->shake_tol);
 +            warning_error(wi, warn_buf);
 +        }
 +
 +        if (IR_TWINRANGE(*ir) && ir->nstlist > 1)
 +        {
 +            sprintf(warn_buf, "With twin-range cut-off's and SHAKE the virial and the pressure are incorrect.");
 +            if (ir->epc == epcNO)
 +            {
 +                warning(wi, warn_buf);
 +            }
 +            else
 +            {
 +                warning_error(wi, warn_buf);
 +            }
 +        }
 +    }
 +
 +    if ( (ir->eConstrAlg == econtLINCS) && bConstr)
 +    {
 +        /* If we have Lincs constraints: */
 +        if (ir->eI == eiMD && ir->etc == etcNO &&
 +            ir->eConstrAlg == econtLINCS && ir->nLincsIter == 1)
 +        {
 +            sprintf(warn_buf, "For energy conservation with LINCS, lincs_iter should be 2 or larger.\n");
 +            warning_note(wi, warn_buf);
 +        }
 +
 +        if ((ir->eI == eiCG || ir->eI == eiLBFGS) && (ir->nProjOrder < 8))
 +        {
 +            sprintf(warn_buf, "For accurate %s with LINCS constraints, lincs-order should be 8 or more.", ei_names[ir->eI]);
 +            warning_note(wi, warn_buf);
 +        }
 +        if (ir->epc == epcMTTK)
 +        {
 +            warning_error(wi, "MTTK not compatible with lincs -- use shake instead.");
 +        }
 +    }
 +
 +    if (ir->LincsWarnAngle > 90.0)
 +    {
 +        sprintf(warn_buf, "lincs-warnangle can not be larger than 90 degrees, setting it to 90.\n");
 +        warning(wi, warn_buf);
 +        ir->LincsWarnAngle = 90.0;
 +    }
 +
 +    if (ir->ePBC != epbcNONE)
 +    {
 +        if (ir->nstlist == 0)
 +        {
 +            warning(wi, "With nstlist=0 atoms are only put into the box at step 0, therefore drifting atoms might cause the simulation to crash.");
 +        }
 +        bTWIN = (ir->rlistlong > ir->rlist);
 +        if (ir->ns_type == ensGRID)
 +        {
 +            if (sqr(ir->rlistlong) >= max_cutoff2(ir->ePBC, box))
 +            {
 +                sprintf(warn_buf, "ERROR: The cut-off length is longer than half the shortest box vector or longer than the smallest box diagonal element. Increase the box size or decrease %s.\n",
 +                        bTWIN ? (ir->rcoulomb == ir->rlistlong ? "rcoulomb" : "rvdw") : "rlist");
 +                warning_error(wi, warn_buf);
 +            }
 +        }
 +        else
 +        {
 +            min_size = min(box[XX][XX], min(box[YY][YY], box[ZZ][ZZ]));
 +            if (2*ir->rlistlong >= min_size)
 +            {
 +                sprintf(warn_buf, "ERROR: One of the box lengths is smaller than twice the cut-off length. Increase the box size or decrease rlist.");
 +                warning_error(wi, warn_buf);
 +                if (TRICLINIC(box))
 +                {
 +                    fprintf(stderr, "Grid search might allow larger cut-off's than simple search with triclinic boxes.");
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +void check_chargegroup_radii(const gmx_mtop_t *mtop, const t_inputrec *ir,
 +                             rvec *x,
 +                             warninp_t wi)
 +{
 +    real rvdw1, rvdw2, rcoul1, rcoul2;
 +    char warn_buf[STRLEN];
 +
 +    calc_chargegroup_radii(mtop, x, &rvdw1, &rvdw2, &rcoul1, &rcoul2);
 +
 +    if (rvdw1 > 0)
 +    {
 +        printf("Largest charge group radii for Van der Waals: %5.3f, %5.3f nm\n",
 +               rvdw1, rvdw2);
 +    }
 +    if (rcoul1 > 0)
 +    {
 +        printf("Largest charge group radii for Coulomb:       %5.3f, %5.3f nm\n",
 +               rcoul1, rcoul2);
 +    }
 +
 +    if (ir->rlist > 0)
 +    {
 +        if (rvdw1  + rvdw2  > ir->rlist ||
 +            rcoul1 + rcoul2 > ir->rlist)
 +        {
 +            sprintf(warn_buf, "The sum of the two largest charge group radii (%f) is larger than rlist (%f)\n", max(rvdw1+rvdw2, rcoul1+rcoul2), ir->rlist);
 +            warning(wi, warn_buf);
 +        }
 +        else
 +        {
 +            /* Here we do not use the zero at cut-off macro,
 +             * since user defined interactions might purposely
 +             * not be zero at the cut-off.
 +             */
 +            if (EVDW_IS_ZERO_AT_CUTOFF(ir->vdwtype) &&
-                 sprintf(warn_buf, "The sum of the two largest charge group radii (%f) is larger than rlist (%f) - rvdw (%f)\n",
++                rvdw1 + rvdw2 > ir->rlistlong - ir->rvdw)
 +            {
-                         ir->rlist, ir->rvdw);
++                sprintf(warn_buf, "The sum of the two largest charge group radii (%f) is larger than %s (%f) - rvdw (%f)\n",
 +                        rvdw1+rvdw2,
++                        ir->rlistlong > ir->rlist ? "rlistlong" : "rlist",
++                        ir->rlistlong, ir->rvdw);
 +                if (ir_NVE(ir))
 +                {
 +                    warning(wi, warn_buf);
 +                }
 +                else
 +                {
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +            if (EEL_IS_ZERO_AT_CUTOFF(ir->coulombtype) &&
 +                rcoul1 + rcoul2 > ir->rlistlong - ir->rcoulomb)
 +            {
 +                sprintf(warn_buf, "The sum of the two largest charge group radii (%f) is larger than %s (%f) - rcoulomb (%f)\n",
 +                        rcoul1+rcoul2,
 +                        ir->rlistlong > ir->rlist ? "rlistlong" : "rlist",
 +                        ir->rlistlong, ir->rcoulomb);
 +                if (ir_NVE(ir))
 +                {
 +                    warning(wi, warn_buf);
 +                }
 +                else
 +                {
 +                    warning_note(wi, warn_buf);
 +                }
 +            }
 +        }
 +    }
 +}
index 3c4a810b777a954870d0d346e47e8676c142fb8b,0000000000000000000000000000000000000000..6bb20fb1d3e9897637499bba806655f248449fba
mode 100644,000000..100644
--- /dev/null
@@@ -1,306 -1,0 +1,310 @@@
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + * This file is part of Gromacs        Copyright (c) 1991-2008
 + * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
 + *
 + * 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.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org
 + *
 + * And Hey:
 + * Gnomes, ROck Monsters And Chili Sauce
 + */
 +
 +#ifndef _domdec_h
 +#define _domdec_h
 +
 +#include "typedefs.h"
 +#include "types/commrec.h"
 +#include "vsite.h"
 +#include "genborn.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +int ddglatnr(gmx_domdec_t *dd, int i);
 +/* Returns the global topology atom number belonging to local atom index i.
 + * This function is intended for writing ascii output
 + * and returns atom numbers starting at 1.
 + * When dd=NULL returns i+1.
 + */
 +
 +t_block *dd_charge_groups_global(gmx_domdec_t *dd);
 +/* Return a block struct for the charge groups of the whole system */
 +
 +gmx_bool dd_filled_nsgrid_home(gmx_domdec_t *dd);
 +/* Is the ns grid already filled with the home particles? */
 +
 +void dd_store_state(gmx_domdec_t *dd, t_state *state);
 +/* Store the global cg indices of the home cgs in state,
 + * so it can be reset, even after a new DD partitioning.
 + */
 +
 +gmx_domdec_zones_t *domdec_zones(gmx_domdec_t *dd);
 +
 +void dd_get_ns_ranges(gmx_domdec_t *dd, int icg,
 +                      int *jcg0, int *jcg1, ivec shift0, ivec shift1);
 +
 +int dd_natoms_vsite(gmx_domdec_t *dd);
 +
 +void dd_get_constraint_range(gmx_domdec_t *dd,
 +                             int *at_start, int *at_end);
 +
 +real dd_cutoff_mbody(gmx_domdec_t *dd);
 +
 +real dd_cutoff_twobody(gmx_domdec_t *dd);
 +
++void get_pme_nnodes(const gmx_domdec_t *dd,
++                    int *npmenodes_x, int *npmenodes_y);
++/* Get the number of PME nodes along x and y, can be called with dd=NULL */
++
 +gmx_bool gmx_pmeonlynode(t_commrec *cr, int nodeid);
 +/* Return if nodeid in cr->mpi_comm_mysim is a PME-only node */
 +
 +void get_pme_ddnodes(t_commrec *cr, int pmenodeid,
 +                     int *nmy_ddnodes, int **my_ddnodes, int *node_peer);
 +/* Returns the set of DD nodes that communicate with pme node cr->nodeid */
 +
 +int dd_pme_maxshift_x(gmx_domdec_t *dd);
 +/* Returns the maximum shift for coordinate communication in PME, dim x */
 +
 +int dd_pme_maxshift_y(gmx_domdec_t *dd);
 +/* Returns the maximum shift for coordinate communication in PME, dim y */
 +
 +void make_dd_communicators(FILE *fplog, t_commrec *cr, int dd_node_order);
 +
 +gmx_domdec_t *
 +init_domain_decomposition(FILE *fplog,
 +                          t_commrec *cr,
 +                          unsigned long Flags,
 +                          ivec nc,
 +                          real comm_distance_min, real rconstr,
 +                          const char *dlb_opt, real dlb_scale,
 +                          const char *sizex, const char *sizey, const char *sizez,
 +                          gmx_mtop_t *mtop, t_inputrec *ir,
 +                          matrix box, rvec *x,
 +                          gmx_ddbox_t *ddbox,
 +                          int *npme_x, int *npme_y);
 +
 +void dd_init_bondeds(FILE *fplog,
 +                     gmx_domdec_t *dd, gmx_mtop_t *mtop,
 +                     gmx_vsite_t *vsite, gmx_constr_t constr,
 +                     t_inputrec *ir, gmx_bool bBCheck, cginfo_mb_t *cginfo_mb);
 +/* Initialize data structures for bonded interactions */
 +
 +gmx_bool dd_bonded_molpbc(gmx_domdec_t *dd, int ePBC);
 +/* Returns if we need to do pbc for calculating bonded interactions */
 +
 +void set_dd_parameters(FILE *fplog, gmx_domdec_t *dd, real dlb_scale,
 +                       t_inputrec *ir, t_forcerec *fr,
 +                       gmx_ddbox_t *ddbox);
 +/* Set DD grid dimensions and limits,
 + * should be called after calling dd_init_bondeds.
 + */
 +
 +gmx_bool change_dd_cutoff(t_commrec *cr, t_state *state, t_inputrec *ir,
 +                          real cutoff_req );
 +/* Change the DD non-bonded communication cut-off.
 + * This could fail when trying to increase the cut-off,
 + * then FALSE will be returned and the cut-off is not modified.
 + */
 +
 +void change_dd_dlb_cutoff_limit(t_commrec *cr);
 +/* Domain boundary changes due to the DD dynamic load balancing can limit
 + * the cut-off distance that can be set in change_dd_cutoff. This function
 + * limits the DLB such that using the currently set cut-off should still be
 + * possible after subsequently setting a shorter cut-off with change_dd_cutoff.
 + */
 +
 +void setup_dd_grid(FILE *fplog, gmx_domdec_t *dd);
 +
 +void dd_collect_vec(gmx_domdec_t *dd,
 +                    t_state *state_local, rvec *lv, rvec *v);
 +
 +void dd_collect_state(gmx_domdec_t *dd,
 +                      t_state *state_local, t_state *state);
 +
 +enum {
 +    ddCyclStep, ddCyclPPduringPME, ddCyclF, ddCyclPME, ddCyclNr
 +};
 +
 +void dd_cycles_add(gmx_domdec_t *dd, float cycles, int ddCycl);
 +/* Add the wallcycle count to the DD counter */
 +
 +void dd_force_flop_start(gmx_domdec_t *dd, t_nrnb *nrnb);
 +/* Start the force flop count */
 +
 +void dd_force_flop_stop(gmx_domdec_t *dd, t_nrnb *nrnb);
 +/* Stop the force flop count */
 +
 +float dd_pme_f_ratio(gmx_domdec_t *dd);
 +/* Return the PME/PP force load ratio, or -1 if nothing was measured.
 + * Should only be called on the DD master node.
 + */
 +
 +void dd_move_x(gmx_domdec_t *dd, matrix box, rvec x[]);
 +/* Communicate the coordinates to the neighboring cells and do pbc. */
 +
 +void dd_move_f(gmx_domdec_t *dd, rvec f[], rvec *fshift);
 +/* Sum the forces over the neighboring cells.
 + * When fshift!=NULL the shift forces are updated to obtain
 + * the correct virial from the single sum including f.
 + */
 +
 +void dd_atom_spread_real(gmx_domdec_t *dd, real v[]);
 +/* Communicate a real for each atom to the neighboring cells. */
 +
 +void dd_atom_sum_real(gmx_domdec_t *dd, real v[]);
 +/* Sum the contributions to a real for each atom over the neighboring cells. */
 +
 +void dd_partition_system(FILE                *fplog,
 +                         gmx_large_int_t      step,
 +                         t_commrec           *cr,
 +                         gmx_bool             bMasterState,
 +                         int                  nstglobalcomm,
 +                         t_state             *state_global,
 +                         gmx_mtop_t          *top_global,
 +                         t_inputrec          *ir,
 +                         t_state             *state_local,
 +                         rvec               **f,
 +                         t_mdatoms           *mdatoms,
 +                         gmx_localtop_t      *top_local,
 +                         t_forcerec          *fr,
 +                         gmx_vsite_t         *vsite,
 +                         gmx_shellfc_t        shellfc,
 +                         gmx_constr_t         constr,
 +                         t_nrnb              *nrnb,
 +                         gmx_wallcycle_t      wcycle,
 +                         gmx_bool             bVerbose);
 +/* Partition the system over the nodes.
 + * step is only used for printing error messages.
 + * If bMasterState==TRUE then state_global from the master node is used,
 + * else state_local is redistributed between the nodes.
 + * When f!=NULL, *f will be reallocated to the size of state_local.
 + */
 +
 +void reset_dd_statistics_counters(gmx_domdec_t *dd);
 +/* Reset all the statistics and counters for total run counting */
 +
 +void print_dd_statistics(t_commrec *cr, t_inputrec *ir, FILE *fplog);
 +
 +/* In domdec_con.c */
 +
 +void dd_move_f_vsites(gmx_domdec_t *dd, rvec *f, rvec *fshift);
 +
 +void dd_clear_f_vsites(gmx_domdec_t *dd, rvec *f);
 +
 +void dd_move_x_constraints(gmx_domdec_t *dd, matrix box,
 +                           rvec *x0, rvec *x1);
 +/* Move x0 and also x1 if x1!=NULL */
 +
 +void dd_move_x_vsites(gmx_domdec_t *dd, matrix box, rvec *x);
 +
 +int *dd_constraints_nlocalatoms(gmx_domdec_t *dd);
 +
 +void dd_clear_local_constraint_indices(gmx_domdec_t *dd);
 +
 +void dd_clear_local_vsite_indices(gmx_domdec_t *dd);
 +
 +int dd_make_local_vsites(gmx_domdec_t *dd, int at_start, t_ilist *lil);
 +
 +int dd_make_local_constraints(gmx_domdec_t *dd, int at_start,
 +                              const gmx_mtop_t *mtop,
 +                              const int *cginfo,
 +                              gmx_constr_t constr, int nrec,
 +                              t_ilist *il_local);
 +
 +void init_domdec_constraints(gmx_domdec_t *dd,
 +                             gmx_mtop_t   *mtop,
 +                             gmx_constr_t  constr);
 +
 +void init_domdec_vsites(gmx_domdec_t *dd, int n_intercg_vsite);
 +
 +
 +/* In domdec_top.c */
 +
 +void dd_print_missing_interactions(FILE *fplog, t_commrec *cr,
 +                                   int local_count,  gmx_mtop_t *top_global, t_state *state_local);
 +
 +void dd_make_reverse_top(FILE *fplog,
 +                         gmx_domdec_t *dd, gmx_mtop_t *mtop,
 +                         gmx_vsite_t *vsite, gmx_constr_t constr,
 +                         t_inputrec *ir, gmx_bool bBCheck);
 +
 +void dd_make_local_cgs(gmx_domdec_t *dd, t_block *lcgs);
 +
 +void dd_make_local_top(FILE *fplog,
 +                       gmx_domdec_t *dd, gmx_domdec_zones_t *zones,
 +                       int npbcdim, matrix box,
 +                       rvec cellsize_min, ivec npulse,
 +                       t_forcerec *fr,
 +                       rvec *cgcm_or_x,
 +                       gmx_vsite_t *vsite,
 +                       gmx_mtop_t *top, gmx_localtop_t *ltop);
 +
 +void dd_sort_local_top(gmx_domdec_t *dd, t_mdatoms *mdatoms,
 +                       gmx_localtop_t *ltop);
 +/* Sort ltop->ilist when we are doing free energy. */
 +
 +gmx_localtop_t *dd_init_local_top(gmx_mtop_t *top_global);
 +
 +void dd_init_local_state(gmx_domdec_t *dd,
 +                         t_state *state_global, t_state *local_state);
 +
 +t_blocka *make_charge_group_links(gmx_mtop_t *mtop, gmx_domdec_t *dd,
 +                                  cginfo_mb_t *cginfo_mb);
 +
 +void dd_bonded_cg_distance(FILE *fplog,
 +                           gmx_domdec_t *dd, gmx_mtop_t *mtop,
 +                           t_inputrec *ir, rvec *x, matrix box,
 +                           gmx_bool bBCheck,
 +                           real *r_2b, real *r_mb);
 +
 +void write_dd_pdb(const char *fn, gmx_large_int_t step, const char *title,
 +                  gmx_mtop_t *mtop,
 +                  t_commrec *cr,
 +                  int natoms, rvec x[], matrix box);
 +/* Dump a pdb file with the current DD home + communicated atoms.
 + * When natoms=-1, dump all known atoms.
 + */
 +
 +
 +/* In domdec_setup.c */
 +
 +real comm_box_frac(ivec dd_nc, real cutoff, gmx_ddbox_t *ddbox);
 +/* Returns the volume fraction of the system that is communicated */
 +
 +real dd_choose_grid(FILE *fplog,
 +                    t_commrec *cr, gmx_domdec_t *dd, t_inputrec *ir,
 +                    gmx_mtop_t *mtop, matrix box, gmx_ddbox_t *ddbox,
 +                    gmx_bool bDynLoadBal, real dlb_scale,
 +                    real cellsize_limit, real cutoff_dd,
 +                    gmx_bool bInterCGBondeds, gmx_bool bInterCGMultiBody);
 +/* Determines the optimal DD cell setup dd->nc and possibly npmenodes
 + * for the system.
 + * On the master node returns the actual cellsize limit used.
 + */
 +
 +
 +/* In domdec_box.c */
 +
 +void set_ddbox(gmx_domdec_t *dd, gmx_bool bMasterState, t_commrec *cr_sum,
 +               t_inputrec *ir, matrix box,
 +               gmx_bool bCalcUnboundedSize, t_block *cgs, rvec *x,
 +               gmx_ddbox_t *ddbox);
 +
 +void set_ddbox_cr(t_commrec *cr, ivec *dd_nc,
 +                  t_inputrec *ir, matrix box, t_block *cgs, rvec *x,
 +                  gmx_ddbox_t *ddbox);
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif  /* _domdec_h */
index a83b9d876e6a0d58e8f6b2565c52c5a55a7725f8,0000000000000000000000000000000000000000..4a769efda4615d320744f2d449407c2caa476253
mode 100644,000000..100644
--- /dev/null
@@@ -1,333 -1,0 +1,463 @@@
- #undef gmx_epi32
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS Development Team
 + * Copyright (c) 2012, by the GROMACS development team, led by
 + * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 + * others, as listed in the AUTHORS file in the top-level source
 + * directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +/* The macros in this file are intended to be used for writing
 + * architecture-independent SIMD intrinsics code.
 + * To support a new architecture, adding macros here should be (nearly)
 + * all that is needed.
 + */
 +
 +/* Undefine all defines used below so we can include this file multiple times
 + * with different settings from the same source file.
 + */
 +
 +/* NOTE: SSE2 acceleration does not include floor or blendv */
 +
 +#undef GMX_SIMD_WIDTH_HERE
 +
- /* Only used for debugging */
- #undef gmx_storeu_pr
 +/* float/double SIMD register type */
 +#undef gmx_mm_pr
 +
++/* integer SIMD register type, only used in the tabulated PME kernels */
++#undef gmx_epi32
++
 +#undef gmx_load_pr
 +#undef gmx_load1_pr
 +#undef gmx_set1_pr
 +#undef gmx_setzero_pr
 +#undef gmx_store_pr
- /* Only used to speed up the nbnxn tabulated PME kernels */
 +
 +#undef gmx_add_pr
 +#undef gmx_sub_pr
 +#undef gmx_mul_pr
++/* For the FMA macros below, aim for c=d in code, so FMA3 uses 1 instruction */
++/* d = gmx_madd_pr(a,b,c): d = a*b + c, could use FMA3 or FMA4 */
++#undef gmx_madd_pr
++/* d = gmx_nmsub_pr(a,b,c): d = -a*b + c, could use FMA3 or FMA4 */
++#undef gmx_nmsub_pr
 +#undef gmx_max_pr
 +#undef gmx_cmplt_pr
++/* gmx_blendzero_pr(real a, boolean b) does: (b ? a : 0) */
++#undef gmx_blendzero_pr
++/* Logical operations on SIMD booleans */
 +#undef gmx_and_pr
 +#undef gmx_or_pr
 +#undef gmx_andnot_pr
 +
- /* Only used with x86 when blendv is faster than comparison */
- #undef gmx_blendv_pr
- #undef gmx_movemask_pr
++/* Only used for PBC in bonded interactions, can be avoided */
++#undef gmx_round_pr
++/* Not required, only used to speed up the nbnxn tabulated PME kernels */
++#undef GMX_HAVE_SIMD_FLOOR
 +#undef gmx_floor_pr
- /* Integer casts are only used for nbnxn x86 exclusion masks */
- #undef gmx_mm_castsi128_pr
- #undef gmx_mm_castsi256_pr
 +
- /* Conversions only used for nbnxn x86 exclusion masks and PME table lookup */
++/* Not required, only used when blendv is faster than comparison */
++#undef GMX_HAVE_SIMD_BLENDV
++#undef gmx_blendv_pr
++/* Not required, gmx_anytrue(x) returns if any of the boolean is x is True.
++ * If this is not present, define GMX_SIMD_IS_TRUE(real x),
++ * which should return x==True, where True is True as defined in SIMD.
++ */
++#undef GMX_HAVE_SIMD_ANYTRUE
++#undef gmx_anytrue_pr
++
++/* Integer set and cast are only used for nbnxn exclusion masks */
++#undef gmx_set1_epi32
++#undef gmx_castsi_pr
++/* For topology exclusion pair checking we need: ((a & b) ? True : False)
++ * when we do a bit-wise and between a and b.
++ * When integer SIMD operations are present, we use gmx_checkbitmask_epi32(a, b)
++ * Otherwise we do all operations, except for the set1, in reals.
++ */
++#undef gmx_load_si
++/* If the same bit is set in both input masks, return all bits 1, otherwise 0 */
++#undef gmx_checkbitmask_epi32
++/* As gmx_checkbitmask_epi32, but operates on reals. In double precision two
++ * identical 32-bit masks are set in one double and one or both can be used.
++ */
++#undef gmx_checkbitmask_pr
 +
- /* By defining GMX_MM128_HERE or GMX_MM256_HERE before including this file
-  * the same intrinsics, with defines, can be compiled for either 128 or 256
-  * bit wide SSE or AVX instructions.
-  * The gmx_ prefix is replaced by _mm_ or _mm256_ (SSE or AVX).
-  * The _pr suffix is replaced by _ps or _pd (single or double precision).
-  * Note that compiler settings will decide if 128-bit intrinsics will
++/* Conversions only used for PME table lookup */
 +#undef gmx_cvttpr_epi32
 +#undef gmx_cvtepi32_pr
 +
 +#undef gmx_invsqrt_pr
++/* sqrt+inv+sin+cos+acos+atan2 are only used for bonded potentials */
++#undef gmx_sqrt_pr
++#undef gmx_inv_pr
++#undef gmx_sincos_pr
++#undef gmx_acos_pr
++#undef gmx_atan_pr
++
 +#undef gmx_calc_rsq_pr
 +#undef gmx_sum4_pr
 +
 +/* Only required for nbnxn analytical PME kernels */
 +#undef gmx_pmecorrF_pr
 +#undef gmx_pmecorrV_pr
 +
 +
 +/* Half SIMD-width types and operations only for nbnxn 2xnn search+kernels */
 +#undef gmx_mm_hpr
 +
 +#undef gmx_load_hpr
 +#undef gmx_load1_hpr
 +#undef gmx_store_hpr
 +#undef gmx_add_hpr
 +#undef gmx_sub_hpr
 +
 +#undef gmx_sum4_hpr
 +
 +#undef gmx_2hpr_to_pr
 +
 +
- #if !defined GMX_MM128_HERE && !defined GMX_MM256_HERE
- #error "You should define GMX_MM128_HERE or GMX_MM256_HERE"
- #endif
++/* The same SIMD macros can be translated to SIMD intrinsics (and compiled
++ * to instructions for) different SIMD width and float precision.
++ *
++ * On x86: The gmx_ prefix is replaced by _mm_ or _mm256_ (SSE or AVX).
++ * The _pr suffix is replaced by _ps or _pd (for single or double precision).
++ * Compiler settings will decide if 128-bit intrinsics will
 + * be translated into SSE or AVX instructions.
 + */
 +
- #if defined GMX_MM128_HERE && defined GMX_MM256_HERE
- #error "You should not define both GMX_MM128_HERE and GMX_MM256_HERE"
 +
- #ifdef GMX_MM128_HERE
- #define gmx_epi32  __m128i
++/* Generic macros for obtaining a SIMD aligned pointer from pointer x */
++#undef gmx_simd_align_real
++#undef gmx_simd_align_int
++
++
++#ifdef GMX_USE_HALF_WIDTH_SIMD_HERE
++#if defined GMX_X86_AVX_256
++/* We have half SIMD width support, continue */
++#else
++#error "half SIMD width intrinsics are not supported"
++#endif
 +#endif
 +
 +
 +#ifdef GMX_X86_SSE2
 +
- #define gmx_storeu_pr     _mm_storeu_ps
++#if !defined GMX_X86_AVX_256 || defined GMX_USE_HALF_WIDTH_SIMD_HERE
 +
 +#ifndef GMX_DOUBLE
 +
 +#include "gmx_x86_simd_single.h"
 +
 +#define GMX_SIMD_WIDTH_HERE  4
 +
 +#define gmx_mm_pr  __m128
 +
++#define gmx_epi32  __m128i
++
 +#define gmx_load_pr       _mm_load_ps
 +#define gmx_load1_pr      _mm_load1_ps
 +#define gmx_set1_pr       _mm_set1_ps
 +#define gmx_setzero_pr    _mm_setzero_ps
 +#define gmx_store_pr      _mm_store_ps
- #define gmx_movemask_pr   _mm_movemask_ps
 +
 +#define gmx_add_pr        _mm_add_ps
 +#define gmx_sub_pr        _mm_sub_ps
 +#define gmx_mul_pr        _mm_mul_ps
++#ifdef GMX_X86_AVX_128_FMA
++#define gmx_madd_pr(a, b, c)   _mm_macc_ps(a, b, c)
++#define gmx_nmsub_pr(a, b, c)  _mm_nmacc_ps(a, b, c)
++#else
++#define gmx_madd_pr(a, b, c)   _mm_add_ps(c, _mm_mul_ps(a, b))
++#define gmx_nmsub_pr(a, b, c)  _mm_sub_ps(c, _mm_mul_ps(a, b))
++#endif
 +#define gmx_max_pr        _mm_max_ps
 +#define gmx_cmplt_pr      _mm_cmplt_ps
++#define gmx_blendzero_pr  _mm_and_ps
 +#define gmx_and_pr        _mm_and_ps
 +#define gmx_or_pr         _mm_or_ps
 +#define gmx_andnot_pr     _mm_andnot_ps
 +
++#ifdef GMX_X86_SSE4_1
++#define gmx_round_pr(x)   _mm_round_ps(x, 0x0)
++#define GMX_HAVE_SIMD_FLOOR
 +#define gmx_floor_pr      _mm_floor_ps
++#else
++#define gmx_round_pr(x)   _mm_cvtepi32_ps(_mm_cvtps_epi32(x))
++#endif
++
++#ifdef GMX_X86_SSE4_1
++#define GMX_HAVE_SIMD_BLENDV
 +#define gmx_blendv_pr     _mm_blendv_ps
++#endif
 +
- #define gmx_mm_castsi128_pr gmx_mm_castsi128_ps
++#define GMX_HAVE_SIMD_ANYTRUE
++#define gmx_anytrue_pr    _mm_movemask_ps
 +
- #define gmx_storeu_pr     _mm_storeu_pd
++#define gmx_set1_epi32    _mm_set1_epi32
++#define gmx_castsi_pr     gmx_mm_castsi128_ps
++#define gmx_load_si(i)    _mm_load_si128((__m128i *) (i))
++#define gmx_checkbitmask_epi32(m0, m1) _mm_cmpeq_epi32(_mm_andnot_si128(m0, m1), _mm_setzero_si128())
 +
 +#define gmx_cvttpr_epi32  _mm_cvttps_epi32
 +#define gmx_cvtepi32_pr   _mm_cvtepi32_ps
 +
 +#define gmx_invsqrt_pr    gmx_mm_invsqrt_ps
++#define gmx_sqrt_pr       gmx_mm_sqrt_ps
++#define gmx_inv_pr        gmx_mm_inv_ps
++#define gmx_sincos_pr     gmx_mm_sincos_ps
++#define gmx_acos_pr       gmx_mm_acos_ps
++#define gmx_atan2_pr      gmx_mm_atan2_ps
++
 +#define gmx_calc_rsq_pr   gmx_mm_calc_rsq_ps
 +#define gmx_sum4_pr       gmx_mm_sum4_ps
 +
 +#define gmx_pmecorrF_pr   gmx_mm_pmecorrF_ps
 +#define gmx_pmecorrV_pr   gmx_mm_pmecorrV_ps
 +
 +#else /* ifndef GMX_DOUBLE */
 +
 +#include "gmx_x86_simd_double.h"
 +
 +#define GMX_SIMD_WIDTH_HERE  2
 +
 +#define gmx_mm_pr  __m128d
 +
++#define gmx_epi32  __m128i
++
 +#define gmx_load_pr       _mm_load_pd
 +#define gmx_load1_pr      _mm_load1_pd
 +#define gmx_set1_pr       _mm_set1_pd
 +#define gmx_setzero_pr    _mm_setzero_pd
 +#define gmx_store_pr      _mm_store_pd
- #define gmx_movemask_pr   _mm_movemask_pd
 +
 +#define gmx_add_pr        _mm_add_pd
 +#define gmx_sub_pr        _mm_sub_pd
 +#define gmx_mul_pr        _mm_mul_pd
++#ifdef GMX_X86_AVX_128_FMA
++#define gmx_madd_pr(a, b, c)   _mm_macc_pd(a, b, c)
++#define gmx_nmsub_pr(a, b, c)  _mm_nmacc_pd(a, b, c)
++#else
++#define gmx_madd_pr(a, b, c)   _mm_add_pd(c, _mm_mul_pd(a, b))
++#define gmx_nmsub_pr(a, b, c)  _mm_sub_pd(c, _mm_mul_pd(a, b))
++#endif
 +#define gmx_max_pr        _mm_max_pd
 +#define gmx_cmplt_pr      _mm_cmplt_pd
++#define gmx_blendzero_pr  _mm_and_pd
 +#define gmx_and_pr        _mm_and_pd
 +#define gmx_or_pr         _mm_or_pd
 +#define gmx_andnot_pr     _mm_andnot_pd
 +
++#ifdef GMX_X86_SSE4_1
++#define gmx_round_pr(x)   _mm_round_pd(x, 0x0)
++#define GMX_HAVE_SIMD_FLOOR
 +#define gmx_floor_pr      _mm_floor_pd
++#else
++#define gmx_round_pr(x)   _mm_cvtepi32_pd(_mm_cvtpd_epi32(x))
++/* gmx_floor_pr is not used in code for pre-SSE4_1 hardware */
++#endif
++
++#ifdef GMX_X86_SSE4_1
++#define GMX_HAVE_SIMD_BLENDV
 +#define gmx_blendv_pr     _mm_blendv_pd
++#endif
 +
- #define gmx_mm_castsi128_pr gmx_mm_castsi128_pd
++#define GMX_HAVE_SIMD_ANYTRUE
++#define gmx_anytrue_pr    _mm_movemask_pd
 +
- #endif /* GMX_MM128_HERE */
- #ifdef GMX_MM256_HERE
- #define gmx_epi32 __m256i
++#define gmx_set1_epi32    _mm_set1_epi32
++#define gmx_castsi_pr     gmx_mm_castsi128_pd
++#define gmx_load_si(i)    _mm_load_si128((__m128i *) (i))
++#define gmx_checkbitmask_epi32(m0, m1) _mm_cmpeq_epi32(_mm_andnot_si128(m0, m1), _mm_setzero_si128())
 +
 +#define gmx_cvttpr_epi32  _mm_cvttpd_epi32
 +#define gmx_cvtepi32_pr   _mm_cvtepi32_pd
 +
 +#define gmx_invsqrt_pr    gmx_mm_invsqrt_pd
++#define gmx_sqrt_pr       gmx_mm_sqrt_pd
++#define gmx_inv_pr        gmx_mm_inv_pd
++#define gmx_sincos_pr     gmx_mm_sincos_pd
++#define gmx_acos_pr       gmx_mm_acos_pd
++#define gmx_atan2_pr      gmx_mm_atan2_pd
++
 +#define gmx_calc_rsq_pr   gmx_mm_calc_rsq_pd
 +#define gmx_sum4_pr       gmx_mm_sum4_pd
 +
 +#define gmx_pmecorrF_pr   gmx_mm_pmecorrF_pd
 +#define gmx_pmecorrV_pr   gmx_mm_pmecorrV_pd
 +
 +#endif /* ifndef GMX_DOUBLE */
 +
- #define gmx_storeu_pr     _mm256_storeu_ps
++#else
++/* We have GMX_X86_AVX_256 and not GMX_USE_HALF_WIDTH_SIMD_HERE,
++ * so we use 256-bit SIMD.
++ */
 +
 +#ifndef GMX_DOUBLE
 +
 +#include "gmx_x86_simd_single.h"
 +
 +#define GMX_SIMD_WIDTH_HERE  8
 +
 +#define gmx_mm_pr  __m256
 +
++#define gmx_epi32  __m256i
++
 +#define gmx_load_pr       _mm256_load_ps
 +#define gmx_load1_pr(x)   _mm256_set1_ps((x)[0])
 +#define gmx_set1_pr       _mm256_set1_ps
 +#define gmx_setzero_pr    _mm256_setzero_ps
 +#define gmx_store_pr      _mm256_store_ps
- /* Not-equal (ordered, non-signaling)  */
- #define gmx_cmpneq_pr(x, y)  _mm256_cmp_ps(x, y, 0x0c)
- /* Less-than (ordered, non-signaling)  */
 +
 +#define gmx_add_pr        _mm256_add_ps
 +#define gmx_sub_pr        _mm256_sub_ps
 +#define gmx_mul_pr        _mm256_mul_ps
++#define gmx_madd_pr(a, b, c)   _mm256_add_ps(c, _mm256_mul_ps(a, b))
++#define gmx_nmsub_pr(a, b, c)  _mm256_sub_ps(c, _mm256_mul_ps(a, b))
 +#define gmx_max_pr        _mm256_max_ps
- #define gmx_movemask_pr   _mm256_movemask_ps
++/* Less-than (we use ordered, non-signaling, but that's not required) */
 +#define gmx_cmplt_pr(x, y) _mm256_cmp_ps(x, y, 0x11)
++#define gmx_blendzero_pr  _mm256_and_ps
 +#define gmx_and_pr        _mm256_and_ps
 +#define gmx_or_pr         _mm256_or_ps
 +#define gmx_andnot_pr     _mm256_andnot_ps
 +
++#define gmx_round_pr(x)   _mm256_round_ps(x, 0x0)
++#define GMX_HAVE_SIMD_FLOOR
 +#define gmx_floor_pr      _mm256_floor_ps
++
++#define GMX_HAVE_SIMD_BLENDV
 +#define gmx_blendv_pr     _mm256_blendv_ps
 +
- #define gmx_mm_castsi256_pr _mm256_castsi256_ps
++#define GMX_HAVE_SIMD_ANYTRUE
++#define gmx_anytrue_pr    _mm256_movemask_ps
 +
- #define gmx_loaddh_pr     gmx_mm256_load4_ps
- /* Half SIMD-width type */
- #define gmx_mm_hpr  __m128
- /* Half SIMD-width macros */
- #define gmx_load_hpr      _mm_load_ps
- #define gmx_load1_hpr(x)  _mm_set1_ps((x)[0])
- #define gmx_store_hpr     _mm_store_ps
- #define gmx_add_hpr       _mm_add_ps
- #define gmx_sub_hpr       _mm_sub_ps
- #define gmx_sum4_hpr      gmx_mm256_sum4h_m128
- /* Conversion between half and full SIMD-width */
- #define gmx_2hpr_to_pr    gmx_mm256_set_m128
++#define gmx_set1_epi32    _mm256_set1_epi32
++#define gmx_castsi_pr     _mm256_castsi256_ps
++/* With <= 16 bits used the cast and conversion should not be required,
++ * since only mantissa bits are set and that would give a non-zero float,
++ * but with the Intel compiler this does not work correctly.
++ */
++#define gmx_checkbitmask_pr(m0, m1) _mm256_cmp_ps(_mm256_cvtepi32_ps(_mm256_castps_si256(_mm256_and_ps(m0, m1))), _mm256_setzero_ps(), 0x0c)
 +
 +#define gmx_cvttpr_epi32  _mm256_cvttps_epi32
 +
 +#define gmx_invsqrt_pr    gmx_mm256_invsqrt_ps
++#define gmx_sqrt_pr       gmx_mm256_sqrt_ps
++#define gmx_inv_pr        gmx_mm256_inv_ps
++#define gmx_sincos_pr     gmx_mm256_sincos_ps
++#define gmx_acos_pr       gmx_mm256_acos_ps
++#define gmx_atan2_pr      gmx_mm256_atan2_ps
++
 +#define gmx_calc_rsq_pr   gmx_mm256_calc_rsq_ps
 +#define gmx_sum4_pr       gmx_mm256_sum4_ps
 +
 +#define gmx_pmecorrF_pr   gmx_mm256_pmecorrF_ps
 +#define gmx_pmecorrV_pr   gmx_mm256_pmecorrV_ps
 +
- #define gmx_storeu_pr     _mm256_storeu_pd
 +#else
 +
 +#include "gmx_x86_simd_double.h"
 +
 +#define GMX_SIMD_WIDTH_HERE  4
 +
 +#define gmx_mm_pr  __m256d
 +
++/* We use 128-bit integer registers because of missing 256-bit operations */
++#define gmx_epi32  __m128i
++
 +#define gmx_load_pr       _mm256_load_pd
 +#define gmx_load1_pr(x)   _mm256_set1_pd((x)[0])
 +#define gmx_set1_pr       _mm256_set1_pd
 +#define gmx_setzero_pr    _mm256_setzero_pd
 +#define gmx_store_pr      _mm256_store_pd
- /* Not-equal (ordered, non-signaling)  */
- #define gmx_cmpneq_pr(x, y)  _mm256_cmp_pd(x, y, 0x0c)
- /* Less-than (ordered, non-signaling)  */
 +
 +#define gmx_add_pr        _mm256_add_pd
 +#define gmx_sub_pr        _mm256_sub_pd
 +#define gmx_mul_pr        _mm256_mul_pd
++#define gmx_madd_pr(a, b, c)   _mm256_add_pd(c, _mm256_mul_pd(a, b))
++#define gmx_nmsub_pr(a, b, c)  _mm256_sub_pd(c, _mm256_mul_pd(a, b))
 +#define gmx_max_pr        _mm256_max_pd
- #define gmx_movemask_pr   _mm256_movemask_pd
++/* Less-than (we use ordered, non-signaling, but that's not required) */
 +#define gmx_cmplt_pr(x, y) _mm256_cmp_pd(x, y, 0x11)
++#define gmx_blendzero_pr  _mm256_and_pd
 +#define gmx_and_pr        _mm256_and_pd
 +#define gmx_or_pr         _mm256_or_pd
 +#define gmx_andnot_pr     _mm256_andnot_pd
 +
++#define gmx_round_pr(x)   _mm256_round_pd(x, 0x0)
++#define GMX_HAVE_SIMD_FLOOR
 +#define gmx_floor_pr      _mm256_floor_pd
++
++#define GMX_HAVE_SIMD_BLENDV
 +#define gmx_blendv_pr     _mm256_blendv_pd
 +
- #define gmx_mm_castsi256_pr _mm256_castsi256_pd
++#define GMX_HAVE_SIMD_ANYTRUE
++#define gmx_anytrue_pr    _mm256_movemask_pd
 +
- #endif
++#define gmx_set1_epi32    _mm256_set1_epi32
++#define gmx_castsi_pr     _mm256_castsi256_pd
++/* With <= 16 bits used the cast and conversion should not be required,
++ * since only mantissa bits are set and that would give a non-zero float,
++ * but with the Intel compiler this does not work correctly.
++ * Because AVX does not have int->double conversion, we convert via float.
++ */
++#define gmx_checkbitmask_pr(m0, m1) _mm256_cmp_pd(_mm256_castps_pd(_mm256_cvtepi32_ps(_mm256_castpd_si256(_mm256_and_pd(m0, m1)))), _mm256_setzero_pd(), 0x0c)
 +
 +#define gmx_cvttpr_epi32  _mm256_cvttpd_epi32
 +
 +#define gmx_invsqrt_pr    gmx_mm256_invsqrt_pd
++#define gmx_sqrt_pr       gmx_mm256_sqrt_pd
++#define gmx_inv_pr        gmx_mm256_inv_pd
++#define gmx_sincos_pr     gmx_mm256_sincos_pd
++#define gmx_acos_pr       gmx_mm256_acos_pd
++#define gmx_atan2_pr      gmx_mm256_atan2_pd
++
 +#define gmx_calc_rsq_pr   gmx_mm256_calc_rsq_pd
 +#define gmx_sum4_pr       gmx_mm256_sum4_pd
 +
 +#define gmx_pmecorrF_pr   gmx_mm256_pmecorrF_pd
 +#define gmx_pmecorrV_pr   gmx_mm256_pmecorrV_pd
 +
- #endif /* GMX_MM256_HERE */
++#endif /* GMX_DOUBLE */
 +
++#endif /* 128- or 256-bit x86 SIMD */
 +
 +#endif /* GMX_X86_SSE2 */
++
++
++/* Generic macros to extract a SIMD aligned pointer from a pointer x.
++ * x should have at least GMX_SIMD_WIDTH_HERE elements extra compared
++ * to how many you want to use, to avoid indexing outside the aligned region.
++ */
++
++#define gmx_simd_align_real(x)  (real *)(((size_t)((x)+GMX_SIMD_WIDTH_HERE)) & (~((size_t)(GMX_SIMD_WIDTH_HERE*sizeof(real)-1))))
++
++#define gmx_simd_align_int(x)   (int  *)(((size_t)((x)+GMX_SIMD_WIDTH_HERE)) & (~((size_t)(GMX_SIMD_WIDTH_HERE*sizeof(int )-1))))
index 1d3ac782de4d14202cf576f7c3ed39b9325f5e79,0000000000000000000000000000000000000000..e096ed63f7093fb8f89c5a2bedf63fe9f8d6ba9f
mode 100644,000000..100644
--- /dev/null
@@@ -1,260 -1,0 +1,260 @@@
-  * Although the rest of Gromacs is GPL, you can copy and use the XDR
 +/*
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gromacs Runs On Most of All Computer Systems
 + */
 +
 +#ifndef _gmx_system_xdr_h
 +#define _gmx_system_xdr_h
 +
 +#include <limits.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +
 +
 +/*
 + * This header file is ONLY used on windows systems, since these do
 + * not include the XDR routines present on a unix machine. It will
 + * most probably work on other platforms too, but make sure you
 + * test that the xtc files produced are ok before using it.
 + *
 + * This header file contains Gromacs versions of the definitions for
 + * Sun External Data Representation (XDR) headers and routines.
 + *
 + * On most UNIX systems this is already present as part of your
 + * system libraries, but since we want to make Gromacs portable to
 + * platforms like Microsoft Windows we have created a private version
 + * of the necessary routines and distribute them with the Gromacs source.
 + *
++ * Although the rest of Gromacs is LGPL, you can copy and use the XDR
 + * routines in any way you want as long as you obey Sun's license:
 + *
 + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
 + * unrestricted use provided that this legend is included on all tape
 + * media and as a part of the software program in whole or part.  Users
 + * may copy or modify Sun RPC without charge, but are not authorized
 + * to license or distribute it to anyone else except as part of a product or
 + * program developed by the user.
 + *
 + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 + *
 + * Sun RPC is provided with no support and without any obligation on the
 + * part of Sun Microsystems, Inc. to assist in its use, correction,
 + * modification or enhancement.
 + *
 + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
 + * OR ANY PART THEREOF.
 + *
 + * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 + * or profits or other special, indirect and consequential damages, even if
 + * Sun has been advised of the possibility of such damages.
 + *
 + * Sun Microsystems, Inc.
 + * 2550 Garcia Avenue
 + * Mountain View, California  94043
 + */
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +/*
 + * Xdr operations.  XDR_ENCODE causes the type to be encoded into the
 + * stream.  XDR_DECODE causes the type to be extracted from the stream.
 + * XDR_FREE can be used to release the space allocated by an
 + * XDR_DECODE request.
 + */
 +
 +/* We already have a boolean type in Gromacs, but the XDR library
 + * one has a slightly different name (the calls should be identical).
 + */
 +typedef int bool_t;
 +
 +/*
 + * Aninteger type that is 32 bits wide. Check if int,
 + * long or short is 32 bits and die if none of them is :-)
 + */
 +#if (INT_MAX == 2147483647)
 +typedef int xdr_int32_t;
 +typedef unsigned int xdr_uint32_t;
 +#elif (LONG_MAX == 2147483647L)
 +typedef long xdr_int32_t;
 +typedef unsigned long xdr_uint32_t;
 +#elif (SHRT_MAX == 2147483647)
 +typedef short xdr_int32_t;
 +typedef unsigned short xdr_uint32_t;
 +#else
 +#  error ERROR: No 32 bit wide integer type found!
 +#endif
 +
 +enum xdr_op {
 +    XDR_ENCODE = 0,
 +    XDR_DECODE = 1,
 +    XDR_FREE   = 2
 +};
 +
 +#ifndef FALSE
 +#      define  FALSE   (0)
 +#endif
 +#ifndef TRUE
 +#      define  TRUE    (1)
 +#endif
 +
 +
 +#define BYTES_PER_XDR_UNIT  (4)
 +/* Macro to round up to units of 4. */
 +#define XDR_RNDUP(x)  (((x) + BYTES_PER_XDR_UNIT - 1) & ~(BYTES_PER_XDR_UNIT - 1))
 +
 +
 +/*
 + * The XDR handle.
 + * Contains operation which is being applied to the stream,
 + * an operations vector for the particular implementation (e.g. see xdr_mem.c),
 + * and two private fields for the use of the particular implementation.
 + */
 +typedef struct XDR XDR;
 +struct XDR
 +{
 +    enum xdr_op x_op;       /* operation; fast additional param */
 +    struct xdr_ops
 +    {
 +        bool_t       (*x_getbytes) (XDR *__xdrs, char *__addr, unsigned int __len);
 +        /* get some bytes from " */
 +        bool_t       (*x_putbytes) (XDR *__xdrs, char *__addr, unsigned int __len);
 +        /* put some bytes to " */
 +        unsigned int (*x_getpostn) (XDR *__xdrs);
 +        /* returns bytes off from beginning */
 +        bool_t       (*x_setpostn) (XDR *__xdrs, unsigned int __pos);
 +        /* lets you reposition the stream */
 +        xdr_int32_t *(*x_inline) (XDR *__xdrs, int __len);
 +        /* buf quick ptr to buffered data */
 +        void         (*x_destroy) (XDR *__xdrs);
 +        /* free privates of this xdr_stream */
 +        bool_t       (*x_getint32) (XDR *__xdrs, xdr_int32_t *__ip);
 +        /* get a int from underlying stream */
 +        bool_t       (*x_putint32) (XDR *__xdrs, xdr_int32_t *__ip);
 +        /* put a int to " */
 +        bool_t       (*x_getuint32) (XDR *__xdrs, xdr_uint32_t *__ip);
 +        /* get a unsigned int from underlying stream */
 +        bool_t       (*x_putuint32) (XDR *__xdrs, xdr_uint32_t *__ip);
 +        /* put a int to " */
 +    }
 +    *x_ops;
 +    char *x_public;     /* users' data */
 +    char *x_private;    /* pointer to private data */
 +    char *x_base;       /* private used for position info */
 +    int   x_handy;      /* extra private word */
 +};
 +
 +/*
 + * A xdrproc_t exists for each data type which is to be encoded or decoded.
 + *
 + * The second argument to the xdrproc_t is a pointer to an opaque pointer.
 + * The opaque pointer generally points to a structure of the data type
 + * to be decoded.  If this pointer is 0, then the type routines should
 + * allocate dynamic storage of the appropriate size and return it.
 + */
 +
 +typedef bool_t (*xdrproc_t) (XDR *, void *, ...);
 +
 +/*
 + * Operations defined on a XDR handle
 + *
 + * XDR          *xdrs;
 + * xdr_int32_t  *int32p;
 + * long         *longp;
 + * char         *addr;
 + * unsigned int  len;
 + * unsigned int  pos;
 + */
 +
 +
 +#define xdr_getint32(xdrs, int32p)                      \
 +    (*(xdrs)->x_ops->x_getint32)(xdrs, int32p)
 +
 +#define xdr_putint32(xdrs, int32p)                      \
 +    (*(xdrs)->x_ops->x_putint32)(xdrs, int32p)
 +
 +#define xdr_getuint32(xdrs, uint32p)                      \
 +    (*(xdrs)->x_ops->x_getuint32)(xdrs, uint32p)
 +
 +#define xdr_putuint32(xdrs, uint32p)                      \
 +    (*(xdrs)->x_ops->x_putuint32)(xdrs, uint32p)
 +
 +#define xdr_getbytes(xdrs, addr, len)           \
 +    (*(xdrs)->x_ops->x_getbytes)(xdrs, addr, len)
 +
 +#define xdr_putbytes(xdrs, addr, len)           \
 +    (*(xdrs)->x_ops->x_putbytes)(xdrs, addr, len)
 +
 +#define xdr_getpos(xdrs)                \
 +    (*(xdrs)->x_ops->x_getpostn)(xdrs)
 +
 +#define xdr_setpos(xdrs, pos)               \
 +    (*(xdrs)->x_ops->x_setpostn)(xdrs, pos)
 +
 +#define xdr_inline(xdrs, len)               \
 +    (*(xdrs)->x_ops->x_inline)(xdrs, len)
 +
 +#define xdr_destroy(xdrs)                   \
 +    do {                            \
 +        if ((xdrs)->x_ops->x_destroy) {           \
 +            (*(xdrs)->x_ops->x_destroy)(xdrs); }  \
 +    } while (0)
 +
 +
 +bool_t xdr_int (XDR *__xdrs, int *__ip);
 +bool_t xdr_u_int (XDR *__xdrs, unsigned int *__ip);
 +bool_t xdr_short (XDR *__xdrs, short *__ip);
 +bool_t xdr_u_short (XDR *__xdrs, unsigned short *__ip);
 +bool_t xdr_bool (XDR *__xdrs, int *__bp);
 +bool_t xdr_opaque (XDR *__xdrs, char *__cp, unsigned int __cnt);
 +bool_t xdr_string (XDR *__xdrs, char **__cpp, unsigned int __maxsize);
 +bool_t xdr_char (XDR *__xdrs, char *__cp);
 +bool_t xdr_u_char (XDR *__xdrs, unsigned char *__cp);
 +bool_t xdr_vector (XDR *__xdrs, char *__basep, unsigned int __nelem,
 +                   unsigned int __elemsize, xdrproc_t __xdr_elem);
 +bool_t xdr_float (XDR *__xdrs, float *__fp);
 +bool_t xdr_double (XDR *__xdrs, double *__dp);
 +void xdrstdio_create (XDR *__xdrs, FILE *__file, enum xdr_op __xop);
 +
 +/* free memory buffers for xdr */
 +void xdr_free (xdrproc_t __proc, char *__objp);
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif /* _gmx_system_xdr_h */
index 0181f477f6916662cb64b836dfd159731a8554cb,0000000000000000000000000000000000000000..8b8f697544a88c0dc73e9457eafda7b2de16a03e
mode 100644,000000..100644
--- /dev/null
@@@ -1,212 -1,0 +1,212 @@@
-              int nsteps_cmdline, int nstepout, int resetstep,
 +/*
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gromacs Runs On Most of All Computer Systems
 + */
 +
 +#ifndef _mdrun_h
 +#define _mdrun_h
 +
 +#include <stdio.h>
 +#include <time.h>
 +#include "typedefs.h"
 +#include "network.h"
 +#include "sim_util.h"
 +#include "tgroup.h"
 +#include "filenm.h"
 +#include "mshift.h"
 +#include "force.h"
 +#include "edsam.h"
 +#include "mdebin.h"
 +#include "vcm.h"
 +#include "vsite.h"
 +#include "pull.h"
 +#include "update.h"
 +#include "types/membedt.h"
 +#include "types/globsig.h"
 +
 +
 +#ifdef GMX_THREAD_MPI
 +#include "thread_mpi/threads.h"
 +#endif
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +#define MD_POLARISE       (1<<2)
 +#define MD_IONIZE         (1<<3)
 +#define MD_RERUN          (1<<4)
 +#define MD_RERUN_VSITE    (1<<5)
 +#define MD_FFSCAN         (1<<6)
 +#define MD_SEPPOT         (1<<7)
 +#define MD_PARTDEC        (1<<9)
 +#define MD_DDBONDCHECK    (1<<10)
 +#define MD_DDBONDCOMM     (1<<11)
 +#define MD_CONFOUT        (1<<12)
 +#define MD_REPRODUCIBLE   (1<<13)
 +#define MD_READ_RNG       (1<<14)
 +#define MD_APPENDFILES    (1<<15)
 +#define MD_APPENDFILESSET (1<<21)
 +#define MD_KEEPANDNUMCPT  (1<<16)
 +#define MD_READ_EKIN      (1<<17)
 +#define MD_STARTFROMCPT   (1<<18)
 +#define MD_RESETCOUNTERSHALFWAY (1<<19)
 +#define MD_TUNEPME        (1<<20)
 +#define MD_TESTVERLET     (1<<22)
 +
 +/* The options for the domain decomposition MPI task ordering */
 +enum {
 +    ddnoSEL, ddnoINTERLEAVE, ddnoPP_PME, ddnoCARTESIAN, ddnoNR
 +};
 +
 +/* The options for the thread affinity setting, default: auto */
 +enum {
 +    threadaffSEL, threadaffAUTO, threadaffON, threadaffOFF, threadaffNR
 +};
 +
 +typedef struct {
 +    int      nthreads_tot;        /* Total number of threads requested (TMPI) */
 +    int      nthreads_tmpi;       /* Number of TMPI threads requested         */
 +    int      nthreads_omp;        /* Number of OpenMP threads requested       */
 +    int      nthreads_omp_pme;    /* As nthreads_omp, but for PME only nodes  */
 +    int      thread_affinity;     /* Thread affinity switch, see enum above   */
 +    int      core_pinning_stride; /* Logical core pinning stride              */
 +    int      core_pinning_offset; /* Logical core pinning offset              */
 +    char    *gpu_id;              /* GPU id's to use, each specified as chars */
 +} gmx_hw_opt_t;
 +
 +/* Variables for temporary use with the deform option,
 + * used in runner.c and md.c.
 + * (These variables should be stored in the tpx file.)
 + */
 +extern gmx_large_int_t     deform_init_init_step_tpx;
 +extern matrix              deform_init_box_tpx;
 +#ifdef GMX_THREAD_MPI
 +extern tMPI_Thread_mutex_t deform_init_box_mutex;
 +
 +/* The minimum number of atoms per tMPI thread. With fewer atoms than this,
 + * the number of threads will get lowered.
 + */
 +#define MIN_ATOMS_PER_MPI_THREAD    90
 +#define MIN_ATOMS_PER_GPU           900
 +#endif
 +
 +
 +typedef double gmx_integrator_t (FILE *log, t_commrec *cr,
 +                                 int nfile, const t_filenm fnm[],
 +                                 const output_env_t oenv, gmx_bool bVerbose,
 +                                 gmx_bool bCompact, int nstglobalcomm,
 +                                 gmx_vsite_t *vsite, gmx_constr_t constr,
 +                                 int stepout,
 +                                 t_inputrec *inputrec,
 +                                 gmx_mtop_t *mtop, t_fcdata *fcd,
 +                                 t_state *state,
 +                                 t_mdatoms *mdatoms,
 +                                 t_nrnb *nrnb, gmx_wallcycle_t wcycle,
 +                                 gmx_edsam_t ed,
 +                                 t_forcerec *fr,
 +                                 int repl_ex_nst, int repl_ex_nex, int repl_ex_seed,
 +                                 gmx_membed_t membed,
 +                                 real cpt_period, real max_hours,
 +                                 const char *deviceOptions,
 +                                 unsigned long Flags,
 +                                 gmx_runtime_t *runtime);
 +
 +/* ROUTINES from md.c */
 +
 +gmx_integrator_t do_md;
 +
 +
 +/* ROUTINES from minimize.c */
 +
 +gmx_integrator_t do_steep;
 +/* Do steepest descents EM */
 +
 +gmx_integrator_t do_cg;
 +/* Do conjugate gradient EM */
 +
 +gmx_integrator_t do_lbfgs;
 +/* Do conjugate gradient L-BFGS */
 +
 +gmx_integrator_t do_nm;
 +/* Do normal mode analysis */
 +
 +/* ROUTINES from tpi.c */
 +
 +gmx_integrator_t do_tpi;
 +/* Do test particle insertion */
 +
 +void init_npt_masses(t_inputrec *ir, t_state *state, t_extmass *MassQ, gmx_bool bInit);
 +
 +int ExpandedEnsembleDynamics(FILE *log, t_inputrec *ir, gmx_enerdata_t *enerd,
 +                             t_state *state, t_extmass *MassQ, df_history_t *dfhist,
 +                             gmx_large_int_t step, gmx_rng_t mcrng,
 +                             rvec *v, t_mdatoms *mdatoms);
 +
 +void PrintFreeEnergyInfoToFile(FILE *outfile, t_lambda *fep, t_expanded *expand, t_simtemp *simtemp, df_history_t *dfhist,
 +                               int nlam, int frequency, gmx_large_int_t step);
 +
 +void get_mc_state(gmx_rng_t rng, t_state *state);
 +
 +void set_mc_state(gmx_rng_t rng, t_state *state);
 +
 +/* check the version */
 +void check_ir_old_tpx_versions(t_commrec *cr, FILE *fplog,
 +                               t_inputrec *ir, gmx_mtop_t *mtop);
 +
 +/* Allocate and initialize node-local state entries. */
 +void set_state_entries(t_state *state, const t_inputrec *ir, int nnodes);
 +
 +/* Broadcast the data for a simulation, and allocate node-specific settings
 +   such as rng generators. */
 +void init_parallel(FILE *log, t_commrec *cr, t_inputrec *inputrec,
 +                   gmx_mtop_t *mtop);
 +
 +int mdrunner(gmx_hw_opt_t *hw_opt,
 +             FILE *fplog, t_commrec *cr, int nfile,
 +             const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
 +             gmx_bool bCompact, int nstglobalcomm, ivec ddxyz, int dd_node_order,
 +             real rdd, real rconstr, const char *dddlb_opt, real dlb_scale,
 +             const char *ddcsx, const char *ddcsy, const char *ddcsz,
 +             const char *nbpu_opt,
++             gmx_large_int_t nsteps_cmdline, int nstepout, int resetstep,
 +             int nmultisim, int repl_ex_nst, int repl_ex_nex,
 +             int repl_ex_seed, real pforce, real cpt_period, real max_hours,
 +             const char *deviceOptions, unsigned long Flags);
 +/* Driver routine, that calls the different methods */
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif  /* _mdrun_h */
index 4643148fdbe8f7c3ffd30dd4eb5ba25499ffd696,0000000000000000000000000000000000000000..a6dd4c67a1a3e18bd83c1d763c0194503817a7ed
mode 100644,000000..100644
--- /dev/null
@@@ -1,182 -1,0 +1,198 @@@
 +/*
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gromacs Runs On Most of All Computer Systems
 + */
 +
 +#ifndef _pme_h
 +#define _pme_h
 +
 +#include <stdio.h>
 +#include "typedefs.h"
 +#include "gmxcomplex.h"
 +#include "gmx_wallcycle.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +typedef real *splinevec[DIM];
 +
 +enum {
 +    GMX_SUM_QGRID_FORWARD, GMX_SUM_QGRID_BACKWARD
 +};
 +
 +int gmx_pme_init(gmx_pme_t *pmedata, t_commrec *cr,
 +                 int nnodes_major, int nnodes_minor,
 +                 t_inputrec *ir, int homenr,
 +                 gmx_bool bFreeEnergy, gmx_bool bReproducible, int nthread);
 +/* Initialize the pme data structures resepectively.
 + * Return value 0 indicates all well, non zero is an error code.
 + */
 +
 +int gmx_pme_reinit(gmx_pme_t *         pmedata,
 +                   t_commrec *         cr,
 +                   gmx_pme_t           pme_src,
 +                   const t_inputrec *  ir,
 +                   ivec                grid_size);
 +/* As gmx_pme_init, but takes most settings, except the grid, from pme_src */
 +
 +int gmx_pme_destroy(FILE *log, gmx_pme_t *pmedata);
 +/* Destroy the pme data structures resepectively.
 + * Return value 0 indicates all well, non zero is an error code.
 + */
 +
 +#define GMX_PME_SPREAD_Q      (1<<0)
 +#define GMX_PME_SOLVE         (1<<1)
 +#define GMX_PME_CALC_F        (1<<2)
 +#define GMX_PME_CALC_ENER_VIR (1<<3)
 +/* This forces the grid to be backtransformed even without GMX_PME_CALC_F */
 +#define GMX_PME_CALC_POT      (1<<4)
 +#define GMX_PME_DO_ALL_F  (GMX_PME_SPREAD_Q | GMX_PME_SOLVE | GMX_PME_CALC_F)
 +
 +int gmx_pme_do(gmx_pme_t pme,
 +               int start,       int homenr,
 +               rvec x[],        rvec f[],
 +               real chargeA[],  real chargeB[],
 +               matrix box,      t_commrec *cr,
 +               int  maxshift_x, int maxshift_y,
 +               t_nrnb *nrnb,    gmx_wallcycle_t wcycle,
 +               matrix lrvir,    real ewaldcoeff,
 +               real *energy,    real lambda,
 +               real *dvdlambda, int flags);
 +/* Do a PME calculation for the long range electrostatics.
 + * flags, defined above, determine which parts of the calculation are performed.
 + * Return value 0 indicates all well, non zero is an error code.
 + */
 +
 +int gmx_pmeonly(gmx_pme_t pme,
 +                t_commrec *cr,     t_nrnb *mynrnb,
 +                gmx_wallcycle_t wcycle,
 +                real ewaldcoeff,   gmx_bool bGatherOnly,
 +                t_inputrec *ir);
 +/* Called on the nodes that do PME exclusively (as slaves)
 + */
 +
 +void gmx_pme_calc_energy(gmx_pme_t pme, int n, rvec *x, real *q, real *V);
 +/* Calculate the PME grid energy V for n charges with a potential
 + * in the pme struct determined before with a call to gmx_pme_do
 + * with at least GMX_PME_SPREAD_Q and GMX_PME_SOLVE specified.
 + * Note that the charges are not spread on the grid in the pme struct.
 + * Currently does not work in parallel or with free energy.
 + */
 +
 +/* The following three routines are for PME/PP node splitting in pme_pp.c */
 +
 +/* Abstract type for PME <-> PP communication */
 +typedef struct gmx_pme_pp *gmx_pme_pp_t;
 +
++void gmx_pme_check_restrictions(int pme_order,
++                                int nkx, int nky, int nkz,
++                                int nnodes_major,
++                                int nnodes_minor,
++                                gmx_bool bUseThreads,
++                                gmx_bool bFatal,
++                                gmx_bool *bValidSettings);
++/* Check restrictions on pme_order and the PME grid nkx,nky,nkz.
++ * With bFatal=TRUE, a fatal error is generated on violation,
++ * bValidSettings=NULL can be passed.
++ * With bFatal=FALSE, *bValidSettings reports the validity of the settings.
++ * bUseThreads tells if any MPI rank doing PME uses more than 1 threads.
++ * If at calling you bUseThreads is unknown, pass TRUE for conservative
++ * checking.
++ */
++
 +gmx_pme_pp_t gmx_pme_pp_init(t_commrec *cr);
 +/* Initialize the PME-only side of the PME <-> PP communication */
 +
 +void gmx_pme_send_q(t_commrec *cr,
 +                    gmx_bool bFreeEnergy, real *chargeA, real *chargeB,
 +                    int maxshift_x, int maxshift_y);
 +/* Send the charges and maxshift to out PME-only node. */
 +
 +void gmx_pme_send_x(t_commrec *cr, matrix box, rvec *x,
 +                    gmx_bool bFreeEnergy, real lambda,
 +                    gmx_bool bEnerVir,
 +                    gmx_large_int_t step);
 +/* Send the coordinates to our PME-only node and request a PME calculation */
 +
 +void gmx_pme_send_finish(t_commrec *cr);
 +/* Tell our PME-only node to finish */
 +
 +void gmx_pme_send_switchgrid(t_commrec *cr, ivec grid_size, real ewaldcoeff);
 +/* Tell our PME-only node to switch to a new grid size */
 +
 +void gmx_pme_send_resetcounters(t_commrec *cr, gmx_large_int_t step);
 +/* Tell our PME-only node to reset all cycle and flop counters */
 +
 +void gmx_pme_receive_f(t_commrec *cr,
 +                       rvec f[], matrix vir,
 +                       real *energy, real *dvdlambda,
 +                       float *pme_cycles);
 +/* PP nodes receive the long range forces from the PME nodes */
 +
 +/* Return values for gmx_pme_recv_q_x */
 +enum {
 +    pmerecvqxX,            /* calculate PME mesh interactions for new x    */
 +    pmerecvqxFINISH,       /* the simulation should finish, we should quit */
 +    pmerecvqxSWITCHGRID,   /* change the PME grid size                     */
 +    pmerecvqxRESETCOUNTERS /* reset the cycle and flop counters            */
 +};
 +
 +int gmx_pme_recv_q_x(gmx_pme_pp_t pme_pp,
 +                     int *natoms,
 +                     real **chargeA, real **chargeB,
 +                     matrix box, rvec **x, rvec **f,
 +                     int *maxshift_x, int *maxshift_y,
 +                     gmx_bool *bFreeEnergy, real *lambda,
 +                     gmx_bool *bEnerVir,
 +                     gmx_large_int_t *step,
 +                     ivec grid_size, real *ewaldcoeff);
 +;
 +/* With return value:
 + * pmerecvqxX:             all parameters set, chargeA and chargeB can be NULL
 + * pmerecvqxFINISH:        no parameters set
 + * pmerecvqxSWITCHGRID:    only grid_size and *ewaldcoeff are set
 + * pmerecvqxRESETCOUNTERS: *step is set
 + */
 +
 +void gmx_pme_send_force_vir_ener(gmx_pme_pp_t pme_pp,
 +                                 rvec *f, matrix vir,
 +                                 real energy, real dvdlambda,
 +                                 float cycles);
 +/* Send the PME mesh force, virial and energy to the PP-only nodes */
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif
index e2ec16f6a58d86454ec44b2e9f797f1549268d82,0000000000000000000000000000000000000000..cd0713c952001e44c0466fa830e8add9409f1227
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,90 @@@
-     tensor         **ekin_work_alloc; /* Allocated locations of ekin_work   */
 +/*
 + *
 + *                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:
 + * GRoups of Organic Molecules in ACtion for Science
 + */
 +
 +
 +#include "simple.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +
 +typedef struct {
 +    real    Th;             /* Temperature at half step        */
 +    real    T;              /* Temperature at full step        */
 +    tensor  ekinh;          /* Kinetic energy at half step     */
 +    tensor  ekinh_old;      /* Kinetic energy at old half step */
 +    tensor  ekinf;          /* Kinetic energy at full step     */
 +    real    lambda;         /* Berendsen coupling lambda       */
 +    double  ekinscalef_nhc; /* Scaling factor for NHC- full step */
 +    double  ekinscaleh_nhc; /* Scaling factor for NHC- half step */
 +    double  vscale_nhc;     /* Scaling factor for NHC- velocity */
 +} t_grp_tcstat;
 +
 +typedef struct {
 +    int     nat;    /* Number of atoms in this group          */
 +    rvec    u;      /* Mean velocities of home particles        */
 +    rvec    uold;   /* Previous mean velocities of home particles   */
 +    double  mA;     /* Mass for topology A                            */
 +    double  mB;     /* Mass for topology B                            */
 +} t_grp_acc;
 +
 +typedef struct {
 +    real    cos_accel;  /* The acceleration for the cosine profile      */
 +    real    mvcos;      /* The cos momenta of home particles            */
 +    real    vcos;       /* The velocity of the cosine profile           */
 +} t_cos_acc;
 +
 +typedef struct {
 +    gmx_bool         bNEMD;
 +    int              ngtc;            /* The number of T-coupling groups      */
 +    t_grp_tcstat    *tcstat;          /* T-coupling data            */
++    tensor         **ekin_work_alloc; /* Allocated locations for *_work members */
 +    tensor         **ekin_work;       /* Work arrays for tcstat per thread    */
++    real           **dekindl_work;    /* Work location for dekindl per thread */
 +    int              ngacc;           /* The number of acceleration groups    */
 +    t_grp_acc       *grpstat;         /* Acceleration data                    */
 +    tensor           ekin;            /* overall kinetic energy               */
 +    tensor           ekinh;           /* overall 1/2 step kinetic energy      */
 +    real             dekindl;         /* dEkin/dlambda at half step           */
 +    real             dekindl_old;     /* dEkin/dlambda at old half step       */
 +    t_cos_acc        cosacc;          /* Cosine acceleration data             */
 +} gmx_ekindata_t;
 +
 +#define GID(igid, jgid, gnr) ((igid < jgid) ? (igid*gnr+jgid) : (jgid*gnr+igid))
 +
 +#ifdef __cplusplus
 +}
 +#endif
index b91ae8224812e481ae6382bb26c03dd35279fc8c,0000000000000000000000000000000000000000..22e9297686918691a18278e77c3f23c74be9ebd1
mode 100644,000000..100644
--- /dev/null
@@@ -1,137 -1,0 +1,139 @@@
- #ifdef GMX_X86_AVX_256
- /* Note that setting this to 128 will also work with AVX-256, but slower */
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +
 +#ifndef NB_VERLET_H
 +#define NB_VERLET_H
 +
 +#include "nbnxn_pairlist.h"
 +#include "nbnxn_cuda_types_ext.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +#ifdef GMX_X86_SSE2
 +/* Use SIMD accelerated nbnxn search and kernels */
 +#define GMX_NBNXN_SIMD
 +
++/* Uncomment the next line to use, slower, 128-bit SIMD with AVX-256 */
++/* #define GMX_NBNXN_HALF_WIDTH_SIMD */
++
++#if defined GMX_X86_AVX_256 && !defined GMX_NBNXN_HALF_WIDTH_SIMD
 +#define GMX_NBNXN_SIMD_BITWIDTH  256
 +#else
 +#define GMX_NBNXN_SIMD_BITWIDTH  128
 +#endif
 +
 +/* The nbnxn SIMD 4xN and 2x(N+N) kernels can be added independently.
 + * Currently the 2xNN SIMD kernels only make sense with:
 + *  8-way SIMD: 4x4 setup, works with AVX-256 in single precision
 + * 16-way SIMD: 4x8 setup, not used, but most of the kernel code is there
 + */
 +#define GMX_NBNXN_SIMD_4XN
 +#if GMX_NBNXN_SIMD_BITWIDTH == 256 && !defined GMX_DOUBLE
 +#define GMX_NBNXN_SIMD_2XNN
 +#endif
 +
 +#endif
 +
 +
 +/*! Nonbonded NxN kernel types: plain C, CPU SIMD, GPU CUDA, GPU emulation */
 +typedef enum
 +{
 +    nbnxnkNotSet = 0,
 +    nbnxnk4x4_PlainC,
 +    nbnxnk4xN_SIMD_4xN,
 +    nbnxnk4xN_SIMD_2xNN,
 +    nbnxnk8x8x8_CUDA,
 +    nbnxnk8x8x8_PlainC,
 +    nbnxnkNR
 +} nbnxn_kernel_type;
 +
 +/*! Return a string indentifying the kernel type */
 +const char *lookup_nbnxn_kernel_name(int kernel_type);
 +
 +enum {
 +    ewaldexclTable, ewaldexclAnalytical
 +};
 +
 +/* Atom locality indicator: local, non-local, all, used for calls to:
 +   gridding, pair-search, force calculation, x/f buffer operations */
 +enum {
 +    eatLocal = 0, eatNonlocal = 1, eatAll
 +};
 +
 +#define LOCAL_A(x)               ((x) == eatLocal)
 +#define NONLOCAL_A(x)            ((x) == eatNonlocal)
 +#define LOCAL_OR_NONLOCAL_A(x)   (LOCAL_A(x) || NONLOCAL_A(x))
 +
 +/* Interaction locality indicator (used in pair-list search/calculations):
 +    - local interactions require local atom data and affect local output only;
 +    - non-local interactions require both local and non-local atom data and
 +      affect both local- and non-local output. */
 +enum {
 +    eintLocal = 0, eintNonlocal = 1
 +};
 +
 +#define LOCAL_I(x)               ((x) == eintLocal)
 +#define NONLOCAL_I(x)            ((x) == eintNonlocal)
 +
 +enum {
 +    enbvClearFNo, enbvClearFYes
 +};
 +
 +typedef struct {
 +    nbnxn_pairlist_set_t  nbl_lists;   /* pair list(s)                       */
 +    nbnxn_atomdata_t     *nbat;        /* atom data                          */
 +    int                   kernel_type; /* non-bonded kernel - see enum above */
 +    int                   ewald_excl;  /* Ewald exclusion - see enum above   */
 +} nonbonded_verlet_group_t;
 +
 +/* non-bonded data structure with Verlet-type cut-off */
 +typedef struct {
 +    nbnxn_search_t           nbs;             /* n vs n atom pair searching data       */
 +    int                      ngrp;            /* number of interaction groups          */
 +    nonbonded_verlet_group_t grp[2];          /* local and non-local interaction group */
 +
 +    gmx_bool                 bUseGPU;         /* TRUE when GPU acceleration is used */
 +    nbnxn_cuda_ptr_t         cu_nbv;          /* pointer to CUDA nb verlet data     */
 +    int                      min_ci_balanced; /* pair list balancing parameter
 +                                                 used for the 8x8x8 CUDA kernels    */
 +} nonbonded_verlet_t;
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif /* NB_VERLET_H */
index 5639311cb882c9003f006ccfaeb676e3a3001e7f,0000000000000000000000000000000000000000..569cec073992632ec6d201794af1883ba14c372e
mode 100644,000000..100644
--- /dev/null
@@@ -1,251 -1,0 +1,252 @@@
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +
 +#ifndef _nbnxn_pairlist_h
 +#define _nbnxn_pairlist_h
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +/* A buffer data structure of 64 bytes
 + * to be placed at the beginning and end of structs
 + * to avoid cache invalidation of the real contents
 + * of the struct by writes to neighboring memory.
 + */
 +typedef struct {
 +    int dummy[16];
 +} gmx_cache_protect_t;
 +
 +/* Abstract type for pair searching data */
 +typedef struct nbnxn_search * nbnxn_search_t;
 +
 +/* Function that should return a pointer *ptr to memory
 + * of size nbytes.
 + * Error handling should be done within this function.
 + */
 +typedef void nbnxn_alloc_t (void **ptr, size_t nbytes);
 +
 +/* Function that should free the memory pointed to by *ptr.
 + * NULL should not be passed to this function.
 + */
 +typedef void nbnxn_free_t (void *ptr);
 +
 +typedef struct {
 +    int      cj;    /* The j-cluster                    */
 +    unsigned excl;  /* The exclusion (interaction) bits */
 +} nbnxn_cj_t;
 +
 +/* In nbnxn_ci_t the integer shift contains the shift in the lower 7 bits.
 + * The upper bits contain information for non-bonded kernel optimization.
 + * Simply calculating LJ and Coulomb for all pairs in a cluster pair is fine.
 + * But three flags can be used to skip interactions, currently only for subc=0
 + * !(shift & NBNXN_CI_DO_LJ(subc))   => we can skip LJ for all pairs
 + * shift & NBNXN_CI_HALF_LJ(subc)    => we can skip LJ for the second half of i
 + * !(shift & NBNXN_CI_DO_COUL(subc)) => we can skip Coulomb for all pairs
 + */
 +#define NBNXN_CI_SHIFT          127
 +#define NBNXN_CI_DO_LJ(subc)    (1<<(7+3*(subc)))
 +#define NBNXN_CI_HALF_LJ(subc)  (1<<(8+3*(subc)))
 +#define NBNXN_CI_DO_COUL(subc)  (1<<(9+3*(subc)))
 +
 +/* Simple pair-list i-unit */
 +typedef struct {
 +    int ci;             /* i-cluster             */
 +    int shift;          /* Shift vector index plus possible flags, see above */
 +    int cj_ind_start;   /* Start index into cj   */
 +    int cj_ind_end;     /* End index into cj     */
 +} nbnxn_ci_t;
 +
 +/* Grouped pair-list i-unit */
 +typedef struct {
 +    int sci;            /* i-super-cluster       */
 +    int shift;          /* Shift vector index plus possible flags */
 +    int cj4_ind_start;  /* Start index into cj4  */
 +    int cj4_ind_end;    /* End index into cj4    */
 +} nbnxn_sci_t;
 +
 +typedef struct {
 +    unsigned imask;        /* The i-cluster interactions mask for 1 warp  */
 +    int      excl_ind;     /* Index into the exclusion array for 1 warp   */
 +} nbnxn_im_ei_t;
 +
 +typedef struct {
 +    int           cj[4];   /* The 4 j-clusters                            */
 +    nbnxn_im_ei_t imei[2]; /* The i-cluster mask data       for 2 warps   */
 +} nbnxn_cj4_t;
 +
 +typedef struct {
 +    unsigned pair[32];     /* Exclusion bits for one warp,                *
 +                            * each unsigned has bit for 4*8 i clusters    */
 +} nbnxn_excl_t;
 +
 +typedef struct {
 +    gmx_cache_protect_t cp0;
 +
 +    nbnxn_alloc_t      *alloc;
 +    nbnxn_free_t       *free;
 +
 +    gmx_bool            bSimple;         /* Simple list has na_sc=na_s and uses cj   *
 +                                          * Complex list uses cj4                    */
 +
 +    int                     na_ci;       /* The number of atoms per i-cluster        */
 +    int                     na_cj;       /* The number of atoms per j-cluster        */
 +    int                     na_sc;       /* The number of atoms per super cluster    */
 +    real                    rlist;       /* The radius for constructing the list     */
 +    int                     nci;         /* The number of i-clusters in the list     */
 +    nbnxn_ci_t             *ci;          /* The i-cluster list, size nci             */
 +    int                     ci_nalloc;   /* The allocation size of ci                */
 +    int                     nsci;        /* The number of i-super-clusters in the list */
 +    nbnxn_sci_t            *sci;         /* The i-super-cluster list                 */
 +    int                     sci_nalloc;  /* The allocation size of sci               */
 +
 +    int                     ncj;         /* The number of j-clusters in the list     */
 +    nbnxn_cj_t             *cj;          /* The j-cluster list, size ncj             */
 +    int                     cj_nalloc;   /* The allocation size of cj                */
 +
 +    int                     ncj4;        /* The total number of 4*j clusters         */
 +    nbnxn_cj4_t            *cj4;         /* The 4*j cluster list, size ncj4          */
 +    int                     cj4_nalloc;  /* The allocation size of cj4               */
 +    int                     nexcl;       /* The count for excl                       */
 +    nbnxn_excl_t           *excl;        /* Atom interaction bits (non-exclusions)   */
 +    int                     excl_nalloc; /* The allocation size for excl             */
 +    int                     nci_tot;     /* The total number of i clusters           */
 +
 +    struct nbnxn_list_work *work;
 +
 +    gmx_cache_protect_t     cp1;
 +} nbnxn_pairlist_t;
 +
 +typedef struct {
 +    int                nnbl;        /* number of lists */
 +    nbnxn_pairlist_t **nbl;         /* lists */
 +    gmx_bool           bCombined;   /* TRUE if lists get combined into one (the 1st) */
 +    gmx_bool           bSimple;     /* TRUE if the list of of type "simple"
 +                                       (na_sc=na_s, no super-clusters used) */
 +    int                natpair_ljq; /* Total number of atom pairs for LJ+Q kernel */
 +    int                natpair_lj;  /* Total number of atom pairs for LJ kernel   */
 +    int                natpair_q;   /* Total number of atom pairs for Q kernel    */
 +} nbnxn_pairlist_set_t;
 +
 +enum {
 +    nbatXYZ, nbatXYZQ, nbatX4, nbatX8
 +};
 +
 +typedef struct {
 +    real *f;      /* f, size natoms*fstride                             */
 +    real *fshift; /* Shift force array, size SHIFTS*DIM                 */
 +    int   nV;     /* The size of *Vvdw and *Vc                          */
 +    real *Vvdw;   /* Temporary Van der Waals group energy storage       */
 +    real *Vc;     /* Temporary Coulomb group energy storage             */
 +    int   nVS;    /* The size of *VSvdw and *VSc                        */
 +    real *VSvdw;  /* Temporary SIMD Van der Waals group energy storage  */
 +    real *VSc;    /* Temporary SIMD Coulomb group energy storage        */
 +} nbnxn_atomdata_output_t;
 +
 +/* Block size in atoms for the non-bonded thread force-buffer reduction,
 + * should be a multiple of all cell and x86 SIMD sizes (i.e. 2, 4 and 8).
 + * Should be small to reduce the reduction and zeroing cost,
 + * but too small will result in overhead.
 + * Currently the block size is NBNXN_BUFFERFLAG_SIZE*3*sizeof(real)=192 bytes.
 + */
 +#ifdef GMX_DOUBLE
 +#define NBNXN_BUFFERFLAG_SIZE   8
 +#else
 +#define NBNXN_BUFFERFLAG_SIZE  16
 +#endif
 +
 +/* We currently store the reduction flags as bits in an unsigned int.
 + * In most cases this limits the number of flags to 32.
 + * The reduction will automatically disable the flagging and do a full
 + * reduction when the flags won't fit, but this will lead to very slow
 + * reduction. As we anyhow don't expect reasonable performance with
 + * more than 32 threads, we put in this hard limit.
 + * You can increase this number, but the reduction will be very slow.
 + */
 +#define NBNXN_BUFFERFLAG_MAX_THREADS  32
 +
 +/* Flags for telling if threads write to force output buffers */
 +typedef struct {
 +    int       nflag;       /* The number of flag blocks                         */
 +    unsigned *flag;        /* Bit i is set when thread i writes to a cell-block */
 +    int       flag_nalloc; /* Allocation size of cxy_flag                       */
 +} nbnxn_buffer_flags_t;
 +
 +/* LJ combination rules: geometric, Lorentz-Berthelot, none */
 +enum {
 +    ljcrGEOM, ljcrLB, ljcrNONE, ljcrNR
 +};
 +
 +typedef struct {
 +    nbnxn_alloc_t           *alloc;
 +    nbnxn_free_t            *free;
 +    int                      ntype;           /* The number of different atom types                 */
 +    real                    *nbfp;            /* Lennard-Jones 6*C6 and 12*C12 params, size ntype^2*2 */
 +    int                      comb_rule;       /* Combination rule, see enum above                   */
 +    real                    *nbfp_comb;       /* LJ parameter per atom type, size ntype*2           */
 +    real                    *nbfp_s4;         /* As nbfp, but with stride 4, size ntype^2*4. This
 +                                               * might suit 4-wide SIMD loads of two values (e.g.
 +                                               * two floats in single precision on x86).            */
 +    int                      natoms;          /* Number of atoms                                    */
 +    int                      natoms_local;    /* Number of local atoms                           */
 +    int                     *type;            /* Atom types                                         */
 +    real                    *lj_comb;         /* LJ parameters per atom for combining for pairs     */
 +    int                      XFormat;         /* The format of x (and q), enum                      */
 +    int                      FFormat;         /* The format of f, enum                              */
 +    real                    *q;               /* Charges, can be NULL if incorporated in x          */
 +    int                      na_c;            /* The number of atoms per cluster                    */
 +    int                      nenergrp;        /* The number of energy groups                        */
 +    int                      neg_2log;        /* Log2 of nenergrp                                   */
 +    int                     *energrp;         /* The energy groups per cluster, can be NULL         */
 +    gmx_bool                 bDynamicBox;     /* Do we need to update shift_vec every step?    */
 +    rvec                    *shift_vec;       /* Shift vectors, copied from t_forcerec              */
 +    int                      xstride;         /* stride for a coordinate in x (usually 3 or 4)      */
 +    int                      fstride;         /* stride for a coordinate in f (usually 3 or 4)      */
 +    real                    *x;               /* x and possibly q, size natoms*xstride              */
 +    real                    *simd_4xn_diag;   /* indices to set the SIMD 4xN diagonal masks    */
 +    real                    *simd_2xnn_diag;  /* indices to set the SIMD 2x(N+N)diagonal masks */
++    unsigned                *simd_excl_mask;  /* exclusion masks for SIMD topology exclusions  */
 +    int                      nout;            /* The number of force arrays                         */
 +    nbnxn_atomdata_output_t *out;             /* Output data structures               */
 +    int                      nalloc;          /* Allocation size of all arrays (for x/f *x/fstride) */
 +    gmx_bool                 bUseBufferFlags; /* Use the flags or operate on all atoms     */
 +    nbnxn_buffer_flags_t     buffer_flags;    /* Flags for buffer zeroing+reduc.  */
 +} nbnxn_atomdata_t;
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif
index 6214ed87732e0331e1067f6812f09cee6ad178cb,0000000000000000000000000000000000000000..bd20652e75035d06eba8cb159f74da92931588f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,1799 -1,0 +1,1799 @@@
-         if (opts->tau_t[i] > 0 && opts->nrdf[i] > 0 && Ek > 0)
 +/* -*- 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 <assert.h>
 +
 +#include "typedefs.h"
 +#include "smalloc.h"
 +#include "update.h"
 +#include "vec.h"
 +#include "macros.h"
 +#include "physics.h"
 +#include "names.h"
 +#include "gmx_fatal.h"
 +#include "txtdump.h"
 +#include "nrnb.h"
 +#include "gmx_random.h"
 +#include "update.h"
 +#include "mdrun.h"
 +
 +#define NTROTTERPARTS 3
 +
 +/* Suzuki-Yoshida Constants, for n=3 and n=5, for symplectic integration  */
 +/* for n=1, w0 = 1 */
 +/* for n=3, w0 = w2 = 1/(2-2^-(1/3)), w1 = 1-2*w0 */
 +/* for n=5, w0 = w1 = w3 = w4 = 1/(4-4^-(1/3)), w1 = 1-4*w0 */
 +
 +#define MAX_SUZUKI_YOSHIDA_NUM 5
 +#define SUZUKI_YOSHIDA_NUM  5
 +
 +static const double  sy_const_1[] = { 1. };
 +static const double  sy_const_3[] = { 0.828981543588751, -0.657963087177502, 0.828981543588751 };
 +static const double  sy_const_5[] = { 0.2967324292201065, 0.2967324292201065, -0.186929716880426, 0.2967324292201065, 0.2967324292201065 };
 +
 +static const double* sy_const[] = {
 +    NULL,
 +    sy_const_1,
 +    NULL,
 +    sy_const_3,
 +    NULL,
 +    sy_const_5
 +};
 +
 +/*
 +   static const double sy_const[MAX_SUZUKI_YOSHIDA_NUM+1][MAX_SUZUKI_YOSHIDA_NUM+1] = {
 +    {},
 +    {1},
 +    {},
 +    {0.828981543588751,-0.657963087177502,0.828981543588751},
 +    {},
 +    {0.2967324292201065,0.2967324292201065,-0.186929716880426,0.2967324292201065,0.2967324292201065}
 +   };*/
 +
 +/* these integration routines are only referenced inside this file */
 +static void NHC_trotter(t_grpopts *opts, int nvar, gmx_ekindata_t *ekind, real dtfull,
 +                        double xi[], double vxi[], double scalefac[], real *veta, t_extmass *MassQ, gmx_bool bEkinAveVel)
 +
 +{
 +    /* general routine for both barostat and thermostat nose hoover chains */
 +
 +    int           i, j, mi, mj, jmax;
 +    double        Ekin, Efac, reft, kT, nd;
 +    double        dt;
 +    t_grp_tcstat *tcstat;
 +    double       *ivxi, *ixi;
 +    double       *iQinv;
 +    double       *GQ;
 +    gmx_bool      bBarostat;
 +    int           mstepsi, mstepsj;
 +    int           ns = SUZUKI_YOSHIDA_NUM; /* set the degree of integration in the types/state.h file */
 +    int           nh = opts->nhchainlength;
 +
 +    snew(GQ, nh);
 +    mstepsi = mstepsj = ns;
 +
 +/* if scalefac is NULL, we are doing the NHC of the barostat */
 +
 +    bBarostat = FALSE;
 +    if (scalefac == NULL)
 +    {
 +        bBarostat = TRUE;
 +    }
 +
 +    for (i = 0; i < nvar; i++)
 +    {
 +
 +        /* make it easier to iterate by selecting
 +           out the sub-array that corresponds to this T group */
 +
 +        ivxi = &vxi[i*nh];
 +        ixi  = &xi[i*nh];
 +        if (bBarostat)
 +        {
 +            iQinv = &(MassQ->QPinv[i*nh]);
 +            nd    = 1.0; /* THIS WILL CHANGE IF NOT ISOTROPIC */
 +            reft  = max(0.0, opts->ref_t[0]);
 +            Ekin  = sqr(*veta)/MassQ->Winv;
 +        }
 +        else
 +        {
 +            iQinv  = &(MassQ->Qinv[i*nh]);
 +            tcstat = &ekind->tcstat[i];
 +            nd     = opts->nrdf[i];
 +            reft   = max(0.0, opts->ref_t[i]);
 +            if (bEkinAveVel)
 +            {
 +                Ekin = 2*trace(tcstat->ekinf)*tcstat->ekinscalef_nhc;
 +            }
 +            else
 +            {
 +                Ekin = 2*trace(tcstat->ekinh)*tcstat->ekinscaleh_nhc;
 +            }
 +        }
 +        kT = BOLTZ*reft;
 +
 +        for (mi = 0; mi < mstepsi; mi++)
 +        {
 +            for (mj = 0; mj < mstepsj; mj++)
 +            {
 +                /* weighting for this step using Suzuki-Yoshida integration - fixed at 5 */
 +                dt = sy_const[ns][mj] * dtfull / mstepsi;
 +
 +                /* compute the thermal forces */
 +                GQ[0] = iQinv[0]*(Ekin - nd*kT);
 +
 +                for (j = 0; j < nh-1; j++)
 +                {
 +                    if (iQinv[j+1] > 0)
 +                    {
 +                        /* we actually don't need to update here if we save the
 +                           state of the GQ, but it's easier to just recompute*/
 +                        GQ[j+1] = iQinv[j+1]*((sqr(ivxi[j])/iQinv[j])-kT);
 +                    }
 +                    else
 +                    {
 +                        GQ[j+1] = 0;
 +                    }
 +                }
 +
 +                ivxi[nh-1] += 0.25*dt*GQ[nh-1];
 +                for (j = nh-1; j > 0; j--)
 +                {
 +                    Efac      = exp(-0.125*dt*ivxi[j]);
 +                    ivxi[j-1] = Efac*(ivxi[j-1]*Efac + 0.25*dt*GQ[j-1]);
 +                }
 +
 +                Efac = exp(-0.5*dt*ivxi[0]);
 +                if (bBarostat)
 +                {
 +                    *veta *= Efac;
 +                }
 +                else
 +                {
 +                    scalefac[i] *= Efac;
 +                }
 +                Ekin *= (Efac*Efac);
 +
 +                /* Issue - if the KE is an average of the last and the current temperatures, then we might not be
 +                   able to scale the kinetic energy directly with this factor.  Might take more bookkeeping -- have to
 +                   think about this a bit more . . . */
 +
 +                GQ[0] = iQinv[0]*(Ekin - nd*kT);
 +
 +                /* update thermostat positions */
 +                for (j = 0; j < nh; j++)
 +                {
 +                    ixi[j] += 0.5*dt*ivxi[j];
 +                }
 +
 +                for (j = 0; j < nh-1; j++)
 +                {
 +                    Efac    = exp(-0.125*dt*ivxi[j+1]);
 +                    ivxi[j] = Efac*(ivxi[j]*Efac + 0.25*dt*GQ[j]);
 +                    if (iQinv[j+1] > 0)
 +                    {
 +                        GQ[j+1] = iQinv[j+1]*((sqr(ivxi[j])/iQinv[j])-kT);
 +                    }
 +                    else
 +                    {
 +                        GQ[j+1] = 0;
 +                    }
 +                }
 +                ivxi[nh-1] += 0.25*dt*GQ[nh-1];
 +            }
 +        }
 +    }
 +    sfree(GQ);
 +}
 +
 +static void boxv_trotter(t_inputrec *ir, real *veta, real dt, tensor box,
 +                         gmx_ekindata_t *ekind, tensor vir, real pcorr, t_extmass *MassQ)
 +{
 +
 +    real   pscal;
 +    double alpha;
 +    int    i, j, d, n, nwall;
 +    real   T, GW, vol;
 +    tensor Winvm, ekinmod, localpres;
 +
 +    /* The heat bath is coupled to a separate barostat, the last temperature group.  In the
 +       2006 Tuckerman et al paper., the order is iL_{T_baro} iL {T_part}
 +     */
 +
 +    if (ir->epct == epctSEMIISOTROPIC)
 +    {
 +        nwall = 2;
 +    }
 +    else
 +    {
 +        nwall = 3;
 +    }
 +
 +    /* eta is in pure units.  veta is in units of ps^-1. GW is in
 +       units of ps^-2.  However, eta has a reference of 1 nm^3, so care must be
 +       taken to use only RATIOS of eta in updating the volume. */
 +
 +    /* we take the partial pressure tensors, modify the
 +       kinetic energy tensor, and recovert to pressure */
 +
 +    if (ir->opts.nrdf[0] == 0)
 +    {
 +        gmx_fatal(FARGS, "Barostat is coupled to a T-group with no degrees of freedom\n");
 +    }
 +    /* alpha factor for phase space volume, then multiply by the ekin scaling factor.  */
 +    alpha  = 1.0 + DIM/((double)ir->opts.nrdf[0]);
 +    alpha *= ekind->tcstat[0].ekinscalef_nhc;
 +    msmul(ekind->ekin, alpha, ekinmod);
 +    /* for now, we use Elr = 0, because if you want to get it right, you
 +       really should be using PME. Maybe print a warning? */
 +
 +    pscal   = calc_pres(ir->ePBC, nwall, box, ekinmod, vir, localpres)+pcorr;
 +
 +    vol = det(box);
 +    GW  = (vol*(MassQ->Winv/PRESFAC))*(DIM*pscal - trace(ir->ref_p));  /* W is in ps^2 * bar * nm^3 */
 +
 +    *veta += 0.5*dt*GW;
 +}
 +
 +/*
 + * This file implements temperature and pressure coupling algorithms:
 + * For now only the Weak coupling and the modified weak coupling.
 + *
 + * Furthermore computation of pressure and temperature is done here
 + *
 + */
 +
 +real calc_pres(int ePBC, int nwall, matrix box, tensor ekin, tensor vir,
 +               tensor pres)
 +{
 +    int  n, m;
 +    real fac;
 +
 +    if (ePBC == epbcNONE || (ePBC == epbcXY && nwall != 2))
 +    {
 +        clear_mat(pres);
 +    }
 +    else
 +    {
 +        /* Uitzoeken welke ekin hier van toepassing is, zie Evans & Morris - E.
 +         * Wrs. moet de druktensor gecorrigeerd worden voor de netto stroom in
 +         * het systeem...
 +         */
 +
 +        fac = PRESFAC*2.0/det(box);
 +        for (n = 0; (n < DIM); n++)
 +        {
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                pres[n][m] = (ekin[n][m] - vir[n][m])*fac;
 +            }
 +        }
 +
 +        if (debug)
 +        {
 +            pr_rvecs(debug, 0, "PC: pres", pres, DIM);
 +            pr_rvecs(debug, 0, "PC: ekin", ekin, DIM);
 +            pr_rvecs(debug, 0, "PC: vir ", vir, DIM);
 +            pr_rvecs(debug, 0, "PC: box ", box, DIM);
 +        }
 +    }
 +    return trace(pres)/DIM;
 +}
 +
 +real calc_temp(real ekin, real nrdf)
 +{
 +    if (nrdf > 0)
 +    {
 +        return (2.0*ekin)/(nrdf*BOLTZ);
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +void parrinellorahman_pcoupl(FILE *fplog, gmx_large_int_t step,
 +                             t_inputrec *ir, real dt, tensor pres,
 +                             tensor box, tensor box_rel, tensor boxv,
 +                             tensor M, matrix mu, gmx_bool bFirstStep)
 +{
 +    /* This doesn't do any coordinate updating. It just
 +     * integrates the box vector equations from the calculated
 +     * acceleration due to pressure difference. We also compute
 +     * the tensor M which is used in update to couple the particle
 +     * coordinates to the box vectors.
 +     *
 +     * In Nose and Klein (Mol.Phys 50 (1983) no 5., p 1055) this is
 +     * given as
 +     *            -1    .           .     -1
 +     * M_nk = (h')   * (h' * h + h' h) * h
 +     *
 +     * with the dots denoting time derivatives and h is the transformation from
 +     * the scaled frame to the real frame, i.e. the TRANSPOSE of the box.
 +     * This also goes for the pressure and M tensors - they are transposed relative
 +     * to ours. Our equation thus becomes:
 +     *
 +     *                  -1       .    .           -1
 +     * M_gmx = M_nk' = b  * (b * b' + b * b') * b'
 +     *
 +     * where b is the gromacs box matrix.
 +     * Our box accelerations are given by
 +     *   ..                                    ..
 +     *   b = vol/W inv(box') * (P-ref_P)     (=h')
 +     */
 +
 +    int    d, n;
 +    tensor winv;
 +    real   vol = box[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
 +    real   atot, arel, change, maxchange, xy_pressure;
 +    tensor invbox, pdiff, t1, t2;
 +
 +    real   maxl;
 +
 +    m_inv_ur0(box, invbox);
 +
 +    if (!bFirstStep)
 +    {
 +        /* Note that PRESFAC does not occur here.
 +         * The pressure and compressibility always occur as a product,
 +         * therefore the pressure unit drops out.
 +         */
 +        maxl = max(box[XX][XX], box[YY][YY]);
 +        maxl = max(maxl, box[ZZ][ZZ]);
 +        for (d = 0; d < DIM; d++)
 +        {
 +            for (n = 0; n < DIM; n++)
 +            {
 +                winv[d][n] =
 +                    (4*M_PI*M_PI*ir->compress[d][n])/(3*ir->tau_p*ir->tau_p*maxl);
 +            }
 +        }
 +
 +        m_sub(pres, ir->ref_p, pdiff);
 +
 +        if (ir->epct == epctSURFACETENSION)
 +        {
 +            /* Unlike Berendsen coupling it might not be trivial to include a z
 +             * pressure correction here? On the other hand we don't scale the
 +             * box momentarily, but change accelerations, so it might not be crucial.
 +             */
 +            xy_pressure = 0.5*(pres[XX][XX]+pres[YY][YY]);
 +            for (d = 0; d < ZZ; d++)
 +            {
 +                pdiff[d][d] = (xy_pressure-(pres[ZZ][ZZ]-ir->ref_p[d][d]/box[d][d]));
 +            }
 +        }
 +
 +        tmmul(invbox, pdiff, t1);
 +        /* Move the off-diagonal elements of the 'force' to one side to ensure
 +         * that we obey the box constraints.
 +         */
 +        for (d = 0; d < DIM; d++)
 +        {
 +            for (n = 0; n < d; n++)
 +            {
 +                t1[d][n] += t1[n][d];
 +                t1[n][d]  = 0;
 +            }
 +        }
 +
 +        switch (ir->epct)
 +        {
 +            case epctANISOTROPIC:
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    for (n = 0; n <= d; n++)
 +                    {
 +                        t1[d][n] *= winv[d][n]*vol;
 +                    }
 +                }
 +                break;
 +            case epctISOTROPIC:
 +                /* calculate total volume acceleration */
 +                atot = box[XX][XX]*box[YY][YY]*t1[ZZ][ZZ]+
 +                    box[XX][XX]*t1[YY][YY]*box[ZZ][ZZ]+
 +                    t1[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
 +                arel = atot/(3*vol);
 +                /* set all RELATIVE box accelerations equal, and maintain total V
 +                 * change speed */
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    for (n = 0; n <= d; n++)
 +                    {
 +                        t1[d][n] = winv[0][0]*vol*arel*box[d][n];
 +                    }
 +                }
 +                break;
 +            case epctSEMIISOTROPIC:
 +            case epctSURFACETENSION:
 +                /* Note the correction to pdiff above for surftens. coupling  */
 +
 +                /* calculate total XY volume acceleration */
 +                atot = box[XX][XX]*t1[YY][YY]+t1[XX][XX]*box[YY][YY];
 +                arel = atot/(2*box[XX][XX]*box[YY][YY]);
 +                /* set RELATIVE XY box accelerations equal, and maintain total V
 +                 * change speed. Dont change the third box vector accelerations */
 +                for (d = 0; d < ZZ; d++)
 +                {
 +                    for (n = 0; n <= d; n++)
 +                    {
 +                        t1[d][n] = winv[d][n]*vol*arel*box[d][n];
 +                    }
 +                }
 +                for (n = 0; n < DIM; n++)
 +                {
 +                    t1[ZZ][n] *= winv[d][n]*vol;
 +                }
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Parrinello-Rahman pressure coupling type %s "
 +                          "not supported yet\n", EPCOUPLTYPETYPE(ir->epct));
 +                break;
 +        }
 +
 +        maxchange = 0;
 +        for (d = 0; d < DIM; d++)
 +        {
 +            for (n = 0; n <= d; n++)
 +            {
 +                boxv[d][n] += dt*t1[d][n];
 +
 +                /* We do NOT update the box vectors themselves here, since
 +                 * we need them for shifting later. It is instead done last
 +                 * in the update() routine.
 +                 */
 +
 +                /* Calculate the change relative to diagonal elements-
 +                   since it's perfectly ok for the off-diagonal ones to
 +                   be zero it doesn't make sense to check the change relative
 +                   to its current size.
 +                 */
 +
 +                change = fabs(dt*boxv[d][n]/box[d][d]);
 +
 +                if (change > maxchange)
 +                {
 +                    maxchange = change;
 +                }
 +            }
 +        }
 +
 +        if (maxchange > 0.01 && fplog)
 +        {
 +            char buf[22];
 +            fprintf(fplog,
 +                    "\nStep %s  Warning: Pressure scaling more than 1%%. "
 +                    "This may mean your system\n is not yet equilibrated. "
 +                    "Use of Parrinello-Rahman pressure coupling during\n"
 +                    "equilibration can lead to simulation instability, "
 +                    "and is discouraged.\n",
 +                    gmx_step_str(step, buf));
 +        }
 +    }
 +
 +    preserve_box_shape(ir, box_rel, boxv);
 +
 +    mtmul(boxv, box, t1);   /* t1=boxv * b' */
 +    mmul(invbox, t1, t2);
 +    mtmul(t2, invbox, M);
 +
 +    /* Determine the scaling matrix mu for the coordinates */
 +    for (d = 0; d < DIM; d++)
 +    {
 +        for (n = 0; n <= d; n++)
 +        {
 +            t1[d][n] = box[d][n] + dt*boxv[d][n];
 +        }
 +    }
 +    preserve_box_shape(ir, box_rel, t1);
 +    /* t1 is the box at t+dt, determine mu as the relative change */
 +    mmul_ur0(invbox, t1, mu);
 +}
 +
 +void berendsen_pcoupl(FILE *fplog, gmx_large_int_t step,
 +                      t_inputrec *ir, real dt, tensor pres, matrix box,
 +                      matrix mu)
 +{
 +    int     d, n;
 +    real    scalar_pressure, xy_pressure, p_corr_z;
 +    char   *ptr, buf[STRLEN];
 +
 +    /*
 +     *  Calculate the scaling matrix mu
 +     */
 +    scalar_pressure = 0;
 +    xy_pressure     = 0;
 +    for (d = 0; d < DIM; d++)
 +    {
 +        scalar_pressure += pres[d][d]/DIM;
 +        if (d != ZZ)
 +        {
 +            xy_pressure += pres[d][d]/(DIM-1);
 +        }
 +    }
 +    /* Pressure is now in bar, everywhere. */
 +#define factor(d, m) (ir->compress[d][m]*dt/ir->tau_p)
 +
 +    /* mu has been changed from pow(1+...,1/3) to 1+.../3, since this is
 +     * necessary for triclinic scaling
 +     */
 +    clear_mat(mu);
 +    switch (ir->epct)
 +    {
 +        case epctISOTROPIC:
 +            for (d = 0; d < DIM; d++)
 +            {
 +                mu[d][d] = 1.0 - factor(d, d)*(ir->ref_p[d][d] - scalar_pressure) /DIM;
 +            }
 +            break;
 +        case epctSEMIISOTROPIC:
 +            for (d = 0; d < ZZ; d++)
 +            {
 +                mu[d][d] = 1.0 - factor(d, d)*(ir->ref_p[d][d]-xy_pressure)/DIM;
 +            }
 +            mu[ZZ][ZZ] =
 +                1.0 - factor(ZZ, ZZ)*(ir->ref_p[ZZ][ZZ] - pres[ZZ][ZZ])/DIM;
 +            break;
 +        case epctANISOTROPIC:
 +            for (d = 0; d < DIM; d++)
 +            {
 +                for (n = 0; n < DIM; n++)
 +                {
 +                    mu[d][n] = (d == n ? 1.0 : 0.0)
 +                        -factor(d, n)*(ir->ref_p[d][n] - pres[d][n])/DIM;
 +                }
 +            }
 +            break;
 +        case epctSURFACETENSION:
 +            /* ir->ref_p[0/1] is the reference surface-tension times *
 +             * the number of surfaces                                */
 +            if (ir->compress[ZZ][ZZ])
 +            {
 +                p_corr_z = dt/ir->tau_p*(ir->ref_p[ZZ][ZZ] - pres[ZZ][ZZ]);
 +            }
 +            else
 +            {
 +                /* when the compressibity is zero, set the pressure correction   *
 +                 * in the z-direction to zero to get the correct surface tension */
 +                p_corr_z = 0;
 +            }
 +            mu[ZZ][ZZ] = 1.0 - ir->compress[ZZ][ZZ]*p_corr_z;
 +            for (d = 0; d < DIM-1; d++)
 +            {
 +                mu[d][d] = 1.0 + factor(d, d)*(ir->ref_p[d][d]/(mu[ZZ][ZZ]*box[ZZ][ZZ])
 +                                               - (pres[ZZ][ZZ]+p_corr_z - xy_pressure))/(DIM-1);
 +            }
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "Berendsen pressure coupling type %s not supported yet\n",
 +                      EPCOUPLTYPETYPE(ir->epct));
 +            break;
 +    }
 +    /* To fullfill the orientation restrictions on triclinic boxes
 +     * we will set mu_yx, mu_zx and mu_zy to 0 and correct
 +     * the other elements of mu to first order.
 +     */
 +    mu[YY][XX] += mu[XX][YY];
 +    mu[ZZ][XX] += mu[XX][ZZ];
 +    mu[ZZ][YY] += mu[YY][ZZ];
 +    mu[XX][YY]  = 0;
 +    mu[XX][ZZ]  = 0;
 +    mu[YY][ZZ]  = 0;
 +
 +    if (debug)
 +    {
 +        pr_rvecs(debug, 0, "PC: pres ", pres, 3);
 +        pr_rvecs(debug, 0, "PC: mu   ", mu, 3);
 +    }
 +
 +    if (mu[XX][XX] < 0.99 || mu[XX][XX] > 1.01 ||
 +        mu[YY][YY] < 0.99 || mu[YY][YY] > 1.01 ||
 +        mu[ZZ][ZZ] < 0.99 || mu[ZZ][ZZ] > 1.01)
 +    {
 +        char buf2[22];
 +        sprintf(buf, "\nStep %s  Warning: pressure scaling more than 1%%, "
 +                "mu: %g %g %g\n",
 +                gmx_step_str(step, buf2), mu[XX][XX], mu[YY][YY], mu[ZZ][ZZ]);
 +        if (fplog)
 +        {
 +            fprintf(fplog, "%s", buf);
 +        }
 +        fprintf(stderr, "%s", buf);
 +    }
 +}
 +
 +void berendsen_pscale(t_inputrec *ir, matrix mu,
 +                      matrix box, matrix box_rel,
 +                      int start, int nr_atoms,
 +                      rvec x[], unsigned short cFREEZE[],
 +                      t_nrnb *nrnb)
 +{
 +    ivec   *nFreeze = ir->opts.nFreeze;
 +    int     n, d, g = 0;
 +
 +    /* Scale the positions */
 +    for (n = start; n < start+nr_atoms; n++)
 +    {
 +        if (cFREEZE)
 +        {
 +            g = cFREEZE[n];
 +        }
 +
 +        if (!nFreeze[g][XX])
 +        {
 +            x[n][XX] = mu[XX][XX]*x[n][XX]+mu[YY][XX]*x[n][YY]+mu[ZZ][XX]*x[n][ZZ];
 +        }
 +        if (!nFreeze[g][YY])
 +        {
 +            x[n][YY] = mu[YY][YY]*x[n][YY]+mu[ZZ][YY]*x[n][ZZ];
 +        }
 +        if (!nFreeze[g][ZZ])
 +        {
 +            x[n][ZZ] = mu[ZZ][ZZ]*x[n][ZZ];
 +        }
 +    }
 +    /* compute final boxlengths */
 +    for (d = 0; d < DIM; d++)
 +    {
 +        box[d][XX] = mu[XX][XX]*box[d][XX]+mu[YY][XX]*box[d][YY]+mu[ZZ][XX]*box[d][ZZ];
 +        box[d][YY] = mu[YY][YY]*box[d][YY]+mu[ZZ][YY]*box[d][ZZ];
 +        box[d][ZZ] = mu[ZZ][ZZ]*box[d][ZZ];
 +    }
 +
 +    preserve_box_shape(ir, box_rel, box);
 +
 +    /* (un)shifting should NOT be done after this,
 +     * since the box vectors might have changed
 +     */
 +    inc_nrnb(nrnb, eNR_PCOUPL, nr_atoms);
 +}
 +
 +void berendsen_tcoupl(t_inputrec *ir, gmx_ekindata_t *ekind, real dt)
 +{
 +    t_grpopts *opts;
 +    int        i;
 +    real       T, reft = 0, lll;
 +
 +    opts = &ir->opts;
 +
 +    for (i = 0; (i < opts->ngtc); i++)
 +    {
 +        if (ir->eI == eiVV)
 +        {
 +            T = ekind->tcstat[i].T;
 +        }
 +        else
 +        {
 +            T = ekind->tcstat[i].Th;
 +        }
 +
 +        if ((opts->tau_t[i] > 0) && (T > 0.0))
 +        {
 +            reft                    = max(0.0, opts->ref_t[i]);
 +            lll                     = sqrt(1.0 + (dt/opts->tau_t[i])*(reft/T-1.0));
 +            ekind->tcstat[i].lambda = max(min(lll, 1.25), 0.8);
 +        }
 +        else
 +        {
 +            ekind->tcstat[i].lambda = 1.0;
 +        }
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "TC: group %d: T: %g, Lambda: %g\n",
 +                    i, T, ekind->tcstat[i].lambda);
 +        }
 +    }
 +}
 +
 +static int poisson_variate(real lambda, gmx_rng_t rng)
 +{
 +
 +    real L;
 +    int  k = 0;
 +    real p = 1.0;
 +
 +    L = exp(-lambda);
 +
 +    do
 +    {
 +        k  = k+1;
 +        p *= gmx_rng_uniform_real(rng);
 +    }
 +    while (p > L);
 +
 +    return k-1;
 +}
 +
 +void andersen_tcoupl(t_inputrec *ir, t_mdatoms *md, t_state *state, gmx_rng_t rng, real rate, t_idef *idef, int nblocks, int *sblock, gmx_bool *randatom, int *randatom_list, gmx_bool *randomize, real *boltzfac)
 +{
 +    t_grpopts *opts;
 +    int        i, j, k, d, len, n, ngtc, gc = 0;
 +    int        nshake, nsettle, nrandom, nrand_group;
 +    real       boltz, scal, reft, prand;
 +    t_iatom   *iatoms;
 +
 +    /* convenience variables */
 +    opts = &ir->opts;
 +    ngtc = opts->ngtc;
 +
 +    /* idef is only passed in if it's chance-per-particle andersen, so
 +       it essentially serves as a boolean to determine which type of
 +       andersen is being used */
 +    if (idef)
 +    {
 +
 +        /* randomly atoms to randomize.  However, all constraint
 +           groups have to have either all of the atoms or none of the
 +           atoms randomize.
 +
 +           Algorithm:
 +           1. Select whether or not to randomize each atom to get the correct probability.
 +           2. Cycle through the constraint groups.
 +              2a. for each constraint group, determine the fraction f of that constraint group that are
 +                  chosen to be randomized.
 +              2b. all atoms in the constraint group are randomized with probability f.
 +         */
 +
 +        nrandom = 0;
 +        if ((rate < 0.05) && (md->homenr > 50))
 +        {
 +            /* if the rate is relatively high, use a standard method, if low rate,
 +             * use poisson */
 +            /* poisson distributions approxmation, more efficient for
 +             * low rates, fewer random numbers required */
 +            nrandom = poisson_variate(md->homenr*rate, rng);  /* how many do we randomize? Use Poisson. */
 +            /* now we know how many, choose them randomly. No worries about repeats, at this rate, it's negligible.
 +               worst thing that happens, it lowers the true rate an negligible amount */
 +            for (i = 0; i < nrandom; i++)
 +            {
 +                randatom[(int)(gmx_rng_uniform_real(rng)*md->homenr)] = TRUE;
 +            }
 +        }
 +        else
 +        {
 +            for (i = 0; i < md->homenr; i++)
 +            {
 +                if (gmx_rng_uniform_real(rng) < rate)
 +                {
 +                    randatom[i] = TRUE;
 +                    nrandom++;
 +                }
 +            }
 +        }
 +
 +        /* instead of looping over the constraint groups, if we had a
 +           list of which atoms were in which constraint groups, we
 +           could then loop over only the groups that are randomized
 +           now.  But that is not available now.  Create later after
 +           determining whether there actually is any slowing. */
 +
 +        /* first, loop through the settles to make sure all groups either entirely randomized, or not randomized. */
 +
 +        nsettle  = idef->il[F_SETTLE].nr/2;
 +        for (i = 0; i < nsettle; i++)
 +        {
 +            iatoms      = idef->il[F_SETTLE].iatoms;
 +            nrand_group = 0;
 +            for (k = 0; k < 3; k++)  /* settles are always 3 atoms, hardcoded */
 +            {
 +                if (randatom[iatoms[2*i+1]+k])
 +                {
 +                    nrand_group++;     /* count the number of atoms to be shaken in the settles group */
 +                    randatom[iatoms[2*i+1]+k] = FALSE;
 +                    nrandom--;
 +                }
 +            }
 +            if (nrand_group > 0)
 +            {
 +                prand = (nrand_group)/3.0;  /* use this fraction to compute the probability the
 +                                               whole group is randomized */
 +                if (gmx_rng_uniform_real(rng) < prand)
 +                {
 +                    for (k = 0; k < 3; k++)
 +                    {
 +                        randatom[iatoms[2*i+1]+k] = TRUE;   /* mark them all to be randomized */
 +                    }
 +                    nrandom += 3;
 +                }
 +            }
 +        }
 +
 +        /* now loop through the shake groups */
 +        nshake = nblocks;
 +        for (i = 0; i < nshake; i++)
 +        {
 +            iatoms      = &(idef->il[F_CONSTR].iatoms[sblock[i]]);
 +            len         = sblock[i+1]-sblock[i];
 +            nrand_group = 0;
 +            for (k = 0; k < len; k++)
 +            {
 +                if (k%3 != 0)
 +                {   /* only 2/3 of the sblock items are atoms, the others are labels */
 +                    if (randatom[iatoms[k]])
 +                    {
 +                        nrand_group++;
 +                        randatom[iatoms[k]] = FALSE;  /* need to mark it false here in case the atom is in more than
 +                                                         one group in the shake block */
 +                        nrandom--;
 +                    }
 +                }
 +            }
 +            if (nrand_group > 0)
 +            {
 +                prand = (nrand_group)/(1.0*(2*len/3));
 +                if (gmx_rng_uniform_real(rng) < prand)
 +                {
 +                    for (k = 0; k < len; k++)
 +                    {
 +                        if (k%3 != 0)
 +                        {                               /* only 2/3 of the sblock items are atoms, the others are labels */
 +                            randatom[iatoms[k]] = TRUE; /* randomize all of them */
 +                            nrandom++;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        if (nrandom > 0)
 +        {
 +            n = 0;
 +            for (i = 0; i < md->homenr; i++)  /* now loop over the list of atoms */
 +            {
 +                if (randatom[i])
 +                {
 +                    randatom_list[n] = i;
 +                    n++;
 +                }
 +            }
 +            nrandom = n;  /* there are some values of nrandom for which
 +                             this algorithm won't work; for example all
 +                             water molecules and nrandom =/= 3.  Better to
 +                             recount and use this number (which we
 +                             calculate anyway: it will not affect
 +                             the average number of atoms accepted.
 +                           */
 +        }
 +    }
 +    else
 +    {
 +        /* if it's andersen-massive, then randomize all the atoms */
 +        nrandom = md->homenr;
 +        for (i = 0; i < nrandom; i++)
 +        {
 +            randatom_list[i] = i;
 +        }
 +    }
 +
 +    /* randomize the velocities of the selected particles */
 +
 +    for (i = 0; i < nrandom; i++)  /* now loop over the list of atoms */
 +    {
 +        n = randatom_list[i];
 +        if (md->cTC)
 +        {
 +            gc   = md->cTC[n];  /* assign the atom to a temperature group if there are more than one */
 +        }
 +        if (randomize[gc])
 +        {
 +            scal = sqrt(boltzfac[gc]*md->invmass[n]);
 +            for (d = 0; d < DIM; d++)
 +            {
 +                state->v[n][d] = scal*gmx_rng_gaussian_table(rng);
 +            }
 +        }
 +        randatom[n] = FALSE; /* unmark this atom for randomization */
 +    }
 +}
 +
 +
 +void nosehoover_tcoupl(t_grpopts *opts, gmx_ekindata_t *ekind, real dt,
 +                       double xi[], double vxi[], t_extmass *MassQ)
 +{
 +    int   i;
 +    real  reft, oldvxi;
 +
 +    /* note that this routine does not include Nose-hoover chains yet. Should be easy to add. */
 +
 +    for (i = 0; (i < opts->ngtc); i++)
 +    {
 +        reft     = max(0.0, opts->ref_t[i]);
 +        oldvxi   = vxi[i];
 +        vxi[i]  += dt*MassQ->Qinv[i]*(ekind->tcstat[i].Th - reft);
 +        xi[i]   += dt*(oldvxi + vxi[i])*0.5;
 +    }
 +}
 +
 +t_state *init_bufstate(const t_state *template_state)
 +{
 +    t_state *state;
 +    int      nc = template_state->nhchainlength;
 +    snew(state, 1);
 +    snew(state->nosehoover_xi, nc*template_state->ngtc);
 +    snew(state->nosehoover_vxi, nc*template_state->ngtc);
 +    snew(state->therm_integral, template_state->ngtc);
 +    snew(state->nhpres_xi, nc*template_state->nnhpres);
 +    snew(state->nhpres_vxi, nc*template_state->nnhpres);
 +
 +    return state;
 +}
 +
 +void destroy_bufstate(t_state *state)
 +{
 +    sfree(state->x);
 +    sfree(state->v);
 +    sfree(state->nosehoover_xi);
 +    sfree(state->nosehoover_vxi);
 +    sfree(state->therm_integral);
 +    sfree(state->nhpres_xi);
 +    sfree(state->nhpres_vxi);
 +    sfree(state);
 +}
 +
 +void trotter_update(t_inputrec *ir, gmx_large_int_t step, gmx_ekindata_t *ekind,
 +                    gmx_enerdata_t *enerd, t_state *state,
 +                    tensor vir, t_mdatoms *md,
 +                    t_extmass *MassQ, int **trotter_seqlist, int trotter_seqno)
 +{
 +
 +    int             n, i, j, d, ntgrp, ngtc, gc = 0;
 +    t_grp_tcstat   *tcstat;
 +    t_grpopts      *opts;
 +    gmx_large_int_t step_eff;
 +    real            ecorr, pcorr, dvdlcorr;
 +    real            bmass, qmass, reft, kT, dt, nd;
 +    tensor          dumpres, dumvir;
 +    double         *scalefac, dtc;
 +    int            *trotter_seq;
 +    rvec            sumv = {0, 0, 0}, consk;
 +    gmx_bool        bCouple;
 +
 +    if (trotter_seqno <= ettTSEQ2)
 +    {
 +        step_eff = step-1;  /* the velocity verlet calls are actually out of order -- the first half step
 +                               is actually the last half step from the previous step.  Thus the first half step
 +                               actually corresponds to the n-1 step*/
 +
 +    }
 +    else
 +    {
 +        step_eff = step;
 +    }
 +
 +    bCouple = (ir->nsttcouple == 1 ||
 +               do_per_step(step_eff+ir->nsttcouple, ir->nsttcouple));
 +
 +    trotter_seq = trotter_seqlist[trotter_seqno];
 +
 +    if ((trotter_seq[0] == etrtSKIPALL) || (!bCouple))
 +    {
 +        return;
 +    }
 +    dtc  = ir->nsttcouple*ir->delta_t; /* This is OK for NPT, because nsttcouple == nstpcouple is enforcesd */
 +    opts = &(ir->opts);                /* just for ease of referencing */
 +    ngtc = opts->ngtc;
 +    assert(ngtc > 0);
 +    snew(scalefac, opts->ngtc);
 +    for (i = 0; i < ngtc; i++)
 +    {
 +        scalefac[i] = 1;
 +    }
 +    /* execute the series of trotter updates specified in the trotterpart array */
 +
 +    for (i = 0; i < NTROTTERPARTS; i++)
 +    {
 +        /* allow for doubled intgrators by doubling dt instead of making 2 calls */
 +        if ((trotter_seq[i] == etrtBAROV2) || (trotter_seq[i] == etrtBARONHC2) || (trotter_seq[i] == etrtNHC2))
 +        {
 +            dt = 2 * dtc;
 +        }
 +        else
 +        {
 +            dt = dtc;
 +        }
 +
 +        switch (trotter_seq[i])
 +        {
 +            case etrtBAROV:
 +            case etrtBAROV2:
 +                boxv_trotter(ir, &(state->veta), dt, state->box, ekind, vir,
 +                             enerd->term[F_PDISPCORR], MassQ);
 +                break;
 +            case etrtBARONHC:
 +            case etrtBARONHC2:
 +                NHC_trotter(opts, state->nnhpres, ekind, dt, state->nhpres_xi,
 +                            state->nhpres_vxi, NULL, &(state->veta), MassQ, FALSE);
 +                break;
 +            case etrtNHC:
 +            case etrtNHC2:
 +                NHC_trotter(opts, opts->ngtc, ekind, dt, state->nosehoover_xi,
 +                            state->nosehoover_vxi, scalefac, NULL, MassQ, (ir->eI == eiVV));
 +                /* need to rescale the kinetic energies and velocities here.  Could
 +                   scale the velocities later, but we need them scaled in order to
 +                   produce the correct outputs, so we'll scale them here. */
 +
 +                for (i = 0; i < ngtc; i++)
 +                {
 +                    tcstat                  = &ekind->tcstat[i];
 +                    tcstat->vscale_nhc      = scalefac[i];
 +                    tcstat->ekinscaleh_nhc *= (scalefac[i]*scalefac[i]);
 +                    tcstat->ekinscalef_nhc *= (scalefac[i]*scalefac[i]);
 +                }
 +                /* now that we've scaled the groupwise velocities, we can add them up to get the total */
 +                /* but do we actually need the total? */
 +
 +                /* modify the velocities as well */
 +                for (n = md->start; n < md->start+md->homenr; n++)
 +                {
 +                    if (md->cTC) /* does this conditional need to be here? is this always true?*/
 +                    {
 +                        gc = md->cTC[n];
 +                    }
 +                    for (d = 0; d < DIM; d++)
 +                    {
 +                        state->v[n][d] *= scalefac[gc];
 +                    }
 +
 +                    if (debug)
 +                    {
 +                        for (d = 0; d < DIM; d++)
 +                        {
 +                            sumv[d] += (state->v[n][d])/md->invmass[n];
 +                        }
 +                    }
 +                }
 +                break;
 +            default:
 +                break;
 +        }
 +    }
 +    /* check for conserved momentum -- worth looking at this again eventually, but not working right now.*/
 +#if 0
 +    if (debug)
 +    {
 +        if (bFirstHalf)
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                consk[d] = sumv[d]*exp((1 + 1.0/opts->nrdf[0])*((1.0/DIM)*log(det(state->box)/state->vol0)) + state->nosehoover_xi[0]);
 +            }
 +            fprintf(debug, "Conserved kappa: %15.8f %15.8f %15.8f\n", consk[0], consk[1], consk[2]);
 +        }
 +    }
 +#endif
 +    sfree(scalefac);
 +}
 +
 +
 +extern void init_npt_masses(t_inputrec *ir, t_state *state, t_extmass *MassQ, gmx_bool bInit)
 +{
 +    int           n, i, j, d, ntgrp, ngtc, nnhpres, nh, gc = 0;
 +    t_grp_tcstat *tcstat;
 +    t_grpopts    *opts;
 +    real          ecorr, pcorr, dvdlcorr;
 +    real          bmass, qmass, reft, kT, dt, ndj, nd;
 +    tensor        dumpres, dumvir;
 +
 +    opts    = &(ir->opts); /* just for ease of referencing */
 +    ngtc    = ir->opts.ngtc;
 +    nnhpres = state->nnhpres;
 +    nh      = state->nhchainlength;
 +
 +    if (ir->eI == eiMD)
 +    {
 +        if (bInit)
 +        {
 +            snew(MassQ->Qinv, ngtc);
 +        }
 +        for (i = 0; (i < ngtc); i++)
 +        {
 +            if ((opts->tau_t[i] > 0) && (opts->ref_t[i] > 0))
 +            {
 +                MassQ->Qinv[i] = 1.0/(sqr(opts->tau_t[i]/M_2PI)*opts->ref_t[i]);
 +            }
 +            else
 +            {
 +                MassQ->Qinv[i] = 0.0;
 +            }
 +        }
 +    }
 +    else if (EI_VV(ir->eI))
 +    {
 +        /* Set pressure variables */
 +
 +        if (bInit)
 +        {
 +            if (state->vol0 == 0)
 +            {
 +                state->vol0 = det(state->box);
 +                /* because we start by defining a fixed
 +                   compressibility, we need the volume at this
 +                   compressibility to solve the problem. */
 +            }
 +        }
 +
 +        /* units are nm^3 * ns^2 / (nm^3 * bar / kJ/mol) = kJ/mol  */
 +        /* Consider evaluating eventually if this the right mass to use.  All are correct, some might be more stable  */
 +        MassQ->Winv = (PRESFAC*trace(ir->compress)*BOLTZ*opts->ref_t[0])/(DIM*state->vol0*sqr(ir->tau_p/M_2PI));
 +        /* An alternate mass definition, from Tuckerman et al. */
 +        /* MassQ->Winv = 1.0/(sqr(ir->tau_p/M_2PI)*(opts->nrdf[0]+DIM)*BOLTZ*opts->ref_t[0]); */
 +        for (d = 0; d < DIM; d++)
 +        {
 +            for (n = 0; n < DIM; n++)
 +            {
 +                MassQ->Winvm[d][n] = PRESFAC*ir->compress[d][n]/(state->vol0*sqr(ir->tau_p/M_2PI));
 +                /* not clear this is correct yet for the anisotropic case. Will need to reevaluate
 +                   before using MTTK for anisotropic states.*/
 +            }
 +        }
 +        /* Allocate space for thermostat variables */
 +        if (bInit)
 +        {
 +            snew(MassQ->Qinv, ngtc*nh);
 +        }
 +
 +        /* now, set temperature variables */
 +        for (i = 0; i < ngtc; i++)
 +        {
 +            if ((opts->tau_t[i] > 0) && (opts->ref_t[i] > 0))
 +            {
 +                reft = max(0.0, opts->ref_t[i]);
 +                nd   = opts->nrdf[i];
 +                kT   = BOLTZ*reft;
 +                for (j = 0; j < nh; j++)
 +                {
 +                    if (j == 0)
 +                    {
 +                        ndj = nd;
 +                    }
 +                    else
 +                    {
 +                        ndj = 1;
 +                    }
 +                    MassQ->Qinv[i*nh+j]   = 1.0/(sqr(opts->tau_t[i]/M_2PI)*ndj*kT);
 +                }
 +            }
 +            else
 +            {
 +                reft = 0.0;
 +                for (j = 0; j < nh; j++)
 +                {
 +                    MassQ->Qinv[i*nh+j] = 0.0;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +int **init_npt_vars(t_inputrec *ir, t_state *state, t_extmass *MassQ, gmx_bool bTrotter)
 +{
 +    int           n, i, j, d, ntgrp, ngtc, nnhpres, nh, gc = 0;
 +    t_grp_tcstat *tcstat;
 +    t_grpopts    *opts;
 +    real          ecorr, pcorr, dvdlcorr;
 +    real          bmass, qmass, reft, kT, dt, ndj, nd;
 +    tensor        dumpres, dumvir;
 +    int         **trotter_seq;
 +
 +    opts    = &(ir->opts); /* just for ease of referencing */
 +    ngtc    = state->ngtc;
 +    nnhpres = state->nnhpres;
 +    nh      = state->nhchainlength;
 +
 +    init_npt_masses(ir, state, MassQ, TRUE);
 +
 +    /* first, initialize clear all the trotter calls */
 +    snew(trotter_seq, ettTSEQMAX);
 +    for (i = 0; i < ettTSEQMAX; i++)
 +    {
 +        snew(trotter_seq[i], NTROTTERPARTS);
 +        for (j = 0; j < NTROTTERPARTS; j++)
 +        {
 +            trotter_seq[i][j] = etrtNONE;
 +        }
 +        trotter_seq[i][0] = etrtSKIPALL;
 +    }
 +
 +    if (!bTrotter)
 +    {
 +        /* no trotter calls, so we never use the values in the array.
 +         * We access them (so we need to define them, but ignore
 +         * then.*/
 +
 +        return trotter_seq;
 +    }
 +
 +    /* compute the kinetic energy by using the half step velocities or
 +     * the kinetic energies, depending on the order of the trotter calls */
 +
 +    if (ir->eI == eiVV)
 +    {
 +        if (IR_NPT_TROTTER(ir))
 +        {
 +            /* This is the complicated version - there are 4 possible calls, depending on ordering.
 +               We start with the initial one. */
 +            /* first, a round that estimates veta. */
 +            trotter_seq[0][0] = etrtBAROV;
 +
 +            /* trotter_seq[1] is etrtNHC for 1/2 step velocities - leave zero */
 +
 +            /* The first half trotter update */
 +            trotter_seq[2][0] = etrtBAROV;
 +            trotter_seq[2][1] = etrtNHC;
 +            trotter_seq[2][2] = etrtBARONHC;
 +
 +            /* The second half trotter update */
 +            trotter_seq[3][0] = etrtBARONHC;
 +            trotter_seq[3][1] = etrtNHC;
 +            trotter_seq[3][2] = etrtBAROV;
 +
 +            /* trotter_seq[4] is etrtNHC for second 1/2 step velocities - leave zero */
 +
 +        }
 +        else if (IR_NVT_TROTTER(ir))
 +        {
 +            /* This is the easy version - there are only two calls, both the same.
 +               Otherwise, even easier -- no calls  */
 +            trotter_seq[2][0] = etrtNHC;
 +            trotter_seq[3][0] = etrtNHC;
 +        }
 +        else if (IR_NPH_TROTTER(ir))
 +        {
 +            /* This is the complicated version - there are 4 possible calls, depending on ordering.
 +               We start with the initial one. */
 +            /* first, a round that estimates veta. */
 +            trotter_seq[0][0] = etrtBAROV;
 +
 +            /* trotter_seq[1] is etrtNHC for 1/2 step velocities - leave zero */
 +
 +            /* The first half trotter update */
 +            trotter_seq[2][0] = etrtBAROV;
 +            trotter_seq[2][1] = etrtBARONHC;
 +
 +            /* The second half trotter update */
 +            trotter_seq[3][0] = etrtBARONHC;
 +            trotter_seq[3][1] = etrtBAROV;
 +
 +            /* trotter_seq[4] is etrtNHC for second 1/2 step velocities - leave zero */
 +        }
 +    }
 +    else if (ir->eI == eiVVAK)
 +    {
 +        if (IR_NPT_TROTTER(ir))
 +        {
 +            /* This is the complicated version - there are 4 possible calls, depending on ordering.
 +               We start with the initial one. */
 +            /* first, a round that estimates veta. */
 +            trotter_seq[0][0] = etrtBAROV;
 +
 +            /* The first half trotter update, part 1 -- double update, because it commutes */
 +            trotter_seq[1][0] = etrtNHC;
 +
 +            /* The first half trotter update, part 2 */
 +            trotter_seq[2][0] = etrtBAROV;
 +            trotter_seq[2][1] = etrtBARONHC;
 +
 +            /* The second half trotter update, part 1 */
 +            trotter_seq[3][0] = etrtBARONHC;
 +            trotter_seq[3][1] = etrtBAROV;
 +
 +            /* The second half trotter update */
 +            trotter_seq[4][0] = etrtNHC;
 +        }
 +        else if (IR_NVT_TROTTER(ir))
 +        {
 +            /* This is the easy version - there is only one call, both the same.
 +               Otherwise, even easier -- no calls  */
 +            trotter_seq[1][0] = etrtNHC;
 +            trotter_seq[4][0] = etrtNHC;
 +        }
 +        else if (IR_NPH_TROTTER(ir))
 +        {
 +            /* This is the complicated version - there are 4 possible calls, depending on ordering.
 +               We start with the initial one. */
 +            /* first, a round that estimates veta. */
 +            trotter_seq[0][0] = etrtBAROV;
 +
 +            /* The first half trotter update, part 1 -- leave zero */
 +            trotter_seq[1][0] = etrtNHC;
 +
 +            /* The first half trotter update, part 2 */
 +            trotter_seq[2][0] = etrtBAROV;
 +            trotter_seq[2][1] = etrtBARONHC;
 +
 +            /* The second half trotter update, part 1 */
 +            trotter_seq[3][0] = etrtBARONHC;
 +            trotter_seq[3][1] = etrtBAROV;
 +
 +            /* The second half trotter update -- blank for now */
 +        }
 +    }
 +
 +    switch (ir->epct)
 +    {
 +        case epctISOTROPIC:
 +        default:
 +            bmass = DIM*DIM; /* recommended mass parameters for isotropic barostat */
 +    }
 +
 +    snew(MassQ->QPinv, nnhpres*opts->nhchainlength);
 +
 +    /* barostat temperature */
 +    if ((ir->tau_p > 0) && (opts->ref_t[0] > 0))
 +    {
 +        reft = max(0.0, opts->ref_t[0]);
 +        kT   = BOLTZ*reft;
 +        for (i = 0; i < nnhpres; i++)
 +        {
 +            for (j = 0; j < nh; j++)
 +            {
 +                if (j == 0)
 +                {
 +                    qmass = bmass;
 +                }
 +                else
 +                {
 +                    qmass = 1;
 +                }
 +                MassQ->QPinv[i*opts->nhchainlength+j]   = 1.0/(sqr(opts->tau_t[0]/M_2PI)*qmass*kT);
 +            }
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; i < nnhpres; i++)
 +        {
 +            for (j = 0; j < nh; j++)
 +            {
 +                MassQ->QPinv[i*nh+j] = 0.0;
 +            }
 +        }
 +    }
 +    return trotter_seq;
 +}
 +
 +real NPT_energy(t_inputrec *ir, t_state *state, t_extmass *MassQ)
 +{
 +    int     i, j, nd, ndj, bmass, qmass, ngtcall;
 +    real    ener_npt, reft, eta, kT, tau;
 +    double *ivxi, *ixi;
 +    double *iQinv;
 +    real    vol, dbaro, W, Q;
 +    int     nh = state->nhchainlength;
 +
 +    ener_npt = 0;
 +
 +    /* now we compute the contribution of the pressure to the conserved quantity*/
 +
 +    if (ir->epc == epcMTTK)
 +    {
 +        /* find the volume, and the kinetic energy of the volume */
 +
 +        switch (ir->epct)
 +        {
 +
 +            case epctISOTROPIC:
 +                /* contribution from the pressure momenenta */
 +                ener_npt += 0.5*sqr(state->veta)/MassQ->Winv;
 +
 +                /* contribution from the PV term */
 +                vol       = det(state->box);
 +                ener_npt += vol*trace(ir->ref_p)/(DIM*PRESFAC);
 +
 +                break;
 +            case epctANISOTROPIC:
 +
 +                break;
 +
 +            case epctSURFACETENSION:
 +
 +                break;
 +            case epctSEMIISOTROPIC:
 +
 +                break;
 +            default:
 +                break;
 +        }
 +    }
 +
 +    if (IR_NPT_TROTTER(ir) || IR_NPH_TROTTER(ir))
 +    {
 +        /* add the energy from the barostat thermostat chain */
 +        for (i = 0; i < state->nnhpres; i++)
 +        {
 +
 +            /* note -- assumes only one degree of freedom that is thermostatted in barostat */
 +            ivxi  = &state->nhpres_vxi[i*nh];
 +            ixi   = &state->nhpres_xi[i*nh];
 +            iQinv = &(MassQ->QPinv[i*nh]);
 +            reft  = max(ir->opts.ref_t[0], 0); /* using 'System' temperature */
 +            kT    = BOLTZ * reft;
 +
 +            for (j = 0; j < nh; j++)
 +            {
 +                if (iQinv[j] > 0)
 +                {
 +                    ener_npt += 0.5*sqr(ivxi[j])/iQinv[j];
 +                    /* contribution from the thermal variable of the NH chain */
 +                    ener_npt += ixi[j]*kT;
 +                }
 +                if (debug)
 +                {
 +                    fprintf(debug, "P-T-group: %10d Chain %4d ThermV: %15.8f ThermX: %15.8f", i, j, ivxi[j], ixi[j]);
 +                }
 +            }
 +        }
 +    }
 +
 +    if (ir->etc)
 +    {
 +        for (i = 0; i < ir->opts.ngtc; i++)
 +        {
 +            ixi   = &state->nosehoover_xi[i*nh];
 +            ivxi  = &state->nosehoover_vxi[i*nh];
 +            iQinv = &(MassQ->Qinv[i*nh]);
 +
 +            nd   = ir->opts.nrdf[i];
 +            reft = max(ir->opts.ref_t[i], 0);
 +            kT   = BOLTZ * reft;
 +
 +            if (nd > 0)
 +            {
 +                if (IR_NVT_TROTTER(ir))
 +                {
 +                    /* contribution from the thermal momenta of the NH chain */
 +                    for (j = 0; j < nh; j++)
 +                    {
 +                        if (iQinv[j] > 0)
 +                        {
 +                            ener_npt += 0.5*sqr(ivxi[j])/iQinv[j];
 +                            /* contribution from the thermal variable of the NH chain */
 +                            if (j == 0)
 +                            {
 +                                ndj = nd;
 +                            }
 +                            else
 +                            {
 +                                ndj = 1;
 +                            }
 +                            ener_npt += ndj*ixi[j]*kT;
 +                        }
 +                    }
 +                }
 +                else  /* Other non Trotter temperature NH control  -- no chains yet. */
 +                {
 +                    ener_npt += 0.5*BOLTZ*nd*sqr(ivxi[0])/iQinv[0];
 +                    ener_npt += nd*ixi[0]*kT;
 +                }
 +            }
 +        }
 +    }
 +    return ener_npt;
 +}
 +
 +static real vrescale_gamdev(int ia, gmx_rng_t rng)
 +/* Gamma distribution, adapted from numerical recipes */
 +{
 +    int  j;
 +    real am, e, s, v1, v2, x, y;
 +
 +    if (ia < 6)
 +    {
 +        do
 +        {
 +            x = 1.0;
 +            for (j = 1; j <= ia; j++)
 +            {
 +                x *= gmx_rng_uniform_real(rng);
 +            }
 +        }
 +        while (x == 0);
 +        x = -log(x);
 +    }
 +    else
 +    {
 +        do
 +        {
 +            do
 +            {
 +                do
 +                {
 +                    v1 = gmx_rng_uniform_real(rng);
 +                    v2 = 2.0*gmx_rng_uniform_real(rng)-1.0;
 +                }
 +                while (v1*v1 + v2*v2 > 1.0 ||
 +                       v1*v1*GMX_REAL_MAX < 3.0*ia);
 +                /* The last check above ensures that both x (3.0 > 2.0 in s)
 +                 * and the pre-factor for e do not go out of range.
 +                 */
 +                y  = v2/v1;
 +                am = ia - 1;
 +                s  = sqrt(2.0*am + 1.0);
 +                x  = s*y + am;
 +            }
 +            while (x <= 0.0);
 +            e = (1.0 + y*y)*exp(am*log(x/am) - s*y);
 +        }
 +        while (gmx_rng_uniform_real(rng) > e);
 +    }
 +
 +    return x;
 +}
 +
 +static real vrescale_sumnoises(int nn, gmx_rng_t rng)
 +{
 +/*
 + * Returns the sum of n independent gaussian noises squared
 + * (i.e. equivalent to summing the square of the return values
 + * of nn calls to gmx_rng_gaussian_real).xs
 + */
 +    real rr;
 +
 +    if (nn == 0)
 +    {
 +        return 0.0;
 +    }
 +    else if (nn == 1)
 +    {
 +        rr = gmx_rng_gaussian_real(rng);
 +        return rr*rr;
 +    }
 +    else if (nn % 2 == 0)
 +    {
 +        return 2.0*vrescale_gamdev(nn/2, rng);
 +    }
 +    else
 +    {
 +        rr = gmx_rng_gaussian_real(rng);
 +        return 2.0*vrescale_gamdev((nn-1)/2, rng) + rr*rr;
 +    }
 +}
 +
 +static real vrescale_resamplekin(real kk, real sigma, int ndeg, real taut,
 +                                 gmx_rng_t rng)
 +{
 +/*
 + * Generates a new value for the kinetic energy,
 + * according to Bussi et al JCP (2007), Eq. (A7)
 + * kk:    present value of the kinetic energy of the atoms to be thermalized (in arbitrary units)
 + * sigma: target average value of the kinetic energy (ndeg k_b T/2)  (in the same units as kk)
 + * ndeg:  number of degrees of freedom of the atoms to be thermalized
 + * taut:  relaxation time of the thermostat, in units of 'how often this routine is called'
 + */
 +    real factor, rr;
 +
 +    if (taut > 0.1)
 +    {
 +        factor = exp(-1.0/taut);
 +    }
 +    else
 +    {
 +        factor = 0.0;
 +    }
 +    rr = gmx_rng_gaussian_real(rng);
 +    return
 +        kk +
 +        (1.0 - factor)*(sigma*(vrescale_sumnoises(ndeg-1, rng) + rr*rr)/ndeg - kk) +
 +        2.0*rr*sqrt(kk*sigma/ndeg*(1.0 - factor)*factor);
 +}
 +
 +void vrescale_tcoupl(t_inputrec *ir, gmx_ekindata_t *ekind, real dt,
 +                     double therm_integral[], gmx_rng_t rng)
 +{
 +    t_grpopts *opts;
 +    int        i;
 +    real       Ek, Ek_ref1, Ek_ref, Ek_new;
 +
 +    opts = &ir->opts;
 +
 +    for (i = 0; (i < opts->ngtc); i++)
 +    {
 +        if (ir->eI == eiVV)
 +        {
 +            Ek = trace(ekind->tcstat[i].ekinf);
 +        }
 +        else
 +        {
 +            Ek = trace(ekind->tcstat[i].ekinh);
 +        }
 +
++        if (opts->tau_t[i] >= 0 && opts->nrdf[i] > 0 && Ek > 0)
 +        {
 +            Ek_ref1 = 0.5*opts->ref_t[i]*BOLTZ;
 +            Ek_ref  = Ek_ref1*opts->nrdf[i];
 +
 +            Ek_new  = vrescale_resamplekin(Ek, Ek_ref, opts->nrdf[i],
 +                                           opts->tau_t[i]/dt, rng);
 +
 +            /* Analytically Ek_new>=0, but we check for rounding errors */
 +            if (Ek_new <= 0)
 +            {
 +                ekind->tcstat[i].lambda = 0.0;
 +            }
 +            else
 +            {
 +                ekind->tcstat[i].lambda = sqrt(Ek_new/Ek);
 +            }
 +
 +            therm_integral[i] -= Ek_new - Ek;
 +
 +            if (debug)
 +            {
 +                fprintf(debug, "TC: group %d: Ekr %g, Ek %g, Ek_new %g, Lambda: %g\n",
 +                        i, Ek_ref, Ek, Ek_new, ekind->tcstat[i].lambda);
 +            }
 +        }
 +        else
 +        {
 +            ekind->tcstat[i].lambda = 1.0;
 +        }
 +    }
 +}
 +
 +real vrescale_energy(t_grpopts *opts, double therm_integral[])
 +{
 +    int  i;
 +    real ener;
 +
 +    ener = 0;
 +    for (i = 0; i < opts->ngtc; i++)
 +    {
 +        ener += therm_integral[i];
 +    }
 +
 +    return ener;
 +}
 +
 +void rescale_velocities(gmx_ekindata_t *ekind, t_mdatoms *mdatoms,
 +                        int start, int end, rvec v[])
 +{
 +    t_grp_acc      *gstat;
 +    t_grp_tcstat   *tcstat;
 +    unsigned short *cACC, *cTC;
 +    int             ga, gt, n, d;
 +    real            lg;
 +    rvec            vrel;
 +
 +    tcstat = ekind->tcstat;
 +    cTC    = mdatoms->cTC;
 +
 +    if (ekind->bNEMD)
 +    {
 +        gstat  = ekind->grpstat;
 +        cACC   = mdatoms->cACC;
 +
 +        ga = 0;
 +        gt = 0;
 +        for (n = start; n < end; n++)
 +        {
 +            if (cACC)
 +            {
 +                ga   = cACC[n];
 +            }
 +            if (cTC)
 +            {
 +                gt   = cTC[n];
 +            }
 +            /* Only scale the velocity component relative to the COM velocity */
 +            rvec_sub(v[n], gstat[ga].u, vrel);
 +            lg = tcstat[gt].lambda;
 +            for (d = 0; d < DIM; d++)
 +            {
 +                v[n][d] = gstat[ga].u[d] + lg*vrel[d];
 +            }
 +        }
 +    }
 +    else
 +    {
 +        gt = 0;
 +        for (n = start; n < end; n++)
 +        {
 +            if (cTC)
 +            {
 +                gt   = cTC[n];
 +            }
 +            lg = tcstat[gt].lambda;
 +            for (d = 0; d < DIM; d++)
 +            {
 +                v[n][d] *= lg;
 +            }
 +        }
 +    }
 +}
 +
 +
 +/* set target temperatures if we are annealing */
 +void update_annealing_target_temp(t_grpopts *opts, real t)
 +{
 +    int  i, j, n, npoints;
 +    real pert, thist = 0, x;
 +
 +    for (i = 0; i < opts->ngtc; i++)
 +    {
 +        npoints = opts->anneal_npoints[i];
 +        switch (opts->annealing[i])
 +        {
 +            case eannNO:
 +                continue;
 +            case  eannPERIODIC:
 +                /* calculate time modulo the period */
 +                pert  = opts->anneal_time[i][npoints-1];
 +                n     = t / pert;
 +                thist = t - n*pert; /* modulo time */
 +                /* Make sure rounding didn't get us outside the interval */
 +                if (fabs(thist-pert) < GMX_REAL_EPS*100)
 +                {
 +                    thist = 0;
 +                }
 +                break;
 +            case eannSINGLE:
 +                thist = t;
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Death horror in update_annealing_target_temp (i=%d/%d npoints=%d)", i, opts->ngtc, npoints);
 +        }
 +        /* We are doing annealing for this group if we got here,
 +         * and we have the (relative) time as thist.
 +         * calculate target temp */
 +        j = 0;
 +        while ((j < npoints-1) && (thist > (opts->anneal_time[i][j+1])))
 +        {
 +            j++;
 +        }
 +        if (j < npoints-1)
 +        {
 +            /* Found our position between points j and j+1.
 +             * Interpolate: x is the amount from j+1, (1-x) from point j
 +             * First treat possible jumps in temperature as a special case.
 +             */
 +            if ((opts->anneal_time[i][j+1]-opts->anneal_time[i][j]) < GMX_REAL_EPS*100)
 +            {
 +                opts->ref_t[i] = opts->anneal_temp[i][j+1];
 +            }
 +            else
 +            {
 +                x = ((thist-opts->anneal_time[i][j])/
 +                     (opts->anneal_time[i][j+1]-opts->anneal_time[i][j]));
 +                opts->ref_t[i] = x*opts->anneal_temp[i][j+1]+(1-x)*opts->anneal_temp[i][j];
 +            }
 +        }
 +        else
 +        {
 +            opts->ref_t[i] = opts->anneal_temp[i][npoints-1];
 +        }
 +    }
 +}
index 25ef8e79b034fac81cfb17b0c2d266d27029d1e5,0000000000000000000000000000000000000000..f7b0479d706785a4525e3a8eb535be78b43bf6de
mode 100644,000000..100644
--- /dev/null
@@@ -1,9708 -1,0 +1,9723 @@@
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + * This file is part of Gromacs        Copyright (c) 1991-2008
 + * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
 + *
 + * 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.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org
 + *
 + * And Hey:
 + * Gnomes, ROck Monsters And Chili Sauce
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdio.h>
 +#include <time.h>
 +#include <math.h>
 +#include <string.h>
 +#include <stdlib.h>
 +#include "typedefs.h"
 +#include "smalloc.h"
 +#include "gmx_fatal.h"
 +#include "gmx_fatal_collective.h"
 +#include "vec.h"
 +#include "domdec.h"
 +#include "domdec_network.h"
 +#include "nrnb.h"
 +#include "pbc.h"
 +#include "chargegroup.h"
 +#include "constr.h"
 +#include "mdatoms.h"
 +#include "names.h"
 +#include "pdbio.h"
 +#include "futil.h"
 +#include "force.h"
 +#include "pme.h"
 +#include "pull.h"
 +#include "pull_rotation.h"
 +#include "gmx_wallcycle.h"
 +#include "mdrun.h"
 +#include "nsgrid.h"
 +#include "shellfc.h"
 +#include "mtop_util.h"
 +#include "gmxfio.h"
 +#include "gmx_ga2la.h"
 +#include "gmx_sort.h"
 +#include "macros.h"
 +#include "nbnxn_search.h"
 +#include "bondf.h"
 +#include "gmx_omp_nthreads.h"
 +
 +#include "gromacs/utility/gmxmpi.h"
 +
 +#define DDRANK(dd, rank)    (rank)
 +#define DDMASTERRANK(dd)   (dd->masterrank)
 +
 +typedef struct gmx_domdec_master
 +{
 +    /* The cell boundaries */
 +    real **cell_x;
 +    /* The global charge group division */
 +    int   *ncg;    /* Number of home charge groups for each node */
 +    int   *index;  /* Index of nnodes+1 into cg */
 +    int   *cg;     /* Global charge group index */
 +    int   *nat;    /* Number of home atoms for each node. */
 +    int   *ibuf;   /* Buffer for communication */
 +    rvec  *vbuf;   /* Buffer for state scattering and gathering */
 +} gmx_domdec_master_t;
 +
 +typedef struct
 +{
 +    /* The numbers of charge groups to send and receive for each cell
 +     * that requires communication, the last entry contains the total
 +     * number of atoms that needs to be communicated.
 +     */
 +    int  nsend[DD_MAXIZONE+2];
 +    int  nrecv[DD_MAXIZONE+2];
 +    /* The charge groups to send */
 +    int *index;
 +    int  nalloc;
 +    /* The atom range for non-in-place communication */
 +    int  cell2at0[DD_MAXIZONE];
 +    int  cell2at1[DD_MAXIZONE];
 +} gmx_domdec_ind_t;
 +
 +typedef struct
 +{
 +    int               np;       /* Number of grid pulses in this dimension */
 +    int               np_dlb;   /* For dlb, for use with edlbAUTO          */
 +    gmx_domdec_ind_t *ind;      /* The indices to communicate, size np     */
 +    int               np_nalloc;
 +    gmx_bool          bInPlace; /* Can we communicate in place?            */
 +} gmx_domdec_comm_dim_t;
 +
 +typedef struct
 +{
 +    gmx_bool *bCellMin;    /* Temp. var.: is this cell size at the limit     */
 +    real     *cell_f;      /* State var.: cell boundaries, box relative      */
 +    real     *old_cell_f;  /* Temp. var.: old cell size                      */
 +    real     *cell_f_max0; /* State var.: max lower boundary, incl neighbors */
 +    real     *cell_f_min1; /* State var.: min upper boundary, incl neighbors */
 +    real     *bound_min;   /* Temp. var.: lower limit for cell boundary      */
 +    real     *bound_max;   /* Temp. var.: upper limit for cell boundary      */
 +    gmx_bool  bLimited;    /* State var.: is DLB limited in this dim and row */
 +    real     *buf_ncd;     /* Temp. var.                                     */
 +} gmx_domdec_root_t;
 +
 +#define DD_NLOAD_MAX 9
 +
 +/* Here floats are accurate enough, since these variables
 + * only influence the load balancing, not the actual MD results.
 + */
 +typedef struct
 +{
 +    int    nload;
 +    float *load;
 +    float  sum;
 +    float  max;
 +    float  sum_m;
 +    float  cvol_min;
 +    float  mdf;
 +    float  pme;
 +    int    flags;
 +} gmx_domdec_load_t;
 +
 +typedef struct
 +{
 +    int  nsc;
 +    int  ind_gl;
 +    int  ind;
 +} gmx_cgsort_t;
 +
 +typedef struct
 +{
 +    gmx_cgsort_t *sort;
 +    gmx_cgsort_t *sort2;
 +    int           sort_nalloc;
 +    gmx_cgsort_t *sort_new;
 +    int           sort_new_nalloc;
 +    int          *ibuf;
 +    int           ibuf_nalloc;
 +} gmx_domdec_sort_t;
 +
 +typedef struct
 +{
 +    rvec *v;
 +    int   nalloc;
 +} vec_rvec_t;
 +
 +/* This enum determines the order of the coordinates.
 + * ddnatHOME and ddnatZONE should be first and second,
 + * the others can be ordered as wanted.
 + */
 +enum {
 +    ddnatHOME, ddnatZONE, ddnatVSITE, ddnatCON, ddnatNR
 +};
 +
 +enum {
 +    edlbAUTO, edlbNO, edlbYES, edlbNR
 +};
 +const char *edlb_names[edlbNR] = { "auto", "no", "yes" };
 +
 +typedef struct
 +{
 +    int      dim;       /* The dimension                                          */
 +    gmx_bool dim_match; /* Tells if DD and PME dims match                         */
 +    int      nslab;     /* The number of PME slabs in this dimension              */
 +    real    *slb_dim_f; /* Cell sizes for determining the PME comm. with SLB    */
 +    int     *pp_min;    /* The minimum pp node location, size nslab               */
 +    int     *pp_max;    /* The maximum pp node location,size nslab                */
 +    int      maxshift;  /* The maximum shift for coordinate redistribution in PME */
 +} gmx_ddpme_t;
 +
 +typedef struct
 +{
 +    real min0;    /* The minimum bottom of this zone                        */
 +    real max1;    /* The maximum top of this zone                           */
 +    real min1;    /* The minimum top of this zone                           */
 +    real mch0;    /* The maximum bottom communicaton height for this zone   */
 +    real mch1;    /* The maximum top communicaton height for this zone      */
 +    real p1_0;    /* The bottom value of the first cell in this zone        */
 +    real p1_1;    /* The top value of the first cell in this zone           */
 +} gmx_ddzone_t;
 +
 +typedef struct
 +{
 +    gmx_domdec_ind_t ind;
 +    int             *ibuf;
 +    int              ibuf_nalloc;
 +    vec_rvec_t       vbuf;
 +    int              nsend;
 +    int              nat;
 +    int              nsend_zone;
 +} dd_comm_setup_work_t;
 +
 +typedef struct gmx_domdec_comm
 +{
 +    /* All arrays are indexed with 0 to dd->ndim (not Cartesian indexing),
 +     * unless stated otherwise.
 +     */
 +
 +    /* The number of decomposition dimensions for PME, 0: no PME */
 +    int         npmedecompdim;
 +    /* The number of nodes doing PME (PP/PME or only PME) */
 +    int         npmenodes;
 +    int         npmenodes_x;
 +    int         npmenodes_y;
 +    /* The communication setup including the PME only nodes */
 +    gmx_bool    bCartesianPP_PME;
 +    ivec        ntot;
 +    int         cartpmedim;
 +    int        *pmenodes;          /* size npmenodes                         */
 +    int        *ddindex2simnodeid; /* size npmenodes, only with bCartesianPP
 +                                    * but with bCartesianPP_PME              */
 +    gmx_ddpme_t ddpme[2];
 +
 +    /* The DD particle-particle nodes only */
 +    gmx_bool bCartesianPP;
 +    int     *ddindex2ddnodeid; /* size npmenode, only with bCartesianPP_PME */
 +
 +    /* The global charge groups */
 +    t_block cgs_gl;
 +
 +    /* Should we sort the cgs */
 +    int                nstSortCG;
 +    gmx_domdec_sort_t *sort;
 +
 +    /* Are there charge groups? */
 +    gmx_bool bCGs;
 +
 +    /* Are there bonded and multi-body interactions between charge groups? */
 +    gmx_bool bInterCGBondeds;
 +    gmx_bool bInterCGMultiBody;
 +
 +    /* Data for the optional bonded interaction atom communication range */
 +    gmx_bool  bBondComm;
 +    t_blocka *cglink;
 +    char     *bLocalCG;
 +
 +    /* The DLB option */
 +    int      eDLB;
 +    /* Are we actually using DLB? */
 +    gmx_bool bDynLoadBal;
 +
 +    /* Cell sizes for static load balancing, first index cartesian */
 +    real **slb_frac;
 +
 +    /* The width of the communicated boundaries */
 +    real     cutoff_mbody;
 +    real     cutoff;
 +    /* The minimum cell size (including triclinic correction) */
 +    rvec     cellsize_min;
 +    /* For dlb, for use with edlbAUTO */
 +    rvec     cellsize_min_dlb;
 +    /* The lower limit for the DD cell size with DLB */
 +    real     cellsize_limit;
 +    /* Effectively no NB cut-off limit with DLB for systems without PBC? */
 +    gmx_bool bVacDLBNoLimit;
 +
 +    /* With PME load balancing we set limits on DLB */
 +    gmx_bool bPMELoadBalDLBLimits;
 +    /* DLB needs to take into account that we want to allow this maximum
 +     * cut-off (for PME load balancing), this could limit cell boundaries.
 +     */
 +    real PMELoadBal_max_cutoff;
 +
 +    /* tric_dir is only stored here because dd_get_ns_ranges needs it */
 +    ivec tric_dir;
 +    /* box0 and box_size are required with dim's without pbc and -gcom */
 +    rvec box0;
 +    rvec box_size;
 +
 +    /* The cell boundaries */
 +    rvec cell_x0;
 +    rvec cell_x1;
 +
 +    /* The old location of the cell boundaries, to check cg displacements */
 +    rvec old_cell_x0;
 +    rvec old_cell_x1;
 +
 +    /* The communication setup and charge group boundaries for the zones */
 +    gmx_domdec_zones_t zones;
 +
 +    /* The zone limits for DD dimensions 1 and 2 (not 0), determined from
 +     * cell boundaries of neighboring cells for dynamic load balancing.
 +     */
 +    gmx_ddzone_t zone_d1[2];
 +    gmx_ddzone_t zone_d2[2][2];
 +
 +    /* The coordinate/force communication setup and indices */
 +    gmx_domdec_comm_dim_t cd[DIM];
 +    /* The maximum number of cells to communicate with in one dimension */
 +    int                   maxpulse;
 +
 +    /* Which cg distribution is stored on the master node */
 +    int master_cg_ddp_count;
 +
 +    /* The number of cg's received from the direct neighbors */
 +    int  zone_ncg1[DD_MAXZONE];
 +
 +    /* The atom counts, the range for each type t is nat[t-1] <= at < nat[t] */
 +    int  nat[ddnatNR];
 +
 +    /* Array for signalling if atoms have moved to another domain */
 +    int  *moved;
 +    int   moved_nalloc;
 +
 +    /* Communication buffer for general use */
 +    int  *buf_int;
 +    int   nalloc_int;
 +
 +    /* Communication buffer for general use */
 +    vec_rvec_t vbuf;
 +
 +    /* Temporary storage for thread parallel communication setup */
 +    int                   nth;
 +    dd_comm_setup_work_t *dth;
 +
 +    /* Communication buffers only used with multiple grid pulses */
 +    int       *buf_int2;
 +    int        nalloc_int2;
 +    vec_rvec_t vbuf2;
 +
 +    /* Communication buffers for local redistribution */
 +    int  **cggl_flag;
 +    int    cggl_flag_nalloc[DIM*2];
 +    rvec **cgcm_state;
 +    int    cgcm_state_nalloc[DIM*2];
 +
 +    /* Cell sizes for dynamic load balancing */
 +    gmx_domdec_root_t **root;
 +    real               *cell_f_row;
 +    real                cell_f0[DIM];
 +    real                cell_f1[DIM];
 +    real                cell_f_max0[DIM];
 +    real                cell_f_min1[DIM];
 +
 +    /* Stuff for load communication */
 +    gmx_bool           bRecordLoad;
 +    gmx_domdec_load_t *load;
 +#ifdef GMX_MPI
 +    MPI_Comm          *mpi_comm_load;
 +#endif
 +
 +    /* Maximum DLB scaling per load balancing step in percent */
 +    int dlb_scale_lim;
 +
 +    /* Cycle counters */
 +    float  cycl[ddCyclNr];
 +    int    cycl_n[ddCyclNr];
 +    float  cycl_max[ddCyclNr];
 +    /* Flop counter (0=no,1=yes,2=with (eFlop-1)*5% noise */
 +    int    eFlop;
 +    double flop;
 +    int    flop_n;
 +    /* Have often have did we have load measurements */
 +    int    n_load_have;
 +    /* Have often have we collected the load measurements */
 +    int    n_load_collect;
 +
 +    /* Statistics */
 +    double sum_nat[ddnatNR-ddnatZONE];
 +    int    ndecomp;
 +    int    nload;
 +    double load_step;
 +    double load_sum;
 +    double load_max;
 +    ivec   load_lim;
 +    double load_mdf;
 +    double load_pme;
 +
 +    /* The last partition step */
 +    gmx_large_int_t partition_step;
 +
 +    /* Debugging */
 +    int  nstDDDump;
 +    int  nstDDDumpGrid;
 +    int  DD_debug;
 +} gmx_domdec_comm_t;
 +
 +/* The size per charge group of the cggl_flag buffer in gmx_domdec_comm_t */
 +#define DD_CGIBS 2
 +
 +/* The flags for the cggl_flag buffer in gmx_domdec_comm_t */
 +#define DD_FLAG_NRCG  65535
 +#define DD_FLAG_FW(d) (1<<(16+(d)*2))
 +#define DD_FLAG_BW(d) (1<<(16+(d)*2+1))
 +
 +/* Zone permutation required to obtain consecutive charge groups
 + * for neighbor searching.
 + */
 +static const int zone_perm[3][4] = { {0, 0, 0, 0}, {1, 0, 0, 0}, {3, 0, 1, 2} };
 +
 +/* dd_zo and dd_zp3/dd_zp2 are set up such that i zones with non-zero
 + * components see only j zones with that component 0.
 + */
 +
 +/* The DD zone order */
 +static const ivec dd_zo[DD_MAXZONE] =
 +{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {1, 0, 1}, {1, 1, 1}};
 +
 +/* The 3D setup */
 +#define dd_z3n  8
 +#define dd_zp3n 4
 +static const ivec dd_zp3[dd_zp3n] = {{0, 0, 8}, {1, 3, 6}, {2, 5, 6}, {3, 5, 7}};
 +
 +/* The 2D setup */
 +#define dd_z2n  4
 +#define dd_zp2n 2
 +static const ivec dd_zp2[dd_zp2n] = {{0, 0, 4}, {1, 3, 4}};
 +
 +/* The 1D setup */
 +#define dd_z1n  2
 +#define dd_zp1n 1
 +static const ivec dd_zp1[dd_zp1n] = {{0, 0, 2}};
 +
 +/* Factors used to avoid problems due to rounding issues */
 +#define DD_CELL_MARGIN       1.0001
 +#define DD_CELL_MARGIN2      1.00005
 +/* Factor to account for pressure scaling during nstlist steps */
 +#define DD_PRES_SCALE_MARGIN 1.02
 +
 +/* Allowed performance loss before we DLB or warn */
 +#define DD_PERF_LOSS 0.05
 +
 +#define DD_CELL_F_SIZE(dd, di) ((dd)->nc[(dd)->dim[(di)]]+1+(di)*2+1+(di))
 +
 +/* Use separate MPI send and receive commands
 + * when nnodes <= GMX_DD_NNODES_SENDRECV.
 + * This saves memory (and some copying for small nnodes).
 + * For high parallelization scatter and gather calls are used.
 + */
 +#define GMX_DD_NNODES_SENDRECV 4
 +
 +
 +/*
 +   #define dd_index(n,i) ((((i)[ZZ]*(n)[YY] + (i)[YY])*(n)[XX]) + (i)[XX])
 +
 +   static void index2xyz(ivec nc,int ind,ivec xyz)
 +   {
 +   xyz[XX] = ind % nc[XX];
 +   xyz[YY] = (ind / nc[XX]) % nc[YY];
 +   xyz[ZZ] = ind / (nc[YY]*nc[XX]);
 +   }
 + */
 +
 +/* This order is required to minimize the coordinate communication in PME
 + * which uses decomposition in the x direction.
 + */
 +#define dd_index(n, i) ((((i)[XX]*(n)[YY] + (i)[YY])*(n)[ZZ]) + (i)[ZZ])
 +
 +static void ddindex2xyz(ivec nc, int ind, ivec xyz)
 +{
 +    xyz[XX] = ind / (nc[YY]*nc[ZZ]);
 +    xyz[YY] = (ind / nc[ZZ]) % nc[YY];
 +    xyz[ZZ] = ind % nc[ZZ];
 +}
 +
 +static int ddcoord2ddnodeid(gmx_domdec_t *dd, ivec c)
 +{
 +    int ddindex;
 +    int ddnodeid = -1;
 +
 +    ddindex = dd_index(dd->nc, c);
 +    if (dd->comm->bCartesianPP_PME)
 +    {
 +        ddnodeid = dd->comm->ddindex2ddnodeid[ddindex];
 +    }
 +    else if (dd->comm->bCartesianPP)
 +    {
 +#ifdef GMX_MPI
 +        MPI_Cart_rank(dd->mpi_comm_all, c, &ddnodeid);
 +#endif
 +    }
 +    else
 +    {
 +        ddnodeid = ddindex;
 +    }
 +
 +    return ddnodeid;
 +}
 +
 +static gmx_bool dynamic_dd_box(gmx_ddbox_t *ddbox, t_inputrec *ir)
 +{
 +    return (ddbox->nboundeddim < DIM || DYNAMIC_BOX(*ir));
 +}
 +
 +int ddglatnr(gmx_domdec_t *dd, int i)
 +{
 +    int atnr;
 +
 +    if (dd == NULL)
 +    {
 +        atnr = i + 1;
 +    }
 +    else
 +    {
 +        if (i >= dd->comm->nat[ddnatNR-1])
 +        {
 +            gmx_fatal(FARGS, "glatnr called with %d, which is larger than the local number of atoms (%d)", i, dd->comm->nat[ddnatNR-1]);
 +        }
 +        atnr = dd->gatindex[i] + 1;
 +    }
 +
 +    return atnr;
 +}
 +
 +t_block *dd_charge_groups_global(gmx_domdec_t *dd)
 +{
 +    return &dd->comm->cgs_gl;
 +}
 +
 +static void vec_rvec_init(vec_rvec_t *v)
 +{
 +    v->nalloc = 0;
 +    v->v      = NULL;
 +}
 +
 +static void vec_rvec_check_alloc(vec_rvec_t *v, int n)
 +{
 +    if (n > v->nalloc)
 +    {
 +        v->nalloc = over_alloc_dd(n);
 +        srenew(v->v, v->nalloc);
 +    }
 +}
 +
 +void dd_store_state(gmx_domdec_t *dd, t_state *state)
 +{
 +    int i;
 +
 +    if (state->ddp_count != dd->ddp_count)
 +    {
 +        gmx_incons("The state does not the domain decomposition state");
 +    }
 +
 +    state->ncg_gl = dd->ncg_home;
 +    if (state->ncg_gl > state->cg_gl_nalloc)
 +    {
 +        state->cg_gl_nalloc = over_alloc_dd(state->ncg_gl);
 +        srenew(state->cg_gl, state->cg_gl_nalloc);
 +    }
 +    for (i = 0; i < state->ncg_gl; i++)
 +    {
 +        state->cg_gl[i] = dd->index_gl[i];
 +    }
 +
 +    state->ddp_count_cg_gl = dd->ddp_count;
 +}
 +
 +gmx_domdec_zones_t *domdec_zones(gmx_domdec_t *dd)
 +{
 +    return &dd->comm->zones;
 +}
 +
 +void dd_get_ns_ranges(gmx_domdec_t *dd, int icg,
 +                      int *jcg0, int *jcg1, ivec shift0, ivec shift1)
 +{
 +    gmx_domdec_zones_t *zones;
 +    int                 izone, d, dim;
 +
 +    zones = &dd->comm->zones;
 +
 +    izone = 0;
 +    while (icg >= zones->izone[izone].cg1)
 +    {
 +        izone++;
 +    }
 +
 +    if (izone == 0)
 +    {
 +        *jcg0 = icg;
 +    }
 +    else if (izone < zones->nizone)
 +    {
 +        *jcg0 = zones->izone[izone].jcg0;
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "DD icg %d out of range: izone (%d) >= nizone (%d)",
 +                  icg, izone, zones->nizone);
 +    }
 +
 +    *jcg1 = zones->izone[izone].jcg1;
 +
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        dim         = dd->dim[d];
 +        shift0[dim] = zones->izone[izone].shift0[dim];
 +        shift1[dim] = zones->izone[izone].shift1[dim];
 +        if (dd->comm->tric_dir[dim] || (dd->bGridJump && d > 0))
 +        {
 +            /* A conservative approach, this can be optimized */
 +            shift0[dim] -= 1;
 +            shift1[dim] += 1;
 +        }
 +    }
 +}
 +
 +int dd_natoms_vsite(gmx_domdec_t *dd)
 +{
 +    return dd->comm->nat[ddnatVSITE];
 +}
 +
 +void dd_get_constraint_range(gmx_domdec_t *dd, int *at_start, int *at_end)
 +{
 +    *at_start = dd->comm->nat[ddnatCON-1];
 +    *at_end   = dd->comm->nat[ddnatCON];
 +}
 +
 +void dd_move_x(gmx_domdec_t *dd, matrix box, rvec x[])
 +{
 +    int                    nzone, nat_tot, n, d, p, i, j, at0, at1, zone;
 +    int                   *index, *cgindex;
 +    gmx_domdec_comm_t     *comm;
 +    gmx_domdec_comm_dim_t *cd;
 +    gmx_domdec_ind_t      *ind;
 +    rvec                   shift = {0, 0, 0}, *buf, *rbuf;
 +    gmx_bool               bPBC, bScrew;
 +
 +    comm = dd->comm;
 +
 +    cgindex = dd->cgindex;
 +
 +    buf = comm->vbuf.v;
 +
 +    nzone   = 1;
 +    nat_tot = dd->nat_home;
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        bPBC   = (dd->ci[dd->dim[d]] == 0);
 +        bScrew = (bPBC && dd->bScrewPBC && dd->dim[d] == XX);
 +        if (bPBC)
 +        {
 +            copy_rvec(box[dd->dim[d]], shift);
 +        }
 +        cd = &comm->cd[d];
 +        for (p = 0; p < cd->np; p++)
 +        {
 +            ind   = &cd->ind[p];
 +            index = ind->index;
 +            n     = 0;
 +            if (!bPBC)
 +            {
 +                for (i = 0; i < ind->nsend[nzone]; i++)
 +                {
 +                    at0 = cgindex[index[i]];
 +                    at1 = cgindex[index[i]+1];
 +                    for (j = at0; j < at1; j++)
 +                    {
 +                        copy_rvec(x[j], buf[n]);
 +                        n++;
 +                    }
 +                }
 +            }
 +            else if (!bScrew)
 +            {
 +                for (i = 0; i < ind->nsend[nzone]; i++)
 +                {
 +                    at0 = cgindex[index[i]];
 +                    at1 = cgindex[index[i]+1];
 +                    for (j = at0; j < at1; j++)
 +                    {
 +                        /* We need to shift the coordinates */
 +                        rvec_add(x[j], shift, buf[n]);
 +                        n++;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; i < ind->nsend[nzone]; i++)
 +                {
 +                    at0 = cgindex[index[i]];
 +                    at1 = cgindex[index[i]+1];
 +                    for (j = at0; j < at1; j++)
 +                    {
 +                        /* Shift x */
 +                        buf[n][XX] = x[j][XX] + shift[XX];
 +                        /* Rotate y and z.
 +                         * This operation requires a special shift force
 +                         * treatment, which is performed in calc_vir.
 +                         */
 +                        buf[n][YY] = box[YY][YY] - x[j][YY];
 +                        buf[n][ZZ] = box[ZZ][ZZ] - x[j][ZZ];
 +                        n++;
 +                    }
 +                }
 +            }
 +
 +            if (cd->bInPlace)
 +            {
 +                rbuf = x + nat_tot;
 +            }
 +            else
 +            {
 +                rbuf = comm->vbuf2.v;
 +            }
 +            /* Send and receive the coordinates */
 +            dd_sendrecv_rvec(dd, d, dddirBackward,
 +                             buf,  ind->nsend[nzone+1],
 +                             rbuf, ind->nrecv[nzone+1]);
 +            if (!cd->bInPlace)
 +            {
 +                j = 0;
 +                for (zone = 0; zone < nzone; zone++)
 +                {
 +                    for (i = ind->cell2at0[zone]; i < ind->cell2at1[zone]; i++)
 +                    {
 +                        copy_rvec(rbuf[j], x[i]);
 +                        j++;
 +                    }
 +                }
 +            }
 +            nat_tot += ind->nrecv[nzone+1];
 +        }
 +        nzone += nzone;
 +    }
 +}
 +
 +void dd_move_f(gmx_domdec_t *dd, rvec f[], rvec *fshift)
 +{
 +    int                    nzone, nat_tot, n, d, p, i, j, at0, at1, zone;
 +    int                   *index, *cgindex;
 +    gmx_domdec_comm_t     *comm;
 +    gmx_domdec_comm_dim_t *cd;
 +    gmx_domdec_ind_t      *ind;
 +    rvec                  *buf, *sbuf;
 +    ivec                   vis;
 +    int                    is;
 +    gmx_bool               bPBC, bScrew;
 +
 +    comm = dd->comm;
 +
 +    cgindex = dd->cgindex;
 +
 +    buf = comm->vbuf.v;
 +
 +    n       = 0;
 +    nzone   = comm->zones.n/2;
 +    nat_tot = dd->nat_tot;
 +    for (d = dd->ndim-1; d >= 0; d--)
 +    {
 +        bPBC   = (dd->ci[dd->dim[d]] == 0);
 +        bScrew = (bPBC && dd->bScrewPBC && dd->dim[d] == XX);
 +        if (fshift == NULL && !bScrew)
 +        {
 +            bPBC = FALSE;
 +        }
 +        /* Determine which shift vector we need */
 +        clear_ivec(vis);
 +        vis[dd->dim[d]] = 1;
 +        is              = IVEC2IS(vis);
 +
 +        cd = &comm->cd[d];
 +        for (p = cd->np-1; p >= 0; p--)
 +        {
 +            ind      = &cd->ind[p];
 +            nat_tot -= ind->nrecv[nzone+1];
 +            if (cd->bInPlace)
 +            {
 +                sbuf = f + nat_tot;
 +            }
 +            else
 +            {
 +                sbuf = comm->vbuf2.v;
 +                j    = 0;
 +                for (zone = 0; zone < nzone; zone++)
 +                {
 +                    for (i = ind->cell2at0[zone]; i < ind->cell2at1[zone]; i++)
 +                    {
 +                        copy_rvec(f[i], sbuf[j]);
 +                        j++;
 +                    }
 +                }
 +            }
 +            /* Communicate the forces */
 +            dd_sendrecv_rvec(dd, d, dddirForward,
 +                             sbuf, ind->nrecv[nzone+1],
 +                             buf,  ind->nsend[nzone+1]);
 +            index = ind->index;
 +            /* Add the received forces */
 +            n = 0;
 +            if (!bPBC)
 +            {
 +                for (i = 0; i < ind->nsend[nzone]; i++)
 +                {
 +                    at0 = cgindex[index[i]];
 +                    at1 = cgindex[index[i]+1];
 +                    for (j = at0; j < at1; j++)
 +                    {
 +                        rvec_inc(f[j], buf[n]);
 +                        n++;
 +                    }
 +                }
 +            }
 +            else if (!bScrew)
 +            {
 +                for (i = 0; i < ind->nsend[nzone]; i++)
 +                {
 +                    at0 = cgindex[index[i]];
 +                    at1 = cgindex[index[i]+1];
 +                    for (j = at0; j < at1; j++)
 +                    {
 +                        rvec_inc(f[j], buf[n]);
 +                        /* Add this force to the shift force */
 +                        rvec_inc(fshift[is], buf[n]);
 +                        n++;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; i < ind->nsend[nzone]; i++)
 +                {
 +                    at0 = cgindex[index[i]];
 +                    at1 = cgindex[index[i]+1];
 +                    for (j = at0; j < at1; j++)
 +                    {
 +                        /* Rotate the force */
 +                        f[j][XX] += buf[n][XX];
 +                        f[j][YY] -= buf[n][YY];
 +                        f[j][ZZ] -= buf[n][ZZ];
 +                        if (fshift)
 +                        {
 +                            /* Add this force to the shift force */
 +                            rvec_inc(fshift[is], buf[n]);
 +                        }
 +                        n++;
 +                    }
 +                }
 +            }
 +        }
 +        nzone /= 2;
 +    }
 +}
 +
 +void dd_atom_spread_real(gmx_domdec_t *dd, real v[])
 +{
 +    int                    nzone, nat_tot, n, d, p, i, j, at0, at1, zone;
 +    int                   *index, *cgindex;
 +    gmx_domdec_comm_t     *comm;
 +    gmx_domdec_comm_dim_t *cd;
 +    gmx_domdec_ind_t      *ind;
 +    real                  *buf, *rbuf;
 +
 +    comm = dd->comm;
 +
 +    cgindex = dd->cgindex;
 +
 +    buf = &comm->vbuf.v[0][0];
 +
 +    nzone   = 1;
 +    nat_tot = dd->nat_home;
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        cd = &comm->cd[d];
 +        for (p = 0; p < cd->np; p++)
 +        {
 +            ind   = &cd->ind[p];
 +            index = ind->index;
 +            n     = 0;
 +            for (i = 0; i < ind->nsend[nzone]; i++)
 +            {
 +                at0 = cgindex[index[i]];
 +                at1 = cgindex[index[i]+1];
 +                for (j = at0; j < at1; j++)
 +                {
 +                    buf[n] = v[j];
 +                    n++;
 +                }
 +            }
 +
 +            if (cd->bInPlace)
 +            {
 +                rbuf = v + nat_tot;
 +            }
 +            else
 +            {
 +                rbuf = &comm->vbuf2.v[0][0];
 +            }
 +            /* Send and receive the coordinates */
 +            dd_sendrecv_real(dd, d, dddirBackward,
 +                             buf,  ind->nsend[nzone+1],
 +                             rbuf, ind->nrecv[nzone+1]);
 +            if (!cd->bInPlace)
 +            {
 +                j = 0;
 +                for (zone = 0; zone < nzone; zone++)
 +                {
 +                    for (i = ind->cell2at0[zone]; i < ind->cell2at1[zone]; i++)
 +                    {
 +                        v[i] = rbuf[j];
 +                        j++;
 +                    }
 +                }
 +            }
 +            nat_tot += ind->nrecv[nzone+1];
 +        }
 +        nzone += nzone;
 +    }
 +}
 +
 +void dd_atom_sum_real(gmx_domdec_t *dd, real v[])
 +{
 +    int                    nzone, nat_tot, n, d, p, i, j, at0, at1, zone;
 +    int                   *index, *cgindex;
 +    gmx_domdec_comm_t     *comm;
 +    gmx_domdec_comm_dim_t *cd;
 +    gmx_domdec_ind_t      *ind;
 +    real                  *buf, *sbuf;
 +
 +    comm = dd->comm;
 +
 +    cgindex = dd->cgindex;
 +
 +    buf = &comm->vbuf.v[0][0];
 +
 +    n       = 0;
 +    nzone   = comm->zones.n/2;
 +    nat_tot = dd->nat_tot;
 +    for (d = dd->ndim-1; d >= 0; d--)
 +    {
 +        cd = &comm->cd[d];
 +        for (p = cd->np-1; p >= 0; p--)
 +        {
 +            ind      = &cd->ind[p];
 +            nat_tot -= ind->nrecv[nzone+1];
 +            if (cd->bInPlace)
 +            {
 +                sbuf = v + nat_tot;
 +            }
 +            else
 +            {
 +                sbuf = &comm->vbuf2.v[0][0];
 +                j    = 0;
 +                for (zone = 0; zone < nzone; zone++)
 +                {
 +                    for (i = ind->cell2at0[zone]; i < ind->cell2at1[zone]; i++)
 +                    {
 +                        sbuf[j] = v[i];
 +                        j++;
 +                    }
 +                }
 +            }
 +            /* Communicate the forces */
 +            dd_sendrecv_real(dd, d, dddirForward,
 +                             sbuf, ind->nrecv[nzone+1],
 +                             buf,  ind->nsend[nzone+1]);
 +            index = ind->index;
 +            /* Add the received forces */
 +            n = 0;
 +            for (i = 0; i < ind->nsend[nzone]; i++)
 +            {
 +                at0 = cgindex[index[i]];
 +                at1 = cgindex[index[i]+1];
 +                for (j = at0; j < at1; j++)
 +                {
 +                    v[j] += buf[n];
 +                    n++;
 +                }
 +            }
 +        }
 +        nzone /= 2;
 +    }
 +}
 +
 +static void print_ddzone(FILE *fp, int d, int i, int j, gmx_ddzone_t *zone)
 +{
 +    fprintf(fp, "zone d0 %d d1 %d d2 %d  min0 %6.3f max1 %6.3f mch0 %6.3f mch1 %6.3f p1_0 %6.3f p1_1 %6.3f\n",
 +            d, i, j,
 +            zone->min0, zone->max1,
 +            zone->mch0, zone->mch0,
 +            zone->p1_0, zone->p1_1);
 +}
 +
 +
 +#define DDZONECOMM_MAXZONE  5
 +#define DDZONECOMM_BUFSIZE  3
 +
 +static void dd_sendrecv_ddzone(const gmx_domdec_t *dd,
 +                               int ddimind, int direction,
 +                               gmx_ddzone_t *buf_s, int n_s,
 +                               gmx_ddzone_t *buf_r, int n_r)
 +{
 +#define ZBS  DDZONECOMM_BUFSIZE
 +    rvec vbuf_s[DDZONECOMM_MAXZONE*ZBS];
 +    rvec vbuf_r[DDZONECOMM_MAXZONE*ZBS];
 +    int  i;
 +
 +    for (i = 0; i < n_s; i++)
 +    {
 +        vbuf_s[i*ZBS  ][0] = buf_s[i].min0;
 +        vbuf_s[i*ZBS  ][1] = buf_s[i].max1;
 +        vbuf_s[i*ZBS  ][2] = buf_s[i].min1;
 +        vbuf_s[i*ZBS+1][0] = buf_s[i].mch0;
 +        vbuf_s[i*ZBS+1][1] = buf_s[i].mch1;
 +        vbuf_s[i*ZBS+1][2] = 0;
 +        vbuf_s[i*ZBS+2][0] = buf_s[i].p1_0;
 +        vbuf_s[i*ZBS+2][1] = buf_s[i].p1_1;
 +        vbuf_s[i*ZBS+2][2] = 0;
 +    }
 +
 +    dd_sendrecv_rvec(dd, ddimind, direction,
 +                     vbuf_s, n_s*ZBS,
 +                     vbuf_r, n_r*ZBS);
 +
 +    for (i = 0; i < n_r; i++)
 +    {
 +        buf_r[i].min0 = vbuf_r[i*ZBS  ][0];
 +        buf_r[i].max1 = vbuf_r[i*ZBS  ][1];
 +        buf_r[i].min1 = vbuf_r[i*ZBS  ][2];
 +        buf_r[i].mch0 = vbuf_r[i*ZBS+1][0];
 +        buf_r[i].mch1 = vbuf_r[i*ZBS+1][1];
 +        buf_r[i].p1_0 = vbuf_r[i*ZBS+2][0];
 +        buf_r[i].p1_1 = vbuf_r[i*ZBS+2][1];
 +    }
 +
 +#undef ZBS
 +}
 +
 +static void dd_move_cellx(gmx_domdec_t *dd, gmx_ddbox_t *ddbox,
 +                          rvec cell_ns_x0, rvec cell_ns_x1)
 +{
 +    int                d, d1, dim, dim1, pos, buf_size, i, j, k, p, npulse, npulse_min;
 +    gmx_ddzone_t      *zp;
 +    gmx_ddzone_t       buf_s[DDZONECOMM_MAXZONE];
 +    gmx_ddzone_t       buf_r[DDZONECOMM_MAXZONE];
 +    gmx_ddzone_t       buf_e[DDZONECOMM_MAXZONE];
 +    rvec               extr_s[2], extr_r[2];
 +    rvec               dh;
 +    real               dist_d, c = 0, det;
 +    gmx_domdec_comm_t *comm;
 +    gmx_bool           bPBC, bUse;
 +
 +    comm = dd->comm;
 +
 +    for (d = 1; d < dd->ndim; d++)
 +    {
 +        dim      = dd->dim[d];
 +        zp       = (d == 1) ? &comm->zone_d1[0] : &comm->zone_d2[0][0];
 +        zp->min0 = cell_ns_x0[dim];
 +        zp->max1 = cell_ns_x1[dim];
 +        zp->min1 = cell_ns_x1[dim];
 +        zp->mch0 = cell_ns_x0[dim];
 +        zp->mch1 = cell_ns_x1[dim];
 +        zp->p1_0 = cell_ns_x0[dim];
 +        zp->p1_1 = cell_ns_x1[dim];
 +    }
 +
 +    for (d = dd->ndim-2; d >= 0; d--)
 +    {
 +        dim  = dd->dim[d];
 +        bPBC = (dim < ddbox->npbcdim);
 +
 +        /* Use an rvec to store two reals */
 +        extr_s[d][0] = comm->cell_f0[d+1];
 +        extr_s[d][1] = comm->cell_f1[d+1];
 +        extr_s[d][2] = comm->cell_f1[d+1];
 +
 +        pos = 0;
 +        /* Store the extremes in the backward sending buffer,
 +         * so the get updated separately from the forward communication.
 +         */
 +        for (d1 = d; d1 < dd->ndim-1; d1++)
 +        {
 +            /* We invert the order to be able to use the same loop for buf_e */
 +            buf_s[pos].min0 = extr_s[d1][1];
 +            buf_s[pos].max1 = extr_s[d1][0];
 +            buf_s[pos].min1 = extr_s[d1][2];
 +            buf_s[pos].mch0 = 0;
 +            buf_s[pos].mch1 = 0;
 +            /* Store the cell corner of the dimension we communicate along */
 +            buf_s[pos].p1_0 = comm->cell_x0[dim];
 +            buf_s[pos].p1_1 = 0;
 +            pos++;
 +        }
 +
 +        buf_s[pos] = (dd->ndim == 2) ? comm->zone_d1[0] : comm->zone_d2[0][0];
 +        pos++;
 +
 +        if (dd->ndim == 3 && d == 0)
 +        {
 +            buf_s[pos] = comm->zone_d2[0][1];
 +            pos++;
 +            buf_s[pos] = comm->zone_d1[0];
 +            pos++;
 +        }
 +
 +        /* We only need to communicate the extremes
 +         * in the forward direction
 +         */
 +        npulse = comm->cd[d].np;
 +        if (bPBC)
 +        {
 +            /* Take the minimum to avoid double communication */
 +            npulse_min = min(npulse, dd->nc[dim]-1-npulse);
 +        }
 +        else
 +        {
 +            /* Without PBC we should really not communicate over
 +             * the boundaries, but implementing that complicates
 +             * the communication setup and therefore we simply
 +             * do all communication, but ignore some data.
 +             */
 +            npulse_min = npulse;
 +        }
 +        for (p = 0; p < npulse_min; p++)
 +        {
 +            /* Communicate the extremes forward */
 +            bUse = (bPBC || dd->ci[dim] > 0);
 +
 +            dd_sendrecv_rvec(dd, d, dddirForward,
 +                             extr_s+d, dd->ndim-d-1,
 +                             extr_r+d, dd->ndim-d-1);
 +
 +            if (bUse)
 +            {
 +                for (d1 = d; d1 < dd->ndim-1; d1++)
 +                {
 +                    extr_s[d1][0] = max(extr_s[d1][0], extr_r[d1][0]);
 +                    extr_s[d1][1] = min(extr_s[d1][1], extr_r[d1][1]);
 +                    extr_s[d1][2] = min(extr_s[d1][2], extr_r[d1][2]);
 +                }
 +            }
 +        }
 +
 +        buf_size = pos;
 +        for (p = 0; p < npulse; p++)
 +        {
 +            /* Communicate all the zone information backward */
 +            bUse = (bPBC || dd->ci[dim] < dd->nc[dim] - 1);
 +
 +            dd_sendrecv_ddzone(dd, d, dddirBackward,
 +                               buf_s, buf_size,
 +                               buf_r, buf_size);
 +
 +            clear_rvec(dh);
 +            if (p > 0)
 +            {
 +                for (d1 = d+1; d1 < dd->ndim; d1++)
 +                {
 +                    /* Determine the decrease of maximum required
 +                     * communication height along d1 due to the distance along d,
 +                     * this avoids a lot of useless atom communication.
 +                     */
 +                    dist_d = comm->cell_x1[dim] - buf_r[0].p1_0;
 +
 +                    if (ddbox->tric_dir[dim])
 +                    {
 +                        /* c is the off-diagonal coupling between the cell planes
 +                         * along directions d and d1.
 +                         */
 +                        c = ddbox->v[dim][dd->dim[d1]][dim];
 +                    }
 +                    else
 +                    {
 +                        c = 0;
 +                    }
 +                    det = (1 + c*c)*comm->cutoff*comm->cutoff - dist_d*dist_d;
 +                    if (det > 0)
 +                    {
 +                        dh[d1] = comm->cutoff - (c*dist_d + sqrt(det))/(1 + c*c);
 +                    }
 +                    else
 +                    {
 +                        /* A negative value signals out of range */
 +                        dh[d1] = -1;
 +                    }
 +                }
 +            }
 +
 +            /* Accumulate the extremes over all pulses */
 +            for (i = 0; i < buf_size; i++)
 +            {
 +                if (p == 0)
 +                {
 +                    buf_e[i] = buf_r[i];
 +                }
 +                else
 +                {
 +                    if (bUse)
 +                    {
 +                        buf_e[i].min0 = min(buf_e[i].min0, buf_r[i].min0);
 +                        buf_e[i].max1 = max(buf_e[i].max1, buf_r[i].max1);
 +                        buf_e[i].min1 = min(buf_e[i].min1, buf_r[i].min1);
 +                    }
 +
 +                    if (dd->ndim == 3 && d == 0 && i == buf_size - 1)
 +                    {
 +                        d1 = 1;
 +                    }
 +                    else
 +                    {
 +                        d1 = d + 1;
 +                    }
 +                    if (bUse && dh[d1] >= 0)
 +                    {
 +                        buf_e[i].mch0 = max(buf_e[i].mch0, buf_r[i].mch0-dh[d1]);
 +                        buf_e[i].mch1 = max(buf_e[i].mch1, buf_r[i].mch1-dh[d1]);
 +                    }
 +                }
 +                /* Copy the received buffer to the send buffer,
 +                 * to pass the data through with the next pulse.
 +                 */
 +                buf_s[i] = buf_r[i];
 +            }
 +            if (((bPBC || dd->ci[dim]+npulse < dd->nc[dim]) && p == npulse-1) ||
 +                (!bPBC && dd->ci[dim]+1+p == dd->nc[dim]-1))
 +            {
 +                /* Store the extremes */
 +                pos = 0;
 +
 +                for (d1 = d; d1 < dd->ndim-1; d1++)
 +                {
 +                    extr_s[d1][1] = min(extr_s[d1][1], buf_e[pos].min0);
 +                    extr_s[d1][0] = max(extr_s[d1][0], buf_e[pos].max1);
 +                    extr_s[d1][2] = min(extr_s[d1][2], buf_e[pos].min1);
 +                    pos++;
 +                }
 +
 +                if (d == 1 || (d == 0 && dd->ndim == 3))
 +                {
 +                    for (i = d; i < 2; i++)
 +                    {
 +                        comm->zone_d2[1-d][i] = buf_e[pos];
 +                        pos++;
 +                    }
 +                }
 +                if (d == 0)
 +                {
 +                    comm->zone_d1[1] = buf_e[pos];
 +                    pos++;
 +                }
 +            }
 +        }
 +    }
 +
 +    if (dd->ndim >= 2)
 +    {
 +        dim = dd->dim[1];
 +        for (i = 0; i < 2; i++)
 +        {
 +            if (debug)
 +            {
 +                print_ddzone(debug, 1, i, 0, &comm->zone_d1[i]);
 +            }
 +            cell_ns_x0[dim] = min(cell_ns_x0[dim], comm->zone_d1[i].min0);
 +            cell_ns_x1[dim] = max(cell_ns_x1[dim], comm->zone_d1[i].max1);
 +        }
 +    }
 +    if (dd->ndim >= 3)
 +    {
 +        dim = dd->dim[2];
 +        for (i = 0; i < 2; i++)
 +        {
 +            for (j = 0; j < 2; j++)
 +            {
 +                if (debug)
 +                {
 +                    print_ddzone(debug, 2, i, j, &comm->zone_d2[i][j]);
 +                }
 +                cell_ns_x0[dim] = min(cell_ns_x0[dim], comm->zone_d2[i][j].min0);
 +                cell_ns_x1[dim] = max(cell_ns_x1[dim], comm->zone_d2[i][j].max1);
 +            }
 +        }
 +    }
 +    for (d = 1; d < dd->ndim; d++)
 +    {
 +        comm->cell_f_max0[d] = extr_s[d-1][0];
 +        comm->cell_f_min1[d] = extr_s[d-1][1];
 +        if (debug)
 +        {
 +            fprintf(debug, "Cell fraction d %d, max0 %f, min1 %f\n",
 +                    d, comm->cell_f_max0[d], comm->cell_f_min1[d]);
 +        }
 +    }
 +}
 +
 +static void dd_collect_cg(gmx_domdec_t *dd,
 +                          t_state      *state_local)
 +{
 +    gmx_domdec_master_t *ma = NULL;
 +    int                  buf2[2], *ibuf, i, ncg_home = 0, *cg = NULL, nat_home = 0;
 +    t_block             *cgs_gl;
 +
 +    if (state_local->ddp_count == dd->comm->master_cg_ddp_count)
 +    {
 +        /* The master has the correct distribution */
 +        return;
 +    }
 +
 +    if (state_local->ddp_count == dd->ddp_count)
 +    {
 +        ncg_home = dd->ncg_home;
 +        cg       = dd->index_gl;
 +        nat_home = dd->nat_home;
 +    }
 +    else if (state_local->ddp_count_cg_gl == state_local->ddp_count)
 +    {
 +        cgs_gl = &dd->comm->cgs_gl;
 +
 +        ncg_home = state_local->ncg_gl;
 +        cg       = state_local->cg_gl;
 +        nat_home = 0;
 +        for (i = 0; i < ncg_home; i++)
 +        {
 +            nat_home += cgs_gl->index[cg[i]+1] - cgs_gl->index[cg[i]];
 +        }
 +    }
 +    else
 +    {
 +        gmx_incons("Attempted to collect a vector for a state for which the charge group distribution is unknown");
 +    }
 +
 +    buf2[0] = dd->ncg_home;
 +    buf2[1] = dd->nat_home;
 +    if (DDMASTER(dd))
 +    {
 +        ma   = dd->ma;
 +        ibuf = ma->ibuf;
 +    }
 +    else
 +    {
 +        ibuf = NULL;
 +    }
 +    /* Collect the charge group and atom counts on the master */
 +    dd_gather(dd, 2*sizeof(int), buf2, ibuf);
 +
 +    if (DDMASTER(dd))
 +    {
 +        ma->index[0] = 0;
 +        for (i = 0; i < dd->nnodes; i++)
 +        {
 +            ma->ncg[i]     = ma->ibuf[2*i];
 +            ma->nat[i]     = ma->ibuf[2*i+1];
 +            ma->index[i+1] = ma->index[i] + ma->ncg[i];
 +
 +        }
 +        /* Make byte counts and indices */
 +        for (i = 0; i < dd->nnodes; i++)
 +        {
 +            ma->ibuf[i]            = ma->ncg[i]*sizeof(int);
 +            ma->ibuf[dd->nnodes+i] = ma->index[i]*sizeof(int);
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "Initial charge group distribution: ");
 +            for (i = 0; i < dd->nnodes; i++)
 +            {
 +                fprintf(debug, " %d", ma->ncg[i]);
 +            }
 +            fprintf(debug, "\n");
 +        }
 +    }
 +
 +    /* Collect the charge group indices on the master */
 +    dd_gatherv(dd,
 +               dd->ncg_home*sizeof(int), dd->index_gl,
 +               DDMASTER(dd) ? ma->ibuf : NULL,
 +               DDMASTER(dd) ? ma->ibuf+dd->nnodes : NULL,
 +               DDMASTER(dd) ? ma->cg : NULL);
 +
 +    dd->comm->master_cg_ddp_count = state_local->ddp_count;
 +}
 +
 +static void dd_collect_vec_sendrecv(gmx_domdec_t *dd,
 +                                    rvec *lv, rvec *v)
 +{
 +    gmx_domdec_master_t *ma;
 +    int                  n, i, c, a, nalloc = 0;
 +    rvec                *buf = NULL;
 +    t_block             *cgs_gl;
 +
 +    ma = dd->ma;
 +
 +    if (!DDMASTER(dd))
 +    {
 +#ifdef GMX_MPI
 +        MPI_Send(lv, dd->nat_home*sizeof(rvec), MPI_BYTE, DDMASTERRANK(dd),
 +                 dd->rank, dd->mpi_comm_all);
 +#endif
 +    }
 +    else
 +    {
 +        /* Copy the master coordinates to the global array */
 +        cgs_gl = &dd->comm->cgs_gl;
 +
 +        n = DDMASTERRANK(dd);
 +        a = 0;
 +        for (i = ma->index[n]; i < ma->index[n+1]; i++)
 +        {
 +            for (c = cgs_gl->index[ma->cg[i]]; c < cgs_gl->index[ma->cg[i]+1]; c++)
 +            {
 +                copy_rvec(lv[a++], v[c]);
 +            }
 +        }
 +
 +        for (n = 0; n < dd->nnodes; n++)
 +        {
 +            if (n != dd->rank)
 +            {
 +                if (ma->nat[n] > nalloc)
 +                {
 +                    nalloc = over_alloc_dd(ma->nat[n]);
 +                    srenew(buf, nalloc);
 +                }
 +#ifdef GMX_MPI
 +                MPI_Recv(buf, ma->nat[n]*sizeof(rvec), MPI_BYTE, DDRANK(dd, n),
 +                         n, dd->mpi_comm_all, MPI_STATUS_IGNORE);
 +#endif
 +                a = 0;
 +                for (i = ma->index[n]; i < ma->index[n+1]; i++)
 +                {
 +                    for (c = cgs_gl->index[ma->cg[i]]; c < cgs_gl->index[ma->cg[i]+1]; c++)
 +                    {
 +                        copy_rvec(buf[a++], v[c]);
 +                    }
 +                }
 +            }
 +        }
 +        sfree(buf);
 +    }
 +}
 +
 +static void get_commbuffer_counts(gmx_domdec_t *dd,
 +                                  int **counts, int **disps)
 +{
 +    gmx_domdec_master_t *ma;
 +    int                  n;
 +
 +    ma = dd->ma;
 +
 +    /* Make the rvec count and displacment arrays */
 +    *counts  = ma->ibuf;
 +    *disps   = ma->ibuf + dd->nnodes;
 +    for (n = 0; n < dd->nnodes; n++)
 +    {
 +        (*counts)[n] = ma->nat[n]*sizeof(rvec);
 +        (*disps)[n]  = (n == 0 ? 0 : (*disps)[n-1] + (*counts)[n-1]);
 +    }
 +}
 +
 +static void dd_collect_vec_gatherv(gmx_domdec_t *dd,
 +                                   rvec *lv, rvec *v)
 +{
 +    gmx_domdec_master_t *ma;
 +    int                 *rcounts = NULL, *disps = NULL;
 +    int                  n, i, c, a;
 +    rvec                *buf = NULL;
 +    t_block             *cgs_gl;
 +
 +    ma = dd->ma;
 +
 +    if (DDMASTER(dd))
 +    {
 +        get_commbuffer_counts(dd, &rcounts, &disps);
 +
 +        buf = ma->vbuf;
 +    }
 +
 +    dd_gatherv(dd, dd->nat_home*sizeof(rvec), lv, rcounts, disps, buf);
 +
 +    if (DDMASTER(dd))
 +    {
 +        cgs_gl = &dd->comm->cgs_gl;
 +
 +        a = 0;
 +        for (n = 0; n < dd->nnodes; n++)
 +        {
 +            for (i = ma->index[n]; i < ma->index[n+1]; i++)
 +            {
 +                for (c = cgs_gl->index[ma->cg[i]]; c < cgs_gl->index[ma->cg[i]+1]; c++)
 +                {
 +                    copy_rvec(buf[a++], v[c]);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +void dd_collect_vec(gmx_domdec_t *dd,
 +                    t_state *state_local, rvec *lv, rvec *v)
 +{
 +    gmx_domdec_master_t *ma;
 +    int                  n, i, c, a, nalloc = 0;
 +    rvec                *buf = NULL;
 +
 +    dd_collect_cg(dd, state_local);
 +
 +    if (dd->nnodes <= GMX_DD_NNODES_SENDRECV)
 +    {
 +        dd_collect_vec_sendrecv(dd, lv, v);
 +    }
 +    else
 +    {
 +        dd_collect_vec_gatherv(dd, lv, v);
 +    }
 +}
 +
 +
 +void dd_collect_state(gmx_domdec_t *dd,
 +                      t_state *state_local, t_state *state)
 +{
 +    int est, i, j, nh;
 +
 +    nh = state->nhchainlength;
 +
 +    if (DDMASTER(dd))
 +    {
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            state->lambda[i] = state_local->lambda[i];
 +        }
 +        state->fep_state = state_local->fep_state;
 +        state->veta      = state_local->veta;
 +        state->vol0      = state_local->vol0;
 +        copy_mat(state_local->box, state->box);
 +        copy_mat(state_local->boxv, state->boxv);
 +        copy_mat(state_local->svir_prev, state->svir_prev);
 +        copy_mat(state_local->fvir_prev, state->fvir_prev);
 +        copy_mat(state_local->pres_prev, state->pres_prev);
 +
 +
 +        for (i = 0; i < state_local->ngtc; i++)
 +        {
 +            for (j = 0; j < nh; j++)
 +            {
 +                state->nosehoover_xi[i*nh+j]        = state_local->nosehoover_xi[i*nh+j];
 +                state->nosehoover_vxi[i*nh+j]       = state_local->nosehoover_vxi[i*nh+j];
 +            }
 +            state->therm_integral[i] = state_local->therm_integral[i];
 +        }
 +        for (i = 0; i < state_local->nnhpres; i++)
 +        {
 +            for (j = 0; j < nh; j++)
 +            {
 +                state->nhpres_xi[i*nh+j]        = state_local->nhpres_xi[i*nh+j];
 +                state->nhpres_vxi[i*nh+j]       = state_local->nhpres_vxi[i*nh+j];
 +            }
 +        }
 +    }
 +    for (est = 0; est < estNR; est++)
 +    {
 +        if (EST_DISTR(est) && (state_local->flags & (1<<est)))
 +        {
 +            switch (est)
 +            {
 +                case estX:
 +                    dd_collect_vec(dd, state_local, state_local->x, state->x);
 +                    break;
 +                case estV:
 +                    dd_collect_vec(dd, state_local, state_local->v, state->v);
 +                    break;
 +                case estSDX:
 +                    dd_collect_vec(dd, state_local, state_local->sd_X, state->sd_X);
 +                    break;
 +                case estCGP:
 +                    dd_collect_vec(dd, state_local, state_local->cg_p, state->cg_p);
 +                    break;
 +                case estLD_RNG:
 +                    if (state->nrngi == 1)
 +                    {
 +                        if (DDMASTER(dd))
 +                        {
 +                            for (i = 0; i < state_local->nrng; i++)
 +                            {
 +                                state->ld_rng[i] = state_local->ld_rng[i];
 +                            }
 +                        }
 +                    }
 +                    else
 +                    {
 +                        dd_gather(dd, state_local->nrng*sizeof(state->ld_rng[0]),
 +                                  state_local->ld_rng, state->ld_rng);
 +                    }
 +                    break;
 +                case estLD_RNGI:
 +                    if (state->nrngi == 1)
 +                    {
 +                        if (DDMASTER(dd))
 +                        {
 +                            state->ld_rngi[0] = state_local->ld_rngi[0];
 +                        }
 +                    }
 +                    else
 +                    {
 +                        dd_gather(dd, sizeof(state->ld_rngi[0]),
 +                                  state_local->ld_rngi, state->ld_rngi);
 +                    }
 +                    break;
 +                case estDISRE_INITF:
 +                case estDISRE_RM3TAV:
 +                case estORIRE_INITF:
 +                case estORIRE_DTAV:
 +                    break;
 +                default:
 +                    gmx_incons("Unknown state entry encountered in dd_collect_state");
 +            }
 +        }
 +    }
 +}
 +
 +static void dd_realloc_state(t_state *state, rvec **f, int nalloc)
 +{
 +    int est;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Reallocating state: currently %d, required %d, allocating %d\n", state->nalloc, nalloc, over_alloc_dd(nalloc));
 +    }
 +
 +    state->nalloc = over_alloc_dd(nalloc);
 +
 +    for (est = 0; est < estNR; est++)
 +    {
 +        if (EST_DISTR(est) && (state->flags & (1<<est)))
 +        {
 +            switch (est)
 +            {
 +                case estX:
 +                    srenew(state->x, state->nalloc);
 +                    break;
 +                case estV:
 +                    srenew(state->v, state->nalloc);
 +                    break;
 +                case estSDX:
 +                    srenew(state->sd_X, state->nalloc);
 +                    break;
 +                case estCGP:
 +                    srenew(state->cg_p, state->nalloc);
 +                    break;
 +                case estLD_RNG:
 +                case estLD_RNGI:
 +                case estDISRE_INITF:
 +                case estDISRE_RM3TAV:
 +                case estORIRE_INITF:
 +                case estORIRE_DTAV:
 +                    /* No reallocation required */
 +                    break;
 +                default:
 +                    gmx_incons("Unknown state entry encountered in dd_realloc_state");
 +            }
 +        }
 +    }
 +
 +    if (f != NULL)
 +    {
 +        srenew(*f, state->nalloc);
 +    }
 +}
 +
 +static void dd_check_alloc_ncg(t_forcerec *fr, t_state *state, rvec **f,
 +                               int nalloc)
 +{
 +    if (nalloc > fr->cg_nalloc)
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "Reallocating forcerec: currently %d, required %d, allocating %d\n", fr->cg_nalloc, nalloc, over_alloc_dd(nalloc));
 +        }
 +        fr->cg_nalloc = over_alloc_dd(nalloc);
 +        srenew(fr->cginfo, fr->cg_nalloc);
 +        if (fr->cutoff_scheme == ecutsGROUP)
 +        {
 +            srenew(fr->cg_cm, fr->cg_nalloc);
 +        }
 +    }
 +    if (fr->cutoff_scheme == ecutsVERLET && nalloc > state->nalloc)
 +    {
 +        /* We don't use charge groups, we use x in state to set up
 +         * the atom communication.
 +         */
 +        dd_realloc_state(state, f, nalloc);
 +    }
 +}
 +
 +static void dd_distribute_vec_sendrecv(gmx_domdec_t *dd, t_block *cgs,
 +                                       rvec *v, rvec *lv)
 +{
 +    gmx_domdec_master_t *ma;
 +    int                  n, i, c, a, nalloc = 0;
 +    rvec                *buf = NULL;
 +
 +    if (DDMASTER(dd))
 +    {
 +        ma  = dd->ma;
 +
 +        for (n = 0; n < dd->nnodes; n++)
 +        {
 +            if (n != dd->rank)
 +            {
 +                if (ma->nat[n] > nalloc)
 +                {
 +                    nalloc = over_alloc_dd(ma->nat[n]);
 +                    srenew(buf, nalloc);
 +                }
 +                /* Use lv as a temporary buffer */
 +                a = 0;
 +                for (i = ma->index[n]; i < ma->index[n+1]; i++)
 +                {
 +                    for (c = cgs->index[ma->cg[i]]; c < cgs->index[ma->cg[i]+1]; c++)
 +                    {
 +                        copy_rvec(v[c], buf[a++]);
 +                    }
 +                }
 +                if (a != ma->nat[n])
 +                {
 +                    gmx_fatal(FARGS, "Internal error a (%d) != nat (%d)",
 +                              a, ma->nat[n]);
 +                }
 +
 +#ifdef GMX_MPI
 +                MPI_Send(buf, ma->nat[n]*sizeof(rvec), MPI_BYTE,
 +                         DDRANK(dd, n), n, dd->mpi_comm_all);
 +#endif
 +            }
 +        }
 +        sfree(buf);
 +        n = DDMASTERRANK(dd);
 +        a = 0;
 +        for (i = ma->index[n]; i < ma->index[n+1]; i++)
 +        {
 +            for (c = cgs->index[ma->cg[i]]; c < cgs->index[ma->cg[i]+1]; c++)
 +            {
 +                copy_rvec(v[c], lv[a++]);
 +            }
 +        }
 +    }
 +    else
 +    {
 +#ifdef GMX_MPI
 +        MPI_Recv(lv, dd->nat_home*sizeof(rvec), MPI_BYTE, DDMASTERRANK(dd),
 +                 MPI_ANY_TAG, dd->mpi_comm_all, MPI_STATUS_IGNORE);
 +#endif
 +    }
 +}
 +
 +static void dd_distribute_vec_scatterv(gmx_domdec_t *dd, t_block *cgs,
 +                                       rvec *v, rvec *lv)
 +{
 +    gmx_domdec_master_t *ma;
 +    int                 *scounts = NULL, *disps = NULL;
 +    int                  n, i, c, a, nalloc = 0;
 +    rvec                *buf = NULL;
 +
 +    if (DDMASTER(dd))
 +    {
 +        ma  = dd->ma;
 +
 +        get_commbuffer_counts(dd, &scounts, &disps);
 +
 +        buf = ma->vbuf;
 +        a   = 0;
 +        for (n = 0; n < dd->nnodes; n++)
 +        {
 +            for (i = ma->index[n]; i < ma->index[n+1]; i++)
 +            {
 +                for (c = cgs->index[ma->cg[i]]; c < cgs->index[ma->cg[i]+1]; c++)
 +                {
 +                    copy_rvec(v[c], buf[a++]);
 +                }
 +            }
 +        }
 +    }
 +
 +    dd_scatterv(dd, scounts, disps, buf, dd->nat_home*sizeof(rvec), lv);
 +}
 +
 +static void dd_distribute_vec(gmx_domdec_t *dd, t_block *cgs, rvec *v, rvec *lv)
 +{
 +    if (dd->nnodes <= GMX_DD_NNODES_SENDRECV)
 +    {
 +        dd_distribute_vec_sendrecv(dd, cgs, v, lv);
 +    }
 +    else
 +    {
 +        dd_distribute_vec_scatterv(dd, cgs, v, lv);
 +    }
 +}
 +
 +static void dd_distribute_state(gmx_domdec_t *dd, t_block *cgs,
 +                                t_state *state, t_state *state_local,
 +                                rvec **f)
 +{
 +    int  i, j, nh;
 +
 +    nh = state->nhchainlength;
 +
 +    if (DDMASTER(dd))
 +    {
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            state_local->lambda[i] = state->lambda[i];
 +        }
 +        state_local->fep_state = state->fep_state;
 +        state_local->veta      = state->veta;
 +        state_local->vol0      = state->vol0;
 +        copy_mat(state->box, state_local->box);
 +        copy_mat(state->box_rel, state_local->box_rel);
 +        copy_mat(state->boxv, state_local->boxv);
 +        copy_mat(state->svir_prev, state_local->svir_prev);
 +        copy_mat(state->fvir_prev, state_local->fvir_prev);
 +        for (i = 0; i < state_local->ngtc; i++)
 +        {
 +            for (j = 0; j < nh; j++)
 +            {
 +                state_local->nosehoover_xi[i*nh+j]        = state->nosehoover_xi[i*nh+j];
 +                state_local->nosehoover_vxi[i*nh+j]       = state->nosehoover_vxi[i*nh+j];
 +            }
 +            state_local->therm_integral[i] = state->therm_integral[i];
 +        }
 +        for (i = 0; i < state_local->nnhpres; i++)
 +        {
 +            for (j = 0; j < nh; j++)
 +            {
 +                state_local->nhpres_xi[i*nh+j]        = state->nhpres_xi[i*nh+j];
 +                state_local->nhpres_vxi[i*nh+j]       = state->nhpres_vxi[i*nh+j];
 +            }
 +        }
 +    }
 +    dd_bcast(dd, ((efptNR)*sizeof(real)), state_local->lambda);
 +    dd_bcast(dd, sizeof(int), &state_local->fep_state);
 +    dd_bcast(dd, sizeof(real), &state_local->veta);
 +    dd_bcast(dd, sizeof(real), &state_local->vol0);
 +    dd_bcast(dd, sizeof(state_local->box), state_local->box);
 +    dd_bcast(dd, sizeof(state_local->box_rel), state_local->box_rel);
 +    dd_bcast(dd, sizeof(state_local->boxv), state_local->boxv);
 +    dd_bcast(dd, sizeof(state_local->svir_prev), state_local->svir_prev);
 +    dd_bcast(dd, sizeof(state_local->fvir_prev), state_local->fvir_prev);
 +    dd_bcast(dd, ((state_local->ngtc*nh)*sizeof(double)), state_local->nosehoover_xi);
 +    dd_bcast(dd, ((state_local->ngtc*nh)*sizeof(double)), state_local->nosehoover_vxi);
 +    dd_bcast(dd, state_local->ngtc*sizeof(double), state_local->therm_integral);
 +    dd_bcast(dd, ((state_local->nnhpres*nh)*sizeof(double)), state_local->nhpres_xi);
 +    dd_bcast(dd, ((state_local->nnhpres*nh)*sizeof(double)), state_local->nhpres_vxi);
 +
 +    if (dd->nat_home > state_local->nalloc)
 +    {
 +        dd_realloc_state(state_local, f, dd->nat_home);
 +    }
 +    for (i = 0; i < estNR; i++)
 +    {
 +        if (EST_DISTR(i) && (state_local->flags & (1<<i)))
 +        {
 +            switch (i)
 +            {
 +                case estX:
 +                    dd_distribute_vec(dd, cgs, state->x, state_local->x);
 +                    break;
 +                case estV:
 +                    dd_distribute_vec(dd, cgs, state->v, state_local->v);
 +                    break;
 +                case estSDX:
 +                    dd_distribute_vec(dd, cgs, state->sd_X, state_local->sd_X);
 +                    break;
 +                case estCGP:
 +                    dd_distribute_vec(dd, cgs, state->cg_p, state_local->cg_p);
 +                    break;
 +                case estLD_RNG:
 +                    if (state->nrngi == 1)
 +                    {
 +                        dd_bcastc(dd,
 +                                  state_local->nrng*sizeof(state_local->ld_rng[0]),
 +                                  state->ld_rng, state_local->ld_rng);
 +                    }
 +                    else
 +                    {
 +                        dd_scatter(dd,
 +                                   state_local->nrng*sizeof(state_local->ld_rng[0]),
 +                                   state->ld_rng, state_local->ld_rng);
 +                    }
 +                    break;
 +                case estLD_RNGI:
 +                    if (state->nrngi == 1)
 +                    {
 +                        dd_bcastc(dd, sizeof(state_local->ld_rngi[0]),
 +                                  state->ld_rngi, state_local->ld_rngi);
 +                    }
 +                    else
 +                    {
 +                        dd_scatter(dd, sizeof(state_local->ld_rngi[0]),
 +                                   state->ld_rngi, state_local->ld_rngi);
 +                    }
 +                    break;
 +                case estDISRE_INITF:
 +                case estDISRE_RM3TAV:
 +                case estORIRE_INITF:
 +                case estORIRE_DTAV:
 +                    /* Not implemented yet */
 +                    break;
 +                default:
 +                    gmx_incons("Unknown state entry encountered in dd_distribute_state");
 +            }
 +        }
 +    }
 +}
 +
 +static char dim2char(int dim)
 +{
 +    char c = '?';
 +
 +    switch (dim)
 +    {
 +        case XX: c = 'X'; break;
 +        case YY: c = 'Y'; break;
 +        case ZZ: c = 'Z'; break;
 +        default: gmx_fatal(FARGS, "Unknown dim %d", dim);
 +    }
 +
 +    return c;
 +}
 +
 +static void write_dd_grid_pdb(const char *fn, gmx_large_int_t step,
 +                              gmx_domdec_t *dd, matrix box, gmx_ddbox_t *ddbox)
 +{
 +    rvec   grid_s[2], *grid_r = NULL, cx, r;
 +    char   fname[STRLEN], format[STRLEN], buf[22];
 +    FILE  *out;
 +    int    a, i, d, z, y, x;
 +    matrix tric;
 +    real   vol;
 +
 +    copy_rvec(dd->comm->cell_x0, grid_s[0]);
 +    copy_rvec(dd->comm->cell_x1, grid_s[1]);
 +
 +    if (DDMASTER(dd))
 +    {
 +        snew(grid_r, 2*dd->nnodes);
 +    }
 +
 +    dd_gather(dd, 2*sizeof(rvec), grid_s[0], DDMASTER(dd) ? grid_r[0] : NULL);
 +
 +    if (DDMASTER(dd))
 +    {
 +        for (d = 0; d < DIM; d++)
 +        {
 +            for (i = 0; i < DIM; i++)
 +            {
 +                if (d == i)
 +                {
 +                    tric[d][i] = 1;
 +                }
 +                else
 +                {
 +                    if (d < ddbox->npbcdim && dd->nc[d] > 1)
 +                    {
 +                        tric[d][i] = box[i][d]/box[i][i];
 +                    }
 +                    else
 +                    {
 +                        tric[d][i] = 0;
 +                    }
 +                }
 +            }
 +        }
 +        sprintf(fname, "%s_%s.pdb", fn, gmx_step_str(step, buf));
 +        sprintf(format, "%s%s\n", get_pdbformat(), "%6.2f%6.2f");
 +        out = gmx_fio_fopen(fname, "w");
 +        gmx_write_pdb_box(out, dd->bScrewPBC ? epbcSCREW : epbcXYZ, box);
 +        a = 1;
 +        for (i = 0; i < dd->nnodes; i++)
 +        {
 +            vol = dd->nnodes/(box[XX][XX]*box[YY][YY]*box[ZZ][ZZ]);
 +            for (d = 0; d < DIM; d++)
 +            {
 +                vol *= grid_r[i*2+1][d] - grid_r[i*2][d];
 +            }
 +            for (z = 0; z < 2; z++)
 +            {
 +                for (y = 0; y < 2; y++)
 +                {
 +                    for (x = 0; x < 2; x++)
 +                    {
 +                        cx[XX] = grid_r[i*2+x][XX];
 +                        cx[YY] = grid_r[i*2+y][YY];
 +                        cx[ZZ] = grid_r[i*2+z][ZZ];
 +                        mvmul(tric, cx, r);
 +                        fprintf(out, format, "ATOM", a++, "CA", "GLY", ' ', 1+i,
 +                                10*r[XX], 10*r[YY], 10*r[ZZ], 1.0, vol);
 +                    }
 +                }
 +            }
 +            for (d = 0; d < DIM; d++)
 +            {
 +                for (x = 0; x < 4; x++)
 +                {
 +                    switch (d)
 +                    {
 +                        case 0: y = 1 + i*8 + 2*x; break;
 +                        case 1: y = 1 + i*8 + 2*x - (x % 2); break;
 +                        case 2: y = 1 + i*8 + x; break;
 +                    }
 +                    fprintf(out, "%6s%5d%5d\n", "CONECT", y, y+(1<<d));
 +                }
 +            }
 +        }
 +        gmx_fio_fclose(out);
 +        sfree(grid_r);
 +    }
 +}
 +
 +void write_dd_pdb(const char *fn, gmx_large_int_t step, const char *title,
 +                  gmx_mtop_t *mtop, t_commrec *cr,
 +                  int natoms, rvec x[], matrix box)
 +{
 +    char          fname[STRLEN], format[STRLEN], format4[STRLEN], buf[22];
 +    FILE         *out;
 +    int           i, ii, resnr, c;
 +    char         *atomname, *resname;
 +    real          b;
 +    gmx_domdec_t *dd;
 +
 +    dd = cr->dd;
 +    if (natoms == -1)
 +    {
 +        natoms = dd->comm->nat[ddnatVSITE];
 +    }
 +
 +    sprintf(fname, "%s_%s_n%d.pdb", fn, gmx_step_str(step, buf), cr->sim_nodeid);
 +
 +    sprintf(format, "%s%s\n", get_pdbformat(), "%6.2f%6.2f");
 +    sprintf(format4, "%s%s\n", get_pdbformat4(), "%6.2f%6.2f");
 +
 +    out = gmx_fio_fopen(fname, "w");
 +
 +    fprintf(out, "TITLE     %s\n", title);
 +    gmx_write_pdb_box(out, dd->bScrewPBC ? epbcSCREW : epbcXYZ, box);
 +    for (i = 0; i < natoms; i++)
 +    {
 +        ii = dd->gatindex[i];
 +        gmx_mtop_atominfo_global(mtop, ii, &atomname, &resnr, &resname);
 +        if (i < dd->comm->nat[ddnatZONE])
 +        {
 +            c = 0;
 +            while (i >= dd->cgindex[dd->comm->zones.cg_range[c+1]])
 +            {
 +                c++;
 +            }
 +            b = c;
 +        }
 +        else if (i < dd->comm->nat[ddnatVSITE])
 +        {
 +            b = dd->comm->zones.n;
 +        }
 +        else
 +        {
 +            b = dd->comm->zones.n + 1;
 +        }
 +        fprintf(out, strlen(atomname) < 4 ? format : format4,
 +                "ATOM", (ii+1)%100000,
 +                atomname, resname, ' ', resnr%10000, ' ',
 +                10*x[i][XX], 10*x[i][YY], 10*x[i][ZZ], 1.0, b);
 +    }
 +    fprintf(out, "TER\n");
 +
 +    gmx_fio_fclose(out);
 +}
 +
 +real dd_cutoff_mbody(gmx_domdec_t *dd)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                di;
 +    real               r;
 +
 +    comm = dd->comm;
 +
 +    r = -1;
 +    if (comm->bInterCGBondeds)
 +    {
 +        if (comm->cutoff_mbody > 0)
 +        {
 +            r = comm->cutoff_mbody;
 +        }
 +        else
 +        {
 +            /* cutoff_mbody=0 means we do not have DLB */
 +            r = comm->cellsize_min[dd->dim[0]];
 +            for (di = 1; di < dd->ndim; di++)
 +            {
 +                r = min(r, comm->cellsize_min[dd->dim[di]]);
 +            }
 +            if (comm->bBondComm)
 +            {
 +                r = max(r, comm->cutoff_mbody);
 +            }
 +            else
 +            {
 +                r = min(r, comm->cutoff);
 +            }
 +        }
 +    }
 +
 +    return r;
 +}
 +
 +real dd_cutoff_twobody(gmx_domdec_t *dd)
 +{
 +    real r_mb;
 +
 +    r_mb = dd_cutoff_mbody(dd);
 +
 +    return max(dd->comm->cutoff, r_mb);
 +}
 +
 +
 +static void dd_cart_coord2pmecoord(gmx_domdec_t *dd, ivec coord, ivec coord_pme)
 +{
 +    int nc, ntot;
 +
 +    nc   = dd->nc[dd->comm->cartpmedim];
 +    ntot = dd->comm->ntot[dd->comm->cartpmedim];
 +    copy_ivec(coord, coord_pme);
 +    coord_pme[dd->comm->cartpmedim] =
 +        nc + (coord[dd->comm->cartpmedim]*(ntot - nc) + (ntot - nc)/2)/nc;
 +}
 +
 +static int low_ddindex2pmeindex(int ndd, int npme, int ddindex)
 +{
 +    /* Here we assign a PME node to communicate with this DD node
 +     * by assuming that the major index of both is x.
 +     * We add cr->npmenodes/2 to obtain an even distribution.
 +     */
 +    return (ddindex*npme + npme/2)/ndd;
 +}
 +
 +static int ddindex2pmeindex(const gmx_domdec_t *dd, int ddindex)
 +{
 +    return low_ddindex2pmeindex(dd->nnodes, dd->comm->npmenodes, ddindex);
 +}
 +
 +static int cr_ddindex2pmeindex(const t_commrec *cr, int ddindex)
 +{
 +    return low_ddindex2pmeindex(cr->dd->nnodes, cr->npmenodes, ddindex);
 +}
 +
 +static int *dd_pmenodes(t_commrec *cr)
 +{
 +    int *pmenodes;
 +    int  n, i, p0, p1;
 +
 +    snew(pmenodes, cr->npmenodes);
 +    n = 0;
 +    for (i = 0; i < cr->dd->nnodes; i++)
 +    {
 +        p0 = cr_ddindex2pmeindex(cr, i);
 +        p1 = cr_ddindex2pmeindex(cr, i+1);
 +        if (i+1 == cr->dd->nnodes || p1 > p0)
 +        {
 +            if (debug)
 +            {
 +                fprintf(debug, "pmenode[%d] = %d\n", n, i+1+n);
 +            }
 +            pmenodes[n] = i + 1 + n;
 +            n++;
 +        }
 +    }
 +
 +    return pmenodes;
 +}
 +
 +static int gmx_ddcoord2pmeindex(t_commrec *cr, int x, int y, int z)
 +{
 +    gmx_domdec_t *dd;
 +    ivec          coords, coords_pme, nc;
 +    int           slab;
 +
 +    dd = cr->dd;
 +    /*
 +       if (dd->comm->bCartesian) {
 +       gmx_ddindex2xyz(dd->nc,ddindex,coords);
 +       dd_coords2pmecoords(dd,coords,coords_pme);
 +       copy_ivec(dd->ntot,nc);
 +       nc[dd->cartpmedim]         -= dd->nc[dd->cartpmedim];
 +       coords_pme[dd->cartpmedim] -= dd->nc[dd->cartpmedim];
 +
 +       slab = (coords_pme[XX]*nc[YY] + coords_pme[YY])*nc[ZZ] + coords_pme[ZZ];
 +       } else {
 +       slab = (ddindex*cr->npmenodes + cr->npmenodes/2)/dd->nnodes;
 +       }
 +     */
 +    coords[XX] = x;
 +    coords[YY] = y;
 +    coords[ZZ] = z;
 +    slab       = ddindex2pmeindex(dd, dd_index(dd->nc, coords));
 +
 +    return slab;
 +}
 +
 +static int ddcoord2simnodeid(t_commrec *cr, int x, int y, int z)
 +{
 +    gmx_domdec_comm_t *comm;
 +    ivec               coords;
 +    int                ddindex, nodeid = -1;
 +
 +    comm = cr->dd->comm;
 +
 +    coords[XX] = x;
 +    coords[YY] = y;
 +    coords[ZZ] = z;
 +    if (comm->bCartesianPP_PME)
 +    {
 +#ifdef GMX_MPI
 +        MPI_Cart_rank(cr->mpi_comm_mysim, coords, &nodeid);
 +#endif
 +    }
 +    else
 +    {
 +        ddindex = dd_index(cr->dd->nc, coords);
 +        if (comm->bCartesianPP)
 +        {
 +            nodeid = comm->ddindex2simnodeid[ddindex];
 +        }
 +        else
 +        {
 +            if (comm->pmenodes)
 +            {
 +                nodeid = ddindex + gmx_ddcoord2pmeindex(cr, x, y, z);
 +            }
 +            else
 +            {
 +                nodeid = ddindex;
 +            }
 +        }
 +    }
 +
 +    return nodeid;
 +}
 +
 +static int dd_simnode2pmenode(t_commrec *cr, int sim_nodeid)
 +{
 +    gmx_domdec_t      *dd;
 +    gmx_domdec_comm_t *comm;
 +    ivec               coord, coord_pme;
 +    int                i;
 +    int                pmenode = -1;
 +
 +    dd   = cr->dd;
 +    comm = dd->comm;
 +
 +    /* This assumes a uniform x domain decomposition grid cell size */
 +    if (comm->bCartesianPP_PME)
 +    {
 +#ifdef GMX_MPI
 +        MPI_Cart_coords(cr->mpi_comm_mysim, sim_nodeid, DIM, coord);
 +        if (coord[comm->cartpmedim] < dd->nc[comm->cartpmedim])
 +        {
 +            /* This is a PP node */
 +            dd_cart_coord2pmecoord(dd, coord, coord_pme);
 +            MPI_Cart_rank(cr->mpi_comm_mysim, coord_pme, &pmenode);
 +        }
 +#endif
 +    }
 +    else if (comm->bCartesianPP)
 +    {
 +        if (sim_nodeid < dd->nnodes)
 +        {
 +            pmenode = dd->nnodes + ddindex2pmeindex(dd, sim_nodeid);
 +        }
 +    }
 +    else
 +    {
 +        /* This assumes DD cells with identical x coordinates
 +         * are numbered sequentially.
 +         */
 +        if (dd->comm->pmenodes == NULL)
 +        {
 +            if (sim_nodeid < dd->nnodes)
 +            {
 +                /* The DD index equals the nodeid */
 +                pmenode = dd->nnodes + ddindex2pmeindex(dd, sim_nodeid);
 +            }
 +        }
 +        else
 +        {
 +            i = 0;
 +            while (sim_nodeid > dd->comm->pmenodes[i])
 +            {
 +                i++;
 +            }
 +            if (sim_nodeid < dd->comm->pmenodes[i])
 +            {
 +                pmenode = dd->comm->pmenodes[i];
 +            }
 +        }
 +    }
 +
 +    return pmenode;
 +}
 +
++void get_pme_nnodes(const gmx_domdec_t *dd,
++                    int *npmenodes_x, int *npmenodes_y)
++{
++    if (dd != NULL)
++    {
++        *npmenodes_x = dd->comm->npmenodes_x;
++        *npmenodes_y = dd->comm->npmenodes_y;
++    }
++    else
++    {
++        *npmenodes_x = 1;
++        *npmenodes_y = 1;
++    }
++}
++
 +gmx_bool gmx_pmeonlynode(t_commrec *cr, int sim_nodeid)
 +{
 +    gmx_bool bPMEOnlyNode;
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        bPMEOnlyNode = (dd_simnode2pmenode(cr, sim_nodeid) == -1);
 +    }
 +    else
 +    {
 +        bPMEOnlyNode = FALSE;
 +    }
 +
 +    return bPMEOnlyNode;
 +}
 +
 +void get_pme_ddnodes(t_commrec *cr, int pmenodeid,
 +                     int *nmy_ddnodes, int **my_ddnodes, int *node_peer)
 +{
 +    gmx_domdec_t *dd;
 +    int           x, y, z;
 +    ivec          coord, coord_pme;
 +
 +    dd = cr->dd;
 +
 +    snew(*my_ddnodes, (dd->nnodes+cr->npmenodes-1)/cr->npmenodes);
 +
 +    *nmy_ddnodes = 0;
 +    for (x = 0; x < dd->nc[XX]; x++)
 +    {
 +        for (y = 0; y < dd->nc[YY]; y++)
 +        {
 +            for (z = 0; z < dd->nc[ZZ]; z++)
 +            {
 +                if (dd->comm->bCartesianPP_PME)
 +                {
 +                    coord[XX] = x;
 +                    coord[YY] = y;
 +                    coord[ZZ] = z;
 +                    dd_cart_coord2pmecoord(dd, coord, coord_pme);
 +                    if (dd->ci[XX] == coord_pme[XX] &&
 +                        dd->ci[YY] == coord_pme[YY] &&
 +                        dd->ci[ZZ] == coord_pme[ZZ])
 +                    {
 +                        (*my_ddnodes)[(*nmy_ddnodes)++] = ddcoord2simnodeid(cr, x, y, z);
 +                    }
 +                }
 +                else
 +                {
 +                    /* The slab corresponds to the nodeid in the PME group */
 +                    if (gmx_ddcoord2pmeindex(cr, x, y, z) == pmenodeid)
 +                    {
 +                        (*my_ddnodes)[(*nmy_ddnodes)++] = ddcoord2simnodeid(cr, x, y, z);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    /* The last PP-only node is the peer node */
 +    *node_peer = (*my_ddnodes)[*nmy_ddnodes-1];
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Receive coordinates from PP nodes:");
 +        for (x = 0; x < *nmy_ddnodes; x++)
 +        {
 +            fprintf(debug, " %d", (*my_ddnodes)[x]);
 +        }
 +        fprintf(debug, "\n");
 +    }
 +}
 +
 +static gmx_bool receive_vir_ener(t_commrec *cr)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                pmenode, coords[DIM], rank;
 +    gmx_bool           bReceive;
 +
 +    bReceive = TRUE;
 +    if (cr->npmenodes < cr->dd->nnodes)
 +    {
 +        comm = cr->dd->comm;
 +        if (comm->bCartesianPP_PME)
 +        {
 +            pmenode = dd_simnode2pmenode(cr, cr->sim_nodeid);
 +#ifdef GMX_MPI
 +            MPI_Cart_coords(cr->mpi_comm_mysim, cr->sim_nodeid, DIM, coords);
 +            coords[comm->cartpmedim]++;
 +            if (coords[comm->cartpmedim] < cr->dd->nc[comm->cartpmedim])
 +            {
 +                MPI_Cart_rank(cr->mpi_comm_mysim, coords, &rank);
 +                if (dd_simnode2pmenode(cr, rank) == pmenode)
 +                {
 +                    /* This is not the last PP node for pmenode */
 +                    bReceive = FALSE;
 +                }
 +            }
 +#endif
 +        }
 +        else
 +        {
 +            pmenode = dd_simnode2pmenode(cr, cr->sim_nodeid);
 +            if (cr->sim_nodeid+1 < cr->nnodes &&
 +                dd_simnode2pmenode(cr, cr->sim_nodeid+1) == pmenode)
 +            {
 +                /* This is not the last PP node for pmenode */
 +                bReceive = FALSE;
 +            }
 +        }
 +    }
 +
 +    return bReceive;
 +}
 +
 +static void set_zones_ncg_home(gmx_domdec_t *dd)
 +{
 +    gmx_domdec_zones_t *zones;
 +    int                 i;
 +
 +    zones = &dd->comm->zones;
 +
 +    zones->cg_range[0] = 0;
 +    for (i = 1; i < zones->n+1; i++)
 +    {
 +        zones->cg_range[i] = dd->ncg_home;
 +    }
 +}
 +
 +static void rebuild_cgindex(gmx_domdec_t *dd,
 +                            const int *gcgs_index, t_state *state)
 +{
 +    int nat, i, *ind, *dd_cg_gl, *cgindex, cg_gl;
 +
 +    ind        = state->cg_gl;
 +    dd_cg_gl   = dd->index_gl;
 +    cgindex    = dd->cgindex;
 +    nat        = 0;
 +    cgindex[0] = nat;
 +    for (i = 0; i < state->ncg_gl; i++)
 +    {
 +        cgindex[i]  = nat;
 +        cg_gl       = ind[i];
 +        dd_cg_gl[i] = cg_gl;
 +        nat        += gcgs_index[cg_gl+1] - gcgs_index[cg_gl];
 +    }
 +    cgindex[i] = nat;
 +
 +    dd->ncg_home = state->ncg_gl;
 +    dd->nat_home = nat;
 +
 +    set_zones_ncg_home(dd);
 +}
 +
 +static int ddcginfo(const cginfo_mb_t *cginfo_mb, int cg)
 +{
 +    while (cg >= cginfo_mb->cg_end)
 +    {
 +        cginfo_mb++;
 +    }
 +
 +    return cginfo_mb->cginfo[(cg - cginfo_mb->cg_start) % cginfo_mb->cg_mod];
 +}
 +
 +static void dd_set_cginfo(int *index_gl, int cg0, int cg1,
 +                          t_forcerec *fr, char *bLocalCG)
 +{
 +    cginfo_mb_t *cginfo_mb;
 +    int         *cginfo;
 +    int          cg;
 +
 +    if (fr != NULL)
 +    {
 +        cginfo_mb = fr->cginfo_mb;
 +        cginfo    = fr->cginfo;
 +
 +        for (cg = cg0; cg < cg1; cg++)
 +        {
 +            cginfo[cg] = ddcginfo(cginfo_mb, index_gl[cg]);
 +        }
 +    }
 +
 +    if (bLocalCG != NULL)
 +    {
 +        for (cg = cg0; cg < cg1; cg++)
 +        {
 +            bLocalCG[index_gl[cg]] = TRUE;
 +        }
 +    }
 +}
 +
 +static void make_dd_indices(gmx_domdec_t *dd,
 +                            const int *gcgs_index, int cg_start)
 +{
 +    int          nzone, zone, zone1, cg0, cg1, cg1_p1, cg, cg_gl, a, a_gl;
 +    int         *zone2cg, *zone_ncg1, *index_gl, *gatindex;
 +    gmx_ga2la_t *ga2la;
 +    char        *bLocalCG;
 +    gmx_bool     bCGs;
 +
 +    bLocalCG = dd->comm->bLocalCG;
 +
 +    if (dd->nat_tot > dd->gatindex_nalloc)
 +    {
 +        dd->gatindex_nalloc = over_alloc_dd(dd->nat_tot);
 +        srenew(dd->gatindex, dd->gatindex_nalloc);
 +    }
 +
 +    nzone      = dd->comm->zones.n;
 +    zone2cg    = dd->comm->zones.cg_range;
 +    zone_ncg1  = dd->comm->zone_ncg1;
 +    index_gl   = dd->index_gl;
 +    gatindex   = dd->gatindex;
 +    bCGs       = dd->comm->bCGs;
 +
 +    if (zone2cg[1] != dd->ncg_home)
 +    {
 +        gmx_incons("dd->ncg_zone is not up to date");
 +    }
 +
 +    /* Make the local to global and global to local atom index */
 +    a = dd->cgindex[cg_start];
 +    for (zone = 0; zone < nzone; zone++)
 +    {
 +        if (zone == 0)
 +        {
 +            cg0 = cg_start;
 +        }
 +        else
 +        {
 +            cg0 = zone2cg[zone];
 +        }
 +        cg1    = zone2cg[zone+1];
 +        cg1_p1 = cg0 + zone_ncg1[zone];
 +
 +        for (cg = cg0; cg < cg1; cg++)
 +        {
 +            zone1 = zone;
 +            if (cg >= cg1_p1)
 +            {
 +                /* Signal that this cg is from more than one pulse away */
 +                zone1 += nzone;
 +            }
 +            cg_gl = index_gl[cg];
 +            if (bCGs)
 +            {
 +                for (a_gl = gcgs_index[cg_gl]; a_gl < gcgs_index[cg_gl+1]; a_gl++)
 +                {
 +                    gatindex[a] = a_gl;
 +                    ga2la_set(dd->ga2la, a_gl, a, zone1);
 +                    a++;
 +                }
 +            }
 +            else
 +            {
 +                gatindex[a] = cg_gl;
 +                ga2la_set(dd->ga2la, cg_gl, a, zone1);
 +                a++;
 +            }
 +        }
 +    }
 +}
 +
 +static int check_bLocalCG(gmx_domdec_t *dd, int ncg_sys, const char *bLocalCG,
 +                          const char *where)
 +{
 +    int ncg, i, ngl, nerr;
 +
 +    nerr = 0;
 +    if (bLocalCG == NULL)
 +    {
 +        return nerr;
 +    }
 +    for (i = 0; i < dd->ncg_tot; i++)
 +    {
 +        if (!bLocalCG[dd->index_gl[i]])
 +        {
 +            fprintf(stderr,
 +                    "DD node %d, %s: cg %d, global cg %d is not marked in bLocalCG (ncg_home %d)\n", dd->rank, where, i+1, dd->index_gl[i]+1, dd->ncg_home);
 +            nerr++;
 +        }
 +    }
 +    ngl = 0;
 +    for (i = 0; i < ncg_sys; i++)
 +    {
 +        if (bLocalCG[i])
 +        {
 +            ngl++;
 +        }
 +    }
 +    if (ngl != dd->ncg_tot)
 +    {
 +        fprintf(stderr, "DD node %d, %s: In bLocalCG %d cgs are marked as local, whereas there are %d\n", dd->rank, where, ngl, dd->ncg_tot);
 +        nerr++;
 +    }
 +
 +    return nerr;
 +}
 +
 +static void check_index_consistency(gmx_domdec_t *dd,
 +                                    int natoms_sys, int ncg_sys,
 +                                    const char *where)
 +{
 +    int   nerr, ngl, i, a, cell;
 +    int  *have;
 +
 +    nerr = 0;
 +
 +    if (dd->comm->DD_debug > 1)
 +    {
 +        snew(have, natoms_sys);
 +        for (a = 0; a < dd->nat_tot; a++)
 +        {
 +            if (have[dd->gatindex[a]] > 0)
 +            {
 +                fprintf(stderr, "DD node %d: global atom %d occurs twice: index %d and %d\n", dd->rank, dd->gatindex[a]+1, have[dd->gatindex[a]], a+1);
 +            }
 +            else
 +            {
 +                have[dd->gatindex[a]] = a + 1;
 +            }
 +        }
 +        sfree(have);
 +    }
 +
 +    snew(have, dd->nat_tot);
 +
 +    ngl  = 0;
 +    for (i = 0; i < natoms_sys; i++)
 +    {
 +        if (ga2la_get(dd->ga2la, i, &a, &cell))
 +        {
 +            if (a >= dd->nat_tot)
 +            {
 +                fprintf(stderr, "DD node %d: global atom %d marked as local atom %d, which is larger than nat_tot (%d)\n", dd->rank, i+1, a+1, dd->nat_tot);
 +                nerr++;
 +            }
 +            else
 +            {
 +                have[a] = 1;
 +                if (dd->gatindex[a] != i)
 +                {
 +                    fprintf(stderr, "DD node %d: global atom %d marked as local atom %d, which has global atom index %d\n", dd->rank, i+1, a+1, dd->gatindex[a]+1);
 +                    nerr++;
 +                }
 +            }
 +            ngl++;
 +        }
 +    }
 +    if (ngl != dd->nat_tot)
 +    {
 +        fprintf(stderr,
 +                "DD node %d, %s: %d global atom indices, %d local atoms\n",
 +                dd->rank, where, ngl, dd->nat_tot);
 +    }
 +    for (a = 0; a < dd->nat_tot; a++)
 +    {
 +        if (have[a] == 0)
 +        {
 +            fprintf(stderr,
 +                    "DD node %d, %s: local atom %d, global %d has no global index\n",
 +                    dd->rank, where, a+1, dd->gatindex[a]+1);
 +        }
 +    }
 +    sfree(have);
 +
 +    nerr += check_bLocalCG(dd, ncg_sys, dd->comm->bLocalCG, where);
 +
 +    if (nerr > 0)
 +    {
 +        gmx_fatal(FARGS, "DD node %d, %s: %d atom/cg index inconsistencies",
 +                  dd->rank, where, nerr);
 +    }
 +}
 +
 +static void clear_dd_indices(gmx_domdec_t *dd, int cg_start, int a_start)
 +{
 +    int   i;
 +    char *bLocalCG;
 +
 +    if (a_start == 0)
 +    {
 +        /* Clear the whole list without searching */
 +        ga2la_clear(dd->ga2la);
 +    }
 +    else
 +    {
 +        for (i = a_start; i < dd->nat_tot; i++)
 +        {
 +            ga2la_del(dd->ga2la, dd->gatindex[i]);
 +        }
 +    }
 +
 +    bLocalCG = dd->comm->bLocalCG;
 +    if (bLocalCG)
 +    {
 +        for (i = cg_start; i < dd->ncg_tot; i++)
 +        {
 +            bLocalCG[dd->index_gl[i]] = FALSE;
 +        }
 +    }
 +
 +    dd_clear_local_vsite_indices(dd);
 +
 +    if (dd->constraints)
 +    {
 +        dd_clear_local_constraint_indices(dd);
 +    }
 +}
 +
 +/* This function should be used for moving the domain boudaries during DLB,
 + * for obtaining the minimum cell size. It checks the initially set limit
 + * comm->cellsize_min, for bonded and initial non-bonded cut-offs,
 + * and, possibly, a longer cut-off limit set for PME load balancing.
 + */
 +static real cellsize_min_dlb(gmx_domdec_comm_t *comm, int dim_ind, int dim)
 +{
 +    real cellsize_min;
 +
 +    cellsize_min = comm->cellsize_min[dim];
 +
 +    if (!comm->bVacDLBNoLimit)
 +    {
 +        /* The cut-off might have changed, e.g. by PME load balacning,
 +         * from the value used to set comm->cellsize_min, so check it.
 +         */
 +        cellsize_min = max(cellsize_min, comm->cutoff/comm->cd[dim_ind].np_dlb);
 +
 +        if (comm->bPMELoadBalDLBLimits)
 +        {
 +            /* Check for the cut-off limit set by the PME load balancing */
 +            cellsize_min = max(cellsize_min, comm->PMELoadBal_max_cutoff/comm->cd[dim_ind].np_dlb);
 +        }
 +    }
 +
 +    return cellsize_min;
 +}
 +
 +static real grid_jump_limit(gmx_domdec_comm_t *comm, real cutoff,
 +                            int dim_ind)
 +{
 +    real grid_jump_limit;
 +
 +    /* The distance between the boundaries of cells at distance
 +     * x+-1,y+-1 or y+-1,z+-1 is limited by the cut-off restrictions
 +     * and by the fact that cells should not be shifted by more than
 +     * half their size, such that cg's only shift by one cell
 +     * at redecomposition.
 +     */
 +    grid_jump_limit = comm->cellsize_limit;
 +    if (!comm->bVacDLBNoLimit)
 +    {
 +        if (comm->bPMELoadBalDLBLimits)
 +        {
 +            cutoff = max(cutoff, comm->PMELoadBal_max_cutoff);
 +        }
 +        grid_jump_limit = max(grid_jump_limit,
 +                              cutoff/comm->cd[dim_ind].np);
 +    }
 +
 +    return grid_jump_limit;
 +}
 +
 +static gmx_bool check_grid_jump(gmx_large_int_t step,
 +                                gmx_domdec_t   *dd,
 +                                real            cutoff,
 +                                gmx_ddbox_t    *ddbox,
 +                                gmx_bool        bFatal)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                d, dim;
 +    real               limit, bfac;
 +    gmx_bool           bInvalid;
 +
 +    bInvalid = FALSE;
 +
 +    comm = dd->comm;
 +
 +    for (d = 1; d < dd->ndim; d++)
 +    {
 +        dim   = dd->dim[d];
 +        limit = grid_jump_limit(comm, cutoff, d);
 +        bfac  = ddbox->box_size[dim];
 +        if (ddbox->tric_dir[dim])
 +        {
 +            bfac *= ddbox->skew_fac[dim];
 +        }
 +        if ((comm->cell_f1[d] - comm->cell_f_max0[d])*bfac <  limit ||
 +                                                              (comm->cell_f0[d] - comm->cell_f_min1[d])*bfac > -limit)
 +        {
 +            bInvalid = TRUE;
 +
 +            if (bFatal)
 +            {
 +                char buf[22];
 +
 +                /* This error should never be triggered under normal
 +                 * circumstances, but you never know ...
 +                 */
 +                gmx_fatal(FARGS, "Step %s: The domain decomposition grid has shifted too much in the %c-direction around cell %d %d %d. This should not have happened. Running with less nodes might avoid this issue.",
 +                          gmx_step_str(step, buf),
 +                          dim2char(dim), dd->ci[XX], dd->ci[YY], dd->ci[ZZ]);
 +            }
 +        }
 +    }
 +
 +    return bInvalid;
 +}
 +
 +static int dd_load_count(gmx_domdec_comm_t *comm)
 +{
 +    return (comm->eFlop ? comm->flop_n : comm->cycl_n[ddCyclF]);
 +}
 +
 +static float dd_force_load(gmx_domdec_comm_t *comm)
 +{
 +    float load;
 +
 +    if (comm->eFlop)
 +    {
 +        load = comm->flop;
 +        if (comm->eFlop > 1)
 +        {
 +            load *= 1.0 + (comm->eFlop - 1)*(0.1*rand()/RAND_MAX - 0.05);
 +        }
 +    }
 +    else
 +    {
 +        load = comm->cycl[ddCyclF];
 +        if (comm->cycl_n[ddCyclF] > 1)
 +        {
 +            /* Subtract the maximum of the last n cycle counts
 +             * to get rid of possible high counts due to other soures,
 +             * for instance system activity, that would otherwise
 +             * affect the dynamic load balancing.
 +             */
 +            load -= comm->cycl_max[ddCyclF];
 +        }
 +    }
 +
 +    return load;
 +}
 +
 +static void set_slb_pme_dim_f(gmx_domdec_t *dd, int dim, real **dim_f)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                i;
 +
 +    comm = dd->comm;
 +
 +    snew(*dim_f, dd->nc[dim]+1);
 +    (*dim_f)[0] = 0;
 +    for (i = 1; i < dd->nc[dim]; i++)
 +    {
 +        if (comm->slb_frac[dim])
 +        {
 +            (*dim_f)[i] = (*dim_f)[i-1] + comm->slb_frac[dim][i-1];
 +        }
 +        else
 +        {
 +            (*dim_f)[i] = (real)i/(real)dd->nc[dim];
 +        }
 +    }
 +    (*dim_f)[dd->nc[dim]] = 1;
 +}
 +
 +static void init_ddpme(gmx_domdec_t *dd, gmx_ddpme_t *ddpme, int dimind)
 +{
 +    int  pmeindex, slab, nso, i;
 +    ivec xyz;
 +
 +    if (dimind == 0 && dd->dim[0] == YY && dd->comm->npmenodes_x == 1)
 +    {
 +        ddpme->dim = YY;
 +    }
 +    else
 +    {
 +        ddpme->dim = dimind;
 +    }
 +    ddpme->dim_match = (ddpme->dim == dd->dim[dimind]);
 +
 +    ddpme->nslab = (ddpme->dim == 0 ?
 +                    dd->comm->npmenodes_x :
 +                    dd->comm->npmenodes_y);
 +
 +    if (ddpme->nslab <= 1)
 +    {
 +        return;
 +    }
 +
 +    nso = dd->comm->npmenodes/ddpme->nslab;
 +    /* Determine for each PME slab the PP location range for dimension dim */
 +    snew(ddpme->pp_min, ddpme->nslab);
 +    snew(ddpme->pp_max, ddpme->nslab);
 +    for (slab = 0; slab < ddpme->nslab; slab++)
 +    {
 +        ddpme->pp_min[slab] = dd->nc[dd->dim[dimind]] - 1;
 +        ddpme->pp_max[slab] = 0;
 +    }
 +    for (i = 0; i < dd->nnodes; i++)
 +    {
 +        ddindex2xyz(dd->nc, i, xyz);
 +        /* For y only use our y/z slab.
 +         * This assumes that the PME x grid size matches the DD grid size.
 +         */
 +        if (dimind == 0 || xyz[XX] == dd->ci[XX])
 +        {
 +            pmeindex = ddindex2pmeindex(dd, i);
 +            if (dimind == 0)
 +            {
 +                slab = pmeindex/nso;
 +            }
 +            else
 +            {
 +                slab = pmeindex % ddpme->nslab;
 +            }
 +            ddpme->pp_min[slab] = min(ddpme->pp_min[slab], xyz[dimind]);
 +            ddpme->pp_max[slab] = max(ddpme->pp_max[slab], xyz[dimind]);
 +        }
 +    }
 +
 +    set_slb_pme_dim_f(dd, ddpme->dim, &ddpme->slb_dim_f);
 +}
 +
 +int dd_pme_maxshift_x(gmx_domdec_t *dd)
 +{
 +    if (dd->comm->ddpme[0].dim == XX)
 +    {
 +        return dd->comm->ddpme[0].maxshift;
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +int dd_pme_maxshift_y(gmx_domdec_t *dd)
 +{
 +    if (dd->comm->ddpme[0].dim == YY)
 +    {
 +        return dd->comm->ddpme[0].maxshift;
 +    }
 +    else if (dd->comm->npmedecompdim >= 2 && dd->comm->ddpme[1].dim == YY)
 +    {
 +        return dd->comm->ddpme[1].maxshift;
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +static void set_pme_maxshift(gmx_domdec_t *dd, gmx_ddpme_t *ddpme,
 +                             gmx_bool bUniform, gmx_ddbox_t *ddbox, real *cell_f)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                nc, ns, s;
 +    int               *xmin, *xmax;
 +    real               range, pme_boundary;
 +    int                sh;
 +
 +    comm = dd->comm;
 +    nc   = dd->nc[ddpme->dim];
 +    ns   = ddpme->nslab;
 +
 +    if (!ddpme->dim_match)
 +    {
 +        /* PP decomposition is not along dim: the worst situation */
 +        sh = ns/2;
 +    }
 +    else if (ns <= 3 || (bUniform && ns == nc))
 +    {
 +        /* The optimal situation */
 +        sh = 1;
 +    }
 +    else
 +    {
 +        /* We need to check for all pme nodes which nodes they
 +         * could possibly need to communicate with.
 +         */
 +        xmin = ddpme->pp_min;
 +        xmax = ddpme->pp_max;
 +        /* Allow for atoms to be maximally 2/3 times the cut-off
 +         * out of their DD cell. This is a reasonable balance between
 +         * between performance and support for most charge-group/cut-off
 +         * combinations.
 +         */
 +        range  = 2.0/3.0*comm->cutoff/ddbox->box_size[ddpme->dim];
 +        /* Avoid extra communication when we are exactly at a boundary */
 +        range *= 0.999;
 +
 +        sh = 1;
 +        for (s = 0; s < ns; s++)
 +        {
 +            /* PME slab s spreads atoms between box frac. s/ns and (s+1)/ns */
 +            pme_boundary = (real)s/ns;
 +            while (sh+1 < ns &&
 +                   ((s-(sh+1) >= 0 &&
 +                     cell_f[xmax[s-(sh+1)   ]+1]     + range > pme_boundary) ||
 +                    (s-(sh+1) <  0 &&
 +                     cell_f[xmax[s-(sh+1)+ns]+1] - 1 + range > pme_boundary)))
 +            {
 +                sh++;
 +            }
 +            pme_boundary = (real)(s+1)/ns;
 +            while (sh+1 < ns &&
 +                   ((s+(sh+1) <  ns &&
 +                     cell_f[xmin[s+(sh+1)   ]  ]     - range < pme_boundary) ||
 +                    (s+(sh+1) >= ns &&
 +                     cell_f[xmin[s+(sh+1)-ns]  ] + 1 - range < pme_boundary)))
 +            {
 +                sh++;
 +            }
 +        }
 +    }
 +
 +    ddpme->maxshift = sh;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "PME slab communication range for dim %d is %d\n",
 +                ddpme->dim, ddpme->maxshift);
 +    }
 +}
 +
 +static void check_box_size(gmx_domdec_t *dd, gmx_ddbox_t *ddbox)
 +{
 +    int d, dim;
 +
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        dim = dd->dim[d];
 +        if (dim < ddbox->nboundeddim &&
 +            ddbox->box_size[dim]*ddbox->skew_fac[dim] <
 +            dd->nc[dim]*dd->comm->cellsize_limit*DD_CELL_MARGIN)
 +        {
 +            gmx_fatal(FARGS, "The %c-size of the box (%f) times the triclinic skew factor (%f) is smaller than the number of DD cells (%d) times the smallest allowed cell size (%f)\n",
 +                      dim2char(dim), ddbox->box_size[dim], ddbox->skew_fac[dim],
 +                      dd->nc[dim], dd->comm->cellsize_limit);
 +        }
 +    }
 +}
 +
 +static void set_dd_cell_sizes_slb(gmx_domdec_t *dd, gmx_ddbox_t *ddbox,
 +                                  gmx_bool bMaster, ivec npulse)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                d, j;
 +    rvec               cellsize_min;
 +    real              *cell_x, cell_dx, cellsize;
 +
 +    comm = dd->comm;
 +
 +    for (d = 0; d < DIM; d++)
 +    {
 +        cellsize_min[d] = ddbox->box_size[d]*ddbox->skew_fac[d];
 +        npulse[d]       = 1;
 +        if (dd->nc[d] == 1 || comm->slb_frac[d] == NULL)
 +        {
 +            /* Uniform grid */
 +            cell_dx = ddbox->box_size[d]/dd->nc[d];
 +            if (bMaster)
 +            {
 +                for (j = 0; j < dd->nc[d]+1; j++)
 +                {
 +                    dd->ma->cell_x[d][j] = ddbox->box0[d] + j*cell_dx;
 +                }
 +            }
 +            else
 +            {
 +                comm->cell_x0[d] = ddbox->box0[d] + (dd->ci[d]  )*cell_dx;
 +                comm->cell_x1[d] = ddbox->box0[d] + (dd->ci[d]+1)*cell_dx;
 +            }
 +            cellsize = cell_dx*ddbox->skew_fac[d];
 +            while (cellsize*npulse[d] < comm->cutoff && npulse[d] < dd->nc[d]-1)
 +            {
 +                npulse[d]++;
 +            }
 +            cellsize_min[d] = cellsize;
 +        }
 +        else
 +        {
 +            /* Statically load balanced grid */
 +            /* Also when we are not doing a master distribution we determine
 +             * all cell borders in a loop to obtain identical values
 +             * to the master distribution case and to determine npulse.
 +             */
 +            if (bMaster)
 +            {
 +                cell_x = dd->ma->cell_x[d];
 +            }
 +            else
 +            {
 +                snew(cell_x, dd->nc[d]+1);
 +            }
 +            cell_x[0] = ddbox->box0[d];
 +            for (j = 0; j < dd->nc[d]; j++)
 +            {
 +                cell_dx     = ddbox->box_size[d]*comm->slb_frac[d][j];
 +                cell_x[j+1] = cell_x[j] + cell_dx;
 +                cellsize    = cell_dx*ddbox->skew_fac[d];
 +                while (cellsize*npulse[d] < comm->cutoff &&
 +                       npulse[d] < dd->nc[d]-1)
 +                {
 +                    npulse[d]++;
 +                }
 +                cellsize_min[d] = min(cellsize_min[d], cellsize);
 +            }
 +            if (!bMaster)
 +            {
 +                comm->cell_x0[d] = cell_x[dd->ci[d]];
 +                comm->cell_x1[d] = cell_x[dd->ci[d]+1];
 +                sfree(cell_x);
 +            }
 +        }
 +        /* The following limitation is to avoid that a cell would receive
 +         * some of its own home charge groups back over the periodic boundary.
 +         * Double charge groups cause trouble with the global indices.
 +         */
 +        if (d < ddbox->npbcdim &&
 +            dd->nc[d] > 1 && npulse[d] >= dd->nc[d])
 +        {
 +            gmx_fatal_collective(FARGS, NULL, dd,
 +                                 "The box size in direction %c (%f) times the triclinic skew factor (%f) is too small for a cut-off of %f with %d domain decomposition cells, use 1 or more than %d %s or increase the box size in this direction",
 +                                 dim2char(d), ddbox->box_size[d], ddbox->skew_fac[d],
 +                                 comm->cutoff,
 +                                 dd->nc[d], dd->nc[d],
 +                                 dd->nnodes > dd->nc[d] ? "cells" : "processors");
 +        }
 +    }
 +
 +    if (!comm->bDynLoadBal)
 +    {
 +        copy_rvec(cellsize_min, comm->cellsize_min);
 +    }
 +
 +    for (d = 0; d < comm->npmedecompdim; d++)
 +    {
 +        set_pme_maxshift(dd, &comm->ddpme[d],
 +                         comm->slb_frac[dd->dim[d]] == NULL, ddbox,
 +                         comm->ddpme[d].slb_dim_f);
 +    }
 +}
 +
 +
 +static void dd_cell_sizes_dlb_root_enforce_limits(gmx_domdec_t *dd,
 +                                                  int d, int dim, gmx_domdec_root_t *root,
 +                                                  gmx_ddbox_t *ddbox,
 +                                                  gmx_bool bUniform, gmx_large_int_t step, real cellsize_limit_f, int range[])
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                ncd, i, j, nmin, nmin_old;
 +    gmx_bool           bLimLo, bLimHi;
 +    real              *cell_size;
 +    real               fac, halfway, cellsize_limit_f_i, region_size;
 +    gmx_bool           bPBC, bLastHi = FALSE;
 +    int                nrange[] = {range[0], range[1]};
 +
 +    region_size = root->cell_f[range[1]]-root->cell_f[range[0]];
 +
 +    comm = dd->comm;
 +
 +    ncd = dd->nc[dim];
 +
 +    bPBC = (dim < ddbox->npbcdim);
 +
 +    cell_size = root->buf_ncd;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "enforce_limits: %d %d\n", range[0], range[1]);
 +    }
 +
 +    /* First we need to check if the scaling does not make cells
 +     * smaller than the smallest allowed size.
 +     * We need to do this iteratively, since if a cell is too small,
 +     * it needs to be enlarged, which makes all the other cells smaller,
 +     * which could in turn make another cell smaller than allowed.
 +     */
 +    for (i = range[0]; i < range[1]; i++)
 +    {
 +        root->bCellMin[i] = FALSE;
 +    }
 +    nmin = 0;
 +    do
 +    {
 +        nmin_old = nmin;
 +        /* We need the total for normalization */
 +        fac = 0;
 +        for (i = range[0]; i < range[1]; i++)
 +        {
 +            if (root->bCellMin[i] == FALSE)
 +            {
 +                fac += cell_size[i];
 +            }
 +        }
 +        fac = ( region_size - nmin*cellsize_limit_f)/fac; /* substracting cells already set to cellsize_limit_f */
 +        /* Determine the cell boundaries */
 +        for (i = range[0]; i < range[1]; i++)
 +        {
 +            if (root->bCellMin[i] == FALSE)
 +            {
 +                cell_size[i] *= fac;
 +                if (!bPBC && (i == 0 || i == dd->nc[dim] -1))
 +                {
 +                    cellsize_limit_f_i = 0;
 +                }
 +                else
 +                {
 +                    cellsize_limit_f_i = cellsize_limit_f;
 +                }
 +                if (cell_size[i] < cellsize_limit_f_i)
 +                {
 +                    root->bCellMin[i] = TRUE;
 +                    cell_size[i]      = cellsize_limit_f_i;
 +                    nmin++;
 +                }
 +            }
 +            root->cell_f[i+1] = root->cell_f[i] + cell_size[i];
 +        }
 +    }
 +    while (nmin > nmin_old);
 +
 +    i            = range[1]-1;
 +    cell_size[i] = root->cell_f[i+1] - root->cell_f[i];
 +    /* For this check we should not use DD_CELL_MARGIN,
 +     * but a slightly smaller factor,
 +     * since rounding could get use below the limit.
 +     */
 +    if (bPBC && cell_size[i] < cellsize_limit_f*DD_CELL_MARGIN2/DD_CELL_MARGIN)
 +    {
 +        char buf[22];
 +        gmx_fatal(FARGS, "Step %s: the dynamic load balancing could not balance dimension %c: box size %f, triclinic skew factor %f, #cells %d, minimum cell size %f\n",
 +                  gmx_step_str(step, buf),
 +                  dim2char(dim), ddbox->box_size[dim], ddbox->skew_fac[dim],
 +                  ncd, comm->cellsize_min[dim]);
 +    }
 +
 +    root->bLimited = (nmin > 0) || (range[0] > 0) || (range[1] < ncd);
 +
 +    if (!bUniform)
 +    {
 +        /* Check if the boundary did not displace more than halfway
 +         * each of the cells it bounds, as this could cause problems,
 +         * especially when the differences between cell sizes are large.
 +         * If changes are applied, they will not make cells smaller
 +         * than the cut-off, as we check all the boundaries which
 +         * might be affected by a change and if the old state was ok,
 +         * the cells will at most be shrunk back to their old size.
 +         */
 +        for (i = range[0]+1; i < range[1]; i++)
 +        {
 +            halfway = 0.5*(root->old_cell_f[i] + root->old_cell_f[i-1]);
 +            if (root->cell_f[i] < halfway)
 +            {
 +                root->cell_f[i] = halfway;
 +                /* Check if the change also causes shifts of the next boundaries */
 +                for (j = i+1; j < range[1]; j++)
 +                {
 +                    if (root->cell_f[j] < root->cell_f[j-1] + cellsize_limit_f)
 +                    {
 +                        root->cell_f[j] =  root->cell_f[j-1] + cellsize_limit_f;
 +                    }
 +                }
 +            }
 +            halfway = 0.5*(root->old_cell_f[i] + root->old_cell_f[i+1]);
 +            if (root->cell_f[i] > halfway)
 +            {
 +                root->cell_f[i] = halfway;
 +                /* Check if the change also causes shifts of the next boundaries */
 +                for (j = i-1; j >= range[0]+1; j--)
 +                {
 +                    if (root->cell_f[j] > root->cell_f[j+1] - cellsize_limit_f)
 +                    {
 +                        root->cell_f[j] = root->cell_f[j+1] - cellsize_limit_f;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    /* nrange is defined as [lower, upper) range for new call to enforce_limits */
 +    /* find highest violation of LimLo (a) and the following violation of LimHi (thus the lowest following) (b)
 +     * then call enforce_limits for (oldb,a), (a,b). In the next step: (b,nexta). oldb and nexta can be the boundaries.
 +     * for a and b nrange is used */
 +    if (d > 0)
 +    {
 +        /* Take care of the staggering of the cell boundaries */
 +        if (bUniform)
 +        {
 +            for (i = range[0]; i < range[1]; i++)
 +            {
 +                root->cell_f_max0[i] = root->cell_f[i];
 +                root->cell_f_min1[i] = root->cell_f[i+1];
 +            }
 +        }
 +        else
 +        {
 +            for (i = range[0]+1; i < range[1]; i++)
 +            {
 +                bLimLo = (root->cell_f[i] < root->bound_min[i]);
 +                bLimHi = (root->cell_f[i] > root->bound_max[i]);
 +                if (bLimLo && bLimHi)
 +                {
 +                    /* Both limits violated, try the best we can */
 +                    /* For this case we split the original range (range) in two parts and care about the other limitiations in the next iteration. */
 +                    root->cell_f[i] = 0.5*(root->bound_min[i] + root->bound_max[i]);
 +                    nrange[0]       = range[0];
 +                    nrange[1]       = i;
 +                    dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
 +
 +                    nrange[0] = i;
 +                    nrange[1] = range[1];
 +                    dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
 +
 +                    return;
 +                }
 +                else if (bLimLo)
 +                {
 +                    /* root->cell_f[i] = root->bound_min[i]; */
 +                    nrange[1] = i;  /* only store violation location. There could be a LimLo violation following with an higher index */
 +                    bLastHi   = FALSE;
 +                }
 +                else if (bLimHi && !bLastHi)
 +                {
 +                    bLastHi = TRUE;
 +                    if (nrange[1] < range[1])   /* found a LimLo before */
 +                    {
 +                        root->cell_f[nrange[1]] = root->bound_min[nrange[1]];
 +                        dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
 +                        nrange[0] = nrange[1];
 +                    }
 +                    root->cell_f[i] = root->bound_max[i];
 +                    nrange[1]       = i;
 +                    dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
 +                    nrange[0] = i;
 +                    nrange[1] = range[1];
 +                }
 +            }
 +            if (nrange[1] < range[1])   /* found last a LimLo */
 +            {
 +                root->cell_f[nrange[1]] = root->bound_min[nrange[1]];
 +                dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
 +                nrange[0] = nrange[1];
 +                nrange[1] = range[1];
 +                dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
 +            }
 +            else if (nrange[0] > range[0]) /* found at least one LimHi */
 +            {
 +                dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, nrange);
 +            }
 +        }
 +    }
 +}
 +
 +
 +static void set_dd_cell_sizes_dlb_root(gmx_domdec_t *dd,
 +                                       int d, int dim, gmx_domdec_root_t *root,
 +                                       gmx_ddbox_t *ddbox, gmx_bool bDynamicBox,
 +                                       gmx_bool bUniform, gmx_large_int_t step)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                ncd, d1, i, j, pos;
 +    real              *cell_size;
 +    real               load_aver, load_i, imbalance, change, change_max, sc;
 +    real               cellsize_limit_f, dist_min_f, dist_min_f_hard, space;
 +    real               change_limit;
 +    real               relax = 0.5;
 +    gmx_bool           bPBC;
 +    int                range[] = { 0, 0 };
 +
 +    comm = dd->comm;
 +
 +    /* Convert the maximum change from the input percentage to a fraction */
 +    change_limit = comm->dlb_scale_lim*0.01;
 +
 +    ncd = dd->nc[dim];
 +
 +    bPBC = (dim < ddbox->npbcdim);
 +
 +    cell_size = root->buf_ncd;
 +
 +    /* Store the original boundaries */
 +    for (i = 0; i < ncd+1; i++)
 +    {
 +        root->old_cell_f[i] = root->cell_f[i];
 +    }
 +    if (bUniform)
 +    {
 +        for (i = 0; i < ncd; i++)
 +        {
 +            cell_size[i] = 1.0/ncd;
 +        }
 +    }
 +    else if (dd_load_count(comm))
 +    {
 +        load_aver  = comm->load[d].sum_m/ncd;
 +        change_max = 0;
 +        for (i = 0; i < ncd; i++)
 +        {
 +            /* Determine the relative imbalance of cell i */
 +            load_i    = comm->load[d].load[i*comm->load[d].nload+2];
 +            imbalance = (load_i - load_aver)/(load_aver > 0 ? load_aver : 1);
 +            /* Determine the change of the cell size using underrelaxation */
 +            change     = -relax*imbalance;
 +            change_max = max(change_max, max(change, -change));
 +        }
 +        /* Limit the amount of scaling.
 +         * We need to use the same rescaling for all cells in one row,
 +         * otherwise the load balancing might not converge.
 +         */
 +        sc = relax;
 +        if (change_max > change_limit)
 +        {
 +            sc *= change_limit/change_max;
 +        }
 +        for (i = 0; i < ncd; i++)
 +        {
 +            /* Determine the relative imbalance of cell i */
 +            load_i    = comm->load[d].load[i*comm->load[d].nload+2];
 +            imbalance = (load_i - load_aver)/(load_aver > 0 ? load_aver : 1);
 +            /* Determine the change of the cell size using underrelaxation */
 +            change       = -sc*imbalance;
 +            cell_size[i] = (root->cell_f[i+1]-root->cell_f[i])*(1 + change);
 +        }
 +    }
 +
 +    cellsize_limit_f  = cellsize_min_dlb(comm, d, dim)/ddbox->box_size[dim];
 +    cellsize_limit_f *= DD_CELL_MARGIN;
 +    dist_min_f_hard   = grid_jump_limit(comm, comm->cutoff, d)/ddbox->box_size[dim];
 +    dist_min_f        = dist_min_f_hard * DD_CELL_MARGIN;
 +    if (ddbox->tric_dir[dim])
 +    {
 +        cellsize_limit_f /= ddbox->skew_fac[dim];
 +        dist_min_f       /= ddbox->skew_fac[dim];
 +    }
 +    if (bDynamicBox && d > 0)
 +    {
 +        dist_min_f *= DD_PRES_SCALE_MARGIN;
 +    }
 +    if (d > 0 && !bUniform)
 +    {
 +        /* Make sure that the grid is not shifted too much */
 +        for (i = 1; i < ncd; i++)
 +        {
 +            if (root->cell_f_min1[i] - root->cell_f_max0[i-1] < 2 * dist_min_f_hard)
 +            {
 +                gmx_incons("Inconsistent DD boundary staggering limits!");
 +            }
 +            root->bound_min[i] = root->cell_f_max0[i-1] + dist_min_f;
 +            space              = root->cell_f[i] - (root->cell_f_max0[i-1] + dist_min_f);
 +            if (space > 0)
 +            {
 +                root->bound_min[i] += 0.5*space;
 +            }
 +            root->bound_max[i] = root->cell_f_min1[i] - dist_min_f;
 +            space              = root->cell_f[i] - (root->cell_f_min1[i] - dist_min_f);
 +            if (space < 0)
 +            {
 +                root->bound_max[i] += 0.5*space;
 +            }
 +            if (debug)
 +            {
 +                fprintf(debug,
 +                        "dim %d boundary %d %.3f < %.3f < %.3f < %.3f < %.3f\n",
 +                        d, i,
 +                        root->cell_f_max0[i-1] + dist_min_f,
 +                        root->bound_min[i], root->cell_f[i], root->bound_max[i],
 +                        root->cell_f_min1[i] - dist_min_f);
 +            }
 +        }
 +    }
 +    range[1]          = ncd;
 +    root->cell_f[0]   = 0;
 +    root->cell_f[ncd] = 1;
 +    dd_cell_sizes_dlb_root_enforce_limits(dd, d, dim, root, ddbox, bUniform, step, cellsize_limit_f, range);
 +
 +
 +    /* After the checks above, the cells should obey the cut-off
 +     * restrictions, but it does not hurt to check.
 +     */
 +    for (i = 0; i < ncd; i++)
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "Relative bounds dim %d  cell %d: %f %f\n",
 +                    dim, i, root->cell_f[i], root->cell_f[i+1]);
 +        }
 +
 +        if ((bPBC || (i != 0 && i != dd->nc[dim]-1)) &&
 +            root->cell_f[i+1] - root->cell_f[i] <
 +            cellsize_limit_f/DD_CELL_MARGIN)
 +        {
 +            char buf[22];
 +            fprintf(stderr,
 +                    "\nWARNING step %s: direction %c, cell %d too small: %f\n",
 +                    gmx_step_str(step, buf), dim2char(dim), i,
 +                    (root->cell_f[i+1] - root->cell_f[i])
 +                    *ddbox->box_size[dim]*ddbox->skew_fac[dim]);
 +        }
 +    }
 +
 +    pos = ncd + 1;
 +    /* Store the cell boundaries of the lower dimensions at the end */
 +    for (d1 = 0; d1 < d; d1++)
 +    {
 +        root->cell_f[pos++] = comm->cell_f0[d1];
 +        root->cell_f[pos++] = comm->cell_f1[d1];
 +    }
 +
 +    if (d < comm->npmedecompdim)
 +    {
 +        /* The master determines the maximum shift for
 +         * the coordinate communication between separate PME nodes.
 +         */
 +        set_pme_maxshift(dd, &comm->ddpme[d], bUniform, ddbox, root->cell_f);
 +    }
 +    root->cell_f[pos++] = comm->ddpme[0].maxshift;
 +    if (d >= 1)
 +    {
 +        root->cell_f[pos++] = comm->ddpme[1].maxshift;
 +    }
 +}
 +
 +static void relative_to_absolute_cell_bounds(gmx_domdec_t *dd,
 +                                             gmx_ddbox_t *ddbox, int dimind)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                dim;
 +
 +    comm = dd->comm;
 +
 +    /* Set the cell dimensions */
 +    dim                = dd->dim[dimind];
 +    comm->cell_x0[dim] = comm->cell_f0[dimind]*ddbox->box_size[dim];
 +    comm->cell_x1[dim] = comm->cell_f1[dimind]*ddbox->box_size[dim];
 +    if (dim >= ddbox->nboundeddim)
 +    {
 +        comm->cell_x0[dim] += ddbox->box0[dim];
 +        comm->cell_x1[dim] += ddbox->box0[dim];
 +    }
 +}
 +
 +static void distribute_dd_cell_sizes_dlb(gmx_domdec_t *dd,
 +                                         int d, int dim, real *cell_f_row,
 +                                         gmx_ddbox_t *ddbox)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                d1, dim1, pos;
 +
 +    comm = dd->comm;
 +
 +#ifdef GMX_MPI
 +    /* Each node would only need to know two fractions,
 +     * but it is probably cheaper to broadcast the whole array.
 +     */
 +    MPI_Bcast(cell_f_row, DD_CELL_F_SIZE(dd, d)*sizeof(real), MPI_BYTE,
 +              0, comm->mpi_comm_load[d]);
 +#endif
 +    /* Copy the fractions for this dimension from the buffer */
 +    comm->cell_f0[d] = cell_f_row[dd->ci[dim]  ];
 +    comm->cell_f1[d] = cell_f_row[dd->ci[dim]+1];
 +    /* The whole array was communicated, so set the buffer position */
 +    pos = dd->nc[dim] + 1;
 +    for (d1 = 0; d1 <= d; d1++)
 +    {
 +        if (d1 < d)
 +        {
 +            /* Copy the cell fractions of the lower dimensions */
 +            comm->cell_f0[d1] = cell_f_row[pos++];
 +            comm->cell_f1[d1] = cell_f_row[pos++];
 +        }
 +        relative_to_absolute_cell_bounds(dd, ddbox, d1);
 +    }
 +    /* Convert the communicated shift from float to int */
 +    comm->ddpme[0].maxshift = (int)(cell_f_row[pos++] + 0.5);
 +    if (d >= 1)
 +    {
 +        comm->ddpme[1].maxshift = (int)(cell_f_row[pos++] + 0.5);
 +    }
 +}
 +
 +static void set_dd_cell_sizes_dlb_change(gmx_domdec_t *dd,
 +                                         gmx_ddbox_t *ddbox, gmx_bool bDynamicBox,
 +                                         gmx_bool bUniform, gmx_large_int_t step)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                d, dim, d1;
 +    gmx_bool           bRowMember, bRowRoot;
 +    real              *cell_f_row;
 +
 +    comm = dd->comm;
 +
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        dim        = dd->dim[d];
 +        bRowMember = TRUE;
 +        bRowRoot   = TRUE;
 +        for (d1 = d; d1 < dd->ndim; d1++)
 +        {
 +            if (dd->ci[dd->dim[d1]] > 0)
 +            {
 +                if (d1 > d)
 +                {
 +                    bRowMember = FALSE;
 +                }
 +                bRowRoot = FALSE;
 +            }
 +        }
 +        if (bRowMember)
 +        {
 +            if (bRowRoot)
 +            {
 +                set_dd_cell_sizes_dlb_root(dd, d, dim, comm->root[d],
 +                                           ddbox, bDynamicBox, bUniform, step);
 +                cell_f_row = comm->root[d]->cell_f;
 +            }
 +            else
 +            {
 +                cell_f_row = comm->cell_f_row;
 +            }
 +            distribute_dd_cell_sizes_dlb(dd, d, dim, cell_f_row, ddbox);
 +        }
 +    }
 +}
 +
 +static void set_dd_cell_sizes_dlb_nochange(gmx_domdec_t *dd, gmx_ddbox_t *ddbox)
 +{
 +    int d;
 +
 +    /* This function assumes the box is static and should therefore
 +     * not be called when the box has changed since the last
 +     * call to dd_partition_system.
 +     */
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        relative_to_absolute_cell_bounds(dd, ddbox, d);
 +    }
 +}
 +
 +
 +
 +static void set_dd_cell_sizes_dlb(gmx_domdec_t *dd,
 +                                  gmx_ddbox_t *ddbox, gmx_bool bDynamicBox,
 +                                  gmx_bool bUniform, gmx_bool bDoDLB, gmx_large_int_t step,
 +                                  gmx_wallcycle_t wcycle)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                dim;
 +
 +    comm = dd->comm;
 +
 +    if (bDoDLB)
 +    {
 +        wallcycle_start(wcycle, ewcDDCOMMBOUND);
 +        set_dd_cell_sizes_dlb_change(dd, ddbox, bDynamicBox, bUniform, step);
 +        wallcycle_stop(wcycle, ewcDDCOMMBOUND);
 +    }
 +    else if (bDynamicBox)
 +    {
 +        set_dd_cell_sizes_dlb_nochange(dd, ddbox);
 +    }
 +
 +    /* Set the dimensions for which no DD is used */
 +    for (dim = 0; dim < DIM; dim++)
 +    {
 +        if (dd->nc[dim] == 1)
 +        {
 +            comm->cell_x0[dim] = 0;
 +            comm->cell_x1[dim] = ddbox->box_size[dim];
 +            if (dim >= ddbox->nboundeddim)
 +            {
 +                comm->cell_x0[dim] += ddbox->box0[dim];
 +                comm->cell_x1[dim] += ddbox->box0[dim];
 +            }
 +        }
 +    }
 +}
 +
 +static void realloc_comm_ind(gmx_domdec_t *dd, ivec npulse)
 +{
 +    int                    d, np, i;
 +    gmx_domdec_comm_dim_t *cd;
 +
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        cd = &dd->comm->cd[d];
 +        np = npulse[dd->dim[d]];
 +        if (np > cd->np_nalloc)
 +        {
 +            if (debug)
 +            {
 +                fprintf(debug, "(Re)allocing cd for %c to %d pulses\n",
 +                        dim2char(dd->dim[d]), np);
 +            }
 +            if (DDMASTER(dd) && cd->np_nalloc > 0)
 +            {
 +                fprintf(stderr, "\nIncreasing the number of cell to communicate in dimension %c to %d for the first time\n", dim2char(dd->dim[d]), np);
 +            }
 +            srenew(cd->ind, np);
 +            for (i = cd->np_nalloc; i < np; i++)
 +            {
 +                cd->ind[i].index  = NULL;
 +                cd->ind[i].nalloc = 0;
 +            }
 +            cd->np_nalloc = np;
 +        }
 +        cd->np = np;
 +    }
 +}
 +
 +
 +static void set_dd_cell_sizes(gmx_domdec_t *dd,
 +                              gmx_ddbox_t *ddbox, gmx_bool bDynamicBox,
 +                              gmx_bool bUniform, gmx_bool bDoDLB, gmx_large_int_t step,
 +                              gmx_wallcycle_t wcycle)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                d;
 +    ivec               npulse;
 +
 +    comm = dd->comm;
 +
 +    /* Copy the old cell boundaries for the cg displacement check */
 +    copy_rvec(comm->cell_x0, comm->old_cell_x0);
 +    copy_rvec(comm->cell_x1, comm->old_cell_x1);
 +
 +    if (comm->bDynLoadBal)
 +    {
 +        if (DDMASTER(dd))
 +        {
 +            check_box_size(dd, ddbox);
 +        }
 +        set_dd_cell_sizes_dlb(dd, ddbox, bDynamicBox, bUniform, bDoDLB, step, wcycle);
 +    }
 +    else
 +    {
 +        set_dd_cell_sizes_slb(dd, ddbox, FALSE, npulse);
 +        realloc_comm_ind(dd, npulse);
 +    }
 +
 +    if (debug)
 +    {
 +        for (d = 0; d < DIM; d++)
 +        {
 +            fprintf(debug, "cell_x[%d] %f - %f skew_fac %f\n",
 +                    d, comm->cell_x0[d], comm->cell_x1[d], ddbox->skew_fac[d]);
 +        }
 +    }
 +}
 +
 +static void comm_dd_ns_cell_sizes(gmx_domdec_t *dd,
 +                                  gmx_ddbox_t *ddbox,
 +                                  rvec cell_ns_x0, rvec cell_ns_x1,
 +                                  gmx_large_int_t step)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                dim_ind, dim;
 +
 +    comm = dd->comm;
 +
 +    for (dim_ind = 0; dim_ind < dd->ndim; dim_ind++)
 +    {
 +        dim = dd->dim[dim_ind];
 +
 +        /* Without PBC we don't have restrictions on the outer cells */
 +        if (!(dim >= ddbox->npbcdim &&
 +              (dd->ci[dim] == 0 || dd->ci[dim] == dd->nc[dim] - 1)) &&
 +            comm->bDynLoadBal &&
 +            (comm->cell_x1[dim] - comm->cell_x0[dim])*ddbox->skew_fac[dim] <
 +            comm->cellsize_min[dim])
 +        {
 +            char buf[22];
 +            gmx_fatal(FARGS, "Step %s: The %c-size (%f) times the triclinic skew factor (%f) is smaller than the smallest allowed cell size (%f) for domain decomposition grid cell %d %d %d",
 +                      gmx_step_str(step, buf), dim2char(dim),
 +                      comm->cell_x1[dim] - comm->cell_x0[dim],
 +                      ddbox->skew_fac[dim],
 +                      dd->comm->cellsize_min[dim],
 +                      dd->ci[XX], dd->ci[YY], dd->ci[ZZ]);
 +        }
 +    }
 +
 +    if ((dd->bGridJump && dd->ndim > 1) || ddbox->nboundeddim < DIM)
 +    {
 +        /* Communicate the boundaries and update cell_ns_x0/1 */
 +        dd_move_cellx(dd, ddbox, cell_ns_x0, cell_ns_x1);
 +        if (dd->bGridJump && dd->ndim > 1)
 +        {
 +            check_grid_jump(step, dd, dd->comm->cutoff, ddbox, TRUE);
 +        }
 +    }
 +}
 +
 +static void make_tric_corr_matrix(int npbcdim, matrix box, matrix tcm)
 +{
 +    if (YY < npbcdim)
 +    {
 +        tcm[YY][XX] = -box[YY][XX]/box[YY][YY];
 +    }
 +    else
 +    {
 +        tcm[YY][XX] = 0;
 +    }
 +    if (ZZ < npbcdim)
 +    {
 +        tcm[ZZ][XX] = -(box[ZZ][YY]*tcm[YY][XX] + box[ZZ][XX])/box[ZZ][ZZ];
 +        tcm[ZZ][YY] = -box[ZZ][YY]/box[ZZ][ZZ];
 +    }
 +    else
 +    {
 +        tcm[ZZ][XX] = 0;
 +        tcm[ZZ][YY] = 0;
 +    }
 +}
 +
 +static void check_screw_box(matrix box)
 +{
 +    /* Mathematical limitation */
 +    if (box[YY][XX] != 0 || box[ZZ][XX] != 0)
 +    {
 +        gmx_fatal(FARGS, "With screw pbc the unit cell can not have non-zero off-diagonal x-components");
 +    }
 +
 +    /* Limitation due to the asymmetry of the eighth shell method */
 +    if (box[ZZ][YY] != 0)
 +    {
 +        gmx_fatal(FARGS, "pbc=screw with non-zero box_zy is not supported");
 +    }
 +}
 +
 +static void distribute_cg(FILE *fplog, gmx_large_int_t step,
 +                          matrix box, ivec tric_dir, t_block *cgs, rvec pos[],
 +                          gmx_domdec_t *dd)
 +{
 +    gmx_domdec_master_t *ma;
 +    int                **tmp_ind = NULL, *tmp_nalloc = NULL;
 +    int                  i, icg, j, k, k0, k1, d, npbcdim;
 +    matrix               tcm;
 +    rvec                 box_size, cg_cm;
 +    ivec                 ind;
 +    real                 nrcg, inv_ncg, pos_d;
 +    atom_id             *cgindex;
 +    gmx_bool             bUnbounded, bScrew;
 +
 +    ma = dd->ma;
 +
 +    if (tmp_ind == NULL)
 +    {
 +        snew(tmp_nalloc, dd->nnodes);
 +        snew(tmp_ind, dd->nnodes);
 +        for (i = 0; i < dd->nnodes; i++)
 +        {
 +            tmp_nalloc[i] = over_alloc_large(cgs->nr/dd->nnodes+1);
 +            snew(tmp_ind[i], tmp_nalloc[i]);
 +        }
 +    }
 +
 +    /* Clear the count */
 +    for (i = 0; i < dd->nnodes; i++)
 +    {
 +        ma->ncg[i] = 0;
 +        ma->nat[i] = 0;
 +    }
 +
 +    make_tric_corr_matrix(dd->npbcdim, box, tcm);
 +
 +    cgindex = cgs->index;
 +
 +    /* Compute the center of geometry for all charge groups */
 +    for (icg = 0; icg < cgs->nr; icg++)
 +    {
 +        k0      = cgindex[icg];
 +        k1      = cgindex[icg+1];
 +        nrcg    = k1 - k0;
 +        if (nrcg == 1)
 +        {
 +            copy_rvec(pos[k0], cg_cm);
 +        }
 +        else
 +        {
 +            inv_ncg = 1.0/nrcg;
 +
 +            clear_rvec(cg_cm);
 +            for (k = k0; (k < k1); k++)
 +            {
 +                rvec_inc(cg_cm, pos[k]);
 +            }
 +            for (d = 0; (d < DIM); d++)
 +            {
 +                cg_cm[d] *= inv_ncg;
 +            }
 +        }
 +        /* Put the charge group in the box and determine the cell index */
 +        for (d = DIM-1; d >= 0; d--)
 +        {
 +            pos_d = cg_cm[d];
 +            if (d < dd->npbcdim)
 +            {
 +                bScrew = (dd->bScrewPBC && d == XX);
 +                if (tric_dir[d] && dd->nc[d] > 1)
 +                {
 +                    /* Use triclinic coordintates for this dimension */
 +                    for (j = d+1; j < DIM; j++)
 +                    {
 +                        pos_d += cg_cm[j]*tcm[j][d];
 +                    }
 +                }
 +                while (pos_d >= box[d][d])
 +                {
 +                    pos_d -= box[d][d];
 +                    rvec_dec(cg_cm, box[d]);
 +                    if (bScrew)
 +                    {
 +                        cg_cm[YY] = box[YY][YY] - cg_cm[YY];
 +                        cg_cm[ZZ] = box[ZZ][ZZ] - cg_cm[ZZ];
 +                    }
 +                    for (k = k0; (k < k1); k++)
 +                    {
 +                        rvec_dec(pos[k], box[d]);
 +                        if (bScrew)
 +                        {
 +                            pos[k][YY] = box[YY][YY] - pos[k][YY];
 +                            pos[k][ZZ] = box[ZZ][ZZ] - pos[k][ZZ];
 +                        }
 +                    }
 +                }
 +                while (pos_d < 0)
 +                {
 +                    pos_d += box[d][d];
 +                    rvec_inc(cg_cm, box[d]);
 +                    if (bScrew)
 +                    {
 +                        cg_cm[YY] = box[YY][YY] - cg_cm[YY];
 +                        cg_cm[ZZ] = box[ZZ][ZZ] - cg_cm[ZZ];
 +                    }
 +                    for (k = k0; (k < k1); k++)
 +                    {
 +                        rvec_inc(pos[k], box[d]);
 +                        if (bScrew)
 +                        {
 +                            pos[k][YY] = box[YY][YY] - pos[k][YY];
 +                            pos[k][ZZ] = box[ZZ][ZZ] - pos[k][ZZ];
 +                        }
 +                    }
 +                }
 +            }
 +            /* This could be done more efficiently */
 +            ind[d] = 0;
 +            while (ind[d]+1 < dd->nc[d] && pos_d >= ma->cell_x[d][ind[d]+1])
 +            {
 +                ind[d]++;
 +            }
 +        }
 +        i = dd_index(dd->nc, ind);
 +        if (ma->ncg[i] == tmp_nalloc[i])
 +        {
 +            tmp_nalloc[i] = over_alloc_large(ma->ncg[i]+1);
 +            srenew(tmp_ind[i], tmp_nalloc[i]);
 +        }
 +        tmp_ind[i][ma->ncg[i]] = icg;
 +        ma->ncg[i]++;
 +        ma->nat[i] += cgindex[icg+1] - cgindex[icg];
 +    }
 +
 +    k1 = 0;
 +    for (i = 0; i < dd->nnodes; i++)
 +    {
 +        ma->index[i] = k1;
 +        for (k = 0; k < ma->ncg[i]; k++)
 +        {
 +            ma->cg[k1++] = tmp_ind[i][k];
 +        }
 +    }
 +    ma->index[dd->nnodes] = k1;
 +
 +    for (i = 0; i < dd->nnodes; i++)
 +    {
 +        sfree(tmp_ind[i]);
 +    }
 +    sfree(tmp_ind);
 +    sfree(tmp_nalloc);
 +
 +    if (fplog)
 +    {
 +        char buf[22];
 +        fprintf(fplog, "Charge group distribution at step %s:",
 +                gmx_step_str(step, buf));
 +        for (i = 0; i < dd->nnodes; i++)
 +        {
 +            fprintf(fplog, " %d", ma->ncg[i]);
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +}
 +
 +static void get_cg_distribution(FILE *fplog, gmx_large_int_t step, gmx_domdec_t *dd,
 +                                t_block *cgs, matrix box, gmx_ddbox_t *ddbox,
 +                                rvec pos[])
 +{
 +    gmx_domdec_master_t *ma = NULL;
 +    ivec                 npulse;
 +    int                  i, cg_gl;
 +    int                 *ibuf, buf2[2] = { 0, 0 };
 +    gmx_bool             bMaster = DDMASTER(dd);
 +    if (bMaster)
 +    {
 +        ma = dd->ma;
 +
 +        if (dd->bScrewPBC)
 +        {
 +            check_screw_box(box);
 +        }
 +
 +        set_dd_cell_sizes_slb(dd, ddbox, TRUE, npulse);
 +
 +        distribute_cg(fplog, step, box, ddbox->tric_dir, cgs, pos, dd);
 +        for (i = 0; i < dd->nnodes; i++)
 +        {
 +            ma->ibuf[2*i]   = ma->ncg[i];
 +            ma->ibuf[2*i+1] = ma->nat[i];
 +        }
 +        ibuf = ma->ibuf;
 +    }
 +    else
 +    {
 +        ibuf = NULL;
 +    }
 +    dd_scatter(dd, 2*sizeof(int), ibuf, buf2);
 +
 +    dd->ncg_home = buf2[0];
 +    dd->nat_home = buf2[1];
 +    dd->ncg_tot  = dd->ncg_home;
 +    dd->nat_tot  = dd->nat_home;
 +    if (dd->ncg_home > dd->cg_nalloc || dd->cg_nalloc == 0)
 +    {
 +        dd->cg_nalloc = over_alloc_dd(dd->ncg_home);
 +        srenew(dd->index_gl, dd->cg_nalloc);
 +        srenew(dd->cgindex, dd->cg_nalloc+1);
 +    }
 +    if (bMaster)
 +    {
 +        for (i = 0; i < dd->nnodes; i++)
 +        {
 +            ma->ibuf[i]            = ma->ncg[i]*sizeof(int);
 +            ma->ibuf[dd->nnodes+i] = ma->index[i]*sizeof(int);
 +        }
 +    }
 +
 +    dd_scatterv(dd,
 +                DDMASTER(dd) ? ma->ibuf : NULL,
 +                DDMASTER(dd) ? ma->ibuf+dd->nnodes : NULL,
 +                DDMASTER(dd) ? ma->cg : NULL,
 +                dd->ncg_home*sizeof(int), dd->index_gl);
 +
 +    /* Determine the home charge group sizes */
 +    dd->cgindex[0] = 0;
 +    for (i = 0; i < dd->ncg_home; i++)
 +    {
 +        cg_gl            = dd->index_gl[i];
 +        dd->cgindex[i+1] =
 +            dd->cgindex[i] + cgs->index[cg_gl+1] - cgs->index[cg_gl];
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Home charge groups:\n");
 +        for (i = 0; i < dd->ncg_home; i++)
 +        {
 +            fprintf(debug, " %d", dd->index_gl[i]);
 +            if (i % 10 == 9)
 +            {
 +                fprintf(debug, "\n");
 +            }
 +        }
 +        fprintf(debug, "\n");
 +    }
 +}
 +
 +static int compact_and_copy_vec_at(int ncg, int *move,
 +                                   int *cgindex,
 +                                   int nvec, int vec,
 +                                   rvec *src, gmx_domdec_comm_t *comm,
 +                                   gmx_bool bCompact)
 +{
 +    int m, icg, i, i0, i1, nrcg;
 +    int home_pos;
 +    int pos_vec[DIM*2];
 +
 +    home_pos = 0;
 +
 +    for (m = 0; m < DIM*2; m++)
 +    {
 +        pos_vec[m] = 0;
 +    }
 +
 +    i0 = 0;
 +    for (icg = 0; icg < ncg; icg++)
 +    {
 +        i1 = cgindex[icg+1];
 +        m  = move[icg];
 +        if (m == -1)
 +        {
 +            if (bCompact)
 +            {
 +                /* Compact the home array in place */
 +                for (i = i0; i < i1; i++)
 +                {
 +                    copy_rvec(src[i], src[home_pos++]);
 +                }
 +            }
 +        }
 +        else
 +        {
 +            /* Copy to the communication buffer */
 +            nrcg        = i1 - i0;
 +            pos_vec[m] += 1 + vec*nrcg;
 +            for (i = i0; i < i1; i++)
 +            {
 +                copy_rvec(src[i], comm->cgcm_state[m][pos_vec[m]++]);
 +            }
 +            pos_vec[m] += (nvec - vec - 1)*nrcg;
 +        }
 +        if (!bCompact)
 +        {
 +            home_pos += i1 - i0;
 +        }
 +        i0 = i1;
 +    }
 +
 +    return home_pos;
 +}
 +
 +static int compact_and_copy_vec_cg(int ncg, int *move,
 +                                   int *cgindex,
 +                                   int nvec, rvec *src, gmx_domdec_comm_t *comm,
 +                                   gmx_bool bCompact)
 +{
 +    int m, icg, i0, i1, nrcg;
 +    int home_pos;
 +    int pos_vec[DIM*2];
 +
 +    home_pos = 0;
 +
 +    for (m = 0; m < DIM*2; m++)
 +    {
 +        pos_vec[m] = 0;
 +    }
 +
 +    i0 = 0;
 +    for (icg = 0; icg < ncg; icg++)
 +    {
 +        i1 = cgindex[icg+1];
 +        m  = move[icg];
 +        if (m == -1)
 +        {
 +            if (bCompact)
 +            {
 +                /* Compact the home array in place */
 +                copy_rvec(src[icg], src[home_pos++]);
 +            }
 +        }
 +        else
 +        {
 +            nrcg = i1 - i0;
 +            /* Copy to the communication buffer */
 +            copy_rvec(src[icg], comm->cgcm_state[m][pos_vec[m]]);
 +            pos_vec[m] += 1 + nrcg*nvec;
 +        }
 +        i0 = i1;
 +    }
 +    if (!bCompact)
 +    {
 +        home_pos = ncg;
 +    }
 +
 +    return home_pos;
 +}
 +
 +static int compact_ind(int ncg, int *move,
 +                       int *index_gl, int *cgindex,
 +                       int *gatindex,
 +                       gmx_ga2la_t ga2la, char *bLocalCG,
 +                       int *cginfo)
 +{
 +    int cg, nat, a0, a1, a, a_gl;
 +    int home_pos;
 +
 +    home_pos = 0;
 +    nat      = 0;
 +    for (cg = 0; cg < ncg; cg++)
 +    {
 +        a0 = cgindex[cg];
 +        a1 = cgindex[cg+1];
 +        if (move[cg] == -1)
 +        {
 +            /* Compact the home arrays in place.
 +             * Anything that can be done here avoids access to global arrays.
 +             */
 +            cgindex[home_pos] = nat;
 +            for (a = a0; a < a1; a++)
 +            {
 +                a_gl          = gatindex[a];
 +                gatindex[nat] = a_gl;
 +                /* The cell number stays 0, so we don't need to set it */
 +                ga2la_change_la(ga2la, a_gl, nat);
 +                nat++;
 +            }
 +            index_gl[home_pos] = index_gl[cg];
 +            cginfo[home_pos]   = cginfo[cg];
 +            /* The charge group remains local, so bLocalCG does not change */
 +            home_pos++;
 +        }
 +        else
 +        {
 +            /* Clear the global indices */
 +            for (a = a0; a < a1; a++)
 +            {
 +                ga2la_del(ga2la, gatindex[a]);
 +            }
 +            if (bLocalCG)
 +            {
 +                bLocalCG[index_gl[cg]] = FALSE;
 +            }
 +        }
 +    }
 +    cgindex[home_pos] = nat;
 +
 +    return home_pos;
 +}
 +
 +static void clear_and_mark_ind(int ncg, int *move,
 +                               int *index_gl, int *cgindex, int *gatindex,
 +                               gmx_ga2la_t ga2la, char *bLocalCG,
 +                               int *cell_index)
 +{
 +    int cg, a0, a1, a;
 +
 +    for (cg = 0; cg < ncg; cg++)
 +    {
 +        if (move[cg] >= 0)
 +        {
 +            a0 = cgindex[cg];
 +            a1 = cgindex[cg+1];
 +            /* Clear the global indices */
 +            for (a = a0; a < a1; a++)
 +            {
 +                ga2la_del(ga2la, gatindex[a]);
 +            }
 +            if (bLocalCG)
 +            {
 +                bLocalCG[index_gl[cg]] = FALSE;
 +            }
 +            /* Signal that this cg has moved using the ns cell index.
 +             * Here we set it to -1. fill_grid will change it
 +             * from -1 to NSGRID_SIGNAL_MOVED_FAC*grid->ncells.
 +             */
 +            cell_index[cg] = -1;
 +        }
 +    }
 +}
 +
 +static void print_cg_move(FILE *fplog,
 +                          gmx_domdec_t *dd,
 +                          gmx_large_int_t step, int cg, int dim, int dir,
 +                          gmx_bool bHaveLimitdAndCMOld, real limitd,
 +                          rvec cm_old, rvec cm_new, real pos_d)
 +{
 +    gmx_domdec_comm_t *comm;
 +    char               buf[22];
 +
 +    comm = dd->comm;
 +
 +    fprintf(fplog, "\nStep %s:\n", gmx_step_str(step, buf));
 +    if (bHaveLimitdAndCMOld)
 +    {
 +        fprintf(fplog, "The charge group starting at atom %d moved more than the distance allowed by the domain decomposition (%f) in direction %c\n",
 +                ddglatnr(dd, dd->cgindex[cg]), limitd, dim2char(dim));
 +    }
 +    else
 +    {
 +        fprintf(fplog, "The charge group starting at atom %d moved than the distance allowed by the domain decomposition in direction %c\n",
 +                ddglatnr(dd, dd->cgindex[cg]), dim2char(dim));
 +    }
 +    fprintf(fplog, "distance out of cell %f\n",
 +            dir == 1 ? pos_d - comm->cell_x1[dim] : pos_d - comm->cell_x0[dim]);
 +    if (bHaveLimitdAndCMOld)
 +    {
 +        fprintf(fplog, "Old coordinates: %8.3f %8.3f %8.3f\n",
 +                cm_old[XX], cm_old[YY], cm_old[ZZ]);
 +    }
 +    fprintf(fplog, "New coordinates: %8.3f %8.3f %8.3f\n",
 +            cm_new[XX], cm_new[YY], cm_new[ZZ]);
 +    fprintf(fplog, "Old cell boundaries in direction %c: %8.3f %8.3f\n",
 +            dim2char(dim),
 +            comm->old_cell_x0[dim], comm->old_cell_x1[dim]);
 +    fprintf(fplog, "New cell boundaries in direction %c: %8.3f %8.3f\n",
 +            dim2char(dim),
 +            comm->cell_x0[dim], comm->cell_x1[dim]);
 +}
 +
 +static void cg_move_error(FILE *fplog,
 +                          gmx_domdec_t *dd,
 +                          gmx_large_int_t step, int cg, int dim, int dir,
 +                          gmx_bool bHaveLimitdAndCMOld, real limitd,
 +                          rvec cm_old, rvec cm_new, real pos_d)
 +{
 +    if (fplog)
 +    {
 +        print_cg_move(fplog, dd, step, cg, dim, dir,
 +                      bHaveLimitdAndCMOld, limitd, cm_old, cm_new, pos_d);
 +    }
 +    print_cg_move(stderr, dd, step, cg, dim, dir,
 +                  bHaveLimitdAndCMOld, limitd, cm_old, cm_new, pos_d);
 +    gmx_fatal(FARGS,
 +              "A charge group moved too far between two domain decomposition steps\n"
 +              "This usually means that your system is not well equilibrated");
 +}
 +
 +static void rotate_state_atom(t_state *state, int a)
 +{
 +    int est;
 +
 +    for (est = 0; est < estNR; est++)
 +    {
 +        if (EST_DISTR(est) && (state->flags & (1<<est)))
 +        {
 +            switch (est)
 +            {
 +                case estX:
 +                    /* Rotate the complete state; for a rectangular box only */
 +                    state->x[a][YY] = state->box[YY][YY] - state->x[a][YY];
 +                    state->x[a][ZZ] = state->box[ZZ][ZZ] - state->x[a][ZZ];
 +                    break;
 +                case estV:
 +                    state->v[a][YY] = -state->v[a][YY];
 +                    state->v[a][ZZ] = -state->v[a][ZZ];
 +                    break;
 +                case estSDX:
 +                    state->sd_X[a][YY] = -state->sd_X[a][YY];
 +                    state->sd_X[a][ZZ] = -state->sd_X[a][ZZ];
 +                    break;
 +                case estCGP:
 +                    state->cg_p[a][YY] = -state->cg_p[a][YY];
 +                    state->cg_p[a][ZZ] = -state->cg_p[a][ZZ];
 +                    break;
 +                case estDISRE_INITF:
 +                case estDISRE_RM3TAV:
 +                case estORIRE_INITF:
 +                case estORIRE_DTAV:
 +                    /* These are distances, so not affected by rotation */
 +                    break;
 +                default:
 +                    gmx_incons("Unknown state entry encountered in rotate_state_atom");
 +            }
 +        }
 +    }
 +}
 +
 +static int *get_moved(gmx_domdec_comm_t *comm, int natoms)
 +{
 +    if (natoms > comm->moved_nalloc)
 +    {
 +        /* Contents should be preserved here */
 +        comm->moved_nalloc = over_alloc_dd(natoms);
 +        srenew(comm->moved, comm->moved_nalloc);
 +    }
 +
 +    return comm->moved;
 +}
 +
 +static void calc_cg_move(FILE *fplog, gmx_large_int_t step,
 +                         gmx_domdec_t *dd,
 +                         t_state *state,
 +                         ivec tric_dir, matrix tcm,
 +                         rvec cell_x0, rvec cell_x1,
 +                         rvec limitd, rvec limit0, rvec limit1,
 +                         const int *cgindex,
 +                         int cg_start, int cg_end,
 +                         rvec *cg_cm,
 +                         int *move)
 +{
 +    int      npbcdim;
 +    int      c, i, cg, k, k0, k1, d, dim, dim2, dir, d2, d3, d4, cell_d;
 +    int      mc, cdd, nrcg, ncg_recv, nat_recv, nvs, nvr, nvec, vec;
 +    int      flag;
 +    gmx_bool bScrew;
 +    ivec     dev;
 +    real     inv_ncg, pos_d;
 +    rvec     cm_new;
 +
 +    npbcdim = dd->npbcdim;
 +
 +    for (cg = cg_start; cg < cg_end; cg++)
 +    {
 +        k0   = cgindex[cg];
 +        k1   = cgindex[cg+1];
 +        nrcg = k1 - k0;
 +        if (nrcg == 1)
 +        {
 +            copy_rvec(state->x[k0], cm_new);
 +        }
 +        else
 +        {
 +            inv_ncg = 1.0/nrcg;
 +
 +            clear_rvec(cm_new);
 +            for (k = k0; (k < k1); k++)
 +            {
 +                rvec_inc(cm_new, state->x[k]);
 +            }
 +            for (d = 0; (d < DIM); d++)
 +            {
 +                cm_new[d] = inv_ncg*cm_new[d];
 +            }
 +        }
 +
 +        clear_ivec(dev);
 +        /* Do pbc and check DD cell boundary crossings */
 +        for (d = DIM-1; d >= 0; d--)
 +        {
 +            if (dd->nc[d] > 1)
 +            {
 +                bScrew = (dd->bScrewPBC && d == XX);
 +                /* Determine the location of this cg in lattice coordinates */
 +                pos_d = cm_new[d];
 +                if (tric_dir[d])
 +                {
 +                    for (d2 = d+1; d2 < DIM; d2++)
 +                    {
 +                        pos_d += cm_new[d2]*tcm[d2][d];
 +                    }
 +                }
 +                /* Put the charge group in the triclinic unit-cell */
 +                if (pos_d >= cell_x1[d])
 +                {
 +                    if (pos_d >= limit1[d])
 +                    {
 +                        cg_move_error(fplog, dd, step, cg, d, 1, TRUE, limitd[d],
 +                                      cg_cm[cg], cm_new, pos_d);
 +                    }
 +                    dev[d] = 1;
 +                    if (dd->ci[d] == dd->nc[d] - 1)
 +                    {
 +                        rvec_dec(cm_new, state->box[d]);
 +                        if (bScrew)
 +                        {
 +                            cm_new[YY] = state->box[YY][YY] - cm_new[YY];
 +                            cm_new[ZZ] = state->box[ZZ][ZZ] - cm_new[ZZ];
 +                        }
 +                        for (k = k0; (k < k1); k++)
 +                        {
 +                            rvec_dec(state->x[k], state->box[d]);
 +                            if (bScrew)
 +                            {
 +                                rotate_state_atom(state, k);
 +                            }
 +                        }
 +                    }
 +                }
 +                else if (pos_d < cell_x0[d])
 +                {
 +                    if (pos_d < limit0[d])
 +                    {
 +                        cg_move_error(fplog, dd, step, cg, d, -1, TRUE, limitd[d],
 +                                      cg_cm[cg], cm_new, pos_d);
 +                    }
 +                    dev[d] = -1;
 +                    if (dd->ci[d] == 0)
 +                    {
 +                        rvec_inc(cm_new, state->box[d]);
 +                        if (bScrew)
 +                        {
 +                            cm_new[YY] = state->box[YY][YY] - cm_new[YY];
 +                            cm_new[ZZ] = state->box[ZZ][ZZ] - cm_new[ZZ];
 +                        }
 +                        for (k = k0; (k < k1); k++)
 +                        {
 +                            rvec_inc(state->x[k], state->box[d]);
 +                            if (bScrew)
 +                            {
 +                                rotate_state_atom(state, k);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +            else if (d < npbcdim)
 +            {
 +                /* Put the charge group in the rectangular unit-cell */
 +                while (cm_new[d] >= state->box[d][d])
 +                {
 +                    rvec_dec(cm_new, state->box[d]);
 +                    for (k = k0; (k < k1); k++)
 +                    {
 +                        rvec_dec(state->x[k], state->box[d]);
 +                    }
 +                }
 +                while (cm_new[d] < 0)
 +                {
 +                    rvec_inc(cm_new, state->box[d]);
 +                    for (k = k0; (k < k1); k++)
 +                    {
 +                        rvec_inc(state->x[k], state->box[d]);
 +                    }
 +                }
 +            }
 +        }
 +
 +        copy_rvec(cm_new, cg_cm[cg]);
 +
 +        /* Determine where this cg should go */
 +        flag = 0;
 +        mc   = -1;
 +        for (d = 0; d < dd->ndim; d++)
 +        {
 +            dim = dd->dim[d];
 +            if (dev[dim] == 1)
 +            {
 +                flag |= DD_FLAG_FW(d);
 +                if (mc == -1)
 +                {
 +                    mc = d*2;
 +                }
 +            }
 +            else if (dev[dim] == -1)
 +            {
 +                flag |= DD_FLAG_BW(d);
 +                if (mc == -1)
 +                {
 +                    if (dd->nc[dim] > 2)
 +                    {
 +                        mc = d*2 + 1;
 +                    }
 +                    else
 +                    {
 +                        mc = d*2;
 +                    }
 +                }
 +            }
 +        }
 +        /* Temporarily store the flag in move */
 +        move[cg] = mc + flag;
 +    }
 +}
 +
 +static void dd_redistribute_cg(FILE *fplog, gmx_large_int_t step,
 +                               gmx_domdec_t *dd, ivec tric_dir,
 +                               t_state *state, rvec **f,
 +                               t_forcerec *fr, t_mdatoms *md,
 +                               gmx_bool bCompact,
 +                               t_nrnb *nrnb,
 +                               int *ncg_stay_home,
 +                               int *ncg_moved)
 +{
 +    int               *move;
 +    int                npbcdim;
 +    int                ncg[DIM*2], nat[DIM*2];
 +    int                c, i, cg, k, k0, k1, d, dim, dim2, dir, d2, d3, d4, cell_d;
 +    int                mc, cdd, nrcg, ncg_recv, nat_recv, nvs, nvr, nvec, vec;
 +    int                sbuf[2], rbuf[2];
 +    int                home_pos_cg, home_pos_at, buf_pos;
 +    int                flag;
 +    gmx_bool           bV = FALSE, bSDX = FALSE, bCGP = FALSE;
 +    gmx_bool           bScrew;
 +    ivec               dev;
 +    real               inv_ncg, pos_d;
 +    matrix             tcm;
 +    rvec              *cg_cm = NULL, cell_x0, cell_x1, limitd, limit0, limit1, cm_new;
 +    atom_id           *cgindex;
 +    cginfo_mb_t       *cginfo_mb;
 +    gmx_domdec_comm_t *comm;
 +    int               *moved;
 +    int                nthread, thread;
 +
 +    if (dd->bScrewPBC)
 +    {
 +        check_screw_box(state->box);
 +    }
 +
 +    comm  = dd->comm;
 +    if (fr->cutoff_scheme == ecutsGROUP)
 +    {
 +        cg_cm = fr->cg_cm;
 +    }
 +
 +    for (i = 0; i < estNR; i++)
 +    {
 +        if (EST_DISTR(i))
 +        {
 +            switch (i)
 +            {
 +                case estX: /* Always present */ break;
 +                case estV:   bV   = (state->flags & (1<<i)); break;
 +                case estSDX: bSDX = (state->flags & (1<<i)); break;
 +                case estCGP: bCGP = (state->flags & (1<<i)); break;
 +                case estLD_RNG:
 +                case estLD_RNGI:
 +                case estDISRE_INITF:
 +                case estDISRE_RM3TAV:
 +                case estORIRE_INITF:
 +                case estORIRE_DTAV:
 +                    /* No processing required */
 +                    break;
 +                default:
 +                    gmx_incons("Unknown state entry encountered in dd_redistribute_cg");
 +            }
 +        }
 +    }
 +
 +    if (dd->ncg_tot > comm->nalloc_int)
 +    {
 +        comm->nalloc_int = over_alloc_dd(dd->ncg_tot);
 +        srenew(comm->buf_int, comm->nalloc_int);
 +    }
 +    move = comm->buf_int;
 +
 +    /* Clear the count */
 +    for (c = 0; c < dd->ndim*2; c++)
 +    {
 +        ncg[c] = 0;
 +        nat[c] = 0;
 +    }
 +
 +    npbcdim = dd->npbcdim;
 +
 +    for (d = 0; (d < DIM); d++)
 +    {
 +        limitd[d] = dd->comm->cellsize_min[d];
 +        if (d >= npbcdim && dd->ci[d] == 0)
 +        {
 +            cell_x0[d] = -GMX_FLOAT_MAX;
 +        }
 +        else
 +        {
 +            cell_x0[d] = comm->cell_x0[d];
 +        }
 +        if (d >= npbcdim && dd->ci[d] == dd->nc[d] - 1)
 +        {
 +            cell_x1[d] = GMX_FLOAT_MAX;
 +        }
 +        else
 +        {
 +            cell_x1[d] = comm->cell_x1[d];
 +        }
 +        if (d < npbcdim)
 +        {
 +            limit0[d] = comm->old_cell_x0[d] - limitd[d];
 +            limit1[d] = comm->old_cell_x1[d] + limitd[d];
 +        }
 +        else
 +        {
 +            /* We check after communication if a charge group moved
 +             * more than one cell. Set the pre-comm check limit to float_max.
 +             */
 +            limit0[d] = -GMX_FLOAT_MAX;
 +            limit1[d] =  GMX_FLOAT_MAX;
 +        }
 +    }
 +
 +    make_tric_corr_matrix(npbcdim, state->box, tcm);
 +
 +    cgindex = dd->cgindex;
 +
 +    nthread = gmx_omp_nthreads_get(emntDomdec);
 +
 +    /* Compute the center of geometry for all home charge groups
 +     * and put them in the box and determine where they should go.
 +     */
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +    for (thread = 0; thread < nthread; thread++)
 +    {
 +        calc_cg_move(fplog, step, dd, state, tric_dir, tcm,
 +                     cell_x0, cell_x1, limitd, limit0, limit1,
 +                     cgindex,
 +                     ( thread   *dd->ncg_home)/nthread,
 +                     ((thread+1)*dd->ncg_home)/nthread,
 +                     fr->cutoff_scheme == ecutsGROUP ? cg_cm : state->x,
 +                     move);
 +    }
 +
 +    for (cg = 0; cg < dd->ncg_home; cg++)
 +    {
 +        if (move[cg] >= 0)
 +        {
 +            mc       = move[cg];
 +            flag     = mc & ~DD_FLAG_NRCG;
 +            mc       = mc & DD_FLAG_NRCG;
 +            move[cg] = mc;
 +
 +            if (ncg[mc]+1 > comm->cggl_flag_nalloc[mc])
 +            {
 +                comm->cggl_flag_nalloc[mc] = over_alloc_dd(ncg[mc]+1);
 +                srenew(comm->cggl_flag[mc], comm->cggl_flag_nalloc[mc]*DD_CGIBS);
 +            }
 +            comm->cggl_flag[mc][ncg[mc]*DD_CGIBS  ] = dd->index_gl[cg];
 +            /* We store the cg size in the lower 16 bits
 +             * and the place where the charge group should go
 +             * in the next 6 bits. This saves some communication volume.
 +             */
 +            nrcg = cgindex[cg+1] - cgindex[cg];
 +            comm->cggl_flag[mc][ncg[mc]*DD_CGIBS+1] = nrcg | flag;
 +            ncg[mc] += 1;
 +            nat[mc] += nrcg;
 +        }
 +    }
 +
 +    inc_nrnb(nrnb, eNR_CGCM, dd->nat_home);
 +    inc_nrnb(nrnb, eNR_RESETX, dd->ncg_home);
 +
 +    *ncg_moved = 0;
 +    for (i = 0; i < dd->ndim*2; i++)
 +    {
 +        *ncg_moved += ncg[i];
 +    }
 +
 +    nvec = 1;
 +    if (bV)
 +    {
 +        nvec++;
 +    }
 +    if (bSDX)
 +    {
 +        nvec++;
 +    }
 +    if (bCGP)
 +    {
 +        nvec++;
 +    }
 +
 +    /* Make sure the communication buffers are large enough */
 +    for (mc = 0; mc < dd->ndim*2; mc++)
 +    {
 +        nvr = ncg[mc] + nat[mc]*nvec;
 +        if (nvr > comm->cgcm_state_nalloc[mc])
 +        {
 +            comm->cgcm_state_nalloc[mc] = over_alloc_dd(nvr);
 +            srenew(comm->cgcm_state[mc], comm->cgcm_state_nalloc[mc]);
 +        }
 +    }
 +
 +    switch (fr->cutoff_scheme)
 +    {
 +        case ecutsGROUP:
 +            /* Recalculating cg_cm might be cheaper than communicating,
 +             * but that could give rise to rounding issues.
 +             */
 +            home_pos_cg =
 +                compact_and_copy_vec_cg(dd->ncg_home, move, cgindex,
 +                                        nvec, cg_cm, comm, bCompact);
 +            break;
 +        case ecutsVERLET:
 +            /* Without charge groups we send the moved atom coordinates
 +             * over twice. This is so the code below can be used without
 +             * many conditionals for both for with and without charge groups.
 +             */
 +            home_pos_cg =
 +                compact_and_copy_vec_cg(dd->ncg_home, move, cgindex,
 +                                        nvec, state->x, comm, FALSE);
 +            if (bCompact)
 +            {
 +                home_pos_cg -= *ncg_moved;
 +            }
 +            break;
 +        default:
 +            gmx_incons("unimplemented");
 +            home_pos_cg = 0;
 +    }
 +
 +    vec         = 0;
 +    home_pos_at =
 +        compact_and_copy_vec_at(dd->ncg_home, move, cgindex,
 +                                nvec, vec++, state->x, comm, bCompact);
 +    if (bV)
 +    {
 +        compact_and_copy_vec_at(dd->ncg_home, move, cgindex,
 +                                nvec, vec++, state->v, comm, bCompact);
 +    }
 +    if (bSDX)
 +    {
 +        compact_and_copy_vec_at(dd->ncg_home, move, cgindex,
 +                                nvec, vec++, state->sd_X, comm, bCompact);
 +    }
 +    if (bCGP)
 +    {
 +        compact_and_copy_vec_at(dd->ncg_home, move, cgindex,
 +                                nvec, vec++, state->cg_p, comm, bCompact);
 +    }
 +
 +    if (bCompact)
 +    {
 +        compact_ind(dd->ncg_home, move,
 +                    dd->index_gl, dd->cgindex, dd->gatindex,
 +                    dd->ga2la, comm->bLocalCG,
 +                    fr->cginfo);
 +    }
 +    else
 +    {
 +        if (fr->cutoff_scheme == ecutsVERLET)
 +        {
 +            moved = get_moved(comm, dd->ncg_home);
 +
 +            for (k = 0; k < dd->ncg_home; k++)
 +            {
 +                moved[k] = 0;
 +            }
 +        }
 +        else
 +        {
 +            moved = fr->ns.grid->cell_index;
 +        }
 +
 +        clear_and_mark_ind(dd->ncg_home, move,
 +                           dd->index_gl, dd->cgindex, dd->gatindex,
 +                           dd->ga2la, comm->bLocalCG,
 +                           moved);
 +    }
 +
 +    cginfo_mb = fr->cginfo_mb;
 +
 +    *ncg_stay_home = home_pos_cg;
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        dim      = dd->dim[d];
 +        ncg_recv = 0;
 +        nat_recv = 0;
 +        nvr      = 0;
 +        for (dir = 0; dir < (dd->nc[dim] == 2 ? 1 : 2); dir++)
 +        {
 +            cdd = d*2 + dir;
 +            /* Communicate the cg and atom counts */
 +            sbuf[0] = ncg[cdd];
 +            sbuf[1] = nat[cdd];
 +            if (debug)
 +            {
 +                fprintf(debug, "Sending ddim %d dir %d: ncg %d nat %d\n",
 +                        d, dir, sbuf[0], sbuf[1]);
 +            }
 +            dd_sendrecv_int(dd, d, dir, sbuf, 2, rbuf, 2);
 +
 +            if ((ncg_recv+rbuf[0])*DD_CGIBS > comm->nalloc_int)
 +            {
 +                comm->nalloc_int = over_alloc_dd((ncg_recv+rbuf[0])*DD_CGIBS);
 +                srenew(comm->buf_int, comm->nalloc_int);
 +            }
 +
 +            /* Communicate the charge group indices, sizes and flags */
 +            dd_sendrecv_int(dd, d, dir,
 +                            comm->cggl_flag[cdd], sbuf[0]*DD_CGIBS,
 +                            comm->buf_int+ncg_recv*DD_CGIBS, rbuf[0]*DD_CGIBS);
 +
 +            nvs = ncg[cdd] + nat[cdd]*nvec;
 +            i   = rbuf[0]  + rbuf[1] *nvec;
 +            vec_rvec_check_alloc(&comm->vbuf, nvr+i);
 +
 +            /* Communicate cgcm and state */
 +            dd_sendrecv_rvec(dd, d, dir,
 +                             comm->cgcm_state[cdd], nvs,
 +                             comm->vbuf.v+nvr, i);
 +            ncg_recv += rbuf[0];
 +            nat_recv += rbuf[1];
 +            nvr      += i;
 +        }
 +
 +        /* Process the received charge groups */
 +        buf_pos = 0;
 +        for (cg = 0; cg < ncg_recv; cg++)
 +        {
 +            flag = comm->buf_int[cg*DD_CGIBS+1];
 +
 +            if (dim >= npbcdim && dd->nc[dim] > 2)
 +            {
 +                /* No pbc in this dim and more than one domain boundary.
 +                 * We do a separate check if a charge group didn't move too far.
 +                 */
 +                if (((flag & DD_FLAG_FW(d)) &&
 +                     comm->vbuf.v[buf_pos][dim] > cell_x1[dim]) ||
 +                    ((flag & DD_FLAG_BW(d)) &&
 +                     comm->vbuf.v[buf_pos][dim] < cell_x0[dim]))
 +                {
 +                    cg_move_error(fplog, dd, step, cg, dim,
 +                                  (flag & DD_FLAG_FW(d)) ? 1 : 0,
 +                                  FALSE, 0,
 +                                  comm->vbuf.v[buf_pos],
 +                                  comm->vbuf.v[buf_pos],
 +                                  comm->vbuf.v[buf_pos][dim]);
 +                }
 +            }
 +
 +            mc = -1;
 +            if (d < dd->ndim-1)
 +            {
 +                /* Check which direction this cg should go */
 +                for (d2 = d+1; (d2 < dd->ndim && mc == -1); d2++)
 +                {
 +                    if (dd->bGridJump)
 +                    {
 +                        /* The cell boundaries for dimension d2 are not equal
 +                         * for each cell row of the lower dimension(s),
 +                         * therefore we might need to redetermine where
 +                         * this cg should go.
 +                         */
 +                        dim2 = dd->dim[d2];
 +                        /* If this cg crosses the box boundary in dimension d2
 +                         * we can use the communicated flag, so we do not
 +                         * have to worry about pbc.
 +                         */
 +                        if (!((dd->ci[dim2] == dd->nc[dim2]-1 &&
 +                               (flag & DD_FLAG_FW(d2))) ||
 +                              (dd->ci[dim2] == 0 &&
 +                               (flag & DD_FLAG_BW(d2)))))
 +                        {
 +                            /* Clear the two flags for this dimension */
 +                            flag &= ~(DD_FLAG_FW(d2) | DD_FLAG_BW(d2));
 +                            /* Determine the location of this cg
 +                             * in lattice coordinates
 +                             */
 +                            pos_d = comm->vbuf.v[buf_pos][dim2];
 +                            if (tric_dir[dim2])
 +                            {
 +                                for (d3 = dim2+1; d3 < DIM; d3++)
 +                                {
 +                                    pos_d +=
 +                                        comm->vbuf.v[buf_pos][d3]*tcm[d3][dim2];
 +                                }
 +                            }
 +                            /* Check of we are not at the box edge.
 +                             * pbc is only handled in the first step above,
 +                             * but this check could move over pbc while
 +                             * the first step did not due to different rounding.
 +                             */
 +                            if (pos_d >= cell_x1[dim2] &&
 +                                dd->ci[dim2] != dd->nc[dim2]-1)
 +                            {
 +                                flag |= DD_FLAG_FW(d2);
 +                            }
 +                            else if (pos_d < cell_x0[dim2] &&
 +                                     dd->ci[dim2] != 0)
 +                            {
 +                                flag |= DD_FLAG_BW(d2);
 +                            }
 +                            comm->buf_int[cg*DD_CGIBS+1] = flag;
 +                        }
 +                    }
 +                    /* Set to which neighboring cell this cg should go */
 +                    if (flag & DD_FLAG_FW(d2))
 +                    {
 +                        mc = d2*2;
 +                    }
 +                    else if (flag & DD_FLAG_BW(d2))
 +                    {
 +                        if (dd->nc[dd->dim[d2]] > 2)
 +                        {
 +                            mc = d2*2+1;
 +                        }
 +                        else
 +                        {
 +                            mc = d2*2;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            nrcg = flag & DD_FLAG_NRCG;
 +            if (mc == -1)
 +            {
 +                if (home_pos_cg+1 > dd->cg_nalloc)
 +                {
 +                    dd->cg_nalloc = over_alloc_dd(home_pos_cg+1);
 +                    srenew(dd->index_gl, dd->cg_nalloc);
 +                    srenew(dd->cgindex, dd->cg_nalloc+1);
 +                }
 +                /* Set the global charge group index and size */
 +                dd->index_gl[home_pos_cg]  = comm->buf_int[cg*DD_CGIBS];
 +                dd->cgindex[home_pos_cg+1] = dd->cgindex[home_pos_cg] + nrcg;
 +                /* Copy the state from the buffer */
 +                dd_check_alloc_ncg(fr, state, f, home_pos_cg+1);
 +                if (fr->cutoff_scheme == ecutsGROUP)
 +                {
 +                    cg_cm = fr->cg_cm;
 +                    copy_rvec(comm->vbuf.v[buf_pos], cg_cm[home_pos_cg]);
 +                }
 +                buf_pos++;
 +
 +                /* Set the cginfo */
 +                fr->cginfo[home_pos_cg] = ddcginfo(cginfo_mb,
 +                                                   dd->index_gl[home_pos_cg]);
 +                if (comm->bLocalCG)
 +                {
 +                    comm->bLocalCG[dd->index_gl[home_pos_cg]] = TRUE;
 +                }
 +
 +                if (home_pos_at+nrcg > state->nalloc)
 +                {
 +                    dd_realloc_state(state, f, home_pos_at+nrcg);
 +                }
 +                for (i = 0; i < nrcg; i++)
 +                {
 +                    copy_rvec(comm->vbuf.v[buf_pos++],
 +                              state->x[home_pos_at+i]);
 +                }
 +                if (bV)
 +                {
 +                    for (i = 0; i < nrcg; i++)
 +                    {
 +                        copy_rvec(comm->vbuf.v[buf_pos++],
 +                                  state->v[home_pos_at+i]);
 +                    }
 +                }
 +                if (bSDX)
 +                {
 +                    for (i = 0; i < nrcg; i++)
 +                    {
 +                        copy_rvec(comm->vbuf.v[buf_pos++],
 +                                  state->sd_X[home_pos_at+i]);
 +                    }
 +                }
 +                if (bCGP)
 +                {
 +                    for (i = 0; i < nrcg; i++)
 +                    {
 +                        copy_rvec(comm->vbuf.v[buf_pos++],
 +                                  state->cg_p[home_pos_at+i]);
 +                    }
 +                }
 +                home_pos_cg += 1;
 +                home_pos_at += nrcg;
 +            }
 +            else
 +            {
 +                /* Reallocate the buffers if necessary  */
 +                if (ncg[mc]+1 > comm->cggl_flag_nalloc[mc])
 +                {
 +                    comm->cggl_flag_nalloc[mc] = over_alloc_dd(ncg[mc]+1);
 +                    srenew(comm->cggl_flag[mc], comm->cggl_flag_nalloc[mc]*DD_CGIBS);
 +                }
 +                nvr = ncg[mc] + nat[mc]*nvec;
 +                if (nvr + 1 + nrcg*nvec > comm->cgcm_state_nalloc[mc])
 +                {
 +                    comm->cgcm_state_nalloc[mc] = over_alloc_dd(nvr + 1 + nrcg*nvec);
 +                    srenew(comm->cgcm_state[mc], comm->cgcm_state_nalloc[mc]);
 +                }
 +                /* Copy from the receive to the send buffers */
 +                memcpy(comm->cggl_flag[mc] + ncg[mc]*DD_CGIBS,
 +                       comm->buf_int + cg*DD_CGIBS,
 +                       DD_CGIBS*sizeof(int));
 +                memcpy(comm->cgcm_state[mc][nvr],
 +                       comm->vbuf.v[buf_pos],
 +                       (1+nrcg*nvec)*sizeof(rvec));
 +                buf_pos += 1 + nrcg*nvec;
 +                ncg[mc] += 1;
 +                nat[mc] += nrcg;
 +            }
 +        }
 +    }
 +
 +    /* With sorting (!bCompact) the indices are now only partially up to date
 +     * and ncg_home and nat_home are not the real count, since there are
 +     * "holes" in the arrays for the charge groups that moved to neighbors.
 +     */
 +    if (fr->cutoff_scheme == ecutsVERLET)
 +    {
 +        moved = get_moved(comm, home_pos_cg);
 +
 +        for (i = dd->ncg_home; i < home_pos_cg; i++)
 +        {
 +            moved[i] = 0;
 +        }
 +    }
 +    dd->ncg_home = home_pos_cg;
 +    dd->nat_home = home_pos_at;
 +
 +    if (debug)
 +    {
 +        fprintf(debug,
 +                "Finished repartitioning: cgs moved out %d, new home %d\n",
 +                *ncg_moved, dd->ncg_home-*ncg_moved);
 +
 +    }
 +}
 +
 +void dd_cycles_add(gmx_domdec_t *dd, float cycles, int ddCycl)
 +{
 +    dd->comm->cycl[ddCycl] += cycles;
 +    dd->comm->cycl_n[ddCycl]++;
 +    if (cycles > dd->comm->cycl_max[ddCycl])
 +    {
 +        dd->comm->cycl_max[ddCycl] = cycles;
 +    }
 +}
 +
 +static double force_flop_count(t_nrnb *nrnb)
 +{
 +    int         i;
 +    double      sum;
 +    const char *name;
 +
 +    sum = 0;
 +    for (i = 0; i < eNR_NBKERNEL_FREE_ENERGY; i++)
 +    {
 +        /* To get closer to the real timings, we half the count
 +         * for the normal loops and again half it for water loops.
 +         */
 +        name = nrnb_str(i);
 +        if (strstr(name, "W3") != NULL || strstr(name, "W4") != NULL)
 +        {
 +            sum += nrnb->n[i]*0.25*cost_nrnb(i);
 +        }
 +        else
 +        {
 +            sum += nrnb->n[i]*0.50*cost_nrnb(i);
 +        }
 +    }
 +    for (i = eNR_NBKERNEL_FREE_ENERGY; i <= eNR_NB14; i++)
 +    {
 +        name = nrnb_str(i);
 +        if (strstr(name, "W3") != NULL || strstr(name, "W4") != NULL)
 +        {
 +            sum += nrnb->n[i]*cost_nrnb(i);
 +        }
 +    }
 +    for (i = eNR_BONDS; i <= eNR_WALLS; i++)
 +    {
 +        sum += nrnb->n[i]*cost_nrnb(i);
 +    }
 +
 +    return sum;
 +}
 +
 +void dd_force_flop_start(gmx_domdec_t *dd, t_nrnb *nrnb)
 +{
 +    if (dd->comm->eFlop)
 +    {
 +        dd->comm->flop -= force_flop_count(nrnb);
 +    }
 +}
 +void dd_force_flop_stop(gmx_domdec_t *dd, t_nrnb *nrnb)
 +{
 +    if (dd->comm->eFlop)
 +    {
 +        dd->comm->flop += force_flop_count(nrnb);
 +        dd->comm->flop_n++;
 +    }
 +}
 +
 +static void clear_dd_cycle_counts(gmx_domdec_t *dd)
 +{
 +    int i;
 +
 +    for (i = 0; i < ddCyclNr; i++)
 +    {
 +        dd->comm->cycl[i]     = 0;
 +        dd->comm->cycl_n[i]   = 0;
 +        dd->comm->cycl_max[i] = 0;
 +    }
 +    dd->comm->flop   = 0;
 +    dd->comm->flop_n = 0;
 +}
 +
 +static void get_load_distribution(gmx_domdec_t *dd, gmx_wallcycle_t wcycle)
 +{
 +    gmx_domdec_comm_t *comm;
 +    gmx_domdec_load_t *load;
 +    gmx_domdec_root_t *root = NULL;
 +    int                d, dim, cid, i, pos;
 +    float              cell_frac = 0, sbuf[DD_NLOAD_MAX];
 +    gmx_bool           bSepPME;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "get_load_distribution start\n");
 +    }
 +
 +    wallcycle_start(wcycle, ewcDDCOMMLOAD);
 +
 +    comm = dd->comm;
 +
 +    bSepPME = (dd->pme_nodeid >= 0);
 +
 +    for (d = dd->ndim-1; d >= 0; d--)
 +    {
 +        dim = dd->dim[d];
 +        /* Check if we participate in the communication in this dimension */
 +        if (d == dd->ndim-1 ||
 +            (dd->ci[dd->dim[d+1]] == 0 && dd->ci[dd->dim[dd->ndim-1]] == 0))
 +        {
 +            load = &comm->load[d];
 +            if (dd->bGridJump)
 +            {
 +                cell_frac = comm->cell_f1[d] - comm->cell_f0[d];
 +            }
 +            pos = 0;
 +            if (d == dd->ndim-1)
 +            {
 +                sbuf[pos++] = dd_force_load(comm);
 +                sbuf[pos++] = sbuf[0];
 +                if (dd->bGridJump)
 +                {
 +                    sbuf[pos++] = sbuf[0];
 +                    sbuf[pos++] = cell_frac;
 +                    if (d > 0)
 +                    {
 +                        sbuf[pos++] = comm->cell_f_max0[d];
 +                        sbuf[pos++] = comm->cell_f_min1[d];
 +                    }
 +                }
 +                if (bSepPME)
 +                {
 +                    sbuf[pos++] = comm->cycl[ddCyclPPduringPME];
 +                    sbuf[pos++] = comm->cycl[ddCyclPME];
 +                }
 +            }
 +            else
 +            {
 +                sbuf[pos++] = comm->load[d+1].sum;
 +                sbuf[pos++] = comm->load[d+1].max;
 +                if (dd->bGridJump)
 +                {
 +                    sbuf[pos++] = comm->load[d+1].sum_m;
 +                    sbuf[pos++] = comm->load[d+1].cvol_min*cell_frac;
 +                    sbuf[pos++] = comm->load[d+1].flags;
 +                    if (d > 0)
 +                    {
 +                        sbuf[pos++] = comm->cell_f_max0[d];
 +                        sbuf[pos++] = comm->cell_f_min1[d];
 +                    }
 +                }
 +                if (bSepPME)
 +                {
 +                    sbuf[pos++] = comm->load[d+1].mdf;
 +                    sbuf[pos++] = comm->load[d+1].pme;
 +                }
 +            }
 +            load->nload = pos;
 +            /* Communicate a row in DD direction d.
 +             * The communicators are setup such that the root always has rank 0.
 +             */
 +#ifdef GMX_MPI
 +            MPI_Gather(sbuf, load->nload*sizeof(float), MPI_BYTE,
 +                       load->load, load->nload*sizeof(float), MPI_BYTE,
 +                       0, comm->mpi_comm_load[d]);
 +#endif
 +            if (dd->ci[dim] == dd->master_ci[dim])
 +            {
 +                /* We are the root, process this row */
 +                if (comm->bDynLoadBal)
 +                {
 +                    root = comm->root[d];
 +                }
 +                load->sum      = 0;
 +                load->max      = 0;
 +                load->sum_m    = 0;
 +                load->cvol_min = 1;
 +                load->flags    = 0;
 +                load->mdf      = 0;
 +                load->pme      = 0;
 +                pos            = 0;
 +                for (i = 0; i < dd->nc[dim]; i++)
 +                {
 +                    load->sum += load->load[pos++];
 +                    load->max  = max(load->max, load->load[pos]);
 +                    pos++;
 +                    if (dd->bGridJump)
 +                    {
 +                        if (root->bLimited)
 +                        {
 +                            /* This direction could not be load balanced properly,
 +                             * therefore we need to use the maximum iso the average load.
 +                             */
 +                            load->sum_m = max(load->sum_m, load->load[pos]);
 +                        }
 +                        else
 +                        {
 +                            load->sum_m += load->load[pos];
 +                        }
 +                        pos++;
 +                        load->cvol_min = min(load->cvol_min, load->load[pos]);
 +                        pos++;
 +                        if (d < dd->ndim-1)
 +                        {
 +                            load->flags = (int)(load->load[pos++] + 0.5);
 +                        }
 +                        if (d > 0)
 +                        {
 +                            root->cell_f_max0[i] = load->load[pos++];
 +                            root->cell_f_min1[i] = load->load[pos++];
 +                        }
 +                    }
 +                    if (bSepPME)
 +                    {
 +                        load->mdf = max(load->mdf, load->load[pos]);
 +                        pos++;
 +                        load->pme = max(load->pme, load->load[pos]);
 +                        pos++;
 +                    }
 +                }
 +                if (comm->bDynLoadBal && root->bLimited)
 +                {
 +                    load->sum_m *= dd->nc[dim];
 +                    load->flags |= (1<<d);
 +                }
 +            }
 +        }
 +    }
 +
 +    if (DDMASTER(dd))
 +    {
 +        comm->nload      += dd_load_count(comm);
 +        comm->load_step  += comm->cycl[ddCyclStep];
 +        comm->load_sum   += comm->load[0].sum;
 +        comm->load_max   += comm->load[0].max;
 +        if (comm->bDynLoadBal)
 +        {
 +            for (d = 0; d < dd->ndim; d++)
 +            {
 +                if (comm->load[0].flags & (1<<d))
 +                {
 +                    comm->load_lim[d]++;
 +                }
 +            }
 +        }
 +        if (bSepPME)
 +        {
 +            comm->load_mdf += comm->load[0].mdf;
 +            comm->load_pme += comm->load[0].pme;
 +        }
 +    }
 +
 +    wallcycle_stop(wcycle, ewcDDCOMMLOAD);
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "get_load_distribution finished\n");
 +    }
 +}
 +
 +static float dd_force_imb_perf_loss(gmx_domdec_t *dd)
 +{
 +    /* Return the relative performance loss on the total run time
 +     * due to the force calculation load imbalance.
 +     */
 +    if (dd->comm->nload > 0)
 +    {
 +        return
 +            (dd->comm->load_max*dd->nnodes - dd->comm->load_sum)/
 +            (dd->comm->load_step*dd->nnodes);
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +static void print_dd_load_av(FILE *fplog, gmx_domdec_t *dd)
 +{
 +    char               buf[STRLEN];
 +    int                npp, npme, nnodes, d, limp;
 +    float              imbal, pme_f_ratio, lossf, lossp = 0;
 +    gmx_bool           bLim;
 +    gmx_domdec_comm_t *comm;
 +
 +    comm = dd->comm;
 +    if (DDMASTER(dd) && comm->nload > 0)
 +    {
 +        npp    = dd->nnodes;
 +        npme   = (dd->pme_nodeid >= 0) ? comm->npmenodes : 0;
 +        nnodes = npp + npme;
 +        imbal  = comm->load_max*npp/comm->load_sum - 1;
 +        lossf  = dd_force_imb_perf_loss(dd);
 +        sprintf(buf, " Average load imbalance: %.1f %%\n", imbal*100);
 +        fprintf(fplog, "%s", buf);
 +        fprintf(stderr, "\n");
 +        fprintf(stderr, "%s", buf);
 +        sprintf(buf, " Part of the total run time spent waiting due to load imbalance: %.1f %%\n", lossf*100);
 +        fprintf(fplog, "%s", buf);
 +        fprintf(stderr, "%s", buf);
 +        bLim = FALSE;
 +        if (comm->bDynLoadBal)
 +        {
 +            sprintf(buf, " Steps where the load balancing was limited by -rdd, -rcon and/or -dds:");
 +            for (d = 0; d < dd->ndim; d++)
 +            {
 +                limp = (200*comm->load_lim[d]+1)/(2*comm->nload);
 +                sprintf(buf+strlen(buf), " %c %d %%", dim2char(dd->dim[d]), limp);
 +                if (limp >= 50)
 +                {
 +                    bLim = TRUE;
 +                }
 +            }
 +            sprintf(buf+strlen(buf), "\n");
 +            fprintf(fplog, "%s", buf);
 +            fprintf(stderr, "%s", buf);
 +        }
 +        if (npme > 0)
 +        {
 +            pme_f_ratio = comm->load_pme/comm->load_mdf;
 +            lossp       = (comm->load_pme -comm->load_mdf)/comm->load_step;
 +            if (lossp <= 0)
 +            {
 +                lossp *= (float)npme/(float)nnodes;
 +            }
 +            else
 +            {
 +                lossp *= (float)npp/(float)nnodes;
 +            }
 +            sprintf(buf, " Average PME mesh/force load: %5.3f\n", pme_f_ratio);
 +            fprintf(fplog, "%s", buf);
 +            fprintf(stderr, "%s", buf);
 +            sprintf(buf, " Part of the total run time spent waiting due to PP/PME imbalance: %.1f %%\n", fabs(lossp)*100);
 +            fprintf(fplog, "%s", buf);
 +            fprintf(stderr, "%s", buf);
 +        }
 +        fprintf(fplog, "\n");
 +        fprintf(stderr, "\n");
 +
 +        if (lossf >= DD_PERF_LOSS)
 +        {
 +            sprintf(buf,
 +                    "NOTE: %.1f %% of the available CPU time was lost due to load imbalance\n"
 +                    "      in the domain decomposition.\n", lossf*100);
 +            if (!comm->bDynLoadBal)
 +            {
 +                sprintf(buf+strlen(buf), "      You might want to use dynamic load balancing (option -dlb.)\n");
 +            }
 +            else if (bLim)
 +            {
 +                sprintf(buf+strlen(buf), "      You might want to decrease the cell size limit (options -rdd, -rcon and/or -dds).\n");
 +            }
 +            fprintf(fplog, "%s\n", buf);
 +            fprintf(stderr, "%s\n", buf);
 +        }
 +        if (npme > 0 && fabs(lossp) >= DD_PERF_LOSS)
 +        {
 +            sprintf(buf,
 +                    "NOTE: %.1f %% performance was lost because the PME nodes\n"
 +                    "      had %s work to do than the PP nodes.\n"
 +                    "      You might want to %s the number of PME nodes\n"
 +                    "      or %s the cut-off and the grid spacing.\n",
 +                    fabs(lossp*100),
 +                    (lossp < 0) ? "less"     : "more",
 +                    (lossp < 0) ? "decrease" : "increase",
 +                    (lossp < 0) ? "decrease" : "increase");
 +            fprintf(fplog, "%s\n", buf);
 +            fprintf(stderr, "%s\n", buf);
 +        }
 +    }
 +}
 +
 +static float dd_vol_min(gmx_domdec_t *dd)
 +{
 +    return dd->comm->load[0].cvol_min*dd->nnodes;
 +}
 +
 +static gmx_bool dd_load_flags(gmx_domdec_t *dd)
 +{
 +    return dd->comm->load[0].flags;
 +}
 +
 +static float dd_f_imbal(gmx_domdec_t *dd)
 +{
 +    return dd->comm->load[0].max*dd->nnodes/dd->comm->load[0].sum - 1;
 +}
 +
 +float dd_pme_f_ratio(gmx_domdec_t *dd)
 +{
 +    if (dd->comm->cycl_n[ddCyclPME] > 0)
 +    {
 +        return dd->comm->load[0].pme/dd->comm->load[0].mdf;
 +    }
 +    else
 +    {
 +        return -1.0;
 +    }
 +}
 +
 +static void dd_print_load(FILE *fplog, gmx_domdec_t *dd, gmx_large_int_t step)
 +{
 +    int  flags, d;
 +    char buf[22];
 +
 +    flags = dd_load_flags(dd);
 +    if (flags)
 +    {
 +        fprintf(fplog,
 +                "DD  load balancing is limited by minimum cell size in dimension");
 +        for (d = 0; d < dd->ndim; d++)
 +        {
 +            if (flags & (1<<d))
 +            {
 +                fprintf(fplog, " %c", dim2char(dd->dim[d]));
 +            }
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +    fprintf(fplog, "DD  step %s", gmx_step_str(step, buf));
 +    if (dd->comm->bDynLoadBal)
 +    {
 +        fprintf(fplog, "  vol min/aver %5.3f%c",
 +                dd_vol_min(dd), flags ? '!' : ' ');
 +    }
 +    fprintf(fplog, " load imb.: force %4.1f%%", dd_f_imbal(dd)*100);
 +    if (dd->comm->cycl_n[ddCyclPME])
 +    {
 +        fprintf(fplog, "  pme mesh/force %5.3f", dd_pme_f_ratio(dd));
 +    }
 +    fprintf(fplog, "\n\n");
 +}
 +
 +static void dd_print_load_verbose(gmx_domdec_t *dd)
 +{
 +    if (dd->comm->bDynLoadBal)
 +    {
 +        fprintf(stderr, "vol %4.2f%c ",
 +                dd_vol_min(dd), dd_load_flags(dd) ? '!' : ' ');
 +    }
 +    fprintf(stderr, "imb F %2d%% ", (int)(dd_f_imbal(dd)*100+0.5));
 +    if (dd->comm->cycl_n[ddCyclPME])
 +    {
 +        fprintf(stderr, "pme/F %4.2f ", dd_pme_f_ratio(dd));
 +    }
 +}
 +
 +#ifdef GMX_MPI
 +static void make_load_communicator(gmx_domdec_t *dd, int dim_ind, ivec loc)
 +{
 +    MPI_Comm           c_row;
 +    int                dim, i, rank;
 +    ivec               loc_c;
 +    gmx_domdec_root_t *root;
 +    gmx_bool           bPartOfGroup = FALSE;
 +
 +    dim = dd->dim[dim_ind];
 +    copy_ivec(loc, loc_c);
 +    for (i = 0; i < dd->nc[dim]; i++)
 +    {
 +        loc_c[dim] = i;
 +        rank       = dd_index(dd->nc, loc_c);
 +        if (rank == dd->rank)
 +        {
 +            /* This process is part of the group */
 +            bPartOfGroup = TRUE;
 +        }
 +    }
 +    MPI_Comm_split(dd->mpi_comm_all, bPartOfGroup ? 0 : MPI_UNDEFINED, dd->rank,
 +                   &c_row);
 +    if (bPartOfGroup)
 +    {
 +        dd->comm->mpi_comm_load[dim_ind] = c_row;
 +        if (dd->comm->eDLB != edlbNO)
 +        {
 +            if (dd->ci[dim] == dd->master_ci[dim])
 +            {
 +                /* This is the root process of this row */
 +                snew(dd->comm->root[dim_ind], 1);
 +                root = dd->comm->root[dim_ind];
 +                snew(root->cell_f, DD_CELL_F_SIZE(dd, dim_ind));
 +                snew(root->old_cell_f, dd->nc[dim]+1);
 +                snew(root->bCellMin, dd->nc[dim]);
 +                if (dim_ind > 0)
 +                {
 +                    snew(root->cell_f_max0, dd->nc[dim]);
 +                    snew(root->cell_f_min1, dd->nc[dim]);
 +                    snew(root->bound_min, dd->nc[dim]);
 +                    snew(root->bound_max, dd->nc[dim]);
 +                }
 +                snew(root->buf_ncd, dd->nc[dim]);
 +            }
 +            else
 +            {
 +                /* This is not a root process, we only need to receive cell_f */
 +                snew(dd->comm->cell_f_row, DD_CELL_F_SIZE(dd, dim_ind));
 +            }
 +        }
 +        if (dd->ci[dim] == dd->master_ci[dim])
 +        {
 +            snew(dd->comm->load[dim_ind].load, dd->nc[dim]*DD_NLOAD_MAX);
 +        }
 +    }
 +}
 +#endif
 +
 +static void make_load_communicators(gmx_domdec_t *dd)
 +{
 +#ifdef GMX_MPI
 +    int  dim0, dim1, i, j;
 +    ivec loc;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Making load communicators\n");
 +    }
 +
 +    snew(dd->comm->load, dd->ndim);
 +    snew(dd->comm->mpi_comm_load, dd->ndim);
 +
 +    clear_ivec(loc);
 +    make_load_communicator(dd, 0, loc);
 +    if (dd->ndim > 1)
 +    {
 +        dim0 = dd->dim[0];
 +        for (i = 0; i < dd->nc[dim0]; i++)
 +        {
 +            loc[dim0] = i;
 +            make_load_communicator(dd, 1, loc);
 +        }
 +    }
 +    if (dd->ndim > 2)
 +    {
 +        dim0 = dd->dim[0];
 +        for (i = 0; i < dd->nc[dim0]; i++)
 +        {
 +            loc[dim0] = i;
 +            dim1      = dd->dim[1];
 +            for (j = 0; j < dd->nc[dim1]; j++)
 +            {
 +                loc[dim1] = j;
 +                make_load_communicator(dd, 2, loc);
 +            }
 +        }
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Finished making load communicators\n");
 +    }
 +#endif
 +}
 +
 +void setup_dd_grid(FILE *fplog, gmx_domdec_t *dd)
 +{
 +    gmx_bool                bZYX;
 +    int                     d, dim, i, j, m;
 +    ivec                    tmp, s;
 +    int                     nzone, nzonep;
 +    ivec                    dd_zp[DD_MAXIZONE];
 +    gmx_domdec_zones_t     *zones;
 +    gmx_domdec_ns_ranges_t *izone;
 +
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        dim = dd->dim[d];
 +        copy_ivec(dd->ci, tmp);
 +        tmp[dim]           = (tmp[dim] + 1) % dd->nc[dim];
 +        dd->neighbor[d][0] = ddcoord2ddnodeid(dd, tmp);
 +        copy_ivec(dd->ci, tmp);
 +        tmp[dim]           = (tmp[dim] - 1 + dd->nc[dim]) % dd->nc[dim];
 +        dd->neighbor[d][1] = ddcoord2ddnodeid(dd, tmp);
 +        if (debug)
 +        {
 +            fprintf(debug, "DD rank %d neighbor ranks in dir %d are + %d - %d\n",
 +                    dd->rank, dim,
 +                    dd->neighbor[d][0],
 +                    dd->neighbor[d][1]);
 +        }
 +    }
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "\nMaking %dD domain decomposition grid %d x %d x %d, home cell index %d %d %d\n\n",
 +                dd->ndim,
 +                dd->nc[XX], dd->nc[YY], dd->nc[ZZ],
 +                dd->ci[XX], dd->ci[YY], dd->ci[ZZ]);
 +    }
 +    switch (dd->ndim)
 +    {
 +        case 3:
 +            nzone  = dd_z3n;
 +            nzonep = dd_zp3n;
 +            for (i = 0; i < nzonep; i++)
 +            {
 +                copy_ivec(dd_zp3[i], dd_zp[i]);
 +            }
 +            break;
 +        case 2:
 +            nzone  = dd_z2n;
 +            nzonep = dd_zp2n;
 +            for (i = 0; i < nzonep; i++)
 +            {
 +                copy_ivec(dd_zp2[i], dd_zp[i]);
 +            }
 +            break;
 +        case 1:
 +            nzone  = dd_z1n;
 +            nzonep = dd_zp1n;
 +            for (i = 0; i < nzonep; i++)
 +            {
 +                copy_ivec(dd_zp1[i], dd_zp[i]);
 +            }
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "Can only do 1, 2 or 3D domain decomposition");
 +            nzone  = 0;
 +            nzonep = 0;
 +    }
 +
 +    zones = &dd->comm->zones;
 +
 +    for (i = 0; i < nzone; i++)
 +    {
 +        m = 0;
 +        clear_ivec(zones->shift[i]);
 +        for (d = 0; d < dd->ndim; d++)
 +        {
 +            zones->shift[i][dd->dim[d]] = dd_zo[i][m++];
 +        }
 +    }
 +
 +    zones->n = nzone;
 +    for (i = 0; i < nzone; i++)
 +    {
 +        for (d = 0; d < DIM; d++)
 +        {
 +            s[d] = dd->ci[d] - zones->shift[i][d];
 +            if (s[d] < 0)
 +            {
 +                s[d] += dd->nc[d];
 +            }
 +            else if (s[d] >= dd->nc[d])
 +            {
 +                s[d] -= dd->nc[d];
 +            }
 +        }
 +    }
 +    zones->nizone = nzonep;
 +    for (i = 0; i < zones->nizone; i++)
 +    {
 +        if (dd_zp[i][0] != i)
 +        {
 +            gmx_fatal(FARGS, "Internal inconsistency in the dd grid setup");
 +        }
 +        izone     = &zones->izone[i];
 +        izone->j0 = dd_zp[i][1];
 +        izone->j1 = dd_zp[i][2];
 +        for (dim = 0; dim < DIM; dim++)
 +        {
 +            if (dd->nc[dim] == 1)
 +            {
 +                /* All shifts should be allowed */
 +                izone->shift0[dim] = -1;
 +                izone->shift1[dim] = 1;
 +            }
 +            else
 +            {
 +                /*
 +                   izone->shift0[d] = 0;
 +                   izone->shift1[d] = 0;
 +                   for(j=izone->j0; j<izone->j1; j++) {
 +                   if (dd->shift[j][d] > dd->shift[i][d])
 +                   izone->shift0[d] = -1;
 +                   if (dd->shift[j][d] < dd->shift[i][d])
 +                   izone->shift1[d] = 1;
 +                   }
 +                 */
 +
 +                int shift_diff;
 +
 +                /* Assume the shift are not more than 1 cell */
 +                izone->shift0[dim] = 1;
 +                izone->shift1[dim] = -1;
 +                for (j = izone->j0; j < izone->j1; j++)
 +                {
 +                    shift_diff = zones->shift[j][dim] - zones->shift[i][dim];
 +                    if (shift_diff < izone->shift0[dim])
 +                    {
 +                        izone->shift0[dim] = shift_diff;
 +                    }
 +                    if (shift_diff > izone->shift1[dim])
 +                    {
 +                        izone->shift1[dim] = shift_diff;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if (dd->comm->eDLB != edlbNO)
 +    {
 +        snew(dd->comm->root, dd->ndim);
 +    }
 +
 +    if (dd->comm->bRecordLoad)
 +    {
 +        make_load_communicators(dd);
 +    }
 +}
 +
 +static void make_pp_communicator(FILE *fplog, t_commrec *cr, int reorder)
 +{
 +    gmx_domdec_t      *dd;
 +    gmx_domdec_comm_t *comm;
 +    int                i, rank, *buf;
 +    ivec               periods;
 +#ifdef GMX_MPI
 +    MPI_Comm           comm_cart;
 +#endif
 +
 +    dd   = cr->dd;
 +    comm = dd->comm;
 +
 +#ifdef GMX_MPI
 +    if (comm->bCartesianPP)
 +    {
 +        /* Set up cartesian communication for the particle-particle part */
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Will use a Cartesian communicator: %d x %d x %d\n",
 +                    dd->nc[XX], dd->nc[YY], dd->nc[ZZ]);
 +        }
 +
 +        for (i = 0; i < DIM; i++)
 +        {
 +            periods[i] = TRUE;
 +        }
 +        MPI_Cart_create(cr->mpi_comm_mygroup, DIM, dd->nc, periods, reorder,
 +                        &comm_cart);
 +        /* We overwrite the old communicator with the new cartesian one */
 +        cr->mpi_comm_mygroup = comm_cart;
 +    }
 +
 +    dd->mpi_comm_all = cr->mpi_comm_mygroup;
 +    MPI_Comm_rank(dd->mpi_comm_all, &dd->rank);
 +
 +    if (comm->bCartesianPP_PME)
 +    {
 +        /* Since we want to use the original cartesian setup for sim,
 +         * and not the one after split, we need to make an index.
 +         */
 +        snew(comm->ddindex2ddnodeid, dd->nnodes);
 +        comm->ddindex2ddnodeid[dd_index(dd->nc, dd->ci)] = dd->rank;
 +        gmx_sumi(dd->nnodes, comm->ddindex2ddnodeid, cr);
 +        /* Get the rank of the DD master,
 +         * above we made sure that the master node is a PP node.
 +         */
 +        if (MASTER(cr))
 +        {
 +            rank = dd->rank;
 +        }
 +        else
 +        {
 +            rank = 0;
 +        }
 +        MPI_Allreduce(&rank, &dd->masterrank, 1, MPI_INT, MPI_SUM, dd->mpi_comm_all);
 +    }
 +    else if (comm->bCartesianPP)
 +    {
 +        if (cr->npmenodes == 0)
 +        {
 +            /* The PP communicator is also
 +             * the communicator for this simulation
 +             */
 +            cr->mpi_comm_mysim = cr->mpi_comm_mygroup;
 +        }
 +        cr->nodeid = dd->rank;
 +
 +        MPI_Cart_coords(dd->mpi_comm_all, dd->rank, DIM, dd->ci);
 +
 +        /* We need to make an index to go from the coordinates
 +         * to the nodeid of this simulation.
 +         */
 +        snew(comm->ddindex2simnodeid, dd->nnodes);
 +        snew(buf, dd->nnodes);
 +        if (cr->duty & DUTY_PP)
 +        {
 +            buf[dd_index(dd->nc, dd->ci)] = cr->sim_nodeid;
 +        }
 +        /* Communicate the ddindex to simulation nodeid index */
 +        MPI_Allreduce(buf, comm->ddindex2simnodeid, dd->nnodes, MPI_INT, MPI_SUM,
 +                      cr->mpi_comm_mysim);
 +        sfree(buf);
 +
 +        /* Determine the master coordinates and rank.
 +         * The DD master should be the same node as the master of this sim.
 +         */
 +        for (i = 0; i < dd->nnodes; i++)
 +        {
 +            if (comm->ddindex2simnodeid[i] == 0)
 +            {
 +                ddindex2xyz(dd->nc, i, dd->master_ci);
 +                MPI_Cart_rank(dd->mpi_comm_all, dd->master_ci, &dd->masterrank);
 +            }
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "The master rank is %d\n", dd->masterrank);
 +        }
 +    }
 +    else
 +    {
 +        /* No Cartesian communicators */
 +        /* We use the rank in dd->comm->all as DD index */
 +        ddindex2xyz(dd->nc, dd->rank, dd->ci);
 +        /* The simulation master nodeid is 0, so the DD master rank is also 0 */
 +        dd->masterrank = 0;
 +        clear_ivec(dd->master_ci);
 +    }
 +#endif
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog,
 +                "Domain decomposition nodeid %d, coordinates %d %d %d\n\n",
 +                dd->rank, dd->ci[XX], dd->ci[YY], dd->ci[ZZ]);
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug,
 +                "Domain decomposition nodeid %d, coordinates %d %d %d\n\n",
 +                dd->rank, dd->ci[XX], dd->ci[YY], dd->ci[ZZ]);
 +    }
 +}
 +
 +static void receive_ddindex2simnodeid(t_commrec *cr)
 +{
 +    gmx_domdec_t      *dd;
 +
 +    gmx_domdec_comm_t *comm;
 +    int               *buf;
 +
 +    dd   = cr->dd;
 +    comm = dd->comm;
 +
 +#ifdef GMX_MPI
 +    if (!comm->bCartesianPP_PME && comm->bCartesianPP)
 +    {
 +        snew(comm->ddindex2simnodeid, dd->nnodes);
 +        snew(buf, dd->nnodes);
 +        if (cr->duty & DUTY_PP)
 +        {
 +            buf[dd_index(dd->nc, dd->ci)] = cr->sim_nodeid;
 +        }
 +#ifdef GMX_MPI
 +        /* Communicate the ddindex to simulation nodeid index */
 +        MPI_Allreduce(buf, comm->ddindex2simnodeid, dd->nnodes, MPI_INT, MPI_SUM,
 +                      cr->mpi_comm_mysim);
 +#endif
 +        sfree(buf);
 +    }
 +#endif
 +}
 +
 +static gmx_domdec_master_t *init_gmx_domdec_master_t(gmx_domdec_t *dd,
 +                                                     int ncg, int natoms)
 +{
 +    gmx_domdec_master_t *ma;
 +    int                  i;
 +
 +    snew(ma, 1);
 +
 +    snew(ma->ncg, dd->nnodes);
 +    snew(ma->index, dd->nnodes+1);
 +    snew(ma->cg, ncg);
 +    snew(ma->nat, dd->nnodes);
 +    snew(ma->ibuf, dd->nnodes*2);
 +    snew(ma->cell_x, DIM);
 +    for (i = 0; i < DIM; i++)
 +    {
 +        snew(ma->cell_x[i], dd->nc[i]+1);
 +    }
 +
 +    if (dd->nnodes <= GMX_DD_NNODES_SENDRECV)
 +    {
 +        ma->vbuf = NULL;
 +    }
 +    else
 +    {
 +        snew(ma->vbuf, natoms);
 +    }
 +
 +    return ma;
 +}
 +
 +static void split_communicator(FILE *fplog, t_commrec *cr, int dd_node_order,
 +                               int reorder)
 +{
 +    gmx_domdec_t      *dd;
 +    gmx_domdec_comm_t *comm;
 +    int                i, rank;
 +    gmx_bool           bDiv[DIM];
 +    ivec               periods;
 +#ifdef GMX_MPI
 +    MPI_Comm           comm_cart;
 +#endif
 +
 +    dd   = cr->dd;
 +    comm = dd->comm;
 +
 +    if (comm->bCartesianPP)
 +    {
 +        for (i = 1; i < DIM; i++)
 +        {
 +            bDiv[i] = ((cr->npmenodes*dd->nc[i]) % (dd->nnodes) == 0);
 +        }
 +        if (bDiv[YY] || bDiv[ZZ])
 +        {
 +            comm->bCartesianPP_PME = TRUE;
 +            /* If we have 2D PME decomposition, which is always in x+y,
 +             * we stack the PME only nodes in z.
 +             * Otherwise we choose the direction that provides the thinnest slab
 +             * of PME only nodes as this will have the least effect
 +             * on the PP communication.
 +             * But for the PME communication the opposite might be better.
 +             */
 +            if (bDiv[ZZ] && (comm->npmenodes_y > 1 ||
 +                             !bDiv[YY] ||
 +                             dd->nc[YY] > dd->nc[ZZ]))
 +            {
 +                comm->cartpmedim = ZZ;
 +            }
 +            else
 +            {
 +                comm->cartpmedim = YY;
 +            }
 +            comm->ntot[comm->cartpmedim]
 +                += (cr->npmenodes*dd->nc[comm->cartpmedim])/dd->nnodes;
 +        }
 +        else if (fplog)
 +        {
 +            fprintf(fplog, "#pmenodes (%d) is not a multiple of nx*ny (%d*%d) or nx*nz (%d*%d)\n", cr->npmenodes, dd->nc[XX], dd->nc[YY], dd->nc[XX], dd->nc[ZZ]);
 +            fprintf(fplog,
 +                    "Will not use a Cartesian communicator for PP <-> PME\n\n");
 +        }
 +    }
 +
 +#ifdef GMX_MPI
 +    if (comm->bCartesianPP_PME)
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Will use a Cartesian communicator for PP <-> PME: %d x %d x %d\n", comm->ntot[XX], comm->ntot[YY], comm->ntot[ZZ]);
 +        }
 +
 +        for (i = 0; i < DIM; i++)
 +        {
 +            periods[i] = TRUE;
 +        }
 +        MPI_Cart_create(cr->mpi_comm_mysim, DIM, comm->ntot, periods, reorder,
 +                        &comm_cart);
 +
 +        MPI_Comm_rank(comm_cart, &rank);
 +        if (MASTERNODE(cr) && rank != 0)
 +        {
 +            gmx_fatal(FARGS, "MPI rank 0 was renumbered by MPI_Cart_create, we do not allow this");
 +        }
 +
 +        /* With this assigment we loose the link to the original communicator
 +         * which will usually be MPI_COMM_WORLD, unless have multisim.
 +         */
 +        cr->mpi_comm_mysim = comm_cart;
 +        cr->sim_nodeid     = rank;
 +
 +        MPI_Cart_coords(cr->mpi_comm_mysim, cr->sim_nodeid, DIM, dd->ci);
 +
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Cartesian nodeid %d, coordinates %d %d %d\n\n",
 +                    cr->sim_nodeid, dd->ci[XX], dd->ci[YY], dd->ci[ZZ]);
 +        }
 +
 +        if (dd->ci[comm->cartpmedim] < dd->nc[comm->cartpmedim])
 +        {
 +            cr->duty = DUTY_PP;
 +        }
 +        if (cr->npmenodes == 0 ||
 +            dd->ci[comm->cartpmedim] >= dd->nc[comm->cartpmedim])
 +        {
 +            cr->duty = DUTY_PME;
 +        }
 +
 +        /* Split the sim communicator into PP and PME only nodes */
 +        MPI_Comm_split(cr->mpi_comm_mysim,
 +                       cr->duty,
 +                       dd_index(comm->ntot, dd->ci),
 +                       &cr->mpi_comm_mygroup);
 +    }
 +    else
 +    {
 +        switch (dd_node_order)
 +        {
 +            case ddnoPP_PME:
 +                if (fplog)
 +                {
 +                    fprintf(fplog, "Order of the nodes: PP first, PME last\n");
 +                }
 +                break;
 +            case ddnoINTERLEAVE:
 +                /* Interleave the PP-only and PME-only nodes,
 +                 * as on clusters with dual-core machines this will double
 +                 * the communication bandwidth of the PME processes
 +                 * and thus speed up the PP <-> PME and inter PME communication.
 +                 */
 +                if (fplog)
 +                {
 +                    fprintf(fplog, "Interleaving PP and PME nodes\n");
 +                }
 +                comm->pmenodes = dd_pmenodes(cr);
 +                break;
 +            case ddnoCARTESIAN:
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Unknown dd_node_order=%d", dd_node_order);
 +        }
 +
 +        if (dd_simnode2pmenode(cr, cr->sim_nodeid) == -1)
 +        {
 +            cr->duty = DUTY_PME;
 +        }
 +        else
 +        {
 +            cr->duty = DUTY_PP;
 +        }
 +
 +        /* Split the sim communicator into PP and PME only nodes */
 +        MPI_Comm_split(cr->mpi_comm_mysim,
 +                       cr->duty,
 +                       cr->nodeid,
 +                       &cr->mpi_comm_mygroup);
 +        MPI_Comm_rank(cr->mpi_comm_mygroup, &cr->nodeid);
 +    }
 +#endif
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "This is a %s only node\n\n",
 +                (cr->duty & DUTY_PP) ? "particle-particle" : "PME-mesh");
 +    }
 +}
 +
 +void make_dd_communicators(FILE *fplog, t_commrec *cr, int dd_node_order)
 +{
 +    gmx_domdec_t      *dd;
 +    gmx_domdec_comm_t *comm;
 +    int                CartReorder;
 +
 +    dd   = cr->dd;
 +    comm = dd->comm;
 +
 +    copy_ivec(dd->nc, comm->ntot);
 +
 +    comm->bCartesianPP     = (dd_node_order == ddnoCARTESIAN);
 +    comm->bCartesianPP_PME = FALSE;
 +
 +    /* Reorder the nodes by default. This might change the MPI ranks.
 +     * Real reordering is only supported on very few architectures,
 +     * Blue Gene is one of them.
 +     */
 +    CartReorder = (getenv("GMX_NO_CART_REORDER") == NULL);
 +
 +    if (cr->npmenodes > 0)
 +    {
 +        /* Split the communicator into a PP and PME part */
 +        split_communicator(fplog, cr, dd_node_order, CartReorder);
 +        if (comm->bCartesianPP_PME)
 +        {
 +            /* We (possibly) reordered the nodes in split_communicator,
 +             * so it is no longer required in make_pp_communicator.
 +             */
 +            CartReorder = FALSE;
 +        }
 +    }
 +    else
 +    {
 +        /* All nodes do PP and PME */
 +#ifdef GMX_MPI
 +        /* We do not require separate communicators */
 +        cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
 +#endif
 +    }
 +
 +    if (cr->duty & DUTY_PP)
 +    {
 +        /* Copy or make a new PP communicator */
 +        make_pp_communicator(fplog, cr, CartReorder);
 +    }
 +    else
 +    {
 +        receive_ddindex2simnodeid(cr);
 +    }
 +
 +    if (!(cr->duty & DUTY_PME))
 +    {
 +        /* Set up the commnuication to our PME node */
 +        dd->pme_nodeid           = dd_simnode2pmenode(cr, cr->sim_nodeid);
 +        dd->pme_receive_vir_ener = receive_vir_ener(cr);
 +        if (debug)
 +        {
 +            fprintf(debug, "My pme_nodeid %d receive ener %d\n",
 +                    dd->pme_nodeid, dd->pme_receive_vir_ener);
 +        }
 +    }
 +    else
 +    {
 +        dd->pme_nodeid = -1;
 +    }
 +
 +    if (DDMASTER(dd))
 +    {
 +        dd->ma = init_gmx_domdec_master_t(dd,
 +                                          comm->cgs_gl.nr,
 +                                          comm->cgs_gl.index[comm->cgs_gl.nr]);
 +    }
 +}
 +
 +static real *get_slb_frac(FILE *fplog, const char *dir, int nc, const char *size_string)
 +{
 +    real  *slb_frac, tot;
 +    int    i, n;
 +    double dbl;
 +
 +    slb_frac = NULL;
 +    if (nc > 1 && size_string != NULL)
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Using static load balancing for the %s direction\n",
 +                    dir);
 +        }
 +        snew(slb_frac, nc);
 +        tot = 0;
 +        for (i = 0; i < nc; i++)
 +        {
 +            dbl = 0;
 +            sscanf(size_string, "%lf%n", &dbl, &n);
 +            if (dbl == 0)
 +            {
 +                gmx_fatal(FARGS, "Incorrect or not enough DD cell size entries for direction %s: '%s'", dir, size_string);
 +            }
 +            slb_frac[i]  = dbl;
 +            size_string += n;
 +            tot         += slb_frac[i];
 +        }
 +        /* Normalize */
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Relative cell sizes:");
 +        }
 +        for (i = 0; i < nc; i++)
 +        {
 +            slb_frac[i] /= tot;
 +            if (fplog)
 +            {
 +                fprintf(fplog, " %5.3f", slb_frac[i]);
 +            }
 +        }
 +        if (fplog)
 +        {
 +            fprintf(fplog, "\n");
 +        }
 +    }
 +
 +    return slb_frac;
 +}
 +
 +static int multi_body_bondeds_count(gmx_mtop_t *mtop)
 +{
 +    int                  n, nmol, ftype;
 +    gmx_mtop_ilistloop_t iloop;
 +    t_ilist             *il;
 +
 +    n     = 0;
 +    iloop = gmx_mtop_ilistloop_init(mtop);
 +    while (gmx_mtop_ilistloop_next(iloop, &il, &nmol))
 +    {
 +        for (ftype = 0; ftype < F_NRE; ftype++)
 +        {
 +            if ((interaction_function[ftype].flags & IF_BOND) &&
 +                NRAL(ftype) >  2)
 +            {
 +                n += nmol*il[ftype].nr/(1 + NRAL(ftype));
 +            }
 +        }
 +    }
 +
 +    return n;
 +}
 +
 +static int dd_nst_env(FILE *fplog, const char *env_var, int def)
 +{
 +    char *val;
 +    int   nst;
 +
 +    nst = def;
 +    val = getenv(env_var);
 +    if (val)
 +    {
 +        if (sscanf(val, "%d", &nst) <= 0)
 +        {
 +            nst = 1;
 +        }
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Found env.var. %s = %s, using value %d\n",
 +                    env_var, val, nst);
 +        }
 +    }
 +
 +    return nst;
 +}
 +
 +static void dd_warning(t_commrec *cr, FILE *fplog, const char *warn_string)
 +{
 +    if (MASTER(cr))
 +    {
 +        fprintf(stderr, "\n%s\n", warn_string);
 +    }
 +    if (fplog)
 +    {
 +        fprintf(fplog, "\n%s\n", warn_string);
 +    }
 +}
 +
 +static void check_dd_restrictions(t_commrec *cr, gmx_domdec_t *dd,
 +                                  t_inputrec *ir, FILE *fplog)
 +{
 +    if (ir->ePBC == epbcSCREW &&
 +        (dd->nc[XX] == 1 || dd->nc[YY] > 1 || dd->nc[ZZ] > 1))
 +    {
 +        gmx_fatal(FARGS, "With pbc=%s can only do domain decomposition in the x-direction", epbc_names[ir->ePBC]);
 +    }
 +
 +    if (ir->ns_type == ensSIMPLE)
 +    {
 +        gmx_fatal(FARGS, "Domain decomposition does not support simple neighbor searching, use grid searching or use particle decomposition");
 +    }
 +
 +    if (ir->nstlist == 0)
 +    {
 +        gmx_fatal(FARGS, "Domain decomposition does not work with nstlist=0");
 +    }
 +
 +    if (ir->comm_mode == ecmANGULAR && ir->ePBC != epbcNONE)
 +    {
 +        dd_warning(cr, fplog, "comm-mode angular will give incorrect results when the comm group partially crosses a periodic boundary");
 +    }
 +}
 +
 +static real average_cellsize_min(gmx_domdec_t *dd, gmx_ddbox_t *ddbox)
 +{
 +    int  di, d;
 +    real r;
 +
 +    r = ddbox->box_size[XX];
 +    for (di = 0; di < dd->ndim; di++)
 +    {
 +        d = dd->dim[di];
 +        /* Check using the initial average cell size */
 +        r = min(r, ddbox->box_size[d]*ddbox->skew_fac[d]/dd->nc[d]);
 +    }
 +
 +    return r;
 +}
 +
 +static int check_dlb_support(FILE *fplog, t_commrec *cr,
 +                             const char *dlb_opt, gmx_bool bRecordLoad,
 +                             unsigned long Flags, t_inputrec *ir)
 +{
 +    gmx_domdec_t *dd;
 +    int           eDLB = -1;
 +    char          buf[STRLEN];
 +
 +    switch (dlb_opt[0])
 +    {
 +        case 'a': eDLB = edlbAUTO; break;
 +        case 'n': eDLB = edlbNO;   break;
 +        case 'y': eDLB = edlbYES;  break;
 +        default: gmx_incons("Unknown dlb_opt");
 +    }
 +
 +    if (Flags & MD_RERUN)
 +    {
 +        return edlbNO;
 +    }
 +
 +    if (!EI_DYNAMICS(ir->eI))
 +    {
 +        if (eDLB == edlbYES)
 +        {
 +            sprintf(buf, "NOTE: dynamic load balancing is only supported with dynamics, not with integrator '%s'\n", EI(ir->eI));
 +            dd_warning(cr, fplog, buf);
 +        }
 +
 +        return edlbNO;
 +    }
 +
 +    if (!bRecordLoad)
 +    {
 +        dd_warning(cr, fplog, "NOTE: Cycle counting is not supported on this architecture, will not use dynamic load balancing\n");
 +
 +        return edlbNO;
 +    }
 +
 +    if (Flags & MD_REPRODUCIBLE)
 +    {
 +        switch (eDLB)
 +        {
 +            case edlbNO:
 +                break;
 +            case edlbAUTO:
 +                dd_warning(cr, fplog, "NOTE: reproducibility requested, will not use dynamic load balancing\n");
 +                eDLB = edlbNO;
 +                break;
 +            case edlbYES:
 +                dd_warning(cr, fplog, "WARNING: reproducibility requested with dynamic load balancing, the simulation will NOT be binary reproducible\n");
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Death horror: undefined case (%d) for load balancing choice", eDLB);
 +                break;
 +        }
 +    }
 +
 +    return eDLB;
 +}
 +
 +static void set_dd_dim(FILE *fplog, gmx_domdec_t *dd)
 +{
 +    int dim;
 +
 +    dd->ndim = 0;
 +    if (getenv("GMX_DD_ORDER_ZYX") != NULL)
 +    {
 +        /* Decomposition order z,y,x */
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Using domain decomposition order z, y, x\n");
 +        }
 +        for (dim = DIM-1; dim >= 0; dim--)
 +        {
 +            if (dd->nc[dim] > 1)
 +            {
 +                dd->dim[dd->ndim++] = dim;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        /* Decomposition order x,y,z */
 +        for (dim = 0; dim < DIM; dim++)
 +        {
 +            if (dd->nc[dim] > 1)
 +            {
 +                dd->dim[dd->ndim++] = dim;
 +            }
 +        }
 +    }
 +}
 +
 +static gmx_domdec_comm_t *init_dd_comm()
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                i;
 +
 +    snew(comm, 1);
 +    snew(comm->cggl_flag, DIM*2);
 +    snew(comm->cgcm_state, DIM*2);
 +    for (i = 0; i < DIM*2; i++)
 +    {
 +        comm->cggl_flag_nalloc[i]  = 0;
 +        comm->cgcm_state_nalloc[i] = 0;
 +    }
 +
 +    comm->nalloc_int = 0;
 +    comm->buf_int    = NULL;
 +
 +    vec_rvec_init(&comm->vbuf);
 +
 +    comm->n_load_have    = 0;
 +    comm->n_load_collect = 0;
 +
 +    for (i = 0; i < ddnatNR-ddnatZONE; i++)
 +    {
 +        comm->sum_nat[i] = 0;
 +    }
 +    comm->ndecomp   = 0;
 +    comm->nload     = 0;
 +    comm->load_step = 0;
 +    comm->load_sum  = 0;
 +    comm->load_max  = 0;
 +    clear_ivec(comm->load_lim);
 +    comm->load_mdf  = 0;
 +    comm->load_pme  = 0;
 +
 +    return comm;
 +}
 +
 +gmx_domdec_t *init_domain_decomposition(FILE *fplog, t_commrec *cr,
 +                                        unsigned long Flags,
 +                                        ivec nc,
 +                                        real comm_distance_min, real rconstr,
 +                                        const char *dlb_opt, real dlb_scale,
 +                                        const char *sizex, const char *sizey, const char *sizez,
 +                                        gmx_mtop_t *mtop, t_inputrec *ir,
 +                                        matrix box, rvec *x,
 +                                        gmx_ddbox_t *ddbox,
 +                                        int *npme_x, int *npme_y)
 +{
 +    gmx_domdec_t      *dd;
 +    gmx_domdec_comm_t *comm;
 +    int                recload;
 +    int                d, i, j;
 +    real               r_2b, r_mb, r_bonded = -1, r_bonded_limit = -1, limit, acs;
 +    gmx_bool           bC;
 +    char               buf[STRLEN];
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog,
 +                "\nInitializing Domain Decomposition on %d nodes\n", cr->nnodes);
 +    }
 +
 +    snew(dd, 1);
 +
 +    dd->comm = init_dd_comm();
 +    comm     = dd->comm;
 +    snew(comm->cggl_flag, DIM*2);
 +    snew(comm->cgcm_state, DIM*2);
 +
 +    dd->npbcdim   = ePBC2npbcdim(ir->ePBC);
 +    dd->bScrewPBC = (ir->ePBC == epbcSCREW);
 +
 +    dd->bSendRecv2      = dd_nst_env(fplog, "GMX_DD_SENDRECV2", 0);
 +    comm->dlb_scale_lim = dd_nst_env(fplog, "GMX_DLB_MAX", 10);
 +    comm->eFlop         = dd_nst_env(fplog, "GMX_DLB_FLOP", 0);
 +    recload             = dd_nst_env(fplog, "GMX_DD_LOAD", 1);
 +    comm->nstSortCG     = dd_nst_env(fplog, "GMX_DD_SORT", 1);
 +    comm->nstDDDump     = dd_nst_env(fplog, "GMX_DD_DUMP", 0);
 +    comm->nstDDDumpGrid = dd_nst_env(fplog, "GMX_DD_DUMP_GRID", 0);
 +    comm->DD_debug      = dd_nst_env(fplog, "GMX_DD_DEBUG", 0);
 +
 +    dd->pme_recv_f_alloc = 0;
 +    dd->pme_recv_f_buf   = NULL;
 +
 +    if (dd->bSendRecv2 && fplog)
 +    {
 +        fprintf(fplog, "Will use two sequential MPI_Sendrecv calls instead of two simultaneous non-blocking MPI_Irecv and MPI_Isend pairs for constraint and vsite communication\n");
 +    }
 +    if (comm->eFlop)
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Will load balance based on FLOP count\n");
 +        }
 +        if (comm->eFlop > 1)
 +        {
 +            srand(1+cr->nodeid);
 +        }
 +        comm->bRecordLoad = TRUE;
 +    }
 +    else
 +    {
 +        comm->bRecordLoad = (wallcycle_have_counter() && recload > 0);
 +
 +    }
 +
 +    comm->eDLB = check_dlb_support(fplog, cr, dlb_opt, comm->bRecordLoad, Flags, ir);
 +
 +    comm->bDynLoadBal = (comm->eDLB == edlbYES);
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Dynamic load balancing: %s\n", edlb_names[comm->eDLB]);
 +    }
 +    dd->bGridJump              = comm->bDynLoadBal;
 +    comm->bPMELoadBalDLBLimits = FALSE;
 +
 +    if (comm->nstSortCG)
 +    {
 +        if (fplog)
 +        {
 +            if (comm->nstSortCG == 1)
 +            {
 +                fprintf(fplog, "Will sort the charge groups at every domain (re)decomposition\n");
 +            }
 +            else
 +            {
 +                fprintf(fplog, "Will sort the charge groups every %d steps\n",
 +                        comm->nstSortCG);
 +            }
 +        }
 +        snew(comm->sort, 1);
 +    }
 +    else
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "Will not sort the charge groups\n");
 +        }
 +    }
 +
 +    comm->bCGs = (ncg_mtop(mtop) < mtop->natoms);
 +
 +    comm->bInterCGBondeds = (ncg_mtop(mtop) > mtop->mols.nr);
 +    if (comm->bInterCGBondeds)
 +    {
 +        comm->bInterCGMultiBody = (multi_body_bondeds_count(mtop) > 0);
 +    }
 +    else
 +    {
 +        comm->bInterCGMultiBody = FALSE;
 +    }
 +
 +    dd->bInterCGcons    = inter_charge_group_constraints(mtop);
 +    dd->bInterCGsettles = inter_charge_group_settles(mtop);
 +
 +    if (ir->rlistlong == 0)
 +    {
 +        /* Set the cut-off to some very large value,
 +         * so we don't need if statements everywhere in the code.
 +         * We use sqrt, since the cut-off is squared in some places.
 +         */
 +        comm->cutoff   = GMX_CUTOFF_INF;
 +    }
 +    else
 +    {
 +        comm->cutoff   = ir->rlistlong;
 +    }
 +    comm->cutoff_mbody = 0;
 +
 +    comm->cellsize_limit = 0;
 +    comm->bBondComm      = FALSE;
 +
 +    if (comm->bInterCGBondeds)
 +    {
 +        if (comm_distance_min > 0)
 +        {
 +            comm->cutoff_mbody = comm_distance_min;
 +            if (Flags & MD_DDBONDCOMM)
 +            {
 +                comm->bBondComm = (comm->cutoff_mbody > comm->cutoff);
 +            }
 +            else
 +            {
 +                comm->cutoff = max(comm->cutoff, comm->cutoff_mbody);
 +            }
 +            r_bonded_limit = comm->cutoff_mbody;
 +        }
 +        else if (ir->bPeriodicMols)
 +        {
 +            /* Can not easily determine the required cut-off */
 +            dd_warning(cr, fplog, "NOTE: Periodic molecules are present in this system. Because of this, the domain decomposition algorithm cannot easily determine the minimum cell size that it requires for treating bonded interactions. Instead, domain decomposition will assume that half the non-bonded cut-off will be a suitable lower bound.\n");
 +            comm->cutoff_mbody = comm->cutoff/2;
 +            r_bonded_limit     = comm->cutoff_mbody;
 +        }
 +        else
 +        {
 +            if (MASTER(cr))
 +            {
 +                dd_bonded_cg_distance(fplog, dd, mtop, ir, x, box,
 +                                      Flags & MD_DDBONDCHECK, &r_2b, &r_mb);
 +            }
 +            gmx_bcast(sizeof(r_2b), &r_2b, cr);
 +            gmx_bcast(sizeof(r_mb), &r_mb, cr);
 +
 +            /* We use an initial margin of 10% for the minimum cell size,
 +             * except when we are just below the non-bonded cut-off.
 +             */
 +            if (Flags & MD_DDBONDCOMM)
 +            {
 +                if (max(r_2b, r_mb) > comm->cutoff)
 +                {
 +                    r_bonded        = max(r_2b, r_mb);
 +                    r_bonded_limit  = 1.1*r_bonded;
 +                    comm->bBondComm = TRUE;
 +                }
 +                else
 +                {
 +                    r_bonded       = r_mb;
 +                    r_bonded_limit = min(1.1*r_bonded, comm->cutoff);
 +                }
 +                /* We determine cutoff_mbody later */
 +            }
 +            else
 +            {
 +                /* No special bonded communication,
 +                 * simply increase the DD cut-off.
 +                 */
 +                r_bonded_limit     = 1.1*max(r_2b, r_mb);
 +                comm->cutoff_mbody = r_bonded_limit;
 +                comm->cutoff       = max(comm->cutoff, comm->cutoff_mbody);
 +            }
 +        }
 +        comm->cellsize_limit = max(comm->cellsize_limit, r_bonded_limit);
 +        if (fplog)
 +        {
 +            fprintf(fplog,
 +                    "Minimum cell size due to bonded interactions: %.3f nm\n",
 +                    comm->cellsize_limit);
 +        }
 +    }
 +
 +    if (dd->bInterCGcons && rconstr <= 0)
 +    {
 +        /* There is a cell size limit due to the constraints (P-LINCS) */
 +        rconstr = constr_r_max(fplog, mtop, ir);
 +        if (fplog)
 +        {
 +            fprintf(fplog,
 +                    "Estimated maximum distance required for P-LINCS: %.3f nm\n",
 +                    rconstr);
 +            if (rconstr > comm->cellsize_limit)
 +            {
 +                fprintf(fplog, "This distance will limit the DD cell size, you can override this with -rcon\n");
 +            }
 +        }
 +    }
 +    else if (rconstr > 0 && fplog)
 +    {
 +        /* Here we do not check for dd->bInterCGcons,
 +         * because one can also set a cell size limit for virtual sites only
 +         * and at this point we don't know yet if there are intercg v-sites.
 +         */
 +        fprintf(fplog,
 +                "User supplied maximum distance required for P-LINCS: %.3f nm\n",
 +                rconstr);
 +    }
 +    comm->cellsize_limit = max(comm->cellsize_limit, rconstr);
 +
 +    comm->cgs_gl = gmx_mtop_global_cgs(mtop);
 +
 +    if (nc[XX] > 0)
 +    {
 +        copy_ivec(nc, dd->nc);
 +        set_dd_dim(fplog, dd);
 +        set_ddbox_cr(cr, &dd->nc, ir, box, &comm->cgs_gl, x, ddbox);
 +
 +        if (cr->npmenodes == -1)
 +        {
 +            cr->npmenodes = 0;
 +        }
 +        acs = average_cellsize_min(dd, ddbox);
 +        if (acs < comm->cellsize_limit)
 +        {
 +            if (fplog)
 +            {
 +                fprintf(fplog, "ERROR: The initial cell size (%f) is smaller than the cell size limit (%f)\n", acs, comm->cellsize_limit);
 +            }
 +            gmx_fatal_collective(FARGS, cr, NULL,
 +                                 "The initial cell size (%f) is smaller than the cell size limit (%f), change options -dd, -rdd or -rcon, see the log file for details",
 +                                 acs, comm->cellsize_limit);
 +        }
 +    }
 +    else
 +    {
 +        set_ddbox_cr(cr, NULL, ir, box, &comm->cgs_gl, x, ddbox);
 +
 +        /* We need to choose the optimal DD grid and possibly PME nodes */
 +        limit = dd_choose_grid(fplog, cr, dd, ir, mtop, box, ddbox,
 +                               comm->eDLB != edlbNO, dlb_scale,
 +                               comm->cellsize_limit, comm->cutoff,
 +                               comm->bInterCGBondeds, comm->bInterCGMultiBody);
 +
 +        if (dd->nc[XX] == 0)
 +        {
 +            bC = (dd->bInterCGcons && rconstr > r_bonded_limit);
 +            sprintf(buf, "Change the number of nodes or mdrun option %s%s%s",
 +                    !bC ? "-rdd" : "-rcon",
 +                    comm->eDLB != edlbNO ? " or -dds" : "",
 +                    bC ? " or your LINCS settings" : "");
 +
 +            gmx_fatal_collective(FARGS, cr, NULL,
 +                                 "There is no domain decomposition for %d nodes that is compatible with the given box and a minimum cell size of %g nm\n"
 +                                 "%s\n"
 +                                 "Look in the log file for details on the domain decomposition",
 +                                 cr->nnodes-cr->npmenodes, limit, buf);
 +        }
 +        set_dd_dim(fplog, dd);
 +    }
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog,
 +                "Domain decomposition grid %d x %d x %d, separate PME nodes %d\n",
 +                dd->nc[XX], dd->nc[YY], dd->nc[ZZ], cr->npmenodes);
 +    }
 +
 +    dd->nnodes = dd->nc[XX]*dd->nc[YY]*dd->nc[ZZ];
 +    if (cr->nnodes - dd->nnodes != cr->npmenodes)
 +    {
 +        gmx_fatal_collective(FARGS, cr, NULL,
 +                             "The size of the domain decomposition grid (%d) does not match the number of nodes (%d). The total number of nodes is %d",
 +                             dd->nnodes, cr->nnodes - cr->npmenodes, cr->nnodes);
 +    }
 +    if (cr->npmenodes > dd->nnodes)
 +    {
 +        gmx_fatal_collective(FARGS, cr, NULL,
 +                             "The number of separate PME nodes (%d) is larger than the number of PP nodes (%d), this is not supported.", cr->npmenodes, dd->nnodes);
 +    }
 +    if (cr->npmenodes > 0)
 +    {
 +        comm->npmenodes = cr->npmenodes;
 +    }
 +    else
 +    {
 +        comm->npmenodes = dd->nnodes;
 +    }
 +
 +    if (EEL_PME(ir->coulombtype))
 +    {
 +        /* The following choices should match those
 +         * in comm_cost_est in domdec_setup.c.
 +         * Note that here the checks have to take into account
 +         * that the decomposition might occur in a different order than xyz
 +         * (for instance through the env.var. GMX_DD_ORDER_ZYX),
 +         * in which case they will not match those in comm_cost_est,
 +         * but since that is mainly for testing purposes that's fine.
 +         */
 +        if (dd->ndim >= 2 && dd->dim[0] == XX && dd->dim[1] == YY &&
 +            comm->npmenodes > dd->nc[XX] && comm->npmenodes % dd->nc[XX] == 0 &&
 +            getenv("GMX_PMEONEDD") == NULL)
 +        {
 +            comm->npmedecompdim = 2;
 +            comm->npmenodes_x   = dd->nc[XX];
 +            comm->npmenodes_y   = comm->npmenodes/comm->npmenodes_x;
 +        }
 +        else
 +        {
 +            /* In case nc is 1 in both x and y we could still choose to
 +             * decompose pme in y instead of x, but we use x for simplicity.
 +             */
 +            comm->npmedecompdim = 1;
 +            if (dd->dim[0] == YY)
 +            {
 +                comm->npmenodes_x = 1;
 +                comm->npmenodes_y = comm->npmenodes;
 +            }
 +            else
 +            {
 +                comm->npmenodes_x = comm->npmenodes;
 +                comm->npmenodes_y = 1;
 +            }
 +        }
 +        if (fplog)
 +        {
 +            fprintf(fplog, "PME domain decomposition: %d x %d x %d\n",
 +                    comm->npmenodes_x, comm->npmenodes_y, 1);
 +        }
 +    }
 +    else
 +    {
 +        comm->npmedecompdim = 0;
 +        comm->npmenodes_x   = 0;
 +        comm->npmenodes_y   = 0;
 +    }
 +
 +    /* Technically we don't need both of these,
 +     * but it simplifies code not having to recalculate it.
 +     */
 +    *npme_x = comm->npmenodes_x;
 +    *npme_y = comm->npmenodes_y;
 +
 +    snew(comm->slb_frac, DIM);
 +    if (comm->eDLB == edlbNO)
 +    {
 +        comm->slb_frac[XX] = get_slb_frac(fplog, "x", dd->nc[XX], sizex);
 +        comm->slb_frac[YY] = get_slb_frac(fplog, "y", dd->nc[YY], sizey);
 +        comm->slb_frac[ZZ] = get_slb_frac(fplog, "z", dd->nc[ZZ], sizez);
 +    }
 +
 +    if (comm->bInterCGBondeds && comm->cutoff_mbody == 0)
 +    {
 +        if (comm->bBondComm || comm->eDLB != edlbNO)
 +        {
 +            /* Set the bonded communication distance to halfway
 +             * the minimum and the maximum,
 +             * since the extra communication cost is nearly zero.
 +             */
 +            acs                = average_cellsize_min(dd, ddbox);
 +            comm->cutoff_mbody = 0.5*(r_bonded + acs);
 +            if (comm->eDLB != edlbNO)
 +            {
 +                /* Check if this does not limit the scaling */
 +                comm->cutoff_mbody = min(comm->cutoff_mbody, dlb_scale*acs);
 +            }
 +            if (!comm->bBondComm)
 +            {
 +                /* Without bBondComm do not go beyond the n.b. cut-off */
 +                comm->cutoff_mbody = min(comm->cutoff_mbody, comm->cutoff);
 +                if (comm->cellsize_limit >= comm->cutoff)
 +                {
 +                    /* We don't loose a lot of efficieny
 +                     * when increasing it to the n.b. cut-off.
 +                     * It can even be slightly faster, because we need
 +                     * less checks for the communication setup.
 +                     */
 +                    comm->cutoff_mbody = comm->cutoff;
 +                }
 +            }
 +            /* Check if we did not end up below our original limit */
 +            comm->cutoff_mbody = max(comm->cutoff_mbody, r_bonded_limit);
 +
 +            if (comm->cutoff_mbody > comm->cellsize_limit)
 +            {
 +                comm->cellsize_limit = comm->cutoff_mbody;
 +            }
 +        }
 +        /* Without DLB and cutoff_mbody<cutoff, cutoff_mbody is dynamic */
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Bonded atom communication beyond the cut-off: %d\n"
 +                "cellsize limit %f\n",
 +                comm->bBondComm, comm->cellsize_limit);
 +    }
 +
 +    if (MASTER(cr))
 +    {
 +        check_dd_restrictions(cr, dd, ir, fplog);
 +    }
 +
 +    comm->partition_step = INT_MIN;
 +    dd->ddp_count        = 0;
 +
 +    clear_dd_cycle_counts(dd);
 +
 +    return dd;
 +}
 +
 +static void set_dlb_limits(gmx_domdec_t *dd)
 +
 +{
 +    int d;
 +
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        dd->comm->cd[d].np                 = dd->comm->cd[d].np_dlb;
 +        dd->comm->cellsize_min[dd->dim[d]] =
 +            dd->comm->cellsize_min_dlb[dd->dim[d]];
 +    }
 +}
 +
 +
 +static void turn_on_dlb(FILE *fplog, t_commrec *cr, gmx_large_int_t step)
 +{
 +    gmx_domdec_t      *dd;
 +    gmx_domdec_comm_t *comm;
 +    real               cellsize_min;
 +    int                d, nc, i;
 +    char               buf[STRLEN];
 +
 +    dd   = cr->dd;
 +    comm = dd->comm;
 +
 +    if (fplog)
 +    {
 +        fprintf(fplog, "At step %s the performance loss due to force load imbalance is %.1f %%\n", gmx_step_str(step, buf), dd_force_imb_perf_loss(dd)*100);
 +    }
 +
 +    cellsize_min = comm->cellsize_min[dd->dim[0]];
 +    for (d = 1; d < dd->ndim; d++)
 +    {
 +        cellsize_min = min(cellsize_min, comm->cellsize_min[dd->dim[d]]);
 +    }
 +
 +    if (cellsize_min < comm->cellsize_limit*1.05)
 +    {
 +        dd_warning(cr, fplog, "NOTE: the minimum cell size is smaller than 1.05 times the cell size limit, will not turn on dynamic load balancing\n");
 +
 +        /* Change DLB from "auto" to "no". */
 +        comm->eDLB = edlbNO;
 +
 +        return;
 +    }
 +
 +    dd_warning(cr, fplog, "NOTE: Turning on dynamic load balancing\n");
 +    comm->bDynLoadBal = TRUE;
 +    dd->bGridJump     = TRUE;
 +
 +    set_dlb_limits(dd);
 +
 +    /* We can set the required cell size info here,
 +     * so we do not need to communicate this.
 +     * The grid is completely uniform.
 +     */
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        if (comm->root[d])
 +        {
 +            comm->load[d].sum_m = comm->load[d].sum;
 +
 +            nc = dd->nc[dd->dim[d]];
 +            for (i = 0; i < nc; i++)
 +            {
 +                comm->root[d]->cell_f[i]    = i/(real)nc;
 +                if (d > 0)
 +                {
 +                    comm->root[d]->cell_f_max0[i] =  i   /(real)nc;
 +                    comm->root[d]->cell_f_min1[i] = (i+1)/(real)nc;
 +                }
 +            }
 +            comm->root[d]->cell_f[nc] = 1.0;
 +        }
 +    }
 +}
 +
 +static char *init_bLocalCG(gmx_mtop_t *mtop)
 +{
 +    int   ncg, cg;
 +    char *bLocalCG;
 +
 +    ncg = ncg_mtop(mtop);
 +    snew(bLocalCG, ncg);
 +    for (cg = 0; cg < ncg; cg++)
 +    {
 +        bLocalCG[cg] = FALSE;
 +    }
 +
 +    return bLocalCG;
 +}
 +
 +void dd_init_bondeds(FILE *fplog,
 +                     gmx_domdec_t *dd, gmx_mtop_t *mtop,
 +                     gmx_vsite_t *vsite, gmx_constr_t constr,
 +                     t_inputrec *ir, gmx_bool bBCheck, cginfo_mb_t *cginfo_mb)
 +{
 +    gmx_domdec_comm_t *comm;
 +    gmx_bool           bBondComm;
 +    int                d;
 +
 +    dd_make_reverse_top(fplog, dd, mtop, vsite, constr, ir, bBCheck);
 +
 +    comm = dd->comm;
 +
 +    if (comm->bBondComm)
 +    {
 +        /* Communicate atoms beyond the cut-off for bonded interactions */
 +        comm = dd->comm;
 +
 +        comm->cglink = make_charge_group_links(mtop, dd, cginfo_mb);
 +
 +        comm->bLocalCG = init_bLocalCG(mtop);
 +    }
 +    else
 +    {
 +        /* Only communicate atoms based on cut-off */
 +        comm->cglink   = NULL;
 +        comm->bLocalCG = NULL;
 +    }
 +}
 +
 +static void print_dd_settings(FILE *fplog, gmx_domdec_t *dd,
 +                              t_inputrec *ir,
 +                              gmx_bool bDynLoadBal, real dlb_scale,
 +                              gmx_ddbox_t *ddbox)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                d;
 +    ivec               np;
 +    real               limit, shrink;
 +    char               buf[64];
 +
 +    if (fplog == NULL)
 +    {
 +        return;
 +    }
 +
 +    comm = dd->comm;
 +
 +    if (bDynLoadBal)
 +    {
 +        fprintf(fplog, "The maximum number of communication pulses is:");
 +        for (d = 0; d < dd->ndim; d++)
 +        {
 +            fprintf(fplog, " %c %d", dim2char(dd->dim[d]), comm->cd[d].np_dlb);
 +        }
 +        fprintf(fplog, "\n");
 +        fprintf(fplog, "The minimum size for domain decomposition cells is %.3f nm\n", comm->cellsize_limit);
 +        fprintf(fplog, "The requested allowed shrink of DD cells (option -dds) is: %.2f\n", dlb_scale);
 +        fprintf(fplog, "The allowed shrink of domain decomposition cells is:");
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if (dd->nc[d] > 1)
 +            {
 +                if (d >= ddbox->npbcdim && dd->nc[d] == 2)
 +                {
 +                    shrink = 0;
 +                }
 +                else
 +                {
 +                    shrink =
 +                        comm->cellsize_min_dlb[d]/
 +                        (ddbox->box_size[d]*ddbox->skew_fac[d]/dd->nc[d]);
 +                }
 +                fprintf(fplog, " %c %.2f", dim2char(d), shrink);
 +            }
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +    else
 +    {
 +        set_dd_cell_sizes_slb(dd, ddbox, FALSE, np);
 +        fprintf(fplog, "The initial number of communication pulses is:");
 +        for (d = 0; d < dd->ndim; d++)
 +        {
 +            fprintf(fplog, " %c %d", dim2char(dd->dim[d]), np[dd->dim[d]]);
 +        }
 +        fprintf(fplog, "\n");
 +        fprintf(fplog, "The initial domain decomposition cell size is:");
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if (dd->nc[d] > 1)
 +            {
 +                fprintf(fplog, " %c %.2f nm",
 +                        dim2char(d), dd->comm->cellsize_min[d]);
 +            }
 +        }
 +        fprintf(fplog, "\n\n");
 +    }
 +
 +    if (comm->bInterCGBondeds || dd->vsite_comm || dd->constraint_comm)
 +    {
 +        fprintf(fplog, "The maximum allowed distance for charge groups involved in interactions is:\n");
 +        fprintf(fplog, "%40s  %-7s %6.3f nm\n",
 +                "non-bonded interactions", "", comm->cutoff);
 +
 +        if (bDynLoadBal)
 +        {
 +            limit = dd->comm->cellsize_limit;
 +        }
 +        else
 +        {
 +            if (dynamic_dd_box(ddbox, ir))
 +            {
 +                fprintf(fplog, "(the following are initial values, they could change due to box deformation)\n");
 +            }
 +            limit = dd->comm->cellsize_min[XX];
 +            for (d = 1; d < DIM; d++)
 +            {
 +                limit = min(limit, dd->comm->cellsize_min[d]);
 +            }
 +        }
 +
 +        if (comm->bInterCGBondeds)
 +        {
 +            fprintf(fplog, "%40s  %-7s %6.3f nm\n",
 +                    "two-body bonded interactions", "(-rdd)",
 +                    max(comm->cutoff, comm->cutoff_mbody));
 +            fprintf(fplog, "%40s  %-7s %6.3f nm\n",
 +                    "multi-body bonded interactions", "(-rdd)",
 +                    (comm->bBondComm || dd->bGridJump) ? comm->cutoff_mbody : min(comm->cutoff, limit));
 +        }
 +        if (dd->vsite_comm)
 +        {
 +            fprintf(fplog, "%40s  %-7s %6.3f nm\n",
 +                    "virtual site constructions", "(-rcon)", limit);
 +        }
 +        if (dd->constraint_comm)
 +        {
 +            sprintf(buf, "atoms separated by up to %d constraints",
 +                    1+ir->nProjOrder);
 +            fprintf(fplog, "%40s  %-7s %6.3f nm\n",
 +                    buf, "(-rcon)", limit);
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +
 +    fflush(fplog);
 +}
 +
 +static void set_cell_limits_dlb(gmx_domdec_t      *dd,
 +                                real               dlb_scale,
 +                                const t_inputrec  *ir,
 +                                const gmx_ddbox_t *ddbox)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                d, dim, npulse, npulse_d_max, npulse_d;
 +    gmx_bool           bNoCutOff;
 +
 +    comm = dd->comm;
 +
 +    bNoCutOff = (ir->rvdw == 0 || ir->rcoulomb == 0);
 +
 +    /* Determine the maximum number of comm. pulses in one dimension */
 +
 +    comm->cellsize_limit = max(comm->cellsize_limit, comm->cutoff_mbody);
 +
 +    /* Determine the maximum required number of grid pulses */
 +    if (comm->cellsize_limit >= comm->cutoff)
 +    {
 +        /* Only a single pulse is required */
 +        npulse = 1;
 +    }
 +    else if (!bNoCutOff && comm->cellsize_limit > 0)
 +    {
 +        /* We round down slightly here to avoid overhead due to the latency
 +         * of extra communication calls when the cut-off
 +         * would be only slightly longer than the cell size.
 +         * Later cellsize_limit is redetermined,
 +         * so we can not miss interactions due to this rounding.
 +         */
 +        npulse = (int)(0.96 + comm->cutoff/comm->cellsize_limit);
 +    }
 +    else
 +    {
 +        /* There is no cell size limit */
 +        npulse = max(dd->nc[XX]-1, max(dd->nc[YY]-1, dd->nc[ZZ]-1));
 +    }
 +
 +    if (!bNoCutOff && npulse > 1)
 +    {
 +        /* See if we can do with less pulses, based on dlb_scale */
 +        npulse_d_max = 0;
 +        for (d = 0; d < dd->ndim; d++)
 +        {
 +            dim      = dd->dim[d];
 +            npulse_d = (int)(1 + dd->nc[dim]*comm->cutoff
 +                             /(ddbox->box_size[dim]*ddbox->skew_fac[dim]*dlb_scale));
 +            npulse_d_max = max(npulse_d_max, npulse_d);
 +        }
 +        npulse = min(npulse, npulse_d_max);
 +    }
 +
 +    /* This env var can override npulse */
 +    d = dd_nst_env(debug, "GMX_DD_NPULSE", 0);
 +    if (d > 0)
 +    {
 +        npulse = d;
 +    }
 +
 +    comm->maxpulse       = 1;
 +    comm->bVacDLBNoLimit = (ir->ePBC == epbcNONE);
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        comm->cd[d].np_dlb    = min(npulse, dd->nc[dd->dim[d]]-1);
 +        comm->cd[d].np_nalloc = comm->cd[d].np_dlb;
 +        snew(comm->cd[d].ind, comm->cd[d].np_nalloc);
 +        comm->maxpulse = max(comm->maxpulse, comm->cd[d].np_dlb);
 +        if (comm->cd[d].np_dlb < dd->nc[dd->dim[d]]-1)
 +        {
 +            comm->bVacDLBNoLimit = FALSE;
 +        }
 +    }
 +
 +    /* cellsize_limit is set for LINCS in init_domain_decomposition */
 +    if (!comm->bVacDLBNoLimit)
 +    {
 +        comm->cellsize_limit = max(comm->cellsize_limit,
 +                                   comm->cutoff/comm->maxpulse);
 +    }
 +    comm->cellsize_limit = max(comm->cellsize_limit, comm->cutoff_mbody);
 +    /* Set the minimum cell size for each DD dimension */
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        if (comm->bVacDLBNoLimit ||
 +            comm->cd[d].np_dlb*comm->cellsize_limit >= comm->cutoff)
 +        {
 +            comm->cellsize_min_dlb[dd->dim[d]] = comm->cellsize_limit;
 +        }
 +        else
 +        {
 +            comm->cellsize_min_dlb[dd->dim[d]] =
 +                comm->cutoff/comm->cd[d].np_dlb;
 +        }
 +    }
 +    if (comm->cutoff_mbody <= 0)
 +    {
 +        comm->cutoff_mbody = min(comm->cutoff, comm->cellsize_limit);
 +    }
 +    if (comm->bDynLoadBal)
 +    {
 +        set_dlb_limits(dd);
 +    }
 +}
 +
 +gmx_bool dd_bonded_molpbc(gmx_domdec_t *dd, int ePBC)
 +{
 +    /* If each molecule is a single charge group
 +     * or we use domain decomposition for each periodic dimension,
 +     * we do not need to take pbc into account for the bonded interactions.
 +     */
 +    return (ePBC != epbcNONE && dd->comm->bInterCGBondeds &&
 +            !(dd->nc[XX] > 1 &&
 +              dd->nc[YY] > 1 &&
 +              (dd->nc[ZZ] > 1 || ePBC == epbcXY)));
 +}
 +
 +void set_dd_parameters(FILE *fplog, gmx_domdec_t *dd, real dlb_scale,
 +                       t_inputrec *ir, t_forcerec *fr,
 +                       gmx_ddbox_t *ddbox)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                natoms_tot;
 +    real               vol_frac;
 +
 +    comm = dd->comm;
 +
 +    /* Initialize the thread data.
 +     * This can not be done in init_domain_decomposition,
 +     * as the numbers of threads is determined later.
 +     */
 +    comm->nth = gmx_omp_nthreads_get(emntDomdec);
 +    if (comm->nth > 1)
 +    {
 +        snew(comm->dth, comm->nth);
 +    }
 +
 +    if (EEL_PME(ir->coulombtype))
 +    {
 +        init_ddpme(dd, &comm->ddpme[0], 0);
 +        if (comm->npmedecompdim >= 2)
 +        {
 +            init_ddpme(dd, &comm->ddpme[1], 1);
 +        }
 +    }
 +    else
 +    {
 +        comm->npmenodes = 0;
 +        if (dd->pme_nodeid >= 0)
 +        {
 +            gmx_fatal_collective(FARGS, NULL, dd,
 +                                 "Can not have separate PME nodes without PME electrostatics");
 +        }
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "The DD cut-off is %f\n", comm->cutoff);
 +    }
 +    if (comm->eDLB != edlbNO)
 +    {
 +        set_cell_limits_dlb(dd, dlb_scale, ir, ddbox);
 +    }
 +
 +    print_dd_settings(fplog, dd, ir, comm->bDynLoadBal, dlb_scale, ddbox);
 +    if (comm->eDLB == edlbAUTO)
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "When dynamic load balancing gets turned on, these settings will change to:\n");
 +        }
 +        print_dd_settings(fplog, dd, ir, TRUE, dlb_scale, ddbox);
 +    }
 +
 +    if (ir->ePBC == epbcNONE)
 +    {
 +        vol_frac = 1 - 1/(double)dd->nnodes;
 +    }
 +    else
 +    {
 +        vol_frac =
 +            (1 + comm_box_frac(dd->nc, comm->cutoff, ddbox))/(double)dd->nnodes;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "Volume fraction for all DD zones: %f\n", vol_frac);
 +    }
 +    natoms_tot = comm->cgs_gl.index[comm->cgs_gl.nr];
 +
 +    dd->ga2la = ga2la_init(natoms_tot, vol_frac*natoms_tot);
 +}
 +
 +static gmx_bool test_dd_cutoff(t_commrec *cr,
 +                               t_state *state, t_inputrec *ir,
 +                               real cutoff_req)
 +{
 +    gmx_domdec_t *dd;
 +    gmx_ddbox_t   ddbox;
 +    int           d, dim, np;
 +    real          inv_cell_size;
 +    int           LocallyLimited;
 +
 +    dd = cr->dd;
 +
 +    set_ddbox(dd, FALSE, cr, ir, state->box,
 +              TRUE, &dd->comm->cgs_gl, state->x, &ddbox);
 +
 +    LocallyLimited = 0;
 +
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        dim = dd->dim[d];
 +
 +        inv_cell_size = DD_CELL_MARGIN*dd->nc[dim]/ddbox.box_size[dim];
 +        if (dynamic_dd_box(&ddbox, ir))
 +        {
 +            inv_cell_size *= DD_PRES_SCALE_MARGIN;
 +        }
 +
 +        np = 1 + (int)(cutoff_req*inv_cell_size*ddbox.skew_fac[dim]);
 +
 +        if (dd->comm->eDLB != edlbNO && dim < ddbox.npbcdim &&
 +            dd->comm->cd[d].np_dlb > 0)
 +        {
 +            if (np > dd->comm->cd[d].np_dlb)
 +            {
 +                return FALSE;
 +            }
 +
 +            /* If a current local cell size is smaller than the requested
 +             * cut-off, we could still fix it, but this gets very complicated.
 +             * Without fixing here, we might actually need more checks.
 +             */
 +            if ((dd->comm->cell_x1[dim] - dd->comm->cell_x0[dim])*ddbox.skew_fac[dim]*dd->comm->cd[d].np_dlb < cutoff_req)
 +            {
 +                LocallyLimited = 1;
 +            }
 +        }
 +    }
 +
 +    if (dd->comm->eDLB != edlbNO)
 +    {
 +        /* If DLB is not active yet, we don't need to check the grid jumps.
 +         * Actually we shouldn't, because then the grid jump data is not set.
 +         */
 +        if (dd->comm->bDynLoadBal &&
 +            check_grid_jump(0, dd, cutoff_req, &ddbox, FALSE))
 +        {
 +            LocallyLimited = 1;
 +        }
 +
 +        gmx_sumi(1, &LocallyLimited, cr);
 +
 +        if (LocallyLimited > 0)
 +        {
 +            return FALSE;
 +        }
 +    }
 +
 +    return TRUE;
 +}
 +
 +gmx_bool change_dd_cutoff(t_commrec *cr, t_state *state, t_inputrec *ir,
 +                          real cutoff_req)
 +{
 +    gmx_bool bCutoffAllowed;
 +
 +    bCutoffAllowed = test_dd_cutoff(cr, state, ir, cutoff_req);
 +
 +    if (bCutoffAllowed)
 +    {
 +        cr->dd->comm->cutoff = cutoff_req;
 +    }
 +
 +    return bCutoffAllowed;
 +}
 +
 +void change_dd_dlb_cutoff_limit(t_commrec *cr)
 +{
 +    gmx_domdec_comm_t *comm;
 +
 +    comm = cr->dd->comm;
 +
 +    /* Turn on the DLB limiting (might have been on already) */
 +    comm->bPMELoadBalDLBLimits = TRUE;
 +
 +    /* Change the cut-off limit */
 +    comm->PMELoadBal_max_cutoff = comm->cutoff;
 +}
 +
 +static void merge_cg_buffers(int ncell,
 +                             gmx_domdec_comm_dim_t *cd, int pulse,
 +                             int  *ncg_cell,
 +                             int  *index_gl, int  *recv_i,
 +                             rvec *cg_cm,    rvec *recv_vr,
 +                             int *cgindex,
 +                             cginfo_mb_t *cginfo_mb, int *cginfo)
 +{
 +    gmx_domdec_ind_t *ind, *ind_p;
 +    int               p, cell, c, cg, cg0, cg1, cg_gl, nat;
 +    int               shift, shift_at;
 +
 +    ind = &cd->ind[pulse];
 +
 +    /* First correct the already stored data */
 +    shift = ind->nrecv[ncell];
 +    for (cell = ncell-1; cell >= 0; cell--)
 +    {
 +        shift -= ind->nrecv[cell];
 +        if (shift > 0)
 +        {
 +            /* Move the cg's present from previous grid pulses */
 +            cg0                = ncg_cell[ncell+cell];
 +            cg1                = ncg_cell[ncell+cell+1];
 +            cgindex[cg1+shift] = cgindex[cg1];
 +            for (cg = cg1-1; cg >= cg0; cg--)
 +            {
 +                index_gl[cg+shift] = index_gl[cg];
 +                copy_rvec(cg_cm[cg], cg_cm[cg+shift]);
 +                cgindex[cg+shift] = cgindex[cg];
 +                cginfo[cg+shift]  = cginfo[cg];
 +            }
 +            /* Correct the already stored send indices for the shift */
 +            for (p = 1; p <= pulse; p++)
 +            {
 +                ind_p = &cd->ind[p];
 +                cg0   = 0;
 +                for (c = 0; c < cell; c++)
 +                {
 +                    cg0 += ind_p->nsend[c];
 +                }
 +                cg1 = cg0 + ind_p->nsend[cell];
 +                for (cg = cg0; cg < cg1; cg++)
 +                {
 +                    ind_p->index[cg] += shift;
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Merge in the communicated buffers */
 +    shift    = 0;
 +    shift_at = 0;
 +    cg0      = 0;
 +    for (cell = 0; cell < ncell; cell++)
 +    {
 +        cg1 = ncg_cell[ncell+cell+1] + shift;
 +        if (shift_at > 0)
 +        {
 +            /* Correct the old cg indices */
 +            for (cg = ncg_cell[ncell+cell]; cg < cg1; cg++)
 +            {
 +                cgindex[cg+1] += shift_at;
 +            }
 +        }
 +        for (cg = 0; cg < ind->nrecv[cell]; cg++)
 +        {
 +            /* Copy this charge group from the buffer */
 +            index_gl[cg1] = recv_i[cg0];
 +            copy_rvec(recv_vr[cg0], cg_cm[cg1]);
 +            /* Add it to the cgindex */
 +            cg_gl          = index_gl[cg1];
 +            cginfo[cg1]    = ddcginfo(cginfo_mb, cg_gl);
 +            nat            = GET_CGINFO_NATOMS(cginfo[cg1]);
 +            cgindex[cg1+1] = cgindex[cg1] + nat;
 +            cg0++;
 +            cg1++;
 +            shift_at += nat;
 +        }
 +        shift                 += ind->nrecv[cell];
 +        ncg_cell[ncell+cell+1] = cg1;
 +    }
 +}
 +
 +static void make_cell2at_index(gmx_domdec_comm_dim_t *cd,
 +                               int nzone, int cg0, const int *cgindex)
 +{
 +    int cg, zone, p;
 +
 +    /* Store the atom block boundaries for easy copying of communication buffers
 +     */
 +    cg = cg0;
 +    for (zone = 0; zone < nzone; zone++)
 +    {
 +        for (p = 0; p < cd->np; p++)
 +        {
 +            cd->ind[p].cell2at0[zone] = cgindex[cg];
 +            cg += cd->ind[p].nrecv[zone];
 +            cd->ind[p].cell2at1[zone] = cgindex[cg];
 +        }
 +    }
 +}
 +
 +static gmx_bool missing_link(t_blocka *link, int cg_gl, char *bLocalCG)
 +{
 +    int      i;
 +    gmx_bool bMiss;
 +
 +    bMiss = FALSE;
 +    for (i = link->index[cg_gl]; i < link->index[cg_gl+1]; i++)
 +    {
 +        if (!bLocalCG[link->a[i]])
 +        {
 +            bMiss = TRUE;
 +        }
 +    }
 +
 +    return bMiss;
 +}
 +
 +/* Domain corners for communication, a maximum of 4 i-zones see a j domain */
 +typedef struct {
 +    real c[DIM][4]; /* the corners for the non-bonded communication */
 +    real cr0;       /* corner for rounding */
 +    real cr1[4];    /* corners for rounding */
 +    real bc[DIM];   /* corners for bounded communication */
 +    real bcr1;      /* corner for rounding for bonded communication */
 +} dd_corners_t;
 +
 +/* Determine the corners of the domain(s) we are communicating with */
 +static void
 +set_dd_corners(const gmx_domdec_t *dd,
 +               int dim0, int dim1, int dim2,
 +               gmx_bool bDistMB,
 +               dd_corners_t *c)
 +{
 +    const gmx_domdec_comm_t  *comm;
 +    const gmx_domdec_zones_t *zones;
 +    int i, j;
 +
 +    comm = dd->comm;
 +
 +    zones = &comm->zones;
 +
 +    /* Keep the compiler happy */
 +    c->cr0  = 0;
 +    c->bcr1 = 0;
 +
 +    /* The first dimension is equal for all cells */
 +    c->c[0][0] = comm->cell_x0[dim0];
 +    if (bDistMB)
 +    {
 +        c->bc[0] = c->c[0][0];
 +    }
 +    if (dd->ndim >= 2)
 +    {
 +        dim1 = dd->dim[1];
 +        /* This cell row is only seen from the first row */
 +        c->c[1][0] = comm->cell_x0[dim1];
 +        /* All rows can see this row */
 +        c->c[1][1] = comm->cell_x0[dim1];
 +        if (dd->bGridJump)
 +        {
 +            c->c[1][1] = max(comm->cell_x0[dim1], comm->zone_d1[1].mch0);
 +            if (bDistMB)
 +            {
 +                /* For the multi-body distance we need the maximum */
 +                c->bc[1] = max(comm->cell_x0[dim1], comm->zone_d1[1].p1_0);
 +            }
 +        }
 +        /* Set the upper-right corner for rounding */
 +        c->cr0 = comm->cell_x1[dim0];
 +
 +        if (dd->ndim >= 3)
 +        {
 +            dim2 = dd->dim[2];
 +            for (j = 0; j < 4; j++)
 +            {
 +                c->c[2][j] = comm->cell_x0[dim2];
 +            }
 +            if (dd->bGridJump)
 +            {
 +                /* Use the maximum of the i-cells that see a j-cell */
 +                for (i = 0; i < zones->nizone; i++)
 +                {
 +                    for (j = zones->izone[i].j0; j < zones->izone[i].j1; j++)
 +                    {
 +                        if (j >= 4)
 +                        {
 +                            c->c[2][j-4] =
 +                                max(c->c[2][j-4],
 +                                    comm->zone_d2[zones->shift[i][dim0]][zones->shift[i][dim1]].mch0);
 +                        }
 +                    }
 +                }
 +                if (bDistMB)
 +                {
 +                    /* For the multi-body distance we need the maximum */
 +                    c->bc[2] = comm->cell_x0[dim2];
 +                    for (i = 0; i < 2; i++)
 +                    {
 +                        for (j = 0; j < 2; j++)
 +                        {
 +                            c->bc[2] = max(c->bc[2], comm->zone_d2[i][j].p1_0);
 +                        }
 +                    }
 +                }
 +            }
 +
 +            /* Set the upper-right corner for rounding */
 +            /* Cell (0,0,0) and cell (1,0,0) can see cell 4 (0,1,1)
 +             * Only cell (0,0,0) can see cell 7 (1,1,1)
 +             */
 +            c->cr1[0] = comm->cell_x1[dim1];
 +            c->cr1[3] = comm->cell_x1[dim1];
 +            if (dd->bGridJump)
 +            {
 +                c->cr1[0] = max(comm->cell_x1[dim1], comm->zone_d1[1].mch1);
 +                if (bDistMB)
 +                {
 +                    /* For the multi-body distance we need the maximum */
 +                    c->bcr1 = max(comm->cell_x1[dim1], comm->zone_d1[1].p1_1);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/* Determine which cg's we need to send in this pulse from this zone */
 +static void
 +get_zone_pulse_cgs(gmx_domdec_t *dd,
 +                   int zonei, int zone,
 +                   int cg0, int cg1,
 +                   const int *index_gl,
 +                   const int *cgindex,
 +                   int dim, int dim_ind,
 +                   int dim0, int dim1, int dim2,
 +                   real r_comm2, real r_bcomm2,
 +                   matrix box,
 +                   ivec tric_dist,
 +                   rvec *normal,
 +                   real skew_fac2_d, real skew_fac_01,
 +                   rvec *v_d, rvec *v_0, rvec *v_1,
 +                   const dd_corners_t *c,
 +                   rvec sf2_round,
 +                   gmx_bool bDistBonded,
 +                   gmx_bool bBondComm,
 +                   gmx_bool bDist2B,
 +                   gmx_bool bDistMB,
 +                   rvec *cg_cm,
 +                   int *cginfo,
 +                   gmx_domdec_ind_t *ind,
 +                   int **ibuf, int *ibuf_nalloc,
 +                   vec_rvec_t *vbuf,
 +                   int *nsend_ptr,
 +                   int *nat_ptr,
 +                   int *nsend_z_ptr)
 +{
 +    gmx_domdec_comm_t *comm;
 +    gmx_bool           bScrew;
 +    gmx_bool           bDistMB_pulse;
 +    int                cg, i;
 +    real               r2, rb2, r, tric_sh;
 +    rvec               rn, rb;
 +    int                dimd;
 +    int                nsend_z, nsend, nat;
 +
 +    comm = dd->comm;
 +
 +    bScrew = (dd->bScrewPBC && dim == XX);
 +
 +    bDistMB_pulse = (bDistMB && bDistBonded);
 +
 +    nsend_z = 0;
 +    nsend   = *nsend_ptr;
 +    nat     = *nat_ptr;
 +
 +    for (cg = cg0; cg < cg1; cg++)
 +    {
 +        r2  = 0;
 +        rb2 = 0;
 +        if (tric_dist[dim_ind] == 0)
 +        {
 +            /* Rectangular direction, easy */
 +            r = cg_cm[cg][dim] - c->c[dim_ind][zone];
 +            if (r > 0)
 +            {
 +                r2 += r*r;
 +            }
 +            if (bDistMB_pulse)
 +            {
 +                r = cg_cm[cg][dim] - c->bc[dim_ind];
 +                if (r > 0)
 +                {
 +                    rb2 += r*r;
 +                }
 +            }
 +            /* Rounding gives at most a 16% reduction
 +             * in communicated atoms
 +             */
 +            if (dim_ind >= 1 && (zonei == 1 || zonei == 2))
 +            {
 +                r = cg_cm[cg][dim0] - c->cr0;
 +                /* This is the first dimension, so always r >= 0 */
 +                r2 += r*r;
 +                if (bDistMB_pulse)
 +                {
 +                    rb2 += r*r;
 +                }
 +            }
 +            if (dim_ind == 2 && (zonei == 2 || zonei == 3))
 +            {
 +                r = cg_cm[cg][dim1] - c->cr1[zone];
 +                if (r > 0)
 +                {
 +                    r2 += r*r;
 +                }
 +                if (bDistMB_pulse)
 +                {
 +                    r = cg_cm[cg][dim1] - c->bcr1;
 +                    if (r > 0)
 +                    {
 +                        rb2 += r*r;
 +                    }
 +                }
 +            }
 +        }
 +        else
 +        {
 +            /* Triclinic direction, more complicated */
 +            clear_rvec(rn);
 +            clear_rvec(rb);
 +            /* Rounding, conservative as the skew_fac multiplication
 +             * will slightly underestimate the distance.
 +             */
 +            if (dim_ind >= 1 && (zonei == 1 || zonei == 2))
 +            {
 +                rn[dim0] = cg_cm[cg][dim0] - c->cr0;
 +                for (i = dim0+1; i < DIM; i++)
 +                {
 +                    rn[dim0] -= cg_cm[cg][i]*v_0[i][dim0];
 +                }
 +                r2 = rn[dim0]*rn[dim0]*sf2_round[dim0];
 +                if (bDistMB_pulse)
 +                {
 +                    rb[dim0] = rn[dim0];
 +                    rb2      = r2;
 +                }
 +                /* Take care that the cell planes along dim0 might not
 +                 * be orthogonal to those along dim1 and dim2.
 +                 */
 +                for (i = 1; i <= dim_ind; i++)
 +                {
 +                    dimd = dd->dim[i];
 +                    if (normal[dim0][dimd] > 0)
 +                    {
 +                        rn[dimd] -= rn[dim0]*normal[dim0][dimd];
 +                        if (bDistMB_pulse)
 +                        {
 +                            rb[dimd] -= rb[dim0]*normal[dim0][dimd];
 +                        }
 +                    }
 +                }
 +            }
 +            if (dim_ind == 2 && (zonei == 2 || zonei == 3))
 +            {
 +                rn[dim1] += cg_cm[cg][dim1] - c->cr1[zone];
 +                tric_sh   = 0;
 +                for (i = dim1+1; i < DIM; i++)
 +                {
 +                    tric_sh -= cg_cm[cg][i]*v_1[i][dim1];
 +                }
 +                rn[dim1] += tric_sh;
 +                if (rn[dim1] > 0)
 +                {
 +                    r2 += rn[dim1]*rn[dim1]*sf2_round[dim1];
 +                    /* Take care of coupling of the distances
 +                     * to the planes along dim0 and dim1 through dim2.
 +                     */
 +                    r2 -= rn[dim0]*rn[dim1]*skew_fac_01;
 +                    /* Take care that the cell planes along dim1
 +                     * might not be orthogonal to that along dim2.
 +                     */
 +                    if (normal[dim1][dim2] > 0)
 +                    {
 +                        rn[dim2] -= rn[dim1]*normal[dim1][dim2];
 +                    }
 +                }
 +                if (bDistMB_pulse)
 +                {
 +                    rb[dim1] +=
 +                        cg_cm[cg][dim1] - c->bcr1 + tric_sh;
 +                    if (rb[dim1] > 0)
 +                    {
 +                        rb2 += rb[dim1]*rb[dim1]*sf2_round[dim1];
 +                        /* Take care of coupling of the distances
 +                         * to the planes along dim0 and dim1 through dim2.
 +                         */
 +                        rb2 -= rb[dim0]*rb[dim1]*skew_fac_01;
 +                        /* Take care that the cell planes along dim1
 +                         * might not be orthogonal to that along dim2.
 +                         */
 +                        if (normal[dim1][dim2] > 0)
 +                        {
 +                            rb[dim2] -= rb[dim1]*normal[dim1][dim2];
 +                        }
 +                    }
 +                }
 +            }
 +            /* The distance along the communication direction */
 +            rn[dim] += cg_cm[cg][dim] - c->c[dim_ind][zone];
 +            tric_sh  = 0;
 +            for (i = dim+1; i < DIM; i++)
 +            {
 +                tric_sh -= cg_cm[cg][i]*v_d[i][dim];
 +            }
 +            rn[dim] += tric_sh;
 +            if (rn[dim] > 0)
 +            {
 +                r2 += rn[dim]*rn[dim]*skew_fac2_d;
 +                /* Take care of coupling of the distances
 +                 * to the planes along dim0 and dim1 through dim2.
 +                 */
 +                if (dim_ind == 1 && zonei == 1)
 +                {
 +                    r2 -= rn[dim0]*rn[dim]*skew_fac_01;
 +                }
 +            }
 +            if (bDistMB_pulse)
 +            {
 +                clear_rvec(rb);
 +                rb[dim] += cg_cm[cg][dim] - c->bc[dim_ind] + tric_sh;
 +                if (rb[dim] > 0)
 +                {
 +                    rb2 += rb[dim]*rb[dim]*skew_fac2_d;
 +                    /* Take care of coupling of the distances
 +                     * to the planes along dim0 and dim1 through dim2.
 +                     */
 +                    if (dim_ind == 1 && zonei == 1)
 +                    {
 +                        rb2 -= rb[dim0]*rb[dim]*skew_fac_01;
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (r2 < r_comm2 ||
 +            (bDistBonded &&
 +             ((bDistMB && rb2 < r_bcomm2) ||
 +              (bDist2B && r2  < r_bcomm2)) &&
 +             (!bBondComm ||
 +              (GET_CGINFO_BOND_INTER(cginfo[cg]) &&
 +               missing_link(comm->cglink, index_gl[cg],
 +                            comm->bLocalCG)))))
 +        {
 +            /* Make an index to the local charge groups */
 +            if (nsend+1 > ind->nalloc)
 +            {
 +                ind->nalloc = over_alloc_large(nsend+1);
 +                srenew(ind->index, ind->nalloc);
 +            }
 +            if (nsend+1 > *ibuf_nalloc)
 +            {
 +                *ibuf_nalloc = over_alloc_large(nsend+1);
 +                srenew(*ibuf, *ibuf_nalloc);
 +            }
 +            ind->index[nsend] = cg;
 +            (*ibuf)[nsend]    = index_gl[cg];
 +            nsend_z++;
 +            vec_rvec_check_alloc(vbuf, nsend+1);
 +
 +            if (dd->ci[dim] == 0)
 +            {
 +                /* Correct cg_cm for pbc */
 +                rvec_add(cg_cm[cg], box[dim], vbuf->v[nsend]);
 +                if (bScrew)
 +                {
 +                    vbuf->v[nsend][YY] = box[YY][YY] - vbuf->v[nsend][YY];
 +                    vbuf->v[nsend][ZZ] = box[ZZ][ZZ] - vbuf->v[nsend][ZZ];
 +                }
 +            }
 +            else
 +            {
 +                copy_rvec(cg_cm[cg], vbuf->v[nsend]);
 +            }
 +            nsend++;
 +            nat += cgindex[cg+1] - cgindex[cg];
 +        }
 +    }
 +
 +    *nsend_ptr   = nsend;
 +    *nat_ptr     = nat;
 +    *nsend_z_ptr = nsend_z;
 +}
 +
 +static void setup_dd_communication(gmx_domdec_t *dd,
 +                                   matrix box, gmx_ddbox_t *ddbox,
 +                                   t_forcerec *fr, t_state *state, rvec **f)
 +{
 +    int                    dim_ind, dim, dim0, dim1, dim2, dimd, p, nat_tot;
 +    int                    nzone, nzone_send, zone, zonei, cg0, cg1;
 +    int                    c, i, j, cg, cg_gl, nrcg;
 +    int                   *zone_cg_range, pos_cg, *index_gl, *cgindex, *recv_i;
 +    gmx_domdec_comm_t     *comm;
 +    gmx_domdec_zones_t    *zones;
 +    gmx_domdec_comm_dim_t *cd;
 +    gmx_domdec_ind_t      *ind;
 +    cginfo_mb_t           *cginfo_mb;
 +    gmx_bool               bBondComm, bDist2B, bDistMB, bDistBonded;
 +    real                   r_mb, r_comm2, r_scomm2, r_bcomm2, r_0, r_1, r2inc, inv_ncg;
 +    dd_corners_t           corners;
 +    ivec                   tric_dist;
 +    rvec                  *cg_cm, *normal, *v_d, *v_0 = NULL, *v_1 = NULL, *recv_vr;
 +    real                   skew_fac2_d, skew_fac_01;
 +    rvec                   sf2_round;
 +    int                    nsend, nat;
 +    int                    th;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Setting up DD communication\n");
 +    }
 +
 +    comm  = dd->comm;
 +
 +    switch (fr->cutoff_scheme)
 +    {
 +        case ecutsGROUP:
 +            cg_cm = fr->cg_cm;
 +            break;
 +        case ecutsVERLET:
 +            cg_cm = state->x;
 +            break;
 +        default:
 +            gmx_incons("unimplemented");
 +            cg_cm = NULL;
 +    }
 +
 +    for (dim_ind = 0; dim_ind < dd->ndim; dim_ind++)
 +    {
 +        dim = dd->dim[dim_ind];
 +
 +        /* Check if we need to use triclinic distances */
 +        tric_dist[dim_ind] = 0;
 +        for (i = 0; i <= dim_ind; i++)
 +        {
 +            if (ddbox->tric_dir[dd->dim[i]])
 +            {
 +                tric_dist[dim_ind] = 1;
 +            }
 +        }
 +    }
 +
 +    bBondComm = comm->bBondComm;
 +
 +    /* Do we need to determine extra distances for multi-body bondeds? */
 +    bDistMB = (comm->bInterCGMultiBody && dd->bGridJump && dd->ndim > 1);
 +
 +    /* Do we need to determine extra distances for only two-body bondeds? */
 +    bDist2B = (bBondComm && !bDistMB);
 +
 +    r_comm2  = sqr(comm->cutoff);
 +    r_bcomm2 = sqr(comm->cutoff_mbody);
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "bBondComm %d, r_bc %f\n", bBondComm, sqrt(r_bcomm2));
 +    }
 +
 +    zones = &comm->zones;
 +
 +    dim0 = dd->dim[0];
 +    dim1 = (dd->ndim >= 2 ? dd->dim[1] : -1);
 +    dim2 = (dd->ndim >= 3 ? dd->dim[2] : -1);
 +
 +    set_dd_corners(dd, dim0, dim1, dim2, bDistMB, &corners);
 +
 +    /* Triclinic stuff */
 +    normal      = ddbox->normal;
 +    skew_fac_01 = 0;
 +    if (dd->ndim >= 2)
 +    {
 +        v_0 = ddbox->v[dim0];
 +        if (ddbox->tric_dir[dim0] && ddbox->tric_dir[dim1])
 +        {
 +            /* Determine the coupling coefficient for the distances
 +             * to the cell planes along dim0 and dim1 through dim2.
 +             * This is required for correct rounding.
 +             */
 +            skew_fac_01 =
 +                ddbox->v[dim0][dim1+1][dim0]*ddbox->v[dim1][dim1+1][dim1];
 +            if (debug)
 +            {
 +                fprintf(debug, "\nskew_fac_01 %f\n", skew_fac_01);
 +            }
 +        }
 +    }
 +    if (dd->ndim >= 3)
 +    {
 +        v_1 = ddbox->v[dim1];
 +    }
 +
 +    zone_cg_range = zones->cg_range;
 +    index_gl      = dd->index_gl;
 +    cgindex       = dd->cgindex;
 +    cginfo_mb     = fr->cginfo_mb;
 +
 +    zone_cg_range[0]   = 0;
 +    zone_cg_range[1]   = dd->ncg_home;
 +    comm->zone_ncg1[0] = dd->ncg_home;
 +    pos_cg             = dd->ncg_home;
 +
 +    nat_tot = dd->nat_home;
 +    nzone   = 1;
 +    for (dim_ind = 0; dim_ind < dd->ndim; dim_ind++)
 +    {
 +        dim = dd->dim[dim_ind];
 +        cd  = &comm->cd[dim_ind];
 +
 +        if (dim >= ddbox->npbcdim && dd->ci[dim] == 0)
 +        {
 +            /* No pbc in this dimension, the first node should not comm. */
 +            nzone_send = 0;
 +        }
 +        else
 +        {
 +            nzone_send = nzone;
 +        }
 +
 +        v_d         = ddbox->v[dim];
 +        skew_fac2_d = sqr(ddbox->skew_fac[dim]);
 +
 +        cd->bInPlace = TRUE;
 +        for (p = 0; p < cd->np; p++)
 +        {
 +            /* Only atoms communicated in the first pulse are used
 +             * for multi-body bonded interactions or for bBondComm.
 +             */
 +            bDistBonded = ((bDistMB || bDist2B) && p == 0);
 +
 +            ind   = &cd->ind[p];
 +            nsend = 0;
 +            nat   = 0;
 +            for (zone = 0; zone < nzone_send; zone++)
 +            {
 +                if (tric_dist[dim_ind] && dim_ind > 0)
 +                {
 +                    /* Determine slightly more optimized skew_fac's
 +                     * for rounding.
 +                     * This reduces the number of communicated atoms
 +                     * by about 10% for 3D DD of rhombic dodecahedra.
 +                     */
 +                    for (dimd = 0; dimd < dim; dimd++)
 +                    {
 +                        sf2_round[dimd] = 1;
 +                        if (ddbox->tric_dir[dimd])
 +                        {
 +                            for (i = dd->dim[dimd]+1; i < DIM; i++)
 +                            {
 +                                /* If we are shifted in dimension i
 +                                 * and the cell plane is tilted forward
 +                                 * in dimension i, skip this coupling.
 +                                 */
 +                                if (!(zones->shift[nzone+zone][i] &&
 +                                      ddbox->v[dimd][i][dimd] >= 0))
 +                                {
 +                                    sf2_round[dimd] +=
 +                                        sqr(ddbox->v[dimd][i][dimd]);
 +                                }
 +                            }
 +                            sf2_round[dimd] = 1/sf2_round[dimd];
 +                        }
 +                    }
 +                }
 +
 +                zonei = zone_perm[dim_ind][zone];
 +                if (p == 0)
 +                {
 +                    /* Here we permutate the zones to obtain a convenient order
 +                     * for neighbor searching
 +                     */
 +                    cg0 = zone_cg_range[zonei];
 +                    cg1 = zone_cg_range[zonei+1];
 +                }
 +                else
 +                {
 +                    /* Look only at the cg's received in the previous grid pulse
 +                     */
 +                    cg1 = zone_cg_range[nzone+zone+1];
 +                    cg0 = cg1 - cd->ind[p-1].nrecv[zone];
 +                }
 +
 +#pragma omp parallel for num_threads(comm->nth) schedule(static)
 +                for (th = 0; th < comm->nth; th++)
 +                {
 +                    gmx_domdec_ind_t *ind_p;
 +                    int             **ibuf_p, *ibuf_nalloc_p;
 +                    vec_rvec_t       *vbuf_p;
 +                    int              *nsend_p, *nat_p;
 +                    int              *nsend_zone_p;
 +                    int               cg0_th, cg1_th;
 +
 +                    if (th == 0)
 +                    {
 +                        /* Thread 0 writes in the comm buffers */
 +                        ind_p         = ind;
 +                        ibuf_p        = &comm->buf_int;
 +                        ibuf_nalloc_p = &comm->nalloc_int;
 +                        vbuf_p        = &comm->vbuf;
 +                        nsend_p       = &nsend;
 +                        nat_p         = &nat;
 +                        nsend_zone_p  = &ind->nsend[zone];
 +                    }
 +                    else
 +                    {
 +                        /* Other threads write into temp buffers */
 +                        ind_p         = &comm->dth[th].ind;
 +                        ibuf_p        = &comm->dth[th].ibuf;
 +                        ibuf_nalloc_p = &comm->dth[th].ibuf_nalloc;
 +                        vbuf_p        = &comm->dth[th].vbuf;
 +                        nsend_p       = &comm->dth[th].nsend;
 +                        nat_p         = &comm->dth[th].nat;
 +                        nsend_zone_p  = &comm->dth[th].nsend_zone;
 +
 +                        comm->dth[th].nsend      = 0;
 +                        comm->dth[th].nat        = 0;
 +                        comm->dth[th].nsend_zone = 0;
 +                    }
 +
 +                    if (comm->nth == 1)
 +                    {
 +                        cg0_th = cg0;
 +                        cg1_th = cg1;
 +                    }
 +                    else
 +                    {
 +                        cg0_th = cg0 + ((cg1 - cg0)* th   )/comm->nth;
 +                        cg1_th = cg0 + ((cg1 - cg0)*(th+1))/comm->nth;
 +                    }
 +
 +                    /* Get the cg's for this pulse in this zone */
 +                    get_zone_pulse_cgs(dd, zonei, zone, cg0_th, cg1_th,
 +                                       index_gl, cgindex,
 +                                       dim, dim_ind, dim0, dim1, dim2,
 +                                       r_comm2, r_bcomm2,
 +                                       box, tric_dist,
 +                                       normal, skew_fac2_d, skew_fac_01,
 +                                       v_d, v_0, v_1, &corners, sf2_round,
 +                                       bDistBonded, bBondComm,
 +                                       bDist2B, bDistMB,
 +                                       cg_cm, fr->cginfo,
 +                                       ind_p,
 +                                       ibuf_p, ibuf_nalloc_p,
 +                                       vbuf_p,
 +                                       nsend_p, nat_p,
 +                                       nsend_zone_p);
 +                }
 +
 +                /* Append data of threads>=1 to the communication buffers */
 +                for (th = 1; th < comm->nth; th++)
 +                {
 +                    dd_comm_setup_work_t *dth;
 +                    int                   i, ns1;
 +
 +                    dth = &comm->dth[th];
 +
 +                    ns1 = nsend + dth->nsend_zone;
 +                    if (ns1 > ind->nalloc)
 +                    {
 +                        ind->nalloc = over_alloc_dd(ns1);
 +                        srenew(ind->index, ind->nalloc);
 +                    }
 +                    if (ns1 > comm->nalloc_int)
 +                    {
 +                        comm->nalloc_int = over_alloc_dd(ns1);
 +                        srenew(comm->buf_int, comm->nalloc_int);
 +                    }
 +                    if (ns1 > comm->vbuf.nalloc)
 +                    {
 +                        comm->vbuf.nalloc = over_alloc_dd(ns1);
 +                        srenew(comm->vbuf.v, comm->vbuf.nalloc);
 +                    }
 +
 +                    for (i = 0; i < dth->nsend_zone; i++)
 +                    {
 +                        ind->index[nsend]    = dth->ind.index[i];
 +                        comm->buf_int[nsend] = dth->ibuf[i];
 +                        copy_rvec(dth->vbuf.v[i],
 +                                  comm->vbuf.v[nsend]);
 +                        nsend++;
 +                    }
 +                    nat              += dth->nat;
 +                    ind->nsend[zone] += dth->nsend_zone;
 +                }
 +            }
 +            /* Clear the counts in case we do not have pbc */
 +            for (zone = nzone_send; zone < nzone; zone++)
 +            {
 +                ind->nsend[zone] = 0;
 +            }
 +            ind->nsend[nzone]   = nsend;
 +            ind->nsend[nzone+1] = nat;
 +            /* Communicate the number of cg's and atoms to receive */
 +            dd_sendrecv_int(dd, dim_ind, dddirBackward,
 +                            ind->nsend, nzone+2,
 +                            ind->nrecv, nzone+2);
 +
 +            /* The rvec buffer is also required for atom buffers of size nsend
 +             * in dd_move_x and dd_move_f.
 +             */
 +            vec_rvec_check_alloc(&comm->vbuf, ind->nsend[nzone+1]);
 +
 +            if (p > 0)
 +            {
 +                /* We can receive in place if only the last zone is not empty */
 +                for (zone = 0; zone < nzone-1; zone++)
 +                {
 +                    if (ind->nrecv[zone] > 0)
 +                    {
 +                        cd->bInPlace = FALSE;
 +                    }
 +                }
 +                if (!cd->bInPlace)
 +                {
 +                    /* The int buffer is only required here for the cg indices */
 +                    if (ind->nrecv[nzone] > comm->nalloc_int2)
 +                    {
 +                        comm->nalloc_int2 = over_alloc_dd(ind->nrecv[nzone]);
 +                        srenew(comm->buf_int2, comm->nalloc_int2);
 +                    }
 +                    /* The rvec buffer is also required for atom buffers
 +                     * of size nrecv in dd_move_x and dd_move_f.
 +                     */
 +                    i = max(cd->ind[0].nrecv[nzone+1], ind->nrecv[nzone+1]);
 +                    vec_rvec_check_alloc(&comm->vbuf2, i);
 +                }
 +            }
 +
 +            /* Make space for the global cg indices */
 +            if (pos_cg + ind->nrecv[nzone] > dd->cg_nalloc
 +                || dd->cg_nalloc == 0)
 +            {
 +                dd->cg_nalloc = over_alloc_dd(pos_cg + ind->nrecv[nzone]);
 +                srenew(index_gl, dd->cg_nalloc);
 +                srenew(cgindex, dd->cg_nalloc+1);
 +            }
 +            /* Communicate the global cg indices */
 +            if (cd->bInPlace)
 +            {
 +                recv_i = index_gl + pos_cg;
 +            }
 +            else
 +            {
 +                recv_i = comm->buf_int2;
 +            }
 +            dd_sendrecv_int(dd, dim_ind, dddirBackward,
 +                            comm->buf_int, nsend,
 +                            recv_i,        ind->nrecv[nzone]);
 +
 +            /* Make space for cg_cm */
 +            dd_check_alloc_ncg(fr, state, f, pos_cg + ind->nrecv[nzone]);
 +            if (fr->cutoff_scheme == ecutsGROUP)
 +            {
 +                cg_cm = fr->cg_cm;
 +            }
 +            else
 +            {
 +                cg_cm = state->x;
 +            }
 +            /* Communicate cg_cm */
 +            if (cd->bInPlace)
 +            {
 +                recv_vr = cg_cm + pos_cg;
 +            }
 +            else
 +            {
 +                recv_vr = comm->vbuf2.v;
 +            }
 +            dd_sendrecv_rvec(dd, dim_ind, dddirBackward,
 +                             comm->vbuf.v, nsend,
 +                             recv_vr,      ind->nrecv[nzone]);
 +
 +            /* Make the charge group index */
 +            if (cd->bInPlace)
 +            {
 +                zone = (p == 0 ? 0 : nzone - 1);
 +                while (zone < nzone)
 +                {
 +                    for (cg = 0; cg < ind->nrecv[zone]; cg++)
 +                    {
 +                        cg_gl              = index_gl[pos_cg];
 +                        fr->cginfo[pos_cg] = ddcginfo(cginfo_mb, cg_gl);
 +                        nrcg               = GET_CGINFO_NATOMS(fr->cginfo[pos_cg]);
 +                        cgindex[pos_cg+1]  = cgindex[pos_cg] + nrcg;
 +                        if (bBondComm)
 +                        {
 +                            /* Update the charge group presence,
 +                             * so we can use it in the next pass of the loop.
 +                             */
 +                            comm->bLocalCG[cg_gl] = TRUE;
 +                        }
 +                        pos_cg++;
 +                    }
 +                    if (p == 0)
 +                    {
 +                        comm->zone_ncg1[nzone+zone] = ind->nrecv[zone];
 +                    }
 +                    zone++;
 +                    zone_cg_range[nzone+zone] = pos_cg;
 +                }
 +            }
 +            else
 +            {
 +                /* This part of the code is never executed with bBondComm. */
 +                merge_cg_buffers(nzone, cd, p, zone_cg_range,
 +                                 index_gl, recv_i, cg_cm, recv_vr,
 +                                 cgindex, fr->cginfo_mb, fr->cginfo);
 +                pos_cg += ind->nrecv[nzone];
 +            }
 +            nat_tot += ind->nrecv[nzone+1];
 +        }
 +        if (!cd->bInPlace)
 +        {
 +            /* Store the atom block for easy copying of communication buffers */
 +            make_cell2at_index(cd, nzone, zone_cg_range[nzone], cgindex);
 +        }
 +        nzone += nzone;
 +    }
 +    dd->index_gl = index_gl;
 +    dd->cgindex  = cgindex;
 +
 +    dd->ncg_tot          = zone_cg_range[zones->n];
 +    dd->nat_tot          = nat_tot;
 +    comm->nat[ddnatHOME] = dd->nat_home;
 +    for (i = ddnatZONE; i < ddnatNR; i++)
 +    {
 +        comm->nat[i] = dd->nat_tot;
 +    }
 +
 +    if (!bBondComm)
 +    {
 +        /* We don't need to update cginfo, since that was alrady done above.
 +         * So we pass NULL for the forcerec.
 +         */
 +        dd_set_cginfo(dd->index_gl, dd->ncg_home, dd->ncg_tot,
 +                      NULL, comm->bLocalCG);
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Finished setting up DD communication, zones:");
 +        for (c = 0; c < zones->n; c++)
 +        {
 +            fprintf(debug, " %d", zones->cg_range[c+1]-zones->cg_range[c]);
 +        }
 +        fprintf(debug, "\n");
 +    }
 +}
 +
 +static void set_cg_boundaries(gmx_domdec_zones_t *zones)
 +{
 +    int c;
 +
 +    for (c = 0; c < zones->nizone; c++)
 +    {
 +        zones->izone[c].cg1  = zones->cg_range[c+1];
 +        zones->izone[c].jcg0 = zones->cg_range[zones->izone[c].j0];
 +        zones->izone[c].jcg1 = zones->cg_range[zones->izone[c].j1];
 +    }
 +}
 +
 +static void set_zones_size(gmx_domdec_t *dd,
 +                           matrix box, const gmx_ddbox_t *ddbox,
 +                           int zone_start, int zone_end)
 +{
 +    gmx_domdec_comm_t  *comm;
 +    gmx_domdec_zones_t *zones;
 +    gmx_bool            bDistMB;
 +    int                 z, zi, zj0, zj1, d, dim;
 +    real                rcs, rcmbs;
 +    int                 i, j;
 +    real                size_j, add_tric;
 +    real                vol;
 +
 +    comm = dd->comm;
 +
 +    zones = &comm->zones;
 +
 +    /* Do we need to determine extra distances for multi-body bondeds? */
 +    bDistMB = (comm->bInterCGMultiBody && dd->bGridJump && dd->ndim > 1);
 +
 +    for (z = zone_start; z < zone_end; z++)
 +    {
 +        /* Copy cell limits to zone limits.
 +         * Valid for non-DD dims and non-shifted dims.
 +         */
 +        copy_rvec(comm->cell_x0, zones->size[z].x0);
 +        copy_rvec(comm->cell_x1, zones->size[z].x1);
 +    }
 +
 +    for (d = 0; d < dd->ndim; d++)
 +    {
 +        dim = dd->dim[d];
 +
 +        for (z = 0; z < zones->n; z++)
 +        {
 +            /* With a staggered grid we have different sizes
 +             * for non-shifted dimensions.
 +             */
 +            if (dd->bGridJump && zones->shift[z][dim] == 0)
 +            {
 +                if (d == 1)
 +                {
 +                    zones->size[z].x0[dim] = comm->zone_d1[zones->shift[z][dd->dim[d-1]]].min0;
 +                    zones->size[z].x1[dim] = comm->zone_d1[zones->shift[z][dd->dim[d-1]]].max1;
 +                }
 +                else if (d == 2)
 +                {
 +                    zones->size[z].x0[dim] = comm->zone_d2[zones->shift[z][dd->dim[d-2]]][zones->shift[z][dd->dim[d-1]]].min0;
 +                    zones->size[z].x1[dim] = comm->zone_d2[zones->shift[z][dd->dim[d-2]]][zones->shift[z][dd->dim[d-1]]].max1;
 +                }
 +            }
 +        }
 +
 +        rcs   = comm->cutoff;
 +        rcmbs = comm->cutoff_mbody;
 +        if (ddbox->tric_dir[dim])
 +        {
 +            rcs   /= ddbox->skew_fac[dim];
 +            rcmbs /= ddbox->skew_fac[dim];
 +        }
 +
 +        /* Set the lower limit for the shifted zone dimensions */
 +        for (z = zone_start; z < zone_end; z++)
 +        {
 +            if (zones->shift[z][dim] > 0)
 +            {
 +                dim = dd->dim[d];
 +                if (!dd->bGridJump || d == 0)
 +                {
 +                    zones->size[z].x0[dim] = comm->cell_x1[dim];
 +                    zones->size[z].x1[dim] = comm->cell_x1[dim] + rcs;
 +                }
 +                else
 +                {
 +                    /* Here we take the lower limit of the zone from
 +                     * the lowest domain of the zone below.
 +                     */
 +                    if (z < 4)
 +                    {
 +                        zones->size[z].x0[dim] =
 +                            comm->zone_d1[zones->shift[z][dd->dim[d-1]]].min1;
 +                    }
 +                    else
 +                    {
 +                        if (d == 1)
 +                        {
 +                            zones->size[z].x0[dim] =
 +                                zones->size[zone_perm[2][z-4]].x0[dim];
 +                        }
 +                        else
 +                        {
 +                            zones->size[z].x0[dim] =
 +                                comm->zone_d2[zones->shift[z][dd->dim[d-2]]][zones->shift[z][dd->dim[d-1]]].min1;
 +                        }
 +                    }
 +                    /* A temporary limit, is updated below */
 +                    zones->size[z].x1[dim] = zones->size[z].x0[dim];
 +
 +                    if (bDistMB)
 +                    {
 +                        for (zi = 0; zi < zones->nizone; zi++)
 +                        {
 +                            if (zones->shift[zi][dim] == 0)
 +                            {
 +                                /* This takes the whole zone into account.
 +                                 * With multiple pulses this will lead
 +                                 * to a larger zone then strictly necessary.
 +                                 */
 +                                zones->size[z].x1[dim] = max(zones->size[z].x1[dim],
 +                                                             zones->size[zi].x1[dim]+rcmbs);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        /* Loop over the i-zones to set the upper limit of each
 +         * j-zone they see.
 +         */
 +        for (zi = 0; zi < zones->nizone; zi++)
 +        {
 +            if (zones->shift[zi][dim] == 0)
 +            {
 +                for (z = zones->izone[zi].j0; z < zones->izone[zi].j1; z++)
 +                {
 +                    if (zones->shift[z][dim] > 0)
 +                    {
 +                        zones->size[z].x1[dim] = max(zones->size[z].x1[dim],
 +                                                     zones->size[zi].x1[dim]+rcs);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    for (z = zone_start; z < zone_end; z++)
 +    {
 +        /* Initialization only required to keep the compiler happy */
 +        rvec corner_min = {0, 0, 0}, corner_max = {0, 0, 0}, corner;
 +        int  nc, c;
 +
 +        /* To determine the bounding box for a zone we need to find
 +         * the extreme corners of 4, 2 or 1 corners.
 +         */
 +        nc = 1 << (ddbox->npbcdim - 1);
 +
 +        for (c = 0; c < nc; c++)
 +        {
 +            /* Set up a zone corner at x=0, ignoring trilinic couplings */
 +            corner[XX] = 0;
 +            if ((c & 1) == 0)
 +            {
 +                corner[YY] = zones->size[z].x0[YY];
 +            }
 +            else
 +            {
 +                corner[YY] = zones->size[z].x1[YY];
 +            }
 +            if ((c & 2) == 0)
 +            {
 +                corner[ZZ] = zones->size[z].x0[ZZ];
 +            }
 +            else
 +            {
 +                corner[ZZ] = zones->size[z].x1[ZZ];
 +            }
 +            if (dd->ndim == 1 && box[ZZ][YY] != 0)
 +            {
 +                /* With 1D domain decomposition the cg's are not in
 +                 * the triclinic box, but triclinic x-y and rectangular y-z.
 +                 * Shift y back, so it will later end up at 0.
 +                 */
 +                corner[YY] -= corner[ZZ]*box[ZZ][YY]/box[ZZ][ZZ];
 +            }
 +            /* Apply the triclinic couplings */
 +            for (i = YY; i < ddbox->npbcdim; i++)
 +            {
 +                for (j = XX; j < i; j++)
 +                {
 +                    corner[j] += corner[i]*box[i][j]/box[i][i];
 +                }
 +            }
 +            if (c == 0)
 +            {
 +                copy_rvec(corner, corner_min);
 +                copy_rvec(corner, corner_max);
 +            }
 +            else
 +            {
 +                for (i = 0; i < DIM; i++)
 +                {
 +                    corner_min[i] = min(corner_min[i], corner[i]);
 +                    corner_max[i] = max(corner_max[i], corner[i]);
 +                }
 +            }
 +        }
 +        /* Copy the extreme cornes without offset along x */
 +        for (i = 0; i < DIM; i++)
 +        {
 +            zones->size[z].bb_x0[i] = corner_min[i];
 +            zones->size[z].bb_x1[i] = corner_max[i];
 +        }
 +        /* Add the offset along x */
 +        zones->size[z].bb_x0[XX] += zones->size[z].x0[XX];
 +        zones->size[z].bb_x1[XX] += zones->size[z].x1[XX];
 +    }
 +
 +    if (zone_start == 0)
 +    {
 +        vol = 1;
 +        for (dim = 0; dim < DIM; dim++)
 +        {
 +            vol *= zones->size[0].x1[dim] - zones->size[0].x0[dim];
 +        }
 +        zones->dens_zone0 = (zones->cg_range[1] - zones->cg_range[0])/vol;
 +    }
 +
 +    if (debug)
 +    {
 +        for (z = zone_start; z < zone_end; z++)
 +        {
 +            fprintf(debug, "zone %d    %6.3f - %6.3f  %6.3f - %6.3f  %6.3f - %6.3f\n",
 +                    z,
 +                    zones->size[z].x0[XX], zones->size[z].x1[XX],
 +                    zones->size[z].x0[YY], zones->size[z].x1[YY],
 +                    zones->size[z].x0[ZZ], zones->size[z].x1[ZZ]);
 +            fprintf(debug, "zone %d bb %6.3f - %6.3f  %6.3f - %6.3f  %6.3f - %6.3f\n",
 +                    z,
 +                    zones->size[z].bb_x0[XX], zones->size[z].bb_x1[XX],
 +                    zones->size[z].bb_x0[YY], zones->size[z].bb_x1[YY],
 +                    zones->size[z].bb_x0[ZZ], zones->size[z].bb_x1[ZZ]);
 +        }
 +    }
 +}
 +
 +static int comp_cgsort(const void *a, const void *b)
 +{
 +    int           comp;
 +
 +    gmx_cgsort_t *cga, *cgb;
 +    cga = (gmx_cgsort_t *)a;
 +    cgb = (gmx_cgsort_t *)b;
 +
 +    comp = cga->nsc - cgb->nsc;
 +    if (comp == 0)
 +    {
 +        comp = cga->ind_gl - cgb->ind_gl;
 +    }
 +
 +    return comp;
 +}
 +
 +static void order_int_cg(int n, const gmx_cgsort_t *sort,
 +                         int *a, int *buf)
 +{
 +    int i;
 +
 +    /* Order the data */
 +    for (i = 0; i < n; i++)
 +    {
 +        buf[i] = a[sort[i].ind];
 +    }
 +
 +    /* Copy back to the original array */
 +    for (i = 0; i < n; i++)
 +    {
 +        a[i] = buf[i];
 +    }
 +}
 +
 +static void order_vec_cg(int n, const gmx_cgsort_t *sort,
 +                         rvec *v, rvec *buf)
 +{
 +    int i;
 +
 +    /* Order the data */
 +    for (i = 0; i < n; i++)
 +    {
 +        copy_rvec(v[sort[i].ind], buf[i]);
 +    }
 +
 +    /* Copy back to the original array */
 +    for (i = 0; i < n; i++)
 +    {
 +        copy_rvec(buf[i], v[i]);
 +    }
 +}
 +
 +static void order_vec_atom(int ncg, const int *cgindex, const gmx_cgsort_t *sort,
 +                           rvec *v, rvec *buf)
 +{
 +    int a, atot, cg, cg0, cg1, i;
 +
 +    if (cgindex == NULL)
 +    {
 +        /* Avoid the useless loop of the atoms within a cg */
 +        order_vec_cg(ncg, sort, v, buf);
 +
 +        return;
 +    }
 +
 +    /* Order the data */
 +    a = 0;
 +    for (cg = 0; cg < ncg; cg++)
 +    {
 +        cg0 = cgindex[sort[cg].ind];
 +        cg1 = cgindex[sort[cg].ind+1];
 +        for (i = cg0; i < cg1; i++)
 +        {
 +            copy_rvec(v[i], buf[a]);
 +            a++;
 +        }
 +    }
 +    atot = a;
 +
 +    /* Copy back to the original array */
 +    for (a = 0; a < atot; a++)
 +    {
 +        copy_rvec(buf[a], v[a]);
 +    }
 +}
 +
 +static void ordered_sort(int nsort2, gmx_cgsort_t *sort2,
 +                         int nsort_new, gmx_cgsort_t *sort_new,
 +                         gmx_cgsort_t *sort1)
 +{
 +    int i1, i2, i_new;
 +
 +    /* The new indices are not very ordered, so we qsort them */
 +    qsort_threadsafe(sort_new, nsort_new, sizeof(sort_new[0]), comp_cgsort);
 +
 +    /* sort2 is already ordered, so now we can merge the two arrays */
 +    i1    = 0;
 +    i2    = 0;
 +    i_new = 0;
 +    while (i2 < nsort2 || i_new < nsort_new)
 +    {
 +        if (i2 == nsort2)
 +        {
 +            sort1[i1++] = sort_new[i_new++];
 +        }
 +        else if (i_new == nsort_new)
 +        {
 +            sort1[i1++] = sort2[i2++];
 +        }
 +        else if (sort2[i2].nsc < sort_new[i_new].nsc ||
 +                 (sort2[i2].nsc == sort_new[i_new].nsc &&
 +                  sort2[i2].ind_gl < sort_new[i_new].ind_gl))
 +        {
 +            sort1[i1++] = sort2[i2++];
 +        }
 +        else
 +        {
 +            sort1[i1++] = sort_new[i_new++];
 +        }
 +    }
 +}
 +
 +static int dd_sort_order(gmx_domdec_t *dd, t_forcerec *fr, int ncg_home_old)
 +{
 +    gmx_domdec_sort_t *sort;
 +    gmx_cgsort_t      *cgsort, *sort_i;
 +    int                ncg_new, nsort2, nsort_new, i, *a, moved, *ibuf;
 +    int                sort_last, sort_skip;
 +
 +    sort = dd->comm->sort;
 +
 +    a = fr->ns.grid->cell_index;
 +
 +    moved = NSGRID_SIGNAL_MOVED_FAC*fr->ns.grid->ncells;
 +
 +    if (ncg_home_old >= 0)
 +    {
 +        /* The charge groups that remained in the same ns grid cell
 +         * are completely ordered. So we can sort efficiently by sorting
 +         * the charge groups that did move into the stationary list.
 +         */
 +        ncg_new   = 0;
 +        nsort2    = 0;
 +        nsort_new = 0;
 +        for (i = 0; i < dd->ncg_home; i++)
 +        {
 +            /* Check if this cg did not move to another node */
 +            if (a[i] < moved)
 +            {
 +                if (i >= ncg_home_old || a[i] != sort->sort[i].nsc)
 +                {
 +                    /* This cg is new on this node or moved ns grid cell */
 +                    if (nsort_new >= sort->sort_new_nalloc)
 +                    {
 +                        sort->sort_new_nalloc = over_alloc_dd(nsort_new+1);
 +                        srenew(sort->sort_new, sort->sort_new_nalloc);
 +                    }
 +                    sort_i = &(sort->sort_new[nsort_new++]);
 +                }
 +                else
 +                {
 +                    /* This cg did not move */
 +                    sort_i = &(sort->sort2[nsort2++]);
 +                }
 +                /* Sort on the ns grid cell indices
 +                 * and the global topology index.
 +                 * index_gl is irrelevant with cell ns,
 +                 * but we set it here anyhow to avoid a conditional.
 +                 */
 +                sort_i->nsc    = a[i];
 +                sort_i->ind_gl = dd->index_gl[i];
 +                sort_i->ind    = i;
 +                ncg_new++;
 +            }
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "ordered sort cgs: stationary %d moved %d\n",
 +                    nsort2, nsort_new);
 +        }
 +        /* Sort efficiently */
 +        ordered_sort(nsort2, sort->sort2, nsort_new, sort->sort_new,
 +                     sort->sort);
 +    }
 +    else
 +    {
 +        cgsort  = sort->sort;
 +        ncg_new = 0;
 +        for (i = 0; i < dd->ncg_home; i++)
 +        {
 +            /* Sort on the ns grid cell indices
 +             * and the global topology index
 +             */
 +            cgsort[i].nsc    = a[i];
 +            cgsort[i].ind_gl = dd->index_gl[i];
 +            cgsort[i].ind    = i;
 +            if (cgsort[i].nsc < moved)
 +            {
 +                ncg_new++;
 +            }
 +        }
 +        if (debug)
 +        {
 +            fprintf(debug, "qsort cgs: %d new home %d\n", dd->ncg_home, ncg_new);
 +        }
 +        /* Determine the order of the charge groups using qsort */
 +        qsort_threadsafe(cgsort, dd->ncg_home, sizeof(cgsort[0]), comp_cgsort);
 +    }
 +
 +    return ncg_new;
 +}
 +
 +static int dd_sort_order_nbnxn(gmx_domdec_t *dd, t_forcerec *fr)
 +{
 +    gmx_cgsort_t *sort;
 +    int           ncg_new, i, *a, na;
 +
 +    sort = dd->comm->sort->sort;
 +
 +    nbnxn_get_atomorder(fr->nbv->nbs, &a, &na);
 +
 +    ncg_new = 0;
 +    for (i = 0; i < na; i++)
 +    {
 +        if (a[i] >= 0)
 +        {
 +            sort[ncg_new].ind = a[i];
 +            ncg_new++;
 +        }
 +    }
 +
 +    return ncg_new;
 +}
 +
 +static void dd_sort_state(gmx_domdec_t *dd, int ePBC,
 +                          rvec *cgcm, t_forcerec *fr, t_state *state,
 +                          int ncg_home_old)
 +{
 +    gmx_domdec_sort_t *sort;
 +    gmx_cgsort_t      *cgsort, *sort_i;
 +    int               *cgindex;
 +    int                ncg_new, i, *ibuf, cgsize;
 +    rvec              *vbuf;
 +
 +    sort = dd->comm->sort;
 +
 +    if (dd->ncg_home > sort->sort_nalloc)
 +    {
 +        sort->sort_nalloc = over_alloc_dd(dd->ncg_home);
 +        srenew(sort->sort, sort->sort_nalloc);
 +        srenew(sort->sort2, sort->sort_nalloc);
 +    }
 +    cgsort = sort->sort;
 +
 +    switch (fr->cutoff_scheme)
 +    {
 +        case ecutsGROUP:
 +            ncg_new = dd_sort_order(dd, fr, ncg_home_old);
 +            break;
 +        case ecutsVERLET:
 +            ncg_new = dd_sort_order_nbnxn(dd, fr);
 +            break;
 +        default:
 +            gmx_incons("unimplemented");
 +            ncg_new = 0;
 +    }
 +
 +    /* We alloc with the old size, since cgindex is still old */
 +    vec_rvec_check_alloc(&dd->comm->vbuf, dd->cgindex[dd->ncg_home]);
 +    vbuf = dd->comm->vbuf.v;
 +
 +    if (dd->comm->bCGs)
 +    {
 +        cgindex = dd->cgindex;
 +    }
 +    else
 +    {
 +        cgindex = NULL;
 +    }
 +
 +    /* Remove the charge groups which are no longer at home here */
 +    dd->ncg_home = ncg_new;
 +    if (debug)
 +    {
 +        fprintf(debug, "Set the new home charge group count to %d\n",
 +                dd->ncg_home);
 +    }
 +
 +    /* Reorder the state */
 +    for (i = 0; i < estNR; i++)
 +    {
 +        if (EST_DISTR(i) && (state->flags & (1<<i)))
 +        {
 +            switch (i)
 +            {
 +                case estX:
 +                    order_vec_atom(dd->ncg_home, cgindex, cgsort, state->x, vbuf);
 +                    break;
 +                case estV:
 +                    order_vec_atom(dd->ncg_home, cgindex, cgsort, state->v, vbuf);
 +                    break;
 +                case estSDX:
 +                    order_vec_atom(dd->ncg_home, cgindex, cgsort, state->sd_X, vbuf);
 +                    break;
 +                case estCGP:
 +                    order_vec_atom(dd->ncg_home, cgindex, cgsort, state->cg_p, vbuf);
 +                    break;
 +                case estLD_RNG:
 +                case estLD_RNGI:
 +                case estDISRE_INITF:
 +                case estDISRE_RM3TAV:
 +                case estORIRE_INITF:
 +                case estORIRE_DTAV:
 +                    /* No ordering required */
 +                    break;
 +                default:
 +                    gmx_incons("Unknown state entry encountered in dd_sort_state");
 +                    break;
 +            }
 +        }
 +    }
 +    if (fr->cutoff_scheme == ecutsGROUP)
 +    {
 +        /* Reorder cgcm */
 +        order_vec_cg(dd->ncg_home, cgsort, cgcm, vbuf);
 +    }
 +
 +    if (dd->ncg_home+1 > sort->ibuf_nalloc)
 +    {
 +        sort->ibuf_nalloc = over_alloc_dd(dd->ncg_home+1);
 +        srenew(sort->ibuf, sort->ibuf_nalloc);
 +    }
 +    ibuf = sort->ibuf;
 +    /* Reorder the global cg index */
 +    order_int_cg(dd->ncg_home, cgsort, dd->index_gl, ibuf);
 +    /* Reorder the cginfo */
 +    order_int_cg(dd->ncg_home, cgsort, fr->cginfo, ibuf);
 +    /* Rebuild the local cg index */
 +    if (dd->comm->bCGs)
 +    {
 +        ibuf[0] = 0;
 +        for (i = 0; i < dd->ncg_home; i++)
 +        {
 +            cgsize    = dd->cgindex[cgsort[i].ind+1] - dd->cgindex[cgsort[i].ind];
 +            ibuf[i+1] = ibuf[i] + cgsize;
 +        }
 +        for (i = 0; i < dd->ncg_home+1; i++)
 +        {
 +            dd->cgindex[i] = ibuf[i];
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; i < dd->ncg_home+1; i++)
 +        {
 +            dd->cgindex[i] = i;
 +        }
 +    }
 +    /* Set the home atom number */
 +    dd->nat_home = dd->cgindex[dd->ncg_home];
 +
 +    if (fr->cutoff_scheme == ecutsVERLET)
 +    {
 +        /* The atoms are now exactly in grid order, update the grid order */
 +        nbnxn_set_atomorder(fr->nbv->nbs);
 +    }
 +    else
 +    {
 +        /* Copy the sorted ns cell indices back to the ns grid struct */
 +        for (i = 0; i < dd->ncg_home; i++)
 +        {
 +            fr->ns.grid->cell_index[i] = cgsort[i].nsc;
 +        }
 +        fr->ns.grid->nr = dd->ncg_home;
 +    }
 +}
 +
 +static void add_dd_statistics(gmx_domdec_t *dd)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                ddnat;
 +
 +    comm = dd->comm;
 +
 +    for (ddnat = ddnatZONE; ddnat < ddnatNR; ddnat++)
 +    {
 +        comm->sum_nat[ddnat-ddnatZONE] +=
 +            comm->nat[ddnat] - comm->nat[ddnat-1];
 +    }
 +    comm->ndecomp++;
 +}
 +
 +void reset_dd_statistics_counters(gmx_domdec_t *dd)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                ddnat;
 +
 +    comm = dd->comm;
 +
 +    /* Reset all the statistics and counters for total run counting */
 +    for (ddnat = ddnatZONE; ddnat < ddnatNR; ddnat++)
 +    {
 +        comm->sum_nat[ddnat-ddnatZONE] = 0;
 +    }
 +    comm->ndecomp   = 0;
 +    comm->nload     = 0;
 +    comm->load_step = 0;
 +    comm->load_sum  = 0;
 +    comm->load_max  = 0;
 +    clear_ivec(comm->load_lim);
 +    comm->load_mdf = 0;
 +    comm->load_pme = 0;
 +}
 +
 +void print_dd_statistics(t_commrec *cr, t_inputrec *ir, FILE *fplog)
 +{
 +    gmx_domdec_comm_t *comm;
 +    int                ddnat;
 +    double             av;
 +
 +    comm = cr->dd->comm;
 +
 +    gmx_sumd(ddnatNR-ddnatZONE, comm->sum_nat, cr);
 +
 +    if (fplog == NULL)
 +    {
 +        return;
 +    }
 +
 +    fprintf(fplog, "\n    D O M A I N   D E C O M P O S I T I O N   S T A T I S T I C S\n\n");
 +
 +    for (ddnat = ddnatZONE; ddnat < ddnatNR; ddnat++)
 +    {
 +        av = comm->sum_nat[ddnat-ddnatZONE]/comm->ndecomp;
 +        switch (ddnat)
 +        {
 +            case ddnatZONE:
 +                fprintf(fplog,
 +                        " av. #atoms communicated per step for force:  %d x %.1f\n",
 +                        2, av);
 +                break;
 +            case ddnatVSITE:
 +                if (cr->dd->vsite_comm)
 +                {
 +                    fprintf(fplog,
 +                            " av. #atoms communicated per step for vsites: %d x %.1f\n",
 +                            (EEL_PME(ir->coulombtype) || ir->coulombtype == eelEWALD) ? 3 : 2,
 +                            av);
 +                }
 +                break;
 +            case ddnatCON:
 +                if (cr->dd->constraint_comm)
 +                {
 +                    fprintf(fplog,
 +                            " av. #atoms communicated per step for LINCS:  %d x %.1f\n",
 +                            1 + ir->nLincsIter, av);
 +                }
 +                break;
 +            default:
 +                gmx_incons(" Unknown type for DD statistics");
 +        }
 +    }
 +    fprintf(fplog, "\n");
 +
 +    if (comm->bRecordLoad && EI_DYNAMICS(ir->eI))
 +    {
 +        print_dd_load_av(fplog, cr->dd);
 +    }
 +}
 +
 +void dd_partition_system(FILE                *fplog,
 +                         gmx_large_int_t      step,
 +                         t_commrec           *cr,
 +                         gmx_bool             bMasterState,
 +                         int                  nstglobalcomm,
 +                         t_state             *state_global,
 +                         gmx_mtop_t          *top_global,
 +                         t_inputrec          *ir,
 +                         t_state             *state_local,
 +                         rvec               **f,
 +                         t_mdatoms           *mdatoms,
 +                         gmx_localtop_t      *top_local,
 +                         t_forcerec          *fr,
 +                         gmx_vsite_t         *vsite,
 +                         gmx_shellfc_t        shellfc,
 +                         gmx_constr_t         constr,
 +                         t_nrnb              *nrnb,
 +                         gmx_wallcycle_t      wcycle,
 +                         gmx_bool             bVerbose)
 +{
 +    gmx_domdec_t      *dd;
 +    gmx_domdec_comm_t *comm;
 +    gmx_ddbox_t        ddbox = {0};
 +    t_block           *cgs_gl;
 +    gmx_large_int_t    step_pcoupl;
 +    rvec               cell_ns_x0, cell_ns_x1;
 +    int                i, j, n, cg0 = 0, ncg_home_old = -1, ncg_moved, nat_f_novirsum;
 +    gmx_bool           bBoxChanged, bNStGlobalComm, bDoDLB, bCheckDLB, bTurnOnDLB, bLogLoad;
 +    gmx_bool           bRedist, bSortCG, bResortAll;
 +    ivec               ncells_old = {0, 0, 0}, ncells_new = {0, 0, 0}, np;
 +    real               grid_density;
 +    char               sbuf[22];
 +
 +    dd   = cr->dd;
 +    comm = dd->comm;
 +
 +    bBoxChanged = (bMasterState || DEFORM(*ir));
 +    if (ir->epc != epcNO)
 +    {
 +        /* With nstpcouple > 1 pressure coupling happens.
 +         * one step after calculating the pressure.
 +         * Box scaling happens at the end of the MD step,
 +         * after the DD partitioning.
 +         * We therefore have to do DLB in the first partitioning
 +         * after an MD step where P-coupling occured.
 +         * We need to determine the last step in which p-coupling occurred.
 +         * MRS -- need to validate this for vv?
 +         */
 +        n = ir->nstpcouple;
 +        if (n == 1)
 +        {
 +            step_pcoupl = step - 1;
 +        }
 +        else
 +        {
 +            step_pcoupl = ((step - 1)/n)*n + 1;
 +        }
 +        if (step_pcoupl >= comm->partition_step)
 +        {
 +            bBoxChanged = TRUE;
 +        }
 +    }
 +
 +    bNStGlobalComm = (step % nstglobalcomm == 0);
 +
 +    if (!comm->bDynLoadBal)
 +    {
 +        bDoDLB = FALSE;
 +    }
 +    else
 +    {
 +        /* Should we do dynamic load balacing this step?
 +         * Since it requires (possibly expensive) global communication,
 +         * we might want to do DLB less frequently.
 +         */
 +        if (bBoxChanged || ir->epc != epcNO)
 +        {
 +            bDoDLB = bBoxChanged;
 +        }
 +        else
 +        {
 +            bDoDLB = bNStGlobalComm;
 +        }
 +    }
 +
 +    /* Check if we have recorded loads on the nodes */
 +    if (comm->bRecordLoad && dd_load_count(comm))
 +    {
 +        if (comm->eDLB == edlbAUTO && !comm->bDynLoadBal)
 +        {
 +            /* Check if we should use DLB at the second partitioning
 +             * and every 100 partitionings,
 +             * so the extra communication cost is negligible.
 +             */
 +            n         = max(100, nstglobalcomm);
 +            bCheckDLB = (comm->n_load_collect == 0 ||
 +                         comm->n_load_have % n == n-1);
 +        }
 +        else
 +        {
 +            bCheckDLB = FALSE;
 +        }
 +
 +        /* Print load every nstlog, first and last step to the log file */
 +        bLogLoad = ((ir->nstlog > 0 && step % ir->nstlog == 0) ||
 +                    comm->n_load_collect == 0 ||
 +                    (ir->nsteps >= 0 &&
 +                     (step + ir->nstlist > ir->init_step + ir->nsteps)));
 +
 +        /* Avoid extra communication due to verbose screen output
 +         * when nstglobalcomm is set.
 +         */
 +        if (bDoDLB || bLogLoad || bCheckDLB ||
 +            (bVerbose && (ir->nstlist == 0 || nstglobalcomm <= ir->nstlist)))
 +        {
 +            get_load_distribution(dd, wcycle);
 +            if (DDMASTER(dd))
 +            {
 +                if (bLogLoad)
 +                {
 +                    dd_print_load(fplog, dd, step-1);
 +                }
 +                if (bVerbose)
 +                {
 +                    dd_print_load_verbose(dd);
 +                }
 +            }
 +            comm->n_load_collect++;
 +
 +            if (bCheckDLB)
 +            {
 +                /* Since the timings are node dependent, the master decides */
 +                if (DDMASTER(dd))
 +                {
 +                    bTurnOnDLB =
 +                        (dd_force_imb_perf_loss(dd) >= DD_PERF_LOSS);
 +                    if (debug)
 +                    {
 +                        fprintf(debug, "step %s, imb loss %f\n",
 +                                gmx_step_str(step, sbuf),
 +                                dd_force_imb_perf_loss(dd));
 +                    }
 +                }
 +                dd_bcast(dd, sizeof(bTurnOnDLB), &bTurnOnDLB);
 +                if (bTurnOnDLB)
 +                {
 +                    turn_on_dlb(fplog, cr, step);
 +                    bDoDLB = TRUE;
 +                }
 +            }
 +        }
 +        comm->n_load_have++;
 +    }
 +
 +    cgs_gl = &comm->cgs_gl;
 +
 +    bRedist = FALSE;
 +    if (bMasterState)
 +    {
 +        /* Clear the old state */
 +        clear_dd_indices(dd, 0, 0);
 +
 +        set_ddbox(dd, bMasterState, cr, ir, state_global->box,
 +                  TRUE, cgs_gl, state_global->x, &ddbox);
 +
 +        get_cg_distribution(fplog, step, dd, cgs_gl,
 +                            state_global->box, &ddbox, state_global->x);
 +
 +        dd_distribute_state(dd, cgs_gl,
 +                            state_global, state_local, f);
 +
 +        dd_make_local_cgs(dd, &top_local->cgs);
 +
 +        /* Ensure that we have space for the new distribution */
 +        dd_check_alloc_ncg(fr, state_local, f, dd->ncg_home);
 +
 +        if (fr->cutoff_scheme == ecutsGROUP)
 +        {
 +            calc_cgcm(fplog, 0, dd->ncg_home,
 +                      &top_local->cgs, state_local->x, fr->cg_cm);
 +        }
 +
 +        inc_nrnb(nrnb, eNR_CGCM, dd->nat_home);
 +
 +        dd_set_cginfo(dd->index_gl, 0, dd->ncg_home, fr, comm->bLocalCG);
 +
 +        cg0 = 0;
 +    }
 +    else if (state_local->ddp_count != dd->ddp_count)
 +    {
 +        if (state_local->ddp_count > dd->ddp_count)
 +        {
 +            gmx_fatal(FARGS, "Internal inconsistency state_local->ddp_count (%d) > dd->ddp_count (%d)", state_local->ddp_count, dd->ddp_count);
 +        }
 +
 +        if (state_local->ddp_count_cg_gl != state_local->ddp_count)
 +        {
 +            gmx_fatal(FARGS, "Internal inconsistency state_local->ddp_count_cg_gl (%d) != state_local->ddp_count (%d)", state_local->ddp_count_cg_gl, state_local->ddp_count);
 +        }
 +
 +        /* Clear the old state */
 +        clear_dd_indices(dd, 0, 0);
 +
 +        /* Build the new indices */
 +        rebuild_cgindex(dd, cgs_gl->index, state_local);
 +        make_dd_indices(dd, cgs_gl->index, 0);
 +
 +        if (fr->cutoff_scheme == ecutsGROUP)
 +        {
 +            /* Redetermine the cg COMs */
 +            calc_cgcm(fplog, 0, dd->ncg_home,
 +                      &top_local->cgs, state_local->x, fr->cg_cm);
 +        }
 +
 +        inc_nrnb(nrnb, eNR_CGCM, dd->nat_home);
 +
 +        dd_set_cginfo(dd->index_gl, 0, dd->ncg_home, fr, comm->bLocalCG);
 +
 +        set_ddbox(dd, bMasterState, cr, ir, state_local->box,
 +                  TRUE, &top_local->cgs, state_local->x, &ddbox);
 +
 +        bRedist = comm->bDynLoadBal;
 +    }
 +    else
 +    {
 +        /* We have the full state, only redistribute the cgs */
 +
 +        /* Clear the non-home indices */
 +        clear_dd_indices(dd, dd->ncg_home, dd->nat_home);
 +
 +        /* Avoid global communication for dim's without pbc and -gcom */
 +        if (!bNStGlobalComm)
 +        {
 +            copy_rvec(comm->box0, ddbox.box0    );
 +            copy_rvec(comm->box_size, ddbox.box_size);
 +        }
 +        set_ddbox(dd, bMasterState, cr, ir, state_local->box,
 +                  bNStGlobalComm, &top_local->cgs, state_local->x, &ddbox);
 +
 +        bBoxChanged = TRUE;
 +        bRedist     = TRUE;
 +    }
 +    /* For dim's without pbc and -gcom */
 +    copy_rvec(ddbox.box0, comm->box0    );
 +    copy_rvec(ddbox.box_size, comm->box_size);
 +
 +    set_dd_cell_sizes(dd, &ddbox, dynamic_dd_box(&ddbox, ir), bMasterState, bDoDLB,
 +                      step, wcycle);
 +
 +    if (comm->nstDDDumpGrid > 0 && step % comm->nstDDDumpGrid == 0)
 +    {
 +        write_dd_grid_pdb("dd_grid", step, dd, state_local->box, &ddbox);
 +    }
 +
 +    /* Check if we should sort the charge groups */
 +    if (comm->nstSortCG > 0)
 +    {
 +        bSortCG = (bMasterState ||
 +                   (bRedist && (step % comm->nstSortCG == 0)));
 +    }
 +    else
 +    {
 +        bSortCG = FALSE;
 +    }
 +
 +    ncg_home_old = dd->ncg_home;
 +
 +    ncg_moved = 0;
 +    if (bRedist)
 +    {
 +        wallcycle_sub_start(wcycle, ewcsDD_REDIST);
 +
 +        dd_redistribute_cg(fplog, step, dd, ddbox.tric_dir,
 +                           state_local, f, fr, mdatoms,
 +                           !bSortCG, nrnb, &cg0, &ncg_moved);
 +
 +        wallcycle_sub_stop(wcycle, ewcsDD_REDIST);
 +    }
 +
 +    get_nsgrid_boundaries(ddbox.nboundeddim, state_local->box,
 +                          dd, &ddbox,
 +                          &comm->cell_x0, &comm->cell_x1,
 +                          dd->ncg_home, fr->cg_cm,
 +                          cell_ns_x0, cell_ns_x1, &grid_density);
 +
 +    if (bBoxChanged)
 +    {
 +        comm_dd_ns_cell_sizes(dd, &ddbox, cell_ns_x0, cell_ns_x1, step);
 +    }
 +
 +    switch (fr->cutoff_scheme)
 +    {
 +        case ecutsGROUP:
 +            copy_ivec(fr->ns.grid->n, ncells_old);
 +            grid_first(fplog, fr->ns.grid, dd, &ddbox, fr->ePBC,
 +                       state_local->box, cell_ns_x0, cell_ns_x1,
 +                       fr->rlistlong, grid_density);
 +            break;
 +        case ecutsVERLET:
 +            nbnxn_get_ncells(fr->nbv->nbs, &ncells_old[XX], &ncells_old[YY]);
 +            break;
 +        default:
 +            gmx_incons("unimplemented");
 +    }
 +    /* We need to store tric_dir for dd_get_ns_ranges called from ns.c */
 +    copy_ivec(ddbox.tric_dir, comm->tric_dir);
 +
 +    if (bSortCG)
 +    {
 +        wallcycle_sub_start(wcycle, ewcsDD_GRID);
 +
 +        /* Sort the state on charge group position.
 +         * This enables exact restarts from this step.
 +         * It also improves performance by about 15% with larger numbers
 +         * of atoms per node.
 +         */
 +
 +        /* Fill the ns grid with the home cell,
 +         * so we can sort with the indices.
 +         */
 +        set_zones_ncg_home(dd);
 +
 +        switch (fr->cutoff_scheme)
 +        {
 +            case ecutsVERLET:
 +                set_zones_size(dd, state_local->box, &ddbox, 0, 1);
 +
 +                nbnxn_put_on_grid(fr->nbv->nbs, fr->ePBC, state_local->box,
 +                                  0,
 +                                  comm->zones.size[0].bb_x0,
 +                                  comm->zones.size[0].bb_x1,
 +                                  0, dd->ncg_home,
 +                                  comm->zones.dens_zone0,
 +                                  fr->cginfo,
 +                                  state_local->x,
 +                                  ncg_moved, bRedist ? comm->moved : NULL,
 +                                  fr->nbv->grp[eintLocal].kernel_type,
 +                                  fr->nbv->grp[eintLocal].nbat);
 +
 +                nbnxn_get_ncells(fr->nbv->nbs, &ncells_new[XX], &ncells_new[YY]);
 +                break;
 +            case ecutsGROUP:
 +                fill_grid(fplog, &comm->zones, fr->ns.grid, dd->ncg_home,
 +                          0, dd->ncg_home, fr->cg_cm);
 +
 +                copy_ivec(fr->ns.grid->n, ncells_new);
 +                break;
 +            default:
 +                gmx_incons("unimplemented");
 +        }
 +
 +        bResortAll = bMasterState;
 +
 +        /* Check if we can user the old order and ns grid cell indices
 +         * of the charge groups to sort the charge groups efficiently.
 +         */
 +        if (ncells_new[XX] != ncells_old[XX] ||
 +            ncells_new[YY] != ncells_old[YY] ||
 +            ncells_new[ZZ] != ncells_old[ZZ])
 +        {
 +            bResortAll = TRUE;
 +        }
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "Step %s, sorting the %d home charge groups\n",
 +                    gmx_step_str(step, sbuf), dd->ncg_home);
 +        }
 +        dd_sort_state(dd, ir->ePBC, fr->cg_cm, fr, state_local,
 +                      bResortAll ? -1 : ncg_home_old);
 +        /* Rebuild all the indices */
 +        cg0 = 0;
 +        ga2la_clear(dd->ga2la);
 +
 +        wallcycle_sub_stop(wcycle, ewcsDD_GRID);
 +    }
 +
 +    wallcycle_sub_start(wcycle, ewcsDD_SETUPCOMM);
 +
 +    /* Setup up the communication and communicate the coordinates */
 +    setup_dd_communication(dd, state_local->box, &ddbox, fr, state_local, f);
 +
 +    /* Set the indices */
 +    make_dd_indices(dd, cgs_gl->index, cg0);
 +
 +    /* Set the charge group boundaries for neighbor searching */
 +    set_cg_boundaries(&comm->zones);
 +
 +    if (fr->cutoff_scheme == ecutsVERLET)
 +    {
 +        set_zones_size(dd, state_local->box, &ddbox,
 +                       bSortCG ? 1 : 0, comm->zones.n);
 +    }
 +
 +    wallcycle_sub_stop(wcycle, ewcsDD_SETUPCOMM);
 +
 +    /*
 +       write_dd_pdb("dd_home",step,"dump",top_global,cr,
 +                 -1,state_local->x,state_local->box);
 +     */
 +
 +    wallcycle_sub_start(wcycle, ewcsDD_MAKETOP);
 +
 +    /* Extract a local topology from the global topology */
 +    for (i = 0; i < dd->ndim; i++)
 +    {
 +        np[dd->dim[i]] = comm->cd[i].np;
 +    }
 +    dd_make_local_top(fplog, dd, &comm->zones, dd->npbcdim, state_local->box,
 +                      comm->cellsize_min, np,
 +                      fr,
 +                      fr->cutoff_scheme == ecutsGROUP ? fr->cg_cm : state_local->x,
 +                      vsite, top_global, top_local);
 +
 +    wallcycle_sub_stop(wcycle, ewcsDD_MAKETOP);
 +
 +    wallcycle_sub_start(wcycle, ewcsDD_MAKECONSTR);
 +
 +    /* Set up the special atom communication */
 +    n = comm->nat[ddnatZONE];
 +    for (i = ddnatZONE+1; i < ddnatNR; i++)
 +    {
 +        switch (i)
 +        {
 +            case ddnatVSITE:
 +                if (vsite && vsite->n_intercg_vsite)
 +                {
 +                    n = dd_make_local_vsites(dd, n, top_local->idef.il);
 +                }
 +                break;
 +            case ddnatCON:
 +                if (dd->bInterCGcons || dd->bInterCGsettles)
 +                {
 +                    /* Only for inter-cg constraints we need special code */
 +                    n = dd_make_local_constraints(dd, n, top_global, fr->cginfo,
 +                                                  constr, ir->nProjOrder,
 +                                                  top_local->idef.il);
 +                }
 +                break;
 +            default:
 +                gmx_incons("Unknown special atom type setup");
 +        }
 +        comm->nat[i] = n;
 +    }
 +
 +    wallcycle_sub_stop(wcycle, ewcsDD_MAKECONSTR);
 +
 +    wallcycle_sub_start(wcycle, ewcsDD_TOPOTHER);
 +
 +    /* Make space for the extra coordinates for virtual site
 +     * or constraint communication.
 +     */
 +    state_local->natoms = comm->nat[ddnatNR-1];
 +    if (state_local->natoms > state_local->nalloc)
 +    {
 +        dd_realloc_state(state_local, f, state_local->natoms);
 +    }
 +
 +    if (fr->bF_NoVirSum)
 +    {
 +        if (vsite && vsite->n_intercg_vsite)
 +        {
 +            nat_f_novirsum = comm->nat[ddnatVSITE];
 +        }
 +        else
 +        {
 +            if (EEL_FULL(ir->coulombtype) && dd->n_intercg_excl > 0)
 +            {
 +                nat_f_novirsum = dd->nat_tot;
 +            }
 +            else
 +            {
 +                nat_f_novirsum = dd->nat_home;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        nat_f_novirsum = 0;
 +    }
 +
 +    /* Set the number of atoms required for the force calculation.
 +     * Forces need to be constrained when using a twin-range setup
 +     * or with energy minimization. For simple simulations we could
 +     * avoid some allocation, zeroing and copying, but this is
 +     * probably not worth the complications ande checking.
 +     */
 +    forcerec_set_ranges(fr, dd->ncg_home, dd->ncg_tot,
 +                        dd->nat_tot, comm->nat[ddnatCON], nat_f_novirsum);
 +
 +    /* We make the all mdatoms up to nat_tot_con.
 +     * We could save some work by only setting invmass
 +     * between nat_tot and nat_tot_con.
 +     */
 +    /* This call also sets the new number of home particles to dd->nat_home */
 +    atoms2md(top_global, ir,
 +             comm->nat[ddnatCON], dd->gatindex, 0, dd->nat_home, mdatoms);
 +
 +    /* Now we have the charges we can sort the FE interactions */
 +    dd_sort_local_top(dd, mdatoms, top_local);
 +
 +    if (vsite != NULL)
 +    {
 +        /* Now we have updated mdatoms, we can do the last vsite bookkeeping */
 +        split_vsites_over_threads(top_local->idef.il, mdatoms, FALSE, vsite);
 +    }
 +
 +    if (shellfc)
 +    {
 +        /* Make the local shell stuff, currently no communication is done */
 +        make_local_shells(cr, mdatoms, shellfc);
 +    }
 +
 +    if (ir->implicit_solvent)
 +    {
 +        make_local_gb(cr, fr->born, ir->gb_algorithm);
 +    }
 +
 +    init_bonded_thread_force_reduction(fr, &top_local->idef);
 +
 +    if (!(cr->duty & DUTY_PME))
 +    {
 +        /* Send the charges to our PME only node */
 +        gmx_pme_send_q(cr, mdatoms->nChargePerturbed,
 +                       mdatoms->chargeA, mdatoms->chargeB,
 +                       dd_pme_maxshift_x(dd), dd_pme_maxshift_y(dd));
 +    }
 +
 +    if (constr)
 +    {
 +        set_constraints(constr, top_local, ir, mdatoms, cr);
 +    }
 +
 +    if (ir->ePull != epullNO)
 +    {
 +        /* Update the local pull groups */
 +        dd_make_local_pull_groups(dd, ir->pull, mdatoms);
 +    }
 +
 +    if (ir->bRot)
 +    {
 +        /* Update the local rotation groups */
 +        dd_make_local_rotation_groups(dd, ir->rot);
 +    }
 +
 +
 +    add_dd_statistics(dd);
 +
 +    /* Make sure we only count the cycles for this DD partitioning */
 +    clear_dd_cycle_counts(dd);
 +
 +    /* Because the order of the atoms might have changed since
 +     * the last vsite construction, we need to communicate the constructing
 +     * atom coordinates again (for spreading the forces this MD step).
 +     */
 +    dd_move_x_vsites(dd, state_local->box, state_local->x);
 +
 +    wallcycle_sub_stop(wcycle, ewcsDD_TOPOTHER);
 +
 +    if (comm->nstDDDump > 0 && step % comm->nstDDDump == 0)
 +    {
 +        dd_move_x(dd, state_local->box, state_local->x);
 +        write_dd_pdb("dd_dump", step, "dump", top_global, cr,
 +                     -1, state_local->x, state_local->box);
 +    }
 +
 +    /* Store the partitioning step */
 +    comm->partition_step = step;
 +
 +    /* Increase the DD partitioning counter */
 +    dd->ddp_count++;
 +    /* The state currently matches this DD partitioning count, store it */
 +    state_local->ddp_count = dd->ddp_count;
 +    if (bMasterState)
 +    {
 +        /* The DD master node knows the complete cg distribution,
 +         * store the count so we can possibly skip the cg info communication.
 +         */
 +        comm->master_cg_ddp_count = (bSortCG ? 0 : dd->ddp_count);
 +    }
 +
 +    if (comm->DD_debug > 0)
 +    {
 +        /* Set the env var GMX_DD_DEBUG if you suspect corrupted indices */
 +        check_index_consistency(dd, top_global->natoms, ncg_mtop(top_global),
 +                                "after partitioning");
 +    }
 +}
index 4000452599d25ced67d806c394da1bed87342cc0,0000000000000000000000000000000000000000..8a732d8706029e4c5538a638d2f539359fd7e34a
mode 100644,000000..100644
--- /dev/null
@@@ -1,3170 -1,0 +1,3174 @@@
-     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;
 +/* -*- 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 "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 "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           */
 +    real   *refproj; /* starting or target projecions  */
 +    /* 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   */
 +
 +    gmx_bool            bNeedDoEdsam; /* if any of the options mon, linfix, ...
 +                                       * is used (i.e. apart from flooding)   */
 +    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 */
 +
 +
 +/* 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
 +     * subroutine again, because these routines are rarely used simultanely */
 +    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_large_int_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_large_int_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_large_int_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 */
 +
-         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)
 +    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_large_int_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_large_int_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++)
 +    {
++        /* 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;
++
 +        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++)
 +    {
-             nice_legend(&setname, &nsets, &LegendStr, "RADCON radius", "nm", get_EDgroupChar(nr_edi, nED));
-         }
++        if (edi->bNeedDoEdsam) /* 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 */
 +
 +
 +    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))
 +    {
 +        /* 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);
 +
 +        /* 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  );
 +            copy_rvecn(edi->sref.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: */
 +        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++)
 +    {
 +        /* Allocate space for ED buffer */
 +        snew(edi->buf, 1);
 +        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_large_int_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++;
 +        if (edi->bNeedDoEdsam)
 +        {
 +
 +            buf = edi->buf->do_edsam;
 +
 +            if (ed->bFirst)
 +            {
 +                /* initialise 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 (edi->bNeedDoEdsam) */
 +
 +        /* Prepare for the next ED group */
 +        edi = edi->next_edi;
 +
 +    } /* END of loop over ED groups */
 +
 +    ed->bFirst = FALSE;
 +}
index c2301f8414a7fd64c99b64b3ab1be96c5041a75e,0000000000000000000000000000000000000000..40a1c5d22e81a9ae67cdf1bfd20edb2b2ac0deea
mode 100644,000000..100644
--- /dev/null
@@@ -1,962 -1,0 +1,954 @@@
-                 case (efptMASS):
-                     index = F_DKDL;
-                     break;
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * GROwing Monsters And Cloning Shrimps
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +#include <assert.h>
 +#include "sysstuff.h"
 +#include "typedefs.h"
 +#include "macros.h"
 +#include "smalloc.h"
 +#include "macros.h"
 +#include "physics.h"
 +#include "force.h"
 +#include "nonbonded.h"
 +#include "names.h"
 +#include "network.h"
 +#include "pbc.h"
 +#include "ns.h"
 +#include "nrnb.h"
 +#include "bondf.h"
 +#include "mshift.h"
 +#include "txtdump.h"
 +#include "coulomb.h"
 +#include "pme.h"
 +#include "mdrun.h"
 +#include "domdec.h"
 +#include "partdec.h"
 +#include "qmmm.h"
 +#include "gmx_omp_nthreads.h"
 +
 +
 +void ns(FILE              *fp,
 +        t_forcerec        *fr,
 +        rvec               x[],
 +        matrix             box,
 +        gmx_groups_t      *groups,
 +        t_grpopts         *opts,
 +        gmx_localtop_t    *top,
 +        t_mdatoms         *md,
 +        t_commrec         *cr,
 +        t_nrnb            *nrnb,
 +        real              *lambda,
 +        real              *dvdlambda,
 +        gmx_grppairener_t *grppener,
 +        gmx_bool           bFillGrid,
 +        gmx_bool           bDoLongRangeNS)
 +{
 +    char   *ptr;
 +    int     nsearch;
 +
 +
 +    if (!fr->ns.nblist_initialized)
 +    {
 +        init_neighbor_list(fp, fr, md->homenr);
 +    }
 +
 +    if (fr->bTwinRange)
 +    {
 +        fr->nlr = 0;
 +    }
 +
 +    nsearch = search_neighbours(fp, fr, x, box, top, groups, cr, nrnb, md,
 +                                lambda, dvdlambda, grppener,
 +                                bFillGrid, bDoLongRangeNS, TRUE);
 +    if (debug)
 +    {
 +        fprintf(debug, "nsearch = %d\n", nsearch);
 +    }
 +
 +    /* Check whether we have to do dynamic load balancing */
 +    /*if ((nsb->nstDlb > 0) && (mod(step,nsb->nstDlb) == 0))
 +       count_nb(cr,nsb,&(top->blocks[ebCGS]),nns,fr->nlr,
 +       &(top->idef),opts->ngener);
 +     */
 +    if (fr->ns.dump_nl > 0)
 +    {
 +        dump_nblist(fp, cr, fr, fr->ns.dump_nl);
 +    }
 +}
 +
 +static void reduce_thread_forces(int n, rvec *f,
 +                                 tensor vir,
 +                                 real *Vcorr,
 +                                 int efpt_ind, real *dvdl,
 +                                 int nthreads, f_thread_t *f_t)
 +{
 +    int t, i;
 +
 +    /* This reduction can run over any number of threads */
 +#pragma omp parallel for num_threads(gmx_omp_nthreads_get(emntBonded)) private(t) schedule(static)
 +    for (i = 0; i < n; i++)
 +    {
 +        for (t = 1; t < nthreads; t++)
 +        {
 +            rvec_inc(f[i], f_t[t].f[i]);
 +        }
 +    }
 +    for (t = 1; t < nthreads; t++)
 +    {
 +        *Vcorr += f_t[t].Vcorr;
 +        *dvdl  += f_t[t].dvdl[efpt_ind];
 +        m_add(vir, f_t[t].vir, vir);
 +    }
 +}
 +
 +void do_force_lowlevel(FILE       *fplog,   gmx_large_int_t step,
 +                       t_forcerec *fr,      t_inputrec *ir,
 +                       t_idef     *idef,    t_commrec  *cr,
 +                       t_nrnb     *nrnb,    gmx_wallcycle_t wcycle,
 +                       t_mdatoms  *md,
 +                       t_grpopts  *opts,
 +                       rvec       x[],      history_t  *hist,
 +                       rvec       f[],
 +                       rvec       f_longrange[],
 +                       gmx_enerdata_t *enerd,
 +                       t_fcdata   *fcd,
 +                       gmx_mtop_t     *mtop,
 +                       gmx_localtop_t *top,
 +                       gmx_genborn_t *born,
 +                       t_atomtypes *atype,
 +                       gmx_bool       bBornRadii,
 +                       matrix     box,
 +                       t_lambda   *fepvals,
 +                       real       *lambda,
 +                       t_graph    *graph,
 +                       t_blocka   *excl,
 +                       rvec       mu_tot[],
 +                       int        flags,
 +                       float      *cycles_pme)
 +{
 +    int         i, j, status;
 +    int         donb_flags;
 +    gmx_bool    bDoEpot, bSepDVDL, bSB;
 +    int         pme_flags;
 +    matrix      boxs;
 +    rvec        box_size;
 +    real        Vsr, Vlr, Vcorr = 0;
 +    t_pbc       pbc;
 +    real        dvdgb;
 +    char        buf[22];
 +    double      clam_i, vlam_i;
 +    real        dvdl_dum[efptNR], dvdl, dvdl_nb[efptNR], lam_i[efptNR];
 +    real        dvdlsum;
 +
 +#ifdef GMX_MPI
 +    double  t0 = 0.0, t1, t2, t3; /* time measurement for coarse load balancing */
 +#endif
 +
 +#define PRINT_SEPDVDL(s, v, dvdlambda) if (bSepDVDL) {fprintf(fplog, sepdvdlformat, s, v, dvdlambda); }
 +
 +
 +    set_pbc(&pbc, fr->ePBC, box);
 +
 +    /* reset free energy components */
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        dvdl_nb[i]  = 0;
 +        dvdl_dum[i] = 0;
 +    }
 +
 +    /* Reset box */
 +    for (i = 0; (i < DIM); i++)
 +    {
 +        box_size[i] = box[i][i];
 +    }
 +
 +    bSepDVDL = (fr->bSepDVDL && do_per_step(step, ir->nstlog));
 +    debug_gmx();
 +
 +    /* do QMMM first if requested */
 +    if (fr->bQMMM)
 +    {
 +        enerd->term[F_EQM] = calculate_QMMM(cr, x, f, fr, md);
 +    }
 +
 +    if (bSepDVDL)
 +    {
 +        fprintf(fplog, "Step %s: non-bonded V and dVdl for node %d:\n",
 +                gmx_step_str(step, buf), cr->nodeid);
 +    }
 +
 +    /* Call the short range functions all in one go. */
 +
 +#ifdef GMX_MPI
 +    /*#define TAKETIME ((cr->npmenodes) && (fr->timesteps < 12))*/
 +#define TAKETIME FALSE
 +    if (TAKETIME)
 +    {
 +        MPI_Barrier(cr->mpi_comm_mygroup);
 +        t0 = MPI_Wtime();
 +    }
 +#endif
 +
 +    if (ir->nwall)
 +    {
 +        /* foreign lambda component for walls */
 +        dvdl = do_walls(ir, fr, box, md, x, f, lambda[efptVDW],
 +                        enerd->grpp.ener[egLJSR], nrnb);
 +        PRINT_SEPDVDL("Walls", 0.0, dvdl);
 +        enerd->dvdl_lin[efptVDW] += dvdl;
 +    }
 +
 +    /* If doing GB, reset dvda and calculate the Born radii */
 +    if (ir->implicit_solvent)
 +    {
 +        wallcycle_sub_start(wcycle, ewcsNONBONDED);
 +
 +        for (i = 0; i < born->nr; i++)
 +        {
 +            fr->dvda[i] = 0;
 +        }
 +
 +        if (bBornRadii)
 +        {
 +            calc_gb_rad(cr, fr, ir, top, atype, x, &(fr->gblist), born, md, nrnb);
 +        }
 +
 +        wallcycle_sub_stop(wcycle, ewcsNONBONDED);
 +    }
 +
 +    where();
 +    /* We only do non-bonded calculation with group scheme here, the verlet
 +     * calls are done from do_force_cutsVERLET(). */
 +    if (fr->cutoff_scheme == ecutsGROUP && (flags & GMX_FORCE_NONBONDED))
 +    {
 +        donb_flags = 0;
 +        /* Add short-range interactions */
 +        donb_flags |= GMX_NONBONDED_DO_SR;
 +
 +        if (flags & GMX_FORCE_FORCES)
 +        {
 +            donb_flags |= GMX_NONBONDED_DO_FORCE;
 +        }
 +        if (flags & GMX_FORCE_ENERGY)
 +        {
 +            donb_flags |= GMX_NONBONDED_DO_POTENTIAL;
 +        }
 +        if (flags & GMX_FORCE_DO_LR)
 +        {
 +            donb_flags |= GMX_NONBONDED_DO_LR;
 +        }
 +
 +        wallcycle_sub_start(wcycle, ewcsNONBONDED);
 +        do_nonbonded(cr, fr, x, f, f_longrange, md, excl,
 +                     &enerd->grpp, box_size, nrnb,
 +                     lambda, dvdl_nb, -1, -1, donb_flags);
 +
 +        /* If we do foreign lambda and we have soft-core interactions
 +         * we have to recalculate the (non-linear) energies contributions.
 +         */
 +        if (fepvals->n_lambda > 0 && (flags & GMX_FORCE_DHDL) && fepvals->sc_alpha != 0)
 +        {
 +            for (i = 0; i < enerd->n_lambda; i++)
 +            {
 +                for (j = 0; j < efptNR; j++)
 +                {
 +                    lam_i[j] = (i == 0 ? lambda[j] : fepvals->all_lambda[j][i-1]);
 +                }
 +                reset_foreign_enerdata(enerd);
 +                do_nonbonded(cr, fr, x, f, f_longrange, md, excl,
 +                             &(enerd->foreign_grpp), box_size, nrnb,
 +                             lam_i, dvdl_dum, -1, -1,
 +                             (donb_flags & ~GMX_NONBONDED_DO_FORCE) | GMX_NONBONDED_DO_FOREIGNLAMBDA);
 +                sum_epot(&ir->opts, &(enerd->foreign_grpp), enerd->foreign_term);
 +                enerd->enerpart_lambda[i] += enerd->foreign_term[F_EPOT];
 +            }
 +        }
 +        wallcycle_sub_stop(wcycle, ewcsNONBONDED);
 +        where();
 +    }
 +
 +    /* If we are doing GB, calculate bonded forces and apply corrections
 +     * to the solvation forces */
 +    /* MRS: Eventually, many need to include free energy contribution here! */
 +    if (ir->implicit_solvent)
 +    {
 +        wallcycle_sub_start(wcycle, ewcsBONDED);
 +        calc_gb_forces(cr, md, born, top, atype, x, f, fr, idef,
 +                       ir->gb_algorithm, ir->sa_algorithm, nrnb, bBornRadii, &pbc, graph, enerd);
 +        wallcycle_sub_stop(wcycle, ewcsBONDED);
 +    }
 +
 +#ifdef GMX_MPI
 +    if (TAKETIME)
 +    {
 +        t1          = MPI_Wtime();
 +        fr->t_fnbf += t1-t0;
 +    }
 +#endif
 +
 +    if (fepvals->sc_alpha != 0)
 +    {
 +        enerd->dvdl_nonlin[efptVDW] += dvdl_nb[efptVDW];
 +    }
 +    else
 +    {
 +        enerd->dvdl_lin[efptVDW] += dvdl_nb[efptVDW];
 +    }
 +
 +    if (fepvals->sc_alpha != 0)
 +
 +    /* even though coulomb part is linear, we already added it, beacuse we
 +       need to go through the vdw calculation anyway */
 +    {
 +        enerd->dvdl_nonlin[efptCOUL] += dvdl_nb[efptCOUL];
 +    }
 +    else
 +    {
 +        enerd->dvdl_lin[efptCOUL] += dvdl_nb[efptCOUL];
 +    }
 +
 +    Vsr = 0;
 +    if (bSepDVDL)
 +    {
 +        for (i = 0; i < enerd->grpp.nener; i++)
 +        {
 +            Vsr +=
 +                (fr->bBHAM ?
 +                 enerd->grpp.ener[egBHAMSR][i] :
 +                 enerd->grpp.ener[egLJSR][i])
 +                + enerd->grpp.ener[egCOULSR][i] + enerd->grpp.ener[egGB][i];
 +        }
 +        dvdlsum = dvdl_nb[efptVDW] + dvdl_nb[efptCOUL];
 +        PRINT_SEPDVDL("VdW and Coulomb SR particle-p.", Vsr, dvdlsum);
 +    }
 +    debug_gmx();
 +
 +
 +    if (debug)
 +    {
 +        pr_rvecs(debug, 0, "fshift after SR", fr->fshift, SHIFTS);
 +    }
 +
 +    /* Shift the coordinates. Must be done before bonded forces and PPPM,
 +     * but is also necessary for SHAKE and update, therefore it can NOT
 +     * go when no bonded forces have to be evaluated.
 +     */
 +
 +    /* Here sometimes we would not need to shift with NBFonly,
 +     * but we do so anyhow for consistency of the returned coordinates.
 +     */
 +    if (graph)
 +    {
 +        shift_self(graph, box, x);
 +        if (TRICLINIC(box))
 +        {
 +            inc_nrnb(nrnb, eNR_SHIFTX, 2*graph->nnodes);
 +        }
 +        else
 +        {
 +            inc_nrnb(nrnb, eNR_SHIFTX, graph->nnodes);
 +        }
 +    }
 +    /* Check whether we need to do bondeds or correct for exclusions */
 +    if (fr->bMolPBC &&
 +        ((flags & GMX_FORCE_BONDED)
 +         || EEL_RF(fr->eeltype) || EEL_FULL(fr->eeltype)))
 +    {
 +        /* Since all atoms are in the rectangular or triclinic unit-cell,
 +         * only single box vector shifts (2 in x) are required.
 +         */
 +        set_pbc_dd(&pbc, fr->ePBC, cr->dd, TRUE, box);
 +    }
 +    debug_gmx();
 +
 +    if (flags & GMX_FORCE_BONDED)
 +    {
 +        wallcycle_sub_start(wcycle, ewcsBONDED);
 +        calc_bonds(fplog, cr->ms,
 +                   idef, x, hist, f, fr, &pbc, graph, enerd, nrnb, lambda, md, fcd,
 +                   DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL, atype, born,
 +                   flags,
 +                   fr->bSepDVDL && do_per_step(step, ir->nstlog), step);
 +
 +        /* Check if we have to determine energy differences
 +         * at foreign lambda's.
 +         */
 +        if (fepvals->n_lambda > 0 && (flags & GMX_FORCE_DHDL) &&
 +            idef->ilsort != ilsortNO_FE)
 +        {
 +            if (idef->ilsort != ilsortFE_SORTED)
 +            {
 +                gmx_incons("The bonded interactions are not sorted for free energy");
 +            }
 +            for (i = 0; i < enerd->n_lambda; i++)
 +            {
 +                reset_foreign_enerdata(enerd);
 +                for (j = 0; j < efptNR; j++)
 +                {
 +                    lam_i[j] = (i == 0 ? lambda[j] : fepvals->all_lambda[j][i-1]);
 +                }
 +                calc_bonds_lambda(fplog, idef, x, fr, &pbc, graph, &(enerd->foreign_grpp), enerd->foreign_term, nrnb, lam_i, md,
 +                                  fcd, DOMAINDECOMP(cr) ? cr->dd->gatindex : NULL);
 +                sum_epot(&ir->opts, &(enerd->foreign_grpp), enerd->foreign_term);
 +                enerd->enerpart_lambda[i] += enerd->foreign_term[F_EPOT];
 +            }
 +        }
 +        debug_gmx();
 +
 +        wallcycle_sub_stop(wcycle, ewcsBONDED);
 +    }
 +
 +    where();
 +
 +    *cycles_pme = 0;
 +    if (EEL_FULL(fr->eeltype))
 +    {
 +        bSB = (ir->nwall == 2);
 +        if (bSB)
 +        {
 +            copy_mat(box, boxs);
 +            svmul(ir->wall_ewald_zfac, boxs[ZZ], boxs[ZZ]);
 +            box_size[ZZ] *= ir->wall_ewald_zfac;
 +        }
 +
 +        clear_mat(fr->vir_el_recip);
 +
 +        if (fr->bEwald)
 +        {
 +            Vcorr = 0;
 +            dvdl  = 0;
 +
 +            /* With the Verlet scheme exclusion forces are calculated
 +             * in the non-bonded kernel.
 +             */
 +            /* The TPI molecule does not have exclusions with the rest
 +             * of the system and no intra-molecular PME grid contributions
 +             * will be calculated in gmx_pme_calc_energy.
 +             */
 +            if ((ir->cutoff_scheme == ecutsGROUP && fr->n_tpi == 0) ||
 +                ir->ewald_geometry != eewg3D ||
 +                ir->epsilon_surface != 0)
 +            {
 +                int nthreads, t;
 +
 +                wallcycle_sub_start(wcycle, ewcsEWALD_CORRECTION);
 +
 +                if (fr->n_tpi > 0)
 +                {
 +                    gmx_fatal(FARGS, "TPI with PME currently only works in a 3D geometry with tin-foil boundary conditions");
 +                }
 +
 +                nthreads = gmx_omp_nthreads_get(emntBonded);
 +#pragma omp parallel for num_threads(nthreads) schedule(static)
 +                for (t = 0; t < nthreads; t++)
 +                {
 +                    int     s, e, i;
 +                    rvec   *fnv;
 +                    tensor *vir;
 +                    real   *Vcorrt, *dvdlt;
 +                    if (t == 0)
 +                    {
 +                        fnv    = fr->f_novirsum;
 +                        vir    = &fr->vir_el_recip;
 +                        Vcorrt = &Vcorr;
 +                        dvdlt  = &dvdl;
 +                    }
 +                    else
 +                    {
 +                        fnv    = fr->f_t[t].f;
 +                        vir    = &fr->f_t[t].vir;
 +                        Vcorrt = &fr->f_t[t].Vcorr;
 +                        dvdlt  = &fr->f_t[t].dvdl[efptCOUL];
 +                        for (i = 0; i < fr->natoms_force; i++)
 +                        {
 +                            clear_rvec(fnv[i]);
 +                        }
 +                        clear_mat(*vir);
 +                    }
 +                    *dvdlt  = 0;
 +                    *Vcorrt =
 +                        ewald_LRcorrection(fplog,
 +                                           fr->excl_load[t], fr->excl_load[t+1],
 +                                           cr, t, fr,
 +                                           md->chargeA,
 +                                           md->nChargePerturbed ? md->chargeB : NULL,
 +                                           ir->cutoff_scheme != ecutsVERLET,
 +                                           excl, x, bSB ? boxs : box, mu_tot,
 +                                           ir->ewald_geometry,
 +                                           ir->epsilon_surface,
 +                                           fnv, *vir,
 +                                           lambda[efptCOUL], dvdlt);
 +                }
 +                if (nthreads > 1)
 +                {
 +                    reduce_thread_forces(fr->natoms_force, fr->f_novirsum,
 +                                         fr->vir_el_recip,
 +                                         &Vcorr, efptCOUL, &dvdl,
 +                                         nthreads, fr->f_t);
 +                }
 +
 +                wallcycle_sub_stop(wcycle, ewcsEWALD_CORRECTION);
 +            }
 +
 +            if (fr->n_tpi == 0)
 +            {
 +                Vcorr += ewald_charge_correction(cr, fr, lambda[efptCOUL], box,
 +                                                 &dvdl, fr->vir_el_recip);
 +            }
 +
 +            PRINT_SEPDVDL("Ewald excl./charge/dip. corr.", Vcorr, dvdl);
 +            enerd->dvdl_lin[efptCOUL] += dvdl;
 +        }
 +
 +        status = 0;
 +        Vlr    = 0;
 +        dvdl   = 0;
 +        switch (fr->eeltype)
 +        {
 +            case eelPME:
 +            case eelPMESWITCH:
 +            case eelPMEUSER:
 +            case eelPMEUSERSWITCH:
 +            case eelP3M_AD:
 +                if (cr->duty & DUTY_PME)
 +                {
 +                    assert(fr->n_tpi >= 0);
 +                    if (fr->n_tpi == 0 || (flags & GMX_FORCE_STATECHANGED))
 +                    {
 +                        pme_flags = GMX_PME_SPREAD_Q | GMX_PME_SOLVE;
 +                        if (flags & GMX_FORCE_FORCES)
 +                        {
 +                            pme_flags |= GMX_PME_CALC_F;
 +                        }
 +                        if (flags & (GMX_FORCE_VIRIAL | GMX_FORCE_ENERGY))
 +                        {
 +                            pme_flags |= GMX_PME_CALC_ENER_VIR;
 +                        }
 +                        if (fr->n_tpi > 0)
 +                        {
 +                            /* We don't calculate f, but we do want the potential */
 +                            pme_flags |= GMX_PME_CALC_POT;
 +                        }
 +                        wallcycle_start(wcycle, ewcPMEMESH);
 +                        status = gmx_pme_do(fr->pmedata,
 +                                            md->start, md->homenr - fr->n_tpi,
 +                                            x, fr->f_novirsum,
 +                                            md->chargeA, md->chargeB,
 +                                            bSB ? boxs : box, cr,
 +                                            DOMAINDECOMP(cr) ? dd_pme_maxshift_x(cr->dd) : 0,
 +                                            DOMAINDECOMP(cr) ? dd_pme_maxshift_y(cr->dd) : 0,
 +                                            nrnb, wcycle,
 +                                            fr->vir_el_recip, fr->ewaldcoeff,
 +                                            &Vlr, lambda[efptCOUL], &dvdl,
 +                                            pme_flags);
 +                        *cycles_pme = wallcycle_stop(wcycle, ewcPMEMESH);
 +
 +                        /* We should try to do as little computation after
 +                         * this as possible, because parallel PME synchronizes
 +                         * the nodes, so we want all load imbalance of the rest
 +                         * of the force calculation to be before the PME call.
 +                         * DD load balancing is done on the whole time of
 +                         * the force call (without PME).
 +                         */
 +                    }
 +                    if (fr->n_tpi > 0)
 +                    {
 +                        /* Determine the PME grid energy of the test molecule
 +                         * with the PME grid potential of the other charges.
 +                         */
 +                        gmx_pme_calc_energy(fr->pmedata, fr->n_tpi,
 +                                            x + md->homenr - fr->n_tpi,
 +                                            md->chargeA + md->homenr - fr->n_tpi,
 +                                            &Vlr);
 +                    }
 +                    PRINT_SEPDVDL("PME mesh", Vlr, dvdl);
 +                }
 +                break;
 +            case eelEWALD:
 +                Vlr = do_ewald(fplog, FALSE, ir, x, fr->f_novirsum,
 +                               md->chargeA, md->chargeB,
 +                               box_size, cr, md->homenr,
 +                               fr->vir_el_recip, fr->ewaldcoeff,
 +                               lambda[efptCOUL], &dvdl, fr->ewald_table);
 +                PRINT_SEPDVDL("Ewald long-range", Vlr, dvdl);
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "No such electrostatics method implemented %s",
 +                          eel_names[fr->eeltype]);
 +        }
 +        if (status != 0)
 +        {
 +            gmx_fatal(FARGS, "Error %d in long range electrostatics routine %s",
 +                      status, EELTYPE(fr->eeltype));
 +        }
 +        /* Note that with separate PME nodes we get the real energies later */
 +        enerd->dvdl_lin[efptCOUL] += dvdl;
 +        enerd->term[F_COUL_RECIP]  = Vlr + Vcorr;
 +        if (debug)
 +        {
 +            fprintf(debug, "Vlr = %g, Vcorr = %g, Vlr_corr = %g\n",
 +                    Vlr, Vcorr, enerd->term[F_COUL_RECIP]);
 +            pr_rvecs(debug, 0, "vir_el_recip after corr", fr->vir_el_recip, DIM);
 +            pr_rvecs(debug, 0, "fshift after LR Corrections", fr->fshift, SHIFTS);
 +        }
 +    }
 +    else
 +    {
 +        if (EEL_RF(fr->eeltype))
 +        {
 +            /* With the Verlet scheme exclusion forces are calculated
 +             * in the non-bonded kernel.
 +             */
 +            if (ir->cutoff_scheme != ecutsVERLET && fr->eeltype != eelRF_NEC)
 +            {
 +                dvdl                   = 0;
 +                enerd->term[F_RF_EXCL] =
 +                    RF_excl_correction(fplog, fr, graph, md, excl, x, f,
 +                                       fr->fshift, &pbc, lambda[efptCOUL], &dvdl);
 +            }
 +
 +            enerd->dvdl_lin[efptCOUL] += dvdl;
 +            PRINT_SEPDVDL("RF exclusion correction",
 +                          enerd->term[F_RF_EXCL], dvdl);
 +        }
 +    }
 +    where();
 +    debug_gmx();
 +
 +    if (debug)
 +    {
 +        print_nrnb(debug, nrnb);
 +    }
 +    debug_gmx();
 +
 +#ifdef GMX_MPI
 +    if (TAKETIME)
 +    {
 +        t2 = MPI_Wtime();
 +        MPI_Barrier(cr->mpi_comm_mygroup);
 +        t3          = MPI_Wtime();
 +        fr->t_wait += t3-t2;
 +        if (fr->timesteps == 11)
 +        {
 +            fprintf(stderr, "* PP load balancing info: node %d, step %s, rel wait time=%3.0f%% , load string value: %7.2f\n",
 +                    cr->nodeid, gmx_step_str(fr->timesteps, buf),
 +                    100*fr->t_wait/(fr->t_wait+fr->t_fnbf),
 +                    (fr->t_fnbf+fr->t_wait)/fr->t_fnbf);
 +        }
 +        fr->timesteps++;
 +    }
 +#endif
 +
 +    if (debug)
 +    {
 +        pr_rvecs(debug, 0, "fshift after bondeds", fr->fshift, SHIFTS);
 +    }
 +
 +}
 +
 +void init_enerdata(int ngener, int n_lambda, gmx_enerdata_t *enerd)
 +{
 +    int i, n2;
 +
 +    for (i = 0; i < F_NRE; i++)
 +    {
 +        enerd->term[i]         = 0;
 +        enerd->foreign_term[i] = 0;
 +    }
 +
 +
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        enerd->dvdl_lin[i]     = 0;
 +        enerd->dvdl_nonlin[i]  = 0;
 +    }
 +
 +    n2 = ngener*ngener;
 +    if (debug)
 +    {
 +        fprintf(debug, "Creating %d sized group matrix for energies\n", n2);
 +    }
 +    enerd->grpp.nener         = n2;
 +    enerd->foreign_grpp.nener = n2;
 +    for (i = 0; (i < egNR); i++)
 +    {
 +        snew(enerd->grpp.ener[i], n2);
 +        snew(enerd->foreign_grpp.ener[i], n2);
 +    }
 +
 +    if (n_lambda)
 +    {
 +        enerd->n_lambda = 1 + n_lambda;
 +        snew(enerd->enerpart_lambda, enerd->n_lambda);
 +    }
 +    else
 +    {
 +        enerd->n_lambda = 0;
 +    }
 +}
 +
 +void destroy_enerdata(gmx_enerdata_t *enerd)
 +{
 +    int i;
 +
 +    for (i = 0; (i < egNR); i++)
 +    {
 +        sfree(enerd->grpp.ener[i]);
 +    }
 +
 +    for (i = 0; (i < egNR); i++)
 +    {
 +        sfree(enerd->foreign_grpp.ener[i]);
 +    }
 +
 +    if (enerd->n_lambda)
 +    {
 +        sfree(enerd->enerpart_lambda);
 +    }
 +}
 +
 +static real sum_v(int n, real v[])
 +{
 +    real t;
 +    int  i;
 +
 +    t = 0.0;
 +    for (i = 0; (i < n); i++)
 +    {
 +        t = t + v[i];
 +    }
 +
 +    return t;
 +}
 +
 +void sum_epot(t_grpopts *opts, gmx_grppairener_t *grpp, real *epot)
 +{
 +    int i;
 +
 +    /* Accumulate energies */
 +    epot[F_COUL_SR]  = sum_v(grpp->nener, grpp->ener[egCOULSR]);
 +    epot[F_LJ]       = sum_v(grpp->nener, grpp->ener[egLJSR]);
 +    epot[F_LJ14]     = sum_v(grpp->nener, grpp->ener[egLJ14]);
 +    epot[F_COUL14]   = sum_v(grpp->nener, grpp->ener[egCOUL14]);
 +    epot[F_COUL_LR]  = sum_v(grpp->nener, grpp->ener[egCOULLR]);
 +    epot[F_LJ_LR]    = sum_v(grpp->nener, grpp->ener[egLJLR]);
 +    /* We have already added 1-2,1-3, and 1-4 terms to F_GBPOL */
 +    epot[F_GBPOL]   += sum_v(grpp->nener, grpp->ener[egGB]);
 +
 +/* lattice part of LR doesnt belong to any group
 + * and has been added earlier
 + */
 +    epot[F_BHAM]     = sum_v(grpp->nener, grpp->ener[egBHAMSR]);
 +    epot[F_BHAM_LR]  = sum_v(grpp->nener, grpp->ener[egBHAMLR]);
 +
 +    epot[F_EPOT] = 0;
 +    for (i = 0; (i < F_EPOT); i++)
 +    {
 +        if (i != F_DISRESVIOL && i != F_ORIRESDEV)
 +        {
 +            epot[F_EPOT] += epot[i];
 +        }
 +    }
 +}
 +
 +void sum_dhdl(gmx_enerdata_t *enerd, real *lambda, t_lambda *fepvals)
 +{
 +    int    i, j, index;
 +    double dlam;
 +
 +    enerd->dvdl_lin[efptVDW] += enerd->term[F_DVDL_VDW];  /* include dispersion correction */
 +    enerd->term[F_DVDL]       = 0.0;
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if (fepvals->separate_dvdl[i])
 +        {
 +            /* could this be done more readably/compactly? */
 +            switch (i)
 +            {
++                case (efptMASS):
++                    index = F_DKDL;
++                    break;
 +                case (efptCOUL):
 +                    index = F_DVDL_COUL;
 +                    break;
 +                case (efptVDW):
 +                    index = F_DVDL_VDW;
 +                    break;
 +                case (efptBONDED):
 +                    index = F_DVDL_BONDED;
 +                    break;
 +                case (efptRESTRAINT):
 +                    index = F_DVDL_RESTRAINT;
 +                    break;
-         /* we don't need to worry about dvdl contributions to the current lambda, because
-            it's automatically zero */
-         /* first kinetic energy term */
-         dlam = (fepvals->all_lambda[efptMASS][i] - lambda[efptMASS]);
-         enerd->enerpart_lambda[i+1] += enerd->term[F_DKDL]*dlam;
 +                default:
 +                    index = F_DVDL;
 +                    break;
 +            }
 +            enerd->term[index] = enerd->dvdl_lin[i] + enerd->dvdl_nonlin[i];
 +            if (debug)
 +            {
 +                fprintf(debug, "dvdl-%s[%2d]: %f: non-linear %f + linear %f\n",
 +                        efpt_names[i], i, enerd->term[index], enerd->dvdl_nonlin[i], enerd->dvdl_lin[i]);
 +            }
 +        }
 +        else
 +        {
 +            enerd->term[F_DVDL] += enerd->dvdl_lin[i] + enerd->dvdl_nonlin[i];
 +            if (debug)
 +            {
 +                fprintf(debug, "dvd-%sl[%2d]: %f: non-linear %f + linear %f\n",
 +                        efpt_names[0], i, enerd->term[F_DVDL], enerd->dvdl_nonlin[i], enerd->dvdl_lin[i]);
 +            }
 +        }
 +    }
 +
 +    /* Notes on the foreign lambda free energy difference evaluation:
 +     * Adding the potential and ekin terms that depend linearly on lambda
 +     * as delta lam * dvdl to the energy differences is exact.
 +     * For the constraints this is not exact, but we have no other option
 +     * without literally changing the lengths and reevaluating the energies at each step.
 +     * (try to remedy this post 4.6 - MRS)
 +     * For the non-bonded LR term we assume that the soft-core (if present)
 +     * no longer affects the energy beyond the short-range cut-off,
 +     * which is a very good approximation (except for exotic settings).
 +     * (investigate how to overcome this post 4.6 - MRS)
 +     */
 +
 +    for (i = 0; i < fepvals->n_lambda; i++)
 +    {                                         /* note we are iterating over fepvals here!
 +                                                 For the current lam, dlam = 0 automatically,
 +                                                 so we don't need to add anything to the
 +                                                 enerd->enerpart_lambda[0] */
 +
-             if (j == efptMASS)
-             {
-                 continue;
-             }                            /* no other mass term to worry about */
++        /* we don't need to worry about dvdl_lin contributions to dE at
++           current lambda, because the contributions to the current
++           lambda are automatically zeroed */
 +
 +        for (j = 0; j < efptNR; j++)
 +        {
++            /* Note that this loop is over all dhdl components, not just the separated ones */
 +            dlam = (fepvals->all_lambda[j][i]-lambda[j]);
 +            enerd->enerpart_lambda[i+1] += dlam*enerd->dvdl_lin[j];
 +            if (debug)
 +            {
 +                fprintf(debug, "enerdiff lam %g: (%15s), non-linear %f linear %f*%f\n",
 +                        fepvals->all_lambda[j][i], efpt_names[j],
 +                        (enerd->enerpart_lambda[i+1] - enerd->enerpart_lambda[0]),
 +                        dlam, enerd->dvdl_lin[j]);
 +            }
 +        }
 +    }
 +}
 +
 +
 +void reset_foreign_enerdata(gmx_enerdata_t *enerd)
 +{
 +    int  i, j;
 +
 +    /* First reset all foreign energy components.  Foreign energies always called on
 +       neighbor search steps */
 +    for (i = 0; (i < egNR); i++)
 +    {
 +        for (j = 0; (j < enerd->grpp.nener); j++)
 +        {
 +            enerd->foreign_grpp.ener[i][j] = 0.0;
 +        }
 +    }
 +
 +    /* potential energy components */
 +    for (i = 0; (i <= F_EPOT); i++)
 +    {
 +        enerd->foreign_term[i] = 0.0;
 +    }
 +}
 +
 +void reset_enerdata(t_grpopts *opts,
 +                    t_forcerec *fr, gmx_bool bNS,
 +                    gmx_enerdata_t *enerd,
 +                    gmx_bool bMaster)
 +{
 +    gmx_bool bKeepLR;
 +    int      i, j;
 +
 +    /* First reset all energy components, except for the long range terms
 +     * on the master at non neighbor search steps, since the long range
 +     * terms have already been summed at the last neighbor search step.
 +     */
 +    bKeepLR = (fr->bTwinRange && !bNS);
 +    for (i = 0; (i < egNR); i++)
 +    {
 +        if (!(bKeepLR && bMaster && (i == egCOULLR || i == egLJLR)))
 +        {
 +            for (j = 0; (j < enerd->grpp.nener); j++)
 +            {
 +                enerd->grpp.ener[i][j] = 0.0;
 +            }
 +        }
 +    }
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        enerd->dvdl_lin[i]    = 0.0;
 +        enerd->dvdl_nonlin[i] = 0.0;
 +    }
 +
 +    /* Normal potential energy components */
 +    for (i = 0; (i <= F_EPOT); i++)
 +    {
 +        enerd->term[i] = 0.0;
 +    }
 +    /* Initialize the dVdlambda term with the long range contribution */
 +    /* Initialize the dvdl term with the long range contribution */
 +    enerd->term[F_DVDL]            = 0.0;
 +    enerd->term[F_DVDL_COUL]       = 0.0;
 +    enerd->term[F_DVDL_VDW]        = 0.0;
 +    enerd->term[F_DVDL_BONDED]     = 0.0;
 +    enerd->term[F_DVDL_RESTRAINT]  = 0.0;
 +    enerd->term[F_DKDL]            = 0.0;
 +    if (enerd->n_lambda > 0)
 +    {
 +        for (i = 0; i < enerd->n_lambda; i++)
 +        {
 +            enerd->enerpart_lambda[i] = 0.0;
 +        }
 +    }
 +    /* reset foreign energy data - separate function since we also call it elsewhere */
 +    reset_foreign_enerdata(enerd);
 +}
index ff9e49dbf6cba88d59c10a845999ab6de99b8cc8,0000000000000000000000000000000000000000..971c27d9514d4e6144e31390210f706279937ccf
mode 100644,000000..100644
--- /dev/null
@@@ -1,861 -1,0 +1,864 @@@
-     nth_tot = npp*nth_pp + npme*nth_pme;
 +/*  -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2008, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <string.h>
 +#include "gmx_wallcycle.h"
 +#include "gmx_cyclecounter.h"
 +#include "smalloc.h"
 +#include "gmx_fatal.h"
 +#include "md_logging.h"
 +#include "string2.h"
 +
 +#include "gromacs/utility/gmxmpi.h"
 +
 +/* DEBUG_WCYCLE adds consistency checking for the counters.
 + * It checks if you stop a counter different from the last
 + * one that was opened and if you do nest too deep.
 + */
 +/* #define DEBUG_WCYCLE */
 +
 +typedef struct
 +{
 +    int          n;
 +    gmx_cycles_t c;
 +    gmx_cycles_t start;
 +    gmx_cycles_t last;
 +} wallcc_t;
 +
 +typedef struct gmx_wallcycle
 +{
 +    wallcc_t        *wcc;
 +    /* variables for testing/debugging */
 +    gmx_bool         wc_barrier;
 +    wallcc_t        *wcc_all;
 +    int              wc_depth;
 +#ifdef DEBUG_WCYCLE
 +#define DEPTH_MAX 6
 +    int               counterlist[DEPTH_MAX];
 +    int               count_depth;
 +#endif
 +    int               ewc_prev;
 +    gmx_cycles_t      cycle_prev;
 +    gmx_large_int_t   reset_counters;
 +#ifdef GMX_MPI
 +    MPI_Comm          mpi_comm_mygroup;
 +#endif
 +    int               nthreads_pp;
 +    int               nthreads_pme;
 +#ifdef GMX_CYCLE_SUBCOUNTERS
 +    wallcc_t         *wcsc;
 +#endif
 +    double           *cycles_sum;
 +} gmx_wallcycle_t_t;
 +
 +/* Each name should not exceed 19 characters */
 +static const char *wcn[ewcNR] =
 +{
 +    "Run", "Step", "PP during PME", "Domain decomp.", "DD comm. load",
 +    "DD comm. bounds", "Vsite constr.", "Send X to PME", "Neighbor search", "Launch GPU ops.",
 +    "Comm. coord.", "Born radii", "Force", "Wait + Comm. F", "PME mesh",
 +    "PME redist. X/F", "PME spread/gather", "PME 3D-FFT", "PME 3D-FFT Comm.", "PME solve",
 +    "PME wait for PP", "Wait + Recv. PME F", "Wait GPU nonlocal", "Wait GPU local", "NB X/F buffer ops.",
 +    "Vsite spread", "Write traj.", "Update", "Constraints", "Comm. energies",
 +    "Enforced rotation", "Add rot. forces", "Test"
 +};
 +
 +static const char *wcsn[ewcsNR] =
 +{
 +    "DD redist.", "DD NS grid + sort", "DD setup comm.",
 +    "DD make top.", "DD make constr.", "DD top. other",
 +    "NS grid local", "NS grid non-loc.", "NS search local", "NS search non-loc.",
 +    "Bonded F", "Nonbonded F", "Ewald F correction",
 +    "NB X buffer ops.", "NB F buffer ops."
 +};
 +
 +gmx_bool wallcycle_have_counter(void)
 +{
 +    return gmx_cycles_have_counter();
 +}
 +
 +gmx_wallcycle_t wallcycle_init(FILE *fplog, int resetstep, t_commrec *cr,
 +                               int nthreads_pp, int nthreads_pme)
 +{
 +    gmx_wallcycle_t wc;
 +
 +
 +    if (!wallcycle_have_counter())
 +    {
 +        return NULL;
 +    }
 +
 +    snew(wc, 1);
 +
 +    wc->wc_barrier          = FALSE;
 +    wc->wcc_all             = NULL;
 +    wc->wc_depth            = 0;
 +    wc->ewc_prev            = -1;
 +    wc->reset_counters      = resetstep;
 +    wc->nthreads_pp         = nthreads_pp;
 +    wc->nthreads_pme        = nthreads_pme;
 +    wc->cycles_sum          = NULL;
 +
 +#ifdef GMX_MPI
 +    if (PAR(cr) && getenv("GMX_CYCLE_BARRIER") != NULL)
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "\nWill call MPI_Barrier before each cycle start/stop call\n\n");
 +        }
 +        wc->wc_barrier       = TRUE;
 +        wc->mpi_comm_mygroup = cr->mpi_comm_mygroup;
 +    }
 +#endif
 +
 +    snew(wc->wcc, ewcNR);
 +    if (getenv("GMX_CYCLE_ALL") != NULL)
 +    {
 +        if (fplog)
 +        {
 +            fprintf(fplog, "\nWill time all the code during the run\n\n");
 +        }
 +        snew(wc->wcc_all, ewcNR*ewcNR);
 +    }
 +
 +#ifdef GMX_CYCLE_SUBCOUNTERS
 +    snew(wc->wcsc, ewcsNR);
 +#endif
 +
 +#ifdef DEBUG_WCYCLE
 +    wc->count_depth = 0;
 +#endif
 +
 +    return wc;
 +}
 +
 +void wallcycle_destroy(gmx_wallcycle_t wc)
 +{
 +    if (wc == NULL)
 +    {
 +        return;
 +    }
 +
 +    if (wc->wcc != NULL)
 +    {
 +        sfree(wc->wcc);
 +    }
 +    if (wc->wcc_all != NULL)
 +    {
 +        sfree(wc->wcc_all);
 +    }
 +#ifdef GMX_CYCLE_SUBCOUNTERS
 +    if (wc->wcsc != NULL)
 +    {
 +        sfree(wc->wcsc);
 +    }
 +#endif
 +    sfree(wc);
 +}
 +
 +static void wallcycle_all_start(gmx_wallcycle_t wc, int ewc, gmx_cycles_t cycle)
 +{
 +    wc->ewc_prev   = ewc;
 +    wc->cycle_prev = cycle;
 +}
 +
 +static void wallcycle_all_stop(gmx_wallcycle_t wc, int ewc, gmx_cycles_t cycle)
 +{
 +    wc->wcc_all[wc->ewc_prev*ewcNR+ewc].n += 1;
 +    wc->wcc_all[wc->ewc_prev*ewcNR+ewc].c += cycle - wc->cycle_prev;
 +}
 +
 +
 +#ifdef DEBUG_WCYCLE
 +static void debug_start_check(gmx_wallcycle_t wc, int ewc)
 +{
 +    /* fprintf(stderr,"wcycle_start depth %d, %s\n",wc->count_depth,wcn[ewc]); */
 +
 +    if (wc->count_depth < 0 || wc->count_depth >= DEPTH_MAX)
 +    {
 +        gmx_fatal(FARGS, "wallcycle counter depth out of range: %d",
 +                  wc->count_depth);
 +    }
 +    wc->counterlist[wc->count_depth] = ewc;
 +    wc->count_depth++;
 +}
 +
 +static void debug_stop_check(gmx_wallcycle_t wc, int ewc)
 +{
 +    wc->count_depth--;
 +
 +    /* fprintf(stderr,"wcycle_stop depth %d, %s\n",wc->count_depth,wcn[ewc]); */
 +
 +    if (wc->count_depth < 0)
 +    {
 +        gmx_fatal(FARGS, "wallcycle counter depth out of range when stopping %s: %d", wcn[ewc], wc->count_depth);
 +    }
 +    if (wc->counterlist[wc->count_depth] != ewc)
 +    {
 +        gmx_fatal(FARGS, "wallcycle mismatch at stop, start %s, stop %s",
 +                  wcn[wc->counterlist[wc->count_depth]], wcn[ewc]);
 +    }
 +}
 +#endif
 +
 +void wallcycle_start(gmx_wallcycle_t wc, int ewc)
 +{
 +    gmx_cycles_t cycle;
 +
 +    if (wc == NULL)
 +    {
 +        return;
 +    }
 +
 +#ifdef GMX_MPI
 +    if (wc->wc_barrier)
 +    {
 +        MPI_Barrier(wc->mpi_comm_mygroup);
 +    }
 +#endif
 +
 +#ifdef DEBUG_WCYCLE
 +    debug_start_check(wc, ewc);
 +#endif
 +
 +    cycle              = gmx_cycles_read();
 +    wc->wcc[ewc].start = cycle;
 +    if (wc->wcc_all != NULL)
 +    {
 +        wc->wc_depth++;
 +        if (ewc == ewcRUN)
 +        {
 +            wallcycle_all_start(wc, ewc, cycle);
 +        }
 +        else if (wc->wc_depth == 3)
 +        {
 +            wallcycle_all_stop(wc, ewc, cycle);
 +        }
 +    }
 +}
 +
 +void wallcycle_start_nocount(gmx_wallcycle_t wc, int ewc)
 +{
 +    if (wc == NULL)
 +    {
 +        return;
 +    }
 +
 +    wallcycle_start(wc, ewc);
 +    wc->wcc[ewc].n--;
 +}
 +
 +double wallcycle_stop(gmx_wallcycle_t wc, int ewc)
 +{
 +    gmx_cycles_t cycle, last;
 +
 +    if (wc == NULL)
 +    {
 +        return 0;
 +    }
 +
 +#ifdef GMX_MPI
 +    if (wc->wc_barrier)
 +    {
 +        MPI_Barrier(wc->mpi_comm_mygroup);
 +    }
 +#endif
 +
 +#ifdef DEBUG_WCYCLE
 +    debug_stop_check(wc, ewc);
 +#endif
 +
 +    cycle           = gmx_cycles_read();
 +    last            = cycle - wc->wcc[ewc].start;
 +    wc->wcc[ewc].c += last;
 +    wc->wcc[ewc].n++;
 +    if (wc->wcc_all)
 +    {
 +        wc->wc_depth--;
 +        if (ewc == ewcRUN)
 +        {
 +            wallcycle_all_stop(wc, ewc, cycle);
 +        }
 +        else if (wc->wc_depth == 2)
 +        {
 +            wallcycle_all_start(wc, ewc, cycle);
 +        }
 +    }
 +
 +    return last;
 +}
 +
 +void wallcycle_reset_all(gmx_wallcycle_t wc)
 +{
 +    int i;
 +
 +    if (wc == NULL)
 +    {
 +        return;
 +    }
 +
 +    for (i = 0; i < ewcNR; i++)
 +    {
 +        wc->wcc[i].n = 0;
 +        wc->wcc[i].c = 0;
 +    }
 +    if (wc->wcc_all)
 +    {
 +        for (i = 0; i < ewcNR*ewcNR; i++)
 +        {
 +            wc->wcc_all[i].n = 0;
 +            wc->wcc_all[i].c = 0;
 +        }
 +    }
 +#ifdef GMX_CYCLE_SUBCOUNTERS
 +    for (i = 0; i < ewcsNR; i++)
 +    {
 +        wc->wcsc[i].n = 0;
 +        wc->wcsc[i].c = 0;
 +    }
 +#endif
 +}
 +
 +static gmx_bool is_pme_counter(int ewc)
 +{
 +    return (ewc >= ewcPMEMESH && ewc <= ewcPMEWAITCOMM);
 +}
 +
 +static gmx_bool is_pme_subcounter(int ewc)
 +{
 +    return (ewc >= ewcPME_REDISTXF && ewc < ewcPMEWAITCOMM);
 +}
 +
 +void wallcycle_sum(t_commrec *cr, gmx_wallcycle_t wc)
 +{
 +    wallcc_t *wcc;
 +    double   *cycles;
 +    double    cycles_n[ewcNR+ewcsNR], buf[ewcNR+ewcsNR], *cyc_all, *buf_all;
 +    int       i, j;
 +    int       nsum;
 +
 +    if (wc == NULL)
 +    {
 +        return;
 +    }
 +
 +    snew(wc->cycles_sum, ewcNR+ewcsNR);
 +    cycles = wc->cycles_sum;
 +
 +    wcc = wc->wcc;
 +
 +    for (i = 0; i < ewcNR; i++)
 +    {
 +        if (is_pme_counter(i) || (i == ewcRUN && cr->duty == DUTY_PME))
 +        {
 +            wcc[i].c *= wc->nthreads_pme;
 +
 +            if (wc->wcc_all)
 +            {
 +                for (j = 0; j < ewcNR; j++)
 +                {
 +                    wc->wcc_all[i*ewcNR+j].c *= wc->nthreads_pme;
 +                }
 +            }
 +        }
 +        else
 +        {
 +            wcc[i].c *= wc->nthreads_pp;
 +
 +            if (wc->wcc_all)
 +            {
 +                for (j = 0; j < ewcNR; j++)
 +                {
 +                    wc->wcc_all[i*ewcNR+j].c *= wc->nthreads_pp;
 +                }
 +            }
 +        }
 +    }
 +
 +    if (wcc[ewcDDCOMMLOAD].n > 0)
 +    {
 +        wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMLOAD].c;
 +    }
 +    if (wcc[ewcDDCOMMBOUND].n > 0)
 +    {
 +        wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMBOUND].c;
 +    }
 +    if (wcc[ewcPME_FFTCOMM].n > 0)
 +    {
 +        wcc[ewcPME_FFT].c -= wcc[ewcPME_FFTCOMM].c;
 +    }
 +
 +    if (cr->npmenodes == 0)
 +    {
 +        /* All nodes do PME (or no PME at all) */
 +        if (wcc[ewcPMEMESH].n > 0)
 +        {
 +            wcc[ewcFORCE].c -= wcc[ewcPMEMESH].c;
 +        }
 +    }
 +    else
 +    {
 +        /* The are PME-only nodes */
 +        if (wcc[ewcPMEMESH].n > 0)
 +        {
 +            /* This must be a PME only node, calculate the Wait + Comm. time */
 +            wcc[ewcPMEWAITCOMM].c = wcc[ewcRUN].c - wcc[ewcPMEMESH].c;
 +        }
 +    }
 +
 +    /* Store the cycles in a double buffer for summing */
 +    for (i = 0; i < ewcNR; i++)
 +    {
 +        cycles_n[i] = (double)wcc[i].n;
 +        cycles[i]   = (double)wcc[i].c;
 +    }
 +    nsum = ewcNR;
 +#ifdef GMX_CYCLE_SUBCOUNTERS
 +    for (i = 0; i < ewcsNR; i++)
 +    {
 +        wc->wcsc[i].c    *= wc->nthreads_pp;
 +        cycles_n[ewcNR+i] = (double)wc->wcsc[i].n;
 +        cycles[ewcNR+i]   = (double)wc->wcsc[i].c;
 +    }
 +    nsum += ewcsNR;
 +#endif
 +
 +#ifdef GMX_MPI
 +    if (cr->nnodes > 1)
 +    {
 +        MPI_Allreduce(cycles_n, buf, nsum, MPI_DOUBLE, MPI_MAX,
 +                      cr->mpi_comm_mysim);
 +        for (i = 0; i < ewcNR; i++)
 +        {
 +            wcc[i].n = (int)(buf[i] + 0.5);
 +        }
 +#ifdef GMX_CYCLE_SUBCOUNTERS
 +        for (i = 0; i < ewcsNR; i++)
 +        {
 +            wc->wcsc[i].n = (int)(buf[ewcNR+i] + 0.5);
 +        }
 +#endif
 +
 +        MPI_Allreduce(cycles, buf, nsum, MPI_DOUBLE, MPI_SUM,
 +                      cr->mpi_comm_mysim);
 +        for (i = 0; i < nsum; i++)
 +        {
 +            cycles[i] = buf[i];
 +        }
 +
 +        if (wc->wcc_all != NULL)
 +        {
 +            snew(cyc_all, ewcNR*ewcNR);
 +            snew(buf_all, ewcNR*ewcNR);
 +            for (i = 0; i < ewcNR*ewcNR; i++)
 +            {
 +                cyc_all[i] = wc->wcc_all[i].c;
 +            }
 +            MPI_Allreduce(cyc_all, buf_all, ewcNR*ewcNR, MPI_DOUBLE, MPI_SUM,
 +                          cr->mpi_comm_mysim);
 +            for (i = 0; i < ewcNR*ewcNR; i++)
 +            {
 +                wc->wcc_all[i].c = buf_all[i];
 +            }
 +            sfree(buf_all);
 +            sfree(cyc_all);
 +        }
 +    }
 +#endif
 +}
 +
 +static void print_cycles(FILE *fplog, double c2t, const char *name,
 +                         int nthreads_tot,
 +                         int nnodes, int nthreads,
 +                         int n, double c, double tot)
 +{
 +    char   num[11];
 +    char   thstr[6];
 +    double wallt;
 +
 +    if (c > 0)
 +    {
 +        if (n > 0)
 +        {
 +            snprintf(num, sizeof(num), "%10d", n);
 +            if (nthreads < 0)
 +            {
 +                snprintf(thstr, sizeof(thstr), "N/A");
 +            }
 +            else
 +            {
 +                snprintf(thstr, sizeof(thstr), "%4d", nthreads);
 +            }
 +        }
 +        else
 +        {
 +            sprintf(num, "          ");
 +            sprintf(thstr, "    ");
 +        }
 +        /* Convert the cycle count to wallclock time for this task */
 +        if (nthreads > 0)
 +        {
 +            /* Cycle count has been multiplied by the thread count,
 +             * correct for the number of threads used.
 +             */
 +            wallt = c*c2t*nthreads_tot/(double)(nnodes*nthreads);
 +        }
 +        else
 +        {
 +            /* nthreads=-1 signals total run time, no correction required */
 +            wallt = c*c2t;
 +        }
 +        fprintf(fplog, " %-19s %4d %4s %10s  %10.3f %12.3f   %5.1f\n",
 +                name, nnodes, thstr, num, wallt, c*1e-9, 100*c/tot);
 +    }
 +}
 +
 +static void print_gputimes(FILE *fplog, const char *name,
 +                           int n, double t, double tot_t)
 +{
 +    char num[11];
 +    char avg_perf[11];
 +
 +    if (n > 0)
 +    {
 +        snprintf(num, sizeof(num), "%10d", n);
 +        snprintf(avg_perf, sizeof(avg_perf), "%10.3f", t/n);
 +    }
 +    else
 +    {
 +        sprintf(num, "          ");
 +        sprintf(avg_perf, "          ");
 +    }
 +    if (t != tot_t)
 +    {
 +        fprintf(fplog, " %-29s %10s%12.3f   %s   %5.1f\n",
 +                name, num, t/1000, avg_perf, 100 * t/tot_t);
 +    }
 +    else
 +    {
 +        fprintf(fplog, " %-29s %10s%12.3f   %s   %5.1f\n",
 +                name, "", t/1000, avg_perf, 100.0);
 +    }
 +}
 +
 +void wallcycle_print(FILE *fplog, int nnodes, int npme, double realtime,
 +                     gmx_wallcycle_t wc, wallclock_gpu_t *gpu_t)
 +{
 +    double     *cycles;
 +    double      c2t, tot, tot_gpu, tot_cpu_overlap, gpu_cpu_ratio, sum, tot_k;
 +    int         i, j, npp, nth_pp, nth_pme, nth_tot;
 +    char        buf[STRLEN];
 +    const char *hline = "-----------------------------------------------------------------------------";
 +
 +    if (wc == NULL)
 +    {
 +        return;
 +    }
 +
 +    nth_pp  = wc->nthreads_pp;
 +    nth_pme = wc->nthreads_pme;
 +
 +    cycles = wc->cycles_sum;
 +
 +    if (npme > 0)
 +    {
 +        npp = nnodes - npme;
++
++        nth_tot = npp*nth_pp + npme*nth_pme;
 +    }
 +    else
 +    {
 +        npp  = nnodes;
 +        npme = nnodes;
++
++        nth_tot = npp*nth_pp;
 +    }
 +
 +    tot = cycles[ewcRUN];
 +
 +    /* Conversion factor from cycles to seconds */
 +    if (tot > 0)
 +    {
 +        c2t = realtime/tot;
 +    }
 +    else
 +    {
 +        c2t = 0;
 +    }
 +
 +    fprintf(fplog, "\n     R E A L   C Y C L E   A N D   T I M E   A C C O U N T I N G\n\n");
 +
 +    fprintf(fplog, " Computing:         Nodes   Th.     Count  Wall t (s)     G-Cycles       %c\n", '%');
 +    fprintf(fplog, "%s\n", hline);
 +    sum = 0;
 +    for (i = ewcPPDURINGPME+1; i < ewcNR; i++)
 +    {
 +        if (!is_pme_subcounter(i))
 +        {
 +            print_cycles(fplog, c2t, wcn[i], nth_tot,
 +                         is_pme_counter(i) ? npme : npp,
 +                         is_pme_counter(i) ? nth_pme : nth_pp,
 +                         wc->wcc[i].n, cycles[i], tot);
 +            sum += cycles[i];
 +        }
 +    }
 +    if (wc->wcc_all != NULL)
 +    {
 +        for (i = 0; i < ewcNR; i++)
 +        {
 +            for (j = 0; j < ewcNR; j++)
 +            {
 +                snprintf(buf, 9, "%-9s", wcn[i]);
 +                buf[9] = ' ';
 +                snprintf(buf+10, 9, "%-9s", wcn[j]);
 +                buf[19] = '\0';
 +                print_cycles(fplog, c2t, buf, nth_tot,
 +                             is_pme_counter(i) ? npme : npp,
 +                             is_pme_counter(i) ? nth_pme : nth_pp,
 +                             wc->wcc_all[i*ewcNR+j].n,
 +                             wc->wcc_all[i*ewcNR+j].c,
 +                             tot);
 +            }
 +        }
 +    }
 +    print_cycles(fplog, c2t, "Rest", nth_tot, npp, -1, 0, tot-sum, tot);
 +    fprintf(fplog, "%s\n", hline);
 +    print_cycles(fplog, c2t, "Total", nth_tot, nnodes, -1, 0, tot, tot);
 +    fprintf(fplog, "%s\n", hline);
 +
 +    if (wc->wcc[ewcPMEMESH].n > 0)
 +    {
 +        fprintf(fplog, "%s\n", hline);
 +        for (i = ewcPPDURINGPME+1; i < ewcNR; i++)
 +        {
 +            if (is_pme_subcounter(i))
 +            {
 +                print_cycles(fplog, c2t, wcn[i], nth_tot,
 +                             is_pme_counter(i) ? npme : npp,
 +                             is_pme_counter(i) ? nth_pme : nth_pp,
 +                             wc->wcc[i].n, cycles[i], tot);
 +            }
 +        }
 +        fprintf(fplog, "%s\n", hline);
 +    }
 +
 +#ifdef GMX_CYCLE_SUBCOUNTERS
 +    fprintf(fplog, "%s\n", hline);
 +    for (i = 0; i < ewcsNR; i++)
 +    {
 +        print_cycles(fplog, c2t, wcsn[i], nth_tot, npp, nth_pp,
 +                     wc->wcsc[i].n, cycles[ewcNR+i], tot);
 +    }
 +    fprintf(fplog, "%s\n", hline);
 +#endif
 +
 +    /* print GPU timing summary */
 +    if (gpu_t)
 +    {
 +        const char *k_log_str[2][2] = {
 +            {"Nonbonded F kernel", "Nonbonded F+ene k."},
 +            {"Nonbonded F+prune k.", "Nonbonded F+ene+prune k."}
 +        };
 +
 +        tot_gpu = gpu_t->pl_h2d_t + gpu_t->nb_h2d_t + gpu_t->nb_d2h_t;
 +
 +        /* add up the kernel timings */
 +        tot_k = 0.0;
 +        for (i = 0; i < 2; i++)
 +        {
 +            for (j = 0; j < 2; j++)
 +            {
 +                tot_k += gpu_t->ktime[i][j].t;
 +            }
 +        }
 +        tot_gpu += tot_k;
 +
 +        tot_cpu_overlap = wc->wcc[ewcFORCE].c;
 +        if (wc->wcc[ewcPMEMESH].n > 0)
 +        {
 +            tot_cpu_overlap += wc->wcc[ewcPMEMESH].c;
 +        }
 +        tot_cpu_overlap *= c2t * 1000; /* convert s to ms */
 +
 +        fprintf(fplog, "\n GPU timings\n%s\n", hline);
 +        fprintf(fplog, " Computing:                         Count  Wall t (s)      ms/step       %c\n", '%');
 +        fprintf(fplog, "%s\n", hline);
 +        print_gputimes(fplog, "Pair list H2D",
 +                       gpu_t->pl_h2d_c, gpu_t->pl_h2d_t, tot_gpu);
 +        print_gputimes(fplog, "X / q H2D",
 +                       gpu_t->nb_c, gpu_t->nb_h2d_t, tot_gpu);
 +
 +        for (i = 0; i < 2; i++)
 +        {
 +            for (j = 0; j < 2; j++)
 +            {
 +                if (gpu_t->ktime[i][j].c)
 +                {
 +                    print_gputimes(fplog, k_log_str[i][j],
 +                                   gpu_t->ktime[i][j].c, gpu_t->ktime[i][j].t, tot_gpu);
 +                }
 +            }
 +        }
 +
 +        print_gputimes(fplog, "F D2H",  gpu_t->nb_c, gpu_t->nb_d2h_t, tot_gpu);
 +        fprintf(fplog, "%s\n", hline);
 +        print_gputimes(fplog, "Total ", gpu_t->nb_c, tot_gpu, tot_gpu);
 +        fprintf(fplog, "%s\n", hline);
 +
 +        gpu_cpu_ratio = tot_gpu/tot_cpu_overlap;
 +        fprintf(fplog, "\nForce evaluation time GPU/CPU: %.3f ms/%.3f ms = %.3f\n",
 +                tot_gpu/gpu_t->nb_c, tot_cpu_overlap/wc->wcc[ewcFORCE].n,
 +                gpu_cpu_ratio);
 +
 +        /* only print notes related to CPU-GPU load balance with PME */
 +        if (wc->wcc[ewcPMEMESH].n > 0)
 +        {
 +            fprintf(fplog, "For optimal performance this ratio should be close to 1!\n");
 +
 +            /* print note if the imbalance is high with PME case in which
 +             * CPU-GPU load balancing is possible */
 +            if (gpu_cpu_ratio < 0.75 || gpu_cpu_ratio > 1.2)
 +            {
 +                /* Only the sim master calls this function, so always print to stderr */
 +                if (gpu_cpu_ratio < 0.75)
 +                {
 +                    if (npp > 1)
 +                    {
 +                        /* The user could have used -notunepme,
 +                         * but we currently can't check that here.
 +                         */
 +                        md_print_warn(NULL, fplog,
 +                                      "\nNOTE: The GPU has >25%% less load than the CPU. This imbalance causes\n"
 +                                      "      performance loss. Maybe the domain decomposition limits the PME tuning.\n"
 +                                      "      In that case, try setting the DD grid manually (-dd) or lowering -dds.");
 +                    }
 +                    else
 +                    {
 +                        /* We should not end up here, unless the box is
 +                         * too small for increasing the cut-off for PME tuning.
 +                         */
 +                        md_print_warn(NULL, fplog,
 +                                      "\nNOTE: The GPU has >25%% less load than the CPU. This imbalance causes\n"
 +                                      "      performance loss.");
 +                    }
 +                }
 +                if (gpu_cpu_ratio > 1.2)
 +                {
 +                    md_print_warn(NULL, fplog,
 +                                  "\nNOTE: The GPU has >20%% more load than the CPU. This imbalance causes\n"
 +                                  "      performance loss, consider using a shorter cut-off and a finer PME grid.");
 +                }
 +            }
 +        }
 +    }
 +
 +    if (wc->wcc[ewcNB_XF_BUF_OPS].n > 0 &&
 +        (cycles[ewcDOMDEC] > tot*0.1 ||
 +         cycles[ewcNS] > tot*0.1))
 +    {
 +        /* Only the sim master calls this function, so always print to stderr */
 +        if (wc->wcc[ewcDOMDEC].n == 0)
 +        {
 +            md_print_warn(NULL, fplog,
 +                          "NOTE: %d %% of the run time was spent in pair search,\n"
 +                          "      you might want to increase nstlist (this has no effect on accuracy)\n",
 +                          (int)(100*cycles[ewcNS]/tot+0.5));
 +        }
 +        else
 +        {
 +            md_print_warn(NULL, fplog,
 +                          "NOTE: %d %% of the run time was spent in domain decomposition,\n"
 +                          "      %d %% of the run time was spent in pair search,\n"
 +                          "      you might want to increase nstlist (this has no effect on accuracy)\n",
 +                          (int)(100*cycles[ewcDOMDEC]/tot+0.5),
 +                          (int)(100*cycles[ewcNS]/tot+0.5));
 +        }
 +    }
 +
 +    if (cycles[ewcMoveE] > tot*0.05)
 +    {
 +        /* Only the sim master calls this function, so always print to stderr */
 +        md_print_warn(NULL, fplog,
 +                      "NOTE: %d %% of the run time was spent communicating energies,\n"
 +                      "      you might want to use the -gcom option of mdrun\n",
 +                      (int)(100*cycles[ewcMoveE]/tot+0.5));
 +    }
 +}
 +
 +extern gmx_large_int_t wcycle_get_reset_counters(gmx_wallcycle_t wc)
 +{
 +    if (wc == NULL)
 +    {
 +        return -1;
 +    }
 +
 +    return wc->reset_counters;
 +}
 +
 +extern void wcycle_set_reset_counters(gmx_wallcycle_t wc, gmx_large_int_t reset_counters)
 +{
 +    if (wc == NULL)
 +    {
 +        return;
 +    }
 +
 +    wc->reset_counters = reset_counters;
 +}
 +
 +#ifdef GMX_CYCLE_SUBCOUNTERS
 +
 +void wallcycle_sub_start(gmx_wallcycle_t wc, int ewcs)
 +{
 +    if (wc != NULL)
 +    {
 +        wc->wcsc[ewcs].start = gmx_cycles_read();
 +    }
 +}
 +
 +void wallcycle_sub_stop(gmx_wallcycle_t wc, int ewcs)
 +{
 +    if (wc != NULL)
 +    {
 +        wc->wcsc[ewcs].c += gmx_cycles_read() - wc->wcsc[ewcs].start;
 +        wc->wcsc[ewcs].n++;
 +    }
 +}
 +
 +#endif /* GMX_CYCLE_SUBCOUNTERS */
index cb9f016320c938201d145ea41a4db05a3465f57c,0000000000000000000000000000000000000000..7d08756262f9e8108e0dc52c803a37181d43d7e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,771 -1,0 +1,772 @@@
-     real     ekin, temp, prescorr, enercorr, dvdlcorr;
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "typedefs.h"
 +#include "string2.h"
 +#include "smalloc.h"
 +#include "mdrun.h"
 +#include "domdec.h"
 +#include "mtop_util.h"
 +#include "gmx_wallcycle.h"
 +#include "vcm.h"
 +#include "nrnb.h"
 +#include "macros.h"
 +#include "md_logging.h"
 +#include "md_support.h"
 +
 +/* Is the signal in one simulation independent of other simulations? */
 +gmx_bool gs_simlocal[eglsNR] = { TRUE, FALSE, FALSE, TRUE };
 +
 +/* check which of the multisim simulations has the shortest number of
 +   steps and return that number of nsteps */
 +gmx_large_int_t get_multisim_nsteps(const t_commrec *cr,
 +                                    gmx_large_int_t  nsteps)
 +{
 +    gmx_large_int_t steps_out;
 +
 +    if MASTER(cr)
 +    {
 +        gmx_large_int_t *buf;
 +        int              s;
 +
 +        snew(buf, cr->ms->nsim);
 +
 +        buf[cr->ms->sim] = nsteps;
 +        gmx_sumli_sim(cr->ms->nsim, buf, cr->ms);
 +
 +        steps_out = -1;
 +        for (s = 0; s < cr->ms->nsim; s++)
 +        {
 +            /* find the smallest positive number */
 +            if (buf[s] >= 0 && ((steps_out < 0) || (buf[s] < steps_out)) )
 +            {
 +                steps_out = buf[s];
 +            }
 +        }
 +        sfree(buf);
 +
 +        /* if we're the limiting simulation, don't do anything */
 +        if (steps_out >= 0 && steps_out < nsteps)
 +        {
 +            char strbuf[255];
 +            snprintf(strbuf, 255, "Will stop simulation %%d after %s steps (another simulation will end then).\n", gmx_large_int_pfmt);
 +            fprintf(stderr, strbuf, cr->ms->sim, steps_out);
 +        }
 +    }
 +    /* broadcast to non-masters */
 +    gmx_bcast(sizeof(gmx_large_int_t), &steps_out, cr);
 +    return steps_out;
 +}
 +
 +int multisim_min(const gmx_multisim_t *ms, int nmin, int n)
 +{
 +    int     *buf;
 +    gmx_bool bPos, bEqual;
 +    int      s, d;
 +
 +    snew(buf, ms->nsim);
 +    buf[ms->sim] = n;
 +    gmx_sumi_sim(ms->nsim, buf, ms);
 +    bPos   = TRUE;
 +    bEqual = TRUE;
 +    for (s = 0; s < ms->nsim; s++)
 +    {
 +        bPos   = bPos   && (buf[s] > 0);
 +        bEqual = bEqual && (buf[s] == buf[0]);
 +    }
 +    if (bPos)
 +    {
 +        if (bEqual)
 +        {
 +            nmin = min(nmin, buf[0]);
 +        }
 +        else
 +        {
 +            /* Find the least common multiple */
 +            for (d = 2; d < nmin; d++)
 +            {
 +                s = 0;
 +                while (s < ms->nsim && d % buf[s] == 0)
 +                {
 +                    s++;
 +                }
 +                if (s == ms->nsim)
 +                {
 +                    /* We found the LCM and it is less than nmin */
 +                    nmin = d;
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +    sfree(buf);
 +
 +    return nmin;
 +}
 +
 +int multisim_nstsimsync(const t_commrec *cr,
 +                        const t_inputrec *ir, int repl_ex_nst)
 +{
 +    int nmin;
 +
 +    if (MASTER(cr))
 +    {
 +        nmin = INT_MAX;
 +        nmin = multisim_min(cr->ms, nmin, ir->nstlist);
 +        nmin = multisim_min(cr->ms, nmin, ir->nstcalcenergy);
 +        nmin = multisim_min(cr->ms, nmin, repl_ex_nst);
 +        if (nmin == INT_MAX)
 +        {
 +            gmx_fatal(FARGS, "Can not find an appropriate interval for inter-simulation communication, since nstlist, nstcalcenergy and -replex are all <= 0");
 +        }
 +        /* Avoid inter-simulation communication at every (second) step */
 +        if (nmin <= 2)
 +        {
 +            nmin = 10;
 +        }
 +    }
 +
 +    gmx_bcast(sizeof(int), &nmin, cr);
 +
 +    return nmin;
 +}
 +
 +void init_global_signals(globsig_t *gs, const t_commrec *cr,
 +                         const t_inputrec *ir, int repl_ex_nst)
 +{
 +    int i;
 +
 +    if (MULTISIM(cr))
 +    {
 +        gs->nstms = multisim_nstsimsync(cr, ir, repl_ex_nst);
 +        if (debug)
 +        {
 +            fprintf(debug, "Syncing simulations for checkpointing and termination every %d steps\n", gs->nstms);
 +        }
 +    }
 +    else
 +    {
 +        gs->nstms = 1;
 +    }
 +
 +    for (i = 0; i < eglsNR; i++)
 +    {
 +        gs->sig[i] = 0;
 +        gs->set[i] = 0;
 +    }
 +}
 +
 +void copy_coupling_state(t_state *statea, t_state *stateb,
 +                         gmx_ekindata_t *ekinda, gmx_ekindata_t *ekindb, t_grpopts* opts)
 +{
 +
 +    /* MRS note -- might be able to get rid of some of the arguments.  Look over it when it's all debugged */
 +
 +    int i, j, nc;
 +
 +    /* Make sure we have enough space for x and v */
 +    if (statea->nalloc > stateb->nalloc)
 +    {
 +        stateb->nalloc = statea->nalloc;
 +        srenew(stateb->x, stateb->nalloc);
 +        srenew(stateb->v, stateb->nalloc);
 +    }
 +
 +    stateb->natoms     = statea->natoms;
 +    stateb->ngtc       = statea->ngtc;
 +    stateb->nnhpres    = statea->nnhpres;
 +    stateb->veta       = statea->veta;
 +    if (ekinda)
 +    {
 +        copy_mat(ekinda->ekin, ekindb->ekin);
 +        for (i = 0; i < stateb->ngtc; i++)
 +        {
 +            ekindb->tcstat[i].T  = ekinda->tcstat[i].T;
 +            ekindb->tcstat[i].Th = ekinda->tcstat[i].Th;
 +            copy_mat(ekinda->tcstat[i].ekinh, ekindb->tcstat[i].ekinh);
 +            copy_mat(ekinda->tcstat[i].ekinf, ekindb->tcstat[i].ekinf);
 +            ekindb->tcstat[i].ekinscalef_nhc =  ekinda->tcstat[i].ekinscalef_nhc;
 +            ekindb->tcstat[i].ekinscaleh_nhc =  ekinda->tcstat[i].ekinscaleh_nhc;
 +            ekindb->tcstat[i].vscale_nhc     =  ekinda->tcstat[i].vscale_nhc;
 +        }
 +    }
 +    copy_rvecn(statea->x, stateb->x, 0, stateb->natoms);
 +    copy_rvecn(statea->v, stateb->v, 0, stateb->natoms);
 +    copy_mat(statea->box, stateb->box);
 +    copy_mat(statea->box_rel, stateb->box_rel);
 +    copy_mat(statea->boxv, stateb->boxv);
 +
 +    for (i = 0; i < stateb->ngtc; i++)
 +    {
 +        nc = i*opts->nhchainlength;
 +        for (j = 0; j < opts->nhchainlength; j++)
 +        {
 +            stateb->nosehoover_xi[nc+j]  = statea->nosehoover_xi[nc+j];
 +            stateb->nosehoover_vxi[nc+j] = statea->nosehoover_vxi[nc+j];
 +        }
 +    }
 +    if (stateb->nhpres_xi != NULL)
 +    {
 +        for (i = 0; i < stateb->nnhpres; i++)
 +        {
 +            nc = i*opts->nhchainlength;
 +            for (j = 0; j < opts->nhchainlength; j++)
 +            {
 +                stateb->nhpres_xi[nc+j]  = statea->nhpres_xi[nc+j];
 +                stateb->nhpres_vxi[nc+j] = statea->nhpres_vxi[nc+j];
 +            }
 +        }
 +    }
 +}
 +
 +real compute_conserved_from_auxiliary(t_inputrec *ir, t_state *state, t_extmass *MassQ)
 +{
 +    real quantity = 0;
 +    switch (ir->etc)
 +    {
 +        case etcNO:
 +            break;
 +        case etcBERENDSEN:
 +            break;
 +        case etcNOSEHOOVER:
 +            quantity = NPT_energy(ir, state, MassQ);
 +            break;
 +        case etcVRESCALE:
 +            quantity = vrescale_energy(&(ir->opts), state->therm_integral);
 +            break;
 +        default:
 +            break;
 +    }
 +    return quantity;
 +}
 +
 +void compute_globals(FILE *fplog, gmx_global_stat_t gstat, t_commrec *cr, t_inputrec *ir,
 +                     t_forcerec *fr, gmx_ekindata_t *ekind,
 +                     t_state *state, t_state *state_global, t_mdatoms *mdatoms,
 +                     t_nrnb *nrnb, t_vcm *vcm, gmx_wallcycle_t wcycle,
 +                     gmx_enerdata_t *enerd, tensor force_vir, tensor shake_vir, tensor total_vir,
 +                     tensor pres, rvec mu_tot, gmx_constr_t constr,
 +                     globsig_t *gs, gmx_bool bInterSimGS,
 +                     matrix box, gmx_mtop_t *top_global, real *pcurr,
 +                     int natoms, gmx_bool *bSumEkinhOld, int flags)
 +{
 +    int      i, gsi;
 +    real     gs_buf[eglsNR];
 +    tensor   corr_vir, corr_pres, shakeall_vir;
 +    gmx_bool bEner, bPres, bTemp, bVV;
 +    gmx_bool bRerunMD, bStopCM, bGStat, bIterate,
 +             bFirstIterate, bReadEkin, bEkinAveVel, bScaleEkin, bConstrain;
-         enerd->term[F_TEMP] = sum_ekin(&(ir->opts), ekind, &(enerd->term[F_DKDL]),
++    real     ekin, temp, prescorr, enercorr, dvdlcorr, dvdl_ekin;
 +
 +    /* translate CGLO flags to gmx_booleans */
 +    bRerunMD = flags & CGLO_RERUNMD;
 +    bStopCM  = flags & CGLO_STOPCM;
 +    bGStat   = flags & CGLO_GSTAT;
 +
 +    bReadEkin     = (flags & CGLO_READEKIN);
 +    bScaleEkin    = (flags & CGLO_SCALEEKIN);
 +    bEner         = flags & CGLO_ENERGY;
 +    bTemp         = flags & CGLO_TEMPERATURE;
 +    bPres         = (flags & CGLO_PRESSURE);
 +    bConstrain    = (flags & CGLO_CONSTRAINT);
 +    bIterate      = (flags & CGLO_ITERATE);
 +    bFirstIterate = (flags & CGLO_FIRSTITERATE);
 +
 +    /* we calculate a full state kinetic energy either with full-step velocity verlet
 +       or half step where we need the pressure */
 +
 +    bEkinAveVel = (ir->eI == eiVV || (ir->eI == eiVVAK && bPres) || bReadEkin);
 +
 +    /* in initalization, it sums the shake virial in vv, and to
 +       sums ekinh_old in leapfrog (or if we are calculating ekinh_old) for other reasons */
 +
 +    /* ########## Kinetic energy  ############## */
 +
 +    if (bTemp)
 +    {
 +        /* Non-equilibrium MD: this is parallellized, but only does communication
 +         * when there really is NEMD.
 +         */
 +
 +        if (PAR(cr) && (ekind->bNEMD))
 +        {
 +            accumulate_u(cr, &(ir->opts), ekind);
 +        }
 +        debug_gmx();
 +        if (bReadEkin)
 +        {
 +            restore_ekinstate_from_state(cr, ekind, &state_global->ekinstate);
 +        }
 +        else
 +        {
 +
 +            calc_ke_part(state, &(ir->opts), mdatoms, ekind, nrnb, bEkinAveVel, bIterate);
 +        }
 +
 +        debug_gmx();
 +    }
 +
 +    /* Calculate center of mass velocity if necessary, also parallellized */
 +    if (bStopCM)
 +    {
 +        calc_vcm_grp(fplog, mdatoms->start, mdatoms->homenr, mdatoms,
 +                     state->x, state->v, vcm);
 +    }
 +
 +    if (bTemp || bStopCM || bPres || bEner || bConstrain)
 +    {
 +        if (!bGStat)
 +        {
 +            /* We will not sum ekinh_old,
 +             * so signal that we still have to do it.
 +             */
 +            *bSumEkinhOld = TRUE;
 +
 +        }
 +        else
 +        {
 +            if (gs != NULL)
 +            {
 +                for (i = 0; i < eglsNR; i++)
 +                {
 +                    gs_buf[i] = gs->sig[i];
 +                }
 +            }
 +            if (PAR(cr))
 +            {
 +                wallcycle_start(wcycle, ewcMoveE);
 +                global_stat(fplog, gstat, cr, enerd, force_vir, shake_vir, mu_tot,
 +                            ir, ekind, constr, bStopCM ? vcm : NULL,
 +                            gs != NULL ? eglsNR : 0, gs_buf,
 +                            top_global, state,
 +                            *bSumEkinhOld, flags);
 +                wallcycle_stop(wcycle, ewcMoveE);
 +            }
 +            if (gs != NULL)
 +            {
 +                if (MULTISIM(cr) && bInterSimGS)
 +                {
 +                    if (MASTER(cr))
 +                    {
 +                        /* Communicate the signals between the simulations */
 +                        gmx_sum_sim(eglsNR, gs_buf, cr->ms);
 +                    }
 +                    /* Communicate the signals form the master to the others */
 +                    gmx_bcast(eglsNR*sizeof(gs_buf[0]), gs_buf, cr);
 +                }
 +                for (i = 0; i < eglsNR; i++)
 +                {
 +                    if (bInterSimGS || gs_simlocal[i])
 +                    {
 +                        /* Set the communicated signal only when it is non-zero,
 +                         * since signals might not be processed at each MD step.
 +                         */
 +                        gsi = (gs_buf[i] >= 0 ?
 +                               (int)(gs_buf[i] + 0.5) :
 +                               (int)(gs_buf[i] - 0.5));
 +                        if (gsi != 0)
 +                        {
 +                            gs->set[i] = gsi;
 +                        }
 +                        /* Turn off the local signal */
 +                        gs->sig[i] = 0;
 +                    }
 +                }
 +            }
 +            *bSumEkinhOld = FALSE;
 +        }
 +    }
 +
 +    if (!ekind->bNEMD && debug && bTemp && (vcm->nr > 0))
 +    {
 +        correct_ekin(debug,
 +                     mdatoms->start, mdatoms->start+mdatoms->homenr,
 +                     state->v, vcm->group_p[0],
 +                     mdatoms->massT, mdatoms->tmass, ekind->ekin);
 +    }
 +
 +    /* Do center of mass motion removal */
 +    if (bStopCM)
 +    {
 +        check_cm_grp(fplog, vcm, ir, 1);
 +        do_stopcm_grp(fplog, mdatoms->start, mdatoms->homenr, mdatoms->cVCM,
 +                      state->x, state->v, vcm);
 +        inc_nrnb(nrnb, eNR_STOPCM, mdatoms->homenr);
 +    }
 +
 +    if (bEner)
 +    {
 +        /* Calculate the amplitude of the cosine velocity profile */
 +        ekind->cosacc.vcos = ekind->cosacc.mvcos/mdatoms->tmass;
 +    }
 +
 +    if (bTemp)
 +    {
 +        /* Sum the kinetic energies of the groups & calc temp */
 +        /* compute full step kinetic energies if vv, or if vv-avek and we are computing the pressure with IR_NPT_TROTTER */
 +        /* three maincase:  VV with AveVel (md-vv), vv with AveEkin (md-vv-avek), leap with AveEkin (md).
 +           Leap with AveVel is not supported; it's not clear that it will actually work.
 +           bEkinAveVel: If TRUE, we simply multiply ekin by ekinscale to get a full step kinetic energy.
 +           If FALSE, we average ekinh_old and ekinh*ekinscale_nhc to get an averaged half step kinetic energy.
 +           bSaveEkinOld: If TRUE (in the case of iteration = bIterate is TRUE), we don't reset the ekinscale_nhc.
 +           If FALSE, we go ahead and erase over it.
 +         */
++        enerd->term[F_TEMP] = sum_ekin(&(ir->opts), ekind, &dvdl_ekin,
 +                                       bEkinAveVel, bIterate, bScaleEkin);
++        enerd->dvdl_lin[efptMASS] = (double) dvdl_ekin;
 +
 +        enerd->term[F_EKIN] = trace(ekind->ekin);
 +    }
 +
 +    /* ##########  Long range energy information ###### */
 +
 +    if (bEner || bPres || bConstrain)
 +    {
 +        calc_dispcorr(fplog, ir, fr, 0, top_global->natoms, box, state->lambda[efptVDW],
 +                      corr_pres, corr_vir, &prescorr, &enercorr, &dvdlcorr);
 +    }
 +
 +    if (bEner && bFirstIterate)
 +    {
 +        enerd->term[F_DISPCORR]  = enercorr;
 +        enerd->term[F_EPOT]     += enercorr;
 +        enerd->term[F_DVDL_VDW] += dvdlcorr;
 +    }
 +
 +    /* ########## Now pressure ############## */
 +    if (bPres || bConstrain)
 +    {
 +
 +        m_add(force_vir, shake_vir, total_vir);
 +
 +        /* Calculate pressure and apply LR correction if PPPM is used.
 +         * Use the box from last timestep since we already called update().
 +         */
 +
 +        enerd->term[F_PRES] = calc_pres(fr->ePBC, ir->nwall, box, ekind->ekin, total_vir, pres);
 +
 +        /* Calculate long range corrections to pressure and energy */
 +        /* this adds to enerd->term[F_PRES] and enerd->term[F_ETOT],
 +           and computes enerd->term[F_DISPCORR].  Also modifies the
 +           total_vir and pres tesors */
 +
 +        m_add(total_vir, corr_vir, total_vir);
 +        m_add(pres, corr_pres, pres);
 +        enerd->term[F_PDISPCORR] = prescorr;
 +        enerd->term[F_PRES]     += prescorr;
 +        *pcurr                   = enerd->term[F_PRES];
 +    }
 +}
 +
 +void check_nst_param(FILE *fplog, t_commrec *cr,
 +                     const char *desc_nst, int nst,
 +                     const char *desc_p, int *p)
 +{
 +    if (*p > 0 && *p % nst != 0)
 +    {
 +        /* Round up to the next multiple of nst */
 +        *p = ((*p)/nst + 1)*nst;
 +        md_print_warn(cr, fplog,
 +                      "NOTE: %s changes %s to %d\n", desc_nst, desc_p, *p);
 +    }
 +}
 +
 +void set_current_lambdas(gmx_large_int_t step, t_lambda *fepvals, gmx_bool bRerunMD,
 +                         t_trxframe *rerun_fr, t_state *state_global, t_state *state, double lam0[])
 +/* find the current lambdas.  If rerunning, we either read in a state, or a lambda value,
 +   requiring different logic. */
 +{
 +    real frac;
 +    int  i, fep_state = 0;
 +    if (bRerunMD)
 +    {
 +        if (rerun_fr->bLambda)
 +        {
 +            if (fepvals->delta_lambda != 0)
 +            {
 +                state_global->lambda[efptFEP] = rerun_fr->lambda;
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    if (i != efptFEP)
 +                    {
 +                        state->lambda[i] = state_global->lambda[i];
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                /* find out between which two value of lambda we should be */
 +                frac      = (step*fepvals->delta_lambda);
 +                fep_state = floor(frac*fepvals->n_lambda);
 +                /* interpolate between this state and the next */
 +                /* this assumes that the initial lambda corresponds to lambda==0, which is verified in grompp */
 +                frac = (frac*fepvals->n_lambda)-fep_state;
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = lam0[i] + (fepvals->all_lambda[i][fep_state]) +
 +                        frac*(fepvals->all_lambda[i][fep_state+1]-fepvals->all_lambda[i][fep_state]);
 +                }
 +            }
 +        }
 +        else if (rerun_fr->bFepState)
 +        {
 +            state_global->fep_state = rerun_fr->fep_state;
 +            for (i = 0; i < efptNR; i++)
 +            {
 +                state_global->lambda[i] = fepvals->all_lambda[i][fep_state];
 +            }
 +        }
 +    }
 +    else
 +    {
 +        if (fepvals->delta_lambda != 0)
 +        {
 +            /* find out between which two value of lambda we should be */
 +            frac = (step*fepvals->delta_lambda);
 +            if (fepvals->n_lambda > 0)
 +            {
 +                fep_state = floor(frac*fepvals->n_lambda);
 +                /* interpolate between this state and the next */
 +                /* this assumes that the initial lambda corresponds to lambda==0, which is verified in grompp */
 +                frac = (frac*fepvals->n_lambda)-fep_state;
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = lam0[i] + (fepvals->all_lambda[i][fep_state]) +
 +                        frac*(fepvals->all_lambda[i][fep_state+1]-fepvals->all_lambda[i][fep_state]);
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    state_global->lambda[i] = lam0[i] + frac;
 +                }
 +            }
 +        }
 +    }
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        state->lambda[i] = state_global->lambda[i];
 +    }
 +}
 +
 +static void min_zero(int *n, int i)
 +{
 +    if (i > 0 && (*n == 0 || i < *n))
 +    {
 +        *n = i;
 +    }
 +}
 +
 +static int lcd4(int i1, int i2, int i3, int i4)
 +{
 +    int nst;
 +
 +    nst = 0;
 +    min_zero(&nst, i1);
 +    min_zero(&nst, i2);
 +    min_zero(&nst, i3);
 +    min_zero(&nst, i4);
 +    if (nst == 0)
 +    {
 +        gmx_incons("All 4 inputs for determininig nstglobalcomm are <= 0");
 +    }
 +
 +    while (nst > 1 && ((i1 > 0 && i1 % nst != 0)  ||
 +                       (i2 > 0 && i2 % nst != 0)  ||
 +                       (i3 > 0 && i3 % nst != 0)  ||
 +                       (i4 > 0 && i4 % nst != 0)))
 +    {
 +        nst--;
 +    }
 +
 +    return nst;
 +}
 +
 +int check_nstglobalcomm(FILE *fplog, t_commrec *cr,
 +                        int nstglobalcomm, t_inputrec *ir)
 +{
 +    if (!EI_DYNAMICS(ir->eI))
 +    {
 +        nstglobalcomm = 1;
 +    }
 +
 +    if (nstglobalcomm == -1)
 +    {
 +        if (!(ir->nstcalcenergy > 0 ||
 +              ir->nstlist > 0 ||
 +              ir->etc != etcNO ||
 +              ir->epc != epcNO))
 +        {
 +            nstglobalcomm = 10;
 +            if (ir->nstenergy > 0 && ir->nstenergy < nstglobalcomm)
 +            {
 +                nstglobalcomm = ir->nstenergy;
 +            }
 +        }
 +        else
 +        {
 +            /* Ensure that we do timely global communication for
 +             * (possibly) each of the four following options.
 +             */
 +            nstglobalcomm = lcd4(ir->nstcalcenergy,
 +                                 ir->nstlist,
 +                                 ir->etc != etcNO ? ir->nsttcouple : 0,
 +                                 ir->epc != epcNO ? ir->nstpcouple : 0);
 +        }
 +    }
 +    else
 +    {
 +        if (ir->nstlist > 0 &&
 +            nstglobalcomm > ir->nstlist && nstglobalcomm % ir->nstlist != 0)
 +        {
 +            nstglobalcomm = (nstglobalcomm / ir->nstlist)*ir->nstlist;
 +            md_print_warn(cr, fplog, "WARNING: nstglobalcomm is larger than nstlist, but not a multiple, setting it to %d\n", nstglobalcomm);
 +        }
 +        if (ir->nstcalcenergy > 0)
 +        {
 +            check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                            "nstcalcenergy", &ir->nstcalcenergy);
 +        }
 +        if (ir->etc != etcNO && ir->nsttcouple > 0)
 +        {
 +            check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                            "nsttcouple", &ir->nsttcouple);
 +        }
 +        if (ir->epc != epcNO && ir->nstpcouple > 0)
 +        {
 +            check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                            "nstpcouple", &ir->nstpcouple);
 +        }
 +
 +        check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                        "nstenergy", &ir->nstenergy);
 +
 +        check_nst_param(fplog, cr, "-gcom", nstglobalcomm,
 +                        "nstlog", &ir->nstlog);
 +    }
 +
 +    if (ir->comm_mode != ecmNO && ir->nstcomm < nstglobalcomm)
 +    {
 +        md_print_warn(cr, fplog, "WARNING: Changing nstcomm from %d to %d\n",
 +                      ir->nstcomm, nstglobalcomm);
 +        ir->nstcomm = nstglobalcomm;
 +    }
 +
 +    return nstglobalcomm;
 +}
 +
 +void check_ir_old_tpx_versions(t_commrec *cr, FILE *fplog,
 +                               t_inputrec *ir, gmx_mtop_t *mtop)
 +{
 +    /* Check required for old tpx files */
 +    if (IR_TWINRANGE(*ir) && ir->nstlist > 1 &&
 +        ir->nstcalcenergy % ir->nstlist != 0)
 +    {
 +        md_print_warn(cr, fplog, "Old tpr file with twin-range settings: modifying energy calculation and/or T/P-coupling frequencies\n");
 +
 +        if (gmx_mtop_ftype_count(mtop, F_CONSTR) +
 +            gmx_mtop_ftype_count(mtop, F_CONSTRNC) > 0 &&
 +            ir->eConstrAlg == econtSHAKE)
 +        {
 +            md_print_warn(cr, fplog, "With twin-range cut-off's and SHAKE the virial and pressure are incorrect\n");
 +            if (ir->epc != epcNO)
 +            {
 +                gmx_fatal(FARGS, "Can not do pressure coupling with twin-range cut-off's and SHAKE");
 +            }
 +        }
 +        check_nst_param(fplog, cr, "nstlist", ir->nstlist,
 +                        "nstcalcenergy", &ir->nstcalcenergy);
 +        if (ir->epc != epcNO)
 +        {
 +            check_nst_param(fplog, cr, "nstlist", ir->nstlist,
 +                            "nstpcouple", &ir->nstpcouple);
 +        }
 +        check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                        "nstenergy", &ir->nstenergy);
 +        check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                        "nstlog", &ir->nstlog);
 +        if (ir->efep != efepNO)
 +        {
 +            check_nst_param(fplog, cr, "nstcalcenergy", ir->nstcalcenergy,
 +                            "nstdhdl", &ir->fepvals->nstdhdl);
 +        }
 +    }
 +}
 +
 +void rerun_parallel_comm(t_commrec *cr, t_trxframe *fr,
 +                         gmx_bool *bNotLastFrame)
 +{
 +    gmx_bool bAlloc;
 +    rvec    *xp, *vp;
 +
 +    bAlloc = (fr->natoms == 0);
 +
 +    if (MASTER(cr) && !*bNotLastFrame)
 +    {
 +        fr->natoms = -1;
 +    }
 +    xp = fr->x;
 +    vp = fr->v;
 +    gmx_bcast(sizeof(*fr), fr, cr);
 +    fr->x = xp;
 +    fr->v = vp;
 +
 +    *bNotLastFrame = (fr->natoms >= 0);
 +
 +    if (*bNotLastFrame && PARTDECOMP(cr))
 +    {
 +        /* x and v are the only variable size quantities stored in trr
 +         * that are required for rerun (f is not needed).
 +         */
 +        if (bAlloc)
 +        {
 +            snew(fr->x, fr->natoms);
 +            snew(fr->v, fr->natoms);
 +        }
 +        if (fr->bX)
 +        {
 +            gmx_bcast(fr->natoms*sizeof(fr->x[0]), fr->x[0], cr);
 +        }
 +        if (fr->bV)
 +        {
 +            gmx_bcast(fr->natoms*sizeof(fr->v[0]), fr->v[0], cr);
 +        }
 +    }
 +}
index a5f580ae3c908fb98f1de603a77daf80af04730a,0000000000000000000000000000000000000000..37fa948faf5a085c357dab4b1cd482b7f6f80415
mode 100644,000000..100644
--- /dev/null
@@@ -1,1658 -1,0 +1,1658 @@@
-                  *dens*vol*sqr(box[ZZ][ZZ]*NANO/(2*M_PI)));
 +/* -*- 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 <string.h>
 +#include <float.h>
 +#include "typedefs.h"
 +#include "string2.h"
 +#include "mdebin.h"
 +#include "smalloc.h"
 +#include "physics.h"
 +#include "enxio.h"
 +#include "vec.h"
 +#include "disre.h"
 +#include "main.h"
 +#include "network.h"
 +#include "names.h"
 +#include "orires.h"
 +#include "constr.h"
 +#include "mtop_util.h"
 +#include "xvgr.h"
 +#include "gmxfio.h"
 +#include "macros.h"
 +#include "mdrun.h"
 +#include "mdebin_bar.h"
 +
 +
 +static const char *conrmsd_nm[] = { "Constr. rmsd", "Constr.2 rmsd" };
 +
 +static const char *boxs_nm[] = { "Box-X", "Box-Y", "Box-Z" };
 +
 +static const char *tricl_boxs_nm[] = {
 +    "Box-XX", "Box-YY", "Box-ZZ",
 +    "Box-YX", "Box-ZX", "Box-ZY"
 +};
 +
 +static const char *vol_nm[] = { "Volume" };
 +
 +static const char *dens_nm[] = {"Density" };
 +
 +static const char *pv_nm[] = {"pV" };
 +
 +static const char *enthalpy_nm[] = {"Enthalpy" };
 +
 +static const char *boxvel_nm[] = {
 +    "Box-Vel-XX", "Box-Vel-YY", "Box-Vel-ZZ",
 +    "Box-Vel-YX", "Box-Vel-ZX", "Box-Vel-ZY"
 +};
 +
 +#define NBOXS asize(boxs_nm)
 +#define NTRICLBOXS asize(tricl_boxs_nm)
 +
 +t_mdebin *init_mdebin(ener_file_t       fp_ene,
 +                      const gmx_mtop_t *mtop,
 +                      const t_inputrec *ir,
 +                      FILE             *fp_dhdl)
 +{
 +    const char         *ener_nm[F_NRE];
 +    static const char  *vir_nm[] = {
 +        "Vir-XX", "Vir-XY", "Vir-XZ",
 +        "Vir-YX", "Vir-YY", "Vir-YZ",
 +        "Vir-ZX", "Vir-ZY", "Vir-ZZ"
 +    };
 +    static const char  *sv_nm[] = {
 +        "ShakeVir-XX", "ShakeVir-XY", "ShakeVir-XZ",
 +        "ShakeVir-YX", "ShakeVir-YY", "ShakeVir-YZ",
 +        "ShakeVir-ZX", "ShakeVir-ZY", "ShakeVir-ZZ"
 +    };
 +    static const char  *fv_nm[] = {
 +        "ForceVir-XX", "ForceVir-XY", "ForceVir-XZ",
 +        "ForceVir-YX", "ForceVir-YY", "ForceVir-YZ",
 +        "ForceVir-ZX", "ForceVir-ZY", "ForceVir-ZZ"
 +    };
 +    static const char  *pres_nm[] = {
 +        "Pres-XX", "Pres-XY", "Pres-XZ",
 +        "Pres-YX", "Pres-YY", "Pres-YZ",
 +        "Pres-ZX", "Pres-ZY", "Pres-ZZ"
 +    };
 +    static const char  *surft_nm[] = {
 +        "#Surf*SurfTen"
 +    };
 +    static const char  *mu_nm[] = {
 +        "Mu-X", "Mu-Y", "Mu-Z"
 +    };
 +    static const char  *vcos_nm[] = {
 +        "2CosZ*Vel-X"
 +    };
 +    static const char  *visc_nm[] = {
 +        "1/Viscosity"
 +    };
 +    static const char  *baro_nm[] = {
 +        "Barostat"
 +    };
 +
 +    char              **grpnms;
 +    const gmx_groups_t *groups;
 +    char              **gnm;
 +    char                buf[256];
 +    const char         *bufi;
 +    t_mdebin           *md;
 +    int                 i, j, ni, nj, n, nh, k, kk, ncon, nset;
 +    gmx_bool            bBHAM, bNoseHoover, b14;
 +
 +    snew(md, 1);
 +
 +    md->bVir   = TRUE;
 +    md->bPress = TRUE;
 +    md->bSurft = TRUE;
 +    md->bMu    = TRUE;
 +
 +    if (EI_DYNAMICS(ir->eI))
 +    {
 +        md->delta_t = ir->delta_t;
 +    }
 +    else
 +    {
 +        md->delta_t = 0;
 +    }
 +
 +    groups = &mtop->groups;
 +
 +    bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
 +    b14   = (gmx_mtop_ftype_count(mtop, F_LJ14) > 0 ||
 +             gmx_mtop_ftype_count(mtop, F_LJC14_Q) > 0);
 +
 +    ncon           = gmx_mtop_ftype_count(mtop, F_CONSTR);
 +    nset           = gmx_mtop_ftype_count(mtop, F_SETTLE);
 +    md->bConstr    = (ncon > 0 || nset > 0);
 +    md->bConstrVir = FALSE;
 +    if (md->bConstr)
 +    {
 +        if (ncon > 0 && ir->eConstrAlg == econtLINCS)
 +        {
 +            if (ir->eI == eiSD2)
 +            {
 +                md->nCrmsd = 2;
 +            }
 +            else
 +            {
 +                md->nCrmsd = 1;
 +            }
 +        }
 +        md->bConstrVir = (getenv("GMX_CONSTRAINTVIR") != NULL);
 +    }
 +    else
 +    {
 +        md->nCrmsd = 0;
 +    }
 +
 +    /* Energy monitoring */
 +    for (i = 0; i < egNR; i++)
 +    {
 +        md->bEInd[i] = FALSE;
 +    }
 +
 +    /* Even though the OpenMM build has moved to contrib, it's not
 +     * practical to move/remove this code fragment, because of the
 +     * fundamental mess that is the GROMACS library structure. */
 +#ifndef GMX_OPENMM
 +    for (i = 0; i < F_NRE; i++)
 +    {
 +        md->bEner[i] = FALSE;
 +        if (i == F_LJ)
 +        {
 +            md->bEner[i] = !bBHAM;
 +        }
 +        else if (i == F_BHAM)
 +        {
 +            md->bEner[i] = bBHAM;
 +        }
 +        else if (i == F_EQM)
 +        {
 +            md->bEner[i] = ir->bQMMM;
 +        }
 +        else if (i == F_COUL_LR)
 +        {
 +            md->bEner[i] = (ir->rcoulomb > ir->rlist);
 +        }
 +        else if (i == F_LJ_LR)
 +        {
 +            md->bEner[i] = (!bBHAM && ir->rvdw > ir->rlist);
 +        }
 +        else if (i == F_BHAM_LR)
 +        {
 +            md->bEner[i] = (bBHAM && ir->rvdw > ir->rlist);
 +        }
 +        else if (i == F_RF_EXCL)
 +        {
 +            md->bEner[i] = (EEL_RF(ir->coulombtype) && ir->coulombtype != eelRF_NEC && ir->cutoff_scheme == ecutsGROUP);
 +        }
 +        else if (i == F_COUL_RECIP)
 +        {
 +            md->bEner[i] = EEL_FULL(ir->coulombtype);
 +        }
 +        else if (i == F_LJ14)
 +        {
 +            md->bEner[i] = b14;
 +        }
 +        else if (i == F_COUL14)
 +        {
 +            md->bEner[i] = b14;
 +        }
 +        else if (i == F_LJC14_Q || i == F_LJC_PAIRS_NB)
 +        {
 +            md->bEner[i] = FALSE;
 +        }
 +        else if ((i == F_DVDL_COUL && ir->fepvals->separate_dvdl[efptCOUL]) ||
 +                 (i == F_DVDL_VDW  && ir->fepvals->separate_dvdl[efptVDW]) ||
 +                 (i == F_DVDL_BONDED && ir->fepvals->separate_dvdl[efptBONDED]) ||
 +                 (i == F_DVDL_RESTRAINT && ir->fepvals->separate_dvdl[efptRESTRAINT]) ||
 +                 (i == F_DKDL && ir->fepvals->separate_dvdl[efptMASS]) ||
 +                 (i == F_DVDL && ir->fepvals->separate_dvdl[efptFEP]))
 +        {
 +            md->bEner[i] = (ir->efep != efepNO);
 +        }
 +        else if ((interaction_function[i].flags & IF_VSITE) ||
 +                 (i == F_CONSTR) || (i == F_CONSTRNC) || (i == F_SETTLE))
 +        {
 +            md->bEner[i] = FALSE;
 +        }
 +        else if ((i == F_COUL_SR) || (i == F_EPOT) || (i == F_PRES)  || (i == F_EQM))
 +        {
 +            md->bEner[i] = TRUE;
 +        }
 +        else if ((i == F_GBPOL) && ir->implicit_solvent == eisGBSA)
 +        {
 +            md->bEner[i] = TRUE;
 +        }
 +        else if ((i == F_NPSOLVATION) && ir->implicit_solvent == eisGBSA && (ir->sa_algorithm != esaNO))
 +        {
 +            md->bEner[i] = TRUE;
 +        }
 +        else if ((i == F_GB12) || (i == F_GB13) || (i == F_GB14))
 +        {
 +            md->bEner[i] = FALSE;
 +        }
 +        else if ((i == F_ETOT) || (i == F_EKIN) || (i == F_TEMP))
 +        {
 +            md->bEner[i] = EI_DYNAMICS(ir->eI);
 +        }
 +        else if (i == F_DISPCORR || i == F_PDISPCORR)
 +        {
 +            md->bEner[i] = (ir->eDispCorr != edispcNO);
 +        }
 +        else if (i == F_DISRESVIOL)
 +        {
 +            md->bEner[i] = (gmx_mtop_ftype_count(mtop, F_DISRES) > 0);
 +        }
 +        else if (i == F_ORIRESDEV)
 +        {
 +            md->bEner[i] = (gmx_mtop_ftype_count(mtop, F_ORIRES) > 0);
 +        }
 +        else if (i == F_CONNBONDS)
 +        {
 +            md->bEner[i] = FALSE;
 +        }
 +        else if (i == F_COM_PULL)
 +        {
 +            md->bEner[i] = (ir->ePull == epullUMBRELLA || ir->ePull == epullCONST_F || ir->bRot);
 +        }
 +        else if (i == F_ECONSERVED)
 +        {
 +            md->bEner[i] = ((ir->etc == etcNOSEHOOVER || ir->etc == etcVRESCALE) &&
 +                            (ir->epc == epcNO || ir->epc == epcMTTK));
 +        }
 +        else
 +        {
 +            md->bEner[i] = (gmx_mtop_ftype_count(mtop, i) > 0);
 +        }
 +    }
 +#else
 +    /* OpenMM always produces only the following 4 energy terms */
 +    md->bEner[F_EPOT] = TRUE;
 +    md->bEner[F_EKIN] = TRUE;
 +    md->bEner[F_ETOT] = TRUE;
 +    md->bEner[F_TEMP] = TRUE;
 +#endif
 +
 +    /* for adress simulations, most energy terms are not meaningfull, and thus disabled*/
 +    if (ir->bAdress && !debug)
 +    {
 +        for (i = 0; i < F_NRE; i++)
 +        {
 +            md->bEner[i] = FALSE;
 +            if (i == F_EKIN)
 +            {
 +                md->bEner[i] = TRUE;
 +            }
 +            if (i == F_TEMP)
 +            {
 +                md->bEner[i] = TRUE;
 +            }
 +        }
 +        md->bVir   = FALSE;
 +        md->bPress = FALSE;
 +        md->bSurft = FALSE;
 +        md->bMu    = FALSE;
 +    }
 +
 +    md->f_nre = 0;
 +    for (i = 0; i < F_NRE; i++)
 +    {
 +        if (md->bEner[i])
 +        {
 +            ener_nm[md->f_nre] = interaction_function[i].longname;
 +            md->f_nre++;
 +        }
 +    }
 +
 +    md->epc            = ir->epc;
 +    md->bDiagPres      = !TRICLINIC(ir->ref_p);
 +    md->ref_p          = (ir->ref_p[XX][XX]+ir->ref_p[YY][YY]+ir->ref_p[ZZ][ZZ])/DIM;
 +    md->bTricl         = TRICLINIC(ir->compress) || TRICLINIC(ir->deform);
 +    md->bDynBox        = DYNAMIC_BOX(*ir);
 +    md->etc            = ir->etc;
 +    md->bNHC_trotter   = IR_NVT_TROTTER(ir);
 +    md->bPrintNHChains = ir->bPrintNHChains;
 +    md->bMTTK          = (IR_NPT_TROTTER(ir) || IR_NPH_TROTTER(ir));
 +    md->bMu            = NEED_MUTOT(*ir);
 +
 +    md->ebin  = mk_ebin();
 +    /* Pass NULL for unit to let get_ebin_space determine the units
 +     * for interaction_function[i].longname
 +     */
 +    md->ie    = get_ebin_space(md->ebin, md->f_nre, ener_nm, NULL);
 +    if (md->nCrmsd)
 +    {
 +        /* This should be called directly after the call for md->ie,
 +         * such that md->iconrmsd follows directly in the list.
 +         */
 +        md->iconrmsd = get_ebin_space(md->ebin, md->nCrmsd, conrmsd_nm, "");
 +    }
 +    if (md->bDynBox)
 +    {
 +        md->ib    = get_ebin_space(md->ebin,
 +                                   md->bTricl ? NTRICLBOXS : NBOXS,
 +                                   md->bTricl ? tricl_boxs_nm : boxs_nm,
 +                                   unit_length);
 +        md->ivol  = get_ebin_space(md->ebin, 1, vol_nm,  unit_volume);
 +        md->idens = get_ebin_space(md->ebin, 1, dens_nm, unit_density_SI);
 +        if (md->bDiagPres)
 +        {
 +            md->ipv       = get_ebin_space(md->ebin, 1, pv_nm,   unit_energy);
 +            md->ienthalpy = get_ebin_space(md->ebin, 1, enthalpy_nm,   unit_energy);
 +        }
 +    }
 +    if (md->bConstrVir)
 +    {
 +        md->isvir = get_ebin_space(md->ebin, asize(sv_nm), sv_nm, unit_energy);
 +        md->ifvir = get_ebin_space(md->ebin, asize(fv_nm), fv_nm, unit_energy);
 +    }
 +    if (md->bVir)
 +    {
 +        md->ivir   = get_ebin_space(md->ebin, asize(vir_nm), vir_nm, unit_energy);
 +    }
 +    if (md->bPress)
 +    {
 +        md->ipres  = get_ebin_space(md->ebin, asize(pres_nm), pres_nm, unit_pres_bar);
 +    }
 +    if (md->bSurft)
 +    {
 +        md->isurft = get_ebin_space(md->ebin, asize(surft_nm), surft_nm,
 +                                    unit_surft_bar);
 +    }
 +    if (md->epc == epcPARRINELLORAHMAN || md->epc == epcMTTK)
 +    {
 +        md->ipc = get_ebin_space(md->ebin, md->bTricl ? 6 : 3,
 +                                 boxvel_nm, unit_vel);
 +    }
 +    if (md->bMu)
 +    {
 +        md->imu    = get_ebin_space(md->ebin, asize(mu_nm), mu_nm, unit_dipole_D);
 +    }
 +    if (ir->cos_accel != 0)
 +    {
 +        md->ivcos = get_ebin_space(md->ebin, asize(vcos_nm), vcos_nm, unit_vel);
 +        md->ivisc = get_ebin_space(md->ebin, asize(visc_nm), visc_nm,
 +                                   unit_invvisc_SI);
 +    }
 +
 +    /* Energy monitoring */
 +    for (i = 0; i < egNR; i++)
 +    {
 +        md->bEInd[i] = FALSE;
 +    }
 +    md->bEInd[egCOULSR] = TRUE;
 +    md->bEInd[egLJSR  ] = TRUE;
 +
 +    if (ir->rcoulomb > ir->rlist)
 +    {
 +        md->bEInd[egCOULLR] = TRUE;
 +    }
 +    if (!bBHAM)
 +    {
 +        if (ir->rvdw > ir->rlist)
 +        {
 +            md->bEInd[egLJLR]   = TRUE;
 +        }
 +    }
 +    else
 +    {
 +        md->bEInd[egLJSR]   = FALSE;
 +        md->bEInd[egBHAMSR] = TRUE;
 +        if (ir->rvdw > ir->rlist)
 +        {
 +            md->bEInd[egBHAMLR]   = TRUE;
 +        }
 +    }
 +    if (b14)
 +    {
 +        md->bEInd[egLJ14]   = TRUE;
 +        md->bEInd[egCOUL14] = TRUE;
 +    }
 +    md->nEc = 0;
 +    for (i = 0; (i < egNR); i++)
 +    {
 +        if (md->bEInd[i])
 +        {
 +            md->nEc++;
 +        }
 +    }
 +
 +    n = groups->grps[egcENER].nr;
 +    /* for adress simulations, most energy terms are not meaningfull, and thus disabled*/
 +    if (!ir->bAdress)
 +    {
 +        /*standard simulation*/
 +        md->nEg = n;
 +        md->nE  = (n*(n+1))/2;
 +    }
 +    else if (!debug)
 +    {
 +        /*AdResS simulation*/
 +        md->nU    = 0;
 +        md->nEg   = 0;
 +        md->nE    = 0;
 +        md->nEc   = 0;
 +        md->isvir = FALSE;
 +    }
 +    snew(md->igrp, md->nE);
 +    if (md->nE > 1)
 +    {
 +        n = 0;
 +        snew(gnm, md->nEc);
 +        for (k = 0; (k < md->nEc); k++)
 +        {
 +            snew(gnm[k], STRLEN);
 +        }
 +        for (i = 0; (i < groups->grps[egcENER].nr); i++)
 +        {
 +            ni = groups->grps[egcENER].nm_ind[i];
 +            for (j = i; (j < groups->grps[egcENER].nr); j++)
 +            {
 +                nj = groups->grps[egcENER].nm_ind[j];
 +                for (k = kk = 0; (k < egNR); k++)
 +                {
 +                    if (md->bEInd[k])
 +                    {
 +                        sprintf(gnm[kk], "%s:%s-%s", egrp_nm[k],
 +                                *(groups->grpname[ni]), *(groups->grpname[nj]));
 +                        kk++;
 +                    }
 +                }
 +                md->igrp[n] = get_ebin_space(md->ebin, md->nEc,
 +                                             (const char **)gnm, unit_energy);
 +                n++;
 +            }
 +        }
 +        for (k = 0; (k < md->nEc); k++)
 +        {
 +            sfree(gnm[k]);
 +        }
 +        sfree(gnm);
 +
 +        if (n != md->nE)
 +        {
 +            gmx_incons("Number of energy terms wrong");
 +        }
 +    }
 +
 +    md->nTC  = groups->grps[egcTC].nr;
 +    md->nNHC = ir->opts.nhchainlength; /* shorthand for number of NH chains */
 +    if (md->bMTTK)
 +    {
 +        md->nTCP = 1;  /* assume only one possible coupling system for barostat
 +                          for now */
 +    }
 +    else
 +    {
 +        md->nTCP = 0;
 +    }
 +    if (md->etc == etcNOSEHOOVER)
 +    {
 +        if (md->bNHC_trotter)
 +        {
 +            md->mde_n = 2*md->nNHC*md->nTC;
 +        }
 +        else
 +        {
 +            md->mde_n = 2*md->nTC;
 +        }
 +        if (md->epc == epcMTTK)
 +        {
 +            md->mdeb_n = 2*md->nNHC*md->nTCP;
 +        }
 +    }
 +    else
 +    {
 +        md->mde_n  = md->nTC;
 +        md->mdeb_n = 0;
 +    }
 +
 +    snew(md->tmp_r, md->mde_n);
 +    snew(md->tmp_v, md->mde_n);
 +    snew(md->grpnms, md->mde_n);
 +    grpnms = md->grpnms;
 +
 +    for (i = 0; (i < md->nTC); i++)
 +    {
 +        ni = groups->grps[egcTC].nm_ind[i];
 +        sprintf(buf, "T-%s", *(groups->grpname[ni]));
 +        grpnms[i] = strdup(buf);
 +    }
 +    md->itemp = get_ebin_space(md->ebin, md->nTC, (const char **)grpnms,
 +                               unit_temp_K);
 +
 +    if (md->etc == etcNOSEHOOVER)
 +    {
 +        if (md->bPrintNHChains)
 +        {
 +            if (md->bNHC_trotter)
 +            {
 +                for (i = 0; (i < md->nTC); i++)
 +                {
 +                    ni   = groups->grps[egcTC].nm_ind[i];
 +                    bufi = *(groups->grpname[ni]);
 +                    for (j = 0; (j < md->nNHC); j++)
 +                    {
 +                        sprintf(buf, "Xi-%d-%s", j, bufi);
 +                        grpnms[2*(i*md->nNHC+j)] = strdup(buf);
 +                        sprintf(buf, "vXi-%d-%s", j, bufi);
 +                        grpnms[2*(i*md->nNHC+j)+1] = strdup(buf);
 +                    }
 +                }
 +                md->itc = get_ebin_space(md->ebin, md->mde_n,
 +                                         (const char **)grpnms, unit_invtime);
 +                if (md->bMTTK)
 +                {
 +                    for (i = 0; (i < md->nTCP); i++)
 +                    {
 +                        bufi = baro_nm[0];  /* All barostat DOF's together for now. */
 +                        for (j = 0; (j < md->nNHC); j++)
 +                        {
 +                            sprintf(buf, "Xi-%d-%s", j, bufi);
 +                            grpnms[2*(i*md->nNHC+j)] = strdup(buf);
 +                            sprintf(buf, "vXi-%d-%s", j, bufi);
 +                            grpnms[2*(i*md->nNHC+j)+1] = strdup(buf);
 +                        }
 +                    }
 +                    md->itcb = get_ebin_space(md->ebin, md->mdeb_n,
 +                                              (const char **)grpnms, unit_invtime);
 +                }
 +            }
 +            else
 +            {
 +                for (i = 0; (i < md->nTC); i++)
 +                {
 +                    ni   = groups->grps[egcTC].nm_ind[i];
 +                    bufi = *(groups->grpname[ni]);
 +                    sprintf(buf, "Xi-%s", bufi);
 +                    grpnms[2*i] = strdup(buf);
 +                    sprintf(buf, "vXi-%s", bufi);
 +                    grpnms[2*i+1] = strdup(buf);
 +                }
 +                md->itc = get_ebin_space(md->ebin, md->mde_n,
 +                                         (const char **)grpnms, unit_invtime);
 +            }
 +        }
 +    }
 +    else if (md->etc == etcBERENDSEN || md->etc == etcYES ||
 +             md->etc == etcVRESCALE)
 +    {
 +        for (i = 0; (i < md->nTC); i++)
 +        {
 +            ni = groups->grps[egcTC].nm_ind[i];
 +            sprintf(buf, "Lamb-%s", *(groups->grpname[ni]));
 +            grpnms[i] = strdup(buf);
 +        }
 +        md->itc = get_ebin_space(md->ebin, md->mde_n, (const char **)grpnms, "");
 +    }
 +
 +    sfree(grpnms);
 +
 +
 +    md->nU = groups->grps[egcACC].nr;
 +    if (md->nU > 1)
 +    {
 +        snew(grpnms, 3*md->nU);
 +        for (i = 0; (i < md->nU); i++)
 +        {
 +            ni = groups->grps[egcACC].nm_ind[i];
 +            sprintf(buf, "Ux-%s", *(groups->grpname[ni]));
 +            grpnms[3*i+XX] = strdup(buf);
 +            sprintf(buf, "Uy-%s", *(groups->grpname[ni]));
 +            grpnms[3*i+YY] = strdup(buf);
 +            sprintf(buf, "Uz-%s", *(groups->grpname[ni]));
 +            grpnms[3*i+ZZ] = strdup(buf);
 +        }
 +        md->iu = get_ebin_space(md->ebin, 3*md->nU, (const char **)grpnms, unit_vel);
 +        sfree(grpnms);
 +    }
 +
 +    if (fp_ene)
 +    {
 +        do_enxnms(fp_ene, &md->ebin->nener, &md->ebin->enm);
 +    }
 +
 +    md->print_grpnms = NULL;
 +
 +    /* check whether we're going to write dh histograms */
 +    md->dhc = NULL;
 +    if (ir->fepvals->separate_dhdl_file == esepdhdlfileNO)
 +    {
 +        /* Currently dh histograms are only written with dynamics */
 +        if (EI_DYNAMICS(ir->eI))
 +        {
 +            snew(md->dhc, 1);
 +
 +            mde_delta_h_coll_init(md->dhc, ir);
 +        }
 +        md->fp_dhdl = NULL;
 +        snew(md->dE, ir->fepvals->n_lambda);
 +    }
 +    else
 +    {
 +        md->fp_dhdl = fp_dhdl;
 +        snew(md->dE, ir->fepvals->n_lambda);
 +    }
 +    if (ir->bSimTemp)
 +    {
 +        int i;
 +        snew(md->temperatures, ir->fepvals->n_lambda);
 +        for (i = 0; i < ir->fepvals->n_lambda; i++)
 +        {
 +            md->temperatures[i] = ir->simtempvals->temperatures[i];
 +        }
 +    }
 +    return md;
 +}
 +
 +/* print a lambda vector to a string
 +   fep = the inputrec's FEP input data
 +   i = the index of the lambda vector
 +   get_native_lambda = whether to print the native lambda
 +   get_names = whether to print the names rather than the values
 +   str = the pre-allocated string buffer to print to. */
 +static void print_lambda_vector(t_lambda *fep, int i,
 +                                gmx_bool get_native_lambda, gmx_bool get_names,
 +                                char *str)
 +{
 +    size_t nps = 0, np;
 +    int    j, k = 0;
 +    int    Nsep = 0;
 +
 +    for (j = 0; j < efptNR; j++)
 +    {
 +        if (fep->separate_dvdl[j])
 +        {
 +            Nsep++;
 +        }
 +    }
 +    str[0] = 0; /* reset the string */
 +    if (Nsep > 1)
 +    {
 +        str += sprintf(str, "("); /* set the opening parenthesis*/
 +    }
 +    for (j = 0; j < efptNR; j++)
 +    {
 +        if (fep->separate_dvdl[j])
 +        {
 +            double lam;
 +            if (!get_names)
 +            {
 +                if (get_native_lambda && fep->init_lambda >= 0)
 +                {
 +                    str += sprintf(str, "%.4f", fep->init_lambda);
 +                }
 +                else
 +                {
 +                    str += sprintf(str, "%.4f", fep->all_lambda[j][i]);
 +                }
 +            }
 +            else
 +            {
 +                str += sprintf(str, "%s", efpt_singular_names[j]);
 +            }
 +            /* print comma for the next item */
 +            if (k < Nsep-1)
 +            {
 +                str += sprintf(str, ", ");
 +            }
 +            k++;
 +        }
 +    }
 +    if (Nsep > 1)
 +    {
 +        /* and add the closing parenthesis */
 +        str += sprintf(str, ")");
 +    }
 +}
 +
 +
 +extern FILE *open_dhdl(const char *filename, const t_inputrec *ir,
 +                       const output_env_t oenv)
 +{
 +    FILE       *fp;
 +    const char *dhdl = "dH/d\\lambda", *deltag = "\\DeltaH", *lambda = "\\lambda",
 +    *lambdastate     = "\\lambda state", *remain = "remaining";
 +    char        title[STRLEN], label_x[STRLEN], label_y[STRLEN];
 +    int         i, np, nps, nsets, nsets_de, nsetsbegin;
 +    int         n_lambda_terms = 0;
 +    t_lambda   *fep            = ir->fepvals; /* for simplicity */
 +    t_expanded *expand         = ir->expandedvals;
 +    char      **setname;
 +    char        buf[STRLEN], lambda_vec_str[STRLEN], lambda_name_str[STRLEN];
 +    int         bufplace = 0;
 +
 +    int         nsets_dhdl = 0;
 +    int         s          = 0;
 +    int         nsetsextend;
 +    gmx_bool    write_pV = FALSE;
 +
 +    /* count the number of different lambda terms */
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        if (fep->separate_dvdl[i])
 +        {
 +            n_lambda_terms++;
 +        }
 +    }
 +
 +    if (fep->n_lambda == 0)
 +    {
 +        sprintf(title, "%s", dhdl);
 +        sprintf(label_x, "Time (ps)");
 +        sprintf(label_y, "%s (%s %s)",
 +                dhdl, unit_energy, "[\\lambda]\\S-1\\N");
 +    }
 +    else
 +    {
 +        sprintf(title, "%s and %s", dhdl, deltag);
 +        sprintf(label_x, "Time (ps)");
 +        sprintf(label_y, "%s and %s (%s %s)",
 +                dhdl, deltag, unit_energy, "[\\8l\\4]\\S-1\\N");
 +    }
 +    fp = gmx_fio_fopen(filename, "w+");
 +    xvgr_header(fp, title, label_x, label_y, exvggtXNY, oenv);
 +
 +    if (!(ir->bSimTemp))
 +    {
 +        bufplace = sprintf(buf, "T = %g (K) ",
 +                           ir->opts.ref_t[0]);
 +    }
 +    if (ir->efep != efepSLOWGROWTH)
 +    {
 +        if ( (fep->init_lambda >= 0)  && (n_lambda_terms == 1 ))
 +        {
 +            /* compatibility output */
 +            sprintf(&(buf[bufplace]), "%s = %.4f", lambda, fep->init_lambda);
 +        }
 +        else
 +        {
 +            print_lambda_vector(fep, fep->init_fep_state, TRUE, FALSE,
 +                                lambda_vec_str);
 +            print_lambda_vector(fep, fep->init_fep_state, TRUE, TRUE,
 +                                lambda_name_str);
 +            sprintf(&(buf[bufplace]), "%s %d: %s = %s",
 +                    lambdastate, fep->init_fep_state,
 +                    lambda_name_str, lambda_vec_str);
 +        }
 +    }
 +    xvgr_subtitle(fp, buf, oenv);
 +
 +
 +    nsets_dhdl = 0;
 +    if (fep->dhdl_derivatives == edhdlderivativesYES)
 +    {
 +        nsets_dhdl = n_lambda_terms;
 +    }
 +    /* count the number of delta_g states */
 +    nsets_de = fep->lambda_stop_n - fep->lambda_start_n;
 +
 +    nsets = nsets_dhdl + nsets_de; /* dhdl + fep differences */
 +
 +    if (fep->n_lambda > 0 && (expand->elmcmove > elmcmoveNO))
 +    {
 +        nsets += 1;   /*add fep state for expanded ensemble */
 +    }
 +
 +    if (fep->bPrintEnergy)
 +    {
 +        nsets += 1;  /* add energy to the dhdl as well */
 +    }
 +
 +    nsetsextend = nsets;
 +    if ((ir->epc != epcNO) && (fep->n_lambda > 0) && (fep->init_lambda < 0))
 +    {
 +        nsetsextend += 1; /* for PV term, other terms possible if required for
 +                             the reduced potential (only needed with foreign
 +                             lambda, and only output when init_lambda is not
 +                             set in order to maintain compatibility of the
 +                             dhdl.xvg file) */
 +        write_pV     = TRUE;
 +    }
 +    snew(setname, nsetsextend);
 +
 +    if (expand->elmcmove > elmcmoveNO)
 +    {
 +        /* state for the fep_vals, if we have alchemical sampling */
 +        sprintf(buf, "%s", "Thermodynamic state");
 +        setname[s] = strdup(buf);
 +        s         += 1;
 +    }
 +
 +    if (fep->bPrintEnergy)
 +    {
 +        sprintf(buf, "%s (%s)", "Energy", unit_energy);
 +        setname[s] = strdup(buf);
 +        s         += 1;
 +    }
 +
 +    if (fep->dhdl_derivatives == edhdlderivativesYES)
 +    {
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            if (fep->separate_dvdl[i])
 +            {
 +
 +                if ( (fep->init_lambda >= 0)  && (n_lambda_terms == 1 ))
 +                {
 +                    /* compatibility output */
 +                    sprintf(buf, "%s %s %.4f", dhdl, lambda, fep->init_lambda);
 +                }
 +                else
 +                {
 +                    double lam = fep->init_lambda;
 +                    if (fep->init_lambda < 0)
 +                    {
 +                        lam = fep->all_lambda[i][fep->init_fep_state];
 +                    }
 +                    sprintf(buf, "%s %s = %.4f", dhdl, efpt_singular_names[i],
 +                            lam);
 +                }
 +                setname[s] = strdup(buf);
 +                s         += 1;
 +            }
 +        }
 +    }
 +
 +    if (fep->n_lambda > 0)
 +    {
 +        /* g_bar has to determine the lambda values used in this simulation
 +         * from this xvg legend.
 +         */
 +
 +        if (expand->elmcmove > elmcmoveNO)
 +        {
 +            nsetsbegin = 1;  /* for including the expanded ensemble */
 +        }
 +        else
 +        {
 +            nsetsbegin = 0;
 +        }
 +
 +        if (fep->bPrintEnergy)
 +        {
 +            nsetsbegin += 1;
 +        }
 +        nsetsbegin += nsets_dhdl;
 +
 +        for (i = fep->lambda_start_n; i < fep->lambda_stop_n; i++)
 +        {
 +            print_lambda_vector(fep, i, FALSE, FALSE, lambda_vec_str);
 +            if ( (fep->init_lambda >= 0)  && (n_lambda_terms == 1 ))
 +            {
 +                /* for compatible dhdl.xvg files */
 +                nps = sprintf(buf, "%s %s %s", deltag, lambda, lambda_vec_str);
 +            }
 +            else
 +            {
 +                nps = sprintf(buf, "%s %s to %s", deltag, lambda, lambda_vec_str);
 +            }
 +
 +            if (ir->bSimTemp)
 +            {
 +                /* print the temperature for this state if doing simulated annealing */
 +                sprintf(&buf[nps], "T = %g (%s)",
 +                        ir->simtempvals->temperatures[s-(nsetsbegin)],
 +                        unit_temp_K);
 +            }
 +            setname[s] = strdup(buf);
 +            s++;
 +        }
 +        if (write_pV)
 +        {
 +            np                     = sprintf(buf, "pV (%s)", unit_energy);
 +            setname[nsetsextend-1] = strdup(buf);  /* the first entry after
 +                                                      nsets */
 +        }
 +
 +        xvgr_legend(fp, nsetsextend, (const char **)setname, oenv);
 +
 +        for (s = 0; s < nsetsextend; s++)
 +        {
 +            sfree(setname[s]);
 +        }
 +        sfree(setname);
 +    }
 +
 +    return fp;
 +}
 +
 +static void copy_energy(t_mdebin *md, real e[], real ecpy[])
 +{
 +    int i, j;
 +
 +    for (i = j = 0; (i < F_NRE); i++)
 +    {
 +        if (md->bEner[i])
 +        {
 +            ecpy[j++] = e[i];
 +        }
 +    }
 +    if (j != md->f_nre)
 +    {
 +        gmx_incons("Number of energy terms wrong");
 +    }
 +}
 +
 +void upd_mdebin(t_mdebin       *md,
 +                gmx_bool        bDoDHDL,
 +                gmx_bool        bSum,
 +                double          time,
 +                real            tmass,
 +                gmx_enerdata_t *enerd,
 +                t_state        *state,
 +                t_lambda       *fep,
 +                t_expanded     *expand,
 +                matrix          box,
 +                tensor          svir,
 +                tensor          fvir,
 +                tensor          vir,
 +                tensor          pres,
 +                gmx_ekindata_t *ekind,
 +                rvec            mu_tot,
 +                gmx_constr_t    constr)
 +{
 +    int    i, j, k, kk, m, n, gid;
 +    real   crmsd[2], tmp6[6];
 +    real   bs[NTRICLBOXS], vol, dens, pv, enthalpy;
 +    real   eee[egNR];
 +    real   ecopy[F_NRE];
 +    double store_dhdl[efptNR];
 +    real   store_energy = 0;
 +    real   tmp;
 +
 +    /* Do NOT use the box in the state variable, but the separate box provided
 +     * as an argument. This is because we sometimes need to write the box from
 +     * the last timestep to match the trajectory frames.
 +     */
 +    copy_energy(md, enerd->term, ecopy);
 +    add_ebin(md->ebin, md->ie, md->f_nre, ecopy, bSum);
 +    if (md->nCrmsd)
 +    {
 +        crmsd[0] = constr_rmsd(constr, FALSE);
 +        if (md->nCrmsd > 1)
 +        {
 +            crmsd[1] = constr_rmsd(constr, TRUE);
 +        }
 +        add_ebin(md->ebin, md->iconrmsd, md->nCrmsd, crmsd, FALSE);
 +    }
 +    if (md->bDynBox)
 +    {
 +        int nboxs;
 +        if (md->bTricl)
 +        {
 +            bs[0] = box[XX][XX];
 +            bs[1] = box[YY][YY];
 +            bs[2] = box[ZZ][ZZ];
 +            bs[3] = box[YY][XX];
 +            bs[4] = box[ZZ][XX];
 +            bs[5] = box[ZZ][YY];
 +            nboxs = NTRICLBOXS;
 +        }
 +        else
 +        {
 +            bs[0] = box[XX][XX];
 +            bs[1] = box[YY][YY];
 +            bs[2] = box[ZZ][ZZ];
 +            nboxs = NBOXS;
 +        }
 +        vol  = box[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
 +        dens = (tmass*AMU)/(vol*NANO*NANO*NANO);
 +        add_ebin(md->ebin, md->ib, nboxs, bs, bSum);
 +        add_ebin(md->ebin, md->ivol, 1, &vol, bSum);
 +        add_ebin(md->ebin, md->idens, 1, &dens, bSum);
 +
 +        if (md->bDiagPres)
 +        {
 +            /* This is pV (in kJ/mol).  The pressure is the reference pressure,
 +               not the instantaneous pressure */
 +            pv = vol*md->ref_p/PRESFAC;
 +
 +            add_ebin(md->ebin, md->ipv, 1, &pv, bSum);
 +            enthalpy = pv + enerd->term[F_ETOT];
 +            add_ebin(md->ebin, md->ienthalpy, 1, &enthalpy, bSum);
 +        }
 +    }
 +    if (md->bConstrVir)
 +    {
 +        add_ebin(md->ebin, md->isvir, 9, svir[0], bSum);
 +        add_ebin(md->ebin, md->ifvir, 9, fvir[0], bSum);
 +    }
 +    if (md->bVir)
 +    {
 +        add_ebin(md->ebin, md->ivir, 9, vir[0], bSum);
 +    }
 +    if (md->bPress)
 +    {
 +        add_ebin(md->ebin, md->ipres, 9, pres[0], bSum);
 +    }
 +    if (md->bSurft)
 +    {
 +        tmp = (pres[ZZ][ZZ]-(pres[XX][XX]+pres[YY][YY])*0.5)*box[ZZ][ZZ];
 +        add_ebin(md->ebin, md->isurft, 1, &tmp, bSum);
 +    }
 +    if (md->epc == epcPARRINELLORAHMAN || md->epc == epcMTTK)
 +    {
 +        tmp6[0] = state->boxv[XX][XX];
 +        tmp6[1] = state->boxv[YY][YY];
 +        tmp6[2] = state->boxv[ZZ][ZZ];
 +        tmp6[3] = state->boxv[YY][XX];
 +        tmp6[4] = state->boxv[ZZ][XX];
 +        tmp6[5] = state->boxv[ZZ][YY];
 +        add_ebin(md->ebin, md->ipc, md->bTricl ? 6 : 3, tmp6, bSum);
 +    }
 +    if (md->bMu)
 +    {
 +        add_ebin(md->ebin, md->imu, 3, mu_tot, bSum);
 +    }
 +    if (ekind && ekind->cosacc.cos_accel != 0)
 +    {
 +        vol  = box[XX][XX]*box[YY][YY]*box[ZZ][ZZ];
 +        dens = (tmass*AMU)/(vol*NANO*NANO*NANO);
 +        add_ebin(md->ebin, md->ivcos, 1, &(ekind->cosacc.vcos), bSum);
 +        /* 1/viscosity, unit 1/(kg m^-1 s^-1) */
 +        tmp = 1/(ekind->cosacc.cos_accel/(ekind->cosacc.vcos*PICO)
++                 *dens*sqr(box[ZZ][ZZ]*NANO/(2*M_PI)));
 +        add_ebin(md->ebin, md->ivisc, 1, &tmp, bSum);
 +    }
 +    if (md->nE > 1)
 +    {
 +        n = 0;
 +        for (i = 0; (i < md->nEg); i++)
 +        {
 +            for (j = i; (j < md->nEg); j++)
 +            {
 +                gid = GID(i, j, md->nEg);
 +                for (k = kk = 0; (k < egNR); k++)
 +                {
 +                    if (md->bEInd[k])
 +                    {
 +                        eee[kk++] = enerd->grpp.ener[k][gid];
 +                    }
 +                }
 +                add_ebin(md->ebin, md->igrp[n], md->nEc, eee, bSum);
 +                n++;
 +            }
 +        }
 +    }
 +
 +    if (ekind)
 +    {
 +        for (i = 0; (i < md->nTC); i++)
 +        {
 +            md->tmp_r[i] = ekind->tcstat[i].T;
 +        }
 +        add_ebin(md->ebin, md->itemp, md->nTC, md->tmp_r, bSum);
 +
 +        if (md->etc == etcNOSEHOOVER)
 +        {
 +            /* whether to print Nose-Hoover chains: */
 +            if (md->bPrintNHChains)
 +            {
 +                if (md->bNHC_trotter)
 +                {
 +                    for (i = 0; (i < md->nTC); i++)
 +                    {
 +                        for (j = 0; j < md->nNHC; j++)
 +                        {
 +                            k                = i*md->nNHC+j;
 +                            md->tmp_r[2*k]   = state->nosehoover_xi[k];
 +                            md->tmp_r[2*k+1] = state->nosehoover_vxi[k];
 +                        }
 +                    }
 +                    add_ebin(md->ebin, md->itc, md->mde_n, md->tmp_r, bSum);
 +
 +                    if (md->bMTTK)
 +                    {
 +                        for (i = 0; (i < md->nTCP); i++)
 +                        {
 +                            for (j = 0; j < md->nNHC; j++)
 +                            {
 +                                k                = i*md->nNHC+j;
 +                                md->tmp_r[2*k]   = state->nhpres_xi[k];
 +                                md->tmp_r[2*k+1] = state->nhpres_vxi[k];
 +                            }
 +                        }
 +                        add_ebin(md->ebin, md->itcb, md->mdeb_n, md->tmp_r, bSum);
 +                    }
 +                }
 +                else
 +                {
 +                    for (i = 0; (i < md->nTC); i++)
 +                    {
 +                        md->tmp_r[2*i]   = state->nosehoover_xi[i];
 +                        md->tmp_r[2*i+1] = state->nosehoover_vxi[i];
 +                    }
 +                    add_ebin(md->ebin, md->itc, md->mde_n, md->tmp_r, bSum);
 +                }
 +            }
 +        }
 +        else if (md->etc == etcBERENDSEN || md->etc == etcYES ||
 +                 md->etc == etcVRESCALE)
 +        {
 +            for (i = 0; (i < md->nTC); i++)
 +            {
 +                md->tmp_r[i] = ekind->tcstat[i].lambda;
 +            }
 +            add_ebin(md->ebin, md->itc, md->nTC, md->tmp_r, bSum);
 +        }
 +    }
 +
 +    if (ekind && md->nU > 1)
 +    {
 +        for (i = 0; (i < md->nU); i++)
 +        {
 +            copy_rvec(ekind->grpstat[i].u, md->tmp_v[i]);
 +        }
 +        add_ebin(md->ebin, md->iu, 3*md->nU, md->tmp_v[0], bSum);
 +    }
 +
 +    ebin_increase_count(md->ebin, bSum);
 +
 +    /* BAR + thermodynamic integration values */
 +    if ((md->fp_dhdl || md->dhc) && bDoDHDL)
 +    {
 +        for (i = 0; i < enerd->n_lambda-1; i++)
 +        {
 +            /* zero for simulated tempering */
 +            md->dE[i] = enerd->enerpart_lambda[i+1]-enerd->enerpart_lambda[0];
 +            if (md->temperatures != NULL)
 +            {
 +                /* MRS: is this right, given the way we have defined the exchange probabilities? */
 +                /* is this even useful to have at all? */
 +                md->dE[i] += (md->temperatures[i]/
 +                              md->temperatures[state->fep_state]-1.0)*
 +                    enerd->term[F_EKIN];
 +            }
 +        }
 +
 +        if (md->fp_dhdl)
 +        {
 +            fprintf(md->fp_dhdl, "%.4f", time);
 +            /* the current free energy state */
 +
 +            /* print the current state if we are doing expanded ensemble */
 +            if (expand->elmcmove > elmcmoveNO)
 +            {
 +                fprintf(md->fp_dhdl, " %4d", state->fep_state);
 +            }
 +            /* total energy (for if the temperature changes */
 +            if (fep->bPrintEnergy)
 +            {
 +                store_energy = enerd->term[F_ETOT];
 +                fprintf(md->fp_dhdl, " %#.8g", store_energy);
 +            }
 +
 +            if (fep->dhdl_derivatives == edhdlderivativesYES)
 +            {
 +                for (i = 0; i < efptNR; i++)
 +                {
 +                    if (fep->separate_dvdl[i])
 +                    {
 +                        /* assumes F_DVDL is first */
 +                        fprintf(md->fp_dhdl, " %#.8g", enerd->term[F_DVDL+i]);
 +                    }
 +                }
 +            }
 +            for (i = fep->lambda_start_n; i < fep->lambda_stop_n; i++)
 +            {
 +                fprintf(md->fp_dhdl, " %#.8g", md->dE[i]);
 +            }
 +            if ((md->epc != epcNO)  &&
 +                (enerd->n_lambda > 0) &&
 +                (fep->init_lambda < 0))
 +            {
 +                fprintf(md->fp_dhdl, " %#.8g", pv);  /* PV term only needed when
 +                                                        there are alternate state
 +                                                        lambda and we're not in
 +                                                        compatibility mode */
 +            }
 +            fprintf(md->fp_dhdl, "\n");
 +            /* and the binary free energy output */
 +        }
 +        if (md->dhc && bDoDHDL)
 +        {
 +            int idhdl = 0;
 +            for (i = 0; i < efptNR; i++)
 +            {
 +                if (fep->separate_dvdl[i])
 +                {
 +                    /* assumes F_DVDL is first */
 +                    store_dhdl[idhdl] = enerd->term[F_DVDL+i];
 +                    idhdl            += 1;
 +                }
 +            }
 +            store_energy = enerd->term[F_ETOT];
 +            /* store_dh is dE */
 +            mde_delta_h_coll_add_dh(md->dhc,
 +                                    (double)state->fep_state,
 +                                    store_energy,
 +                                    pv,
 +                                    store_dhdl,
 +                                    md->dE + fep->lambda_start_n,
 +                                    time);
 +        }
 +    }
 +}
 +
 +
 +void upd_mdebin_step(t_mdebin *md)
 +{
 +    ebin_increase_count(md->ebin, FALSE);
 +}
 +
 +static void npr(FILE *log, int n, char c)
 +{
 +    for (; (n > 0); n--)
 +    {
 +        fprintf(log, "%c", c);
 +    }
 +}
 +
 +static void pprint(FILE *log, const char *s, t_mdebin *md)
 +{
 +    char CHAR = '#';
 +    int  slen;
 +    char buf1[22], buf2[22];
 +
 +    slen = strlen(s);
 +    fprintf(log, "\t<======  ");
 +    npr(log, slen, CHAR);
 +    fprintf(log, "  ==>\n");
 +    fprintf(log, "\t<====  %s  ====>\n", s);
 +    fprintf(log, "\t<==  ");
 +    npr(log, slen, CHAR);
 +    fprintf(log, "  ======>\n\n");
 +
 +    fprintf(log, "\tStatistics over %s steps using %s frames\n",
 +            gmx_step_str(md->ebin->nsteps_sim, buf1),
 +            gmx_step_str(md->ebin->nsum_sim, buf2));
 +    fprintf(log, "\n");
 +}
 +
 +void print_ebin_header(FILE *log, gmx_large_int_t steps, double time, real lambda)
 +{
 +    char buf[22];
 +
 +    fprintf(log, "   %12s   %12s   %12s\n"
 +            "   %12s   %12.5f   %12.5f\n\n",
 +            "Step", "Time", "Lambda", gmx_step_str(steps, buf), time, lambda);
 +}
 +
 +void print_ebin(ener_file_t fp_ene, gmx_bool bEne, gmx_bool bDR, gmx_bool bOR,
 +                FILE *log,
 +                gmx_large_int_t step, double time,
 +                int mode, gmx_bool bCompact,
 +                t_mdebin *md, t_fcdata *fcd,
 +                gmx_groups_t *groups, t_grpopts *opts)
 +{
 +    /*static char **grpnms=NULL;*/
 +    char         buf[246];
 +    int          i, j, n, ni, nj, ndr, nor, b;
 +    int          ndisre = 0;
 +    real        *disre_rm3tav, *disre_rt;
 +
 +    /* these are for the old-style blocks (1 subblock, only reals), because
 +       there can be only one per ID for these */
 +    int          nr[enxNR];
 +    int          id[enxNR];
 +    real        *block[enxNR];
 +
 +    /* temporary arrays for the lambda values to write out */
 +    double      enxlambda_data[2];
 +
 +    t_enxframe  fr;
 +
 +    switch (mode)
 +    {
 +        case eprNORMAL:
 +            init_enxframe(&fr);
 +            fr.t            = time;
 +            fr.step         = step;
 +            fr.nsteps       = md->ebin->nsteps;
 +            fr.dt           = md->delta_t;
 +            fr.nsum         = md->ebin->nsum;
 +            fr.nre          = (bEne) ? md->ebin->nener : 0;
 +            fr.ener         = md->ebin->e;
 +            ndisre          = bDR ? fcd->disres.npair : 0;
 +            disre_rm3tav    = fcd->disres.rm3tav;
 +            disre_rt        = fcd->disres.rt;
 +            /* Optional additional old-style (real-only) blocks. */
 +            for (i = 0; i < enxNR; i++)
 +            {
 +                nr[i] = 0;
 +            }
 +            if (fcd->orires.nr > 0 && bOR)
 +            {
 +                diagonalize_orires_tensors(&(fcd->orires));
 +                nr[enxOR]     = fcd->orires.nr;
 +                block[enxOR]  = fcd->orires.otav;
 +                id[enxOR]     = enxOR;
 +                nr[enxORI]    = (fcd->orires.oinsl != fcd->orires.otav) ?
 +                    fcd->orires.nr : 0;
 +                block[enxORI] = fcd->orires.oinsl;
 +                id[enxORI]    = enxORI;
 +                nr[enxORT]    = fcd->orires.nex*12;
 +                block[enxORT] = fcd->orires.eig;
 +                id[enxORT]    = enxORT;
 +            }
 +
 +            /* whether we are going to wrte anything out: */
 +            if (fr.nre || ndisre || nr[enxOR] || nr[enxORI])
 +            {
 +
 +                /* the old-style blocks go first */
 +                fr.nblock = 0;
 +                for (i = 0; i < enxNR; i++)
 +                {
 +                    if (nr[i] > 0)
 +                    {
 +                        fr.nblock = i + 1;
 +                    }
 +                }
 +                add_blocks_enxframe(&fr, fr.nblock);
 +                for (b = 0; b < fr.nblock; b++)
 +                {
 +                    add_subblocks_enxblock(&(fr.block[b]), 1);
 +                    fr.block[b].id        = id[b];
 +                    fr.block[b].sub[0].nr = nr[b];
 +#ifndef GMX_DOUBLE
 +                    fr.block[b].sub[0].type = xdr_datatype_float;
 +                    fr.block[b].sub[0].fval = block[b];
 +#else
 +                    fr.block[b].sub[0].type = xdr_datatype_double;
 +                    fr.block[b].sub[0].dval = block[b];
 +#endif
 +                }
 +
 +                /* check for disre block & fill it. */
 +                if (ndisre > 0)
 +                {
 +                    int db = fr.nblock;
 +                    fr.nblock += 1;
 +                    add_blocks_enxframe(&fr, fr.nblock);
 +
 +                    add_subblocks_enxblock(&(fr.block[db]), 2);
 +                    fr.block[db].id        = enxDISRE;
 +                    fr.block[db].sub[0].nr = ndisre;
 +                    fr.block[db].sub[1].nr = ndisre;
 +#ifndef GMX_DOUBLE
 +                    fr.block[db].sub[0].type = xdr_datatype_float;
 +                    fr.block[db].sub[1].type = xdr_datatype_float;
 +                    fr.block[db].sub[0].fval = disre_rt;
 +                    fr.block[db].sub[1].fval = disre_rm3tav;
 +#else
 +                    fr.block[db].sub[0].type = xdr_datatype_double;
 +                    fr.block[db].sub[1].type = xdr_datatype_double;
 +                    fr.block[db].sub[0].dval = disre_rt;
 +                    fr.block[db].sub[1].dval = disre_rm3tav;
 +#endif
 +                }
 +                /* here we can put new-style blocks */
 +
 +                /* Free energy perturbation blocks */
 +                if (md->dhc)
 +                {
 +                    mde_delta_h_coll_handle_block(md->dhc, &fr, fr.nblock);
 +                }
 +
 +                /* we can now free & reset the data in the blocks */
 +                if (md->dhc)
 +                {
 +                    mde_delta_h_coll_reset(md->dhc);
 +                }
 +
 +                /* do the actual I/O */
 +                do_enx(fp_ene, &fr);
 +                gmx_fio_check_file_position(enx_file_pointer(fp_ene));
 +                if (fr.nre)
 +                {
 +                    /* We have stored the sums, so reset the sum history */
 +                    reset_ebin_sums(md->ebin);
 +                }
 +            }
 +            free_enxframe(&fr);
 +            break;
 +        case eprAVER:
 +            if (log)
 +            {
 +                pprint(log, "A V E R A G E S", md);
 +            }
 +            break;
 +        case eprRMS:
 +            if (log)
 +            {
 +                pprint(log, "R M S - F L U C T U A T I O N S", md);
 +            }
 +            break;
 +        default:
 +            gmx_fatal(FARGS, "Invalid print mode (%d)", mode);
 +    }
 +
 +    if (log)
 +    {
 +        for (i = 0; i < opts->ngtc; i++)
 +        {
 +            if (opts->annealing[i] != eannNO)
 +            {
 +                fprintf(log, "Current ref_t for group %s: %8.1f\n",
 +                        *(groups->grpname[groups->grps[egcTC].nm_ind[i]]),
 +                        opts->ref_t[i]);
 +            }
 +        }
 +        if (mode == eprNORMAL && fcd->orires.nr > 0)
 +        {
 +            print_orires_log(log, &(fcd->orires));
 +        }
 +        fprintf(log, "   Energies (%s)\n", unit_energy);
 +        pr_ebin(log, md->ebin, md->ie, md->f_nre+md->nCrmsd, 5, mode, TRUE);
 +        fprintf(log, "\n");
 +
 +        if (!bCompact)
 +        {
 +            if (md->bDynBox)
 +            {
 +                pr_ebin(log, md->ebin, md->ib, md->bTricl ? NTRICLBOXS : NBOXS, 5,
 +                        mode, TRUE);
 +                fprintf(log, "\n");
 +            }
 +            if (md->bConstrVir)
 +            {
 +                fprintf(log, "   Constraint Virial (%s)\n", unit_energy);
 +                pr_ebin(log, md->ebin, md->isvir, 9, 3, mode, FALSE);
 +                fprintf(log, "\n");
 +                fprintf(log, "   Force Virial (%s)\n", unit_energy);
 +                pr_ebin(log, md->ebin, md->ifvir, 9, 3, mode, FALSE);
 +                fprintf(log, "\n");
 +            }
 +            if (md->bVir)
 +            {
 +                fprintf(log, "   Total Virial (%s)\n", unit_energy);
 +                pr_ebin(log, md->ebin, md->ivir, 9, 3, mode, FALSE);
 +                fprintf(log, "\n");
 +            }
 +            if (md->bPress)
 +            {
 +                fprintf(log, "   Pressure (%s)\n", unit_pres_bar);
 +                pr_ebin(log, md->ebin, md->ipres, 9, 3, mode, FALSE);
 +                fprintf(log, "\n");
 +            }
 +            if (md->bMu)
 +            {
 +                fprintf(log, "   Total Dipole (%s)\n", unit_dipole_D);
 +                pr_ebin(log, md->ebin, md->imu, 3, 3, mode, FALSE);
 +                fprintf(log, "\n");
 +            }
 +
 +            if (md->nE > 1)
 +            {
 +                if (md->print_grpnms == NULL)
 +                {
 +                    snew(md->print_grpnms, md->nE);
 +                    n = 0;
 +                    for (i = 0; (i < md->nEg); i++)
 +                    {
 +                        ni = groups->grps[egcENER].nm_ind[i];
 +                        for (j = i; (j < md->nEg); j++)
 +                        {
 +                            nj = groups->grps[egcENER].nm_ind[j];
 +                            sprintf(buf, "%s-%s", *(groups->grpname[ni]),
 +                                    *(groups->grpname[nj]));
 +                            md->print_grpnms[n++] = strdup(buf);
 +                        }
 +                    }
 +                }
 +                sprintf(buf, "Epot (%s)", unit_energy);
 +                fprintf(log, "%15s   ", buf);
 +                for (i = 0; (i < egNR); i++)
 +                {
 +                    if (md->bEInd[i])
 +                    {
 +                        fprintf(log, "%12s   ", egrp_nm[i]);
 +                    }
 +                }
 +                fprintf(log, "\n");
 +                for (i = 0; (i < md->nE); i++)
 +                {
 +                    fprintf(log, "%15s", md->print_grpnms[i]);
 +                    pr_ebin(log, md->ebin, md->igrp[i], md->nEc, md->nEc, mode,
 +                            FALSE);
 +                }
 +                fprintf(log, "\n");
 +            }
 +            if (md->nTC > 1)
 +            {
 +                pr_ebin(log, md->ebin, md->itemp, md->nTC, 4, mode, TRUE);
 +                fprintf(log, "\n");
 +            }
 +            if (md->nU > 1)
 +            {
 +                fprintf(log, "%15s   %12s   %12s   %12s\n",
 +                        "Group", "Ux", "Uy", "Uz");
 +                for (i = 0; (i < md->nU); i++)
 +                {
 +                    ni = groups->grps[egcACC].nm_ind[i];
 +                    fprintf(log, "%15s", *groups->grpname[ni]);
 +                    pr_ebin(log, md->ebin, md->iu+3*i, 3, 3, mode, FALSE);
 +                }
 +                fprintf(log, "\n");
 +            }
 +        }
 +    }
 +
 +}
 +
 +void update_energyhistory(energyhistory_t * enerhist, t_mdebin * mdebin)
 +{
 +    int i;
 +
 +    enerhist->nsteps     = mdebin->ebin->nsteps;
 +    enerhist->nsum       = mdebin->ebin->nsum;
 +    enerhist->nsteps_sim = mdebin->ebin->nsteps_sim;
 +    enerhist->nsum_sim   = mdebin->ebin->nsum_sim;
 +    enerhist->nener      = mdebin->ebin->nener;
 +
 +    if (mdebin->ebin->nsum > 0)
 +    {
 +        /* Check if we need to allocate first */
 +        if (enerhist->ener_ave == NULL)
 +        {
 +            snew(enerhist->ener_ave, enerhist->nener);
 +            snew(enerhist->ener_sum, enerhist->nener);
 +        }
 +
 +        for (i = 0; i < enerhist->nener; i++)
 +        {
 +            enerhist->ener_ave[i] = mdebin->ebin->e[i].eav;
 +            enerhist->ener_sum[i] = mdebin->ebin->e[i].esum;
 +        }
 +    }
 +
 +    if (mdebin->ebin->nsum_sim > 0)
 +    {
 +        /* Check if we need to allocate first */
 +        if (enerhist->ener_sum_sim == NULL)
 +        {
 +            snew(enerhist->ener_sum_sim, enerhist->nener);
 +        }
 +
 +        for (i = 0; i < enerhist->nener; i++)
 +        {
 +            enerhist->ener_sum_sim[i] = mdebin->ebin->e_sim[i].esum;
 +        }
 +    }
 +    if (mdebin->dhc)
 +    {
 +        mde_delta_h_coll_update_energyhistory(mdebin->dhc, enerhist);
 +    }
 +}
 +
 +void restore_energyhistory_from_state(t_mdebin        * mdebin,
 +                                      energyhistory_t * enerhist)
 +{
 +    int i;
 +
 +    if ((enerhist->nsum > 0 || enerhist->nsum_sim > 0) &&
 +        mdebin->ebin->nener != enerhist->nener)
 +    {
 +        gmx_fatal(FARGS, "Mismatch between number of energies in run input (%d) and checkpoint file (%d).",
 +                  mdebin->ebin->nener, enerhist->nener);
 +    }
 +
 +    mdebin->ebin->nsteps     = enerhist->nsteps;
 +    mdebin->ebin->nsum       = enerhist->nsum;
 +    mdebin->ebin->nsteps_sim = enerhist->nsteps_sim;
 +    mdebin->ebin->nsum_sim   = enerhist->nsum_sim;
 +
 +    for (i = 0; i < mdebin->ebin->nener; i++)
 +    {
 +        mdebin->ebin->e[i].eav  =
 +            (enerhist->nsum > 0 ? enerhist->ener_ave[i] : 0);
 +        mdebin->ebin->e[i].esum =
 +            (enerhist->nsum > 0 ? enerhist->ener_sum[i] : 0);
 +        mdebin->ebin->e_sim[i].esum =
 +            (enerhist->nsum_sim > 0 ? enerhist->ener_sum_sim[i] : 0);
 +    }
 +    if (mdebin->dhc)
 +    {
 +        mde_delta_h_coll_restore_energyhistory(mdebin->dhc, enerhist);
 +    }
 +}
index c01ffb2b4eedb800fdba45d4e423e596fd6fe085,0000000000000000000000000000000000000000..10aa13a1a10a0f08276923f5964de027d9e1ac27
mode 100644,000000..100644
--- /dev/null
@@@ -1,1348 -1,0 +1,1362 @@@
-         int       simd_4xn_diag_size, j;
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +#include "smalloc.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "nbnxn_consts.h"
 +#include "nbnxn_internal.h"
 +#include "nbnxn_search.h"
 +#include "nbnxn_atomdata.h"
 +#include "gmx_omp_nthreads.h"
 +
 +/* Default nbnxn allocation routine, allocates NBNXN_MEM_ALIGN byte aligned */
 +void nbnxn_alloc_aligned(void **ptr, size_t nbytes)
 +{
 +    *ptr = save_malloc_aligned("ptr", __FILE__, __LINE__, nbytes, 1, NBNXN_MEM_ALIGN);
 +}
 +
 +/* Free function for memory allocated with nbnxn_alloc_aligned */
 +void nbnxn_free_aligned(void *ptr)
 +{
 +    sfree_aligned(ptr);
 +}
 +
 +/* Reallocation wrapper function for nbnxn data structures */
 +void nbnxn_realloc_void(void **ptr,
 +                        int nbytes_copy, int nbytes_new,
 +                        nbnxn_alloc_t *ma,
 +                        nbnxn_free_t  *mf)
 +{
 +    void *ptr_new;
 +
 +    ma(&ptr_new, nbytes_new);
 +
 +    if (nbytes_new > 0 && ptr_new == NULL)
 +    {
 +        gmx_fatal(FARGS, "Allocation of %d bytes failed", nbytes_new);
 +    }
 +
 +    if (nbytes_copy > 0)
 +    {
 +        if (nbytes_new < nbytes_copy)
 +        {
 +            gmx_incons("In nbnxn_realloc_void: new size less than copy size");
 +        }
 +        memcpy(ptr_new, *ptr, nbytes_copy);
 +    }
 +    if (*ptr != NULL)
 +    {
 +        mf(*ptr);
 +    }
 +    *ptr = ptr_new;
 +}
 +
 +/* Reallocate the nbnxn_atomdata_t for a size of n atoms */
 +void nbnxn_atomdata_realloc(nbnxn_atomdata_t *nbat, int n)
 +{
 +    int t;
 +
 +    nbnxn_realloc_void((void **)&nbat->type,
 +                       nbat->natoms*sizeof(*nbat->type),
 +                       n*sizeof(*nbat->type),
 +                       nbat->alloc, nbat->free);
 +    nbnxn_realloc_void((void **)&nbat->lj_comb,
 +                       nbat->natoms*2*sizeof(*nbat->lj_comb),
 +                       n*2*sizeof(*nbat->lj_comb),
 +                       nbat->alloc, nbat->free);
 +    if (nbat->XFormat != nbatXYZQ)
 +    {
 +        nbnxn_realloc_void((void **)&nbat->q,
 +                           nbat->natoms*sizeof(*nbat->q),
 +                           n*sizeof(*nbat->q),
 +                           nbat->alloc, nbat->free);
 +    }
 +    if (nbat->nenergrp > 1)
 +    {
 +        nbnxn_realloc_void((void **)&nbat->energrp,
 +                           nbat->natoms/nbat->na_c*sizeof(*nbat->energrp),
 +                           n/nbat->na_c*sizeof(*nbat->energrp),
 +                           nbat->alloc, nbat->free);
 +    }
 +    nbnxn_realloc_void((void **)&nbat->x,
 +                       nbat->natoms*nbat->xstride*sizeof(*nbat->x),
 +                       n*nbat->xstride*sizeof(*nbat->x),
 +                       nbat->alloc, nbat->free);
 +    for (t = 0; t < nbat->nout; t++)
 +    {
 +        /* Allocate one element extra for possible signaling with CUDA */
 +        nbnxn_realloc_void((void **)&nbat->out[t].f,
 +                           nbat->natoms*nbat->fstride*sizeof(*nbat->out[t].f),
 +                           n*nbat->fstride*sizeof(*nbat->out[t].f),
 +                           nbat->alloc, nbat->free);
 +    }
 +    nbat->nalloc = n;
 +}
 +
 +/* Initializes an nbnxn_atomdata_output_t data structure */
 +static void nbnxn_atomdata_output_init(nbnxn_atomdata_output_t *out,
 +                                       int nb_kernel_type,
 +                                       int nenergrp, int stride,
 +                                       nbnxn_alloc_t *ma)
 +{
 +    int cj_size;
 +
 +    out->f = NULL;
 +    ma((void **)&out->fshift, SHIFTS*DIM*sizeof(*out->fshift));
 +    out->nV = nenergrp*nenergrp;
 +    ma((void **)&out->Vvdw, out->nV*sizeof(*out->Vvdw));
 +    ma((void **)&out->Vc, out->nV*sizeof(*out->Vc  ));
 +
 +    if (nb_kernel_type == nbnxnk4xN_SIMD_4xN ||
 +        nb_kernel_type == nbnxnk4xN_SIMD_2xNN)
 +    {
 +        cj_size  = nbnxn_kernel_to_cj_size(nb_kernel_type);
 +        out->nVS = nenergrp*nenergrp*stride*(cj_size>>1)*cj_size;
 +        ma((void **)&out->VSvdw, out->nVS*sizeof(*out->VSvdw));
 +        ma((void **)&out->VSc, out->nVS*sizeof(*out->VSc  ));
 +    }
 +    else
 +    {
 +        out->nVS = 0;
 +    }
 +}
 +
 +static void copy_int_to_nbat_int(const int *a, int na, int na_round,
 +                                 const int *in, int fill, int *innb)
 +{
 +    int i, j;
 +
 +    j = 0;
 +    for (i = 0; i < na; i++)
 +    {
 +        innb[j++] = in[a[i]];
 +    }
 +    /* Complete the partially filled last cell with fill */
 +    for (; i < na_round; i++)
 +    {
 +        innb[j++] = fill;
 +    }
 +}
 +
 +static void clear_nbat_real(int na, int nbatFormat, real *xnb, int a0)
 +{
 +    int a, d, j, c;
 +
 +    switch (nbatFormat)
 +    {
 +        case nbatXYZ:
 +            for (a = 0; a < na; a++)
 +            {
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    xnb[(a0+a)*STRIDE_XYZ+d] = 0;
 +                }
 +            }
 +            break;
 +        case nbatXYZQ:
 +            for (a = 0; a < na; a++)
 +            {
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    xnb[(a0+a)*STRIDE_XYZQ+d] = 0;
 +                }
 +            }
 +            break;
 +        case nbatX4:
 +            j = X4_IND_A(a0);
 +            c = a0 & (PACK_X4-1);
 +            for (a = 0; a < na; a++)
 +            {
 +                xnb[j+XX*PACK_X4] = 0;
 +                xnb[j+YY*PACK_X4] = 0;
 +                xnb[j+ZZ*PACK_X4] = 0;
 +                j++;
 +                c++;
 +                if (c == PACK_X4)
 +                {
 +                    j += (DIM-1)*PACK_X4;
 +                    c  = 0;
 +                }
 +            }
 +            break;
 +        case nbatX8:
 +            j = X8_IND_A(a0);
 +            c = a0 & (PACK_X8-1);
 +            for (a = 0; a < na; a++)
 +            {
 +                xnb[j+XX*PACK_X8] = 0;
 +                xnb[j+YY*PACK_X8] = 0;
 +                xnb[j+ZZ*PACK_X8] = 0;
 +                j++;
 +                c++;
 +                if (c == PACK_X8)
 +                {
 +                    j += (DIM-1)*PACK_X8;
 +                    c  = 0;
 +                }
 +            }
 +            break;
 +    }
 +}
 +
 +void copy_rvec_to_nbat_real(const int *a, int na, int na_round,
 +                            rvec *x, int nbatFormat, real *xnb, int a0,
 +                            int cx, int cy, int cz)
 +{
 +    int i, j, c;
 +
 +/* We might need to place filler particles to fill up the cell to na_round.
 + * The coefficients (LJ and q) for such particles are zero.
 + * But we might still get NaN as 0*NaN when distances are too small.
 + * We hope that -107 nm is far away enough from to zero
 + * to avoid accidental short distances to particles shifted down for pbc.
 + */
 +#define NBAT_FAR_AWAY 107
 +
 +    switch (nbatFormat)
 +    {
 +        case nbatXYZ:
 +            j = a0*STRIDE_XYZ;
 +            for (i = 0; i < na; i++)
 +            {
 +                xnb[j++] = x[a[i]][XX];
 +                xnb[j++] = x[a[i]][YY];
 +                xnb[j++] = x[a[i]][ZZ];
 +            }
 +            /* Complete the partially filled last cell with copies of the last element.
 +             * This simplifies the bounding box calculation and avoid
 +             * numerical issues with atoms that are coincidentally close.
 +             */
 +            for (; i < na_round; i++)
 +            {
 +                xnb[j++] = -NBAT_FAR_AWAY*(1 + cx);
 +                xnb[j++] = -NBAT_FAR_AWAY*(1 + cy);
 +                xnb[j++] = -NBAT_FAR_AWAY*(1 + cz + i);
 +            }
 +            break;
 +        case nbatXYZQ:
 +            j = a0*STRIDE_XYZQ;
 +            for (i = 0; i < na; i++)
 +            {
 +                xnb[j++] = x[a[i]][XX];
 +                xnb[j++] = x[a[i]][YY];
 +                xnb[j++] = x[a[i]][ZZ];
 +                j++;
 +            }
 +            /* Complete the partially filled last cell with particles far apart */
 +            for (; i < na_round; i++)
 +            {
 +                xnb[j++] = -NBAT_FAR_AWAY*(1 + cx);
 +                xnb[j++] = -NBAT_FAR_AWAY*(1 + cy);
 +                xnb[j++] = -NBAT_FAR_AWAY*(1 + cz + i);
 +                j++;
 +            }
 +            break;
 +        case nbatX4:
 +            j = X4_IND_A(a0);
 +            c = a0 & (PACK_X4-1);
 +            for (i = 0; i < na; i++)
 +            {
 +                xnb[j+XX*PACK_X4] = x[a[i]][XX];
 +                xnb[j+YY*PACK_X4] = x[a[i]][YY];
 +                xnb[j+ZZ*PACK_X4] = x[a[i]][ZZ];
 +                j++;
 +                c++;
 +                if (c == PACK_X4)
 +                {
 +                    j += (DIM-1)*PACK_X4;
 +                    c  = 0;
 +                }
 +            }
 +            /* Complete the partially filled last cell with particles far apart */
 +            for (; i < na_round; i++)
 +            {
 +                xnb[j+XX*PACK_X4] = -NBAT_FAR_AWAY*(1 + cx);
 +                xnb[j+YY*PACK_X4] = -NBAT_FAR_AWAY*(1 + cy);
 +                xnb[j+ZZ*PACK_X4] = -NBAT_FAR_AWAY*(1 + cz + i);
 +                j++;
 +                c++;
 +                if (c == PACK_X4)
 +                {
 +                    j += (DIM-1)*PACK_X4;
 +                    c  = 0;
 +                }
 +            }
 +            break;
 +        case nbatX8:
 +            j = X8_IND_A(a0);
 +            c = a0 & (PACK_X8 - 1);
 +            for (i = 0; i < na; i++)
 +            {
 +                xnb[j+XX*PACK_X8] = x[a[i]][XX];
 +                xnb[j+YY*PACK_X8] = x[a[i]][YY];
 +                xnb[j+ZZ*PACK_X8] = x[a[i]][ZZ];
 +                j++;
 +                c++;
 +                if (c == PACK_X8)
 +                {
 +                    j += (DIM-1)*PACK_X8;
 +                    c  = 0;
 +                }
 +            }
 +            /* Complete the partially filled last cell with particles far apart */
 +            for (; i < na_round; i++)
 +            {
 +                xnb[j+XX*PACK_X8] = -NBAT_FAR_AWAY*(1 + cx);
 +                xnb[j+YY*PACK_X8] = -NBAT_FAR_AWAY*(1 + cy);
 +                xnb[j+ZZ*PACK_X8] = -NBAT_FAR_AWAY*(1 + cz + i);
 +                j++;
 +                c++;
 +                if (c == PACK_X8)
 +                {
 +                    j += (DIM-1)*PACK_X8;
 +                    c  = 0;
 +                }
 +            }
 +            break;
 +        default:
 +            gmx_incons("Unsupported nbnxn_atomdata_t format");
 +    }
 +}
 +
 +/* Determines the combination rule (or none) to be used, stores it,
 + * and sets the LJ parameters required with the rule.
 + */
 +static void set_combination_rule_data(nbnxn_atomdata_t *nbat)
 +{
 +    int  nt, i, j;
 +    real c6, c12;
 +
 +    nt = nbat->ntype;
 +
 +    switch (nbat->comb_rule)
 +    {
 +        case  ljcrGEOM:
 +            nbat->comb_rule = ljcrGEOM;
 +
 +            for (i = 0; i < nt; i++)
 +            {
 +                /* Copy the diagonal from the nbfp matrix */
 +                nbat->nbfp_comb[i*2  ] = sqrt(nbat->nbfp[(i*nt+i)*2  ]);
 +                nbat->nbfp_comb[i*2+1] = sqrt(nbat->nbfp[(i*nt+i)*2+1]);
 +            }
 +            break;
 +        case ljcrLB:
 +            for (i = 0; i < nt; i++)
 +            {
 +                /* Get 6*C6 and 12*C12 from the diagonal of the nbfp matrix */
 +                c6  = nbat->nbfp[(i*nt+i)*2  ];
 +                c12 = nbat->nbfp[(i*nt+i)*2+1];
 +                if (c6 > 0 && c12 > 0)
 +                {
 +                    /* We store 0.5*2^1/6*sigma and sqrt(4*3*eps),
 +                     * so we get 6*C6 and 12*C12 after combining.
 +                     */
 +                    nbat->nbfp_comb[i*2  ] = 0.5*pow(c12/c6, 1.0/6.0);
 +                    nbat->nbfp_comb[i*2+1] = sqrt(c6*c6/c12);
 +                }
 +                else
 +                {
 +                    nbat->nbfp_comb[i*2  ] = 0;
 +                    nbat->nbfp_comb[i*2+1] = 0;
 +                }
 +            }
 +            break;
 +        case ljcrNONE:
 +            /* nbfp_s4 stores two parameters using a stride of 4,
 +             * because this would suit x86 SIMD single-precision
 +             * quad-load intrinsics. There's a slight inefficiency in
 +             * allocating and initializing nbfp_s4 when it might not
 +             * be used, but introducing the conditional code is not
 +             * really worth it. */
 +            nbat->alloc((void **)&nbat->nbfp_s4, nt*nt*4*sizeof(*nbat->nbfp_s4));
 +            for (i = 0; i < nt; i++)
 +            {
 +                for (j = 0; j < nt; j++)
 +                {
 +                    nbat->nbfp_s4[(i*nt+j)*4+0] = nbat->nbfp[(i*nt+j)*2+0];
 +                    nbat->nbfp_s4[(i*nt+j)*4+1] = nbat->nbfp[(i*nt+j)*2+1];
 +                    nbat->nbfp_s4[(i*nt+j)*4+2] = 0;
 +                    nbat->nbfp_s4[(i*nt+j)*4+3] = 0;
 +                }
 +            }
 +            break;
 +        default:
 +            gmx_incons("Unknown combination rule");
 +            break;
 +    }
 +}
 +
 +/* Initializes an nbnxn_atomdata_t data structure */
 +void nbnxn_atomdata_init(FILE *fp,
 +                         nbnxn_atomdata_t *nbat,
 +                         int nb_kernel_type,
 +                         int ntype, const real *nbfp,
 +                         int n_energygroups,
 +                         int nout,
 +                         nbnxn_alloc_t *alloc,
 +                         nbnxn_free_t  *free)
 +{
 +    int      i, j;
 +    real     c6, c12, tol;
 +    char    *ptr;
 +    gmx_bool simple, bCombGeom, bCombLB;
 +
 +    if (alloc == NULL)
 +    {
 +        nbat->alloc = nbnxn_alloc_aligned;
 +    }
 +    else
 +    {
 +        nbat->alloc = alloc;
 +    }
 +    if (free == NULL)
 +    {
 +        nbat->free = nbnxn_free_aligned;
 +    }
 +    else
 +    {
 +        nbat->free = free;
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "There are %d atom types in the system, adding one for nbnxn_atomdata_t\n", ntype);
 +    }
 +    nbat->ntype = ntype + 1;
 +    nbat->alloc((void **)&nbat->nbfp,
 +                nbat->ntype*nbat->ntype*2*sizeof(*nbat->nbfp));
 +    nbat->alloc((void **)&nbat->nbfp_comb, nbat->ntype*2*sizeof(*nbat->nbfp_comb));
 +
 +    /* A tolerance of 1e-5 seems reasonable for (possibly hand-typed)
 +     * force-field floating point parameters.
 +     */
 +    tol = 1e-5;
 +    ptr = getenv("GMX_LJCOMB_TOL");
 +    if (ptr != NULL)
 +    {
 +        double dbl;
 +
 +        sscanf(ptr, "%lf", &dbl);
 +        tol = dbl;
 +    }
 +    bCombGeom = TRUE;
 +    bCombLB   = TRUE;
 +
 +    /* Temporarily fill nbat->nbfp_comb with sigma and epsilon
 +     * to check for the LB rule.
 +     */
 +    for (i = 0; i < ntype; i++)
 +    {
 +        c6  = nbfp[(i*ntype+i)*2  ]/6.0;
 +        c12 = nbfp[(i*ntype+i)*2+1]/12.0;
 +        if (c6 > 0 && c12 > 0)
 +        {
 +            nbat->nbfp_comb[i*2  ] = pow(c12/c6, 1.0/6.0);
 +            nbat->nbfp_comb[i*2+1] = 0.25*c6*c6/c12;
 +        }
 +        else if (c6 == 0 && c12 == 0)
 +        {
 +            nbat->nbfp_comb[i*2  ] = 0;
 +            nbat->nbfp_comb[i*2+1] = 0;
 +        }
 +        else
 +        {
 +            /* Can not use LB rule with only dispersion or repulsion */
 +            bCombLB = FALSE;
 +        }
 +    }
 +
 +    for (i = 0; i < nbat->ntype; i++)
 +    {
 +        for (j = 0; j < nbat->ntype; j++)
 +        {
 +            if (i < ntype && j < ntype)
 +            {
 +                /* fr->nbfp has been updated, so that array too now stores c6/c12 including
 +                 * the 6.0/12.0 prefactors to save 2 flops in the most common case (force-only).
 +                 */
 +                c6  = nbfp[(i*ntype+j)*2  ];
 +                c12 = nbfp[(i*ntype+j)*2+1];
 +                nbat->nbfp[(i*nbat->ntype+j)*2  ] = c6;
 +                nbat->nbfp[(i*nbat->ntype+j)*2+1] = c12;
 +
 +                /* Compare 6*C6 and 12*C12 for geometric cobination rule */
 +                bCombGeom = bCombGeom &&
 +                    gmx_within_tol(c6*c6, nbfp[(i*ntype+i)*2  ]*nbfp[(j*ntype+j)*2  ], tol) &&
 +                    gmx_within_tol(c12*c12, nbfp[(i*ntype+i)*2+1]*nbfp[(j*ntype+j)*2+1], tol);
 +
 +                /* Compare C6 and C12 for Lorentz-Berthelot combination rule */
 +                c6     /= 6.0;
 +                c12    /= 12.0;
 +                bCombLB = bCombLB &&
 +                    ((c6 == 0 && c12 == 0 &&
 +                      (nbat->nbfp_comb[i*2+1] == 0 || nbat->nbfp_comb[j*2+1] == 0)) ||
 +                     (c6 > 0 && c12 > 0 &&
 +                      gmx_within_tol(pow(c12/c6, 1.0/6.0), 0.5*(nbat->nbfp_comb[i*2]+nbat->nbfp_comb[j*2]), tol) &&
 +                      gmx_within_tol(0.25*c6*c6/c12, sqrt(nbat->nbfp_comb[i*2+1]*nbat->nbfp_comb[j*2+1]), tol)));
 +            }
 +            else
 +            {
 +                /* Add zero parameters for the additional dummy atom type */
 +                nbat->nbfp[(i*nbat->ntype+j)*2  ] = 0;
 +                nbat->nbfp[(i*nbat->ntype+j)*2+1] = 0;
 +            }
 +        }
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "Combination rules: geometric %d Lorentz-Berthelot %d\n",
 +                bCombGeom, bCombLB);
 +    }
 +
 +    simple = nbnxn_kernel_pairlist_simple(nb_kernel_type);
 +
 +    if (simple)
 +    {
 +        /* We prefer the geometic combination rule,
 +         * as that gives a slightly faster kernel than the LB rule.
 +         */
 +        if (bCombGeom)
 +        {
 +            nbat->comb_rule = ljcrGEOM;
 +        }
 +        else if (bCombLB)
 +        {
 +            nbat->comb_rule = ljcrLB;
 +        }
 +        else
 +        {
 +            nbat->comb_rule = ljcrNONE;
 +
 +            nbat->free(nbat->nbfp_comb);
 +        }
 +
 +        if (fp)
 +        {
 +            if (nbat->comb_rule == ljcrNONE)
 +            {
 +                fprintf(fp, "Using full Lennard-Jones parameter combination matrix\n\n");
 +            }
 +            else
 +            {
 +                fprintf(fp, "Using %s Lennard-Jones combination rule\n\n",
 +                        nbat->comb_rule == ljcrGEOM ? "geometric" : "Lorentz-Berthelot");
 +            }
 +        }
 +
 +        set_combination_rule_data(nbat);
 +    }
 +    else
 +    {
 +        nbat->comb_rule = ljcrNONE;
 +
 +        nbat->free(nbat->nbfp_comb);
 +    }
 +
 +    nbat->natoms  = 0;
 +    nbat->type    = NULL;
 +    nbat->lj_comb = NULL;
 +    if (simple)
 +    {
 +        int pack_x;
 +
 +        switch (nb_kernel_type)
 +        {
 +            case nbnxnk4xN_SIMD_4xN:
 +            case nbnxnk4xN_SIMD_2xNN:
 +                pack_x = max(NBNXN_CPU_CLUSTER_I_SIZE,
 +                             nbnxn_kernel_to_cj_size(nb_kernel_type));
 +                switch (pack_x)
 +                {
 +                    case 4:
 +                        nbat->XFormat = nbatX4;
 +                        break;
 +                    case 8:
 +                        nbat->XFormat = nbatX8;
 +                        break;
 +                    default:
 +                        gmx_incons("Unsupported packing width");
 +                }
 +                break;
 +            default:
 +                nbat->XFormat = nbatXYZ;
 +                break;
 +        }
 +
 +        nbat->FFormat = nbat->XFormat;
 +    }
 +    else
 +    {
 +        nbat->XFormat = nbatXYZQ;
 +        nbat->FFormat = nbatXYZ;
 +    }
 +    nbat->q        = NULL;
 +    nbat->nenergrp = n_energygroups;
 +    if (!simple)
 +    {
 +        /* Energy groups not supported yet for super-sub lists */
 +        if (n_energygroups > 1 && fp != NULL)
 +        {
 +            fprintf(fp, "\nNOTE: With GPUs, reporting energy group contributions is not supported\n\n");
 +        }
 +        nbat->nenergrp = 1;
 +    }
 +    /* Temporary storage goes as #grp^3*simd_width^2/2, so limit to 64 */
 +    if (nbat->nenergrp > 64)
 +    {
 +        gmx_fatal(FARGS, "With NxN kernels not more than 64 energy groups are supported\n");
 +    }
 +    nbat->neg_2log = 1;
 +    while (nbat->nenergrp > (1<<nbat->neg_2log))
 +    {
 +        nbat->neg_2log++;
 +    }
 +    nbat->energrp = NULL;
 +    nbat->alloc((void **)&nbat->shift_vec, SHIFTS*sizeof(*nbat->shift_vec));
 +    nbat->xstride = (nbat->XFormat == nbatXYZQ ? STRIDE_XYZQ : DIM);
 +    nbat->fstride = (nbat->FFormat == nbatXYZQ ? STRIDE_XYZQ : DIM);
 +    nbat->x       = NULL;
 +
 +#ifdef GMX_NBNXN_SIMD
 +    if (simple)
 +    {
 +        /* Set the diagonal cluster pair exclusion mask setup data.
 +         * In the kernel we check 0 < j - i to generate the masks.
 +         * Here we store j - i for generating the mask for the first i,
 +         * we substract 0.5 to avoid rounding issues.
 +         * In the kernel we can subtract 1 to generate the subsequent mask.
 +         */
 +        const int simd_width = GMX_NBNXN_SIMD_BITWIDTH/(sizeof(real)*8);
- #if GMX_NBNXN_SIMD_BITWIDTH == 128
- #define GMX_MM128_HERE
- #endif
- #if GMX_NBNXN_SIMD_BITWIDTH == 256
- #define GMX_MM256_HERE
++        int       simd_4xn_diag_size, real_excl, simd_excl_size, j, s;
 +
 +        simd_4xn_diag_size = max(NBNXN_CPU_CLUSTER_I_SIZE, simd_width);
 +        snew_aligned(nbat->simd_4xn_diag, simd_4xn_diag_size, NBNXN_MEM_ALIGN);
 +        for (j = 0; j < simd_4xn_diag_size; j++)
 +        {
 +            nbat->simd_4xn_diag[j] = j - 0.5;
 +        }
 +
 +        snew_aligned(nbat->simd_2xnn_diag, simd_width, NBNXN_MEM_ALIGN);
 +        for (j = 0; j < simd_width/2; j++)
 +        {
 +            /* The j-cluster size is half the SIMD width */
 +            nbat->simd_2xnn_diag[j]              = j - 0.5;
 +            /* The next half of the SIMD width is for i + 1 */
 +            nbat->simd_2xnn_diag[simd_width/2+j] = j - 1 - 0.5;
 +        }
++
++        /* We always use 32-bit integer exclusion masks. When we use
++         * double precision, we fit two integers in a double SIMD register.
++         */
++        real_excl = sizeof(real)/sizeof(*nbat->simd_excl_mask);
++        /* Set bits for use with both 4xN and 2x(N+N) kernels */
++        simd_excl_size = NBNXN_CPU_CLUSTER_I_SIZE*simd_width*real_excl;
++        snew_aligned(nbat->simd_excl_mask, simd_excl_size*real_excl, NBNXN_MEM_ALIGN);
++        for (j = 0; j < simd_excl_size; j++)
++        {
++            /* Set the consecutive bits for masking pair exclusions.
++             * For double a single-bit mask would be enough.
++             * But using two bits avoids endianness issues.
++             */
++            for (s = 0; s < real_excl; s++)
++            {
++                /* Set the consecutive bits for masking pair exclusions */
++                nbat->simd_excl_mask[j*real_excl + s] = (1U << j);
++            }
++        }
 +    }
 +#endif
 +
 +    /* Initialize the output data structures */
 +    nbat->nout    = nout;
 +    snew(nbat->out, nbat->nout);
 +    nbat->nalloc  = 0;
 +    for (i = 0; i < nbat->nout; i++)
 +    {
 +        nbnxn_atomdata_output_init(&nbat->out[i],
 +                                   nb_kernel_type,
 +                                   nbat->nenergrp, 1<<nbat->neg_2log,
 +                                   nbat->alloc);
 +    }
 +    nbat->buffer_flags.flag        = NULL;
 +    nbat->buffer_flags.flag_nalloc = 0;
 +}
 +
 +static void copy_lj_to_nbat_lj_comb_x4(const real *ljparam_type,
 +                                       const int *type, int na,
 +                                       real *ljparam_at)
 +{
 +    int is, k, i;
 +
 +    /* The LJ params follow the combination rule:
 +     * copy the params for the type array to the atom array.
 +     */
 +    for (is = 0; is < na; is += PACK_X4)
 +    {
 +        for (k = 0; k < PACK_X4; k++)
 +        {
 +            i = is + k;
 +            ljparam_at[is*2        +k] = ljparam_type[type[i]*2  ];
 +            ljparam_at[is*2+PACK_X4+k] = ljparam_type[type[i]*2+1];
 +        }
 +    }
 +}
 +
 +static void copy_lj_to_nbat_lj_comb_x8(const real *ljparam_type,
 +                                       const int *type, int na,
 +                                       real *ljparam_at)
 +{
 +    int is, k, i;
 +
 +    /* The LJ params follow the combination rule:
 +     * copy the params for the type array to the atom array.
 +     */
 +    for (is = 0; is < na; is += PACK_X8)
 +    {
 +        for (k = 0; k < PACK_X8; k++)
 +        {
 +            i = is + k;
 +            ljparam_at[is*2        +k] = ljparam_type[type[i]*2  ];
 +            ljparam_at[is*2+PACK_X8+k] = ljparam_type[type[i]*2+1];
 +        }
 +    }
 +}
 +
 +/* Sets the atom type and LJ data in nbnxn_atomdata_t */
 +static void nbnxn_atomdata_set_atomtypes(nbnxn_atomdata_t    *nbat,
 +                                         int                  ngrid,
 +                                         const nbnxn_search_t nbs,
 +                                         const int           *type)
 +{
 +    int                 g, i, ncz, ash;
 +    const nbnxn_grid_t *grid;
 +
 +    for (g = 0; g < ngrid; g++)
 +    {
 +        grid = &nbs->grid[g];
 +
 +        /* Loop over all columns and copy and fill */
 +        for (i = 0; i < grid->ncx*grid->ncy; i++)
 +        {
 +            ncz = grid->cxy_ind[i+1] - grid->cxy_ind[i];
 +            ash = (grid->cell0 + grid->cxy_ind[i])*grid->na_sc;
 +
 +            copy_int_to_nbat_int(nbs->a+ash, grid->cxy_na[i], ncz*grid->na_sc,
 +                                 type, nbat->ntype-1, nbat->type+ash);
 +
 +            if (nbat->comb_rule != ljcrNONE)
 +            {
 +                if (nbat->XFormat == nbatX4)
 +                {
 +                    copy_lj_to_nbat_lj_comb_x4(nbat->nbfp_comb,
 +                                               nbat->type+ash, ncz*grid->na_sc,
 +                                               nbat->lj_comb+ash*2);
 +                }
 +                else if (nbat->XFormat == nbatX8)
 +                {
 +                    copy_lj_to_nbat_lj_comb_x8(nbat->nbfp_comb,
 +                                               nbat->type+ash, ncz*grid->na_sc,
 +                                               nbat->lj_comb+ash*2);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/* Sets the charges in nbnxn_atomdata_t *nbat */
 +static void nbnxn_atomdata_set_charges(nbnxn_atomdata_t    *nbat,
 +                                       int                  ngrid,
 +                                       const nbnxn_search_t nbs,
 +                                       const real          *charge)
 +{
 +    int                 g, cxy, ncz, ash, na, na_round, i, j;
 +    real               *q;
 +    const nbnxn_grid_t *grid;
 +
 +    for (g = 0; g < ngrid; g++)
 +    {
 +        grid = &nbs->grid[g];
 +
 +        /* Loop over all columns and copy and fill */
 +        for (cxy = 0; cxy < grid->ncx*grid->ncy; cxy++)
 +        {
 +            ash      = (grid->cell0 + grid->cxy_ind[cxy])*grid->na_sc;
 +            na       = grid->cxy_na[cxy];
 +            na_round = (grid->cxy_ind[cxy+1] - grid->cxy_ind[cxy])*grid->na_sc;
 +
 +            if (nbat->XFormat == nbatXYZQ)
 +            {
 +                q = nbat->x + ash*STRIDE_XYZQ + ZZ + 1;
 +                for (i = 0; i < na; i++)
 +                {
 +                    *q = charge[nbs->a[ash+i]];
 +                    q += STRIDE_XYZQ;
 +                }
 +                /* Complete the partially filled last cell with zeros */
 +                for (; i < na_round; i++)
 +                {
 +                    *q = 0;
 +                    q += STRIDE_XYZQ;
 +                }
 +            }
 +            else
 +            {
 +                q = nbat->q + ash;
 +                for (i = 0; i < na; i++)
 +                {
 +                    *q = charge[nbs->a[ash+i]];
 +                    q++;
 +                }
 +                /* Complete the partially filled last cell with zeros */
 +                for (; i < na_round; i++)
 +                {
 +                    *q = 0;
 +                    q++;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/* Copies the energy group indices to a reordered and packed array */
 +static void copy_egp_to_nbat_egps(const int *a, int na, int na_round,
 +                                  int na_c, int bit_shift,
 +                                  const int *in, int *innb)
 +{
 +    int i, j, sa, at;
 +    int comb;
 +
 +    j = 0;
 +    for (i = 0; i < na; i += na_c)
 +    {
 +        /* Store na_c energy group numbers into one int */
 +        comb = 0;
 +        for (sa = 0; sa < na_c; sa++)
 +        {
 +            at = a[i+sa];
 +            if (at >= 0)
 +            {
 +                comb |= (GET_CGINFO_GID(in[at]) << (sa*bit_shift));
 +            }
 +        }
 +        innb[j++] = comb;
 +    }
 +    /* Complete the partially filled last cell with fill */
 +    for (; i < na_round; i += na_c)
 +    {
 +        innb[j++] = 0;
 +    }
 +}
 +
 +/* Set the energy group indices for atoms in nbnxn_atomdata_t */
 +static void nbnxn_atomdata_set_energygroups(nbnxn_atomdata_t    *nbat,
 +                                            int                  ngrid,
 +                                            const nbnxn_search_t nbs,
 +                                            const int           *atinfo)
 +{
 +    int                 g, i, ncz, ash;
 +    const nbnxn_grid_t *grid;
 +
 +    for (g = 0; g < ngrid; g++)
 +    {
 +        grid = &nbs->grid[g];
 +
 +        /* Loop over all columns and copy and fill */
 +        for (i = 0; i < grid->ncx*grid->ncy; i++)
 +        {
 +            ncz = grid->cxy_ind[i+1] - grid->cxy_ind[i];
 +            ash = (grid->cell0 + grid->cxy_ind[i])*grid->na_sc;
 +
 +            copy_egp_to_nbat_egps(nbs->a+ash, grid->cxy_na[i], ncz*grid->na_sc,
 +                                  nbat->na_c, nbat->neg_2log,
 +                                  atinfo, nbat->energrp+(ash>>grid->na_c_2log));
 +        }
 +    }
 +}
 +
 +/* Sets all required atom parameter data in nbnxn_atomdata_t */
 +void nbnxn_atomdata_set(nbnxn_atomdata_t    *nbat,
 +                        int                  locality,
 +                        const nbnxn_search_t nbs,
 +                        const t_mdatoms     *mdatoms,
 +                        const int           *atinfo)
 +{
 +    int ngrid;
 +
 +    if (locality == eatLocal)
 +    {
 +        ngrid = 1;
 +    }
 +    else
 +    {
 +        ngrid = nbs->ngrid;
 +    }
 +
 +    nbnxn_atomdata_set_atomtypes(nbat, ngrid, nbs, mdatoms->typeA);
 +
 +    nbnxn_atomdata_set_charges(nbat, ngrid, nbs, mdatoms->chargeA);
 +
 +    if (nbat->nenergrp > 1)
 +    {
 +        nbnxn_atomdata_set_energygroups(nbat, ngrid, nbs, atinfo);
 +    }
 +}
 +
 +/* Copies the shift vector array to nbnxn_atomdata_t */
 +void nbnxn_atomdata_copy_shiftvec(gmx_bool          bDynamicBox,
 +                                  rvec             *shift_vec,
 +                                  nbnxn_atomdata_t *nbat)
 +{
 +    int i;
 +
 +    nbat->bDynamicBox = bDynamicBox;
 +    for (i = 0; i < SHIFTS; i++)
 +    {
 +        copy_rvec(shift_vec[i], nbat->shift_vec[i]);
 +    }
 +}
 +
 +/* Copies (and reorders) the coordinates to nbnxn_atomdata_t */
 +void nbnxn_atomdata_copy_x_to_nbat_x(const nbnxn_search_t nbs,
 +                                     int                  locality,
 +                                     gmx_bool             FillLocal,
 +                                     rvec                *x,
 +                                     nbnxn_atomdata_t    *nbat)
 +{
 +    int g0 = 0, g1 = 0;
 +    int nth, th;
 +
 +    switch (locality)
 +    {
 +        case eatAll:
 +            g0 = 0;
 +            g1 = nbs->ngrid;
 +            break;
 +        case eatLocal:
 +            g0 = 0;
 +            g1 = 1;
 +            break;
 +        case eatNonlocal:
 +            g0 = 1;
 +            g1 = nbs->ngrid;
 +            break;
 +    }
 +
 +    if (FillLocal)
 +    {
 +        nbat->natoms_local = nbs->grid[0].nc*nbs->grid[0].na_sc;
 +    }
 +
 +    nth = gmx_omp_nthreads_get(emntPairsearch);
 +
 +#pragma omp parallel for num_threads(nth) schedule(static)
 +    for (th = 0; th < nth; th++)
 +    {
 +        int g;
 +
 +        for (g = g0; g < g1; g++)
 +        {
 +            const nbnxn_grid_t *grid;
 +            int                 cxy0, cxy1, cxy;
 +
 +            grid = &nbs->grid[g];
 +
 +            cxy0 = (grid->ncx*grid->ncy* th   +nth-1)/nth;
 +            cxy1 = (grid->ncx*grid->ncy*(th+1)+nth-1)/nth;
 +
 +            for (cxy = cxy0; cxy < cxy1; cxy++)
 +            {
 +                int na, ash, na_fill;
 +
 +                na  = grid->cxy_na[cxy];
 +                ash = (grid->cell0 + grid->cxy_ind[cxy])*grid->na_sc;
 +
 +                if (g == 0 && FillLocal)
 +                {
 +                    na_fill =
 +                        (grid->cxy_ind[cxy+1] - grid->cxy_ind[cxy])*grid->na_sc;
 +                }
 +                else
 +                {
 +                    /* We fill only the real particle locations.
 +                     * We assume the filling entries at the end have been
 +                     * properly set before during ns.
 +                     */
 +                    na_fill = na;
 +                }
 +                copy_rvec_to_nbat_real(nbs->a+ash, na, na_fill, x,
 +                                       nbat->XFormat, nbat->x, ash,
 +                                       0, 0, 0);
 +            }
 +        }
 +    }
 +}
 +
 +static void
 +nbnxn_atomdata_clear_reals(real * gmx_restrict dest,
 +                           int i0, int i1)
 +{
 +    int i;
 +
 +    for (i = i0; i < i1; i++)
 +    {
 +        dest[i] = 0;
 +    }
 +}
 +
 +static void
 +nbnxn_atomdata_reduce_reals(real * gmx_restrict dest,
 +                            gmx_bool bDestSet,
 +                            real ** gmx_restrict src,
 +                            int nsrc,
 +                            int i0, int i1)
 +{
 +    int i, s;
 +
 +    if (bDestSet)
 +    {
 +        /* The destination buffer contains data, add to it */
 +        for (i = i0; i < i1; i++)
 +        {
 +            for (s = 0; s < nsrc; s++)
 +            {
 +                dest[i] += src[s][i];
 +            }
 +        }
 +    }
 +    else
 +    {
 +        /* The destination buffer is unitialized, set it first */
 +        for (i = i0; i < i1; i++)
 +        {
 +            dest[i] = src[0][i];
 +            for (s = 1; s < nsrc; s++)
 +            {
 +                dest[i] += src[s][i];
 +            }
 +        }
 +    }
 +}
 +
 +static void
 +nbnxn_atomdata_reduce_reals_simd(real * gmx_restrict dest,
 +                                 gmx_bool bDestSet,
 +                                 real ** gmx_restrict src,
 +                                 int nsrc,
 +                                 int i0, int i1)
 +{
 +#ifdef GMX_NBNXN_SIMD
 +/* The SIMD width here is actually independent of that in the kernels,
 + * but we use the same width for simplicity (usually optimal anyhow).
 + */
- #undef GMX_MM128_HERE
- #undef GMX_MM256_HERE
++#ifdef GMX_NBNXN_HALF_WIDTH_SIMD
++#define GMX_USE_HALF_WIDTH_SIMD_HERE
 +#endif
 +#include "gmx_simd_macros.h"
 +
 +    int       i, s;
 +    gmx_mm_pr dest_SSE, src_SSE;
 +
 +    if (bDestSet)
 +    {
 +        for (i = i0; i < i1; i += GMX_SIMD_WIDTH_HERE)
 +        {
 +            dest_SSE = gmx_load_pr(dest+i);
 +            for (s = 0; s < nsrc; s++)
 +            {
 +                src_SSE  = gmx_load_pr(src[s]+i);
 +                dest_SSE = gmx_add_pr(dest_SSE, src_SSE);
 +            }
 +            gmx_store_pr(dest+i, dest_SSE);
 +        }
 +    }
 +    else
 +    {
 +        for (i = i0; i < i1; i += GMX_SIMD_WIDTH_HERE)
 +        {
 +            dest_SSE = gmx_load_pr(src[0]+i);
 +            for (s = 1; s < nsrc; s++)
 +            {
 +                src_SSE  = gmx_load_pr(src[s]+i);
 +                dest_SSE = gmx_add_pr(dest_SSE, src_SSE);
 +            }
 +            gmx_store_pr(dest+i, dest_SSE);
 +        }
 +    }
 +#endif
 +}
 +
 +/* Add part of the force array(s) from nbnxn_atomdata_t to f */
 +static void
 +nbnxn_atomdata_add_nbat_f_to_f_part(const nbnxn_search_t nbs,
 +                                    const nbnxn_atomdata_t *nbat,
 +                                    nbnxn_atomdata_output_t *out,
 +                                    int nfa,
 +                                    int a0, int a1,
 +                                    rvec *f)
 +{
 +    int         a, i, fa;
 +    const int  *cell;
 +    const real *fnb;
 +
 +    cell = nbs->cell;
 +
 +    /* Loop over all columns and copy and fill */
 +    switch (nbat->FFormat)
 +    {
 +        case nbatXYZ:
 +        case nbatXYZQ:
 +            if (nfa == 1)
 +            {
 +                fnb = out[0].f;
 +
 +                for (a = a0; a < a1; a++)
 +                {
 +                    i = cell[a]*nbat->fstride;
 +
 +                    f[a][XX] += fnb[i];
 +                    f[a][YY] += fnb[i+1];
 +                    f[a][ZZ] += fnb[i+2];
 +                }
 +            }
 +            else
 +            {
 +                for (a = a0; a < a1; a++)
 +                {
 +                    i = cell[a]*nbat->fstride;
 +
 +                    for (fa = 0; fa < nfa; fa++)
 +                    {
 +                        f[a][XX] += out[fa].f[i];
 +                        f[a][YY] += out[fa].f[i+1];
 +                        f[a][ZZ] += out[fa].f[i+2];
 +                    }
 +                }
 +            }
 +            break;
 +        case nbatX4:
 +            if (nfa == 1)
 +            {
 +                fnb = out[0].f;
 +
 +                for (a = a0; a < a1; a++)
 +                {
 +                    i = X4_IND_A(cell[a]);
 +
 +                    f[a][XX] += fnb[i+XX*PACK_X4];
 +                    f[a][YY] += fnb[i+YY*PACK_X4];
 +                    f[a][ZZ] += fnb[i+ZZ*PACK_X4];
 +                }
 +            }
 +            else
 +            {
 +                for (a = a0; a < a1; a++)
 +                {
 +                    i = X4_IND_A(cell[a]);
 +
 +                    for (fa = 0; fa < nfa; fa++)
 +                    {
 +                        f[a][XX] += out[fa].f[i+XX*PACK_X4];
 +                        f[a][YY] += out[fa].f[i+YY*PACK_X4];
 +                        f[a][ZZ] += out[fa].f[i+ZZ*PACK_X4];
 +                    }
 +                }
 +            }
 +            break;
 +        case nbatX8:
 +            if (nfa == 1)
 +            {
 +                fnb = out[0].f;
 +
 +                for (a = a0; a < a1; a++)
 +                {
 +                    i = X8_IND_A(cell[a]);
 +
 +                    f[a][XX] += fnb[i+XX*PACK_X8];
 +                    f[a][YY] += fnb[i+YY*PACK_X8];
 +                    f[a][ZZ] += fnb[i+ZZ*PACK_X8];
 +                }
 +            }
 +            else
 +            {
 +                for (a = a0; a < a1; a++)
 +                {
 +                    i = X8_IND_A(cell[a]);
 +
 +                    for (fa = 0; fa < nfa; fa++)
 +                    {
 +                        f[a][XX] += out[fa].f[i+XX*PACK_X8];
 +                        f[a][YY] += out[fa].f[i+YY*PACK_X8];
 +                        f[a][ZZ] += out[fa].f[i+ZZ*PACK_X8];
 +                    }
 +                }
 +            }
 +            break;
 +        default:
 +            gmx_incons("Unsupported nbnxn_atomdata_t format");
 +    }
 +}
 +
 +/* Add the force array(s) from nbnxn_atomdata_t to f */
 +void nbnxn_atomdata_add_nbat_f_to_f(const nbnxn_search_t    nbs,
 +                                    int                     locality,
 +                                    const nbnxn_atomdata_t *nbat,
 +                                    rvec                   *f)
 +{
 +    int a0 = 0, na = 0;
 +    int nth, th;
 +
 +    nbs_cycle_start(&nbs->cc[enbsCCreducef]);
 +
 +    switch (locality)
 +    {
 +        case eatAll:
 +            a0 = 0;
 +            na = nbs->natoms_nonlocal;
 +            break;
 +        case eatLocal:
 +            a0 = 0;
 +            na = nbs->natoms_local;
 +            break;
 +        case eatNonlocal:
 +            a0 = nbs->natoms_local;
 +            na = nbs->natoms_nonlocal - nbs->natoms_local;
 +            break;
 +    }
 +
 +    nth = gmx_omp_nthreads_get(emntNonbonded);
 +
 +    if (nbat->nout > 1)
 +    {
 +        if (locality != eatAll)
 +        {
 +            gmx_incons("add_f_to_f called with nout>1 and locality!=eatAll");
 +        }
 +
 +        /* Reduce the force thread output buffers into buffer 0, before adding
 +         * them to the, differently ordered, "real" force buffer.
 +         */
 +#pragma omp parallel for num_threads(nth) schedule(static)
 +        for (th = 0; th < nth; th++)
 +        {
 +            const nbnxn_buffer_flags_t *flags;
 +            int   b0, b1, b;
 +            int   i0, i1;
 +            int   nfptr;
 +            real *fptr[NBNXN_BUFFERFLAG_MAX_THREADS];
 +            int   out;
 +
 +            flags = &nbat->buffer_flags;
 +
 +            /* Calculate the cell-block range for our thread */
 +            b0 = (flags->nflag* th   )/nth;
 +            b1 = (flags->nflag*(th+1))/nth;
 +
 +            for (b = b0; b < b1; b++)
 +            {
 +                i0 =  b   *NBNXN_BUFFERFLAG_SIZE*nbat->fstride;
 +                i1 = (b+1)*NBNXN_BUFFERFLAG_SIZE*nbat->fstride;
 +
 +                nfptr = 0;
 +                for (out = 1; out < nbat->nout; out++)
 +                {
 +                    if (flags->flag[b] & (1U<<out))
 +                    {
 +                        fptr[nfptr++] = nbat->out[out].f;
 +                    }
 +                }
 +                if (nfptr > 0)
 +                {
 +#ifdef GMX_NBNXN_SIMD
 +                    nbnxn_atomdata_reduce_reals_simd
 +#else
 +                    nbnxn_atomdata_reduce_reals
 +#endif
 +                        (nbat->out[0].f,
 +                        flags->flag[b] & (1U<<0),
 +                        fptr, nfptr,
 +                        i0, i1);
 +                }
 +                else if (!(flags->flag[b] & (1U<<0)))
 +                {
 +                    nbnxn_atomdata_clear_reals(nbat->out[0].f,
 +                                               i0, i1);
 +                }
 +            }
 +        }
 +    }
 +
 +#pragma omp parallel for num_threads(nth) schedule(static)
 +    for (th = 0; th < nth; th++)
 +    {
 +        nbnxn_atomdata_add_nbat_f_to_f_part(nbs, nbat,
 +                                            nbat->out,
 +                                            1,
 +                                            a0+((th+0)*na)/nth,
 +                                            a0+((th+1)*na)/nth,
 +                                            f);
 +    }
 +
 +    nbs_cycle_stop(&nbs->cc[enbsCCreducef]);
 +}
 +
 +/* Adds the shift forces from nbnxn_atomdata_t to fshift */
 +void nbnxn_atomdata_add_nbat_fshift_to_fshift(const nbnxn_atomdata_t *nbat,
 +                                              rvec                   *fshift)
 +{
 +    const nbnxn_atomdata_output_t *out;
 +    int  th;
 +    int  s;
 +    rvec sum;
 +
 +    out = nbat->out;
 +
 +    for (s = 0; s < SHIFTS; s++)
 +    {
 +        clear_rvec(sum);
 +        for (th = 0; th < nbat->nout; th++)
 +        {
 +            sum[XX] += out[th].fshift[s*DIM+XX];
 +            sum[YY] += out[th].fshift[s*DIM+YY];
 +            sum[ZZ] += out[th].fshift[s*DIM+ZZ];
 +        }
 +        rvec_inc(fshift[s], sum);
 +    }
 +}
index b42694bf9fd873772bf517c3e7fadd8b8e8be56e,0000000000000000000000000000000000000000..7e8a566e3c30b904fb9c2d8293ab0c11f188f633
mode 100644,000000..100644
--- /dev/null
@@@ -1,8 -1,0 +1,9 @@@
 +if(GMX_GPU)
 +    file(GLOB CUDA_NB_SOURCES *.cu)
 +    CUDA_INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
 +    CUDA_ADD_LIBRARY(nbnxn_cuda STATIC ${CUDA_NB_SOURCES}
 +            OPTIONS
 +            RELWITHDEBINFO -g
 +            DEBUG -g -D_DEBUG_=1)
++    target_link_libraries(nbnxn_cuda cuda_tools)
 +endif()
index 4acca9c3b1de7199554bef079d7e87a90e66b4f3,0000000000000000000000000000000000000000..6bf1aeea23679cf8614c66c86e4bda4ad98a12e3
mode 100644,000000..100644
--- /dev/null
@@@ -1,676 -1,0 +1,707 @@@
- /* Generate all combinations of kernels through multiple inclusion:
-    F, F + E, F + prune, F + E + prune. */
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +
 +#include <stdlib.h>
 +#include <assert.h>
 +
 +#if defined(_MSVC)
 +#include <limits>
 +#endif
 +
 +#include <cuda.h>
 +
 +#include "types/simple.h" 
 +#include "types/nbnxn_pairlist.h"
 +#include "types/nb_verlet.h"
 +#include "types/ishift.h"
 +#include "types/force_flags.h"
 +#include "../nbnxn_consts.h"
 +
 +#ifdef TMPI_ATOMICS
 +#include "thread_mpi/atomic.h"
 +#endif
 +
 +#include "nbnxn_cuda_types.h"
 +#include "../../gmxlib/cuda_tools/cudautils.cuh"
 +#include "nbnxn_cuda.h"
 +#include "nbnxn_cuda_data_mgmt.h"
 +
 +
 +/*! Texture reference for nonbonded parameters; bound to cu_nbparam_t.nbfp*/
 +texture<float, 1, cudaReadModeElementType> tex_nbfp;
 +
 +/*! Texture reference for Ewald coulomb force table; bound to cu_nbparam_t.coulomb_tab */
 +texture<float, 1, cudaReadModeElementType> tex_coulomb_tab;
 +
 +/* Convenience defines */
 +#define NCL_PER_SUPERCL         (NBNXN_GPU_NCLUSTER_PER_SUPERCLUSTER)
 +#define CL_SIZE                 (NBNXN_GPU_CLUSTER_SIZE)
 +
 +/***** The kernels come here *****/
 +#include "nbnxn_cuda_kernel_utils.cuh"
 +
-         gmx_fatal(FARGS, "Watch out system too large to simulate!\n"
++/* Top-level kernel generation: will generate through multiple inclusion the
++ * following flavors for all kernels:
++ * - force-only output;
++ * - force and energy output;
++ * - force-only with pair list pruning;
++ * - force and energy output with pair list pruning.
++ */
 +/** Force only **/
 +#include "nbnxn_cuda_kernels.cuh"
 +/** Force & energy **/
 +#define CALC_ENERGIES
 +#include "nbnxn_cuda_kernels.cuh"
 +#undef CALC_ENERGIES
 +
 +/*** Pair-list pruning kernels ***/
 +/** Force only **/
 +#define PRUNE_NBL
 +#include "nbnxn_cuda_kernels.cuh"
 +/** Force & energy **/
 +#define CALC_ENERGIES
 +#include "nbnxn_cuda_kernels.cuh"
 +#undef CALC_ENERGIES
 +#undef PRUNE_NBL
 +
 +/*! Nonbonded kernel function pointer type */
 +typedef void (*nbnxn_cu_kfunc_ptr_t)(const cu_atomdata_t,
 +                                     const cu_nbparam_t,
 +                                     const cu_plist_t,
 +                                     bool);
 +
 +/*********************************/
 +
 +/* XXX always/never run the energy/pruning kernels -- only for benchmarking purposes */
 +static bool always_ener  = (getenv("GMX_GPU_ALWAYS_ENER") != NULL);
 +static bool never_ener   = (getenv("GMX_GPU_NEVER_ENER") != NULL);
 +static bool always_prune = (getenv("GMX_GPU_ALWAYS_PRUNE") != NULL);
 +
 +
 +/* Bit-pattern used for polling-based GPU synchronization. It is used as a float
 + * and corresponds to having the exponent set to the maximum (127 -- single
 + * precision) and the mantissa to 0.
 + */
 +static unsigned int poll_wait_pattern = (0x7FU << 23);
 +
 +/*! Returns the number of blocks to be used for the nonbonded GPU kernel. */
 +static inline int calc_nb_kernel_nblock(int nwork_units, cuda_dev_info_t *dinfo)
 +{
 +    int max_grid_x_size;
 +
 +    assert(dinfo);
 +
 +    max_grid_x_size = dinfo->prop.maxGridSize[0];
 +
 +    /* do we exceed the grid x dimension limit? */
 +    if (nwork_units > max_grid_x_size)
 +    {
- /* Default kernels */
++        gmx_fatal(FARGS, "Watch out, the input system is too large to simulate!\n"
 +                  "The number of nonbonded work units (=number of super-clusters) exceeds the"
 +                  "maximum grid size in x dimension (%d > %d)!", nwork_units, max_grid_x_size);
 +    }
 +
 +    return nwork_units;
 +}
 +
 +
 +/* Constant arrays listing all kernel function pointers and enabling selection
 +   of a kernel in an elegant manner. */
 +
 +static const int nEnergyKernelTypes = 2; /* 0 - no energy, 1 - energy */
 +static const int nPruneKernelTypes  = 2; /* 0 - no prune, 1 - prune */
 +
-     { { k_nbnxn_ewald,              k_nbnxn_ewald_prune },
-       { k_nbnxn_ewald_ener,         k_nbnxn_ewald_ener_prune } },
-     { { k_nbnxn_ewald_twin,         k_nbnxn_ewald_twin_prune },
-       { k_nbnxn_ewald_twin_ener,    k_nbnxn_ewald_twin_ener_prune } },
-     { { k_nbnxn_rf,                 k_nbnxn_rf_prune },
-       { k_nbnxn_rf_ener,            k_nbnxn_rf_ener_prune } },
-     { { k_nbnxn_cutoff,             k_nbnxn_cutoff_prune },
-       { k_nbnxn_cutoff_ener,        k_nbnxn_cutoff_ener_prune } },
++/*! Pointers to the default kernels organized in a 3 dim array by:
++ *  electrostatics type, energy calculation on/off, and pruning on/off.
++ *
++ *  Note that the order of electrostatics (1st dimension) has to match the
++ *  order of corresponding enumerated types defined in nbnxn_cuda_types.h.
++ */
 +static const nbnxn_cu_kfunc_ptr_t
 +nb_default_kfunc_ptr[eelCuNR][nEnergyKernelTypes][nPruneKernelTypes] =
 +{
- /* Legacy kernels */
++    { { k_nbnxn_cutoff,                     k_nbnxn_cutoff_prune },
++      { k_nbnxn_cutoff_ener,                k_nbnxn_cutoff_ener_prune } },
++    { { k_nbnxn_rf,                         k_nbnxn_rf_prune },
++      { k_nbnxn_rf_ener,                    k_nbnxn_rf_ener_prune } },
++    { { k_nbnxn_ewald_tab,                  k_nbnxn_ewald_tab_prune },
++      { k_nbnxn_ewald_tab_ener,             k_nbnxn_ewald_tab_ener_prune } },
++    { { k_nbnxn_ewald_tab_twin,             k_nbnxn_ewald_tab_twin_prune },
++      { k_nbnxn_ewald_tab_twin_ener,        k_nbnxn_ewald_twin_ener_prune } },
++    { { k_nbnxn_ewald,                      k_nbnxn_ewald_prune },
++      { k_nbnxn_ewald_ener,                 k_nbnxn_ewald_ener_prune } },
++    { { k_nbnxn_ewald_twin,                 k_nbnxn_ewald_twin_prune },
++      { k_nbnxn_ewald_twin_ener,            k_nbnxn_ewald_twin_ener_prune } },
 +};
 +
-     { { k_nbnxn_ewald_legacy,           k_nbnxn_ewald_prune_legacy },
-       { k_nbnxn_ewald_ener_legacy,      k_nbnxn_ewald_ener_prune_legacy } },
-     { { k_nbnxn_ewald_twin_legacy,      k_nbnxn_ewald_twin_prune_legacy },
-       { k_nbnxn_ewald_twin_ener_legacy, k_nbnxn_ewald_twin_ener_prune_legacy } },
-     { { k_nbnxn_rf_legacy,              k_nbnxn_rf_prune_legacy },
-       { k_nbnxn_rf_ener_legacy,         k_nbnxn_rf_ener_prune_legacy } },
-     { { k_nbnxn_cutoff_legacy,          k_nbnxn_cutoff_prune_legacy },
-       { k_nbnxn_cutoff_ener_legacy,     k_nbnxn_cutoff_ener_prune_legacy } },
++/*! Pointers to the legacy kernels organized in a 3 dim array by:
++ *  electrostatics type, energy calculation on/off, and pruning on/off.
++ *
++ *  Note that the order of electrostatics (1st dimension) has to match the
++ *  order of corresponding enumerated types defined in nbnxn_cuda_types.h.
++ */
 +static const nbnxn_cu_kfunc_ptr_t
 +nb_legacy_kfunc_ptr[eelCuNR][nEnergyKernelTypes][nPruneKernelTypes] =
 +{
-                 /* Legacy kernel 16/48 kB Shared/L1 */
-                 stat = cudaFuncSetCacheConfig(nb_legacy_kfunc_ptr[i][j][k], cudaFuncCachePreferL1);
-                 CU_RET_ERR(stat, "cudaFuncSetCacheConfig failed");
++    { { k_nbnxn_cutoff_legacy,              k_nbnxn_cutoff_prune_legacy },
++      { k_nbnxn_cutoff_ener_legacy,         k_nbnxn_cutoff_ener_prune_legacy } },
++    { { k_nbnxn_rf_legacy,                  k_nbnxn_rf_prune_legacy },
++      { k_nbnxn_rf_ener_legacy,             k_nbnxn_rf_ener_prune_legacy } },
++    { { k_nbnxn_ewald_tab_legacy,           k_nbnxn_ewald_tab_prune_legacy },
++      { k_nbnxn_ewald_tab_ener_legacy,      k_nbnxn_ewald_tab_ener_prune_legacy } },
++    { { k_nbnxn_ewald_tab_twin_legacy,      k_nbnxn_ewald_tab_twin_prune_legacy },
++      { k_nbnxn_ewald_tab_twin_ener_legacy, k_nbnxn_ewald_tab_twin_ener_prune_legacy } },
 +};
 +
 +/*! Return a pointer to the kernel version to be executed at the current step. */
 +static inline nbnxn_cu_kfunc_ptr_t select_nbnxn_kernel(int kver, int eeltype,
 +                                                       bool bDoEne, bool bDoPrune)
 +{
 +    assert(kver < eNbnxnCuKNR);
 +    assert(eeltype < eelCuNR);
 +
 +    if (NBNXN_KVER_LEGACY(kver))
 +    {
++        /* no analytical Ewald with legacy kernels */
++        assert(eeltype <= eelCuEWALD_TAB_TWIN);
++
 +        return nb_legacy_kfunc_ptr[eeltype][bDoEne][bDoPrune];
 +    }
 +    else
 +    {
 +        return nb_default_kfunc_ptr[eeltype][bDoEne][bDoPrune];
 +    }
 +}
 +
 +/*! Calculates the amount of shared memory required for kernel version in use. */
 +static inline int calc_shmem_required(int kver)
 +{
 +    int shmem;
 +
 +    /* size of shmem (force-buffers/xq/atom type preloading) */
 +    if (NBNXN_KVER_LEGACY(kver))
 +    {
 +        /* i-atom x+q in shared memory */
 +        shmem =  NCL_PER_SUPERCL * CL_SIZE * sizeof(float4);
 +        /* force reduction buffers in shared memory */
 +        shmem += CL_SIZE * CL_SIZE * 3 * sizeof(float);
 +    }
 +    else
 +    {
 +        /* NOTE: with the default kernel on sm3.0 we need shmem only for pre-loading */
 +        /* i-atom x+q in shared memory */
 +        shmem  = NCL_PER_SUPERCL * CL_SIZE * sizeof(float4);
 +        /* cj in shared memory, for both warps separately */
 +        shmem += 2 * NBNXN_GPU_JGROUP_SIZE * sizeof(int);
 +#ifdef IATYPE_SHMEM
 +        /* i-atom types in shared memory */
 +        shmem += NCL_PER_SUPERCL * CL_SIZE * sizeof(int);
 +#endif
 +#if __CUDA_ARCH__ < 300
 +        /* force reduction buffers in shared memory */
 +        shmem += CL_SIZE * CL_SIZE * 3 * sizeof(float);
 +#endif
 +    }
 +
 +    return shmem;
 +}
 +
 +/*! As we execute nonbonded workload in separate streams, before launching 
 +   the kernel we need to make sure that he following operations have completed:
 +   - atomdata allocation and related H2D transfers (every nstlist step);
 +   - pair list H2D transfer (every nstlist step);
 +   - shift vector H2D transfer (every nstlist step);
 +   - force (+shift force and energy) output clearing (every step).
 +
 +   These operations are issued in the local stream at the beginning of the step
 +   and therefore always complete before the local kernel launch. The non-local
 +   kernel is launched after the local on the same device/context, so this is
 +   inherently scheduled after the operations in the local stream (including the
 +   above "misc_ops").
 +   However, for the sake of having a future-proof implementation, we use the
 +   misc_ops_done event to record the point in time when the above  operations
 +   are finished and synchronize with this event in the non-local stream.
 +*/
 +void nbnxn_cuda_launch_kernel(nbnxn_cuda_ptr_t cu_nb,
 +                              const nbnxn_atomdata_t *nbatom,
 +                              int flags,
 +                              int iloc)
 +{
 +    cudaError_t stat;
 +    int adat_begin, adat_len;  /* local/nonlocal offset and length used for xq and f */
 +    /* CUDA kernel launch-related stuff */
 +    int  shmem, nblock;
 +    dim3 dim_block, dim_grid;
 +    nbnxn_cu_kfunc_ptr_t nb_kernel = NULL; /* fn pointer to the nonbonded kernel */
 +
 +    cu_atomdata_t   *adat   = cu_nb->atdat;
 +    cu_nbparam_t    *nbp    = cu_nb->nbparam;
 +    cu_plist_t      *plist  = cu_nb->plist[iloc];
 +    cu_timers_t     *t      = cu_nb->timers;
 +    cudaStream_t    stream  = cu_nb->stream[iloc];
 +
 +    bool bCalcEner   = flags & GMX_FORCE_VIRIAL;
 +    bool bCalcFshift = flags & GMX_FORCE_VIRIAL;
 +    bool bDoTime     = cu_nb->bDoTime;
 +
 +    /* turn energy calculation always on/off (for debugging/testing only) */
 +    bCalcEner = (bCalcEner || always_ener) && !never_ener;
 +
 +    /* don't launch the kernel if there is no work to do */
 +    if (plist->nsci == 0)
 +    {
 +        return;
 +    }
 +
 +    /* calculate the atom data index range based on locality */
 +    if (LOCAL_I(iloc))
 +    {
 +        adat_begin  = 0;
 +        adat_len    = adat->natoms_local;
 +    }
 +    else
 +    {
 +        adat_begin  = adat->natoms_local;
 +        adat_len    = adat->natoms - adat->natoms_local;
 +    }
 +
 +    /* When we get here all misc operations issues in the local stream are done,
 +       so we record that in the local stream and wait for it in the nonlocal one. */
 +    if (cu_nb->bUseTwoStreams)
 +    {
 +        if (iloc == eintLocal)
 +        {
 +            stat = cudaEventRecord(cu_nb->misc_ops_done, stream);
 +            CU_RET_ERR(stat, "cudaEventRecord on misc_ops_done failed");
 +        }
 +        else
 +        {
 +            stat = cudaStreamWaitEvent(stream, cu_nb->misc_ops_done, 0);
 +            CU_RET_ERR(stat, "cudaStreamWaitEvent on misc_ops_done failed");
 +        }
 +    }
 +
 +    /* beginning of timed HtoD section */
 +    if (bDoTime)
 +    {
 +        stat = cudaEventRecord(t->start_nb_h2d[iloc], stream);
 +        CU_RET_ERR(stat, "cudaEventRecord failed");
 +    }
 +
 +    /* HtoD x, q */
 +    cu_copy_H2D_async(adat->xq + adat_begin, nbatom->x + adat_begin * 4,
 +                      adat_len * sizeof(*adat->xq), stream); 
 +
 +    if (bDoTime)
 +    {
 +        stat = cudaEventRecord(t->stop_nb_h2d[iloc], stream);
 +        CU_RET_ERR(stat, "cudaEventRecord failed");
 +    }
 +
 +    /* beginning of timed nonbonded calculation section */
 +    if (bDoTime)
 +    {
 +        stat = cudaEventRecord(t->start_nb_k[iloc], stream);
 +        CU_RET_ERR(stat, "cudaEventRecord failed");
 +    }
 +
 +    /* get the pointer to the kernel flavor we need to use */
 +    nb_kernel = select_nbnxn_kernel(cu_nb->kernel_ver, nbp->eeltype, bCalcEner,
 +                                    plist->bDoPrune || always_prune);
 +
 +    /* kernel launch config */
 +    nblock    = calc_nb_kernel_nblock(plist->nsci, cu_nb->dev_info);
 +    dim_block = dim3(CL_SIZE, CL_SIZE, 1);
 +    dim_grid  = dim3(nblock, 1, 1);
 +    shmem     = calc_shmem_required(cu_nb->kernel_ver);
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "GPU launch configuration:\n\tThread block: %dx%dx%d\n\t"
 +                "Grid: %dx%d\n\t#Super-clusters/clusters: %d/%d (%d)\n",
 +                dim_block.x, dim_block.y, dim_block.z,
 +                dim_grid.x, dim_grid.y, plist->nsci*NCL_PER_SUPERCL,
 +                NCL_PER_SUPERCL, plist->na_c);
 +    }
 +
 +    nb_kernel<<<dim_grid, dim_block, shmem, stream>>>(*adat, *nbp, *plist, bCalcFshift);
 +    CU_LAUNCH_ERR("k_calc_nb");
 +
 +    if (bDoTime)
 +    {
 +        stat = cudaEventRecord(t->stop_nb_k[iloc], stream);
 +        CU_RET_ERR(stat, "cudaEventRecord failed");
 +    }
 +}
 +
 +void nbnxn_cuda_launch_cpyback(nbnxn_cuda_ptr_t cu_nb,
 +                               const nbnxn_atomdata_t *nbatom,
 +                               int flags,
 +                               int aloc)
 +{
 +    cudaError_t stat;
 +    int adat_begin, adat_len, adat_end;  /* local/nonlocal offset and length used for xq and f */
 +    int iloc = -1;
 +
 +    /* determine interaction locality from atom locality */
 +    if (LOCAL_A(aloc))
 +    {
 +        iloc = eintLocal;
 +    }
 +    else if (NONLOCAL_A(aloc))
 +    {
 +        iloc = eintNonlocal;
 +    }
 +    else
 +    {
 +        char stmp[STRLEN];
 +        sprintf(stmp, "Invalid atom locality passed (%d); valid here is only "
 +                "local (%d) or nonlocal (%d)", aloc, eatLocal, eatNonlocal);
 +        gmx_incons(stmp);
 +    }
 +
 +    cu_atomdata_t   *adat   = cu_nb->atdat;
 +    cu_timers_t     *t      = cu_nb->timers;
 +    bool            bDoTime = cu_nb->bDoTime;
 +    cudaStream_t    stream  = cu_nb->stream[iloc];
 +
 +    bool bCalcEner   = flags & GMX_FORCE_VIRIAL;
 +    bool bCalcFshift = flags & GMX_FORCE_VIRIAL;
 +
 +    /* don't launch copy-back if there was no work to do */
 +    if (cu_nb->plist[iloc]->nsci == 0)
 +    {
 +        return;
 +    }
 +
 +    /* calculate the atom data index range based on locality */
 +    if (LOCAL_A(aloc))
 +    {
 +        adat_begin  = 0;
 +        adat_len    = adat->natoms_local;
 +        adat_end    = cu_nb->atdat->natoms_local;
 +    }
 +    else
 +    {
 +        adat_begin  = adat->natoms_local;
 +        adat_len    = adat->natoms - adat->natoms_local;
 +        adat_end    = cu_nb->atdat->natoms;
 +    }
 +
 +    /* beginning of timed D2H section */
 +    if (bDoTime)
 +    {
 +        stat = cudaEventRecord(t->start_nb_d2h[iloc], stream);
 +        CU_RET_ERR(stat, "cudaEventRecord failed");
 +    }
 +
 +    if (!cu_nb->bUseStreamSync)
 +    {
 +        /* For safety reasons set a few (5%) forces to NaN. This way even if the
 +           polling "hack" fails with some future NVIDIA driver we'll get a crash. */
 +        for (int i = adat_begin; i < 3*adat_end + 2; i += adat_len/20)
 +        {
 +#ifdef NAN
 +            nbatom->out[0].f[i] = NAN;
 +#else
 +#  ifdef _MSVC
 +            if (numeric_limits<float>::has_quiet_NaN)
 +            {
 +                nbatom->out[0].f[i] = numeric_limits<float>::quiet_NaN();
 +            }
 +            else
 +#  endif
 +            {
 +                nbatom->out[0].f[i] = GMX_REAL_MAX;
 +            }
 +#endif
 +        }
 +
 +        /* Set the last four bytes of the force array to a bit pattern
 +           which can't be the result of the force calculation:
 +           max exponent (127) and zero mantissa. */
 +        *(unsigned int*)&nbatom->out[0].f[adat_end*3 - 1] = poll_wait_pattern;
 +    }
 +
 +    /* With DD the local D2H transfer can only start after the non-local 
 +       has been launched. */
 +    if (iloc == eintLocal && cu_nb->bUseTwoStreams)
 +    {
 +        stat = cudaStreamWaitEvent(stream, cu_nb->nonlocal_done, 0);
 +        CU_RET_ERR(stat, "cudaStreamWaitEvent on nonlocal_done failed");
 +    }
 +
 +    /* DtoH f */
 +    cu_copy_D2H_async(nbatom->out[0].f + adat_begin * 3, adat->f + adat_begin, 
 +                      (adat_len)*sizeof(*adat->f), stream);
 +
 +    /* After the non-local D2H is launched the nonlocal_done event can be
 +       recorded which signals that the local D2H can proceed. This event is not
 +       placed after the non-local kernel because we first need the non-local
 +       data back first. */
 +    if (iloc == eintNonlocal)
 +    {
 +        stat = cudaEventRecord(cu_nb->nonlocal_done, stream);
 +        CU_RET_ERR(stat, "cudaEventRecord on nonlocal_done failed");
 +    }
 +
 +    /* only transfer energies in the local stream */
 +    if (LOCAL_I(iloc))
 +    {
 +        /* DtoH fshift */
 +        if (bCalcFshift)
 +        {
 +            cu_copy_D2H_async(cu_nb->nbst.fshift, adat->fshift,
 +                              SHIFTS * sizeof(*cu_nb->nbst.fshift), stream);
 +        }
 +
 +        /* DtoH energies */
 +        if (bCalcEner)
 +        {
 +            cu_copy_D2H_async(cu_nb->nbst.e_lj, adat->e_lj,
 +                              sizeof(*cu_nb->nbst.e_lj), stream);
 +            cu_copy_D2H_async(cu_nb->nbst.e_el, adat->e_el,
 +                              sizeof(*cu_nb->nbst.e_el), stream);
 +        }
 +    }
 +
 +    if (bDoTime)
 +    {
 +        stat = cudaEventRecord(t->stop_nb_d2h[iloc], stream);
 +        CU_RET_ERR(stat, "cudaEventRecord failed");
 +    }
 +}
 +
 +/* Atomic compare-exchange operation on unsigned values. It is used in
 + * polling wait for the GPU.
 + */
 +static inline bool atomic_cas(volatile unsigned int *ptr,
 +                              unsigned int oldval,
 +                              unsigned int newval)
 +{
 +    assert(ptr);
 +
 +#ifdef TMPI_ATOMICS
 +    return tMPI_Atomic_cas((tMPI_Atomic_t *)ptr, oldval, newval);
 +#else
 +    gmx_incons("Atomic operations not available, atomic_cas() should not have been called!");
 +    return true;
 +#endif
 +}
 +
 +void nbnxn_cuda_wait_gpu(nbnxn_cuda_ptr_t cu_nb,
 +                         const nbnxn_atomdata_t *nbatom,
 +                         int flags, int aloc,
 +                         real *e_lj, real *e_el, rvec *fshift)
 +{
 +    /* NOTE:  only implemented for single-precision at this time */
 +    cudaError_t stat;
 +    int i, adat_end, iloc = -1;
 +    volatile unsigned int *poll_word;
 +
 +    /* determine interaction locality from atom locality */
 +    if (LOCAL_A(aloc))
 +    {
 +        iloc = eintLocal;
 +    }
 +    else if (NONLOCAL_A(aloc))
 +    {
 +        iloc = eintNonlocal;
 +    }
 +    else
 +    {
 +        char stmp[STRLEN];
 +        sprintf(stmp, "Invalid atom locality passed (%d); valid here is only "
 +                "local (%d) or nonlocal (%d)", aloc, eatLocal, eatNonlocal);
 +        gmx_incons(stmp);
 +    }
 +
 +    cu_plist_t      *plist   = cu_nb->plist[iloc];
 +    cu_timers_t     *timers  = cu_nb->timers;
 +    wallclock_gpu_t *timings = cu_nb->timings;
 +    nb_staging      nbst     = cu_nb->nbst;
 +
 +    bool    bCalcEner   = flags & GMX_FORCE_VIRIAL;
 +    bool    bCalcFshift = flags & GMX_FORCE_VIRIAL;
 +
 +    /* turn energy calculation always on/off (for debugging/testing only) */
 +    bCalcEner = (bCalcEner || always_ener) && !never_ener; 
 +
 +    /* don't launch wait/update timers & counters if there was no work to do
 +
 +       NOTE: if timing with multiple GPUs (streams) becomes possible, the
 +       counters could end up being inconsistent due to not being incremented
 +       on some of the nodes! */
 +    if (cu_nb->plist[iloc]->nsci == 0)
 +    {
 +        return;
 +    }
 +
 +    /* calculate the atom data index range based on locality */
 +    if (LOCAL_A(aloc))
 +    {
 +        adat_end = cu_nb->atdat->natoms_local;
 +    }
 +    else
 +    {
 +        adat_end = cu_nb->atdat->natoms;
 +    }
 +
 +    if (cu_nb->bUseStreamSync)
 +    {
 +        stat = cudaStreamSynchronize(cu_nb->stream[iloc]);
 +        CU_RET_ERR(stat, "cudaStreamSynchronize failed in cu_blockwait_nb");
 +    }
 +    else 
 +    {
 +        /* Busy-wait until we get the signal pattern set in last byte
 +         * of the l/nl float vector. This pattern corresponds to a floating
 +         * point number which can't be the result of the force calculation
 +         * (maximum, 127 exponent and 0 mantissa).
 +         * The polling uses atomic compare-exchange.
 +         */
 +        poll_word = (volatile unsigned int*)&nbatom->out[0].f[adat_end*3 - 1];
 +        while (atomic_cas(poll_word, poll_wait_pattern, poll_wait_pattern)) {}
 +    }
 +
 +    /* timing data accumulation */
 +    if (cu_nb->bDoTime)
 +    {
 +        /* only increase counter once (at local F wait) */
 +        if (LOCAL_I(iloc))
 +        {
 +            timings->nb_c++;
 +            timings->ktime[plist->bDoPrune ? 1 : 0][bCalcEner ? 1 : 0].c += 1;
 +        }
 +
 +        /* kernel timings */
 +        timings->ktime[plist->bDoPrune ? 1 : 0][bCalcEner ? 1 : 0].t +=
 +            cu_event_elapsed(timers->start_nb_k[iloc], timers->stop_nb_k[iloc]);
 +
 +        /* X/q H2D and F D2H timings */
 +        timings->nb_h2d_t += cu_event_elapsed(timers->start_nb_h2d[iloc],
 +                                                 timers->stop_nb_h2d[iloc]);
 +        timings->nb_d2h_t += cu_event_elapsed(timers->start_nb_d2h[iloc],
 +                                                 timers->stop_nb_d2h[iloc]);
 +
 +        /* only count atdat and pair-list H2D at pair-search step */
 +        if (plist->bDoPrune)
 +        {
 +            /* atdat transfer timing (add only once, at local F wait) */
 +            if (LOCAL_A(aloc))
 +            {
 +                timings->pl_h2d_c++;
 +                timings->pl_h2d_t += cu_event_elapsed(timers->start_atdat,
 +                                                         timers->stop_atdat);
 +            }
 +
 +            timings->pl_h2d_t += cu_event_elapsed(timers->start_pl_h2d[iloc],
 +                                                     timers->stop_pl_h2d[iloc]);
 +        }
 +    }
 +
 +    /* add up energies and shift forces (only once at local F wait) */
 +    if (LOCAL_I(iloc))
 +    {
 +        if (bCalcEner)
 +        {
 +            *e_lj += *nbst.e_lj;
 +            *e_el += *nbst.e_el;
 +        }
 +
 +        if (bCalcFshift)
 +        {
 +            for (i = 0; i < SHIFTS; i++)
 +            {
 +                fshift[i][0] += nbst.fshift[i].x;
 +                fshift[i][1] += nbst.fshift[i].y;
 +                fshift[i][2] += nbst.fshift[i].z;
 +            }
 +        }
 +    }
 +
 +    /* turn off pruning (doesn't matter if this is pair-search step or not) */
 +    plist->bDoPrune = false;
 +}
 +
 +/*! Return the reference to the nbfp texture. */
 +const struct texture<float, 1, cudaReadModeElementType>& nbnxn_cuda_get_nbfp_texref()
 +{
 +    return tex_nbfp;
 +}
 +
 +/*! Return the reference to the coulomb_tab. */
 +const struct texture<float, 1, cudaReadModeElementType>& nbnxn_cuda_get_coulomb_tab_texref()
 +{
 +    return tex_coulomb_tab;
 +}
 +
 +/*! Set up the cache configuration for the non-bonded kernels,
 + */
 +void nbnxn_cuda_set_cacheconfig(cuda_dev_info_t *devinfo)
 +{
 +    cudaError_t stat;
 +
 +    for (int i = 0; i < eelCuNR; i++)
++    {
 +        for (int j = 0; j < nEnergyKernelTypes; j++)
++        {
 +            for (int k = 0; k < nPruneKernelTypes; k++)
 +            {
++                /* Legacy kernel 16/48 kB Shared/L1
++                 * No analytical Ewald!
++                 */
++                if (i != eelCuEWALD_ANA && i != eelCuEWALD_ANA_TWIN)
++                {
++                    stat = cudaFuncSetCacheConfig(nb_legacy_kfunc_ptr[i][j][k], cudaFuncCachePreferL1);
++                    CU_RET_ERR(stat, "cudaFuncSetCacheConfig failed");
++                }
 +
 +                if (devinfo->prop.major >= 3)
 +                {
 +                    /* Default kernel on sm 3.x 48/16 kB Shared/L1 */
 +                    stat = cudaFuncSetCacheConfig(nb_default_kfunc_ptr[i][j][k], cudaFuncCachePreferShared);
 +                }
 +                else
 +                {
 +                    /* On Fermi prefer L1 gives 2% higher performance */
 +                    /* Default kernel on sm_2.x 16/48 kB Shared/L1 */
 +                    stat = cudaFuncSetCacheConfig(nb_default_kfunc_ptr[i][j][k], cudaFuncCachePreferL1);
 +                }
 +                CU_RET_ERR(stat, "cudaFuncSetCacheConfig failed");
 +            }
++        }
++    }
 +}
index 3475ad96da5f9a648271373bbd62aaf5952d4996,0000000000000000000000000000000000000000..19c0b2d4f4d865786162b4f8f049ba68fa344d33
mode 100644,000000..100644
--- /dev/null
@@@ -1,923 -1,0 +1,961 @@@
-                          const nonbonded_verlet_t *nbv)
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <assert.h>
 +
 +#include <cuda.h>
 +
 +#include "gmx_fatal.h"
 +#include "smalloc.h"
 +#include "tables.h"
 +#include "typedefs.h"
 +#include "types/nb_verlet.h"
 +#include "types/interaction_const.h"
 +#include "types/force_flags.h"
 +#include "../nbnxn_consts.h"
 +
 +#include "nbnxn_cuda_types.h"
 +#include "../../gmxlib/cuda_tools/cudautils.cuh"
 +#include "nbnxn_cuda_data_mgmt.h"
 +#include "pmalloc_cuda.h"
 +#include "gpu_utils.h"
 +
 +static bool bUseCudaEventBlockingSync = false; /* makes the CPU thread block */
 +
 +/* This is a heuristically determined parameter for the Fermi architecture for
 + * the minimum size of ci lists by multiplying this constant with the # of
 + * multiprocessors on the current device.
 + */
 +static unsigned int gpu_min_ci_balanced_factor = 40;
 +
 +/* Functions from nbnxn_cuda.cu */
 +extern void nbnxn_cuda_set_cacheconfig(cuda_dev_info_t *devinfo);
 +extern const struct texture<float, 1, cudaReadModeElementType>& nbnxn_cuda_get_nbfp_texref();
 +extern const struct texture<float, 1, cudaReadModeElementType>& nbnxn_cuda_get_coulomb_tab_texref();
 +
 +/* We should actually be using md_print_warn in md_logging.c,
 + * but we can't include mpi.h in CUDA code.
 + */
 +static void md_print_warn(FILE       *fplog,
 +                          const char *fmt, ...)
 +{
 +    va_list ap;
 +
 +    if (fplog != NULL)
 +    {
 +        /* We should only print to stderr on the master node,
 +         * in most cases fplog is only set on the master node, so this works.
 +         */
 +        va_start(ap, fmt);
 +        fprintf(stderr, "\n");
 +        vfprintf(stderr, fmt, ap);
 +        fprintf(stderr, "\n");
 +        va_end(ap);
 +
 +        va_start(ap, fmt);
 +        fprintf(fplog, "\n");
 +        vfprintf(fplog, fmt, ap);
 +        fprintf(fplog, "\n");
 +        va_end(ap);
 +    }
 +}
 +
 +
 +/* Fw. decl. */
 +static void nbnxn_cuda_clear_e_fshift(nbnxn_cuda_ptr_t cu_nb);
 +
 +
 +/*! Tabulates the Ewald Coulomb force and initializes the size/scale
 +    and the table GPU array. If called with an already allocated table,
 +    it just re-uploads the table.
 + */
 +static void init_ewald_coulomb_force_table(cu_nbparam_t *nbp)
 +{
 +    float       *ftmp, *coul_tab;
 +    int         tabsize;
 +    double      tabscale;
 +    cudaError_t stat;
 +
 +    tabsize     = GPU_EWALD_COULOMB_FORCE_TABLE_SIZE;
 +    /* Subtract 2 iso 1 to avoid access out of range due to rounding */
 +    tabscale    = (tabsize - 2) / sqrt(nbp->rcoulomb_sq);
 +
 +    pmalloc((void**)&ftmp, tabsize*sizeof(*ftmp));
 +
 +    table_spline3_fill_ewald_lr(ftmp, NULL, NULL, tabsize,
 +                                1/tabscale, nbp->ewald_beta);
 +
 +    /* If the table pointer == NULL the table is generated the first time =>
 +       the array pointer will be saved to nbparam and the texture is bound.
 +     */
 +    coul_tab = nbp->coulomb_tab;
 +    if (coul_tab == NULL)
 +    {
 +        stat = cudaMalloc((void **)&coul_tab, tabsize*sizeof(*coul_tab));
 +        CU_RET_ERR(stat, "cudaMalloc failed on coul_tab");
 +
 +        nbp->coulomb_tab = coul_tab;
 +
 +        cudaChannelFormatDesc cd   = cudaCreateChannelDesc<float>();
 +        stat = cudaBindTexture(NULL, &nbnxn_cuda_get_coulomb_tab_texref(),
 +                               coul_tab, &cd, tabsize*sizeof(*coul_tab));
 +        CU_RET_ERR(stat, "cudaBindTexture on coul_tab failed");
 +    }
 +
 +    cu_copy_H2D(coul_tab, ftmp, tabsize*sizeof(*coul_tab));
 +
 +    nbp->coulomb_tab_size     = tabsize;
 +    nbp->coulomb_tab_scale    = tabscale;
 +
 +    pfree(ftmp);
 +}
 +
 +
 +/*! Initializes the atomdata structure first time, it only gets filled at
 +    pair-search. */
 +static void init_atomdata_first(cu_atomdata_t *ad, int ntypes)
 +{
 +    cudaError_t stat;
 +
 +    ad->ntypes  = ntypes;
 +    stat = cudaMalloc((void**)&ad->shift_vec, SHIFTS*sizeof(*ad->shift_vec));
 +    CU_RET_ERR(stat, "cudaMalloc failed on ad->shift_vec");
 +    ad->bShiftVecUploaded = false;
 +
 +    stat = cudaMalloc((void**)&ad->fshift, SHIFTS*sizeof(*ad->fshift));
 +    CU_RET_ERR(stat, "cudaMalloc failed on ad->fshift");
 +
 +    stat = cudaMalloc((void**)&ad->e_lj, sizeof(*ad->e_lj));
 +    CU_RET_ERR(stat, "cudaMalloc failed on ad->e_lj");
 +    stat = cudaMalloc((void**)&ad->e_el, sizeof(*ad->e_el));
 +    CU_RET_ERR(stat, "cudaMalloc failed on ad->e_el");
 +
 +    /* initialize to NULL poiters to data that is not allocated here and will
 +       need reallocation in nbnxn_cuda_init_atomdata */
 +    ad->xq = NULL;
 +    ad->f  = NULL;
 +
 +    /* size -1 indicates that the respective array hasn't been initialized yet */
 +    ad->natoms = -1;
 +    ad->nalloc = -1;
 +}
 +
++/*! Selects the Ewald kernel type, analytical on SM 3.0 and later, tabulated on
++    earlier GPUs, single or twin cut-off. */
++static int pick_ewald_kernel_type(bool                   bTwinCut,
++                                  const cuda_dev_info_t *dev_info)
++{
++    bool bUseAnalyticalEwald, bForceAnalyticalEwald, bForceTabulatedEwald;
++    int  kernel_type;
++
++    /* Benchmarking/development environment variables to force the use of
++       analytical or tabulated Ewald kernel. */
++    bForceAnalyticalEwald = (getenv("GMX_CUDA_NB_ANA_EWALD") != NULL);
++    bForceTabulatedEwald  = (getenv("GMX_CUDA_NB_TAB_EWALD") != NULL);
++
++    if (bForceAnalyticalEwald && bForceTabulatedEwald)
++    {
++        gmx_incons("Both analytical and tabulated Ewald CUDA non-bonded kernels "
++                   "requested through environment variables.");
++    }
++
++    /* By default, on SM 3.0 and later use analytical Ewald, on earlier tabulated. */
++    if ((dev_info->prop.major >= 3 || bForceAnalyticalEwald) && !bForceTabulatedEwald)
++    {
++        bUseAnalyticalEwald = true;
++
++        if (debug)
++        {
++            fprintf(debug, "Using analytical Ewald CUDA kernels\n");
++        }
++    }
++    else
++    {
++        bUseAnalyticalEwald = false;
++
++        if (debug)
++        {
++            fprintf(debug, "Using tabulated Ewald CUDA kernels\n");
++        }
++    }
++
++    /* Use twin cut-off kernels if requested by bTwinCut or the env. var.
++       forces it (use it for debugging/benchmarking only). */
++    if (!bTwinCut && (getenv("GMX_CUDA_NB_EWALD_TWINCUT") == NULL))
++    {
++        kernel_type = bUseAnalyticalEwald ? eelCuEWALD_ANA : eelCuEWALD_TAB;
++    }
++    else
++    {
++        kernel_type = bUseAnalyticalEwald ? eelCuEWALD_ANA_TWIN : eelCuEWALD_TAB_TWIN;
++    }
++
++    return kernel_type;
++}
++
++
 +/*! Initializes the nonbonded parameter data structure. */
 +static void init_nbparam(cu_nbparam_t *nbp,
 +                         const interaction_const_t *ic,
-         /* Initially rcoulomb == rvdw, so it's surely not twin cut-off, unless
-            forced by the env. var. (used only for benchmarking). */
-         if (getenv("GMX_CUDA_NB_EWALD_TWINCUT") == NULL)
-         {
-             nbp->eeltype = eelCuEWALD;
-         }
-         else
-         {
-             nbp->eeltype = eelCuEWALD_TWIN;
-         }
++                         const nonbonded_verlet_t *nbv,
++                         const cuda_dev_info_t *dev_info)
 +{
 +    cudaError_t stat;
 +    int         ntypes, nnbfp;
 +
 +    ntypes  = nbv->grp[0].nbat->ntype;
 +
 +    nbp->ewald_beta = ic->ewaldcoeff;
 +    nbp->sh_ewald   = ic->sh_ewald;
 +    nbp->epsfac     = ic->epsfac;
 +    nbp->two_k_rf   = 2.0 * ic->k_rf;
 +    nbp->c_rf       = ic->c_rf;
 +    nbp->rvdw_sq    = ic->rvdw * ic->rvdw;
 +    nbp->rcoulomb_sq= ic->rcoulomb * ic->rcoulomb;
 +    nbp->rlist_sq   = ic->rlist * ic->rlist;
 +    nbp->sh_invrc6  = ic->sh_invrc6;
 +
 +    if (ic->eeltype == eelCUT)
 +    {
 +        nbp->eeltype = eelCuCUT;
 +    }
 +    else if (EEL_RF(ic->eeltype))
 +    {
 +        nbp->eeltype = eelCuRF;
 +    }
 +    else if ((EEL_PME(ic->eeltype) || ic->eeltype==eelEWALD))
 +    {
-     if (nbp->eeltype == eelCuEWALD)
++        /* Initially rcoulomb == rvdw, so it's surely not twin cut-off. */
++        nbp->eeltype = pick_ewald_kernel_type(false, dev_info);
 +    }
 +    else
 +    {
 +        /* Shouldn't happen, as this is checked when choosing Verlet-scheme */
 +        gmx_incons("The requested electrostatics type is not implemented in the CUDA GPU accelerated kernels!");
 +    }
 +
 +    /* generate table for PME */
-         nbp->coulomb_tab = NULL;
++    nbp->coulomb_tab = NULL;
++    if (nbp->eeltype == eelCuEWALD_TAB || nbp->eeltype == eelCuEWALD_TAB_TWIN)
 +    {
-     /* When switching to/from twin cut-off, the electrostatics type needs updating.
-        (The env. var. that forces twin cut-off is for benchmarking only!) */
-     if (ic->rcoulomb == ic->rvdw &&
-         getenv("GMX_CUDA_NB_EWALD_TWINCUT") == NULL)
-     {
-         nbp->eeltype = eelCuEWALD;
-     }
-     else
-     {
-         nbp->eeltype = eelCuEWALD_TWIN;
-     }
 +        init_ewald_coulomb_force_table(nbp);
 +    }
 +
 +    nnbfp = 2*ntypes*ntypes;
 +    stat = cudaMalloc((void **)&nbp->nbfp, nnbfp*sizeof(*nbp->nbfp));
 +    CU_RET_ERR(stat, "cudaMalloc failed on nbp->nbfp");
 +    cu_copy_H2D(nbp->nbfp, nbv->grp[0].nbat->nbfp, nnbfp*sizeof(*nbp->nbfp));
 +
 +    cudaChannelFormatDesc cd   = cudaCreateChannelDesc<float>();
 +    stat = cudaBindTexture(NULL, &nbnxn_cuda_get_nbfp_texref(),
 +                           nbp->nbfp, &cd, nnbfp*sizeof(*nbp->nbfp));
 +    CU_RET_ERR(stat, "cudaBindTexture on nbfp failed");
 +}
 +
 +/*! Re-generate the GPU Ewald force table, resets rlist, and update the
 + *  electrostatic type switching to twin cut-off (or back) if needed. */
 +void nbnxn_cuda_pme_loadbal_update_param(nbnxn_cuda_ptr_t cu_nb,
 +                                         const interaction_const_t *ic)
 +{
 +    cu_nbparam_t *nbp = cu_nb->nbparam;
 +
 +    nbp->rlist_sq       = ic->rlist * ic->rlist;
 +    nbp->rcoulomb_sq    = ic->rcoulomb * ic->rcoulomb;
 +    nbp->ewald_beta     = ic->ewaldcoeff;
 +
-     init_nbparam(cu_nb->nbparam, ic, nbv);
++    nbp->eeltype        = pick_ewald_kernel_type(ic->rcoulomb != ic->rvdw,
++                                                 cu_nb->dev_info);
 +
 +    init_ewald_coulomb_force_table(cu_nb->nbparam);
 +}
 +
 +/*! Initializes the pair list data structure. */
 +static void init_plist(cu_plist_t *pl)
 +{
 +    /* initialize to NULL pointers to data that is not allocated here and will
 +       need reallocation in nbnxn_cuda_init_pairlist */
 +    pl->sci     = NULL;
 +    pl->cj4     = NULL;
 +    pl->excl    = NULL;
 +
 +    /* size -1 indicates that the respective array hasn't been initialized yet */
 +    pl->na_c        = -1;
 +    pl->nsci        = -1;
 +    pl->sci_nalloc  = -1;
 +    pl->ncj4        = -1;
 +    pl->cj4_nalloc  = -1;
 +    pl->nexcl       = -1;
 +    pl->excl_nalloc = -1;
 +    pl->bDoPrune    = false;
 +}
 +
 +/*! Initializes the timer data structure. */
 +static void init_timers(cu_timers_t *t, bool bUseTwoStreams)
 +{
 +    cudaError_t stat;
 +    int eventflags = ( bUseCudaEventBlockingSync ? cudaEventBlockingSync: cudaEventDefault );
 +
 +    stat = cudaEventCreateWithFlags(&(t->start_atdat), eventflags);
 +    CU_RET_ERR(stat, "cudaEventCreate on start_atdat failed");
 +    stat = cudaEventCreateWithFlags(&(t->stop_atdat), eventflags);
 +    CU_RET_ERR(stat, "cudaEventCreate on stop_atdat failed");
 +
 +    /* The non-local counters/stream (second in the array) are needed only with DD. */
 +    for (int i = 0; i <= (bUseTwoStreams ? 1 : 0); i++)
 +    {
 +        stat = cudaEventCreateWithFlags(&(t->start_nb_k[i]), eventflags);
 +        CU_RET_ERR(stat, "cudaEventCreate on start_nb_k failed");
 +        stat = cudaEventCreateWithFlags(&(t->stop_nb_k[i]), eventflags);
 +        CU_RET_ERR(stat, "cudaEventCreate on stop_nb_k failed");
 +
 +
 +        stat = cudaEventCreateWithFlags(&(t->start_pl_h2d[i]), eventflags);
 +        CU_RET_ERR(stat, "cudaEventCreate on start_pl_h2d failed");
 +        stat = cudaEventCreateWithFlags(&(t->stop_pl_h2d[i]), eventflags);
 +        CU_RET_ERR(stat, "cudaEventCreate on stop_pl_h2d failed");
 +
 +        stat = cudaEventCreateWithFlags(&(t->start_nb_h2d[i]), eventflags);
 +        CU_RET_ERR(stat, "cudaEventCreate on start_nb_h2d failed");
 +        stat = cudaEventCreateWithFlags(&(t->stop_nb_h2d[i]), eventflags);
 +        CU_RET_ERR(stat, "cudaEventCreate on stop_nb_h2d failed");
 +
 +        stat = cudaEventCreateWithFlags(&(t->start_nb_d2h[i]), eventflags);
 +        CU_RET_ERR(stat, "cudaEventCreate on start_nb_d2h failed");
 +        stat = cudaEventCreateWithFlags(&(t->stop_nb_d2h[i]), eventflags);
 +        CU_RET_ERR(stat, "cudaEventCreate on stop_nb_d2h failed");
 +    }
 +}
 +
 +/*! Initializes the timings data structure. */
 +static void init_timings(wallclock_gpu_t *t)
 +{
 +    int i, j;
 +
 +    t->nb_h2d_t = 0.0;
 +    t->nb_d2h_t = 0.0;
 +    t->nb_c    = 0;
 +    t->pl_h2d_t = 0.0;
 +    t->pl_h2d_c = 0;
 +    for (i = 0; i < 2; i++)
 +    {
 +        for(j = 0; j < 2; j++)
 +        {
 +            t->ktime[i][j].t = 0.0;
 +            t->ktime[i][j].c = 0;
 +        }
 +    }
 +}
 +
 +/* Decide which kernel version to use (default or legacy) based on:
 + *  - CUDA version used for compilation
 + *  - non-bonded kernel selector environment variables
 + *  - GPU architecture version
 + */
 +static int pick_nbnxn_kernel_version(FILE            *fplog,
 +                                     cuda_dev_info_t *devinfo)
 +{
 +    bool bForceLegacyKernel, bForceDefaultKernel, bCUDA40, bCUDA32;
 +    char sbuf[STRLEN];
 +    int  kver;
 +
 +    /* Legacy kernel (former k2), kept for backward compatibility as it is
 +       faster than the default with CUDA 3.2/4.0 on Fermi (not on Kepler). */
 +    bForceLegacyKernel  = (getenv("GMX_CUDA_NB_LEGACY") != NULL);
 +    /* default kernel (former k3). */
 +    bForceDefaultKernel = (getenv("GMX_CUDA_NB_DEFAULT") != NULL);
 +
 +    if ((unsigned)(bForceLegacyKernel + bForceDefaultKernel) > 1)
 +    {
 +        gmx_fatal(FARGS, "Multiple CUDA non-bonded kernels requested; to manually pick a kernel set only one \n"
 +                  "of the following environment variables: \n"
 +                  "GMX_CUDA_NB_DEFAULT, GMX_CUDA_NB_LEGACY");
 +    }
 +
 +    bCUDA32 = bCUDA40 = false;
 +#if CUDA_VERSION == 3200
 +    bCUDA32 = true;
 +    sprintf(sbuf, "3.2");
 +#elif CUDA_VERSION == 4000
 +    bCUDA40 = true;
 +    sprintf(sbuf, "4.0");
 +#endif
 +
 +    /* default is default ;) */
 +    kver = eNbnxnCuKDefault;
 +
 +    /* Consider switching to legacy kernels only on Fermi */
 +    if (devinfo->prop.major < 3 && (bCUDA32 || bCUDA40))
 +    {
 +        /* use legacy kernel unless something else is forced by an env. var */
 +        if (bForceDefaultKernel)
 +        {
 +            md_print_warn(fplog,
 +                          "NOTE: CUDA %s compilation detected; with this compiler version the legacy\n"
 +                          "      non-bonded kernels perform best. However, the default kernels were\n"
 +                          "      selected by the GMX_CUDA_NB_DEFAULT environment variable.\n"
 +                          "      For best performance upgrade your CUDA toolkit.\n",
 +                          sbuf);
 +        }
 +        else
 +        {
 +            kver = eNbnxnCuKLegacy;
 +        }
 +    }
 +    else
 +    {
 +        /* issue note if the non-default kernel is forced by an env. var */
 +        if (bForceLegacyKernel)
 +        {
 +            md_print_warn(fplog,
 +                    "NOTE: Legacy non-bonded CUDA kernels selected by the GMX_CUDA_NB_LEGACY\n"
 +                    "      env. var. Consider using using the default kernels which should be faster!\n");
 +
 +            kver = eNbnxnCuKLegacy;
 +        }
 +    }
 +
 +    return kver;
 +}
 +
 +void nbnxn_cuda_init(FILE *fplog,
 +                     nbnxn_cuda_ptr_t *p_cu_nb,
 +                     gmx_gpu_info_t *gpu_info, int my_gpu_index,
 +                     gmx_bool bLocalAndNonlocal)
 +{
 +    cudaError_t stat;
 +    nbnxn_cuda_ptr_t  nb;
 +    char sbuf[STRLEN];
 +    bool bStreamSync, bNoStreamSync, bTMPIAtomics, bX86, bOldDriver;
 +    int cuda_drv_ver;
 +
 +    assert(gpu_info);
 +
 +    if (p_cu_nb == NULL) return;
 +
 +    snew(nb, 1);
 +    snew(nb->atdat, 1);
 +    snew(nb->nbparam, 1);
 +    snew(nb->plist[eintLocal], 1);
 +    if (bLocalAndNonlocal)
 +    {
 +        snew(nb->plist[eintNonlocal], 1);
 +    }
 +
 +    nb->bUseTwoStreams = bLocalAndNonlocal;
 +
 +    snew(nb->timers, 1);
 +    snew(nb->timings, 1);
 +
 +    /* init nbst */
 +    pmalloc((void**)&nb->nbst.e_lj, sizeof(*nb->nbst.e_lj));
 +    pmalloc((void**)&nb->nbst.e_el, sizeof(*nb->nbst.e_el));
 +    pmalloc((void**)&nb->nbst.fshift, SHIFTS * sizeof(*nb->nbst.fshift));
 +
 +    init_plist(nb->plist[eintLocal]);
 +
 +    /* local/non-local GPU streams */
 +    stat = cudaStreamCreate(&nb->stream[eintLocal]);
 +    CU_RET_ERR(stat, "cudaStreamCreate on stream[eintLocal] failed");
 +    if (nb->bUseTwoStreams)
 +    {
 +        init_plist(nb->plist[eintNonlocal]);
 +        stat = cudaStreamCreate(&nb->stream[eintNonlocal]);
 +        CU_RET_ERR(stat, "cudaStreamCreate on stream[eintNonlocal] failed");
 +    }
 +
 +    /* init events for sychronization (timing disabled for performance reasons!) */
 +    stat = cudaEventCreateWithFlags(&nb->nonlocal_done, cudaEventDisableTiming);
 +    CU_RET_ERR(stat, "cudaEventCreate on nonlocal_done failed");
 +    stat = cudaEventCreateWithFlags(&nb->misc_ops_done, cudaEventDisableTiming);
 +    CU_RET_ERR(stat, "cudaEventCreate on misc_ops_one failed");
 +
 +    /* set device info, just point it to the right GPU among the detected ones */
 +    nb->dev_info = &gpu_info->cuda_dev[get_gpu_device_id(gpu_info, my_gpu_index)];
 +
 +    /* On GPUs with ECC enabled, cudaStreamSynchronize shows a large overhead
 +     * (which increases with shorter time/step) caused by a known CUDA driver bug.
 +     * To work around the issue we'll use an (admittedly fragile) memory polling
 +     * waiting to preserve performance. This requires support for atomic
 +     * operations and only works on x86/x86_64.
 +     * With polling wait event-timing also needs to be disabled.
 +     *
 +     * The overhead is greatly reduced in API v5.0 drivers and the improvement
 +     $ is independent of runtime version. Hence, with API v5.0 drivers and later
 +     * we won't switch to polling.
 +     *
 +     * NOTE: Unfortunately, this is known to fail when GPUs are shared by (t)MPI,
 +     * ranks so we will also disable it in that case.
 +     */
 +
 +    bStreamSync    = getenv("GMX_CUDA_STREAMSYNC") != NULL;
 +    bNoStreamSync  = getenv("GMX_NO_CUDA_STREAMSYNC") != NULL;
 +
 +#ifdef TMPI_ATOMICS
 +    bTMPIAtomics = true;
 +#else
 +    bTMPIAtomics = false;
 +#endif
 +
 +#if defined(i386) || defined(__x86_64__)
 +    bX86 = true;
 +#else
 +    bX86 = false;
 +#endif
 +
 +    if (bStreamSync && bNoStreamSync)
 +    {
 +        gmx_fatal(FARGS, "Conflicting environment variables: both GMX_CUDA_STREAMSYNC and GMX_NO_CUDA_STREAMSYNC defined");
 +    }
 +
 +    stat = cudaDriverGetVersion(&cuda_drv_ver);
 +    CU_RET_ERR(stat, "cudaDriverGetVersion failed");
 +    bOldDriver = (cuda_drv_ver < 5000);
 +
 +    if (nb->dev_info->prop.ECCEnabled == 1)
 +    {
 +        if (bStreamSync)
 +        {
 +            nb->bUseStreamSync = true;
 +
 +            /* only warn if polling should be used */
 +            if (bOldDriver && !gpu_info->bDevShare)
 +            {
 +                md_print_warn(fplog,
 +                              "NOTE: Using a GPU with ECC enabled and CUDA driver API version <5.0, but\n"
 +                              "      cudaStreamSynchronize waiting is forced by the GMX_CUDA_STREAMSYNC env. var.\n");
 +            }
 +        }
 +        else
 +        {
 +            /* Can/should turn of cudaStreamSynchronize wait only if
 +             *   - we're on x86/x86_64
 +             *   - atomics are available
 +             *   - GPUs are not being shared
 +             *   - and driver is old. */
 +            nb->bUseStreamSync =
 +                (bX86 && bTMPIAtomics && !gpu_info->bDevShare && bOldDriver) ?
 +                true : false;
 +
 +            if (nb->bUseStreamSync)
 +            {
 +                md_print_warn(fplog,
 +                              "NOTE: Using a GPU with ECC enabled and CUDA driver API version <5.0, known to\n"
 +                              "      cause performance loss. Switching to the alternative polling GPU waiting.\n"
 +                              "      If you encounter issues, switch back to standard GPU waiting by setting\n"
 +                              "      the GMX_CUDA_STREAMSYNC environment variable.\n");
 +            }
 +            else if (bOldDriver)
 +            {
 +                /* Tell the user that the ECC+old driver combination can be bad */
 +                sprintf(sbuf,
 +                        "NOTE: Using a GPU with ECC enabled and CUDA driver API version <5.0. A bug in this\n"
 +                        "      driver can cause performance loss.\n"
 +                        "      However, the polling waiting workaround can not be used because\n%s\n"
 +                        "      Consider updating the driver or turning ECC off.",
 +                        (!bX86 || !bTMPIAtomics) ?
 +                           "         atomic operations are not supported by the platform/CPU+compiler." :
 +                           "         GPU(s) are being oversubscribed.");
 +                md_print_warn(fplog, sbuf);
 +            }
 +        }
 +    }
 +    else
 +    {
 +        if (bNoStreamSync)
 +        {
 +            nb->bUseStreamSync = false;
 +
 +            md_print_warn(fplog,
 +                          "NOTE: Polling wait for GPU synchronization requested by GMX_NO_CUDA_STREAMSYNC\n");
 +        }
 +        else
 +        {
 +            /* no/off ECC, cudaStreamSynchronize not turned off by env. var. */
 +            nb->bUseStreamSync = true;
 +        }
 +    }
 +
 +    /* CUDA timing disabled as event timers don't work:
 +       - with multiple streams = domain-decomposition;
 +       - with the polling waiting hack (without cudaStreamSynchronize);
 +       - when turned off by GMX_DISABLE_CUDA_TIMING.
 +     */
 +    nb->bDoTime = (!nb->bUseTwoStreams && nb->bUseStreamSync &&
 +                   (getenv("GMX_DISABLE_CUDA_TIMING") == NULL));
 +
 +    if (nb->bDoTime)
 +    {
 +        init_timers(nb->timers, nb->bUseTwoStreams);
 +        init_timings(nb->timings);
 +    }
 +
 +    /* set the kernel type for the current GPU */
 +    nb->kernel_ver = pick_nbnxn_kernel_version(fplog, nb->dev_info);
 +    /* pick L1 cache configuration */
 +    nbnxn_cuda_set_cacheconfig(nb->dev_info);
 +
 +    *p_cu_nb = nb;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Initialized CUDA data structures.\n");
 +    }
 +}
 +
 +void nbnxn_cuda_init_const(nbnxn_cuda_ptr_t cu_nb,
 +                           const interaction_const_t *ic,
 +                           const nonbonded_verlet_t *nbv)
 +{
 +    init_atomdata_first(cu_nb->atdat, nbv->grp[0].nbat->ntype);
-     if (nbparam->eeltype == eelCuEWALD || nbparam->eeltype == eelCuEWALD_TWIN)
++    init_nbparam(cu_nb->nbparam, ic, nbv, cu_nb->dev_info);
 +
 +    /* clear energy and shift force outputs */
 +    nbnxn_cuda_clear_e_fshift(cu_nb);
 +}
 +
 +void nbnxn_cuda_init_pairlist(nbnxn_cuda_ptr_t cu_nb,
 +                              const nbnxn_pairlist_t *h_plist,
 +                              int iloc)
 +{
 +    char         sbuf[STRLEN];
 +    cudaError_t  stat;
 +    bool         bDoTime    = cu_nb->bDoTime;
 +    cudaStream_t stream     = cu_nb->stream[iloc];
 +    cu_plist_t   *d_plist   = cu_nb->plist[iloc];
 +
 +    if (d_plist->na_c < 0)
 +    {
 +        d_plist->na_c = h_plist->na_ci;
 +    }
 +    else
 +    {
 +        if (d_plist->na_c != h_plist->na_ci)
 +        {
 +            sprintf(sbuf, "In cu_init_plist: the #atoms per cell has changed (from %d to %d)",
 +                    d_plist->na_c, h_plist->na_ci);
 +            gmx_incons(sbuf);
 +        }
 +    }
 +
 +    if (bDoTime)
 +    {
 +        stat = cudaEventRecord(cu_nb->timers->start_pl_h2d[iloc], stream);
 +        CU_RET_ERR(stat, "cudaEventRecord failed");
 +    }
 +
 +    cu_realloc_buffered((void **)&d_plist->sci, h_plist->sci, sizeof(*d_plist->sci),
 +                         &d_plist->nsci, &d_plist->sci_nalloc,
 +                         h_plist->nsci,
 +                         stream, true);
 +
 +    cu_realloc_buffered((void **)&d_plist->cj4, h_plist->cj4, sizeof(*d_plist->cj4),
 +                         &d_plist->ncj4, &d_plist->cj4_nalloc,
 +                         h_plist->ncj4,
 +                         stream, true);
 +
 +    cu_realloc_buffered((void **)&d_plist->excl, h_plist->excl, sizeof(*d_plist->excl),
 +                         &d_plist->nexcl, &d_plist->excl_nalloc,
 +                         h_plist->nexcl,
 +                         stream, true);
 +
 +    if (bDoTime)
 +    {
 +        stat = cudaEventRecord(cu_nb->timers->stop_pl_h2d[iloc], stream);
 +        CU_RET_ERR(stat, "cudaEventRecord failed");
 +    }
 +
 +    /* need to prune the pair list during the next step */
 +    d_plist->bDoPrune = true;
 +}
 +
 +void nbnxn_cuda_upload_shiftvec(nbnxn_cuda_ptr_t cu_nb,
 +                                const nbnxn_atomdata_t *nbatom)
 +{
 +    cu_atomdata_t *adat = cu_nb->atdat;
 +    cudaStream_t  ls    = cu_nb->stream[eintLocal];
 +
 +    /* only if we have a dynamic box */
 +    if (nbatom->bDynamicBox || !adat->bShiftVecUploaded)
 +    {
 +        cu_copy_H2D_async(adat->shift_vec, nbatom->shift_vec, 
 +                          SHIFTS * sizeof(*adat->shift_vec), ls);
 +        adat->bShiftVecUploaded = true;
 +    }
 +}
 +
 +/*! Clears the first natoms_clear elements of the GPU nonbonded force output array. */
 +static void nbnxn_cuda_clear_f(nbnxn_cuda_ptr_t cu_nb, int natoms_clear)
 +{
 +    cudaError_t   stat;
 +    cu_atomdata_t *adat = cu_nb->atdat;
 +    cudaStream_t  ls    = cu_nb->stream[eintLocal];
 +
 +    stat = cudaMemsetAsync(adat->f, 0, natoms_clear * sizeof(*adat->f), ls);
 +    CU_RET_ERR(stat, "cudaMemsetAsync on f falied");
 +}
 +
 +/*! Clears nonbonded shift force output array and energy outputs on the GPU. */
 +static void nbnxn_cuda_clear_e_fshift(nbnxn_cuda_ptr_t cu_nb)
 +{
 +    cudaError_t   stat;
 +    cu_atomdata_t *adat = cu_nb->atdat;
 +    cudaStream_t  ls    = cu_nb->stream[eintLocal];
 +
 +    stat = cudaMemsetAsync(adat->fshift, 0, SHIFTS * sizeof(*adat->fshift), ls);
 +    CU_RET_ERR(stat, "cudaMemsetAsync on fshift falied");
 +    stat = cudaMemsetAsync(adat->e_lj, 0, sizeof(*adat->e_lj), ls);
 +    CU_RET_ERR(stat, "cudaMemsetAsync on e_lj falied");
 +    stat = cudaMemsetAsync(adat->e_el, 0, sizeof(*adat->e_el), ls);
 +    CU_RET_ERR(stat, "cudaMemsetAsync on e_el falied");
 +}
 +
 +void nbnxn_cuda_clear_outputs(nbnxn_cuda_ptr_t cu_nb, int flags)
 +{
 +    nbnxn_cuda_clear_f(cu_nb, cu_nb->atdat->natoms);
 +    /* clear shift force array and energies if the outputs were 
 +       used in the current step */
 +    if (flags & GMX_FORCE_VIRIAL)
 +    {
 +        nbnxn_cuda_clear_e_fshift(cu_nb);
 +    }
 +}
 +
 +void nbnxn_cuda_init_atomdata(nbnxn_cuda_ptr_t cu_nb,
 +                              const nbnxn_atomdata_t *nbat)
 +{
 +    cudaError_t   stat;
 +    int           nalloc, natoms;
 +    bool          realloced;
 +    bool          bDoTime   = cu_nb->bDoTime;
 +    cu_timers_t   *timers   = cu_nb->timers;
 +    cu_atomdata_t *d_atdat  = cu_nb->atdat;
 +    cudaStream_t  ls        = cu_nb->stream[eintLocal];
 +
 +    natoms = nbat->natoms;
 +    realloced = false;
 +
 +    if (bDoTime)
 +    {
 +        /* time async copy */
 +        stat = cudaEventRecord(timers->start_atdat, ls);
 +        CU_RET_ERR(stat, "cudaEventRecord failed");
 +    }
 +
 +    /* need to reallocate if we have to copy more atoms than the amount of space
 +       available and only allocate if we haven't initialized yet, i.e d_atdat->natoms == -1 */
 +    if (natoms > d_atdat->nalloc)
 +    {
 +        nalloc = over_alloc_small(natoms);
 +
 +        /* free up first if the arrays have already been initialized */
 +        if (d_atdat->nalloc != -1)
 +        {
 +            cu_free_buffered(d_atdat->f, &d_atdat->natoms, &d_atdat->nalloc);
 +            cu_free_buffered(d_atdat->xq);
 +            cu_free_buffered(d_atdat->atom_types);
 +        }
 +
 +        stat = cudaMalloc((void **)&d_atdat->f, nalloc*sizeof(*d_atdat->f));
 +        CU_RET_ERR(stat, "cudaMalloc failed on d_atdat->f");
 +        stat = cudaMalloc((void **)&d_atdat->xq, nalloc*sizeof(*d_atdat->xq));
 +        CU_RET_ERR(stat, "cudaMalloc failed on d_atdat->xq");
 +
 +        stat = cudaMalloc((void **)&d_atdat->atom_types, nalloc*sizeof(*d_atdat->atom_types));
 +        CU_RET_ERR(stat, "cudaMalloc failed on d_atdat->atom_types");
 +
 +        d_atdat->nalloc = nalloc;
 +        realloced = true;
 +    }
 +
 +    d_atdat->natoms = natoms;
 +    d_atdat->natoms_local = nbat->natoms_local;
 +
 +    /* need to clear GPU f output if realloc happened */
 +    if (realloced)
 +    {
 +        nbnxn_cuda_clear_f(cu_nb, nalloc);
 +    }
 +
 +    cu_copy_H2D_async(d_atdat->atom_types, nbat->type,
 +                      natoms*sizeof(*d_atdat->atom_types), ls);
 +
 +    if (bDoTime)
 +    {
 +        stat = cudaEventRecord(timers->stop_atdat, ls);
 +        CU_RET_ERR(stat, "cudaEventRecord failed");
 +    }
 +}
 +
 +void nbnxn_cuda_free(FILE *fplog, nbnxn_cuda_ptr_t cu_nb)
 +{
 +    cudaError_t     stat;
 +    cu_atomdata_t   *atdat;
 +    cu_nbparam_t    *nbparam;
 +    cu_plist_t      *plist, *plist_nl;
 +    cu_timers_t     *timers;
 +
 +    if (cu_nb == NULL) return;
 +
 +    atdat       = cu_nb->atdat;
 +    nbparam     = cu_nb->nbparam;
 +    plist       = cu_nb->plist[eintLocal];
 +    plist_nl    = cu_nb->plist[eintNonlocal];
 +    timers      = cu_nb->timers;
 +
++    if (nbparam->eeltype == eelCuEWALD_TAB || nbparam->eeltype == eelCuEWALD_TAB_TWIN)
 +    {
 +      stat = cudaUnbindTexture(nbnxn_cuda_get_coulomb_tab_texref());
 +      CU_RET_ERR(stat, "cudaUnbindTexture on coulomb_tab failed");
 +      cu_free_buffered(nbparam->coulomb_tab, &nbparam->coulomb_tab_size);
 +    }
 +
 +    stat = cudaEventDestroy(cu_nb->nonlocal_done);
 +    CU_RET_ERR(stat, "cudaEventDestroy failed on timers->nonlocal_done");
 +    stat = cudaEventDestroy(cu_nb->misc_ops_done);
 +    CU_RET_ERR(stat, "cudaEventDestroy failed on timers->misc_ops_done");
 +
 +    if (cu_nb->bDoTime)
 +    {
 +        stat = cudaEventDestroy(timers->start_atdat);
 +        CU_RET_ERR(stat, "cudaEventDestroy failed on timers->start_atdat");
 +        stat = cudaEventDestroy(timers->stop_atdat);
 +        CU_RET_ERR(stat, "cudaEventDestroy failed on timers->stop_atdat");
 +
 +        /* The non-local counters/stream (second in the array) are needed only with DD. */
 +        for (int i = 0; i <= (cu_nb->bUseTwoStreams ? 1 : 0); i++)
 +        {
 +            stat = cudaEventDestroy(timers->start_nb_k[i]);
 +            CU_RET_ERR(stat, "cudaEventDestroy failed on timers->start_nb_k");
 +            stat = cudaEventDestroy(timers->stop_nb_k[i]);
 +            CU_RET_ERR(stat, "cudaEventDestroy failed on timers->stop_nb_k");
 +
 +            stat = cudaEventDestroy(timers->start_pl_h2d[i]);
 +            CU_RET_ERR(stat, "cudaEventDestroy failed on timers->start_pl_h2d");
 +            stat = cudaEventDestroy(timers->stop_pl_h2d[i]);
 +            CU_RET_ERR(stat, "cudaEventDestroy failed on timers->stop_pl_h2d");
 +
 +            stat = cudaStreamDestroy(cu_nb->stream[i]);
 +            CU_RET_ERR(stat, "cudaStreamDestroy failed on stream");
 +
 +            stat = cudaEventDestroy(timers->start_nb_h2d[i]);
 +            CU_RET_ERR(stat, "cudaEventDestroy failed on timers->start_nb_h2d");
 +            stat = cudaEventDestroy(timers->stop_nb_h2d[i]);
 +            CU_RET_ERR(stat, "cudaEventDestroy failed on timers->stop_nb_h2d");
 +
 +            stat = cudaEventDestroy(timers->start_nb_d2h[i]);
 +            CU_RET_ERR(stat, "cudaEventDestroy failed on timers->start_nb_d2h");
 +            stat = cudaEventDestroy(timers->stop_nb_d2h[i]);
 +            CU_RET_ERR(stat, "cudaEventDestroy failed on timers->stop_nb_d2h");
 +        }
 +    }
 +
 +    stat = cudaUnbindTexture(nbnxn_cuda_get_nbfp_texref());
 +    CU_RET_ERR(stat, "cudaUnbindTexture on coulomb_tab failed");
 +    cu_free_buffered(nbparam->nbfp);
 +
 +    stat = cudaFree(atdat->shift_vec);
 +    CU_RET_ERR(stat, "cudaFree failed on atdat->shift_vec");
 +    stat = cudaFree(atdat->fshift);
 +    CU_RET_ERR(stat, "cudaFree failed on atdat->fshift");
 +
 +    stat = cudaFree(atdat->e_lj);
 +    CU_RET_ERR(stat, "cudaFree failed on atdat->e_lj");
 +    stat = cudaFree(atdat->e_el);
 +    CU_RET_ERR(stat, "cudaFree failed on atdat->e_el");
 +
 +    cu_free_buffered(atdat->f, &atdat->natoms, &atdat->nalloc);
 +    cu_free_buffered(atdat->xq);
 +    cu_free_buffered(atdat->atom_types, &atdat->ntypes);
 +
 +    cu_free_buffered(plist->sci, &plist->nsci, &plist->sci_nalloc);
 +    cu_free_buffered(plist->cj4, &plist->ncj4, &plist->cj4_nalloc);
 +    cu_free_buffered(plist->excl, &plist->nexcl, &plist->excl_nalloc);
 +    if (cu_nb->bUseTwoStreams)
 +    {
 +        cu_free_buffered(plist_nl->sci, &plist_nl->nsci, &plist_nl->sci_nalloc);
 +        cu_free_buffered(plist_nl->cj4, &plist_nl->ncj4, &plist_nl->cj4_nalloc);
 +        cu_free_buffered(plist_nl->excl, &plist_nl->nexcl, &plist->excl_nalloc);
 +    }
 +
 +    sfree(atdat);
 +    sfree(nbparam);
 +    sfree(plist);
 +    if (cu_nb->bUseTwoStreams)
 +    {
 +        sfree(plist_nl);
 +    }
 +    sfree(timers);
 +    sfree(cu_nb->timings);
 +    sfree(cu_nb);
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Cleaned up CUDA data structures.\n");
 +    }
 +}
 +
 +void cu_synchstream_atdat(nbnxn_cuda_ptr_t cu_nb, int iloc)
 +{
 +    cudaError_t stat;
 +    cudaStream_t stream = cu_nb->stream[iloc];
 +
 +    stat = cudaStreamWaitEvent(stream, cu_nb->timers->stop_atdat, 0);
 +    CU_RET_ERR(stat, "cudaStreamWaitEvent failed");
 +}
 +
 +wallclock_gpu_t * nbnxn_cuda_get_timings(nbnxn_cuda_ptr_t cu_nb)
 +{
 +    return (cu_nb != NULL && cu_nb->bDoTime) ? cu_nb->timings : NULL;
 +}
 +
 +void nbnxn_cuda_reset_timings(nbnxn_cuda_ptr_t cu_nb)
 +{
 +    if (cu_nb->bDoTime)
 +    {
 +        init_timings(cu_nb->timings);
 +    }
 +}
 +
 +int nbnxn_cuda_min_ci_balanced(nbnxn_cuda_ptr_t cu_nb)
 +{
 +    return cu_nb != NULL ?
 +        gpu_min_ci_balanced_factor*cu_nb->dev_info->prop.multiProcessorCount : 0;
 +
 +}
index c7ab7cc6c62fc2db701e8b7df5d451ca8f9278fa,0000000000000000000000000000000000000000..3ef5f31ec34889d39e1cbcf3f6f695ab34ad96d9
mode 100644,000000..100644
--- /dev/null
@@@ -1,439 -1,0 +1,452 @@@
- #ifdef EL_EWALD
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +
 +#include "maths.h"
 +/* Note that floating-point constants in CUDA code should be suffixed
 + * with f (e.g. 0.5f), to stop the compiler producing intermediate
 + * code that is in double precision.
 + */
 +
 +#if __CUDA_ARCH__ >= 300
 +#define REDUCE_SHUFFLE
 +/* On Kepler pre-loading i-atom types to shmem gives a few %,
 +   but on Fermi it does not */
 +#define IATYPE_SHMEM
 +#endif
 +
++#if defined EL_EWALD_ANA || defined EL_EWALD_TAB
++/* Note: convenience macro, needs to be undef-ed at the end of the file. */
++#define EL_EWALD_ANY
++#endif
++
 +/*
 +   Kernel launch parameters:
 +    - #blocks   = #pair lists, blockId = pair list Id
 +    - #threads  = CL_SIZE^2
 +    - shmem     = CL_SIZE^2 * sizeof(float)
 +
 +    Each thread calculates an i force-component taking one pair of i-j atoms.
 + */
 +#ifdef PRUNE_NBL
 +#ifdef CALC_ENERGIES
 +__global__ void NB_KERNEL_FUNC_NAME(k_nbnxn, _ener_prune)
 +#else
 +__global__ void NB_KERNEL_FUNC_NAME(k_nbnxn, _prune)
 +#endif
 +#else
 +#ifdef CALC_ENERGIES
 +__global__ void NB_KERNEL_FUNC_NAME(k_nbnxn, _ener)
 +#else
 +__global__ void NB_KERNEL_FUNC_NAME(k_nbnxn)
 +#endif
 +#endif
 +            (const cu_atomdata_t atdat,
 +             const cu_nbparam_t nbparam,
 +             const cu_plist_t plist,
 +             bool bCalcFshift)
 +{
 +    /* convenience variables */
 +    const nbnxn_sci_t *pl_sci   = plist.sci;
 +#ifndef PRUNE_NBL
 +    const
 +#endif
 +    nbnxn_cj4_t *pl_cj4         = plist.cj4;
 +    const nbnxn_excl_t *excl    = plist.excl;
 +    const int *atom_types       = atdat.atom_types;
 +    int ntypes                  = atdat.ntypes;
 +    const float4 *xq            = atdat.xq;
 +    float3 *f                   = atdat.f;
 +    const float3 *shift_vec     = atdat.shift_vec;
 +    float rcoulomb_sq           = nbparam.rcoulomb_sq;
 +#ifdef VDW_CUTOFF_CHECK
 +    float rvdw_sq               = nbparam.rvdw_sq;
 +    float vdw_in_range;
 +#endif
 +#ifdef EL_RF
 +    float two_k_rf              = nbparam.two_k_rf;
 +#endif
- #ifdef EL_EWALD
++#ifdef EL_EWALD_TAB
 +    float coulomb_tab_scale     = nbparam.coulomb_tab_scale;
 +#endif
++#ifdef EL_EWALD_ANA
++    float beta2                 = nbparam.ewald_beta*nbparam.ewald_beta;
++    float beta3                 = nbparam.ewald_beta*nbparam.ewald_beta*nbparam.ewald_beta;
++#endif
 +#ifdef PRUNE_NBL
 +    float rlist_sq              = nbparam.rlist_sq;
 +#endif
 +
 +#ifdef CALC_ENERGIES
 +    float lj_shift    = nbparam.sh_invrc6;
- #if defined EL_EWALD || defined EL_RF
++#ifdef EL_EWALD_ANY
 +    float beta        = nbparam.ewald_beta;
 +    float ewald_shift = nbparam.sh_ewald;
 +#else
 +    float c_rf        = nbparam.c_rf;
 +#endif
 +    float *e_lj       = atdat.e_lj;
 +    float *e_el       = atdat.e_el;
 +#endif
 +
 +    /* thread/block/warp id-s */
 +    unsigned int tidxi  = threadIdx.x;
 +    unsigned int tidxj  = threadIdx.y;
 +    unsigned int tidx   = threadIdx.y * blockDim.x + threadIdx.x;
 +    unsigned int bidx   = blockIdx.x;
 +    unsigned int widx   = tidx / WARP_SIZE; /* warp index */
 +
 +    int sci, ci, cj, ci_offset,
 +        ai, aj,
 +        cij4_start, cij4_end,
 +        typei, typej,
 +        i, jm, j4, wexcl_idx;
 +    float qi, qj_f,
 +          r2, inv_r, inv_r2, inv_r6,
 +          c6, c12,
 +          int_bit,
 +#ifdef CALC_ENERGIES
 +          E_lj, E_el, E_lj_p,
 +#endif
 +          F_invr;
 +    unsigned int wexcl, imask, mask_ji;
 +    float4 xqbuf;
 +    float3 xi, xj, rv, f_ij, fcj_buf, fshift_buf;
 +    float3 fci_buf[NCL_PER_SUPERCL];    /* i force buffer */
 +    nbnxn_sci_t nb_sci;
 +
 +    /* shmem buffer for i x+q pre-loading */
 +    extern __shared__  float4 xqib[];
 +    /* shmem buffer for cj, for both warps separately */
 +    int *cjs     = (int *)(xqib + NCL_PER_SUPERCL * CL_SIZE);
 +#ifdef IATYPE_SHMEM
 +    /* shmem buffer for i atom-type pre-loading */
 +    int *atib = (int *)(cjs + 2 * NBNXN_GPU_JGROUP_SIZE);
 +#endif
 +
 +#ifndef REDUCE_SHUFFLE
 +    /* shmem j force buffer */
 +#ifdef IATYPE_SHMEM
 +    float *f_buf = (float *)(atib + NCL_PER_SUPERCL * CL_SIZE);
 +#else
 +    float *f_buf = (float *)(cjs + 2 * NBNXN_GPU_JGROUP_SIZE);
 +#endif
 +#endif
 +
 +    nb_sci      = pl_sci[bidx];         /* my i super-cluster's index = current bidx */
 +    sci         = nb_sci.sci;           /* super-cluster */
 +    cij4_start  = nb_sci.cj4_ind_start; /* first ...*/
 +    cij4_end    = nb_sci.cj4_ind_end;   /* and last index of j clusters */
 +
 +    /* Store the i-atom x and q in shared memory */
 +    /* Note: the thread indexing here is inverted with respect to the
 +       inner-loop as this results in slightly higher performance */
 +    ci = sci * NCL_PER_SUPERCL + tidxi;
 +    ai = ci * CL_SIZE + tidxj;
 +    xqib[tidxi * CL_SIZE + tidxj] = xq[ai] + shift_vec[nb_sci.shift];
 +#ifdef IATYPE_SHMEM
 +    ci = sci * NCL_PER_SUPERCL + tidxj;
 +    ai = ci * CL_SIZE + tidxi;
 +    atib[tidxj * CL_SIZE + tidxi] = atom_types[ai];
 +#endif
 +    __syncthreads();
 +
 +    for(ci_offset = 0; ci_offset < NCL_PER_SUPERCL; ci_offset++)
 +    {
 +        fci_buf[ci_offset] = make_float3(0.0f);
 +    }
 +
 +#ifdef CALC_ENERGIES
 +    E_lj = 0.0f;
 +    E_el = 0.0f;
 +
- #if !defined PRUNE_NBL && !(CUDA_VERSION < 4010 && (defined EL_EWALD || defined EL_RF))
++#if defined EL_EWALD_ANY || defined EL_RF
 +    if (nb_sci.shift == CENTRAL && pl_cj4[cij4_start].cj[0] == sci*NCL_PER_SUPERCL)
 +    {
 +        /* we have the diagonal: add the charge self interaction energy term */
 +        for (i = 0; i < NCL_PER_SUPERCL; i++)
 +        {
 +            qi    = xqib[i * CL_SIZE + tidxi].w;
 +            E_el += qi*qi;
 +        }
 +        /* divide the self term equally over the j-threads */
 +        E_el /= CL_SIZE;
 +#ifdef EL_RF
 +        E_el *= -nbparam.epsfac*0.5f*c_rf;
 +#else
 +        E_el *= -nbparam.epsfac*beta*M_FLOAT_1_SQRTPI; /* last factor 1/sqrt(pi) */
 +#endif
 +    }
 +#endif
 +#endif
 +
 +    /* skip central shifts when summing shift forces */
 +    if (nb_sci.shift == CENTRAL)
 +    {
 +        bCalcFshift = false;
 +    }
 +
 +    fshift_buf = make_float3(0.0f);
 +
 +    /* loop over the j clusters = seen by any of the atoms in the current super-cluster */
 +    for (j4 = cij4_start; j4 < cij4_end; j4++)
 +    {
 +        wexcl_idx   = pl_cj4[j4].imei[widx].excl_ind;
 +        imask       = pl_cj4[j4].imei[widx].imask;
 +        wexcl       = excl[wexcl_idx].pair[(tidx) & (WARP_SIZE - 1)];
 +
 +#ifndef PRUNE_NBL
 +        if (imask)
 +#endif
 +        {
 +            /* Pre-load cj into shared memory on both warps separately */
 +            if ((tidxj == 0 || tidxj == 4) && tidxi < NBNXN_GPU_JGROUP_SIZE)
 +            {
 +                cjs[tidxi + tidxj * NBNXN_GPU_JGROUP_SIZE / 4] = pl_cj4[j4].cj[tidxi];
 +            }
 +
 +            /* Unrolling this loop
 +               - with pruning leads to register spilling;
 +               - on Kepler is much slower;
 +               - doesn't work on CUDA <v4.1
 +               Tested with nvcc 3.2 - 5.0.7 */
 +#if !defined PRUNE_NBL && __CUDA_ARCH__ < 300 && CUDA_VERSION >= 4010
 +#pragma unroll 4
 +#endif
 +            for (jm = 0; jm < NBNXN_GPU_JGROUP_SIZE; jm++)
 +            {
 +                if (imask & (supercl_interaction_mask << (jm * NCL_PER_SUPERCL)))
 +                {
 +                    mask_ji = (1U << (jm * NCL_PER_SUPERCL));
 +
 +                    cj      = cjs[jm + (tidxj & 4) * NBNXN_GPU_JGROUP_SIZE / 4];
 +                    aj      = cj * CL_SIZE + tidxj;
 +
 +                    /* load j atom data */
 +                    xqbuf   = xq[aj];
 +                    xj      = make_float3(xqbuf.x, xqbuf.y, xqbuf.z);
 +                    qj_f    = nbparam.epsfac * xqbuf.w;
 +                    typej   = atom_types[aj];
 +
 +                    fcj_buf = make_float3(0.0f);
 +
 +                    /* The PME and RF kernels don't unroll with CUDA <v4.1. */
- #if defined EL_EWALD || defined EL_RF
++#if !defined PRUNE_NBL && !(CUDA_VERSION < 4010 && (defined EL_EWALD_ANY || defined EL_RF))
 +#pragma unroll 8
 +#endif
 +                    for(i = 0; i < NCL_PER_SUPERCL; i++)
 +                    {
 +                        if (imask & mask_ji)
 +                        {
 +                            ci_offset   = i;    /* i force buffer offset */
 +
 +                            ci      = sci * NCL_PER_SUPERCL + i; /* i cluster index */
 +                            ai      = ci * CL_SIZE + tidxi;      /* i atom index */
 +
 +                            /* all threads load an atom from i cluster ci into shmem! */
 +                            xqbuf   = xqib[i * CL_SIZE + tidxi];
 +                            xi      = make_float3(xqbuf.x, xqbuf.y, xqbuf.z);
 +
 +                            /* distance between i and j atoms */
 +                            rv      = xi - xj;
 +                            r2      = norm2(rv);
 +
 +#ifdef PRUNE_NBL
 +                            /* If _none_ of the atoms pairs are in cutoff range,
 +                               the bit corresponding to the current
 +                               cluster-pair in imask gets set to 0. */
 +                            if (!__any(r2 < rlist_sq))
 +                            {
 +                                imask &= ~mask_ji;
 +                            }
 +#endif
 +
 +                            int_bit = (wexcl & mask_ji) ? 1.0f : 0.0f;
 +
 +                            /* cutoff & exclusion check */
- #if defined EL_EWALD || defined EL_RF
++#if defined EL_EWALD_ANY || defined EL_RF
 +                            if (r2 < rcoulomb_sq *
 +                                (nb_sci.shift != CENTRAL || ci != cj || tidxj > tidxi))
 +#else
 +                            if (r2 < rcoulomb_sq * int_bit)
 +#endif
 +                            {
 +                                /* load the rest of the i-atom parameters */
 +                                qi      = xqbuf.w;
 +#ifdef IATYPE_SHMEM
 +                                typei   = atib[i * CL_SIZE + tidxi];
 +#else
 +                                typei   = atom_types[ai];
 +#endif
 +
 +                                /* LJ 6*C6 and 12*C12 */
 +                                c6      = tex1Dfetch(tex_nbfp, 2 * (ntypes * typei + typej));
 +                                c12     = tex1Dfetch(tex_nbfp, 2 * (ntypes * typei + typej) + 1);
 +
 +                                /* avoid NaN for excluded pairs at r=0 */
 +                                r2      += (1.0f - int_bit) * NBNXN_AVOID_SING_R2_INC;
 +
 +                                inv_r   = rsqrt(r2);
 +                                inv_r2  = inv_r * inv_r;
 +                                inv_r6  = inv_r2 * inv_r2 * inv_r2;
- #ifdef EL_EWALD
++#if defined EL_EWALD_ANY || defined EL_RF
 +                                /* We could mask inv_r2, but with Ewald
 +                                 * masking both inv_r6 and F_invr is faster */
 +                                inv_r6  *= int_bit;
 +#endif
 +
 +                                F_invr  = inv_r6 * (c12 * inv_r6 - c6) * inv_r2;
 +
 +#ifdef CALC_ENERGIES
 +                                E_lj_p  = int_bit * (c12 * (inv_r6 * inv_r6 - lj_shift * lj_shift) * 0.08333333f - c6 * (inv_r6 - lj_shift) * 0.16666667f);
 +#endif
 +
 +#ifdef VDW_CUTOFF_CHECK
 +                                /* this enables twin-range cut-offs (rvdw < rcoulomb <= rlist) */
 +                                vdw_in_range = (r2 < rvdw_sq) ? 1.0f : 0.0f;
 +                                F_invr  *= vdw_in_range;
 +#ifdef CALC_ENERGIES
 +                                E_lj_p  *= vdw_in_range;
 +#endif
 +#endif
 +#ifdef CALC_ENERGIES
 +                                E_lj    += E_lj_p;
 +#endif
 +
 +
 +#ifdef EL_CUTOFF
 +                                F_invr  += qi * qj_f * inv_r2 * inv_r;
 +#endif
 +#ifdef EL_RF
 +                                F_invr  += qi * qj_f * (int_bit*inv_r2 * inv_r - two_k_rf);
 +#endif
- #endif
++#if defined EL_EWALD_ANA
++                                F_invr  += qi * qj_f * (int_bit*inv_r2*inv_r + pmecorrF(beta2*r2)*beta3);
++#elif defined EL_EWALD_TAB
 +                                F_invr  += qi * qj_f * (int_bit*inv_r2 - interpolate_coulomb_force_r(r2 * inv_r, coulomb_tab_scale)) * inv_r;
- #ifdef EL_EWALD
++#endif /* EL_EWALD_ANA/TAB */
 +
 +#ifdef CALC_ENERGIES
 +#ifdef EL_CUTOFF
 +                                E_el    += qi * qj_f * (inv_r - c_rf);
 +#endif
 +#ifdef EL_RF
 +                                E_el    += qi * qj_f * (int_bit*inv_r + 0.5f * two_k_rf * r2 - c_rf);
 +#endif
- #endif
++#ifdef EL_EWALD_ANY
 +                                /* 1.0f - erff is faster than erfcf */
 +                                E_el    += qi * qj_f * (inv_r * (int_bit - erff(r2 * inv_r * beta)) - int_bit * ewald_shift);
++#endif /* EL_EWALD_ANY */
 +#endif
 +                                f_ij    = rv * F_invr;
 +
 +                                /* accumulate j forces in registers */
 +                                fcj_buf -= f_ij;
 +
 +                                /* accumulate i forces in registers */
 +                                fci_buf[ci_offset] += f_ij;
 +                            }
 +                        }
 +
 +                        /* shift the mask bit by 1 */
 +                        mask_ji += mask_ji;
 +                    }
 +
 +                    /* reduce j forces */
 +#ifdef REDUCE_SHUFFLE
 +                    reduce_force_j_warp_shfl(fcj_buf, f, tidxi, aj);
 +#else
 +                    /* store j forces in shmem */
 +                    f_buf[                  tidx] = fcj_buf.x;
 +                    f_buf[    FBUF_STRIDE + tidx] = fcj_buf.y;
 +                    f_buf[2 * FBUF_STRIDE + tidx] = fcj_buf.z;
 +
 +                    reduce_force_j_generic(f_buf, f, tidxi, tidxj, aj);
 +#endif
 +                }
 +            }
 +#ifdef PRUNE_NBL
 +            /* Update the imask with the new one which does not contain the
 +               out of range clusters anymore. */
 +            pl_cj4[j4].imei[widx].imask = imask;
 +#endif
 +        }
 +    }
 +
 +    /* reduce i forces */
 +    for(ci_offset = 0; ci_offset < NCL_PER_SUPERCL; ci_offset++)
 +    {
 +        ai  = (sci * NCL_PER_SUPERCL + ci_offset) * CL_SIZE + tidxi;
 +#ifdef REDUCE_SHUFFLE
 +        reduce_force_i_warp_shfl(fci_buf[ci_offset], f,
 +                                 &fshift_buf, bCalcFshift,
 +                                 tidxj, ai);
 +#else
 +        f_buf[                  tidx] = fci_buf[ci_offset].x;
 +        f_buf[    FBUF_STRIDE + tidx] = fci_buf[ci_offset].y;
 +        f_buf[2 * FBUF_STRIDE + tidx] = fci_buf[ci_offset].z;
 +        __syncthreads();
 +        reduce_force_i(f_buf, f,
 +                       &fshift_buf, bCalcFshift,
 +                       tidxi, tidxj, ai);
 +        __syncthreads();
 +#endif
 +    }
 +
 +    /* add up local shift forces into global mem */
 +#ifdef REDUCE_SHUFFLE
 +    if (bCalcFshift && (tidxj == 0 || tidxj == 4))
 +#else
 +    if (bCalcFshift && tidxj == 0)
 +#endif
 +    {
 +        atomicAdd(&atdat.fshift[nb_sci.shift].x, fshift_buf.x);
 +        atomicAdd(&atdat.fshift[nb_sci.shift].y, fshift_buf.y);
 +        atomicAdd(&atdat.fshift[nb_sci.shift].z, fshift_buf.z);
 +    }
 +
 +#ifdef CALC_ENERGIES
 +#ifdef REDUCE_SHUFFLE
 +    /* reduce the energies over warps and store into global memory */
 +    reduce_energy_warp_shfl(E_lj, E_el, e_lj, e_el, tidx);
 +#else
 +    /* flush the energies to shmem and reduce them */
 +    f_buf[              tidx] = E_lj;
 +    f_buf[FBUF_STRIDE + tidx] = E_el;
 +    reduce_energy_pow2(f_buf + (tidx & WARP_SIZE), e_lj, e_el, tidx & ~WARP_SIZE);
 +#endif
 +#endif
 +}
++
++#undef EL_EWALD_ANY
index be1c7a778c95b6258fcb0ba80111d9b06822841c,0000000000000000000000000000000000000000..892c360e1e292372db4d848ceea87d4fedd7bf1a
mode 100644,000000..100644
--- /dev/null
@@@ -1,385 -1,0 +1,385 @@@
- #ifdef EL_EWALD
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +
 +#include "maths.h"
 +/* Note that floating-point constants in CUDA code should be suffixed
 + * with f (e.g. 0.5f), to stop the compiler producing intermediate
 + * code that is in double precision.
 + */
 +
++/* Analytical Ewald is not implemented for the legacy kernels (as it is anyway
++   slower than the tabulated kernel on Fermi). */
++#ifdef EL_EWALD_ANA
++#error Trying to generate Analytical Ewald legacy kernels which is not implemented in the legacy kernels!
++#endif
++
 +/*
 +   Kernel launch parameters:
 +    - #blocks   = #pair lists, blockId = pair list Id
 +    - #threads  = CL_SIZE^2
 +    - shmem     = CL_SIZE^2 * sizeof(float)
 +
 +    Each thread calculates an i force-component taking one pair of i-j atoms.
 + */
 +#ifdef PRUNE_NBL
 +#ifdef CALC_ENERGIES
 +__global__ void NB_KERNEL_FUNC_NAME(k_nbnxn, _ener_prune_legacy)
 +#else
 +__global__ void NB_KERNEL_FUNC_NAME(k_nbnxn, _prune_legacy)
 +#endif
 +#else
 +#ifdef CALC_ENERGIES
 +__global__ void NB_KERNEL_FUNC_NAME(k_nbnxn, _ener_legacy)
 +#else
 +__global__ void NB_KERNEL_FUNC_NAME(k_nbnxn, _legacy)
 +#endif
 +#endif
 +            (const cu_atomdata_t atdat,
 +             const cu_nbparam_t nbparam,
 +             const cu_plist_t plist,
 +             bool bCalcFshift)
 +{
 +    /* convenience variables */
 +    const nbnxn_sci_t *pl_sci   = plist.sci;
 +#ifndef PRUNE_NBL
 +    const
 +#endif
 +    nbnxn_cj4_t *pl_cj4         = plist.cj4;
 +    const nbnxn_excl_t *excl    = plist.excl;
 +    const int *atom_types       = atdat.atom_types;
 +    int ntypes                  = atdat.ntypes;
 +    const float4 *xq            = atdat.xq;
 +    float3 *f                   = atdat.f;
 +    const float3 *shift_vec     = atdat.shift_vec;
 +    float rcoulomb_sq           = nbparam.rcoulomb_sq;
 +#ifdef VDW_CUTOFF_CHECK
 +    float rvdw_sq               = nbparam.rvdw_sq;
 +    float vdw_in_range;
 +#endif
 +#ifdef EL_RF
 +    float two_k_rf              = nbparam.two_k_rf;
 +#endif
- #ifdef EL_EWALD
++#ifdef EL_EWALD_TAB
 +    float coulomb_tab_scale     = nbparam.coulomb_tab_scale;
 +#endif
 +#ifdef PRUNE_NBL
 +    float rlist_sq              = nbparam.rlist_sq;
 +#endif
 +
 +#ifdef CALC_ENERGIES
 +    float lj_shift    = nbparam.sh_invrc6;
-     unsigned int wexcl, int_bit, imask, imask_j;
- #ifdef PRUNE_NBL
-     unsigned int imask_prune;
- #endif
++#ifdef EL_EWALD_TAB
 +    float beta        = nbparam.ewald_beta;
 +    float ewald_shift = nbparam.sh_ewald;
 +#else
 +    float c_rf        = nbparam.c_rf;
 +#endif
 +    float *e_lj       = atdat.e_lj;
 +    float *e_el       = atdat.e_el;
 +#endif
 +
 +    /* thread/block/warp id-s */
 +    unsigned int tidxi  = threadIdx.x;
 +    unsigned int tidxj  = threadIdx.y;
 +    unsigned int tidx   = threadIdx.y * blockDim.x + threadIdx.x;
 +    unsigned int bidx   = blockIdx.x;
 +    unsigned int widx   = tidx / WARP_SIZE; /* warp index */
 +
 +    int sci, ci, cj, ci_offset,
 +        ai, aj,
 +        cij4_start, cij4_end,
 +        typei, typej,
 +        i, cii, jm, j4, nsubi, wexcl_idx;
 +    float qi, qj_f,
 +          r2, inv_r, inv_r2, inv_r6,
 +          c6, c12,
++          int_bit,
 +#ifdef CALC_ENERGIES
 +          E_lj, E_el, E_lj_p,
 +#endif
 +          F_invr;
- #if defined EL_EWALD || defined EL_RF
++    unsigned int wexcl, imask, mask_ji;
 +    float4 xqbuf;
 +    float3 xi, xj, rv, f_ij, fcj_buf, fshift_buf;
 +    float3 fci_buf[NCL_PER_SUPERCL];    /* i force buffer */
 +    nbnxn_sci_t nb_sci;
 +
 +    /* shmem buffer for i x+q pre-loading */
 +    extern __shared__  float4 xqib[];
 +    /* shmem j force buffer */
 +    float *f_buf = (float *)(xqib + NCL_PER_SUPERCL * CL_SIZE);
 +
 +    nb_sci      = pl_sci[bidx];         /* my i super-cluster's index = current bidx */
 +    sci         = nb_sci.sci;           /* super-cluster */
 +    cij4_start  = nb_sci.cj4_ind_start; /* first ...*/
 +    cij4_end    = nb_sci.cj4_ind_end;   /* and last index of j clusters */
 +
 +    /* Store the i-atom x and q in shared memory */
 +    /* Note: the thread indexing here is inverted with respect to the
 +       inner-loop as this results in slightly higher performance */
 +    ci = sci * NCL_PER_SUPERCL + tidxi;
 +    ai = ci * CL_SIZE + tidxj;
 +    xqib[tidxi * CL_SIZE + tidxj] = xq[ai] + shift_vec[nb_sci.shift];
 +    __syncthreads();
 +
 +    for(ci_offset = 0; ci_offset < NCL_PER_SUPERCL; ci_offset++)
 +    {
 +        fci_buf[ci_offset] = make_float3(0.0f);
 +    }
 +
 +#ifdef CALC_ENERGIES
 +    E_lj = 0.0f;
 +    E_el = 0.0f;
 +
- #ifdef PRUNE_NBL
-             imask_prune = imask;
- #endif
++#if defined EL_EWALD_TAB || defined EL_RF
 +    if (nb_sci.shift == CENTRAL && pl_cj4[cij4_start].cj[0] == sci*NCL_PER_SUPERCL)
 +    {
 +        /* we have the diagonal: add the charge self interaction energy term */
 +        for (i = 0; i < NCL_PER_SUPERCL; i++)
 +        {
 +            qi    = xqib[i * CL_SIZE + tidxi].w;
 +            E_el += qi*qi;
 +        }
 +        /* divide the self term equally over the j-threads */
 +        E_el /= CL_SIZE;
 +#ifdef EL_RF
 +        E_el *= -nbparam.epsfac*0.5f*c_rf;
 +#else
 +        E_el *= -nbparam.epsfac*beta*M_FLOAT_1_SQRTPI; /* last factor 1/sqrt(pi) */
 +#endif
 +    }
 +#endif
 +#endif
 +
 +    /* skip central shifts when summing shift forces */
 +    if (nb_sci.shift == CENTRAL)
 +    {
 +        bCalcFshift = false;
 +    }
 +
 +    fshift_buf = make_float3(0.0f);
 +
 +    /* loop over the j clusters = seen by any of the atoms in the current super-cluster */
 +    for (j4 = cij4_start; j4 < cij4_end; j4++)
 +    {
 +        wexcl_idx   = pl_cj4[j4].imei[widx].excl_ind;
 +        imask       = pl_cj4[j4].imei[widx].imask;
 +        wexcl       = excl[wexcl_idx].pair[(tidx) & (WARP_SIZE - 1)];
 +
 +#ifndef PRUNE_NBL
 +        if (imask)
 +#endif
 +        {
-                 imask_j = (imask >> (jm * CL_SIZE)) & supercl_interaction_mask;
-                 if (imask_j)
 +            /* nvcc >v4.1 doesn't like this loop, it refuses to unroll it */
 +#if CUDA_VERSION >= 4010
 +            #pragma unroll 4
 +#endif
 +            for (jm = 0; jm < NBNXN_GPU_JGROUP_SIZE; jm++)
 +            {
-                     nsubi = __popc(imask_j);
++                mask_ji = (imask >> (jm * CL_SIZE)) & supercl_interaction_mask;
++                if (mask_ji)
 +                {
-                         i = __ffs(imask_j) - 1;
-                         imask_j &= ~(1U << i);
++                    nsubi = __popc(mask_ji);
 +
 +                    cj      = pl_cj4[j4].cj[jm];
 +                    aj      = cj * CL_SIZE + tidxj;
 +
 +                    /* load j atom data */
 +                    xqbuf   = xq[aj];
 +                    xj      = make_float3(xqbuf.x, xqbuf.y, xqbuf.z);
 +                    qj_f    = nbparam.epsfac * xqbuf.w;
 +                    typej   = atom_types[aj];
 +
 +                    fcj_buf = make_float3(0.0f);
 +
 +                    /* loop over the i-clusters in sci */
 +                    /* #pragma unroll 8
 +                       -- nvcc doesn't like my code, it refuses to unroll it
 +                       which is a pity because here unrolling could help.  */
 +                    for (cii = 0; cii < nsubi; cii++)
 +                    {
-                             imask_prune &= ~(1U << (jm * NCL_PER_SUPERCL + i));
++                        i = __ffs(mask_ji) - 1;
++                        mask_ji &= ~(1U << i);
 +
 +                        ci_offset   = i;    /* i force buffer offset */
 +
 +                        ci      = sci * NCL_PER_SUPERCL + i; /* i cluster index */
 +                        ai      = ci * CL_SIZE + tidxi;      /* i atom index */
 +
 +                        /* all threads load an atom from i cluster ci into shmem! */
 +                        xqbuf   = xqib[i * CL_SIZE + tidxi];
 +                        xi      = make_float3(xqbuf.x, xqbuf.y, xqbuf.z);
 +
 +                        /* distance between i and j atoms */
 +                        rv      = xi - xj;
 +                        r2      = norm2(rv);
 +
 +#ifdef PRUNE_NBL
 +                        /* If _none_ of the atoms pairs are in cutoff range,
 +                               the bit corresponding to the current
 +                               cluster-pair in imask gets set to 0. */
 +                        if (!__any(r2 < rlist_sq))
 +                        {
-                         int_bit = ((wexcl >> (jm * NCL_PER_SUPERCL + i)) & 1);
++                            imask &= ~(1U << (jm * NCL_PER_SUPERCL + i));
 +                        }
 +#endif
 +
- #if defined EL_EWALD || defined EL_RF
++                        int_bit = ((wexcl >> (jm * NCL_PER_SUPERCL + i)) & 1) ? 1.0f : 0.0f;
 +
 +                        /* cutoff & exclusion check */
- #if defined EL_EWALD || defined EL_RF
++#if defined EL_EWALD_TAB || defined EL_RF
 +                        if (r2 < rcoulomb_sq *
 +                            (nb_sci.shift != CENTRAL || ci != cj || tidxj > tidxi))
 +#else
 +                        if (r2 < rcoulomb_sq * int_bit)
 +#endif
 +                        {
 +                            /* load the rest of the i-atom parameters */
 +                            qi      = xqbuf.w;
 +                            typei   = atom_types[ai];
 +
 +                            /* LJ 6*C6 and 12*C12 */
 +                            c6      = tex1Dfetch(tex_nbfp, 2 * (ntypes * typei + typej));
 +                            c12     = tex1Dfetch(tex_nbfp, 2 * (ntypes * typei + typej) + 1);
 +
 +                            /* avoid NaN for excluded pairs at r=0 */
 +                            r2      += (1.0f - int_bit) * NBNXN_AVOID_SING_R2_INC;
 +
 +                            inv_r   = rsqrt(r2);
 +                            inv_r2  = inv_r * inv_r;
 +                            inv_r6  = inv_r2 * inv_r2 * inv_r2;
-                                 E_lj_p  = int_bit * (c12 * (inv_r6 * inv_r6 - lj_shift * lj_shift) * 0.08333333f - c6 * (inv_r6 - lj_shift) * 0.16666667f);
++#if defined EL_EWALD_TAB || defined EL_RF
 +                            /* We could mask inv_r2, but with Ewald
 +                             * masking both inv_r6 and F_invr is faster */
 +                            inv_r6  *= int_bit;
 +#endif
 +
 +                            F_invr  = inv_r6 * (c12 * inv_r6 - c6) * inv_r2;
 +
 +#ifdef CALC_ENERGIES
-                                 /* this enables twin-range cut-offs (rvdw < rcoulomb <= rlist) */
-                                 vdw_in_range = (r2 < rvdw_sq) ? 1.0f : 0.0f;
-                                 F_invr  *= vdw_in_range;
++                            E_lj_p  = int_bit * (c12 * (inv_r6 * inv_r6 - lj_shift * lj_shift) * 0.08333333f - c6 * (inv_r6 - lj_shift) * 0.16666667f);
 +#endif
 +
 +#ifdef VDW_CUTOFF_CHECK
-                                 E_lj_p  *= vdw_in_range;
++                            /* this enables twin-range cut-offs (rvdw < rcoulomb <= rlist) */
++                            vdw_in_range = (r2 < rvdw_sq) ? 1.0f : 0.0f;
++                            F_invr  *= vdw_in_range;
 +#ifdef CALC_ENERGIES
-                                 E_lj    += E_lj_p;
++                            E_lj_p  *= vdw_in_range;
 +#endif
 +#endif
 +#ifdef CALC_ENERGIES
- #ifdef EL_EWALD
++                            E_lj    += E_lj_p;
 +#endif
 +
 +
 +#ifdef EL_CUTOFF
 +                            F_invr  += qi * qj_f * inv_r2 * inv_r;
 +#endif
 +#ifdef EL_RF
 +                            F_invr  += qi * qj_f * (int_bit*inv_r2 * inv_r - two_k_rf);
 +#endif
- #endif
++#ifdef EL_EWALD_TAB
 +                            F_invr  += qi * qj_f * (int_bit*inv_r2 - interpolate_coulomb_force_r(r2 * inv_r, coulomb_tab_scale)) * inv_r;
- #ifdef EL_EWALD
++#endif /* EL_EWALD_TAB */
 +
 +#ifdef CALC_ENERGIES
 +#ifdef EL_CUTOFF
 +                            E_el    += qi * qj_f * (inv_r - c_rf);
 +#endif
 +#ifdef EL_RF
 +                            E_el    += qi * qj_f * (int_bit*inv_r + 0.5f * two_k_rf * r2 - c_rf);
 +#endif
-             pl_cj4[j4].imei[widx].imask = imask_prune;
++#ifdef EL_EWALD_TAB
 +                            /* 1.0f - erff is faster than erfcf */
 +                            E_el    += qi * qj_f * (inv_r * (int_bit - erff(r2 * inv_r * beta)) - int_bit * ewald_shift);
 +#endif
 +#endif
 +                            f_ij    = rv * F_invr;
 +
 +                            /* accumulate j forces in registers */
 +                            fcj_buf -= f_ij;
 +
 +                            /* accumulate i forces in registers */
 +                            fci_buf[ci_offset] += f_ij;
 +                        }
 +                    }
 +
 +                    /* store j forces in shmem */
 +                    f_buf[                  tidx] = fcj_buf.x;
 +                    f_buf[    FBUF_STRIDE + tidx] = fcj_buf.y;
 +                    f_buf[2 * FBUF_STRIDE + tidx] = fcj_buf.z;
 +
 +                    /* reduce j forces */
 +                    reduce_force_j_generic(f_buf, f, tidxi, tidxj, aj);
 +                }
 +            }
 +#ifdef PRUNE_NBL
 +            /* Update the imask with the new one which does not contain the
 +               out of range clusters anymore. */
++            pl_cj4[j4].imei[widx].imask = imask;
 +#endif
 +        }
 +    }
 +
 +    /* reduce i forces */
 +    for(ci_offset = 0; ci_offset < NCL_PER_SUPERCL; ci_offset++)
 +    {
 +        ai  = (sci * NCL_PER_SUPERCL + ci_offset) * CL_SIZE + tidxi;
 +        f_buf[                  tidx] = fci_buf[ci_offset].x;
 +        f_buf[    FBUF_STRIDE + tidx] = fci_buf[ci_offset].y;
 +        f_buf[2 * FBUF_STRIDE + tidx] = fci_buf[ci_offset].z;
 +        __syncthreads();
 +        reduce_force_i(f_buf, f,
 +                       &fshift_buf, bCalcFshift,
 +                       tidxi, tidxj, ai);
 +        __syncthreads();
 +    }
 +
 +    /* add up local shift forces into global mem */
 +    if (bCalcFshift && tidxj == 0)
 +    {
 +        atomicAdd(&atdat.fshift[nb_sci.shift].x, fshift_buf.x);
 +        atomicAdd(&atdat.fshift[nb_sci.shift].y, fshift_buf.y);
 +        atomicAdd(&atdat.fshift[nb_sci.shift].z, fshift_buf.z);
 +    }
 +
 +#ifdef CALC_ENERGIES
 +    /* flush the energies to shmem and reduce them */
 +    f_buf[              tidx] = E_lj;
 +    f_buf[FBUF_STRIDE + tidx] = E_el;
 +    reduce_energy_pow2(f_buf + (tidx & WARP_SIZE), e_lj, e_el, tidx & ~WARP_SIZE);
 +#endif
 +}
index d057fc550a158c4fec3640a668bf026df5c78798,0000000000000000000000000000000000000000..6c2b258e47db58ad8d7ad5b3fe58cb684fbba268
mode 100644,000000..100644
--- /dev/null
@@@ -1,305 -1,0 +1,345 @@@
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +
 +/* Note that floating-point constants in CUDA code should be suffixed
 + * with f (e.g. 0.5f), to stop the compiler producing intermediate
 + * code that is in double precision.
 + */
 +
 +#include "../../gmxlib/cuda_tools/vectype_ops.cuh"
 +
 +#ifndef NBNXN_CUDA_KERNEL_UTILS_CUH
 +#define NBNXN_CUDA_KERNEL_UTILS_CUH
 +
 +#define WARP_SIZE_POW2_EXPONENT     (5)
 +#define CL_SIZE_POW2_EXPONENT       (3)  /* change this together with GPU_NS_CLUSTER_SIZE !*/
 +#define CL_SIZE_SQ                  (CL_SIZE * CL_SIZE)
 +#define FBUF_STRIDE                 (CL_SIZE_SQ)
 +
 +/*! i-cluster interaction mask for a super-cluster with all NCL_PER_SUPERCL bits set */
 +const unsigned supercl_interaction_mask = ((1U << NCL_PER_SUPERCL) - 1U);
 +
 +/*! Interpolate Ewald coulomb force using the table through the tex_nbfp texture.
 + *  Original idea: OpenMM
 + */
 +static inline __device__
 +float interpolate_coulomb_force_r(float r, float scale)
 +{
 +    float   normalized = scale * r;
 +    int     index = (int) normalized;
 +    float   fract2 = normalized - index;
 +    float   fract1 = 1.0f - fract2;
 +
 +    return  fract1 * tex1Dfetch(tex_coulomb_tab, index)
 +            + fract2 * tex1Dfetch(tex_coulomb_tab, index + 1);
 +}
 +
++/*! Calculate analytical Ewald correction term. */
++static inline __device__
++float pmecorrF(float z2)
++{
++    const float FN6 = -1.7357322914161492954e-8f;
++    const float FN5 = 1.4703624142580877519e-6f;
++    const float FN4 = -0.000053401640219807709149f;
++    const float FN3 = 0.0010054721316683106153f;
++    const float FN2 = -0.019278317264888380590f;
++    const float FN1 = 0.069670166153766424023f;
++    const float FN0 = -0.75225204789749321333f;
++
++    const float FD4 = 0.0011193462567257629232f;
++    const float FD3 = 0.014866955030185295499f;
++    const float FD2 = 0.11583842382862377919f;
++    const float FD1 = 0.50736591960530292870f;
++    const float FD0 = 1.0f;
++
++    float       z4;
++    float       polyFN0,polyFN1,polyFD0,polyFD1;
++
++    z4          = z2*z2;
++
++    polyFD0     = FD4*z4 + FD2;
++    polyFD1     = FD3*z4 + FD1;
++    polyFD0     = polyFD0*z4 + FD0;
++    polyFD0     = polyFD1*z2 + polyFD0;
++
++    polyFD0     = 1.0f/polyFD0;
++
++    polyFN0     = FN6*z4 + FN4;
++    polyFN1     = FN5*z4 + FN3;
++    polyFN0     = polyFN0*z4 + FN2;
++    polyFN1     = polyFN1*z4 + FN1;
++    polyFN0     = polyFN0*z4 + FN0;
++    polyFN0     = polyFN1*z2 + polyFN0;
++
++    return polyFN0*polyFD0;
++}
++
 +/*! Final j-force reduction; this generic implementation works with
 + *  arbitrary array sizes.
 + */
 +static inline __device__
 +void reduce_force_j_generic(float *f_buf, float3 *fout,
 +                            int tidxi, int tidxj, int aidx)
 +{
 +    if (tidxi == 0)
 +    {
 +        float3 f = make_float3(0.0f);
 +        for (int j = tidxj * CL_SIZE; j < (tidxj + 1) * CL_SIZE; j++)
 +        {
 +            f.x += f_buf[                  j];
 +            f.y += f_buf[    FBUF_STRIDE + j];
 +            f.z += f_buf[2 * FBUF_STRIDE + j];
 +        }
 +
 +        atomicAdd(&fout[aidx], f);
 +    }
 +}
 +
 +/*! Final j-force reduction; this implementation only with power of two
 + *  array sizes and with sm >= 3.0
 + */
 +#if __CUDA_ARCH__ >= 300
 +static inline __device__
 +void reduce_force_j_warp_shfl(float3 f, float3 *fout,
 +                              int tidxi, int aidx)
 +{
 +    int i;
 +
 +#pragma unroll 3
 +    for (i = 0; i < 3; i++)
 +    {
 +        f.x += __shfl_down(f.x, 1<<i);
 +        f.y += __shfl_down(f.y, 1<<i);
 +        f.z += __shfl_down(f.z, 1<<i);
 +    }
 +
 +    /* Write the reduced j-force on one thread for each j */
 +    if (tidxi == 0)
 +    {
 +        atomicAdd(&fout[aidx], f);
 +    }
 +}
 +#endif
 +
 +/*! Final i-force reduction; this generic implementation works with
 + *  arbitrary array sizes.
 + */
 +static inline __device__
 +void reduce_force_i_generic(float *f_buf, float3 *fout,
 +                            float3 *fshift_buf, bool bCalcFshift,
 +                            int tidxi, int tidxj, int aidx)
 +{
 +    if (tidxj == 0)
 +    {
 +        float3 f = make_float3(0.0f);
 +        for (int j = tidxi; j < CL_SIZE_SQ; j += CL_SIZE)
 +        {
 +            f.x += f_buf[                  j];
 +            f.y += f_buf[    FBUF_STRIDE + j];
 +            f.z += f_buf[2 * FBUF_STRIDE + j];
 +        }
 +
 +        atomicAdd(&fout[aidx], f);
 +
 +        if (bCalcFshift)
 +        {
 +            *fshift_buf += f;
 +        }
 +    }
 +}
 +
 +/*! Final i-force reduction; this implementation works only with power of two
 + *  array sizes.
 + */
 +static inline __device__
 +void reduce_force_i_pow2(volatile float *f_buf, float3 *fout,
 +                         float3 *fshift_buf, bool bCalcFshift,
 +                         int tidxi, int tidxj, int aidx)
 +{
 +    int     i, j;
 +    float3  f = make_float3(0.0f);
 +
 +    /* Reduce the initial CL_SIZE values for each i atom to half
 +     * every step by using CL_SIZE * i threads.
 +     * Can't just use i as loop variable because than nvcc refuses to unroll.
 +     */
 +    i = CL_SIZE/2;
 +    # pragma unroll 5
 +    for (j = CL_SIZE_POW2_EXPONENT - 1; j > 0; j--)
 +    {
 +        if (tidxj < i)
 +        {
 +
 +            f_buf[                  tidxj * CL_SIZE + tidxi] += f_buf[                  (tidxj + i) * CL_SIZE + tidxi];
 +            f_buf[    FBUF_STRIDE + tidxj * CL_SIZE + tidxi] += f_buf[    FBUF_STRIDE + (tidxj + i) * CL_SIZE + tidxi];
 +            f_buf[2 * FBUF_STRIDE + tidxj * CL_SIZE + tidxi] += f_buf[2 * FBUF_STRIDE + (tidxj + i) * CL_SIZE + tidxi];
 +        }
 +        i >>= 1;
 +    }
 +
 +    /* i == 1, last reduction step, writing to global mem */
 +    if (tidxj == 0)
 +    {
 +        f.x = f_buf[                  tidxj * CL_SIZE + tidxi] + f_buf[                  (tidxj + i) * CL_SIZE + tidxi];
 +        f.y = f_buf[    FBUF_STRIDE + tidxj * CL_SIZE + tidxi] + f_buf[    FBUF_STRIDE + (tidxj + i) * CL_SIZE + tidxi];
 +        f.z = f_buf[2 * FBUF_STRIDE + tidxj * CL_SIZE + tidxi] + f_buf[2 * FBUF_STRIDE + (tidxj + i) * CL_SIZE + tidxi];
 +
 +        atomicAdd(&fout[aidx], f);
 +
 +        if (bCalcFshift)
 +        {
 +            *fshift_buf += f;
 +        }
 +    }
 +}
 +
 +/*! Final i-force reduction wrapper; calls the generic or pow2 reduction depending
 + *  on whether the size of the array to be reduced is power of two or not.
 + */
 +static inline __device__
 +void reduce_force_i(float *f_buf, float3 *f,
 +                    float3 *fshift_buf, bool bCalcFshift,
 +                    int tidxi, int tidxj, int ai)
 +{
 +    if ((CL_SIZE & (CL_SIZE - 1)))
 +    {
 +        reduce_force_i_generic(f_buf, f, fshift_buf, bCalcFshift, tidxi, tidxj, ai);
 +    }
 +    else
 +    {
 +        reduce_force_i_pow2(f_buf, f, fshift_buf, bCalcFshift, tidxi, tidxj, ai);
 +    }
 +}
 +
 +/*! Final i-force reduction; this implementation works only with power of two
 + *  array sizes and with sm >= 3.0
 + */
 +#if __CUDA_ARCH__ >= 300
 +static inline __device__
 +void reduce_force_i_warp_shfl(float3 fin, float3 *fout,
 +                              float3 *fshift_buf, bool bCalcFshift,
 +                              int tidxj, int aidx)
 +{
 +    int j;
 +
 +#pragma unroll 2
 +    for (j = 0; j < 2; j++)
 +    {
 +        fin.x += __shfl_down(fin.x,  CL_SIZE<<j);
 +        fin.y += __shfl_down(fin.y,  CL_SIZE<<j);
 +        fin.z += __shfl_down(fin.z,  CL_SIZE<<j);
 +    }
 +
 +    /* The first thread in the warp writes the reduced force */
 +    if (tidxj == 0 || tidxj == 4)
 +    {
 +        atomicAdd(&fout[aidx], fin);
 +
 +        if (bCalcFshift)
 +        {
 +            fshift_buf->x += fin.x;
 +            fshift_buf->y += fin.y;
 +            fshift_buf->z += fin.z;
 +        }
 +    }
 +}
 +#endif
 +
 +/*! Energy reduction; this implementation works only with power of two
 + *  array sizes.
 + */
 +static inline __device__
 +void reduce_energy_pow2(volatile float *buf,
 +                        float *e_lj, float *e_el,
 +                        unsigned int tidx)
 +{
 +    int     i, j;
 +    float   e1, e2;
 +
 +    i = WARP_SIZE/2;
 +
 +    /* Can't just use i as loop variable because than nvcc refuses to unroll. */
 +# pragma unroll 10
 +    for (j = WARP_SIZE_POW2_EXPONENT - 1; j > 0; j--)
 +    {
 +        if (tidx < i)
 +        {
 +            buf[              tidx] += buf[              tidx + i];
 +            buf[FBUF_STRIDE + tidx] += buf[FBUF_STRIDE + tidx + i];
 +        }
 +        i >>= 1;
 +    }
 +
 +    /* last reduction step, writing to global mem */
 +    if (tidx == 0)
 +    {
 +        e1 = buf[              tidx] + buf[              tidx + i];
 +        e2 = buf[FBUF_STRIDE + tidx] + buf[FBUF_STRIDE + tidx + i];
 +
 +        atomicAdd(e_lj, e1);
 +        atomicAdd(e_el, e2);
 +    }
 +}
 +
 +/*! Energy reduction; this implementation works only with power of two
 + *  array sizes and with sm >= 3.0
 + */
 +#if __CUDA_ARCH__ >= 300
 +static inline __device__
 +void reduce_energy_warp_shfl(float E_lj, float E_el,
 +                             float *e_lj, float *e_el,
 +                             int tidx)
 +{
 +    int i, sh;
 +
 +    sh = 1;
 +#pragma unroll 5
 +    for (i = 0; i < 5; i++)
 +    {
 +        E_lj += __shfl_down(E_lj,sh);
 +        E_el += __shfl_down(E_el,sh);
 +        sh += sh;
 +    }
 +
 +    /* The first thread in the warp writes the reduced energies */
 +    if (tidx == 0 || tidx == WARP_SIZE)
 +    {
 +        atomicAdd(e_lj,E_lj);
 +        atomicAdd(e_el,E_el);
 +    }
 +}
 +#endif /* __CUDA_ARCH__ */
 +
 +#endif /* NBNXN_CUDA_KERNEL_UTILS_CUH */
index a18f905bbf8a03c31bdfeebc198d6a4442a61b89,0000000000000000000000000000000000000000..c8566a12ece804dd4ae5f8631012f507d9cb8357
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,99 @@@
-  *  This header has the sole purpose of generating kernels for the different
-  *  type of electrostatics supported: Cut-off, Reaction-Field, and Ewald/PME;
-  *  the latter has a twin-range cut-off version (rcoul!=rvdw) which enables
-  *  PME tuning (otherwise in the Verlet scheme rcoul==rvdw).
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +
 +/*! \file
-  *  (No include fence as it is meant to be included multiple times.)
++ *  This header has the sole purpose of generating kernels for the supported
++ *  electrostatics types: cut-off, reaction-field, Ewald, and tabulated Ewald.
 + *
- /* Cut-Off */
++ *  The Ewald kernels have twin-range cut-off versions with rcoul != rvdw which
++ *  require an extra distance check to enable  PP-PME load balancing
++ *  (otherwise, by default rcoul == rvdw).
++ *
++ *  NOTE: No include fence as it is meant to be included multiple times.
 + */
 +
- /* Reaction-Field */
++/* Analytical plain cut-off kernels */
 +#define EL_CUTOFF
 +#define NB_KERNEL_FUNC_NAME(x,...) x##_cutoff##__VA_ARGS__
 +#include "nbnxn_cuda_kernel_legacy.cuh"
 +#include "nbnxn_cuda_kernel.cuh"
 +#undef EL_CUTOFF
 +#undef NB_KERNEL_FUNC_NAME
 +
- /* Ewald */
- #define EL_EWALD
++/* Analytical reaction-field kernels */
 +#define EL_RF
 +#define NB_KERNEL_FUNC_NAME(x,...) x##_rf##__VA_ARGS__
 +#include "nbnxn_cuda_kernel_legacy.cuh"
 +#include "nbnxn_cuda_kernel.cuh"
 +#undef EL_RF
 +#undef NB_KERNEL_FUNC_NAME
 +
- #include "nbnxn_cuda_kernel_legacy.cuh"
++/* Analytical Ewald interaction kernels
++ * NOTE: no legacy kernels with analytical Ewald.
++ */
++#define EL_EWALD_ANA
 +#define NB_KERNEL_FUNC_NAME(x,...) x##_ewald##__VA_ARGS__
- #undef EL_EWALD
 +#include "nbnxn_cuda_kernel.cuh"
- /* Ewald with twin-range cut-off */
- #define EL_EWALD
++#undef EL_EWALD_ANA
 +#undef NB_KERNEL_FUNC_NAME
 +
- #undef EL_EWALD
++/* Analytical Ewald interaction kernels with twin-range cut-off
++ * NOTE: no legacy kernels with analytical Ewald.
++ */
++#define EL_EWALD_ANA
 +#define VDW_CUTOFF_CHECK
 +#define NB_KERNEL_FUNC_NAME(x,...) x##_ewald_twin##__VA_ARGS__
++#include "nbnxn_cuda_kernel.cuh"
++#undef EL_EWALD_ANA
++#undef VDW_CUTOFF_CHECK
++#undef NB_KERNEL_FUNC_NAME
++
++/* Tabulated Ewald interaction kernels */
++#define EL_EWALD_TAB
++#define NB_KERNEL_FUNC_NAME(x,...) x##_ewald_tab##__VA_ARGS__
++#include "nbnxn_cuda_kernel_legacy.cuh"
++#include "nbnxn_cuda_kernel.cuh"
++#undef EL_EWALD_TAB
++#undef NB_KERNEL_FUNC_NAME
++
++/* Tabulated Ewald interaction kernels with twin-range cut-off */
++#define EL_EWALD_TAB
++#define VDW_CUTOFF_CHECK
++#define NB_KERNEL_FUNC_NAME(x,...) x##_ewald_tab_twin##__VA_ARGS__
 +#include "nbnxn_cuda_kernel_legacy.cuh"
 +#include "nbnxn_cuda_kernel.cuh"
++#undef EL_EWALD_TAB
 +#undef VDW_CUTOFF_CHECK
 +#undef NB_KERNEL_FUNC_NAME
index 9d6e70c0b19a0159076d97ae89a2d117d3bea01b,0000000000000000000000000000000000000000..b7d0dd29d1cc8f9c7718f8e4282e80513426b14e
mode 100644,000000..100644
--- /dev/null
@@@ -1,200 -1,0 +1,215 @@@
- /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
++/*
++ * This file is part of the GROMACS molecular simulation package.
 + *
-  *
-  *                This source code is part of
-  *
-  *                 G   R   O   M   A   C   S
-  *
-  *          GROningen MAchine for Chemical Simulations
-  *
-  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
++ * Copyright (c) 2012,2013, by the GROMACS development team, led by
++ * David van der Spoel, Berk Hess, Erik Lindahl, and including many
++ * others, as listed in the AUTHORS file in the top-level source
++ * directory and at http://www.gromacs.org.
 + *
-  * 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
++ * 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.
 + *
-  * 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.
++ * 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.
 + *
-  * 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.
++ * 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.
 + *
-  * For more info, check our website at http://www.gromacs.org
++ * 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.
 + *
-  * And Hey:
-  * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
++ * To help us fund GROMACS development, we humbly ask that you cite
++ * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +#ifndef NBNXN_CUDA_TYPES_H
 +#define NBNXN_CUDA_TYPES_H
 +
 +#include "types/nbnxn_pairlist.h"
 +#include "types/nbnxn_cuda_types_ext.h"
 +#include "../../gmxlib/cuda_tools/cudautils.cuh"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
- /** Types of electrostatics available in the CUDA nonbonded force kernels. */
++/** Types of electrostatics implementations available in the CUDA non-bonded
++ *  force kernels. These represent both the electrostatics types implemented
++ *  by the kernels (cut-off, RF, and Ewald - a subset of what's defined in
++ *  enums.h) as well as encode implementation details analytical/tabulated
++ *  and single or twin cut-off (for Ewald kernels).
++ *  Note that the cut-off and RF kernels have only analytical flavor and unlike
++ *  in the CPU kernels, the tabulated kernels are ATM Ewald-only.
++ *
++ *  The order of pointers to different electrostatic kernels defined in
++ *  nbnxn_cuda.cu by the nb_default_kfunc_ptr and nb_legacy_kfunc_ptr arrays
++ *  should match the order of enumerated types below. */
 +enum {
-     eelCuEWALD, eelCuEWALD_TWIN, eelCuRF, eelCuCUT, eelCuNR
++    eelCuCUT, eelCuRF, eelCuEWALD_TAB, eelCuEWALD_TAB_TWIN, eelCuEWALD_ANA, eelCuEWALD_ANA_TWIN, eelCuNR
 +};
 +
++/** Kernel flavors with different set of optimizations: default for CUDA <=v4.1
++ *  compilers and legacy for earlier, 3.2 and 4.0 CUDA compilers. */
 +enum {
-     eNbnxnCuKDefault, eNbnxnCuKLegacy, eNbnxnCuKOld, eNbnxnCuKNR
++    eNbnxnCuKDefault, eNbnxnCuKLegacy, eNbnxnCuKNR
 +};
 +
 +#define NBNXN_KVER_OLD(k)      (k == eNbnxnCuKOld)
 +#define NBNXN_KVER_LEGACY(k)   (k == eNbnxnCuKLegacy)
 +#define NBNXN_KVER_DEFAULT(k)  (k == eNbnxnCuKDefault)
 +
 +/* Non-bonded kernel versions. */
 +
 +/*  All structs prefixed with "cu_" hold data used in GPU calculations and
 + *  are passed to the kernels, except cu_timers_t. */
 +typedef struct cu_plist     cu_plist_t;
 +typedef struct cu_atomdata  cu_atomdata_t;
 +typedef struct cu_nbparam   cu_nbparam_t;
 +typedef struct cu_timers    cu_timers_t;
 +typedef struct nb_staging   nb_staging_t;
 +
 +
 +/** Staging area for temporary data. The energies get downloaded here first,
 + *   before getting added to the CPU-side aggregate values.
 + */
 +struct nb_staging
 +{
 +    float   *e_lj;      /**< LJ energy            */
 +    float   *e_el;      /**< electrostatic energy */
 +    float3  *fshift;    /**< shift forces         */
 +};
 +
 +/** Nonbonded atom data -- both inputs and outputs. */
 +struct cu_atomdata
 +{
 +    int      natoms;            /**< number of atoms                              */
 +    int      natoms_local;      /**< number of local atoms                        */
 +    int      nalloc;            /**< allocation size for the atom data (xq, f)    */
 +
 +    float4  *xq;                /**< atom coordinates + charges, size natoms      */
 +    float3  *f;                 /**< force output array, size natoms              */
 +    /* TODO: try float2 for the energies */
 +    float   *e_lj,              /**< LJ energy output, size 1                     */
 +            *e_el;              /**< Electrostatics energy input, size 1          */
 +
 +    float3  *fshift;            /**< shift forces                                 */
 +
 +    int      ntypes;            /**< number of atom types                         */
 +    int     *atom_types;        /**< atom type indices, size natoms               */
 +
 +    float3  *shift_vec;         /**< shifts                                       */
 +    bool     bShiftVecUploaded; /**< true if the shift vector has been uploaded   */
 +};
 +
 +/** Parameters required for the CUDA nonbonded calculations. */
 +struct cu_nbparam
 +{
 +    int      eeltype;       /**< type of electrostatics                             */
 +
 +    float    epsfac;        /**< charge multiplication factor                       */
 +    float    c_rf,          /**< Reaction-field/plain cutoff electrostatics const.  */
 +             two_k_rf;      /**< Reaction-field electrostatics constant             */
 +    float    ewald_beta;    /**< Ewald/PME parameter                                */
 +    float    sh_ewald;      /**< Ewald/PME  correction term                         */
 +    float    rvdw_sq;       /**< VdW cut-off                                        */
 +    float    rcoulomb_sq;   /**< Coulomb cut-off                                    */
 +    float    rlist_sq;      /**< pair-list cut-off                                  */
 +    float    sh_invrc6;     /**< LJ potential correction term                       */
 +
 +    float   *nbfp;          /**< nonbonded parameter table with C6/C12 pairs        */
 +
 +    /* Ewald Coulomb force table data */
 +    int      coulomb_tab_size;  /**< table size (s.t. it fits in texture cache)     */
 +    float    coulomb_tab_scale; /**< table scale/spacing                            */
 +    float   *coulomb_tab;       /**< pointer to the table in the device memory      */
 +};
 +
 +/** Pair list data */
 +struct cu_plist
 +{
 +    int              na_c;        /**< number of atoms per cluster                  */
 +
 +    int              nsci;        /**< size of sci, # of i clusters in the list     */
 +    int              sci_nalloc;  /**< allocation size of sci                       */
 +    nbnxn_sci_t     *sci;         /**< list of i-cluster ("super-clusters")         */
 +
 +    int              ncj4;        /**< total # of 4*j clusters                      */
 +    int              cj4_nalloc;  /**< allocation size of cj4                       */
 +    nbnxn_cj4_t     *cj4;         /**< 4*j cluster list, contains j cluster number
 +                                       and index into the i cluster list            */
 +    nbnxn_excl_t    *excl;        /**< atom interaction bits                        */
 +    int              nexcl;       /**< count for excl                               */
 +    int              excl_nalloc; /**< allocation size of excl                      */
 +
 +    bool             bDoPrune;    /**< true if pair-list pruning needs to be
 +                                       done during the  current step                */
 +};
 +
 +/** CUDA events used for timing GPU kernels and H2D/D2H transfers.
 + * The two-sized arrays hold the local and non-local values and should always
 + * be indexed with eintLocal/eintNonlocal.
 + */
 +struct cu_timers
 +{
 +    cudaEvent_t start_atdat;     /**< start event for atom data transfer (every PS step)             */
 +    cudaEvent_t stop_atdat;      /**< stop event for atom data transfer (every PS step)              */
 +    cudaEvent_t start_nb_h2d[2]; /**< start events for x/q H2D transfers (l/nl, every step)          */
 +    cudaEvent_t stop_nb_h2d[2];  /**< stop events for x/q H2D transfers (l/nl, every step)           */
 +    cudaEvent_t start_nb_d2h[2]; /**< start events for f D2H transfer (l/nl, every step)             */
 +    cudaEvent_t stop_nb_d2h[2];  /**< stop events for f D2H transfer (l/nl, every step)              */
 +    cudaEvent_t start_pl_h2d[2]; /**< start events for pair-list H2D transfers (l/nl, every PS step) */
 +    cudaEvent_t stop_pl_h2d[2];  /**< start events for pair-list H2D transfers (l/nl, every PS step) */
 +    cudaEvent_t start_nb_k[2];   /**< start event for non-bonded kernels (l/nl, every step)          */
 +    cudaEvent_t stop_nb_k[2];    /**< stop event non-bonded kernels (l/nl, every step)               */
 +};
 +
 +/** Main data structure for CUDA nonbonded force calculations. */
 +struct nbnxn_cuda
 +{
 +    cuda_dev_info_t *dev_info;       /**< CUDA device information                              */
 +    int              kernel_ver;     /**< The version of the kernel to be executed on the
 +                                          device in use, possible values: eNbnxnCuK*           */
 +    bool             bUseTwoStreams; /**< true if doing both local/non-local NB work on GPU    */
 +    bool             bUseStreamSync; /**< true if the standard cudaStreamSynchronize is used
 +                                          and not memory polling-based waiting                 */
 +    cu_atomdata_t   *atdat;          /**< atom data                                            */
 +    cu_nbparam_t    *nbparam;        /**< parameters required for the non-bonded calc.         */
 +    cu_plist_t      *plist[2];       /**< pair-list data structures (local and non-local)      */
 +    nb_staging_t     nbst;           /**< staging area where fshift/energies get downloaded    */
 +
 +    cudaStream_t     stream[2];      /**< local and non-local GPU streams                      */
 +
 +    /** events used for synchronization */
 +    cudaEvent_t    nonlocal_done;   /**< event triggered when the non-local non-bonded kernel
 +                                       is done (and the local transfer can proceed)            */
 +    cudaEvent_t    misc_ops_done;   /**< event triggered when the operations that precede the
 +                                         main force calculations are done (e.g. buffer 0-ing) */
 +
 +    /* NOTE: With current CUDA versions (<=5.0) timing doesn't work with multiple
 +     * concurrent streams, so we won't time if both l/nl work is done on GPUs.
 +     * Timer init/uninit is still done even with timing off so only the condition
 +     * setting bDoTime needs to be change if this CUDA "feature" gets fixed. */
 +    bool             bDoTime;       /**< True if event-based timing is enabled.               */
 +    cu_timers_t     *timers;        /**< CUDA event-based timers.                             */
 +    wallclock_gpu_t *timings;       /**< Timing data.                                         */
 +};
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif  /* NBNXN_CUDA_TYPES_H */
index 319178e066611a71cc6d1a844d8e5fee978515c0,0000000000000000000000000000000000000000..37c45fb62ecacbed4e521eb62990d6f04abe0e16
mode 100644,000000..100644
--- /dev/null
@@@ -1,262 -1,0 +1,253 @@@
- #if GMX_NBNXN_SIMD_BITWIDTH == 128
- #define GMX_MM128_HERE
- #else
- #if GMX_NBNXN_SIMD_BITWIDTH == 256
- #define GMX_MM256_HERE
- #else
- #error "unsupported GMX_NBNXN_SIMD_BITWIDTH"
- #endif
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustr
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +
 +#ifndef _nbnxn_internal_h
 +#define _nbnxn_internal_h
 +
 +#include "typedefs.h"
 +#include "domdec.h"
 +#include "gmx_cyclecounter.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +
 +#ifdef GMX_X86_SSE2
 +/* Use 4-way SIMD for, always, single precision bounding box calculations */
 +#define NBNXN_SEARCH_BB_SSE
 +#endif
 +
 +
 +#ifdef GMX_NBNXN_SIMD
 +/* Memory alignment in bytes as required by SIMD aligned loads/stores */
 +#define NBNXN_MEM_ALIGN  (GMX_NBNXN_SIMD_BITWIDTH/8)
 +#else
 +/* No alignment required, but set it so we can call the same routines */
 +#define NBNXN_MEM_ALIGN  32
 +#endif
 +
 +
 +/* A pair-search grid struct for one domain decomposition zone */
 +typedef struct {
 +    rvec     c0;               /* The lower corner of the (local) grid        */
 +    rvec     c1;               /* The upper corner of the (local) grid        */
 +    real     atom_density;     /* The atom number density for the local grid  */
 +
 +    gmx_bool bSimple;          /* Is this grid simple or super/sub            */
 +    int      na_c;             /* Number of atoms per cluster                 */
 +    int      na_cj;            /* Number of atoms for list j-clusters         */
 +    int      na_sc;            /* Number of atoms per super-cluster           */
 +    int      na_c_2log;        /* 2log of na_c                                */
 +
 +    int      ncx;              /* Number of (super-)cells along x             */
 +    int      ncy;              /* Number of (super-)cells along y             */
 +    int      nc;               /* Total number of (super-)cells               */
 +
 +    real     sx;               /* x-size of a (super-)cell                    */
 +    real     sy;               /* y-size of a (super-)cell                    */
 +    real     inv_sx;           /* 1/sx                                        */
 +    real     inv_sy;           /* 1/sy                                        */
 +
 +    int      cell0;            /* Index in nbs->cell corresponding to cell 0  */
 +
 +    int     *cxy_na;           /* The number of atoms for each column in x,y  */
 +    int     *cxy_ind;          /* Grid (super)cell index, offset from cell0   */
 +    int      cxy_nalloc;       /* Allocation size for cxy_na and cxy_ind      */
 +
 +    int     *nsubc;            /* The number of sub cells for each super cell */
 +    float   *bbcz;             /* Bounding boxes in z for the super cells     */
 +    float   *bb;               /* 3D bounding boxes for the sub cells         */
 +    float   *bbj;              /* 3D j-b.boxes for SSE-double or AVX-single   */
 +    int     *flags;            /* Flag for the super cells                    */
 +    int      nc_nalloc;        /* Allocation size for the pointers above      */
 +
 +    float   *bbcz_simple;      /* bbcz for simple grid converted from super   */
 +    float   *bb_simple;        /* bb for simple grid converted from super     */
 +    int     *flags_simple;     /* flags for simple grid converted from super  */
 +    int      nc_nalloc_simple; /* Allocation size for the pointers above   */
 +
 +    int      nsubc_tot;        /* Total number of subcell, used for printing  */
 +} nbnxn_grid_t;
 +
 +#ifdef GMX_NBNXN_SIMD
- #undef GMX_MM128_HERE
- #undef GMX_MM256_HERE
++#ifdef GMX_NBNXN_HALF_WIDTH_SIMD
++#define GMX_USE_HALF_WIDTH_SIMD_HERE
 +#endif
 +#include "gmx_simd_macros.h"
 +
 +typedef struct nbnxn_x_ci_simd_4xn {
 +    /* The i-cluster coordinates for simple search */
 +    gmx_mm_pr ix_SSE0, iy_SSE0, iz_SSE0;
 +    gmx_mm_pr ix_SSE1, iy_SSE1, iz_SSE1;
 +    gmx_mm_pr ix_SSE2, iy_SSE2, iz_SSE2;
 +    gmx_mm_pr ix_SSE3, iy_SSE3, iz_SSE3;
 +} nbnxn_x_ci_simd_4xn_t;
 +
 +typedef struct nbnxn_x_ci_simd_2xnn {
 +    /* The i-cluster coordinates for simple search */
 +    gmx_mm_pr ix_SSE0, iy_SSE0, iz_SSE0;
 +    gmx_mm_pr ix_SSE2, iy_SSE2, iz_SSE2;
 +} nbnxn_x_ci_simd_2xnn_t;
 +
 +#endif
 +
 +/* Working data for the actual i-supercell during pair search */
 +typedef struct nbnxn_list_work {
 +    gmx_cache_protect_t     cp0;   /* Protect cache between threads               */
 +
 +    float                  *bb_ci; /* The bounding boxes, pbc shifted, for each cluster */
 +    real                   *x_ci;  /* The coordinates, pbc shifted, for each atom       */
 +#ifdef GMX_NBNXN_SIMD
 +    nbnxn_x_ci_simd_4xn_t  *x_ci_simd_4xn;
 +    nbnxn_x_ci_simd_2xnn_t *x_ci_simd_2xnn;
 +#endif
 +    int                     cj_ind;          /* The current cj_ind index for the current list     */
 +    int                     cj4_init;        /* The first unitialized cj4 block                   */
 +
 +    float                  *d2;              /* Bounding box distance work array                  */
 +
 +    nbnxn_cj_t             *cj;              /* The j-cell list                                   */
 +    int                     cj_nalloc;       /* Allocation size of cj                             */
 +
 +    int                     ncj_noq;         /* Nr. of cluster pairs without Coul for flop count  */
 +    int                     ncj_hlj;         /* Nr. of cluster pairs with 1/2 LJ for flop count   */
 +
 +    int                    *sort;            /* Sort index                    */
 +    int                     sort_nalloc;     /* Allocation size of sort       */
 +
 +    nbnxn_sci_t            *sci_sort;        /* Second sci array, for sorting */
 +    int                     sci_sort_nalloc; /* Allocation size of sci_sort   */
 +
 +    gmx_cache_protect_t     cp1;             /* Protect cache between threads               */
 +} nbnxn_list_work_t;
 +
 +/* Function type for setting the i-atom coordinate working data */
 +typedef void
 +    gmx_icell_set_x_t (int ci,
 +                       real shx, real shy, real shz,
 +                       int na_c,
 +                       int stride, const real *x,
 +                       nbnxn_list_work_t *work);
 +
 +static gmx_icell_set_x_t icell_set_x_simple;
 +#ifdef GMX_NBNXN_SIMD
 +static gmx_icell_set_x_t icell_set_x_simple_simd_4xn;
 +static gmx_icell_set_x_t icell_set_x_simple_simd_2xnn;
 +#endif
 +static gmx_icell_set_x_t icell_set_x_supersub;
 +#ifdef NBNXN_SEARCH_SSE
 +static gmx_icell_set_x_t icell_set_x_supersub_sse8;
 +#endif
 +
 +/* Local cycle count struct for profiling */
 +typedef struct {
 +    int          count;
 +    gmx_cycles_t c;
 +    gmx_cycles_t start;
 +} nbnxn_cycle_t;
 +
 +/* Local cycle count enum for profiling */
 +enum {
 +    enbsCCgrid, enbsCCsearch, enbsCCcombine, enbsCCreducef, enbsCCnr
 +};
 +
 +/* Thread-local work struct, contains part of nbnxn_grid_t */
 +typedef struct {
 +    gmx_cache_protect_t  cp0;
 +
 +    int                 *cxy_na;
 +    int                  cxy_na_nalloc;
 +
 +    int                 *sort_work;
 +    int                  sort_work_nalloc;
 +
 +    nbnxn_buffer_flags_t buffer_flags; /* Flags for force buffer access */
 +
 +    int                  ndistc;       /* Number of distance checks for flop counting */
 +
 +    nbnxn_cycle_t        cc[enbsCCnr];
 +
 +    gmx_cache_protect_t  cp1;
 +} nbnxn_search_work_t;
 +
 +/* Main pair-search struct, contains the grid(s), not the pair-list(s) */
 +typedef struct nbnxn_search {
 +    int                 ePBC;            /* PBC type enum                              */
 +    matrix              box;             /* The periodic unit-cell                     */
 +
 +    gmx_bool            DomDec;          /* Are we doing domain decomposition?         */
 +    ivec                dd_dim;          /* Are we doing DD in x,y,z?                  */
 +    gmx_domdec_zones_t *zones;           /* The domain decomposition zones        */
 +
 +    int                 ngrid;           /* The number of grids, equal to #DD-zones    */
 +    nbnxn_grid_t       *grid;            /* Array of grids, size ngrid                 */
 +    int                *cell;            /* Actual allocated cell array for all grids  */
 +    int                 cell_nalloc;     /* Allocation size of cell                    */
 +    int                *a;               /* Atom index for grid, the inverse of cell   */
 +    int                 a_nalloc;        /* Allocation size of a                       */
 +
 +    int                 natoms_local;    /* The local atoms run from 0 to natoms_local */
 +    int                 natoms_nonlocal; /* The non-local atoms run from natoms_local
 +                                          * to natoms_nonlocal */
 +
 +    gmx_bool             print_cycles;
 +    int                  search_count;
 +    nbnxn_cycle_t        cc[enbsCCnr];
 +
 +    gmx_icell_set_x_t   *icell_set_x; /* Function for setting i-coords    */
 +
 +    int                  nthread_max; /* Maximum number of threads for pair-search  */
 +    nbnxn_search_work_t *work;        /* Work array, size nthread_max          */
 +} nbnxn_search_t_t;
 +
 +
 +static void nbs_cycle_start(nbnxn_cycle_t *cc)
 +{
 +    cc->start = gmx_cycles_read();
 +}
 +
 +static void nbs_cycle_stop(nbnxn_cycle_t *cc)
 +{
 +    cc->c += gmx_cycles_read() - cc->start;
 +    cc->count++;
 +}
 +
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif
index 1aea092f52a5568d5eee52a3f4003b0c63b1685a,0000000000000000000000000000000000000000..e63a81796ab9557854b6e5c65568f9036bdfd0b4
mode 100644,000000..100644
--- /dev/null
@@@ -1,752 -1,0 +1,759 @@@
-  * this can be faster with blendv (only available with SSE4.1 and later).
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2009, The GROMACS Development Team
 + * Copyright (c) 2012, by the GROMACS development team, led by
 + * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 + * others, as listed in the AUTHORS file in the top-level source
 + * directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +/* This is the innermost loop contents for the 4 x N atom SIMD kernel.
 + * This flavor of the kernel duplicates the data for N j-particles in
 + * 2xN wide SIMD registers to do operate on 2 i-particles at once.
 + * This leads to 4/2=2 sets of most instructions. Therefore we call
 + * this kernel 2x(N+N) = 2xnn
 + *
 + * This 2xnn kernel is basically the 4xn equivalent with half the registers
 + * and instructions removed.
 + *
 + * An alternative would be to load to different cluster of N j-particles
 + * into SIMD registers, giving a 4x(N+N) kernel. This doubles the amount
 + * of instructions, which could lead to better scheduling. But we actually
 + * observed worse scheduling for the AVX-256 4x8 normal analytical PME
 + * kernel, which has a lower pair throughput than 2x(4+4) with gcc 4.7.
 + * It could be worth trying this option, but it takes some more effort.
 + * This 2xnn kernel is basically the 4xn equivalent with
 + */
 +
 +
 +/* When calculating RF or Ewald interactions we calculate the electrostatic
 + * forces on excluded atom pairs here in the non-bonded loops.
 + * But when energies and/or virial is required we calculate them
 + * separately to as then it is easier to separate the energy and virial
 + * contributions.
 + */
 +#if defined CHECK_EXCLS && defined CALC_COULOMB
 +#define EXCL_FORCES
 +#endif
 +
 +/* Without exclusions and energies we only need to mask the cut-off,
- #if !(defined CHECK_EXCLS || defined CALC_ENERGIES) && defined GMX_X86_SSE4_1 && !defined COUNT_PAIRS
++ * this can be faster with blendv.
 + */
-     gmx_mm_pr  int_SSE0;
-     gmx_mm_pr  int_SSE2;
++#if !(defined CHECK_EXCLS || defined CALC_ENERGIES) && defined GMX_HAVE_SIMD_BLENDV && !defined COUNT_PAIRS
 +/* With RF and tabulated Coulomb we replace cmp+and with sub+blendv.
 + * With gcc this is slower, except for RF on Sandy Bridge.
 + * Tested with gcc 4.6.2, 4.6.3 and 4.7.1.
 + */
 +#if (defined CALC_COUL_RF || defined CALC_COUL_TAB) && (!defined __GNUC__ || (defined CALC_COUL_RF && defined GMX_X86_AVX_256))
 +#define CUTOFF_BLENDV
 +#endif
 +/* With analytical Ewald we replace cmp+and+and with sub+blendv+blendv.
 + * This is only faster with icc on Sandy Bridge (PS kernel slower than gcc 4.7).
 + * Tested with icc 13.
 + */
 +#if defined CALC_COUL_EWALD && defined __INTEL_COMPILER && defined GMX_X86_AVX_256
 +#define CUTOFF_BLENDV
 +#endif
 +#endif
 +
 +{
 +    int        cj, aj, ajx, ajy, ajz;
 +
 +#ifdef ENERGY_GROUPS
 +    /* Energy group indices for two atoms packed into one int */
 +    int        egp_jj[UNROLLJ/2];
 +#endif
 +
 +#ifdef CHECK_EXCLS
 +    /* Interaction (non-exclusion) mask of all 1's or 0's */
-     gmx_mm_pr  jxSSE, jySSE, jzSSE;
-     gmx_mm_pr  dx_SSE0, dy_SSE0, dz_SSE0;
-     gmx_mm_pr  dx_SSE2, dy_SSE2, dz_SSE2;
-     gmx_mm_pr  tx_SSE0, ty_SSE0, tz_SSE0;
-     gmx_mm_pr  tx_SSE2, ty_SSE2, tz_SSE2;
-     gmx_mm_pr  rsq_SSE0, rinv_SSE0, rinvsq_SSE0;
-     gmx_mm_pr  rsq_SSE2, rinv_SSE2, rinvsq_SSE2;
++    gmx_mm_pr  int_S0;
++    gmx_mm_pr  int_S2;
 +#endif
 +
-     gmx_mm_pr  wco_SSE0;
-     gmx_mm_pr  wco_SSE2;
++    gmx_mm_pr  jx_S, jy_S, jz_S;
++    gmx_mm_pr  dx_S0, dy_S0, dz_S0;
++    gmx_mm_pr  dx_S2, dy_S2, dz_S2;
++    gmx_mm_pr  tx_S0, ty_S0, tz_S0;
++    gmx_mm_pr  tx_S2, ty_S2, tz_S2;
++    gmx_mm_pr  rsq_S0, rinv_S0, rinvsq_S0;
++    gmx_mm_pr  rsq_S2, rinv_S2, rinvsq_S2;
 +#ifndef CUTOFF_BLENDV
 +    /* wco: within cut-off, mask of all 1's or 0's */
-     gmx_mm_pr  wco_vdw_SSE0;
++    gmx_mm_pr  wco_S0;
++    gmx_mm_pr  wco_S2;
 +#endif
 +#ifdef VDW_CUTOFF_CHECK
-     gmx_mm_pr  wco_vdw_SSE2;
++    gmx_mm_pr  wco_vdw_S0;
 +#ifndef HALF_LJ
-     gmx_mm_pr  rinv_ex_SSE0;
-     gmx_mm_pr  rinv_ex_SSE2;
++    gmx_mm_pr  wco_vdw_S2;
 +#endif
 +#endif
 +#ifdef CALC_COULOMB
 +#ifdef CHECK_EXCLS
 +    /* 1/r masked with the interaction mask */
-     gmx_mm_pr  jq_SSE;
-     gmx_mm_pr  qq_SSE0;
-     gmx_mm_pr  qq_SSE2;
++    gmx_mm_pr  rinv_ex_S0;
++    gmx_mm_pr  rinv_ex_S2;
 +#endif
-     gmx_mm_pr  fsub_SSE0;
-     gmx_mm_pr  fsub_SSE2;
++    gmx_mm_pr  jq_S;
++    gmx_mm_pr  qq_S0;
++    gmx_mm_pr  qq_S2;
 +#ifdef CALC_COUL_TAB
 +    /* The force (PME mesh force) we need to subtract from 1/r^2 */
-     gmx_mm_pr  brsq_SSE0, brsq_SSE2;
-     gmx_mm_pr  ewcorr_SSE0, ewcorr_SSE2;
++    gmx_mm_pr  fsub_S0;
++    gmx_mm_pr  fsub_S2;
 +#endif
 +#ifdef CALC_COUL_EWALD
-     gmx_mm_pr  frcoul_SSE0;
-     gmx_mm_pr  frcoul_SSE2;
++    gmx_mm_pr  brsq_S0, brsq_S2;
++    gmx_mm_pr  ewcorr_S0, ewcorr_S2;
 +#endif
 +
 +    /* frcoul = (1/r - fsub)*r */
-     gmx_mm_pr  r_SSE0, rs_SSE0, rf_SSE0, frac_SSE0;
-     gmx_mm_pr  r_SSE2, rs_SSE2, rf_SSE2, frac_SSE2;
++    gmx_mm_pr  frcoul_S0;
++    gmx_mm_pr  frcoul_S2;
 +#ifdef CALC_COUL_TAB
 +    /* For tables: r, rs=r/sp, rf=floor(rs), frac=rs-rf */
- #if !(defined GMX_MM256_HERE && defined GMX_DOUBLE)
-     gmx_epi32  ti_SSE0, ti_SSE2;
- #else
-     __m128i    ti_SSE0, ti_SSE2;
- #endif
++    gmx_mm_pr  r_S0, rs_S0, rf_S0, frac_S0;
++    gmx_mm_pr  r_S2, rs_S2, rf_S2, frac_S2;
 +    /* Table index: rs truncated to an int */
-     gmx_mm_pr  ctab0_SSE0, ctab1_SSE0;
-     gmx_mm_pr  ctab0_SSE2, ctab1_SSE2;
++    gmx_epi32  ti_S0, ti_S2;
 +    /* Linear force table values */
-     gmx_mm_pr  ctabv_SSE0;
-     gmx_mm_pr  ctabv_SSE2;
++    gmx_mm_pr  ctab0_S0, ctab1_S0;
++    gmx_mm_pr  ctab0_S2, ctab1_S2;
 +#ifdef CALC_ENERGIES
 +    /* Quadratic energy table value */
-     gmx_mm_pr  vc_sub_SSE0;
-     gmx_mm_pr  vc_sub_SSE2;
++    gmx_mm_pr  ctabv_S0;
++    gmx_mm_pr  ctabv_S2;
 +#endif
 +#endif
 +#if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
 +    /* The potential (PME mesh) we need to subtract from 1/r */
-     gmx_mm_pr  vcoul_SSE0;
-     gmx_mm_pr  vcoul_SSE2;
++    gmx_mm_pr  vc_sub_S0;
++    gmx_mm_pr  vc_sub_S2;
 +#endif
 +#ifdef CALC_ENERGIES
 +    /* Electrostatic potential */
-     gmx_mm_pr  fscal_SSE0;
-     gmx_mm_pr  fscal_SSE2;
++    gmx_mm_pr  vcoul_S0;
++    gmx_mm_pr  vcoul_S2;
 +#endif
 +#endif
 +    /* The force times 1/r */
-     gmx_mm_pr  hsig_j_SSE, seps_j_SSE;
++    gmx_mm_pr  fscal_S0;
++    gmx_mm_pr  fscal_S2;
 +
 +#ifdef CALC_LJ
 +#ifdef LJ_COMB_LB
 +    /* LJ sigma_j/2 and sqrt(epsilon_j) */
-     gmx_mm_pr  sig_SSE0, eps_SSE0;
++    gmx_mm_pr  hsig_j_S, seps_j_S;
 +    /* LJ sigma_ij and epsilon_ij */
-     gmx_mm_pr  sig_SSE2, eps_SSE2;
++    gmx_mm_pr  sig_S0, eps_S0;
 +#ifndef HALF_LJ
-     gmx_mm_pr  sig2_SSE0, sig6_SSE0;
++    gmx_mm_pr  sig_S2, eps_S2;
 +#endif
 +#ifdef CALC_ENERGIES
-     gmx_mm_pr  sig2_SSE2, sig6_SSE2;
++    gmx_mm_pr  sig2_S0, sig6_S0;
 +#ifndef HALF_LJ
-     gmx_mm_pr  c6s_j_SSE, c12s_j_SSE;
++    gmx_mm_pr  sig2_S2, sig6_S2;
 +#endif
 +#endif /* LJ_COMB_LB */
 +#endif /* CALC_LJ */
 +
 +#ifdef LJ_COMB_GEOM
-     gmx_mm_pr  c6_SSE0, c12_SSE0;
++    gmx_mm_pr  c6s_j_S, c12s_j_S;
 +#endif
 +
 +#if defined LJ_COMB_GEOM || defined LJ_COMB_LB
 +    /* Index for loading LJ parameters, complicated when interleaving */
 +    int         aj2;
 +#endif
 +
 +#ifndef FIX_LJ_C
 +    /* LJ C6 and C12 parameters, used with geometric comb. rule */
-     gmx_mm_pr  c6_SSE2, c12_SSE2;
++    gmx_mm_pr  c6_S0, c12_S0;
 +#ifndef HALF_LJ
-     gmx_mm_pr  rinvsix_SSE0;
++    gmx_mm_pr  c6_S2, c12_S2;
 +#endif
 +#endif
 +
 +    /* Intermediate variables for LJ calculation */
 +#ifndef LJ_COMB_LB
-     gmx_mm_pr  rinvsix_SSE2;
++    gmx_mm_pr  rinvsix_S0;
 +#ifndef HALF_LJ
-     gmx_mm_pr  sir_SSE0, sir2_SSE0, sir6_SSE0;
++    gmx_mm_pr  rinvsix_S2;
 +#endif
 +#endif
 +#ifdef LJ_COMB_LB
-     gmx_mm_pr  sir_SSE2, sir2_SSE2, sir6_SSE2;
++    gmx_mm_pr  sir_S0, sir2_S0, sir6_S0;
 +#ifndef HALF_LJ
-     gmx_mm_pr  FrLJ6_SSE0, FrLJ12_SSE0;
++    gmx_mm_pr  sir_S2, sir2_S2, sir6_S2;
 +#endif
 +#endif
 +
-     gmx_mm_pr  FrLJ6_SSE2, FrLJ12_SSE2;
++    gmx_mm_pr  FrLJ6_S0, FrLJ12_S0;
 +#ifndef HALF_LJ
-     gmx_mm_pr  VLJ6_SSE0, VLJ12_SSE0, VLJ_SSE0;
++    gmx_mm_pr  FrLJ6_S2, FrLJ12_S2;
 +#endif
 +#ifdef CALC_ENERGIES
-     gmx_mm_pr  VLJ6_SSE2, VLJ12_SSE2, VLJ_SSE2;
++    gmx_mm_pr  VLJ6_S0, VLJ12_S0, VLJ_S0;
 +#ifndef HALF_LJ
-         /* With AVX there are no integer operations, so cast to real */
-         gmx_mm_pr mask_pr = gmx_mm_castsi256_pr(_mm256_set1_epi32(l_cj[cjind].excl));
-         /* Intel Compiler version 12.1.3 20120130 is buggy: use cast.
-          * With gcc we don't need the cast, but it's faster.
-          */
- #define cast_cvt(x)  _mm256_cvtepi32_ps(_mm256_castps_si256(x))
-         int_SSE0  = gmx_cmpneq_pr(cast_cvt(gmx_and_pr(mask_pr, mask0)), zero_SSE);
-         int_SSE2  = gmx_cmpneq_pr(cast_cvt(gmx_and_pr(mask_pr, mask2)), zero_SSE);
- #undef cast_cvt
++    gmx_mm_pr  VLJ6_S2, VLJ12_S2, VLJ_S2;
 +#endif
 +#endif
 +#endif /* CALC_LJ */
 +
++    gmx_mm_hpr fjx_S, fjy_S, fjz_S;
++
 +    /* j-cluster index */
 +    cj            = l_cj[cjind].cj;
 +
 +    /* Atom indices (of the first atom in the cluster) */
 +    aj            = cj*UNROLLJ;
 +#if defined CALC_LJ && (defined LJ_COMB_GEOM || defined LJ_COMB_LB)
 +#if UNROLLJ == STRIDE
 +    aj2           = aj*2;
 +#else
 +    aj2           = (cj>>1)*2*STRIDE + (cj & 1)*UNROLLJ;
 +#endif
 +#endif
 +#if UNROLLJ == STRIDE
 +    ajx           = aj*DIM;
 +#else
 +    ajx           = (cj>>1)*DIM*STRIDE + (cj & 1)*UNROLLJ;
 +#endif
 +    ajy           = ajx + STRIDE;
 +    ajz           = ajy + STRIDE;
 +
 +#ifdef CHECK_EXCLS
 +    {
 +        /* Load integer interaction mask */
-     jxSSE         = gmx_loaddh_pr(x+ajx);
-     jySSE         = gmx_loaddh_pr(x+ajy);
-     jzSSE         = gmx_loaddh_pr(x+ajz);
++        gmx_mm_pr mask_pr_S = gmx_castsi_pr(gmx_set1_epi32(l_cj[cjind].excl));
++
++        int_S0  = gmx_checkbitmask_pr(mask_pr_S, mask_S0);
++        int_S2  = gmx_checkbitmask_pr(mask_pr_S, mask_S2);
 +    }
 +#endif
++
 +    /* load j atom coordinates */
-     dx_SSE0       = gmx_sub_pr(ix_SSE0, jxSSE);
-     dy_SSE0       = gmx_sub_pr(iy_SSE0, jySSE);
-     dz_SSE0       = gmx_sub_pr(iz_SSE0, jzSSE);
-     dx_SSE2       = gmx_sub_pr(ix_SSE2, jxSSE);
-     dy_SSE2       = gmx_sub_pr(iy_SSE2, jySSE);
-     dz_SSE2       = gmx_sub_pr(iz_SSE2, jzSSE);
++    gmx_loaddh_pr(jx_S, x+ajx);
++    gmx_loaddh_pr(jy_S, x+ajy);
++    gmx_loaddh_pr(jz_S, x+ajz);
 +
 +    /* Calculate distance */
-     rsq_SSE0      = gmx_calc_rsq_pr(dx_SSE0, dy_SSE0, dz_SSE0);
-     rsq_SSE2      = gmx_calc_rsq_pr(dx_SSE2, dy_SSE2, dz_SSE2);
++    dx_S0       = gmx_sub_pr(ix_S0, jx_S);
++    dy_S0       = gmx_sub_pr(iy_S0, jy_S);
++    dz_S0       = gmx_sub_pr(iz_S0, jz_S);
++    dx_S2       = gmx_sub_pr(ix_S2, jx_S);
++    dy_S2       = gmx_sub_pr(iy_S2, jy_S);
++    dz_S2       = gmx_sub_pr(iz_S2, jz_S);
 +
 +    /* rsq = dx*dx+dy*dy+dz*dz */
-     wco_SSE0      = gmx_cmplt_pr(rsq_SSE0, rc2_SSE);
-     wco_SSE2      = gmx_cmplt_pr(rsq_SSE2, rc2_SSE);
++    rsq_S0      = gmx_calc_rsq_pr(dx_S0, dy_S0, dz_S0);
++    rsq_S2      = gmx_calc_rsq_pr(dx_S2, dy_S2, dz_S2);
 +
 +#ifndef CUTOFF_BLENDV
-         wco_SSE0  = gmx_and_pr(wco_SSE0, diag_SSE0);
-         wco_SSE2  = gmx_and_pr(wco_SSE2, diag_SSE2);
++    wco_S0      = gmx_cmplt_pr(rsq_S0, rc2_S);
++    wco_S2      = gmx_cmplt_pr(rsq_S2, rc2_S);
 +#endif
 +
 +#ifdef CHECK_EXCLS
 +#ifdef EXCL_FORCES
 +    /* Only remove the (sub-)diagonal to avoid double counting */
 +#if UNROLLJ == UNROLLI
 +    if (cj == ci_sh)
 +    {
- #error "only UNROLLJ == UNROLLI currently supported in the joined kernels"
++        wco_S0  = gmx_and_pr(wco_S0, diag_S0);
++        wco_S2  = gmx_and_pr(wco_S2, diag_S2);
 +    }
 +#else
-       /* Remove all excluded atom pairs from the list */
-     wco_SSE0      = gmx_and_pr(wco_SSE0, int_SSE0);
-     wco_SSE2      = gmx_and_pr(wco_SSE2, int_SSE2);
++#if UNROLLJ == 2*UNROLLI
++    if (cj*2 == ci_sh)
++    {
++        wco_S0  = gmx_and_pr(wco_S0, diag0_S0);
++        wco_S2  = gmx_and_pr(wco_S2, diag0_S2);
++    }
++    else if (cj*2 + 1 == ci_sh)
++    {
++        wco_S0  = gmx_and_pr(wco_S0, diag1_S0);
++        wco_S2  = gmx_and_pr(wco_S2, diag1_S2);
++    }
++#else
++#error "only UNROLLJ == UNROLLI*(1 or 2) currently supported in 2xnn kernels"
++#endif
 +#endif
 +#else /* EXCL_FORCES */
-         real tmp[UNROLLJ];
-         for (i = 0; i < UNROLLI; i++)
++    /* No exclusion forces: remove all excluded atom pairs from the list */
++    wco_S0      = gmx_and_pr(wco_S0, int_S0);
++    wco_S2      = gmx_and_pr(wco_S2, int_S2);
 +#endif
 +#endif
 +
 +#ifdef COUNT_PAIRS
 +    {
 +        int  i, j;
-             gmx_storeu_pr(tmp, i == 0 ? wco_SSE0 : (i == 1 ? wco_SSE1 : (i == 2 ? wco_SSE2 : wco_SSE3)));
-             for (j = 0; j < UNROLLJ; j++)
++        real tmpa[2*GMX_SIMD_WIDTH_HERE], *tmp;
++        tmp = gmx_simd_align_real(tmpa);
++        for (i = 0; i < UNROLLI; i+=2)
 +        {
-     rsq_SSE0      = gmx_add_pr(rsq_SSE0, gmx_andnot_pr(int_SSE0, avoid_sing_SSE));
-     rsq_SSE2      = gmx_add_pr(rsq_SSE2, gmx_andnot_pr(int_SSE2, avoid_sing_SSE));
++            gmx_store_pr(tmp, i == 0 ? wco_S0 : wco_S2);
++            for (j = 0; j < 2*UNROLLJ; j++)
 +            {
 +                if (!(tmp[j] == 0))
 +                {
 +                    npair++;
 +                }
 +            }
 +        }
 +    }
 +#endif
 +
 +#ifdef CHECK_EXCLS
 +    /* For excluded pairs add a small number to avoid r^-6 = NaN */
-     rinv_SSE0     = gmx_invsqrt_pr(rsq_SSE0);
-     rinv_SSE2     = gmx_invsqrt_pr(rsq_SSE2);
++    rsq_S0      = gmx_add_pr(rsq_S0, gmx_andnot_pr(int_S0, avoid_sing_S));
++    rsq_S2      = gmx_add_pr(rsq_S2, gmx_andnot_pr(int_S2, avoid_sing_S));
 +#endif
 +
 +    /* Calculate 1/r */
-     jq_SSE        = gmx_loaddh_pr(q+aj);
-     qq_SSE0       = gmx_mul_pr(iq_SSE0, jq_SSE);
-     qq_SSE2       = gmx_mul_pr(iq_SSE2, jq_SSE);
++    rinv_S0     = gmx_invsqrt_pr(rsq_S0);
++    rinv_S2     = gmx_invsqrt_pr(rsq_S2);
 +
 +#ifdef CALC_COULOMB
 +    /* Load parameters for j atom */
-     load_lj_pair_params2(nbfp0, nbfp1, type, aj, c6_SSE0, c12_SSE0);
++    gmx_loaddh_pr(jq_S, q+aj);
++    qq_S0       = gmx_mul_pr(iq_S0, jq_S);
++    qq_S2       = gmx_mul_pr(iq_S2, jq_S);
 +#endif
 +
 +#ifdef CALC_LJ
 +
 +#if !defined LJ_COMB_GEOM && !defined LJ_COMB_LB && !defined FIX_LJ_C
-     load_lj_pair_params2(nbfp2, nbfp3, type, aj, c6_SSE2, c12_SSE2);
++    load_lj_pair_params2(nbfp0, nbfp1, type, aj, c6_S0, c12_S0);
 +#ifndef HALF_LJ
-     c6s_j_SSE     = gmx_loaddh_pr(ljc+aj2+0);
-     c12s_j_SSE    = gmx_loaddh_pr(ljc+aj2+STRIDE);
-     c6_SSE0       = gmx_mul_pr(c6s_SSE0, c6s_j_SSE );
++    load_lj_pair_params2(nbfp2, nbfp3, type, aj, c6_S2, c12_S2);
 +#endif
 +#endif /* not defined any LJ rule */
 +
 +#ifdef LJ_COMB_GEOM
-     c6_SSE2       = gmx_mul_pr(c6s_SSE2, c6s_j_SSE );
++    gmx_loaddh_pr(c6s_j_S,  ljc+aj2+0);
++    gmx_loaddh_pr(c12s_j_S, ljc+aj2+STRIDE);
++    c6_S0       = gmx_mul_pr(c6s_S0, c6s_j_S );
 +#ifndef HALF_LJ
-     c12_SSE0      = gmx_mul_pr(c12s_SSE0, c12s_j_SSE);
++    c6_S2       = gmx_mul_pr(c6s_S2, c6s_j_S );
 +#endif
-     c12_SSE2      = gmx_mul_pr(c12s_SSE2, c12s_j_SSE);
++    c12_S0      = gmx_mul_pr(c12s_S0, c12s_j_S);
 +#ifndef HALF_LJ
-     hsig_j_SSE    = gmx_loaddh_pr(ljc+aj2+0);
-     seps_j_SSE    = gmx_loaddh_pr(ljc+aj2+STRIDE);
++    c12_S2      = gmx_mul_pr(c12s_S2, c12s_j_S);
 +#endif
 +#endif /* LJ_COMB_GEOM */
 +
 +#ifdef LJ_COMB_LB
-     sig_SSE0      = gmx_add_pr(hsig_i_SSE0, hsig_j_SSE);
-     eps_SSE0      = gmx_mul_pr(seps_i_SSE0, seps_j_SSE);
++    gmx_loaddh_pr(hsig_j_S, ljc+aj2+0);
++    gmx_loaddh_pr(seps_j_S, ljc+aj2+STRIDE);
 +
-     sig_SSE2      = gmx_add_pr(hsig_i_SSE2, hsig_j_SSE);
-     eps_SSE2      = gmx_mul_pr(seps_i_SSE2, seps_j_SSE);
++    sig_S0      = gmx_add_pr(hsig_i_S0, hsig_j_S);
++    eps_S0      = gmx_mul_pr(seps_i_S0, seps_j_S);
 +#ifndef HALF_LJ
-     rinv_SSE0     = gmx_and_pr(rinv_SSE0, wco_SSE0);
-     rinv_SSE2     = gmx_and_pr(rinv_SSE2, wco_SSE2);
++    sig_S2      = gmx_add_pr(hsig_i_S2, hsig_j_S);
++    eps_S2      = gmx_mul_pr(seps_i_S2, seps_j_S);
 +#endif
 +#endif /* LJ_COMB_LB */
 +
 +#endif /* CALC_LJ */
 +
 +#ifndef CUTOFF_BLENDV
-     rinv_SSE0     = gmx_blendv_pr(rinv_SSE0, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE0));
-     rinv_SSE2     = gmx_blendv_pr(rinv_SSE2, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE2));
++    rinv_S0     = gmx_blendzero_pr(rinv_S0, wco_S0);
++    rinv_S2     = gmx_blendzero_pr(rinv_S2, wco_S2);
 +#else
 +    /* We only need to mask for the cut-off: blendv is faster */
-     rinvsq_SSE0   = gmx_mul_pr(rinv_SSE0, rinv_SSE0);
-     rinvsq_SSE2   = gmx_mul_pr(rinv_SSE2, rinv_SSE2);
++    rinv_S0     = gmx_blendv_pr(rinv_S0, zero_S, gmx_sub_pr(rc2_S, rsq_S0));
++    rinv_S2     = gmx_blendv_pr(rinv_S2, zero_S, gmx_sub_pr(rc2_S, rsq_S2));
 +#endif
 +
-     rinv_ex_SSE0  = gmx_and_pr(rinv_SSE0, int_SSE0);
-     rinv_ex_SSE2  = gmx_and_pr(rinv_SSE2, int_SSE2);
++    rinvsq_S0   = gmx_mul_pr(rinv_S0, rinv_S0);
++    rinvsq_S2   = gmx_mul_pr(rinv_S2, rinv_S2);
 +
 +#ifdef CALC_COULOMB
 +    /* Note that here we calculate force*r, not the usual force/r.
 +     * This allows avoiding masking the reaction-field contribution,
 +     * as frcoul is later multiplied by rinvsq which has been
 +     * masked with the cut-off check.
 +     */
 +
 +#ifdef EXCL_FORCES
 +    /* Only add 1/r for non-excluded atom pairs */
- #define     rinv_ex_SSE0    rinv_SSE0
- #define     rinv_ex_SSE2    rinv_SSE2
++    rinv_ex_S0  = gmx_blendzero_pr(rinv_S0, int_S0);
++    rinv_ex_S2  = gmx_blendzero_pr(rinv_S2, int_S2);
 +#else
 +    /* No exclusion forces, we always need 1/r */
-     frcoul_SSE0   = gmx_mul_pr(qq_SSE0, gmx_add_pr(rinv_ex_SSE0, gmx_mul_pr(rsq_SSE0, mrc_3_SSE)));
-     frcoul_SSE2   = gmx_mul_pr(qq_SSE2, gmx_add_pr(rinv_ex_SSE2, gmx_mul_pr(rsq_SSE2, mrc_3_SSE)));
++#define     rinv_ex_S0    rinv_S0
++#define     rinv_ex_S2    rinv_S2
 +#endif
 +
 +#ifdef CALC_COUL_RF
 +    /* Electrostatic interactions */
-     vcoul_SSE0    = gmx_mul_pr(qq_SSE0, gmx_add_pr(rinv_ex_SSE0, gmx_add_pr(gmx_mul_pr(rsq_SSE0, hrc_3_SSE), moh_rc_SSE)));
-     vcoul_SSE2    = gmx_mul_pr(qq_SSE2, gmx_add_pr(rinv_ex_SSE2, gmx_add_pr(gmx_mul_pr(rsq_SSE2, hrc_3_SSE), moh_rc_SSE)));
++    frcoul_S0   = gmx_mul_pr(qq_S0, gmx_add_pr(rinv_ex_S0, gmx_mul_pr(rsq_S0, mrc_3_S)));
++    frcoul_S2   = gmx_mul_pr(qq_S2, gmx_add_pr(rinv_ex_S2, gmx_mul_pr(rsq_S2, mrc_3_S)));
 +
 +#ifdef CALC_ENERGIES
-     brsq_SSE0     = gmx_mul_pr(beta2_SSE, gmx_and_pr(rsq_SSE0, wco_SSE0));
-     brsq_SSE2     = gmx_mul_pr(beta2_SSE, gmx_and_pr(rsq_SSE2, wco_SSE2));
++    vcoul_S0    = gmx_mul_pr(qq_S0, gmx_add_pr(rinv_ex_S0, gmx_add_pr(gmx_mul_pr(rsq_S0, hrc_3_S), moh_rc_S)));
++    vcoul_S2    = gmx_mul_pr(qq_S2, gmx_add_pr(rinv_ex_S2, gmx_add_pr(gmx_mul_pr(rsq_S2, hrc_3_S), moh_rc_S)));
 +#endif
 +#endif
 +
 +#ifdef CALC_COUL_EWALD
 +    /* We need to mask (or limit) rsq for the cut-off,
 +     * as large distances can cause an overflow in gmx_pmecorrF/V.
 +     */
 +#ifndef CUTOFF_BLENDV
-     brsq_SSE0     = gmx_mul_pr(beta2_SSE, gmx_blendv_pr(rsq_SSE0, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE0)));
-     brsq_SSE2     = gmx_mul_pr(beta2_SSE, gmx_blendv_pr(rsq_SSE2, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE2)));
++    brsq_S0     = gmx_mul_pr(beta2_S, gmx_blendzero_pr(rsq_S0, wco_S0));
++    brsq_S2     = gmx_mul_pr(beta2_S, gmx_blendzero_pr(rsq_S2, wco_S2));
 +#else
 +    /* Strangely, putting mul on a separate line is slower (icc 13) */
-     ewcorr_SSE0   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_SSE0), beta_SSE);
-     ewcorr_SSE2   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_SSE2), beta_SSE);
-     frcoul_SSE0   = gmx_mul_pr(qq_SSE0, gmx_add_pr(rinv_ex_SSE0, gmx_mul_pr(ewcorr_SSE0, brsq_SSE0)));
-     frcoul_SSE2   = gmx_mul_pr(qq_SSE2, gmx_add_pr(rinv_ex_SSE2, gmx_mul_pr(ewcorr_SSE2, brsq_SSE2)));
++    brsq_S0     = gmx_mul_pr(beta2_S, gmx_blendv_pr(rsq_S0, zero_S, gmx_sub_pr(rc2_S, rsq_S0)));
++    brsq_S2     = gmx_mul_pr(beta2_S, gmx_blendv_pr(rsq_S2, zero_S, gmx_sub_pr(rc2_S, rsq_S2)));
 +#endif
-     vc_sub_SSE0   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_SSE0), beta_SSE);
-     vc_sub_SSE2   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_SSE2), beta_SSE);
++    ewcorr_S0   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_S0), beta_S);
++    ewcorr_S2   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_S2), beta_S);
++    frcoul_S0   = gmx_mul_pr(qq_S0, gmx_add_pr(rinv_ex_S0, gmx_mul_pr(ewcorr_S0, brsq_S0)));
++    frcoul_S2   = gmx_mul_pr(qq_S2, gmx_add_pr(rinv_ex_S2, gmx_mul_pr(ewcorr_S2, brsq_S2)));
 +
 +#ifdef CALC_ENERGIES
-     r_SSE0        = gmx_mul_pr(rsq_SSE0, rinv_SSE0);
-     r_SSE2        = gmx_mul_pr(rsq_SSE2, rinv_SSE2);
++    vc_sub_S0   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_S0), beta_S);
++    vc_sub_S2   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_S2), beta_S);
 +#endif
 +
 +#endif /* CALC_COUL_EWALD */
 +
 +#ifdef CALC_COUL_TAB
 +    /* Electrostatic interactions */
-     rs_SSE0       = gmx_mul_pr(r_SSE0, invtsp_SSE);
-     rs_SSE2       = gmx_mul_pr(r_SSE2, invtsp_SSE);
++    r_S0        = gmx_mul_pr(rsq_S0, rinv_S0);
++    r_S2        = gmx_mul_pr(rsq_S2, rinv_S2);
 +    /* Convert r to scaled table units */
-     ti_SSE0       = gmx_cvttpr_epi32(rs_SSE0);
-     ti_SSE2       = gmx_cvttpr_epi32(rs_SSE2);
- #ifdef GMX_X86_SSE4_1
-     /* SSE4.1 floor is faster than gmx_cvtepi32_ps int->float cast */
-     rf_SSE0       = gmx_floor_pr(rs_SSE0);
-     rf_SSE2       = gmx_floor_pr(rs_SSE2);
++    rs_S0       = gmx_mul_pr(r_S0, invtsp_S);
++    rs_S2       = gmx_mul_pr(r_S2, invtsp_S);
 +    /* Truncate scaled r to an int */
-     rf_SSE0       = gmx_cvtepi32_pr(ti_SSE0);
-     rf_SSE2       = gmx_cvtepi32_pr(ti_SSE2);
++    ti_S0       = gmx_cvttpr_epi32(rs_S0);
++    ti_S2       = gmx_cvttpr_epi32(rs_S2);
++#ifdef GMX_HAVE_SIMD_FLOOR
++    rf_S0       = gmx_floor_pr(rs_S0);
++    rf_S2       = gmx_floor_pr(rs_S2);
 +#else
-     frac_SSE0     = gmx_sub_pr(rs_SSE0, rf_SSE0);
-     frac_SSE2     = gmx_sub_pr(rs_SSE2, rf_SSE2);
++    rf_S0       = gmx_cvtepi32_pr(ti_S0);
++    rf_S2       = gmx_cvtepi32_pr(ti_S2);
 +#endif
-     load_table_f(tab_coul_F, ti_SSE0, ti0, ctab0_SSE0, ctab1_SSE0);
-     load_table_f(tab_coul_F, ti_SSE2, ti2, ctab0_SSE2, ctab1_SSE2);
++    frac_S0     = gmx_sub_pr(rs_S0, rf_S0);
++    frac_S2     = gmx_sub_pr(rs_S2, rf_S2);
 +
 +    /* Load and interpolate table forces and possibly energies.
 +     * Force and energy can be combined in one table, stride 4: FDV0
 +     * or in two separate tables with stride 1: F and V
 +     * Currently single precision uses FDV0, double F and V.
 +     */
 +#ifndef CALC_ENERGIES
-     load_table_f_v(tab_coul_F, ti_SSE0, ti0, ctab0_SSE0, ctab1_SSE0, ctabv_SSE0);
-     load_table_f_v(tab_coul_F, ti_SSE2, ti2, ctab0_SSE2, ctab1_SSE2, ctabv_SSE2);
++    load_table_f(tab_coul_F, ti_S0, ti0, ctab0_S0, ctab1_S0);
++    load_table_f(tab_coul_F, ti_S2, ti2, ctab0_S2, ctab1_S2);
 +#else
 +#ifdef TAB_FDV0
-     load_table_f_v(tab_coul_F, tab_coul_V, ti_SSE0, ti0, ctab0_SSE0, ctab1_SSE0, ctabv_SSE0);
-     load_table_f_v(tab_coul_F, tab_coul_V, ti_SSE2, ti2, ctab0_SSE2, ctab1_SSE2, ctabv_SSE2);
++    load_table_f_v(tab_coul_F, ti_S0, ti0, ctab0_S0, ctab1_S0, ctabv_S0);
++    load_table_f_v(tab_coul_F, ti_S2, ti2, ctab0_S2, ctab1_S2, ctabv_S2);
 +#else
-     fsub_SSE0     = gmx_add_pr(ctab0_SSE0, gmx_mul_pr(frac_SSE0, ctab1_SSE0));
-     fsub_SSE2     = gmx_add_pr(ctab0_SSE2, gmx_mul_pr(frac_SSE2, ctab1_SSE2));
-     frcoul_SSE0   = gmx_mul_pr(qq_SSE0, gmx_sub_pr(rinv_ex_SSE0, gmx_mul_pr(fsub_SSE0, r_SSE0)));
-     frcoul_SSE2   = gmx_mul_pr(qq_SSE2, gmx_sub_pr(rinv_ex_SSE2, gmx_mul_pr(fsub_SSE2, r_SSE2)));
++    load_table_f_v(tab_coul_F, tab_coul_V, ti_S0, ti0, ctab0_S0, ctab1_S0, ctabv_S0);
++    load_table_f_v(tab_coul_F, tab_coul_V, ti_S2, ti2, ctab0_S2, ctab1_S2, ctabv_S2);
 +#endif
 +#endif
-     vc_sub_SSE0   = gmx_add_pr(ctabv_SSE0, gmx_mul_pr(gmx_mul_pr(mhalfsp_SSE, frac_SSE0), gmx_add_pr(ctab0_SSE0, fsub_SSE0)));
-     vc_sub_SSE2   = gmx_add_pr(ctabv_SSE2, gmx_mul_pr(gmx_mul_pr(mhalfsp_SSE, frac_SSE2), gmx_add_pr(ctab0_SSE2, fsub_SSE2)));
++    fsub_S0     = gmx_add_pr(ctab0_S0, gmx_mul_pr(frac_S0, ctab1_S0));
++    fsub_S2     = gmx_add_pr(ctab0_S2, gmx_mul_pr(frac_S2, ctab1_S2));
++    frcoul_S0   = gmx_mul_pr(qq_S0, gmx_sub_pr(rinv_ex_S0, gmx_mul_pr(fsub_S0, r_S0)));
++    frcoul_S2   = gmx_mul_pr(qq_S2, gmx_sub_pr(rinv_ex_S2, gmx_mul_pr(fsub_S2, r_S2)));
 +
 +#ifdef CALC_ENERGIES
-     vc_sub_SSE0   = gmx_add_pr(vc_sub_SSE0, gmx_and_pr(sh_ewald_SSE, int_SSE0));
-     vc_sub_SSE2   = gmx_add_pr(vc_sub_SSE2, gmx_and_pr(sh_ewald_SSE, int_SSE2));
++    vc_sub_S0   = gmx_add_pr(ctabv_S0, gmx_mul_pr(gmx_mul_pr(mhalfsp_S, frac_S0), gmx_add_pr(ctab0_S0, fsub_S0)));
++    vc_sub_S2   = gmx_add_pr(ctabv_S2, gmx_mul_pr(gmx_mul_pr(mhalfsp_S, frac_S2), gmx_add_pr(ctab0_S2, fsub_S2)));
 +#endif
 +#endif /* CALC_COUL_TAB */
 +
 +#if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
 +#ifndef NO_SHIFT_EWALD
 +    /* Add Ewald potential shift to vc_sub for convenience */
 +#ifdef CHECK_EXCLS
-     vc_sub_SSE0   = gmx_add_pr(vc_sub_SSE0, sh_ewald_SSE);
-     vc_sub_SSE2   = gmx_add_pr(vc_sub_SSE2, sh_ewald_SSE);
++    vc_sub_S0   = gmx_add_pr(vc_sub_S0, gmx_blendzero_pr(sh_ewald_S, int_S0));
++    vc_sub_S2   = gmx_add_pr(vc_sub_S2, gmx_blendzero_pr(sh_ewald_S, int_S2));
 +#else
-     vcoul_SSE0    = gmx_mul_pr(qq_SSE0, gmx_sub_pr(rinv_ex_SSE0, vc_sub_SSE0));
-     vcoul_SSE2    = gmx_mul_pr(qq_SSE2, gmx_sub_pr(rinv_ex_SSE2, vc_sub_SSE2));
++    vc_sub_S0   = gmx_add_pr(vc_sub_S0, sh_ewald_S);
++    vc_sub_S2   = gmx_add_pr(vc_sub_S2, sh_ewald_S);
 +#endif
 +#endif
 +
-     vcoul_SSE0    = gmx_and_pr(vcoul_SSE0, wco_SSE0);
-     vcoul_SSE2    = gmx_and_pr(vcoul_SSE2, wco_SSE2);
++    vcoul_S0    = gmx_mul_pr(qq_S0, gmx_sub_pr(rinv_ex_S0, vc_sub_S0));
++    vcoul_S2    = gmx_mul_pr(qq_S2, gmx_sub_pr(rinv_ex_S2, vc_sub_S2));
 +#endif
 +
 +#ifdef CALC_ENERGIES
 +    /* Mask energy for cut-off and diagonal */
-     wco_vdw_SSE0  = gmx_cmplt_pr(rsq_SSE0, rcvdw2_SSE);
++    vcoul_S0    = gmx_blendzero_pr(vcoul_S0, wco_S0);
++    vcoul_S2    = gmx_blendzero_pr(vcoul_S2, wco_S2);
 +#endif
 +
 +#endif /* CALC_COULOMB */
 +
 +#ifdef CALC_LJ
 +    /* Lennard-Jones interaction */
 +
 +#ifdef VDW_CUTOFF_CHECK
-     wco_vdw_SSE2  = gmx_cmplt_pr(rsq_SSE2, rcvdw2_SSE);
++    wco_vdw_S0  = gmx_cmplt_pr(rsq_S0, rcvdw2_S);
 +#ifndef HALF_LJ
- #define     wco_vdw_SSE0    wco_SSE0
- #define     wco_vdw_SSE2    wco_SSE2
++    wco_vdw_S2  = gmx_cmplt_pr(rsq_S2, rcvdw2_S);
 +#endif
 +#else
 +    /* Same cut-off for Coulomb and VdW, reuse the registers */
-     rinvsix_SSE0  = gmx_mul_pr(rinvsq_SSE0, gmx_mul_pr(rinvsq_SSE0, rinvsq_SSE0));
++#define     wco_vdw_S0    wco_S0
++#define     wco_vdw_S2    wco_S2
 +#endif
 +
 +#ifndef LJ_COMB_LB
-     rinvsix_SSE0  = gmx_and_pr(rinvsix_SSE0, int_SSE0);
++    rinvsix_S0  = gmx_mul_pr(rinvsq_S0, gmx_mul_pr(rinvsq_S0, rinvsq_S0));
 +#ifdef EXCL_FORCES
-     rinvsix_SSE2  = gmx_mul_pr(rinvsq_SSE2, gmx_mul_pr(rinvsq_SSE2, rinvsq_SSE2));
++    rinvsix_S0  = gmx_blendzero_pr(rinvsix_S0, int_S0);
 +#endif
 +#ifndef HALF_LJ
-     rinvsix_SSE2  = gmx_and_pr(rinvsix_SSE2, int_SSE2);
++    rinvsix_S2  = gmx_mul_pr(rinvsq_S2, gmx_mul_pr(rinvsq_S2, rinvsq_S2));
 +#ifdef EXCL_FORCES
-     rinvsix_SSE0  = gmx_and_pr(rinvsix_SSE0, wco_vdw_SSE0);
++    rinvsix_S2  = gmx_blendzero_pr(rinvsix_S2, int_S2);
 +#endif
 +#endif
 +#ifdef VDW_CUTOFF_CHECK
-     rinvsix_SSE2  = gmx_and_pr(rinvsix_SSE2, wco_vdw_SSE2);
++    rinvsix_S0  = gmx_blendzero_pr(rinvsix_S0, wco_vdw_S0);
 +#ifndef HALF_LJ
-     FrLJ6_SSE0    = gmx_mul_pr(c6_SSE0, rinvsix_SSE0);
++    rinvsix_S2  = gmx_blendzero_pr(rinvsix_S2, wco_vdw_S2);
 +#endif
 +#endif
-     FrLJ6_SSE2    = gmx_mul_pr(c6_SSE2, rinvsix_SSE2);
++    FrLJ6_S0    = gmx_mul_pr(c6_S0, rinvsix_S0);
 +#ifndef HALF_LJ
-     FrLJ12_SSE0   = gmx_mul_pr(c12_SSE0, gmx_mul_pr(rinvsix_SSE0, rinvsix_SSE0));
++    FrLJ6_S2    = gmx_mul_pr(c6_S2, rinvsix_S2);
 +#endif
-     FrLJ12_SSE2   = gmx_mul_pr(c12_SSE2, gmx_mul_pr(rinvsix_SSE2, rinvsix_SSE2));
++    FrLJ12_S0   = gmx_mul_pr(c12_S0, gmx_mul_pr(rinvsix_S0, rinvsix_S0));
 +#ifndef HALF_LJ
-     sir_SSE0      = gmx_mul_pr(sig_SSE0, rinv_SSE0);
++    FrLJ12_S2   = gmx_mul_pr(c12_S2, gmx_mul_pr(rinvsix_S2, rinvsix_S2));
 +#endif
 +#endif /* not LJ_COMB_LB */
 +
 +#ifdef LJ_COMB_LB
-     sir_SSE2      = gmx_mul_pr(sig_SSE2, rinv_SSE2);
++    sir_S0      = gmx_mul_pr(sig_S0, rinv_S0);
 +#ifndef HALF_LJ
-     sir2_SSE0     = gmx_mul_pr(sir_SSE0, sir_SSE0);
++    sir_S2      = gmx_mul_pr(sig_S2, rinv_S2);
 +#endif
-     sir2_SSE2     = gmx_mul_pr(sir_SSE2, sir_SSE2);
++    sir2_S0     = gmx_mul_pr(sir_S0, sir_S0);
 +#ifndef HALF_LJ
-     sir6_SSE0     = gmx_mul_pr(sir2_SSE0, gmx_mul_pr(sir2_SSE0, sir2_SSE0));
++    sir2_S2     = gmx_mul_pr(sir_S2, sir_S2);
 +#endif
-     sir6_SSE0     = gmx_and_pr(sir6_SSE0, int_SSE0);
++    sir6_S0     = gmx_mul_pr(sir2_S0, gmx_mul_pr(sir2_S0, sir2_S0));
 +#ifdef EXCL_FORCES
-     sir6_SSE2     = gmx_mul_pr(sir2_SSE2, gmx_mul_pr(sir2_SSE2, sir2_SSE2));
++    sir6_S0     = gmx_blendzero_pr(sir6_S0, int_S0);
 +#endif
 +#ifndef HALF_LJ
-     sir6_SSE2     = gmx_and_pr(sir6_SSE2, int_SSE2);
++    sir6_S2     = gmx_mul_pr(sir2_S2, gmx_mul_pr(sir2_S2, sir2_S2));
 +#ifdef EXCL_FORCES
-     sir6_SSE0     = gmx_and_pr(sir6_SSE0, wco_vdw_SSE0);
++    sir6_S2     = gmx_blendzero_pr(sir6_S2, int_S2);
 +#endif
 +#endif
 +#ifdef VDW_CUTOFF_CHECK
-     sir6_SSE2     = gmx_and_pr(sir6_SSE2, wco_vdw_SSE2);
++    sir6_S0     = gmx_blendzero_pr(sir6_S0, wco_vdw_S0);
 +#ifndef HALF_LJ
-     FrLJ6_SSE0    = gmx_mul_pr(eps_SSE0, sir6_SSE0);
++    sir6_S2     = gmx_blendzero_pr(sir6_S2, wco_vdw_S2);
 +#endif
 +#endif
-     FrLJ6_SSE2    = gmx_mul_pr(eps_SSE2, sir6_SSE2);
++    FrLJ6_S0    = gmx_mul_pr(eps_S0, sir6_S0);
 +#ifndef HALF_LJ
-     FrLJ12_SSE0   = gmx_mul_pr(FrLJ6_SSE0, sir6_SSE0);
++    FrLJ6_S2    = gmx_mul_pr(eps_S2, sir6_S2);
 +#endif
-     FrLJ12_SSE2   = gmx_mul_pr(FrLJ6_SSE2, sir6_SSE2);
++    FrLJ12_S0   = gmx_mul_pr(FrLJ6_S0, sir6_S0);
 +#ifndef HALF_LJ
-     sig2_SSE0     = gmx_mul_pr(sig_SSE0, sig_SSE0);
++    FrLJ12_S2   = gmx_mul_pr(FrLJ6_S2, sir6_S2);
 +#endif
 +#if defined CALC_ENERGIES
 +    /* We need C6 and C12 to calculate the LJ potential shift */
-     sig2_SSE2     = gmx_mul_pr(sig_SSE2, sig_SSE2);
++    sig2_S0     = gmx_mul_pr(sig_S0, sig_S0);
 +#ifndef HALF_LJ
-     sig6_SSE0     = gmx_mul_pr(sig2_SSE0, gmx_mul_pr(sig2_SSE0, sig2_SSE0));
++    sig2_S2     = gmx_mul_pr(sig_S2, sig_S2);
 +#endif
-     sig6_SSE2     = gmx_mul_pr(sig2_SSE2, gmx_mul_pr(sig2_SSE2, sig2_SSE2));
++    sig6_S0     = gmx_mul_pr(sig2_S0, gmx_mul_pr(sig2_S0, sig2_S0));
 +#ifndef HALF_LJ
-     c6_SSE0       = gmx_mul_pr(eps_SSE0, sig6_SSE0);
++    sig6_S2     = gmx_mul_pr(sig2_S2, gmx_mul_pr(sig2_S2, sig2_S2));
 +#endif
-     c6_SSE2       = gmx_mul_pr(eps_SSE2, sig6_SSE2);
++    c6_S0       = gmx_mul_pr(eps_S0, sig6_S0);
 +#ifndef HALF_LJ
-     c12_SSE0      = gmx_mul_pr(c6_SSE0, sig6_SSE0);
++    c6_S2       = gmx_mul_pr(eps_S2, sig6_S2);
 +#endif
-     c12_SSE2      = gmx_mul_pr(c6_SSE2, sig6_SSE2);
++    c12_S0      = gmx_mul_pr(c6_S0, sig6_S0);
 +#ifndef HALF_LJ
-     vctotSSE      = gmx_add_pr(vctotSSE, gmx_add_pr(vcoul_SSE0, vcoul_SSE2));
++    c12_S2      = gmx_mul_pr(c6_S2, sig6_S2);
 +#endif
 +#endif
 +#endif /* LJ_COMB_LB */
 +
 +#endif /* CALC_LJ */
 +
 +#ifdef CALC_ENERGIES
 +#ifdef ENERGY_GROUPS
 +    /* Extract the group pair index per j pair.
 +     * Energy groups are stored per i-cluster, so things get
 +     * complicated when the i- and j-cluster size don't match.
 +     */
 +    {
 +        int egps_j;
 +#if UNROLLJ == 2
 +        egps_j    = nbat->energrp[cj>>1];
 +        egp_jj[0] = ((egps_j >> ((cj & 1)*egps_jshift)) & egps_jmask)*egps_jstride;
 +#else
 +        /* We assume UNROLLI <= UNROLLJ */
 +        int jdi;
 +        for (jdi = 0; jdi < UNROLLJ/UNROLLI; jdi++)
 +        {
 +            int jj;
 +            egps_j = nbat->energrp[cj*(UNROLLJ/UNROLLI)+jdi];
 +            for (jj = 0; jj < (UNROLLI/2); jj++)
 +            {
 +                egp_jj[jdi*(UNROLLI/2)+jj] = ((egps_j >> (jj*egps_jshift)) & egps_jmask)*egps_jstride;
 +            }
 +        }
 +#endif
 +    }
 +#endif
 +
 +#ifdef CALC_COULOMB
 +#ifndef ENERGY_GROUPS
-     add_ener_grp_halves(vcoul_SSE0, vctp[0], vctp[1], egp_jj);
-     add_ener_grp_halves(vcoul_SSE2, vctp[2], vctp[3], egp_jj);
++    vctot_S      = gmx_add_pr(vctot_S, gmx_add_pr(vcoul_S0, vcoul_S2));
 +#else
-     VLJ6_SSE0     = gmx_mul_pr(sixthSSE, gmx_sub_pr(FrLJ6_SSE0, gmx_mul_pr(c6_SSE0, sh_invrc6_SSE)));
++    add_ener_grp_halves(vcoul_S0, vctp[0], vctp[1], egp_jj);
++    add_ener_grp_halves(vcoul_S2, vctp[2], vctp[3], egp_jj);
 +#endif
 +#endif
 +
 +#ifdef CALC_LJ
 +    /* Calculate the LJ energies */
-     VLJ6_SSE2     = gmx_mul_pr(sixthSSE, gmx_sub_pr(FrLJ6_SSE2, gmx_mul_pr(c6_SSE2, sh_invrc6_SSE)));
++    VLJ6_S0     = gmx_mul_pr(sixth_S, gmx_sub_pr(FrLJ6_S0, gmx_mul_pr(c6_S0, sh_invrc6_S)));
 +#ifndef HALF_LJ
-     VLJ12_SSE0    = gmx_mul_pr(twelvethSSE, gmx_sub_pr(FrLJ12_SSE0, gmx_mul_pr(c12_SSE0, sh_invrc12_SSE)));
++    VLJ6_S2     = gmx_mul_pr(sixth_S, gmx_sub_pr(FrLJ6_S2, gmx_mul_pr(c6_S2, sh_invrc6_S)));
 +#endif
-     VLJ12_SSE2    = gmx_mul_pr(twelvethSSE, gmx_sub_pr(FrLJ12_SSE2, gmx_mul_pr(c12_SSE2, sh_invrc12_SSE)));
++    VLJ12_S0    = gmx_mul_pr(twelveth_S, gmx_sub_pr(FrLJ12_S0, gmx_mul_pr(c12_S0, sh_invrc12_S)));
 +#ifndef HALF_LJ
-     VLJ_SSE0      = gmx_sub_pr(VLJ12_SSE0, VLJ6_SSE0);
++    VLJ12_S2    = gmx_mul_pr(twelveth_S, gmx_sub_pr(FrLJ12_S2, gmx_mul_pr(c12_S2, sh_invrc12_S)));
 +#endif
 +
-     VLJ_SSE2      = gmx_sub_pr(VLJ12_SSE2, VLJ6_SSE2);
++    VLJ_S0      = gmx_sub_pr(VLJ12_S0, VLJ6_S0);
 +#ifndef HALF_LJ
-     VLJ_SSE0      = gmx_and_pr(VLJ_SSE0, wco_vdw_SSE0);
++    VLJ_S2      = gmx_sub_pr(VLJ12_S2, VLJ6_S2);
 +#endif
 +    /* The potential shift should be removed for pairs beyond cut-off */
-     VLJ_SSE2      = gmx_and_pr(VLJ_SSE2, wco_vdw_SSE2);
++    VLJ_S0      = gmx_blendzero_pr(VLJ_S0, wco_vdw_S0);
 +#ifndef HALF_LJ
-     VLJ_SSE0      = gmx_and_pr(VLJ_SSE0, int_SSE0);
++    VLJ_S2      = gmx_blendzero_pr(VLJ_S2, wco_vdw_S2);
 +#endif
 +#ifdef CHECK_EXCLS
 +    /* The potential shift should be removed for excluded pairs */
-     VLJ_SSE2      = gmx_and_pr(VLJ_SSE2, int_SSE2);
++    VLJ_S0      = gmx_blendzero_pr(VLJ_S0, int_S0);
 +#ifndef HALF_LJ
-     VvdwtotSSE    = gmx_add_pr(VvdwtotSSE,
++    VLJ_S2      = gmx_blendzero_pr(VLJ_S2, int_S2);
 +#endif
 +#endif
 +#ifndef ENERGY_GROUPS
-                                gmx_add_pr(VLJ_SSE0, VLJ_SSE2)
++    Vvdwtot_S    = gmx_add_pr(Vvdwtot_S,
 +#ifndef HALF_LJ
-                                VLJ_SSE0
++                              gmx_add_pr(VLJ_S0, VLJ_S2)
 +#else
-                                );
++                              VLJ_S0
 +#endif
-     add_ener_grp_halves(VLJ_SSE0, vvdwtp[0], vvdwtp[1], egp_jj);
++                              );
 +#else
-     add_ener_grp_halves(VLJ_SSE2, vvdwtp[2], vvdwtp[3], egp_jj);
++    add_ener_grp_halves(VLJ_S0, vvdwtp[0], vvdwtp[1], egp_jj);
 +#ifndef HALF_LJ
-     fscal_SSE0    = gmx_mul_pr(rinvsq_SSE0,
++    add_ener_grp_halves(VLJ_S2, vvdwtp[2], vvdwtp[3], egp_jj);
 +#endif
 +#endif
 +#endif /* CALC_LJ */
 +#endif /* CALC_ENERGIES */
 +
 +#ifdef CALC_LJ
-                                gmx_add_pr(frcoul_SSE0,
++    fscal_S0    = gmx_mul_pr(rinvsq_S0,
 +#ifdef CALC_COULOMB
-                                (
++                             gmx_add_pr(frcoul_S0,
 +#else
-                                           gmx_sub_pr(FrLJ12_SSE0, FrLJ6_SSE0)));
++                             (
 +#endif
-     fscal_SSE0    = gmx_mul_pr(rinvsq_SSE0, frcoul_SSE0);
++                              gmx_sub_pr(FrLJ12_S0, FrLJ6_S0)));
 +#else
-     fscal_SSE2    = gmx_mul_pr(rinvsq_SSE2,
++    fscal_S0    = gmx_mul_pr(rinvsq_S0, frcoul_S0);
 +#endif /* CALC_LJ */
 +#if defined CALC_LJ && !defined HALF_LJ
-                                gmx_add_pr(frcoul_SSE2,
++    fscal_S2    = gmx_mul_pr(rinvsq_S2,
 +#ifdef CALC_COULOMB
-                                (
++                             gmx_add_pr(frcoul_S2,
 +#else
-                                           gmx_sub_pr(FrLJ12_SSE2, FrLJ6_SSE2)));
++                             (
 +#endif
-     fscal_SSE2    = gmx_mul_pr(rinvsq_SSE2, frcoul_SSE2);
++                              gmx_sub_pr(FrLJ12_S2, FrLJ6_S2)));
 +#else
 +    /* Atom 2 and 3 don't have LJ, so only add Coulomb forces */
-     tx_SSE0       = gmx_mul_pr(fscal_SSE0, dx_SSE0);
-     tx_SSE2       = gmx_mul_pr(fscal_SSE2, dx_SSE2);
-     ty_SSE0       = gmx_mul_pr(fscal_SSE0, dy_SSE0);
-     ty_SSE2       = gmx_mul_pr(fscal_SSE2, dy_SSE2);
-     tz_SSE0       = gmx_mul_pr(fscal_SSE0, dz_SSE0);
-     tz_SSE2       = gmx_mul_pr(fscal_SSE2, dz_SSE2);
++    fscal_S2    = gmx_mul_pr(rinvsq_S2, frcoul_S2);
 +#endif
 +
 +    /* Calculate temporary vectorial force */
-     fix_SSE0      = gmx_add_pr(fix_SSE0, tx_SSE0);
-     fix_SSE2      = gmx_add_pr(fix_SSE2, tx_SSE2);
-     fiy_SSE0      = gmx_add_pr(fiy_SSE0, ty_SSE0);
-     fiy_SSE2      = gmx_add_pr(fiy_SSE2, ty_SSE2);
-     fiz_SSE0      = gmx_add_pr(fiz_SSE0, tz_SSE0);
-     fiz_SSE2      = gmx_add_pr(fiz_SSE2, tz_SSE2);
++    tx_S0       = gmx_mul_pr(fscal_S0, dx_S0);
++    tx_S2       = gmx_mul_pr(fscal_S2, dx_S2);
++    ty_S0       = gmx_mul_pr(fscal_S0, dy_S0);
++    ty_S2       = gmx_mul_pr(fscal_S2, dy_S2);
++    tz_S0       = gmx_mul_pr(fscal_S0, dz_S0);
++    tz_S2       = gmx_mul_pr(fscal_S2, dz_S2);
 +
 +    /* Increment i atom force */
-     gmx_store_hpr(f+ajx,
-                   gmx_sub_hpr( gmx_load_hpr(f+ajx), gmx_sum4_hpr(tx_SSE0, tx_SSE2) ));
-     gmx_store_hpr(f+ajy,
-                   gmx_sub_hpr( gmx_load_hpr(f+ajy), gmx_sum4_hpr(ty_SSE0, ty_SSE2) ));
-     gmx_store_hpr(f+ajz,
-                   gmx_sub_hpr( gmx_load_hpr(f+ajz), gmx_sum4_hpr(tz_SSE0, tz_SSE2) ));
++    fix_S0      = gmx_add_pr(fix_S0, tx_S0);
++    fix_S2      = gmx_add_pr(fix_S2, tx_S2);
++    fiy_S0      = gmx_add_pr(fiy_S0, ty_S0);
++    fiy_S2      = gmx_add_pr(fiy_S2, ty_S2);
++    fiz_S0      = gmx_add_pr(fiz_S0, tz_S0);
++    fiz_S2      = gmx_add_pr(fiz_S2, tz_S2);
 +
 +    /* Decrement j atom force */
- #undef  rinv_ex_SSE0
- #undef  rinv_ex_SSE2
++    gmx_load_hpr(fjx_S, f+ajx);
++    gmx_load_hpr(fjy_S, f+ajy);
++    gmx_load_hpr(fjz_S, f+ajz);
++    gmx_store_hpr(f+ajx, gmx_sub_hpr(fjx_S, gmx_sum4_hpr(tx_S0, tx_S2)));
++    gmx_store_hpr(f+ajy, gmx_sub_hpr(fjy_S, gmx_sum4_hpr(ty_S0, ty_S2)));
++    gmx_store_hpr(f+ajz, gmx_sub_hpr(fjz_S, gmx_sum4_hpr(tz_S0, tz_S2)));
 +}
 +
- #undef  wco_vdw_SSE0
- #undef  wco_vdw_SSE2
++#undef  rinv_ex_S0
++#undef  rinv_ex_S2
 +
++#undef  wco_vdw_S0
++#undef  wco_vdw_S2
 +
 +#undef  CUTOFF_BLENDV
 +
 +#undef  EXCL_FORCES
index d4ea4474d505d9bf9051962e3e397a833049a4bb,0000000000000000000000000000000000000000..20b2e0f4bb737ba9fcbec8e018a4a5068870d96f
mode 100644,000000..100644
--- /dev/null
@@@ -1,720 -1,0 +1,747 @@@
- /* GMX_MM256_HERE should be set before including this file */
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2009, The GROMACS Development Team
 + * Copyright (c) 2012, by the GROMACS development team, led by
 + * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 + * others, as listed in the AUTHORS file in the top-level source
 + * directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
- #if defined GMX_MM256_HERE
- #define STRIDE     4
- #endif
++
++/* Include the full width SIMD macros */
 +#include "gmx_simd_macros.h"
 +
++
++/* Define a few macros for half-width SIMD */
++#if defined GMX_X86_AVX_256 && !defined GMX_DOUBLE
++
++/* Half-width SIMD real type */
++#define gmx_mm_hpr  __m128
++
++/* Half-width SIMD operations */
++/* Load reals at half-width aligned pointer b into half-width SIMD register a */
++#define gmx_load_hpr(a, b)    a = _mm_load_ps(b)
++/* Load one real at pointer b into half-width SIMD register a */
++#define gmx_load1_hpr(a, b)   a = _mm_load1_ps(b)
++/* Load one real at b and one real at b+1 into halves of a, respectively */
++#define gmx_load1p1_pr(a, b)  a = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load1_ps(b)), _mm_load1_ps(b+1), 0x1)
++/* Load reals at half-width aligned pointer b into two halves of a */
++#define gmx_loaddh_pr(a, b)   a = gmx_mm256_load4_ps(b)
++/* To half-width SIMD register b into half width aligned memory a */
++#define gmx_store_hpr(a, b)       _mm_store_ps(a, b)
++#define gmx_add_hpr               _mm_add_ps
++#define gmx_sub_hpr               _mm_sub_ps
++/* Horizontal sum over a half SIMD register */
++#define gmx_sum4_hpr              gmx_mm256_sum4h_m128
++
++#else
++#error "Half-width SIMD macros are not yet defined"
++#endif
++
++
 +#define SUM_SIMD4(x) (x[0]+x[1]+x[2]+x[3])
 +
 +#define UNROLLI    NBNXN_CPU_CLUSTER_I_SIZE
 +#define UNROLLJ    (GMX_SIMD_WIDTH_HERE/2)
 +
- #ifdef GMX_MM256_HERE
- #ifndef GMX_DOUBLE
- /* single precision 2x(4+4) kernel */
++/* The stride of all the atom data arrays is equal to half the SIMD width */
++#define STRIDE     (GMX_SIMD_WIDTH_HERE/2)
 +
- #define TAB_FDV0
++#if GMX_SIMD_WIDTH_HERE == 8
 +#define SUM_SIMD(x) (x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+x[7])
-     gmx_mm_pr  shX_SSE;
-     gmx_mm_pr  shY_SSE;
-     gmx_mm_pr  shZ_SSE;
-     gmx_mm_pr  ix_SSE0, iy_SSE0, iz_SSE0;
-     gmx_mm_pr  ix_SSE2, iy_SSE2, iz_SSE2;
-     gmx_mm_pr  fix_SSE0, fiy_SSE0, fiz_SSE0;
-     gmx_mm_pr  fix_SSE2, fiy_SSE2, fiz_SSE2;
++#else
++#if GMX_SIMD_WIDTH_HERE == 16
++/* This is getting ridiculous, SIMD horizontal adds would help,
++ * but this is not performance critical (only used to reduce energies)
++ */
++#define SUM_SIMD(x) (x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+x[7]+x[8]+x[9]+x[10]+x[11]+x[12]+x[13]+x[14]+x[15])
 +#else
 +#error "unsupported kernel configuration"
 +#endif
 +#endif
 +
++
++#if defined GMX_X86_AVX_256 && !defined GMX_DOUBLE
++/* AVX-256 single precision 2x(4+4) kernel,
++ * we can do half SIMD-width aligned FDV0 table loads.
++ */
++#define TAB_FDV0
++#endif
++
++
 +#define SIMD_MASK_ALL   0xffffffff
 +
 +#include "nbnxn_kernel_simd_utils.h"
 +
 +/* All functionality defines are set here, except for:
 + * CALC_ENERGIES, ENERGY_GROUPS which are defined before.
 + * CHECK_EXCLS, which is set just before including the inner loop contents.
 + * The combination rule defines, LJ_COMB_GEOM or LJ_COMB_LB are currently
 + * set before calling the kernel function. We might want to move that
 + * to inside the n-loop and have a different combination rule for different
 + * ci's, as no combination rule gives a 50% performance hit for LJ.
 + */
 +
 +/* We always calculate shift forces, because it's cheap anyhow */
 +#define CALC_SHIFTFORCES
 +
 +/* Assumes all LJ parameters are identical */
 +/* #define FIX_LJ_C */
 +
 +/* The NBK_FUNC_NAME... macros below generate the whole zoo of kernels names
 + * with all combinations off electrostatics (coul), LJ combination rules (ljc)
 + * and energy calculations (ene), depending on the defines set.
 + */
 +
 +#define NBK_FUNC_NAME_C_LJC(base, coul, ljc, ene) base ## _ ## coul ## _comb_ ## ljc ## _ ## ene
 +
 +#if defined LJ_COMB_GEOM
 +#define NBK_FUNC_NAME_C(base, coul, ene) NBK_FUNC_NAME_C_LJC(base, coul, geom, ene)
 +#else
 +#if defined LJ_COMB_LB
 +#define NBK_FUNC_NAME_C(base, coul, ene) NBK_FUNC_NAME_C_LJC(base, coul, lb, ene)
 +#else
 +#define NBK_FUNC_NAME_C(base, coul, ene) NBK_FUNC_NAME_C_LJC(base, coul, none, ene)
 +#endif
 +#endif
 +
 +#ifdef CALC_COUL_RF
 +#define NBK_FUNC_NAME(base, ene) NBK_FUNC_NAME_C(base, rf, ene)
 +#endif
 +#ifdef CALC_COUL_TAB
 +#ifndef VDW_CUTOFF_CHECK
 +#define NBK_FUNC_NAME(base, ene) NBK_FUNC_NAME_C(base, tab, ene)
 +#else
 +#define NBK_FUNC_NAME(base, ene) NBK_FUNC_NAME_C(base, tab_twin, ene)
 +#endif
 +#endif
 +#ifdef CALC_COUL_EWALD
 +#ifndef VDW_CUTOFF_CHECK
 +#define NBK_FUNC_NAME(base, ene) NBK_FUNC_NAME_C(base, ewald, ene)
 +#else
 +#define NBK_FUNC_NAME(base, ene) NBK_FUNC_NAME_C(base, ewald_twin, ene)
 +#endif
 +#endif
 +
 +static void
 +#ifndef CALC_ENERGIES
 +NBK_FUNC_NAME(nbnxn_kernel_simd_2xnn, noener)
 +#else
 +#ifndef ENERGY_GROUPS
 +NBK_FUNC_NAME(nbnxn_kernel_simd_2xnn, ener)
 +#else
 +NBK_FUNC_NAME(nbnxn_kernel_simd_2xnn, energrp)
 +#endif
 +#endif
 +#undef NBK_FUNC_NAME
 +#undef NBK_FUNC_NAME_C
 +#undef NBK_FUNC_NAME_C_LJC
 +(const nbnxn_pairlist_t     *nbl,
 + const nbnxn_atomdata_t     *nbat,
 + const interaction_const_t  *ic,
 + rvec                       *shift_vec,
 + real                       *f
 +#ifdef CALC_SHIFTFORCES
 + ,
 + real                       *fshift
 +#endif
 +#ifdef CALC_ENERGIES
 + ,
 + real                       *Vvdw,
 + real                       *Vc
 +#endif
 +)
 +{
 +    const nbnxn_ci_t   *nbln;
 +    const nbnxn_cj_t   *l_cj;
 +    const int          *type;
 +    const real         *q;
 +    const real         *shiftvec;
 +    const real         *x;
 +    const real         *nbfp0, *nbfp1, *nbfp2 = NULL, *nbfp3 = NULL;
 +    real                facel;
 +    real               *nbfp_ptr;
 +    int                 nbfp_stride;
 +    int                 n, ci, ci_sh;
 +    int                 ish, ish3;
 +    gmx_bool            do_LJ, half_LJ, do_coul;
 +    int                 sci, scix, sciy, sciz, sci2;
 +    int                 cjind0, cjind1, cjind;
 +    int                 ip, jp;
 +
 +#ifdef ENERGY_GROUPS
 +    int         Vstride_i;
 +    int         egps_ishift, egps_imask;
 +    int         egps_jshift, egps_jmask, egps_jstride;
 +    int         egps_i;
 +    real       *vvdwtp[UNROLLI];
 +    real       *vctp[UNROLLI];
 +#endif
 +
-     __m128     fix_SSE, fiy_SSE, fiz_SSE;
++    gmx_mm_pr  shX_S;
++    gmx_mm_pr  shY_S;
++    gmx_mm_pr  shZ_S;
++    gmx_mm_pr  ix_S0, iy_S0, iz_S0;
++    gmx_mm_pr  ix_S2, iy_S2, iz_S2;
++    gmx_mm_pr  fix_S0, fiy_S0, fiz_S0;
++    gmx_mm_pr  fix_S2, fiy_S2, fiz_S2;
 +#if UNROLLJ >= 4
 +#ifndef GMX_DOUBLE
-     __m256d    fix_SSE, fiy_SSE, fiz_SSE;
++    __m128     fix_S, fiy_S, fiz_S;
 +#else
-     __m128d    fix0_SSE, fiy0_SSE, fiz0_SSE;
-     __m128d    fix2_SSE, fiy2_SSE, fiz2_SSE;
++    __m256d    fix_S, fiy_S, fiz_S;
 +#endif
 +#else
-     /* AVX: use floating point masks, as there are no integer instructions */
-     gmx_mm_pr  mask0 = _mm256_castsi256_ps(_mm256_set_epi32( 0x0080, 0x0040, 0x0020, 0x0010, 0x0008, 0x0004, 0x0002, 0x0001 ));
-     gmx_mm_pr  mask2 = _mm256_castsi256_ps(_mm256_set_epi32( 0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100 ));
-     gmx_mm_pr  diag_jmi_SSE;
++    __m128d    fix0_S, fiy0_S, fiz0_S;
++    __m128d    fix2_S, fiy2_S, fiz2_S;
 +#endif
 +
-     gmx_mm_pr  diag_SSE0, diag_SSE2;
++    gmx_mm_pr  diag_jmi_S;
 +#if UNROLLI == UNROLLJ
-     gmx_mm_pr  diag0_SSE0, diag0_SSE2;
-     gmx_mm_pr  diag1_SSE0, diag1_SSE2;
++    gmx_mm_pr  diag_S0, diag_S2;
 +#else
-     gmx_mm_pr  zero_SSE = gmx_set1_pr(0);
++    gmx_mm_pr  diag0_S0, diag0_S2;
++    gmx_mm_pr  diag1_S0, diag1_S2;
 +#endif
 +
-     gmx_mm_pr  one_SSE = gmx_set1_pr(1.0);
-     gmx_mm_pr  iq_SSE0 = gmx_setzero_pr();
-     gmx_mm_pr  iq_SSE2 = gmx_setzero_pr();
-     gmx_mm_pr  mrc_3_SSE;
++    gmx_mm_pr  mask_S0, mask_S2;
++
++    gmx_mm_pr  zero_S = gmx_set1_pr(0);
 +
-     gmx_mm_pr  hrc_3_SSE, moh_rc_SSE;
++    gmx_mm_pr  one_S = gmx_set1_pr(1.0);
++    gmx_mm_pr  iq_S0 = gmx_setzero_pr();
++    gmx_mm_pr  iq_S2 = gmx_setzero_pr();
++    gmx_mm_pr  mrc_3_S;
 +#ifdef CALC_ENERGIES
-     gmx_mm_pr   invtsp_SSE;
++    gmx_mm_pr  hrc_3_S, moh_rc_S;
 +#endif
 +
 +#ifdef CALC_COUL_TAB
 +    /* Coulomb table variables */
- #ifdef GMX_MM256_HERE
-     int        ti0_array[2*GMX_SIMD_WIDTH_HERE-1], *ti0;
-     int        ti2_array[2*GMX_SIMD_WIDTH_HERE-1], *ti2;
- #endif
++    gmx_mm_pr   invtsp_S;
 +    const real *tab_coul_F;
 +#ifndef TAB_FDV0
 +    const real *tab_coul_V;
 +#endif
-     gmx_mm_pr  mhalfsp_SSE;
++    int        ti0_array[2*GMX_SIMD_WIDTH_HERE], *ti0;
++    int        ti2_array[2*GMX_SIMD_WIDTH_HERE], *ti2;
 +#ifdef CALC_ENERGIES
-     gmx_mm_pr beta2_SSE, beta_SSE;
++    gmx_mm_pr  mhalfsp_S;
 +#endif
 +#endif
 +
 +#ifdef CALC_COUL_EWALD
-     gmx_mm_pr  sh_ewald_SSE;
++    gmx_mm_pr beta2_S, beta_S;
 +#endif
 +
 +#if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
-     gmx_mm_pr   hsig_i_SSE0, seps_i_SSE0;
-     gmx_mm_pr   hsig_i_SSE2, seps_i_SSE2;
++    gmx_mm_pr  sh_ewald_S;
 +#endif
 +
 +#ifdef LJ_COMB_LB
 +    const real *ljc;
 +
-     real        pvdw_array[2*UNROLLI*UNROLLJ+3];
++    gmx_mm_pr   hsig_i_S0, seps_i_S0;
++    gmx_mm_pr   hsig_i_S2, seps_i_S2;
 +#else
 +#ifdef FIX_LJ_C
-     gmx_mm_pr   c6_SSE0, c12_SSE0;
-     gmx_mm_pr   c6_SSE2, c12_SSE2;
++    real        pvdw_array[2*UNROLLI*UNROLLJ+GMX_SIMD_WIDTH_HERE];
 +    real       *pvdw_c6, *pvdw_c12;
-     gmx_mm_pr   c6s_SSE0, c12s_SSE0;
-     gmx_mm_pr   c6s_SSE1, c12s_SSE1;
-     gmx_mm_pr   c6s_SSE2 = gmx_setzero_pr(), c12s_SSE2 = gmx_setzero_pr();
-     gmx_mm_pr   c6s_SSE3 = gmx_setzero_pr(), c12s_SSE3 = gmx_setzero_pr();
++    gmx_mm_pr   c6_S0, c12_S0;
++    gmx_mm_pr   c6_S2, c12_S2;
 +#endif
 +
 +#ifdef LJ_COMB_GEOM
 +    const real *ljc;
 +
-     gmx_mm_pr  vctotSSE, VvdwtotSSE;
-     gmx_mm_pr  sixthSSE, twelvethSSE;
++    gmx_mm_pr   c6s_S0, c12s_S0;
++    gmx_mm_pr   c6s_S1, c12s_S1;
++    gmx_mm_pr   c6s_S2 = gmx_setzero_pr(), c12s_S2 = gmx_setzero_pr();
++    gmx_mm_pr   c6s_S3 = gmx_setzero_pr(), c12s_S3 = gmx_setzero_pr();
 +#endif
 +#endif /* LJ_COMB_LB */
 +
-     gmx_mm_pr  avoid_sing_SSE;
-     gmx_mm_pr  rc2_SSE;
++    gmx_mm_pr  vctot_S, Vvdwtot_S;
++    gmx_mm_pr  sixth_S, twelveth_S;
 +
-     gmx_mm_pr  rcvdw2_SSE;
++    gmx_mm_pr  avoid_sing_S;
++    gmx_mm_pr  rc2_S;
 +#ifdef VDW_CUTOFF_CHECK
-     gmx_mm_pr  sh_invrc6_SSE, sh_invrc12_SSE;
++    gmx_mm_pr  rcvdw2_S;
 +#endif
 +
 +#ifdef CALC_ENERGIES
-     real       tmpsum_array[15], *tmpsum;
++    gmx_mm_pr  sh_invrc6_S, sh_invrc12_S;
 +
 +    /* cppcheck-suppress unassignedVariable */
-     real       shf_array[15], *shf;
++    real       tmpsum_array[2*GMX_SIMD_WIDTH_HERE], *tmpsum;
 +#endif
 +#ifdef CALC_SHIFTFORCES
 +    /* cppcheck-suppress unassignedVariable */
-     diag_jmi_SSE = gmx_load_pr(nbat->simd_2xnn_diag);
++    real       shf_array[2*GMX_SIMD_WIDTH_HERE], *shf;
 +#endif
 +
 +    int ninner;
 +
 +#ifdef COUNT_PAIRS
 +    int npair = 0;
 +#endif
 +
 +#if defined LJ_COMB_GEOM || defined LJ_COMB_LB
 +    ljc = nbat->lj_comb;
 +#else
 +    /* No combination rule used */
 +#ifndef GMX_DOUBLE
 +    nbfp_ptr    = nbat->nbfp_s4;
 +#define NBFP_STRIDE  4
 +#else
 +    nbfp_ptr    = nbat->nbfp;
 +#define NBFP_STRIDE  2
 +#endif
 +    nbfp_stride = NBFP_STRIDE;
 +#endif
 +
 +    /* Load j-i for the first i */
-     diag_SSE0    = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag_SSE2    = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
++    diag_jmi_S = gmx_load_pr(nbat->simd_2xnn_diag);
 +    /* Generate all the diagonal masks as comparison results */
 +#if UNROLLI == UNROLLJ
-     diag0_SSE0 = gmx_cmplt_pr(diag_i_SSE, diag_j_SSE);
-     diag_i_SSE = gmx_add_pr(diag_i_SSE, one_SSE);
-     diag_i_SSE = gmx_add_pr(diag_i_SSE, one_SSE);
-     diag0_SSE2 = gmx_cmplt_pr(diag_i_SSE, diag_j_SSE);
-     diag_i_SSE = gmx_add_pr(diag_i_SSE, one_SSE);
-     diag_i_SSE = gmx_add_pr(diag_i_SSE, one_SSE);
-     diag1_SSE0 = gmx_cmplt_pr(diag_i_SSE, diag_j_SSE);
-     diag_i_SSE = gmx_add_pr(diag_i_SSE, one_SSE);
-     diag_i_SSE = gmx_add_pr(diag_i_SSE, one_SSE);
-     diag1_SSE2 = gmx_cmplt_pr(diag_i_SSE, diag_j_SSE);
++    diag_S0    = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag_S2    = gmx_cmplt_pr(zero_S, diag_jmi_S);
 +#else
 +#if 2*UNROLLI == UNROLLJ
- #ifdef GMX_MM256_HERE
++    diag0_S0 = gmx_cmplt_pr(diag_i_S, diag_j_S);
++    diag_i_S = gmx_add_pr(diag_i_S, one_S);
++    diag_i_S = gmx_add_pr(diag_i_S, one_S);
++    diag0_S2 = gmx_cmplt_pr(diag_i_S, diag_j_S);
++    diag_i_S = gmx_add_pr(diag_i_S, one_S);
++    diag_i_S = gmx_add_pr(diag_i_S, one_S);
++    diag1_S0 = gmx_cmplt_pr(diag_i_S, diag_j_S);
++    diag_i_S = gmx_add_pr(diag_i_S, one_S);
++    diag_i_S = gmx_add_pr(diag_i_S, one_S);
++    diag1_S2 = gmx_cmplt_pr(diag_i_S, diag_j_S);
 +#endif
 +#endif
 +
++    /* Load masks for topology exclusion masking */
++    mask_S0    = gmx_load_pr((real *)nbat->simd_excl_mask + 0*2*UNROLLJ);
++    mask_S2    = gmx_load_pr((real *)nbat->simd_excl_mask + 1*2*UNROLLJ);
++
 +#ifdef CALC_COUL_TAB
-     ti0 = (int *)(((size_t)(ti0_array+GMX_SIMD_WIDTH_HERE-1)) & (~((size_t)(GMX_SIMD_WIDTH_HERE*sizeof(int)-1))));
-     ti2 = (int *)(((size_t)(ti2_array+GMX_SIMD_WIDTH_HERE-1)) & (~((size_t)(GMX_SIMD_WIDTH_HERE*sizeof(int)-1))));
- #endif
 +    /* Generate aligned table index pointers */
-     invtsp_SSE  = gmx_set1_pr(ic->tabq_scale);
++    ti0 = gmx_simd_align_int(ti0_array);
++    ti2 = gmx_simd_align_int(ti2_array);
 +
-     mhalfsp_SSE = gmx_set1_pr(-0.5/ic->tabq_scale);
++    invtsp_S  = gmx_set1_pr(ic->tabq_scale);
 +#ifdef CALC_ENERGIES
-     beta2_SSE = gmx_set1_pr(ic->ewaldcoeff*ic->ewaldcoeff);
-     beta_SSE  = gmx_set1_pr(ic->ewaldcoeff);
++    mhalfsp_S = gmx_set1_pr(-0.5/ic->tabq_scale);
 +#endif
 +
 +#ifdef TAB_FDV0
 +    tab_coul_F = ic->tabq_coul_FDV0;
 +#else
 +    tab_coul_F = ic->tabq_coul_F;
 +    tab_coul_V = ic->tabq_coul_V;
 +#endif
 +#endif /* CALC_COUL_TAB */
 +
 +#ifdef CALC_COUL_EWALD
-     sh_ewald_SSE = gmx_set1_pr(ic->sh_ewald);
++    beta2_S = gmx_set1_pr(ic->ewaldcoeff*ic->ewaldcoeff);
++    beta_S  = gmx_set1_pr(ic->ewaldcoeff);
 +#endif
 +
 +#if (defined CALC_COUL_TAB || defined CALC_COUL_EWALD) && defined CALC_ENERGIES
-     avoid_sing_SSE = gmx_set1_pr(NBNXN_AVOID_SING_R2_INC);
++    sh_ewald_S = gmx_set1_pr(ic->sh_ewald);
 +#endif
 +
 +    q                   = nbat->q;
 +    type                = nbat->type;
 +    facel               = ic->epsfac;
 +    shiftvec            = shift_vec[0];
 +    x                   = nbat->x;
 +
-     rc2_SSE    = gmx_set1_pr(ic->rcoulomb*ic->rcoulomb);
++    avoid_sing_S = gmx_set1_pr(NBNXN_AVOID_SING_R2_INC);
 +
 +    /* The kernel either supports rcoulomb = rvdw or rcoulomb >= rvdw */
-     rcvdw2_SSE = gmx_set1_pr(ic->rvdw*ic->rvdw);
++    rc2_S    = gmx_set1_pr(ic->rcoulomb*ic->rcoulomb);
 +#ifdef VDW_CUTOFF_CHECK
-     sixthSSE    = gmx_set1_pr(1.0/6.0);
-     twelvethSSE = gmx_set1_pr(1.0/12.0);
++    rcvdw2_S = gmx_set1_pr(ic->rvdw*ic->rvdw);
 +#endif
 +
 +#ifdef CALC_ENERGIES
-     sh_invrc6_SSE  = gmx_set1_pr(ic->sh_invrc6);
-     sh_invrc12_SSE = gmx_set1_pr(ic->sh_invrc6*ic->sh_invrc6);
++    sixth_S      = gmx_set1_pr(1.0/6.0);
++    twelveth_S   = gmx_set1_pr(1.0/12.0);
 +
-     mrc_3_SSE = gmx_set1_pr(-2*ic->k_rf);
++    sh_invrc6_S  = gmx_set1_pr(ic->sh_invrc6);
++    sh_invrc12_S = gmx_set1_pr(ic->sh_invrc6*ic->sh_invrc6);
 +#endif
 +
-     hrc_3_SSE = gmx_set1_pr(ic->k_rf);
++    mrc_3_S  = gmx_set1_pr(-2*ic->k_rf);
 +
 +#ifdef CALC_ENERGIES
-     moh_rc_SSE = gmx_set1_pr(-ic->c_rf);
++    hrc_3_S  = gmx_set1_pr(ic->k_rf);
 +
-     tmpsum = (real *)(((size_t)(tmpsum_array+7)) & (~((size_t)31)));
++    moh_rc_S = gmx_set1_pr(-ic->c_rf);
 +#endif
 +
 +#ifdef CALC_ENERGIES
-     shf = (real *)(((size_t)(shf_array+7)) & (~((size_t)31)));
++    tmpsum   = gmx_simd_align_real(tmpsum_array);
 +#endif
 +#ifdef CALC_SHIFTFORCES
-     pvdw_c6  = (real *)(((size_t)(pvdw_array+3)) & (~((size_t)15)));
++    shf      = gmx_simd_align_real(shf_array);
 +#endif
 +
 +#ifdef FIX_LJ_C
-     c6_SSE0            = gmx_load_pr(pvdw_c6 +0*UNROLLJ);
-     c6_SSE1            = gmx_load_pr(pvdw_c6 +1*UNROLLJ);
-     c6_SSE2            = gmx_load_pr(pvdw_c6 +2*UNROLLJ);
-     c6_SSE3            = gmx_load_pr(pvdw_c6 +3*UNROLLJ);
-     c12_SSE0           = gmx_load_pr(pvdw_c12+0*UNROLLJ);
-     c12_SSE1           = gmx_load_pr(pvdw_c12+1*UNROLLJ);
-     c12_SSE2           = gmx_load_pr(pvdw_c12+2*UNROLLJ);
-     c12_SSE3           = gmx_load_pr(pvdw_c12+3*UNROLLJ);
++    pvdw_c6  = gmx_simd_align_real(pvdw_array);
 +    pvdw_c12 = pvdw_c6 + UNROLLI*UNROLLJ;
 +
 +    for (jp = 0; jp < UNROLLJ; jp++)
 +    {
 +        pvdw_c6 [0*UNROLLJ+jp] = nbat->nbfp[0*2];
 +        pvdw_c6 [1*UNROLLJ+jp] = nbat->nbfp[0*2];
 +        pvdw_c6 [2*UNROLLJ+jp] = nbat->nbfp[0*2];
 +        pvdw_c6 [3*UNROLLJ+jp] = nbat->nbfp[0*2];
 +
 +        pvdw_c12[0*UNROLLJ+jp] = nbat->nbfp[0*2+1];
 +        pvdw_c12[1*UNROLLJ+jp] = nbat->nbfp[0*2+1];
 +        pvdw_c12[2*UNROLLJ+jp] = nbat->nbfp[0*2+1];
 +        pvdw_c12[3*UNROLLJ+jp] = nbat->nbfp[0*2+1];
 +    }
-         shX_SSE = gmx_load1_pr(shiftvec+ish3);
-         shY_SSE = gmx_load1_pr(shiftvec+ish3+1);
-         shZ_SSE = gmx_load1_pr(shiftvec+ish3+2);
++    c6_S0            = gmx_load_pr(pvdw_c6 +0*UNROLLJ);
++    c6_S1            = gmx_load_pr(pvdw_c6 +1*UNROLLJ);
++    c6_S2            = gmx_load_pr(pvdw_c6 +2*UNROLLJ);
++    c6_S3            = gmx_load_pr(pvdw_c6 +3*UNROLLJ);
++
++    c12_S0           = gmx_load_pr(pvdw_c12+0*UNROLLJ);
++    c12_S1           = gmx_load_pr(pvdw_c12+1*UNROLLJ);
++    c12_S2           = gmx_load_pr(pvdw_c12+2*UNROLLJ);
++    c12_S3           = gmx_load_pr(pvdw_c12+3*UNROLLJ);
 +#endif /* FIX_LJ_C */
 +
 +#ifdef ENERGY_GROUPS
 +    egps_ishift  = nbat->neg_2log;
 +    egps_imask   = (1<<egps_ishift) - 1;
 +    egps_jshift  = 2*nbat->neg_2log;
 +    egps_jmask   = (1<<egps_jshift) - 1;
 +    egps_jstride = (UNROLLJ>>1)*UNROLLJ;
 +    /* Major division is over i-particle energy groups, determine the stride */
 +    Vstride_i    = nbat->nenergrp*(1<<nbat->neg_2log)*egps_jstride;
 +#endif
 +
 +    l_cj = nbl->cj;
 +
 +    ninner = 0;
 +    for (n = 0; n < nbl->nci; n++)
 +    {
 +        nbln = &nbl->ci[n];
 +
 +        ish              = (nbln->shift & NBNXN_CI_SHIFT);
 +        ish3             = ish*3;
 +        cjind0           = nbln->cj_ind_start;
 +        cjind1           = nbln->cj_ind_end;
 +        ci               = nbln->ci;
 +        ci_sh            = (ish == CENTRAL ? ci : -1);
 +
- #define gmx_load2_hpr(x)  _mm256_insertf128_ps(gmx_load1_pr(x), gmx_load1_hpr(x+1), 1)
++        shX_S = gmx_load1_pr(shiftvec+ish3);
++        shY_S = gmx_load1_pr(shiftvec+ish3+1);
++        shZ_S = gmx_load1_pr(shiftvec+ish3+2);
 +
 +#if UNROLLJ <= 4
 +        sci              = ci*STRIDE;
 +        scix             = sci*DIM;
 +        sci2             = sci*2;
 +#else
 +        sci              = (ci>>1)*STRIDE;
 +        scix             = sci*DIM + (ci & 1)*(STRIDE>>1);
 +        sci2             = sci*2 + (ci & 1)*(STRIDE>>1);
 +        sci             += (ci & 1)*(STRIDE>>1);
 +#endif
 +
 +        /* We have 5 LJ/C combinations, but use only three inner loops,
 +         * as the other combinations are unlikely and/or not much faster:
 +         * inner half-LJ + C for half-LJ + C / no-LJ + C
 +         * inner LJ + C      for full-LJ + C
 +         * inner LJ          for full-LJ + no-C / half-LJ + no-C
 +         */
 +        do_LJ   = (nbln->shift & NBNXN_CI_DO_LJ(0));
 +        do_coul = (nbln->shift & NBNXN_CI_DO_COUL(0));
 +        half_LJ = ((nbln->shift & NBNXN_CI_HALF_LJ(0)) || !do_LJ) && do_coul;
 +
 +#ifdef ENERGY_GROUPS
 +        egps_i = nbat->energrp[ci];
 +        {
 +            int ia, egp_ia;
 +
 +            for (ia = 0; ia < UNROLLI; ia++)
 +            {
 +                egp_ia     = (egps_i >> (ia*egps_ishift)) & egps_imask;
 +                vvdwtp[ia] = Vvdw + egp_ia*Vstride_i;
 +                vctp[ia]   = Vc   + egp_ia*Vstride_i;
 +            }
 +        }
 +#endif
 +#if defined CALC_ENERGIES
 +#if UNROLLJ == 4
 +        if (do_coul && l_cj[nbln->cj_ind_start].cj == ci_sh)
 +#endif
 +#if UNROLLJ == 2
 +        if (do_coul && l_cj[nbln->cj_ind_start].cj == (ci_sh<<1))
 +#endif
 +#if UNROLLJ == 8
 +        if (do_coul && l_cj[nbln->cj_ind_start].cj == (ci_sh>>1))
 +#endif
 +        {
 +            int  ia;
 +            real Vc_sub_self;
 +
 +#ifdef CALC_COUL_RF
 +            Vc_sub_self = 0.5*ic->c_rf;
 +#endif
 +#ifdef CALC_COUL_TAB
 +#ifdef TAB_FDV0
 +            Vc_sub_self = 0.5*tab_coul_F[2];
 +#else
 +            Vc_sub_self = 0.5*tab_coul_V[0];
 +#endif
 +#endif
 +#ifdef CALC_COUL_EWALD
 +            /* beta/sqrt(pi) */
 +            Vc_sub_self = 0.5*ic->ewaldcoeff*M_2_SQRTPI;
 +#endif
 +
 +            for (ia = 0; ia < UNROLLI; ia++)
 +            {
 +                real qi;
 +
 +                qi = q[sci+ia];
 +#ifdef ENERGY_GROUPS
 +                vctp[ia][((egps_i>>(ia*egps_ishift)) & egps_imask)*egps_jstride]
 +#else
 +                Vc[0]
 +#endif
 +                    -= facel*qi*qi*Vc_sub_self;
 +            }
 +        }
 +#endif
 +
-         ix_SSE0          = gmx_add_pr(gmx_load2_hpr(x+scix), shX_SSE);
-         ix_SSE2          = gmx_add_pr(gmx_load2_hpr(x+scix+2), shX_SSE);
-         iy_SSE0          = gmx_add_pr(gmx_load2_hpr(x+sciy), shY_SSE);
-         iy_SSE2          = gmx_add_pr(gmx_load2_hpr(x+sciy+2), shY_SSE);
-         iz_SSE0          = gmx_add_pr(gmx_load2_hpr(x+sciz), shZ_SSE);
-         iz_SSE2          = gmx_add_pr(gmx_load2_hpr(x+sciz+2), shZ_SSE);
 +        /* Load i atom data */
 +        sciy             = scix + STRIDE;
 +        sciz             = sciy + STRIDE;
-             gmx_mm_pr facel_SSE;
++        gmx_load1p1_pr(ix_S0, x+scix);
++        gmx_load1p1_pr(ix_S2, x+scix+2);
++        gmx_load1p1_pr(iy_S0, x+sciy);
++        gmx_load1p1_pr(iy_S2, x+sciy+2);
++        gmx_load1p1_pr(iz_S0, x+sciz);
++        gmx_load1p1_pr(iz_S2, x+sciz+2);
++        ix_S0          = gmx_add_pr(ix_S0, shX_S);
++        ix_S2          = gmx_add_pr(ix_S2, shX_S);
++        iy_S0          = gmx_add_pr(iy_S0, shY_S);
++        iy_S2          = gmx_add_pr(iy_S2, shY_S);
++        iz_S0          = gmx_add_pr(iz_S0, shZ_S);
++        iz_S2          = gmx_add_pr(iz_S2, shZ_S);
 +
 +        if (do_coul)
 +        {
-             facel_SSE    = gmx_set1_pr(facel);
++            gmx_mm_pr facel_S;
 +
-             iq_SSE0      = gmx_mul_pr(facel_SSE, gmx_load2_hpr(q+sci));
-             iq_SSE2      = gmx_mul_pr(facel_SSE, gmx_load2_hpr(q+sci+2));
++            facel_S    = gmx_set1_pr(facel);
 +
-         hsig_i_SSE0      = gmx_load2_hpr(ljc+sci2+0);
-         hsig_i_SSE2      = gmx_load2_hpr(ljc+sci2+2);
-         seps_i_SSE0      = gmx_load2_hpr(ljc+sci2+STRIDE+0);
-         seps_i_SSE2      = gmx_load2_hpr(ljc+sci2+STRIDE+2);
++            gmx_load1p1_pr(iq_S0, q+sci);
++            gmx_load1p1_pr(iq_S2, q+sci+2);
++            iq_S0      = gmx_mul_pr(facel_S, iq_S0);
++            iq_S2      = gmx_mul_pr(facel_S, iq_S2);
 +        }
 +
 +#ifdef LJ_COMB_LB
-         c6s_SSE0         = gmx_load2_hpr(ljc+sci2+0);
++        gmx_load1p1_pr(hsig_i_S0, ljc+sci2+0);
++        gmx_load1p1_pr(hsig_i_S2, ljc+sci2+2);
++        gmx_load1p1_pr(seps_i_S0, ljc+sci2+STRIDE+0);
++        gmx_load1p1_pr(seps_i_S2, ljc+sci2+STRIDE+2);
 +#else
 +#ifdef LJ_COMB_GEOM
-             c6s_SSE2     = gmx_load2_hpr(ljc+sci2+2);
++        gmx_load1p1_pr(c6s_S0, ljc+sci2+0);
 +        if (!half_LJ)
 +        {
-         c12s_SSE0        = gmx_load2_hpr(ljc+sci2+STRIDE+0);
++            gmx_load1p1_pr(c6s_S2, ljc+sci2+2);
 +        }
-             c12s_SSE2    = gmx_load2_hpr(ljc+sci2+STRIDE+2);
++        gmx_load1p1_pr(c12s_S0, ljc+sci2+STRIDE+0);
 +        if (!half_LJ)
 +        {
-         VvdwtotSSE       = gmx_setzero_pr();
-         vctotSSE         = gmx_setzero_pr();
++            gmx_load1p1_pr(c12s_S2, ljc+sci2+STRIDE+2);
 +        }
 +#else
 +        nbfp0     = nbfp_ptr + type[sci  ]*nbat->ntype*nbfp_stride;
 +        nbfp1     = nbfp_ptr + type[sci+1]*nbat->ntype*nbfp_stride;
 +        if (!half_LJ)
 +        {
 +            nbfp2 = nbfp_ptr + type[sci+2]*nbat->ntype*nbfp_stride;
 +            nbfp3 = nbfp_ptr + type[sci+3]*nbat->ntype*nbfp_stride;
 +        }
 +#endif
 +#endif
 +
 +        /* Zero the potential energy for this list */
-         fix_SSE0           = gmx_setzero_pr();
-         fix_SSE2           = gmx_setzero_pr();
-         fiy_SSE0           = gmx_setzero_pr();
-         fiy_SSE2           = gmx_setzero_pr();
-         fiz_SSE0           = gmx_setzero_pr();
-         fiz_SSE2           = gmx_setzero_pr();
++        Vvdwtot_S        = gmx_setzero_pr();
++        vctot_S          = gmx_setzero_pr();
 +
 +        /* Clear i atom forces */
- #if UNROLLJ >= 4
- #ifndef GMX_DOUBLE
- #define gmx_load_ps4  _mm_load_ps
- #define gmx_store_ps4 _mm_store_ps
- #define gmx_add_ps4   _mm_add_ps
++        fix_S0           = gmx_setzero_pr();
++        fix_S2           = gmx_setzero_pr();
++        fiy_S0           = gmx_setzero_pr();
++        fiy_S2           = gmx_setzero_pr();
++        fiz_S0           = gmx_setzero_pr();
++        fiz_S2           = gmx_setzero_pr();
 +
 +        cjind = cjind0;
 +
 +        /* Currently all kernels use (at least half) LJ */
 +#define CALC_LJ
 +        if (half_LJ)
 +        {
 +#define CALC_COULOMB
 +#define HALF_LJ
 +#define CHECK_EXCLS
 +            while (cjind < cjind1 && nbl->cj[cjind].excl != SIMD_MASK_ALL)
 +            {
 +#include "nbnxn_kernel_simd_2xnn_inner.h"
 +                cjind++;
 +            }
 +#undef CHECK_EXCLS
 +            for (; (cjind < cjind1); cjind++)
 +            {
 +#include "nbnxn_kernel_simd_2xnn_inner.h"
 +            }
 +#undef HALF_LJ
 +#undef CALC_COULOMB
 +        }
 +        else if (do_coul)
 +        {
 +#define CALC_COULOMB
 +#define CHECK_EXCLS
 +            while (cjind < cjind1 && nbl->cj[cjind].excl != SIMD_MASK_ALL)
 +            {
 +#include "nbnxn_kernel_simd_2xnn_inner.h"
 +                cjind++;
 +            }
 +#undef CHECK_EXCLS
 +            for (; (cjind < cjind1); cjind++)
 +            {
 +#include "nbnxn_kernel_simd_2xnn_inner.h"
 +            }
 +#undef CALC_COULOMB
 +        }
 +        else
 +        {
 +#define CHECK_EXCLS
 +            while (cjind < cjind1 && nbl->cj[cjind].excl != SIMD_MASK_ALL)
 +            {
 +#include "nbnxn_kernel_simd_2xnn_inner.h"
 +                cjind++;
 +            }
 +#undef CHECK_EXCLS
 +            for (; (cjind < cjind1); cjind++)
 +            {
 +#include "nbnxn_kernel_simd_2xnn_inner.h"
 +            }
 +        }
 +#undef CALC_LJ
 +        ninner += cjind1 - cjind0;
 +
 +        /* Add accumulated i-forces to the force array */
- #define gmx_load_ps4  _mm256_load_pd
- #define gmx_store_ps4 _mm256_store_pd
- #define gmx_add_ps4   _mm256_add_pd
++#if defined GMX_X86_AVX_256 && !defined GMX_DOUBLE
++#define gmx_load_pr4  _mm_load_ps
++#define gmx_store_pr4 _mm_store_ps
++#define gmx_add_pr4   _mm_add_ps
 +#else
-         GMX_MM_TRANSPOSE_SUM4H_PR(fix_SSE0, fix_SSE2, fix_SSE);
-         gmx_store_ps4(f+scix, gmx_add_ps4(fix_SSE, gmx_load_ps4(f+scix)));
++#error "You need to define 4-width SIM macros for i-force reduction"
 +#endif
-         GMX_MM_TRANSPOSE_SUM4H_PR(fiy_SSE0, fiy_SSE2, fiy_SSE);
-         gmx_store_ps4(f+sciy, gmx_add_ps4(fiy_SSE, gmx_load_ps4(f+sciy)));
++        GMX_MM_TRANSPOSE_SUM4H_PR(fix_S0, fix_S2, fix_S);
++        gmx_store_pr4(f+scix, gmx_add_pr4(fix_S, gmx_load_pr4(f+scix)));
 +
-         GMX_MM_TRANSPOSE_SUM4H_PR(fiz_SSE0, fiz_SSE2, fiz_SSE);
-         gmx_store_ps4(f+sciz, gmx_add_ps4(fiz_SSE, gmx_load_ps4(f+sciz)));
++        GMX_MM_TRANSPOSE_SUM4H_PR(fiy_S0, fiy_S2, fiy_S);
++        gmx_store_pr4(f+sciy, gmx_add_pr4(fiy_S, gmx_load_pr4(f+sciy)));
 +
-         gmx_store_ps4(shf, fix_SSE);
++        GMX_MM_TRANSPOSE_SUM4H_PR(fiz_S0, fiz_S2, fiz_S);
++        gmx_store_pr4(f+sciz, gmx_add_pr4(fiz_S, gmx_load_pr4(f+sciz)));
 +
 +#ifdef CALC_SHIFTFORCES
-         gmx_store_ps4(shf, fiy_SSE);
++        gmx_store_pr4(shf, fix_S);
 +        fshift[ish3+0] += SUM_SIMD4(shf);
-         gmx_store_ps4(shf, fiz_SSE);
++        gmx_store_pr4(shf, fiy_S);
 +        fshift[ish3+1] += SUM_SIMD4(shf);
- #else
-         GMX_MM_TRANSPOSE_SUM2_PD(fix_SSE0, fix_SSE1, fix0_SSE);
-         _mm_store_pd(f+scix, _mm_add_pd(fix0_SSE, _mm_load_pd(f+scix)));
-         GMX_MM_TRANSPOSE_SUM2_PD(fix_SSE2, fix_SSE3, fix2_SSE);
-         _mm_store_pd(f+scix+2, _mm_add_pd(fix2_SSE, _mm_load_pd(f+scix+2)));
-         GMX_MM_TRANSPOSE_SUM2_PD(fiy_SSE0, fiy_SSE1, fiy0_SSE);
-         _mm_store_pd(f+sciy, _mm_add_pd(fiy0_SSE, _mm_load_pd(f+sciy)));
-         GMX_MM_TRANSPOSE_SUM2_PD(fiy_SSE2, fiy_SSE3, fiy2_SSE);
-         _mm_store_pd(f+sciy+2, _mm_add_pd(fiy2_SSE, _mm_load_pd(f+sciy+2)));
-         GMX_MM_TRANSPOSE_SUM2_PD(fiz_SSE0, fiz_SSE1, fiz0_SSE);
-         _mm_store_pd(f+sciz, _mm_add_pd(fiz0_SSE, _mm_load_pd(f+sciz)));
-         GMX_MM_TRANSPOSE_SUM2_PD(fiz_SSE2, fiz_SSE3, fiz2_SSE);
-         _mm_store_pd(f+sciz+2, _mm_add_pd(fiz2_SSE, _mm_load_pd(f+sciz+2)));
- #ifdef CALC_SHIFTFORCES
-         _mm_store_pd(shf, _mm_add_pd(fix0_SSE, fix2_SSE));
-         fshift[ish3+0] += shf[0] + shf[1];
-         _mm_store_pd(shf, _mm_add_pd(fiy0_SSE, fiy2_SSE));
-         fshift[ish3+1] += shf[0] + shf[1];
-         _mm_store_pd(shf, _mm_add_pd(fiz0_SSE, fiz2_SSE));
-         fshift[ish3+2] += shf[0] + shf[1];
- #endif
- #endif
++        gmx_store_pr4(shf, fiz_S);
 +        fshift[ish3+2] += SUM_SIMD4(shf);
 +#endif
-             gmx_store_pr(tmpsum, vctotSSE);
 +
 +#ifdef CALC_ENERGIES
 +        if (do_coul)
 +        {
-         gmx_store_pr(tmpsum, VvdwtotSSE);
++            gmx_store_pr(tmpsum, vctot_S);
 +            *Vc += SUM_SIMD(tmpsum);
 +        }
 +
- #undef gmx_load2_hpr
++        gmx_store_pr(tmpsum, Vvdwtot_S);
 +        *Vvdw += SUM_SIMD(tmpsum);
 +#endif
 +
 +        /* Outer loop uses 6 flops/iteration */
 +    }
 +
 +#ifdef COUNT_PAIRS
 +    printf("atom pairs %d\n", npair);
 +#endif
 +}
 +
- #undef gmx_load_ps4
- #undef gmx_store_ps4
- #undef gmx_store_ps4
 +
++#undef gmx_load_pr4
++#undef gmx_store_pr4
++#undef gmx_store_pr4
 +
 +#undef CALC_SHIFTFORCES
 +
 +#undef UNROLLI
 +#undef UNROLLJ
 +#undef STRIDE
 +#undef TAB_FDV0
 +#undef NBFP_STRIDE
++
++#undef gmx_mm_hpr
++
++#undef gmx_load_hpr
++#undef gmx_load1_hpr
++#undef gmx_load1p1_pr
++#undef gmx_loaddh_pr
++#undef gmx_store_hpr
++#undef gmx_add_hpr
++#undef gmx_sub_hpr
++
++#undef gmx_sum4_hpr
index bb90a9361ab5d85ccd3419a042dcdef3d58a33d7,0000000000000000000000000000000000000000..848fefa0da20c25c49deeb1d9d1dfb96db30c330
mode 100644,000000..100644
--- /dev/null
@@@ -1,334 -1,0 +1,328 @@@
- #if GMX_NBNXN_SIMD_BITWIDTH == 128
- #define GMX_MM128_HERE
- #else
- #if GMX_NBNXN_SIMD_BITWIDTH == 256
- #define GMX_MM256_HERE
- #else
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + * Copyright (c) 2012, by the GROMACS development team, led by
 + * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 + * others, as listed in the AUTHORS file in the top-level source
 + * directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +
 +#include "typedefs.h"
 +#include "vec.h"
 +#include "smalloc.h"
 +#include "force.h"
 +#include "gmx_omp_nthreads.h"
 +#include "../nbnxn_consts.h"
 +#include "nbnxn_kernel_common.h"
 +
 +#ifdef GMX_NBNXN_SIMD_4XN
 +
 +#include "nbnxn_kernel_simd_4xn.h"
 +
 +/* Include all flavors of the SSE or AVX 4xN kernel loops */
 +
- #endif
++#if !(GMX_NBNXN_SIMD_BITWIDTH == 128 || GMX_NBNXN_SIMD_BITWIDTH == 256)
 +#error "unsupported GMX_NBNXN_SIMD_BITWIDTH"
 +#endif
 +
 +/* Analytical reaction-field kernels */
 +#define CALC_COUL_RF
 +
 +#include "nbnxn_kernel_simd_4xn_includes.h"
 +
 +#undef CALC_COUL_RF
 +
 +/* Tabulated exclusion interaction electrostatics kernels */
 +#define CALC_COUL_TAB
 +
 +/* Single cut-off: rcoulomb = rvdw */
 +#include "nbnxn_kernel_simd_4xn_includes.h"
 +
 +/* Twin cut-off: rcoulomb >= rvdw */
 +#define VDW_CUTOFF_CHECK
 +#include "nbnxn_kernel_simd_4xn_includes.h"
 +#undef VDW_CUTOFF_CHECK
 +
 +#undef CALC_COUL_TAB
 +
 +/* Analytical Ewald exclusion interaction electrostatics kernels */
 +#define CALC_COUL_EWALD
 +
 +/* Single cut-off: rcoulomb = rvdw */
 +#include "nbnxn_kernel_simd_4xn_includes.h"
 +
 +/* Twin cut-off: rcoulomb >= rvdw */
 +#define VDW_CUTOFF_CHECK
 +#include "nbnxn_kernel_simd_4xn_includes.h"
 +#undef VDW_CUTOFF_CHECK
 +
 +#undef CALC_COUL_EWALD
 +
 +
 +typedef void (*p_nbk_func_ener)(const nbnxn_pairlist_t     *nbl,
 +                                const nbnxn_atomdata_t     *nbat,
 +                                const interaction_const_t  *ic,
 +                                rvec                       *shift_vec,
 +                                real                       *f,
 +                                real                       *fshift,
 +                                real                       *Vvdw,
 +                                real                       *Vc);
 +
 +typedef void (*p_nbk_func_noener)(const nbnxn_pairlist_t     *nbl,
 +                                  const nbnxn_atomdata_t     *nbat,
 +                                  const interaction_const_t  *ic,
 +                                  rvec                       *shift_vec,
 +                                  real                       *f,
 +                                  real                       *fshift);
 +
 +enum {
 +    coultRF, coultTAB, coultTAB_TWIN, coultEWALD, coultEWALD_TWIN, coultNR
 +};
 +
 +#define NBK_FN(elec, ljcomb) nbnxn_kernel_simd_4xn_ ## elec ## _comb_ ## ljcomb ## _ener
 +static p_nbk_func_ener p_nbk_ener[coultNR][ljcrNR] =
 +{ { NBK_FN(rf, geom), NBK_FN(rf, lb), NBK_FN(rf, none) },
 +  { NBK_FN(tab, geom), NBK_FN(tab, lb), NBK_FN(tab, none) },
 +  { NBK_FN(tab_twin, geom), NBK_FN(tab_twin, lb), NBK_FN(tab_twin, none) },
 +  { NBK_FN(ewald, geom), NBK_FN(ewald, lb), NBK_FN(ewald, none) },
 +  { NBK_FN(ewald_twin, geom), NBK_FN(ewald_twin, lb), NBK_FN(ewald_twin, none) } };
 +#undef NBK_FN
 +
 +#define NBK_FN(elec, ljcomb) nbnxn_kernel_simd_4xn_ ## elec ## _comb_ ## ljcomb ## _energrp
 +static p_nbk_func_ener p_nbk_energrp[coultNR][ljcrNR] =
 +{ { NBK_FN(rf, geom), NBK_FN(rf, lb), NBK_FN(rf, none) },
 +  { NBK_FN(tab, geom), NBK_FN(tab, lb), NBK_FN(tab, none) },
 +  { NBK_FN(tab_twin, geom), NBK_FN(tab_twin, lb), NBK_FN(tab_twin, none) },
 +  { NBK_FN(ewald, geom), NBK_FN(ewald, lb), NBK_FN(ewald, none) },
 +  { NBK_FN(ewald_twin, geom), NBK_FN(ewald_twin, lb), NBK_FN(ewald_twin, none) } };
 +#undef NBK_FN
 +
 +#define NBK_FN(elec, ljcomb) nbnxn_kernel_simd_4xn_ ## elec ## _comb_ ## ljcomb ## _noener
 +static p_nbk_func_noener p_nbk_noener[coultNR][ljcrNR] =
 +{ { NBK_FN(rf, geom), NBK_FN(rf, lb), NBK_FN(rf, none) },
 +  { NBK_FN(tab, geom), NBK_FN(tab, lb), NBK_FN(tab, none) },
 +  { NBK_FN(tab_twin, geom), NBK_FN(tab_twin, lb), NBK_FN(tab_twin, none) },
 +  { NBK_FN(ewald, geom), NBK_FN(ewald, lb), NBK_FN(ewald, none) },
 +  { NBK_FN(ewald_twin, geom), NBK_FN(ewald_twin, lb), NBK_FN(ewald_twin, none) } };
 +#undef NBK_FN
 +
 +
 +static void reduce_group_energies(int ng, int ng_2log,
 +                                  const real *VSvdw, const real *VSc,
 +                                  real *Vvdw, real *Vc)
 +{
 +    const int simd_width   = GMX_SIMD_WIDTH_HERE;
 +    const int unrollj_half = GMX_SIMD_WIDTH_HERE/2;
 +    int       ng_p2, i, j, j0, j1, c, s;
 +
 +    ng_p2 = (1<<ng_2log);
 +
 +    /* The size of the x86 SIMD energy group buffer array is:
 +     * ng*ng*ng_p2*unrollj_half*simd_width
 +     */
 +    for (i = 0; i < ng; i++)
 +    {
 +        for (j = 0; j < ng; j++)
 +        {
 +            Vvdw[i*ng+j] = 0;
 +            Vc[i*ng+j]   = 0;
 +        }
 +
 +        for (j1 = 0; j1 < ng; j1++)
 +        {
 +            for (j0 = 0; j0 < ng; j0++)
 +            {
 +                c = ((i*ng + j1)*ng_p2 + j0)*unrollj_half*simd_width;
 +                for (s = 0; s < unrollj_half; s++)
 +                {
 +                    Vvdw[i*ng+j0] += VSvdw[c+0];
 +                    Vvdw[i*ng+j1] += VSvdw[c+1];
 +                    Vc  [i*ng+j0] += VSc  [c+0];
 +                    Vc  [i*ng+j1] += VSc  [c+1];
 +                    c             += simd_width + 2;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#endif /* GMX_NBNXN_SIMD_4XN */
 +
 +void
 +nbnxn_kernel_simd_4xn(nbnxn_pairlist_set_t       *nbl_list,
 +                      const nbnxn_atomdata_t     *nbat,
 +                      const interaction_const_t  *ic,
 +                      int                         ewald_excl,
 +                      rvec                       *shift_vec,
 +                      int                         force_flags,
 +                      int                         clearF,
 +                      real                       *fshift,
 +                      real                       *Vc,
 +                      real                       *Vvdw)
 +#ifdef GMX_NBNXN_SIMD_4XN
 +{
 +    int                nnbl;
 +    nbnxn_pairlist_t **nbl;
 +    int                coult;
 +    int                nb;
 +
 +    nnbl = nbl_list->nnbl;
 +    nbl  = nbl_list->nbl;
 +
 +    if (EEL_RF(ic->eeltype) || ic->eeltype == eelCUT)
 +    {
 +        coult = coultRF;
 +    }
 +    else
 +    {
 +        if (ewald_excl == ewaldexclTable)
 +        {
 +            if (ic->rcoulomb == ic->rvdw)
 +            {
 +                coult = coultTAB;
 +            }
 +            else
 +            {
 +                coult = coultTAB_TWIN;
 +            }
 +        }
 +        else
 +        {
 +            if (ic->rcoulomb == ic->rvdw)
 +            {
 +                coult = coultEWALD;
 +            }
 +            else
 +            {
 +                coult = coultEWALD_TWIN;
 +            }
 +        }
 +    }
 +
 +#pragma omp parallel for schedule(static) num_threads(gmx_omp_nthreads_get(emntNonbonded))
 +    for (nb = 0; nb < nnbl; nb++)
 +    {
 +        nbnxn_atomdata_output_t *out;
 +        real                    *fshift_p;
 +
 +        out = &nbat->out[nb];
 +
 +        if (clearF == enbvClearFYes)
 +        {
 +            clear_f(nbat, nb, out->f);
 +        }
 +
 +        if ((force_flags & GMX_FORCE_VIRIAL) && nnbl == 1)
 +        {
 +            fshift_p = fshift;
 +        }
 +        else
 +        {
 +            fshift_p = out->fshift;
 +
 +            if (clearF == enbvClearFYes)
 +            {
 +                clear_fshift(fshift_p);
 +            }
 +        }
 +
 +        /* With Ewald type electrostatics we the forces for excluded atom pairs
 +         * should not contribute to the virial sum. The exclusion forces
 +         * are not calculate in the energy kernels, but are in _noener.
 +         */
 +        if (!((force_flags & GMX_FORCE_ENERGY) ||
 +              (EEL_FULL(ic->eeltype) && (force_flags & GMX_FORCE_VIRIAL))))
 +        {
 +            /* Don't calculate energies */
 +            p_nbk_noener[coult][nbat->comb_rule](nbl[nb], nbat,
 +                                                 ic,
 +                                                 shift_vec,
 +                                                 out->f,
 +                                                 fshift_p);
 +        }
 +        else if (out->nV == 1 || !(force_flags & GMX_FORCE_ENERGY))
 +        {
 +            /* No energy groups */
 +            out->Vvdw[0] = 0;
 +            out->Vc[0]   = 0;
 +
 +            p_nbk_ener[coult][nbat->comb_rule](nbl[nb], nbat,
 +                                               ic,
 +                                               shift_vec,
 +                                               out->f,
 +                                               fshift_p,
 +                                               out->Vvdw,
 +                                               out->Vc);
 +        }
 +        else
 +        {
 +            /* Calculate energy group contributions */
 +            int i;
 +
 +            for (i = 0; i < out->nVS; i++)
 +            {
 +                out->VSvdw[i] = 0;
 +            }
 +            for (i = 0; i < out->nVS; i++)
 +            {
 +                out->VSc[i] = 0;
 +            }
 +
 +            p_nbk_energrp[coult][nbat->comb_rule](nbl[nb], nbat,
 +                                                  ic,
 +                                                  shift_vec,
 +                                                  out->f,
 +                                                  fshift_p,
 +                                                  out->VSvdw,
 +                                                  out->VSc);
 +
 +            reduce_group_energies(nbat->nenergrp, nbat->neg_2log,
 +                                  out->VSvdw, out->VSc,
 +                                  out->Vvdw, out->Vc);
 +        }
 +    }
 +
 +    if (force_flags & GMX_FORCE_ENERGY)
 +    {
 +        reduce_energies_over_lists(nbat, nnbl, Vvdw, Vc);
 +    }
 +}
 +#else
 +{
 +    gmx_incons("nbnxn_kernel_simd_4xn called while GROMACS was configured without 4xN SIMD kernels enabled");
 +}
 +#endif
index 9dfaf83ef2c22a9fbd3533893afcb9c688e9886c,0000000000000000000000000000000000000000..329fd97602bb2738e0e7b1fde2b4e3be70a57950
mode 100644,000000..100644
--- /dev/null
@@@ -1,1020 -1,0 +1,997 @@@
-  * this can be faster with blendv (only available with SSE4.1 and later).
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2009, The GROMACS Development Team
 + * Copyright (c) 2012, by the GROMACS development team, led by
 + * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 + * others, as listed in the AUTHORS file in the top-level source
 + * directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +/* This is the innermost loop contents for the 4 x N atom SIMD kernel.
 + * This flavor of the kernel calculates interactions of 4 i-atoms
 + * with N j-atoms stored in N wide SIMD registers.
 + */
 +
 +
 +/* When calculating RF or Ewald interactions we calculate the electrostatic
 + * forces on excluded atom pairs here in the non-bonded loops.
 + * But when energies and/or virial is required we calculate them
 + * separately to as then it is easier to separate the energy and virial
 + * contributions.
 + */
 +#if defined CHECK_EXCLS && defined CALC_COULOMB
 +#define EXCL_FORCES
 +#endif
 +
 +/* Without exclusions and energies we only need to mask the cut-off,
- #if !(defined CHECK_EXCLS || defined CALC_ENERGIES) && defined GMX_X86_SSE4_1 && !defined COUNT_PAIRS
++ * this can be faster when we have defined gmx_blendv_pr, i.e. an instruction
++ * that selects from two SIMD registers based on the contents of a third.
 + */
- #define CUTOFF_BLENDV
++#if !(defined CHECK_EXCLS || defined CALC_ENERGIES) && defined GMX_HAVE_SIMD_BLENDV && !defined COUNT_PAIRS
 +/* With RF and tabulated Coulomb we replace cmp+and with sub+blendv.
 + * With gcc this is slower, except for RF on Sandy Bridge.
 + * Tested with gcc 4.6.2, 4.6.3 and 4.7.1.
 + */
 +#if (defined CALC_COUL_RF || defined CALC_COUL_TAB) && (!defined __GNUC__ || (defined CALC_COUL_RF && defined GMX_X86_AVX_256))
- #define CUTOFF_BLENDV
++#define NBNXN_CUTOFF_USE_BLENDV
 +#endif
 +/* With analytical Ewald we replace cmp+and+and with sub+blendv+blendv.
 + * This is only faster with icc on Sandy Bridge (PS kernel slower than gcc 4.7).
 + * Tested with icc 13.
 + */
 +#if defined CALC_COUL_EWALD && defined __INTEL_COMPILER && defined GMX_X86_AVX_256
-     gmx_mm_pr  int_SSE0;
-     gmx_mm_pr  int_SSE1;
-     gmx_mm_pr  int_SSE2;
-     gmx_mm_pr  int_SSE3;
- #endif
-     gmx_mm_pr  jxSSE, jySSE, jzSSE;
-     gmx_mm_pr  dx_SSE0, dy_SSE0, dz_SSE0;
-     gmx_mm_pr  dx_SSE1, dy_SSE1, dz_SSE1;
-     gmx_mm_pr  dx_SSE2, dy_SSE2, dz_SSE2;
-     gmx_mm_pr  dx_SSE3, dy_SSE3, dz_SSE3;
-     gmx_mm_pr  tx_SSE0, ty_SSE0, tz_SSE0;
-     gmx_mm_pr  tx_SSE1, ty_SSE1, tz_SSE1;
-     gmx_mm_pr  tx_SSE2, ty_SSE2, tz_SSE2;
-     gmx_mm_pr  tx_SSE3, ty_SSE3, tz_SSE3;
-     gmx_mm_pr  rsq_SSE0, rinv_SSE0, rinvsq_SSE0;
-     gmx_mm_pr  rsq_SSE1, rinv_SSE1, rinvsq_SSE1;
-     gmx_mm_pr  rsq_SSE2, rinv_SSE2, rinvsq_SSE2;
-     gmx_mm_pr  rsq_SSE3, rinv_SSE3, rinvsq_SSE3;
- #ifndef CUTOFF_BLENDV
++#define NBNXN_CUTOFF_USE_BLENDV
 +#endif
 +#endif
 +
 +{
 +    int        cj, aj, ajx, ajy, ajz;
 +
 +#ifdef ENERGY_GROUPS
 +    /* Energy group indices for two atoms packed into one int */
 +    int        egp_jj[UNROLLJ/2];
 +#endif
 +
 +#ifdef CHECK_EXCLS
 +    /* Interaction (non-exclusion) mask of all 1's or 0's */
-     gmx_mm_pr  wco_SSE0;
-     gmx_mm_pr  wco_SSE1;
-     gmx_mm_pr  wco_SSE2;
-     gmx_mm_pr  wco_SSE3;
++    gmx_mm_pr  int_S0;
++    gmx_mm_pr  int_S1;
++    gmx_mm_pr  int_S2;
++    gmx_mm_pr  int_S3;
++#endif
++
++    gmx_mm_pr  jx_S, jy_S, jz_S;
++    gmx_mm_pr  dx_S0, dy_S0, dz_S0;
++    gmx_mm_pr  dx_S1, dy_S1, dz_S1;
++    gmx_mm_pr  dx_S2, dy_S2, dz_S2;
++    gmx_mm_pr  dx_S3, dy_S3, dz_S3;
++    gmx_mm_pr  tx_S0, ty_S0, tz_S0;
++    gmx_mm_pr  tx_S1, ty_S1, tz_S1;
++    gmx_mm_pr  tx_S2, ty_S2, tz_S2;
++    gmx_mm_pr  tx_S3, ty_S3, tz_S3;
++    gmx_mm_pr  rsq_S0, rinv_S0, rinvsq_S0;
++    gmx_mm_pr  rsq_S1, rinv_S1, rinvsq_S1;
++    gmx_mm_pr  rsq_S2, rinv_S2, rinvsq_S2;
++    gmx_mm_pr  rsq_S3, rinv_S3, rinvsq_S3;
++#ifndef NBNXN_CUTOFF_USE_BLENDV
 +    /* wco: within cut-off, mask of all 1's or 0's */
-     gmx_mm_pr  wco_vdw_SSE0;
-     gmx_mm_pr  wco_vdw_SSE1;
++    gmx_mm_pr  wco_S0;
++    gmx_mm_pr  wco_S1;
++    gmx_mm_pr  wco_S2;
++    gmx_mm_pr  wco_S3;
 +#endif
 +#ifdef VDW_CUTOFF_CHECK
-     gmx_mm_pr  wco_vdw_SSE2;
-     gmx_mm_pr  wco_vdw_SSE3;
++    gmx_mm_pr  wco_vdw_S0;
++    gmx_mm_pr  wco_vdw_S1;
 +#ifndef HALF_LJ
-     gmx_mm_pr  rinv_ex_SSE0;
-     gmx_mm_pr  rinv_ex_SSE1;
-     gmx_mm_pr  rinv_ex_SSE2;
-     gmx_mm_pr  rinv_ex_SSE3;
- #endif
-     gmx_mm_pr  jq_SSE;
-     gmx_mm_pr  qq_SSE0;
-     gmx_mm_pr  qq_SSE1;
-     gmx_mm_pr  qq_SSE2;
-     gmx_mm_pr  qq_SSE3;
++    gmx_mm_pr  wco_vdw_S2;
++    gmx_mm_pr  wco_vdw_S3;
 +#endif
 +#endif
 +#ifdef CALC_COULOMB
 +#ifdef CHECK_EXCLS
 +    /* 1/r masked with the interaction mask */
-     gmx_mm_pr  fsub_SSE0;
-     gmx_mm_pr  fsub_SSE1;
-     gmx_mm_pr  fsub_SSE2;
-     gmx_mm_pr  fsub_SSE3;
++    gmx_mm_pr  rinv_ex_S0;
++    gmx_mm_pr  rinv_ex_S1;
++    gmx_mm_pr  rinv_ex_S2;
++    gmx_mm_pr  rinv_ex_S3;
++#endif
++    gmx_mm_pr  jq_S;
++    gmx_mm_pr  qq_S0;
++    gmx_mm_pr  qq_S1;
++    gmx_mm_pr  qq_S2;
++    gmx_mm_pr  qq_S3;
 +#ifdef CALC_COUL_TAB
 +    /* The force (PME mesh force) we need to subtract from 1/r^2 */
-     gmx_mm_pr  brsq_SSE0, brsq_SSE1, brsq_SSE2, brsq_SSE3;
-     gmx_mm_pr  ewcorr_SSE0, ewcorr_SSE1, ewcorr_SSE2, ewcorr_SSE3;
++    gmx_mm_pr  fsub_S0;
++    gmx_mm_pr  fsub_S1;
++    gmx_mm_pr  fsub_S2;
++    gmx_mm_pr  fsub_S3;
 +#endif
 +#ifdef CALC_COUL_EWALD
-     gmx_mm_pr  frcoul_SSE0;
-     gmx_mm_pr  frcoul_SSE1;
-     gmx_mm_pr  frcoul_SSE2;
-     gmx_mm_pr  frcoul_SSE3;
++    gmx_mm_pr  brsq_S0, brsq_S1, brsq_S2, brsq_S3;
++    gmx_mm_pr  ewcorr_S0, ewcorr_S1, ewcorr_S2, ewcorr_S3;
 +#endif
 +
 +    /* frcoul = (1/r - fsub)*r */
-     gmx_mm_pr  r_SSE0, rs_SSE0, rf_SSE0, frac_SSE0;
-     gmx_mm_pr  r_SSE1, rs_SSE1, rf_SSE1, frac_SSE1;
-     gmx_mm_pr  r_SSE2, rs_SSE2, rf_SSE2, frac_SSE2;
-     gmx_mm_pr  r_SSE3, rs_SSE3, rf_SSE3, frac_SSE3;
++    gmx_mm_pr  frcoul_S0;
++    gmx_mm_pr  frcoul_S1;
++    gmx_mm_pr  frcoul_S2;
++    gmx_mm_pr  frcoul_S3;
 +#ifdef CALC_COUL_TAB
 +    /* For tables: r, rs=r/sp, rf=floor(rs), frac=rs-rf */
- #if !(defined GMX_MM256_HERE && defined GMX_DOUBLE)
-     gmx_epi32  ti_SSE0, ti_SSE1, ti_SSE2, ti_SSE3;
- #else
-     __m128i    ti_SSE0, ti_SSE1, ti_SSE2, ti_SSE3;
- #endif
++    gmx_mm_pr  r_S0, rs_S0, rf_S0, frac_S0;
++    gmx_mm_pr  r_S1, rs_S1, rf_S1, frac_S1;
++    gmx_mm_pr  r_S2, rs_S2, rf_S2, frac_S2;
++    gmx_mm_pr  r_S3, rs_S3, rf_S3, frac_S3;
 +    /* Table index: rs truncated to an int */
-     gmx_mm_pr  ctab0_SSE0, ctab1_SSE0;
-     gmx_mm_pr  ctab0_SSE1, ctab1_SSE1;
-     gmx_mm_pr  ctab0_SSE2, ctab1_SSE2;
-     gmx_mm_pr  ctab0_SSE3, ctab1_SSE3;
++    gmx_epi32  ti_S0, ti_S1, ti_S2, ti_S3;
 +    /* Linear force table values */
-     gmx_mm_pr  ctabv_SSE0;
-     gmx_mm_pr  ctabv_SSE1;
-     gmx_mm_pr  ctabv_SSE2;
-     gmx_mm_pr  ctabv_SSE3;
++    gmx_mm_pr  ctab0_S0, ctab1_S0;
++    gmx_mm_pr  ctab0_S1, ctab1_S1;
++    gmx_mm_pr  ctab0_S2, ctab1_S2;
++    gmx_mm_pr  ctab0_S3, ctab1_S3;
 +#ifdef CALC_ENERGIES
 +    /* Quadratic energy table value */
-     gmx_mm_pr  vc_sub_SSE0;
-     gmx_mm_pr  vc_sub_SSE1;
-     gmx_mm_pr  vc_sub_SSE2;
-     gmx_mm_pr  vc_sub_SSE3;
++    gmx_mm_pr  ctabv_S0;
++    gmx_mm_pr  ctabv_S1;
++    gmx_mm_pr  ctabv_S2;
++    gmx_mm_pr  ctabv_S3;
 +#endif
 +#endif
 +#if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
 +    /* The potential (PME mesh) we need to subtract from 1/r */
-     gmx_mm_pr  vcoul_SSE0;
-     gmx_mm_pr  vcoul_SSE1;
-     gmx_mm_pr  vcoul_SSE2;
-     gmx_mm_pr  vcoul_SSE3;
++    gmx_mm_pr  vc_sub_S0;
++    gmx_mm_pr  vc_sub_S1;
++    gmx_mm_pr  vc_sub_S2;
++    gmx_mm_pr  vc_sub_S3;
 +#endif
 +#ifdef CALC_ENERGIES
 +    /* Electrostatic potential */
-     gmx_mm_pr  fscal_SSE0;
-     gmx_mm_pr  fscal_SSE1;
-     gmx_mm_pr  fscal_SSE2;
-     gmx_mm_pr  fscal_SSE3;
++    gmx_mm_pr  vcoul_S0;
++    gmx_mm_pr  vcoul_S1;
++    gmx_mm_pr  vcoul_S2;
++    gmx_mm_pr  vcoul_S3;
 +#endif
 +#endif
 +    /* The force times 1/r */
-     gmx_mm_pr  hsig_j_SSE, seps_j_SSE;
++    gmx_mm_pr  fscal_S0;
++    gmx_mm_pr  fscal_S1;
++    gmx_mm_pr  fscal_S2;
++    gmx_mm_pr  fscal_S3;
 +
 +#ifdef CALC_LJ
 +#ifdef LJ_COMB_LB
 +    /* LJ sigma_j/2 and sqrt(epsilon_j) */
-     gmx_mm_pr  sig_SSE0, eps_SSE0;
-     gmx_mm_pr  sig_SSE1, eps_SSE1;
++    gmx_mm_pr  hsig_j_S, seps_j_S;
 +    /* LJ sigma_ij and epsilon_ij */
-     gmx_mm_pr  sig_SSE2, eps_SSE2;
-     gmx_mm_pr  sig_SSE3, eps_SSE3;
++    gmx_mm_pr  sig_S0, eps_S0;
++    gmx_mm_pr  sig_S1, eps_S1;
 +#ifndef HALF_LJ
-     gmx_mm_pr  sig2_SSE0, sig6_SSE0;
-     gmx_mm_pr  sig2_SSE1, sig6_SSE1;
++    gmx_mm_pr  sig_S2, eps_S2;
++    gmx_mm_pr  sig_S3, eps_S3;
 +#endif
 +#ifdef CALC_ENERGIES
-     gmx_mm_pr  sig2_SSE2, sig6_SSE2;
-     gmx_mm_pr  sig2_SSE3, sig6_SSE3;
++    gmx_mm_pr  sig2_S0, sig6_S0;
++    gmx_mm_pr  sig2_S1, sig6_S1;
 +#ifndef HALF_LJ
-     gmx_mm_pr  c6s_j_SSE, c12s_j_SSE;
++    gmx_mm_pr  sig2_S2, sig6_S2;
++    gmx_mm_pr  sig2_S3, sig6_S3;
 +#endif
 +#endif /* LJ_COMB_LB */
 +#endif /* CALC_LJ */
 +
 +#ifdef LJ_COMB_GEOM
-     gmx_mm_pr  c6_SSE0, c12_SSE0;
-     gmx_mm_pr  c6_SSE1, c12_SSE1;
++    gmx_mm_pr  c6s_j_S, c12s_j_S;
 +#endif
 +
 +#if defined LJ_COMB_GEOM || defined LJ_COMB_LB
 +    /* Index for loading LJ parameters, complicated when interleaving */
 +    int         aj2;
 +#endif
 +
 +#ifndef FIX_LJ_C
 +    /* LJ C6 and C12 parameters, used with geometric comb. rule */
-     gmx_mm_pr  c6_SSE2, c12_SSE2;
-     gmx_mm_pr  c6_SSE3, c12_SSE3;
++    gmx_mm_pr  c6_S0, c12_S0;
++    gmx_mm_pr  c6_S1, c12_S1;
 +#ifndef HALF_LJ
-     gmx_mm_pr  rinvsix_SSE0;
-     gmx_mm_pr  rinvsix_SSE1;
++    gmx_mm_pr  c6_S2, c12_S2;
++    gmx_mm_pr  c6_S3, c12_S3;
 +#endif
 +#endif
 +
 +    /* Intermediate variables for LJ calculation */
 +#ifndef LJ_COMB_LB
-     gmx_mm_pr  rinvsix_SSE2;
-     gmx_mm_pr  rinvsix_SSE3;
++    gmx_mm_pr  rinvsix_S0;
++    gmx_mm_pr  rinvsix_S1;
 +#ifndef HALF_LJ
-     gmx_mm_pr  sir_SSE0, sir2_SSE0, sir6_SSE0;
-     gmx_mm_pr  sir_SSE1, sir2_SSE1, sir6_SSE1;
++    gmx_mm_pr  rinvsix_S2;
++    gmx_mm_pr  rinvsix_S3;
 +#endif
 +#endif
 +#ifdef LJ_COMB_LB
-     gmx_mm_pr  sir_SSE2, sir2_SSE2, sir6_SSE2;
-     gmx_mm_pr  sir_SSE3, sir2_SSE3, sir6_SSE3;
++    gmx_mm_pr  sir_S0, sir2_S0, sir6_S0;
++    gmx_mm_pr  sir_S1, sir2_S1, sir6_S1;
 +#ifndef HALF_LJ
-     gmx_mm_pr  FrLJ6_SSE0, FrLJ12_SSE0;
-     gmx_mm_pr  FrLJ6_SSE1, FrLJ12_SSE1;
++    gmx_mm_pr  sir_S2, sir2_S2, sir6_S2;
++    gmx_mm_pr  sir_S3, sir2_S3, sir6_S3;
 +#endif
 +#endif
 +
-     gmx_mm_pr  FrLJ6_SSE2, FrLJ12_SSE2;
-     gmx_mm_pr  FrLJ6_SSE3, FrLJ12_SSE3;
++    gmx_mm_pr  FrLJ6_S0, FrLJ12_S0;
++    gmx_mm_pr  FrLJ6_S1, FrLJ12_S1;
 +#ifndef HALF_LJ
-     gmx_mm_pr  VLJ6_SSE0, VLJ12_SSE0, VLJ_SSE0;
-     gmx_mm_pr  VLJ6_SSE1, VLJ12_SSE1, VLJ_SSE1;
++    gmx_mm_pr  FrLJ6_S2, FrLJ12_S2;
++    gmx_mm_pr  FrLJ6_S3, FrLJ12_S3;
 +#endif
 +#ifdef CALC_ENERGIES
-     gmx_mm_pr  VLJ6_SSE2, VLJ12_SSE2, VLJ_SSE2;
-     gmx_mm_pr  VLJ6_SSE3, VLJ12_SSE3, VLJ_SSE3;
++    gmx_mm_pr  VLJ6_S0, VLJ12_S0, VLJ_S0;
++    gmx_mm_pr  VLJ6_S1, VLJ12_S1, VLJ_S1;
 +#ifndef HALF_LJ
- #if defined GMX_X86_SSE2 && defined GMX_MM128_HERE
++    gmx_mm_pr  VLJ6_S2, VLJ12_S2, VLJ_S2;
++    gmx_mm_pr  VLJ6_S3, VLJ12_S3, VLJ_S3;
 +#endif
 +#endif
 +#endif /* CALC_LJ */
 +
 +    /* j-cluster index */
 +    cj            = l_cj[cjind].cj;
 +
 +    /* Atom indices (of the first atom in the cluster) */
 +    aj            = cj*UNROLLJ;
 +#if defined CALC_LJ && (defined LJ_COMB_GEOM || defined LJ_COMB_LB)
 +#if UNROLLJ == STRIDE
 +    aj2           = aj*2;
 +#else
 +    aj2           = (cj>>1)*2*STRIDE + (cj & 1)*UNROLLJ;
 +#endif
 +#endif
 +#if UNROLLJ == STRIDE
 +    ajx           = aj*DIM;
 +#else
 +    ajx           = (cj>>1)*DIM*STRIDE + (cj & 1)*UNROLLJ;
 +#endif
 +    ajy           = ajx + STRIDE;
 +    ajz           = ajy + STRIDE;
 +
 +#ifdef CHECK_EXCLS
-         /* Load integer interaction mask */
-         __m128i mask_int = _mm_set1_epi32(l_cj[cjind].excl);
++#ifdef gmx_checkbitmask_epi32
 +    {
-         int_SSE0  = gmx_mm_castsi128_pr(_mm_cmpeq_epi32(_mm_andnot_si128(mask_int, mask0), zeroi_SSE));
-         int_SSE1  = gmx_mm_castsi128_pr(_mm_cmpeq_epi32(_mm_andnot_si128(mask_int, mask1), zeroi_SSE));
-         int_SSE2  = gmx_mm_castsi128_pr(_mm_cmpeq_epi32(_mm_andnot_si128(mask_int, mask2), zeroi_SSE));
-         int_SSE3  = gmx_mm_castsi128_pr(_mm_cmpeq_epi32(_mm_andnot_si128(mask_int, mask3), zeroi_SSE));
++        /* Integer mask set and operations, cast result to real */
++        gmx_epi32 mask_pr_S = gmx_set1_epi32(l_cj[cjind].excl);
 +
- #endif
- #if defined GMX_X86_SSE2 && defined GMX_MM256_HERE
-     {
- #ifndef GMX_DOUBLE
-         /* Load integer interaction mask */
-         /* With AVX there are no integer operations, so cast to real */
-         gmx_mm_pr mask_pr = gmx_mm_castsi256_pr(_mm256_set1_epi32(l_cj[cjind].excl));
-         /* We can't compare all 4*8=32 float bits: shift the mask */
-         gmx_mm_pr masksh_pr = gmx_mm_castsi256_pr(_mm256_set1_epi32(l_cj[cjind].excl>>(2*UNROLLJ)));
-         /* Intel Compiler version 12.1.3 20120130 is buggy: use cast.
-          * With gcc we don't need the cast, but it's faster.
-          */
- #define cast_cvt(x)  _mm256_cvtepi32_ps(_mm256_castps_si256(x))
-         int_SSE0  = gmx_cmpneq_pr(cast_cvt(gmx_and_pr(mask_pr, mask0)), zero_SSE);
-         int_SSE1  = gmx_cmpneq_pr(cast_cvt(gmx_and_pr(mask_pr, mask1)), zero_SSE);
-         int_SSE2  = gmx_cmpneq_pr(cast_cvt(gmx_and_pr(masksh_pr, mask0)), zero_SSE);
-         int_SSE3  = gmx_cmpneq_pr(cast_cvt(gmx_and_pr(masksh_pr, mask1)), zero_SSE);
- #undef cast_cvt
++        int_S0  = gmx_castsi_pr(gmx_checkbitmask_epi32(mask_pr_S, mask_S0));
++        int_S1  = gmx_castsi_pr(gmx_checkbitmask_epi32(mask_pr_S, mask_S1));
++        int_S2  = gmx_castsi_pr(gmx_checkbitmask_epi32(mask_pr_S, mask_S2));
++        int_S3  = gmx_castsi_pr(gmx_checkbitmask_epi32(mask_pr_S, mask_S3));
 +    }
-         /* Load integer interaction mask */
-         /* With AVX there are no integer operations,
-          * and there is no int to double conversion, so cast to float
-          */
-         __m256 mask_ps = _mm256_castsi256_ps(_mm256_set1_epi32(l_cj[cjind].excl));
- #define cast_cvt(x)  _mm256_castps_pd(_mm256_cvtepi32_ps(_mm256_castps_si256(x)))
-         int_SSE0  = gmx_cmpneq_pr(cast_cvt(_mm256_and_ps(mask_ps, mask0)), zero_SSE);
-         int_SSE1  = gmx_cmpneq_pr(cast_cvt(_mm256_and_ps(mask_ps, mask1)), zero_SSE);
-         int_SSE2  = gmx_cmpneq_pr(cast_cvt(_mm256_and_ps(mask_ps, mask2)), zero_SSE);
-         int_SSE3  = gmx_cmpneq_pr(cast_cvt(_mm256_and_ps(mask_ps, mask3)), zero_SSE);
- #undef cast_cvt
- #endif
 +#else
-     jxSSE         = gmx_load_pr(x+ajx);
-     jySSE         = gmx_load_pr(x+ajy);
-     jzSSE         = gmx_load_pr(x+ajz);
++    {
++        /* Integer mask set, cast to real and real mask operations */
++        gmx_mm_pr mask_pr_S = gmx_castsi_pr(gmx_set1_epi32(l_cj[cjind].excl));
++
++        int_S0  = gmx_checkbitmask_pr(mask_pr_S, mask_S0);
++        int_S1  = gmx_checkbitmask_pr(mask_pr_S, mask_S1);
++        int_S2  = gmx_checkbitmask_pr(mask_pr_S, mask_S2);
++        int_S3  = gmx_checkbitmask_pr(mask_pr_S, mask_S3);
 +    }
 +#endif
 +#endif
++
 +    /* load j atom coordinates */
-     dx_SSE0       = gmx_sub_pr(ix_SSE0, jxSSE);
-     dy_SSE0       = gmx_sub_pr(iy_SSE0, jySSE);
-     dz_SSE0       = gmx_sub_pr(iz_SSE0, jzSSE);
-     dx_SSE1       = gmx_sub_pr(ix_SSE1, jxSSE);
-     dy_SSE1       = gmx_sub_pr(iy_SSE1, jySSE);
-     dz_SSE1       = gmx_sub_pr(iz_SSE1, jzSSE);
-     dx_SSE2       = gmx_sub_pr(ix_SSE2, jxSSE);
-     dy_SSE2       = gmx_sub_pr(iy_SSE2, jySSE);
-     dz_SSE2       = gmx_sub_pr(iz_SSE2, jzSSE);
-     dx_SSE3       = gmx_sub_pr(ix_SSE3, jxSSE);
-     dy_SSE3       = gmx_sub_pr(iy_SSE3, jySSE);
-     dz_SSE3       = gmx_sub_pr(iz_SSE3, jzSSE);
++    jx_S        = gmx_load_pr(x+ajx);
++    jy_S        = gmx_load_pr(x+ajy);
++    jz_S        = gmx_load_pr(x+ajz);
 +
 +    /* Calculate distance */
-     rsq_SSE0      = gmx_calc_rsq_pr(dx_SSE0, dy_SSE0, dz_SSE0);
-     rsq_SSE1      = gmx_calc_rsq_pr(dx_SSE1, dy_SSE1, dz_SSE1);
-     rsq_SSE2      = gmx_calc_rsq_pr(dx_SSE2, dy_SSE2, dz_SSE2);
-     rsq_SSE3      = gmx_calc_rsq_pr(dx_SSE3, dy_SSE3, dz_SSE3);
++    dx_S0       = gmx_sub_pr(ix_S0, jx_S);
++    dy_S0       = gmx_sub_pr(iy_S0, jy_S);
++    dz_S0       = gmx_sub_pr(iz_S0, jz_S);
++    dx_S1       = gmx_sub_pr(ix_S1, jx_S);
++    dy_S1       = gmx_sub_pr(iy_S1, jy_S);
++    dz_S1       = gmx_sub_pr(iz_S1, jz_S);
++    dx_S2       = gmx_sub_pr(ix_S2, jx_S);
++    dy_S2       = gmx_sub_pr(iy_S2, jy_S);
++    dz_S2       = gmx_sub_pr(iz_S2, jz_S);
++    dx_S3       = gmx_sub_pr(ix_S3, jx_S);
++    dy_S3       = gmx_sub_pr(iy_S3, jy_S);
++    dz_S3       = gmx_sub_pr(iz_S3, jz_S);
 +
 +    /* rsq = dx*dx+dy*dy+dz*dz */
- #ifndef CUTOFF_BLENDV
-     wco_SSE0      = gmx_cmplt_pr(rsq_SSE0, rc2_SSE);
-     wco_SSE1      = gmx_cmplt_pr(rsq_SSE1, rc2_SSE);
-     wco_SSE2      = gmx_cmplt_pr(rsq_SSE2, rc2_SSE);
-     wco_SSE3      = gmx_cmplt_pr(rsq_SSE3, rc2_SSE);
++    rsq_S0      = gmx_calc_rsq_pr(dx_S0, dy_S0, dz_S0);
++    rsq_S1      = gmx_calc_rsq_pr(dx_S1, dy_S1, dz_S1);
++    rsq_S2      = gmx_calc_rsq_pr(dx_S2, dy_S2, dz_S2);
++    rsq_S3      = gmx_calc_rsq_pr(dx_S3, dy_S3, dz_S3);
 +
-         wco_SSE0  = gmx_and_pr(wco_SSE0, diag_SSE0);
-         wco_SSE1  = gmx_and_pr(wco_SSE1, diag_SSE1);
-         wco_SSE2  = gmx_and_pr(wco_SSE2, diag_SSE2);
-         wco_SSE3  = gmx_and_pr(wco_SSE3, diag_SSE3);
++#ifndef NBNXN_CUTOFF_USE_BLENDV
++    wco_S0      = gmx_cmplt_pr(rsq_S0, rc2_S);
++    wco_S1      = gmx_cmplt_pr(rsq_S1, rc2_S);
++    wco_S2      = gmx_cmplt_pr(rsq_S2, rc2_S);
++    wco_S3      = gmx_cmplt_pr(rsq_S3, rc2_S);
 +#endif
 +
 +#ifdef CHECK_EXCLS
 +#ifdef EXCL_FORCES
 +    /* Only remove the (sub-)diagonal to avoid double counting */
 +#if UNROLLJ == UNROLLI
 +    if (cj == ci_sh)
 +    {
-         wco_SSE0  = gmx_and_pr(wco_SSE0, diag0_SSE0);
-         wco_SSE1  = gmx_and_pr(wco_SSE1, diag0_SSE1);
-         wco_SSE2  = gmx_and_pr(wco_SSE2, diag0_SSE2);
-         wco_SSE3  = gmx_and_pr(wco_SSE3, diag0_SSE3);
++        wco_S0  = gmx_and_pr(wco_S0, diag_S0);
++        wco_S1  = gmx_and_pr(wco_S1, diag_S1);
++        wco_S2  = gmx_and_pr(wco_S2, diag_S2);
++        wco_S3  = gmx_and_pr(wco_S3, diag_S3);
 +    }
 +#else
 +#if UNROLLJ < UNROLLI
 +    if (cj == ci_sh*2)
 +    {
-         wco_SSE0  = gmx_and_pr(wco_SSE0, diag1_SSE0);
-         wco_SSE1  = gmx_and_pr(wco_SSE1, diag1_SSE1);
-         wco_SSE2  = gmx_and_pr(wco_SSE2, diag1_SSE2);
-         wco_SSE3  = gmx_and_pr(wco_SSE3, diag1_SSE3);
++        wco_S0  = gmx_and_pr(wco_S0, diag0_S0);
++        wco_S1  = gmx_and_pr(wco_S1, diag0_S1);
++        wco_S2  = gmx_and_pr(wco_S2, diag0_S2);
++        wco_S3  = gmx_and_pr(wco_S3, diag0_S3);
 +    }
 +    if (cj == ci_sh*2 + 1)
 +    {
-         wco_SSE0  = gmx_and_pr(wco_SSE0, diag0_SSE0);
-         wco_SSE1  = gmx_and_pr(wco_SSE1, diag0_SSE1);
-         wco_SSE2  = gmx_and_pr(wco_SSE2, diag0_SSE2);
-         wco_SSE3  = gmx_and_pr(wco_SSE3, diag0_SSE3);
++        wco_S0  = gmx_and_pr(wco_S0, diag1_S0);
++        wco_S1  = gmx_and_pr(wco_S1, diag1_S1);
++        wco_S2  = gmx_and_pr(wco_S2, diag1_S2);
++        wco_S3  = gmx_and_pr(wco_S3, diag1_S3);
 +    }
 +#else
 +    if (cj*2 == ci_sh)
 +    {
-         wco_SSE0  = gmx_and_pr(wco_SSE0, diag1_SSE0);
-         wco_SSE1  = gmx_and_pr(wco_SSE1, diag1_SSE1);
-         wco_SSE2  = gmx_and_pr(wco_SSE2, diag1_SSE2);
-         wco_SSE3  = gmx_and_pr(wco_SSE3, diag1_SSE3);
++        wco_S0  = gmx_and_pr(wco_S0, diag0_S0);
++        wco_S1  = gmx_and_pr(wco_S1, diag0_S1);
++        wco_S2  = gmx_and_pr(wco_S2, diag0_S2);
++        wco_S3  = gmx_and_pr(wco_S3, diag0_S3);
 +    }
 +    else if (cj*2 + 1 == ci_sh)
 +    {
-       /* Remove all excluded atom pairs from the list */
-     wco_SSE0      = gmx_and_pr(wco_SSE0, int_SSE0);
-     wco_SSE1      = gmx_and_pr(wco_SSE1, int_SSE1);
-     wco_SSE2      = gmx_and_pr(wco_SSE2, int_SSE2);
-     wco_SSE3      = gmx_and_pr(wco_SSE3, int_SSE3);
++        wco_S0  = gmx_and_pr(wco_S0, diag1_S0);
++        wco_S1  = gmx_and_pr(wco_S1, diag1_S1);
++        wco_S2  = gmx_and_pr(wco_S2, diag1_S2);
++        wco_S3  = gmx_and_pr(wco_S3, diag1_S3);
 +    }
 +#endif
 +#endif
 +#else /* EXCL_FORCES */
-         real tmp[UNROLLJ];
++    /* No exclusion forces: remove all excluded atom pairs from the list */
++    wco_S0      = gmx_and_pr(wco_S0, int_S0);
++    wco_S1      = gmx_and_pr(wco_S1, int_S1);
++    wco_S2      = gmx_and_pr(wco_S2, int_S2);
++    wco_S3      = gmx_and_pr(wco_S3, int_S3);
 +#endif
 +#endif
 +
 +#ifdef COUNT_PAIRS
 +    {
 +        int  i, j;
-             gmx_storeu_pr(tmp, i == 0 ? wco_SSE0 : (i == 1 ? wco_SSE1 : (i == 2 ? wco_SSE2 : wco_SSE3)));
++        real tmpa[2*GMX_SIMD_WIDTH_HERE], *tmp;
++        tmp = gmx_simd_align_real(tmpa);
 +        for (i = 0; i < UNROLLI; i++)
 +        {
-     rsq_SSE0      = gmx_add_pr(rsq_SSE0, gmx_andnot_pr(int_SSE0, avoid_sing_SSE));
-     rsq_SSE1      = gmx_add_pr(rsq_SSE1, gmx_andnot_pr(int_SSE1, avoid_sing_SSE));
-     rsq_SSE2      = gmx_add_pr(rsq_SSE2, gmx_andnot_pr(int_SSE2, avoid_sing_SSE));
-     rsq_SSE3      = gmx_add_pr(rsq_SSE3, gmx_andnot_pr(int_SSE3, avoid_sing_SSE));
++            gmx_store_pr(tmp, i == 0 ? wco_S0 : (i == 1 ? wco_S1 : (i == 2 ? wco_S2 : wco_S3)));
 +            for (j = 0; j < UNROLLJ; j++)
 +            {
 +                if (!(tmp[j] == 0))
 +                {
 +                    npair++;
 +                }
 +            }
 +        }
 +    }
 +#endif
 +
 +#ifdef CHECK_EXCLS
 +    /* For excluded pairs add a small number to avoid r^-6 = NaN */
-     rinv_SSE0     = gmx_invsqrt_pr(rsq_SSE0);
-     rinv_SSE1     = gmx_invsqrt_pr(rsq_SSE1);
-     rinv_SSE2     = gmx_invsqrt_pr(rsq_SSE2);
-     rinv_SSE3     = gmx_invsqrt_pr(rsq_SSE3);
++    rsq_S0      = gmx_add_pr(rsq_S0, gmx_andnot_pr(int_S0, avoid_sing_S));
++    rsq_S1      = gmx_add_pr(rsq_S1, gmx_andnot_pr(int_S1, avoid_sing_S));
++    rsq_S2      = gmx_add_pr(rsq_S2, gmx_andnot_pr(int_S2, avoid_sing_S));
++    rsq_S3      = gmx_add_pr(rsq_S3, gmx_andnot_pr(int_S3, avoid_sing_S));
 +#endif
 +
 +    /* Calculate 1/r */
 +#ifndef GMX_DOUBLE
-     GMX_MM_INVSQRT2_PD(rsq_SSE0, rsq_SSE1, rinv_SSE0, rinv_SSE1);
-     GMX_MM_INVSQRT2_PD(rsq_SSE2, rsq_SSE3, rinv_SSE2, rinv_SSE3);
++    rinv_S0     = gmx_invsqrt_pr(rsq_S0);
++    rinv_S1     = gmx_invsqrt_pr(rsq_S1);
++    rinv_S2     = gmx_invsqrt_pr(rsq_S2);
++    rinv_S3     = gmx_invsqrt_pr(rsq_S3);
 +#else
-     jq_SSE        = gmx_load_pr(q+aj);
-     qq_SSE0       = gmx_mul_pr(iq_SSE0, jq_SSE);
-     qq_SSE1       = gmx_mul_pr(iq_SSE1, jq_SSE);
-     qq_SSE2       = gmx_mul_pr(iq_SSE2, jq_SSE);
-     qq_SSE3       = gmx_mul_pr(iq_SSE3, jq_SSE);
++    GMX_MM_INVSQRT2_PD(rsq_S0, rsq_S1, rinv_S0, rinv_S1);
++    GMX_MM_INVSQRT2_PD(rsq_S2, rsq_S3, rinv_S2, rinv_S3);
 +#endif
 +
 +#ifdef CALC_COULOMB
 +    /* Load parameters for j atom */
-     load_lj_pair_params(nbfp0, type, aj, c6_SSE0, c12_SSE0);
-     load_lj_pair_params(nbfp1, type, aj, c6_SSE1, c12_SSE1);
++    jq_S        = gmx_load_pr(q+aj);
++    qq_S0       = gmx_mul_pr(iq_S0, jq_S);
++    qq_S1       = gmx_mul_pr(iq_S1, jq_S);
++    qq_S2       = gmx_mul_pr(iq_S2, jq_S);
++    qq_S3       = gmx_mul_pr(iq_S3, jq_S);
 +#endif
 +
 +#ifdef CALC_LJ
 +
 +#if !defined LJ_COMB_GEOM && !defined LJ_COMB_LB && !defined FIX_LJ_C
-     load_lj_pair_params(nbfp2, type, aj, c6_SSE2, c12_SSE2);
-     load_lj_pair_params(nbfp3, type, aj, c6_SSE3, c12_SSE3);
++    load_lj_pair_params(nbfp0, type, aj, c6_S0, c12_S0);
++    load_lj_pair_params(nbfp1, type, aj, c6_S1, c12_S1);
 +#ifndef HALF_LJ
-     c6s_j_SSE     = gmx_load_pr(ljc+aj2+0);
-     c12s_j_SSE    = gmx_load_pr(ljc+aj2+STRIDE);
-     c6_SSE0       = gmx_mul_pr(c6s_SSE0, c6s_j_SSE );
-     c6_SSE1       = gmx_mul_pr(c6s_SSE1, c6s_j_SSE );
++    load_lj_pair_params(nbfp2, type, aj, c6_S2, c12_S2);
++    load_lj_pair_params(nbfp3, type, aj, c6_S3, c12_S3);
 +#endif
 +#endif /* not defined any LJ rule */
 +
 +#ifdef LJ_COMB_GEOM
-     c6_SSE2       = gmx_mul_pr(c6s_SSE2, c6s_j_SSE );
-     c6_SSE3       = gmx_mul_pr(c6s_SSE3, c6s_j_SSE );
++    c6s_j_S     = gmx_load_pr(ljc+aj2+0);
++    c12s_j_S    = gmx_load_pr(ljc+aj2+STRIDE);
++    c6_S0       = gmx_mul_pr(c6s_S0, c6s_j_S );
++    c6_S1       = gmx_mul_pr(c6s_S1, c6s_j_S );
 +#ifndef HALF_LJ
-     c12_SSE0      = gmx_mul_pr(c12s_SSE0, c12s_j_SSE);
-     c12_SSE1      = gmx_mul_pr(c12s_SSE1, c12s_j_SSE);
++    c6_S2       = gmx_mul_pr(c6s_S2, c6s_j_S );
++    c6_S3       = gmx_mul_pr(c6s_S3, c6s_j_S );
 +#endif
-     c12_SSE2      = gmx_mul_pr(c12s_SSE2, c12s_j_SSE);
-     c12_SSE3      = gmx_mul_pr(c12s_SSE3, c12s_j_SSE);
++    c12_S0      = gmx_mul_pr(c12s_S0, c12s_j_S);
++    c12_S1      = gmx_mul_pr(c12s_S1, c12s_j_S);
 +#ifndef HALF_LJ
-     hsig_j_SSE    = gmx_load_pr(ljc+aj2+0);
-     seps_j_SSE    = gmx_load_pr(ljc+aj2+STRIDE);
++    c12_S2      = gmx_mul_pr(c12s_S2, c12s_j_S);
++    c12_S3      = gmx_mul_pr(c12s_S3, c12s_j_S);
 +#endif
 +#endif /* LJ_COMB_GEOM */
 +
 +#ifdef LJ_COMB_LB
-     sig_SSE0      = gmx_add_pr(hsig_i_SSE0, hsig_j_SSE);
-     sig_SSE1      = gmx_add_pr(hsig_i_SSE1, hsig_j_SSE);
-     eps_SSE0      = gmx_mul_pr(seps_i_SSE0, seps_j_SSE);
-     eps_SSE1      = gmx_mul_pr(seps_i_SSE1, seps_j_SSE);
++    hsig_j_S    = gmx_load_pr(ljc+aj2+0);
++    seps_j_S    = gmx_load_pr(ljc+aj2+STRIDE);
 +
-     sig_SSE2      = gmx_add_pr(hsig_i_SSE2, hsig_j_SSE);
-     sig_SSE3      = gmx_add_pr(hsig_i_SSE3, hsig_j_SSE);
-     eps_SSE2      = gmx_mul_pr(seps_i_SSE2, seps_j_SSE);
-     eps_SSE3      = gmx_mul_pr(seps_i_SSE3, seps_j_SSE);
++    sig_S0      = gmx_add_pr(hsig_i_S0, hsig_j_S);
++    sig_S1      = gmx_add_pr(hsig_i_S1, hsig_j_S);
++    eps_S0      = gmx_mul_pr(seps_i_S0, seps_j_S);
++    eps_S1      = gmx_mul_pr(seps_i_S1, seps_j_S);
 +#ifndef HALF_LJ
- #ifndef CUTOFF_BLENDV
-     rinv_SSE0     = gmx_and_pr(rinv_SSE0, wco_SSE0);
-     rinv_SSE1     = gmx_and_pr(rinv_SSE1, wco_SSE1);
-     rinv_SSE2     = gmx_and_pr(rinv_SSE2, wco_SSE2);
-     rinv_SSE3     = gmx_and_pr(rinv_SSE3, wco_SSE3);
++    sig_S2      = gmx_add_pr(hsig_i_S2, hsig_j_S);
++    sig_S3      = gmx_add_pr(hsig_i_S3, hsig_j_S);
++    eps_S2      = gmx_mul_pr(seps_i_S2, seps_j_S);
++    eps_S3      = gmx_mul_pr(seps_i_S3, seps_j_S);
 +#endif
 +#endif /* LJ_COMB_LB */
 +
 +#endif /* CALC_LJ */
 +
-     rinv_SSE0     = gmx_blendv_pr(rinv_SSE0, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE0));
-     rinv_SSE1     = gmx_blendv_pr(rinv_SSE1, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE1));
-     rinv_SSE2     = gmx_blendv_pr(rinv_SSE2, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE2));
-     rinv_SSE3     = gmx_blendv_pr(rinv_SSE3, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE3));
++#ifndef NBNXN_CUTOFF_USE_BLENDV
++    rinv_S0     = gmx_blendzero_pr(rinv_S0, wco_S0);
++    rinv_S1     = gmx_blendzero_pr(rinv_S1, wco_S1);
++    rinv_S2     = gmx_blendzero_pr(rinv_S2, wco_S2);
++    rinv_S3     = gmx_blendzero_pr(rinv_S3, wco_S3);
 +#else
 +    /* We only need to mask for the cut-off: blendv is faster */
-     rinvsq_SSE0   = gmx_mul_pr(rinv_SSE0, rinv_SSE0);
-     rinvsq_SSE1   = gmx_mul_pr(rinv_SSE1, rinv_SSE1);
-     rinvsq_SSE2   = gmx_mul_pr(rinv_SSE2, rinv_SSE2);
-     rinvsq_SSE3   = gmx_mul_pr(rinv_SSE3, rinv_SSE3);
++    rinv_S0     = gmx_blendv_pr(rinv_S0, zero_S, gmx_sub_pr(rc2_S, rsq_S0));
++    rinv_S1     = gmx_blendv_pr(rinv_S1, zero_S, gmx_sub_pr(rc2_S, rsq_S1));
++    rinv_S2     = gmx_blendv_pr(rinv_S2, zero_S, gmx_sub_pr(rc2_S, rsq_S2));
++    rinv_S3     = gmx_blendv_pr(rinv_S3, zero_S, gmx_sub_pr(rc2_S, rsq_S3));
 +#endif
 +
-     rinv_ex_SSE0  = gmx_and_pr(rinv_SSE0, int_SSE0);
-     rinv_ex_SSE1  = gmx_and_pr(rinv_SSE1, int_SSE1);
-     rinv_ex_SSE2  = gmx_and_pr(rinv_SSE2, int_SSE2);
-     rinv_ex_SSE3  = gmx_and_pr(rinv_SSE3, int_SSE3);
++    rinvsq_S0   = gmx_mul_pr(rinv_S0, rinv_S0);
++    rinvsq_S1   = gmx_mul_pr(rinv_S1, rinv_S1);
++    rinvsq_S2   = gmx_mul_pr(rinv_S2, rinv_S2);
++    rinvsq_S3   = gmx_mul_pr(rinv_S3, rinv_S3);
 +
 +#ifdef CALC_COULOMB
 +    /* Note that here we calculate force*r, not the usual force/r.
 +     * This allows avoiding masking the reaction-field contribution,
 +     * as frcoul is later multiplied by rinvsq which has been
 +     * masked with the cut-off check.
 +     */
 +
 +#ifdef EXCL_FORCES
 +    /* Only add 1/r for non-excluded atom pairs */
- #define     rinv_ex_SSE0    rinv_SSE0
- #define     rinv_ex_SSE1    rinv_SSE1
- #define     rinv_ex_SSE2    rinv_SSE2
- #define     rinv_ex_SSE3    rinv_SSE3
++    rinv_ex_S0  = gmx_blendzero_pr(rinv_S0, int_S0);
++    rinv_ex_S1  = gmx_blendzero_pr(rinv_S1, int_S1);
++    rinv_ex_S2  = gmx_blendzero_pr(rinv_S2, int_S2);
++    rinv_ex_S3  = gmx_blendzero_pr(rinv_S3, int_S3);
 +#else
 +    /* No exclusion forces, we always need 1/r */
-     frcoul_SSE0   = gmx_mul_pr(qq_SSE0, gmx_add_pr(rinv_ex_SSE0, gmx_mul_pr(rsq_SSE0, mrc_3_SSE)));
-     frcoul_SSE1   = gmx_mul_pr(qq_SSE1, gmx_add_pr(rinv_ex_SSE1, gmx_mul_pr(rsq_SSE1, mrc_3_SSE)));
-     frcoul_SSE2   = gmx_mul_pr(qq_SSE2, gmx_add_pr(rinv_ex_SSE2, gmx_mul_pr(rsq_SSE2, mrc_3_SSE)));
-     frcoul_SSE3   = gmx_mul_pr(qq_SSE3, gmx_add_pr(rinv_ex_SSE3, gmx_mul_pr(rsq_SSE3, mrc_3_SSE)));
++#define     rinv_ex_S0    rinv_S0
++#define     rinv_ex_S1    rinv_S1
++#define     rinv_ex_S2    rinv_S2
++#define     rinv_ex_S3    rinv_S3
 +#endif
 +
 +#ifdef CALC_COUL_RF
 +    /* Electrostatic interactions */
-     vcoul_SSE0    = gmx_mul_pr(qq_SSE0, gmx_add_pr(rinv_ex_SSE0, gmx_add_pr(gmx_mul_pr(rsq_SSE0, hrc_3_SSE), moh_rc_SSE)));
-     vcoul_SSE1    = gmx_mul_pr(qq_SSE1, gmx_add_pr(rinv_ex_SSE1, gmx_add_pr(gmx_mul_pr(rsq_SSE1, hrc_3_SSE), moh_rc_SSE)));
-     vcoul_SSE2    = gmx_mul_pr(qq_SSE2, gmx_add_pr(rinv_ex_SSE2, gmx_add_pr(gmx_mul_pr(rsq_SSE2, hrc_3_SSE), moh_rc_SSE)));
-     vcoul_SSE3    = gmx_mul_pr(qq_SSE3, gmx_add_pr(rinv_ex_SSE3, gmx_add_pr(gmx_mul_pr(rsq_SSE3, hrc_3_SSE), moh_rc_SSE)));
++    frcoul_S0   = gmx_mul_pr(qq_S0, gmx_add_pr(rinv_ex_S0, gmx_mul_pr(rsq_S0, mrc_3_S)));
++    frcoul_S1   = gmx_mul_pr(qq_S1, gmx_add_pr(rinv_ex_S1, gmx_mul_pr(rsq_S1, mrc_3_S)));
++    frcoul_S2   = gmx_mul_pr(qq_S2, gmx_add_pr(rinv_ex_S2, gmx_mul_pr(rsq_S2, mrc_3_S)));
++    frcoul_S3   = gmx_mul_pr(qq_S3, gmx_add_pr(rinv_ex_S3, gmx_mul_pr(rsq_S3, mrc_3_S)));
 +
 +#ifdef CALC_ENERGIES
- #ifndef CUTOFF_BLENDV
-     brsq_SSE0     = gmx_mul_pr(beta2_SSE, gmx_and_pr(rsq_SSE0, wco_SSE0));
-     brsq_SSE1     = gmx_mul_pr(beta2_SSE, gmx_and_pr(rsq_SSE1, wco_SSE1));
-     brsq_SSE2     = gmx_mul_pr(beta2_SSE, gmx_and_pr(rsq_SSE2, wco_SSE2));
-     brsq_SSE3     = gmx_mul_pr(beta2_SSE, gmx_and_pr(rsq_SSE3, wco_SSE3));
++    vcoul_S0    = gmx_mul_pr(qq_S0, gmx_add_pr(rinv_ex_S0, gmx_add_pr(gmx_mul_pr(rsq_S0, hrc_3_S), moh_rc_S)));
++    vcoul_S1    = gmx_mul_pr(qq_S1, gmx_add_pr(rinv_ex_S1, gmx_add_pr(gmx_mul_pr(rsq_S1, hrc_3_S), moh_rc_S)));
++    vcoul_S2    = gmx_mul_pr(qq_S2, gmx_add_pr(rinv_ex_S2, gmx_add_pr(gmx_mul_pr(rsq_S2, hrc_3_S), moh_rc_S)));
++    vcoul_S3    = gmx_mul_pr(qq_S3, gmx_add_pr(rinv_ex_S3, gmx_add_pr(gmx_mul_pr(rsq_S3, hrc_3_S), moh_rc_S)));
 +#endif
 +#endif
 +
 +#ifdef CALC_COUL_EWALD
 +    /* We need to mask (or limit) rsq for the cut-off,
 +     * as large distances can cause an overflow in gmx_pmecorrF/V.
 +     */
-     brsq_SSE0     = gmx_mul_pr(beta2_SSE, gmx_blendv_pr(rsq_SSE0, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE0)));
-     brsq_SSE1     = gmx_mul_pr(beta2_SSE, gmx_blendv_pr(rsq_SSE1, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE1)));
-     brsq_SSE2     = gmx_mul_pr(beta2_SSE, gmx_blendv_pr(rsq_SSE2, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE2)));
-     brsq_SSE3     = gmx_mul_pr(beta2_SSE, gmx_blendv_pr(rsq_SSE3, zero_SSE, gmx_sub_pr(rc2_SSE, rsq_SSE3)));
- #endif
-     ewcorr_SSE0   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_SSE0), beta_SSE);
-     ewcorr_SSE1   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_SSE1), beta_SSE);
-     ewcorr_SSE2   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_SSE2), beta_SSE);
-     ewcorr_SSE3   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_SSE3), beta_SSE);
-     frcoul_SSE0   = gmx_mul_pr(qq_SSE0, gmx_add_pr(rinv_ex_SSE0, gmx_mul_pr(ewcorr_SSE0, brsq_SSE0)));
-     frcoul_SSE1   = gmx_mul_pr(qq_SSE1, gmx_add_pr(rinv_ex_SSE1, gmx_mul_pr(ewcorr_SSE1, brsq_SSE1)));
-     frcoul_SSE2   = gmx_mul_pr(qq_SSE2, gmx_add_pr(rinv_ex_SSE2, gmx_mul_pr(ewcorr_SSE2, brsq_SSE2)));
-     frcoul_SSE3   = gmx_mul_pr(qq_SSE3, gmx_add_pr(rinv_ex_SSE3, gmx_mul_pr(ewcorr_SSE3, brsq_SSE3)));
++#ifndef NBNXN_CUTOFF_USE_BLENDV
++    brsq_S0     = gmx_mul_pr(beta2_S, gmx_blendzero_pr(rsq_S0, wco_S0));
++    brsq_S1     = gmx_mul_pr(beta2_S, gmx_blendzero_pr(rsq_S1, wco_S1));
++    brsq_S2     = gmx_mul_pr(beta2_S, gmx_blendzero_pr(rsq_S2, wco_S2));
++    brsq_S3     = gmx_mul_pr(beta2_S, gmx_blendzero_pr(rsq_S3, wco_S3));
 +#else
 +    /* Strangely, putting mul on a separate line is slower (icc 13) */
-     vc_sub_SSE0   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_SSE0), beta_SSE);
-     vc_sub_SSE1   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_SSE1), beta_SSE);
-     vc_sub_SSE2   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_SSE2), beta_SSE);
-     vc_sub_SSE3   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_SSE3), beta_SSE);
++    brsq_S0     = gmx_mul_pr(beta2_S, gmx_blendv_pr(rsq_S0, zero_S, gmx_sub_pr(rc2_S, rsq_S0)));
++    brsq_S1     = gmx_mul_pr(beta2_S, gmx_blendv_pr(rsq_S1, zero_S, gmx_sub_pr(rc2_S, rsq_S1)));
++    brsq_S2     = gmx_mul_pr(beta2_S, gmx_blendv_pr(rsq_S2, zero_S, gmx_sub_pr(rc2_S, rsq_S2)));
++    brsq_S3     = gmx_mul_pr(beta2_S, gmx_blendv_pr(rsq_S3, zero_S, gmx_sub_pr(rc2_S, rsq_S3)));
++#endif
++    ewcorr_S0   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_S0), beta_S);
++    ewcorr_S1   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_S1), beta_S);
++    ewcorr_S2   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_S2), beta_S);
++    ewcorr_S3   = gmx_mul_pr(gmx_pmecorrF_pr(brsq_S3), beta_S);
++    frcoul_S0   = gmx_mul_pr(qq_S0, gmx_add_pr(rinv_ex_S0, gmx_mul_pr(ewcorr_S0, brsq_S0)));
++    frcoul_S1   = gmx_mul_pr(qq_S1, gmx_add_pr(rinv_ex_S1, gmx_mul_pr(ewcorr_S1, brsq_S1)));
++    frcoul_S2   = gmx_mul_pr(qq_S2, gmx_add_pr(rinv_ex_S2, gmx_mul_pr(ewcorr_S2, brsq_S2)));
++    frcoul_S3   = gmx_mul_pr(qq_S3, gmx_add_pr(rinv_ex_S3, gmx_mul_pr(ewcorr_S3, brsq_S3)));
 +
 +#ifdef CALC_ENERGIES
-     r_SSE0        = gmx_mul_pr(rsq_SSE0, rinv_SSE0);
-     r_SSE1        = gmx_mul_pr(rsq_SSE1, rinv_SSE1);
-     r_SSE2        = gmx_mul_pr(rsq_SSE2, rinv_SSE2);
-     r_SSE3        = gmx_mul_pr(rsq_SSE3, rinv_SSE3);
++    vc_sub_S0   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_S0), beta_S);
++    vc_sub_S1   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_S1), beta_S);
++    vc_sub_S2   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_S2), beta_S);
++    vc_sub_S3   = gmx_mul_pr(gmx_pmecorrV_pr(brsq_S3), beta_S);
 +#endif
 +
 +#endif /* CALC_COUL_EWALD */
 +
 +#ifdef CALC_COUL_TAB
 +    /* Electrostatic interactions */
-     rs_SSE0       = gmx_mul_pr(r_SSE0, invtsp_SSE);
-     rs_SSE1       = gmx_mul_pr(r_SSE1, invtsp_SSE);
-     rs_SSE2       = gmx_mul_pr(r_SSE2, invtsp_SSE);
-     rs_SSE3       = gmx_mul_pr(r_SSE3, invtsp_SSE);
++    r_S0        = gmx_mul_pr(rsq_S0, rinv_S0);
++    r_S1        = gmx_mul_pr(rsq_S1, rinv_S1);
++    r_S2        = gmx_mul_pr(rsq_S2, rinv_S2);
++    r_S3        = gmx_mul_pr(rsq_S3, rinv_S3);
 +    /* Convert r to scaled table units */
-     ti_SSE0       = gmx_cvttpr_epi32(rs_SSE0);
-     ti_SSE1       = gmx_cvttpr_epi32(rs_SSE1);
-     ti_SSE2       = gmx_cvttpr_epi32(rs_SSE2);
-     ti_SSE3       = gmx_cvttpr_epi32(rs_SSE3);
- #ifdef GMX_X86_SSE4_1
++    rs_S0       = gmx_mul_pr(r_S0, invtsp_S);
++    rs_S1       = gmx_mul_pr(r_S1, invtsp_S);
++    rs_S2       = gmx_mul_pr(r_S2, invtsp_S);
++    rs_S3       = gmx_mul_pr(r_S3, invtsp_S);
 +    /* Truncate scaled r to an int */
-     rf_SSE0       = gmx_floor_pr(rs_SSE0);
-     rf_SSE1       = gmx_floor_pr(rs_SSE1);
-     rf_SSE2       = gmx_floor_pr(rs_SSE2);
-     rf_SSE3       = gmx_floor_pr(rs_SSE3);
++    ti_S0       = gmx_cvttpr_epi32(rs_S0);
++    ti_S1       = gmx_cvttpr_epi32(rs_S1);
++    ti_S2       = gmx_cvttpr_epi32(rs_S2);
++    ti_S3       = gmx_cvttpr_epi32(rs_S3);
++#ifdef GMX_HAVE_SIMD_FLOOR
 +    /* SSE4.1 floor is faster than gmx_cvtepi32_ps int->float cast */
-     rf_SSE0       = gmx_cvtepi32_pr(ti_SSE0);
-     rf_SSE1       = gmx_cvtepi32_pr(ti_SSE1);
-     rf_SSE2       = gmx_cvtepi32_pr(ti_SSE2);
-     rf_SSE3       = gmx_cvtepi32_pr(ti_SSE3);
++    rf_S0       = gmx_floor_pr(rs_S0);
++    rf_S1       = gmx_floor_pr(rs_S1);
++    rf_S2       = gmx_floor_pr(rs_S2);
++    rf_S3       = gmx_floor_pr(rs_S3);
 +#else
-     frac_SSE0     = gmx_sub_pr(rs_SSE0, rf_SSE0);
-     frac_SSE1     = gmx_sub_pr(rs_SSE1, rf_SSE1);
-     frac_SSE2     = gmx_sub_pr(rs_SSE2, rf_SSE2);
-     frac_SSE3     = gmx_sub_pr(rs_SSE3, rf_SSE3);
++    rf_S0       = gmx_cvtepi32_pr(ti_S0);
++    rf_S1       = gmx_cvtepi32_pr(ti_S1);
++    rf_S2       = gmx_cvtepi32_pr(ti_S2);
++    rf_S3       = gmx_cvtepi32_pr(ti_S3);
 +#endif
-     load_table_f(tab_coul_F, ti_SSE0, ti0, ctab0_SSE0, ctab1_SSE0);
-     load_table_f(tab_coul_F, ti_SSE1, ti1, ctab0_SSE1, ctab1_SSE1);
-     load_table_f(tab_coul_F, ti_SSE2, ti2, ctab0_SSE2, ctab1_SSE2);
-     load_table_f(tab_coul_F, ti_SSE3, ti3, ctab0_SSE3, ctab1_SSE3);
++    frac_S0     = gmx_sub_pr(rs_S0, rf_S0);
++    frac_S1     = gmx_sub_pr(rs_S1, rf_S1);
++    frac_S2     = gmx_sub_pr(rs_S2, rf_S2);
++    frac_S3     = gmx_sub_pr(rs_S3, rf_S3);
 +
 +    /* Load and interpolate table forces and possibly energies.
 +     * Force and energy can be combined in one table, stride 4: FDV0
 +     * or in two separate tables with stride 1: F and V
 +     * Currently single precision uses FDV0, double F and V.
 +     */
 +#ifndef CALC_ENERGIES
-     load_table_f_v(tab_coul_F, ti_SSE0, ti0, ctab0_SSE0, ctab1_SSE0, ctabv_SSE0);
-     load_table_f_v(tab_coul_F, ti_SSE1, ti1, ctab0_SSE1, ctab1_SSE1, ctabv_SSE1);
-     load_table_f_v(tab_coul_F, ti_SSE2, ti2, ctab0_SSE2, ctab1_SSE2, ctabv_SSE2);
-     load_table_f_v(tab_coul_F, ti_SSE3, ti3, ctab0_SSE3, ctab1_SSE3, ctabv_SSE3);
++    load_table_f(tab_coul_F, ti_S0, ti0, ctab0_S0, ctab1_S0);
++    load_table_f(tab_coul_F, ti_S1, ti1, ctab0_S1, ctab1_S1);
++    load_table_f(tab_coul_F, ti_S2, ti2, ctab0_S2, ctab1_S2);
++    load_table_f(tab_coul_F, ti_S3, ti3, ctab0_S3, ctab1_S3);
 +#else
 +#ifdef TAB_FDV0
-     load_table_f_v(tab_coul_F, tab_coul_V, ti_SSE0, ti0, ctab0_SSE0, ctab1_SSE0, ctabv_SSE0);
-     load_table_f_v(tab_coul_F, tab_coul_V, ti_SSE1, ti1, ctab0_SSE1, ctab1_SSE1, ctabv_SSE1);
-     load_table_f_v(tab_coul_F, tab_coul_V, ti_SSE2, ti2, ctab0_SSE2, ctab1_SSE2, ctabv_SSE2);
-     load_table_f_v(tab_coul_F, tab_coul_V, ti_SSE3, ti3, ctab0_SSE3, ctab1_SSE3, ctabv_SSE3);
++    load_table_f_v(tab_coul_F, ti_S0, ti0, ctab0_S0, ctab1_S0, ctabv_S0);
++    load_table_f_v(tab_coul_F, ti_S1, ti1, ctab0_S1, ctab1_S1, ctabv_S1);
++    load_table_f_v(tab_coul_F, ti_S2, ti2, ctab0_S2, ctab1_S2, ctabv_S2);
++    load_table_f_v(tab_coul_F, ti_S3, ti3, ctab0_S3, ctab1_S3, ctabv_S3);
 +#else
-     fsub_SSE0     = gmx_add_pr(ctab0_SSE0, gmx_mul_pr(frac_SSE0, ctab1_SSE0));
-     fsub_SSE1     = gmx_add_pr(ctab0_SSE1, gmx_mul_pr(frac_SSE1, ctab1_SSE1));
-     fsub_SSE2     = gmx_add_pr(ctab0_SSE2, gmx_mul_pr(frac_SSE2, ctab1_SSE2));
-     fsub_SSE3     = gmx_add_pr(ctab0_SSE3, gmx_mul_pr(frac_SSE3, ctab1_SSE3));
-     frcoul_SSE0   = gmx_mul_pr(qq_SSE0, gmx_sub_pr(rinv_ex_SSE0, gmx_mul_pr(fsub_SSE0, r_SSE0)));
-     frcoul_SSE1   = gmx_mul_pr(qq_SSE1, gmx_sub_pr(rinv_ex_SSE1, gmx_mul_pr(fsub_SSE1, r_SSE1)));
-     frcoul_SSE2   = gmx_mul_pr(qq_SSE2, gmx_sub_pr(rinv_ex_SSE2, gmx_mul_pr(fsub_SSE2, r_SSE2)));
-     frcoul_SSE3   = gmx_mul_pr(qq_SSE3, gmx_sub_pr(rinv_ex_SSE3, gmx_mul_pr(fsub_SSE3, r_SSE3)));
++    load_table_f_v(tab_coul_F, tab_coul_V, ti_S0, ti0, ctab0_S0, ctab1_S0, ctabv_S0);
++    load_table_f_v(tab_coul_F, tab_coul_V, ti_S1, ti1, ctab0_S1, ctab1_S1, ctabv_S1);
++    load_table_f_v(tab_coul_F, tab_coul_V, ti_S2, ti2, ctab0_S2, ctab1_S2, ctabv_S2);
++    load_table_f_v(tab_coul_F, tab_coul_V, ti_S3, ti3, ctab0_S3, ctab1_S3, ctabv_S3);
 +#endif
 +#endif
-     vc_sub_SSE0   = gmx_add_pr(ctabv_SSE0, gmx_mul_pr(gmx_mul_pr(mhalfsp_SSE, frac_SSE0), gmx_add_pr(ctab0_SSE0, fsub_SSE0)));
-     vc_sub_SSE1   = gmx_add_pr(ctabv_SSE1, gmx_mul_pr(gmx_mul_pr(mhalfsp_SSE, frac_SSE1), gmx_add_pr(ctab0_SSE1, fsub_SSE1)));
-     vc_sub_SSE2   = gmx_add_pr(ctabv_SSE2, gmx_mul_pr(gmx_mul_pr(mhalfsp_SSE, frac_SSE2), gmx_add_pr(ctab0_SSE2, fsub_SSE2)));
-     vc_sub_SSE3   = gmx_add_pr(ctabv_SSE3, gmx_mul_pr(gmx_mul_pr(mhalfsp_SSE, frac_SSE3), gmx_add_pr(ctab0_SSE3, fsub_SSE3)));
++    fsub_S0     = gmx_add_pr(ctab0_S0, gmx_mul_pr(frac_S0, ctab1_S0));
++    fsub_S1     = gmx_add_pr(ctab0_S1, gmx_mul_pr(frac_S1, ctab1_S1));
++    fsub_S2     = gmx_add_pr(ctab0_S2, gmx_mul_pr(frac_S2, ctab1_S2));
++    fsub_S3     = gmx_add_pr(ctab0_S3, gmx_mul_pr(frac_S3, ctab1_S3));
++    frcoul_S0   = gmx_mul_pr(qq_S0, gmx_sub_pr(rinv_ex_S0, gmx_mul_pr(fsub_S0, r_S0)));
++    frcoul_S1   = gmx_mul_pr(qq_S1, gmx_sub_pr(rinv_ex_S1, gmx_mul_pr(fsub_S1, r_S1)));
++    frcoul_S2   = gmx_mul_pr(qq_S2, gmx_sub_pr(rinv_ex_S2, gmx_mul_pr(fsub_S2, r_S2)));
++    frcoul_S3   = gmx_mul_pr(qq_S3, gmx_sub_pr(rinv_ex_S3, gmx_mul_pr(fsub_S3, r_S3)));
 +
 +#ifdef CALC_ENERGIES
-     vc_sub_SSE0   = gmx_add_pr(vc_sub_SSE0, gmx_and_pr(sh_ewald_SSE, int_SSE0));
-     vc_sub_SSE1   = gmx_add_pr(vc_sub_SSE1, gmx_and_pr(sh_ewald_SSE, int_SSE1));
-     vc_sub_SSE2   = gmx_add_pr(vc_sub_SSE2, gmx_and_pr(sh_ewald_SSE, int_SSE2));
-     vc_sub_SSE3   = gmx_add_pr(vc_sub_SSE3, gmx_and_pr(sh_ewald_SSE, int_SSE3));
++    vc_sub_S0   = gmx_add_pr(ctabv_S0, gmx_mul_pr(gmx_mul_pr(mhalfsp_S, frac_S0), gmx_add_pr(ctab0_S0, fsub_S0)));
++    vc_sub_S1   = gmx_add_pr(ctabv_S1, gmx_mul_pr(gmx_mul_pr(mhalfsp_S, frac_S1), gmx_add_pr(ctab0_S1, fsub_S1)));
++    vc_sub_S2   = gmx_add_pr(ctabv_S2, gmx_mul_pr(gmx_mul_pr(mhalfsp_S, frac_S2), gmx_add_pr(ctab0_S2, fsub_S2)));
++    vc_sub_S3   = gmx_add_pr(ctabv_S3, gmx_mul_pr(gmx_mul_pr(mhalfsp_S, frac_S3), gmx_add_pr(ctab0_S3, fsub_S3)));
 +#endif
 +#endif /* CALC_COUL_TAB */
 +
 +#if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
 +#ifndef NO_SHIFT_EWALD
 +    /* Add Ewald potential shift to vc_sub for convenience */
 +#ifdef CHECK_EXCLS
-     vc_sub_SSE0   = gmx_add_pr(vc_sub_SSE0, sh_ewald_SSE);
-     vc_sub_SSE1   = gmx_add_pr(vc_sub_SSE1, sh_ewald_SSE);
-     vc_sub_SSE2   = gmx_add_pr(vc_sub_SSE2, sh_ewald_SSE);
-     vc_sub_SSE3   = gmx_add_pr(vc_sub_SSE3, sh_ewald_SSE);
++    vc_sub_S0   = gmx_add_pr(vc_sub_S0, gmx_blendzero_pr(sh_ewald_S, int_S0));
++    vc_sub_S1   = gmx_add_pr(vc_sub_S1, gmx_blendzero_pr(sh_ewald_S, int_S1));
++    vc_sub_S2   = gmx_add_pr(vc_sub_S2, gmx_blendzero_pr(sh_ewald_S, int_S2));
++    vc_sub_S3   = gmx_add_pr(vc_sub_S3, gmx_blendzero_pr(sh_ewald_S, int_S3));
 +#else
-     vcoul_SSE0    = gmx_mul_pr(qq_SSE0, gmx_sub_pr(rinv_ex_SSE0, vc_sub_SSE0));
-     vcoul_SSE1    = gmx_mul_pr(qq_SSE1, gmx_sub_pr(rinv_ex_SSE1, vc_sub_SSE1));
-     vcoul_SSE2    = gmx_mul_pr(qq_SSE2, gmx_sub_pr(rinv_ex_SSE2, vc_sub_SSE2));
-     vcoul_SSE3    = gmx_mul_pr(qq_SSE3, gmx_sub_pr(rinv_ex_SSE3, vc_sub_SSE3));
++    vc_sub_S0   = gmx_add_pr(vc_sub_S0, sh_ewald_S);
++    vc_sub_S1   = gmx_add_pr(vc_sub_S1, sh_ewald_S);
++    vc_sub_S2   = gmx_add_pr(vc_sub_S2, sh_ewald_S);
++    vc_sub_S3   = gmx_add_pr(vc_sub_S3, sh_ewald_S);
 +#endif
 +#endif
 +
-     vcoul_SSE0    = gmx_and_pr(vcoul_SSE0, wco_SSE0);
-     vcoul_SSE1    = gmx_and_pr(vcoul_SSE1, wco_SSE1);
-     vcoul_SSE2    = gmx_and_pr(vcoul_SSE2, wco_SSE2);
-     vcoul_SSE3    = gmx_and_pr(vcoul_SSE3, wco_SSE3);
++    vcoul_S0    = gmx_mul_pr(qq_S0, gmx_sub_pr(rinv_ex_S0, vc_sub_S0));
++    vcoul_S1    = gmx_mul_pr(qq_S1, gmx_sub_pr(rinv_ex_S1, vc_sub_S1));
++    vcoul_S2    = gmx_mul_pr(qq_S2, gmx_sub_pr(rinv_ex_S2, vc_sub_S2));
++    vcoul_S3    = gmx_mul_pr(qq_S3, gmx_sub_pr(rinv_ex_S3, vc_sub_S3));
 +
 +#endif
 +
 +#ifdef CALC_ENERGIES
 +    /* Mask energy for cut-off and diagonal */
-     wco_vdw_SSE0  = gmx_cmplt_pr(rsq_SSE0, rcvdw2_SSE);
-     wco_vdw_SSE1  = gmx_cmplt_pr(rsq_SSE1, rcvdw2_SSE);
++    vcoul_S0    = gmx_blendzero_pr(vcoul_S0, wco_S0);
++    vcoul_S1    = gmx_blendzero_pr(vcoul_S1, wco_S1);
++    vcoul_S2    = gmx_blendzero_pr(vcoul_S2, wco_S2);
++    vcoul_S3    = gmx_blendzero_pr(vcoul_S3, wco_S3);
 +#endif
 +
 +#endif /* CALC_COULOMB */
 +
 +#ifdef CALC_LJ
 +    /* Lennard-Jones interaction */
 +
 +#ifdef VDW_CUTOFF_CHECK
-     wco_vdw_SSE2  = gmx_cmplt_pr(rsq_SSE2, rcvdw2_SSE);
-     wco_vdw_SSE3  = gmx_cmplt_pr(rsq_SSE3, rcvdw2_SSE);
++    wco_vdw_S0  = gmx_cmplt_pr(rsq_S0, rcvdw2_S);
++    wco_vdw_S1  = gmx_cmplt_pr(rsq_S1, rcvdw2_S);
 +#ifndef HALF_LJ
- #define     wco_vdw_SSE0    wco_SSE0
- #define     wco_vdw_SSE1    wco_SSE1
- #define     wco_vdw_SSE2    wco_SSE2
- #define     wco_vdw_SSE3    wco_SSE3
++    wco_vdw_S2  = gmx_cmplt_pr(rsq_S2, rcvdw2_S);
++    wco_vdw_S3  = gmx_cmplt_pr(rsq_S3, rcvdw2_S);
 +#endif
 +#else
 +    /* Same cut-off for Coulomb and VdW, reuse the registers */
-     rinvsix_SSE0  = gmx_mul_pr(rinvsq_SSE0, gmx_mul_pr(rinvsq_SSE0, rinvsq_SSE0));
-     rinvsix_SSE1  = gmx_mul_pr(rinvsq_SSE1, gmx_mul_pr(rinvsq_SSE1, rinvsq_SSE1));
++#define     wco_vdw_S0    wco_S0
++#define     wco_vdw_S1    wco_S1
++#define     wco_vdw_S2    wco_S2
++#define     wco_vdw_S3    wco_S3
 +#endif
 +
 +#ifndef LJ_COMB_LB
-     rinvsix_SSE0  = gmx_and_pr(rinvsix_SSE0, int_SSE0);
-     rinvsix_SSE1  = gmx_and_pr(rinvsix_SSE1, int_SSE1);
++    rinvsix_S0  = gmx_mul_pr(rinvsq_S0, gmx_mul_pr(rinvsq_S0, rinvsq_S0));
++    rinvsix_S1  = gmx_mul_pr(rinvsq_S1, gmx_mul_pr(rinvsq_S1, rinvsq_S1));
 +#ifdef EXCL_FORCES
-     rinvsix_SSE2  = gmx_mul_pr(rinvsq_SSE2, gmx_mul_pr(rinvsq_SSE2, rinvsq_SSE2));
-     rinvsix_SSE3  = gmx_mul_pr(rinvsq_SSE3, gmx_mul_pr(rinvsq_SSE3, rinvsq_SSE3));
++    rinvsix_S0  = gmx_blendzero_pr(rinvsix_S0, int_S0);
++    rinvsix_S1  = gmx_blendzero_pr(rinvsix_S1, int_S1);
 +#endif
 +#ifndef HALF_LJ
-     rinvsix_SSE2  = gmx_and_pr(rinvsix_SSE2, int_SSE2);
-     rinvsix_SSE3  = gmx_and_pr(rinvsix_SSE3, int_SSE3);
++    rinvsix_S2  = gmx_mul_pr(rinvsq_S2, gmx_mul_pr(rinvsq_S2, rinvsq_S2));
++    rinvsix_S3  = gmx_mul_pr(rinvsq_S3, gmx_mul_pr(rinvsq_S3, rinvsq_S3));
 +#ifdef EXCL_FORCES
-     rinvsix_SSE0  = gmx_and_pr(rinvsix_SSE0, wco_vdw_SSE0);
-     rinvsix_SSE1  = gmx_and_pr(rinvsix_SSE1, wco_vdw_SSE1);
++    rinvsix_S2  = gmx_blendzero_pr(rinvsix_S2, int_S2);
++    rinvsix_S3  = gmx_blendzero_pr(rinvsix_S3, int_S3);
 +#endif
 +#endif
 +#ifdef VDW_CUTOFF_CHECK
-     rinvsix_SSE2  = gmx_and_pr(rinvsix_SSE2, wco_vdw_SSE2);
-     rinvsix_SSE3  = gmx_and_pr(rinvsix_SSE3, wco_vdw_SSE3);
++    rinvsix_S0  = gmx_blendzero_pr(rinvsix_S0, wco_vdw_S0);
++    rinvsix_S1  = gmx_blendzero_pr(rinvsix_S1, wco_vdw_S1);
 +#ifndef HALF_LJ
-     FrLJ6_SSE0    = gmx_mul_pr(c6_SSE0, rinvsix_SSE0);
-     FrLJ6_SSE1    = gmx_mul_pr(c6_SSE1, rinvsix_SSE1);
++    rinvsix_S2  = gmx_blendzero_pr(rinvsix_S2, wco_vdw_S2);
++    rinvsix_S3  = gmx_blendzero_pr(rinvsix_S3, wco_vdw_S3);
 +#endif
 +#endif
-     FrLJ6_SSE2    = gmx_mul_pr(c6_SSE2, rinvsix_SSE2);
-     FrLJ6_SSE3    = gmx_mul_pr(c6_SSE3, rinvsix_SSE3);
++    FrLJ6_S0    = gmx_mul_pr(c6_S0, rinvsix_S0);
++    FrLJ6_S1    = gmx_mul_pr(c6_S1, rinvsix_S1);
 +#ifndef HALF_LJ
-     FrLJ12_SSE0   = gmx_mul_pr(c12_SSE0, gmx_mul_pr(rinvsix_SSE0, rinvsix_SSE0));
-     FrLJ12_SSE1   = gmx_mul_pr(c12_SSE1, gmx_mul_pr(rinvsix_SSE1, rinvsix_SSE1));
++    FrLJ6_S2    = gmx_mul_pr(c6_S2, rinvsix_S2);
++    FrLJ6_S3    = gmx_mul_pr(c6_S3, rinvsix_S3);
 +#endif
-     FrLJ12_SSE2   = gmx_mul_pr(c12_SSE2, gmx_mul_pr(rinvsix_SSE2, rinvsix_SSE2));
-     FrLJ12_SSE3   = gmx_mul_pr(c12_SSE3, gmx_mul_pr(rinvsix_SSE3, rinvsix_SSE3));
++    FrLJ12_S0   = gmx_mul_pr(c12_S0, gmx_mul_pr(rinvsix_S0, rinvsix_S0));
++    FrLJ12_S1   = gmx_mul_pr(c12_S1, gmx_mul_pr(rinvsix_S1, rinvsix_S1));
 +#ifndef HALF_LJ
-     sir_SSE0      = gmx_mul_pr(sig_SSE0, rinv_SSE0);
-     sir_SSE1      = gmx_mul_pr(sig_SSE1, rinv_SSE1);
++    FrLJ12_S2   = gmx_mul_pr(c12_S2, gmx_mul_pr(rinvsix_S2, rinvsix_S2));
++    FrLJ12_S3   = gmx_mul_pr(c12_S3, gmx_mul_pr(rinvsix_S3, rinvsix_S3));
 +#endif
 +#endif /* not LJ_COMB_LB */
 +
 +#ifdef LJ_COMB_LB
-     sir_SSE2      = gmx_mul_pr(sig_SSE2, rinv_SSE2);
-     sir_SSE3      = gmx_mul_pr(sig_SSE3, rinv_SSE3);
++    sir_S0      = gmx_mul_pr(sig_S0, rinv_S0);
++    sir_S1      = gmx_mul_pr(sig_S1, rinv_S1);
 +#ifndef HALF_LJ
-     sir2_SSE0     = gmx_mul_pr(sir_SSE0, sir_SSE0);
-     sir2_SSE1     = gmx_mul_pr(sir_SSE1, sir_SSE1);
++    sir_S2      = gmx_mul_pr(sig_S2, rinv_S2);
++    sir_S3      = gmx_mul_pr(sig_S3, rinv_S3);
 +#endif
-     sir2_SSE2     = gmx_mul_pr(sir_SSE2, sir_SSE2);
-     sir2_SSE3     = gmx_mul_pr(sir_SSE3, sir_SSE3);
++    sir2_S0     = gmx_mul_pr(sir_S0, sir_S0);
++    sir2_S1     = gmx_mul_pr(sir_S1, sir_S1);
 +#ifndef HALF_LJ
-     sir6_SSE0     = gmx_mul_pr(sir2_SSE0, gmx_mul_pr(sir2_SSE0, sir2_SSE0));
-     sir6_SSE1     = gmx_mul_pr(sir2_SSE1, gmx_mul_pr(sir2_SSE1, sir2_SSE1));
++    sir2_S2     = gmx_mul_pr(sir_S2, sir_S2);
++    sir2_S3     = gmx_mul_pr(sir_S3, sir_S3);
 +#endif
-     sir6_SSE0     = gmx_and_pr(sir6_SSE0, int_SSE0);
-     sir6_SSE1     = gmx_and_pr(sir6_SSE1, int_SSE1);
++    sir6_S0     = gmx_mul_pr(sir2_S0, gmx_mul_pr(sir2_S0, sir2_S0));
++    sir6_S1     = gmx_mul_pr(sir2_S1, gmx_mul_pr(sir2_S1, sir2_S1));
 +#ifdef EXCL_FORCES
-     sir6_SSE2     = gmx_mul_pr(sir2_SSE2, gmx_mul_pr(sir2_SSE2, sir2_SSE2));
-     sir6_SSE3     = gmx_mul_pr(sir2_SSE3, gmx_mul_pr(sir2_SSE3, sir2_SSE3));
++    sir6_S0     = gmx_blendzero_pr(sir6_S0, int_S0);
++    sir6_S1     = gmx_blendzero_pr(sir6_S1, int_S1);
 +#endif
 +#ifndef HALF_LJ
-     sir6_SSE2     = gmx_and_pr(sir6_SSE2, int_SSE2);
-     sir6_SSE3     = gmx_and_pr(sir6_SSE3, int_SSE3);
++    sir6_S2     = gmx_mul_pr(sir2_S2, gmx_mul_pr(sir2_S2, sir2_S2));
++    sir6_S3     = gmx_mul_pr(sir2_S3, gmx_mul_pr(sir2_S3, sir2_S3));
 +#ifdef EXCL_FORCES
-     sir6_SSE0     = gmx_and_pr(sir6_SSE0, wco_vdw_SSE0);
-     sir6_SSE1     = gmx_and_pr(sir6_SSE1, wco_vdw_SSE1);
++    sir6_S2     = gmx_blendzero_pr(sir6_S2, int_S2);
++    sir6_S3     = gmx_blendzero_pr(sir6_S3, int_S3);
 +#endif
 +#endif
 +#ifdef VDW_CUTOFF_CHECK
-     sir6_SSE2     = gmx_and_pr(sir6_SSE2, wco_vdw_SSE2);
-     sir6_SSE3     = gmx_and_pr(sir6_SSE3, wco_vdw_SSE3);
++    sir6_S0     = gmx_blendzero_pr(sir6_S0, wco_vdw_S0);
++    sir6_S1     = gmx_blendzero_pr(sir6_S1, wco_vdw_S1);
 +#ifndef HALF_LJ
-     FrLJ6_SSE0    = gmx_mul_pr(eps_SSE0, sir6_SSE0);
-     FrLJ6_SSE1    = gmx_mul_pr(eps_SSE1, sir6_SSE1);
++    sir6_S2     = gmx_blendzero_pr(sir6_S2, wco_vdw_S2);
++    sir6_S3     = gmx_blendzero_pr(sir6_S3, wco_vdw_S3);
 +#endif
 +#endif
-     FrLJ6_SSE2    = gmx_mul_pr(eps_SSE2, sir6_SSE2);
-     FrLJ6_SSE3    = gmx_mul_pr(eps_SSE3, sir6_SSE3);
++    FrLJ6_S0    = gmx_mul_pr(eps_S0, sir6_S0);
++    FrLJ6_S1    = gmx_mul_pr(eps_S1, sir6_S1);
 +#ifndef HALF_LJ
-     FrLJ12_SSE0   = gmx_mul_pr(FrLJ6_SSE0, sir6_SSE0);
-     FrLJ12_SSE1   = gmx_mul_pr(FrLJ6_SSE1, sir6_SSE1);
++    FrLJ6_S2    = gmx_mul_pr(eps_S2, sir6_S2);
++    FrLJ6_S3    = gmx_mul_pr(eps_S3, sir6_S3);
 +#endif
-     FrLJ12_SSE2   = gmx_mul_pr(FrLJ6_SSE2, sir6_SSE2);
-     FrLJ12_SSE3   = gmx_mul_pr(FrLJ6_SSE3, sir6_SSE3);
++    FrLJ12_S0   = gmx_mul_pr(FrLJ6_S0, sir6_S0);
++    FrLJ12_S1   = gmx_mul_pr(FrLJ6_S1, sir6_S1);
 +#ifndef HALF_LJ
-     sig2_SSE0     = gmx_mul_pr(sig_SSE0, sig_SSE0);
-     sig2_SSE1     = gmx_mul_pr(sig_SSE1, sig_SSE1);
++    FrLJ12_S2   = gmx_mul_pr(FrLJ6_S2, sir6_S2);
++    FrLJ12_S3   = gmx_mul_pr(FrLJ6_S3, sir6_S3);
 +#endif
 +#if defined CALC_ENERGIES
 +    /* We need C6 and C12 to calculate the LJ potential shift */
-     sig2_SSE2     = gmx_mul_pr(sig_SSE2, sig_SSE2);
-     sig2_SSE3     = gmx_mul_pr(sig_SSE3, sig_SSE3);
++    sig2_S0     = gmx_mul_pr(sig_S0, sig_S0);
++    sig2_S1     = gmx_mul_pr(sig_S1, sig_S1);
 +#ifndef HALF_LJ
-     sig6_SSE0     = gmx_mul_pr(sig2_SSE0, gmx_mul_pr(sig2_SSE0, sig2_SSE0));
-     sig6_SSE1     = gmx_mul_pr(sig2_SSE1, gmx_mul_pr(sig2_SSE1, sig2_SSE1));
++    sig2_S2     = gmx_mul_pr(sig_S2, sig_S2);
++    sig2_S3     = gmx_mul_pr(sig_S3, sig_S3);
 +#endif
-     sig6_SSE2     = gmx_mul_pr(sig2_SSE2, gmx_mul_pr(sig2_SSE2, sig2_SSE2));
-     sig6_SSE3     = gmx_mul_pr(sig2_SSE3, gmx_mul_pr(sig2_SSE3, sig2_SSE3));
++    sig6_S0     = gmx_mul_pr(sig2_S0, gmx_mul_pr(sig2_S0, sig2_S0));
++    sig6_S1     = gmx_mul_pr(sig2_S1, gmx_mul_pr(sig2_S1, sig2_S1));
 +#ifndef HALF_LJ
-     c6_SSE0       = gmx_mul_pr(eps_SSE0, sig6_SSE0);
-     c6_SSE1       = gmx_mul_pr(eps_SSE1, sig6_SSE1);
++    sig6_S2     = gmx_mul_pr(sig2_S2, gmx_mul_pr(sig2_S2, sig2_S2));
++    sig6_S3     = gmx_mul_pr(sig2_S3, gmx_mul_pr(sig2_S3, sig2_S3));
 +#endif
-     c6_SSE2       = gmx_mul_pr(eps_SSE2, sig6_SSE2);
-     c6_SSE3       = gmx_mul_pr(eps_SSE3, sig6_SSE3);
++    c6_S0       = gmx_mul_pr(eps_S0, sig6_S0);
++    c6_S1       = gmx_mul_pr(eps_S1, sig6_S1);
 +#ifndef HALF_LJ
-     c12_SSE0      = gmx_mul_pr(c6_SSE0, sig6_SSE0);
-     c12_SSE1      = gmx_mul_pr(c6_SSE1, sig6_SSE1);
++    c6_S2       = gmx_mul_pr(eps_S2, sig6_S2);
++    c6_S3       = gmx_mul_pr(eps_S3, sig6_S3);
 +#endif
-     c12_SSE2      = gmx_mul_pr(c6_SSE2, sig6_SSE2);
-     c12_SSE3      = gmx_mul_pr(c6_SSE3, sig6_SSE3);
++    c12_S0      = gmx_mul_pr(c6_S0, sig6_S0);
++    c12_S1      = gmx_mul_pr(c6_S1, sig6_S1);
 +#ifndef HALF_LJ
-     vctotSSE      = gmx_add_pr(vctotSSE, gmx_sum4_pr(vcoul_SSE0, vcoul_SSE1, vcoul_SSE2, vcoul_SSE3));
++    c12_S2      = gmx_mul_pr(c6_S2, sig6_S2);
++    c12_S3      = gmx_mul_pr(c6_S3, sig6_S3);
 +#endif
 +#endif
 +#endif /* LJ_COMB_LB */
 +
 +#endif /* CALC_LJ */
 +
 +#ifdef CALC_ENERGIES
 +#ifdef ENERGY_GROUPS
 +    /* Extract the group pair index per j pair.
 +     * Energy groups are stored per i-cluster, so things get
 +     * complicated when the i- and j-cluster size don't match.
 +     */
 +    {
 +        int egps_j;
 +#if UNROLLJ == 2
 +        egps_j    = nbat->energrp[cj>>1];
 +        egp_jj[0] = ((egps_j >> ((cj & 1)*egps_jshift)) & egps_jmask)*egps_jstride;
 +#else
 +        /* We assume UNROLLI <= UNROLLJ */
 +        int jdi;
 +        for (jdi = 0; jdi < UNROLLJ/UNROLLI; jdi++)
 +        {
 +            int jj;
 +            egps_j = nbat->energrp[cj*(UNROLLJ/UNROLLI)+jdi];
 +            for (jj = 0; jj < (UNROLLI/2); jj++)
 +            {
 +                egp_jj[jdi*(UNROLLI/2)+jj] = ((egps_j >> (jj*egps_jshift)) & egps_jmask)*egps_jstride;
 +            }
 +        }
 +#endif
 +    }
 +#endif
 +
 +#ifdef CALC_COULOMB
 +#ifndef ENERGY_GROUPS
-     add_ener_grp(vcoul_SSE0, vctp[0], egp_jj);
-     add_ener_grp(vcoul_SSE1, vctp[1], egp_jj);
-     add_ener_grp(vcoul_SSE2, vctp[2], egp_jj);
-     add_ener_grp(vcoul_SSE3, vctp[3], egp_jj);
++    vctot_S      = gmx_add_pr(vctot_S, gmx_sum4_pr(vcoul_S0, vcoul_S1, vcoul_S2, vcoul_S3));
 +#else
-     VLJ6_SSE0     = gmx_mul_pr(sixthSSE, gmx_sub_pr(FrLJ6_SSE0, gmx_mul_pr(c6_SSE0, sh_invrc6_SSE)));
-     VLJ6_SSE1     = gmx_mul_pr(sixthSSE, gmx_sub_pr(FrLJ6_SSE1, gmx_mul_pr(c6_SSE1, sh_invrc6_SSE)));
++    add_ener_grp(vcoul_S0, vctp[0], egp_jj);
++    add_ener_grp(vcoul_S1, vctp[1], egp_jj);
++    add_ener_grp(vcoul_S2, vctp[2], egp_jj);
++    add_ener_grp(vcoul_S3, vctp[3], egp_jj);
 +#endif
 +#endif
 +
 +#ifdef CALC_LJ
 +    /* Calculate the LJ energies */
-     VLJ6_SSE2     = gmx_mul_pr(sixthSSE, gmx_sub_pr(FrLJ6_SSE2, gmx_mul_pr(c6_SSE2, sh_invrc6_SSE)));
-     VLJ6_SSE3     = gmx_mul_pr(sixthSSE, gmx_sub_pr(FrLJ6_SSE3, gmx_mul_pr(c6_SSE3, sh_invrc6_SSE)));
++    VLJ6_S0     = gmx_mul_pr(sixth_S, gmx_sub_pr(FrLJ6_S0, gmx_mul_pr(c6_S0, sh_invrc6_S)));
++    VLJ6_S1     = gmx_mul_pr(sixth_S, gmx_sub_pr(FrLJ6_S1, gmx_mul_pr(c6_S1, sh_invrc6_S)));
 +#ifndef HALF_LJ
-     VLJ12_SSE0    = gmx_mul_pr(twelvethSSE, gmx_sub_pr(FrLJ12_SSE0, gmx_mul_pr(c12_SSE0, sh_invrc12_SSE)));
-     VLJ12_SSE1    = gmx_mul_pr(twelvethSSE, gmx_sub_pr(FrLJ12_SSE1, gmx_mul_pr(c12_SSE1, sh_invrc12_SSE)));
++    VLJ6_S2     = gmx_mul_pr(sixth_S, gmx_sub_pr(FrLJ6_S2, gmx_mul_pr(c6_S2, sh_invrc6_S)));
++    VLJ6_S3     = gmx_mul_pr(sixth_S, gmx_sub_pr(FrLJ6_S3, gmx_mul_pr(c6_S3, sh_invrc6_S)));
 +#endif
-     VLJ12_SSE2    = gmx_mul_pr(twelvethSSE, gmx_sub_pr(FrLJ12_SSE2, gmx_mul_pr(c12_SSE2, sh_invrc12_SSE)));
-     VLJ12_SSE3    = gmx_mul_pr(twelvethSSE, gmx_sub_pr(FrLJ12_SSE3, gmx_mul_pr(c12_SSE3, sh_invrc12_SSE)));
++    VLJ12_S0    = gmx_mul_pr(twelveth_S, gmx_sub_pr(FrLJ12_S0, gmx_mul_pr(c12_S0, sh_invrc12_S)));
++    VLJ12_S1    = gmx_mul_pr(twelveth_S, gmx_sub_pr(FrLJ12_S1, gmx_mul_pr(c12_S1, sh_invrc12_S)));
 +#ifndef HALF_LJ
-     VLJ_SSE0      = gmx_sub_pr(VLJ12_SSE0, VLJ6_SSE0);
-     VLJ_SSE1      = gmx_sub_pr(VLJ12_SSE1, VLJ6_SSE1);
++    VLJ12_S2    = gmx_mul_pr(twelveth_S, gmx_sub_pr(FrLJ12_S2, gmx_mul_pr(c12_S2, sh_invrc12_S)));
++    VLJ12_S3    = gmx_mul_pr(twelveth_S, gmx_sub_pr(FrLJ12_S3, gmx_mul_pr(c12_S3, sh_invrc12_S)));
 +#endif
 +
-     VLJ_SSE2      = gmx_sub_pr(VLJ12_SSE2, VLJ6_SSE2);
-     VLJ_SSE3      = gmx_sub_pr(VLJ12_SSE3, VLJ6_SSE3);
++    VLJ_S0      = gmx_sub_pr(VLJ12_S0, VLJ6_S0);
++    VLJ_S1      = gmx_sub_pr(VLJ12_S1, VLJ6_S1);
 +#ifndef HALF_LJ
-     VLJ_SSE0      = gmx_and_pr(VLJ_SSE0, wco_vdw_SSE0);
-     VLJ_SSE1      = gmx_and_pr(VLJ_SSE1, wco_vdw_SSE1);
++    VLJ_S2      = gmx_sub_pr(VLJ12_S2, VLJ6_S2);
++    VLJ_S3      = gmx_sub_pr(VLJ12_S3, VLJ6_S3);
 +#endif
 +    /* The potential shift should be removed for pairs beyond cut-off */
-     VLJ_SSE2      = gmx_and_pr(VLJ_SSE2, wco_vdw_SSE2);
-     VLJ_SSE3      = gmx_and_pr(VLJ_SSE3, wco_vdw_SSE3);
++    VLJ_S0      = gmx_blendzero_pr(VLJ_S0, wco_vdw_S0);
++    VLJ_S1      = gmx_blendzero_pr(VLJ_S1, wco_vdw_S1);
 +#ifndef HALF_LJ
-     VLJ_SSE0      = gmx_and_pr(VLJ_SSE0, int_SSE0);
-     VLJ_SSE1      = gmx_and_pr(VLJ_SSE1, int_SSE1);
++    VLJ_S2      = gmx_blendzero_pr(VLJ_S2, wco_vdw_S2);
++    VLJ_S3      = gmx_blendzero_pr(VLJ_S3, wco_vdw_S3);
 +#endif
 +#ifdef CHECK_EXCLS
 +    /* The potential shift should be removed for excluded pairs */
-     VLJ_SSE2      = gmx_and_pr(VLJ_SSE2, int_SSE2);
-     VLJ_SSE3      = gmx_and_pr(VLJ_SSE3, int_SSE3);
++    VLJ_S0      = gmx_blendzero_pr(VLJ_S0, int_S0);
++    VLJ_S1      = gmx_blendzero_pr(VLJ_S1, int_S1);
 +#ifndef HALF_LJ
-     VvdwtotSSE    = gmx_add_pr(VvdwtotSSE,
++    VLJ_S2      = gmx_blendzero_pr(VLJ_S2, int_S2);
++    VLJ_S3      = gmx_blendzero_pr(VLJ_S3, int_S3);
 +#endif
 +#endif
 +#ifndef ENERGY_GROUPS
-                                gmx_sum4_pr(VLJ_SSE0, VLJ_SSE1, VLJ_SSE2, VLJ_SSE3)
++    Vvdwtot_S   = gmx_add_pr(Vvdwtot_S,
 +#ifndef HALF_LJ
-                                gmx_add_pr(VLJ_SSE0, VLJ_SSE1)
++                             gmx_sum4_pr(VLJ_S0, VLJ_S1, VLJ_S2, VLJ_S3)
 +#else
-                                );
++                             gmx_add_pr(VLJ_S0, VLJ_S1)
 +#endif
-     add_ener_grp(VLJ_SSE0, vvdwtp[0], egp_jj);
-     add_ener_grp(VLJ_SSE1, vvdwtp[1], egp_jj);
++                             );
 +#else
-     add_ener_grp(VLJ_SSE2, vvdwtp[2], egp_jj);
-     add_ener_grp(VLJ_SSE3, vvdwtp[3], egp_jj);
++    add_ener_grp(VLJ_S0, vvdwtp[0], egp_jj);
++    add_ener_grp(VLJ_S1, vvdwtp[1], egp_jj);
 +#ifndef HALF_LJ
-     fscal_SSE0    = gmx_mul_pr(rinvsq_SSE0,
++    add_ener_grp(VLJ_S2, vvdwtp[2], egp_jj);
++    add_ener_grp(VLJ_S3, vvdwtp[3], egp_jj);
 +#endif
 +#endif
 +#endif /* CALC_LJ */
 +#endif /* CALC_ENERGIES */
 +
 +#ifdef CALC_LJ
-                                gmx_add_pr(frcoul_SSE0,
++    fscal_S0    = gmx_mul_pr(rinvsq_S0,
 +#ifdef CALC_COULOMB
-                                           gmx_sub_pr(FrLJ12_SSE0, FrLJ6_SSE0)));
-     fscal_SSE1    = gmx_mul_pr(rinvsq_SSE1,
++                               gmx_add_pr(frcoul_S0,
 +#else
 +                               (
 +#endif
-                                gmx_add_pr(frcoul_SSE1,
++                                          gmx_sub_pr(FrLJ12_S0, FrLJ6_S0)));
++    fscal_S1    = gmx_mul_pr(rinvsq_S1,
 +#ifdef CALC_COULOMB
-                                           gmx_sub_pr(FrLJ12_SSE1, FrLJ6_SSE1)));
++                               gmx_add_pr(frcoul_S1,
 +#else
 +                               (
 +#endif
-     fscal_SSE0    = gmx_mul_pr(rinvsq_SSE0, frcoul_SSE0);
-     fscal_SSE1    = gmx_mul_pr(rinvsq_SSE1, frcoul_SSE1);
++                                          gmx_sub_pr(FrLJ12_S1, FrLJ6_S1)));
 +#else
-     fscal_SSE2    = gmx_mul_pr(rinvsq_SSE2,
++    fscal_S0    = gmx_mul_pr(rinvsq_S0, frcoul_S0);
++    fscal_S1    = gmx_mul_pr(rinvsq_S1, frcoul_S1);
 +#endif /* CALC_LJ */
 +#if defined CALC_LJ && !defined HALF_LJ
-                                gmx_add_pr(frcoul_SSE2,
++    fscal_S2    = gmx_mul_pr(rinvsq_S2,
 +#ifdef CALC_COULOMB
-                                           gmx_sub_pr(FrLJ12_SSE2, FrLJ6_SSE2)));
-     fscal_SSE3    = gmx_mul_pr(rinvsq_SSE3,
++                               gmx_add_pr(frcoul_S2,
 +#else
 +                               (
 +#endif
-                                gmx_add_pr(frcoul_SSE3,
++                                          gmx_sub_pr(FrLJ12_S2, FrLJ6_S2)));
++    fscal_S3    = gmx_mul_pr(rinvsq_S3,
 +#ifdef CALC_COULOMB
-                                           gmx_sub_pr(FrLJ12_SSE3, FrLJ6_SSE3)));
++                               gmx_add_pr(frcoul_S3,
 +#else
 +                               (
 +#endif
-     fscal_SSE2    = gmx_mul_pr(rinvsq_SSE2, frcoul_SSE2);
-     fscal_SSE3    = gmx_mul_pr(rinvsq_SSE3, frcoul_SSE3);
++                                          gmx_sub_pr(FrLJ12_S3, FrLJ6_S3)));
 +#else
 +    /* Atom 2 and 3 don't have LJ, so only add Coulomb forces */
-     tx_SSE0       = gmx_mul_pr(fscal_SSE0, dx_SSE0);
-     tx_SSE1       = gmx_mul_pr(fscal_SSE1, dx_SSE1);
-     tx_SSE2       = gmx_mul_pr(fscal_SSE2, dx_SSE2);
-     tx_SSE3       = gmx_mul_pr(fscal_SSE3, dx_SSE3);
-     ty_SSE0       = gmx_mul_pr(fscal_SSE0, dy_SSE0);
-     ty_SSE1       = gmx_mul_pr(fscal_SSE1, dy_SSE1);
-     ty_SSE2       = gmx_mul_pr(fscal_SSE2, dy_SSE2);
-     ty_SSE3       = gmx_mul_pr(fscal_SSE3, dy_SSE3);
-     tz_SSE0       = gmx_mul_pr(fscal_SSE0, dz_SSE0);
-     tz_SSE1       = gmx_mul_pr(fscal_SSE1, dz_SSE1);
-     tz_SSE2       = gmx_mul_pr(fscal_SSE2, dz_SSE2);
-     tz_SSE3       = gmx_mul_pr(fscal_SSE3, dz_SSE3);
++    fscal_S2    = gmx_mul_pr(rinvsq_S2, frcoul_S2);
++    fscal_S3    = gmx_mul_pr(rinvsq_S3, frcoul_S3);
 +#endif
 +
 +    /* Calculate temporary vectorial force */
-     fix_SSE0      = gmx_add_pr(fix_SSE0, tx_SSE0);
-     fix_SSE1      = gmx_add_pr(fix_SSE1, tx_SSE1);
-     fix_SSE2      = gmx_add_pr(fix_SSE2, tx_SSE2);
-     fix_SSE3      = gmx_add_pr(fix_SSE3, tx_SSE3);
-     fiy_SSE0      = gmx_add_pr(fiy_SSE0, ty_SSE0);
-     fiy_SSE1      = gmx_add_pr(fiy_SSE1, ty_SSE1);
-     fiy_SSE2      = gmx_add_pr(fiy_SSE2, ty_SSE2);
-     fiy_SSE3      = gmx_add_pr(fiy_SSE3, ty_SSE3);
-     fiz_SSE0      = gmx_add_pr(fiz_SSE0, tz_SSE0);
-     fiz_SSE1      = gmx_add_pr(fiz_SSE1, tz_SSE1);
-     fiz_SSE2      = gmx_add_pr(fiz_SSE2, tz_SSE2);
-     fiz_SSE3      = gmx_add_pr(fiz_SSE3, tz_SSE3);
++    tx_S0       = gmx_mul_pr(fscal_S0, dx_S0);
++    tx_S1       = gmx_mul_pr(fscal_S1, dx_S1);
++    tx_S2       = gmx_mul_pr(fscal_S2, dx_S2);
++    tx_S3       = gmx_mul_pr(fscal_S3, dx_S3);
++    ty_S0       = gmx_mul_pr(fscal_S0, dy_S0);
++    ty_S1       = gmx_mul_pr(fscal_S1, dy_S1);
++    ty_S2       = gmx_mul_pr(fscal_S2, dy_S2);
++    ty_S3       = gmx_mul_pr(fscal_S3, dy_S3);
++    tz_S0       = gmx_mul_pr(fscal_S0, dz_S0);
++    tz_S1       = gmx_mul_pr(fscal_S1, dz_S1);
++    tz_S2       = gmx_mul_pr(fscal_S2, dz_S2);
++    tz_S3       = gmx_mul_pr(fscal_S3, dz_S3);
 +
 +    /* Increment i atom force */
-                  gmx_sub_pr( gmx_load_pr(f+ajx), gmx_sum4_pr(tx_SSE0, tx_SSE1, tx_SSE2, tx_SSE3) ));
++    fix_S0      = gmx_add_pr(fix_S0, tx_S0);
++    fix_S1      = gmx_add_pr(fix_S1, tx_S1);
++    fix_S2      = gmx_add_pr(fix_S2, tx_S2);
++    fix_S3      = gmx_add_pr(fix_S3, tx_S3);
++    fiy_S0      = gmx_add_pr(fiy_S0, ty_S0);
++    fiy_S1      = gmx_add_pr(fiy_S1, ty_S1);
++    fiy_S2      = gmx_add_pr(fiy_S2, ty_S2);
++    fiy_S3      = gmx_add_pr(fiy_S3, ty_S3);
++    fiz_S0      = gmx_add_pr(fiz_S0, tz_S0);
++    fiz_S1      = gmx_add_pr(fiz_S1, tz_S1);
++    fiz_S2      = gmx_add_pr(fiz_S2, tz_S2);
++    fiz_S3      = gmx_add_pr(fiz_S3, tz_S3);
 +
 +    /* Decrement j atom force */
 +    gmx_store_pr(f+ajx,
-                  gmx_sub_pr( gmx_load_pr(f+ajy), gmx_sum4_pr(ty_SSE0, ty_SSE1, ty_SSE2, ty_SSE3) ));
++                 gmx_sub_pr( gmx_load_pr(f+ajx), gmx_sum4_pr(tx_S0, tx_S1, tx_S2, tx_S3) ));
 +    gmx_store_pr(f+ajy,
-                  gmx_sub_pr( gmx_load_pr(f+ajz), gmx_sum4_pr(tz_SSE0, tz_SSE1, tz_SSE2, tz_SSE3) ));
++                 gmx_sub_pr( gmx_load_pr(f+ajy), gmx_sum4_pr(ty_S0, ty_S1, ty_S2, ty_S3) ));
 +    gmx_store_pr(f+ajz,
- #undef  rinv_ex_SSE0
- #undef  rinv_ex_SSE1
- #undef  rinv_ex_SSE2
- #undef  rinv_ex_SSE3
++                 gmx_sub_pr( gmx_load_pr(f+ajz), gmx_sum4_pr(tz_S0, tz_S1, tz_S2, tz_S3) ));
 +}
 +
- #undef  wco_vdw_SSE0
- #undef  wco_vdw_SSE1
- #undef  wco_vdw_SSE2
- #undef  wco_vdw_SSE3
++#undef  rinv_ex_S0
++#undef  rinv_ex_S1
++#undef  rinv_ex_S2
++#undef  rinv_ex_S3
 +
- #undef  CUTOFF_BLENDV
++#undef  wco_vdw_S0
++#undef  wco_vdw_S1
++#undef  wco_vdw_S2
++#undef  wco_vdw_S3
 +
++#undef  NBNXN_CUTOFF_USE_BLENDV
 +
 +#undef  EXCL_FORCES
index 91cae9439ca8f586e3d539651a8c40876bbdc9ca,0000000000000000000000000000000000000000..e5f4ae0fa8a520f98ee90e7c14eba96eda9e9215
mode 100644,000000..100644
--- /dev/null
@@@ -1,804 -1,0 +1,808 @@@
- /* GMX_MM128_HERE or GMX_MM256_HERE should be set before including this file */
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2009, The GROMACS Development Team
 + * Copyright (c) 2012, by the GROMACS development team, led by
 + * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 + * others, as listed in the AUTHORS file in the top-level source
 + * directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
- #if defined GMX_MM128_HERE || defined GMX_DOUBLE
- #define STRIDE     4
- #endif
- #if defined GMX_MM256_HERE && !defined GMX_DOUBLE
- #define STRIDE     8
++#if !(GMX_NBNXN_SIMD_BITWIDTH == 128 || GMX_NBNXN_SIMD_BITWIDTH == 256)
++#error "unsupported GMX_NBNXN_SIMD_BITWIDTH"
++#endif
++
++#ifdef GMX_NBNXN_HALF_WIDTH_SIMD
++#define GMX_USE_HALF_WIDTH_SIMD_HERE
++#endif
 +#include "gmx_simd_macros.h"
 +
 +#define SUM_SIMD4(x) (x[0]+x[1]+x[2]+x[3])
 +
 +#define UNROLLI    NBNXN_CPU_CLUSTER_I_SIZE
 +#define UNROLLJ    GMX_SIMD_WIDTH_HERE
 +
- #ifdef GMX_MM128_HERE
- #ifndef GMX_DOUBLE
- /* single precision 4x4 kernel */
- #define SUM_SIMD(x) SUM_SIMD4(x)
- #define TAB_FDV0
++/* The stride of all the atom data arrays is max(UNROLLI,UNROLLJ) */
++#if GMX_SIMD_WIDTH_HERE >= UNROLLI
++#define STRIDE     GMX_SIMD_WIDTH_HERE
++#else
++#define STRIDE     UNROLLI
 +#endif
 +
- /* double precision 4x2 kernel */
- #define SUM_SIMD(x) (x[0]+x[1])
++#if GMX_SIMD_WIDTH_HERE == 2
++#define SUM_SIMD(x)  (x[0]+x[1])
++#else
++#if GMX_SIMD_WIDTH_HERE == 4
++#define SUM_SIMD(x)  SUM_SIMD4(x)
 +#else
- #ifdef GMX_MM256_HERE
- #ifndef GMX_DOUBLE
- /* single precision 4x8 kernel */
- #define SUM_SIMD(x) (x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+x[7])
++#if GMX_SIMD_WIDTH_HERE == 8
++#define SUM_SIMD(x)  (x[0]+x[1]+x[2]+x[3]+x[4]+x[5]+x[6]+x[7])
++#else
++#error "unsupported kernel configuration"
++#endif
 +#endif
 +#endif
 +
- /* double precision 4x4 kernel */
- #define SUM_SIMD(x) SUM_SIMD4(x)
++
++/* Decide if we should use the FDV0 table layout */
++#if defined GMX_X86_AVX_256 && !defined GMX_USE_HALF_WIDTH_SIMD_HERE
++/* With full AVX-256 SIMD, half SIMD-width table loads are optimal */
++#if GMX_SIMD_WIDTH_HERE/2 == 4
 +#define TAB_FDV0
++#endif
 +#else
-     gmx_mm_pr  shX_SSE;
-     gmx_mm_pr  shY_SSE;
-     gmx_mm_pr  shZ_SSE;
-     gmx_mm_pr  ix_SSE0, iy_SSE0, iz_SSE0;
-     gmx_mm_pr  ix_SSE1, iy_SSE1, iz_SSE1;
-     gmx_mm_pr  ix_SSE2, iy_SSE2, iz_SSE2;
-     gmx_mm_pr  ix_SSE3, iy_SSE3, iz_SSE3;
-     gmx_mm_pr  fix_SSE0, fiy_SSE0, fiz_SSE0;
-     gmx_mm_pr  fix_SSE1, fiy_SSE1, fiz_SSE1;
-     gmx_mm_pr  fix_SSE2, fiy_SSE2, fiz_SSE2;
-     gmx_mm_pr  fix_SSE3, fiy_SSE3, fiz_SSE3;
++/* We use the FDV0 table layout when we can use aligned table loads */
++#if GMX_SIMD_WIDTH_HERE == 4
++#define TAB_FDV0
 +#endif
 +#endif
 +
++
 +#define SIMD_MASK_ALL   0xffffffff
 +
 +#include "nbnxn_kernel_simd_utils.h"
 +
 +/* All functionality defines are set here, except for:
 + * CALC_ENERGIES, ENERGY_GROUPS which are defined before.
 + * CHECK_EXCLS, which is set just before including the inner loop contents.
 + * The combination rule defines, LJ_COMB_GEOM or LJ_COMB_LB are currently
 + * set before calling the kernel function. We might want to move that
 + * to inside the n-loop and have a different combination rule for different
 + * ci's, as no combination rule gives a 50% performance hit for LJ.
 + */
 +
 +/* We always calculate shift forces, because it's cheap anyhow */
 +#define CALC_SHIFTFORCES
 +
 +/* Assumes all LJ parameters are identical */
 +/* #define FIX_LJ_C */
 +
 +/* The NBK_FUNC_NAME... macros below generate the whole zoo of kernels names
 + * with all combinations off electrostatics (coul), LJ combination rules (ljc)
 + * and energy calculations (ene), depending on the defines set.
 + */
 +
 +#define NBK_FUNC_NAME_C_LJC(base, coul, ljc, ene) base ## _ ## coul ## _comb_ ## ljc ## _ ## ene
 +
 +#if defined LJ_COMB_GEOM
 +#define NBK_FUNC_NAME_C(base, coul, ene) NBK_FUNC_NAME_C_LJC(base, coul, geom, ene)
 +#else
 +#if defined LJ_COMB_LB
 +#define NBK_FUNC_NAME_C(base, coul, ene) NBK_FUNC_NAME_C_LJC(base, coul, lb, ene)
 +#else
 +#define NBK_FUNC_NAME_C(base, coul, ene) NBK_FUNC_NAME_C_LJC(base, coul, none, ene)
 +#endif
 +#endif
 +
 +#ifdef CALC_COUL_RF
 +#define NBK_FUNC_NAME(base, ene) NBK_FUNC_NAME_C(base, rf, ene)
 +#endif
 +#ifdef CALC_COUL_TAB
 +#ifndef VDW_CUTOFF_CHECK
 +#define NBK_FUNC_NAME(base, ene) NBK_FUNC_NAME_C(base, tab, ene)
 +#else
 +#define NBK_FUNC_NAME(base, ene) NBK_FUNC_NAME_C(base, tab_twin, ene)
 +#endif
 +#endif
 +#ifdef CALC_COUL_EWALD
 +#ifndef VDW_CUTOFF_CHECK
 +#define NBK_FUNC_NAME(base, ene) NBK_FUNC_NAME_C(base, ewald, ene)
 +#else
 +#define NBK_FUNC_NAME(base, ene) NBK_FUNC_NAME_C(base, ewald_twin, ene)
 +#endif
 +#endif
 +
 +static void
 +#ifndef CALC_ENERGIES
 +NBK_FUNC_NAME(nbnxn_kernel_simd_4xn, noener)
 +#else
 +#ifndef ENERGY_GROUPS
 +NBK_FUNC_NAME(nbnxn_kernel_simd_4xn, ener)
 +#else
 +NBK_FUNC_NAME(nbnxn_kernel_simd_4xn, energrp)
 +#endif
 +#endif
 +#undef NBK_FUNC_NAME
 +#undef NBK_FUNC_NAME_C
 +#undef NBK_FUNC_NAME_C_LJC
 +(const nbnxn_pairlist_t     *nbl,
 + const nbnxn_atomdata_t     *nbat,
 + const interaction_const_t  *ic,
 + rvec                       *shift_vec,
 + real                       *f
 +#ifdef CALC_SHIFTFORCES
 + ,
 + real                       *fshift
 +#endif
 +#ifdef CALC_ENERGIES
 + ,
 + real                       *Vvdw,
 + real                       *Vc
 +#endif
 +)
 +{
 +    const nbnxn_ci_t   *nbln;
 +    const nbnxn_cj_t   *l_cj;
 +    const int          *type;
 +    const real         *q;
 +    const real         *shiftvec;
 +    const real         *x;
 +    const real         *nbfp0, *nbfp1, *nbfp2 = NULL, *nbfp3 = NULL;
 +    real                facel;
 +    real               *nbfp_ptr;
 +    int                 nbfp_stride;
 +    int                 n, ci, ci_sh;
 +    int                 ish, ish3;
 +    gmx_bool            do_LJ, half_LJ, do_coul;
 +    int                 sci, scix, sciy, sciz, sci2;
 +    int                 cjind0, cjind1, cjind;
 +    int                 ip, jp;
 +
 +#ifdef ENERGY_GROUPS
 +    int         Vstride_i;
 +    int         egps_ishift, egps_imask;
 +    int         egps_jshift, egps_jmask, egps_jstride;
 +    int         egps_i;
 +    real       *vvdwtp[UNROLLI];
 +    real       *vctp[UNROLLI];
 +#endif
 +
-     __m128     fix_SSE, fiy_SSE, fiz_SSE;
++    gmx_mm_pr  shX_S;
++    gmx_mm_pr  shY_S;
++    gmx_mm_pr  shZ_S;
++    gmx_mm_pr  ix_S0, iy_S0, iz_S0;
++    gmx_mm_pr  ix_S1, iy_S1, iz_S1;
++    gmx_mm_pr  ix_S2, iy_S2, iz_S2;
++    gmx_mm_pr  ix_S3, iy_S3, iz_S3;
++    gmx_mm_pr  fix_S0, fiy_S0, fiz_S0;
++    gmx_mm_pr  fix_S1, fiy_S1, fiz_S1;
++    gmx_mm_pr  fix_S2, fiy_S2, fiz_S2;
++    gmx_mm_pr  fix_S3, fiy_S3, fiz_S3;
 +#if UNROLLJ >= 4
 +#ifndef GMX_DOUBLE
-     __m256d    fix_SSE, fiy_SSE, fiz_SSE;
++    __m128     fix_S, fiy_S, fiz_S;
 +#else
-     __m128d    fix0_SSE, fiy0_SSE, fiz0_SSE;
-     __m128d    fix2_SSE, fiy2_SSE, fiz2_SSE;
++    __m256d    fix_S, fiy_S, fiz_S;
 +#endif
 +#else
- #ifdef GMX_MM128_HERE
- #ifndef GMX_DOUBLE
-     __m128i    mask0 = _mm_set_epi32( 0x0008, 0x0004, 0x0002, 0x0001 );
-     __m128i    mask1 = _mm_set_epi32( 0x0080, 0x0040, 0x0020, 0x0010 );
-     __m128i    mask2 = _mm_set_epi32( 0x0800, 0x0400, 0x0200, 0x0100 );
-     __m128i    mask3 = _mm_set_epi32( 0x8000, 0x4000, 0x2000, 0x1000 );
- #else
-     /* For double precision we need to set two 32bit ints for one double */
-     __m128i    mask0 = _mm_set_epi32( 0x0002, 0x0002, 0x0001, 0x0001 );
-     __m128i    mask1 = _mm_set_epi32( 0x0008, 0x0008, 0x0004, 0x0004 );
-     __m128i    mask2 = _mm_set_epi32( 0x0020, 0x0020, 0x0010, 0x0010 );
-     __m128i    mask3 = _mm_set_epi32( 0x0080, 0x0080, 0x0040, 0x0040 );
- #endif
- #endif
- #ifdef GMX_MM256_HERE
-     /* AVX: use floating point masks, as there are no integer instructions */
- #ifndef GMX_DOUBLE
-     gmx_mm_pr  mask0 = _mm256_castsi256_ps(_mm256_set_epi32( 0x0080, 0x0040, 0x0020, 0x0010, 0x0008, 0x0004, 0x0002, 0x0001 ));
-     gmx_mm_pr  mask1 = _mm256_castsi256_ps(_mm256_set_epi32( 0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100 ));
++    __m128d    fix0_S, fiy0_S, fiz0_S;
++    __m128d    fix2_S, fiy2_S, fiz2_S;
 +#endif
 +
-     /* There is no 256-bit int to double conversion, so we use float here */
-     __m256     mask0 = _mm256_castsi256_ps(_mm256_set_epi32( 0x0008, 0x0008, 0x0004, 0x0004, 0x0002, 0x0002, 0x0001, 0x0001 ));
-     __m256     mask1 = _mm256_castsi256_ps(_mm256_set_epi32( 0x0080, 0x0080, 0x0040, 0x0040, 0x0020, 0x0020, 0x0010, 0x0010 ));
-     __m256     mask2 = _mm256_castsi256_ps(_mm256_set_epi32( 0x0800, 0x0800, 0x0400, 0x0400, 0x0200, 0x0200, 0x0100, 0x0100 ));
-     __m256     mask3 = _mm256_castsi256_ps(_mm256_set_epi32( 0x8000, 0x8000, 0x4000, 0x4000, 0x2000, 0x2000, 0x1000, 0x1000 ));
- #endif
++    gmx_mm_pr diag_jmi_S;
++#if UNROLLI == UNROLLJ
++    gmx_mm_pr diag_S0, diag_S1, diag_S2, diag_S3;
 +#else
-     gmx_mm_pr diag_jmi_SSE;
- #if UNROLLI == UNROLLJ
-     gmx_mm_pr diag_SSE0, diag_SSE1, diag_SSE2, diag_SSE3;
++    gmx_mm_pr diag0_S0, diag0_S1, diag0_S2, diag0_S3;
++    gmx_mm_pr diag1_S0, diag1_S1, diag1_S2, diag1_S3;
 +#endif
 +
-     gmx_mm_pr diag0_SSE0, diag0_SSE1, diag0_SSE2, diag0_SSE3;
-     gmx_mm_pr diag1_SSE0, diag1_SSE1, diag1_SSE2, diag1_SSE3;
++#ifdef gmx_checkbitmask_epi32
++    gmx_epi32 mask_S0, mask_S1, mask_S2, mask_S3;
 +#else
- #if defined GMX_X86_SSE2 && defined GMX_MM128_HERE
-     __m128i    zeroi_SSE = _mm_setzero_si128();
- #endif
-     gmx_mm_pr  zero_SSE = gmx_set1_pr(0);
++    gmx_mm_pr mask_S0, mask_S1, mask_S2, mask_S3;
 +#endif
 +
-     gmx_mm_pr  one_SSE = gmx_set1_pr(1.0);
-     gmx_mm_pr  iq_SSE0 = gmx_setzero_pr();
-     gmx_mm_pr  iq_SSE1 = gmx_setzero_pr();
-     gmx_mm_pr  iq_SSE2 = gmx_setzero_pr();
-     gmx_mm_pr  iq_SSE3 = gmx_setzero_pr();
-     gmx_mm_pr  mrc_3_SSE;
++    gmx_mm_pr  zero_S = gmx_set1_pr(0);
 +
-     gmx_mm_pr  hrc_3_SSE, moh_rc_SSE;
++    gmx_mm_pr  one_S  = gmx_set1_pr(1.0);
++    gmx_mm_pr  iq_S0  = gmx_setzero_pr();
++    gmx_mm_pr  iq_S1  = gmx_setzero_pr();
++    gmx_mm_pr  iq_S2  = gmx_setzero_pr();
++    gmx_mm_pr  iq_S3  = gmx_setzero_pr();
++    gmx_mm_pr  mrc_3_S;
 +#ifdef CALC_ENERGIES
-     gmx_mm_pr   invtsp_SSE;
++    gmx_mm_pr  hrc_3_S, moh_rc_S;
 +#endif
 +
 +#ifdef CALC_COUL_TAB
 +    /* Coulomb table variables */
- #ifdef GMX_MM256_HERE
++    gmx_mm_pr   invtsp_S;
 +    const real *tab_coul_F;
 +#ifndef TAB_FDV0
 +    const real *tab_coul_V;
 +#endif
-     gmx_mm_pr  mhalfsp_SSE;
++#if GMX_NBNXN_SIMD_BITWIDTH == 256
 +    int        ti0_array[2*GMX_SIMD_WIDTH_HERE-1], *ti0;
 +    int        ti1_array[2*GMX_SIMD_WIDTH_HERE-1], *ti1;
 +    int        ti2_array[2*GMX_SIMD_WIDTH_HERE-1], *ti2;
 +    int        ti3_array[2*GMX_SIMD_WIDTH_HERE-1], *ti3;
 +#endif
 +#ifdef CALC_ENERGIES
-     gmx_mm_pr beta2_SSE, beta_SSE;
++    gmx_mm_pr  mhalfsp_S;
 +#endif
 +#endif
 +
 +#ifdef CALC_COUL_EWALD
-     gmx_mm_pr  sh_ewald_SSE;
++    gmx_mm_pr beta2_S, beta_S;
 +#endif
 +
 +#if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
-     gmx_mm_pr   hsig_i_SSE0, seps_i_SSE0;
-     gmx_mm_pr   hsig_i_SSE1, seps_i_SSE1;
-     gmx_mm_pr   hsig_i_SSE2, seps_i_SSE2;
-     gmx_mm_pr   hsig_i_SSE3, seps_i_SSE3;
++    gmx_mm_pr  sh_ewald_S;
 +#endif
 +
 +#ifdef LJ_COMB_LB
 +    const real *ljc;
 +
-     gmx_mm_pr   c6_SSE0, c12_SSE0;
-     gmx_mm_pr   c6_SSE1, c12_SSE1;
-     gmx_mm_pr   c6_SSE2, c12_SSE2;
-     gmx_mm_pr   c6_SSE3, c12_SSE3;
++    gmx_mm_pr   hsig_i_S0, seps_i_S0;
++    gmx_mm_pr   hsig_i_S1, seps_i_S1;
++    gmx_mm_pr   hsig_i_S2, seps_i_S2;
++    gmx_mm_pr   hsig_i_S3, seps_i_S3;
 +#else
 +#ifdef FIX_LJ_C
 +    real        pvdw_array[2*UNROLLI*UNROLLJ+3];
 +    real       *pvdw_c6, *pvdw_c12;
-     gmx_mm_pr   c6s_SSE0, c12s_SSE0;
-     gmx_mm_pr   c6s_SSE1, c12s_SSE1;
-     gmx_mm_pr   c6s_SSE2 = gmx_setzero_pr(), c12s_SSE2 = gmx_setzero_pr();
-     gmx_mm_pr   c6s_SSE3 = gmx_setzero_pr(), c12s_SSE3 = gmx_setzero_pr();
++    gmx_mm_pr   c6_S0, c12_S0;
++    gmx_mm_pr   c6_S1, c12_S1;
++    gmx_mm_pr   c6_S2, c12_S2;
++    gmx_mm_pr   c6_S3, c12_S3;
 +#endif
 +
 +#ifdef LJ_COMB_GEOM
 +    const real *ljc;
 +
-     gmx_mm_pr  vctotSSE, VvdwtotSSE;
-     gmx_mm_pr  sixthSSE, twelvethSSE;
++    gmx_mm_pr   c6s_S0, c12s_S0;
++    gmx_mm_pr   c6s_S1, c12s_S1;
++    gmx_mm_pr   c6s_S2 = gmx_setzero_pr(), c12s_S2 = gmx_setzero_pr();
++    gmx_mm_pr   c6s_S3 = gmx_setzero_pr(), c12s_S3 = gmx_setzero_pr();
 +#endif
 +#endif /* LJ_COMB_LB */
 +
-     gmx_mm_pr  avoid_sing_SSE;
-     gmx_mm_pr  rc2_SSE;
++    gmx_mm_pr  vctot_S, Vvdwtot_S;
++    gmx_mm_pr  sixth_S, twelveth_S;
 +
-     gmx_mm_pr  rcvdw2_SSE;
++    gmx_mm_pr  avoid_sing_S;
++    gmx_mm_pr  rc2_S;
 +#ifdef VDW_CUTOFF_CHECK
-     gmx_mm_pr  sh_invrc6_SSE, sh_invrc12_SSE;
++    gmx_mm_pr  rcvdw2_S;
 +#endif
 +
 +#ifdef CALC_ENERGIES
-     diag_jmi_SSE = gmx_load_pr(nbat->simd_4xn_diag);
++    gmx_mm_pr  sh_invrc6_S, sh_invrc12_S;
 +
 +    /* cppcheck-suppress unassignedVariable */
 +    real       tmpsum_array[15], *tmpsum;
 +#endif
 +#ifdef CALC_SHIFTFORCES
 +    /* cppcheck-suppress unassignedVariable */
 +    real       shf_array[15], *shf;
 +#endif
 +
 +    int ninner;
 +
 +#ifdef COUNT_PAIRS
 +    int npair = 0;
 +#endif
 +
 +#if defined LJ_COMB_GEOM || defined LJ_COMB_LB
 +    ljc = nbat->lj_comb;
 +#else
 +    /* No combination rule used */
 +#ifndef GMX_DOUBLE
 +    nbfp_ptr    = nbat->nbfp_s4;
 +#define NBFP_STRIDE  4
 +#else
 +    nbfp_ptr    = nbat->nbfp;
 +#define NBFP_STRIDE  2
 +#endif
 +    nbfp_stride = NBFP_STRIDE;
 +#endif
 +
 +    /* Load j-i for the first i */
-     diag_SSE0    = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag_SSE1    = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag_SSE2    = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag_SSE3    = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
++    diag_jmi_S = gmx_load_pr(nbat->simd_4xn_diag);
 +    /* Generate all the diagonal masks as comparison results */
 +#if UNROLLI == UNROLLJ
-     diag0_SSE0   = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag0_SSE1   = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag0_SSE2   = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag0_SSE3   = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
++    diag_S0    = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag_S1    = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag_S2    = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag_S3    = gmx_cmplt_pr(zero_S, diag_jmi_S);
 +#else
 +#if UNROLLI == 2*UNROLLJ || 2*UNROLLI == UNROLLJ
-     diag_jmi_SSE = gmx_load_pr(nbat->simd_4xn_diag+UNROLLJ);
++    diag0_S0   = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag0_S1   = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag0_S2   = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag0_S3   = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
 +
 +#if UNROLLI == 2*UNROLLJ
 +    /* Load j-i for the second half of the j-cluster */
-     diag1_SSE0   = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag1_SSE1   = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag1_SSE2   = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
-     diag_jmi_SSE = gmx_sub_pr(diag_jmi_SSE, one_SSE);
-     diag1_SSE3   = gmx_cmplt_pr(zero_SSE, diag_jmi_SSE);
++    diag_jmi_S = gmx_load_pr(nbat->simd_4xn_diag+UNROLLJ);
 +#endif
 +
- #ifdef GMX_MM256_HERE
++    diag1_S0   = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag1_S1   = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag1_S2   = gmx_cmplt_pr(zero_S, diag_jmi_S);
++    diag_jmi_S = gmx_sub_pr(diag_jmi_S, one_S);
++    diag1_S3   = gmx_cmplt_pr(zero_S, diag_jmi_S);
 +#endif
 +#endif
 +
++    /* Load masks for topology exclusion masking */
++#ifdef gmx_checkbitmask_epi32
++    mask_S0    = gmx_load_si(nbat->simd_excl_mask + 0*GMX_NBNXN_SIMD_BITWIDTH/32);
++    mask_S1    = gmx_load_si(nbat->simd_excl_mask + 1*GMX_NBNXN_SIMD_BITWIDTH/32);
++    mask_S2    = gmx_load_si(nbat->simd_excl_mask + 2*GMX_NBNXN_SIMD_BITWIDTH/32);
++    mask_S3    = gmx_load_si(nbat->simd_excl_mask + 3*GMX_NBNXN_SIMD_BITWIDTH/32);
++#else
++    mask_S0    = gmx_load_pr((real *)nbat->simd_excl_mask + 0*UNROLLJ);
++    mask_S1    = gmx_load_pr((real *)nbat->simd_excl_mask + 1*UNROLLJ);
++    mask_S2    = gmx_load_pr((real *)nbat->simd_excl_mask + 2*UNROLLJ);
++    mask_S3    = gmx_load_pr((real *)nbat->simd_excl_mask + 3*UNROLLJ);
++#endif
++
 +#ifdef CALC_COUL_TAB
-     ti0 = (int *)(((size_t)(ti0_array+GMX_SIMD_WIDTH_HERE-1)) & (~((size_t)(GMX_SIMD_WIDTH_HERE*sizeof(int)-1))));
-     ti1 = (int *)(((size_t)(ti1_array+GMX_SIMD_WIDTH_HERE-1)) & (~((size_t)(GMX_SIMD_WIDTH_HERE*sizeof(int)-1))));
-     ti2 = (int *)(((size_t)(ti2_array+GMX_SIMD_WIDTH_HERE-1)) & (~((size_t)(GMX_SIMD_WIDTH_HERE*sizeof(int)-1))));
-     ti3 = (int *)(((size_t)(ti3_array+GMX_SIMD_WIDTH_HERE-1)) & (~((size_t)(GMX_SIMD_WIDTH_HERE*sizeof(int)-1))));
++#if GMX_NBNXN_SIMD_BITWIDTH == 256
 +    /* Generate aligned table index pointers */
-     invtsp_SSE  = gmx_set1_pr(ic->tabq_scale);
++    ti0 = gmx_simd_align_int(ti0_array);
++    ti1 = gmx_simd_align_int(ti1_array);
++    ti2 = gmx_simd_align_int(ti2_array);
++    ti3 = gmx_simd_align_int(ti3_array);
 +#endif
 +
-     mhalfsp_SSE = gmx_set1_pr(-0.5/ic->tabq_scale);
++    invtsp_S  = gmx_set1_pr(ic->tabq_scale);
 +#ifdef CALC_ENERGIES
-     beta2_SSE = gmx_set1_pr(ic->ewaldcoeff*ic->ewaldcoeff);
-     beta_SSE  = gmx_set1_pr(ic->ewaldcoeff);
++    mhalfsp_S = gmx_set1_pr(-0.5/ic->tabq_scale);
 +#endif
 +
 +#ifdef TAB_FDV0
 +    tab_coul_F = ic->tabq_coul_FDV0;
 +#else
 +    tab_coul_F = ic->tabq_coul_F;
 +    tab_coul_V = ic->tabq_coul_V;
 +#endif
 +#endif /* CALC_COUL_TAB */
 +
 +#ifdef CALC_COUL_EWALD
-     sh_ewald_SSE = gmx_set1_pr(ic->sh_ewald);
++    beta2_S = gmx_set1_pr(ic->ewaldcoeff*ic->ewaldcoeff);
++    beta_S  = gmx_set1_pr(ic->ewaldcoeff);
 +#endif
 +
 +#if (defined CALC_COUL_TAB || defined CALC_COUL_EWALD) && defined CALC_ENERGIES
-     avoid_sing_SSE = gmx_set1_pr(NBNXN_AVOID_SING_R2_INC);
++    sh_ewald_S = gmx_set1_pr(ic->sh_ewald);
 +#endif
 +
 +    q                   = nbat->q;
 +    type                = nbat->type;
 +    facel               = ic->epsfac;
 +    shiftvec            = shift_vec[0];
 +    x                   = nbat->x;
 +
-     rc2_SSE    = gmx_set1_pr(ic->rcoulomb*ic->rcoulomb);
++    avoid_sing_S = gmx_set1_pr(NBNXN_AVOID_SING_R2_INC);
 +
 +    /* The kernel either supports rcoulomb = rvdw or rcoulomb >= rvdw */
-     rcvdw2_SSE = gmx_set1_pr(ic->rvdw*ic->rvdw);
++    rc2_S    = gmx_set1_pr(ic->rcoulomb*ic->rcoulomb);
 +#ifdef VDW_CUTOFF_CHECK
-     sixthSSE    = gmx_set1_pr(1.0/6.0);
-     twelvethSSE = gmx_set1_pr(1.0/12.0);
++    rcvdw2_S = gmx_set1_pr(ic->rvdw*ic->rvdw);
 +#endif
 +
 +#ifdef CALC_ENERGIES
-     sh_invrc6_SSE  = gmx_set1_pr(ic->sh_invrc6);
-     sh_invrc12_SSE = gmx_set1_pr(ic->sh_invrc6*ic->sh_invrc6);
++    sixth_S      = gmx_set1_pr(1.0/6.0);
++    twelveth_S   = gmx_set1_pr(1.0/12.0);
 +
-     mrc_3_SSE = gmx_set1_pr(-2*ic->k_rf);
++    sh_invrc6_S  = gmx_set1_pr(ic->sh_invrc6);
++    sh_invrc12_S = gmx_set1_pr(ic->sh_invrc6*ic->sh_invrc6);
 +#endif
 +
-     hrc_3_SSE = gmx_set1_pr(ic->k_rf);
++    mrc_3_S  = gmx_set1_pr(-2*ic->k_rf);
 +
 +#ifdef CALC_ENERGIES
-     moh_rc_SSE = gmx_set1_pr(-ic->c_rf);
++    hrc_3_S  = gmx_set1_pr(ic->k_rf);
 +
-     tmpsum = (real *)(((size_t)(tmpsum_array+7)) & (~((size_t)31)));
++    moh_rc_S = gmx_set1_pr(-ic->c_rf);
 +#endif
 +
 +#ifdef CALC_ENERGIES
-     shf = (real *)(((size_t)(shf_array+7)) & (~((size_t)31)));
++    tmpsum   = gmx_simd_align_real(tmpsum_array);
 +#endif
 +#ifdef CALC_SHIFTFORCES
-     pvdw_c6  = (real *)(((size_t)(pvdw_array+3)) & (~((size_t)15)));
++    shf      = gmx_simd_align_real(shf_array);
 +#endif
 +
 +#ifdef FIX_LJ_C
-     c6_SSE0            = gmx_load_pr(pvdw_c6 +0*UNROLLJ);
-     c6_SSE1            = gmx_load_pr(pvdw_c6 +1*UNROLLJ);
-     c6_SSE2            = gmx_load_pr(pvdw_c6 +2*UNROLLJ);
-     c6_SSE3            = gmx_load_pr(pvdw_c6 +3*UNROLLJ);
-     c12_SSE0           = gmx_load_pr(pvdw_c12+0*UNROLLJ);
-     c12_SSE1           = gmx_load_pr(pvdw_c12+1*UNROLLJ);
-     c12_SSE2           = gmx_load_pr(pvdw_c12+2*UNROLLJ);
-     c12_SSE3           = gmx_load_pr(pvdw_c12+3*UNROLLJ);
++    pvdw_c6  = gmx_simd_align_real(pvdw_array+3);
 +    pvdw_c12 = pvdw_c6 + UNROLLI*UNROLLJ;
 +
 +    for (jp = 0; jp < UNROLLJ; jp++)
 +    {
 +        pvdw_c6 [0*UNROLLJ+jp] = nbat->nbfp[0*2];
 +        pvdw_c6 [1*UNROLLJ+jp] = nbat->nbfp[0*2];
 +        pvdw_c6 [2*UNROLLJ+jp] = nbat->nbfp[0*2];
 +        pvdw_c6 [3*UNROLLJ+jp] = nbat->nbfp[0*2];
 +
 +        pvdw_c12[0*UNROLLJ+jp] = nbat->nbfp[0*2+1];
 +        pvdw_c12[1*UNROLLJ+jp] = nbat->nbfp[0*2+1];
 +        pvdw_c12[2*UNROLLJ+jp] = nbat->nbfp[0*2+1];
 +        pvdw_c12[3*UNROLLJ+jp] = nbat->nbfp[0*2+1];
 +    }
-         shX_SSE = gmx_load1_pr(shiftvec+ish3);
-         shY_SSE = gmx_load1_pr(shiftvec+ish3+1);
-         shZ_SSE = gmx_load1_pr(shiftvec+ish3+2);
++    c6_S0            = gmx_load_pr(pvdw_c6 +0*UNROLLJ);
++    c6_S1            = gmx_load_pr(pvdw_c6 +1*UNROLLJ);
++    c6_S2            = gmx_load_pr(pvdw_c6 +2*UNROLLJ);
++    c6_S3            = gmx_load_pr(pvdw_c6 +3*UNROLLJ);
++
++    c12_S0           = gmx_load_pr(pvdw_c12+0*UNROLLJ);
++    c12_S1           = gmx_load_pr(pvdw_c12+1*UNROLLJ);
++    c12_S2           = gmx_load_pr(pvdw_c12+2*UNROLLJ);
++    c12_S3           = gmx_load_pr(pvdw_c12+3*UNROLLJ);
 +#endif /* FIX_LJ_C */
 +
 +#ifdef ENERGY_GROUPS
 +    egps_ishift  = nbat->neg_2log;
 +    egps_imask   = (1<<egps_ishift) - 1;
 +    egps_jshift  = 2*nbat->neg_2log;
 +    egps_jmask   = (1<<egps_jshift) - 1;
 +    egps_jstride = (UNROLLJ>>1)*UNROLLJ;
 +    /* Major division is over i-particle energy groups, determine the stride */
 +    Vstride_i    = nbat->nenergrp*(1<<nbat->neg_2log)*egps_jstride;
 +#endif
 +
 +    l_cj = nbl->cj;
 +
 +    ninner = 0;
 +    for (n = 0; n < nbl->nci; n++)
 +    {
 +        nbln = &nbl->ci[n];
 +
 +        ish              = (nbln->shift & NBNXN_CI_SHIFT);
 +        ish3             = ish*3;
 +        cjind0           = nbln->cj_ind_start;
 +        cjind1           = nbln->cj_ind_end;
 +        ci               = nbln->ci;
 +        ci_sh            = (ish == CENTRAL ? ci : -1);
 +
-         ix_SSE0          = gmx_add_pr(gmx_load1_pr(x+scix), shX_SSE);
-         ix_SSE1          = gmx_add_pr(gmx_load1_pr(x+scix+1), shX_SSE);
-         ix_SSE2          = gmx_add_pr(gmx_load1_pr(x+scix+2), shX_SSE);
-         ix_SSE3          = gmx_add_pr(gmx_load1_pr(x+scix+3), shX_SSE);
-         iy_SSE0          = gmx_add_pr(gmx_load1_pr(x+sciy), shY_SSE);
-         iy_SSE1          = gmx_add_pr(gmx_load1_pr(x+sciy+1), shY_SSE);
-         iy_SSE2          = gmx_add_pr(gmx_load1_pr(x+sciy+2), shY_SSE);
-         iy_SSE3          = gmx_add_pr(gmx_load1_pr(x+sciy+3), shY_SSE);
-         iz_SSE0          = gmx_add_pr(gmx_load1_pr(x+sciz), shZ_SSE);
-         iz_SSE1          = gmx_add_pr(gmx_load1_pr(x+sciz+1), shZ_SSE);
-         iz_SSE2          = gmx_add_pr(gmx_load1_pr(x+sciz+2), shZ_SSE);
-         iz_SSE3          = gmx_add_pr(gmx_load1_pr(x+sciz+3), shZ_SSE);
++        shX_S = gmx_load1_pr(shiftvec+ish3);
++        shY_S = gmx_load1_pr(shiftvec+ish3+1);
++        shZ_S = gmx_load1_pr(shiftvec+ish3+2);
 +
 +#if UNROLLJ <= 4
 +        sci              = ci*STRIDE;
 +        scix             = sci*DIM;
 +        sci2             = sci*2;
 +#else
 +        sci              = (ci>>1)*STRIDE;
 +        scix             = sci*DIM + (ci & 1)*(STRIDE>>1);
 +        sci2             = sci*2 + (ci & 1)*(STRIDE>>1);
 +        sci             += (ci & 1)*(STRIDE>>1);
 +#endif
 +
 +        /* We have 5 LJ/C combinations, but use only three inner loops,
 +         * as the other combinations are unlikely and/or not much faster:
 +         * inner half-LJ + C for half-LJ + C / no-LJ + C
 +         * inner LJ + C      for full-LJ + C
 +         * inner LJ          for full-LJ + no-C / half-LJ + no-C
 +         */
 +        do_LJ   = (nbln->shift & NBNXN_CI_DO_LJ(0));
 +        do_coul = (nbln->shift & NBNXN_CI_DO_COUL(0));
 +        half_LJ = ((nbln->shift & NBNXN_CI_HALF_LJ(0)) || !do_LJ) && do_coul;
 +
 +#ifdef ENERGY_GROUPS
 +        egps_i = nbat->energrp[ci];
 +        {
 +            int ia, egp_ia;
 +
 +            for (ia = 0; ia < UNROLLI; ia++)
 +            {
 +                egp_ia     = (egps_i >> (ia*egps_ishift)) & egps_imask;
 +                vvdwtp[ia] = Vvdw + egp_ia*Vstride_i;
 +                vctp[ia]   = Vc   + egp_ia*Vstride_i;
 +            }
 +        }
 +#endif
 +#if defined CALC_ENERGIES
 +#if UNROLLJ == 4
 +        if (do_coul && l_cj[nbln->cj_ind_start].cj == ci_sh)
 +#endif
 +#if UNROLLJ == 2
 +        if (do_coul && l_cj[nbln->cj_ind_start].cj == (ci_sh<<1))
 +#endif
 +#if UNROLLJ == 8
 +        if (do_coul && l_cj[nbln->cj_ind_start].cj == (ci_sh>>1))
 +#endif
 +        {
 +            int  ia;
 +            real Vc_sub_self;
 +
 +#ifdef CALC_COUL_RF
 +            Vc_sub_self = 0.5*ic->c_rf;
 +#endif
 +#ifdef CALC_COUL_TAB
 +#ifdef TAB_FDV0
 +            Vc_sub_self = 0.5*tab_coul_F[2];
 +#else
 +            Vc_sub_self = 0.5*tab_coul_V[0];
 +#endif
 +#endif
 +#ifdef CALC_COUL_EWALD
 +            /* beta/sqrt(pi) */
 +            Vc_sub_self = 0.5*ic->ewaldcoeff*M_2_SQRTPI;
 +#endif
 +
 +            for (ia = 0; ia < UNROLLI; ia++)
 +            {
 +                real qi;
 +
 +                qi = q[sci+ia];
 +#ifdef ENERGY_GROUPS
 +                vctp[ia][((egps_i>>(ia*egps_ishift)) & egps_imask)*egps_jstride]
 +#else
 +                Vc[0]
 +#endif
 +                    -= facel*qi*qi*Vc_sub_self;
 +            }
 +        }
 +#endif
 +
 +        /* Load i atom data */
 +        sciy             = scix + STRIDE;
 +        sciz             = sciy + STRIDE;
-             iq_SSE0      = gmx_set1_pr(facel*q[sci]);
-             iq_SSE1      = gmx_set1_pr(facel*q[sci+1]);
-             iq_SSE2      = gmx_set1_pr(facel*q[sci+2]);
-             iq_SSE3      = gmx_set1_pr(facel*q[sci+3]);
++        ix_S0          = gmx_add_pr(gmx_load1_pr(x+scix), shX_S);
++        ix_S1          = gmx_add_pr(gmx_load1_pr(x+scix+1), shX_S);
++        ix_S2          = gmx_add_pr(gmx_load1_pr(x+scix+2), shX_S);
++        ix_S3          = gmx_add_pr(gmx_load1_pr(x+scix+3), shX_S);
++        iy_S0          = gmx_add_pr(gmx_load1_pr(x+sciy), shY_S);
++        iy_S1          = gmx_add_pr(gmx_load1_pr(x+sciy+1), shY_S);
++        iy_S2          = gmx_add_pr(gmx_load1_pr(x+sciy+2), shY_S);
++        iy_S3          = gmx_add_pr(gmx_load1_pr(x+sciy+3), shY_S);
++        iz_S0          = gmx_add_pr(gmx_load1_pr(x+sciz), shZ_S);
++        iz_S1          = gmx_add_pr(gmx_load1_pr(x+sciz+1), shZ_S);
++        iz_S2          = gmx_add_pr(gmx_load1_pr(x+sciz+2), shZ_S);
++        iz_S3          = gmx_add_pr(gmx_load1_pr(x+sciz+3), shZ_S);
 +
 +        if (do_coul)
 +        {
-         hsig_i_SSE0      = gmx_load1_pr(ljc+sci2+0);
-         hsig_i_SSE1      = gmx_load1_pr(ljc+sci2+1);
-         hsig_i_SSE2      = gmx_load1_pr(ljc+sci2+2);
-         hsig_i_SSE3      = gmx_load1_pr(ljc+sci2+3);
-         seps_i_SSE0      = gmx_load1_pr(ljc+sci2+STRIDE+0);
-         seps_i_SSE1      = gmx_load1_pr(ljc+sci2+STRIDE+1);
-         seps_i_SSE2      = gmx_load1_pr(ljc+sci2+STRIDE+2);
-         seps_i_SSE3      = gmx_load1_pr(ljc+sci2+STRIDE+3);
++            iq_S0      = gmx_set1_pr(facel*q[sci]);
++            iq_S1      = gmx_set1_pr(facel*q[sci+1]);
++            iq_S2      = gmx_set1_pr(facel*q[sci+2]);
++            iq_S3      = gmx_set1_pr(facel*q[sci+3]);
 +        }
 +
 +#ifdef LJ_COMB_LB
-         c6s_SSE0         = gmx_load1_pr(ljc+sci2+0);
-         c6s_SSE1         = gmx_load1_pr(ljc+sci2+1);
++        hsig_i_S0      = gmx_load1_pr(ljc+sci2+0);
++        hsig_i_S1      = gmx_load1_pr(ljc+sci2+1);
++        hsig_i_S2      = gmx_load1_pr(ljc+sci2+2);
++        hsig_i_S3      = gmx_load1_pr(ljc+sci2+3);
++        seps_i_S0      = gmx_load1_pr(ljc+sci2+STRIDE+0);
++        seps_i_S1      = gmx_load1_pr(ljc+sci2+STRIDE+1);
++        seps_i_S2      = gmx_load1_pr(ljc+sci2+STRIDE+2);
++        seps_i_S3      = gmx_load1_pr(ljc+sci2+STRIDE+3);
 +#else
 +#ifdef LJ_COMB_GEOM
-             c6s_SSE2     = gmx_load1_pr(ljc+sci2+2);
-             c6s_SSE3     = gmx_load1_pr(ljc+sci2+3);
++        c6s_S0         = gmx_load1_pr(ljc+sci2+0);
++        c6s_S1         = gmx_load1_pr(ljc+sci2+1);
 +        if (!half_LJ)
 +        {
-         c12s_SSE0        = gmx_load1_pr(ljc+sci2+STRIDE+0);
-         c12s_SSE1        = gmx_load1_pr(ljc+sci2+STRIDE+1);
++            c6s_S2     = gmx_load1_pr(ljc+sci2+2);
++            c6s_S3     = gmx_load1_pr(ljc+sci2+3);
 +        }
-             c12s_SSE2    = gmx_load1_pr(ljc+sci2+STRIDE+2);
-             c12s_SSE3    = gmx_load1_pr(ljc+sci2+STRIDE+3);
++        c12s_S0        = gmx_load1_pr(ljc+sci2+STRIDE+0);
++        c12s_S1        = gmx_load1_pr(ljc+sci2+STRIDE+1);
 +        if (!half_LJ)
 +        {
-         VvdwtotSSE       = gmx_setzero_pr();
-         vctotSSE         = gmx_setzero_pr();
++            c12s_S2    = gmx_load1_pr(ljc+sci2+STRIDE+2);
++            c12s_S3    = gmx_load1_pr(ljc+sci2+STRIDE+3);
 +        }
 +#else
 +        nbfp0     = nbfp_ptr + type[sci  ]*nbat->ntype*nbfp_stride;
 +        nbfp1     = nbfp_ptr + type[sci+1]*nbat->ntype*nbfp_stride;
 +        if (!half_LJ)
 +        {
 +            nbfp2 = nbfp_ptr + type[sci+2]*nbat->ntype*nbfp_stride;
 +            nbfp3 = nbfp_ptr + type[sci+3]*nbat->ntype*nbfp_stride;
 +        }
 +#endif
 +#endif
 +
 +        /* Zero the potential energy for this list */
-         fix_SSE0           = gmx_setzero_pr();
-         fix_SSE1           = gmx_setzero_pr();
-         fix_SSE2           = gmx_setzero_pr();
-         fix_SSE3           = gmx_setzero_pr();
-         fiy_SSE0           = gmx_setzero_pr();
-         fiy_SSE1           = gmx_setzero_pr();
-         fiy_SSE2           = gmx_setzero_pr();
-         fiy_SSE3           = gmx_setzero_pr();
-         fiz_SSE0           = gmx_setzero_pr();
-         fiz_SSE1           = gmx_setzero_pr();
-         fiz_SSE2           = gmx_setzero_pr();
-         fiz_SSE3           = gmx_setzero_pr();
++        Vvdwtot_S        = gmx_setzero_pr();
++        vctot_S          = gmx_setzero_pr();
 +
 +        /* Clear i atom forces */
- #define gmx_load_ps4  _mm_load_ps
- #define gmx_store_ps4 _mm_store_ps
- #define gmx_add_ps4   _mm_add_ps
++        fix_S0           = gmx_setzero_pr();
++        fix_S1           = gmx_setzero_pr();
++        fix_S2           = gmx_setzero_pr();
++        fix_S3           = gmx_setzero_pr();
++        fiy_S0           = gmx_setzero_pr();
++        fiy_S1           = gmx_setzero_pr();
++        fiy_S2           = gmx_setzero_pr();
++        fiy_S3           = gmx_setzero_pr();
++        fiz_S0           = gmx_setzero_pr();
++        fiz_S1           = gmx_setzero_pr();
++        fiz_S2           = gmx_setzero_pr();
++        fiz_S3           = gmx_setzero_pr();
 +
 +        cjind = cjind0;
 +
 +        /* Currently all kernels use (at least half) LJ */
 +#define CALC_LJ
 +        if (half_LJ)
 +        {
 +#define CALC_COULOMB
 +#define HALF_LJ
 +#define CHECK_EXCLS
 +            while (cjind < cjind1 && nbl->cj[cjind].excl != SIMD_MASK_ALL)
 +            {
 +#include "nbnxn_kernel_simd_4xn_inner.h"
 +                cjind++;
 +            }
 +#undef CHECK_EXCLS
 +            for (; (cjind < cjind1); cjind++)
 +            {
 +#include "nbnxn_kernel_simd_4xn_inner.h"
 +            }
 +#undef HALF_LJ
 +#undef CALC_COULOMB
 +        }
 +        else if (do_coul)
 +        {
 +#define CALC_COULOMB
 +#define CHECK_EXCLS
 +            while (cjind < cjind1 && nbl->cj[cjind].excl != SIMD_MASK_ALL)
 +            {
 +#include "nbnxn_kernel_simd_4xn_inner.h"
 +                cjind++;
 +            }
 +#undef CHECK_EXCLS
 +            for (; (cjind < cjind1); cjind++)
 +            {
 +#include "nbnxn_kernel_simd_4xn_inner.h"
 +            }
 +#undef CALC_COULOMB
 +        }
 +        else
 +        {
 +#define CHECK_EXCLS
 +            while (cjind < cjind1 && nbl->cj[cjind].excl != SIMD_MASK_ALL)
 +            {
 +#include "nbnxn_kernel_simd_4xn_inner.h"
 +                cjind++;
 +            }
 +#undef CHECK_EXCLS
 +            for (; (cjind < cjind1); cjind++)
 +            {
 +#include "nbnxn_kernel_simd_4xn_inner.h"
 +            }
 +        }
 +#undef CALC_LJ
 +        ninner += cjind1 - cjind0;
 +
 +        /* Add accumulated i-forces to the force array */
 +#if UNROLLJ >= 4
 +#ifndef GMX_DOUBLE
- #define gmx_load_ps4  _mm256_load_pd
- #define gmx_store_ps4 _mm256_store_pd
- #define gmx_add_ps4   _mm256_add_pd
++#define gmx_load_pr4  _mm_load_ps
++#define gmx_store_pr4 _mm_store_ps
++#define gmx_add_pr4   _mm_add_ps
 +#else
-         GMX_MM_TRANSPOSE_SUM4_PR(fix_SSE0, fix_SSE1, fix_SSE2, fix_SSE3, fix_SSE);
-         gmx_store_ps4(f+scix, gmx_add_ps4(fix_SSE, gmx_load_ps4(f+scix)));
++#define gmx_load_pr4  _mm256_load_pd
++#define gmx_store_pr4 _mm256_store_pd
++#define gmx_add_pr4   _mm256_add_pd
 +#endif
-         GMX_MM_TRANSPOSE_SUM4_PR(fiy_SSE0, fiy_SSE1, fiy_SSE2, fiy_SSE3, fiy_SSE);
-         gmx_store_ps4(f+sciy, gmx_add_ps4(fiy_SSE, gmx_load_ps4(f+sciy)));
++        GMX_MM_TRANSPOSE_SUM4_PR(fix_S0, fix_S1, fix_S2, fix_S3, fix_S);
++        gmx_store_pr4(f+scix, gmx_add_pr4(fix_S, gmx_load_pr4(f+scix)));
 +
-         GMX_MM_TRANSPOSE_SUM4_PR(fiz_SSE0, fiz_SSE1, fiz_SSE2, fiz_SSE3, fiz_SSE);
-         gmx_store_ps4(f+sciz, gmx_add_ps4(fiz_SSE, gmx_load_ps4(f+sciz)));
++        GMX_MM_TRANSPOSE_SUM4_PR(fiy_S0, fiy_S1, fiy_S2, fiy_S3, fiy_S);
++        gmx_store_pr4(f+sciy, gmx_add_pr4(fiy_S, gmx_load_pr4(f+sciy)));
 +
-         gmx_store_ps4(shf, fix_SSE);
++        GMX_MM_TRANSPOSE_SUM4_PR(fiz_S0, fiz_S1, fiz_S2, fiz_S3, fiz_S);
++        gmx_store_pr4(f+sciz, gmx_add_pr4(fiz_S, gmx_load_pr4(f+sciz)));
 +
 +#ifdef CALC_SHIFTFORCES
-         gmx_store_ps4(shf, fiy_SSE);
++        gmx_store_pr4(shf, fix_S);
 +        fshift[ish3+0] += SUM_SIMD4(shf);
-         gmx_store_ps4(shf, fiz_SSE);
++        gmx_store_pr4(shf, fiy_S);
 +        fshift[ish3+1] += SUM_SIMD4(shf);
-         GMX_MM_TRANSPOSE_SUM2_PD(fix_SSE0, fix_SSE1, fix0_SSE);
-         _mm_store_pd(f+scix, _mm_add_pd(fix0_SSE, _mm_load_pd(f+scix)));
-         GMX_MM_TRANSPOSE_SUM2_PD(fix_SSE2, fix_SSE3, fix2_SSE);
-         _mm_store_pd(f+scix+2, _mm_add_pd(fix2_SSE, _mm_load_pd(f+scix+2)));
++        gmx_store_pr4(shf, fiz_S);
 +        fshift[ish3+2] += SUM_SIMD4(shf);
 +#endif
 +#else
-         GMX_MM_TRANSPOSE_SUM2_PD(fiy_SSE0, fiy_SSE1, fiy0_SSE);
-         _mm_store_pd(f+sciy, _mm_add_pd(fiy0_SSE, _mm_load_pd(f+sciy)));
-         GMX_MM_TRANSPOSE_SUM2_PD(fiy_SSE2, fiy_SSE3, fiy2_SSE);
-         _mm_store_pd(f+sciy+2, _mm_add_pd(fiy2_SSE, _mm_load_pd(f+sciy+2)));
++        GMX_MM_TRANSPOSE_SUM2_PD(fix_S0, fix_S1, fix0_S);
++        _mm_store_pd(f+scix, _mm_add_pd(fix0_S, _mm_load_pd(f+scix)));
++        GMX_MM_TRANSPOSE_SUM2_PD(fix_S2, fix_S3, fix2_S);
++        _mm_store_pd(f+scix+2, _mm_add_pd(fix2_S, _mm_load_pd(f+scix+2)));
 +
-         GMX_MM_TRANSPOSE_SUM2_PD(fiz_SSE0, fiz_SSE1, fiz0_SSE);
-         _mm_store_pd(f+sciz, _mm_add_pd(fiz0_SSE, _mm_load_pd(f+sciz)));
-         GMX_MM_TRANSPOSE_SUM2_PD(fiz_SSE2, fiz_SSE3, fiz2_SSE);
-         _mm_store_pd(f+sciz+2, _mm_add_pd(fiz2_SSE, _mm_load_pd(f+sciz+2)));
++        GMX_MM_TRANSPOSE_SUM2_PD(fiy_S0, fiy_S1, fiy0_S);
++        _mm_store_pd(f+sciy, _mm_add_pd(fiy0_S, _mm_load_pd(f+sciy)));
++        GMX_MM_TRANSPOSE_SUM2_PD(fiy_S2, fiy_S3, fiy2_S);
++        _mm_store_pd(f+sciy+2, _mm_add_pd(fiy2_S, _mm_load_pd(f+sciy+2)));
 +
-         _mm_store_pd(shf, _mm_add_pd(fix0_SSE, fix2_SSE));
++        GMX_MM_TRANSPOSE_SUM2_PD(fiz_S0, fiz_S1, fiz0_S);
++        _mm_store_pd(f+sciz, _mm_add_pd(fiz0_S, _mm_load_pd(f+sciz)));
++        GMX_MM_TRANSPOSE_SUM2_PD(fiz_S2, fiz_S3, fiz2_S);
++        _mm_store_pd(f+sciz+2, _mm_add_pd(fiz2_S, _mm_load_pd(f+sciz+2)));
 +
 +#ifdef CALC_SHIFTFORCES
-         _mm_store_pd(shf, _mm_add_pd(fiy0_SSE, fiy2_SSE));
++        _mm_store_pd(shf, _mm_add_pd(fix0_S, fix2_S));
 +        fshift[ish3+0] += shf[0] + shf[1];
-         _mm_store_pd(shf, _mm_add_pd(fiz0_SSE, fiz2_SSE));
++        _mm_store_pd(shf, _mm_add_pd(fiy0_S, fiy2_S));
 +        fshift[ish3+1] += shf[0] + shf[1];
-             gmx_store_pr(tmpsum, vctotSSE);
++        _mm_store_pd(shf, _mm_add_pd(fiz0_S, fiz2_S));
 +        fshift[ish3+2] += shf[0] + shf[1];
 +#endif
 +#endif
 +
 +#ifdef CALC_ENERGIES
 +        if (do_coul)
 +        {
-         gmx_store_pr(tmpsum, VvdwtotSSE);
++            gmx_store_pr(tmpsum, vctot_S);
 +            *Vc += SUM_SIMD(tmpsum);
 +        }
 +
- #undef gmx_load_ps4
- #undef gmx_store_ps4
- #undef gmx_store_ps4
++        gmx_store_pr(tmpsum, Vvdwtot_S);
 +        *Vvdw += SUM_SIMD(tmpsum);
 +#endif
 +
 +        /* Outer loop uses 6 flops/iteration */
 +    }
 +
 +#ifdef COUNT_PAIRS
 +    printf("atom pairs %d\n", npair);
 +#endif
 +}
 +
++
++#undef gmx_load_pr4
++#undef gmx_store_pr4
++#undef gmx_store_pr4
 +
 +#undef CALC_SHIFTFORCES
 +
 +#undef UNROLLI
 +#undef UNROLLJ
 +#undef STRIDE
 +#undef TAB_FDV0
 +#undef NBFP_STRIDE
++
++#undef GMX_USE_HALF_WIDTH_SIMD_HERE
index ad5ad953f13931eeb06a9579bb8d57fb9c716049,0000000000000000000000000000000000000000..6f52427c75bfec9667c4b90099aba3df1e5c3bae
mode 100644,000000..100644
--- /dev/null
@@@ -1,572 -1,0 +1,572 @@@
- #if defined GMX_MM128_HERE || !defined GMX_DOUBLE
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS Development Team
 + * Copyright (c) 2012, by the GROMACS development team, led by
 + * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 + * others, as listed in the AUTHORS file in the top-level source
 + * directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#ifndef _nbnxn_kernel_sse_utils_h_
 +#define _nbnxn_kernel_sse_utils_h_
 +
 +/* This files contains all functions/macros for the SIMD kernels
 + * which have explicit dependencies on the j-cluster size and/or SIMD-width.
 + * The functionality which depends on the j-cluster size is:
 + *   LJ-parameter lookup
 + *   force table lookup
 + *   energy group pair energy storage
 + */
 +
 +#ifdef GMX_X86_SSE2
 +
 +/* Transpose 2 double precision registers */
 +#define GMX_MM_TRANSPOSE2_OP_PD(in0, in1, out0, out1)                      \
 +    {                                                                       \
 +        out0 = _mm_unpacklo_pd(in0, in1);                                    \
 +        out1 = _mm_unpackhi_pd(in0, in1);                                    \
 +    }
 +
- #ifndef GMX_MM256_HERE
++#if GMX_NBNXN_SIMD_BITWIDTH == 128 || !defined GMX_DOUBLE
 +/* Collect element 0 and 1 of the 4 inputs to out0 and out1, respectively */
 +#define GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(in0, in1, in2, in3, out0, out1)    \
 +    {                                                                       \
 +        __m128 _c01, _c23;                                                   \
 +        _c01 = _mm_movelh_ps(in0, in1);                                      \
 +        _c23 = _mm_movelh_ps(in2, in3);                                      \
 +        out0 = _mm_shuffle_ps(_c01, _c23, _MM_SHUFFLE(2, 0, 2, 0));              \
 +        out1 = _mm_shuffle_ps(_c01, _c23, _MM_SHUFFLE(3, 1, 3, 1));              \
 +    }
 +#else
 +/* Collect element 0 and 1 of the 4 inputs to out0 and out1, respectively */
 +#define GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(in0, in1, in2, in3, out0, out1)    \
 +    {                                                                       \
 +        __m256d _c01, _c23;                                                  \
 +        _c01 = _mm256_shuffle_pd(in0, in1, _MM_SHUFFLE(1, 0, 1, 0));             \
 +        _c23 = _mm256_shuffle_pd(in2, in3, _MM_SHUFFLE(1, 0, 1, 0));             \
 +        out0 = _mm256_shuffle_pd(_c01, _c23, _MM_SHUFFLE(2, 0, 2, 0));           \
 +        out1 = _mm256_shuffle_pd(_c01, _c23, _MM_SHUFFLE(3, 1, 3, 1));           \
 +    }
 +#endif
 +
 +/* Collect element 2 of the 4 inputs to out */
 +#define GMX_MM_SHUFFLE_4_PS_FIL2_TO_1_PS(in0, in1, in2, in3, out)           \
 +    {                                                                       \
 +        __m128 _c01, _c23;                                                   \
 +        _c01 = _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(3, 2, 3, 2));                \
 +        _c23 = _mm_shuffle_ps(in2, in3, _MM_SHUFFLE(3, 2, 3, 2));                \
 +        out  = _mm_shuffle_ps(_c01, _c23, _MM_SHUFFLE(2, 0, 2, 0));              \
 +    }
 +
- #ifdef GMX_MM128_HERE
++#if GMX_NBNXN_SIMD_BITWIDTH == 128
 +#ifndef GMX_DOUBLE
 +/* Sum the elements within each input register and store the sums in out */
 +#define GMX_MM_TRANSPOSE_SUM4_PR(in0, in1, in2, in3, out)                   \
 +    {                                                                       \
 +        _MM_TRANSPOSE4_PS(in0, in1, in2, in3);                                 \
 +        in0  = _mm_add_ps(in0, in1);                                          \
 +        in2  = _mm_add_ps(in2, in3);                                          \
 +        out  = _mm_add_ps(in0, in2);                                         \
 +    }
 +#else
 +/* Sum the elements within each input register and store the sums in out */
 +#define GMX_MM_TRANSPOSE_SUM2_PD(in0, in1, out)                           \
 +    {                                                                       \
 +        GMX_MM_TRANSPOSE2_PD(in0, in1);                                      \
 +        out  = _mm_add_pd(in0, in1);                                         \
 +    }
 +#endif
 +#else
 +#ifndef GMX_DOUBLE
 +/* Sum the elements within each input register and store the sums in out */
 +#define GMX_MM_TRANSPOSE_SUM4_PR(in0, in1, in2, in3, out)                   \
 +    {                                                                       \
 +        in0 = _mm256_hadd_ps(in0, in1);                                      \
 +        in2 = _mm256_hadd_ps(in2, in3);                                      \
 +        in1 = _mm256_hadd_ps(in0, in2);                                      \
 +        out = _mm_add_ps(_mm256_castps256_ps128(in1), _mm256_extractf128_ps(in1, 1)); \
 +    }
 +/* Sum the elements of halfs of each input register and store sums in out */
 +#define GMX_MM_TRANSPOSE_SUM4H_PR(in0, in2, out)                          \
 +    {                                                                       \
 +        in0 = _mm256_hadd_ps(in0, _mm256_setzero_ps());                      \
 +        in2 = _mm256_hadd_ps(in2, _mm256_setzero_ps());                      \
 +        in0 = _mm256_hadd_ps(in0, in2);                                      \
 +        in2 = _mm256_permute_ps(in0, _MM_SHUFFLE(2, 3, 0, 1));                  \
 +        out = _mm_add_ps(_mm256_castps256_ps128(in0), _mm256_extractf128_ps(in2, 1)); \
 +    }
 +#else
 +/* Sum the elements within each input register and store the sums in out */
 +#define GMX_MM_TRANSPOSE_SUM4_PR(in0, in1, in2, in3, out)                   \
 +    {                                                                       \
 +        in0 = _mm256_hadd_pd(in0, in1);                                      \
 +        in2 = _mm256_hadd_pd(in2, in3);                                      \
 +        out = _mm256_add_pd(_mm256_permute2f128_pd(in0, in2, 0x20), _mm256_permute2f128_pd(in0, in2, 0x31)); \
 +    }
 +#endif
 +#endif
 +
- #ifdef GMX_MM256_HERE
++#if GMX_NBNXN_SIMD_BITWIDTH == 128
 +
 +static inline __m128
 +gmx_mm128_invsqrt_ps_single(__m128 x)
 +{
 +    const __m128 half  = _mm_set_ps(0.5, 0.5, 0.5, 0.5);
 +    const __m128 three = _mm_set_ps(3.0, 3.0, 3.0, 3.0);
 +
 +    __m128       lu = _mm_rsqrt_ps(x);
 +
 +    return _mm_mul_ps(half, _mm_mul_ps(_mm_sub_ps(three, _mm_mul_ps(_mm_mul_ps(lu, lu), x)), lu));
 +}
 +
 +/* Do 2 double precision invsqrt operations.
 + * Doing the SIMD rsqrt and the first Newton Raphson iteration
 + * in single precision gives full double precision accuracy.
 + * The speed is more than double that of two gmx_mm_invsqrt_pd calls.
 + */
 +#define GMX_MM128_INVSQRT2_PD(in0, in1, out0, out1)                        \
 +    {                                                                       \
 +        const __m128d half  = _mm_set1_pd(0.5);                             \
 +        const __m128d three = _mm_set1_pd(3.0);                             \
 +        __m128        s, ir;                                                       \
 +        __m128d       lu0, lu1;                                                    \
 +                                                                        \
 +        s    = _mm_movelh_ps(_mm_cvtpd_ps(in0), _mm_cvtpd_ps(in1));          \
 +        ir   = gmx_mm128_invsqrt_ps_single(s);                              \
 +        lu0  = _mm_cvtps_pd(ir);                                            \
 +        lu1  = _mm_cvtps_pd(_mm_movehl_ps(ir, ir));                          \
 +        out0 = _mm_mul_pd(half, _mm_mul_pd(_mm_sub_pd(three, _mm_mul_pd(_mm_mul_pd(lu0, lu0), in0)), lu0)); \
 +        out1 = _mm_mul_pd(half, _mm_mul_pd(_mm_sub_pd(three, _mm_mul_pd(_mm_mul_pd(lu1, lu1), in1)), lu1)); \
 +    }
 +
 +#define GMX_MM_INVSQRT2_PD GMX_MM128_INVSQRT2_PD
 +
 +#endif
 +
- #if defined GMX_MM128_HERE && !defined GMX_DOUBLE
++#if GMX_NBNXN_SIMD_BITWIDTH == 256
 +
 +static inline __m256
 +gmx_mm256_invsqrt_ps_single(__m256 x)
 +{
 +    const __m256 half  = _mm256_set_ps(0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5);
 +    const __m256 three = _mm256_set_ps(3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0);
 +
 +    __m256       lu = _mm256_rsqrt_ps(x);
 +
 +    return _mm256_mul_ps(half, _mm256_mul_ps(_mm256_sub_ps(three, _mm256_mul_ps(_mm256_mul_ps(lu, lu), x)), lu));
 +}
 +
 +/* Do 4 double precision invsqrt operations.
 + * Doing the SIMD rsqrt and the first Newton Raphson iteration
 + * in single precision gives full double precision accuracy.
 + */
 +#define GMX_MM256_INVSQRT2_PD(in0, in1, out0, out1)                        \
 +    {                                                                       \
 +        const __m256d half  = _mm256_set1_pd(0.5);                          \
 +        const __m256d three = _mm256_set1_pd(3.0);                          \
 +        __m256        s, ir;                                                       \
 +        __m256d       lu0, lu1;                                                    \
 +                                                                        \
 +        s    = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm256_cvtpd_ps(in0)), _mm256_cvtpd_ps(in1), 1); \
 +        ir   = gmx_mm256_invsqrt_ps_single(s);                              \
 +        lu0  = _mm256_cvtps_pd(_mm256_castps256_ps128(ir));                 \
 +        lu1  = _mm256_cvtps_pd(_mm256_extractf128_ps(ir, 1));                \
 +        out0 = _mm256_mul_pd(half, _mm256_mul_pd(_mm256_sub_pd(three, _mm256_mul_pd(_mm256_mul_pd(lu0, lu0), in0)), lu0)); \
 +        out1 = _mm256_mul_pd(half, _mm256_mul_pd(_mm256_sub_pd(three, _mm256_mul_pd(_mm256_mul_pd(lu1, lu1), in1)), lu1)); \
 +    }
 +
 +#define GMX_MM_INVSQRT2_PD GMX_MM256_INVSQRT2_PD
 +
 +#endif
 +
 +/* Force and energy table load and interpolation routines */
 +
- #if defined GMX_MM256_HERE && !defined GMX_DOUBLE
++#if GMX_NBNXN_SIMD_BITWIDTH == 128 && !defined GMX_DOUBLE
 +
 +#define load_lj_pair_params(nbfp, type, aj, c6_SSE, c12_SSE)                \
 +    {                                                                       \
 +        gmx_mm_pr clj_SSE[UNROLLJ];                                         \
 +        int       p;                                                              \
 +                                                                        \
 +        for (p = 0; p < UNROLLJ; p++)                                            \
 +        {                                                                   \
 +            /* Here we load 4 aligned floats, but we need just 2 */         \
 +            clj_SSE[p] = gmx_load_pr(nbfp+type[aj+p]*NBFP_STRIDE);          \
 +        }                                                                   \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(clj_SSE[0], clj_SSE[1], clj_SSE[2], clj_SSE[3], c6_SSE, c12_SSE); \
 +    }
 +
 +#endif
 +
- #if defined GMX_MM128_HERE && defined GMX_DOUBLE
++#if GMX_NBNXN_SIMD_BITWIDTH == 256 && !defined GMX_DOUBLE
 +
 +/* Put two 128-bit 4-float registers into one 256-bit 8-float register */
 +#define GMX_2_MM_TO_M256(in0, in1, out)                                   \
 +    {                                                                       \
 +        out = _mm256_insertf128_ps(_mm256_castps128_ps256(in0), in1, 1);      \
 +    }
 +
 +#define load_lj_pair_params(nbfp, type, aj, c6_SSE, c12_SSE)                \
 +    {                                                                       \
 +        __m128 clj_SSE[UNROLLJ], c6t_SSE[2], c12t_SSE[2];                     \
 +        int    p;                                                              \
 +                                                                        \
 +        for (p = 0; p < UNROLLJ; p++)                                            \
 +        {                                                                   \
 +            /* Here we load 4 aligned floats, but we need just 2 */         \
 +            clj_SSE[p] = _mm_load_ps(nbfp+type[aj+p]*NBFP_STRIDE);          \
 +        }                                                                   \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(clj_SSE[0], clj_SSE[1], clj_SSE[2], clj_SSE[3], c6t_SSE[0], c12t_SSE[0]); \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(clj_SSE[4], clj_SSE[5], clj_SSE[6], clj_SSE[7], c6t_SSE[1], c12t_SSE[1]); \
 +                                                                        \
 +        GMX_2_MM_TO_M256(c6t_SSE[0], c6t_SSE[1], c6_SSE);                     \
 +        GMX_2_MM_TO_M256(c12t_SSE[0], c12t_SSE[1], c12_SSE);                  \
 +    }
 +
 +#define load_lj_pair_params2(nbfp0, nbfp1, type, aj, c6_SSE, c12_SSE)        \
 +    {                                                                       \
 +        __m128 clj_SSE0[UNROLLJ], clj_SSE1[UNROLLJ], c6t_SSE[2], c12t_SSE[2];  \
 +        int    p;                                                              \
 +                                                                        \
 +        for (p = 0; p < UNROLLJ; p++)                                            \
 +        {                                                                   \
 +            /* Here we load 4 aligned floats, but we need just 2 */         \
 +            clj_SSE0[p] = _mm_load_ps(nbfp0+type[aj+p]*NBFP_STRIDE);        \
 +        }                                                                   \
 +        for (p = 0; p < UNROLLJ; p++)                                            \
 +        {                                                                   \
 +            /* Here we load 4 aligned floats, but we need just 2 */         \
 +            clj_SSE1[p] = _mm_load_ps(nbfp1+type[aj+p]*NBFP_STRIDE);        \
 +        }                                                                   \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(clj_SSE0[0], clj_SSE0[1], clj_SSE0[2], clj_SSE0[3], c6t_SSE[0], c12t_SSE[0]); \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(clj_SSE1[0], clj_SSE1[1], clj_SSE1[2], clj_SSE1[3], c6t_SSE[1], c12t_SSE[1]); \
 +                                                                        \
 +        GMX_2_MM_TO_M256(c6t_SSE[0], c6t_SSE[1], c6_SSE);                     \
 +        GMX_2_MM_TO_M256(c12t_SSE[0], c12t_SSE[1], c12_SSE);                  \
 +    }
 +
 +#endif
 +
- #if defined GMX_MM256_HERE && defined GMX_DOUBLE
++#if GMX_NBNXN_SIMD_BITWIDTH == 128 && defined GMX_DOUBLE
 +
 +#define load_lj_pair_params(nbfp, type, aj, c6_SSE, c12_SSE)                \
 +    {                                                                       \
 +        gmx_mm_pr clj_SSE[UNROLLJ];                                         \
 +        int       p;                                                              \
 +                                                                        \
 +        for (p = 0; p < UNROLLJ; p++)                                            \
 +        {                                                                   \
 +            clj_SSE[p] = gmx_load_pr(nbfp+type[aj+p]*NBFP_STRIDE);          \
 +        }                                                                   \
 +        GMX_MM_TRANSPOSE2_OP_PD(clj_SSE[0], clj_SSE[1], c6_SSE, c12_SSE);      \
 +    }
 +
 +#endif
 +
- #if defined GMX_MM128_HERE && !defined GMX_DOUBLE
++#if GMX_NBNXN_SIMD_BITWIDTH == 256 && defined GMX_DOUBLE
 +
 +#define load_lj_pair_params(nbfp, type, aj, c6_SSE, c12_SSE)                \
 +    {                                                                       \
 +        __m128d clj_SSE[UNROLLJ], c6t_SSE[2], c12t_SSE[2];                    \
 +        int     p;                                                              \
 +                                                                        \
 +        for (p = 0; p < UNROLLJ; p++)                                            \
 +        {                                                                   \
 +            clj_SSE[p] = _mm_load_pd(nbfp+type[aj+p]*NBFP_STRIDE);          \
 +        }                                                                   \
 +        GMX_MM_TRANSPOSE2_OP_PD(clj_SSE[0], clj_SSE[1], c6t_SSE[0], c12t_SSE[0]); \
 +        GMX_MM_TRANSPOSE2_OP_PD(clj_SSE[2], clj_SSE[3], c6t_SSE[1], c12t_SSE[1]); \
 +        GMX_2_M128D_TO_M256D(c6t_SSE[0], c6t_SSE[1], c6_SSE);                 \
 +        GMX_2_M128D_TO_M256D(c12t_SSE[0], c12t_SSE[1], c12_SSE);              \
 +    }
 +
 +#endif
 +
 +
 +/* The load_table functions below are performance critical.
 + * The routines issue UNROLLI*UNROLLJ _mm_load_ps calls.
 + * As these all have latencies, scheduling is crucial.
 + * The Intel compilers and CPUs seem to do a good job at this.
 + * But AMD CPUs perform significantly worse with gcc than with icc.
 + * Performance is improved a bit by using the extract function UNROLLJ times,
 + * instead of doing an _mm_store_si128 for every i-particle.
 + * This is only faster when we use FDV0 formatted tables, where we also need
 + * to multiple the index by 4, which can be done by a SIMD bit shift.
 + * With single precision AVX, 8 extracts are much slower than 1 store.
 + * Because of this, the load_table_f macro always takes the ti parameter,
 + * but it is only used with AVX.
 + */
 +
- #if defined GMX_MM256_HERE && !defined GMX_DOUBLE
++#if GMX_NBNXN_SIMD_BITWIDTH == 128 && !defined GMX_DOUBLE
 +
 +#define load_table_f(tab_coul_FDV0, ti_SSE, ti, ctab0_SSE, ctab1_SSE)   \
 +    {                                                                       \
 +        int    idx[4];                                                      \
 +        __m128 ctab_SSE[4];                                                 \
 +                                                                        \
 +        /* Table has 4 entries, left-shift index by 2 */                    \
 +        ti_SSE = _mm_slli_epi32(ti_SSE, 2);                                  \
 +        /* Without SSE4.1 the extract macro needs an immediate: unroll */   \
 +        idx[0]      = gmx_mm_extract_epi32(ti_SSE, 0);                            \
 +        ctab_SSE[0] = _mm_load_ps(tab_coul_FDV0+idx[0]);                    \
 +        idx[1]      = gmx_mm_extract_epi32(ti_SSE, 1);                            \
 +        ctab_SSE[1] = _mm_load_ps(tab_coul_FDV0+idx[1]);                    \
 +        idx[2]      = gmx_mm_extract_epi32(ti_SSE, 2);                            \
 +        ctab_SSE[2] = _mm_load_ps(tab_coul_FDV0+idx[2]);                    \
 +        idx[3]      = gmx_mm_extract_epi32(ti_SSE, 3);                            \
 +        ctab_SSE[3] = _mm_load_ps(tab_coul_FDV0+idx[3]);                    \
 +                                                                        \
 +        /* Shuffle the force table entries to a convenient order */         \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(ctab_SSE[0], ctab_SSE[1], ctab_SSE[2], ctab_SSE[3], ctab0_SSE, ctab1_SSE); \
 +    }
 +
 +#define load_table_f_v(tab_coul_FDV0, ti_SSE, ti, ctab0_SSE, ctab1_SSE, ctabv_SSE) \
 +    {                                                                       \
 +        int    idx[4];                                                      \
 +        __m128 ctab_SSE[4];                                                 \
 +                                                                        \
 +        /* Table has 4 entries, left-shift index by 2 */                    \
 +        ti_SSE = _mm_slli_epi32(ti_SSE, 2);                                  \
 +        /* Without SSE4.1 the extract macro needs an immediate: unroll */   \
 +        idx[0]      = gmx_mm_extract_epi32(ti_SSE, 0);                            \
 +        ctab_SSE[0] = _mm_load_ps(tab_coul_FDV0+idx[0]);                    \
 +        idx[1]      = gmx_mm_extract_epi32(ti_SSE, 1);                            \
 +        ctab_SSE[1] = _mm_load_ps(tab_coul_FDV0+idx[1]);                    \
 +        idx[2]      = gmx_mm_extract_epi32(ti_SSE, 2);                            \
 +        ctab_SSE[2] = _mm_load_ps(tab_coul_FDV0+idx[2]);                    \
 +        idx[3]      = gmx_mm_extract_epi32(ti_SSE, 3);                            \
 +        ctab_SSE[3] = _mm_load_ps(tab_coul_FDV0+idx[3]);                    \
 +                                                                        \
 +        /* Shuffle the force  table entries to a convenient order */        \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(ctab_SSE[0], ctab_SSE[1], ctab_SSE[2], ctab_SSE[3], ctab0_SSE, ctab1_SSE); \
 +        /* Shuffle the energy table entries to a convenient order */        \
 +        GMX_MM_SHUFFLE_4_PS_FIL2_TO_1_PS(ctab_SSE[0], ctab_SSE[1], ctab_SSE[2], ctab_SSE[3], ctabv_SSE); \
 +    }
 +
 +#endif
 +
- #if defined GMX_MM128_HERE && defined GMX_DOUBLE
++#if GMX_NBNXN_SIMD_BITWIDTH == 256 && !defined GMX_DOUBLE
 +
 +#define load_table_f(tab_coul_FDV0, ti_SSE, ti, ctab0_SSE, ctab1_SSE)   \
 +    {                                                                       \
 +        __m128 ctab_SSE[8], ctabt_SSE[4];                                    \
 +        int    j;                                                           \
 +                                                                        \
 +        /* Bit shifting would be faster, but AVX doesn't support that */    \
 +        _mm256_store_si256((__m256i *)ti, ti_SSE);                           \
 +        for (j = 0; j < 8; j++)                                                  \
 +        {                                                                   \
 +            ctab_SSE[j] = _mm_load_ps(tab_coul_FDV0+ti[j]*4);               \
 +        }                                                                   \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(ctab_SSE[0], ctab_SSE[1], ctab_SSE[2], ctab_SSE[3], ctabt_SSE[0], ctabt_SSE[2]); \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(ctab_SSE[4], ctab_SSE[5], ctab_SSE[6], ctab_SSE[7], ctabt_SSE[1], ctabt_SSE[3]); \
 +                                                                        \
 +        GMX_2_MM_TO_M256(ctabt_SSE[0], ctabt_SSE[1], ctab0_SSE);              \
 +        GMX_2_MM_TO_M256(ctabt_SSE[2], ctabt_SSE[3], ctab1_SSE);              \
 +    }
 +
 +#define load_table_f_v(tab_coul_FDV0, ti_SSE, ti, ctab0_SSE, ctab1_SSE, ctabv_SSE) \
 +    {                                                                       \
 +        __m128 ctab_SSE[8], ctabt_SSE[4], ctabvt_SSE[2];                      \
 +        int    j;                                                           \
 +                                                                        \
 +        /* Bit shifting would be faster, but AVX doesn't support that */    \
 +        _mm256_store_si256((__m256i *)ti, ti_SSE);                           \
 +        for (j = 0; j < 8; j++)                                                  \
 +        {                                                                   \
 +            ctab_SSE[j] = _mm_load_ps(tab_coul_FDV0+ti[j]*4);               \
 +        }                                                                   \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(ctab_SSE[0], ctab_SSE[1], ctab_SSE[2], ctab_SSE[3], ctabt_SSE[0], ctabt_SSE[2]); \
 +        GMX_MM_SHUFFLE_4_PS_FIL01_TO_2_PS(ctab_SSE[4], ctab_SSE[5], ctab_SSE[6], ctab_SSE[7], ctabt_SSE[1], ctabt_SSE[3]); \
 +                                                                        \
 +        GMX_2_MM_TO_M256(ctabt_SSE[0], ctabt_SSE[1], ctab0_SSE);              \
 +        GMX_2_MM_TO_M256(ctabt_SSE[2], ctabt_SSE[3], ctab1_SSE);              \
 +                                                                        \
 +        GMX_MM_SHUFFLE_4_PS_FIL2_TO_1_PS(ctab_SSE[0], ctab_SSE[1], ctab_SSE[2], ctab_SSE[3], ctabvt_SSE[0]); \
 +        GMX_MM_SHUFFLE_4_PS_FIL2_TO_1_PS(ctab_SSE[4], ctab_SSE[5], ctab_SSE[6], ctab_SSE[7], ctabvt_SSE[1]); \
 +                                                                        \
 +        GMX_2_MM_TO_M256(ctabvt_SSE[0], ctabvt_SSE[1], ctabv_SSE);            \
 +    }
 +
 +#endif
 +
- #if defined GMX_MM256_HERE && defined GMX_DOUBLE
++#if GMX_NBNXN_SIMD_BITWIDTH == 128 && defined GMX_DOUBLE
 +
 +#define load_table_f(tab_coul_F, ti_SSE, ti, ctab0_SSE, ctab1_SSE)      \
 +    {                                                                       \
 +        int     idx[2];                                                     \
 +        __m128d ctab_SSE[2];                                                \
 +                                                                        \
 +        /* Without SSE4.1 the extract macro needs an immediate: unroll */   \
 +        idx[0]      = gmx_mm_extract_epi32(ti_SSE, 0);                            \
 +        ctab_SSE[0] = _mm_loadu_pd(tab_coul_F+idx[0]);                      \
 +        idx[1]      = gmx_mm_extract_epi32(ti_SSE, 1);                            \
 +        ctab_SSE[1] = _mm_loadu_pd(tab_coul_F+idx[1]);                      \
 +                                                                        \
 +        /* Shuffle the force table entries to a convenient order */         \
 +        GMX_MM_TRANSPOSE2_OP_PD(ctab_SSE[0], ctab_SSE[1], ctab0_SSE, ctab1_SSE); \
 +        /* The second force table entry should contain the difference */    \
 +        ctab1_SSE = _mm_sub_pd(ctab1_SSE, ctab0_SSE);                        \
 +    }
 +
 +#define load_table_f_v(tab_coul_F, tab_coul_V, ti_SSE, ti, ctab0_SSE, ctab1_SSE, ctabv_SSE) \
 +    {                                                                       \
 +        int     idx[2];                                                     \
 +        __m128d ctab_SSE[4];                                                \
 +                                                                        \
 +        /* Without SSE4.1 the extract macro needs an immediate: unroll */   \
 +        idx[0]      = gmx_mm_extract_epi32(ti_SSE, 0);                            \
 +        ctab_SSE[0] = _mm_loadu_pd(tab_coul_F+idx[0]);                      \
 +        idx[1]      = gmx_mm_extract_epi32(ti_SSE, 1);                            \
 +        ctab_SSE[1] = _mm_loadu_pd(tab_coul_F+idx[1]);                      \
 +                                                                        \
 +        /* Shuffle the force table entries to a convenient order */         \
 +        GMX_MM_TRANSPOSE2_OP_PD(ctab_SSE[0], ctab_SSE[1], ctab0_SSE, ctab1_SSE); \
 +        /* The second force table entry should contain the difference */    \
 +        ctab1_SSE = _mm_sub_pd(ctab1_SSE, ctab0_SSE);                        \
 +                                                                        \
 +        ctab_SSE[2] = _mm_loadu_pd(tab_coul_V+idx[0]);                      \
 +        ctab_SSE[3] = _mm_loadu_pd(tab_coul_V+idx[1]);                      \
 +                                                                        \
 +        /* Shuffle the energy table entries to a single register */         \
 +        ctabv_SSE = _mm_shuffle_pd(ctab_SSE[2], ctab_SSE[3], _MM_SHUFFLE2(0, 0)); \
 +    }
 +
 +#endif
 +
- #if defined GMX_X86_AVX_256 && GMX_SIMD_WIDTH_HERE == 8
++#if GMX_NBNXN_SIMD_BITWIDTH == 256 && defined GMX_DOUBLE
 +
 +/* Put two 128-bit 2-double registers into one 256-bit 4-ouble register */
 +#define GMX_2_M128D_TO_M256D(in0, in1, out)                               \
 +    {                                                                       \
 +        out = _mm256_insertf128_pd(_mm256_castpd128_pd256(in0), in1, 1);      \
 +    }
 +
 +#define load_table_f(tab_coul_F, ti_SSE, ti, ctab0_SSE, ctab1_SSE)      \
 +    {                                                                       \
 +        __m128d ctab_SSE[4], tr_SSE[4];                                      \
 +        int     j;                                                          \
 +                                                                        \
 +        _mm_store_si128((__m128i *)ti, ti_SSE);                              \
 +        for (j = 0; j < 4; j++)                                                  \
 +        {                                                                   \
 +            ctab_SSE[j] = _mm_loadu_pd(tab_coul_F+ti[j]);                   \
 +        }                                                                   \
 +        /* Shuffle the force table entries to a convenient order */         \
 +        GMX_MM_TRANSPOSE2_OP_PD(ctab_SSE[0], ctab_SSE[1], tr_SSE[0], tr_SSE[1]); \
 +        GMX_MM_TRANSPOSE2_OP_PD(ctab_SSE[2], ctab_SSE[3], tr_SSE[2], tr_SSE[3]); \
 +        GMX_2_M128D_TO_M256D(tr_SSE[0], tr_SSE[2], ctab0_SSE);                \
 +        GMX_2_M128D_TO_M256D(tr_SSE[1], tr_SSE[3], ctab1_SSE);                \
 +        /* The second force table entry should contain the difference */    \
 +        ctab1_SSE = _mm256_sub_pd(ctab1_SSE, ctab0_SSE);                     \
 +    }
 +
 +#define load_table_f_v(tab_coul_F, tab_coul_V, ti_SSE, ti, ctab0_SSE, ctab1_SSE, ctabv_SSE) \
 +    {                                                                       \
 +        __m128d ctab_SSE[8], tr_SSE[4];                                      \
 +        int     j;                                                          \
 +                                                                        \
 +        _mm_store_si128((__m128i *)ti, ti_SSE);                              \
 +        for (j = 0; j < 4; j++)                                                  \
 +        {                                                                   \
 +            ctab_SSE[j] = _mm_loadu_pd(tab_coul_F+ti[j]);                   \
 +        }                                                                   \
 +        /* Shuffle the force table entries to a convenient order */         \
 +        GMX_MM_TRANSPOSE2_OP_PD(ctab_SSE[0], ctab_SSE[1], tr_SSE[0], tr_SSE[1]); \
 +        GMX_MM_TRANSPOSE2_OP_PD(ctab_SSE[2], ctab_SSE[3], tr_SSE[2], tr_SSE[3]); \
 +        GMX_2_M128D_TO_M256D(tr_SSE[0], tr_SSE[2], ctab0_SSE);                \
 +        GMX_2_M128D_TO_M256D(tr_SSE[1], tr_SSE[3], ctab1_SSE);                \
 +        /* The second force table entry should contain the difference */    \
 +        ctab1_SSE = _mm256_sub_pd(ctab1_SSE, ctab0_SSE);                     \
 +                                                                        \
 +        for (j = 0; j < 4; j++)                                                  \
 +        {                                                                   \
 +            ctab_SSE[4+j] = _mm_loadu_pd(tab_coul_V+ti[j]);                 \
 +        }                                                                   \
 +        /* Shuffle the energy table entries to a single register */         \
 +        GMX_2_M128D_TO_M256D(_mm_shuffle_pd(ctab_SSE[4], ctab_SSE[5], _MM_SHUFFLE2(0, 0)), _mm_shuffle_pd(ctab_SSE[6], ctab_SSE[7], _MM_SHUFFLE2(0, 0)), ctabv_SSE); \
 +    }
 +
 +#endif
 +
 +
 +/* Add energy register to possibly multiple terms in the energy array.
 + * This function is the same for SSE/AVX single/double.
 + */
 +static inline void add_ener_grp(gmx_mm_pr e_SSE, real *v, const int *offset_jj)
 +{
 +    int jj;
 +
 +    /* We need to balance the number of store operations with
 +     * the rapidly increases number of combinations of energy groups.
 +     * We add to a temporary buffer for 1 i-group vs 2 j-groups.
 +     */
 +    for (jj = 0; jj < (UNROLLJ/2); jj++)
 +    {
 +        gmx_mm_pr v_SSE;
 +
 +        v_SSE = gmx_load_pr(v+offset_jj[jj]+jj*GMX_SIMD_WIDTH_HERE);
 +        gmx_store_pr(v+offset_jj[jj]+jj*GMX_SIMD_WIDTH_HERE, gmx_add_pr(v_SSE, e_SSE));
 +    }
 +}
 +
-         v_SSE = gmx_load_hpr(v0+offset_jj[jj]+jj*GMX_SIMD_WIDTH_HERE/2);
++#if defined GMX_X86_AVX_256 && GMX_SIMD_WIDTH_HERE == 8 && defined gmx_mm_hpr
 +/* As add_ener_grp above, but for two groups of UNROLLJ/2 stored in
 + * a single SIMD register.
 + */
 +static inline void add_ener_grp_halves(gmx_mm_pr e_SSE,
 +                                       real *v0, real *v1, const int *offset_jj)
 +{
 +    gmx_mm_hpr e_SSE0, e_SSE1;
 +    int        jj;
 +
 +    e_SSE0 = _mm256_extractf128_ps(e_SSE, 0);
 +    e_SSE1 = _mm256_extractf128_ps(e_SSE, 1);
 +
 +    for (jj = 0; jj < (UNROLLJ/2); jj++)
 +    {
 +        gmx_mm_hpr v_SSE;
 +
-         v_SSE = gmx_load_hpr(v1+offset_jj[jj]+jj*GMX_SIMD_WIDTH_HERE/2);
++        gmx_load_hpr(v_SSE, v0+offset_jj[jj]+jj*GMX_SIMD_WIDTH_HERE/2);
 +        gmx_store_hpr(v0+offset_jj[jj]+jj*GMX_SIMD_WIDTH_HERE/2, gmx_add_hpr(v_SSE, e_SSE0));
 +    }
 +    for (jj = 0; jj < (UNROLLJ/2); jj++)
 +    {
 +        gmx_mm_hpr v_SSE;
 +
++        gmx_load_hpr(v_SSE, v1+offset_jj[jj]+jj*GMX_SIMD_WIDTH_HERE/2);
 +        gmx_store_hpr(v1+offset_jj[jj]+jj*GMX_SIMD_WIDTH_HERE/2, gmx_add_hpr(v_SSE, e_SSE1));
 +    }
 +}
 +#endif
 +
 +#endif /* GMX_X86_SSE2 */
 +
 +#endif /* _nbnxn_kernel_sse_utils_h_ */
index fb4aaf8da208c6591e70e6fd2691549ce670dd22,0000000000000000000000000000000000000000..2987fb117a4e26a948b5e1f8a11eb6daf974608c
mode 100644,000000..100644
--- /dev/null
@@@ -1,5147 -1,0 +1,5147 @@@
- static unsigned int get_imask_x86_simd128(gmx_bool rdiag, int ci, int cj)
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <math.h>
 +#include <string.h>
 +#include "sysstuff.h"
 +#include "smalloc.h"
 +#include "macros.h"
 +#include "maths.h"
 +#include "vec.h"
 +#include "pbc.h"
 +#include "nbnxn_consts.h"
 +#include "nbnxn_internal.h"
 +#include "nbnxn_atomdata.h"
 +#include "nbnxn_search.h"
 +#include "gmx_cyclecounter.h"
 +#include "gmxfio.h"
 +#include "gmx_omp_nthreads.h"
 +#include "nrnb.h"
 +
 +
 +/* Pair search box lower and upper corner in x,y,z.
 + * Store this in 4 iso 3 reals, which is useful with SSE.
 + * To avoid complicating the code we also use 4 without SSE.
 + */
 +#define NNBSBB_C         4
 +#define NNBSBB_B         (2*NNBSBB_C)
 +/* Pair search box lower and upper bound in z only. */
 +#define NNBSBB_D         2
 +/* Pair search box lower and upper corner x,y,z indices */
 +#define BBL_X  0
 +#define BBL_Y  1
 +#define BBL_Z  2
 +#define BBU_X  4
 +#define BBU_Y  5
 +#define BBU_Z  6
 +
 +
 +#ifdef NBNXN_SEARCH_BB_SSE
 +/* We use SSE or AVX-128bit for bounding box calculations */
 +
 +#ifndef GMX_DOUBLE
 +/* Single precision BBs + coordinates, we can also load coordinates using SSE */
 +#define NBNXN_SEARCH_SSE_SINGLE
 +#endif
 +
 +/* Include basic SSE2 stuff */
 +#include <emmintrin.h>
 +
 +#if defined NBNXN_SEARCH_SSE_SINGLE && (GPU_NSUBCELL == 4 || GPU_NSUBCELL == 8)
 +/* Store bounding boxes with x, y and z coordinates in packs of 4 */
 +#define NBNXN_PBB_SSE
 +#endif
 +
 +/* The width of SSE/AVX128 with single precision for bounding boxes with GPU.
 + * Here AVX-256 turns out to be slightly slower than AVX-128.
 + */
 +#define STRIDE_PBB        4
 +#define STRIDE_PBB_2LOG   2
 +
 +#endif /* NBNXN_SEARCH_BB_SSE */
 +
 +#ifdef GMX_NBNXN_SIMD
 +
 +/* The functions below are macros as they are performance sensitive */
 +
 +/* 4x4 list, pack=4: no complex conversion required */
 +/* i-cluster to j-cluster conversion */
 +#define CI_TO_CJ_J4(ci)   (ci)
 +/* cluster index to coordinate array index conversion */
 +#define X_IND_CI_J4(ci)  ((ci)*STRIDE_P4)
 +#define X_IND_CJ_J4(cj)  ((cj)*STRIDE_P4)
 +
 +/* 4x2 list, pack=4: j-cluster size is half the packing width */
 +/* i-cluster to j-cluster conversion */
 +#define CI_TO_CJ_J2(ci)  ((ci)<<1)
 +/* cluster index to coordinate array index conversion */
 +#define X_IND_CI_J2(ci)  ((ci)*STRIDE_P4)
 +#define X_IND_CJ_J2(cj)  (((cj)>>1)*STRIDE_P4 + ((cj) & 1)*(PACK_X4>>1))
 +
 +/* 4x8 list, pack=8: i-cluster size is half the packing width */
 +/* i-cluster to j-cluster conversion */
 +#define CI_TO_CJ_J8(ci)  ((ci)>>1)
 +/* cluster index to coordinate array index conversion */
 +#define X_IND_CI_J8(ci)  (((ci)>>1)*STRIDE_P8 + ((ci) & 1)*(PACK_X8>>1))
 +#define X_IND_CJ_J8(cj)  ((cj)*STRIDE_P8)
 +
 +/* The j-cluster size is matched to the SIMD width */
 +#if GMX_NBNXN_SIMD_BITWIDTH == 128
 +#ifdef GMX_DOUBLE
 +#define CI_TO_CJ_SIMD_4XN(ci)  CI_TO_CJ_J2(ci)
 +#define X_IND_CI_SIMD_4XN(ci)  X_IND_CI_J2(ci)
 +#define X_IND_CJ_SIMD_4XN(cj)  X_IND_CJ_J2(cj)
 +#else
 +#define CI_TO_CJ_SIMD_4XN(ci)  CI_TO_CJ_J4(ci)
 +#define X_IND_CI_SIMD_4XN(ci)  X_IND_CI_J4(ci)
 +#define X_IND_CJ_SIMD_4XN(cj)  X_IND_CJ_J4(cj)
 +#endif
 +#else
 +#if GMX_NBNXN_SIMD_BITWIDTH == 256
 +#ifdef GMX_DOUBLE
 +#define CI_TO_CJ_SIMD_4XN(ci)  CI_TO_CJ_J4(ci)
 +#define X_IND_CI_SIMD_4XN(ci)  X_IND_CI_J4(ci)
 +#define X_IND_CJ_SIMD_4XN(cj)  X_IND_CJ_J4(cj)
 +#else
 +#define CI_TO_CJ_SIMD_4XN(ci)  CI_TO_CJ_J8(ci)
 +#define X_IND_CI_SIMD_4XN(ci)  X_IND_CI_J8(ci)
 +#define X_IND_CJ_SIMD_4XN(cj)  X_IND_CJ_J8(cj)
 +/* Half SIMD with j-cluster size */
 +#define CI_TO_CJ_SIMD_2XNN(ci) CI_TO_CJ_J4(ci)
 +#define X_IND_CI_SIMD_2XNN(ci) X_IND_CI_J4(ci)
 +#define X_IND_CJ_SIMD_2XNN(cj) X_IND_CJ_J4(cj)
 +#endif
 +#else
 +#error "unsupported GMX_NBNXN_SIMD_WIDTH"
 +#endif
 +#endif
 +
 +#endif /* GMX_NBNXN_SIMD */
 +
 +
 +/* Interaction masks for 4xN atom interactions.
 + * Bit i*CJ_SIZE + j tells if atom i and j interact.
 + */
 +/* All interaction mask is the same for all kernels */
 +#define NBNXN_INT_MASK_ALL        0xffffffff
 +/* 4x4 kernel diagonal mask */
 +#define NBNXN_INT_MASK_DIAG       0x08ce
 +/* 4x2 kernel diagonal masks */
 +#define NBNXN_INT_MASK_DIAG_J2_0  0x0002
 +#define NBNXN_INT_MASK_DIAG_J2_1  0x002F
 +/* 4x8 kernel diagonal masks */
 +#define NBNXN_INT_MASK_DIAG_J8_0  0xf0f8fcfe
 +#define NBNXN_INT_MASK_DIAG_J8_1  0x0080c0e0
 +
 +
 +#ifdef NBNXN_SEARCH_BB_SSE
 +/* Store bounding boxes corners as quadruplets: xxxxyyyyzzzz */
 +#define NBNXN_BBXXXX
 +/* Size of bounding box corners quadruplet */
 +#define NNBSBB_XXXX      (NNBSBB_D*DIM*STRIDE_PBB)
 +#endif
 +
 +/* We shift the i-particles backward for PBC.
 + * This leads to more conditionals than shifting forward.
 + * We do this to get more balanced pair lists.
 + */
 +#define NBNXN_SHIFT_BACKWARD
 +
 +
 +/* This define is a lazy way to avoid interdependence of the grid
 + * and searching data structures.
 + */
 +#define NBNXN_NA_SC_MAX (GPU_NSUBCELL*NBNXN_GPU_CLUSTER_SIZE)
 +
 +
 +static void nbs_cycle_clear(nbnxn_cycle_t *cc)
 +{
 +    int i;
 +
 +    for (i = 0; i < enbsCCnr; i++)
 +    {
 +        cc[i].count = 0;
 +        cc[i].c     = 0;
 +    }
 +}
 +
 +static double Mcyc_av(const nbnxn_cycle_t *cc)
 +{
 +    return (double)cc->c*1e-6/cc->count;
 +}
 +
 +static void nbs_cycle_print(FILE *fp, const nbnxn_search_t nbs)
 +{
 +    int n;
 +    int t;
 +
 +    fprintf(fp, "\n");
 +    fprintf(fp, "ns %4d grid %4.1f search %4.1f red.f %5.3f",
 +            nbs->cc[enbsCCgrid].count,
 +            Mcyc_av(&nbs->cc[enbsCCgrid]),
 +            Mcyc_av(&nbs->cc[enbsCCsearch]),
 +            Mcyc_av(&nbs->cc[enbsCCreducef]));
 +
 +    if (nbs->nthread_max > 1)
 +    {
 +        if (nbs->cc[enbsCCcombine].count > 0)
 +        {
 +            fprintf(fp, " comb %5.2f",
 +                    Mcyc_av(&nbs->cc[enbsCCcombine]));
 +        }
 +        fprintf(fp, " s. th");
 +        for (t = 0; t < nbs->nthread_max; t++)
 +        {
 +            fprintf(fp, " %4.1f",
 +                    Mcyc_av(&nbs->work[t].cc[enbsCCsearch]));
 +        }
 +    }
 +    fprintf(fp, "\n");
 +}
 +
 +static void nbnxn_grid_init(nbnxn_grid_t * grid)
 +{
 +    grid->cxy_na      = NULL;
 +    grid->cxy_ind     = NULL;
 +    grid->cxy_nalloc  = 0;
 +    grid->bb          = NULL;
 +    grid->bbj         = NULL;
 +    grid->nc_nalloc   = 0;
 +}
 +
 +static int get_2log(int n)
 +{
 +    int log2;
 +
 +    log2 = 0;
 +    while ((1<<log2) < n)
 +    {
 +        log2++;
 +    }
 +    if ((1<<log2) != n)
 +    {
 +        gmx_fatal(FARGS, "nbnxn na_c (%d) is not a power of 2", n);
 +    }
 +
 +    return log2;
 +}
 +
 +static int nbnxn_kernel_to_ci_size(int nb_kernel_type)
 +{
 +    switch (nb_kernel_type)
 +    {
 +        case nbnxnk4x4_PlainC:
 +        case nbnxnk4xN_SIMD_4xN:
 +        case nbnxnk4xN_SIMD_2xNN:
 +            return NBNXN_CPU_CLUSTER_I_SIZE;
 +        case nbnxnk8x8x8_CUDA:
 +        case nbnxnk8x8x8_PlainC:
 +            /* The cluster size for super/sub lists is only set here.
 +             * Any value should work for the pair-search and atomdata code.
 +             * The kernels, of course, might require a particular value.
 +             */
 +            return NBNXN_GPU_CLUSTER_SIZE;
 +        default:
 +            gmx_incons("unknown kernel type");
 +    }
 +
 +    return 0;
 +}
 +
 +int nbnxn_kernel_to_cj_size(int nb_kernel_type)
 +{
 +    int nbnxn_simd_width = 0;
 +    int cj_size          = 0;
 +
 +#ifdef GMX_NBNXN_SIMD
 +    nbnxn_simd_width = GMX_NBNXN_SIMD_BITWIDTH/(sizeof(real)*8);
 +#endif
 +
 +    switch (nb_kernel_type)
 +    {
 +        case nbnxnk4x4_PlainC:
 +            cj_size = NBNXN_CPU_CLUSTER_I_SIZE;
 +            break;
 +        case nbnxnk4xN_SIMD_4xN:
 +            cj_size = nbnxn_simd_width;
 +            break;
 +        case nbnxnk4xN_SIMD_2xNN:
 +            cj_size = nbnxn_simd_width/2;
 +            break;
 +        case nbnxnk8x8x8_CUDA:
 +        case nbnxnk8x8x8_PlainC:
 +            cj_size = nbnxn_kernel_to_ci_size(nb_kernel_type);
 +            break;
 +        default:
 +            gmx_incons("unknown kernel type");
 +    }
 +
 +    return cj_size;
 +}
 +
 +static int ci_to_cj(int na_cj_2log, int ci)
 +{
 +    switch (na_cj_2log)
 +    {
 +        case 2: return ci;     break;
 +        case 1: return (ci<<1); break;
 +        case 3: return (ci>>1); break;
 +    }
 +
 +    return 0;
 +}
 +
 +gmx_bool nbnxn_kernel_pairlist_simple(int nb_kernel_type)
 +{
 +    if (nb_kernel_type == nbnxnkNotSet)
 +    {
 +        gmx_fatal(FARGS, "Non-bonded kernel type not set for Verlet-style pair-list.");
 +    }
 +
 +    switch (nb_kernel_type)
 +    {
 +        case nbnxnk8x8x8_CUDA:
 +        case nbnxnk8x8x8_PlainC:
 +            return FALSE;
 +
 +        case nbnxnk4x4_PlainC:
 +        case nbnxnk4xN_SIMD_4xN:
 +        case nbnxnk4xN_SIMD_2xNN:
 +            return TRUE;
 +
 +        default:
 +            gmx_incons("Invalid nonbonded kernel type passed!");
 +            return FALSE;
 +    }
 +}
 +
 +void nbnxn_init_search(nbnxn_search_t    * nbs_ptr,
 +                       ivec               *n_dd_cells,
 +                       gmx_domdec_zones_t *zones,
 +                       int                 nthread_max)
 +{
 +    nbnxn_search_t nbs;
 +    int            d, g, t;
 +
 +    snew(nbs, 1);
 +    *nbs_ptr = nbs;
 +
 +    nbs->DomDec = (n_dd_cells != NULL);
 +
 +    clear_ivec(nbs->dd_dim);
 +    nbs->ngrid = 1;
 +    if (nbs->DomDec)
 +    {
 +        nbs->zones = zones;
 +
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if ((*n_dd_cells)[d] > 1)
 +            {
 +                nbs->dd_dim[d] = 1;
 +                /* Each grid matches a DD zone */
 +                nbs->ngrid *= 2;
 +            }
 +        }
 +    }
 +
 +    snew(nbs->grid, nbs->ngrid);
 +    for (g = 0; g < nbs->ngrid; g++)
 +    {
 +        nbnxn_grid_init(&nbs->grid[g]);
 +    }
 +    nbs->cell        = NULL;
 +    nbs->cell_nalloc = 0;
 +    nbs->a           = NULL;
 +    nbs->a_nalloc    = 0;
 +
 +    nbs->nthread_max = nthread_max;
 +
 +    /* Initialize the work data structures for each thread */
 +    snew(nbs->work, nbs->nthread_max);
 +    for (t = 0; t < nbs->nthread_max; t++)
 +    {
 +        nbs->work[t].cxy_na           = NULL;
 +        nbs->work[t].cxy_na_nalloc    = 0;
 +        nbs->work[t].sort_work        = NULL;
 +        nbs->work[t].sort_work_nalloc = 0;
 +    }
 +
 +    /* Initialize detailed nbsearch cycle counting */
 +    nbs->print_cycles = (getenv("GMX_NBNXN_CYCLE") != 0);
 +    nbs->search_count = 0;
 +    nbs_cycle_clear(nbs->cc);
 +    for (t = 0; t < nbs->nthread_max; t++)
 +    {
 +        nbs_cycle_clear(nbs->work[t].cc);
 +    }
 +}
 +
 +static real grid_atom_density(int n, rvec corner0, rvec corner1)
 +{
 +    rvec size;
 +
 +    rvec_sub(corner1, corner0, size);
 +
 +    return n/(size[XX]*size[YY]*size[ZZ]);
 +}
 +
 +static int set_grid_size_xy(const nbnxn_search_t nbs,
 +                            nbnxn_grid_t *grid,
 +                            int dd_zone,
 +                            int n, rvec corner0, rvec corner1,
 +                            real atom_density,
 +                            int XFormat)
 +{
 +    rvec size;
 +    int  na_c;
 +    real adens, tlen, tlen_x, tlen_y, nc_max;
 +    int  t;
 +
 +    rvec_sub(corner1, corner0, size);
 +
 +    if (n > grid->na_sc)
 +    {
 +        /* target cell length */
 +        if (grid->bSimple)
 +        {
 +            /* To minimize the zero interactions, we should make
 +             * the largest of the i/j cell cubic.
 +             */
 +            na_c = max(grid->na_c, grid->na_cj);
 +
 +            /* Approximately cubic cells */
 +            tlen   = pow(na_c/atom_density, 1.0/3.0);
 +            tlen_x = tlen;
 +            tlen_y = tlen;
 +        }
 +        else
 +        {
 +            /* Approximately cubic sub cells */
 +            tlen   = pow(grid->na_c/atom_density, 1.0/3.0);
 +            tlen_x = tlen*GPU_NSUBCELL_X;
 +            tlen_y = tlen*GPU_NSUBCELL_Y;
 +        }
 +        /* We round ncx and ncy down, because we get less cell pairs
 +         * in the nbsist when the fixed cell dimensions (x,y) are
 +         * larger than the variable one (z) than the other way around.
 +         */
 +        grid->ncx = max(1, (int)(size[XX]/tlen_x));
 +        grid->ncy = max(1, (int)(size[YY]/tlen_y));
 +    }
 +    else
 +    {
 +        grid->ncx = 1;
 +        grid->ncy = 1;
 +    }
 +
 +    grid->sx     = size[XX]/grid->ncx;
 +    grid->sy     = size[YY]/grid->ncy;
 +    grid->inv_sx = 1/grid->sx;
 +    grid->inv_sy = 1/grid->sy;
 +
 +    if (dd_zone > 0)
 +    {
 +        /* This is a non-home zone, add an extra row of cells
 +         * for particles communicated for bonded interactions.
 +         * These can be beyond the cut-off. It doesn't matter where
 +         * they end up on the grid, but for performance it's better
 +         * if they don't end up in cells that can be within cut-off range.
 +         */
 +        grid->ncx++;
 +        grid->ncy++;
 +    }
 +
 +    /* We need one additional cell entry for particles moved by DD */
 +    if (grid->ncx*grid->ncy+1 > grid->cxy_nalloc)
 +    {
 +        grid->cxy_nalloc = over_alloc_large(grid->ncx*grid->ncy+1);
 +        srenew(grid->cxy_na, grid->cxy_nalloc);
 +        srenew(grid->cxy_ind, grid->cxy_nalloc+1);
 +    }
 +    for (t = 0; t < nbs->nthread_max; t++)
 +    {
 +        if (grid->ncx*grid->ncy+1 > nbs->work[t].cxy_na_nalloc)
 +        {
 +            nbs->work[t].cxy_na_nalloc = over_alloc_large(grid->ncx*grid->ncy+1);
 +            srenew(nbs->work[t].cxy_na, nbs->work[t].cxy_na_nalloc);
 +        }
 +    }
 +
 +    /* Worst case scenario of 1 atom in each last cell */
 +    if (grid->na_cj <= grid->na_c)
 +    {
 +        nc_max = n/grid->na_sc + grid->ncx*grid->ncy;
 +    }
 +    else
 +    {
 +        nc_max = n/grid->na_sc + grid->ncx*grid->ncy*grid->na_cj/grid->na_c;
 +    }
 +
 +    if (nc_max > grid->nc_nalloc)
 +    {
 +        int bb_nalloc;
 +
 +        grid->nc_nalloc = over_alloc_large(nc_max);
 +        srenew(grid->nsubc, grid->nc_nalloc);
 +        srenew(grid->bbcz, grid->nc_nalloc*NNBSBB_D);
 +#ifdef NBNXN_PBB_SSE
 +        bb_nalloc = grid->nc_nalloc*GPU_NSUBCELL/STRIDE_PBB*NNBSBB_XXXX;
 +#else
 +        bb_nalloc = grid->nc_nalloc*GPU_NSUBCELL*NNBSBB_B;
 +#endif
 +        sfree_aligned(grid->bb);
 +        /* This snew also zeros the contents, this avoid possible
 +         * floating exceptions in SSE with the unused bb elements.
 +         */
 +        snew_aligned(grid->bb, bb_nalloc, 16);
 +
 +        if (grid->bSimple)
 +        {
 +            if (grid->na_cj == grid->na_c)
 +            {
 +                grid->bbj = grid->bb;
 +            }
 +            else
 +            {
 +                sfree_aligned(grid->bbj);
 +                snew_aligned(grid->bbj, bb_nalloc*grid->na_c/grid->na_cj, 16);
 +            }
 +        }
 +
 +        srenew(grid->flags, grid->nc_nalloc);
 +    }
 +
 +    copy_rvec(corner0, grid->c0);
 +    copy_rvec(corner1, grid->c1);
 +
 +    return nc_max;
 +}
 +
 +/* We need to sort paricles in grid columns on z-coordinate.
 + * As particle are very often distributed homogeneously, we a sorting
 + * algorithm similar to pigeonhole sort. We multiply the z-coordinate
 + * by a factor, cast to an int and try to store in that hole. If the hole
 + * is full, we move this or another particle. A second pass is needed to make
 + * contiguous elements. SORT_GRID_OVERSIZE is the ratio of holes to particles.
 + * 4 is the optimal value for homogeneous particle distribution and allows
 + * for an O(#particles) sort up till distributions were all particles are
 + * concentrated in 1/4 of the space. No NlogN fallback is implemented,
 + * as it can be expensive to detect imhomogeneous particle distributions.
 + * SGSF is the maximum ratio of holes used, in the worst case all particles
 + * end up in the last hole and we need #particles extra holes at the end.
 + */
 +#define SORT_GRID_OVERSIZE 4
 +#define SGSF (SORT_GRID_OVERSIZE + 1)
 +
 +/* Sort particle index a on coordinates x along dim.
 + * Backwards tells if we want decreasing iso increasing coordinates.
 + * h0 is the minimum of the coordinate range.
 + * invh is the 1/length of the sorting range.
 + * n_per_h (>=n) is the expected average number of particles per 1/invh
 + * sort is the sorting work array.
 + * sort should have a size of at least n_per_h*SORT_GRID_OVERSIZE + n,
 + * or easier, allocate at least n*SGSF elements.
 + */
 +static void sort_atoms(int dim, gmx_bool Backwards,
 +                       int *a, int n, rvec *x,
 +                       real h0, real invh, int n_per_h,
 +                       int *sort)
 +{
 +    int nsort, i, c;
 +    int zi, zim, zi_min, zi_max;
 +    int cp, tmp;
 +
 +    if (n <= 1)
 +    {
 +        /* Nothing to do */
 +        return;
 +    }
 +
 +#ifndef NDEBUG
 +    if (n > n_per_h)
 +    {
 +        gmx_incons("n > n_per_h");
 +    }
 +#endif
 +
 +    /* Transform the inverse range height into the inverse hole height */
 +    invh *= n_per_h*SORT_GRID_OVERSIZE;
 +
 +    /* Set nsort to the maximum possible number of holes used.
 +     * In worst case all n elements end up in the last bin.
 +     */
 +    nsort = n_per_h*SORT_GRID_OVERSIZE + n;
 +
 +    /* Determine the index range used, so we can limit it for the second pass */
 +    zi_min = INT_MAX;
 +    zi_max = -1;
 +
 +    /* Sort the particles using a simple index sort */
 +    for (i = 0; i < n; i++)
 +    {
 +        /* The cast takes care of float-point rounding effects below zero.
 +         * This code assumes particles are less than 1/SORT_GRID_OVERSIZE
 +         * times the box height out of the box.
 +         */
 +        zi = (int)((x[a[i]][dim] - h0)*invh);
 +
 +#ifndef NDEBUG
 +        /* As we can have rounding effect, we use > iso >= here */
 +        if (zi < 0 || zi > n_per_h*SORT_GRID_OVERSIZE)
 +        {
 +            gmx_fatal(FARGS, "(int)((x[%d][%c]=%f - %f)*%f) = %d, not in 0 - %d*%d\n",
 +                      a[i], 'x'+dim, x[a[i]][dim], h0, invh, zi,
 +                      n_per_h, SORT_GRID_OVERSIZE);
 +        }
 +#endif
 +
 +        /* Ideally this particle should go in sort cell zi,
 +         * but that might already be in use,
 +         * in that case find the first empty cell higher up
 +         */
 +        if (sort[zi] < 0)
 +        {
 +            sort[zi] = a[i];
 +            zi_min   = min(zi_min, zi);
 +            zi_max   = max(zi_max, zi);
 +        }
 +        else
 +        {
 +            /* We have multiple atoms in the same sorting slot.
 +             * Sort on real z for minimal bounding box size.
 +             * There is an extra check for identical z to ensure
 +             * well-defined output order, independent of input order
 +             * to ensure binary reproducibility after restarts.
 +             */
 +            while (sort[zi] >= 0 && ( x[a[i]][dim] >  x[sort[zi]][dim] ||
 +                                      (x[a[i]][dim] == x[sort[zi]][dim] &&
 +                                       a[i] > sort[zi])))
 +            {
 +                zi++;
 +            }
 +
 +            if (sort[zi] >= 0)
 +            {
 +                /* Shift all elements by one slot until we find an empty slot */
 +                cp  = sort[zi];
 +                zim = zi + 1;
 +                while (sort[zim] >= 0)
 +                {
 +                    tmp       = sort[zim];
 +                    sort[zim] = cp;
 +                    cp        = tmp;
 +                    zim++;
 +                }
 +                sort[zim] = cp;
 +                zi_max    = max(zi_max, zim);
 +            }
 +            sort[zi] = a[i];
 +            zi_max   = max(zi_max, zi);
 +        }
 +    }
 +
 +    c = 0;
 +    if (!Backwards)
 +    {
 +        for (zi = 0; zi < nsort; zi++)
 +        {
 +            if (sort[zi] >= 0)
 +            {
 +                a[c++]   = sort[zi];
 +                sort[zi] = -1;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        for (zi = zi_max; zi >= zi_min; zi--)
 +        {
 +            if (sort[zi] >= 0)
 +            {
 +                a[c++]   = sort[zi];
 +                sort[zi] = -1;
 +            }
 +        }
 +    }
 +    if (c < n)
 +    {
 +        gmx_incons("Lost particles while sorting");
 +    }
 +}
 +
 +#ifdef GMX_DOUBLE
 +#define R2F_D(x) ((float)((x) >= 0 ? ((1-GMX_FLOAT_EPS)*(x)) : ((1+GMX_FLOAT_EPS)*(x))))
 +#define R2F_U(x) ((float)((x) >= 0 ? ((1+GMX_FLOAT_EPS)*(x)) : ((1-GMX_FLOAT_EPS)*(x))))
 +#else
 +#define R2F_D(x) (x)
 +#define R2F_U(x) (x)
 +#endif
 +
 +/* Coordinate order x,y,z, bb order xyz0 */
 +static void calc_bounding_box(int na, int stride, const real *x, float *bb)
 +{
 +    int  i, j;
 +    real xl, xh, yl, yh, zl, zh;
 +
 +    i  = 0;
 +    xl = x[i+XX];
 +    xh = x[i+XX];
 +    yl = x[i+YY];
 +    yh = x[i+YY];
 +    zl = x[i+ZZ];
 +    zh = x[i+ZZ];
 +    i += stride;
 +    for (j = 1; j < na; j++)
 +    {
 +        xl = min(xl, x[i+XX]);
 +        xh = max(xh, x[i+XX]);
 +        yl = min(yl, x[i+YY]);
 +        yh = max(yh, x[i+YY]);
 +        zl = min(zl, x[i+ZZ]);
 +        zh = max(zh, x[i+ZZ]);
 +        i += stride;
 +    }
 +    /* Note: possible double to float conversion here */
 +    bb[BBL_X] = R2F_D(xl);
 +    bb[BBL_Y] = R2F_D(yl);
 +    bb[BBL_Z] = R2F_D(zl);
 +    bb[BBU_X] = R2F_U(xh);
 +    bb[BBU_Y] = R2F_U(yh);
 +    bb[BBU_Z] = R2F_U(zh);
 +}
 +
 +/* Packed coordinates, bb order xyz0 */
 +static void calc_bounding_box_x_x4(int na, const real *x, float *bb)
 +{
 +    int  j;
 +    real xl, xh, yl, yh, zl, zh;
 +
 +    xl = x[XX*PACK_X4];
 +    xh = x[XX*PACK_X4];
 +    yl = x[YY*PACK_X4];
 +    yh = x[YY*PACK_X4];
 +    zl = x[ZZ*PACK_X4];
 +    zh = x[ZZ*PACK_X4];
 +    for (j = 1; j < na; j++)
 +    {
 +        xl = min(xl, x[j+XX*PACK_X4]);
 +        xh = max(xh, x[j+XX*PACK_X4]);
 +        yl = min(yl, x[j+YY*PACK_X4]);
 +        yh = max(yh, x[j+YY*PACK_X4]);
 +        zl = min(zl, x[j+ZZ*PACK_X4]);
 +        zh = max(zh, x[j+ZZ*PACK_X4]);
 +    }
 +    /* Note: possible double to float conversion here */
 +    bb[BBL_X] = R2F_D(xl);
 +    bb[BBL_Y] = R2F_D(yl);
 +    bb[BBL_Z] = R2F_D(zl);
 +    bb[BBU_X] = R2F_U(xh);
 +    bb[BBU_Y] = R2F_U(yh);
 +    bb[BBU_Z] = R2F_U(zh);
 +}
 +
 +/* Packed coordinates, bb order xyz0 */
 +static void calc_bounding_box_x_x8(int na, const real *x, float *bb)
 +{
 +    int  j;
 +    real xl, xh, yl, yh, zl, zh;
 +
 +    xl = x[XX*PACK_X8];
 +    xh = x[XX*PACK_X8];
 +    yl = x[YY*PACK_X8];
 +    yh = x[YY*PACK_X8];
 +    zl = x[ZZ*PACK_X8];
 +    zh = x[ZZ*PACK_X8];
 +    for (j = 1; j < na; j++)
 +    {
 +        xl = min(xl, x[j+XX*PACK_X8]);
 +        xh = max(xh, x[j+XX*PACK_X8]);
 +        yl = min(yl, x[j+YY*PACK_X8]);
 +        yh = max(yh, x[j+YY*PACK_X8]);
 +        zl = min(zl, x[j+ZZ*PACK_X8]);
 +        zh = max(zh, x[j+ZZ*PACK_X8]);
 +    }
 +    /* Note: possible double to float conversion here */
 +    bb[BBL_X] = R2F_D(xl);
 +    bb[BBL_Y] = R2F_D(yl);
 +    bb[BBL_Z] = R2F_D(zl);
 +    bb[BBU_X] = R2F_U(xh);
 +    bb[BBU_Y] = R2F_U(yh);
 +    bb[BBU_Z] = R2F_U(zh);
 +}
 +
 +#ifdef NBNXN_SEARCH_BB_SSE
 +
 +/* Packed coordinates, bb order xyz0 */
 +static void calc_bounding_box_x_x4_halves(int na, const real *x,
 +                                          float *bb, float *bbj)
 +{
 +    calc_bounding_box_x_x4(min(na, 2), x, bbj);
 +
 +    if (na > 2)
 +    {
 +        calc_bounding_box_x_x4(min(na-2, 2), x+(PACK_X4>>1), bbj+NNBSBB_B);
 +    }
 +    else
 +    {
 +        /* Set the "empty" bounding box to the same as the first one,
 +         * so we don't need to treat special cases in the rest of the code.
 +         */
 +        _mm_store_ps(bbj+NNBSBB_B, _mm_load_ps(bbj));
 +        _mm_store_ps(bbj+NNBSBB_B+NNBSBB_C, _mm_load_ps(bbj+NNBSBB_C));
 +    }
 +
 +    _mm_store_ps(bb, _mm_min_ps(_mm_load_ps(bbj),
 +                                _mm_load_ps(bbj+NNBSBB_B)));
 +    _mm_store_ps(bb+NNBSBB_C, _mm_max_ps(_mm_load_ps(bbj+NNBSBB_C),
 +                                         _mm_load_ps(bbj+NNBSBB_B+NNBSBB_C)));
 +}
 +
 +/* Coordinate order xyz, bb order xxxxyyyyzzzz */
 +static void calc_bounding_box_xxxx(int na, int stride, const real *x, float *bb)
 +{
 +    int  i, j;
 +    real xl, xh, yl, yh, zl, zh;
 +
 +    i  = 0;
 +    xl = x[i+XX];
 +    xh = x[i+XX];
 +    yl = x[i+YY];
 +    yh = x[i+YY];
 +    zl = x[i+ZZ];
 +    zh = x[i+ZZ];
 +    i += stride;
 +    for (j = 1; j < na; j++)
 +    {
 +        xl = min(xl, x[i+XX]);
 +        xh = max(xh, x[i+XX]);
 +        yl = min(yl, x[i+YY]);
 +        yh = max(yh, x[i+YY]);
 +        zl = min(zl, x[i+ZZ]);
 +        zh = max(zh, x[i+ZZ]);
 +        i += stride;
 +    }
 +    /* Note: possible double to float conversion here */
 +    bb[0*STRIDE_PBB] = R2F_D(xl);
 +    bb[1*STRIDE_PBB] = R2F_D(yl);
 +    bb[2*STRIDE_PBB] = R2F_D(zl);
 +    bb[3*STRIDE_PBB] = R2F_U(xh);
 +    bb[4*STRIDE_PBB] = R2F_U(yh);
 +    bb[5*STRIDE_PBB] = R2F_U(zh);
 +}
 +
 +#endif /* NBNXN_SEARCH_BB_SSE */
 +
 +#ifdef NBNXN_SEARCH_SSE_SINGLE
 +
 +/* Coordinate order xyz?, bb order xyz0 */
 +static void calc_bounding_box_sse(int na, const float *x, float *bb)
 +{
 +    __m128 bb_0_SSE, bb_1_SSE;
 +    __m128 x_SSE;
 +
 +    int    i;
 +
 +    bb_0_SSE = _mm_load_ps(x);
 +    bb_1_SSE = bb_0_SSE;
 +
 +    for (i = 1; i < na; i++)
 +    {
 +        x_SSE    = _mm_load_ps(x+i*NNBSBB_C);
 +        bb_0_SSE = _mm_min_ps(bb_0_SSE, x_SSE);
 +        bb_1_SSE = _mm_max_ps(bb_1_SSE, x_SSE);
 +    }
 +
 +    _mm_store_ps(bb, bb_0_SSE);
 +    _mm_store_ps(bb+4, bb_1_SSE);
 +}
 +
 +/* Coordinate order xyz?, bb order xxxxyyyyzzzz */
 +static void calc_bounding_box_xxxx_sse(int na, const float *x,
 +                                       float *bb_work,
 +                                       real *bb)
 +{
 +    calc_bounding_box_sse(na, x, bb_work);
 +
 +    bb[0*STRIDE_PBB] = bb_work[BBL_X];
 +    bb[1*STRIDE_PBB] = bb_work[BBL_Y];
 +    bb[2*STRIDE_PBB] = bb_work[BBL_Z];
 +    bb[3*STRIDE_PBB] = bb_work[BBU_X];
 +    bb[4*STRIDE_PBB] = bb_work[BBU_Y];
 +    bb[5*STRIDE_PBB] = bb_work[BBU_Z];
 +}
 +
 +#endif /* NBNXN_SEARCH_SSE_SINGLE */
 +
 +#ifdef NBNXN_SEARCH_BB_SSE
 +
 +/* Combines pairs of consecutive bounding boxes */
 +static void combine_bounding_box_pairs(nbnxn_grid_t *grid, const float *bb)
 +{
 +    int    i, j, sc2, nc2, c2;
 +    __m128 min_SSE, max_SSE;
 +
 +    for (i = 0; i < grid->ncx*grid->ncy; i++)
 +    {
 +        /* Starting bb in a column is expected to be 2-aligned */
 +        sc2 = grid->cxy_ind[i]>>1;
 +        /* For odd numbers skip the last bb here */
 +        nc2 = (grid->cxy_na[i]+3)>>(2+1);
 +        for (c2 = sc2; c2 < sc2+nc2; c2++)
 +        {
 +            min_SSE = _mm_min_ps(_mm_load_ps(bb+(c2*4+0)*NNBSBB_C),
 +                                 _mm_load_ps(bb+(c2*4+2)*NNBSBB_C));
 +            max_SSE = _mm_max_ps(_mm_load_ps(bb+(c2*4+1)*NNBSBB_C),
 +                                 _mm_load_ps(bb+(c2*4+3)*NNBSBB_C));
 +            _mm_store_ps(grid->bbj+(c2*2+0)*NNBSBB_C, min_SSE);
 +            _mm_store_ps(grid->bbj+(c2*2+1)*NNBSBB_C, max_SSE);
 +        }
 +        if (((grid->cxy_na[i]+3)>>2) & 1)
 +        {
 +            /* Copy the last bb for odd bb count in this column */
 +            for (j = 0; j < NNBSBB_C; j++)
 +            {
 +                grid->bbj[(c2*2+0)*NNBSBB_C+j] = bb[(c2*4+0)*NNBSBB_C+j];
 +                grid->bbj[(c2*2+1)*NNBSBB_C+j] = bb[(c2*4+1)*NNBSBB_C+j];
 +            }
 +        }
 +    }
 +}
 +
 +#endif
 +
 +
 +/* Prints the average bb size, used for debug output */
 +static void print_bbsizes_simple(FILE                *fp,
 +                                 const nbnxn_search_t nbs,
 +                                 const nbnxn_grid_t  *grid)
 +{
 +    int  c, d;
 +    dvec ba;
 +
 +    clear_dvec(ba);
 +    for (c = 0; c < grid->nc; c++)
 +    {
 +        for (d = 0; d < DIM; d++)
 +        {
 +            ba[d] += grid->bb[c*NNBSBB_B+NNBSBB_C+d] - grid->bb[c*NNBSBB_B+d];
 +        }
 +    }
 +    dsvmul(1.0/grid->nc, ba, ba);
 +
 +    fprintf(fp, "ns bb: %4.2f %4.2f %4.2f  %4.2f %4.2f %4.2f rel %4.2f %4.2f %4.2f\n",
 +            nbs->box[XX][XX]/grid->ncx,
 +            nbs->box[YY][YY]/grid->ncy,
 +            nbs->box[ZZ][ZZ]*grid->ncx*grid->ncy/grid->nc,
 +            ba[XX], ba[YY], ba[ZZ],
 +            ba[XX]*grid->ncx/nbs->box[XX][XX],
 +            ba[YY]*grid->ncy/nbs->box[YY][YY],
 +            ba[ZZ]*grid->nc/(grid->ncx*grid->ncy*nbs->box[ZZ][ZZ]));
 +}
 +
 +/* Prints the average bb size, used for debug output */
 +static void print_bbsizes_supersub(FILE                *fp,
 +                                   const nbnxn_search_t nbs,
 +                                   const nbnxn_grid_t  *grid)
 +{
 +    int  ns, c, s;
 +    dvec ba;
 +
 +    clear_dvec(ba);
 +    ns = 0;
 +    for (c = 0; c < grid->nc; c++)
 +    {
 +#ifdef NBNXN_BBXXXX
 +        for (s = 0; s < grid->nsubc[c]; s += STRIDE_PBB)
 +        {
 +            int cs_w, i, d;
 +
 +            cs_w = (c*GPU_NSUBCELL + s)/STRIDE_PBB;
 +            for (i = 0; i < STRIDE_PBB; i++)
 +            {
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    ba[d] +=
 +                        grid->bb[cs_w*NNBSBB_XXXX+(DIM+d)*STRIDE_PBB+i] -
 +                        grid->bb[cs_w*NNBSBB_XXXX+     d *STRIDE_PBB+i];
 +                }
 +            }
 +        }
 +#else
 +        for (s = 0; s < grid->nsubc[c]; s++)
 +        {
 +            int cs, d;
 +
 +            cs = c*GPU_NSUBCELL + s;
 +            for (d = 0; d < DIM; d++)
 +            {
 +                ba[d] +=
 +                    grid->bb[cs*NNBSBB_B+NNBSBB_C+d] -
 +                    grid->bb[cs*NNBSBB_B         +d];
 +            }
 +        }
 +#endif
 +        ns += grid->nsubc[c];
 +    }
 +    dsvmul(1.0/ns, ba, ba);
 +
 +    fprintf(fp, "ns bb: %4.2f %4.2f %4.2f  %4.2f %4.2f %4.2f rel %4.2f %4.2f %4.2f\n",
 +            nbs->box[XX][XX]/(grid->ncx*GPU_NSUBCELL_X),
 +            nbs->box[YY][YY]/(grid->ncy*GPU_NSUBCELL_Y),
 +            nbs->box[ZZ][ZZ]*grid->ncx*grid->ncy/(grid->nc*GPU_NSUBCELL_Z),
 +            ba[XX], ba[YY], ba[ZZ],
 +            ba[XX]*grid->ncx*GPU_NSUBCELL_X/nbs->box[XX][XX],
 +            ba[YY]*grid->ncy*GPU_NSUBCELL_Y/nbs->box[YY][YY],
 +            ba[ZZ]*grid->nc*GPU_NSUBCELL_Z/(grid->ncx*grid->ncy*nbs->box[ZZ][ZZ]));
 +}
 +
 +/* Potentially sorts atoms on LJ coefficients !=0 and ==0.
 + * Also sets interaction flags.
 + */
 +void sort_on_lj(nbnxn_atomdata_t *nbat, int na_c,
 +                int a0, int a1, const int *atinfo,
 +                int *order,
 +                int *flags)
 +{
 +    int      subc, s, a, n1, n2, a_lj_max, i, j;
 +    int      sort1[NBNXN_NA_SC_MAX/GPU_NSUBCELL];
 +    int      sort2[NBNXN_NA_SC_MAX/GPU_NSUBCELL];
 +    gmx_bool haveQ;
 +
 +    *flags = 0;
 +
 +    subc = 0;
 +    for (s = a0; s < a1; s += na_c)
 +    {
 +        /* Make lists for this (sub-)cell on atoms with and without LJ */
 +        n1       = 0;
 +        n2       = 0;
 +        haveQ    = FALSE;
 +        a_lj_max = -1;
 +        for (a = s; a < min(s+na_c, a1); a++)
 +        {
 +            haveQ = haveQ || GET_CGINFO_HAS_Q(atinfo[order[a]]);
 +
 +            if (GET_CGINFO_HAS_VDW(atinfo[order[a]]))
 +            {
 +                sort1[n1++] = order[a];
 +                a_lj_max    = a;
 +            }
 +            else
 +            {
 +                sort2[n2++] = order[a];
 +            }
 +        }
 +
 +        /* If we don't have atom with LJ, there's nothing to sort */
 +        if (n1 > 0)
 +        {
 +            *flags |= NBNXN_CI_DO_LJ(subc);
 +
 +            if (2*n1 <= na_c)
 +            {
 +                /* Only sort when strictly necessary. Ordering particles
 +                 * Ordering particles can lead to less accurate summation
 +                 * due to rounding, both for LJ and Coulomb interactions.
 +                 */
 +                if (2*(a_lj_max - s) >= na_c)
 +                {
 +                    for (i = 0; i < n1; i++)
 +                    {
 +                        order[a0+i] = sort1[i];
 +                    }
 +                    for (j = 0; j < n2; j++)
 +                    {
 +                        order[a0+n1+j] = sort2[j];
 +                    }
 +                }
 +
 +                *flags |= NBNXN_CI_HALF_LJ(subc);
 +            }
 +        }
 +        if (haveQ)
 +        {
 +            *flags |= NBNXN_CI_DO_COUL(subc);
 +        }
 +        subc++;
 +    }
 +}
 +
 +/* Fill a pair search cell with atoms.
 + * Potentially sorts atoms and sets the interaction flags.
 + */
 +void fill_cell(const nbnxn_search_t nbs,
 +               nbnxn_grid_t *grid,
 +               nbnxn_atomdata_t *nbat,
 +               int a0, int a1,
 +               const int *atinfo,
 +               rvec *x,
 +               int sx, int sy, int sz,
 +               float *bb_work)
 +{
 +    int     na, a;
 +    size_t  offset;
 +    float  *bb_ptr;
 +
 +    na = a1 - a0;
 +
 +    if (grid->bSimple)
 +    {
 +        sort_on_lj(nbat, grid->na_c, a0, a1, atinfo, nbs->a,
 +                   grid->flags+(a0>>grid->na_c_2log)-grid->cell0);
 +    }
 +
 +    /* Now we have sorted the atoms, set the cell indices */
 +    for (a = a0; a < a1; a++)
 +    {
 +        nbs->cell[nbs->a[a]] = a;
 +    }
 +
 +    copy_rvec_to_nbat_real(nbs->a+a0, a1-a0, grid->na_c, x,
 +                           nbat->XFormat, nbat->x, a0,
 +                           sx, sy, sz);
 +
 +    if (nbat->XFormat == nbatX4)
 +    {
 +        /* Store the bounding boxes as xyz.xyz. */
 +        offset = ((a0 - grid->cell0*grid->na_sc)>>grid->na_c_2log)*NNBSBB_B;
 +        bb_ptr = grid->bb + offset;
 +
 +#if defined GMX_DOUBLE && defined NBNXN_SEARCH_BB_SSE
 +        if (2*grid->na_cj == grid->na_c)
 +        {
 +            calc_bounding_box_x_x4_halves(na, nbat->x+X4_IND_A(a0), bb_ptr,
 +                                          grid->bbj+offset*2);
 +        }
 +        else
 +#endif
 +        {
 +            calc_bounding_box_x_x4(na, nbat->x+X4_IND_A(a0), bb_ptr);
 +        }
 +    }
 +    else if (nbat->XFormat == nbatX8)
 +    {
 +        /* Store the bounding boxes as xyz.xyz. */
 +        offset = ((a0 - grid->cell0*grid->na_sc)>>grid->na_c_2log)*NNBSBB_B;
 +        bb_ptr = grid->bb + offset;
 +
 +        calc_bounding_box_x_x8(na, nbat->x+X8_IND_A(a0), bb_ptr);
 +    }
 +#ifdef NBNXN_BBXXXX
 +    else if (!grid->bSimple)
 +    {
 +        /* Store the bounding boxes in a format convenient
 +         * for SSE calculations: xxxxyyyyzzzz...
 +         */
 +        bb_ptr =
 +            grid->bb +
 +            ((a0-grid->cell0*grid->na_sc)>>(grid->na_c_2log+STRIDE_PBB_2LOG))*NNBSBB_XXXX +
 +            (((a0-grid->cell0*grid->na_sc)>>grid->na_c_2log) & (STRIDE_PBB-1));
 +
 +#ifdef NBNXN_SEARCH_SSE_SINGLE
 +        if (nbat->XFormat == nbatXYZQ)
 +        {
 +            calc_bounding_box_xxxx_sse(na, nbat->x+a0*nbat->xstride,
 +                                       bb_work, bb_ptr);
 +        }
 +        else
 +#endif
 +        {
 +            calc_bounding_box_xxxx(na, nbat->xstride, nbat->x+a0*nbat->xstride,
 +                                   bb_ptr);
 +        }
 +        if (gmx_debug_at)
 +        {
 +            fprintf(debug, "%2d %2d %2d bb %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n",
 +                    sx, sy, sz,
 +                    bb_ptr[0*STRIDE_PBB], bb_ptr[3*STRIDE_PBB],
 +                    bb_ptr[1*STRIDE_PBB], bb_ptr[4*STRIDE_PBB],
 +                    bb_ptr[2*STRIDE_PBB], bb_ptr[5*STRIDE_PBB]);
 +        }
 +    }
 +#endif
 +    else
 +    {
 +        /* Store the bounding boxes as xyz.xyz. */
 +        bb_ptr = grid->bb+((a0-grid->cell0*grid->na_sc)>>grid->na_c_2log)*NNBSBB_B;
 +
 +        calc_bounding_box(na, nbat->xstride, nbat->x+a0*nbat->xstride,
 +                          bb_ptr);
 +
 +        if (gmx_debug_at)
 +        {
 +            int bbo;
 +            bbo = (a0 - grid->cell0*grid->na_sc)/grid->na_c;
 +            fprintf(debug, "%2d %2d %2d bb %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n",
 +                    sx, sy, sz,
 +                    (grid->bb+bbo*NNBSBB_B)[BBL_X],
 +                    (grid->bb+bbo*NNBSBB_B)[BBU_X],
 +                    (grid->bb+bbo*NNBSBB_B)[BBL_Y],
 +                    (grid->bb+bbo*NNBSBB_B)[BBU_Y],
 +                    (grid->bb+bbo*NNBSBB_B)[BBL_Z],
 +                    (grid->bb+bbo*NNBSBB_B)[BBU_Z]);
 +        }
 +    }
 +}
 +
 +/* Spatially sort the atoms within one grid column */
 +static void sort_columns_simple(const nbnxn_search_t nbs,
 +                                int dd_zone,
 +                                nbnxn_grid_t *grid,
 +                                int a0, int a1,
 +                                const int *atinfo,
 +                                rvec *x,
 +                                nbnxn_atomdata_t *nbat,
 +                                int cxy_start, int cxy_end,
 +                                int *sort_work)
 +{
 +    int  cxy;
 +    int  cx, cy, cz, ncz, cfilled, c;
 +    int  na, ash, ind, a;
 +    int  na_c, ash_c;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "cell0 %d sorting columns %d - %d, atoms %d - %d\n",
 +                grid->cell0, cxy_start, cxy_end, a0, a1);
 +    }
 +
 +    /* Sort the atoms within each x,y column in 3 dimensions */
 +    for (cxy = cxy_start; cxy < cxy_end; cxy++)
 +    {
 +        cx = cxy/grid->ncy;
 +        cy = cxy - cx*grid->ncy;
 +
 +        na  = grid->cxy_na[cxy];
 +        ncz = grid->cxy_ind[cxy+1] - grid->cxy_ind[cxy];
 +        ash = (grid->cell0 + grid->cxy_ind[cxy])*grid->na_sc;
 +
 +        /* Sort the atoms within each x,y column on z coordinate */
 +        sort_atoms(ZZ, FALSE,
 +                   nbs->a+ash, na, x,
 +                   grid->c0[ZZ],
 +                   1.0/nbs->box[ZZ][ZZ], ncz*grid->na_sc,
 +                   sort_work);
 +
 +        /* Fill the ncz cells in this column */
 +        cfilled = grid->cxy_ind[cxy];
 +        for (cz = 0; cz < ncz; cz++)
 +        {
 +            c  = grid->cxy_ind[cxy] + cz;
 +
 +            ash_c = ash + cz*grid->na_sc;
 +            na_c  = min(grid->na_sc, na-(ash_c-ash));
 +
 +            fill_cell(nbs, grid, nbat,
 +                      ash_c, ash_c+na_c, atinfo, x,
 +                      grid->na_sc*cx + (dd_zone >> 2),
 +                      grid->na_sc*cy + (dd_zone & 3),
 +                      grid->na_sc*cz,
 +                      NULL);
 +
 +            /* This copy to bbcz is not really necessary.
 +             * But it allows to use the same grid search code
 +             * for the simple and supersub cell setups.
 +             */
 +            if (na_c > 0)
 +            {
 +                cfilled = c;
 +            }
 +            grid->bbcz[c*NNBSBB_D  ] = grid->bb[cfilled*NNBSBB_B+2];
 +            grid->bbcz[c*NNBSBB_D+1] = grid->bb[cfilled*NNBSBB_B+6];
 +        }
 +
 +        /* Set the unused atom indices to -1 */
 +        for (ind = na; ind < ncz*grid->na_sc; ind++)
 +        {
 +            nbs->a[ash+ind] = -1;
 +        }
 +    }
 +}
 +
 +/* Spatially sort the atoms within one grid column */
 +static void sort_columns_supersub(const nbnxn_search_t nbs,
 +                                  int dd_zone,
 +                                  nbnxn_grid_t *grid,
 +                                  int a0, int a1,
 +                                  const int *atinfo,
 +                                  rvec *x,
 +                                  nbnxn_atomdata_t *nbat,
 +                                  int cxy_start, int cxy_end,
 +                                  int *sort_work)
 +{
 +    int  cxy;
 +    int  cx, cy, cz = -1, c = -1, ncz;
 +    int  na, ash, na_c, ind, a;
 +    int  subdiv_z, sub_z, na_z, ash_z;
 +    int  subdiv_y, sub_y, na_y, ash_y;
 +    int  subdiv_x, sub_x, na_x, ash_x;
 +
 +    /* cppcheck-suppress unassignedVariable */
 +    float bb_work_array[NNBSBB_B+3], *bb_work_align;
 +
 +    bb_work_align = (float *)(((size_t)(bb_work_array+3)) & (~((size_t)15)));
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "cell0 %d sorting columns %d - %d, atoms %d - %d\n",
 +                grid->cell0, cxy_start, cxy_end, a0, a1);
 +    }
 +
 +    subdiv_x = grid->na_c;
 +    subdiv_y = GPU_NSUBCELL_X*subdiv_x;
 +    subdiv_z = GPU_NSUBCELL_Y*subdiv_y;
 +
 +    /* Sort the atoms within each x,y column in 3 dimensions */
 +    for (cxy = cxy_start; cxy < cxy_end; cxy++)
 +    {
 +        cx = cxy/grid->ncy;
 +        cy = cxy - cx*grid->ncy;
 +
 +        na  = grid->cxy_na[cxy];
 +        ncz = grid->cxy_ind[cxy+1] - grid->cxy_ind[cxy];
 +        ash = (grid->cell0 + grid->cxy_ind[cxy])*grid->na_sc;
 +
 +        /* Sort the atoms within each x,y column on z coordinate */
 +        sort_atoms(ZZ, FALSE,
 +                   nbs->a+ash, na, x,
 +                   grid->c0[ZZ],
 +                   1.0/nbs->box[ZZ][ZZ], ncz*grid->na_sc,
 +                   sort_work);
 +
 +        /* This loop goes over the supercells and subcells along z at once */
 +        for (sub_z = 0; sub_z < ncz*GPU_NSUBCELL_Z; sub_z++)
 +        {
 +            ash_z = ash + sub_z*subdiv_z;
 +            na_z  = min(subdiv_z, na-(ash_z-ash));
 +
 +            /* We have already sorted on z */
 +
 +            if (sub_z % GPU_NSUBCELL_Z == 0)
 +            {
 +                cz = sub_z/GPU_NSUBCELL_Z;
 +                c  = grid->cxy_ind[cxy] + cz;
 +
 +                /* The number of atoms in this supercell */
 +                na_c = min(grid->na_sc, na-(ash_z-ash));
 +
 +                grid->nsubc[c] = min(GPU_NSUBCELL, (na_c+grid->na_c-1)/grid->na_c);
 +
 +                /* Store the z-boundaries of the super cell */
 +                grid->bbcz[c*NNBSBB_D  ] = x[nbs->a[ash_z]][ZZ];
 +                grid->bbcz[c*NNBSBB_D+1] = x[nbs->a[ash_z+na_c-1]][ZZ];
 +            }
 +
 +#if GPU_NSUBCELL_Y > 1
 +            /* Sort the atoms along y */
 +            sort_atoms(YY, (sub_z & 1),
 +                       nbs->a+ash_z, na_z, x,
 +                       grid->c0[YY]+cy*grid->sy,
 +                       grid->inv_sy, subdiv_z,
 +                       sort_work);
 +#endif
 +
 +            for (sub_y = 0; sub_y < GPU_NSUBCELL_Y; sub_y++)
 +            {
 +                ash_y = ash_z + sub_y*subdiv_y;
 +                na_y  = min(subdiv_y, na-(ash_y-ash));
 +
 +#if GPU_NSUBCELL_X > 1
 +                /* Sort the atoms along x */
 +                sort_atoms(XX, ((cz*GPU_NSUBCELL_Y + sub_y) & 1),
 +                           nbs->a+ash_y, na_y, x,
 +                           grid->c0[XX]+cx*grid->sx,
 +                           grid->inv_sx, subdiv_y,
 +                           sort_work);
 +#endif
 +
 +                for (sub_x = 0; sub_x < GPU_NSUBCELL_X; sub_x++)
 +                {
 +                    ash_x = ash_y + sub_x*subdiv_x;
 +                    na_x  = min(subdiv_x, na-(ash_x-ash));
 +
 +                    fill_cell(nbs, grid, nbat,
 +                              ash_x, ash_x+na_x, atinfo, x,
 +                              grid->na_c*(cx*GPU_NSUBCELL_X+sub_x) + (dd_zone >> 2),
 +                              grid->na_c*(cy*GPU_NSUBCELL_Y+sub_y) + (dd_zone & 3),
 +                              grid->na_c*sub_z,
 +                              bb_work_align);
 +                }
 +            }
 +        }
 +
 +        /* Set the unused atom indices to -1 */
 +        for (ind = na; ind < ncz*grid->na_sc; ind++)
 +        {
 +            nbs->a[ash+ind] = -1;
 +        }
 +    }
 +}
 +
 +/* Determine in which grid column atoms should go */
 +static void calc_column_indices(nbnxn_grid_t *grid,
 +                                int a0, int a1,
 +                                rvec *x,
 +                                int dd_zone, const int *move,
 +                                int thread, int nthread,
 +                                int *cell,
 +                                int *cxy_na)
 +{
 +    int  n0, n1, i;
 +    int  cx, cy;
 +
 +    /* We add one extra cell for particles which moved during DD */
 +    for (i = 0; i < grid->ncx*grid->ncy+1; i++)
 +    {
 +        cxy_na[i] = 0;
 +    }
 +
 +    n0 = a0 + (int)((thread+0)*(a1 - a0))/nthread;
 +    n1 = a0 + (int)((thread+1)*(a1 - a0))/nthread;
 +    if (dd_zone == 0)
 +    {
 +        /* Home zone */
 +        for (i = n0; i < n1; i++)
 +        {
 +            if (move == NULL || move[i] >= 0)
 +            {
 +                /* We need to be careful with rounding,
 +                 * particles might be a few bits outside the local zone.
 +                 * The int cast takes care of the lower bound,
 +                 * we will explicitly take care of the upper bound.
 +                 */
 +                cx = (int)((x[i][XX] - grid->c0[XX])*grid->inv_sx);
 +                cy = (int)((x[i][YY] - grid->c0[YY])*grid->inv_sy);
 +
 +#ifndef NDEBUG
 +                if (cx < 0 || cx > grid->ncx ||
 +                    cy < 0 || cy > grid->ncy)
 +                {
 +                    gmx_fatal(FARGS,
 +                              "grid cell cx %d cy %d out of range (max %d %d)\n"
 +                              "atom %f %f %f, grid->c0 %f %f",
 +                              cx, cy, grid->ncx, grid->ncy,
 +                              x[i][XX], x[i][YY], x[i][ZZ], grid->c0[XX], grid->c0[YY]);
 +                }
 +#endif
 +                /* Take care of potential rouding issues */
 +                cx = min(cx, grid->ncx - 1);
 +                cy = min(cy, grid->ncy - 1);
 +
 +                /* For the moment cell will contain only the, grid local,
 +                 * x and y indices, not z.
 +                 */
 +                cell[i] = cx*grid->ncy + cy;
 +            }
 +            else
 +            {
 +                /* Put this moved particle after the end of the grid,
 +                 * so we can process it later without using conditionals.
 +                 */
 +                cell[i] = grid->ncx*grid->ncy;
 +            }
 +
 +            cxy_na[cell[i]]++;
 +        }
 +    }
 +    else
 +    {
 +        /* Non-home zone */
 +        for (i = n0; i < n1; i++)
 +        {
 +            cx = (int)((x[i][XX] - grid->c0[XX])*grid->inv_sx);
 +            cy = (int)((x[i][YY] - grid->c0[YY])*grid->inv_sy);
 +
 +            /* For non-home zones there could be particles outside
 +             * the non-bonded cut-off range, which have been communicated
 +             * for bonded interactions only. For the result it doesn't
 +             * matter where these end up on the grid. For performance
 +             * we put them in an extra row at the border.
 +             */
 +            cx = max(cx, 0);
 +            cx = min(cx, grid->ncx - 1);
 +            cy = max(cy, 0);
 +            cy = min(cy, grid->ncy - 1);
 +
 +            /* For the moment cell will contain only the, grid local,
 +             * x and y indices, not z.
 +             */
 +            cell[i] = cx*grid->ncy + cy;
 +
 +            cxy_na[cell[i]]++;
 +        }
 +    }
 +}
 +
 +/* Determine in which grid cells the atoms should go */
 +static void calc_cell_indices(const nbnxn_search_t nbs,
 +                              int dd_zone,
 +                              nbnxn_grid_t *grid,
 +                              int a0, int a1,
 +                              const int *atinfo,
 +                              rvec *x,
 +                              const int *move,
 +                              nbnxn_atomdata_t *nbat)
 +{
 +    int   n0, n1, i;
 +    int   cx, cy, cxy, ncz_max, ncz;
 +    int   nthread, thread;
 +    int  *cxy_na, cxy_na_i;
 +
 +    nthread = gmx_omp_nthreads_get(emntPairsearch);
 +
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +    for (thread = 0; thread < nthread; thread++)
 +    {
 +        calc_column_indices(grid, a0, a1, x, dd_zone, move, thread, nthread,
 +                            nbs->cell, nbs->work[thread].cxy_na);
 +    }
 +
 +    /* Make the cell index as a function of x and y */
 +    ncz_max          = 0;
 +    ncz              = 0;
 +    grid->cxy_ind[0] = 0;
 +    for (i = 0; i < grid->ncx*grid->ncy+1; i++)
 +    {
 +        /* We set ncz_max at the beginning of the loop iso at the end
 +         * to skip i=grid->ncx*grid->ncy which are moved particles
 +         * that do not need to be ordered on the grid.
 +         */
 +        if (ncz > ncz_max)
 +        {
 +            ncz_max = ncz;
 +        }
 +        cxy_na_i = nbs->work[0].cxy_na[i];
 +        for (thread = 1; thread < nthread; thread++)
 +        {
 +            cxy_na_i += nbs->work[thread].cxy_na[i];
 +        }
 +        ncz = (cxy_na_i + grid->na_sc - 1)/grid->na_sc;
 +        if (nbat->XFormat == nbatX8)
 +        {
 +            /* Make the number of cell a multiple of 2 */
 +            ncz = (ncz + 1) & ~1;
 +        }
 +        grid->cxy_ind[i+1] = grid->cxy_ind[i] + ncz;
 +        /* Clear cxy_na, so we can reuse the array below */
 +        grid->cxy_na[i] = 0;
 +    }
 +    grid->nc = grid->cxy_ind[grid->ncx*grid->ncy] - grid->cxy_ind[0];
 +
 +    nbat->natoms = (grid->cell0 + grid->nc)*grid->na_sc;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "ns na_sc %d na_c %d super-cells: %d x %d y %d z %.1f maxz %d\n",
 +                grid->na_sc, grid->na_c, grid->nc,
 +                grid->ncx, grid->ncy, grid->nc/((double)(grid->ncx*grid->ncy)),
 +                ncz_max);
 +        if (gmx_debug_at)
 +        {
 +            i = 0;
 +            for (cy = 0; cy < grid->ncy; cy++)
 +            {
 +                for (cx = 0; cx < grid->ncx; cx++)
 +                {
 +                    fprintf(debug, " %2d", grid->cxy_ind[i+1]-grid->cxy_ind[i]);
 +                    i++;
 +                }
 +                fprintf(debug, "\n");
 +            }
 +        }
 +    }
 +
 +    /* Make sure the work array for sorting is large enough */
 +    if (ncz_max*grid->na_sc*SGSF > nbs->work[0].sort_work_nalloc)
 +    {
 +        for (thread = 0; thread < nbs->nthread_max; thread++)
 +        {
 +            nbs->work[thread].sort_work_nalloc =
 +                over_alloc_large(ncz_max*grid->na_sc*SGSF);
 +            srenew(nbs->work[thread].sort_work,
 +                   nbs->work[thread].sort_work_nalloc);
 +            /* When not in use, all elements should be -1 */
 +            for (i = 0; i < nbs->work[thread].sort_work_nalloc; i++)
 +            {
 +                nbs->work[thread].sort_work[i] = -1;
 +            }
 +        }
 +    }
 +
 +    /* Now we know the dimensions we can fill the grid.
 +     * This is the first, unsorted fill. We sort the columns after this.
 +     */
 +    for (i = a0; i < a1; i++)
 +    {
 +        /* At this point nbs->cell contains the local grid x,y indices */
 +        cxy = nbs->cell[i];
 +        nbs->a[(grid->cell0 + grid->cxy_ind[cxy])*grid->na_sc + grid->cxy_na[cxy]++] = i;
 +    }
 +
 +    if (dd_zone == 0)
 +    {
 +        /* Set the cell indices for the moved particles */
 +        n0 = grid->nc*grid->na_sc;
 +        n1 = grid->nc*grid->na_sc+grid->cxy_na[grid->ncx*grid->ncy];
 +        if (dd_zone == 0)
 +        {
 +            for (i = n0; i < n1; i++)
 +            {
 +                nbs->cell[nbs->a[i]] = i;
 +            }
 +        }
 +    }
 +
 +    /* Sort the super-cell columns along z into the sub-cells. */
 +#pragma omp parallel for num_threads(nbs->nthread_max) schedule(static)
 +    for (thread = 0; thread < nbs->nthread_max; thread++)
 +    {
 +        if (grid->bSimple)
 +        {
 +            sort_columns_simple(nbs, dd_zone, grid, a0, a1, atinfo, x, nbat,
 +                                ((thread+0)*grid->ncx*grid->ncy)/nthread,
 +                                ((thread+1)*grid->ncx*grid->ncy)/nthread,
 +                                nbs->work[thread].sort_work);
 +        }
 +        else
 +        {
 +            sort_columns_supersub(nbs, dd_zone, grid, a0, a1, atinfo, x, nbat,
 +                                  ((thread+0)*grid->ncx*grid->ncy)/nthread,
 +                                  ((thread+1)*grid->ncx*grid->ncy)/nthread,
 +                                  nbs->work[thread].sort_work);
 +        }
 +    }
 +
 +#ifdef NBNXN_SEARCH_BB_SSE
 +    if (grid->bSimple && nbat->XFormat == nbatX8)
 +    {
 +        combine_bounding_box_pairs(grid, grid->bb);
 +    }
 +#endif
 +
 +    if (!grid->bSimple)
 +    {
 +        grid->nsubc_tot = 0;
 +        for (i = 0; i < grid->nc; i++)
 +        {
 +            grid->nsubc_tot += grid->nsubc[i];
 +        }
 +    }
 +
 +    if (debug)
 +    {
 +        if (grid->bSimple)
 +        {
 +            print_bbsizes_simple(debug, nbs, grid);
 +        }
 +        else
 +        {
 +            fprintf(debug, "ns non-zero sub-cells: %d average atoms %.2f\n",
 +                    grid->nsubc_tot, (a1-a0)/(double)grid->nsubc_tot);
 +
 +            print_bbsizes_supersub(debug, nbs, grid);
 +        }
 +    }
 +}
 +
 +static void init_buffer_flags(nbnxn_buffer_flags_t *flags,
 +                              int                   natoms)
 +{
 +    int b;
 +
 +    flags->nflag = (natoms + NBNXN_BUFFERFLAG_SIZE - 1)/NBNXN_BUFFERFLAG_SIZE;
 +    if (flags->nflag > flags->flag_nalloc)
 +    {
 +        flags->flag_nalloc = over_alloc_large(flags->nflag);
 +        srenew(flags->flag, flags->flag_nalloc);
 +    }
 +    for (b = 0; b < flags->nflag; b++)
 +    {
 +        flags->flag[b] = 0;
 +    }
 +}
 +
 +/* Sets up a grid and puts the atoms on the grid.
 + * This function only operates on one domain of the domain decompostion.
 + * Note that without domain decomposition there is only one domain.
 + */
 +void nbnxn_put_on_grid(nbnxn_search_t nbs,
 +                       int ePBC, matrix box,
 +                       int dd_zone,
 +                       rvec corner0, rvec corner1,
 +                       int a0, int a1,
 +                       real atom_density,
 +                       const int *atinfo,
 +                       rvec *x,
 +                       int nmoved, int *move,
 +                       int nb_kernel_type,
 +                       nbnxn_atomdata_t *nbat)
 +{
 +    nbnxn_grid_t *grid;
 +    int           n;
 +    int           nc_max_grid, nc_max;
 +
 +    grid = &nbs->grid[dd_zone];
 +
 +    nbs_cycle_start(&nbs->cc[enbsCCgrid]);
 +
 +    grid->bSimple = nbnxn_kernel_pairlist_simple(nb_kernel_type);
 +
 +    grid->na_c      = nbnxn_kernel_to_ci_size(nb_kernel_type);
 +    grid->na_cj     = nbnxn_kernel_to_cj_size(nb_kernel_type);
 +    grid->na_sc     = (grid->bSimple ? 1 : GPU_NSUBCELL)*grid->na_c;
 +    grid->na_c_2log = get_2log(grid->na_c);
 +
 +    nbat->na_c = grid->na_c;
 +
 +    if (dd_zone == 0)
 +    {
 +        grid->cell0 = 0;
 +    }
 +    else
 +    {
 +        grid->cell0 =
 +            (nbs->grid[dd_zone-1].cell0 + nbs->grid[dd_zone-1].nc)*
 +            nbs->grid[dd_zone-1].na_sc/grid->na_sc;
 +    }
 +
 +    n = a1 - a0;
 +
 +    if (dd_zone == 0)
 +    {
 +        nbs->ePBC = ePBC;
 +        copy_mat(box, nbs->box);
 +
 +        if (atom_density >= 0)
 +        {
 +            grid->atom_density = atom_density;
 +        }
 +        else
 +        {
 +            grid->atom_density = grid_atom_density(n-nmoved, corner0, corner1);
 +        }
 +
 +        grid->cell0 = 0;
 +
 +        nbs->natoms_local    = a1 - nmoved;
 +        /* We assume that nbnxn_put_on_grid is called first
 +         * for the local atoms (dd_zone=0).
 +         */
 +        nbs->natoms_nonlocal = a1 - nmoved;
 +    }
 +    else
 +    {
 +        nbs->natoms_nonlocal = max(nbs->natoms_nonlocal, a1);
 +    }
 +
 +    nc_max_grid = set_grid_size_xy(nbs, grid,
 +                                   dd_zone, n-nmoved, corner0, corner1,
 +                                   nbs->grid[0].atom_density,
 +                                   nbat->XFormat);
 +
 +    nc_max = grid->cell0 + nc_max_grid;
 +
 +    if (a1 > nbs->cell_nalloc)
 +    {
 +        nbs->cell_nalloc = over_alloc_large(a1);
 +        srenew(nbs->cell, nbs->cell_nalloc);
 +    }
 +
 +    /* To avoid conditionals we store the moved particles at the end of a,
 +     * make sure we have enough space.
 +     */
 +    if (nc_max*grid->na_sc + nmoved > nbs->a_nalloc)
 +    {
 +        nbs->a_nalloc = over_alloc_large(nc_max*grid->na_sc + nmoved);
 +        srenew(nbs->a, nbs->a_nalloc);
 +    }
 +
 +    /* We need padding up to a multiple of the buffer flag size: simply add */
 +    if (nc_max*grid->na_sc + NBNXN_BUFFERFLAG_SIZE > nbat->nalloc)
 +    {
 +        nbnxn_atomdata_realloc(nbat, nc_max*grid->na_sc+NBNXN_BUFFERFLAG_SIZE);
 +    }
 +
 +    calc_cell_indices(nbs, dd_zone, grid, a0, a1, atinfo, x, move, nbat);
 +
 +    if (dd_zone == 0)
 +    {
 +        nbat->natoms_local = nbat->natoms;
 +    }
 +
 +    nbs_cycle_stop(&nbs->cc[enbsCCgrid]);
 +}
 +
 +/* Calls nbnxn_put_on_grid for all non-local domains */
 +void nbnxn_put_on_grid_nonlocal(nbnxn_search_t            nbs,
 +                                const gmx_domdec_zones_t *zones,
 +                                const int                *atinfo,
 +                                rvec                     *x,
 +                                int                       nb_kernel_type,
 +                                nbnxn_atomdata_t         *nbat)
 +{
 +    int  zone, d;
 +    rvec c0, c1;
 +
 +    for (zone = 1; zone < zones->n; zone++)
 +    {
 +        for (d = 0; d < DIM; d++)
 +        {
 +            c0[d] = zones->size[zone].bb_x0[d];
 +            c1[d] = zones->size[zone].bb_x1[d];
 +        }
 +
 +        nbnxn_put_on_grid(nbs, nbs->ePBC, NULL,
 +                          zone, c0, c1,
 +                          zones->cg_range[zone],
 +                          zones->cg_range[zone+1],
 +                          -1,
 +                          atinfo,
 +                          x,
 +                          0, NULL,
 +                          nb_kernel_type,
 +                          nbat);
 +    }
 +}
 +
 +/* Add simple grid type information to the local super/sub grid */
 +void nbnxn_grid_add_simple(nbnxn_search_t    nbs,
 +                           nbnxn_atomdata_t *nbat)
 +{
 +    nbnxn_grid_t *grid;
 +    float        *bbcz, *bb;
 +    int           ncd, sc;
 +
 +    grid = &nbs->grid[0];
 +
 +    if (grid->bSimple)
 +    {
 +        gmx_incons("nbnxn_grid_simple called with a simple grid");
 +    }
 +
 +    ncd = grid->na_sc/NBNXN_CPU_CLUSTER_I_SIZE;
 +
 +    if (grid->nc*ncd > grid->nc_nalloc_simple)
 +    {
 +        grid->nc_nalloc_simple = over_alloc_large(grid->nc*ncd);
 +        srenew(grid->bbcz_simple, grid->nc_nalloc_simple*NNBSBB_D);
 +        srenew(grid->bb_simple, grid->nc_nalloc_simple*NNBSBB_B);
 +        srenew(grid->flags_simple, grid->nc_nalloc_simple);
 +        if (nbat->XFormat)
 +        {
 +            sfree_aligned(grid->bbj);
 +            snew_aligned(grid->bbj, grid->nc_nalloc_simple/2, 16);
 +        }
 +    }
 +
 +    bbcz = grid->bbcz_simple;
 +    bb   = grid->bb_simple;
 +
 +#pragma omp parallel for num_threads(gmx_omp_nthreads_get(emntPairsearch)) schedule(static)
 +    for (sc = 0; sc < grid->nc; sc++)
 +    {
 +        int c, tx, na;
 +
 +        for (c = 0; c < ncd; c++)
 +        {
 +            tx = sc*ncd + c;
 +
 +            na = NBNXN_CPU_CLUSTER_I_SIZE;
 +            while (na > 0 &&
 +                   nbat->type[tx*NBNXN_CPU_CLUSTER_I_SIZE+na-1] == nbat->ntype-1)
 +            {
 +                na--;
 +            }
 +
 +            if (na > 0)
 +            {
 +                switch (nbat->XFormat)
 +                {
 +                    case nbatX4:
 +                        /* PACK_X4==NBNXN_CPU_CLUSTER_I_SIZE, so this is simple */
 +                        calc_bounding_box_x_x4(na, nbat->x+tx*STRIDE_P4,
 +                                               bb+tx*NNBSBB_B);
 +                        break;
 +                    case nbatX8:
 +                        /* PACK_X8>NBNXN_CPU_CLUSTER_I_SIZE, more complicated */
 +                        calc_bounding_box_x_x8(na, nbat->x+X8_IND_A(tx*NBNXN_CPU_CLUSTER_I_SIZE),
 +                                               bb+tx*NNBSBB_B);
 +                        break;
 +                    default:
 +                        calc_bounding_box(na, nbat->xstride,
 +                                          nbat->x+tx*NBNXN_CPU_CLUSTER_I_SIZE*nbat->xstride,
 +                                          bb+tx*NNBSBB_B);
 +                        break;
 +                }
 +                bbcz[tx*NNBSBB_D+0] = bb[tx*NNBSBB_B         +ZZ];
 +                bbcz[tx*NNBSBB_D+1] = bb[tx*NNBSBB_B+NNBSBB_C+ZZ];
 +
 +                /* No interaction optimization yet here */
 +                grid->flags_simple[tx] = NBNXN_CI_DO_LJ(0) | NBNXN_CI_DO_COUL(0);
 +            }
 +            else
 +            {
 +                grid->flags_simple[tx] = 0;
 +            }
 +        }
 +    }
 +
 +#ifdef NBNXN_SEARCH_BB_SSE
 +    if (grid->bSimple && nbat->XFormat == nbatX8)
 +    {
 +        combine_bounding_box_pairs(grid, grid->bb_simple);
 +    }
 +#endif
 +}
 +
 +void nbnxn_get_ncells(nbnxn_search_t nbs, int *ncx, int *ncy)
 +{
 +    *ncx = nbs->grid[0].ncx;
 +    *ncy = nbs->grid[0].ncy;
 +}
 +
 +void nbnxn_get_atomorder(nbnxn_search_t nbs, int **a, int *n)
 +{
 +    const nbnxn_grid_t *grid;
 +
 +    grid = &nbs->grid[0];
 +
 +    /* Return the atom order for the home cell (index 0) */
 +    *a  = nbs->a;
 +
 +    *n = grid->cxy_ind[grid->ncx*grid->ncy]*grid->na_sc;
 +}
 +
 +void nbnxn_set_atomorder(nbnxn_search_t nbs)
 +{
 +    nbnxn_grid_t *grid;
 +    int           ao, cx, cy, cxy, cz, j;
 +
 +    /* Set the atom order for the home cell (index 0) */
 +    grid = &nbs->grid[0];
 +
 +    ao = 0;
 +    for (cx = 0; cx < grid->ncx; cx++)
 +    {
 +        for (cy = 0; cy < grid->ncy; cy++)
 +        {
 +            cxy = cx*grid->ncy + cy;
 +            j   = grid->cxy_ind[cxy]*grid->na_sc;
 +            for (cz = 0; cz < grid->cxy_na[cxy]; cz++)
 +            {
 +                nbs->a[j]     = ao;
 +                nbs->cell[ao] = j;
 +                ao++;
 +                j++;
 +            }
 +        }
 +    }
 +}
 +
 +/* Determines the cell range along one dimension that
 + * the bounding box b0 - b1 sees.
 + */
 +static void get_cell_range(real b0, real b1,
 +                           int nc, real c0, real s, real invs,
 +                           real d2, real r2, int *cf, int *cl)
 +{
 +    *cf = max((int)((b0 - c0)*invs), 0);
 +
 +    while (*cf > 0 && d2 + sqr((b0 - c0) - (*cf-1+1)*s) < r2)
 +    {
 +        (*cf)--;
 +    }
 +
 +    *cl = min((int)((b1 - c0)*invs), nc-1);
 +    while (*cl < nc-1 && d2 + sqr((*cl+1)*s - (b1 - c0)) < r2)
 +    {
 +        (*cl)++;
 +    }
 +}
 +
 +/* Reference code calculating the distance^2 between two bounding boxes */
 +static float box_dist2(float bx0, float bx1, float by0,
 +                       float by1, float bz0, float bz1,
 +                       const float *bb)
 +{
 +    float d2;
 +    float dl, dh, dm, dm0;
 +
 +    d2 = 0;
 +
 +    dl  = bx0 - bb[BBU_X];
 +    dh  = bb[BBL_X] - bx1;
 +    dm  = max(dl, dh);
 +    dm0 = max(dm, 0);
 +    d2 += dm0*dm0;
 +
 +    dl  = by0 - bb[BBU_Y];
 +    dh  = bb[BBL_Y] - by1;
 +    dm  = max(dl, dh);
 +    dm0 = max(dm, 0);
 +    d2 += dm0*dm0;
 +
 +    dl  = bz0 - bb[BBU_Z];
 +    dh  = bb[BBL_Z] - bz1;
 +    dm  = max(dl, dh);
 +    dm0 = max(dm, 0);
 +    d2 += dm0*dm0;
 +
 +    return d2;
 +}
 +
 +/* Plain C code calculating the distance^2 between two bounding boxes */
 +static float subc_bb_dist2(int si, const float *bb_i_ci,
 +                           int csj, const float *bb_j_all)
 +{
 +    const float *bb_i, *bb_j;
 +    float        d2;
 +    float        dl, dh, dm, dm0;
 +
 +    bb_i = bb_i_ci  +  si*NNBSBB_B;
 +    bb_j = bb_j_all + csj*NNBSBB_B;
 +
 +    d2 = 0;
 +
 +    dl  = bb_i[BBL_X] - bb_j[BBU_X];
 +    dh  = bb_j[BBL_X] - bb_i[BBU_X];
 +    dm  = max(dl, dh);
 +    dm0 = max(dm, 0);
 +    d2 += dm0*dm0;
 +
 +    dl  = bb_i[BBL_Y] - bb_j[BBU_Y];
 +    dh  = bb_j[BBL_Y] - bb_i[BBU_Y];
 +    dm  = max(dl, dh);
 +    dm0 = max(dm, 0);
 +    d2 += dm0*dm0;
 +
 +    dl  = bb_i[BBL_Z] - bb_j[BBU_Z];
 +    dh  = bb_j[BBL_Z] - bb_i[BBU_Z];
 +    dm  = max(dl, dh);
 +    dm0 = max(dm, 0);
 +    d2 += dm0*dm0;
 +
 +    return d2;
 +}
 +
 +#ifdef NBNXN_SEARCH_BB_SSE
 +
 +/* SSE code for bb distance for bb format xyz0 */
 +static float subc_bb_dist2_sse(int na_c,
 +                               int si, const float *bb_i_ci,
 +                               int csj, const float *bb_j_all)
 +{
 +    const float *bb_i, *bb_j;
 +
 +    __m128       bb_i_SSE0, bb_i_SSE1;
 +    __m128       bb_j_SSE0, bb_j_SSE1;
 +    __m128       dl_SSE;
 +    __m128       dh_SSE;
 +    __m128       dm_SSE;
 +    __m128       dm0_SSE;
 +    __m128       d2_SSE;
 +#ifndef GMX_X86_SSE4_1
 +    float        d2_array[7], *d2_align;
 +
 +    d2_align = (float *)(((size_t)(d2_array+3)) & (~((size_t)15)));
 +#else
 +    float d2;
 +#endif
 +
 +    bb_i = bb_i_ci  +  si*NNBSBB_B;
 +    bb_j = bb_j_all + csj*NNBSBB_B;
 +
 +    bb_i_SSE0 = _mm_load_ps(bb_i);
 +    bb_i_SSE1 = _mm_load_ps(bb_i+NNBSBB_C);
 +    bb_j_SSE0 = _mm_load_ps(bb_j);
 +    bb_j_SSE1 = _mm_load_ps(bb_j+NNBSBB_C);
 +
 +    dl_SSE    = _mm_sub_ps(bb_i_SSE0, bb_j_SSE1);
 +    dh_SSE    = _mm_sub_ps(bb_j_SSE0, bb_i_SSE1);
 +
 +    dm_SSE    = _mm_max_ps(dl_SSE, dh_SSE);
 +    dm0_SSE   = _mm_max_ps(dm_SSE, _mm_setzero_ps());
 +#ifndef GMX_X86_SSE4_1
 +    d2_SSE    = _mm_mul_ps(dm0_SSE, dm0_SSE);
 +
 +    _mm_store_ps(d2_align, d2_SSE);
 +
 +    return d2_align[0] + d2_align[1] + d2_align[2];
 +#else
 +    /* SSE4.1 dot product of components 0,1,2 */
 +    d2_SSE    = _mm_dp_ps(dm0_SSE, dm0_SSE, 0x71);
 +
 +    _mm_store_ss(&d2, d2_SSE);
 +
 +    return d2;
 +#endif
 +}
 +
 +/* Calculate bb bounding distances of bb_i[si,...,si+3] and store them in d2 */
 +#define SUBC_BB_DIST2_SSE_XXXX_INNER(si, bb_i, d2) \
 +    {                                                \
 +        int    shi;                                  \
 +                                                 \
 +        __m128 dx_0, dy_0, dz_0;                       \
 +        __m128 dx_1, dy_1, dz_1;                       \
 +                                                 \
 +        __m128 mx, my, mz;                             \
 +        __m128 m0x, m0y, m0z;                          \
 +                                                 \
 +        __m128 d2x, d2y, d2z;                          \
 +        __m128 d2s, d2t;                              \
 +                                                 \
 +        shi = si*NNBSBB_D*DIM;                       \
 +                                                 \
 +        xi_l = _mm_load_ps(bb_i+shi+0*STRIDE_PBB);   \
 +        yi_l = _mm_load_ps(bb_i+shi+1*STRIDE_PBB);   \
 +        zi_l = _mm_load_ps(bb_i+shi+2*STRIDE_PBB);   \
 +        xi_h = _mm_load_ps(bb_i+shi+3*STRIDE_PBB);   \
 +        yi_h = _mm_load_ps(bb_i+shi+4*STRIDE_PBB);   \
 +        zi_h = _mm_load_ps(bb_i+shi+5*STRIDE_PBB);   \
 +                                                 \
 +        dx_0 = _mm_sub_ps(xi_l, xj_h);                \
 +        dy_0 = _mm_sub_ps(yi_l, yj_h);                \
 +        dz_0 = _mm_sub_ps(zi_l, zj_h);                \
 +                                                 \
 +        dx_1 = _mm_sub_ps(xj_l, xi_h);                \
 +        dy_1 = _mm_sub_ps(yj_l, yi_h);                \
 +        dz_1 = _mm_sub_ps(zj_l, zi_h);                \
 +                                                 \
 +        mx   = _mm_max_ps(dx_0, dx_1);                \
 +        my   = _mm_max_ps(dy_0, dy_1);                \
 +        mz   = _mm_max_ps(dz_0, dz_1);                \
 +                                                 \
 +        m0x  = _mm_max_ps(mx, zero);                  \
 +        m0y  = _mm_max_ps(my, zero);                  \
 +        m0z  = _mm_max_ps(mz, zero);                  \
 +                                                 \
 +        d2x  = _mm_mul_ps(m0x, m0x);                  \
 +        d2y  = _mm_mul_ps(m0y, m0y);                  \
 +        d2z  = _mm_mul_ps(m0z, m0z);                  \
 +                                                 \
 +        d2s  = _mm_add_ps(d2x, d2y);                  \
 +        d2t  = _mm_add_ps(d2s, d2z);                  \
 +                                                 \
 +        _mm_store_ps(d2+si, d2t);                     \
 +    }
 +
 +/* SSE code for nsi bb distances for bb format xxxxyyyyzzzz */
 +static void subc_bb_dist2_sse_xxxx(const float *bb_j,
 +                                   int nsi, const float *bb_i,
 +                                   float *d2)
 +{
 +    __m128 xj_l, yj_l, zj_l;
 +    __m128 xj_h, yj_h, zj_h;
 +    __m128 xi_l, yi_l, zi_l;
 +    __m128 xi_h, yi_h, zi_h;
 +
 +    __m128 zero;
 +
 +    zero = _mm_setzero_ps();
 +
 +    xj_l = _mm_set1_ps(bb_j[0*STRIDE_PBB]);
 +    yj_l = _mm_set1_ps(bb_j[1*STRIDE_PBB]);
 +    zj_l = _mm_set1_ps(bb_j[2*STRIDE_PBB]);
 +    xj_h = _mm_set1_ps(bb_j[3*STRIDE_PBB]);
 +    yj_h = _mm_set1_ps(bb_j[4*STRIDE_PBB]);
 +    zj_h = _mm_set1_ps(bb_j[5*STRIDE_PBB]);
 +
 +    /* Here we "loop" over si (0,STRIDE_PBB) from 0 to nsi with step STRIDE_PBB.
 +     * But as we know the number of iterations is 1 or 2, we unroll manually.
 +     */
 +    SUBC_BB_DIST2_SSE_XXXX_INNER(0, bb_i, d2);
 +    if (STRIDE_PBB < nsi)
 +    {
 +        SUBC_BB_DIST2_SSE_XXXX_INNER(STRIDE_PBB, bb_i, d2);
 +    }
 +}
 +
 +#endif /* NBNXN_SEARCH_BB_SSE */
 +
 +/* Plain C function which determines if any atom pair between two cells
 + * is within distance sqrt(rl2).
 + */
 +static gmx_bool subc_in_range_x(int na_c,
 +                                int si, const real *x_i,
 +                                int csj, int stride, const real *x_j,
 +                                real rl2)
 +{
 +    int  i, j, i0, j0;
 +    real d2;
 +
 +    for (i = 0; i < na_c; i++)
 +    {
 +        i0 = (si*na_c + i)*DIM;
 +        for (j = 0; j < na_c; j++)
 +        {
 +            j0 = (csj*na_c + j)*stride;
 +
 +            d2 = sqr(x_i[i0  ] - x_j[j0  ]) +
 +                sqr(x_i[i0+1] - x_j[j0+1]) +
 +                sqr(x_i[i0+2] - x_j[j0+2]);
 +
 +            if (d2 < rl2)
 +            {
 +                return TRUE;
 +            }
 +        }
 +    }
 +
 +    return FALSE;
 +}
 +
 +/* SSE function which determines if any atom pair between two cells,
 + * both with 8 atoms, is within distance sqrt(rl2).
 + */
 +static gmx_bool subc_in_range_sse8(int na_c,
 +                                   int si, const real *x_i,
 +                                   int csj, int stride, const real *x_j,
 +                                   real rl2)
 +{
 +#ifdef NBNXN_SEARCH_SSE_SINGLE
 +    __m128 ix_SSE0, iy_SSE0, iz_SSE0;
 +    __m128 ix_SSE1, iy_SSE1, iz_SSE1;
 +
 +    __m128 rc2_SSE;
 +
 +    int    na_c_sse;
 +    int    j0, j1;
 +
 +    rc2_SSE   = _mm_set1_ps(rl2);
 +
 +    na_c_sse = NBNXN_GPU_CLUSTER_SIZE/STRIDE_PBB;
 +    ix_SSE0  = _mm_load_ps(x_i+(si*na_c_sse*DIM+0)*STRIDE_PBB);
 +    iy_SSE0  = _mm_load_ps(x_i+(si*na_c_sse*DIM+1)*STRIDE_PBB);
 +    iz_SSE0  = _mm_load_ps(x_i+(si*na_c_sse*DIM+2)*STRIDE_PBB);
 +    ix_SSE1  = _mm_load_ps(x_i+(si*na_c_sse*DIM+3)*STRIDE_PBB);
 +    iy_SSE1  = _mm_load_ps(x_i+(si*na_c_sse*DIM+4)*STRIDE_PBB);
 +    iz_SSE1  = _mm_load_ps(x_i+(si*na_c_sse*DIM+5)*STRIDE_PBB);
 +
 +    /* We loop from the outer to the inner particles to maximize
 +     * the chance that we find a pair in range quickly and return.
 +     */
 +    j0 = csj*na_c;
 +    j1 = j0 + na_c - 1;
 +    while (j0 < j1)
 +    {
 +        __m128 jx0_SSE, jy0_SSE, jz0_SSE;
 +        __m128 jx1_SSE, jy1_SSE, jz1_SSE;
 +
 +        __m128 dx_SSE0, dy_SSE0, dz_SSE0;
 +        __m128 dx_SSE1, dy_SSE1, dz_SSE1;
 +        __m128 dx_SSE2, dy_SSE2, dz_SSE2;
 +        __m128 dx_SSE3, dy_SSE3, dz_SSE3;
 +
 +        __m128 rsq_SSE0;
 +        __m128 rsq_SSE1;
 +        __m128 rsq_SSE2;
 +        __m128 rsq_SSE3;
 +
 +        __m128 wco_SSE0;
 +        __m128 wco_SSE1;
 +        __m128 wco_SSE2;
 +        __m128 wco_SSE3;
 +        __m128 wco_any_SSE01, wco_any_SSE23, wco_any_SSE;
 +
 +        jx0_SSE = _mm_load1_ps(x_j+j0*stride+0);
 +        jy0_SSE = _mm_load1_ps(x_j+j0*stride+1);
 +        jz0_SSE = _mm_load1_ps(x_j+j0*stride+2);
 +
 +        jx1_SSE = _mm_load1_ps(x_j+j1*stride+0);
 +        jy1_SSE = _mm_load1_ps(x_j+j1*stride+1);
 +        jz1_SSE = _mm_load1_ps(x_j+j1*stride+2);
 +
 +        /* Calculate distance */
 +        dx_SSE0            = _mm_sub_ps(ix_SSE0, jx0_SSE);
 +        dy_SSE0            = _mm_sub_ps(iy_SSE0, jy0_SSE);
 +        dz_SSE0            = _mm_sub_ps(iz_SSE0, jz0_SSE);
 +        dx_SSE1            = _mm_sub_ps(ix_SSE1, jx0_SSE);
 +        dy_SSE1            = _mm_sub_ps(iy_SSE1, jy0_SSE);
 +        dz_SSE1            = _mm_sub_ps(iz_SSE1, jz0_SSE);
 +        dx_SSE2            = _mm_sub_ps(ix_SSE0, jx1_SSE);
 +        dy_SSE2            = _mm_sub_ps(iy_SSE0, jy1_SSE);
 +        dz_SSE2            = _mm_sub_ps(iz_SSE0, jz1_SSE);
 +        dx_SSE3            = _mm_sub_ps(ix_SSE1, jx1_SSE);
 +        dy_SSE3            = _mm_sub_ps(iy_SSE1, jy1_SSE);
 +        dz_SSE3            = _mm_sub_ps(iz_SSE1, jz1_SSE);
 +
 +        /* rsq = dx*dx+dy*dy+dz*dz */
 +        rsq_SSE0           = gmx_mm_calc_rsq_ps(dx_SSE0, dy_SSE0, dz_SSE0);
 +        rsq_SSE1           = gmx_mm_calc_rsq_ps(dx_SSE1, dy_SSE1, dz_SSE1);
 +        rsq_SSE2           = gmx_mm_calc_rsq_ps(dx_SSE2, dy_SSE2, dz_SSE2);
 +        rsq_SSE3           = gmx_mm_calc_rsq_ps(dx_SSE3, dy_SSE3, dz_SSE3);
 +
 +        wco_SSE0           = _mm_cmplt_ps(rsq_SSE0, rc2_SSE);
 +        wco_SSE1           = _mm_cmplt_ps(rsq_SSE1, rc2_SSE);
 +        wco_SSE2           = _mm_cmplt_ps(rsq_SSE2, rc2_SSE);
 +        wco_SSE3           = _mm_cmplt_ps(rsq_SSE3, rc2_SSE);
 +
 +        wco_any_SSE01      = _mm_or_ps(wco_SSE0, wco_SSE1);
 +        wco_any_SSE23      = _mm_or_ps(wco_SSE2, wco_SSE3);
 +        wco_any_SSE        = _mm_or_ps(wco_any_SSE01, wco_any_SSE23);
 +
 +        if (_mm_movemask_ps(wco_any_SSE))
 +        {
 +            return TRUE;
 +        }
 +
 +        j0++;
 +        j1--;
 +    }
 +    return FALSE;
 +
 +#else
 +    /* No SSE */
 +    gmx_incons("SSE function called without SSE support");
 +
 +    return TRUE;
 +#endif
 +}
 +
 +/* Returns the j sub-cell for index cj_ind */
 +static int nbl_cj(const nbnxn_pairlist_t *nbl, int cj_ind)
 +{
 +    return nbl->cj4[cj_ind >> NBNXN_GPU_JGROUP_SIZE_2LOG].cj[cj_ind & (NBNXN_GPU_JGROUP_SIZE - 1)];
 +}
 +
 +/* Returns the i-interaction mask of the j sub-cell for index cj_ind */
 +static unsigned nbl_imask0(const nbnxn_pairlist_t *nbl, int cj_ind)
 +{
 +    return nbl->cj4[cj_ind >> NBNXN_GPU_JGROUP_SIZE_2LOG].imei[0].imask;
 +}
 +
 +/* Ensures there is enough space for extra extra exclusion masks */
 +static void check_excl_space(nbnxn_pairlist_t *nbl, int extra)
 +{
 +    if (nbl->nexcl+extra > nbl->excl_nalloc)
 +    {
 +        nbl->excl_nalloc = over_alloc_small(nbl->nexcl+extra);
 +        nbnxn_realloc_void((void **)&nbl->excl,
 +                           nbl->nexcl*sizeof(*nbl->excl),
 +                           nbl->excl_nalloc*sizeof(*nbl->excl),
 +                           nbl->alloc, nbl->free);
 +    }
 +}
 +
 +/* Ensures there is enough space for ncell extra j-cells in the list */
 +static void check_subcell_list_space_simple(nbnxn_pairlist_t *nbl,
 +                                            int               ncell)
 +{
 +    int cj_max;
 +
 +    cj_max = nbl->ncj + ncell;
 +
 +    if (cj_max > nbl->cj_nalloc)
 +    {
 +        nbl->cj_nalloc = over_alloc_small(cj_max);
 +        nbnxn_realloc_void((void **)&nbl->cj,
 +                           nbl->ncj*sizeof(*nbl->cj),
 +                           nbl->cj_nalloc*sizeof(*nbl->cj),
 +                           nbl->alloc, nbl->free);
 +    }
 +}
 +
 +/* Ensures there is enough space for ncell extra j-subcells in the list */
 +static void check_subcell_list_space_supersub(nbnxn_pairlist_t *nbl,
 +                                              int               nsupercell)
 +{
 +    int ncj4_max, j4, j, w, t;
 +
 +#define NWARP       2
 +#define WARP_SIZE  32
 +
 +    /* We can have maximally nsupercell*GPU_NSUBCELL sj lists */
 +    /* We can store 4 j-subcell - i-supercell pairs in one struct.
 +     * since we round down, we need one extra entry.
 +     */
 +    ncj4_max = ((nbl->work->cj_ind + nsupercell*GPU_NSUBCELL + NBNXN_GPU_JGROUP_SIZE - 1) >> NBNXN_GPU_JGROUP_SIZE_2LOG);
 +
 +    if (ncj4_max > nbl->cj4_nalloc)
 +    {
 +        nbl->cj4_nalloc = over_alloc_small(ncj4_max);
 +        nbnxn_realloc_void((void **)&nbl->cj4,
 +                           nbl->work->cj4_init*sizeof(*nbl->cj4),
 +                           nbl->cj4_nalloc*sizeof(*nbl->cj4),
 +                           nbl->alloc, nbl->free);
 +    }
 +
 +    if (ncj4_max > nbl->work->cj4_init)
 +    {
 +        for (j4 = nbl->work->cj4_init; j4 < ncj4_max; j4++)
 +        {
 +            /* No i-subcells and no excl's in the list initially */
 +            for (w = 0; w < NWARP; w++)
 +            {
 +                nbl->cj4[j4].imei[w].imask    = 0U;
 +                nbl->cj4[j4].imei[w].excl_ind = 0;
 +
 +            }
 +        }
 +        nbl->work->cj4_init = ncj4_max;
 +    }
 +}
 +
 +/* Set all excl masks for one GPU warp no exclusions */
 +static void set_no_excls(nbnxn_excl_t *excl)
 +{
 +    int t;
 +
 +    for (t = 0; t < WARP_SIZE; t++)
 +    {
 +        /* Turn all interaction bits on */
 +        excl->pair[t] = NBNXN_INT_MASK_ALL;
 +    }
 +}
 +
 +/* Initializes a single nbnxn_pairlist_t data structure */
 +static void nbnxn_init_pairlist(nbnxn_pairlist_t *nbl,
 +                                gmx_bool          bSimple,
 +                                nbnxn_alloc_t    *alloc,
 +                                nbnxn_free_t     *free)
 +{
 +    if (alloc == NULL)
 +    {
 +        nbl->alloc = nbnxn_alloc_aligned;
 +    }
 +    else
 +    {
 +        nbl->alloc = alloc;
 +    }
 +    if (free == NULL)
 +    {
 +        nbl->free = nbnxn_free_aligned;
 +    }
 +    else
 +    {
 +        nbl->free = free;
 +    }
 +
 +    nbl->bSimple     = bSimple;
 +    nbl->na_sc       = 0;
 +    nbl->na_ci       = 0;
 +    nbl->na_cj       = 0;
 +    nbl->nci         = 0;
 +    nbl->ci          = NULL;
 +    nbl->ci_nalloc   = 0;
 +    nbl->ncj         = 0;
 +    nbl->cj          = NULL;
 +    nbl->cj_nalloc   = 0;
 +    nbl->ncj4        = 0;
 +    /* We need one element extra in sj, so alloc initially with 1 */
 +    nbl->cj4_nalloc  = 0;
 +    nbl->cj4         = NULL;
 +    nbl->nci_tot     = 0;
 +
 +    if (!nbl->bSimple)
 +    {
 +        nbl->excl        = NULL;
 +        nbl->excl_nalloc = 0;
 +        nbl->nexcl       = 0;
 +        check_excl_space(nbl, 1);
 +        nbl->nexcl       = 1;
 +        set_no_excls(&nbl->excl[0]);
 +    }
 +
 +    snew(nbl->work, 1);
 +#ifdef NBNXN_BBXXXX
 +    snew_aligned(nbl->work->bb_ci, GPU_NSUBCELL/STRIDE_PBB*NNBSBB_XXXX, NBNXN_MEM_ALIGN);
 +#else
 +    snew_aligned(nbl->work->bb_ci, GPU_NSUBCELL*NNBSBB_B, NBNXN_MEM_ALIGN);
 +#endif
 +    snew_aligned(nbl->work->x_ci, NBNXN_NA_SC_MAX*DIM, NBNXN_MEM_ALIGN);
 +#ifdef GMX_NBNXN_SIMD
 +    snew_aligned(nbl->work->x_ci_simd_4xn, 1, NBNXN_MEM_ALIGN);
 +    snew_aligned(nbl->work->x_ci_simd_2xnn, 1, NBNXN_MEM_ALIGN);
 +#endif
 +    snew_aligned(nbl->work->d2, GPU_NSUBCELL, NBNXN_MEM_ALIGN);
 +
 +    nbl->work->sort            = NULL;
 +    nbl->work->sort_nalloc     = 0;
 +    nbl->work->sci_sort        = NULL;
 +    nbl->work->sci_sort_nalloc = 0;
 +}
 +
 +void nbnxn_init_pairlist_set(nbnxn_pairlist_set_t *nbl_list,
 +                             gmx_bool bSimple, gmx_bool bCombined,
 +                             nbnxn_alloc_t *alloc,
 +                             nbnxn_free_t  *free)
 +{
 +    int i;
 +
 +    nbl_list->bSimple   = bSimple;
 +    nbl_list->bCombined = bCombined;
 +
 +    nbl_list->nnbl = gmx_omp_nthreads_get(emntNonbonded);
 +
 +    if (!nbl_list->bCombined &&
 +        nbl_list->nnbl > NBNXN_BUFFERFLAG_MAX_THREADS)
 +    {
 +        gmx_fatal(FARGS, "%d OpenMP threads were requested. Since the non-bonded force buffer reduction is prohibitively slow with more than %d threads, we do not allow this. Use %d or less OpenMP threads.",
 +                  nbl_list->nnbl, NBNXN_BUFFERFLAG_MAX_THREADS, NBNXN_BUFFERFLAG_MAX_THREADS);
 +    }
 +
 +    snew(nbl_list->nbl, nbl_list->nnbl);
 +    /* Execute in order to avoid memory interleaving between threads */
 +#pragma omp parallel for num_threads(nbl_list->nnbl) schedule(static)
 +    for (i = 0; i < nbl_list->nnbl; i++)
 +    {
 +        /* Allocate the nblist data structure locally on each thread
 +         * to optimize memory access for NUMA architectures.
 +         */
 +        snew(nbl_list->nbl[i], 1);
 +
 +        /* Only list 0 is used on the GPU, use normal allocation for i>0 */
 +        if (i == 0)
 +        {
 +            nbnxn_init_pairlist(nbl_list->nbl[i], nbl_list->bSimple, alloc, free);
 +        }
 +        else
 +        {
 +            nbnxn_init_pairlist(nbl_list->nbl[i], nbl_list->bSimple, NULL, NULL);
 +        }
 +    }
 +}
 +
 +/* Print statistics of a pair list, used for debug output */
 +static void print_nblist_statistics_simple(FILE *fp, const nbnxn_pairlist_t *nbl,
 +                                           const nbnxn_search_t nbs, real rl)
 +{
 +    const nbnxn_grid_t *grid;
 +    int                 cs[SHIFTS];
 +    int                 s, i, j;
 +    int                 npexcl;
 +
 +    /* This code only produces correct statistics with domain decomposition */
 +    grid = &nbs->grid[0];
 +
 +    fprintf(fp, "nbl nci %d ncj %d\n",
 +            nbl->nci, nbl->ncj);
 +    fprintf(fp, "nbl na_sc %d rl %g ncp %d per cell %.1f atoms %.1f ratio %.2f\n",
 +            nbl->na_sc, rl, nbl->ncj, nbl->ncj/(double)grid->nc,
 +            nbl->ncj/(double)grid->nc*grid->na_sc,
 +            nbl->ncj/(double)grid->nc*grid->na_sc/(0.5*4.0/3.0*M_PI*rl*rl*rl*grid->nc*grid->na_sc/det(nbs->box)));
 +
 +    fprintf(fp, "nbl average j cell list length %.1f\n",
 +            0.25*nbl->ncj/(double)nbl->nci);
 +
 +    for (s = 0; s < SHIFTS; s++)
 +    {
 +        cs[s] = 0;
 +    }
 +    npexcl = 0;
 +    for (i = 0; i < nbl->nci; i++)
 +    {
 +        cs[nbl->ci[i].shift & NBNXN_CI_SHIFT] +=
 +            nbl->ci[i].cj_ind_end - nbl->ci[i].cj_ind_start;
 +
 +        j = nbl->ci[i].cj_ind_start;
 +        while (j < nbl->ci[i].cj_ind_end &&
 +               nbl->cj[j].excl != NBNXN_INT_MASK_ALL)
 +        {
 +            npexcl++;
 +            j++;
 +        }
 +    }
 +    fprintf(fp, "nbl cell pairs, total: %d excl: %d %.1f%%\n",
 +            nbl->ncj, npexcl, 100*npexcl/(double)nbl->ncj);
 +    for (s = 0; s < SHIFTS; s++)
 +    {
 +        if (cs[s] > 0)
 +        {
 +            fprintf(fp, "nbl shift %2d ncj %3d\n", s, cs[s]);
 +        }
 +    }
 +}
 +
 +/* Print statistics of a pair lists, used for debug output */
 +static void print_nblist_statistics_supersub(FILE *fp, const nbnxn_pairlist_t *nbl,
 +                                             const nbnxn_search_t nbs, real rl)
 +{
 +    const nbnxn_grid_t *grid;
 +    int                 i, j4, j, si, b;
 +    int                 c[GPU_NSUBCELL+1];
 +
 +    /* This code only produces correct statistics with domain decomposition */
 +    grid = &nbs->grid[0];
 +
 +    fprintf(fp, "nbl nsci %d ncj4 %d nsi %d excl4 %d\n",
 +            nbl->nsci, nbl->ncj4, nbl->nci_tot, nbl->nexcl);
 +    fprintf(fp, "nbl na_c %d rl %g ncp %d per cell %.1f atoms %.1f ratio %.2f\n",
 +            nbl->na_ci, rl, nbl->nci_tot, nbl->nci_tot/(double)grid->nsubc_tot,
 +            nbl->nci_tot/(double)grid->nsubc_tot*grid->na_c,
 +            nbl->nci_tot/(double)grid->nsubc_tot*grid->na_c/(0.5*4.0/3.0*M_PI*rl*rl*rl*grid->nsubc_tot*grid->na_c/det(nbs->box)));
 +
 +    fprintf(fp, "nbl average j super cell list length %.1f\n",
 +            0.25*nbl->ncj4/(double)nbl->nsci);
 +    fprintf(fp, "nbl average i sub cell list length %.1f\n",
 +            nbl->nci_tot/((double)nbl->ncj4));
 +
 +    for (si = 0; si <= GPU_NSUBCELL; si++)
 +    {
 +        c[si] = 0;
 +    }
 +    for (i = 0; i < nbl->nsci; i++)
 +    {
 +        for (j4 = nbl->sci[i].cj4_ind_start; j4 < nbl->sci[i].cj4_ind_end; j4++)
 +        {
 +            for (j = 0; j < NBNXN_GPU_JGROUP_SIZE; j++)
 +            {
 +                b = 0;
 +                for (si = 0; si < GPU_NSUBCELL; si++)
 +                {
 +                    if (nbl->cj4[j4].imei[0].imask & (1U << (j*GPU_NSUBCELL + si)))
 +                    {
 +                        b++;
 +                    }
 +                }
 +                c[b]++;
 +            }
 +        }
 +    }
 +    for (b = 0; b <= GPU_NSUBCELL; b++)
 +    {
 +        fprintf(fp, "nbl j-list #i-subcell %d %7d %4.1f\n",
 +                b, c[b], 100.0*c[b]/(double)(nbl->ncj4*NBNXN_GPU_JGROUP_SIZE));
 +    }
 +}
 +
 +/* Returns a pointer to the exclusion mask for cj4-unit cj4, warp warp */
 +static void low_get_nbl_exclusions(nbnxn_pairlist_t *nbl, int cj4,
 +                                   int warp, nbnxn_excl_t **excl)
 +{
 +    if (nbl->cj4[cj4].imei[warp].excl_ind == 0)
 +    {
 +        /* No exclusions set, make a new list entry */
 +        nbl->cj4[cj4].imei[warp].excl_ind = nbl->nexcl;
 +        nbl->nexcl++;
 +        *excl = &nbl->excl[nbl->cj4[cj4].imei[warp].excl_ind];
 +        set_no_excls(*excl);
 +    }
 +    else
 +    {
 +        /* We already have some exclusions, new ones can be added to the list */
 +        *excl = &nbl->excl[nbl->cj4[cj4].imei[warp].excl_ind];
 +    }
 +}
 +
 +/* Returns a pointer to the exclusion mask for cj4-unit cj4, warp warp,
 + * allocates extra memory, if necessary.
 + */
 +static void get_nbl_exclusions_1(nbnxn_pairlist_t *nbl, int cj4,
 +                                 int warp, nbnxn_excl_t **excl)
 +{
 +    if (nbl->cj4[cj4].imei[warp].excl_ind == 0)
 +    {
 +        /* We need to make a new list entry, check if we have space */
 +        check_excl_space(nbl, 1);
 +    }
 +    low_get_nbl_exclusions(nbl, cj4, warp, excl);
 +}
 +
 +/* Returns pointers to the exclusion mask for cj4-unit cj4 for both warps,
 + * allocates extra memory, if necessary.
 + */
 +static void get_nbl_exclusions_2(nbnxn_pairlist_t *nbl, int cj4,
 +                                 nbnxn_excl_t **excl_w0,
 +                                 nbnxn_excl_t **excl_w1)
 +{
 +    /* Check for space we might need */
 +    check_excl_space(nbl, 2);
 +
 +    low_get_nbl_exclusions(nbl, cj4, 0, excl_w0);
 +    low_get_nbl_exclusions(nbl, cj4, 1, excl_w1);
 +}
 +
 +/* Sets the self exclusions i=j and pair exclusions i>j */
 +static void set_self_and_newton_excls_supersub(nbnxn_pairlist_t *nbl,
 +                                               int cj4_ind, int sj_offset,
 +                                               int si)
 +{
 +    nbnxn_excl_t *excl[2];
 +    int           ei, ej, w;
 +
 +    /* Here we only set the set self and double pair exclusions */
 +
 +    get_nbl_exclusions_2(nbl, cj4_ind, &excl[0], &excl[1]);
 +
 +    /* Only minor < major bits set */
 +    for (ej = 0; ej < nbl->na_ci; ej++)
 +    {
 +        w = (ej>>2);
 +        for (ei = ej; ei < nbl->na_ci; ei++)
 +        {
 +            excl[w]->pair[(ej & (NBNXN_GPU_JGROUP_SIZE-1))*nbl->na_ci + ei] &=
 +                ~(1U << (sj_offset*GPU_NSUBCELL + si));
 +        }
 +    }
 +}
 +
 +/* Returns a diagonal or off-diagonal interaction mask for plain C lists */
 +static unsigned int get_imask(gmx_bool rdiag, int ci, int cj)
 +{
 +    return (rdiag && ci == cj ? NBNXN_INT_MASK_DIAG : NBNXN_INT_MASK_ALL);
 +}
 +
 +/* Returns a diagonal or off-diagonal interaction mask for SIMD128 lists */
- static unsigned int get_imask_x86_simd256(gmx_bool rdiag, int ci, int cj)
++static unsigned int get_imask_simd128(gmx_bool rdiag, int ci, int cj)
 +{
 +#ifndef GMX_DOUBLE /* cj-size = 4 */
 +    return (rdiag && ci == cj ? NBNXN_INT_MASK_DIAG : NBNXN_INT_MASK_ALL);
 +#else              /* cj-size = 2 */
 +    return (rdiag && ci*2 == cj ? NBNXN_INT_MASK_DIAG_J2_0 :
 +            (rdiag && ci*2+1 == cj ? NBNXN_INT_MASK_DIAG_J2_1 :
 +             NBNXN_INT_MASK_ALL));
 +#endif
 +}
 +
 +/* Returns a diagonal or off-diagonal interaction mask for SIMD256 lists */
- #define get_imask_x86_simd_4xn  get_imask_x86_simd128
++static unsigned int get_imask_simd256(gmx_bool rdiag, int ci, int cj)
 +{
 +#ifndef GMX_DOUBLE /* cj-size = 8 */
 +    return (rdiag && ci == cj*2 ? NBNXN_INT_MASK_DIAG_J8_0 :
 +            (rdiag && ci == cj*2+1 ? NBNXN_INT_MASK_DIAG_J8_1 :
 +             NBNXN_INT_MASK_ALL));
 +#else              /* cj-size = 4 */
 +    return (rdiag && ci == cj ? NBNXN_INT_MASK_DIAG : NBNXN_INT_MASK_ALL);
 +#endif
 +}
 +
 +#ifdef GMX_NBNXN_SIMD
 +#if GMX_NBNXN_SIMD_BITWIDTH == 128
- #define get_imask_x86_simd_4xn  get_imask_x86_simd256
- #define get_imask_x86_simd_2xnn get_imask_x86_simd128
++#define get_imask_simd_4xn  get_imask_simd128
 +#else
 +#if GMX_NBNXN_SIMD_BITWIDTH == 256
++#define get_imask_simd_4xn  get_imask_simd256
++#define get_imask_simd_2xnn get_imask_simd128
 +#else
 +#error "unsupported GMX_NBNXN_SIMD_BITWIDTH"
 +#endif
 +#endif
 +#endif
 +
 +/* Plain C code for making a pair list of cell ci vs cell cjf-cjl.
 + * Checks bounding box distances and possibly atom pair distances.
 + */
 +static void make_cluster_list_simple(const nbnxn_grid_t *gridj,
 +                                     nbnxn_pairlist_t *nbl,
 +                                     int ci, int cjf, int cjl,
 +                                     gmx_bool remove_sub_diag,
 +                                     const real *x_j,
 +                                     real rl2, float rbb2,
 +                                     int *ndistc)
 +{
 +    const nbnxn_list_work_t *work;
 +
 +    const float             *bb_ci;
 +    const real              *x_ci;
 +
 +    gmx_bool                 InRange;
 +    real                     d2;
 +    int                      cjf_gl, cjl_gl, cj;
 +
 +    work = nbl->work;
 +
 +    bb_ci = nbl->work->bb_ci;
 +    x_ci  = nbl->work->x_ci;
 +
 +    InRange = FALSE;
 +    while (!InRange && cjf <= cjl)
 +    {
 +        d2       = subc_bb_dist2(0, bb_ci, cjf, gridj->bb);
 +        *ndistc += 2;
 +
 +        /* Check if the distance is within the distance where
 +         * we use only the bounding box distance rbb,
 +         * or within the cut-off and there is at least one atom pair
 +         * within the cut-off.
 +         */
 +        if (d2 < rbb2)
 +        {
 +            InRange = TRUE;
 +        }
 +        else if (d2 < rl2)
 +        {
 +            int i, j;
 +
 +            cjf_gl = gridj->cell0 + cjf;
 +            for (i = 0; i < NBNXN_CPU_CLUSTER_I_SIZE && !InRange; i++)
 +            {
 +                for (j = 0; j < NBNXN_CPU_CLUSTER_I_SIZE; j++)
 +                {
 +                    InRange = InRange ||
 +                        (sqr(x_ci[i*STRIDE_XYZ+XX] - x_j[(cjf_gl*NBNXN_CPU_CLUSTER_I_SIZE+j)*STRIDE_XYZ+XX]) +
 +                         sqr(x_ci[i*STRIDE_XYZ+YY] - x_j[(cjf_gl*NBNXN_CPU_CLUSTER_I_SIZE+j)*STRIDE_XYZ+YY]) +
 +                         sqr(x_ci[i*STRIDE_XYZ+ZZ] - x_j[(cjf_gl*NBNXN_CPU_CLUSTER_I_SIZE+j)*STRIDE_XYZ+ZZ]) < rl2);
 +                }
 +            }
 +            *ndistc += NBNXN_CPU_CLUSTER_I_SIZE*NBNXN_CPU_CLUSTER_I_SIZE;
 +        }
 +        if (!InRange)
 +        {
 +            cjf++;
 +        }
 +    }
 +    if (!InRange)
 +    {
 +        return;
 +    }
 +
 +    InRange = FALSE;
 +    while (!InRange && cjl > cjf)
 +    {
 +        d2       = subc_bb_dist2(0, bb_ci, cjl, gridj->bb);
 +        *ndistc += 2;
 +
 +        /* Check if the distance is within the distance where
 +         * we use only the bounding box distance rbb,
 +         * or within the cut-off and there is at least one atom pair
 +         * within the cut-off.
 +         */
 +        if (d2 < rbb2)
 +        {
 +            InRange = TRUE;
 +        }
 +        else if (d2 < rl2)
 +        {
 +            int i, j;
 +
 +            cjl_gl = gridj->cell0 + cjl;
 +            for (i = 0; i < NBNXN_CPU_CLUSTER_I_SIZE && !InRange; i++)
 +            {
 +                for (j = 0; j < NBNXN_CPU_CLUSTER_I_SIZE; j++)
 +                {
 +                    InRange = InRange ||
 +                        (sqr(x_ci[i*STRIDE_XYZ+XX] - x_j[(cjl_gl*NBNXN_CPU_CLUSTER_I_SIZE+j)*STRIDE_XYZ+XX]) +
 +                         sqr(x_ci[i*STRIDE_XYZ+YY] - x_j[(cjl_gl*NBNXN_CPU_CLUSTER_I_SIZE+j)*STRIDE_XYZ+YY]) +
 +                         sqr(x_ci[i*STRIDE_XYZ+ZZ] - x_j[(cjl_gl*NBNXN_CPU_CLUSTER_I_SIZE+j)*STRIDE_XYZ+ZZ]) < rl2);
 +                }
 +            }
 +            *ndistc += NBNXN_CPU_CLUSTER_I_SIZE*NBNXN_CPU_CLUSTER_I_SIZE;
 +        }
 +        if (!InRange)
 +        {
 +            cjl--;
 +        }
 +    }
 +
 +    if (cjf <= cjl)
 +    {
 +        for (cj = cjf; cj <= cjl; cj++)
 +        {
 +            /* Store cj and the interaction mask */
 +            nbl->cj[nbl->ncj].cj   = gridj->cell0 + cj;
 +            nbl->cj[nbl->ncj].excl = get_imask(remove_sub_diag, ci, cj);
 +            nbl->ncj++;
 +        }
 +        /* Increase the closing index in i super-cell list */
 +        nbl->ci[nbl->nci].cj_ind_end = nbl->ncj;
 +    }
 +}
 +
 +#ifdef GMX_NBNXN_SIMD_4XN
 +#include "nbnxn_search_simd_4xn.h"
 +#endif
 +#ifdef GMX_NBNXN_SIMD_2XNN
 +#include "nbnxn_search_simd_2xnn.h"
 +#endif
 +
 +/* Plain C or SSE code for making a pair list of super-cell sci vs scj.
 + * Checks bounding box distances and possibly atom pair distances.
 + */
 +static void make_cluster_list_supersub(const nbnxn_search_t nbs,
 +                                       const nbnxn_grid_t *gridi,
 +                                       const nbnxn_grid_t *gridj,
 +                                       nbnxn_pairlist_t *nbl,
 +                                       int sci, int scj,
 +                                       gmx_bool sci_equals_scj,
 +                                       int stride, const real *x,
 +                                       real rl2, float rbb2,
 +                                       int *ndistc)
 +{
 +    int          na_c;
 +    int          npair;
 +    int          cjo, ci1, ci, cj, cj_gl;
 +    int          cj4_ind, cj_offset;
 +    unsigned     imask;
 +    nbnxn_cj4_t *cj4;
 +    const float *bb_ci;
 +    const real  *x_ci;
 +    float       *d2l, d2;
 +    int          w;
 +#define PRUNE_LIST_CPU_ONE
 +#ifdef PRUNE_LIST_CPU_ONE
 +    int  ci_last = -1;
 +#endif
 +
 +    d2l = nbl->work->d2;
 +
 +    bb_ci = nbl->work->bb_ci;
 +    x_ci  = nbl->work->x_ci;
 +
 +    na_c = gridj->na_c;
 +
 +    for (cjo = 0; cjo < gridj->nsubc[scj]; cjo++)
 +    {
 +        cj4_ind   = (nbl->work->cj_ind >> NBNXN_GPU_JGROUP_SIZE_2LOG);
 +        cj_offset = nbl->work->cj_ind - cj4_ind*NBNXN_GPU_JGROUP_SIZE;
 +        cj4       = &nbl->cj4[cj4_ind];
 +
 +        cj = scj*GPU_NSUBCELL + cjo;
 +
 +        cj_gl = gridj->cell0*GPU_NSUBCELL + cj;
 +
 +        /* Initialize this j-subcell i-subcell list */
 +        cj4->cj[cj_offset] = cj_gl;
 +        imask              = 0;
 +
 +        if (sci_equals_scj)
 +        {
 +            ci1 = cjo + 1;
 +        }
 +        else
 +        {
 +            ci1 = gridi->nsubc[sci];
 +        }
 +
 +#ifdef NBNXN_BBXXXX
 +        /* Determine all ci1 bb distances in one call with SSE */
 +        subc_bb_dist2_sse_xxxx(gridj->bb+(cj>>STRIDE_PBB_2LOG)*NNBSBB_XXXX+(cj & (STRIDE_PBB-1)),
 +                               ci1, bb_ci, d2l);
 +        *ndistc += na_c*2;
 +#endif
 +
 +        npair = 0;
 +        /* We use a fixed upper-bound instead of ci1 to help optimization */
 +        for (ci = 0; ci < GPU_NSUBCELL; ci++)
 +        {
 +            if (ci == ci1)
 +            {
 +                break;
 +            }
 +
 +#ifndef NBNXN_BBXXXX
 +            /* Determine the bb distance between ci and cj */
 +            d2l[ci]  = subc_bb_dist2(ci, bb_ci, cj, gridj->bb);
 +            *ndistc += 2;
 +#endif
 +            d2 = d2l[ci];
 +
 +#ifdef PRUNE_LIST_CPU_ALL
 +            /* Check if the distance is within the distance where
 +             * we use only the bounding box distance rbb,
 +             * or within the cut-off and there is at least one atom pair
 +             * within the cut-off. This check is very costly.
 +             */
 +            *ndistc += na_c*na_c;
 +            if (d2 < rbb2 ||
 +                (d2 < rl2 &&
 +#ifdef NBNXN_PBB_SSE
 +                 subc_in_range_sse8
 +#else
 +                 subc_in_range_x
 +#endif
 +                     (na_c, ci, x_ci, cj_gl, stride, x, rl2)))
 +#else
 +            /* Check if the distance between the two bounding boxes
 +             * in within the pair-list cut-off.
 +             */
 +            if (d2 < rl2)
 +#endif
 +            {
 +                /* Flag this i-subcell to be taken into account */
 +                imask |= (1U << (cj_offset*GPU_NSUBCELL+ci));
 +
 +#ifdef PRUNE_LIST_CPU_ONE
 +                ci_last = ci;
 +#endif
 +
 +                npair++;
 +            }
 +        }
 +
 +#ifdef PRUNE_LIST_CPU_ONE
 +        /* If we only found 1 pair, check if any atoms are actually
 +         * within the cut-off, so we could get rid of it.
 +         */
 +        if (npair == 1 && d2l[ci_last] >= rbb2)
 +        {
 +            /* Avoid using function pointers here, as it's slower */
 +            if (
 +#ifdef NBNXN_PBB_SSE
 +                !subc_in_range_sse8
 +#else
 +                !subc_in_range_x
 +#endif
 +                    (na_c, ci_last, x_ci, cj_gl, stride, x, rl2))
 +            {
 +                imask &= ~(1U << (cj_offset*GPU_NSUBCELL+ci_last));
 +                npair--;
 +            }
 +        }
 +#endif
 +
 +        if (npair > 0)
 +        {
 +            /* We have a useful sj entry, close it now */
 +
 +            /* Set the exclucions for the ci== sj entry.
 +             * Here we don't bother to check if this entry is actually flagged,
 +             * as it will nearly always be in the list.
 +             */
 +            if (sci_equals_scj)
 +            {
 +                set_self_and_newton_excls_supersub(nbl, cj4_ind, cj_offset, cjo);
 +            }
 +
 +            /* Copy the cluster interaction mask to the list */
 +            for (w = 0; w < NWARP; w++)
 +            {
 +                cj4->imei[w].imask |= imask;
 +            }
 +
 +            nbl->work->cj_ind++;
 +
 +            /* Keep the count */
 +            nbl->nci_tot += npair;
 +
 +            /* Increase the closing index in i super-cell list */
 +            nbl->sci[nbl->nsci].cj4_ind_end =
 +                ((nbl->work->cj_ind+NBNXN_GPU_JGROUP_SIZE-1) >> NBNXN_GPU_JGROUP_SIZE_2LOG);
 +        }
 +    }
 +}
 +
 +/* Set all atom-pair exclusions from the topology stored in excl
 + * as masks in the pair-list for simple list i-entry nbl_ci
 + */
 +static void set_ci_top_excls(const nbnxn_search_t nbs,
 +                             nbnxn_pairlist_t    *nbl,
 +                             gmx_bool             diagRemoved,
 +                             int                  na_ci_2log,
 +                             int                  na_cj_2log,
 +                             const nbnxn_ci_t    *nbl_ci,
 +                             const t_blocka      *excl)
 +{
 +    const int    *cell;
 +    int           ci;
 +    int           cj_ind_first, cj_ind_last;
 +    int           cj_first, cj_last;
 +    int           ndirect;
 +    int           i, ai, aj, si, eind, ge, se;
 +    int           found, cj_ind_0, cj_ind_1, cj_ind_m;
 +    int           cj_m;
 +    gmx_bool      Found_si;
 +    int           si_ind;
 +    nbnxn_excl_t *nbl_excl;
 +    int           inner_i, inner_e;
 +
 +    cell = nbs->cell;
 +
 +    if (nbl_ci->cj_ind_end == nbl_ci->cj_ind_start)
 +    {
 +        /* Empty list */
 +        return;
 +    }
 +
 +    ci = nbl_ci->ci;
 +
 +    cj_ind_first = nbl_ci->cj_ind_start;
 +    cj_ind_last  = nbl->ncj - 1;
 +
 +    cj_first = nbl->cj[cj_ind_first].cj;
 +    cj_last  = nbl->cj[cj_ind_last].cj;
 +
 +    /* Determine how many contiguous j-cells we have starting
 +     * from the first i-cell. This number can be used to directly
 +     * calculate j-cell indices for excluded atoms.
 +     */
 +    ndirect = 0;
 +    if (na_ci_2log == na_cj_2log)
 +    {
 +        while (cj_ind_first + ndirect <= cj_ind_last &&
 +               nbl->cj[cj_ind_first+ndirect].cj == ci + ndirect)
 +        {
 +            ndirect++;
 +        }
 +    }
 +#ifdef NBNXN_SEARCH_BB_SSE
 +    else
 +    {
 +        while (cj_ind_first + ndirect <= cj_ind_last &&
 +               nbl->cj[cj_ind_first+ndirect].cj == ci_to_cj(na_cj_2log, ci) + ndirect)
 +        {
 +            ndirect++;
 +        }
 +    }
 +#endif
 +
 +    /* Loop over the atoms in the i super-cell */
 +    for (i = 0; i < nbl->na_sc; i++)
 +    {
 +        ai = nbs->a[ci*nbl->na_sc+i];
 +        if (ai >= 0)
 +        {
 +            si  = (i>>na_ci_2log);
 +
 +            /* Loop over the topology-based exclusions for this i-atom */
 +            for (eind = excl->index[ai]; eind < excl->index[ai+1]; eind++)
 +            {
 +                aj = excl->a[eind];
 +
 +                if (aj == ai)
 +                {
 +                    /* The self exclusion are already set, save some time */
 +                    continue;
 +                }
 +
 +                ge = cell[aj];
 +
 +                /* Without shifts we only calculate interactions j>i
 +                 * for one-way pair-lists.
 +                 */
 +                if (diagRemoved && ge <= ci*nbl->na_sc + i)
 +                {
 +                    continue;
 +                }
 +
 +                se = (ge >> na_cj_2log);
 +
 +                /* Could the cluster se be in our list? */
 +                if (se >= cj_first && se <= cj_last)
 +                {
 +                    if (se < cj_first + ndirect)
 +                    {
 +                        /* We can calculate cj_ind directly from se */
 +                        found = cj_ind_first + se - cj_first;
 +                    }
 +                    else
 +                    {
 +                        /* Search for se using bisection */
 +                        found    = -1;
 +                        cj_ind_0 = cj_ind_first + ndirect;
 +                        cj_ind_1 = cj_ind_last + 1;
 +                        while (found == -1 && cj_ind_0 < cj_ind_1)
 +                        {
 +                            cj_ind_m = (cj_ind_0 + cj_ind_1)>>1;
 +
 +                            cj_m = nbl->cj[cj_ind_m].cj;
 +
 +                            if (se == cj_m)
 +                            {
 +                                found = cj_ind_m;
 +                            }
 +                            else if (se < cj_m)
 +                            {
 +                                cj_ind_1 = cj_ind_m;
 +                            }
 +                            else
 +                            {
 +                                cj_ind_0 = cj_ind_m + 1;
 +                            }
 +                        }
 +                    }
 +
 +                    if (found >= 0)
 +                    {
 +                        inner_i = i  - (si << na_ci_2log);
 +                        inner_e = ge - (se << na_cj_2log);
 +
 +                        nbl->cj[found].excl &= ~(1U<<((inner_i<<na_cj_2log) + inner_e));
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/* Set all atom-pair exclusions from the topology stored in excl
 + * as masks in the pair-list for i-super-cell entry nbl_sci
 + */
 +static void set_sci_top_excls(const nbnxn_search_t nbs,
 +                              nbnxn_pairlist_t    *nbl,
 +                              gmx_bool             diagRemoved,
 +                              int                  na_c_2log,
 +                              const nbnxn_sci_t   *nbl_sci,
 +                              const t_blocka      *excl)
 +{
 +    const int    *cell;
 +    int           na_c;
 +    int           sci;
 +    int           cj_ind_first, cj_ind_last;
 +    int           cj_first, cj_last;
 +    int           ndirect;
 +    int           i, ai, aj, si, eind, ge, se;
 +    int           found, cj_ind_0, cj_ind_1, cj_ind_m;
 +    int           cj_m;
 +    gmx_bool      Found_si;
 +    int           si_ind;
 +    nbnxn_excl_t *nbl_excl;
 +    int           inner_i, inner_e, w;
 +
 +    cell = nbs->cell;
 +
 +    na_c = nbl->na_ci;
 +
 +    if (nbl_sci->cj4_ind_end == nbl_sci->cj4_ind_start)
 +    {
 +        /* Empty list */
 +        return;
 +    }
 +
 +    sci = nbl_sci->sci;
 +
 +    cj_ind_first = nbl_sci->cj4_ind_start*NBNXN_GPU_JGROUP_SIZE;
 +    cj_ind_last  = nbl->work->cj_ind - 1;
 +
 +    cj_first = nbl->cj4[nbl_sci->cj4_ind_start].cj[0];
 +    cj_last  = nbl_cj(nbl, cj_ind_last);
 +
 +    /* Determine how many contiguous j-clusters we have starting
 +     * from the first i-cluster. This number can be used to directly
 +     * calculate j-cluster indices for excluded atoms.
 +     */
 +    ndirect = 0;
 +    while (cj_ind_first + ndirect <= cj_ind_last &&
 +           nbl_cj(nbl, cj_ind_first+ndirect) == sci*GPU_NSUBCELL + ndirect)
 +    {
 +        ndirect++;
 +    }
 +
 +    /* Loop over the atoms in the i super-cell */
 +    for (i = 0; i < nbl->na_sc; i++)
 +    {
 +        ai = nbs->a[sci*nbl->na_sc+i];
 +        if (ai >= 0)
 +        {
 +            si  = (i>>na_c_2log);
 +
 +            /* Loop over the topology-based exclusions for this i-atom */
 +            for (eind = excl->index[ai]; eind < excl->index[ai+1]; eind++)
 +            {
 +                aj = excl->a[eind];
 +
 +                if (aj == ai)
 +                {
 +                    /* The self exclusion are already set, save some time */
 +                    continue;
 +                }
 +
 +                ge = cell[aj];
 +
 +                /* Without shifts we only calculate interactions j>i
 +                 * for one-way pair-lists.
 +                 */
 +                if (diagRemoved && ge <= sci*nbl->na_sc + i)
 +                {
 +                    continue;
 +                }
 +
 +                se = ge>>na_c_2log;
 +                /* Could the cluster se be in our list? */
 +                if (se >= cj_first && se <= cj_last)
 +                {
 +                    if (se < cj_first + ndirect)
 +                    {
 +                        /* We can calculate cj_ind directly from se */
 +                        found = cj_ind_first + se - cj_first;
 +                    }
 +                    else
 +                    {
 +                        /* Search for se using bisection */
 +                        found    = -1;
 +                        cj_ind_0 = cj_ind_first + ndirect;
 +                        cj_ind_1 = cj_ind_last + 1;
 +                        while (found == -1 && cj_ind_0 < cj_ind_1)
 +                        {
 +                            cj_ind_m = (cj_ind_0 + cj_ind_1)>>1;
 +
 +                            cj_m = nbl_cj(nbl, cj_ind_m);
 +
 +                            if (se == cj_m)
 +                            {
 +                                found = cj_ind_m;
 +                            }
 +                            else if (se < cj_m)
 +                            {
 +                                cj_ind_1 = cj_ind_m;
 +                            }
 +                            else
 +                            {
 +                                cj_ind_0 = cj_ind_m + 1;
 +                            }
 +                        }
 +                    }
 +
 +                    if (found >= 0)
 +                    {
 +                        inner_i = i  - si*na_c;
 +                        inner_e = ge - se*na_c;
 +
 +/* Macro for getting the index of atom a within a cluster */
 +#define AMODCJ4(a)  ((a) & (NBNXN_GPU_JGROUP_SIZE - 1))
 +/* Macro for converting an atom number to a cluster number */
 +#define A2CJ4(a)    ((a) >> NBNXN_GPU_JGROUP_SIZE_2LOG)
 +/* Macro for getting the index of an i-atom within a warp */
 +#define AMODWI(a)   ((a) & (NBNXN_GPU_CLUSTER_SIZE/2 - 1))
 +
 +                        if (nbl_imask0(nbl, found) & (1U << (AMODCJ4(found)*GPU_NSUBCELL + si)))
 +                        {
 +                            w       = (inner_e >> 2);
 +
 +                            get_nbl_exclusions_1(nbl, A2CJ4(found), w, &nbl_excl);
 +
 +                            nbl_excl->pair[AMODWI(inner_e)*nbl->na_ci+inner_i] &=
 +                                ~(1U << (AMODCJ4(found)*GPU_NSUBCELL + si));
 +                        }
 +
 +#undef AMODCJ4
 +#undef A2CJ4
 +#undef AMODWI
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/* Reallocate the simple ci list for at least n entries */
 +static void nb_realloc_ci(nbnxn_pairlist_t *nbl, int n)
 +{
 +    nbl->ci_nalloc = over_alloc_small(n);
 +    nbnxn_realloc_void((void **)&nbl->ci,
 +                       nbl->nci*sizeof(*nbl->ci),
 +                       nbl->ci_nalloc*sizeof(*nbl->ci),
 +                       nbl->alloc, nbl->free);
 +}
 +
 +/* Reallocate the super-cell sci list for at least n entries */
 +static void nb_realloc_sci(nbnxn_pairlist_t *nbl, int n)
 +{
 +    nbl->sci_nalloc = over_alloc_small(n);
 +    nbnxn_realloc_void((void **)&nbl->sci,
 +                       nbl->nsci*sizeof(*nbl->sci),
 +                       nbl->sci_nalloc*sizeof(*nbl->sci),
 +                       nbl->alloc, nbl->free);
 +}
 +
 +/* Make a new ci entry at index nbl->nci */
 +static void new_ci_entry(nbnxn_pairlist_t *nbl, int ci, int shift, int flags,
 +                         nbnxn_list_work_t *work)
 +{
 +    if (nbl->nci + 1 > nbl->ci_nalloc)
 +    {
 +        nb_realloc_ci(nbl, nbl->nci+1);
 +    }
 +    nbl->ci[nbl->nci].ci            = ci;
 +    nbl->ci[nbl->nci].shift         = shift;
 +    /* Store the interaction flags along with the shift */
 +    nbl->ci[nbl->nci].shift        |= flags;
 +    nbl->ci[nbl->nci].cj_ind_start  = nbl->ncj;
 +    nbl->ci[nbl->nci].cj_ind_end    = nbl->ncj;
 +}
 +
 +/* Make a new sci entry at index nbl->nsci */
 +static void new_sci_entry(nbnxn_pairlist_t *nbl, int sci, int shift, int flags,
 +                          nbnxn_list_work_t *work)
 +{
 +    if (nbl->nsci + 1 > nbl->sci_nalloc)
 +    {
 +        nb_realloc_sci(nbl, nbl->nsci+1);
 +    }
 +    nbl->sci[nbl->nsci].sci           = sci;
 +    nbl->sci[nbl->nsci].shift         = shift;
 +    nbl->sci[nbl->nsci].cj4_ind_start = nbl->ncj4;
 +    nbl->sci[nbl->nsci].cj4_ind_end   = nbl->ncj4;
 +}
 +
 +/* Sort the simple j-list cj on exclusions.
 + * Entries with exclusions will all be sorted to the beginning of the list.
 + */
 +static void sort_cj_excl(nbnxn_cj_t *cj, int ncj,
 +                         nbnxn_list_work_t *work)
 +{
 +    int jnew, j;
 +
 +    if (ncj > work->cj_nalloc)
 +    {
 +        work->cj_nalloc = over_alloc_large(ncj);
 +        srenew(work->cj, work->cj_nalloc);
 +    }
 +
 +    /* Make a list of the j-cells involving exclusions */
 +    jnew = 0;
 +    for (j = 0; j < ncj; j++)
 +    {
 +        if (cj[j].excl != NBNXN_INT_MASK_ALL)
 +        {
 +            work->cj[jnew++] = cj[j];
 +        }
 +    }
 +    /* Check if there are exclusions at all or not just the first entry */
 +    if (!((jnew == 0) ||
 +          (jnew == 1 && cj[0].excl != NBNXN_INT_MASK_ALL)))
 +    {
 +        for (j = 0; j < ncj; j++)
 +        {
 +            if (cj[j].excl == NBNXN_INT_MASK_ALL)
 +            {
 +                work->cj[jnew++] = cj[j];
 +            }
 +        }
 +        for (j = 0; j < ncj; j++)
 +        {
 +            cj[j] = work->cj[j];
 +        }
 +    }
 +}
 +
 +/* Close this simple list i entry */
 +static void close_ci_entry_simple(nbnxn_pairlist_t *nbl)
 +{
 +    int jlen;
 +
 +    /* All content of the new ci entry have already been filled correctly,
 +     * we only need to increase the count here (for non empty lists).
 +     */
 +    jlen = nbl->ci[nbl->nci].cj_ind_end - nbl->ci[nbl->nci].cj_ind_start;
 +    if (jlen > 0)
 +    {
 +        sort_cj_excl(nbl->cj+nbl->ci[nbl->nci].cj_ind_start, jlen, nbl->work);
 +
 +        /* The counts below are used for non-bonded pair/flop counts
 +         * and should therefore match the available kernel setups.
 +         */
 +        if (!(nbl->ci[nbl->nci].shift & NBNXN_CI_DO_COUL(0)))
 +        {
 +            nbl->work->ncj_noq += jlen;
 +        }
 +        else if ((nbl->ci[nbl->nci].shift & NBNXN_CI_HALF_LJ(0)) ||
 +                 !(nbl->ci[nbl->nci].shift & NBNXN_CI_DO_LJ(0)))
 +        {
 +            nbl->work->ncj_hlj += jlen;
 +        }
 +
 +        nbl->nci++;
 +    }
 +}
 +
 +/* Split sci entry for load balancing on the GPU.
 + * Splitting ensures we have enough lists to fully utilize the whole GPU.
 + * With progBal we generate progressively smaller lists, which improves
 + * load balancing. As we only know the current count on our own thread,
 + * we will need to estimate the current total amount of i-entries.
 + * As the lists get concatenated later, this estimate depends
 + * both on nthread and our own thread index.
 + */
 +static void split_sci_entry(nbnxn_pairlist_t *nbl,
 +                            int nsp_max_av, gmx_bool progBal, int nc_bal,
 +                            int thread, int nthread)
 +{
 +    int nsci_est;
 +    int nsp_max;
 +    int cj4_start, cj4_end, j4len, cj4;
 +    int sci;
 +    int nsp, nsp_sci, nsp_cj4, nsp_cj4_e, nsp_cj4_p;
 +    int p;
 +
 +    if (progBal)
 +    {
 +        /* Estimate the total numbers of ci's of the nblist combined
 +         * over all threads using the target number of ci's.
 +         */
 +        nsci_est = nc_bal*thread/nthread + nbl->nsci;
 +
 +        /* The first ci blocks should be larger, to avoid overhead.
 +         * The last ci blocks should be smaller, to improve load balancing.
 +         */
 +        nsp_max = max(1,
 +                      nsp_max_av*nc_bal*3/(2*(nsci_est - 1 + nc_bal)));
 +    }
 +    else
 +    {
 +        nsp_max = nsp_max_av;
 +    }
 +
 +    cj4_start = nbl->sci[nbl->nsci-1].cj4_ind_start;
 +    cj4_end   = nbl->sci[nbl->nsci-1].cj4_ind_end;
 +    j4len     = cj4_end - cj4_start;
 +
 +    if (j4len > 1 && j4len*GPU_NSUBCELL*NBNXN_GPU_JGROUP_SIZE > nsp_max)
 +    {
 +        /* Remove the last ci entry and process the cj4's again */
 +        nbl->nsci -= 1;
 +
 +        sci        = nbl->nsci;
 +        nsp        = 0;
 +        nsp_sci    = 0;
 +        nsp_cj4_e  = 0;
 +        nsp_cj4    = 0;
 +        for (cj4 = cj4_start; cj4 < cj4_end; cj4++)
 +        {
 +            nsp_cj4_p = nsp_cj4;
 +            /* Count the number of cluster pairs in this cj4 group */
 +            nsp_cj4   = 0;
 +            for (p = 0; p < GPU_NSUBCELL*NBNXN_GPU_JGROUP_SIZE; p++)
 +            {
 +                nsp_cj4 += (nbl->cj4[cj4].imei[0].imask >> p) & 1;
 +            }
 +
 +            if (nsp_cj4 > 0 && nsp + nsp_cj4 > nsp_max)
 +            {
 +                /* Split the list at cj4 */
 +                nbl->sci[sci].cj4_ind_end = cj4;
 +                /* Create a new sci entry */
 +                sci++;
 +                nbl->nsci++;
 +                if (nbl->nsci+1 > nbl->sci_nalloc)
 +                {
 +                    nb_realloc_sci(nbl, nbl->nsci+1);
 +                }
 +                nbl->sci[sci].sci           = nbl->sci[nbl->nsci-1].sci;
 +                nbl->sci[sci].shift         = nbl->sci[nbl->nsci-1].shift;
 +                nbl->sci[sci].cj4_ind_start = cj4;
 +                nsp_sci                     = nsp;
 +                nsp_cj4_e                   = nsp_cj4_p;
 +                nsp                         = 0;
 +            }
 +            nsp += nsp_cj4;
 +        }
 +
 +        /* Put the remaining cj4's in the last sci entry */
 +        nbl->sci[sci].cj4_ind_end = cj4_end;
 +
 +        /* Possibly balance out the last two sci's
 +         * by moving the last cj4 of the second last sci.
 +         */
 +        if (nsp_sci - nsp_cj4_e >= nsp + nsp_cj4_e)
 +        {
 +            nbl->sci[sci-1].cj4_ind_end--;
 +            nbl->sci[sci].cj4_ind_start--;
 +        }
 +
 +        nbl->nsci++;
 +    }
 +}
 +
 +/* Clost this super/sub list i entry */
 +static void close_ci_entry_supersub(nbnxn_pairlist_t *nbl,
 +                                    int nsp_max_av,
 +                                    gmx_bool progBal, int nc_bal,
 +                                    int thread, int nthread)
 +{
 +    int j4len, tlen;
 +    int nb, b;
 +
 +    /* All content of the new ci entry have already been filled correctly,
 +     * we only need to increase the count here (for non empty lists).
 +     */
 +    j4len = nbl->sci[nbl->nsci].cj4_ind_end - nbl->sci[nbl->nsci].cj4_ind_start;
 +    if (j4len > 0)
 +    {
 +        /* We can only have complete blocks of 4 j-entries in a list,
 +         * so round the count up before closing.
 +         */
 +        nbl->ncj4         = ((nbl->work->cj_ind + NBNXN_GPU_JGROUP_SIZE - 1) >> NBNXN_GPU_JGROUP_SIZE_2LOG);
 +        nbl->work->cj_ind = nbl->ncj4*NBNXN_GPU_JGROUP_SIZE;
 +
 +        nbl->nsci++;
 +
 +        if (nsp_max_av > 0)
 +        {
 +            /* Measure the size of the new entry and potentially split it */
 +            split_sci_entry(nbl, nsp_max_av, progBal, nc_bal, thread, nthread);
 +        }
 +    }
 +}
 +
 +/* Syncs the working array before adding another grid pair to the list */
 +static void sync_work(nbnxn_pairlist_t *nbl)
 +{
 +    if (!nbl->bSimple)
 +    {
 +        nbl->work->cj_ind   = nbl->ncj4*NBNXN_GPU_JGROUP_SIZE;
 +        nbl->work->cj4_init = nbl->ncj4;
 +    }
 +}
 +
 +/* Clears an nbnxn_pairlist_t data structure */
 +static void clear_pairlist(nbnxn_pairlist_t *nbl)
 +{
 +    nbl->nci           = 0;
 +    nbl->nsci          = 0;
 +    nbl->ncj           = 0;
 +    nbl->ncj4          = 0;
 +    nbl->nci_tot       = 0;
 +    nbl->nexcl         = 1;
 +
 +    nbl->work->ncj_noq = 0;
 +    nbl->work->ncj_hlj = 0;
 +}
 +
 +/* Sets a simple list i-cell bounding box, including PBC shift */
 +static void set_icell_bb_simple(const float *bb, int ci,
 +                                real shx, real shy, real shz,
 +                                float *bb_ci)
 +{
 +    int ia;
 +
 +    ia           = ci*NNBSBB_B;
 +    bb_ci[BBL_X] = bb[ia+BBL_X] + shx;
 +    bb_ci[BBL_Y] = bb[ia+BBL_Y] + shy;
 +    bb_ci[BBL_Z] = bb[ia+BBL_Z] + shz;
 +    bb_ci[BBU_X] = bb[ia+BBU_X] + shx;
 +    bb_ci[BBU_Y] = bb[ia+BBU_Y] + shy;
 +    bb_ci[BBU_Z] = bb[ia+BBU_Z] + shz;
 +}
 +
 +/* Sets a super-cell and sub cell bounding boxes, including PBC shift */
 +static void set_icell_bb_supersub(const float *bb, int ci,
 +                                  real shx, real shy, real shz,
 +                                  float *bb_ci)
 +{
 +    int ia, m, i;
 +
 +#ifdef NBNXN_BBXXXX
 +    ia = ci*(GPU_NSUBCELL>>STRIDE_PBB_2LOG)*NNBSBB_XXXX;
 +    for (m = 0; m < (GPU_NSUBCELL>>STRIDE_PBB_2LOG)*NNBSBB_XXXX; m += NNBSBB_XXXX)
 +    {
 +        for (i = 0; i < STRIDE_PBB; i++)
 +        {
 +            bb_ci[m+0*STRIDE_PBB+i] = bb[ia+m+0*STRIDE_PBB+i] + shx;
 +            bb_ci[m+1*STRIDE_PBB+i] = bb[ia+m+1*STRIDE_PBB+i] + shy;
 +            bb_ci[m+2*STRIDE_PBB+i] = bb[ia+m+2*STRIDE_PBB+i] + shz;
 +            bb_ci[m+3*STRIDE_PBB+i] = bb[ia+m+3*STRIDE_PBB+i] + shx;
 +            bb_ci[m+4*STRIDE_PBB+i] = bb[ia+m+4*STRIDE_PBB+i] + shy;
 +            bb_ci[m+5*STRIDE_PBB+i] = bb[ia+m+5*STRIDE_PBB+i] + shz;
 +        }
 +    }
 +#else
 +    ia = ci*GPU_NSUBCELL*NNBSBB_B;
 +    for (i = 0; i < GPU_NSUBCELL*NNBSBB_B; i += NNBSBB_B)
 +    {
 +        bb_ci[i+BBL_X] = bb[ia+i+BBL_X] + shx;
 +        bb_ci[i+BBL_Y] = bb[ia+i+BBL_Y] + shy;
 +        bb_ci[i+BBL_Z] = bb[ia+i+BBL_Z] + shz;
 +        bb_ci[i+BBU_X] = bb[ia+i+BBU_X] + shx;
 +        bb_ci[i+BBU_Y] = bb[ia+i+BBU_Y] + shy;
 +        bb_ci[i+BBU_Z] = bb[ia+i+BBU_Z] + shz;
 +    }
 +#endif
 +}
 +
 +/* Copies PBC shifted i-cell atom coordinates x,y,z to working array */
 +static void icell_set_x_simple(int ci,
 +                               real shx, real shy, real shz,
 +                               int na_c,
 +                               int stride, const real *x,
 +                               nbnxn_list_work_t *work)
 +{
 +    int  ia, i;
 +
 +    ia = ci*NBNXN_CPU_CLUSTER_I_SIZE;
 +
 +    for (i = 0; i < NBNXN_CPU_CLUSTER_I_SIZE; i++)
 +    {
 +        work->x_ci[i*STRIDE_XYZ+XX] = x[(ia+i)*stride+XX] + shx;
 +        work->x_ci[i*STRIDE_XYZ+YY] = x[(ia+i)*stride+YY] + shy;
 +        work->x_ci[i*STRIDE_XYZ+ZZ] = x[(ia+i)*stride+ZZ] + shz;
 +    }
 +}
 +
 +/* Copies PBC shifted super-cell atom coordinates x,y,z to working array */
 +static void icell_set_x_supersub(int ci,
 +                                 real shx, real shy, real shz,
 +                                 int na_c,
 +                                 int stride, const real *x,
 +                                 nbnxn_list_work_t *work)
 +{
 +    int  ia, i;
 +    real *x_ci;
 +
 +    x_ci = work->x_ci;
 +
 +    ia = ci*GPU_NSUBCELL*na_c;
 +    for (i = 0; i < GPU_NSUBCELL*na_c; i++)
 +    {
 +        x_ci[i*DIM + XX] = x[(ia+i)*stride + XX] + shx;
 +        x_ci[i*DIM + YY] = x[(ia+i)*stride + YY] + shy;
 +        x_ci[i*DIM + ZZ] = x[(ia+i)*stride + ZZ] + shz;
 +    }
 +}
 +
 +#ifdef NBNXN_SEARCH_BB_SSE
 +/* Copies PBC shifted super-cell packed atom coordinates to working array */
 +static void icell_set_x_supersub_sse8(int ci,
 +                                      real shx, real shy, real shz,
 +                                      int na_c,
 +                                      int stride, const real *x,
 +                                      nbnxn_list_work_t *work)
 +{
 +    int  si, io, ia, i, j;
 +    real *x_ci;
 +
 +    x_ci = work->x_ci;
 +
 +    for (si = 0; si < GPU_NSUBCELL; si++)
 +    {
 +        for (i = 0; i < na_c; i += STRIDE_PBB)
 +        {
 +            io = si*na_c + i;
 +            ia = ci*GPU_NSUBCELL*na_c + io;
 +            for (j = 0; j < STRIDE_PBB; j++)
 +            {
 +                x_ci[io*DIM + j + XX*STRIDE_PBB] = x[(ia+j)*stride+XX] + shx;
 +                x_ci[io*DIM + j + YY*STRIDE_PBB] = x[(ia+j)*stride+YY] + shy;
 +                x_ci[io*DIM + j + ZZ*STRIDE_PBB] = x[(ia+j)*stride+ZZ] + shz;
 +            }
 +        }
 +    }
 +}
 +#endif
 +
 +static real nbnxn_rlist_inc_nonloc_fac = 0.6;
 +
 +/* Due to the cluster size the effective pair-list is longer than
 + * that of a simple atom pair-list. This function gives the extra distance.
 + */
 +real nbnxn_get_rlist_effective_inc(int cluster_size, real atom_density)
 +{
 +    return ((0.5 + nbnxn_rlist_inc_nonloc_fac)*sqr(((cluster_size) - 1.0)/(cluster_size))*pow((cluster_size)/(atom_density), 1.0/3.0));
 +}
 +
 +/* Estimates the interaction volume^2 for non-local interactions */
 +static real nonlocal_vol2(const gmx_domdec_zones_t *zones, rvec ls, real r)
 +{
 +    int  z, d;
 +    real cl, ca, za;
 +    real vold_est;
 +    real vol2_est_tot;
 +
 +    vol2_est_tot = 0;
 +
 +    /* Here we simply add up the volumes of 1, 2 or 3 1D decomposition
 +     * not home interaction volume^2. As these volumes are not additive,
 +     * this is an overestimate, but it would only be significant in the limit
 +     * of small cells, where we anyhow need to split the lists into
 +     * as small parts as possible.
 +     */
 +
 +    for (z = 0; z < zones->n; z++)
 +    {
 +        if (zones->shift[z][XX] + zones->shift[z][YY] + zones->shift[z][ZZ] == 1)
 +        {
 +            cl = 0;
 +            ca = 1;
 +            za = 1;
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if (zones->shift[z][d] == 0)
 +                {
 +                    cl += 0.5*ls[d];
 +                    ca *= ls[d];
 +                    za *= zones->size[z].x1[d] - zones->size[z].x0[d];
 +                }
 +            }
 +
 +            /* 4 octants of a sphere */
 +            vold_est  = 0.25*M_PI*r*r*r*r;
 +            /* 4 quarter pie slices on the edges */
 +            vold_est += 4*cl*M_PI/6.0*r*r*r;
 +            /* One rectangular volume on a face */
 +            vold_est += ca*0.5*r*r;
 +
 +            vol2_est_tot += vold_est*za;
 +        }
 +    }
 +
 +    return vol2_est_tot;
 +}
 +
 +/* Estimates the average size of a full j-list for super/sub setup */
 +static int get_nsubpair_max(const nbnxn_search_t nbs,
 +                            int                  iloc,
 +                            real                 rlist,
 +                            int                  min_ci_balanced)
 +{
 +    const nbnxn_grid_t *grid;
 +    rvec ls;
 +    real xy_diag2, r_eff_sup, vol_est, nsp_est, nsp_est_nl;
 +    int  nsubpair_max;
 +
 +    grid = &nbs->grid[0];
 +
 +    ls[XX] = (grid->c1[XX] - grid->c0[XX])/(grid->ncx*GPU_NSUBCELL_X);
 +    ls[YY] = (grid->c1[YY] - grid->c0[YY])/(grid->ncy*GPU_NSUBCELL_Y);
 +    ls[ZZ] = (grid->c1[ZZ] - grid->c0[ZZ])*grid->ncx*grid->ncy/(grid->nc*GPU_NSUBCELL_Z);
 +
 +    /* The average squared length of the diagonal of a sub cell */
 +    xy_diag2 = ls[XX]*ls[XX] + ls[YY]*ls[YY] + ls[ZZ]*ls[ZZ];
 +
 +    /* The formulas below are a heuristic estimate of the average nsj per si*/
 +    r_eff_sup = rlist + nbnxn_rlist_inc_nonloc_fac*sqr((grid->na_c - 1.0)/grid->na_c)*sqrt(xy_diag2/3);
 +
 +    if (!nbs->DomDec || nbs->zones->n == 1)
 +    {
 +        nsp_est_nl = 0;
 +    }
 +    else
 +    {
 +        nsp_est_nl =
 +            sqr(grid->atom_density/grid->na_c)*
 +            nonlocal_vol2(nbs->zones, ls, r_eff_sup);
 +    }
 +
 +    if (LOCAL_I(iloc))
 +    {
 +        /* Sub-cell interacts with itself */
 +        vol_est  = ls[XX]*ls[YY]*ls[ZZ];
 +        /* 6/2 rectangular volume on the faces */
 +        vol_est += (ls[XX]*ls[YY] + ls[XX]*ls[ZZ] + ls[YY]*ls[ZZ])*r_eff_sup;
 +        /* 12/2 quarter pie slices on the edges */
 +        vol_est += 2*(ls[XX] + ls[YY] + ls[ZZ])*0.25*M_PI*sqr(r_eff_sup);
 +        /* 4 octants of a sphere */
 +        vol_est += 0.5*4.0/3.0*M_PI*pow(r_eff_sup, 3);
 +
 +        nsp_est = grid->nsubc_tot*vol_est*grid->atom_density/grid->na_c;
 +
 +        /* Subtract the non-local pair count */
 +        nsp_est -= nsp_est_nl;
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "nsp_est local %5.1f non-local %5.1f\n",
 +                    nsp_est, nsp_est_nl);
 +        }
 +    }
 +    else
 +    {
 +        nsp_est = nsp_est_nl;
 +    }
 +
 +    if (min_ci_balanced <= 0 || grid->nc >= min_ci_balanced || grid->nc == 0)
 +    {
 +        /* We don't need to worry */
 +        nsubpair_max = -1;
 +    }
 +    else
 +    {
 +        /* Thus the (average) maximum j-list size should be as follows */
 +        nsubpair_max = max(1, (int)(nsp_est/min_ci_balanced+0.5));
 +
 +        /* Since the target value is a maximum (this avoids high outliers,
 +         * which lead to load imbalance), not average, we add half the
 +         * number of pairs in a cj4 block to get the average about right.
 +         */
 +        nsubpair_max += GPU_NSUBCELL*NBNXN_GPU_JGROUP_SIZE/2;
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "nbl nsp estimate %.1f, nsubpair_max %d\n",
 +                nsp_est, nsubpair_max);
 +    }
 +
 +    return nsubpair_max;
 +}
 +
 +/* Debug list print function */
 +static void print_nblist_ci_cj(FILE *fp, const nbnxn_pairlist_t *nbl)
 +{
 +    int i, j;
 +
 +    for (i = 0; i < nbl->nci; i++)
 +    {
 +        fprintf(fp, "ci %4d  shift %2d  ncj %3d\n",
 +                nbl->ci[i].ci, nbl->ci[i].shift,
 +                nbl->ci[i].cj_ind_end - nbl->ci[i].cj_ind_start);
 +
 +        for (j = nbl->ci[i].cj_ind_start; j < nbl->ci[i].cj_ind_end; j++)
 +        {
 +            fprintf(fp, "  cj %5d  imask %x\n",
 +                    nbl->cj[j].cj,
 +                    nbl->cj[j].excl);
 +        }
 +    }
 +}
 +
 +/* Debug list print function */
 +static void print_nblist_sci_cj(FILE *fp, const nbnxn_pairlist_t *nbl)
 +{
 +    int i, j4, j, ncp, si;
 +
 +    for (i = 0; i < nbl->nsci; i++)
 +    {
 +        fprintf(fp, "ci %4d  shift %2d  ncj4 %2d\n",
 +                nbl->sci[i].sci, nbl->sci[i].shift,
 +                nbl->sci[i].cj4_ind_end - nbl->sci[i].cj4_ind_start);
 +
 +        ncp = 0;
 +        for (j4 = nbl->sci[i].cj4_ind_start; j4 < nbl->sci[i].cj4_ind_end; j4++)
 +        {
 +            for (j = 0; j < NBNXN_GPU_JGROUP_SIZE; j++)
 +            {
 +                fprintf(fp, "  sj %5d  imask %x\n",
 +                        nbl->cj4[j4].cj[j],
 +                        nbl->cj4[j4].imei[0].imask);
 +                for (si = 0; si < GPU_NSUBCELL; si++)
 +                {
 +                    if (nbl->cj4[j4].imei[0].imask & (1U << (j*GPU_NSUBCELL + si)))
 +                    {
 +                        ncp++;
 +                    }
 +                }
 +            }
 +        }
 +        fprintf(fp, "ci %4d  shift %2d  ncj4 %2d ncp %3d\n",
 +                nbl->sci[i].sci, nbl->sci[i].shift,
 +                nbl->sci[i].cj4_ind_end - nbl->sci[i].cj4_ind_start,
 +                ncp);
 +    }
 +}
 +
 +/* Combine pair lists *nbl generated on multiple threads nblc */
 +static void combine_nblists(int nnbl, nbnxn_pairlist_t **nbl,
 +                            nbnxn_pairlist_t *nblc)
 +{
 +    int nsci, ncj4, nexcl;
 +    int n, i;
 +
 +    if (nblc->bSimple)
 +    {
 +        gmx_incons("combine_nblists does not support simple lists");
 +    }
 +
 +    nsci  = nblc->nsci;
 +    ncj4  = nblc->ncj4;
 +    nexcl = nblc->nexcl;
 +    for (i = 0; i < nnbl; i++)
 +    {
 +        nsci  += nbl[i]->nsci;
 +        ncj4  += nbl[i]->ncj4;
 +        nexcl += nbl[i]->nexcl;
 +    }
 +
 +    if (nsci > nblc->sci_nalloc)
 +    {
 +        nb_realloc_sci(nblc, nsci);
 +    }
 +    if (ncj4 > nblc->cj4_nalloc)
 +    {
 +        nblc->cj4_nalloc = over_alloc_small(ncj4);
 +        nbnxn_realloc_void((void **)&nblc->cj4,
 +                           nblc->ncj4*sizeof(*nblc->cj4),
 +                           nblc->cj4_nalloc*sizeof(*nblc->cj4),
 +                           nblc->alloc, nblc->free);
 +    }
 +    if (nexcl > nblc->excl_nalloc)
 +    {
 +        nblc->excl_nalloc = over_alloc_small(nexcl);
 +        nbnxn_realloc_void((void **)&nblc->excl,
 +                           nblc->nexcl*sizeof(*nblc->excl),
 +                           nblc->excl_nalloc*sizeof(*nblc->excl),
 +                           nblc->alloc, nblc->free);
 +    }
 +
 +    /* Each thread should copy its own data to the combined arrays,
 +     * as otherwise data will go back and forth between different caches.
 +     */
 +#pragma omp parallel for num_threads(gmx_omp_nthreads_get(emntPairsearch)) schedule(static)
 +    for (n = 0; n < nnbl; n++)
 +    {
 +        int sci_offset;
 +        int cj4_offset;
 +        int ci_offset;
 +        int excl_offset;
 +        int i, j4;
 +        const nbnxn_pairlist_t *nbli;
 +
 +        /* Determine the offset in the combined data for our thread */
 +        sci_offset  = nblc->nsci;
 +        cj4_offset  = nblc->ncj4;
 +        ci_offset   = nblc->nci_tot;
 +        excl_offset = nblc->nexcl;
 +
 +        for (i = 0; i < n; i++)
 +        {
 +            sci_offset  += nbl[i]->nsci;
 +            cj4_offset  += nbl[i]->ncj4;
 +            ci_offset   += nbl[i]->nci_tot;
 +            excl_offset += nbl[i]->nexcl;
 +        }
 +
 +        nbli = nbl[n];
 +
 +        for (i = 0; i < nbli->nsci; i++)
 +        {
 +            nblc->sci[sci_offset+i]                = nbli->sci[i];
 +            nblc->sci[sci_offset+i].cj4_ind_start += cj4_offset;
 +            nblc->sci[sci_offset+i].cj4_ind_end   += cj4_offset;
 +        }
 +
 +        for (j4 = 0; j4 < nbli->ncj4; j4++)
 +        {
 +            nblc->cj4[cj4_offset+j4]                   = nbli->cj4[j4];
 +            nblc->cj4[cj4_offset+j4].imei[0].excl_ind += excl_offset;
 +            nblc->cj4[cj4_offset+j4].imei[1].excl_ind += excl_offset;
 +        }
 +
 +        for (j4 = 0; j4 < nbli->nexcl; j4++)
 +        {
 +            nblc->excl[excl_offset+j4] = nbli->excl[j4];
 +        }
 +    }
 +
 +    for (n = 0; n < nnbl; n++)
 +    {
 +        nblc->nsci    += nbl[n]->nsci;
 +        nblc->ncj4    += nbl[n]->ncj4;
 +        nblc->nci_tot += nbl[n]->nci_tot;
 +        nblc->nexcl   += nbl[n]->nexcl;
 +    }
 +}
 +
 +/* Returns the next ci to be processes by our thread */
 +static gmx_bool next_ci(const nbnxn_grid_t *grid,
 +                        int conv,
 +                        int nth, int ci_block,
 +                        int *ci_x, int *ci_y,
 +                        int *ci_b, int *ci)
 +{
 +    (*ci_b)++;
 +    (*ci)++;
 +
 +    if (*ci_b == ci_block)
 +    {
 +        /* Jump to the next block assigned to this task */
 +        *ci   += (nth - 1)*ci_block;
 +        *ci_b  = 0;
 +    }
 +
 +    if (*ci >= grid->nc*conv)
 +    {
 +        return FALSE;
 +    }
 +
 +    while (*ci >= grid->cxy_ind[*ci_x*grid->ncy + *ci_y + 1]*conv)
 +    {
 +        *ci_y += 1;
 +        if (*ci_y == grid->ncy)
 +        {
 +            *ci_x += 1;
 +            *ci_y  = 0;
 +        }
 +    }
 +
 +    return TRUE;
 +}
 +
 +/* Returns the distance^2 for which we put cell pairs in the list
 + * without checking atom pair distances. This is usually < rlist^2.
 + */
 +static float boundingbox_only_distance2(const nbnxn_grid_t *gridi,
 +                                        const nbnxn_grid_t *gridj,
 +                                        real                rlist,
 +                                        gmx_bool            simple)
 +{
 +    /* If the distance between two sub-cell bounding boxes is less
 +     * than this distance, do not check the distance between
 +     * all particle pairs in the sub-cell, since then it is likely
 +     * that the box pair has atom pairs within the cut-off.
 +     * We use the nblist cut-off minus 0.5 times the average x/y diagonal
 +     * spacing of the sub-cells. Around 40% of the checked pairs are pruned.
 +     * Using more than 0.5 gains at most 0.5%.
 +     * If forces are calculated more than twice, the performance gain
 +     * in the force calculation outweighs the cost of checking.
 +     * Note that with subcell lists, the atom-pair distance check
 +     * is only performed when only 1 out of 8 sub-cells in within range,
 +     * this is because the GPU is much faster than the cpu.
 +     */
 +    real bbx, bby;
 +    real rbb2;
 +
 +    bbx = 0.5*(gridi->sx + gridj->sx);
 +    bby = 0.5*(gridi->sy + gridj->sy);
 +    if (!simple)
 +    {
 +        bbx /= GPU_NSUBCELL_X;
 +        bby /= GPU_NSUBCELL_Y;
 +    }
 +
 +    rbb2 = sqr(max(0, rlist - 0.5*sqrt(bbx*bbx + bby*bby)));
 +
 +#ifndef GMX_DOUBLE
 +    return rbb2;
 +#else
 +    return (float)((1+GMX_FLOAT_EPS)*rbb2);
 +#endif
 +}
 +
 +static int get_ci_block_size(const nbnxn_grid_t *gridi,
 +                             gmx_bool bDomDec, int nth)
 +{
 +    const int ci_block_enum      = 5;
 +    const int ci_block_denom     = 11;
 +    const int ci_block_min_atoms = 16;
 +    int ci_block;
 +
 +    /* Here we decide how to distribute the blocks over the threads.
 +     * We use prime numbers to try to avoid that the grid size becomes
 +     * a multiple of the number of threads, which would lead to some
 +     * threads getting "inner" pairs and others getting boundary pairs,
 +     * which in turns will lead to load imbalance between threads.
 +     * Set the block size as 5/11/ntask times the average number of cells
 +     * in a y,z slab. This should ensure a quite uniform distribution
 +     * of the grid parts of the different thread along all three grid
 +     * zone boundaries with 3D domain decomposition. At the same time
 +     * the blocks will not become too small.
 +     */
 +    ci_block = (gridi->nc*ci_block_enum)/(ci_block_denom*gridi->ncx*nth);
 +
 +    /* Ensure the blocks are not too small: avoids cache invalidation */
 +    if (ci_block*gridi->na_sc < ci_block_min_atoms)
 +    {
 +        ci_block = (ci_block_min_atoms + gridi->na_sc - 1)/gridi->na_sc;
 +    }
 +
 +    /* Without domain decomposition
 +     * or with less than 3 blocks per task, divide in nth blocks.
 +     */
 +    if (!bDomDec || ci_block*3*nth > gridi->nc)
 +    {
 +        ci_block = (gridi->nc + nth - 1)/nth;
 +    }
 +
 +    return ci_block;
 +}
 +
 +/* Generates the part of pair-list nbl assigned to our thread */
 +static void nbnxn_make_pairlist_part(const nbnxn_search_t nbs,
 +                                     const nbnxn_grid_t *gridi,
 +                                     const nbnxn_grid_t *gridj,
 +                                     nbnxn_search_work_t *work,
 +                                     const nbnxn_atomdata_t *nbat,
 +                                     const t_blocka *excl,
 +                                     real rlist,
 +                                     int nb_kernel_type,
 +                                     int ci_block,
 +                                     gmx_bool bFBufferFlag,
 +                                     int nsubpair_max,
 +                                     gmx_bool progBal,
 +                                     int min_ci_balanced,
 +                                     int th, int nth,
 +                                     nbnxn_pairlist_t *nbl)
 +{
 +    int  na_cj_2log;
 +    matrix box;
 +    real rl2;
 +    float rbb2;
 +    int  d;
 +    int  ci_b, ci, ci_x, ci_y, ci_xy, cj;
 +    ivec shp;
 +    int  tx, ty, tz;
 +    int  shift;
 +    gmx_bool bMakeList;
 +    real shx, shy, shz;
 +    int  conv_i, cell0_i;
 +    const float *bb_i, *bbcz_i, *bbcz_j;
 +    const int *flags_i;
 +    real bx0, bx1, by0, by1, bz0, bz1;
 +    real bz1_frac;
 +    real d2cx, d2z, d2z_cx, d2z_cy, d2zx, d2zxy, d2xy;
 +    int  cxf, cxl, cyf, cyf_x, cyl;
 +    int  cx, cy;
 +    int  c0, c1, cs, cf, cl;
 +    int  ndistc;
 +    int  ncpcheck;
 +    int  gridi_flag_shift = 0, gridj_flag_shift = 0;
 +    unsigned *gridj_flag  = NULL;
 +    int  ncj_old_i, ncj_old_j;
 +
 +    nbs_cycle_start(&work->cc[enbsCCsearch]);
 +
 +    if (gridj->bSimple != nbl->bSimple)
 +    {
 +        gmx_incons("Grid incompatible with pair-list");
 +    }
 +
 +    sync_work(nbl);
 +    nbl->na_sc = gridj->na_sc;
 +    nbl->na_ci = gridj->na_c;
 +    nbl->na_cj = nbnxn_kernel_to_cj_size(nb_kernel_type);
 +    na_cj_2log = get_2log(nbl->na_cj);
 +
 +    nbl->rlist  = rlist;
 +
 +    if (bFBufferFlag)
 +    {
 +        /* Determine conversion of clusters to flag blocks */
 +        gridi_flag_shift = 0;
 +        while ((nbl->na_ci<<gridi_flag_shift) < NBNXN_BUFFERFLAG_SIZE)
 +        {
 +            gridi_flag_shift++;
 +        }
 +        gridj_flag_shift = 0;
 +        while ((nbl->na_cj<<gridj_flag_shift) < NBNXN_BUFFERFLAG_SIZE)
 +        {
 +            gridj_flag_shift++;
 +        }
 +
 +        gridj_flag = work->buffer_flags.flag;
 +    }
 +
 +    copy_mat(nbs->box, box);
 +
 +    rl2 = nbl->rlist*nbl->rlist;
 +
 +    rbb2 = boundingbox_only_distance2(gridi, gridj, nbl->rlist, nbl->bSimple);
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "nbl bounding box only distance %f\n", sqrt(rbb2));
 +    }
 +
 +    /* Set the shift range */
 +    for (d = 0; d < DIM; d++)
 +    {
 +        /* Check if we need periodicity shifts.
 +         * Without PBC or with domain decomposition we don't need them.
 +         */
 +        if (d >= ePBC2npbcdim(nbs->ePBC) || nbs->dd_dim[d])
 +        {
 +            shp[d] = 0;
 +        }
 +        else
 +        {
 +            if (d == XX &&
 +                box[XX][XX] - fabs(box[YY][XX]) - fabs(box[ZZ][XX]) < sqrt(rl2))
 +            {
 +                shp[d] = 2;
 +            }
 +            else
 +            {
 +                shp[d] = 1;
 +            }
 +        }
 +    }
 +
 +    if (nbl->bSimple && !gridi->bSimple)
 +    {
 +        conv_i  = gridi->na_sc/gridj->na_sc;
 +        bb_i    = gridi->bb_simple;
 +        bbcz_i  = gridi->bbcz_simple;
 +        flags_i = gridi->flags_simple;
 +    }
 +    else
 +    {
 +        conv_i  = 1;
 +        bb_i    = gridi->bb;
 +        bbcz_i  = gridi->bbcz;
 +        flags_i = gridi->flags;
 +    }
 +    cell0_i = gridi->cell0*conv_i;
 +
 +    bbcz_j = gridj->bbcz;
 +
 +    if (conv_i != 1)
 +    {
 +        /* Blocks of the conversion factor - 1 give a large repeat count
 +         * combined with a small block size. This should result in good
 +         * load balancing for both small and large domains.
 +         */
 +        ci_block = conv_i - 1;
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "nbl nc_i %d col.av. %.1f ci_block %d\n",
 +                gridi->nc, gridi->nc/(double)(gridi->ncx*gridi->ncy), ci_block);
 +    }
 +
 +    ndistc   = 0;
 +    ncpcheck = 0;
 +
 +    /* Initially ci_b and ci to 1 before where we want them to start,
 +     * as they will both be incremented in next_ci.
 +     */
 +    ci_b = -1;
 +    ci   = th*ci_block - 1;
 +    ci_x = 0;
 +    ci_y = 0;
 +    while (next_ci(gridi, conv_i, nth, ci_block, &ci_x, &ci_y, &ci_b, &ci))
 +    {
 +        if (nbl->bSimple && flags_i[ci] == 0)
 +        {
 +            continue;
 +        }
 +
 +        ncj_old_i = nbl->ncj;
 +
 +        d2cx = 0;
 +        if (gridj != gridi && shp[XX] == 0)
 +        {
 +            if (nbl->bSimple)
 +            {
 +                bx1 = bb_i[ci*NNBSBB_B+NNBSBB_C+XX];
 +            }
 +            else
 +            {
 +                bx1 = gridi->c0[XX] + (ci_x+1)*gridi->sx;
 +            }
 +            if (bx1 < gridj->c0[XX])
 +            {
 +                d2cx = sqr(gridj->c0[XX] - bx1);
 +
 +                if (d2cx >= rl2)
 +                {
 +                    continue;
 +                }
 +            }
 +        }
 +
 +        ci_xy = ci_x*gridi->ncy + ci_y;
 +
 +        /* Loop over shift vectors in three dimensions */
 +        for (tz = -shp[ZZ]; tz <= shp[ZZ]; tz++)
 +        {
 +            shz = tz*box[ZZ][ZZ];
 +
 +            bz0 = bbcz_i[ci*NNBSBB_D  ] + shz;
 +            bz1 = bbcz_i[ci*NNBSBB_D+1] + shz;
 +
 +            if (tz == 0)
 +            {
 +                d2z = 0;
 +            }
 +            else if (tz < 0)
 +            {
 +                d2z = sqr(bz1);
 +            }
 +            else
 +            {
 +                d2z = sqr(bz0 - box[ZZ][ZZ]);
 +            }
 +
 +            d2z_cx = d2z + d2cx;
 +
 +            if (d2z_cx >= rl2)
 +            {
 +                continue;
 +            }
 +
 +            bz1_frac =
 +                bz1/((real)(gridi->cxy_ind[ci_xy+1] - gridi->cxy_ind[ci_xy]));
 +            if (bz1_frac < 0)
 +            {
 +                bz1_frac = 0;
 +            }
 +            /* The check with bz1_frac close to or larger than 1 comes later */
 +
 +            for (ty = -shp[YY]; ty <= shp[YY]; ty++)
 +            {
 +                shy = ty*box[YY][YY] + tz*box[ZZ][YY];
 +
 +                if (nbl->bSimple)
 +                {
 +                    by0 = bb_i[ci*NNBSBB_B         +YY] + shy;
 +                    by1 = bb_i[ci*NNBSBB_B+NNBSBB_C+YY] + shy;
 +                }
 +                else
 +                {
 +                    by0 = gridi->c0[YY] + (ci_y  )*gridi->sy + shy;
 +                    by1 = gridi->c0[YY] + (ci_y+1)*gridi->sy + shy;
 +                }
 +
 +                get_cell_range(by0, by1,
 +                               gridj->ncy, gridj->c0[YY], gridj->sy, gridj->inv_sy,
 +                               d2z_cx, rl2,
 +                               &cyf, &cyl);
 +
 +                if (cyf > cyl)
 +                {
 +                    continue;
 +                }
 +
 +                d2z_cy = d2z;
 +                if (by1 < gridj->c0[YY])
 +                {
 +                    d2z_cy += sqr(gridj->c0[YY] - by1);
 +                }
 +                else if (by0 > gridj->c1[YY])
 +                {
 +                    d2z_cy += sqr(by0 - gridj->c1[YY]);
 +                }
 +
 +                for (tx = -shp[XX]; tx <= shp[XX]; tx++)
 +                {
 +                    shift = XYZ2IS(tx, ty, tz);
 +
 +#ifdef NBNXN_SHIFT_BACKWARD
 +                    if (gridi == gridj && shift > CENTRAL)
 +                    {
 +                        continue;
 +                    }
 +#endif
 +
 +                    shx = tx*box[XX][XX] + ty*box[YY][XX] + tz*box[ZZ][XX];
 +
 +                    if (nbl->bSimple)
 +                    {
 +                        bx0 = bb_i[ci*NNBSBB_B         +XX] + shx;
 +                        bx1 = bb_i[ci*NNBSBB_B+NNBSBB_C+XX] + shx;
 +                    }
 +                    else
 +                    {
 +                        bx0 = gridi->c0[XX] + (ci_x  )*gridi->sx + shx;
 +                        bx1 = gridi->c0[XX] + (ci_x+1)*gridi->sx + shx;
 +                    }
 +
 +                    get_cell_range(bx0, bx1,
 +                                   gridj->ncx, gridj->c0[XX], gridj->sx, gridj->inv_sx,
 +                                   d2z_cy, rl2,
 +                                   &cxf, &cxl);
 +
 +                    if (cxf > cxl)
 +                    {
 +                        continue;
 +                    }
 +
 +                    if (nbl->bSimple)
 +                    {
 +                        new_ci_entry(nbl, cell0_i+ci, shift, flags_i[ci],
 +                                     nbl->work);
 +                    }
 +                    else
 +                    {
 +                        new_sci_entry(nbl, cell0_i+ci, shift, flags_i[ci],
 +                                      nbl->work);
 +                    }
 +
 +#ifndef NBNXN_SHIFT_BACKWARD
 +                    if (cxf < ci_x)
 +#else
 +                    if (shift == CENTRAL && gridi == gridj &&
 +                        cxf < ci_x)
 +#endif
 +                    {
 +                        /* Leave the pairs with i > j.
 +                         * x is the major index, so skip half of it.
 +                         */
 +                        cxf = ci_x;
 +                    }
 +
 +                    if (nbl->bSimple)
 +                    {
 +                        set_icell_bb_simple(bb_i, ci, shx, shy, shz,
 +                                            nbl->work->bb_ci);
 +                    }
 +                    else
 +                    {
 +                        set_icell_bb_supersub(bb_i, ci, shx, shy, shz,
 +                                              nbl->work->bb_ci);
 +                    }
 +
 +                    nbs->icell_set_x(cell0_i+ci, shx, shy, shz,
 +                                     gridi->na_c, nbat->xstride, nbat->x,
 +                                     nbl->work);
 +
 +                    for (cx = cxf; cx <= cxl; cx++)
 +                    {
 +                        d2zx = d2z;
 +                        if (gridj->c0[XX] + cx*gridj->sx > bx1)
 +                        {
 +                            d2zx += sqr(gridj->c0[XX] + cx*gridj->sx - bx1);
 +                        }
 +                        else if (gridj->c0[XX] + (cx+1)*gridj->sx < bx0)
 +                        {
 +                            d2zx += sqr(gridj->c0[XX] + (cx+1)*gridj->sx - bx0);
 +                        }
 +
 +#ifndef NBNXN_SHIFT_BACKWARD
 +                        if (gridi == gridj &&
 +                            cx == 0 && cyf < ci_y)
 +#else
 +                        if (gridi == gridj &&
 +                            cx == 0 && shift == CENTRAL && cyf < ci_y)
 +#endif
 +                        {
 +                            /* Leave the pairs with i > j.
 +                             * Skip half of y when i and j have the same x.
 +                             */
 +                            cyf_x = ci_y;
 +                        }
 +                        else
 +                        {
 +                            cyf_x = cyf;
 +                        }
 +
 +                        for (cy = cyf_x; cy <= cyl; cy++)
 +                        {
 +                            c0 = gridj->cxy_ind[cx*gridj->ncy+cy];
 +                            c1 = gridj->cxy_ind[cx*gridj->ncy+cy+1];
 +#ifdef NBNXN_SHIFT_BACKWARD
 +                            if (gridi == gridj &&
 +                                shift == CENTRAL && c0 < ci)
 +                            {
 +                                c0 = ci;
 +                            }
 +#endif
 +
 +                            d2zxy = d2zx;
 +                            if (gridj->c0[YY] + cy*gridj->sy > by1)
 +                            {
 +                                d2zxy += sqr(gridj->c0[YY] + cy*gridj->sy - by1);
 +                            }
 +                            else if (gridj->c0[YY] + (cy+1)*gridj->sy < by0)
 +                            {
 +                                d2zxy += sqr(gridj->c0[YY] + (cy+1)*gridj->sy - by0);
 +                            }
 +                            if (c1 > c0 && d2zxy < rl2)
 +                            {
 +                                cs = c0 + (int)(bz1_frac*(c1 - c0));
 +                                if (cs >= c1)
 +                                {
 +                                    cs = c1 - 1;
 +                                }
 +
 +                                d2xy = d2zxy - d2z;
 +
 +                                /* Find the lowest cell that can possibly
 +                                 * be within range.
 +                                 */
 +                                cf = cs;
 +                                while (cf > c0 &&
 +                                       (bbcz_j[cf*NNBSBB_D+1] >= bz0 ||
 +                                        d2xy + sqr(bbcz_j[cf*NNBSBB_D+1] - bz0) < rl2))
 +                                {
 +                                    cf--;
 +                                }
 +
 +                                /* Find the highest cell that can possibly
 +                                 * be within range.
 +                                 */
 +                                cl = cs;
 +                                while (cl < c1-1 &&
 +                                       (bbcz_j[cl*NNBSBB_D] <= bz1 ||
 +                                        d2xy + sqr(bbcz_j[cl*NNBSBB_D] - bz1) < rl2))
 +                                {
 +                                    cl++;
 +                                }
 +
 +#ifdef NBNXN_REFCODE
 +                                {
 +                                    /* Simple reference code, for debugging,
 +                                     * overrides the more complex code above.
 +                                     */
 +                                    int k;
 +                                    cf = c1;
 +                                    cl = -1;
 +                                    for (k = c0; k < c1; k++)
 +                                    {
 +                                        if (box_dist2(bx0, bx1, by0, by1, bz0, bz1,
 +                                                      bb+k*NNBSBB_B) < rl2 &&
 +                                            k < cf)
 +                                        {
 +                                            cf = k;
 +                                        }
 +                                        if (box_dist2(bx0, bx1, by0, by1, bz0, bz1,
 +                                                      bb+k*NNBSBB_B) < rl2 &&
 +                                            k > cl)
 +                                        {
 +                                            cl = k;
 +                                        }
 +                                    }
 +                                }
 +#endif
 +
 +                                if (gridi == gridj)
 +                                {
 +                                    /* We want each atom/cell pair only once,
 +                                     * only use cj >= ci.
 +                                     */
 +#ifndef NBNXN_SHIFT_BACKWARD
 +                                    cf = max(cf, ci);
 +#else
 +                                    if (shift == CENTRAL)
 +                                    {
 +                                        cf = max(cf, ci);
 +                                    }
 +#endif
 +                                }
 +
 +                                if (cf <= cl)
 +                                {
 +                                    /* For f buffer flags with simple lists */
 +                                    ncj_old_j = nbl->ncj;
 +
 +                                    switch (nb_kernel_type)
 +                                    {
 +                                        case nbnxnk4x4_PlainC:
 +                                            check_subcell_list_space_simple(nbl, cl-cf+1);
 +
 +                                            make_cluster_list_simple(gridj,
 +                                                                     nbl, ci, cf, cl,
 +                                                                     (gridi == gridj && shift == CENTRAL),
 +                                                                     nbat->x,
 +                                                                     rl2, rbb2,
 +                                                                     &ndistc);
 +                                            break;
 +#ifdef GMX_NBNXN_SIMD_4XN
 +                                        case nbnxnk4xN_SIMD_4xN:
 +                                            check_subcell_list_space_simple(nbl, ci_to_cj(na_cj_2log, cl-cf)+2);
 +                                            make_cluster_list_simd_4xn(gridj,
 +                                                                       nbl, ci, cf, cl,
 +                                                                       (gridi == gridj && shift == CENTRAL),
 +                                                                       nbat->x,
 +                                                                       rl2, rbb2,
 +                                                                       &ndistc);
 +                                            break;
 +#endif
 +#ifdef GMX_NBNXN_SIMD_2XNN
 +                                        case nbnxnk4xN_SIMD_2xNN:
 +                                            check_subcell_list_space_simple(nbl, ci_to_cj(na_cj_2log, cl-cf)+2);
 +                                            make_cluster_list_simd_2xnn(gridj,
 +                                                                        nbl, ci, cf, cl,
 +                                                                        (gridi == gridj && shift == CENTRAL),
 +                                                                        nbat->x,
 +                                                                        rl2, rbb2,
 +                                                                        &ndistc);
 +                                            break;
 +#endif
 +                                        case nbnxnk8x8x8_PlainC:
 +                                        case nbnxnk8x8x8_CUDA:
 +                                            check_subcell_list_space_supersub(nbl, cl-cf+1);
 +                                            for (cj = cf; cj <= cl; cj++)
 +                                            {
 +                                                make_cluster_list_supersub(nbs, gridi, gridj,
 +                                                                           nbl, ci, cj,
 +                                                                           (gridi == gridj && shift == CENTRAL && ci == cj),
 +                                                                           nbat->xstride, nbat->x,
 +                                                                           rl2, rbb2,
 +                                                                           &ndistc);
 +                                            }
 +                                            break;
 +                                    }
 +                                    ncpcheck += cl - cf + 1;
 +
 +                                    if (bFBufferFlag && nbl->ncj > ncj_old_j)
 +                                    {
 +                                        int cbf, cbl, cb;
 +
 +                                        cbf = nbl->cj[ncj_old_j].cj >> gridj_flag_shift;
 +                                        cbl = nbl->cj[nbl->ncj-1].cj >> gridj_flag_shift;
 +                                        for (cb = cbf; cb <= cbl; cb++)
 +                                        {
 +                                            gridj_flag[cb] = 1U<<th;
 +                                        }
 +                                    }
 +                                }
 +                            }
 +                        }
 +                    }
 +
 +                    /* Set the exclusions for this ci list */
 +                    if (nbl->bSimple)
 +                    {
 +                        set_ci_top_excls(nbs,
 +                                         nbl,
 +                                         shift == CENTRAL && gridi == gridj,
 +                                         gridj->na_c_2log,
 +                                         na_cj_2log,
 +                                         &(nbl->ci[nbl->nci]),
 +                                         excl);
 +                    }
 +                    else
 +                    {
 +                        set_sci_top_excls(nbs,
 +                                          nbl,
 +                                          shift == CENTRAL && gridi == gridj,
 +                                          gridj->na_c_2log,
 +                                          &(nbl->sci[nbl->nsci]),
 +                                          excl);
 +                    }
 +
 +                    /* Close this ci list */
 +                    if (nbl->bSimple)
 +                    {
 +                        close_ci_entry_simple(nbl);
 +                    }
 +                    else
 +                    {
 +                        close_ci_entry_supersub(nbl,
 +                                                nsubpair_max,
 +                                                progBal, min_ci_balanced,
 +                                                th, nth);
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (bFBufferFlag && nbl->ncj > ncj_old_i)
 +        {
 +            work->buffer_flags.flag[(gridi->cell0+ci)>>gridi_flag_shift] = 1U<<th;
 +        }
 +    }
 +
 +    work->ndistc = ndistc;
 +
 +    nbs_cycle_stop(&work->cc[enbsCCsearch]);
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "number of distance checks %d\n", ndistc);
 +        fprintf(debug, "ncpcheck %s %d\n", gridi == gridj ? "local" : "non-local",
 +                ncpcheck);
 +
 +        if (nbl->bSimple)
 +        {
 +            print_nblist_statistics_simple(debug, nbl, nbs, rlist);
 +        }
 +        else
 +        {
 +            print_nblist_statistics_supersub(debug, nbl, nbs, rlist);
 +        }
 +
 +    }
 +}
 +
 +static void reduce_buffer_flags(const nbnxn_search_t        nbs,
 +                                int                         nsrc,
 +                                const nbnxn_buffer_flags_t *dest)
 +{
 +    int s, b;
 +    const unsigned *flag;
 +
 +    for (s = 0; s < nsrc; s++)
 +    {
 +        flag = nbs->work[s].buffer_flags.flag;
 +
 +        for (b = 0; b < dest->nflag; b++)
 +        {
 +            dest->flag[b] |= flag[b];
 +        }
 +    }
 +}
 +
 +static void print_reduction_cost(const nbnxn_buffer_flags_t *flags, int nout)
 +{
 +    int nelem, nkeep, ncopy, nred, b, c, out;
 +
 +    nelem = 0;
 +    nkeep = 0;
 +    ncopy = 0;
 +    nred  = 0;
 +    for (b = 0; b < flags->nflag; b++)
 +    {
 +        if (flags->flag[b] == 1)
 +        {
 +            /* Only flag 0 is set, no copy of reduction required */
 +            nelem++;
 +            nkeep++;
 +        }
 +        else if (flags->flag[b] > 0)
 +        {
 +            c = 0;
 +            for (out = 0; out < nout; out++)
 +            {
 +                if (flags->flag[b] & (1U<<out))
 +                {
 +                    c++;
 +                }
 +            }
 +            nelem += c;
 +            if (c == 1)
 +            {
 +                ncopy++;
 +            }
 +            else
 +            {
 +                nred += c;
 +            }
 +        }
 +    }
 +
 +    fprintf(debug, "nbnxn reduction: #flag %d #list %d elem %4.2f, keep %4.2f copy %4.2f red %4.2f\n",
 +            flags->nflag, nout,
 +            nelem/(double)(flags->nflag),
 +            nkeep/(double)(flags->nflag),
 +            ncopy/(double)(flags->nflag),
 +            nred/(double)(flags->nflag));
 +}
 +
 +/* Perform a count (linear) sort to sort the smaller lists to the end.
 + * This avoids load imbalance on the GPU, as large lists will be
 + * scheduled and executed first and the smaller lists later.
 + * Load balancing between multi-processors only happens at the end
 + * and there smaller lists lead to more effective load balancing.
 + * The sorting is done on the cj4 count, not on the actual pair counts.
 + * Not only does this make the sort faster, but it also results in
 + * better load balancing than using a list sorted on exact load.
 + * This function swaps the pointer in the pair list to avoid a copy operation.
 + */
 +static void sort_sci(nbnxn_pairlist_t *nbl)
 +{
 +    nbnxn_list_work_t *work;
 +    int                m, i, s, s0, s1;
 +    nbnxn_sci_t       *sci_sort;
 +
 +    if (nbl->ncj4 <= nbl->nsci)
 +    {
 +        /* nsci = 0 or all sci have size 1, sorting won't change the order */
 +        return;
 +    }
 +
 +    work = nbl->work;
 +
 +    /* We will distinguish differences up to double the average */
 +    m = (2*nbl->ncj4)/nbl->nsci;
 +
 +    if (m + 1 > work->sort_nalloc)
 +    {
 +        work->sort_nalloc = over_alloc_large(m + 1);
 +        srenew(work->sort, work->sort_nalloc);
 +    }
 +
 +    if (work->sci_sort_nalloc != nbl->sci_nalloc)
 +    {
 +        work->sci_sort_nalloc = nbl->sci_nalloc;
 +        nbnxn_realloc_void((void **)&work->sci_sort,
 +                           0,
 +                           work->sci_sort_nalloc*sizeof(*work->sci_sort),
 +                           nbl->alloc, nbl->free);
 +    }
 +
 +    /* Count the entries of each size */
 +    for (i = 0; i <= m; i++)
 +    {
 +        work->sort[i] = 0;
 +    }
 +    for (s = 0; s < nbl->nsci; s++)
 +    {
 +        i = min(m, nbl->sci[s].cj4_ind_end - nbl->sci[s].cj4_ind_start);
 +        work->sort[i]++;
 +    }
 +    /* Calculate the offset for each count */
 +    s0            = work->sort[m];
 +    work->sort[m] = 0;
 +    for (i = m - 1; i >= 0; i--)
 +    {
 +        s1            = work->sort[i];
 +        work->sort[i] = work->sort[i + 1] + s0;
 +        s0            = s1;
 +    }
 +
 +    /* Sort entries directly into place */
 +    sci_sort = work->sci_sort;
 +    for (s = 0; s < nbl->nsci; s++)
 +    {
 +        i = min(m, nbl->sci[s].cj4_ind_end - nbl->sci[s].cj4_ind_start);
 +        sci_sort[work->sort[i]++] = nbl->sci[s];
 +    }
 +
 +    /* Swap the sci pointers so we use the new, sorted list */
 +    work->sci_sort = nbl->sci;
 +    nbl->sci       = sci_sort;
 +}
 +
 +/* Make a local or non-local pair-list, depending on iloc */
 +void nbnxn_make_pairlist(const nbnxn_search_t  nbs,
 +                         nbnxn_atomdata_t     *nbat,
 +                         const t_blocka       *excl,
 +                         real                  rlist,
 +                         int                   min_ci_balanced,
 +                         nbnxn_pairlist_set_t *nbl_list,
 +                         int                   iloc,
 +                         int                   nb_kernel_type,
 +                         t_nrnb               *nrnb)
 +{
 +    nbnxn_grid_t *gridi, *gridj;
 +    gmx_bool bGPUCPU;
 +    int nzi, zi, zj0, zj1, zj;
 +    int nsubpair_max;
 +    int th;
 +    int nnbl;
 +    nbnxn_pairlist_t **nbl;
 +    int ci_block;
 +    gmx_bool CombineNBLists;
 +    gmx_bool progBal;
 +    int np_tot, np_noq, np_hlj, nap;
 +
 +    /* Check if we are running hybrid GPU + CPU nbnxn mode */
 +    bGPUCPU = (!nbs->grid[0].bSimple && nbl_list->bSimple);
 +
 +    nnbl            = nbl_list->nnbl;
 +    nbl             = nbl_list->nbl;
 +    CombineNBLists  = nbl_list->bCombined;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "ns making %d nblists\n", nnbl);
 +    }
 +
 +    nbat->bUseBufferFlags = (nbat->nout > 1);
 +    /* We should re-init the flags before making the first list */
 +    if (nbat->bUseBufferFlags && (LOCAL_I(iloc) || bGPUCPU))
 +    {
 +        init_buffer_flags(&nbat->buffer_flags, nbat->natoms);
 +    }
 +
 +    if (nbl_list->bSimple)
 +    {
 +        switch (nb_kernel_type)
 +        {
 +#ifdef GMX_NBNXN_SIMD_4XN
 +            case nbnxnk4xN_SIMD_4xN:
 +                nbs->icell_set_x = icell_set_x_simd_4xn;
 +                break;
 +#endif
 +#ifdef GMX_NBNXN_SIMD_2XNN
 +            case nbnxnk4xN_SIMD_2xNN:
 +                nbs->icell_set_x = icell_set_x_simd_2xnn;
 +                break;
 +#endif
 +            default:
 +                nbs->icell_set_x = icell_set_x_simple;
 +                break;
 +        }
 +    }
 +    else
 +    {
 +#ifdef NBNXN_SEARCH_BB_SSE
 +        nbs->icell_set_x = icell_set_x_supersub_sse8;
 +#else
 +        nbs->icell_set_x = icell_set_x_supersub;
 +#endif
 +    }
 +
 +    if (LOCAL_I(iloc))
 +    {
 +        /* Only zone (grid) 0 vs 0 */
 +        nzi = 1;
 +        zj0 = 0;
 +        zj1 = 1;
 +    }
 +    else
 +    {
 +        nzi = nbs->zones->nizone;
 +    }
 +
 +    if (!nbl_list->bSimple && min_ci_balanced > 0)
 +    {
 +        nsubpair_max = get_nsubpair_max(nbs, iloc, rlist, min_ci_balanced);
 +    }
 +    else
 +    {
 +        nsubpair_max = 0;
 +    }
 +
 +    /* Clear all pair-lists */
 +    for (th = 0; th < nnbl; th++)
 +    {
 +        clear_pairlist(nbl[th]);
 +    }
 +
 +    for (zi = 0; zi < nzi; zi++)
 +    {
 +        gridi = &nbs->grid[zi];
 +
 +        if (NONLOCAL_I(iloc))
 +        {
 +            zj0 = nbs->zones->izone[zi].j0;
 +            zj1 = nbs->zones->izone[zi].j1;
 +            if (zi == 0)
 +            {
 +                zj0++;
 +            }
 +        }
 +        for (zj = zj0; zj < zj1; zj++)
 +        {
 +            gridj = &nbs->grid[zj];
 +
 +            if (debug)
 +            {
 +                fprintf(debug, "ns search grid %d vs %d\n", zi, zj);
 +            }
 +
 +            nbs_cycle_start(&nbs->cc[enbsCCsearch]);
 +
 +            if (nbl[0]->bSimple && !gridi->bSimple)
 +            {
 +                /* Hybrid list, determine blocking later */
 +                ci_block = 0;
 +            }
 +            else
 +            {
 +                ci_block = get_ci_block_size(gridi, nbs->DomDec, nnbl);
 +            }
 +
 +#pragma omp parallel for num_threads(nnbl) schedule(static)
 +            for (th = 0; th < nnbl; th++)
 +            {
 +                /* Re-init the thread-local work flag data before making
 +                 * the first list (not an elegant conditional).
 +                 */
 +                if (nbat->bUseBufferFlags && ((zi == 0 && zj == 0) ||
 +                                              (bGPUCPU && zi == 0 && zj == 1)))
 +                {
 +                    init_buffer_flags(&nbs->work[th].buffer_flags, nbat->natoms);
 +                }
 +
 +                if (CombineNBLists && th > 0)
 +                {
 +                    clear_pairlist(nbl[th]);
 +                }
 +
 +                /* With GPU: generate progressively smaller lists for
 +                 * load balancing for local only or non-local with 2 zones.
 +                 */
 +                progBal = (LOCAL_I(iloc) || nbs->zones->n <= 2);
 +
 +                /* Divide the i super cell equally over the nblists */
 +                nbnxn_make_pairlist_part(nbs, gridi, gridj,
 +                                         &nbs->work[th], nbat, excl,
 +                                         rlist,
 +                                         nb_kernel_type,
 +                                         ci_block,
 +                                         nbat->bUseBufferFlags,
 +                                         nsubpair_max,
 +                                         progBal, min_ci_balanced,
 +                                         th, nnbl,
 +                                         nbl[th]);
 +            }
 +            nbs_cycle_stop(&nbs->cc[enbsCCsearch]);
 +
 +            np_tot = 0;
 +            np_noq = 0;
 +            np_hlj = 0;
 +            for (th = 0; th < nnbl; th++)
 +            {
 +                inc_nrnb(nrnb, eNR_NBNXN_DIST2, nbs->work[th].ndistc);
 +
 +                if (nbl_list->bSimple)
 +                {
 +                    np_tot += nbl[th]->ncj;
 +                    np_noq += nbl[th]->work->ncj_noq;
 +                    np_hlj += nbl[th]->work->ncj_hlj;
 +                }
 +                else
 +                {
 +                    /* This count ignores potential subsequent pair pruning */
 +                    np_tot += nbl[th]->nci_tot;
 +                }
 +            }
 +            nap                   = nbl[0]->na_ci*nbl[0]->na_cj;
 +            nbl_list->natpair_ljq = (np_tot - np_noq)*nap - np_hlj*nap/2;
 +            nbl_list->natpair_lj  = np_noq*nap;
 +            nbl_list->natpair_q   = np_hlj*nap/2;
 +
 +            if (CombineNBLists && nnbl > 1)
 +            {
 +                nbs_cycle_start(&nbs->cc[enbsCCcombine]);
 +
 +                combine_nblists(nnbl-1, nbl+1, nbl[0]);
 +
 +                nbs_cycle_stop(&nbs->cc[enbsCCcombine]);
 +            }
 +        }
 +    }
 +
 +    if (!nbl_list->bSimple)
 +    {
 +        /* Sort the entries on size, large ones first */
 +        if (CombineNBLists || nnbl == 1)
 +        {
 +            sort_sci(nbl[0]);
 +        }
 +        else
 +        {
 +#pragma omp parallel for num_threads(nnbl) schedule(static)
 +            for (th = 0; th < nnbl; th++)
 +            {
 +                sort_sci(nbl[th]);
 +            }
 +        }
 +    }
 +
 +    if (nbat->bUseBufferFlags)
 +    {
 +        reduce_buffer_flags(nbs, nnbl, &nbat->buffer_flags);
 +    }
 +
 +    /* Special performance logging stuff (env.var. GMX_NBNXN_CYCLE) */
 +    if (LOCAL_I(iloc))
 +    {
 +        nbs->search_count++;
 +    }
 +    if (nbs->print_cycles &&
 +        (!nbs->DomDec || (nbs->DomDec && !LOCAL_I(iloc))) &&
 +        nbs->search_count % 100 == 0)
 +    {
 +        nbs_cycle_print(stderr, nbs);
 +    }
 +
 +    if (debug && (CombineNBLists && nnbl > 1))
 +    {
 +        if (nbl[0]->bSimple)
 +        {
 +            print_nblist_statistics_simple(debug, nbl[0], nbs, rlist);
 +        }
 +        else
 +        {
 +            print_nblist_statistics_supersub(debug, nbl[0], nbs, rlist);
 +        }
 +    }
 +
 +    if (debug)
 +    {
 +        if (gmx_debug_at)
 +        {
 +            if (nbl[0]->bSimple)
 +            {
 +                print_nblist_ci_cj(debug, nbl[0]);
 +            }
 +            else
 +            {
 +                print_nblist_sci_cj(debug, nbl[0]);
 +            }
 +        }
 +
 +        if (nbat->bUseBufferFlags)
 +        {
 +            print_reduction_cost(&nbat->buffer_flags, nnbl);
 +        }
 +    }
 +}
index c0a0c11f9daa238f7de48fe0fd1a671e7182fadc,0000000000000000000000000000000000000000..e047262ce85f85656be99c64b9df799792b2cf06
mode 100644,000000..100644
--- /dev/null
@@@ -1,262 -1,0 +1,317 @@@
- #if GMX_NBNXN_SIMD_BITWIDTH == 128
- #define GMX_MM128_HERE
- #else
- #if GMX_NBNXN_SIMD_BITWIDTH == 256
- #define GMX_MM256_HERE
- #else
- #error "unsupported GMX_NBNXN_SIMD_BITWIDTH"
- #endif
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + * Copyright (c) 2012, by the GROMACS development team, led by
 + * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 + * others, as listed in the AUTHORS file in the top-level source
 + * directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
-     gmx_mm_hpr a_SSE;
++#if GMX_NBNXN_SIMD_BITWIDTH != 256
++#error "unsupported SIMD width"
 +#endif
++
 +#include "gmx_simd_macros.h"
 +
++/* Define a few macros for half-width SIMD */
++#if defined GMX_X86_AVX_256 && !defined GMX_DOUBLE
++/* Half-width SIMD real type */
++#define gmx_mm_hpr  __m128
++/* Half-width SIMD operations */
++/* Load reals at half-width aligned pointer b into half-width SIMD register a */
++#define gmx_load_hpr(a,b)       a = _mm_load_ps(b)
++#define gmx_set1_hpr                _mm_set1_ps
++/* Load reals at half-width aligned pointer b into two halves of a */
++#define gmx_loaddh_pr(a, b)     a = gmx_mm256_load4_ps(b)
++/* Store half width SIMD registers b and c in ful width register a */
++#define gmx_2hpr_to_pr(a, b, c) a = _mm256_insertf128_ps(_mm256_castps128_ps256(b), c, 0x1)
++#else
++#error "Half-width SIMD macros are not yet defined"
++#endif
++
++
 +#if GMX_SIMD_WIDTH_HERE >= 2*NBNXN_CPU_CLUSTER_I_SIZE
 +#define STRIDE_S  (GMX_SIMD_WIDTH_HERE/2)
 +#else
 +#define STRIDE_S  NBNXN_CPU_CLUSTER_I_SIZE
 +#endif
 +
 +static gmx_inline gmx_mm_pr gmx_load_hpr_hilo_pr(const real *a)
 +{
-     a_SSE = _mm_load_ps(a);
++    gmx_mm_hpr a_S;
++    gmx_mm_pr  a_a_S;
++
++    gmx_load_hpr(a_S, a);
 +
-     return gmx_2hpr_to_pr(a_SSE, a_SSE);
++    gmx_2hpr_to_pr(a_a_S, a_S, a_S);
 +
-     gmx_mm_hpr a0, a1;
++    return a_a_S;
 +}
 +
 +static gmx_inline gmx_mm_pr gmx_set_2real_shift_pr(const real *a, real shift)
 +{
-     a0 = _mm_set1_ps(a[0] + shift);
-     a1 = _mm_set1_ps(a[1] + shift);
++    gmx_mm_hpr a0_S, a1_S;
++    gmx_mm_pr  a0_a1_S;
 +
-     return gmx_2hpr_to_pr(a1, a0);
++    a0_S = gmx_set1_hpr(a[0] + shift);
++    a1_S = gmx_set1_hpr(a[1] + shift);
 +
-             InRange            = gmx_movemask_pr(wco_any_SSE);
++    gmx_2hpr_to_pr(a0_a1_S, a0_S, a1_S);
++
++    return a0_a1_S;
 +}
 +
 +/* Copies PBC shifted i-cell packed atom coordinates to working array */
 +static gmx_inline void
 +icell_set_x_simd_2xnn(int ci,
 +                      real shx, real shy, real shz,
 +                      int na_c,
 +                      int stride, const real *x,
 +                      nbnxn_list_work_t *work)
 +{
 +    int                     ia;
 +    nbnxn_x_ci_simd_2xnn_t *x_ci;
 +
 +    x_ci = work->x_ci_simd_2xnn;
 +
 +    ia = X_IND_CI_SIMD_2XNN(ci);
 +
 +    x_ci->ix_SSE0 = gmx_set_2real_shift_pr(x + ia + 0*STRIDE_S + 0, shx);
 +    x_ci->iy_SSE0 = gmx_set_2real_shift_pr(x + ia + 1*STRIDE_S + 0, shy);
 +    x_ci->iz_SSE0 = gmx_set_2real_shift_pr(x + ia + 2*STRIDE_S + 0, shz);
 +    x_ci->ix_SSE2 = gmx_set_2real_shift_pr(x + ia + 0*STRIDE_S + 2, shx);
 +    x_ci->iy_SSE2 = gmx_set_2real_shift_pr(x + ia + 1*STRIDE_S + 2, shy);
 +    x_ci->iz_SSE2 = gmx_set_2real_shift_pr(x + ia + 2*STRIDE_S + 2, shz);
 +}
 +
++#ifndef GMX_HAVE_SIMD_ANYTRUE
++/* Fallback function in case gmx_anytrue_pr is not present */
++static gmx_inline gmx_bool
++gmx_anytrue_2xn_pr(gmx_mm_pr bool_S)
++{
++    real     bools_array[2*GMX_SIMD_WIDTH_HERE], *bools;
++    gmx_bool any;
++    int      s;
++
++    bools = gmx_simd_align_real(bools_array);
++
++    gmx_store_pr(bools, bool_S);
++
++    any = FALSE;
++    for (s = 0; s < GMX_SIMD_WIDTH_HERE; s++)
++    {
++        if (GMX_SIMD_IS_TRUE(s))
++        {
++            any = TRUE;
++        }
++    }
++
++    return any;
++}
++#endif
++
 +/* SIMD code for making a pair list of cell ci vs cell cjf-cjl
 + * for coordinates in packed format.
 + * Checks bouding box distances and possibly atom pair distances.
 + * This is an accelerated version of make_cluster_list_simple.
 + */
 +static gmx_inline void
 +make_cluster_list_simd_2xnn(const nbnxn_grid_t *gridj,
 +                            nbnxn_pairlist_t *nbl,
 +                            int ci, int cjf, int cjl,
 +                            gmx_bool remove_sub_diag,
 +                            const real *x_j,
 +                            real rl2, float rbb2,
 +                            int *ndistc)
 +{
 +    const nbnxn_x_ci_simd_2xnn_t *work;
 +    const float                  *bb_ci;
 +
 +    gmx_mm_pr                     jx_SSE, jy_SSE, jz_SSE;
 +
 +    gmx_mm_pr                     dx_SSE0, dy_SSE0, dz_SSE0;
 +    gmx_mm_pr                     dx_SSE2, dy_SSE2, dz_SSE2;
 +
 +    gmx_mm_pr                     rsq_SSE0;
 +    gmx_mm_pr                     rsq_SSE2;
 +
 +    gmx_mm_pr                     wco_SSE0;
 +    gmx_mm_pr                     wco_SSE2;
 +    gmx_mm_pr                     wco_any_SSE;
 +
 +    gmx_mm_pr                     rc2_SSE;
 +
 +    gmx_bool                      InRange;
 +    float                         d2;
 +    int                           xind_f, xind_l, cj;
 +
 +    cjf = CI_TO_CJ_SIMD_2XNN(cjf);
 +    cjl = CI_TO_CJ_SIMD_2XNN(cjl+1) - 1;
 +
 +    work = nbl->work->x_ci_simd_2xnn;
 +
 +    bb_ci = nbl->work->bb_ci;
 +
 +    rc2_SSE   = gmx_set1_pr(rl2);
 +
 +    InRange = FALSE;
 +    while (!InRange && cjf <= cjl)
 +    {
 +        d2       = subc_bb_dist2_sse(4, 0, bb_ci, cjf, gridj->bbj);
 +        *ndistc += 2;
 +
 +        /* Check if the distance is within the distance where
 +         * we use only the bounding box distance rbb,
 +         * or within the cut-off and there is at least one atom pair
 +         * within the cut-off.
 +         */
 +        if (d2 < rbb2)
 +        {
 +            InRange = TRUE;
 +        }
 +        else if (d2 < rl2)
 +        {
 +            xind_f  = X_IND_CJ_SIMD_2XNN(CI_TO_CJ_SIMD_2XNN(gridj->cell0) + cjf);
 +
 +            jx_SSE  = gmx_load_hpr_hilo_pr(x_j+xind_f+0*STRIDE_S);
 +            jy_SSE  = gmx_load_hpr_hilo_pr(x_j+xind_f+1*STRIDE_S);
 +            jz_SSE  = gmx_load_hpr_hilo_pr(x_j+xind_f+2*STRIDE_S);
 +
 +            /* Calculate distance */
 +            dx_SSE0            = gmx_sub_pr(work->ix_SSE0, jx_SSE);
 +            dy_SSE0            = gmx_sub_pr(work->iy_SSE0, jy_SSE);
 +            dz_SSE0            = gmx_sub_pr(work->iz_SSE0, jz_SSE);
 +            dx_SSE2            = gmx_sub_pr(work->ix_SSE2, jx_SSE);
 +            dy_SSE2            = gmx_sub_pr(work->iy_SSE2, jy_SSE);
 +            dz_SSE2            = gmx_sub_pr(work->iz_SSE2, jz_SSE);
 +
 +            /* rsq = dx*dx+dy*dy+dz*dz */
 +            rsq_SSE0           = gmx_calc_rsq_pr(dx_SSE0, dy_SSE0, dz_SSE0);
 +            rsq_SSE2           = gmx_calc_rsq_pr(dx_SSE2, dy_SSE2, dz_SSE2);
 +
 +            wco_SSE0           = gmx_cmplt_pr(rsq_SSE0, rc2_SSE);
 +            wco_SSE2           = gmx_cmplt_pr(rsq_SSE2, rc2_SSE);
 +
 +            wco_any_SSE        = gmx_or_pr(wco_SSE0, wco_SSE2);
 +
-             InRange            = gmx_movemask_pr(wco_any_SSE);
++#ifdef GMX_HAVE_SIMD_ANYTRUE
++            InRange            = gmx_anytrue_pr(wco_any_SSE);
++#else
++            InRange            = gmx_anytrue_2xn_pr(wco_any_SSE);
++#endif
 +
 +            *ndistc += 2*GMX_SIMD_WIDTH_HERE;
 +        }
 +        if (!InRange)
 +        {
 +            cjf++;
 +        }
 +    }
 +    if (!InRange)
 +    {
 +        return;
 +    }
 +
 +    InRange = FALSE;
 +    while (!InRange && cjl > cjf)
 +    {
 +        d2       = subc_bb_dist2_sse(4, 0, bb_ci, cjl, gridj->bbj);
 +        *ndistc += 2;
 +
 +        /* Check if the distance is within the distance where
 +         * we use only the bounding box distance rbb,
 +         * or within the cut-off and there is at least one atom pair
 +         * within the cut-off.
 +         */
 +        if (d2 < rbb2)
 +        {
 +            InRange = TRUE;
 +        }
 +        else if (d2 < rl2)
 +        {
 +            xind_l  = X_IND_CJ_SIMD_2XNN(CI_TO_CJ_SIMD_2XNN(gridj->cell0) + cjl);
 +
 +            jx_SSE  = gmx_load_hpr_hilo_pr(x_j+xind_l+0*STRIDE_S);
 +            jy_SSE  = gmx_load_hpr_hilo_pr(x_j+xind_l+1*STRIDE_S);
 +            jz_SSE  = gmx_load_hpr_hilo_pr(x_j+xind_l+2*STRIDE_S);
 +
 +            /* Calculate distance */
 +            dx_SSE0            = gmx_sub_pr(work->ix_SSE0, jx_SSE);
 +            dy_SSE0            = gmx_sub_pr(work->iy_SSE0, jy_SSE);
 +            dz_SSE0            = gmx_sub_pr(work->iz_SSE0, jz_SSE);
 +            dx_SSE2            = gmx_sub_pr(work->ix_SSE2, jx_SSE);
 +            dy_SSE2            = gmx_sub_pr(work->iy_SSE2, jy_SSE);
 +            dz_SSE2            = gmx_sub_pr(work->iz_SSE2, jz_SSE);
 +
 +            /* rsq = dx*dx+dy*dy+dz*dz */
 +            rsq_SSE0           = gmx_calc_rsq_pr(dx_SSE0, dy_SSE0, dz_SSE0);
 +            rsq_SSE2           = gmx_calc_rsq_pr(dx_SSE2, dy_SSE2, dz_SSE2);
 +
 +            wco_SSE0           = gmx_cmplt_pr(rsq_SSE0, rc2_SSE);
 +            wco_SSE2           = gmx_cmplt_pr(rsq_SSE2, rc2_SSE);
 +
 +            wco_any_SSE        = gmx_or_pr(wco_SSE0, wco_SSE2);
 +
-             nbl->cj[nbl->ncj].excl = get_imask_x86_simd_2xnn(remove_sub_diag, ci, cj);
++#ifdef GMX_HAVE_SIMD_ANYTRUE
++            InRange            = gmx_anytrue_pr(wco_any_SSE);
++#else
++            InRange            = gmx_anytrue_2xn_pr(wco_any_SSE);
++#endif
 +
 +            *ndistc += 2*GMX_SIMD_WIDTH_HERE;
 +        }
 +        if (!InRange)
 +        {
 +            cjl--;
 +        }
 +    }
 +
 +    if (cjf <= cjl)
 +    {
 +        for (cj = cjf; cj <= cjl; cj++)
 +        {
 +            /* Store cj and the interaction mask */
 +            nbl->cj[nbl->ncj].cj   = CI_TO_CJ_SIMD_2XNN(gridj->cell0) + cj;
- #undef GMX_MM128_HERE
- #undef GMX_MM256_HERE
++            nbl->cj[nbl->ncj].excl = get_imask_simd_2xnn(remove_sub_diag, ci, cj);
 +            nbl->ncj++;
 +        }
 +        /* Increase the closing index in i super-cell list */
 +        nbl->ci[nbl->nci].cj_ind_end = nbl->ncj;
 +    }
 +}
 +
 +#undef STRIDE_S
++
++#undef gmx_mm_hpr
++#undef gmx_load_hpr
++#undef gmx_set1_hpr
++#undef gmx_2hpr_to_pr
index 5e529ae752f2bddda4e30a541f22bd26b032aefd,0000000000000000000000000000000000000000..d95daabfe8ffe8cf47724fac82101a722d72ff03
mode 100644,000000..100644
--- /dev/null
@@@ -1,280 -1,0 +1,311 @@@
- #if GMX_NBNXN_SIMD_BITWIDTH == 128
- #define GMX_MM128_HERE
- #else
- #if GMX_NBNXN_SIMD_BITWIDTH == 256
- #define GMX_MM256_HERE
- #else
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2012, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 + * Copyright (c) 2012, by the GROMACS development team, led by
 + * David van der Spoel, Berk Hess, Erik Lindahl, and including many
 + * others, as listed in the AUTHORS file in the top-level source
 + * directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
-             InRange            = gmx_movemask_pr(wco_any_SSE);
++#if !(GMX_NBNXN_SIMD_BITWIDTH == 128 || GMX_NBNXN_SIMD_BITWIDTH == 256)
 +#error "unsupported GMX_NBNXN_SIMD_BITWIDTH"
 +#endif
++
++#ifdef GMX_NBNXN_HALF_WIDTH_SIMD
++#define GMX_USE_HALF_WIDTH_SIMD_HERE
 +#endif
 +#include "gmx_simd_macros.h"
 +
 +#if GMX_SIMD_WIDTH_HERE >= NBNXN_CPU_CLUSTER_I_SIZE
 +#define STRIDE_S  (GMX_SIMD_WIDTH_HERE)
 +#else
 +#define STRIDE_S  NBNXN_CPU_CLUSTER_I_SIZE
 +#endif
 +
 +/* Copies PBC shifted i-cell packed atom coordinates to working array */
 +static gmx_inline void
 +icell_set_x_simd_4xn(int ci,
 +                     real shx, real shy, real shz,
 +                     int na_c,
 +                     int stride, const real *x,
 +                     nbnxn_list_work_t *work)
 +{
 +    int                    ia;
 +    nbnxn_x_ci_simd_4xn_t *x_ci;
 +
 +    x_ci = work->x_ci_simd_4xn;
 +
 +    ia = X_IND_CI_SIMD_4XN(ci);
 +
 +    x_ci->ix_SSE0 = gmx_set1_pr(x[ia + 0*STRIDE_S    ] + shx);
 +    x_ci->iy_SSE0 = gmx_set1_pr(x[ia + 1*STRIDE_S    ] + shy);
 +    x_ci->iz_SSE0 = gmx_set1_pr(x[ia + 2*STRIDE_S    ] + shz);
 +    x_ci->ix_SSE1 = gmx_set1_pr(x[ia + 0*STRIDE_S + 1] + shx);
 +    x_ci->iy_SSE1 = gmx_set1_pr(x[ia + 1*STRIDE_S + 1] + shy);
 +    x_ci->iz_SSE1 = gmx_set1_pr(x[ia + 2*STRIDE_S + 1] + shz);
 +    x_ci->ix_SSE2 = gmx_set1_pr(x[ia + 0*STRIDE_S + 2] + shx);
 +    x_ci->iy_SSE2 = gmx_set1_pr(x[ia + 1*STRIDE_S + 2] + shy);
 +    x_ci->iz_SSE2 = gmx_set1_pr(x[ia + 2*STRIDE_S + 2] + shz);
 +    x_ci->ix_SSE3 = gmx_set1_pr(x[ia + 0*STRIDE_S + 3] + shx);
 +    x_ci->iy_SSE3 = gmx_set1_pr(x[ia + 1*STRIDE_S + 3] + shy);
 +    x_ci->iz_SSE3 = gmx_set1_pr(x[ia + 2*STRIDE_S + 3] + shz);
 +}
 +
++#ifndef GMX_HAVE_SIMD_ANYTRUE
++/* Fallback function in case gmx_anytrue_pr is not present */
++static gmx_inline gmx_bool
++gmx_anytrue_4xn_pr(gmx_mm_pr bool_S)
++{
++    real     bools_array[2*GMX_SIMD_WIDTH_HERE], *bools;
++    gmx_bool any;
++    int      s;
++
++    bools = gmx_simd_align_real(bools_array);
++
++    gmx_store_pr(bools, bool_S);
++
++    any = FALSE;
++    for (s = 0; s < GMX_SIMD_WIDTH_HERE; s++)
++    {
++        if (GMX_SIMD_IS_TRUE(bools[s]))
++        {
++            any = TRUE;
++        }
++    }
++
++    return any;
++}
++#endif
++
 +/* SIMD code for making a pair list of cell ci vs cell cjf-cjl
 + * for coordinates in packed format.
 + * Checks bouding box distances and possibly atom pair distances.
 + * This is an accelerated version of make_cluster_list_simple.
 + */
 +static gmx_inline void
 +make_cluster_list_simd_4xn(const nbnxn_grid_t *gridj,
 +                           nbnxn_pairlist_t *nbl,
 +                           int ci, int cjf, int cjl,
 +                           gmx_bool remove_sub_diag,
 +                           const real *x_j,
 +                           real rl2, float rbb2,
 +                           int *ndistc)
 +{
 +    const nbnxn_x_ci_simd_4xn_t *work;
 +    const float                 *bb_ci;
 +
 +    gmx_mm_pr                    jx_SSE, jy_SSE, jz_SSE;
 +
 +    gmx_mm_pr                    dx_SSE0, dy_SSE0, dz_SSE0;
 +    gmx_mm_pr                    dx_SSE1, dy_SSE1, dz_SSE1;
 +    gmx_mm_pr                    dx_SSE2, dy_SSE2, dz_SSE2;
 +    gmx_mm_pr                    dx_SSE3, dy_SSE3, dz_SSE3;
 +
 +    gmx_mm_pr                    rsq_SSE0;
 +    gmx_mm_pr                    rsq_SSE1;
 +    gmx_mm_pr                    rsq_SSE2;
 +    gmx_mm_pr                    rsq_SSE3;
 +
 +    gmx_mm_pr                    wco_SSE0;
 +    gmx_mm_pr                    wco_SSE1;
 +    gmx_mm_pr                    wco_SSE2;
 +    gmx_mm_pr                    wco_SSE3;
 +    gmx_mm_pr                    wco_any_SSE01, wco_any_SSE23, wco_any_SSE;
 +
 +    gmx_mm_pr                    rc2_SSE;
 +
 +    gmx_bool                     InRange;
 +    float                        d2;
 +    int                          xind_f, xind_l, cj;
 +
 +    cjf = CI_TO_CJ_SIMD_4XN(cjf);
 +    cjl = CI_TO_CJ_SIMD_4XN(cjl+1) - 1;
 +
 +    work = nbl->work->x_ci_simd_4xn;
 +
 +    bb_ci = nbl->work->bb_ci;
 +
 +    rc2_SSE   = gmx_set1_pr(rl2);
 +
 +    InRange = FALSE;
 +    while (!InRange && cjf <= cjl)
 +    {
 +        d2       = subc_bb_dist2_sse(4, 0, bb_ci, cjf, gridj->bbj);
 +        *ndistc += 2;
 +
 +        /* Check if the distance is within the distance where
 +         * we use only the bounding box distance rbb,
 +         * or within the cut-off and there is at least one atom pair
 +         * within the cut-off.
 +         */
 +        if (d2 < rbb2)
 +        {
 +            InRange = TRUE;
 +        }
 +        else if (d2 < rl2)
 +        {
 +            xind_f  = X_IND_CJ_SIMD_4XN(CI_TO_CJ_SIMD_4XN(gridj->cell0) + cjf);
 +
 +            jx_SSE  = gmx_load_pr(x_j+xind_f+0*STRIDE_S);
 +            jy_SSE  = gmx_load_pr(x_j+xind_f+1*STRIDE_S);
 +            jz_SSE  = gmx_load_pr(x_j+xind_f+2*STRIDE_S);
 +
 +
 +            /* Calculate distance */
 +            dx_SSE0            = gmx_sub_pr(work->ix_SSE0, jx_SSE);
 +            dy_SSE0            = gmx_sub_pr(work->iy_SSE0, jy_SSE);
 +            dz_SSE0            = gmx_sub_pr(work->iz_SSE0, jz_SSE);
 +            dx_SSE1            = gmx_sub_pr(work->ix_SSE1, jx_SSE);
 +            dy_SSE1            = gmx_sub_pr(work->iy_SSE1, jy_SSE);
 +            dz_SSE1            = gmx_sub_pr(work->iz_SSE1, jz_SSE);
 +            dx_SSE2            = gmx_sub_pr(work->ix_SSE2, jx_SSE);
 +            dy_SSE2            = gmx_sub_pr(work->iy_SSE2, jy_SSE);
 +            dz_SSE2            = gmx_sub_pr(work->iz_SSE2, jz_SSE);
 +            dx_SSE3            = gmx_sub_pr(work->ix_SSE3, jx_SSE);
 +            dy_SSE3            = gmx_sub_pr(work->iy_SSE3, jy_SSE);
 +            dz_SSE3            = gmx_sub_pr(work->iz_SSE3, jz_SSE);
 +
 +            /* rsq = dx*dx+dy*dy+dz*dz */
 +            rsq_SSE0           = gmx_calc_rsq_pr(dx_SSE0, dy_SSE0, dz_SSE0);
 +            rsq_SSE1           = gmx_calc_rsq_pr(dx_SSE1, dy_SSE1, dz_SSE1);
 +            rsq_SSE2           = gmx_calc_rsq_pr(dx_SSE2, dy_SSE2, dz_SSE2);
 +            rsq_SSE3           = gmx_calc_rsq_pr(dx_SSE3, dy_SSE3, dz_SSE3);
 +
 +            wco_SSE0           = gmx_cmplt_pr(rsq_SSE0, rc2_SSE);
 +            wco_SSE1           = gmx_cmplt_pr(rsq_SSE1, rc2_SSE);
 +            wco_SSE2           = gmx_cmplt_pr(rsq_SSE2, rc2_SSE);
 +            wco_SSE3           = gmx_cmplt_pr(rsq_SSE3, rc2_SSE);
 +
 +            wco_any_SSE01      = gmx_or_pr(wco_SSE0, wco_SSE1);
 +            wco_any_SSE23      = gmx_or_pr(wco_SSE2, wco_SSE3);
 +            wco_any_SSE        = gmx_or_pr(wco_any_SSE01, wco_any_SSE23);
 +
-             InRange            = gmx_movemask_pr(wco_any_SSE);
++#ifdef GMX_HAVE_SIMD_ANYTRUE
++            InRange            = gmx_anytrue_pr(wco_any_SSE);
++#else
++            InRange            = gmx_anytrue_4xn_pr(wco_any_SSE);
++#endif
 +
 +            *ndistc += 4*GMX_SIMD_WIDTH_HERE;
 +        }
 +        if (!InRange)
 +        {
 +            cjf++;
 +        }
 +    }
 +    if (!InRange)
 +    {
 +        return;
 +    }
 +
 +    InRange = FALSE;
 +    while (!InRange && cjl > cjf)
 +    {
 +        d2       = subc_bb_dist2_sse(4, 0, bb_ci, cjl, gridj->bbj);
 +        *ndistc += 2;
 +
 +        /* Check if the distance is within the distance where
 +         * we use only the bounding box distance rbb,
 +         * or within the cut-off and there is at least one atom pair
 +         * within the cut-off.
 +         */
 +        if (d2 < rbb2)
 +        {
 +            InRange = TRUE;
 +        }
 +        else if (d2 < rl2)
 +        {
 +            xind_l  = X_IND_CJ_SIMD_4XN(CI_TO_CJ_SIMD_4XN(gridj->cell0) + cjl);
 +
 +            jx_SSE  = gmx_load_pr(x_j+xind_l+0*STRIDE_S);
 +            jy_SSE  = gmx_load_pr(x_j+xind_l+1*STRIDE_S);
 +            jz_SSE  = gmx_load_pr(x_j+xind_l+2*STRIDE_S);
 +
 +            /* Calculate distance */
 +            dx_SSE0            = gmx_sub_pr(work->ix_SSE0, jx_SSE);
 +            dy_SSE0            = gmx_sub_pr(work->iy_SSE0, jy_SSE);
 +            dz_SSE0            = gmx_sub_pr(work->iz_SSE0, jz_SSE);
 +            dx_SSE1            = gmx_sub_pr(work->ix_SSE1, jx_SSE);
 +            dy_SSE1            = gmx_sub_pr(work->iy_SSE1, jy_SSE);
 +            dz_SSE1            = gmx_sub_pr(work->iz_SSE1, jz_SSE);
 +            dx_SSE2            = gmx_sub_pr(work->ix_SSE2, jx_SSE);
 +            dy_SSE2            = gmx_sub_pr(work->iy_SSE2, jy_SSE);
 +            dz_SSE2            = gmx_sub_pr(work->iz_SSE2, jz_SSE);
 +            dx_SSE3            = gmx_sub_pr(work->ix_SSE3, jx_SSE);
 +            dy_SSE3            = gmx_sub_pr(work->iy_SSE3, jy_SSE);
 +            dz_SSE3            = gmx_sub_pr(work->iz_SSE3, jz_SSE);
 +
 +            /* rsq = dx*dx+dy*dy+dz*dz */
 +            rsq_SSE0           = gmx_calc_rsq_pr(dx_SSE0, dy_SSE0, dz_SSE0);
 +            rsq_SSE1           = gmx_calc_rsq_pr(dx_SSE1, dy_SSE1, dz_SSE1);
 +            rsq_SSE2           = gmx_calc_rsq_pr(dx_SSE2, dy_SSE2, dz_SSE2);
 +            rsq_SSE3           = gmx_calc_rsq_pr(dx_SSE3, dy_SSE3, dz_SSE3);
 +
 +            wco_SSE0           = gmx_cmplt_pr(rsq_SSE0, rc2_SSE);
 +            wco_SSE1           = gmx_cmplt_pr(rsq_SSE1, rc2_SSE);
 +            wco_SSE2           = gmx_cmplt_pr(rsq_SSE2, rc2_SSE);
 +            wco_SSE3           = gmx_cmplt_pr(rsq_SSE3, rc2_SSE);
 +
 +            wco_any_SSE01      = gmx_or_pr(wco_SSE0, wco_SSE1);
 +            wco_any_SSE23      = gmx_or_pr(wco_SSE2, wco_SSE3);
 +            wco_any_SSE        = gmx_or_pr(wco_any_SSE01, wco_any_SSE23);
 +
-             nbl->cj[nbl->ncj].excl = get_imask_x86_simd_4xn(remove_sub_diag, ci, cj);
++#ifdef GMX_HAVE_SIMD_ANYTRUE
++            InRange            = gmx_anytrue_pr(wco_any_SSE);
++#else
++            InRange            = gmx_anytrue_4xn_pr(wco_any_SSE);
++#endif
 +
 +            *ndistc += 4*GMX_SIMD_WIDTH_HERE;
 +        }
 +        if (!InRange)
 +        {
 +            cjl--;
 +        }
 +    }
 +
 +    if (cjf <= cjl)
 +    {
 +        for (cj = cjf; cj <= cjl; cj++)
 +        {
 +            /* Store cj and the interaction mask */
 +            nbl->cj[nbl->ncj].cj   = CI_TO_CJ_SIMD_4XN(gridj->cell0) + cj;
- #undef GMX_MM128_HERE
- #undef GMX_MM256_HERE
++            nbl->cj[nbl->ncj].excl = get_imask_simd_4xn(remove_sub_diag, ci, cj);
 +            nbl->ncj++;
 +        }
 +        /* Increase the closing index in i super-cell list */
 +        nbl->ci[nbl->nci].cj_ind_end = nbl->ncj;
 +    }
 +}
 +
 +#undef STRIDE_S
++#undef GMX_USE_HALF_WIDTH_SIMD_HERE
index cd59ff79893816d5665f3a643489c04123bf2500,0000000000000000000000000000000000000000..feeec50fb164d64966f1ac4cd0cae147c0e58a13
mode 100644,000000..100644
--- /dev/null
@@@ -1,4616 -1,0 +1,4660 @@@
-     int        nthread;       /* The number of threads doing PME */
 +/* -*- 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
 + */
 +/* IMPORTANT FOR DEVELOPERS:
 + *
 + * Triclinic pme stuff isn't entirely trivial, and we've experienced
 + * some bugs during development (many of them due to me). To avoid
 + * this in the future, please check the following things if you make
 + * changes in this file:
 + *
 + * 1. You should obtain identical (at least to the PME precision)
 + *    energies, forces, and virial for
 + *    a rectangular box and a triclinic one where the z (or y) axis is
 + *    tilted a whole box side. For instance you could use these boxes:
 + *
 + *    rectangular       triclinic
 + *     2  0  0           2  0  0
 + *     0  2  0           0  2  0
 + *     0  0  6           2  2  6
 + *
 + * 2. You should check the energy conservation in a triclinic box.
 + *
 + * It might seem an overkill, but better safe than sorry.
 + * /Erik 001109
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "gromacs/utility/gmxmpi.h"
 +
 +#include <stdio.h>
 +#include <string.h>
 +#include <math.h>
 +#include <assert.h>
 +#include "typedefs.h"
 +#include "txtdump.h"
 +#include "vec.h"
 +#include "gmxcomplex.h"
 +#include "smalloc.h"
 +#include "futil.h"
 +#include "coulomb.h"
 +#include "gmx_fatal.h"
 +#include "pme.h"
 +#include "network.h"
 +#include "physics.h"
 +#include "nrnb.h"
 +#include "copyrite.h"
 +#include "gmx_wallcycle.h"
 +#include "gmx_parallel_3dfft.h"
 +#include "pdbio.h"
 +#include "gmx_cyclecounter.h"
 +#include "gmx_omp.h"
 +#include "macros.h"
 +
 +/* Single precision, with SSE2 or higher available */
 +#if defined(GMX_X86_SSE2) && !defined(GMX_DOUBLE)
 +
 +#include "gmx_x86_simd_single.h"
 +
 +#define PME_SSE
 +/* Some old AMD processors could have problems with unaligned loads+stores */
 +#ifndef GMX_FAHCORE
 +#define PME_SSE_UNALIGNED
 +#endif
 +#endif
 +
 +#define DFT_TOL 1e-7
 +/* #define PRT_FORCE */
 +/* conditions for on the fly time-measurement */
 +/* #define TAKETIME (step > 1 && timesteps < 10) */
 +#define TAKETIME FALSE
 +
 +/* #define PME_TIME_THREADS */
 +
 +#ifdef GMX_DOUBLE
 +#define mpi_type MPI_DOUBLE
 +#else
 +#define mpi_type MPI_FLOAT
 +#endif
 +
 +/* GMX_CACHE_SEP should be a multiple of 16 to preserve alignment */
 +#define GMX_CACHE_SEP 64
 +
 +/* We only define a maximum to be able to use local arrays without allocation.
 + * An order larger than 12 should never be needed, even for test cases.
 + * If needed it can be changed here.
 + */
 +#define PME_ORDER_MAX 12
 +
 +/* Internal datastructures */
 +typedef struct {
 +    int send_index0;
 +    int send_nindex;
 +    int recv_index0;
 +    int recv_nindex;
 +    int recv_size;   /* Receive buffer width, used with OpenMP */
 +} pme_grid_comm_t;
 +
 +typedef struct {
 +#ifdef GMX_MPI
 +    MPI_Comm         mpi_comm;
 +#endif
 +    int              nnodes, nodeid;
 +    int             *s2g0;
 +    int             *s2g1;
 +    int              noverlap_nodes;
 +    int             *send_id, *recv_id;
 +    int              send_size; /* Send buffer width, used with OpenMP */
 +    pme_grid_comm_t *comm_data;
 +    real            *sendbuf;
 +    real            *recvbuf;
 +} pme_overlap_t;
 +
 +typedef struct {
 +    int *n;      /* Cumulative counts of the number of particles per thread */
 +    int  nalloc; /* Allocation size of i */
 +    int *i;      /* Particle indices ordered on thread index (n) */
 +} thread_plist_t;
 +
 +typedef struct {
 +    int      *thread_one;
 +    int       n;
 +    int      *ind;
 +    splinevec theta;
 +    real     *ptr_theta_z;
 +    splinevec dtheta;
 +    real     *ptr_dtheta_z;
 +} splinedata_t;
 +
 +typedef struct {
 +    int      dimind;        /* The index of the dimension, 0=x, 1=y */
 +    int      nslab;
 +    int      nodeid;
 +#ifdef GMX_MPI
 +    MPI_Comm mpi_comm;
 +#endif
 +
 +    int     *node_dest;     /* The nodes to send x and q to with DD */
 +    int     *node_src;      /* The nodes to receive x and q from with DD */
 +    int     *buf_index;     /* Index for commnode into the buffers */
 +
 +    int      maxshift;
 +
 +    int      npd;
 +    int      pd_nalloc;
 +    int     *pd;
 +    int     *count;         /* The number of atoms to send to each node */
 +    int    **count_thread;
 +    int     *rcount;        /* The number of atoms to receive */
 +
 +    int      n;
 +    int      nalloc;
 +    rvec    *x;
 +    real    *q;
 +    rvec    *f;
 +    gmx_bool bSpread;       /* These coordinates are used for spreading */
 +    int      pme_order;
 +    ivec    *idx;
 +    rvec    *fractx;            /* Fractional coordinate relative to the
 +                                 * lower cell boundary
 +                                 */
 +    int             nthread;
 +    int            *thread_idx; /* Which thread should spread which charge */
 +    thread_plist_t *thread_plist;
 +    splinedata_t   *spline;
 +} pme_atomcomm_t;
 +
 +#define FLBS  3
 +#define FLBSZ 4
 +
 +typedef struct {
 +    ivec  ci;     /* The spatial location of this grid         */
 +    ivec  n;      /* The used size of *grid, including order-1 */
 +    ivec  offset; /* The grid offset from the full node grid   */
 +    int   order;  /* PME spreading order                       */
 +    ivec  s;      /* The allocated size of *grid, s >= n       */
 +    real *grid;   /* The grid local thread, size n             */
 +} pmegrid_t;
 +
 +typedef struct {
 +    pmegrid_t  grid;         /* The full node grid (non thread-local)            */
 +    int        nthread;      /* The number of threads operating on this grid     */
 +    ivec       nc;           /* The local spatial decomposition over the threads */
 +    pmegrid_t *grid_th;      /* Array of grids for each thread                   */
 +    real      *grid_all;     /* Allocated array for the grids in *grid_th        */
 +    int      **g2t;          /* The grid to thread index                         */
 +    ivec       nthread_comm; /* The number of threads to communicate with        */
 +} pmegrids_t;
 +
 +
 +typedef struct {
 +#ifdef PME_SSE
 +    /* Masks for SSE aligned spreading and gathering */
 +    __m128 mask_SSE0[6], mask_SSE1[6];
 +#else
 +    int    dummy; /* C89 requires that struct has at least one member */
 +#endif
 +} pme_spline_work_t;
 +
 +typedef struct {
 +    /* work data for solve_pme */
 +    int      nalloc;
 +    real *   mhx;
 +    real *   mhy;
 +    real *   mhz;
 +    real *   m2;
 +    real *   denom;
 +    real *   tmp1_alloc;
 +    real *   tmp1;
 +    real *   eterm;
 +    real *   m2inv;
 +
 +    real     energy;
 +    matrix   vir;
 +} pme_work_t;
 +
 +typedef struct gmx_pme {
 +    int           ndecompdim; /* The number of decomposition dimensions */
 +    int           nodeid;     /* Our nodeid in mpi->mpi_comm */
 +    int           nodeid_major;
 +    int           nodeid_minor;
 +    int           nnodes;    /* The number of nodes doing PME */
 +    int           nnodes_major;
 +    int           nnodes_minor;
 +
 +    MPI_Comm      mpi_comm;
 +    MPI_Comm      mpi_comm_d[2]; /* Indexed on dimension, 0=x, 1=y */
 +#ifdef GMX_MPI
 +    MPI_Datatype  rvec_mpi;      /* the pme vector's MPI type */
 +#endif
 +
-     if (grids->nthread > 1)
++    gmx_bool   bUseThreads;   /* Does any of the PME ranks have nthread>1 ?  */
++    int        nthread;       /* The number of threads doing PME on our rank */
 +
 +    gmx_bool   bPPnode;       /* Node also does particle-particle forces */
 +    gmx_bool   bFEP;          /* Compute Free energy contribution */
 +    int        nkx, nky, nkz; /* Grid dimensions */
 +    gmx_bool   bP3M;          /* Do P3M: optimize the influence function */
 +    int        pme_order;
 +    real       epsilon_r;
 +
 +    pmegrids_t pmegridA;  /* Grids on which we do spreading/interpolation, includes overlap */
 +    pmegrids_t pmegridB;
 +    /* The PME charge spreading grid sizes/strides, includes pme_order-1 */
 +    int        pmegrid_nx, pmegrid_ny, pmegrid_nz;
 +    /* pmegrid_nz might be larger than strictly necessary to ensure
 +     * memory alignment, pmegrid_nz_base gives the real base size.
 +     */
 +    int     pmegrid_nz_base;
 +    /* The local PME grid starting indices */
 +    int     pmegrid_start_ix, pmegrid_start_iy, pmegrid_start_iz;
 +
 +    /* Work data for spreading and gathering */
 +    pme_spline_work_t    *spline_work;
 +
 +    real                 *fftgridA; /* Grids for FFT. With 1D FFT decomposition this can be a pointer */
 +    real                 *fftgridB; /* inside the interpolation grid, but separate for 2D PME decomp. */
 +    int                   fftgrid_nx, fftgrid_ny, fftgrid_nz;
 +
 +    t_complex            *cfftgridA;  /* Grids for complex FFT data */
 +    t_complex            *cfftgridB;
 +    int                   cfftgrid_nx, cfftgrid_ny, cfftgrid_nz;
 +
 +    gmx_parallel_3dfft_t  pfft_setupA;
 +    gmx_parallel_3dfft_t  pfft_setupB;
 +
 +    int                  *nnx, *nny, *nnz;
 +    real                 *fshx, *fshy, *fshz;
 +
 +    pme_atomcomm_t        atc[2]; /* Indexed on decomposition index */
 +    matrix                recipbox;
 +    splinevec             bsp_mod;
 +
 +    pme_overlap_t         overlap[2]; /* Indexed on dimension, 0=x, 1=y */
 +
 +    pme_atomcomm_t        atc_energy; /* Only for gmx_pme_calc_energy */
 +
 +    rvec                 *bufv;       /* Communication buffer */
 +    real                 *bufr;       /* Communication buffer */
 +    int                   buf_nalloc; /* The communication buffer size */
 +
 +    /* thread local work data for solve_pme */
 +    pme_work_t *work;
 +
 +    /* Work data for PME_redist */
 +    gmx_bool redist_init;
 +    int *    scounts;
 +    int *    rcounts;
 +    int *    sdispls;
 +    int *    rdispls;
 +    int *    sidx;
 +    int *    idxa;
 +    real *   redist_buf;
 +    int      redist_buf_nalloc;
 +
 +    /* Work data for sum_qgrid */
 +    real *   sum_qgrid_tmp;
 +    real *   sum_qgrid_dd_tmp;
 +} t_gmx_pme;
 +
 +
 +static void calc_interpolation_idx(gmx_pme_t pme, pme_atomcomm_t *atc,
 +                                   int start, int end, int thread)
 +{
 +    int             i;
 +    int            *idxptr, tix, tiy, tiz;
 +    real           *xptr, *fptr, tx, ty, tz;
 +    real            rxx, ryx, ryy, rzx, rzy, rzz;
 +    int             nx, ny, nz;
 +    int             start_ix, start_iy, start_iz;
 +    int            *g2tx, *g2ty, *g2tz;
 +    gmx_bool        bThreads;
 +    int            *thread_idx = NULL;
 +    thread_plist_t *tpl        = NULL;
 +    int            *tpl_n      = NULL;
 +    int             thread_i;
 +
 +    nx  = pme->nkx;
 +    ny  = pme->nky;
 +    nz  = pme->nkz;
 +
 +    start_ix = pme->pmegrid_start_ix;
 +    start_iy = pme->pmegrid_start_iy;
 +    start_iz = pme->pmegrid_start_iz;
 +
 +    rxx = pme->recipbox[XX][XX];
 +    ryx = pme->recipbox[YY][XX];
 +    ryy = pme->recipbox[YY][YY];
 +    rzx = pme->recipbox[ZZ][XX];
 +    rzy = pme->recipbox[ZZ][YY];
 +    rzz = pme->recipbox[ZZ][ZZ];
 +
 +    g2tx = pme->pmegridA.g2t[XX];
 +    g2ty = pme->pmegridA.g2t[YY];
 +    g2tz = pme->pmegridA.g2t[ZZ];
 +
 +    bThreads = (atc->nthread > 1);
 +    if (bThreads)
 +    {
 +        thread_idx = atc->thread_idx;
 +
 +        tpl   = &atc->thread_plist[thread];
 +        tpl_n = tpl->n;
 +        for (i = 0; i < atc->nthread; i++)
 +        {
 +            tpl_n[i] = 0;
 +        }
 +    }
 +
 +    for (i = start; i < end; i++)
 +    {
 +        xptr   = atc->x[i];
 +        idxptr = atc->idx[i];
 +        fptr   = atc->fractx[i];
 +
 +        /* Fractional coordinates along box vectors, add 2.0 to make 100% sure we are positive for triclinic boxes */
 +        tx = nx * ( xptr[XX] * rxx + xptr[YY] * ryx + xptr[ZZ] * rzx + 2.0 );
 +        ty = ny * (                  xptr[YY] * ryy + xptr[ZZ] * rzy + 2.0 );
 +        tz = nz * (                                   xptr[ZZ] * rzz + 2.0 );
 +
 +        tix = (int)(tx);
 +        tiy = (int)(ty);
 +        tiz = (int)(tz);
 +
 +        /* Because decomposition only occurs in x and y,
 +         * we never have a fraction correction in z.
 +         */
 +        fptr[XX] = tx - tix + pme->fshx[tix];
 +        fptr[YY] = ty - tiy + pme->fshy[tiy];
 +        fptr[ZZ] = tz - tiz;
 +
 +        idxptr[XX] = pme->nnx[tix];
 +        idxptr[YY] = pme->nny[tiy];
 +        idxptr[ZZ] = pme->nnz[tiz];
 +
 +#ifdef DEBUG
 +        range_check(idxptr[XX], 0, pme->pmegrid_nx);
 +        range_check(idxptr[YY], 0, pme->pmegrid_ny);
 +        range_check(idxptr[ZZ], 0, pme->pmegrid_nz);
 +#endif
 +
 +        if (bThreads)
 +        {
 +            thread_i      = g2tx[idxptr[XX]] + g2ty[idxptr[YY]] + g2tz[idxptr[ZZ]];
 +            thread_idx[i] = thread_i;
 +            tpl_n[thread_i]++;
 +        }
 +    }
 +
 +    if (bThreads)
 +    {
 +        /* Make a list of particle indices sorted on thread */
 +
 +        /* Get the cumulative count */
 +        for (i = 1; i < atc->nthread; i++)
 +        {
 +            tpl_n[i] += tpl_n[i-1];
 +        }
 +        /* The current implementation distributes particles equally
 +         * over the threads, so we could actually allocate for that
 +         * in pme_realloc_atomcomm_things.
 +         */
 +        if (tpl_n[atc->nthread-1] > tpl->nalloc)
 +        {
 +            tpl->nalloc = over_alloc_large(tpl_n[atc->nthread-1]);
 +            srenew(tpl->i, tpl->nalloc);
 +        }
 +        /* Set tpl_n to the cumulative start */
 +        for (i = atc->nthread-1; i >= 1; i--)
 +        {
 +            tpl_n[i] = tpl_n[i-1];
 +        }
 +        tpl_n[0] = 0;
 +
 +        /* Fill our thread local array with indices sorted on thread */
 +        for (i = start; i < end; i++)
 +        {
 +            tpl->i[tpl_n[atc->thread_idx[i]]++] = i;
 +        }
 +        /* Now tpl_n contains the cummulative count again */
 +    }
 +}
 +
 +static void make_thread_local_ind(pme_atomcomm_t *atc,
 +                                  int thread, splinedata_t *spline)
 +{
 +    int             n, t, i, start, end;
 +    thread_plist_t *tpl;
 +
 +    /* Combine the indices made by each thread into one index */
 +
 +    n     = 0;
 +    start = 0;
 +    for (t = 0; t < atc->nthread; t++)
 +    {
 +        tpl = &atc->thread_plist[t];
 +        /* Copy our part (start - end) from the list of thread t */
 +        if (thread > 0)
 +        {
 +            start = tpl->n[thread-1];
 +        }
 +        end = tpl->n[thread];
 +        for (i = start; i < end; i++)
 +        {
 +            spline->ind[n++] = tpl->i[i];
 +        }
 +    }
 +
 +    spline->n = n;
 +}
 +
 +
 +static void pme_calc_pidx(int start, int end,
 +                          matrix recipbox, rvec x[],
 +                          pme_atomcomm_t *atc, int *count)
 +{
 +    int   nslab, i;
 +    int   si;
 +    real *xptr, s;
 +    real  rxx, ryx, rzx, ryy, rzy;
 +    int  *pd;
 +
 +    /* Calculate PME task index (pidx) for each grid index.
 +     * Here we always assign equally sized slabs to each node
 +     * for load balancing reasons (the PME grid spacing is not used).
 +     */
 +
 +    nslab = atc->nslab;
 +    pd    = atc->pd;
 +
 +    /* Reset the count */
 +    for (i = 0; i < nslab; i++)
 +    {
 +        count[i] = 0;
 +    }
 +
 +    if (atc->dimind == 0)
 +    {
 +        rxx = recipbox[XX][XX];
 +        ryx = recipbox[YY][XX];
 +        rzx = recipbox[ZZ][XX];
 +        /* Calculate the node index in x-dimension */
 +        for (i = start; i < end; i++)
 +        {
 +            xptr   = x[i];
 +            /* Fractional coordinates along box vectors */
 +            s     = nslab*(xptr[XX]*rxx + xptr[YY]*ryx + xptr[ZZ]*rzx);
 +            si    = (int)(s + 2*nslab) % nslab;
 +            pd[i] = si;
 +            count[si]++;
 +        }
 +    }
 +    else
 +    {
 +        ryy = recipbox[YY][YY];
 +        rzy = recipbox[ZZ][YY];
 +        /* Calculate the node index in y-dimension */
 +        for (i = start; i < end; i++)
 +        {
 +            xptr   = x[i];
 +            /* Fractional coordinates along box vectors */
 +            s     = nslab*(xptr[YY]*ryy + xptr[ZZ]*rzy);
 +            si    = (int)(s + 2*nslab) % nslab;
 +            pd[i] = si;
 +            count[si]++;
 +        }
 +    }
 +}
 +
 +static void pme_calc_pidx_wrapper(int natoms, matrix recipbox, rvec x[],
 +                                  pme_atomcomm_t *atc)
 +{
 +    int nthread, thread, slab;
 +
 +    nthread = atc->nthread;
 +
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +    for (thread = 0; thread < nthread; thread++)
 +    {
 +        pme_calc_pidx(natoms* thread   /nthread,
 +                      natoms*(thread+1)/nthread,
 +                      recipbox, x, atc, atc->count_thread[thread]);
 +    }
 +    /* Non-parallel reduction, since nslab is small */
 +
 +    for (thread = 1; thread < nthread; thread++)
 +    {
 +        for (slab = 0; slab < atc->nslab; slab++)
 +        {
 +            atc->count_thread[0][slab] += atc->count_thread[thread][slab];
 +        }
 +    }
 +}
 +
 +static void realloc_splinevec(splinevec th, real **ptr_z, int nalloc)
 +{
 +    const int padding = 4;
 +    int       i;
 +
 +    srenew(th[XX], nalloc);
 +    srenew(th[YY], nalloc);
 +    /* In z we add padding, this is only required for the aligned SSE code */
 +    srenew(*ptr_z, nalloc+2*padding);
 +    th[ZZ] = *ptr_z + padding;
 +
 +    for (i = 0; i < padding; i++)
 +    {
 +        (*ptr_z)[               i] = 0;
 +        (*ptr_z)[padding+nalloc+i] = 0;
 +    }
 +}
 +
 +static void pme_realloc_splinedata(splinedata_t *spline, pme_atomcomm_t *atc)
 +{
 +    int i, d;
 +
 +    srenew(spline->ind, atc->nalloc);
 +    /* Initialize the index to identity so it works without threads */
 +    for (i = 0; i < atc->nalloc; i++)
 +    {
 +        spline->ind[i] = i;
 +    }
 +
 +    realloc_splinevec(spline->theta, &spline->ptr_theta_z,
 +                      atc->pme_order*atc->nalloc);
 +    realloc_splinevec(spline->dtheta, &spline->ptr_dtheta_z,
 +                      atc->pme_order*atc->nalloc);
 +}
 +
 +static void pme_realloc_atomcomm_things(pme_atomcomm_t *atc)
 +{
 +    int nalloc_old, i, j, nalloc_tpl;
 +
 +    /* We have to avoid a NULL pointer for atc->x to avoid
 +     * possible fatal errors in MPI routines.
 +     */
 +    if (atc->n > atc->nalloc || atc->nalloc == 0)
 +    {
 +        nalloc_old  = atc->nalloc;
 +        atc->nalloc = over_alloc_dd(max(atc->n, 1));
 +
 +        if (atc->nslab > 1)
 +        {
 +            srenew(atc->x, atc->nalloc);
 +            srenew(atc->q, atc->nalloc);
 +            srenew(atc->f, atc->nalloc);
 +            for (i = nalloc_old; i < atc->nalloc; i++)
 +            {
 +                clear_rvec(atc->f[i]);
 +            }
 +        }
 +        if (atc->bSpread)
 +        {
 +            srenew(atc->fractx, atc->nalloc);
 +            srenew(atc->idx, atc->nalloc);
 +
 +            if (atc->nthread > 1)
 +            {
 +                srenew(atc->thread_idx, atc->nalloc);
 +            }
 +
 +            for (i = 0; i < atc->nthread; i++)
 +            {
 +                pme_realloc_splinedata(&atc->spline[i], atc);
 +            }
 +        }
 +    }
 +}
 +
 +static void pmeredist_pd(gmx_pme_t pme, gmx_bool forw,
 +                         int n, gmx_bool bXF, rvec *x_f, real *charge,
 +                         pme_atomcomm_t *atc)
 +/* Redistribute particle data for PME calculation */
 +/* domain decomposition by x coordinate           */
 +{
 +    int *idxa;
 +    int  i, ii;
 +
 +    if (FALSE == pme->redist_init)
 +    {
 +        snew(pme->scounts, atc->nslab);
 +        snew(pme->rcounts, atc->nslab);
 +        snew(pme->sdispls, atc->nslab);
 +        snew(pme->rdispls, atc->nslab);
 +        snew(pme->sidx, atc->nslab);
 +        pme->redist_init = TRUE;
 +    }
 +    if (n > pme->redist_buf_nalloc)
 +    {
 +        pme->redist_buf_nalloc = over_alloc_dd(n);
 +        srenew(pme->redist_buf, pme->redist_buf_nalloc*DIM);
 +    }
 +
 +    pme->idxa = atc->pd;
 +
 +#ifdef GMX_MPI
 +    if (forw && bXF)
 +    {
 +        /* forward, redistribution from pp to pme */
 +
 +        /* Calculate send counts and exchange them with other nodes */
 +        for (i = 0; (i < atc->nslab); i++)
 +        {
 +            pme->scounts[i] = 0;
 +        }
 +        for (i = 0; (i < n); i++)
 +        {
 +            pme->scounts[pme->idxa[i]]++;
 +        }
 +        MPI_Alltoall( pme->scounts, 1, MPI_INT, pme->rcounts, 1, MPI_INT, atc->mpi_comm);
 +
 +        /* Calculate send and receive displacements and index into send
 +           buffer */
 +        pme->sdispls[0] = 0;
 +        pme->rdispls[0] = 0;
 +        pme->sidx[0]    = 0;
 +        for (i = 1; i < atc->nslab; i++)
 +        {
 +            pme->sdispls[i] = pme->sdispls[i-1]+pme->scounts[i-1];
 +            pme->rdispls[i] = pme->rdispls[i-1]+pme->rcounts[i-1];
 +            pme->sidx[i]    = pme->sdispls[i];
 +        }
 +        /* Total # of particles to be received */
 +        atc->n = pme->rdispls[atc->nslab-1] + pme->rcounts[atc->nslab-1];
 +
 +        pme_realloc_atomcomm_things(atc);
 +
 +        /* Copy particle coordinates into send buffer and exchange*/
 +        for (i = 0; (i < n); i++)
 +        {
 +            ii = DIM*pme->sidx[pme->idxa[i]];
 +            pme->sidx[pme->idxa[i]]++;
 +            pme->redist_buf[ii+XX] = x_f[i][XX];
 +            pme->redist_buf[ii+YY] = x_f[i][YY];
 +            pme->redist_buf[ii+ZZ] = x_f[i][ZZ];
 +        }
 +        MPI_Alltoallv(pme->redist_buf, pme->scounts, pme->sdispls,
 +                      pme->rvec_mpi, atc->x, pme->rcounts, pme->rdispls,
 +                      pme->rvec_mpi, atc->mpi_comm);
 +    }
 +    if (forw)
 +    {
 +        /* Copy charge into send buffer and exchange*/
 +        for (i = 0; i < atc->nslab; i++)
 +        {
 +            pme->sidx[i] = pme->sdispls[i];
 +        }
 +        for (i = 0; (i < n); i++)
 +        {
 +            ii = pme->sidx[pme->idxa[i]];
 +            pme->sidx[pme->idxa[i]]++;
 +            pme->redist_buf[ii] = charge[i];
 +        }
 +        MPI_Alltoallv(pme->redist_buf, pme->scounts, pme->sdispls, mpi_type,
 +                      atc->q, pme->rcounts, pme->rdispls, mpi_type,
 +                      atc->mpi_comm);
 +    }
 +    else   /* backward, redistribution from pme to pp */
 +    {
 +        MPI_Alltoallv(atc->f, pme->rcounts, pme->rdispls, pme->rvec_mpi,
 +                      pme->redist_buf, pme->scounts, pme->sdispls,
 +                      pme->rvec_mpi, atc->mpi_comm);
 +
 +        /* Copy data from receive buffer */
 +        for (i = 0; i < atc->nslab; i++)
 +        {
 +            pme->sidx[i] = pme->sdispls[i];
 +        }
 +        for (i = 0; (i < n); i++)
 +        {
 +            ii          = DIM*pme->sidx[pme->idxa[i]];
 +            x_f[i][XX] += pme->redist_buf[ii+XX];
 +            x_f[i][YY] += pme->redist_buf[ii+YY];
 +            x_f[i][ZZ] += pme->redist_buf[ii+ZZ];
 +            pme->sidx[pme->idxa[i]]++;
 +        }
 +    }
 +#endif
 +}
 +
 +static void pme_dd_sendrecv(pme_atomcomm_t *atc,
 +                            gmx_bool bBackward, int shift,
 +                            void *buf_s, int nbyte_s,
 +                            void *buf_r, int nbyte_r)
 +{
 +#ifdef GMX_MPI
 +    int        dest, src;
 +    MPI_Status stat;
 +
 +    if (bBackward == FALSE)
 +    {
 +        dest = atc->node_dest[shift];
 +        src  = atc->node_src[shift];
 +    }
 +    else
 +    {
 +        dest = atc->node_src[shift];
 +        src  = atc->node_dest[shift];
 +    }
 +
 +    if (nbyte_s > 0 && nbyte_r > 0)
 +    {
 +        MPI_Sendrecv(buf_s, nbyte_s, MPI_BYTE,
 +                     dest, shift,
 +                     buf_r, nbyte_r, MPI_BYTE,
 +                     src, shift,
 +                     atc->mpi_comm, &stat);
 +    }
 +    else if (nbyte_s > 0)
 +    {
 +        MPI_Send(buf_s, nbyte_s, MPI_BYTE,
 +                 dest, shift,
 +                 atc->mpi_comm);
 +    }
 +    else if (nbyte_r > 0)
 +    {
 +        MPI_Recv(buf_r, nbyte_r, MPI_BYTE,
 +                 src, shift,
 +                 atc->mpi_comm, &stat);
 +    }
 +#endif
 +}
 +
 +static void dd_pmeredist_x_q(gmx_pme_t pme,
 +                             int n, gmx_bool bX, rvec *x, real *charge,
 +                             pme_atomcomm_t *atc)
 +{
 +    int *commnode, *buf_index;
 +    int  nnodes_comm, i, nsend, local_pos, buf_pos, node, scount, rcount;
 +
 +    commnode  = atc->node_dest;
 +    buf_index = atc->buf_index;
 +
 +    nnodes_comm = min(2*atc->maxshift, atc->nslab-1);
 +
 +    nsend = 0;
 +    for (i = 0; i < nnodes_comm; i++)
 +    {
 +        buf_index[commnode[i]] = nsend;
 +        nsend                 += atc->count[commnode[i]];
 +    }
 +    if (bX)
 +    {
 +        if (atc->count[atc->nodeid] + nsend != n)
 +        {
 +            gmx_fatal(FARGS, "%d particles communicated to PME node %d are more than 2/3 times the cut-off out of the domain decomposition cell of their charge group in dimension %c.\n"
 +                      "This usually means that your system is not well equilibrated.",
 +                      n - (atc->count[atc->nodeid] + nsend),
 +                      pme->nodeid, 'x'+atc->dimind);
 +        }
 +
 +        if (nsend > pme->buf_nalloc)
 +        {
 +            pme->buf_nalloc = over_alloc_dd(nsend);
 +            srenew(pme->bufv, pme->buf_nalloc);
 +            srenew(pme->bufr, pme->buf_nalloc);
 +        }
 +
 +        atc->n = atc->count[atc->nodeid];
 +        for (i = 0; i < nnodes_comm; i++)
 +        {
 +            scount = atc->count[commnode[i]];
 +            /* Communicate the count */
 +            if (debug)
 +            {
 +                fprintf(debug, "dimind %d PME node %d send to node %d: %d\n",
 +                        atc->dimind, atc->nodeid, commnode[i], scount);
 +            }
 +            pme_dd_sendrecv(atc, FALSE, i,
 +                            &scount, sizeof(int),
 +                            &atc->rcount[i], sizeof(int));
 +            atc->n += atc->rcount[i];
 +        }
 +
 +        pme_realloc_atomcomm_things(atc);
 +    }
 +
 +    local_pos = 0;
 +    for (i = 0; i < n; i++)
 +    {
 +        node = atc->pd[i];
 +        if (node == atc->nodeid)
 +        {
 +            /* Copy direct to the receive buffer */
 +            if (bX)
 +            {
 +                copy_rvec(x[i], atc->x[local_pos]);
 +            }
 +            atc->q[local_pos] = charge[i];
 +            local_pos++;
 +        }
 +        else
 +        {
 +            /* Copy to the send buffer */
 +            if (bX)
 +            {
 +                copy_rvec(x[i], pme->bufv[buf_index[node]]);
 +            }
 +            pme->bufr[buf_index[node]] = charge[i];
 +            buf_index[node]++;
 +        }
 +    }
 +
 +    buf_pos = 0;
 +    for (i = 0; i < nnodes_comm; i++)
 +    {
 +        scount = atc->count[commnode[i]];
 +        rcount = atc->rcount[i];
 +        if (scount > 0 || rcount > 0)
 +        {
 +            if (bX)
 +            {
 +                /* Communicate the coordinates */
 +                pme_dd_sendrecv(atc, FALSE, i,
 +                                pme->bufv[buf_pos], scount*sizeof(rvec),
 +                                atc->x[local_pos], rcount*sizeof(rvec));
 +            }
 +            /* Communicate the charges */
 +            pme_dd_sendrecv(atc, FALSE, i,
 +                            pme->bufr+buf_pos, scount*sizeof(real),
 +                            atc->q+local_pos, rcount*sizeof(real));
 +            buf_pos   += scount;
 +            local_pos += atc->rcount[i];
 +        }
 +    }
 +}
 +
 +static void dd_pmeredist_f(gmx_pme_t pme, pme_atomcomm_t *atc,
 +                           int n, rvec *f,
 +                           gmx_bool bAddF)
 +{
 +    int *commnode, *buf_index;
 +    int  nnodes_comm, local_pos, buf_pos, i, scount, rcount, node;
 +
 +    commnode  = atc->node_dest;
 +    buf_index = atc->buf_index;
 +
 +    nnodes_comm = min(2*atc->maxshift, atc->nslab-1);
 +
 +    local_pos = atc->count[atc->nodeid];
 +    buf_pos   = 0;
 +    for (i = 0; i < nnodes_comm; i++)
 +    {
 +        scount = atc->rcount[i];
 +        rcount = atc->count[commnode[i]];
 +        if (scount > 0 || rcount > 0)
 +        {
 +            /* Communicate the forces */
 +            pme_dd_sendrecv(atc, TRUE, i,
 +                            atc->f[local_pos], scount*sizeof(rvec),
 +                            pme->bufv[buf_pos], rcount*sizeof(rvec));
 +            local_pos += scount;
 +        }
 +        buf_index[commnode[i]] = buf_pos;
 +        buf_pos               += rcount;
 +    }
 +
 +    local_pos = 0;
 +    if (bAddF)
 +    {
 +        for (i = 0; i < n; i++)
 +        {
 +            node = atc->pd[i];
 +            if (node == atc->nodeid)
 +            {
 +                /* Add from the local force array */
 +                rvec_inc(f[i], atc->f[local_pos]);
 +                local_pos++;
 +            }
 +            else
 +            {
 +                /* Add from the receive buffer */
 +                rvec_inc(f[i], pme->bufv[buf_index[node]]);
 +                buf_index[node]++;
 +            }
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; i < n; i++)
 +        {
 +            node = atc->pd[i];
 +            if (node == atc->nodeid)
 +            {
 +                /* Copy from the local force array */
 +                copy_rvec(atc->f[local_pos], f[i]);
 +                local_pos++;
 +            }
 +            else
 +            {
 +                /* Copy from the receive buffer */
 +                copy_rvec(pme->bufv[buf_index[node]], f[i]);
 +                buf_index[node]++;
 +            }
 +        }
 +    }
 +}
 +
 +#ifdef GMX_MPI
 +static void
 +gmx_sum_qgrid_dd(gmx_pme_t pme, real *grid, int direction)
 +{
 +    pme_overlap_t *overlap;
 +    int            send_index0, send_nindex;
 +    int            recv_index0, recv_nindex;
 +    MPI_Status     stat;
 +    int            i, j, k, ix, iy, iz, icnt;
 +    int            ipulse, send_id, recv_id, datasize;
 +    real          *p;
 +    real          *sendptr, *recvptr;
 +
 +    /* Start with minor-rank communication. This is a bit of a pain since it is not contiguous */
 +    overlap = &pme->overlap[1];
 +
 +    for (ipulse = 0; ipulse < overlap->noverlap_nodes; ipulse++)
 +    {
 +        /* Since we have already (un)wrapped the overlap in the z-dimension,
 +         * we only have to communicate 0 to nkz (not pmegrid_nz).
 +         */
 +        if (direction == GMX_SUM_QGRID_FORWARD)
 +        {
 +            send_id       = overlap->send_id[ipulse];
 +            recv_id       = overlap->recv_id[ipulse];
 +            send_index0   = overlap->comm_data[ipulse].send_index0;
 +            send_nindex   = overlap->comm_data[ipulse].send_nindex;
 +            recv_index0   = overlap->comm_data[ipulse].recv_index0;
 +            recv_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +        }
 +        else
 +        {
 +            send_id       = overlap->recv_id[ipulse];
 +            recv_id       = overlap->send_id[ipulse];
 +            send_index0   = overlap->comm_data[ipulse].recv_index0;
 +            send_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +            recv_index0   = overlap->comm_data[ipulse].send_index0;
 +            recv_nindex   = overlap->comm_data[ipulse].send_nindex;
 +        }
 +
 +        /* Copy data to contiguous send buffer */
 +        if (debug)
 +        {
 +            fprintf(debug, "PME send node %d %d -> %d grid start %d Communicating %d to %d\n",
 +                    pme->nodeid, overlap->nodeid, send_id,
 +                    pme->pmegrid_start_iy,
 +                    send_index0-pme->pmegrid_start_iy,
 +                    send_index0-pme->pmegrid_start_iy+send_nindex);
 +        }
 +        icnt = 0;
 +        for (i = 0; i < pme->pmegrid_nx; i++)
 +        {
 +            ix = i;
 +            for (j = 0; j < send_nindex; j++)
 +            {
 +                iy = j + send_index0 - pme->pmegrid_start_iy;
 +                for (k = 0; k < pme->nkz; k++)
 +                {
 +                    iz = k;
 +                    overlap->sendbuf[icnt++] = grid[ix*(pme->pmegrid_ny*pme->pmegrid_nz)+iy*(pme->pmegrid_nz)+iz];
 +                }
 +            }
 +        }
 +
 +        datasize      = pme->pmegrid_nx * pme->nkz;
 +
 +        MPI_Sendrecv(overlap->sendbuf, send_nindex*datasize, GMX_MPI_REAL,
 +                     send_id, ipulse,
 +                     overlap->recvbuf, recv_nindex*datasize, GMX_MPI_REAL,
 +                     recv_id, ipulse,
 +                     overlap->mpi_comm, &stat);
 +
 +        /* Get data from contiguous recv buffer */
 +        if (debug)
 +        {
 +            fprintf(debug, "PME recv node %d %d <- %d grid start %d Communicating %d to %d\n",
 +                    pme->nodeid, overlap->nodeid, recv_id,
 +                    pme->pmegrid_start_iy,
 +                    recv_index0-pme->pmegrid_start_iy,
 +                    recv_index0-pme->pmegrid_start_iy+recv_nindex);
 +        }
 +        icnt = 0;
 +        for (i = 0; i < pme->pmegrid_nx; i++)
 +        {
 +            ix = i;
 +            for (j = 0; j < recv_nindex; j++)
 +            {
 +                iy = j + recv_index0 - pme->pmegrid_start_iy;
 +                for (k = 0; k < pme->nkz; k++)
 +                {
 +                    iz = k;
 +                    if (direction == GMX_SUM_QGRID_FORWARD)
 +                    {
 +                        grid[ix*(pme->pmegrid_ny*pme->pmegrid_nz)+iy*(pme->pmegrid_nz)+iz] += overlap->recvbuf[icnt++];
 +                    }
 +                    else
 +                    {
 +                        grid[ix*(pme->pmegrid_ny*pme->pmegrid_nz)+iy*(pme->pmegrid_nz)+iz]  = overlap->recvbuf[icnt++];
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Major dimension is easier, no copying required,
 +     * but we might have to sum to separate array.
 +     * Since we don't copy, we have to communicate up to pmegrid_nz,
 +     * not nkz as for the minor direction.
 +     */
 +    overlap = &pme->overlap[0];
 +
 +    for (ipulse = 0; ipulse < overlap->noverlap_nodes; ipulse++)
 +    {
 +        if (direction == GMX_SUM_QGRID_FORWARD)
 +        {
 +            send_id       = overlap->send_id[ipulse];
 +            recv_id       = overlap->recv_id[ipulse];
 +            send_index0   = overlap->comm_data[ipulse].send_index0;
 +            send_nindex   = overlap->comm_data[ipulse].send_nindex;
 +            recv_index0   = overlap->comm_data[ipulse].recv_index0;
 +            recv_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +            recvptr       = overlap->recvbuf;
 +        }
 +        else
 +        {
 +            send_id       = overlap->recv_id[ipulse];
 +            recv_id       = overlap->send_id[ipulse];
 +            send_index0   = overlap->comm_data[ipulse].recv_index0;
 +            send_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +            recv_index0   = overlap->comm_data[ipulse].send_index0;
 +            recv_nindex   = overlap->comm_data[ipulse].send_nindex;
 +            recvptr       = grid + (recv_index0-pme->pmegrid_start_ix)*(pme->pmegrid_ny*pme->pmegrid_nz);
 +        }
 +
 +        sendptr       = grid + (send_index0-pme->pmegrid_start_ix)*(pme->pmegrid_ny*pme->pmegrid_nz);
 +        datasize      = pme->pmegrid_ny * pme->pmegrid_nz;
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "PME send node %d %d -> %d grid start %d Communicating %d to %d\n",
 +                    pme->nodeid, overlap->nodeid, send_id,
 +                    pme->pmegrid_start_ix,
 +                    send_index0-pme->pmegrid_start_ix,
 +                    send_index0-pme->pmegrid_start_ix+send_nindex);
 +            fprintf(debug, "PME recv node %d %d <- %d grid start %d Communicating %d to %d\n",
 +                    pme->nodeid, overlap->nodeid, recv_id,
 +                    pme->pmegrid_start_ix,
 +                    recv_index0-pme->pmegrid_start_ix,
 +                    recv_index0-pme->pmegrid_start_ix+recv_nindex);
 +        }
 +
 +        MPI_Sendrecv(sendptr, send_nindex*datasize, GMX_MPI_REAL,
 +                     send_id, ipulse,
 +                     recvptr, recv_nindex*datasize, GMX_MPI_REAL,
 +                     recv_id, ipulse,
 +                     overlap->mpi_comm, &stat);
 +
 +        /* ADD data from contiguous recv buffer */
 +        if (direction == GMX_SUM_QGRID_FORWARD)
 +        {
 +            p = grid + (recv_index0-pme->pmegrid_start_ix)*(pme->pmegrid_ny*pme->pmegrid_nz);
 +            for (i = 0; i < recv_nindex*datasize; i++)
 +            {
 +                p[i] += overlap->recvbuf[i];
 +            }
 +        }
 +    }
 +}
 +#endif
 +
 +
 +static int
 +copy_pmegrid_to_fftgrid(gmx_pme_t pme, real *pmegrid, real *fftgrid)
 +{
 +    ivec    local_fft_ndata, local_fft_offset, local_fft_size;
 +    ivec    local_pme_size;
 +    int     i, ix, iy, iz;
 +    int     pmeidx, fftidx;
 +
 +    /* Dimensions should be identical for A/B grid, so we just use A here */
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setupA,
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +
 +    local_pme_size[0] = pme->pmegrid_nx;
 +    local_pme_size[1] = pme->pmegrid_ny;
 +    local_pme_size[2] = pme->pmegrid_nz;
 +
 +    /* The fftgrid is always 'justified' to the lower-left corner of the PME grid,
 +       the offset is identical, and the PME grid always has more data (due to overlap)
 +     */
 +    {
 +#ifdef DEBUG_PME
 +        FILE *fp, *fp2;
 +        char  fn[STRLEN], format[STRLEN];
 +        real  val;
 +        sprintf(fn, "pmegrid%d.pdb", pme->nodeid);
 +        fp = ffopen(fn, "w");
 +        sprintf(fn, "pmegrid%d.txt", pme->nodeid);
 +        fp2 = ffopen(fn, "w");
 +        sprintf(format, "%s%s\n", pdbformat, "%6.2f%6.2f");
 +#endif
 +
 +        for (ix = 0; ix < local_fft_ndata[XX]; ix++)
 +        {
 +            for (iy = 0; iy < local_fft_ndata[YY]; iy++)
 +            {
 +                for (iz = 0; iz < local_fft_ndata[ZZ]; iz++)
 +                {
 +                    pmeidx          = ix*(local_pme_size[YY]*local_pme_size[ZZ])+iy*(local_pme_size[ZZ])+iz;
 +                    fftidx          = ix*(local_fft_size[YY]*local_fft_size[ZZ])+iy*(local_fft_size[ZZ])+iz;
 +                    fftgrid[fftidx] = pmegrid[pmeidx];
 +#ifdef DEBUG_PME
 +                    val = 100*pmegrid[pmeidx];
 +                    if (pmegrid[pmeidx] != 0)
 +                    {
 +                        fprintf(fp, format, "ATOM", pmeidx, "CA", "GLY", ' ', pmeidx, ' ',
 +                                5.0*ix, 5.0*iy, 5.0*iz, 1.0, val);
 +                    }
 +                    if (pmegrid[pmeidx] != 0)
 +                    {
 +                        fprintf(fp2, "%-12s  %5d  %5d  %5d  %12.5e\n",
 +                                "qgrid",
 +                                pme->pmegrid_start_ix + ix,
 +                                pme->pmegrid_start_iy + iy,
 +                                pme->pmegrid_start_iz + iz,
 +                                pmegrid[pmeidx]);
 +                    }
 +#endif
 +                }
 +            }
 +        }
 +#ifdef DEBUG_PME
 +        ffclose(fp);
 +        ffclose(fp2);
 +#endif
 +    }
 +    return 0;
 +}
 +
 +
 +static gmx_cycles_t omp_cyc_start()
 +{
 +    return gmx_cycles_read();
 +}
 +
 +static gmx_cycles_t omp_cyc_end(gmx_cycles_t c)
 +{
 +    return gmx_cycles_read() - c;
 +}
 +
 +
 +static int
 +copy_fftgrid_to_pmegrid(gmx_pme_t pme, const real *fftgrid, real *pmegrid,
 +                        int nthread, int thread)
 +{
 +    ivec          local_fft_ndata, local_fft_offset, local_fft_size;
 +    ivec          local_pme_size;
 +    int           ixy0, ixy1, ixy, ix, iy, iz;
 +    int           pmeidx, fftidx;
 +#ifdef PME_TIME_THREADS
 +    gmx_cycles_t  c1;
 +    static double cs1 = 0;
 +    static int    cnt = 0;
 +#endif
 +
 +#ifdef PME_TIME_THREADS
 +    c1 = omp_cyc_start();
 +#endif
 +    /* Dimensions should be identical for A/B grid, so we just use A here */
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setupA,
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +
 +    local_pme_size[0] = pme->pmegrid_nx;
 +    local_pme_size[1] = pme->pmegrid_ny;
 +    local_pme_size[2] = pme->pmegrid_nz;
 +
 +    /* The fftgrid is always 'justified' to the lower-left corner of the PME grid,
 +       the offset is identical, and the PME grid always has more data (due to overlap)
 +     */
 +    ixy0 = ((thread  )*local_fft_ndata[XX]*local_fft_ndata[YY])/nthread;
 +    ixy1 = ((thread+1)*local_fft_ndata[XX]*local_fft_ndata[YY])/nthread;
 +
 +    for (ixy = ixy0; ixy < ixy1; ixy++)
 +    {
 +        ix = ixy/local_fft_ndata[YY];
 +        iy = ixy - ix*local_fft_ndata[YY];
 +
 +        pmeidx = (ix*local_pme_size[YY] + iy)*local_pme_size[ZZ];
 +        fftidx = (ix*local_fft_size[YY] + iy)*local_fft_size[ZZ];
 +        for (iz = 0; iz < local_fft_ndata[ZZ]; iz++)
 +        {
 +            pmegrid[pmeidx+iz] = fftgrid[fftidx+iz];
 +        }
 +    }
 +
 +#ifdef PME_TIME_THREADS
 +    c1   = omp_cyc_end(c1);
 +    cs1 += (double)c1;
 +    cnt++;
 +    if (cnt % 20 == 0)
 +    {
 +        printf("copy %.2f\n", cs1*1e-9);
 +    }
 +#endif
 +
 +    return 0;
 +}
 +
 +
 +static void
 +wrap_periodic_pmegrid(gmx_pme_t pme, real *pmegrid)
 +{
 +    int     nx, ny, nz, pnx, pny, pnz, ny_x, overlap, ix, iy, iz;
 +
 +    nx = pme->nkx;
 +    ny = pme->nky;
 +    nz = pme->nkz;
 +
 +    pnx = pme->pmegrid_nx;
 +    pny = pme->pmegrid_ny;
 +    pnz = pme->pmegrid_nz;
 +
 +    overlap = pme->pme_order - 1;
 +
 +    /* Add periodic overlap in z */
 +    for (ix = 0; ix < pme->pmegrid_nx; ix++)
 +    {
 +        for (iy = 0; iy < pme->pmegrid_ny; iy++)
 +        {
 +            for (iz = 0; iz < overlap; iz++)
 +            {
 +                pmegrid[(ix*pny+iy)*pnz+iz] +=
 +                    pmegrid[(ix*pny+iy)*pnz+nz+iz];
 +            }
 +        }
 +    }
 +
 +    if (pme->nnodes_minor == 1)
 +    {
 +        for (ix = 0; ix < pme->pmegrid_nx; ix++)
 +        {
 +            for (iy = 0; iy < overlap; iy++)
 +            {
 +                for (iz = 0; iz < nz; iz++)
 +                {
 +                    pmegrid[(ix*pny+iy)*pnz+iz] +=
 +                        pmegrid[(ix*pny+ny+iy)*pnz+iz];
 +                }
 +            }
 +        }
 +    }
 +
 +    if (pme->nnodes_major == 1)
 +    {
 +        ny_x = (pme->nnodes_minor == 1 ? ny : pme->pmegrid_ny);
 +
 +        for (ix = 0; ix < overlap; ix++)
 +        {
 +            for (iy = 0; iy < ny_x; iy++)
 +            {
 +                for (iz = 0; iz < nz; iz++)
 +                {
 +                    pmegrid[(ix*pny+iy)*pnz+iz] +=
 +                        pmegrid[((nx+ix)*pny+iy)*pnz+iz];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +
 +static void
 +unwrap_periodic_pmegrid(gmx_pme_t pme, real *pmegrid)
 +{
 +    int     nx, ny, nz, pnx, pny, pnz, ny_x, overlap, ix;
 +
 +    nx = pme->nkx;
 +    ny = pme->nky;
 +    nz = pme->nkz;
 +
 +    pnx = pme->pmegrid_nx;
 +    pny = pme->pmegrid_ny;
 +    pnz = pme->pmegrid_nz;
 +
 +    overlap = pme->pme_order - 1;
 +
 +    if (pme->nnodes_major == 1)
 +    {
 +        ny_x = (pme->nnodes_minor == 1 ? ny : pme->pmegrid_ny);
 +
 +        for (ix = 0; ix < overlap; ix++)
 +        {
 +            int iy, iz;
 +
 +            for (iy = 0; iy < ny_x; iy++)
 +            {
 +                for (iz = 0; iz < nz; iz++)
 +                {
 +                    pmegrid[((nx+ix)*pny+iy)*pnz+iz] =
 +                        pmegrid[(ix*pny+iy)*pnz+iz];
 +                }
 +            }
 +        }
 +    }
 +
 +    if (pme->nnodes_minor == 1)
 +    {
 +#pragma omp parallel for num_threads(pme->nthread) schedule(static)
 +        for (ix = 0; ix < pme->pmegrid_nx; ix++)
 +        {
 +            int iy, iz;
 +
 +            for (iy = 0; iy < overlap; iy++)
 +            {
 +                for (iz = 0; iz < nz; iz++)
 +                {
 +                    pmegrid[(ix*pny+ny+iy)*pnz+iz] =
 +                        pmegrid[(ix*pny+iy)*pnz+iz];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Copy periodic overlap in z */
 +#pragma omp parallel for num_threads(pme->nthread) schedule(static)
 +    for (ix = 0; ix < pme->pmegrid_nx; ix++)
 +    {
 +        int iy, iz;
 +
 +        for (iy = 0; iy < pme->pmegrid_ny; iy++)
 +        {
 +            for (iz = 0; iz < overlap; iz++)
 +            {
 +                pmegrid[(ix*pny+iy)*pnz+nz+iz] =
 +                    pmegrid[(ix*pny+iy)*pnz+iz];
 +            }
 +        }
 +    }
 +}
 +
 +static void clear_grid(int nx, int ny, int nz, real *grid,
 +                       ivec fs, int *flag,
 +                       int fx, int fy, int fz,
 +                       int order)
 +{
 +    int nc, ncz;
 +    int fsx, fsy, fsz, gx, gy, gz, g0x, g0y, x, y, z;
 +    int flind;
 +
 +    nc  = 2 + (order - 2)/FLBS;
 +    ncz = 2 + (order - 2)/FLBSZ;
 +
 +    for (fsx = fx; fsx < fx+nc; fsx++)
 +    {
 +        for (fsy = fy; fsy < fy+nc; fsy++)
 +        {
 +            for (fsz = fz; fsz < fz+ncz; fsz++)
 +            {
 +                flind = (fsx*fs[YY] + fsy)*fs[ZZ] + fsz;
 +                if (flag[flind] == 0)
 +                {
 +                    gx  = fsx*FLBS;
 +                    gy  = fsy*FLBS;
 +                    gz  = fsz*FLBSZ;
 +                    g0x = (gx*ny + gy)*nz + gz;
 +                    for (x = 0; x < FLBS; x++)
 +                    {
 +                        g0y = g0x;
 +                        for (y = 0; y < FLBS; y++)
 +                        {
 +                            for (z = 0; z < FLBSZ; z++)
 +                            {
 +                                grid[g0y+z] = 0;
 +                            }
 +                            g0y += nz;
 +                        }
 +                        g0x += ny*nz;
 +                    }
 +
 +                    flag[flind] = 1;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/* This has to be a macro to enable full compiler optimization with xlC (and probably others too) */
 +#define DO_BSPLINE(order)                            \
 +    for (ithx = 0; (ithx < order); ithx++)                    \
 +    {                                                    \
 +        index_x = (i0+ithx)*pny*pnz;                     \
 +        valx    = qn*thx[ithx];                          \
 +                                                     \
 +        for (ithy = 0; (ithy < order); ithy++)                \
 +        {                                                \
 +            valxy    = valx*thy[ithy];                   \
 +            index_xy = index_x+(j0+ithy)*pnz;            \
 +                                                     \
 +            for (ithz = 0; (ithz < order); ithz++)            \
 +            {                                            \
 +                index_xyz        = index_xy+(k0+ithz);   \
 +                grid[index_xyz] += valxy*thz[ithz];      \
 +            }                                            \
 +        }                                                \
 +    }
 +
 +
 +static void spread_q_bsplines_thread(pmegrid_t *pmegrid,
 +                                     pme_atomcomm_t *atc, splinedata_t *spline,
 +                                     pme_spline_work_t *work)
 +{
 +
 +    /* spread charges from home atoms to local grid */
 +    real          *grid;
 +    pme_overlap_t *ol;
 +    int            b, i, nn, n, ithx, ithy, ithz, i0, j0, k0;
 +    int       *    idxptr;
 +    int            order, norder, index_x, index_xy, index_xyz;
 +    real           valx, valxy, qn;
 +    real          *thx, *thy, *thz;
 +    int            localsize, bndsize;
 +    int            pnx, pny, pnz, ndatatot;
 +    int            offx, offy, offz;
 +
 +    pnx = pmegrid->s[XX];
 +    pny = pmegrid->s[YY];
 +    pnz = pmegrid->s[ZZ];
 +
 +    offx = pmegrid->offset[XX];
 +    offy = pmegrid->offset[YY];
 +    offz = pmegrid->offset[ZZ];
 +
 +    ndatatot = pnx*pny*pnz;
 +    grid     = pmegrid->grid;
 +    for (i = 0; i < ndatatot; i++)
 +    {
 +        grid[i] = 0;
 +    }
 +
 +    order = pmegrid->order;
 +
 +    for (nn = 0; nn < spline->n; nn++)
 +    {
 +        n  = spline->ind[nn];
 +        qn = atc->q[n];
 +
 +        if (qn != 0)
 +        {
 +            idxptr = atc->idx[n];
 +            norder = nn*order;
 +
 +            i0   = idxptr[XX] - offx;
 +            j0   = idxptr[YY] - offy;
 +            k0   = idxptr[ZZ] - offz;
 +
 +            thx = spline->theta[XX] + norder;
 +            thy = spline->theta[YY] + norder;
 +            thz = spline->theta[ZZ] + norder;
 +
 +            switch (order)
 +            {
 +                case 4:
 +#ifdef PME_SSE
 +#ifdef PME_SSE_UNALIGNED
 +#define PME_SPREAD_SSE_ORDER4
 +#else
 +#define PME_SPREAD_SSE_ALIGNED
 +#define PME_ORDER 4
 +#endif
 +#include "pme_sse_single.h"
 +#else
 +                    DO_BSPLINE(4);
 +#endif
 +                    break;
 +                case 5:
 +#ifdef PME_SSE
 +#define PME_SPREAD_SSE_ALIGNED
 +#define PME_ORDER 5
 +#include "pme_sse_single.h"
 +#else
 +                    DO_BSPLINE(5);
 +#endif
 +                    break;
 +                default:
 +                    DO_BSPLINE(order);
 +                    break;
 +            }
 +        }
 +    }
 +}
 +
 +static void set_grid_alignment(int *pmegrid_nz, int pme_order)
 +{
 +#ifdef PME_SSE
 +    if (pme_order == 5
 +#ifndef PME_SSE_UNALIGNED
 +        || pme_order == 4
 +#endif
 +        )
 +    {
 +        /* Round nz up to a multiple of 4 to ensure alignment */
 +        *pmegrid_nz = ((*pmegrid_nz + 3) & ~3);
 +    }
 +#endif
 +}
 +
 +static void set_gridsize_alignment(int *gridsize, int pme_order)
 +{
 +#ifdef PME_SSE
 +#ifndef PME_SSE_UNALIGNED
 +    if (pme_order == 4)
 +    {
 +        /* Add extra elements to ensured aligned operations do not go
 +         * beyond the allocated grid size.
 +         * Note that for pme_order=5, the pme grid z-size alignment
 +         * ensures that we will not go beyond the grid size.
 +         */
 +        *gridsize += 4;
 +    }
 +#endif
 +#endif
 +}
 +
 +static void pmegrid_init(pmegrid_t *grid,
 +                         int cx, int cy, int cz,
 +                         int x0, int y0, int z0,
 +                         int x1, int y1, int z1,
 +                         gmx_bool set_alignment,
 +                         int pme_order,
 +                         real *ptr)
 +{
 +    int nz, gridsize;
 +
 +    grid->ci[XX]     = cx;
 +    grid->ci[YY]     = cy;
 +    grid->ci[ZZ]     = cz;
 +    grid->offset[XX] = x0;
 +    grid->offset[YY] = y0;
 +    grid->offset[ZZ] = z0;
 +    grid->n[XX]      = x1 - x0 + pme_order - 1;
 +    grid->n[YY]      = y1 - y0 + pme_order - 1;
 +    grid->n[ZZ]      = z1 - z0 + pme_order - 1;
 +    copy_ivec(grid->n, grid->s);
 +
 +    nz = grid->s[ZZ];
 +    set_grid_alignment(&nz, pme_order);
 +    if (set_alignment)
 +    {
 +        grid->s[ZZ] = nz;
 +    }
 +    else if (nz != grid->s[ZZ])
 +    {
 +        gmx_incons("pmegrid_init call with an unaligned z size");
 +    }
 +
 +    grid->order = pme_order;
 +    if (ptr == NULL)
 +    {
 +        gridsize = grid->s[XX]*grid->s[YY]*grid->s[ZZ];
 +        set_gridsize_alignment(&gridsize, pme_order);
 +        snew_aligned(grid->grid, gridsize, 16);
 +    }
 +    else
 +    {
 +        grid->grid = ptr;
 +    }
 +}
 +
 +static int div_round_up(int enumerator, int denominator)
 +{
 +    return (enumerator + denominator - 1)/denominator;
 +}
 +
 +static void make_subgrid_division(const ivec n, int ovl, int nthread,
 +                                  ivec nsub)
 +{
 +    int gsize_opt, gsize;
 +    int nsx, nsy, nsz;
 +    char *env;
 +
 +    gsize_opt = -1;
 +    for (nsx = 1; nsx <= nthread; nsx++)
 +    {
 +        if (nthread % nsx == 0)
 +        {
 +            for (nsy = 1; nsy <= nthread; nsy++)
 +            {
 +                if (nsx*nsy <= nthread && nthread % (nsx*nsy) == 0)
 +                {
 +                    nsz = nthread/(nsx*nsy);
 +
 +                    /* Determine the number of grid points per thread */
 +                    gsize =
 +                        (div_round_up(n[XX], nsx) + ovl)*
 +                        (div_round_up(n[YY], nsy) + ovl)*
 +                        (div_round_up(n[ZZ], nsz) + ovl);
 +
 +                    /* Minimize the number of grids points per thread
 +                     * and, secondarily, the number of cuts in minor dimensions.
 +                     */
 +                    if (gsize_opt == -1 ||
 +                        gsize < gsize_opt ||
 +                        (gsize == gsize_opt &&
 +                         (nsz < nsub[ZZ] || (nsz == nsub[ZZ] && nsy < nsub[YY]))))
 +                    {
 +                        nsub[XX]  = nsx;
 +                        nsub[YY]  = nsy;
 +                        nsub[ZZ]  = nsz;
 +                        gsize_opt = gsize;
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    env = getenv("GMX_PME_THREAD_DIVISION");
 +    if (env != NULL)
 +    {
 +        sscanf(env, "%d %d %d", &nsub[XX], &nsub[YY], &nsub[ZZ]);
 +    }
 +
 +    if (nsub[XX]*nsub[YY]*nsub[ZZ] != nthread)
 +    {
 +        gmx_fatal(FARGS, "PME grid thread division (%d x %d x %d) does not match the total number of threads (%d)", nsub[XX], nsub[YY], nsub[ZZ], nthread);
 +    }
 +}
 +
 +static void pmegrids_init(pmegrids_t *grids,
 +                          int nx, int ny, int nz, int nz_base,
 +                          int pme_order,
++                          gmx_bool bUseThreads,
 +                          int nthread,
 +                          int overlap_x,
 +                          int overlap_y)
 +{
 +    ivec n, n_base, g0, g1;
 +    int t, x, y, z, d, i, tfac;
 +    int max_comm_lines = -1;
 +
 +    n[XX] = nx - (pme_order - 1);
 +    n[YY] = ny - (pme_order - 1);
 +    n[ZZ] = nz - (pme_order - 1);
 +
 +    copy_ivec(n, n_base);
 +    n_base[ZZ] = nz_base;
 +
 +    pmegrid_init(&grids->grid, 0, 0, 0, 0, 0, 0, n[XX], n[YY], n[ZZ], FALSE, pme_order,
 +                 NULL);
 +
 +    grids->nthread = nthread;
 +
 +    make_subgrid_division(n_base, pme_order-1, grids->nthread, grids->nc);
 +
- static void
- gmx_pme_check_grid_restrictions(FILE *fplog, char dim, int nnodes, int *nk)
++    if (bUseThreads)
 +    {
 +        ivec nst;
 +        int gridsize;
 +
 +        for (d = 0; d < DIM; d++)
 +        {
 +            nst[d] = div_round_up(n[d], grids->nc[d]) + pme_order - 1;
 +        }
 +        set_grid_alignment(&nst[ZZ], pme_order);
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "pmegrid thread local division: %d x %d x %d\n",
 +                    grids->nc[XX], grids->nc[YY], grids->nc[ZZ]);
 +            fprintf(debug, "pmegrid %d %d %d max thread pmegrid %d %d %d\n",
 +                    nx, ny, nz,
 +                    nst[XX], nst[YY], nst[ZZ]);
 +        }
 +
 +        snew(grids->grid_th, grids->nthread);
 +        t        = 0;
 +        gridsize = nst[XX]*nst[YY]*nst[ZZ];
 +        set_gridsize_alignment(&gridsize, pme_order);
 +        snew_aligned(grids->grid_all,
 +                     grids->nthread*gridsize+(grids->nthread+1)*GMX_CACHE_SEP,
 +                     16);
 +
 +        for (x = 0; x < grids->nc[XX]; x++)
 +        {
 +            for (y = 0; y < grids->nc[YY]; y++)
 +            {
 +                for (z = 0; z < grids->nc[ZZ]; z++)
 +                {
 +                    pmegrid_init(&grids->grid_th[t],
 +                                 x, y, z,
 +                                 (n[XX]*(x  ))/grids->nc[XX],
 +                                 (n[YY]*(y  ))/grids->nc[YY],
 +                                 (n[ZZ]*(z  ))/grids->nc[ZZ],
 +                                 (n[XX]*(x+1))/grids->nc[XX],
 +                                 (n[YY]*(y+1))/grids->nc[YY],
 +                                 (n[ZZ]*(z+1))/grids->nc[ZZ],
 +                                 TRUE,
 +                                 pme_order,
 +                                 grids->grid_all+GMX_CACHE_SEP+t*(gridsize+GMX_CACHE_SEP));
 +                    t++;
 +                }
 +            }
 +        }
 +    }
++    else
++    {
++        grids->grid_th = NULL;
++    }
 +
 +    snew(grids->g2t, DIM);
 +    tfac = 1;
 +    for (d = DIM-1; d >= 0; d--)
 +    {
 +        snew(grids->g2t[d], n[d]);
 +        t = 0;
 +        for (i = 0; i < n[d]; i++)
 +        {
 +            /* The second check should match the parameters
 +             * of the pmegrid_init call above.
 +             */
 +            while (t + 1 < grids->nc[d] && i >= (n[d]*(t+1))/grids->nc[d])
 +            {
 +                t++;
 +            }
 +            grids->g2t[d][i] = t*tfac;
 +        }
 +
 +        tfac *= grids->nc[d];
 +
 +        switch (d)
 +        {
 +            case XX: max_comm_lines = overlap_x;     break;
 +            case YY: max_comm_lines = overlap_y;     break;
 +            case ZZ: max_comm_lines = pme_order - 1; break;
 +        }
 +        grids->nthread_comm[d] = 0;
 +        while ((n[d]*grids->nthread_comm[d])/grids->nc[d] < max_comm_lines &&
 +               grids->nthread_comm[d] < grids->nc[d])
 +        {
 +            grids->nthread_comm[d]++;
 +        }
 +        if (debug != NULL)
 +        {
 +            fprintf(debug, "pmegrid thread grid communication range in %c: %d\n",
 +                    'x'+d, grids->nthread_comm[d]);
 +        }
 +        /* It should be possible to make grids->nthread_comm[d]==grids->nc[d]
 +         * work, but this is not a problematic restriction.
 +         */
 +        if (grids->nc[d] > 1 && grids->nthread_comm[d] > grids->nc[d])
 +        {
 +            gmx_fatal(FARGS, "Too many threads for PME (%d) compared to the number of grid lines, reduce the number of threads doing PME", grids->nthread);
 +        }
 +    }
 +}
 +
 +
 +static void pmegrids_destroy(pmegrids_t *grids)
 +{
 +    int t;
 +
 +    if (grids->grid.grid != NULL)
 +    {
 +        sfree(grids->grid.grid);
 +
 +        if (grids->nthread > 0)
 +        {
 +            for (t = 0; t < grids->nthread; t++)
 +            {
 +                sfree(grids->grid_th[t].grid);
 +            }
 +            sfree(grids->grid_th);
 +        }
 +    }
 +}
 +
 +
 +static void realloc_work(pme_work_t *work, int nkx)
 +{
 +    if (nkx > work->nalloc)
 +    {
 +        work->nalloc = nkx;
 +        srenew(work->mhx, work->nalloc);
 +        srenew(work->mhy, work->nalloc);
 +        srenew(work->mhz, work->nalloc);
 +        srenew(work->m2, work->nalloc);
 +        /* Allocate an aligned pointer for SSE operations, including 3 extra
 +         * elements at the end since SSE operates on 4 elements at a time.
 +         */
 +        sfree_aligned(work->denom);
 +        sfree_aligned(work->tmp1);
 +        sfree_aligned(work->eterm);
 +        snew_aligned(work->denom, work->nalloc+3, 16);
 +        snew_aligned(work->tmp1, work->nalloc+3, 16);
 +        snew_aligned(work->eterm, work->nalloc+3, 16);
 +        srenew(work->m2inv, work->nalloc);
 +    }
 +}
 +
 +
 +static void free_work(pme_work_t *work)
 +{
 +    sfree(work->mhx);
 +    sfree(work->mhy);
 +    sfree(work->mhz);
 +    sfree(work->m2);
 +    sfree_aligned(work->denom);
 +    sfree_aligned(work->tmp1);
 +    sfree_aligned(work->eterm);
 +    sfree(work->m2inv);
 +}
 +
 +
 +#ifdef PME_SSE
 +/* Calculate exponentials through SSE in float precision */
 +inline static void calc_exponentials(int start, int end, real f, real *d_aligned, real *r_aligned, real *e_aligned)
 +{
 +    {
 +        const __m128 two = _mm_set_ps(2.0f, 2.0f, 2.0f, 2.0f);
 +        __m128 f_sse;
 +        __m128 lu;
 +        __m128 tmp_d1, d_inv, tmp_r, tmp_e;
 +        int kx;
 +        f_sse = _mm_load1_ps(&f);
 +        for (kx = 0; kx < end; kx += 4)
 +        {
 +            tmp_d1   = _mm_load_ps(d_aligned+kx);
 +            lu       = _mm_rcp_ps(tmp_d1);
 +            d_inv    = _mm_mul_ps(lu, _mm_sub_ps(two, _mm_mul_ps(lu, tmp_d1)));
 +            tmp_r    = _mm_load_ps(r_aligned+kx);
 +            tmp_r    = gmx_mm_exp_ps(tmp_r);
 +            tmp_e    = _mm_mul_ps(f_sse, d_inv);
 +            tmp_e    = _mm_mul_ps(tmp_e, tmp_r);
 +            _mm_store_ps(e_aligned+kx, tmp_e);
 +        }
 +    }
 +}
 +#else
 +inline static void calc_exponentials(int start, int end, real f, real *d, real *r, real *e)
 +{
 +    int kx;
 +    for (kx = start; kx < end; kx++)
 +    {
 +        d[kx] = 1.0/d[kx];
 +    }
 +    for (kx = start; kx < end; kx++)
 +    {
 +        r[kx] = exp(r[kx]);
 +    }
 +    for (kx = start; kx < end; kx++)
 +    {
 +        e[kx] = f*r[kx]*d[kx];
 +    }
 +}
 +#endif
 +
 +
 +static int solve_pme_yzx(gmx_pme_t pme, t_complex *grid,
 +                         real ewaldcoeff, real vol,
 +                         gmx_bool bEnerVir,
 +                         int nthread, int thread)
 +{
 +    /* do recip sum over local cells in grid */
 +    /* y major, z middle, x minor or continuous */
 +    t_complex *p0;
 +    int     kx, ky, kz, maxkx, maxky, maxkz;
 +    int     nx, ny, nz, iyz0, iyz1, iyz, iy, iz, kxstart, kxend;
 +    real    mx, my, mz;
 +    real    factor = M_PI*M_PI/(ewaldcoeff*ewaldcoeff);
 +    real    ets2, struct2, vfactor, ets2vf;
 +    real    d1, d2, energy = 0;
 +    real    by, bz;
 +    real    virxx = 0, virxy = 0, virxz = 0, viryy = 0, viryz = 0, virzz = 0;
 +    real    rxx, ryx, ryy, rzx, rzy, rzz;
 +    pme_work_t *work;
 +    real    *mhx, *mhy, *mhz, *m2, *denom, *tmp1, *eterm, *m2inv;
 +    real    mhxk, mhyk, mhzk, m2k;
 +    real    corner_fac;
 +    ivec    complex_order;
 +    ivec    local_ndata, local_offset, local_size;
 +    real    elfac;
 +
 +    elfac = ONE_4PI_EPS0/pme->epsilon_r;
 +
 +    nx = pme->nkx;
 +    ny = pme->nky;
 +    nz = pme->nkz;
 +
 +    /* Dimensions should be identical for A/B grid, so we just use A here */
 +    gmx_parallel_3dfft_complex_limits(pme->pfft_setupA,
 +                                      complex_order,
 +                                      local_ndata,
 +                                      local_offset,
 +                                      local_size);
 +
 +    rxx = pme->recipbox[XX][XX];
 +    ryx = pme->recipbox[YY][XX];
 +    ryy = pme->recipbox[YY][YY];
 +    rzx = pme->recipbox[ZZ][XX];
 +    rzy = pme->recipbox[ZZ][YY];
 +    rzz = pme->recipbox[ZZ][ZZ];
 +
 +    maxkx = (nx+1)/2;
 +    maxky = (ny+1)/2;
 +    maxkz = nz/2+1;
 +
 +    work  = &pme->work[thread];
 +    mhx   = work->mhx;
 +    mhy   = work->mhy;
 +    mhz   = work->mhz;
 +    m2    = work->m2;
 +    denom = work->denom;
 +    tmp1  = work->tmp1;
 +    eterm = work->eterm;
 +    m2inv = work->m2inv;
 +
 +    iyz0 = local_ndata[YY]*local_ndata[ZZ]* thread   /nthread;
 +    iyz1 = local_ndata[YY]*local_ndata[ZZ]*(thread+1)/nthread;
 +
 +    for (iyz = iyz0; iyz < iyz1; iyz++)
 +    {
 +        iy = iyz/local_ndata[ZZ];
 +        iz = iyz - iy*local_ndata[ZZ];
 +
 +        ky = iy + local_offset[YY];
 +
 +        if (ky < maxky)
 +        {
 +            my = ky;
 +        }
 +        else
 +        {
 +            my = (ky - ny);
 +        }
 +
 +        by = M_PI*vol*pme->bsp_mod[YY][ky];
 +
 +        kz = iz + local_offset[ZZ];
 +
 +        mz = kz;
 +
 +        bz = pme->bsp_mod[ZZ][kz];
 +
 +        /* 0.5 correction for corner points */
 +        corner_fac = 1;
 +        if (kz == 0 || kz == (nz+1)/2)
 +        {
 +            corner_fac = 0.5;
 +        }
 +
 +        p0 = grid + iy*local_size[ZZ]*local_size[XX] + iz*local_size[XX];
 +
 +        /* We should skip the k-space point (0,0,0) */
 +        if (local_offset[XX] > 0 || ky > 0 || kz > 0)
 +        {
 +            kxstart = local_offset[XX];
 +        }
 +        else
 +        {
 +            kxstart = local_offset[XX] + 1;
 +            p0++;
 +        }
 +        kxend = local_offset[XX] + local_ndata[XX];
 +
 +        if (bEnerVir)
 +        {
 +            /* More expensive inner loop, especially because of the storage
 +             * of the mh elements in array's.
 +             * Because x is the minor grid index, all mh elements
 +             * depend on kx for triclinic unit cells.
 +             */
 +
 +            /* Two explicit loops to avoid a conditional inside the loop */
 +            for (kx = kxstart; kx < maxkx; kx++)
 +            {
 +                mx = kx;
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                mhx[kx]   = mhxk;
 +                mhy[kx]   = mhyk;
 +                mhz[kx]   = mhzk;
 +                m2[kx]    = m2k;
 +                denom[kx] = m2k*bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +            }
 +
 +            for (kx = maxkx; kx < kxend; kx++)
 +            {
 +                mx = (kx - nx);
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                mhx[kx]   = mhxk;
 +                mhy[kx]   = mhyk;
 +                mhz[kx]   = mhzk;
 +                m2[kx]    = m2k;
 +                denom[kx] = m2k*bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +            }
 +
 +            for (kx = kxstart; kx < kxend; kx++)
 +            {
 +                m2inv[kx] = 1.0/m2[kx];
 +            }
 +
 +            calc_exponentials(kxstart, kxend, elfac, denom, tmp1, eterm);
 +
 +            for (kx = kxstart; kx < kxend; kx++, p0++)
 +            {
 +                d1      = p0->re;
 +                d2      = p0->im;
 +
 +                p0->re  = d1*eterm[kx];
 +                p0->im  = d2*eterm[kx];
 +
 +                struct2 = 2.0*(d1*d1+d2*d2);
 +
 +                tmp1[kx] = eterm[kx]*struct2;
 +            }
 +
 +            for (kx = kxstart; kx < kxend; kx++)
 +            {
 +                ets2     = corner_fac*tmp1[kx];
 +                vfactor  = (factor*m2[kx] + 1.0)*2.0*m2inv[kx];
 +                energy  += ets2;
 +
 +                ets2vf   = ets2*vfactor;
 +                virxx   += ets2vf*mhx[kx]*mhx[kx] - ets2;
 +                virxy   += ets2vf*mhx[kx]*mhy[kx];
 +                virxz   += ets2vf*mhx[kx]*mhz[kx];
 +                viryy   += ets2vf*mhy[kx]*mhy[kx] - ets2;
 +                viryz   += ets2vf*mhy[kx]*mhz[kx];
 +                virzz   += ets2vf*mhz[kx]*mhz[kx] - ets2;
 +            }
 +        }
 +        else
 +        {
 +            /* We don't need to calculate the energy and the virial.
 +             * In this case the triclinic overhead is small.
 +             */
 +
 +            /* Two explicit loops to avoid a conditional inside the loop */
 +
 +            for (kx = kxstart; kx < maxkx; kx++)
 +            {
 +                mx = kx;
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                denom[kx] = m2k*bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +            }
 +
 +            for (kx = maxkx; kx < kxend; kx++)
 +            {
 +                mx = (kx - nx);
 +
 +                mhxk      = mx * rxx;
 +                mhyk      = mx * ryx + my * ryy;
 +                mhzk      = mx * rzx + my * rzy + mz * rzz;
 +                m2k       = mhxk*mhxk + mhyk*mhyk + mhzk*mhzk;
 +                denom[kx] = m2k*bz*by*pme->bsp_mod[XX][kx];
 +                tmp1[kx]  = -factor*m2k;
 +            }
 +
 +            calc_exponentials(kxstart, kxend, elfac, denom, tmp1, eterm);
 +
 +            for (kx = kxstart; kx < kxend; kx++, p0++)
 +            {
 +                d1      = p0->re;
 +                d2      = p0->im;
 +
 +                p0->re  = d1*eterm[kx];
 +                p0->im  = d2*eterm[kx];
 +            }
 +        }
 +    }
 +
 +    if (bEnerVir)
 +    {
 +        /* Update virial with local values.
 +         * The virial is symmetric by definition.
 +         * this virial seems ok for isotropic scaling, but I'm
 +         * experiencing problems on semiisotropic membranes.
 +         * IS THAT COMMENT STILL VALID??? (DvdS, 2001/02/07).
 +         */
 +        work->vir[XX][XX] = 0.25*virxx;
 +        work->vir[YY][YY] = 0.25*viryy;
 +        work->vir[ZZ][ZZ] = 0.25*virzz;
 +        work->vir[XX][YY] = work->vir[YY][XX] = 0.25*virxy;
 +        work->vir[XX][ZZ] = work->vir[ZZ][XX] = 0.25*virxz;
 +        work->vir[YY][ZZ] = work->vir[ZZ][YY] = 0.25*viryz;
 +
 +        /* This energy should be corrected for a charged system */
 +        work->energy = 0.5*energy;
 +    }
 +
 +    /* Return the loop count */
 +    return local_ndata[YY]*local_ndata[XX];
 +}
 +
 +static void get_pme_ener_vir(const gmx_pme_t pme, int nthread,
 +                             real *mesh_energy, matrix vir)
 +{
 +    /* This function sums output over threads
 +     * and should therefore only be called after thread synchronization.
 +     */
 +    int thread;
 +
 +    *mesh_energy = pme->work[0].energy;
 +    copy_mat(pme->work[0].vir, vir);
 +
 +    for (thread = 1; thread < nthread; thread++)
 +    {
 +        *mesh_energy += pme->work[thread].energy;
 +        m_add(vir, pme->work[thread].vir, vir);
 +    }
 +}
 +
 +#define DO_FSPLINE(order)                      \
 +    for (ithx = 0; (ithx < order); ithx++)              \
 +    {                                              \
 +        index_x = (i0+ithx)*pny*pnz;               \
 +        tx      = thx[ithx];                       \
 +        dx      = dthx[ithx];                      \
 +                                               \
 +        for (ithy = 0; (ithy < order); ithy++)          \
 +        {                                          \
 +            index_xy = index_x+(j0+ithy)*pnz;      \
 +            ty       = thy[ithy];                  \
 +            dy       = dthy[ithy];                 \
 +            fxy1     = fz1 = 0;                    \
 +                                               \
 +            for (ithz = 0; (ithz < order); ithz++)      \
 +            {                                      \
 +                gval  = grid[index_xy+(k0+ithz)];  \
 +                fxy1 += thz[ithz]*gval;            \
 +                fz1  += dthz[ithz]*gval;           \
 +            }                                      \
 +            fx += dx*ty*fxy1;                      \
 +            fy += tx*dy*fxy1;                      \
 +            fz += tx*ty*fz1;                       \
 +        }                                          \
 +    }
 +
 +
 +static void gather_f_bsplines(gmx_pme_t pme, real *grid,
 +                              gmx_bool bClearF, pme_atomcomm_t *atc,
 +                              splinedata_t *spline,
 +                              real scale)
 +{
 +    /* sum forces for local particles */
 +    int     nn, n, ithx, ithy, ithz, i0, j0, k0;
 +    int     index_x, index_xy;
 +    int     nx, ny, nz, pnx, pny, pnz;
 +    int *   idxptr;
 +    real    tx, ty, dx, dy, qn;
 +    real    fx, fy, fz, gval;
 +    real    fxy1, fz1;
 +    real    *thx, *thy, *thz, *dthx, *dthy, *dthz;
 +    int     norder;
 +    real    rxx, ryx, ryy, rzx, rzy, rzz;
 +    int     order;
 +
 +    pme_spline_work_t *work;
 +
 +    work = pme->spline_work;
 +
 +    order = pme->pme_order;
 +    thx   = spline->theta[XX];
 +    thy   = spline->theta[YY];
 +    thz   = spline->theta[ZZ];
 +    dthx  = spline->dtheta[XX];
 +    dthy  = spline->dtheta[YY];
 +    dthz  = spline->dtheta[ZZ];
 +    nx    = pme->nkx;
 +    ny    = pme->nky;
 +    nz    = pme->nkz;
 +    pnx   = pme->pmegrid_nx;
 +    pny   = pme->pmegrid_ny;
 +    pnz   = pme->pmegrid_nz;
 +
 +    rxx   = pme->recipbox[XX][XX];
 +    ryx   = pme->recipbox[YY][XX];
 +    ryy   = pme->recipbox[YY][YY];
 +    rzx   = pme->recipbox[ZZ][XX];
 +    rzy   = pme->recipbox[ZZ][YY];
 +    rzz   = pme->recipbox[ZZ][ZZ];
 +
 +    for (nn = 0; nn < spline->n; nn++)
 +    {
 +        n  = spline->ind[nn];
 +        qn = scale*atc->q[n];
 +
 +        if (bClearF)
 +        {
 +            atc->f[n][XX] = 0;
 +            atc->f[n][YY] = 0;
 +            atc->f[n][ZZ] = 0;
 +        }
 +        if (qn != 0)
 +        {
 +            fx     = 0;
 +            fy     = 0;
 +            fz     = 0;
 +            idxptr = atc->idx[n];
 +            norder = nn*order;
 +
 +            i0   = idxptr[XX];
 +            j0   = idxptr[YY];
 +            k0   = idxptr[ZZ];
 +
 +            /* Pointer arithmetic alert, next six statements */
 +            thx  = spline->theta[XX] + norder;
 +            thy  = spline->theta[YY] + norder;
 +            thz  = spline->theta[ZZ] + norder;
 +            dthx = spline->dtheta[XX] + norder;
 +            dthy = spline->dtheta[YY] + norder;
 +            dthz = spline->dtheta[ZZ] + norder;
 +
 +            switch (order)
 +            {
 +                case 4:
 +#ifdef PME_SSE
 +#ifdef PME_SSE_UNALIGNED
 +#define PME_GATHER_F_SSE_ORDER4
 +#else
 +#define PME_GATHER_F_SSE_ALIGNED
 +#define PME_ORDER 4
 +#endif
 +#include "pme_sse_single.h"
 +#else
 +                    DO_FSPLINE(4);
 +#endif
 +                    break;
 +                case 5:
 +#ifdef PME_SSE
 +#define PME_GATHER_F_SSE_ALIGNED
 +#define PME_ORDER 5
 +#include "pme_sse_single.h"
 +#else
 +                    DO_FSPLINE(5);
 +#endif
 +                    break;
 +                default:
 +                    DO_FSPLINE(order);
 +                    break;
 +            }
 +
 +            atc->f[n][XX] += -qn*( fx*nx*rxx );
 +            atc->f[n][YY] += -qn*( fx*nx*ryx + fy*ny*ryy );
 +            atc->f[n][ZZ] += -qn*( fx*nx*rzx + fy*ny*rzy + fz*nz*rzz );
 +        }
 +    }
 +    /* Since the energy and not forces are interpolated
 +     * the net force might not be exactly zero.
 +     * This can be solved by also interpolating F, but
 +     * that comes at a cost.
 +     * A better hack is to remove the net force every
 +     * step, but that must be done at a higher level
 +     * since this routine doesn't see all atoms if running
 +     * in parallel. Don't know how important it is?  EL 990726
 +     */
 +}
 +
 +
 +static real gather_energy_bsplines(gmx_pme_t pme, real *grid,
 +                                   pme_atomcomm_t *atc)
 +{
 +    splinedata_t *spline;
 +    int     n, ithx, ithy, ithz, i0, j0, k0;
 +    int     index_x, index_xy;
 +    int *   idxptr;
 +    real    energy, pot, tx, ty, qn, gval;
 +    real    *thx, *thy, *thz;
 +    int     norder;
 +    int     order;
 +
 +    spline = &atc->spline[0];
 +
 +    order = pme->pme_order;
 +
 +    energy = 0;
 +    for (n = 0; (n < atc->n); n++)
 +    {
 +        qn      = atc->q[n];
 +
 +        if (qn != 0)
 +        {
 +            idxptr = atc->idx[n];
 +            norder = n*order;
 +
 +            i0   = idxptr[XX];
 +            j0   = idxptr[YY];
 +            k0   = idxptr[ZZ];
 +
 +            /* Pointer arithmetic alert, next three statements */
 +            thx  = spline->theta[XX] + norder;
 +            thy  = spline->theta[YY] + norder;
 +            thz  = spline->theta[ZZ] + norder;
 +
 +            pot = 0;
 +            for (ithx = 0; (ithx < order); ithx++)
 +            {
 +                index_x = (i0+ithx)*pme->pmegrid_ny*pme->pmegrid_nz;
 +                tx      = thx[ithx];
 +
 +                for (ithy = 0; (ithy < order); ithy++)
 +                {
 +                    index_xy = index_x+(j0+ithy)*pme->pmegrid_nz;
 +                    ty       = thy[ithy];
 +
 +                    for (ithz = 0; (ithz < order); ithz++)
 +                    {
 +                        gval  = grid[index_xy+(k0+ithz)];
 +                        pot  += tx*ty*thz[ithz]*gval;
 +                    }
 +
 +                }
 +            }
 +
 +            energy += pot*qn;
 +        }
 +    }
 +
 +    return energy;
 +}
 +
 +/* Macro to force loop unrolling by fixing order.
 + * This gives a significant performance gain.
 + */
 +#define CALC_SPLINE(order)                     \
 +    {                                              \
 +        int j, k, l;                                 \
 +        real dr, div;                               \
 +        real data[PME_ORDER_MAX];                  \
 +        real ddata[PME_ORDER_MAX];                 \
 +                                               \
 +        for (j = 0; (j < DIM); j++)                     \
 +        {                                          \
 +            dr  = xptr[j];                         \
 +                                               \
 +            /* dr is relative offset from lower cell limit */ \
 +            data[order-1] = 0;                     \
 +            data[1]       = dr;                          \
 +            data[0]       = 1 - dr;                      \
 +                                               \
 +            for (k = 3; (k < order); k++)               \
 +            {                                      \
 +                div       = 1.0/(k - 1.0);               \
 +                data[k-1] = div*dr*data[k-2];      \
 +                for (l = 1; (l < (k-1)); l++)           \
 +                {                                  \
 +                    data[k-l-1] = div*((dr+l)*data[k-l-2]+(k-l-dr)* \
 +                                       data[k-l-1]);                \
 +                }                                  \
 +                data[0] = div*(1-dr)*data[0];      \
 +            }                                      \
 +            /* differentiate */                    \
 +            ddata[0] = -data[0];                   \
 +            for (k = 1; (k < order); k++)               \
 +            {                                      \
 +                ddata[k] = data[k-1] - data[k];    \
 +            }                                      \
 +                                               \
 +            div           = 1.0/(order - 1);                 \
 +            data[order-1] = div*dr*data[order-2];  \
 +            for (l = 1; (l < (order-1)); l++)           \
 +            {                                      \
 +                data[order-l-1] = div*((dr+l)*data[order-l-2]+    \
 +                                       (order-l-dr)*data[order-l-1]); \
 +            }                                      \
 +            data[0] = div*(1 - dr)*data[0];        \
 +                                               \
 +            for (k = 0; k < order; k++)                 \
 +            {                                      \
 +                theta[j][i*order+k]  = data[k];    \
 +                dtheta[j][i*order+k] = ddata[k];   \
 +            }                                      \
 +        }                                          \
 +    }
 +
 +void make_bsplines(splinevec theta, splinevec dtheta, int order,
 +                   rvec fractx[], int nr, int ind[], real charge[],
 +                   gmx_bool bFreeEnergy)
 +{
 +    /* construct splines for local atoms */
 +    int  i, ii;
 +    real *xptr;
 +
 +    for (i = 0; i < nr; i++)
 +    {
 +        /* With free energy we do not use the charge check.
 +         * In most cases this will be more efficient than calling make_bsplines
 +         * twice, since usually more than half the particles have charges.
 +         */
 +        ii = ind[i];
 +        if (bFreeEnergy || charge[ii] != 0.0)
 +        {
 +            xptr = fractx[ii];
 +            switch (order)
 +            {
 +                case 4:  CALC_SPLINE(4);     break;
 +                case 5:  CALC_SPLINE(5);     break;
 +                default: CALC_SPLINE(order); break;
 +            }
 +        }
 +    }
 +}
 +
 +
 +void make_dft_mod(real *mod, real *data, int ndata)
 +{
 +    int i, j;
 +    real sc, ss, arg;
 +
 +    for (i = 0; i < ndata; i++)
 +    {
 +        sc = ss = 0;
 +        for (j = 0; j < ndata; j++)
 +        {
 +            arg = (2.0*M_PI*i*j)/ndata;
 +            sc += data[j]*cos(arg);
 +            ss += data[j]*sin(arg);
 +        }
 +        mod[i] = sc*sc+ss*ss;
 +    }
 +    for (i = 0; i < ndata; i++)
 +    {
 +        if (mod[i] < 1e-7)
 +        {
 +            mod[i] = (mod[i-1]+mod[i+1])*0.5;
 +        }
 +    }
 +}
 +
 +
 +static void make_bspline_moduli(splinevec bsp_mod,
 +                                int nx, int ny, int nz, int order)
 +{
 +    int nmax = max(nx, max(ny, nz));
 +    real *data, *ddata, *bsp_data;
 +    int i, k, l;
 +    real div;
 +
 +    snew(data, order);
 +    snew(ddata, order);
 +    snew(bsp_data, nmax);
 +
 +    data[order-1] = 0;
 +    data[1]       = 0;
 +    data[0]       = 1;
 +
 +    for (k = 3; k < order; k++)
 +    {
 +        div       = 1.0/(k-1.0);
 +        data[k-1] = 0;
 +        for (l = 1; l < (k-1); l++)
 +        {
 +            data[k-l-1] = div*(l*data[k-l-2]+(k-l)*data[k-l-1]);
 +        }
 +        data[0] = div*data[0];
 +    }
 +    /* differentiate */
 +    ddata[0] = -data[0];
 +    for (k = 1; k < order; k++)
 +    {
 +        ddata[k] = data[k-1]-data[k];
 +    }
 +    div           = 1.0/(order-1);
 +    data[order-1] = 0;
 +    for (l = 1; l < (order-1); l++)
 +    {
 +        data[order-l-1] = div*(l*data[order-l-2]+(order-l)*data[order-l-1]);
 +    }
 +    data[0] = div*data[0];
 +
 +    for (i = 0; i < nmax; i++)
 +    {
 +        bsp_data[i] = 0;
 +    }
 +    for (i = 1; i <= order; i++)
 +    {
 +        bsp_data[i] = data[i-1];
 +    }
 +
 +    make_dft_mod(bsp_mod[XX], bsp_data, nx);
 +    make_dft_mod(bsp_mod[YY], bsp_data, ny);
 +    make_dft_mod(bsp_mod[ZZ], bsp_data, nz);
 +
 +    sfree(data);
 +    sfree(ddata);
 +    sfree(bsp_data);
 +}
 +
 +
 +/* Return the P3M optimal influence function */
 +static double do_p3m_influence(double z, int order)
 +{
 +    double z2, z4;
 +
 +    z2 = z*z;
 +    z4 = z2*z2;
 +
 +    /* The formula and most constants can be found in:
 +     * Ballenegger et al., JCTC 8, 936 (2012)
 +     */
 +    switch (order)
 +    {
 +        case 2:
 +            return 1.0 - 2.0*z2/3.0;
 +            break;
 +        case 3:
 +            return 1.0 - z2 + 2.0*z4/15.0;
 +            break;
 +        case 4:
 +            return 1.0 - 4.0*z2/3.0 + 2.0*z4/5.0 + 4.0*z2*z4/315.0;
 +            break;
 +        case 5:
 +            return 1.0 - 5.0*z2/3.0 + 7.0*z4/9.0 - 17.0*z2*z4/189.0 + 2.0*z4*z4/2835.0;
 +            break;
 +        case 6:
 +            return 1.0 - 2.0*z2 + 19.0*z4/15.0 - 256.0*z2*z4/945.0 + 62.0*z4*z4/4725.0 + 4.0*z2*z4*z4/155925.0;
 +            break;
 +        case 7:
 +            return 1.0 - 7.0*z2/3.0 + 28.0*z4/15.0 - 16.0*z2*z4/27.0 + 26.0*z4*z4/405.0 - 2.0*z2*z4*z4/1485.0 + 4.0*z4*z4*z4/6081075.0;
 +        case 8:
 +            return 1.0 - 8.0*z2/3.0 + 116.0*z4/45.0 - 344.0*z2*z4/315.0 + 914.0*z4*z4/4725.0 - 248.0*z4*z4*z2/22275.0 + 21844.0*z4*z4*z4/212837625.0 - 8.0*z4*z4*z4*z2/638512875.0;
 +            break;
 +    }
 +
 +    return 0.0;
 +}
 +
 +/* Calculate the P3M B-spline moduli for one dimension */
 +static void make_p3m_bspline_moduli_dim(real *bsp_mod, int n, int order)
 +{
 +    double zarg, zai, sinzai, infl;
 +    int    maxk, i;
 +
 +    if (order > 8)
 +    {
 +        gmx_fatal(FARGS, "The current P3M code only supports orders up to 8");
 +    }
 +
 +    zarg = M_PI/n;
 +
 +    maxk = (n + 1)/2;
 +
 +    for (i = -maxk; i < 0; i++)
 +    {
 +        zai          = zarg*i;
 +        sinzai       = sin(zai);
 +        infl         = do_p3m_influence(sinzai, order);
 +        bsp_mod[n+i] = infl*infl*pow(sinzai/zai, -2.0*order);
 +    }
 +    bsp_mod[0] = 1.0;
 +    for (i = 1; i < maxk; i++)
 +    {
 +        zai        = zarg*i;
 +        sinzai     = sin(zai);
 +        infl       = do_p3m_influence(sinzai, order);
 +        bsp_mod[i] = infl*infl*pow(sinzai/zai, -2.0*order);
 +    }
 +}
 +
 +/* Calculate the P3M B-spline moduli */
 +static void make_p3m_bspline_moduli(splinevec bsp_mod,
 +                                    int nx, int ny, int nz, int order)
 +{
 +    make_p3m_bspline_moduli_dim(bsp_mod[XX], nx, order);
 +    make_p3m_bspline_moduli_dim(bsp_mod[YY], ny, order);
 +    make_p3m_bspline_moduli_dim(bsp_mod[ZZ], nz, order);
 +}
 +
 +
 +static void setup_coordinate_communication(pme_atomcomm_t *atc)
 +{
 +    int nslab, n, i;
 +    int fw, bw;
 +
 +    nslab = atc->nslab;
 +
 +    n = 0;
 +    for (i = 1; i <= nslab/2; i++)
 +    {
 +        fw = (atc->nodeid + i) % nslab;
 +        bw = (atc->nodeid - i + nslab) % nslab;
 +        if (n < nslab - 1)
 +        {
 +            atc->node_dest[n] = fw;
 +            atc->node_src[n]  = bw;
 +            n++;
 +        }
 +        if (n < nslab - 1)
 +        {
 +            atc->node_dest[n] = bw;
 +            atc->node_src[n]  = fw;
 +            n++;
 +        }
 +    }
 +}
 +
 +int gmx_pme_destroy(FILE *log, gmx_pme_t *pmedata)
 +{
 +    int thread;
 +
 +    if (NULL != log)
 +    {
 +        fprintf(log, "Destroying PME data structures.\n");
 +    }
 +
 +    sfree((*pmedata)->nnx);
 +    sfree((*pmedata)->nny);
 +    sfree((*pmedata)->nnz);
 +
 +    pmegrids_destroy(&(*pmedata)->pmegridA);
 +
 +    sfree((*pmedata)->fftgridA);
 +    sfree((*pmedata)->cfftgridA);
 +    gmx_parallel_3dfft_destroy((*pmedata)->pfft_setupA);
 +
 +    if ((*pmedata)->pmegridB.grid.grid != NULL)
 +    {
 +        pmegrids_destroy(&(*pmedata)->pmegridB);
 +        sfree((*pmedata)->fftgridB);
 +        sfree((*pmedata)->cfftgridB);
 +        gmx_parallel_3dfft_destroy((*pmedata)->pfft_setupB);
 +    }
 +    for (thread = 0; thread < (*pmedata)->nthread; thread++)
 +    {
 +        free_work(&(*pmedata)->work[thread]);
 +    }
 +    sfree((*pmedata)->work);
 +
 +    sfree(*pmedata);
 +    *pmedata = NULL;
 +
 +    return 0;
 +}
 +
 +static int mult_up(int n, int f)
 +{
 +    return ((n + f - 1)/f)*f;
 +}
 +
 +
 +static double pme_load_imbalance(gmx_pme_t pme)
 +{
 +    int    nma, nmi;
 +    double n1, n2, n3;
 +
 +    nma = pme->nnodes_major;
 +    nmi = pme->nnodes_minor;
 +
 +    n1 = mult_up(pme->nkx, nma)*mult_up(pme->nky, nmi)*pme->nkz;
 +    n2 = mult_up(pme->nkx, nma)*mult_up(pme->nkz, nmi)*pme->nky;
 +    n3 = mult_up(pme->nky, nma)*mult_up(pme->nkz, nmi)*pme->nkx;
 +
 +    /* pme_solve is roughly double the cost of an fft */
 +
 +    return (n1 + n2 + 3*n3)/(double)(6*pme->nkx*pme->nky*pme->nkz);
 +}
 +
 +static void init_atomcomm(gmx_pme_t pme, pme_atomcomm_t *atc, t_commrec *cr,
 +                          int dimind, gmx_bool bSpread)
 +{
 +    int nk, k, s, thread;
 +
 +    atc->dimind    = dimind;
 +    atc->nslab     = 1;
 +    atc->nodeid    = 0;
 +    atc->pd_nalloc = 0;
 +#ifdef GMX_MPI
 +    if (pme->nnodes > 1)
 +    {
 +        atc->mpi_comm = pme->mpi_comm_d[dimind];
 +        MPI_Comm_size(atc->mpi_comm, &atc->nslab);
 +        MPI_Comm_rank(atc->mpi_comm, &atc->nodeid);
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "For PME atom communication in dimind %d: nslab %d rank %d\n", atc->dimind, atc->nslab, atc->nodeid);
 +    }
 +#endif
 +
 +    atc->bSpread   = bSpread;
 +    atc->pme_order = pme->pme_order;
 +
 +    if (atc->nslab > 1)
 +    {
 +        /* These three allocations are not required for particle decomp. */
 +        snew(atc->node_dest, atc->nslab);
 +        snew(atc->node_src, atc->nslab);
 +        setup_coordinate_communication(atc);
 +
 +        snew(atc->count_thread, pme->nthread);
 +        for (thread = 0; thread < pme->nthread; thread++)
 +        {
 +            snew(atc->count_thread[thread], atc->nslab);
 +        }
 +        atc->count = atc->count_thread[0];
 +        snew(atc->rcount, atc->nslab);
 +        snew(atc->buf_index, atc->nslab);
 +    }
 +
 +    atc->nthread = pme->nthread;
 +    if (atc->nthread > 1)
 +    {
 +        snew(atc->thread_plist, atc->nthread);
 +    }
 +    snew(atc->spline, atc->nthread);
 +    for (thread = 0; thread < atc->nthread; thread++)
 +    {
 +        if (atc->nthread > 1)
 +        {
 +            snew(atc->thread_plist[thread].n, atc->nthread+2*GMX_CACHE_SEP);
 +            atc->thread_plist[thread].n += GMX_CACHE_SEP;
 +        }
 +        snew(atc->spline[thread].thread_one, pme->nthread);
 +        atc->spline[thread].thread_one[thread] = 1;
 +    }
 +}
 +
 +static void
 +init_overlap_comm(pme_overlap_t *  ol,
 +                  int              norder,
 +#ifdef GMX_MPI
 +                  MPI_Comm         comm,
 +#endif
 +                  int              nnodes,
 +                  int              nodeid,
 +                  int              ndata,
 +                  int              commplainsize)
 +{
 +    int lbnd, rbnd, maxlr, b, i;
 +    int exten;
 +    int nn, nk;
 +    pme_grid_comm_t *pgc;
 +    gmx_bool bCont;
 +    int fft_start, fft_end, send_index1, recv_index1;
 +#ifdef GMX_MPI
 +    MPI_Status stat;
 +
 +    ol->mpi_comm = comm;
 +#endif
 +
 +    ol->nnodes = nnodes;
 +    ol->nodeid = nodeid;
 +
 +    /* Linear translation of the PME grid won't affect reciprocal space
 +     * calculations, so to optimize we only interpolate "upwards",
 +     * which also means we only have to consider overlap in one direction.
 +     * I.e., particles on this node might also be spread to grid indices
 +     * that belong to higher nodes (modulo nnodes)
 +     */
 +
 +    snew(ol->s2g0, ol->nnodes+1);
 +    snew(ol->s2g1, ol->nnodes);
 +    if (debug)
 +    {
 +        fprintf(debug, "PME slab boundaries:");
 +    }
 +    for (i = 0; i < nnodes; i++)
 +    {
 +        /* s2g0 the local interpolation grid start.
 +         * s2g1 the local interpolation grid end.
 +         * Because grid overlap communication only goes forward,
 +         * the grid the slabs for fft's should be rounded down.
 +         */
 +        ol->s2g0[i] = ( i   *ndata + 0       )/nnodes;
 +        ol->s2g1[i] = ((i+1)*ndata + nnodes-1)/nnodes + norder - 1;
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "  %3d %3d", ol->s2g0[i], ol->s2g1[i]);
 +        }
 +    }
 +    ol->s2g0[nnodes] = ndata;
 +    if (debug)
 +    {
 +        fprintf(debug, "\n");
 +    }
 +
 +    /* Determine with how many nodes we need to communicate the grid overlap */
 +    b = 0;
 +    do
 +    {
 +        b++;
 +        bCont = FALSE;
 +        for (i = 0; i < nnodes; i++)
 +        {
 +            if ((i+b <  nnodes && ol->s2g1[i] > ol->s2g0[i+b]) ||
 +                (i+b >= nnodes && ol->s2g1[i] > ol->s2g0[i+b-nnodes] + ndata))
 +            {
 +                bCont = TRUE;
 +            }
 +        }
 +    }
 +    while (bCont && b < nnodes);
 +    ol->noverlap_nodes = b - 1;
 +
 +    snew(ol->send_id, ol->noverlap_nodes);
 +    snew(ol->recv_id, ol->noverlap_nodes);
 +    for (b = 0; b < ol->noverlap_nodes; b++)
 +    {
 +        ol->send_id[b] = (ol->nodeid + (b + 1)) % ol->nnodes;
 +        ol->recv_id[b] = (ol->nodeid - (b + 1) + ol->nnodes) % ol->nnodes;
 +    }
 +    snew(ol->comm_data, ol->noverlap_nodes);
 +
 +    ol->send_size = 0;
 +    for (b = 0; b < ol->noverlap_nodes; b++)
 +    {
 +        pgc = &ol->comm_data[b];
 +        /* Send */
 +        fft_start        = ol->s2g0[ol->send_id[b]];
 +        fft_end          = ol->s2g0[ol->send_id[b]+1];
 +        if (ol->send_id[b] < nodeid)
 +        {
 +            fft_start += ndata;
 +            fft_end   += ndata;
 +        }
 +        send_index1       = ol->s2g1[nodeid];
 +        send_index1       = min(send_index1, fft_end);
 +        pgc->send_index0  = fft_start;
 +        pgc->send_nindex  = max(0, send_index1 - pgc->send_index0);
 +        ol->send_size    += pgc->send_nindex;
 +
 +        /* We always start receiving to the first index of our slab */
 +        fft_start        = ol->s2g0[ol->nodeid];
 +        fft_end          = ol->s2g0[ol->nodeid+1];
 +        recv_index1      = ol->s2g1[ol->recv_id[b]];
 +        if (ol->recv_id[b] > nodeid)
 +        {
 +            recv_index1 -= ndata;
 +        }
 +        recv_index1      = min(recv_index1, fft_end);
 +        pgc->recv_index0 = fft_start;
 +        pgc->recv_nindex = max(0, recv_index1 - pgc->recv_index0);
 +    }
 +
 +#ifdef GMX_MPI
 +    /* Communicate the buffer sizes to receive */
 +    for (b = 0; b < ol->noverlap_nodes; b++)
 +    {
 +        MPI_Sendrecv(&ol->send_size, 1, MPI_INT, ol->send_id[b], b,
 +                     &ol->comm_data[b].recv_size, 1, MPI_INT, ol->recv_id[b], b,
 +                     ol->mpi_comm, &stat);
 +    }
 +#endif
 +
 +    /* For non-divisible grid we need pme_order iso pme_order-1 */
 +    snew(ol->sendbuf, norder*commplainsize);
 +    snew(ol->recvbuf, norder*commplainsize);
 +}
 +
 +static void
 +make_gridindex5_to_localindex(int n, int local_start, int local_range,
 +                              int **global_to_local,
 +                              real **fraction_shift)
 +{
 +    int i;
 +    int * gtl;
 +    real * fsh;
 +
 +    snew(gtl, 5*n);
 +    snew(fsh, 5*n);
 +    for (i = 0; (i < 5*n); i++)
 +    {
 +        /* Determine the global to local grid index */
 +        gtl[i] = (i - local_start + n) % n;
 +        /* For coordinates that fall within the local grid the fraction
 +         * is correct, we don't need to shift it.
 +         */
 +        fsh[i] = 0;
 +        if (local_range < n)
 +        {
 +            /* Due to rounding issues i could be 1 beyond the lower or
 +             * upper boundary of the local grid. Correct the index for this.
 +             * If we shift the index, we need to shift the fraction by
 +             * the same amount in the other direction to not affect
 +             * the weights.
 +             * Note that due to this shifting the weights at the end of
 +             * the spline might change, but that will only involve values
 +             * between zero and values close to the precision of a real,
 +             * which is anyhow the accuracy of the whole mesh calculation.
 +             */
 +            /* With local_range=0 we should not change i=local_start */
 +            if (i % n != local_start)
 +            {
 +                if (gtl[i] == n-1)
 +                {
 +                    gtl[i] = 0;
 +                    fsh[i] = -1;
 +                }
 +                else if (gtl[i] == local_range)
 +                {
 +                    gtl[i] = local_range - 1;
 +                    fsh[i] = 1;
 +                }
 +            }
 +        }
 +    }
 +
 +    *global_to_local = gtl;
 +    *fraction_shift  = fsh;
 +}
 +
 +static pme_spline_work_t *make_pme_spline_work(int order)
 +{
 +    pme_spline_work_t *work;
 +
 +#ifdef PME_SSE
 +    float  tmp[8];
 +    __m128 zero_SSE;
 +    int    of, i;
 +
 +    snew_aligned(work, 1, 16);
 +
 +    zero_SSE = _mm_setzero_ps();
 +
 +    /* Generate bit masks to mask out the unused grid entries,
 +     * as we only operate on order of the 8 grid entries that are
 +     * load into 2 SSE float registers.
 +     */
 +    for (of = 0; of < 8-(order-1); of++)
 +    {
 +        for (i = 0; i < 8; i++)
 +        {
 +            tmp[i] = (i >= of && i < of+order ? 1 : 0);
 +        }
 +        work->mask_SSE0[of] = _mm_loadu_ps(tmp);
 +        work->mask_SSE1[of] = _mm_loadu_ps(tmp+4);
 +        work->mask_SSE0[of] = _mm_cmpgt_ps(work->mask_SSE0[of], zero_SSE);
 +        work->mask_SSE1[of] = _mm_cmpgt_ps(work->mask_SSE1[of], zero_SSE);
 +    }
 +#else
 +    work = NULL;
 +#endif
 +
 +    return work;
 +}
 +
-     int nk_new;
-     if (*nk % nnodes != 0)
++void gmx_pme_check_restrictions(int pme_order,
++                                int nkx, int nky, int nkz,
++                                int nnodes_major,
++                                int nnodes_minor,
++                                gmx_bool bUseThreads,
++                                gmx_bool bFatal,
++                                gmx_bool *bValidSettings)
 +{
-         nk_new = nnodes*(*nk/nnodes + 1);
++    if (pme_order > PME_ORDER_MAX)
 +    {
-         if (2*nk_new >= 3*(*nk))
++        if (!bFatal)
++        {
++            *bValidSettings = FALSE;
++            return;
++        }
++        gmx_fatal(FARGS, "pme_order (%d) is larger than the maximum allowed value (%d). Modify and recompile the code if you really need such a high order.",
++                  pme_order, PME_ORDER_MAX);
++    }
 +
-             gmx_fatal(FARGS, "The PME grid size in dim %c (%d) is not divisble by the number of nodes doing PME in dim %c (%d). The grid size would have to be increased by more than 50%% to make the grid divisible. Change the total number of nodes or the number of domain decomposition cells in x or the PME grid %c dimension (and the cut-off).",
-                       dim, *nk, dim, nnodes, dim);
++    if (nkx <= pme_order*(nnodes_major > 1 ? 2 : 1) ||
++        nky <= pme_order*(nnodes_minor > 1 ? 2 : 1) ||
++        nkz <= pme_order)
++    {
++        if (!bFatal)
 +        {
-         if (fplog != NULL)
++            *bValidSettings = FALSE;
++            return;
 +        }
++        gmx_fatal(FARGS, "The PME grid sizes need to be larger than pme_order (%d) and for dimensions with domain decomposition larger than 2*pme_order",
++                  pme_order);
++    }
 +
-             fprintf(fplog, "\nNOTE: The PME grid size in dim %c (%d) is not divisble by the number of nodes doing PME in dim %c (%d). Increasing the PME grid size in dim %c to %d. This will increase the accuracy and will not decrease the performance significantly on this number of nodes. For optimal performance change the total number of nodes or the number of domain decomposition cells in x or the PME grid %c dimension (and the cut-off).\n\n",
-                     dim, *nk, dim, nnodes, dim, nk_new, dim);
++    /* Check for a limitation of the (current) sum_fftgrid_dd code.
++     * We only allow multiple communication pulses in dim 1, not in dim 0.
++     */
++    if (bUseThreads && (nkx < nnodes_major*pme_order &&
++                        nkx != nnodes_major*(pme_order - 1)))
++    {
++        if (!bFatal)
 +        {
-         *nk = nk_new;
++            *bValidSettings = FALSE;
++            return;
 +        }
++        gmx_fatal(FARGS, "The number of PME grid lines per node along x is %g. But when using OpenMP threads, the number of grid lines per node along x should be >= pme_order (%d) or = pmeorder-1. To resolve this issue, use less nodes along x (and possibly more along y and/or z) by specifying -dd manually.",
++                  nkx/(double)nnodes_major, pme_order);
++    }
 +
-     pme_atomcomm_t *atc;
++    if (bValidSettings != NULL)
++    {
++        *bValidSettings = TRUE;
 +    }
++
++    return;
 +}
 +
 +int gmx_pme_init(gmx_pme_t *         pmedata,
 +                 t_commrec *         cr,
 +                 int                 nnodes_major,
 +                 int                 nnodes_minor,
 +                 t_inputrec *        ir,
 +                 int                 homenr,
 +                 gmx_bool            bFreeEnergy,
 +                 gmx_bool            bReproducible,
 +                 int                 nthread)
 +{
 +    gmx_pme_t pme = NULL;
 +
-     if (pme->pme_order > PME_ORDER_MAX)
-     {
-         gmx_fatal(FARGS, "pme_order (%d) is larger than the maximum allowed value (%d). Modify and recompile the code if you really need such a high order.",
-                   pme->pme_order, PME_ORDER_MAX);
-     }
-     /* Currently pme.c supports only the fft5d FFT code.
-      * Therefore the grid always needs to be divisible by nnodes.
-      * When the old 1D code is also supported again, change this check.
-      *
-      * This check should be done before calling gmx_pme_init
-      * and fplog should be passed iso stderr.
-      *
-        if (pme->ndecompdim >= 2)
-      */
-     if (pme->ndecompdim >= 1)
-     {
-         /*
-            gmx_pme_check_grid_restrictions(pme->nodeid==0 ? stderr : NULL,
-                                         'x',nnodes_major,&pme->nkx);
-            gmx_pme_check_grid_restrictions(pme->nodeid==0 ? stderr : NULL,
-                                         'y',nnodes_minor,&pme->nky);
-          */
-     }
-     if (pme->nkx <= pme->pme_order*(pme->nnodes_major > 1 ? 2 : 1) ||
-         pme->nky <= pme->pme_order*(pme->nnodes_minor > 1 ? 2 : 1) ||
-         pme->nkz <= pme->pme_order)
-     {
-         gmx_fatal(FARGS, "The PME grid sizes need to be larger than pme_order (%d) and for dimensions with domain decomposition larger than 2*pme_order", pme->pme_order);
-     }
++    int  use_threads, sum_use_threads;
 +    ivec ndata;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "Creating PME data structures.\n");
 +    }
 +    snew(pme, 1);
 +
 +    pme->redist_init         = FALSE;
 +    pme->sum_qgrid_tmp       = NULL;
 +    pme->sum_qgrid_dd_tmp    = NULL;
 +    pme->buf_nalloc          = 0;
 +    pme->redist_buf_nalloc   = 0;
 +
 +    pme->nnodes              = 1;
 +    pme->bPPnode             = TRUE;
 +
 +    pme->nnodes_major        = nnodes_major;
 +    pme->nnodes_minor        = nnodes_minor;
 +
 +#ifdef GMX_MPI
 +    if (nnodes_major*nnodes_minor > 1)
 +    {
 +        pme->mpi_comm = cr->mpi_comm_mygroup;
 +
 +        MPI_Comm_rank(pme->mpi_comm, &pme->nodeid);
 +        MPI_Comm_size(pme->mpi_comm, &pme->nnodes);
 +        if (pme->nnodes != nnodes_major*nnodes_minor)
 +        {
 +            gmx_incons("PME node count mismatch");
 +        }
 +    }
 +    else
 +    {
 +        pme->mpi_comm = MPI_COMM_NULL;
 +    }
 +#endif
 +
 +    if (pme->nnodes == 1)
 +    {
 +#ifdef GMX_MPI
 +        pme->mpi_comm_d[0] = MPI_COMM_NULL;
 +        pme->mpi_comm_d[1] = MPI_COMM_NULL;
 +#endif
 +        pme->ndecompdim   = 0;
 +        pme->nodeid_major = 0;
 +        pme->nodeid_minor = 0;
 +#ifdef GMX_MPI
 +        pme->mpi_comm_d[0] = pme->mpi_comm_d[1] = MPI_COMM_NULL;
 +#endif
 +    }
 +    else
 +    {
 +        if (nnodes_minor == 1)
 +        {
 +#ifdef GMX_MPI
 +            pme->mpi_comm_d[0] = pme->mpi_comm;
 +            pme->mpi_comm_d[1] = MPI_COMM_NULL;
 +#endif
 +            pme->ndecompdim   = 1;
 +            pme->nodeid_major = pme->nodeid;
 +            pme->nodeid_minor = 0;
 +
 +        }
 +        else if (nnodes_major == 1)
 +        {
 +#ifdef GMX_MPI
 +            pme->mpi_comm_d[0] = MPI_COMM_NULL;
 +            pme->mpi_comm_d[1] = pme->mpi_comm;
 +#endif
 +            pme->ndecompdim   = 1;
 +            pme->nodeid_major = 0;
 +            pme->nodeid_minor = pme->nodeid;
 +        }
 +        else
 +        {
 +            if (pme->nnodes % nnodes_major != 0)
 +            {
 +                gmx_incons("For 2D PME decomposition, #PME nodes must be divisible by the number of nodes in the major dimension");
 +            }
 +            pme->ndecompdim = 2;
 +
 +#ifdef GMX_MPI
 +            MPI_Comm_split(pme->mpi_comm, pme->nodeid % nnodes_minor,
 +                           pme->nodeid, &pme->mpi_comm_d[0]);  /* My communicator along major dimension */
 +            MPI_Comm_split(pme->mpi_comm, pme->nodeid/nnodes_minor,
 +                           pme->nodeid, &pme->mpi_comm_d[1]);  /* My communicator along minor dimension */
 +
 +            MPI_Comm_rank(pme->mpi_comm_d[0], &pme->nodeid_major);
 +            MPI_Comm_size(pme->mpi_comm_d[0], &pme->nnodes_major);
 +            MPI_Comm_rank(pme->mpi_comm_d[1], &pme->nodeid_minor);
 +            MPI_Comm_size(pme->mpi_comm_d[1], &pme->nnodes_minor);
 +#endif
 +        }
 +        pme->bPPnode = (cr->duty & DUTY_PP);
 +    }
 +
 +    pme->nthread = nthread;
 +
++     /* Check if any of the PME MPI ranks uses threads */
++    use_threads = (pme->nthread > 1 ? 1 : 0);
++#ifdef GMX_MPI
++    if (pme->nnodes > 1)
++    {
++        MPI_Allreduce(&use_threads, &sum_use_threads, 1, MPI_INT,
++                      MPI_SUM, pme->mpi_comm);
++    }
++    else
++#endif
++    {
++        sum_use_threads = use_threads;
++    }
++    pme->bUseThreads = (sum_use_threads > 0);
++
 +    if (ir->ePBC == epbcSCREW)
 +    {
 +        gmx_fatal(FARGS, "pme does not (yet) work with pbc = screw");
 +    }
 +
 +    pme->bFEP        = ((ir->efep != efepNO) && bFreeEnergy);
 +    pme->nkx         = ir->nkx;
 +    pme->nky         = ir->nky;
 +    pme->nkz         = ir->nkz;
 +    pme->bP3M        = (ir->coulombtype == eelP3M_AD || getenv("GMX_PME_P3M") != NULL);
 +    pme->pme_order   = ir->pme_order;
 +    pme->epsilon_r   = ir->epsilon_r;
 +
-     /* Check for a limitation of the (current) sum_fftgrid_dd code.
-      * We only allow multiple communication pulses in dim 1, not in dim 0.
++    /* If we violate restrictions, generate a fatal error here */
++    gmx_pme_check_restrictions(pme->pme_order,
++                               pme->nkx, pme->nky, pme->nkz,
++                               pme->nnodes_major,
++                               pme->nnodes_minor,
++                               pme->bUseThreads,
++                               TRUE,
++                               NULL);
 +
 +    if (pme->nnodes > 1)
 +    {
 +        double imbal;
 +
 +#ifdef GMX_MPI
 +        MPI_Type_contiguous(DIM, mpi_type, &(pme->rvec_mpi));
 +        MPI_Type_commit(&(pme->rvec_mpi));
 +#endif
 +
 +        /* Note that the charge spreading and force gathering, which usually
 +         * takes about the same amount of time as FFT+solve_pme,
 +         * is always fully load balanced
 +         * (unless the charge distribution is inhomogeneous).
 +         */
 +
 +        imbal = pme_load_imbalance(pme);
 +        if (imbal >= 1.2 && pme->nodeid_major == 0 && pme->nodeid_minor == 0)
 +        {
 +            fprintf(stderr,
 +                    "\n"
 +                    "NOTE: The load imbalance in PME FFT and solve is %d%%.\n"
 +                    "      For optimal PME load balancing\n"
 +                    "      PME grid_x (%d) and grid_y (%d) should be divisible by #PME_nodes_x (%d)\n"
 +                    "      and PME grid_y (%d) and grid_z (%d) should be divisible by #PME_nodes_y (%d)\n"
 +                    "\n",
 +                    (int)((imbal-1)*100 + 0.5),
 +                    pme->nkx, pme->nky, pme->nnodes_major,
 +                    pme->nky, pme->nkz, pme->nnodes_minor);
 +        }
 +    }
 +
 +    /* For non-divisible grid we need pme_order iso pme_order-1 */
 +    /* In sum_qgrid_dd x overlap is copied in place: take padding into account.
 +     * y is always copied through a buffer: we don't need padding in z,
 +     * but we do need the overlap in x because of the communication order.
 +     */
 +    init_overlap_comm(&pme->overlap[0], pme->pme_order,
 +#ifdef GMX_MPI
 +                      pme->mpi_comm_d[0],
 +#endif
 +                      pme->nnodes_major, pme->nodeid_major,
 +                      pme->nkx,
 +                      (div_round_up(pme->nky, pme->nnodes_minor)+pme->pme_order)*(pme->nkz+pme->pme_order-1));
 +
 +    /* Along overlap dim 1 we can send in multiple pulses in sum_fftgrid_dd.
 +     * We do this with an offset buffer of equal size, so we need to allocate
 +     * extra for the offset. That's what the (+1)*pme->nkz is for.
 +     */
 +    init_overlap_comm(&pme->overlap[1], pme->pme_order,
 +#ifdef GMX_MPI
 +                      pme->mpi_comm_d[1],
 +#endif
 +                      pme->nnodes_minor, pme->nodeid_minor,
 +                      pme->nky,
 +                      (div_round_up(pme->nkx, pme->nnodes_major)+pme->pme_order+1)*pme->nkz);
 +
-     if (pme->nthread > 1 && (pme->overlap[0].noverlap_nodes > 1 ||
-                              pme->nkx < pme->nnodes_major*pme->pme_order))
++    /* Double-check for a limitation of the (current) sum_fftgrid_dd code.
++     * Note that gmx_pme_check_restrictions checked for this already.
 +     */
-         gmx_fatal(FARGS, "The number of PME grid lines per node along x is %g. But when using OpenMP threads, the number of grid lines per node along x and should be >= pme_order (%d). To resolve this issue, use less nodes along x (and possibly more along y and/or z) by specifying -dd manually.",
-                   pme->nkx/(double)pme->nnodes_major, pme->pme_order);
++    if (pme->bUseThreads && pme->overlap[0].noverlap_nodes > 1)
 +    {
-     if (new->nthread > 1 && new->nthread == old->nthread)
++        gmx_incons("More than one communication pulse required for grid overlap communication along the major dimension while using threads");
 +    }
 +
 +    snew(pme->bsp_mod[XX], pme->nkx);
 +    snew(pme->bsp_mod[YY], pme->nky);
 +    snew(pme->bsp_mod[ZZ], pme->nkz);
 +
 +    /* The required size of the interpolation grid, including overlap.
 +     * The allocated size (pmegrid_n?) might be slightly larger.
 +     */
 +    pme->pmegrid_nx = pme->overlap[0].s2g1[pme->nodeid_major] -
 +        pme->overlap[0].s2g0[pme->nodeid_major];
 +    pme->pmegrid_ny = pme->overlap[1].s2g1[pme->nodeid_minor] -
 +        pme->overlap[1].s2g0[pme->nodeid_minor];
 +    pme->pmegrid_nz_base = pme->nkz;
 +    pme->pmegrid_nz      = pme->pmegrid_nz_base + pme->pme_order - 1;
 +    set_grid_alignment(&pme->pmegrid_nz, pme->pme_order);
 +
 +    pme->pmegrid_start_ix = pme->overlap[0].s2g0[pme->nodeid_major];
 +    pme->pmegrid_start_iy = pme->overlap[1].s2g0[pme->nodeid_minor];
 +    pme->pmegrid_start_iz = 0;
 +
 +    make_gridindex5_to_localindex(pme->nkx,
 +                                  pme->pmegrid_start_ix,
 +                                  pme->pmegrid_nx - (pme->pme_order-1),
 +                                  &pme->nnx, &pme->fshx);
 +    make_gridindex5_to_localindex(pme->nky,
 +                                  pme->pmegrid_start_iy,
 +                                  pme->pmegrid_ny - (pme->pme_order-1),
 +                                  &pme->nny, &pme->fshy);
 +    make_gridindex5_to_localindex(pme->nkz,
 +                                  pme->pmegrid_start_iz,
 +                                  pme->pmegrid_nz_base,
 +                                  &pme->nnz, &pme->fshz);
 +
 +    pmegrids_init(&pme->pmegridA,
 +                  pme->pmegrid_nx, pme->pmegrid_ny, pme->pmegrid_nz,
 +                  pme->pmegrid_nz_base,
 +                  pme->pme_order,
++                  pme->bUseThreads,
 +                  pme->nthread,
 +                  pme->overlap[0].s2g1[pme->nodeid_major]-pme->overlap[0].s2g0[pme->nodeid_major+1],
 +                  pme->overlap[1].s2g1[pme->nodeid_minor]-pme->overlap[1].s2g0[pme->nodeid_minor+1]);
 +
 +    pme->spline_work = make_pme_spline_work(pme->pme_order);
 +
 +    ndata[0] = pme->nkx;
 +    ndata[1] = pme->nky;
 +    ndata[2] = pme->nkz;
 +
 +    /* This routine will allocate the grid data to fit the FFTs */
 +    gmx_parallel_3dfft_init(&pme->pfft_setupA, ndata,
 +                            &pme->fftgridA, &pme->cfftgridA,
 +                            pme->mpi_comm_d,
 +                            pme->overlap[0].s2g0, pme->overlap[1].s2g0,
 +                            bReproducible, pme->nthread);
 +
 +    if (bFreeEnergy)
 +    {
 +        pmegrids_init(&pme->pmegridB,
 +                      pme->pmegrid_nx, pme->pmegrid_ny, pme->pmegrid_nz,
 +                      pme->pmegrid_nz_base,
 +                      pme->pme_order,
++                      pme->bUseThreads,
 +                      pme->nthread,
 +                      pme->nkx % pme->nnodes_major != 0,
 +                      pme->nky % pme->nnodes_minor != 0);
 +
 +        gmx_parallel_3dfft_init(&pme->pfft_setupB, ndata,
 +                                &pme->fftgridB, &pme->cfftgridB,
 +                                pme->mpi_comm_d,
 +                                pme->overlap[0].s2g0, pme->overlap[1].s2g0,
 +                                bReproducible, pme->nthread);
 +    }
 +    else
 +    {
 +        pme->pmegridB.grid.grid = NULL;
 +        pme->fftgridB           = NULL;
 +        pme->cfftgridB          = NULL;
 +    }
 +
 +    if (!pme->bP3M)
 +    {
 +        /* Use plain SPME B-spline interpolation */
 +        make_bspline_moduli(pme->bsp_mod, pme->nkx, pme->nky, pme->nkz, pme->pme_order);
 +    }
 +    else
 +    {
 +        /* Use the P3M grid-optimized influence function */
 +        make_p3m_bspline_moduli(pme->bsp_mod, pme->nkx, pme->nky, pme->nkz, pme->pme_order);
 +    }
 +
 +    /* Use atc[0] for spreading */
 +    init_atomcomm(pme, &pme->atc[0], cr, nnodes_major > 1 ? 0 : 1, TRUE);
 +    if (pme->ndecompdim >= 2)
 +    {
 +        init_atomcomm(pme, &pme->atc[1], cr, 1, FALSE);
 +    }
 +
 +    if (pme->nnodes == 1)
 +    {
 +        pme->atc[0].n = homenr;
 +        pme_realloc_atomcomm_things(&pme->atc[0]);
 +    }
 +
 +    {
 +        int thread;
 +
 +        /* Use fft5d, order after FFT is y major, z, x minor */
 +
 +        snew(pme->work, pme->nthread);
 +        for (thread = 0; thread < pme->nthread; thread++)
 +        {
 +            realloc_work(&pme->work[thread], pme->nkx);
 +        }
 +    }
 +
 +    *pmedata = pme;
 +
 +    return 0;
 +}
 +
 +static void reuse_pmegrids(const pmegrids_t *old, pmegrids_t *new)
 +{
 +    int d, t;
 +
 +    for (d = 0; d < DIM; d++)
 +    {
 +        if (new->grid.n[d] > old->grid.n[d])
 +        {
 +            return;
 +        }
 +    }
 +
 +    sfree_aligned(new->grid.grid);
 +    new->grid.grid = old->grid.grid;
 +
-         pmegrid_t *grid;
++    if (new->grid_th != NULL && new->nthread == old->nthread)
 +    {
 +        sfree_aligned(new->grid_all);
 +        for (t = 0; t < new->nthread; t++)
 +        {
 +            new->grid_th[t].grid = old->grid_th[t].grid;
 +        }
 +    }
 +}
 +
 +int gmx_pme_reinit(gmx_pme_t *         pmedata,
 +                   t_commrec *         cr,
 +                   gmx_pme_t           pme_src,
 +                   const t_inputrec *  ir,
 +                   ivec                grid_size)
 +{
 +    t_inputrec irc;
 +    int homenr;
 +    int ret;
 +
 +    irc     = *ir;
 +    irc.nkx = grid_size[XX];
 +    irc.nky = grid_size[YY];
 +    irc.nkz = grid_size[ZZ];
 +
 +    if (pme_src->nnodes == 1)
 +    {
 +        homenr = pme_src->atc[0].n;
 +    }
 +    else
 +    {
 +        homenr = -1;
 +    }
 +
 +    ret = gmx_pme_init(pmedata, cr, pme_src->nnodes_major, pme_src->nnodes_minor,
 +                       &irc, homenr, pme_src->bFEP, FALSE, pme_src->nthread);
 +
 +    if (ret == 0)
 +    {
 +        /* We can easily reuse the allocated pme grids in pme_src */
 +        reuse_pmegrids(&pme_src->pmegridA, &(*pmedata)->pmegridA);
 +        /* We would like to reuse the fft grids, but that's harder */
 +    }
 +
 +    return ret;
 +}
 +
 +
 +static void copy_local_grid(gmx_pme_t pme,
 +                            pmegrids_t *pmegrids, int thread, real *fftgrid)
 +{
 +    ivec local_fft_ndata, local_fft_offset, local_fft_size;
 +    int  fft_my, fft_mz;
 +    int  nsx, nsy, nsz;
 +    ivec nf;
 +    int  offx, offy, offz, x, y, z, i0, i0t;
 +    int  d;
 +    pmegrid_t *pmegrid;
 +    real *grid_th;
 +
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setupA,
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +    fft_my = local_fft_size[YY];
 +    fft_mz = local_fft_size[ZZ];
 +
 +    pmegrid = &pmegrids->grid_th[thread];
 +
 +    nsx = pmegrid->s[XX];
 +    nsy = pmegrid->s[YY];
 +    nsz = pmegrid->s[ZZ];
 +
 +    for (d = 0; d < DIM; d++)
 +    {
 +        nf[d] = min(pmegrid->n[d] - (pmegrid->order - 1),
 +                    local_fft_ndata[d] - pmegrid->offset[d]);
 +    }
 +
 +    offx = pmegrid->offset[XX];
 +    offy = pmegrid->offset[YY];
 +    offz = pmegrid->offset[ZZ];
 +
 +    /* Directly copy the non-overlapping parts of the local grids.
 +     * This also initializes the full grid.
 +     */
 +    grid_th = pmegrid->grid;
 +    for (x = 0; x < nf[XX]; x++)
 +    {
 +        for (y = 0; y < nf[YY]; y++)
 +        {
 +            i0  = ((offx + x)*fft_my + (offy + y))*fft_mz + offz;
 +            i0t = (x*nsy + y)*nsz;
 +            for (z = 0; z < nf[ZZ]; z++)
 +            {
 +                fftgrid[i0+z] = grid_th[i0t+z];
 +            }
 +        }
 +    }
 +}
 +
 +static void
 +reduce_threadgrid_overlap(gmx_pme_t pme,
 +                          const pmegrids_t *pmegrids, int thread,
 +                          real *fftgrid, real *commbuf_x, real *commbuf_y)
 +{
 +    ivec local_fft_ndata, local_fft_offset, local_fft_size;
 +    int  fft_nx, fft_ny, fft_nz;
 +    int  fft_my, fft_mz;
 +    int  buf_my = -1;
 +    int  nsx, nsy, nsz;
 +    ivec ne;
 +    int  offx, offy, offz, x, y, z, i0, i0t;
 +    int  sx, sy, sz, fx, fy, fz, tx1, ty1, tz1, ox, oy, oz;
 +    gmx_bool bClearBufX, bClearBufY, bClearBufXY, bClearBuf;
 +    gmx_bool bCommX, bCommY;
 +    int  d;
 +    int  thread_f;
 +    const pmegrid_t *pmegrid, *pmegrid_g, *pmegrid_f;
 +    const real *grid_th;
 +    real *commbuf = NULL;
 +
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setupA,
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +    fft_nx = local_fft_ndata[XX];
 +    fft_ny = local_fft_ndata[YY];
 +    fft_nz = local_fft_ndata[ZZ];
 +
 +    fft_my = local_fft_size[YY];
 +    fft_mz = local_fft_size[ZZ];
 +
 +    /* This routine is called when all thread have finished spreading.
 +     * Here each thread sums grid contributions calculated by other threads
 +     * to the thread local grid volume.
 +     * To minimize the number of grid copying operations,
 +     * this routines sums immediately from the pmegrid to the fftgrid.
 +     */
 +
 +    /* Determine which part of the full node grid we should operate on,
 +     * this is our thread local part of the full grid.
 +     */
 +    pmegrid = &pmegrids->grid_th[thread];
 +
 +    for (d = 0; d < DIM; d++)
 +    {
 +        ne[d] = min(pmegrid->offset[d]+pmegrid->n[d]-(pmegrid->order-1),
 +                    local_fft_ndata[d]);
 +    }
 +
 +    offx = pmegrid->offset[XX];
 +    offy = pmegrid->offset[YY];
 +    offz = pmegrid->offset[ZZ];
 +
 +
 +    bClearBufX  = TRUE;
 +    bClearBufY  = TRUE;
 +    bClearBufXY = TRUE;
 +
 +    /* Now loop over all the thread data blocks that contribute
 +     * to the grid region we (our thread) are operating on.
 +     */
 +    /* Note that ffy_nx/y is equal to the number of grid points
 +     * between the first point of our node grid and the one of the next node.
 +     */
 +    for (sx = 0; sx >= -pmegrids->nthread_comm[XX]; sx--)
 +    {
 +        fx     = pmegrid->ci[XX] + sx;
 +        ox     = 0;
 +        bCommX = FALSE;
 +        if (fx < 0)
 +        {
 +            fx    += pmegrids->nc[XX];
 +            ox    -= fft_nx;
 +            bCommX = (pme->nnodes_major > 1);
 +        }
 +        pmegrid_g = &pmegrids->grid_th[fx*pmegrids->nc[YY]*pmegrids->nc[ZZ]];
 +        ox       += pmegrid_g->offset[XX];
 +        if (!bCommX)
 +        {
 +            tx1 = min(ox + pmegrid_g->n[XX], ne[XX]);
 +        }
 +        else
 +        {
 +            tx1 = min(ox + pmegrid_g->n[XX], pme->pme_order);
 +        }
 +
 +        for (sy = 0; sy >= -pmegrids->nthread_comm[YY]; sy--)
 +        {
 +            fy     = pmegrid->ci[YY] + sy;
 +            oy     = 0;
 +            bCommY = FALSE;
 +            if (fy < 0)
 +            {
 +                fy    += pmegrids->nc[YY];
 +                oy    -= fft_ny;
 +                bCommY = (pme->nnodes_minor > 1);
 +            }
 +            pmegrid_g = &pmegrids->grid_th[fy*pmegrids->nc[ZZ]];
 +            oy       += pmegrid_g->offset[YY];
 +            if (!bCommY)
 +            {
 +                ty1 = min(oy + pmegrid_g->n[YY], ne[YY]);
 +            }
 +            else
 +            {
 +                ty1 = min(oy + pmegrid_g->n[YY], pme->pme_order);
 +            }
 +
 +            for (sz = 0; sz >= -pmegrids->nthread_comm[ZZ]; sz--)
 +            {
 +                fz = pmegrid->ci[ZZ] + sz;
 +                oz = 0;
 +                if (fz < 0)
 +                {
 +                    fz += pmegrids->nc[ZZ];
 +                    oz -= fft_nz;
 +                }
 +                pmegrid_g = &pmegrids->grid_th[fz];
 +                oz       += pmegrid_g->offset[ZZ];
 +                tz1       = min(oz + pmegrid_g->n[ZZ], ne[ZZ]);
 +
 +                if (sx == 0 && sy == 0 && sz == 0)
 +                {
 +                    /* We have already added our local contribution
 +                     * before calling this routine, so skip it here.
 +                     */
 +                    continue;
 +                }
 +
 +                thread_f = (fx*pmegrids->nc[YY] + fy)*pmegrids->nc[ZZ] + fz;
 +
 +                pmegrid_f = &pmegrids->grid_th[thread_f];
 +
 +                grid_th = pmegrid_f->grid;
 +
 +                nsx = pmegrid_f->s[XX];
 +                nsy = pmegrid_f->s[YY];
 +                nsz = pmegrid_f->s[ZZ];
 +
 +#ifdef DEBUG_PME_REDUCE
 +                printf("n%d t%d add %d  %2d %2d %2d  %2d %2d %2d  %2d-%2d %2d-%2d, %2d-%2d %2d-%2d, %2d-%2d %2d-%2d\n",
 +                       pme->nodeid, thread, thread_f,
 +                       pme->pmegrid_start_ix,
 +                       pme->pmegrid_start_iy,
 +                       pme->pmegrid_start_iz,
 +                       sx, sy, sz,
 +                       offx-ox, tx1-ox, offx, tx1,
 +                       offy-oy, ty1-oy, offy, ty1,
 +                       offz-oz, tz1-oz, offz, tz1);
 +#endif
 +
 +                if (!(bCommX || bCommY))
 +                {
 +                    /* Copy from the thread local grid to the node grid */
 +                    for (x = offx; x < tx1; x++)
 +                    {
 +                        for (y = offy; y < ty1; y++)
 +                        {
 +                            i0  = (x*fft_my + y)*fft_mz;
 +                            i0t = ((x - ox)*nsy + (y - oy))*nsz - oz;
 +                            for (z = offz; z < tz1; z++)
 +                            {
 +                                fftgrid[i0+z] += grid_th[i0t+z];
 +                            }
 +                        }
 +                    }
 +                }
 +                else
 +                {
 +                    /* The order of this conditional decides
 +                     * where the corner volume gets stored with x+y decomp.
 +                     */
 +                    if (bCommY)
 +                    {
 +                        commbuf = commbuf_y;
 +                        buf_my  = ty1 - offy;
 +                        if (bCommX)
 +                        {
 +                            /* We index commbuf modulo the local grid size */
 +                            commbuf += buf_my*fft_nx*fft_nz;
 +
 +                            bClearBuf   = bClearBufXY;
 +                            bClearBufXY = FALSE;
 +                        }
 +                        else
 +                        {
 +                            bClearBuf  = bClearBufY;
 +                            bClearBufY = FALSE;
 +                        }
 +                    }
 +                    else
 +                    {
 +                        commbuf    = commbuf_x;
 +                        buf_my     = fft_ny;
 +                        bClearBuf  = bClearBufX;
 +                        bClearBufX = FALSE;
 +                    }
 +
 +                    /* Copy to the communication buffer */
 +                    for (x = offx; x < tx1; x++)
 +                    {
 +                        for (y = offy; y < ty1; y++)
 +                        {
 +                            i0  = (x*buf_my + y)*fft_nz;
 +                            i0t = ((x - ox)*nsy + (y - oy))*nsz - oz;
 +
 +                            if (bClearBuf)
 +                            {
 +                                /* First access of commbuf, initialize it */
 +                                for (z = offz; z < tz1; z++)
 +                                {
 +                                    commbuf[i0+z]  = grid_th[i0t+z];
 +                                }
 +                            }
 +                            else
 +                            {
 +                                for (z = offz; z < tz1; z++)
 +                                {
 +                                    commbuf[i0+z] += grid_th[i0t+z];
 +                                }
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +
 +static void sum_fftgrid_dd(gmx_pme_t pme, real *fftgrid)
 +{
 +    ivec local_fft_ndata, local_fft_offset, local_fft_size;
 +    pme_overlap_t *overlap;
 +    int  send_index0, send_nindex;
 +    int  recv_nindex;
 +#ifdef GMX_MPI
 +    MPI_Status stat;
 +#endif
 +    int  send_size_y, recv_size_y;
 +    int  ipulse, send_id, recv_id, datasize, gridsize, size_yx;
 +    real *sendptr, *recvptr;
 +    int  x, y, z, indg, indb;
 +
 +    /* Note that this routine is only used for forward communication.
 +     * Since the force gathering, unlike the charge spreading,
 +     * can be trivially parallelized over the particles,
 +     * the backwards process is much simpler and can use the "old"
 +     * communication setup.
 +     */
 +
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setupA,
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +
 +    if (pme->nnodes_minor > 1)
 +    {
 +        /* Major dimension */
 +        overlap = &pme->overlap[1];
 +
 +        if (pme->nnodes_major > 1)
 +        {
 +            size_yx = pme->overlap[0].comm_data[0].send_nindex;
 +        }
 +        else
 +        {
 +            size_yx = 0;
 +        }
 +        datasize = (local_fft_ndata[XX] + size_yx)*local_fft_ndata[ZZ];
 +
 +        send_size_y = overlap->send_size;
 +
 +        for (ipulse = 0; ipulse < overlap->noverlap_nodes; ipulse++)
 +        {
 +            send_id       = overlap->send_id[ipulse];
 +            recv_id       = overlap->recv_id[ipulse];
 +            send_index0   =
 +                overlap->comm_data[ipulse].send_index0 -
 +                overlap->comm_data[0].send_index0;
 +            send_nindex   = overlap->comm_data[ipulse].send_nindex;
 +            /* We don't use recv_index0, as we always receive starting at 0 */
 +            recv_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +            recv_size_y   = overlap->comm_data[ipulse].recv_size;
 +
 +            sendptr = overlap->sendbuf + send_index0*local_fft_ndata[ZZ];
 +            recvptr = overlap->recvbuf;
 +
 +#ifdef GMX_MPI
 +            MPI_Sendrecv(sendptr, send_size_y*datasize, GMX_MPI_REAL,
 +                         send_id, ipulse,
 +                         recvptr, recv_size_y*datasize, GMX_MPI_REAL,
 +                         recv_id, ipulse,
 +                         overlap->mpi_comm, &stat);
 +#endif
 +
 +            for (x = 0; x < local_fft_ndata[XX]; x++)
 +            {
 +                for (y = 0; y < recv_nindex; y++)
 +                {
 +                    indg = (x*local_fft_size[YY] + y)*local_fft_size[ZZ];
 +                    indb = (x*recv_size_y        + y)*local_fft_ndata[ZZ];
 +                    for (z = 0; z < local_fft_ndata[ZZ]; z++)
 +                    {
 +                        fftgrid[indg+z] += recvptr[indb+z];
 +                    }
 +                }
 +            }
 +
 +            if (pme->nnodes_major > 1)
 +            {
 +                /* Copy from the received buffer to the send buffer for dim 0 */
 +                sendptr = pme->overlap[0].sendbuf;
 +                for (x = 0; x < size_yx; x++)
 +                {
 +                    for (y = 0; y < recv_nindex; y++)
 +                    {
 +                        indg = (x*local_fft_ndata[YY] + y)*local_fft_ndata[ZZ];
 +                        indb = ((local_fft_ndata[XX] + x)*recv_size_y + y)*local_fft_ndata[ZZ];
 +                        for (z = 0; z < local_fft_ndata[ZZ]; z++)
 +                        {
 +                            sendptr[indg+z] += recvptr[indb+z];
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    /* We only support a single pulse here.
 +     * This is not a severe limitation, as this code is only used
 +     * with OpenMP and with OpenMP the (PME) domains can be larger.
 +     */
 +    if (pme->nnodes_major > 1)
 +    {
 +        /* Major dimension */
 +        overlap = &pme->overlap[0];
 +
 +        datasize = local_fft_ndata[YY]*local_fft_ndata[ZZ];
 +        gridsize = local_fft_size[YY] *local_fft_size[ZZ];
 +
 +        ipulse = 0;
 +
 +        send_id       = overlap->send_id[ipulse];
 +        recv_id       = overlap->recv_id[ipulse];
 +        send_nindex   = overlap->comm_data[ipulse].send_nindex;
 +        /* We don't use recv_index0, as we always receive starting at 0 */
 +        recv_nindex   = overlap->comm_data[ipulse].recv_nindex;
 +
 +        sendptr = overlap->sendbuf;
 +        recvptr = overlap->recvbuf;
 +
 +        if (debug != NULL)
 +        {
 +            fprintf(debug, "PME fftgrid comm %2d x %2d x %2d\n",
 +                    send_nindex, local_fft_ndata[YY], local_fft_ndata[ZZ]);
 +        }
 +
 +#ifdef GMX_MPI
 +        MPI_Sendrecv(sendptr, send_nindex*datasize, GMX_MPI_REAL,
 +                     send_id, ipulse,
 +                     recvptr, recv_nindex*datasize, GMX_MPI_REAL,
 +                     recv_id, ipulse,
 +                     overlap->mpi_comm, &stat);
 +#endif
 +
 +        for (x = 0; x < recv_nindex; x++)
 +        {
 +            for (y = 0; y < local_fft_ndata[YY]; y++)
 +            {
 +                indg = (x*local_fft_size[YY]  + y)*local_fft_size[ZZ];
 +                indb = (x*local_fft_ndata[YY] + y)*local_fft_ndata[ZZ];
 +                for (z = 0; z < local_fft_ndata[ZZ]; z++)
 +                {
 +                    fftgrid[indg+z] += recvptr[indb+z];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +
 +static void spread_on_grid(gmx_pme_t pme,
 +                           pme_atomcomm_t *atc, pmegrids_t *grids,
 +                           gmx_bool bCalcSplines, gmx_bool bSpread,
 +                           real *fftgrid)
 +{
 +    int nthread, thread;
 +#ifdef PME_TIME_THREADS
 +    gmx_cycles_t c1, c2, c3, ct1a, ct1b, ct1c;
 +    static double cs1     = 0, cs2 = 0, cs3 = 0;
 +    static double cs1a[6] = {0, 0, 0, 0, 0, 0};
 +    static int cnt        = 0;
 +#endif
 +
 +    nthread = pme->nthread;
 +    assert(nthread > 0);
 +
 +#ifdef PME_TIME_THREADS
 +    c1 = omp_cyc_start();
 +#endif
 +    if (bCalcSplines)
 +    {
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +        for (thread = 0; thread < nthread; thread++)
 +        {
 +            int start, end;
 +
 +            start = atc->n* thread   /nthread;
 +            end   = atc->n*(thread+1)/nthread;
 +
 +            /* Compute fftgrid index for all atoms,
 +             * with help of some extra variables.
 +             */
 +            calc_interpolation_idx(pme, atc, start, end, thread);
 +        }
 +    }
 +#ifdef PME_TIME_THREADS
 +    c1   = omp_cyc_end(c1);
 +    cs1 += (double)c1;
 +#endif
 +
 +#ifdef PME_TIME_THREADS
 +    c2 = omp_cyc_start();
 +#endif
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +    for (thread = 0; thread < nthread; thread++)
 +    {
 +        splinedata_t *spline;
-         if (grids == NULL || grids->nthread == 1)
++        pmegrid_t *grid = NULL;
 +
 +        /* make local bsplines  */
-             grid = &grids->grid;
++        if (grids == NULL || !pme->bUseThreads)
 +        {
 +            spline = &atc->spline[0];
 +
 +            spline->n = atc->n;
 +
-             make_thread_local_ind(atc, thread, spline);
++            if (bSpread)
++            {
++                grid = &grids->grid;
++            }
 +        }
 +        else
 +        {
 +            spline = &atc->spline[thread];
 +
-             if (grids->nthread > 1)
++            if (grids->nthread == 1)
++            {
++                /* One thread, we operate on all charges */
++                spline->n = atc->n;
++            }
++            else
++            {
++                /* Get the indices our thread should operate on */
++                make_thread_local_ind(atc, thread, spline);
++            }
 +
 +            grid = &grids->grid_th[thread];
 +        }
 +
 +        if (bCalcSplines)
 +        {
 +            make_bsplines(spline->theta, spline->dtheta, pme->pme_order,
 +                          atc->fractx, spline->n, spline->ind, atc->q, pme->bFEP);
 +        }
 +
 +        if (bSpread)
 +        {
 +            /* put local atoms on grid. */
 +#ifdef PME_TIME_SPREAD
 +            ct1a = omp_cyc_start();
 +#endif
 +            spread_q_bsplines_thread(grid, atc, spline, pme->spline_work);
 +
-     if (bSpread && grids->nthread > 1)
++            if (pme->bUseThreads)
 +            {
 +                copy_local_grid(pme, grids, thread, fftgrid);
 +            }
 +#ifdef PME_TIME_SPREAD
 +            ct1a          = omp_cyc_end(ct1a);
 +            cs1a[thread] += (double)ct1a;
 +#endif
 +        }
 +    }
 +#ifdef PME_TIME_THREADS
 +    c2   = omp_cyc_end(c2);
 +    cs2 += (double)c2;
 +#endif
 +
-             /* Communicate the overlapping part of the fftgrid */
++    if (bSpread && pme->bUseThreads)
 +    {
 +#ifdef PME_TIME_THREADS
 +        c3 = omp_cyc_start();
 +#endif
 +#pragma omp parallel for num_threads(grids->nthread) schedule(static)
 +        for (thread = 0; thread < grids->nthread; thread++)
 +        {
 +            reduce_threadgrid_overlap(pme, grids, thread,
 +                                      fftgrid,
 +                                      pme->overlap[0].sendbuf,
 +                                      pme->overlap[1].sendbuf);
 +        }
 +#ifdef PME_TIME_THREADS
 +        c3   = omp_cyc_end(c3);
 +        cs3 += (double)c3;
 +#endif
 +
 +        if (pme->nnodes > 1)
 +        {
-             if (pme->nthread == 1)
++            /* Communicate the overlapping part of the fftgrid.
++             * For this communication call we need to check pme->bUseThreads
++             * to have all ranks communicate here, regardless of pme->nthread.
++             */
 +            sum_fftgrid_dd(pme, fftgrid);
 +        }
 +    }
 +
 +#ifdef PME_TIME_THREADS
 +    cnt++;
 +    if (cnt % 20 == 0)
 +    {
 +        printf("idx %.2f spread %.2f red %.2f",
 +               cs1*1e-9, cs2*1e-9, cs3*1e-9);
 +#ifdef PME_TIME_SPREAD
 +        for (thread = 0; thread < nthread; thread++)
 +        {
 +            printf(" %.2f", cs1a[thread]*1e-9);
 +        }
 +#endif
 +        printf("\n");
 +    }
 +#endif
 +}
 +
 +
 +static void dump_grid(FILE *fp,
 +                      int sx, int sy, int sz, int nx, int ny, int nz,
 +                      int my, int mz, const real *g)
 +{
 +    int x, y, z;
 +
 +    for (x = 0; x < nx; x++)
 +    {
 +        for (y = 0; y < ny; y++)
 +        {
 +            for (z = 0; z < nz; z++)
 +            {
 +                fprintf(fp, "%2d %2d %2d %6.3f\n",
 +                        sx+x, sy+y, sz+z, g[(x*my + y)*mz + z]);
 +            }
 +        }
 +    }
 +}
 +
 +static void dump_local_fftgrid(gmx_pme_t pme, const real *fftgrid)
 +{
 +    ivec local_fft_ndata, local_fft_offset, local_fft_size;
 +
 +    gmx_parallel_3dfft_real_limits(pme->pfft_setupA,
 +                                   local_fft_ndata,
 +                                   local_fft_offset,
 +                                   local_fft_size);
 +
 +    dump_grid(stderr,
 +              pme->pmegrid_start_ix,
 +              pme->pmegrid_start_iy,
 +              pme->pmegrid_start_iz,
 +              pme->pmegrid_nx-pme->pme_order+1,
 +              pme->pmegrid_ny-pme->pme_order+1,
 +              pme->pmegrid_nz-pme->pme_order+1,
 +              local_fft_size[YY],
 +              local_fft_size[ZZ],
 +              fftgrid);
 +}
 +
 +
 +void gmx_pme_calc_energy(gmx_pme_t pme, int n, rvec *x, real *q, real *V)
 +{
 +    pme_atomcomm_t *atc;
 +    pmegrids_t *grid;
 +
 +    if (pme->nnodes > 1)
 +    {
 +        gmx_incons("gmx_pme_calc_energy called in parallel");
 +    }
 +    if (pme->bFEP > 1)
 +    {
 +        gmx_incons("gmx_pme_calc_energy with free energy");
 +    }
 +
 +    atc            = &pme->atc_energy;
 +    atc->nthread   = 1;
 +    if (atc->spline == NULL)
 +    {
 +        snew(atc->spline, atc->nthread);
 +    }
 +    atc->nslab     = 1;
 +    atc->bSpread   = TRUE;
 +    atc->pme_order = pme->pme_order;
 +    atc->n         = n;
 +    pme_realloc_atomcomm_things(atc);
 +    atc->x         = x;
 +    atc->q         = q;
 +
 +    /* We only use the A-charges grid */
 +    grid = &pme->pmegridA;
 +
++    /* Only calculate the spline coefficients, don't actually spread */
 +    spread_on_grid(pme, atc, NULL, TRUE, FALSE, pme->fftgridA);
 +
 +    *V = gather_energy_bsplines(pme, grid->grid.grid, atc);
 +}
 +
 +
 +static void reset_pmeonly_counters(t_commrec *cr, gmx_wallcycle_t wcycle,
 +                                   t_nrnb *nrnb, t_inputrec *ir,
 +                                   gmx_large_int_t step)
 +{
 +    /* Reset all the counters related to performance over the run */
 +    wallcycle_stop(wcycle, ewcRUN);
 +    wallcycle_reset_all(wcycle);
 +    init_nrnb(nrnb);
 +    if (ir->nsteps >= 0)
 +    {
 +        /* ir->nsteps is not used here, but we update it for consistency */
 +        ir->nsteps -= step - ir->init_step;
 +    }
 +    ir->init_step = step;
 +    wallcycle_start(wcycle, ewcRUN);
 +}
 +
 +
 +static void gmx_pmeonly_switch(int *npmedata, gmx_pme_t **pmedata,
 +                               ivec grid_size,
 +                               t_commrec *cr, t_inputrec *ir,
 +                               gmx_pme_t *pme_ret)
 +{
 +    int ind;
 +    gmx_pme_t pme = NULL;
 +
 +    ind = 0;
 +    while (ind < *npmedata)
 +    {
 +        pme = (*pmedata)[ind];
 +        if (pme->nkx == grid_size[XX] &&
 +            pme->nky == grid_size[YY] &&
 +            pme->nkz == grid_size[ZZ])
 +        {
 +            *pme_ret = pme;
 +
 +            return;
 +        }
 +
 +        ind++;
 +    }
 +
 +    (*npmedata)++;
 +    srenew(*pmedata, *npmedata);
 +
 +    /* Generate a new PME data structure, copying part of the old pointers */
 +    gmx_pme_reinit(&((*pmedata)[ind]), cr, pme, ir, grid_size);
 +
 +    *pme_ret = (*pmedata)[ind];
 +}
 +
 +
 +int gmx_pmeonly(gmx_pme_t pme,
 +                t_commrec *cr,    t_nrnb *nrnb,
 +                gmx_wallcycle_t wcycle,
 +                real ewaldcoeff,  gmx_bool bGatherOnly,
 +                t_inputrec *ir)
 +{
 +    int npmedata;
 +    gmx_pme_t *pmedata;
 +    gmx_pme_pp_t pme_pp;
 +    int  ret;
 +    int  natoms;
 +    matrix box;
 +    rvec *x_pp      = NULL, *f_pp = NULL;
 +    real *chargeA   = NULL, *chargeB = NULL;
 +    real lambda     = 0;
 +    int  maxshift_x = 0, maxshift_y = 0;
 +    real energy, dvdlambda;
 +    matrix vir;
 +    float cycles;
 +    int  count;
 +    gmx_bool bEnerVir;
 +    gmx_large_int_t step, step_rel;
 +    ivec grid_switch;
 +
 +    /* This data will only use with PME tuning, i.e. switching PME grids */
 +    npmedata = 1;
 +    snew(pmedata, npmedata);
 +    pmedata[0] = pme;
 +
 +    pme_pp = gmx_pme_pp_init(cr);
 +
 +    init_nrnb(nrnb);
 +
 +    count = 0;
 +    do /****** this is a quasi-loop over time steps! */
 +    {
 +        /* The reason for having a loop here is PME grid tuning/switching */
 +        do
 +        {
 +            /* Domain decomposition */
 +            ret = gmx_pme_recv_q_x(pme_pp,
 +                                   &natoms,
 +                                   &chargeA, &chargeB, box, &x_pp, &f_pp,
 +                                   &maxshift_x, &maxshift_y,
 +                                   &pme->bFEP, &lambda,
 +                                   &bEnerVir,
 +                                   &step,
 +                                   grid_switch, &ewaldcoeff);
 +
 +            if (ret == pmerecvqxSWITCHGRID)
 +            {
 +                /* Switch the PME grid to grid_switch */
 +                gmx_pmeonly_switch(&npmedata, &pmedata, grid_switch, cr, ir, &pme);
 +            }
 +
 +            if (ret == pmerecvqxRESETCOUNTERS)
 +            {
 +                /* Reset the cycle and flop counters */
 +                reset_pmeonly_counters(cr, wcycle, nrnb, ir, step);
 +            }
 +        }
 +        while (ret == pmerecvqxSWITCHGRID || ret == pmerecvqxRESETCOUNTERS);
 +
 +        if (ret == pmerecvqxFINISH)
 +        {
 +            /* We should stop: break out of the loop */
 +            break;
 +        }
 +
 +        step_rel = step - ir->init_step;
 +
 +        if (count == 0)
 +        {
 +            wallcycle_start(wcycle, ewcRUN);
 +        }
 +
 +        wallcycle_start(wcycle, ewcPMEMESH);
 +
 +        dvdlambda = 0;
 +        clear_mat(vir);
 +        gmx_pme_do(pme, 0, natoms, x_pp, f_pp, chargeA, chargeB, box,
 +                   cr, maxshift_x, maxshift_y, nrnb, wcycle, vir, ewaldcoeff,
 +                   &energy, lambda, &dvdlambda,
 +                   GMX_PME_DO_ALL_F | (bEnerVir ? GMX_PME_CALC_ENER_VIR : 0));
 +
 +        cycles = wallcycle_stop(wcycle, ewcPMEMESH);
 +
 +        gmx_pme_send_force_vir_ener(pme_pp,
 +                                    f_pp, vir, energy, dvdlambda,
 +                                    cycles);
 +
 +        count++;
 +    } /***** end of quasi-loop, we stop with the break above */
 +    while (TRUE);
 +
 +    return 0;
 +}
 +
 +int gmx_pme_do(gmx_pme_t pme,
 +               int start,       int homenr,
 +               rvec x[],        rvec f[],
 +               real *chargeA,   real *chargeB,
 +               matrix box, t_commrec *cr,
 +               int  maxshift_x, int maxshift_y,
 +               t_nrnb *nrnb,    gmx_wallcycle_t wcycle,
 +               matrix vir,      real ewaldcoeff,
 +               real *energy,    real lambda,
 +               real *dvdlambda, int flags)
 +{
 +    int     q, d, i, j, ntot, npme;
 +    int     nx, ny, nz;
 +    int     n_d, local_ny;
 +    pme_atomcomm_t *atc = NULL;
 +    pmegrids_t *pmegrid = NULL;
 +    real    *grid       = NULL;
 +    real    *ptr;
 +    rvec    *x_d, *f_d;
 +    real    *charge = NULL, *q_d;
 +    real    energy_AB[2];
 +    matrix  vir_AB[2];
 +    gmx_bool bClearF;
 +    gmx_parallel_3dfft_t pfft_setup;
 +    real *  fftgrid;
 +    t_complex * cfftgrid;
 +    int     thread;
 +    const gmx_bool bCalcEnerVir = flags & GMX_PME_CALC_ENER_VIR;
 +    const gmx_bool bCalcF       = flags & GMX_PME_CALC_F;
 +
 +    assert(pme->nnodes > 0);
 +    assert(pme->nnodes == 1 || pme->ndecompdim > 0);
 +
 +    if (pme->nnodes > 1)
 +    {
 +        atc      = &pme->atc[0];
 +        atc->npd = homenr;
 +        if (atc->npd > atc->pd_nalloc)
 +        {
 +            atc->pd_nalloc = over_alloc_dd(atc->npd);
 +            srenew(atc->pd, atc->pd_nalloc);
 +        }
 +        atc->maxshift = (atc->dimind == 0 ? maxshift_x : maxshift_y);
 +    }
 +    else
 +    {
 +        /* This could be necessary for TPI */
 +        pme->atc[0].n = homenr;
 +    }
 +
 +    for (q = 0; q < (pme->bFEP ? 2 : 1); q++)
 +    {
 +        if (q == 0)
 +        {
 +            pmegrid    = &pme->pmegridA;
 +            fftgrid    = pme->fftgridA;
 +            cfftgrid   = pme->cfftgridA;
 +            pfft_setup = pme->pfft_setupA;
 +            charge     = chargeA+start;
 +        }
 +        else
 +        {
 +            pmegrid    = &pme->pmegridB;
 +            fftgrid    = pme->fftgridB;
 +            cfftgrid   = pme->cfftgridB;
 +            pfft_setup = pme->pfft_setupB;
 +            charge     = chargeB+start;
 +        }
 +        grid = pmegrid->grid.grid;
 +        /* Unpack structure */
 +        if (debug)
 +        {
 +            fprintf(debug, "PME: nnodes = %d, nodeid = %d\n",
 +                    cr->nnodes, cr->nodeid);
 +            fprintf(debug, "Grid = %p\n", (void*)grid);
 +            if (grid == NULL)
 +            {
 +                gmx_fatal(FARGS, "No grid!");
 +            }
 +        }
 +        where();
 +
 +        m_inv_ur0(box, pme->recipbox);
 +
 +        if (pme->nnodes == 1)
 +        {
 +            atc = &pme->atc[0];
 +            if (DOMAINDECOMP(cr))
 +            {
 +                atc->n = homenr;
 +                pme_realloc_atomcomm_things(atc);
 +            }
 +            atc->x = x;
 +            atc->q = charge;
 +            atc->f = f;
 +        }
 +        else
 +        {
 +            wallcycle_start(wcycle, ewcPME_REDISTXF);
 +            for (d = pme->ndecompdim-1; d >= 0; d--)
 +            {
 +                if (d == pme->ndecompdim-1)
 +                {
 +                    n_d = homenr;
 +                    x_d = x + start;
 +                    q_d = charge;
 +                }
 +                else
 +                {
 +                    n_d = pme->atc[d+1].n;
 +                    x_d = atc->x;
 +                    q_d = atc->q;
 +                }
 +                atc      = &pme->atc[d];
 +                atc->npd = n_d;
 +                if (atc->npd > atc->pd_nalloc)
 +                {
 +                    atc->pd_nalloc = over_alloc_dd(atc->npd);
 +                    srenew(atc->pd, atc->pd_nalloc);
 +                }
 +                atc->maxshift = (atc->dimind == 0 ? maxshift_x : maxshift_y);
 +                pme_calc_pidx_wrapper(n_d, pme->recipbox, x_d, atc);
 +                where();
 +
 +                /* Redistribute x (only once) and qA or qB */
 +                if (DOMAINDECOMP(cr))
 +                {
 +                    dd_pmeredist_x_q(pme, n_d, q == 0, x_d, q_d, atc);
 +                }
 +                else
 +                {
 +                    pmeredist_pd(pme, TRUE, n_d, q == 0, x_d, q_d, atc);
 +                }
 +            }
 +            where();
 +
 +            wallcycle_stop(wcycle, ewcPME_REDISTXF);
 +        }
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "Node= %6d, pme local particles=%6d\n",
 +                    cr->nodeid, atc->n);
 +        }
 +
 +        if (flags & GMX_PME_SPREAD_Q)
 +        {
 +            wallcycle_start(wcycle, ewcPME_SPREADGATHER);
 +
 +            /* Spread the charges on a grid */
 +            spread_on_grid(pme, &pme->atc[0], pmegrid, q == 0, TRUE, fftgrid);
 +
 +            if (q == 0)
 +            {
 +                inc_nrnb(nrnb, eNR_WEIGHTS, DIM*atc->n);
 +            }
 +            inc_nrnb(nrnb, eNR_SPREADQBSP,
 +                     pme->pme_order*pme->pme_order*pme->pme_order*atc->n);
 +
++            if (!pme->bUseThreads)
 +            {
 +                wrap_periodic_pmegrid(pme, grid);
 +
 +                /* sum contributions to local grid from other nodes */
 +#ifdef GMX_MPI
 +                if (pme->nnodes > 1)
 +                {
 +                    gmx_sum_qgrid_dd(pme, grid, GMX_SUM_QGRID_FORWARD);
 +                    where();
 +                }
 +#endif
 +
 +                copy_pmegrid_to_fftgrid(pme, grid, fftgrid);
 +            }
 +
 +            wallcycle_stop(wcycle, ewcPME_SPREADGATHER);
 +
 +            /*
 +               dump_local_fftgrid(pme,fftgrid);
 +               exit(0);
 +             */
 +        }
 +
 +        /* Here we start a large thread parallel region */
 +#pragma omp parallel num_threads(pme->nthread) private(thread)
 +        {
 +            thread = gmx_omp_get_thread_num();
 +            if (flags & GMX_PME_SOLVE)
 +            {
 +                int loop_count;
 +
 +                /* do 3d-fft */
 +                if (thread == 0)
 +                {
 +                    wallcycle_start(wcycle, ewcPME_FFT);
 +                }
 +                gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_REAL_TO_COMPLEX,
 +                                           fftgrid, cfftgrid, thread, wcycle);
 +                if (thread == 0)
 +                {
 +                    wallcycle_stop(wcycle, ewcPME_FFT);
 +                }
 +                where();
 +
 +                /* solve in k-space for our local cells */
 +                if (thread == 0)
 +                {
 +                    wallcycle_start(wcycle, ewcPME_SOLVE);
 +                }
 +                loop_count =
 +                    solve_pme_yzx(pme, cfftgrid, ewaldcoeff,
 +                                  box[XX][XX]*box[YY][YY]*box[ZZ][ZZ],
 +                                  bCalcEnerVir,
 +                                  pme->nthread, thread);
 +                if (thread == 0)
 +                {
 +                    wallcycle_stop(wcycle, ewcPME_SOLVE);
 +                    where();
 +                    inc_nrnb(nrnb, eNR_SOLVEPME, loop_count);
 +                }
 +            }
 +
 +            if (bCalcF)
 +            {
 +                /* do 3d-invfft */
 +                if (thread == 0)
 +                {
 +                    where();
 +                    wallcycle_start(wcycle, ewcPME_FFT);
 +                }
 +                gmx_parallel_3dfft_execute(pfft_setup, GMX_FFT_COMPLEX_TO_REAL,
 +                                           cfftgrid, fftgrid, thread, wcycle);
 +                if (thread == 0)
 +                {
 +                    wallcycle_stop(wcycle, ewcPME_FFT);
 +
 +                    where();
 +
 +                    if (pme->nodeid == 0)
 +                    {
 +                        ntot  = pme->nkx*pme->nky*pme->nkz;
 +                        npme  = ntot*log((real)ntot)/log(2.0);
 +                        inc_nrnb(nrnb, eNR_FFT, 2*npme);
 +                    }
 +
 +                    wallcycle_start(wcycle, ewcPME_SPREADGATHER);
 +                }
 +
 +                copy_fftgrid_to_pmegrid(pme, fftgrid, grid, pme->nthread, thread);
 +            }
 +        }
 +        /* End of thread parallel section.
 +         * With MPI we have to synchronize here before gmx_sum_qgrid_dd.
 +         */
 +
 +        if (bCalcF)
 +        {
 +            /* distribute local grid to all nodes */
 +#ifdef GMX_MPI
 +            if (pme->nnodes > 1)
 +            {
 +                gmx_sum_qgrid_dd(pme, grid, GMX_SUM_QGRID_BACKWARD);
 +            }
 +#endif
 +            where();
 +
 +            unwrap_periodic_pmegrid(pme, grid);
 +
 +            /* interpolate forces for our local atoms */
 +
 +            where();
 +
 +            /* If we are running without parallelization,
 +             * atc->f is the actual force array, not a buffer,
 +             * therefore we should not clear it.
 +             */
 +            bClearF = (q == 0 && PAR(cr));
 +#pragma omp parallel for num_threads(pme->nthread) schedule(static)
 +            for (thread = 0; thread < pme->nthread; thread++)
 +            {
 +                gather_f_bsplines(pme, grid, bClearF, atc,
 +                                  &atc->spline[thread],
 +                                  pme->bFEP ? (q == 0 ? 1.0-lambda : lambda) : 1.0);
 +            }
 +
 +            where();
 +
 +            inc_nrnb(nrnb, eNR_GATHERFBSP,
 +                     pme->pme_order*pme->pme_order*pme->pme_order*pme->atc[0].n);
 +            wallcycle_stop(wcycle, ewcPME_SPREADGATHER);
 +        }
 +
 +        if (bCalcEnerVir)
 +        {
 +            /* This should only be called on the master thread
 +             * and after the threads have synchronized.
 +             */
 +            get_pme_ener_vir(pme, pme->nthread, &energy_AB[q], vir_AB[q]);
 +        }
 +    } /* of q-loop */
 +
 +    if (bCalcF && pme->nnodes > 1)
 +    {
 +        wallcycle_start(wcycle, ewcPME_REDISTXF);
 +        for (d = 0; d < pme->ndecompdim; d++)
 +        {
 +            atc = &pme->atc[d];
 +            if (d == pme->ndecompdim - 1)
 +            {
 +                n_d = homenr;
 +                f_d = f + start;
 +            }
 +            else
 +            {
 +                n_d = pme->atc[d+1].n;
 +                f_d = pme->atc[d+1].f;
 +            }
 +            if (DOMAINDECOMP(cr))
 +            {
 +                dd_pmeredist_f(pme, atc, n_d, f_d,
 +                               d == pme->ndecompdim-1 && pme->bPPnode);
 +            }
 +            else
 +            {
 +                pmeredist_pd(pme, FALSE, n_d, TRUE, f_d, NULL, atc);
 +            }
 +        }
 +
 +        wallcycle_stop(wcycle, ewcPME_REDISTXF);
 +    }
 +    where();
 +
 +    if (bCalcEnerVir)
 +    {
 +        if (!pme->bFEP)
 +        {
 +            *energy = energy_AB[0];
 +            m_add(vir, vir_AB[0], vir);
 +        }
 +        else
 +        {
 +            *energy     = (1.0-lambda)*energy_AB[0] + lambda*energy_AB[1];
 +            *dvdlambda += energy_AB[1] - energy_AB[0];
 +            for (i = 0; i < DIM; i++)
 +            {
 +                for (j = 0; j < DIM; j++)
 +                {
 +                    vir[i][j] += (1.0-lambda)*vir_AB[0][i][j] +
 +                        lambda*vir_AB[1][i][j];
 +                }
 +            }
 +        }
 +    }
 +    else
 +    {
 +        *energy = 0;
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "PME mesh energy: %g\n", *energy);
 +    }
 +
 +    return 0;
 +}
index 1f10a1796d7397ab09c680d2ae621ab420e7ebea,0000000000000000000000000000000000000000..fb6c2764160bd8de226eb25aa63e0f147dcc7a8a
mode 100644,000000..100644
--- /dev/null
@@@ -1,4071 -1,0 +1,4092 @@@
-                            gmx_bool bOutputCenters)
 +/*
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2008, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include "domdec.h"
 +#include "gmx_wallcycle.h"
 +#include "gmx_cyclecounter.h"
 +#include "trnio.h"
 +#include "smalloc.h"
 +#include "network.h"
 +#include "pbc.h"
 +#include "futil.h"
 +#include "mdrun.h"
 +#include "txtdump.h"
 +#include "names.h"
 +#include "mtop_util.h"
 +#include "names.h"
 +#include "nrjac.h"
 +#include "vec.h"
 +#include "gmx_ga2la.h"
 +#include "xvgr.h"
 +#include "gmxfio.h"
 +#include "groupcoord.h"
 +#include "pull_rotation.h"
 +#include "gmx_sort.h"
 +#include "copyrite.h"
 +#include "macros.h"
 +
 +
 +static char *RotStr = {"Enforced rotation:"};
 +
 +
 +/* Set the minimum weight for the determination of the slab centers */
 +#define WEIGHT_MIN (10*GMX_FLOAT_MIN)
 +
 +/* Helper structure for sorting positions along rotation vector             */
 +typedef struct {
 +    real xcproj;            /* Projection of xc on the rotation vector        */
 +    int  ind;               /* Index of xc                                    */
 +    real m;                 /* Mass                                           */
 +    rvec x;                 /* Position                                       */
 +    rvec x_ref;             /* Reference position                             */
 +} sort_along_vec_t;
 +
 +
 +/* Enforced rotation / flexible: determine the angle of each slab             */
 +typedef struct gmx_slabdata
 +{
 +    int   nat;              /* Number of atoms belonging to this slab         */
 +    rvec *x;                /* The positions belonging to this slab. In
 +                               general, this should be all positions of the
 +                               whole rotation group, but we leave those away
 +                               that have a small enough weight                */
 +    rvec *ref;              /* Same for reference                             */
 +    real *weight;           /* The weight for each atom                       */
 +} t_gmx_slabdata;
 +
 +
 +/* Helper structure for potential fitting */
 +typedef struct gmx_potfit
 +{
 +    real   *degangle;       /* Set of angles for which the potential is
 +                               calculated. The optimum fit is determined as
 +                               the angle for with the potential is minimal    */
 +    real   *V;              /* Potential for the different angles             */
 +    matrix *rotmat;         /* Rotation matrix corresponding to the angles    */
 +} t_gmx_potfit;
 +
 +
 +/* Enforced rotation data for all groups                                      */
 +typedef struct gmx_enfrot
 +{
 +    FILE             *out_rot;     /* Output file for rotation data                  */
 +    FILE             *out_torque;  /* Output file for torque data                    */
 +    FILE             *out_angles;  /* Output file for slab angles for flexible type  */
 +    FILE             *out_slabs;   /* Output file for slab centers                   */
 +    int               bufsize;     /* Allocation size of buf                         */
 +    rvec             *xbuf;        /* Coordinate buffer variable for sorting         */
 +    real             *mbuf;        /* Masses buffer variable for sorting             */
 +    sort_along_vec_t *data;        /* Buffer variable needed for position sorting    */
 +    real             *mpi_inbuf;   /* MPI buffer                                     */
 +    real             *mpi_outbuf;  /* MPI buffer                                     */
 +    int               mpi_bufsize; /* Allocation size of in & outbuf                 */
 +    unsigned long     Flags;       /* mdrun flags                                    */
 +    gmx_bool          bOut;        /* Used to skip first output when appending to
 +                                    * avoid duplicate entries in rotation outfiles   */
 +} t_gmx_enfrot;
 +
 +
 +/* Global enforced rotation data for a single rotation group                  */
 +typedef struct gmx_enfrotgrp
 +{
 +    real     degangle;      /* Rotation angle in degrees                      */
 +    matrix   rotmat;        /* Rotation matrix                                */
 +    atom_id *ind_loc;       /* Local rotation indices                         */
 +    int      nat_loc;       /* Number of local group atoms                    */
 +    int      nalloc_loc;    /* Allocation size for ind_loc and weight_loc     */
 +
 +    real     V;             /* Rotation potential for this rotation group     */
 +    rvec    *f_rot_loc;     /* Array to store the forces on the local atoms
 +                               resulting from enforced rotation potential     */
 +
 +    /* Collective coordinates for the whole rotation group */
 +    real  *xc_ref_length;   /* Length of each x_rotref vector after x_rotref
 +                               has been put into origin                       */
 +    int   *xc_ref_ind;      /* Position of each local atom in the collective
 +                               array                                          */
 +    rvec   xc_center;       /* Center of the rotation group positions, may
 +                               be mass weighted                               */
 +    rvec   xc_ref_center;   /* dito, for the reference positions              */
 +    rvec  *xc;              /* Current (collective) positions                 */
 +    ivec  *xc_shifts;       /* Current (collective) shifts                    */
 +    ivec  *xc_eshifts;      /* Extra shifts since last DD step                */
 +    rvec  *xc_old;          /* Old (collective) positions                     */
 +    rvec  *xc_norm;         /* Normalized form of the current positions       */
 +    rvec  *xc_ref_sorted;   /* Reference positions (sorted in the same order
 +                               as xc when sorted)                             */
 +    int   *xc_sortind;      /* Where is a position found after sorting?       */
 +    real  *mc;              /* Collective masses                              */
 +    real  *mc_sorted;
 +    real   invmass;         /* one over the total mass of the rotation group  */
 +
 +    real   torque_v;        /* Torque in the direction of rotation vector     */
 +    real   angle_v;         /* Actual angle of the whole rotation group       */
 +    /* Fixed rotation only */
 +    real   weight_v;        /* Weights for angle determination                */
 +    rvec  *xr_loc;          /* Local reference coords, correctly rotated      */
 +    rvec  *x_loc_pbc;       /* Local current coords, correct PBC image        */
 +    real  *m_loc;           /* Masses of the current local atoms              */
 +
 +    /* Flexible rotation only */
 +    int    nslabs_alloc;              /* For this many slabs memory is allocated        */
 +    int    slab_first;                /* Lowermost slab for that the calculation needs
 +                                         to be performed at a given time step           */
 +    int    slab_last;                 /* Uppermost slab ...                             */
 +    int    slab_first_ref;            /* First slab for which ref. center is stored     */
 +    int    slab_last_ref;             /* Last ...                                       */
 +    int    slab_buffer;               /* Slab buffer region around reference slabs      */
 +    int   *firstatom;                 /* First relevant atom for a slab                 */
 +    int   *lastatom;                  /* Last relevant atom for a slab                  */
 +    rvec  *slab_center;               /* Gaussian-weighted slab center                  */
 +    rvec  *slab_center_ref;           /* Gaussian-weighted slab center for the
 +                                         reference positions                            */
 +    real  *slab_weights;              /* Sum of gaussian weights in a slab              */
 +    real  *slab_torque_v;             /* Torque T = r x f for each slab.                */
 +                                      /* torque_v = m.v = angular momentum in the
 +                                         direction of v                                 */
 +    real  max_beta;                   /* min_gaussian from inputrec->rotgrp is the
 +                                         minimum value the gaussian must have so that
 +                                         the force is actually evaluated max_beta is
 +                                         just another way to put it                     */
 +    real           *gn_atom;          /* Precalculated gaussians for a single atom      */
 +    int            *gn_slabind;       /* Tells to which slab each precalculated gaussian
 +                                         belongs                                        */
 +    rvec           *slab_innersumvec; /* Inner sum of the flexible2 potential per slab;
 +                                         this is precalculated for optimization reasons */
 +    t_gmx_slabdata *slab_data;        /* Holds atom positions and gaussian weights
 +                                         of atoms belonging to a slab                   */
 +
 +    /* For potential fits with varying angle: */
 +    t_gmx_potfit *PotAngleFit;  /* Used for fit type 'potential'              */
 +} t_gmx_enfrotgrp;
 +
 +
 +/* Activate output of forces for correctness checks */
 +/* #define PRINT_FORCES */
 +#ifdef PRINT_FORCES
 +#define PRINT_FORCE_J  fprintf(stderr, "f%d = %15.8f %15.8f %15.8f\n", erg->xc_ref_ind[j], erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ]);
 +#define PRINT_POT_TAU  if (MASTER(cr)) { \
 +        fprintf(stderr, "potential = %15.8f\n" "torque    = %15.8f\n", erg->V, erg->torque_v); \
 +}
 +#else
 +#define PRINT_FORCE_J
 +#define PRINT_POT_TAU
 +#endif
 +
 +/* Shortcuts for often used queries */
 +#define ISFLEX(rg) ( (rg->eType == erotgFLEX) || (rg->eType == erotgFLEXT) || (rg->eType == erotgFLEX2) || (rg->eType == erotgFLEX2T) )
 +#define ISCOLL(rg) ( (rg->eType == erotgFLEX) || (rg->eType == erotgFLEXT) || (rg->eType == erotgFLEX2) || (rg->eType == erotgFLEX2T) || (rg->eType == erotgRMPF) || (rg->eType == erotgRM2PF) )
 +
 +
 +/* Does any of the rotation groups use slab decomposition? */
 +static gmx_bool HaveFlexibleGroups(t_rot *rot)
 +{
 +    int       g;
 +    t_rotgrp *rotg;
 +
 +
 +    for (g = 0; g < rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        if (ISFLEX(rotg))
 +        {
 +            return TRUE;
 +        }
 +    }
 +
 +    return FALSE;
 +}
 +
 +
 +/* Is for any group the fit angle determined by finding the minimum of the
 + * rotation potential? */
 +static gmx_bool HavePotFitGroups(t_rot *rot)
 +{
 +    int       g;
 +    t_rotgrp *rotg;
 +
 +
 +    for (g = 0; g < rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        if (erotgFitPOT == rotg->eFittype)
 +        {
 +            return TRUE;
 +        }
 +    }
 +
 +    return FALSE;
 +}
 +
 +
 +static double** allocate_square_matrix(int dim)
 +{
 +    int      i;
 +    double** mat = NULL;
 +
 +
 +    snew(mat, dim);
 +    for (i = 0; i < dim; i++)
 +    {
 +        snew(mat[i], dim);
 +    }
 +
 +    return mat;
 +}
 +
 +
 +static void free_square_matrix(double** mat, int dim)
 +{
 +    int i;
 +
 +
 +    for (i = 0; i < dim; i++)
 +    {
 +        sfree(mat[i]);
 +    }
 +    sfree(mat);
 +}
 +
 +
 +/* Return the angle for which the potential is minimal */
 +static real get_fitangle(t_rotgrp *rotg, gmx_enfrotgrp_t erg)
 +{
 +    int           i;
 +    real          fitangle = -999.9;
 +    real          pot_min  = GMX_FLOAT_MAX;
 +    t_gmx_potfit *fit;
 +
 +
 +    fit = erg->PotAngleFit;
 +
 +    for (i = 0; i < rotg->PotAngle_nstep; i++)
 +    {
 +        if (fit->V[i] < pot_min)
 +        {
 +            pot_min  = fit->V[i];
 +            fitangle = fit->degangle[i];
 +        }
 +    }
 +
 +    return fitangle;
 +}
 +
 +
 +/* Reduce potential angle fit data for this group at this time step? */
 +static gmx_inline gmx_bool bPotAngle(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
 +{
 +    return ( (erotgFitPOT == rotg->eFittype) && (do_per_step(step, rot->nstsout) || do_per_step(step, rot->nstrout)) );
 +}
 +
 +/* Reduce slab torqe data for this group at this time step? */
 +static gmx_inline gmx_bool bSlabTau(t_rot *rot, t_rotgrp *rotg, gmx_large_int_t step)
 +{
 +    return ( (ISFLEX(rotg)) && do_per_step(step, rot->nstsout) );
 +}
 +
 +/* Output rotation energy, torques, etc. for each rotation group */
 +static void reduce_output(t_commrec *cr, t_rot *rot, real t, gmx_large_int_t step)
 +{
 +    int             g, i, islab, nslabs = 0;
 +    int             count; /* MPI element counter                               */
 +    t_rotgrp       *rotg;
 +    gmx_enfrot_t    er;    /* Pointer to the enforced rotation buffer variables */
 +    gmx_enfrotgrp_t erg;   /* Pointer to enforced rotation group data           */
 +    real            fitangle;
 +    gmx_bool        bFlex;
 +
 +
 +    er = rot->enfrot;
 +
 +    /* Fill the MPI buffer with stuff to reduce. If items are added for reduction
 +     * here, the MPI buffer size has to be enlarged also in calc_mpi_bufsize() */
 +    if (PAR(cr))
 +    {
 +        count = 0;
 +        for (g = 0; g < rot->ngrp; g++)
 +        {
 +            rotg                   = &rot->grp[g];
 +            erg                    = rotg->enfrotgrp;
 +            nslabs                 = erg->slab_last - erg->slab_first + 1;
 +            er->mpi_inbuf[count++] = erg->V;
 +            er->mpi_inbuf[count++] = erg->torque_v;
 +            er->mpi_inbuf[count++] = erg->angle_v;
 +            er->mpi_inbuf[count++] = erg->weight_v; /* weights are not needed for flex types, but this is just a single value */
 +
 +            if (bPotAngle(rot, rotg, step))
 +            {
 +                for (i = 0; i < rotg->PotAngle_nstep; i++)
 +                {
 +                    er->mpi_inbuf[count++] = erg->PotAngleFit->V[i];
 +                }
 +            }
 +            if (bSlabTau(rot, rotg, step))
 +            {
 +                for (i = 0; i < nslabs; i++)
 +                {
 +                    er->mpi_inbuf[count++] = erg->slab_torque_v[i];
 +                }
 +            }
 +        }
 +        if (count > er->mpi_bufsize)
 +        {
 +            gmx_fatal(FARGS, "%s MPI buffer overflow, please report this error.", RotStr);
 +        }
 +
 +#ifdef GMX_MPI
 +        MPI_Reduce(er->mpi_inbuf, er->mpi_outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
 +#endif
 +
 +        /* Copy back the reduced data from the buffer on the master */
 +        if (MASTER(cr))
 +        {
 +            count = 0;
 +            for (g = 0; g < rot->ngrp; g++)
 +            {
 +                rotg          = &rot->grp[g];
 +                erg           = rotg->enfrotgrp;
 +                nslabs        = erg->slab_last - erg->slab_first + 1;
 +                erg->V        = er->mpi_outbuf[count++];
 +                erg->torque_v = er->mpi_outbuf[count++];
 +                erg->angle_v  = er->mpi_outbuf[count++];
 +                erg->weight_v = er->mpi_outbuf[count++];
 +
 +                if (bPotAngle(rot, rotg, step))
 +                {
 +                    for (i = 0; i < rotg->PotAngle_nstep; i++)
 +                    {
 +                        erg->PotAngleFit->V[i] = er->mpi_outbuf[count++];
 +                    }
 +                }
 +                if (bSlabTau(rot, rotg, step))
 +                {
 +                    for (i = 0; i < nslabs; i++)
 +                    {
 +                        erg->slab_torque_v[i] = er->mpi_outbuf[count++];
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Output */
 +    if (MASTER(cr))
 +    {
 +        /* Angle and torque for each rotation group */
 +        for (g = 0; g < rot->ngrp; g++)
 +        {
 +            rotg  = &rot->grp[g];
 +            bFlex = ISFLEX(rotg);
 +
 +            erg = rotg->enfrotgrp;
 +
 +            /* Output to main rotation output file: */
 +            if (do_per_step(step, rot->nstrout) )
 +            {
 +                if (erotgFitPOT == rotg->eFittype)
 +                {
 +                    fitangle = get_fitangle(rotg, erg);
 +                }
 +                else
 +                {
 +                    if (bFlex)
 +                    {
 +                        fitangle = erg->angle_v; /* RMSD fit angle */
 +                    }
 +                    else
 +                    {
 +                        fitangle = (erg->angle_v/erg->weight_v)*180.0*M_1_PI;
 +                    }
 +                }
 +                fprintf(er->out_rot, "%12.4f", fitangle);
 +                fprintf(er->out_rot, "%12.3e", erg->torque_v);
 +                fprintf(er->out_rot, "%12.3e", erg->V);
 +            }
 +
 +            if (do_per_step(step, rot->nstsout) )
 +            {
 +                /* Output to torque log file: */
 +                if (bFlex)
 +                {
 +                    fprintf(er->out_torque, "%12.3e%6d", t, g);
 +                    for (i = erg->slab_first; i <= erg->slab_last; i++)
 +                    {
 +                        islab = i - erg->slab_first;  /* slab index */
 +                        /* Only output if enough weight is in slab */
 +                        if (erg->slab_weights[islab] > rotg->min_gaussian)
 +                        {
 +                            fprintf(er->out_torque, "%6d%12.3e", i, erg->slab_torque_v[islab]);
 +                        }
 +                    }
 +                    fprintf(er->out_torque, "\n");
 +                }
 +
 +                /* Output to angles log file: */
 +                if (erotgFitPOT == rotg->eFittype)
 +                {
 +                    fprintf(er->out_angles, "%12.3e%6d%12.4f", t, g, erg->degangle);
 +                    /* Output energies at a set of angles around the reference angle */
 +                    for (i = 0; i < rotg->PotAngle_nstep; i++)
 +                    {
 +                        fprintf(er->out_angles, "%12.3e", erg->PotAngleFit->V[i]);
 +                    }
 +                    fprintf(er->out_angles, "\n");
 +                }
 +            }
 +        }
 +        if (do_per_step(step, rot->nstrout) )
 +        {
 +            fprintf(er->out_rot, "\n");
 +        }
 +    }
 +}
 +
 +
 +/* Add the forces from enforced rotation potential to the local forces.
 + * Should be called after the SR forces have been evaluated */
 +extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, gmx_large_int_t step, real t)
 +{
 +    int             g, l, ii;
 +    t_rotgrp       *rotg;
 +    gmx_enfrot_t    er;         /* Pointer to the enforced rotation buffer variables */
 +    gmx_enfrotgrp_t erg;        /* Pointer to enforced rotation group data           */
 +    real            Vrot = 0.0; /* If more than one rotation group is present, Vrot
 +                                   assembles the local parts from all groups         */
 +
 +
 +    er = rot->enfrot;
 +
 +    /* Loop over enforced rotation groups (usually 1, though)
 +     * Apply the forces from rotation potentials */
 +    for (g = 0; g < rot->ngrp; g++)
 +    {
 +        rotg  = &rot->grp[g];
 +        erg   = rotg->enfrotgrp;
 +        Vrot += erg->V;  /* add the local parts from the nodes */
 +        for (l = 0; l < erg->nat_loc; l++)
 +        {
 +            /* Get the right index of the local force */
 +            ii = erg->ind_loc[l];
 +            /* Add */
 +            rvec_inc(f[ii], erg->f_rot_loc[l]);
 +        }
 +    }
 +
 +    /* Reduce energy,torque, angles etc. to get the sum values (per rotation group)
 +     * on the master and output these values to file. */
 +    if ( (do_per_step(step, rot->nstrout) || do_per_step(step, rot->nstsout)) && er->bOut)
 +    {
 +        reduce_output(cr, rot, t, step);
 +    }
 +
 +    /* When appending, er->bOut is FALSE the first time to avoid duplicate entries */
 +    er->bOut = TRUE;
 +
 +    PRINT_POT_TAU
 +
 +    return Vrot;
 +}
 +
 +
 +/* The Gaussian norm is chosen such that the sum of the gaussian functions
 + * over the slabs is approximately 1.0 everywhere */
 +#define GAUSS_NORM   0.569917543430618
 +
 +
 +/* Calculate the maximum beta that leads to a gaussian larger min_gaussian,
 + * also does some checks
 + */
 +static double calc_beta_max(real min_gaussian, real slab_dist)
 +{
 +    double sigma;
 +    double arg;
 +
 +
 +    /* Actually the next two checks are already made in grompp */
 +    if (slab_dist <= 0)
 +    {
 +        gmx_fatal(FARGS, "Slab distance of flexible rotation groups must be >=0 !");
 +    }
 +    if (min_gaussian <= 0)
 +    {
 +        gmx_fatal(FARGS, "Cutoff value for Gaussian must be > 0. (You requested %f)");
 +    }
 +
 +    /* Define the sigma value */
 +    sigma = 0.7*slab_dist;
 +
 +    /* Calculate the argument for the logarithm and check that the log() result is negative or 0 */
 +    arg = min_gaussian/GAUSS_NORM;
 +    if (arg > 1.0)
 +    {
 +        gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", GAUSS_NORM);
 +    }
 +
 +    return sqrt(-2.0*sigma*sigma*log(min_gaussian/GAUSS_NORM));
 +}
 +
 +
 +static gmx_inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
 +{
 +    return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
 +}
 +
 +
 +static gmx_inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
 +{
 +    const real norm = GAUSS_NORM;
 +    real       sigma;
 +
 +
 +    /* Define the sigma value */
 +    sigma = 0.7*rotg->slab_dist;
 +    /* Calculate the Gaussian value of slab n for position curr_x */
 +    return norm * exp( -0.5 * sqr( calc_beta(curr_x, rotg, n)/sigma ) );
 +}
 +
 +
 +/* Returns the weight in a single slab, also calculates the Gaussian- and mass-
 + * weighted sum of positions for that slab */
 +static real get_slab_weight(int j, t_rotgrp *rotg, rvec xc[], real mc[], rvec *x_weighted_sum)
 +{
 +    rvec            curr_x;           /* The position of an atom                      */
 +    rvec            curr_x_weighted;  /* The gaussian-weighted position               */
 +    real            gaussian;         /* A single gaussian weight                     */
 +    real            wgauss;           /* gaussian times current mass                  */
 +    real            slabweight = 0.0; /* The sum of weights in the slab               */
 +    int             i, islab;
 +    gmx_enfrotgrp_t erg;              /* Pointer to enforced rotation group data      */
 +
 +
 +    erg = rotg->enfrotgrp;
 +    clear_rvec(*x_weighted_sum);
 +
 +    /* Slab index */
 +    islab = j - erg->slab_first;
 +
 +    /* Loop over all atoms in the rotation group */
 +    for (i = 0; i < rotg->nat; i++)
 +    {
 +        copy_rvec(xc[i], curr_x);
 +        gaussian = gaussian_weight(curr_x, rotg, j);
 +        wgauss   = gaussian * mc[i];
 +        svmul(wgauss, curr_x, curr_x_weighted);
 +        rvec_add(*x_weighted_sum, curr_x_weighted, *x_weighted_sum);
 +        slabweight += wgauss;
 +    }  /* END of loop over rotation group atoms */
 +
 +    return slabweight;
 +}
 +
 +
 +static void get_slab_centers(
 +        t_rotgrp  *rotg,       /* The rotation group information               */
 +        rvec      *xc,         /* The rotation group positions; will
 +                                  typically be enfrotgrp->xc, but at first call
 +                                  it is enfrotgrp->xc_ref                      */
 +        real      *mc,         /* The masses of the rotation group atoms       */
 +        int        g,          /* The number of the rotation group             */
 +        real       time,       /* Used for output only                         */
 +        FILE      *out_slabs,  /* For outputting center per slab information   */
 +        gmx_bool   bOutStep,   /* Is this an output step?                      */
 +        gmx_bool   bReference) /* If this routine is called from
 +                                  init_rot_group we need to store
 +                                  the reference slab centers                   */
 +{
 +    int             j, islab;
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* Loop over slabs */
 +    for (j = erg->slab_first; j <= erg->slab_last; j++)
 +    {
 +        islab                    = j - erg->slab_first;
 +        erg->slab_weights[islab] = get_slab_weight(j, rotg, xc, mc, &erg->slab_center[islab]);
 +
 +        /* We can do the calculations ONLY if there is weight in the slab! */
 +        if (erg->slab_weights[islab] > WEIGHT_MIN)
 +        {
 +            svmul(1.0/erg->slab_weights[islab], erg->slab_center[islab], erg->slab_center[islab]);
 +        }
 +        else
 +        {
 +            /* We need to check this here, since we divide through slab_weights
 +             * in the flexible low-level routines! */
 +            gmx_fatal(FARGS, "Not enough weight in slab %d. Slab center cannot be determined!", j);
 +        }
 +
 +        /* At first time step: save the centers of the reference structure */
 +        if (bReference)
 +        {
 +            copy_rvec(erg->slab_center[islab], erg->slab_center_ref[islab]);
 +        }
 +    } /* END of loop over slabs */
 +
 +    /* Output on the master */
 +    if ( (NULL != out_slabs) && bOutStep)
 +    {
 +        fprintf(out_slabs, "%12.3e%6d", time, g);
 +        for (j = erg->slab_first; j <= erg->slab_last; j++)
 +        {
 +            islab = j - erg->slab_first;
 +            fprintf(out_slabs, "%6d%12.3e%12.3e%12.3e",
 +                    j, erg->slab_center[islab][XX], erg->slab_center[islab][YY], erg->slab_center[islab][ZZ]);
 +        }
 +        fprintf(out_slabs, "\n");
 +    }
 +}
 +
 +
 +static void calc_rotmat(
 +        rvec   vec,
 +        real   degangle,      /* Angle alpha of rotation at time t in degrees       */
 +        matrix rotmat)        /* Rotation matrix                                    */
 +{
 +    real radangle;            /* Rotation angle in radians */
 +    real cosa;                /* cosine alpha              */
 +    real sina;                /* sine alpha                */
 +    real OMcosa;              /* 1 - cos(alpha)            */
 +    real dumxy, dumxz, dumyz; /* save computations         */
 +    rvec rot_vec;             /* Rotate around rot_vec ... */
 +
 +
 +    radangle = degangle * M_PI/180.0;
 +    copy_rvec(vec, rot_vec );
 +
 +    /* Precompute some variables: */
 +    cosa   = cos(radangle);
 +    sina   = sin(radangle);
 +    OMcosa = 1.0 - cosa;
 +    dumxy  = rot_vec[XX]*rot_vec[YY]*OMcosa;
 +    dumxz  = rot_vec[XX]*rot_vec[ZZ]*OMcosa;
 +    dumyz  = rot_vec[YY]*rot_vec[ZZ]*OMcosa;
 +
 +    /* Construct the rotation matrix for this rotation group: */
 +    /* 1st column: */
 +    rotmat[XX][XX] = cosa  + rot_vec[XX]*rot_vec[XX]*OMcosa;
 +    rotmat[YY][XX] = dumxy + rot_vec[ZZ]*sina;
 +    rotmat[ZZ][XX] = dumxz - rot_vec[YY]*sina;
 +    /* 2nd column: */
 +    rotmat[XX][YY] = dumxy - rot_vec[ZZ]*sina;
 +    rotmat[YY][YY] = cosa  + rot_vec[YY]*rot_vec[YY]*OMcosa;
 +    rotmat[ZZ][YY] = dumyz + rot_vec[XX]*sina;
 +    /* 3rd column: */
 +    rotmat[XX][ZZ] = dumxz + rot_vec[YY]*sina;
 +    rotmat[YY][ZZ] = dumyz - rot_vec[XX]*sina;
 +    rotmat[ZZ][ZZ] = cosa  + rot_vec[ZZ]*rot_vec[ZZ]*OMcosa;
 +
 +#ifdef PRINTMATRIX
 +    int iii, jjj;
 +
 +    for (iii = 0; iii < 3; iii++)
 +    {
 +        for (jjj = 0; jjj < 3; jjj++)
 +        {
 +            fprintf(stderr, " %10.8f ",  rotmat[iii][jjj]);
 +        }
 +        fprintf(stderr, "\n");
 +    }
 +#endif
 +}
 +
 +
 +/* Calculates torque on the rotation axis tau = position x force */
 +static gmx_inline real torque(
 +        rvec rotvec,  /* rotation vector; MUST be normalized!                 */
 +        rvec force,   /* force                                                */
 +        rvec x,       /* position of atom on which the force acts             */
 +        rvec pivot)   /* pivot point of rotation axis                         */
 +{
 +    rvec vectmp, tau;
 +
 +
 +    /* Subtract offset */
 +    rvec_sub(x, pivot, vectmp);
 +
 +    /* position x force */
 +    cprod(vectmp, force, tau);
 +
 +    /* Return the part of the torque which is parallel to the rotation vector */
 +    return iprod(tau, rotvec);
 +}
 +
 +
 +/* Right-aligned output of value with standard width */
 +static void print_aligned(FILE *fp, char *str)
 +{
 +    fprintf(fp, "%12s", str);
 +}
 +
 +
 +/* Right-aligned output of value with standard short width */
 +static void print_aligned_short(FILE *fp, char *str)
 +{
 +    fprintf(fp, "%6s", str);
 +}
 +
 +
 +static FILE *open_output_file(const char *fn, int steps, const char what[])
 +{
 +    FILE *fp;
 +
 +
 +    fp = ffopen(fn, "w");
 +
 +    fprintf(fp, "# Output of %s is written in intervals of %d time step%s.\n#\n",
 +            what, steps, steps > 1 ? "s" : "");
 +
 +    return fp;
 +}
 +
 +
 +/* Open output file for slab center data. Call on master only */
 +static FILE *open_slab_out(const char *fn, t_rot *rot, const output_env_t oenv)
 +{
 +    FILE      *fp;
 +    int        g, i;
 +    t_rotgrp  *rotg;
 +
 +
 +    if (rot->enfrot->Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn, "a");
 +    }
 +    else
 +    {
 +        fp = open_output_file(fn, rot->nstsout, "gaussian weighted slab centers");
 +
 +        for (g = 0; g < rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            if (ISFLEX(rotg))
 +            {
 +                fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, %s.\n",
 +                        g, erotg_names[rotg->eType], rotg->slab_dist,
 +                        rotg->bMassW ? "centers of mass" : "geometrical centers");
 +            }
 +        }
 +
 +        fprintf(fp, "# Reference centers are listed first (t=-1).\n");
 +        fprintf(fp, "# The following columns have the syntax:\n");
 +        fprintf(fp, "#     ");
 +        print_aligned_short(fp, "t");
 +        print_aligned_short(fp, "grp");
 +        /* Print legend for the first two entries only ... */
 +        for (i = 0; i < 2; i++)
 +        {
 +            print_aligned_short(fp, "slab");
 +            print_aligned(fp, "X center");
 +            print_aligned(fp, "Y center");
 +            print_aligned(fp, "Z center");
 +        }
 +        fprintf(fp, " ...\n");
 +        fflush(fp);
 +    }
 +
 +    return fp;
 +}
 +
 +
 +/* Adds 'buf' to 'str' */
 +static void add_to_string(char **str, char *buf)
 +{
 +    int len;
 +
 +
 +    len = strlen(*str) + strlen(buf) + 1;
 +    srenew(*str, len);
 +    strcat(*str, buf);
 +}
 +
 +
 +static void add_to_string_aligned(char **str, char *buf)
 +{
 +    char buf_aligned[STRLEN];
 +
 +    sprintf(buf_aligned, "%12s", buf);
 +    add_to_string(str, buf_aligned);
 +}
 +
 +
 +/* Open output file and print some general information about the rotation groups.
 + * Call on master only */
 +static FILE *open_rot_out(const char *fn, t_rot *rot, const output_env_t oenv)
 +{
 +    FILE           *fp;
 +    int             g, nsets;
 +    t_rotgrp       *rotg;
 +    const char    **setname;
 +    char            buf[50], buf2[75];
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    gmx_bool        bFlex;
 +    char           *LegendStr = NULL;
 +
 +
 +    if (rot->enfrot->Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn, "a");
 +    }
 +    else
 +    {
 +        fp = xvgropen(fn, "Rotation angles and energy", "Time (ps)", "angles (degrees) and energies (kJ/mol)", oenv);
 +        fprintf(fp, "# Output of enforced rotation data is written in intervals of %d time step%s.\n#\n", rot->nstrout, rot->nstrout > 1 ? "s" : "");
 +        fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector v.\n");
 +        fprintf(fp, "# To obtain the vectorial torque, multiply tau with the group's rot_vec.\n");
 +        fprintf(fp, "# For flexible groups, tau(t,n) from all slabs n have been summed in a single value tau(t) here.\n");
 +        fprintf(fp, "# The torques tau(t,n) are found in the rottorque.log (-rt) output file\n");
 +
 +        for (g = 0; g < rot->ngrp; g++)
 +        {
 +            rotg  = &rot->grp[g];
 +            erg   = rotg->enfrotgrp;
 +            bFlex = ISFLEX(rotg);
 +
 +            fprintf(fp, "#\n");
 +            fprintf(fp, "# ROTATION GROUP %d, potential type '%s':\n", g, erotg_names[rotg->eType]);
 +            fprintf(fp, "# rot_massw%d          %s\n", g, yesno_names[rotg->bMassW]);
 +            fprintf(fp, "# rot_vec%d            %12.5e %12.5e %12.5e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
 +            fprintf(fp, "# rot_rate%d           %12.5e degrees/ps\n", g, rotg->rate);
 +            fprintf(fp, "# rot_k%d              %12.5e kJ/(mol*nm^2)\n", g, rotg->k);
 +            if (rotg->eType == erotgISO || rotg->eType == erotgPM || rotg->eType == erotgRM || rotg->eType == erotgRM2)
 +            {
 +                fprintf(fp, "# rot_pivot%d          %12.5e %12.5e %12.5e  nm\n", g, rotg->pivot[XX], rotg->pivot[YY], rotg->pivot[ZZ]);
 +            }
 +
 +            if (bFlex)
 +            {
 +                fprintf(fp, "# rot_slab_distance%d   %f nm\n", g, rotg->slab_dist);
 +                fprintf(fp, "# rot_min_gaussian%d   %12.5e\n", g, rotg->min_gaussian);
 +            }
 +
 +            /* Output the centers of the rotation groups for the pivot-free potentials */
 +            if ((rotg->eType == erotgISOPF) || (rotg->eType == erotgPMPF) || (rotg->eType == erotgRMPF) || (rotg->eType == erotgRM2PF
 +                                                                                                            || (rotg->eType == erotgFLEXT) || (rotg->eType == erotgFLEX2T)) )
 +            {
 +                fprintf(fp, "# ref. grp. %d center  %12.5e %12.5e %12.5e\n", g,
 +                        erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
 +
 +                fprintf(fp, "# grp. %d init.center  %12.5e %12.5e %12.5e\n", g,
 +                        erg->xc_center[XX], erg->xc_center[YY], erg->xc_center[ZZ]);
 +            }
 +
 +            if ( (rotg->eType == erotgRM2) || (rotg->eType == erotgFLEX2) || (rotg->eType == erotgFLEX2T) )
 +            {
 +                fprintf(fp, "# rot_eps%d            %12.5e nm^2\n", g, rotg->eps);
 +            }
 +            if (erotgFitPOT == rotg->eFittype)
 +            {
 +                fprintf(fp, "#\n");
 +                fprintf(fp, "# theta_fit%d is determined by first evaluating the potential for %d angles around theta_ref%d.\n",
 +                        g, rotg->PotAngle_nstep, g);
 +                fprintf(fp, "# The fit angle is the one with the smallest potential. It is given as the deviation\n");
 +                fprintf(fp, "# from the reference angle, i.e. if theta_ref=X and theta_fit=Y, then the angle with\n");
 +                fprintf(fp, "# minimal value of the potential is X+Y. Angular resolution is %g degrees.\n", rotg->PotAngle_step);
 +            }
 +        }
 +
 +        /* Print a nice legend */
 +        snew(LegendStr, 1);
 +        LegendStr[0] = '\0';
 +        sprintf(buf, "#     %6s", "time");
 +        add_to_string_aligned(&LegendStr, buf);
 +
 +        nsets = 0;
 +        snew(setname, 4*rot->ngrp);
 +
 +        for (g = 0; g < rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            sprintf(buf, "theta_ref%d", g);
 +            add_to_string_aligned(&LegendStr, buf);
 +
 +            sprintf(buf2, "%s (degrees)", buf);
 +            setname[nsets] = strdup(buf2);
 +            nsets++;
 +        }
 +        for (g = 0; g < rot->ngrp; g++)
 +        {
 +            rotg  = &rot->grp[g];
 +            bFlex = ISFLEX(rotg);
 +
 +            /* For flexible axis rotation we use RMSD fitting to determine the
 +             * actual angle of the rotation group */
 +            if (bFlex || erotgFitPOT == rotg->eFittype)
 +            {
 +                sprintf(buf, "theta_fit%d", g);
 +            }
 +            else
 +            {
 +                sprintf(buf, "theta_av%d", g);
 +            }
 +            add_to_string_aligned(&LegendStr, buf);
 +            sprintf(buf2, "%s (degrees)", buf);
 +            setname[nsets] = strdup(buf2);
 +            nsets++;
 +
 +            sprintf(buf, "tau%d", g);
 +            add_to_string_aligned(&LegendStr, buf);
 +            sprintf(buf2, "%s (kJ/mol)", buf);
 +            setname[nsets] = strdup(buf2);
 +            nsets++;
 +
 +            sprintf(buf, "energy%d", g);
 +            add_to_string_aligned(&LegendStr, buf);
 +            sprintf(buf2, "%s (kJ/mol)", buf);
 +            setname[nsets] = strdup(buf2);
 +            nsets++;
 +        }
 +        fprintf(fp, "#\n");
 +
 +        if (nsets > 1)
 +        {
 +            xvgr_legend(fp, nsets, setname, oenv);
 +        }
 +        sfree(setname);
 +
 +        fprintf(fp, "#\n# Legend for the following data columns:\n");
 +        fprintf(fp, "%s\n", LegendStr);
 +        sfree(LegendStr);
 +
 +        fflush(fp);
 +    }
 +
 +    return fp;
 +}
 +
 +
 +/* Call on master only */
 +static FILE *open_angles_out(const char *fn, t_rot *rot, const output_env_t oenv)
 +{
 +    int             g, i;
 +    FILE           *fp;
 +    t_rotgrp       *rotg;
 +    gmx_enfrotgrp_t erg;        /* Pointer to enforced rotation group data */
 +    char            buf[100];
 +
 +
 +    if (rot->enfrot->Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn, "a");
 +    }
 +    else
 +    {
 +        /* Open output file and write some information about it's structure: */
 +        fp = open_output_file(fn, rot->nstsout, "rotation group angles");
 +        fprintf(fp, "# All angles given in degrees, time in ps.\n");
 +        for (g = 0; g < rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            erg  = rotg->enfrotgrp;
 +
 +            /* Output for this group happens only if potential type is flexible or
 +             * if fit type is potential! */
 +            if (ISFLEX(rotg) || (erotgFitPOT == rotg->eFittype) )
 +            {
 +                if (ISFLEX(rotg))
 +                {
 +                    sprintf(buf, " slab distance %f nm, ", rotg->slab_dist);
 +                }
 +                else
 +                {
 +                    buf[0] = '\0';
 +                }
 +
 +                fprintf(fp, "#\n# ROTATION GROUP %d '%s',%s fit type '%s'.\n",
 +                        g, erotg_names[rotg->eType], buf, erotg_fitnames[rotg->eFittype]);
 +
 +                /* Special type of fitting using the potential minimum. This is
 +                 * done for the whole group only, not for the individual slabs. */
 +                if (erotgFitPOT == rotg->eFittype)
 +                {
 +                    fprintf(fp, "#    To obtain theta_fit%d, the potential is evaluated for %d angles around theta_ref%d\n", g, rotg->PotAngle_nstep, g);
 +                    fprintf(fp, "#    The fit angle in the rotation standard outfile is the one with minimal energy E(theta_fit) [kJ/mol].\n");
 +                    fprintf(fp, "#\n");
 +                }
 +
 +                fprintf(fp, "# Legend for the group %d data columns:\n", g);
 +                fprintf(fp, "#     ");
 +                print_aligned_short(fp, "time");
 +                print_aligned_short(fp, "grp");
 +                print_aligned(fp, "theta_ref");
 +
 +                if (erotgFitPOT == rotg->eFittype)
 +                {
 +                    /* Output the set of angles around the reference angle */
 +                    for (i = 0; i < rotg->PotAngle_nstep; i++)
 +                    {
 +                        sprintf(buf, "E(%g)", erg->PotAngleFit->degangle[i]);
 +                        print_aligned(fp, buf);
 +                    }
 +                }
 +                else
 +                {
 +                    /* Output fit angle for each slab */
 +                    print_aligned_short(fp, "slab");
 +                    print_aligned_short(fp, "atoms");
 +                    print_aligned(fp, "theta_fit");
 +                    print_aligned_short(fp, "slab");
 +                    print_aligned_short(fp, "atoms");
 +                    print_aligned(fp, "theta_fit");
 +                    fprintf(fp, " ...");
 +                }
 +                fprintf(fp, "\n");
 +            }
 +        }
 +        fflush(fp);
 +    }
 +
 +    return fp;
 +}
 +
 +
 +/* Open torque output file and write some information about it's structure.
 + * Call on master only */
 +static FILE *open_torque_out(const char *fn, t_rot *rot, const output_env_t oenv)
 +{
 +    FILE      *fp;
 +    int        g;
 +    t_rotgrp  *rotg;
 +
 +
 +    if (rot->enfrot->Flags & MD_APPENDFILES)
 +    {
 +        fp = gmx_fio_fopen(fn, "a");
 +    }
 +    else
 +    {
 +        fp = open_output_file(fn, rot->nstsout, "torques");
 +
 +        for (g = 0; g < rot->ngrp; g++)
 +        {
 +            rotg = &rot->grp[g];
 +            if (ISFLEX(rotg))
 +            {
 +                fprintf(fp, "# Rotation group %d (%s), slab distance %f nm.\n", g, erotg_names[rotg->eType], rotg->slab_dist);
 +                fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector.\n");
 +                fprintf(fp, "# To obtain the vectorial torque, multiply tau with\n");
 +                fprintf(fp, "# rot_vec%d            %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
 +                fprintf(fp, "#\n");
 +            }
 +        }
 +        fprintf(fp, "# Legend for the following data columns: (tau=torque for that slab):\n");
 +        fprintf(fp, "#     ");
 +        print_aligned_short(fp, "t");
 +        print_aligned_short(fp, "grp");
 +        print_aligned_short(fp, "slab");
 +        print_aligned(fp, "tau");
 +        print_aligned_short(fp, "slab");
 +        print_aligned(fp, "tau");
 +        fprintf(fp, " ...\n");
 +        fflush(fp);
 +    }
 +
 +    return fp;
 +}
 +
 +
 +static void swap_val(double* vec, int i, int j)
 +{
 +    double tmp = vec[j];
 +
 +
 +    vec[j] = vec[i];
 +    vec[i] = tmp;
 +}
 +
 +
 +static void swap_col(double **mat, int i, int j)
 +{
 +    double tmp[3] = {mat[0][j], mat[1][j], mat[2][j]};
 +
 +
 +    mat[0][j] = mat[0][i];
 +    mat[1][j] = mat[1][i];
 +    mat[2][j] = mat[2][i];
 +
 +    mat[0][i] = tmp[0];
 +    mat[1][i] = tmp[1];
 +    mat[2][i] = tmp[2];
 +}
 +
 +
 +/* Eigenvectors are stored in columns of eigen_vec */
 +static void diagonalize_symmetric(
 +        double **matrix,
 +        double **eigen_vec,
 +        double   eigenval[3])
 +{
 +    int n_rot;
 +
 +
 +    jacobi(matrix, 3, eigenval, eigen_vec, &n_rot);
 +
 +    /* sort in ascending order */
 +    if (eigenval[0] > eigenval[1])
 +    {
 +        swap_val(eigenval, 0, 1);
 +        swap_col(eigen_vec, 0, 1);
 +    }
 +    if (eigenval[1] > eigenval[2])
 +    {
 +        swap_val(eigenval, 1, 2);
 +        swap_col(eigen_vec, 1, 2);
 +    }
 +    if (eigenval[0] > eigenval[1])
 +    {
 +        swap_val(eigenval, 0, 1);
 +        swap_col(eigen_vec, 0, 1);
 +    }
 +}
 +
 +
 +static void align_with_z(
 +        rvec* s,           /* Structure to align */
 +        int   natoms,
 +        rvec  axis)
 +{
 +    int     i, j, k;
 +    rvec    zet         = {0.0, 0.0, 1.0};
 +    rvec    rot_axis    = {0.0, 0.0, 0.0};
 +    rvec   *rotated_str = NULL;
 +    real    ooanorm;
 +    real    angle;
 +    matrix  rotmat;
 +
 +
 +    snew(rotated_str, natoms);
 +
 +    /* Normalize the axis */
 +    ooanorm = 1.0/norm(axis);
 +    svmul(ooanorm, axis, axis);
 +
 +    /* Calculate the angle for the fitting procedure */
 +    cprod(axis, zet, rot_axis);
 +    angle = acos(axis[2]);
 +    if (angle < 0.0)
 +    {
 +        angle += M_PI;
 +    }
 +
 +    /* Calculate the rotation matrix */
 +    calc_rotmat(rot_axis, angle*180.0/M_PI, rotmat);
 +
 +    /* Apply the rotation matrix to s */
 +    for (i = 0; i < natoms; i++)
 +    {
 +        for (j = 0; j < 3; j++)
 +        {
 +            for (k = 0; k < 3; k++)
 +            {
 +                rotated_str[i][j] += rotmat[j][k]*s[i][k];
 +            }
 +        }
 +    }
 +
 +    /* Rewrite the rotated structure to s */
 +    for (i = 0; i < natoms; i++)
 +    {
 +        for (j = 0; j < 3; j++)
 +        {
 +            s[i][j] = rotated_str[i][j];
 +        }
 +    }
 +
 +    sfree(rotated_str);
 +}
 +
 +
 +static void calc_correl_matrix(rvec* Xstr, rvec* Ystr, double** Rmat, int natoms)
 +{
 +    int i, j, k;
 +
 +
 +    for (i = 0; i < 3; i++)
 +    {
 +        for (j = 0; j < 3; j++)
 +        {
 +            Rmat[i][j] = 0.0;
 +        }
 +    }
 +
 +    for (i = 0; i < 3; i++)
 +    {
 +        for (j = 0; j < 3; j++)
 +        {
 +            for (k = 0; k < natoms; k++)
 +            {
 +                Rmat[i][j] += Ystr[k][i] * Xstr[k][j];
 +            }
 +        }
 +    }
 +}
 +
 +
 +static void weigh_coords(rvec* str, real* weight, int natoms)
 +{
 +    int i, j;
 +
 +
 +    for (i = 0; i < natoms; i++)
 +    {
 +        for (j = 0; j < 3; j++)
 +        {
 +            str[i][j] *= sqrt(weight[i]);
 +        }
 +    }
 +}
 +
 +
 +static real opt_angle_analytic(
 +        rvec* ref_s,
 +        rvec* act_s,
 +        real* weight,
 +        int   natoms,
 +        rvec  ref_com,
 +        rvec  act_com,
 +        rvec  axis)
 +{
 +    int      i, j, k;
 +    rvec    *ref_s_1 = NULL;
 +    rvec    *act_s_1 = NULL;
 +    rvec     shift;
 +    double **Rmat, **RtR, **eigvec;
 +    double   eigval[3];
 +    double   V[3][3], WS[3][3];
 +    double   rot_matrix[3][3];
 +    double   opt_angle;
 +
 +
 +    /* Do not change the original coordinates */
 +    snew(ref_s_1, natoms);
 +    snew(act_s_1, natoms);
 +    for (i = 0; i < natoms; i++)
 +    {
 +        copy_rvec(ref_s[i], ref_s_1[i]);
 +        copy_rvec(act_s[i], act_s_1[i]);
 +    }
 +
 +    /* Translate the structures to the origin */
 +    shift[XX] = -ref_com[XX];
 +    shift[YY] = -ref_com[YY];
 +    shift[ZZ] = -ref_com[ZZ];
 +    translate_x(ref_s_1, natoms, shift);
 +
 +    shift[XX] = -act_com[XX];
 +    shift[YY] = -act_com[YY];
 +    shift[ZZ] = -act_com[ZZ];
 +    translate_x(act_s_1, natoms, shift);
 +
 +    /* Align rotation axis with z */
 +    align_with_z(ref_s_1, natoms, axis);
 +    align_with_z(act_s_1, natoms, axis);
 +
 +    /* Correlation matrix */
 +    Rmat = allocate_square_matrix(3);
 +
 +    for (i = 0; i < natoms; i++)
 +    {
 +        ref_s_1[i][2] = 0.0;
 +        act_s_1[i][2] = 0.0;
 +    }
 +
 +    /* Weight positions with sqrt(weight) */
 +    if (NULL != weight)
 +    {
 +        weigh_coords(ref_s_1, weight, natoms);
 +        weigh_coords(act_s_1, weight, natoms);
 +    }
 +
 +    /* Calculate correlation matrices R=YXt (X=ref_s; Y=act_s) */
 +    calc_correl_matrix(ref_s_1, act_s_1, Rmat, natoms);
 +
 +    /* Calculate RtR */
 +    RtR = allocate_square_matrix(3);
 +    for (i = 0; i < 3; i++)
 +    {
 +        for (j = 0; j < 3; j++)
 +        {
 +            for (k = 0; k < 3; k++)
 +            {
 +                RtR[i][j] += Rmat[k][i] * Rmat[k][j];
 +            }
 +        }
 +    }
 +    /* Diagonalize RtR */
 +    snew(eigvec, 3);
 +    for (i = 0; i < 3; i++)
 +    {
 +        snew(eigvec[i], 3);
 +    }
 +
 +    diagonalize_symmetric(RtR, eigvec, eigval);
 +    swap_col(eigvec, 0, 1);
 +    swap_col(eigvec, 1, 2);
 +    swap_val(eigval, 0, 1);
 +    swap_val(eigval, 1, 2);
 +
 +    /* Calculate V */
 +    for (i = 0; i < 3; i++)
 +    {
 +        for (j = 0; j < 3; j++)
 +        {
 +            V[i][j]  = 0.0;
 +            WS[i][j] = 0.0;
 +        }
 +    }
 +
 +    for (i = 0; i < 2; i++)
 +    {
 +        for (j = 0; j < 2; j++)
 +        {
 +            WS[i][j] = eigvec[i][j] / sqrt(eigval[j]);
 +        }
 +    }
 +
 +    for (i = 0; i < 3; i++)
 +    {
 +        for (j = 0; j < 3; j++)
 +        {
 +            for (k = 0; k < 3; k++)
 +            {
 +                V[i][j] += Rmat[i][k]*WS[k][j];
 +            }
 +        }
 +    }
 +    free_square_matrix(Rmat, 3);
 +
 +    /* Calculate optimal rotation matrix */
 +    for (i = 0; i < 3; i++)
 +    {
 +        for (j = 0; j < 3; j++)
 +        {
 +            rot_matrix[i][j] = 0.0;
 +        }
 +    }
 +
 +    for (i = 0; i < 3; i++)
 +    {
 +        for (j = 0; j < 3; j++)
 +        {
 +            for (k = 0; k < 3; k++)
 +            {
 +                rot_matrix[i][j] += eigvec[i][k]*V[j][k];
 +            }
 +        }
 +    }
 +    rot_matrix[2][2] = 1.0;
 +
 +    /* In some cases abs(rot_matrix[0][0]) can be slighly larger
 +     * than unity due to numerical inacurracies. To be able to calculate
 +     * the acos function, we put these values back in range. */
 +    if (rot_matrix[0][0] > 1.0)
 +    {
 +        rot_matrix[0][0] = 1.0;
 +    }
 +    else if (rot_matrix[0][0] < -1.0)
 +    {
 +        rot_matrix[0][0] = -1.0;
 +    }
 +
 +    /* Determine the optimal rotation angle: */
 +    opt_angle = (-1.0)*acos(rot_matrix[0][0])*180.0/M_PI;
 +    if (rot_matrix[0][1] < 0.0)
 +    {
 +        opt_angle = (-1.0)*opt_angle;
 +    }
 +
 +    /* Give back some memory */
 +    free_square_matrix(RtR, 3);
 +    sfree(ref_s_1);
 +    sfree(act_s_1);
 +    for (i = 0; i < 3; i++)
 +    {
 +        sfree(eigvec[i]);
 +    }
 +    sfree(eigvec);
 +
 +    return (real) opt_angle;
 +}
 +
 +
 +/* Determine angle of the group by RMSD fit to the reference */
 +/* Not parallelized, call this routine only on the master */
 +static real flex_fit_angle(t_rotgrp *rotg)
 +{
 +    int             i;
 +    rvec           *fitcoords = NULL;
 +    rvec            center;     /* Center of positions passed to the fit routine */
 +    real            fitangle;   /* Angle of the rotation group derived by fitting */
 +    rvec            coord;
 +    real            scal;
 +    gmx_enfrotgrp_t erg;        /* Pointer to enforced rotation group data */
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* Get the center of the rotation group.
 +     * Note, again, erg->xc has been sorted in do_flexible */
 +    get_center(erg->xc, erg->mc_sorted, rotg->nat, center);
 +
 +    /* === Determine the optimal fit angle for the rotation group === */
 +    if (rotg->eFittype == erotgFitNORM)
 +    {
 +        /* Normalize every position to it's reference length */
 +        for (i = 0; i < rotg->nat; i++)
 +        {
 +            /* Put the center of the positions into the origin */
 +            rvec_sub(erg->xc[i], center, coord);
 +            /* Determine the scaling factor for the length: */
 +            scal = erg->xc_ref_length[erg->xc_sortind[i]] / norm(coord);
 +            /* Get position, multiply with the scaling factor and save  */
 +            svmul(scal, coord, erg->xc_norm[i]);
 +        }
 +        fitcoords = erg->xc_norm;
 +    }
 +    else
 +    {
 +        fitcoords = erg->xc;
 +    }
 +    /* From the point of view of the current positions, the reference has rotated
 +     * backwards. Since we output the angle relative to the fixed reference,
 +     * we need the minus sign. */
 +    fitangle = -opt_angle_analytic(erg->xc_ref_sorted, fitcoords, erg->mc_sorted,
 +                                   rotg->nat, erg->xc_ref_center, center, rotg->vec);
 +
 +    return fitangle;
 +}
 +
 +
 +/* Determine actual angle of each slab by RMSD fit to the reference */
 +/* Not parallelized, call this routine only on the master */
 +static void flex_fit_angle_perslab(
 +        int       g,
 +        t_rotgrp *rotg,
 +        double    t,
 +        real      degangle,
 +        FILE     *fp)
 +{
 +    int             i, l, n, islab, ind;
 +    rvec            curr_x, ref_x;
 +    rvec            act_center; /* Center of actual positions that are passed to the fit routine */
 +    rvec            ref_center; /* Same for the reference positions */
 +    real            fitangle;   /* Angle of a slab derived from an RMSD fit to
 +                                 * the reference structure at t=0  */
 +    t_gmx_slabdata *sd;
 +    gmx_enfrotgrp_t erg;        /* Pointer to enforced rotation group data */
 +    real            OOm_av;     /* 1/average_mass of a rotation group atom */
 +    real            m_rel;      /* Relative mass of a rotation group atom  */
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* Average mass of a rotation group atom: */
 +    OOm_av = erg->invmass*rotg->nat;
 +
 +    /**********************************/
 +    /* First collect the data we need */
 +    /**********************************/
 +
 +    /* Collect the data for the individual slabs */
 +    for (n = erg->slab_first; n <= erg->slab_last; n++)
 +    {
 +        islab   = n - erg->slab_first; /* slab index */
 +        sd      = &(rotg->enfrotgrp->slab_data[islab]);
 +        sd->nat = erg->lastatom[islab]-erg->firstatom[islab]+1;
 +        ind     = 0;
 +
 +        /* Loop over the relevant atoms in the slab */
 +        for (l = erg->firstatom[islab]; l <= erg->lastatom[islab]; l++)
 +        {
 +            /* Current position of this atom: x[ii][XX/YY/ZZ] */
 +            copy_rvec(erg->xc[l], curr_x);
 +
 +            /* The (unrotated) reference position of this atom is copied to ref_x.
 +             * Beware, the xc coords have been sorted in do_flexible */
 +            copy_rvec(erg->xc_ref_sorted[l], ref_x);
 +
 +            /* Save data for doing angular RMSD fit later */
 +            /* Save the current atom position */
 +            copy_rvec(curr_x, sd->x[ind]);
 +            /* Save the corresponding reference position */
 +            copy_rvec(ref_x, sd->ref[ind]);
 +
 +            /* Maybe also mass-weighting was requested. If yes, additionally
 +             * multiply the weights with the relative mass of the atom. If not,
 +             * multiply with unity. */
 +            m_rel = erg->mc_sorted[l]*OOm_av;
 +
 +            /* Save the weight for this atom in this slab */
 +            sd->weight[ind] = gaussian_weight(curr_x, rotg, n) * m_rel;
 +
 +            /* Next atom in this slab */
 +            ind++;
 +        }
 +    }
 +
 +    /******************************/
 +    /* Now do the fit calculation */
 +    /******************************/
 +
 +    fprintf(fp, "%12.3e%6d%12.3f", t, g, degangle);
 +
 +    /* === Now do RMSD fitting for each slab === */
 +    /* We require at least SLAB_MIN_ATOMS in a slab, such that the fit makes sense. */
 +#define SLAB_MIN_ATOMS 4
 +
 +    for (n = erg->slab_first; n <= erg->slab_last; n++)
 +    {
 +        islab = n - erg->slab_first; /* slab index */
 +        sd    = &(rotg->enfrotgrp->slab_data[islab]);
 +        if (sd->nat >= SLAB_MIN_ATOMS)
 +        {
 +            /* Get the center of the slabs reference and current positions */
 +            get_center(sd->ref, sd->weight, sd->nat, ref_center);
 +            get_center(sd->x, sd->weight, sd->nat, act_center);
 +            if (rotg->eFittype == erotgFitNORM)
 +            {
 +                /* Normalize every position to it's reference length
 +                 * prior to performing the fit */
 +                for (i = 0; i < sd->nat; i++) /* Center */
 +                {
 +                    rvec_dec(sd->ref[i], ref_center);
 +                    rvec_dec(sd->x[i], act_center);
 +                    /* Normalize x_i such that it gets the same length as ref_i */
 +                    svmul( norm(sd->ref[i])/norm(sd->x[i]), sd->x[i], sd->x[i] );
 +                }
 +                /* We already subtracted the centers */
 +                clear_rvec(ref_center);
 +                clear_rvec(act_center);
 +            }
 +            fitangle = -opt_angle_analytic(sd->ref, sd->x, sd->weight, sd->nat,
 +                                           ref_center, act_center, rotg->vec);
 +            fprintf(fp, "%6d%6d%12.3f", n, sd->nat, fitangle);
 +        }
 +    }
 +    fprintf(fp, "\n");
 +
 +#undef SLAB_MIN_ATOMS
 +}
 +
 +
 +/* Shift x with is */
 +static gmx_inline void shift_single_coord(matrix box, rvec x, const ivec is)
 +{
 +    int tx, ty, tz;
 +
 +
 +    tx = is[XX];
 +    ty = is[YY];
 +    tz = is[ZZ];
 +
 +    if (TRICLINIC(box))
 +    {
 +        x[XX] += tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
 +        x[YY] += ty*box[YY][YY]+tz*box[ZZ][YY];
 +        x[ZZ] += tz*box[ZZ][ZZ];
 +    }
 +    else
 +    {
 +        x[XX] += tx*box[XX][XX];
 +        x[YY] += ty*box[YY][YY];
 +        x[ZZ] += tz*box[ZZ][ZZ];
 +    }
 +}
 +
 +
 +/* Determine the 'home' slab of this atom which is the
 + * slab with the highest Gaussian weight of all */
 +#define round(a) (int)(a+0.5)
 +static gmx_inline int get_homeslab(
 +        rvec curr_x,   /* The position for which the home slab shall be determined */
 +        rvec rotvec,   /* The rotation vector */
 +        real slabdist) /* The slab distance */
 +{
 +    real dist;
 +
 +
 +    /* The distance of the atom to the coordinate center (where the
 +     * slab with index 0) is */
 +    dist = iprod(rotvec, curr_x);
 +
 +    return round(dist / slabdist);
 +}
 +
 +
 +/* For a local atom determine the relevant slabs, i.e. slabs in
 + * which the gaussian is larger than min_gaussian
 + */
 +static int get_single_atom_gaussians(
 +        rvec       curr_x,
 +        t_rotgrp  *rotg)
 +{
 +    int             slab, homeslab;
 +    real            g;
 +    int             count = 0;
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* Determine the 'home' slab of this atom: */
 +    homeslab = get_homeslab(curr_x, rotg->vec, rotg->slab_dist);
 +
 +    /* First determine the weight in the atoms home slab: */
 +    g = gaussian_weight(curr_x, rotg, homeslab);
 +
 +    erg->gn_atom[count]    = g;
 +    erg->gn_slabind[count] = homeslab;
 +    count++;
 +
 +
 +    /* Determine the max slab */
 +    slab = homeslab;
 +    while (g > rotg->min_gaussian)
 +    {
 +        slab++;
 +        g = gaussian_weight(curr_x, rotg, slab);
 +        erg->gn_slabind[count] = slab;
 +        erg->gn_atom[count]    = g;
 +        count++;
 +    }
 +    count--;
 +
 +    /* Determine the max slab */
 +    slab = homeslab;
 +    do
 +    {
 +        slab--;
 +        g = gaussian_weight(curr_x, rotg, slab);
 +        erg->gn_slabind[count] = slab;
 +        erg->gn_atom[count]    = g;
 +        count++;
 +    }
 +    while (g > rotg->min_gaussian);
 +    count--;
 +
 +    return count;
 +}
 +
 +
 +static void flex2_precalc_inner_sum(t_rotgrp *rotg)
 +{
 +    int             i, n, islab;
 +    rvec            xi;       /* positions in the i-sum                        */
 +    rvec            xcn, ycn; /* the current and the reference slab centers    */
 +    real            gaussian_xi;
 +    rvec            yi0;
 +    rvec            rin;     /* Helper variables                              */
 +    real            fac, fac2;
 +    rvec            innersumvec;
 +    real            OOpsii, OOpsiistar;
 +    real            sin_rin; /* s_ii.r_ii */
 +    rvec            s_in, tmpvec, tmpvec2;
 +    real            mi, wi;  /* Mass-weighting of the positions                 */
 +    real            N_M;     /* N/M                                             */
 +    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
 +
 +
 +    erg = rotg->enfrotgrp;
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Loop over all slabs that contain something */
 +    for (n = erg->slab_first; n <= erg->slab_last; n++)
 +    {
 +        islab = n - erg->slab_first; /* slab index */
 +
 +        /* The current center of this slab is saved in xcn: */
 +        copy_rvec(erg->slab_center[islab], xcn);
 +        /* ... and the reference center in ycn: */
 +        copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
 +
 +        /*** D. Calculate the whole inner sum used for second and third sum */
 +        /* For slab n, we need to loop over all atoms i again. Since we sorted
 +         * the atoms with respect to the rotation vector, we know that it is sufficient
 +         * to calculate from firstatom to lastatom only. All other contributions will
 +         * be very small. */
 +        clear_rvec(innersumvec);
 +        for (i = erg->firstatom[islab]; i <= erg->lastatom[islab]; i++)
 +        {
 +            /* Coordinate xi of this atom */
 +            copy_rvec(erg->xc[i], xi);
 +
 +            /* The i-weights */
 +            gaussian_xi = gaussian_weight(xi, rotg, n);
 +            mi          = erg->mc_sorted[i]; /* need the sorted mass here */
 +            wi          = N_M*mi;
 +
 +            /* Calculate rin */
 +            copy_rvec(erg->xc_ref_sorted[i], yi0); /* Reference position yi0   */
 +            rvec_sub(yi0, ycn, tmpvec2);           /* tmpvec2 = yi0 - ycn      */
 +            mvmul(erg->rotmat, tmpvec2, rin);      /* rin = Omega.(yi0 - ycn)  */
 +
 +            /* Calculate psi_i* and sin */
 +            rvec_sub(xi, xcn, tmpvec2);           /* tmpvec2 = xi - xcn       */
 +            cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x (xi - xcn)  */
 +            OOpsiistar = norm2(tmpvec)+rotg->eps; /* OOpsii* = 1/psii* = |v x (xi-xcn)|^2 + eps */
 +            OOpsii     = norm(tmpvec);            /* OOpsii = 1 / psii = |v x (xi - xcn)| */
 +
 +            /*                           *         v x (xi - xcn)          */
 +            unitv(tmpvec, s_in);        /*  sin = ----------------         */
 +                                        /*        |v x (xi - xcn)|         */
 +
 +            sin_rin = iprod(s_in, rin); /* sin_rin = sin . rin             */
 +
 +            /* Now the whole sum */
 +            fac = OOpsii/OOpsiistar;
 +            svmul(fac, rin, tmpvec);
 +            fac2 = fac*fac*OOpsii;
 +            svmul(fac2*sin_rin, s_in, tmpvec2);
 +            rvec_dec(tmpvec, tmpvec2);
 +
 +            svmul(wi*gaussian_xi*sin_rin, tmpvec, tmpvec2);
 +
 +            rvec_inc(innersumvec, tmpvec2);
 +        } /* now we have the inner sum, used both for sum2 and sum3 */
 +
 +        /* Save it to be used in do_flex2_lowlevel */
 +        copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
 +    } /* END of loop over slabs */
 +}
 +
 +
 +static void flex_precalc_inner_sum(t_rotgrp *rotg)
 +{
 +    int             i, n, islab;
 +    rvec            xi;       /* position                                      */
 +    rvec            xcn, ycn; /* the current and the reference slab centers    */
 +    rvec            qin, rin; /* q_i^n and r_i^n                               */
 +    real            bin;
 +    rvec            tmpvec;
 +    rvec            innersumvec; /* Inner part of sum_n2                          */
 +    real            gaussian_xi; /* Gaussian weight gn(xi)                        */
 +    real            mi, wi;      /* Mass-weighting of the positions               */
 +    real            N_M;         /* N/M                                           */
 +
 +    gmx_enfrotgrp_t erg;         /* Pointer to enforced rotation group data */
 +
 +
 +    erg = rotg->enfrotgrp;
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Loop over all slabs that contain something */
 +    for (n = erg->slab_first; n <= erg->slab_last; n++)
 +    {
 +        islab = n - erg->slab_first; /* slab index */
 +
 +        /* The current center of this slab is saved in xcn: */
 +        copy_rvec(erg->slab_center[islab], xcn);
 +        /* ... and the reference center in ycn: */
 +        copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
 +
 +        /* For slab n, we need to loop over all atoms i again. Since we sorted
 +         * the atoms with respect to the rotation vector, we know that it is sufficient
 +         * to calculate from firstatom to lastatom only. All other contributions will
 +         * be very small. */
 +        clear_rvec(innersumvec);
 +        for (i = erg->firstatom[islab]; i <= erg->lastatom[islab]; i++)
 +        {
 +            /* Coordinate xi of this atom */
 +            copy_rvec(erg->xc[i], xi);
 +
 +            /* The i-weights */
 +            gaussian_xi = gaussian_weight(xi, rotg, n);
 +            mi          = erg->mc_sorted[i]; /* need the sorted mass here */
 +            wi          = N_M*mi;
 +
 +            /* Calculate rin and qin */
 +            rvec_sub(erg->xc_ref_sorted[i], ycn, tmpvec); /* tmpvec = yi0-ycn */
 +            mvmul(erg->rotmat, tmpvec, rin);              /* rin = Omega.(yi0 - ycn)  */
 +            cprod(rotg->vec, rin, tmpvec);                /* tmpvec = v x Omega*(yi0-ycn) */
 +
 +            /*                                *        v x Omega*(yi0-ycn)    */
 +            unitv(tmpvec, qin);              /* qin = ---------------------   */
 +                                             /*       |v x Omega*(yi0-ycn)|   */
 +
 +            /* Calculate bin */
 +            rvec_sub(xi, xcn, tmpvec);            /* tmpvec = xi-xcn          */
 +            bin = iprod(qin, tmpvec);             /* bin  = qin*(xi-xcn)      */
 +
 +            svmul(wi*gaussian_xi*bin, qin, tmpvec);
 +
 +            /* Add this contribution to the inner sum: */
 +            rvec_add(innersumvec, tmpvec, innersumvec);
 +        } /* now we have the inner sum vector S^n for this slab */
 +          /* Save it to be used in do_flex_lowlevel */
 +        copy_rvec(innersumvec, erg->slab_innersumvec[islab]);
 +    }
 +}
 +
 +
 +static real do_flex2_lowlevel(
 +        t_rotgrp  *rotg,
 +        real       sigma,   /* The Gaussian width sigma */
 +        rvec       x[],
 +        gmx_bool   bOutstepRot,
 +        gmx_bool   bOutstepSlab,
 +        matrix     box)
 +{
 +    int             count, ic, ii, j, m, n, islab, iigrp, ifit;
 +    rvec            xj;          /* position in the i-sum                         */
 +    rvec            yj0;         /* the reference position in the j-sum           */
 +    rvec            xcn, ycn;    /* the current and the reference slab centers    */
 +    real            V;           /* This node's part of the rotation pot. energy  */
 +    real            gaussian_xj; /* Gaussian weight                               */
 +    real            beta;
 +
 +    real            numerator, fit_numerator;
 +    rvec            rjn, fit_rjn; /* Helper variables                              */
 +    real            fac, fac2;
 +
 +    real            OOpsij, OOpsijstar;
 +    real            OOsigma2; /* 1/(sigma^2)                                   */
 +    real            sjn_rjn;
 +    real            betasigpsi;
 +    rvec            sjn, tmpvec, tmpvec2, yj0_ycn;
 +    rvec            sum1vec_part, sum1vec, sum2vec_part, sum2vec, sum3vec, sum4vec, innersumvec;
 +    real            sum3, sum4;
 +    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data       */
 +    real            mj, wj;  /* Mass-weighting of the positions               */
 +    real            N_M;     /* N/M                                           */
 +    real            Wjn;     /* g_n(x_j) m_j / Mjn                            */
 +    gmx_bool        bCalcPotFit;
 +
 +    /* To calculate the torque per slab */
 +    rvec slab_force;         /* Single force from slab n on one atom          */
 +    rvec slab_sum1vec_part;
 +    real slab_sum3part, slab_sum4part;
 +    rvec slab_sum1vec, slab_sum2vec, slab_sum3vec, slab_sum4vec;
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* Pre-calculate the inner sums, so that we do not have to calculate
 +     * them again for every atom */
 +    flex2_precalc_inner_sum(rotg);
 +
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
 +
 +    /********************************************************/
 +    /* Main loop over all local atoms of the rotation group */
 +    /********************************************************/
 +    N_M      = rotg->nat * erg->invmass;
 +    V        = 0.0;
 +    OOsigma2 = 1.0 / (sigma*sigma);
 +    for (j = 0; j < erg->nat_loc; j++)
 +    {
 +        /* Local index of a rotation group atom  */
 +        ii = erg->ind_loc[j];
 +        /* Position of this atom in the collective array */
 +        iigrp = erg->xc_ref_ind[j];
 +        /* Mass-weighting */
 +        mj = erg->mc[iigrp];  /* need the unsorted mass here */
 +        wj = N_M*mj;
 +
 +        /* Current position of this atom: x[ii][XX/YY/ZZ]
 +         * Note that erg->xc_center contains the center of mass in case the flex2-t
 +         * potential was chosen. For the flex2 potential erg->xc_center must be
 +         * zero. */
 +        rvec_sub(x[ii], erg->xc_center, xj);
 +
 +        /* Shift this atom such that it is near its reference */
 +        shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
 +
 +        /* Determine the slabs to loop over, i.e. the ones with contributions
 +         * larger than min_gaussian */
 +        count = get_single_atom_gaussians(xj, rotg);
 +
 +        clear_rvec(sum1vec_part);
 +        clear_rvec(sum2vec_part);
 +        sum3 = 0.0;
 +        sum4 = 0.0;
 +        /* Loop over the relevant slabs for this atom */
 +        for (ic = 0; ic < count; ic++)
 +        {
 +            n = erg->gn_slabind[ic];
 +
 +            /* Get the precomputed Gaussian value of curr_slab for curr_x */
 +            gaussian_xj = erg->gn_atom[ic];
 +
 +            islab = n - erg->slab_first; /* slab index */
 +
 +            /* The (unrotated) reference position of this atom is copied to yj0: */
 +            copy_rvec(rotg->x_ref[iigrp], yj0);
 +
 +            beta = calc_beta(xj, rotg, n);
 +
 +            /* The current center of this slab is saved in xcn: */
 +            copy_rvec(erg->slab_center[islab], xcn);
 +            /* ... and the reference center in ycn: */
 +            copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
 +
 +            rvec_sub(yj0, ycn, yj0_ycn);          /* yj0_ycn = yj0 - ycn      */
 +
 +            /* Rotate: */
 +            mvmul(erg->rotmat, yj0_ycn, rjn);     /* rjn = Omega.(yj0 - ycn)  */
 +
 +            /* Subtract the slab center from xj */
 +            rvec_sub(xj, xcn, tmpvec2);           /* tmpvec2 = xj - xcn       */
 +
 +            /* Calculate sjn */
 +            cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x (xj - xcn)  */
 +
 +            OOpsijstar = norm2(tmpvec)+rotg->eps; /* OOpsij* = 1/psij* = |v x (xj-xcn)|^2 + eps */
 +
 +            numerator = sqr(iprod(tmpvec, rjn));
 +
 +            /*********************************/
 +            /* Add to the rotation potential */
 +            /*********************************/
 +            V += 0.5*rotg->k*wj*gaussian_xj*numerator/OOpsijstar;
 +
 +            /* If requested, also calculate the potential for a set of angles
 +             * near the current reference angle */
 +            if (bCalcPotFit)
 +            {
 +                for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +                {
 +                    mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, fit_rjn);
 +                    fit_numerator              = sqr(iprod(tmpvec, fit_rjn));
 +                    erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*fit_numerator/OOpsijstar;
 +                }
 +            }
 +
 +            /*************************************/
 +            /* Now calculate the force on atom j */
 +            /*************************************/
 +
 +            OOpsij = norm(tmpvec);    /* OOpsij = 1 / psij = |v x (xj - xcn)| */
 +
 +            /*                              *         v x (xj - xcn)          */
 +            unitv(tmpvec, sjn);            /*  sjn = ----------------         */
 +                                           /*        |v x (xj - xcn)|         */
 +
 +            sjn_rjn = iprod(sjn, rjn);     /* sjn_rjn = sjn . rjn             */
 +
 +
 +            /*** A. Calculate the first of the four sum terms: ****************/
 +            fac = OOpsij/OOpsijstar;
 +            svmul(fac, rjn, tmpvec);
 +            fac2 = fac*fac*OOpsij;
 +            svmul(fac2*sjn_rjn, sjn, tmpvec2);
 +            rvec_dec(tmpvec, tmpvec2);
 +            fac2 = wj*gaussian_xj; /* also needed for sum4 */
 +            svmul(fac2*sjn_rjn, tmpvec, slab_sum1vec_part);
 +            /********************/
 +            /*** Add to sum1: ***/
 +            /********************/
 +            rvec_inc(sum1vec_part, slab_sum1vec_part); /* sum1 still needs to vector multiplied with v */
 +
 +            /*** B. Calculate the forth of the four sum terms: ****************/
 +            betasigpsi = beta*OOsigma2*OOpsij; /* this is also needed for sum3 */
 +            /********************/
 +            /*** Add to sum4: ***/
 +            /********************/
 +            slab_sum4part = fac2*betasigpsi*fac*sjn_rjn*sjn_rjn; /* Note that fac is still valid from above */
 +            sum4         += slab_sum4part;
 +
 +            /*** C. Calculate Wjn for second and third sum */
 +            /* Note that we can safely divide by slab_weights since we check in
 +             * get_slab_centers that it is non-zero. */
 +            Wjn = gaussian_xj*mj/erg->slab_weights[islab];
 +
 +            /* We already have precalculated the inner sum for slab n */
 +            copy_rvec(erg->slab_innersumvec[islab], innersumvec);
 +
 +            /* Weigh the inner sum vector with Wjn */
 +            svmul(Wjn, innersumvec, innersumvec);
 +
 +            /*** E. Calculate the second of the four sum terms: */
 +            /********************/
 +            /*** Add to sum2: ***/
 +            /********************/
 +            rvec_inc(sum2vec_part, innersumvec); /* sum2 still needs to be vector crossproduct'ed with v */
 +
 +            /*** F. Calculate the third of the four sum terms: */
 +            slab_sum3part = betasigpsi * iprod(sjn, innersumvec);
 +            sum3         += slab_sum3part; /* still needs to be multiplied with v */
 +
 +            /*** G. Calculate the torque on the local slab's axis: */
 +            if (bOutstepRot)
 +            {
 +                /* Sum1 */
 +                cprod(slab_sum1vec_part, rotg->vec, slab_sum1vec);
 +                /* Sum2 */
 +                cprod(innersumvec, rotg->vec, slab_sum2vec);
 +                /* Sum3 */
 +                svmul(slab_sum3part, rotg->vec, slab_sum3vec);
 +                /* Sum4 */
 +                svmul(slab_sum4part, rotg->vec, slab_sum4vec);
 +
 +                /* The force on atom ii from slab n only: */
 +                for (m = 0; m < DIM; m++)
 +                {
 +                    slab_force[m] = rotg->k * (-slab_sum1vec[m] + slab_sum2vec[m] - slab_sum3vec[m] + 0.5*slab_sum4vec[m]);
 +                }
 +
 +                erg->slab_torque_v[islab] += torque(rotg->vec, slab_force, xj, xcn);
 +            }
 +        } /* END of loop over slabs */
 +
 +        /* Construct the four individual parts of the vector sum: */
 +        cprod(sum1vec_part, rotg->vec, sum1vec);      /* sum1vec =   { } x v  */
 +        cprod(sum2vec_part, rotg->vec, sum2vec);      /* sum2vec =   { } x v  */
 +        svmul(sum3, rotg->vec, sum3vec);              /* sum3vec =   { } . v  */
 +        svmul(sum4, rotg->vec, sum4vec);              /* sum4vec =   { } . v  */
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        for (m = 0; m < DIM; m++)
 +        {
 +            erg->f_rot_loc[j][m] = rotg->k * (-sum1vec[m] + sum2vec[m] - sum3vec[m] + 0.5*sum4vec[m]);
 +        }
 +
 +#ifdef SUM_PARTS
 +        fprintf(stderr, "sum1: %15.8f %15.8f %15.8f\n",    -rotg->k*sum1vec[XX],    -rotg->k*sum1vec[YY],    -rotg->k*sum1vec[ZZ]);
 +        fprintf(stderr, "sum2: %15.8f %15.8f %15.8f\n",     rotg->k*sum2vec[XX],     rotg->k*sum2vec[YY],     rotg->k*sum2vec[ZZ]);
 +        fprintf(stderr, "sum3: %15.8f %15.8f %15.8f\n",    -rotg->k*sum3vec[XX],    -rotg->k*sum3vec[YY],    -rotg->k*sum3vec[ZZ]);
 +        fprintf(stderr, "sum4: %15.8f %15.8f %15.8f\n", 0.5*rotg->k*sum4vec[XX], 0.5*rotg->k*sum4vec[YY], 0.5*rotg->k*sum4vec[ZZ]);
 +#endif
 +
 +        PRINT_FORCE_J
 +
 +    } /* END of loop over local atoms */
 +
 +    return V;
 +}
 +
 +
 +static real do_flex_lowlevel(
 +        t_rotgrp *rotg,
 +        real      sigma,     /* The Gaussian width sigma                      */
 +        rvec      x[],
 +        gmx_bool  bOutstepRot,
 +        gmx_bool  bOutstepSlab,
 +        matrix    box)
 +{
 +    int             count, ic, ifit, ii, j, m, n, islab, iigrp;
 +    rvec            xj, yj0;                /* current and reference position                */
 +    rvec            xcn, ycn;               /* the current and the reference slab centers    */
 +    rvec            yj0_ycn;                /* yj0 - ycn                                     */
 +    rvec            xj_xcn;                 /* xj - xcn                                      */
 +    rvec            qjn, fit_qjn;           /* q_i^n                                         */
 +    rvec            sum_n1, sum_n2;         /* Two contributions to the rotation force       */
 +    rvec            innersumvec;            /* Inner part of sum_n2                          */
 +    rvec            s_n;
 +    rvec            force_n;                /* Single force from slab n on one atom          */
 +    rvec            force_n1, force_n2;     /* First and second part of force_n              */
 +    rvec            tmpvec, tmpvec2, tmp_f; /* Helper variables                              */
 +    real            V;                      /* The rotation potential energy                 */
 +    real            OOsigma2;               /* 1/(sigma^2)                                   */
 +    real            beta;                   /* beta_n(xj)                                    */
 +    real            bjn, fit_bjn;           /* b_j^n                                         */
 +    real            gaussian_xj;            /* Gaussian weight gn(xj)                        */
 +    real            betan_xj_sigma2;
 +    real            mj, wj;                 /* Mass-weighting of the positions               */
 +    real            N_M;                    /* N/M                                           */
 +    gmx_enfrotgrp_t erg;                    /* Pointer to enforced rotation group data       */
 +    gmx_bool        bCalcPotFit;
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* Pre-calculate the inner sums, so that we do not have to calculate
 +     * them again for every atom */
 +    flex_precalc_inner_sum(rotg);
 +
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
 +
 +    /********************************************************/
 +    /* Main loop over all local atoms of the rotation group */
 +    /********************************************************/
 +    OOsigma2 = 1.0/(sigma*sigma);
 +    N_M      = rotg->nat * erg->invmass;
 +    V        = 0.0;
 +    for (j = 0; j < erg->nat_loc; j++)
 +    {
 +        /* Local index of a rotation group atom  */
 +        ii = erg->ind_loc[j];
 +        /* Position of this atom in the collective array */
 +        iigrp = erg->xc_ref_ind[j];
 +        /* Mass-weighting */
 +        mj = erg->mc[iigrp];  /* need the unsorted mass here */
 +        wj = N_M*mj;
 +
 +        /* Current position of this atom: x[ii][XX/YY/ZZ]
 +         * Note that erg->xc_center contains the center of mass in case the flex-t
 +         * potential was chosen. For the flex potential erg->xc_center must be
 +         * zero. */
 +        rvec_sub(x[ii], erg->xc_center, xj);
 +
 +        /* Shift this atom such that it is near its reference */
 +        shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
 +
 +        /* Determine the slabs to loop over, i.e. the ones with contributions
 +         * larger than min_gaussian */
 +        count = get_single_atom_gaussians(xj, rotg);
 +
 +        clear_rvec(sum_n1);
 +        clear_rvec(sum_n2);
 +
 +        /* Loop over the relevant slabs for this atom */
 +        for (ic = 0; ic < count; ic++)
 +        {
 +            n = erg->gn_slabind[ic];
 +
 +            /* Get the precomputed Gaussian for xj in slab n */
 +            gaussian_xj = erg->gn_atom[ic];
 +
 +            islab = n - erg->slab_first; /* slab index */
 +
 +            /* The (unrotated) reference position of this atom is saved in yj0: */
 +            copy_rvec(rotg->x_ref[iigrp], yj0);
 +
 +            beta = calc_beta(xj, rotg, n);
 +
 +            /* The current center of this slab is saved in xcn: */
 +            copy_rvec(erg->slab_center[islab], xcn);
 +            /* ... and the reference center in ycn: */
 +            copy_rvec(erg->slab_center_ref[islab+erg->slab_buffer], ycn);
 +
 +            rvec_sub(yj0, ycn, yj0_ycn); /* yj0_ycn = yj0 - ycn */
 +
 +            /* Rotate: */
 +            mvmul(erg->rotmat, yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
 +
 +            /* Subtract the slab center from xj */
 +            rvec_sub(xj, xcn, xj_xcn);           /* xj_xcn = xj - xcn         */
 +
 +            /* Calculate qjn */
 +            cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
 +
 +            /*                         *         v x Omega.(yj0-ycn)    */
 +            unitv(tmpvec, qjn);       /*  qjn = ---------------------   */
 +                                      /*        |v x Omega.(yj0-ycn)|   */
 +
 +            bjn = iprod(qjn, xj_xcn); /* bjn = qjn * (xj - xcn) */
 +
 +            /*********************************/
 +            /* Add to the rotation potential */
 +            /*********************************/
 +            V += 0.5*rotg->k*wj*gaussian_xj*sqr(bjn);
 +
 +            /* If requested, also calculate the potential for a set of angles
 +             * near the current reference angle */
 +            if (bCalcPotFit)
 +            {
 +                for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +                {
 +                    /* As above calculate Omega.(yj0-ycn), now for the other angles */
 +                    mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
 +                    /* As above calculate qjn */
 +                    cprod(rotg->vec, tmpvec2, tmpvec);                       /* tmpvec= v x Omega.(yj0-ycn) */
 +                    /*                                                        *             v x Omega.(yj0-ycn)    */
 +                    unitv(tmpvec, fit_qjn);                                  /*  fit_qjn = ---------------------   */
 +                                                                             /*            |v x Omega.(yj0-ycn)|   */
 +                    fit_bjn = iprod(fit_qjn, xj_xcn);                        /* fit_bjn = fit_qjn * (xj - xcn) */
 +                    /* Add to the rotation potential for this angle */
 +                    erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*gaussian_xj*sqr(fit_bjn);
 +                }
 +            }
 +
 +            /****************************************************************/
 +            /* sum_n1 will typically be the main contribution to the force: */
 +            /****************************************************************/
 +            betan_xj_sigma2 = beta*OOsigma2;  /*  beta_n(xj)/sigma^2  */
 +
 +            /* The next lines calculate
 +             *  qjn - (bjn*beta(xj)/(2sigma^2))v  */
 +            svmul(bjn*0.5*betan_xj_sigma2, rotg->vec, tmpvec2);
 +            rvec_sub(qjn, tmpvec2, tmpvec);
 +
 +            /* Multiply with gn(xj)*bjn: */
 +            svmul(gaussian_xj*bjn, tmpvec, tmpvec2);
 +
 +            /* Sum over n: */
 +            rvec_inc(sum_n1, tmpvec2);
 +
 +            /* We already have precalculated the Sn term for slab n */
 +            copy_rvec(erg->slab_innersumvec[islab], s_n);
 +            /*                                                             *          beta_n(xj)              */
 +            svmul(betan_xj_sigma2*iprod(s_n, xj_xcn), rotg->vec, tmpvec); /* tmpvec = ---------- s_n (xj-xcn) */
 +                                                                          /*            sigma^2               */
 +
 +            rvec_sub(s_n, tmpvec, innersumvec);
 +
 +            /* We can safely divide by slab_weights since we check in get_slab_centers
 +             * that it is non-zero. */
 +            svmul(gaussian_xj/erg->slab_weights[islab], innersumvec, innersumvec);
 +
 +            rvec_add(sum_n2, innersumvec, sum_n2);
 +
 +            /* Calculate the torque: */
 +            if (bOutstepRot)
 +            {
 +                /* The force on atom ii from slab n only: */
 +                svmul(-rotg->k*wj, tmpvec2, force_n1);     /* part 1 */
 +                svmul( rotg->k*mj, innersumvec, force_n2); /* part 2 */
 +                rvec_add(force_n1, force_n2, force_n);
 +                erg->slab_torque_v[islab] += torque(rotg->vec, force_n, xj, xcn);
 +            }
 +        } /* END of loop over slabs */
 +
 +        /* Put both contributions together: */
 +        svmul(wj, sum_n1, sum_n1);
 +        svmul(mj, sum_n2, sum_n2);
 +        rvec_sub(sum_n2, sum_n1, tmp_f); /* F = -grad V */
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        for (m = 0; m < DIM; m++)
 +        {
 +            erg->f_rot_loc[j][m] = rotg->k*tmp_f[m];
 +        }
 +
 +        PRINT_FORCE_J
 +
 +    } /* END of loop over local atoms */
 +
 +    return V;
 +}
 +
 +#ifdef PRINT_COORDS
 +static void print_coordinates(t_rotgrp *rotg, rvec x[], matrix box, int step)
 +{
 +    int             i;
 +    static FILE    *fp;
 +    static char     buf[STRLEN];
 +    static gmx_bool bFirst = 1;
 +
 +
 +    if (bFirst)
 +    {
 +        sprintf(buf, "coords%d.txt", cr->nodeid);
 +        fp     = fopen(buf, "w");
 +        bFirst = 0;
 +    }
 +
 +    fprintf(fp, "\nStep %d\n", step);
 +    fprintf(fp, "box: %f %f %f %f %f %f %f %f %f\n",
 +            box[XX][XX], box[XX][YY], box[XX][ZZ],
 +            box[YY][XX], box[YY][YY], box[YY][ZZ],
 +            box[ZZ][XX], box[ZZ][ZZ], box[ZZ][ZZ]);
 +    for (i = 0; i < rotg->nat; i++)
 +    {
 +        fprintf(fp, "%4d  %f %f %f\n", i,
 +                erg->xc[i][XX], erg->xc[i][YY], erg->xc[i][ZZ]);
 +    }
 +    fflush(fp);
 +
 +}
 +#endif
 +
 +
 +static int projection_compare(const void *a, const void *b)
 +{
 +    sort_along_vec_t *xca, *xcb;
 +
 +
 +    xca = (sort_along_vec_t *)a;
 +    xcb = (sort_along_vec_t *)b;
 +
 +    if (xca->xcproj < xcb->xcproj)
 +    {
 +        return -1;
 +    }
 +    else if (xca->xcproj > xcb->xcproj)
 +    {
 +        return 1;
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +
 +static void sort_collective_coordinates(
 +        t_rotgrp         *rotg, /* Rotation group */
 +        sort_along_vec_t *data) /* Buffer for sorting the positions */
 +{
 +    int             i;
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* The projection of the position vector on the rotation vector is
 +     * the relevant value for sorting. Fill the 'data' structure */
 +    for (i = 0; i < rotg->nat; i++)
 +    {
 +        data[i].xcproj = iprod(erg->xc[i], rotg->vec);  /* sort criterium */
 +        data[i].m      = erg->mc[i];
 +        data[i].ind    = i;
 +        copy_rvec(erg->xc[i], data[i].x    );
 +        copy_rvec(rotg->x_ref[i], data[i].x_ref);
 +    }
 +    /* Sort the 'data' structure */
 +    gmx_qsort(data, rotg->nat, sizeof(sort_along_vec_t), projection_compare);
 +
 +    /* Copy back the sorted values */
 +    for (i = 0; i < rotg->nat; i++)
 +    {
 +        copy_rvec(data[i].x, erg->xc[i]           );
 +        copy_rvec(data[i].x_ref, erg->xc_ref_sorted[i]);
 +        erg->mc_sorted[i]  = data[i].m;
 +        erg->xc_sortind[i] = data[i].ind;
 +    }
 +}
 +
 +
 +/* For each slab, get the first and the last index of the sorted atom
 + * indices */
 +static void get_firstlast_atom_per_slab(t_rotgrp *rotg)
 +{
 +    int             i, islab, n;
 +    real            beta;
 +    gmx_enfrotgrp_t erg;     /* Pointer to enforced rotation group data */
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* Find the first atom that needs to enter the calculation for each slab */
 +    n = erg->slab_first; /* slab */
 +    i = 0;               /* start with the first atom */
 +    do
 +    {
 +        /* Find the first atom that significantly contributes to this slab */
 +        do /* move forward in position until a large enough beta is found */
 +        {
 +            beta = calc_beta(erg->xc[i], rotg, n);
 +            i++;
 +        }
 +        while ((beta < -erg->max_beta) && (i < rotg->nat));
 +        i--;
 +        islab                 = n - erg->slab_first; /* slab index */
 +        erg->firstatom[islab] = i;
 +        /* Proceed to the next slab */
 +        n++;
 +    }
 +    while (n <= erg->slab_last);
 +
 +    /* Find the last atom for each slab */
 +    n = erg->slab_last; /* start with last slab */
 +    i = rotg->nat-1;    /* start with the last atom */
 +    do
 +    {
 +        do  /* move backward in position until a large enough beta is found */
 +        {
 +            beta = calc_beta(erg->xc[i], rotg, n);
 +            i--;
 +        }
 +        while ((beta > erg->max_beta) && (i > -1));
 +        i++;
 +        islab                = n - erg->slab_first; /* slab index */
 +        erg->lastatom[islab] = i;
 +        /* Proceed to the next slab */
 +        n--;
 +    }
 +    while (n >= erg->slab_first);
 +}
 +
 +
 +/* Determine the very first and very last slab that needs to be considered
 + * For the first slab that needs to be considered, we have to find the smallest
 + * n that obeys:
 + *
 + * x_first * v - n*Delta_x <= beta_max
 + *
 + * slab index n, slab distance Delta_x, rotation vector v. For the last slab we
 + * have to find the largest n that obeys
 + *
 + * x_last * v - n*Delta_x >= -beta_max
 + *
 + */
 +static gmx_inline int get_first_slab(
 +        t_rotgrp *rotg,      /* The rotation group (inputrec data) */
 +        real      max_beta,  /* The max_beta value, instead of min_gaussian */
 +        rvec      firstatom) /* First atom after sorting along the rotation vector v */
 +{
 +    /* Find the first slab for the first atom */
 +    return ceil((iprod(firstatom, rotg->vec) - max_beta)/rotg->slab_dist);
 +}
 +
 +
 +static gmx_inline int get_last_slab(
 +        t_rotgrp *rotg,     /* The rotation group (inputrec data) */
 +        real      max_beta, /* The max_beta value, instead of min_gaussian */
 +        rvec      lastatom) /* Last atom along v */
 +{
 +    /* Find the last slab for the last atom */
 +    return floor((iprod(lastatom, rotg->vec) + max_beta)/rotg->slab_dist);
 +}
 +
 +
 +static void get_firstlast_slab_check(
 +        t_rotgrp        *rotg,      /* The rotation group (inputrec data) */
 +        t_gmx_enfrotgrp *erg,       /* The rotation group (data only accessible in this file) */
 +        rvec             firstatom, /* First atom after sorting along the rotation vector v */
 +        rvec             lastatom,  /* Last atom along v */
 +        int              g)         /* The rotation group number */
 +{
 +    erg->slab_first = get_first_slab(rotg, erg->max_beta, firstatom);
 +    erg->slab_last  = get_last_slab(rotg, erg->max_beta, lastatom);
 +
 +    /* Check whether we have reference data to compare against */
 +    if (erg->slab_first < erg->slab_first_ref)
 +    {
 +        gmx_fatal(FARGS, "%s No reference data for first slab (n=%d), unable to proceed.",
 +                  RotStr, erg->slab_first);
 +    }
 +
 +    /* Check whether we have reference data to compare against */
 +    if (erg->slab_last > erg->slab_last_ref)
 +    {
 +        gmx_fatal(FARGS, "%s No reference data for last slab (n=%d), unable to proceed.",
 +                  RotStr, erg->slab_last);
 +    }
 +}
 +
 +
 +/* Enforced rotation with a flexible axis */
 +static void do_flexible(
 +        gmx_bool        bMaster,
 +        gmx_enfrot_t    enfrot,       /* Other rotation data                        */
 +        t_rotgrp       *rotg,         /* The rotation group                         */
 +        int             g,            /* Group number                               */
 +        rvec            x[],          /* The local positions                        */
 +        matrix          box,
 +        double          t,            /* Time in picoseconds                        */
 +        gmx_large_int_t step,         /* The time step                              */
 +        gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
 +        gmx_bool        bOutstepSlab) /* Output per-slab data                       */
 +{
 +    int             l, nslabs;
 +    real            sigma;    /* The Gaussian width sigma */
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* Define the sigma value */
 +    sigma = 0.7*rotg->slab_dist;
 +
 +    /* Sort the collective coordinates erg->xc along the rotation vector. This is
 +     * an optimization for the inner loop. */
 +    sort_collective_coordinates(rotg, enfrot->data);
 +
 +    /* Determine the first relevant slab for the first atom and the last
 +     * relevant slab for the last atom */
 +    get_firstlast_slab_check(rotg, erg, erg->xc[0], erg->xc[rotg->nat-1], g);
 +
 +    /* Determine for each slab depending on the min_gaussian cutoff criterium,
 +     * a first and a last atom index inbetween stuff needs to be calculated */
 +    get_firstlast_atom_per_slab(rotg);
 +
 +    /* Determine the gaussian-weighted center of positions for all slabs */
 +    get_slab_centers(rotg, erg->xc, erg->mc_sorted, g, t, enfrot->out_slabs, bOutstepSlab, FALSE);
 +
 +    /* Clear the torque per slab from last time step: */
 +    nslabs = erg->slab_last - erg->slab_first + 1;
 +    for (l = 0; l < nslabs; l++)
 +    {
 +        erg->slab_torque_v[l] = 0.0;
 +    }
 +
 +    /* Call the rotational forces kernel */
 +    if (rotg->eType == erotgFLEX || rotg->eType == erotgFLEXT)
 +    {
 +        erg->V = do_flex_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
 +    }
 +    else if (rotg->eType == erotgFLEX2 || rotg->eType == erotgFLEX2T)
 +    {
 +        erg->V = do_flex2_lowlevel(rotg, sigma, x, bOutstepRot, bOutstepSlab, box);
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "Unknown flexible rotation type");
 +    }
 +
 +    /* Determine angle by RMSD fit to the reference - Let's hope this */
 +    /* only happens once in a while, since this is not parallelized! */
 +    if (bMaster && (erotgFitPOT != rotg->eFittype) )
 +    {
 +        if (bOutstepRot)
 +        {
 +            /* Fit angle of the whole rotation group */
 +            erg->angle_v = flex_fit_angle(rotg);
 +        }
 +        if (bOutstepSlab)
 +        {
 +            /* Fit angle of each slab */
 +            flex_fit_angle_perslab(g, rotg, t, erg->degangle, enfrot->out_angles);
 +        }
 +    }
 +
 +    /* Lump together the torques from all slabs: */
 +    erg->torque_v = 0.0;
 +    for (l = 0; l < nslabs; l++)
 +    {
 +        erg->torque_v += erg->slab_torque_v[l];
 +    }
 +}
 +
 +
 +/* Calculate the angle between reference and actual rotation group atom,
 + * both projected into a plane perpendicular to the rotation vector: */
 +static void angle(t_rotgrp *rotg,
 +                  rvec      x_act,
 +                  rvec      x_ref,
 +                  real     *alpha,
 +                  real     *weight) /* atoms near the rotation axis should count less than atoms far away */
 +{
 +    rvec xp, xrp;                   /* current and reference positions projected on a plane perpendicular to pg->vec */
 +    rvec dum;
 +
 +
 +    /* Project x_ref and x into a plane through the origin perpendicular to rot_vec: */
 +    /* Project x_ref: xrp = x_ref - (vec * x_ref) * vec */
 +    svmul(iprod(rotg->vec, x_ref), rotg->vec, dum);
 +    rvec_sub(x_ref, dum, xrp);
 +    /* Project x_act: */
 +    svmul(iprod(rotg->vec, x_act), rotg->vec, dum);
 +    rvec_sub(x_act, dum, xp);
 +
 +    /* Retrieve information about which vector precedes. gmx_angle always
 +     * returns a positive angle. */
 +    cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
 +
 +    if (iprod(rotg->vec, dum) >= 0)
 +    {
 +        *alpha = -gmx_angle(xrp, xp);
 +    }
 +    else
 +    {
 +        *alpha = +gmx_angle(xrp, xp);
 +    }
 +
 +    /* Also return the weight */
 +    *weight = norm(xp);
 +}
 +
 +
 +/* Project first vector onto a plane perpendicular to the second vector
 + * dr = dr - (dr.v)v
 + * Note that v must be of unit length.
 + */
 +static gmx_inline void project_onto_plane(rvec dr, const rvec v)
 +{
 +    rvec tmp;
 +
 +
 +    svmul(iprod(dr, v), v, tmp); /* tmp = (dr.v)v */
 +    rvec_dec(dr, tmp);           /* dr = dr - (dr.v)v */
 +}
 +
 +
 +/* Fixed rotation: The rotation reference group rotates around the v axis. */
 +/* The atoms of the actual rotation group are attached with imaginary  */
 +/* springs to the reference atoms.                                     */
 +static void do_fixed(
 +        t_rotgrp       *rotg,         /* The rotation group                         */
 +        rvec            x[],          /* The positions                              */
 +        matrix          box,          /* The simulation box                         */
 +        double          t,            /* Time in picoseconds                        */
 +        gmx_large_int_t step,         /* The time step                              */
 +        gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
 +        gmx_bool        bOutstepSlab) /* Output per-slab data                       */
 +{
 +    int             ifit, j, jj, m;
 +    rvec            dr;
 +    rvec            tmp_f;     /* Force */
 +    real            alpha;     /* a single angle between an actual and a reference position */
 +    real            weight;    /* single weight for a single angle */
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    rvec            xi_xc;     /* xi - xc */
 +    gmx_bool        bCalcPotFit;
 +    rvec            fit_xr_loc;
 +
 +    /* for mass weighting: */
 +    real      wi;              /* Mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +    real      k_wi;            /* k times wi */
 +
 +    gmx_bool  bProject;
 +
 +
 +    erg         = rotg->enfrotgrp;
 +    bProject    = (rotg->eType == erotgPM) || (rotg->eType == erotgPMPF);
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
 +
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Each process calculates the forces on its local atoms */
 +    for (j = 0; j < erg->nat_loc; j++)
 +    {
 +        /* Calculate (x_i-x_c) resp. (x_i-u) */
 +        rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xi_xc);
 +
 +        /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
 +        rvec_sub(erg->xr_loc[j], xi_xc, dr);
 +
 +        if (bProject)
 +        {
 +            project_onto_plane(dr, rotg->vec);
 +        }
 +
 +        /* Mass-weighting */
 +        wi = N_M*erg->m_loc[j];
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        k_wi = rotg->k*wi;
 +        for (m = 0; m < DIM; m++)
 +        {
 +            tmp_f[m]             = k_wi*dr[m];
 +            erg->f_rot_loc[j][m] = tmp_f[m];
 +            erg->V              += 0.5*k_wi*sqr(dr[m]);
 +        }
 +
 +        /* If requested, also calculate the potential for a set of angles
 +         * near the current reference angle */
 +        if (bCalcPotFit)
 +        {
 +            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +            {
 +                /* Index of this rotation group atom with respect to the whole rotation group */
 +                jj = erg->xc_ref_ind[j];
 +
 +                /* Rotate with the alternative angle. Like rotate_local_reference(),
 +                 * just for a single local atom */
 +                mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_xr_loc); /* fit_xr_loc = Omega*(y_i-y_c) */
 +
 +                /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
 +                rvec_sub(fit_xr_loc, xi_xc, dr);
 +
 +                if (bProject)
 +                {
 +                    project_onto_plane(dr, rotg->vec);
 +                }
 +
 +                /* Add to the rotation potential for this angle: */
 +                erg->PotAngleFit->V[ifit] += 0.5*k_wi*norm2(dr);
 +            }
 +        }
 +
 +        if (bOutstepRot)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
 +
 +            /* Calculate the angle between reference and actual rotation group atom. */
 +            angle(rotg, xi_xc, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
 +            erg->angle_v  += alpha * weight;
 +            erg->weight_v += weight;
 +        }
 +        /* If you want enforced rotation to contribute to the virial,
 +         * activate the following lines:
 +            if (MASTER(cr))
 +            {
 +               Add the rotation contribution to the virial
 +              for(j=0; j<DIM; j++)
 +                for(m=0;m<DIM;m++)
 +                  vir[j][m] += 0.5*f[ii][j]*dr[m];
 +            }
 +         */
 +
 +        PRINT_FORCE_J
 +
 +    } /* end of loop over local rotation group atoms */
 +}
 +
 +
 +/* Calculate the radial motion potential and forces */
 +static void do_radial_motion(
 +        t_rotgrp       *rotg,         /* The rotation group                         */
 +        rvec            x[],          /* The positions                              */
 +        matrix          box,          /* The simulation box                         */
 +        double          t,            /* Time in picoseconds                        */
 +        gmx_large_int_t step,         /* The time step                              */
 +        gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
 +        gmx_bool        bOutstepSlab) /* Output per-slab data                       */
 +{
 +    int             j, jj, ifit;
 +    rvec            tmp_f;     /* Force */
 +    real            alpha;     /* a single angle between an actual and a reference position */
 +    real            weight;    /* single weight for a single angle */
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    rvec            xj_u;      /* xj - u */
 +    rvec            tmpvec, fit_tmpvec;
 +    real            fac, fac2, sum = 0.0;
 +    rvec            pj;
 +    gmx_bool        bCalcPotFit;
 +
 +    /* For mass weighting: */
 +    real      wj;              /* Mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +
 +
 +    erg         = rotg->enfrotgrp;
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
 +
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Each process calculates the forces on its local atoms */
 +    for (j = 0; j < erg->nat_loc; j++)
 +    {
 +        /* Calculate (xj-u) */
 +        rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xj_u);  /* xj_u = xj-u */
 +
 +        /* Calculate Omega.(yj0-u) */
 +        cprod(rotg->vec, erg->xr_loc[j], tmpvec);  /* tmpvec = v x Omega.(yj0-u) */
 +
 +        /*                       *         v x Omega.(yj0-u)     */
 +        unitv(tmpvec, pj);      /*  pj = ---------------------   */
 +                                /*       | v x Omega.(yj0-u) |   */
 +
 +        fac  = iprod(pj, xj_u); /* fac = pj.(xj-u) */
 +        fac2 = fac*fac;
 +
 +        /* Mass-weighting */
 +        wj = N_M*erg->m_loc[j];
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        svmul(-rotg->k*wj*fac, pj, tmp_f);
 +        copy_rvec(tmp_f, erg->f_rot_loc[j]);
 +        sum += wj*fac2;
 +
 +        /* If requested, also calculate the potential for a set of angles
 +         * near the current reference angle */
 +        if (bCalcPotFit)
 +        {
 +            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +            {
 +                /* Index of this rotation group atom with respect to the whole rotation group */
 +                jj = erg->xc_ref_ind[j];
 +
 +                /* Rotate with the alternative angle. Like rotate_local_reference(),
 +                 * just for a single local atom */
 +                mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[jj], fit_tmpvec); /* fit_tmpvec = Omega*(yj0-u) */
 +
 +                /* Calculate Omega.(yj0-u) */
 +                cprod(rotg->vec, fit_tmpvec, tmpvec); /* tmpvec = v x Omega.(yj0-u) */
 +                /*                                     *         v x Omega.(yj0-u)     */
 +                unitv(tmpvec, pj);                    /*  pj = ---------------------   */
 +                                                      /*       | v x Omega.(yj0-u) |   */
 +
 +                fac  = iprod(pj, xj_u);               /* fac = pj.(xj-u) */
 +                fac2 = fac*fac;
 +
 +                /* Add to the rotation potential for this angle: */
 +                erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
 +            }
 +        }
 +
 +        if (bOutstepRot)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->torque_v += torque(rotg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
 +
 +            /* Calculate the angle between reference and actual rotation group atom. */
 +            angle(rotg, xj_u, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
 +            erg->angle_v  += alpha * weight;
 +            erg->weight_v += weight;
 +        }
 +
 +        PRINT_FORCE_J
 +
 +    } /* end of loop over local rotation group atoms */
 +    erg->V = 0.5*rotg->k*sum;
 +}
 +
 +
 +/* Calculate the radial motion pivot-free potential and forces */
 +static void do_radial_motion_pf(
 +        t_rotgrp       *rotg,         /* The rotation group                         */
 +        rvec            x[],          /* The positions                              */
 +        matrix          box,          /* The simulation box                         */
 +        double          t,            /* Time in picoseconds                        */
 +        gmx_large_int_t step,         /* The time step                              */
 +        gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
 +        gmx_bool        bOutstepSlab) /* Output per-slab data                       */
 +{
 +    int             i, ii, iigrp, ifit, j;
 +    rvec            xj;          /* Current position */
 +    rvec            xj_xc;       /* xj  - xc  */
 +    rvec            yj0_yc0;     /* yj0 - yc0 */
 +    rvec            tmp_f;       /* Force */
 +    real            alpha;       /* a single angle between an actual and a reference position */
 +    real            weight;      /* single weight for a single angle */
 +    gmx_enfrotgrp_t erg;         /* Pointer to enforced rotation group data */
 +    rvec            tmpvec, tmpvec2;
 +    rvec            innersumvec; /* Precalculation of the inner sum */
 +    rvec            innersumveckM;
 +    real            fac, fac2, V = 0.0;
 +    rvec            qi, qj;
 +    gmx_bool        bCalcPotFit;
 +
 +    /* For mass weighting: */
 +    real      mj, wi, wj;      /* Mass-weighting of the positions */
 +    real      N_M;             /* N/M */
 +
 +
 +    erg         = rotg->enfrotgrp;
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
 +
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Get the current center of the rotation group: */
 +    get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
 +
 +    /* Precalculate Sum_i [ wi qi.(xi-xc) qi ] which is needed for every single j */
 +    clear_rvec(innersumvec);
 +    for (i = 0; i < rotg->nat; i++)
 +    {
 +        /* Mass-weighting */
 +        wi = N_M*erg->mc[i];
 +
 +        /* Calculate qi. Note that xc_ref_center has already been subtracted from
 +         * x_ref in init_rot_group.*/
 +        mvmul(erg->rotmat, rotg->x_ref[i], tmpvec); /* tmpvec  = Omega.(yi0-yc0) */
 +
 +        cprod(rotg->vec, tmpvec, tmpvec2);          /* tmpvec2 = v x Omega.(yi0-yc0) */
 +
 +        /*                                             *         v x Omega.(yi0-yc0)     */
 +        unitv(tmpvec2, qi);                           /*  qi = -----------------------   */
 +                                                      /*       | v x Omega.(yi0-yc0) |   */
 +
 +        rvec_sub(erg->xc[i], erg->xc_center, tmpvec); /* tmpvec = xi-xc */
 +
 +        svmul(wi*iprod(qi, tmpvec), qi, tmpvec2);
 +
 +        rvec_inc(innersumvec, tmpvec2);
 +    }
 +    svmul(rotg->k*erg->invmass, innersumvec, innersumveckM);
 +
 +    /* Each process calculates the forces on its local atoms */
 +    for (j = 0; j < erg->nat_loc; j++)
 +    {
 +        /* Local index of a rotation group atom  */
 +        ii = erg->ind_loc[j];
 +        /* Position of this atom in the collective array */
 +        iigrp = erg->xc_ref_ind[j];
 +        /* Mass-weighting */
 +        mj = erg->mc[iigrp];  /* need the unsorted mass here */
 +        wj = N_M*mj;
 +
 +        /* Current position of this atom: x[ii][XX/YY/ZZ] */
 +        copy_rvec(x[ii], xj);
 +
 +        /* Shift this atom such that it is near its reference */
 +        shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
 +
 +        /* The (unrotated) reference position is yj0. yc0 has already
 +         * been subtracted in init_rot_group */
 +        copy_rvec(rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0      */
 +
 +        /* Calculate Omega.(yj0-yc0) */
 +        mvmul(erg->rotmat, yj0_yc0, tmpvec2); /* tmpvec2 = Omega.(yj0 - yc0)  */
 +
 +        cprod(rotg->vec, tmpvec2, tmpvec);    /* tmpvec = v x Omega.(yj0-yc0) */
 +
 +        /*                     *         v x Omega.(yj0-yc0)     */
 +        unitv(tmpvec, qj);    /*  qj = -----------------------   */
 +                              /*       | v x Omega.(yj0-yc0) |   */
 +
 +        /* Calculate (xj-xc) */
 +        rvec_sub(xj, erg->xc_center, xj_xc); /* xj_xc = xj-xc */
 +
 +        fac  = iprod(qj, xj_xc);             /* fac = qj.(xj-xc) */
 +        fac2 = fac*fac;
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        svmul(-rotg->k*wj*fac, qj, tmp_f); /* part 1 of force */
 +        svmul(mj, innersumveckM, tmpvec);  /* part 2 of force */
 +        rvec_inc(tmp_f, tmpvec);
 +        copy_rvec(tmp_f, erg->f_rot_loc[j]);
 +        V += wj*fac2;
 +
 +        /* If requested, also calculate the potential for a set of angles
 +         * near the current reference angle */
 +        if (bCalcPotFit)
 +        {
 +            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +            {
 +                /* Rotate with the alternative angle. Like rotate_local_reference(),
 +                 * just for a single local atom */
 +                mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, tmpvec2); /* tmpvec2 = Omega*(yj0-yc0) */
 +
 +                /* Calculate Omega.(yj0-u) */
 +                cprod(rotg->vec, tmpvec2, tmpvec); /* tmpvec = v x Omega.(yj0-yc0) */
 +                /*                                  *         v x Omega.(yj0-yc0)     */
 +                unitv(tmpvec, qj);                 /*  qj = -----------------------   */
 +                                                   /*       | v x Omega.(yj0-yc0) |   */
 +
 +                fac  = iprod(qj, xj_xc);           /* fac = qj.(xj-xc) */
 +                fac2 = fac*fac;
 +
 +                /* Add to the rotation potential for this angle: */
 +                erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*fac2;
 +            }
 +        }
 +
 +        if (bOutstepRot)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->torque_v += torque(rotg->vec, tmp_f, xj, erg->xc_center);
 +
 +            /* Calculate the angle between reference and actual rotation group atom. */
 +            angle(rotg, xj_xc, yj0_yc0, &alpha, &weight);  /* angle in rad, weighted */
 +            erg->angle_v  += alpha * weight;
 +            erg->weight_v += weight;
 +        }
 +
 +        PRINT_FORCE_J
 +
 +    } /* end of loop over local rotation group atoms */
 +    erg->V = 0.5*rotg->k*V;
 +}
 +
 +
 +/* Precalculate the inner sum for the radial motion 2 forces */
 +static void radial_motion2_precalc_inner_sum(t_rotgrp  *rotg, rvec innersumvec)
 +{
 +    int             i;
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    rvec            xi_xc;     /* xj - xc */
 +    rvec            tmpvec, tmpvec2;
 +    real            fac, fac2;
 +    rvec            ri, si;
 +    real            siri;
 +    rvec            v_xi_xc;   /* v x (xj - u) */
 +    real            psii, psiistar;
 +    real            wi;        /* Mass-weighting of the positions */
 +    real            N_M;       /* N/M */
 +    rvec            sumvec;
 +
 +    erg = rotg->enfrotgrp;
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Loop over the collective set of positions */
 +    clear_rvec(sumvec);
 +    for (i = 0; i < rotg->nat; i++)
 +    {
 +        /* Mass-weighting */
 +        wi = N_M*erg->mc[i];
 +
 +        rvec_sub(erg->xc[i], erg->xc_center, xi_xc); /* xi_xc = xi-xc         */
 +
 +        /* Calculate ri. Note that xc_ref_center has already been subtracted from
 +         * x_ref in init_rot_group.*/
 +        mvmul(erg->rotmat, rotg->x_ref[i], ri);      /* ri  = Omega.(yi0-yc0) */
 +
 +        cprod(rotg->vec, xi_xc, v_xi_xc);            /* v_xi_xc = v x (xi-u)  */
 +
 +        fac = norm2(v_xi_xc);
 +        /*                                 *                      1           */
 +        psiistar = 1.0/(fac + rotg->eps); /* psiistar = --------------------- */
 +                                          /*            |v x (xi-xc)|^2 + eps */
 +
 +        psii = gmx_invsqrt(fac);          /*                 1                */
 +                                          /*  psii    = -------------         */
 +                                          /*            |v x (xi-xc)|         */
 +
 +        svmul(psii, v_xi_xc, si);         /*  si = psii * (v x (xi-xc) )     */
 +
 +        fac  = iprod(v_xi_xc, ri);        /* fac = (v x (xi-xc)).ri */
 +        fac2 = fac*fac;
 +
 +        siri = iprod(si, ri);                       /* siri = si.ri           */
 +
 +        svmul(psiistar/psii, ri, tmpvec);
 +        svmul(psiistar*psiistar/(psii*psii*psii) * siri, si, tmpvec2);
 +        rvec_dec(tmpvec, tmpvec2);
 +        cprod(tmpvec, rotg->vec, tmpvec2);
 +
 +        svmul(wi*siri, tmpvec2, tmpvec);
 +
 +        rvec_inc(sumvec, tmpvec);
 +    }
 +    svmul(rotg->k*erg->invmass, sumvec, innersumvec);
 +}
 +
 +
 +/* Calculate the radial motion 2 potential and forces */
 +static void do_radial_motion2(
 +        t_rotgrp       *rotg,         /* The rotation group                         */
 +        rvec            x[],          /* The positions                              */
 +        matrix          box,          /* The simulation box                         */
 +        double          t,            /* Time in picoseconds                        */
 +        gmx_large_int_t step,         /* The time step                              */
 +        gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
 +        gmx_bool        bOutstepSlab) /* Output per-slab data                       */
 +{
 +    int             ii, iigrp, ifit, j;
 +    rvec            xj;        /* Position */
 +    real            alpha;     /* a single angle between an actual and a reference position */
 +    real            weight;    /* single weight for a single angle */
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    rvec            xj_u;      /* xj - u */
 +    rvec            yj0_yc0;   /* yj0 -yc0 */
 +    rvec            tmpvec, tmpvec2;
 +    real            fac, fit_fac, fac2, Vpart = 0.0;
 +    rvec            rj, fit_rj, sj;
 +    real            sjrj;
 +    rvec            v_xj_u;    /* v x (xj - u) */
 +    real            psij, psijstar;
 +    real            mj, wj;    /* For mass-weighting of the positions */
 +    real            N_M;       /* N/M */
 +    gmx_bool        bPF;
 +    rvec            innersumvec;
 +    gmx_bool        bCalcPotFit;
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    bPF         = rotg->eType == erotgRM2PF;
 +    bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == rotg->eFittype);
 +
 +
 +    clear_rvec(yj0_yc0); /* Make the compiler happy */
 +
 +    clear_rvec(innersumvec);
 +    if (bPF)
 +    {
 +        /* For the pivot-free variant we have to use the current center of
 +         * mass of the rotation group instead of the pivot u */
 +        get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
 +
 +        /* Also, we precalculate the second term of the forces that is identical
 +         * (up to the weight factor mj) for all forces */
 +        radial_motion2_precalc_inner_sum(rotg, innersumvec);
 +    }
 +
 +    N_M = rotg->nat * erg->invmass;
 +
 +    /* Each process calculates the forces on its local atoms */
 +    for (j = 0; j < erg->nat_loc; j++)
 +    {
 +        if (bPF)
 +        {
 +            /* Local index of a rotation group atom  */
 +            ii = erg->ind_loc[j];
 +            /* Position of this atom in the collective array */
 +            iigrp = erg->xc_ref_ind[j];
 +            /* Mass-weighting */
 +            mj = erg->mc[iigrp];
 +
 +            /* Current position of this atom: x[ii] */
 +            copy_rvec(x[ii], xj);
 +
 +            /* Shift this atom such that it is near its reference */
 +            shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
 +
 +            /* The (unrotated) reference position is yj0. yc0 has already
 +             * been subtracted in init_rot_group */
 +            copy_rvec(rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0  */
 +
 +            /* Calculate Omega.(yj0-yc0) */
 +            mvmul(erg->rotmat, yj0_yc0, rj);         /* rj = Omega.(yj0-yc0)  */
 +        }
 +        else
 +        {
 +            mj = erg->m_loc[j];
 +            copy_rvec(erg->x_loc_pbc[j], xj);
 +            copy_rvec(erg->xr_loc[j], rj);           /* rj = Omega.(yj0-u)    */
 +        }
 +        /* Mass-weighting */
 +        wj = N_M*mj;
 +
 +        /* Calculate (xj-u) resp. (xj-xc) */
 +        rvec_sub(xj, erg->xc_center, xj_u);          /* xj_u = xj-u           */
 +
 +        cprod(rotg->vec, xj_u, v_xj_u);              /* v_xj_u = v x (xj-u)   */
 +
 +        fac = norm2(v_xj_u);
 +        /*                                 *                      1           */
 +        psijstar = 1.0/(fac + rotg->eps); /*  psistar = --------------------  */
 +                                          /*            |v x (xj-u)|^2 + eps  */
 +
 +        psij = gmx_invsqrt(fac);          /*                 1                */
 +                                          /*  psij    = ------------          */
 +                                          /*            |v x (xj-u)|          */
 +
 +        svmul(psij, v_xj_u, sj);          /*  sj = psij * (v x (xj-u) )       */
 +
 +        fac  = iprod(v_xj_u, rj);         /* fac = (v x (xj-u)).rj */
 +        fac2 = fac*fac;
 +
 +        sjrj = iprod(sj, rj);                        /* sjrj = sj.rj          */
 +
 +        svmul(psijstar/psij, rj, tmpvec);
 +        svmul(psijstar*psijstar/(psij*psij*psij) * sjrj, sj, tmpvec2);
 +        rvec_dec(tmpvec, tmpvec2);
 +        cprod(tmpvec, rotg->vec, tmpvec2);
 +
 +        /* Store the additional force so that it can be added to the force
 +         * array after the normal forces have been evaluated */
 +        svmul(-rotg->k*wj*sjrj, tmpvec2, tmpvec);
 +        svmul(mj, innersumvec, tmpvec2);  /* This is != 0 only for the pivot-free variant */
 +
 +        rvec_add(tmpvec2, tmpvec, erg->f_rot_loc[j]);
 +        Vpart += wj*psijstar*fac2;
 +
 +        /* If requested, also calculate the potential for a set of angles
 +         * near the current reference angle */
 +        if (bCalcPotFit)
 +        {
 +            for (ifit = 0; ifit < rotg->PotAngle_nstep; ifit++)
 +            {
 +                if (bPF)
 +                {
 +                    mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, fit_rj); /* fit_rj = Omega.(yj0-yc0) */
 +                }
 +                else
 +                {
 +                    /* Position of this atom in the collective array */
 +                    iigrp = erg->xc_ref_ind[j];
 +                    /* Rotate with the alternative angle. Like rotate_local_reference(),
 +                     * just for a single local atom */
 +                    mvmul(erg->PotAngleFit->rotmat[ifit], rotg->x_ref[iigrp], fit_rj); /* fit_rj = Omega*(yj0-u) */
 +                }
 +                fit_fac = iprod(v_xj_u, fit_rj);                                       /* fac = (v x (xj-u)).fit_rj */
 +                /* Add to the rotation potential for this angle: */
 +                erg->PotAngleFit->V[ifit] += 0.5*rotg->k*wj*psijstar*fit_fac*fit_fac;
 +            }
 +        }
 +
 +        if (bOutstepRot)
 +        {
 +            /* Add to the torque of this rotation group */
 +            erg->torque_v += torque(rotg->vec, erg->f_rot_loc[j], xj, erg->xc_center);
 +
 +            /* Calculate the angle between reference and actual rotation group atom. */
 +            angle(rotg, xj_u, rj, &alpha, &weight);  /* angle in rad, weighted */
 +            erg->angle_v  += alpha * weight;
 +            erg->weight_v += weight;
 +        }
 +
 +        PRINT_FORCE_J
 +
 +    } /* end of loop over local rotation group atoms */
 +    erg->V = 0.5*rotg->k*Vpart;
 +}
 +
 +
 +/* Determine the smallest and largest position vector (with respect to the
 + * rotation vector) for the reference group */
 +static void get_firstlast_atom_ref(
 +        t_rotgrp  *rotg,
 +        int       *firstindex,
 +        int       *lastindex)
 +{
 +    gmx_enfrotgrp_t erg;              /* Pointer to enforced rotation group data */
 +    int             i;
 +    real            xcproj;           /* The projection of a reference position on the
 +                                         rotation vector */
 +    real            minproj, maxproj; /* Smallest and largest projection on v */
 +
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* Start with some value */
 +    minproj = iprod(rotg->x_ref[0], rotg->vec);
 +    maxproj = minproj;
 +
 +    /* This is just to ensure that it still works if all the atoms of the
 +     * reference structure are situated in a plane perpendicular to the rotation
 +     * vector */
 +    *firstindex = 0;
 +    *lastindex  = rotg->nat-1;
 +
 +    /* Loop over all atoms of the reference group,
 +     * project them on the rotation vector to find the extremes */
 +    for (i = 0; i < rotg->nat; i++)
 +    {
 +        xcproj = iprod(rotg->x_ref[i], rotg->vec);
 +        if (xcproj < minproj)
 +        {
 +            minproj     = xcproj;
 +            *firstindex = i;
 +        }
 +        if (xcproj > maxproj)
 +        {
 +            maxproj    = xcproj;
 +            *lastindex = i;
 +        }
 +    }
 +}
 +
 +
 +/* Allocate memory for the slabs */
 +static void allocate_slabs(
 +        t_rotgrp  *rotg,
 +        FILE      *fplog,
 +        int        g,
 +        gmx_bool   bVerbose)
 +{
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +    int             i, nslabs;
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* More slabs than are defined for the reference are never needed */
 +    nslabs = erg->slab_last_ref - erg->slab_first_ref + 1;
 +
 +    /* Remember how many we allocated */
 +    erg->nslabs_alloc = nslabs;
 +
 +    if ( (NULL != fplog) && bVerbose)
 +    {
 +        fprintf(fplog, "%s allocating memory to store data for %d slabs (rotation group %d).\n",
 +                RotStr, nslabs, g);
 +    }
 +    snew(erg->slab_center, nslabs);
 +    snew(erg->slab_center_ref, nslabs);
 +    snew(erg->slab_weights, nslabs);
 +    snew(erg->slab_torque_v, nslabs);
 +    snew(erg->slab_data, nslabs);
 +    snew(erg->gn_atom, nslabs);
 +    snew(erg->gn_slabind, nslabs);
 +    snew(erg->slab_innersumvec, nslabs);
 +    for (i = 0; i < nslabs; i++)
 +    {
 +        snew(erg->slab_data[i].x, rotg->nat);
 +        snew(erg->slab_data[i].ref, rotg->nat);
 +        snew(erg->slab_data[i].weight, rotg->nat);
 +    }
 +    snew(erg->xc_ref_sorted, rotg->nat);
 +    snew(erg->xc_sortind, rotg->nat);
 +    snew(erg->firstatom, nslabs);
 +    snew(erg->lastatom, nslabs);
 +}
 +
 +
 +/* From the extreme coordinates of the reference group, determine the first
 + * and last slab of the reference. We can never have more slabs in the real
 + * simulation than calculated here for the reference.
 + */
 +static void get_firstlast_slab_ref(t_rotgrp *rotg, real mc[], int ref_firstindex, int ref_lastindex)
 +{
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +    int             first, last, firststart;
 +    rvec            dummy;
 +
 +
 +    erg        = rotg->enfrotgrp;
 +    first      = get_first_slab(rotg, erg->max_beta, rotg->x_ref[ref_firstindex]);
 +    last       = get_last_slab( rotg, erg->max_beta, rotg->x_ref[ref_lastindex ]);
 +    firststart = first;
 +
 +    while (get_slab_weight(first, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
 +    {
 +        first--;
 +    }
 +    erg->slab_first_ref = first+1;
 +    while (get_slab_weight(last, rotg, rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
 +    {
 +        last++;
 +    }
 +    erg->slab_last_ref  = last-1;
 +
 +    erg->slab_buffer = firststart - erg->slab_first_ref;
 +}
 +
 +
 +/* Special version of copy_rvec:
 + * During the copy procedure of xcurr to b, the correct PBC image is chosen
 + * such that the copied vector ends up near its reference position xref */
 +static inline void copy_correct_pbc_image(
 +        const rvec  xcurr,  /* copy vector xcurr ...                */
 +        rvec        b,      /* ... to b ...                         */
 +        const rvec  xref,   /* choosing the PBC image such that b ends up near xref */
 +        matrix      box,
 +        int         npbcdim)
 +{
 +    rvec  dx;
 +    int   d, m;
 +    ivec  shift;
 +
 +
 +    /* Shortest PBC distance between the atom and its reference */
 +    rvec_sub(xcurr, xref, dx);
 +
 +    /* Determine the shift for this atom */
 +    clear_ivec(shift);
 +    for (m = npbcdim-1; m >= 0; m--)
 +    {
 +        while (dx[m] < -0.5*box[m][m])
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                dx[d] += box[m][d];
 +            }
 +            shift[m]++;
 +        }
 +        while (dx[m] >= 0.5*box[m][m])
 +        {
 +            for (d = 0; d < DIM; d++)
 +            {
 +                dx[d] -= box[m][d];
 +            }
 +            shift[m]--;
 +        }
 +    }
 +
 +    /* Apply the shift to the position */
 +    copy_rvec(xcurr, b);
 +    shift_single_coord(box, b, shift);
 +}
 +
 +
 +static void init_rot_group(FILE *fplog, t_commrec *cr, int g, t_rotgrp *rotg,
 +                           rvec *x, gmx_mtop_t *mtop, gmx_bool bVerbose, FILE *out_slabs, matrix box,
-     rvec                  coord, *xdum;
++                           t_inputrec *ir, gmx_bool bOutputCenters)
 +{
 +    int                   i, ii;
-         /* Save the original (whole) set of positions such that later the
-          * molecule can always be made whole again */
++    rvec                  coord, xref, *xdum;
 +    gmx_bool              bFlex, bColl;
 +    t_atom               *atom;
 +    gmx_enfrotgrp_t       erg; /* Pointer to enforced rotation group data */
 +    int                   ref_firstindex, ref_lastindex;
 +    gmx_mtop_atomlookup_t alook = NULL;
 +    real                  mass, totalmass;
 +    real                  start = 0.0;
++    double                t_start;
 +
 +
 +    /* Do we have a flexible axis? */
 +    bFlex = ISFLEX(rotg);
 +    /* Do we use a global set of coordinates? */
 +    bColl = ISCOLL(rotg);
 +
 +    erg = rotg->enfrotgrp;
 +
 +    /* Allocate space for collective coordinates if needed */
 +    if (bColl)
 +    {
 +        snew(erg->xc, rotg->nat);
 +        snew(erg->xc_shifts, rotg->nat);
 +        snew(erg->xc_eshifts, rotg->nat);
-         if (MASTER(cr))
-         {
-             for (i = 0; i < rotg->nat; i++)
-             {
-                 ii = rotg->ind[i];
-                 copy_correct_pbc_image(x[ii], erg->xc_old[i], rotg->x_ref[i], box, 3);
-             }
-         }
- #ifdef GMX_MPI
-         if (PAR(cr))
-         {
-             gmx_bcast(rotg->nat*sizeof(erg->xc_old[0]), erg->xc_old, cr);
-         }
- #endif
 +        snew(erg->xc_old, rotg->nat);
-             init_rot_group(fplog, cr, g, rotg, x_pbc, mtop, bVerbose, er->out_slabs, box,
 +
 +        if (rotg->eFittype == erotgFitNORM)
 +        {
 +            snew(erg->xc_ref_length, rotg->nat); /* in case fit type NORM is chosen */
 +            snew(erg->xc_norm, rotg->nat);
 +        }
 +    }
 +    else
 +    {
 +        snew(erg->xr_loc, rotg->nat);
 +        snew(erg->x_loc_pbc, rotg->nat);
 +    }
 +
 +    snew(erg->f_rot_loc, rotg->nat);
 +    snew(erg->xc_ref_ind, rotg->nat);
 +
 +    /* Make space for the calculation of the potential at other angles (used
 +     * for fitting only) */
 +    if (erotgFitPOT == rotg->eFittype)
 +    {
 +        snew(erg->PotAngleFit, 1);
 +        snew(erg->PotAngleFit->degangle, rotg->PotAngle_nstep);
 +        snew(erg->PotAngleFit->V, rotg->PotAngle_nstep);
 +        snew(erg->PotAngleFit->rotmat, rotg->PotAngle_nstep);
 +
 +        /* Get the set of angles around the reference angle */
 +        start = -0.5 * (rotg->PotAngle_nstep - 1)*rotg->PotAngle_step;
 +        for (i = 0; i < rotg->PotAngle_nstep; i++)
 +        {
 +            erg->PotAngleFit->degangle[i] = start + i*rotg->PotAngle_step;
 +        }
 +    }
 +    else
 +    {
 +        erg->PotAngleFit = NULL;
 +    }
 +
 +    /* xc_ref_ind needs to be set to identity in the serial case */
 +    if (!PAR(cr))
 +    {
 +        for (i = 0; i < rotg->nat; i++)
 +        {
 +            erg->xc_ref_ind[i] = i;
 +        }
 +    }
 +
 +    /* Copy the masses so that the center can be determined. For all types of
 +     * enforced rotation, we store the masses in the erg->mc array. */
 +    if (rotg->bMassW)
 +    {
 +        alook = gmx_mtop_atomlookup_init(mtop);
 +    }
 +    snew(erg->mc, rotg->nat);
 +    if (bFlex)
 +    {
 +        snew(erg->mc_sorted, rotg->nat);
 +    }
 +    if (!bColl)
 +    {
 +        snew(erg->m_loc, rotg->nat);
 +    }
 +    totalmass = 0.0;
 +    for (i = 0; i < rotg->nat; i++)
 +    {
 +        if (rotg->bMassW)
 +        {
 +            gmx_mtop_atomnr_to_atom(alook, rotg->ind[i], &atom);
 +            mass = atom->m;
 +        }
 +        else
 +        {
 +            mass = 1.0;
 +        }
 +        erg->mc[i] = mass;
 +        totalmass += mass;
 +    }
 +    erg->invmass = 1.0/totalmass;
 +
 +    if (rotg->bMassW)
 +    {
 +        gmx_mtop_atomlookup_destroy(alook);
 +    }
 +
 +    /* Set xc_ref_center for any rotation potential */
 +    if ((rotg->eType == erotgISO) || (rotg->eType == erotgPM) || (rotg->eType == erotgRM) || (rotg->eType == erotgRM2))
 +    {
 +        /* Set the pivot point for the fixed, stationary-axis potentials. This
 +         * won't change during the simulation */
 +        copy_rvec(rotg->pivot, erg->xc_ref_center);
 +        copy_rvec(rotg->pivot, erg->xc_center    );
 +    }
 +    else
 +    {
 +        /* Center of the reference positions */
 +        get_center(rotg->x_ref, erg->mc, rotg->nat, erg->xc_ref_center);
 +
 +        /* Center of the actual positions */
 +        if (MASTER(cr))
 +        {
 +            snew(xdum, rotg->nat);
 +            for (i = 0; i < rotg->nat; i++)
 +            {
 +                ii = rotg->ind[i];
 +                copy_rvec(x[ii], xdum[i]);
 +            }
 +            get_center(xdum, erg->mc, rotg->nat, erg->xc_center);
 +            sfree(xdum);
 +        }
 +#ifdef GMX_MPI
 +        if (PAR(cr))
 +        {
 +            gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
 +        }
 +#endif
 +    }
++    
++    if (bColl)
++    {
++        /* Save the original (whole) set of positions in xc_old such that at later 
++         * steps the rotation group can always be made whole again. If the simulation is
++         * restarted, we compute the starting reference positions (given the time)
++         * and assume that the correct PBC image of each position is the one nearest
++         * to the current reference */
++        if (MASTER(cr))
++        {
++            /* Calculate the rotation matrix for this angle: */
++            t_start       = ir->init_t + ir->init_step*ir->delta_t;
++            erg->degangle = rotg->rate * t_start;
++            calc_rotmat(rotg->vec, erg->degangle, erg->rotmat);
++
++            for (i = 0; i < rotg->nat; i++)
++            {
++                ii = rotg->ind[i];
++
++                /* Subtract pivot, rotate, and add pivot again. This will yield the 
++                 * reference position for time t */
++                rvec_sub(rotg->x_ref[i], erg->xc_ref_center, coord);
++                mvmul(erg->rotmat, coord, xref);
++                rvec_inc(xref, erg->xc_ref_center);
++
++                copy_correct_pbc_image(x[ii], erg->xc_old[i], xref, box, 3);
++            }
++        }
++#ifdef GMX_MPI
++        if (PAR(cr))
++        {
++            gmx_bcast(rotg->nat*sizeof(erg->xc_old[0]), erg->xc_old, cr);
++        }
++#endif
++    }
 +
 +    if ( (rotg->eType != erotgFLEX) && (rotg->eType != erotgFLEX2) )
 +    {
 +        /* Put the reference positions into origin: */
 +        for (i = 0; i < rotg->nat; i++)
 +        {
 +            rvec_dec(rotg->x_ref[i], erg->xc_ref_center);
 +        }
 +    }
 +
 +    /* Enforced rotation with flexible axis */
 +    if (bFlex)
 +    {
 +        /* Calculate maximum beta value from minimum gaussian (performance opt.) */
 +        erg->max_beta = calc_beta_max(rotg->min_gaussian, rotg->slab_dist);
 +
 +        /* Determine the smallest and largest coordinate with respect to the rotation vector */
 +        get_firstlast_atom_ref(rotg, &ref_firstindex, &ref_lastindex);
 +
 +        /* From the extreme coordinates of the reference group, determine the first
 +         * and last slab of the reference. */
 +        get_firstlast_slab_ref(rotg, erg->mc, ref_firstindex, ref_lastindex);
 +
 +        /* Allocate memory for the slabs */
 +        allocate_slabs(rotg, fplog, g, bVerbose);
 +
 +        /* Flexible rotation: determine the reference centers for the rest of the simulation */
 +        erg->slab_first = erg->slab_first_ref;
 +        erg->slab_last  = erg->slab_last_ref;
 +        get_slab_centers(rotg, rotg->x_ref, erg->mc, g, -1, out_slabs, bOutputCenters, TRUE);
 +
 +        /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
 +        if (rotg->eFittype == erotgFitNORM)
 +        {
 +            for (i = 0; i < rotg->nat; i++)
 +            {
 +                rvec_sub(rotg->x_ref[i], erg->xc_ref_center, coord);
 +                erg->xc_ref_length[i] = norm(coord);
 +            }
 +        }
 +    }
 +}
 +
 +
 +extern void dd_make_local_rotation_groups(gmx_domdec_t *dd, t_rot *rot)
 +{
 +    gmx_ga2la_t     ga2la;
 +    int             g;
 +    t_rotgrp       *rotg;
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +
 +    ga2la = dd->ga2la;
 +
 +    for (g = 0; g < rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        erg  = rotg->enfrotgrp;
 +
 +
 +        dd_make_local_group_indices(ga2la, rotg->nat, rotg->ind,
 +                                    &erg->nat_loc, &erg->ind_loc, &erg->nalloc_loc, erg->xc_ref_ind);
 +    }
 +}
 +
 +
 +/* Calculate the size of the MPI buffer needed in reduce_output() */
 +static int calc_mpi_bufsize(t_rot *rot)
 +{
 +    int             g;
 +    int             count_group, count_total;
 +    t_rotgrp       *rotg;
 +    gmx_enfrotgrp_t erg;      /* Pointer to enforced rotation group data */
 +
 +
 +    count_total = 0;
 +    for (g = 0; g < rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        erg  = rotg->enfrotgrp;
 +
 +        /* Count the items that are transferred for this group: */
 +        count_group = 4; /* V, torque, angle, weight */
 +
 +        /* Add the maximum number of slabs for flexible groups */
 +        if (ISFLEX(rotg))
 +        {
 +            count_group += erg->slab_last_ref - erg->slab_first_ref + 1;
 +        }
 +
 +        /* Add space for the potentials at different angles: */
 +        if (erotgFitPOT == rotg->eFittype)
 +        {
 +            count_group += rotg->PotAngle_nstep;
 +        }
 +
 +        /* Add to the total number: */
 +        count_total += count_group;
 +    }
 +
 +    return count_total;
 +}
 +
 +
 +extern void init_rot(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
 +                     t_commrec *cr, rvec *x, matrix box, gmx_mtop_t *mtop, const output_env_t oenv,
 +                     gmx_bool bVerbose, unsigned long Flags)
 +{
 +    t_rot          *rot;
 +    t_rotgrp       *rotg;
 +    int             g;
 +    int             nat_max = 0;  /* Size of biggest rotation group */
 +    gmx_enfrot_t    er;           /* Pointer to the enforced rotation buffer variables */
 +    gmx_enfrotgrp_t erg;          /* Pointer to enforced rotation group data */
 +    rvec           *x_pbc = NULL; /* Space for the pbc-correct atom positions */
 +
 +
 +    if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
 +    {
 +        gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
 +    }
 +
 +    if (MASTER(cr) && bVerbose)
 +    {
 +        fprintf(stdout, "%s Initializing ...\n", RotStr);
 +    }
 +
 +    rot = ir->rot;
 +    snew(rot->enfrot, 1);
 +    er        = rot->enfrot;
 +    er->Flags = Flags;
 +
 +    /* When appending, skip first output to avoid duplicate entries in the data files */
 +    if (er->Flags & MD_APPENDFILES)
 +    {
 +        er->bOut = FALSE;
 +    }
 +    else
 +    {
 +        er->bOut = TRUE;
 +    }
 +
 +    if (MASTER(cr) && er->bOut)
 +    {
 +        please_cite(fplog, "Kutzner2011");
 +    }
 +
 +    /* Output every step for reruns */
 +    if (er->Flags & MD_RERUN)
 +    {
 +        if (NULL != fplog)
 +        {
 +            fprintf(fplog, "%s rerun - will write rotation output every available step.\n", RotStr);
 +        }
 +        rot->nstrout = 1;
 +        rot->nstsout = 1;
 +    }
 +
 +    er->out_slabs = NULL;
 +    if (MASTER(cr) && HaveFlexibleGroups(rot) )
 +    {
 +        er->out_slabs = open_slab_out(opt2fn("-rs", nfile, fnm), rot, oenv);
 +    }
 +
 +    if (MASTER(cr))
 +    {
 +        /* Remove pbc, make molecule whole.
 +         * When ir->bContinuation=TRUE this has already been done, but ok. */
 +        snew(x_pbc, mtop->natoms);
 +        m_rveccopy(mtop->natoms, x, x_pbc);
 +        do_pbc_first_mtop(NULL, ir->ePBC, box, mtop, x_pbc);
 +        /* All molecules will be whole now, but not necessarily in the home box.
 +         * Additionally, if a rotation group consists of more than one molecule
 +         * (e.g. two strands of DNA), each one of them can end up in a different
 +         * periodic box. This is taken care of in init_rot_group.  */
 +    }
 +
 +    for (g = 0; g < rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +
 +        if (NULL != fplog)
 +        {
 +            fprintf(fplog, "%s group %d type '%s'\n", RotStr, g, erotg_names[rotg->eType]);
 +        }
 +
 +        if (rotg->nat > 0)
 +        {
 +            /* Allocate space for the rotation group's data: */
 +            snew(rotg->enfrotgrp, 1);
 +            erg  = rotg->enfrotgrp;
 +
 +            nat_max = max(nat_max, rotg->nat);
 +
 +            if (PAR(cr))
 +            {
 +                erg->nat_loc    = 0;
 +                erg->nalloc_loc = 0;
 +                erg->ind_loc    = NULL;
 +            }
 +            else
 +            {
 +                erg->nat_loc = rotg->nat;
 +                erg->ind_loc = rotg->ind;
 +            }
-         /* Get the reference position. The pivot was already
++            init_rot_group(fplog, cr, g, rotg, x_pbc, mtop, bVerbose, er->out_slabs, box, ir,
 +                           !(er->Flags & MD_APPENDFILES) ); /* Do not output the reference centers
 +                                                             * again if we are appending */
 +        }
 +    }
 +
 +    /* Allocate space for enforced rotation buffer variables */
 +    er->bufsize = nat_max;
 +    snew(er->data, nat_max);
 +    snew(er->xbuf, nat_max);
 +    snew(er->mbuf, nat_max);
 +
 +    /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
 +    if (PAR(cr))
 +    {
 +        er->mpi_bufsize = calc_mpi_bufsize(rot) + 100; /* larger to catch errors */
 +        snew(er->mpi_inbuf, er->mpi_bufsize);
 +        snew(er->mpi_outbuf, er->mpi_bufsize);
 +    }
 +    else
 +    {
 +        er->mpi_bufsize = 0;
 +        er->mpi_inbuf   = NULL;
 +        er->mpi_outbuf  = NULL;
 +    }
 +
 +    /* Only do I/O on the MASTER */
 +    er->out_angles  = NULL;
 +    er->out_rot     = NULL;
 +    er->out_torque  = NULL;
 +    if (MASTER(cr))
 +    {
 +        er->out_rot = open_rot_out(opt2fn("-ro", nfile, fnm), rot, oenv);
 +
 +        if (rot->nstsout > 0)
 +        {
 +            if (HaveFlexibleGroups(rot) || HavePotFitGroups(rot) )
 +            {
 +                er->out_angles  = open_angles_out(opt2fn("-ra", nfile, fnm), rot, oenv);
 +            }
 +            if (HaveFlexibleGroups(rot) )
 +            {
 +                er->out_torque  = open_torque_out(opt2fn("-rt", nfile, fnm), rot, oenv);
 +            }
 +        }
 +
 +        sfree(x_pbc);
 +    }
 +}
 +
 +
 +extern void finish_rot(FILE *fplog, t_rot *rot)
 +{
 +    gmx_enfrot_t er;        /* Pointer to the enforced rotation buffer variables */
 +
 +
 +    er = rot->enfrot;
 +    if (er->out_rot)
 +    {
 +        gmx_fio_fclose(er->out_rot);
 +    }
 +    if (er->out_slabs)
 +    {
 +        gmx_fio_fclose(er->out_slabs);
 +    }
 +    if (er->out_angles)
 +    {
 +        gmx_fio_fclose(er->out_angles);
 +    }
 +    if (er->out_torque)
 +    {
 +        gmx_fio_fclose(er->out_torque);
 +    }
 +}
 +
 +
 +/* Rotate the local reference positions and store them in
 + * erg->xr_loc[0...(nat_loc-1)]
 + *
 + * Note that we already subtracted u or y_c from the reference positions
 + * in init_rot_group().
 + */
 +static void rotate_local_reference(t_rotgrp *rotg)
 +{
 +    gmx_enfrotgrp_t erg;
 +    int             i, ii;
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    for (i = 0; i < erg->nat_loc; i++)
 +    {
 +        /* Index of this rotation group atom with respect to the whole rotation group */
 +        ii = erg->xc_ref_ind[i];
 +        /* Rotate */
 +        mvmul(erg->rotmat, rotg->x_ref[ii], erg->xr_loc[i]);
 +    }
 +}
 +
 +
 +/* Select the PBC representation for each local x position and store that
 + * for later usage. We assume the right PBC image of an x is the one nearest to
 + * its rotated reference */
 +static void choose_pbc_image(rvec x[], t_rotgrp *rotg, matrix box, int npbcdim)
 +{
 +    int             i, ii;
 +    gmx_enfrotgrp_t erg;       /* Pointer to enforced rotation group data */
 +    rvec            xref;
 +
 +
 +    erg = rotg->enfrotgrp;
 +
 +    for (i = 0; i < erg->nat_loc; i++)
 +    {
 +        /* Index of a rotation group atom  */
 +        ii = erg->ind_loc[i];
 +
-          * rotate_local_reference() */
++        /* Get the correctly rotated reference position. The pivot was already
 +         * subtracted in init_rot_group() from the reference positions. Also,
 +         * the reference positions have already been rotated in
++         * rotate_local_reference(). For the current reference position we thus
++         * only need to add the pivot again. */
 +        copy_rvec(erg->xr_loc[i], xref);
++        rvec_inc(xref, erg->xc_ref_center);
 +
 +        copy_correct_pbc_image(x[ii], erg->x_loc_pbc[i], xref, box, npbcdim);
 +    }
 +}
 +
 +
 +extern void do_rotation(
 +        t_commrec      *cr,
 +        t_inputrec     *ir,
 +        matrix          box,
 +        rvec            x[],
 +        real            t,
 +        gmx_large_int_t step,
 +        gmx_wallcycle_t wcycle,
 +        gmx_bool        bNS)
 +{
 +    int             g, i, ii;
 +    t_rot          *rot;
 +    t_rotgrp       *rotg;
 +    gmx_bool        outstep_slab, outstep_rot;
 +    gmx_bool        bFlex, bColl;
 +    gmx_enfrot_t    er;         /* Pointer to the enforced rotation buffer variables */
 +    gmx_enfrotgrp_t erg;        /* Pointer to enforced rotation group data           */
 +    rvec            transvec;
 +    t_gmx_potfit   *fit = NULL; /* For fit type 'potential' determine the fit
 +                                   angle via the potential minimum            */
 +
 +    /* Enforced rotation cycle counting: */
 +    gmx_cycles_t cycles_comp;   /* Cycles for the enf. rotation computation
 +                                   only, does not count communication. This
 +                                   counter is used for load-balancing         */
 +
 +#ifdef TAKETIME
 +    double t0;
 +#endif
 +
 +    rot = ir->rot;
 +    er  = rot->enfrot;
 +
 +    /* When to output in main rotation output file */
 +    outstep_rot  = do_per_step(step, rot->nstrout) && er->bOut;
 +    /* When to output per-slab data */
 +    outstep_slab = do_per_step(step, rot->nstsout) && er->bOut;
 +
 +    /* Output time into rotation output file */
 +    if (outstep_rot && MASTER(cr))
 +    {
 +        fprintf(er->out_rot, "%12.3e", t);
 +    }
 +
 +    /**************************************************************************/
 +    /* First do ALL the communication! */
 +    for (g = 0; g < rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        erg  = rotg->enfrotgrp;
 +
 +        /* Do we have a flexible axis? */
 +        bFlex = ISFLEX(rotg);
 +        /* Do we use a collective (global) set of coordinates? */
 +        bColl = ISCOLL(rotg);
 +
 +        /* Calculate the rotation matrix for this angle: */
 +        erg->degangle = rotg->rate * t;
 +        calc_rotmat(rotg->vec, erg->degangle, erg->rotmat);
 +
 +        if (bColl)
 +        {
 +            /* Transfer the rotation group's positions such that every node has
 +             * all of them. Every node contributes its local positions x and stores
 +             * it in the collective erg->xc array. */
 +            communicate_group_positions(cr, erg->xc, erg->xc_shifts, erg->xc_eshifts, bNS,
 +                                        x, rotg->nat, erg->nat_loc, erg->ind_loc, erg->xc_ref_ind, erg->xc_old, box);
 +        }
 +        else
 +        {
 +            /* Fill the local masses array;
 +             * this array changes in DD/neighborsearching steps */
 +            if (bNS)
 +            {
 +                for (i = 0; i < erg->nat_loc; i++)
 +                {
 +                    /* Index of local atom w.r.t. the collective rotation group */
 +                    ii            = erg->xc_ref_ind[i];
 +                    erg->m_loc[i] = erg->mc[ii];
 +                }
 +            }
 +
 +            /* Calculate Omega*(y_i-y_c) for the local positions */
 +            rotate_local_reference(rotg);
 +
 +            /* Choose the nearest PBC images of the group atoms with respect
 +             * to the rotated reference positions */
 +            choose_pbc_image(x, rotg, box, 3);
 +
 +            /* Get the center of the rotation group */
 +            if ( (rotg->eType == erotgISOPF) || (rotg->eType == erotgPMPF) )
 +            {
 +                get_center_comm(cr, erg->x_loc_pbc, erg->m_loc, erg->nat_loc, rotg->nat, erg->xc_center);
 +            }
 +        }
 +
 +    } /* End of loop over rotation groups */
 +
 +    /**************************************************************************/
 +    /* Done communicating, we can start to count cycles for the load balancing now ... */
 +    cycles_comp = gmx_cycles_read();
 +
 +
 +#ifdef TAKETIME
 +    t0 = MPI_Wtime();
 +#endif
 +
 +    for (g = 0; g < rot->ngrp; g++)
 +    {
 +        rotg = &rot->grp[g];
 +        erg  = rotg->enfrotgrp;
 +
 +        bFlex = ISFLEX(rotg);
 +        bColl = ISCOLL(rotg);
 +
 +        if (outstep_rot && MASTER(cr))
 +        {
 +            fprintf(er->out_rot, "%12.4f", erg->degangle);
 +        }
 +
 +        /* Calculate angles and rotation matrices for potential fitting: */
 +        if ( (outstep_rot || outstep_slab) && (erotgFitPOT == rotg->eFittype) )
 +        {
 +            fit = erg->PotAngleFit;
 +            for (i = 0; i < rotg->PotAngle_nstep; i++)
 +            {
 +                calc_rotmat(rotg->vec, erg->degangle + fit->degangle[i], fit->rotmat[i]);
 +
 +                /* Clear value from last step */
 +                erg->PotAngleFit->V[i] = 0.0;
 +            }
 +        }
 +
 +        /* Clear values from last time step */
 +        erg->V        = 0.0;
 +        erg->torque_v = 0.0;
 +        erg->angle_v  = 0.0;
 +        erg->weight_v = 0.0;
 +
 +        switch (rotg->eType)
 +        {
 +            case erotgISO:
 +            case erotgISOPF:
 +            case erotgPM:
 +            case erotgPMPF:
 +                do_fixed(rotg, x, box, t, step, outstep_rot, outstep_slab);
 +                break;
 +            case erotgRM:
 +                do_radial_motion(rotg, x, box, t, step, outstep_rot, outstep_slab);
 +                break;
 +            case erotgRMPF:
 +                do_radial_motion_pf(rotg, x, box, t, step, outstep_rot, outstep_slab);
 +                break;
 +            case erotgRM2:
 +            case erotgRM2PF:
 +                do_radial_motion2(rotg, x, box, t, step, outstep_rot, outstep_slab);
 +                break;
 +            case erotgFLEXT:
 +            case erotgFLEX2T:
 +                /* Subtract the center of the rotation group from the collective positions array
 +                 * Also store the center in erg->xc_center since it needs to be subtracted
 +                 * in the low level routines from the local coordinates as well */
 +                get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
 +                svmul(-1.0, erg->xc_center, transvec);
 +                translate_x(erg->xc, rotg->nat, transvec);
 +                do_flexible(MASTER(cr), er, rotg, g, x, box, t, step, outstep_rot, outstep_slab);
 +                break;
 +            case erotgFLEX:
 +            case erotgFLEX2:
 +                /* Do NOT subtract the center of mass in the low level routines! */
 +                clear_rvec(erg->xc_center);
 +                do_flexible(MASTER(cr), er, rotg, g, x, box, t, step, outstep_rot, outstep_slab);
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "No such rotation potential.");
 +                break;
 +        }
 +    }
 +
 +#ifdef TAKETIME
 +    if (MASTER(cr))
 +    {
 +        fprintf(stderr, "%s calculation (step %d) took %g seconds.\n", RotStr, step, MPI_Wtime()-t0);
 +    }
 +#endif
 +
 +    /* Stop the enforced rotation cycle counter and add the computation-only
 +     * cycles to the force cycles for load balancing */
 +    cycles_comp  = gmx_cycles_read() - cycles_comp;
 +
 +    if (DOMAINDECOMP(cr) && wcycle)
 +    {
 +        dd_cycles_add(cr->dd, cycles_comp, ddCyclF);
 +    }
 +}
index 52cd0b8816660ad615ab7a1b962d6a6995af2b62,0000000000000000000000000000000000000000..62081692fd498a00c61f902c0671d3d7e2d8445c
mode 100644,000000..100644
--- /dev/null
@@@ -1,2726 -1,0 +1,2751 @@@
-     /* update QMMMrec, if necessary */
-     if (fr->bQMMM)
-     {
-         update_QMMMrec(cr, fr, x, mdatoms, box, top);
-     }
-     if ((flags & GMX_FORCE_BONDED) && top->idef.il[F_POSRES].nr > 0)
-     {
-         posres_wrapper(fplog, flags, bSepDVDL, inputrec, nrnb, top, box, x,
-                        f, enerd, lambda, fr);
-     }
-     /* Compute the bonded and non-bonded energies and optionally forces */
-     do_force_lowlevel(fplog, step, fr, inputrec, &(top->idef),
-                       cr, nrnb, wcycle, mdatoms, &(inputrec->opts),
-                       x, hist, f, bSepLRF ? fr->f_twin : f, enerd, fcd, mtop, top, fr->born,
-                       &(top->atomtypes), bBornRadii, box,
-                       inputrec->fepvals, lambda, graph, &(top->excls), fr->mu_tot,
-                       flags, &cycles_pme);
-     if (bSepLRF)
-     {
-         if (do_per_step(step, inputrec->nstcalclr))
-         {
-             /* Add the long range forces to the short range forces */
-             for (i = 0; i < fr->natoms_force_constr; i++)
-             {
-                 rvec_add(fr->f_twin[i], f[i], f[i]);
-             }
-         }
-     }
 +/* -*- 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
 +
 +#ifdef GMX_CRAY_XT3
 +#include <catamount/dclock.h>
 +#endif
 +
 +
 +#include <stdio.h>
 +#include <time.h>
 +#ifdef HAVE_SYS_TIME_H
 +#include <sys/time.h>
 +#endif
 +#include <math.h>
 +#include "typedefs.h"
 +#include "string2.h"
 +#include "gmxfio.h"
 +#include "smalloc.h"
 +#include "names.h"
 +#include "confio.h"
 +#include "mvdata.h"
 +#include "txtdump.h"
 +#include "pbc.h"
 +#include "chargegroup.h"
 +#include "vec.h"
 +#include <time.h>
 +#include "nrnb.h"
 +#include "mshift.h"
 +#include "mdrun.h"
 +#include "sim_util.h"
 +#include "update.h"
 +#include "physics.h"
 +#include "main.h"
 +#include "mdatoms.h"
 +#include "force.h"
 +#include "bondf.h"
 +#include "pme.h"
 +#include "disre.h"
 +#include "orires.h"
 +#include "network.h"
 +#include "calcmu.h"
 +#include "constr.h"
 +#include "xvgr.h"
 +#include "trnio.h"
 +#include "xtcio.h"
 +#include "copyrite.h"
 +#include "pull_rotation.h"
 +#include "gmx_random.h"
 +#include "domdec.h"
 +#include "partdec.h"
 +#include "gmx_wallcycle.h"
 +#include "genborn.h"
 +#include "nbnxn_atomdata.h"
 +#include "nbnxn_search.h"
 +#include "nbnxn_kernels/nbnxn_kernel_ref.h"
 +#include "nbnxn_kernels/nbnxn_kernel_simd_4xn.h"
 +#include "nbnxn_kernels/nbnxn_kernel_simd_2xnn.h"
 +#include "nbnxn_kernels/nbnxn_kernel_gpu_ref.h"
 +
 +#include "gromacs/utility/gmxmpi.h"
 +
 +#include "adress.h"
 +#include "qmmm.h"
 +
 +#include "nbnxn_cuda_data_mgmt.h"
 +#include "nbnxn_cuda/nbnxn_cuda.h"
 +
 +#if 0
 +typedef struct gmx_timeprint {
 +
 +} t_gmx_timeprint;
 +#endif
 +
 +/* Portable version of ctime_r implemented in src/gmxlib/string2.c, but we do not want it declared in public installed headers */
 +char *
 +gmx_ctime_r(const time_t *clock, char *buf, int n);
 +
 +
 +double
 +gmx_gettime()
 +{
 +#ifdef HAVE_GETTIMEOFDAY
 +    struct timeval t;
 +    double         seconds;
 +
 +    gettimeofday(&t, NULL);
 +
 +    seconds = (double) t.tv_sec + 1e-6*(double)t.tv_usec;
 +
 +    return seconds;
 +#else
 +    double  seconds;
 +
 +    seconds = time(NULL);
 +
 +    return seconds;
 +#endif
 +}
 +
 +
 +#define difftime(end, start) ((double)(end)-(double)(start))
 +
 +void print_time(FILE *out, gmx_runtime_t *runtime, gmx_large_int_t step,
 +                t_inputrec *ir, t_commrec *cr)
 +{
 +    time_t finish;
 +    char   timebuf[STRLEN];
 +    double dt;
 +    char   buf[48];
 +
 +#ifndef GMX_THREAD_MPI
 +    if (!PAR(cr))
 +#endif
 +    {
 +        fprintf(out, "\r");
 +    }
 +    fprintf(out, "step %s", gmx_step_str(step, buf));
 +    if ((step >= ir->nstlist))
 +    {
 +        runtime->last          = gmx_gettime();
 +        dt                     = difftime(runtime->last, runtime->real);
 +        runtime->time_per_step = dt/(step - ir->init_step + 1);
 +
 +        dt = (ir->nsteps + ir->init_step - step)*runtime->time_per_step;
 +
 +        if (ir->nsteps >= 0)
 +        {
 +            if (dt >= 300)
 +            {
 +                finish = (time_t) (runtime->last + dt);
 +                gmx_ctime_r(&finish, timebuf, STRLEN);
 +                sprintf(buf, "%s", timebuf);
 +                buf[strlen(buf)-1] = '\0';
 +                fprintf(out, ", will finish %s", buf);
 +            }
 +            else
 +            {
 +                fprintf(out, ", remaining runtime: %5d s          ", (int)dt);
 +            }
 +        }
 +        else
 +        {
 +            fprintf(out, " performance: %.1f ns/day    ",
 +                    ir->delta_t/1000*24*60*60/runtime->time_per_step);
 +        }
 +    }
 +#ifndef GMX_THREAD_MPI
 +    if (PAR(cr))
 +    {
 +        fprintf(out, "\n");
 +    }
 +#endif
 +
 +    fflush(out);
 +}
 +
 +#ifdef NO_CLOCK
 +#define clock() -1
 +#endif
 +
 +static double set_proctime(gmx_runtime_t *runtime)
 +{
 +    double diff;
 +#ifdef GMX_CRAY_XT3
 +    double prev;
 +
 +    prev          = runtime->proc;
 +    runtime->proc = dclock();
 +
 +    diff = runtime->proc - prev;
 +#else
 +    clock_t prev;
 +
 +    prev          = runtime->proc;
 +    runtime->proc = clock();
 +
 +    diff = (double)(runtime->proc - prev)/(double)CLOCKS_PER_SEC;
 +#endif
 +    if (diff < 0)
 +    {
 +        /* The counter has probably looped, ignore this data */
 +        diff = 0;
 +    }
 +
 +    return diff;
 +}
 +
 +void runtime_start(gmx_runtime_t *runtime)
 +{
 +    runtime->real          = gmx_gettime();
 +    runtime->proc          = 0;
 +    set_proctime(runtime);
 +    runtime->realtime      = 0;
 +    runtime->proctime      = 0;
 +    runtime->last          = 0;
 +    runtime->time_per_step = 0;
 +}
 +
 +void runtime_end(gmx_runtime_t *runtime)
 +{
 +    double now;
 +
 +    now = gmx_gettime();
 +
 +    runtime->proctime += set_proctime(runtime);
 +    runtime->realtime  = now - runtime->real;
 +    runtime->real      = now;
 +}
 +
 +void runtime_upd_proc(gmx_runtime_t *runtime)
 +{
 +    runtime->proctime += set_proctime(runtime);
 +}
 +
 +void print_date_and_time(FILE *fplog, int nodeid, const char *title,
 +                         const gmx_runtime_t *runtime)
 +{
 +    int    i;
 +    char   timebuf[STRLEN];
 +    char   time_string[STRLEN];
 +    time_t tmptime;
 +
 +    if (fplog)
 +    {
 +        if (runtime != NULL)
 +        {
 +            tmptime = (time_t) runtime->real;
 +            gmx_ctime_r(&tmptime, timebuf, STRLEN);
 +        }
 +        else
 +        {
 +            tmptime = (time_t) gmx_gettime();
 +            gmx_ctime_r(&tmptime, timebuf, STRLEN);
 +        }
 +        for (i = 0; timebuf[i] >= ' '; i++)
 +        {
 +            time_string[i] = timebuf[i];
 +        }
 +        time_string[i] = '\0';
 +
 +        fprintf(fplog, "%s on node %d %s\n", title, nodeid, time_string);
 +    }
 +}
 +
 +static void sum_forces(int start, int end, rvec f[], rvec flr[])
 +{
 +    int i;
 +
 +    if (gmx_debug_at)
 +    {
 +        pr_rvecs(debug, 0, "fsr", f+start, end-start);
 +        pr_rvecs(debug, 0, "flr", flr+start, end-start);
 +    }
 +    for (i = start; (i < end); i++)
 +    {
 +        rvec_inc(f[i], flr[i]);
 +    }
 +}
 +
 +/*
 + * calc_f_el calculates forces due to an electric field.
 + *
 + * force is kJ mol^-1 nm^-1 = e * kJ mol^-1 nm^-1 / e
 + *
 + * Et[] contains the parameters for the time dependent
 + * part of the field (not yet used).
 + * Ex[] contains the parameters for
 + * the spatial dependent part of the field. You can have cool periodic
 + * fields in principle, but only a constant field is supported
 + * now.
 + * The function should return the energy due to the electric field
 + * (if any) but for now returns 0.
 + *
 + * WARNING:
 + * There can be problems with the virial.
 + * Since the field is not self-consistent this is unavoidable.
 + * For neutral molecules the virial is correct within this approximation.
 + * For neutral systems with many charged molecules the error is small.
 + * But for systems with a net charge or a few charged molecules
 + * the error can be significant when the field is high.
 + * Solution: implement a self-consitent electric field into PME.
 + */
 +static void calc_f_el(FILE *fp, int  start, int homenr,
 +                      real charge[], rvec x[], rvec f[],
 +                      t_cosines Ex[], t_cosines Et[], double t)
 +{
 +    rvec Ext;
 +    real t0;
 +    int  i, m;
 +
 +    for (m = 0; (m < DIM); m++)
 +    {
 +        if (Et[m].n > 0)
 +        {
 +            if (Et[m].n == 3)
 +            {
 +                t0     = Et[m].a[1];
 +                Ext[m] = cos(Et[m].a[0]*(t-t0))*exp(-sqr(t-t0)/(2.0*sqr(Et[m].a[2])));
 +            }
 +            else
 +            {
 +                Ext[m] = cos(Et[m].a[0]*t);
 +            }
 +        }
 +        else
 +        {
 +            Ext[m] = 1.0;
 +        }
 +        if (Ex[m].n > 0)
 +        {
 +            /* Convert the field strength from V/nm to MD-units */
 +            Ext[m] *= Ex[m].a[0]*FIELDFAC;
 +            for (i = start; (i < start+homenr); i++)
 +            {
 +                f[i][m] += charge[i]*Ext[m];
 +            }
 +        }
 +        else
 +        {
 +            Ext[m] = 0;
 +        }
 +    }
 +    if (fp != NULL)
 +    {
 +        fprintf(fp, "%10g  %10g  %10g  %10g #FIELD\n", t,
 +                Ext[XX]/FIELDFAC, Ext[YY]/FIELDFAC, Ext[ZZ]/FIELDFAC);
 +    }
 +}
 +
 +static void calc_virial(FILE *fplog, int start, int homenr, rvec x[], rvec f[],
 +                        tensor vir_part, t_graph *graph, matrix box,
 +                        t_nrnb *nrnb, const t_forcerec *fr, int ePBC)
 +{
 +    int    i, j;
 +    tensor virtest;
 +
 +    /* The short-range virial from surrounding boxes */
 +    clear_mat(vir_part);
 +    calc_vir(fplog, SHIFTS, fr->shift_vec, fr->fshift, vir_part, ePBC == epbcSCREW, box);
 +    inc_nrnb(nrnb, eNR_VIRIAL, SHIFTS);
 +
 +    /* Calculate partial virial, for local atoms only, based on short range.
 +     * Total virial is computed in global_stat, called from do_md
 +     */
 +    f_calc_vir(fplog, start, start+homenr, x, f, vir_part, graph, box);
 +    inc_nrnb(nrnb, eNR_VIRIAL, homenr);
 +
 +    /* Add position restraint contribution */
 +    for (i = 0; i < DIM; i++)
 +    {
 +        vir_part[i][i] += fr->vir_diag_posres[i];
 +    }
 +
 +    /* Add wall contribution */
 +    for (i = 0; i < DIM; i++)
 +    {
 +        vir_part[i][ZZ] += fr->vir_wall_z[i];
 +    }
 +
 +    if (debug)
 +    {
 +        pr_rvecs(debug, 0, "vir_part", vir_part, DIM);
 +    }
 +}
 +
 +static void posres_wrapper(FILE *fplog,
 +                           int flags,
 +                           gmx_bool bSepDVDL,
 +                           t_inputrec *ir,
 +                           t_nrnb *nrnb,
 +                           gmx_localtop_t *top,
 +                           matrix box, rvec x[],
 +                           rvec f[],
 +                           gmx_enerdata_t *enerd,
 +                           real *lambda,
 +                           t_forcerec *fr)
 +{
 +    t_pbc pbc;
 +    real  v, dvdl;
 +    int   i;
 +
 +    /* Position restraints always require full pbc */
 +    set_pbc(&pbc, ir->ePBC, box);
 +    dvdl = 0;
 +    v    = posres(top->idef.il[F_POSRES].nr, top->idef.il[F_POSRES].iatoms,
 +                  top->idef.iparams_posres,
 +                  (const rvec*)x, fr->f_novirsum, fr->vir_diag_posres,
 +                  ir->ePBC == epbcNONE ? NULL : &pbc,
 +                  lambda[efptRESTRAINT], &dvdl,
 +                  fr->rc_scaling, fr->ePBC, fr->posres_com, fr->posres_comB);
 +    if (bSepDVDL)
 +    {
 +        fprintf(fplog, sepdvdlformat,
 +                interaction_function[F_POSRES].longname, v, dvdl);
 +    }
 +    enerd->term[F_POSRES] += v;
 +    /* If just the force constant changes, the FEP term is linear,
 +     * but if k changes, it is not.
 +     */
 +    enerd->dvdl_nonlin[efptRESTRAINT] += dvdl;
 +    inc_nrnb(nrnb, eNR_POSRES, top->idef.il[F_POSRES].nr/2);
 +
 +    if ((ir->fepvals->n_lambda > 0) && (flags & GMX_FORCE_DHDL))
 +    {
 +        for (i = 0; i < enerd->n_lambda; i++)
 +        {
 +            real dvdl_dum, lambda_dum;
 +
 +            lambda_dum = (i == 0 ? lambda[efptRESTRAINT] : ir->fepvals->all_lambda[efptRESTRAINT][i-1]);
 +            v          = posres(top->idef.il[F_POSRES].nr, top->idef.il[F_POSRES].iatoms,
 +                                top->idef.iparams_posres,
 +                                (const rvec*)x, NULL, NULL,
 +                                ir->ePBC == epbcNONE ? NULL : &pbc, lambda_dum, &dvdl,
 +                                fr->rc_scaling, fr->ePBC, fr->posres_com, fr->posres_comB);
 +            enerd->enerpart_lambda[i] += v;
 +        }
 +    }
 +}
 +
 +static void pull_potential_wrapper(FILE *fplog,
 +                                   gmx_bool bSepDVDL,
 +                                   t_commrec *cr,
 +                                   t_inputrec *ir,
 +                                   matrix box, rvec x[],
 +                                   rvec f[],
 +                                   tensor vir_force,
 +                                   t_mdatoms *mdatoms,
 +                                   gmx_enerdata_t *enerd,
 +                                   real *lambda,
 +                                   double t)
 +{
 +    t_pbc  pbc;
 +    real   dvdl;
 +
 +    /* Calculate the center of mass forces, this requires communication,
 +     * which is why pull_potential is called close to other communication.
 +     * The virial contribution is calculated directly,
 +     * which is why we call pull_potential after calc_virial.
 +     */
 +    set_pbc(&pbc, ir->ePBC, box);
 +    dvdl                     = 0;
 +    enerd->term[F_COM_PULL] +=
 +        pull_potential(ir->ePull, ir->pull, mdatoms, &pbc,
 +                       cr, t, lambda[efptRESTRAINT], x, f, vir_force, &dvdl);
 +    if (bSepDVDL)
 +    {
 +        fprintf(fplog, sepdvdlformat, "Com pull", enerd->term[F_COM_PULL], dvdl);
 +    }
 +    enerd->dvdl_lin[efptRESTRAINT] += dvdl;
 +}
 +
 +static void pme_receive_force_ener(FILE           *fplog,
 +                                   gmx_bool        bSepDVDL,
 +                                   t_commrec      *cr,
 +                                   gmx_wallcycle_t wcycle,
 +                                   gmx_enerdata_t *enerd,
 +                                   t_forcerec     *fr)
 +{
 +    real   e, v, dvdl;
 +    float  cycles_ppdpme, cycles_seppme;
 +
 +    cycles_ppdpme = wallcycle_stop(wcycle, ewcPPDURINGPME);
 +    dd_cycles_add(cr->dd, cycles_ppdpme, ddCyclPPduringPME);
 +
 +    /* In case of node-splitting, the PP nodes receive the long-range
 +     * forces, virial and energy from the PME nodes here.
 +     */
 +    wallcycle_start(wcycle, ewcPP_PMEWAITRECVF);
 +    dvdl = 0;
 +    gmx_pme_receive_f(cr, fr->f_novirsum, fr->vir_el_recip, &e, &dvdl,
 +                      &cycles_seppme);
 +    if (bSepDVDL)
 +    {
 +        fprintf(fplog, sepdvdlformat, "PME mesh", e, dvdl);
 +    }
 +    enerd->term[F_COUL_RECIP] += e;
 +    enerd->dvdl_lin[efptCOUL] += dvdl;
 +    if (wcycle)
 +    {
 +        dd_cycles_add(cr->dd, cycles_seppme, ddCyclPME);
 +    }
 +    wallcycle_stop(wcycle, ewcPP_PMEWAITRECVF);
 +}
 +
 +static void print_large_forces(FILE *fp, t_mdatoms *md, t_commrec *cr,
 +                               gmx_large_int_t step, real pforce, rvec *x, rvec *f)
 +{
 +    int  i;
 +    real pf2, fn2;
 +    char buf[STEPSTRSIZE];
 +
 +    pf2 = sqr(pforce);
 +    for (i = md->start; i < md->start+md->homenr; i++)
 +    {
 +        fn2 = norm2(f[i]);
 +        /* We also catch NAN, if the compiler does not optimize this away. */
 +        if (fn2 >= pf2 || fn2 != fn2)
 +        {
 +            fprintf(fp, "step %s  atom %6d  x %8.3f %8.3f %8.3f  force %12.5e\n",
 +                    gmx_step_str(step, buf),
 +                    ddglatnr(cr->dd, i), x[i][XX], x[i][YY], x[i][ZZ], sqrt(fn2));
 +        }
 +    }
 +}
 +
 +static void post_process_forces(FILE *fplog,
 +                                t_commrec *cr,
 +                                gmx_large_int_t step,
 +                                t_nrnb *nrnb, gmx_wallcycle_t wcycle,
 +                                gmx_localtop_t *top,
 +                                matrix box, rvec x[],
 +                                rvec f[],
 +                                tensor vir_force,
 +                                t_mdatoms *mdatoms,
 +                                t_graph *graph,
 +                                t_forcerec *fr, gmx_vsite_t *vsite,
 +                                int flags)
 +{
 +    if (fr->bF_NoVirSum)
 +    {
 +        if (vsite)
 +        {
 +            /* Spread the mesh force on virtual sites to the other particles...
 +             * This is parallellized. MPI communication is performed
 +             * if the constructing atoms aren't local.
 +             */
 +            wallcycle_start(wcycle, ewcVSITESPREAD);
 +            spread_vsite_f(fplog, vsite, x, fr->f_novirsum, NULL,
 +                           (flags & GMX_FORCE_VIRIAL), fr->vir_el_recip,
 +                           nrnb,
 +                           &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr);
 +            wallcycle_stop(wcycle, ewcVSITESPREAD);
 +        }
 +        if (flags & GMX_FORCE_VIRIAL)
 +        {
 +            /* Now add the forces, this is local */
 +            if (fr->bDomDec)
 +            {
 +                sum_forces(0, fr->f_novirsum_n, f, fr->f_novirsum);
 +            }
 +            else
 +            {
 +                sum_forces(mdatoms->start, mdatoms->start+mdatoms->homenr,
 +                           f, fr->f_novirsum);
 +            }
 +            if (EEL_FULL(fr->eeltype))
 +            {
 +                /* Add the mesh contribution to the virial */
 +                m_add(vir_force, fr->vir_el_recip, vir_force);
 +            }
 +            if (debug)
 +            {
 +                pr_rvecs(debug, 0, "vir_force", vir_force, DIM);
 +            }
 +        }
 +    }
 +
 +    if (fr->print_force >= 0)
 +    {
 +        print_large_forces(stderr, mdatoms, cr, step, fr->print_force, x, f);
 +    }
 +}
 +
 +static void do_nb_verlet(t_forcerec *fr,
 +                         interaction_const_t *ic,
 +                         gmx_enerdata_t *enerd,
 +                         int flags, int ilocality,
 +                         int clearF,
 +                         t_nrnb *nrnb,
 +                         gmx_wallcycle_t wcycle)
 +{
 +    int                        nnbl, kernel_type, enr_nbnxn_kernel_ljc, enr_nbnxn_kernel_lj;
 +    char                      *env;
 +    nonbonded_verlet_group_t  *nbvg;
 +
 +    if (!(flags & GMX_FORCE_NONBONDED))
 +    {
 +        /* skip non-bonded calculation */
 +        return;
 +    }
 +
 +    nbvg = &fr->nbv->grp[ilocality];
 +
 +    /* CUDA kernel launch overhead is already timed separately */
 +    if (fr->cutoff_scheme != ecutsVERLET)
 +    {
 +        gmx_incons("Invalid cut-off scheme passed!");
 +    }
 +
 +    if (nbvg->kernel_type != nbnxnk8x8x8_CUDA)
 +    {
 +        wallcycle_sub_start(wcycle, ewcsNONBONDED);
 +    }
 +    switch (nbvg->kernel_type)
 +    {
 +        case nbnxnk4x4_PlainC:
 +            nbnxn_kernel_ref(&nbvg->nbl_lists,
 +                             nbvg->nbat, ic,
 +                             fr->shift_vec,
 +                             flags,
 +                             clearF,
 +                             fr->fshift[0],
 +                             enerd->grpp.ener[egCOULSR],
 +                             fr->bBHAM ?
 +                             enerd->grpp.ener[egBHAMSR] :
 +                             enerd->grpp.ener[egLJSR]);
 +            break;
 +
 +        case nbnxnk4xN_SIMD_4xN:
 +            nbnxn_kernel_simd_4xn(&nbvg->nbl_lists,
 +                                  nbvg->nbat, ic,
 +                                  nbvg->ewald_excl,
 +                                  fr->shift_vec,
 +                                  flags,
 +                                  clearF,
 +                                  fr->fshift[0],
 +                                  enerd->grpp.ener[egCOULSR],
 +                                  fr->bBHAM ?
 +                                  enerd->grpp.ener[egBHAMSR] :
 +                                  enerd->grpp.ener[egLJSR]);
 +            break;
 +        case nbnxnk4xN_SIMD_2xNN:
 +            nbnxn_kernel_simd_2xnn(&nbvg->nbl_lists,
 +                                   nbvg->nbat, ic,
 +                                   nbvg->ewald_excl,
 +                                   fr->shift_vec,
 +                                   flags,
 +                                   clearF,
 +                                   fr->fshift[0],
 +                                   enerd->grpp.ener[egCOULSR],
 +                                   fr->bBHAM ?
 +                                   enerd->grpp.ener[egBHAMSR] :
 +                                   enerd->grpp.ener[egLJSR]);
 +            break;
 +
 +        case nbnxnk8x8x8_CUDA:
 +            nbnxn_cuda_launch_kernel(fr->nbv->cu_nbv, nbvg->nbat, flags, ilocality);
 +            break;
 +
 +        case nbnxnk8x8x8_PlainC:
 +            nbnxn_kernel_gpu_ref(nbvg->nbl_lists.nbl[0],
 +                                 nbvg->nbat, ic,
 +                                 fr->shift_vec,
 +                                 flags,
 +                                 clearF,
 +                                 nbvg->nbat->out[0].f,
 +                                 fr->fshift[0],
 +                                 enerd->grpp.ener[egCOULSR],
 +                                 fr->bBHAM ?
 +                                 enerd->grpp.ener[egBHAMSR] :
 +                                 enerd->grpp.ener[egLJSR]);
 +            break;
 +
 +        default:
 +            gmx_incons("Invalid nonbonded kernel type passed!");
 +
 +    }
 +    if (nbvg->kernel_type != nbnxnk8x8x8_CUDA)
 +    {
 +        wallcycle_sub_stop(wcycle, ewcsNONBONDED);
 +    }
 +
 +    if (EEL_RF(ic->eeltype) || ic->eeltype == eelCUT)
 +    {
 +        enr_nbnxn_kernel_ljc = eNR_NBNXN_LJ_RF;
 +    }
 +    else if (nbvg->ewald_excl == ewaldexclTable)
 +    {
 +        enr_nbnxn_kernel_ljc = eNR_NBNXN_LJ_TAB;
 +    }
 +    else
 +    {
 +        enr_nbnxn_kernel_ljc = eNR_NBNXN_LJ_EWALD;
 +    }
 +    enr_nbnxn_kernel_lj = eNR_NBNXN_LJ;
 +    if (flags & GMX_FORCE_ENERGY)
 +    {
 +        /* In eNR_??? the nbnxn F+E kernels are always the F kernel + 1 */
 +        enr_nbnxn_kernel_ljc += 1;
 +        enr_nbnxn_kernel_lj  += 1;
 +    }
 +
 +    inc_nrnb(nrnb, enr_nbnxn_kernel_ljc,
 +             nbvg->nbl_lists.natpair_ljq);
 +    inc_nrnb(nrnb, enr_nbnxn_kernel_lj,
 +             nbvg->nbl_lists.natpair_lj);
 +    inc_nrnb(nrnb, enr_nbnxn_kernel_ljc-eNR_NBNXN_LJ_RF+eNR_NBNXN_RF,
 +             nbvg->nbl_lists.natpair_q);
 +}
 +
 +void do_force_cutsVERLET(FILE *fplog, t_commrec *cr,
 +                         t_inputrec *inputrec,
 +                         gmx_large_int_t step, t_nrnb *nrnb, gmx_wallcycle_t wcycle,
 +                         gmx_localtop_t *top,
 +                         gmx_mtop_t *mtop,
 +                         gmx_groups_t *groups,
 +                         matrix box, rvec x[], history_t *hist,
 +                         rvec f[],
 +                         tensor vir_force,
 +                         t_mdatoms *mdatoms,
 +                         gmx_enerdata_t *enerd, t_fcdata *fcd,
 +                         real *lambda, t_graph *graph,
 +                         t_forcerec *fr, interaction_const_t *ic,
 +                         gmx_vsite_t *vsite, rvec mu_tot,
 +                         double t, FILE *field, gmx_edsam_t ed,
 +                         gmx_bool bBornRadii,
 +                         int flags)
 +{
 +    int                 cg0, cg1, i, j;
 +    int                 start, homenr;
 +    int                 nb_kernel_type;
 +    double              mu[2*DIM];
 +    gmx_bool            bSepDVDL, bStateChanged, bNS, bFillGrid, bCalcCGCM, bBS;
 +    gmx_bool            bDoLongRange, bDoForces, bSepLRF, bUseGPU, bUseOrEmulGPU;
 +    gmx_bool            bDiffKernels = FALSE;
 +    matrix              boxs;
 +    rvec                vzero, box_diag;
 +    real                e, v, dvdl;
 +    float               cycles_pme, cycles_force;
 +    nonbonded_verlet_t *nbv;
 +
 +    cycles_force   = 0;
 +    nbv            = fr->nbv;
 +    nb_kernel_type = fr->nbv->grp[0].kernel_type;
 +
 +    start  = mdatoms->start;
 +    homenr = mdatoms->homenr;
 +
 +    bSepDVDL = (fr->bSepDVDL && do_per_step(step, inputrec->nstlog));
 +
 +    clear_mat(vir_force);
 +
 +    cg0 = 0;
 +    if (DOMAINDECOMP(cr))
 +    {
 +        cg1 = cr->dd->ncg_tot;
 +    }
 +    else
 +    {
 +        cg1 = top->cgs.nr;
 +    }
 +    if (fr->n_tpi > 0)
 +    {
 +        cg1--;
 +    }
 +
 +    bStateChanged = (flags & GMX_FORCE_STATECHANGED);
 +    bNS           = (flags & GMX_FORCE_NS) && (fr->bAllvsAll == FALSE);
 +    bFillGrid     = (bNS && bStateChanged);
 +    bCalcCGCM     = (bFillGrid && !DOMAINDECOMP(cr));
 +    bDoLongRange  = (fr->bTwinRange && bNS && (flags & GMX_FORCE_DO_LR));
 +    bDoForces     = (flags & GMX_FORCE_FORCES);
 +    bSepLRF       = (bDoLongRange && bDoForces && (flags & GMX_FORCE_SEPLRF));
 +    bUseGPU       = fr->nbv->bUseGPU;
 +    bUseOrEmulGPU = bUseGPU || (nbv->grp[0].kernel_type == nbnxnk8x8x8_PlainC);
 +
 +    if (bStateChanged)
 +    {
 +        update_forcerec(fplog, fr, box);
 +
 +        if (NEED_MUTOT(*inputrec))
 +        {
 +            /* Calculate total (local) dipole moment in a temporary common array.
 +             * This makes it possible to sum them over nodes faster.
 +             */
 +            calc_mu(start, homenr,
 +                    x, mdatoms->chargeA, mdatoms->chargeB, mdatoms->nChargePerturbed,
 +                    mu, mu+DIM);
 +        }
 +    }
 +
 +    if (fr->ePBC != epbcNONE)
 +    {
 +        /* Compute shift vectors every step,
 +         * because of pressure coupling or box deformation!
 +         */
 +        if ((flags & GMX_FORCE_DYNAMICBOX) && bStateChanged)
 +        {
 +            calc_shifts(box, fr->shift_vec);
 +        }
 +
 +        if (bCalcCGCM)
 +        {
 +            put_atoms_in_box_omp(fr->ePBC, box, homenr, x);
 +            inc_nrnb(nrnb, eNR_SHIFTX, homenr);
 +        }
 +        else if (EI_ENERGY_MINIMIZATION(inputrec->eI) && graph)
 +        {
 +            unshift_self(graph, box, x);
 +        }
 +    }
 +
 +    nbnxn_atomdata_copy_shiftvec(flags & GMX_FORCE_DYNAMICBOX,
 +                                 fr->shift_vec, nbv->grp[0].nbat);
 +
 +#ifdef GMX_MPI
 +    if (!(cr->duty & DUTY_PME))
 +    {
 +        /* Send particle coordinates to the pme nodes.
 +         * Since this is only implemented for domain decomposition
 +         * and domain decomposition does not use the graph,
 +         * we do not need to worry about shifting.
 +         */
 +
 +        wallcycle_start(wcycle, ewcPP_PMESENDX);
 +
 +        bBS = (inputrec->nwall == 2);
 +        if (bBS)
 +        {
 +            copy_mat(box, boxs);
 +            svmul(inputrec->wall_ewald_zfac, boxs[ZZ], boxs[ZZ]);
 +        }
 +
 +        gmx_pme_send_x(cr, bBS ? boxs : box, x,
 +                       mdatoms->nChargePerturbed, lambda[efptCOUL],
 +                       (flags & (GMX_FORCE_VIRIAL | GMX_FORCE_ENERGY)), step);
 +
 +        wallcycle_stop(wcycle, ewcPP_PMESENDX);
 +    }
 +#endif /* GMX_MPI */
 +
 +    /* do gridding for pair search */
 +    if (bNS)
 +    {
 +        if (graph && bStateChanged)
 +        {
 +            /* Calculate intramolecular shift vectors to make molecules whole */
 +            mk_mshift(fplog, graph, fr->ePBC, box, x);
 +        }
 +
 +        clear_rvec(vzero);
 +        box_diag[XX] = box[XX][XX];
 +        box_diag[YY] = box[YY][YY];
 +        box_diag[ZZ] = box[ZZ][ZZ];
 +
 +        wallcycle_start(wcycle, ewcNS);
 +        if (!fr->bDomDec)
 +        {
 +            wallcycle_sub_start(wcycle, ewcsNBS_GRID_LOCAL);
 +            nbnxn_put_on_grid(nbv->nbs, fr->ePBC, box,
 +                              0, vzero, box_diag,
 +                              0, mdatoms->homenr, -1, fr->cginfo, x,
 +                              0, NULL,
 +                              nbv->grp[eintLocal].kernel_type,
 +                              nbv->grp[eintLocal].nbat);
 +            wallcycle_sub_stop(wcycle, ewcsNBS_GRID_LOCAL);
 +        }
 +        else
 +        {
 +            wallcycle_sub_start(wcycle, ewcsNBS_GRID_NONLOCAL);
 +            nbnxn_put_on_grid_nonlocal(nbv->nbs, domdec_zones(cr->dd),
 +                                       fr->cginfo, x,
 +                                       nbv->grp[eintNonlocal].kernel_type,
 +                                       nbv->grp[eintNonlocal].nbat);
 +            wallcycle_sub_stop(wcycle, ewcsNBS_GRID_NONLOCAL);
 +        }
 +
 +        if (nbv->ngrp == 1 ||
 +            nbv->grp[eintNonlocal].nbat == nbv->grp[eintLocal].nbat)
 +        {
 +            nbnxn_atomdata_set(nbv->grp[eintLocal].nbat, eatAll,
 +                               nbv->nbs, mdatoms, fr->cginfo);
 +        }
 +        else
 +        {
 +            nbnxn_atomdata_set(nbv->grp[eintLocal].nbat, eatLocal,
 +                               nbv->nbs, mdatoms, fr->cginfo);
 +            nbnxn_atomdata_set(nbv->grp[eintNonlocal].nbat, eatAll,
 +                               nbv->nbs, mdatoms, fr->cginfo);
 +        }
 +        wallcycle_stop(wcycle, ewcNS);
 +    }
 +
 +    /* initialize the GPU atom data and copy shift vector */
 +    if (bUseGPU)
 +    {
 +        if (bNS)
 +        {
 +            wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU_NB);
 +            nbnxn_cuda_init_atomdata(nbv->cu_nbv, nbv->grp[eintLocal].nbat);
 +            wallcycle_stop(wcycle, ewcLAUNCH_GPU_NB);
 +        }
 +
 +        wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU_NB);
 +        nbnxn_cuda_upload_shiftvec(nbv->cu_nbv, nbv->grp[eintLocal].nbat);
 +        wallcycle_stop(wcycle, ewcLAUNCH_GPU_NB);
 +    }
 +
 +    /* do local pair search */
 +    if (bNS)
 +    {
 +        wallcycle_start_nocount(wcycle, ewcNS);
 +        wallcycle_sub_start(wcycle, ewcsNBS_SEARCH_LOCAL);
 +        nbnxn_make_pairlist(nbv->nbs, nbv->grp[eintLocal].nbat,
 +                            &top->excls,
 +                            ic->rlist,
 +                            nbv->min_ci_balanced,
 +                            &nbv->grp[eintLocal].nbl_lists,
 +                            eintLocal,
 +                            nbv->grp[eintLocal].kernel_type,
 +                            nrnb);
 +        wallcycle_sub_stop(wcycle, ewcsNBS_SEARCH_LOCAL);
 +
 +        if (bUseGPU)
 +        {
 +            /* initialize local pair-list on the GPU */
 +            nbnxn_cuda_init_pairlist(nbv->cu_nbv,
 +                                     nbv->grp[eintLocal].nbl_lists.nbl[0],
 +                                     eintLocal);
 +        }
 +        wallcycle_stop(wcycle, ewcNS);
 +    }
 +    else
 +    {
 +        wallcycle_start(wcycle, ewcNB_XF_BUF_OPS);
 +        wallcycle_sub_start(wcycle, ewcsNB_X_BUF_OPS);
 +        nbnxn_atomdata_copy_x_to_nbat_x(nbv->nbs, eatLocal, FALSE, x,
 +                                        nbv->grp[eintLocal].nbat);
 +        wallcycle_sub_stop(wcycle, ewcsNB_X_BUF_OPS);
 +        wallcycle_stop(wcycle, ewcNB_XF_BUF_OPS);
 +    }
 +
 +    if (bUseGPU)
 +    {
 +        wallcycle_start(wcycle, ewcLAUNCH_GPU_NB);
 +        /* launch local nonbonded F on GPU */
 +        do_nb_verlet(fr, ic, enerd, flags, eintLocal, enbvClearFNo,
 +                     nrnb, wcycle);
 +        wallcycle_stop(wcycle, ewcLAUNCH_GPU_NB);
 +    }
 +
 +    /* Communicate coordinates and sum dipole if necessary +
 +       do non-local pair search */
 +    if (DOMAINDECOMP(cr))
 +    {
 +        bDiffKernels = (nbv->grp[eintNonlocal].kernel_type !=
 +                        nbv->grp[eintLocal].kernel_type);
 +
 +        if (bDiffKernels)
 +        {
 +            /* With GPU+CPU non-bonded calculations we need to copy
 +             * the local coordinates to the non-local nbat struct
 +             * (in CPU format) as the non-local kernel call also
 +             * calculates the local - non-local interactions.
 +             */
 +            wallcycle_start(wcycle, ewcNB_XF_BUF_OPS);
 +            wallcycle_sub_start(wcycle, ewcsNB_X_BUF_OPS);
 +            nbnxn_atomdata_copy_x_to_nbat_x(nbv->nbs, eatLocal, TRUE, x,
 +                                            nbv->grp[eintNonlocal].nbat);
 +            wallcycle_sub_stop(wcycle, ewcsNB_X_BUF_OPS);
 +            wallcycle_stop(wcycle, ewcNB_XF_BUF_OPS);
 +        }
 +
 +        if (bNS)
 +        {
 +            wallcycle_start_nocount(wcycle, ewcNS);
 +            wallcycle_sub_start(wcycle, ewcsNBS_SEARCH_NONLOCAL);
 +
 +            if (bDiffKernels)
 +            {
 +                nbnxn_grid_add_simple(nbv->nbs, nbv->grp[eintNonlocal].nbat);
 +            }
 +
 +            nbnxn_make_pairlist(nbv->nbs, nbv->grp[eintNonlocal].nbat,
 +                                &top->excls,
 +                                ic->rlist,
 +                                nbv->min_ci_balanced,
 +                                &nbv->grp[eintNonlocal].nbl_lists,
 +                                eintNonlocal,
 +                                nbv->grp[eintNonlocal].kernel_type,
 +                                nrnb);
 +
 +            wallcycle_sub_stop(wcycle, ewcsNBS_SEARCH_NONLOCAL);
 +
 +            if (nbv->grp[eintNonlocal].kernel_type == nbnxnk8x8x8_CUDA)
 +            {
 +                /* initialize non-local pair-list on the GPU */
 +                nbnxn_cuda_init_pairlist(nbv->cu_nbv,
 +                                         nbv->grp[eintNonlocal].nbl_lists.nbl[0],
 +                                         eintNonlocal);
 +            }
 +            wallcycle_stop(wcycle, ewcNS);
 +        }
 +        else
 +        {
 +            wallcycle_start(wcycle, ewcMOVEX);
 +            dd_move_x(cr->dd, box, x);
 +
 +            /* When we don't need the total dipole we sum it in global_stat */
 +            if (bStateChanged && NEED_MUTOT(*inputrec))
 +            {
 +                gmx_sumd(2*DIM, mu, cr);
 +            }
 +            wallcycle_stop(wcycle, ewcMOVEX);
 +
 +            wallcycle_start(wcycle, ewcNB_XF_BUF_OPS);
 +            wallcycle_sub_start(wcycle, ewcsNB_X_BUF_OPS);
 +            nbnxn_atomdata_copy_x_to_nbat_x(nbv->nbs, eatNonlocal, FALSE, x,
 +                                            nbv->grp[eintNonlocal].nbat);
 +            wallcycle_sub_stop(wcycle, ewcsNB_X_BUF_OPS);
 +            cycles_force += wallcycle_stop(wcycle, ewcNB_XF_BUF_OPS);
 +        }
 +
 +        if (bUseGPU && !bDiffKernels)
 +        {
 +            wallcycle_start(wcycle, ewcLAUNCH_GPU_NB);
 +            /* launch non-local nonbonded F on GPU */
 +            do_nb_verlet(fr, ic, enerd, flags, eintNonlocal, enbvClearFNo,
 +                         nrnb, wcycle);
 +            cycles_force += wallcycle_stop(wcycle, ewcLAUNCH_GPU_NB);
 +        }
 +    }
 +
 +    if (bUseGPU)
 +    {
 +        /* launch D2H copy-back F */
 +        wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU_NB);
 +        if (DOMAINDECOMP(cr) && !bDiffKernels)
 +        {
 +            nbnxn_cuda_launch_cpyback(nbv->cu_nbv, nbv->grp[eintNonlocal].nbat,
 +                                      flags, eatNonlocal);
 +        }
 +        nbnxn_cuda_launch_cpyback(nbv->cu_nbv, nbv->grp[eintLocal].nbat,
 +                                  flags, eatLocal);
 +        cycles_force += wallcycle_stop(wcycle, ewcLAUNCH_GPU_NB);
 +    }
 +
 +    if (bStateChanged && NEED_MUTOT(*inputrec))
 +    {
 +        if (PAR(cr))
 +        {
 +            gmx_sumd(2*DIM, mu, cr);
 +        }
 +
 +        for (i = 0; i < 2; i++)
 +        {
 +            for (j = 0; j < DIM; j++)
 +            {
 +                fr->mu_tot[i][j] = mu[i*DIM + j];
 +            }
 +        }
 +    }
 +    if (fr->efep == efepNO)
 +    {
 +        copy_rvec(fr->mu_tot[0], mu_tot);
 +    }
 +    else
 +    {
 +        for (j = 0; j < DIM; j++)
 +        {
 +            mu_tot[j] =
 +                (1.0 - lambda[efptCOUL])*fr->mu_tot[0][j] +
 +                lambda[efptCOUL]*fr->mu_tot[1][j];
 +        }
 +    }
 +
 +    /* Reset energies */
 +    reset_enerdata(&(inputrec->opts), fr, bNS, enerd, MASTER(cr));
 +    clear_rvecs(SHIFTS, fr->fshift);
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        if (!(cr->duty & DUTY_PME))
 +        {
 +            wallcycle_start(wcycle, ewcPPDURINGPME);
 +            dd_force_flop_start(cr->dd, nrnb);
 +        }
 +    }
 +
++    if (inputrec->bRot)
++    {
++        /* Enforced rotation has its own cycle counter that starts after the collective
++         * coordinates have been communicated. It is added to ddCyclF to allow
++         * for proper load-balancing */
++        wallcycle_start(wcycle, ewcROT);
++        do_rotation(cr, inputrec, box, x, t, step, wcycle, bNS);
++        wallcycle_stop(wcycle, ewcROT);
++    }
++
 +    /* Start the force cycle counter.
 +     * This counter is stopped in do_forcelow_level.
 +     * No parallel communication should occur while this counter is running,
 +     * since that will interfere with the dynamic load balancing.
 +     */
 +    wallcycle_start(wcycle, ewcFORCE);
 +    if (bDoForces)
 +    {
 +        /* Reset forces for which the virial is calculated separately:
 +         * PME/Ewald forces if necessary */
 +        if (fr->bF_NoVirSum)
 +        {
 +            if (flags & GMX_FORCE_VIRIAL)
 +            {
 +                fr->f_novirsum = fr->f_novirsum_alloc;
 +                if (fr->bDomDec)
 +                {
 +                    clear_rvecs(fr->f_novirsum_n, fr->f_novirsum);
 +                }
 +                else
 +                {
 +                    clear_rvecs(homenr, fr->f_novirsum+start);
 +                }
 +            }
 +            else
 +            {
 +                /* We are not calculating the pressure so we do not need
 +                 * a separate array for forces that do not contribute
 +                 * to the pressure.
 +                 */
 +                fr->f_novirsum = f;
 +            }
 +        }
 +
 +        /* Clear the short- and long-range forces */
 +        clear_rvecs(fr->natoms_force_constr, f);
 +        if (bSepLRF && do_per_step(step, inputrec->nstcalclr))
 +        {
 +            clear_rvecs(fr->natoms_force_constr, fr->f_twin);
 +        }
 +
 +        clear_rvec(fr->vir_diag_posres);
 +    }
++
 +    if (inputrec->ePull == epullCONSTRAINT)
 +    {
 +        clear_pull_forces(inputrec->pull);
 +    }
 +
++    /* We calculate the non-bonded forces, when done on the CPU, here.
++     * We do this before calling do_force_lowlevel, as in there bondeds
++     * forces are calculated before PME, which does communication.
++     * With this order, non-bonded and bonded force calculation imbalance
++     * can be balanced out by the domain decomposition load balancing.
++     */
 +
 +    if (!bUseOrEmulGPU)
 +    {
 +        /* Maybe we should move this into do_force_lowlevel */
 +        do_nb_verlet(fr, ic, enerd, flags, eintLocal, enbvClearFYes,
 +                     nrnb, wcycle);
 +    }
 +
 +    if (!bUseOrEmulGPU || bDiffKernels)
 +    {
 +        int aloc;
 +
 +        if (DOMAINDECOMP(cr))
 +        {
 +            do_nb_verlet(fr, ic, enerd, flags, eintNonlocal,
 +                         bDiffKernels ? enbvClearFYes : enbvClearFNo,
 +                         nrnb, wcycle);
 +        }
 +
 +        if (!bUseOrEmulGPU)
 +        {
 +            aloc = eintLocal;
 +        }
 +        else
 +        {
 +            aloc = eintNonlocal;
 +        }
 +
 +        /* Add all the non-bonded force to the normal force array.
 +         * This can be split into a local a non-local part when overlapping
 +         * communication with calculation with domain decomposition.
 +         */
 +        cycles_force += wallcycle_stop(wcycle, ewcFORCE);
 +        wallcycle_start(wcycle, ewcNB_XF_BUF_OPS);
 +        wallcycle_sub_start(wcycle, ewcsNB_F_BUF_OPS);
 +        nbnxn_atomdata_add_nbat_f_to_f(nbv->nbs, eatAll, nbv->grp[aloc].nbat, f);
 +        wallcycle_sub_stop(wcycle, ewcsNB_F_BUF_OPS);
 +        cycles_force += wallcycle_stop(wcycle, ewcNB_XF_BUF_OPS);
 +        wallcycle_start_nocount(wcycle, ewcFORCE);
 +
 +        /* if there are multiple fshift output buffers reduce them */
 +        if ((flags & GMX_FORCE_VIRIAL) &&
 +            nbv->grp[aloc].nbl_lists.nnbl > 1)
 +        {
 +            nbnxn_atomdata_add_nbat_fshift_to_fshift(nbv->grp[aloc].nbat,
 +                                                     fr->fshift);
 +        }
 +    }
 +
++    /* update QMMMrec, if necessary */
++    if (fr->bQMMM)
++    {
++        update_QMMMrec(cr, fr, x, mdatoms, box, top);
++    }
++
++    if ((flags & GMX_FORCE_BONDED) && top->idef.il[F_POSRES].nr > 0)
++    {
++        posres_wrapper(fplog, flags, bSepDVDL, inputrec, nrnb, top, box, x,
++                       f, enerd, lambda, fr);
++    }
++
++    /* Compute the bonded and non-bonded energies and optionally forces */
++    do_force_lowlevel(fplog, step, fr, inputrec, &(top->idef),
++                      cr, nrnb, wcycle, mdatoms, &(inputrec->opts),
++                      x, hist, f, bSepLRF ? fr->f_twin : f, enerd, fcd, mtop, top, fr->born,
++                      &(top->atomtypes), bBornRadii, box,
++                      inputrec->fepvals, lambda, graph, &(top->excls), fr->mu_tot,
++                      flags, &cycles_pme);
++
++    if (bSepLRF)
++    {
++        if (do_per_step(step, inputrec->nstcalclr))
++        {
++            /* Add the long range forces to the short range forces */
++            for (i = 0; i < fr->natoms_force_constr; i++)
++            {
++                rvec_add(fr->f_twin[i], f[i], f[i]);
++            }
++        }
++    }
++
 +    cycles_force += wallcycle_stop(wcycle, ewcFORCE);
 +
 +    if (ed)
 +    {
 +        do_flood(cr, inputrec, x, f, ed, box, step, bNS);
 +    }
 +
 +    if (bUseOrEmulGPU && !bDiffKernels)
 +    {
 +        /* wait for non-local forces (or calculate in emulation mode) */
 +        if (DOMAINDECOMP(cr))
 +        {
 +            if (bUseGPU)
 +            {
 +                wallcycle_start(wcycle, ewcWAIT_GPU_NB_NL);
 +                nbnxn_cuda_wait_gpu(nbv->cu_nbv,
 +                                    nbv->grp[eintNonlocal].nbat,
 +                                    flags, eatNonlocal,
 +                                    enerd->grpp.ener[egLJSR], enerd->grpp.ener[egCOULSR],
 +                                    fr->fshift);
 +                cycles_force += wallcycle_stop(wcycle, ewcWAIT_GPU_NB_NL);
 +            }
 +            else
 +            {
 +                wallcycle_start_nocount(wcycle, ewcFORCE);
 +                do_nb_verlet(fr, ic, enerd, flags, eintNonlocal, enbvClearFYes,
 +                             nrnb, wcycle);
 +                cycles_force += wallcycle_stop(wcycle, ewcFORCE);
 +            }
 +            wallcycle_start(wcycle, ewcNB_XF_BUF_OPS);
 +            wallcycle_sub_start(wcycle, ewcsNB_F_BUF_OPS);
 +            /* skip the reduction if there was no non-local work to do */
 +            if (nbv->grp[eintLocal].nbl_lists.nbl[0]->nsci > 0)
 +            {
 +                nbnxn_atomdata_add_nbat_f_to_f(nbv->nbs, eatNonlocal,
 +                                               nbv->grp[eintNonlocal].nbat, f);
 +            }
 +            wallcycle_sub_stop(wcycle, ewcsNB_F_BUF_OPS);
 +            cycles_force += wallcycle_stop(wcycle, ewcNB_XF_BUF_OPS);
 +        }
 +    }
 +
 +    if (bDoForces)
 +    {
 +        /* Communicate the forces */
 +        if (PAR(cr))
 +        {
 +            wallcycle_start(wcycle, ewcMOVEF);
 +            if (DOMAINDECOMP(cr))
 +            {
 +                dd_move_f(cr->dd, f, fr->fshift);
 +                /* Do we need to communicate the separate force array
 +                 * for terms that do not contribute to the single sum virial?
 +                 * Position restraints and electric fields do not introduce
 +                 * inter-cg forces, only full electrostatics methods do.
 +                 * When we do not calculate the virial, fr->f_novirsum = f,
 +                 * so we have already communicated these forces.
 +                 */
 +                if (EEL_FULL(fr->eeltype) && cr->dd->n_intercg_excl &&
 +                    (flags & GMX_FORCE_VIRIAL))
 +                {
 +                    dd_move_f(cr->dd, fr->f_novirsum, NULL);
 +                }
 +                if (bSepLRF)
 +                {
 +                    /* We should not update the shift forces here,
 +                     * since f_twin is already included in f.
 +                     */
 +                    dd_move_f(cr->dd, fr->f_twin, NULL);
 +                }
 +            }
 +            wallcycle_stop(wcycle, ewcMOVEF);
 +        }
 +    }
 +
 +    if (bUseOrEmulGPU)
 +    {
 +        /* wait for local forces (or calculate in emulation mode) */
 +        if (bUseGPU)
 +        {
 +            wallcycle_start(wcycle, ewcWAIT_GPU_NB_L);
 +            nbnxn_cuda_wait_gpu(nbv->cu_nbv,
 +                                nbv->grp[eintLocal].nbat,
 +                                flags, eatLocal,
 +                                enerd->grpp.ener[egLJSR], enerd->grpp.ener[egCOULSR],
 +                                fr->fshift);
 +            wallcycle_stop(wcycle, ewcWAIT_GPU_NB_L);
 +
 +            /* now clear the GPU outputs while we finish the step on the CPU */
 +
 +            wallcycle_start_nocount(wcycle, ewcLAUNCH_GPU_NB);
 +            nbnxn_cuda_clear_outputs(nbv->cu_nbv, flags);
 +            wallcycle_stop(wcycle, ewcLAUNCH_GPU_NB);
 +        }
 +        else
 +        {
 +            wallcycle_start_nocount(wcycle, ewcFORCE);
 +            do_nb_verlet(fr, ic, enerd, flags, eintLocal,
 +                         DOMAINDECOMP(cr) ? enbvClearFNo : enbvClearFYes,
 +                         nrnb, wcycle);
 +            wallcycle_stop(wcycle, ewcFORCE);
 +        }
 +        wallcycle_start(wcycle, ewcNB_XF_BUF_OPS);
 +        wallcycle_sub_start(wcycle, ewcsNB_F_BUF_OPS);
 +        if (nbv->grp[eintLocal].nbl_lists.nbl[0]->nsci > 0)
 +        {
 +            /* skip the reduction if there was no non-local work to do */
 +            nbnxn_atomdata_add_nbat_f_to_f(nbv->nbs, eatLocal,
 +                                           nbv->grp[eintLocal].nbat, f);
 +        }
 +        wallcycle_sub_stop(wcycle, ewcsNB_F_BUF_OPS);
 +        wallcycle_stop(wcycle, ewcNB_XF_BUF_OPS);
 +    }
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        dd_force_flop_stop(cr->dd, nrnb);
 +        if (wcycle)
 +        {
 +            dd_cycles_add(cr->dd, cycles_force-cycles_pme, ddCyclF);
 +        }
 +    }
 +
 +    if (bDoForces)
 +    {
 +        if (IR_ELEC_FIELD(*inputrec))
 +        {
 +            /* Compute forces due to electric field */
 +            calc_f_el(MASTER(cr) ? field : NULL,
 +                      start, homenr, mdatoms->chargeA, x, fr->f_novirsum,
 +                      inputrec->ex, inputrec->et, t);
 +        }
 +
 +        /* If we have NoVirSum forces, but we do not calculate the virial,
 +         * we sum fr->f_novirum=f later.
 +         */
 +        if (vsite && !(fr->bF_NoVirSum && !(flags & GMX_FORCE_VIRIAL)))
 +        {
 +            wallcycle_start(wcycle, ewcVSITESPREAD);
 +            spread_vsite_f(fplog, vsite, x, f, fr->fshift, FALSE, NULL, nrnb,
 +                           &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr);
 +            wallcycle_stop(wcycle, ewcVSITESPREAD);
 +
 +            if (bSepLRF)
 +            {
 +                wallcycle_start(wcycle, ewcVSITESPREAD);
 +                spread_vsite_f(fplog, vsite, x, fr->f_twin, NULL, FALSE, NULL,
 +                               nrnb,
 +                               &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr);
 +                wallcycle_stop(wcycle, ewcVSITESPREAD);
 +            }
 +        }
 +
 +        if (flags & GMX_FORCE_VIRIAL)
 +        {
 +            /* Calculation of the virial must be done after vsites! */
 +            calc_virial(fplog, mdatoms->start, mdatoms->homenr, x, f,
 +                        vir_force, graph, box, nrnb, fr, inputrec->ePBC);
 +        }
 +    }
 +
 +    if (inputrec->ePull == epullUMBRELLA || inputrec->ePull == epullCONST_F)
 +    {
 +        pull_potential_wrapper(fplog, bSepDVDL, cr, inputrec, box, x,
 +                               f, vir_force, mdatoms, enerd, lambda, t);
 +    }
 +
++    /* Add the forces from enforced rotation potentials (if any) */
++    if (inputrec->bRot)
++    {
++        wallcycle_start(wcycle, ewcROTadd);
++        enerd->term[F_COM_PULL] += add_rot_forces(inputrec->rot, f, cr, step, t);
++        wallcycle_stop(wcycle, ewcROTadd);
++    }
++
 +    if (PAR(cr) && !(cr->duty & DUTY_PME))
 +    {
 +        /* In case of node-splitting, the PP nodes receive the long-range
 +         * forces, virial and energy from the PME nodes here.
 +         */
 +        pme_receive_force_ener(fplog, bSepDVDL, cr, wcycle, enerd, fr);
 +    }
 +
 +    if (bDoForces)
 +    {
 +        post_process_forces(fplog, cr, step, nrnb, wcycle,
 +                            top, box, x, f, vir_force, mdatoms, graph, fr, vsite,
 +                            flags);
 +    }
 +
 +    /* Sum the potential energy terms from group contributions */
 +    sum_epot(&(inputrec->opts), &(enerd->grpp), enerd->term);
 +}
 +
 +void do_force_cutsGROUP(FILE *fplog, t_commrec *cr,
 +                        t_inputrec *inputrec,
 +                        gmx_large_int_t step, t_nrnb *nrnb, gmx_wallcycle_t wcycle,
 +                        gmx_localtop_t *top,
 +                        gmx_mtop_t *mtop,
 +                        gmx_groups_t *groups,
 +                        matrix box, rvec x[], history_t *hist,
 +                        rvec f[],
 +                        tensor vir_force,
 +                        t_mdatoms *mdatoms,
 +                        gmx_enerdata_t *enerd, t_fcdata *fcd,
 +                        real *lambda, t_graph *graph,
 +                        t_forcerec *fr, gmx_vsite_t *vsite, rvec mu_tot,
 +                        double t, FILE *field, gmx_edsam_t ed,
 +                        gmx_bool bBornRadii,
 +                        int flags)
 +{
 +    int        cg0, cg1, i, j;
 +    int        start, homenr;
 +    double     mu[2*DIM];
 +    gmx_bool   bSepDVDL, bStateChanged, bNS, bFillGrid, bCalcCGCM, bBS;
 +    gmx_bool   bDoLongRangeNS, bDoForces, bDoPotential, bSepLRF;
 +    gmx_bool   bDoAdressWF;
 +    matrix     boxs;
 +    rvec       vzero, box_diag;
 +    real       e, v, dvdlambda[efptNR];
 +    t_pbc      pbc;
 +    float      cycles_pme, cycles_force;
 +
 +    start  = mdatoms->start;
 +    homenr = mdatoms->homenr;
 +
 +    bSepDVDL = (fr->bSepDVDL && do_per_step(step, inputrec->nstlog));
 +
 +    clear_mat(vir_force);
 +
 +    if (PARTDECOMP(cr))
 +    {
 +        pd_cg_range(cr, &cg0, &cg1);
 +    }
 +    else
 +    {
 +        cg0 = 0;
 +        if (DOMAINDECOMP(cr))
 +        {
 +            cg1 = cr->dd->ncg_tot;
 +        }
 +        else
 +        {
 +            cg1 = top->cgs.nr;
 +        }
 +        if (fr->n_tpi > 0)
 +        {
 +            cg1--;
 +        }
 +    }
 +
 +    bStateChanged  = (flags & GMX_FORCE_STATECHANGED);
 +    bNS            = (flags & GMX_FORCE_NS) && (fr->bAllvsAll == FALSE);
 +    /* Should we update the long-range neighborlists at this step? */
 +    bDoLongRangeNS = fr->bTwinRange && bNS;
 +    /* Should we perform the long-range nonbonded evaluation inside the neighborsearching? */
 +    bFillGrid      = (bNS && bStateChanged);
 +    bCalcCGCM      = (bFillGrid && !DOMAINDECOMP(cr));
 +    bDoForces      = (flags & GMX_FORCE_FORCES);
 +    bDoPotential   = (flags & GMX_FORCE_ENERGY);
 +    bSepLRF        = ((inputrec->nstcalclr > 1) && bDoForces &&
 +                      (flags & GMX_FORCE_SEPLRF) && (flags & GMX_FORCE_DO_LR));
 +
 +    /* should probably move this to the forcerec since it doesn't change */
 +    bDoAdressWF   = ((fr->adress_type != eAdressOff));
 +
 +    if (bStateChanged)
 +    {
 +        update_forcerec(fplog, fr, box);
 +
 +        if (NEED_MUTOT(*inputrec))
 +        {
 +            /* Calculate total (local) dipole moment in a temporary common array.
 +             * This makes it possible to sum them over nodes faster.
 +             */
 +            calc_mu(start, homenr,
 +                    x, mdatoms->chargeA, mdatoms->chargeB, mdatoms->nChargePerturbed,
 +                    mu, mu+DIM);
 +        }
 +    }
 +
 +    if (fr->ePBC != epbcNONE)
 +    {
 +        /* Compute shift vectors every step,
 +         * because of pressure coupling or box deformation!
 +         */
 +        if ((flags & GMX_FORCE_DYNAMICBOX) && bStateChanged)
 +        {
 +            calc_shifts(box, fr->shift_vec);
 +        }
 +
 +        if (bCalcCGCM)
 +        {
 +            put_charge_groups_in_box(fplog, cg0, cg1, fr->ePBC, box,
 +                                     &(top->cgs), x, fr->cg_cm);
 +            inc_nrnb(nrnb, eNR_CGCM, homenr);
 +            inc_nrnb(nrnb, eNR_RESETX, cg1-cg0);
 +        }
 +        else if (EI_ENERGY_MINIMIZATION(inputrec->eI) && graph)
 +        {
 +            unshift_self(graph, box, x);
 +        }
 +    }
 +    else if (bCalcCGCM)
 +    {
 +        calc_cgcm(fplog, cg0, cg1, &(top->cgs), x, fr->cg_cm);
 +        inc_nrnb(nrnb, eNR_CGCM, homenr);
 +    }
 +
 +    if (bCalcCGCM)
 +    {
 +        if (PAR(cr))
 +        {
 +            move_cgcm(fplog, cr, fr->cg_cm);
 +        }
 +        if (gmx_debug_at)
 +        {
 +            pr_rvecs(debug, 0, "cgcm", fr->cg_cm, top->cgs.nr);
 +        }
 +    }
 +
 +#ifdef GMX_MPI
 +    if (!(cr->duty & DUTY_PME))
 +    {
 +        /* Send particle coordinates to the pme nodes.
 +         * Since this is only implemented for domain decomposition
 +         * and domain decomposition does not use the graph,
 +         * we do not need to worry about shifting.
 +         */
 +
 +        wallcycle_start(wcycle, ewcPP_PMESENDX);
 +
 +        bBS = (inputrec->nwall == 2);
 +        if (bBS)
 +        {
 +            copy_mat(box, boxs);
 +            svmul(inputrec->wall_ewald_zfac, boxs[ZZ], boxs[ZZ]);
 +        }
 +
 +        gmx_pme_send_x(cr, bBS ? boxs : box, x,
 +                       mdatoms->nChargePerturbed, lambda[efptCOUL],
 +                       (flags & (GMX_FORCE_VIRIAL | GMX_FORCE_ENERGY)), step);
 +
 +        wallcycle_stop(wcycle, ewcPP_PMESENDX);
 +    }
 +#endif /* GMX_MPI */
 +
 +    /* Communicate coordinates and sum dipole if necessary */
 +    if (PAR(cr))
 +    {
 +        wallcycle_start(wcycle, ewcMOVEX);
 +        if (DOMAINDECOMP(cr))
 +        {
 +            dd_move_x(cr->dd, box, x);
 +        }
 +        else
 +        {
 +            move_x(fplog, cr, GMX_LEFT, GMX_RIGHT, x, nrnb);
 +        }
 +        wallcycle_stop(wcycle, ewcMOVEX);
 +    }
 +
 +    /* update adress weight beforehand */
 +    if (bStateChanged && bDoAdressWF)
 +    {
 +        /* need pbc for adress weight calculation with pbc_dx */
 +        set_pbc(&pbc, inputrec->ePBC, box);
 +        if (fr->adress_site == eAdressSITEcog)
 +        {
 +            update_adress_weights_cog(top->idef.iparams, top->idef.il, x, fr, mdatoms,
 +                                      inputrec->ePBC == epbcNONE ? NULL : &pbc);
 +        }
 +        else if (fr->adress_site == eAdressSITEcom)
 +        {
 +            update_adress_weights_com(fplog, cg0, cg1, &(top->cgs), x, fr, mdatoms,
 +                                      inputrec->ePBC == epbcNONE ? NULL : &pbc);
 +        }
 +        else if (fr->adress_site == eAdressSITEatomatom)
 +        {
 +            update_adress_weights_atom_per_atom(cg0, cg1, &(top->cgs), x, fr, mdatoms,
 +                                                inputrec->ePBC == epbcNONE ? NULL : &pbc);
 +        }
 +        else
 +        {
 +            update_adress_weights_atom(cg0, cg1, &(top->cgs), x, fr, mdatoms,
 +                                       inputrec->ePBC == epbcNONE ? NULL : &pbc);
 +        }
 +    }
 +
 +    if (NEED_MUTOT(*inputrec))
 +    {
 +
 +        if (bStateChanged)
 +        {
 +            if (PAR(cr))
 +            {
 +                gmx_sumd(2*DIM, mu, cr);
 +            }
 +            for (i = 0; i < 2; i++)
 +            {
 +                for (j = 0; j < DIM; j++)
 +                {
 +                    fr->mu_tot[i][j] = mu[i*DIM + j];
 +                }
 +            }
 +        }
 +        if (fr->efep == efepNO)
 +        {
 +            copy_rvec(fr->mu_tot[0], mu_tot);
 +        }
 +        else
 +        {
 +            for (j = 0; j < DIM; j++)
 +            {
 +                mu_tot[j] =
 +                    (1.0 - lambda[efptCOUL])*fr->mu_tot[0][j] + lambda[efptCOUL]*fr->mu_tot[1][j];
 +            }
 +        }
 +    }
 +
 +    /* Reset energies */
 +    reset_enerdata(&(inputrec->opts), fr, bNS, enerd, MASTER(cr));
 +    clear_rvecs(SHIFTS, fr->fshift);
 +
 +    if (bNS)
 +    {
 +        wallcycle_start(wcycle, ewcNS);
 +
 +        if (graph && bStateChanged)
 +        {
 +            /* Calculate intramolecular shift vectors to make molecules whole */
 +            mk_mshift(fplog, graph, fr->ePBC, box, x);
 +        }
 +
 +        /* Do the actual neighbour searching and if twin range electrostatics
 +         * also do the calculation of long range forces and energies.
 +         */
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            dvdlambda[i] = 0;
 +        }
 +        ns(fplog, fr, x, box,
 +           groups, &(inputrec->opts), top, mdatoms,
 +           cr, nrnb, lambda, dvdlambda, &enerd->grpp, bFillGrid,
 +           bDoLongRangeNS);
 +        if (bSepDVDL)
 +        {
 +            fprintf(fplog, sepdvdlformat, "LR non-bonded", 0.0, dvdlambda);
 +        }
 +        enerd->dvdl_lin[efptVDW]  += dvdlambda[efptVDW];
 +        enerd->dvdl_lin[efptCOUL] += dvdlambda[efptCOUL];
 +
 +        wallcycle_stop(wcycle, ewcNS);
 +    }
 +
 +    if (inputrec->implicit_solvent && bNS)
 +    {
 +        make_gb_nblist(cr, inputrec->gb_algorithm, inputrec->rlist,
 +                       x, box, fr, &top->idef, graph, fr->born);
 +    }
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        if (!(cr->duty & DUTY_PME))
 +        {
 +            wallcycle_start(wcycle, ewcPPDURINGPME);
 +            dd_force_flop_start(cr->dd, nrnb);
 +        }
 +    }
 +
 +    if (inputrec->bRot)
 +    {
 +        /* Enforced rotation has its own cycle counter that starts after the collective
 +         * coordinates have been communicated. It is added to ddCyclF to allow
 +         * for proper load-balancing */
 +        wallcycle_start(wcycle, ewcROT);
 +        do_rotation(cr, inputrec, box, x, t, step, wcycle, bNS);
 +        wallcycle_stop(wcycle, ewcROT);
 +    }
 +
 +    /* Start the force cycle counter.
 +     * This counter is stopped in do_forcelow_level.
 +     * No parallel communication should occur while this counter is running,
 +     * since that will interfere with the dynamic load balancing.
 +     */
 +    wallcycle_start(wcycle, ewcFORCE);
 +
 +    if (bDoForces)
 +    {
 +        /* Reset forces for which the virial is calculated separately:
 +         * PME/Ewald forces if necessary */
 +        if (fr->bF_NoVirSum)
 +        {
 +            if (flags & GMX_FORCE_VIRIAL)
 +            {
 +                fr->f_novirsum = fr->f_novirsum_alloc;
 +                if (fr->bDomDec)
 +                {
 +                    clear_rvecs(fr->f_novirsum_n, fr->f_novirsum);
 +                }
 +                else
 +                {
 +                    clear_rvecs(homenr, fr->f_novirsum+start);
 +                }
 +            }
 +            else
 +            {
 +                /* We are not calculating the pressure so we do not need
 +                 * a separate array for forces that do not contribute
 +                 * to the pressure.
 +                 */
 +                fr->f_novirsum = f;
 +            }
 +        }
 +
 +        /* Clear the short- and long-range forces */
 +        clear_rvecs(fr->natoms_force_constr, f);
 +        if (bSepLRF && do_per_step(step, inputrec->nstcalclr))
 +        {
 +            clear_rvecs(fr->natoms_force_constr, fr->f_twin);
 +        }
 +
 +        clear_rvec(fr->vir_diag_posres);
 +    }
 +    if (inputrec->ePull == epullCONSTRAINT)
 +    {
 +        clear_pull_forces(inputrec->pull);
 +    }
 +
 +    /* update QMMMrec, if necessary */
 +    if (fr->bQMMM)
 +    {
 +        update_QMMMrec(cr, fr, x, mdatoms, box, top);
 +    }
 +
 +    if ((flags & GMX_FORCE_BONDED) && top->idef.il[F_POSRES].nr > 0)
 +    {
 +        posres_wrapper(fplog, flags, bSepDVDL, inputrec, nrnb, top, box, x,
 +                       f, enerd, lambda, fr);
 +    }
 +
 +    if ((flags & GMX_FORCE_BONDED) && top->idef.il[F_FBPOSRES].nr > 0)
 +    {
 +        /* Flat-bottomed position restraints always require full pbc */
 +        if (!(bStateChanged && bDoAdressWF))
 +        {
 +            set_pbc(&pbc, inputrec->ePBC, box);
 +        }
 +        v = fbposres(top->idef.il[F_FBPOSRES].nr, top->idef.il[F_FBPOSRES].iatoms,
 +                     top->idef.iparams_fbposres,
 +                     (const rvec*)x, fr->f_novirsum, fr->vir_diag_posres,
 +                     inputrec->ePBC == epbcNONE ? NULL : &pbc,
 +                     fr->rc_scaling, fr->ePBC, fr->posres_com);
 +        enerd->term[F_FBPOSRES] += v;
 +        inc_nrnb(nrnb, eNR_FBPOSRES, top->idef.il[F_FBPOSRES].nr/2);
 +    }
 +
 +    /* Compute the bonded and non-bonded energies and optionally forces */
 +    do_force_lowlevel(fplog, step, fr, inputrec, &(top->idef),
 +                      cr, nrnb, wcycle, mdatoms, &(inputrec->opts),
 +                      x, hist, f, bSepLRF ? fr->f_twin : f, enerd, fcd, mtop, top, fr->born,
 +                      &(top->atomtypes), bBornRadii, box,
 +                      inputrec->fepvals, lambda,
 +                      graph, &(top->excls), fr->mu_tot,
 +                      flags,
 +                      &cycles_pme);
 +
 +    if (bSepLRF)
 +    {
 +        if (do_per_step(step, inputrec->nstcalclr))
 +        {
 +            /* Add the long range forces to the short range forces */
 +            for (i = 0; i < fr->natoms_force_constr; i++)
 +            {
 +                rvec_add(fr->f_twin[i], f[i], f[i]);
 +            }
 +        }
 +    }
 +
 +    cycles_force = wallcycle_stop(wcycle, ewcFORCE);
 +
 +    if (ed)
 +    {
 +        do_flood(cr, inputrec, x, f, ed, box, step, bNS);
 +    }
 +
 +    if (DOMAINDECOMP(cr))
 +    {
 +        dd_force_flop_stop(cr->dd, nrnb);
 +        if (wcycle)
 +        {
 +            dd_cycles_add(cr->dd, cycles_force-cycles_pme, ddCyclF);
 +        }
 +    }
 +
 +    if (bDoForces)
 +    {
 +        if (IR_ELEC_FIELD(*inputrec))
 +        {
 +            /* Compute forces due to electric field */
 +            calc_f_el(MASTER(cr) ? field : NULL,
 +                      start, homenr, mdatoms->chargeA, x, fr->f_novirsum,
 +                      inputrec->ex, inputrec->et, t);
 +        }
 +
 +        if (bDoAdressWF && fr->adress_icor == eAdressICThermoForce)
 +        {
 +            /* Compute thermodynamic force in hybrid AdResS region */
 +            adress_thermo_force(start, homenr, &(top->cgs), x, fr->f_novirsum, fr, mdatoms,
 +                                inputrec->ePBC == epbcNONE ? NULL : &pbc);
 +        }
 +
 +        /* Communicate the forces */
 +        if (PAR(cr))
 +        {
 +            wallcycle_start(wcycle, ewcMOVEF);
 +            if (DOMAINDECOMP(cr))
 +            {
 +                dd_move_f(cr->dd, f, fr->fshift);
 +                /* Do we need to communicate the separate force array
 +                 * for terms that do not contribute to the single sum virial?
 +                 * Position restraints and electric fields do not introduce
 +                 * inter-cg forces, only full electrostatics methods do.
 +                 * When we do not calculate the virial, fr->f_novirsum = f,
 +                 * so we have already communicated these forces.
 +                 */
 +                if (EEL_FULL(fr->eeltype) && cr->dd->n_intercg_excl &&
 +                    (flags & GMX_FORCE_VIRIAL))
 +                {
 +                    dd_move_f(cr->dd, fr->f_novirsum, NULL);
 +                }
 +                if (bSepLRF)
 +                {
 +                    /* We should not update the shift forces here,
 +                     * since f_twin is already included in f.
 +                     */
 +                    dd_move_f(cr->dd, fr->f_twin, NULL);
 +                }
 +            }
 +            else
 +            {
 +                pd_move_f(cr, f, nrnb);
 +                if (bSepLRF)
 +                {
 +                    pd_move_f(cr, fr->f_twin, nrnb);
 +                }
 +            }
 +            wallcycle_stop(wcycle, ewcMOVEF);
 +        }
 +
 +        /* If we have NoVirSum forces, but we do not calculate the virial,
 +         * we sum fr->f_novirum=f later.
 +         */
 +        if (vsite && !(fr->bF_NoVirSum && !(flags & GMX_FORCE_VIRIAL)))
 +        {
 +            wallcycle_start(wcycle, ewcVSITESPREAD);
 +            spread_vsite_f(fplog, vsite, x, f, fr->fshift, FALSE, NULL, nrnb,
 +                           &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr);
 +            wallcycle_stop(wcycle, ewcVSITESPREAD);
 +
 +            if (bSepLRF)
 +            {
 +                wallcycle_start(wcycle, ewcVSITESPREAD);
 +                spread_vsite_f(fplog, vsite, x, fr->f_twin, NULL, FALSE, NULL,
 +                               nrnb,
 +                               &top->idef, fr->ePBC, fr->bMolPBC, graph, box, cr);
 +                wallcycle_stop(wcycle, ewcVSITESPREAD);
 +            }
 +        }
 +
 +        if (flags & GMX_FORCE_VIRIAL)
 +        {
 +            /* Calculation of the virial must be done after vsites! */
 +            calc_virial(fplog, mdatoms->start, mdatoms->homenr, x, f,
 +                        vir_force, graph, box, nrnb, fr, inputrec->ePBC);
 +        }
 +    }
 +
 +    if (inputrec->ePull == epullUMBRELLA || inputrec->ePull == epullCONST_F)
 +    {
 +        pull_potential_wrapper(fplog, bSepDVDL, cr, inputrec, box, x,
 +                               f, vir_force, mdatoms, enerd, lambda, t);
 +    }
 +
 +    /* Add the forces from enforced rotation potentials (if any) */
 +    if (inputrec->bRot)
 +    {
 +        wallcycle_start(wcycle, ewcROTadd);
 +        enerd->term[F_COM_PULL] += add_rot_forces(inputrec->rot, f, cr, step, t);
 +        wallcycle_stop(wcycle, ewcROTadd);
 +    }
 +
 +    if (PAR(cr) && !(cr->duty & DUTY_PME))
 +    {
 +        /* In case of node-splitting, the PP nodes receive the long-range
 +         * forces, virial and energy from the PME nodes here.
 +         */
 +        pme_receive_force_ener(fplog, bSepDVDL, cr, wcycle, enerd, fr);
 +    }
 +
 +    if (bDoForces)
 +    {
 +        post_process_forces(fplog, cr, step, nrnb, wcycle,
 +                            top, box, x, f, vir_force, mdatoms, graph, fr, vsite,
 +                            flags);
 +    }
 +
 +    /* Sum the potential energy terms from group contributions */
 +    sum_epot(&(inputrec->opts), &(enerd->grpp), enerd->term);
 +}
 +
 +void do_force(FILE *fplog, t_commrec *cr,
 +              t_inputrec *inputrec,
 +              gmx_large_int_t step, t_nrnb *nrnb, gmx_wallcycle_t wcycle,
 +              gmx_localtop_t *top,
 +              gmx_mtop_t *mtop,
 +              gmx_groups_t *groups,
 +              matrix box, rvec x[], history_t *hist,
 +              rvec f[],
 +              tensor vir_force,
 +              t_mdatoms *mdatoms,
 +              gmx_enerdata_t *enerd, t_fcdata *fcd,
 +              real *lambda, t_graph *graph,
 +              t_forcerec *fr,
 +              gmx_vsite_t *vsite, rvec mu_tot,
 +              double t, FILE *field, gmx_edsam_t ed,
 +              gmx_bool bBornRadii,
 +              int flags)
 +{
 +    /* modify force flag if not doing nonbonded */
 +    if (!fr->bNonbonded)
 +    {
 +        flags &= ~GMX_FORCE_NONBONDED;
 +    }
 +
 +    switch (inputrec->cutoff_scheme)
 +    {
 +        case ecutsVERLET:
 +            do_force_cutsVERLET(fplog, cr, inputrec,
 +                                step, nrnb, wcycle,
 +                                top, mtop,
 +                                groups,
 +                                box, x, hist,
 +                                f, vir_force,
 +                                mdatoms,
 +                                enerd, fcd,
 +                                lambda, graph,
 +                                fr, fr->ic,
 +                                vsite, mu_tot,
 +                                t, field, ed,
 +                                bBornRadii,
 +                                flags);
 +            break;
 +        case ecutsGROUP:
 +            do_force_cutsGROUP(fplog, cr, inputrec,
 +                               step, nrnb, wcycle,
 +                               top, mtop,
 +                               groups,
 +                               box, x, hist,
 +                               f, vir_force,
 +                               mdatoms,
 +                               enerd, fcd,
 +                               lambda, graph,
 +                               fr, vsite, mu_tot,
 +                               t, field, ed,
 +                               bBornRadii,
 +                               flags);
 +            break;
 +        default:
 +            gmx_incons("Invalid cut-off scheme passed!");
 +    }
 +}
 +
 +
 +void do_constrain_first(FILE *fplog, gmx_constr_t constr,
 +                        t_inputrec *ir, t_mdatoms *md,
 +                        t_state *state, rvec *f,
 +                        t_graph *graph, t_commrec *cr, t_nrnb *nrnb,
 +                        t_forcerec *fr, gmx_localtop_t *top, tensor shake_vir)
 +{
 +    int             i, m, start, end;
 +    gmx_large_int_t step;
 +    real            dt = ir->delta_t;
 +    real            dvdl_dum;
 +    rvec           *savex;
 +
 +    snew(savex, state->natoms);
 +
 +    start = md->start;
 +    end   = md->homenr + start;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "vcm: start=%d, homenr=%d, end=%d\n",
 +                start, md->homenr, end);
 +    }
 +    /* Do a first constrain to reset particles... */
 +    step = ir->init_step;
 +    if (fplog)
 +    {
 +        char buf[STEPSTRSIZE];
 +        fprintf(fplog, "\nConstraining the starting coordinates (step %s)\n",
 +                gmx_step_str(step, buf));
 +    }
 +    dvdl_dum = 0;
 +
 +    /* constrain the current position */
 +    constrain(NULL, TRUE, FALSE, constr, &(top->idef),
 +              ir, NULL, cr, step, 0, md,
 +              state->x, state->x, NULL,
 +              fr->bMolPBC, state->box,
 +              state->lambda[efptBONDED], &dvdl_dum,
 +              NULL, NULL, nrnb, econqCoord,
 +              ir->epc == epcMTTK, state->veta, state->veta);
 +    if (EI_VV(ir->eI))
 +    {
 +        /* constrain the inital velocity, and save it */
 +        /* also may be useful if we need the ekin from the halfstep for velocity verlet */
 +        /* might not yet treat veta correctly */
 +        constrain(NULL, TRUE, FALSE, constr, &(top->idef),
 +                  ir, NULL, cr, step, 0, md,
 +                  state->x, state->v, state->v,
 +                  fr->bMolPBC, state->box,
 +                  state->lambda[efptBONDED], &dvdl_dum,
 +                  NULL, NULL, nrnb, econqVeloc,
 +                  ir->epc == epcMTTK, state->veta, state->veta);
 +    }
 +    /* constrain the inital velocities at t-dt/2 */
 +    if (EI_STATE_VELOCITY(ir->eI) && ir->eI != eiVV)
 +    {
 +        for (i = start; (i < end); i++)
 +        {
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                /* Reverse the velocity */
 +                state->v[i][m] = -state->v[i][m];
 +                /* Store the position at t-dt in buf */
 +                savex[i][m] = state->x[i][m] + dt*state->v[i][m];
 +            }
 +        }
 +        /* Shake the positions at t=-dt with the positions at t=0
 +         * as reference coordinates.
 +         */
 +        if (fplog)
 +        {
 +            char buf[STEPSTRSIZE];
 +            fprintf(fplog, "\nConstraining the coordinates at t0-dt (step %s)\n",
 +                    gmx_step_str(step, buf));
 +        }
 +        dvdl_dum = 0;
 +        constrain(NULL, TRUE, FALSE, constr, &(top->idef),
 +                  ir, NULL, cr, step, -1, md,
 +                  state->x, savex, NULL,
 +                  fr->bMolPBC, state->box,
 +                  state->lambda[efptBONDED], &dvdl_dum,
 +                  state->v, NULL, nrnb, econqCoord,
 +                  ir->epc == epcMTTK, state->veta, state->veta);
 +
 +        for (i = start; i < end; i++)
 +        {
 +            for (m = 0; m < DIM; m++)
 +            {
 +                /* Re-reverse the velocities */
 +                state->v[i][m] = -state->v[i][m];
 +            }
 +        }
 +    }
 +    sfree(savex);
 +}
 +
 +void calc_enervirdiff(FILE *fplog, int eDispCorr, t_forcerec *fr)
 +{
 +    double eners[2], virs[2], enersum, virsum, y0, f, g, h;
 +    double r0, r1, r, rc3, rc9, ea, eb, ec, pa, pb, pc, pd;
 +    double invscale, invscale2, invscale3;
 +    int    ri0, ri1, ri, i, offstart, offset;
 +    real   scale, *vdwtab, tabfactor, tmp;
 +
 +    fr->enershiftsix    = 0;
 +    fr->enershifttwelve = 0;
 +    fr->enerdiffsix     = 0;
 +    fr->enerdifftwelve  = 0;
 +    fr->virdiffsix      = 0;
 +    fr->virdifftwelve   = 0;
 +
 +    if (eDispCorr != edispcNO)
 +    {
 +        for (i = 0; i < 2; i++)
 +        {
 +            eners[i] = 0;
 +            virs[i]  = 0;
 +        }
 +        if ((fr->vdwtype == evdwSWITCH) || (fr->vdwtype == evdwSHIFT))
 +        {
 +            if (fr->rvdw_switch == 0)
 +            {
 +                gmx_fatal(FARGS,
 +                          "With dispersion correction rvdw-switch can not be zero "
 +                          "for vdw-type = %s", evdw_names[fr->vdwtype]);
 +            }
 +
 +            scale  = fr->nblists[0].table_elec_vdw.scale;
 +            vdwtab = fr->nblists[0].table_vdw.data;
 +
 +            /* Round the cut-offs to exact table values for precision */
 +            ri0  = floor(fr->rvdw_switch*scale);
 +            ri1  = ceil(fr->rvdw*scale);
 +            r0   = ri0/scale;
 +            r1   = ri1/scale;
 +            rc3  = r0*r0*r0;
 +            rc9  = rc3*rc3*rc3;
 +
 +            if (fr->vdwtype == evdwSHIFT)
 +            {
 +                /* Determine the constant energy shift below rvdw_switch.
 +                 * Table has a scale factor since we have scaled it down to compensate
 +                 * for scaling-up c6/c12 with the derivative factors to save flops in analytical kernels.
 +                 */
 +                fr->enershiftsix    = (real)(-1.0/(rc3*rc3)) - 6.0*vdwtab[8*ri0];
 +                fr->enershifttwelve = (real)( 1.0/(rc9*rc3)) - 12.0*vdwtab[8*ri0 + 4];
 +            }
 +            /* Add the constant part from 0 to rvdw_switch.
 +             * This integration from 0 to rvdw_switch overcounts the number
 +             * of interactions by 1, as it also counts the self interaction.
 +             * We will correct for this later.
 +             */
 +            eners[0] += 4.0*M_PI*fr->enershiftsix*rc3/3.0;
 +            eners[1] += 4.0*M_PI*fr->enershifttwelve*rc3/3.0;
 +
 +            invscale  = 1.0/(scale);
 +            invscale2 = invscale*invscale;
 +            invscale3 = invscale*invscale2;
 +
 +            /* following summation derived from cubic spline definition,
 +               Numerical Recipies in C, second edition, p. 113-116.  Exact
 +               for the cubic spline.  We first calculate the negative of
 +               the energy from rvdw to rvdw_switch, assuming that g(r)=1,
 +               and then add the more standard, abrupt cutoff correction to
 +               that result, yielding the long-range correction for a
 +               switched function.  We perform both the pressure and energy
 +               loops at the same time for simplicity, as the computational
 +               cost is low. */
 +
 +            for (i = 0; i < 2; i++)
 +            {
 +                enersum = 0.0; virsum = 0.0;
 +                if (i == 0)
 +                {
 +                    offstart = 0;
 +                    /* Since the dispersion table has been scaled down a factor 6.0 and the repulsion
 +                     * a factor 12.0 to compensate for the c6/c12 parameters inside nbfp[] being scaled
 +                     * up (to save flops in kernels), we need to correct for this.
 +                     */
 +                    tabfactor = 6.0;
 +                }
 +                else
 +                {
 +                    offstart  = 4;
 +                    tabfactor = 12.0;
 +                }
 +                for (ri = ri0; ri < ri1; ri++)
 +                {
 +                    r  = ri*invscale;
 +                    ea = invscale3;
 +                    eb = 2.0*invscale2*r;
 +                    ec = invscale*r*r;
 +
 +                    pa = invscale3;
 +                    pb = 3.0*invscale2*r;
 +                    pc = 3.0*invscale*r*r;
 +                    pd = r*r*r;
 +
 +                    /* this "8" is from the packing in the vdwtab array - perhaps should be #define'ed? */
 +                    offset = 8*ri + offstart;
 +                    y0     = vdwtab[offset];
 +                    f      = vdwtab[offset+1];
 +                    g      = vdwtab[offset+2];
 +                    h      = vdwtab[offset+3];
 +
 +                    enersum += y0*(ea/3 + eb/2 + ec) + f*(ea/4 + eb/3 + ec/2) + g*(ea/5 + eb/4 + ec/3) + h*(ea/6 + eb/5 + ec/4);
 +                    virsum  += f*(pa/4 + pb/3 + pc/2 + pd) + 2*g*(pa/5 + pb/4 + pc/3 + pd/2) + 3*h*(pa/6 + pb/5 + pc/4 + pd/3);
 +                }
 +
 +                enersum  *= 4.0*M_PI*tabfactor;
 +                virsum   *= 4.0*M_PI*tabfactor;
 +                eners[i] -= enersum;
 +                virs[i]  -= virsum;
 +            }
 +
 +            /* now add the correction for rvdw_switch to infinity */
 +            eners[0] += -4.0*M_PI/(3.0*rc3);
 +            eners[1] +=  4.0*M_PI/(9.0*rc9);
 +            virs[0]  +=  8.0*M_PI/rc3;
 +            virs[1]  += -16.0*M_PI/(3.0*rc9);
 +        }
 +        else if ((fr->vdwtype == evdwCUT) || (fr->vdwtype == evdwUSER))
 +        {
 +            if (fr->vdwtype == evdwUSER && fplog)
 +            {
 +                fprintf(fplog,
 +                        "WARNING: using dispersion correction with user tables\n");
 +            }
 +            rc3  = fr->rvdw*fr->rvdw*fr->rvdw;
 +            rc9  = rc3*rc3*rc3;
 +            /* Contribution beyond the cut-off */
 +            eners[0] += -4.0*M_PI/(3.0*rc3);
 +            eners[1] +=  4.0*M_PI/(9.0*rc9);
 +            if (fr->vdw_modifier == eintmodPOTSHIFT)
 +            {
 +                /* Contribution within the cut-off */
 +                eners[0] += -4.0*M_PI/(3.0*rc3);
 +                eners[1] +=  4.0*M_PI/(3.0*rc9);
 +            }
 +            /* Contribution beyond the cut-off */
 +            virs[0]  +=  8.0*M_PI/rc3;
 +            virs[1]  += -16.0*M_PI/(3.0*rc9);
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS,
 +                      "Dispersion correction is not implemented for vdw-type = %s",
 +                      evdw_names[fr->vdwtype]);
 +        }
 +        fr->enerdiffsix    = eners[0];
 +        fr->enerdifftwelve = eners[1];
 +        /* The 0.5 is due to the Gromacs definition of the virial */
 +        fr->virdiffsix     = 0.5*virs[0];
 +        fr->virdifftwelve  = 0.5*virs[1];
 +    }
 +}
 +
 +void calc_dispcorr(FILE *fplog, t_inputrec *ir, t_forcerec *fr,
 +                   gmx_large_int_t step, int natoms,
 +                   matrix box, real lambda, tensor pres, tensor virial,
 +                   real *prescorr, real *enercorr, real *dvdlcorr)
 +{
 +    gmx_bool bCorrAll, bCorrPres;
 +    real     dvdlambda, invvol, dens, ninter, avcsix, avctwelve, enerdiff, svir = 0, spres = 0;
 +    int      m;
 +
 +    *prescorr = 0;
 +    *enercorr = 0;
 +    *dvdlcorr = 0;
 +
 +    clear_mat(virial);
 +    clear_mat(pres);
 +
 +    if (ir->eDispCorr != edispcNO)
 +    {
 +        bCorrAll  = (ir->eDispCorr == edispcAllEner ||
 +                     ir->eDispCorr == edispcAllEnerPres);
 +        bCorrPres = (ir->eDispCorr == edispcEnerPres ||
 +                     ir->eDispCorr == edispcAllEnerPres);
 +
 +        invvol = 1/det(box);
 +        if (fr->n_tpi)
 +        {
 +            /* Only correct for the interactions with the inserted molecule */
 +            dens   = (natoms - fr->n_tpi)*invvol;
 +            ninter = fr->n_tpi;
 +        }
 +        else
 +        {
 +            dens   = natoms*invvol;
 +            ninter = 0.5*natoms;
 +        }
 +
 +        if (ir->efep == efepNO)
 +        {
 +            avcsix    = fr->avcsix[0];
 +            avctwelve = fr->avctwelve[0];
 +        }
 +        else
 +        {
 +            avcsix    = (1 - lambda)*fr->avcsix[0]    + lambda*fr->avcsix[1];
 +            avctwelve = (1 - lambda)*fr->avctwelve[0] + lambda*fr->avctwelve[1];
 +        }
 +
 +        enerdiff   = ninter*(dens*fr->enerdiffsix - fr->enershiftsix);
 +        *enercorr += avcsix*enerdiff;
 +        dvdlambda  = 0.0;
 +        if (ir->efep != efepNO)
 +        {
 +            dvdlambda += (fr->avcsix[1] - fr->avcsix[0])*enerdiff;
 +        }
 +        if (bCorrAll)
 +        {
 +            enerdiff   = ninter*(dens*fr->enerdifftwelve - fr->enershifttwelve);
 +            *enercorr += avctwelve*enerdiff;
 +            if (fr->efep != efepNO)
 +            {
 +                dvdlambda += (fr->avctwelve[1] - fr->avctwelve[0])*enerdiff;
 +            }
 +        }
 +
 +        if (bCorrPres)
 +        {
 +            svir = ninter*dens*avcsix*fr->virdiffsix/3.0;
 +            if (ir->eDispCorr == edispcAllEnerPres)
 +            {
 +                svir += ninter*dens*avctwelve*fr->virdifftwelve/3.0;
 +            }
 +            /* The factor 2 is because of the Gromacs virial definition */
 +            spres = -2.0*invvol*svir*PRESFAC;
 +
 +            for (m = 0; m < DIM; m++)
 +            {
 +                virial[m][m] += svir;
 +                pres[m][m]   += spres;
 +            }
 +            *prescorr += spres;
 +        }
 +
 +        /* Can't currently control when it prints, for now, just print when degugging */
 +        if (debug)
 +        {
 +            if (bCorrAll)
 +            {
 +                fprintf(debug, "Long Range LJ corr.: <C6> %10.4e, <C12> %10.4e\n",
 +                        avcsix, avctwelve);
 +            }
 +            if (bCorrPres)
 +            {
 +                fprintf(debug,
 +                        "Long Range LJ corr.: Epot %10g, Pres: %10g, Vir: %10g\n",
 +                        *enercorr, spres, svir);
 +            }
 +            else
 +            {
 +                fprintf(debug, "Long Range LJ corr.: Epot %10g\n", *enercorr);
 +            }
 +        }
 +
 +        if (fr->bSepDVDL && do_per_step(step, ir->nstlog))
 +        {
 +            fprintf(fplog, sepdvdlformat, "Dispersion correction",
 +                    *enercorr, dvdlambda);
 +        }
 +        if (fr->efep != efepNO)
 +        {
 +            *dvdlcorr += dvdlambda;
 +        }
 +    }
 +}
 +
 +void do_pbc_first(FILE *fplog, matrix box, t_forcerec *fr,
 +                  t_graph *graph, rvec x[])
 +{
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Removing pbc first time\n");
 +    }
 +    calc_shifts(box, fr->shift_vec);
 +    if (graph)
 +    {
 +        mk_mshift(fplog, graph, fr->ePBC, box, x);
 +        if (gmx_debug_at)
 +        {
 +            p_graph(debug, "do_pbc_first 1", graph);
 +        }
 +        shift_self(graph, box, x);
 +        /* By doing an extra mk_mshift the molecules that are broken
 +         * because they were e.g. imported from another software
 +         * will be made whole again. Such are the healing powers
 +         * of GROMACS.
 +         */
 +        mk_mshift(fplog, graph, fr->ePBC, box, x);
 +        if (gmx_debug_at)
 +        {
 +            p_graph(debug, "do_pbc_first 2", graph);
 +        }
 +    }
 +    if (fplog)
 +    {
 +        fprintf(fplog, "Done rmpbc\n");
 +    }
 +}
 +
 +static void low_do_pbc_mtop(FILE *fplog, int ePBC, matrix box,
 +                            gmx_mtop_t *mtop, rvec x[],
 +                            gmx_bool bFirst)
 +{
 +    t_graph        *graph;
 +    int             mb, as, mol;
 +    gmx_molblock_t *molb;
 +
 +    if (bFirst && fplog)
 +    {
 +        fprintf(fplog, "Removing pbc first time\n");
 +    }
 +
 +    snew(graph, 1);
 +    as = 0;
 +    for (mb = 0; mb < mtop->nmolblock; mb++)
 +    {
 +        molb = &mtop->molblock[mb];
 +        if (molb->natoms_mol == 1 ||
 +            (!bFirst && mtop->moltype[molb->type].cgs.nr == 1))
 +        {
 +            /* Just one atom or charge group in the molecule, no PBC required */
 +            as += molb->nmol*molb->natoms_mol;
 +        }
 +        else
 +        {
 +            /* Pass NULL iso fplog to avoid graph prints for each molecule type */
 +            mk_graph_ilist(NULL, mtop->moltype[molb->type].ilist,
 +                           0, molb->natoms_mol, FALSE, FALSE, graph);
 +
 +            for (mol = 0; mol < molb->nmol; mol++)
 +            {
 +                mk_mshift(fplog, graph, ePBC, box, x+as);
 +
 +                shift_self(graph, box, x+as);
 +                /* The molecule is whole now.
 +                 * We don't need the second mk_mshift call as in do_pbc_first,
 +                 * since we no longer need this graph.
 +                 */
 +
 +                as += molb->natoms_mol;
 +            }
 +            done_graph(graph);
 +        }
 +    }
 +    sfree(graph);
 +}
 +
 +void do_pbc_first_mtop(FILE *fplog, int ePBC, matrix box,
 +                       gmx_mtop_t *mtop, rvec x[])
 +{
 +    low_do_pbc_mtop(fplog, ePBC, box, mtop, x, TRUE);
 +}
 +
 +void do_pbc_mtop(FILE *fplog, int ePBC, matrix box,
 +                 gmx_mtop_t *mtop, rvec x[])
 +{
 +    low_do_pbc_mtop(fplog, ePBC, box, mtop, x, FALSE);
 +}
 +
 +void finish_run(FILE *fplog, t_commrec *cr, const char *confout,
 +                t_inputrec *inputrec,
 +                t_nrnb nrnb[], gmx_wallcycle_t wcycle,
 +                gmx_runtime_t *runtime,
 +                wallclock_gpu_t *gputimes,
 +                int omp_nth_pp,
 +                gmx_bool bWriteStat)
 +{
 +    int     i, j;
 +    t_nrnb *nrnb_tot = NULL;
 +    real    delta_t;
 +    double  nbfs, mflop;
 +
 +    wallcycle_sum(cr, wcycle);
 +
 +    if (cr->nnodes > 1)
 +    {
 +        snew(nrnb_tot, 1);
 +#ifdef GMX_MPI
 +        MPI_Allreduce(nrnb->n, nrnb_tot->n, eNRNB, MPI_DOUBLE, MPI_SUM,
 +                      cr->mpi_comm_mysim);
 +#endif
 +    }
 +    else
 +    {
 +        nrnb_tot = nrnb;
 +    }
 +
 +#if defined(GMX_MPI) && !defined(GMX_THREAD_MPI)
 +    if (cr->nnodes > 1)
 +    {
 +        /* reduce nodetime over all MPI processes in the current simulation */
 +        double sum;
 +        MPI_Allreduce(&runtime->proctime, &sum, 1, MPI_DOUBLE, MPI_SUM,
 +                      cr->mpi_comm_mysim);
 +        runtime->proctime = sum;
 +    }
 +#endif
 +
 +    if (SIMMASTER(cr))
 +    {
 +        print_flop(fplog, nrnb_tot, &nbfs, &mflop);
 +    }
 +    if (cr->nnodes > 1)
 +    {
 +        sfree(nrnb_tot);
 +    }
 +
 +    if ((cr->duty & DUTY_PP) && DOMAINDECOMP(cr))
 +    {
 +        print_dd_statistics(cr, inputrec, fplog);
 +    }
 +
 +#ifdef GMX_MPI
 +    if (PARTDECOMP(cr))
 +    {
 +        if (MASTER(cr))
 +        {
 +            t_nrnb     *nrnb_all;
 +            int         s;
 +            MPI_Status  stat;
 +
 +            snew(nrnb_all, cr->nnodes);
 +            nrnb_all[0] = *nrnb;
 +            for (s = 1; s < cr->nnodes; s++)
 +            {
 +                MPI_Recv(nrnb_all[s].n, eNRNB, MPI_DOUBLE, s, 0,
 +                         cr->mpi_comm_mysim, &stat);
 +            }
 +            pr_load(fplog, cr, nrnb_all);
 +            sfree(nrnb_all);
 +        }
 +        else
 +        {
 +            MPI_Send(nrnb->n, eNRNB, MPI_DOUBLE, MASTERRANK(cr), 0,
 +                     cr->mpi_comm_mysim);
 +        }
 +    }
 +#endif
 +
 +    if (SIMMASTER(cr))
 +    {
 +        wallcycle_print(fplog, cr->nnodes, cr->npmenodes, runtime->realtime,
 +                        wcycle, gputimes);
 +
 +        if (EI_DYNAMICS(inputrec->eI))
 +        {
 +            delta_t = inputrec->delta_t;
 +        }
 +        else
 +        {
 +            delta_t = 0;
 +        }
 +
 +        if (fplog)
 +        {
 +            print_perf(fplog, runtime->proctime, runtime->realtime,
 +                       cr->nnodes-cr->npmenodes,
 +                       runtime->nsteps_done, delta_t, nbfs, mflop,
 +                       omp_nth_pp);
 +        }
 +        if (bWriteStat)
 +        {
 +            print_perf(stderr, runtime->proctime, runtime->realtime,
 +                       cr->nnodes-cr->npmenodes,
 +                       runtime->nsteps_done, delta_t, nbfs, mflop,
 +                       omp_nth_pp);
 +        }
 +    }
 +}
 +
 +extern void initialize_lambdas(FILE *fplog, t_inputrec *ir, int *fep_state, real *lambda, double *lam0)
 +{
 +    /* this function works, but could probably use a logic rewrite to keep all the different
 +       types of efep straight. */
 +
 +    int       i;
 +    t_lambda *fep = ir->fepvals;
 +
 +    if ((ir->efep == efepNO) && (ir->bSimTemp == FALSE))
 +    {
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            lambda[i] = 0.0;
 +            if (lam0)
 +            {
 +                lam0[i] = 0.0;
 +            }
 +        }
 +        return;
 +    }
 +    else
 +    {
 +        *fep_state = fep->init_fep_state; /* this might overwrite the checkpoint
 +                                             if checkpoint is set -- a kludge is in for now
 +                                             to prevent this.*/
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            /* overwrite lambda state with init_lambda for now for backwards compatibility */
 +            if (fep->init_lambda >= 0) /* if it's -1, it was never initializd */
 +            {
 +                lambda[i] = fep->init_lambda;
 +                if (lam0)
 +                {
 +                    lam0[i] = lambda[i];
 +                }
 +            }
 +            else
 +            {
 +                lambda[i] = fep->all_lambda[i][*fep_state];
 +                if (lam0)
 +                {
 +                    lam0[i] = lambda[i];
 +                }
 +            }
 +        }
 +        if (ir->bSimTemp)
 +        {
 +            /* need to rescale control temperatures to match current state */
 +            for (i = 0; i < ir->opts.ngtc; i++)
 +            {
 +                if (ir->opts.ref_t[i] > 0)
 +                {
 +                    ir->opts.ref_t[i] = ir->simtempvals->temperatures[*fep_state];
 +                }
 +            }
 +        }
 +    }
 +
 +    /* Send to the log the information on the current lambdas */
 +    if (fplog != NULL)
 +    {
 +        fprintf(fplog, "Initial vector of lambda components:[ ");
 +        for (i = 0; i < efptNR; i++)
 +        {
 +            fprintf(fplog, "%10.4f ", lambda[i]);
 +        }
 +        fprintf(fplog, "]\n");
 +    }
 +    return;
 +}
 +
 +
 +void init_md(FILE *fplog,
 +             t_commrec *cr, t_inputrec *ir, const output_env_t oenv,
 +             double *t, double *t0,
 +             real *lambda, int *fep_state, double *lam0,
 +             t_nrnb *nrnb, gmx_mtop_t *mtop,
 +             gmx_update_t *upd,
 +             int nfile, const t_filenm fnm[],
 +             gmx_mdoutf_t **outf, t_mdebin **mdebin,
 +             tensor force_vir, tensor shake_vir, rvec mu_tot,
 +             gmx_bool *bSimAnn, t_vcm **vcm, t_state *state, unsigned long Flags)
 +{
 +    int  i, j, n;
 +    real tmpt, mod;
 +
 +    /* Initial values */
 +    *t = *t0       = ir->init_t;
 +
 +    *bSimAnn = FALSE;
 +    for (i = 0; i < ir->opts.ngtc; i++)
 +    {
 +        /* set bSimAnn if any group is being annealed */
 +        if (ir->opts.annealing[i] != eannNO)
 +        {
 +            *bSimAnn = TRUE;
 +        }
 +    }
 +    if (*bSimAnn)
 +    {
 +        update_annealing_target_temp(&(ir->opts), ir->init_t);
 +    }
 +
 +    /* Initialize lambda variables */
 +    initialize_lambdas(fplog, ir, fep_state, lambda, lam0);
 +
 +    if (upd)
 +    {
 +        *upd = init_update(fplog, ir);
 +    }
 +
 +
 +    if (vcm != NULL)
 +    {
 +        *vcm = init_vcm(fplog, &mtop->groups, ir);
 +    }
 +
 +    if (EI_DYNAMICS(ir->eI) && !(Flags & MD_APPENDFILES))
 +    {
 +        if (ir->etc == etcBERENDSEN)
 +        {
 +            please_cite(fplog, "Berendsen84a");
 +        }
 +        if (ir->etc == etcVRESCALE)
 +        {
 +            please_cite(fplog, "Bussi2007a");
 +        }
 +    }
 +
 +    init_nrnb(nrnb);
 +
 +    if (nfile != -1)
 +    {
 +        *outf = init_mdoutf(nfile, fnm, Flags, cr, ir, oenv);
 +
 +        *mdebin = init_mdebin((Flags & MD_APPENDFILES) ? NULL : (*outf)->fp_ene,
 +                              mtop, ir, (*outf)->fp_dhdl);
 +    }
 +
 +    if (ir->bAdress)
 +    {
 +        please_cite(fplog, "Fritsch12");
 +        please_cite(fplog, "Junghans10");
 +    }
 +    /* Initiate variables */
 +    clear_mat(force_vir);
 +    clear_mat(shake_vir);
 +    clear_rvec(mu_tot);
 +
 +    debug_gmx();
 +}
index c07d86201a9c9ec36309bf6d2d812d73423006e3,0000000000000000000000000000000000000000..deffc3b437f1515608719f8a3736a34a27e5e4dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,311 -1,0 +1,326 @@@
-         /* Allocate 2 elements extra on both sides,
-          * so in single precision we have 2*3*3*4=72 bytes buffer
-          * on both sides to avoid cache pollution.
 +/* -*- 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
 + */
 +/* This file is completely threadsafe - keep it that way! */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +
 +#include <math.h>
 +#include "macros.h"
 +#include "main.h"
 +#include "smalloc.h"
 +#include "futil.h"
 +#include "tgroup.h"
 +#include "vec.h"
 +#include "network.h"
 +#include "smalloc.h"
 +#include "update.h"
 +#include "rbin.h"
 +#include "mtop_util.h"
 +#include "gmx_omp_nthreads.h"
 +
 +static void init_grptcstat(int ngtc, t_grp_tcstat tcstat[])
 +{
 +    int i, j;
 +
 +    for (i = 0; (i < ngtc); i++)
 +    {
 +        tcstat[i].T = 0;
 +        clear_mat(tcstat[i].ekinh);
 +        clear_mat(tcstat[i].ekinh_old);
 +        clear_mat(tcstat[i].ekinf);
 +    }
 +}
 +
 +static void init_grpstat(FILE *log,
 +                         gmx_mtop_t *mtop, int ngacc, t_grp_acc gstat[])
 +{
 +    gmx_groups_t           *groups;
 +    gmx_mtop_atomloop_all_t aloop;
 +    int                     i, grp;
 +    t_atom                 *atom;
 +
 +    if (ngacc > 0)
 +    {
 +        groups = &mtop->groups;
 +        aloop  = gmx_mtop_atomloop_all_init(mtop);
 +        while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
 +        {
 +            grp = ggrpnr(groups, egcACC, i);
 +            if ((grp < 0) && (grp >= ngacc))
 +            {
 +                gmx_incons("Input for acceleration groups wrong");
 +            }
 +            gstat[grp].nat++;
 +            /* This will not work for integrator BD */
 +            gstat[grp].mA += atom->m;
 +            gstat[grp].mB += atom->mB;
 +        }
 +    }
 +}
 +
 +void init_ekindata(FILE *log, gmx_mtop_t *mtop, t_grpopts *opts,
 +                   gmx_ekindata_t *ekind)
 +{
 +    int i;
 +    int nthread, thread;
 +#ifdef DEBUG
 +    fprintf(log, "ngtc: %d, ngacc: %d, ngener: %d\n", opts->ngtc, opts->ngacc,
 +            opts->ngener);
 +#endif
 +
 +    /* bNEMD tells if we should remove remove the COM velocity
 +     * from the velocities during velocity scaling in T-coupling.
 +     * Turn this on when we have multiple acceleration groups
 +     * or one accelerated group.
 +     */
 +    ekind->bNEMD = (opts->ngacc > 1 || norm(opts->acc[0]) > 0);
 +
 +    ekind->ngtc = opts->ngtc;
 +    snew(ekind->tcstat, opts->ngtc);
 +    init_grptcstat(opts->ngtc, ekind->tcstat);
 +    /* Set Berendsen tcoupl lambda's to 1,
 +     * so runs without Berendsen coupling are not affected.
 +     */
 +    for (i = 0; i < opts->ngtc; i++)
 +    {
 +        ekind->tcstat[i].lambda         = 1.0;
 +        ekind->tcstat[i].vscale_nhc     = 1.0;
 +        ekind->tcstat[i].ekinscaleh_nhc = 1.0;
 +        ekind->tcstat[i].ekinscalef_nhc = 1.0;
 +    }
 +
 +    nthread = gmx_omp_nthreads_get(emntUpdate);
 +
 +    snew(ekind->ekin_work_alloc, nthread);
 +    snew(ekind->ekin_work, nthread);
++    snew(ekind->dekindl_work, nthread);
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +    for (thread = 0; thread < nthread; thread++)
 +    {
-         snew(ekind->ekin_work_alloc[thread], ekind->ngtc+4);
-         ekind->ekin_work[thread] = ekind->ekin_work_alloc[thread] + 2;
++#define EKIN_WORK_BUFFER_SIZE 2
++        /* Allocate 2 extra elements on both sides, so in single
++         * precision we have
++         * EKIN_WORK_BUFFER_SIZE*DIM*DIM*sizeof(real) = 72/144 bytes
++         * buffer on both sides to avoid cache pollution.
 +         */
++        snew(ekind->ekin_work_alloc[thread], ekind->ngtc+2*EKIN_WORK_BUFFER_SIZE);
++        ekind->ekin_work[thread] = ekind->ekin_work_alloc[thread] + EKIN_WORK_BUFFER_SIZE;
++        /* Nasty hack so we can have the per-thread accumulation
++         * variable for dekindl in the same thread-local cache lines
++         * as the per-thread accumulation tensors for ekin[fh],
++         * because they are accumulated in the same loop. */
++        ekind->dekindl_work[thread] = &(ekind->ekin_work[thread][ekind->ngtc][0][0]);
++#undef EKIN_WORK_BUFFER_SIZE
 +    }
 +
 +    ekind->ngacc = opts->ngacc;
 +    snew(ekind->grpstat, opts->ngacc);
 +    init_grpstat(log, mtop, opts->ngacc, ekind->grpstat);
 +}
 +
 +void accumulate_u(t_commrec *cr, t_grpopts *opts, gmx_ekindata_t *ekind)
 +{
 +    /* This routine will only be called when it's necessary */
 +    t_bin *rb;
 +    int    g;
 +
 +    rb = mk_bin();
 +
 +    for (g = 0; (g < opts->ngacc); g++)
 +    {
 +        add_binr(rb, DIM, ekind->grpstat[g].u);
 +    }
 +    sum_bin(rb, cr);
 +
 +    for (g = 0; (g < opts->ngacc); g++)
 +    {
 +        extract_binr(rb, DIM*g, DIM, ekind->grpstat[g].u);
 +    }
 +    destroy_bin(rb);
 +}
 +
 +/* I don't think accumulate_ekin is used anymore? */
 +
 +#if 0
 +static void accumulate_ekin(t_commrec *cr, t_grpopts *opts,
 +                            gmx_ekindata_t *ekind)
 +{
 +    int g;
 +
 +    if (PAR(cr))
 +    {
 +        for (g = 0; (g < opts->ngtc); g++)
 +        {
 +            gmx_sum(DIM*DIM, ekind->tcstat[g].ekinf[0], cr);
 +        }
 +    }
 +}
 +#endif
 +
 +void update_ekindata(int start, int homenr, gmx_ekindata_t *ekind,
 +                     t_grpopts *opts, rvec v[], t_mdatoms *md, real lambda)
 +{
 +    int  d, g, n;
 +    real mv;
 +
 +    /* calculate mean velocities at whole timestep */
 +    for (g = 0; (g < opts->ngtc); g++)
 +    {
 +        ekind->tcstat[g].T = 0;
 +    }
 +
 +    if (ekind->bNEMD)
 +    {
 +        for (g = 0; (g < opts->ngacc); g++)
 +        {
 +            clear_rvec(ekind->grpstat[g].u);
 +        }
 +
 +        g = 0;
 +        for (n = start; (n < start+homenr); n++)
 +        {
 +            if (md->cACC)
 +            {
 +                g = md->cACC[n];
 +            }
 +            for (d = 0; (d < DIM); d++)
 +            {
 +                mv                      = md->massT[n]*v[n][d];
 +                ekind->grpstat[g].u[d] += mv;
 +            }
 +        }
 +
 +        for (g = 0; (g < opts->ngacc); g++)
 +        {
 +            for (d = 0; (d < DIM); d++)
 +            {
 +                ekind->grpstat[g].u[d] /=
 +                    (1-lambda)*ekind->grpstat[g].mA + lambda*ekind->grpstat[g].mB;
 +            }
 +        }
 +    }
 +}
 +
 +real sum_ekin(t_grpopts *opts, gmx_ekindata_t *ekind, real *dekindlambda,
 +              gmx_bool bEkinAveVel, gmx_bool bSaveEkinOld, gmx_bool bScaleEkin)
 +{
 +    int           i, j, m, ngtc;
 +    real          T, ek;
 +    t_grp_tcstat *tcstat;
 +    real          nrdf, nd, *ndf;
 +
 +    ngtc = opts->ngtc;
 +    ndf  = opts->nrdf;
 +
 +    T    = 0;
 +    nrdf = 0;
 +
 +    clear_mat(ekind->ekin);
 +
 +    for (i = 0; (i < ngtc); i++)
 +    {
 +
 +        nd     = ndf[i];
 +        tcstat = &ekind->tcstat[i];
 +        /* Sometimes a group does not have degrees of freedom, e.g.
 +         * when it consists of shells and virtual sites, then we just
 +         * set the temperatue to 0 and also neglect the kinetic
 +         * energy, which should be  zero anyway.
 +         */
 +
 +        if (nd > 0)
 +        {
 +            if (bEkinAveVel)
 +            {
 +                if (!bScaleEkin)
 +                {
 +                    /* in this case, kinetic energy is from the current velocities already */
 +                    msmul(tcstat->ekinf, tcstat->ekinscalef_nhc, tcstat->ekinf);
 +                }
 +            }
 +            else
-         *dekindlambda = 0.5*(ekind->dekindl + ekind->dekindl_old);
 +            {
 +                /* Calculate the full step Ekin as the average of the half steps */
 +                for (j = 0; (j < DIM); j++)
 +                {
 +                    for (m = 0; (m < DIM); m++)
 +                    {
 +                        tcstat->ekinf[j][m] =
 +                            0.5*(tcstat->ekinh[j][m]*tcstat->ekinscaleh_nhc + tcstat->ekinh_old[j][m]);
 +                    }
 +                }
 +            }
 +            m_add(tcstat->ekinf, ekind->ekin, ekind->ekin);
 +
 +            tcstat->Th = calc_temp(trace(tcstat->ekinh), nd);
 +            tcstat->T  = calc_temp(trace(tcstat->ekinf), nd);
 +
 +            /* after the scaling factors have been multiplied in, we can remove them */
 +            if (bEkinAveVel)
 +            {
 +                tcstat->ekinscalef_nhc = 1.0;
 +            }
 +            else
 +            {
 +                tcstat->ekinscaleh_nhc = 1.0;
 +            }
 +        }
 +        else
 +        {
 +            tcstat->T  = 0;
 +            tcstat->Th = 0;
 +        }
 +        T    += nd*tcstat->T;
 +        nrdf += nd;
 +    }
 +    if (nrdf > 0)
 +    {
 +        T /= nrdf;
 +    }
 +    if (dekindlambda)
 +    {
++        if (bEkinAveVel)
++        {
++            *dekindlambda = ekind->dekindl;
++        }
++        else
++        {
++            *dekindlambda = 0.5*(ekind->dekindl + ekind->dekindl_old);
++        }
 +    }
 +    return T;
 +}
index 119e8420f096b04f7f76a54364b34776662cba1d,0000000000000000000000000000000000000000..ab6fba50dc20a3df1c4d6c5c51d415e8842a550b
mode 100644,000000..100644
--- /dev/null
@@@ -1,2083 -1,0 +1,2084 @@@
-         dekindl_sum = &ekind->ekin_work[thread][opts->ngtc][0][0];
 +/* -*- 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 <math.h>
 +
 +#include "types/commrec.h"
 +#include "sysstuff.h"
 +#include "smalloc.h"
 +#include "typedefs.h"
 +#include "nrnb.h"
 +#include "physics.h"
 +#include "macros.h"
 +#include "vec.h"
 +#include "main.h"
 +#include "confio.h"
 +#include "update.h"
 +#include "gmx_random.h"
 +#include "futil.h"
 +#include "mshift.h"
 +#include "tgroup.h"
 +#include "force.h"
 +#include "names.h"
 +#include "txtdump.h"
 +#include "mdrun.h"
 +#include "copyrite.h"
 +#include "constr.h"
 +#include "edsam.h"
 +#include "pull.h"
 +#include "disre.h"
 +#include "orires.h"
 +#include "gmx_wallcycle.h"
 +#include "gmx_omp_nthreads.h"
 +#include "gmx_omp.h"
 +
 +/*For debugging, start at v(-dt/2) for velolcity verlet -- uncomment next line */
 +/*#define STARTFROMDT2*/
 +
 +typedef struct {
 +    double gdt;
 +    double eph;
 +    double emh;
 +    double em;
 +    double b;
 +    double c;
 +    double d;
 +} gmx_sd_const_t;
 +
 +typedef struct {
 +    real V;
 +    real X;
 +    real Yv;
 +    real Yx;
 +} gmx_sd_sigma_t;
 +
 +typedef struct {
 +    /* The random state for ngaussrand threads.
 +     * Normal thermostats need just 1 random number generator,
 +     * but SD and BD with OpenMP parallelization need 1 for each thread.
 +     */
 +    int             ngaussrand;
 +    gmx_rng_t      *gaussrand;
 +    /* BD stuff */
 +    real           *bd_rf;
 +    /* SD stuff */
 +    gmx_sd_const_t *sdc;
 +    gmx_sd_sigma_t *sdsig;
 +    rvec           *sd_V;
 +    int             sd_V_nalloc;
 +    /* andersen temperature control stuff */
 +    gmx_bool       *randomize_group;
 +    real           *boltzfac;
 +} gmx_stochd_t;
 +
 +typedef struct gmx_update
 +{
 +    gmx_stochd_t *sd;
 +    /* xprime for constraint algorithms */
 +    rvec         *xp;
 +    int           xp_nalloc;
 +
 +    /* variable size arrays for andersen */
 +    gmx_bool *randatom;
 +    int      *randatom_list;
 +    gmx_bool  randatom_list_init;
 +
 +    /* Variables for the deform algorithm */
 +    gmx_large_int_t deformref_step;
 +    matrix          deformref_box;
 +} t_gmx_update;
 +
 +
 +static void do_update_md(int start, int nrend, double dt,
 +                         t_grp_tcstat *tcstat,
 +                         double nh_vxi[],
 +                         gmx_bool bNEMD, t_grp_acc *gstat, rvec accel[],
 +                         ivec nFreeze[],
 +                         real invmass[],
 +                         unsigned short ptype[], unsigned short cFREEZE[],
 +                         unsigned short cACC[], unsigned short cTC[],
 +                         rvec x[], rvec xprime[], rvec v[],
 +                         rvec f[], matrix M,
 +                         gmx_bool bNH, gmx_bool bPR)
 +{
 +    double imass, w_dt;
 +    int    gf = 0, ga = 0, gt = 0;
 +    rvec   vrel;
 +    real   vn, vv, va, vb, vnrel;
 +    real   lg, vxi = 0, u;
 +    int    n, d;
 +
 +    if (bNH || bPR)
 +    {
 +        /* Update with coupling to extended ensembles, used for
 +         * Nose-Hoover and Parrinello-Rahman coupling
 +         * Nose-Hoover uses the reversible leap-frog integrator from
 +         * Holian et al. Phys Rev E 52(3) : 2338, 1995
 +         */
 +        for (n = start; n < nrend; n++)
 +        {
 +            imass = invmass[n];
 +            if (cFREEZE)
 +            {
 +                gf   = cFREEZE[n];
 +            }
 +            if (cACC)
 +            {
 +                ga   = cACC[n];
 +            }
 +            if (cTC)
 +            {
 +                gt   = cTC[n];
 +            }
 +            lg   = tcstat[gt].lambda;
 +            if (bNH)
 +            {
 +                vxi   = nh_vxi[gt];
 +            }
 +            rvec_sub(v[n], gstat[ga].u, vrel);
 +
 +            for (d = 0; d < DIM; d++)
 +            {
 +                if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +                {
 +                    vnrel = (lg*vrel[d] + dt*(imass*f[n][d] - 0.5*vxi*vrel[d]
 +                                              - iprod(M[d], vrel)))/(1 + 0.5*vxi*dt);
 +                    /* do not scale the mean velocities u */
 +                    vn             = gstat[ga].u[d] + accel[ga][d]*dt + vnrel;
 +                    v[n][d]        = vn;
 +                    xprime[n][d]   = x[n][d]+vn*dt;
 +                }
 +                else
 +                {
 +                    v[n][d]        = 0.0;
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +    else if (cFREEZE != NULL ||
 +             nFreeze[0][XX] || nFreeze[0][YY] || nFreeze[0][ZZ] ||
 +             bNEMD)
 +    {
 +        /* Update with Berendsen/v-rescale coupling and freeze or NEMD */
 +        for (n = start; n < nrend; n++)
 +        {
 +            w_dt = invmass[n]*dt;
 +            if (cFREEZE)
 +            {
 +                gf   = cFREEZE[n];
 +            }
 +            if (cACC)
 +            {
 +                ga   = cACC[n];
 +            }
 +            if (cTC)
 +            {
 +                gt   = cTC[n];
 +            }
 +            lg   = tcstat[gt].lambda;
 +
 +            for (d = 0; d < DIM; d++)
 +            {
 +                vn             = v[n][d];
 +                if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +                {
 +                    vv             = lg*vn + f[n][d]*w_dt;
 +
 +                    /* do not scale the mean velocities u */
 +                    u              = gstat[ga].u[d];
 +                    va             = vv + accel[ga][d]*dt;
 +                    vb             = va + (1.0-lg)*u;
 +                    v[n][d]        = vb;
 +                    xprime[n][d]   = x[n][d]+vb*dt;
 +                }
 +                else
 +                {
 +                    v[n][d]        = 0.0;
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +    else
 +    {
 +        /* Plain update with Berendsen/v-rescale coupling */
 +        for (n = start; n < nrend; n++)
 +        {
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell))
 +            {
 +                w_dt = invmass[n]*dt;
 +                if (cTC)
 +                {
 +                    gt = cTC[n];
 +                }
 +                lg = tcstat[gt].lambda;
 +
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    vn           = lg*v[n][d] + f[n][d]*w_dt;
 +                    v[n][d]      = vn;
 +                    xprime[n][d] = x[n][d] + vn*dt;
 +                }
 +            }
 +            else
 +            {
 +                for (d = 0; d < DIM; d++)
 +                {
 +                    v[n][d]        = 0.0;
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static void do_update_vv_vel(int start, int nrend, double dt,
 +                             t_grp_tcstat *tcstat, t_grp_acc *gstat,
 +                             rvec accel[], ivec nFreeze[], real invmass[],
 +                             unsigned short ptype[], unsigned short cFREEZE[],
 +                             unsigned short cACC[], rvec v[], rvec f[],
 +                             gmx_bool bExtended, real veta, real alpha)
 +{
 +    double imass, w_dt;
 +    int    gf = 0, ga = 0;
 +    rvec   vrel;
 +    real   u, vn, vv, va, vb, vnrel;
 +    int    n, d;
 +    double g, mv1, mv2;
 +
 +    if (bExtended)
 +    {
 +        g        = 0.25*dt*veta*alpha;
 +        mv1      = exp(-g);
 +        mv2      = series_sinhx(g);
 +    }
 +    else
 +    {
 +        mv1      = 1.0;
 +        mv2      = 1.0;
 +    }
 +    for (n = start; n < nrend; n++)
 +    {
 +        w_dt = invmass[n]*dt;
 +        if (cFREEZE)
 +        {
 +            gf   = cFREEZE[n];
 +        }
 +        if (cACC)
 +        {
 +            ga   = cACC[n];
 +        }
 +
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +            {
 +                v[n][d]             = mv1*(mv1*v[n][d] + 0.5*(w_dt*mv2*f[n][d]))+0.5*accel[ga][d]*dt;
 +            }
 +            else
 +            {
 +                v[n][d]        = 0.0;
 +            }
 +        }
 +    }
 +} /* do_update_vv_vel */
 +
 +static void do_update_vv_pos(int start, int nrend, double dt,
 +                             t_grp_tcstat *tcstat, t_grp_acc *gstat,
 +                             rvec accel[], ivec nFreeze[], real invmass[],
 +                             unsigned short ptype[], unsigned short cFREEZE[],
 +                             rvec x[], rvec xprime[], rvec v[],
 +                             rvec f[], gmx_bool bExtended, real veta, real alpha)
 +{
 +    double imass, w_dt;
 +    int    gf = 0;
 +    int    n, d;
 +    double g, mr1, mr2;
 +
 +    /* Would it make more sense if Parrinello-Rahman was put here? */
 +    if (bExtended)
 +    {
 +        g        = 0.5*dt*veta;
 +        mr1      = exp(g);
 +        mr2      = series_sinhx(g);
 +    }
 +    else
 +    {
 +        mr1      = 1.0;
 +        mr2      = 1.0;
 +    }
 +
 +    for (n = start; n < nrend; n++)
 +    {
 +
 +        if (cFREEZE)
 +        {
 +            gf   = cFREEZE[n];
 +        }
 +
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +            {
 +                xprime[n][d]   = mr1*(mr1*x[n][d]+mr2*dt*v[n][d]);
 +            }
 +            else
 +            {
 +                xprime[n][d]   = x[n][d];
 +            }
 +        }
 +    }
 +} /* do_update_vv_pos */
 +
 +static void do_update_visc(int start, int nrend, double dt,
 +                           t_grp_tcstat *tcstat,
 +                           double nh_vxi[],
 +                           real invmass[],
 +                           unsigned short ptype[], unsigned short cTC[],
 +                           rvec x[], rvec xprime[], rvec v[],
 +                           rvec f[], matrix M, matrix box, real
 +                           cos_accel, real vcos,
 +                           gmx_bool bNH, gmx_bool bPR)
 +{
 +    double imass, w_dt;
 +    int    gt = 0;
 +    real   vn, vc;
 +    real   lg, vxi = 0, vv;
 +    real   fac, cosz;
 +    rvec   vrel;
 +    int    n, d;
 +
 +    fac = 2*M_PI/(box[ZZ][ZZ]);
 +
 +    if (bNH || bPR)
 +    {
 +        /* Update with coupling to extended ensembles, used for
 +         * Nose-Hoover and Parrinello-Rahman coupling
 +         */
 +        for (n = start; n < nrend; n++)
 +        {
 +            imass = invmass[n];
 +            if (cTC)
 +            {
 +                gt   = cTC[n];
 +            }
 +            lg   = tcstat[gt].lambda;
 +            cosz = cos(fac*x[n][ZZ]);
 +
 +            copy_rvec(v[n], vrel);
 +
 +            vc            = cosz*vcos;
 +            vrel[XX]     -= vc;
 +            if (bNH)
 +            {
 +                vxi        = nh_vxi[gt];
 +            }
 +            for (d = 0; d < DIM; d++)
 +            {
 +                vn             = v[n][d];
 +
 +                if ((ptype[n] != eptVSite) && (ptype[n] != eptShell))
 +                {
 +                    vn  = (lg*vrel[d] + dt*(imass*f[n][d] - 0.5*vxi*vrel[d]
 +                                            - iprod(M[d], vrel)))/(1 + 0.5*vxi*dt);
 +                    if (d == XX)
 +                    {
 +                        vn += vc + dt*cosz*cos_accel;
 +                    }
 +                    v[n][d]        = vn;
 +                    xprime[n][d]   = x[n][d]+vn*dt;
 +                }
 +                else
 +                {
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +    else
 +    {
 +        /* Classic version of update, used with berendsen coupling */
 +        for (n = start; n < nrend; n++)
 +        {
 +            w_dt = invmass[n]*dt;
 +            if (cTC)
 +            {
 +                gt   = cTC[n];
 +            }
 +            lg   = tcstat[gt].lambda;
 +            cosz = cos(fac*x[n][ZZ]);
 +
 +            for (d = 0; d < DIM; d++)
 +            {
 +                vn             = v[n][d];
 +
 +                if ((ptype[n] != eptVSite) && (ptype[n] != eptShell))
 +                {
 +                    if (d == XX)
 +                    {
 +                        vc           = cosz*vcos;
 +                        /* Do not scale the cosine velocity profile */
 +                        vv           = vc + lg*(vn - vc + f[n][d]*w_dt);
 +                        /* Add the cosine accelaration profile */
 +                        vv          += dt*cosz*cos_accel;
 +                    }
 +                    else
 +                    {
 +                        vv           = lg*(vn + f[n][d]*w_dt);
 +                    }
 +                    v[n][d]        = vv;
 +                    xprime[n][d]   = x[n][d]+vv*dt;
 +                }
 +                else
 +                {
 +                    v[n][d]        = 0.0;
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/* Allocates and initializes sd->gaussrand[i] for i=1, i<sd->ngaussrand,
 + * Using seeds generated from sd->gaussrand[0].
 + */
 +static void init_multiple_gaussrand(gmx_stochd_t *sd)
 +{
 +    int           ngr, i;
 +    unsigned int *seed;
 +
 +    ngr = sd->ngaussrand;
 +    snew(seed, ngr);
 +
 +    for (i = 1; i < ngr; i++)
 +    {
 +        seed[i] = gmx_rng_uniform_uint32(sd->gaussrand[0]);
 +    }
 +
 +#pragma omp parallel num_threads(ngr)
 +    {
 +        int th;
 +
 +        th = gmx_omp_get_thread_num();
 +        if (th > 0)
 +        {
 +            /* Initialize on each thread to have thread-local memory alloced */
 +            sd->gaussrand[th] = gmx_rng_init(seed[th]);
 +        }
 +    }
 +
 +    sfree(seed);
 +}
 +
 +static gmx_stochd_t *init_stochd(FILE *fplog, t_inputrec *ir, int nthreads)
 +{
 +    gmx_stochd_t   *sd;
 +    gmx_sd_const_t *sdc;
 +    int             ngtc, n, th;
 +    real            y;
 +
 +    snew(sd, 1);
 +
 +    /* Initiate random number generator for langevin type dynamics,
 +     * for BD, SD or velocity rescaling temperature coupling.
 +     */
 +    if (ir->eI == eiBD || EI_SD(ir->eI))
 +    {
 +        sd->ngaussrand = nthreads;
 +    }
 +    else
 +    {
 +        sd->ngaussrand = 1;
 +    }
 +    snew(sd->gaussrand, sd->ngaussrand);
 +
 +    /* Initialize the first random generator */
 +    sd->gaussrand[0] = gmx_rng_init(ir->ld_seed);
 +
 +    if (sd->ngaussrand > 1)
 +    {
 +        /* Initialize the rest of the random number generators,
 +         * using the first one to generate seeds.
 +         */
 +        init_multiple_gaussrand(sd);
 +    }
 +
 +    ngtc = ir->opts.ngtc;
 +
 +    if (ir->eI == eiBD)
 +    {
 +        snew(sd->bd_rf, ngtc);
 +    }
 +    else if (EI_SD(ir->eI))
 +    {
 +        snew(sd->sdc, ngtc);
 +        snew(sd->sdsig, ngtc);
 +
 +        sdc = sd->sdc;
 +        for (n = 0; n < ngtc; n++)
 +        {
 +            if (ir->opts.tau_t[n] > 0)
 +            {
 +                sdc[n].gdt = ir->delta_t/ir->opts.tau_t[n];
 +                sdc[n].eph = exp(sdc[n].gdt/2);
 +                sdc[n].emh = exp(-sdc[n].gdt/2);
 +                sdc[n].em  = exp(-sdc[n].gdt);
 +            }
 +            else
 +            {
 +                /* No friction and noise on this group */
 +                sdc[n].gdt = 0;
 +                sdc[n].eph = 1;
 +                sdc[n].emh = 1;
 +                sdc[n].em  = 1;
 +            }
 +            if (sdc[n].gdt >= 0.05)
 +            {
 +                sdc[n].b = sdc[n].gdt*(sdc[n].eph*sdc[n].eph - 1)
 +                    - 4*(sdc[n].eph - 1)*(sdc[n].eph - 1);
 +                sdc[n].c = sdc[n].gdt - 3 + 4*sdc[n].emh - sdc[n].em;
 +                sdc[n].d = 2 - sdc[n].eph - sdc[n].emh;
 +            }
 +            else
 +            {
 +                y = sdc[n].gdt/2;
 +                /* Seventh order expansions for small y */
 +                sdc[n].b = y*y*y*y*(1/3.0+y*(1/3.0+y*(17/90.0+y*7/9.0)));
 +                sdc[n].c = y*y*y*(2/3.0+y*(-1/2.0+y*(7/30.0+y*(-1/12.0+y*31/1260.0))));
 +                sdc[n].d = y*y*(-1+y*y*(-1/12.0-y*y/360.0));
 +            }
 +            if (debug)
 +            {
 +                fprintf(debug, "SD const tc-grp %d: b %g  c %g  d %g\n",
 +                        n, sdc[n].b, sdc[n].c, sdc[n].d);
 +            }
 +        }
 +    }
 +    else if (ETC_ANDERSEN(ir->etc))
 +    {
 +        int        ngtc;
 +        t_grpopts *opts;
 +        real       reft;
 +
 +        opts = &ir->opts;
 +        ngtc = opts->ngtc;
 +
 +        snew(sd->randomize_group, ngtc);
 +        snew(sd->boltzfac, ngtc);
 +
 +        /* for now, assume that all groups, if randomized, are randomized at the same rate, i.e. tau_t is the same. */
 +        /* since constraint groups don't necessarily match up with temperature groups! This is checked in readir.c */
 +
 +        for (n = 0; n < ngtc; n++)
 +        {
 +            reft = max(0.0, opts->ref_t[n]);
 +            if ((opts->tau_t[n] > 0) && (reft > 0))  /* tau_t or ref_t = 0 means that no randomization is done */
 +            {
 +                sd->randomize_group[n] = TRUE;
 +                sd->boltzfac[n]        = BOLTZ*opts->ref_t[n];
 +            }
 +            else
 +            {
 +                sd->randomize_group[n] = FALSE;
 +            }
 +        }
 +    }
 +    return sd;
 +}
 +
 +void get_stochd_state(gmx_update_t upd, t_state *state)
 +{
 +    /* Note that we only get the state of the first random generator,
 +     * even if there are multiple. This avoids repetition.
 +     */
 +    gmx_rng_get_state(upd->sd->gaussrand[0], state->ld_rng, state->ld_rngi);
 +}
 +
 +void set_stochd_state(gmx_update_t upd, t_state *state)
 +{
 +    gmx_stochd_t *sd;
 +    int           i;
 +
 +    sd = upd->sd;
 +
 +    gmx_rng_set_state(sd->gaussrand[0], state->ld_rng, state->ld_rngi[0]);
 +
 +    if (sd->ngaussrand > 1)
 +    {
 +        /* We only end up here with SD or BD with OpenMP.
 +         * Destroy and reinitialize the rest of the random number generators,
 +         * using seeds generated from the first one.
 +         * Although this doesn't recover the previous state,
 +         * it at least avoids repetition, which is most important.
 +         * Exaclty restoring states with all MPI+OpenMP setups is difficult
 +         * and as the integrator is random to start with, doesn't gain us much.
 +         */
 +        for (i = 1; i < sd->ngaussrand; i++)
 +        {
 +            gmx_rng_destroy(sd->gaussrand[i]);
 +        }
 +
 +        init_multiple_gaussrand(sd);
 +    }
 +}
 +
 +gmx_update_t init_update(FILE *fplog, t_inputrec *ir)
 +{
 +    t_gmx_update *upd;
 +
 +    snew(upd, 1);
 +
 +    if (ir->eI == eiBD || EI_SD(ir->eI) || ir->etc == etcVRESCALE || ETC_ANDERSEN(ir->etc))
 +    {
 +        upd->sd = init_stochd(fplog, ir, gmx_omp_nthreads_get(emntUpdate));
 +    }
 +
 +    upd->xp                 = NULL;
 +    upd->xp_nalloc          = 0;
 +    upd->randatom           = NULL;
 +    upd->randatom_list      = NULL;
 +    upd->randatom_list_init = FALSE; /* we have not yet cleared the data structure at this point */
 +
 +    return upd;
 +}
 +
 +static void do_update_sd1(gmx_stochd_t *sd,
 +                          gmx_rng_t gaussrand,
 +                          int start, int nrend, double dt,
 +                          rvec accel[], ivec nFreeze[],
 +                          real invmass[], unsigned short ptype[],
 +                          unsigned short cFREEZE[], unsigned short cACC[],
 +                          unsigned short cTC[],
 +                          rvec x[], rvec xprime[], rvec v[], rvec f[],
 +                          rvec sd_X[],
 +                          int ngtc, real tau_t[], real ref_t[])
 +{
 +    gmx_sd_const_t *sdc;
 +    gmx_sd_sigma_t *sig;
 +    real            kT;
 +    int             gf = 0, ga = 0, gt = 0;
 +    real            ism, sd_V;
 +    int             n, d;
 +
 +    sdc = sd->sdc;
 +    sig = sd->sdsig;
 +
 +    for (n = 0; n < ngtc; n++)
 +    {
 +        kT = BOLTZ*ref_t[n];
 +        /* The mass is encounted for later, since this differs per atom */
 +        sig[n].V  = sqrt(kT*(1 - sdc[n].em*sdc[n].em));
 +    }
 +
 +    for (n = start; n < nrend; n++)
 +    {
 +        ism = sqrt(invmass[n]);
 +        if (cFREEZE)
 +        {
 +            gf  = cFREEZE[n];
 +        }
 +        if (cACC)
 +        {
 +            ga  = cACC[n];
 +        }
 +        if (cTC)
 +        {
 +            gt  = cTC[n];
 +        }
 +
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +            {
 +                sd_V = ism*sig[gt].V*gmx_rng_gaussian_table(gaussrand);
 +
 +                v[n][d] = v[n][d]*sdc[gt].em
 +                    + (invmass[n]*f[n][d] + accel[ga][d])*tau_t[gt]*(1 - sdc[gt].em)
 +                    + sd_V;
 +
 +                xprime[n][d] = x[n][d] + v[n][d]*dt;
 +            }
 +            else
 +            {
 +                v[n][d]      = 0.0;
 +                xprime[n][d] = x[n][d];
 +            }
 +        }
 +    }
 +}
 +
 +static void check_sd2_work_data_allocation(gmx_stochd_t *sd, int nrend)
 +{
 +    if (nrend > sd->sd_V_nalloc)
 +    {
 +        sd->sd_V_nalloc = over_alloc_dd(nrend);
 +        srenew(sd->sd_V, sd->sd_V_nalloc);
 +    }
 +}
 +
 +static void do_update_sd2(gmx_stochd_t *sd,
 +                          gmx_rng_t gaussrand,
 +                          gmx_bool bInitStep,
 +                          int start, int nrend,
 +                          rvec accel[], ivec nFreeze[],
 +                          real invmass[], unsigned short ptype[],
 +                          unsigned short cFREEZE[], unsigned short cACC[],
 +                          unsigned short cTC[],
 +                          rvec x[], rvec xprime[], rvec v[], rvec f[],
 +                          rvec sd_X[],
 +                          int ngtc, real tau_t[], real ref_t[],
 +                          gmx_bool bFirstHalf)
 +{
 +    gmx_sd_const_t *sdc;
 +    gmx_sd_sigma_t *sig;
 +    /* The random part of the velocity update, generated in the first
 +     * half of the update, needs to be remembered for the second half.
 +     */
 +    rvec  *sd_V;
 +    real   kT;
 +    int    gf = 0, ga = 0, gt = 0;
 +    real   vn = 0, Vmh, Xmh;
 +    real   ism;
 +    int    n, d;
 +
 +    sdc  = sd->sdc;
 +    sig  = sd->sdsig;
 +    sd_V = sd->sd_V;
 +
 +    if (bFirstHalf)
 +    {
 +        for (n = 0; n < ngtc; n++)
 +        {
 +            kT = BOLTZ*ref_t[n];
 +            /* The mass is encounted for later, since this differs per atom */
 +            sig[n].V  = sqrt(kT*(1-sdc[n].em));
 +            sig[n].X  = sqrt(kT*sqr(tau_t[n])*sdc[n].c);
 +            sig[n].Yv = sqrt(kT*sdc[n].b/sdc[n].c);
 +            sig[n].Yx = sqrt(kT*sqr(tau_t[n])*sdc[n].b/(1-sdc[n].em));
 +        }
 +    }
 +
 +    for (n = start; n < nrend; n++)
 +    {
 +        ism = sqrt(invmass[n]);
 +        if (cFREEZE)
 +        {
 +            gf  = cFREEZE[n];
 +        }
 +        if (cACC)
 +        {
 +            ga  = cACC[n];
 +        }
 +        if (cTC)
 +        {
 +            gt  = cTC[n];
 +        }
 +
 +        for (d = 0; d < DIM; d++)
 +        {
 +            if (bFirstHalf)
 +            {
 +                vn             = v[n][d];
 +            }
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +            {
 +                if (bFirstHalf)
 +                {
 +                    if (bInitStep)
 +                    {
 +                        sd_X[n][d] = ism*sig[gt].X*gmx_rng_gaussian_table(gaussrand);
 +                    }
 +                    Vmh = sd_X[n][d]*sdc[gt].d/(tau_t[gt]*sdc[gt].c)
 +                        + ism*sig[gt].Yv*gmx_rng_gaussian_table(gaussrand);
 +                    sd_V[n][d] = ism*sig[gt].V*gmx_rng_gaussian_table(gaussrand);
 +
 +                    v[n][d] = vn*sdc[gt].em
 +                        + (invmass[n]*f[n][d] + accel[ga][d])*tau_t[gt]*(1 - sdc[gt].em)
 +                        + sd_V[n][d] - sdc[gt].em*Vmh;
 +
 +                    xprime[n][d] = x[n][d] + v[n][d]*tau_t[gt]*(sdc[gt].eph - sdc[gt].emh);
 +                }
 +                else
 +                {
 +
 +                    /* Correct the velocities for the constraints.
 +                     * This operation introduces some inaccuracy,
 +                     * since the velocity is determined from differences in coordinates.
 +                     */
 +                    v[n][d] =
 +                        (xprime[n][d] - x[n][d])/(tau_t[gt]*(sdc[gt].eph - sdc[gt].emh));
 +
 +                    Xmh = sd_V[n][d]*tau_t[gt]*sdc[gt].d/(sdc[gt].em-1)
 +                        + ism*sig[gt].Yx*gmx_rng_gaussian_table(gaussrand);
 +                    sd_X[n][d] = ism*sig[gt].X*gmx_rng_gaussian_table(gaussrand);
 +
 +                    xprime[n][d] += sd_X[n][d] - Xmh;
 +
 +                }
 +            }
 +            else
 +            {
 +                if (bFirstHalf)
 +                {
 +                    v[n][d]        = 0.0;
 +                    xprime[n][d]   = x[n][d];
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +static void do_update_bd(int start, int nrend, double dt,
 +                         ivec nFreeze[],
 +                         real invmass[], unsigned short ptype[],
 +                         unsigned short cFREEZE[], unsigned short cTC[],
 +                         rvec x[], rvec xprime[], rvec v[],
 +                         rvec f[], real friction_coefficient,
 +                         int ngtc, real tau_t[], real ref_t[],
 +                         real *rf, gmx_rng_t gaussrand)
 +{
 +    /* note -- these appear to be full step velocities . . .  */
 +    int    gf = 0, gt = 0;
 +    real   vn;
 +    real   invfr = 0;
 +    int    n, d;
 +
 +    if (friction_coefficient != 0)
 +    {
 +        invfr = 1.0/friction_coefficient;
 +        for (n = 0; n < ngtc; n++)
 +        {
 +            rf[n] = sqrt(2.0*BOLTZ*ref_t[n]/(friction_coefficient*dt));
 +        }
 +    }
 +    else
 +    {
 +        for (n = 0; n < ngtc; n++)
 +        {
 +            rf[n] = sqrt(2.0*BOLTZ*ref_t[n]);
 +        }
 +    }
 +    for (n = start; (n < nrend); n++)
 +    {
 +        if (cFREEZE)
 +        {
 +            gf = cFREEZE[n];
 +        }
 +        if (cTC)
 +        {
 +            gt = cTC[n];
 +        }
 +        for (d = 0; (d < DIM); d++)
 +        {
 +            if ((ptype[n] != eptVSite) && (ptype[n] != eptShell) && !nFreeze[gf][d])
 +            {
 +                if (friction_coefficient != 0)
 +                {
 +                    vn = invfr*f[n][d] + rf[gt]*gmx_rng_gaussian_table(gaussrand);
 +                }
 +                else
 +                {
 +                    /* NOTE: invmass = 2/(mass*friction_constant*dt) */
 +                    vn = 0.5*invmass[n]*f[n][d]*dt
 +                        + sqrt(0.5*invmass[n])*rf[gt]*gmx_rng_gaussian_table(gaussrand);
 +                }
 +
 +                v[n][d]      = vn;
 +                xprime[n][d] = x[n][d]+vn*dt;
 +            }
 +            else
 +            {
 +                v[n][d]      = 0.0;
 +                xprime[n][d] = x[n][d];
 +            }
 +        }
 +    }
 +}
 +
 +static void dump_it_all(FILE *fp, const char *title,
 +                        int natoms, rvec x[], rvec xp[], rvec v[], rvec f[])
 +{
 +#ifdef DEBUG
 +    if (fp)
 +    {
 +        fprintf(fp, "%s\n", title);
 +        pr_rvecs(fp, 0, "x", x, natoms);
 +        pr_rvecs(fp, 0, "xp", xp, natoms);
 +        pr_rvecs(fp, 0, "v", v, natoms);
 +        pr_rvecs(fp, 0, "f", f, natoms);
 +    }
 +#endif
 +}
 +
 +static void calc_ke_part_normal(rvec v[], t_grpopts *opts, t_mdatoms *md,
 +                                gmx_ekindata_t *ekind, t_nrnb *nrnb, gmx_bool bEkinAveVel,
 +                                gmx_bool bSaveEkinOld)
 +{
 +    int           g;
 +    t_grp_tcstat *tcstat  = ekind->tcstat;
 +    t_grp_acc    *grpstat = ekind->grpstat;
 +    int           nthread, thread;
 +
 +    /* three main: VV with AveVel, vv with AveEkin, leap with AveEkin.  Leap with AveVel is also
 +       an option, but not supported now.  Additionally, if we are doing iterations.
 +       bEkinAveVel: If TRUE, we sum into ekin, if FALSE, into ekinh.
 +       bSavEkinOld: If TRUE (in the case of iteration = bIterate is TRUE), we don't copy over the ekinh_old.
 +       If FALSE, we overrwrite it.
 +     */
 +
 +    /* group velocities are calculated in update_ekindata and
 +     * accumulated in acumulate_groups.
 +     * Now the partial global and groups ekin.
 +     */
 +    for (g = 0; (g < opts->ngtc); g++)
 +    {
 +
 +        if (!bSaveEkinOld)
 +        {
 +            copy_mat(tcstat[g].ekinh, tcstat[g].ekinh_old);
 +        }
 +        if (bEkinAveVel)
 +        {
 +            clear_mat(tcstat[g].ekinf);
 +        }
 +        else
 +        {
 +            clear_mat(tcstat[g].ekinh);
 +        }
 +        if (bEkinAveVel)
 +        {
 +            tcstat[g].ekinscalef_nhc = 1.0; /* need to clear this -- logic is complicated! */
 +        }
 +    }
 +    ekind->dekindl_old = ekind->dekindl;
 +
 +    nthread = gmx_omp_nthreads_get(emntUpdate);
 +
 +#pragma omp parallel for num_threads(nthread) schedule(static)
 +    for (thread = 0; thread < nthread; thread++)
 +    {
 +        int     start_t, end_t, n;
 +        int     ga, gt;
 +        rvec    v_corrt;
 +        real    hm;
 +        int     d, m;
 +        matrix *ekin_sum;
 +        real   *dekindl_sum;
 +
 +        start_t = md->start + ((thread+0)*md->homenr)/nthread;
 +        end_t   = md->start + ((thread+1)*md->homenr)/nthread;
 +
 +        ekin_sum    = ekind->ekin_work[thread];
-                 *dekindl_sum -=
++        dekindl_sum = ekind->dekindl_work[thread];
 +
 +        for (gt = 0; gt < opts->ngtc; gt++)
 +        {
 +            clear_mat(ekin_sum[gt]);
 +        }
++        *dekindl_sum = 0.0;
 +
 +        ga = 0;
 +        gt = 0;
 +        for (n = start_t; n < end_t; n++)
 +        {
 +            if (md->cACC)
 +            {
 +                ga = md->cACC[n];
 +            }
 +            if (md->cTC)
 +            {
 +                gt = md->cTC[n];
 +            }
 +            hm   = 0.5*md->massT[n];
 +
 +            for (d = 0; (d < DIM); d++)
 +            {
 +                v_corrt[d]  = v[n][d]  - grpstat[ga].u[d];
 +            }
 +            for (d = 0; (d < DIM); d++)
 +            {
 +                for (m = 0; (m < DIM); m++)
 +                {
 +                    /* if we're computing a full step velocity, v_corrt[d] has v(t).  Otherwise, v(t+dt/2) */
 +                    ekin_sum[gt][m][d] += hm*v_corrt[m]*v_corrt[d];
 +                }
 +            }
 +            if (md->nMassPerturbed && md->bPerturbed[n])
 +            {
-         ekind->dekindl += ekind->ekin_work[thread][opts->ngtc][0][0];
++                *dekindl_sum +=
 +                    0.5*(md->massB[n] - md->massA[n])*iprod(v_corrt, v_corrt);
 +            }
 +        }
 +    }
 +
 +    ekind->dekindl = 0;
 +    for (thread = 0; thread < nthread; thread++)
 +    {
 +        for (g = 0; g < opts->ngtc; g++)
 +        {
 +            if (bEkinAveVel)
 +            {
 +                m_add(tcstat[g].ekinf, ekind->ekin_work[thread][g],
 +                      tcstat[g].ekinf);
 +            }
 +            else
 +            {
 +                m_add(tcstat[g].ekinh, ekind->ekin_work[thread][g],
 +                      tcstat[g].ekinh);
 +            }
 +        }
 +
-             dekindl -= 0.5*(md->massB[n] - md->massA[n])*iprod(v_corrt, v_corrt);
++        ekind->dekindl += *ekind->dekindl_work[thread];
 +    }
 +
 +    inc_nrnb(nrnb, eNR_EKIN, md->homenr);
 +}
 +
 +static void calc_ke_part_visc(matrix box, rvec x[], rvec v[],
 +                              t_grpopts *opts, t_mdatoms *md,
 +                              gmx_ekindata_t *ekind,
 +                              t_nrnb *nrnb, gmx_bool bEkinAveVel, gmx_bool bSaveEkinOld)
 +{
 +    int           start = md->start, homenr = md->homenr;
 +    int           g, d, n, m, gt = 0;
 +    rvec          v_corrt;
 +    real          hm;
 +    t_grp_tcstat *tcstat = ekind->tcstat;
 +    t_cos_acc    *cosacc = &(ekind->cosacc);
 +    real          dekindl;
 +    real          fac, cosz;
 +    double        mvcos;
 +
 +    for (g = 0; g < opts->ngtc; g++)
 +    {
 +        copy_mat(ekind->tcstat[g].ekinh, ekind->tcstat[g].ekinh_old);
 +        clear_mat(ekind->tcstat[g].ekinh);
 +    }
 +    ekind->dekindl_old = ekind->dekindl;
 +
 +    fac     = 2*M_PI/box[ZZ][ZZ];
 +    mvcos   = 0;
 +    dekindl = 0;
 +    for (n = start; n < start+homenr; n++)
 +    {
 +        if (md->cTC)
 +        {
 +            gt = md->cTC[n];
 +        }
 +        hm   = 0.5*md->massT[n];
 +
 +        /* Note that the times of x and v differ by half a step */
 +        /* MRS -- would have to be changed for VV */
 +        cosz         = cos(fac*x[n][ZZ]);
 +        /* Calculate the amplitude of the new velocity profile */
 +        mvcos       += 2*cosz*md->massT[n]*v[n][XX];
 +
 +        copy_rvec(v[n], v_corrt);
 +        /* Subtract the profile for the kinetic energy */
 +        v_corrt[XX] -= cosz*cosacc->vcos;
 +        for (d = 0; (d < DIM); d++)
 +        {
 +            for (m = 0; (m < DIM); m++)
 +            {
 +                /* if we're computing a full step velocity, v_corrt[d] has v(t).  Otherwise, v(t+dt/2) */
 +                if (bEkinAveVel)
 +                {
 +                    tcstat[gt].ekinf[m][d] += hm*v_corrt[m]*v_corrt[d];
 +                }
 +                else
 +                {
 +                    tcstat[gt].ekinh[m][d] += hm*v_corrt[m]*v_corrt[d];
 +                }
 +            }
 +        }
 +        if (md->nPerturbed && md->bPerturbed[n])
 +        {
++            dekindl += 0.5*(md->massB[n] - md->massA[n])*iprod(v_corrt, v_corrt);
 +        }
 +    }
 +    ekind->dekindl = dekindl;
 +    cosacc->mvcos  = mvcos;
 +
 +    inc_nrnb(nrnb, eNR_EKIN, homenr);
 +}
 +
 +void calc_ke_part(t_state *state, t_grpopts *opts, t_mdatoms *md,
 +                  gmx_ekindata_t *ekind, t_nrnb *nrnb, gmx_bool bEkinAveVel, gmx_bool bSaveEkinOld)
 +{
 +    if (ekind->cosacc.cos_accel == 0)
 +    {
 +        calc_ke_part_normal(state->v, opts, md, ekind, nrnb, bEkinAveVel, bSaveEkinOld);
 +    }
 +    else
 +    {
 +        calc_ke_part_visc(state->box, state->x, state->v, opts, md, ekind, nrnb, bEkinAveVel, bSaveEkinOld);
 +    }
 +}
 +
 +extern void init_ekinstate(ekinstate_t *ekinstate, const t_inputrec *ir)
 +{
 +    ekinstate->ekin_n = ir->opts.ngtc;
 +    snew(ekinstate->ekinh, ekinstate->ekin_n);
 +    snew(ekinstate->ekinf, ekinstate->ekin_n);
 +    snew(ekinstate->ekinh_old, ekinstate->ekin_n);
 +    snew(ekinstate->ekinscalef_nhc, ekinstate->ekin_n);
 +    snew(ekinstate->ekinscaleh_nhc, ekinstate->ekin_n);
 +    snew(ekinstate->vscale_nhc, ekinstate->ekin_n);
 +    ekinstate->dekindl = 0;
 +    ekinstate->mvcos   = 0;
 +}
 +
 +void update_ekinstate(ekinstate_t *ekinstate, gmx_ekindata_t *ekind)
 +{
 +    int i;
 +
 +    for (i = 0; i < ekinstate->ekin_n; i++)
 +    {
 +        copy_mat(ekind->tcstat[i].ekinh, ekinstate->ekinh[i]);
 +        copy_mat(ekind->tcstat[i].ekinf, ekinstate->ekinf[i]);
 +        copy_mat(ekind->tcstat[i].ekinh_old, ekinstate->ekinh_old[i]);
 +        ekinstate->ekinscalef_nhc[i] = ekind->tcstat[i].ekinscalef_nhc;
 +        ekinstate->ekinscaleh_nhc[i] = ekind->tcstat[i].ekinscaleh_nhc;
 +        ekinstate->vscale_nhc[i]     = ekind->tcstat[i].vscale_nhc;
 +    }
 +
 +    copy_mat(ekind->ekin, ekinstate->ekin_total);
 +    ekinstate->dekindl = ekind->dekindl;
 +    ekinstate->mvcos   = ekind->cosacc.mvcos;
 +
 +}
 +
 +void restore_ekinstate_from_state(t_commrec *cr,
 +                                  gmx_ekindata_t *ekind, ekinstate_t *ekinstate)
 +{
 +    int i, n;
 +
 +    if (MASTER(cr))
 +    {
 +        for (i = 0; i < ekinstate->ekin_n; i++)
 +        {
 +            copy_mat(ekinstate->ekinh[i], ekind->tcstat[i].ekinh);
 +            copy_mat(ekinstate->ekinf[i], ekind->tcstat[i].ekinf);
 +            copy_mat(ekinstate->ekinh_old[i], ekind->tcstat[i].ekinh_old);
 +            ekind->tcstat[i].ekinscalef_nhc = ekinstate->ekinscalef_nhc[i];
 +            ekind->tcstat[i].ekinscaleh_nhc = ekinstate->ekinscaleh_nhc[i];
 +            ekind->tcstat[i].vscale_nhc     = ekinstate->vscale_nhc[i];
 +        }
 +
 +        copy_mat(ekinstate->ekin_total, ekind->ekin);
 +
 +        ekind->dekindl      = ekinstate->dekindl;
 +        ekind->cosacc.mvcos = ekinstate->mvcos;
 +        n                   = ekinstate->ekin_n;
 +    }
 +
 +    if (PAR(cr))
 +    {
 +        gmx_bcast(sizeof(n), &n, cr);
 +        for (i = 0; i < n; i++)
 +        {
 +            gmx_bcast(DIM*DIM*sizeof(ekind->tcstat[i].ekinh[0][0]),
 +                      ekind->tcstat[i].ekinh[0], cr);
 +            gmx_bcast(DIM*DIM*sizeof(ekind->tcstat[i].ekinf[0][0]),
 +                      ekind->tcstat[i].ekinf[0], cr);
 +            gmx_bcast(DIM*DIM*sizeof(ekind->tcstat[i].ekinh_old[0][0]),
 +                      ekind->tcstat[i].ekinh_old[0], cr);
 +
 +            gmx_bcast(sizeof(ekind->tcstat[i].ekinscalef_nhc),
 +                      &(ekind->tcstat[i].ekinscalef_nhc), cr);
 +            gmx_bcast(sizeof(ekind->tcstat[i].ekinscaleh_nhc),
 +                      &(ekind->tcstat[i].ekinscaleh_nhc), cr);
 +            gmx_bcast(sizeof(ekind->tcstat[i].vscale_nhc),
 +                      &(ekind->tcstat[i].vscale_nhc), cr);
 +        }
 +        gmx_bcast(DIM*DIM*sizeof(ekind->ekin[0][0]),
 +                  ekind->ekin[0], cr);
 +
 +        gmx_bcast(sizeof(ekind->dekindl), &ekind->dekindl, cr);
 +        gmx_bcast(sizeof(ekind->cosacc.mvcos), &ekind->cosacc.mvcos, cr);
 +    }
 +}
 +
 +void set_deform_reference_box(gmx_update_t upd, gmx_large_int_t step, matrix box)
 +{
 +    upd->deformref_step = step;
 +    copy_mat(box, upd->deformref_box);
 +}
 +
 +static void deform(gmx_update_t upd,
 +                   int start, int homenr, rvec x[], matrix box, matrix *scale_tot,
 +                   const t_inputrec *ir, gmx_large_int_t step)
 +{
 +    matrix bnew, invbox, mu;
 +    real   elapsed_time;
 +    int    i, j;
 +
 +    elapsed_time = (step + 1 - upd->deformref_step)*ir->delta_t;
 +    copy_mat(box, bnew);
 +    for (i = 0; i < DIM; i++)
 +    {
 +        for (j = 0; j < DIM; j++)
 +        {
 +            if (ir->deform[i][j] != 0)
 +            {
 +                bnew[i][j] =
 +                    upd->deformref_box[i][j] + elapsed_time*ir->deform[i][j];
 +            }
 +        }
 +    }
 +    /* We correct the off-diagonal elements,
 +     * which can grow indefinitely during shearing,
 +     * so the shifts do not get messed up.
 +     */
 +    for (i = 1; i < DIM; i++)
 +    {
 +        for (j = i-1; j >= 0; j--)
 +        {
 +            while (bnew[i][j] - box[i][j] > 0.5*bnew[j][j])
 +            {
 +                rvec_dec(bnew[i], bnew[j]);
 +            }
 +            while (bnew[i][j] - box[i][j] < -0.5*bnew[j][j])
 +            {
 +                rvec_inc(bnew[i], bnew[j]);
 +            }
 +        }
 +    }
 +    m_inv_ur0(box, invbox);
 +    copy_mat(bnew, box);
 +    mmul_ur0(box, invbox, mu);
 +
 +    for (i = start; i < start+homenr; i++)
 +    {
 +        x[i][XX] = mu[XX][XX]*x[i][XX]+mu[YY][XX]*x[i][YY]+mu[ZZ][XX]*x[i][ZZ];
 +        x[i][YY] = mu[YY][YY]*x[i][YY]+mu[ZZ][YY]*x[i][ZZ];
 +        x[i][ZZ] = mu[ZZ][ZZ]*x[i][ZZ];
 +    }
 +    if (*scale_tot)
 +    {
 +        /* The transposes of the scaling matrices are stored,
 +         * so we need to do matrix multiplication in the inverse order.
 +         */
 +        mmul_ur0(*scale_tot, mu, *scale_tot);
 +    }
 +}
 +
 +static void combine_forces(int nstcalclr,
 +                           gmx_constr_t constr,
 +                           t_inputrec *ir, t_mdatoms *md, t_idef *idef,
 +                           t_commrec *cr,
 +                           gmx_large_int_t step,
 +                           t_state *state, gmx_bool bMolPBC,
 +                           int start, int nrend,
 +                           rvec f[], rvec f_lr[],
 +                           t_nrnb *nrnb)
 +{
 +    int  i, d, nm1;
 +
 +    /* f contains the short-range forces + the long range forces
 +     * which are stored separately in f_lr.
 +     */
 +
 +    if (constr != NULL && !(ir->eConstrAlg == econtSHAKE && ir->epc == epcNO))
 +    {
 +        /* We need to constrain the LR forces separately,
 +         * because due to the different pre-factor for the SR and LR
 +         * forces in the update algorithm, we can not determine
 +         * the constraint force for the coordinate constraining.
 +         * Constrain only the additional LR part of the force.
 +         */
 +        /* MRS -- need to make sure this works with trotter integration -- the constraint calls may not be right.*/
 +        constrain(NULL, FALSE, FALSE, constr, idef, ir, NULL, cr, step, 0, md,
 +                  state->x, f_lr, f_lr, bMolPBC, state->box, state->lambda[efptBONDED], NULL,
 +                  NULL, NULL, nrnb, econqForce, ir->epc == epcMTTK, state->veta, state->veta);
 +    }
 +
 +    /* Add nstcalclr-1 times the LR force to the sum of both forces
 +     * and store the result in forces_lr.
 +     */
 +    nm1 = nstcalclr - 1;
 +    for (i = start; i < nrend; i++)
 +    {
 +        for (d = 0; d < DIM; d++)
 +        {
 +            f_lr[i][d] = f[i][d] + nm1*f_lr[i][d];
 +        }
 +    }
 +}
 +
 +void update_tcouple(FILE             *fplog,
 +                    gmx_large_int_t   step,
 +                    t_inputrec       *inputrec,
 +                    t_state          *state,
 +                    gmx_ekindata_t   *ekind,
 +                    gmx_wallcycle_t   wcycle,
 +                    gmx_update_t      upd,
 +                    t_extmass        *MassQ,
 +                    t_mdatoms        *md)
 +
 +{
 +    gmx_bool   bTCouple = FALSE;
 +    real       dttc;
 +    int        i, start, end, homenr, offset;
 +
 +    /* if using vv with trotter decomposition methods, we do this elsewhere in the code */
 +    if (inputrec->etc != etcNO &&
 +        !(IR_NVT_TROTTER(inputrec) || IR_NPT_TROTTER(inputrec) || IR_NPH_TROTTER(inputrec)))
 +    {
 +        /* We should only couple after a step where energies were determined (for leapfrog versions)
 +           or the step energies are determined, for velocity verlet versions */
 +
 +        if (EI_VV(inputrec->eI))
 +        {
 +            offset = 0;
 +        }
 +        else
 +        {
 +            offset = 1;
 +        }
 +        bTCouple = (inputrec->nsttcouple == 1 ||
 +                    do_per_step(step+inputrec->nsttcouple-offset,
 +                                inputrec->nsttcouple));
 +    }
 +
 +    if (bTCouple)
 +    {
 +        dttc = inputrec->nsttcouple*inputrec->delta_t;
 +
 +        switch (inputrec->etc)
 +        {
 +            case etcNO:
 +                break;
 +            case etcBERENDSEN:
 +                berendsen_tcoupl(inputrec, ekind, dttc);
 +                break;
 +            case etcNOSEHOOVER:
 +                nosehoover_tcoupl(&(inputrec->opts), ekind, dttc,
 +                                  state->nosehoover_xi, state->nosehoover_vxi, MassQ);
 +                break;
 +            case etcVRESCALE:
 +                vrescale_tcoupl(inputrec, ekind, dttc,
 +                                state->therm_integral, upd->sd->gaussrand[0]);
 +                break;
 +        }
 +        /* rescale in place here */
 +        if (EI_VV(inputrec->eI))
 +        {
 +            rescale_velocities(ekind, md, md->start, md->start+md->homenr, state->v);
 +        }
 +    }
 +    else
 +    {
 +        /* Set the T scaling lambda to 1 to have no scaling */
 +        for (i = 0; (i < inputrec->opts.ngtc); i++)
 +        {
 +            ekind->tcstat[i].lambda = 1.0;
 +        }
 +    }
 +}
 +
 +void update_pcouple(FILE             *fplog,
 +                    gmx_large_int_t   step,
 +                    t_inputrec       *inputrec,
 +                    t_state          *state,
 +                    matrix            pcoupl_mu,
 +                    matrix            M,
 +                    gmx_wallcycle_t   wcycle,
 +                    gmx_update_t      upd,
 +                    gmx_bool          bInitStep)
 +{
 +    gmx_bool   bPCouple = FALSE;
 +    real       dtpc     = 0;
 +    int        i;
 +
 +    /* if using Trotter pressure, we do this in coupling.c, so we leave it false. */
 +    if (inputrec->epc != epcNO && (!(IR_NPT_TROTTER(inputrec) || IR_NPH_TROTTER(inputrec))))
 +    {
 +        /* We should only couple after a step where energies were determined */
 +        bPCouple = (inputrec->nstpcouple == 1 ||
 +                    do_per_step(step+inputrec->nstpcouple-1,
 +                                inputrec->nstpcouple));
 +    }
 +
 +    clear_mat(pcoupl_mu);
 +    for (i = 0; i < DIM; i++)
 +    {
 +        pcoupl_mu[i][i] = 1.0;
 +    }
 +
 +    clear_mat(M);
 +
 +    if (bPCouple)
 +    {
 +        dtpc = inputrec->nstpcouple*inputrec->delta_t;
 +
 +        switch (inputrec->epc)
 +        {
 +            /* We can always pcoupl, even if we did not sum the energies
 +             * the previous step, since state->pres_prev is only updated
 +             * when the energies have been summed.
 +             */
 +            case (epcNO):
 +                break;
 +            case (epcBERENDSEN):
 +                if (!bInitStep)
 +                {
 +                    berendsen_pcoupl(fplog, step, inputrec, dtpc, state->pres_prev, state->box,
 +                                     pcoupl_mu);
 +                }
 +                break;
 +            case (epcPARRINELLORAHMAN):
 +                parrinellorahman_pcoupl(fplog, step, inputrec, dtpc, state->pres_prev,
 +                                        state->box, state->box_rel, state->boxv,
 +                                        M, pcoupl_mu, bInitStep);
 +                break;
 +            default:
 +                break;
 +        }
 +    }
 +}
 +
 +static rvec *get_xprime(const t_state *state, gmx_update_t upd)
 +{
 +    if (state->nalloc > upd->xp_nalloc)
 +    {
 +        upd->xp_nalloc = state->nalloc;
 +        srenew(upd->xp, upd->xp_nalloc);
 +    }
 +
 +    return upd->xp;
 +}
 +
 +void update_constraints(FILE             *fplog,
 +                        gmx_large_int_t   step,
 +                        real             *dvdlambda, /* the contribution to be added to the bonded interactions */
 +                        t_inputrec       *inputrec,  /* input record and box stuff    */
 +                        gmx_ekindata_t   *ekind,
 +                        t_mdatoms        *md,
 +                        t_state          *state,
 +                        gmx_bool          bMolPBC,
 +                        t_graph          *graph,
 +                        rvec              force[],   /* forces on home particles */
 +                        t_idef           *idef,
 +                        tensor            vir_part,
 +                        tensor            vir,       /* tensors for virial and ekin, needed for computing */
 +                        t_commrec        *cr,
 +                        t_nrnb           *nrnb,
 +                        gmx_wallcycle_t   wcycle,
 +                        gmx_update_t      upd,
 +                        gmx_constr_t      constr,
 +                        gmx_bool          bInitStep,
 +                        gmx_bool          bFirstHalf,
 +                        gmx_bool          bCalcVir,
 +                        real              vetanew)
 +{
 +    gmx_bool             bExtended, bLastStep, bLog = FALSE, bEner = FALSE, bDoConstr = FALSE;
 +    double               dt;
 +    real                 dt_1;
 +    int                  start, homenr, nrend, i, n, m, g, d;
 +    tensor               vir_con;
 +    rvec                *vbuf, *xprime = NULL;
 +    int                  nth, th;
 +
 +    if (constr)
 +    {
 +        bDoConstr = TRUE;
 +    }
 +    if (bFirstHalf && !EI_VV(inputrec->eI))
 +    {
 +        bDoConstr = FALSE;
 +    }
 +
 +    /* for now, SD update is here -- though it really seems like it
 +       should be reformulated as a velocity verlet method, since it has two parts */
 +
 +    start  = md->start;
 +    homenr = md->homenr;
 +    nrend  = start+homenr;
 +
 +    dt   = inputrec->delta_t;
 +    dt_1 = 1.0/dt;
 +
 +    /*
 +     *  Steps (7C, 8C)
 +     *  APPLY CONSTRAINTS:
 +     *  BLOCK SHAKE
 +
 +     * When doing PR pressure coupling we have to constrain the
 +     * bonds in each iteration. If we are only using Nose-Hoover tcoupling
 +     * it is enough to do this once though, since the relative velocities
 +     * after this will be normal to the bond vector
 +     */
 +
 +    if (bDoConstr)
 +    {
 +        /* clear out constraints before applying */
 +        clear_mat(vir_part);
 +
 +        xprime = get_xprime(state, upd);
 +
 +        bLastStep = (step == inputrec->init_step+inputrec->nsteps);
 +        bLog      = (do_per_step(step, inputrec->nstlog) || bLastStep || (step < 0));
 +        bEner     = (do_per_step(step, inputrec->nstenergy) || bLastStep);
 +        /* Constrain the coordinates xprime */
 +        wallcycle_start(wcycle, ewcCONSTR);
 +        if (EI_VV(inputrec->eI) && bFirstHalf)
 +        {
 +            constrain(NULL, bLog, bEner, constr, idef,
 +                      inputrec, ekind, cr, step, 1, md,
 +                      state->x, state->v, state->v,
 +                      bMolPBC, state->box,
 +                      state->lambda[efptBONDED], dvdlambda,
 +                      NULL, bCalcVir ? &vir_con : NULL, nrnb, econqVeloc,
 +                      inputrec->epc == epcMTTK, state->veta, vetanew);
 +        }
 +        else
 +        {
 +            constrain(NULL, bLog, bEner, constr, idef,
 +                      inputrec, ekind, cr, step, 1, md,
 +                      state->x, xprime, NULL,
 +                      bMolPBC, state->box,
 +                      state->lambda[efptBONDED], dvdlambda,
 +                      state->v, bCalcVir ? &vir_con : NULL, nrnb, econqCoord,
 +                      inputrec->epc == epcMTTK, state->veta, state->veta);
 +        }
 +        wallcycle_stop(wcycle, ewcCONSTR);
 +
 +        where();
 +
 +        dump_it_all(fplog, "After Shake",
 +                    state->natoms, state->x, xprime, state->v, force);
 +
 +        if (bCalcVir)
 +        {
 +            if (inputrec->eI == eiSD2)
 +            {
 +                /* A correction factor eph is needed for the SD constraint force */
 +                /* Here we can, unfortunately, not have proper corrections
 +                 * for different friction constants, so we use the first one.
 +                 */
 +                for (i = 0; i < DIM; i++)
 +                {
 +                    for (m = 0; m < DIM; m++)
 +                    {
 +                        vir_part[i][m] += upd->sd->sdc[0].eph*vir_con[i][m];
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                m_add(vir_part, vir_con, vir_part);
 +            }
 +            if (debug)
 +            {
 +                pr_rvecs(debug, 0, "constraint virial", vir_part, DIM);
 +            }
 +        }
 +    }
 +
 +    where();
 +    if ((inputrec->eI == eiSD2) && !(bFirstHalf))
 +    {
 +        xprime = get_xprime(state, upd);
 +
 +        nth = gmx_omp_nthreads_get(emntUpdate);
 +
 +#pragma omp parallel for num_threads(nth) schedule(static)
 +        for (th = 0; th < nth; th++)
 +        {
 +            int start_th, end_th;
 +
 +            start_th = start + ((nrend-start)* th   )/nth;
 +            end_th   = start + ((nrend-start)*(th+1))/nth;
 +
 +            /* The second part of the SD integration */
 +            do_update_sd2(upd->sd, upd->sd->gaussrand[th],
 +                          FALSE, start_th, end_th,
 +                          inputrec->opts.acc, inputrec->opts.nFreeze,
 +                          md->invmass, md->ptype,
 +                          md->cFREEZE, md->cACC, md->cTC,
 +                          state->x, xprime, state->v, force, state->sd_X,
 +                          inputrec->opts.ngtc, inputrec->opts.tau_t,
 +                          inputrec->opts.ref_t, FALSE);
 +        }
 +        inc_nrnb(nrnb, eNR_UPDATE, homenr);
 +
 +        if (bDoConstr)
 +        {
 +            /* Constrain the coordinates xprime */
 +            wallcycle_start(wcycle, ewcCONSTR);
 +            constrain(NULL, bLog, bEner, constr, idef,
 +                      inputrec, NULL, cr, step, 1, md,
 +                      state->x, xprime, NULL,
 +                      bMolPBC, state->box,
 +                      state->lambda[efptBONDED], dvdlambda,
 +                      NULL, NULL, nrnb, econqCoord, FALSE, 0, 0);
 +            wallcycle_stop(wcycle, ewcCONSTR);
 +        }
 +    }
 +
 +    /* We must always unshift after updating coordinates; if we did not shake
 +       x was shifted in do_force */
 +
 +    if (!(bFirstHalf)) /* in the first half of vv, no shift. */
 +    {
 +        if (graph && (graph->nnodes > 0))
 +        {
 +            unshift_x(graph, state->box, state->x, upd->xp);
 +            if (TRICLINIC(state->box))
 +            {
 +                inc_nrnb(nrnb, eNR_SHIFTX, 2*graph->nnodes);
 +            }
 +            else
 +            {
 +                inc_nrnb(nrnb, eNR_SHIFTX, graph->nnodes);
 +            }
 +        }
 +        else
 +        {
 +#pragma omp parallel for num_threads(gmx_omp_nthreads_get(emntUpdate)) schedule(static)
 +            for (i = start; i < nrend; i++)
 +            {
 +                copy_rvec(upd->xp[i], state->x[i]);
 +            }
 +        }
 +
 +        dump_it_all(fplog, "After unshift",
 +                    state->natoms, state->x, upd->xp, state->v, force);
 +    }
 +/* ############# END the update of velocities and positions ######### */
 +}
 +
 +void update_box(FILE             *fplog,
 +                gmx_large_int_t   step,
 +                t_inputrec       *inputrec,  /* input record and box stuff    */
 +                t_mdatoms        *md,
 +                t_state          *state,
 +                t_graph          *graph,
 +                rvec              force[],   /* forces on home particles */
 +                matrix           *scale_tot,
 +                matrix            pcoupl_mu,
 +                t_nrnb           *nrnb,
 +                gmx_wallcycle_t   wcycle,
 +                gmx_update_t      upd,
 +                gmx_bool          bInitStep,
 +                gmx_bool          bFirstHalf)
 +{
 +    gmx_bool             bExtended, bLastStep, bLog = FALSE, bEner = FALSE;
 +    double               dt;
 +    real                 dt_1;
 +    int                  start, homenr, nrend, i, n, m, g;
 +    tensor               vir_con;
 +
 +    start  = md->start;
 +    homenr = md->homenr;
 +    nrend  = start+homenr;
 +
 +    bExtended =
 +        (inputrec->etc == etcNOSEHOOVER) ||
 +        (inputrec->epc == epcPARRINELLORAHMAN) ||
 +        (inputrec->epc == epcMTTK);
 +
 +    dt = inputrec->delta_t;
 +
 +    where();
 +
 +    /* now update boxes */
 +    switch (inputrec->epc)
 +    {
 +        case (epcNO):
 +            break;
 +        case (epcBERENDSEN):
 +            berendsen_pscale(inputrec, pcoupl_mu, state->box, state->box_rel,
 +                             start, homenr, state->x, md->cFREEZE, nrnb);
 +            break;
 +        case (epcPARRINELLORAHMAN):
 +            /* The box velocities were updated in do_pr_pcoupl in the update
 +             * iteration, but we dont change the box vectors until we get here
 +             * since we need to be able to shift/unshift above.
 +             */
 +            for (i = 0; i < DIM; i++)
 +            {
 +                for (m = 0; m <= i; m++)
 +                {
 +                    state->box[i][m] += dt*state->boxv[i][m];
 +                }
 +            }
 +            preserve_box_shape(inputrec, state->box_rel, state->box);
 +
 +            /* Scale the coordinates */
 +            for (n = start; (n < start+homenr); n++)
 +            {
 +                tmvmul_ur0(pcoupl_mu, state->x[n], state->x[n]);
 +            }
 +            break;
 +        case (epcMTTK):
 +            switch (inputrec->epct)
 +            {
 +                case (epctISOTROPIC):
 +                    /* DIM * eta = ln V.  so DIM*eta_new = DIM*eta_old + DIM*dt*veta =>
 +                       ln V_new = ln V_old + 3*dt*veta => V_new = V_old*exp(3*dt*veta) =>
 +                       Side length scales as exp(veta*dt) */
 +
 +                    msmul(state->box, exp(state->veta*dt), state->box);
 +
 +                    /* Relate veta to boxv.  veta = d(eta)/dT = (1/DIM)*1/V dV/dT.
 +                       o               If we assume isotropic scaling, and box length scaling
 +                       factor L, then V = L^DIM (det(M)).  So dV/dt = DIM
 +                       L^(DIM-1) dL/dt det(M), and veta = (1/L) dL/dt.  The
 +                       determinant of B is L^DIM det(M), and the determinant
 +                       of dB/dt is (dL/dT)^DIM det (M).  veta will be
 +                       (det(dB/dT)/det(B))^(1/3).  Then since M =
 +                       B_new*(vol_new)^(1/3), dB/dT_new = (veta_new)*B(new). */
 +
 +                    msmul(state->box, state->veta, state->boxv);
 +                    break;
 +                default:
 +                    break;
 +            }
 +            break;
 +        default:
 +            break;
 +    }
 +
 +    if ((!(IR_NPT_TROTTER(inputrec) || IR_NPH_TROTTER(inputrec))) && scale_tot)
 +    {
 +        /* The transposes of the scaling matrices are stored,
 +         * therefore we need to reverse the order in the multiplication.
 +         */
 +        mmul_ur0(*scale_tot, pcoupl_mu, *scale_tot);
 +    }
 +
 +    if (DEFORM(*inputrec))
 +    {
 +        deform(upd, start, homenr, state->x, state->box, scale_tot, inputrec, step);
 +    }
 +    where();
 +    dump_it_all(fplog, "After update",
 +                state->natoms, state->x, upd->xp, state->v, force);
 +}
 +
 +void update_coords(FILE             *fplog,
 +                   gmx_large_int_t   step,
 +                   t_inputrec       *inputrec,  /* input record and box stuff */
 +                   t_mdatoms        *md,
 +                   t_state          *state,
 +                   gmx_bool          bMolPBC,
 +                   rvec             *f,    /* forces on home particles */
 +                   gmx_bool          bDoLR,
 +                   rvec             *f_lr,
 +                   t_fcdata         *fcd,
 +                   gmx_ekindata_t   *ekind,
 +                   matrix            M,
 +                   gmx_wallcycle_t   wcycle,
 +                   gmx_update_t      upd,
 +                   gmx_bool          bInitStep,
 +                   int               UpdatePart,
 +                   t_commrec        *cr, /* these shouldn't be here -- need to think about it */
 +                   t_nrnb           *nrnb,
 +                   gmx_constr_t      constr,
 +                   t_idef           *idef)
 +{
 +    gmx_bool          bNH, bPR, bLastStep, bLog = FALSE, bEner = FALSE;
 +    double            dt, alpha;
 +    real             *imass, *imassin;
 +    rvec             *force;
 +    real              dt_1;
 +    int               start, homenr, nrend, i, j, d, n, m, g;
 +    int               blen0, blen1, iatom, jatom, nshake, nsettle, nconstr, nexpand;
 +    int              *icom = NULL;
 +    tensor            vir_con;
 +    rvec             *vcom, *xcom, *vall, *xall, *xin, *vin, *forcein, *fall, *xpall, *xprimein, *xprime;
 +    int               nth, th;
 +
 +    /* Running the velocity half does nothing except for velocity verlet */
 +    if ((UpdatePart == etrtVELOCITY1 || UpdatePart == etrtVELOCITY2) &&
 +        !EI_VV(inputrec->eI))
 +    {
 +        gmx_incons("update_coords called for velocity without VV integrator");
 +    }
 +
 +    start  = md->start;
 +    homenr = md->homenr;
 +    nrend  = start+homenr;
 +
 +    xprime = get_xprime(state, upd);
 +
 +    dt   = inputrec->delta_t;
 +    dt_1 = 1.0/dt;
 +
 +    /* We need to update the NMR restraint history when time averaging is used */
 +    if (state->flags & (1<<estDISRE_RM3TAV))
 +    {
 +        update_disres_history(fcd, &state->hist);
 +    }
 +    if (state->flags & (1<<estORIRE_DTAV))
 +    {
 +        update_orires_history(fcd, &state->hist);
 +    }
 +
 +
 +    bNH = inputrec->etc == etcNOSEHOOVER;
 +    bPR = ((inputrec->epc == epcPARRINELLORAHMAN) || (inputrec->epc == epcMTTK));
 +
 +    if (bDoLR && inputrec->nstcalclr > 1 && !EI_VV(inputrec->eI))  /* get this working with VV? */
 +    {
 +        /* Store the total force + nstcalclr-1 times the LR force
 +         * in forces_lr, so it can be used in a normal update algorithm
 +         * to produce twin time stepping.
 +         */
 +        /* is this correct in the new construction? MRS */
 +        combine_forces(inputrec->nstcalclr, constr, inputrec, md, idef, cr,
 +                       step, state, bMolPBC,
 +                       start, nrend, f, f_lr, nrnb);
 +        force = f_lr;
 +    }
 +    else
 +    {
 +        force = f;
 +    }
 +
 +    /* ############# START The update of velocities and positions ######### */
 +    where();
 +    dump_it_all(fplog, "Before update",
 +                state->natoms, state->x, xprime, state->v, force);
 +
 +    if (EI_RANDOM(inputrec->eI))
 +    {
 +        /* We still need to take care of generating random seeds properly
 +         * when multi-threading.
 +         */
 +        nth = 1;
 +    }
 +    else
 +    {
 +        nth = gmx_omp_nthreads_get(emntUpdate);
 +    }
 +
 +    if (inputrec->eI == eiSD2)
 +    {
 +        check_sd2_work_data_allocation(upd->sd, nrend);
 +    }
 +
 +#pragma omp parallel for num_threads(nth) schedule(static) private(alpha)
 +    for (th = 0; th < nth; th++)
 +    {
 +        int start_th, end_th;
 +
 +        start_th = start + ((nrend-start)* th   )/nth;
 +        end_th   = start + ((nrend-start)*(th+1))/nth;
 +
 +        switch (inputrec->eI)
 +        {
 +            case (eiMD):
 +                if (ekind->cosacc.cos_accel == 0)
 +                {
 +                    do_update_md(start_th, end_th, dt,
 +                                 ekind->tcstat, state->nosehoover_vxi,
 +                                 ekind->bNEMD, ekind->grpstat, inputrec->opts.acc,
 +                                 inputrec->opts.nFreeze,
 +                                 md->invmass, md->ptype,
 +                                 md->cFREEZE, md->cACC, md->cTC,
 +                                 state->x, xprime, state->v, force, M,
 +                                 bNH, bPR);
 +                }
 +                else
 +                {
 +                    do_update_visc(start_th, end_th, dt,
 +                                   ekind->tcstat, state->nosehoover_vxi,
 +                                   md->invmass, md->ptype,
 +                                   md->cTC, state->x, xprime, state->v, force, M,
 +                                   state->box,
 +                                   ekind->cosacc.cos_accel,
 +                                   ekind->cosacc.vcos,
 +                                   bNH, bPR);
 +                }
 +                break;
 +            case (eiSD1):
 +                do_update_sd1(upd->sd, upd->sd->gaussrand[th],
 +                              start_th, end_th, dt,
 +                              inputrec->opts.acc, inputrec->opts.nFreeze,
 +                              md->invmass, md->ptype,
 +                              md->cFREEZE, md->cACC, md->cTC,
 +                              state->x, xprime, state->v, force, state->sd_X,
 +                              inputrec->opts.ngtc, inputrec->opts.tau_t, inputrec->opts.ref_t);
 +                break;
 +            case (eiSD2):
 +                /* The SD update is done in 2 parts, because an extra constraint step
 +                 * is needed
 +                 */
 +                do_update_sd2(upd->sd, upd->sd->gaussrand[th],
 +                              bInitStep, start_th, end_th,
 +                              inputrec->opts.acc, inputrec->opts.nFreeze,
 +                              md->invmass, md->ptype,
 +                              md->cFREEZE, md->cACC, md->cTC,
 +                              state->x, xprime, state->v, force, state->sd_X,
 +                              inputrec->opts.ngtc, inputrec->opts.tau_t, inputrec->opts.ref_t,
 +                              TRUE);
 +                break;
 +            case (eiBD):
 +                do_update_bd(start_th, end_th, dt,
 +                             inputrec->opts.nFreeze, md->invmass, md->ptype,
 +                             md->cFREEZE, md->cTC,
 +                             state->x, xprime, state->v, force,
 +                             inputrec->bd_fric,
 +                             inputrec->opts.ngtc, inputrec->opts.tau_t, inputrec->opts.ref_t,
 +                             upd->sd->bd_rf, upd->sd->gaussrand[th]);
 +                break;
 +            case (eiVV):
 +            case (eiVVAK):
 +                alpha = 1.0 + DIM/((double)inputrec->opts.nrdf[0]); /* assuming barostat coupled to group 0. */
 +                switch (UpdatePart)
 +                {
 +                    case etrtVELOCITY1:
 +                    case etrtVELOCITY2:
 +                        do_update_vv_vel(start_th, end_th, dt,
 +                                         ekind->tcstat, ekind->grpstat,
 +                                         inputrec->opts.acc, inputrec->opts.nFreeze,
 +                                         md->invmass, md->ptype,
 +                                         md->cFREEZE, md->cACC,
 +                                         state->v, force,
 +                                         (bNH || bPR), state->veta, alpha);
 +                        break;
 +                    case etrtPOSITION:
 +                        do_update_vv_pos(start_th, end_th, dt,
 +                                         ekind->tcstat, ekind->grpstat,
 +                                         inputrec->opts.acc, inputrec->opts.nFreeze,
 +                                         md->invmass, md->ptype, md->cFREEZE,
 +                                         state->x, xprime, state->v, force,
 +                                         (bNH || bPR), state->veta, alpha);
 +                        break;
 +                }
 +                break;
 +            default:
 +                gmx_fatal(FARGS, "Don't know how to update coordinates");
 +                break;
 +        }
 +    }
 +
 +}
 +
 +
 +void correct_ekin(FILE *log, int start, int end, rvec v[], rvec vcm, real mass[],
 +                  real tmass, tensor ekin)
 +{
 +    /*
 +     * This is a debugging routine. It should not be called for production code
 +     *
 +     * The kinetic energy should calculated according to:
 +     *   Ekin = 1/2 m (v-vcm)^2
 +     * However the correction is not always applied, since vcm may not be
 +     * known in time and we compute
 +     *   Ekin' = 1/2 m v^2 instead
 +     * This can be corrected afterwards by computing
 +     *   Ekin = Ekin' + 1/2 m ( -2 v vcm + vcm^2)
 +     * or in hsorthand:
 +     *   Ekin = Ekin' - m v vcm + 1/2 m vcm^2
 +     */
 +    int    i, j, k;
 +    real   m, tm;
 +    rvec   hvcm, mv;
 +    tensor dekin;
 +
 +    /* Local particles */
 +    clear_rvec(mv);
 +
 +    /* Processor dependent part. */
 +    tm = 0;
 +    for (i = start; (i < end); i++)
 +    {
 +        m      = mass[i];
 +        tm    += m;
 +        for (j = 0; (j < DIM); j++)
 +        {
 +            mv[j] += m*v[i][j];
 +        }
 +    }
 +    /* Shortcut */
 +    svmul(1/tmass, vcm, vcm);
 +    svmul(0.5, vcm, hvcm);
 +    clear_mat(dekin);
 +    for (j = 0; (j < DIM); j++)
 +    {
 +        for (k = 0; (k < DIM); k++)
 +        {
 +            dekin[j][k] += vcm[k]*(tm*hvcm[j]-mv[j]);
 +        }
 +    }
 +    pr_rvecs(log, 0, "dekin", dekin, DIM);
 +    pr_rvecs(log, 0, " ekin", ekin, DIM);
 +    fprintf(log, "dekin = %g, ekin = %g  vcm = (%8.4f %8.4f %8.4f)\n",
 +            trace(dekin), trace(ekin), vcm[XX], vcm[YY], vcm[ZZ]);
 +    fprintf(log, "mv = (%8.4f %8.4f %8.4f)\n",
 +            mv[XX], mv[YY], mv[ZZ]);
 +}
 +
 +extern gmx_bool update_randomize_velocities(t_inputrec *ir, gmx_large_int_t step, t_mdatoms *md, t_state *state, gmx_update_t upd, t_idef *idef, gmx_constr_t constr)
 +{
 +
 +    int  i;
 +    real rate = (ir->delta_t)/ir->opts.tau_t[0];
 +    /* proceed with andersen if 1) it's fixed probability per
 +       particle andersen or 2) it's massive andersen and it's tau_t/dt */
 +    if ((ir->etc == etcANDERSEN) || do_per_step(step, (int)(1.0/rate)))
 +    {
 +        srenew(upd->randatom, state->nalloc);
 +        srenew(upd->randatom_list, state->nalloc);
 +        if (upd->randatom_list_init == FALSE)
 +        {
 +            for (i = 0; i < state->nalloc; i++)
 +            {
 +                upd->randatom[i]      = FALSE;
 +                upd->randatom_list[i] = 0;
 +            }
 +            upd->randatom_list_init = TRUE;
 +        }
 +        andersen_tcoupl(ir, md, state, upd->sd->gaussrand[0], rate,
 +                        (ir->etc == etcANDERSEN) ? idef : NULL,
 +                        constr ? get_nblocks(constr) : 0,
 +                        constr ? get_sblock(constr) : NULL,
 +                        upd->randatom, upd->randatom_list,
 +                        upd->sd->randomize_group, upd->sd->boltzfac);
 +        return TRUE;
 +    }
 +    return FALSE;
 +}
index af42da368c113e336365358f64339bafba1aa63c,cfb344f9b6e628be2579c2f490591893ddddfd55..791061d39084f6c74e3687992cd8fd19a6173db6
@@@ -19,8 -53,8 +19,8 @@@ set(NGMX_PROGRAM
  
  foreach(PROG ${NGMX_PROGRAMS})
          add_executable(${PROG} ${PROG}.c ${NGMX_COMMON_SOURCE})
-         target_link_libraries(${PROG} libgromacs ${GMX_EXTRA_LIBRARIES} ${X11_LIBRARIES})        
++        target_link_libraries(${PROG} libgromacs ${GMX_EXTRA_LIBRARIES})
          gmx_add_man_page(${PROG})
 -        target_link_libraries(${PROG} gmx ${X11_LIBRARIES})
          set_target_properties(${PROG} PROPERTIES OUTPUT_NAME "${PROG}${GMX_BINARY_SUFFIX}")
  endforeach(PROG) 
  
index 1a42a532bc1dce8ddd68a706b39fa22f7cb35dff,0000000000000000000000000000000000000000..0cf9905bb0a8789386503216f238e89aea90c80c
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,51 @@@
-  *                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.
 +/*
++ * This file is part of the GROMACS molecular simulation package.
 + *
-  * 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
 + * 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.
-  * 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.
++ * Copyright (c) 2012, by the GROMACS development team, led by
++ * David van der Spoel, Berk Hess, Erik Lindahl, and including many
++ * others, as listed in the AUTHORS file in the top-level source
++ * directory and at http://www.gromacs.org.
++ *
++ * GROMACS is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public License
++ * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
-  * 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.
++ * 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.
 + *
-  * For more info, check our website at http://www.gromacs.org
++ * 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.
 + *
-  * And Hey:
-  * Gromacs Runs On Most of All Computer Systems
++ * 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.
 + */
 +
 +#ifndef _tomorse_h
 +#define _tomorse_h
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <stdio.h>
 +#include "typedefs.h"
 +
 +extern void convert_harmonics(int nrmols, t_molinfo mols[], gpp_atomtype_t atype);
 +
 +#endif  /* _grompp_h */
index 93d385cf715e346a64ddd7f4b147a10b3c556a7b,0000000000000000000000000000000000000000..e3cb576f79b124b13ff7df284ceca5a8007b70e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,50 @@@
-     include(../contrib/BuildMdrunOpenMM)
 +include_directories(${CMAKE_SOURCE_DIR}/src/gromacs/gmxpreprocess)
 +
 +set(MDRUN_SOURCES
 +    do_gct.c      gctio.c       genalg.c    ionize.c
 +    md.c          mdrun.c     membed.c
 +    pme_loadbal.c repl_ex.c     runner.c    xutils.c
 +    ../main.cpp)
 +
 +if(GMX_OPENMM)
 +    # Even though the OpenMM build has "moved to contrib", many things
 +    # have be be done from within the scope of the CMakeLists.txt that
 +    # builds its mdrun, and that is here
++    list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/src/contrib)
++    find_package(OpenMM)
++    include_directories(${CMAKE_CURRENT_SOURCE_DIR})
++    include(${CMAKE_SOURCE_DIR}/src/contrib/BuildMdrunOpenMM.cmake)
 +endif(GMX_OPENMM)
 +
 +if(GMX_FAHCORE)
 +    add_library(fahcore ${MDRUN_SOURCES})
 +else(GMX_FAHCORE)
 +    add_executable(mdrun ${MDRUN_SOURCES})
 +    gmx_add_man_page(mdrun)
 +    target_link_libraries(mdrun ${GMX_EXTRA_LIBRARIES} libgromacs
 +        ${GMX_EXE_LINKER_FLAGS})
 +    set_target_properties(mdrun PROPERTIES OUTPUT_NAME "mdrun${GMX_BINARY_SUFFIX}"
 +        COMPILE_FLAGS "${OpenMP_C_FLAGS}")
++    if(GMX_OPENMM)
++        target_link_libraries(mdrun openmm_api_wrapper)
++    endif()
 +    install(TARGETS mdrun DESTINATION ${BIN_INSTALL_DIR} COMPONENT mdrun)
 +
 +    # Create the custom install-mdrun target
 +    if (BUILD_SHARED_LIBS)
 +        # If shared libraries are used, we need to install the libraries in
 +        # addition to the mdrun binary.
 +        add_custom_target(install-mdrun
 +            COMMAND ${CMAKE_COMMAND} -DCOMPONENT=libraries
 +                    -P ${CMAKE_BINARY_DIR}/cmake_install.cmake
 +            COMMAND ${CMAKE_COMMAND} -DCOMPONENT=mdrun
 +                    -P ${CMAKE_BINARY_DIR}/cmake_install.cmake
 +            COMMENT "Installing mdrun")
 +    else (BUILD_SHARED_LIBS)
 +        add_custom_target(install-mdrun
 +            COMMAND ${CMAKE_COMMAND} -DCOMPONENT=mdrun
 +                    -P ${CMAKE_BINARY_DIR}/cmake_install.cmake
 +            COMMENT "Installing mdrun")
 +    endif (BUILD_SHARED_LIBS)
 +    add_dependencies(install-mdrun mdrun)
 +endif(GMX_FAHCORE)
index d334b8b7cb1c21414e01f7031c372987995bca9d,0000000000000000000000000000000000000000..41635d608727b34284c71ec082d312de999e81f1
mode 100644,000000..100644
--- /dev/null
@@@ -1,758 -1,0 +1,758 @@@
-     int           nsteps        = -2; /* the value -2 means that the mdp option will be used */
 +/*  -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "typedefs.h"
 +#include "macros.h"
 +#include "copyrite.h"
 +#include "main.h"
 +#include "statutil.h"
 +#include "smalloc.h"
 +#include "futil.h"
 +#include "smalloc.h"
 +#include "edsam.h"
 +#include "mdrun.h"
 +#include "xmdrun.h"
 +#include "checkpoint.h"
 +#ifdef GMX_THREAD_MPI
 +#include "thread_mpi.h"
 +#endif
 +
 +/* afm stuf */
 +#include "pull.h"
 +
 +int cmain(int argc, char *argv[])
 +{
 +    const char   *desc[] = {
 +        "The [TT]mdrun[tt] program is the main computational chemistry engine",
 +        "within GROMACS. Obviously, it performs Molecular Dynamics simulations,",
 +        "but it can also perform Stochastic Dynamics, Energy Minimization,",
 +        "test particle insertion or (re)calculation of energies.",
 +        "Normal mode analysis is another option. In this case [TT]mdrun[tt]",
 +        "builds a Hessian matrix from single conformation.",
 +        "For usual Normal Modes-like calculations, make sure that",
 +        "the structure provided is properly energy-minimized.",
 +        "The generated matrix can be diagonalized by [TT]g_nmeig[tt].[PAR]",
 +        "The [TT]mdrun[tt] program reads the run input file ([TT]-s[tt])",
 +        "and distributes the topology over nodes if needed.",
 +        "[TT]mdrun[tt] produces at least four output files.",
 +        "A single log file ([TT]-g[tt]) is written, unless the option",
 +        "[TT]-seppot[tt] is used, in which case each node writes a log file.",
 +        "The trajectory file ([TT]-o[tt]), contains coordinates, velocities and",
 +        "optionally forces.",
 +        "The structure file ([TT]-c[tt]) contains the coordinates and",
 +        "velocities of the last step.",
 +        "The energy file ([TT]-e[tt]) contains energies, the temperature,",
 +        "pressure, etc, a lot of these things are also printed in the log file.",
 +        "Optionally coordinates can be written to a compressed trajectory file",
 +        "([TT]-x[tt]).[PAR]",
 +        "The option [TT]-dhdl[tt] is only used when free energy calculation is",
 +        "turned on.[PAR]",
 +        "A simulation can be run in parallel using two different parallelization",
 +        "schemes: MPI parallelization and/or OpenMP thread parallelization.",
 +        "The MPI parallelization uses multiple processes when [TT]mdrun[tt] is",
 +        "compiled with a normal MPI library or threads when [TT]mdrun[tt] is",
 +        "compiled with the GROMACS built-in thread-MPI library. OpenMP threads",
 +        "are supported when mdrun is compiled with OpenMP. Full OpenMP support",
 +        "is only available with the Verlet cut-off scheme, with the (older)",
 +        "group scheme only PME-only processes can use OpenMP parallelization.",
 +        "In all cases [TT]mdrun[tt] will by default try to use all the available",
 +        "hardware resources. With a normal MPI library only the options",
 +        "[TT]-ntomp[tt] (with the Verlet cut-off scheme) and [TT]-ntomp_pme[tt],",
 +        "for PME-only processes, can be used to control the number of threads.",
 +        "With thread-MPI there are additional options [TT]-nt[tt], which sets",
 +        "the total number of threads, and [TT]-ntmpi[tt], which sets the number",
 +        "of thread-MPI threads.",
 +        "Note that using combined MPI+OpenMP parallelization is almost always",
 +        "slower than single parallelization, except at the scaling limit, where",
 +        "especially OpenMP parallelization of PME reduces the communication cost.",
 +        "OpenMP-only parallelization is much faster than MPI-only parallelization",
 +        "on a single CPU(-die). Since we currently don't have proper hardware",
 +        "topology detection, [TT]mdrun[tt] compiled with thread-MPI will only",
 +        "automatically use OpenMP-only parallelization when you use up to 4",
 +        "threads, up to 12 threads with Intel Nehalem/Westmere, or up to 16",
 +        "threads with Intel Sandy Bridge or newer CPUs. Otherwise MPI-only",
 +        "parallelization is used (except with GPUs, see below).",
 +        "[PAR]",
 +        "To quickly test the performance of the new Verlet cut-off scheme",
 +        "with old [TT].tpr[tt] files, either on CPUs or CPUs+GPUs, you can use",
 +        "the [TT]-testverlet[tt] option. This should not be used for production,",
 +        "since it can slightly modify potentials and it will remove charge groups",
 +        "making analysis difficult, as the [TT].tpr[tt] file will still contain",
 +        "charge groups. For production simulations it is highly recommended",
 +        "to specify [TT]cutoff-scheme = Verlet[tt] in the [TT].mdp[tt] file.",
 +        "[PAR]",
 +        "With GPUs (only supported with the Verlet cut-off scheme), the number",
 +        "of GPUs should match the number of MPI processes or MPI threads,",
 +        "excluding PME-only processes/threads. With thread-MPI the number",
 +        "of MPI threads will automatically be set to the number of GPUs detected.",
 +        "When you want to use a subset of the available GPUs, you can use",
 +        "the [TT]-gpu_id[tt] option, where GPU id's are passed as a string,",
 +        "e.g. 02 for using GPUs 0 and 2. When you want different GPU id's",
 +        "on different nodes of a compute cluster, use the GMX_GPU_ID environment",
 +        "variable instead. The format for GMX_GPU_ID is identical to ",
 +        "[TT]-gpu_id[tt], but an environment variable can have different values",
 +        "on different nodes of a cluster.",
 +        "[PAR]",
 +        "When using PME with separate PME nodes or with a GPU, the two major",
 +        "compute tasks, the non-bonded force calculation and the PME calculation",
 +        "run on different compute resources. If this load is not balanced,",
 +        "some of the resources will be idle part of time. With the Verlet",
 +        "cut-off scheme this load is automatically balanced when the PME load",
 +        "is too high (but not when it is too low). This is done by scaling",
 +        "the Coulomb cut-off and PME grid spacing by the same amount. In the first",
 +        "few hundred steps different settings are tried and the fastest is chosen",
 +        "for the rest of the simulation. This does not affect the accuracy of",
 +        "the results, but it does affect the decomposition of the Coulomb energy",
 +        "into particle and mesh contributions. The auto-tuning can be turned off",
 +        "with the option [TT]-notunepme[tt].",
 +        "[PAR]",
 +        "[TT]mdrun[tt] pins (sets affinity of) threads to specific cores,",
 +        "when all (logical) cores on a compute node are used by [TT]mdrun[tt],",
 +        "even when no multi-threading is used,",
 +        "as this usually results in significantly better performance.",
 +        "If the queuing systems or the OpenMP library pinned threads, we honor",
 +        "this and don't pin again, even though the layout may be sub-optimal.",
 +        "If you want to have [TT]mdrun[tt] override an already set thread affinity",
 +        "or pin threads when using less cores, use [TT]-pin on[tt].",
 +        "With SMT (simultaneous multithreading), e.g. Intel Hyper-Threading,",
 +        "there are multiple logical cores per physical core.",
 +        "The option [TT]-pinstride[tt] sets the stride in logical cores for",
 +        "pinning consecutive threads. Without SMT, 1 is usually the best choice.",
 +        "With Intel Hyper-Threading 2 is best when using half or less of the",
 +        "logical cores, 1 otherwise. The default value of 0 do exactly that:",
 +        "it minimizes the threads per logical core, to optimize performance.",
 +        "If you want to run multiple mdrun jobs on the same physical node,"
 +        "you should set [TT]-pinstride[tt] to 1 when using all logical cores.",
 +        "When running multiple mdrun (or other) simulations on the same physical",
 +        "node, some simulations need to start pinning from a non-zero core",
 +        "to avoid overloading cores; with [TT]-pinoffset[tt] you can specify",
 +        "the offset in logical cores for pinning.",
 +        "[PAR]",
 +        "When [TT]mdrun[tt] is started using MPI with more than 1 process",
 +        "or with thread-MPI with more than 1 thread, MPI parallelization is used.",
 +        "By default domain decomposition is used, unless the [TT]-pd[tt]",
 +        "option is set, which selects particle decomposition.",
 +        "[PAR]",
 +        "With domain decomposition, the spatial decomposition can be set",
 +        "with option [TT]-dd[tt]. By default [TT]mdrun[tt] selects a good decomposition.",
 +        "The user only needs to change this when the system is very inhomogeneous.",
 +        "Dynamic load balancing is set with the option [TT]-dlb[tt],",
 +        "which can give a significant performance improvement,",
 +        "especially for inhomogeneous systems. The only disadvantage of",
 +        "dynamic load balancing is that runs are no longer binary reproducible,",
 +        "but in most cases this is not important.",
 +        "By default the dynamic load balancing is automatically turned on",
 +        "when the measured performance loss due to load imbalance is 5% or more.",
 +        "At low parallelization these are the only important options",
 +        "for domain decomposition.",
 +        "At high parallelization the options in the next two sections",
 +        "could be important for increasing the performace.",
 +        "[PAR]",
 +        "When PME is used with domain decomposition, separate nodes can",
 +        "be assigned to do only the PME mesh calculation;",
 +        "this is computationally more efficient starting at about 12 nodes.",
 +        "The number of PME nodes is set with option [TT]-npme[tt],",
 +        "this can not be more than half of the nodes.",
 +        "By default [TT]mdrun[tt] makes a guess for the number of PME",
 +        "nodes when the number of nodes is larger than 11 or performance wise",
 +        "not compatible with the PME grid x dimension.",
 +        "But the user should optimize npme. Performance statistics on this issue",
 +        "are written at the end of the log file.",
 +        "For good load balancing at high parallelization, the PME grid x and y",
 +        "dimensions should be divisible by the number of PME nodes",
 +        "(the simulation will run correctly also when this is not the case).",
 +        "[PAR]",
 +        "This section lists all options that affect the domain decomposition.",
 +        "[PAR]",
 +        "Option [TT]-rdd[tt] can be used to set the required maximum distance",
 +        "for inter charge-group bonded interactions.",
 +        "Communication for two-body bonded interactions below the non-bonded",
 +        "cut-off distance always comes for free with the non-bonded communication.",
 +        "Atoms beyond the non-bonded cut-off are only communicated when they have",
 +        "missing bonded interactions; this means that the extra cost is minor",
 +        "and nearly indepedent of the value of [TT]-rdd[tt].",
 +        "With dynamic load balancing option [TT]-rdd[tt] also sets",
 +        "the lower limit for the domain decomposition cell sizes.",
 +        "By default [TT]-rdd[tt] is determined by [TT]mdrun[tt] based on",
 +        "the initial coordinates. The chosen value will be a balance",
 +        "between interaction range and communication cost.",
 +        "[PAR]",
 +        "When inter charge-group bonded interactions are beyond",
 +        "the bonded cut-off distance, [TT]mdrun[tt] terminates with an error message.",
 +        "For pair interactions and tabulated bonds",
 +        "that do not generate exclusions, this check can be turned off",
 +        "with the option [TT]-noddcheck[tt].",
 +        "[PAR]",
 +        "When constraints are present, option [TT]-rcon[tt] influences",
 +        "the cell size limit as well.",
 +        "Atoms connected by NC constraints, where NC is the LINCS order plus 1,",
 +        "should not be beyond the smallest cell size. A error message is",
 +        "generated when this happens and the user should change the decomposition",
 +        "or decrease the LINCS order and increase the number of LINCS iterations.",
 +        "By default [TT]mdrun[tt] estimates the minimum cell size required for P-LINCS",
 +        "in a conservative fashion. For high parallelization it can be useful",
 +        "to set the distance required for P-LINCS with the option [TT]-rcon[tt].",
 +        "[PAR]",
 +        "The [TT]-dds[tt] option sets the minimum allowed x, y and/or z scaling",
 +        "of the cells with dynamic load balancing. [TT]mdrun[tt] will ensure that",
 +        "the cells can scale down by at least this factor. This option is used",
 +        "for the automated spatial decomposition (when not using [TT]-dd[tt])",
 +        "as well as for determining the number of grid pulses, which in turn",
 +        "sets the minimum allowed cell size. Under certain circumstances",
 +        "the value of [TT]-dds[tt] might need to be adjusted to account for",
 +        "high or low spatial inhomogeneity of the system.",
 +        "[PAR]",
 +        "The option [TT]-gcom[tt] can be used to only do global communication",
 +        "every n steps.",
 +        "This can improve performance for highly parallel simulations",
 +        "where this global communication step becomes the bottleneck.",
 +        "For a global thermostat and/or barostat the temperature",
 +        "and/or pressure will also only be updated every [TT]-gcom[tt] steps.",
 +        "By default it is set to the minimum of nstcalcenergy and nstlist.[PAR]",
 +        "With [TT]-rerun[tt] an input trajectory can be given for which ",
 +        "forces and energies will be (re)calculated. Neighbor searching will be",
 +        "performed for every frame, unless [TT]nstlist[tt] is zero",
 +        "(see the [TT].mdp[tt] file).[PAR]",
 +        "ED (essential dynamics) sampling and/or additional flooding potentials",
 +        "are switched on by using the [TT]-ei[tt] flag followed by an [TT].edi[tt]",
 +        "file. The [TT].edi[tt] file can be produced with the [TT]make_edi[tt] tool",
 +        "or by using options in the essdyn menu of the WHAT IF program.",
 +        "[TT]mdrun[tt] produces a [TT].xvg[tt] output file that",
 +        "contains projections of positions, velocities and forces onto selected",
 +        "eigenvectors.[PAR]",
 +        "When user-defined potential functions have been selected in the",
 +        "[TT].mdp[tt] file the [TT]-table[tt] option is used to pass [TT]mdrun[tt]",
 +        "a formatted table with potential functions. The file is read from",
 +        "either the current directory or from the [TT]GMXLIB[tt] directory.",
 +        "A number of pre-formatted tables are presented in the [TT]GMXLIB[tt] dir,",
 +        "for 6-8, 6-9, 6-10, 6-11, 6-12 Lennard-Jones potentials with",
 +        "normal Coulomb.",
 +        "When pair interactions are present, a separate table for pair interaction",
 +        "functions is read using the [TT]-tablep[tt] option.[PAR]",
 +        "When tabulated bonded functions are present in the topology,",
 +        "interaction functions are read using the [TT]-tableb[tt] option.",
 +        "For each different tabulated interaction type the table file name is",
 +        "modified in a different way: before the file extension an underscore is",
 +        "appended, then a 'b' for bonds, an 'a' for angles or a 'd' for dihedrals",
 +        "and finally the table number of the interaction type.[PAR]",
 +        "The options [TT]-px[tt] and [TT]-pf[tt] are used for writing pull COM",
 +        "coordinates and forces when pulling is selected",
 +        "in the [TT].mdp[tt] file.[PAR]",
 +        "With [TT]-multi[tt] or [TT]-multidir[tt], multiple systems can be ",
 +        "simulated in parallel.",
 +        "As many input files/directories are required as the number of systems. ",
 +        "The [TT]-multidir[tt] option takes a list of directories (one for each ",
 +        "system) and runs in each of them, using the input/output file names, ",
 +        "such as specified by e.g. the [TT]-s[tt] option, relative to these ",
 +        "directories.",
 +        "With [TT]-multi[tt], the system number is appended to the run input ",
 +        "and each output filename, for instance [TT]topol.tpr[tt] becomes",
 +        "[TT]topol0.tpr[tt], [TT]topol1.tpr[tt] etc.",
 +        "The number of nodes per system is the total number of nodes",
 +        "divided by the number of systems.",
 +        "One use of this option is for NMR refinement: when distance",
 +        "or orientation restraints are present these can be ensemble averaged",
 +        "over all the systems.[PAR]",
 +        "With [TT]-replex[tt] replica exchange is attempted every given number",
 +        "of steps. The number of replicas is set with the [TT]-multi[tt] or ",
 +        "[TT]-multidir[tt] option, described above.",
 +        "All run input files should use a different coupling temperature,",
 +        "the order of the files is not important. The random seed is set with",
 +        "[TT]-reseed[tt]. The velocities are scaled and neighbor searching",
 +        "is performed after every exchange.[PAR]",
 +        "Finally some experimental algorithms can be tested when the",
 +        "appropriate options have been given. Currently under",
 +        "investigation are: polarizability and X-ray bombardments.",
 +        "[PAR]",
 +        "The option [TT]-membed[tt] does what used to be g_membed, i.e. embed",
 +        "a protein into a membrane. The data file should contain the options",
 +        "that where passed to g_membed before. The [TT]-mn[tt] and [TT]-mp[tt]",
 +        "both apply to this as well.",
 +        "[PAR]",
 +        "The option [TT]-pforce[tt] is useful when you suspect a simulation",
 +        "crashes due to too large forces. With this option coordinates and",
 +        "forces of atoms with a force larger than a certain value will",
 +        "be printed to stderr.",
 +        "[PAR]",
 +        "Checkpoints containing the complete state of the system are written",
 +        "at regular intervals (option [TT]-cpt[tt]) to the file [TT]-cpo[tt],",
 +        "unless option [TT]-cpt[tt] is set to -1.",
 +        "The previous checkpoint is backed up to [TT]state_prev.cpt[tt] to",
 +        "make sure that a recent state of the system is always available,",
 +        "even when the simulation is terminated while writing a checkpoint.",
 +        "With [TT]-cpnum[tt] all checkpoint files are kept and appended",
 +        "with the step number.",
 +        "A simulation can be continued by reading the full state from file",
 +        "with option [TT]-cpi[tt]. This option is intelligent in the way that",
 +        "if no checkpoint file is found, Gromacs just assumes a normal run and",
 +        "starts from the first step of the [TT].tpr[tt] file. By default the output",
 +        "will be appending to the existing output files. The checkpoint file",
 +        "contains checksums of all output files, such that you will never",
 +        "loose data when some output files are modified, corrupt or removed.",
 +        "There are three scenarios with [TT]-cpi[tt]:[PAR]",
 +        "[TT]*[tt] no files with matching names are present: new output files are written[PAR]",
 +        "[TT]*[tt] all files are present with names and checksums matching those stored",
 +        "in the checkpoint file: files are appended[PAR]",
 +        "[TT]*[tt] otherwise no files are modified and a fatal error is generated[PAR]",
 +        "With [TT]-noappend[tt] new output files are opened and the simulation",
 +        "part number is added to all output file names.",
 +        "Note that in all cases the checkpoint file itself is not renamed",
 +        "and will be overwritten, unless its name does not match",
 +        "the [TT]-cpo[tt] option.",
 +        "[PAR]",
 +        "With checkpointing the output is appended to previously written",
 +        "output files, unless [TT]-noappend[tt] is used or none of the previous",
 +        "output files are present (except for the checkpoint file).",
 +        "The integrity of the files to be appended is verified using checksums",
 +        "which are stored in the checkpoint file. This ensures that output can",
 +        "not be mixed up or corrupted due to file appending. When only some",
 +        "of the previous output files are present, a fatal error is generated",
 +        "and no old output files are modified and no new output files are opened.",
 +        "The result with appending will be the same as from a single run.",
 +        "The contents will be binary identical, unless you use a different number",
 +        "of nodes or dynamic load balancing or the FFT library uses optimizations",
 +        "through timing.",
 +        "[PAR]",
 +        "With option [TT]-maxh[tt] a simulation is terminated and a checkpoint",
 +        "file is written at the first neighbor search step where the run time",
 +        "exceeds [TT]-maxh[tt]*0.99 hours.",
 +        "[PAR]",
 +        "When [TT]mdrun[tt] receives a TERM signal, it will set nsteps to the current",
 +        "step plus one. When [TT]mdrun[tt] receives an INT signal (e.g. when ctrl+C is",
 +        "pressed), it will stop after the next neighbor search step ",
 +        "(with nstlist=0 at the next step).",
 +        "In both cases all the usual output will be written to file.",
 +        "When running with MPI, a signal to one of the [TT]mdrun[tt] processes",
 +        "is sufficient, this signal should not be sent to mpirun or",
 +        "the [TT]mdrun[tt] process that is the parent of the others.",
 +        "[PAR]",
 +        "When [TT]mdrun[tt] is started with MPI, it does not run niced by default."
 +    };
 +    t_commrec    *cr;
 +    t_filenm      fnm[] = {
 +        { efTPX, NULL,      NULL,       ffREAD },
 +        { efTRN, "-o",      NULL,       ffWRITE },
 +        { efXTC, "-x",      NULL,       ffOPTWR },
 +        { efCPT, "-cpi",    NULL,       ffOPTRD },
 +        { efCPT, "-cpo",    NULL,       ffOPTWR },
 +        { efSTO, "-c",      "confout",  ffWRITE },
 +        { efEDR, "-e",      "ener",     ffWRITE },
 +        { efLOG, "-g",      "md",       ffWRITE },
 +        { efXVG, "-dhdl",   "dhdl",     ffOPTWR },
 +        { efXVG, "-field",  "field",    ffOPTWR },
 +        { efXVG, "-table",  "table",    ffOPTRD },
 +        { efXVG, "-tabletf", "tabletf",    ffOPTRD },
 +        { efXVG, "-tablep", "tablep",   ffOPTRD },
 +        { efXVG, "-tableb", "table",    ffOPTRD },
 +        { efTRX, "-rerun",  "rerun",    ffOPTRD },
 +        { efXVG, "-tpi",    "tpi",      ffOPTWR },
 +        { efXVG, "-tpid",   "tpidist",  ffOPTWR },
 +        { efEDI, "-ei",     "sam",      ffOPTRD },
 +        { efXVG, "-eo",     "edsam",    ffOPTWR },
 +        { efGCT, "-j",      "wham",     ffOPTRD },
 +        { efGCT, "-jo",     "bam",      ffOPTWR },
 +        { efXVG, "-ffout",  "gct",      ffOPTWR },
 +        { efXVG, "-devout", "deviatie", ffOPTWR },
 +        { efXVG, "-runav",  "runaver",  ffOPTWR },
 +        { efXVG, "-px",     "pullx",    ffOPTWR },
 +        { efXVG, "-pf",     "pullf",    ffOPTWR },
 +        { efXVG, "-ro",     "rotation", ffOPTWR },
 +        { efLOG, "-ra",     "rotangles", ffOPTWR },
 +        { efLOG, "-rs",     "rotslabs", ffOPTWR },
 +        { efLOG, "-rt",     "rottorque", ffOPTWR },
 +        { efMTX, "-mtx",    "nm",       ffOPTWR },
 +        { efNDX, "-dn",     "dipole",   ffOPTWR },
 +        { efRND, "-multidir", NULL,      ffOPTRDMULT},
 +        { efDAT, "-membed", "membed",   ffOPTRD },
 +        { efTOP, "-mp",     "membed",   ffOPTRD },
 +        { efNDX, "-mn",     "membed",   ffOPTRD }
 +    };
 +#define NFILE asize(fnm)
 +
 +    /* Command line options ! */
 +    gmx_bool      bCart         = FALSE;
 +    gmx_bool      bPPPME        = FALSE;
 +    gmx_bool      bPartDec      = FALSE;
 +    gmx_bool      bDDBondCheck  = TRUE;
 +    gmx_bool      bDDBondComm   = TRUE;
 +    gmx_bool      bTunePME      = TRUE;
 +    gmx_bool      bTestVerlet   = FALSE;
 +    gmx_bool      bVerbose      = FALSE;
 +    gmx_bool      bCompact      = TRUE;
 +    gmx_bool      bSepPot       = FALSE;
 +    gmx_bool      bRerunVSite   = FALSE;
 +    gmx_bool      bIonize       = FALSE;
 +    gmx_bool      bConfout      = TRUE;
 +    gmx_bool      bReproducible = FALSE;
 +
 +    int           npme          = -1;
 +    int           nmultisim     = 0;
 +    int           nstglobalcomm = -1;
 +    int           repl_ex_nst   = 0;
 +    int           repl_ex_seed  = -1;
 +    int           repl_ex_nex   = 0;
 +    int           nstepout      = 100;
 +    int           resetstep     = -1;
-         { "-nsteps",  FALSE, etINT, {&nsteps},
++    gmx_large_int_t nsteps      = -2; /* the value -2 means that the mdp option will be used */
 +
 +    rvec          realddxyz          = {0, 0, 0};
 +    const char   *ddno_opt[ddnoNR+1] =
 +    { NULL, "interleave", "pp_pme", "cartesian", NULL };
 +    const char   *dddlb_opt[] =
 +    { NULL, "auto", "no", "yes", NULL };
 +    const char   *thread_aff_opt[threadaffNR+1] =
 +    { NULL, "auto", "on", "off", NULL };
 +    const char   *nbpu_opt[] =
 +    { NULL, "auto", "cpu", "gpu", "gpu_cpu", NULL };
 +    real          rdd                   = 0.0, rconstr = 0.0, dlb_scale = 0.8, pforce = -1;
 +    char         *ddcsx                 = NULL, *ddcsy = NULL, *ddcsz = NULL;
 +    real          cpt_period            = 15.0, max_hours = -1;
 +    gmx_bool      bAppendFiles          = TRUE;
 +    gmx_bool      bKeepAndNumCPT        = FALSE;
 +    gmx_bool      bResetCountersHalfWay = FALSE;
 +    output_env_t  oenv                  = NULL;
 +    const char   *deviceOptions         = "";
 +
 +    gmx_hw_opt_t  hw_opt = {0, 0, 0, 0, threadaffSEL, 0, 0, NULL};
 +
 +    t_pargs       pa[] = {
 +
 +        { "-pd",      FALSE, etBOOL, {&bPartDec},
 +          "Use particle decompostion" },
 +        { "-dd",      FALSE, etRVEC, {&realddxyz},
 +          "Domain decomposition grid, 0 is optimize" },
 +        { "-ddorder", FALSE, etENUM, {ddno_opt},
 +          "DD node order" },
 +        { "-npme",    FALSE, etINT, {&npme},
 +          "Number of separate nodes to be used for PME, -1 is guess" },
 +        { "-nt",      FALSE, etINT, {&hw_opt.nthreads_tot},
 +          "Total number of threads to start (0 is guess)" },
 +        { "-ntmpi",   FALSE, etINT, {&hw_opt.nthreads_tmpi},
 +          "Number of thread-MPI threads to start (0 is guess)" },
 +        { "-ntomp",   FALSE, etINT, {&hw_opt.nthreads_omp},
 +          "Number of OpenMP threads per MPI process/thread to start (0 is guess)" },
 +        { "-ntomp_pme", FALSE, etINT, {&hw_opt.nthreads_omp_pme},
 +          "Number of OpenMP threads per MPI process/thread to start (0 is -ntomp)" },
 +        { "-pin",     FALSE, etENUM, {thread_aff_opt},
 +          "Fix threads (or processes) to specific cores" },
 +        { "-pinoffset", FALSE, etINT, {&hw_opt.core_pinning_offset},
 +          "The starting logical core number for pinning to cores; used to avoid pinning threads from different mdrun instances to the same core" },
 +        { "-pinstride", FALSE, etINT, {&hw_opt.core_pinning_stride},
 +          "Pinning distance in logical cores for threads, use 0 to minimize the number of threads per physical core" },
 +        { "-gpu_id",  FALSE, etSTR, {&hw_opt.gpu_id},
 +          "List of GPU id's to use" },
 +        { "-ddcheck", FALSE, etBOOL, {&bDDBondCheck},
 +          "Check for all bonded interactions with DD" },
 +        { "-ddbondcomm", FALSE, etBOOL, {&bDDBondComm},
 +          "HIDDENUse special bonded atom communication when [TT]-rdd[tt] > cut-off" },
 +        { "-rdd",     FALSE, etREAL, {&rdd},
 +          "The maximum distance for bonded interactions with DD (nm), 0 is determine from initial coordinates" },
 +        { "-rcon",    FALSE, etREAL, {&rconstr},
 +          "Maximum distance for P-LINCS (nm), 0 is estimate" },
 +        { "-dlb",     FALSE, etENUM, {dddlb_opt},
 +          "Dynamic load balancing (with DD)" },
 +        { "-dds",     FALSE, etREAL, {&dlb_scale},
 +          "Minimum allowed dlb scaling of the DD cell size" },
 +        { "-ddcsx",   FALSE, etSTR, {&ddcsx},
 +          "HIDDENThe DD cell sizes in x" },
 +        { "-ddcsy",   FALSE, etSTR, {&ddcsy},
 +          "HIDDENThe DD cell sizes in y" },
 +        { "-ddcsz",   FALSE, etSTR, {&ddcsz},
 +          "HIDDENThe DD cell sizes in z" },
 +        { "-gcom",    FALSE, etINT, {&nstglobalcomm},
 +          "Global communication frequency" },
 +        { "-nb",      FALSE, etENUM, {&nbpu_opt},
 +          "Calculate non-bonded interactions on" },
 +        { "-tunepme", FALSE, etBOOL, {&bTunePME},
 +          "Optimize PME load between PP/PME nodes or GPU/CPU" },
 +        { "-testverlet", FALSE, etBOOL, {&bTestVerlet},
 +          "Test the Verlet non-bonded scheme" },
 +        { "-v",       FALSE, etBOOL, {&bVerbose},
 +          "Be loud and noisy" },
 +        { "-compact", FALSE, etBOOL, {&bCompact},
 +          "Write a compact log file" },
 +        { "-seppot",  FALSE, etBOOL, {&bSepPot},
 +          "Write separate V and dVdl terms for each interaction type and node to the log file(s)" },
 +        { "-pforce",  FALSE, etREAL, {&pforce},
 +          "Print all forces larger than this (kJ/mol nm)" },
 +        { "-reprod",  FALSE, etBOOL, {&bReproducible},
 +          "Try to avoid optimizations that affect binary reproducibility" },
 +        { "-cpt",     FALSE, etREAL, {&cpt_period},
 +          "Checkpoint interval (minutes)" },
 +        { "-cpnum",   FALSE, etBOOL, {&bKeepAndNumCPT},
 +          "Keep and number checkpoint files" },
 +        { "-append",  FALSE, etBOOL, {&bAppendFiles},
 +          "Append to previous output files when continuing from checkpoint instead of adding the simulation part number to all file names" },
++        { "-nsteps",  FALSE, etGMX_LARGE_INT, {&nsteps},
 +          "Run this number of steps, overrides .mdp file option" },
 +        { "-maxh",   FALSE, etREAL, {&max_hours},
 +          "Terminate after 0.99 times this time (hours)" },
 +        { "-multi",   FALSE, etINT, {&nmultisim},
 +          "Do multiple simulations in parallel" },
 +        { "-replex",  FALSE, etINT, {&repl_ex_nst},
 +          "Attempt replica exchange periodically with this period (steps)" },
 +        { "-nex",  FALSE, etINT, {&repl_ex_nex},
 +          "Number of random exchanges to carry out each exchange interval (N^3 is one suggestion).  -nex zero or not specified gives neighbor replica exchange." },
 +        { "-reseed",  FALSE, etINT, {&repl_ex_seed},
 +          "Seed for replica exchange, -1 is generate a seed" },
 +        { "-rerunvsite", FALSE, etBOOL, {&bRerunVSite},
 +          "HIDDENRecalculate virtual site coordinates with [TT]-rerun[tt]" },
 +        { "-ionize",  FALSE, etBOOL, {&bIonize},
 +          "Do a simulation including the effect of an X-Ray bombardment on your system" },
 +        { "-confout", FALSE, etBOOL, {&bConfout},
 +          "HIDDENWrite the last configuration with [TT]-c[tt] and force checkpointing at the last step" },
 +        { "-stepout", FALSE, etINT, {&nstepout},
 +          "HIDDENFrequency of writing the remaining runtime" },
 +        { "-resetstep", FALSE, etINT, {&resetstep},
 +          "HIDDENReset cycle counters after these many time steps" },
 +        { "-resethway", FALSE, etBOOL, {&bResetCountersHalfWay},
 +          "HIDDENReset the cycle counters after half the number of steps or halfway [TT]-maxh[tt]" }
 +    };
 +    gmx_edsam_t   ed;
 +    unsigned long Flags, PCA_Flags;
 +    ivec          ddxyz;
 +    int           dd_node_order;
 +    gmx_bool      bAddPart;
 +    FILE         *fplog, *fpmulti;
 +    int           sim_part, sim_part_fn;
 +    const char   *part_suffix = ".part";
 +    char          suffix[STRLEN];
 +    int           rc;
 +    char        **multidir = NULL;
 +
 +
 +    cr = init_par(&argc, &argv);
 +
 +    if (MASTER(cr))
 +    {
 +        CopyRight(stderr, argv[0]);
 +    }
 +
 +    PCA_Flags = (PCA_CAN_SET_DEFFNM | (MASTER(cr) ? 0 : PCA_QUIET));
 +
 +    /* Comment this in to do fexist calls only on master
 +     * works not with rerun or tables at the moment
 +     * also comment out the version of init_forcerec in md.c
 +     * with NULL instead of opt2fn
 +     */
 +    /*
 +       if (!MASTER(cr))
 +       {
 +       PCA_Flags |= PCA_NOT_READ_NODE;
 +       }
 +     */
 +
 +    parse_common_args(&argc, argv, PCA_Flags, NFILE, fnm, asize(pa), pa,
 +                      asize(desc), desc, 0, NULL, &oenv);
 +
 +
 +    /* we set these early because they might be used in init_multisystem()
 +       Note that there is the potential for npme>nnodes until the number of
 +       threads is set later on, if there's thread parallelization. That shouldn't
 +       lead to problems. */
 +    dd_node_order = nenum(ddno_opt);
 +    cr->npmenodes = npme;
 +
 +    hw_opt.thread_affinity = nenum(thread_aff_opt);
 +
 +    /* now check the -multi and -multidir option */
 +    if (opt2bSet("-multidir", NFILE, fnm))
 +    {
 +        int i;
 +        if (nmultisim > 0)
 +        {
 +            gmx_fatal(FARGS, "mdrun -multi and -multidir options are mutually exclusive.");
 +        }
 +        nmultisim = opt2fns(&multidir, "-multidir", NFILE, fnm);
 +    }
 +
 +
 +    if (repl_ex_nst != 0 && nmultisim < 2)
 +    {
 +        gmx_fatal(FARGS, "Need at least two replicas for replica exchange (option -multi)");
 +    }
 +
 +    if (repl_ex_nex < 0)
 +    {
 +        gmx_fatal(FARGS, "Replica exchange number of exchanges needs to be positive");
 +    }
 +
 +    if (nmultisim > 1)
 +    {
 +#ifndef GMX_THREAD_MPI
 +        gmx_bool bParFn = (multidir == NULL);
 +        init_multisystem(cr, nmultisim, multidir, NFILE, fnm, bParFn);
 +#else
 +        gmx_fatal(FARGS, "mdrun -multi is not supported with the thread library.Please compile GROMACS with MPI support");
 +#endif
 +    }
 +
 +    bAddPart = !bAppendFiles;
 +
 +    /* Check if there is ANY checkpoint file available */
 +    sim_part    = 1;
 +    sim_part_fn = sim_part;
 +    if (opt2bSet("-cpi", NFILE, fnm))
 +    {
 +        if (bSepPot && bAppendFiles)
 +        {
 +            gmx_fatal(FARGS, "Output file appending is not supported with -seppot");
 +        }
 +
 +        bAppendFiles =
 +            read_checkpoint_simulation_part(opt2fn_master("-cpi", NFILE,
 +                                                          fnm, cr),
 +                                            &sim_part_fn, NULL, cr,
 +                                            bAppendFiles, NFILE, fnm,
 +                                            part_suffix, &bAddPart);
 +        if (sim_part_fn == 0 && MULTIMASTER(cr))
 +        {
 +            fprintf(stdout, "No previous checkpoint file present, assuming this is a new run.\n");
 +        }
 +        else
 +        {
 +            sim_part = sim_part_fn + 1;
 +        }
 +
 +        if (MULTISIM(cr) && MASTER(cr))
 +        {
 +            if (MULTIMASTER(cr))
 +            {
 +                /* Log file is not yet available, so if there's a
 +                 * problem we can only write to stderr. */
 +                fpmulti = stderr;
 +            }
 +            else
 +            {
 +                fpmulti = NULL;
 +            }
 +            check_multi_int(fpmulti, cr->ms, sim_part, "simulation part", TRUE);
 +        }
 +    }
 +    else
 +    {
 +        bAppendFiles = FALSE;
 +    }
 +
 +    if (!bAppendFiles)
 +    {
 +        sim_part_fn = sim_part;
 +    }
 +
 +    if (bAddPart)
 +    {
 +        /* Rename all output files (except checkpoint files) */
 +        /* create new part name first (zero-filled) */
 +        sprintf(suffix, "%s%04d", part_suffix, sim_part_fn);
 +
 +        add_suffix_to_output_names(fnm, NFILE, suffix);
 +        if (MULTIMASTER(cr))
 +        {
 +            fprintf(stdout, "Checkpoint file is from part %d, new output files will be suffixed '%s'.\n", sim_part-1, suffix);
 +        }
 +    }
 +
 +    Flags = opt2bSet("-rerun", NFILE, fnm) ? MD_RERUN : 0;
 +    Flags = Flags | (bSepPot       ? MD_SEPPOT       : 0);
 +    Flags = Flags | (bIonize       ? MD_IONIZE       : 0);
 +    Flags = Flags | (bPartDec      ? MD_PARTDEC      : 0);
 +    Flags = Flags | (bDDBondCheck  ? MD_DDBONDCHECK  : 0);
 +    Flags = Flags | (bDDBondComm   ? MD_DDBONDCOMM   : 0);
 +    Flags = Flags | (bTunePME      ? MD_TUNEPME      : 0);
 +    Flags = Flags | (bTestVerlet   ? MD_TESTVERLET   : 0);
 +    Flags = Flags | (bConfout      ? MD_CONFOUT      : 0);
 +    Flags = Flags | (bRerunVSite   ? MD_RERUN_VSITE  : 0);
 +    Flags = Flags | (bReproducible ? MD_REPRODUCIBLE : 0);
 +    Flags = Flags | (bAppendFiles  ? MD_APPENDFILES  : 0);
 +    Flags = Flags | (opt2parg_bSet("-append", asize(pa), pa) ? MD_APPENDFILESSET : 0);
 +    Flags = Flags | (bKeepAndNumCPT ? MD_KEEPANDNUMCPT : 0);
 +    Flags = Flags | (sim_part > 1    ? MD_STARTFROMCPT : 0);
 +    Flags = Flags | (bResetCountersHalfWay ? MD_RESETCOUNTERSHALFWAY : 0);
 +
 +
 +    /* We postpone opening the log file if we are appending, so we can
 +       first truncate the old log file and append to the correct position
 +       there instead.  */
 +    if ((MASTER(cr) || bSepPot) && !bAppendFiles)
 +    {
 +        gmx_log_open(ftp2fn(efLOG, NFILE, fnm), cr,
 +                     !bSepPot, Flags & MD_APPENDFILES, &fplog);
 +        CopyRight(fplog, argv[0]);
 +        please_cite(fplog, "Hess2008b");
 +        please_cite(fplog, "Spoel2005a");
 +        please_cite(fplog, "Lindahl2001a");
 +        please_cite(fplog, "Berendsen95a");
 +    }
 +    else if (!MASTER(cr) && bSepPot)
 +    {
 +        gmx_log_open(ftp2fn(efLOG, NFILE, fnm), cr, !bSepPot, Flags, &fplog);
 +    }
 +    else
 +    {
 +        fplog = NULL;
 +    }
 +
 +    ddxyz[XX] = (int)(realddxyz[XX] + 0.5);
 +    ddxyz[YY] = (int)(realddxyz[YY] + 0.5);
 +    ddxyz[ZZ] = (int)(realddxyz[ZZ] + 0.5);
 +
 +    rc = mdrunner(&hw_opt, fplog, cr, NFILE, fnm, oenv, bVerbose, bCompact,
 +                  nstglobalcomm, ddxyz, dd_node_order, rdd, rconstr,
 +                  dddlb_opt[0], dlb_scale, ddcsx, ddcsy, ddcsz,
 +                  nbpu_opt[0],
 +                  nsteps, nstepout, resetstep,
 +                  nmultisim, repl_ex_nst, repl_ex_nex, repl_ex_seed,
 +                  pforce, cpt_period, max_hours, deviceOptions, Flags);
 +
 +    gmx_finalize_par();
 +
 +    if (MULTIMASTER(cr))
 +    {
 +        thanx(stderr);
 +    }
 +
 +    /* Log file has to be closed in mdrunner if we are appending to it
 +       (fplog not set here) */
 +    if (MASTER(cr) && !bAppendFiles)
 +    {
 +        gmx_log_close(fplog);
 +    }
 +
 +    return rc;
 +}
index 1a2544f6b4ea642a68adb7cace9a2c09b3d38fe8,0000000000000000000000000000000000000000..cb88d382495a64bb19fb799da31a2903dc7a5c5a
mode 100644,000000..100644
--- /dev/null
@@@ -1,795 -1,0 +1,816 @@@
-     epmelblimNO, epmelblimBOX, epmelblimDD, epmelblimNR
 +/* -*- 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 4.6.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-2011, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "smalloc.h"
 +#include "network.h"
 +#include "calcgrid.h"
 +#include "pme.h"
 +#include "vec.h"
 +#include "domdec.h"
 +#include "nbnxn_cuda_data_mgmt.h"
 +#include "force.h"
 +#include "macros.h"
 +#include "md_logging.h"
 +#include "pme_loadbal.h"
 +
 +/* Parameters and setting for one PP-PME setup */
 +typedef struct {
 +    real      rcut_coulomb;    /* Coulomb cut-off                              */
 +    real      rlist;           /* pair-list cut-off                            */
 +    real      rlistlong;       /* LR pair-list cut-off                         */
 +    int       nstcalclr;       /* frequency of evaluating long-range forces for group scheme */
 +    real      spacing;         /* (largest) PME grid spacing                   */
 +    ivec      grid;            /* the PME grid dimensions                      */
 +    real      grid_efficiency; /* ineffiency factor for non-uniform grids <= 1 */
 +    real      ewaldcoeff;      /* the Ewald coefficient                        */
 +    gmx_pme_t pmedata;         /* the data structure used in the PME code      */
 +
 +    int       count;           /* number of times this setup has been timed    */
 +    double    cycles;          /* the fastest time for this setup in cycles    */
 +} pme_setup_t;
 +
 +/* In the initial scan, step by grids that are at least a factor 0.8 coarser */
 +#define PME_LB_GRID_SCALE_FAC  0.8
 +/* In the initial scan, try to skip grids with uneven x/y/z spacing,
 + * checking if the "efficiency" is more than 5% worse than the previous grid.
 + */
 +#define PME_LB_GRID_EFFICIENCY_REL_FAC  1.05
 +/* Rerun up till 12% slower setups than the fastest up till now */
 +#define PME_LB_SLOW_FAC  1.12
 +/* If setups get more than 2% faster, do another round to avoid
 + * choosing a slower setup due to acceleration or fluctuations.
 + */
 +#define PME_LB_ACCEL_TOL 1.02
 +
 +enum {
- { "no", "box size", "domain decompostion" };
++    epmelblimNO, epmelblimBOX, epmelblimDD, epmelblimPMEGRID, epmelblimNR
 +};
 +
 +const char *pmelblim_str[epmelblimNR] =
-                                             int                  pme_order)
++{ "no", "box size", "domain decompostion", "PME grid restriction" };
 +
 +struct pme_load_balancing {
 +    int          nstage;             /* the current maximum number of stages */
 +
 +    real         cut_spacing;        /* the minimum cutoff / PME grid spacing ratio */
 +    real         rcut_vdw;           /* Vdw cutoff (does not change) */
 +    real         rcut_coulomb_start; /* Initial electrostatics cutoff */
 +    int          nstcalclr_start;    /* Initial electrostatics cutoff */
 +    real         rbuf_coulomb;       /* the pairlist buffer size */
 +    real         rbuf_vdw;           /* the pairlist buffer size */
 +    matrix       box_start;          /* the initial simulation box */
 +    int          n;                  /* the count of setup as well as the allocation size */
 +    pme_setup_t *setup;              /* the PME+cutoff setups */
 +    int          cur;                /* the current setup */
 +    int          fastest;            /* fastest setup up till now */
 +    int          start;              /* start of setup range to consider in stage>0 */
 +    int          end;                /* end   of setup range to consider in stage>0 */
 +    int          elimited;           /* was the balancing limited, uses enum above */
 +    int          cutoff_scheme;      /* Verlet or group cut-offs */
 +
 +    int          stage;              /* the current stage */
 +};
 +
 +void pme_loadbal_init(pme_load_balancing_t *pme_lb_p,
 +                      const t_inputrec *ir, matrix box,
 +                      const interaction_const_t *ic,
 +                      gmx_pme_t pmedata)
 +{
 +    pme_load_balancing_t pme_lb;
 +    real                 spm, sp;
 +    int                  d;
 +
 +    snew(pme_lb, 1);
 +
 +    /* Any number of stages >= 2 is supported */
 +    pme_lb->nstage   = 2;
 +
 +    pme_lb->cutoff_scheme = ir->cutoff_scheme;
 +
 +    if (pme_lb->cutoff_scheme == ecutsVERLET)
 +    {
 +        pme_lb->rbuf_coulomb = ic->rlist - ic->rcoulomb;
 +        pme_lb->rbuf_vdw     = pme_lb->rbuf_coulomb;
 +    }
 +    else
 +    {
 +        if (ic->rcoulomb > ic->rlist)
 +        {
 +            pme_lb->rbuf_coulomb = ic->rlistlong - ic->rcoulomb;
 +        }
 +        else
 +        {
 +            pme_lb->rbuf_coulomb = ic->rlist - ic->rcoulomb;
 +        }
 +        if (ic->rvdw > ic->rlist)
 +        {
 +            pme_lb->rbuf_vdw = ic->rlistlong - ic->rvdw;
 +        }
 +        else
 +        {
 +            pme_lb->rbuf_vdw = ic->rlist - ic->rvdw;
 +        }
 +    }
 +
 +    copy_mat(box, pme_lb->box_start);
 +    if (ir->ePBC == epbcXY && ir->nwall == 2)
 +    {
 +        svmul(ir->wall_ewald_zfac, pme_lb->box_start[ZZ], pme_lb->box_start[ZZ]);
 +    }
 +
 +    pme_lb->n = 1;
 +    snew(pme_lb->setup, pme_lb->n);
 +
 +    pme_lb->rcut_vdw              = ic->rvdw;
 +    pme_lb->rcut_coulomb_start    = ir->rcoulomb;
 +    pme_lb->nstcalclr_start       = ir->nstcalclr;
 +
 +    pme_lb->cur                   = 0;
 +    pme_lb->setup[0].rcut_coulomb = ic->rcoulomb;
 +    pme_lb->setup[0].rlist        = ic->rlist;
 +    pme_lb->setup[0].rlistlong    = ic->rlistlong;
 +    pme_lb->setup[0].nstcalclr    = ir->nstcalclr;
 +    pme_lb->setup[0].grid[XX]     = ir->nkx;
 +    pme_lb->setup[0].grid[YY]     = ir->nky;
 +    pme_lb->setup[0].grid[ZZ]     = ir->nkz;
 +    pme_lb->setup[0].ewaldcoeff   = ic->ewaldcoeff;
 +
 +    pme_lb->setup[0].pmedata  = pmedata;
 +
 +    spm = 0;
 +    for (d = 0; d < DIM; d++)
 +    {
 +        sp = norm(pme_lb->box_start[d])/pme_lb->setup[0].grid[d];
 +        if (sp > spm)
 +        {
 +            spm = sp;
 +        }
 +    }
 +    pme_lb->setup[0].spacing = spm;
 +
 +    if (ir->fourier_spacing > 0)
 +    {
 +        pme_lb->cut_spacing = ir->rcoulomb/ir->fourier_spacing;
 +    }
 +    else
 +    {
 +        pme_lb->cut_spacing = ir->rcoulomb/pme_lb->setup[0].spacing;
 +    }
 +
 +    pme_lb->stage = 0;
 +
 +    pme_lb->fastest  = 0;
 +    pme_lb->start    = 0;
 +    pme_lb->end      = 0;
 +    pme_lb->elimited = epmelblimNO;
 +
 +    *pme_lb_p = pme_lb;
 +}
 +
 +static gmx_bool pme_loadbal_increase_cutoff(pme_load_balancing_t pme_lb,
-         /* In parallel we can't have grids smaller than 2*pme_order,
-          * and we would anyhow not gain much speed at these grid sizes.
++                                            int                  pme_order,
++                                            const gmx_domdec_t   *dd)
 +{
 +    pme_setup_t *set;
++    int          npmenodes_x, npmenodes_y;
 +    real         fac, sp;
 +    real         tmpr_coulomb, tmpr_vdw;
 +    int          d;
++    gmx_bool     grid_ok;
 +
 +    /* Try to add a new setup with next larger cut-off to the list */
 +    pme_lb->n++;
 +    srenew(pme_lb->setup, pme_lb->n);
 +    set          = &pme_lb->setup[pme_lb->n-1];
 +    set->pmedata = NULL;
 +
++    get_pme_nnodes(dd, &npmenodes_x, &npmenodes_y);
++
 +    fac = 1;
 +    do
 +    {
++        /* Avoid infinite while loop, which can occur at the minimum grid size.
++         * Note that in practice load balancing will stop before this point.
++         * The factor 2.1 allows for the extreme case in which only grids
++         * of powers of 2 are allowed (the current code supports more grids).
++         */
++        if (fac > 2.1)
++        {
++            pme_lb->n--;
++
++            return FALSE;
++        }
++
 +        fac *= 1.01;
 +        clear_ivec(set->grid);
 +        sp = calc_grid(NULL, pme_lb->box_start,
 +                       fac*pme_lb->setup[pme_lb->cur].spacing,
 +                       &set->grid[XX],
 +                       &set->grid[YY],
 +                       &set->grid[ZZ]);
 +
-         for (d = 0; d < DIM; d++)
-         {
-             if (set->grid[d] <= 2*pme_order)
-             {
-                 pme_lb->n--;
-                 return FALSE;
-             }
-         }
++        /* As here we can't easily check if one of the PME nodes
++         * uses threading, we do a conservative grid check.
++         * This means we can't use pme_order or less grid lines
++         * per PME node along x, which is not a strong restriction.
 +         */
-     while (sp <= 1.001*pme_lb->setup[pme_lb->cur].spacing);
++        gmx_pme_check_restrictions(pme_order,
++                                   set->grid[XX], set->grid[YY], set->grid[ZZ],
++                                   npmenodes_x, npmenodes_y,
++                                   TRUE,
++                                   FALSE,
++                                   &grid_ok);
 +    }
-     sprintf(buf, "step %4s: the %s limited the PME load balancing to a coulomb cut-off of %.3f",
++    while (sp <= 1.001*pme_lb->setup[pme_lb->cur].spacing || !grid_ok);
 +
 +    set->rcut_coulomb = pme_lb->cut_spacing*sp;
 +
 +    if (pme_lb->cutoff_scheme == ecutsVERLET)
 +    {
 +        set->rlist        = set->rcut_coulomb + pme_lb->rbuf_coulomb;
 +        /* We dont use LR lists with Verlet, but this avoids if-statements in further checks */
 +        set->rlistlong    = set->rlist;
 +    }
 +    else
 +    {
 +        tmpr_coulomb          = set->rcut_coulomb + pme_lb->rbuf_coulomb;
 +        tmpr_vdw              = pme_lb->rcut_vdw + pme_lb->rbuf_vdw;
 +        set->rlist            = min(tmpr_coulomb, tmpr_vdw);
 +        set->rlistlong        = max(tmpr_coulomb, tmpr_vdw);
 +
 +        /* Set the long-range update frequency */
 +        if (set->rlist == set->rlistlong)
 +        {
 +            /* No long-range interactions if the short-/long-range cutoffs are identical */
 +            set->nstcalclr = 0;
 +        }
 +        else if (pme_lb->nstcalclr_start == 0 || pme_lb->nstcalclr_start == 1)
 +        {
 +            /* We were not doing long-range before, but now we are since rlist!=rlistlong */
 +            set->nstcalclr = 1;
 +        }
 +        else
 +        {
 +            /* We were already doing long-range interactions from the start */
 +            if (pme_lb->rcut_vdw > pme_lb->rcut_coulomb_start)
 +            {
 +                /* We were originally doing long-range VdW-only interactions.
 +                 * If rvdw is still longer than rcoulomb we keep the original nstcalclr,
 +                 * but if the coulomb cutoff has become longer we should update the long-range
 +                 * part every step.
 +                 */
 +                set->nstcalclr = (tmpr_vdw > tmpr_coulomb) ? pme_lb->nstcalclr_start : 1;
 +            }
 +            else
 +            {
 +                /* We were not doing any long-range interaction from the start,
 +                 * since it is not possible to do twin-range coulomb for the PME interaction.
 +                 */
 +                set->nstcalclr = 1;
 +            }
 +        }
 +    }
 +
 +    set->spacing      = sp;
 +    /* The grid efficiency is the size wrt a grid with uniform x/y/z spacing */
 +    set->grid_efficiency = 1;
 +    for (d = 0; d < DIM; d++)
 +    {
 +        set->grid_efficiency *= (set->grid[d]*sp)/norm(pme_lb->box_start[d]);
 +    }
 +    /* The Ewald coefficient is inversly proportional to the cut-off */
 +    set->ewaldcoeff =
 +        pme_lb->setup[0].ewaldcoeff*pme_lb->setup[0].rcut_coulomb/set->rcut_coulomb;
 +
 +    set->count   = 0;
 +    set->cycles  = 0;
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "PME loadbal: grid %d %d %d, coulomb cutoff %f\n",
 +                set->grid[XX], set->grid[YY], set->grid[ZZ], set->rcut_coulomb);
 +    }
 +    return TRUE;
 +}
 +
 +static void print_grid(FILE *fp_err, FILE *fp_log,
 +                       const char *pre,
 +                       const char *desc,
 +                       const pme_setup_t *set,
 +                       double cycles)
 +{
 +    char buf[STRLEN], buft[STRLEN];
 +
 +    if (cycles >= 0)
 +    {
 +        sprintf(buft, ": %.1f M-cycles", cycles*1e-6);
 +    }
 +    else
 +    {
 +        buft[0] = '\0';
 +    }
 +    sprintf(buf, "%-11s%10s pme grid %d %d %d, coulomb cutoff %.3f%s",
 +            pre,
 +            desc, set->grid[XX], set->grid[YY], set->grid[ZZ], set->rcut_coulomb,
 +            buft);
 +    if (fp_err != NULL)
 +    {
 +        fprintf(fp_err, "\r%s\n", buf);
 +    }
 +    if (fp_log != NULL)
 +    {
 +        fprintf(fp_log, "%s\n", buf);
 +    }
 +}
 +
 +static int pme_loadbal_end(pme_load_balancing_t pme_lb)
 +{
 +    /* In the initial stage only n is set; end is not set yet */
 +    if (pme_lb->end > 0)
 +    {
 +        return pme_lb->end;
 +    }
 +    else
 +    {
 +        return pme_lb->n;
 +    }
 +}
 +
 +static void print_loadbal_limited(FILE *fp_err, FILE *fp_log,
 +                                  gmx_large_int_t step,
 +                                  pme_load_balancing_t pme_lb)
 +{
 +    char buf[STRLEN], sbuf[22];
 +
-                 OK = pme_loadbal_increase_cutoff(pme_lb, ir->pme_order);
++    sprintf(buf, "step %4s: the %s limits the PME load balancing to a coulomb cut-off of %.3f",
 +            gmx_step_str(step, sbuf),
 +            pmelblim_str[pme_lb->elimited],
 +            pme_lb->setup[pme_loadbal_end(pme_lb)-1].rcut_coulomb);
 +    if (fp_err != NULL)
 +    {
 +        fprintf(fp_err, "\r%s\n", buf);
 +    }
 +    if (fp_log != NULL)
 +    {
 +        fprintf(fp_log, "%s\n", buf);
 +    }
 +}
 +
 +static void switch_to_stage1(pme_load_balancing_t pme_lb)
 +{
 +    pme_lb->start = 0;
 +    while (pme_lb->start+1 < pme_lb->n &&
 +           (pme_lb->setup[pme_lb->start].count == 0 ||
 +            pme_lb->setup[pme_lb->start].cycles >
 +            pme_lb->setup[pme_lb->fastest].cycles*PME_LB_SLOW_FAC))
 +    {
 +        pme_lb->start++;
 +    }
 +    while (pme_lb->start > 0 && pme_lb->setup[pme_lb->start-1].cycles == 0)
 +    {
 +        pme_lb->start--;
 +    }
 +
 +    pme_lb->end = pme_lb->n;
 +    if (pme_lb->setup[pme_lb->end-1].count > 0 &&
 +        pme_lb->setup[pme_lb->end-1].cycles >
 +        pme_lb->setup[pme_lb->fastest].cycles*PME_LB_SLOW_FAC)
 +    {
 +        pme_lb->end--;
 +    }
 +
 +    pme_lb->stage = 1;
 +
 +    /* Next we want to choose setup pme_lb->start, but as we will increase
 +     * pme_ln->cur by one right after returning, we subtract 1 here.
 +     */
 +    pme_lb->cur = pme_lb->start - 1;
 +}
 +
 +gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
 +                          t_commrec           *cr,
 +                          FILE                *fp_err,
 +                          FILE                *fp_log,
 +                          t_inputrec          *ir,
 +                          t_state             *state,
 +                          double               cycles,
 +                          interaction_const_t *ic,
 +                          nonbonded_verlet_t  *nbv,
 +                          gmx_pme_t           *pmedata,
 +                          gmx_large_int_t      step)
 +{
 +    gmx_bool     OK;
 +    pme_setup_t *set;
 +    double       cycles_fast;
 +    char         buf[STRLEN], sbuf[22];
 +    real         rtab;
 +    gmx_bool     bUsesSimpleTables = TRUE;
 +
 +    if (pme_lb->stage == pme_lb->nstage)
 +    {
 +        return FALSE;
 +    }
 +
 +    if (PAR(cr))
 +    {
 +        gmx_sumd(1, &cycles, cr);
 +        cycles /= cr->nnodes;
 +    }
 +
 +    set = &pme_lb->setup[pme_lb->cur];
 +    set->count++;
 +
 +    rtab = ir->rlistlong + ir->tabext;
 +
 +    if (set->count % 2 == 1)
 +    {
 +        /* Skip the first cycle, because the first step after a switch
 +         * is much slower due to allocation and/or caching effects.
 +         */
 +        return TRUE;
 +    }
 +
 +    sprintf(buf, "step %4s: ", gmx_step_str(step, sbuf));
 +    print_grid(fp_err, fp_log, buf, "timed with", set, cycles);
 +
 +    if (set->count <= 2)
 +    {
 +        set->cycles = cycles;
 +    }
 +    else
 +    {
 +        if (cycles*PME_LB_ACCEL_TOL < set->cycles &&
 +            pme_lb->stage == pme_lb->nstage - 1)
 +        {
 +            /* The performance went up a lot (due to e.g. DD load balancing).
 +             * Add a stage, keep the minima, but rescan all setups.
 +             */
 +            pme_lb->nstage++;
 +
 +            if (debug)
 +            {
 +                fprintf(debug, "The performance for grid %d %d %d went from %.3f to %.1f M-cycles, this is more than %f\n"
 +                        "Increased the number stages to %d"
 +                        " and ignoring the previous performance\n",
 +                        set->grid[XX], set->grid[YY], set->grid[ZZ],
 +                        cycles*1e-6, set->cycles*1e-6, PME_LB_ACCEL_TOL,
 +                        pme_lb->nstage);
 +            }
 +        }
 +        set->cycles = min(set->cycles, cycles);
 +    }
 +
 +    if (set->cycles < pme_lb->setup[pme_lb->fastest].cycles)
 +    {
 +        pme_lb->fastest = pme_lb->cur;
 +
 +        if (DOMAINDECOMP(cr))
 +        {
 +            /* We found a new fastest setting, ensure that with subsequent
 +             * shorter cut-off's the dynamic load balancing does not make
 +             * the use of the current cut-off impossible. This solution is
 +             * a trade-off, as the PME load balancing and DD domain size
 +             * load balancing can interact in complex ways.
 +             * With the Verlet kernels, DD load imbalance will usually be
 +             * mainly due to bonded interaction imbalance, which will often
 +             * quickly push the domain boundaries beyond the limit for the
 +             * optimal, PME load balanced, cut-off. But it could be that
 +             * better overal performance can be obtained with a slightly
 +             * shorter cut-off and better DD load balancing.
 +             */
 +            change_dd_dlb_cutoff_limit(cr);
 +        }
 +    }
 +    cycles_fast = pme_lb->setup[pme_lb->fastest].cycles;
 +
 +    /* Check in stage 0 if we should stop scanning grids.
 +     * Stop when the time is more than SLOW_FAC longer than the fastest.
 +     */
 +    if (pme_lb->stage == 0 && pme_lb->cur > 0 &&
 +        cycles > pme_lb->setup[pme_lb->fastest].cycles*PME_LB_SLOW_FAC)
 +    {
 +        pme_lb->n = pme_lb->cur + 1;
 +        /* Done with scanning, go to stage 1 */
 +        switch_to_stage1(pme_lb);
 +    }
 +
 +    if (pme_lb->stage == 0)
 +    {
 +        int gridsize_start;
 +
 +        gridsize_start = set->grid[XX]*set->grid[YY]*set->grid[ZZ];
 +
 +        do
 +        {
 +            if (pme_lb->cur+1 < pme_lb->n)
 +            {
 +                /* We had already generated the next setup */
 +                OK = TRUE;
 +            }
 +            else
 +            {
 +                /* Find the next setup */
++                OK = pme_loadbal_increase_cutoff(pme_lb, ir->pme_order, cr->dd);
++
++                if (!OK)
++                {
++                    pme_lb->elimited = epmelblimPMEGRID;
++                }
 +            }
 +
 +            if (OK && ir->ePBC != epbcNONE)
 +            {
 +                OK = (sqr(pme_lb->setup[pme_lb->cur+1].rlistlong)
 +                      <= max_cutoff2(ir->ePBC, state->box));
 +                if (!OK)
 +                {
 +                    pme_lb->elimited = epmelblimBOX;
 +                }
 +            }
 +
 +            if (OK)
 +            {
 +                pme_lb->cur++;
 +
 +                if (DOMAINDECOMP(cr))
 +                {
 +                    OK = change_dd_cutoff(cr, state, ir,
 +                                          pme_lb->setup[pme_lb->cur].rlistlong);
 +                    if (!OK)
 +                    {
 +                        /* Failed: do not use this setup */
 +                        pme_lb->cur--;
 +                        pme_lb->elimited = epmelblimDD;
 +                    }
 +                }
 +            }
 +            if (!OK)
 +            {
 +                /* We hit the upper limit for the cut-off,
 +                 * the setup should not go further than cur.
 +                 */
 +                pme_lb->n = pme_lb->cur + 1;
 +                print_loadbal_limited(fp_err, fp_log, step, pme_lb);
 +                /* Switch to the next stage */
 +                switch_to_stage1(pme_lb);
 +            }
 +        }
 +        while (OK &&
 +               !(pme_lb->setup[pme_lb->cur].grid[XX]*
 +                 pme_lb->setup[pme_lb->cur].grid[YY]*
 +                 pme_lb->setup[pme_lb->cur].grid[ZZ] <
 +                 gridsize_start*PME_LB_GRID_SCALE_FAC
 +                 &&
 +                 pme_lb->setup[pme_lb->cur].grid_efficiency <
 +                 pme_lb->setup[pme_lb->cur-1].grid_efficiency*PME_LB_GRID_EFFICIENCY_REL_FAC));
 +    }
 +
 +    if (pme_lb->stage > 0 && pme_lb->end == 1)
 +    {
 +        pme_lb->cur   = 0;
 +        pme_lb->stage = pme_lb->nstage;
 +    }
 +    else if (pme_lb->stage > 0 && pme_lb->end > 1)
 +    {
 +        /* If stage = nstage-1:
 +         *   scan over all setups, rerunning only those setups
 +         *   which are not much slower than the fastest
 +         * else:
 +         *   use the next setup
 +         */
 +        do
 +        {
 +            pme_lb->cur++;
 +            if (pme_lb->cur == pme_lb->end)
 +            {
 +                pme_lb->stage++;
 +                pme_lb->cur = pme_lb->start;
 +            }
 +        }
 +        while (pme_lb->stage == pme_lb->nstage - 1 &&
 +               pme_lb->setup[pme_lb->cur].count > 0 &&
 +               pme_lb->setup[pme_lb->cur].cycles > cycles_fast*PME_LB_SLOW_FAC);
 +
 +        if (pme_lb->stage == pme_lb->nstage)
 +        {
 +            /* We are done optimizing, use the fastest setup we found */
 +            pme_lb->cur = pme_lb->fastest;
 +        }
 +    }
 +
 +    if (DOMAINDECOMP(cr) && pme_lb->stage > 0)
 +    {
 +        OK = change_dd_cutoff(cr, state, ir, pme_lb->setup[pme_lb->cur].rlistlong);
 +        if (!OK)
 +        {
 +            /* Failsafe solution */
 +            if (pme_lb->cur > 1 && pme_lb->stage == pme_lb->nstage)
 +            {
 +                pme_lb->stage--;
 +            }
 +            pme_lb->fastest  = 0;
 +            pme_lb->start    = 0;
 +            pme_lb->end      = pme_lb->cur;
 +            pme_lb->cur      = pme_lb->start;
 +            pme_lb->elimited = epmelblimDD;
 +            print_loadbal_limited(fp_err, fp_log, step, pme_lb);
 +        }
 +    }
 +
 +    /* Change the Coulomb cut-off and the PME grid */
 +
 +    set = &pme_lb->setup[pme_lb->cur];
 +
 +    ic->rcoulomb   = set->rcut_coulomb;
 +    ic->rlist      = set->rlist;
 +    ic->rlistlong  = set->rlistlong;
 +    ir->nstcalclr  = set->nstcalclr;
 +    ic->ewaldcoeff = set->ewaldcoeff;
 +
 +    bUsesSimpleTables = uses_simple_tables(ir->cutoff_scheme, nbv, 0);
 +    if (pme_lb->cutoff_scheme == ecutsVERLET &&
 +        nbv->grp[0].kernel_type == nbnxnk8x8x8_CUDA)
 +    {
 +        nbnxn_cuda_pme_loadbal_update_param(nbv->cu_nbv, ic);
 +    }
 +    else
 +    {
 +        init_interaction_const_tables(NULL, ic, bUsesSimpleTables,
 +                                      rtab);
 +    }
 +
 +    if (pme_lb->cutoff_scheme == ecutsVERLET && nbv->ngrp > 1)
 +    {
 +        init_interaction_const_tables(NULL, ic, bUsesSimpleTables,
 +                                      rtab);
 +    }
 +
 +    if (cr->duty & DUTY_PME)
 +    {
 +        if (pme_lb->setup[pme_lb->cur].pmedata == NULL)
 +        {
 +            /* Generate a new PME data structure,
 +             * copying part of the old pointers.
 +             */
 +            gmx_pme_reinit(&set->pmedata,
 +                           cr, pme_lb->setup[0].pmedata, ir,
 +                           set->grid);
 +        }
 +        *pmedata = set->pmedata;
 +    }
 +    else
 +    {
 +        /* Tell our PME-only node to switch grid */
 +        gmx_pme_send_switchgrid(cr, set->grid, set->ewaldcoeff);
 +    }
 +
 +    if (debug)
 +    {
 +        print_grid(NULL, debug, "", "switched to", set, -1);
 +    }
 +
 +    if (pme_lb->stage == pme_lb->nstage)
 +    {
 +        print_grid(fp_err, fp_log, "", "optimal", set, -1);
 +    }
 +
 +    return TRUE;
 +}
 +
 +void restart_pme_loadbal(pme_load_balancing_t pme_lb, int n)
 +{
 +    pme_lb->nstage += n;
 +}
 +
 +static int pme_grid_points(const pme_setup_t *setup)
 +{
 +    return setup->grid[XX]*setup->grid[YY]*setup->grid[ZZ];
 +}
 +
 +static real pme_loadbal_rlist(const pme_setup_t *setup)
 +{
 +    /* With the group cut-off scheme we can have twin-range either
 +     * for Coulomb or for VdW, so we need a check here.
 +     * With the Verlet cut-off scheme rlist=rlistlong.
 +     */
 +    if (setup->rcut_coulomb > setup->rlist)
 +    {
 +        return setup->rlistlong;
 +    }
 +    else
 +    {
 +        return setup->rlist;
 +    }
 +}
 +
 +static void print_pme_loadbal_setting(FILE              *fplog,
 +                                      char              *name,
 +                                      const pme_setup_t *setup)
 +{
 +    fprintf(fplog,
 +            "   %-7s %6.3f nm %6.3f nm     %3d %3d %3d   %5.3f nm  %5.3f nm\n",
 +            name,
 +            setup->rcut_coulomb, pme_loadbal_rlist(setup),
 +            setup->grid[XX], setup->grid[YY], setup->grid[ZZ],
 +            setup->spacing, 1/setup->ewaldcoeff);
 +}
 +
 +static void print_pme_loadbal_settings(pme_load_balancing_t pme_lb,
 +                                       t_commrec           *cr,
 +                                       FILE                *fplog,
 +                                       gmx_bool             bNonBondedOnGPU)
 +{
 +    double pp_ratio, grid_ratio;
 +
 +    pp_ratio   = pow(pme_loadbal_rlist(&pme_lb->setup[pme_lb->cur])/pme_loadbal_rlist(&pme_lb->setup[0]), 3.0);
 +    grid_ratio = pme_grid_points(&pme_lb->setup[pme_lb->cur])/
 +        (double)pme_grid_points(&pme_lb->setup[0]);
 +
 +    fprintf(fplog, "\n");
 +    fprintf(fplog, "       P P   -   P M E   L O A D   B A L A N C I N G\n");
 +    fprintf(fplog, "\n");
 +    /* Here we only warn when the optimal setting is the last one */
 +    if (pme_lb->elimited != epmelblimNO &&
 +        pme_lb->cur == pme_loadbal_end(pme_lb)-1)
 +    {
 +        fprintf(fplog, " NOTE: The PP/PME load balancing was limited by the %s,\n",
 +                pmelblim_str[pme_lb->elimited]);
 +        fprintf(fplog, "       you might not have reached a good load balance.\n");
 +        if (pme_lb->elimited == epmelblimDD)
 +        {
 +            fprintf(fplog, "       Try different mdrun -dd settings or lower the -dds value.\n");
 +        }
 +        fprintf(fplog, "\n");
 +    }
 +    fprintf(fplog, " PP/PME load balancing changed the cut-off and PME settings:\n");
 +    fprintf(fplog, "           particle-particle                    PME\n");
 +    fprintf(fplog, "            rcoulomb  rlist            grid      spacing   1/beta\n");
 +    print_pme_loadbal_setting(fplog, "initial", &pme_lb->setup[0]);
 +    print_pme_loadbal_setting(fplog, "final", &pme_lb->setup[pme_lb->cur]);
 +    fprintf(fplog, " cost-ratio           %4.2f             %4.2f\n",
 +            pp_ratio, grid_ratio);
 +    fprintf(fplog, " (note that these numbers concern only part of the total PP and PME load)\n");
 +
 +    if (pp_ratio > 1.5 && !bNonBondedOnGPU)
 +    {
 +        md_print_warn(cr, fplog,
 +                      "NOTE: PME load balancing increased the non-bonded workload by more than 50%%.\n"
 +                      "      For better performance use (more) PME nodes (mdrun -npme),\n"
 +                      "      or in case you are beyond the scaling limit, use less nodes in total.\n");
 +    }
 +    else
 +    {
 +        fprintf(fplog, "\n");
 +    }
 +}
 +
 +void pme_loadbal_done(pme_load_balancing_t pme_lb,
 +                      t_commrec *cr, FILE *fplog,
 +                      gmx_bool bNonBondedOnGPU)
 +{
 +    if (fplog != NULL && (pme_lb->cur > 0 || pme_lb->elimited != epmelblimNO))
 +    {
 +        print_pme_loadbal_settings(pme_lb, cr, fplog, bNonBondedOnGPU);
 +    }
 +
 +    /* TODO: Here we should free all pointers in pme_lb,
 +     * but as it contains pme data structures,
 +     * we need to first make pme.c free all data.
 +     */
 +}
index fb3cecf874f1b13ea60f089ac389acb4d49a57c6,0000000000000000000000000000000000000000..8ff74dbdd5af48281df4335180d9078b0553a7f7
mode 100644,000000..100644
--- /dev/null
@@@ -1,1680 -1,0 +1,1689 @@@
-     int             nsteps_cmdline;
 +/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
 + *
 + *
 + *                This source code is part of
 + *
 + *                 G   R   O   M   A   C   S
 + *
 + *          GROningen MAchine for Chemical Simulations
 + *
 + *                        VERSION 3.2.0
 + * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team,
 + * check out http://www.gromacs.org for more information.
 +
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * If you want to redistribute modifications, please consider that
 + * scientific software is very special. Version control is crucial -
 + * bugs must be traceable. We will be happy to consider code for
 + * inclusion in the official distribution, but derived work must not
 + * be called official GROMACS. Details are found in the README & COPYING
 + * files - if they are missing, get the official version at www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the papers on the package - you can find them in the top README file.
 + *
 + * For more info, check our website at http://www.gromacs.org
 + *
 + * And Hey:
 + * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
 + */
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +#include <signal.h>
 +#include <stdlib.h>
 +#ifdef HAVE_UNISTD_H
 +#include <unistd.h>
 +#endif
 +#include <string.h>
 +#include <assert.h>
 +
 +#include "typedefs.h"
 +#include "smalloc.h"
 +#include "sysstuff.h"
 +#include "statutil.h"
 +#include "mdrun.h"
 +#include "md_logging.h"
 +#include "md_support.h"
 +#include "network.h"
 +#include "pull.h"
 +#include "pull_rotation.h"
 +#include "names.h"
 +#include "disre.h"
 +#include "orires.h"
 +#include "pme.h"
 +#include "mdatoms.h"
 +#include "repl_ex.h"
 +#include "qmmm.h"
 +#include "domdec.h"
 +#include "partdec.h"
 +#include "coulomb.h"
 +#include "constr.h"
 +#include "mvdata.h"
 +#include "checkpoint.h"
 +#include "mtop_util.h"
 +#include "sighandler.h"
 +#include "tpxio.h"
 +#include "txtdump.h"
 +#include "gmx_detect_hardware.h"
 +#include "gmx_omp_nthreads.h"
 +#include "pull_rotation.h"
 +#include "calc_verletbuf.h"
 +#include "../mdlib/nbnxn_search.h"
 +#include "../mdlib/nbnxn_consts.h"
 +#include "gmx_fatal_collective.h"
 +#include "membed.h"
 +#include "macros.h"
 +#include "gmx_omp.h"
 +#include "gmx_thread_affinity.h"
 +
 +#include "gromacs/utility/gmxmpi.h"
 +
 +#ifdef GMX_FAHCORE
 +#include "corewrap.h"
 +#endif
 +
 +#include "gpu_utils.h"
 +#include "nbnxn_cuda_data_mgmt.h"
 +
 +typedef struct {
 +    gmx_integrator_t *func;
 +} gmx_intp_t;
 +
 +/* The array should match the eI array in include/types/enums.h */
 +const gmx_intp_t    integrator[eiNR] = { {do_md}, {do_steep}, {do_cg}, {do_md}, {do_md}, {do_nm}, {do_lbfgs}, {do_tpi}, {do_tpi}, {do_md}, {do_md}, {do_md}};
 +
 +gmx_large_int_t     deform_init_init_step_tpx;
 +matrix              deform_init_box_tpx;
 +#ifdef GMX_THREAD_MPI
 +tMPI_Thread_mutex_t deform_init_box_mutex = TMPI_THREAD_MUTEX_INITIALIZER;
 +#endif
 +
 +
 +#ifdef GMX_THREAD_MPI
 +struct mdrunner_arglist
 +{
 +    gmx_hw_opt_t   *hw_opt;
 +    FILE           *fplog;
 +    t_commrec      *cr;
 +    int             nfile;
 +    const t_filenm *fnm;
 +    output_env_t    oenv;
 +    gmx_bool        bVerbose;
 +    gmx_bool        bCompact;
 +    int             nstglobalcomm;
 +    ivec            ddxyz;
 +    int             dd_node_order;
 +    real            rdd;
 +    real            rconstr;
 +    const char     *dddlb_opt;
 +    real            dlb_scale;
 +    const char     *ddcsx;
 +    const char     *ddcsy;
 +    const char     *ddcsz;
 +    const char     *nbpu_opt;
-                                          int nsteps_cmdline, int nstepout, int resetstep,
++    gmx_large_int_t nsteps_cmdline;
 +    int             nstepout;
 +    int             resetstep;
 +    int             nmultisim;
 +    int             repl_ex_nst;
 +    int             repl_ex_nex;
 +    int             repl_ex_seed;
 +    real            pforce;
 +    real            cpt_period;
 +    real            max_hours;
 +    const char     *deviceOptions;
 +    unsigned long   Flags;
 +    int             ret; /* return value */
 +};
 +
 +
 +/* The function used for spawning threads. Extracts the mdrunner()
 +   arguments from its one argument and calls mdrunner(), after making
 +   a commrec. */
 +static void mdrunner_start_fn(void *arg)
 +{
 +    struct mdrunner_arglist *mda = (struct mdrunner_arglist*)arg;
 +    struct mdrunner_arglist  mc  = *mda; /* copy the arg list to make sure
 +                                            that it's thread-local. This doesn't
 +                                            copy pointed-to items, of course,
 +                                            but those are all const. */
 +    t_commrec *cr;                       /* we need a local version of this */
 +    FILE      *fplog = NULL;
 +    t_filenm  *fnm;
 +
 +    fnm = dup_tfn(mc.nfile, mc.fnm);
 +
 +    cr = init_par_threads(mc.cr);
 +
 +    if (MASTER(cr))
 +    {
 +        fplog = mc.fplog;
 +    }
 +
 +    mda->ret = mdrunner(mc.hw_opt, fplog, cr, mc.nfile, fnm, mc.oenv,
 +                        mc.bVerbose, mc.bCompact, mc.nstglobalcomm,
 +                        mc.ddxyz, mc.dd_node_order, mc.rdd,
 +                        mc.rconstr, mc.dddlb_opt, mc.dlb_scale,
 +                        mc.ddcsx, mc.ddcsy, mc.ddcsz,
 +                        mc.nbpu_opt,
 +                        mc.nsteps_cmdline, mc.nstepout, mc.resetstep,
 +                        mc.nmultisim, mc.repl_ex_nst, mc.repl_ex_nex, mc.repl_ex_seed, mc.pforce,
 +                        mc.cpt_period, mc.max_hours, mc.deviceOptions, mc.Flags);
 +}
 +
 +/* called by mdrunner() to start a specific number of threads (including
 +   the main thread) for thread-parallel runs. This in turn calls mdrunner()
 +   for each thread.
 +   All options besides nthreads are the same as for mdrunner(). */
 +static t_commrec *mdrunner_start_threads(gmx_hw_opt_t *hw_opt,
 +                                         FILE *fplog, t_commrec *cr, int nfile,
 +                                         const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
 +                                         gmx_bool bCompact, int nstglobalcomm,
 +                                         ivec ddxyz, int dd_node_order, real rdd, real rconstr,
 +                                         const char *dddlb_opt, real dlb_scale,
 +                                         const char *ddcsx, const char *ddcsy, const char *ddcsz,
 +                                         const char *nbpu_opt,
-                                     int              nsteps_cmdline,
++                                         gmx_large_int_t nsteps_cmdline,
++                                         int nstepout, int resetstep,
 +                                         int nmultisim, int repl_ex_nst, int repl_ex_nex, int repl_ex_seed,
 +                                         real pforce, real cpt_period, real max_hours,
 +                                         const char *deviceOptions, unsigned long Flags)
 +{
 +    int                      ret;
 +    struct mdrunner_arglist *mda;
 +    t_commrec               *crn; /* the new commrec */
 +    t_filenm                *fnmn;
 +
 +    /* first check whether we even need to start tMPI */
 +    if (hw_opt->nthreads_tmpi < 2)
 +    {
 +        return cr;
 +    }
 +
 +    /* a few small, one-time, almost unavoidable memory leaks: */
 +    snew(mda, 1);
 +    fnmn = dup_tfn(nfile, fnm);
 +
 +    /* fill the data structure to pass as void pointer to thread start fn */
 +    mda->hw_opt         = hw_opt;
 +    mda->fplog          = fplog;
 +    mda->cr             = cr;
 +    mda->nfile          = nfile;
 +    mda->fnm            = fnmn;
 +    mda->oenv           = oenv;
 +    mda->bVerbose       = bVerbose;
 +    mda->bCompact       = bCompact;
 +    mda->nstglobalcomm  = nstglobalcomm;
 +    mda->ddxyz[XX]      = ddxyz[XX];
 +    mda->ddxyz[YY]      = ddxyz[YY];
 +    mda->ddxyz[ZZ]      = ddxyz[ZZ];
 +    mda->dd_node_order  = dd_node_order;
 +    mda->rdd            = rdd;
 +    mda->rconstr        = rconstr;
 +    mda->dddlb_opt      = dddlb_opt;
 +    mda->dlb_scale      = dlb_scale;
 +    mda->ddcsx          = ddcsx;
 +    mda->ddcsy          = ddcsy;
 +    mda->ddcsz          = ddcsz;
 +    mda->nbpu_opt       = nbpu_opt;
 +    mda->nsteps_cmdline = nsteps_cmdline;
 +    mda->nstepout       = nstepout;
 +    mda->resetstep      = resetstep;
 +    mda->nmultisim      = nmultisim;
 +    mda->repl_ex_nst    = repl_ex_nst;
 +    mda->repl_ex_nex    = repl_ex_nex;
 +    mda->repl_ex_seed   = repl_ex_seed;
 +    mda->pforce         = pforce;
 +    mda->cpt_period     = cpt_period;
 +    mda->max_hours      = max_hours;
 +    mda->deviceOptions  = deviceOptions;
 +    mda->Flags          = Flags;
 +
 +    /* now spawn new threads that start mdrunner_start_fn(), while
 +       the main thread returns, we set thread affinity later */
 +    ret = tMPI_Init_fn(TRUE, hw_opt->nthreads_tmpi, TMPI_AFFINITY_NONE,
 +                       mdrunner_start_fn, (void*)(mda) );
 +    if (ret != TMPI_SUCCESS)
 +    {
 +        return NULL;
 +    }
 +
 +    /* make a new comm_rec to reflect the new situation */
 +    crn = init_par_threads(cr);
 +    return crn;
 +}
 +
 +
 +static int get_tmpi_omp_thread_division(const gmx_hw_info_t *hwinfo,
 +                                        const gmx_hw_opt_t  *hw_opt,
 +                                        int                  nthreads_tot,
 +                                        int                  ngpu)
 +{
 +    int nthreads_tmpi;
 +
 +    /* There are no separate PME nodes here, as we ensured in
 +     * check_and_update_hw_opt that nthreads_tmpi>0 with PME nodes
 +     * and a conditional ensures we would not have ended up here.
 +     * Note that separate PME nodes might be switched on later.
 +     */
 +    if (ngpu > 0)
 +    {
 +        nthreads_tmpi = ngpu;
 +        if (nthreads_tot > 0 && nthreads_tot < nthreads_tmpi)
 +        {
 +            nthreads_tmpi = nthreads_tot;
 +        }
 +    }
 +    else if (hw_opt->nthreads_omp > 0)
 +    {
 +        /* Here we could oversubscribe, when we do, we issue a warning later */
 +        nthreads_tmpi = max(1, nthreads_tot/hw_opt->nthreads_omp);
 +    }
 +    else
 +    {
 +        /* TODO choose nthreads_omp based on hardware topology
 +           when we have a hardware topology detection library */
 +        /* In general, when running up to 4 threads, OpenMP should be faster.
 +         * Note: on AMD Bulldozer we should avoid running OpenMP over two dies.
 +         * On Intel>=Nehalem running OpenMP on a single CPU is always faster,
 +         * even on two CPUs it's usually faster (but with many OpenMP threads
 +         * it could be faster not to use HT, currently we always use HT).
 +         * On Nehalem/Westmere we want to avoid running 16 threads over
 +         * two CPUs with HT, so we need a limit<16; thus we use 12.
 +         * A reasonable limit for Intel Sandy and Ivy bridge,
 +         * not knowing the topology, is 16 threads.
 +         */
 +        const int nthreads_omp_always_faster             =  4;
 +        const int nthreads_omp_always_faster_Nehalem     = 12;
 +        const int nthreads_omp_always_faster_SandyBridge = 16;
 +        const int first_model_Nehalem                    = 0x1A;
 +        const int first_model_SandyBridge                = 0x2A;
 +        gmx_bool  bIntel_Family6;
 +
 +        bIntel_Family6 =
 +            (gmx_cpuid_vendor(hwinfo->cpuid_info) == GMX_CPUID_VENDOR_INTEL &&
 +             gmx_cpuid_family(hwinfo->cpuid_info) == 6);
 +
 +        if (nthreads_tot <= nthreads_omp_always_faster ||
 +            (bIntel_Family6 &&
 +             ((gmx_cpuid_model(hwinfo->cpuid_info) >= nthreads_omp_always_faster_Nehalem && nthreads_tot <= nthreads_omp_always_faster_Nehalem) ||
 +              (gmx_cpuid_model(hwinfo->cpuid_info) >= nthreads_omp_always_faster_SandyBridge && nthreads_tot <= nthreads_omp_always_faster_SandyBridge))))
 +        {
 +            /* Use pure OpenMP parallelization */
 +            nthreads_tmpi = 1;
 +        }
 +        else
 +        {
 +            /* Don't use OpenMP parallelization */
 +            nthreads_tmpi = nthreads_tot;
 +        }
 +    }
 +
 +    return nthreads_tmpi;
 +}
 +
 +
 +/* Get the number of threads to use for thread-MPI based on how many
 + * were requested, which algorithms we're using,
 + * and how many particles there are.
 + * At the point we have already called check_and_update_hw_opt.
 + * Thus all options should be internally consistent and consistent
 + * with the hardware, except that ntmpi could be larger than #GPU.
 + */
 +static int get_nthreads_mpi(gmx_hw_info_t *hwinfo,
 +                            gmx_hw_opt_t *hw_opt,
 +                            t_inputrec *inputrec, gmx_mtop_t *mtop,
 +                            const t_commrec *cr,
 +                            FILE *fplog)
 +{
 +    int      nthreads_hw, nthreads_tot_max, nthreads_tmpi, nthreads_new, ngpu;
 +    int      min_atoms_per_mpi_thread;
 +    char    *env;
 +    char     sbuf[STRLEN];
 +    gmx_bool bCanUseGPU;
 +
 +    if (hw_opt->nthreads_tmpi > 0)
 +    {
 +        /* Trivial, return right away */
 +        return hw_opt->nthreads_tmpi;
 +    }
 +
 +    nthreads_hw = hwinfo->nthreads_hw_avail;
 +
 +    /* How many total (#tMPI*#OpenMP) threads can we start? */
 +    if (hw_opt->nthreads_tot > 0)
 +    {
 +        nthreads_tot_max = hw_opt->nthreads_tot;
 +    }
 +    else
 +    {
 +        nthreads_tot_max = nthreads_hw;
 +    }
 +
 +    bCanUseGPU = (inputrec->cutoff_scheme == ecutsVERLET && hwinfo->bCanUseGPU);
 +    if (bCanUseGPU)
 +    {
 +        ngpu = hwinfo->gpu_info.ncuda_dev_use;
 +    }
 +    else
 +    {
 +        ngpu = 0;
 +    }
 +
 +    nthreads_tmpi =
 +        get_tmpi_omp_thread_division(hwinfo, hw_opt, nthreads_tot_max, ngpu);
 +
 +    if (inputrec->eI == eiNM || EI_TPI(inputrec->eI))
 +    {
 +        /* Steps are divided over the nodes iso splitting the atoms */
 +        min_atoms_per_mpi_thread = 0;
 +    }
 +    else
 +    {
 +        if (bCanUseGPU)
 +        {
 +            min_atoms_per_mpi_thread = MIN_ATOMS_PER_GPU;
 +        }
 +        else
 +        {
 +            min_atoms_per_mpi_thread = MIN_ATOMS_PER_MPI_THREAD;
 +        }
 +    }
 +
 +    /* Check if an algorithm does not support parallel simulation.  */
 +    if (nthreads_tmpi != 1 &&
 +        ( inputrec->eI == eiLBFGS ||
 +          inputrec->coulombtype == eelEWALD ) )
 +    {
 +        nthreads_tmpi = 1;
 +
 +        md_print_warn(cr, fplog, "The integration or electrostatics algorithm doesn't support parallel runs. Using a single thread-MPI thread.\n");
 +        if (hw_opt->nthreads_tmpi > nthreads_tmpi)
 +        {
 +            gmx_fatal(FARGS, "You asked for more than 1 thread-MPI thread, but an algorithm doesn't support that");
 +        }
 +    }
 +    else if (mtop->natoms/nthreads_tmpi < min_atoms_per_mpi_thread)
 +    {
 +        /* the thread number was chosen automatically, but there are too many
 +           threads (too few atoms per thread) */
 +        nthreads_new = max(1, mtop->natoms/min_atoms_per_mpi_thread);
 +
 +        /* Avoid partial use of Hyper-Threading */
 +        if (gmx_cpuid_x86_smt(hwinfo->cpuid_info) == GMX_CPUID_X86_SMT_ENABLED &&
 +            nthreads_new > nthreads_hw/2 && nthreads_new < nthreads_hw)
 +        {
 +            nthreads_new = nthreads_hw/2;
 +        }
 +
 +        /* Avoid large prime numbers in the thread count */
 +        if (nthreads_new >= 6)
 +        {
 +            /* Use only 6,8,10 with additional factors of 2 */
 +            int fac;
 +
 +            fac = 2;
 +            while (3*fac*2 <= nthreads_new)
 +            {
 +                fac *= 2;
 +            }
 +
 +            nthreads_new = (nthreads_new/fac)*fac;
 +        }
 +        else
 +        {
 +            /* Avoid 5 */
 +            if (nthreads_new == 5)
 +            {
 +                nthreads_new = 4;
 +            }
 +        }
 +
 +        nthreads_tmpi = nthreads_new;
 +
 +        fprintf(stderr, "\n");
 +        fprintf(stderr, "NOTE: Parallelization is limited by the small number of atoms,\n");
 +        fprintf(stderr, "      only starting %d thread-MPI threads.\n", nthreads_tmpi);
 +        fprintf(stderr, "      You can use the -nt and/or -ntmpi option to optimize the number of threads.\n\n");
 +    }
 +
 +    return nthreads_tmpi;
 +}
 +#endif /* GMX_THREAD_MPI */
 +
 +
 +/* Environment variable for setting nstlist */
 +static const char*  NSTLIST_ENVVAR          =  "GMX_NSTLIST";
 +/* Try to increase nstlist when using a GPU with nstlist less than this */
 +static const int    NSTLIST_GPU_ENOUGH      = 20;
 +/* Increase nstlist until the non-bonded cost increases more than this factor */
 +static const float  NBNXN_GPU_LIST_OK_FAC   = 1.25;
 +/* Don't increase nstlist beyond a non-bonded cost increases of this factor */
 +static const float  NBNXN_GPU_LIST_MAX_FAC  = 1.40;
 +
 +/* Try to increase nstlist when running on a GPU */
 +static void increase_nstlist(FILE *fp, t_commrec *cr,
 +                             t_inputrec *ir, const gmx_mtop_t *mtop, matrix box)
 +{
 +    char                  *env;
 +    int                    nstlist_orig, nstlist_prev;
 +    verletbuf_list_setup_t ls;
 +    real                   rlist_inc, rlist_ok, rlist_max, rlist_new, rlist_prev;
 +    int                    i;
 +    t_state                state_tmp;
 +    gmx_bool               bBox, bDD, bCont;
 +    const char            *nstl_fmt = "\nFor optimal performance with a GPU nstlist (now %d) should be larger.\nThe optimum depends on your CPU and GPU resources.\nYou might want to try several nstlist values.\n";
 +    const char            *vbd_err  = "Can not increase nstlist for GPU run because verlet-buffer-drift is not set or used";
 +    const char            *box_err  = "Can not increase nstlist for GPU run because the box is too small";
 +    const char            *dd_err   = "Can not increase nstlist for GPU run because of domain decomposition limitations";
 +    char                   buf[STRLEN];
 +
 +    /* Number of + nstlist alternative values to try when switching  */
 +    const int nstl[] = { 20, 25, 40, 50 };
 +#define NNSTL  sizeof(nstl)/sizeof(nstl[0])
 +
 +    env = getenv(NSTLIST_ENVVAR);
 +    if (env == NULL)
 +    {
 +        if (fp != NULL)
 +        {
 +            fprintf(fp, nstl_fmt, ir->nstlist);
 +        }
 +    }
 +
 +    if (ir->verletbuf_drift == 0)
 +    {
 +        gmx_fatal(FARGS, "You are using an old tpr file with a GPU, please generate a new tpr file with an up to date version of grompp");
 +    }
 +
 +    if (ir->verletbuf_drift < 0)
 +    {
 +        if (MASTER(cr))
 +        {
 +            fprintf(stderr, "%s\n", vbd_err);
 +        }
 +        if (fp != NULL)
 +        {
 +            fprintf(fp, "%s\n", vbd_err);
 +        }
 +
 +        return;
 +    }
 +
 +    nstlist_orig = ir->nstlist;
 +    if (env != NULL)
 +    {
 +        sprintf(buf, "Getting nstlist from environment variable GMX_NSTLIST=%s", env);
 +        if (MASTER(cr))
 +        {
 +            fprintf(stderr, "%s\n", buf);
 +        }
 +        if (fp != NULL)
 +        {
 +            fprintf(fp, "%s\n", buf);
 +        }
 +        sscanf(env, "%d", &ir->nstlist);
 +    }
 +
 +    verletbuf_get_list_setup(TRUE, &ls);
 +
 +    /* Allow rlist to make the list double the size of the cut-off sphere */
 +    rlist_inc = nbnxn_get_rlist_effective_inc(NBNXN_GPU_CLUSTER_SIZE, mtop->natoms/det(box));
 +    rlist_ok  = (max(ir->rvdw, ir->rcoulomb) + rlist_inc)*pow(NBNXN_GPU_LIST_OK_FAC, 1.0/3.0) - rlist_inc;
 +    rlist_max = (max(ir->rvdw, ir->rcoulomb) + rlist_inc)*pow(NBNXN_GPU_LIST_MAX_FAC, 1.0/3.0) - rlist_inc;
 +    if (debug)
 +    {
 +        fprintf(debug, "GPU nstlist tuning: rlist_inc %.3f rlist_max %.3f\n",
 +                rlist_inc, rlist_max);
 +    }
 +
 +    i            = 0;
 +    nstlist_prev = nstlist_orig;
 +    rlist_prev   = ir->rlist;
 +    do
 +    {
 +        if (env == NULL)
 +        {
 +            ir->nstlist = nstl[i];
 +        }
 +
 +        /* Set the pair-list buffer size in ir */
 +        calc_verlet_buffer_size(mtop, det(box), ir, ir->verletbuf_drift, &ls,
 +                                NULL, &rlist_new);
 +
 +        /* Does rlist fit in the box? */
 +        bBox = (sqr(rlist_new) < max_cutoff2(ir->ePBC, box));
 +        bDD  = TRUE;
 +        if (bBox && DOMAINDECOMP(cr))
 +        {
 +            /* Check if rlist fits in the domain decomposition */
 +            if (inputrec2nboundeddim(ir) < DIM)
 +            {
 +                gmx_incons("Changing nstlist with domain decomposition and unbounded dimensions is not implemented yet");
 +            }
 +            copy_mat(box, state_tmp.box);
 +            bDD = change_dd_cutoff(cr, &state_tmp, ir, rlist_new);
 +        }
 +
 +        bCont = FALSE;
 +
 +        if (env == NULL)
 +        {
 +            if (bBox && bDD && rlist_new <= rlist_max)
 +            {
 +                /* Increase nstlist */
 +                nstlist_prev = ir->nstlist;
 +                rlist_prev   = rlist_new;
 +                bCont        = (i+1 < NNSTL && rlist_new < rlist_ok);
 +            }
 +            else
 +            {
 +                /* Stick with the previous nstlist */
 +                ir->nstlist = nstlist_prev;
 +                rlist_new   = rlist_prev;
 +                bBox        = TRUE;
 +                bDD         = TRUE;
 +            }
 +        }
 +
 +        i++;
 +    }
 +    while (bCont);
 +
 +    if (!bBox || !bDD)
 +    {
 +        gmx_warning(!bBox ? box_err : dd_err);
 +        if (fp != NULL)
 +        {
 +            fprintf(fp, "\n%s\n", bBox ? box_err : dd_err);
 +        }
 +        ir->nstlist = nstlist_orig;
 +    }
 +    else if (ir->nstlist != nstlist_orig || rlist_new != ir->rlist)
 +    {
 +        sprintf(buf, "Changing nstlist from %d to %d, rlist from %g to %g",
 +                nstlist_orig, ir->nstlist,
 +                ir->rlist, rlist_new);
 +        if (MASTER(cr))
 +        {
 +            fprintf(stderr, "%s\n\n", buf);
 +        }
 +        if (fp != NULL)
 +        {
 +            fprintf(fp, "%s\n\n", buf);
 +        }
 +        ir->rlist     = rlist_new;
 +        ir->rlistlong = rlist_new;
 +    }
 +}
 +
 +static void prepare_verlet_scheme(FILE             *fplog,
 +                                  gmx_hw_info_t    *hwinfo,
 +                                  t_commrec        *cr,
 +                                  gmx_hw_opt_t     *hw_opt,
 +                                  const char       *nbpu_opt,
 +                                  t_inputrec       *ir,
 +                                  const gmx_mtop_t *mtop,
 +                                  matrix            box,
 +                                  gmx_bool         *bUseGPU)
 +{
 +    /* Here we only check for GPU usage on the MPI master process,
 +     * as here we don't know how many GPUs we will use yet.
 +     * We check for a GPU on all processes later.
 +     */
 +    *bUseGPU = hwinfo->bCanUseGPU || (getenv("GMX_EMULATE_GPU") != NULL);
 +
 +    if (ir->verletbuf_drift > 0)
 +    {
 +        /* Update the Verlet buffer size for the current run setup */
 +        verletbuf_list_setup_t ls;
 +        real                   rlist_new;
 +
 +        /* Here we assume CPU acceleration is on. But as currently
 +         * calc_verlet_buffer_size gives the same results for 4x8 and 4x4
 +         * and 4x2 gives a larger buffer than 4x4, this is ok.
 +         */
 +        verletbuf_get_list_setup(*bUseGPU, &ls);
 +
 +        calc_verlet_buffer_size(mtop, det(box), ir,
 +                                ir->verletbuf_drift, &ls,
 +                                NULL, &rlist_new);
 +        if (rlist_new != ir->rlist)
 +        {
 +            if (fplog != NULL)
 +            {
 +                fprintf(fplog, "\nChanging rlist from %g to %g for non-bonded %dx%d atom kernels\n\n",
 +                        ir->rlist, rlist_new,
 +                        ls.cluster_size_i, ls.cluster_size_j);
 +            }
 +            ir->rlist     = rlist_new;
 +            ir->rlistlong = rlist_new;
 +        }
 +    }
 +
 +    /* With GPU or emulation we should check nstlist for performance */
 +    if ((EI_DYNAMICS(ir->eI) &&
 +         *bUseGPU &&
 +         ir->nstlist < NSTLIST_GPU_ENOUGH) ||
 +        getenv(NSTLIST_ENVVAR) != NULL)
 +    {
 +        /* Choose a better nstlist */
 +        increase_nstlist(fplog, cr, ir, mtop, box);
 +    }
 +}
 +
 +static void convert_to_verlet_scheme(FILE *fplog,
 +                                     t_inputrec *ir,
 +                                     gmx_mtop_t *mtop, real box_vol)
 +{
 +    char *conv_mesg = "Converting input file with group cut-off scheme to the Verlet cut-off scheme";
 +
 +    md_print_warn(NULL, fplog, "%s\n", conv_mesg);
 +
 +    ir->cutoff_scheme   = ecutsVERLET;
 +    ir->verletbuf_drift = 0.005;
 +
 +    if (ir->rcoulomb != ir->rvdw)
 +    {
 +        gmx_fatal(FARGS, "The VdW and Coulomb cut-offs are different, whereas the Verlet scheme only supports equal cut-offs");
 +    }
 +
 +    if (ir->vdwtype == evdwUSER || EEL_USER(ir->coulombtype))
 +    {
 +        gmx_fatal(FARGS, "User non-bonded potentials are not (yet) supported with the Verlet scheme");
 +    }
 +    else if (EVDW_SWITCHED(ir->vdwtype) || EEL_SWITCHED(ir->coulombtype))
 +    {
 +        md_print_warn(NULL, fplog, "Converting switched or shifted interactions to a shifted potential (without force shift), this will lead to slightly different interaction potentials");
 +
 +        if (EVDW_SWITCHED(ir->vdwtype))
 +        {
 +            ir->vdwtype = evdwCUT;
 +        }
 +        if (EEL_SWITCHED(ir->coulombtype))
 +        {
 +            if (EEL_FULL(ir->coulombtype))
 +            {
 +                /* With full electrostatic only PME can be switched */
 +                ir->coulombtype = eelPME;
 +            }
 +            else
 +            {
 +                md_print_warn(NULL, fplog, "NOTE: Replacing %s electrostatics with reaction-field with epsilon-rf=inf\n", eel_names[ir->coulombtype]);
 +                ir->coulombtype = eelRF;
 +                ir->epsilon_rf  = 0.0;
 +            }
 +        }
 +
 +        /* We set the target energy drift to a small number.
 +         * Note that this is only for testing. For production the user
 +         * should think about this and set the mdp options.
 +         */
 +        ir->verletbuf_drift = 1e-4;
 +    }
 +
 +    if (inputrec2nboundeddim(ir) != 3)
 +    {
 +        gmx_fatal(FARGS, "Can only convert old tpr files to the Verlet cut-off scheme with 3D pbc");
 +    }
 +
 +    if (ir->efep != efepNO || ir->implicit_solvent != eisNO)
 +    {
 +        gmx_fatal(FARGS, "Will not convert old tpr files to the Verlet cut-off scheme with free-energy calculations or implicit solvent");
 +    }
 +
 +    if (EI_DYNAMICS(ir->eI) && !(EI_MD(ir->eI) && ir->etc == etcNO))
 +    {
 +        verletbuf_list_setup_t ls;
 +
 +        verletbuf_get_list_setup(FALSE, &ls);
 +        calc_verlet_buffer_size(mtop, box_vol, ir, ir->verletbuf_drift, &ls,
 +                                NULL, &ir->rlist);
 +    }
 +    else
 +    {
 +        ir->verletbuf_drift = -1;
 +        ir->rlist           = 1.05*max(ir->rvdw, ir->rcoulomb);
 +    }
 +
 +    gmx_mtop_remove_chargegroups(mtop);
 +}
 +
 +static void check_and_update_hw_opt(gmx_hw_opt_t *hw_opt,
 +                                    int           cutoff_scheme,
 +                                    gmx_bool      bIsSimMaster)
 +{
 +    gmx_omp_nthreads_read_env(&hw_opt->nthreads_omp, bIsSimMaster);
 +
 +#ifndef GMX_THREAD_MPI
 +    if (hw_opt->nthreads_tot > 0)
 +    {
 +        gmx_fatal(FARGS, "Setting the total number of threads is only supported with thread-MPI and Gromacs was compiled without thread-MPI");
 +    }
 +    if (hw_opt->nthreads_tmpi > 0)
 +    {
 +        gmx_fatal(FARGS, "Setting the number of thread-MPI threads is only supported with thread-MPI and Gromacs was compiled without thread-MPI");
 +    }
 +#endif
 +
 +    if (hw_opt->nthreads_tot > 0 && hw_opt->nthreads_omp_pme <= 0)
 +    {
 +        /* We have the same number of OpenMP threads for PP and PME processes,
 +         * thus we can perform several consistency checks.
 +         */
 +        if (hw_opt->nthreads_tmpi > 0 &&
 +            hw_opt->nthreads_omp > 0 &&
 +            hw_opt->nthreads_tot != hw_opt->nthreads_tmpi*hw_opt->nthreads_omp)
 +        {
 +            gmx_fatal(FARGS, "The total number of threads requested (%d) does not match the thread-MPI threads (%d) times the OpenMP threads (%d) requested",
 +                      hw_opt->nthreads_tot, hw_opt->nthreads_tmpi, hw_opt->nthreads_omp);
 +        }
 +
 +        if (hw_opt->nthreads_tmpi > 0 &&
 +            hw_opt->nthreads_tot % hw_opt->nthreads_tmpi != 0)
 +        {
 +            gmx_fatal(FARGS, "The total number of threads requested (%d) is not divisible by the number of thread-MPI threads requested (%d)",
 +                      hw_opt->nthreads_tot, hw_opt->nthreads_tmpi);
 +        }
 +
 +        if (hw_opt->nthreads_omp > 0 &&
 +            hw_opt->nthreads_tot % hw_opt->nthreads_omp != 0)
 +        {
 +            gmx_fatal(FARGS, "The total number of threads requested (%d) is not divisible by the number of OpenMP threads requested (%d)",
 +                      hw_opt->nthreads_tot, hw_opt->nthreads_omp);
 +        }
 +
 +        if (hw_opt->nthreads_tmpi > 0 &&
 +            hw_opt->nthreads_omp <= 0)
 +        {
 +            hw_opt->nthreads_omp = hw_opt->nthreads_tot/hw_opt->nthreads_tmpi;
 +        }
 +    }
 +
 +#ifndef GMX_OPENMP
 +    if (hw_opt->nthreads_omp > 1)
 +    {
 +        gmx_fatal(FARGS, "OpenMP threads are requested, but Gromacs was compiled without OpenMP support");
 +    }
 +#endif
 +
 +    if (cutoff_scheme == ecutsGROUP)
 +    {
 +        /* We only have OpenMP support for PME only nodes */
 +        if (hw_opt->nthreads_omp > 1)
 +        {
 +            gmx_fatal(FARGS, "OpenMP threads have been requested with cut-off scheme %s, but these are only supported with cut-off scheme %s",
 +                      ecutscheme_names[cutoff_scheme],
 +                      ecutscheme_names[ecutsVERLET]);
 +        }
 +        hw_opt->nthreads_omp = 1;
 +    }
 +
 +    if (hw_opt->nthreads_omp_pme > 0 && hw_opt->nthreads_omp <= 0)
 +    {
 +        gmx_fatal(FARGS, "You need to specify -ntomp in addition to -ntomp_pme");
 +    }
 +
 +    if (hw_opt->nthreads_tot == 1)
 +    {
 +        hw_opt->nthreads_tmpi = 1;
 +
 +        if (hw_opt->nthreads_omp > 1)
 +        {
 +            gmx_fatal(FARGS, "You requested %d OpenMP threads with %d total threads",
 +                      hw_opt->nthreads_tmpi, hw_opt->nthreads_tot);
 +        }
 +        hw_opt->nthreads_omp = 1;
 +    }
 +
 +    if (hw_opt->nthreads_omp_pme <= 0 && hw_opt->nthreads_omp > 0)
 +    {
 +        hw_opt->nthreads_omp_pme = hw_opt->nthreads_omp;
 +    }
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "hw_opt: nt %d ntmpi %d ntomp %d ntomp_pme %d gpu_id '%s'\n",
 +                hw_opt->nthreads_tot,
 +                hw_opt->nthreads_tmpi,
 +                hw_opt->nthreads_omp,
 +                hw_opt->nthreads_omp_pme,
 +                hw_opt->gpu_id != NULL ? hw_opt->gpu_id : "");
 +
 +    }
 +}
 +
 +
 +/* Override the value in inputrec with value passed on the command line (if any) */
 +static void override_nsteps_cmdline(FILE            *fplog,
-             sprintf(stmp, "Overriding nsteps with value passed on the command line: %d steps, %.3f ps",
-                     nsteps_cmdline, nsteps_cmdline*ir->delta_t);
++                                    gmx_large_int_t  nsteps_cmdline,
 +                                    t_inputrec      *ir,
 +                                    const t_commrec *cr)
 +{
++    char sbuf[STEPSTRSIZE];
++
 +    assert(ir);
 +    assert(cr);
 +
 +    /* override with anything else than the default -2 */
 +    if (nsteps_cmdline > -2)
 +    {
 +        char stmp[STRLEN];
 +
 +        ir->nsteps = nsteps_cmdline;
 +        if (EI_DYNAMICS(ir->eI))
 +        {
-             sprintf(stmp, "Overriding nsteps with value passed on the command line: %d steps",
-                     nsteps_cmdline);
++            sprintf(stmp, "Overriding nsteps with value passed on the command line: %s steps, %.3f ps",
++                    gmx_step_str(nsteps_cmdline, sbuf),
++                    nsteps_cmdline*ir->delta_t);
 +        }
 +        else
 +        {
-              int nsteps_cmdline, int nstepout, int resetstep,
++            sprintf(stmp, "Overriding nsteps with value passed on the command line: %s steps",
++                    gmx_step_str(nsteps_cmdline, sbuf));
 +        }
 +
 +        md_print_warn(cr, fplog, "%s\n", stmp);
 +    }
 +}
 +
 +/* Data structure set by SIMMASTER which needs to be passed to all nodes
 + * before the other nodes have read the tpx file and called gmx_detect_hardware.
 + */
 +typedef struct {
 +    int      cutoff_scheme; /* The cutoff scheme from inputrec_t */
 +    gmx_bool bUseGPU;       /* Use GPU or GPU emulation          */
 +} master_inf_t;
 +
 +int mdrunner(gmx_hw_opt_t *hw_opt,
 +             FILE *fplog, t_commrec *cr, int nfile,
 +             const t_filenm fnm[], const output_env_t oenv, gmx_bool bVerbose,
 +             gmx_bool bCompact, int nstglobalcomm,
 +             ivec ddxyz, int dd_node_order, real rdd, real rconstr,
 +             const char *dddlb_opt, real dlb_scale,
 +             const char *ddcsx, const char *ddcsy, const char *ddcsz,
 +             const char *nbpu_opt,
++             gmx_large_int_t nsteps_cmdline, int nstepout, int resetstep,
 +             int nmultisim, int repl_ex_nst, int repl_ex_nex,
 +             int repl_ex_seed, real pforce, real cpt_period, real max_hours,
 +             const char *deviceOptions, unsigned long Flags)
 +{
 +    gmx_bool        bForceUseGPU, bTryUseGPU;
 +    double          nodetime = 0, realtime;
 +    t_inputrec     *inputrec;
 +    t_state        *state = NULL;
 +    matrix          box;
 +    gmx_ddbox_t     ddbox = {0};
 +    int             npme_major, npme_minor;
 +    real            tmpr1, tmpr2;
 +    t_nrnb         *nrnb;
 +    gmx_mtop_t     *mtop       = NULL;
 +    t_mdatoms      *mdatoms    = NULL;
 +    t_forcerec     *fr         = NULL;
 +    t_fcdata       *fcd        = NULL;
 +    real            ewaldcoeff = 0;
 +    gmx_pme_t      *pmedata    = NULL;
 +    gmx_vsite_t    *vsite      = NULL;
 +    gmx_constr_t    constr;
 +    int             i, m, nChargePerturbed = -1, status, nalloc;
 +    char           *gro;
 +    gmx_wallcycle_t wcycle;
 +    gmx_bool        bReadRNG, bReadEkin;
 +    int             list;
 +    gmx_runtime_t   runtime;
 +    int             rc;
 +    gmx_large_int_t reset_counters;
 +    gmx_edsam_t     ed           = NULL;
 +    t_commrec      *cr_old       = cr;
 +    int             nthreads_pme = 1;
 +    int             nthreads_pp  = 1;
 +    gmx_membed_t    membed       = NULL;
 +    gmx_hw_info_t  *hwinfo       = NULL;
 +    master_inf_t    minf         = {-1, FALSE};
 +
 +    /* CAUTION: threads may be started later on in this function, so
 +       cr doesn't reflect the final parallel state right now */
 +    snew(inputrec, 1);
 +    snew(mtop, 1);
 +
 +    if (Flags & MD_APPENDFILES)
 +    {
 +        fplog = NULL;
 +    }
 +
 +    bForceUseGPU = (strncmp(nbpu_opt, "gpu", 3) == 0);
 +    bTryUseGPU   = (strncmp(nbpu_opt, "auto", 4) == 0) || bForceUseGPU;
 +
 +    snew(state, 1);
 +    if (SIMMASTER(cr))
 +    {
 +        /* Read (nearly) all data required for the simulation */
 +        read_tpx_state(ftp2fn(efTPX, nfile, fnm), inputrec, state, NULL, mtop);
 +
 +        if (inputrec->cutoff_scheme != ecutsVERLET &&
 +            ((Flags & MD_TESTVERLET) || getenv("GMX_VERLET_SCHEME") != NULL))
 +        {
 +            convert_to_verlet_scheme(fplog, inputrec, mtop, det(state->box));
 +        }
 +
 +        /* Detect hardware, gather information. With tMPI only thread 0 does it
 +         * and after threads are started broadcasts hwinfo around. */
 +        snew(hwinfo, 1);
 +        gmx_detect_hardware(fplog, hwinfo, cr,
 +                            bForceUseGPU, bTryUseGPU, hw_opt->gpu_id);
 +
 +        minf.cutoff_scheme = inputrec->cutoff_scheme;
 +        minf.bUseGPU       = FALSE;
 +
 +        if (inputrec->cutoff_scheme == ecutsVERLET)
 +        {
 +            prepare_verlet_scheme(fplog, hwinfo, cr, hw_opt, nbpu_opt,
 +                                  inputrec, mtop, state->box,
 +                                  &minf.bUseGPU);
 +        }
 +        else if (hwinfo->bCanUseGPU)
 +        {
 +            md_print_warn(cr, fplog,
 +                          "NOTE: GPU(s) found, but the current simulation can not use GPUs\n"
 +                          "      To use a GPU, set the mdp option: cutoff-scheme = Verlet\n"
 +                          "      (for quick performance testing you can use the -testverlet option)\n");
 +
 +            if (bForceUseGPU)
 +            {
 +                gmx_fatal(FARGS, "GPU requested, but can't be used without cutoff-scheme=Verlet");
 +            }
 +        }
 +    }
 +#ifndef GMX_THREAD_MPI
 +    if (PAR(cr))
 +    {
 +        gmx_bcast_sim(sizeof(minf), &minf, cr);
 +    }
 +#endif
 +    if (minf.bUseGPU && cr->npmenodes == -1)
 +    {
 +        /* Don't automatically use PME-only nodes with GPUs */
 +        cr->npmenodes = 0;
 +    }
 +
 +    /* Check for externally set OpenMP affinity and turn off internal
 +     * pinning if any is found. We need to do this check early to tell
 +     * thread-MPI whether it should do pinning when spawning threads.
 +     * TODO: the above no longer holds, we should move these checks down
 +     */
 +    gmx_omp_check_thread_affinity(fplog, cr, hw_opt);
 +
 +#ifdef GMX_THREAD_MPI
 +    /* With thread-MPI inputrec is only set here on the master thread */
 +    if (SIMMASTER(cr))
 +#endif
 +    {
 +        check_and_update_hw_opt(hw_opt, minf.cutoff_scheme, SIMMASTER(cr));
 +
 +#ifdef GMX_THREAD_MPI
 +        /* Early check for externally set process affinity. Can't do over all
 +         * MPI processes because hwinfo is not available everywhere, but with
 +         * thread-MPI it's needed as pinning might get turned off which needs
 +         * to be known before starting thread-MPI. */
 +        gmx_check_thread_affinity_set(fplog,
 +                                      NULL,
 +                                      hw_opt, hwinfo->nthreads_hw_avail, FALSE);
 +#endif
 +
 +#ifdef GMX_THREAD_MPI
 +        if (cr->npmenodes > 0 && hw_opt->nthreads_tmpi <= 0)
 +        {
 +            gmx_fatal(FARGS, "You need to explicitly specify the number of MPI threads (-ntmpi) when using separate PME nodes");
 +        }
 +#endif
 +
 +        if (hw_opt->nthreads_omp_pme != hw_opt->nthreads_omp &&
 +            cr->npmenodes <= 0)
 +        {
 +            gmx_fatal(FARGS, "You need to explicitly specify the number of PME nodes (-npme) when using different number of OpenMP threads for PP and PME nodes");
 +        }
 +    }
 +
 +#ifdef GMX_THREAD_MPI
 +    if (SIMMASTER(cr))
 +    {
 +        /* NOW the threads will be started: */
 +        hw_opt->nthreads_tmpi = get_nthreads_mpi(hwinfo,
 +                                                 hw_opt,
 +                                                 inputrec, mtop,
 +                                                 cr, fplog);
 +        if (hw_opt->nthreads_tot > 0 && hw_opt->nthreads_omp <= 0)
 +        {
 +            hw_opt->nthreads_omp = hw_opt->nthreads_tot/hw_opt->nthreads_tmpi;
 +        }
 +
 +        if (hw_opt->nthreads_tmpi > 1)
 +        {
 +            /* now start the threads. */
 +            cr = mdrunner_start_threads(hw_opt, fplog, cr_old, nfile, fnm,
 +                                        oenv, bVerbose, bCompact, nstglobalcomm,
 +                                        ddxyz, dd_node_order, rdd, rconstr,
 +                                        dddlb_opt, dlb_scale, ddcsx, ddcsy, ddcsz,
 +                                        nbpu_opt,
 +                                        nsteps_cmdline, nstepout, resetstep, nmultisim,
 +                                        repl_ex_nst, repl_ex_nex, repl_ex_seed, pforce,
 +                                        cpt_period, max_hours, deviceOptions,
 +                                        Flags);
 +            /* the main thread continues here with a new cr. We don't deallocate
 +               the old cr because other threads may still be reading it. */
 +            if (cr == NULL)
 +            {
 +                gmx_comm("Failed to spawn threads");
 +            }
 +        }
 +    }
 +#endif
 +    /* END OF CAUTION: cr is now reliable */
 +
 +    /* g_membed initialisation *
 +     * Because we change the mtop, init_membed is called before the init_parallel *
 +     * (in case we ever want to make it run in parallel) */
 +    if (opt2bSet("-membed", nfile, fnm))
 +    {
 +        if (MASTER(cr))
 +        {
 +            fprintf(stderr, "Initializing membed");
 +        }
 +        membed = init_membed(fplog, nfile, fnm, mtop, inputrec, state, cr, &cpt_period);
 +    }
 +
 +    if (PAR(cr))
 +    {
 +        /* now broadcast everything to the non-master nodes/threads: */
 +        init_parallel(fplog, cr, inputrec, mtop);
 +
 +        /* This check needs to happen after get_nthreads_mpi() */
 +        if (inputrec->cutoff_scheme == ecutsVERLET && (Flags & MD_PARTDEC))
 +        {
 +            gmx_fatal_collective(FARGS, cr, NULL,
 +                                 "The Verlet cut-off scheme is not supported with particle decomposition.\n"
 +                                 "You can achieve the same effect as particle decomposition by running in parallel using only OpenMP threads.");
 +        }
 +    }
 +    if (fplog != NULL)
 +    {
 +        pr_inputrec(fplog, 0, "Input Parameters", inputrec, FALSE);
 +    }
 +
 +#if defined GMX_THREAD_MPI
 +    /* With tMPI we detected on thread 0 and we'll just pass the hwinfo pointer
 +     * to the other threads  -- slightly uncool, but works fine, just need to
 +     * make sure that the data doesn't get freed twice. */
 +    if (cr->nnodes > 1)
 +    {
 +        if (!SIMMASTER(cr))
 +        {
 +            snew(hwinfo, 1);
 +        }
 +        gmx_bcast(sizeof(&hwinfo), &hwinfo, cr);
 +    }
 +#else
 +    if (PAR(cr) && !SIMMASTER(cr))
 +    {
 +        /* now we have inputrec on all nodes, can run the detection */
 +        /* TODO: perhaps it's better to propagate within a node instead? */
 +        snew(hwinfo, 1);
 +        gmx_detect_hardware(fplog, hwinfo, cr,
 +                            bForceUseGPU, bTryUseGPU, hw_opt->gpu_id);
 +    }
 +
 +    /* Now do the affinity check with MPI/no-MPI (done earlier with thread-MPI). */
 +    gmx_check_thread_affinity_set(fplog, cr,
 +                                  hw_opt, hwinfo->nthreads_hw_avail, FALSE);
 +#endif
 +
 +    /* now make sure the state is initialized and propagated */
 +    set_state_entries(state, inputrec, cr->nnodes);
 +
 +    /* A parallel command line option consistency check that we can
 +       only do after any threads have started. */
 +    if (!PAR(cr) &&
 +        (ddxyz[XX] > 1 || ddxyz[YY] > 1 || ddxyz[ZZ] > 1 || cr->npmenodes > 0))
 +    {
 +        gmx_fatal(FARGS,
 +                  "The -dd or -npme option request a parallel simulation, "
 +#ifndef GMX_MPI
 +                  "but %s was compiled without threads or MPI enabled"
 +#else
 +#ifdef GMX_THREAD_MPI
 +                  "but the number of threads (option -nt) is 1"
 +#else
 +                  "but %s was not started through mpirun/mpiexec or only one process was requested through mpirun/mpiexec"
 +#endif
 +#endif
 +                  , ShortProgram()
 +                  );
 +    }
 +
 +    if ((Flags & MD_RERUN) &&
 +        (EI_ENERGY_MINIMIZATION(inputrec->eI) || eiNM == inputrec->eI))
 +    {
 +        gmx_fatal(FARGS, "The .mdp file specified an energy mininization or normal mode algorithm, and these are not compatible with mdrun -rerun");
 +    }
 +
 +    if (can_use_allvsall(inputrec, mtop, TRUE, cr, fplog) && PAR(cr))
 +    {
 +        /* All-vs-all loops do not work with domain decomposition */
 +        Flags |= MD_PARTDEC;
 +    }
 +
 +    if (!EEL_PME(inputrec->coulombtype) || (Flags & MD_PARTDEC))
 +    {
 +        if (cr->npmenodes > 0)
 +        {
 +            if (!EEL_PME(inputrec->coulombtype))
 +            {
 +                gmx_fatal_collective(FARGS, cr, NULL,
 +                                     "PME nodes are requested, but the system does not use PME electrostatics");
 +            }
 +            if (Flags & MD_PARTDEC)
 +            {
 +                gmx_fatal_collective(FARGS, cr, NULL,
 +                                     "PME nodes are requested, but particle decomposition does not support separate PME nodes");
 +            }
 +        }
 +
 +        cr->npmenodes = 0;
 +    }
 +
 +#ifdef GMX_FAHCORE
 +    fcRegisterSteps(inputrec->nsteps, inputrec->init_step);
 +#endif
 +
 +    /* NMR restraints must be initialized before load_checkpoint,
 +     * since with time averaging the history is added to t_state.
 +     * For proper consistency check we therefore need to extend
 +     * t_state here.
 +     * So the PME-only nodes (if present) will also initialize
 +     * the distance restraints.
 +     */
 +    snew(fcd, 1);
 +
 +    /* This needs to be called before read_checkpoint to extend the state */
 +    init_disres(fplog, mtop, inputrec, cr, Flags & MD_PARTDEC, fcd, state, repl_ex_nst > 0);
 +
 +    if (gmx_mtop_ftype_count(mtop, F_ORIRES) > 0)
 +    {
 +        if (PAR(cr) && !(Flags & MD_PARTDEC))
 +        {
 +            gmx_fatal(FARGS, "Orientation restraints do not work (yet) with domain decomposition, use particle decomposition (mdrun option -pd)");
 +        }
 +        /* Orientation restraints */
 +        if (MASTER(cr))
 +        {
 +            init_orires(fplog, mtop, state->x, inputrec, cr->ms, &(fcd->orires),
 +                        state);
 +        }
 +    }
 +
 +    if (DEFORM(*inputrec))
 +    {
 +        /* Store the deform reference box before reading the checkpoint */
 +        if (SIMMASTER(cr))
 +        {
 +            copy_mat(state->box, box);
 +        }
 +        if (PAR(cr))
 +        {
 +            gmx_bcast(sizeof(box), box, cr);
 +        }
 +        /* Because we do not have the update struct available yet
 +         * in which the reference values should be stored,
 +         * we store them temporarily in static variables.
 +         * This should be thread safe, since they are only written once
 +         * and with identical values.
 +         */
 +#ifdef GMX_THREAD_MPI
 +        tMPI_Thread_mutex_lock(&deform_init_box_mutex);
 +#endif
 +        deform_init_init_step_tpx = inputrec->init_step;
 +        copy_mat(box, deform_init_box_tpx);
 +#ifdef GMX_THREAD_MPI
 +        tMPI_Thread_mutex_unlock(&deform_init_box_mutex);
 +#endif
 +    }
 +
 +    if (opt2bSet("-cpi", nfile, fnm))
 +    {
 +        /* Check if checkpoint file exists before doing continuation.
 +         * This way we can use identical input options for the first and subsequent runs...
 +         */
 +        if (gmx_fexist_master(opt2fn_master("-cpi", nfile, fnm, cr), cr) )
 +        {
 +            load_checkpoint(opt2fn_master("-cpi", nfile, fnm, cr), &fplog,
 +                            cr, Flags & MD_PARTDEC, ddxyz,
 +                            inputrec, state, &bReadRNG, &bReadEkin,
 +                            (Flags & MD_APPENDFILES),
 +                            (Flags & MD_APPENDFILESSET));
 +
 +            if (bReadRNG)
 +            {
 +                Flags |= MD_READ_RNG;
 +            }
 +            if (bReadEkin)
 +            {
 +                Flags |= MD_READ_EKIN;
 +            }
 +        }
 +    }
 +
 +    if (((MASTER(cr) || (Flags & MD_SEPPOT)) && (Flags & MD_APPENDFILES))
 +#ifdef GMX_THREAD_MPI
 +        /* With thread MPI only the master node/thread exists in mdrun.c,
 +         * therefore non-master nodes need to open the "seppot" log file here.
 +         */
 +        || (!MASTER(cr) && (Flags & MD_SEPPOT))
 +#endif
 +        )
 +    {
 +        gmx_log_open(ftp2fn(efLOG, nfile, fnm), cr, !(Flags & MD_SEPPOT),
 +                     Flags, &fplog);
 +    }
 +
 +    /* override nsteps with value from cmdline */
 +    override_nsteps_cmdline(fplog, nsteps_cmdline, inputrec, cr);
 +
 +    if (SIMMASTER(cr))
 +    {
 +        copy_mat(state->box, box);
 +    }
 +
 +    if (PAR(cr))
 +    {
 +        gmx_bcast(sizeof(box), box, cr);
 +    }
 +
 +    /* Essential dynamics */
 +    if (opt2bSet("-ei", nfile, fnm))
 +    {
 +        /* Open input and output files, allocate space for ED data structure */
 +        ed = ed_open(mtop->natoms, &state->edsamstate, nfile, fnm, Flags, oenv, cr);
 +    }
 +
 +    if (PAR(cr) && !((Flags & MD_PARTDEC) ||
 +                     EI_TPI(inputrec->eI) ||
 +                     inputrec->eI == eiNM))
 +    {
 +        cr->dd = init_domain_decomposition(fplog, cr, Flags, ddxyz, rdd, rconstr,
 +                                           dddlb_opt, dlb_scale,
 +                                           ddcsx, ddcsy, ddcsz,
 +                                           mtop, inputrec,
 +                                           box, state->x,
 +                                           &ddbox, &npme_major, &npme_minor);
 +
 +        make_dd_communicators(fplog, cr, dd_node_order);
 +
 +        /* Set overallocation to avoid frequent reallocation of arrays */
 +        set_over_alloc_dd(TRUE);
 +    }
 +    else
 +    {
 +        /* PME, if used, is done on all nodes with 1D decomposition */
 +        cr->npmenodes = 0;
 +        cr->duty      = (DUTY_PP | DUTY_PME);
 +        npme_major    = 1;
 +        npme_minor    = 1;
 +        if (!EI_TPI(inputrec->eI))
 +        {
 +            npme_major = cr->nnodes;
 +        }
 +
 +        if (inputrec->ePBC == epbcSCREW)
 +        {
 +            gmx_fatal(FARGS,
 +                      "pbc=%s is only implemented with domain decomposition",
 +                      epbc_names[inputrec->ePBC]);
 +        }
 +    }
 +
 +    if (PAR(cr))
 +    {
 +        /* After possible communicator splitting in make_dd_communicators.
 +         * we can set up the intra/inter node communication.
 +         */
 +        gmx_setup_nodecomm(fplog, cr);
 +    }
 +
 +    /* Initialize per-physical-node MPI process/thread ID and counters. */
 +    gmx_init_intranode_counters(cr);
 +
 +#ifdef GMX_MPI
 +    md_print_info(cr, fplog, "Using %d MPI %s\n",
 +                  cr->nnodes,
 +#ifdef GMX_THREAD_MPI
 +                  cr->nnodes == 1 ? "thread" : "threads"
 +#else
 +                  cr->nnodes == 1 ? "process" : "processes"
 +#endif
 +                  );
 +    fflush(stderr);
 +#endif
 +
 +    gmx_omp_nthreads_init(fplog, cr,
 +                          hwinfo->nthreads_hw_avail,
 +                          hw_opt->nthreads_omp,
 +                          hw_opt->nthreads_omp_pme,
 +                          (cr->duty & DUTY_PP) == 0,
 +                          inputrec->cutoff_scheme == ecutsVERLET);
 +
 +    gmx_check_hw_runconf_consistency(fplog, hwinfo, cr, hw_opt->nthreads_tmpi, minf.bUseGPU);
 +
 +    /* getting number of PP/PME threads
 +       PME: env variable should be read only on one node to make sure it is
 +       identical everywhere;
 +     */
 +    /* TODO nthreads_pp is only used for pinning threads.
 +     * This is a temporary solution until we have a hw topology library.
 +     */
 +    nthreads_pp  = gmx_omp_nthreads_get(emntNonbonded);
 +    nthreads_pme = gmx_omp_nthreads_get(emntPME);
 +
 +    wcycle = wallcycle_init(fplog, resetstep, cr, nthreads_pp, nthreads_pme);
 +
 +    if (PAR(cr))
 +    {
 +        /* Master synchronizes its value of reset_counters with all nodes
 +         * including PME only nodes */
 +        reset_counters = wcycle_get_reset_counters(wcycle);
 +        gmx_bcast_sim(sizeof(reset_counters), &reset_counters, cr);
 +        wcycle_set_reset_counters(wcycle, reset_counters);
 +    }
 +
 +    snew(nrnb, 1);
 +    if (cr->duty & DUTY_PP)
 +    {
 +        /* For domain decomposition we allocate dynamically
 +         * in dd_partition_system.
 +         */
 +        if (DOMAINDECOMP(cr))
 +        {
 +            bcast_state_setup(cr, state);
 +        }
 +        else
 +        {
 +            if (PAR(cr))
 +            {
 +                bcast_state(cr, state, TRUE);
 +            }
 +        }
 +
 +        /* Initiate forcerecord */
 +        fr         = mk_forcerec();
 +        fr->hwinfo = hwinfo;
 +        init_forcerec(fplog, oenv, fr, fcd, inputrec, mtop, cr, box, FALSE,
 +                      opt2fn("-table", nfile, fnm),
 +                      opt2fn("-tabletf", nfile, fnm),
 +                      opt2fn("-tablep", nfile, fnm),
 +                      opt2fn("-tableb", nfile, fnm),
 +                      nbpu_opt,
 +                      FALSE, pforce);
 +
 +        /* version for PCA_NOT_READ_NODE (see md.c) */
 +        /*init_forcerec(fplog,fr,fcd,inputrec,mtop,cr,box,FALSE,
 +           "nofile","nofile","nofile","nofile",FALSE,pforce);
 +         */
 +        fr->bSepDVDL = ((Flags & MD_SEPPOT) == MD_SEPPOT);
 +
 +        /* Initialize QM-MM */
 +        if (fr->bQMMM)
 +        {
 +            init_QMMMrec(cr, box, mtop, inputrec, fr);
 +        }
 +
 +        /* Initialize the mdatoms structure.
 +         * mdatoms is not filled with atom data,
 +         * as this can not be done now with domain decomposition.
 +         */
 +        mdatoms = init_mdatoms(fplog, mtop, inputrec->efep != efepNO);
 +
++        if (mdatoms->nPerturbed > 0 && inputrec->cutoff_scheme == ecutsVERLET)
++        {
++            gmx_fatal(FARGS, "The Verlet cut-off scheme does not (yet) support free-energy calculations with perturbed atoms, only perturbed interactions. This will be implemented soon. Use the group scheme for now.");
++        }
++
 +        /* Initialize the virtual site communication */
 +        vsite = init_vsite(mtop, cr, FALSE);
 +
 +        calc_shifts(box, fr->shift_vec);
 +
 +        /* With periodic molecules the charge groups should be whole at start up
 +         * and the virtual sites should not be far from their proper positions.
 +         */
 +        if (!inputrec->bContinuation && MASTER(cr) &&
 +            !(inputrec->ePBC != epbcNONE && inputrec->bPeriodicMols))
 +        {
 +            /* Make molecules whole at start of run */
 +            if (fr->ePBC != epbcNONE)
 +            {
 +                do_pbc_first_mtop(fplog, inputrec->ePBC, box, mtop, state->x);
 +            }
 +            if (vsite)
 +            {
 +                /* Correct initial vsite positions are required
 +                 * for the initial distribution in the domain decomposition
 +                 * and for the initial shell prediction.
 +                 */
 +                construct_vsites_mtop(fplog, vsite, mtop, state->x);
 +            }
 +        }
 +
 +        if (EEL_PME(fr->eeltype))
 +        {
 +            ewaldcoeff = fr->ewaldcoeff;
 +            pmedata    = &fr->pmedata;
 +        }
 +        else
 +        {
 +            pmedata = NULL;
 +        }
 +    }
 +    else
 +    {
 +        /* This is a PME only node */
 +
 +        /* We don't need the state */
 +        done_state(state);
 +
 +        ewaldcoeff = calc_ewaldcoeff(inputrec->rcoulomb, inputrec->ewald_rtol);
 +        snew(pmedata, 1);
 +    }
 +
 +    if (hw_opt->thread_affinity != threadaffOFF)
 +    {
 +        /* Before setting affinity, check whether the affinity has changed
 +         * - which indicates that probably the OpenMP library has changed it
 +         * since we first checked).
 +         */
 +        gmx_check_thread_affinity_set(fplog, cr,
 +                                      hw_opt, hwinfo->nthreads_hw_avail, TRUE);
 +
 +        /* Set the CPU affinity */
 +        gmx_set_thread_affinity(fplog, cr, hw_opt, nthreads_pme, hwinfo, inputrec);
 +    }
 +
 +    /* Initiate PME if necessary,
 +     * either on all nodes or on dedicated PME nodes only. */
 +    if (EEL_PME(inputrec->coulombtype))
 +    {
 +        if (mdatoms)
 +        {
 +            nChargePerturbed = mdatoms->nChargePerturbed;
 +        }
 +        if (cr->npmenodes > 0)
 +        {
 +            /* The PME only nodes need to know nChargePerturbed */
 +            gmx_bcast_sim(sizeof(nChargePerturbed), &nChargePerturbed, cr);
 +        }
 +
 +        if (cr->duty & DUTY_PME)
 +        {
 +            status = gmx_pme_init(pmedata, cr, npme_major, npme_minor, inputrec,
 +                                  mtop ? mtop->natoms : 0, nChargePerturbed,
 +                                  (Flags & MD_REPRODUCIBLE), nthreads_pme);
 +            if (status != 0)
 +            {
 +                gmx_fatal(FARGS, "Error %d initializing PME", status);
 +            }
 +        }
 +    }
 +
 +
 +    if (integrator[inputrec->eI].func == do_md)
 +    {
 +        /* Turn on signal handling on all nodes */
 +        /*
 +         * (A user signal from the PME nodes (if any)
 +         * is communicated to the PP nodes.
 +         */
 +        signal_handler_install();
 +    }
 +
 +    if (cr->duty & DUTY_PP)
 +    {
 +        if (inputrec->ePull != epullNO)
 +        {
 +            /* Initialize pull code */
 +            init_pull(fplog, inputrec, nfile, fnm, mtop, cr, oenv, inputrec->fepvals->init_lambda,
 +                      EI_DYNAMICS(inputrec->eI) && MASTER(cr), Flags);
 +        }
 +
 +        if (inputrec->bRot)
 +        {
 +            /* Initialize enforced rotation code */
 +            init_rot(fplog, inputrec, nfile, fnm, cr, state->x, box, mtop, oenv,
 +                     bVerbose, Flags);
 +        }
 +
 +        constr = init_constraints(fplog, mtop, inputrec, ed, state, cr);
 +
 +        if (DOMAINDECOMP(cr))
 +        {
 +            dd_init_bondeds(fplog, cr->dd, mtop, vsite, constr, inputrec,
 +                            Flags & MD_DDBONDCHECK, fr->cginfo_mb);
 +
 +            set_dd_parameters(fplog, cr->dd, dlb_scale, inputrec, fr, &ddbox);
 +
 +            setup_dd_grid(fplog, cr->dd);
 +        }
 +
 +        /* Now do whatever the user wants us to do (how flexible...) */
 +        integrator[inputrec->eI].func(fplog, cr, nfile, fnm,
 +                                      oenv, bVerbose, bCompact,
 +                                      nstglobalcomm,
 +                                      vsite, constr,
 +                                      nstepout, inputrec, mtop,
 +                                      fcd, state,
 +                                      mdatoms, nrnb, wcycle, ed, fr,
 +                                      repl_ex_nst, repl_ex_nex, repl_ex_seed,
 +                                      membed,
 +                                      cpt_period, max_hours,
 +                                      deviceOptions,
 +                                      Flags,
 +                                      &runtime);
 +
 +        if (inputrec->ePull != epullNO)
 +        {
 +            finish_pull(fplog, inputrec->pull);
 +        }
 +
 +        if (inputrec->bRot)
 +        {
 +            finish_rot(fplog, inputrec->rot);
 +        }
 +
 +    }
 +    else
 +    {
 +        /* do PME only */
 +        gmx_pmeonly(*pmedata, cr, nrnb, wcycle, ewaldcoeff, FALSE, inputrec);
 +    }
 +
 +    if (EI_DYNAMICS(inputrec->eI) || EI_TPI(inputrec->eI))
 +    {
 +        /* Some timing stats */
 +        if (SIMMASTER(cr))
 +        {
 +            if (runtime.proc == 0)
 +            {
 +                runtime.proc = runtime.real;
 +            }
 +        }
 +        else
 +        {
 +            runtime.real = 0;
 +        }
 +    }
 +
 +    wallcycle_stop(wcycle, ewcRUN);
 +
 +    /* Finish up, write some stuff
 +     * if rerunMD, don't write last frame again
 +     */
 +    finish_run(fplog, cr, ftp2fn(efSTO, nfile, fnm),
 +               inputrec, nrnb, wcycle, &runtime,
 +               fr != NULL && fr->nbv != NULL && fr->nbv->bUseGPU ?
 +               nbnxn_cuda_get_timings(fr->nbv->cu_nbv) : NULL,
 +               nthreads_pp,
 +               EI_DYNAMICS(inputrec->eI) && !MULTISIM(cr));
 +
 +    if ((cr->duty & DUTY_PP) && fr->nbv != NULL && fr->nbv->bUseGPU)
 +    {
 +        char gpu_err_str[STRLEN];
 +
 +        /* free GPU memory and uninitialize GPU (by destroying the context) */
 +        nbnxn_cuda_free(fplog, fr->nbv->cu_nbv);
 +
 +        if (!free_gpu(gpu_err_str))
 +        {
 +            gmx_warning("On node %d failed to free GPU #%d: %s",
 +                        cr->nodeid, get_current_gpu_device_id(), gpu_err_str);
 +        }
 +    }
 +
 +    if (opt2bSet("-membed", nfile, fnm))
 +    {
 +        sfree(membed);
 +    }
 +
 +#ifdef GMX_THREAD_MPI
 +    if (PAR(cr) && SIMMASTER(cr))
 +#endif
 +    {
 +        gmx_hardware_info_free(hwinfo);
 +    }
 +
 +    /* Does what it says */
 +    print_date_and_time(fplog, cr->nodeid, "Finished mdrun", &runtime);
 +
 +    /* Close logfile already here if we were appending to it */
 +    if (MASTER(cr) && (Flags & MD_APPENDFILES))
 +    {
 +        gmx_log_close(fplog);
 +    }
 +
 +    rc = (int)gmx_get_stop_condition();
 +
 +#ifdef GMX_THREAD_MPI
 +    /* we need to join all threads. The sub-threads join when they
 +       exit this function, but the master thread needs to be told to
 +       wait for that. */
 +    if (PAR(cr) && MASTER(cr))
 +    {
 +        tMPI_Finalize();
 +    }
 +#endif
 +
 +    return rc;
 +}
Simple merge