Source code for waterEntropy.entropy.convariances

"""
Store the rotated mass-weighted forces and inertia-weighted torques in this class.
"""

import numpy as np

from waterEntropy.utils.helpers import nested_dict


[docs] class CovarianceCollection: """ Class for covariance matrices molecules, grouped by what their closest non-like molecule is. """
[docs] def __init__(self): self.forces = nested_dict() self.torques = nested_dict() self.counts = nested_dict()
[docs] def add_data( self, nearest, molecule_name: str, force: np.ndarray, torque: np.ndarray ): """ Add force, torque covariances to the class dictionaries :param nearest: the value for grouping molecules into a class. e.g. the name of the nearest molecule :param molecule_name: the name of the molecule being populated into the dictionaries :param force: array of the mass-weighted forces of the molecule :param torque: array of the inertia-weighted torques of the molecule """ # order is important, update count last CovarianceCollection.populate_dicts( self, nearest, molecule_name, force, self.forces, self.counts ) CovarianceCollection.populate_dicts( self, nearest, molecule_name, torque, self.torques, self.counts ) CovarianceCollection.update_counts(self, nearest, molecule_name, self.counts)
[docs] def populate_dicts( self, nearest, molecule_name, variable, variable_dict, count_dict ): # pylint: disable=too-many-arguments """ For a given molecule, append the summed, weighted and rotated forces, and the torques for the whole molecule. Add as a running average by keeping count of the number of molecules added. :param self: class instance :param nearest: the value for grouping molecules into a class. e.g. the name of the nearest molecule :param molecule_name: name of molecule :param variable: variable to update a dict :param variable_dict: the dictionary where updated variables are added :param count_dict: the dictionary where counts are updated """ # add molecule name to dicts if it doesn't exist if (nearest, molecule_name) not in variable_dict: variable_dict[(nearest, molecule_name)] = variable else: # get running average of forces and torques stored_variable = variable_dict[(nearest, molecule_name)] stored_count = count_dict[(nearest, molecule_name)] updated_variable = (stored_variable * stored_count + variable) / ( stored_count + 1 ) # update dictionaries with running averages variable_dict[(nearest, molecule_name)] = updated_variable
[docs] def update_counts(self, nearest, molecule_name, count_dict): """Update the counts in dictionary :param nearest: the value for grouping molecules into a class. e.g. the name of the nearest molecule :param molecule_name: name of molecule :param count_dict: the dictionary where counts are updated """ if (nearest, molecule_name) not in count_dict: count_dict[(nearest, molecule_name)] = 1 else: count_dict[(nearest, molecule_name)] += 1
[docs] def merge(self, other): """ Merge another CovarianceCollection into this one. :param other: another CovarianceCollection to merge """ for key in other.forces: if key not in self.forces: self.forces[key] = other.forces[key] self.torques[key] = other.torques[key] self.counts[key] = other.counts[key] else: # weighted sum of the forces and torques self.forces[key] = ( self.forces[key] * self.counts[key] + other.forces[key] * other.counts[key] ) / (self.counts[key] + other.counts[key]) self.torques[key] = ( self.torques[key] * self.counts[key] + other.torques[key] * other.counts[key] ) / (self.counts[key] + other.counts[key]) # sum of the counts self.counts[key] += other.counts[key]