Task: Updates to AmoebaVdwForce, AmoebaGeneralizedKirkwoodForce and AmoebaWcaDispersionForce (openmm/openmm#4647)

org: openmm
repo: openmm
pr_number: 4647
issue_title: Updates to AmoebaVdwForce, AmoebaGeneralizedKirkwoodForce and AmoebaWcaDispersionForce
issue_body: The updates to the AmoebaGeneralizedKirkwoodForce and AmoebaWcaDispersionForce will align OpenMM with recent updates to the corresponding forces in Tinker and FFX (further details are available here https://doi.org/10.1063/5.0158914).

The update to the AmoebaVdwForce adds a per-particle scale factor needed for continuous AMOEBA CpHMD (described here https://doi.org/10.1021/acs.jctc.3c01180) to support many hydrogen atoms being turned on / off.
resolved_issues: title: Updates to AmoebaVdwForce, AmoebaGeneralizedKirkwoodForce and AmoebaWcaDispersionForce
body: ### Issue Description

**Problem:**
The current implementation of the `AmoebaGeneralizedKirkwoodForce`, `AmoebaWcaDispersionForce`, and `AmoebaVdwForce` in OpenMM is outdated and lacks compatibility with recent advancements in related force field models. Specifically:
1. The `AmoebaGeneralizedKirkwoodForce` and `AmoebaWcaDispersionForce` do not reflect updates introduced in the latest versions of Tinker and FFX, which include improved parameterization and functionality for generalized Born and dispersion forces.
2. The `AmoebaVdwForce` is missing a per-particle scale factor required to support continuous AMOEBA constant-pH molecular dynamics (CpHMD), which is necessary for simulations involving hydrogen atoms being turned on or off.

**Expected Behavior:**
1. The `AmoebaGeneralizedKirkwoodForce` and `AmoebaWcaDispersionForce` should be updated to align with the latest advancements in Tinker and FFX, ensuring consistency and improved accuracy in simulations.
2. The `AmoebaVdwForce` should support a per-particle scale factor to enable continuous AMOEBA CpHMD simulations, allowing for proper handling of hydrogen atom transitions during simulations.

These updates are essential to maintain compatibility with modern force field developments and to support advanced simulation techniques.

## IMPORTANT INSTRUCTIONS

1. If you edit a python file, you MUST run 'make PythonInstall' inside the build directory
2. If you edit a c++ file, you MUST run 'make -j4 && make install" inside the build directory

Reference solution:
fix_patch: diff --git a/plugins/amoeba/openmmapi/include/openmm/AmoebaGeneralizedKirkwoodForce.h b/plugins/amoeba/openmmapi/include/openmm/AmoebaGeneralizedKirkwoodForce.h
index b258f4eb05..af52935cac 100644
--- a/plugins/amoeba/openmmapi/include/openmm/AmoebaGeneralizedKirkwoodForce.h
+++ b/plugins/amoeba/openmmapi/include/openmm/AmoebaGeneralizedKirkwoodForce.h
@@ -69,6 +69,10 @@ class OPENMM_EXPORT_AMOEBA AmoebaGeneralizedKirkwoodForce : public Force {
      * Add the  parameters for a particle.  This should be called once for each particle
      * in the System.  When it is called for the i'th time, it specifies the parameters for the i'th particle.
      *
+     * This method is provided for backwards compatibility. Compared to the alternative five parameter addParticle
+     * method, the descreenRadius parameter is set to base radius value and the neckFactor is set to zero
+     * (no neck descreening).
+     *
      * @param charge         the charge of the particle, measured in units of the proton charge
      * @param radius         the atomic radius of the particle, measured in nm
      * @param scalingFactor  the scaling factor for the particle
@@ -76,25 +80,55 @@ class OPENMM_EXPORT_AMOEBA AmoebaGeneralizedKirkwoodForce : public Force {
      */
     int addParticle(double charge, double radius, double scalingFactor);
 
+    /**
+     * Add the  parameters for a particle.  This should be called once for each particle
+     * in the System.  When it is called for the i'th time, it specifies the parameters for the i'th particle.
+     * <p>
+     * For generalized Born / generalized Kirkwood methods, the radius of each atom has two roles. The first
+     * is to define the base radius of the atom when computing its effective radius. This base radius is usually
+     * parameterized against solvation free energy differences. The second role is to describe how much continuum
+     * water is displaced when the atom descreens water for the calculation of the Born radii of other atoms.
+     * Separation of the two roles into the "radius" and "descreenRadius" parameters gives model developers more
+     * control over these separate roles.
+     * <p>
+     * For example, the fitting of base "radius" values will usually result in deviation from the force field's
+     * van der Waals definition of Rmin (or sigma). The descreenRadius can be defined separately using force field
+     * van der Waals Rmin values, which maintains consistency of atomic sizes during the HCT pairwise
+     * descreening integral. The "scalingFactor" is applied to the descreenRadius during the HCT pairwise
+     * descreening integral, while the neckFactor (if greater than zero) includes neck contributions to descreening.
+     *
+     * @param charge         the charge of the particle, measured in units of the proton charge
+     * @param radius         the atomic radius of the particle, measured in nm
+     * @param scalingFactor  the scaling factor for the particle (unitless)
+     * @param descreenRadius the atomic radius of the particle for descreening, measure in nm
+     * @param neckFactor     the scaling factor for interstitial neck descreening (unitless)
+     * @return the index of the particle that was added
+    */
+    int addParticle(double charge, double radius, double scalingFactor, double descreenRadius, double neckFactor);
+
     /**
      * Get the force field parameters for a particle.
-     * 
+     *
      * @param      index          the index of the particle for which to get parameters
      * @param[out] charge         the charge of the particle, measured in units of the proton charge
      * @param[out] radius         the atomic radius of the particle, measured in nm
      * @param[out] scalingFactor  the scaling factor for the particle
-     */
-    void getParticleParameters(int index, double& charge, double& radius, double& scalingFactor) const;
+     * @param[out] descreenRadius the atomic radius of the particle for descreening, measure in nm
+     * @param[out] neckFactor     the scaling factor for interstitial neck descreening (unitless)
+    */
+    void getParticleParameters(int index, double& charge, double& radius, double& scalingFactor, double& descreenRadius, double& neckFactor) const;
 
     /**
      * Set the force field parameters for a particle.
-     * 
-     * @param index          the index of the particle for which to set parameters
-     * @param charge         the charge of the particle, measured in units of the proton charge
-     * @param radius         the atomic radius of the particle, measured in nm
-     * @param scalingFactor  the scaling factor for the particle
-     */
-    void setParticleParameters(int index, double charge, double radius, double scalingFactor);
+    *
+    * @param index          the index of the particle for which to set parameters
+    * @param charge         the charge of the particle, measured in units of the proton charge
+    * @param radius         the atomic radius of the particle, measured in nm
+    * @param scalingFactor  the scaling factor for the particle
+    * @param descreenRadius the atomic radius of the particle for descreening, measure in nm
+    * @param neckFactor     the scaling factor for interstitial neck descreening (unitless)
+    */
+    void setParticleParameters(int index, double charge, double radius, double scalingFactor, double descreenRadius, double neckFactor);
 
     /**
      * Get the dielectric constant for the solvent.
@@ -119,38 +153,116 @@ class OPENMM_EXPORT_AMOEBA AmoebaGeneralizedKirkwoodForce : public Force {
 
     /**
      * Set the dielectric constant for the solute.
+     *
+     * @param dielectric The solute dielectric constant.
      */
     void setSoluteDielectric(double dielectric) {
         soluteDielectric = dielectric;
     }
 
+    /**
+     * Get the flag signaling whether the solute descreening integral is rescaled by a Tanh function
+     * to account for interstitial spaces. If True then tanh rescaling is used.
+    */
+    bool getTanhRescaling() const {
+        return tanhRescaling;
+    }
+
+    /**
+     * Set the flag signaling whether the solute descreening integral is rescaled by a Tanh function
+     * to account for interstitial spaces.
+     *
+     * @param tanhRescale False to turn off Tanh rescaling; true to turn on.
+    */
+    void setTanhRescaling(bool tanhRescale) {
+        this->tanhRescaling = tanhRescale;
+    }
+
+    /**
+     * Get Tanh function parameters b0, b1 and b2.
+     *
+     * @param b0 The first tanh parameter.
+     * @param b1 The second tanh parameter.
+     * @param b2 The third tanh parameter.
+     */
+    void getTanhParameters(double& b0, double& b1, double& b2) const {
+        b0 = this->beta0;
+        b1 = this->beta1;
+        b2 = this->beta2;
+    }
+
+    /**
+     * Set the the Tanh function parameters to account for interstitial spaces.
+     *
+     * @param b0 The first tanh parameter.
+     * @param b1 The second tanh parameter.
+     * @param b2 The third tanh parameter.
+     */
+    void setTanhParameters(double b0, double b1, double b2) {
+        this->beta0 = b0;
+        this->beta1 = b1;
+        this->beta2 = b2;
+    }
+
+    /**
+    * Get the offset added to the atomic radius of each atom that sets the beginning of the
+    * descreening integral when calculating effective Born radii.
+    */
+    double getDescreenOffset() const;
+
+    /**
+     * Get the offset added to the atomic radius of each atom that sets the beginning of the
+     * descreening integral when calculating effective Born radii.
+     *
+     * @param descreenOffet The descreening offset (nm).
+     */
+    void setDescreenOffset(double descreenOffet);
+
     /**
      * Get the flag signaling whether the cavity term should be included
      */
     int getIncludeCavityTerm() const;
 
     /**
-     * Set the flag signaling whether the cavity term should be included
+     * Set the flag signaling whether the cavity term should be included.
+     *
+     * @param includeCavityTerm Zero to turn off the cavity term; one to turn on.
      */
     void setIncludeCavityTerm(int includeCavityTerm);
 
     /**
-     * Get the probe radius (nm) used in SASA contribution
+     * Get the probe radius (nm) used for the cavity contribution.
      */
     double getProbeRadius() const;
 
     /**
-     * Set the probe radius (nm) used in SASA contribution
+     * Set the probe radius (nm) used for the cavity contribution.
+     *
+     * @param probeRadius The probeRadius for the cavity term.
      */
     void setProbeRadius(double probeRadius);
 
+    /**
+     * Get the dielectric offset (nm) used for cavity contribution.
+     */
+    double getDielectricOffset() const;
+
+    /**
+     * Set the dielectric offset (nm) used for cavity contribution.
+     *
+     * @param dielectricOffset The dielectric offset (nm).
+     */
+    void setDielectricOffset(double dielectricOffset);
+
     /**
      * Get the surface area factor kJ/(nm*nm) used in SASA contribution
      */
     double getSurfaceAreaFactor() const;
 
     /**
-     * Set the surface area factor kJ/(nm*nm) used in SASA contribution
+     * Set the surface area factor kJ/(nm*nm) used in SASA contribution.
+     *
+     * @param surfaceAreaFactor The surface area factor in kJ/(nm*nm).
      */
     void setSurfaceAreaFactor(double surfaceAreaFactor);
     /**
@@ -177,8 +289,10 @@ class OPENMM_EXPORT_AMOEBA AmoebaGeneralizedKirkwoodForce : public Force {
 private:
     class ParticleInfo;
     int includeCavityTerm;
+    bool tanhRescaling;
     double solventDielectric, soluteDielectric, dielectricOffset,
            probeRadius, surfaceAreaFactor;
+    double beta0, beta1, beta2, descreenOffset;
     std::vector<ParticleInfo> particles;
 };
 
@@ -188,12 +302,12 @@ class OPENMM_EXPORT_AMOEBA AmoebaGeneralizedKirkwoodForce : public Force {
  */
 class AmoebaGeneralizedKirkwoodForce::ParticleInfo {
 public:
-    double charge, radius, scalingFactor;
+    double charge, radius, scalingFactor, descreenRadius, neckFactor;
     ParticleInfo() {
-        charge = radius = scalingFactor = 0.0;
+        charge = radius = scalingFactor = descreenRadius = neckFactor = 0.0;
     }
-    ParticleInfo(double charge, double radius, double scalingFactor) :
-        charge(charge), radius(radius), scalingFactor(scalingFactor) {
+    ParticleInfo(double charge, double radius, double scalingFactor, double descreenRadius, double neckFactor) :
+        charge(charge), radius(radius), scalingFactor(scalingFactor), descreenRadius(descreenRadius), neckFactor(neckFactor) {
     }
 };
 
diff --git a/plugins/amoeba/openmmapi/include/openmm/AmoebaVdwForce.h b/plugins/amoeba/openmmapi/include/openmm/AmoebaVdwForce.h
index 11af5f67bb..60373e8e74 100644
--- a/plugins/amoeba/openmmapi/include/openmm/AmoebaVdwForce.h
+++ b/plugins/amoeba/openmmapi/include/openmm/AmoebaVdwForce.h
@@ -173,10 +173,11 @@ class OPENMM_EXPORT_AMOEBA AmoebaVdwForce : public Force {
      * @param reductionFactor the fraction of the distance along the line from the parent particle to this particle
      *                        at which the interaction site should be placed
      * @param isAlchemical    if true, this vdW particle is undergoing an alchemical change.
-     * @param typeIndex       the index of the particle type for this particle
+     * @param typeIndex       the index of the particle type for this particle.
+     * @param scaleFactor     a scale factor to apply to all interactions involving this particle (used for CpHMD).
      */
     void setParticleParameters(int particleIndex, int parentIndex, double sigma, double epsilon, 
-                               double reductionFactor, bool isAlchemical=false, int typeIndex=-1);
+                               double reductionFactor, bool isAlchemical=false, int typeIndex=-1, double scaleFactor = 1.0);
 
     /**
      * Get the force field parameters for a vdw particle.
@@ -189,9 +190,10 @@ class OPENMM_EXPORT_AMOEBA AmoebaVdwForce : public Force {
      *                             at which the interaction site should be placed
      * @param[out] isAlchemical    if true, this vdW particle is undergoing an alchemical change.
      * @param[out] typeIndex       the index of the particle type for this particle
+     * @param[out] scaleFactor     a scale factor to apply to all interactions involving this particle (used for CpHMD).
      */
     void getParticleParameters(int particleIndex, int& parentIndex, double& sigma, double& epsilon, 
-                               double& reductionFactor, bool& isAlchemical, int& typeIndex) const;
+                               double& reductionFactor, bool& isAlchemical, int& typeIndex, double& scaleFactor) const;
 
     /**
      * Add the force field parameters for a vdw particle.  This version is used when parameters
@@ -203,9 +205,10 @@ class OPENMM_EXPORT_AMOEBA AmoebaVdwForce : public Force {
      * @param reductionFactor the fraction of the distance along the line from the parent particle to this particle
      *                        at which the interaction site should be placed
      * @param isAlchemical    if true, this vdW particle is undergoing an alchemical change.
+     * @param scaleFactor     a scale factor to apply to all interactions involving this particle (used for CpHMD).
      * @return index of added particle
      */
-    int addParticle(int parentIndex, double sigma, double epsilon, double reductionFactor, bool isAlchemical = false);
+    int addParticle(int parentIndex, double sigma, double epsilon, double reductionFactor, bool isAlchemical = false, double scaleFactor = 1.0);
 
     /**
      * Add the force field parameters for a vdw particle. This version is used when parameters
@@ -216,9 +219,10 @@ class OPENMM_EXPORT_AMOEBA AmoebaVdwForce : public Force {
      * @param reductionFactor the fraction of the distance along the line from the parent particle to this particle
      *                        at which the interaction site should be placed
      * @param isAlchemical    if true, this vdW particle is undergoing an alchemical change.
+     * @param scaleFactor     a scale factor to apply to all interactions involving this particle (used for CpHMD).
      * @return index of added particle
      */
-    int addParticle(int parentIndex, int typeIndex, double reductionFactor, bool isAlchemical = false);
+    int addParticle(int parentIndex, int typeIndex, double reductionFactor, bool isAlchemical = false, double scaleFactor = 1.0);
 
     /**
      * Add a particle type.
@@ -489,17 +493,20 @@ class OPENMM_EXPORT_AMOEBA AmoebaVdwForce : public Force {
 class AmoebaVdwForce::VdwInfo {
 public:
     int parentIndex, typeIndex;
-    double reductionFactor, sigma, epsilon, cutoff;
+    double reductionFactor, sigma, epsilon, scaleFactor;
     bool isAlchemical;
     VdwInfo() {
         parentIndex = -1;
+        typeIndex   = -1;
         reductionFactor      = 0.0;
         sigma                = 1.0;
         epsilon              = 0.0;
+        scaleFactor          = 1.0;
         isAlchemical         = false;
     }
-    VdwInfo(int parentIndex, double sigma, double epsilon, int typeIndex, double reductionFactor, bool isAlchemical) :
-        parentIndex(parentIndex), reductionFactor(reductionFactor), sigma(sigma), epsilon(epsilon), typeIndex(typeIndex), isAlchemical(isAlchemical) {
+    VdwInfo(int parentIndex, double sigma, double epsilon, int typeIndex, double reductionFactor, bool isAlchemical, double scaleFactor) :
+        parentIndex(parentIndex), reductionFactor(reductionFactor), sigma(sigma), epsilon(epsilon),
+        typeIndex(typeIndex), isAlchemical(isAlchemical), scaleFactor(scaleFactor) {
     }
 };
 
diff --git a/plugins/amoeba/openmmapi/include/openmm/internal/AmoebaGeneralizedKirkwoodForceImpl.h b/plugins/amoeba/openmmapi/include/openmm/internal/AmoebaGeneralizedKirkwoodForceImpl.h
index d82c0c19a9..3f46708af3 100644
--- a/plugins/amoeba/openmmapi/include/openmm/internal/AmoebaGeneralizedKirkwoodForceImpl.h
+++ b/plugins/amoeba/openmmapi/include/openmm/internal/AmoebaGeneralizedKirkwoodForceImpl.h
@@ -62,6 +62,54 @@ class AmoebaGeneralizedKirkwoodForceImpl : public ForceImpl {
         return kernel;
     }
     void updateParametersInContext(ContextImpl& context);
+
+    /**
+     * Compute a "neck" descreening contribution.
+     * <p>
+     * Using the tabulated Aij and Bij values, compute the neck integral
+     * using Equations 13 and 14 from Aguilar, Shadrach and Onufriev (10.1021/ct100392h).
+     *
+     * @param r separation distance.
+     * @param radius Radius of the current atom.
+     * @param radiusK Radius of the descreening atom.
+     * @param sneck The neck scale factor.
+     * @return The integral value.
+     */
+    static double neckDescreen(double r, double radius, double radiusK, double sneck);
+
+    /**
+     * Get Neck Aij and Bij constants.
+     *
+     * Based on the radii of two input atoms ("radius" for the atom being descreened
+     * and "radiusK" for the descreening atom), the neck parameters Aij and Bij are
+     * interpolated from tabulated values. The definitions of Aij and Bij are defined by
+     * Eq. 11 from Corrigan et al. (10.1063/5.0158914) while Figure 2 describes
+     * a neck descreening region.
+     *
+     * @param radius Radius of the current atom.
+     * @param radiusK Radius of the descreening atom.
+     * @param aij The Aij neck constant.
+     * @param bij the Bij neck constant.
+     */
+    static void getNeckConstants(double radius, double radiusK, double &aij, double &bij);
+
+     /**
+      * The array of neck tabulated radii values.
+      */
+    static const std::vector<float>& getNeckRadii();
+
+    const static int NUM_NECK_RADII = 45;
+
+    /**
+     * The tabulated Aij parameters.
+     */
+    const static float (&getAij())[NUM_NECK_RADII][NUM_NECK_RADII];
+
+     /**
+      * The tabulated Bij parameters.
+      */
+     const static float (&getBij())[NUM_NECK_RADII][NUM_NECK_RADII];
+
 private:
     const AmoebaGeneralizedKirkwoodForce& owner;
     Kernel kernel;
diff --git a/plugins/amoeba/openmmapi/src/AmoebaGeneralizedKirkwoodForce.cpp b/plugins/amoeba/openmmapi/src/AmoebaGeneralizedKirkwoodForce.cpp
index afcd67eb7e..fd1dd68ea6 100644
--- a/plugins/amoeba/openmmapi/src/AmoebaGeneralizedKirkwoodForce.cpp
+++ b/plugins/amoeba/openmmapi/src/AmoebaGeneralizedKirkwoodForce.cpp
@@ -36,35 +36,45 @@
 
 using namespace OpenMM;
 
-AmoebaGeneralizedKirkwoodForce::AmoebaGeneralizedKirkwoodForce() : solventDielectric(78.3), soluteDielectric(1.0), dielectricOffset(0.009), includeCavityTerm(1), probeRadius(0.14) {
-
+AmoebaGeneralizedKirkwoodForce::AmoebaGeneralizedKirkwoodForce() :
+    solventDielectric(78.3), soluteDielectric(1.0), dielectricOffset(0.009), includeCavityTerm(1), probeRadius(0.14),
+    tanhRescaling(false), beta0(0.9563), beta1(0.2578), beta2(0.0810), descreenOffset(0.0) {
      surfaceAreaFactor = -6.0* 3.1415926535*0.0216*1000.0*0.4184;
 }
 
 int AmoebaGeneralizedKirkwoodForce::addParticle(double charge, double radius, double scalingFactor) {
-    particles.push_back(ParticleInfo(charge, radius, scalingFactor));
+    particles.push_back(ParticleInfo(charge, radius, scalingFactor, radius, 0.0));
     return particles.size()-1;
 }
 
-void AmoebaGeneralizedKirkwoodForce::getParticleParameters(int index, double& charge, double& radius, double& scalingFactor) const {
+int AmoebaGeneralizedKirkwoodForce::addParticle(double charge, double radius, double scalingFactor, double descreenRadius, double neckFactor) {
+    particles.push_back(ParticleInfo(charge, radius, scalingFactor, descreenRadius, neckFactor));
+    return particles.size()-1;
+}
+
+void AmoebaGeneralizedKirkwoodForce::getParticleParameters(int index, double& charge, double& radius, double& scalingFactor, double& descreenRadius, double& neckFactor) const {
     charge = particles[index].charge;
     radius = particles[index].radius;
     scalingFactor = particles[index].scalingFactor;
+    descreenRadius = particles[index].descreenRadius;
+    neckFactor = particles[index].neckFactor;
 }
 
-void AmoebaGeneralizedKirkwoodForce::setParticleParameters(int index, double charge, double radius, double scalingFactor) {
+void AmoebaGeneralizedKirkwoodForce::setParticleParameters(int index, double charge, double radius, double scalingFactor, double descreenRadius, double neckFactor) {
     particles[index].charge = charge;
     particles[index].radius = radius;
     particles[index].scalingFactor = scalingFactor;
+    particles[index].descreenRadius = descreenRadius;
+    particles[index].neckFactor = neckFactor;
 }
-/*
+
 double AmoebaGeneralizedKirkwoodForce::getDielectricOffset() const {
     return dielectricOffset;
 }
 
 void AmoebaGeneralizedKirkwoodForce::setDielectricOffset(double inputDielectricOffset) {
     dielectricOffset = inputDielectricOffset;
-} */
+}
 
 int AmoebaGeneralizedKirkwoodForce::getIncludeCavityTerm() const {
     return includeCavityTerm;
@@ -90,6 +100,14 @@ void AmoebaGeneralizedKirkwoodForce::setSurfaceAreaFactor(double inputSurfaceAre
     surfaceAreaFactor = inputSurfaceAreaFactor;
 }
 
+double AmoebaGeneralizedKirkwoodForce::getDescreenOffset() const {
+    return descreenOffset;
+}
+
+void AmoebaGeneralizedKirkwoodForce::setDescreenOffset(double inputDescreenOffet) {
+    descreenOffset = inputDescreenOffet;
+}
+
 ForceImpl* AmoebaGeneralizedKirkwoodForce::createImpl() const {
     return new AmoebaGeneralizedKirkwoodForceImpl(*this);
 }
diff --git a/plugins/amoeba/openmmapi/src/AmoebaGeneralizedKirkwoodForceImpl.cpp b/plugins/amoeba/openmmapi/src/AmoebaGeneralizedKirkwoodForceImpl.cpp
index 432700e74a..4328ed08a0 100644
--- a/plugins/amoeba/openmmapi/src/AmoebaGeneralizedKirkwoodForceImpl.cpp
+++ b/plugins/amoeba/openmmapi/src/AmoebaGeneralizedKirkwoodForceImpl.cpp
@@ -34,7 +34,6 @@
 #include "openmm/amoebaKernels.h"
 
 using namespace OpenMM;
-using std::vector;
 
 AmoebaGeneralizedKirkwoodForceImpl::AmoebaGeneralizedKirkwoodForceImpl(const AmoebaGeneralizedKirkwoodForce& owner) : owner(owner) {
 }
@@ -68,3 +67,567 @@ void AmoebaGeneralizedKirkwoodForceImpl::updateParametersInContext(ContextImpl&
     kernel.getAs<CalcAmoebaGeneralizedKirkwoodForceKernel>().copyParametersToContext(context, owner);
     context.systemChanged();
 }
+
+const static std::vector<float> NECK_RADII = {
+        0.80, 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15, 1.20, 1.25,
+        1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75,
+        1.80, 1.85, 1.90, 1.95, 2.00, 2.05, 2.10, 2.15, 2.20, 2.25,
+        2.30, 2.35, 2.40, 2.45, 2.50, 2.55, 2.60, 2.65, 2.70, 2.75,
+        2.80, 2.85, 2.90, 2.95, 3.00};
+
+const static double MINIMUM_RADIUS = 0.80;
+const static double MAXIMUM_RADIUS = 3.00;
+const static double SPACING = 0.05;
+
+const static float Aij[AmoebaGeneralizedKirkwoodForceImpl::NUM_NECK_RADII][AmoebaGeneralizedKirkwoodForceImpl::NUM_NECK_RADII] = {
+        {0.0000577616, 0.0000584661, 0.0000363925, 0.0000395472, 0.0000443202, 0.0000485507, 0.0000430862,
+                0.0000485067, 0.0000244504, 0.0000278293, 0.0000329908, 0.0000292135, 0.0000343621, 0.0000393724,
+                0.0000352501, 0.0000303823, 0.0000360595, 0.0000418690, 0.0000365804, 0.0000248824, 0.0000375656,
+                0.0000428918, 0.0000377450, 0.0000447160, 0.0000395375, 0.0000345934, 0.0000536114, 0.0000470958,
+                0.0000542111, 0.0000360263, 0.0000553398, 0.0000483197, 0.0000555466, 0.0000374290, 0.0000327412,
+                0.0000386635, 0.0000578345, 0.0000513194, 0.0000581077, 0.0000394022, 0.0000599546, 0.0000392112,
+                0.0000597613, 0.0000403186, 0.0000615149},
+        {0.0000446374, 0.0000460309, 0.0000475714, 0.0000225974, 0.0000318052, 0.0000275537, 0.0000296576,
+                0.0000339157, 0.0000295595, 0.0000344661, 0.0000395587, 0.0000197502, 0.0000300923, 0.0000272524,
+                0.0000187561, 0.0000272680, 0.0000247659, 0.0000214933, 0.0000252850, 0.0000223386, 0.0000261633,
+                0.0000398363, 0.0000363492, 0.0000412599, 0.0000363327, 0.0000422768, 0.0000367723, 0.0000425282,
+                0.0000374607, 0.0000325975, 0.0000383563, 0.0000253707, 0.0000301193, 0.0000200866, 0.0000299808,
+                0.0000263514, 0.0000304240, 0.0000349497, 0.0000408533, 0.0000268003, 0.0000407538, 0.0000357651,
+                0.0000418473, 0.0000278226, 0.0000419066},
+        {0.0000360721, 0.0000365794, 0.0000372415, 0.0000303637, 0.0000319835, 0.0000267215, 0.0000171087,
+                0.0000252702, 0.0000221094, 0.0000187747, 0.0000212341, 0.0000251617, 0.0000215761, 0.0000191079,
+                0.0000291389, 0.0000199177, 0.0000288958, 0.0000260343, 0.0000179669, 0.0000210581, 0.0000178820,
+                0.0000215615, 0.0000243911, 0.0000220007, 0.0000249689, 0.0000295075, 0.0000260062, 0.0000297050,
+                0.0000203093, 0.0000305802, 0.0000202272, 0.0000308984, 0.0000207930, 0.0000316764, 0.0000277135,
+                0.0000316983, 0.0000219362, 0.0000326865, 0.0000286724, 0.0000332929, 0.0000220858, 0.0000332871,
+                0.0000294921, 0.0000348374, 0.0000230989},
+        {0.0000398429, 0.0000223074, 0.0000298279, 0.0000315221, 0.0000187855, 0.0000264807, 0.0000169459,
+                0.0000180824, 0.0000162496, 0.0000233148, 0.0000157394, 0.0000174931, 0.0000152866, 0.0000230620,
+                0.0000205539, 0.0000234872, 0.0000213273, 0.0000181123, 0.0000210938, 0.0000186307, 0.0000216437,
+                0.0000146084, 0.0000222850, 0.0000149429, 0.0000170964, 0.0000153536, 0.0000177037, 0.0000208958,
+                0.0000237412, 0.0000164214, 0.0000239561, 0.0000166849, 0.0000188702, 0.0000167221, 0.0000188448,
+                0.0000172644, 0.0000194706, 0.0000174585, 0.0000259642, 0.0000177409, 0.0000199219, 0.0000235990,
+                0.0000202504, 0.0000234495, 0.0000156052},
+        {0.0000435754, 0.0000245527, 0.0000318302, 0.0000188461, 0.0000192238, 0.0000208396, 0.0000169209,
+                0.0000183816, 0.0000204363, 0.0000131412, 0.0000193141, 0.0000097655, 0.0000110841, 0.0000129627,
+                0.0000112003, 0.0000130467, 0.0000113669, 0.0000168195, 0.0000117549, 0.0000134876, 0.0000118881,
+                0.0000180528, 0.0000159126, 0.0000181928, 0.0000125940, 0.0000183351, 0.0000125678, 0.0000145035,
+                0.0000131847, 0.0000144906, 0.0000129840, 0.0000150912, 0.0000176485, 0.0000153063, 0.0000178114,
+                0.0000157934, 0.0000180763, 0.0000157725, 0.0000186835, 0.0000121847, 0.0000185069, 0.0000161900,
+                0.0000191223, 0.0000126643, 0.0000192108},
+        {0.0000505580, 0.0000264633, 0.0000205166, 0.0000195164, 0.0000204987, 0.0000160556, 0.0000127648,
+                0.0000137952, 0.0000155458, 0.0000176400, 0.0000146241, 0.0000124027, 0.0000103247, 0.0000120745,
+                0.0000105222, 0.0000124882, 0.0000140152, 0.0000095449, 0.0000141168, 0.0000074383, 0.0000142994,
+                0.0000098671, 0.0000112045, 0.0000132832, 0.0000113876, 0.0000135139, 0.0000116918, 0.0000137152,
+                0.0000118068, 0.0000140495, 0.0000093075, 0.0000144645, 0.0000126001, 0.0000142255, 0.0000128571,
+                0.0000148412, 0.0000128819, 0.0000152166, 0.0000099735, 0.0000151882, 0.0000133944, 0.0000154829,
+                0.0000135105, 0.0000154771, 0.0000106154},
+        {0.0000449657, 0.0000230612, 0.0000221541, 0.0000166532, 0.0000171555, 0.0000227174, 0.0000182899,
+                0.0000193198, 0.0000118768, 0.0000130396, 0.0000113361, 0.0000093799, 0.0000108573, 0.0000120843,
+                0.0000106342, 0.0000117938, 0.0000080569, 0.0000091361, 0.0000107718, 0.0000123647, 0.0000106453,
+                0.0000123678, 0.0000109954, 0.0000126151, 0.0000145692, 0.0000128038, 0.0000146248, 0.0000099091,
+                0.0000114200, 0.0000059787, 0.0000117991, 0.0000136370, 0.0000119830, 0.0000080880, 0.0000120147,
+                0.0000108970, 0.0000161522, 0.0000083711, 0.0000094980, 0.0000112647, 0.0000096319, 0.0000113677,
+                0.0000127823, 0.0000114549, 0.0000100471},
+        {0.0000280510, 0.0000266023, 0.0000144227, 0.0000188499, 0.0000184712, 0.0000146008, 0.0000195724,
+                0.0000161268, 0.0000073492, 0.0000137704, 0.0000111866, 0.0000072795, 0.0000081037, 0.0000092319,
+                0.0000078144, 0.0000089354, 0.0000102556, 0.0000090005, 0.0000059425, 0.0000090194, 0.0000080186,
+                0.0000069127, 0.0000082105, 0.0000093797, 0.0000082401, 0.0000093675, 0.0000082671, 0.0000071727,
+                0.0000064889, 0.0000097376, 0.0000087729, 0.0000098019, 0.0000088064, 0.0000059057, 0.0000089896,
+                0.0000077635, 0.0000070336, 0.0000047870, 0.0000094273, 0.0000080466, 0.0000093429, 0.0000083051,
+                0.0000096725, 0.0000109657, 0.0000097148},
+        {0.0000325317, 0.0000295175, 0.0000213868, 0.0000208878, 0.0000153087, 0.0000116695, 0.0000091353,
+                0.0000128753, 0.0000102693, 0.0000107086, 0.0000088500, 0.0000098955, 0.0000081413, 0.0000052255,
+                0.0000078083, 0.0000067997, 0.0000098737, 0.0000066979, 0.0000098507, 0.0000051252, 0.0000074307,
+                0.0000067922, 0.0000057491, 0.0000068223, 0.0000058920, 0.0000070420, 0.0000060742, 0.0000069635,
+                0.0000062687, 0.0000054473, 0.0000061911, 0.0000073394, 0.0000037836, 0.0000074661, 0.0000084983,
+                0.0000073838, 0.0000087027, 0.0000076952, 0.0000088636, 0.0000102208, 0.0000090108, 0.0000104452,
+                0.0000090944, 0.0000104765, 0.0000070232},
+        {0.0000289111, 0.0000198943, 0.0000241993, 0.0000238536, 0.0000182705, 0.0000129878, 0.0000076255,
+                0.0000102719, 0.0000112418, 0.0000067659, 0.0000071756, 0.0000100155, 0.0000065300, 0.0000071811,
+                0.0000060818, 0.0000066768, 0.0000059369, 0.0000065993, 0.0000076971, 0.0000063984, 0.0000057069,
+                0.0000086185, 0.0000076581, 0.0000051018, 0.0000057661, 0.0000051292, 0.0000058121, 0.0000050955,
+                0.0000079236, 0.0000069082, 0.0000060582, 0.0000041682, 0.0000080245, 0.0000042302, 0.0000082046,
+                0.0000056029, 0.0000048280, 0.0000057624, 0.0000065014, 0.0000058246, 0.0000049969, 0.0000058491,
+                0.0000066001, 0.0000059999, 0.0000088507},
+        {0.0000265006, 0.0000302279, 0.0000215102, 0.0000203741, 0.0000195197, 0.0000114271, 0.0000110442,
+                0.0000113577, 0.0000067378, 0.0000072687, 0.0000074058, 0.0000047733, 0.0000067184, 0.0000076291,
+                0.0000062951, 0.0000051957, 0.0000044871, 0.0000050768, 0.0000044211, 0.0000049880, 0.0000043518,
+                0.0000048796, 0.0000057445, 0.0000049576, 0.0000057224, 0.0000037798, 0.0000043883, 0.0000066161,
+                0.0000044279, 0.0000050580, 0.0000045758, 0.0000051723, 0.0000046936, 0.0000052703, 0.0000047182,
+                0.0000041352, 0.0000063088, 0.0000041672, 0.0000063473, 0.0000055519, 0.0000049210, 0.0000033576,
+                0.0000049766, 0.0000033829, 0.0000051220},
+        {0.0000393068, 0.0000205177, 0.0000254533, 0.0000229604, 0.0000129838, 0.0000126427, 0.0000096583,
+                0.0000123670, 0.0000100764, 0.0000100625, 0.0000047514, 0.0000049466, 0.0000041283, 0.0000059449,
+                0.0000065208, 0.0000072136, 0.0000045347, 0.0000039272, 0.0000033654, 0.0000038633, 0.0000032913,
+                0.0000038211, 0.0000041976, 0.0000037860, 0.0000042838, 0.0000037697, 0.0000043641, 0.0000038556,
+                0.0000033089, 0.0000039242, 0.0000043859, 0.0000051678, 0.0000034265, 0.0000040012, 0.0000035224,
+                0.0000053110, 0.0000046531, 0.0000053869, 0.0000036188, 0.0000054308, 0.0000048054, 0.0000055242,
+                0.0000048512, 0.0000056159, 0.0000037853},
+        {0.0000340677, 0.0000307596, 0.0000223668, 0.0000152946, 0.0000146712, 0.0000106255, 0.0000106032,
+                0.0000081832, 0.0000086244, 0.0000049411, 0.0000067545, 0.0000040421, 0.0000044178, 0.0000036241,
+                0.0000052208, 0.0000032905, 0.0000048780, 0.0000053199, 0.0000046459, 0.0000039440, 0.0000026124,
+                0.0000050097, 0.0000026014, 0.0000029505, 0.0000032710, 0.0000037980, 0.0000043410, 0.0000037740,
+                0.0000033662, 0.0000050175, 0.0000033958, 0.0000051015, 0.0000034086, 0.0000030335, 0.0000034235,
+                0.0000039828, 0.0000045344, 0.0000031386, 0.0000046458, 0.0000041624, 0.0000036086, 0.0000041947,
+                0.0000028209, 0.0000033460, 0.0000028613},
+        {0.0000305973, 0.0000272968, 0.0000142276, 0.0000179127, 0.0000097246, 0.0000127507, 0.0000070473,
+                0.0000090593, 0.0000092161, 0.0000074076, 0.0000074685, 0.0000058617, 0.0000047016, 0.0000051588,
+                0.0000056952, 0.0000035730, 0.0000038387, 0.0000042402, 0.0000035579, 0.0000018248, 0.0000046133,
+                0.0000039370, 0.0000045159, 0.0000029244, 0.0000019710, 0.0000022822, 0.0000025439, 0.0000028944,
+                0.0000025756, 0.0000029250, 0.0000034260, 0.0000050361, 0.0000034605, 0.0000038688, 0.0000026949,
+                0.0000014049, 0.0000027321, 0.0000023567, 0.0000027913, 0.0000031218, 0.0000027763, 0.0000031969,
+                0.0000037163, 0.0000032316, 0.0000037723},
+        {0.0000363524, 0.0000185238, 0.0000223944, 0.0000156641, 0.0000146928, 0.0000107770, 0.0000105276,
+                0.0000102180, 0.0000079337, 0.0000078580, 0.0000062276, 0.0000066384, 0.0000039738, 0.0000055727,
+                0.0000060414, 0.0000037461, 0.0000023444, 0.0000033734, 0.0000028739, 0.0000032262, 0.0000026725,
+                0.0000030603, 0.0000025932, 0.0000030096, 0.0000019791, 0.0000029386, 0.0000033334, 0.0000029324,
+                0.0000025033, 0.0000017645, 0.0000014959, 0.0000022899, 0.0000025847, 0.0000030273, 0.0000026349,
+                0.0000039712, 0.0000026279, 0.0000040671, 0.0000020776, 0.0000023832, 0.0000020939, 0.0000024085,
+                0.0000021149, 0.0000041807, 0.0000016785},
+        {0.0000531068, 0.0000206882, 0.0000259899, 0.0000178808, 0.0000168038, 0.0000128055, 0.0000119227,
+                0.0000069103, 0.0000090425, 0.0000068286, 0.0000069793, 0.0000031585, 0.0000042989, 0.0000027028,
+                0.0000036738, 0.0000031193, 0.0000043434, 0.0000035227, 0.0000030391, 0.0000024934, 0.0000027944,
+                0.0000024067, 0.0000027907, 0.0000023644, 0.0000026945, 0.0000022659, 0.0000020013, 0.0000029627,
+                0.0000026172, 0.0000017442, 0.0000025980, 0.0000017461, 0.0000020037, 0.0000017818, 0.0000026313,
+                0.0000023219, 0.0000026522, 0.0000023901, 0.0000026985, 0.0000014157, 0.0000027079, 0.0000018522,
+                0.0000016312, 0.0000019179, 0.0000016703},
+        {0.0000471466, 0.0000321678, 0.0000221166, 0.0000210526, 0.0000114910, 0.0000108287, 0.0000080777,
+                0.0000104203, 0.0000101134, 0.0000058443, 0.0000045067, 0.0000045975, 0.0000027454, 0.0000037782,
+                0.0000039918, 0.0000043093, 0.0000046405, 0.0000029403, 0.0000023896, 0.0000020792, 0.0000029414,
+                0.0000014845, 0.0000016390, 0.0000018155, 0.0000020600, 0.0000018173, 0.0000020305, 0.0000018045,
+                0.0000020275, 0.0000023056, 0.0000026380, 0.0000022906, 0.0000015633, 0.0000022861, 0.0000015702,
+                0.0000022925, 0.0000015926, 0.0000017741, 0.0000015991, 0.0000017969, 0.0000021445, 0.0000018491,
+                0.0000021663, 0.0000018937, 0.0000021829},
+        {0.0000415033, 0.0000375035, 0.0000257317, 0.0000181483, 0.0000170401, 0.0000165719, 0.0000091912,
+                0.0000089667, 0.0000066295, 0.0000051660, 0.0000051868, 0.0000051552, 0.0000041581, 0.0000041949,
+                0.0000044142, 0.0000035882, 0.0000029449, 0.0000031798, 0.0000020273, 0.0000021690, 0.0000024377,
+                0.0000026775, 0.0000029680, 0.0000014909, 0.0000021480, 0.0000018690, 0.0000027312, 0.0000014150,
+                0.0000020266, 0.0000013942, 0.0000015678, 0.0000014176, 0.0000015601, 0.0000018280, 0.0000020424,
+                0.0000023951, 0.0000015782, 0.0000024117, 0.0000016197, 0.0000014155, 0.0000012558, 0.0000018686,
+                0.0000016359, 0.0000018949, 0.0000010017},
+        {0.0000468296, 0.0000330068, 0.0000237247, 0.0000165336, 0.0000150520, 0.0000109676, 0.0000103584,
+                0.0000059806, 0.0000075019, 0.0000075639, 0.0000075577, 0.0000044171, 0.0000035406, 0.0000027896,
+                0.0000038483, 0.0000030165, 0.0000024502, 0.0000020229, 0.0000037725, 0.0000023248, 0.0000025725,
+                0.0000016526, 0.0000018416, 0.0000020307, 0.0000023292, 0.0000019462, 0.0000022344, 0.0000014452,
+                0.0000016743, 0.0000024751, 0.0000021503, 0.0000010914, 0.0000016308, 0.0000018570, 0.0000021221,
+                0.0000010982, 0.0000016425, 0.0000011133, 0.0000021459, 0.0000018960, 0.0000021557, 0.0000011360,
+                0.0000012755, 0.0000011503, 0.0000012923},
+        {0.0000326803, 0.0000295857, 0.0000154475, 0.0000195504, 0.0000133752, 0.0000130826, 0.0000093108,
+                0.0000090846, 0.0000051384, 0.0000065258, 0.0000049655, 0.0000050318, 0.0000039301, 0.0000031064,
+                0.0000018937, 0.0000033983, 0.0000027192, 0.0000021941, 0.0000023262, 0.0000011684, 0.0000015864,
+                0.0000017653, 0.0000019394, 0.0000021477, 0.0000018003, 0.0000015654, 0.0000017271, 0.0000019541,
+                0.0000012911, 0.0000008739, 0.0000016831, 0.0000011063, 0.0000016687, 0.0000014377, 0.0000016576,
+                0.0000010945, 0.0000012820, 0.0000010911, 0.0000007782, 0.0000014342, 0.0000013165, 0.0000018913,
+                0.0000013170, 0.0000008899, 0.0000013269},
+        {0.0000381583, 0.0000264565, 0.0000136612, 0.0000212466, 0.0000153348, 0.0000145316, 0.0000138739,
+                0.0000060648, 0.0000076347, 0.0000074893, 0.0000043282, 0.0000057183, 0.0000033840, 0.0000045556,
+                0.0000035441, 0.0000028461, 0.0000022853, 0.0000031916, 0.0000026005, 0.0000021084, 0.0000013651,
+                0.0000024956, 0.0000016110, 0.0000017750, 0.0000014880, 0.0000016813, 0.0000010862, 0.0000016184,
+                0.0000013616, 0.0000015542, 0.0000017206, 0.0000015449, 0.0000013086, 0.0000015137, 0.0000012941,
+                0.0000011585, 0.0000012793, 0.0000008977, 0.0000009980, 0.0000011586, 0.0000010254, 0.0000011655,
+                0.0000010218, 0.0000011671, 0.0000010320},
+        {0.0000435206, 0.0000394602, 0.0000280329, 0.0000143528, 0.0000103966, 0.0000099195, 0.0000123591,
+                0.0000070629, 0.0000087274, 0.0000066090, 0.0000048718, 0.0000038172, 0.0000050202, 0.0000029810,
+                0.0000040378, 0.0000024193, 0.0000024874, 0.0000015771, 0.0000016403, 0.0000017939, 0.0000019398,
+                0.0000012193, 0.0000017244, 0.0000008475, 0.0000009524, 0.0000008041, 0.0000011807, 0.0000009651,
+                0.0000011501, 0.0000012202, 0.0000010892, 0.0000011977, 0.0000010599, 0.0000015547, 0.0000010348,
+                0.0000011765, 0.0000010241, 0.0000009188, 0.0000007943, 0.0000009237, 0.0000010314, 0.0000009055,
+                0.0000010352, 0.0000009196, 0.0000013384},
+        {0.0000379248, 0.0000353934, 0.0000241508, 0.0000168891, 0.0000210529, 0.0000086678, 0.0000083510,
+                0.0000106765, 0.0000058348, 0.0000057662, 0.0000074621, 0.0000043406, 0.0000033715, 0.0000044195,
+                0.0000020552, 0.0000027516, 0.0000016638, 0.0000022213, 0.0000023581, 0.0000019527, 0.0000016003,
+                0.0000017368, 0.0000008516, 0.0000009200, 0.0000016828, 0.0000008623, 0.0000009286, 0.0000006197,
+                0.0000011498, 0.0000007848, 0.0000006628, 0.0000009788, 0.0000010822, 0.0000012385, 0.0000008152,
+                0.0000009435, 0.0000010686, 0.0000012014, 0.0000006276, 0.0000007030, 0.0000010605, 0.0000012119,
+                0.0000006389, 0.0000007195, 0.0000006456},
+        {0.0000457913, 0.0000308903, 0.0000282516, 0.0000197423, 0.0000139036, 0.0000076554, 0.0000101164,
+                0.0000092286, 0.0000068324, 0.0000052054, 0.0000050091, 0.0000049163, 0.0000039143, 0.0000029462,
+                0.0000039499, 0.0000031099, 0.0000024405, 0.0000014864, 0.0000012048, 0.0000021759, 0.0000013931,
+                0.0000014556, 0.0000009208, 0.0000017108, 0.0000011004, 0.0000011923, 0.0000007721, 0.0000006708,
+                0.0000005731, 0.0000008092, 0.0000009072, 0.0000007914, 0.0000008763, 0.0000007716, 0.0000006536,
+                0.0000007653, 0.0000008561, 0.0000009956, 0.0000008472, 0.0000007539, 0.0000008319, 0.0000005765,
+                0.0000008451, 0.0000009713, 0.0000008480},
+        {0.0000514751, 0.0000274557, 0.0000241481, 0.0000174373, 0.0000161298, 0.0000116871, 0.0000084217,
+                0.0000081004, 0.0000058484, 0.0000056938, 0.0000057532, 0.0000043019, 0.0000032347, 0.0000025748,
+                0.0000034234, 0.0000026896, 0.0000016029, 0.0000016452, 0.0000023736, 0.0000013932, 0.0000011273,
+                0.0000009500, 0.0000013052, 0.0000010750, 0.0000012044, 0.0000010077, 0.0000011046, 0.0000007011,
+                0.0000010268, 0.0000008714, 0.0000012822, 0.0000010702, 0.0000007283, 0.0000010366, 0.0000005479,
+                0.0000007812, 0.0000005332, 0.0000007670, 0.0000006899, 0.0000007700, 0.0000006826, 0.0000007637,
+                0.0000008807, 0.0000004626, 0.0000006769},
+        {0.0000347279, 0.0000326038, 0.0000222389, 0.0000203032, 0.0000186389, 0.0000135396, 0.0000074878,
+                0.0000093982, 0.0000069447, 0.0000051305, 0.0000038953, 0.0000049720, 0.0000038543, 0.0000038303,
+                0.0000017387, 0.0000013908, 0.0000018492, 0.0000018676, 0.0000011554, 0.0000016034, 0.0000009913,
+                0.0000010369, 0.0000011030, 0.0000015670, 0.0000013066, 0.0000011031, 0.0000009195, 0.0000007778,
+                0.0000010981, 0.0000009562, 0.0000010346, 0.0000011776, 0.0000007583, 0.0000005111, 0.0000005726,
+                0.0000011004, 0.0000009317, 0.0000004887, 0.0000005500, 0.0000008051, 0.0000005530, 0.0000004756,
+                0.0000005513, 0.0000006031, 0.0000005515},
+        {0.0000404627, 0.0000279374, 0.0000254737, 0.0000173416, 0.0000166276, 0.0000090743, 0.0000084501,
+                0.0000108659, 0.0000080225, 0.0000045088, 0.0000044169, 0.0000057245, 0.0000033186, 0.0000043660,
+                0.0000034171, 0.0000015607, 0.0000020522, 0.0000012726, 0.0000016999, 0.0000013417, 0.0000018688,
+                0.0000015245, 0.0000009452, 0.0000007794, 0.0000008405, 0.0000011825, 0.0000007598, 0.0000008210,
+                0.0000007108, 0.0000007647, 0.0000006612, 0.0000007339, 0.0000008145, 0.0000007040, 0.0000006095,
+                0.0000008740, 0.0000003513, 0.0000006717, 0.0000005660, 0.0000004997, 0.0000009522, 0.0000005060,
+                0.0000005619, 0.0000005063, 0.0000004323},
+        {0.0000472063, 0.0000323179, 0.0000294752, 0.0000207416, 0.0000113045, 0.0000136112, 0.0000129076,
+                0.0000071735, 0.0000091135, 0.0000052733, 0.0000039191, 0.0000038507, 0.0000022739, 0.0000029313,
+                0.0000017476, 0.0000029817, 0.0000017682, 0.0000014044, 0.0000018903, 0.0000011418, 0.0000015945,
+                0.0000009870, 0.0000010526, 0.0000011160, 0.0000011997, 0.0000013110, 0.0000010830, 0.0000006980,
+                0.0000009891, 0.0000006386, 0.0000005495, 0.0000006040, 0.0000006782, 0.0000004347, 0.0000004971,
+                0.0000004232, 0.0000004838, 0.0000006935, 0.0000003617, 0.0000004015, 0.0000003611, 0.0000003155,
+                0.0000005902, 0.0000003968, 0.0000004545},
+        {0.0000534708, 0.0000373744, 0.0000196796, 0.0000137544, 0.0000129458, 0.0000119235, 0.0000088305,
+                0.0000111632, 0.0000062367, 0.0000059625, 0.0000034417, 0.0000044561, 0.0000033301, 0.0000025232,
+                0.0000019586, 0.0000027071, 0.0000015804, 0.0000012263, 0.0000016555, 0.0000017219, 0.0000010458,
+                0.0000008561, 0.0000006975, 0.0000009600, 0.0000013346, 0.0000008501, 0.0000004210, 0.0000005776,
+                0.0000008267, 0.0000009169, 0.0000004551, 0.0000005061, 0.0000007113, 0.0000006128, 0.0000006790,
+                0.0000004531, 0.0000005155, 0.0000005740, 0.0000006449, 0.0000002634, 0.0000004837, 0.0000005547,
+                0.0000004790, 0.0000004165, 0.0000004781},
+        {0.0000471509, 0.0000331027, 0.0000226179, 0.0000161311, 0.0000144893, 0.0000107687, 0.0000104009,
+                0.0000128476, 0.0000071831, 0.0000053734, 0.0000030440, 0.0000051224, 0.0000039650, 0.0000022452,
+                0.0000017674, 0.0000017430, 0.0000017813, 0.0000014173, 0.0000018887, 0.0000008757, 0.0000012001,
+                0.0000016279, 0.0000012946, 0.0000004971, 0.0000008642, 0.0000005591, 0.0000006041, 0.0000006461,
+                0.0000005270, 0.0000007557, 0.0000004882, 0.0000007062, 0.0000005940, 0.0000008734, 0.0000005615,
+                0.0000004848, 0.0000004202, 0.0000004705, 0.0000004021, 0.0000004565, 0.0000003960, 0.0000004488,
+                0.0000004998, 0.0000005792, 0.0000003821},
+        {0.0000408712, 0.0000290825, 0.0000201809, 0.0000184693, 0.0000127543, 0.0000122856, 0.0000089579,
+                0.0000087349, 0.0000048255, 0.0000062188, 0.0000059289, 0.0000044984, 0.0000019907, 0.0000033642,
+                0.0000034197, 0.0000026227, 0.0000012039, 0.0000015836, 0.0000009945, 0.0000013051, 0.0000017849,
+                0.0000010646, 0.0000005240, 0.0000007062, 0.0000005767, 0.0000006087, 0.0000008726, 0.0000005467,
+                0.0000007758, 0.0000006348, 0.0000007039, 0.0000003514, 0.0000006567, 0.0000002671, 0.0000006217,
+                0.0000006907, 0.0000005847, 0.0000001470, 0.0000004367, 0.0000003746, 0.0000003283, 0.0000003639,
+                0.0000004146, 0.0000003630, 0.0000004069},
+        {0.0000365867, 0.0000334092, 0.0000233935, 0.0000211266, 0.0000148841, 0.0000145388, 0.0000102065,
+                0.0000099718, 0.0000095120, 0.0000041586, 0.0000052071, 0.0000030423, 0.0000030232, 0.0000022968,
+                0.0000023000, 0.0000030274, 0.0000022943, 0.0000010722, 0.0000011058, 0.0000014789, 0.0000009014,
+                0.0000009248, 0.0000009830, 0.0000010354, 0.0000014149, 0.0000011942, 0.0000005606, 0.0000006027,
+                0.0000005065, 0.0000004217, 0.0000005883, 0.0000005132, 0.0000003302, 0.0000004589, 0.0000006648,
+                0.0000003397, 0.0000004773, 0.0000003190, 0.0000004559, 0.0000003103, 0.0000005849, 0.0000003016,
+                0.0000005635, 0.0000003806, 0.0000002617},
+        {0.0000424740, 0.0000299616, 0.0000274655, 0.0000188374, 0.0000175698, 0.0000126091, 0.0000089948,
+                0.0000068101, 0.0000083814, 0.0000048968, 0.0000060247, 0.0000059532, 0.0000020417, 0.0000044737,
+                0.0000025919, 0.0000020128, 0.0000020369, 0.0000012489, 0.0000016173, 0.0000012975, 0.0000013224,
+                0.0000010382, 0.0000008390, 0.0000008790, 0.0000009452, 0.0000009921, 0.0000006307, 0.0000006689,
+                0.0000003338, 0.0000007868, 0.0000005076, 0.0000004219, 0.0000003654, 0.0000008632, 0.0000004278,
+                0.0000003612, 0.0000002445, 0.0000005864, 0.0000002315, 0.0000004357, 0.0000002247, 0.0000004156,
+                0.0000002193, 0.0000003178, 0.0000002730},
+        {0.0000503599, 0.0000451538, 0.0000316354, 0.0000217532, 0.0000202497, 0.0000144337, 0.0000104958,
+                0.0000100705, 0.0000074034, 0.0000054909, 0.0000041189, 0.0000040598, 0.0000030289, 0.0000030085,
+                0.0000029895, 0.0000023332, 0.0000018003, 0.0000013931, 0.0000018829, 0.0000014630, 0.0000006967,
+                0.0000009205, 0.0000012416, 0.0000007812, 0.0000010583, 0.0000006536, 0.0000009038, 0.0000004477,
+                0.0000010522, 0.0000006597, 0.0000005567, 0.0000004616, 0.0000006575, 0.0000003344, 0.0000002766,
+                0.0000003963, 0.0000005688, 0.0000001793, 0.0000004153, 0.0000004620, 0.0000003992, 0.0000004404,
+                0.0000002976, 0.0000004364, 0.0000003740},
+        {0.0000430595, 0.0000397799, 0.0000271616, 0.0000143472, 0.0000176142, 0.0000168235, 0.0000093500,
+                0.0000067329, 0.0000066446, 0.0000082213, 0.0000061390, 0.0000046372, 0.0000034811, 0.0000034735,
+                0.0000026730, 0.0000026634, 0.0000015581, 0.0000021099, 0.0000012381, 0.0000016679, 0.0000010186,
+                0.0000010483, 0.0000008241, 0.0000011290, 0.0000005475, 0.0000005724, 0.0000004661, 0.0000006409,
+                0.0000004075, 0.0000005660, 0.0000004676, 0.0000006678, 0.0000005536, 0.0000004588, 0.0000003086,
+                0.0000002600, 0.0000002221, 0.0000004089, 0.0000005910, 0.0000002319, 0.0000003286, 0.0000002849,
+                0.0000003188, 0.0000001718, 0.0000003117},
+        {0.0000503956, 0.0000262755, 0.0000184690, 0.0000169051, 0.0000157560, 0.0000113206, 0.0000108187,
+                0.0000079141, 0.0000075536, 0.0000055140, 0.0000054540, 0.0000040614, 0.0000039965, 0.0000023509,
+                0.0000030468, 0.0000018116, 0.0000023451, 0.0000013890, 0.0000014094, 0.0000011387, 0.0000011426,
+                0.0000007091, 0.0000009704, 0.0000007647, 0.0000008029, 0.0000011008, 0.0000003155, 0.0000004312,
+                0.0000004596, 0.0000004817, 0.0000005269, 0.0000005676, 0.0000003706, 0.0000004006, 0.0000004315,
+                0.0000004775, 0.0000003098, 0.0000003407, 0.0000004886, 0.0000004152, 0.0000001682, 0.0000003093,
+                0.0000002068, 0.0000002292, 0.0000002008},
+        {0.0000334173, 0.0000309726, 0.0000218877, 0.0000196442, 0.0000138546, 0.0000130794, 0.0000094927,
+                0.0000068714, 0.0000086744, 0.0000063898, 0.0000037817, 0.0000048166, 0.0000046345, 0.0000026610,
+                0.0000015855, 0.0000020759, 0.0000027141, 0.0000020849, 0.0000016433, 0.0000012979, 0.0000007813,
+                0.0000010596, 0.0000004958, 0.0000008546, 0.0000008928, 0.0000005593, 0.0000009962, 0.0000006205,
+                0.0000006694, 0.0000004270, 0.0000004437, 0.0000003712, 0.0000005299, 0.0000003383, 0.0000004823,
+                0.0000002425, 0.0000004443, 0.0000004843, 0.0000004110, 0.0000001667, 0.0000001831, 0.0000001992,
+                0.0000002251, 0.0000003165, 0.0000001686},
+        {0.0000389225, 0.0000356628, 0.0000246098, 0.0000234810, 0.0000158826, 0.0000154517, 0.0000109594,
+                0.0000061715, 0.0000077203, 0.0000057494, 0.0000072006, 0.0000031528, 0.0000032123, 0.0000024479,
+                0.0000023896, 0.0000023499, 0.0000018589, 0.0000010985, 0.0000014288, 0.0000011269, 0.0000015138,
+                0.0000011720, 0.0000009382, 0.0000009879, 0.0000006065, 0.0000008389, 0.0000005190, 0.0000007012,
+                0.0000005724, 0.0000004774, 0.0000003925, 0.0000005365, 0.0000002703, 0.0000004855, 0.0000004093,
+                0.0000003496, 0.0000003709, 0.0000002481, 0.0000002089, 0.0000001810, 0.0000001978, 0.0000001719,
+                0.0000003092, 0.0000001622, 0.0000002294},
+        {0.0000337803, 0.0000404714, 0.0000284070, 0.0000198125, 0.0000183754, 0.0000174965, 0.0000126377,
+                0.0000121499, 0.0000086656, 0.0000051637, 0.0000063642, 0.0000036800, 0.0000027442, 0.0000046261,
+                0.0000027739, 0.0000020991, 0.0000027411, 0.0000015953, 0.0000016454, 0.0000010057, 0.0000013415,
+                0.0000010441, 0.0000008211, 0.0000011139, 0.0000004134, 0.0000004295, 0.0000005796, 0.0000003604,
+                0.0000004939, 0.0000003153, 0.0000004360, 0.0000004611, 0.0000003900, 0.0000005448, 0.0000003467,
+                0.0000004915, 0.0000002450, 0.0000002082, 0.0000002350, 0.0000002486, 0.0000002168, 0.0000002377,
+                0.0000001597, 0.0000002236, 0.0000001948},
+        {0.0000513067, 0.0000471119, 0.0000328458, 0.0000132586, 0.0000211621, 0.0000154592, 0.0000085830,
+                0.0000106377, 0.0000060140, 0.0000058815, 0.0000073820, 0.0000042029, 0.0000054567, 0.0000041212,
+                0.0000018646, 0.0000023994, 0.0000014275, 0.0000018493, 0.0000011151, 0.0000011543, 0.0000009245,
+                0.0000012066, 0.0000007246, 0.0000007519, 0.0000004803, 0.0000008191, 0.0000005011, 0.0000004138,
+                0.0000004368, 0.0000003569, 0.0000006387, 0.0000005261, 0.0000004315, 0.0000002781, 0.0000003871,
+                0.0000001946, 0.0000003503, 0.0000002314, 0.0000002482, 0.0000002172, 0.0000003055, 0.0000001223,
+                0.0000002858, 0.0000001905, 0.0000002117},
+        {0.0000450816, 0.0000408154, 0.0000282845, 0.0000155178, 0.0000138761, 0.0000175937, 0.0000098262,
+                0.0000121258, 0.0000069414, 0.0000066229, 0.0000029233, 0.0000048194, 0.0000037116, 0.0000036148,
+                0.0000027563, 0.0000016149, 0.0000009741, 0.0000012561, 0.0000012827, 0.0000016831, 0.0000010254,
+                0.0000010378, 0.0000014009, 0.0000008403, 0.0000008877, 0.0000009243, 0.0000009675, 0.0000003626,
+                0.0000004818, 0.0000003920, 0.0000003302, 0.0000004548, 0.0000002250, 0.0000003083, 0.0000003321,
+                0.0000003601, 0.0000001838, 0.0000002508, 0.0000001319, 0.0000003035, 0.0000000966, 0.0000001703,
+                0.0000002389, 0.0000002086, 0.0000000873},
+        {0.0000516615, 0.0000359046, 0.0000250255, 0.0000176967, 0.0000162043, 0.0000117023, 0.0000111500,
+                0.0000083372, 0.0000080845, 0.0000057760, 0.0000057683, 0.0000042198, 0.0000042307, 0.0000031731,
+                0.0000018613, 0.0000018786, 0.0000018362, 0.0000018880, 0.0000011419, 0.0000019392, 0.0000015208,
+                0.0000005532, 0.0000005717, 0.0000005781, 0.0000007821, 0.0000004847, 0.0000005016, 0.0000004026,
+                0.0000005570, 0.0000007553, 0.0000003651, 0.0000002375, 0.0000004137, 0.0000004454, 0.0000003721,
+                0.0000003077, 0.0000002562, 0.0000001692, 0.0000001437, 0.0000003380, 0.0000001705, 0.0000002396,
+                0.0000001596, 0.0000002284, 0.0000001911},
+        {0.0000345441, 0.0000316241, 0.0000286584, 0.0000206648, 0.0000186795, 0.0000136872, 0.0000097047,
+                0.0000096665, 0.0000091419, 0.0000067531, 0.0000038985, 0.0000048445, 0.0000028978, 0.0000037146,
+                0.0000021550, 0.0000021547, 0.0000016273, 0.0000009960, 0.0000012967, 0.0000013308, 0.0000008052,
+                0.0000010640, 0.0000008348, 0.0000008528, 0.0000008847, 0.0000003357, 0.0000004374, 0.0000005905,
+                0.0000002889, 0.0000005062, 0.0000005427, 0.0000003357, 0.0000003624, 0.0000002976, 0.0000004179,
+                0.0000002635, 0.0000003711, 0.0000001854, 0.0000003373, 0.0000001698, 0.0000002400, 0.0000001599,
+                0.0000001739, 0.0000001177, 0.0000002124},
+        {0.0000408929, 0.0000365974, 0.0000338537, 0.0000239837, 0.0000219789, 0.0000159079, 0.0000148083,
+                0.0000064093, 0.0000047381, 0.0000060372, 0.0000058068, 0.0000056862, 0.0000032968, 0.0000024911,
+                0.0000024593, 0.0000019073, 0.0000019128, 0.0000019005, 0.0000014645, 0.0000009027, 0.0000008974,
+                0.0000012109, 0.0000007360, 0.0000005796, 0.0000007748, 0.0000003802, 0.0000005085, 0.0000004071,
+                0.0000004279, 0.0000004439, 0.0000003642, 0.0000002963, 0.0000003129, 0.0000003366, 0.0000003574,
+                0.0000001793, 0.0000001519, 0.0000002667, 0.0000001760, 0.0000002482, 0.0000001261, 0.0000002263,
+                0.0000001493, 0.0000002143, 0.0000000869},
+        {0.0000460894, 0.0000423271, 0.0000297879, 0.0000205531, 0.0000193030, 0.0000178901, 0.0000130914,
+                0.0000096919, 0.0000091541, 0.0000052562, 0.0000039234, 0.0000038485, 0.0000029218, 0.0000028777,
+                0.0000021609, 0.0000021577, 0.0000022213, 0.0000012807, 0.0000010154, 0.0000007967, 0.0000006264,
+                0.0000008077, 0.0000006542, 0.0000006707, 0.0000006786, 0.0000005617, 0.0000004357, 0.0000005927,
+                0.0000004775, 0.0000003001, 0.0000003137, 0.0000002632, 0.0000002739, 0.0000002881, 0.0000003141,
+                0.0000002009, 0.0000002152, 0.0000003873, 0.0000001959, 0.0000001287, 0.0000002305, 0.0000001938,
+                0.0000002115, 0.0000003004, 0.0000001533}
+};
+
+const static float Bij[AmoebaGeneralizedKirkwoodForceImpl::NUM_NECK_RADII][AmoebaGeneralizedKirkwoodForceImpl::NUM_NECK_RADII] = {
+        {-1.20, -1.05, -1.30, -1.15, -1.00, -0.85, -0.90, -0.75, -1.20, -1.05, -0.90, -0.95, -0.80, -0.65, -0.70,
+            -0.75, -0.60, -0.45, -0.50, -0.75, -0.40, -0.25, -0.30, -0.15, -0.20, -0.25, 0.10,  0.05, 0.20,-0.05,
+            0.30, 0.25, 0.40, 0.15,  0.10, 0.25, 0.60, 0.55, 0.70, 0.45, 0.80, 0.55, 0.90, 0.65, 1.00},
+        {-1.25, -1.10, -0.95, -1.40, -1.05, -1.10, -0.95, -0.80, -0.85, -0.70, -0.55, -1.00, -0.65, -0.70, -0.95,
+            -0.60, -0.65, -0.70, -0.55, -0.60, -0.45, -0.10, -0.15, 0.00,  -0.05, 0.10,  0.05,  0.20, 0.15, 0.10,
+            0.25, 0.00, 0.15, -0.10, 0.25, 0.20, 0.35, 0.50, 0.65, 0.40, 0.75, 0.70, 0.85, 0.60, 0.95},
+        {-1.30, -1.15, -1.00, -1.05, -0.90, -0.95, -1.20, -0.85, -0.90, -0.95, -0.80, -0.65, -0.70, -0.75, -0.40,
+            -0.65, -0.30, -0.35, -0.60, -0.45, -0.50, -0.35, -0.20, -0.25, -0.10, 0.05,  0.00,  0.15, -0.10,0.25,
+            0.00, 0.35, 0.10, 0.45,  0.40, 0.55, 0.30, 0.65, 0.60, 0.75, 0.50, 0.85, 0.80, 0.95, 0.70},
+        {-1.15, -1.40, -1.05, -0.90, -1.15, -0.80, -1.05, -0.90, -0.95, -0.60, -0.85, -0.70, -0.75, -0.40, -0.45,
+            -0.30, -0.35, -0.40, -0.25, -0.30, -0.15, -0.40, -0.05, -0.30, -0.15, -0.20, -0.05, 0.10, 0.25,0.00,
+            0.35, 0.10, 0.25, 0.20,  0.35, 0.30, 0.45, 0.40, 0.75, 0.50, 0.65, 0.80, 0.75, 0.90, 0.65},
+        {-1.00, -1.25, -0.90, -1.15, -1.00, -0.85, -0.90, -0.75, -0.60, -0.85, -0.50, -0.95, -0.80, -0.65, -0.70,
+            -0.55, -0.60, -0.25, -0.50, -0.35, -0.40, -0.05, -0.10, 0.05,  -0.20, 0.15,  -0.10, 0.05, 0.00,0.15,
+            0.10, 0.25, 0.40, 0.35,  0.50, 0.45, 0.60, 0.55, 0.70, 0.45, 0.80, 0.75, 0.90, 0.65, 1.00},
+        {-0.85, -1.10, -1.15, -1.00, -0.85, -0.90, -0.95, -0.80, -0.65, -0.50, -0.55, -0.60, -0.65, -0.50, -0.55,
+            -0.40, -0.25, -0.50, -0.15, -0.60, -0.05, -0.30, -0.15, 0.00,  -0.05, 0.10,  0.05,  0.20, 0.15, 0.30,
+            0.05, 0.40, 0.35, 0.50,  0.45, 0.60, 0.55, 0.70, 0.45, 0.80, 0.75, 0.90, 0.85, 1.00, 0.75},
+        {-0.90, -1.15, -1.00, -1.05, -0.90, -0.55, -0.60, -0.45, -0.70, -0.55, -0.60, -0.65, -0.50, -0.35, -0.40,
+            -0.25, -0.50, -0.35, -0.20, -0.05, -0.10, 0.05,  0.00,  0.15,  0.30,  0.25,  0.40,  0.15, 0.30, -0.15,
+            0.40, 0.55, 0.50, 0.25,  0.60, 0.55, 0.90, 0.45, 0.60, 0.75, 0.70, 0.85, 1.00, 0.95, 0.90},
+        {-1.15, -1.00, -1.25, -0.90, -0.75, -0.80, -0.45, -0.50, -0.95, -0.40, -0.45, -0.70, -0.55, -0.40, -0.45,
+            -0.30, -0.15, -0.20, -0.45, -0.10, -0.15, -0.20, -0.05, 0.10,  0.05,  0.20,  0.15,  0.10, 0.05, 0.40,
+            0.35, 0.50, 0.45, 0.20,  0.55, 0.50, 0.45, 0.20, 0.75, 0.70, 0.85, 0.80, 0.95, 1.10, 1.05},
+        {-1.00, -0.85, -0.90, -0.75, -0.80, -0.85, -0.90, -0.55, -0.60, -0.45, -0.50, -0.35, -0.40, -0.65, -0.30,
+            -0.35, 0.00,  -0.25, 0.10,  -0.35, 0.00,  -0.05, -0.10, 0.05,  0.00,  0.15,  0.10,  0.25, 0.20, 0.15,
+            0.30, 0.45, 0.00, 0.55,  0.70, 0.65, 0.80, 0.75, 0.90, 1.05, 1.00, 1.15, 1.10, 1.25, 1.00},
+        {-1.05, -1.10, -0.75, -0.60, -0.65, -0.70, -0.95, -0.60, -0.45, -0.70, -0.55, -0.20, -0.45, -0.30, -0.35,
+            -0.20, -0.25, -0.10, 0.05,  0.00,  -0.05, 0.30,  0.25,  0.00,  0.15,  0.10,  0.25,  0.20, 0.55, 0.50,
+            0.45, 0.20, 0.75, 0.30,  0.85, 0.60, 0.55, 0.70, 0.85, 0.80, 0.75, 0.90, 1.05, 1.00, 1.35},
+        {-1.10, -0.75, -0.80, -0.65, -0.50, -0.75, -0.60, -0.45, -0.70, -0.55, -0.40, -0.65, -0.30, -0.15, -0.20,
+            -0.25, -0.30, -0.15, -0.20, -0.05, -0.10, 0.05,  0.20,  0.15,  0.30,  0.05,  0.20,  0.55, 0.30, 0.45,
+            0.40, 0.55, 0.50, 0.65,  0.60, 0.55, 0.90, 0.65, 1.00, 0.95, 0.90, 0.65, 1.00, 0.75, 1.10},
+        {-0.75, -1.00, -0.65, -0.50, -0.75, -0.60, -0.65, -0.30, -0.35, -0.20, -0.65, -0.50, -0.55, -0.20, -0.05,
+            0.10,  -0.15, -0.20, -0.25, -0.10, -0.15, 0.00,  0.15,  0.10,  0.25,  0.20,  0.35,  0.30, 0.25, 0.40,
+            0.55, 0.70, 0.45, 0.60,  0.55, 0.90, 0.85, 1.00, 0.75, 1.10, 1.05, 1.20, 1.15, 1.30, 1.05},
+        {-0.80, -0.65, -0.70, -0.75, -0.60, -0.65, -0.50, -0.55, -0.40, -0.65, -0.30, -0.55, -0.40, -0.45,-0.10,
+            -0.35, 0.00,  0.15,  0.10,  0.05,  -0.20, 0.35,  -0.10, 0.05,  0.20,  0.35,  0.50,  0.45, 0.40,0.75,
+            0.50, 0.85, 0.60, 0.55,  0.70, 0.85, 1.00, 0.75, 1.10, 1.05, 1.00, 1.15, 0.90, 1.05, 1.00},
+        {-0.85, -0.70, -0.95, -0.60, -0.85, -0.50, -0.75, -0.40, -0.25, -0.30, -0.15, -0.20, -0.25, -0.10, 0.05,
+            -0.20, -0.05, 0.10,  0.05,  -0.40, 0.35,  0.30,  0.45,  0.20,  -0.05, 0.10,  0.25,  0.40, 0.35, 0.50,
+            0.65, 1.00, 0.75, 0.90,  0.65, 0.20, 0.75, 0.70, 0.85, 1.00, 0.95, 1.10, 1.25, 1.20, 1.35},
+        {-0.70, -0.95, -0.60, -0.65, -0.50, -0.55, -0.40, -0.25, -0.30, -0.15, -0.20, -0.05, -0.30, 0.05,  0.20,
+            -0.05, -0.30, 0.05,  0.00,  0.15,  0.10,  0.25,  0.20,  0.35,  0.10,  0.45,  0.60,  0.55, 0.50, 0.25,
+            0.20,0.55, 0.70, 0.85,  0.80, 1.15, 0.90, 1.25, 0.80, 0.95, 0.90, 1.05, 1.00, 1.55, 0.90},
+        {-0.35, -0.80, -0.45, -0.50, -0.35, -0.40, -0.25, -0.50, -0.15, -0.20, -0.05, -0.50, -0.15, -0.40, -0.05,
+            -0.10, 0.25,  0.20,  0.15,  0.10,  0.25,  0.20,  0.35,  0.30,  0.45,  0.40,  0.35,  0.70, 0.65, 0.40,
+            0.75,0.50, 0.65, 0.60,  0.95, 0.90, 1.05, 1.00, 1.15, 0.70, 1.25, 1.00, 0.95, 1.10, 1.05},
+        {-0.40, -0.45, -0.50, -0.35, -0.60, -0.45, -0.50, -0.15, 0.00,  -0.25, -0.30, -0.15, -0.40, -0.05, 0.10,
+            0.25,  0.40,  0.15,  0.10,  0.05,  0.40,  -0.05, 0.10,  0.25,  0.40,  0.35,  0.50,  0.45, 0.60, 0.75,
+            0.90,0.85, 0.60, 0.95,  0.70, 1.05, 0.80, 0.95, 0.90, 1.05, 1.20, 1.15, 1.30, 1.25, 1.40},
+        {-0.45, -0.30, -0.35, -0.40, -0.25, -0.10, -0.35, -0.20, -0.25, -0.30, -0.15, 0.00,  -0.05, 0.10,  0.25,
+            0.20,  0.15,  0.30,  0.05,  0.20,  0.35,  0.50,  0.65,  0.20,  0.55,  0.50,  0.85,  0.40, 0.75, 0.50,
+            0.65,0.60, 0.75, 0.90,  1.05, 1.20, 0.95, 1.30, 1.05, 1.00, 0.95, 1.30, 1.25, 1.40, 0.95},
+        {-0.30, -0.35, -0.40, -0.45, -0.30, -0.35, -0.20, -0.45, -0.10, 0.05,  0.20,  -0.05, -0.10, -0.15, 0.20,
+            0.15,  0.10,  0.05,  0.60,  0.35,  0.50,  0.25,  0.40,  0.55,  0.70,  0.65,  0.80,  0.55, 0.70, 1.05,
+            1.00,0.55, 0.90, 1.05,  1.20, 0.75, 1.10, 0.85, 1.40, 1.35, 1.50, 1.05, 1.20, 1.15, 1.30},
+        {-0.55, -0.40, -0.65, -0.30, -0.35, -0.20, -0.25, -0.10, -0.35, 0.00,  -0.05, 0.10,  0.05,  0.00,  -0.25,
+            0.30,0.25,  0.20,  0.35,  -0.10, 0.25,  0.40,  0.55,  0.70,  0.65,  0.60,  0.75,  0.90, 0.65, 0.40,
+            0.95,0.70, 1.05, 1.00,  1.15, 0.90, 1.05, 1.00, 0.75, 1.30, 1.25, 1.60, 1.35, 1.10, 1.45},
+        {-0.40, -0.45, -0.70, -0.15, -0.20, -0.05, 0.10,  -0.35, 0.00,  0.15,  -0.10, 0.25,  0.00,  0.35,  0.30,
+            0.25,0.20,  0.55,  0.50,  0.45,  0.20,  0.75,  0.50,  0.65,  0.60,  0.75,  0.50,  0.85, 0.80, 0.95,
+            1.10, 1.05,1.00, 1.15,  1.10, 1.05, 1.20, 0.95, 1.10, 1.25, 1.20, 1.35, 1.30, 1.45, 1.40},
+        {-0.25, -0.10, -0.15, -0.40, -0.45, -0.30, 0.05,  -0.20, 0.15,  0.10,  0.05,  0.00,  0.35,  0.10,  0.45,
+            0.20,0.35,0.10,  0.25,  0.40,  0.55,  0.30,  0.65,  0.20,  0.35,  0.30,  0.65,  0.60, 0.75, 0.90,
+            0.85, 1.00,0.95, 1.30,  1.05, 1.20, 1.15, 1.10, 1.05, 1.20, 1.35, 1.30, 1.45, 1.40, 1.75},
+        {-0.30, -0.15, -0.20, -0.25, 0.10,  -0.35, -0.20, 0.15,  -0.10, 0.05,  0.40,  0.15,  0.10,  0.45,  0.00,
+            0.35,0.10,  0.45,  0.60,  0.55,  0.50,  0.65,  0.20,  0.35,  0.90,  0.45,  0.60,  0.35, 0.90, 0.65,
+            0.60, 0.95,1.10, 1.25,  1.00, 1.15, 1.30, 1.45, 1.00, 1.15, 1.50, 1.65, 1.20, 1.35, 1.30},
+        {-0.15, -0.20, -0.05, -0.10, -0.15, -0.40, -0.05, 0.10,  0.05,  0.00,  0.15,  0.30,  0.25,  0.20,  0.55,
+            0.50,0.45,  0.20,  0.15,  0.70,  0.45,  0.60,  0.35,  0.90,  0.65,  0.80,  0.55,  0.50, 0.45, 0.80,
+            0.95, 0.90,1.05, 1.00,  0.95, 1.10, 1.25, 1.40, 1.35, 1.30, 1.45, 1.20, 1.55, 1.70, 1.65},
+        {0.00,  -0.25, -0.10, -0.15, 0.00,  -0.05, -0.10, 0.05,  0.00,  0.15,  0.30,  0.25,  0.20,  0.15,  0.50,
+            0.45,0.20,  0.35,  0.70,  0.45,  0.40,  0.35,  0.70,  0.65,  0.80,  0.75,  0.90,  0.65, 1.00, 0.95,
+            1.30, 1.25,1.00, 1.35,  0.90, 1.25, 1.00, 1.35, 1.30, 1.45, 1.40, 1.55, 1.70, 1.25, 1.60},
+        {-0.25, -0.10, -0.15, 0.00,  0.15,  0.10,  -0.15, 0.20,  0.15,  0.10,  0.05,  0.40,  0.35,  0.50,  0.05,
+            0.00,0.35,  0.50,  0.25,  0.60,  0.35,  0.50,  0.65,  1.00,  0.95,  0.90,  0.85,  0.80, 1.15, 1.10,
+            1.25, 1.40,1.15, 0.90,  1.05, 1.60, 1.55, 1.10, 1.25, 1.60, 1.35, 1.30, 1.45, 1.60, 1.55},
+        {-0.10, -0.15, 0.00,  -0.05, 0.10,  -0.15, 0.00,  0.35,  0.30,  0.05,  0.20,  0.55,  0.30,  0.65,  0.60,
+            0.15,  0.50,0.25,  0.60,  0.55,  0.90,  0.85,  0.60,  0.55,  0.70,  1.05,  0.80,  0.95, 0.90, 1.05,
+            1.00, 1.15, 1.30,1.25,  1.20, 1.55, 0.90, 1.45, 1.40, 1.35, 1.90, 1.45, 1.60, 1.55, 1.50},
+        {0.05,  0.00,  0.15,  0.10,  -0.15, 0.20,  0.35,  0.10,  0.45,  0.20,  0.15,  0.30,  0.05,  0.40,  0.15,
+            0.70,  0.45,0.40,  0.75,  0.50,  0.85,  0.60,  0.75,  0.90,  1.05,  1.20,  1.15,  0.90, 1.25, 1.00,
+            0.95, 1.10, 1.25,1.00,  1.15, 1.10, 1.25, 1.60, 1.15, 1.30, 1.25, 1.20, 1.75, 1.50, 1.65},
+        {0.20,  0.15,  -0.10, -0.15, 0.00,  0.15,  0.10,  0.45,  0.20,  0.35,  0.10,  0.45,  0.40,  0.35,  0.30,
+            0.65,  0.40,0.35,  0.70,  0.85,  0.60,  0.55,  0.50,  0.85,  1.20,  0.95,  0.50,  0.85, 1.20, 1.35,
+            0.90, 1.05, 1.40,1.35,  1.50, 1.25, 1.40, 1.55, 1.70, 1.05, 1.60, 1.75, 1.70, 1.65, 1.80},
+        {0.15,  0.10,  0.05,  0.00,  0.15,  0.10,  0.25,  0.60,  0.35,  0.30,  0.05,  0.60,  0.55,  0.30,  0.25,
+            0.40,  0.55,0.50,  0.85,  0.40,  0.75,  1.10,  1.05,  0.40,  0.95,  0.70,  0.85,  1.00, 0.95, 1.30,
+            1.05, 1.40, 1.35,1.70,  1.45, 1.40, 1.35, 1.50, 1.45, 1.60, 1.55, 1.70, 1.85, 2.00, 1.75},
+        {0.10,  0.05,  0.00,  0.15,  0.10,  0.25,  0.20,  0.35,  0.10,  0.45,  0.60,  0.55,  0.10,  0.65,  0.80,
+            0.75,  0.30,0.65,  0.40,  0.75,  1.10,  0.85,  0.40,  0.75,  0.70,  0.85,  1.20,  0.95, 1.30, 1.25,
+            1.40, 0.95, 1.50,0.85,  1.60, 1.75, 1.70, 0.65, 1.60, 1.55, 1.50, 1.65, 1.80, 1.75, 1.90},
+        {0.05,  0.20,  0.15,  0.30,  0.25,  0.40,  0.35,  0.50,  0.65,  0.20,  0.55,  0.30,  0.45,  0.40,  0.55,
+            0.90,  0.85,0.40,  0.55,  0.90,  0.65,  0.80,  0.95,  1.10,  1.45,  1.40,  0.95,  1.10, 1.05, 1.00,
+            1.35, 1.30, 1.05,1.40,  1.75, 1.30, 1.65, 1.40, 1.75, 1.50, 2.05, 1.60, 2.15, 1.90, 1.65},
+        {0.20,  0.15,  0.30,  0.25,  0.40,  0.35,  0.30,  0.25,  0.60,  0.35,  0.70,  0.85,  0.20,  0.95,  0.70,
+            0.65,  0.80,0.55,  0.90,  0.85,  1.00,  0.95,  0.90,  1.05,  1.20,  1.35,  1.10,  1.25, 0.80, 1.55,
+            1.30, 1.25, 1.20,1.95,  1.50, 1.45, 1.20, 1.95, 1.30, 1.85, 1.40, 1.95, 1.50, 1.85, 1.80},
+        {0.35,  0.50,  0.45,  0.40,  0.55,  0.50,  0.45,  0.60,  0.55,  0.50,  0.45,  0.60,  0.55,  0.70,  0.85,
+            0.80,  0.75,0.70,  1.05,  1.00,  0.55,  0.90,  1.25,  1.00,  1.35,  1.10,  1.45,  1.00, 1.75, 1.50,
+            1.45, 1.40, 1.75,1.30,  1.25, 1.60, 1.95, 1.10, 1.85, 2.00, 1.95, 2.10, 1.85, 2.20, 2.15},
+        {0.30,  0.45,  0.40,  0.15,  0.50,  0.65,  0.40,  0.35,  0.50,  0.85,  0.80,  0.75,  0.70,  0.85,  0.80,
+            0.95,  0.70,1.05,  0.80,  1.15,  0.90,  1.05,  1.00,  1.35,  0.90,  1.05,  1.00,  1.35, 1.10, 1.45,
+            1.40, 1.75, 1.70,1.65,  1.40, 1.35, 1.30, 1.85, 2.20, 1.55, 1.90, 1.85, 2.00, 1.55, 2.10},
+        {0.45,  0.20,  0.15,  0.30,  0.45,  0.40,  0.55,  0.50,  0.65,  0.60,  0.75,  0.70,  0.85,  0.60,  0.95,
+            0.70,  1.05,0.80,  0.95,  0.90,  1.05,  0.80,  1.15,  1.10,  1.25,  1.60,  0.75,  1.10, 1.25, 1.40,
+            1.55, 1.70, 1.45,1.60,  1.75, 1.90, 1.65, 1.80, 2.15, 2.10, 1.45, 2.00, 1.75, 1.90, 1.85},
+        {0.20,  0.35,  0.30,  0.45,  0.40,  0.55,  0.50,  0.45,  0.80,  0.75,  0.50,  0.85,  1.00,  0.75,  0.50,
+            0.85,  1.20,1.15,  1.10,  1.05,  0.80,  1.15,  0.70,  1.25,  1.40,  1.15,  1.70,  1.45, 1.60, 1.35,
+            1.50, 1.45, 1.80,1.55,  1.90, 1.45, 2.00, 2.15, 2.10, 1.45, 1.60, 1.75, 1.90, 2.25, 1.80},
+        {0.35,  0.50,  0.45,  0.60,  0.55,  0.70,  0.65,  0.40,  0.75,  0.70,  1.05,  0.60,  0.75,  0.70,  0.85,
+            1.00,  0.95,0.70,  1.05,  1.00,  1.35,  1.30,  1.25,  1.40,  1.15,  1.50,  1.25,  1.60, 1.55, 1.50,
+            1.45, 1.80, 1.35,1.90,  1.85, 1.80, 1.95, 1.70, 1.65, 1.60, 1.75, 1.70, 2.25, 1.80, 2.15},
+        {0.30,  0.65,  0.60,  0.55,  0.70,  0.85,  0.80,  0.95,  0.90,  0.65,  1.00,  0.75,  0.70,  1.25,  1.00,
+            0.95,  1.30,1.05,  1.20,  0.95,  1.30,  1.25,  1.20,  1.55,  0.90,  1.05,  1.40,  1.15, 1.50, 1.25,
+            1.60, 1.75, 1.70,2.05,  1.80, 2.15, 1.70, 1.65, 1.80, 1.95, 1.90, 2.05, 1.80, 2.15, 2.10},
+        {0.65,  0.80,  0.75,  0.30,  0.85,  0.80,  0.55,  0.90,  0.65,  0.80,  1.15,  0.90,  1.25,  1.20,  0.75,
+            1.10,  0.85,1.20,  0.95,  1.10,  1.05,  1.40,  1.15,  1.30,  1.05,  1.60,  1.35,  1.30, 1.45, 1.40,
+            1.95, 1.90, 1.85,1.60,  1.95, 1.50, 2.05, 1.80, 1.95, 1.90, 2.25, 1.60, 2.35, 2.10, 2.25},
+        {0.60,  0.75,  0.70,  0.45,  0.60,  0.95,  0.70,  1.05,  0.80,  0.95,  0.50,  1.05,  1.00,  1.15,  1.10,
+            0.85,  0.60,0.95,  1.10,  1.45,  1.20,  1.35,  1.70,  1.45,  1.60,  1.75,  1.90,  1.25, 1.60, 1.55,
+            1.50, 1.85, 1.40,1.75,  1.90, 2.05, 1.60, 1.95, 1.50, 2.25, 1.40, 1.95, 2.30, 2.25, 1.60},
+        {0.75,  0.70,  0.65,  0.60,  0.75,  0.70,  0.85,  0.80,  0.95,  0.90,  1.05,  1.00,  1.15,  1.10,  0.85,
+            1.00,  1.15,1.30,  1.05,  1.60,  1.55,  0.90,  1.05,  1.20,  1.55,  1.30,  1.45,  1.40, 1.75, 2.10,
+            1.65, 1.40, 1.95,2.10,  2.05, 2.00, 1.95, 1.70, 1.65, 2.40, 1.95, 2.30, 2.05, 2.40, 2.35},
+        {0.50,  0.65,  0.80,  0.75,  0.90,  0.85,  0.80,  0.95,  1.10,  1.05,  0.80,  1.15,  0.90,  1.25,  1.00,
+            1.15,  1.10,0.85,  1.20,  1.35,  1.10,  1.45,  1.40,  1.55,  1.70,  1.05,  1.40,  1.75, 1.30, 1.85,
+            2.00, 1.75, 1.90,1.85,  2.20, 1.95, 2.30, 1.85, 2.40, 1.95, 2.30, 2.05, 2.20, 1.95, 2.50},
+        {0.65,  0.80,  0.95,  0.90,  1.05,  1.00,  1.15,  0.70,  0.65,  1.00,  1.15,  1.30,  1.05,  1.00,  1.15,
+            1.10,  1.25,1.40,  1.35,  1.10,  1.25,  1.60,  1.35,  1.30,  1.65,  1.20,  1.55,  1.50, 1.65, 1.80,
+            1.75, 1.70, 1.85,2.00,  2.15, 1.70, 1.65, 2.20, 1.95, 2.30, 1.85, 2.40, 2.15, 2.50, 1.85},
+        {0.80,  0.95,  0.90,  0.85,  1.00,  1.15,  1.10,  1.05,  1.20,  0.95,  0.90,  1.05,  1.00,  1.15,  1.10,
+            1.25,  1.40,1.15,  1.10,  1.05,  1.00,  1.35,  1.30,  1.45,  1.60,  1.55,  1.50,  1.85, 1.80, 1.55,
+            1.70, 1.65, 1.80,1.95,  2.10, 1.85, 2.00, 2.55, 2.10, 1.85, 2.40, 2.35, 2.50, 2.85, 2.40}
+};
+
+static void getBounds(double rho, int& below, int& above) {
+    double calculateIndex = (rho - MINIMUM_RADIUS) / SPACING;
+    below = (int) floor(calculateIndex);
+    above = below + 1;
+    if (above >= AmoebaGeneralizedKirkwoodForceImpl::NUM_NECK_RADII) {
+        // Extrapolate up from the top table values.
+        below = AmoebaGeneralizedKirkwoodForceImpl::NUM_NECK_RADII - 1;
+        above = AmoebaGeneralizedKirkwoodForceImpl::NUM_NECK_RADII - 2;
+    } else if (below < 0) {
+        // If below is less than 0, extrapolate down from the bottom table values.
+        below = 0;
+        above = 1;
+    }
+}
+
+static double interpolate2D(double x1, double x2, double y1, double y2, double x, double y,
+                            double fx1y1, double fx2y1, double fx1y2, double fx2y2) {
+    double fxy1 = (x2 - x) / (x2 - x1) * fx1y1 + (x - x1) / (x2 - x1) * fx2y1;
+    double fxy2 = (x2 - x) / (x2 - x1) * fx1y2 + (x - x1) / (x2 - x1) * fx2y2;
+
+    return (y2 - y) / (y2 - y1) * fxy1 + (y - y1) / (y2 - y1) * fxy2;
+}
+
+void AmoebaGeneralizedKirkwoodForceImpl::getNeckConstants(double radius, double radiusK, double &aij, double &bij) {
+    // Convert the radii values from nm to A.
+    radius = radius * 10.0;
+    radiusK = radiusK * 10.0;
+
+    // Determine low and high values for integration
+    int lowI, highI, lowJ, highJ;
+    getBounds(radius, lowI, highI);
+    getBounds(radiusK, lowJ, highJ);
+
+    // Interpolate/Extrapolate Aij and Bij constant values
+    aij = interpolate2D(NECK_RADII[lowI], NECK_RADII[highI], NECK_RADII[lowJ], NECK_RADII[highJ],
+                               radius, radiusK, Aij[lowI][lowJ], Aij[highI][lowJ], Aij[lowI][highJ], Aij[highI][highJ]);
+
+    bij = interpolate2D(NECK_RADII[lowI], NECK_RADII[highI], NECK_RADII[lowJ], NECK_RADII[highJ],
+                               radius, radiusK, Bij[lowI][lowJ], Bij[highI][lowJ], Bij[lowI][highJ], Bij[highI][highJ]);
+
+    // Never let Aij be negative.
+    if (aij < 0.0) {
+        aij = 0.0;
+    }
+
+    // Convert aij from Ang(-11) to nm(-11)
+    aij = aij * 1.0e11;
+    // Convert bij from A to nm.
+    bij = bij * 0.1;
+}
+
+double AmoebaGeneralizedKirkwoodForceImpl::neckDescreen(double r, double radius, double radiusK, double sneck) {
+
+    // Radius of water in nm.
+    double radiusWater = 0.14;
+
+    // If atoms are too widely separated there is no neck formed.
+    if (r > radius + radiusK + 2.0 * radiusWater) {
+        return 0.0;
+    }
+
+    // Get Aij and Bij based on parameterization by Corrigan et al.
+    double aij, bij;
+    getNeckConstants(radius, radiusK, aij, bij);
+    double rMinusBij = r - bij;
+    double radiiMinusr = radius + radiusK + 2.0 * radiusWater - r;
+    double power1 = rMinusBij * rMinusBij * rMinusBij * rMinusBij;
+    double power2 = radiiMinusr * radiiMinusr * radiiMinusr * radiiMinusr;
+
+    // Use Aij and Bij to get neck integral using Equations 13 and 14 from Aguilar/Onufriev 2010 paper
+    // Sneck may be based on the number of heavy atoms bound to the atom being descreened.
+    double neckIntegral = 4.0 * M_PI / 3.0 * sneck * aij * power1 * power2;
+
+    return neckIntegral;
+}
+
+/**
+ * The array of neck tabulated radii values.
+ */
+const std::vector<float>& AmoebaGeneralizedKirkwoodForceImpl::getNeckRadii() {
+    return NECK_RADII;
+}
+
+/**
+ * The tabulated Aij parameters.
+ */
+const float (&AmoebaGeneralizedKirkwoodForceImpl::getAij())[NUM_NECK_RADII][NUM_NECK_RADII]  {
+    return Aij;
+}
+
+/**
+ * The tabulated Bij parameters.
+ */
+const float (&AmoebaGeneralizedKirkwoodForceImpl::getBij())[NUM_NECK_RADII][NUM_NECK_RADII]  {
+    return Bij;
+}
diff --git a/plugins/amoeba/openmmapi/src/AmoebaVdwForce.cpp b/plugins/amoeba/openmmapi/src/AmoebaVdwForce.cpp
index 70046d6b3f..6bc089b3d8 100644
--- a/plugins/amoeba/openmmapi/src/AmoebaVdwForce.cpp
+++ b/plugins/amoeba/openmmapi/src/AmoebaVdwForce.cpp
@@ -44,23 +44,23 @@ AmoebaVdwForce::AmoebaVdwForce() : nonbondedMethod(NoCutoff), potentialFunction(
         useTypes(false), alchemicalMethod(None), n(5), alpha(0.7) {
 }
 
-int AmoebaVdwForce::addParticle(int parentIndex, double sigma, double epsilon, double reductionFactor, bool isAlchemical) {
+int AmoebaVdwForce::addParticle(int parentIndex, double sigma, double epsilon, double reductionFactor, bool isAlchemical, double scaleFactor) {
     if (useTypes)
         throw OpenMMException("AmoebaVdwForce: must use the same version of addParticle() for all particles");
-    parameters.push_back(VdwInfo(parentIndex, sigma, epsilon, -1, reductionFactor, isAlchemical));
+    parameters.push_back(VdwInfo(parentIndex, sigma, epsilon, -1, reductionFactor, isAlchemical, scaleFactor));
     return parameters.size()-1;
 }
 
-int AmoebaVdwForce::addParticle(int parentIndex, int typeIndex, double reductionFactor, bool isAlchemical) {
+int AmoebaVdwForce::addParticle(int parentIndex, int typeIndex, double reductionFactor, bool isAlchemical, double scaleFactor) {
     if (parameters.size() > 0 && !useTypes)
         throw OpenMMException("AmoebaVdwForce: must use the same version of addParticle() for all particles");
     useTypes = true;
-    parameters.push_back(VdwInfo(parentIndex, 1.0, 0.0, typeIndex, reductionFactor, isAlchemical));
+    parameters.push_back(VdwInfo(parentIndex, 1.0, 0.0, typeIndex, reductionFactor, isAlchemical, scaleFactor));
     return parameters.size()-1;
 }
 
-void AmoebaVdwForce::getParticleParameters(int particleIndex, int& parentIndex,
-                                           double& sigma, double& epsilon, double& reductionFactor, bool& isAlchemical, int& typeIndex) const {
+void AmoebaVdwForce::getParticleParameters(int particleIndex, int& parentIndex,double& sigma, double& epsilon, double& reductionFactor,
+                                           bool& isAlchemical, int& typeIndex, double& scaleFactor) const {
     ASSERT_VALID_INDEX(particleIndex, parameters);
     parentIndex     = parameters[particleIndex].parentIndex;
     sigma           = parameters[particleIndex].sigma;
@@ -68,10 +68,11 @@ void AmoebaVdwForce::getParticleParameters(int particleIndex, int& parentIndex,
     reductionFactor = parameters[particleIndex].reductionFactor;
     isAlchemical    = parameters[particleIndex].isAlchemical;
     typeIndex       = parameters[particleIndex].typeIndex;
+    scaleFactor     = parameters[particleIndex].scaleFactor;
 }
 
-void AmoebaVdwForce::setParticleParameters(int particleIndex, int parentIndex,
-                                           double sigma, double epsilon, double reductionFactor, bool isAlchemical, int typeIndex) {
+void AmoebaVdwForce::setParticleParameters(int particleIndex, int parentIndex,double sigma, double epsilon, double reductionFactor,
+                                           bool isAlchemical, int typeIndex, double scaleFactor) {
     ASSERT_VALID_INDEX(particleIndex, parameters);
     parameters[particleIndex].parentIndex     = parentIndex;
     parameters[particleIndex].sigma           = sigma;
@@ -79,6 +80,7 @@ void AmoebaVdwForce::setParticleParameters(int particleIndex, int parentIndex,
     parameters[particleIndex].reductionFactor = reductionFactor;
     parameters[particleIndex].isAlchemical    = isAlchemical;
     parameters[particleIndex].typeIndex       = typeIndex;
+    parameters[particleIndex].scaleFactor     = scaleFactor;
 }
 
 int AmoebaVdwForce::addParticleType(double sigma, double epsilon) {
diff --git a/plugins/amoeba/openmmapi/src/AmoebaVdwForceImpl.cpp b/plugins/amoeba/openmmapi/src/AmoebaVdwForceImpl.cpp
index 152abd5d70..cd8ddbe4de 100644
--- a/plugins/amoeba/openmmapi/src/AmoebaVdwForceImpl.cpp
+++ b/plugins/amoeba/openmmapi/src/AmoebaVdwForceImpl.cpp
@@ -55,9 +55,9 @@ void AmoebaVdwForceImpl::initialize(ContextImpl& context) {
         throw OpenMMException("AmoebaVdwForce must have exactly as many particles as the System it belongs to.");
     for (int i = 0; i < owner.getNumParticles(); i++) {
         int parentIndex, typeIndex;
-        double sigma, epsilon, reductionFactor;
+        double sigma, epsilon, reductionFactor, scaleFactor;
         bool isAlchemical;
-        owner.getParticleParameters(i, parentIndex, sigma, epsilon, reductionFactor, isAlchemical, typeIndex);
+        owner.getParticleParameters(i, parentIndex, sigma, epsilon, reductionFactor, isAlchemical, typeIndex, scaleFactor);
         if (sigma < 0)
             throw OpenMMException("AmoebaVdwForce: sigma for a particle cannot be negative");
         if (owner.getPotentialFunction() == AmoebaVdwForce::Buffered147 && sigma == 0)
@@ -101,11 +101,11 @@ void AmoebaVdwForceImpl::createParameterMatrix(const AmoebaVdwForce& force, vect
     if (force.getUseParticleTypes()) {
         // We get the types directly from the particles.
 
-        double sigma, epsilon, reduction;
+        double sigma, epsilon, reduction, scaleFactor;
         int parent;
         bool isAlchemical;
         for (int i = 0; i < numParticles; i++)
-            force.getParticleParameters(i, parent, sigma, epsilon, reduction, isAlchemical, type[i]);
+            force.getParticleParameters(i, parent, sigma, epsilon, reduction, isAlchemical, type[i], scaleFactor);
         numTypes = force.getNumParticleTypes();
         typeSigma.resize(numTypes);
         typeEpsilon.resize(numTypes);
@@ -117,10 +117,10 @@ void AmoebaVdwForceImpl::createParameterMatrix(const AmoebaVdwForce& force, vect
 
         map<pair<double, double>, int> typeForParams;
         for (int i = 0; i < numParticles; i++) {
-            double sigma, epsilon, reduction;
+            double sigma, epsilon, reduction, scaleFactor;
             int parent, typeIndex;
             bool isAlchemical;
-            force.getParticleParameters(i, parent, sigma, epsilon, reduction, isAlchemical, typeIndex);
+            force.getParticleParameters(i, parent, sigma, epsilon, reduction, isAlchemical, typeIndex, scaleFactor);
             pair<double, double> params = make_pair(sigma, epsilon);
             map<pair<double, double>, int>::iterator entry = typeForParams.find(params);
             if (entry == typeForParams.end()) {
diff --git a/plugins/amoeba/openmmapi/src/AmoebaWcaDispersionForce.cpp b/plugins/amoeba/openmmapi/src/AmoebaWcaDispersionForce.cpp
index bb316da575..1e8d7777d6 100644
--- a/plugins/amoeba/openmmapi/src/AmoebaWcaDispersionForce.cpp
+++ b/plugins/amoeba/openmmapi/src/AmoebaWcaDispersionForce.cpp
@@ -38,14 +38,22 @@
 using namespace OpenMM;
 
 AmoebaWcaDispersionForce::AmoebaWcaDispersionForce() {
-    epso      = 0.1100;
-    epsh      = 0.0135;
-    rmino     = 1.7025;
-    rminh     = 1.3275;
-    awater    = 0.033428;
-    slevy     = 1.0;
-    shctd     = 0.81;
-    dispoff   = 0.26;
+    // Amoeba Water '03 vdW parameters (Diameters in Angstroms; Well depth in kcal/mole)
+    // vdw           1               3.4050     0.1100
+    // vdw           2               2.6550     0.0135      0.910
+
+    // Convert kcal/mol to kJ/mol
+    epso = 0.1100 * 4.184e0;
+    epsh = 0.0135 * 4.184e0;
+    // Convert A to nm.
+    rmino = 1.7025e-01;
+    rminh = 1.3275e-01;
+    dispoff = 1.056e-01;
+    // Convert water number density from water / A^3 to water / nm^3.
+    awater = 0.033428e03;
+    // No units.
+    slevy = 1.0;
+    shctd = 0.82;
 }
 
 int AmoebaWcaDispersionForce::addParticle(double radius, double epsilon) {
diff --git a/plugins/amoeba/openmmapi/src/AmoebaWcaDispersionForceImpl.cpp b/plugins/amoeba/openmmapi/src/AmoebaWcaDispersionForceImpl.cpp
index d8448d14e0..0f6549fc75 100644
--- a/plugins/amoeba/openmmapi/src/AmoebaWcaDispersionForceImpl.cpp
+++ b/plugins/amoeba/openmmapi/src/AmoebaWcaDispersionForceImpl.cpp
@@ -61,66 +61,66 @@ double AmoebaWcaDispersionForceImpl::calcForcesAndEnergy(ContextImpl& context, b
         return kernel.getAs<CalcAmoebaWcaDispersionForceKernel>().execute(context, includeForces, includeEnergy);
     return 0.0;
 }
-void AmoebaWcaDispersionForceImpl::getMaximumDispersionEnergy(const AmoebaWcaDispersionForce& force, int particleIndex, double& maxDispersionEnergy) {
 
-    const double pi  = 3.1415926535897;
+static double tailCorrection(double ri, double eps, double rmin) {
+    if (ri < rmin) {
+        double r3 = ri * ri * ri;
+        double rmin3 = rmin * rmin * rmin;
+        return -eps * 4.0e0 * M_PI * (rmin3 - r3) / 3.0e0 - eps * M_PI * 18.0e0 * rmin3 / 11.0e0;
+    } else {
+        double ri2 = ri * ri;
+        double ri4 = ri2 * ri2;
+        double ri7 = ri * ri2 * ri4;
+        double ri11 = ri7 * ri4;
+        double rmin2 = rmin * rmin;
+        double rmin4 = rmin2 * rmin2;
+        double rmin7 = rmin * rmin2 * rmin4;
+        return 2.0e0 * M_PI * eps * rmin7 * (2.0e0 * rmin7 - 11.0e0 * ri7) / (11.0e0 * ri11);
+    }
+}
 
-    // from last loop in subroutine knp in ksolv.f
+void AmoebaWcaDispersionForceImpl::getMaximumDispersionEnergy(const AmoebaWcaDispersionForce &force, int particleIndex,
+                                                              double &maxDispersionEnergy) {
 
-    double rdisp, epsi;
-    force.getParticleParameters(particleIndex, rdisp, epsi);
-    if (epsi <= 0.0 || rdisp <= 0.0) {
+    // Atom i parameters
+    double rmini, epsi;
+    force.getParticleParameters(particleIndex, rmini, epsi);
+    if (epsi <= 0.0 || rmini <= 0.0) {
         maxDispersionEnergy = 0.0;
         return;
     }
-    double rmini     = rdisp;
-    rdisp           += force.getDispoff();
-    double epso      = force.getEpso();
-    double emixo     = std::sqrt(epso) + std::sqrt(epsi);
-           emixo     = 4.0*epso*epsi/(emixo*emixo);
-
-    double rmino     = force.getRmino();
-    double rmino2    = rmino*rmino;
-    double rmini2    = rmini*rmini;
-    double rmixo     = 2.0*(rmino2*rmino + rmini2*rmini) / (rmino2 + rmini2);
-
-    double rmixo3    = rmixo*rmixo*rmixo;
-    double rmixo7    = rmixo*rmixo3*rmixo3;
-    double ao        = emixo*rmixo7;
-
-    double epsh      = force.getEpsh();
-    double emixh     = std::sqrt(epsh) + std::sqrt(epsi);
-           emixh     = 4.0*epsh*epsi/(emixh*emixh);
-    double rminh     = force.getRminh();
-    double rminh2    = rminh*rminh;
-    double rmixh     = rminh*rminh + rmini2;
-           rmixh     = 2.0 * (rminh2*rminh + rmini2*rmini) / (rminh2 + rmini2);
-    double rmixh3    = rmixh*rmixh*rmixh;
-    double rmixh7    = rmixh3*rmixh3*rmixh;
-    double ah        = emixh*rmixh7;
-
-    double rdisp3    = rdisp*rdisp*rdisp;
-    double rdisp7    = rdisp*rdisp3*rdisp3;
-    double rdisp11   = rdisp7*rdisp3*rdisp;
-
-    double cdisp;
-    if (rdisp < rmixh) {
-        cdisp = -4.0*pi*emixh*(rmixh3-rdisp3)/3.0 - emixh*18.0/11.0*rmixh3*pi;
-    } else {
-        cdisp = 2.0*pi*(2.0*rmixh7-11.0*rdisp7)*ah/ (11.0*rdisp11);
-    }
-    cdisp *= 2.0;
-    if (rdisp < rmixo) {
-        cdisp -= 4.0*pi*emixo*(rmixo3-rdisp3)/3.0;
-        cdisp -= emixo*18.0/11.0*rmixo3*pi;
-    } else {
-        cdisp += 2.0*pi*(2.0*rmixo7-11.0*rdisp7) * ao/(11.0*rdisp11);
-    }
-    maxDispersionEnergy = force.getSlevy()*force.getAwater()*cdisp;
+    double rmini2 = rmini * rmini;
+    double sepsi = std::sqrt(epsi);
+
+    // Atom i with water oxygen.
+    double epso = force.getEpso();
+    double emixo = std::sqrt(epso) + sepsi;
+    emixo = 4.0 * epso * epsi / (emixo * emixo);
+
+    double rmino = force.getRmino();
+    double rmino2 = rmino * rmino;
+    double rmixo = 2.0 * (rmino2 * rmino + rmini2 * rmini) / (rmino2 + rmini2);
+
+    // Atom i with water hydrogen.
+    double epsh = force.getEpsh();
+    double emixh = std::sqrt(epsh) + sepsi;
+    emixh = 4.0 * epsh * epsi / (emixh * emixh);
+
+    double rminh = force.getRminh();
+    double rminh2 = rminh * rminh;
+    double rmixh = 2.0 * (rminh2 * rminh + rmini2 * rmini) / (rminh2 + rmini2);
+
+    // Compute the tail correction (i.e. the dispersion integral in the absence of any other solute atoms).
+    double dispersionOffest = force.getDispoff();
+    double riO = rmixo / 2.0e0 + dispersionOffest;
+    double cdisp = tailCorrection(riO, emixo, rmixo);
+    double riH = rmixh / 2.0e0 + dispersionOffest;
+    cdisp += 2.0e0 * tailCorrection(riH, emixh, rmixh);
+
+    maxDispersionEnergy = force.getSlevy() * force.getAwater() * cdisp;
 }
 
-double AmoebaWcaDispersionForceImpl::getTotalMaximumDispersionEnergy(const AmoebaWcaDispersionForce& force) {
-
+double AmoebaWcaDispersionForceImpl::getTotalMaximumDispersionEnergy(const AmoebaWcaDispersionForce &force) {
     double totalMaximumDispersionEnergy = 0.0;
     for (int ii = 0; ii < force.getNumParticles(); ii++) {
         double maximumDispersionEnergy;
diff --git a/plugins/amoeba/platforms/common/src/AmoebaCommonKernels.cpp b/plugins/amoeba/platforms/common/src/AmoebaCommonKernels.cpp
index 872200bdef..77b318d6df 100644
--- a/plugins/amoeba/platforms/common/src/AmoebaCommonKernels.cpp
+++ b/plugins/amoeba/platforms/common/src/AmoebaCommonKernels.cpp
@@ -1722,10 +1722,10 @@ class CommonCalcAmoebaGeneralizedKirkwoodForceKernel::ForceInfo : public Compute
     ForceInfo(const AmoebaGeneralizedKirkwoodForce& force) : force(force) {
     }
     bool areParticlesIdentical(int particle1, int particle2) {
-        double charge1, charge2, radius1, radius2, scale1, scale2;
-        force.getParticleParameters(particle1, charge1, radius1, scale1);
-        force.getParticleParameters(particle2, charge2, radius2, scale2);
-        return (charge1 == charge2 && radius1 == radius2 && scale1 == scale2);
+        double charge1, charge2, radius1, radius2, scale1, scale2, descreen1, descreen2, neck1, neck2;
+        force.getParticleParameters(particle1, charge1, radius1, scale1, descreen1, neck1);
+        force.getParticleParameters(particle2, charge2, radius2, scale2, descreen2, neck2);
+        return (charge1 == charge2 && radius1 == radius2 && scale1 == scale2 && descreen1 == descreen2 && neck1 == neck2);
     }
 private:
     const AmoebaGeneralizedKirkwoodForce& force;
@@ -1747,7 +1747,16 @@ void CommonCalcAmoebaGeneralizedKirkwoodForceKernel::initialize(const System& sy
     NonbondedUtilities& nb = cc.getNonbondedUtilities();
     int paddedNumAtoms = cc.getPaddedNumAtoms();
     int elementSize = (cc.getUseDoublePrecision() ? sizeof(double) : sizeof(float));
-    params.initialize<mm_float2>(cc, paddedNumAtoms, "amoebaGkParams");
+    params.initialize<mm_float4>(cc, paddedNumAtoms,"amoebaGkParams");
+    vector<float> neckRadiiVector = AmoebaGeneralizedKirkwoodForceImpl::getNeckRadii();
+    unsigned long numNeckRadii = neckRadiiVector.size();
+    unsigned long numNeckRadii2 = numNeckRadii * numNeckRadii;
+    neckRadii.initialize<float>(cc, numNeckRadii, "neckRadii");
+    neckRadii.upload(neckRadiiVector);
+    neckA.initialize<float>( cc, numNeckRadii2, "neckA");
+    neckA.upload(AmoebaGeneralizedKirkwoodForceImpl::getAij());
+    neckB.initialize<float>( cc, numNeckRadii2, "neckB");
+    neckB.upload(AmoebaGeneralizedKirkwoodForceImpl::getBij());
     bornRadii.initialize(cc, paddedNumAtoms, elementSize, "bornRadii");
     field.initialize(cc, 3*paddedNumAtoms, sizeof(long long), "gkField");
     bornSum.initialize<long long>(cc, paddedNumAtoms, "bornSum");
@@ -1762,14 +1771,12 @@ void CommonCalcAmoebaGeneralizedKirkwoodForceKernel::initialize(const System& sy
     cc.addAutoclearBuffer(field);
     cc.addAutoclearBuffer(bornSum);
     cc.addAutoclearBuffer(bornForce);
-    vector<mm_float2> paramsVector(paddedNumAtoms);
+    vector<mm_float4> paramsVector(paddedNumAtoms);
     for (int i = 0; i < force.getNumParticles(); i++) {
-        double charge, radius, scalingFactor;
-        force.getParticleParameters(i, charge, radius, scalingFactor);
-        paramsVector[i] = mm_float2((float) radius, (float) (scalingFactor*radius));
-        
+        double charge, radius, scalingFactor, descreenRadius, neckFactor;
+        force.getParticleParameters(i, charge, radius, scalingFactor, descreenRadius, neckFactor);
+        paramsVector[i] = mm_float4((float) radius,(float) scalingFactor, (float) descreenRadius, (float) neckFactor);
         // Make sure the charge matches the one specified by the AmoebaMultipoleForce.
-        
         double charge2, thole, damping, polarity;
         int axisType, atomX, atomY, atomZ;
         vector<double> dipole, quadrupole;
@@ -1814,11 +1821,28 @@ void CommonCalcAmoebaGeneralizedKirkwoodForceKernel::initialize(const System& sy
         defines["MUTUAL_POLARIZATION"] = "";
     else if (polarizationType == AmoebaMultipoleForce::Extrapolated)
         defines["EXTRAPOLATED_POLARIZATION"] = "";
+    defines["DESCREEN_OFFSET"] = cc.doubleToString(force.getDescreenOffset());
+    tanhRescaling = force.getTanhRescaling();
+    // Max Born radius is 3 nm (30 A).
+    defines["BIG_RADIUS"] = cc.doubleToString(3.0);
+    if (tanhRescaling) {
+        defines["TANH_RESCALING"] = "true";
+        double beta0, beta1, beta2;
+        force.getTanhParameters(beta0, beta1, beta2);
+        defines["BETA0"] = cc.doubleToString(beta0);
+        defines["BETA1"] = cc.doubleToString(beta1);
+        defines["BETA2"] = cc.doubleToString(beta2);
+    } else {
+        defines["TANH_RESCALING"] = "false";
+        defines["BETA0"] = cc.doubleToString(0.0);
+        defines["BETA1"] = cc.doubleToString(0.0);
+        defines["BETA2"] = cc.doubleToString(0.0);
+    }
     includeSurfaceArea = force.getIncludeCavityTerm();
     if (includeSurfaceArea) {
         defines["SURFACE_AREA_FACTOR"] = cc.doubleToString(force.getSurfaceAreaFactor());
         defines["PROBE_RADIUS"] = cc.doubleToString(force.getProbeRadius());
-        defines["DIELECTRIC_OFFSET"] = cc.doubleToString(0.009);
+        defines["DIELECTRIC_OFFSET"] = cc.doubleToString(force.getDielectricOffset());
     }
     cc.addForce(new ForceInfo(force));
 }
@@ -1877,6 +1901,9 @@ void CommonCalcAmoebaGeneralizedKirkwoodForceKernel::computeBornRadii(ComputeArr
         computeBornSumKernel->addArg(cc.getPosq());
         computeBornSumKernel->addArg(params);
         computeBornSumKernel->addArg();
+        computeBornSumKernel->addArg(neckRadii);
+        computeBornSumKernel->addArg(neckA);
+        computeBornSumKernel->addArg(neckB);
         reduceBornSumKernel = program->createKernel("reduceBornSum");
         reduceBornSumKernel->addArg(bornSum);
         reduceBornSumKernel->addArg(params);
@@ -1900,6 +1927,10 @@ void CommonCalcAmoebaGeneralizedKirkwoodForceKernel::computeBornRadii(ComputeArr
         chainRuleKernel->addArg();
         chainRuleKernel->addArg();
         chainRuleKernel->addArg(params);
+        chainRuleKernel->addArg(neckRadii);
+        chainRuleKernel->addArg(neckA);
+        chainRuleKernel->addArg(neckB);
+        chainRuleKernel->addArg(bornSum);
         chainRuleKernel->addArg(bornRadii);
         chainRuleKernel->addArg(bornForce);
         ediffKernel = program->createKernel("computeEDiffForce");
@@ -1968,12 +1999,11 @@ void CommonCalcAmoebaGeneralizedKirkwoodForceKernel::copyParametersToContext(Con
         throw OpenMMException("updateParametersInContext: The number of particles has changed");
     
     // Record the per-particle parameters.
-    
-    vector<mm_float2> paramsVector(cc.getPaddedNumAtoms());
+    vector<mm_float4> paramsVector(cc.getPaddedNumAtoms());
     for (int i = 0; i < force.getNumParticles(); i++) {
-        double charge, radius, scalingFactor;
-        force.getParticleParameters(i, charge, radius, scalingFactor);
-        paramsVector[i] = mm_float2((float) radius, (float) (scalingFactor*radius));
+        double charge, radius, scalingFactor, descreenRadius, neckFactor;
+        force.getParticleParameters(i, charge, radius, scalingFactor, descreenRadius, neckFactor);
+        paramsVector[i] = mm_float4((float) radius, (float) scalingFactor, (float) descreenRadius, (float) neckFactor);
     }
     params.upload(paramsVector);
     cc.invalidateMolecules();
@@ -1989,11 +2019,11 @@ class CommonCalcAmoebaVdwForceKernel::ForceInfo : public ComputeForceInfo {
     }
     bool areParticlesIdentical(int particle1, int particle2) {
         int iv1, iv2, type1, type2;
-        double sigma1, sigma2, epsilon1, epsilon2, reduction1, reduction2;
+        double sigma1, sigma2, epsilon1, epsilon2, reduction1, reduction2, scaleFactor1, scaleFactor2;
         bool isAlchemical1, isAlchemical2;
-        force.getParticleParameters(particle1, iv1, sigma1, epsilon1, reduction1, isAlchemical1, type1);
-        force.getParticleParameters(particle2, iv2, sigma2, epsilon2, reduction2, isAlchemical2, type2);
-        return (sigma1 == sigma2 && epsilon1 == epsilon2 && reduction1 == reduction2 && isAlchemical1 == isAlchemical2 && type1 == type2);
+        force.getParticleParameters(particle1, iv1, sigma1, epsilon1, reduction1, isAlchemical1, type1, scaleFactor1);
+        force.getParticleParameters(particle2, iv2, sigma2, epsilon2, reduction2, isAlchemical2, type2, scaleFactor2);
+        return (sigma1 == sigma2 && epsilon1 == epsilon2 && reduction1 == reduction2 && isAlchemical1 == isAlchemical2 && type1 == type2 && scaleFactor1 == scaleFactor2);
     }
 private:
     const AmoebaVdwForce& force;
@@ -2014,6 +2044,7 @@ void CommonCalcAmoebaVdwForceKernel::initialize(const System& system, const Amoe
     int paddedNumAtoms = cc.getPaddedNumAtoms();
     bondReductionAtoms.initialize<int>(cc, paddedNumAtoms, "bondReductionAtoms");
     bondReductionFactors.initialize<float>(cc, paddedNumAtoms, "bondReductionFactors");
+    scaleFactors.initialize<float>(cc, paddedNumAtoms, "scaleFactors");
     tempPosq.initialize(cc, paddedNumAtoms, cc.getUseDoublePrecision() ? sizeof(mm_double4) : sizeof(mm_float4), "tempPosq");
     tempForces.initialize<long long>(cc, 3*paddedNumAtoms, "tempForces");
     
@@ -2035,6 +2066,7 @@ void CommonCalcAmoebaVdwForceKernel::initialize(const System& system, const Amoe
     vector<float> isAlchemicalVec(paddedNumAtoms, 0);
     vector<int> bondReductionAtomsVec(paddedNumAtoms, 0);
     vector<float> bondReductionFactorsVec(paddedNumAtoms, 0);
+    vector<float> scaleFactorsVec(paddedNumAtoms, 0);
     vector<vector<int> > exclusions(cc.getNumAtoms());
 
     // Handle Alchemical parameters.
@@ -2046,17 +2078,19 @@ void CommonCalcAmoebaVdwForceKernel::initialize(const System& system, const Amoe
 
     for (int i = 0; i < force.getNumParticles(); i++) {
         int ivIndex, type;
-        double sigma, epsilon, reductionFactor;
+        double sigma, epsilon, reductionFactor, scaleFactor;
         bool alchemical;
-        force.getParticleParameters(i, ivIndex, sigma, epsilon, reductionFactor, alchemical, type);
+        force.getParticleParameters(i, ivIndex, sigma, epsilon, reductionFactor, alchemical, type, scaleFactor);
         isAlchemicalVec[i] = (alchemical) ? 1.0f : 0.0f;
         bondReductionAtomsVec[i] = ivIndex;
         bondReductionFactorsVec[i] = (float) reductionFactor;
+        scaleFactorsVec[i] = (float) scaleFactor;
         force.getParticleExclusions(i, exclusions[i]);
         exclusions[i].push_back(i);
     }
     bondReductionAtoms.upload(bondReductionAtomsVec);
     bondReductionFactors.upload(bondReductionFactorsVec);
+    scaleFactors.upload(scaleFactorsVec);
     if (force.getUseDispersionCorrection())
         dispersionCoefficient = AmoebaVdwForceImpl::calcDispersionCorrection(system, force);
     else
@@ -2069,7 +2103,6 @@ void CommonCalcAmoebaVdwForceKernel::initialize(const System& system, const Amoe
     nonbonded = cc.createNonbondedUtilities();
     nonbonded->addParameter(ComputeParameterInfo(atomType, "atomType", "int", 1));
     nonbonded->addArgument(ComputeParameterInfo(sigmaEpsilon, "sigmaEpsilon", "float", 2));
-
     if (hasAlchemical) {
        isAlchemical.upload(isAlchemicalVec);
        currentVdwLambda = 1.0f;
@@ -2077,7 +2110,7 @@ void CommonCalcAmoebaVdwForceKernel::initialize(const System& system, const Amoe
        nonbonded->addParameter(ComputeParameterInfo(isAlchemical, "isAlchemical", "float", 1));
        nonbonded->addArgument(ComputeParameterInfo(vdwLambda, "vdwLambda", "float", 1));
     }
-    
+    nonbonded->addParameter(ComputeParameterInfo(scaleFactors, "scaleFactor", "float", 1));
     // Create the interaction kernel.
     
     map<string, string> replacements;
@@ -2170,18 +2203,21 @@ void CommonCalcAmoebaVdwForceKernel::copyParametersToContext(ContextImpl& contex
     vector<float> isAlchemicalVec(cc.getPaddedNumAtoms(), 0);
     vector<int> bondReductionAtomsVec(cc.getPaddedNumAtoms(), 0);
     vector<float> bondReductionFactorsVec(cc.getPaddedNumAtoms(), 0);
+    vector<float> scaleFactorsVec(cc.getPaddedNumAtoms(), 0);
     for (int i = 0; i < force.getNumParticles(); i++) {
         int ivIndex, type;
-        double sigma, epsilon, reductionFactor;
+        double sigma, epsilon, reductionFactor, scaleFactor;
         bool alchemical;
-        force.getParticleParameters(i, ivIndex, sigma, epsilon, reductionFactor, alchemical, type);
+        force.getParticleParameters(i, ivIndex, sigma, epsilon, reductionFactor, alchemical, type, scaleFactor);
         isAlchemicalVec[i] = (alchemical) ? 1.0f : 0.0f;
         bondReductionAtomsVec[i] = ivIndex;
         bondReductionFactorsVec[i] = (float) reductionFactor;
+        scaleFactorsVec[i] = (float) scaleFactor;
     }
     if (hasAlchemical) isAlchemical.upload(isAlchemicalVec);
     bondReductionAtoms.upload(bondReductionAtomsVec);
     bondReductionFactors.upload(bondReductionFactorsVec);
+    scaleFactors.upload(scaleFactorsVec);
     if (force.getUseDispersionCorrection())
         dispersionCoefficient = AmoebaVdwForceImpl::calcDispersionCorrection(system, force);
     else
@@ -2240,6 +2276,7 @@ void CommonCalcAmoebaWcaDispersionForceKernel::initialize(const System& system,
     defines["RMINO"] = cc.doubleToString(force.getRmino());
     defines["RMINH"] = cc.doubleToString(force.getRminh());
     defines["AWATER"] = cc.doubleToString(force.getAwater());
+    defines["DISPOFF"] = cc.doubleToString(force.getDispoff());
     defines["SHCTD"] = cc.doubleToString(force.getShctd());
     defines["M_PI"] = cc.doubleToString(M_PI);
     ComputeProgram program = cc.compileProgram(CommonAmoebaKernelSources::amoebaWcaForce, defines);
diff --git a/plugins/amoeba/platforms/common/src/AmoebaCommonKernels.h b/plugins/amoeba/platforms/common/src/AmoebaCommonKernels.h
index e2d665617a..6a23bacffe 100644
--- a/plugins/amoeba/platforms/common/src/AmoebaCommonKernels.h
+++ b/plugins/amoeba/platforms/common/src/AmoebaCommonKernels.h
@@ -298,15 +298,19 @@ class CommonCalcAmoebaGeneralizedKirkwoodForceKernel : public CalcAmoebaGenerali
      * @param force      the AmoebaGeneralizedKirkwoodForce to copy the parameters from
      */
     void copyParametersToContext(ContextImpl& context, const AmoebaGeneralizedKirkwoodForce& force);
+
 private:
     class ForceInfo;
     ComputeContext& cc;
     const System& system;
-    bool includeSurfaceArea, hasInitializedKernels;
+    bool includeSurfaceArea, tanhRescaling, hasInitializedKernels;
     int computeBornSumThreads, gkForceThreads, chainRuleThreads, ediffThreads;
     AmoebaMultipoleForce::PolarizationType polarizationType;
     std::map<std::string, std::string> defines;
     ComputeArray params;
+    ComputeArray neckRadii;
+    ComputeArray neckA;
+    ComputeArray neckB;
     ComputeArray bornSum;
     ComputeArray bornRadii;
     ComputeArray bornForce;
@@ -362,6 +366,8 @@ class CommonCalcAmoebaVdwForceKernel : public CalcAmoebaVdwForceKernel {
     float currentVdwLambda;
     // Per particle alchemical flag.
     ComputeArray isAlchemical;
+    // Per particle scale factor.
+    ComputeArray scaleFactors;
 
     double dispersionCoefficient;
     ComputeArray sigmaEpsilon, atomType;
diff --git a/plugins/amoeba/platforms/common/src/kernels/amoebaGk.cc b/plugins/amoeba/platforms/common/src/kernels/amoebaGk.cc
index 65770343d5..7facdf16bd 100644
--- a/plugins/amoeba/platforms/common/src/kernels/amoebaGk.cc
+++ b/plugins/amoeba/platforms/common/src/kernels/amoebaGk.cc
@@ -3,19 +3,49 @@
 /**
  * Reduce the Born sums to compute the Born radii.
  */
-KERNEL void reduceBornSum(GLOBAL const mm_long* RESTRICT bornSum, GLOBAL const float2* RESTRICT params, GLOBAL real* RESTRICT bornRadii) {
+KERNEL void reduceBornSum(GLOBAL const mm_long* RESTRICT bornSum, GLOBAL const float4* RESTRICT params, GLOBAL real* RESTRICT bornRadii) {
+
+    real RECIP_MAX_RADIUS3 = POW(BIG_RADIUS, (real) -3);
+    real PI4_3 = 4 * M_PI / 3;
+    real INVERSE_PI4_3 = 1 / PI4_3;
+    real ONE_THIRD = 1 / (real) 3;
+
     for (unsigned int index = GLOBAL_ID; index < NUM_ATOMS; index += GLOBAL_SIZE) {
         // Get summed Born data
-
         real sum = RECIP((real) 0x100000000)*bornSum[index];
+        sum = sum * PI4_3;
+
+        real radius = params[index].x;
+        real radius3 = radius * radius * radius;
+        real ir3 = RECIP(radius3);
+
+        // Scale up the solute integral in account for interstitial spaces.
+        if (TANH_RESCALING) {
+            // Set up tanh function components
+            real rhoi3Psi = radius3 * sum;
+            real rhoi6Psi2 = rhoi3Psi * rhoi3Psi;
+            real rhoi9Psi3 = rhoi6Psi2 * rhoi3Psi;
+            // If the output of the tanh function is 1.0, then the Born radius will be MaxBornRadius
+            real tanh_constant = PI4_3 * (ir3 - RECIP_MAX_RADIUS3);
+            sum = tanh_constant * tanh(BETA0 * rhoi3Psi - BETA1 * rhoi6Psi2 + BETA2 * rhoi9Psi3);
+        }
 
         // Now calculate Born radius.
+        sum = PI4_3 * ir3 - sum;
 
-        float radius = params[index].x;
-        radius = RECIP(radius*radius*radius);
-        sum = radius-sum;
-        sum = (sum <= 0 ? (real) 1000 : POW(sum, -1/(real) 3));
+        // If the sum is less than zero, set the Born radius to 30.0 Angstroms.
+        sum = (sum <= 0 ? BIG_RADIUS : POW(INVERSE_PI4_3 * sum, -ONE_THIRD));
         bornRadii[index] = sum;
+
+        // Born radius should be at least as large as its base radius.
+        if (bornRadii[index] < radius) {
+            bornRadii[index] = radius;
+        }
+
+        // Check if we have exceeded the maximum Born radius.
+        if (bornRadii[index] > BIG_RADIUS) {
+            bornRadii[index] = BIG_RADIUS;
+        }
     }
 }
 
@@ -23,7 +53,7 @@ KERNEL void reduceBornSum(GLOBAL const mm_long* RESTRICT bornSum, GLOBAL const f
 /**
  * Apply the surface area term to the force and energy.
  */
-KERNEL void computeSurfaceAreaForce(GLOBAL mm_long* RESTRICT bornForce, GLOBAL mixed* RESTRICT energyBuffer, GLOBAL const float2* RESTRICT params, GLOBAL const real* RESTRICT bornRadii) {
+KERNEL void computeSurfaceAreaForce(GLOBAL mm_long* RESTRICT bornForce, GLOBAL mixed* RESTRICT energyBuffer, GLOBAL const float4* RESTRICT params, GLOBAL const real* RESTRICT bornRadii) {
     mixed energy = 0;
     for (unsigned int index = GLOBAL_ID; index < NUM_ATOMS; index += GLOBAL_SIZE) {
         real bornRadius = bornRadii[index];
@@ -46,51 +76,169 @@ KERNEL void computeSurfaceAreaForce(GLOBAL mm_long* RESTRICT bornForce, GLOBAL m
 typedef struct {
     real3 pos;
     real bornSum;
-    float radius, scaledRadius, padding;
+    float radius, scaleFactor, descreenRadius, neckFactor, padding;
 } AtomData1;
 
-DEVICE real computeBornSumOneInteraction(AtomData1 atom1, AtomData1 atom2) {
+DEVICE real interpolate2D(real x1, real x2, real y1, real y2, real x, real y,
+                            real fx1y1, real fx2y1, real fx1y2, real fx2y2) {
+    real fxy1 = (x2 - x) / (x2 - x1) * fx1y1 + (x - x1) / (x2 - x1) * fx2y1;
+    real fxy2 = (x2 - x) / (x2 - x1) * fx1y2 + (x - x1) / (x2 - x1) * fx2y2;
+
+    return (y2 - y) / (y2 - y1) * fxy1 + (y - y1) / (y2 - y1) * fxy2;
+}
+
+DEVICE void getBounds(real rho, int* below, int* above) {
+    // Note this function is in Angstroms.
+    real MINIMUM_RADIUS = 0.8f;
+    real SPACING = 0.05f;
+    real calculateIndex = (rho - MINIMUM_RADIUS) / SPACING;
+    *below = (int) floor(calculateIndex);
+    *above = *below + 1;
+    int NUM_POINTS = 45;
+
+    if (*above >= NUM_POINTS) {
+        // Extrapolate up from the top table values.
+        *below = NUM_POINTS - 1;
+        *above = NUM_POINTS - 2;
+    } else if (*below < 0) {
+        // If below is less than 0, extrapolate down from the bottom table values.
+        *below = 0;
+        *above = 1;
+    }
+}
+
+DEVICE void getNeckConstants(real rhoDescreened, real rhoDescreening, real* aij, real *bij,
+    GLOBAL const float* RESTRICT neckRadii,
+    GLOBAL const float* RESTRICT neckA,
+    GLOBAL const float* RESTRICT neckB) {
+
+    // Convert the input radii from nm to A.
+    rhoDescreened *= 10;
+    rhoDescreening *= 10;
+
+    // Determine low and high values for integration
+    int lowI, highI, lowJ, highJ;
+    getBounds(rhoDescreened, &lowI, &highI);
+    getBounds(rhoDescreening, &lowJ, &highJ);
+    // Interpolate/Extrapolate Aij and Bij constant values
+    int NUM_POINTS = 45;
+    int lowOff = lowI * NUM_POINTS;
+    int highOff = highI * NUM_POINTS;
+    *aij = interpolate2D(neckRadii[lowI], neckRadii[highI], neckRadii[lowJ], neckRadii[highJ],
+                               rhoDescreened, rhoDescreening,
+                               neckA[lowOff + lowJ], neckA[highOff + lowJ],
+                               neckA[lowOff + highJ], neckA[highOff + highJ]);
+
+    *bij = interpolate2D(neckRadii[lowI], neckRadii[highI],neckRadii[lowJ], neckRadii[highJ],
+                               rhoDescreened, rhoDescreening,
+                               neckB[lowOff + lowJ], neckB[highOff + lowJ],
+                               neckB[lowOff + highJ], neckB[highOff + highJ]);
+
+    // Never let Aij be negative.
+    if (*aij < 0.0f) {
+        *aij = 0.0f;
+    }
+
+    // Convert aij from A^(-11) to nm^(-11);
+    *aij /= 10;
+    // Convert bij from A to nm.
+    *bij /= 10;
+}
+
+DEVICE real neckDescreen(real r, real radius, real radiusK, real sneck,
+        GLOBAL const float* RESTRICT neckRadii,
+        GLOBAL const float* RESTRICT neckA,
+        GLOBAL const float* RESTRICT neckB) {
+
+    real radiusWater = 0.14f;
+
+    // If atoms are too widely separated there is no neck formed.
+    if (r > radius + radiusK + 2 * radiusWater) {
+        return 0.0f;
+    }
+
+    // Get Aij and Bij based on parameterization by Corrigan et al.
+    real aij, bij;
+    getNeckConstants(radius, radiusK, &aij, &bij, neckRadii, neckA, neckB);
+    real rMinusBij = r - bij;
+    real radiiMinusr = radius + radiusK + 2 * radiusWater - r;
+    real power1 = rMinusBij * rMinusBij * rMinusBij * rMinusBij;
+    real power2 = radiiMinusr * radiiMinusr * radiiMinusr * radiiMinusr;
+
+    // Use Aij and Bij to get neck integral using Equations 13 and 14 from Aguilar/Onufriev 2010 paper
+    // Sneck may be based on the number of heavy atoms bound to the atom being descreened.
+    real PI4_3 = 4 * M_PI / (real) 3;
+    real neckIntegral = PI4_3 * sneck * aij * power1 * power2;
+
+    return neckIntegral;
+}
+
+
+DEVICE real computeBornSumOneInteraction(AtomData1 atom1, AtomData1 atom2,
+        GLOBAL const float* RESTRICT neckRadii,
+        GLOBAL const float* RESTRICT neckA,
+        GLOBAL const float* RESTRICT neckB) {
+
     if (atom1.radius <= 0)
         return 0; // Ignore this interaction
+
+    float sk = atom2.scaleFactor * atom2.descreenRadius;
+    if (sk <= 0.0f)
+        return 0; // No descreening.
+
     real3 delta = atom2.pos - atom1.pos;
     real r2 = dot(delta, delta);
     real r = SQRT(r2);
-    float sk = atom2.scaledRadius;
+    real baseRadius = max(atom1.radius, atom1.descreenRadius) + DESCREEN_OFFSET;
 
-    if (atom1.radius > r + sk)
+    if (baseRadius > r + sk)
         return 0; // No descreening due to atom1 engulfing atom2.
 
     real sk2 = sk*sk;
-    if (atom1.radius+r < sk) {
-        real lik = atom1.radius;
-        real uik = sk - r; 
+    if (baseRadius + r < sk) {
+        real lik = baseRadius;
+        real uik = sk - r;
         atom1.bornSum -= RECIP(uik*uik*uik) - RECIP(lik*lik*lik);
     }
     real uik = r+sk;
     real lik;
-    if (atom1.radius+r < sk)
+    if (baseRadius+r < sk)
         lik = sk-r;
-    else if (r < atom1.radius+sk)
-        lik = atom1.radius;
+    else if (r < baseRadius+sk)
+        lik = baseRadius;
     else
         lik = r-sk;
-    real l2 = lik*lik; 
+    real l2 = lik*lik;
     real l4 = l2*l2;
     real lr = lik*r;
-    real l4r = l4*r; 
+    real l4r = l4*r;
     real u2 = uik*uik;
     real u4 = u2*u2;
-    real ur = uik*r; 
+    real ur = uik*r;
     real u4r = u4*r;
     real term = (3*(r2-sk2)+6*u2-8*ur)/u4r - (3*(r2-sk2)+6*l2-8*lr)/l4r;
-    return term/16;
+    term = term / (real) 16;
+
+    real neck1 = atom1.neckFactor;
+    real neck2 = atom2.neckFactor;
+    real mixedNeckScale = 0.5f * (neck1 + neck2);
+    if (mixedNeckScale > 0 && atom2.scaleFactor > 0) {
+        real ret = neckDescreen(r, baseRadius, atom2.descreenRadius, mixedNeckScale, neckRadii, neckA, neckB);
+        real pi43 = 4 * M_PI / (real) 3;
+        term += ret / pi43;
+    }
+
+    return term;
 }
 
 /**
  * Compute the Born sum.
  */
 KERNEL void computeBornSum(GLOBAL mm_ulong* RESTRICT bornSum, GLOBAL const real4* RESTRICT posq,
-        GLOBAL const float2* RESTRICT params, unsigned int numTiles) {
+        GLOBAL const float4* RESTRICT params, unsigned int numTiles,
+        GLOBAL const float* RESTRICT neckRadii,
+        GLOBAL const float* RESTRICT neckA,
+        GLOBAL const float* RESTRICT neckB) {
     unsigned int totalWarps = (GLOBAL_SIZE)/TILE_SIZE;
     unsigned int warp = (GLOBAL_ID)/TILE_SIZE;
     unsigned int pos = (unsigned int) (warp*(mm_long)numTiles/totalWarps);
@@ -113,22 +261,26 @@ KERNEL void computeBornSum(GLOBAL mm_ulong* RESTRICT bornSum, GLOBAL const real4
             }
             unsigned int atom1 = x*TILE_SIZE + tgx;
             data.pos = trimTo3(posq[atom1]);
-            float2 params1 = params[atom1];
+            float4 params1 = params[atom1];
             data.radius = params1.x;
-            data.scaledRadius = params1.y;
+            data.scaleFactor = params1.y;
+            data.descreenRadius = params1.z;
+            data.neckFactor = params1.w;
             if (pos >= end)
                 ; // This warp is done.
             else if (x == y) {
                 // This tile is on the diagonal.
-
                 localData[LOCAL_ID].pos = data.pos;
                 localData[LOCAL_ID].radius = params1.x;
-                localData[LOCAL_ID].scaledRadius = params1.y;
+                localData[LOCAL_ID].scaleFactor = params1.y;
+                localData[LOCAL_ID].descreenRadius = params1.z;
+                localData[LOCAL_ID].neckFactor = params1.w;
+
                 SYNC_WARPS;
                 for (unsigned int j = 0; j < TILE_SIZE; j++) {
                     int atom2 = y*TILE_SIZE+j;
                     if (atom1 < NUM_ATOMS && atom2 < NUM_ATOMS && atom1 != atom2) {
-                        real bornSum = computeBornSumOneInteraction(data, localData[tbx+j]);
+                        real bornSum = computeBornSumOneInteraction(data, localData[tbx+j], neckRadii, neckA, neckB);
                         data.bornSum += bornSum;
                     }
                 }
@@ -141,9 +293,11 @@ KERNEL void computeBornSum(GLOBAL mm_ulong* RESTRICT bornSum, GLOBAL const real4
                     unsigned int j = y*TILE_SIZE + tgx;
                     real4 tempPosq = posq[j];
                     localData[LOCAL_ID].pos = trimTo3(tempPosq);
-                    float2 tempParams = params[j];
-                    localData[LOCAL_ID].radius = tempParams.x;
-                    localData[LOCAL_ID].scaledRadius = tempParams.y;
+                    float4 paramsJ = params[j];
+                    localData[LOCAL_ID].radius = paramsJ.x;
+                    localData[LOCAL_ID].scaleFactor = paramsJ.y;
+                    localData[LOCAL_ID].descreenRadius = paramsJ.z;
+                    localData[LOCAL_ID].neckFactor = paramsJ.w;
                 }
                 localData[LOCAL_ID].bornSum = 0;
                 SYNC_WARPS;
@@ -154,9 +308,9 @@ KERNEL void computeBornSum(GLOBAL mm_ulong* RESTRICT bornSum, GLOBAL const real4
                 for (unsigned int j = 0; j < TILE_SIZE; j++) {
                     int atom2 = y*TILE_SIZE+tj;
                     if (atom1 < NUM_ATOMS && atom2 < NUM_ATOMS) {
-                        real bornSum = computeBornSumOneInteraction(data, localData[tbx+tj]);
+                        real bornSum = computeBornSumOneInteraction(data, localData[tbx+tj], neckRadii, neckA, neckB);
                         data.bornSum += bornSum;
-                        bornSum = computeBornSumOneInteraction(localData[tbx+tj], data);
+                        bornSum = computeBornSumOneInteraction(localData[tbx+tj], data, neckRadii, neckA, neckB);
                         localData[tbx+tj].bornSum += bornSum;
                     }
                     tj = (tj + 1) & (TILE_SIZE - 1);
@@ -432,65 +586,153 @@ KERNEL void computeGKForces(
  */
 typedef struct {
     real3 pos, force;
-    real radius, scaledRadius, bornRadius, bornForce;
+    real radius, scaleFactor, descreenRadius, neckFactor, bornSum, bornRadius, bornForce;
 } AtomData3;
 
-inline DEVICE AtomData3 loadAtomData3(int atom, GLOBAL const real4* RESTRICT posq, GLOBAL const float2* RESTRICT params,
-        GLOBAL const real* RESTRICT bornRadius, GLOBAL const mm_long* RESTRICT bornForce) {
+inline DEVICE AtomData3 loadAtomData3(int atom, GLOBAL const real4* RESTRICT posq, GLOBAL const float4* RESTRICT params,
+                                      GLOBAL const mm_long* RESTRICT bornSum,
+                                      GLOBAL const real* RESTRICT bornRadius, GLOBAL const mm_long* RESTRICT bornForce) {
     AtomData3 data;
     data.pos = trimTo3(posq[atom]);
     data.bornRadius = bornRadius[atom];
-    float2 params1 = params[atom];
+    data.bornSum = bornSum[atom]/(real) 0x100000000;
+    float4 params1 = params[atom];
     data.radius = params1.x;
-    data.scaledRadius = params1.y;
+    data.scaleFactor = params1.y;
+    data.descreenRadius = params1.z;
+    data.neckFactor = params1.w;
     data.bornForce = bornForce[atom]/(real) 0x100000000;
     return data;
 }
 
-DEVICE void computeBornChainRuleInteraction(AtomData3 atom1, AtomData3 atom2, real3* force) {
-    real third = 1/(real) 3;
-    real pi43 = 4*third*M_PI;
-    real factor = -POW(M_PI, third)*POW((real) 6, 2/(real) 3)/9;
+/**
+ * Use pairwise descreening to compute derivative of the integral of 1/r^6 with respect to r.
+ *
+ * @param r separation distance.
+ * @param r2 separation distance squared.
+ * @param radius base radius of descreened atom.
+ * @param scaledRadius scaled radius descreening atom.
+ * @return the derivative.
+ */
+DEVICE real pairIntegralDerivative(real r, real r2, real radius, real scaledRadius) {
+
+        real de = 0.0f;
+        // Descreen only if the scaledRadius is greater than zero.
+        // and atom I does not engulf atom K.
+        if (scaledRadius > 0.0f && (radius < r + scaledRadius)) {
+            // Atom i is engulfed by atom k.
+            if (radius + r < scaledRadius) {
+                real uik = scaledRadius - r;
+                real uik2 = uik * uik;
+                real uik4 = uik2 * uik2;
+                de = -4.0f * M_PI / uik4;
+            }
+
+            // Lower integration bound depends on atoms sizes and separation.
+            real sk2 = scaledRadius * scaledRadius;
+            if (radius + r < scaledRadius) {
+                // Atom i is engulfed by atom k.
+                real lik = scaledRadius - r;
+                real lik2 = lik * lik;
+                real lik4 = lik2 * lik2;
+                de = de + 0.25f * M_PI * (sk2 - 4.0f * scaledRadius * r + 17.0f * r2) / (r2 * lik4);
+            } else if (r < radius + scaledRadius) {
+                // Atoms are overlapped, begin integration from ri.
+                real lik = radius;
+                real lik2 = lik * lik;
+                real lik4 = lik2 * lik2;
+                de = de + 0.25f * M_PI * (2.0f * radius * radius - sk2 - r2) / (r2 * lik4);
+            } else {
+                // No overlap between atoms.
+                real lik = r - scaledRadius;
+                real lik2 = lik * lik;
+                real lik4 = lik2 * lik2;
+                de = de + 0.25f * M_PI * (sk2 - 4.0f * scaledRadius * r + r2) / (r2 * lik4);
+            }
+
+            // Upper integration bound is always the same.
+            real uik = r + scaledRadius;
+            real uik2 = uik * uik;
+            real uik4 = uik2 * uik2;
+            de = de - 0.25f * M_PI * (sk2 + 4.0f * scaledRadius * r + r2) / (r2 * uik4);
+        }
+        return de;
+}
+
+DEVICE real neckDescreenDerivative(real r, real radius, real radiusK, real sneck,
+                                   GLOBAL const float* RESTRICT neckRadii,
+                                   GLOBAL const float* RESTRICT neckA,
+                                   GLOBAL const float* RESTRICT neckB) {
+
+    real radiusWater = 0.14f;
+
+    if (r > radius + radiusK + 2 * radiusWater) {
+        return 0;
+    }
+
+    // Get Aij and Bij
+    real Aij, Bij;
+    getNeckConstants(radius, radiusK, &Aij, &Bij, neckRadii, neckA, neckB);
+
+    // Use Aij and Bij to get neck value using derivative of Equation 13 from Aguilar/Onufriev 2010 paper
+    real rMinusBij = r - Bij;
+    real rMinusBij3 = rMinusBij * rMinusBij * rMinusBij;
+    real rMinusBij4 = rMinusBij3 * rMinusBij;
+    real radiiMinusr = radius + radiusK + 2 * radiusWater - r;
+    real radiiMinusr3 = radiiMinusr * radiiMinusr * radiiMinusr;
+    real radiiMinusr4 = radiiMinusr3 * radiiMinusr;
+
+    real PI4_3 = 4 * M_PI / (real) 3;
+
+    return 4 * PI4_3 * (sneck * Aij * rMinusBij3 * radiiMinusr4 - sneck * Aij * rMinusBij4 * radiiMinusr3);
+}
+
+DEVICE void computeBornChainRuleInteraction(AtomData3 atom1, AtomData3 atom2, real3* force,
+                                            GLOBAL const float* RESTRICT neckRadii,
+                                            GLOBAL const float* RESTRICT neckA,
+                                            GLOBAL const float* RESTRICT neckB) {
+    real third = 1 / (real) 3;
+    real pi43 = 4 * third * M_PI;
+    real factor = -POW(M_PI, third)*POW((real) 6.0f, (real) 2 * third) / (real) 9;
     real term = pi43/(atom1.bornRadius*atom1.bornRadius*atom1.bornRadius);
-    term = factor/POW(term, 4/(real) 3);
+    term = factor/POW(term, (real) 4 * third);
+
+    if (TANH_RESCALING) {
+        real bornSum = atom1.bornSum * pi43;
+        real rhoi = atom1.radius;
+        real rhoi3 = rhoi * rhoi * rhoi;
+        real rhoi3Psi = rhoi3 * bornSum;
+        real rhoi6Psi2 = rhoi3Psi * rhoi3Psi;
+        real rhoi9Psi3 = rhoi6Psi2 * rhoi3Psi;
+        real rhoi6Psi = rhoi3 * rhoi3 * bornSum;
+        real rhoi9Psi2 = rhoi6Psi2 * rhoi3;
+        real tanhTerm = tanh(BETA0 * rhoi3Psi - BETA1 * rhoi6Psi2 + BETA2 * rhoi9Psi3);
+        real tanh2 = tanhTerm * tanhTerm;
+        real chainRuleTerm = BETA0 * rhoi3 - 2 * BETA1 * rhoi6Psi + 3 * BETA2 * rhoi9Psi2;
+        real recipBigRad = 1 / (real) BIG_RADIUS;
+        real recipBigRad3 = recipBigRad * recipBigRad * recipBigRad;
+        real tanh_constant = pi43 * ((1 / (real) rhoi3) - recipBigRad3);
+        term = term * tanh_constant * chainRuleTerm * (1.0f - tanh2);
+    }
 
+    real de = 0.0f;
     real3 delta = atom2.pos-atom1.pos;
+    real sk = atom2.scaleFactor;
+    if (sk <= 0 || atom1.bornRadius >= BIG_RADIUS || atom2.descreenRadius <= 0) {
+        *force = delta * de;
+        return;
+    }
 
-    float sk = atom2.scaledRadius;
-    real sk2 = sk*sk;
+    real baseRadius = max(atom1.radius, atom1.descreenRadius) + DESCREEN_OFFSET;
     real r2 = dot(delta, delta);
     real r = SQRT(r2);
-    real de = 0;
-
-    if (atom1.radius > r + sk)
-        return; // No descreening due to atom1 engulfing atom2.
-
-    if (atom1.radius+r < sk) {
-        real uik = sk-r;
-        real uik4 = uik*uik;
-        uik4 = uik4*uik4;
-        de = -4*M_PI/uik4;
-        real lik = sk - r;
-        real lik4 = lik*lik;
-        lik4 = lik4*lik4;
-        de += 0.25f*M_PI*(sk2-4*sk*r+17*r2)/(r2*lik4);
-    }
-    else if (r < atom1.radius+sk) {
-        real lik = atom1.radius;
-        real lik4 = lik*lik;
-        lik4 = lik4*lik4;
-        de += 0.25f*M_PI*(2*atom1.radius*atom1.radius-sk2-r2)/(r2*lik4);
-    }
-    else {
-        real lik = r-sk;
-        real lik4 = lik*lik;
-        lik4 = lik4*lik4;
-        de += 0.25f*M_PI*(sk2-4*sk*r+r2)/(r2*lik4);
+    de = pairIntegralDerivative(r, r2, baseRadius, sk * atom2.descreenRadius);
+
+    real mixedNeckScale = 0.5f * (atom1.neckFactor + atom2.neckFactor);
+    if (mixedNeckScale > 0) {
+        de += neckDescreenDerivative(r, baseRadius, atom2.descreenRadius, mixedNeckScale, neckRadii, neckA, neckB);
     }
-    real uik = r+sk;
-    real uik4 = uik*uik;
-    uik4 = uik4*uik4;
-    de -= 0.25f*M_PI*(sk2+4*sk*r+r2)/(r2*uik4);
+
     real dbr = term*de/r;
     de = dbr*atom1.bornForce;
     *force = delta*de;
@@ -501,7 +743,12 @@ DEVICE void computeBornChainRuleInteraction(AtomData3 atom1, AtomData3 atom2, re
  */
 KERNEL void computeChainRuleForce(
         GLOBAL mm_ulong* RESTRICT forceBuffers, GLOBAL const real4* RESTRICT posq, unsigned int startTileIndex, unsigned int numTileIndices,
-        GLOBAL const float2* RESTRICT params, GLOBAL const real* RESTRICT bornRadii, GLOBAL const mm_long* RESTRICT bornForce) {
+        GLOBAL const float4* RESTRICT params,
+        GLOBAL const float* RESTRICT neckRadii,
+        GLOBAL const float* RESTRICT neckA,
+        GLOBAL const float* RESTRICT neckB,
+        GLOBAL const mm_long* RESTRICT bornSum,
+        GLOBAL const real* RESTRICT bornRadii, GLOBAL const mm_long* RESTRICT bornForce) {
     unsigned int totalWarps = (GLOBAL_SIZE)/TILE_SIZE;
     unsigned int warp = (GLOBAL_ID)/TILE_SIZE;
     const unsigned int numTiles = numTileIndices;
@@ -522,17 +769,19 @@ KERNEL void computeChainRuleForce(
                 x = (pos-y*NUM_BLOCKS+y*(y+1)/2);
             }
             unsigned int atom1 = x*TILE_SIZE + tgx;
-            AtomData3 data = loadAtomData3(atom1, posq, params, bornRadii, bornForce);
+            AtomData3 data = loadAtomData3(atom1, posq, params, bornSum, bornRadii, bornForce);
             data.force = make_real3(0);
             if (pos >= end)
                 ; // This warp is done.
             else if (x == y) {
                 // This tile is on the diagonal.
-
                 localData[LOCAL_ID].pos = data.pos;
                 localData[LOCAL_ID].radius = data.radius;
-                localData[LOCAL_ID].scaledRadius = data.scaledRadius;
+                localData[LOCAL_ID].scaleFactor = data.scaleFactor;
+                localData[LOCAL_ID].descreenRadius = data.descreenRadius;
+                localData[LOCAL_ID].neckFactor = data.neckFactor;
                 localData[LOCAL_ID].bornRadius = data.bornRadius;
+                localData[LOCAL_ID].bornSum = data.bornSum;
                 localData[LOCAL_ID].bornForce = data.bornForce;
                 localData[LOCAL_ID].force = make_real3(0);
                 SYNC_WARPS;
@@ -543,7 +792,7 @@ KERNEL void computeChainRuleForce(
                     int atom2 = y*TILE_SIZE+j;
                     if (atom1 != atom2 && atom1 < NUM_ATOMS && atom2 < NUM_ATOMS) {
                         real3 tempForce;
-                        computeBornChainRuleInteraction(data, localData[tbx+j], &tempForce);
+                        computeBornChainRuleInteraction(data, localData[tbx+j], &tempForce, neckRadii, neckA, neckB);
                         data.force -= tempForce;
                         localData[tbx+j].force += tempForce;
                     }
@@ -557,7 +806,7 @@ KERNEL void computeChainRuleForce(
                 // This is an off-diagonal tile.
 
                 unsigned int j = y*TILE_SIZE + tgx;
-                localData[LOCAL_ID] = loadAtomData3(j, posq, params, bornRadii, bornForce);
+                localData[LOCAL_ID] = loadAtomData3(j, posq, params, bornSum, bornRadii, bornForce);
                 localData[LOCAL_ID].force = make_real3(0);
                 SYNC_WARPS;
                 unsigned int tj = tgx;
@@ -565,10 +814,10 @@ KERNEL void computeChainRuleForce(
                     int atom2 = y*TILE_SIZE+tj;
                     if (atom1 < NUM_ATOMS && atom2 < NUM_ATOMS) {
                         real3 tempForce;
-                        computeBornChainRuleInteraction(data, localData[tbx+tj], &tempForce);
+                        computeBornChainRuleInteraction(data, localData[tbx+tj], &tempForce, neckRadii, neckA, neckB);
                         data.force -= tempForce;
                         localData[tbx+tj].force += tempForce;
-                        computeBornChainRuleInteraction(localData[tbx+tj], data, &tempForce);
+                        computeBornChainRuleInteraction(localData[tbx+tj], data, &tempForce, neckRadii, neckA, neckB);
                         data.force += tempForce;
                         localData[tbx+tj].force -= tempForce;
                     }
diff --git a/plugins/amoeba/platforms/common/src/kernels/amoebaVdwForce2.cc b/plugins/amoeba/platforms/common/src/kernels/amoebaVdwForce2.cc
index fa4bc34687..d01e50410b 100644
--- a/plugins/amoeba/platforms/common/src/kernels/amoebaVdwForce2.cc
+++ b/plugins/amoeba/platforms/common/src/kernels/amoebaVdwForce2.cc
@@ -7,7 +7,7 @@
     real tempForce = 0.0f;
     float2 pairSigmaEpsilon = sigmaEpsilon[atomType1+atomType2*NUM_TYPES];
     real sigma = pairSigmaEpsilon.x;
-    real epsilon = pairSigmaEpsilon.y;
+    real epsilon = pairSigmaEpsilon.y * scaleFactor1 * scaleFactor2;
     real softcore = 0.0f;
 #if VDW_ALCHEMICAL_METHOD == 1
     if (isAlchemical1 != isAlchemical2) { 
diff --git a/plugins/amoeba/platforms/common/src/kernels/amoebaWcaForce.cc b/plugins/amoeba/platforms/common/src/kernels/amoebaWcaForce.cc
index 58ef0891d8..0126b77847 100644
--- a/plugins/amoeba/platforms/common/src/kernels/amoebaWcaForce.cc
+++ b/plugins/amoeba/platforms/common/src/kernels/amoebaWcaForce.cc
@@ -29,10 +29,145 @@ DEVICE void initParticleParameters(float radius, float epsilon, real* rmixo, rea
     *rmixh = 2*(rminh2*RMINH + radius2*radius) / (rminh2+radius2);
 }
 
+DEVICE real integralBeforeRMin(real eps, real r, real r2, real sk2,
+        real lik2, real lik3, real lik4, real uik2, real uik3, real uik4) {
+        return -eps * (4 * M_PI / (48 * r) * (3 * (lik4 - uik4) - 8 * r * (lik3 - uik3) + 6 * (r2 - sk2) * (lik2 - uik2)));
+}
+
+DEVICE real integralBeforeRminDerivative(real ri, real eps, real rmin, real r, real r2,
+                                         real r3, real sk, real sk2, real lik, real lik2,
+                                         real lik3, real uik, real uik2, real uik3) {
+    real dl;
+    if (ri > r - sk) {
+        dl = (-lik2 + 2 * r2 + 2 * sk2) * lik2;
+    } else {
+        dl = (-lik3 + 4 * lik2 * r - 6 * lik * r2 + 2 * lik * sk2 + 4 * r3 - 4 * r * sk2) * lik;
+    }
+    real du;
+    if (r + sk > rmin) {
+        du = -(-uik2 + 2 * r2 + 2 * sk2) * uik2;
+    } else {
+        du = -(-uik3 + 4 * uik2 * r - 6 * uik * r2 + 2 * uik * sk2 + 4 * r3 - 4 * r * sk2) * uik;
+    }
+    return -eps * M_PI * (dl + du) / (4 * r2);
+}
+
+DEVICE real integralAfterRmin(real eps, real rmin7, real r, real r2, real sk2,
+                               real lik, real lik2, real lik3, real lik4, real lik5, real lik10,
+                                real lik11, real lik12, real uik, real uik2, real uik3, real uik4,
+                                real uik5, real uik10, real uik11, real uik12) {
+    real er7 = eps * rmin7;
+    real term = 4 * M_PI / (120 * r * lik5 * uik5)
+            * (15 * uik * lik * r * (uik4 - lik4)
+               - 10 * uik2 * lik2 * (uik3 - lik3)
+               + 6 * (sk2 - r2) * (uik5 - lik5));
+    real term2 = 4 * M_PI / (2640 * r * lik12 * uik12)
+             * (120 * uik * lik * r * (uik11 - lik11)
+                - 66 * uik2 * lik2 * (uik10 - lik10)
+                + 55 * (sk2 - r2) * (uik12 - lik12));
+    real idisp = -2 * er7 * term;
+    real irep = er7 * rmin7 * term2;
+    return irep + idisp;
+}
+
+DEVICE real integralAfterRminDerivative(real ri, real eps, real rmin, real rmin7, real rmax,
+                                        real r, real r2, real r3, real sk, real sk2, real lik,
+                                        real lik2, real lik3, real lik5, real lik6, real lik12,
+                                        real lik13, real uik, real uik2, real uik3, real uik6,
+                                        real uik13) {
+    real er7 = eps * rmin7;
+    real lowerTerm = lik2 * r + r3 - r * sk2;
+    real upperTerm = uik2 * r + r3 - r * sk2;
+
+    real dl;
+    if (ri > r - sk || rmax < rmin) {
+        dl = -(-5 * lik2 + 3 * r2 + 3 * sk2) / lik5;
+    } else {
+        dl = (5 * lik3 - 33 * lik * r2 - 3 * lik * sk2 + 15 * lowerTerm) / lik6;
+    }
+    real du = -(5 * uik3 - 33 * uik * r2 - 3 * uik * sk2 + 15 * upperTerm) / uik6;
+    real de = -2 * M_PI * er7 * (dl + du) / (15 * r2);
+
+    if (ri > r - sk || rmax < rmin) {
+        dl = -(-6 * lik2 + 5 * r2 + 5 * sk2) / lik12;
+    } else {
+        dl = (6 * lik3 - 125 * lik * r2 - 5 * lik * sk2 + 60 * lowerTerm) / lik13;
+    }
+    du = -(6 * uik3 - 125 * uik * r2 - 5 * uik * sk2 + 60 * upperTerm) / uik13;
+    de += M_PI * er7 * rmin7 * (dl + du) / (60 * r2);
+    return de;
+}
+
+DEVICE real interact(real factor, real ri, real sk, real rmix, real emix,
+                     real r, real r2, real r3, real3 *force) {
+    real sum = 0;
+    // Nothing to do if the integral begins beyond r + sk (i.e. atom k does not exclude solvent)
+    if (ri < r + sk) {
+        // Zero out the derivative contribution of atom k.
+        real de = 0;
+        real sk2 = sk * sk;
+        // Compute the maximum of 1) the beginning of the integral and 2) closest edge of atom K.
+        real iStart = ri > r - sk ? ri : r - sk;
+        // Use this as the lower limit for integrating the constant eps value below Rmin.
+        real lik = iStart;
+        // Interaction with water from lik to Rmin; nothing to do if the lower limit is greater than Rmin.
+        if (lik < rmix) {
+            real lik2 = lik * lik;
+            real lik3 = lik2 * lik;
+            real lik4 = lik3 * lik;
+            // Upper limit is the minimum of Rmin and the farthest edge of atom K.
+            real uik = r + sk < rmix ? r + sk : rmix;
+            real uik2 = uik * uik;
+            real uik3 = uik2 * uik;
+            real uik4 = uik3 * uik;
+            sum = integralBeforeRMin(emix, r, r2, sk2, lik2, lik3, lik4, uik2, uik3, uik4);
+            de = integralBeforeRminDerivative(ri, emix, rmix, r, r2, r3, sk, sk2, lik, lik2, lik3, uik, uik2, uik3);
+        }
+        // Upper limit the variable part of Uwca always the farthest edge of atom K.
+        real uik = r + sk;
+        // Interaction with water beyond Rmin, from lik to uik = r + sk.
+        if (uik > rmix) {
+            // Start the integral at the max of 1) iStart and 2) Rmin.
+            lik = iStart > rmix ? iStart : rmix;
+            real lik2 = lik * lik;
+            real lik3 = lik2 * lik;
+            real lik4 = lik3 * lik;
+            real lik5 = lik4 * lik;
+            real lik6 = lik5 * lik;
+            real lik10 = lik5 * lik5;
+            real lik11 = lik10 * lik;
+            real lik12 = lik11 * lik;
+            real uik2 = uik * uik;
+            real uik3 = uik2 * uik;
+            real uik4 = uik3 * uik;
+            real uik5 = uik4 * uik;
+            real uik10 = uik5 * uik5;
+            real uik11 = uik10 * uik;
+            real uik12 = uik11 * uik;
+            real rmix3 = rmix * rmix * rmix;
+            real rmix7 = rmix3 * rmix3 * rmix;
+            sum += integralAfterRmin(emix, rmix7, r, r2, sk2, lik, lik2, lik3, lik4, lik5, lik10, lik11, lik12, uik,
+                    uik2, uik3, uik4, uik5, uik10, uik11, uik12);
+            real lik13 = lik12 * lik;
+            real uik6 = uik5 * uik;
+            real uik13 = uik12 * uik;
+            de += integralAfterRminDerivative(ri, emix, rmix, rmix7, iStart, r, r2, r3, sk, sk2, lik, lik2, lik3,
+                    lik5, lik6, lik12, lik13, uik, uik2, uik3, uik6, uik13);
+        }
+        // Increment the individual dispersion gradient components.
+        de *= factor / r;
+        (*force).x += de;
+        (*force).y += de;
+        (*force).z += de;
+    }
+    return factor * sum;
+}
+
+
 DEVICE void computeOneInteraction(AtomData atom1, AtomData atom2, real rmixo, real rmixh, real emixo, real emixh, real3* force, real* energy) {
     // get deltaR and r between 2 atoms
-    
-    *force = atom2.pos - atom1.pos;
+
+    *force = atom1.pos - atom2.pos;
     real r2 = dot(*force, *force);
     if (r2 <= 0) {
         *force = make_real3(0);
@@ -42,152 +177,28 @@ DEVICE void computeOneInteraction(AtomData atom1, AtomData atom2, real rmixo, re
     real rI = RSQRT(r2);
     real r = RECIP(rI);
 
-    real sk = atom2.radius*SHCTD;
-    real sk2 = sk*sk;
-    if (atom1.radius >= (r+sk)) {
-        *force = make_real3(0);
-        *energy = 0;
-        return;
-    }
-
-    real rmax = atom1.radius > (r - sk) ? atom1.radius : (r - sk);
-    real lik = rmax;
-    real lik2 = lik*lik;
-    real lik3 = lik2*lik;
-    real lik4 = lik2*lik2;
- 
-    real uik = (r+sk) < rmixo ? (r+sk) : rmixo;
-    real uik2 = uik*uik;
-    real uik3 = uik2*uik;
-    real uik4 = uik2*uik2;
-
-    real term = 4*M_PI/(48*r)*(3*(lik4-uik4) - 8*r*(lik3-uik3) + 6*(r2-sk2)*(lik2-uik2));
+    real xr = (*force).x;
+    real yr = (*force).y;
+    real zr = (*force).z;
+    real r3 = r2 * r;
 
-    real r3 = r2*r;
-    real dl1 = lik2*(-lik2 + 2*(r2 + sk2));
-    real dl2 = lik*(-lik3 + 4*lik2*r - 6*lik*r2 + 2*lik*sk2 + 4*r3 - 4*r*sk2);
-    real dl = atom1.radius > (r-sk)? dl1 : dl2;
+    real sk = atom2.radius * SHCTD;
 
-    real du1 = uik2*(-uik2 + 2*(r2 + sk2));
-    real du2 = uik*(-uik3 + 4*uik2*r - 2*uik*(3*r2 - sk2) + 4*r*(r2 - sk2));
-    real du = (r+sk) > rmixo ? -du1 : -du2;
+    // Start of integration of dispersion for atom i with water oxygen.
+    real riO = rmixo * 0.5f + DISPOFF;
+    real nO = 1.0f;
 
-    real mask2 = lik < rmixo ? 1 : 0;
-    real sum = -mask2*(emixo*term);
-    real de = -mask2*emixo*M_PI*(dl+du)/(4*r2);
+    // Start of integration of dispersion for atom i with water hydrogen.
+    real riH = rmixh * 0.5f + DISPOFF;
+    real nH = 2.0f;
 
-    uik = (r+sk) < rmixh ? (r+sk) : rmixh;
-    uik2 = uik*uik;
-    uik3 = uik2*uik;
-    uik4 = uik2*uik2;
+    *force = make_real3(0);
+    *energy = interact(nO, riO, sk, rmixo, emixo, r, r2, r3, force) +
+             interact(nH, riH, sk, rmixh, emixh, r, r2, r3, force);
 
-    term = (M_PI)/ (12*r) * (3*(lik4-uik4) - 8*r*(lik3-uik3) + 6*(r2-sk2)*(lik2-uik2));
-
-    dl1 = lik2*(-lik2 + 2*r2 + 2*sk2);
-    dl2 = lik*(-lik3 + 4*lik2*r - 6*lik*r2 + 2*lik*sk2 + 4*r3 - 4*r*sk2);
-    dl = atom1.radius > (r-sk) ? dl1 : dl2;
-
-    du1 = -uik2*(-uik2 + 2*r2 + 2*sk2);
-    du2 = -uik*(-uik3 + 4*uik2*r - 6*uik*r2 + 2*uik*sk2 + 4*r3 - 4*r*sk2);
-    du = (r+sk) > rmixh ? du1 : du2;
-
-    mask2 = lik < rmixh ? 1 : 0;
-    sum -= mask2*(2*emixh*term);
-    de -= mask2*(2*emixh*M_PI*(dl+du)/(4*r2));
-
-    uik = r + sk;
-    uik2 = uik*uik;
-    uik3 = uik2*uik;
-    uik4 = uik2*uik2;
-    real uik5 = uik4*uik;
-    real uik6 = uik3*uik3;
-    real uik10 = uik5*uik5;
-    real uik11 = uik10*uik;
-    real uik12 = uik6*uik6;
-    real uik13 = uik12*uik;
-
-    lik = rmax > rmixo ? rmax : rmixo;
-    lik2 = lik*lik;
-    lik3 = lik2*lik;
-    lik4 = lik2*lik2;
-    real lik5 = lik4*lik;
-    real lik6 = lik3*lik3;
-    real lik10 = lik5*lik5;
-    real lik11 = lik10*lik;
-    real lik12 = lik6*lik6;
-    real lik13 = lik12*lik;
-
-    term = 4*M_PI/(120*r*lik5*uik5)*(15*uik*lik*r*(uik4-lik4) - 10*uik2*lik2*(uik3-lik3) + 6*(sk2-r2)*(uik5-lik5));
-    dl1 = (-5*lik2 + 3*r2 + 3*sk2)/lik5;
-    dl2 = (5*lik3 - 33*lik*r2 - 3*lik*sk2 + 15*(lik2*r+r3-r*sk2))/lik6;
-    dl = (atom1.radius > (r-sk)) || (rmax < rmixo) ? -dl1 : dl2;
-
-    du = (-5*uik3 + 33*uik*r2 + 3*uik*sk2 - 15*(uik2*r+r3-r*sk2))/uik6;
-
-    real rmixo7 = rmixo*rmixo*rmixo;
-    rmixo7 = rmixo7*rmixo7*rmixo;
-    real ao = emixo*rmixo7;
-
-    real idisp = -2*ao*term;
-    mask2 = uik > rmixo ? 1 : 0;
-
-    de -= mask2*(2*ao*M_PI*(dl + du)/(15*r2));
-
-    term = 4*M_PI/(2640*r*lik12*uik12) * (120*uik*lik*r*(uik11-lik11) - 66*uik2*lik2*(uik10-lik10) + 55*(sk2-r2)*(uik12-lik12));
-
-    dl1 = (6*lik2 - 5*r2 - 5*sk2)/lik12;
-    dl2 = (6*lik3 - 125*lik*r2 - 5*lik*sk2 + 60*(lik2*r+r3-r*sk2))/lik13;
-    dl = (atom1.radius > (r-sk)) || (rmax < rmixo) ? dl1 : dl2;
-
-    du = (-6*uik3 + 125*uik*r2 + 5*uik*sk2 - 60*(uik2*r+r3-r*sk2))/uik13;
-
-    de += mask2*(ao*rmixo7*M_PI*(dl + du)/(60*r2));
-    real irep = ao*rmixo7*term;
-    sum += mask2*(irep + idisp);
-
-    lik = rmax > rmixh ? rmax : rmixh;
-    lik2 = lik*lik;
-    lik3 = lik2*lik;
-    lik4 = lik2*lik2;
-    lik5 = lik4*lik;
-    lik6 = lik3*lik3;
-    lik10 = lik5*lik5;
-    lik11 = lik10*lik;
-    lik12 = lik6*lik6;
-    lik13 = lik12*lik;
-
-    term = 4*M_PI / (120*r*lik5*uik5) * (15*uik*lik*r*(uik4-lik4) - 10*uik2*lik2*(uik3-lik3) + 6*(sk2-r2)*(uik5-lik5));
-
-    dl1 = (-5*lik2 + 3*r2 + 3*sk2)/lik5;
-    dl2 = (5*lik3 - 33*lik*r2 - 3*lik*sk2+ 15*(lik2*r+r3-r*sk2))/lik6;
-    dl = (atom1.radius > (r-sk)) || (rmax < rmixh) ? -dl1 : dl2;
-
-    du = -(5*uik3 - 33*uik*r2 - 3*uik*sk2 + 15*(uik2*r+r3-r*sk2))/uik6;
-
-    real rmixh7 = rmixh*rmixh*rmixh;
-    rmixh7 = rmixh7*rmixh7*rmixh;
-    real ah = emixh*rmixh7;
-
-    idisp = -4*ah*term;
-
-    mask2 = uik > rmixh ? 1 : 0;
-    de -= mask2*(4*ah*M_PI*(dl + du)/(15*r2));
-
-    term = 4*M_PI / (2640*r*lik12*uik12) * (120*uik*lik*r*(uik11-lik11) - 66*uik2*lik2*(uik10-lik10) + 55*(sk2-r2)*(uik12-lik12));
-
-    dl1 = -(-6*lik2 + 5*r2 + 5*sk2)/lik12;
-    dl2 = (6*lik3 - 125*lik*r2 - 5*lik*sk2 + 60*(lik2*r+r3-r*sk2))/lik13;
-    dl = ((atom1.radius > (r-sk)) || (rmax < rmixh)) ? dl1 : dl2;
-
-    du = -(6*uik3 - 125*uik*r2 -5*uik*sk2 + 60*(uik2*r+r3-r*sk2))/uik13;
-    irep = 2*ah*rmixh7*term;
-
-    de += mask2*(ah*rmixh7*M_PI*(dl+du)/(30*r2));
-    sum += mask2*(irep+idisp);
-
-    *energy = sum;
-    de *= -AWATER*rI;
-    *force *= de;
+    (*force).x *= AWATER * xr;
+    (*force).y *= AWATER * yr;
+    (*force).z *= AWATER * zr;
 }
 
 /**
@@ -263,4 +274,4 @@ KERNEL void computeWCAForce(GLOBAL mm_ulong* RESTRICT forceBuffers, GLOBAL mixed
         pos++;
     } while (pos < end);
     energyBuffer[GLOBAL_ID] -= AWATER*energy;
-}
\ No newline at end of file
+}
diff --git a/plugins/amoeba/platforms/reference/src/AmoebaReferenceKernels.cpp b/plugins/amoeba/platforms/reference/src/AmoebaReferenceKernels.cpp
index f2ce6e8c41..af2c258705 100644
--- a/plugins/amoeba/platforms/reference/src/AmoebaReferenceKernels.cpp
+++ b/plugins/amoeba/platforms/reference/src/AmoebaReferenceKernels.cpp
@@ -294,19 +294,18 @@ AmoebaReferenceMultipoleForce* ReferenceCalcAmoebaMultipoleForceKernel::setupAmo
         amoebaReferenceGeneralizedKirkwoodForce->setSurfaceAreaFactor(gkKernel->getSurfaceAreaFactor());
         amoebaReferenceGeneralizedKirkwoodForce->setIncludeCavityTerm(gkKernel->getIncludeCavityTerm());
         amoebaReferenceGeneralizedKirkwoodForce->setDirectPolarization(gkKernel->getDirectPolarization());
-
-        vector<double> parameters; 
-        gkKernel->getAtomicRadii(parameters);
-        amoebaReferenceGeneralizedKirkwoodForce->setAtomicRadii(parameters);
-
-        gkKernel->getScaleFactors(parameters);
-        amoebaReferenceGeneralizedKirkwoodForce->setScaleFactors(parameters);
-
-        gkKernel->getCharges(parameters);
-        amoebaReferenceGeneralizedKirkwoodForce->setCharges(parameters);
+        amoebaReferenceGeneralizedKirkwoodForce->setTanhRescaling(gkKernel->getTanhRescaling());
+        double beta0, beta1, beta2;
+        gkKernel->getTanhParameters(beta0, beta1, beta2);
+        amoebaReferenceGeneralizedKirkwoodForce->setTanhParameters(beta0, beta1, beta2);
+        amoebaReferenceGeneralizedKirkwoodForce->setDescreenOffset(gkKernel->getDescreenOffset());
+        amoebaReferenceGeneralizedKirkwoodForce->setAtomicRadii(gkKernel->getAtomicRadii());
+        amoebaReferenceGeneralizedKirkwoodForce->setScaleFactors(gkKernel->getScaleFactors());
+        amoebaReferenceGeneralizedKirkwoodForce->setCharges(gkKernel->getCharges());
+        amoebaReferenceGeneralizedKirkwoodForce->setDescreenRadii(gkKernel->getDescreenRadii());
+        amoebaReferenceGeneralizedKirkwoodForce->setNeckFactors(gkKernel->getNeckFactors());
 
         // calculate Grycuk Born radii
-
         vector<Vec3>& posData   = extractPositions(context);
         amoebaReferenceGeneralizedKirkwoodForce->calculateGrycukBornRadii(posData);
 
@@ -534,6 +533,20 @@ int ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getIncludeCavityTerm() co
     return includeCavityTerm;
 }
 
+bool ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getTanhRescaling() const {
+    return tanhRescaling;
+}
+
+void ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getTanhParameters(double &b0, double &b1, double &b2) const {
+    b0 = beta0;
+    b1 = beta1;
+    b2 = beta2;
+}
+
+double ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getDescreenOffset() const {
+    return descreenOffset;
+}
+
 int ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getDirectPolarization() const {
     return directPolarization;
 }
@@ -558,19 +571,24 @@ double ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getSurfaceAreaFactor()
     return surfaceAreaFactor;
 }
 
-void ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getAtomicRadii(vector<double>& outputAtomicRadii) const {
-    outputAtomicRadii.resize(atomicRadii.size());
-    copy(atomicRadii.begin(), atomicRadii.end(), outputAtomicRadii.begin());
+const vector<double>& ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getAtomicRadii() const {
+    return atomicRadii;
+}
+
+const vector<double>& ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getCharges() const {
+    return charges;
+}
+
+const vector<double>& ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getScaleFactors() const {
+    return scaleFactors;
 }
 
-void ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getScaleFactors(vector<double>& outputScaleFactors) const {
-    outputScaleFactors.resize(scaleFactors.size());
-    copy(scaleFactors.begin(), scaleFactors.end(), outputScaleFactors.begin());
+const vector<double>& ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getDescreenRadii() const {
+    return descreenRadii;
 }
 
-void ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getCharges(vector<double>& outputCharges) const {
-    outputCharges.resize(charges.size());
-    copy(charges.begin(), charges.end(), outputCharges.begin());
+const vector<double>& ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::getNeckFactors() const {
+    return neckFactors;
 }
 
 void ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::initialize(const System& system, const AmoebaGeneralizedKirkwoodForce& force) {
@@ -593,13 +611,13 @@ void ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::initialize(const System&
     numParticles = system.getNumParticles();
 
     for (int ii = 0; ii < numParticles; ii++) {
-
-        double particleCharge, particleRadius, scalingFactor;
-        force.getParticleParameters(ii, particleCharge, particleRadius, scalingFactor);
+        double particleCharge, particleRadius, scalingFactor, descreenRadius, neckFactor;
+        force.getParticleParameters(ii, particleCharge, particleRadius, scalingFactor, descreenRadius, neckFactor);
         atomicRadii.push_back(particleRadius);
         scaleFactors.push_back(scalingFactor);
         charges.push_back(particleCharge);
-
+        descreenRadii.push_back(descreenRadius);
+        neckFactors.push_back(neckFactor);
         // Make sure the charge matches the one specified by the AmoebaMultipoleForce.
 
         double charge2, thole, damping, polarity;
@@ -610,13 +628,20 @@ void ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::initialize(const System&
             throw OpenMMException("AmoebaGeneralizedKirkwoodForce and AmoebaMultipoleForce must specify the same charge for every atom.");
         }
 
-    }   
+    }
     includeCavityTerm  = force.getIncludeCavityTerm();
+    tanhRescaling      = force.getTanhRescaling();
+    double b0, b1, b2;
+    force.getTanhParameters(b0, b1, b2);
+    beta0 = b0;
+    beta1 = b1;
+    beta2 = b2;
+    descreenOffset     = force.getDescreenOffset();
     soluteDielectric   = force.getSoluteDielectric();
     solventDielectric  = force.getSolventDielectric();
-    dielectricOffset   = 0.009;
-    probeRadius        = force.getProbeRadius(), 
-    surfaceAreaFactor  = force.getSurfaceAreaFactor(); 
+    dielectricOffset   = force.getDielectricOffset();
+    probeRadius        = force.getProbeRadius();
+    surfaceAreaFactor  = force.getSurfaceAreaFactor();
     directPolarization = amoebaMultipoleForce->getPolarizationType() == AmoebaMultipoleForce::Direct ? 1 : 0;
 }
 
@@ -632,11 +657,13 @@ void ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel::copyParametersToContext(
     // Record the values.
 
     for (int i = 0; i < numParticles; ++i) {
-        double particleCharge, particleRadius, scalingFactor;
-        force.getParticleParameters(i, particleCharge, particleRadius, scalingFactor);
+        double particleCharge, particleRadius, scalingFactor, descreenRadius, neckFactor;
+        force.getParticleParameters(i, particleCharge, particleRadius, scalingFactor, descreenRadius, neckFactor);
         atomicRadii[i] = particleRadius;
         scaleFactors[i] = scalingFactor;
         charges[i] = particleCharge;
+        descreenRadii[i] = descreenRadius;
+        neckFactors[i] = neckFactor;
     }
 }
 
diff --git a/plugins/amoeba/platforms/reference/src/AmoebaReferenceKernels.h b/plugins/amoeba/platforms/reference/src/AmoebaReferenceKernels.h
index 4fb401eeba..5c0b9f6119 100644
--- a/plugins/amoeba/platforms/reference/src/AmoebaReferenceKernels.h
+++ b/plugins/amoeba/platforms/reference/src/AmoebaReferenceKernels.h
@@ -318,6 +318,25 @@ class ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel : public CalcAmoebaGener
      */
     int getIncludeCavityTerm() const;
 
+    /**
+     *  Get the 'tanh rescaling' flag.
+     *
+     *  @return tanhRescaling
+     */
+    bool getTanhRescaling() const;
+
+    /**
+     * Get Tanh parameters beta0, beta1 and beta2.
+     */
+    void getTanhParameters(double& b0, double& b1, double& b2) const;
+
+    /**
+     *  Get the descreen offset for computing effective Born radii.
+     *
+     *  @return descreenOffset
+     */
+    double getDescreenOffset() const;
+
     /**
      *  Get the number of particles.
      *
@@ -350,10 +369,9 @@ class ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel : public CalcAmoebaGener
     double getSolventDielectric() const;
 
     /**
-     *  Get the dielectric offset.
+     *  Get the dielectric offset for the cavity term.
      *
      *  @return dielectricOffset
-     *
      */
     double getDielectricOffset() const;
 
@@ -375,27 +393,28 @@ class ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel : public CalcAmoebaGener
 
     /**
      *  Get the vector of particle radii.
-     *
-     *  @param atomicRadii vector of atomic radii
-     *
      */
-    void getAtomicRadii(std::vector<double>& atomicRadii) const;
+    const vector<double>& getAtomicRadii() const;
 
     /**
      *  Get the vector of scale factors.
-     *
-     *  @param scaleFactors vector of scale factors
-     *
      */
-    void getScaleFactors(std::vector<double>& scaleFactors) const;
+    const vector<double>& getScaleFactors() const;
 
     /**
      *  Get the vector of charges.
-     *
-     *  @param charges vector of charges
-     *
      */
-    void getCharges(std::vector<double>& charges) const;
+    const vector<double>& getCharges() const;
+
+    /**
+     *  Get the vector of descreening radii.
+     */
+    const vector<double>& getDescreenRadii() const;
+
+    /**
+     *  Get the vector of neck scaling factors.
+     */
+    const vector<double>& getNeckFactors() const;
 
     /**
      * Copy changed parameters over to a context.
@@ -411,12 +430,19 @@ class ReferenceCalcAmoebaGeneralizedKirkwoodForceKernel : public CalcAmoebaGener
     std::vector<double> atomicRadii;
     std::vector<double> scaleFactors;
     std::vector<double> charges;
+    std::vector<double> descreenRadii;
+    std::vector<double> neckFactors;
     double soluteDielectric;
     double solventDielectric;
     double dielectricOffset;
     double probeRadius;
     double surfaceAreaFactor;
     int includeCavityTerm;
+    bool tanhRescaling;
+    double beta0;
+    double beta1;
+    double beta2;
+    double descreenOffset;
     int directPolarization;
     const System& system;
 };
diff --git a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceGeneralizedKirkwoodForce.cpp b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceGeneralizedKirkwoodForce.cpp
index 91ae280569..87dd1ab107 100644
--- a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceGeneralizedKirkwoodForce.cpp
+++ b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceGeneralizedKirkwoodForce.cpp
@@ -22,20 +22,25 @@
  */
 
 #include "AmoebaReferenceGeneralizedKirkwoodForce.h"
+#include "openmm/internal/AmoebaGeneralizedKirkwoodForceImpl.h"
 #include <cmath>
 
 using std::vector;
 using namespace OpenMM;
 
 AmoebaReferenceGeneralizedKirkwoodForce::AmoebaReferenceGeneralizedKirkwoodForce() : _numParticles(0),
-                                                                                      _includeCavityTerm(1),
-                                                                                      _directPolarization(0),
-                                                                                      _soluteDielectric(1.0),
-                                                                                      _solventDielectric(78.3),
-                                                                                      _dielectricOffset(0.009),
-                                                                                      _probeRadius(0.14),
-                                                                                      _surfaceAreaFactor(0.0054) {
-
+                                                                                     _includeCavityTerm(1),
+                                                                                     _directPolarization(0),
+                                                                                     _soluteDielectric(1.0),
+                                                                                     _solventDielectric(78.3),
+                                                                                     _dielectricOffset(0.009),
+                                                                                     _probeRadius(0.14),
+                                                                                     _surfaceAreaFactor(0.0054),
+                                                                                     _tanhRescaling(false),
+                                                                                     _beta0(0.9563),
+                                                                                     _beta1(0.2578),
+                                                                                     _beta2(0.0810),
+                                                                                     _descreenOffset(0.0) {
 }
 
 void AmoebaReferenceGeneralizedKirkwoodForce::setNumParticles(int numParticles) {
@@ -54,6 +59,41 @@ int AmoebaReferenceGeneralizedKirkwoodForce::getIncludeCavityTerm() const {
     return _includeCavityTerm;
 }
 
+void AmoebaReferenceGeneralizedKirkwoodForce::setTanhRescaling(bool tanhRescaling) {
+    _tanhRescaling = tanhRescaling;
+}
+
+bool AmoebaReferenceGeneralizedKirkwoodForce::getTanhRescaling() const {
+    return _tanhRescaling;
+}
+
+/**
+ * Get Tanh parameters beta0, beta1 and beta2.
+ */
+void AmoebaReferenceGeneralizedKirkwoodForce::getTanhParameters(double& b0, double& b1, double& b2) const {
+    b0 = _beta0;
+    b1 = _beta1;
+    b2 = _beta2;
+}
+
+/**
+ * Set the flag signaling whether the solute integral is rescaled by a Tanh function
+ * to account for interstitial spaces.
+*/
+void AmoebaReferenceGeneralizedKirkwoodForce::setTanhParameters(double b0, double b1, double b2) {
+    _beta0 = b0;
+    _beta1 = b1;
+    _beta2 = b2;
+}
+
+void AmoebaReferenceGeneralizedKirkwoodForce::setDescreenOffset(double descreenOffset) {
+    _descreenOffset  = descreenOffset;
+}
+
+double AmoebaReferenceGeneralizedKirkwoodForce::getDescreenOffset() const {
+    return _descreenOffset;
+}
+
 void AmoebaReferenceGeneralizedKirkwoodForce::setDirectPolarization(int directPolarization) {
     _directPolarization = directPolarization;
 }
@@ -127,77 +167,145 @@ void AmoebaReferenceGeneralizedKirkwoodForce::setCharges(const vector<double>& c
     copy(charges.begin(), charges.end(), _charges.begin());
 }
 
+void AmoebaReferenceGeneralizedKirkwoodForce::setDescreenRadii(const vector<double> &descreenRadii) {
+    _descreenRadii.resize(descreenRadii.size());
+    copy(descreenRadii.begin(), descreenRadii.end(), _descreenRadii.begin());
+}
+
+void AmoebaReferenceGeneralizedKirkwoodForce::getDescreenRadii(vector<double> &descreenRadii) const {
+    descreenRadii.resize(_atomicRadii.size());
+    copy(_descreenRadii.begin(), _descreenRadii.end(), descreenRadii.begin());
+}
+
+void AmoebaReferenceGeneralizedKirkwoodForce::setNeckFactors(const vector<double> &neckFactors) {
+    _neckFactors.resize(neckFactors.size());
+    copy(neckFactors.begin(), neckFactors.end(), _neckFactors.begin());
+}
+
+void AmoebaReferenceGeneralizedKirkwoodForce::getNeckFactors(vector<double> &neckFactors) const {
+    neckFactors.resize(_neckFactors.size());
+    copy(_neckFactors.begin(), _neckFactors.end(), neckFactors.begin());
+}
+
 void AmoebaReferenceGeneralizedKirkwoodForce::getGrycukBornRadii(vector<double>& bornRadii) const {
     bornRadii.resize(_bornRadii.size());
     copy(_bornRadii.begin(), _bornRadii.end(), bornRadii.begin());
 }
 
-void AmoebaReferenceGeneralizedKirkwoodForce::calculateGrycukBornRadii(const vector<Vec3>& particlePositions) {
+void AmoebaReferenceGeneralizedKirkwoodForce::getSoluteIntegral(vector<double> &soluteIntegral) const {
+    soluteIntegral.resize(_soluteIntegral.size());
+    copy(_soluteIntegral.begin(), _soluteIntegral.end(), soluteIntegral.begin());
+}
 
-    const double bigRadius = 1000.0;
+void AmoebaReferenceGeneralizedKirkwoodForce::calculateGrycukBornRadii(const vector<Vec3> &particlePositions) {
+
+    // Set the radius to 30 Angstroms (3 nm) if either the base radius is zero, or the
+    // descreening integral is negative.
+    const double MAX_RADIUS = 3.0;
+    const double RECIP_MAX_RADIUS3 = pow(MAX_RADIUS, -3.0);
+    const double PI4_3 = 4.0 / 3.0 * M_PI;
+    const double INVERSE_PI4_3 = 1.0 / PI4_3;
+    const double ONE_THIRD = 1.0 / 3.0;
 
     _bornRadii.resize(_numParticles);
+    _soluteIntegral.resize(_numParticles);
+
     for (unsigned int ii = 0; ii < _numParticles; ii++) {
 
         if (_atomicRadii[ii] <= 0.0) {
-            _bornRadii[ii] = bigRadius;
+            _bornRadii[ii] = MAX_RADIUS;
             continue;
         }
 
+        double integralStartI = max(_atomicRadii[ii], _descreenRadii[ii]) + _descreenOffset;
+
         double bornSum = 0.0;
+        double neckSum = 0.0;
         for (unsigned int jj = 0; jj < _numParticles; jj++) {
 
-            if (ii == jj || _atomicRadii[jj] < 0.0)continue;
-          
-            double xr       = particlePositions[jj][0] - particlePositions[ii][0];
-            double yr       = particlePositions[jj][1] - particlePositions[ii][1];
-            double zr       = particlePositions[jj][2] - particlePositions[ii][2];
+            double sk = _descreenRadii[jj] * _scaleFactors[jj];
+
+            if (ii == jj || integralStartI <= 0.0 || sk <= 0.0) continue;
 
-            double r2       = xr*xr + yr*yr + zr*zr;
-            double r        = sqrt(r2);
+            double xr = particlePositions[jj][0] - particlePositions[ii][0];
+            double yr = particlePositions[jj][1] - particlePositions[ii][1];
+            double zr = particlePositions[jj][2] - particlePositions[ii][2];
 
-            double sk       = _atomicRadii[jj]*_scaleFactors[jj];
+            double r2 = xr * xr + yr * yr + zr * zr;
+            double r = sqrt(r2);
 
             // If atom ii engulfs the descreening atom, then continue.
-            if (_atomicRadii[ii] > r + sk) continue;
-
-            double sk2      = sk*sk;
-
-            if ((_atomicRadii[ii] + r) < sk) {
-                double lik       = _atomicRadii[ii];
-                double uik       = sk - r;  
-                double lik3      = lik*lik*lik;
-                double uik3      = uik*uik*uik;
-                bornSum             -= (1.0/uik3 - 1.0/lik3);
-            }   
-        
-            double uik = r + sk; 
+            if (integralStartI > r + sk) continue;
+
+            double sk2 = sk * sk;
+
+            if ((integralStartI + r) < sk) {
+                double lik = integralStartI;
+                double uik = sk - r;
+                double lik3 = lik * lik * lik;
+                double uik3 = uik * uik * uik;
+                bornSum -= (1.0 / uik3 - 1.0 / lik3);
+            }
+
+            double uik = r + sk;
             double lik;
-            if ((_atomicRadii[ii] + r) < sk) {
-                lik = sk - r;  
-            } else if (r < (_atomicRadii[ii] + sk)) {
-                lik = _atomicRadii[ii];
+            if ((integralStartI + r) < sk) {
+                lik = sk - r;
+            } else if (r < (integralStartI + sk)) {
+                lik = integralStartI;
             } else {
-                lik = r - sk; 
-            }   
-        
-            double l2          = lik*lik; 
-            double l4          = l2*l2;
-            double lr          = lik*r;
-            double l4r         = l4*r;
-        
-            double u2          = uik*uik;
-            double u4          = u2*u2;
-            double ur          = uik*r;
-            double u4r         = u4*r;
-        
-            double term        = (3.0*(r2-sk2) + 6.0*u2 - 8.0*ur)/u4r - (3.0*(r2-sk2) + 6.0*l2 - 8.0*lr)/l4r;
-            bornSum           += term/16.0;
-        
+                lik = r - sk;
+            }
+
+            double l2 = lik * lik;
+            double l4 = l2 * l2;
+            double lr = lik * r;
+            double l4r = l4 * r;
+
+            double u2 = uik * uik;
+            double u4 = u2 * u2;
+            double ur = uik * r;
+            double u4r = u4 * r;
+
+            double term =
+                    (3.0 * (r2 - sk2) + 6.0 * u2 - 8.0 * ur) / u4r
+                    - (3.0 * (r2 - sk2) + 6.0 * l2 - 8.0 * lr) / l4r;
+            bornSum += term / 16.0;
+
+            double mixedNeckScale = 0.5 * (_neckFactors[ii] + _neckFactors[jj]);
+            if (mixedNeckScale > 0.0) {
+                neckSum += AmoebaGeneralizedKirkwoodForceImpl::neckDescreen(r, integralStartI, _descreenRadii[jj], mixedNeckScale);
+            }
+        }
+
+        bornSum = bornSum * PI4_3 + neckSum;
+        _soluteIntegral[ii] = bornSum;
+
+        double baseRadiusI3 = _atomicRadii[ii] * _atomicRadii[ii] * _atomicRadii[ii];
+
+        if (_tanhRescaling) {
+            // Set up tanh function components
+            double rhoi3Psi = baseRadiusI3 * _soluteIntegral[ii];
+            double rhoi6Psi2 = rhoi3Psi * rhoi3Psi;
+            double rhoi9Psi3 = rhoi6Psi2 * rhoi3Psi;
+            // If the output of the tanh function is 1.0, then the Born radius will be MaxBornRadius
+            double tanh_constant = PI4_3 * ((1.0 / baseRadiusI3) - RECIP_MAX_RADIUS3);
+            bornSum = tanh_constant * tanh(_beta0 * rhoi3Psi - _beta1 * rhoi6Psi2 + _beta2 * rhoi9Psi3);
+        }
+
+        bornSum = PI4_3 / baseRadiusI3 - bornSum;
+
+        _bornRadii[ii] = (bornSum <= 0.0) ? MAX_RADIUS : pow(INVERSE_PI4_3 * bornSum, -ONE_THIRD);
+
+        // Born radius must be at least as large as its base radius.
+        if (_bornRadii[ii] < _atomicRadii[ii]) {
+            _bornRadii[ii] = _atomicRadii[ii];
+        }
+
+        // Maximum Born radius is 30.0 Angstroms.
+        if (_bornRadii[ii] > MAX_RADIUS) {
+            _bornRadii[ii] = MAX_RADIUS;
         }
-        bornSum        = 1.0/(_atomicRadii[ii]*_atomicRadii[ii]*_atomicRadii[ii]) - bornSum;
-        _bornRadii[ii] = (bornSum <= 0.0) ? bigRadius : pow(bornSum, -1.0/3.0);
     }
 
-    return;
 }
diff --git a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceGeneralizedKirkwoodForce.h b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceGeneralizedKirkwoodForce.h
index 8debaaf2b0..70a36ffff3 100644
--- a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceGeneralizedKirkwoodForce.h
+++ b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceGeneralizedKirkwoodForce.h
@@ -81,6 +81,53 @@ class AmoebaReferenceGeneralizedKirkwoodForce {
      */
     void setIncludeCavityTerm(int includeCavityTerm);
 
+    /**
+     * Get the tanhRescaling flag, which controls whether tanh rescaling of the solute integral is performed.
+     *
+     * @return tanhRescaling The value tanhRescaling flag.
+     */
+    bool getTanhRescaling() const;
+
+    /**
+     *  Set the tanhRescaling flag.
+     *
+     *  @param tanhRescaling if true, tanh rescaling of the solute integral is performed.
+     */
+    void setTanhRescaling(bool tanhRescaling);
+
+    /**
+     * Get Tanh parameters b0, b1 and b2.
+     *
+     * @param b0 The first tanh parameter.
+     * @param b1 The second tanh parameter.
+     * @param b2 The third tanh parameter.
+    */
+    void getTanhParameters(double& b0, double& b1, double& b2) const;
+
+    /**
+     * Set the the Tanh function parameters that account for interstitial spaces.
+     *
+     * @param b0 The first tanh parameter.
+     * @param b1 The second tanh parameter.
+     * @param b2 The third tanh parameter.
+     */
+    void setTanhParameters(double b0, double b1, double b2);
+
+    /**
+     *  Get the descreen offset for calculating Born radii.
+     *
+     *  @return descreenOffset
+     */
+    double getDescreenOffset() const;
+
+    /**
+     * Set descreen offset for calculating Born radii.
+     *
+     * @param descreenOffset descreen offset
+     *
+     */
+    void setDescreenOffset(double descreenOffset);
+
     /**
      *  Get directPolarization flag 
      *
@@ -128,7 +175,7 @@ class AmoebaReferenceGeneralizedKirkwoodForce {
     void setSolventDielectric(double solventDielectric);
 
     /**
-     *  Get dielectric offset
+     *  Get dielectric offset for the cavity term.
      *
      *  @return dielectricOffset
      *
@@ -136,7 +183,7 @@ class AmoebaReferenceGeneralizedKirkwoodForce {
     double getDielectricOffset() const;
 
     /**
-     * Set dielectric offset
+     * Set dielectric offset for the cavity term.
      *
      * @param dielectricOffset dielectric offset
      *
@@ -215,6 +262,36 @@ class AmoebaReferenceGeneralizedKirkwoodForce {
      */
     void setCharges(const vector<double>& charges);
 
+    /**
+     * Set descreen radii
+     *
+     * @param descreenRadii input vector of descreen radii
+     *
+    */
+    void setDescreenRadii(const vector<double>& descreenRadii);
+
+    /**
+     * Get descreen radii
+     *
+     * @param descreenRadii output vector of descreen radii
+     *
+     */
+    void getDescreenRadii(vector<double>& descreenRadii) const;
+
+    /**
+     * Set neck scale factors
+     *
+     * @param neckFactors input vector of neck scale factors
+     */
+    void setNeckFactors(const vector<double>& neckFactors);
+
+    /**
+     * Get neck scale factors
+     *
+     * @param neckFactors output vector of neck scale factors
+     */
+    void getNeckFactors(vector<double>& neckFactors) const;
+
     /**
      * Calculate Grycuk Born radii
      *
@@ -229,13 +306,23 @@ class AmoebaReferenceGeneralizedKirkwoodForce {
      * @param bornRadii vector of Born radii
      *
      */
-    void getGrycukBornRadii(vector<double>& bornRadii) const;     
+    void getGrycukBornRadii(vector<double>& bornRadii) const;
+
+    /**
+     * Get Grycik Solute Integral (must have called calculateGrycukBornRadii())
+     *
+     * @param bornRadii vector of Born radii
+     *
+     */
+    void getSoluteIntegral(vector<double>& soluteIntegral) const;
 
 private:
 
     int _numParticles;
     int _includeCavityTerm;
     int _directPolarization;
+    bool _tanhRescaling;
+    double _descreenOffset;
 
     double _soluteDielectric;
     double _solventDielectric;
@@ -243,10 +330,17 @@ class AmoebaReferenceGeneralizedKirkwoodForce {
     double _probeRadius;
     double _surfaceAreaFactor;
 
+    double _beta0;
+    double _beta1;
+    double _beta2;
+
     std::vector<double> _atomicRadii;
     std::vector<double> _scaleFactors;
     std::vector<double> _charges;
+    std::vector<double> _descreenRadii;
+    std::vector<double> _neckFactors;
 
+    std::vector<double> _soluteIntegral;
     std::vector<double> _bornRadii;
 
 };
diff --git a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceMultipoleForce.cpp b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceMultipoleForce.cpp
index a464d2b8e0..3e333b534a 100644
--- a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceMultipoleForce.cpp
+++ b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceMultipoleForce.cpp
@@ -23,6 +23,7 @@
  */
 
 #include "AmoebaReferenceMultipoleForce.h"
+#include "openmm/internal/AmoebaGeneralizedKirkwoodForceImpl.h"
 #include "SimTKOpenMMRealType.h"
 #include "jama_svd.h"
 #include <algorithm>
@@ -35,6 +36,7 @@
 // make sure that erf() and erfc() are defined.
 #include "openmm/internal/MSVC_erfc.h"
 
+
 using std::vector;
 using namespace OpenMM;
 
@@ -2117,8 +2119,7 @@ AmoebaReferenceMultipoleForce::UpdateInducedDipoleFieldStruct::UpdateInducedDipo
 AmoebaReferenceGeneralizedKirkwoodMultipoleForce::AmoebaReferenceGeneralizedKirkwoodMultipoleForce(AmoebaReferenceGeneralizedKirkwoodForce* amoebaReferenceGeneralizedKirkwoodForce) :
                AmoebaReferenceMultipoleForce(NoCutoff),
                _amoebaReferenceGeneralizedKirkwoodForce(amoebaReferenceGeneralizedKirkwoodForce),
-               _gkc(2.455)
-{
+               _gkc(2.455), _beta0(0.9563), _beta1(0.2578), _beta2(0.0810) {
 
     double solventDielectric  = _amoebaReferenceGeneralizedKirkwoodForce->getSolventDielectric();
 
@@ -2127,17 +2128,19 @@ AmoebaReferenceGeneralizedKirkwoodMultipoleForce::AmoebaReferenceGeneralizedKirk
     _fq                           =  3.0*(1.0 - solventDielectric)/(2.0 + 3.0*solventDielectric);
 
     _amoebaReferenceGeneralizedKirkwoodForce->getGrycukBornRadii(_bornRadii);
+    _amoebaReferenceGeneralizedKirkwoodForce->getSoluteIntegral(_soluteIntegral);
     _amoebaReferenceGeneralizedKirkwoodForce->getAtomicRadii(_atomicRadii);
-    _amoebaReferenceGeneralizedKirkwoodForce->getScaleFactors(_scaledRadii);
-
-    _includeCavityTerm            = _amoebaReferenceGeneralizedKirkwoodForce->getIncludeCavityTerm();
-    _probeRadius                  = _amoebaReferenceGeneralizedKirkwoodForce->getProbeRadius();
-    _surfaceAreaFactor            = _amoebaReferenceGeneralizedKirkwoodForce->getSurfaceAreaFactor();
-    _dielectricOffset             = _amoebaReferenceGeneralizedKirkwoodForce->getDielectricOffset();
-
-    for (unsigned int ii = 0; ii < _scaledRadii.size(); ii++) {
-        _scaledRadii[ii] *= _atomicRadii[ii];
-    }
+    _amoebaReferenceGeneralizedKirkwoodForce->getScaleFactors(_scaleFactors);
+    _amoebaReferenceGeneralizedKirkwoodForce->getDescreenRadii(_descreenRadii);
+    _amoebaReferenceGeneralizedKirkwoodForce->getNeckFactors(_neckFactors);
+
+    _includeCavityTerm = _amoebaReferenceGeneralizedKirkwoodForce->getIncludeCavityTerm();
+    _probeRadius = _amoebaReferenceGeneralizedKirkwoodForce->getProbeRadius();
+    _surfaceAreaFactor = _amoebaReferenceGeneralizedKirkwoodForce->getSurfaceAreaFactor();
+    _dielectricOffset = _amoebaReferenceGeneralizedKirkwoodForce->getDielectricOffset();
+    _tanhRescaling = _amoebaReferenceGeneralizedKirkwoodForce->getTanhRescaling();
+    _amoebaReferenceGeneralizedKirkwoodForce->getTanhParameters(_beta0, _beta1, _beta2);
+    _descreenOffset = _amoebaReferenceGeneralizedKirkwoodForce->getDescreenOffset();
 }
 
 AmoebaReferenceGeneralizedKirkwoodMultipoleForce::~AmoebaReferenceGeneralizedKirkwoodMultipoleForce()
@@ -2165,6 +2168,12 @@ double AmoebaReferenceGeneralizedKirkwoodMultipoleForce::getDielectricOffset() c
      return _dielectricOffset;
 };
 
+double AmoebaReferenceGeneralizedKirkwoodMultipoleForce::getDescreenOffset() const
+{
+    return _descreenOffset;
+};
+
+
 void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::zeroFixedMultipoleFields()
 {
     this->AmoebaReferenceMultipoleForce::zeroFixedMultipoleFields();
@@ -4060,67 +4069,125 @@ double AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateElectrostatic(
     return energy;
 }
 
-void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateGrycukChainRulePairIxn(const MultipoleParticleData& particleI, const MultipoleParticleData& particleJ,
-                                                                                       const vector<double>& dBorn, vector<Vec3>& forces) const
-{
-    unsigned int iIndex             = particleI.particleIndex;
-    unsigned int jIndex             = particleJ.particleIndex;
+static double neckDescreenDerivative(double r, double radius, double radiusK, double sneck) {
+    // Radius of water in nm.
+    double radiusWater = 0.14;
+
+    if (r > radius + radiusK + 2 * radiusWater) {
+        return 0.0;
+    }
 
-    double pi43                     = (4.0/3.0)*M_PI;
+    // Get Aij and Bij
+    double Aij, Bij;
+    AmoebaGeneralizedKirkwoodForceImpl::getNeckConstants(radius, radiusK, Aij, Bij);
+    // Use Aij and Bij to get neck value using derivative of Equation 13 from Aguilar/Onufriev 2010 paper
+    double rMinusBij = r - Bij;
+    double rMinusBij3 = rMinusBij * rMinusBij * rMinusBij;
+    double rMinusBij4 = rMinusBij3 * rMinusBij;
+    double radiiMinusr = radius + radiusK + 2.0 * radiusWater - r;
+    double radiiMinusr3 = radiiMinusr * radiiMinusr * radiiMinusr;
+    double radiiMinusr4 = radiiMinusr3 * radiiMinusr;
+
+    double PI4_3 = 4.0 * M_PI / 3.0;
+    return 4.0 * PI4_3 * (sneck * Aij * rMinusBij3 * radiiMinusr4 - sneck * Aij * rMinusBij4 * radiiMinusr3);
+}
+
+void AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateGrycukChainRulePairIxn(
+        const MultipoleParticleData &particleI, const MultipoleParticleData &particleJ,
+        const vector<double> &dBorn, vector<Vec3> &forces) const {
+    unsigned int iIndex = particleI.particleIndex;
+    unsigned int jIndex = particleJ.particleIndex;
+
+    if (iIndex == jIndex) return;
+
+    double bornRadiusI = _bornRadii[iIndex];
+
+    const double bigRadius = 3.0;
+    if (bornRadiusI >= bigRadius) return;
+
+    double third = 1.0 / 3.0;
+    double pi43 = 4.0 * third * M_PI;
 
     double lik, uik;
     double lik4, uik4;
-    double factor                   = -pow(M_PI, (1.0/3.0))*pow(6.0,(2.0/3.0))/9.0;
-    double term                     = pi43/(_bornRadii[iIndex]*_bornRadii[iIndex]*_bornRadii[iIndex]);
-          term                      = factor/pow(term, (4.0/3.0));
-
-    Vec3 deltaR                     = particleJ.position - particleI.position;
+    double factor = -pow(M_PI, third) * pow(6.0, 2.0 * third) / 9.0;
+    double term = pi43 / (_bornRadii[iIndex] * _bornRadii[iIndex] * _bornRadii[iIndex]);
+    term = factor / pow(term, 4.0 * third);
+    if (_tanhRescaling) {
+        double rhoi = _atomicRadii[iIndex];
+        double rhoi3 = rhoi * rhoi * rhoi;
+        double rhoi3Psi = rhoi3 * _soluteIntegral[iIndex];
+        double rhoi6Psi2 = rhoi3Psi * rhoi3Psi;
+        double rhoi9Psi3 = rhoi6Psi2 * rhoi3Psi;
+        double rhoi6Psi = rhoi3 * rhoi3 * _soluteIntegral[iIndex];
+        double rhoi9Psi2 = rhoi6Psi2 * rhoi3;
+        double tanhTerm = tanh(_beta0 * rhoi3Psi - _beta1 * rhoi6Psi2 + _beta2 * rhoi9Psi3);
+        double tanh2 = tanhTerm * tanhTerm;
+        double chainRuleTerm = _beta0 * rhoi3 - 2.0 * _beta1 * rhoi6Psi + 3.0 * _beta2 * rhoi9Psi2;
+        double recipBigRad = 1.0 / bigRadius;
+        double recipBigRad3 = recipBigRad * recipBigRad * recipBigRad;
+        double tanh_constant = pi43 * ((1.0 / rhoi3) - recipBigRad3);
+        term = term * tanh_constant * chainRuleTerm * (1.0 - tanh2);
+    }
 
-    double sk                       = _scaledRadii[jIndex];
-    double sk2                      = sk*sk;
-    double r2                       = deltaR.dot(deltaR);
-    double r                        = sqrt(r2);
-    double de                       = 0.0;
+    Vec3 deltaR = particleJ.position - particleI.position;
+    double sk = _scaleFactors[jIndex] * _descreenRadii[jIndex];
+    if (sk <= 0.0) return;
+    double sk2 = sk * sk;
+    double r2 = deltaR.dot(deltaR);
+    double r = sqrt(r2);
+    double de = 0.0;
 
-    // If atom index engulfs the descreening atom, then there is no descreening. 
-    if (_atomicRadii[iIndex] > r + sk) return;
+    double baseRadiusI = max(_atomicRadii[iIndex], _descreenRadii[iIndex]) + _descreenOffset;
+    // If atom index engulfs the descreening atom, then there is no descreening.
+    if (baseRadiusI > r + sk) return;
 
-    if ((_atomicRadii[iIndex] + r) < sk) {
+    if ((baseRadiusI + r) < sk) {
         double uik4;
-        uik                = sk - r;
-        uik4               = uik*uik;
-        uik4               = uik4*uik4;
-        de                 = -4.0*M_PI/uik4;
+        uik = sk - r;
+        uik4 = uik * uik;
+        uik4 = uik4 * uik4;
+        de = -4.0 * M_PI / uik4;
     }
 
-    if ((_atomicRadii[iIndex] + r) < sk) {
-        lik          = sk - r;
-        lik4         = lik*lik;
-        lik4         = lik4*lik4;
-        de          += 0.25*M_PI*(sk2-4.0*sk*r+17.0*r2)/ (r2*lik4);
-    } else if (r < (_atomicRadii[iIndex] +sk)) {
-        lik          = _atomicRadii[iIndex];
-        lik4         = lik*lik;
-        lik4         = lik4*lik4;
-        de          += 0.25*M_PI*(2.0*_atomicRadii[iIndex]*_atomicRadii[iIndex]-sk2-r2)/ (r2*lik4);
+    if ((baseRadiusI + r) < sk) {
+        lik = sk - r;
+        lik4 = lik * lik;
+        lik4 = lik4 * lik4;
+        de += 0.25 * M_PI * (sk2 - 4.0 * sk * r + 17.0 * r2) / (r2 * lik4);
+    } else if (r < (baseRadiusI + sk)) {
+        lik = baseRadiusI;
+        lik4 = lik * lik;
+        lik4 = lik4 * lik4;
+        de += 0.25 * M_PI * (2.0 * baseRadiusI * baseRadiusI - sk2 - r2) / (r2 * lik4);
     } else {
-        lik          = r - sk;
-        lik4         = lik*lik;
-        lik4         = lik4*lik4;
-        de          += 0.25*M_PI*(sk2-4.0*sk*r+r2)/ (r2*lik4);
+        lik = r - sk;
+        lik4 = lik * lik;
+        lik4 = lik4 * lik4;
+        de += 0.25 * M_PI * (sk2 - 4.0 * sk * r + r2) / (r2 * lik4);
+    }
+    uik = r + sk;
+    uik4 = uik * uik;
+    uik4 = uik4 * uik4;
+
+    de -= 0.25 * M_PI * (sk2 + 4.0 * sk * r + r2) / (r2 * uik4);
+
+    double mixedNeckScale =  0.5 * (_neckFactors[iIndex] + _neckFactors[jIndex]);
+    if (mixedNeckScale > 0.0) {
+        de += neckDescreenDerivative(r, baseRadiusI, _descreenRadii[jIndex], mixedNeckScale);
     }
-    uik                = r + sk;
-    uik4               = uik*uik;
-    uik4               = uik4*uik4;
 
-    de                -= 0.25*M_PI*(sk2+4.0*sk*r+r2)/ (r2*uik4);
-    double dbr         = term*de/r;
-          de           = dbr*dBorn[iIndex];
+    double dbr = term * de / r;
+    de = dbr * dBorn[iIndex];
+
+    forces[iIndex] -= deltaR * de;
+    forces[jIndex] += deltaR * de;
 
-    forces[iIndex]    -= deltaR*de;
-    forces[jIndex]    += deltaR*de;
 }
 
+
+
+
 double AmoebaReferenceGeneralizedKirkwoodMultipoleForce::calculateCavityTermEnergyAndForces(vector<double>& dBorn) const
 {
 
diff --git a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceMultipoleForce.h b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceMultipoleForce.h
index 8ef6393282..1f1a92541d 100644
--- a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceMultipoleForce.h
+++ b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceMultipoleForce.h
@@ -1239,10 +1239,16 @@ class AmoebaReferenceGeneralizedKirkwoodMultipoleForce : public AmoebaReferenceM
      * Get dielectric offset.
      *
      * @return dielectric offset
-     *
      */
     double getDielectricOffset() const;
 
+    /**
+     * Get the descreen offset.
+     *
+     * @return descreen offset.
+     */
+    double getDescreenOffset() const;
+
 private:
 
     AmoebaReferenceGeneralizedKirkwoodForce* _amoebaReferenceGeneralizedKirkwoodForce;
@@ -1252,10 +1258,17 @@ class AmoebaReferenceGeneralizedKirkwoodMultipoleForce : public AmoebaReferenceM
     double _fd;
     double _fq;
 
+    double _beta0;
+    double _beta1;
+    double _beta2;
+
     std::vector<double> _atomicRadii;
-    std::vector<double> _scaledRadii;
+    std::vector<double> _scaleFactors;
+    std::vector<double> _descreenRadii;
+    std::vector<double> _neckFactors;
+
     std::vector<double> _bornRadii;
-    std::vector<double> _bornForce;
+    std::vector<double> _soluteIntegral;
 
     std::vector<Vec3> _gkField;
     std::vector<Vec3> _inducedDipoleS;
@@ -1269,6 +1282,8 @@ class AmoebaReferenceGeneralizedKirkwoodMultipoleForce : public AmoebaReferenceM
     double _probeRadius;
     double _surfaceAreaFactor;
     double _dielectricOffset;
+    double _tanhRescaling;
+    double _descreenOffset;
 
     /**
      * Zero fixed multipole fields.
diff --git a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.cpp b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.cpp
index 8e8d2b7308..4141a7fcff 100644
--- a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.cpp
+++ b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.cpp
@@ -49,6 +49,7 @@ void AmoebaReferenceVdwForce::initialize(const AmoebaVdwForce& force) {
     int numParticles = force.getNumParticles();
     indexIVs.resize(numParticles);
     reductions.resize(numParticles);
+    scaleFactors.resize(numParticles);
     isAlchemical.resize(numParticles);
     allExclusions.clear();
     allExclusions.resize(numParticles);
@@ -57,7 +58,7 @@ void AmoebaReferenceVdwForce::initialize(const AmoebaVdwForce& force) {
         double sigma, epsilon;
         bool alchemical;
         vector<int> exclusions;
-        force.getParticleParameters(i, indexIVs[i], sigma, epsilon, reductions[i], alchemical, type);
+        force.getParticleParameters(i, indexIVs[i], sigma, epsilon, reductions[i], alchemical, type, scaleFactors[i]);
         isAlchemical[i] = alchemical;
         force.getParticleExclusions(i, exclusions);
         for (unsigned int j = 0; j < exclusions.size(); j++)
@@ -224,6 +225,9 @@ double AmoebaReferenceVdwForce::calculateForceAndEnergy(int numParticles, double
                 double combinedEpsilon = epsilonMatrix[particleType[ii]][particleType[jj]];
                 double softcore = 0.0;
 
+                // Apply per particle scale factors (for CpHMD).
+                combinedEpsilon *= scaleFactors[ii] * scaleFactors[jj];
+
                 if (this->_alchemicalMethod == AmoebaVdwForce::Decouple && (isAlchemicalI != isAlchemical[jj])) {
                    combinedEpsilon *= pow(lambda, this->_n);
                    softcore = this->_alpha * pow(1.0 - lambda, 2);
@@ -287,6 +291,9 @@ double AmoebaReferenceVdwForce::calculateForceAndEnergy(int numParticles, double
         double combinedSigma = sigmaMatrix[particleType[siteI]][particleType[siteJ]];
         double combinedEpsilon = epsilonMatrix[particleType[siteI]][particleType[siteJ]];
 
+        // Apply per particle scale factors (for CpHMD).
+        combinedEpsilon *= scaleFactors[siteI] * scaleFactors[siteJ];
+
         double softcore        = 0.0;
         int isAlchemicalI      = isAlchemical[siteI];
         int isAlchemicalJ      = isAlchemical[siteJ];
diff --git a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.h b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.h
index e51c799770..d14ee4aa42 100644
--- a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.h
+++ b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceVdwForce.h
@@ -135,6 +135,7 @@ class AmoebaReferenceVdwForce {
     std::vector<std::vector<double> > epsilonMatrix;
     std::vector<int> indexIVs;
     std::vector<double> reductions;
+    std::vector<double> scaleFactors;
     std::vector<bool> isAlchemical;
     std::vector<std::set<int> > allExclusions;
     Vec3 _periodicBoxVectors[3];
diff --git a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceWcaDispersionForce.cpp b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceWcaDispersionForce.cpp
index a3fb8c33bb..1fcd099294 100644
--- a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceWcaDispersionForce.cpp
+++ b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceWcaDispersionForce.cpp
@@ -23,8 +23,9 @@
  */
 
 #ifdef WIN32
-  #define _USE_MATH_DEFINES // Needed to get M_PI
+#define _USE_MATH_DEFINES // Needed to get M_PI
 #endif
+
 #include "AmoebaReferenceForce.h"
 #include "AmoebaReferenceWcaDispersionForce.h"
 #include <cmath>
@@ -32,280 +33,240 @@
 using std::vector;
 using namespace OpenMM;
 
-AmoebaReferenceWcaDispersionForce::AmoebaReferenceWcaDispersionForce(double epso, double epsh, double rmino, double rminh, 
-                                                                     double awater, double shctd, double dispoff, double slevy) :
-                               _epso(epso), _epsh(epsh), _rmino(rmino), _rminh(rminh), _awater(awater), _shctd(shctd), _dispoff(dispoff), _slevy(slevy) {
-}   
-
-
-double AmoebaReferenceWcaDispersionForce::calculatePairIxn(double radiusI, double radiusK, 
-                                                           const Vec3& particleIPosition,
-                                                           const Vec3& particleJPosition,
-                                                           const double* const intermediateValues,
-                                                           Vec3& force) const {
-
-    static const double PI = M_PI;
-
-    double xr           = particleIPosition[0] - particleJPosition[0];
-    double yr           = particleIPosition[1] - particleJPosition[1];
-    double zr           = particleIPosition[2] - particleJPosition[2];
-
-    double r2           = xr*xr + yr*yr + zr*zr;
-    double r            = sqrt(r2);
-    double r3           = r2*r;
-
-    double sK           = radiusK*_shctd;
-    double sK2          = sK*sK;
-
-    double rmixo        = intermediateValues[RMIXO];
-    double rmixo7       = intermediateValues[RMIXO7];
-
-    double emixo        = intermediateValues[EMIXO];
-
-    double rmixh        = intermediateValues[RMIXH];
-    double rmixh7       = intermediateValues[RMIXH7];
-
-    double emixh        = intermediateValues[EMIXH];
-
-    double ao           = intermediateValues[AO];
-    double ah           = intermediateValues[AH];
-
-    double sum          = 0.0;
-    double de           = 0.0;
-
-    if (radiusI < (r + sK)) {
-
-        double rmax     = (radiusI > (r - sK)) ? radiusI : (r - sK);
-
-        double lik      = rmax;
-        double lik2     = lik*lik;
-        double lik3     = lik2*lik;
-        double lik4     = lik2*lik2;
+AmoebaReferenceWcaDispersionForce::AmoebaReferenceWcaDispersionForce(double epso, double epsh, double rmino,
+                                                                     double rminh, double awater, double shctd,
+                                                                     double dispoff, double slevy) :
+        _epso(epso), _epsh(epsh), _rmino(rmino), _rminh(rminh), _awater(awater), _shctd(shctd), _dispoff(dispoff),
+        _slevy(slevy) {
+}
 
-        if (lik < rmixo) { 
+static double integralBeforeRMin(double eps, double r, double r2, double sk2,
+                                 double lik2, double lik3, double lik4,
+                                 double uik2, double uik3, double uik4) {
+    return -eps * (4.0e0 * M_PI / (48.0e0 * r) *
+                   (3.0e0 * (lik4 - uik4) - 8.0e0 * r * (lik3 - uik3) + 6.0e0 * (r2 - sk2) * (lik2 - uik2)));
+}
 
-            double uik  = (r + sK) < rmixo ? (r + sK) : rmixo;
-            double uik2 = uik*uik;
-            double uik3 = uik2*uik;
-            double uik4 = uik2*uik2;
+static double
+integralBeforeRminDerivative(double ri, double eps, double rmin, double r, double r2, double r3, double sk, double sk2,
+                             double lik, double lik2, double lik3, double uik, double uik2, double uik3) {
+    double dl;
+    if (ri > r - sk) {
+        dl = (-lik2 + 2.0 * r2 + 2.0 * sk2) * lik2;
+    } else {
+        dl = (-lik3 + 4.0 * lik2 * r - 6.0 * lik * r2 + 2.0 * lik * sk2 + 4.0 * r3 - 4.0 * r * sk2) * lik;
+    }
+    double du;
+    if (r + sk > rmin) {
+        du = -(-uik2 + 2.0 * r2 + 2.0 * sk2) * uik2;
+    } else {
+        du = -(-uik3 + 4.0 * uik2 * r - 6.0 * uik * r2 + 2.0 * uik * sk2 + 4.0 * r3 - 4.0 * r * sk2) * uik;
+    }
+    return -eps * M_PI * (dl + du) / (4.0 * r2);
+}
 
-            double term = 4.0*PI/(48.0*r)* (3.0*(lik4-uik4) - 8.0*r*(lik3-uik3) + 6.0*(r2-sK2)*(lik2-uik2));
+static double integralAfterRmin(double eps, double rmin7, double r, double r2, double sk2,
+                                 double lik, double lik2, double lik3, double lik4, double lik5, double lik10,
+                                 double lik11, double lik12, double uik, double uik2, double uik3, double uik4,
+                                 double uik5, double uik10, double uik11, double uik12) {
+    double er7 = eps * rmin7;
+    double term = 4.0 * M_PI / (120.0 * r * lik5 * uik5)
+                  * (15.0 * uik * lik * r * (uik4 - lik4)
+                     - 10.0 * uik2 * lik2 * (uik3 - lik3)
+                     + 6.0 * (sk2 - r2) * (uik5 - lik5));
+    double term2 = 4.0 * M_PI / (2640.0 * r * lik12 * uik12)
+                   * (120.0 * uik * lik * r * (uik11 - lik11)
+                      - 66.0 * uik2 * lik2 * (uik10 - lik10)
+                      + 55.0 * (sk2 - r2) * (uik12 - lik12));
+    double idisp = -2.0 * er7 * term;
+    double irep = er7 * rmin7 * term2;
+    return irep + idisp;
+}
 
-            double dl;
-            if (radiusI >  (r - sK)) {
-                dl  = -lik2 + 2.0*(r2 + sK2);
-                dl *= lik2;
-            } else {
-                dl  = -lik3 + 4.0*lik2*r - 6.0*lik*r2 + 2.0*lik*sK2 + 4.0*r*(r2 - sK2);
-                dl *= lik;
-            }
+static double integralAfterRminDerivative(double ri, double eps, double rmin, double rmin7, double rmax,
+                                           double r, double r2, double r3, double sk, double sk2, double lik,
+                                           double lik2, double lik3, double lik5, double lik6, double lik12,
+                                           double lik13, double uik, double uik2, double uik3, double uik6,
+                                           double uik13) {
+    double er7 = eps * rmin7;
+    double lowerTerm = lik2 * r + r3 - r * sk2;
+    double upperTerm = uik2 * r + r3 - r * sk2;
+
+    double dl;
+    if (ri > r - sk || rmax < rmin) {
+        dl = -(-5.0 * lik2 + 3.0 * r2 + 3.0 * sk2) / lik5;
+    } else {
+        dl = (5.0 * lik3 - 33.0 * lik * r2 - 3.0 * lik * sk2 + 15.0 * lowerTerm) / lik6;
+    }
+    double du = -(5.0 * uik3 - 33.0 * uik * r2 - 3.0 * uik * sk2 + 15.0 * upperTerm) / uik6;
+    double de = -2.0 * M_PI * er7 * (dl + du) / (15.0 * r2);
 
-            double du;
-            if ((r+sK) > rmixo) {
-                du  = -uik2 + 2.0*(r2 + sK2);
-                du *= -uik2;
-            } else {
-                du  = -uik3 + 4.0*uik2*r - 6.0*uik*r2 + 2.0*uik*sK2 + 4.0*r*(r2 - sK2);
-                du *= -uik;
-            }
-            de     = -emixo*PI*(dl+du)/(4.0*r2);
-            sum   += -emixo*term;
-        }
+    if (ri > r - sk || rmax < rmin) {
+        dl = -(-6.0 * lik2 + 5.0 * r2 + 5.0 * sk2) / lik12;
+    } else {
+        dl = (6.0 * lik3 - 125.0 * lik * r2 - 5.0 * lik * sk2 + 60.0 * lowerTerm) / lik13;
+    }
+    du = -(6.0 * uik3 - 125.0 * uik * r2 - 5.0 * uik * sk2 + 60.0 * upperTerm) / uik13;
+    de += M_PI * er7 * rmin7 * (dl + du) / (60.0 * r2);
 
-        if (lik < rmixh) {
-
-            double uik  = (r + sK) < rmixh ? (r + sK) : rmixh;
-            double uik2 = uik*uik;
-            double uik3 = uik2*uik;
-            double uik4 = uik2*uik2;
-            double term = 4.0*PI / (48.0*r)*(3.0*(lik4-uik4) - 8.0*r*(lik3-uik3) + 6.0*(r2-sK2)*(lik2-uik2));
-            double dl;
-            if (radiusI > (r-sK)) {
-                dl  = -lik2 + 2.0*(r2 + sK2);
-                dl *= lik2;
-            } else {
-                dl  = -lik3 + 4.0*lik2*r - 6.0*lik*r2 + 2.0*lik*sK2 + 4.0*r*(r2 -sK2);
-                dl *= lik;
-            }
-      
-            double du;
-            if (r+sK > rmixh) {
-                du  = -uik2 + 2.0*(r2 + sK2);
-                du *= -uik2;
-            } else {
-                du  = -uik3 +4.0*uik2*r - 6.0*uik*r2 + 2.0*uik*sK2 +4.0*r*(r2 - sK2);
-                du *= -uik;
-            }
-            de   -= 2.0*emixh*PI*(dl+du)/(4.0*r2);
-            sum  -= 2.0*emixh*term;
-        }
+    return de;
+}
 
-        double uik   = r + sK;
-        double uik2  = uik   * uik;
-        double uik3  = uik2  * uik;
-        double uik4  = uik2  * uik2;
-        double uik5  = uik3  * uik2;
-        double uik6  = uik3  * uik3;
-        double uik10 = uik5  * uik5;
-        double uik11 = uik5  * uik6;
-        double uik12 = uik6  * uik6;
-        double uik13 = uik10 * uik3;
-
-        if (uik > rmixo) {
-
-            double lik   = rmax > rmixo ? rmax : rmixo;
-            double lik2  = lik   * lik;
-            double lik3  = lik2  * lik;
-            double lik4  = lik2  * lik2;
-            double lik5  = lik2  * lik3;
-            double lik6  = lik3  * lik3;
-            double lik10 = lik5  * lik5;
-            double lik11 = lik5  * lik6;
-            double lik12 = lik6  * lik6;
-            double lik13 = lik10 * lik3;
-
-            double term  = 4.0*PI/(120.0*r*lik5*uik5)*(15.0*uik*lik*r*(uik4-lik4) - 10.0*uik2*lik2*(uik3-lik3) + 6.0*(sK2-r2)*(uik5-lik5));
-            double dl;
-            if (radiusI > (r-sK) || rmax < rmixo) {
-                dl  = -5.0*lik2 + 3.0*(r2 + sK2);
-                dl /= -lik5;
-            } else {
-                dl  = 5.0*lik3 - 33.0*lik*r2 - 3.0*lik*sK2 + 15.0*(lik2*r+r3-r*sK2);
-                dl /= lik6;
-            }
-            double du     = 5.0*uik3 - 33.0*uik*r2 - 3.0*uik*sK2 + 15.0*(uik2*r+r3-r*sK2);
-                   du    /= -uik6;
-            double idisp  = -2.0*ao*term;
-                   de    -= 2.0*ao*PI*(dl + du)/(15.0*r2);
-                   term   = 4.0*PI/(2640.0*r*lik12*uik12) * (120.0*uik*lik*r*(uik11-lik11) - 66.0*uik2*lik2*(uik10-lik10) + 55.0*(sK2-r2)*(uik12-lik12));
-            if (radiusI > (r-sK) || rmax < rmixo) {
-                dl  = -6.0*lik2 + 5.0*r2 + 5.0*sK2;
-                dl /= -lik12;
-            } else {
-                dl  = 6.0*lik3 - 125.0*lik*r2 - 5.0*lik*sK2 + 60.0*(lik2*r+r3-r*sK2);
-                dl /= lik13;
-            }
-            du               = 6.0*uik3 - 125.0*uik*r2 - 5.0*uik*sK2 + 60.0*(uik2*r+r3-r*sK2);
-            du              /= -uik13;
-            de              += ao*rmixo7*PI*(dl + du)/(60.0*r2);
-            sum             += ao*rmixo7*term + idisp;
+static double interact(double factor, double ri, double sk, double rmix, double emix,
+                       double r, double r2, double r3, Vec3 &force) {
+    double sum = 0.0;
+    // Nothing to do if the integral begins beyond r + sk (i.e. atom k does not exclude solvent)
+    if (ri < r + sk) {
+        // Zero out the derivative contribution of atom k.
+        double de = 0.0;
+        double sk2 = sk * sk;
+        // Compute the maximum of 1) the beginning of the integral and 2) closest edge of atom K.
+        double iStart = ri > r - sk ? ri : r - sk;
+        // Use this as the lower limit for integrating the constant eps value below Rmin.
+        double lik = iStart;
+        // Interaction with water from lik to Rmin; nothing to do if the lower limit is greater than Rmin.
+        if (lik < rmix) {
+            double lik2 = lik * lik;
+            double lik3 = lik2 * lik;
+            double lik4 = lik3 * lik;
+            // Upper limit is the minimum of Rmin and the farthest edge of atom K.
+            double uik = r + sk < rmix ? r + sk : rmix;
+            double uik2 = uik * uik;
+            double uik3 = uik2 * uik;
+            double uik4 = uik3 * uik;
+            sum = integralBeforeRMin(emix, r, r2, sk2, lik2, lik3, lik4, uik2, uik3, uik4);
+            de = integralBeforeRminDerivative(ri, emix, rmix, r, r2, r3, sk, sk2, lik, lik2, lik3, uik, uik2, uik3);
         }
-        if (uik > rmixh) {
-                   lik   = rmax > rmixh ? rmax : rmixh;
-                   lik2  = lik  * lik;
-                   lik3  = lik2 * lik;
-                   lik4  = lik2 * lik2;
-            double lik5  = lik2 * lik3;
-            double lik6  = lik3 * lik3;
+        // Upper limit the variable part of Uwca always the farthest edge of atom K.
+        double uik = r + sk;
+        // Interaction with water beyond Rmin, from lik to uik = r + sk.
+        if (uik > rmix) {
+            // Start the integral at the max of 1) iStart and 2) Rmin.
+            lik = iStart > rmix ? iStart : rmix;
+            double lik2 = lik * lik;
+            double lik3 = lik2 * lik;
+            double lik4 = lik3 * lik;
+            double lik5 = lik4 * lik;
+            double lik6 = lik5 * lik;
             double lik10 = lik5 * lik5;
-            double lik11 = lik5 * lik6;
-            double lik12 = lik6 * lik6;
-            double lik13 = lik3 * lik10;
-
-            double term  = 4.0*PI / (120.0*r*lik5*uik5)*(15.0*uik*lik*r*(uik4-lik4) - 10.0*uik2*lik2*(uik3-lik3) + 6.0*(sK2-r2)*(uik5-lik5));
-            double dl;
-            if (radiusI > (r-sK) || rmax < rmixh) {
-                dl  = -5.0*lik2 + 3.0*(r2 + sK2);
-                dl /= -lik5;
-            } else {
-                dl  = 5.0*lik3 - 33.0*lik*r2 - 3.0*lik*sK2 + 15.0*(lik2*r+r3-r*sK2);
-                dl /= lik6;
-            }
-            double du     = 5.0*uik3 - 33.0*uik*r2 - 3.0*uik*sK2 + 15.0*(uik2*r+r3-r*sK2);
-                   du    /= -uik6;
-            double idisp  = -4.0*ah*term;
-            de                = de - 4.0*ah*PI*(dl + du)/(15.0*r2);
-            term              = 4.0*PI / (2640.0*r*lik12*uik12)* (120.0*uik*lik*r*(uik11-lik11)- 66.0*uik2*lik2*(uik10-lik10)+ 55.0*(sK2-r2)*(uik12-lik12));
-            if (radiusI > (r-sK) || rmax < rmixh) {
-                dl = -6.0*lik2 + 5.0*r2 + 5.0*sK2;
-                dl = -dl / lik12;
-            } else {;
-                dl = 6.0*lik3 - 125.0*lik*r2 - 5.0*lik*sK2 + 60.0*(lik2*r+r3-r*sK2);
-                dl = dl / lik13;
-            }
-            du            = 6.0*uik3 - 125.0*uik*r2 - 5.0*uik*sK2 + 60.0*(uik2*r+r3-r*sK2);
-            du           /= -uik13;
-            double irep   = 2.0*ah*rmixh7*term;
-            de           += ah*rmixh7*PI*(dl+du)/(30.0*r2);
-            sum          += irep + idisp;
+            double lik11 = lik10 * lik;
+            double lik12 = lik11 * lik;
+            double uik2 = uik * uik;
+            double uik3 = uik2 * uik;
+            double uik4 = uik3 * uik;
+            double uik5 = uik4 * uik;
+            double uik10 = uik5 * uik5;
+            double uik11 = uik10 * uik;
+            double uik12 = uik11 * uik;
+            double rmix3 = rmix * rmix * rmix;
+            double rmix7 = rmix3 * rmix3 * rmix;
+            sum += integralAfterRmin(emix, rmix7, r, r2, sk2, lik, lik2, lik3, lik4, lik5, lik10, lik11, lik12, uik,
+                                      uik2, uik3, uik4, uik5, uik10, uik11, uik12);
+            double lik13 = lik12 * lik;
+            double uik6 = uik5 * uik;
+            double uik13 = uik12 * uik;
+            de += integralAfterRminDerivative(ri, emix, rmix, rmix7, iStart, r, r2, r3, sk, sk2, lik, lik2, lik3,
+                                               lik5, lik6, lik12, lik13, uik, uik2, uik3, uik6, uik13);
         }
+        // Increment the individual dispersion gradient components.
+        de *= factor / r;
+        force[0] += de;
+        force[1] += de;
+        force[2] += de;
     }
+    return factor * sum;
+}
 
-    // increment the individual dispersion gradient components
-
-    de          *= (_slevy*_awater)/r;
-
-    force[0]     = de*xr;
-    force[1]     = de*yr;
-    force[2]     = de*zr;
+double AmoebaReferenceWcaDispersionForce::calculatePairIxn(double radiusJ,
+                                                           const Vec3 &particleIPosition,
+                                                           const Vec3 &particleJPosition,
+                                                           const double *const intermediateValues,
+                                                           Vec3 &force) const {
+    // Atomic separation
+    double xr = particleIPosition[0] - particleJPosition[0];
+    double yr = particleIPosition[1] - particleJPosition[1];
+    double zr = particleIPosition[2] - particleJPosition[2];
+    double r2 = xr * xr + yr * yr + zr * zr;
+    double r = sqrt(r2);
+    double r3 = r2 * r;
+
+    // Parameters for atom i with water oxygen.
+    double rmixo = intermediateValues[RMIXO];
+    double emixo = intermediateValues[EMIXO];
+
+    // Parameters for atom i with water hydrogen.
+    double rmixh = intermediateValues[RMIXH];
+    double emixh = intermediateValues[EMIXH];
+
+    // Start of integration of dispersion for atom i with water oxygen.
+    double riO = rmixo / 2.0e0 + _dispoff;
+    double nO = 1.0e0;
+
+    // Start of integration of dispersion for atom i with water hydrogen.
+    double riH = rmixh / 2.0e0 + _dispoff;
+    double nH = 2.0e0;
+
+    // Atom k blocks the interaction of atom i with solvent.
+    double sJ = radiusJ * _shctd;
+
+    double sum = interact(nO, riO, sJ, rmixo, emixo, r, r2, r3, force) +
+                 interact(nH, riH, sJ, rmixh, emixh, r, r2, r3, force);
+
+    force[0] *= xr;
+    force[1] *= yr;
+    force[2] *= zr;
 
     return sum;
-
 }
 
 double AmoebaReferenceWcaDispersionForce::calculateForceAndEnergy(int numParticles,
-                                                                  const vector<Vec3>& particlePositions,
-                                                                  const std::vector<double>& radii,
-                                                                  const std::vector<double>& epsilons,
+                                                                  const vector<Vec3> &particlePositions,
+                                                                  const std::vector<double> &radii,
+                                                                  const std::vector<double> &epsilons,
                                                                   double totalMaximumDispersionEnergy,
-                                                                  vector<Vec3>& forces) const {
-
-    // loop over all ixns
-
-    double energy     = 0.0;
-
-    double rmino2     = _rmino*_rmino;
-    double rmino3     = rmino2*_rmino;
-
-    double rminh2     = _rminh*_rminh;
-    double rminh3     = rminh2*_rminh;
-
+                                                                  vector<Vec3> &forces) const {
+    double energy = 0.0;
+    double rmino2 = _rmino * _rmino;
+    double rmino3 = rmino2 * _rmino;
+    double rminh2 = _rminh * _rminh;
+    double rminh3 = rminh2 * _rminh;
     double intermediateValues[LastIntermediateValueIndex];
 
+    // loop over all ixns
     for (unsigned int ii = 0; ii < static_cast<unsigned int>(numParticles); ii++) {
- 
-        double epsi              = epsilons[ii];
-        double rmini             = radii[ii];
-
-        double denominator       = sqrt(_epso) + sqrt(epsi);
-        double emixo             = 4.0*_epso*epsi/(denominator*denominator);
-        intermediateValues[EMIXO]    = emixo;
-
-        double rminI2            = rmini*rmini;
-        double rminI3            = rminI2*rmini;
- 
-        double rmixo             = 2.0*(rmino3 + rminI3) / (rmino2 + rminI2);
-        intermediateValues[RMIXO] = rmixo;
 
-        double rmixo7            = rmixo*rmixo*rmixo;
-               rmixo7            = rmixo7*rmixo7*rmixo;
-        intermediateValues[RMIXO7] = rmixo7;
+        double epsi = epsilons[ii];
+        double rmini = radii[ii];
+        double rminI2 = rmini * rmini;
+        double rminI3 = rminI2 * rmini;
 
-        intermediateValues[AO]     = emixo*rmixo7;
+        double denominator = sqrt(_epso) + sqrt(epsi);
+        double emixo = 4.0 * _epso * epsi / (denominator * denominator);
+        intermediateValues[EMIXO] = emixo;
 
-                   denominator     = sqrt(_epsh) + sqrt(epsi);
+        double rmixo = 2.0 * (rmino3 + rminI3) / (rmino2 + rminI2);
+        intermediateValues[RMIXO] = rmixo;
 
-        double emixh              = 4.0*_epsh*epsi/ (denominator*denominator);
+        denominator = sqrt(_epsh) + sqrt(epsi);
+        double emixh = 4.0 * _epsh * epsi / (denominator * denominator);
         intermediateValues[EMIXH] = emixh;
 
-        double rmixh              = 2.0 * (rminh3 + rminI3) / (rminh2 + rminI2);
+        double rmixh = 2.0 * (rminh3 + rminI3) / (rminh2 + rminI2);
         intermediateValues[RMIXH] = rmixh;
 
-        double rmixh7              = rmixh*rmixh*rmixh;
-                   rmixh7          = rmixh7*rmixh7*rmixh;
-        intermediateValues[RMIXH7] = rmixh7;
-
-        intermediateValues[AH]     = emixh*rmixh7;
-
+        // Remove dispersion for atom i by atom j.
         for (unsigned int jj = 0; jj < static_cast<unsigned int>(numParticles); jj++) {
 
-            if (ii == jj)continue;
+            if (ii == jj) continue;
 
-            Vec3 force;
-            energy += calculatePairIxn(rmini, radii[jj],
-                                       particlePositions[ii], particlePositions[jj],
+            Vec3 force = Vec3();
+            energy += calculatePairIxn(radii[jj], particlePositions[ii], particlePositions[jj],
                                        intermediateValues, force);
-            
+
+            force[0] *= _slevy * _awater;
+            force[1] *= _slevy * _awater;
+            force[2] *= _slevy * _awater;
+
             forces[ii][0] += force[0];
             forces[ii][1] += force[1];
             forces[ii][2] += force[2];
@@ -313,11 +274,12 @@ double AmoebaReferenceWcaDispersionForce::calculateForceAndEnergy(int numParticl
             forces[jj][0] -= force[0];
             forces[jj][1] -= force[1];
             forces[jj][2] -= force[2];
+
         }
 
     }
 
-    energy = totalMaximumDispersionEnergy - _slevy*_awater*energy;
+    energy = totalMaximumDispersionEnergy - _slevy * _awater * energy;
 
     return energy;
 }
diff --git a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceWcaDispersionForce.h b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceWcaDispersionForce.h
index 5f2f3cd48e..4a8838d0ed 100644
--- a/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceWcaDispersionForce.h
+++ b/plugins/amoeba/platforms/reference/src/SimTKReference/AmoebaReferenceWcaDispersionForce.h
@@ -91,13 +91,12 @@ class AmoebaReferenceWcaDispersionForce {
     double _dispoff;
     double _slevy;
 
-    enum { EMIXO, RMIXO, RMIXO7, AO, EMIXH, RMIXH, RMIXH7, AH, LastIntermediateValueIndex }; 
+    enum { EMIXO, RMIXO, EMIXH, RMIXH, LastIntermediateValueIndex };
 
     /**---------------------------------------------------------------------------------------
     
        Calculate pair ixn
-    
-       @param  radiusI              radius of particle I
+
        @param  radiusJ              radius of particle J
        @param  particleIPosition    particle I position 
        @param  particleJPosition    particle J position 
@@ -108,7 +107,7 @@ class AmoebaReferenceWcaDispersionForce {
 
        --------------------------------------------------------------------------------------- */
     
-    double calculatePairIxn(double radiusI, double radiusJ,
+    double calculatePairIxn(double radiusJ,
                             const OpenMM::Vec3& particleIPosition, const OpenMM::Vec3& particleJPosition,
                             const double* const intermediateValues,
                             Vec3& force) const;
diff --git a/plugins/amoeba/serialization/src/AmoebaGeneralizedKirkwoodForceProxy.cpp b/plugins/amoeba/serialization/src/AmoebaGeneralizedKirkwoodForceProxy.cpp
index 13f7099c01..31395b3421 100644
--- a/plugins/amoeba/serialization/src/AmoebaGeneralizedKirkwoodForceProxy.cpp
+++ b/plugins/amoeba/serialization/src/AmoebaGeneralizedKirkwoodForceProxy.cpp
@@ -42,46 +42,70 @@ AmoebaGeneralizedKirkwoodForceProxy::AmoebaGeneralizedKirkwoodForceProxy() : Ser
 }
 
 void AmoebaGeneralizedKirkwoodForceProxy::serialize(const void* object, SerializationNode& node) const {
-    node.setIntProperty("version", 2);
+    node.setIntProperty("version", 3);
     const AmoebaGeneralizedKirkwoodForce& force = *reinterpret_cast<const AmoebaGeneralizedKirkwoodForce*>(object);
 
     node.setIntProperty("forceGroup", force.getForceGroup());
     node.setStringProperty("name", force.getName());
     node.setDoubleProperty("GeneralizedKirkwoodSolventDielectric", force.getSolventDielectric());
     node.setDoubleProperty("GeneralizedKirkwoodSoluteDielectric",  force.getSoluteDielectric());
-    //node.setDoubleProperty("GeneralizedKirkwoodDielectricOffset",  force.getDielectricOffset());
+    node.setDoubleProperty("GeneralizedKirkwoodDielectricOffset",  force.getDielectricOffset());
     node.setDoubleProperty("GeneralizedKirkwoodProbeRadius",       force.getProbeRadius());
     node.setDoubleProperty("GeneralizedKirkwoodSurfaceAreaFactor", force.getSurfaceAreaFactor());
     node.setIntProperty(  "GeneralizedKirkwoodIncludeCavityTerm", force.getIncludeCavityTerm());
-
+    node.setBoolProperty("GeneralizedKirkwoodTanhRescaling", force.getTanhRescaling());
+    double b0, b1, b2;
+    force.getTanhParameters(b0, b1, b2);
+    node.setDoubleProperty("GeneralizedKirkwoodTanhB0", b0);
+    node.setDoubleProperty("GeneralizedKirkwoodTanhB1", b1);
+    node.setDoubleProperty("GeneralizedKirkwoodTanhB2", b2);
+    node.setDoubleProperty("GeneralizedKirkwoodDescreenOffset", force.getDescreenOffset());
     SerializationNode& particles = node.createChildNode("GeneralizedKirkwoodParticles");
     for (unsigned int ii = 0; ii < static_cast<unsigned int>(force.getNumParticles()); ii++) {
-        double radius, charge, scalingFactor;
-        force.getParticleParameters(ii, charge, radius, scalingFactor);
-        particles.createChildNode("Particle").setDoubleProperty("charge", charge).setDoubleProperty("radius", radius).setDoubleProperty("scaleFactor", scalingFactor);
+        double radius, charge, scalingFactor, descreenRadius, neckFactor;
+        force.getParticleParameters(ii, charge, radius, scalingFactor, descreenRadius, neckFactor);
+        particles.createChildNode("Particle").setDoubleProperty("charge", charge).setDoubleProperty("radius", radius).setDoubleProperty("scaleFactor", scalingFactor).setDoubleProperty("descreenRadius", descreenRadius).setDoubleProperty("neckFactor", neckFactor);
     }
 
 }
 
 void* AmoebaGeneralizedKirkwoodForceProxy::deserialize(const SerializationNode& node) const {
     int version = node.getIntProperty("version");
-    if (version < 1 || version > 2)
+    if (version < 1 || version > 3)
         throw OpenMMException("Unsupported version number");
     AmoebaGeneralizedKirkwoodForce* force = new AmoebaGeneralizedKirkwoodForce();
     try {
         force->setForceGroup(node.getIntProperty("forceGroup", 0));
         force->setName(node.getStringProperty("name", force->getName()));
-        force->setSolventDielectric(  node.getDoubleProperty("GeneralizedKirkwoodSolventDielectric"));
-        force->setSoluteDielectric(   node.getDoubleProperty("GeneralizedKirkwoodSoluteDielectric"));
-        //force->setDielectricOffset(   node.getDoubleProperty("GeneralizedKirkwoodDielectricOffset"));
-        force->setProbeRadius(        node.getDoubleProperty("GeneralizedKirkwoodProbeRadius"));
-        force->setSurfaceAreaFactor(  node.getDoubleProperty("GeneralizedKirkwoodSurfaceAreaFactor"));
-        force->setIncludeCavityTerm(  node.getIntProperty(   "GeneralizedKirkwoodIncludeCavityTerm"));
+        force->setSolventDielectric(node.getDoubleProperty("GeneralizedKirkwoodSolventDielectric"));
+        force->setSoluteDielectric(node.getDoubleProperty("GeneralizedKirkwoodSoluteDielectric"));
+        force->setProbeRadius(node.getDoubleProperty("GeneralizedKirkwoodProbeRadius"));
+        force->setSurfaceAreaFactor(node.getDoubleProperty("GeneralizedKirkwoodSurfaceAreaFactor"));
+        force->setIncludeCavityTerm(node.getIntProperty("GeneralizedKirkwoodIncludeCavityTerm"));
+        if (version > 2) {
+            force->setDielectricOffset(node.getDoubleProperty("GeneralizedKirkwoodDielectricOffset"));
+            force->setTanhRescaling(node.getBoolProperty("GeneralizedKirkwoodTanhRescaling"));
+            double b0 = node.getDoubleProperty("GeneralizedKirkwoodTanhB0");
+            double b1 = node.getDoubleProperty("GeneralizedKirkwoodTanhB1");
+            double b2 = node.getDoubleProperty("GeneralizedKirkwoodTanhB2");
+            force->setTanhParameters(b0, b1, b2);
+            force->setDescreenOffset(node.getDoubleProperty("GeneralizedKirkwoodDescreenOffset"));
+
+        }
 
         const SerializationNode& particles = node.getChildNode("GeneralizedKirkwoodParticles");
         for (unsigned int ii = 0; ii < particles.getChildren().size(); ii++) {
             const SerializationNode& particle = particles.getChildren()[ii];
-            force->addParticle(particle.getDoubleProperty("charge"), particle.getDoubleProperty("radius"), particle.getDoubleProperty("scaleFactor"));
+            double charge = particle.getDoubleProperty("charge");
+            double radius = particle.getDoubleProperty("radius");
+            double scaleFactor = particle.getDoubleProperty("scaleFactor");
+            double descreenRadius = radius;
+            double neckFactor = 0.0;
+            if (version > 2) {
+                descreenRadius = particle.getDoubleProperty("descreenRadius");
+                neckFactor = particle.getDoubleProperty("neckFactor");
+            }
+            force->addParticle(charge, radius, scaleFactor, descreenRadius, neckFactor);
         }
     }
     catch (...) {
diff --git a/plugins/amoeba/serialization/src/AmoebaVdwForceProxy.cpp b/plugins/amoeba/serialization/src/AmoebaVdwForceProxy.cpp
index b52b6092eb..f51985f797 100644
--- a/plugins/amoeba/serialization/src/AmoebaVdwForceProxy.cpp
+++ b/plugins/amoeba/serialization/src/AmoebaVdwForceProxy.cpp
@@ -42,7 +42,7 @@ AmoebaVdwForceProxy::AmoebaVdwForceProxy() : SerializationProxy("AmoebaVdwForce"
 }
 
 void AmoebaVdwForceProxy::serialize(const void* object, SerializationNode& node) const {
-    node.setIntProperty("version", 4);
+    node.setIntProperty("version", 5);
     const AmoebaVdwForce& force = *reinterpret_cast<const AmoebaVdwForce*>(object);
     bool useTypes = force.getUseParticleTypes();
 
@@ -62,14 +62,14 @@ void AmoebaVdwForceProxy::serialize(const void* object, SerializationNode& node)
     SerializationNode& particles = node.createChildNode("VdwParticles");
     for (int i = 0; i < force.getNumParticles(); i++) {
         int ivIndex, typeIndex;
-        double sigma, epsilon, reductionFactor;
+        double sigma, epsilon, reductionFactor, scaleFactor;
         bool isAlchemical;
-        force.getParticleParameters(i, ivIndex, sigma, epsilon, reductionFactor, isAlchemical, typeIndex);
+        force.getParticleParameters(i, ivIndex, sigma, epsilon, reductionFactor, isAlchemical, typeIndex, scaleFactor);
         SerializationNode& particle = particles.createChildNode("Particle");
         if (useTypes)
-            particle.setIntProperty("ivIndex", ivIndex).setIntProperty("type", typeIndex).setDoubleProperty("reductionFactor", reductionFactor).setBoolProperty("isAlchemical", isAlchemical);
+            particle.setIntProperty("ivIndex", ivIndex).setIntProperty("type", typeIndex).setDoubleProperty("reductionFactor", reductionFactor).setBoolProperty("isAlchemical", isAlchemical).setDoubleProperty("scaleFactor", scaleFactor);
         else
-            particle.setIntProperty("ivIndex", ivIndex).setDoubleProperty("sigma", sigma).setDoubleProperty("epsilon", epsilon).setDoubleProperty("reductionFactor", reductionFactor).setBoolProperty("isAlchemical", isAlchemical); 
+            particle.setIntProperty("ivIndex", ivIndex).setDoubleProperty("sigma", sigma).setDoubleProperty("epsilon", epsilon).setDoubleProperty("reductionFactor", reductionFactor).setBoolProperty("isAlchemical", isAlchemical).setDoubleProperty("scaleFactor", scaleFactor);
 
         std::vector< int > exclusions;
         force.getParticleExclusions(i,  exclusions);
@@ -98,7 +98,7 @@ void AmoebaVdwForceProxy::serialize(const void* object, SerializationNode& node)
 
 void* AmoebaVdwForceProxy::deserialize(const SerializationNode& node) const {
     int version = node.getIntProperty("version");
-    if (version < 1 || version > 4)
+    if (version < 1 || version > 5)
         throw OpenMMException("Unsupported version number");
     AmoebaVdwForce* force = new AmoebaVdwForce();
     try {
@@ -128,16 +128,30 @@ void* AmoebaVdwForceProxy::deserialize(const SerializationNode& node) const {
             if (version < 3) 
                force->addParticle(particle.getIntProperty("ivIndex"), particle.getDoubleProperty("sigma"), 
                                   particle.getDoubleProperty("epsilon"), particle.getDoubleProperty("reductionFactor"));
-            else if (useTypes)
-               force->addParticle(particle.getIntProperty("ivIndex"), particle.getIntProperty("type"), 
-                                  particle.getDoubleProperty("reductionFactor"), particle.getBoolProperty("isAlchemical"));
-            else
-               force->addParticle(particle.getIntProperty("ivIndex"), particle.getDoubleProperty("sigma"), 
-                                  particle.getDoubleProperty("epsilon"), particle.getDoubleProperty("reductionFactor"), 
-                                  particle.getBoolProperty("isAlchemical"));
+            else if (version == 4) {
+                if (useTypes)
+                    force->addParticle(particle.getIntProperty("ivIndex"), particle.getIntProperty("type"),
+                                       particle.getDoubleProperty("reductionFactor"),
+                                       particle.getBoolProperty("isAlchemical"));
+                else
+                    force->addParticle(particle.getIntProperty("ivIndex"), particle.getDoubleProperty("sigma"),
+                                       particle.getDoubleProperty("epsilon"),
+                                       particle.getDoubleProperty("reductionFactor"),
+                                       particle.getBoolProperty("isAlchemical"));
+            } else {
+                // Version 5 includes per particle scale factor for CpHMD.
+                if (useTypes)
+                    force->addParticle(particle.getIntProperty("ivIndex"), particle.getIntProperty("type"),
+                                       particle.getDoubleProperty("reductionFactor"),
+                                       particle.getBoolProperty("isAlchemical"), particle.getDoubleProperty("scaleFactor"));
+                else
+                    force->addParticle(particle.getIntProperty("ivIndex"), particle.getDoubleProperty("sigma"),
+                                       particle.getDoubleProperty("epsilon"),
+                                       particle.getDoubleProperty("reductionFactor"),
+                                       particle.getBoolProperty("isAlchemical"), particle.getDoubleProperty("scaleFactor"));
+            }
 
             // exclusions
-
             const SerializationNode& particleExclusions = particle.getChildNode("ParticleExclusions");
             std::vector<int> exclusions;
             for (int j = 0; j < particleExclusions.getChildren().size(); j++)
diff --git a/wrappers/python/src/swig_doxygen/swigInputConfig.py b/wrappers/python/src/swig_doxygen/swigInputConfig.py
index 4b0b80b53f..c8d2875ac2 100755
--- a/wrappers/python/src/swig_doxygen/swigInputConfig.py
+++ b/wrappers/python/src/swig_doxygen/swigInputConfig.py
@@ -240,12 +240,18 @@
 ("SerializationProxy", "getTypeName") : (None, ()),
 
 # check getSurfaceAreaFactor
-("AmoebaGeneralizedKirkwoodForce",       "getParticleParameters")                         :  (None, ('unit.elementary_charge', 'unit.nanometer', None)),
+("AmoebaGeneralizedKirkwoodForce",       "getParticleParameters")                         :  (None, ('unit.elementary_charge', 'unit.nanometer', None, 'unit.nanometer', None)),
 ("AmoebaGeneralizedKirkwoodForce",       "getDielectricOffset")                           :  ( 'unit.nanometer', ()),
+("AmoebaGeneralizedKirkwoodForce",       "getSolventDielectric")                          :  (None, ()),
+("AmoebaGeneralizedKirkwoodForce",       "getSoluteDielectric")                           :  (None, ()),
 ("AmoebaGeneralizedKirkwoodForce",       "getIncludeCavityTerm")                          :  ( None,()),
+("AmoebaGeneralizedKirkwoodForce",       "getTanhRescaling")                              :  ( None,()),
+("AmoebaGeneralizedKirkwoodForce",       "getTanhParameters")                             : ( None,(None, None, None)),
+("AmoebaGeneralizedKirkwoodForce",       "getDescreenOffset")                             :  ( 'unit.nanometer', ()),
 ("AmoebaGeneralizedKirkwoodForce",       "getProbeRadius")                                :  ( 'unit.nanometer', ()),
 ("AmoebaGeneralizedKirkwoodForce",       "getSurfaceAreaFactor")                          :  ( 'unit.kilojoule_per_mole/(unit.nanometer*unit.nanometer)',()),
 
+
 ("AmoebaMultipoleForce",                 "getNumMultipoles")                              :  ( None,()),
 ("AmoebaMultipoleForce",                 "getPolarizationType")                           :  ( None,()),
 ("AmoebaMultipoleForce",                 "getCutoffDistance")                             :  (  'unit.nanometer',()),
@@ -297,7 +303,7 @@
 ("AmoebaVdwForce",                        "getSoftcorePower")                              :  ( None, ()),
 ("AmoebaVdwForce",                        "getSoftcoreAlpha")                              :  ( None, ()),
 ("AmoebaVdwForce",                        "getCutoff")                                     :  ( 'unit.nanometer', ()),
-("AmoebaVdwForce",                        "getParticleParameters")                         :  ( None, (None, 'unit.nanometer', 'unit.kilojoule_per_mole', None, None, None)),
+("AmoebaVdwForce",                        "getParticleParameters")                         :  ( None, (None, 'unit.nanometer', 'unit.kilojoule_per_mole', None, None, None, None)),
 ("AmoebaVdwForce",                        "getParticleTypeParameters")                     :  ( None, ('unit.nanometer', 'unit.kilojoule_per_mole')),
 ("AmoebaVdwForce",                        "getTypePairParameters")                         :  ( None, (None, None, 'unit.nanometer', 'unit.kilojoule_per_mole')),