Skip to content

Commit 15697e0

Browse files
committed
update docstring and comments within get_weighted_forces and get_weighted_torques
1 parent e247c10 commit 15697e0

File tree

1 file changed

+59
-56
lines changed

1 file changed

+59
-56
lines changed

CodeEntropy/levels.py

Lines changed: 59 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)