r"""
Module for handling Lennard-Jones interaction.
Potential
*********
The generalized Lennard-Jones potential is defined as
.. math::
U_{\mu\nu}(r) = k \epsilon_{\mu\nu} \left [ \left ( \frac{\sigma_{\mu\nu}}{r}\right )^m -
\left ( \frac{\sigma_{\mu\nu}}{r}\right )^n \right ],
where
.. math::
k = \frac{n}{m-n} \left ( \frac{n}{m} \right )^{\frac{m}{n-m}}.
In the case of multispecies liquids we use the `Lorentz-Berthelot <https://en.wikipedia.org/wiki/Combining_rules>`_
mixing rules
.. math::
\epsilon_{12} = \sqrt{\epsilon_{11} \epsilon_{22}}, \quad \sigma_{12} = \frac{\sigma_{11} + \sigma_{22}}{2}.
Force Error
***********
The force error for the LJ potential is given by
.. math::
\Delta F = \frac{k\epsilon}{ \sqrt{2\pi n}} \left [ \frac{m^2 \sigma^{2m}}{2m - 1} \frac{1}{r_c^{2m -1}}
+ \frac{n^2 \sigma^{2n}}{2n - 1} \frac{1}{r_c^{2n -1}} \
-\frac{2 m n \sigma^{m + n}}{m + n - 1} \frac{1}{r_c^{m + n -1}} \
\right ]^{1/2}
which we approximate with the first term only
.. math::
\Delta F \approx \frac{k\epsilon} {\sqrt{2\pi n} }
\left [ \frac{m^2 \sigma^{2m}}{2m - 1} \frac{1}{r_c^{2m -1}} \right ]^{1/2}
Potential Attributes
********************
The elements of the :attr:`sarkas.potentials.core.Potential.pot_matrix` are:
.. code-block::
pot_matrix[0] = epsilon_12 * lj_constant
pot_matrix[1] = sigmas
pot_matrix[2] = highest power
pot_matrix[3] = lowest power
pot_matrix[4] = short-range cutoff
"""
from numba import jit
from numba.core.types import float64, UniTuple
from numpy import array, pi, sqrt, zeros
from ..utilities.maths import force_error_analytic_lcl
[docs]def update_params(potential):
"""
Assign potential dependent simulation's parameters.
Parameters
----------
potential : :class:`sarkas.potentials.core.Potential`
Class handling potential form.
"""
potential.matrix = zeros((5, potential.num_species, potential.num_species))
# See Lima Physica A 391 4281 (2012) for the following definitions
if not hasattr(potential, "powers"):
potential.powers = array([12, 6])
exponent = potential.powers[0] / (potential.powers[1] - potential.powers[0])
lj_constant = potential.powers[1] / (potential.powers[0] - potential.powers[1])
lj_constant *= (potential.powers[1] / potential.powers[0]) ** exponent
# Use the Lorentz-Berthelot mixing rules.
# Lorentz: sigma_12 = 0.5 * (sigma_1 + sigma_2)
# Berthelot: epsilon_12 = sqrt( eps_1 eps2)
potential.epsilon_tot = 0.0
# Recall that species_charges contains sqrt(epsilon)
for i, q1 in enumerate(potential.species_charges):
for j, q2 in enumerate(potential.species_charges):
potential.matrix[0, i, j] = lj_constant * q1 * q2
potential.matrix[1, i, j] = 0.5 * (potential.species_lj_sigmas[i] + potential.species_lj_sigmas[j])
potential.matrix[2, i, j] = potential.powers[0]
potential.matrix[3, i, j] = potential.powers[1]
potential.epsilon_tot += q1 * q2
potential.sigma_avg = potential.species_lj_sigmas.mean()
potential.matrix[4, :, :] = potential.a_rs
potential.force = lj_force
# The rescaling constant is sqrt ( na^4 ) = sqrt( 3 a/(4pi) )
potential.force_error = force_error_analytic_lcl(
potential.type, potential.rc, potential.matrix, sqrt(3.0 * potential.a_ws / (4.0 * pi))
)
@jit(UniTuple(float64, 2)(float64, float64[:]), nopython=True)
def lj_force(r_in, pot_matrix):
"""
Numba'd function to calculate the PP force between particles using Lennard-Jones Potential.
Parameters
----------
r_in : float
Particles' distance.
pot_matrix : numpy.ndarray
LJ potential parameters. \n
Shape = (5, :attr:`sarkas.core.Parameters.num_species`, :attr:`sarkas.core.Parameters.num_species`)
Returns
-------
U : float
Potential.
force : float
Force.
Examples
--------
>>> pot_const = 4.0 * 1.656e-21 # 4*epsilon in [J] (mks units)
>>> sigma = 3.4e-10 # [m] (mks units)
>>> high_pow, low_pow = 12., 6.
>>> short_cutoff = 0.0001 * sigma
>>> pot_mat = array([pot_const, sigma, high_pow, low_pow, short_cutoff])
>>> r = 15.0 * sigma # particles' distance in [m]
>>> lj_force(r, pot_mat)
(-5.815308131440668e-28, -6.841538377536503e-19)
"""
rs = pot_matrix[4]
# Branchless programming
r = r_in * (r_in >= rs) + rs * (r_in < rs)
epsilon = pot_matrix[0]
sigma = pot_matrix[1]
s_over_r = sigma / r
s_over_r_high = s_over_r ** pot_matrix[2]
s_over_r_low = s_over_r ** pot_matrix[3]
U = epsilon * (s_over_r_high - s_over_r_low)
force = epsilon * (pot_matrix[2] * s_over_r_high - pot_matrix[3] * s_over_r_low) / r
return U, force
[docs]def pretty_print_info(potential):
"""
Print potential specific parameters in a user-friendly way.
Parameters
----------
potential : :class:`sarkas.potentials.core.Potential`
Class handling potential form.
"""
print(f"epsilon_tot = {potential.epsilon_tot:.6e}")
print(f"sigma_avg = {potential.sigma_avg:.6e}")
rho = potential.sigma_avg**3 * potential.total_num_density
tau = potential.kB * potential.T_desired / potential.epsilon_tot
print(f"reduced density = {rho:.6e}")
print(f"reduced temperature = {tau:.6e}")