From aef6b5a811ec29743f97b7b97f3a0ef28378dfc5 Mon Sep 17 00:00:00 2001 From: Nuri Jung Date: Wed, 22 May 2024 16:29:52 +0900 Subject: [PATCH 1/4] feat(python/core): add has_bond() interface --- python/src/nuri/core/molecule.cpp | 28 +++++++++++++++++++++++++++ python/src/nuri/core/substructure.cpp | 28 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/python/src/nuri/core/molecule.cpp b/python/src/nuri/core/molecule.cpp index 4aea6258..51d7b0f6 100644 --- a/python/src/nuri/core/molecule.cpp +++ b/python/src/nuri/core/molecule.cpp @@ -617,6 +617,34 @@ Get a bond of the molecule. ``src`` and ``dst`` are interchangeable. The returned bond is invalidated when a mutator context is exited. If the bond must be kept alive, copy the bond data first with :meth:`Bond.copy_data` method. +)doc") + .def( + "has_bond", + [](PyMol &self, int src, int dst) { + std::tie(src, dst) = check_bond_ends(*self, src, dst); + return self->find_bond(src, dst) != self->bond_end(); + }, + py::arg("src"), py::arg("dst"), R"doc( +Check if two atoms are connected by a bond. + +:param src: The source atom of the bond. +:param dst: The destination atom of the bond. +:returns: Whether the source and destination atoms are connected by a bond. +:raises IndexError: If the source or destination atom does not exist. +)doc") + .def( + "has_bond", + [](PyMol &self, PyAtom &src, PyAtom &dst) { + auto [sa, da] = check_bond_ends(*self, src, dst); + return self->find_bond(sa, da) != self->bond_end(); + }, + py::arg("src"), py::arg("dst"), R"doc( +Check if two atoms are connected by a bond. + +:param src: The source atom of the bond. +:param dst: The destination atom of the bond. +:returns: Whether the source and destination atoms are connected by a bond. +:raises ValueError: If any of the atoms does not belong to the molecule. )doc") .def( "num_bonds", [](PyMol &self) { return self->num_bonds(); }, R"doc( diff --git a/python/src/nuri/core/substructure.cpp b/python/src/nuri/core/substructure.cpp index ef0ca2be..312bfc3a 100644 --- a/python/src/nuri/core/substructure.cpp +++ b/python/src/nuri/core/substructure.cpp @@ -430,6 +430,34 @@ Get a bond of the substructure. ``src`` and ``dst`` are interchangeable. The returned bond is invalidated when the parent molecule is modified, or if the substructure is modified. If the bond must be kept alive, copy the bond data first. +)doc"); + sub.def( + "has_bond", + [](P &self, PySubAtom

&src, PySubAtom

&dst) { + auto [sa, da] = check_subbond_ends(*self, src, dst); + return self->find_bond(sa, da) != self->bond_end(); + }, + py::arg("src"), py::arg("dst"), R"doc( +Check if two atoms are connected by a bond. + +:param src: The source sub-atom of the bond. +:param dst: The destination sub-atom of the bond. +:returns: Whether the source and destination atoms are connected by a bond. +:raises ValueError: If any of the sub-atoms does not belong to the substructure. +)doc"); + sub.def( + "has_bond", + [](P &self, PyAtom &src, PyAtom &dst) { + auto [sa, da] = check_bond_ends(*self.parent(), src, dst); + return self->find_bond(sa, da) != self->bond_end(); + }, + py::arg("src"), py::arg("dst"), R"doc( +Check if two atoms are connected by a bond. + +:param src: The source atom of the bond. +:param dst: The destination atom of the bond. +:returns: Whether the source and destination atoms are connected by a bond. +:raises ValueError: If any of the atoms does not belong to the molecule. )doc"); sub.def( "num_bonds", [](P &self) { return self->num_bonds(); }, From de1ac9f8e42155460e61eb8126505d4df3fdb08c Mon Sep 17 00:00:00 2001 From: Nuri Jung Date: Wed, 22 May 2024 16:30:19 +0900 Subject: [PATCH 2/4] test(python/core): add tests for has_bond() interface --- python/test/core/molecule_test.py | 5 +++++ python/test/core/substructure_test.py | 3 +++ 2 files changed, 8 insertions(+) diff --git a/python/test/core/molecule_test.py b/python/test/core/molecule_test.py index 81b9b908..279081b1 100644 --- a/python/test/core/molecule_test.py +++ b/python/test/core/molecule_test.py @@ -153,6 +153,8 @@ def test_add_bond(): assert {bond.src.id, bond.dst.id} == {0, 1} assert bond.src.atomic_number == 6 assert bond.dst.atomic_number == 6 + assert mol.has_bond(0, 1) + assert mol.has_bond(bond.src, bond.dst) i = -1 for i, bond in enumerate(mol.bonds(), 1): @@ -165,6 +167,9 @@ def test_add_bond(): a3 = mut.add_atom(6) a4 = mut.add_atom(6) + assert not mol.has_bond(a3.id, a4.id) + assert not mol.has_bond(a3, a4) + mut.add_bond(a3, a4, BondData(BondOrder.Triple)) mut.add_bond(1, 3, BondData(BondOrder.Aromatic)) diff --git a/python/test/core/substructure_test.py b/python/test/core/substructure_test.py index 7d80f7c3..2a63c483 100644 --- a/python/test/core/substructure_test.py +++ b/python/test/core/substructure_test.py @@ -211,14 +211,17 @@ def test_neighbors(molsub: Molecule): def test_find_bond(molsub: Molecule): sub = molsub.subs[0] + assert not sub.has_bond(sub[0], sub[2]) with pytest.raises(ValueError, match="no such bond"): sub.bond(sub[0], sub[2]) bond_01 = sub.bond(sub[0], sub[1]) bond_10 = sub.bond(sub[1], sub[0]) + assert sub.has_bond(sub[0], sub[1]) bond_01_atoms = sub.bond(sub[0].as_parent(), sub[1].as_parent()) bond_10_atoms = sub.bond(sub[1].as_parent(), sub[0].as_parent()) + assert sub.has_bond(sub[0].as_parent(), sub[1].as_parent()) assert bond_01.src.id == bond_10.src.id assert bond_01.dst.id == bond_10.dst.id From 1d60fe3e98d8e19d766e2ef12b47eea2941190b6 Mon Sep 17 00:00:00 2001 From: Nuri Jung Date: Wed, 22 May 2024 16:30:42 +0900 Subject: [PATCH 3/4] docs(python/core): document has_bond() method --- python/docs/nuri.core.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/python/docs/nuri.core.rst b/python/docs/nuri.core.rst index eb92bc51..015efcda 100644 --- a/python/docs/nuri.core.rst +++ b/python/docs/nuri.core.rst @@ -18,6 +18,7 @@ nuri.core .. automethod:: num_atoms .. automethod:: bonds .. automethod:: bond + .. automethod:: has_bond .. automethod:: num_bonds .. automethod:: neighbor .. automethod:: mutator From bfe9f401dbb8bbce69de54b13a070d6eb3cf0e78 Mon Sep 17 00:00:00 2001 From: Nuri Jung Date: Wed, 22 May 2024 16:36:02 +0900 Subject: [PATCH 4/4] refactor(python/core): remove unnecessary return statement --- python/src/nuri/core/molecule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/nuri/core/molecule.cpp b/python/src/nuri/core/molecule.cpp index 51d7b0f6..9db7433d 100644 --- a/python/src/nuri/core/molecule.cpp +++ b/python/src/nuri/core/molecule.cpp @@ -837,7 +837,7 @@ Remove a conformation from the molecule. Get the number of conformations of the molecule. )doc") .def( - "clear_confs", [](PyMol &self) { return self->confs().clear(); }, + "clear_confs", [](PyMol &self) { self->confs().clear(); }, R"doc( Remove all conformations from the molecule. )doc")