Skip to content

Commit d4cf3e8

Browse files
committed
Refactor water entropy calculations and update tests:
- Updated `_calculate_water_entropy` and related methods to properly aggregate orientational, translational, and rotational entropy - Incorporated actual water counts from covariances for accurate logging - Revised unit tests to reflect updated method signatures and behaviors, ensuring `add_residue_data` and `add_group_label` calls are correctly tested - Stop adding aggregated water entropy results to the Total Group table, as this is incorrect
1 parent ad77383 commit d4cf3e8

2 files changed

Lines changed: 191 additions & 179 deletions

File tree

CodeEntropy/entropy.py

Lines changed: 92 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
TimeElapsedColumn,
1515
)
1616

17+
from CodeEntropy.config.logging_config import LoggingConfig
18+
1719
logger = logging.getLogger(__name__)
20+
console = LoggingConfig.get_console()
1821

1922

2023
class EntropyManager:
@@ -58,6 +61,10 @@ def execute(self):
5861
start, end, step = self._get_trajectory_bounds()
5962
number_frames = self._get_number_frames(start, end, step)
6063

64+
console.print(
65+
f"Analyzing a total of {number_frames} frames in this calculation."
66+
)
67+
6168
ve = VibrationalEntropy(
6269
self._run_manager,
6370
self._args,
@@ -572,7 +579,7 @@ def _finalize_molecule_results(self):
572579
entropy_type,
573580
result,
574581
) in self._data_logger.molecule_data:
575-
if level != "Molecule Total":
582+
if level != "Group Total":
576583
try:
577584
entropy_by_molecule[mol_id] += float(result)
578585
except ValueError:
@@ -584,8 +591,8 @@ def _finalize_molecule_results(self):
584591
self._data_logger.molecule_data.append(
585592
(
586593
mol_id,
587-
"Molecule Total",
588-
"Molecule Total Entropy",
594+
"Group Total",
595+
"Group Total Entropy",
589596
total_entropy,
590597
)
591598
)
@@ -616,103 +623,126 @@ def _finalize_molecule_results(self):
616623

617624
def _calculate_water_entropy(self, universe, start, end, step, group_id=None):
618625
"""
619-
Calculate water entropy and map all waters to a single group ID.
620-
Aggregates entropy components per water group.
626+
Calculate and aggregate the entropy of water molecules in a simulation.
627+
628+
This function computes orientational, translational, and rotational
629+
entropy components for all water molecules, aggregates them per residue,
630+
and maps all waters to a single group ID. It also logs the total results
631+
and labels the water group in the data logger.
632+
633+
Parameters
634+
----------
635+
universe : MDAnalysis.Universe
636+
The simulation universe containing water molecules.
637+
start : int
638+
The starting frame for analysis.
639+
end : int
640+
The ending frame for analysis.
641+
step : int
642+
Frame interval for analysis.
643+
group_id : int or str, optional
644+
The group ID to which all water molecules will be assigned.
621645
"""
622-
Sorient_dict, _, vibrations, _, water_count = (
646+
Sorient_dict, covariances, vibrations, _, water_count = (
623647
GetSolvent.get_interfacial_water_orient_entropy(
624648
universe, start, end, step, self._args.temperature, parallel=True
625649
)
626650
)
627651

628-
self._calculate_water_orientational_entropy(Sorient_dict, group_id, water_count)
652+
self._calculate_water_orientational_entropy(Sorient_dict, group_id)
629653
self._calculate_water_vibrational_translational_entropy(
630-
vibrations, group_id, water_count
654+
vibrations, group_id, covariances
631655
)
632656
self._calculate_water_vibrational_rotational_entropy(
633-
vibrations, group_id, water_count
657+
vibrations, group_id, covariances
634658
)
635659

636-
results = {}
637-
for row in self._data_logger.residue_data:
638-
mol_id = row[1]
639-
entropy_type = row[3].split()[0]
640-
value = float(row[5])
641-
642-
if mol_id not in results:
643-
results[mol_id] = {
644-
"Orientational": 0.0,
645-
"Transvibrational": 0.0,
646-
"Rovibrational": 0.0,
647-
}
648-
results[mol_id][entropy_type] += value
649-
650-
for mol_id, components in results.items():
651-
for entropy_type in ["Orientational", "Transvibrational", "Rovibrational"]:
652-
S_component = components[entropy_type]
653-
self._data_logger.add_results_data(
654-
group_id, "water", entropy_type, S_component
655-
)
656-
657660
water_selection = universe.select_atoms("resname WAT")
658661
actual_water_residues = len(water_selection.residues)
659-
660-
residue_names = set()
661-
for res_dict in Sorient_dict.values():
662-
for resname in res_dict.keys():
663-
if resname.upper() in water_selection.residues.resnames:
664-
residue_names.add(resname)
662+
residue_names = {
663+
resname
664+
for res_dict in Sorient_dict.values()
665+
for resname in res_dict.keys()
666+
if resname.upper() in water_selection.residues.resnames
667+
}
665668

666669
residue_group = "_".join(sorted(residue_names)) if residue_names else "WAT"
667-
residue_count = actual_water_residues
668-
atom_count = len(water_selection.atoms)
669-
670670
self._data_logger.add_group_label(
671-
group_id, residue_group, residue_count, atom_count
671+
group_id, residue_group, actual_water_residues, len(water_selection.atoms)
672672
)
673673

674-
def _calculate_water_orientational_entropy(
675-
self, Sorient_dict, group_id, water_count
676-
):
674+
def _calculate_water_orientational_entropy(self, Sorient_dict, group_id):
677675
"""
678-
Aggregate all orientational entropy for waters into a single group.
676+
Aggregate orientational entropy for all water molecules into a single group.
677+
678+
Parameters
679+
----------
680+
Sorient_dict : dict
681+
Dictionary containing orientational entropy values per residue.
682+
group_id : int or str
683+
The group ID to which the water residues belong.
684+
covariances : object
685+
Covariance object.
679686
"""
680-
total_S = 0.0
681687
for resid, resname_dict in Sorient_dict.items():
682688
for resname, values in resname_dict.items():
683689
if isinstance(values, list) and len(values) == 2:
684690
Sor, count = values
685-
total_S += Sor
686-
687-
self._data_logger.add_residue_data(
688-
group_id, "WAT", "Water", "Orientational", water_count, total_S
689-
)
691+
self._data_logger.add_residue_data(
692+
group_id, resname, "Water", "Orientational", count, Sor
693+
)
690694

691695
def _calculate_water_vibrational_translational_entropy(
692-
self, vibrations, group_id, water_count
696+
self, vibrations, group_id, covariances
693697
):
694-
total_S = 0.0
698+
"""
699+
Aggregate translational vibrational entropy for all water molecules.
700+
701+
Parameters
702+
----------
703+
vibrations : object
704+
Object containing translational entropy data (vibrations.translational_S).
705+
group_id : int or str
706+
The group ID for the water residues.
707+
covariances : object
708+
Covariance object.
709+
"""
710+
695711
for (solute_id, _), entropy in vibrations.translational_S.items():
696712
if isinstance(entropy, (list, np.ndarray)):
697713
entropy = float(np.sum(entropy))
698-
total_S += entropy
699714

700-
self._data_logger.add_residue_data(
701-
group_id, "WAT", "Water", "Transvibrational", water_count, total_S
702-
)
715+
count = covariances.counts.get((solute_id, "WAT"), 1)
716+
resname = solute_id.rsplit("_", 1)[0] if "_" in solute_id else solute_id
717+
self._data_logger.add_residue_data(
718+
group_id, resname, "Water", "Transvibrational", count, entropy
719+
)
703720

704721
def _calculate_water_vibrational_rotational_entropy(
705-
self, vibrations, group_id, water_count
722+
self, vibrations, group_id, covariances
706723
):
707-
total_S = 0.0
724+
"""
725+
Aggregate rotational vibrational entropy for all water molecules.
726+
727+
Parameters
728+
----------
729+
vibrations : object
730+
Object containing rotational entropy data (vibrations.rotational_S).
731+
group_id : int or str
732+
The group ID for the water residues.
733+
covariances : object
734+
Covariance object.
735+
"""
708736
for (solute_id, _), entropy in vibrations.rotational_S.items():
709737
if isinstance(entropy, (list, np.ndarray)):
710738
entropy = float(np.sum(entropy))
711-
total_S += entropy
712739

713-
self._data_logger.add_residue_data(
714-
group_id, "WAT", "Water", "Rovibrational", water_count, total_S
715-
)
740+
count = covariances.counts.get((solute_id, "WAT"), 1)
741+
742+
resname = solute_id.rsplit("_", 1)[0] if "_" in solute_id else solute_id
743+
self._data_logger.add_residue_data(
744+
group_id, resname, "Water", "Rovibrational", count, entropy
745+
)
716746

717747

718748
class VibrationalEntropy(EntropyManager):

0 commit comments

Comments
 (0)