Skip to content

Commit ab5eb3b

Browse files
committed
Add unit tests for mcs_kwargs and updated ring-break check.
1 parent 646fbef commit ab5eb3b

2 files changed

Lines changed: 145 additions & 16 deletions

File tree

tests/Align/test_align.py

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -674,10 +674,10 @@ def test_roi_flex_align(protein_inputs):
674674
def test_empty_custom_roi_mapping():
675675
# mut contains a proline mutation at position 15
676676
wt = BSS.IO.readMolecules(
677-
BSS.IO.expand(BSS.tutorialUrl(), f"1choFH_apo_wt_flare_processed.pdb")
677+
BSS.IO.expand(BSS.tutorialUrl(), "1choFH_apo_wt_flare_processed.pdb")
678678
)[0]
679679
mut = BSS.IO.readMolecules(
680-
BSS.IO.expand(BSS.tutorialUrl(), f"1choFH_apo_mut_flare_processed.pdb")
680+
BSS.IO.expand(BSS.tutorialUrl(), "1choFH_apo_mut_flare_processed.pdb")
681681
)[0]
682682

683683
# use the custom_roi_map to specify that residue 15 in the WT protein should be
@@ -691,15 +691,16 @@ def test_empty_custom_roi_mapping():
691691
for atom_idx in roi_res_idx:
692692
assert atom_idx not in mapping.keys()
693693

694+
694695
@pytest.mark.skipif(has_amber is False, reason="Requires AMBER to be installed.")
695696
def test_custom_roi_ring_break_merge():
696697
# wt contains a leucine at position 15
697698
# mut contains a proline at position 15
698699
wt = BSS.IO.readMolecules(
699-
BSS.IO.expand(BSS.tutorialUrl(), f"1choFH_apo_wt_flare_processed.pdb")
700+
BSS.IO.expand(BSS.tutorialUrl(), "1choFH_apo_wt_flare_processed.pdb")
700701
)[0]
701702
mut = BSS.IO.readMolecules(
702-
BSS.IO.expand(BSS.tutorialUrl(), f"1choFH_apo_mut_flare_processed.pdb")
703+
BSS.IO.expand(BSS.tutorialUrl(), "1choFH_apo_mut_flare_processed.pdb")
703704
)[0]
704705

705706
wt = BSS.Parameters.ff14SB(wt, ensure_compatible=False).getMolecule()
@@ -743,13 +744,14 @@ def test_custom_roi_ring_break_merge():
743744
assert n_bonds_created == 1
744745
assert n_bonds_annihilated == 0
745746

747+
746748
@pytest.mark.skipif(has_amber is False, reason="Requires AMBER to be installed.")
747749
def test_custom_roi_map_invalid_outside_roi():
748750
wt = BSS.IO.readMolecules(
749-
BSS.IO.expand(BSS.tutorialUrl(), f"1choFH_apo_wt_flare_processed.pdb")
751+
BSS.IO.expand(BSS.tutorialUrl(), "1choFH_apo_wt_flare_processed.pdb")
750752
)[0]
751753
mut = BSS.IO.readMolecules(
752-
BSS.IO.expand(BSS.tutorialUrl(), f"1choFH_apo_mut_flare_processed.pdb")
754+
BSS.IO.expand(BSS.tutorialUrl(), "1choFH_apo_mut_flare_processed.pdb")
753755
)[0]
754756

755757
wt = BSS.Parameters.ff14SB(wt, ensure_compatible=False).getMolecule()
@@ -761,7 +763,6 @@ def test_custom_roi_map_invalid_outside_roi():
761763
molecule0=wt,
762764
molecule1=mut,
763765
roi=[15],
764-
765766
custom_roi_map={
766767
0: 0,
767768
1: 1,
@@ -1313,9 +1314,9 @@ def test_ring_breaking_cross_bond_cleanup():
13131314
mol_info.atom_idx(p.atom3()).value(),
13141315
}
13151316
for a, b in changing:
1316-
assert not (
1317-
a in atoms and b in atoms
1318-
), f"improper{suffix} spans absent bond ({a},{b})"
1317+
assert not (a in atoms and b in atoms), (
1318+
f"improper{suffix} spans absent bond ({a},{b})"
1319+
)
13191320

13201321
# Check that the ring-breaking and ring-making bond properties are set.
13211322
def _read_pairs(prop_name):
@@ -1326,9 +1327,73 @@ def _read_pairs(prop_name):
13261327

13271328
stored_breaking = _read_pairs("ring_breaking_bonds")
13281329
stored_making = _read_pairs("ring_making_bonds")
1329-
assert (
1330-
stored_breaking == ring_breaking
1331-
), f"ring_breaking_bonds property mismatch: {stored_breaking} != {ring_breaking}"
1332-
assert (
1333-
stored_making == ring_making
1334-
), f"ring_making_bonds property mismatch: {stored_making} != {ring_making}"
1330+
assert stored_breaking == ring_breaking, (
1331+
f"ring_breaking_bonds property mismatch: {stored_breaking} != {ring_breaking}"
1332+
)
1333+
assert stored_making == ring_making, (
1334+
f"ring_making_bonds property mismatch: {stored_making} != {ring_making}"
1335+
)
1336+
1337+
1338+
@pytest.fixture(scope="session")
1339+
def ejm31():
1340+
return BSS.IO.readMolecules(
1341+
[f"{url}/lig_ejm31.prm7.bz2", f"{url}/lig_ejm31.rst7.bz2"]
1342+
).getMolecules()[0]
1343+
1344+
1345+
@pytest.fixture(scope="session")
1346+
def jmc28():
1347+
return BSS.IO.readMolecules(
1348+
[f"{url}/lig_jmc28.prm7.bz2", f"{url}/lig_jmc28.rst7.bz2"]
1349+
).getMolecules()[0]
1350+
1351+
1352+
def test_default_mcs_options():
1353+
# The MCS defaults should be discoverable, and ring matching is on.
1354+
options = BSS.Align.defaultMCSOptions()
1355+
assert options["ringMatchesRingOnly"] is True
1356+
assert options["completeRingsOnly"] is True
1357+
1358+
# The returned dictionary is a copy, so mutating it has no side effects.
1359+
options["ringMatchesRingOnly"] = False
1360+
assert BSS.Align.defaultMCSOptions()["ringMatchesRingOnly"] is True
1361+
1362+
1363+
def test_mcs_kwargs_ring_matches_ring_only(ejm31, jmc28):
1364+
# Perturbing a methyl to a 2-methylcyclopropyl. Atom 19 is the methyl
1365+
# carbon in ejm31 and the ring carbon bonded to the carbonyl in jmc28.
1366+
1367+
# By default an acyclic atom can't map onto a ring atom, so the whole
1368+
# substituent is unmapped.
1369+
mapping = BSS.Align.matchAtoms(ejm31, jmc28)
1370+
assert 19 not in mapping
1371+
1372+
# Allowing the match maps the two carbons onto each other, along with one
1373+
# of the methyl hydrogens.
1374+
mapping = BSS.Align.matchAtoms(
1375+
ejm31, jmc28, mcs_kwargs={"ringMatchesRingOnly": False}
1376+
)
1377+
assert mapping[19] == 19
1378+
assert len(mapping) == 30
1379+
1380+
# Only two hydrogens are removed and the ring is grown from dummy atoms.
1381+
assert sorted(set(range(32)) - set(mapping)) == [27, 28]
1382+
1383+
1384+
def test_mcs_kwargs_merge(ejm31, jmc28):
1385+
# The options are used when merge autogenerates a mapping.
1386+
merged = BSS.Align.merge(ejm31, jmc28, mcs_kwargs={"ringMatchesRingOnly": False})
1387+
sire_mol = merged._sire_object
1388+
1389+
# A ring grown entirely from dummy atoms breaks no bond between mapped
1390+
# atoms, so the merge doesn't require 'allow_ring_breaking' and the end
1391+
# states have the same number of bonds.
1392+
assert sire_mol.num_atoms() == 41
1393+
assert len(sire_mol.property("bond0").potentials()) == len(
1394+
sire_mol.property("bond1").potentials()
1395+
)
1396+
1397+
# No ring is broken or made, so neither property is set.
1398+
assert not sire_mol.has_property("ring_breaking_bonds")
1399+
assert not sire_mol.has_property("ring_making_bonds")

tests/Sandpit/Exscientia/Align/test_align.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,3 +904,67 @@ def test_ring_opening_and_size_change(ligands, mapping):
904904
BSS.Align.merge(
905905
m0, m1, mapping, allow_ring_breaking=True, allow_ring_size_change=True
906906
)
907+
908+
909+
@pytest.fixture(scope="session")
910+
def ejm31():
911+
return BSS.IO.readMolecules(
912+
[f"{url}/lig_ejm31.prm7.bz2", f"{url}/lig_ejm31.rst7.bz2"]
913+
).getMolecules()[0]
914+
915+
916+
@pytest.fixture(scope="session")
917+
def jmc28():
918+
return BSS.IO.readMolecules(
919+
[f"{url}/lig_jmc28.prm7.bz2", f"{url}/lig_jmc28.rst7.bz2"]
920+
).getMolecules()[0]
921+
922+
923+
def test_default_mcs_options():
924+
# The MCS defaults should be discoverable, and ring matching is on.
925+
options = BSS.Align.defaultMCSOptions()
926+
assert options["ringMatchesRingOnly"] is True
927+
assert options["completeRingsOnly"] is True
928+
929+
# The returned dictionary is a copy, so mutating it has no side effects.
930+
options["ringMatchesRingOnly"] = False
931+
assert BSS.Align.defaultMCSOptions()["ringMatchesRingOnly"] is True
932+
933+
934+
def test_mcs_kwargs_ring_matches_ring_only(ejm31, jmc28):
935+
# Perturbing a methyl to a 2-methylcyclopropyl. Atom 19 is the methyl
936+
# carbon in ejm31 and the ring carbon bonded to the carbonyl in jmc28.
937+
938+
# By default an acyclic atom can't map onto a ring atom, so the whole
939+
# substituent is unmapped.
940+
mapping = BSS.Align.matchAtoms(ejm31, jmc28)
941+
assert 19 not in mapping
942+
943+
# Allowing the match maps the two carbons onto each other, along with one
944+
# of the methyl hydrogens.
945+
mapping = BSS.Align.matchAtoms(
946+
ejm31, jmc28, mcs_kwargs={"ringMatchesRingOnly": False}
947+
)
948+
assert mapping[19] == 19
949+
assert len(mapping) == 30
950+
951+
# Only two hydrogens are removed and the ring is grown from dummy atoms.
952+
assert sorted(set(range(32)) - set(mapping)) == [27, 28]
953+
954+
955+
def test_mcs_kwargs_merge(ejm31, jmc28):
956+
# The options are used when merge autogenerates a mapping.
957+
merged = BSS.Align.merge(ejm31, jmc28, mcs_kwargs={"ringMatchesRingOnly": False})
958+
sire_mol = merged._sire_object
959+
960+
# A ring grown entirely from dummy atoms breaks no bond between mapped
961+
# atoms, so the merge doesn't require 'allow_ring_breaking' and the end
962+
# states have the same number of bonds.
963+
assert sire_mol.num_atoms() == 41
964+
assert len(sire_mol.property("bond0").potentials()) == len(
965+
sire_mol.property("bond1").potentials()
966+
)
967+
968+
# No ring is broken or made, so neither property is set.
969+
assert not sire_mol.has_property("ring_breaking_bonds")
970+
assert not sire_mol.has_property("ring_making_bonds")

0 commit comments

Comments
 (0)