Source code for gamdpy.interactions.potential_functions.apply_gromacs_cutoff
import numba
[docs]
def apply_gromacs_cutoff(pair_potential: callable) -> callable:
r""" Apply a smooth cutoff to a pair-potential function using GROMACS style.
The potential is smoothly truncated at :math:`r_c` by applying a switching function.
Specifically, if :math:`u(r)` is the original potential, then the (modified) truncated potential is
.. math::
u_m(r) = u(r) + S(r)
Outside the cut-off, the switching function is :math:`S(r) = -u(r)` (:math:`r > r_c`)
truncating the potential at :math:`r_c` :math:`u_m(r) = 0 \quad (r > r_c)`.
In the switching region
.. math::
S(r) = \frac{A}{3}[r-r_1]^3 + \frac{B}{4}[r-r_1]^4 + C \quad (r_1 < r < r_c)
At :math:`r<r_1`, the potential is shifted by a constant
.. math::
S(r) = C \quad (r < r_1)
The parameters ensuring smooth truncation are
.. math::
A = \frac{- 3 u'(r_c) + [r_c - r_1] u''(r_c) }{[ r_c - r_1 ]^2}
.. math::
B = [ 2 u'(r_c) - [r_c - r_1] u''(r_c) ]/[ r_c - r_1 ]^3
.. math::
C = - u(r_c) + \frac{1}{2} [r_c - r_1] u'(r_c) - \frac{1}{12} [r_c - r_1]^2 u''(r_c)
The parameters are computed automatically.
The last two values of the parameter array (`params`) are the inner and outer cutoffs: `[..., r_1, r_c]`.
For more, see GROMACS force-switch function (3rd degree polynomial) in manual at https://www.gromacs.org/,
or the LAMMPS documentation, https://docs.lammps.org/pair_gromacs.html.
Parameters
----------
pair_potential: callable
a function that calculates a pair-potential:
u, s, umm = pair_potential(dist, params)
Returns
-------
potential: callable
a function where a smooth cutoff is applied to the original function.
Example
-------
>>> import gamdpy as gp
>>> params = sig, eps, cut_inner, cut_outer = 1.0, 1.0, 3.0, 4.0
>>> pair_func = gp.apply_gromacs_cutoff(gp.LJ_12_6)
"""
pair_pot = numba.njit(pair_potential)
#@numba.njit # Jit moved to 'last minut', i.e. PairPotential.get_kernel
def potential(dist, params): # pragma: no cover
flt = numba.float32
r_c = flt(params[-1])
r_1 = flt(params[-2])
dr = dist - r_1 # r - r_1
dr2 = dr*dr
Dr = r_c - r_1 # r_c - r_1
Dr2 = Dr*Dr
one_twelve = flt(flt(1.0)/flt(12.0))
# u s=-[du/dr]/r umm=[d2u/dr2]
u_bare, s_bare, umm_bare = pair_pot(dist, params)
u_c, s_c, umm_c = pair_pot(r_c, params)
um_c = -s_c*r_c
C = - u_c + flt(0.5) * Dr * um_c - one_twelve * Dr2 * umm_c
if dist <= r_1: # inside the inner cutoff (r<r_1)
u = u_bare + C
s = s_bare
umm = umm_bare
elif dist < r_c: # in the switching region (r_1<r<r_c)
dr3 = dr2 * dr
dr4 = dr2 * dr2
one_third = flt(flt(1.0) / flt(3.0))
Dr3 = Dr2 * Dr
A = - flt(3.0) * um_c + Dr * umm_c
A /= Dr2
B = flt(2.0) * um_c - Dr * umm_c
B /= Dr3
S = one_third * A * dr3 + flt(0.25)*B*dr4 + C
u = u_bare + S
S_m = A*dr2 + B*dr3 # derivative of S with respect to r
s = s_bare - S_m/dist
S_mm = 2*A*dr + 3*B*dr2
umm = umm_bare + S_mm
else: # outside the cutoff (r>r_c)
u, s, umm = flt(0.0), flt(0.0), flt(0.0)
return u, s, umm
return potential