ipynb download badge   Binder badge

Creating common geometries

Importing sisl

Import the sisl package and start working with it.
To ensure there is no clashes with other packages we encourage users to stick with the same short-hand name. The sisl-developers recommends using si. In all of sisl documentation it is assumed that si refers to sisl.

An important aspect of sisl is the units used:

  • Ångstrøm [Ang]

  • Electron volt [eV]

[1]:
import sisl as si
import numpy as np
info:0: SislInfo: Please install tqdm (pip install tqdm) for better looking progress bars

Creating a geometry

sisl provides a broad set of methods to create default geometries. There are fcc, bcc, sc, graphene and many other default geometries available.
The default geometry creations are found in the sisl.geom module, (for additional details check out help(si.geom)).

Our focus here will be to create an FCC lattice of iron.

[2]:
iron = si.geom.fcc(2.4, si.Atom("Fe"))

There is lots of information one can retrieve from the geometry, such as:

  • lattice vectors

  • number of atoms and orbitals

  • atomic species

Try and extract the above information:

[3]:
print("All lattice vectors:")
print(iron.lattice.cell)
c = iron.lattice.cell[2]
print(f"lattice vector c = {c}")
print(f"iron has {iron.na} atoms and {iron.no} orbitals")
print(f"iron's only atom has the atomic number {iron.atoms[0].Z}")
All lattice vectors:
[[0.  1.2 1.2]
 [1.2 0.  1.2]
 [1.2 1.2 0. ]]
lattice vector c = [1.2 1.2 0. ]
iron has 1 atoms and 1 orbitals
iron's only atom has the atomic number 26

Let us print out the geometry and see for additional information:

[4]:
print(iron)
Geometry{na: 1, no: 1,
 Atoms{species: 1,
  Atom{Fe, Z: 26, mass(au): 55.84500, maxR: -1.00000,
   Orbital{R: -1.00000, q0: 0.0}
  }: 1,
 },
 maxR: -1.00000,
 Lattice{nsc: [3 3 3],
  origin=[0.0000, 0.0000, 0.0000],
  A=[0.0000, 1.2000, 1.2000],
  B=[1.2000, 0.0000, 1.2000],
  C=[1.2000, 1.2000, 0.0000],
  bc=[Periodic,
      Periodic,
      Periodic]
 }
}

This shows a greater detail of the geometry. - it shows there is 1 atom (na: 1), and 1 orbital (no: 1) - a complete list of atoms (Atoms{...}), their atomic number, mass and associated orbitals - the associated Lattice object describes the lattice vectors, and which lattice vectors uses periodicity


The geometry also has associated coordinates of the atomic structure, these can be accessed through the .xyz attribute:

[5]:
iron.xyz
[5]:
array([[0., 0., 0.]])

In this case there is only 1 atom, and its position is at the origin.

Let us try and do a little more complicated structure, say graphene.

[6]:
graphene = si.geom.graphene()
print(graphene)
Geometry{na: 2, no: 2,
 Atoms{species: 1,
  Atom{C, Z: 6, mass(au): 12.01070, maxR: 1.43420,
   Orbital{R: 1.43420, q0: 0.0}
  }: 2,
 },
 maxR: 1.43420,
 Lattice{nsc: [3 3 1],
  origin=[0.0000, 0.0000, 0.0000],
  A=[2.1300, -1.2298, 0.0000],
  B=[2.1300, 1.2298, 0.0000],
  C=[0.0000, 0.0000, 14.2000],
  bc=[Periodic,
      Periodic,
      Unknown]
 }
}
[7]:
graphene.xyz
[7]:
array([[0.  , 0.  , 0.  ],
       [1.42, 0.  , 0.  ]])

Note how the changed output looks, we now have 2 atoms, but the atom is not duplicated, instead we share a reference (to minimize memory requirement).

The atomic coordinates here signals the two positions, and it is obvious that the default bond-length for graphene is defined to be \(1.42\).

Other default geometries

There are many other implicit geometries available in sisl which can be found here. These can be used to generalize and construct geometries on the fly, in a simply and efficient manner.

Defining atoms

The geometries will accept an argument atoms= where you can define the atoms in the geometry. We already did this in the fcc system where we defined the atom si.Atom("Fe").
Lets delve into the Atom object.
[8]:
help(si.Atom)
Help on class Atom in module sisl:

class Atom(sisl._dispatch_class._Dispatchs)
 |  Atom(*args, **kwargs)
 |
 |  Atomic information for a single atomic species
 |
 |  An atomic object retaining information about a single atomic species.
 |  It describes the atomic number (integer), the mass of the atom, and
 |  holds a list of atomic centered orbitals. It also allows one
 |  to tag the atom to distinguish it from other atoms of the same species.
 |
 |  Parameters
 |  ----------
 |  Z : int or str
 |      determine species for the atomic species.
 |  orbitals : list of Orbital or float, optional
 |      orbitals associated with this atom. See `Orbital` for details on
 |      how to define orbitals.
 |      Defaults to one orbital.
 |  mass : float, optional
 |      the atomic mass, defaults to the mass found in `PeriodicTable`.
 |  tag : str, optional
 |      arbitrary designation for user handling similar atoms with
 |      different settings (defaults to the label of the atom)
 |
 |
 |  Examples
 |  --------
 |  >>> Carbon = Atom(6)
 |  >>> Carbon = Atom("C")
 |  >>> Carbon = Atom("Carbon")
 |
 |  Add a tag to be able to distinguish it from other atoms
 |  >>> tagged_Carbon = Atom("Carbon", tag="siteA")
 |
 |  Create deuterium
 |  >>> D = Atom("H", mass=2.014)
 |
 |  Define an atom with 3 orbitals, each with a range of 2 Angstroem
 |  >>> C3 = Atom("C", orbitals=[2, 2, 2])
 |
 |  Define an atom outside of the periodic table (negative will yield an
 |  AtomGhost object)
 |  >>> ghost_C = Atom(-6)
 |
 |  Define an unknown atom (basically anything can do)
 |  >>> unknown_atom = Atom(1000)
 |
 |  Notes
 |  -----
 |  One can define atoms outside of the periodic table. They will generally
 |  be handled in this order:
 |
 |  * negative numbers will be converted into positive ones, and the returned
 |    object will be an `AtomGhost`
 |  * any other number (or name) not found in the periodic table will be returned
 |    in an AtomUnknown object
 |
 |  The mass for atoms outside the periodic table will default to 1e40 amu.
 |
 |  See Also
 |  --------
 |  Orbital : define an orbital
 |  Atoms : an efficient collection of Atom objects
 |
 |  Method resolution order:
 |      Atom
 |      sisl._dispatch_class._Dispatchs
 |      builtins.object
 |
 |  Methods defined here:
 |
 |  __eq__(self, b) from sisl._core.atom.Atom
 |      Return true if the saved quantities are the same
 |
 |  __getattr__(self, attr) from sisl._core.atom.Atom
 |      Pass attribute calls to the orbital classes and return lists/array
 |
 |      Parameters
 |      ----------
 |      attr : str
 |
 |  __getitem__(self, key) from sisl._core.atom.Atom
 |      The orbital corresponding to index `key`
 |
 |  __getstate__(self) from sisl._core.atom.Atom
 |      Return the state of this object
 |
 |  __hash__(self) from sisl._core.atom.Atom
 |      Return hash(self).
 |
 |  __init__(self, Z, orbitals=None, mass: 'Optional[float]' = None, tag: 'Optional[str]' = None, **kwargs) from sisl._core.atom.Atom
 |      Initialize self.  See help(type(self)) for accurate signature.
 |
 |  __iter__(self) from sisl._core.atom.Atom
 |      Loop on all orbitals in this atom
 |
 |  __len__(self) from sisl._core.atom.Atom
 |      Return number of orbitals in this atom
 |
 |  __ne__(self, b) from sisl._core.atom.Atom
 |      Return self!=value.
 |
 |  __repr__(self) from sisl._core.atom.Atom
 |      Return repr(self).
 |
 |  __setstate__(self, d) from sisl._core.atom.Atom
 |      Re-create the state of this object
 |
 |  __str__(self) from sisl._core.atom.Atom
 |      Return str(self).
 |
 |  copy(atom: 'Atom', Z: 'Optional[Union[int, str]]' = None, orbitals=None, mass: 'Optional[float]' = None, tag: 'Optional[str]' = None) -> 'Atom' from sisl
 |      Return copy of this object
 |
 |  equal(self, other, R=True, psi=False) from sisl._core.atom.Atom
 |      True if `other` is the same as this atomic species
 |
 |      Parameters
 |      ----------
 |      other : Atom
 |         the other object to check againts
 |      R : bool, optional
 |         if True the equality check also checks the orbital radii, else they are not compared
 |      psi : bool, optional
 |         if True, also check the wave-function component of the orbitals, see `Orbital.psi`
 |
 |  index(self, orbital) from sisl._core.atom.Atom
 |      Return the index of the orbital in the atom object
 |
 |  iter(self, group=False) from sisl._core.atom.Atom
 |      Loop on all orbitals in this atom
 |
 |      Parameters
 |      ----------
 |      group : bool, optional
 |         if two orbitals share the same radius
 |         one may be able to group two orbitals together
 |
 |      Returns
 |      -------
 |      Orbital
 |          current orbital, if `group` is ``True`` this is a list of orbitals,
 |          otherwise a single orbital is returned
 |
 |  maxR(self) from sisl._core.atom.Atom
 |      Return the maximum range of orbitals.
 |
 |  radius(self, method='calc') from sisl._core.atom.Atom
 |      Return the atomic radii of the atom (in Ang)
 |
 |      See `PeriodicTable.radius` for details on the argument.
 |
 |  remove(atom: 'Atom', orbitals: 'SimpleIndex') -> 'Atom' from sisl
 |      Return the same atom without a specific set of orbitals
 |
 |      Parameters
 |      ----------
 |      orbitals :
 |         indices of the orbitals to remove
 |
 |      Returns
 |      -------
 |      Atom
 |          without the specified orbitals
 |
 |      See Also
 |      --------
 |      Atom.sub : retain a selected set of orbitals
 |
 |  scale(atom: 'Atom', scale: 'float') -> 'Atom' from sisl
 |      Scale the atomic radii and return an equivalent atom.
 |
 |      Parameters
 |      ----------
 |      scale :
 |         the scale factor for the atomic radii
 |
 |  sub(atom: 'Atom', orbitals: 'SimpleIndex') -> 'Atom' from sisl
 |      Return the same atom with only a subset of the orbitals present
 |
 |      Parameters
 |      ----------
 |      orbitals :
 |         indices of the orbitals to retain
 |
 |      Returns
 |      -------
 |      Atom
 |          with only the subset of orbitals
 |
 |      Raises
 |      ------
 |      ValueError
 |         if the number of orbitals removed is too large or some indices are outside the allowed range
 |
 |  to = ClassDispatcher(...)
 |
 |  toSphere(self, center=None) from sisl._core.atom.Atom
 |      Return a sphere with the maximum orbital radius equal
 |
 |      Returns
 |      -------
 |      ~sisl.shape.Sphere
 |           a sphere with radius equal to the maximum radius of the orbitals
 |
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |
 |  __new__(cls, *args, **kwargs) from sisl._core.atom.Atom
 |      Figure out which class to actually use
 |
 |  ----------------------------------------------------------------------
 |  Readonly properties defined here:
 |
 |  Z
 |      Atomic number
 |
 |  mass
 |      Atomic mass
 |
 |  no
 |      Number of orbitals on this atom
 |
 |  orbitals
 |      List of orbitals
 |
 |  symbol
 |      Return short atomic name (Au==79).
 |
 |  tag
 |      Tag for atom
 |
 |  ----------------------------------------------------------------------
 |  Class methods inherited from sisl._dispatch_class._Dispatchs:
 |
 |  __init_subclass__(dispatchs: 'Optional[Union[str, Sequence[Any]]]' = None, when_subclassing: 'Optional[str]' = None, **kwargs)
 |      This method is called when a class is subclassed.
 |
 |      The default implementation does nothing. It may be
 |      overridden to extend subclasses.
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from sisl._dispatch_class._Dispatchs:
 |
 |  __dict__
 |      dictionary for instance variables
 |
 |  __weakref__
 |      list of weak references to the object

Here we create an fcc lattice made up of Deuterium atoms

[9]:
D = si.Atom(1, mass=2.014)
fcc_D = si.geom.fcc(1.42, atoms=D)
print(fcc_D)
Geometry{na: 1, no: 1,
 Atoms{species: 1,
  Atom{H, Z: 1, mass(au): 2.01400, maxR: -1.00000,
   Orbital{R: -1.00000, q0: 0.0}
  }: 1,
 },
 maxR: -1.00000,
 Lattice{nsc: [3 3 3],
  origin=[0.0000, 0.0000, 0.0000],
  A=[0.0000, 0.7100, 0.7100],
  B=[0.7100, 0.0000, 0.7100],
  C=[0.7100, 0.7100, 0.0000],
  bc=[Periodic,
      Periodic,
      Periodic]
 }
}

Another example would be to create a bilayer structure with 2 different atoms (say graphene below hBN)

[10]:
C = si.Atom("C")
B = si.Atom("B")
N = si.Atom("N")
hBN = si.geom.bilayer(1.45, bottom_atoms=C, top_atoms=[B, N])
print(hBN)
Geometry{na: 4, no: 4,
 Atoms{species: 3,
  Atom{C, Z: 6, mass(au): 12.01070, maxR: -1.00000,
   Orbital{R: -1.00000, q0: 0.0}
  }: 2,
  Atom{B, Z: 5, mass(au): 10.81100, maxR: -1.00000,
   Orbital{R: -1.00000, q0: 0.0}
  }: 1,
  Atom{N, Z: 7, mass(au): 14.00670, maxR: -1.00000,
   Orbital{R: -1.00000, q0: 0.0}
  }: 1,
 },
 maxR: -1.00000,
 Lattice{nsc: [3 3 1],
  origin=[0.0000, 0.0000, 0.0000],
  A=[2.1750, -1.2557, 0.0000],
  B=[2.1750, 1.2557, 0.0000],
  C=[0.0000, 0.0000, 14.5000],
  bc=[Periodic,
      Periodic,
      Unknown]
 }
}

This concludes a quick tutorial on how to create a predefined geometry and how to define the atoms in it.