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

fixed modifcation parsing #389

Merged
merged 8 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
fgrunewald marked this conversation as resolved.
Show resolved Hide resolved

[ 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
16 changes: 6 additions & 10 deletions polyply/src/apply_modifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,9 @@ 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]))

return protein_termini


Expand All @@ -60,7 +59,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 @@ -76,13 +75,13 @@ 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.")
if target_residue.get('from_itp'):
LOGGER.info("meta_molecule has come from itp. Will not attempt to modify.")
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 +113,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
89 changes: 71 additions & 18 deletions polyply/tests/test_apply_modifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import polyply.src.ff_parser_sub
import networkx as nx
from vermouth.molecule import Interaction
from polyply.src.meta_molecule import MetaMolecule


@pytest.mark.parametrize('input_mods, expected',(
Expand All @@ -37,8 +38,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 +74,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 +111,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 +143,69 @@ 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',
(
(
[['A1', 'N-ter']],
True
),

@pytest.mark.parametrize('adding, expected',
(
(True,
True),
(False,
False)
))
def test_from_itp(caplog, adding, expected):

caplog.set_level(logging.INFO)
#make the meta molecule from the itp and ff files
file_name = TEST_DATA / "itp" / "ALA5.itp"

ff = vermouth.forcefield.ForceField(name='martini3')

ff_lines = []
for file in ['aminoacids.ff', 'modifications.ff']:
with open(TEST_DATA/ "ff" / file) as f:
ff_lines += f.readlines()
polyply.src.ff_parser_sub.read_ff(ff_lines, ff)

meta_mol = MetaMolecule.from_itp(ff, file_name, "pALA")

if adding:
for node in meta_mol.nodes:
meta_mol.nodes[node]['from_itp'] = 'True'

termini = _patch_protein_termini(meta_mol)
apply_mod(meta_mol, termini)

found = False
expected_msg = "meta_molecule has come from itp. Will not attempt to modify."
for record in caplog.records:
if record.message == expected_msg:
found = True
break
else:
continue

assert found == expected

@pytest.mark.parametrize('modifications, expected, text',
(
(
[],
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)
Loading
Loading