Merge release-4-6 into release-5-0
authorRoland Schulz <roland@utk.edu>
Tue, 24 Jun 2014 01:52:58 +0000 (21:52 -0400)
committerMark Abraham <mark.j.abraham@gmail.com>
Thu, 26 Jun 2014 10:31:03 +0000 (12:31 +0200)
This merges commit dced970, which changes many free-energy,
modifier and table-generation code paths, and its fix 349d8056.
That patch 349d8056 contains fixes to potential-shift and
potential-switch, as well as the shift/switch interactions
in combination with free energy. Since 5.0 has undergone
changes in the same areas (both nbnxn free energy, LJ-PME
and force-switch), this commit is a likely place for
bugs to have been introduced, so we keep it as a separate
commit.

Uncrustified the result of the merge.

Conflicts:
src/gmxlib/nonbonded/nb_free_energy.c
Resolved in favour of whichever branch seemed most right; changes from
dced97099aa704d and 5f59569a8 were all relevant here. We have
introduced some new LJ-PME-related variables so that code path is
reasonably similar to the coulomb path. We have also fixed a small
bug where the LJPME self-energy (i==j for verlet kernels) was not
multiplied by 0.5.

src/gromacs/gmxlib/nonbonded/nonbonded.c
Resolved as for dced970

src/gromacs/gmxpreprocess/readir.c
Resolved as for dced970

src/gromacs/mdlib/forcerec.c
Resolved from both branches

src/gromacs/mdlib/sim_util.c
Resolved from both branches, and from 349d8056

src/gromacs/mdlib/tables.c
Resolved from both branches, and added a few lines of code
to make LJ-PME work with shift modifiers.

As noted above, to avoid breaking the 5.0 branch, we have
manually added the changes corresponding to 349d8056 to make
sure force-switch (same as vdwtype=shift) results in correct
dispersion correction, and we have added a fix for the sign
of the LJPME grid c6 term in the generic nonbonded kernels.
This means 349d8056 should not be merged in again to 5.0 later.

Change-Id: Ida29b143a1bcb727ff38f9c63bf133bf749477b1

src/gromacs/gmxlib/nonbonded/nb_free_energy.c
src/gromacs/gmxlib/nonbonded/nb_generic.c
src/gromacs/gmxlib/nonbonded/nonbonded.c
src/gromacs/gmxpreprocess/readir.c
src/gromacs/legacyheaders/nonbonded.h
src/gromacs/mdlib/forcerec.c
src/gromacs/mdlib/ns.c
src/gromacs/mdlib/sim_util.c
src/gromacs/mdlib/tables.c

index a2e145153bdff7eeb9640187324f9670af855c36..058b2a87eb2df4fae11ecc0e39fbded877bb2803 100644 (file)
@@ -74,7 +74,7 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
     real          Vvdw6, Vvdw12, vvtot;
     real          ix, iy, iz, fix, fiy, fiz;
     real          dx, dy, dz, rsq, rinv;
-    real          c6[NSTATES], c12[NSTATES], c6grid[NSTATES];
+    real          c6[NSTATES], c12[NSTATES], c6grid;
     real          LFC[NSTATES], LFV[NSTATES], DLF[NSTATES];
     double        dvdl_coul, dvdl_vdw;
     real          lfac_coul[NSTATES], dlfac_coul[NSTATES], lfac_vdw[NSTATES], dlfac_vdw[NSTATES];
@@ -111,16 +111,27 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
     real *        Vv;
     real *        Vc;
     gmx_bool      bDoForces, bDoShiftForces, bDoPotential;
-    real          rcoulomb, sh_ewald;
-    real          rvdw, sh_invrc6;
-    gmx_bool      bExactElecCutoff, bExactVdwCutoff, bExactCutoffAll, bEwald;
+    real          rcoulomb, rvdw, sh_invrc6;
+    gmx_bool      bExactElecCutoff, bExactVdwCutoff, bExactCutoffAll;
+    gmx_bool      bEwald, bEwaldLJ;
     real          rcutoff_max2;
-    real          rcutoff, rcutoff2, rswitch, d, d2, swV3, swV4, swV5, swF2, swF3, swF4, sw, dsw, rinvcorr;
-    const real *  tab_ewald_F;
-    const real *  tab_ewald_V;
     const real *  tab_ewald_F_lj;
     const real *  tab_ewald_V_lj;
-    real          tab_ewald_scale, tab_ewald_halfsp;
+    real          d, d2, sw, dsw, rinvcorr;
+    real          elec_swV3, elec_swV4, elec_swV5, elec_swF2, elec_swF3, elec_swF4;
+    real          vdw_swV3, vdw_swV4, vdw_swV5, vdw_swF2, vdw_swF3, vdw_swF4;
+    gmx_bool      bConvertEwaldToCoulomb, bConvertLJEwaldToLJ6;
+    gmx_bool      bComputeVdwInteraction, bComputeElecInteraction;
+    const real *  ewtab;
+    int           ewitab;
+    real          ewrt, eweps, ewtabscale, ewtabhalfspace, sh_ewald;
+
+    sh_ewald            = fr->ic->sh_ewald;
+    ewtab               = fr->ic->tabq_coul_FDV0;
+    ewtabscale          = fr->ic->tabq_scale;
+    ewtabhalfspace      = 0.5/ewtabscale;
+    tab_ewald_F_lj      = fr->ic->tabq_vdw_F;
+    tab_ewald_V_lj      = fr->ic->tabq_vdw_V;
 
     x                   = xx[0];
     f                   = ff[0];
@@ -168,37 +179,36 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
     rvdw                = fr->rvdw;
     sh_invrc6           = fr->ic->sh_invrc6;
 
-    /* Ewald (PME) reciprocal force and energy quadratic spline tables */
-    tab_ewald_F         = fr->ic->tabq_coul_F;
-    tab_ewald_V         = fr->ic->tabq_coul_V;
-    tab_ewald_scale     = fr->ic->tabq_scale;
-    tab_ewald_F_lj      = fr->ic->tabq_vdw_F;
-    tab_ewald_V_lj      = fr->ic->tabq_vdw_V;
-    tab_ewald_halfsp    = 0.5/tab_ewald_scale;
+    if (fr->coulomb_modifier == eintmodPOTSWITCH)
+    {
+        d               = fr->rcoulomb-fr->rcoulomb_switch;
+        elec_swV3       = -10.0/(d*d*d);
+        elec_swV4       =  15.0/(d*d*d*d);
+        elec_swV5       =  -6.0/(d*d*d*d*d);
+        elec_swF2       = -30.0/(d*d*d);
+        elec_swF3       =  60.0/(d*d*d*d);
+        elec_swF4       = -30.0/(d*d*d*d*d);
+    }
+    else
+    {
+        /* Avoid warnings from stupid compilers (looking at you, Clang!) */
+        elec_swV3 = elec_swV4 = elec_swV5 = elec_swF2 = elec_swF3 = elec_swF4 = 0.0;
+    }
 
-    if (fr->coulomb_modifier == eintmodPOTSWITCH || fr->vdw_modifier == eintmodPOTSWITCH)
+    if (fr->vdw_modifier == eintmodPOTSWITCH)
     {
-        rcutoff         = (fr->coulomb_modifier == eintmodPOTSWITCH) ? fr->rcoulomb : fr->rvdw;
-        rcutoff2        = rcutoff*rcutoff;
-        rswitch         = (fr->coulomb_modifier == eintmodPOTSWITCH) ? fr->rcoulomb_switch : fr->rvdw_switch;
-        d               = rcutoff-rswitch;
-        swV3            = -10.0/(d*d*d);
-        swV4            =  15.0/(d*d*d*d);
-        swV5            =  -6.0/(d*d*d*d*d);
-        swF2            = -30.0/(d*d*d);
-        swF3            =  60.0/(d*d*d*d);
-        swF4            = -30.0/(d*d*d*d*d);
+        d               = fr->rvdw-fr->rvdw_switch;
+        vdw_swV3        = -10.0/(d*d*d);
+        vdw_swV4        =  15.0/(d*d*d*d);
+        vdw_swV5        =  -6.0/(d*d*d*d*d);
+        vdw_swF2        = -30.0/(d*d*d);
+        vdw_swF3        =  60.0/(d*d*d*d);
+        vdw_swF4        = -30.0/(d*d*d*d*d);
     }
     else
     {
-        /* Stupid compilers dont realize these variables will not be used */
-        rswitch         = 0.0;
-        swV3            = 0.0;
-        swV4            = 0.0;
-        swV5            = 0.0;
-        swF2            = 0.0;
-        swF3            = 0.0;
-        swF4            = 0.0;
+        /* Avoid warnings from stupid compilers (looking at you, Clang!) */
+        vdw_swV3 = vdw_swV4 = vdw_swV5 = vdw_swF2 = vdw_swF3 = vdw_swF4 = 0.0;
     }
 
     if (fr->cutoff_scheme == ecutsVERLET)
@@ -242,6 +252,26 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
     rcutoff_max2    = rcutoff_max2*rcutoff_max2;
 
     bEwald          = (icoul == GMX_NBKERNEL_ELEC_EWALD);
+    bEwaldLJ        = (ivdw == GMX_NBKERNEL_VDW_LJEWALD);
+
+    /* For Ewald/PME interactions we cannot easily apply the soft-core component to
+     * reciprocal space. When we use vanilla (not switch/shift) Ewald interactions, we
+     * can apply the small trick of subtracting the _reciprocal_ space contribution
+     * in this kernel, and instead apply the free energy interaction to the 1/r
+     * (standard coulomb) interaction.
+     *
+     * However, we cannot use this approach for switch-modified since we would then
+     * effectively end up evaluating a significantly different interaction here compared to the
+     * normal (non-free-energy) kernels, either by applying a cutoff at a different
+     * position than what the user requested, or by switching different
+     * things (1/r rather than short-range Ewald). For these settings, we just
+     * use the traditional short-range Ewald interaction in that case.
+     */
+    bConvertEwaldToCoulomb = (bEwald && (fr->coulomb_modifier != eintmodPOTSWITCH));
+    /* For now the below will always be true (since LJ-PME only works with Shift in Gromacs-5.0),
+     * but writing it this way means we stay in sync with coulomb, and it avoids future bugs.
+     */
+    bConvertLJEwaldToLJ6   = (bEwaldLJ && (fr->vdw_modifier   != eintmodPOTSWITCH));
 
     /* fix compiler warnings */
     nj1   = 0;
@@ -376,12 +406,6 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
             tj[STATE_A]      = ntiA+2*typeA[jnr];
             tj[STATE_B]      = ntiB+2*typeB[jnr];
 
-            if (ivdw == GMX_NBKERNEL_VDW_LJEWALD)
-            {
-                c6grid[STATE_A] = nbfp_grid[tj[STATE_A]];
-                c6grid[STATE_B] = nbfp_grid[tj[STATE_B]];
-            }
-
             if (nlist->excl_fep == NULL || nlist->excl_fep[k])
             {
                 c6[STATE_A]      = nbfp[tj[STATE_A]];
@@ -473,14 +497,15 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
                             n1V        = tab_elemsize*n0;
                         }
 
-                        /* With Ewald and soft-core we should put the cut-off on r,
-                         * not on the soft-cored rC, as the real-space and
-                         * reciprocal space contributions should (almost) cancel.
+                        /* Only process the coulomb interactions if we have charges,
+                         * and if we either include all entries in the list (no cutoff
+                         * used in the kernel), or if we are within the cutoff.
                          */
-                        if (qq[i] != 0 &&
-                            !(bExactElecCutoff &&
-                              ((!bEwald && rC >= rcoulomb) ||
-                               (bEwald && r >= rcoulomb))))
+                        bComputeElecInteraction = !bExactElecCutoff ||
+                            ( bConvertEwaldToCoulomb && r < rcoulomb) ||
+                            (!bConvertEwaldToCoulomb && rC < rcoulomb);
+
+                        if ( (qq[i] != 0) && bComputeElecInteraction)
                         {
                             switch (icoul)
                             {
@@ -488,12 +513,10 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
                                     /* simple cutoff */
                                     Vcoul[i]   = qq[i]*rinvC;
                                     FscalC[i]  = Vcoul[i];
-                                    break;
-
-                                case GMX_NBKERNEL_ELEC_EWALD:
-                                    /* Ewald FEP is done only on the 1/r part */
-                                    Vcoul[i]   = qq[i]*(rinvC - sh_ewald);
-                                    FscalC[i]  = Vcoul[i];
+                                    /* The shift for the Coulomb potential is stored in
+                                     * the RF parameter c_rf, which is 0 without shift
+                                     */
+                                    Vcoul[i]  -= qq[i]*fr->ic->c_rf;
                                     break;
 
                                 case GMX_NBKERNEL_ELEC_REACTIONFIELD:
@@ -520,6 +543,26 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
                                     gmx_fatal(FARGS, "Free energy and GB not implemented.\n");
                                     break;
 
+                                case GMX_NBKERNEL_ELEC_EWALD:
+                                    if (bConvertEwaldToCoulomb)
+                                    {
+                                        /* Ewald FEP is done only on the 1/r part */
+                                        Vcoul[i]   = qq[i]*(rinvC-sh_ewald);
+                                        FscalC[i]  = qq[i]*rinvC;
+                                    }
+                                    else
+                                    {
+                                        ewrt      = rC*ewtabscale;
+                                        ewitab    = (int) ewrt;
+                                        eweps     = ewrt-ewitab;
+                                        ewitab    = 4*ewitab;
+                                        FscalC[i] = ewtab[ewitab]+eweps*ewtab[ewitab+1];
+                                        rinvcorr  = rinvC-sh_ewald;
+                                        Vcoul[i]  = qq[i]*(rinvcorr-(ewtab[ewitab+2]-ewtabhalfspace*eweps*(ewtab[ewitab]+FscalC[i])));
+                                        FscalC[i] = qq[i]*(rinvC-rC*FscalC[i]);
+                                    }
+                                    break;
+
                                 case GMX_NBKERNEL_ELEC_NONE:
                                     FscalC[i]  = 0.0;
                                     Vcoul[i]   = 0.0;
@@ -532,21 +575,29 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
 
                             if (fr->coulomb_modifier == eintmodPOTSWITCH)
                             {
-                                d                = rC-rswitch;
+                                d                = rC-fr->rcoulomb_switch;
                                 d                = (d > 0.0) ? d : 0.0;
                                 d2               = d*d;
-                                sw               = 1.0+d2*d*(swV3+d*(swV4+d*swV5));
-                                dsw              = d2*(swF2+d*(swF3+d*swF4));
+                                sw               = 1.0+d2*d*(elec_swV3+d*(elec_swV4+d*elec_swV5));
+                                dsw              = d2*(elec_swF2+d*(elec_swF3+d*elec_swF4));
 
-                                Vcoul[i]  *= sw;
-                                FscalC[i]  = FscalC[i]*sw + Vcoul[i]*dsw;
+                                FscalC[i]        = FscalC[i]*sw - rC*Vcoul[i]*dsw;
+                                Vcoul[i]        *= sw;
+
+                                FscalC[i]        = (rC < rcoulomb) ? FscalC[i] : 0.0;
+                                Vcoul[i]         = (rC < rcoulomb) ? Vcoul[i] : 0.0;
                             }
                         }
 
-                        if ((c6[i] != 0 || c12[i] != 0) &&
-                            !(bExactVdwCutoff &&
-                              ((ivdw != GMX_NBKERNEL_VDW_LJEWALD && rV >= rvdw) ||
-                               (ivdw == GMX_NBKERNEL_VDW_LJEWALD && r >= rvdw))))
+                        /* Only process the VDW interactions if we have
+                         * some non-zero parameters, and if we either
+                         * include all entries in the list (no cutoff used
+                         * in the kernel), or if we are within the cutoff.
+                         */
+                        bComputeVdwInteraction = !bExactVdwCutoff ||
+                            ( bConvertLJEwaldToLJ6 && r < rvdw) ||
+                            (!bConvertLJEwaldToLJ6 && rV < rvdw);
+                        if ((c6[i] != 0 || c12[i] != 0) && bComputeVdwInteraction)
                         {
                             switch (ivdw)
                             {
@@ -617,14 +668,14 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
 
                             if (fr->vdw_modifier == eintmodPOTSWITCH)
                             {
-                                d          = rV-rswitch;
-                                d          = (d > 0.0) ? d : 0.0;
-                                d2         = d*d;
-                                sw         = 1.0+d2*d*(swV3+d*(swV4+d*swV5));
-                                dsw        = d2*(swF2+d*(swF3+d*swF4));
+                                d                = rV-fr->rvdw_switch;
+                                d                = (d > 0.0) ? d : 0.0;
+                                d2               = d*d;
+                                sw               = 1.0+d2*d*(vdw_swV3+d*(vdw_swV4+d*vdw_swV5));
+                                dsw              = d2*(vdw_swF2+d*(vdw_swF3+d*vdw_swF4));
 
-                                Vvdw[i]   *= sw;
-                                FscalV[i]  = FscalV[i]*sw + Vvdw[i]*dsw;
+                                FscalV[i]        = FscalV[i]*sw - rV*Vvdw[i]*dsw;
+                                Vvdw[i]         *= sw;
 
                                 FscalV[i]  = (rV < rvdw) ? FscalV[i] : 0.0;
                                 Vvdw[i]    = (rV < rvdw) ? Vvdw[i] : 0.0;
@@ -677,54 +728,80 @@ gmx_nb_free_energy_kernel(const t_nblist * gmx_restrict    nlist,
                 }
             }
 
-            if (icoul == GMX_NBKERNEL_ELEC_EWALD &&
-                !(bExactElecCutoff && r >= rcoulomb))
+            if (bConvertEwaldToCoulomb && ( !bExactElecCutoff || r < rcoulomb ) )
             {
-                /* Because we compute the soft-core normally,
-                 * we have to remove the Ewald short range portion.
-                 * Done outside of the states loop because this part
-                 * doesn't depend on the scaled R.
+                /* See comment in the preamble. When using Ewald interactions
+                 * (unless we use a switch modifier) we subtract the reciprocal-space
+                 * Ewald component here which made it possible to apply the free
+                 * energy interaction to 1/r (vanilla coulomb short-range part)
+                 * above. This gets us closer to the ideal case of applying
+                 * the softcore to the entire electrostatic interaction,
+                 * including the reciprocal-space component.
                  */
-                real rs, frac, f_lr;
-                int  ri;
+                real v_lr, f_lr;
 
-                rs     = rsq*rinv*tab_ewald_scale;
-                ri     = (int)rs;
-                frac   = rs - ri;
-                f_lr   = (1 - frac)*tab_ewald_F[ri] + frac*tab_ewald_F[ri+1];
-                FF     = f_lr*rinv;
-                VV     = tab_ewald_V[ri] - tab_ewald_halfsp*frac*(tab_ewald_F[ri] + f_lr);
+                ewrt      = r*ewtabscale;
+                ewitab    = (int) ewrt;
+                eweps     = ewrt-ewitab;
+                ewitab    = 4*ewitab;
+                f_lr      = ewtab[ewitab]+eweps*ewtab[ewitab+1];
+                v_lr      = (ewtab[ewitab+2]-ewtabhalfspace*eweps*(ewtab[ewitab]+f_lr));
+                f_lr     *= rinv;
 
                 if (ii == jnr)
                 {
-                    VV   *= 0.5;
+                    /* If we get here, the i particle (ii) has itself (jnr)
+                     * in its neighborlist. This can only happen with the Verlet
+                     * scheme, and corresponds to a self-interaction that will
+                     * occur twice. Scale it down by 50% to only include it once.
+                     */
+                    v_lr *= 0.5;
                 }
 
                 for (i = 0; i < NSTATES; i++)
                 {
-                    vctot      -= LFC[i]*qq[i]*VV;
-                    Fscal      -= LFC[i]*qq[i]*FF;
-                    dvdl_coul  -= (DLF[i]*qq[i])*VV;
+                    vctot      -= LFC[i]*qq[i]*v_lr;
+                    Fscal      -= LFC[i]*qq[i]*f_lr;
+                    dvdl_coul  -= (DLF[i]*qq[i])*v_lr;
                 }
             }
 
-            if (ivdw == GMX_NBKERNEL_VDW_LJEWALD &&
-                !(bExactVdwCutoff && r >= rvdw))
+            if (bConvertLJEwaldToLJ6 && (!bExactVdwCutoff || r < rvdw))
             {
+                /* See comment in the preamble. When using LJ-Ewald interactions
+                 * (unless we use a switch modifier) we subtract the reciprocal-space
+                 * Ewald component here which made it possible to apply the free
+                 * energy interaction to r^-6 (vanilla LJ6 short-range part)
+                 * above. This gets us closer to the ideal case of applying
+                 * the softcore to the entire VdW interaction,
+                 * including the reciprocal-space component.
+                 */
                 real rs, frac, f_lr;
                 int  ri;
 
-                rs     = rsq*rinv*tab_ewald_scale;
+                rs     = rsq*rinv*ewtabscale;
                 ri     = (int)rs;
                 frac   = rs - ri;
                 f_lr   = (1 - frac)*tab_ewald_F_lj[ri] + frac*tab_ewald_F_lj[ri+1];
                 FF     = f_lr*rinv;
-                VV     = tab_ewald_V_lj[ri] - tab_ewald_halfsp*frac*(tab_ewald_F_lj[ri] + f_lr);
+                VV     = tab_ewald_V_lj[ri] - ewtabhalfspace*frac*(tab_ewald_F_lj[ri] + f_lr);
+
+                if (ii == jnr)
+                {
+                    /* If we get here, the i particle (ii) has itself (jnr)
+                     * in its neighborlist. This can only happen with the Verlet
+                     * scheme, and corresponds to a self-interaction that will
+                     * occur twice. Scale it down by 50% to only include it once.
+                     */
+                    VV *= 0.5;
+                }
+
                 for (i = 0; i < NSTATES; i++)
                 {
-                    vvtot      += LFV[i]*c6grid[i]*VV*(1.0/6.0);
-                    Fscal      += LFV[i]*c6grid[i]*FF*(1.0/6.0);
-                    dvdl_vdw   += (DLF[i]*c6grid[i])*VV*(1.0/6.0);
+                    c6grid      = nbfp_grid[tj[i]];
+                    vvtot      += LFV[i]*c6grid*VV*(1.0/6.0);
+                    Fscal      += LFV[i]*c6grid*FF*(1.0/6.0);
+                    dvdl_vdw   += (DLF[i]*c6grid)*VV*(1.0/6.0);
                 }
 
             }
index 4f29311deaa61d044f465093dd9dd9c998e0639a..019ba8b3410946c852a81c644c78880079f2e4c7 100644 (file)
@@ -418,7 +418,7 @@ gmx_nb_generic_kernel(t_nblist *                nlist,
                         fvdw             = (vvdw_rep - vvdw_disp - c6grid*(1.0/6.0)*exponent*ewclj6)*rinvsq;
                         if (fr->vdw_modifier == eintmodPOTSHIFT)
                         {
-                            vvdw             = (vvdw_rep + c12*sh_repulsion)/12.0 - (vvdw_disp + c6*sh_dispersion + c6grid*sh_lj_ewald)/6.0;
+                            vvdw             = (vvdw_rep + c12*sh_repulsion)/12.0 - (vvdw_disp + c6*sh_dispersion - c6grid*sh_lj_ewald)/6.0;
                         }
                         else
                         {
index 95cc2d57202414b51988acd462b220e1205bcb30..ab68c47db00a6c05a02e531cb77075475fa546a9 100644 (file)
@@ -164,7 +164,7 @@ gmx_nonbonded_setup(t_forcerec *   fr,
 
 
 void
-gmx_nonbonded_set_kernel_pointers(FILE *log, t_nblist *nl)
+gmx_nonbonded_set_kernel_pointers(FILE *log, t_nblist *nl, gmx_bool bElecAndVdwSwitchDiffers)
 {
     const char *     elec;
     const char *     elec_mod;
@@ -285,9 +285,28 @@ gmx_nonbonded_set_kernel_pointers(FILE *log, t_nblist *nl)
             }
         }
 
-        /* Give up. If this was a water kernel, leave the pointer as NULL, which
-         * will disable water optimization in NS. If it is a particle kernel, set
-         * the pointer to the generic NB kernel.
+        /* For now, the accelerated kernels cannot handle the combination of switch functions for both
+         * electrostatics and VdW that use different switch radius or switch cutoff distances
+         * (both of them enter in the switch function calculation). This would require
+         * us to evaluate two completely separate switch functions for every interaction.
+         * Instead, we disable such kernels by setting the pointer to NULL.
+         * This will cause the generic kernel (which can handle it) to be called instead.
+         *
+         * Note that we typically already enable tabulated coulomb interactions for this case,
+         * so this is mostly a safe-guard to make sure we call the generic kernel if the
+         * tables are disabled.
+         */
+        if ((nl->ielec != GMX_NBKERNEL_ELEC_NONE) && (nl->ielecmod == eintmodPOTSWITCH) &&
+            (nl->ivdw  != GMX_NBKERNEL_VDW_NONE)  && (nl->ivdwmod  == eintmodPOTSWITCH) &&
+            bElecAndVdwSwitchDiffers)
+        {
+            nl->kernelptr_vf = NULL;
+            nl->kernelptr_f  = NULL;
+        }
+
+        /* Give up, pick a generic one instead.
+         * We only do this for particle-particle kernels; by leaving the water-optimized kernel
+         * pointers to NULL, the water optimization will automatically be disabled for this interaction.
          */
         if (nl->kernelptr_vf == NULL && !gmx_strcasecmp_min(geom, "Particle-Particle"))
         {
@@ -299,13 +318,11 @@ gmx_nonbonded_set_kernel_pointers(FILE *log, t_nblist *nl)
                 fprintf(debug,
                         "WARNING - Slow generic NB kernel used for neighborlist with\n"
                         "    Elec: '%s', Modifier: '%s'\n"
-                        "    Vdw:  '%s', Modifier: '%s'\n"
-                        "    Geom: '%s', Other: '%s'\n\n",
-                        elec, elec_mod, vdw, vdw_mod, geom, other);
+                        "    Vdw:  '%s', Modifier: '%s'\n",
+                        elec, elec_mod, vdw, vdw_mod);
             }
         }
     }
-
     return;
 }
 
@@ -418,6 +435,10 @@ void do_nonbonded(t_forcerec *fr,
                     {
                         (*kernelptr)(&(nlist[i]), x, f, fr, mdatoms, &kernel_data, nrnb);
                     }
+                    else
+                    {
+                        gmx_fatal(FARGS, "Non-empty neighborlist does not have any kernel pointer assigned.");
+                    }
                 }
             }
         }
index 4dbdf75e3e93a1875ae040aa849ff947b18d93aa..5fbede033e118c8893880a79db5f9a31b994e874 100644 (file)
@@ -1128,6 +1128,19 @@ void check_ir(const char *mdparin, t_inputrec *ir, t_gromppopts *opts,
         }
     }
 
+    if (ir->coulombtype == eelSWITCH || ir->coulombtype == eelSHIFT)
+    {
+        sprintf(err_buf,
+                "Explicit switch/shift coulomb interactions cannot be used in combination with a secondary coulomb-modifier.");
+        CHECK( ir->coulomb_modifier != eintmodNONE);
+    }
+    if (ir->vdwtype == evdwSWITCH || ir->vdwtype == evdwSHIFT)
+    {
+        sprintf(err_buf,
+                "Explicit switch/shift vdw interactions cannot be used in combination with a secondary vdw-modifier.");
+        CHECK( ir->vdw_modifier != eintmodNONE);
+    }
+
     if (ir->coulombtype == eelSWITCH || ir->coulombtype == eelSHIFT ||
         ir->vdwtype == evdwSWITCH || ir->vdwtype == evdwSHIFT)
     {
@@ -1142,7 +1155,7 @@ void check_ir(const char *mdparin, t_inputrec *ir, t_gromppopts *opts,
         if (ir->rcoulomb_switch/ir->rcoulomb < 0.9499)
         {
             real percentage  = 100*(ir->rcoulomb-ir->rcoulomb_switch)/ir->rcoulomb;
-            sprintf(warn_buf, "The switching range for should be 5%% or less (currently %.2f%% using a switching range of %4f-%4f) for accurate electrostatic energies, energy conservation will be good regardless, since ewald_rtol = %g.",
+            sprintf(warn_buf, "The switching range should be 5%% or less (currently %.2f%% using a switching range of %4f-%4f) for accurate electrostatic energies, energy conservation will be good regardless, since ewald_rtol = %g.",
                     percentage, ir->rcoulomb_switch, ir->rcoulomb, ir->ewald_rtol);
             warning(wi, warn_buf);
         }
index 6176d60631c116ebfc441988703f8d59d45958a3..d05b3e5d7e32c2c2555cdc80c09c3c916b195386 100644 (file)
@@ -63,7 +63,8 @@ gmx_nonbonded_setup(t_forcerec *   fr,
 
 void
 gmx_nonbonded_set_kernel_pointers(FILE *       fplog,
-                                  t_nblist *   nl);
+                                  t_nblist *   nl,
+                                  gmx_bool     bElecAndVdwSwitchDiffers);
 
 
 
index 165a1243d27201af1d4efbcb463b2a6848e07185..3b2d69ca048b06ff393bba9d74c54d8412632532 100644 (file)
@@ -2666,6 +2666,11 @@ void init_forcerec(FILE              *fp,
     fr->nbkernel_elec_modifier    = fr->coulomb_modifier;
     fr->nbkernel_vdw_modifier     = fr->vdw_modifier;
 
+    fr->rvdw             = cutoff_inf(ir->rvdw);
+    fr->rvdw_switch      = ir->rvdw_switch;
+    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);
 
@@ -2684,12 +2689,18 @@ void init_forcerec(FILE              *fp,
 
         /* If the user absolutely wants different switch/shift settings for coul/vdw, it is likely
          * going to be faster to tabulate the interaction than calling the generic kernel.
+         * However, if generic kernels have been requested we keep things analytically.
          */
-        if (fr->nbkernel_elec_modifier == eintmodPOTSWITCH && fr->nbkernel_vdw_modifier == eintmodPOTSWITCH)
+        if (fr->nbkernel_elec_modifier == eintmodPOTSWITCH &&
+            fr->nbkernel_vdw_modifier == eintmodPOTSWITCH &&
+            bGenericKernelOnly == FALSE)
         {
             if ((fr->rcoulomb_switch != fr->rvdw_switch) || (fr->rcoulomb != fr->rvdw))
             {
                 fr->bcoultab = TRUE;
+                /* Once we tabulate electrostatics, we can use the switch function for LJ,
+                 * which would otherwise need two tables.
+                 */
             }
         }
         else if ((fr->nbkernel_elec_modifier == eintmodPOTSHIFT && fr->nbkernel_vdw_modifier == eintmodPOTSHIFT) ||
@@ -2697,12 +2708,21 @@ void init_forcerec(FILE              *fp,
                    fr->nbkernel_elec_modifier == eintmodEXACTCUTOFF &&
                    (fr->nbkernel_vdw_modifier == eintmodPOTSWITCH || fr->nbkernel_vdw_modifier == eintmodPOTSHIFT))))
         {
-            if (fr->rcoulomb != fr->rvdw)
+            if ((fr->rcoulomb != fr->rvdw) && (bGenericKernelOnly == FALSE))
             {
                 fr->bcoultab = TRUE;
             }
         }
 
+        if (fr->nbkernel_elec_modifier == eintmodFORCESWITCH)
+        {
+            fr->bcoultab = TRUE;
+        }
+        if (fr->nbkernel_vdw_modifier == eintmodFORCESWITCH)
+        {
+            fr->bvdwtab = TRUE;
+        }
+
         if (getenv("GMX_REQUIRE_TABLES"))
         {
             fr->bvdwtab  = TRUE;
@@ -2793,8 +2813,6 @@ void init_forcerec(FILE              *fp,
     fr->epsilon_r       = ir->epsilon_r;
     fr->epsilon_rf      = ir->epsilon_rf;
     fr->fudgeQQ         = mtop->ffparams.fudgeQQ;
-    fr->rcoulomb_switch = ir->rcoulomb_switch;
-    fr->rcoulomb        = cutoff_inf(ir->rcoulomb);
 
     /* Parameters for generalized RF */
     fr->zsquare = 0.0;
@@ -2843,8 +2861,6 @@ void init_forcerec(FILE              *fp,
     fr->egp_flags = ir->opts.egp_flags;
 
     /* Van der Waals stuff */
-    fr->rvdw        = cutoff_inf(ir->rvdw);
-    fr->rvdw_switch = ir->rvdw_switch;
     if ((fr->vdwtype != evdwCUT) && (fr->vdwtype != evdwUSER) && !fr->bBHAM)
     {
         if (fr->rvdw_switch >= fr->rvdw)
@@ -2984,6 +3000,8 @@ void init_forcerec(FILE              *fp,
         (ir->eDispCorr != edispcNO && ir_vdw_switched(ir));
 
     bMakeSeparate14Table = ((!bMakeTables || fr->eeltype != eelCUT || fr->vdwtype != evdwCUT ||
+                             fr->coulomb_modifier != eintmodNONE ||
+                             fr->vdw_modifier != eintmodNONE ||
                              fr->bBHAM || fr->bEwald) &&
                             (gmx_mtop_ftype_count(mtop, F_LJ14) > 0 ||
                              gmx_mtop_ftype_count(mtop, F_LJC14_Q) > 0 ||
@@ -3102,6 +3120,17 @@ void init_forcerec(FILE              *fp,
             }
         }
     }
+    else if ((fr->eDispCorr != edispcNO) &&
+             ((fr->vdw_modifier == eintmodPOTSWITCH) ||
+              (fr->vdw_modifier == eintmodFORCESWITCH) ||
+              (fr->vdw_modifier == eintmodPOTSHIFT)))
+    {
+        /* 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]);
+    }
+
     if (bMakeSeparate14Table)
     {
         /* generate extra tables with plain Coulomb for 1-4 interactions only */
index 5208983deccab7cc9b8e44c25ab7ba293a59f4cb..496f605300962ce27da0b3a89047a353ffdd4de3 100644 (file)
@@ -127,7 +127,8 @@ static void init_nblist(FILE *log, t_nblist *nl_sr, t_nblist *nl_lr,
                         int maxsr, int maxlr,
                         int ivdw, int ivdwmod,
                         int ielec, int ielecmod,
-                        int igeometry, int type)
+                        int igeometry, int type,
+                        gmx_bool bElecAndVdwSwitchDiffers)
 {
     t_nblist *nl;
     int       homenr;
@@ -160,7 +161,7 @@ static void init_nblist(FILE *log, t_nblist *nl_sr, t_nblist *nl_lr,
         }
 
         /* This will also set the simd_padding_width field */
-        gmx_nonbonded_set_kernel_pointers( (i == 0) ? log : NULL, nl);
+        gmx_nonbonded_set_kernel_pointers( (i == 0) ? log : NULL, nl, bElecAndVdwSwitchDiffers);
 
         /* maxnri is influenced by the number of shifts (maximum is 8)
          * and the number of energy groups.
@@ -197,10 +198,11 @@ void init_neighbor_list(FILE *log, t_forcerec *fr, int homenr)
      * cache trashing.
      */
     int        maxsr, maxsr_wat, maxlr, maxlr_wat;
-    int        ielec, ielecf, ivdw, ielecmod, ielecmodf, ivdwmod, type;
+    int        ielec, ivdw, ielecmod, ivdwmod, type;
     int        solvent;
     int        igeometry_def, igeometry_w, igeometry_ww;
     int        i;
+    gmx_bool   bElecAndVdwSwitchDiffers;
     t_nblists *nbl;
 
     /* maxsr     = homenr-fr->nWatMol*3; */
@@ -227,11 +229,12 @@ void init_neighbor_list(FILE *log, t_forcerec *fr, int homenr)
     }
 
     /* Determine the values for ielec/ivdw. */
-    ielec    = fr->nbkernel_elec_interaction;
-    ivdw     = fr->nbkernel_vdw_interaction;
-    ielecmod = fr->nbkernel_elec_modifier;
-    ivdwmod  = fr->nbkernel_vdw_modifier;
-    type     = GMX_NBLIST_INTERACTION_STANDARD;
+    ielec                    = fr->nbkernel_elec_interaction;
+    ivdw                     = fr->nbkernel_vdw_interaction;
+    ielecmod                 = fr->nbkernel_elec_modifier;
+    ivdwmod                  = fr->nbkernel_vdw_modifier;
+    type                     = GMX_NBLIST_INTERACTION_STANDARD;
+    bElecAndVdwSwitchDiffers = ( (fr->rcoulomb_switch != fr->rvdw_switch) || (fr->rcoulomb != fr->rvdw));
 
     fr->ns.bCGlist = (getenv("GMX_NBLISTCG") != 0);
     if (!fr->ns.bCGlist)
@@ -267,19 +270,19 @@ void init_neighbor_list(FILE *log, t_forcerec *fr, int homenr)
             type = GMX_NBLIST_INTERACTION_ADRESS;
         }
         init_nblist(log, &nbl->nlist_sr[eNL_VDWQQ], &nbl->nlist_lr[eNL_VDWQQ],
-                    maxsr, maxlr, ivdw, ivdwmod, ielec, ielecmod, igeometry_def, type);
+                    maxsr, maxlr, ivdw, ivdwmod, ielec, ielecmod, igeometry_def, type, bElecAndVdwSwitchDiffers);
         init_nblist(log, &nbl->nlist_sr[eNL_VDW], &nbl->nlist_lr[eNL_VDW],
-                    maxsr, maxlr, ivdw, ivdwmod, GMX_NBKERNEL_ELEC_NONE, eintmodNONE, igeometry_def, type);
+                    maxsr, maxlr, ivdw, ivdwmod, GMX_NBKERNEL_ELEC_NONE, eintmodNONE, igeometry_def, type, bElecAndVdwSwitchDiffers);
         init_nblist(log, &nbl->nlist_sr[eNL_QQ], &nbl->nlist_lr[eNL_QQ],
-                    maxsr, maxlr, GMX_NBKERNEL_VDW_NONE, eintmodNONE, ielec, ielecmod, igeometry_def, type);
+                    maxsr, maxlr, GMX_NBKERNEL_VDW_NONE, eintmodNONE, ielec, ielecmod, igeometry_def, type, bElecAndVdwSwitchDiffers);
         init_nblist(log, &nbl->nlist_sr[eNL_VDWQQ_WATER], &nbl->nlist_lr[eNL_VDWQQ_WATER],
-                    maxsr_wat, maxlr_wat, ivdw, ivdwmod, ielec, ielecmod, igeometry_w, type);
+                    maxsr_wat, maxlr_wat, ivdw, ivdwmod, ielec, ielecmod, igeometry_w, type, bElecAndVdwSwitchDiffers);
         init_nblist(log, &nbl->nlist_sr[eNL_QQ_WATER], &nbl->nlist_lr[eNL_QQ_WATER],
-                    maxsr_wat, maxlr_wat, GMX_NBKERNEL_VDW_NONE, eintmodNONE, ielec, ielecmod, igeometry_w, type);
+                    maxsr_wat, maxlr_wat, GMX_NBKERNEL_VDW_NONE, eintmodNONE, ielec, ielecmod, igeometry_w, type, bElecAndVdwSwitchDiffers);
         init_nblist(log, &nbl->nlist_sr[eNL_VDWQQ_WATERWATER], &nbl->nlist_lr[eNL_VDWQQ_WATERWATER],
-                    maxsr_wat, maxlr_wat, ivdw, ivdwmod, ielec, ielecmod, igeometry_ww, type);
+                    maxsr_wat, maxlr_wat, ivdw, ivdwmod, ielec, ielecmod, igeometry_ww, type, bElecAndVdwSwitchDiffers);
         init_nblist(log, &nbl->nlist_sr[eNL_QQ_WATERWATER], &nbl->nlist_lr[eNL_QQ_WATERWATER],
-                    maxsr_wat, maxlr_wat, GMX_NBKERNEL_VDW_NONE, eintmodNONE, ielec, ielecmod, igeometry_ww, type);
+                    maxsr_wat, maxlr_wat, GMX_NBKERNEL_VDW_NONE, eintmodNONE, ielec, ielecmod, igeometry_ww, type, bElecAndVdwSwitchDiffers);
 
         /* Did we get the solvent loops so we can use optimized water kernels? */
         if (nbl->nlist_sr[eNL_VDWQQ_WATER].kernelptr_vf == NULL
@@ -299,30 +302,19 @@ void init_neighbor_list(FILE *log, t_forcerec *fr, int homenr)
 
         if (fr->efep != efepNO)
         {
-            if ((fr->bEwald) && (fr->sc_alphacoul > 0)) /* need to handle long range differently if using softcore */
-            {
-                ielecf    = GMX_NBKERNEL_ELEC_EWALD;
-                ielecmodf = eintmodNONE;
-            }
-            else
-            {
-                ielecf    = ielec;
-                ielecmodf = ielecmod;
-            }
-
             init_nblist(log, &nbl->nlist_sr[eNL_VDWQQ_FREE], &nbl->nlist_lr[eNL_VDWQQ_FREE],
-                        maxsr, maxlr, ivdw, ivdwmod, ielecf, ielecmod, GMX_NBLIST_GEOMETRY_PARTICLE_PARTICLE, GMX_NBLIST_INTERACTION_FREE_ENERGY);
+                        maxsr, maxlr, ivdw, ivdwmod, ielec, ielecmod, GMX_NBLIST_GEOMETRY_PARTICLE_PARTICLE, GMX_NBLIST_INTERACTION_FREE_ENERGY, bElecAndVdwSwitchDiffers);
             init_nblist(log, &nbl->nlist_sr[eNL_VDW_FREE], &nbl->nlist_lr[eNL_VDW_FREE],
-                        maxsr, maxlr, ivdw, ivdwmod, GMX_NBKERNEL_ELEC_NONE, eintmodNONE, GMX_NBLIST_GEOMETRY_PARTICLE_PARTICLE, GMX_NBLIST_INTERACTION_FREE_ENERGY);
+                        maxsr, maxlr, ivdw, ivdwmod, GMX_NBKERNEL_ELEC_NONE, eintmodNONE, GMX_NBLIST_GEOMETRY_PARTICLE_PARTICLE, GMX_NBLIST_INTERACTION_FREE_ENERGY, bElecAndVdwSwitchDiffers);
             init_nblist(log, &nbl->nlist_sr[eNL_QQ_FREE], &nbl->nlist_lr[eNL_QQ_FREE],
-                        maxsr, maxlr, GMX_NBKERNEL_VDW_NONE, eintmodNONE, ielecf, ielecmod, GMX_NBLIST_GEOMETRY_PARTICLE_PARTICLE, GMX_NBLIST_INTERACTION_FREE_ENERGY);
+                        maxsr, maxlr, GMX_NBKERNEL_VDW_NONE, eintmodNONE, ielec, ielecmod, GMX_NBLIST_GEOMETRY_PARTICLE_PARTICLE, GMX_NBLIST_INTERACTION_FREE_ENERGY, bElecAndVdwSwitchDiffers);
         }
     }
     /* QMMM MM list */
     if (fr->bQMMM && fr->qr->QMMMscheme != eQMMMschemeoniom)
     {
         init_nblist(log, &fr->QMMMlist, NULL,
-                    maxsr, maxlr, 0, 0, ielec, ielecmod, GMX_NBLIST_GEOMETRY_PARTICLE_PARTICLE, GMX_NBLIST_INTERACTION_STANDARD);
+                    maxsr, maxlr, 0, 0, ielec, ielecmod, GMX_NBLIST_GEOMETRY_PARTICLE_PARTICLE, GMX_NBLIST_INTERACTION_STANDARD, bElecAndVdwSwitchDiffers);
     }
 
     if (log != NULL)
index 8155c99ee75af15dc463944b453cdb0d627e4c2b..02e1248b2cbf4f380268f6ea1b21fe3d6a418182 100644 (file)
@@ -2262,11 +2262,11 @@ integrate_table(real vdwtab[], real scale, int offstart, int rstart, int rend,
 
 void calc_enervirdiff(FILE *fplog, int eDispCorr, t_forcerec *fr)
 {
-    double eners[2], virs[2], enersum, virsum, y0, f, g, h;
-    double r0, r1, r, rc3, rc9, ea, eb, ec, pa, pb, pc, pd;
-    double invscale, invscale2, invscale3;
-    int    ri0, ri1, ri, i, offstart, offset;
-    real   scale, *vdwtab, tabfactor, tmp;
+    double   eners[2], virs[2], enersum, virsum, y0, f, g, h;
+    double   r0, r1, r, rc3, rc9, ea, eb, ec, pa, pb, pc, pd;
+    double   invscale, invscale2, invscale3;
+    int      ri0, ri1, ri, i, offstart, offset;
+    real     scale, *vdwtab, tabfactor, tmp;
 
     fr->enershiftsix    = 0;
     fr->enershifttwelve = 0;
@@ -2282,30 +2282,53 @@ void calc_enervirdiff(FILE *fplog, int eDispCorr, t_forcerec *fr)
             eners[i] = 0;
             virs[i]  = 0;
         }
-        if (fr->vdwtype == evdwSWITCH || fr->vdwtype == evdwSHIFT ||
-            fr->vdw_modifier == eintmodPOTSWITCH ||
-            fr->vdw_modifier == eintmodFORCESWITCH)
+        if ((fr->vdw_modifier == eintmodPOTSHIFT) ||
+            (fr->vdw_modifier == eintmodPOTSWITCH) ||
+            (fr->vdw_modifier == eintmodFORCESWITCH) ||
+            (fr->vdwtype == evdwSHIFT) ||
+            (fr->vdwtype == evdwSWITCH))
         {
-            if (fr->rvdw_switch == 0)
+            if (((fr->vdw_modifier == eintmodPOTSWITCH) ||
+                 (fr->vdw_modifier == eintmodFORCESWITCH) ||
+                 (fr->vdwtype == evdwSWITCH)) && fr->rvdw_switch == 0)
             {
                 gmx_fatal(FARGS,
                           "With dispersion correction rvdw-switch can not be zero "
                           "for vdw-type = %s", evdw_names[fr->vdwtype]);
             }
 
-            scale  = fr->nblists[0].table_elec_vdw.scale;
+            scale  = fr->nblists[0].table_vdw.scale;
             vdwtab = fr->nblists[0].table_vdw.data;
 
             /* Round the cut-offs to exact table values for precision */
             ri0  = floor(fr->rvdw_switch*scale);
             ri1  = ceil(fr->rvdw*scale);
+
+            /* The code below has some support for handling force-switching, i.e.
+             * when the force (instead of potential) is switched over a limited
+             * region. This leads to a constant shift in the potential inside the
+             * switching region, which we can handle by adding a constant energy
+             * term in the force-switch case just like when we do potential-shift.
+             *
+             * For now this is not enabled, but to keep the functionality in the
+             * code we check separately for switch and shift. When we do force-switch
+             * the shifting point is rvdw_switch, while it is the cutoff when we
+             * have a classical potential-shift.
+             *
+             * For a pure potential-shift the potential has a constant shift
+             * all the way out to the cutoff, and that is it. For other forms
+             * we need to calculate the constant shift up to the point where we
+             * start modifying the potential.
+             */
+            ri0  = (fr->vdw_modifier == eintmodPOTSHIFT) ? ri1 : ri0;
+
             r0   = ri0/scale;
             r1   = ri1/scale;
             rc3  = r0*r0*r0;
             rc9  = rc3*rc3*rc3;
 
-            if (fr->vdwtype == evdwSHIFT ||
-                fr->vdw_modifier == eintmodFORCESWITCH)
+            if ((fr->vdw_modifier == eintmodFORCESWITCH) ||
+                (fr->vdwtype == evdwSHIFT))
             {
                 /* Determine the constant energy shift below rvdw_switch.
                  * Table has a scale factor since we have scaled it down to compensate
@@ -2314,6 +2337,12 @@ void calc_enervirdiff(FILE *fplog, int eDispCorr, t_forcerec *fr)
                 fr->enershiftsix    = (real)(-1.0/(rc3*rc3)) - 6.0*vdwtab[8*ri0];
                 fr->enershifttwelve = (real)( 1.0/(rc9*rc3)) - 12.0*vdwtab[8*ri0 + 4];
             }
+            else if (fr->vdw_modifier == eintmodPOTSHIFT)
+            {
+                fr->enershiftsix    = (real)(-1.0/(rc3*rc3));
+                fr->enershifttwelve = (real)( 1.0/(rc9*rc3));
+            }
+
             /* Add the constant part from 0 to rvdw_switch.
              * This integration from 0 to rvdw_switch overcounts the number
              * of interactions by 1, as it also counts the self interaction.
@@ -2321,6 +2350,11 @@ void calc_enervirdiff(FILE *fplog, int eDispCorr, t_forcerec *fr)
              */
             eners[0] += 4.0*M_PI*fr->enershiftsix*rc3/3.0;
             eners[1] += 4.0*M_PI*fr->enershifttwelve*rc3/3.0;
+
+            /* Calculate the contribution in the range [r0,r1] where we
+             * modify the potential. For a pure potential-shift modifier we will
+             * have ri0==ri1, and there will not be any contribution here.
+             */
             for (i = 0; i < 2; i++)
             {
                 enersum = 0;
@@ -2330,7 +2364,14 @@ void calc_enervirdiff(FILE *fplog, int eDispCorr, t_forcerec *fr)
                 virs[i]  -= virsum;
             }
 
-            /* now add the correction for rvdw_switch to infinity */
+            /* Alright: Above we compensated by REMOVING the parts outside r0
+             * corresponding to the ideal VdW 1/r6 and /r12 potentials.
+             *
+             * Regardless of whether r0 is the point where we start switching,
+             * or the cutoff where we calculated the constant shift, we include
+             * all the parts we are missing out to infinity from r0 by
+             * calculating the analytical dispersion correction.
+             */
             eners[0] += -4.0*M_PI/(3.0*rc3);
             eners[1] +=  4.0*M_PI/(9.0*rc9);
             virs[0]  +=  8.0*M_PI/rc3;
@@ -2375,10 +2416,7 @@ void calc_enervirdiff(FILE *fplog, int eDispCorr, t_forcerec *fr)
                       evdw_names[fr->vdwtype]);
         }
 
-        /* TODO: remove this code once we have group LJ-PME kernels
-         * that calculate the exact, full LJ param C6/r^6 within the cut-off,
-         * as the current nbnxn kernels do.
-         */
+        /* When we deprecate the group kernels the code below can go too */
         if (fr->vdwtype == evdwPME && fr->cutoff_scheme == ecutsGROUP)
         {
             /* Calculate self-interaction coefficient (assuming that
index ff73a90e89e5452f64cc7feb7d4f570675ac2011..404516c4674eb205ae11425bddb2938921799571 100644 (file)
@@ -746,7 +746,8 @@ static void done_tabledata(t_tabledata *td)
     sfree(td->f);
 }
 
-static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr)
+static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr,
+                       gmx_bool b14only)
 {
     /* Fill the table according to the formulas in the manual.
      * In principle, we only need the potential and the second
@@ -764,23 +765,38 @@ static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr)
     int      i;
     double   reppow, p;
     double   r1, rc, r12, r13;
-    double   r, r2, r6, rc6;
+    double   r, r2, r6, rc2, rc6, rc12;
     double   expr, Vtab, Ftab;
     /* Parameters for David's function */
     double   A = 0, B = 0, C = 0, A_3 = 0, B_4 = 0;
     /* Parameters for the switching function */
     double   ksw, swi, swi1;
     /* Temporary parameters */
-    gmx_bool bSwitch, bShift;
+    gmx_bool bPotentialSwitch, bForceSwitch, bPotentialShift;
     double   ewc   = fr->ewaldcoeff_q;
     double   ewclj = fr->ewaldcoeff_lj;
+    double   Vcut  = 0;
 
-    bSwitch = ((tp == etabLJ6Switch) || (tp == etabLJ12Switch) ||
-               (tp == etabCOULSwitch) ||
-               (tp == etabEwaldSwitch) || (tp == etabEwaldUserSwitch));
-
-    bShift  = ((tp == etabLJ6Shift) || (tp == etabLJ12Shift) ||
-               (tp == etabShift));
+    if (b14only)
+    {
+        bPotentialSwitch = FALSE;
+        bForceSwitch     = FALSE;
+        bPotentialShift  = FALSE;
+    }
+    else
+    {
+        bPotentialSwitch = ((tp == etabLJ6Switch) || (tp == etabLJ12Switch) ||
+                            (tp == etabCOULSwitch) ||
+                            (tp == etabEwaldSwitch) || (tp == etabEwaldUserSwitch) ||
+                            (tprops[tp].bCoulomb && (fr->coulomb_modifier == eintmodPOTSWITCH)) ||
+                            (!tprops[tp].bCoulomb && (fr->vdw_modifier == eintmodPOTSWITCH)));
+        bForceSwitch  = ((tp == etabLJ6Shift) || (tp == etabLJ12Shift) ||
+                         (tp == etabShift) ||
+                         (tprops[tp].bCoulomb && (fr->coulomb_modifier == eintmodFORCESWITCH)) ||
+                         (!tprops[tp].bCoulomb && (fr->vdw_modifier == eintmodFORCESWITCH)));
+        bPotentialShift = ((tprops[tp].bCoulomb && (fr->coulomb_modifier == eintmodPOTSHIFT)) ||
+                           (!tprops[tp].bCoulomb && (fr->vdw_modifier == eintmodPOTSHIFT)));
+    }
 
     reppow = fr->reppow;
 
@@ -794,7 +810,7 @@ static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr)
         r1 = fr->rvdw_switch;
         rc = fr->rvdw;
     }
-    if (bSwitch)
+    if (bPotentialSwitch)
     {
         ksw  = 1.0/(pow5(rc-r1));
     }
@@ -802,7 +818,7 @@ static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr)
     {
         ksw  = 0.0;
     }
-    if (bShift)
+    if (bForceSwitch)
     {
         if (tp == etabShift)
         {
@@ -838,6 +854,57 @@ static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr)
     fp = xvgropen("switch.xvg", "switch", "r", "s");
 #endif
 
+    if (bPotentialShift)
+    {
+        rc2   = rc*rc;
+        rc6   = 1.0/(rc2*rc2*rc2);
+        if (gmx_within_tol(reppow, 12.0, 10*GMX_DOUBLE_EPS))
+        {
+            rc12 = rc6*rc6;
+        }
+        else
+        {
+            rc12 = pow(rc, -reppow);
+        }
+
+        switch (tp)
+        {
+            case etabLJ6:
+                /* Dispersion */
+                Vcut = -rc6;
+                break;
+            case etabLJ6Ewald:
+                Vcut  = -rc6*exp(-ewclj*ewclj*rc2)*(1 + ewclj*ewclj*rc2 + pow4(ewclj)*rc2*rc2/2);
+                break;
+            case etabLJ12:
+                /* Repulsion */
+                Vcut  = rc12;
+                break;
+            case etabCOUL:
+                Vcut  = 1.0/rc;
+                break;
+            case etabEwald:
+            case etabEwaldSwitch:
+                Vtab  = gmx_erfc(ewc*rc)/rc;
+                break;
+            case etabEwaldUser:
+                /* Only calculate minus the reciprocal space contribution */
+                Vtab  = -gmx_erf(ewc*rc)/rc;
+                break;
+            case etabRF:
+            case etabRF_ZERO:
+                /* No need for preventing the usage of modifiers with RF */
+                Vcut  = 0.0;
+                break;
+            case etabEXPMIN:
+                Vcut  = exp(-rc);
+                break;
+            default:
+                gmx_fatal(FARGS, "Cannot apply new potential-shift modifier to interaction type '%s' yet. (%s,%d)",
+                          tprops[tp].name, __FILE__, __LINE__);
+        }
+    }
+
     for (i = td->nx0; (i < td->nx); i++)
     {
         r     = td->x[i];
@@ -853,7 +920,7 @@ static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr)
         }
         Vtab  = 0.0;
         Ftab  = 0.0;
-        if (bSwitch)
+        if (bPotentialSwitch)
         {
             /* swi is function, swi1 1st derivative and swi2 2nd derivative */
             /* The switch function is 1 for r<r1, 0 for r>rc, and smooth for
@@ -1004,7 +1071,7 @@ static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr)
                 gmx_fatal(FARGS, "Table type %d not implemented yet. (%s,%d)",
                           tp, __FILE__, __LINE__);
         }
-        if (bShift)
+        if (bForceSwitch)
         {
             /* Normal coulomb with cut-off correction for potential */
             if (r < rc)
@@ -1019,6 +1086,25 @@ static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr)
                     Ftab  +=   A*r12 + B*r13;
                 }
             }
+            else
+            {
+                /* Make sure interactions are zero outside cutoff with modifiers */
+                Vtab = 0;
+                Ftab = 0;
+            }
+        }
+        if (bPotentialShift)
+        {
+            if (r < rc)
+            {
+                Vtab -= Vcut;
+            }
+            else
+            {
+                /* Make sure interactions are zero outside cutoff with modifiers */
+                Vtab = 0;
+                Ftab = 0;
+            }
         }
 
         if (ETAB_USER(tp))
@@ -1027,12 +1113,20 @@ static void fill_table(t_tabledata *td, int tp, const t_forcerec *fr)
             Ftab += td->f[i];
         }
 
-        if ((r > r1) && bSwitch)
+        if (bPotentialSwitch)
         {
-            Ftab = Ftab*swi - Vtab*swi1;
-            Vtab = Vtab*swi;
+            if (r >= rc)
+            {
+                /* Make sure interactions are zero outside cutoff with modifiers */
+                Vtab = 0;
+                Ftab = 0;
+            }
+            else if (r > r1)
+            {
+                Ftab = Ftab*swi - Vtab*swi1;
+                Vtab = Vtab*swi;
+            }
         }
-
         /* Convert to single precision when we store to mem */
         td->v[i]  = Vtab;
         td->f[i]  = Ftab;
@@ -1191,23 +1285,29 @@ static void set_table_type(int tabsel[], const t_forcerec *fr, gmx_bool b14only)
                 gmx_incons("Potential modifiers other than potential-shift are only implemented for LJ cut-off");
             }
 
-            switch (fr->vdw_modifier)
+            /* LJ-PME and other (shift-only) modifiers are handled by applying the modifiers
+             * to the original interaction forms when we fill the table, so we only check cutoffs here.
+             */
+            if (fr->vdwtype == evdwCUT)
             {
-                case eintmodNONE:
-                case eintmodPOTSHIFT:
-                case eintmodEXACTCUTOFF:
-                    /* No modification */
-                    break;
-                case eintmodPOTSWITCH:
-                    tabsel[etiLJ6]  = etabLJ6Switch;
-                    tabsel[etiLJ12] = etabLJ12Switch;
-                    break;
-                case eintmodFORCESWITCH:
-                    tabsel[etiLJ6]  = etabLJ6Shift;
-                    tabsel[etiLJ12] = etabLJ12Shift;
-                    break;
-                default:
-                    gmx_incons("Unsupported vdw_modifier");
+                switch (fr->vdw_modifier)
+                {
+                    case eintmodNONE:
+                    case eintmodPOTSHIFT:
+                    case eintmodEXACTCUTOFF:
+                        /* No modification */
+                        break;
+                    case eintmodPOTSWITCH:
+                        tabsel[etiLJ6]  = etabLJ6Switch;
+                        tabsel[etiLJ12] = etabLJ12Switch;
+                        break;
+                    case eintmodFORCESWITCH:
+                        tabsel[etiLJ6]  = etabLJ6Shift;
+                        tabsel[etiLJ12] = etabLJ12Shift;
+                        break;
+                    default:
+                        gmx_incons("Unsupported vdw_modifier");
+                }
             }
         }
     }
@@ -1327,7 +1427,7 @@ t_forcetable make_tables(FILE *out, const output_env_t oenv,
             init_table(nx, nx0,
                        (tabsel[k] == etabEXPMIN) ? table.scale_exp : table.scale,
                        &(td[k]), !bReadTab);
-            fill_table(&(td[k]), tabsel[k], fr);
+            fill_table(&(td[k]), tabsel[k], fr, b14only);
             if (out)
             {
                 fprintf(out, "%s table with %d data points for %s%s.\n"