@@ -237,40 +237,53 @@ def get_weighted_forces(
237237 self , data_container , bead , trans_axes , highest_level , force_partitioning
238238 ):
239239 """
240- Function to calculate the mass weighted forces for a given bead.
240+ Compute mass- weighted translational forces for a bead.
241241
242- Args:
243- data_container (MDAnalysis.Universe): Contains atomic positions and forces.
244- bead : The part of the molecule to be considered.
245- trans_axes (np.ndarray): The axes relative to which the forces are located.
246- highest_level (bool): Is this the largest level of the length scale hierarchy
247- force_partitioning (float): Factor to adjust force contributions to avoid
248- over counting correlated forces, default is 0.5.
242+ The forces acting on all atoms belonging to the bead are first transformed
243+ into the provided translational reference frame and summed. If this bead
244+ corresponds to the highest level of a hierarchical coarse-graining scheme,
245+ the total force is scaled by a force-partitioning factor to avoid double
246+ counting forces from weakly correlated atoms.
249247
250- Returns:
251- weighted_force (np.ndarray): The mass-weighted sum of the forces in the
252- bead.
253- """
248+ The resulting force vector is then normalized by the square root of the
249+ bead's total mass.
250+
251+ Parameters
252+ ----------
253+ data_container : MDAnalysis.Universe
254+ Container holding atomic positions and forces.
255+ bead : object
256+ Molecular subunit whose atoms contribute to the force.
257+ trans_axes : np.ndarray
258+ Transformation matrix defining the translational reference frame.
259+ highest_level : bool
260+ Whether this bead is the highest level in the length-scale hierarchy.
261+ If True, force partitioning is applied.
262+ force_partitioning : float
263+ Scaling factor applied to forces to avoid over-counting correlated
264+ contributions (typically 0.5).
265+
266+ Returns
267+ -------
268+ weighted_force : np.ndarray
269+ Mass-weighted translational force acting on the bead.
254270
271+ Raises
272+ ------
273+ ValueError
274+ If the bead mass is zero or negative.
275+ """
255276 forces_trans = np .zeros ((3 ,))
256277
257- # Sum forces from all atoms in the bead
258278 for atom in bead .atoms :
259- # update local forces in translational axes
260279 forces_local = np .matmul (trans_axes , data_container .atoms [atom .index ].force )
261280 forces_trans += forces_local
262281
263282 if highest_level :
264- # multiply by the force_partitioning parameter to avoid double counting
265- # of the forces on weakly correlated atoms
266- # the default value of force_partitioning is 0.5 (dividing by two)
267283 forces_trans = force_partitioning * forces_trans
268284
269- # divide the sum of forces by the mass of the bead to get the weighted forces
270285 mass = bead .total_mass ()
271286
272- # Check that mass is positive to avoid division by 0 or negative values inside
273- # sqrt
274287 if mass <= 0 :
275288 raise ValueError (
276289 f"Invalid mass value: { mass } . Mass must be positive to compute the "
@@ -285,91 +298,81 @@ def get_weighted_forces(
285298
286299 def get_weighted_torques (self , data_container , bead , rot_axes , force_partitioning ):
287300 """
288- Function to calculate the moment of inertia weighted torques for a given bead.
301+ Compute moment-of- inertia weighted torques for a bead.
289302
290- This function computes torques in a rotated frame and then weights them using
291- the moment of inertia tensor. To prevent numerical instability, it treats
292- extremely small diagonal elements of the moment of inertia tensor as zero
293- (since values below machine precision are effectively zero). This avoids
294- unnecessary use of extended precision (e.g., float128) .
303+ Atomic coordinates and forces are transformed into the provided rotational
304+ reference frame. Torques are computed as the cross product of position
305+ vectors (relative to the bead center of mass) and forces, with a
306+ force-partitioning factor applied to reduce over-counting of correlated
307+ atomic contributions .
295308
296- Additionally, if the computed torque is already zero, the function skips
297- the division step, reducing unnecessary computations and potential errors.
309+ The total torque vector is then weighted by the square root of the bead's
310+ principal moments of inertia. Weighting is performed component-wise using
311+ the sorted eigenvalues of the moment of inertia tensor.
312+
313+ To ensure numerical stability:
314+ - Torque components that are effectively zero are skipped.
315+ - Zero moments of inertia result in zero weighted torque with a warning.
316+ - Negative moments of inertia raise an error.
298317
299318 Parameters
300319 ----------
301320 data_container : object
302- Contains atomic positions and forces.
321+ Container holding atomic positions and forces.
303322 bead : object
304- The part of the molecule to be considered .
323+ Molecular subunit whose atoms contribute to the torque .
305324 rot_axes : np.ndarray
306- The axes relative to which the forces and coordinates are located.
307- force_partitioning : float, optional
308- Factor to adjust force contributions, default is 0.5.
325+ Transformation matrix defining the rotational reference frame.
326+ force_partitioning : float
327+ Scaling factor applied to forces to avoid over-counting correlated
328+ contributions (typically 0.5).
309329
310330 Returns
311331 -------
312332 weighted_torque : np.ndarray
313- The mass-weighted sum of the torques in the bead.
314- """
333+ Moment-of-inertia weighted torque acting on the bead.
315334
335+ Raises
336+ ------
337+ ValueError
338+ If a negative principal moment of inertia is encountered.
339+ """
316340 torques = np .zeros ((3 ,))
317341 weighted_torque = np .zeros ((3 ,))
318342 moment_of_inertia = np .zeros (3 )
319343
320344 for atom in bead .atoms :
321- # update local coordinates in rotational axes
322345 coords_rot = (
323346 data_container .atoms [atom .index ].position - bead .center_of_mass ()
324347 )
325348 coords_rot = np .matmul (rot_axes , coords_rot )
326- # update local forces in rotational frame
327349 forces_rot = np .matmul (rot_axes , data_container .atoms [atom .index ].force )
328350
329- # multiply by the force_partitioning parameter to avoid double counting
330- # of the forces on weakly correlated atoms
331- # the default value of force_partitioning is 0.5 (dividing by two)
332351 forces_rot = force_partitioning * forces_rot
333352
334- # define torques (cross product of coordinates and forces) in rotational
335- # axes
336353 torques_local = np .cross (coords_rot , forces_rot )
337354 torques += torques_local
338355
339- # multiply by the force_partitioning parameter to avoid double counting
340- # of the forces on weakly correlated atoms
341- # the default value of force_partitioning is 0.5 (dividing by two)
342- # torques = force_partitioning * torques
343-
344- # divide by moment of inertia to get weighted torques
345- # moment of inertia is a 3x3 tensor
346- # the weighting is done in each dimension (x,y,z) using
347- # the sorted eigenvalues of the moment of inertia tensor
348356 eigenvalues , _ = np .linalg .eig (bead .moment_of_inertia ())
349357 moments_of_inertia = sorted (eigenvalues , reverse = True )
350358
351359 for dimension in range (3 ):
352- # Skip calculation if torque is already zero
353360 if np .isclose (torques [dimension ], 0 ):
354361 weighted_torque [dimension ] = 0
355362 continue
356363
357- # Check for zero moment of inertia
358364 if np .isclose (moments_of_inertia [dimension ], 0 ):
359- # If moment of inertia is 0 there should be 0 torque
360365 weighted_torque [dimension ] = 0
361366 logger .warning ("Zero moment of inertia. Setting torque to 0" )
362367 continue
363368
364- # Check for negative moment of inertia
365369 if moments_of_inertia [dimension ] < 0 :
366370 raise ValueError (
367371 f"Negative value encountered for moment of inertia: "
368372 f"{ moment_of_inertia [dimension ]} "
369373 f"Cannot compute weighted torque."
370374 )
371375
372- # Compute weighted torque
373376 weighted_torque [dimension ] = torques [dimension ] / np .sqrt (
374377 moments_of_inertia [dimension ]
375378 )
0 commit comments