Skip to content

Commit 17b5abd

Browse files
authored
Merge pull request #170 from CCPBioSim/169-add-python-314
Add `Python v3.14` Support for `CodeEntropy`
2 parents e2b63fb + f6631a2 commit 17b5abd

7 files changed

Lines changed: 135 additions & 23 deletions

File tree

.github/workflows/project-ci.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
strategy:
1515
matrix:
1616
os: [ubuntu-24.04, windows-2025, macos-15]
17-
python-version: ["3.11", "3.12", "3.13"]
17+
python-version: ["3.11", "3.12", "3.13", "3.14"]
1818
steps:
1919
- name: Checkout repo
2020
uses: actions/checkout@v5.0.0
@@ -40,10 +40,10 @@ jobs:
4040
timeout-minutes: 15
4141
steps:
4242
- uses: actions/checkout@v5.0.0
43-
- name: Set up Python 3.13
43+
- name: Set up Python 3.14
4444
uses: actions/setup-python@v6.0.0
4545
with:
46-
python-version: 3.13
46+
python-version: 3.14
4747
- name: Install python dependencies
4848
run: |
4949
pip install --upgrade pip
@@ -56,10 +56,10 @@ jobs:
5656
timeout-minutes: 15
5757
steps:
5858
- uses: actions/checkout@v5.0.0
59-
- name: Set up Python 3.13
59+
- name: Set up Python 3.14
6060
uses: actions/setup-python@v6.0.0
6161
with:
62-
python-version: 3.13
62+
python-version: 3.14
6363
- name: Install python dependencies
6464
run: |
6565
pip install --upgrade pip
@@ -75,7 +75,7 @@ jobs:
7575
timeout-minutes: 15
7676
strategy:
7777
matrix:
78-
python-version: ["3.11", "3.12", "3.13"]
78+
python-version: ["3.11", "3.12", "3.13", "3.14"]
7979
mdanalysis-version: ["2.9.0", "latest"]
8080
name: MDAnalysis Compatibility Tests
8181
steps:

.github/workflows/release.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- name: Set up Python
2424
uses: actions/setup-python@v6.0.0
2525
with:
26-
python-version: 3.13
26+
python-version: 3.14
2727

2828
- name: Get latest release from pip
2929
id: latestreleased
@@ -133,7 +133,7 @@ jobs:
133133
- name: Set up Python
134134
uses: actions/setup-python@v6.0.0
135135
with:
136-
python-version: 3.13
136+
python-version: 3.14
137137

138138
- name: Install flit
139139
run: |

CodeEntropy/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ def main():
2525

2626
if __name__ == "__main__":
2727

28-
main()
28+
main() # pragma: no cover

pyproject.toml

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ classifiers = [
2929
"Programming Language :: Python :: 3.11",
3030
"Programming Language :: Python :: 3.12",
3131
"Programming Language :: Python :: 3.13",
32+
"Programming Language :: Python :: 3.14",
3233
"Intended Audience :: Science/Research",
3334
"License :: OSI Approved :: MIT License",
3435
"Natural Language :: English",
@@ -37,15 +38,15 @@ classifiers = [
3738
keywords = ["entropy", "macromolecular systems", "MD simulation"]
3839
requires-python = ">=3.11"
3940
dependencies = [
40-
"numpy==2.2.3",
41-
"mdanalysis>=2.9.0",
42-
"pandas==2.2.3",
43-
"psutil==5.9.5",
44-
"PyYAML==6.0.2",
45-
"python-json-logger==3.3.0",
46-
"rich==14.0.0",
41+
"numpy==2.3.4",
42+
"mdanalysis>=2.10.0",
43+
"pandas==2.3.3",
44+
"psutil==7.1.3",
45+
"PyYAML==6.0.3",
46+
"python-json-logger==4.0.0",
47+
"rich==14.2.0",
4748
"art==6.5",
48-
"waterEntropy==1.2.0",
49+
"waterEntropy==1.2.2",
4950
"requests>=2.32.5",
5051
]
5152

@@ -56,14 +57,14 @@ Documentation = "https://codeentropy.readthedocs.io"
5657

5758
[project.optional-dependencies]
5859
testing = [
59-
"pytest==8.2.2",
60-
"pytest-cov==5.0.0",
61-
"pytest-sugar==1.0.0"
60+
"pytest>=8.4.2",
61+
"pytest-cov>=7.0.0",
62+
"pytest-sugar>=1.1.1"
6263
]
6364

6465
pre-commit = [
65-
"pre-commit==3.7.1",
66-
"pylint==3.2.5"
66+
"pre-commit>=4.3.0",
67+
"pylint>=4.0.0"
6768
]
6869
docs = [
6970
"sphinx",

tests/test_CodeEntropy/test_data_logger.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ def test_save_dataframes_as_json(self):
115115

116116
def test_log_tables_rich_output(self):
117117
console = LoggingConfig.get_console()
118-
console.clear_live()
119118

120119
self.logger.add_results_data(
121120
0, "united_atom", "Transvibrational", 653.4041220313459

tests/test_CodeEntropy/test_entropy.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,51 @@ def test_assign_conformation(self):
16571657
assert np.all(result >= 0)
16581658
assert np.issubdtype(result.dtype, np.floating)
16591659

1660+
def test_assign_conformation_last_bin_peak(self):
1661+
"""
1662+
Test that the last bin in the histogram is correctly evaluated as a peak
1663+
when its population is greater than or equal to its neighbors.
1664+
"""
1665+
1666+
dihedral = MagicMock()
1667+
dihedral.value = MagicMock(side_effect=[5, 10, 250, 260, 350, 355])
1668+
1669+
# Mock trajectory frames
1670+
mock_timesteps = [MagicMock(frame=i) for i in range(6)]
1671+
data_container = MagicMock()
1672+
data_container.trajectory.__getitem__.return_value = mock_timesteps
1673+
1674+
# Create dummy universe and managers
1675+
tprfile = os.path.join(self.test_data_dir, "md_A4_dna.tpr")
1676+
trrfile = os.path.join(self.test_data_dir, "md_A4_dna_xf.trr")
1677+
u = mda.Universe(tprfile, trrfile)
1678+
1679+
args = MagicMock(bin_width=60, temperature=300, selection_string="all")
1680+
run_manager = RunManager("mock_folder/job001")
1681+
level_manager = LevelManager()
1682+
data_logger = DataLogger()
1683+
group_molecules = MagicMock()
1684+
1685+
ce = ConformationalEntropy(
1686+
run_manager, args, u, data_logger, level_manager, group_molecules
1687+
)
1688+
1689+
result = ce.assign_conformation(
1690+
data_container=data_container,
1691+
dihedral=dihedral,
1692+
number_frames=6,
1693+
bin_width=60,
1694+
start=0,
1695+
end=6,
1696+
step=1,
1697+
)
1698+
1699+
# Basic checks
1700+
assert isinstance(result, np.ndarray)
1701+
assert len(result) == 6
1702+
assert np.all(result >= 0)
1703+
assert np.issubdtype(result.dtype, np.floating)
1704+
16601705
def test_conformational_entropy_calculation(self):
16611706
"""
16621707
Test `conformational_entropy_calculation` method to verify

tests/test_CodeEntropy/test_levels.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,6 +1065,73 @@ def test_update_force_torque_matrices_united_atom(self):
10651065
np.testing.assert_array_equal(torque_avg["ua"][key], t_mat_mock)
10661066
self.assertEqual(frame_counts["ua"][key], 1)
10671067

1068+
def test_update_force_torque_matrices_united_atom_increment(self):
1069+
"""
1070+
Test that `update_force_torque_matrices` correctly updates force and torque
1071+
matrices for the 'united_atom' level when the key already exists.
1072+
"""
1073+
level_manager = LevelManager()
1074+
entropy_manager = MagicMock()
1075+
mol = MagicMock()
1076+
1077+
# Simulate one residue with two atoms
1078+
residue = MagicMock()
1079+
residue.atoms.indices = [0, 1]
1080+
mol.residues = [residue]
1081+
mol.trajectory.__getitem__.return_value = None
1082+
1083+
selected_atoms = MagicMock()
1084+
entropy_manager._run_manager.new_U_select_atom.return_value = selected_atoms
1085+
selected_atoms.trajectory.__getitem__.return_value = None
1086+
1087+
f_mat_1 = np.array([[1.0]], dtype=np.float64)
1088+
t_mat_1 = np.array([[2.0]], dtype=np.float64)
1089+
f_mat_2 = np.array([[3.0]], dtype=np.float64)
1090+
t_mat_2 = np.array([[4.0]], dtype=np.float64)
1091+
1092+
level_manager.get_matrices = MagicMock(return_value=(f_mat_1, t_mat_1))
1093+
1094+
force_avg = {"ua": {}, "res": [None], "poly": [None]}
1095+
torque_avg = {"ua": {}, "res": [None], "poly": [None]}
1096+
frame_counts = {"ua": {}, "res": [None], "poly": [None]}
1097+
1098+
# First call: initialize
1099+
level_manager.update_force_torque_matrices(
1100+
entropy_manager=entropy_manager,
1101+
mol=mol,
1102+
group_id=0,
1103+
level="united_atom",
1104+
level_list=["residue", "united_atom"],
1105+
time_index=0,
1106+
num_frames=10,
1107+
force_avg=force_avg,
1108+
torque_avg=torque_avg,
1109+
frame_counts=frame_counts,
1110+
)
1111+
1112+
# Second call: update
1113+
level_manager.get_matrices = MagicMock(return_value=(f_mat_2, t_mat_2))
1114+
1115+
level_manager.update_force_torque_matrices(
1116+
entropy_manager=entropy_manager,
1117+
mol=mol,
1118+
group_id=0,
1119+
level="united_atom",
1120+
level_list=["residue", "united_atom"],
1121+
time_index=1,
1122+
num_frames=10,
1123+
force_avg=force_avg,
1124+
torque_avg=torque_avg,
1125+
frame_counts=frame_counts,
1126+
)
1127+
1128+
expected_force = f_mat_1 + (f_mat_2 - f_mat_1) / 2
1129+
expected_torque = t_mat_1 + (t_mat_2 - t_mat_1) / 2
1130+
1131+
np.testing.assert_array_almost_equal(force_avg["ua"][(0, 0)], expected_force)
1132+
np.testing.assert_array_almost_equal(torque_avg["ua"][(0, 0)], expected_torque)
1133+
self.assertEqual(frame_counts["ua"][(0, 0)], 2)
1134+
10681135
def test_update_force_torque_matrices_residue(self):
10691136
"""
10701137
Test that `update_force_torque_matrices` correctly updates force and torque

0 commit comments

Comments
 (0)