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.pot_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
"""
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
@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
@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((3, potential.num_species, potential.num_species))
potential.matrix[1, :, :] = 1.0 / potential.screening_length
for i, sp1 in enumerate(species):
for j, sp2 in enumerate(species):
potential.matrix[0, i, j] = 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}")