Skip to content

Commit

Permalink
Merge pull request #322 from seoklab/jnooree/issue-321
Browse files Browse the repository at this point in the history
feat(python/core): add has_bond() interface
  • Loading branch information
jnooree authored May 22, 2024
2 parents fbe0d22 + bfe9f40 commit 60f076b
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 1 deletion.
1 change: 1 addition & 0 deletions python/docs/nuri.core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ nuri.core
.. automethod:: num_atoms
.. automethod:: bonds
.. automethod:: bond
.. automethod:: has_bond
.. automethod:: num_bonds
.. automethod:: neighbor
.. automethod:: mutator
Expand Down
30 changes: 29 additions & 1 deletion python/src/nuri/core/molecule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -809,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")
Expand Down
28 changes: 28 additions & 0 deletions python/src/nuri/core/substructure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<P> &src, PySubAtom<P> &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(); },
Expand Down
5 changes: 5 additions & 0 deletions python/test/core/molecule_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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))

Expand Down
3 changes: 3 additions & 0 deletions python/test/core/substructure_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 60f076b

Please sign in to comment.