sisl
sisl copied to clipboard
Modifying the returns of the neighbor finder
- [ ] Closes #744
- [ ] Added tests for new/changed functions?
- [ ] Ran
isort .
andblack .
[24.2.0] at top-level - [ ] Documentation for functionality in
docs/
- [ ] Changes documented in
CHANGELOG.md
As discussed in #744, this PR modifies the return values of the neighbor finder so that the user has to ask explicitly for a certain quantity, making sure that they get what they want.
There is a general Interactions
class, but the different ways in which the finder can work require slightly different manipulations, and I have created multiple classes to facilitate that the user notices what they have at hand.
For finding neighbors (atom-atom interactions), we have the following classes:
-
FullNeighborsList
: Contains all the neighbor interactions in the system. Returned byfind_neighbors
ifatoms
isNone
. -
PartialNeighborsList
: Contains the neighbors for a list of atoms (neighbors can be outside that list). Returned byfind_neighbors
ifatoms
is notNone
. -
UniqueNeighborsList
: Contains all the neighbor interactions in the system, but only on one direction. Returned byfind_unique_pairs
. This is like the upper triangular part of the sparsity pattern/adjacency matrix. -
AtomNeighborsList
: Contains the neighbors for a single atom. This is returned when iterating overFullNeighborsList
orPartialNeighborsList
.
For finding atoms that are close to certain points in space we have:
-
CloseAtoms
: Contains all the interactions between points and atoms. Returned byfind_close
. -
PointCloseAtoms
: Contains the atoms that are close to a certain point. Returned when iterating overCloseAtoms
.
This is not finished, but before finishing the details I would like to get your opinion to see if you think it is on the right track or not.
One thing I noticed is that the FullNeighborsList
and UniqueNeighborsList
are very similar to a SparseAtoms
object. I don't know if we should return that instead, or subclass SparseAtoms
. Although with a neighbors list it is probably more convenient to have the matrix in COO format.
Here is an example of how one might get and use a FullNeighborsList
:
import sisl
from sisl.geom import NeighborFinder
# Create a graphene sheet with a defect
graphene = sisl.geom.graphene().tile(6, 0).tile(6, 1)
graphene = graphene.remove(15).translate2uc()
# Initialize a finder
finder = NeighborFinder(graphene, R=1.5)
# Get the neighbors
# Here "neighs" is a FullNeighborsList
neighs = finder.find_neighbors()
# neighs has attributes like `ì`, `j`, `isc`, `j_sc`, `n_neighbors`
# Here we plot the graphene sheet, coloring the atoms by the number of neighbors they have
graphene.plot(
axes="xy", nsc=[2, 2, 1], atoms_style={"color": neighs.n_neighbors}
).update_layout(
title="Number of neighbors in the graphene sheet",
height=700
).show()
# We can loop over it to get the neighbors of each atom
for neigh in neighs:
# neigh here is an AtomNeighborsList containing
# the neighbors of a given atom.
# We can get the atom to which the neighbors belong
neigh.atom
# We can get the neighbors, or their isc
neigh.j
neigh.isc
# Or the neighbors in supercell indices
neigh.j_sc
break
# Instead of looping we can also get the neighbors of a given atom directly
neighs[10]
# And we can plot the neighbors of a given atom. We color the atom in black and
# its neighbors in pink
# Atom for which we want to plot neighbors
at = 0
graphene.plot(
axes="xy",
nsc=[2, 2, 1],
atoms_style=[
{"atoms": at, "color": "black"},
{"atoms": neighs[at].j, "color": "pink"}
]
).update_layout(
title=f"Neighbors of atom {at}",
height=700
).show()