CodeEntropy 1.0.5__tar.gz → 1.0.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codeentropy-1.0.5 → codeentropy-1.0.7}/.github/renovate.json +5 -3
- {codeentropy-1.0.5 → codeentropy-1.0.7}/.github/workflows/mdanalysis-compatibility.yaml +3 -3
- {codeentropy-1.0.5 → codeentropy-1.0.7}/.github/workflows/project-ci.yaml +10 -10
- {codeentropy-1.0.5 → codeentropy-1.0.7}/.github/workflows/release.yaml +11 -11
- codeentropy-1.0.7/.github/workflows/update-copyright-years-in-license-file.yaml +17 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CITATION.cff +2 -2
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CodeEntropy/__init__.py +1 -1
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CodeEntropy/config/arg_config_manager.py +1 -1
- codeentropy-1.0.7/CodeEntropy/dihedral_tools.py +392 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CodeEntropy/entropy.py +82 -126
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CodeEntropy/group_molecules.py +0 -10
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CodeEntropy/levels.py +4 -272
- codeentropy-1.0.7/CodeEntropy/mda_universe_operations.py +159 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CodeEntropy/run.py +21 -110
- {codeentropy-1.0.5 → codeentropy-1.0.7}/LICENSE +1 -1
- {codeentropy-1.0.5 → codeentropy-1.0.7}/PKG-INFO +24 -24
- {codeentropy-1.0.5 → codeentropy-1.0.7}/pyproject.toml +23 -23
- codeentropy-1.0.7/tests/test_CodeEntropy/test_dihedral_tools.py +571 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/test_CodeEntropy/test_entropy.py +323 -219
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/test_CodeEntropy/test_levels.py +177 -347
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/test_CodeEntropy/test_main.py +3 -2
- codeentropy-1.0.7/tests/test_CodeEntropy/test_mda_universe_operations.py +315 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/test_CodeEntropy/test_run.py +69 -280
- codeentropy-1.0.5/.github/workflows/renovate.yaml +0 -28
- {codeentropy-1.0.5 → codeentropy-1.0.7}/.github/CONTRIBUTING.md +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/.github/dependabot.yml +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/.github/workflows/mdanalysis-compatibility-failure.md +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/.gitignore +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/.pre-commit-config.yaml +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CODE_OF_CONDUCT.md +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CodeEntropy/config/__init__.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CodeEntropy/config/data_logger.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CodeEntropy/config/logging_config.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/CodeEntropy/main.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/README.md +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/Makefile +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/README.md +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/_static/README.md +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/_static/custom.css +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/_templates/README.md +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/api.rst +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/conf.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/config.yaml +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/developer_guide.rst +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/faq.rst +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/getting_started.rst +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/images/biosim-codeentropy_logo_grey.svg +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/index.rst +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/docs/science.rst +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/readthedocs.yml +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/__init__.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/data/__init__.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/data/md_A4_dna.tpr +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/data/md_A4_dna_xf.trr +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/test_CodeEntropy/test_arg_config_manager.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/test_CodeEntropy/test_base.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/test_CodeEntropy/test_data_logger.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/test_CodeEntropy/test_group_molecules.py +0 -0
- {codeentropy-1.0.5 → codeentropy-1.0.7}/tests/test_CodeEntropy/test_logging_config.py +0 -0
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": [
|
|
3
3
|
"config:best-practices",
|
|
4
|
-
":pinAllExceptPeerDependencies",
|
|
5
4
|
":dependencyDashboard",
|
|
6
5
|
"group:monorepos",
|
|
7
6
|
"group:recommended"
|
|
8
7
|
],
|
|
9
|
-
"
|
|
10
|
-
"
|
|
8
|
+
"enabledManagers": ["pep621"],
|
|
9
|
+
"pep621": {
|
|
11
10
|
"enabled": true
|
|
12
11
|
},
|
|
12
|
+
"rangeStrategy": "replace",
|
|
13
|
+
"schedule": ["* 8 * * 1-5"],
|
|
14
|
+
"labels": ["dependencies"],
|
|
13
15
|
"packageRules": [
|
|
14
16
|
{
|
|
15
17
|
"matchUpdateTypes": ["minor", "patch"],
|
|
@@ -18,10 +18,10 @@ jobs:
|
|
|
18
18
|
|
|
19
19
|
steps:
|
|
20
20
|
- name: Checkout repo
|
|
21
|
-
uses: actions/checkout@
|
|
21
|
+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
|
22
22
|
|
|
23
23
|
- name: Set up Python ${{ matrix.python-version }}
|
|
24
|
-
uses: actions/setup-python@v6.
|
|
24
|
+
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
|
25
25
|
with:
|
|
26
26
|
python-version: ${{ matrix.python-version }}
|
|
27
27
|
|
|
@@ -36,7 +36,7 @@ jobs:
|
|
|
36
36
|
|
|
37
37
|
- name: Create Issue on Failure
|
|
38
38
|
if: failure()
|
|
39
|
-
uses: JasonEtco/create-an-issue@v2
|
|
39
|
+
uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2
|
|
40
40
|
env:
|
|
41
41
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
42
42
|
PYTHON_VERSION: ${{ matrix.python-version }}
|
|
@@ -5,7 +5,7 @@ on:
|
|
|
5
5
|
branches: [main]
|
|
6
6
|
pull_request:
|
|
7
7
|
schedule:
|
|
8
|
-
- cron: '0 8 * * 1'
|
|
8
|
+
- cron: '0 8 * * 1-5'
|
|
9
9
|
workflow_dispatch:
|
|
10
10
|
|
|
11
11
|
jobs:
|
|
@@ -18,10 +18,10 @@ jobs:
|
|
|
18
18
|
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
19
19
|
steps:
|
|
20
20
|
- name: Checkout repo
|
|
21
|
-
uses: actions/checkout@
|
|
21
|
+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
|
22
22
|
|
|
23
23
|
- name: Set up Python ${{ matrix.python-version }}
|
|
24
|
-
uses: actions/setup-python@v6.
|
|
24
|
+
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
|
25
25
|
with:
|
|
26
26
|
python-version: ${{ matrix.python-version }}
|
|
27
27
|
|
|
@@ -32,7 +32,7 @@ jobs:
|
|
|
32
32
|
run: pytest --cov CodeEntropy --cov-report term-missing --cov-append .
|
|
33
33
|
|
|
34
34
|
- name: Coveralls GitHub Action
|
|
35
|
-
uses: coverallsapp/github-action@v2.3.7
|
|
35
|
+
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
|
|
36
36
|
with:
|
|
37
37
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
38
38
|
|
|
@@ -40,11 +40,11 @@ jobs:
|
|
|
40
40
|
runs-on: ubuntu-latest
|
|
41
41
|
timeout-minutes: 15
|
|
42
42
|
steps:
|
|
43
|
-
- uses: actions/checkout@
|
|
43
|
+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
|
44
44
|
- name: Set up Python 3.14
|
|
45
|
-
uses: actions/setup-python@v6.
|
|
45
|
+
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
|
46
46
|
with:
|
|
47
|
-
python-version: 3.14
|
|
47
|
+
python-version: 3.14.0
|
|
48
48
|
- name: Install python dependencies
|
|
49
49
|
run: |
|
|
50
50
|
pip install --upgrade pip
|
|
@@ -56,11 +56,11 @@ jobs:
|
|
|
56
56
|
runs-on: ubuntu-24.04
|
|
57
57
|
timeout-minutes: 15
|
|
58
58
|
steps:
|
|
59
|
-
- uses: actions/checkout@
|
|
59
|
+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
|
60
60
|
- name: Set up Python 3.14
|
|
61
|
-
uses: actions/setup-python@v6.
|
|
61
|
+
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
|
62
62
|
with:
|
|
63
|
-
python-version: 3.14
|
|
63
|
+
python-version: 3.14.0
|
|
64
64
|
- name: Install python dependencies
|
|
65
65
|
run: |
|
|
66
66
|
pip install --upgrade pip
|
|
@@ -18,12 +18,12 @@ jobs:
|
|
|
18
18
|
steps:
|
|
19
19
|
- name: Checkout repository
|
|
20
20
|
id: repo
|
|
21
|
-
uses: actions/checkout@
|
|
21
|
+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
|
22
22
|
|
|
23
23
|
- name: Set up Python
|
|
24
|
-
uses: actions/setup-python@v6.
|
|
24
|
+
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
|
25
25
|
with:
|
|
26
|
-
python-version: 3.14
|
|
26
|
+
python-version: 3.14.0
|
|
27
27
|
|
|
28
28
|
- name: Get latest release from pip
|
|
29
29
|
id: latestreleased
|
|
@@ -46,7 +46,7 @@ jobs:
|
|
|
46
46
|
steps:
|
|
47
47
|
|
|
48
48
|
- name: checkout
|
|
49
|
-
uses: actions/checkout@
|
|
49
|
+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
|
50
50
|
|
|
51
51
|
- name: Change version in repo and CITATION.cff
|
|
52
52
|
run: |
|
|
@@ -61,7 +61,7 @@ jobs:
|
|
|
61
61
|
|
|
62
62
|
- name: send PR
|
|
63
63
|
id: pr_id
|
|
64
|
-
uses: peter-evans/create-pull-request@
|
|
64
|
+
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
|
|
65
65
|
with:
|
|
66
66
|
commit-message: Update version to ${{ github.event.inputs.version }}
|
|
67
67
|
branch: version-update
|
|
@@ -78,7 +78,7 @@ jobs:
|
|
|
78
78
|
draft: false
|
|
79
79
|
|
|
80
80
|
- name: auto approve review
|
|
81
|
-
uses: hmarr/auto-approve-action@v4.0.0
|
|
81
|
+
uses: hmarr/auto-approve-action@f0939ea97e9205ef24d872e76833fa908a770363 # v4.0.0
|
|
82
82
|
with:
|
|
83
83
|
pull-request-number: ${{ steps.pr_id.outputs.pull-request-number }}
|
|
84
84
|
review-message: "Auto approved version bump PR"
|
|
@@ -95,7 +95,7 @@ jobs:
|
|
|
95
95
|
runs-on: ubuntu-24.04
|
|
96
96
|
steps:
|
|
97
97
|
- name: Checkout repository
|
|
98
|
-
uses: actions/checkout@
|
|
98
|
+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
|
99
99
|
with:
|
|
100
100
|
ref: main
|
|
101
101
|
|
|
@@ -113,7 +113,7 @@ jobs:
|
|
|
113
113
|
steps:
|
|
114
114
|
|
|
115
115
|
- name: create release
|
|
116
|
-
uses: softprops/action-gh-release@v2.
|
|
116
|
+
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
|
117
117
|
with:
|
|
118
118
|
name: v${{ github.event.inputs.version }}
|
|
119
119
|
generate_release_notes: true
|
|
@@ -126,14 +126,14 @@ jobs:
|
|
|
126
126
|
steps:
|
|
127
127
|
|
|
128
128
|
- name: checkout
|
|
129
|
-
uses: actions/checkout@
|
|
129
|
+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
|
130
130
|
with:
|
|
131
131
|
ref: main
|
|
132
132
|
|
|
133
133
|
- name: Set up Python
|
|
134
|
-
uses: actions/setup-python@v6.
|
|
134
|
+
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
|
135
135
|
with:
|
|
136
|
-
python-version: 3.14
|
|
136
|
+
python-version: 3.14.0
|
|
137
137
|
|
|
138
138
|
- name: Install flit
|
|
139
139
|
run: |
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Update copyright year(s) in license file
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
schedule:
|
|
5
|
+
- cron: '0 3 1 1 *' # 03:00 AM on January 1
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
update-license-year:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v6
|
|
13
|
+
with:
|
|
14
|
+
fetch-depth: 0
|
|
15
|
+
- uses: FantasticFiasco/action-update-license-year@v3
|
|
16
|
+
with:
|
|
17
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -27,7 +27,7 @@ arg_map = {
|
|
|
27
27
|
"kcal_force_units": {
|
|
28
28
|
"type": bool,
|
|
29
29
|
"default": False,
|
|
30
|
-
"help": "Set this to True if you have a separate force file with
|
|
30
|
+
"help": "Set this to True if you have a separate force file with kcal units.",
|
|
31
31
|
},
|
|
32
32
|
"selection_string": {
|
|
33
33
|
"type": str,
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from MDAnalysis.analysis.dihedrals import Dihedral
|
|
5
|
+
from rich.progress import (
|
|
6
|
+
BarColumn,
|
|
7
|
+
Progress,
|
|
8
|
+
SpinnerColumn,
|
|
9
|
+
TextColumn,
|
|
10
|
+
TimeElapsedColumn,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DihedralAnalysis:
|
|
17
|
+
"""
|
|
18
|
+
Functions for finding dihedral angles and analysing them to get the
|
|
19
|
+
states needed for the conformational entropy functions.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, universe_operations=None):
|
|
23
|
+
"""
|
|
24
|
+
Initialise with placeholders.
|
|
25
|
+
"""
|
|
26
|
+
self._universe_operations = universe_operations
|
|
27
|
+
self.data_container = None
|
|
28
|
+
self.states_ua = None
|
|
29
|
+
self.states_res = None
|
|
30
|
+
|
|
31
|
+
def build_conformational_states(
|
|
32
|
+
self,
|
|
33
|
+
data_container,
|
|
34
|
+
levels,
|
|
35
|
+
groups,
|
|
36
|
+
start,
|
|
37
|
+
end,
|
|
38
|
+
step,
|
|
39
|
+
bin_width,
|
|
40
|
+
):
|
|
41
|
+
"""
|
|
42
|
+
Build the conformational states descriptors based on dihedral angles
|
|
43
|
+
needed for the calculation of the conformational entropy.
|
|
44
|
+
"""
|
|
45
|
+
number_groups = len(groups)
|
|
46
|
+
states_ua = {}
|
|
47
|
+
states_res = [None] * number_groups
|
|
48
|
+
|
|
49
|
+
total_items = sum(
|
|
50
|
+
len(levels[mol_id]) for mols in groups.values() for mol_id in mols
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
with Progress(
|
|
54
|
+
SpinnerColumn(),
|
|
55
|
+
TextColumn("[bold blue]{task.fields[title]}", justify="right"),
|
|
56
|
+
BarColumn(),
|
|
57
|
+
TextColumn("[progress.percentage]{task.percentage:>3.1f}%"),
|
|
58
|
+
TimeElapsedColumn(),
|
|
59
|
+
) as progress:
|
|
60
|
+
|
|
61
|
+
task = progress.add_task(
|
|
62
|
+
"[green]Building Conformational States...",
|
|
63
|
+
total=total_items,
|
|
64
|
+
title="Starting...",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
for group_id in groups.keys():
|
|
68
|
+
molecules = groups[group_id]
|
|
69
|
+
mol = self._universe_operations.get_molecule_container(
|
|
70
|
+
data_container, molecules[0]
|
|
71
|
+
)
|
|
72
|
+
num_residues = len(mol.residues)
|
|
73
|
+
dihedrals_ua = [[] for _ in range(num_residues)]
|
|
74
|
+
peaks_ua = [{} for _ in range(num_residues)]
|
|
75
|
+
dihedrals_res = []
|
|
76
|
+
peaks_res = {}
|
|
77
|
+
|
|
78
|
+
# Identify dihedral AtomGroups
|
|
79
|
+
for level in levels[molecules[0]]:
|
|
80
|
+
if level == "united_atom":
|
|
81
|
+
for res_id in range(num_residues):
|
|
82
|
+
selection1 = mol.residues[res_id].atoms.indices[0]
|
|
83
|
+
selection2 = mol.residues[res_id].atoms.indices[-1]
|
|
84
|
+
res_container = self._universe_operations.new_U_select_atom(
|
|
85
|
+
mol,
|
|
86
|
+
f"index {selection1}:" f"{selection2}",
|
|
87
|
+
)
|
|
88
|
+
heavy_res = self._universe_operations.new_U_select_atom(
|
|
89
|
+
res_container, "prop mass > 1.1"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
dihedrals_ua[res_id] = self._get_dihedrals(heavy_res, level)
|
|
93
|
+
|
|
94
|
+
elif level == "residue":
|
|
95
|
+
dihedrals_res = self._get_dihedrals(mol, level)
|
|
96
|
+
|
|
97
|
+
# Identify peaks
|
|
98
|
+
for level in levels[molecules[0]]:
|
|
99
|
+
if level == "united_atom":
|
|
100
|
+
for res_id in range(num_residues):
|
|
101
|
+
if len(dihedrals_ua[res_id]) == 0:
|
|
102
|
+
# No dihedrals means no histogram or peaks
|
|
103
|
+
peaks_ua[res_id] = []
|
|
104
|
+
else:
|
|
105
|
+
peaks_ua[res_id] = self._identify_peaks(
|
|
106
|
+
data_container,
|
|
107
|
+
molecules,
|
|
108
|
+
dihedrals_ua[res_id],
|
|
109
|
+
bin_width,
|
|
110
|
+
start,
|
|
111
|
+
end,
|
|
112
|
+
step,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
elif level == "residue":
|
|
116
|
+
if len(dihedrals_res) == 0:
|
|
117
|
+
# No dihedrals means no histogram or peaks
|
|
118
|
+
peaks_res = []
|
|
119
|
+
else:
|
|
120
|
+
peaks_res = self._identify_peaks(
|
|
121
|
+
data_container,
|
|
122
|
+
molecules,
|
|
123
|
+
dihedrals_res,
|
|
124
|
+
bin_width,
|
|
125
|
+
start,
|
|
126
|
+
end,
|
|
127
|
+
step,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Assign states for each group
|
|
131
|
+
for level in levels[molecules[0]]:
|
|
132
|
+
if level == "united_atom":
|
|
133
|
+
for res_id in range(num_residues):
|
|
134
|
+
key = (group_id, res_id)
|
|
135
|
+
if len(dihedrals_ua[res_id]) == 0:
|
|
136
|
+
# No conformational states
|
|
137
|
+
states_ua[key] = []
|
|
138
|
+
else:
|
|
139
|
+
states_ua[key] = self._assign_states(
|
|
140
|
+
data_container,
|
|
141
|
+
molecules,
|
|
142
|
+
dihedrals_ua[res_id],
|
|
143
|
+
peaks_ua[res_id],
|
|
144
|
+
start,
|
|
145
|
+
end,
|
|
146
|
+
step,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
elif level == "residue":
|
|
150
|
+
if len(dihedrals_res) == 0:
|
|
151
|
+
# No conformational states
|
|
152
|
+
states_res[group_id] = []
|
|
153
|
+
else:
|
|
154
|
+
states_res[group_id] = self._assign_states(
|
|
155
|
+
data_container,
|
|
156
|
+
molecules,
|
|
157
|
+
dihedrals_res,
|
|
158
|
+
peaks_res,
|
|
159
|
+
start,
|
|
160
|
+
end,
|
|
161
|
+
step,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
progress.advance(task)
|
|
165
|
+
|
|
166
|
+
return states_ua, states_res
|
|
167
|
+
|
|
168
|
+
def _get_dihedrals(self, data_container, level):
|
|
169
|
+
"""
|
|
170
|
+
Define the set of dihedrals for use in the conformational entropy function.
|
|
171
|
+
If united atom level, the dihedrals are defined from the heavy atoms
|
|
172
|
+
(4 bonded atoms for 1 dihedral).
|
|
173
|
+
If residue level, use the bonds between residues to cast dihedrals.
|
|
174
|
+
Note: not using improper dihedrals only ones with 4 atoms/residues
|
|
175
|
+
in a linear arrangement.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
data_container (MDAnalysis.Universe): system information
|
|
179
|
+
level (str): level of the hierarchy (should be residue or polymer)
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
dihedrals (array): set of dihedrals
|
|
183
|
+
"""
|
|
184
|
+
# Start with empty array
|
|
185
|
+
dihedrals = []
|
|
186
|
+
atom_groups = []
|
|
187
|
+
|
|
188
|
+
# if united atom level, read dihedrals from MDAnalysis universe
|
|
189
|
+
if level == "united_atom":
|
|
190
|
+
dihedrals = data_container.dihedrals
|
|
191
|
+
num_dihedrals = len(dihedrals)
|
|
192
|
+
for index in range(num_dihedrals):
|
|
193
|
+
atom_groups.append(dihedrals[index].atoms)
|
|
194
|
+
|
|
195
|
+
# if residue level, looking for dihedrals involving residues
|
|
196
|
+
if level == "residue":
|
|
197
|
+
num_residues = len(data_container.residues)
|
|
198
|
+
logger.debug(f"Number Residues: {num_residues}")
|
|
199
|
+
if num_residues < 4:
|
|
200
|
+
logger.debug("no residue level dihedrals")
|
|
201
|
+
|
|
202
|
+
else:
|
|
203
|
+
# find bonds between residues N-3:N-2 and N-1:N
|
|
204
|
+
for residue in range(4, num_residues + 1):
|
|
205
|
+
# Using MDAnalysis selection,
|
|
206
|
+
# assuming only one covalent bond between neighbouring residues
|
|
207
|
+
# TODO not written for branched polymers
|
|
208
|
+
atom_string = (
|
|
209
|
+
"resindex "
|
|
210
|
+
+ str(residue - 4)
|
|
211
|
+
+ " and bonded resindex "
|
|
212
|
+
+ str(residue - 3)
|
|
213
|
+
)
|
|
214
|
+
atom1 = data_container.select_atoms(atom_string)
|
|
215
|
+
|
|
216
|
+
atom_string = (
|
|
217
|
+
"resindex "
|
|
218
|
+
+ str(residue - 3)
|
|
219
|
+
+ " and bonded resindex "
|
|
220
|
+
+ str(residue - 4)
|
|
221
|
+
)
|
|
222
|
+
atom2 = data_container.select_atoms(atom_string)
|
|
223
|
+
|
|
224
|
+
atom_string = (
|
|
225
|
+
"resindex "
|
|
226
|
+
+ str(residue - 2)
|
|
227
|
+
+ " and bonded resindex "
|
|
228
|
+
+ str(residue - 1)
|
|
229
|
+
)
|
|
230
|
+
atom3 = data_container.select_atoms(atom_string)
|
|
231
|
+
|
|
232
|
+
atom_string = (
|
|
233
|
+
"resindex "
|
|
234
|
+
+ str(residue - 1)
|
|
235
|
+
+ " and bonded resindex "
|
|
236
|
+
+ str(residue - 2)
|
|
237
|
+
)
|
|
238
|
+
atom4 = data_container.select_atoms(atom_string)
|
|
239
|
+
|
|
240
|
+
atom_group = atom1 + atom2 + atom3 + atom4
|
|
241
|
+
atom_groups.append(atom_group)
|
|
242
|
+
|
|
243
|
+
logger.debug(f"Level: {level}, Dihedrals: {atom_groups}")
|
|
244
|
+
|
|
245
|
+
return atom_groups
|
|
246
|
+
|
|
247
|
+
def _identify_peaks(
|
|
248
|
+
self,
|
|
249
|
+
data_container,
|
|
250
|
+
molecules,
|
|
251
|
+
dihedrals,
|
|
252
|
+
bin_width,
|
|
253
|
+
start,
|
|
254
|
+
end,
|
|
255
|
+
step,
|
|
256
|
+
):
|
|
257
|
+
"""
|
|
258
|
+
Build a histogram of the dihedral data and identify the peaks.
|
|
259
|
+
This is to give the information needed for the adaptive method
|
|
260
|
+
of identifying dihedral states.
|
|
261
|
+
"""
|
|
262
|
+
peak_values = [] * len(dihedrals)
|
|
263
|
+
for dihedral_index in range(len(dihedrals)):
|
|
264
|
+
phi = []
|
|
265
|
+
# get the values of the angle for the dihedral
|
|
266
|
+
# loop over all molecules in the averaging group
|
|
267
|
+
# dihedral angle values have a range from -180 to 180
|
|
268
|
+
for molecule in molecules:
|
|
269
|
+
mol = self._universe_operations.get_molecule_container(
|
|
270
|
+
data_container, molecule
|
|
271
|
+
)
|
|
272
|
+
number_frames = len(mol.trajectory)
|
|
273
|
+
dihedral_results = Dihedral(dihedrals).run()
|
|
274
|
+
for timestep in range(number_frames):
|
|
275
|
+
value = dihedral_results.results.angles[timestep][dihedral_index]
|
|
276
|
+
|
|
277
|
+
# We want postive values in range 0 to 360 to make
|
|
278
|
+
# the peak assignment.
|
|
279
|
+
# works using the fact that dihedrals have circular symetry
|
|
280
|
+
# (i.e. -15 degrees = +345 degrees)
|
|
281
|
+
if value < 0:
|
|
282
|
+
value += 360
|
|
283
|
+
phi.append(value)
|
|
284
|
+
|
|
285
|
+
# create a histogram using numpy
|
|
286
|
+
number_bins = int(360 / bin_width)
|
|
287
|
+
popul, bin_edges = np.histogram(a=phi, bins=number_bins, range=(0, 360))
|
|
288
|
+
bin_value = [
|
|
289
|
+
0.5 * (bin_edges[i] + bin_edges[i + 1]) for i in range(0, len(popul))
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
# identify "convex turning-points" and populate a list of peaks
|
|
293
|
+
# peak : a bin whose neighboring bins have smaller population
|
|
294
|
+
# NOTE might have problems if the peak is wide with a flat or
|
|
295
|
+
# sawtooth top in which case check you have a sensible bin width
|
|
296
|
+
|
|
297
|
+
peaks = []
|
|
298
|
+
for bin_index in range(number_bins):
|
|
299
|
+
# if there is no dihedrals in a bin then it cannot be a peak
|
|
300
|
+
if popul[bin_index] == 0:
|
|
301
|
+
pass
|
|
302
|
+
# being careful of the last bin
|
|
303
|
+
# (dihedrals have circular symmetry, the histogram does not)
|
|
304
|
+
elif (
|
|
305
|
+
bin_index == number_bins - 1
|
|
306
|
+
): # the -1 is because the index starts with 0 not 1
|
|
307
|
+
if (
|
|
308
|
+
popul[bin_index] >= popul[bin_index - 1]
|
|
309
|
+
and popul[bin_index] >= popul[0]
|
|
310
|
+
):
|
|
311
|
+
peaks.append(bin_value[bin_index])
|
|
312
|
+
else:
|
|
313
|
+
if (
|
|
314
|
+
popul[bin_index] >= popul[bin_index - 1]
|
|
315
|
+
and popul[bin_index] >= popul[bin_index + 1]
|
|
316
|
+
):
|
|
317
|
+
peaks.append(bin_value[bin_index])
|
|
318
|
+
|
|
319
|
+
peak_values.append(peaks)
|
|
320
|
+
|
|
321
|
+
logger.debug(f"Dihedral: {dihedral_index}, Peak Values: {peak_values}")
|
|
322
|
+
|
|
323
|
+
return peak_values
|
|
324
|
+
|
|
325
|
+
def _assign_states(
|
|
326
|
+
self,
|
|
327
|
+
data_container,
|
|
328
|
+
molecules,
|
|
329
|
+
dihedrals,
|
|
330
|
+
peaks,
|
|
331
|
+
start,
|
|
332
|
+
end,
|
|
333
|
+
step,
|
|
334
|
+
):
|
|
335
|
+
"""
|
|
336
|
+
Turn the dihedral values into conformations based on the peaks
|
|
337
|
+
from the histogram.
|
|
338
|
+
Then combine these to form states for each molecule.
|
|
339
|
+
"""
|
|
340
|
+
states = None
|
|
341
|
+
|
|
342
|
+
# get the values of the angle for the dihedral
|
|
343
|
+
# dihedral angle values have a range from -180 to 180
|
|
344
|
+
for molecule in molecules:
|
|
345
|
+
conformations = []
|
|
346
|
+
mol = self._universe_operations.get_molecule_container(
|
|
347
|
+
data_container, molecule
|
|
348
|
+
)
|
|
349
|
+
number_frames = len(mol.trajectory)
|
|
350
|
+
dihedral_results = Dihedral(dihedrals).run()
|
|
351
|
+
for dihedral_index in range(len(dihedrals)):
|
|
352
|
+
conformation = []
|
|
353
|
+
for timestep in range(number_frames):
|
|
354
|
+
value = dihedral_results.results.angles[timestep][dihedral_index]
|
|
355
|
+
|
|
356
|
+
# We want postive values in range 0 to 360 to make
|
|
357
|
+
# the peak assignment.
|
|
358
|
+
# works using the fact that dihedrals have circular symetry
|
|
359
|
+
# (i.e. -15 degrees = +345 degrees)
|
|
360
|
+
if value < 0:
|
|
361
|
+
value += 360
|
|
362
|
+
|
|
363
|
+
# Find the turning point/peak that the snapshot is closest to.
|
|
364
|
+
distances = [abs(value - peak) for peak in peaks[dihedral_index]]
|
|
365
|
+
conformation.append(np.argmin(distances))
|
|
366
|
+
|
|
367
|
+
logger.debug(
|
|
368
|
+
f"Dihedral: {dihedral_index} Conformations: {conformation}"
|
|
369
|
+
)
|
|
370
|
+
conformations.append(conformation)
|
|
371
|
+
|
|
372
|
+
# for all the dihedrals available concatenate the label of each
|
|
373
|
+
# dihedral into the state for that frame
|
|
374
|
+
mol_states = [
|
|
375
|
+
state
|
|
376
|
+
for state in (
|
|
377
|
+
"".join(
|
|
378
|
+
str(int(conformations[d][f])) for d in range(len(dihedrals))
|
|
379
|
+
)
|
|
380
|
+
for f in range(number_frames)
|
|
381
|
+
)
|
|
382
|
+
if state
|
|
383
|
+
]
|
|
384
|
+
|
|
385
|
+
if states is None:
|
|
386
|
+
states = mol_states
|
|
387
|
+
else:
|
|
388
|
+
states.extend(mol_states)
|
|
389
|
+
|
|
390
|
+
logger.debug(f"States: {states}")
|
|
391
|
+
|
|
392
|
+
return states
|