Skip to content

Commit 266391b

Browse files
committed
Merge branch 'main' into 43-version-1-documentation
Adding the recent developments to the main code
2 parents 427bc56 + 2ecc5e3 commit 266391b

18 files changed

Lines changed: 3750 additions & 320 deletions

.github/workflows/project-ci.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
name: Run tests
1818
steps:
1919
- name: Checkout repo
20-
uses: actions/checkout@v4
20+
uses: actions/checkout@v5
2121

2222
- name: Set up Python ${{ matrix.python-version }}
2323
uses: actions/setup-python@v5
@@ -39,7 +39,7 @@ jobs:
3939
runs-on: ubuntu-latest
4040
timeout-minutes: 15
4141
steps:
42-
- uses: actions/checkout@v4
42+
- uses: actions/checkout@v5
4343
- name: Set up Python 3.12
4444
uses: actions/setup-python@v5
4545
with:
@@ -55,7 +55,7 @@ jobs:
5555
runs-on: ubuntu-latest
5656
timeout-minutes: 15
5757
steps:
58-
- uses: actions/checkout@v4
58+
- uses: actions/checkout@v5
5959
- name: Set up Python 3.11
6060
uses: actions/setup-python@v5
6161
with:
@@ -76,11 +76,11 @@ jobs:
7676
strategy:
7777
matrix:
7878
python-version: ["3.11", "3.12", "3.13"]
79-
mdanalysis-version: ["2.7.0", "2.8.0", "2.9.0", "latest"]
79+
mdanalysis-version: ["2.9.0", "latest"]
8080
name: MDAnalysis Compatibility Tests
8181
steps:
8282
- name: Checkout repo
83-
uses: actions/checkout@v4
83+
uses: actions/checkout@v5
8484

8585
- name: Set up Python ${{ matrix.python-version }}
8686
uses: actions/setup-python@v5
@@ -110,6 +110,6 @@ jobs:
110110
RUN_NUMBER: ${{ github.run_number }}
111111
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
112112
with:
113-
filename: .github/mdanalysis-compatibility-failure.md
113+
filename: .github/workflows/mdanalysis-compatibility-failure.md
114114
update_existing: true
115115
search_existing: open

.github/workflows/publish-on-pypi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
name: Publish to pypi if tagged
1212
runs-on: ubuntu-latest
1313
steps:
14-
- uses: actions/checkout@v4
14+
- uses: actions/checkout@v5
1515
- uses: actions/setup-python@v5
1616

1717
- uses: casperdcl/deploy-pypi@v2

CITATION.cff

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# This CITATION.cff file was generated with cffinit.
2+
# Visit https://bit.ly/cffinit to generate yours today!
3+
4+
cff-version: 1.2.0
5+
title: CodeEntropy
6+
message: >-
7+
If you use this software, please cite it using the
8+
metadata from this file.
9+
type: software
10+
authors:
11+
- given-names: Arghya
12+
family-names: Chakravorty
13+
email: arghyac@umich.edu
14+
orcid: 'https://orcid.org/0000-0002-4467-1135'
15+
affiliation: University of Michigan
16+
- given-names: Donald
17+
family-names: Chung-HK
18+
email: donald.chung@stfc.ac.uk
19+
affiliation: 'STFC, Scientific Computing'
20+
- given-names: Sarah
21+
family-names: Fegan
22+
email: sarah.fegan@stfc.ac.uk
23+
affiliation: 'STFC, Scientific Computing'
24+
orcid: 'https://orcid.org/0009-0007-1030-228X'
25+
- given-names: James
26+
family-names: Gebbie-Rayet
27+
email: james.gebbie@stfc.ac.uk
28+
affiliation: 'STFC, Scientific Computing'
29+
orcid: 'https://orcid.org/0000-0001-8271-3431'
30+
- given-names: Sarah
31+
family-names: Harris
32+
email: sarah.harris@sheffield.ac.uk
33+
affiliation: University of Sheffield
34+
orcid: 'https://orcid.org/0000-0002-2812-1651'
35+
- given-names: Richard
36+
family-names: Henchman
37+
email: rhen7213@uni.sydney.edu.au
38+
affiliation: University of Sydney
39+
orcid: 'https://orcid.org/0000-0002-0461-6625'
40+
- given-names: Jas
41+
family-names: Kalayan
42+
email: jas.kalayan@stfc.ac.uk
43+
affiliation: 'STFC, Scientific Computing'
44+
orcid: 'https://orcid.org/0000-0002-6833-1864'
45+
- given-names: Ioana
46+
family-names: Papa
47+
email: iapapa1@sheffield.ac.uk
48+
affiliation: University of Sheffield
49+
orcid: 'https://orcid.org/0009-0000-2287-4081'
50+
- given-names: Harry
51+
family-names: Swift
52+
email: harry.swift@stfc.ac.uk
53+
affiliation: 'STFC, Scientific Computing'
54+
repository-code: 'https://github.com/CCPBioSim/CodeEntropy'
55+
url: 'https://ccpbiosim.github.io/CodeEntropy/'
56+
abstract: >-
57+
CodeEntropy is a Python package for calculating
58+
configurational entropy from molecular dynamics
59+
simulations using the multiscale cell correlation (MCC)
60+
method. It enables multiscale analysis of atomic
61+
fluctuations to quantify molecular flexibility and
62+
disorder.
63+
keywords:
64+
- entropy
65+
- configurational entropy
66+
- molecular dynamics
67+
- MD simulations
68+
- multiscale cell correlation
69+
- statistical mechanics
70+
- biomolecular simulations
71+
- protein flexibility
72+
license: MIT
73+
version: 0.3.6
74+
date-released: '2022-07-06'

CodeEntropy/config/arg_config_manager.py

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,13 @@
6161
"force_partitioning": {"type": float, "help": "Force partitioning", "default": 0.5},
6262
"water_entropy": {
6363
"type": bool,
64-
"help": "Calculate water entropy",
65-
"default": False,
64+
"help": "If set to False, disables the calculation of water entropy",
65+
"default": True,
66+
},
67+
"grouping": {
68+
"type": str,
69+
"help": "How to group molecules for averaging",
70+
"default": "molecules",
6671
},
6772
}
6873

@@ -85,15 +90,54 @@ def load_config(self, file_path):
8590

8691
return config
8792

93+
def str2bool(self, value):
94+
"""
95+
Convert a string or boolean input into a boolean value.
96+
97+
Accepts common string representations of boolean values such as:
98+
- True values: "true", "t", "yes", "1"
99+
- False values: "false", "f", "no", "0"
100+
101+
If the input is already a boolean, it is returned as-is.
102+
Raises:
103+
argparse.ArgumentTypeError: If the input cannot be interpreted as a boolean.
104+
105+
Args:
106+
value (str or bool): The input value to convert.
107+
108+
Returns:
109+
bool: The corresponding boolean value.
110+
"""
111+
if isinstance(value, bool):
112+
return value
113+
value = value.lower()
114+
if value in {"true", "t", "yes", "1"}:
115+
return True
116+
elif value in {"false", "f", "no", "0"}:
117+
return False
118+
else:
119+
raise argparse.ArgumentTypeError("Boolean value expected (True/False).")
120+
88121
def setup_argparse(self):
89122
"""Setup argument parsing dynamically based on arg_map."""
90123
parser = argparse.ArgumentParser(
91124
description="CodeEntropy: Entropy calculation with MCC method."
92125
)
93126

94127
for arg, properties in self.arg_map.items():
95-
kwargs = {key: properties[key] for key in properties if key != "help"}
96-
parser.add_argument(f"--{arg}", **kwargs, help=properties.get("help"))
128+
help_text = properties.get("help", "")
129+
default = properties.get("default", None)
130+
131+
if properties.get("type") == bool:
132+
parser.add_argument(
133+
f"--{arg}",
134+
type=self.str2bool,
135+
default=default,
136+
help=f"{help_text} (default: {default})",
137+
)
138+
else:
139+
kwargs = {k: v for k, v in properties.items() if k != "help"}
140+
parser.add_argument(f"--{arg}", **kwargs, help=help_text)
97141

98142
return parser
99143

@@ -146,3 +190,62 @@ def merge_configs(self, args, run_config):
146190
handler.setLevel(logging.INFO)
147191

148192
return args
193+
194+
def input_parameters_validation(self, u, args):
195+
"""Check the validity of the user inputs against sensible values"""
196+
197+
self._check_input_start(u, args)
198+
self._check_input_end(u, args)
199+
self._check_input_step(args)
200+
self._check_input_bin_width(args)
201+
self._check_input_temperature(args)
202+
self._check_input_force_partitioning(args)
203+
204+
def _check_input_start(self, u, args):
205+
"""Check that the input does not exceed the length of the trajectory."""
206+
if args.start > len(u.trajectory):
207+
raise ValueError(
208+
f"Invalid 'start' value: {args.start}. It exceeds the trajectory length"
209+
" of {len(u.trajectory)}."
210+
)
211+
212+
def _check_input_end(self, u, args):
213+
"""Check that the end index does not exceed the trajectory length."""
214+
if args.end > len(u.trajectory):
215+
raise ValueError(
216+
f"Invalid 'end' value: {args.end}. It exceeds the trajectory length of"
217+
" {len(u.trajectory)}."
218+
)
219+
220+
def _check_input_step(self, args):
221+
"""Check that the step value is non-negative."""
222+
if args.step < 0:
223+
logger.warning(
224+
f"Negative 'step' value provided: {args.step}. This may lead to"
225+
" unexpected behavior."
226+
)
227+
228+
def _check_input_bin_width(self, args):
229+
"""Check that the bin width is within the valid range [0, 360]."""
230+
if args.bin_width < 0 or args.bin_width > 360:
231+
raise ValueError(
232+
f"Invalid 'bin_width': {args.bin_width}. It must be between 0 and 360"
233+
" degrees."
234+
)
235+
236+
def _check_input_temperature(self, args):
237+
"""Check that the temperature is non-negative."""
238+
if args.temperature < 0:
239+
raise ValueError(
240+
f"Invalid 'temperature': {args.temperature}. Temperature cannot be"
241+
" below 0."
242+
)
243+
244+
def _check_input_force_partitioning(self, args):
245+
"""Warn if force partitioning is not set to the default value."""
246+
default_value = arg_map["force_partitioning"]["default"]
247+
if args.force_partitioning != default_value:
248+
logger.warning(
249+
f"'force_partitioning' is set to {args.force_partitioning},"
250+
" which differs from the default ({default_value})."
251+
)

CodeEntropy/config/data_logger.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import logging
3+
import re
34

45
from tabulate import tabulate
56

@@ -23,13 +24,19 @@ def save_dataframes_as_json(self, molecule_df, residue_df, output_file):
2324
with open(output_file, "w") as out:
2425
json.dump(data, out, indent=4)
2526

26-
def add_results_data(self, molecule, level, type, S_molecule):
27+
def clean_residue_name(self, resname):
28+
"""Ensures residue names are stripped and cleaned before being stored"""
29+
return re.sub(r"[-–—]", "", str(resname))
30+
31+
def add_results_data(self, resname, level, entropy_type, value):
2732
"""Add data for molecule-level entries"""
28-
self.molecule_data.append([molecule, level, type, f"{S_molecule}"])
33+
resname = self.clean_residue_name(resname)
34+
self.molecule_data.append((resname, level, entropy_type, value))
2935

30-
def add_residue_data(self, molecule, residue, type, S_trans_residue):
36+
def add_residue_data(self, resid, resname, level, entropy_type, value):
3137
"""Add data for residue-level entries"""
32-
self.residue_data.append([molecule, residue, type, f"{S_trans_residue}"])
38+
resname = self.clean_residue_name(resname)
39+
self.residue_data.append([resid, resname, level, entropy_type, value])
3340

3441
def log_tables(self):
3542
"""Log both tables at once"""
@@ -38,8 +45,10 @@ def log_tables(self):
3845
logger.info("Molecule Data Table:")
3946
table_str = tabulate(
4047
self.molecule_data,
41-
headers=["Molecule ID", "Level", "Type", "Result (J/mol/K)"],
48+
headers=["Residue Name", "Level", "Type", "Result (J/mol/K)"],
4249
tablefmt="grid",
50+
numalign="center",
51+
stralign="center",
4352
)
4453
logger.info(f"\n{table_str}")
4554

@@ -48,7 +57,15 @@ def log_tables(self):
4857
logger.info("Residue Data Table:")
4958
table_str = tabulate(
5059
self.residue_data,
51-
headers=["Molecule ID", "Residue", "Type", "Result (J/mol/K)"],
60+
headers=[
61+
"Residue ID",
62+
"Residue Name",
63+
"Level",
64+
"Type",
65+
"Result (J/mol/K)",
66+
],
5267
tablefmt="grid",
68+
numalign="center",
69+
stralign="center",
5370
)
5471
logger.info(f"\n{table_str}")

0 commit comments

Comments
 (0)