Skip to content

Commit 5644876

Browse files
committed
[cli.interface] new optarg AA_selection, tests
1 parent 8c7053a commit 5644876

File tree

2 files changed

+131
-7
lines changed

2 files changed

+131
-7
lines changed

mdciao/cli/cli.py

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ def interface(
11031103
topology=None,
11041104
frag_idxs_group_1=None,
11051105
frag_idxs_group_2=None,
1106+
AA_selection=None,
11061107
GPCR_UniProt="None",
11071108
CGN_UniProt="None",
11081109
KLIFS_string=None,
@@ -1161,26 +1162,27 @@ def interface(
11611162
in a receptor--G-protein complex, one partner is
11621163
the receptor and the other partner is the G-protein.
11631164
1164-
By default, mdciao.cli.interface doesn't allow interface
1165-
members to share residues. However, sometimes it's
1165+
This is why mdciao.cli.interface doesn't allow interface
1166+
members to share residues by default. However, sometimes it's
11661167
useful to allow it because the contacts of one fragment
1167-
with itself (the self-contacts) are also important.
1168-
E.g. the C-terminus of a receptor interfacing with
1169-
the entire receptor, **including the C-terminus**.
1168+
with itself are also important. E.g. the
1169+
C-terminus of a receptor interfacing with
1170+
the entire receptor, **including the C-terminus itself**.
11701171
To allow for this behaviour, use `self_interface` = True,
11711172
and possibly increase `n_nearest`, since otherwise
11721173
neighboring residues of the shared set (e.g. C-terminus)
11731174
will always appear as formed.
11741175
11751176
Finally, the interface strength, defined as the
11761177
per-residue sum of contacts participating in
1177-
the interface, is written as the `bfactor <http://www.wwpdb.org/documentation/file-format-content/format33/sect9.html#ATOM>`_
1178+
the interface, is written as the
1179+
`bfactor <http://www.wwpdb.org/documentation/file-format-content/format33/sect9.html#ATOM>`_
11781180
in a .pdb file called (for the default `ctc_cutoff_Ang`=4)
11791181
'[email protected]_Ang.as_bfactors.pdb'. You
11801182
can see an example of how to use this file (e.g. with
11811183
VMD) in the online documentation. The structures, i.e.
11821184
frames, in that .pdb-file are chosen using the
1183-
method :obj:`mdciao.contacts.ContactGroup.n_repframes`.
1185+
method :obj:`mdciao.contacts.ContactGroup.repframes` .
11841186
See below the parameter `n_repframes` for more info.
11851187
11861188
Parameters
@@ -1216,6 +1218,37 @@ def interface(
12161218
Defaults to None which will prompt the user of
12171219
information, except when only two fragments are
12181220
present. Then it defaults to [1]
1221+
AA_selection : str or list, default is None
1222+
Whatever the fragment definition and fragment selection
1223+
has been, one can further refine the list of
1224+
potential residue pairs by making a per aminoacid (AA)
1225+
selection here. E.g., if one has selected the interface
1226+
to be "TM3" vs "TM2", but wants to select only some
1227+
regions of those helices, one can pass here an `AA_selection`.
1228+
This can be a string or a list of len two:
1229+
1230+
* A string leads to a boolean "or" selection, i.e. keep
1231+
residue pair [ii,jj] if either ii **or** jj
1232+
match `AA_selection`. E.g.
1233+
1234+
>>> AA_selection = "3.45-3.55"
1235+
1236+
is equivalent of "3.45-3.55" vs "TM2" contacts
1237+
* A list of len two leads to a boolean "and" selection, i.e. keep
1238+
residue pair [ii,jj] if ii **and** jj
1239+
match `AA_selection`. E.g.
1240+
1241+
>>> AA_selection = ["3.45-3.55","2.45-2.55"]
1242+
1243+
is equivalent of "3.45-3.55" vs "2.45-2.55" contacts
1244+
1245+
In principle, one could use
1246+
1247+
>>> fragments = ["3.45-3.55","2.45-2.55"]
1248+
1249+
and get the same contacts, but this would then exclude all other
1250+
residues of the topology from being tagged with fragment
1251+
and or consensus labels.
12191252
GPCR_UniProt : str or :obj:`mdciao.nomenclature.LabelerGPCR`, default is None
12201253
For GPCR nomenclature. If str, e.g. "adrb2_human".
12211254
will try to locate a local filename or do a web lookup in the GPCRdb.
@@ -1481,6 +1514,25 @@ def interface(
14811514
print("\nWill look for contacts in the interface between fragments\n%s\nand\n%s. "%
14821515
('\n'.join(_twrap(', '.join(['%s' % gg for gg in intf_frags_as_str_or_keys[0]]))),
14831516
'\n'.join(_twrap(', '.join(['%s' % gg for gg in intf_frags_as_str_or_keys[1]])))))
1517+
1518+
# Sub-select at the AA-level #TODO consider making method out of this
1519+
if AA_selection is not None:
1520+
if isinstance(AA_selection, str):
1521+
lambda_sel = lambda pair, sel: _np.in1d(pair, sel).any()
1522+
elif isinstance(AA_selection, list) and len(AA_selection)==2:
1523+
lambda_sel = lambda pair, sel: _np.in1d(pair, sel).all()
1524+
AA_selection = ",".join(AA_selection)
1525+
else:
1526+
raise ValueError(f"'AA_selection' as to be a sting or a list of len 2, "
1527+
f"but your input is a {type(AA_selection).__name__} of len {len(AA_selection)}.")
1528+
sel = _mdcu.residue_and_atom.rangeexpand_residues2residxs(AA_selection,
1529+
fragments_as_residue_idxs,
1530+
refgeom.top,
1531+
fragment_names=fragment_names,
1532+
additional_resnaming_dicts=consensus_maps)
1533+
print(f"Excluding residue pairs not involving residues '{AA_selection}' ({len(sel)} AAs).")
1534+
ctc_idxs = [pair for pair in ctc_idxs if lambda_sel(pair, sel)]
1535+
14841536
print(f"Performing a first pass on the {len(ctc_idxs)} group_1-group_2 residue pairs to compute lower bounds "
14851537
f"on residue-residue distances via residue-COM distances.")
14861538
lb_cutoff_buffer_Ang = 2.5

tests/test_cli.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,78 @@ def test_w_nomenclature_CGN_GPCR_fragments_are_consensus_and_flareplot_and_self_
612612
self_interface=True,
613613
)
614614

615+
def test_w_nomenclature_CGN_GPCR_fragments_are_consensus_and_flareplot_and_AA_selection_OR(self):
616+
with TemporaryDirectory(suffix='_test_mdciao') as tmpdir:
617+
shutil.copy(test_filenames.gnas2_human_xlsx, tmpdir)
618+
shutil.copy(test_filenames.adrb2_human_xlsx, tmpdir)
619+
with remember_cwd():
620+
os.chdir(tmpdir)
621+
intf = cli.interface([self.traj, self.traj_reverse],
622+
self.geom,
623+
ctc_cutoff_Ang=5,
624+
n_nearest=4,
625+
output_dir=tmpdir,
626+
fragments=["consensus"],
627+
CGN_UniProt="gnas2_human",
628+
GPCR_UniProt="adrb2_human",
629+
accept_guess=True,
630+
frag_idxs_group_1='TM6',
631+
frag_idxs_group_2='TM5',
632+
self_interface=True,
633+
AA_selection="5.50x50-5.55x55"
634+
)
635+
TM5 = ["5.50x50", "5.51x51", "5.52x52", "5.53x53", "5.54x54", "5.55x55"]
636+
assert all ([lab[1] in TM5 for lab in intf.consensus_labels]), intf.consensus_labels
637+
_plt.close("all")
638+
639+
def test_w_nomenclature_CGN_GPCR_fragments_are_consensus_and_flareplot_and_AA_selection_AND(self):
640+
with TemporaryDirectory(suffix='_test_mdciao') as tmpdir:
641+
shutil.copy(test_filenames.gnas2_human_xlsx, tmpdir)
642+
shutil.copy(test_filenames.adrb2_human_xlsx, tmpdir)
643+
with remember_cwd():
644+
intf = cli.interface([self.traj, self.traj_reverse],
645+
self.geom,
646+
ctc_cutoff_Ang=5,
647+
n_nearest=4,
648+
output_dir=tmpdir,
649+
fragments=["consensus"],
650+
CGN_UniProt="gnas2_human",
651+
GPCR_UniProt="adrb2_human",
652+
accept_guess=True,
653+
frag_idxs_group_1='TM6',
654+
frag_idxs_group_2='TM5',
655+
self_interface=True,
656+
AA_selection=["5.50x50-5.55x55", "6.45x45,6.49x49"]
657+
)
658+
TM5 = ["5.50x50", "5.51x51", "5.52x52", "5.53x53", "5.54x54", "5.55x55"]
659+
assert all ([lab[1] in TM5 for lab in intf.consensus_labels]), intf.consensus_labels
660+
assert all ([lab[0] in ["6.45x45","6.49x49"] for lab in intf.consensus_labels]), intf.consensus_labels
661+
_plt.close("all")
662+
663+
def test_w_nomenclature_CGN_GPCR_fragments_are_consensus_and_flareplot_and_AA_selection_raises(self):
664+
with TemporaryDirectory(suffix='_test_mdciao') as tmpdir:
665+
shutil.copy(test_filenames.gnas2_human_xlsx, tmpdir)
666+
shutil.copy(test_filenames.adrb2_human_xlsx, tmpdir)
667+
with remember_cwd():
668+
with self.assertRaises(ValueError):
669+
intf = cli.interface([self.traj, self.traj_reverse],
670+
self.geom,
671+
ctc_cutoff_Ang=5,
672+
n_nearest=4,
673+
output_dir=tmpdir,
674+
fragments=["consensus"],
675+
CGN_UniProt="gnas2_human",
676+
GPCR_UniProt="adrb2_human",
677+
accept_guess=True,
678+
frag_idxs_group_1='TM6',
679+
frag_idxs_group_2='TM5',
680+
self_interface=True,
681+
AA_selection=["5.50x50-5.55x55,6.45x45,6.49x49"]
682+
)
683+
684+
685+
686+
615687
class Test_pdb(TestCLTBaseClass):
616688

617689
def test_works(self):

0 commit comments

Comments
 (0)