2121
2222@dataclass (frozen = True )
2323class EntropyPair :
24- """Container for paired translational and rotational entropy values."""
24+ """Container for paired translational and rotational entropy values.
25+
26+ Attributes:
27+ trans: Translational vibrational entropy value.
28+ rot: Rotational vibrational entropy value.
29+ """
2530
2631 trans : float
2732 rot : float
2833
2934
3035class VibrationalEntropyNode :
31- """Compute vibrational entropy from force/torque (and optional FT) covariances."""
36+ """Compute vibrational entropy from force/torque (and optional FT) covariances.
37+
38+ This node reads covariance matrices from a shared data mapping, computes
39+ translational and rotational vibrational entropy at requested hierarchy levels,
40+ and stores results back into the shared data structure.
41+
42+ The node supports:
43+ - Force and torque covariance matrices ("force" / "torque") at residue/polymer
44+ levels.
45+ - United-atom per-residue covariances keyed by (group_id, residue_id).
46+ - Optional combined force-torque covariance matrices ("forcetorque") for the
47+ highest level when enabled via args.combined_forcetorque.
48+ """
3249
3350 def __init__ (self ) -> None :
51+ """Initialize the node with matrix utilities and numerical tolerances."""
3452 self ._mat_ops = MatrixUtils ()
3553 self ._zero_atol = 1e-8
3654
3755 def run (self , shared_data : MutableMapping [str , Any ], ** _ : Any ) -> Dict [str , Any ]:
56+ """Run vibrational entropy calculations and update the shared data mapping.
57+
58+ Args:
59+ shared_data: Mutable mapping containing inputs (covariances, groups,
60+ levels, args, etc.) and where outputs will be written.
61+ **_: Unused keyword arguments, accepted for framework compatibility.
62+
63+ Returns:
64+ A dict containing the computed vibrational entropy results under the
65+ key "vibrational_entropy".
66+
67+ Raises:
68+ ValueError: If an unknown level is encountered in the level list for a
69+ representative molecule.
70+ """
3871 ve = self ._build_entropy_engine (shared_data )
3972 temp = shared_data ["args" ].temperature
4073
@@ -125,18 +158,48 @@ def run(self, shared_data: MutableMapping[str, Any], **_: Any) -> Dict[str, Any]
125158 def _build_entropy_engine (
126159 self , shared_data : Mapping [str , Any ]
127160 ) -> VibrationalEntropy :
161+ """Construct the vibrational entropy engine used for calculations.
162+
163+ Args:
164+ shared_data: Read-only mapping containing a "run_manager" entry.
165+
166+ Returns:
167+ A configured VibrationalEntropy instance.
168+ """
128169 return VibrationalEntropy (
129170 run_manager = shared_data ["run_manager" ],
130171 )
131172
132173 def _get_group_id_to_index (self , shared_data : Mapping [str , Any ]) -> Dict [int , int ]:
174+ """Return a mapping from group_id to contiguous index used by covariance lists.
175+
176+ If a precomputed mapping is provided under "group_id_to_index", it is used.
177+ Otherwise, the mapping is derived from the insertion order of "groups".
178+
179+ Args:
180+ shared_data: Read-only mapping containing "groups" and optionally
181+ "group_id_to_index".
182+
183+ Returns:
184+ Dictionary mapping each group_id to an integer index.
185+ """
133186 gid2i = shared_data .get ("group_id_to_index" )
134187 if isinstance (gid2i , dict ) and gid2i :
135188 return gid2i
136189 groups = shared_data ["groups" ]
137190 return {gid : i for i , gid in enumerate (groups .keys ())}
138191
139192 def _get_ua_frame_counts (self , shared_data : Mapping [str , Any ]) -> Dict [CovKey , int ]:
193+ """Extract per-(group,residue) frame counts for united-atom covariances.
194+
195+ Args:
196+ shared_data: Read-only mapping which may contain nested frame count data
197+ under shared_data["frame_counts"]["ua"].
198+
199+ Returns:
200+ A dict keyed by (group_id, residue_id) containing frame counts. Returns
201+ an empty dict if not present or not well-formed.
202+ """
140203 counts = shared_data .get ("frame_counts" , {})
141204 if isinstance (counts , dict ):
142205 ua_counts = counts .get ("ua" , {})
@@ -158,6 +221,27 @@ def _compute_united_atom_entropy(
158221 n_frames_default : int ,
159222 highest : bool ,
160223 ) -> EntropyPair :
224+ """Compute total united-atom vibrational entropy for a group's residues.
225+
226+ Iterates over residues, looks up per-residue force and torque covariance
227+ matrices keyed by (group_id, residue_index), computes entropy contributions,
228+ accumulates totals, and optionally reports per-residue values.
229+
230+ Args:
231+ ve: VibrationalEntropy calculation engine.
232+ temp: Temperature (K) for entropy calculation.
233+ group_id: Identifier for the group being processed.
234+ residues: Residue container/sequence for the representative molecule.
235+ force_ua: Mapping from (group_id, residue_id) to force covariance matrix.
236+ torque_ua: Mapping from (group_id, residue_id) to torque covariance matrix.
237+ ua_frame_counts: Mapping from (group_id, residue_id) to frame counts.
238+ reporter: Optional reporter object supporting add_residue_data calls.
239+ n_frames_default: Fallback frame count if per-residue count missing.
240+ highest: Whether this computation is at the highest requested level.
241+
242+ Returns:
243+ EntropyPair with summed translational and rotational entropy across residues
244+ """
161245 s_trans_total = 0.0
162246 s_rot_total = 0.0
163247
@@ -207,6 +291,22 @@ def _compute_force_torque_entropy(
207291 tmat : Any ,
208292 highest : bool ,
209293 ) -> EntropyPair :
294+ """Compute vibrational entropy from separate force and torque covariances.
295+
296+ Matrices are filtered to remove (near-)zero rows/columns before computation.
297+ If either matrix is missing or becomes empty after filtering, returns zeros.
298+
299+ Args:
300+ ve: VibrationalEntropy calculation engine.
301+ temp: Temperature (K) for entropy calculation.
302+ fmat: Force covariance matrix (array-like) or None.
303+ tmat: Torque covariance matrix (array-like) or None.
304+ highest: Whether this computation is at the highest requested level.
305+
306+ Returns:
307+ EntropyPair containing translational entropy (from force covariance) and
308+ rotational entropy (from torque covariance).
309+ """
210310 if fmat is None or tmat is None :
211311 return EntropyPair (trans = 0.0 , rot = 0.0 )
212312
@@ -235,6 +335,20 @@ def _compute_ft_entropy(
235335 temp : float ,
236336 ftmat : Any ,
237337 ) -> EntropyPair :
338+ """Compute vibrational entropy from a combined force-torque covariance matrix.
339+
340+ The combined covariance matrix is filtered to remove (near-)zero rows/columns
341+ before computation. If missing or empty after filtering, returns zeros.
342+
343+ Args:
344+ ve: VibrationalEntropy calculation engine.
345+ temp: Temperature (K) for entropy calculation.
346+ ftmat: Combined force-torque covariance matrix (array-like) or None.
347+
348+ Returns:
349+ EntropyPair containing translational and rotational entropy values derived
350+ from the combined covariance matrix.
351+ """
238352 if ftmat is None :
239353 return EntropyPair (trans = 0.0 , rot = 0.0 )
240354
@@ -259,6 +373,14 @@ def _store_results(
259373 level : str ,
260374 pair : EntropyPair ,
261375 ) -> None :
376+ """Store entropy results for a group/level into the results structure.
377+
378+ Args:
379+ results: Nested results dict indexed by group_id then level.
380+ group_id: Group identifier to store under.
381+ level: Hierarchy level name (e.g., "united_atom", "residue", "polymer").
382+ pair: EntropyPair containing translational and rotational values.
383+ """
262384 results [group_id ][level ] = {"trans" : pair .trans , "rot" : pair .rot }
263385
264386 @staticmethod
@@ -270,6 +392,15 @@ def _log_molecule_level_results(
270392 * ,
271393 use_ft_labels : bool ,
272394 ) -> None :
395+ """Log molecule-level entropy results to the reporter, if available.
396+
397+ Args:
398+ reporter: Optional reporter object supporting add_results_data calls.
399+ group_id: Group identifier being reported.
400+ level: Hierarchy level name being reported.
401+ pair: EntropyPair containing translational and rotational values.
402+ use_ft_labels: Whether to use FT-specific labels for the entropy types.
403+ """
273404 if reporter is None :
274405 return
275406
@@ -285,6 +416,15 @@ def _log_molecule_level_results(
285416
286417 @staticmethod
287418 def _get_indexed_matrix (mats : Any , index : int ) -> Any :
419+ """Safely retrieve mats[index] if mats is indexable and index is in range.
420+
421+ Args:
422+ mats: Indexable container of matrices (e.g., list/tuple) or other object.
423+ index: Desired index.
424+
425+ Returns:
426+ The matrix at the given index if available; otherwise None.
427+ """
288428 try :
289429 return mats [index ] if index < len (mats ) else None
290430 except TypeError :
0 commit comments