#!/usr/bin/env python## Copyright (c) 2025 Authors and contributors# (see the AUTHORS.rst file for the full list of names)## Released under the GNU Public Licence, v3 or any higher version# SPDX-License-Identifier: GPL-3.0-or-later"""Module for computing kinetic energy timeseries."""importloggingimportMDAnalysisasmdaimportnumpyasnpfrom..coreimportAnalysisBasefrom..lib.utilimportget_compound,render_docs
[docs]@render_docsclassKineticEnergy(AnalysisBase):"""Kinetic energy timeseries. The kinetic energy function computes the translational and rotational kinetic energy with respect to molecular center (center of mass, center of charge) of a molecular dynamics simulation trajectory. The analysis can be applied to study the dynamics of water molecules during an excitation pulse. For more details read :footcite:t:`elgabartyEnergyTransferHydrogen2020`. Parameters ---------- ${ATOMGROUP_PARAMETER} ${BASE_CLASS_PARAMETERS} refpoint : str reference point for molecular center: center of mass (``"com"``) or center of charge (``"coc"``). ${OUTPUT_PARAMETER} Attributes ---------- results.t : numpy.ndarray time (ps). results.trans : numpy.ndarray translational kinetic energy (kJ/mol). results.rot : numpy.ndarray rotational kinetic energy (kJ/mol). References ---------- .. footbibliography:: """def__init__(self,atomgroup:mda.AtomGroup,unwrap:bool=False,pack:bool=True,refgroup:mda.AtomGroup|None=None,jitter:float=0.0,concfreq:int=0,output:str="ke.dat",refpoint:str="com",)->None:self._locals=locals()self.comp=get_compound(atomgroup)super().__init__(atomgroup,unwrap=unwrap,pack=pack,refgroup=refgroup,jitter=jitter,concfreq=concfreq,wrap_compound=self.comp,)self.output=outputself.refpoint=refpoint.lower()def_prepare(self)->None:"""Set things up before the analysis loop begins."""logging.info("Analysis of the kinetic energy timeseries.")ifself.refpointnotin["com","coc"]:raiseValueError(f"Invalid choice for dens: {self.refpoint} (choose from 'com' or 'coc')")self.masses=self.atomgroup.accumulate(self.atomgroup.masses,compound=self.comp)self.abscharges=self.atomgroup.accumulate(np.abs(self.atomgroup.charges),compound=self.comp)# Total kinetic energyself.E_kin=np.zeros(self.n_frames)# Molecular center energyself.E_center=np.zeros(self.n_frames)def_single_frame(self)->None:self.E_kin[self._frame_index]=np.dot(self.atomgroup.masses,np.linalg.norm(self.atomgroup.velocities,axis=1)**2,)ifself.refpoint=="com":massvel=self.atomgroup.velocities*self.atomgroup.masses[:,np.newaxis]v=self.atomgroup.accumulate(massvel,compound=get_compound(self.atomgroup))v/=self.masses[:,np.newaxis]elifself.refpoint=="coc":abschargevel=(self.atomgroup.velocities*np.abs(self.atomgroup.charges)[:,np.newaxis])v=self.atomgroup.accumulate(abschargevel,compound=get_compound(self.atomgroup))v/=self.abscharges[:,np.newaxis]self.E_center[self._frame_index]=np.dot(self.masses,np.linalg.norm(v,axis=1)**2)def_conclude(self)->None:self.results.t=self.timesself.results.trans=self.E_center/2/100self.results.rot=(self.E_kin-self.E_center)/2/100