Merge branch release-5-1
authorMark Abraham <mark.j.abraham@gmail.com>
Fri, 19 Feb 2016 09:18:47 +0000 (10:18 +0100)
committerBerk Hess <hess@kth.se>
Fri, 19 Feb 2016 11:01:24 +0000 (12:01 +0100)
Change-Id: I8935d1351656d6b0888e923c8316fce5d5c27cde

1  2 
docs/manual/forcefield.tex
src/gromacs/gmxpreprocess/toppush.cpp
src/gromacs/mdlib/forcerec.cpp
src/gromacs/mdlib/nbnxn_search.cpp
src/gromacs/mdlib/tgroup.cpp
src/gromacs/tools/compare.cpp

index 74bdb3f974c4e9e11bd8623f8573c0bcacafb7e2,e6e76823898ef09a0067692317700331c1e69e46..169c13d4f94572fc785e1ce0424ea67ade1a2f5c
@@@ -731,7 -731,7 +731,7 @@@ this will never cause problems
  force constants in kJ/mol/rad$^2$.
  
  \begin{figure}
- \centerline{\includegraphics[width=8cm]{plots/f-imps}}
+ \centerline{\includegraphics[width=10cm]{plots/f-imps.pdf}}
  \caption{Improper dihedral potential.}
  \label{fig:imps}
  \end{figure}
@@@ -1932,7 -1932,7 +1932,7 @@@ rather than using soft-core interaction
  statistically noisy in most cases. This behavior can be overwritten
  by using the mdp option {\tt sc-coul} to {\tt yes}. Note that the {\tt sc-coul}
  is only taken into account when lambda states are used, not with
- {\tt couple-lambda0/couple-lambda1}, and you can still turn off soft-core
+ {\tt couple-lambda0}~/ {\tt couple-lambda1}, and you can still turn off soft-core
  interactions by setting {\tt sc-alpha=0}. Additionally, the soft-core
  interaction potential is only applied when either the A or B
  state has zero interaction potential. If both A and B states have
@@@ -2030,13 -2030,48 +2030,13 @@@ type selectors (termed {\tt vdwtype} an
  parameters, for a total of six non-bonded interaction parameters. See
  the User Guide for a complete description of these parameters.
  
 -The neighbor searching (NS) can be performed using a single-range, or a twin-range 
 -approach. Since the former is merely a special case of the latter, we will 
 -discuss the more general twin-range. In this case, NS is described by two
 -radii: {\tt rlist} and max({\tt rcoulomb},{\tt rvdw}).
 -Usually one builds the neighbor list every 10 time steps
 -or every 20 fs (parameter {\tt nstlist}). In the neighbor list, all interaction 
 -pairs that  fall within {\tt rlist} are stored. Furthermore, the 
 -interactions between pairs that do not
 -fall within {\tt rlist} but do fall within max({\tt rcoulomb},{\tt rvdw})
 -are computed during NS.  The
 -forces and energy are stored separately and added to short-range forces
 -at every time step between successive NS. If {\tt rlist} = 
 -max({\tt rcoulomb},{\tt rvdw}), no forces
 -are evaluated during neighbor list generation.
 -The \normindex{virial} is calculated from the sum of the short- and
 -long-range forces.
 -This means that the virial can be slightly asymmetrical at non-NS steps.
 -When mdrun is compiled to use mixed precision, the virial is almost always asymmetrical because the
 -off-diagonal elements are about as large as each element in the sum.
 -In most cases this is not really a problem, since the fluctuations in the
 -virial can be 2 orders of magnitude larger than the average.
 -
 -Except for the plain cut-off,
 -all of the interaction functions in \tabref{funcparm}
 -require that neighbor searching be done with a larger radius than the $r_c$
 +In the group cut-off scheme, all of the interaction functions in \tabref{funcparm}
 +require that neighbor searching be done with a radius at least as large as the $r_c$
  specified for the functional form, because of the use of charge groups.
  The extra radius is typically of the order of 0.25 nm (roughly the 
  largest distance between two atoms in a charge group plus the distance a 
  charge group can diffuse within neighbor list updates).
  
 -%If your charge groups are very large it may be interesting to turn off charge
 -%groups, by setting the option 
 -%{\tt bAtomList = yes} in your {\tt grompp.mdp} file.
 -%In this case only a small extra radius to account for diffusion needs to be 
 -%added (0.1 nm). Do not however use this together with the plain cut-off
 -%method, as it will generate large artifacts (\secref{cg}).
 -%In summary, there are four parameters that describe NS behavior:
 -%{\tt nstlist} (update frequency in number of time steps),
 -%{\tt bAtomList} (whether or not charge groups are used to generate neighbor list, the default is to use charge groups, so {\tt bAtomList = no}),
 -%{\tt rshort} and {\tt rlong} which are the two radii {\rs} and {\rl}
 -%described above.
 -
  \begin{table}[ht]
  \centering
  \begin{tabular}{|ll|l|}
@@@ -2488,33 -2523,9 +2488,9 @@@ It is recommended to optimize the param
  electrostatic interaction such as PME grid dimensions and cut-off radii.
  This is particularly relevant to do before launching long production runs.
  
- {\gromacs} includes a special tool, {\tt g_tune_pme}, which automates the 
- process of selecting the optimal size of the grid and number of PME-only
- notes.
- %
- % Temporarily removed since I am not sure about the state of the testlr
- % program...
- %
- %It is possible to test the accuracy of your settings using the program
- %{\tt\normindex{testlr}} in the {\tt src/gmxlib} dir. This program computes
- %forces and potentials using PPPM and an Ewald implementation and gives the
- %absolute and RMS errors in both:
- %\begin{verbatim}
- %ERROR ANALYSIS
- %Error:         Max Abs         RMS
- %Force            1.132       0.251
- %Potential        0.113       0.035
- %\end{verbatim}
- %{\bf Note:} these numbers were generated using a grid spacing of
- %0.058 nm and $r_c$ = 1.0 nm.
- %
- %You can see what the accuracy is without optimizing the
- %$\hat{G}(k)$ function, if you pass the {\tt -ghat} option to {\tt
- %testlr}. Try it if you think the {\tt mk_ghat} procedure is a waste
- %of time.
- %} % Brace matches ifthenelse test for gmxlite
+ {\tt gmx mdrun} will automatically do a lot of PME optimization, and
+ {\gromacs} also includes a special tool, {\tt gmx tune_pme}, which
+ automates the process of selecting the optimal number of PME-only ranks.
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
index 10bd4a0218fef09a03bf0f0c250ff32a5a967c26,0000000000000000000000000000000000000000..9bc3a53aecc10af74ad4615915f824cf682d81dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,2749 -1,0 +1,2741 @@@
-     const char  *formal = "%s%s%s%s%s%s%s%s";
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "gmxpre.h"
 +
 +#include "toppush.h"
 +
 +#include <ctype.h>
 +#include <stdlib.h>
 +
 +#include <cmath>
 +
 +#include <algorithm>
 +
 +#include "gromacs/fileio/warninp.h"
 +#include "gromacs/gmxpreprocess/gpp_atomtype.h"
 +#include "gromacs/gmxpreprocess/gpp_bond_atomtype.h"
 +#include "gromacs/gmxpreprocess/notset.h"
 +#include "gromacs/gmxpreprocess/readir.h"
 +#include "gromacs/gmxpreprocess/topdirs.h"
 +#include "gromacs/gmxpreprocess/toputil.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/topology/ifunc.h"
 +#include "gromacs/topology/symtab.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/gmxassert.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +void generate_nbparams(int comb, int ftype, t_params *plist, gpp_atomtype_t atype,
 +                       warninp_t wi)
 +{
 +    int   i, j, k = -1, nf;
 +    int   nr, nrfp;
 +    real  c, bi, bj, ci, cj, ci0, ci1, ci2, cj0, cj1, cj2;
 +    char  errbuf[256];
 +
 +    /* Lean mean shortcuts */
 +    nr   = get_atomtype_ntypes(atype);
 +    nrfp = NRFP(ftype);
 +    snew(plist->param, nr*nr);
 +    plist->nr = nr*nr;
 +
 +    /* Fill the matrix with force parameters */
 +    switch (ftype)
 +    {
 +        case F_LJ:
 +            switch (comb)
 +            {
 +                case eCOMB_GEOMETRIC:
 +                    /* Gromos rules */
 +                    for (i = k = 0; (i < nr); i++)
 +                    {
 +                        for (j = 0; (j < nr); j++, k++)
 +                        {
 +                            for (nf = 0; (nf < nrfp); nf++)
 +                            {
 +                                ci = get_atomtype_nbparam(i, nf, atype);
 +                                cj = get_atomtype_nbparam(j, nf, atype);
 +                                c  = std::sqrt(ci * cj);
 +                                plist->param[k].c[nf]      = c;
 +                            }
 +                        }
 +                    }
 +                    break;
 +
 +                case eCOMB_ARITHMETIC:
 +                    /* c0 and c1 are sigma and epsilon */
 +                    for (i = k = 0; (i < nr); i++)
 +                    {
 +                        for (j = 0; (j < nr); j++, k++)
 +                        {
 +                            ci0                  = get_atomtype_nbparam(i, 0, atype);
 +                            cj0                  = get_atomtype_nbparam(j, 0, atype);
 +                            ci1                  = get_atomtype_nbparam(i, 1, atype);
 +                            cj1                  = get_atomtype_nbparam(j, 1, atype);
 +                            plist->param[k].c[0] = (fabs(ci0) + fabs(cj0))*0.5;
 +                            /* Negative sigma signals that c6 should be set to zero later,
 +                             * so we need to propagate that through the combination rules.
 +                             */
 +                            if (ci0 < 0 || cj0 < 0)
 +                            {
 +                                plist->param[k].c[0] *= -1;
 +                            }
 +                            plist->param[k].c[1] = std::sqrt(ci1*cj1);
 +                        }
 +                    }
 +
 +                    break;
 +                case eCOMB_GEOM_SIG_EPS:
 +                    /* c0 and c1 are sigma and epsilon */
 +                    for (i = k = 0; (i < nr); i++)
 +                    {
 +                        for (j = 0; (j < nr); j++, k++)
 +                        {
 +                            ci0                  = get_atomtype_nbparam(i, 0, atype);
 +                            cj0                  = get_atomtype_nbparam(j, 0, atype);
 +                            ci1                  = get_atomtype_nbparam(i, 1, atype);
 +                            cj1                  = get_atomtype_nbparam(j, 1, atype);
 +                            plist->param[k].c[0] = std::sqrt(fabs(ci0*cj0));
 +                            /* Negative sigma signals that c6 should be set to zero later,
 +                             * so we need to propagate that through the combination rules.
 +                             */
 +                            if (ci0 < 0 || cj0 < 0)
 +                            {
 +                                plist->param[k].c[0] *= -1;
 +                            }
 +                            plist->param[k].c[1] = std::sqrt(ci1*cj1);
 +                        }
 +                    }
 +
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "No such combination rule %d", comb);
 +            }
 +            if (plist->nr != k)
 +            {
 +                gmx_incons("Topology processing, generate nb parameters");
 +            }
 +            break;
 +
 +        case F_BHAM:
 +            /* Buckingham rules */
 +            for (i = k = 0; (i < nr); i++)
 +            {
 +                for (j = 0; (j < nr); j++, k++)
 +                {
 +                    ci0                  = get_atomtype_nbparam(i, 0, atype);
 +                    cj0                  = get_atomtype_nbparam(j, 0, atype);
 +                    ci2                  = get_atomtype_nbparam(i, 2, atype);
 +                    cj2                  = get_atomtype_nbparam(j, 2, atype);
 +                    bi                   = get_atomtype_nbparam(i, 1, atype);
 +                    bj                   = get_atomtype_nbparam(j, 1, atype);
 +                    plist->param[k].c[0] = std::sqrt(ci0 * cj0);
 +                    if ((bi == 0) || (bj == 0))
 +                    {
 +                        plist->param[k].c[1] = 0;
 +                    }
 +                    else
 +                    {
 +                        plist->param[k].c[1] = 2.0/(1/bi+1/bj);
 +                    }
 +                    plist->param[k].c[2] = std::sqrt(ci2 * cj2);
 +                }
 +            }
 +
 +            break;
 +        default:
 +            sprintf(errbuf, "Invalid nonbonded type %s",
 +                    interaction_function[ftype].longname);
 +            warning_error(wi, errbuf);
 +    }
 +}
 +
 +static void realloc_nb_params(gpp_atomtype_t at,
 +                              t_nbparam ***nbparam, t_nbparam ***pair)
 +{
 +    /* Add space in the non-bonded parameters matrix */
 +    int atnr = get_atomtype_ntypes(at);
 +    srenew(*nbparam, atnr);
 +    snew((*nbparam)[atnr-1], atnr);
 +    if (pair)
 +    {
 +        srenew(*pair, atnr);
 +        snew((*pair)[atnr-1], atnr);
 +    }
 +}
 +
 +static void copy_B_from_A(int ftype, double *c)
 +{
 +    int nrfpA, nrfpB, i;
 +
 +    nrfpA = NRFPA(ftype);
 +    nrfpB = NRFPB(ftype);
 +
 +    /* Copy the B parameters from the first nrfpB A parameters */
 +    for (i = 0; (i < nrfpB); i++)
 +    {
 +        c[nrfpA+i] = c[i];
 +    }
 +}
 +
 +void push_at (t_symtab *symtab, gpp_atomtype_t at, t_bond_atomtype bat,
 +              char *line, int nb_funct,
 +              t_nbparam ***nbparam, t_nbparam ***pair,
 +              warninp_t wi)
 +{
 +    typedef struct {
 +        const char *entry;
 +        int         ptype;
 +    } t_xlate;
 +    t_xlate    xl[eptNR] = {
 +        { "A",   eptAtom },
 +        { "N",   eptNucleus },
 +        { "S",   eptShell },
 +        { "B",   eptBond },
 +        { "V",   eptVSite },
 +    };
 +
 +    int        nr, i, nfields, j, pt, nfp0 = -1;
 +    int        batype_nr, nread;
 +    char       type[STRLEN], btype[STRLEN], ptype[STRLEN];
 +    double     m, q;
 +    double     c[MAXFORCEPARAM];
 +    double     radius, vol, surftens, gb_radius, S_hct;
 +    char       tmpfield[12][100]; /* Max 12 fields of width 100 */
 +    char       errbuf[256];
 +    t_atom    *atom;
 +    t_param   *param;
 +    int        atomnr;
 +    gmx_bool   have_atomic_number;
 +    gmx_bool   have_bonded_type;
 +
 +    snew(atom, 1);
 +    snew(param, 1);
 +
 +    /* First assign input line to temporary array */
 +    nfields = sscanf(line, "%s%s%s%s%s%s%s%s%s%s%s%s",
 +                     tmpfield[0], tmpfield[1], tmpfield[2], tmpfield[3], tmpfield[4], tmpfield[5],
 +                     tmpfield[6], tmpfield[7], tmpfield[8], tmpfield[9], tmpfield[10], tmpfield[11]);
 +
 +    /* Comments on optional fields in the atomtypes section:
 +     *
 +     * The force field format is getting a bit old. For OPLS-AA we needed
 +     * to add a special bonded atomtype, and for Gerrit Groenhofs QM/MM stuff
 +     * we also needed the atomic numbers.
 +     * To avoid making all old or user-generated force fields unusable we
 +     * have introduced both these quantities as optional columns, and do some
 +     * acrobatics to check whether they are present or not.
 +     * This will all look much nicer when we switch to XML... sigh.
 +     *
 +     * Field 0 (mandatory) is the nonbonded type name. (string)
 +     * Field 1 (optional)  is the bonded type (string)
 +     * Field 2 (optional)  is the atomic number (int)
 +     * Field 3 (mandatory) is the mass (numerical)
 +     * Field 4 (mandatory) is the charge (numerical)
 +     * Field 5 (mandatory) is the particle type (single character)
 +     * This is followed by a number of nonbonded parameters.
 +     *
 +     * The safest way to identify the format is the particle type field.
 +     *
 +     * So, here is what we do:
 +     *
 +     * A. Read in the first six fields as strings
 +     * B. If field 3 (starting from 0) is a single char, we have neither
 +     *    bonded_type or atomic numbers.
 +     * C. If field 5 is a single char we have both.
 +     * D. If field 4 is a single char we check field 1. If this begins with
 +     *    an alphabetical character we have bonded types, otherwise atomic numbers.
 +     */
 +
 +    if (nfields < 6)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +
 +    if ( (strlen(tmpfield[5]) == 1) && isalpha(tmpfield[5][0]) )
 +    {
 +        have_bonded_type   = TRUE;
 +        have_atomic_number = TRUE;
 +    }
 +    else if ( (strlen(tmpfield[3]) == 1) && isalpha(tmpfield[3][0]) )
 +    {
 +        have_bonded_type   = FALSE;
 +        have_atomic_number = FALSE;
 +    }
 +    else
 +    {
 +        have_bonded_type   = ( isalpha(tmpfield[1][0]) != 0 );
 +        have_atomic_number = !have_bonded_type;
 +    }
 +
 +    /* optional fields */
 +    surftens  = -1;
 +    vol       = -1;
 +    radius    = -1;
 +    gb_radius = -1;
 +    atomnr    = -1;
 +    S_hct     = -1;
 +
 +    switch (nb_funct)
 +    {
 +
 +        case F_LJ:
 +            nfp0 = 2;
 +
 +            if (have_atomic_number)
 +            {
 +                if (have_bonded_type)
 +                {
 +                    nread = sscanf(line, "%s%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &atomnr, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 8)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, &atomnr, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 7)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                if (have_bonded_type)
 +                {
 +                    /* !have_atomic_number && have_bonded_type */
 +                    nread = sscanf(line, "%s%s%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 7)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* !have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%lf%lf%s%lf%lf%lf%lf%lf%lf",
 +                                   type, &m, &q, ptype, &c[0], &c[1],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 6)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +
 +            if (!have_bonded_type)
 +            {
 +                strcpy(btype, type);
 +            }
 +
 +            if (!have_atomic_number)
 +            {
 +                atomnr = -1;
 +            }
 +
 +            break;
 +
 +        case F_BHAM:
 +            nfp0 = 3;
 +
 +            if (have_atomic_number)
 +            {
 +                if (have_bonded_type)
 +                {
 +                    nread = sscanf(line, "%s%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &atomnr, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 9)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%d%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, &atomnr, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 8)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                if (have_bonded_type)
 +                {
 +                    /* !have_atomic_number && have_bonded_type */
 +                    nread = sscanf(line, "%s%s%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, btype, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 8)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +                else
 +                {
 +                    /* !have_atomic_number && !have_bonded_type */
 +                    nread = sscanf(line, "%s%lf%lf%s%lf%lf%lf%lf%lf%lf%lf",
 +                                   type, &m, &q, ptype, &c[0], &c[1], &c[2],
 +                                   &radius, &vol, &surftens, &gb_radius);
 +                    if (nread < 7)
 +                    {
 +                        too_few(wi);
 +                        return;
 +                    }
 +                }
 +            }
 +
 +            if (!have_bonded_type)
 +            {
 +                strcpy(btype, type);
 +            }
 +
 +            if (!have_atomic_number)
 +            {
 +                atomnr = -1;
 +            }
 +
 +            break;
 +
 +        default:
 +            gmx_fatal(FARGS, "Invalid function type %d in push_at %s %d", nb_funct,
 +                      __FILE__, __LINE__);
 +    }
 +    for (j = nfp0; (j < MAXFORCEPARAM); j++)
 +    {
 +        c[j] = 0.0;
 +    }
 +
 +    if (strlen(type) == 1 && isdigit(type[0]))
 +    {
 +        gmx_fatal(FARGS, "Atom type names can't be single digits.");
 +    }
 +
 +    if (strlen(btype) == 1 && isdigit(btype[0]))
 +    {
 +        gmx_fatal(FARGS, "Bond atom type names can't be single digits.");
 +    }
 +
 +    /* Hack to read old topologies */
 +    if (gmx_strcasecmp(ptype, "D") == 0)
 +    {
 +        sprintf(ptype, "V");
 +    }
 +    for (j = 0; (j < eptNR); j++)
 +    {
 +        if (gmx_strcasecmp(ptype, xl[j].entry) == 0)
 +        {
 +            break;
 +        }
 +    }
 +    if (j == eptNR)
 +    {
 +        gmx_fatal(FARGS, "Invalid particle type %s on line %s",
 +                  ptype, line);
 +    }
 +    pt = xl[j].ptype;
 +    if (debug)
 +    {
 +        fprintf(debug, "ptype: %s\n", ptype_str[pt]);
 +    }
 +
 +    atom->q     = q;
 +    atom->m     = m;
 +    atom->ptype = pt;
 +    for (i = 0; (i < MAXFORCEPARAM); i++)
 +    {
 +        param->c[i] = c[i];
 +    }
 +
 +    if ((batype_nr = get_bond_atomtype_type(btype, bat)) == NOTSET)
 +    {
 +        add_bond_atomtype(bat, symtab, btype);
 +    }
 +    batype_nr = get_bond_atomtype_type(btype, bat);
 +
 +    if ((nr = get_atomtype_type(type, at)) != NOTSET)
 +    {
 +        sprintf(errbuf, "Overriding atomtype %s", type);
 +        warning(wi, errbuf);
 +        if ((nr = set_atomtype(nr, at, symtab, atom, type, param, batype_nr,
 +                               radius, vol, surftens, atomnr, gb_radius, S_hct)) == NOTSET)
 +        {
 +            gmx_fatal(FARGS, "Replacing atomtype %s failed", type);
 +        }
 +    }
 +    else if ((add_atomtype(at, symtab, atom, type, param,
 +                           batype_nr, radius, vol,
 +                           surftens, atomnr, gb_radius, S_hct)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Adding atomtype %s failed", type);
 +    }
 +    else
 +    {
 +        /* Add space in the non-bonded parameters matrix */
 +        realloc_nb_params(at, nbparam, pair);
 +    }
 +    sfree(atom);
 +    sfree(param);
 +}
 +
 +static void push_bondtype(t_params     *       bt,
 +                          t_param     *        b,
 +                          int                  nral,
 +                          int                  ftype,
 +                          gmx_bool             bAllowRepeat,
 +                          char     *           line,
 +                          warninp_t            wi)
 +{
 +    int      i, j;
 +    gmx_bool bTest, bFound, bCont, bId;
 +    int      nr   = bt->nr;
 +    int      nrfp = NRFP(ftype);
 +    char     errbuf[256];
 +
 +    /* If bAllowRepeat is TRUE, we allow multiple entries as long as they
 +       are on directly _adjacent_ lines.
 +     */
 +
 +    /* First check if our atomtypes are _identical_ (not reversed) to the previous
 +       entry. If they are not identical we search for earlier duplicates. If they are
 +       we can skip it, since we already searched for the first line
 +       in this group.
 +     */
 +
 +    bFound = FALSE;
 +    bCont  = FALSE;
 +
 +    if (bAllowRepeat && nr > 1)
 +    {
 +        for (j = 0, bCont = TRUE; (j < nral); j++)
 +        {
 +            bCont = bCont && (b->a[j] == bt->param[nr-2].a[j]);
 +        }
 +    }
 +
 +    /* Search for earlier duplicates if this entry was not a continuation
 +       from the previous line.
 +     */
 +    if (!bCont)
 +    {
 +        bFound = FALSE;
 +        for (i = 0; (i < nr); i++)
 +        {
 +            bTest = TRUE;
 +            for (j = 0; (j < nral); j++)
 +            {
 +                bTest = (bTest && (b->a[j] == bt->param[i].a[j]));
 +            }
 +            if (!bTest)
 +            {
 +                bTest = TRUE;
 +                for (j = 0; (j < nral); j++)
 +                {
 +                    bTest = (bTest && (b->a[nral-1-j] == bt->param[i].a[j]));
 +                }
 +            }
 +            if (bTest)
 +            {
 +                if (!bFound)
 +                {
 +                    bId = TRUE;
 +                    for (j = 0; (j < nrfp); j++)
 +                    {
 +                        bId = bId && (bt->param[i].c[j] == b->c[j]);
 +                    }
 +                    if (!bId)
 +                    {
 +                        sprintf(errbuf, "Overriding %s parameters.%s",
 +                                interaction_function[ftype].longname,
 +                                (ftype == F_PDIHS) ?
 +                                "\nUse dihedraltype 9 to allow several multiplicity terms. Only consecutive lines are combined. Non-consective lines overwrite each other."
 +                                : "");
 +                        warning(wi, errbuf);
 +                        fprintf(stderr, "  old:                                         ");
 +                        for (j = 0; (j < nrfp); j++)
 +                        {
 +                            fprintf(stderr, " %g", bt->param[i].c[j]);
 +                        }
 +                        fprintf(stderr, " \n  new: %s\n\n", line);
 +                    }
 +                }
 +                /* Overwrite it! */
 +                for (j = 0; (j < nrfp); j++)
 +                {
 +                    bt->param[i].c[j] = b->c[j];
 +                }
 +                bFound = TRUE;
 +            }
 +        }
 +    }
 +    if (!bFound)
 +    {
 +        /* alloc */
 +        pr_alloc (2, bt);
 +
 +        /* fill the arrays up and down */
 +        memcpy(bt->param[bt->nr].c,  b->c, sizeof(b->c));
 +        memcpy(bt->param[bt->nr].a,  b->a, sizeof(b->a));
 +        memcpy(bt->param[bt->nr+1].c, b->c, sizeof(b->c));
 +
 +        /* The definitions of linear angles depend on the order of atoms,
 +         * that means that for atoms i-j-k, with certain parameter a, the
 +         * corresponding k-j-i angle will have parameter 1-a.
 +         */
 +        if (ftype == F_LINEAR_ANGLES)
 +        {
 +            bt->param[bt->nr+1].c[0] = 1-bt->param[bt->nr+1].c[0];
 +            bt->param[bt->nr+1].c[2] = 1-bt->param[bt->nr+1].c[2];
 +        }
 +
 +        for (j = 0; (j < nral); j++)
 +        {
 +            bt->param[bt->nr+1].a[j] = b->a[nral-1-j];
 +        }
 +
 +        bt->nr += 2;
 +    }
 +}
 +
 +void push_bt(directive d, t_params bt[], int nral,
 +             gpp_atomtype_t at,
 +             t_bond_atomtype bat, char *line,
 +             warninp_t wi)
 +{
 +    const char *formal[MAXATOMLIST+1] = {
 +        "%s",
 +        "%s%s",
 +        "%s%s%s",
 +        "%s%s%s%s",
 +        "%s%s%s%s%s",
 +        "%s%s%s%s%s%s",
 +        "%s%s%s%s%s%s%s"
 +    };
 +    const char *formnl[MAXATOMLIST+1] = {
 +        "%*s",
 +        "%*s%*s",
 +        "%*s%*s%*s",
 +        "%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s%*s"
 +    };
 +    const char *formlf = "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf";
 +    int         i, ft, ftype, nn, nrfp, nrfpA;
 +    char        f1[STRLEN];
 +    char        alc[MAXATOMLIST+1][20];
 +    /* One force parameter more, so we can check if we read too many */
 +    double      c[MAXFORCEPARAM+1];
 +    t_param     p;
 +    char        errbuf[256];
 +
 +    if ((bat && at) || (!bat && !at))
 +    {
 +        gmx_incons("You should pass either bat or at to push_bt");
 +    }
 +
 +    /* Make format string (nral ints+functype) */
 +    if ((nn = sscanf(line, formal[nral],
 +                     alc[0], alc[1], alc[2], alc[3], alc[4], alc[5])) != nral+1)
 +    {
 +        sprintf(errbuf, "Not enough atomtypes (%d instead of %d)", nn-1, nral);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +
 +    ft    = strtol(alc[nral], NULL, 10);
 +    ftype = ifunc_index(d, ft);
 +    nrfp  = NRFP(ftype);
 +    nrfpA = interaction_function[ftype].nrfpA;
 +    strcpy(f1, formnl[nral]);
 +    strcat(f1, formlf);
 +    if ((nn = sscanf(line, f1, &c[0], &c[1], &c[2], &c[3], &c[4], &c[5], &c[6], &c[7], &c[8], &c[9], &c[10], &c[11], &c[12]))
 +        != nrfp)
 +    {
 +        if (nn == nrfpA)
 +        {
 +            /* Copy the B-state from the A-state */
 +            copy_B_from_A(ftype, c);
 +        }
 +        else
 +        {
 +            if (nn < nrfpA)
 +            {
 +                warning_error(wi, "Not enough parameters");
 +            }
 +            else if (nn > nrfpA && nn < nrfp)
 +            {
 +                warning_error(wi, "Too many parameters or not enough parameters for topology B");
 +            }
 +            else if (nn > nrfp)
 +            {
 +                warning_error(wi, "Too many parameters");
 +            }
 +            for (i = nn; (i < nrfp); i++)
 +            {
 +                c[i] = 0.0;
 +            }
 +        }
 +    }
 +    for (i = 0; (i < nral); i++)
 +    {
 +        if (at && ((p.a[i] = get_atomtype_type(alc[i], at)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown atomtype %s\n", alc[i]);
 +        }
 +        else if (bat && ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown bond_atomtype %s\n", alc[i]);
 +        }
 +    }
 +    for (i = 0; (i < nrfp); i++)
 +    {
 +        p.c[i] = c[i];
 +    }
 +    push_bondtype (&(bt[ftype]), &p, nral, ftype, FALSE, line, wi);
 +}
 +
 +
 +void push_dihedraltype(directive d, t_params bt[],
 +                       t_bond_atomtype bat, char *line,
 +                       warninp_t wi)
 +{
 +    const char  *formal[MAXATOMLIST+1] = {
 +        "%s",
 +        "%s%s",
 +        "%s%s%s",
 +        "%s%s%s%s",
 +        "%s%s%s%s%s",
 +        "%s%s%s%s%s%s",
 +        "%s%s%s%s%s%s%s"
 +    };
 +    const char  *formnl[MAXATOMLIST+1] = {
 +        "%*s",
 +        "%*s%*s",
 +        "%*s%*s%*s",
 +        "%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s%*s"
 +    };
 +    const char  *formlf[MAXFORCEPARAM] = {
 +        "%lf",
 +        "%lf%lf",
 +        "%lf%lf%lf",
 +        "%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +        "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
 +    };
 +    int          i, ft, ftype, nn, nrfp, nrfpA, nral;
 +    char         f1[STRLEN];
 +    char         alc[MAXATOMLIST+1][20];
 +    double       c[MAXFORCEPARAM];
 +    t_param      p;
 +    gmx_bool     bAllowRepeat;
 +    char         errbuf[256];
 +
 +    /* This routine accepts dihedraltypes defined from either 2 or 4 atoms.
 +     *
 +     * We first check for 2 atoms with the 3th column being an integer
 +     * defining the type. If this isn't the case, we try it with 4 atoms
 +     * and the 5th column defining the dihedral type.
 +     */
 +    nn = sscanf(line, formal[4], alc[0], alc[1], alc[2], alc[3], alc[4]);
 +    if (nn >= 3 && strlen(alc[2]) == 1 && isdigit(alc[2][0]))
 +    {
 +        nral  = 2;
 +        ft    = strtol(alc[nral], NULL, 10);
 +        /* Move atom types around a bit and use 'X' for wildcard atoms
 +         * to create a 4-atom dihedral definition with arbitrary atoms in
 +         * position 1 and 4.
 +         */
 +        if (alc[2][0] == '2')
 +        {
 +            /* improper - the two atomtypes are 1,4. Use wildcards for 2,3 */
 +            strcpy(alc[3], alc[1]);
 +            sprintf(alc[2], "X");
 +            sprintf(alc[1], "X");
 +            /* alc[0] stays put */
 +        }
 +        else
 +        {
 +            /* proper - the two atomtypes are 2,3. Use wildcards for 1,4 */
 +            sprintf(alc[3], "X");
 +            strcpy(alc[2], alc[1]);
 +            strcpy(alc[1], alc[0]);
 +            sprintf(alc[0], "X");
 +        }
 +    }
 +    else if (nn == 5 && strlen(alc[4]) == 1 && isdigit(alc[4][0]))
 +    {
 +        nral  = 4;
 +        ft    = strtol(alc[nral], NULL, 10);
 +    }
 +    else
 +    {
 +        sprintf(errbuf, "Incorrect number of atomtypes for dihedral (%d instead of 2 or 4)", nn-1);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +
 +    if (ft == 9)
 +    {
 +        /* Previously, we have always overwritten parameters if e.g. a torsion
 +           with the same atomtypes occurs on multiple lines. However, CHARMM and
 +           some other force fields specify multiple dihedrals over some bonds,
 +           including cosines with multiplicity 6 and somethimes even higher.
 +           Thus, they cannot be represented with Ryckaert-Bellemans terms.
 +           To add support for these force fields, Dihedral type 9 is identical to
 +           normal proper dihedrals, but repeated entries are allowed.
 +         */
 +        bAllowRepeat = TRUE;
 +        ft           = 1;
 +    }
 +    else
 +    {
 +        bAllowRepeat = FALSE;
 +    }
 +
 +
 +    ftype = ifunc_index(d, ft);
 +    nrfp  = NRFP(ftype);
 +    nrfpA = interaction_function[ftype].nrfpA;
 +
 +    strcpy(f1, formnl[nral]);
 +    strcat(f1, formlf[nrfp-1]);
 +
 +    /* Check number of parameters given */
 +    if ((nn = sscanf(line, f1, &c[0], &c[1], &c[2], &c[3], &c[4], &c[5], &c[6], &c[7], &c[8], &c[9], &c[10], &c[11]))
 +        != nrfp)
 +    {
 +        if (nn == nrfpA)
 +        {
 +            /* Copy the B-state from the A-state */
 +            copy_B_from_A(ftype, c);
 +        }
 +        else
 +        {
 +            if (nn < nrfpA)
 +            {
 +                warning_error(wi, "Not enough parameters");
 +            }
 +            else if (nn > nrfpA && nn < nrfp)
 +            {
 +                warning_error(wi, "Too many parameters or not enough parameters for topology B");
 +            }
 +            else if (nn > nrfp)
 +            {
 +                warning_error(wi, "Too many parameters");
 +            }
 +            for (i = nn; (i < nrfp); i++)
 +            {
 +                c[i] = 0.0;
 +            }
 +        }
 +    }
 +
 +    for (i = 0; (i < 4); i++)
 +    {
 +        if (!strcmp(alc[i], "X"))
 +        {
 +            p.a[i] = -1;
 +        }
 +        else
 +        {
 +            if ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET)
 +            {
 +                gmx_fatal(FARGS, "Unknown bond_atomtype %s", alc[i]);
 +            }
 +        }
 +    }
 +    for (i = 0; (i < nrfp); i++)
 +    {
 +        p.c[i] = c[i];
 +    }
 +    /* Always use 4 atoms here, since we created two wildcard atoms
 +     * if there wasn't of them 4 already.
 +     */
 +    push_bondtype (&(bt[ftype]), &p, 4, ftype, bAllowRepeat, line, wi);
 +}
 +
 +
 +void push_nbt(directive d, t_nbparam **nbt, gpp_atomtype_t atype,
 +              char *pline, int nb_funct,
 +              warninp_t wi)
 +{
 +    /* swap the atoms */
 +    const char *form3 = "%*s%*s%*s%lf%lf%lf";
 +    const char *form4 = "%*s%*s%*s%lf%lf%lf%lf";
 +    const char *form5 = "%*s%*s%*s%lf%lf%lf%lf%lf";
 +    char        a0[80], a1[80];
 +    int         i, f, n, ftype, nrfp;
 +    double      c[4], dum;
 +    real        cr[4];
 +    int         ai, aj;
 +    t_nbparam  *nbp;
 +    gmx_bool    bId;
 +    char        errbuf[256];
 +
 +    if (sscanf (pline, "%s%s%d", a0, a1, &f) != 3)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +
 +    ftype = ifunc_index(d, f);
 +
 +    if (ftype != nb_funct)
 +    {
 +        sprintf(errbuf, "Trying to add %s while the default nonbond type is %s",
 +                interaction_function[ftype].longname,
 +                interaction_function[nb_funct].longname);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
 +
 +    /* Get the force parameters */
 +    nrfp = NRFP(ftype);
 +    if (ftype == F_LJ14)
 +    {
 +        n = sscanf(pline, form4, &c[0], &c[1], &c[2], &c[3]);
 +        if (n < 2)
 +        {
 +            too_few(wi);
 +            return;
 +        }
 +        /* When the B topology parameters are not set,
 +         * copy them from topology A
 +         */
 +        GMX_ASSERT(nrfp <= 4, "LJ-14 cannot have more than 4 parameters");
 +        for (i = n; i < nrfp; i++)
 +        {
 +            c[i] = c[i-2];
 +        }
 +    }
 +    else if (ftype == F_LJC14_Q)
 +    {
 +        n = sscanf(pline, form5, &c[0], &c[1], &c[2], &c[3], &dum);
 +        if (n != 4)
 +        {
 +            incorrect_n_param(wi);
 +            return;
 +        }
 +    }
 +    else if (nrfp == 2)
 +    {
 +        if (sscanf(pline, form3, &c[0], &c[1], &dum) != 2)
 +        {
 +            incorrect_n_param(wi);
 +            return;
 +        }
 +    }
 +    else if (nrfp == 3)
 +    {
 +        if (sscanf(pline, form4, &c[0], &c[1], &c[2], &dum) != 3)
 +        {
 +            incorrect_n_param(wi);
 +            return;
 +        }
 +    }
 +    else
 +    {
 +        gmx_fatal(FARGS, "Number of force parameters for nonbonded interactions is %d"
 +                  " in file %s, line %d", nrfp, __FILE__, __LINE__);
 +    }
 +    for (i = 0; (i < nrfp); i++)
 +    {
 +        cr[i] = c[i];
 +    }
 +
 +    /* Put the parameters in the matrix */
 +    if ((ai = get_atomtype_type (a0, atype)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Atomtype %s not found", a0);
 +    }
 +    if ((aj = get_atomtype_type (a1, atype)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Atomtype %s not found", a1);
 +    }
 +    nbp = &(nbt[std::max(ai, aj)][std::min(ai, aj)]);
 +
 +    if (nbp->bSet)
 +    {
 +        bId = TRUE;
 +        for (i = 0; i < nrfp; i++)
 +        {
 +            bId = bId && (nbp->c[i] == cr[i]);
 +        }
 +        if (!bId)
 +        {
 +            sprintf(errbuf, "Overriding non-bonded parameters,");
 +            warning(wi, errbuf);
 +            fprintf(stderr, "  old:");
 +            for (i = 0; i < nrfp; i++)
 +            {
 +                fprintf(stderr, " %g", nbp->c[i]);
 +            }
 +            fprintf(stderr, " new\n%s\n", pline);
 +        }
 +    }
 +    nbp->bSet = TRUE;
 +    for (i = 0; i < nrfp; i++)
 +    {
 +        nbp->c[i] = cr[i];
 +    }
 +}
 +
 +void
 +push_gb_params (gpp_atomtype_t at, char *line,
 +                warninp_t wi)
 +{
 +    int    atype;
 +    double radius, vol, surftens, gb_radius, S_hct;
 +    char   atypename[STRLEN];
 +    char   errbuf[STRLEN];
 +
 +    if ( (sscanf(line, "%s%lf%lf%lf%lf%lf", atypename, &radius, &vol, &surftens, &gb_radius, &S_hct)) != 6)
 +    {
 +        sprintf(errbuf, "Too few gb parameters for type %s\n", atypename);
 +        warning(wi, errbuf);
 +    }
 +
 +    /* Search for atomtype */
 +    atype = get_atomtype_type(atypename, at);
 +
 +    if (atype == NOTSET)
 +    {
 +        printf("Couldn't find topology match for atomtype %s\n", atypename);
 +        abort();
 +    }
 +
 +    set_atomtype_gbparam(at, atype, radius, vol, surftens, gb_radius, S_hct);
 +}
 +
 +void
 +push_cmaptype(directive d, t_params bt[], int nral, gpp_atomtype_t at,
 +              t_bond_atomtype bat, char *line,
 +              warninp_t wi)
 +{
-     int          start;
++    const char  *formal = "%s%s%s%s%s%s%s%s%n";
 +
 +    int          i, ft, ftype, nn, nrfp, nrfpA, nrfpB;
-     if ((nn = sscanf(line, formal, alc[0], alc[1], alc[2], alc[3], alc[4], alc[5], alc[6], alc[7])) != nral+3)
++    int          start, nchar_consumed;
 +    int          nxcmap, nycmap, ncmap, read_cmap, sl, nct;
 +    char         s[20], alc[MAXATOMLIST+2][20];
 +    t_param      p;
 +    char         errbuf[256];
 +
 +    /* Keep the compiler happy */
 +    read_cmap = 0;
 +    start     = 0;
 +
-     /* Compute an offset for each line where the cmap parameters start
-      * ie. where the atom types and grid spacing information ends
-      */
-     for (i = 0; i < nn; i++)
-     {
-         start += (int)strlen(alc[i]);
-     }
-     /* There are nn-1 spaces between the atom types and the grid spacing info in the cmap.itp file */
-     /* start is the position on the line where we start to read the actual cmap grid data from the itp file */
-     start = start + nn -1;
++    GMX_ASSERT(nral == 5, "CMAP requires 5 atoms per interaction");
++
++    /* Here we can only check for < 8 */
++    if ((nn = sscanf(line, formal, alc[0], alc[1], alc[2], alc[3], alc[4], alc[5], alc[6], alc[7], &nchar_consumed)) < nral+3)
 +    {
 +        sprintf(errbuf, "Incorrect number of atomtypes for cmap (%d instead of 5)", nn-1);
 +        warning_error(wi, errbuf);
 +        return;
 +    }
++    start += nchar_consumed;
 +
 +    ft     = strtol(alc[nral], NULL, 10);
 +    nxcmap = strtol(alc[nral+1], NULL, 10);
 +    nycmap = strtol(alc[nral+2], NULL, 10);
 +
 +    /* Check for equal grid spacing in x and y dims */
 +    if (nxcmap != nycmap)
 +    {
 +        gmx_fatal(FARGS, "Not the same grid spacing in x and y for cmap grid: x=%d, y=%d", nxcmap, nycmap);
 +    }
 +
 +    ncmap  = nxcmap*nycmap;
 +    ftype  = ifunc_index(d, ft);
 +    nrfpA  = strtol(alc[6], NULL, 10)*strtol(alc[6], NULL, 10);
 +    nrfpB  = strtol(alc[7], NULL, 10)*strtol(alc[7], NULL, 10);
 +    nrfp   = nrfpA+nrfpB;
 +
 +    /* Allocate memory for the CMAP grid */
 +    bt[F_CMAP].ncmap += nrfp;
 +    srenew(bt[F_CMAP].cmap, bt[F_CMAP].ncmap);
 +
 +    /* Read in CMAP parameters */
 +    sl = 0;
 +    for (i = 0; i < ncmap; i++)
 +    {
 +        while (isspace(*(line+start+sl)))
 +        {
 +            sl++;
 +        }
 +        nn  = sscanf(line+start+sl, " %s ", s);
 +        sl += strlen(s);
 +        bt[F_CMAP].cmap[i+(bt[F_CMAP].ncmap)-nrfp] = strtod(s, NULL);
 +
 +        if (nn == 1)
 +        {
 +            read_cmap++;
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "Error in reading cmap parameter for angle %s %s %s %s %s", alc[0], alc[1], alc[2], alc[3], alc[4]);
 +        }
 +
 +    }
 +
 +    /* Check do that we got the number of parameters we expected */
 +    if (read_cmap == nrfpA)
 +    {
 +        for (i = 0; i < ncmap; i++)
 +        {
 +            bt[F_CMAP].cmap[i+ncmap] = bt[F_CMAP].cmap[i];
 +        }
 +    }
 +    else
 +    {
 +        if (read_cmap < nrfpA)
 +        {
 +            warning_error(wi, "Not enough cmap parameters");
 +        }
 +        else if (read_cmap > nrfpA && read_cmap < nrfp)
 +        {
 +            warning_error(wi, "Too many cmap parameters or not enough parameters for topology B");
 +        }
 +        else if (read_cmap > nrfp)
 +        {
 +            warning_error(wi, "Too many cmap parameters");
 +        }
 +    }
 +
 +
 +    /* Set grid spacing and the number of grids (we assume these numbers to be the same for all grids
 +     * so we can safely assign them each time
 +     */
 +    bt[F_CMAP].grid_spacing = nxcmap;            /* Or nycmap, they need to be equal */
 +    bt[F_CMAP].nc           = bt[F_CMAP].nc + 1; /* Since we are incrementing here, we need to subtract later, see (*****) */
 +    nct                     = (nral+1) * bt[F_CMAP].nc;
 +
 +    /* Allocate memory for the cmap_types information */
 +    srenew(bt[F_CMAP].cmap_types, nct);
 +
 +    for (i = 0; (i < nral); i++)
 +    {
 +        if (at && ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown atomtype %s\n", alc[i]);
 +        }
 +        else if (bat && ((p.a[i] = get_bond_atomtype_type(alc[i], bat)) == NOTSET))
 +        {
 +            gmx_fatal(FARGS, "Unknown bond_atomtype %s\n", alc[i]);
 +        }
 +
 +        /* Assign a grid number to each cmap_type */
 +        bt[F_CMAP].cmap_types[bt[F_CMAP].nct++] = get_bond_atomtype_type(alc[i], bat);
 +    }
 +
 +    /* Assign a type number to this cmap */
 +    bt[F_CMAP].cmap_types[bt[F_CMAP].nct++] = bt[F_CMAP].nc-1; /* Since we inremented earlier, we need to subtrac here, to get the types right (****) */
 +
 +    /* Check for the correct number of atoms (again) */
 +    if (bt[F_CMAP].nct != nct)
 +    {
 +        gmx_fatal(FARGS, "Incorrect number of atom types (%d) in cmap type %d\n", nct, bt[F_CMAP].nc);
 +    }
 +
 +    /* Is this correct?? */
 +    for (i = 0; i < MAXFORCEPARAM; i++)
 +    {
 +        p.c[i] = NOTSET;
 +    }
 +
 +    /* Push the bond to the bondlist */
 +    push_bondtype (&(bt[ftype]), &p, nral, ftype, FALSE, line, wi);
 +}
 +
 +
 +static void push_atom_now(t_symtab *symtab, t_atoms *at, int atomnr,
 +                          int atomicnumber,
 +                          int type, char *ctype, int ptype,
 +                          char *resnumberic,
 +                          char *resname, char *name, real m0, real q0,
 +                          int typeB, char *ctypeB, real mB, real qB)
 +{
 +    int           j, resind = 0, resnr;
 +    unsigned char ric;
 +    int           nr = at->nr;
 +
 +    if (((nr == 0) && (atomnr != 1)) || (nr && (atomnr != at->nr+1)))
 +    {
 +        gmx_fatal(FARGS, "Atoms in the .top are not numbered consecutively from 1 (rather, atomnr = %d, while at->nr = %d)", atomnr, at->nr);
 +    }
 +
 +    j = strlen(resnumberic) - 1;
 +    if (isdigit(resnumberic[j]))
 +    {
 +        ric = ' ';
 +    }
 +    else
 +    {
 +        ric = resnumberic[j];
 +        if (j == 0 || !isdigit(resnumberic[j-1]))
 +        {
 +            gmx_fatal(FARGS, "Invalid residue number '%s' for atom %d",
 +                      resnumberic, atomnr);
 +        }
 +    }
 +    resnr = strtol(resnumberic, NULL, 10);
 +
 +    if (nr > 0)
 +    {
 +        resind = at->atom[nr-1].resind;
 +    }
 +    if (nr == 0 || strcmp(resname, *at->resinfo[resind].name) != 0 ||
 +        resnr != at->resinfo[resind].nr ||
 +        ric   != at->resinfo[resind].ic)
 +    {
 +        if (nr == 0)
 +        {
 +            resind = 0;
 +        }
 +        else
 +        {
 +            resind++;
 +        }
 +        at->nres = resind + 1;
 +        srenew(at->resinfo, at->nres);
 +        at->resinfo[resind].name = put_symtab(symtab, resname);
 +        at->resinfo[resind].nr   = resnr;
 +        at->resinfo[resind].ic   = ric;
 +    }
 +    else
 +    {
 +        resind = at->atom[at->nr-1].resind;
 +    }
 +
 +    /* New atom instance
 +     * get new space for arrays
 +     */
 +    srenew(at->atom, nr+1);
 +    srenew(at->atomname, nr+1);
 +    srenew(at->atomtype, nr+1);
 +    srenew(at->atomtypeB, nr+1);
 +
 +    /* fill the list */
 +    at->atom[nr].type  = type;
 +    at->atom[nr].ptype = ptype;
 +    at->atom[nr].q     = q0;
 +    at->atom[nr].m     = m0;
 +    at->atom[nr].typeB = typeB;
 +    at->atom[nr].qB    = qB;
 +    at->atom[nr].mB    = mB;
 +
 +    at->atom[nr].resind     = resind;
 +    at->atom[nr].atomnumber = atomicnumber;
 +    at->atomname[nr]        = put_symtab(symtab, name);
 +    at->atomtype[nr]        = put_symtab(symtab, ctype);
 +    at->atomtypeB[nr]       = put_symtab(symtab, ctypeB);
 +    at->nr++;
 +}
 +
 +void push_cg(t_block *block, int *lastindex, int index, int a)
 +{
 +    if (debug)
 +    {
 +        fprintf (debug, "Index %d, Atom %d\n", index, a);
 +    }
 +
 +    if (((block->nr) && (*lastindex != index)) || (!block->nr))
 +    {
 +        /* add a new block */
 +        block->nr++;
 +        srenew(block->index, block->nr+1);
 +    }
 +    block->index[block->nr] = a + 1;
 +    *lastindex              = index;
 +}
 +
 +void push_atom(t_symtab *symtab, t_block *cgs,
 +               t_atoms *at, gpp_atomtype_t atype, char *line, int *lastcg,
 +               warninp_t wi)
 +{
 +    int           nr, ptype;
 +    int           cgnumber, atomnr, type, typeB, nscan;
 +    char          id[STRLEN], ctype[STRLEN], ctypeB[STRLEN],
 +                  resnumberic[STRLEN], resname[STRLEN], name[STRLEN], check[STRLEN];
 +    double        m, q, mb, qb;
 +    real          m0, q0, mB, qB;
 +
 +    /* Make a shortcut for writing in this molecule  */
 +    nr = at->nr;
 +
 +    /* Fixed parameters */
 +    if (sscanf(line, "%s%s%s%s%s%d",
 +               id, ctype, resnumberic, resname, name, &cgnumber) != 6)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +    sscanf(id, "%d", &atomnr);
 +    if ((type  = get_atomtype_type(ctype, atype)) == NOTSET)
 +    {
 +        gmx_fatal(FARGS, "Atomtype %s not found", ctype);
 +    }
 +    ptype = get_atomtype_ptype(type, atype);
 +
 +    /* Set default from type */
 +    q0    = get_atomtype_qA(type, atype);
 +    m0    = get_atomtype_massA(type, atype);
 +    typeB = type;
 +    qB    = q0;
 +    mB    = m0;
 +
 +    /* Optional parameters */
 +    nscan = sscanf(line, "%*s%*s%*s%*s%*s%*s%lf%lf%s%lf%lf%s",
 +                   &q, &m, ctypeB, &qb, &mb, check);
 +
 +    /* Nasty switch that falls thru all the way down! */
 +    if (nscan > 0)
 +    {
 +        q0 = qB = q;
 +        if (nscan > 1)
 +        {
 +            m0 = mB = m;
 +            if (nscan > 2)
 +            {
 +                if ((typeB = get_atomtype_type(ctypeB, atype)) == NOTSET)
 +                {
 +                    gmx_fatal(FARGS, "Atomtype %s not found", ctypeB);
 +                }
 +                qB = get_atomtype_qA(typeB, atype);
 +                mB = get_atomtype_massA(typeB, atype);
 +                if (nscan > 3)
 +                {
 +                    qB = qb;
 +                    if (nscan > 4)
 +                    {
 +                        mB = mb;
 +                        if (nscan > 5)
 +                        {
 +                            warning_error(wi, "Too many parameters");
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    if (debug)
 +    {
 +        fprintf(debug, "mB=%g, qB=%g, typeB=%d\n", mB, qB, typeB);
 +    }
 +
 +    push_cg(cgs, lastcg, cgnumber, nr);
 +
 +    push_atom_now(symtab, at, atomnr, get_atomtype_atomnumber(type, atype),
 +                  type, ctype, ptype, resnumberic,
 +                  resname, name, m0, q0, typeB,
 +                  typeB == type ? ctype : ctypeB, mB, qB);
 +}
 +
 +void push_molt(t_symtab *symtab, int *nmol, t_molinfo **mol, char *line,
 +               warninp_t wi)
 +{
 +    char       type[STRLEN];
 +    int        nrexcl, i;
 +    t_molinfo *newmol;
 +
 +    if ((sscanf(line, "%s%d", type, &nrexcl)) != 2)
 +    {
 +        warning_error(wi, "Expected a molecule type name and nrexcl");
 +    }
 +
 +    /* Test if this moleculetype overwrites another */
 +    i    = 0;
 +    while (i < *nmol)
 +    {
 +        if (strcmp(*((*mol)[i].name), type) == 0)
 +        {
 +            gmx_fatal(FARGS, "moleculetype %s is redefined", type);
 +        }
 +        i++;
 +    }
 +
 +    (*nmol)++;
 +    srenew(*mol, *nmol);
 +    newmol = &((*mol)[*nmol-1]);
 +    init_molinfo(newmol);
 +
 +    /* Fill in the values */
 +    newmol->name     = put_symtab(symtab, type);
 +    newmol->nrexcl   = nrexcl;
 +    newmol->excl_set = FALSE;
 +}
 +
 +static gmx_bool default_nb_params(int ftype, t_params bt[], t_atoms *at,
 +                                  t_param *p, int c_start, gmx_bool bB, gmx_bool bGenPairs)
 +{
 +    int          i, j, ti, tj, ntype;
 +    gmx_bool     bFound;
 +    t_param     *pi    = NULL;
 +    int          nr    = bt[ftype].nr;
 +    int          nral  = NRAL(ftype);
 +    int          nrfp  = interaction_function[ftype].nrfpA;
 +    int          nrfpB = interaction_function[ftype].nrfpB;
 +
 +    if ((!bB && nrfp == 0) || (bB && nrfpB == 0))
 +    {
 +        return TRUE;
 +    }
 +
 +    bFound = FALSE;
 +    if (bGenPairs)
 +    {
 +        /* First test the generated-pair position to save
 +         * time when we have 1000*1000 entries for e.g. OPLS...
 +         */
 +        ntype = static_cast<int>(std::sqrt(static_cast<double>(nr)));
 +        GMX_ASSERT(ntype * ntype == nr, "Number of pairs of generated non-bonded parameters should be a perfect square");
 +        if (bB)
 +        {
 +            ti = at->atom[p->a[0]].typeB;
 +            tj = at->atom[p->a[1]].typeB;
 +        }
 +        else
 +        {
 +            ti = at->atom[p->a[0]].type;
 +            tj = at->atom[p->a[1]].type;
 +        }
 +        pi     = &(bt[ftype].param[ntype*ti+tj]);
 +        bFound = ((ti == pi->a[0]) && (tj == pi->a[1]));
 +    }
 +
 +    /* Search explicitly if we didnt find it */
 +    if (!bFound)
 +    {
 +        for (i = 0; ((i < nr) && !bFound); i++)
 +        {
 +            pi = &(bt[ftype].param[i]);
 +            if (bB)
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (at->atom[p->a[j]].typeB == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            else
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (at->atom[p->a[j]].type == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            bFound = (j == nral);
 +        }
 +    }
 +
 +    if (bFound)
 +    {
 +        if (bB)
 +        {
 +            if (nrfp+nrfpB > MAXFORCEPARAM)
 +            {
 +                gmx_incons("Too many force parameters");
 +            }
 +            for (j = c_start; (j < nrfpB); j++)
 +            {
 +                p->c[nrfp+j] = pi->c[j];
 +            }
 +        }
 +        else
 +        {
 +            for (j = c_start; (j < nrfp); j++)
 +            {
 +                p->c[j] = pi->c[j];
 +            }
 +        }
 +    }
 +    else
 +    {
 +        for (j = c_start; (j < nrfp); j++)
 +        {
 +            p->c[j] = 0.0;
 +        }
 +    }
 +    return bFound;
 +}
 +
 +static gmx_bool default_cmap_params(t_params bondtype[],
 +                                    t_atoms *at, gpp_atomtype_t atype,
 +                                    t_param *p, gmx_bool bB,
 +                                    int *cmap_type, int *nparam_def)
 +{
 +    int      i, nparam_found;
 +    int      ct;
 +    gmx_bool bFound = FALSE;
 +
 +    nparam_found = 0;
 +    ct           = 0;
 +
 +    /* Match the current cmap angle against the list of cmap_types */
 +    for (i = 0; i < bondtype[F_CMAP].nct && !bFound; i += 6)
 +    {
 +        if (bB)
 +        {
 +
 +        }
 +        else
 +        {
 +            if (
 +                (get_atomtype_batype(at->atom[p->a[0]].type, atype) == bondtype[F_CMAP].cmap_types[i])   &&
 +                (get_atomtype_batype(at->atom[p->a[1]].type, atype) == bondtype[F_CMAP].cmap_types[i+1]) &&
 +                (get_atomtype_batype(at->atom[p->a[2]].type, atype) == bondtype[F_CMAP].cmap_types[i+2]) &&
 +                (get_atomtype_batype(at->atom[p->a[3]].type, atype) == bondtype[F_CMAP].cmap_types[i+3]) &&
 +                (get_atomtype_batype(at->atom[p->a[4]].type, atype) == bondtype[F_CMAP].cmap_types[i+4]))
 +            {
 +                /* Found cmap torsion */
 +                bFound       = TRUE;
 +                ct           = bondtype[F_CMAP].cmap_types[i+5];
 +                nparam_found = 1;
 +            }
 +        }
 +    }
 +
 +    /* If we did not find a matching type for this cmap torsion */
 +    if (!bFound)
 +    {
 +        gmx_fatal(FARGS, "Unknown cmap torsion between atoms %d %d %d %d %d\n",
 +                  p->a[0]+1, p->a[1]+1, p->a[2]+1, p->a[3]+1, p->a[4]+1);
 +    }
 +
 +    *nparam_def = nparam_found;
 +    *cmap_type  = ct;
 +
 +    return bFound;
 +}
 +
 +static gmx_bool default_params(int ftype, t_params bt[],
 +                               t_atoms *at, gpp_atomtype_t atype,
 +                               t_param *p, gmx_bool bB,
 +                               t_param **param_def,
 +                               int *nparam_def)
 +{
 +    int          i, j, nparam_found;
 +    gmx_bool     bFound, bSame;
 +    t_param     *pi    = NULL;
 +    t_param     *pj    = NULL;
 +    int          nr    = bt[ftype].nr;
 +    int          nral  = NRAL(ftype);
 +    int          nrfpA = interaction_function[ftype].nrfpA;
 +    int          nrfpB = interaction_function[ftype].nrfpB;
 +
 +    if ((!bB && nrfpA == 0) || (bB && nrfpB == 0))
 +    {
 +        return TRUE;
 +    }
 +
 +
 +    /* We allow wildcards now. The first type (with or without wildcards) that
 +     * fits is used, so you should probably put the wildcarded bondtypes
 +     * at the end of each section.
 +     */
 +    bFound       = FALSE;
 +    nparam_found = 0;
 +    /* OPLS uses 1000s of dihedraltypes, so in order to speed up the scanning we have a
 +     * special case for this. Check for B state outside loop to speed it up.
 +     */
 +    if (ftype == F_PDIHS || ftype == F_RBDIHS || ftype == F_IDIHS || ftype == F_PIDIHS)
 +    {
 +        if (bB)
 +        {
 +            for (i = 0; ((i < nr) && !bFound); i++)
 +            {
 +                pi     = &(bt[ftype].param[i]);
 +                bFound =
 +                    (
 +                        ((pi->ai() == -1) || (get_atomtype_batype(at->atom[p->ai()].typeB, atype) == pi->ai())) &&
 +                        ((pi->aj() == -1) || (get_atomtype_batype(at->atom[p->aj()].typeB, atype) == pi->aj())) &&
 +                        ((pi->ak() == -1) || (get_atomtype_batype(at->atom[p->ak()].typeB, atype) == pi->ak())) &&
 +                        ((pi->al() == -1) || (get_atomtype_batype(at->atom[p->al()].typeB, atype) == pi->al()))
 +                    );
 +            }
 +        }
 +        else
 +        {
 +            /* State A */
 +            for (i = 0; ((i < nr) && !bFound); i++)
 +            {
 +                pi     = &(bt[ftype].param[i]);
 +                bFound =
 +                    (
 +                        ((pi->ai() == -1) || (get_atomtype_batype(at->atom[p->ai()].type, atype) == pi->ai())) &&
 +                        ((pi->aj() == -1) || (get_atomtype_batype(at->atom[p->aj()].type, atype) == pi->aj())) &&
 +                        ((pi->ak() == -1) || (get_atomtype_batype(at->atom[p->ak()].type, atype) == pi->ak())) &&
 +                        ((pi->al() == -1) || (get_atomtype_batype(at->atom[p->al()].type, atype) == pi->al()))
 +                    );
 +            }
 +        }
 +        /* Find additional matches for this dihedral - necessary for ftype==9 which is used e.g. for charmm.
 +         * The rules in that case is that additional matches HAVE to be on adjacent lines!
 +         */
 +        if (bFound == TRUE)
 +        {
 +            nparam_found++;
 +            bSame = TRUE;
 +            /* Continue from current i value */
 +            for (j = i+1; j < nr && bSame; j += 2)
 +            {
 +                pj    = &(bt[ftype].param[j]);
 +                bSame = (pi->ai() == pj->ai() && pi->aj() == pj->aj() && pi->ak() == pj->ak() && pi->al() == pj->al());
 +                if (bSame)
 +                {
 +                    nparam_found++;
 +                }
 +                /* nparam_found will be increased as long as the numbers match */
 +            }
 +        }
 +    }
 +    else   /* Not a dihedral */
 +    {
 +        for (i = 0; ((i < nr) && !bFound); i++)
 +        {
 +            pi = &(bt[ftype].param[i]);
 +            if (bB)
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (get_atomtype_batype(at->atom[p->a[j]].typeB, atype) == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            else
 +            {
 +                for (j = 0; ((j < nral) &&
 +                             (get_atomtype_batype(at->atom[p->a[j]].type, atype) == pi->a[j])); j++)
 +                {
 +                    ;
 +                }
 +            }
 +            bFound = (j == nral);
 +        }
 +        if (bFound)
 +        {
 +            nparam_found = 1;
 +        }
 +    }
 +
 +    *param_def  = pi;
 +    *nparam_def = nparam_found;
 +
 +    return bFound;
 +}
 +
 +
 +
 +void push_bond(directive d, t_params bondtype[], t_params bond[],
 +               t_atoms *at, gpp_atomtype_t atype, char *line,
 +               gmx_bool bBonded, gmx_bool bGenPairs, real fudgeQQ,
 +               gmx_bool bZero, gmx_bool *bWarn_copy_A_B,
 +               warninp_t wi)
 +{
 +    const char  *aaformat[MAXATOMLIST] = {
 +        "%d%d",
 +        "%d%d%d",
 +        "%d%d%d%d",
 +        "%d%d%d%d%d",
 +        "%d%d%d%d%d%d",
 +        "%d%d%d%d%d%d%d"
 +    };
 +    const char  *asformat[MAXATOMLIST] = {
 +        "%*s%*s",
 +        "%*s%*s%*s",
 +        "%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s",
 +        "%*s%*s%*s%*s%*s%*s%*s"
 +    };
 +    const char  *ccformat = "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf";
 +    int          nr, i, j, nral, nral_fmt, nread, ftype;
 +    char         format[STRLEN];
 +    /* One force parameter more, so we can check if we read too many */
 +    double       cc[MAXFORCEPARAM+1];
 +    int          aa[MAXATOMLIST+1];
 +    t_param      param, *param_defA, *param_defB;
 +    gmx_bool     bFoundA = FALSE, bFoundB = FALSE, bDef, bPert, bSwapParity = FALSE;
 +    int          nparam_defA, nparam_defB;
 +    char         errbuf[256];
 +
 +    nparam_defA = nparam_defB = 0;
 +
 +    ftype = ifunc_index(d, 1);
 +    nral  = NRAL(ftype);
 +    for (j = 0; j < MAXATOMLIST; j++)
 +    {
 +        aa[j] = NOTSET;
 +    }
 +    bDef = (NRFP(ftype) > 0);
 +
 +    if (ftype == F_SETTLE)
 +    {
 +        /* SETTLE acts on 3 atoms, but the topology format only specifies
 +         * the first atom (for historical reasons).
 +         */
 +        nral_fmt = 1;
 +    }
 +    else
 +    {
 +        nral_fmt = nral;
 +    }
 +
 +    nread = sscanf(line, aaformat[nral_fmt-1],
 +                   &aa[0], &aa[1], &aa[2], &aa[3], &aa[4], &aa[5]);
 +
 +    if (ftype == F_SETTLE)
 +    {
 +        aa[3] = aa[1];
 +        aa[1] = aa[0] + 1;
 +        aa[2] = aa[0] + 2;
 +    }
 +
 +    if (nread < nral_fmt)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +    else if (nread > nral_fmt)
 +    {
 +        /* this is a hack to allow for virtual sites with swapped parity */
 +        bSwapParity = (aa[nral] < 0);
 +        if (bSwapParity)
 +        {
 +            aa[nral] = -aa[nral];
 +        }
 +        ftype = ifunc_index(d, aa[nral]);
 +        if (bSwapParity)
 +        {
 +            switch (ftype)
 +            {
 +                case F_VSITE3FAD:
 +                case F_VSITE3OUT:
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "Negative function types only allowed for %s and %s",
 +                              interaction_function[F_VSITE3FAD].longname,
 +                              interaction_function[F_VSITE3OUT].longname);
 +            }
 +        }
 +    }
 +
 +
 +    /* Check for double atoms and atoms out of bounds */
 +    for (i = 0; (i < nral); i++)
 +    {
 +        if (aa[i] < 1 || aa[i] > at->nr)
 +        {
 +            gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                      "Atom index (%d) in %s out of bounds (1-%d).\n"
 +                      "This probably means that you have inserted topology section \"%s\"\n"
 +                      "in a part belonging to a different molecule than you intended to.\n"
 +                      "In that case move the \"%s\" section to the right molecule.",
 +                      get_warning_file(wi), get_warning_line(wi),
 +                      aa[i], dir2str(d), at->nr, dir2str(d), dir2str(d));
 +        }
 +        for (j = i+1; (j < nral); j++)
 +        {
 +            if (aa[i] == aa[j])
 +            {
 +                sprintf(errbuf, "Duplicate atom index (%d) in %s", aa[i], dir2str(d));
 +                warning(wi, errbuf);
 +            }
 +        }
 +    }
 +
 +    /* default force parameters  */
 +    for (j = 0; (j < MAXATOMLIST); j++)
 +    {
 +        param.a[j] = aa[j]-1;
 +    }
 +    for (j = 0; (j < MAXFORCEPARAM); j++)
 +    {
 +        param.c[j] = 0.0;
 +    }
 +
 +    /* Get force params for normal and free energy perturbation
 +     * studies, as determined by types!
 +     */
 +
 +    if (bBonded)
 +    {
 +        bFoundA = default_params(ftype, bondtype, at, atype, &param, FALSE, &param_defA, &nparam_defA);
 +        if (bFoundA)
 +        {
 +            /* Copy the A-state and B-state default parameters. */
 +            GMX_ASSERT(NRFPA(ftype)+NRFPB(ftype) <= MAXFORCEPARAM, "Bonded interactions may have at most 12 parameters");
 +            for (j = 0; (j < NRFPA(ftype)+NRFPB(ftype)); j++)
 +            {
 +                param.c[j] = param_defA->c[j];
 +            }
 +        }
 +        bFoundB = default_params(ftype, bondtype, at, atype, &param, TRUE, &param_defB, &nparam_defB);
 +        if (bFoundB)
 +        {
 +            /* Copy only the B-state default parameters */
 +            for (j = NRFPA(ftype); (j < NRFP(ftype)); j++)
 +            {
 +                param.c[j] = param_defB->c[j];
 +            }
 +        }
 +    }
 +    else if (ftype == F_LJ14)
 +    {
 +        bFoundA = default_nb_params(ftype, bondtype, at, &param, 0, FALSE, bGenPairs);
 +        bFoundB = default_nb_params(ftype, bondtype, at, &param, 0, TRUE, bGenPairs);
 +    }
 +    else if (ftype == F_LJC14_Q)
 +    {
 +        param.c[0] = fudgeQQ;
 +        /* Fill in the A-state charges as default parameters */
 +        param.c[1] = at->atom[param.a[0]].q;
 +        param.c[2] = at->atom[param.a[1]].q;
 +        /* The default LJ parameters are the standard 1-4 parameters */
 +        bFoundA = default_nb_params(F_LJ14, bondtype, at, &param, 3, FALSE, bGenPairs);
 +        bFoundB = TRUE;
 +    }
 +    else if (ftype == F_LJC_PAIRS_NB)
 +    {
 +        /* Defaults are not supported here */
 +        bFoundA = FALSE;
 +        bFoundB = TRUE;
 +    }
 +    else
 +    {
 +        gmx_incons("Unknown function type in push_bond");
 +    }
 +
 +    if (nread > nral_fmt)
 +    {
 +        /* Manually specified parameters - in this case we discard multiple torsion info! */
 +
 +        strcpy(format, asformat[nral_fmt-1]);
 +        strcat(format, ccformat);
 +
 +        nread = sscanf(line, format, &cc[0], &cc[1], &cc[2], &cc[3], &cc[4], &cc[5],
 +                       &cc[6], &cc[7], &cc[8], &cc[9], &cc[10], &cc[11], &cc[12]);
 +
 +        if ((nread == NRFPA(ftype)) && (NRFPB(ftype) != 0))
 +        {
 +            /* We only have to issue a warning if these atoms are perturbed! */
 +            bPert = FALSE;
 +            for (j = 0; (j < nral); j++)
 +            {
 +                bPert = bPert || PERTURBED(at->atom[param.a[j]]);
 +            }
 +
 +            if (bPert && *bWarn_copy_A_B)
 +            {
 +                sprintf(errbuf,
 +                        "Some parameters for bonded interaction involving perturbed atoms are specified explicitly in state A, but not B - copying A to B");
 +                warning(wi, errbuf);
 +                *bWarn_copy_A_B = FALSE;
 +            }
 +
 +            /* If only the A parameters were specified, copy them to the B state */
 +            /* The B-state parameters correspond to the first nrfpB
 +             * A-state parameters.
 +             */
 +            for (j = 0; (j < NRFPB(ftype)); j++)
 +            {
 +                cc[nread++] = cc[j];
 +            }
 +        }
 +
 +        /* If nread was 0 or EOF, no parameters were read => use defaults.
 +         * If nread was nrfpA we copied above so nread=nrfp.
 +         * If nread was nrfp we are cool.
 +         * For F_LJC14_Q we allow supplying fudgeQQ only.
 +         * Anything else is an error!
 +         */
 +        if ((nread != 0) && (nread != EOF) && (nread != NRFP(ftype)) &&
 +            !(ftype == F_LJC14_Q && nread == 1))
 +        {
 +            gmx_fatal(FARGS, "Incorrect number of parameters - found %d, expected %d or %d for %s.",
 +                      nread, NRFPA(ftype), NRFP(ftype),
 +                      interaction_function[ftype].longname);
 +        }
 +
 +        for (j = 0; (j < nread); j++)
 +        {
 +            param.c[j] = cc[j];
 +        }
 +
 +        /* Check whether we have to use the defaults */
 +        if (nread == NRFP(ftype))
 +        {
 +            bDef = FALSE;
 +        }
 +    }
 +    else
 +    {
 +        nread = 0;
 +    }
 +    /* nread now holds the number of force parameters read! */
 +
 +    if (bDef)
 +    {
 +        /* Use defaults */
 +        /* When we have multiple terms it would be very dangerous to allow perturbations to a different atom type! */
 +        if (ftype == F_PDIHS)
 +        {
 +            if ((nparam_defA != nparam_defB) || ((nparam_defA > 1 || nparam_defB > 1) && (param_defA != param_defB)))
 +            {
 +                sprintf(errbuf,
 +                        "Cannot automatically perturb a torsion with multiple terms to different form.\n"
 +                        "Please specify perturbed parameters manually for this torsion in your topology!");
 +                warning_error(wi, errbuf);
 +            }
 +        }
 +
 +        if (nread > 0 && nread < NRFPA(ftype))
 +        {
 +            /* Issue an error, do not use defaults */
 +            sprintf(errbuf, "Not enough parameters, there should be at least %d (or 0 for defaults)", NRFPA(ftype));
 +            warning_error(wi, errbuf);
 +        }
 +
 +        if (nread == 0 || nread == EOF)
 +        {
 +            if (!bFoundA)
 +            {
 +                if (interaction_function[ftype].flags & IF_VSITE)
 +                {
 +                    /* set them to NOTSET, will be calculated later */
 +                    for (j = 0; (j < MAXFORCEPARAM); j++)
 +                    {
 +                        param.c[j] = NOTSET;
 +                    }
 +
 +                    if (bSwapParity)
 +                    {
 +                        param.c1() = -1; /* flag to swap parity of vsite construction */
 +                    }
 +                }
 +                else
 +                {
 +                    if (bZero)
 +                    {
 +                        fprintf(stderr, "NOTE: No default %s types, using zeroes\n",
 +                                interaction_function[ftype].longname);
 +                    }
 +                    else
 +                    {
 +                        sprintf(errbuf, "No default %s types", interaction_function[ftype].longname);
 +                        warning_error(wi, errbuf);
 +                    }
 +                }
 +            }
 +            else
 +            {
 +                if (bSwapParity)
 +                {
 +                    switch (ftype)
 +                    {
 +                        case F_VSITE3FAD:
 +                            param.c0() = 360 - param.c0();
 +                            break;
 +                        case F_VSITE3OUT:
 +                            param.c2() = -param.c2();
 +                            break;
 +                    }
 +                }
 +            }
 +            if (!bFoundB)
 +            {
 +                /* We only have to issue a warning if these atoms are perturbed! */
 +                bPert = FALSE;
 +                for (j = 0; (j < nral); j++)
 +                {
 +                    bPert = bPert || PERTURBED(at->atom[param.a[j]]);
 +                }
 +
 +                if (bPert)
 +                {
 +                    sprintf(errbuf, "No default %s types for perturbed atoms, "
 +                            "using normal values", interaction_function[ftype].longname);
 +                    warning(wi, errbuf);
 +                }
 +            }
 +        }
 +    }
 +
 +    if ((ftype == F_PDIHS || ftype == F_ANGRES || ftype == F_ANGRESZ)
 +        && param.c[5] != param.c[2])
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             %s multiplicity can not be perturbed %f!=%f",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  interaction_function[ftype].longname,
 +                  param.c[2], param.c[5]);
 +    }
 +
 +    if (IS_TABULATED(ftype) && param.c[2] != param.c[0])
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             %s table number can not be perturbed %d!=%d",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  interaction_function[ftype].longname,
 +                  (int)(param.c[0]+0.5), (int)(param.c[2]+0.5));
 +    }
 +
 +    /* Dont add R-B dihedrals where all parameters are zero (no interaction) */
 +    if (ftype == F_RBDIHS)
 +    {
 +        nr = 0;
 +        for (i = 0; i < NRFP(ftype); i++)
 +        {
 +            if (param.c[i] != 0)
 +            {
 +                nr++;
 +            }
 +        }
 +        if (nr == 0)
 +        {
 +            return;
 +        }
 +    }
 +
 +    /* Put the values in the appropriate arrays */
 +    add_param_to_list (&bond[ftype], &param);
 +
 +    /* Push additional torsions from FF for ftype==9 if we have them.
 +     * We have already checked that the A/B states do not differ in this case,
 +     * so we do not have to double-check that again, or the vsite stuff.
 +     * In addition, those torsions cannot be automatically perturbed.
 +     */
 +    if (bDef && ftype == F_PDIHS)
 +    {
 +        for (i = 1; i < nparam_defA; i++)
 +        {
 +            /* Advance pointer! */
 +            param_defA += 2;
 +            for (j = 0; (j < NRFPA(ftype)+NRFPB(ftype)); j++)
 +            {
 +                param.c[j] = param_defA->c[j];
 +            }
 +            /* And push the next term for this torsion */
 +            add_param_to_list (&bond[ftype], &param);
 +        }
 +    }
 +}
 +
 +void push_cmap(directive d, t_params bondtype[], t_params bond[],
 +               t_atoms *at, gpp_atomtype_t atype, char *line,
 +               warninp_t wi)
 +{
 +    const char *aaformat[MAXATOMLIST+1] =
 +    {
 +        "%d",
 +        "%d%d",
 +        "%d%d%d",
 +        "%d%d%d%d",
 +        "%d%d%d%d%d",
 +        "%d%d%d%d%d%d",
 +        "%d%d%d%d%d%d%d"
 +    };
 +
 +    int      i, j, ftype, nral, nread, ncmap_params;
 +    int      cmap_type;
 +    int      aa[MAXATOMLIST+1];
 +    char     errbuf[256];
 +    gmx_bool bFound;
 +    t_param  param;
 +
 +    ftype        = ifunc_index(d, 1);
 +    nral         = NRAL(ftype);
 +    ncmap_params = 0;
 +
 +    nread = sscanf(line, aaformat[nral-1],
 +                   &aa[0], &aa[1], &aa[2], &aa[3], &aa[4], &aa[5]);
 +
 +    if (nread < nral)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +    else if (nread == nral)
 +    {
 +        ftype = ifunc_index(d, 1);
 +    }
 +
 +    /* Check for double atoms and atoms out of bounds */
 +    for (i = 0; i < nral; i++)
 +    {
 +        if (aa[i] < 1 || aa[i] > at->nr)
 +        {
 +            gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                      "Atom index (%d) in %s out of bounds (1-%d).\n"
 +                      "This probably means that you have inserted topology section \"%s\"\n"
 +                      "in a part belonging to a different molecule than you intended to.\n"
 +                      "In that case move the \"%s\" section to the right molecule.",
 +                      get_warning_file(wi), get_warning_line(wi),
 +                      aa[i], dir2str(d), at->nr, dir2str(d), dir2str(d));
 +        }
 +
 +        for (j = i+1; (j < nral); j++)
 +        {
 +            if (aa[i] == aa[j])
 +            {
 +                sprintf(errbuf, "Duplicate atom index (%d) in %s", aa[i], dir2str(d));
 +                warning(wi, errbuf);
 +            }
 +        }
 +    }
 +
 +    /* default force parameters  */
 +    for (j = 0; (j < MAXATOMLIST); j++)
 +    {
 +        param.a[j] = aa[j]-1;
 +    }
 +    for (j = 0; (j < MAXFORCEPARAM); j++)
 +    {
 +        param.c[j] = 0.0;
 +    }
 +
 +    /* Get the cmap type for this cmap angle */
 +    bFound = default_cmap_params(bondtype, at, atype, &param, FALSE, &cmap_type, &ncmap_params);
 +
 +    /* We want exactly one parameter (the cmap type in state A (currently no state B) back */
 +    if (bFound && ncmap_params == 1)
 +    {
 +        /* Put the values in the appropriate arrays */
 +        param.c[0] = cmap_type;
 +        add_param_to_list(&bond[ftype], &param);
 +    }
 +    else
 +    {
 +        /* This is essentially the same check as in default_cmap_params() done one more time */
 +        gmx_fatal(FARGS, "Unable to assign a cmap type to torsion %d %d %d %d and %d\n",
 +                  param.a[0]+1, param.a[1]+1, param.a[2]+1, param.a[3]+1, param.a[4]+1);
 +    }
 +}
 +
 +
 +
 +void push_vsitesn(directive d, t_params bond[],
 +                  t_atoms *at, char *line,
 +                  warninp_t wi)
 +{
 +    char   *ptr;
 +    int     type, ftype, j, n, ret, nj, a;
 +    int    *atc    = NULL;
 +    double *weight = NULL, weight_tot;
 +    t_param param;
 +
 +    /* default force parameters  */
 +    for (j = 0; (j < MAXATOMLIST); j++)
 +    {
 +        param.a[j] = NOTSET;
 +    }
 +    for (j = 0; (j < MAXFORCEPARAM); j++)
 +    {
 +        param.c[j] = 0.0;
 +    }
 +
 +    ptr  = line;
 +    ret  = sscanf(ptr, "%d%n", &a, &n);
 +    ptr += n;
 +    if (ret == 0)
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             Expected an atom index in section \"%s\"",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  dir2str(d));
 +    }
 +
 +    param.a[0] = a - 1;
 +
 +    sscanf(ptr, "%d%n", &type, &n);
 +    ptr  += n;
 +    ftype = ifunc_index(d, type);
 +
 +    weight_tot = 0;
 +    nj         = 0;
 +    do
 +    {
 +        ret  = sscanf(ptr, "%d%n", &a, &n);
 +        ptr += n;
 +        if (ret > 0)
 +        {
 +            if (nj % 20 == 0)
 +            {
 +                srenew(atc, nj+20);
 +                srenew(weight, nj+20);
 +            }
 +            atc[nj] = a - 1;
 +            switch (type)
 +            {
 +                case 1:
 +                    weight[nj] = 1;
 +                    break;
 +                case 2:
 +                    /* Here we use the A-state mass as a parameter.
 +                     * Note that the B-state mass has no influence.
 +                     */
 +                    weight[nj] = at->atom[atc[nj]].m;
 +                    break;
 +                case 3:
 +                    weight[nj] = -1;
 +                    ret        = sscanf(ptr, "%lf%n", &(weight[nj]), &n);
 +                    ptr       += n;
 +                    if (weight[nj] < 0)
 +                    {
 +                        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                                  "             No weight or negative weight found for vsiten constructing atom %d (atom index %d)",
 +                                  get_warning_file(wi), get_warning_line(wi),
 +                                  nj+1, atc[nj]+1);
 +                    }
 +                    break;
 +                default:
 +                    gmx_fatal(FARGS, "Unknown vsiten type %d", type);
 +            }
 +            weight_tot += weight[nj];
 +            nj++;
 +        }
 +    }
 +    while (ret > 0);
 +
 +    if (nj == 0)
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             Expected more than one atom index in section \"%s\"",
 +                  get_warning_file(wi), get_warning_line(wi),
 +                  dir2str(d));
 +    }
 +
 +    if (weight_tot == 0)
 +    {
 +        gmx_fatal(FARGS, "[ file %s, line %d ]:\n"
 +                  "             The total mass of the construting atoms is zero",
 +                  get_warning_file(wi), get_warning_line(wi));
 +    }
 +
 +    for (j = 0; j < nj; j++)
 +    {
 +        param.a[1] = atc[j];
 +        param.c[0] = nj;
 +        param.c[1] = weight[j]/weight_tot;
 +        /* Put the values in the appropriate arrays */
 +        add_param_to_list (&bond[ftype], &param);
 +    }
 +
 +    sfree(atc);
 +    sfree(weight);
 +}
 +
 +void push_mol(int nrmols, t_molinfo mols[], char *pline, int *whichmol,
 +              int *nrcopies,
 +              warninp_t wi)
 +{
 +    char type[STRLEN];
 +
 +    if (sscanf(pline, "%s%d", type, nrcopies) != 2)
 +    {
 +        too_few(wi);
 +        return;
 +    }
 +
 +    /* Search moleculename.
 +     * Here we originally only did case insensitive matching. But because
 +     * some PDB files can have many chains and use case to generate more
 +     * chain-identifiers, which in turn end up in our moleculetype name,
 +     * we added support for case-sensitivity.
 +     */
 +    int nrcs    = 0;
 +    int nrci    = 0;
 +    int matchci = -1;
 +    int matchcs = -1;
 +    for (int i = 0; i < nrmols; i++)
 +    {
 +        if (strcmp(type, *(mols[i].name)) == 0)
 +        {
 +            nrcs++;
 +            matchcs = i;
 +        }
 +        if (gmx_strcasecmp(type, *(mols[i].name)) == 0)
 +        {
 +            nrci++;
 +            matchci = i;
 +        }
 +    }
 +
 +    if (nrcs == 1)
 +    {
 +        // select the case sensitive match
 +        *whichmol = matchcs;
 +    }
 +    else
 +    {
 +        // avoid matching case-insensitive when we have multiple matches
 +        if (nrci > 1)
 +        {
 +            gmx_fatal(FARGS, "For moleculetype '%s' in [ system ] %d case insensitive matches, but %d case sensitive matches were found. Check the case of the characters in the moleculetypes.", type, nrci, nrcs);
 +        }
 +        if (nrci == 1)
 +        {
 +            // select the unique case insensitive match
 +            *whichmol = matchci;
 +        }
 +        else
 +        {
 +            gmx_fatal(FARGS, "No such moleculetype %s", type);
 +        }
 +    }
 +}
 +
 +void init_block2(t_block2 *b2, int natom)
 +{
 +    int i;
 +
 +    b2->nr = natom;
 +    snew(b2->nra, b2->nr);
 +    snew(b2->a, b2->nr);
 +    for (i = 0; (i < b2->nr); i++)
 +    {
 +        b2->a[i] = NULL;
 +    }
 +}
 +
 +void done_block2(t_block2 *b2)
 +{
 +    int i;
 +
 +    if (b2->nr)
 +    {
 +        for (i = 0; (i < b2->nr); i++)
 +        {
 +            sfree(b2->a[i]);
 +        }
 +        sfree(b2->a);
 +        sfree(b2->nra);
 +        b2->nr = 0;
 +    }
 +}
 +
 +void push_excl(char *line, t_block2 *b2)
 +{
 +    int  i, j;
 +    int  n;
 +    char base[STRLEN], format[STRLEN];
 +
 +    if (sscanf(line, "%d", &i) == 0)
 +    {
 +        return;
 +    }
 +
 +    if ((1 <= i) && (i <= b2->nr))
 +    {
 +        i--;
 +    }
 +    else
 +    {
 +        if (debug)
 +        {
 +            fprintf(debug, "Unbound atom %d\n", i-1);
 +        }
 +        return;
 +    }
 +    strcpy(base, "%*d");
 +    do
 +    {
 +        strcpy(format, base);
 +        strcat(format, "%d");
 +        n = sscanf(line, format, &j);
 +        if (n == 1)
 +        {
 +            if ((1 <= j) && (j <= b2->nr))
 +            {
 +                j--;
 +                srenew(b2->a[i], ++(b2->nra[i]));
 +                b2->a[i][b2->nra[i]-1] = j;
 +                /* also add the reverse exclusion! */
 +                srenew(b2->a[j], ++(b2->nra[j]));
 +                b2->a[j][b2->nra[j]-1] = i;
 +                strcat(base, "%*d");
 +            }
 +            else
 +            {
 +                gmx_fatal(FARGS, "Invalid Atomnr j: %d, b2->nr: %d\n", j, b2->nr);
 +            }
 +        }
 +    }
 +    while (n == 1);
 +}
 +
 +void b_to_b2(t_blocka *b, t_block2 *b2)
 +{
 +    int     i;
 +    int     j, a;
 +
 +    for (i = 0; (i < b->nr); i++)
 +    {
 +        for (j = b->index[i]; (j < b->index[i+1]); j++)
 +        {
 +            a = b->a[j];
 +            srenew(b2->a[i], ++b2->nra[i]);
 +            b2->a[i][b2->nra[i]-1] = a;
 +        }
 +    }
 +}
 +
 +void b2_to_b(t_block2 *b2, t_blocka *b)
 +{
 +    int     i, nra;
 +    int     j;
 +
 +    nra = 0;
 +    for (i = 0; (i < b2->nr); i++)
 +    {
 +        b->index[i] = nra;
 +        for (j = 0; (j < b2->nra[i]); j++)
 +        {
 +            b->a[nra+j] = b2->a[i][j];
 +        }
 +        nra += b2->nra[i];
 +    }
 +    /* terminate list */
 +    b->index[i] = nra;
 +}
 +
 +static int icomp(const void *v1, const void *v2)
 +{
 +    return (*((int *) v1))-(*((int *) v2));
 +}
 +
 +void merge_excl(t_blocka *excl, t_block2 *b2)
 +{
 +    int     i, k;
 +    int     j;
 +    int     nra;
 +
 +    if (!b2->nr)
 +    {
 +        return;
 +    }
 +    else if (b2->nr != excl->nr)
 +    {
 +        gmx_fatal(FARGS, "DEATH HORROR: b2->nr = %d, while excl->nr = %d",
 +                  b2->nr, excl->nr);
 +    }
 +    else if (debug)
 +    {
 +        fprintf(debug, "Entering merge_excl\n");
 +    }
 +
 +    /* First copy all entries from excl to b2 */
 +    b_to_b2(excl, b2);
 +
 +    /* Count and sort the exclusions */
 +    nra = 0;
 +    for (i = 0; (i < b2->nr); i++)
 +    {
 +        if (b2->nra[i] > 0)
 +        {
 +            /* remove double entries */
 +            qsort(b2->a[i], (size_t)b2->nra[i], (size_t)sizeof(b2->a[i][0]), icomp);
 +            k = 1;
 +            for (j = 1; (j < b2->nra[i]); j++)
 +            {
 +                if (b2->a[i][j] != b2->a[i][k-1])
 +                {
 +                    b2->a[i][k] = b2->a[i][j];
 +                    k++;
 +                }
 +            }
 +            b2->nra[i] = k;
 +            nra       += b2->nra[i];
 +        }
 +    }
 +    excl->nra = nra;
 +    srenew(excl->a, excl->nra);
 +
 +    b2_to_b(b2, excl);
 +}
 +
 +int add_atomtype_decoupled(t_symtab *symtab, gpp_atomtype_t at,
 +                           t_nbparam ***nbparam, t_nbparam ***pair)
 +{
 +    t_atom  atom;
 +    t_param param;
 +    int     i, nr;
 +
 +    /* Define an atom type with all parameters set to zero (no interactions) */
 +    atom.q = 0.0;
 +    atom.m = 0.0;
 +    /* Type for decoupled atoms could be anything,
 +     * this should be changed automatically later when required.
 +     */
 +    atom.ptype = eptAtom;
 +    for (i = 0; (i < MAXFORCEPARAM); i++)
 +    {
 +        param.c[i] = 0.0;
 +    }
 +
 +    nr = add_atomtype(at, symtab, &atom, "decoupled", &param, -1, 0.0, 0.0, 0.0, 0, 0, 0);
 +
 +    /* Add space in the non-bonded parameters matrix */
 +    realloc_nb_params(at, nbparam, pair);
 +
 +    return nr;
 +}
 +
 +static void convert_pairs_to_pairsQ(t_params *plist,
 +                                    real fudgeQQ, t_atoms *atoms)
 +{
 +    t_param *paramp1, *paramp2, *paramnew;
 +    int      i, j, p1nr, p2nr, p2newnr;
 +
 +    /* Add the pair list to the pairQ list */
 +    p1nr    = plist[F_LJ14].nr;
 +    p2nr    = plist[F_LJC14_Q].nr;
 +    p2newnr = p1nr + p2nr;
 +    snew(paramnew, p2newnr);
 +
 +    paramp1             = plist[F_LJ14].param;
 +    paramp2             = plist[F_LJC14_Q].param;
 +
 +    /* Fill in the new F_LJC14_Q array with the old one. NOTE:
 +       it may be possible to just ADD the converted F_LJ14 array
 +       to the old F_LJC14_Q array, but since we have to create
 +       a new sized memory structure, better just to deep copy it all.
 +     */
 +
 +    for (i = 0; i < p2nr; i++)
 +    {
 +        /* Copy over parameters */
 +        for (j = 0; j < 5; j++) /* entries are 0-4 for F_LJC14_Q */
 +        {
 +            paramnew[i].c[j] = paramp2[i].c[j];
 +        }
 +
 +        /* copy over atoms */
 +        for (j = 0; j < 2; j++)
 +        {
 +            paramnew[i].a[j] = paramp2[i].a[j];
 +        }
 +    }
 +
 +    for (i = p2nr; i < p2newnr; i++)
 +    {
 +        j             = i-p2nr;
 +
 +        /* Copy over parameters */
 +        paramnew[i].c[0] = fudgeQQ;
 +        paramnew[i].c[1] = atoms->atom[paramp1[j].a[0]].q;
 +        paramnew[i].c[2] = atoms->atom[paramp1[j].a[1]].q;
 +        paramnew[i].c[3] = paramp1[j].c[0];
 +        paramnew[i].c[4] = paramp1[j].c[1];
 +
 +        /* copy over atoms */
 +        paramnew[i].a[0] = paramp1[j].a[0];
 +        paramnew[i].a[1] = paramp1[j].a[1];
 +    }
 +
 +    /* free the old pairlists */
 +    sfree(plist[F_LJC14_Q].param);
 +    sfree(plist[F_LJ14].param);
 +
 +    /* now assign the new data to the F_LJC14_Q structure */
 +    plist[F_LJC14_Q].param   = paramnew;
 +    plist[F_LJC14_Q].nr      = p2newnr;
 +
 +    /* Empty the LJ14 pairlist */
 +    plist[F_LJ14].nr    = 0;
 +    plist[F_LJ14].param = NULL;
 +}
 +
 +static void generate_LJCpairsNB(t_molinfo *mol, int nb_funct, t_params *nbp)
 +{
 +    int       n, ntype, i, j, k;
 +    t_atom   *atom;
 +    t_blocka *excl;
 +    gmx_bool  bExcl;
 +    t_param   param;
 +
 +    n    = mol->atoms.nr;
 +    atom = mol->atoms.atom;
 +
 +    ntype = static_cast<int>(std::sqrt(static_cast<double>(nbp->nr)));
 +    GMX_ASSERT(ntype * ntype == nbp->nr, "Number of pairs of generated non-bonded parameters should be a perfect square");
 +
 +    for (i = 0; i < MAXATOMLIST; i++)
 +    {
 +        param.a[i] = NOTSET;
 +    }
 +    for (i = 0; i < MAXFORCEPARAM; i++)
 +    {
 +        param.c[i] = NOTSET;
 +    }
 +
 +    /* Add a pair interaction for all non-excluded atom pairs */
 +    excl = &mol->excls;
 +    for (i = 0; i < n; i++)
 +    {
 +        for (j = i+1; j < n; j++)
 +        {
 +            bExcl = FALSE;
 +            for (k = excl->index[i]; k < excl->index[i+1]; k++)
 +            {
 +                if (excl->a[k] == j)
 +                {
 +                    bExcl = TRUE;
 +                }
 +            }
 +            if (!bExcl)
 +            {
 +                if (nb_funct != F_LJ)
 +                {
 +                    gmx_fatal(FARGS, "Can only generate non-bonded pair interactions for Van der Waals type Lennard-Jones");
 +                }
 +                param.a[0] = i;
 +                param.a[1] = j;
 +                param.c[0] = atom[i].q;
 +                param.c[1] = atom[j].q;
 +                param.c[2] = nbp->param[ntype*atom[i].type+atom[j].type].c[0];
 +                param.c[3] = nbp->param[ntype*atom[i].type+atom[j].type].c[1];
 +                add_param_to_list(&mol->plist[F_LJC_PAIRS_NB], &param);
 +            }
 +        }
 +    }
 +}
 +
 +static void set_excl_all(t_blocka *excl)
 +{
 +    int nat, i, j, k;
 +
 +    /* Get rid of the current exclusions and exclude all atom pairs */
 +    nat       = excl->nr;
 +    excl->nra = nat*nat;
 +    srenew(excl->a, excl->nra);
 +    k = 0;
 +    for (i = 0; i < nat; i++)
 +    {
 +        excl->index[i] = k;
 +        for (j = 0; j < nat; j++)
 +        {
 +            excl->a[k++] = j;
 +        }
 +    }
 +    excl->index[nat] = k;
 +}
 +
 +static void decouple_atoms(t_atoms *atoms, int atomtype_decouple,
 +                           int couple_lam0, int couple_lam1,
 +                           const char *mol_name)
 +{
 +    int i;
 +
 +    for (i = 0; i < atoms->nr; i++)
 +    {
 +        t_atom *atom;
 +
 +        atom = &atoms->atom[i];
 +
 +        if (atom->qB != atom->q || atom->typeB != atom->type)
 +        {
 +            gmx_fatal(FARGS, "Atom %d in molecule type '%s' has different A and B state charges and/or atom types set in the topology file as well as through the mdp option '%s'. You can not use both these methods simultaneously.",
 +                      i + 1, mol_name, "couple-moltype");
 +        }
 +
 +        if (couple_lam0 == ecouplamNONE || couple_lam0 == ecouplamVDW)
 +        {
 +            atom->q     = 0.0;
 +        }
 +        if (couple_lam0 == ecouplamNONE || couple_lam0 == ecouplamQ)
 +        {
 +            atom->type  = atomtype_decouple;
 +        }
 +        if (couple_lam1 == ecouplamNONE || couple_lam1 == ecouplamVDW)
 +        {
 +            atom->qB    = 0.0;
 +        }
 +        if (couple_lam1 == ecouplamNONE || couple_lam1 == ecouplamQ)
 +        {
 +            atom->typeB = atomtype_decouple;
 +        }
 +    }
 +}
 +
 +void convert_moltype_couple(t_molinfo *mol, int atomtype_decouple, real fudgeQQ,
 +                            int couple_lam0, int couple_lam1,
 +                            gmx_bool bCoupleIntra, int nb_funct, t_params *nbp)
 +{
 +    convert_pairs_to_pairsQ(mol->plist, fudgeQQ, &mol->atoms);
 +    if (!bCoupleIntra)
 +    {
 +        generate_LJCpairsNB(mol, nb_funct, nbp);
 +        set_excl_all(&mol->excls);
 +    }
 +    decouple_atoms(&mol->atoms, atomtype_decouple, couple_lam0, couple_lam1,
 +                   *mol->name);
 +}
index ba73710a20e0bb8831ed5f7dd867edc981d6da57,b019306d7c4cfbb031a2203b2ad6931af326050a..1d13d3550aa96b1714352adf5480c530b706b628
   */
  #include "gmxpre.h"
  
 +#include "forcerec.h"
 +
  #include "config.h"
  
  #include <assert.h>
 -#include <math.h>
  #include <stdlib.h>
  #include <string.h>
  
 +#include <cmath>
 +
  #include <algorithm>
  
  #include "gromacs/domdec/domdec.h"
 +#include "gromacs/domdec/domdec_struct.h"
  #include "gromacs/ewald/ewald.h"
 -#include "gromacs/gmxlib/gpu_utils/gpu_utils.h"
 -#include "gromacs/legacyheaders/copyrite.h"
 -#include "gromacs/legacyheaders/force.h"
 -#include "gromacs/legacyheaders/gmx_detect_hardware.h"
 -#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
 -#include "gromacs/legacyheaders/inputrec.h"
 -#include "gromacs/legacyheaders/macros.h"
 -#include "gromacs/legacyheaders/md_logging.h"
 -#include "gromacs/legacyheaders/md_support.h"
 -#include "gromacs/legacyheaders/names.h"
 -#include "gromacs/legacyheaders/network.h"
 -#include "gromacs/legacyheaders/nonbonded.h"
 -#include "gromacs/legacyheaders/ns.h"
 -#include "gromacs/legacyheaders/qmmm.h"
 -#include "gromacs/legacyheaders/tables.h"
 -#include "gromacs/legacyheaders/txtdump.h"
 -#include "gromacs/legacyheaders/typedefs.h"
 -#include "gromacs/legacyheaders/types/commrec.h"
 +#include "gromacs/fileio/filetypes.h"
 +#include "gromacs/gmxlib/md_logging.h"
 +#include "gromacs/gmxlib/network.h"
 +#include "gromacs/gmxlib/nonbonded/nonbonded.h"
 +#include "gromacs/gpu_utils/gpu_utils.h"
 +#include "gromacs/hardware/detecthardware.h"
  #include "gromacs/listed-forces/manage-threading.h"
  #include "gromacs/math/calculate-ewald-splitting-coefficient.h"
 +#include "gromacs/math/functions.h"
  #include "gromacs/math/units.h"
  #include "gromacs/math/utilities.h"
  #include "gromacs/math/vec.h"
 +#include "gromacs/mdlib/force.h"
  #include "gromacs/mdlib/forcerec-threading.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/md_support.h"
  #include "gromacs/mdlib/nb_verlet.h"
  #include "gromacs/mdlib/nbnxn_atomdata.h"
  #include "gromacs/mdlib/nbnxn_gpu_data_mgmt.h"
  #include "gromacs/mdlib/nbnxn_search.h"
  #include "gromacs/mdlib/nbnxn_simd.h"
 +#include "gromacs/mdlib/nbnxn_util.h"
 +#include "gromacs/mdlib/ns.h"
 +#include "gromacs/mdlib/qmmm.h"
 +#include "gromacs/mdlib/sim_util.h"
 +#include "gromacs/mdtypes/commrec.h"
 +#include "gromacs/mdtypes/fcdata.h"
 +#include "gromacs/mdtypes/group.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
  #include "gromacs/pbcutil/ishift.h"
  #include "gromacs/pbcutil/pbc.h"
  #include "gromacs/simd/simd.h"
 +#include "gromacs/tables/forcetable.h"
  #include "gromacs/topology/mtop_util.h"
 +#include "gromacs/trajectory/trajectoryframe.h"
 +#include "gromacs/utility/cstringutil.h"
  #include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/pleasecite.h"
  #include "gromacs/utility/smalloc.h"
 +#include "gromacs/utility/stringutil.h"
  
  #include "nbnxn_gpu_jit_support.h"
  
 +const char *egrp_nm[egNR+1] = {
 +    "Coul-SR", "LJ-SR", "Buck-SR",
 +    "Coul-14", "LJ-14", NULL
 +};
 +
  t_forcerec *mk_forcerec(void)
  {
      t_forcerec *fr;
@@@ -176,6 -161,7 +176,6 @@@ static real *make_ljpme_c6grid(const gm
      int        i, j, k, atnr;
      real       c6, c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
      real      *grid;
 -    const real oneOverSix = 1.0 / 6.0;
  
      /* For LJ-PME simulations, we correct the energies with the reciprocal space
       * inside of the cut-off. To do this the non-bonded kernels needs to have
              c12i = idef->iparams[i*(atnr+1)].lj.c12;
              c6j  = idef->iparams[j*(atnr+1)].lj.c6;
              c12j = idef->iparams[j*(atnr+1)].lj.c12;
 -            c6   = sqrt(c6i * c6j);
 +            c6   = std::sqrt(c6i * c6j);
              if (fr->ljpme_combination_rule == eljpmeLB
                  && !gmx_numzero(c6) && !gmx_numzero(c12i) && !gmx_numzero(c12j))
              {
 -                sigmai = pow(c12i / c6i, oneOverSix);
 -                sigmaj = pow(c12j / c6j, oneOverSix);
 +                sigmai = gmx::sixthroot(c12i / c6i);
 +                sigmaj = gmx::sixthroot(c12j / c6j);
                  epsi   = c6i * c6i / c12i;
                  epsj   = c6j * c6j / c12j;
 -                c6     = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 6);
 +                c6     = std::sqrt(epsi * epsj) * gmx::power6(0.5*(sigmai+sigmaj));
              }
              /* Store the elements at the same relative positions as C6 in nbfp in order
               * to simplify access in the kernels
@@@ -217,6 -203,7 +217,6 @@@ static real *mk_nbfp_combination_rule(c
      int        i, j, atnr;
      real       c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
      real       c6, c12;
 -    const real oneOverSix = 1.0 / 6.0;
  
      atnr = idef->atnr;
      snew(nbfp, 2*atnr*atnr);
              c12i = idef->iparams[i*(atnr+1)].lj.c12;
              c6j  = idef->iparams[j*(atnr+1)].lj.c6;
              c12j = idef->iparams[j*(atnr+1)].lj.c12;
 -            c6   = sqrt(c6i  * c6j);
 -            c12  = sqrt(c12i * c12j);
 +            c6   = std::sqrt(c6i  * c6j);
 +            c12  = std::sqrt(c12i * c12j);
              if (comb_rule == eCOMB_ARITHMETIC
                  && !gmx_numzero(c6) && !gmx_numzero(c12))
              {
 -                sigmai = pow(c12i / c6i, oneOverSix);
 -                sigmaj = pow(c12j / c6j, oneOverSix);
 +                sigmai = gmx::sixthroot(c12i / c6i);
 +                sigmaj = gmx::sixthroot(c12j / c6j);
                  epsi   = c6i * c6i / c12i;
                  epsj   = c6j * c6j / c12j;
 -                c6     = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 6);
 -                c12    = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 12);
 +                c6     = std::sqrt(epsi * epsj) * gmx::power6(0.5*(sigmai+sigmaj));
 +                c12    = std::sqrt(epsi * epsj) * gmx::power12(0.5*(sigmai+sigmaj));
              }
              C6(nbfp, atnr, i, j)   = c6*6.0;
              C12(nbfp, atnr, i, j)  = c12*12.0;
@@@ -1284,8 -1271,9 +1284,8 @@@ static void set_bham_b_max(FILE *fplog
      }
  }
  
 -static void make_nbf_tables(FILE *fp, const output_env_t oenv,
 +static void make_nbf_tables(FILE *fp,
                              t_forcerec *fr, real rtab,
 -                            const t_commrec *cr,
                              const char *tabfn, char *eg1, char *eg2,
                              t_nblists *nbl)
  {
          sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "_%s_%s.%s",
                  eg1, eg2, ftp2ext(efXVG));
      }
 -    nbl->table_elec_vdw = make_tables(fp, oenv, fr, MASTER(cr), buf, rtab, 0);
 +    nbl->table_elec_vdw = make_tables(fp, fr, buf, rtab, 0);
      /* Copy the contents of the table to separate coulomb and LJ tables too,
       * to improve cache performance.
       */
       * the table data to be aligned to 16-byte. The pointers could be freed
       * but currently aren't.
       */
 -    nbl->table_elec.interaction   = GMX_TABLE_INTERACTION_ELEC;
 -    nbl->table_elec.format        = nbl->table_elec_vdw.format;
 -    nbl->table_elec.r             = nbl->table_elec_vdw.r;
 -    nbl->table_elec.n             = nbl->table_elec_vdw.n;
 -    nbl->table_elec.scale         = nbl->table_elec_vdw.scale;
 -    nbl->table_elec.scale_exp     = nbl->table_elec_vdw.scale_exp;
 -    nbl->table_elec.formatsize    = nbl->table_elec_vdw.formatsize;
 -    nbl->table_elec.ninteractions = 1;
 -    nbl->table_elec.stride        = nbl->table_elec.formatsize * nbl->table_elec.ninteractions;
 -    snew_aligned(nbl->table_elec.data, nbl->table_elec.stride*(nbl->table_elec.n+1), 32);
 -
 -    nbl->table_vdw.interaction   = GMX_TABLE_INTERACTION_VDWREP_VDWDISP;
 -    nbl->table_vdw.format        = nbl->table_elec_vdw.format;
 -    nbl->table_vdw.r             = nbl->table_elec_vdw.r;
 -    nbl->table_vdw.n             = nbl->table_elec_vdw.n;
 -    nbl->table_vdw.scale         = nbl->table_elec_vdw.scale;
 -    nbl->table_vdw.scale_exp     = nbl->table_elec_vdw.scale_exp;
 -    nbl->table_vdw.formatsize    = nbl->table_elec_vdw.formatsize;
 -    nbl->table_vdw.ninteractions = 2;
 -    nbl->table_vdw.stride        = nbl->table_vdw.formatsize * nbl->table_vdw.ninteractions;
 -    snew_aligned(nbl->table_vdw.data, nbl->table_vdw.stride*(nbl->table_vdw.n+1), 32);
 -
 -    for (i = 0; i <= nbl->table_elec_vdw.n; i++)
 +    snew(nbl->table_elec, 1);
 +    nbl->table_elec->interaction   = GMX_TABLE_INTERACTION_ELEC;
 +    nbl->table_elec->format        = nbl->table_elec_vdw->format;
 +    nbl->table_elec->r             = nbl->table_elec_vdw->r;
 +    nbl->table_elec->n             = nbl->table_elec_vdw->n;
 +    nbl->table_elec->scale         = nbl->table_elec_vdw->scale;
 +    nbl->table_elec->formatsize    = nbl->table_elec_vdw->formatsize;
 +    nbl->table_elec->ninteractions = 1;
 +    nbl->table_elec->stride        = nbl->table_elec->formatsize * nbl->table_elec->ninteractions;
 +    snew_aligned(nbl->table_elec->data, nbl->table_elec->stride*(nbl->table_elec->n+1), 32);
 +
 +    snew(nbl->table_vdw, 1);
 +    nbl->table_vdw->interaction   = GMX_TABLE_INTERACTION_VDWREP_VDWDISP;
 +    nbl->table_vdw->format        = nbl->table_elec_vdw->format;
 +    nbl->table_vdw->r             = nbl->table_elec_vdw->r;
 +    nbl->table_vdw->n             = nbl->table_elec_vdw->n;
 +    nbl->table_vdw->scale         = nbl->table_elec_vdw->scale;
 +    nbl->table_vdw->formatsize    = nbl->table_elec_vdw->formatsize;
 +    nbl->table_vdw->ninteractions = 2;
 +    nbl->table_vdw->stride        = nbl->table_vdw->formatsize * nbl->table_vdw->ninteractions;
 +    snew_aligned(nbl->table_vdw->data, nbl->table_vdw->stride*(nbl->table_vdw->n+1), 32);
 +
 +    for (i = 0; i <= nbl->table_elec_vdw->n; i++)
      {
          for (j = 0; j < 4; j++)
          {
 -            nbl->table_elec.data[4*i+j] = nbl->table_elec_vdw.data[12*i+j];
 +            nbl->table_elec->data[4*i+j] = nbl->table_elec_vdw->data[12*i+j];
          }
          for (j = 0; j < 8; j++)
          {
 -            nbl->table_vdw.data[8*i+j] = nbl->table_elec_vdw.data[12*i+4+j];
 +            nbl->table_vdw->data[8*i+j] = nbl->table_elec_vdw->data[12*i+4+j];
          }
      }
  }
@@@ -1442,6 -1430,11 +1442,6 @@@ void forcerec_set_ranges(t_forcerec *fr
      if (fr->natoms_force_constr > fr->nalloc_force)
      {
          fr->nalloc_force = over_alloc_dd(fr->natoms_force_constr);
 -
 -        if (fr->bTwinRange)
 -        {
 -            srenew(fr->f_twin, fr->nalloc_force);
 -        }
      }
  
      if (fr->bF_NoVirSum)
@@@ -1469,6 -1462,37 +1469,6 @@@ static real cutoff_inf(real cutoff
      return cutoff;
  }
  
 -static void make_adress_tf_tables(FILE *fp, const output_env_t oenv,
 -                                  t_forcerec *fr, const t_inputrec *ir,
 -                                  const char *tabfn, const gmx_mtop_t *mtop,
 -                                  matrix     box)
 -{
 -    char buf[STRLEN];
 -    int  i, j;
 -
 -    if (tabfn == NULL)
 -    {
 -        gmx_fatal(FARGS, "No thermoforce table file given. Use -tabletf to specify a file\n");
 -        return;
 -    }
 -
 -    snew(fr->atf_tabs, ir->adress->n_tf_grps);
 -
 -    sprintf(buf, "%s", tabfn);
 -    for (i = 0; i < ir->adress->n_tf_grps; i++)
 -    {
 -        j = ir->adress->tf_table_index[i]; /* get energy group index */
 -        sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "tf_%s.%s",
 -                *(mtop->groups.grpname[mtop->groups.grps[egcENER].nm_ind[j]]), ftp2ext(efXVG));
 -        if (fp)
 -        {
 -            fprintf(fp, "loading tf table for energygrp index %d from %s\n", ir->adress->tf_table_index[i], buf);
 -        }
 -        fr->atf_tabs[i] = make_atf_table(fp, oenv, fr, buf, box);
 -    }
 -
 -}
 -
  gmx_bool can_use_allvsall(const t_inputrec *ir, gmx_bool bPrintNote, t_commrec *cr, FILE *fp)
  {
      gmx_bool bAllvsAll;
@@@ -1535,15 -1559,6 +1535,6 @@@ gmx_bool nbnxn_gpu_acceleration_support
          return FALSE;
      }
  
-     if (ir->vdwtype == evdwPME && ir->ljpme_combination_rule == eljpmeLB)
-     {
-         /* LJ PME with LB combination rule does 7 mesh operations.
-          * This so slow that we don't compile GPU non-bonded kernels for that.
-          */
-         md_print_warn(cr, fplog, "LJ-PME with Lorentz-Berthelot is not supported with GPUs, falling back to CPU only\n");
-         return FALSE;
-     }
      return TRUE;
  }
  
@@@ -1571,7 -1586,7 +1562,7 @@@ static void pick_nbnxn_kernel_cpu(cons
      *kernel_type = nbnxnk4x4_PlainC;
      *ewald_excl  = ewaldexclTable;
  
 -#ifdef GMX_NBNXN_SIMD
 +#if GMX_SIMD
      {
  #ifdef GMX_NBNXN_SIMD_4XN
          *kernel_type = nbnxnk4xN_SIMD_4xN;
           */
          *kernel_type = nbnxnk4xN_SIMD_4xN;
  
 -#ifndef GMX_SIMD_HAVE_FMA
 +#if !GMX_SIMD_HAVE_FMA
          if (EEL_PME_EWALD(ir->coulombtype) ||
              EVDW_PME(ir->vdwtype))
          {
           * In single precision, this is faster on Bulldozer.
           */
  #if GMX_SIMD_REAL_WIDTH >= 8 || \
 -        (GMX_SIMD_REAL_WIDTH >= 4 && defined GMX_SIMD_HAVE_FMA && !defined GMX_DOUBLE) || \
 -        defined GMX_SIMD_IBM_QPX
 +        (GMX_SIMD_REAL_WIDTH >= 4 && GMX_SIMD_HAVE_FMA && !GMX_DOUBLE) || GMX_SIMD_IBM_QPX
          *ewald_excl = ewaldexclAnalytical;
  #endif
          if (getenv("GMX_NBNXN_EWALD_TABLE") != NULL)
          }
  
      }
 -#endif /* GMX_NBNXN_SIMD */
 +#endif // GMX_SIMD
  }
  
  
@@@ -1668,11 -1684,23 +1659,11 @@@ const char *lookup_nbnxn_kernel_name(in
              break;
          case nbnxnk4xN_SIMD_4xN:
          case nbnxnk4xN_SIMD_2xNN:
 -#ifdef GMX_NBNXN_SIMD
 -#if defined GMX_SIMD_X86_SSE2
 -            returnvalue = "SSE2";
 -#elif defined GMX_SIMD_X86_SSE4_1
 -            returnvalue = "SSE4.1";
 -#elif defined GMX_SIMD_X86_AVX_128_FMA
 -            returnvalue = "AVX_128_FMA";
 -#elif defined GMX_SIMD_X86_AVX_256
 -            returnvalue = "AVX_256";
 -#elif defined GMX_SIMD_X86_AVX2_256
 -            returnvalue = "AVX2_256";
 -#else
 +#if GMX_SIMD
              returnvalue = "SIMD";
 -#endif
 -#else  /* GMX_NBNXN_SIMD */
 +#else  // GMX_SIMD
              returnvalue = "not available";
 -#endif /* GMX_NBNXN_SIMD */
 +#endif // GMX_SIMD
              break;
          case nbnxnk8x8x8_GPU: returnvalue    = "GPU"; break;
          case nbnxnk8x8x8_PlainC: returnvalue = "plain C"; break;
@@@ -1732,8 -1760,8 +1723,8 @@@ static void pick_nbnxn_kernel(FIL
      {
          fprintf(fp, "\nUsing %s %dx%d non-bonded kernels\n\n",
                  lookup_nbnxn_kernel_name(*kernel_type),
 -                nbnxn_kernel_to_ci_size(*kernel_type),
 -                nbnxn_kernel_to_cj_size(*kernel_type));
 +                nbnxn_kernel_to_cluster_i_size(*kernel_type),
 +                nbnxn_kernel_to_cluster_j_size(*kernel_type));
  
          if (nbnxnk4x4_PlainC == *kernel_type ||
              nbnxnk8x8x8_PlainC == *kernel_type)
@@@ -1907,9 -1935,9 +1898,9 @@@ static void force_switch_constants(rea
       * force/p   = r^-(p+1) + c2*r^2 + c3*r^3
       * potential = r^-p + c2/3*r^3 + c3/4*r^4 + cpot
       */
 -    sc->c2   =  ((p + 1)*rsw - (p + 4)*rc)/(pow(rc, p + 2)*pow(rc - rsw, 2));
 -    sc->c3   = -((p + 1)*rsw - (p + 3)*rc)/(pow(rc, p + 2)*pow(rc - rsw, 3));
 -    sc->cpot = -pow(rc, -p) + p*sc->c2/3*pow(rc - rsw, 3) + p*sc->c3/4*pow(rc - rsw, 4);
 +    sc->c2   =  ((p + 1)*rsw - (p + 4)*rc)/(pow(rc, p + 2)*gmx::square(rc - rsw));
 +    sc->c3   = -((p + 1)*rsw - (p + 3)*rc)/(pow(rc, p + 2)*gmx::power3(rc - rsw));
 +    sc->cpot = -pow(rc, -p) + p*sc->c2/3*gmx::power3(rc - rsw) + p*sc->c3/4*gmx::power4(rc - rsw);
  }
  
  static void potential_switch_constants(real rsw, real rc,
       * force      = force*dsw - potential*sw
       * potential *= sw
       */
 -    sc->c3 = -10*pow(rc - rsw, -3);
 -    sc->c4 =  15*pow(rc - rsw, -4);
 -    sc->c5 =  -6*pow(rc - rsw, -5);
 +    sc->c3 = -10/gmx::power3(rc - rsw);
 +    sc->c4 =  15/gmx::power4(rc - rsw);
 +    sc->c5 =  -6/gmx::power5(rc - rsw);
  }
  
  /*! \brief Construct interaction constants
@@@ -1940,6 -1968,8 +1931,6 @@@ init_interaction_const(FIL
                         const t_forcerec           *fr)
  {
      interaction_const_t *ic;
 -    const real           minusSix          = -6.0;
 -    const real           minusTwelve       = -12.0;
  
      snew(ic, 1);
  
      snew_aligned(ic->tabq_coul_V, 16, 32);
  
      ic->rlist           = fr->rlist;
 -    ic->rlistlong       = fr->rlistlong;
  
      /* Lennard-Jones */
      ic->vdwtype         = fr->vdwtype;
      {
          case eintmodPOTSHIFT:
              /* Only shift the potential, don't touch the force */
 -            ic->dispersion_shift.cpot = -pow(ic->rvdw, minusSix);
 -            ic->repulsion_shift.cpot  = -pow(ic->rvdw, minusTwelve);
 +            ic->dispersion_shift.cpot = -1.0/gmx::power6(ic->rvdw);
 +            ic->repulsion_shift.cpot  = -1.0/gmx::power12(ic->rvdw);
              if (EVDW_PME(ic->vdwtype))
              {
                  real crc2;
  
 -                crc2            = sqr(ic->ewaldcoeff_lj*ic->rvdw);
 -                ic->sh_lj_ewald = (exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)*pow(ic->rvdw, minusSix);
 +                crc2            = gmx::square(ic->ewaldcoeff_lj*ic->rvdw);
 +                ic->sh_lj_ewald = (std::exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)/gmx::power6(ic->rvdw);
              }
              break;
          case eintmodFORCESWITCH:
  
      if (fr->coulomb_modifier == eintmodPOTSHIFT)
      {
 -        ic->sh_ewald = gmx_erfc(ic->ewaldcoeff_q*ic->rcoulomb);
 +        ic->sh_ewald = std::erfc(ic->ewaldcoeff_q*ic->rcoulomb);
      }
      else
      {
@@@ -2197,7 -2228,7 +2188,7 @@@ static void init_nb_verlet(FIL
      {
          /* init the NxN GPU data; the last argument tells whether we'll have
           * both local and non-local NB calculation on GPU */
 -        nbnxn_gpu_init(fp, &nbv->gpu_nbv,
 +        nbnxn_gpu_init(&nbv->gpu_nbv,
                         &fr->hwinfo->gpu_info,
                         fr->gpu_opt,
                         fr->ic,
           * texture objects are used), but as this is initialization code, there
           * is no point in complicating things.
           */
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
          if (PAR(cr))
          {
              gmx_barrier(cr);
              char *end;
  
              nbv->min_ci_balanced = strtol(env, &end, 10);
 -            if (!end || (*end != 0) || nbv->min_ci_balanced <= 0)
 +            if (!end || (*end != 0) || nbv->min_ci_balanced < 0)
              {
 -                gmx_fatal(FARGS, "Invalid value passed in GMX_NB_MIN_CI=%s, positive integer required", env);
 +                gmx_fatal(FARGS, "Invalid value passed in GMX_NB_MIN_CI=%s, non-negative integer required", env);
              }
  
              if (debug)
@@@ -2262,6 -2293,7 +2253,6 @@@ gmx_bool usingGpu(nonbonded_verlet_t *n
  }
  
  void init_forcerec(FILE              *fp,
 -                   const output_env_t oenv,
                     t_forcerec        *fr,
                     t_fcdata          *fcd,
                     const t_inputrec  *ir,
                     const t_commrec   *cr,
                     matrix             box,
                     const char        *tabfn,
 -                   const char        *tabafn,
                     const char        *tabpfn,
                     const char        *tabbfn,
                     const char        *nbpu_opt,
          fr->n_tpi = 0;
      }
  
 -    /* Copy AdResS parameters */
 -    if (ir->bAdress)
 +    if (ir->coulombtype == eelRF_NEC_UNSUPPORTED)
      {
 -        fr->adress_type           = ir->adress->type;
 -        fr->adress_const_wf       = ir->adress->const_wf;
 -        fr->adress_ex_width       = ir->adress->ex_width;
 -        fr->adress_hy_width       = ir->adress->hy_width;
 -        fr->adress_icor           = ir->adress->icor;
 -        fr->adress_site           = ir->adress->site;
 -        fr->adress_ex_forcecap    = ir->adress->ex_forcecap;
 -        fr->adress_do_hybridpairs = ir->adress->do_hybridpairs;
 -
 -
 -        snew(fr->adress_group_explicit, ir->adress->n_energy_grps);
 -        for (i = 0; i < ir->adress->n_energy_grps; i++)
 -        {
 -            fr->adress_group_explicit[i] = ir->adress->group_explicit[i];
 -        }
 +        gmx_fatal(FARGS, "%s electrostatics is no longer supported",
 +                  eel_names[ir->coulombtype]);
 +    }
  
 -        fr->n_adress_tf_grps = ir->adress->n_tf_grps;
 -        snew(fr->adress_tf_table_index, fr->n_adress_tf_grps);
 -        for (i = 0; i < fr->n_adress_tf_grps; i++)
 -        {
 -            fr->adress_tf_table_index[i] = ir->adress->tf_table_index[i];
 -        }
 -        copy_rvec(ir->adress->refs, fr->adress_refs);
 +    if (ir->bAdress)
 +    {
 +        gmx_fatal(FARGS, "AdResS simulations are no longer supported");
      }
 -    else
 +    if (ir->useTwinRange)
      {
 -        fr->adress_type           = eAdressOff;
 -        fr->adress_do_hybridpairs = FALSE;
 +        gmx_fatal(FARGS, "Twin-range simulations are no longer supported");
      }
 -
      /* Copy the user determined parameters */
      fr->userint1  = ir->userint1;
      fr->userint2  = ir->userint2;
      if (ir->fepvals->bScCoul)
      {
          fr->sc_alphacoul  = ir->fepvals->sc_alpha;
 -        fr->sc_sigma6_min = pow(ir->fepvals->sc_sigma_min, 6);
 +        fr->sc_sigma6_min = gmx::power6(ir->fepvals->sc_sigma_min);
      }
      else
      {
      }
      fr->sc_power      = ir->fepvals->sc_power;
      fr->sc_r_power    = ir->fepvals->sc_r_power;
 -    fr->sc_sigma6_def = pow(ir->fepvals->sc_sigma, 6);
 +    fr->sc_sigma6_def = gmx::power6(ir->fepvals->sc_sigma);
  
      env = getenv("GMX_SCSIGMA_MIN");
      if (env != NULL)
      {
          dbl = 0;
          sscanf(env, "%20lf", &dbl);
 -        fr->sc_sigma6_min = pow(dbl, 6);
 +        fr->sc_sigma6_min = gmx::power6(dbl);
          if (fp)
          {
              fprintf(fp, "Setting the minimum soft core sigma to %g nm\n", dbl);
      copy_rvec(ir->posres_com, fr->posres_com);
      copy_rvec(ir->posres_comB, fr->posres_comB);
      fr->rlist                    = cutoff_inf(ir->rlist);
 -    fr->rlistlong                = cutoff_inf(ir->rlistlong);
      fr->eeltype                  = ir->coulombtype;
      fr->vdwtype                  = ir->vdwtype;
      fr->ljpme_combination_rule   = ir->ljpme_combination_rule;
  
          case eelRF:
          case eelGRF:
 -        case eelRF_NEC:
              fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD;
              break;
  
      fr->rcoulomb         = cutoff_inf(ir->rcoulomb);
      fr->rcoulomb_switch  = ir->rcoulomb_switch;
  
 -    fr->bTwinRange = fr->rlistlong > fr->rlist;
      fr->bEwald     = (EEL_PME(fr->eeltype) || fr->eeltype == eelEWALD);
  
      fr->reppow     = mtop->ffparams.reppow;
  
          if (fp)
          {
 -            fprintf(fp, "Table routines are used for coulomb: %s\n", bool_names[fr->bcoultab]);
 -            fprintf(fp, "Table routines are used for vdw:     %s\n", bool_names[fr->bvdwtab ]);
 +            fprintf(fp, "Table routines are used for coulomb: %s\n",
 +                    gmx::boolToString(fr->bcoultab));
 +            fprintf(fp, "Table routines are used for vdw:     %s\n",
 +                    gmx::boolToString(fr->bvdwtab));
          }
  
          if (fr->bvdwtab == TRUE)
      fr->bF_NoVirSum = (EEL_FULL(fr->eeltype) || EVDW_PME(fr->vdwtype) ||
                         gmx_mtop_ftype_count(mtop, F_POSRES) > 0 ||
                         gmx_mtop_ftype_count(mtop, F_FBPOSRES) > 0 ||
 -                       IR_ELEC_FIELD(*ir) ||
 -                       (fr->adress_icor != eAdressICOff)
 +                       inputrecElecField(ir)
                         );
  
      if (fr->cutoff_scheme == ecutsGROUP &&
      }
  
      fr->eDispCorr = ir->eDispCorr;
 +    fr->numAtomsForDispersionCorrection = mtop->natoms;
      if (ir->eDispCorr != edispcNO)
      {
          set_avcsixtwelve(fp, fr, mtop);
      /* Generate the GB table if needed */
      if (fr->bGB)
      {
 -#ifdef GMX_DOUBLE
 +#if GMX_DOUBLE
          fr->gbtabscale = 2000;
  #else
          fr->gbtabscale = 500;
  #endif
  
          fr->gbtabr = 100;
 -        fr->gbtab  = make_gb_table(oenv, fr);
 +        fr->gbtab  = make_gb_table(fr);
  
          init_gb(&fr->born, fr, ir, mtop, ir->gb_algorithm);
  
          }
      }
  
 -    if (ir->adress)
 -    {
 -        fr->nnblists *= 2;
 -    }
 -
      snew(fr->nblists, fr->nnblists);
  
      /* This code automatically gives table length tabext without cut-off's,
       * in that case grompp should already have checked that we do not need
       * normal tables and we only generate tables for 1-4 interactions.
       */
 -    rtab = ir->rlistlong + ir->tabext;
 +    rtab = ir->rlist + ir->tabext;
  
      if (bMakeTables)
      {
          /* make tables for ordinary interactions */
          if (bSomeNormalNbListsAreInUse)
          {
 -            make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, NULL, NULL, &fr->nblists[0]);
 -            if (ir->adress)
 -            {
 -                make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, NULL, NULL, &fr->nblists[fr->nnblists/2]);
 -            }
 +            make_nbf_tables(fp, fr, rtab, tabfn, NULL, NULL, &fr->nblists[0]);
              if (!bMakeSeparate14Table)
              {
                  fr->tab14 = fr->nblists[0].table_elec_vdw;
                              fr->gid2nblists[GID(egi, egj, ir->opts.ngener)] = m;
                          }
                          /* Read the table file with the two energy groups names appended */
 -                        make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn,
 +                        make_nbf_tables(fp, fr, rtab, tabfn,
                                          *mtop->groups.grpname[nm_ind[egi]],
                                          *mtop->groups.grpname[nm_ind[egj]],
                                          &fr->nblists[m]);
 -                        if (ir->adress)
 -                        {
 -                            make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn,
 -                                            *mtop->groups.grpname[nm_ind[egi]],
 -                                            *mtop->groups.grpname[nm_ind[egj]],
 -                                            &fr->nblists[fr->nnblists/2+m]);
 -                        }
                          m++;
                      }
                      else if (fr->nnblists > 1)
          /* Tables might not be used for the potential modifier interactions per se, but
           * we still need them to evaluate switch/shift dispersion corrections in this case.
           */
 -        make_nbf_tables(fp, oenv, fr, rtab, cr, tabfn, NULL, NULL, &fr->nblists[0]);
 +        make_nbf_tables(fp, fr, rtab, tabfn, NULL, NULL, &fr->nblists[0]);
      }
  
      if (bMakeSeparate14Table)
      {
          /* generate extra tables with plain Coulomb for 1-4 interactions only */
 -        fr->tab14 = make_tables(fp, oenv, fr, MASTER(cr), tabpfn, rtab,
 +        fr->tab14 = make_tables(fp, fr, tabpfn, rtab,
                                  GMX_MAKETABLES_14ONLY);
      }
  
 -    /* Read AdResS Thermo Force table if needed */
 -    if (fr->adress_icor == eAdressICThermoForce)
 -    {
 -        /* old todo replace */
 -
 -        if (ir->adress->n_tf_grps > 0)
 -        {
 -            make_adress_tf_tables(fp, oenv, fr, ir, tabfn, mtop, box);
 -
 -        }
 -        else
 -        {
 -            /* load the default table */
 -            snew(fr->atf_tabs, 1);
 -            fr->atf_tabs[DEFAULT_TF_TABLE] = make_atf_table(fp, oenv, fr, tabafn, box);
 -        }
 -    }
 -
      /* Wall stuff */
      fr->nwall = ir->nwall;
      if (ir->nwall && ir->wall_type == ewtTABLE)
      {
 -        make_wall_tables(fp, oenv, ir, tabfn, &mtop->groups, fr);
 +        make_wall_tables(fp, ir, tabfn, &mtop->groups, fr);
      }
  
      if (fcd && tabbfn)
      fr->timesteps = 0;
  
      /* Initialize neighbor search */
 -    init_ns(fp, cr, &fr->ns, fr, mtop);
 +    snew(fr->ns, 1);
 +    init_ns(fp, cr, fr->ns, fr, mtop);
  
      if (cr->duty & DUTY_PP)
      {
          gmx_nonbonded_setup(fr, bGenericKernelOnly);
 -        /*
 -           if (ir->bAdress)
 -            {
 -                gmx_setup_adress_kernels(fp,bGenericKernelOnly);
 -            }
 -         */
      }
  
      /* Initialize the thread working data for bonded interactions */
 -    init_bonded_threading(fp, fr, mtop->groups.grps[egcENER].nr);
 +    init_bonded_threading(fp, mtop->groups.grps[egcENER].nr,
 +                          &fr->bonded_threading);
  
 -    snew(fr->excl_load, fr->nthreads+1);
 +    fr->nthread_ewc = gmx_omp_nthreads_get(emntBonded);
 +    snew(fr->ewc_t, fr->nthread_ewc);
 +    snew(fr->excl_load, fr->nthread_ewc + 1);
  
      /* fr->ic is used both by verlet and group kernels (to some extent) now */
      init_interaction_const(fp, &fr->ic, fr);
  
  #define pr_real(fp, r) fprintf(fp, "%s: %e\n",#r, r)
  #define pr_int(fp, i)  fprintf((fp), "%s: %d\n",#i, i)
 -#define pr_bool(fp, b) fprintf((fp), "%s: %s\n",#b, bool_names[b])
 +#define pr_bool(fp, b) fprintf((fp), "%s: %s\n",#b, gmx::boolToString(b))
  
  void pr_forcerec(FILE *fp, t_forcerec *fr)
  {
      pr_real(fp, fr->rcoulomb);
      pr_real(fp, fr->fudgeQQ);
      pr_bool(fp, fr->bGrid);
 -    pr_bool(fp, fr->bTwinRange);
      /*pr_int(fp,fr->cg0);
         pr_int(fp,fr->hcg);*/
      for (i = 0; i < fr->nnblists; i++)
      {
 -        pr_int(fp, fr->nblists[i].table_elec_vdw.n);
 +        pr_int(fp, fr->nblists[i].table_elec_vdw->n);
      }
      pr_real(fp, fr->rcoulomb_switch);
      pr_real(fp, fr->rcoulomb);
@@@ -3214,9 -3304,9 +3205,9 @@@ void forcerec_set_excl_load(t_forcere
      fr->excl_load[0] = 0;
      n                = 0;
      i                = 0;
 -    for (t = 1; t <= fr->nthreads; t++)
 +    for (t = 1; t <= fr->nthread_ewc; t++)
      {
 -        ntarget = (ntot*t)/fr->nthreads;
 +        ntarget = (ntot*t)/fr->nthread_ewc;
          while (i < top->excls.nr && n < ntarget)
          {
              for (j = ind[i]; j < ind[i+1]; j++)
@@@ -3259,7 -3349,7 +3250,7 @@@ void free_gpu_resources(const t_forcere
           * Note: as only PP ranks need to free GPU resources, so it is safe to
           * not call the barrier on PME ranks.
           */
 -#ifdef GMX_THREAD_MPI
 +#if GMX_THREAD_MPI
          if (PAR(cr))
          {
              gmx_barrier(cr);
index 7cf2114f381e5d2fa9ee002ad1e999ac553b0359,0000000000000000000000000000000000000000..14bdadede10ddc133513f026582c71a28e8b9c10
mode 100644,000000..100644
--- /dev/null
@@@ -1,4173 -1,0 +1,4181 @@@
-                             gmx_bool progBal, int nsp_tot_est,
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +
 +#include "gmxpre.h"
 +
 +#include "nbnxn_search.h"
 +
 +#include "config.h"
 +
 +#include <assert.h>
 +#include <string.h>
 +
 +#include <cmath>
 +
 +#include <algorithm>
 +
 +#include "gromacs/domdec/domdec_struct.h"
 +#include "gromacs/gmxlib/nrnb.h"
 +#include "gromacs/math/functions.h"
 +#include "gromacs/math/utilities.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/nb_verlet.h"
 +#include "gromacs/mdlib/nbnxn_atomdata.h"
 +#include "gromacs/mdlib/nbnxn_consts.h"
 +#include "gromacs/mdlib/nbnxn_grid.h"
 +#include "gromacs/mdlib/nbnxn_internal.h"
 +#include "gromacs/mdlib/nbnxn_simd.h"
 +#include "gromacs/mdlib/nbnxn_util.h"
 +#include "gromacs/mdlib/ns.h"
 +#include "gromacs/mdtypes/group.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/pbcutil/ishift.h"
 +#include "gromacs/pbcutil/pbc.h"
 +#include "gromacs/simd/simd.h"
 +#include "gromacs/simd/vector_operations.h"
 +#include "gromacs/utility/exceptions.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +using namespace gmx; // TODO: Remove when this file is moved into gmx namespace
 +
 +
 +/* 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.
 + */
 +static const bool pbc_shift_backward = true;
 +
 +
 +static void nbs_cycle_clear(nbnxn_cycle_t *cc)
 +{
 +    for (int 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)
 +{
 +    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 (int t = 0; t < nbs->nthread_max; t++)
 +        {
 +            fprintf(fp, " %4.1f",
 +                    Mcyc_av(&nbs->work[t].cc[enbsCCsearch]));
 +        }
 +    }
 +    fprintf(fp, "\n");
 +}
 +
 +static gmx_inline int ci_to_cj(int ci, int na_cj_2log)
 +{
 +    switch (na_cj_2log)
 +    {
 +        case 2: return ci;     break;
 +        case 3: return (ci>>1); break;
 +        case 1: return (ci<<1); break;
 +    }
 +
 +    return 0;
 +}
 +
 +#if GMX_SIMD
 +
 +/* Returns the j-cluster index corresponding to the i-cluster index */
 +template<int cj_size> static gmx_inline int ci_to_cj(int ci)
 +{
 +    if (cj_size == 2)
 +    {
 +        return ci << 1;
 +    }
 +    else if (cj_size == 4)
 +    {
 +        return ci;
 +    }
 +    else if (cj_size == 8)
 +    {
 +        return ci >> 1;
 +    }
 +    else
 +    {
 +        GMX_ASSERT(false, "Only j-cluster sizes 2, 4, 8 are implemented");
 +        return -1;
 +    }
 +}
 +
 +/* Returns the index in the coordinate array corresponding to the i-cluster index */
 +template<int cj_size> static gmx_inline int x_ind_ci(int ci)
 +{
 +    if (cj_size <= 4)
 +    {
 +        /* Coordinates are stored packed in groups of 4 */
 +        return ci*STRIDE_P4;
 +    }
 +    else if (cj_size == 8)
 +    {
 +        /* Coordinates packed in 8, i-cluster size is half the packing width */
 +        return (ci >> 1)*STRIDE_P8 + (ci & 1)*(c_packX8 >> 1);
 +    }
 +    else
 +    {
 +        GMX_ASSERT(false, "Only j-cluster sizes 2, 4, 8 are implemented");
 +        return -1;
 +    }
 +}
 +
 +/* Returns the index in the coordinate array corresponding to the j-cluster index */
 +template<int cj_size> static gmx_inline int x_ind_cj(int cj)
 +{
 +    if (cj_size == 2)
 +    {
 +        /* Coordinates are stored packed in groups of 4 */
 +        return (cj >> 1)*STRIDE_P4 + (cj & 1)*(c_packX4 >> 1);
 +    }
 +    else if (cj_size <= 4)
 +    {
 +        /* Coordinates are stored packed in groups of 4 */
 +        return cj*STRIDE_P4;
 +    }
 +    else if (cj_size == 8)
 +    {
 +        /* Coordinates are stored packed in groups of 8 */
 +        return cj*STRIDE_P8;
 +    }
 +    else
 +    {
 +        GMX_ASSERT(false, "Only j-cluster sizes 2, 4, 8 are implemented");
 +        return -1;
 +    }
 +}
 +
 +/* The 6 functions below are only introduced to make the code more readable */
 +
 +static gmx_inline int ci_to_cj_simd_4xn(int ci)
 +{
 +    return ci_to_cj<GMX_SIMD_REAL_WIDTH>(ci);
 +}
 +
 +static gmx_inline int x_ind_ci_simd_4xn(int ci)
 +{
 +    return x_ind_ci<GMX_SIMD_REAL_WIDTH>(ci);
 +}
 +
 +static gmx_inline int x_ind_cj_simd_4xn(int cj)
 +{
 +    return x_ind_cj<GMX_SIMD_REAL_WIDTH>(cj);
 +}
 +
 +static gmx_inline int ci_to_cj_simd_2xnn(int ci)
 +{
 +    return ci_to_cj<GMX_SIMD_REAL_WIDTH/2>(ci);
 +}
 +
 +static gmx_inline int x_ind_ci_simd_2xnn(int ci)
 +{
 +    return x_ind_ci<GMX_SIMD_REAL_WIDTH/2>(ci);
 +}
 +
 +static gmx_inline int x_ind_cj_simd_2xnn(int cj)
 +{
 +    return x_ind_cj<GMX_SIMD_REAL_WIDTH/2>(cj);
 +}
 +
 +#endif // GMX_SIMD
 +
 +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_GPU:
 +        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;
 +    }
 +}
 +
 +/* Initializes a single nbnxn_pairlist_t data structure */
 +static void nbnxn_init_pairlist_fep(t_nblist *nl)
 +{
 +    nl->type        = GMX_NBLIST_INTERACTION_FREE_ENERGY;
 +    nl->igeometry   = GMX_NBLIST_GEOMETRY_PARTICLE_PARTICLE;
 +    /* The interaction functions are set in the free energy kernel fuction */
 +    nl->ivdw        = -1;
 +    nl->ivdwmod     = -1;
 +    nl->ielec       = -1;
 +    nl->ielecmod    = -1;
 +
 +    nl->maxnri      = 0;
 +    nl->maxnrj      = 0;
 +    nl->nri         = 0;
 +    nl->nrj         = 0;
 +    nl->iinr        = NULL;
 +    nl->gid         = NULL;
 +    nl->shift       = NULL;
 +    nl->jindex      = NULL;
 +    nl->jjnr        = NULL;
 +    nl->excl_fep    = NULL;
 +
 +}
 +
 +void nbnxn_init_search(nbnxn_search_t           * nbs_ptr,
 +                       ivec                      *n_dd_cells,
 +                       struct gmx_domdec_zones_t *zones,
 +                       gmx_bool                   bFEP,
 +                       int                        nthread_max)
 +{
 +    nbnxn_search_t nbs;
 +    int            ngrid;
 +
 +    snew(nbs, 1);
 +    *nbs_ptr = nbs;
 +
 +    nbs->bFEP   = bFEP;
 +
 +    nbs->DomDec = (n_dd_cells != NULL);
 +
 +    clear_ivec(nbs->dd_dim);
 +    ngrid = 1;
 +    if (nbs->DomDec)
 +    {
 +        nbs->zones = zones;
 +
 +        for (int d = 0; d < DIM; d++)
 +        {
 +            if ((*n_dd_cells)[d] > 1)
 +            {
 +                nbs->dd_dim[d] = 1;
 +                /* Each grid matches a DD zone */
 +                ngrid *= 2;
 +            }
 +        }
 +    }
 +
 +    nbnxn_grids_init(nbs, ngrid);
 +
 +    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 (int 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;
 +
 +        snew(nbs->work[t].nbl_fep, 1);
 +        nbnxn_init_pairlist_fep(nbs->work[t].nbl_fep);
 +    }
 +
 +    /* Initialize detailed nbsearch cycle counting */
 +    nbs->print_cycles = (getenv("GMX_NBNXN_CYCLE") != 0);
 +    nbs->search_count = 0;
 +    nbs_cycle_clear(nbs->cc);
 +    for (int t = 0; t < nbs->nthread_max; t++)
 +    {
 +        nbs_cycle_clear(nbs->work[t].cc);
 +    }
 +}
 +
 +static void init_buffer_flags(nbnxn_buffer_flags_t *flags,
 +                              int                   natoms)
 +{
 +    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 (int b = 0; b < flags->nflag; b++)
 +    {
 +        bitmask_clear(&(flags->flag[b]));
 +    }
 +}
 +
 +/* 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 = std::max(static_cast<int>((b0 - c0)*invs), 0);
 +
 +    while (*cf > 0 && d2 + gmx::square((b0 - c0) - (*cf-1+1)*s) < r2)
 +    {
 +        (*cf)--;
 +    }
 +
 +    *cl = std::min(static_cast<int>((b1 - c0)*invs), nc-1);
 +    while (*cl < nc-1 && d2 + gmx::square((*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 nbnxn_bb_t *bb)
 +{
 +    float d2;
 +    float dl, dh, dm, dm0;
 +
 +    d2 = 0;
 +
 +    dl  = bx0 - bb->upper[BB_X];
 +    dh  = bb->lower[BB_X] - bx1;
 +    dm  = std::max(dl, dh);
 +    dm0 = std::max(dm, 0.0f);
 +    d2 += dm0*dm0;
 +
 +    dl  = by0 - bb->upper[BB_Y];
 +    dh  = bb->lower[BB_Y] - by1;
 +    dm  = std::max(dl, dh);
 +    dm0 = std::max(dm, 0.0f);
 +    d2 += dm0*dm0;
 +
 +    dl  = bz0 - bb->upper[BB_Z];
 +    dh  = bb->lower[BB_Z] - bz1;
 +    dm  = std::max(dl, dh);
 +    dm0 = std::max(dm, 0.0f);
 +    d2 += dm0*dm0;
 +
 +    return d2;
 +}
 +
 +/* Plain C code calculating the distance^2 between two bounding boxes */
 +static float subc_bb_dist2(int si, const nbnxn_bb_t *bb_i_ci,
 +                           int csj, const nbnxn_bb_t *bb_j_all)
 +{
 +    const nbnxn_bb_t *bb_i, *bb_j;
 +    float             d2;
 +    float             dl, dh, dm, dm0;
 +
 +    bb_i = bb_i_ci  +  si;
 +    bb_j = bb_j_all + csj;
 +
 +    d2 = 0;
 +
 +    dl  = bb_i->lower[BB_X] - bb_j->upper[BB_X];
 +    dh  = bb_j->lower[BB_X] - bb_i->upper[BB_X];
 +    dm  = std::max(dl, dh);
 +    dm0 = std::max(dm, 0.0f);
 +    d2 += dm0*dm0;
 +
 +    dl  = bb_i->lower[BB_Y] - bb_j->upper[BB_Y];
 +    dh  = bb_j->lower[BB_Y] - bb_i->upper[BB_Y];
 +    dm  = std::max(dl, dh);
 +    dm0 = std::max(dm, 0.0f);
 +    d2 += dm0*dm0;
 +
 +    dl  = bb_i->lower[BB_Z] - bb_j->upper[BB_Z];
 +    dh  = bb_j->lower[BB_Z] - bb_i->upper[BB_Z];
 +    dm  = std::max(dl, dh);
 +    dm0 = std::max(dm, 0.0f);
 +    d2 += dm0*dm0;
 +
 +    return d2;
 +}
 +
 +#if NBNXN_SEARCH_BB_SIMD4
 +
 +/* 4-wide SIMD code for bb distance for bb format xyz0 */
 +static float subc_bb_dist2_simd4(int si, const nbnxn_bb_t *bb_i_ci,
 +                                 int csj, const nbnxn_bb_t *bb_j_all)
 +{
 +    // TODO: During SIMDv2 transition only some archs use namespace (remove when done)
 +    using namespace gmx;
 +
 +    Simd4Float bb_i_S0, bb_i_S1;
 +    Simd4Float bb_j_S0, bb_j_S1;
 +    Simd4Float dl_S;
 +    Simd4Float dh_S;
 +    Simd4Float dm_S;
 +    Simd4Float dm0_S;
 +
 +    bb_i_S0 = load4(&bb_i_ci[si].lower[0]);
 +    bb_i_S1 = load4(&bb_i_ci[si].upper[0]);
 +    bb_j_S0 = load4(&bb_j_all[csj].lower[0]);
 +    bb_j_S1 = load4(&bb_j_all[csj].upper[0]);
 +
 +    dl_S    = bb_i_S0 - bb_j_S1;
 +    dh_S    = bb_j_S0 - bb_i_S1;
 +
 +    dm_S    = max(dl_S, dh_S);
 +    dm0_S   = max(dm_S, simd4SetZeroF());
 +
 +    return dotProduct(dm0_S, dm0_S);
 +}
 +
 +/* Calculate bb bounding distances of bb_i[si,...,si+3] and store them in d2 */
 +#define SUBC_BB_DIST2_SIMD4_XXXX_INNER(si, bb_i, d2) \
 +    {                                                \
 +        int               shi;                                  \
 +                                                 \
 +        Simd4Float        dx_0, dy_0, dz_0;                    \
 +        Simd4Float        dx_1, dy_1, dz_1;                    \
 +                                                 \
 +        Simd4Float        mx, my, mz;                          \
 +        Simd4Float        m0x, m0y, m0z;                       \
 +                                                 \
 +        Simd4Float        d2x, d2y, d2z;                       \
 +        Simd4Float        d2s, d2t;                            \
 +                                                 \
 +        shi = si*NNBSBB_D*DIM;                       \
 +                                                 \
 +        xi_l = load4(bb_i+shi+0*STRIDE_PBB);   \
 +        yi_l = load4(bb_i+shi+1*STRIDE_PBB);   \
 +        zi_l = load4(bb_i+shi+2*STRIDE_PBB);   \
 +        xi_h = load4(bb_i+shi+3*STRIDE_PBB);   \
 +        yi_h = load4(bb_i+shi+4*STRIDE_PBB);   \
 +        zi_h = load4(bb_i+shi+5*STRIDE_PBB);   \
 +                                                 \
 +        dx_0 = xi_l - xj_h;                 \
 +        dy_0 = yi_l - yj_h;                 \
 +        dz_0 = zi_l - zj_h;                 \
 +                                                 \
 +        dx_1 = xj_l - xi_h;                 \
 +        dy_1 = yj_l - yi_h;                 \
 +        dz_1 = zj_l - zi_h;                 \
 +                                                 \
 +        mx   = max(dx_0, dx_1);                 \
 +        my   = max(dy_0, dy_1);                 \
 +        mz   = max(dz_0, dz_1);                 \
 +                                                 \
 +        m0x  = max(mx, zero);                   \
 +        m0y  = max(my, zero);                   \
 +        m0z  = max(mz, zero);                   \
 +                                                 \
 +        d2x  = m0x * m0x;                   \
 +        d2y  = m0y * m0y;                   \
 +        d2z  = m0z * m0z;                   \
 +                                                 \
 +        d2s  = d2x + d2y;                   \
 +        d2t  = d2s + d2z;                   \
 +                                                 \
 +        store4(d2+si, d2t);                      \
 +    }
 +
 +/* 4-wide SIMD code for nsi bb distances for bb format xxxxyyyyzzzz */
 +static void subc_bb_dist2_simd4_xxxx(const float *bb_j,
 +                                     int nsi, const float *bb_i,
 +                                     float *d2)
 +{
 +    // TODO: During SIMDv2 transition only some archs use namespace (remove when done)
 +    using namespace gmx;
 +
 +    Simd4Float xj_l, yj_l, zj_l;
 +    Simd4Float xj_h, yj_h, zj_h;
 +    Simd4Float xi_l, yi_l, zi_l;
 +    Simd4Float xi_h, yi_h, zi_h;
 +
 +    Simd4Float zero;
 +
 +    zero = setZero();
 +
 +    xj_l = Simd4Float(bb_j[0*STRIDE_PBB]);
 +    yj_l = Simd4Float(bb_j[1*STRIDE_PBB]);
 +    zj_l = Simd4Float(bb_j[2*STRIDE_PBB]);
 +    xj_h = Simd4Float(bb_j[3*STRIDE_PBB]);
 +    yj_h = Simd4Float(bb_j[4*STRIDE_PBB]);
 +    zj_h = Simd4Float(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_SIMD4_XXXX_INNER(0, bb_i, d2);
 +    if (STRIDE_PBB < nsi)
 +    {
 +        SUBC_BB_DIST2_SIMD4_XXXX_INNER(STRIDE_PBB, bb_i, d2);
 +    }
 +}
 +
 +#endif /* NBNXN_SEARCH_BB_SIMD4 */
 +
 +
 +/* Returns if any atom pair from two clusters is within distance sqrt(rl2) */
 +static gmx_inline gmx_bool
 +clusterpair_in_range(const nbnxn_list_work_t *work,
 +                     int si,
 +                     int csj, int stride, const real *x_j,
 +                     real rl2)
 +{
 +#if !NBNXN_SEARCH_BB_SIMD4
 +
 +    /* Plain C version.
 +     * All coordinates are stored as xyzxyz...
 +     */
 +
 +    const real *x_i = work->x_ci;
 +
 +    for (int i = 0; i < c_nbnxnGpuClusterSize; i++)
 +    {
 +        int i0 = (si*c_nbnxnGpuClusterSize + i)*DIM;
 +        for (int j = 0; j < c_nbnxnGpuClusterSize; j++)
 +        {
 +            int  j0 = (csj*c_nbnxnGpuClusterSize + j)*stride;
 +
 +            real d2 = gmx::square(x_i[i0  ] - x_j[j0  ]) + gmx::square(x_i[i0+1] - x_j[j0+1]) + gmx::square(x_i[i0+2] - x_j[j0+2]);
 +
 +            if (d2 < rl2)
 +            {
 +                return TRUE;
 +            }
 +        }
 +    }
 +
 +    return FALSE;
 +
 +#else /* !NBNXN_SEARCH_BB_SIMD4 */
 +
 +    /* 4-wide SIMD version.
 +     * A cluster is hard-coded to 8 atoms.
 +     * The coordinates x_i are stored as xxxxyyyy..., x_j is stored xyzxyz...
 +     * Using 8-wide AVX is not faster on Intel Sandy Bridge.
 +     */
 +    assert(c_nbnxnGpuClusterSize == 8);
 +
 +    Simd4Real   rc2_S      = Simd4Real(rl2);
 +
 +    const real *x_i        = work->x_ci_simd;
 +
 +    int         dim_stride = c_nbnxnGpuClusterSize*DIM;
 +    Simd4Real   ix_S0      = load4(x_i + si*dim_stride + 0*GMX_SIMD4_WIDTH);
 +    Simd4Real   iy_S0      = load4(x_i + si*dim_stride + 1*GMX_SIMD4_WIDTH);
 +    Simd4Real   iz_S0      = load4(x_i + si*dim_stride + 2*GMX_SIMD4_WIDTH);
 +    Simd4Real   ix_S1      = load4(x_i + si*dim_stride + 3*GMX_SIMD4_WIDTH);
 +    Simd4Real   iy_S1      = load4(x_i + si*dim_stride + 4*GMX_SIMD4_WIDTH);
 +    Simd4Real   iz_S1      = load4(x_i + si*dim_stride + 5*GMX_SIMD4_WIDTH);
 +
 +    /* We loop from the outer to the inner particles to maximize
 +     * the chance that we find a pair in range quickly and return.
 +     */
 +    int j0 = csj*c_nbnxnGpuClusterSize;
 +    int j1 = j0 + c_nbnxnGpuClusterSize - 1;
 +    while (j0 < j1)
 +    {
 +        Simd4Real jx0_S, jy0_S, jz0_S;
 +        Simd4Real jx1_S, jy1_S, jz1_S;
 +
 +        Simd4Real dx_S0, dy_S0, dz_S0;
 +        Simd4Real dx_S1, dy_S1, dz_S1;
 +        Simd4Real dx_S2, dy_S2, dz_S2;
 +        Simd4Real dx_S3, dy_S3, dz_S3;
 +
 +        Simd4Real rsq_S0;
 +        Simd4Real rsq_S1;
 +        Simd4Real rsq_S2;
 +        Simd4Real rsq_S3;
 +
 +        Simd4Bool wco_S0;
 +        Simd4Bool wco_S1;
 +        Simd4Bool wco_S2;
 +        Simd4Bool wco_S3;
 +        Simd4Bool wco_any_S01, wco_any_S23, wco_any_S;
 +
 +        jx0_S = Simd4Real(x_j[j0*stride+0]);
 +        jy0_S = Simd4Real(x_j[j0*stride+1]);
 +        jz0_S = Simd4Real(x_j[j0*stride+2]);
 +
 +        jx1_S = Simd4Real(x_j[j1*stride+0]);
 +        jy1_S = Simd4Real(x_j[j1*stride+1]);
 +        jz1_S = Simd4Real(x_j[j1*stride+2]);
 +
 +        /* Calculate distance */
 +        dx_S0            = ix_S0 - jx0_S;
 +        dy_S0            = iy_S0 - jy0_S;
 +        dz_S0            = iz_S0 - jz0_S;
 +        dx_S1            = ix_S1 - jx0_S;
 +        dy_S1            = iy_S1 - jy0_S;
 +        dz_S1            = iz_S1 - jz0_S;
 +        dx_S2            = ix_S0 - jx1_S;
 +        dy_S2            = iy_S0 - jy1_S;
 +        dz_S2            = iz_S0 - jz1_S;
 +        dx_S3            = ix_S1 - jx1_S;
 +        dy_S3            = iy_S1 - jy1_S;
 +        dz_S3            = iz_S1 - jz1_S;
 +
 +        /* rsq = dx*dx+dy*dy+dz*dz */
 +        rsq_S0           = norm2(dx_S0, dy_S0, dz_S0);
 +        rsq_S1           = norm2(dx_S1, dy_S1, dz_S1);
 +        rsq_S2           = norm2(dx_S2, dy_S2, dz_S2);
 +        rsq_S3           = norm2(dx_S3, dy_S3, dz_S3);
 +
 +        wco_S0           = (rsq_S0 < rc2_S);
 +        wco_S1           = (rsq_S1 < rc2_S);
 +        wco_S2           = (rsq_S2 < rc2_S);
 +        wco_S3           = (rsq_S3 < rc2_S);
 +
 +        wco_any_S01      = wco_S0 || wco_S1;
 +        wco_any_S23      = wco_S2 || wco_S3;
 +        wco_any_S        = wco_any_S01 || wco_any_S23;
 +
 +        if (anyTrue(wco_any_S))
 +        {
 +            return TRUE;
 +        }
 +
 +        j0++;
 +        j1--;
 +    }
 +
 +    return FALSE;
 +
 +#endif /* !NBNXN_SEARCH_BB_SIMD4 */
 +}
 +
 +/* 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/c_nbnxnGpuJgroupSize].cj[cj_ind & (c_nbnxnGpuJgroupSize - 1)];
 +}
 +
 +/* Returns the i-interaction mask of the j sub-cell for index cj_ind */
 +static unsigned int nbl_imask0(const nbnxn_pairlist_t *nbl, int cj_ind)
 +{
 +    return nbl->cj4[cj_ind/c_nbnxnGpuJgroupSize].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_cell_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-clusters in the list */
 +static void check_cell_list_space_supersub(nbnxn_pairlist_t *nbl,
 +                                           int               ncell)
 +{
 +    int ncj4_max, w;
 +
 +    /* We can have maximally nsupercell*c_gpuNumClusterPerCell 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 + ncell*c_gpuNumClusterPerCell + c_nbnxnGpuJgroupSize - 1)/c_nbnxnGpuJgroupSize);
 +
 +    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 (int j4 = nbl->work->cj4_init; j4 < ncj4_max; j4++)
 +        {
 +            /* No i-subcells and no excl's in the list initially */
 +            for (w = 0; w < c_nbnxnGpuClusterpairSplit; 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)
 +{
 +    for (int t = 0; t < c_nbnxnGpuExclSize; t++)
 +    {
 +        /* Turn all interaction bits on */
 +        excl->pair[t] = NBNXN_INTERACTION_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->nsci        = 0;
 +    nbl->sci         = NULL;
 +    nbl->sci_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)
 +    {
 +        GMX_ASSERT(c_nbnxnGpuNumClusterPerSupercluster == c_gpuNumClusterPerCell, "The search code assumes that the a super-cluster matches a search grid cell");
 +
 +        GMX_ASSERT(sizeof(nbl->cj4[0].imei[0].imask)*8 >= c_nbnxnGpuJgroupSize*c_gpuNumClusterPerCell, "The i super-cluster cluster interaction mask does not contain a sufficient number of bits");
 +        GMX_ASSERT(sizeof(nbl->excl[0])*8 >= c_nbnxnGpuJgroupSize*c_gpuNumClusterPerCell, "The GPU exclusion mask does not contain a sufficient number of bits");
 +
 +        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);
 +    if (nbl->bSimple)
 +    {
 +        snew_aligned(nbl->work->bb_ci, 1, NBNXN_SEARCH_BB_MEM_ALIGN);
 +    }
 +    else
 +    {
 +#if NBNXN_BBXXXX
 +        snew_aligned(nbl->work->pbb_ci, c_gpuNumClusterPerCell/STRIDE_PBB*NNBSBB_XXXX, NBNXN_SEARCH_BB_MEM_ALIGN);
 +#else
 +        snew_aligned(nbl->work->bb_ci, c_gpuNumClusterPerCell, NBNXN_SEARCH_BB_MEM_ALIGN);
 +#endif
 +    }
 +    int gpu_clusterpair_nc = c_gpuNumClusterPerCell*c_nbnxnGpuClusterSize*DIM;
 +    snew(nbl->work->x_ci, gpu_clusterpair_nc);
 +#if GMX_SIMD
 +    snew_aligned(nbl->work->x_ci_simd,
 +                 std::max(NBNXN_CPU_CLUSTER_I_SIZE*DIM*GMX_SIMD_REAL_WIDTH,
 +                          gpu_clusterpair_nc),
 +                 GMX_SIMD_REAL_WIDTH);
 +#endif
 +    snew_aligned(nbl->work->d2, c_gpuNumClusterPerCell, NBNXN_SEARCH_BB_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)
 +{
 +    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);
 +    snew(nbl_list->nbl_fep, nbl_list->nnbl);
 +    /* Execute in order to avoid memory interleaving between threads */
 +#pragma omp parallel for num_threads(nbl_list->nnbl) schedule(static)
 +    for (int i = 0; i < nbl_list->nnbl; i++)
 +    {
 +        try
 +        {
 +            /* 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);
 +            }
 +
 +            snew(nbl_list->nbl_fep[i], 1);
 +            nbnxn_init_pairlist_fep(nbl_list->nbl_fep[i]);
 +        }
 +        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 +    }
 +}
 +
 +/* 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                 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/(grid->size[XX]*grid->size[YY]*grid->size[ZZ])));
 +
 +    fprintf(fp, "nbl average j cell list length %.1f\n",
 +            0.25*nbl->ncj/(double)std::max(nbl->nci, 1));
 +
 +    for (int s = 0; s < SHIFTS; s++)
 +    {
 +        cs[s] = 0;
 +    }
 +    npexcl = 0;
 +    for (int 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;
 +
 +        int j = nbl->ci[i].cj_ind_start;
 +        while (j < nbl->ci[i].cj_ind_end &&
 +               nbl->cj[j].excl != NBNXN_INTERACTION_MASK_ALL)
 +        {
 +            npexcl++;
 +            j++;
 +        }
 +    }
 +    fprintf(fp, "nbl cell pairs, total: %d excl: %d %.1f%%\n",
 +            nbl->ncj, npexcl, 100*npexcl/(double)std::max(nbl->ncj, 1));
 +    for (int 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                 b;
 +    int                 c[c_gpuNumClusterPerCell + 1];
 +    double              sum_nsp, sum_nsp2;
 +    int                 nsp_max;
 +
 +    /* 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/(grid->size[XX]*grid->size[YY]*grid->size[ZZ])));
 +
 +    sum_nsp  = 0;
 +    sum_nsp2 = 0;
 +    nsp_max  = 0;
 +    for (int si = 0; si <= c_gpuNumClusterPerCell; si++)
 +    {
 +        c[si] = 0;
 +    }
 +    for (int i = 0; i < nbl->nsci; i++)
 +    {
 +        int nsp;
 +
 +        nsp = 0;
 +        for (int j4 = nbl->sci[i].cj4_ind_start; j4 < nbl->sci[i].cj4_ind_end; j4++)
 +        {
 +            for (int j = 0; j < c_nbnxnGpuJgroupSize; j++)
 +            {
 +                b = 0;
 +                for (int si = 0; si < c_gpuNumClusterPerCell; si++)
 +                {
 +                    if (nbl->cj4[j4].imei[0].imask & (1U << (j*c_gpuNumClusterPerCell + si)))
 +                    {
 +                        b++;
 +                    }
 +                }
 +                nsp += b;
 +                c[b]++;
 +            }
 +        }
 +        sum_nsp  += nsp;
 +        sum_nsp2 += nsp*nsp;
 +        nsp_max   = std::max(nsp_max, nsp);
 +    }
 +    if (nbl->nsci > 0)
 +    {
 +        sum_nsp  /= nbl->nsci;
 +        sum_nsp2 /= nbl->nsci;
 +    }
 +    fprintf(fp, "nbl #cluster-pairs: av %.1f stddev %.1f max %d\n",
 +            sum_nsp, std::sqrt(sum_nsp2 - sum_nsp*sum_nsp), nsp_max);
 +
 +    if (nbl->ncj4 > 0)
 +    {
 +        for (b = 0; b <= c_gpuNumClusterPerCell; b++)
 +        {
 +            fprintf(fp, "nbl j-list #i-subcell %d %7d %4.1f\n",
 +                    b, c[b],
 +                    100.0*c[b]/(double)(nbl->ncj4*c_nbnxnGpuJgroupSize));
 +        }
 +    }
 +}
 +
 +/* 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,
 + * generates a new element and 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 masks for cj4-unit cj4 for both warps,
 + * generates a new element and 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 i_cluster_in_cell)
 +{
 +    nbnxn_excl_t *excl[c_nbnxnGpuClusterpairSplit];
 +
 +    /* Here we only set the set self and double pair exclusions */
 +
 +    assert(c_nbnxnGpuClusterpairSplit == 2);
 +
 +    get_nbl_exclusions_2(nbl, cj4_ind, &excl[0], &excl[1]);
 +
 +    /* Only minor < major bits set */
 +    for (int ej = 0; ej < nbl->na_ci; ej++)
 +    {
 +        int w = (ej>>2);
 +        for (int ei = ej; ei < nbl->na_ci; ei++)
 +        {
 +            excl[w]->pair[(ej & (c_nbnxnGpuJgroupSize-1))*nbl->na_ci + ei] &=
 +                ~(1U << (sj_offset*c_gpuNumClusterPerCell + i_cluster_in_cell));
 +        }
 +    }
 +}
 +
 +/* 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_INTERACTION_MASK_DIAG : NBNXN_INTERACTION_MASK_ALL);
 +}
 +
 +/* Returns a diagonal or off-diagonal interaction mask for cj-size=2 */
 +static unsigned int get_imask_simd_j2(gmx_bool rdiag, int ci, int cj)
 +{
 +    return (rdiag && ci*2 == cj ? NBNXN_INTERACTION_MASK_DIAG_J2_0 :
 +            (rdiag && ci*2+1 == cj ? NBNXN_INTERACTION_MASK_DIAG_J2_1 :
 +             NBNXN_INTERACTION_MASK_ALL));
 +}
 +
 +/* Returns a diagonal or off-diagonal interaction mask for cj-size=4 */
 +static unsigned int get_imask_simd_j4(gmx_bool rdiag, int ci, int cj)
 +{
 +    return (rdiag && ci == cj ? NBNXN_INTERACTION_MASK_DIAG : NBNXN_INTERACTION_MASK_ALL);
 +}
 +
 +/* Returns a diagonal or off-diagonal interaction mask for cj-size=8 */
 +static unsigned int get_imask_simd_j8(gmx_bool rdiag, int ci, int cj)
 +{
 +    return (rdiag && ci == cj*2 ? NBNXN_INTERACTION_MASK_DIAG_J8_0 :
 +            (rdiag && ci == cj*2+1 ? NBNXN_INTERACTION_MASK_DIAG_J8_1 :
 +             NBNXN_INTERACTION_MASK_ALL));
 +}
 +
 +#if GMX_SIMD
 +#if GMX_SIMD_REAL_WIDTH == 2
 +#define get_imask_simd_4xn  get_imask_simd_j2
 +#endif
 +#if GMX_SIMD_REAL_WIDTH == 4
 +#define get_imask_simd_4xn  get_imask_simd_j4
 +#endif
 +#if GMX_SIMD_REAL_WIDTH == 8
 +#define get_imask_simd_4xn  get_imask_simd_j8
 +#define get_imask_simd_2xnn get_imask_simd_j4
 +#endif
 +#if GMX_SIMD_REAL_WIDTH == 16
 +#define get_imask_simd_2xnn get_imask_simd_j8
 +#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_bb_t        *bb_ci;
 +    const real              *x_ci;
 +
 +    gmx_bool                 InRange;
 +    real                     d2;
 +    int                      cjf_gl, cjl_gl;
 +
 +    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)
 +        {
 +            cjf_gl = gridj->cell0 + cjf;
 +            for (int i = 0; i < NBNXN_CPU_CLUSTER_I_SIZE && !InRange; i++)
 +            {
 +                for (int j = 0; j < NBNXN_CPU_CLUSTER_I_SIZE; j++)
 +                {
 +                    InRange = InRange ||
 +                        (gmx::square(x_ci[i*STRIDE_XYZ+XX] - x_j[(cjf_gl*NBNXN_CPU_CLUSTER_I_SIZE+j)*STRIDE_XYZ+XX]) +
 +                         gmx::square(x_ci[i*STRIDE_XYZ+YY] - x_j[(cjf_gl*NBNXN_CPU_CLUSTER_I_SIZE+j)*STRIDE_XYZ+YY]) +
 +                         gmx::square(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)
 +        {
 +            cjl_gl = gridj->cell0 + cjl;
 +            for (int i = 0; i < NBNXN_CPU_CLUSTER_I_SIZE && !InRange; i++)
 +            {
 +                for (int j = 0; j < NBNXN_CPU_CLUSTER_I_SIZE; j++)
 +                {
 +                    InRange = InRange ||
 +                        (gmx::square(x_ci[i*STRIDE_XYZ+XX] - x_j[(cjl_gl*NBNXN_CPU_CLUSTER_I_SIZE+j)*STRIDE_XYZ+XX]) +
 +                         gmx::square(x_ci[i*STRIDE_XYZ+YY] - x_j[(cjl_gl*NBNXN_CPU_CLUSTER_I_SIZE+j)*STRIDE_XYZ+YY]) +
 +                         gmx::square(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 (int 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 "gromacs/mdlib/nbnxn_search_simd_4xn.h"
 +#endif
 +#ifdef GMX_NBNXN_SIMD_2XNN
 +#include "gromacs/mdlib/nbnxn_search_simd_2xnn.h"
 +#endif
 +
 +/* Plain C or SIMD4 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_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)
 +{
 +    nbnxn_list_work_t *work   = nbl->work;
 +
 +#if NBNXN_BBXXXX
 +    const float       *pbb_ci = work->pbb_ci;
 +#else
 +    const nbnxn_bb_t  *bb_ci  = work->bb_ci;
 +#endif
 +
 +    assert(c_nbnxnGpuClusterSize == gridi->na_c);
 +    assert(c_nbnxnGpuClusterSize == gridj->na_c);
 +
 +    /* We generate the pairlist mainly based on bounding-box distances
 +     * and do atom pair distance based pruning on the GPU.
 +     * Only if a j-group contains a single cluster-pair, we try to prune
 +     * that pair based on atom distances on the CPU to avoid empty j-groups.
 +     */
 +#define PRUNE_LIST_CPU_ONE 1
 +#define PRUNE_LIST_CPU_ALL 0
 +
 +#if PRUNE_LIST_CPU_ONE
 +    int  ci_last = -1;
 +#endif
 +
 +    float *d2l = work->d2;
 +
 +    for (int subc = 0; subc < gridj->nsubc[scj]; subc++)
 +    {
 +        int          cj4_ind   = nbl->work->cj_ind/c_nbnxnGpuJgroupSize;
 +        int          cj_offset = nbl->work->cj_ind - cj4_ind*c_nbnxnGpuJgroupSize;
 +        nbnxn_cj4_t *cj4       = &nbl->cj4[cj4_ind];
 +
 +        int          cj        = scj*c_gpuNumClusterPerCell + subc;
 +
 +        int          cj_gl     = gridj->cell0*c_gpuNumClusterPerCell + cj;
 +
 +        /* Initialize this j-subcell i-subcell list */
 +        cj4->cj[cj_offset] = cj_gl;
 +
 +        int ci1;
 +        if (sci_equals_scj)
 +        {
 +            ci1 = subc + 1;
 +        }
 +        else
 +        {
 +            ci1 = gridi->nsubc[sci];
 +        }
 +
 +#if NBNXN_BBXXXX
 +        /* Determine all ci1 bb distances in one call with SIMD4 */
 +        subc_bb_dist2_simd4_xxxx(gridj->pbb+(cj>>STRIDE_PBB_2LOG)*NNBSBB_XXXX+(cj & (STRIDE_PBB-1)),
 +                                 ci1, pbb_ci, d2l);
 +        *ndistc += c_nbnxnGpuClusterSize*2;
 +#endif
 +
 +        int          npair = 0;
 +        unsigned int imask = 0;
 +        /* We use a fixed upper-bound instead of ci1 to help optimization */
 +        for (int ci = 0; ci < c_gpuNumClusterPerCell; ci++)
 +        {
 +            if (ci == ci1)
 +            {
 +                break;
 +            }
 +
 +#if !NBNXN_BBXXXX
 +            /* Determine the bb distance between ci and cj */
 +            d2l[ci]  = subc_bb_dist2(ci, bb_ci, cj, gridj->bb);
 +            *ndistc += 2;
 +#endif
 +            float d2 = d2l[ci];
 +
 +#if 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 += c_nbnxnGpuClusterSize*c_nbnxnGpuClusterSize;
 +            if (d2 < rbb2 ||
 +                (d2 < rl2 &&
 +                 clusterpair_in_range(work, 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*c_gpuNumClusterPerCell + ci));
 +
 +#if PRUNE_LIST_CPU_ONE
 +                ci_last = ci;
 +#endif
 +
 +                npair++;
 +            }
 +        }
 +
 +#if 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 &&
 +            !clusterpair_in_range(work, ci_last, cj_gl, stride, x, rl2))
 +        {
 +            imask &= ~(1U << (cj_offset*c_gpuNumClusterPerCell + 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, subc);
 +            }
 +
 +            /* Copy the cluster interaction mask to the list */
 +            for (int w = 0; w < c_nbnxnGpuClusterpairSplit; 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 + c_nbnxnGpuJgroupSize - 1)/c_nbnxnGpuJgroupSize;
 +        }
 +    }
 +}
 +
 +/* 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           ai, aj, si, ge, se;
 +    int           found, cj_ind_0, cj_ind_1, cj_ind_m;
 +    int           cj_m;
 +    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++;
 +        }
 +    }
 +#if NBNXN_SEARCH_BB_SIMD4
 +    else
 +    {
 +        while (cj_ind_first + ndirect <= cj_ind_last &&
 +               nbl->cj[cj_ind_first+ndirect].cj == ci_to_cj(ci, na_cj_2log) + ndirect)
 +        {
 +            ndirect++;
 +        }
 +    }
 +#endif
 +
 +    /* Loop over the atoms in the i super-cell */
 +    for (int 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 (int 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));
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/* Add a new i-entry to the FEP list and copy the i-properties */
 +static gmx_inline void fep_list_new_nri_copy(t_nblist *nlist)
 +{
 +    /* Add a new i-entry */
 +    nlist->nri++;
 +
 +    assert(nlist->nri < nlist->maxnri);
 +
 +    /* Duplicate the last i-entry, except for jindex, which continues */
 +    nlist->iinr[nlist->nri]   = nlist->iinr[nlist->nri-1];
 +    nlist->shift[nlist->nri]  = nlist->shift[nlist->nri-1];
 +    nlist->gid[nlist->nri]    = nlist->gid[nlist->nri-1];
 +    nlist->jindex[nlist->nri] = nlist->nrj;
 +}
 +
 +/* For load balancing of the free-energy lists over threads, we set
 + * the maximum nrj size of an i-entry to 40. This leads to good
 + * load balancing in the worst case scenario of a single perturbed
 + * particle on 16 threads, while not introducing significant overhead.
 + * Note that half of the perturbed pairs will anyhow end up in very small lists,
 + * since non perturbed i-particles will see few perturbed j-particles).
 + */
 +const int max_nrj_fep = 40;
 +
 +/* Exclude the perturbed pairs from the Verlet list. This is only done to avoid
 + * singularities for overlapping particles (0/0), since the charges and
 + * LJ parameters have been zeroed in the nbnxn data structure.
 + * Simultaneously make a group pair list for the perturbed pairs.
 + */
 +static void make_fep_list(const nbnxn_search_t    nbs,
 +                          const nbnxn_atomdata_t *nbat,
 +                          nbnxn_pairlist_t       *nbl,
 +                          gmx_bool                bDiagRemoved,
 +                          nbnxn_ci_t             *nbl_ci,
 +                          const nbnxn_grid_t     *gridi,
 +                          const nbnxn_grid_t     *gridj,
 +                          t_nblist               *nlist)
 +{
 +    int      ci, cj_ind_start, cj_ind_end, cja, cjr;
 +    int      nri_max;
 +    int      ngid, gid_i = 0, gid_j, gid;
 +    int      egp_shift, egp_mask;
 +    int      gid_cj = 0;
 +    int      ind_i, ind_j, ai, aj;
 +    int      nri;
 +    gmx_bool bFEP_i, bFEP_i_all;
 +
 +    if (nbl_ci->cj_ind_end == nbl_ci->cj_ind_start)
 +    {
 +        /* Empty list */
 +        return;
 +    }
 +
 +    ci = nbl_ci->ci;
 +
 +    cj_ind_start = nbl_ci->cj_ind_start;
 +    cj_ind_end   = nbl_ci->cj_ind_end;
 +
 +    /* In worst case we have alternating energy groups
 +     * and create #atom-pair lists, which means we need the size
 +     * of a cluster pair (na_ci*na_cj) times the number of cj's.
 +     */
 +    nri_max = nbl->na_ci*nbl->na_cj*(cj_ind_end - cj_ind_start);
 +    if (nlist->nri + nri_max > nlist->maxnri)
 +    {
 +        nlist->maxnri = over_alloc_large(nlist->nri + nri_max);
 +        reallocate_nblist(nlist);
 +    }
 +
 +    ngid = nbat->nenergrp;
 +
 +    if (static_cast<std::size_t>(ngid*gridj->na_cj) > sizeof(gid_cj)*8)
 +    {
 +        gmx_fatal(FARGS, "The Verlet scheme with %dx%d kernels and free-energy only supports up to %d energy groups",
 +                  gridi->na_c, gridj->na_cj, (sizeof(gid_cj)*8)/gridj->na_cj);
 +    }
 +
 +    egp_shift = nbat->neg_2log;
 +    egp_mask  = (1<<nbat->neg_2log) - 1;
 +
 +    /* Loop over the atoms in the i sub-cell */
 +    bFEP_i_all = TRUE;
 +    for (int i = 0; i < nbl->na_ci; i++)
 +    {
 +        ind_i = ci*nbl->na_ci + i;
 +        ai    = nbs->a[ind_i];
 +        if (ai >= 0)
 +        {
 +            nri                  = nlist->nri;
 +            nlist->jindex[nri+1] = nlist->jindex[nri];
 +            nlist->iinr[nri]     = ai;
 +            /* The actual energy group pair index is set later */
 +            nlist->gid[nri]      = 0;
 +            nlist->shift[nri]    = nbl_ci->shift & NBNXN_CI_SHIFT;
 +
 +            bFEP_i = gridi->fep[ci - gridi->cell0] & (1 << i);
 +
 +            bFEP_i_all = bFEP_i_all && bFEP_i;
 +
 +            if ((nlist->nrj + cj_ind_end - cj_ind_start)*nbl->na_cj > nlist->maxnrj)
 +            {
 +                nlist->maxnrj = over_alloc_small((nlist->nrj + cj_ind_end - cj_ind_start)*nbl->na_cj);
 +                srenew(nlist->jjnr,     nlist->maxnrj);
 +                srenew(nlist->excl_fep, nlist->maxnrj);
 +            }
 +
 +            if (ngid > 1)
 +            {
 +                gid_i = (nbat->energrp[ci] >> (egp_shift*i)) & egp_mask;
 +            }
 +
 +            for (int cj_ind = cj_ind_start; cj_ind < cj_ind_end; cj_ind++)
 +            {
 +                unsigned int fep_cj;
 +
 +                cja = nbl->cj[cj_ind].cj;
 +
 +                if (gridj->na_cj == gridj->na_c)
 +                {
 +                    cjr    = cja - gridj->cell0;
 +                    fep_cj = gridj->fep[cjr];
 +                    if (ngid > 1)
 +                    {
 +                        gid_cj = nbat->energrp[cja];
 +                    }
 +                }
 +                else if (2*gridj->na_cj == gridj->na_c)
 +                {
 +                    cjr    = cja - gridj->cell0*2;
 +                    /* Extract half of the ci fep/energrp mask */
 +                    fep_cj = (gridj->fep[cjr>>1] >> ((cjr&1)*gridj->na_cj)) & ((1<<gridj->na_cj) - 1);
 +                    if (ngid > 1)
 +                    {
 +                        gid_cj = nbat->energrp[cja>>1] >> ((cja&1)*gridj->na_cj*egp_shift) & ((1<<(gridj->na_cj*egp_shift)) - 1);
 +                    }
 +                }
 +                else
 +                {
 +                    cjr    = cja - (gridj->cell0>>1);
 +                    /* Combine two ci fep masks/energrp */
 +                    fep_cj = gridj->fep[cjr*2] + (gridj->fep[cjr*2+1] << gridj->na_c);
 +                    if (ngid > 1)
 +                    {
 +                        gid_cj = nbat->energrp[cja*2] + (nbat->energrp[cja*2+1] << (gridj->na_c*egp_shift));
 +                    }
 +                }
 +
 +                if (bFEP_i || fep_cj != 0)
 +                {
 +                    for (int j = 0; j < nbl->na_cj; j++)
 +                    {
 +                        /* Is this interaction perturbed and not excluded? */
 +                        ind_j = cja*nbl->na_cj + j;
 +                        aj    = nbs->a[ind_j];
 +                        if (aj >= 0 &&
 +                            (bFEP_i || (fep_cj & (1 << j))) &&
 +                            (!bDiagRemoved || ind_j >= ind_i))
 +                        {
 +                            if (ngid > 1)
 +                            {
 +                                gid_j = (gid_cj >> (j*egp_shift)) & egp_mask;
 +                                gid   = GID(gid_i, gid_j, ngid);
 +
 +                                if (nlist->nrj > nlist->jindex[nri] &&
 +                                    nlist->gid[nri] != gid)
 +                                {
 +                                    /* Energy group pair changed: new list */
 +                                    fep_list_new_nri_copy(nlist);
 +                                    nri = nlist->nri;
 +                                }
 +                                nlist->gid[nri] = gid;
 +                            }
 +
 +                            if (nlist->nrj - nlist->jindex[nri] >= max_nrj_fep)
 +                            {
 +                                fep_list_new_nri_copy(nlist);
 +                                nri = nlist->nri;
 +                            }
 +
 +                            /* Add it to the FEP list */
 +                            nlist->jjnr[nlist->nrj]     = aj;
 +                            nlist->excl_fep[nlist->nrj] = (nbl->cj[cj_ind].excl >> (i*nbl->na_cj + j)) & 1;
 +                            nlist->nrj++;
 +
 +                            /* Exclude it from the normal list.
 +                             * Note that the charge has been set to zero,
 +                             * but we need to avoid 0/0, as perturbed atoms
 +                             * can be on top of each other.
 +                             */
 +                            nbl->cj[cj_ind].excl &= ~(1U << (i*nbl->na_cj + j));
 +                        }
 +                    }
 +                }
 +            }
 +
 +            if (nlist->nrj > nlist->jindex[nri])
 +            {
 +                /* Actually add this new, non-empty, list */
 +                nlist->nri++;
 +                nlist->jindex[nlist->nri] = nlist->nrj;
 +            }
 +        }
 +    }
 +
 +    if (bFEP_i_all)
 +    {
 +        /* All interactions are perturbed, we can skip this entry */
 +        nbl_ci->cj_ind_end = cj_ind_start;
 +    }
 +}
 +
 +/* Return the index of atom a within a cluster */
 +static gmx_inline int cj_mod_cj4(int cj)
 +{
 +    return cj & (c_nbnxnGpuJgroupSize - 1);
 +}
 +
 +/* Convert a j-cluster to a cj4 group */
 +static gmx_inline int cj_to_cj4(int cj)
 +{
 +    return cj/c_nbnxnGpuJgroupSize;
 +}
 +
 +/* Return the index of an j-atom within a warp */
 +static gmx_inline int a_mod_wj(int a)
 +{
 +    return a & (c_nbnxnGpuClusterSize/c_nbnxnGpuClusterpairSplit - 1);
 +}
 +
 +/* As make_fep_list above, but for super/sub lists. */
 +static void make_fep_list_supersub(const nbnxn_search_t    nbs,
 +                                   const nbnxn_atomdata_t *nbat,
 +                                   nbnxn_pairlist_t       *nbl,
 +                                   gmx_bool                bDiagRemoved,
 +                                   const nbnxn_sci_t      *nbl_sci,
 +                                   real                    shx,
 +                                   real                    shy,
 +                                   real                    shz,
 +                                   real                    rlist_fep2,
 +                                   const nbnxn_grid_t     *gridi,
 +                                   const nbnxn_grid_t     *gridj,
 +                                   t_nblist               *nlist)
 +{
 +    int                sci, cj4_ind_start, cj4_ind_end, cjr;
 +    int                nri_max;
 +    int                c_abs;
 +    int                ind_i, ind_j, ai, aj;
 +    int                nri;
 +    gmx_bool           bFEP_i;
 +    real               xi, yi, zi;
 +    const nbnxn_cj4_t *cj4;
 +
 +    if (nbl_sci->cj4_ind_end == nbl_sci->cj4_ind_start)
 +    {
 +        /* Empty list */
 +        return;
 +    }
 +
 +    sci = nbl_sci->sci;
 +
 +    cj4_ind_start = nbl_sci->cj4_ind_start;
 +    cj4_ind_end   = nbl_sci->cj4_ind_end;
 +
 +    /* Here we process one super-cell, max #atoms na_sc, versus a list
 +     * cj4 entries, each with max c_nbnxnGpuJgroupSize cj's, each
 +     * of size na_cj atoms.
 +     * On the GPU we don't support energy groups (yet).
 +     * So for each of the na_sc i-atoms, we need max one FEP list
 +     * for each max_nrj_fep j-atoms.
 +     */
 +    nri_max = nbl->na_sc*nbl->na_cj*(1 + ((cj4_ind_end - cj4_ind_start)*c_nbnxnGpuJgroupSize)/max_nrj_fep);
 +    if (nlist->nri + nri_max > nlist->maxnri)
 +    {
 +        nlist->maxnri = over_alloc_large(nlist->nri + nri_max);
 +        reallocate_nblist(nlist);
 +    }
 +
 +    /* Loop over the atoms in the i super-cluster */
 +    for (int c = 0; c < c_gpuNumClusterPerCell; c++)
 +    {
 +        c_abs = sci*c_gpuNumClusterPerCell + c;
 +
 +        for (int i = 0; i < nbl->na_ci; i++)
 +        {
 +            ind_i = c_abs*nbl->na_ci + i;
 +            ai    = nbs->a[ind_i];
 +            if (ai >= 0)
 +            {
 +                nri                  = nlist->nri;
 +                nlist->jindex[nri+1] = nlist->jindex[nri];
 +                nlist->iinr[nri]     = ai;
 +                /* With GPUs, energy groups are not supported */
 +                nlist->gid[nri]      = 0;
 +                nlist->shift[nri]    = nbl_sci->shift & NBNXN_CI_SHIFT;
 +
 +                bFEP_i = (gridi->fep[c_abs - gridi->cell0*c_gpuNumClusterPerCell] & (1 << i));
 +
 +                xi = nbat->x[ind_i*nbat->xstride+XX] + shx;
 +                yi = nbat->x[ind_i*nbat->xstride+YY] + shy;
 +                zi = nbat->x[ind_i*nbat->xstride+ZZ] + shz;
 +
 +                if ((nlist->nrj + cj4_ind_end - cj4_ind_start)*c_nbnxnGpuJgroupSize*nbl->na_cj > nlist->maxnrj)
 +                {
 +                    nlist->maxnrj = over_alloc_small((nlist->nrj + cj4_ind_end - cj4_ind_start)*c_nbnxnGpuJgroupSize*nbl->na_cj);
 +                    srenew(nlist->jjnr,     nlist->maxnrj);
 +                    srenew(nlist->excl_fep, nlist->maxnrj);
 +                }
 +
 +                for (int cj4_ind = cj4_ind_start; cj4_ind < cj4_ind_end; cj4_ind++)
 +                {
 +                    cj4 = &nbl->cj4[cj4_ind];
 +
 +                    for (int gcj = 0; gcj < c_nbnxnGpuJgroupSize; gcj++)
 +                    {
 +                        unsigned int fep_cj;
 +
 +                        if ((cj4->imei[0].imask & (1U << (gcj*c_gpuNumClusterPerCell + c))) == 0)
 +                        {
 +                            /* Skip this ci for this cj */
 +                            continue;
 +                        }
 +
 +                        cjr = cj4->cj[gcj] - gridj->cell0*c_gpuNumClusterPerCell;
 +
 +                        fep_cj = gridj->fep[cjr];
 +
 +                        if (bFEP_i || fep_cj != 0)
 +                        {
 +                            for (int j = 0; j < nbl->na_cj; j++)
 +                            {
 +                                /* Is this interaction perturbed and not excluded? */
 +                                ind_j = (gridj->cell0*c_gpuNumClusterPerCell + cjr)*nbl->na_cj + j;
 +                                aj    = nbs->a[ind_j];
 +                                if (aj >= 0 &&
 +                                    (bFEP_i || (fep_cj & (1 << j))) &&
 +                                    (!bDiagRemoved || ind_j >= ind_i))
 +                                {
 +                                    nbnxn_excl_t *excl;
 +                                    int           excl_pair;
 +                                    unsigned int  excl_bit;
 +                                    real          dx, dy, dz;
 +
 +                                    get_nbl_exclusions_1(nbl, cj4_ind, j>>2, &excl);
 +
 +                                    excl_pair = a_mod_wj(j)*nbl->na_ci + i;
 +                                    excl_bit  = (1U << (gcj*c_gpuNumClusterPerCell + c));
 +
 +                                    dx = nbat->x[ind_j*nbat->xstride+XX] - xi;
 +                                    dy = nbat->x[ind_j*nbat->xstride+YY] - yi;
 +                                    dz = nbat->x[ind_j*nbat->xstride+ZZ] - zi;
 +
 +                                    /* The unpruned GPU list has more than 2/3
 +                                     * of the atom pairs beyond rlist. Using
 +                                     * this list will cause a lot of overhead
 +                                     * in the CPU FEP kernels, especially
 +                                     * relative to the fast GPU kernels.
 +                                     * So we prune the FEP list here.
 +                                     */
 +                                    if (dx*dx + dy*dy + dz*dz < rlist_fep2)
 +                                    {
 +                                        if (nlist->nrj - nlist->jindex[nri] >= max_nrj_fep)
 +                                        {
 +                                            fep_list_new_nri_copy(nlist);
 +                                            nri = nlist->nri;
 +                                        }
 +
 +                                        /* Add it to the FEP list */
 +                                        nlist->jjnr[nlist->nrj]     = aj;
 +                                        nlist->excl_fep[nlist->nrj] = (excl->pair[excl_pair] & excl_bit) ? 1 : 0;
 +                                        nlist->nrj++;
 +                                    }
 +
 +                                    /* Exclude it from the normal list.
 +                                     * Note that the charge and LJ parameters have
 +                                     * been set to zero, but we need to avoid 0/0,
 +                                     * as perturbed atoms can be on top of each other.
 +                                     */
 +                                    excl->pair[excl_pair] &= ~excl_bit;
 +                                }
 +                            }
 +
 +                            /* Note that we could mask out this pair in imask
 +                             * if all i- and/or all j-particles are perturbed.
 +                             * But since the perturbed pairs on the CPU will
 +                             * take an order of magnitude more time, the GPU
 +                             * will finish before the CPU and there is no gain.
 +                             */
 +                        }
 +                    }
 +                }
 +
 +                if (nlist->nrj > nlist->jindex[nri])
 +                {
 +                    /* Actually add this new, non-empty, list */
 +                    nlist->nri++;
 +                    nlist->jindex[nlist->nri] = nlist->nrj;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/* 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           ai, aj, si, ge, se;
 +    int           found, cj_ind_0, cj_ind_1, cj_ind_m;
 +    int           cj_m;
 +    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*c_nbnxnGpuJgroupSize;
 +    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*c_gpuNumClusterPerCell + ndirect)
 +    {
 +        ndirect++;
 +    }
 +
 +    /* Loop over the atoms in the i super-cell */
 +    for (int 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 (int 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;
 +
 +                        if (nbl_imask0(nbl, found) & (1U << (cj_mod_cj4(found)*c_gpuNumClusterPerCell + si)))
 +                        {
 +                            w       = (inner_e >> 2);
 +
 +                            get_nbl_exclusions_1(nbl, cj_to_cj4(found), w, &nbl_excl);
 +
 +                            nbl_excl->pair[a_mod_wj(inner_e)*nbl->na_ci+inner_i] &=
 +                                ~(1U << (cj_mod_cj4(found)*c_gpuNumClusterPerCell + si));
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/* 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)
 +{
 +    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)
 +{
 +    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;
 +
 +    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 (int j = 0; j < ncj; j++)
 +    {
 +        if (cj[j].excl != NBNXN_INTERACTION_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_INTERACTION_MASK_ALL)))
 +    {
 +        for (int j = 0; j < ncj; j++)
 +        {
 +            if (cj[j].excl == NBNXN_INTERACTION_MASK_ALL)
 +            {
 +                work->cj[jnew++] = cj[j];
 +            }
 +        }
 +        for (int 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_target_av,
-     int nsp_est;
++                            gmx_bool progBal, float nsp_tot_est,
 +                            int thread, int nthread)
 +{
-          * that with of equally sized blocks of size nsp_target_av.
 +    int nsp_max;
 +    int cj4_start, cj4_end, j4len;
 +    int sci;
 +    int nsp, nsp_sci, nsp_cj4, nsp_cj4_e, nsp_cj4_p;
 +
 +    if (progBal)
 +    {
++        float nsp_est;
++
 +        /* Estimate the total numbers of ci's of the nblist combined
 +         * over all threads using the target number of ci's.
 +         */
 +        nsp_est = (nsp_tot_est*thread)/nthread + nbl->nci_tot;
 +
 +        /* The first ci blocks should be larger, to avoid overhead.
 +         * The last ci blocks should be smaller, to improve load balancing.
 +         * The factor 3/2 makes the first block 3/2 times the target average
 +         * and ensures that the total number of blocks end up equal to
-         nsp_max = nsp_target_av*nsp_tot_est*3/(2*(nsp_est + nsp_tot_est));
++         * that of equally sized blocks of size nsp_target_av.
 +         */
-                                     gmx_bool progBal, int nsp_tot_est,
++        nsp_max = static_cast<int>(nsp_target_av*(nsp_tot_est*1.5/(nsp_est + nsp_tot_est)));
 +    }
 +    else
 +    {
 +        nsp_max = nsp_target_av;
 +    }
 +
 +    /* Since nsp_max is a maximum/cut-off (this avoids high outliers,
 +     * which lead to load imbalance), not an average, we add half the
 +     * number of pairs in a cj4 block to get the average about right.
 +     */
 +    nsp_max += c_gpuNumClusterPerCell*c_nbnxnGpuJgroupSize/2;
 +
 +    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*c_gpuNumClusterPerCell*c_nbnxnGpuJgroupSize > 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 (int 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 (int p = 0; p < c_gpuNumClusterPerCell*c_nbnxnGpuJgroupSize; p++)
 +            {
 +                nsp_cj4 += (nbl->cj4[cj4].imei[0].imask >> p) & 1;
 +            }
 +
 +            /* Check if we should split at this cj4 to get a list of size nsp */
 +            if (nsp > 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,
-                                 int                  *nsubpair_tot_est)
++                                    gmx_bool progBal, float nsp_tot_est,
 +                                    int thread, int nthread)
 +{
 +    /* All content of the new ci entry have already been filled correctly,
 +     * we only need to increase the count here (for non empty lists).
 +     */
 +    int 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 + c_nbnxnGpuJgroupSize - 1)/c_nbnxnGpuJgroupSize;
 +        nbl->work->cj_ind = nbl->ncj4*c_nbnxnGpuJgroupSize;
 +
 +        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, nsp_tot_est,
 +                            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*c_nbnxnGpuJgroupSize;
 +        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;
 +}
 +
 +/* Clears a group scheme pair list */
 +static void clear_pairlist_fep(t_nblist *nl)
 +{
 +    nl->nri = 0;
 +    nl->nrj = 0;
 +    if (nl->jindex == NULL)
 +    {
 +        snew(nl->jindex, 1);
 +    }
 +    nl->jindex[0] = 0;
 +}
 +
 +/* Sets a simple list i-cell bounding box, including PBC shift */
 +static gmx_inline void set_icell_bb_simple(const nbnxn_bb_t *bb, int ci,
 +                                           real shx, real shy, real shz,
 +                                           nbnxn_bb_t *bb_ci)
 +{
 +    bb_ci->lower[BB_X] = bb[ci].lower[BB_X] + shx;
 +    bb_ci->lower[BB_Y] = bb[ci].lower[BB_Y] + shy;
 +    bb_ci->lower[BB_Z] = bb[ci].lower[BB_Z] + shz;
 +    bb_ci->upper[BB_X] = bb[ci].upper[BB_X] + shx;
 +    bb_ci->upper[BB_Y] = bb[ci].upper[BB_Y] + shy;
 +    bb_ci->upper[BB_Z] = bb[ci].upper[BB_Z] + shz;
 +}
 +
 +#if NBNXN_BBXXXX
 +/* Sets a super-cell and sub cell bounding boxes, including PBC shift */
 +static void set_icell_bbxxxx_supersub(const float *bb, int ci,
 +                                      real shx, real shy, real shz,
 +                                      float *bb_ci)
 +{
 +    int ia = ci*(c_gpuNumClusterPerCell >> STRIDE_PBB_2LOG)*NNBSBB_XXXX;
 +    for (int m = 0; m < (c_gpuNumClusterPerCell >> STRIDE_PBB_2LOG)*NNBSBB_XXXX; m += NNBSBB_XXXX)
 +    {
 +        for (int 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;
 +        }
 +    }
 +}
 +#endif
 +
 +/* Sets a super-cell and sub cell bounding boxes, including PBC shift */
 +static void set_icell_bb_supersub(const nbnxn_bb_t *bb, int ci,
 +                                  real shx, real shy, real shz,
 +                                  nbnxn_bb_t *bb_ci)
 +{
 +    for (int i = 0; i < c_gpuNumClusterPerCell; i++)
 +    {
 +        set_icell_bb_simple(bb, ci*c_gpuNumClusterPerCell+i,
 +                            shx, shy, shz,
 +                            &bb_ci[i]);
 +    }
 +}
 +
 +/* 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 stride, const real *x,
 +                               nbnxn_list_work_t *work)
 +{
 +    int ia = ci*NBNXN_CPU_CLUSTER_I_SIZE;
 +
 +    for (int 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 stride, const real *x,
 +                                 nbnxn_list_work_t *work)
 +{
 +#if !NBNXN_SEARCH_BB_SIMD4
 +
 +    real * x_ci = work->x_ci;
 +
 +    int    ia = ci*c_gpuNumClusterPerCell*c_nbnxnGpuClusterSize;
 +    for (int i = 0; i < c_gpuNumClusterPerCell*c_nbnxnGpuClusterSize; 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;
 +    }
 +
 +#else /* !NBNXN_SEARCH_BB_SIMD4 */
 +
 +    real * x_ci = work->x_ci_simd;
 +
 +    for (int si = 0; si < c_gpuNumClusterPerCell; si++)
 +    {
 +        for (int i = 0; i < c_nbnxnGpuClusterSize; i += GMX_SIMD4_WIDTH)
 +        {
 +            int io = si*c_nbnxnGpuClusterSize + i;
 +            int ia = ci*c_gpuNumClusterPerCell*c_nbnxnGpuClusterSize + io;
 +            for (int j = 0; j < GMX_SIMD4_WIDTH; j++)
 +            {
 +                x_ci[io*DIM + j + XX*GMX_SIMD4_WIDTH] = x[(ia + j)*stride + XX] + shx;
 +                x_ci[io*DIM + j + YY*GMX_SIMD4_WIDTH] = x[(ia + j)*stride + YY] + shy;
 +                x_ci[io*DIM + j + ZZ*GMX_SIMD4_WIDTH] = x[(ia + j)*stride + ZZ] + shz;
 +            }
 +        }
 +    }
 +
 +#endif /* !NBNXN_SEARCH_BB_SIMD4 */
 +}
 +
 +static real minimum_subgrid_size_xy(const nbnxn_grid_t *grid)
 +{
 +    if (grid->bSimple)
 +    {
 +        return std::min(grid->sx, grid->sy);
 +    }
 +    else
 +    {
 +        return std::min(grid->sx/c_gpuNumClusterPerCellX,
 +                        grid->sy/c_gpuNumClusterPerCellY);
 +    }
 +}
 +
 +static real effective_buffer_1x1_vs_MxN(const nbnxn_grid_t *gridi,
 +                                        const nbnxn_grid_t *gridj)
 +{
 +    const real eff_1x1_buffer_fac_overest = 0.1;
 +
 +    /* Determine an atom-pair list cut-off buffer size for atom pairs,
 +     * to be added to rlist (including buffer) used for MxN.
 +     * This is for converting an MxN list to a 1x1 list. This means we can't
 +     * use the normal buffer estimate, as we have an MxN list in which
 +     * some atom pairs beyond rlist are missing. We want to capture
 +     * the beneficial effect of buffering by extra pairs just outside rlist,
 +     * while removing the useless pairs that are further away from rlist.
 +     * (Also the buffer could have been set manually not using the estimate.)
 +     * This buffer size is an overestimate.
 +     * We add 10% of the smallest grid sub-cell dimensions.
 +     * Note that the z-size differs per cell and we don't use this,
 +     * so we overestimate.
 +     * With PME, the 10% value gives a buffer that is somewhat larger
 +     * than the effective buffer with a tolerance of 0.005 kJ/mol/ps.
 +     * Smaller tolerances or using RF lead to a smaller effective buffer,
 +     * so 10% gives a safe overestimate.
 +     */
 +    return eff_1x1_buffer_fac_overest*(minimum_subgrid_size_xy(gridi) +
 +                                       minimum_subgrid_size_xy(gridj));
 +}
 +
 +/* Clusters at the cut-off only increase rlist by 60% of their size */
 +static real nbnxn_rlist_inc_outside_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_j, real atom_density)
 +{
 +    int  cluster_size_i;
 +    real vol_inc_i, vol_inc_j;
 +
 +    /* We should get this from the setup, but currently it's the same for
 +     * all setups, including GPUs.
 +     */
 +    cluster_size_i = NBNXN_CPU_CLUSTER_I_SIZE;
 +
 +    vol_inc_i = (cluster_size_i - 1)/atom_density;
 +    vol_inc_j = (cluster_size_j - 1)/atom_density;
 +
 +    return nbnxn_rlist_inc_outside_fac*std::cbrt(vol_inc_i + vol_inc_j);
 +}
 +
 +/* Estimates the interaction volume^2 for non-local interactions */
 +static real nonlocal_vol2(const struct gmx_domdec_zones_t *zones, rvec ls, real r)
 +{
 +    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 (int 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 (int 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 void get_nsubpair_target(const nbnxn_search_t  nbs,
 +                                int                   iloc,
 +                                real                  rlist,
 +                                int                   min_ci_balanced,
 +                                int                  *nsubpair_target,
-         /* We don't need to balance the list sizes */
++                                float                *nsubpair_tot_est)
 +{
 +    /* The target value of 36 seems to be the optimum for Kepler.
 +     * Maxwell is less sensitive to the exact value.
 +     */
 +    const int           nsubpair_target_min = 36;
 +    const nbnxn_grid_t *grid;
 +    rvec                ls;
 +    real                xy_diag2, r_eff_sup, vol_est, nsp_est, nsp_est_nl;
 +
 +    grid = &nbs->grid[0];
 +
++    /* We don't need to balance list sizes if:
++     * - We didn't request balancing.
++     * - The number of grid cells >= the number of lists requested,
++     *   since we will always generate at least #cells lists.
++     * - We don't have any cells, since then there won't be any lists.
++     */
 +    if (min_ci_balanced <= 0 || grid->nc >= min_ci_balanced || grid->nc == 0)
 +    {
-                                      int nsubpair_tot_est,
++        /* nsubpair_target==0 signals no balancing */
 +        *nsubpair_target  = 0;
 +        *nsubpair_tot_est = 0;
 +
 +        return;
 +    }
 +
 +    ls[XX] = (grid->c1[XX] - grid->c0[XX])/(grid->ncx*c_gpuNumClusterPerCellX);
 +    ls[YY] = (grid->c1[YY] - grid->c0[YY])/(grid->ncy*c_gpuNumClusterPerCellY);
 +    ls[ZZ] = grid->na_c/(grid->atom_density*ls[XX]*ls[YY]);
 +
 +    /* 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_outside_fac*gmx::square((grid->na_c - 1.0)/grid->na_c)*std::sqrt(xy_diag2/3);
 +
 +    if (!nbs->DomDec || nbs->zones->n == 1)
 +    {
 +        nsp_est_nl = 0;
 +    }
 +    else
 +    {
 +        nsp_est_nl =
 +            gmx::square(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*gmx::square(r_eff_sup);
 +        /* 4 octants of a sphere */
 +        vol_est += 0.5*4.0/3.0*M_PI*gmx::power3(r_eff_sup);
 +
 +        /* Estimate the number of cluster pairs as the local number of
 +         * clusters times the volume they interact with times the density.
 +         */
 +        nsp_est = grid->nsubc_tot*vol_est*grid->atom_density/grid->na_c;
 +
 +        /* Subtract the non-local pair count */
 +        nsp_est -= nsp_est_nl;
 +
 +        /* For small cut-offs nsp_est will be an underesimate.
 +         * With DD nsp_est_nl is an overestimate so nsp_est can get negative.
 +         * So to avoid too small or negative nsp_est we set a minimum of
 +         * all cells interacting with all 3^3 direct neighbors (3^3-1)/2+1=14.
 +         * This might be a slight overestimate for small non-periodic groups of
 +         * atoms as will occur for a local domain with DD, but for small
 +         * groups of atoms we'll anyhow be limited by nsubpair_target_min,
 +         * so this overestimation will not matter.
 +         */
 +        nsp_est = std::max(nsp_est, grid->nsubc_tot*static_cast<real>(14));
 +
 +        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;
 +    }
 +
 +    /* Thus the (average) maximum j-list size should be as follows.
 +     * Since there is overhead, we shouldn't make the lists too small
 +     * (and we can't chop up j-groups) so we use a minimum target size of 36.
 +     */
 +    *nsubpair_target  = std::max(nsubpair_target_min,
 +                                 static_cast<int>(nsp_est/min_ci_balanced + 0.5));
 +    *nsubpair_tot_est = static_cast<int>(nsp_est);
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "nbl nsp estimate %.1f, nsubpair_target %d\n",
 +                nsp_est, *nsubpair_target);
 +    }
 +}
 +
 +/* Debug list print function */
 +static void print_nblist_ci_cj(FILE *fp, const nbnxn_pairlist_t *nbl)
 +{
 +    for (int 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 (int 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)
 +{
 +    for (int 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);
 +
 +        int ncp = 0;
 +        for (int j4 = nbl->sci[i].cj4_ind_start; j4 < nbl->sci[i].cj4_ind_end; j4++)
 +        {
 +            for (int j = 0; j < c_nbnxnGpuJgroupSize; j++)
 +            {
 +                fprintf(fp, "  sj %5d  imask %x\n",
 +                        nbl->cj4[j4].cj[j],
 +                        nbl->cj4[j4].imei[0].imask);
 +                for (int si = 0; si < c_gpuNumClusterPerCell; si++)
 +                {
 +                    if (nbl->cj4[j4].imei[0].imask & (1U << (j*c_gpuNumClusterPerCell + 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;
 +
 +    if (nblc->bSimple)
 +    {
 +        gmx_incons("combine_nblists does not support simple lists");
 +    }
 +
 +    nsci  = nblc->nsci;
 +    ncj4  = nblc->ncj4;
 +    nexcl = nblc->nexcl;
 +    for (int 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.
 +     */
 +#if GMX_OPENMP && !(defined __clang_analyzer__)
 +    // cppcheck-suppress unreadVariable
 +    int nthreads = gmx_omp_nthreads_get(emntPairsearch);
 +#endif
 +
 +#pragma omp parallel for num_threads(nthreads) schedule(static)
 +    for (int n = 0; n < nnbl; n++)
 +    {
 +        try
 +        {
 +            int                     sci_offset;
 +            int                     cj4_offset;
 +            int                     excl_offset;
 +            const nbnxn_pairlist_t *nbli;
 +
 +            /* Determine the offset in the combined data for our thread */
 +            sci_offset  = nblc->nsci;
 +            cj4_offset  = nblc->ncj4;
 +            excl_offset = nblc->nexcl;
 +
 +            for (int i = 0; i < n; i++)
 +            {
 +                sci_offset  += nbl[i]->nsci;
 +                cj4_offset  += nbl[i]->ncj4;
 +                excl_offset += nbl[i]->nexcl;
 +            }
 +
 +            nbli = nbl[n];
 +
 +            for (int 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 (int 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 (int j4 = 0; j4 < nbli->nexcl; j4++)
 +            {
 +                nblc->excl[excl_offset+j4] = nbli->excl[j4];
 +            }
 +        }
 +        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 +    }
 +
 +    for (int 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;
 +    }
 +}
 +
 +static void balance_fep_lists(const nbnxn_search_t  nbs,
 +                              nbnxn_pairlist_set_t *nbl_lists)
 +{
 +    int       nnbl;
 +    int       nri_tot, nrj_tot, nrj_target;
 +    int       th_dest;
 +    t_nblist *nbld;
 +
 +    nnbl = nbl_lists->nnbl;
 +
 +    if (nnbl == 1)
 +    {
 +        /* Nothing to balance */
 +        return;
 +    }
 +
 +    /* Count the total i-lists and pairs */
 +    nri_tot = 0;
 +    nrj_tot = 0;
 +    for (int th = 0; th < nnbl; th++)
 +    {
 +        nri_tot += nbl_lists->nbl_fep[th]->nri;
 +        nrj_tot += nbl_lists->nbl_fep[th]->nrj;
 +    }
 +
 +    nrj_target = (nrj_tot + nnbl - 1)/nnbl;
 +
 +    assert(gmx_omp_nthreads_get(emntNonbonded) == nnbl);
 +
 +#pragma omp parallel for schedule(static) num_threads(nnbl)
 +    for (int th = 0; th < nnbl; th++)
 +    {
 +        try
 +        {
 +            t_nblist *nbl;
 +
 +            nbl = nbs->work[th].nbl_fep;
 +
 +            /* Note that here we allocate for the total size, instead of
 +             * a per-thread esimate (which is hard to obtain).
 +             */
 +            if (nri_tot > nbl->maxnri)
 +            {
 +                nbl->maxnri = over_alloc_large(nri_tot);
 +                reallocate_nblist(nbl);
 +            }
 +            if (nri_tot > nbl->maxnri || nrj_tot > nbl->maxnrj)
 +            {
 +                nbl->maxnrj = over_alloc_small(nrj_tot);
 +                srenew(nbl->jjnr, nbl->maxnrj);
 +                srenew(nbl->excl_fep, nbl->maxnrj);
 +            }
 +
 +            clear_pairlist_fep(nbl);
 +        }
 +        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 +    }
 +
 +    /* Loop over the source lists and assign and copy i-entries */
 +    th_dest = 0;
 +    nbld    = nbs->work[th_dest].nbl_fep;
 +    for (int th = 0; th < nnbl; th++)
 +    {
 +        t_nblist *nbls;
 +
 +        nbls = nbl_lists->nbl_fep[th];
 +
 +        for (int i = 0; i < nbls->nri; i++)
 +        {
 +            int nrj;
 +
 +            /* The number of pairs in this i-entry */
 +            nrj = nbls->jindex[i+1] - nbls->jindex[i];
 +
 +            /* Decide if list th_dest is too large and we should procede
 +             * to the next destination list.
 +             */
 +            if (th_dest+1 < nnbl && nbld->nrj > 0 &&
 +                nbld->nrj + nrj - nrj_target > nrj_target - nbld->nrj)
 +            {
 +                th_dest++;
 +                nbld = nbs->work[th_dest].nbl_fep;
 +            }
 +
 +            nbld->iinr[nbld->nri]  = nbls->iinr[i];
 +            nbld->gid[nbld->nri]   = nbls->gid[i];
 +            nbld->shift[nbld->nri] = nbls->shift[i];
 +
 +            for (int j = nbls->jindex[i]; j < nbls->jindex[i+1]; j++)
 +            {
 +                nbld->jjnr[nbld->nrj]     = nbls->jjnr[j];
 +                nbld->excl_fep[nbld->nrj] = nbls->excl_fep[j];
 +                nbld->nrj++;
 +            }
 +            nbld->nri++;
 +            nbld->jindex[nbld->nri] = nbld->nrj;
 +        }
 +    }
 +
 +    /* Swap the list pointers */
 +    for (int th = 0; th < nnbl; th++)
 +    {
 +        t_nblist *nbl_tmp;
 +
 +        nbl_tmp                = nbl_lists->nbl_fep[th];
 +        nbl_lists->nbl_fep[th] = nbs->work[th].nbl_fep;
 +        nbs->work[th].nbl_fep  = nbl_tmp;
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "nbl_fep[%d] nri %4d nrj %4d\n",
 +                    th,
 +                    nbl_lists->nbl_fep[th]->nri,
 +                    nbl_lists->nbl_fep[th]->nrj);
 +        }
 +    }
 +}
 +
 +/* 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 /= c_gpuNumClusterPerCellX;
 +        bby /= c_gpuNumClusterPerCellY;
 +    }
 +
 +    rbb2 = std::max(0.0, rlist - 0.5*std::sqrt(bbx*bbx + bby*bby));
 +    rbb2 = rbb2 * rbb2;
 +
 +#if !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 || nth*3*ci_block > gridi->nc)
 +    {
 +        ci_block = (gridi->nc + nth - 1)/nth;
 +    }
 +
 +    if (ci_block > 1 && (nth - 1)*ci_block >= gridi->nc)
 +    {
 +        /* Some threads have no work. Although reducing the block size
 +         * does not decrease the block count on the first few threads,
 +         * with GPUs better mixing of "upper" cells that have more empty
 +         * clusters results in a somewhat lower max load over all threads.
 +         * Without GPUs the regime of so few atoms per thread is less
 +         * performance relevant, but with 8-wide SIMD the same reasoning
 +         * applies, since the pair list uses 4 i-atom "sub-clusters".
 +         */
 +        ci_block--;
 +    }
 +
 +    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                nsubpair_target, nsubpair_tot_est;
++                                     float nsubpair_tot_est,
 +                                     int th, int nth,
 +                                     nbnxn_pairlist_t *nbl,
 +                                     t_nblist *nbl_fep)
 +{
 +    int               na_cj_2log;
 +    matrix            box;
 +    real              rl2, rl_fep2 = 0;
 +    float             rbb2;
 +    int               ci_b, ci, ci_x, ci_y, ci_xy, cj;
 +    ivec              shp;
 +    int               shift;
 +    real              shx, shy, shz;
 +    int               conv_i, cell0_i;
 +    const nbnxn_bb_t *bb_i = NULL;
 +#if NBNXN_BBXXXX
 +    const float      *pbb_i = NULL;
 +#endif
 +    const float      *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               c0, c1, cs, cf, cl;
 +    int               ndistc;
 +    int               ncpcheck;
 +    int               gridi_flag_shift = 0, gridj_flag_shift = 0;
 +    gmx_bitmask_t    *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_cluster_j_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;
 +
 +    if (nbs->bFEP && !nbl->bSimple)
 +    {
 +        /* Determine an atom-pair list cut-off distance for FEP atom pairs.
 +         * We should not simply use rlist, since then we would not have
 +         * the small, effective buffering of the NxN lists.
 +         * The buffer is on overestimate, but the resulting cost for pairs
 +         * beyond rlist is neglible compared to the FEP pairs within rlist.
 +         */
 +        rl_fep2 = nbl->rlist + effective_buffer_1x1_vs_MxN(gridi, gridj);
 +
 +        if (debug)
 +        {
 +            fprintf(debug, "nbl_fep atom-pair rlist %f\n", rl_fep2);
 +        }
 +        rl_fep2 = rl_fep2*rl_fep2;
 +    }
 +
 +    rbb2 = boundingbox_only_distance2(gridi, gridj, nbl->rlist, nbl->bSimple);
 +
 +    if (debug)
 +    {
 +        fprintf(debug, "nbl bounding box only distance %f\n", std::sqrt(rbb2));
 +    }
 +
 +    /* Set the shift range */
 +    for (int 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]) < std::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;
 +#if NBNXN_BBXXXX
 +        if (gridi->bSimple)
 +        {
 +            bb_i  = gridi->bb;
 +        }
 +        else
 +        {
 +            pbb_i = gridi->pbb;
 +        }
 +#else
 +        /* We use the normal bounding box format for both grid types */
 +        bb_i  = gridi->bb;
 +#endif
 +        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].upper[BB_X];
 +            }
 +            else
 +            {
 +                bx1 = gridi->c0[XX] + (ci_x+1)*gridi->sx;
 +            }
 +            if (bx1 < gridj->c0[XX])
 +            {
 +                d2cx = gmx::square(gridj->c0[XX] - bx1);
 +
 +                if (d2cx >= rl2)
 +                {
 +                    continue;
 +                }
 +            }
 +        }
 +
 +        ci_xy = ci_x*gridi->ncy + ci_y;
 +
 +        /* Loop over shift vectors in three dimensions */
 +        for (int 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 = gmx::square(bz1);
 +            }
 +            else
 +            {
 +                d2z = gmx::square(bz0 - box[ZZ][ZZ]);
 +            }
 +
 +            d2z_cx = d2z + d2cx;
 +
 +            if (d2z_cx >= rl2)
 +            {
 +                continue;
 +            }
 +
 +            bz1_frac = bz1/(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 (int ty = -shp[YY]; ty <= shp[YY]; ty++)
 +            {
 +                shy = ty*box[YY][YY] + tz*box[ZZ][YY];
 +
 +                if (nbl->bSimple)
 +                {
 +                    by0 = bb_i[ci].lower[BB_Y] + shy;
 +                    by1 = bb_i[ci].upper[BB_Y] + 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 += gmx::square(gridj->c0[YY] - by1);
 +                }
 +                else if (by0 > gridj->c1[YY])
 +                {
 +                    d2z_cy += gmx::square(by0 - gridj->c1[YY]);
 +                }
 +
 +                for (int tx = -shp[XX]; tx <= shp[XX]; tx++)
 +                {
 +                    shift = XYZ2IS(tx, ty, tz);
 +
 +                    if (pbc_shift_backward && gridi == gridj && shift > CENTRAL)
 +                    {
 +                        continue;
 +                    }
 +
 +                    shx = tx*box[XX][XX] + ty*box[YY][XX] + tz*box[ZZ][XX];
 +
 +                    if (nbl->bSimple)
 +                    {
 +                        bx0 = bb_i[ci].lower[BB_X] + shx;
 +                        bx1 = bb_i[ci].upper[BB_X] + 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]);
 +                    }
 +                    else
 +                    {
 +                        new_sci_entry(nbl, cell0_i+ci, shift);
 +                    }
 +
 +                    if ((!pbc_shift_backward || (shift == CENTRAL &&
 +                                                 gridi == gridj)) &&
 +                        cxf < ci_x)
 +                    {
 +                        /* 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
 +                    {
 +#if NBNXN_BBXXXX
 +                        set_icell_bbxxxx_supersub(pbb_i, ci, shx, shy, shz,
 +                                                  nbl->work->pbb_ci);
 +#else
 +                        set_icell_bb_supersub(bb_i, ci, shx, shy, shz,
 +                                              nbl->work->bb_ci);
 +#endif
 +                    }
 +
 +                    nbs->icell_set_x(cell0_i+ci, shx, shy, shz,
 +                                     nbat->xstride, nbat->x,
 +                                     nbl->work);
 +
 +                    for (int cx = cxf; cx <= cxl; cx++)
 +                    {
 +                        d2zx = d2z;
 +                        if (gridj->c0[XX] + cx*gridj->sx > bx1)
 +                        {
 +                            d2zx += gmx::square(gridj->c0[XX] + cx*gridj->sx - bx1);
 +                        }
 +                        else if (gridj->c0[XX] + (cx+1)*gridj->sx < bx0)
 +                        {
 +                            d2zx += gmx::square(gridj->c0[XX] + (cx+1)*gridj->sx - bx0);
 +                        }
 +
 +                        if (gridi == gridj &&
 +                            cx == 0 &&
 +                            (!pbc_shift_backward || shift == CENTRAL) &&
 +                            cyf < ci_y)
 +                        {
 +                            /* 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 (int cy = cyf_x; cy <= cyl; cy++)
 +                        {
 +                            c0 = gridj->cxy_ind[cx*gridj->ncy+cy];
 +                            c1 = gridj->cxy_ind[cx*gridj->ncy+cy+1];
 +
 +                            if (pbc_shift_backward &&
 +                                gridi == gridj &&
 +                                shift == CENTRAL && c0 < ci)
 +                            {
 +                                c0 = ci;
 +                            }
 +
 +                            d2zxy = d2zx;
 +                            if (gridj->c0[YY] + cy*gridj->sy > by1)
 +                            {
 +                                d2zxy += gmx::square(gridj->c0[YY] + cy*gridj->sy - by1);
 +                            }
 +                            else if (gridj->c0[YY] + (cy+1)*gridj->sy < by0)
 +                            {
 +                                d2zxy += gmx::square(gridj->c0[YY] + (cy+1)*gridj->sy - by0);
 +                            }
 +                            if (c1 > c0 && d2zxy < rl2)
 +                            {
 +                                cs = c0 + static_cast<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 + gmx::square(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 + gmx::square(bbcz_j[cl*NNBSBB_D] - bz1) < rl2))
 +                                {
 +                                    cl++;
 +                                }
 +
 +#ifdef NBNXN_REFCODE
 +                                {
 +                                    /* Simple reference code, for debugging,
 +                                     * overrides the more complex code above.
 +                                     */
 +                                    cf = c1;
 +                                    cl = -1;
 +                                    for (int k = c0; k < c1; k++)
 +                                    {
 +                                        if (box_dist2(bx0, bx1, by0, by1, bz0, bz1, bb+k) < rl2 &&
 +                                            k < cf)
 +                                        {
 +                                            cf = k;
 +                                        }
 +                                        if (box_dist2(bx0, bx1, by0, by1, bz0, bz1, bb+k) < rl2 &&
 +                                            k > cl)
 +                                        {
 +                                            cl = k;
 +                                        }
 +                                    }
 +                                }
 +#endif
 +
 +                                if (gridi == gridj)
 +                                {
 +                                    /* We want each atom/cell pair only once,
 +                                     * only use cj >= ci.
 +                                     */
 +                                    if (!pbc_shift_backward || shift == CENTRAL)
 +                                    {
 +                                        cf = std::max(cf, ci);
 +                                    }
 +                                }
 +
 +                                if (cf <= cl)
 +                                {
 +                                    /* For f buffer flags with simple lists */
 +                                    ncj_old_j = nbl->ncj;
 +
 +                                    switch (nb_kernel_type)
 +                                    {
 +                                        case nbnxnk4x4_PlainC:
 +                                            check_cell_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_cell_list_space_simple(nbl, ci_to_cj_simd_4xn(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_cell_list_space_simple(nbl, ci_to_cj_simd_2xnn(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_GPU:
 +                                            check_cell_list_space_supersub(nbl, cl-cf+1);
 +                                            for (cj = cf; cj <= cl; cj++)
 +                                            {
 +                                                make_cluster_list_supersub(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 = nbl->cj[ncj_old_j].cj >> gridj_flag_shift;
 +                                        int cbl = nbl->cj[nbl->ncj-1].cj >> gridj_flag_shift;
 +                                        for (int cb = cbf; cb <= cbl; cb++)
 +                                        {
 +                                            bitmask_init_bit(&gridj_flag[cb], 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);
 +
 +                        if (nbs->bFEP)
 +                        {
 +                            make_fep_list(nbs, nbat, nbl,
 +                                          shift == CENTRAL && gridi == gridj,
 +                                          &(nbl->ci[nbl->nci]),
 +                                          gridi, gridj, nbl_fep);
 +                        }
 +                    }
 +                    else
 +                    {
 +                        set_sci_top_excls(nbs,
 +                                          nbl,
 +                                          shift == CENTRAL && gridi == gridj,
 +                                          gridj->na_c_2log,
 +                                          &(nbl->sci[nbl->nsci]),
 +                                          excl);
 +
 +                        if (nbs->bFEP)
 +                        {
 +                            make_fep_list_supersub(nbs, nbat, nbl,
 +                                                   shift == CENTRAL && gridi == gridj,
 +                                                   &(nbl->sci[nbl->nsci]),
 +                                                   shx, shy, shz,
 +                                                   rl_fep2,
 +                                                   gridi, gridj, nbl_fep);
 +                        }
 +                    }
 +
 +                    /* Close this ci list */
 +                    if (nbl->bSimple)
 +                    {
 +                        close_ci_entry_simple(nbl);
 +                    }
 +                    else
 +                    {
 +                        close_ci_entry_supersub(nbl,
 +                                                nsubpair_max,
 +                                                progBal, nsubpair_tot_est,
 +                                                th, nth);
 +                    }
 +                }
 +            }
 +        }
 +
 +        if (bFBufferFlag && nbl->ncj > ncj_old_i)
 +        {
 +            bitmask_init_bit(&(work->buffer_flags.flag[(gridi->cell0+ci)>>gridi_flag_shift]), 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);
 +        }
 +
 +        if (nbs->bFEP)
 +        {
 +            fprintf(debug, "nbl FEP list pairs: %d\n", nbl_fep->nrj);
 +        }
 +    }
 +}
 +
 +static void reduce_buffer_flags(const nbnxn_search_t        nbs,
 +                                int                         nsrc,
 +                                const nbnxn_buffer_flags_t *dest)
 +{
 +    for (int s = 0; s < nsrc; s++)
 +    {
 +        gmx_bitmask_t * flag = nbs->work[s].buffer_flags.flag;
 +
 +        for (int b = 0; b < dest->nflag; b++)
 +        {
 +            bitmask_union(&(dest->flag[b]), flag[b]);
 +        }
 +    }
 +}
 +
 +static void print_reduction_cost(const nbnxn_buffer_flags_t *flags, int nout)
 +{
 +    int           nelem, nkeep, ncopy, nred, out;
 +    gmx_bitmask_t mask_0;
 +
 +    nelem = 0;
 +    nkeep = 0;
 +    ncopy = 0;
 +    nred  = 0;
 +    bitmask_init_bit(&mask_0, 0);
 +    for (int b = 0; b < flags->nflag; b++)
 +    {
 +        if (bitmask_is_equal(flags->flag[b], mask_0))
 +        {
 +            /* Only flag 0 is set, no copy of reduction required */
 +            nelem++;
 +            nkeep++;
 +        }
 +        else if (!bitmask_is_zero(flags->flag[b]))
 +        {
 +            int c = 0;
 +            for (out = 0; out < nout; out++)
 +            {
 +                if (bitmask_is_set(flags->flag[b], 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, 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 (int i = 0; i <= m; i++)
 +    {
 +        work->sort[i] = 0;
 +    }
 +    for (int s = 0; s < nbl->nsci; s++)
 +    {
 +        int i = std::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 (int 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 (int s = 0; s < nbl->nsci; s++)
 +    {
 +        int i = std::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, zj0, zj1;
++    int                nsubpair_target;
++    float              nsubpair_tot_est;
 +    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)
 +    {
 +#if GMX_SIMD
 +        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   // GMX_SIMD
 +        /* MSVC 2013 complains about switch statements without case */
 +        nbs->icell_set_x = icell_set_x_simple;
 +#endif  // GMX_SIMD
 +    }
 +    else
 +    {
 +        nbs->icell_set_x = icell_set_x_supersub;
 +    }
 +
 +    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)
 +    {
 +        get_nsubpair_target(nbs, iloc, rlist, min_ci_balanced,
 +                            &nsubpair_target, &nsubpair_tot_est);
 +    }
 +    else
 +    {
 +        nsubpair_target  = 0;
 +        nsubpair_tot_est = 0;
 +    }
 +
 +    /* Clear all pair-lists */
 +    for (int th = 0; th < nnbl; th++)
 +    {
 +        clear_pairlist(nbl[th]);
 +
 +        if (nbs->bFEP)
 +        {
 +            clear_pairlist_fep(nbl_list->nbl_fep[th]);
 +        }
 +    }
 +
 +    for (int 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 (int 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);
 +            }
 +
 +            /* 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);
 +
 +#pragma omp parallel for num_threads(nnbl) schedule(static)
 +            for (int th = 0; th < nnbl; th++)
 +            {
 +                try
 +                {
 +                    /* 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]);
 +                    }
 +
 +                    /* 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_target,
 +                                             progBal, nsubpair_tot_est,
 +                                             th, nnbl,
 +                                             nbl[th],
 +                                             nbl_list->nbl_fep[th]);
 +                }
 +                GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 +            }
 +            nbs_cycle_stop(&nbs->cc[enbsCCsearch]);
 +
 +            np_tot = 0;
 +            np_noq = 0;
 +            np_hlj = 0;
 +            for (int 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 (int th = 0; th < nnbl; th++)
 +            {
 +                try
 +                {
 +                    sort_sci(nbl[th]);
 +                }
 +                GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 +            }
 +        }
 +    }
 +
 +    if (nbat->bUseBufferFlags)
 +    {
 +        reduce_buffer_flags(nbs, nnbl, &nbat->buffer_flags);
 +    }
 +
 +    if (nbs->bFEP)
 +    {
 +        /* Balance the free-energy lists over all the threads */
 +        balance_fep_lists(nbs, nbl_list);
 +    }
 +
 +    /* Special performance logging stuff (env.var. GMX_NBNXN_CYCLE) */
 +    if (LOCAL_I(iloc))
 +    {
 +        nbs->search_count++;
 +    }
 +    if (nbs->print_cycles &&
 +        (!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 e8e117b4287b59f823de629bb02c7fa0e36d8436,0000000000000000000000000000000000000000..78ece843ca9321ea3707a4833b826577dbefeb37
mode 100644,000000..100644
--- /dev/null
@@@ -1,333 -1,0 +1,333 @@@
-  * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
-     ekind->bNEMD = (opts->ngacc > 1 || norm(opts->acc[0]) > 0);
++ * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +/* This file is completely threadsafe - keep it that way! */
 +#include "gmxpre.h"
 +
 +#include "tgroup.h"
 +
 +#include <math.h>
 +
 +#include "gromacs/gmxlib/network.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/mdlib/gmx_omp_nthreads.h"
 +#include "gromacs/mdlib/rbin.h"
 +#include "gromacs/mdlib/update.h"
 +#include "gromacs/mdtypes/group.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/mdatom.h"
 +#include "gromacs/topology/mtop_util.h"
 +#include "gromacs/topology/topology.h"
 +#include "gromacs/utility/exceptions.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/futil.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +static void init_grptcstat(int ngtc, t_grp_tcstat tcstat[])
 +{
 +    int i;
 +
 +    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(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 gmx_unused *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 || norm2(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++)
 +    {
 +        try
 +        {
 +#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
 +        }
 +        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 +    }
 +
 +    ekind->ngacc = opts->ngacc;
 +    snew(ekind->grpstat, opts->ngacc);
 +    init_grpstat(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 bScaleEkin)
 +{
 +    int           i, j, m, ngtc;
 +    real          T;
 +    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
 +            {
 +                /* 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 95e13233f98bce2e8d712ac591f279a1de968090,0000000000000000000000000000000000000000..c5afe10f5825604a275097a935a50a9806191e2b
mode 100644,000000..100644
--- /dev/null
@@@ -1,1445 -1,0 +1,1469 @@@
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +/* This file is completely threadsafe - keep it that way! */
 +
 +#include "gmxpre.h"
 +
 +#include <cmath>
 +#include <cstdio>
 +#include <cstring>
 +
 +#include <algorithm>
 +
 +#include "gromacs/fileio/enxio.h"
 +#include "gromacs/fileio/tpxio.h"
 +#include "gromacs/fileio/trxio.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/mdtypes/pull-params.h"
 +#include "gromacs/topology/ifunc.h"
 +#include "gromacs/topology/mtop_util.h"
 +#include "gromacs/topology/topology.h"
 +#include "gromacs/trajectory/trajectoryframe.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/futil.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/utility/stringutil.h"
 +
 +static void cmp_int(FILE *fp, const char *s, int index, int i1, int i2)
 +{
 +    if (i1 != i2)
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%d] (%d - %d)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%d - %d)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +static void cmp_int64(FILE *fp, const char *s, gmx_int64_t i1, gmx_int64_t i2)
 +{
 +    if (i1 != i2)
 +    {
 +        fprintf(fp, "%s (", s);
 +        fprintf(fp, "%" GMX_PRId64, i1);
 +        fprintf(fp, " - ");
 +        fprintf(fp, "%" GMX_PRId64, i2);
 +        fprintf(fp, ")\n");
 +    }
 +}
 +
 +static void cmp_us(FILE *fp, const char *s, int index, unsigned short i1, unsigned short i2)
 +{
 +    if (i1 != i2)
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%d] (%hu - %hu)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%hu - %hu)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +static void cmp_uc(FILE *fp, const char *s, int index, unsigned char i1, unsigned char i2)
 +{
 +    if (i1 != i2)
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%d] (%d - %d)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%d - %d)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +static gmx_bool cmp_bool(FILE *fp, const char *s, int index, gmx_bool b1, gmx_bool b2)
 +{
 +    if (b1)
 +    {
 +        b1 = 1;
 +    }
 +    else
 +    {
 +        b1 = 0;
 +    }
 +    if (b2)
 +    {
 +        b2 = 1;
 +    }
 +    else
 +    {
 +        b2 = 0;
 +    }
 +    if (b1 != b2)
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%d] (%s - %s)\n", s, index,
 +                    gmx::boolToString(b1), gmx::boolToString(b2));
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%s - %s)\n", s,
 +                    gmx::boolToString(b1), gmx::boolToString(b2));
 +        }
 +    }
 +    return b1 && b2;
 +}
 +
 +static void cmp_str(FILE *fp, const char *s, int index,
 +                    const char *s1, const char *s2)
 +{
 +    if (std::strcmp(s1, s2) != 0)
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%d] (%s - %s)\n", s, index, s1, s2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%s - %s)\n", s, s1, s2);
 +        }
 +    }
 +}
 +
 +static gmx_bool equal_real(real i1, real i2, real ftol, real abstol)
 +{
 +    return ( ( 2*fabs(i1 - i2) <= (fabs(i1) + fabs(i2))*ftol ) || fabs(i1-i2) <= abstol );
 +}
 +
 +static gmx_bool equal_float(float i1, float i2, float ftol, float abstol)
 +{
 +    return ( ( 2*fabs(i1 - i2) <= (fabs(i1) + fabs(i2))*ftol ) || fabs(i1-i2) <= abstol );
 +}
 +
 +static gmx_bool equal_double(double i1, double i2, real ftol, real abstol)
 +{
 +    return ( ( 2*fabs(i1 - i2) <= (fabs(i1) + fabs(i2))*ftol ) || fabs(i1-i2) <= abstol );
 +}
 +
 +static void
 +cmp_real(FILE *fp, const char *s, int index, real i1, real i2, real ftol, real abstol)
 +{
 +    if (!equal_real(i1, i2, ftol, abstol))
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%2d] (%e - %e)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%e - %e)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +static void
 +cmp_float(FILE *fp, const char *s, int index, float i1, float i2, float ftol, float abstol)
 +{
 +    if (!equal_float(i1, i2, ftol, abstol))
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%2d] (%e - %e)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%e - %e)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +
 +
 +static void
 +cmp_double(FILE *fp, const char *s, int index, double i1, double i2, double ftol, double abstol)
 +{
 +    if (!equal_double(i1, i2, ftol, abstol))
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%2d] (%16.9e - %16.9e)\n", s, index, i1, i2);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%16.9e - %16.9e)\n", s, i1, i2);
 +        }
 +    }
 +}
 +
 +static void cmp_rvec(FILE *fp, const char *s, int index, rvec i1, rvec i2, real ftol, real abstol)
 +{
 +    if (!equal_real(i1[XX], i2[XX], ftol, abstol) ||
 +        !equal_real(i1[YY], i2[YY], ftol, abstol) ||
 +        !equal_real(i1[ZZ], i2[ZZ], ftol, abstol))
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%5d] (%12.5e %12.5e %12.5e) - (%12.5e %12.5e %12.5e)\n",
 +                    s, index, i1[XX], i1[YY], i1[ZZ], i2[XX], i2[YY], i2[ZZ]);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%12.5e %12.5e %12.5e) - (%12.5e %12.5e %12.5e)\n",
 +                    s, i1[XX], i1[YY], i1[ZZ], i2[XX], i2[YY], i2[ZZ]);
 +        }
 +    }
 +}
 +
 +static void cmp_ivec(FILE *fp, const char *s, int index, ivec i1, ivec i2)
 +{
 +    if ((i1[XX] != i2[XX]) || (i1[YY] != i2[YY]) || (i1[ZZ] != i2[ZZ]))
 +    {
 +        if (index != -1)
 +        {
 +            fprintf(fp, "%s[%5d] (%8d,%8d,%8d - %8d,%8d,%8d)\n", s, index,
 +                    i1[XX], i1[YY], i1[ZZ], i2[XX], i2[YY], i2[ZZ]);
 +        }
 +        else
 +        {
 +            fprintf(fp, "%s (%8d,%8d,%8d - %8d,%8d,%8d)\n", s,
 +                    i1[XX], i1[YY], i1[ZZ], i2[XX], i2[YY], i2[ZZ]);
 +        }
 +    }
 +}
 +
 +static void cmp_ilist(FILE *fp, int ftype, t_ilist *il1, t_ilist *il2)
 +{
 +    int  i;
 +    char buf[256];
 +
 +    fprintf(fp, "comparing ilist %s\n", interaction_function[ftype].name);
 +    sprintf(buf, "%s->nr", interaction_function[ftype].name);
 +    cmp_int(fp, buf, -1, il1->nr, il2->nr);
 +    sprintf(buf, "%s->iatoms", interaction_function[ftype].name);
 +    if (((il1->nr > 0) && (!il1->iatoms)) ||
 +        ((il2->nr > 0) && (!il2->iatoms)) ||
 +        ((il1->nr != il2->nr)))
 +    {
 +        fprintf(fp, "Comparing radically different topologies - %s is different\n",
 +                buf);
 +    }
 +    else
 +    {
 +        for (i = 0; (i < il1->nr); i++)
 +        {
 +            cmp_int(fp, buf, i, il1->iatoms[i], il2->iatoms[i]);
 +        }
 +    }
 +}
 +
 +void cmp_iparm(FILE *fp, const char *s, t_functype ft,
 +               t_iparams ip1, t_iparams ip2, real ftol, real abstol)
 +{
 +    int      i;
 +    gmx_bool bDiff;
 +
 +    bDiff = FALSE;
 +    for (i = 0; i < MAXFORCEPARAM && !bDiff; i++)
 +    {
 +        bDiff = !equal_real(ip1.generic.buf[i], ip2.generic.buf[i], ftol, abstol);
 +    }
 +    if (bDiff)
 +    {
 +        fprintf(fp, "%s1: ", s);
 +        pr_iparams(fp, ft, &ip1);
 +        fprintf(fp, "%s2: ", s);
 +        pr_iparams(fp, ft, &ip2);
 +    }
 +}
 +
 +void cmp_iparm_AB(FILE *fp, const char *s, t_functype ft, t_iparams ip1, real ftol, real abstol)
 +{
 +    int      nrfpA, nrfpB, p0, i;
 +    gmx_bool bDiff;
 +
 +    /* Normally the first parameter is perturbable */
 +    p0    = 0;
 +    nrfpA = interaction_function[ft].nrfpA;
 +    nrfpB = interaction_function[ft].nrfpB;
 +    if (ft == F_PDIHS)
 +    {
 +        nrfpB = 2;
 +    }
 +    else if (interaction_function[ft].flags & IF_TABULATED)
 +    {
 +        /* For tabulated interactions only the second parameter is perturbable */
 +        p0    = 1;
 +        nrfpB = 1;
 +    }
 +    bDiff = FALSE;
 +    for (i = 0; i < nrfpB && !bDiff; i++)
 +    {
 +        bDiff = !equal_real(ip1.generic.buf[p0+i], ip1.generic.buf[nrfpA+i], ftol, abstol);
 +    }
 +    if (bDiff)
 +    {
 +        fprintf(fp, "%s: ", s);
 +        pr_iparams(fp, ft, &ip1);
 +    }
 +}
 +
++static void cmp_cmap(FILE *fp, const gmx_cmap_t *cmap1, const gmx_cmap_t *cmap2, real ftol, real abstol)
++{
++    cmp_int(fp, "cmap ngrid", -1, cmap1->ngrid, cmap2->ngrid);
++    cmp_int(fp, "cmap grid_spacing", -1, cmap1->grid_spacing, cmap2->grid_spacing);
++    if (cmap1->ngrid == cmap2->ngrid &&
++        cmap1->grid_spacing == cmap2->grid_spacing)
++    {
++        int g;
++
++        for (g = 0; g < cmap1->ngrid; g++)
++        {
++            int i;
++
++            fprintf(fp, "comparing cmap %d\n", g);
++
++            for (i = 0; i < 4*cmap1->grid_spacing*cmap1->grid_spacing; i++)
++            {
++                cmp_real(fp, "", i, cmap1->cmapdata[g].cmap[i], cmap2->cmapdata[g].cmap[i], ftol, abstol);
++            }
++        }
++    }
++}
++
 +static void cmp_idef(FILE *fp, t_idef *id1, t_idef *id2, real ftol, real abstol)
 +{
 +    int  i;
 +    char buf1[64], buf2[64];
 +
 +    fprintf(fp, "comparing idef\n");
 +    if (id2)
 +    {
 +        cmp_int(fp, "idef->ntypes", -1, id1->ntypes, id2->ntypes);
 +        cmp_int(fp, "idef->atnr",  -1, id1->atnr, id2->atnr);
 +        for (i = 0; (i < std::min(id1->ntypes, id2->ntypes)); i++)
 +        {
 +            sprintf(buf1, "idef->functype[%d]", i);
 +            sprintf(buf2, "idef->iparam[%d]", i);
 +            cmp_int(fp, buf1, i, (int)id1->functype[i], (int)id2->functype[i]);
 +            cmp_iparm(fp, buf2, id1->functype[i],
 +                      id1->iparams[i], id2->iparams[i], ftol, abstol);
 +        }
 +        cmp_real(fp, "fudgeQQ", -1, id1->fudgeQQ, id2->fudgeQQ, ftol, abstol);
++        cmp_cmap(fp, &id1->cmap_grid, &id2->cmap_grid, ftol, abstol);
 +        for (i = 0; (i < F_NRE); i++)
 +        {
 +            cmp_ilist(fp, i, &(id1->il[i]), &(id2->il[i]));
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; (i < id1->ntypes); i++)
 +        {
 +            cmp_iparm_AB(fp, "idef->iparam", id1->functype[i], id1->iparams[i], ftol, abstol);
 +        }
 +    }
 +}
 +
 +static void cmp_block(FILE *fp, t_block *b1, t_block *b2, const char *s)
 +{
 +    char buf[32];
 +
 +    fprintf(fp, "comparing block %s\n", s);
 +    sprintf(buf, "%s.nr", s);
 +    cmp_int(fp, buf, -1, b1->nr, b2->nr);
 +}
 +
 +static void cmp_blocka(FILE *fp, t_blocka *b1, t_blocka *b2, const char *s)
 +{
 +    char buf[32];
 +
 +    fprintf(fp, "comparing blocka %s\n", s);
 +    sprintf(buf, "%s.nr", s);
 +    cmp_int(fp, buf, -1, b1->nr, b2->nr);
 +    sprintf(buf, "%s.nra", s);
 +    cmp_int(fp, buf, -1, b1->nra, b2->nra);
 +}
 +
 +static void cmp_atom(FILE *fp, int index, t_atom *a1, t_atom *a2, real ftol, real abstol)
 +{
 +    if (a2)
 +    {
 +        cmp_us(fp, "atom.type", index, a1->type, a2->type);
 +        cmp_us(fp, "atom.ptype", index, a1->ptype, a2->ptype);
 +        cmp_int(fp, "atom.resind", index, a1->resind, a2->resind);
 +        cmp_int(fp, "atom.atomnumber", index, a1->atomnumber, a2->atomnumber);
 +        cmp_real(fp, "atom.m", index, a1->m, a2->m, ftol, abstol);
 +        cmp_real(fp, "atom.q", index, a1->q, a2->q, ftol, abstol);
 +        cmp_us(fp, "atom.typeB", index, a1->typeB, a2->typeB);
 +        cmp_real(fp, "atom.mB", index, a1->mB, a2->mB, ftol, abstol);
 +        cmp_real(fp, "atom.qB", index, a1->qB, a2->qB, ftol, abstol);
 +    }
 +    else
 +    {
 +        cmp_us(fp, "atom.type", index, a1->type, a1->typeB);
 +        cmp_real(fp, "atom.m", index, a1->m, a1->mB, ftol, abstol);
 +        cmp_real(fp, "atom.q", index, a1->q, a1->qB, ftol, abstol);
 +    }
 +}
 +
 +static void cmp_atoms(FILE *fp, t_atoms *a1, t_atoms *a2, real ftol, real abstol)
 +{
 +    int i;
 +
 +    fprintf(fp, "comparing atoms\n");
 +
 +    if (a2)
 +    {
 +        cmp_int(fp, "atoms->nr", -1, a1->nr, a2->nr);
 +        for (i = 0; (i < a1->nr); i++)
 +        {
 +            cmp_atom(fp, i, &(a1->atom[i]), &(a2->atom[i]), ftol, abstol);
 +        }
 +    }
 +    else
 +    {
 +        for (i = 0; (i < a1->nr); i++)
 +        {
 +            cmp_atom(fp, i, &(a1->atom[i]), NULL, ftol, abstol);
 +        }
 +    }
 +}
 +
 +static void cmp_top(FILE *fp, t_topology *t1, t_topology *t2, real ftol, real abstol)
 +{
 +    fprintf(fp, "comparing top\n");
 +    if (t2)
 +    {
 +        cmp_idef(fp, &(t1->idef), &(t2->idef), ftol, abstol);
 +        cmp_atoms(fp, &(t1->atoms), &(t2->atoms), ftol, abstol);
 +        cmp_block(fp, &t1->cgs, &t2->cgs, "cgs");
 +        cmp_block(fp, &t1->mols, &t2->mols, "mols");
 +        cmp_bool(fp, "bIntermolecularInteractions", -1, t1->bIntermolecularInteractions, t2->bIntermolecularInteractions);
 +        cmp_blocka(fp, &t1->excls, &t2->excls, "excls");
 +    }
 +    else
 +    {
 +        cmp_idef(fp, &(t1->idef), NULL, ftol, abstol);
 +        cmp_atoms(fp, &(t1->atoms), NULL, ftol, abstol);
 +    }
 +}
 +
 +static void cmp_groups(FILE *fp, gmx_groups_t *g0, gmx_groups_t *g1,
 +                       int natoms0, int natoms1)
 +{
 +    int  i, j;
 +    char buf[32];
 +
 +    fprintf(fp, "comparing groups\n");
 +
 +    for (i = 0; i < egcNR; i++)
 +    {
 +        sprintf(buf, "grps[%d].nr", i);
 +        cmp_int(fp, buf, -1, g0->grps[i].nr, g1->grps[i].nr);
 +        if (g0->grps[i].nr == g1->grps[i].nr)
 +        {
 +            for (j = 0; j < g0->grps[i].nr; j++)
 +            {
 +                sprintf(buf, "grps[%d].name[%d]", i, j);
 +                cmp_str(fp, buf, -1,
 +                        *g0->grpname[g0->grps[i].nm_ind[j]],
 +                        *g1->grpname[g1->grps[i].nm_ind[j]]);
 +            }
 +        }
 +        cmp_int(fp, "ngrpnr", i, g0->ngrpnr[i], g1->ngrpnr[i]);
 +        if (g0->ngrpnr[i] == g1->ngrpnr[i] && natoms0 == natoms1 &&
 +            (g0->grpnr[i] != NULL || g1->grpnr[i] != NULL))
 +        {
 +            for (j = 0; j < natoms0; j++)
 +            {
 +                cmp_int(fp, gtypes[i], j, ggrpnr(g0, i, j), ggrpnr(g1, i, j));
 +            }
 +        }
 +    }
 +    /* We have compared the names in the groups lists,
 +     * so we can skip the grpname list comparison.
 +     */
 +}
 +
 +static void cmp_rvecs_rmstol(FILE *fp, const char *title, int n, rvec x1[], rvec x2[],
 +                             real ftol, real abstol)
 +{
 +    int    i, m;
 +    double rms;
 +
 +    /* For a vector you are usally not interested in a relative difference
 +     * on a component that is very small compared to the other components.
 +     * Therefore we do the relative comparision relative to the RMS component.
 +     */
 +    rms = 0.0;
 +    for (i = 0; (i < n); i++)
 +    {
 +        for (m = 0; m < DIM; m++)
 +        {
 +            rms += x1[i][m]*x1[i][m] + x2[i][m]*x2[i][m];
 +        }
 +    }
 +    rms = sqrt(rms/(2*n*DIM));
 +
 +    /* Convert the relative tolerance into an absolute tolerance */
 +    if (ftol*rms < abstol)
 +    {
 +        abstol = ftol*rms;
 +    }
 +
 +    /* And now do the actual comparision */
 +    for (i = 0; (i < n); i++)
 +    {
 +        cmp_rvec(fp, title, i, x1[i], x2[i], 0.0, abstol);
 +    }
 +}
 +
 +static void cmp_rvecs(FILE *fp, const char *title, int n, rvec x1[], rvec x2[],
 +                      gmx_bool bRMSD, real ftol, real abstol)
 +{
 +    int    i, m;
 +    double d, ssd;
 +
 +    if (bRMSD)
 +    {
 +        ssd = 0;
 +        for (i = 0; (i < n); i++)
 +        {
 +            for (m = 0; m < DIM; m++)
 +            {
 +                d    = x1[i][m] - x2[i][m];
 +                ssd += d*d;
 +            }
 +        }
 +        fprintf(fp, "%s RMSD %g\n", title, std::sqrt(ssd/n));
 +    }
 +    else
 +    {
 +        cmp_rvecs_rmstol(fp, title, n, x1, x2, ftol, abstol);
 +    }
 +}
 +
 +static void cmp_grpopts(FILE *fp, t_grpopts *opt1, t_grpopts *opt2, real ftol, real abstol)
 +{
 +    int  i, j;
 +    char buf1[256], buf2[256];
 +
 +    cmp_int(fp, "inputrec->grpopts.ngtc", -1,  opt1->ngtc, opt2->ngtc);
 +    cmp_int(fp, "inputrec->grpopts.ngacc", -1, opt1->ngacc, opt2->ngacc);
 +    cmp_int(fp, "inputrec->grpopts.ngfrz", -1, opt1->ngfrz, opt2->ngfrz);
 +    cmp_int(fp, "inputrec->grpopts.ngener", -1, opt1->ngener, opt2->ngener);
 +    for (i = 0; (i < std::min(opt1->ngtc, opt2->ngtc)); i++)
 +    {
 +        cmp_real(fp, "inputrec->grpopts.nrdf", i, opt1->nrdf[i], opt2->nrdf[i], ftol, abstol);
 +        cmp_real(fp, "inputrec->grpopts.ref_t", i, opt1->ref_t[i], opt2->ref_t[i], ftol, abstol);
 +        cmp_real(fp, "inputrec->grpopts.tau_t", i, opt1->tau_t[i], opt2->tau_t[i], ftol, abstol);
 +        cmp_int(fp, "inputrec->grpopts.annealing", i, opt1->annealing[i], opt2->annealing[i]);
 +        cmp_int(fp, "inputrec->grpopts.anneal_npoints", i,
 +                opt1->anneal_npoints[i], opt2->anneal_npoints[i]);
 +        if (opt1->anneal_npoints[i] == opt2->anneal_npoints[i])
 +        {
 +            sprintf(buf1, "inputrec->grpopts.anneal_time[%d]", i);
 +            sprintf(buf2, "inputrec->grpopts.anneal_temp[%d]", i);
 +            for (j = 0; j < opt1->anneal_npoints[i]; j++)
 +            {
 +                cmp_real(fp, buf1, j, opt1->anneal_time[i][j], opt2->anneal_time[i][j], ftol, abstol);
 +                cmp_real(fp, buf2, j, opt1->anneal_temp[i][j], opt2->anneal_temp[i][j], ftol, abstol);
 +            }
 +        }
 +    }
 +    if (opt1->ngener == opt2->ngener)
 +    {
 +        for (i = 0; i < opt1->ngener; i++)
 +        {
 +            for (j = i; j < opt1->ngener; j++)
 +            {
 +                sprintf(buf1, "inputrec->grpopts.egp_flags[%d]", i);
 +                cmp_int(fp, buf1, j,
 +                        opt1->egp_flags[opt1->ngener*i+j],
 +                        opt2->egp_flags[opt1->ngener*i+j]);
 +            }
 +        }
 +    }
 +    for (i = 0; (i < std::min(opt1->ngacc, opt2->ngacc)); i++)
 +    {
 +        cmp_rvec(fp, "inputrec->grpopts.acc", i, opt1->acc[i], opt2->acc[i], ftol, abstol);
 +    }
 +    for (i = 0; (i < std::min(opt1->ngfrz, opt2->ngfrz)); i++)
 +    {
 +        cmp_ivec(fp, "inputrec->grpopts.nFreeze", i, opt1->nFreeze[i], opt2->nFreeze[i]);
 +    }
 +}
 +
 +static void cmp_cosines(FILE *fp, const char *s, t_cosines c1[DIM], t_cosines c2[DIM], real ftol, real abstol)
 +{
 +    int  i, m;
 +    char buf[256];
 +
 +    for (m = 0; (m < DIM); m++)
 +    {
 +        sprintf(buf, "inputrec->%s[%d]", s, m);
 +        cmp_int(fp, buf, 0, c1->n, c2->n);
 +        for (i = 0; (i < std::min(c1->n, c2->n)); i++)
 +        {
 +            cmp_real(fp, buf, i, c1->a[i], c2->a[i], ftol, abstol);
 +            cmp_real(fp, buf, i, c1->phi[i], c2->phi[i], ftol, abstol);
 +        }
 +    }
 +}
 +static void cmp_pull(FILE *fp)
 +{
 +    fprintf(fp, "WARNING: Both files use COM pulling, but comparing of the pull struct is not implemented (yet). The pull parameters could be the same or different.\n");
 +}
 +
 +static void cmp_simtempvals(FILE *fp, t_simtemp *simtemp1, t_simtemp *simtemp2, int n_lambda, real ftol, real abstol)
 +{
 +    int i;
 +    cmp_int(fp, "inputrec->simtempvals->eSimTempScale", -1, simtemp1->eSimTempScale, simtemp2->eSimTempScale);
 +    cmp_real(fp, "inputrec->simtempvals->simtemp_high", -1, simtemp1->simtemp_high, simtemp2->simtemp_high, ftol, abstol);
 +    cmp_real(fp, "inputrec->simtempvals->simtemp_low", -1, simtemp1->simtemp_low, simtemp2->simtemp_low, ftol, abstol);
 +    for (i = 0; i < n_lambda; i++)
 +    {
 +        cmp_real(fp, "inputrec->simtempvals->temperatures", -1, simtemp1->temperatures[i], simtemp2->temperatures[i], ftol, abstol);
 +    }
 +}
 +
 +static void cmp_expandedvals(FILE *fp, t_expanded *expand1, t_expanded *expand2, int n_lambda, real ftol, real abstol)
 +{
 +    int i;
 +
 +    cmp_bool(fp, "inputrec->fepvals->bInit_weights", -1, expand1->bInit_weights, expand2->bInit_weights);
 +    cmp_bool(fp, "inputrec->fepvals->bWLoneovert", -1, expand1->bWLoneovert, expand2->bWLoneovert);
 +
 +    for (i = 0; i < n_lambda; i++)
 +    {
 +        cmp_real(fp, "inputrec->expandedvals->init_lambda_weights", -1,
 +                 expand1->init_lambda_weights[i], expand2->init_lambda_weights[i], ftol, abstol);
 +    }
 +
 +    cmp_int(fp, "inputrec->expandedvals->lambda-stats", -1, expand1->elamstats, expand2->elamstats);
 +    cmp_int(fp, "inputrec->expandedvals->lambda-mc-move", -1, expand1->elmcmove, expand2->elmcmove);
 +    cmp_int(fp, "inputrec->expandedvals->lmc-repeats", -1, expand1->lmc_repeats, expand2->lmc_repeats);
 +    cmp_int(fp, "inputrec->expandedvals->lmc-gibbsdelta", -1, expand1->gibbsdeltalam, expand2->gibbsdeltalam);
 +    cmp_int(fp, "inputrec->expandedvals->lmc-forced-nstart", -1, expand1->lmc_forced_nstart, expand2->lmc_forced_nstart);
 +    cmp_int(fp, "inputrec->expandedvals->lambda-weights-equil", -1, expand1->elmceq, expand2->elmceq);
 +    cmp_int(fp, "inputrec->expandedvals->,weight-equil-number-all-lambda", -1, expand1->equil_n_at_lam, expand2->equil_n_at_lam);
 +    cmp_int(fp, "inputrec->expandedvals->weight-equil-number-samples", -1, expand1->equil_samples, expand2->equil_samples);
 +    cmp_int(fp, "inputrec->expandedvals->weight-equil-number-steps", -1, expand1->equil_steps, expand2->equil_steps);
 +    cmp_real(fp, "inputrec->expandedvals->weight-equil-wl-delta", -1, expand1->equil_wl_delta, expand2->equil_wl_delta, ftol, abstol);
 +    cmp_real(fp, "inputrec->expandedvals->weight-equil-count-ratio", -1, expand1->equil_ratio, expand2->equil_ratio, ftol, abstol);
 +    cmp_bool(fp, "inputrec->expandedvals->symmetrized-transition-matrix", -1, expand1->bSymmetrizedTMatrix, expand2->bSymmetrizedTMatrix);
 +    cmp_int(fp, "inputrec->expandedvals->nstTij", -1, expand1->nstTij, expand2->nstTij);
 +    cmp_int(fp, "inputrec->expandedvals->mininum-var-min", -1, expand1->minvarmin, expand2->minvarmin); /*default is reasonable */
 +    cmp_int(fp, "inputrec->expandedvals->weight-c-range", -1, expand1->c_range, expand2->c_range);      /* default is just C=0 */
 +    cmp_real(fp, "inputrec->expandedvals->wl-scale", -1, expand1->wl_scale, expand2->wl_scale, ftol, abstol);
 +    cmp_real(fp, "inputrec->expandedvals->init-wl-delta", -1, expand1->init_wl_delta, expand2->init_wl_delta, ftol, abstol);
 +    cmp_real(fp, "inputrec->expandedvals->wl-ratio", -1, expand1->wl_ratio, expand2->wl_ratio, ftol, abstol);
 +    cmp_int(fp, "inputrec->expandedvals->nstexpanded", -1, expand1->nstexpanded, expand2->nstexpanded);
 +    cmp_int(fp, "inputrec->expandedvals->lmc-seed", -1, expand1->lmc_seed, expand2->lmc_seed);
 +    cmp_real(fp, "inputrec->expandedvals->mc-temperature", -1, expand1->mc_temp, expand2->mc_temp, ftol, abstol);
 +}
 +
 +static void cmp_fepvals(FILE *fp, t_lambda *fep1, t_lambda *fep2, real ftol, real abstol)
 +{
 +    int i, j;
 +    cmp_int(fp, "inputrec->nstdhdl", -1, fep1->nstdhdl, fep2->nstdhdl);
 +    cmp_double(fp, "inputrec->fepvals->init_fep_state", -1, fep1->init_fep_state, fep2->init_fep_state, ftol, abstol);
 +    cmp_double(fp, "inputrec->fepvals->delta_lambda", -1, fep1->delta_lambda, fep2->delta_lambda, ftol, abstol);
 +    cmp_int(fp, "inputrec->fepvals->n_lambda", -1, fep1->n_lambda, fep2->n_lambda);
 +    for (i = 0; i < efptNR; i++)
 +    {
 +        for (j = 0; j < std::min(fep1->n_lambda, fep2->n_lambda); j++)
 +        {
 +            cmp_double(fp, "inputrec->fepvals->all_lambda", -1, fep1->all_lambda[i][j], fep2->all_lambda[i][j], ftol, abstol);
 +        }
 +    }
 +    cmp_int(fp, "inputrec->fepvals->lambda_neighbors", 1, fep1->lambda_neighbors,
 +            fep2->lambda_neighbors);
 +    cmp_real(fp, "inputrec->fepvals->sc_alpha", -1, fep1->sc_alpha, fep2->sc_alpha, ftol, abstol);
 +    cmp_int(fp, "inputrec->fepvals->sc_power", -1, fep1->sc_power, fep2->sc_power);
 +    cmp_real(fp, "inputrec->fepvals->sc_r_power", -1, fep1->sc_r_power, fep2->sc_r_power, ftol, abstol);
 +    cmp_real(fp, "inputrec->fepvals->sc_sigma", -1, fep1->sc_sigma, fep2->sc_sigma, ftol, abstol);
 +    cmp_int(fp, "inputrec->fepvals->edHdLPrintEnergy", -1, fep1->edHdLPrintEnergy, fep1->edHdLPrintEnergy);
 +    cmp_bool(fp, "inputrec->fepvals->bScCoul", -1, fep1->bScCoul, fep1->bScCoul);
 +    cmp_int(fp, "inputrec->separate_dhdl_file", -1, fep1->separate_dhdl_file, fep2->separate_dhdl_file);
 +    cmp_int(fp, "inputrec->dhdl_derivatives", -1, fep1->dhdl_derivatives, fep2->dhdl_derivatives);
 +    cmp_int(fp, "inputrec->dh_hist_size", -1, fep1->dh_hist_size, fep2->dh_hist_size);
 +    cmp_double(fp, "inputrec->dh_hist_spacing", -1, fep1->dh_hist_spacing, fep2->dh_hist_spacing, ftol, abstol);
 +}
 +
 +static void cmp_inputrec(FILE *fp, t_inputrec *ir1, t_inputrec *ir2, real ftol, real abstol)
 +{
 +    fprintf(fp, "comparing inputrec\n");
 +
 +    /* gcc 2.96 doesnt like these defines at all, but issues a huge list
 +     * of warnings. Maybe it will change in future versions, but for the
 +     * moment I've spelled them out instead. /EL 000820
 +     * #define CIB(s) cmp_int(fp,"inputrec->"#s,0,ir1->##s,ir2->##s)
 +     * #define CII(s) cmp_int(fp,"inputrec->"#s,0,ir1->##s,ir2->##s)
 +     * #define CIR(s) cmp_real(fp,"inputrec->"#s,0,ir1->##s,ir2->##s,ftol)
 +     */
 +    cmp_int(fp, "inputrec->eI", -1, ir1->eI, ir2->eI);
 +    cmp_int64(fp, "inputrec->nsteps", ir1->nsteps, ir2->nsteps);
 +    cmp_int64(fp, "inputrec->init_step", ir1->init_step, ir2->init_step);
 +    cmp_int(fp, "inputrec->simulation_part", -1, ir1->simulation_part, ir2->simulation_part);
 +    cmp_int(fp, "inputrec->ePBC", -1, ir1->ePBC, ir2->ePBC);
 +    cmp_int(fp, "inputrec->bPeriodicMols", -1, ir1->bPeriodicMols, ir2->bPeriodicMols);
 +    cmp_int(fp, "inputrec->cutoff_scheme", -1, ir1->cutoff_scheme, ir2->cutoff_scheme);
 +    cmp_int(fp, "inputrec->ns_type", -1, ir1->ns_type, ir2->ns_type);
 +    cmp_int(fp, "inputrec->nstlist", -1, ir1->nstlist, ir2->nstlist);
 +    cmp_int(fp, "inputrec->nstcomm", -1, ir1->nstcomm, ir2->nstcomm);
 +    cmp_int(fp, "inputrec->comm_mode", -1, ir1->comm_mode, ir2->comm_mode);
 +    cmp_int(fp, "inputrec->nstlog", -1, ir1->nstlog, ir2->nstlog);
 +    cmp_int(fp, "inputrec->nstxout", -1, ir1->nstxout, ir2->nstxout);
 +    cmp_int(fp, "inputrec->nstvout", -1, ir1->nstvout, ir2->nstvout);
 +    cmp_int(fp, "inputrec->nstfout", -1, ir1->nstfout, ir2->nstfout);
 +    cmp_int(fp, "inputrec->nstcalcenergy", -1, ir1->nstcalcenergy, ir2->nstcalcenergy);
 +    cmp_int(fp, "inputrec->nstenergy", -1, ir1->nstenergy, ir2->nstenergy);
 +    cmp_int(fp, "inputrec->nstxout_compressed", -1, ir1->nstxout_compressed, ir2->nstxout_compressed);
 +    cmp_double(fp, "inputrec->init_t", -1, ir1->init_t, ir2->init_t, ftol, abstol);
 +    cmp_double(fp, "inputrec->delta_t", -1, ir1->delta_t, ir2->delta_t, ftol, abstol);
 +    cmp_real(fp, "inputrec->x_compression_precision", -1, ir1->x_compression_precision, ir2->x_compression_precision, ftol, abstol);
 +    cmp_real(fp, "inputrec->fourierspacing", -1, ir1->fourier_spacing, ir2->fourier_spacing, ftol, abstol);
 +    cmp_int(fp, "inputrec->nkx", -1, ir1->nkx, ir2->nkx);
 +    cmp_int(fp, "inputrec->nky", -1, ir1->nky, ir2->nky);
 +    cmp_int(fp, "inputrec->nkz", -1, ir1->nkz, ir2->nkz);
 +    cmp_int(fp, "inputrec->pme_order", -1, ir1->pme_order, ir2->pme_order);
 +    cmp_real(fp, "inputrec->ewald_rtol", -1, ir1->ewald_rtol, ir2->ewald_rtol, ftol, abstol);
 +    cmp_int(fp, "inputrec->ewald_geometry", -1, ir1->ewald_geometry, ir2->ewald_geometry);
 +    cmp_real(fp, "inputrec->epsilon_surface", -1, ir1->epsilon_surface, ir2->epsilon_surface, ftol, abstol);
 +    cmp_int(fp, "inputrec->bContinuation", -1, ir1->bContinuation, ir2->bContinuation);
 +    cmp_int(fp, "inputrec->bShakeSOR", -1, ir1->bShakeSOR, ir2->bShakeSOR);
 +    cmp_int(fp, "inputrec->etc", -1, ir1->etc, ir2->etc);
 +    cmp_int(fp, "inputrec->bPrintNHChains", -1, ir1->bPrintNHChains, ir2->bPrintNHChains);
 +    cmp_int(fp, "inputrec->epc", -1, ir1->epc, ir2->epc);
 +    cmp_int(fp, "inputrec->epct", -1, ir1->epct, ir2->epct);
 +    cmp_real(fp, "inputrec->tau_p", -1, ir1->tau_p, ir2->tau_p, ftol, abstol);
 +    cmp_rvec(fp, "inputrec->ref_p(x)", -1, ir1->ref_p[XX], ir2->ref_p[XX], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->ref_p(y)", -1, ir1->ref_p[YY], ir2->ref_p[YY], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->ref_p(z)", -1, ir1->ref_p[ZZ], ir2->ref_p[ZZ], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->compress(x)", -1, ir1->compress[XX], ir2->compress[XX], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->compress(y)", -1, ir1->compress[YY], ir2->compress[YY], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->compress(z)", -1, ir1->compress[ZZ], ir2->compress[ZZ], ftol, abstol);
 +    cmp_int(fp, "refcoord_scaling", -1, ir1->refcoord_scaling, ir2->refcoord_scaling);
 +    cmp_rvec(fp, "inputrec->posres_com", -1, ir1->posres_com, ir2->posres_com, ftol, abstol);
 +    cmp_rvec(fp, "inputrec->posres_comB", -1, ir1->posres_comB, ir2->posres_comB, ftol, abstol);
 +    cmp_real(fp, "inputrec->verletbuf_tol", -1, ir1->verletbuf_tol, ir2->verletbuf_tol, ftol, abstol);
 +    cmp_real(fp, "inputrec->rlist", -1, ir1->rlist, ir2->rlist, ftol, abstol);
 +    cmp_real(fp, "inputrec->rtpi", -1, ir1->rtpi, ir2->rtpi, ftol, abstol);
 +    cmp_int(fp, "inputrec->coulombtype", -1, ir1->coulombtype, ir2->coulombtype);
 +    cmp_int(fp, "inputrec->coulomb_modifier", -1, ir1->coulomb_modifier, ir2->coulomb_modifier);
 +    cmp_real(fp, "inputrec->rcoulomb_switch", -1, ir1->rcoulomb_switch, ir2->rcoulomb_switch, ftol, abstol);
 +    cmp_real(fp, "inputrec->rcoulomb", -1, ir1->rcoulomb, ir2->rcoulomb, ftol, abstol);
 +    cmp_int(fp, "inputrec->vdwtype", -1, ir1->vdwtype, ir2->vdwtype);
 +    cmp_int(fp, "inputrec->vdw_modifier", -1, ir1->vdw_modifier, ir2->vdw_modifier);  cmp_real(fp, "inputrec->rvdw_switch", -1, ir1->rvdw_switch, ir2->rvdw_switch, ftol, abstol);
 +    cmp_real(fp, "inputrec->rvdw", -1, ir1->rvdw, ir2->rvdw, ftol, abstol);
 +    cmp_real(fp, "inputrec->epsilon_r", -1, ir1->epsilon_r, ir2->epsilon_r, ftol, abstol);
 +    cmp_real(fp, "inputrec->epsilon_rf", -1, ir1->epsilon_rf, ir2->epsilon_rf, ftol, abstol);
 +    cmp_real(fp, "inputrec->tabext", -1, ir1->tabext, ir2->tabext, ftol, abstol);
 +    cmp_int(fp, "inputrec->implicit_solvent", -1, ir1->implicit_solvent, ir2->implicit_solvent);
 +    cmp_int(fp, "inputrec->gb_algorithm", -1, ir1->gb_algorithm, ir2->gb_algorithm);
 +    cmp_int(fp, "inputrec->nstgbradii", -1, ir1->nstgbradii, ir2->nstgbradii);
 +    cmp_real(fp, "inputrec->rgbradii", -1, ir1->rgbradii, ir2->rgbradii, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_saltconc", -1, ir1->gb_saltconc, ir2->gb_saltconc, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_epsilon_solvent", -1, ir1->gb_epsilon_solvent, ir2->gb_epsilon_solvent, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_obc_alpha", -1, ir1->gb_obc_alpha, ir2->gb_obc_alpha, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_obc_beta", -1, ir1->gb_obc_beta, ir2->gb_obc_beta, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_obc_gamma", -1, ir1->gb_obc_gamma, ir2->gb_obc_gamma, ftol, abstol);
 +    cmp_real(fp, "inputrec->gb_dielectric_offset", -1, ir1->gb_dielectric_offset, ir2->gb_dielectric_offset, ftol, abstol);
 +    cmp_int(fp, "inputrec->sa_algorithm", -1, ir1->sa_algorithm, ir2->sa_algorithm);
 +    cmp_real(fp, "inputrec->sa_surface_tension", -1, ir1->sa_surface_tension, ir2->sa_surface_tension, ftol, abstol);
 +
 +    cmp_int(fp, "inputrec->eDispCorr", -1, ir1->eDispCorr, ir2->eDispCorr);
 +    cmp_real(fp, "inputrec->shake_tol", -1, ir1->shake_tol, ir2->shake_tol, ftol, abstol);
 +    cmp_int(fp, "inputrec->efep", -1, ir1->efep, ir2->efep);
 +    cmp_fepvals(fp, ir1->fepvals, ir2->fepvals, ftol, abstol);
 +    cmp_int(fp, "inputrec->bSimTemp", -1, ir1->bSimTemp, ir2->bSimTemp);
 +    if ((ir1->bSimTemp == ir2->bSimTemp) && (ir1->bSimTemp))
 +    {
 +        cmp_simtempvals(fp, ir1->simtempvals, ir2->simtempvals, std::min(ir1->fepvals->n_lambda, ir2->fepvals->n_lambda), ftol, abstol);
 +    }
 +    cmp_int(fp, "inputrec->bExpanded", -1, ir1->bExpanded, ir2->bExpanded);
 +    if ((ir1->bExpanded == ir2->bExpanded) && (ir1->bExpanded))
 +    {
 +        cmp_expandedvals(fp, ir1->expandedvals, ir2->expandedvals, std::min(ir1->fepvals->n_lambda, ir2->fepvals->n_lambda), ftol, abstol);
 +    }
 +    cmp_int(fp, "inputrec->nwall", -1, ir1->nwall, ir2->nwall);
 +    cmp_int(fp, "inputrec->wall_type", -1, ir1->wall_type, ir2->wall_type);
 +    cmp_int(fp, "inputrec->wall_atomtype[0]", -1, ir1->wall_atomtype[0], ir2->wall_atomtype[0]);
 +    cmp_int(fp, "inputrec->wall_atomtype[1]", -1, ir1->wall_atomtype[1], ir2->wall_atomtype[1]);
 +    cmp_real(fp, "inputrec->wall_density[0]", -1, ir1->wall_density[0], ir2->wall_density[0], ftol, abstol);
 +    cmp_real(fp, "inputrec->wall_density[1]", -1, ir1->wall_density[1], ir2->wall_density[1], ftol, abstol);
 +    cmp_real(fp, "inputrec->wall_ewald_zfac", -1, ir1->wall_ewald_zfac, ir2->wall_ewald_zfac, ftol, abstol);
 +
 +    cmp_bool(fp, "inputrec->bPull", -1, ir1->bPull, ir2->bPull);
 +    if (ir1->bPull && ir2->bPull)
 +    {
 +        cmp_pull(fp);
 +    }
 +
 +    cmp_int(fp, "inputrec->eDisre", -1, ir1->eDisre, ir2->eDisre);
 +    cmp_real(fp, "inputrec->dr_fc", -1, ir1->dr_fc, ir2->dr_fc, ftol, abstol);
 +    cmp_int(fp, "inputrec->eDisreWeighting", -1, ir1->eDisreWeighting, ir2->eDisreWeighting);
 +    cmp_int(fp, "inputrec->bDisreMixed", -1, ir1->bDisreMixed, ir2->bDisreMixed);
 +    cmp_int(fp, "inputrec->nstdisreout", -1, ir1->nstdisreout, ir2->nstdisreout);
 +    cmp_real(fp, "inputrec->dr_tau", -1, ir1->dr_tau, ir2->dr_tau, ftol, abstol);
 +    cmp_real(fp, "inputrec->orires_fc", -1, ir1->orires_fc, ir2->orires_fc, ftol, abstol);
 +    cmp_real(fp, "inputrec->orires_tau", -1, ir1->orires_tau, ir2->orires_tau, ftol, abstol);
 +    cmp_int(fp, "inputrec->nstorireout", -1, ir1->nstorireout, ir2->nstorireout);
 +    cmp_real(fp, "inputrec->em_stepsize", -1, ir1->em_stepsize, ir2->em_stepsize, ftol, abstol);
 +    cmp_real(fp, "inputrec->em_tol", -1, ir1->em_tol, ir2->em_tol, ftol, abstol);
 +    cmp_int(fp, "inputrec->niter", -1, ir1->niter, ir2->niter);
 +    cmp_real(fp, "inputrec->fc_stepsize", -1, ir1->fc_stepsize, ir2->fc_stepsize, ftol, abstol);
 +    cmp_int(fp, "inputrec->nstcgsteep", -1, ir1->nstcgsteep, ir2->nstcgsteep);
 +    cmp_int(fp, "inputrec->nbfgscorr", 0, ir1->nbfgscorr, ir2->nbfgscorr);
 +    cmp_int(fp, "inputrec->eConstrAlg", -1, ir1->eConstrAlg, ir2->eConstrAlg);
 +    cmp_int(fp, "inputrec->nProjOrder", -1, ir1->nProjOrder, ir2->nProjOrder);
 +    cmp_real(fp, "inputrec->LincsWarnAngle", -1, ir1->LincsWarnAngle, ir2->LincsWarnAngle, ftol, abstol);
 +    cmp_int(fp, "inputrec->nLincsIter", -1, ir1->nLincsIter, ir2->nLincsIter);
 +    cmp_real(fp, "inputrec->bd_fric", -1, ir1->bd_fric, ir2->bd_fric, ftol, abstol);
 +    cmp_int64(fp, "inputrec->ld_seed", ir1->ld_seed, ir2->ld_seed);
 +    cmp_real(fp, "inputrec->cos_accel", -1, ir1->cos_accel, ir2->cos_accel, ftol, abstol);
 +    cmp_rvec(fp, "inputrec->deform(a)", -1, ir1->deform[XX], ir2->deform[XX], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->deform(b)", -1, ir1->deform[YY], ir2->deform[YY], ftol, abstol);
 +    cmp_rvec(fp, "inputrec->deform(c)", -1, ir1->deform[ZZ], ir2->deform[ZZ], ftol, abstol);
 +
 +
 +    cmp_int(fp, "inputrec->userint1", -1, ir1->userint1, ir2->userint1);
 +    cmp_int(fp, "inputrec->userint2", -1, ir1->userint2, ir2->userint2);
 +    cmp_int(fp, "inputrec->userint3", -1, ir1->userint3, ir2->userint3);
 +    cmp_int(fp, "inputrec->userint4", -1, ir1->userint4, ir2->userint4);
 +    cmp_real(fp, "inputrec->userreal1", -1, ir1->userreal1, ir2->userreal1, ftol, abstol);
 +    cmp_real(fp, "inputrec->userreal2", -1, ir1->userreal2, ir2->userreal2, ftol, abstol);
 +    cmp_real(fp, "inputrec->userreal3", -1, ir1->userreal3, ir2->userreal3, ftol, abstol);
 +    cmp_real(fp, "inputrec->userreal4", -1, ir1->userreal4, ir2->userreal4, ftol, abstol);
 +    cmp_grpopts(fp, &(ir1->opts), &(ir2->opts), ftol, abstol);
 +    cmp_cosines(fp, "ex", ir1->ex, ir2->ex, ftol, abstol);
 +    cmp_cosines(fp, "et", ir1->et, ir2->et, ftol, abstol);
 +}
 +
 +static void comp_pull_AB(FILE *fp, pull_params_t *pull, real ftol, real abstol)
 +{
 +    int i;
 +
 +    for (i = 0; i < pull->ncoord; i++)
 +    {
 +        fprintf(fp, "comparing pull coord %d\n", i);
 +        cmp_real(fp, "pull-coord->k", -1, pull->coord[i].k, pull->coord[i].kB, ftol, abstol);
 +    }
 +}
 +
 +static void comp_state(t_state *st1, t_state *st2,
 +                       gmx_bool bRMSD, real ftol, real abstol)
 +{
 +    int i, j, nc;
 +
 +    fprintf(stdout, "comparing flags\n");
 +    cmp_int(stdout, "flags", -1, st1->flags, st2->flags);
 +    fprintf(stdout, "comparing box\n");
 +    cmp_rvecs(stdout, "box", DIM, st1->box, st2->box, FALSE, ftol, abstol);
 +    fprintf(stdout, "comparing box_rel\n");
 +    cmp_rvecs(stdout, "box_rel", DIM, st1->box_rel, st2->box_rel, FALSE, ftol, abstol);
 +    fprintf(stdout, "comparing boxv\n");
 +    cmp_rvecs(stdout, "boxv", DIM, st1->boxv, st2->boxv, FALSE, ftol, abstol);
 +    if (st1->flags & (1<<estSVIR_PREV))
 +    {
 +        fprintf(stdout, "comparing shake vir_prev\n");
 +        cmp_rvecs(stdout, "svir_prev", DIM, st1->svir_prev, st2->svir_prev, FALSE, ftol, abstol);
 +    }
 +    if (st1->flags & (1<<estFVIR_PREV))
 +    {
 +        fprintf(stdout, "comparing force vir_prev\n");
 +        cmp_rvecs(stdout, "fvir_prev", DIM, st1->fvir_prev, st2->fvir_prev, FALSE, ftol, abstol);
 +    }
 +    if (st1->flags & (1<<estPRES_PREV))
 +    {
 +        fprintf(stdout, "comparing prev_pres\n");
 +        cmp_rvecs(stdout, "pres_prev", DIM, st1->pres_prev, st2->pres_prev, FALSE, ftol, abstol);
 +    }
 +    cmp_int(stdout, "ngtc", -1, st1->ngtc, st2->ngtc);
 +    cmp_int(stdout, "nhchainlength", -1, st1->nhchainlength, st2->nhchainlength);
 +    if (st1->ngtc == st2->ngtc && st1->nhchainlength == st2->nhchainlength)
 +    {
 +        for (i = 0; i < st1->ngtc; i++)
 +        {
 +            nc = i*st1->nhchainlength;
 +            for (j = 0; j < nc; j++)
 +            {
 +                cmp_real(stdout, "nosehoover_xi",
 +                         i, st1->nosehoover_xi[nc+j], st2->nosehoover_xi[nc+j], ftol, abstol);
 +            }
 +        }
 +    }
 +    cmp_int(stdout, "nnhpres", -1, st1->nnhpres, st2->nnhpres);
 +    if (st1->nnhpres == st2->nnhpres && st1->nhchainlength == st2->nhchainlength)
 +    {
 +        for (i = 0; i < st1->nnhpres; i++)
 +        {
 +            nc = i*st1->nhchainlength;
 +            for (j = 0; j < nc; j++)
 +            {
 +                cmp_real(stdout, "nosehoover_xi",
 +                         i, st1->nhpres_xi[nc+j], st2->nhpres_xi[nc+j], ftol, abstol);
 +            }
 +        }
 +    }
 +
 +    cmp_int(stdout, "natoms", -1, st1->natoms, st2->natoms);
 +    if (st1->natoms == st2->natoms)
 +    {
 +        if ((st1->flags & (1<<estX)) && (st2->flags & (1<<estX)))
 +        {
 +            fprintf(stdout, "comparing x\n");
 +            cmp_rvecs(stdout, "x", st1->natoms, st1->x, st2->x, bRMSD, ftol, abstol);
 +        }
 +        if ((st1->flags & (1<<estV)) && (st2->flags & (1<<estV)))
 +        {
 +            fprintf(stdout, "comparing v\n");
 +            cmp_rvecs(stdout, "v", st1->natoms, st1->v, st2->v, bRMSD, ftol, abstol);
 +        }
 +    }
 +}
 +
 +void comp_tpx(const char *fn1, const char *fn2,
 +              gmx_bool bRMSD, real ftol, real abstol)
 +{
 +    const char  *ff[2];
 +    t_inputrec   ir[2];
 +    t_state      state[2];
 +    gmx_mtop_t   mtop[2];
 +    t_topology   top[2];
 +    int          i;
 +
 +    ff[0] = fn1;
 +    ff[1] = fn2;
 +    for (i = 0; i < (fn2 ? 2 : 1); i++)
 +    {
 +        read_tpx_state(ff[i], &(ir[i]), &state[i], &(mtop[i]));
 +    }
 +    if (fn2)
 +    {
 +        cmp_inputrec(stdout, &ir[0], &ir[1], ftol, abstol);
 +        /* Convert gmx_mtop_t to t_topology.
 +         * We should implement direct mtop comparison,
 +         * but it might be useful to keep t_topology comparison as an option.
 +         */
 +        top[0] = gmx_mtop_t_to_t_topology(&mtop[0]);
 +        top[1] = gmx_mtop_t_to_t_topology(&mtop[1]);
 +        cmp_top(stdout, &top[0], &top[1], ftol, abstol);
 +        cmp_groups(stdout, &mtop[0].groups, &mtop[1].groups,
 +                   mtop[0].natoms, mtop[1].natoms);
 +        comp_state(&state[0], &state[1], bRMSD, ftol, abstol);
 +    }
 +    else
 +    {
 +        if (ir[0].efep == efepNO)
 +        {
 +            fprintf(stdout, "inputrec->efep = %s\n", efep_names[ir[0].efep]);
 +        }
 +        else
 +        {
 +            if (ir[0].bPull)
 +            {
 +                comp_pull_AB(stdout, ir->pull, ftol, abstol);
 +            }
 +            /* Convert gmx_mtop_t to t_topology.
 +             * We should implement direct mtop comparison,
 +             * but it might be useful to keep t_topology comparison as an option.
 +             */
 +            top[0] = gmx_mtop_t_to_t_topology(&mtop[0]);
 +            cmp_top(stdout, &top[0], NULL, ftol, abstol);
 +        }
 +    }
 +}
 +
 +void comp_frame(FILE *fp, t_trxframe *fr1, t_trxframe *fr2,
 +                gmx_bool bRMSD, real ftol, real abstol)
 +{
 +    fprintf(fp, "\n");
 +    cmp_int(fp, "not_ok", -1, fr1->not_ok, fr2->not_ok);
 +    cmp_int(fp, "natoms", -1, fr1->natoms, fr2->natoms);
 +    if (cmp_bool(fp, "bTitle", -1, fr1->bTitle, fr2->bTitle))
 +    {
 +        cmp_str(fp, "title", -1, fr1->title, fr2->title);
 +    }
 +    if (cmp_bool(fp, "bStep", -1, fr1->bStep, fr2->bStep))
 +    {
 +        cmp_int(fp, "step", -1, fr1->step, fr2->step);
 +    }
 +    cmp_int(fp, "step", -1, fr1->step, fr2->step);
 +    if (cmp_bool(fp, "bTime", -1, fr1->bTime, fr2->bTime))
 +    {
 +        cmp_real(fp, "time", -1, fr1->time, fr2->time, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bLambda", -1, fr1->bLambda, fr2->bLambda))
 +    {
 +        cmp_real(fp, "lambda", -1, fr1->lambda, fr2->lambda, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bAtoms", -1, fr1->bAtoms, fr2->bAtoms))
 +    {
 +        cmp_atoms(fp, fr1->atoms, fr2->atoms, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bPrec", -1, fr1->bPrec, fr2->bPrec))
 +    {
 +        cmp_real(fp, "prec", -1, fr1->prec, fr2->prec, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bX", -1, fr1->bX, fr2->bX))
 +    {
 +        cmp_rvecs(fp, "x", std::min(fr1->natoms, fr2->natoms), fr1->x, fr2->x, bRMSD, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bV", -1, fr1->bV, fr2->bV))
 +    {
 +        cmp_rvecs(fp, "v", std::min(fr1->natoms, fr2->natoms), fr1->v, fr2->v, bRMSD, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bF", -1, fr1->bF, fr2->bF))
 +    {
 +        cmp_rvecs(fp, "f", std::min(fr1->natoms, fr2->natoms), fr1->f, fr2->f, bRMSD, ftol, abstol);
 +    }
 +    if (cmp_bool(fp, "bBox", -1, fr1->bBox, fr2->bBox))
 +    {
 +        cmp_rvecs(fp, "box", 3, fr1->box, fr2->box, FALSE, ftol, abstol);
 +    }
 +}
 +
 +void comp_trx(const gmx_output_env_t *oenv, const char *fn1, const char *fn2,
 +              gmx_bool bRMSD, real ftol, real abstol)
 +{
 +    int          i;
 +    const char  *fn[2];
 +    t_trxframe   fr[2];
 +    t_trxstatus *status[2];
 +    gmx_bool     b[2];
 +
 +    fn[0] = fn1;
 +    fn[1] = fn2;
 +    fprintf(stderr, "Comparing trajectory files %s and %s\n", fn1, fn2);
 +    for (i = 0; i < 2; i++)
 +    {
 +        b[i] = read_first_frame(oenv, &status[i], fn[i], &fr[i], TRX_READ_X|TRX_READ_V|TRX_READ_F);
 +    }
 +
 +    if (b[0] && b[1])
 +    {
 +        do
 +        {
 +            comp_frame(stdout, &(fr[0]), &(fr[1]), bRMSD, ftol, abstol);
 +
 +            for (i = 0; i < 2; i++)
 +            {
 +                b[i] = read_next_frame(oenv, status[i], &fr[i]);
 +            }
 +        }
 +        while (b[0] && b[1]);
 +
 +        for (i = 0; i < 2; i++)
 +        {
 +            if (b[i] && !b[1-i])
 +            {
 +                fprintf(stdout, "\nEnd of file on %s but not on %s\n", fn[1-i], fn[i]);
 +            }
 +            close_trj(status[i]);
 +        }
 +    }
 +    if (!b[0] && !b[1])
 +    {
 +        fprintf(stdout, "\nBoth files read correctly\n");
 +    }
 +}
 +
 +static real ener_tensor_diag(int n, int *ind1, int *ind2,
 +                             gmx_enxnm_t *enm1,
 +                             int *tensi, int i,
 +                             t_energy e1[], t_energy e2[])
 +{
 +    int    d1, d2;
 +    int    j;
 +    real   prod1, prod2;
 +    int    nfound;
 +    size_t len;
 +
 +    d1 = tensi[i]/DIM;
 +    d2 = tensi[i] - d1*DIM;
 +
 +    /* Find the diagonal elements d1 and d2 */
 +    len    = std::strlen(enm1[ind1[i]].name);
 +    prod1  = 1;
 +    prod2  = 1;
 +    nfound = 0;
 +    for (j = 0; j < n; j++)
 +    {
 +        if (tensi[j] >= 0 &&
 +            std::strlen(enm1[ind1[j]].name) == len &&
 +            std::strncmp(enm1[ind1[i]].name, enm1[ind1[j]].name, len-2) == 0 &&
 +            (tensi[j] == d1*DIM+d1 || tensi[j] == d2*DIM+d2))
 +        {
 +            prod1 *= fabs(e1[ind1[j]].e);
 +            prod2 *= fabs(e2[ind2[j]].e);
 +            nfound++;
 +        }
 +    }
 +
 +    if (nfound == 2)
 +    {
 +        return 0.5*(std::sqrt(prod1) + std::sqrt(prod2));
 +    }
 +    else
 +    {
 +        return 0;
 +    }
 +}
 +
 +static gmx_bool enernm_equal(const char *nm1, const char *nm2)
 +{
 +    int len1, len2;
 +
 +    len1 = std::strlen(nm1);
 +    len2 = std::strlen(nm2);
 +
 +    /* Remove " (bar)" at the end of a name */
 +    if (len1 > 6 && std::strcmp(nm1+len1-6, " (bar)") == 0)
 +    {
 +        len1 -= 6;
 +    }
 +    if (len2 > 6 && std::strcmp(nm2+len2-6, " (bar)") == 0)
 +    {
 +        len2 -= 6;
 +    }
 +
 +    return (len1 == len2 && gmx_strncasecmp(nm1, nm2, len1) == 0);
 +}
 +
 +static void cmp_energies(FILE *fp, int step1, int step2,
 +                         t_energy e1[], t_energy e2[],
 +                         gmx_enxnm_t *enm1,
 +                         real ftol, real abstol,
 +                         int nre, int *ind1, int *ind2, int maxener)
 +{
 +    int   i, ii;
 +    int  *tensi, len, d1, d2;
 +    real  ftol_i, abstol_i;
 +
 +    snew(tensi, maxener);
 +    /* Check for tensor elements ending on "-XX", "-XY", ... , "-ZZ" */
 +    for (i = 0; (i < maxener); i++)
 +    {
 +        ii       = ind1[i];
 +        tensi[i] = -1;
 +        len      = std::strlen(enm1[ii].name);
 +        if (len > 3 && enm1[ii].name[len-3] == '-')
 +        {
 +            d1 = enm1[ii].name[len-2] - 'X';
 +            d2 = enm1[ii].name[len-1] - 'X';
 +            if (d1 >= 0 && d1 < DIM &&
 +                d2 >= 0 && d2 < DIM)
 +            {
 +                tensi[i] = d1*DIM + d2;
 +            }
 +        }
 +    }
 +
 +    for (i = 0; (i < maxener); i++)
 +    {
 +        /* Check if this is an off-diagonal tensor element */
 +        if (tensi[i] >= 0 && tensi[i] != 0 && tensi[i] != 4 && tensi[i] != 8)
 +        {
 +            /* Turn on the relative tolerance check (4 is maximum relative diff.) */
 +            ftol_i = 5;
 +            /* Do the relative tolerance through an absolute tolerance times
 +             * the size of diagonal components of the tensor.
 +             */
 +            abstol_i = ftol*ener_tensor_diag(nre, ind1, ind2, enm1, tensi, i, e1, e2);
 +            if (debug)
 +            {
 +                fprintf(debug, "tensor '%s' val %f diag %f\n",
 +                        enm1[i].name, e1[i].e, abstol_i/ftol);
 +            }
 +            if (abstol_i > 0)
 +            {
 +                /* We found a diagonal, we need to check with the minimum tolerance */
 +                abstol_i = std::min(abstol_i, abstol);
 +            }
 +            else
 +            {
 +                /* We did not find a diagonal, ignore the relative tolerance check */
 +                abstol_i = abstol;
 +            }
 +        }
 +        else
 +        {
 +            ftol_i   = ftol;
 +            abstol_i = abstol;
 +        }
 +        if (!equal_real(e1[ind1[i]].e, e2[ind2[i]].e, ftol_i, abstol_i))
 +        {
 +            fprintf(fp, "%-15s  step %3d:  %12g,  step %3d: %12g\n",
 +                    enm1[ind1[i]].name,
 +                    step1, e1[ind1[i]].e,
 +                    step2, e2[ind2[i]].e);
 +        }
 +    }
 +
 +    sfree(tensi);
 +}
 +
 +#if 0
 +static void cmp_disres(t_enxframe *fr1, t_enxframe *fr2, real ftol, real abstol)
 +{
 +    int  i;
 +    char bav[64], bt[64], bs[22];
 +
 +    cmp_int(stdout, "ndisre", -1, fr1->ndisre, fr2->ndisre);
 +    if ((fr1->ndisre == fr2->ndisre) && (fr1->ndisre > 0))
 +    {
 +        sprintf(bav, "step %s: disre rav", gmx_step_str(fr1->step, bs));
 +        sprintf(bt, "step %s: disre  rt", gmx_step_str(fr1->step, bs));
 +        for (i = 0; (i < fr1->ndisre); i++)
 +        {
 +            cmp_real(stdout, bav, i, fr1->disre_rm3tav[i], fr2->disre_rm3tav[i], ftol, abstol);
 +            cmp_real(stdout, bt, i, fr1->disre_rt[i], fr2->disre_rt[i], ftol, abstol);
 +        }
 +    }
 +}
 +#endif
 +
 +static void cmp_eblocks(t_enxframe *fr1, t_enxframe *fr2, real ftol, real abstol)
 +{
 +    int  i, j, k;
 +    char buf[64], bs[22];
 +
 +    cmp_int(stdout, "nblock", -1, fr1->nblock, fr2->nblock);
 +    if ((fr1->nblock == fr2->nblock) && (fr1->nblock > 0))
 +    {
 +        for (j = 0; (j < fr1->nblock); j++)
 +        {
 +            t_enxblock *b1, *b2; /* convenience vars */
 +
 +            b1 = &(fr1->block[j]);
 +            b2 = &(fr2->block[j]);
 +
 +            sprintf(buf, "step %s: block[%d]", gmx_step_str(fr1->step, bs), j);
 +            cmp_int(stdout, buf, -1, b1->nsub, b2->nsub);
 +            cmp_int(stdout, buf, -1, b1->id, b2->id);
 +
 +            if ( (b1->nsub == b2->nsub) && (b1->id == b2->id) )
 +            {
 +                for (i = 0; i < b1->nsub; i++)
 +                {
 +                    t_enxsubblock *s1, *s2;
 +
 +                    s1 = &(b1->sub[i]);
 +                    s2 = &(b2->sub[i]);
 +
 +                    cmp_int(stdout, buf, -1, (int)s1->type, (int)s2->type);
 +                    cmp_int64(stdout, buf, s1->nr, s2->nr);
 +
 +                    if ((s1->type == s2->type) && (s1->nr == s2->nr))
 +                    {
 +                        switch (s1->type)
 +                        {
 +                            case xdr_datatype_float:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_float(stdout, buf, i,
 +                                              s1->fval[k], s2->fval[k],
 +                                              ftol, abstol);
 +                                }
 +                                break;
 +                            case xdr_datatype_double:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_double(stdout, buf, i,
 +                                               s1->dval[k], s2->dval[k],
 +                                               ftol, abstol);
 +                                }
 +                                break;
 +                            case xdr_datatype_int:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_int(stdout, buf, i,
 +                                            s1->ival[k], s2->ival[k]);
 +                                }
 +                                break;
 +                            case xdr_datatype_int64:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_int64(stdout, buf,
 +                                              s1->lval[k], s2->lval[k]);
 +                                }
 +                                break;
 +                            case xdr_datatype_char:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_uc(stdout, buf, i,
 +                                           s1->cval[k], s2->cval[k]);
 +                                }
 +                                break;
 +                            case xdr_datatype_string:
 +                                for (k = 0; k < s1->nr; k++)
 +                                {
 +                                    cmp_str(stdout, buf, i,
 +                                            s1->sval[k], s2->sval[k]);
 +                                }
 +                                break;
 +                            default:
 +                                gmx_incons("Unknown data type!!");
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +void comp_enx(const char *fn1, const char *fn2, real ftol, real abstol, const char *lastener)
 +{
 +    int            nre, nre1, nre2;
 +    ener_file_t    in1, in2;
 +    int            i, j, maxener, *ind1, *ind2, *have;
 +    gmx_enxnm_t   *enm1 = NULL, *enm2 = NULL;
 +    t_enxframe    *fr1, *fr2;
 +    gmx_bool       b1, b2;
 +
 +    fprintf(stdout, "comparing energy file %s and %s\n\n", fn1, fn2);
 +
 +    in1 = open_enx(fn1, "r");
 +    in2 = open_enx(fn2, "r");
 +    do_enxnms(in1, &nre1, &enm1);
 +    do_enxnms(in2, &nre2, &enm2);
 +    if (nre1 != nre2)
 +    {
 +        fprintf(stdout, "There are %d and %d terms in the energy files\n\n",
 +                nre1, nre2);
 +    }
 +    else
 +    {
 +        fprintf(stdout, "There are %d terms in the energy files\n\n", nre1);
 +    }
 +
 +    snew(ind1, nre1);
 +    snew(ind2, nre2);
 +    snew(have, nre2);
 +    nre = 0;
 +    for (i = 0; i < nre1; i++)
 +    {
 +        for (j = 0; j < nre2; j++)
 +        {
 +            if (enernm_equal(enm1[i].name, enm2[j].name))
 +            {
 +                ind1[nre] = i;
 +                ind2[nre] = j;
 +                have[j]   = 1;
 +                nre++;
 +                break;
 +            }
 +        }
 +        if (nre == 0 || ind1[nre-1] != i)
 +        {
 +            cmp_str(stdout, "enm", i, enm1[i].name, "-");
 +        }
 +    }
 +    for (i = 0; i < nre2; i++)
 +    {
 +        if (have[i] == 0)
 +        {
 +            cmp_str(stdout, "enm", i, "-", enm2[i].name);
 +        }
 +    }
 +
 +    maxener = nre;
 +    for (i = 0; i < nre; i++)
 +    {
 +        if ((lastener != NULL) && (std::strstr(enm1[i].name, lastener) != NULL))
 +        {
 +            maxener = i+1;
 +            break;
 +        }
 +    }
 +
 +    fprintf(stdout, "There are %d terms to compare in the energy files\n\n",
 +            maxener);
 +
 +    for (i = 0; i < maxener; i++)
 +    {
 +        cmp_str(stdout, "unit", i, enm1[ind1[i]].unit, enm2[ind2[i]].unit);
 +    }
 +
 +    snew(fr1, 1);
 +    snew(fr2, 1);
 +    do
 +    {
 +        b1 = do_enx(in1, fr1);
 +        b2 = do_enx(in2, fr2);
 +        if (b1 && !b2)
 +        {
 +            fprintf(stdout, "\nEnd of file on %s but not on %s\n", fn2, fn1);
 +        }
 +        else if (!b1 && b2)
 +        {
 +            fprintf(stdout, "\nEnd of file on %s but not on %s\n", fn1, fn2);
 +        }
 +        else if (!b1 && !b2)
 +        {
 +            fprintf(stdout, "\nFiles read successfully\n");
 +        }
 +        else
 +        {
 +            cmp_real(stdout, "t", -1, fr1->t, fr2->t, ftol, abstol);
 +            cmp_int(stdout, "step", -1, fr1->step, fr2->step);
 +            /* We don't want to print the nre mismatch for every frame */
 +            /* cmp_int(stdout,"nre",-1,fr1->nre,fr2->nre); */
 +            if ((fr1->nre >= nre) && (fr2->nre >= nre))
 +            {
 +                cmp_energies(stdout, fr1->step, fr1->step, fr1->ener, fr2->ener,
 +                             enm1, ftol, abstol, nre, ind1, ind2, maxener);
 +            }
 +            /*cmp_disres(fr1,fr2,ftol,abstol);*/
 +            cmp_eblocks(fr1, fr2, ftol, abstol);
 +        }
 +    }
 +    while (b1 && b2);
 +
 +    close_enx(in1);
 +    close_enx(in2);
 +
 +    free_enxframe(fr2);
 +    sfree(fr2);
 +    free_enxframe(fr1);
 +    sfree(fr1);
 +}