Skip to content

Commit

Permalink
fixed modifcation parsing
Browse files Browse the repository at this point in the history
- -mods flag now only read once for all modifications
- patch all molecule termini as proteins, so any modification in addition to these ones are also applied.
- add more modifications from vermouth to martini3 forcefield
- change logging level and fix tests as per #385
  • Loading branch information
csbrasnett committed Oct 8, 2024
1 parent 8d1cab6 commit 8c2792a
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 30 deletions.
6 changes: 3 additions & 3 deletions bin/polyply
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ def main(): # pylint: disable=too-many-locals,too-many-statements
help='A linear sequence of residue names.')
seq_group.add_argument('-seqf', dest='seq_file', type=Path,
help='A graph input file (JSON|TXT|FASTA|IG)')
dna_group = parser_gen_itp.add_argument_group('DNA specifc options')
dna_group = parser_gen_itp.add_argument_group('DNA specific options')
dna_group.add_argument('-dsdna', dest='dsdna', action='store_true',
help='complement single sequence to dsDNA sequence')
modifications_group = parser_gen_itp.add_argument_group('Modifications group')
modifications_group.add_argument('-mods', dest='mods', action='append',
default=[], type=lambda s: s.split(":"),
modifications_group.add_argument('-mods', dest='mods', nargs='+',
default=[], type=lambda s: [i.split(':') for i in s.split(" ")][0],
help=('Add a modification to a residue. The format is '
'<resname><resid>:modification_name, '
'e.g. ASP1:N-ter')
Expand Down
69 changes: 69 additions & 0 deletions polyply/data/martini3/modifications.ff
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,72 @@ BB {"replace": {"atype": "P1", "charge": 0.0}}
NCAP-ter
[ atoms ]
BB {"replace": {"atype": "P2", "charge": 0.0}}

[ modification ]
GLU-HE1
[ atoms ]
SC1 {"replace": {"atype": "P2", "charge": 0}}

[ modification ]
GLU-HE2
[ atoms ]
SC1 {"replace": {"atype": "P2", "charge": 0}}

[ modification ]
ASP-HD1
[ atoms ]
SC1 {"replace": {"atype": "P2", "charge": 0}}

[ modification ]
ASP-HD2
[ atoms ]
SC1 {"replace": {"atype": "P2", "charge": 0}}

[ modification ]
LYS-LSN
[ atoms ]
SC2 {"resname": "LYS", "replace": {"atype": "SN6d", "charge": 0}}

[ modification ]
LYS-HZ3
[ atoms ]
SC2 {"replace": {"resname": "LYS"}}

[ modification ]
HIS-HP
[ atoms ]
BB {"resname": "HIS", "replace": {"resname": "HIS"}}
SC1 {"resname": "HIS", "replace": {"resname": "HIS"}}
SC2 {"resname": "HIS", "replace": {"atype": "TP1dq", "charge": "0.5", "resname": "HIS"}}
SC3 {"resname": "HIS", "replace": {"atype": "TP1dq", "charge": "0.5", "resname": "HIS"}}
;[ edges ]
;BB SC1
;SC1 SC2
;SC1 SC3
;SC2 SC3

[ modification ]
HIS-HE
[ atoms ]
BB {"resname": "HIS", "replace": {"resname": "HIS"}}
SC1 {"resname": "HIS", "replace": {"resname": "HIS"}}
SC2 {"resname": "HIS", "replace": {"resname": "HIS"}}
SC3 {"resname": "HIS", "replace": {"resname": "HIS"}}
;[ edges ]
;BB SC1
;SC1 SC2
;SC1 SC3
;SC2 SC3

[ modification ]
HIS-HD
[ atoms ]
BB {"resname": "HIS", "replace": {"resname": "HIS"}}
SC1 {"resname": "HIS", "replace": {"resname": "HIS"}}
SC2 {"resname": "HIS", "replace": {"resname": "HIS", "atype": "TN5a"}}
SC3 {"resname": "HIS", "replace": {"resname": "HIS", "atype": "TN6a"}}
;[ edges ]
;BB SC1
;SC1 SC2
;SC1 SC3
;SC2 SC3
15 changes: 6 additions & 9 deletions polyply/src/apply_modifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ def _patch_protein_termini(meta_molecule, ter_mods=['N-ter', 'C-ter']):
protein_termini.append(last_mod)
else:
# if only one mod in ter_mods, apply the mod to both start and end residue
LOGGER.info("Only one terminal modification specified. "
LOGGER.warning("Only one terminal modification specified. "
f"Will apply {ter_mods[0]} to both {meta_molecule.nodes[0]['resname']}1 and {last_resname}{max_resid}")
protein_termini.append(({'resid': max_resid, 'resname': last_resname}, ter_mods[0]))

print(protein_termini)
return protein_termini


Expand All @@ -60,7 +60,7 @@ def apply_mod(meta_molecule, modifications):
molecule = meta_molecule.molecule

if not molecule.force_field.modifications:
LOGGER.warning('No modifications present in forcefield, none will be applied')
LOGGER.info('No modifications present in forcefield, none will be applied')
return meta_molecule

for target, desired_mod in modifications:
Expand All @@ -77,12 +77,12 @@ def apply_mod(meta_molecule, modifications):
target_residue = meta_molecule.nodes[target_resid - 1]
# takes care to skip all residues that come from an itp file
if not target_residue.get('from_itp', 'False'):
LOGGER.warning("meta_molecule has come from itp. Will not attempt to modify.")
LOGGER.info("meta_molecule has come from itp. Will not attempt to modify.")

Check warning on line 80 in polyply/src/apply_modifications.py

View check run for this annotation

Codecov / codecov/patch

polyply/src/apply_modifications.py#L80

Added line #L80 was not covered by tests
continue
# checks that the resname is a protein resname as defined above
if not vermouth.molecule.attributes_match(target_residue,
{'resname': vermouth.molecule.Choice(protein_resnames.split("|"))}):
LOGGER.warning("The resname of your target residue is not recognised a protein resname. "
LOGGER.info("The resname of your target residue is not recognised as a protein resname. "
"Will not attempt to modify.")
continue

Expand Down Expand Up @@ -114,13 +114,10 @@ class ApplyModifications(Processor):
"""
def __init__(self, meta_molecule, modifications=[]):
self.target_mods = []
self.target_mods = _patch_protein_termini(meta_molecule)
for resspec, val in modifications:
self.target_mods.append((parse_residue_spec(resspec), val))
if len(self.target_mods) == 0:
self.target_mods = _patch_protein_termini(meta_molecule)

def run_molecule(self, meta_molecule):

apply_mod(meta_molecule, self.target_mods)
return meta_molecule
46 changes: 28 additions & 18 deletions polyply/tests/test_apply_modifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
[({'resid': 1, 'resname': 'A'}, 'Zwitter'),
({'resid': 3, 'resname': 'A'}, 'Zwitter')]
)
))
def test_annotate_protein(example_meta_molecule, input_mods, expected):
""""
Expand Down Expand Up @@ -75,20 +73,25 @@ def test_mods_in_ff(caplog, ff_files, expected):
with expected:
assert len(ff.modifications) > 0

@pytest.mark.parametrize('input_itp, molname, expected',
@pytest.mark.parametrize('input_itp, molname, expected, text',
(
(
'ALA5.itp',
'pALA',
False),
False,
None),
('PEO.itp',
'PEO',
True)
))
def test_apply_mod(input_itp, molname, expected, caplog):
True,
("The resname of your target residue"
" is not recognised as a protein resname."
" Will not attempt to modify."))
))
def test_apply_mod(input_itp, molname, expected, caplog, text):
"""
test that modifications get applied correctly
"""
caplog.set_level(logging.INFO)
#make the meta molecule from the itp and ff files
file_name = TEST_DATA / "itp" / input_itp

Expand All @@ -107,7 +110,12 @@ def test_apply_mod(input_itp, molname, expected, caplog):
apply_mod(meta_mol, termini)

if expected:
assert any(rec.levelname == 'WARNING' for rec in caplog.records)
for record in caplog.records:
if record.message == text:
assert True
break
else:
assert False

else:
#for each mod applied, check that the mod atom and interactions have been changed correctly
Expand All @@ -134,25 +142,27 @@ def test_apply_mod(input_itp, molname, expected, caplog):
meta=interaction.meta)
assert _interaction in meta_mol.molecule.interactions[interaction_type]

@pytest.mark.parametrize('modifications, expected',
@pytest.mark.parametrize('modifications, expected, text',
(
(
[['A1', 'N-ter']],
True
),
(
[],
True
True,
"No modifications present in forcefield, none will be applied"
),
))
def test_ApplyModifications(example_meta_molecule, caplog, modifications, expected):
def test_ApplyModifications(example_meta_molecule, caplog, modifications, expected, text):

caplog.set_level(logging.INFO)

ApplyModifications(modifications=modifications,
meta_molecule=example_meta_molecule).run_molecule(example_meta_molecule)

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

for record in caplog.records:
if record.message == text:
assert True
break
else:
assert False
assert any(rec.levelname == 'INFO' for rec in caplog.records)

0 comments on commit 8c2792a

Please sign in to comment.