Source code for idpconfgen.libs.libenergyij
"""
Functions to calculate energy from i-j pairs evaluation.
Contains only energy terms that evaluate energy for each atom-atom pair
separately.
"""
import numpy as np
from numba import njit
from idpconfgen import log
from idpconfgen.logger import S
post_calc_options = [
'pairs',
'whole',
]
"""Option keys to apply after calculating energy value for each pair."""
default_post_calc_option = post_calc_options[0]
[docs]def init_lennard_jones_calculator(
acoeff,
bcoeff,
postf=default_post_calc_option,
):
"""
Calculate Lennard-Jones full pontential.
The LJ potential is calculated fully and no approximations to
proximity of infinite distance are considered.
Parameters
----------
acoeff, bcoeff : np.ndarray, shape (N, 3), dtype=np.float
The LJ coefficients prepared already for the ij-pairs upon which
the resulting function is expected to operate.
IMPORTANT: it is up to the user to define the coefficients such
that resulting energy is np.nan for non-relevant ij-pairs, for
example, covalently bonded pairs, or pairs 2 bonds apart.
postf : str
There are different implementations of the energy calculation.
Current options are:
"whole": applies np.nansum after calculating the energy per
pair.
"pairs": returns the energies per pair.
Returns
-------
numba.njitted func
Function closure with registered `acoeff`s and `bcoeff`s that
expects an np.ndarray of distances with same shape as `acoeff`
and `bcoeff`: (N,). The `func` return value depends on the
`postf` options.
"""
@njit
def calculate(distances_ij):
ar = acoeff / (distances_ij ** 12)
br = bcoeff / (distances_ij ** 6)
return ar - br
@njit
def calculate_nansum(distances_ij, NANSUM=np.nansum):
energy_ij = calculate(distances_ij)
return NANSUM(energy_ij)
# has to be aligned with post_calc_options
_options = [
calculate,
calculate_nansum,
]
options = {k: v for k, v in zip(post_calc_options, _options)}
log.info(S(f'Lennard-Jones type configured to: {postf!r}'))
return options[postf]
[docs]def init_coulomb_calculator(charges_ij, postf=default_post_calc_option):
"""
Calculate Coulomb portential.
Parameters
----------
charges_ij : np.ndarray, shape (N, 3), dtype=np.float
The `charges_ij` prepared already for the ij-pairs upon which
the resulting function is expected to operate.
IMPORTANT: it is up to the user to define the charge such
that resulting energy is np.nan for non-relevant ij-pairs, for
example, covalently bonded pairs, or pairs 2 bonds apart.
postf : str
There are different implementations of the energy calculation.
Current options are:
"whole": applies np.nansum after calculating the energy per
pair.
"pairs": returns the energies per pair.
Returns
-------
numba.njitted func
Function closure with registered `charges_ij` that expects an
np.ndarray of distances with same shape as `acoeff` and `bcoeff`:
(N,). The `func` return value depends on the `postf` options.
"""
@njit
def calculate(distances_ij):
return charges_ij / distances_ij
@njit
def calculate_nansum(distances_ij, NANSUM=np.nansum):
return NANSUM(calculate(distances_ij))
# has to be aligned with post_calc_options
_options = [
calculate,
calculate_nansum,
]
options = {k: v for k, v in zip(post_calc_options, _options)}
log.info(S(f'Coulomb type configured to: {postf!r}'))
return options[postf]
[docs]def energycalculator_ij(distf, efuncs):
"""
Calculate the sum of energy terms.
This function works as a closure.
Accepts only energy terms that compute for non-redundant ij-pairs.
Energy terms must have distances ij as unique positional parameter,
and should return an integer.
Example
-------
>>> ecalc = energycalculator_ij(calc_ij_pair_distances, [...])
>>> total_energy = ecalc(coords)
Where `[...]` is a list containing energy term functions.
See Also
--------
init_lennard_jones_calculator
init_coulomb_calculator
Parameters
----------
distf : func
The function that will be used to calculate ij-pair distances
on each call. If performance is a must, this function should be
fast. `distf` function should receive `coords` as unique
argument where `coords` is a np.ndarray of shape (N, 3), where N
is the number of atoms, and 3 represents the XYZ coordinates.
This function should return a np.ndarray of shape
(N * (N - 1)) / 2,), dtype=np.float.
efuncs : list
A list containing the energy terms functions. Energy term
functions are prepared closures that accept the output of
`distf` function.
Returns
-------
func
A function that accepts coords in the form of (N, 3). The
coordinates sent to the resulting function MUST be aligned with
the labels used to prepare the `efuncs` closures.
"""
def calculate(coords):
dist_ij = distf(coords)
energy = 0
for func in efuncs:
energy += func(dist_ij)
return energy
return calculate