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

Bond completion (or saturation) #202

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
71 changes: 70 additions & 1 deletion sisl/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from .utils.mathematics import fnorm
from .quaternion import Quaternion
from .supercell import SuperCell, SuperCellChild
from .atom import Atom, Atoms
from .atom import Atom, Atoms, PeriodicTable
from .shape import Shape, Sphere, Cube
from ._namedindex import NamedIndex

Expand Down Expand Up @@ -3066,6 +3066,75 @@ def bond_correct(self, ia, atom, method='calc'):
raise NotImplementedError(
'Changing bond-length dependent on several lacks implementation.')

def bond_completion(self, nbonds, bond=None, new_bond=None, atom=None, idx=None):
""" Return a new geometry with additional atoms added to complete the number of bonds

This function may be useful to saturate dangling bonds at edges in sp2-carbon structures,
e.g., with H-atoms.

Parameters
----------
nbonds : int
number of bonds requested
bond, float, optional
distance between existing atoms to be considered a bond. Defaults to value from `PeriodicTable`
new_bond, float, optional
bond length to added atom. Defaults to value from `PeriodicTable`
atom : `Atom`, optional
kind of atom to be added, where missing. Defaults to atoms of the same type.
idx : array_like, optional
List of indices for atoms that are to be considered

Examples
--------
>>> g = geom.graphene(orthogonal=True).tile(3, 0).tile(4, 1)
>>> g.cell[0] *= 2
>>> g.bond_completion(3, bond=1.42, new_bond=1.09, atom=Atom(1))
"""

if idx is not None:
if not isndarray(idx):
selection = _a.asarrayi(idx).ravel()
else:
selection = range(len(self))

new_xyz = []
new_atom = []

PT = PeriodicTable()

for ia in selection:
a = self.atoms[ia]
# Determine radius of sphere for bond search
if bond is None:
ria = 0.1 + 2 * PT.radius(a.Z)
else:
ria = 1e-4 + bond
idx = self.close(ia, R=(0.1, ria), ret_xyz=True)
# We just need second-shell coordinates
xyz = idx[1][1]
if len(xyz) == nbonds - 1:
# Compute bond vector
bvec = len(xyz) * self.xyz[ia] - np.sum(xyz, axis=0)
bnorm = bvec.dot(bvec) ** .5
if bnorm > 0.1:
# only add to geometry if new position is away from ia-atom
if atom is None:
new_a = Atom(a.Z, R=a.R)
else:
new_a = atom
if new_bond is None:
bond_length = PT.radius(a.Z) + PT.radius(new_a.Z)
else:
bond_length = new_bond
bvec *= bond_length / bnorm
new_xyz.append(self.xyz[ia] + bvec)
new_atom.append(new_a)
if len(new_xyz) > 0:
return self.add(self.__class__(new_xyz, new_atom))
else:
return self.copy()

def within(self, shapes,
idx=None, idx_xyz=None,
ret_xyz=False, ret_rij=False):
Expand Down