Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple mutate fixes #565

Merged
merged 25 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
70c3b27
fixed the system error
csbrasnett Dec 13, 2023
d4c886e
Fixed edge case and tests
csbrasnett Dec 14, 2023
597f64a
moved loop into its own function
csbrasnett Apr 5, 2024
32b2ad9
found a new quote
csbrasnett Apr 5, 2024
f07e228
minor formatting changes
csbrasnett Apr 5, 2024
319ede6
made changes and fixed annotation test
csbrasnett Apr 5, 2024
eab5a1f
Merge branch 'marrink-lab:master' into mutate-fix
csbrasnett Apr 8, 2024
84ba9f5
Update codecov action
pckroon Apr 8, 2024
bc7e724
Don't fail deploy on minor issues
pckroon Apr 8, 2024
2c681be
added and clarified tests
csbrasnett Apr 8, 2024
6988702
Merge branch 'mutate-fix' of https://github.com/csbrasnett/vermouth-m…
csbrasnett Apr 8, 2024
2129a4d
added resid tests
csbrasnett Apr 8, 2024
da16846
added mut/mod examples to help
csbrasnett Apr 8, 2024
0a52393
fixed resid specification and added tests
csbrasnett Apr 8, 2024
311704e
Adapt mutmod warning test to run on System
pckroon Apr 10, 2024
6af9f66
Merge branch 'marrink-lab:master' into mutate-fix
csbrasnett Apr 11, 2024
047b98b
updated mutmod annotating and appropriate tests
csbrasnett Apr 11, 2024
6680cbe
added more conditions and corrected tests
csbrasnett Apr 11, 2024
2933079
changed comments
csbrasnett Apr 11, 2024
5fb2b3c
added more tests to increase coverage
csbrasnett Apr 11, 2024
c4c72f0
removed a degenerate condition
csbrasnett Apr 11, 2024
3e1c5c1
simplified annotation function
csbrasnett Apr 16, 2024
643b7e5
addressed comments
csbrasnett Apr 22, 2024
7224854
removed unnecessary conditions
csbrasnett Apr 23, 2024
431b322
added info to docstring
csbrasnett Apr 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,13 @@ jobs:
coverage report --omit='*/bin/pytest'

- if: ${{ matrix.WITH_CODECOV }}
name: Report code coverage
run: |
codecov
name: Upload coverage codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
fail_ci_if_error: false
verbose: true

lint:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:

- if: ${{ matrix.WITH_CODECOV }}
name: Upload coverage codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage.xml
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ htmlcov
doc/build
doc/source/api
doc/source/.doctrees

.idea/
2 changes: 2 additions & 0 deletions vermouth/data/quotes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ Happiness is a dry martini and a good woman... or a bad woman. -- George Burns
A classical Martini is made without Vermouth, although it is better with. -- Peter C Kroon

A classical Martini is made with up to 2 sizes of olives, although newer variants can contain up to three sizes of olives. -- Peter C Kroon

So I said 'I must get out of these wet clothes, and into a dry Martini' -- Homer Simpson
68 changes: 51 additions & 17 deletions vermouth/processors/annotate_mut_mod.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,40 @@ def _format_resname(res):
out += res.get('insertion_code', '')
return out

def _resiter(mod, residue_graph, resspec, library, key, molecule):
pckroon marked this conversation as resolved.
Show resolved Hide resolved
"""
Iterate over residues to find a specific modification

Parameters
----------
mod: str
The modification to apply, eg N-ter, C-ter
residue_graph: networkx.Graph
A graph with one node per residue.
resspec: dict
Attributes that must be present in the residue node. 'resname' is
treated specially as described above.
library: dict
dictionary of modifications/mutations from the force field
key: str
from associations
molecule: networkx.Graph
"""
mod_found = False
for res_idx in residue_graph:
if residue_matches(resspec, residue_graph, res_idx):
mod_found = True
if mod != 'none' and mod not in library:
csbrasnett marked this conversation as resolved.
Show resolved Hide resolved
raise NameError('{} is not known as a {} for '
'force field {}'
''.format(mod, key, molecule.force_field.name))
res = residue_graph.nodes[res_idx]
LOGGER.debug('Annotating {} with {} {}',
_format_resname(res), key, mod)
for node_idx in res['graph']:
molecule.nodes[node_idx][key] = molecule.nodes[node_idx].get(key, []) + [mod]
return mod_found


def annotate_modifications(molecule, modifications, mutations):
"""
Expand Down Expand Up @@ -210,25 +244,22 @@ def annotate_modifications(molecule, modifications, mutations):
(mutations, 'mutation', molecule.force_field.blocks)]

residue_graph = make_residue_graph(molecule)
# Get the name of the chain in the molecule that we're looking at
residue = {key: residue_graph.nodes[0].get(key)
for key in 'chain resid resname insertion_code'.split()}
chain = residue['chain']

for mutmod, key, library in associations:
for resspec, mod in mutmod:
mod_found = False
for res_idx in residue_graph:
if residue_matches(resspec, residue_graph, res_idx):
mod_found = True
if mod != 'none' and mod not in library:
raise NameError('{} is not known as a {} for '
'force field {}'
''.format(mod, key, molecule.force_field.name))
res = residue_graph.nodes[res_idx]
LOGGER.debug('Annotating {} with {} {}',
_format_resname(res), key, mod)
for node_idx in res['graph']:
molecule.nodes[node_idx][key] = molecule.nodes[node_idx].get(key, []) + [mod]
if mod_found == False:
LOGGER.warning('Mutation "{}" not found. '
'Check target resid!'
''.format(_format_resname(resspec)))
# Ie. the target residue is chain specific
if (resspec.get('chain') is not None) and (resspec.get('chain') == chain):
mod_found = _resiter(mod, residue_graph, resspec, library, key, molecule)
if not mod_found:
LOGGER.warning('Residue specified by "{}" for mutation "{}" not found. ',
_format_resname(resspec), mod)
# If instead we're targeting residues in any chain
elif resspec.get(chain) is None:
_resiter(mod, residue_graph, resspec, library, key, molecule)

class AnnotateMutMod(Processor):
"""
Expand Down Expand Up @@ -259,3 +290,6 @@ def __init__(self, modifications=None, mutations=None):
def run_molecule(self, molecule):
annotate_modifications(molecule, self.modifications, self.mutations)
return molecule
def run_system(self, system):
pckroon marked this conversation as resolved.
Show resolved Hide resolved
super().run_system(system)

56 changes: 42 additions & 14 deletions vermouth/tests/test_annotate_mut_mod.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,38 +302,66 @@ def test_nter_cter_modifications(node_data, edge_data, expected):

assert found == expected

@pytest.mark.parametrize('node_data, edge_data, expected', [
@pytest.mark.parametrize('node_data, edge_data, mutation, expected', [
(
[
{'resname': 'GLY', 'resid': 1},
{'resname': 'ALA', 'resid': 2},
{'resname': 'ALA', 'resid': 3}
{'chain': 'A', 'resname': 'GLY', 'resid': 1},
{'chain': 'A', 'resname': 'ALA', 'resid': 2},
{'chain': 'A', 'resname': 'ALA', 'resid': 3}
],
[(0, 1), (1, 2)],
[({'resname': 'GLY', 'resid': 1, 'chain': 'A'}, 'MET')],
False
),
(
[
{'resname': 'ALA', 'resid': 1},
{'resname': 'ALA', 'resid': 2},
{'resname': 'ALA', 'resid': 3}
{'chain': 'A', 'resname': 'ALA', 'resid': 1},
{'chain': 'A', 'resname': 'ALA', 'resid': 2},
{'chain': 'A', 'resname': 'ALA', 'resid': 3}
],
[(0, 1), (1, 2)],
[({'resname': 'GLY', 'resid': 1, 'chain': 'A'}, 'MET')],
True
),
(
[
{'chain': 'A', 'resname': 'ALA', 'resid': 1},
{'chain': 'A', 'resname': 'ALA', 'resid': 2},
{'chain': 'A', 'resname': 'ALA', 'resid': 3},
{'chain': 'B', 'resname': 'ALA', 'resid': 1},
{'chain': 'B', 'resname': 'ALA', 'resid': 2},
{'chain': 'B', 'resname': 'ALA', 'resid': 3}
],
[(0, 1), (1, 2), (3, 4), (4, 5)],
[({'resname': 'ALA', 'chain': 'A'}, 'GLY')],
False
),
(
[
{'chain': 'A', 'resname': 'ALA', 'resid': 1},
{'chain': 'A', 'resname': 'ALA', 'resid': 2},
{'chain': 'A', 'resname': 'ALA', 'resid': 3},
{'chain': 'B', 'resname': 'ALA', 'resid': 1},
{'chain': 'B', 'resname': 'ALA', 'resid': 2},
{'chain': 'B', 'resname': 'ALA', 'resid': 3}
],
[(0, 1), (1, 2), (3, 4), (4, 5)],
[({'resname': 'GLY', 'chain': 'A'}, 'ALA')],
True
)])
def test_mod_resid_not_correct(caplog, node_data, edge_data, expected):
)
])
def test_mod_resid_not_correct(caplog, node_data, edge_data, mutation, expected):
"""
Tests that the modification is found in the expected residue.
"""
mol = Molecule(force_field=ForceField(FF_UNIVERSAL_TEST))
mol.add_nodes_from(enumerate(node_data))
mol.add_edges_from(edge_data)
mutation = [({'resname': 'GLY', 'resid': 1}, 'MET')]


caplog.clear()
annotate_modifications(mol, [], mutation)
if expected:
assert '"GLY1" not found.' in str(caplog.records[0].getMessage())

if expected:
assert any(rec.levelname == 'WARNING' for rec in caplog.records)
else:
assert caplog.records == []
Loading