11import logging
22
33import numpy as np
4+ from MDAnalysis .lib .mdamath import make_whole
45
56logger = logging .getLogger (__name__ )
67
@@ -49,9 +50,7 @@ def get_residue_axes(self, data_container, index):
4950 f"and bonded resid { index } "
5051 )
5152 residue = data_container .select_atoms (f"resindex { index } " )
52- # set center of rotation to center of mass of the residue
53- # we might want to change to center of bonding later
54- center = residue .atoms .center_of_mass ()
53+ center = residue .atoms .center_of_mass (unwrap = True )
5554
5655 if len (atom_set ) == 0 :
5756 # No bonds to other residues
@@ -61,7 +60,7 @@ def get_residue_axes(self, data_container, index):
6160 UAs = residue .select_atoms ("mass 2 to 999" )
6261 UA_masses = self .get_UA_masses (residue )
6362 moment_of_inertia_tensor = self .get_moment_of_inertia_tensor (
64- center , UAs .positions , UA_masses
63+ center , UAs .positions , UA_masses , data_container . dimensions [: 3 ]
6564 )
6665 rot_axes , moment_of_inertia = self .get_custom_principal_axes (
6766 moment_of_inertia_tensor
@@ -71,11 +70,13 @@ def get_residue_axes(self, data_container, index):
7170 )
7271 else :
7372 # if bonded to other residues, use default axes and MOI
73+ make_whole (data_container .atoms )
7474 trans_axes = data_container .atoms .principal_axes ()
75- rot_axes = np .real (residue .principal_axes ())
76- eigenvalues , _ = np .linalg .eig (residue .moment_of_inertia ())
77- moment_of_inertia = sorted (eigenvalues , reverse = True )
78- center = residue .center_of_mass ()
75+ rot_axes , moment_of_inertia = self .get_vanilla_axes (residue )
76+ # rot_axes = np.real(residue.principal_axes())
77+ # eigenvalues, _ = np.linalg.eig(residue.moment_of_inertia(unwrap=True))
78+ # moment_of_inertia = sorted(eigenvalues, reverse=True)
79+ center = residue .center_of_mass (unwrap = True )
7980
8081 return trans_axes , rot_axes , center , moment_of_inertia
8182
@@ -96,13 +97,12 @@ def get_UA_axes(self, data_container, index):
9697
9798 index = int (index )
9899
99- # trans_axes = data_container.atoms.principal_axes()
100100 # use the same customPI trans axes as the residue level
101101 UAs = data_container .select_atoms ("mass 2 to 999" )
102102 UA_masses = self .get_UA_masses (data_container .atoms )
103- center = data_container .atoms .center_of_mass ()
103+ center = data_container .atoms .center_of_mass (unwrap = True )
104104 moment_of_inertia_tensor = self .get_moment_of_inertia_tensor (
105- center , UAs .positions , UA_masses
105+ center , UAs .positions , UA_masses , data_container . dimensions [: 3 ]
106106 )
107107 trans_axes , _moment_of_inertia = self .get_custom_principal_axes (
108108 moment_of_inertia_tensor
@@ -185,9 +185,12 @@ def get_bonded_axes(self, system, atom, dimensions):
185185 # find the heavy bonded atoms and light bonded atoms
186186 heavy_bonded , light_bonded = self .find_bonded_atoms (atom .index , system )
187187 UA = atom + light_bonded
188+ UA_all = atom + heavy_bonded + light_bonded
188189
189190 # now find which atoms to select to find the axes for rotating forces:
190- # case1, won't apply to UA level
191+ # case1
192+ if len (heavy_bonded ) == 0 :
193+ custom_axes , custom_moment_of_inertia = self .get_vanilla_axes (UA_all )
191194 # case2
192195 if len (heavy_bonded ) == 1 and len (light_bonded ) == 0 :
193196 custom_axes = self .get_custom_axes (
@@ -201,7 +204,7 @@ def get_bonded_axes(self, system, atom, dimensions):
201204 light_bonded [0 ].position ,
202205 dimensions ,
203206 )
204- # case4, not used in Jon's code, use case5 instead
207+ # case4, not used in Jon's 2019 paper code, use case5 instead
205208 # case5
206209 if len (heavy_bonded ) >= 2 :
207210 custom_axes = self .get_custom_axes (
@@ -214,7 +217,7 @@ def get_bonded_axes(self, system, atom, dimensions):
214217 if custom_moment_of_inertia is None :
215218 # find moment of inertia using custom axes and atom position as COM
216219 custom_moment_of_inertia = self .get_custom_moment_of_inertia (
217- UA , custom_axes , atom .position
220+ UA , custom_axes , atom .position , dimensions
218221 )
219222
220223 # get the moment of inertia from the custom axes
@@ -243,6 +246,33 @@ def find_bonded_atoms(self, atom_idx: int, system):
243246 bonded_H_atoms = bonded_atoms .select_atoms ("mass 1 to 1.1" )
244247 return bonded_heavy_atoms , bonded_H_atoms
245248
249+ def get_vanilla_axes (self , molecule ):
250+ """
251+ From a selection of atoms, get the ordered principal axes (3,3) and
252+ the ordered moment of inertia axes (3,) for that selection of atoms
253+
254+ Args:
255+ molecule: mdanalysis instance of molecule
256+ molecule_scale: the length scale of molecule
257+
258+ Returns:
259+ principal_axes: the principal axes, (3,3) array
260+ moment_of_inertia: the moment of inertia, (3,) array
261+ """
262+ # default moment of inertia
263+ moment_of_inertia = molecule .moment_of_inertia (unwrap = True )
264+ make_whole (molecule .atoms )
265+ principal_axes = molecule .principal_axes ()
266+ # diagonalise moment of inertia tensor here
267+ # pylint: disable=unused-variable
268+ eigenvalues , _eigenvectors = np .linalg .eig (moment_of_inertia )
269+ # sort eigenvalues of moi tensor by largest to smallest magnitude
270+ order = sorted (eigenvalues , reverse = True ) # decending order
271+ # principal_axes = principal_axes[order] # PI already ordered correctly
272+ moment_of_inertia = eigenvalues [order ]
273+
274+ return principal_axes , moment_of_inertia
275+
246276 def get_custom_axes (
247277 self , a : np .ndarray , b_list : list , c : np .ndarray , dimensions : np .ndarray
248278 ):
@@ -303,7 +333,11 @@ def get_custom_axes(
303333 return scaled_custom_axes
304334
305335 def get_custom_moment_of_inertia (
306- self , UA , custom_rotation_axes : np .ndarray , center_of_mass : np .ndarray
336+ self ,
337+ UA ,
338+ custom_rotation_axes : np .ndarray ,
339+ center_of_mass : np .ndarray ,
340+ dimensions : np .ndarray ,
307341 ):
308342 """
309343 Get the moment of inertia (specifically used for the united atom level)
@@ -318,7 +352,7 @@ def get_custom_moment_of_inertia(
318352 Returns:
319353 custom_moment_of_inertia: (3,) array for moment of inertia
320354 """
321- translated_coords = UA .positions - center_of_mass
355+ translated_coords = self . get_vector ( center_of_mass , UA .positions , dimensions )
322356 custom_moment_of_inertia = np .zeros (3 )
323357 for coord , mass in zip (translated_coords , UA .masses ):
324358 axis_component = np .sum (
@@ -366,27 +400,24 @@ def get_vector(self, a: np.ndarray, b: np.ndarray, dimensions: np.ndarray):
366400 For vector of two coordinates over periodic boundary conditions (PBCs).
367401
368402 Args:
369- a: (3, ) array of atom cooordinates
403+ a: (N,3 ) array of atom cooordinates
370404 b: (3,) array of atom cooordinates
371405 dimensions: (3,) array of system box dimensions.
372406
373407 Returns:
374- delta_wrapped: (3, ) array of the vector between two points
408+ delta_wrapped: (N,3 ) array of the vector
375409 """
376410 delta = b - a
377- delta_wrapped = []
378- for delt , box in zip (delta , dimensions ):
379- if delt < 0 and delt < 0.5 * box :
380- delt = delt + box
381- if delt > 0 and delt > 0.5 * box :
382- delt = delt - box
383- delta_wrapped .append (delt )
384- delta_wrapped = np .array (delta_wrapped )
411+ delta -= dimensions * np .round (delta / dimensions )
385412
386- return delta_wrapped
413+ return delta
387414
388415 def get_moment_of_inertia_tensor (
389- self , center_of_mass : np .ndarray , positions : np .ndarray , masses : list
416+ self ,
417+ center_of_mass : np .ndarray ,
418+ positions : np .ndarray ,
419+ masses : list ,
420+ dimensions : np .array ,
390421 ) -> np .ndarray :
391422 """
392423 Calculate a custom moment of inertia tensor.
@@ -402,7 +433,7 @@ def get_moment_of_inertia_tensor(
402433 Returns:
403434 moment_of_inertia_tensor: a (3,3) moment of inertia tensor
404435 """
405- r = positions - center_of_mass
436+ r = self . get_vector ( center_of_mass , positions , dimensions )
406437 r2 = np .sum (r ** 2 , axis = 1 )
407438 moment_of_inertia_tensor = np .eye (3 ) * np .sum (masses * r2 )
408439 moment_of_inertia_tensor -= np .einsum ("i,ij,ik->jk" , masses , r , r )
0 commit comments