예제 #1
0
static real ener_drift(const verletbuf_atomtype_t *att, int natt,
                       const gmx_ffparams_t *ffp,
                       real kT_fac,
                       real md1_ljd, real d2_ljd, real md3_ljd,
                       real md1_ljr, real d2_ljr, real md3_ljr,
                       real md1_el,  real d2_el,
                       real r_buffer,
                       real rlist, real boxvol)
{
    /* Erfc(8)=1e-29, use this limit so we have some space for arithmetic
     * on the result when using float precision.
     */
    const real erfc_arg_max = 8.0;

    double     drift_tot, pot1, pot2, pot3, pot;
    int        i, j;
    real       s2i_2d, s2i_3d, s2j_2d, s2j_3d, s2, s;
    int        ti, tj;
    real       md1, d2, md3;
    real       sc_fac, rsh, rsh2;
    double     c_exp, c_erfc;

    drift_tot = 0;

    /* Loop over the different atom type pairs */
    for (i = 0; i < natt; i++)
    {
        get_atom_sigma2(kT_fac, &att[i].prop, &s2i_2d, &s2i_3d);
        ti = att[i].prop.type;

        for (j = i; j < natt; j++)
        {
            get_atom_sigma2(kT_fac, &att[j].prop, &s2j_2d, &s2j_3d);
            tj = att[j].prop.type;

            /* Add up the up to four independent variances */
            s2 = s2i_2d + s2i_3d + s2j_2d + s2j_3d;

            /* Note that attractive and repulsive potentials for individual
             * pairs will partially cancel.
             */
            /* -dV/dr at the cut-off for LJ + Coulomb */
            md1 =
                md1_ljd*ffp->iparams[ti*ffp->atnr+tj].lj.c6 +
                md1_ljr*ffp->iparams[ti*ffp->atnr+tj].lj.c12 +
                md1_el*att[i].prop.q*att[j].prop.q;

            /* d2V/dr2 at the cut-off for LJ + Coulomb */
            d2 =
                d2_ljd*ffp->iparams[ti*ffp->atnr+tj].lj.c6 +
                d2_ljr*ffp->iparams[ti*ffp->atnr+tj].lj.c12 +
                d2_el*att[i].prop.q*att[j].prop.q;

            /* -d3V/dr3 at the cut-off for LJ, we neglect Coulomb */
            md3 =
                md3_ljd*ffp->iparams[ti*ffp->atnr+tj].lj.c6 +
                md3_ljr*ffp->iparams[ti*ffp->atnr+tj].lj.c12;

            rsh    = r_buffer;
            sc_fac = 1.0;

            if (rsh*rsh > 2*s2*erfc_arg_max*erfc_arg_max)
            {
                /* Erfc might run out of float and become 0, somewhat before
                 * c_exp becomes 0. To avoid this and to avoid NaN in
                 * approx_2dof, we set both c_expc and c_erfc to zero.
                 * In any relevant case this has no effect on the results,
                 * since c_exp < 6e-29, so the displacement is completely
                 * negligible for such atom pairs (and an overestimate).
                 * In nearly all use cases, there will be other atom
                 * pairs that contribute much more to the total, so zeroing
                 * this particular contribution has no effect at all.
                 */
                c_exp  = 0;
                c_erfc = 0;
            }
            else
            {
                /* For constraints: adapt r and scaling for the Gaussian */
                if (att[i].prop.bConstr)
                {
                    real sh, sc;

                    approx_2dof(s2i_2d, r_buffer*s2i_2d/s2, &sh, &sc);
                    rsh    += sh;
                    sc_fac *= sc;
                }
                if (att[j].prop.bConstr)
                {
                    real sh, sc;

                    approx_2dof(s2j_2d, r_buffer*s2j_2d/s2, &sh, &sc);
                    rsh    += sh;
                    sc_fac *= sc;
                }

                /* Exact contribution of an atom pair with Gaussian displacement
                 * with sigma s to the energy drift for a potential with
                 * derivative -md and second derivative dd at the cut-off.
                 * The only catch is that for potentials that change sign
                 * near the cut-off there could be an unlucky compensation
                 * of positive and negative energy drift.
                 * Such potentials are extremely rare though.
                 *
                 * Note that pot has unit energy*length, as the linear
                 * atom density still needs to be put in.
                 */
                c_exp  = exp(-rsh*rsh/(2*s2))/sqrt(2*M_PI);
                c_erfc = 0.5*gmx_erfc(rsh/(sqrt(2*s2)));
            }
            s      = sqrt(s2);
            rsh2   = rsh*rsh;

            pot1 = sc_fac*
                md1/2*((rsh2 + s2)*c_erfc - rsh*s*c_exp);
            pot2 = sc_fac*
                d2/6*(s*(rsh2 + 2*s2)*c_exp - rsh*(rsh2 + 3*s2)*c_erfc);
            pot3 = sc_fac*
                md3/24*((rsh2*rsh2 + 6*rsh2*s2 + 3*s2*s2)*c_erfc - rsh*s*(rsh2 + 5*s2)*c_exp);
            pot = pot1 + pot2 + pot3;

            if (gmx_debug_at)
            {
                fprintf(debug, "n %d %d d s %.3f %.3f %.3f %.3f con %d -d1 %8.1e d2 %8.1e -d3 %8.1e pot1 %8.1e pot2 %8.1e pot3 %8.1e pot %8.1e\n",
                        att[i].n, att[j].n,
                        sqrt(s2i_2d), sqrt(s2i_3d),
                        sqrt(s2j_2d), sqrt(s2j_3d),
                        att[i].prop.bConstr+att[j].prop.bConstr,
                        md1, d2, md3,
                        pot1, pot2, pot3, pot);
            }

            /* Multiply by the number of atom pairs */
            if (j == i)
            {
                pot *= (double)att[i].n*(att[i].n - 1)/2;
            }
            else
            {
                pot *= (double)att[i].n*att[j].n;
            }
            /* We need the line density to get the energy drift of the system.
             * The effective average r^2 is close to (rlist+sigma)^2.
             */
            pot *= 4*M_PI*sqr(rlist + s)/boxvol;

            /* Add the unsigned drift to avoid cancellation of errors */
            drift_tot += fabs(pot);
        }
    }

    return drift_tot;
}
예제 #2
0
static real ener_drift(const verletbuf_atomtype_t *att, int natt,
                       const gmx_ffparams_t *ffp,
                       real kT_fac,
                       real md_ljd, real md_ljr, real md_el, real dd_el,
                       real r_buffer,
                       real rlist, real boxvol)
{
    double drift_tot, pot1, pot2, pot;
    int    i, j;
    real   s2i_2d, s2i_3d, s2j_2d, s2j_3d, s2, s;
    int    ti, tj;
    real   md, dd;
    real   sc_fac, rsh;
    double c_exp, c_erfc;

    drift_tot = 0;

    /* Loop over the different atom type pairs */
    for (i = 0; i < natt; i++)
    {
        get_atom_sigma2(kT_fac, &att[i].prop, &s2i_2d, &s2i_3d);
        ti = att[i].prop.type;

        for (j = i; j < natt; j++)
        {
            get_atom_sigma2(kT_fac, &att[j].prop, &s2j_2d, &s2j_3d);
            tj = att[j].prop.type;

            /* Add up the up to four independent variances */
            s2 = s2i_2d + s2i_3d + s2j_2d + s2j_3d;

            /* Note that attractive and repulsive potentials for individual
             * pairs will partially cancel.
             */
            /* -dV/dr at the cut-off for LJ + Coulomb */
            md =
                md_ljd*ffp->iparams[ti*ffp->atnr+tj].lj.c6 +
                md_ljr*ffp->iparams[ti*ffp->atnr+tj].lj.c12 +
                md_el*att[i].prop.q*att[j].prop.q;

            /* d2V/dr2 at the cut-off for Coulomb, we neglect LJ */
            dd = dd_el*att[i].prop.q*att[j].prop.q;

            rsh    = r_buffer;
            sc_fac = 1.0;
            /* For constraints: adapt r and scaling for the Gaussian */
            if (att[i].prop.bConstr)
            {
                real sh, sc;

                approx_2dof(s2i_2d, r_buffer*s2i_2d/s2, &sh, &sc);
                rsh    += sh;
                sc_fac *= sc;
            }
            if (att[j].prop.bConstr)
            {
                real sh, sc;

                approx_2dof(s2j_2d, r_buffer*s2j_2d/s2, &sh, &sc);
                rsh    += sh;
                sc_fac *= sc;
            }

            /* Exact contribution of an atom pair with Gaussian displacement
             * with sigma s to the energy drift for a potential with
             * derivative -md and second derivative dd at the cut-off.
             * The only catch is that for potentials that change sign
             * near the cut-off there could be an unlucky compensation
             * of positive and negative energy drift.
             * Such potentials are extremely rare though.
             *
             * Note that pot has unit energy*length, as the linear
             * atom density still needs to be put in.
             */
            c_exp  = exp(-rsh*rsh/(2*s2))/sqrt(2*M_PI);
            c_erfc = 0.5*gmx_erfc(rsh/(sqrt(2*s2)));
            s      = sqrt(s2);

            pot1 = sc_fac*
                md/2*((rsh*rsh + s2)*c_erfc - rsh*s*c_exp);
            pot2 = sc_fac*
                dd/6*(s*(rsh*rsh + 2*s2)*c_exp - rsh*(rsh*rsh + 3*s2)*c_erfc);
            pot = pot1 + pot2;

            if (gmx_debug_at)
            {
                fprintf(debug, "n %d %d d s %.3f %.3f %.3f %.3f con %d md %8.1e dd %8.1e pot1 %8.1e pot2 %8.1e pot %8.1e\n",
                        att[i].n, att[j].n,
                        sqrt(s2i_2d), sqrt(s2i_3d),
                        sqrt(s2j_2d), sqrt(s2j_3d),
                        att[i].prop.bConstr+att[j].prop.bConstr,
                        md, dd, pot1, pot2, pot);
            }

            /* Multiply by the number of atom pairs */
            if (j == i)
            {
                pot *= (double)att[i].n*(att[i].n - 1)/2;
            }
            else
            {
                pot *= (double)att[i].n*att[j].n;
            }
            /* We need the line density to get the energy drift of the system.
             * The effective average r^2 is close to (rlist+sigma)^2.
             */
            pot *= 4*M_PI*sqr(rlist + s)/boxvol;

            /* Add the unsigned drift to avoid cancellation of errors */
            drift_tot += fabs(pot);
        }
    }

    return drift_tot;
}