r"""
Module for handling Hard-Sphere Yukawa potential.
Potential
*********
The Hard-Sphere Yukawa potential between two charges :math:`q_i` and :math:`q_j` at distant :math:`r` is defined as
.. math::
U_{ab}(r) = \left ( \frac{\sigma}{r} \right )^{n} + \frac{q_a q_b}{4 \pi \epsilon_0} \frac{e^{- \kappa r} }{r}.
where :math:`\kappa = 1/\lambda` is the screening parameter.
Potential Attributes
********************
The elements of the :attr:`sarkas.potentials.core.Potential.matrix` are:
.. code-block:: python
pot_matrix[0] = q_iq_j^2/(4 pi eps0)
pot_matrix[1] = 1/lambda
pot_matrix[2] = Ewald screening parameter
pot_matrix[3] = short range cutoff
"""
# TODO: Review this entire module. It was left unchecked because the potential is too steep at small r.
from numba import njit
from numpy import exp, pi, sqrt
from numpy import zeros as np_zeros
from warnings import warn
from ..utilities.maths import force_error_analytic_lcl
[docs]@njit
def hs_yukawa_force(r, pot_matrix):
"""
Calculates Potential and Force between two particles.
Parameters
----------
r : float
Distance between two particles.
pot_matrix : numpy.ndarray
It contains potential dependent variables. \n
Shape = (3, :attr:`sarkas.core.Parameters.num_species`, :attr:`sarkas.core.Parameters.num_species`)
Returns
-------
U : float
Potential.
force : float
Force between two particles.
"""
U_y = pot_matrix[0] * exp(-pot_matrix[1] * r) / r
U_hs = (pot_matrix[2] / r) ** (50)
U = U_y + U_hs
force = U_y * (1.0 / r + pot_matrix[1]) + (50.0) * U_hs / r
return U, force
[docs]@njit
def force_deriv(r, pot_matrix):
"""Calculate the second derivative of the potential.
Parameters
----------
r : float
Distance between particles
pot_matrix : numpy.ndarray
Values of the potential constants. \n
Shape = (3, :attr:`sarkas.core.Parameters.num_species`, :attr:`sarkas.core.Parameters.num_species`)
Returns
-------
f_dev : float
Second derivative of potential.
"""
kappa_r = pot_matrix[1] * r
U2 = pot_matrix[0] * exp(-kappa_r) / r**3
f_dev = U2 * (2.0 * (1.0 + kappa_r) + kappa_r**2)
return f_dev
[docs]def update_params(potential, species):
"""
Assign potential dependent simulation's parameters.
Parameters
----------
potential : :class:`sarkas.potentials.core.Potential`
Class handling potential form.
"""
# Potential specific parameters
potential.packing_fraction = pi / 6.0 * potential.total_num_density * potential.hs_diameter**3
if hasattr(potential, "kappa") and potential.screening_length is not None:
warn(
"You have defined both kappa and the screening_length. \n"
"I will use kappa to calculate the screening_length from lambda = sigma/kappa"
)
potential.screening_length = potential.hs_diameter / potential.kappa
elif hasattr(potential, "kappa"):
potential.screening_length = potential.hs_diameter / potential.kappa
elif potential.screening_length:
potential.kappa = potential.hs_diameter / potential.screening_length
# Interaction Matrix
potential.matrix = np_zeros((potential.num_species, potential.num_species, 3))
potential.matrix[:, :, 1] = 1.0 / potential.screening_length
for i, sp1 in enumerate(species):
for j, sp2 in enumerate(species):
potential.matrix[i, j, 0] = sp1.charge * sp2.charge / potential.fourpie0
potential.matrix[:, :, 2] = potential.hs_diameter
if potential.method == "pp":
# The rescaling constant is sqrt ( n sigma^4 ) = sqrt( 6 eta *sigma/pi )
potential.force = hs_yukawa_force
potential.force_error = force_error_analytic_lcl(
"yukawa", potential.rc, potential.matrix, sqrt(6.0 * potential.packing_fraction * potential.hs_diameter / pi)
)
# # Force error calculated from eq.(43) in Ref.[1]_
# potential.force_error = sqrt( TWOPI / potential.electron_TF_wavelength) * exp(- potential.rc / potential.electron_TF_wavelength)
# # Renormalize
# potential.force_error *= potential.a_ws ** 2 * sqrt(potential.total_num_ptcls / potential.pbox_volume)
elif potential.method == "pppm":
raise ValueError("PPPM algorithm not supported.")
[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.
"""
b = potential.hs_diameter / potential.a_ws
print(f"hard sphere diameter = {b:.4f} a_ws = {potential.hs_diameter:.4e} ", end="")
print("[cm]" if potential.units == "cgs" else "[m]")
print(f"screening length = {potential.screening_length} ", end="")
print("[cm]" if potential.units == "cgs" else "[m]")
print(f"kappa = sigma/lambda = {potential.kappa:.4f}")
print(f"reduced density = n sigma^3 = {potential.hs_diameter ** 3 * potential.total_num_density:.4f}")
print(f"packing fraction = {potential.packing_fraction:.4f}")
print(f"Gamma_eff = {potential.coupling_constant / b:.4f}")