"""Module for coherent FQt class"""
import numpy as np
import periodictable
from MDMC.common.mathematics import faster_correlation
from MDMC.trajectory_analysis.observables.fqt import AbstractFQt
from MDMC.trajectory_analysis.observables.obs_factory import ObservableFactory
[docs]
@ObservableFactory.register(('CoherentIntermediateScatteringFunction',
'FQtCoherent',
'FQtCoh',
'FQt_coh'))
class FQtCoherent(AbstractFQt):
"""
A class for containing, calculating and reading the intermediate scattering
function for the coherent dynamic structure factor
"""
def _set_weights(self) -> None:
"""Calculate the neutron weighting for coherent scattering"""
self.weights = {element: periodictable.elements.symbol(element).neutron.b_c \
if periodictable.elements.symbol(element).neutron.b_c is not None \
else 0 for element in self._trajectory.element_set}
def _calculate_FQt_single_Q(self, single_Q_vectors: list) -> 'np.ndarray':
# Inherit docstring of abstract method
n_t = len(self.t)
elements = self._trajectory.element_set
FQt_single_Q = np.zeros(n_t)
rho_element = {}
n_atoms = 0
for element in elements:
# Get the positions of all atoms (the configuration) of each
# element over time such that ``element_configs`` has time as its
# first dimension and each atom of ``element`` as its second
indexes = np.where(np.array(self._trajectory.element_list)
== element)
element_configs = self._trajectory.position[:, indexes[0], :]
rho_element[element] = self.calculate_rho_config(element_configs, single_Q_vectors)
n_atoms += np.shape(indexes)[1]
for element1 in elements:
for element2 in elements:
# A sum over the Q vectors is performed within ``correlation``.
FQt_single_Q += self.weights[element1] \
* self.weights[element2] \
* faster_correlation(rho_element[element1],
rho_element[element2])[:n_t]
# Normalise to the number of orthogonal vectors
try:
norm = np.shape(single_Q_vectors)[0]
except IndexError:
norm = 1
return FQt_single_Q / (n_atoms * norm)