@@ -77,15 +77,17 @@ def execute(self):
7777 self ._handle_water_entropy (start , end , step )
7878 reduced_atom , number_molecules , levels , groups = self ._initialize_molecules ()
7979
80- force_matrices , torque_matrices = self ._level_manager .build_covariance_matrices (
81- self ,
82- reduced_atom ,
83- levels ,
84- groups ,
85- start ,
86- end ,
87- step ,
88- number_frames ,
80+ force_matrices , torque_matrices , frame_counts = (
81+ self ._level_manager .build_covariance_matrices (
82+ self ,
83+ reduced_atom ,
84+ levels ,
85+ groups ,
86+ start ,
87+ end ,
88+ step ,
89+ number_frames ,
90+ )
8991 )
9092
9193 states_ua , states_res = self ._level_manager .build_conformational_states (
@@ -109,6 +111,7 @@ def execute(self):
109111 torque_matrices ,
110112 states_ua ,
111113 states_res ,
114+ frame_counts ,
112115 number_frames ,
113116 ve ,
114117 ce ,
@@ -171,6 +174,7 @@ def _compute_entropies(
171174 torque_matrices ,
172175 states_ua ,
173176 states_res ,
177+ frame_counts ,
174178 number_frames ,
175179 ve ,
176180 ce ,
@@ -195,6 +199,7 @@ def _compute_entropies(
195199 torque_matrices (dict): Precomputed torque covariance matrices.
196200 states_ua (dict): Dictionary to store united-atom conformational states.
197201 states_res (list): List to store residue-level conformational states.
202+ frames_count (dict): Dictionary to store the frame counts
198203 number_frames (int): Total number of trajectory frames to process.
199204 """
200205 with Progress (
@@ -239,6 +244,7 @@ def _compute_entropies(
239244 force_matrices ["ua" ],
240245 torque_matrices ["ua" ],
241246 states_ua ,
247+ frame_counts ["ua" ],
242248 highest ,
243249 number_frames ,
244250 )
@@ -300,16 +306,6 @@ def _get_number_frames(self, start, end, step):
300306 Returns:
301307 int: Total number of frames considered.
302308 """
303- trajectory_length = len (self ._universe .trajectory )
304-
305- if start == 0 and end == - 1 and step == 1 :
306- return trajectory_length
307-
308- if end == - 1 :
309- end = trajectory_length
310- else :
311- end += 1
312-
313309 return math .floor ((end - start ) / step )
314310
315311 def _get_reduced_universe (self ):
@@ -353,6 +349,7 @@ def _process_united_atom_entropy(
353349 force_matrix ,
354350 torque_matrix ,
355351 states ,
352+ frame_counts ,
356353 highest ,
357354 number_frames ,
358355 ):
@@ -368,6 +365,7 @@ def _process_united_atom_entropy(
368365 level (str): Granularity level (should be 'united_atom').
369366 start, end, step (int): Trajectory frame parameters.
370367 n_frames (int): Number of trajectory frames.
368+ frame_counts: Number of frames counted
371369 highest (bool): Whether this is the highest level of resolution for
372370 the molecule.
373371 """
@@ -407,21 +405,43 @@ def _process_united_atom_entropy(
407405 S_conf += S_conf_res
408406
409407 self ._data_logger .add_residue_data (
410- residue_id , residue .resname , level , "Transvibrational" , S_trans_res
408+ residue_id ,
409+ residue .resname ,
410+ level ,
411+ "Transvibrational" ,
412+ frame_counts [key ],
413+ S_trans_res ,
411414 )
412415 self ._data_logger .add_residue_data (
413- residue_id , residue .resname , level , "Rovibrational" , S_rot_res
416+ residue_id ,
417+ residue .resname ,
418+ level ,
419+ "Rovibrational" ,
420+ frame_counts [key ],
421+ S_rot_res ,
414422 )
415423 self ._data_logger .add_residue_data (
416- residue_id , residue .resname , level , "Conformational" , S_conf_res
424+ residue_id ,
425+ residue .resname ,
426+ level ,
427+ "Conformational" ,
428+ frame_counts [key ],
429+ S_conf_res ,
417430 )
418431
419432 self ._data_logger .add_results_data (group_id , level , "Transvibrational" , S_trans )
420433 self ._data_logger .add_results_data (group_id , level , "Rovibrational" , S_rot )
421434 self ._data_logger .add_results_data (group_id , level , "Conformational" , S_conf )
422435
423436 def _process_vibrational_entropy (
424- self , group_id , number_frames , ve , level , force_matrix , torque_matrix , highest
437+ self ,
438+ group_id ,
439+ number_frames ,
440+ ve ,
441+ level ,
442+ force_matrix ,
443+ torque_matrix ,
444+ highest ,
425445 ):
426446 """
427447 Calculates vibrational entropy.
@@ -433,6 +453,7 @@ def _process_vibrational_entropy(
433453 level (str): Current granularity level.
434454 force_matrix : Force covariance matrix
435455 torque_matrix : Torque covariance matrix
456+ frame_count:
436457 highest (bool): Flag indicating if this is the highest granularity
437458 level.
438459 """
@@ -486,28 +507,44 @@ def _process_conformational_entropy(
486507
487508 def _finalize_molecule_results (self ):
488509 """
489- Aggregates and logs total entropy per molecule using residue_data grouped by
490- resid.
510+ Aggregates and logs total entropy and frame counts per molecule.
491511 """
492512 entropy_by_molecule = defaultdict (float )
493-
494- for mol_id , level , entropy_type , result in self ._data_logger .molecule_data :
513+ for (
514+ mol_id ,
515+ level ,
516+ entropy_type ,
517+ result ,
518+ ) in self ._data_logger .molecule_data :
495519 if level != "Molecule Total" :
496520 try :
497521 entropy_by_molecule [mol_id ] += float (result )
498522 except ValueError :
499523 logger .warning (f"Skipping invalid entry: { mol_id } , { result } " )
500524
501- for mol_id , total_entropy in entropy_by_molecule .items ():
525+ # Write totals into molecule_data
526+ for mol_id in entropy_by_molecule .keys ():
527+ total_entropy = entropy_by_molecule [mol_id ]
528+
502529 self ._data_logger .molecule_data .append (
503- (mol_id , "Molecule Total" , "Molecule Total Entropy" , total_entropy )
530+ (
531+ mol_id ,
532+ "Molecule Total" ,
533+ "Molecule Total Entropy" ,
534+ total_entropy ,
535+ )
504536 )
505537
506538 # Save to file
507539 self ._data_logger .save_dataframes_as_json (
508540 pd .DataFrame (
509541 self ._data_logger .molecule_data ,
510- columns = ["Molecule ID" , "Level" , "Type" , "Result (J/mol/K)" ],
542+ columns = [
543+ "Group ID" ,
544+ "Level" ,
545+ "Type" ,
546+ "Result (J/mol/K)" ,
547+ ],
511548 ),
512549 pd .DataFrame (
513550 self ._data_logger .residue_data ,
@@ -516,6 +553,7 @@ def _finalize_molecule_results(self):
516553 "Residue Name" ,
517554 "Level" ,
518555 "Type" ,
556+ "Frame Count" ,
519557 "Result (J/mol/K)" ,
520558 ],
521559 ),
@@ -532,24 +570,24 @@ def _calculate_water_entropy(self, universe, start, end, step):
532570 end (int): End frame.
533571 step (int): Step size.
534572 """
535- Sorient_dict , _ , vibrations , _ , _ = (
573+ Sorient_dict , _ , vibrations , _ , water_count = (
536574 GetSolvent .get_interfacial_water_orient_entropy (
537575 universe , start , end , step , self ._args .temperature , parallel = True
538576 )
539577 )
540578
541579 # Log per-residue entropy using helper functions
542- self ._calculate_water_orientational_entropy (Sorient_dict )
543- self ._calculate_water_vibrational_translational_entropy (vibrations )
544- self ._calculate_water_vibrational_rotational_entropy (vibrations )
580+ self ._calculate_water_orientational_entropy (Sorient_dict , water_count )
581+ self ._calculate_water_vibrational_translational_entropy (vibrations , water_count )
582+ self ._calculate_water_vibrational_rotational_entropy (vibrations , water_count )
545583
546584 # Aggregate entropy components per molecule
547585 results = {}
548586
549587 for row in self ._data_logger .residue_data :
550588 mol_id = row [1 ]
551589 entropy_type = row [3 ].split ()[0 ]
552- value = float (row [4 ])
590+ value = float (row [5 ])
553591
554592 if mol_id not in results :
555593 results [mol_id ] = {
@@ -570,7 +608,7 @@ def _calculate_water_entropy(self, universe, start, end, step):
570608 )
571609 total += S_component
572610
573- def _calculate_water_orientational_entropy (self , Sorient_dict ):
611+ def _calculate_water_orientational_entropy (self , Sorient_dict , water_count ):
574612 """
575613 Logs orientational entropy values directly from Sorient_dict.
576614 """
@@ -579,10 +617,12 @@ def _calculate_water_orientational_entropy(self, Sorient_dict):
579617 if isinstance (values , list ) and len (values ) == 2 :
580618 Sor , count = values
581619 self ._data_logger .add_residue_data (
582- resid , resname , "Water" , "Orientational" , Sor
620+ resid , resname , "Water" , "Orientational" , water_count , Sor
583621 )
584622
585- def _calculate_water_vibrational_translational_entropy (self , vibrations ):
623+ def _calculate_water_vibrational_translational_entropy (
624+ self , vibrations , water_count
625+ ):
586626 """
587627 Logs summed translational entropy values per residue-solvent pair.
588628 """
@@ -601,10 +641,10 @@ def _calculate_water_vibrational_translational_entropy(self, vibrations):
601641 resid = - 1
602642
603643 self ._data_logger .add_residue_data (
604- resid , resname , "Water" , "Transvibrational" , entropy
644+ resid , resname , "Water" , "Transvibrational" , water_count , entropy
605645 )
606646
607- def _calculate_water_vibrational_rotational_entropy (self , vibrations ):
647+ def _calculate_water_vibrational_rotational_entropy (self , vibrations , water_count ):
608648 """
609649 Logs summed rotational entropy values per residue-solvent pair.
610650 """
@@ -623,7 +663,7 @@ def _calculate_water_vibrational_rotational_entropy(self, vibrations):
623663 resid = - 1
624664
625665 self ._data_logger .add_residue_data (
626- resid , resname , "Water" , "Rovibrational" , entropy
666+ resid , resname , "Water" , "Rovibrational" , water_count , entropy
627667 )
628668
629669
@@ -813,7 +853,7 @@ def assign_conformation(
813853 for timestep_index , _ in zip (
814854 indices , data_container .trajectory [start :end :step ]
815855 ):
816- timestep_index = timestep_index - start
856+ timestep_index = timestep_index
817857 value = dihedral .value ()
818858 # we want postive values in range 0 to 360 to make the peak assignment
819859 # work using the fact that dihedrals have circular symetry
0 commit comments