Skip to content

Commit fff6315

Browse files
committed
docs: tidy comments and standardise to Google-style docstrings
1 parent 25e1cb5 commit fff6315

File tree

14 files changed

+695
-162
lines changed

14 files changed

+695
-162
lines changed

CodeEntropy/config/runtime.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,6 @@ def read_universe(self, path: str) -> mda.Universe:
383383
def change_lambda_units(self, arg_lambdas: Any) -> Any:
384384
"""Unit of lambdas : kJ2 mol-2 A-2 amu-1
385385
change units of lambda to J/s2"""
386-
# return arg_lambdas * N_AVOGADRO * N_AVOGADRO * AMU2KG * 1e-26
387386
return arg_lambdas * 1e29 / self.N_AVOGADRO
388387

389388
def get_KT2J(self, arg_temper: float) -> float:

CodeEntropy/entropy/configurational.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def assign_conformation(
9696
Raises:
9797
ValueError: If `bin_width` or `step` are invalid.
9898
"""
99-
_ = number_frames # kept for compatibility; sizing follows the slice length.
99+
_ = number_frames
100100

101101
config = ConformationConfig(
102102
bin_width=int(bin_width),
@@ -115,7 +115,6 @@ def assign_conformation(
115115
peak_values = self._find_histogram_peaks(phi, config.bin_width)
116116

117117
if peak_values.size == 0:
118-
# No peaks means no distinguishable states; assign everything to 0.
119118
return np.zeros(n_slice, dtype=int)
120119

121120
states = self._assign_nearest_peaks(phi, peak_values)
@@ -138,21 +137,18 @@ def conformational_entropy_calculation(
138137
Returns:
139138
Conformational entropy in J/mol/K.
140139
"""
141-
_ = number_frames # accepted as metadata; distribution uses observed counts.
140+
_ = number_frames
142141

143142
arr = self._to_1d_array(states)
144143
if arr is None or arr.size == 0:
145144
return 0.0
146145

147-
# If states contain only falsy values (e.g., all zeros) this is still valid:
148-
# entropy would be 0 because only one state is present.
149146
values, counts = np.unique(arr, return_counts=True)
150147
total_count = int(np.sum(counts))
151148
if total_count <= 0 or values.size <= 1:
152149
return 0.0
153150

154151
probs = counts.astype(float) / float(total_count)
155-
# Guard against log(0) (shouldn't happen because counts>0), but keep robust.
156152
probs = probs[probs > 0.0]
157153

158154
s_conf = -self._GAS_CONST * float(np.sum(probs * np.log(probs)))
@@ -174,7 +170,6 @@ def _validate_assignment_config(config: ConformationConfig) -> None:
174170
if config.bin_width <= 0 or config.bin_width > 360:
175171
raise ValueError("bin_width must be in the range (0, 360]")
176172
if 360 % config.bin_width != 0:
177-
# Not strictly required, but prevents uneven bins and edge-case confusion.
178173
logger.warning(
179174
"bin_width=%s does not evenly divide 360; histogram bins will be "
180175
"uneven.",
@@ -242,8 +237,6 @@ def _assign_nearest_peaks(phi: np.ndarray, peak_values: np.ndarray) -> np.ndarra
242237
Returns:
243238
Integer state labels aligned with `phi`.
244239
"""
245-
# Vectorized nearest-peak assignment
246-
# shape: (n_frames, n_peaks)
247240
distances = np.abs(phi[:, None] - peak_values[None, :])
248241
return np.argmin(distances, axis=1).astype(int)
249242

CodeEntropy/entropy/nodes/vibrational.py

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,53 @@
2121

2222
@dataclass(frozen=True)
2323
class 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

3035
class 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:

CodeEntropy/entropy/orientational.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ def calculate(self, neighbours: Mapping[str, int]) -> OrientationalEntropyResult
9595
total = 0.0
9696
for species, count in neighbours.items():
9797
if self._is_water(species):
98-
# Water handling can be added later (e.g., via a strategy).
9998
logger.debug(
10099
"Skipping water species %s in orientational entropy.", species
101100
)
@@ -141,7 +140,6 @@ def _entropy_contribution(self, neighbour_count: int) -> float:
141140
return 0.0
142141

143142
omega = self._omega(neighbour_count)
144-
# omega should always be > 0 when neighbour_count > 0, but guard anyway.
145143
if omega <= 0.0:
146144
return 0.0
147145

CodeEntropy/entropy/vibrational.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,6 @@ def _entropy_components_from_frequencies(
200200
kT = float(self._run_manager.get_KT2J(temp))
201201
exponent = (self._planck_const * frequencies) / kT
202202

203-
# Numerically stable enough for typical ranges; callers filter eigenvalues.
204203
exp_pos = np.exp(exponent)
205204
exp_neg = np.exp(-exponent)
206205

CodeEntropy/entropy/workflow.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ def execute(self) -> None:
114114
if self._args.water_entropy and water_groups:
115115
self._compute_water_entropy(traj, water_groups)
116116
else:
117-
# If water entropy isn't computed, include water in the remaining groups.
118117
nonwater_groups.update(water_groups)
119118

120119
shared_data = self._build_shared_data(
@@ -297,8 +296,6 @@ def _compute_water_entropy(
297296
water_entropy = WaterEntropy(self._args)
298297

299298
for group_id in water_groups.keys():
300-
# WaterEntropy currently exposes a concrete API; keep this manager
301-
# as an orchestrator and avoid duplicating internals here.
302299
water_entropy._calculate_water_entropy(
303300
universe=self._universe,
304301
start=traj.start,
@@ -307,7 +304,6 @@ def _compute_water_entropy(
307304
group_id=group_id,
308305
)
309306

310-
# Exclude water from subsequent analysis when water entropy has been computed.
311307
self._args.selection_string = (
312308
f"{self._args.selection_string} and not water"
313309
if self._args.selection_string != "all"

CodeEntropy/levels/dihedrals.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@
1010

1111
import numpy as np
1212
from MDAnalysis.analysis.dihedrals import Dihedral
13-
from rich.progress import (
14-
BarColumn,
15-
Progress,
16-
SpinnerColumn,
17-
TextColumn,
18-
TimeElapsedColumn,
19-
)
2013

2114
logger = logging.getLogger(__name__)
2215

@@ -497,19 +490,3 @@ def _assign_states(
497490

498491
logger.debug("States: %s", states)
499492
return states
500-
501-
@staticmethod
502-
def _count_total_items(levels, groups) -> int:
503-
"""Count total progress items."""
504-
return sum(len(levels[mol_id]) for mols in groups.values() for mol_id in mols)
505-
506-
@staticmethod
507-
def _progress_bar(total_items: int) -> Progress:
508-
"""Create a Rich progress bar."""
509-
return Progress(
510-
SpinnerColumn(),
511-
TextColumn("[bold blue]{task.fields[title]}", justify="right"),
512-
BarColumn(),
513-
TextColumn("[progress.percentage]{task.percentage:>3.1f}%"),
514-
TimeElapsedColumn(),
515-
)

0 commit comments

Comments
 (0)