* To help us fund GROMACS development, we humbly ask that you cite
* the research papers on the package. Check out http://www.gromacs.org.
*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "gmxpre.h"
+
+#include "config.h"
+#include <assert.h>
#include <math.h>
#include <string.h>
-#include <assert.h>
-#include "sysstuff.h"
-#include "typedefs.h"
-#include "vec.h"
-#include "gromacs/math/utilities.h"
-#include "macros.h"
-#include "smalloc.h"
-#include "macros.h"
-#include "gmx_fatal.h"
-#include "gmx_fatal_collective.h"
-#include "physics.h"
-#include "force.h"
-#include "tables.h"
-#include "nonbonded.h"
-#include "invblock.h"
-#include "names.h"
-#include "network.h"
-#include "pbc.h"
-#include "ns.h"
-#include "mshift.h"
-#include "txtdump.h"
-#include "coulomb.h"
-#include "md_support.h"
-#include "md_logging.h"
-#include "domdec.h"
-#include "partdec.h"
-#include "qmmm.h"
-#include "copyrite.h"
-#include "mtop_util.h"
-#include "nbnxn_search.h"
-#include "nbnxn_atomdata.h"
-#include "nbnxn_consts.h"
-#include "gmx_omp_nthreads.h"
-#include "gmx_detect_hardware.h"
-#include "inputrec.h"
-
-#ifdef _MSC_VER
-/* MSVC definition for __cpuid() */
-#include <intrin.h>
-#endif
-#include "types/nbnxn_cuda_types_ext.h"
-#include "gpu_utils.h"
-#include "nbnxn_cuda_data_mgmt.h"
-#include "pmalloc_cuda.h"
+#include "gromacs/legacyheaders/copyrite.h"
+#include "gromacs/legacyheaders/coulomb.h"
+#include "gromacs/legacyheaders/domdec.h"
+#include "gromacs/legacyheaders/force.h"
+#include "gromacs/legacyheaders/gmx_detect_hardware.h"
+#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
+#include "gromacs/legacyheaders/gpu_utils.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/pmalloc_cuda.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/legacyheaders/types/nbnxn_cuda_types_ext.h"
+#include "gromacs/math/units.h"
+#include "gromacs/math/utilities.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/mdlib/nb_verlet.h"
+#include "gromacs/mdlib/nbnxn_atomdata.h"
+#include "gromacs/mdlib/nbnxn_consts.h"
+#include "gromacs/mdlib/nbnxn_search.h"
+#include "gromacs/mdlib/nbnxn_simd.h"
+#include "gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_data_mgmt.h"
+#include "gromacs/pbcutil/ishift.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/topology/mtop_util.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/smalloc.h"
t_forcerec *mk_forcerec(void)
{
return nbfp;
}
+static real *make_ljpme_c6grid(const gmx_ffparams_t *idef, t_forcerec *fr)
+{
+ int i, j, k, atnr;
+ real c6, c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
+ real *grid;
+
+ /* 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
+ * access to the C6-values used on the reciprocal grid in pme.c
+ */
+
+ atnr = idef->atnr;
+ snew(grid, 2*atnr*atnr);
+ for (i = k = 0; (i < atnr); i++)
+ {
+ for (j = 0; (j < atnr); j++, k++)
+ {
+ c6i = idef->iparams[i*(atnr+1)].lj.c6;
+ 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);
+ if (fr->ljpme_combination_rule == eljpmeLB
+ && !gmx_numzero(c6) && !gmx_numzero(c12i) && !gmx_numzero(c12j))
+ {
+ sigmai = pow(c12i / c6i, 1.0/6.0);
+ sigmaj = pow(c12j / c6j, 1.0/6.0);
+ epsi = c6i * c6i / c12i;
+ epsj = c6j * c6j / c12j;
+ c6 = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 6);
+ }
+ /* Store the elements at the same relative positions as C6 in nbfp in order
+ * to simplify access in the kernels
+ */
+ grid[2*(atnr*i+j)] = c6*6.0;
+ }
+ }
+ return grid;
+}
+
static real *mk_nbfp_combination_rule(const gmx_ffparams_t *idef, int comb_rule)
{
real *nbfp;
sigmaj = pow(c12j / c6j, 1.0/6.0);
epsi = c6i * c6i / c12i;
epsj = c6j * c6j / c12j;
- c6 = epsi * epsj * pow(0.5*(sigmai+sigmaj), 6);
- c12 = epsi * epsj * pow(0.5*(sigmai+sigmaj), 12);
+ c6 = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 6);
+ c12 = sqrt(epsi * epsj) * pow(0.5*(sigmai+sigmaj), 12);
}
C6(nbfp, atnr, i, j) = c6*6.0;
C12(nbfp, atnr, i, j) = c12*12.0;
int cginfo,
int *cg_sp)
{
- const t_blocka * excl;
+ const t_blocka *excl;
t_atom *atom;
int j, k;
int j0, j1, nj;
gmx_bool perturbed;
gmx_bool has_vdw[4];
gmx_bool match;
- real tmp_charge[4];
- int tmp_vdwtype[4];
+ real tmp_charge[4] = { 0.0 }; /* init to zero to make gcc4.8 happy */
+ int tmp_vdwtype[4] = { 0 }; /* init to zero to make gcc4.8 happy */
int tjA;
gmx_bool qm;
solvent_parameters_t *solvent_parameters;
bestsol = esolNO;
}
-#ifdef DISABLE_WATER_NLIST
- bestsol = esolNO;
-#endif
-
fr->nWatMol = 0;
for (mb = 0; mb < mtop->nmolblock; mb++)
{
static cginfo_mb_t *init_cginfo_mb(FILE *fplog, const gmx_mtop_t *mtop,
t_forcerec *fr, gmx_bool bNoSolvOpt,
+ gmx_bool *bFEP_NonBonded,
gmx_bool *bExcl_IntraCGAll_InterCGNone)
{
const t_block *cgs;
int *a_con;
int ftype;
int ia;
- gmx_bool bId, *bExcl, bExclIntraAll, bExclInter, bHaveVDW, bHaveQ;
+ gmx_bool bId, *bExcl, bExclIntraAll, bExclInter, bHaveVDW, bHaveQ, bHavePerturbedAtoms;
ncg_tot = ncg_mtop(mtop);
snew(cginfo_mb, mtop->nmolblock);
}
}
+ *bFEP_NonBonded = FALSE;
*bExcl_IntraCGAll_InterCGNone = TRUE;
excl_nalloc = 10;
/* bExclIntraAll: all intra cg interactions excluded
* bExclInter: any inter cg interactions excluded
*/
- bExclIntraAll = TRUE;
- bExclInter = FALSE;
- bHaveVDW = FALSE;
- bHaveQ = FALSE;
+ bExclIntraAll = TRUE;
+ bExclInter = FALSE;
+ bHaveVDW = FALSE;
+ bHaveQ = FALSE;
+ bHavePerturbedAtoms = FALSE;
for (ai = a0; ai < a1; ai++)
{
/* Check VDW and electrostatic interactions */
bHaveQ = bHaveQ || (molt->atoms.atom[ai].q != 0 ||
molt->atoms.atom[ai].qB != 0);
+ bHavePerturbedAtoms = bHavePerturbedAtoms || (PERTURBED(molt->atoms.atom[ai]) != 0);
+
/* Clear the exclusion list for atom ai */
for (aj = a0; aj < a1; aj++)
{
{
SET_CGINFO_HAS_Q(cginfo[cgm+cg]);
}
+ if (bHavePerturbedAtoms && fr->efep != efepNO)
+ {
+ SET_CGINFO_FEP(cginfo[cgm+cg]);
+ *bFEP_NonBonded = TRUE;
+ }
/* Store the charge group size */
SET_CGINFO_NATOMS(cginfo[cgm+cg], a1-a0);
static void set_chargesum(FILE *log, t_forcerec *fr, const gmx_mtop_t *mtop)
{
/*This now calculates sum for q and c6*/
- double qsum, q2sum, q, c6sum, c6, sqrc6;
+ double qsum, q2sum, q, c6sum, c6;
int mb, nmol, i;
const t_atoms *atoms;
qsum += nmol*q;
q2sum += nmol*q*q;
c6 = mtop->ffparams.iparams[atoms->atom[i].type*(mtop->ffparams.atnr+1)].lj.c6;
- sqrc6 = sqrt(c6);
- c6sum += nmol*sqrc6*sqrc6;
+ c6sum += nmol*c6;
}
}
fr->qsum[0] = qsum;
fr->q2sum[0] = q2sum;
- fr->c6sum[0] = c6sum/12;
+ fr->c6sum[0] = c6sum;
if (fr->efep != efepNO)
{
qsum += nmol*q;
q2sum += nmol*q*q;
c6 = mtop->ffparams.iparams[atoms->atom[i].typeB*(mtop->ffparams.atnr+1)].lj.c6;
- sqrc6 = sqrt(c6);
- c6sum += nmol*sqrc6*sqrc6;
+ c6sum += nmol*c6;
}
fr->qsum[1] = qsum;
fr->q2sum[1] = q2sum;
- fr->c6sum[1] = c6sum/12;
+ fr->c6sum[1] = c6sum;
}
}
else
}
+gmx_bool nbnxn_acceleration_supported(FILE *fplog,
+ const t_commrec *cr,
+ const t_inputrec *ir,
+ gmx_bool bGPU)
+{
+ if (!bGPU && (ir->vdwtype == evdwPME && ir->ljpme_combination_rule == eljpmeLB))
+ {
+ md_print_warn(cr, fplog, "LJ-PME with Lorentz-Berthelot is not supported with %s, falling back to %s\n",
+ bGPU ? "GPUs" : "SIMD kernels",
+ bGPU ? "CPU only" : "plain-C kernels");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
static void pick_nbnxn_kernel_cpu(const t_inputrec gmx_unused *ir,
int *kernel_type,
int *ewald_excl)
*kernel_type = nbnxnk4xN_SIMD_4xN;
#endif
#ifdef GMX_NBNXN_SIMD_2XNN
- /* We expect the 2xNN kernels to be faster in most cases */
*kernel_type = nbnxnk4xN_SIMD_2xNN;
#endif
-#if defined GMX_NBNXN_SIMD_4XN && defined GMX_SIMD_X86_AVX_256_OR_HIGHER
- if (EEL_RF(ir->coulombtype) || ir->coulombtype == eelCUT)
+#if defined GMX_NBNXN_SIMD_2XNN && defined GMX_NBNXN_SIMD_4XN
+ /* We need to choose if we want 2x(N+N) or 4xN kernels.
+ * Currently this is based on the SIMD acceleration choice,
+ * but it might be better to decide this at runtime based on CPU.
+ *
+ * 4xN calculates more (zero) interactions, but has less pair-search
+ * work and much better kernel instruction scheduling.
+ *
+ * Up till now we have only seen that on Intel Sandy/Ivy Bridge,
+ * which doesn't have FMA, both the analytical and tabulated Ewald
+ * kernels have similar pair rates for 4x8 and 2x(4+4), so we choose
+ * 2x(4+4) because it results in significantly fewer pairs.
+ * For RF, the raw pair rate of the 4x8 kernel is higher than 2x(4+4),
+ * 10% with HT, 50% without HT. As we currently don't detect the actual
+ * use of HT, use 4x8 to avoid a potential performance hit.
+ * On Intel Haswell 4x8 is always faster.
+ */
+ *kernel_type = nbnxnk4xN_SIMD_4xN;
+
+#ifndef GMX_SIMD_HAVE_FMA
+ if (EEL_PME_EWALD(ir->coulombtype) ||
+ EVDW_PME(ir->vdwtype))
{
- /* The raw pair rate of the 4x8 kernel is higher than 2x(4+4),
- * 10% with HT, 50% without HT, but extra zeros interactions
- * can compensate. As we currently don't detect the actual use
- * of HT, switch to 4x8 to avoid a potential performance hit.
+ /* We have Ewald kernels without FMA (Intel Sandy/Ivy Bridge).
+ * There are enough instructions to make 2x(4+4) efficient.
*/
- *kernel_type = nbnxnk4xN_SIMD_4xN;
+ *kernel_type = nbnxnk4xN_SIMD_2xNN;
}
#endif
+#endif /* GMX_NBNXN_SIMD_2XNN && GMX_NBNXN_SIMD_4XN */
+
+
if (getenv("GMX_NBNXN_SIMD_4XN") != NULL)
{
#ifdef GMX_NBNXN_SIMD_4XN
}
/* Analytical Ewald exclusion correction is only an option in
- * the SIMD kernel. On BlueGene/Q, this is faster regardless
- * of precision. In single precision, this is faster on
- * Bulldozer, and slightly faster on Sandy Bridge.
+ * the SIMD kernel.
+ * Since table lookup's don't parallelize with SIMD, analytical
+ * will probably always be faster for a SIMD width of 8 or more.
+ * With FMA analytical is sometimes faster for a width if 4 as well.
+ * On BlueGene/Q, this is faster regardless of precision.
+ * In single precision, this is faster on Bulldozer.
*/
-#if ((defined GMX_SIMD_X86_AVX_128_FMA_OR_HIGHER || defined GMX_SIMD_X86_AVX_256_OR_HIGHER || defined __MIC__) && !defined GMX_DOUBLE) || (defined GMX_SIMD_IBM_QPX)
+#if GMX_SIMD_REAL_WIDTH >= 8 || \
+ (GMX_SIMD_REAL_WIDTH >= 4 && defined GMX_SIMD_HAVE_FMA && !defined GMX_DOUBLE) || \
+ defined GMX_SIMD_IBM_QPX
*ewald_excl = ewaldexclAnalytical;
#endif
if (getenv("GMX_NBNXN_EWALD_TABLE") != NULL)
case nbnxnk4xN_SIMD_4xN:
case nbnxnk4xN_SIMD_2xNN:
#ifdef GMX_NBNXN_SIMD
-#ifdef GMX_SIMD_X86_SSE2_OR_HIGHER
- /* We have x86 SSE2 compatible SIMD */
-#ifdef GMX_SIMD_X86_AVX_128_FMA_OR_HIGHER
- returnvalue = "AVX-128-FMA";
+#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 defined GMX_SIMD_X86_AVX_256_OR_HIGHER || defined __AVX__
- /* x86 SIMD intrinsics can be converted to SSE or AVX depending
- * on compiler flags. As we use nearly identical intrinsics,
- * compiling for AVX without an AVX macros effectively results
- * in AVX kernels.
- * For gcc we check for __AVX__
- * At least a check for icc should be added (if there is a macro)
- */
-#if defined GMX_SIMD_X86_AVX_256_OR_HIGHER && !defined GMX_NBNXN_HALF_WIDTH_SIMD
- returnvalue = "AVX-256";
-#else
- returnvalue = "AVX-128";
+ returnvalue = "SIMD";
#endif
-#else
-#ifdef GMX_SIMD_X86_SSE4_1_OR_HIGHER
- returnvalue = "SSE4.1";
-#else
- returnvalue = "SSE2";
-#endif
-#endif
-#endif
-#else /* GMX_SIMD_X86_SSE2_OR_HIGHER */
- /* not GMX_SIMD_X86_SSE2_OR_HIGHER, but other SIMD */
- returnvalue = "SIMD";
-#endif /* GMX_SIMD_X86_SSE2_OR_HIGHER */
#else /* GMX_NBNXN_SIMD */
returnvalue = "not available";
#endif /* GMX_NBNXN_SIMD */
if (*kernel_type == nbnxnkNotSet)
{
- if (use_simd_kernels)
+ /* LJ PME with LB combination rule does 7 mesh operations.
+ * This so slow that we don't compile SIMD non-bonded kernels for that.
+ */
+ if (use_simd_kernels &&
+ nbnxn_acceleration_supported(fp, cr, ir, FALSE))
{
pick_nbnxn_kernel_cpu(ir, kernel_type, ewald_excl);
}
lookup_nbnxn_kernel_name(*kernel_type),
nbnxn_kernel_pairlist_simple(*kernel_type) ? NBNXN_CPU_CLUSTER_I_SIZE : NBNXN_GPU_CLUSTER_SIZE,
nbnxn_kernel_to_cj_size(*kernel_type));
+
+ if (nbnxnk4x4_PlainC == *kernel_type ||
+ nbnxnk8x8x8_PlainC == *kernel_type)
+ {
+ md_print_warn(cr, fp,
+ "WARNING: Using the slow %s kernels. This should\n"
+ "not happen during routine usage on supported platforms.\n\n",
+ lookup_nbnxn_kernel_name(*kernel_type));
+ }
}
}
{
/* At this point the init should never fail as we made sure that
* we have all the GPUs we need. If it still does, we'll bail. */
- gmx_fatal(FARGS, "On node %d failed to initialize GPU #%d: %s",
+ gmx_fatal(FARGS, "On rank %d failed to initialize GPU #%d: %s",
cr->nodeid,
get_gpu_device_id(&hwinfo->gpu_info, gpu_opt,
cr->rank_pp_intranode),
if (bUsesSimpleTables)
{
- /* With a spacing of 0.0005 we are at the force summation accuracy
- * for the SSE kernels for "normal" atomistic simulations.
+ /* Get the Ewald table spacing based on Coulomb and/or LJ
+ * Ewald coefficients and rtol.
*/
- ic->tabq_scale = ewald_spline3_table_scale(ic->ewaldcoeff_q,
- ic->rcoulomb);
+ ic->tabq_scale = ewald_spline3_table_scale(ic);
maxr = (rtab > ic->rcoulomb) ? rtab : ic->rcoulomb;
ic->tabq_size = (int)(maxr*ic->tabq_scale) + 2;
sfree_aligned(ic->tabq_coul_F);
sfree_aligned(ic->tabq_coul_V);
- /* Create the original table data in FDV0 */
- snew_aligned(ic->tabq_coul_FDV0, ic->tabq_size*4, 32);
- snew_aligned(ic->tabq_coul_F, ic->tabq_size, 32);
- snew_aligned(ic->tabq_coul_V, ic->tabq_size, 32);
- table_spline3_fill_ewald_lr(ic->tabq_coul_F, ic->tabq_coul_V, ic->tabq_coul_FDV0,
- ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_q);
+ sfree_aligned(ic->tabq_vdw_FDV0);
+ sfree_aligned(ic->tabq_vdw_F);
+ sfree_aligned(ic->tabq_vdw_V);
+
+ if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype))
+ {
+ /* Create the original table data in FDV0 */
+ snew_aligned(ic->tabq_coul_FDV0, ic->tabq_size*4, 32);
+ snew_aligned(ic->tabq_coul_F, ic->tabq_size, 32);
+ snew_aligned(ic->tabq_coul_V, ic->tabq_size, 32);
+ table_spline3_fill_ewald_lr(ic->tabq_coul_F, ic->tabq_coul_V, ic->tabq_coul_FDV0,
+ ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_q, v_q_ewald_lr);
+ }
+
+ if (EVDW_PME(ic->vdwtype))
+ {
+ snew_aligned(ic->tabq_vdw_FDV0, ic->tabq_size*4, 32);
+ snew_aligned(ic->tabq_vdw_F, ic->tabq_size, 32);
+ snew_aligned(ic->tabq_vdw_V, ic->tabq_size, 32);
+ table_spline3_fill_ewald_lr(ic->tabq_vdw_F, ic->tabq_vdw_V, ic->tabq_vdw_FDV0,
+ ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_lj, v_lj_ewald_lr);
+ }
}
void init_interaction_const_tables(FILE *fp,
{
real spacing;
- if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype))
+ if (ic->eeltype == eelEWALD || EEL_PME(ic->eeltype) || EVDW_PME(ic->vdwtype))
{
init_ewald_f_table(ic, bUsesSimpleTables, rtab);
snew_aligned(ic->tabq_coul_F, 16, 32);
snew_aligned(ic->tabq_coul_V, 16, 32);
- ic->rlist = fr->rlist;
- ic->rlistlong = fr->rlistlong;
+ ic->rlist = fr->rlist;
+ ic->rlistlong = fr->rlistlong;
/* Lennard-Jones */
- ic->vdw_modifier = fr->vdw_modifier;
- ic->rvdw = fr->rvdw;
- ic->rvdw_switch = fr->rvdw_switch;
+ ic->vdwtype = fr->vdwtype;
+ ic->vdw_modifier = fr->vdw_modifier;
+ ic->rvdw = fr->rvdw;
+ ic->rvdw_switch = fr->rvdw_switch;
+ ic->ewaldcoeff_lj = fr->ewaldcoeff_lj;
+ ic->ljpme_comb_rule = fr->ljpme_combination_rule;
+ ic->sh_lj_ewald = 0;
clear_force_switch_constants(&ic->dispersion_shift);
clear_force_switch_constants(&ic->repulsion_shift);
/* Only shift the potential, don't touch the force */
ic->dispersion_shift.cpot = -pow(ic->rvdw, -6.0);
ic->repulsion_shift.cpot = -pow(ic->rvdw, -12.0);
+ 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, -6.0);
+ }
break;
case eintmodFORCESWITCH:
/* Switch the force, switch and shift the potential */
ic->sh_invrc6 = -ic->dispersion_shift.cpot;
/* Electrostatics */
- ic->eeltype = fr->eeltype;
- ic->rcoulomb = fr->rcoulomb;
- ic->epsilon_r = fr->epsilon_r;
- ic->epsfac = fr->epsfac;
-
- /* Ewald */
- ic->ewaldcoeff_q = fr->ewaldcoeff_q;
- ic->ewaldcoeff_lj = fr->ewaldcoeff_lj;
+ ic->eeltype = fr->eeltype;
+ ic->coulomb_modifier = fr->coulomb_modifier;
+ ic->rcoulomb = fr->rcoulomb;
+ ic->epsilon_r = fr->epsilon_r;
+ ic->epsfac = fr->epsfac;
+ ic->ewaldcoeff_q = fr->ewaldcoeff_q;
if (fr->coulomb_modifier == eintmodPOTSHIFT)
{
if (fp != NULL)
{
- fprintf(fp, "Potential shift: LJ r^-12: %.3f r^-6 %.3f",
- ic->repulsion_shift.cpot, ic->dispersion_shift.cpot);
+ real dispersion_shift;
+
+ dispersion_shift = ic->dispersion_shift.cpot;
+ if (EVDW_PME(ic->vdwtype))
+ {
+ dispersion_shift -= ic->sh_lj_ewald;
+ }
+ fprintf(fp, "Potential shift: LJ r^-12: %.3e r^-6: %.3e",
+ ic->repulsion_shift.cpot, dispersion_shift);
+
if (ic->eeltype == eelCUT)
{
- fprintf(fp, ", Coulomb %.3f", -ic->c_rf);
+ fprintf(fp, ", Coulomb %.e", -ic->c_rf);
}
else if (EEL_PME(ic->eeltype))
{
static void init_nb_verlet(FILE *fp,
nonbonded_verlet_t **nb_verlet,
+ gmx_bool bFEP_NonBonded,
const t_inputrec *ir,
const t_forcerec *fr,
const t_commrec *cr,
nbnxn_init_search(&nbv->nbs,
DOMAINDECOMP(cr) ? &cr->dd->nc : NULL,
DOMAINDECOMP(cr) ? domdec_zones(cr->dd) : NULL,
- gmx_omp_nthreads_get(emntNonbonded));
+ bFEP_NonBonded,
+ gmx_omp_nthreads_get(emntPairsearch));
for (i = 0; i < nbv->ngrp; i++)
{
nbv->grp[0].kernel_type != nbv->grp[i].kernel_type)
{
gmx_bool bSimpleList;
+ int enbnxninitcombrule;
bSimpleList = nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type);
+ if (bSimpleList && (fr->vdwtype == evdwCUT && (fr->vdw_modifier == eintmodNONE || fr->vdw_modifier == eintmodPOTSHIFT)))
+ {
+ /* Plain LJ cut-off: we can optimize with combination rules */
+ enbnxninitcombrule = enbnxninitcombruleDETECT;
+ }
+ else if (fr->vdwtype == evdwPME)
+ {
+ /* LJ-PME: we need to use a combination rule for the grid */
+ if (fr->ljpme_combination_rule == eljpmeGEOM)
+ {
+ enbnxninitcombrule = enbnxninitcombruleGEOM;
+ }
+ else
+ {
+ enbnxninitcombrule = enbnxninitcombruleLB;
+ }
+ }
+ else
+ {
+ /* We use a full combination matrix: no rule required */
+ enbnxninitcombrule = enbnxninitcombruleNONE;
+ }
+
+
snew(nbv->grp[i].nbat, 1);
nbnxn_atomdata_init(fp,
nbv->grp[i].nbat,
nbv->grp[i].kernel_type,
- /* SIMD LJ switch kernels don't support LJ combination rules */
- bSimpleList && !(fr->vdw_modifier == eintmodFORCESWITCH || fr->vdw_modifier == eintmodPOTSWITCH),
+ enbnxninitcombrule,
fr->ntype, fr->nbfp,
ir->opts.ngener,
bSimpleList ? gmx_omp_nthreads_get(emntNonbonded) : 1,
const t_block *cgs;
gmx_bool bGenericKernelOnly;
gmx_bool bMakeTables, bMakeSeparate14Table, bSomeNormalNbListsAreInUse;
+ gmx_bool bFEP_NonBonded;
t_nblists *nbl;
int *nm_ind, egp_flags;
fr->bGrid = (ir->ns_type == ensGRID);
fr->ePBC = ir->ePBC;
+ if (fr->cutoff_scheme == ecutsGROUP)
+ {
+ const char *note = "NOTE: This file uses the deprecated 'group' cutoff_scheme. This will be\n"
+ "removed in a future release when 'verlet' supports all interaction forms.\n";
+
+ if (MASTER(cr))
+ {
+ fprintf(stderr, "\n%s\n", note);
+ }
+ if (fp != NULL)
+ {
+ fprintf(fp, "\n%s\n", note);
+ }
+ }
+
/* Determine if we will do PBC for distances in bonded interactions */
if (fr->ePBC == epbcNONE)
{
switch (fr->vdwtype)
{
case evdwCUT:
- case evdwPME:
if (fr->bBHAM)
{
fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_BUCKINGHAM;
fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LENNARDJONES;
}
break;
+ case evdwPME:
+ fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LJEWALD;
+ break;
case evdwSWITCH:
case evdwSHIFT:
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);
if (ir->cutoff_scheme == ecutsGROUP)
{
- fr->bvdwtab = (fr->vdwtype != evdwCUT ||
- !gmx_within_tol(fr->reppow, 12.0, 10*GMX_DOUBLE_EPS));
+ fr->bvdwtab = ((fr->vdwtype != evdwCUT || !gmx_within_tol(fr->reppow, 12.0, 10*GMX_DOUBLE_EPS))
+ && !EVDW_PME(fr->vdwtype));
/* We have special kernels for standard Ewald and PME, but the pme-switch ones are tabulated above */
fr->bcoultab = !(fr->eeltype == eelCUT ||
fr->eeltype == eelEWALD ||
/* 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) ||
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;
{
fprintf(fp, "Will do PME sum in reciprocal space for LJ dispersion interactions.\n");
}
- please_cite(fp, "Essman95a");
+ please_cite(fp, "Essmann95a");
fr->ewaldcoeff_lj = calc_ewaldcoeff_lj(ir->rvdw, ir->ewald_rtol_lj);
if (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;
{
fr->ntype = mtop->ffparams.atnr;
fr->nbfp = mk_nbfp(&mtop->ffparams, fr->bBHAM);
+ if (EVDW_PME(fr->vdwtype))
+ {
+ fr->ljpme_c6grid = make_ljpme_c6grid(&mtop->ffparams, fr);
+ }
}
/* Copy the energy group exclusions */
fr->egp_flags = ir->opts.egp_flags;
/* Van der Waals stuff */
- fr->rvdw = cutoff_inf(ir->rvdw);
- fr->rvdw_switch = ir->rvdw_switch;
if ((fr->vdwtype != evdwCUT) && (fr->vdwtype != evdwUSER) && !fr->bBHAM)
{
if (fr->rvdw_switch >= fr->rvdw)
gmx_fatal(FARGS, "Switch/shift interaction not supported with Buckingham");
}
+ if (fr->bBHAM && fr->cutoff_scheme == ecutsVERLET)
+ {
+ gmx_fatal(FARGS, "Verlet cutoff-scheme is not supported with Buckingham");
+ }
+
if (fp)
{
fprintf(fp, "Cut-off's: NS: %g Coulomb: %g %s: %g\n",
fr->gbtabr = 100;
fr->gbtab = make_gb_table(oenv, fr);
- init_gb(&fr->born, cr, fr, ir, mtop, ir->gb_algorithm);
+ init_gb(&fr->born, fr, ir, mtop, ir->gb_algorithm);
/* Copy local gb data (for dd, this is done in dd_partition_system) */
if (!DOMAINDECOMP(cr))
(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 ||
}
}
}
+ 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 */
/* Set all the static charge group info */
fr->cginfo_mb = init_cginfo_mb(fp, mtop, fr, bNoSolvOpt,
+ &bFEP_NonBonded,
&fr->bExcl_IntraCGAll_InterCGNone);
if (DOMAINDECOMP(cr))
{
if (!DOMAINDECOMP(cr))
{
- /* When using particle decomposition, the effect of the second argument,
- * which sets fr->hcg, is corrected later in do_md and init_em.
- */
forcerec_set_ranges(fr, ncg_mtop(mtop), ncg_mtop(mtop),
mtop->natoms, mtop->natoms, mtop->natoms);
}
gmx_fatal(FARGS, "With Verlet lists rcoulomb and rvdw should be identical");
}
- init_nb_verlet(fp, &fr->nbv, ir, fr, cr, nbpu_opt);
+ init_nb_verlet(fp, &fr->nbv, bFEP_NonBonded, ir, fr, cr, nbpu_opt);
}
/* fr->ic is used both by verlet and group kernels (to some extent) now */
fflush(fp);
}
-void forcerec_set_excl_load(t_forcerec *fr,
- const gmx_localtop_t *top, const t_commrec *cr)
+void forcerec_set_excl_load(t_forcerec *fr,
+ const gmx_localtop_t *top)
{
const int *ind, *a;
int t, i, j, ntot, n, ntarget;
- if (cr != NULL && PARTDECOMP(cr))
- {
- /* No OpenMP with particle decomposition */
- pd_at_range(cr,
- &fr->excl_load[0],
- &fr->excl_load[1]);
-
- return;
- }
-
ind = top->excls.index;
a = top->excls.a;
fr->excl_load[t] = i;
}
}
+
+/* Frees GPU memory and destroys the CUDA context.
+ *
+ * Note that this function needs to be called even if GPUs are not used
+ * in this run because the PME ranks have no knowledge of whether GPUs
+ * are used or not, but all ranks need to enter the barrier below.
+ */
+void free_gpu_resources(const t_forcerec *fr,
+ const t_commrec *cr)
+{
+ gmx_bool bIsPPrankUsingGPU;
+ char gpu_err_str[STRLEN];
+
+ bIsPPrankUsingGPU = (cr->duty & DUTY_PP) && fr && fr->nbv && fr->nbv->bUseGPU;
+
+ if (bIsPPrankUsingGPU)
+ {
+ /* free nbnxn data in GPU memory */
+ nbnxn_cuda_free(fr->nbv->cu_nbv);
+
+ /* With tMPI we need to wait for all ranks to finish deallocation before
+ * destroying the context in free_gpu() as some ranks may be sharing
+ * GPU and context.
+ * 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 (PAR(cr))
+ {
+ gmx_barrier(cr);
+ }
+#endif /* GMX_THREAD_MPI */
+
+ /* uninitialize GPU (by destroying the context) */
+ if (!free_gpu(gpu_err_str))
+ {
+ gmx_warning("On rank %d failed to free GPU #%d: %s",
+ cr->nodeid, get_current_gpu_device_id(), gpu_err_str);
+ }
+ }
+}