Manipulations with atoms and cell#
For the full technical reference see wulfric.crystal.
On this page we give examples of what can be done with the atoms
and reference cell
introduced on the key concepts page. Functions
from the examples are available under wulfric.crystal
submodule.
In the examples on this page we assume that wulfric and NumPy are imported as
>>> import numpy as np
>>> import wulfric as wulf
In the examples we use crystall with six atoms and orthorhombic cell.
>>> cell = np.array([
... [0.000000, 4.744935, 0.000000],
... [3.553350, 0.000000, 0.000000],
... [0.000000, 0.000000, 8.760497],
... ])
>>> atoms = {
... "names": ["Cr1", "Br1", "S1", "Cr2", "Br2", "S2"],
... "positions": [
... [0.000000, 0.500000, 0.882382],
... [0.000000, 0.000000, 0.677322],
... [0.500000, 0.500000, 0.935321],
... [0.500000, 0.000000, 0.117618],
... [0.500000, 0.500000, 0.322678],
... [0.000000, 0.000000, 0.064679],
... ],
... }
Atom's names#
There are no rules specified by wulfric for the atom names. Any non-empty string is a
valid name. Wulfric can modify names to ensure their uniqueness with
wulfric.crystal.ensure_unique_names()
>>> import wulfric as wulf
>>> atoms1 = {"names" : ["Cr1", "Cr2", "Br", "Br", "S", "S"]}
>>> # Default strategy is "all"
>>> wulf.crystal.ensure_unique_names(atoms1)
>>> atoms1
{'names': ['Cr11', 'Cr22', 'Br3', 'Br4', 'S5', 'S6']}
>>> atoms1 = {"names" : ["Cr1", "Cr2", "Br", "Br", "S", "S"]}
>>> wulf.crystal.ensure_unique_names(atoms1, strategy="repeated-only")
>>> atoms1
{'names': ['Cr1', 'Cr2', 'Br1', 'Br2', 'S1', 'S2']}
Atom's species#
Wulfric recognizes two keywords in the atoms
dictionary: "names" and "species".
Both are lists of strings. Names of atoms are not restricted in any way and user is free
to name atoms as they please (however, it is a common practice to include atom's species
in the name). On contrary the "species" are interpreted having a set of special values in
mind. Correct strings for possible species are hard-coded and available under the
wulfric.constants.ATOM_SPECIES
constant.
Wulfric implements two functions to automatically guess the atom's species from its name
>>> wulf.crystal.get_atom_species("Cr1")
'Cr'
If it is unable to guess the atom species, then it issues a RuntimeWarning
and return
"X"
as a species.
>>> wulf.crystal.get_atom_species("124")
...
'X'
>>> # It is possible to raise an error instead of the warning
wulf.crystal.get_atom_species("124", raise_on_fail=True)
...
wulfric._exceptions.FailedToDeduceAtomSpecies: Tried to deduce name from '124'. Failed.
To guess the names for the dictionary of atoms
use
>>> # Note: this function modifies the dictionary on which it is called
>>> wulf.crystal.populate_atom_species(atoms)
>>> atoms["species"]
['Cr', 'Br', 'S', 'Cr', 'Br', 'S']
Pair of atoms#
Often a distance between a pair of atoms is required. Wulfric has two functions for that. It assumes that first atom is located in the reference unit cell with indices \((0, 0, 0)\) and second atom is located in any unit cell of the crystal with indices \((i, j, k)\).
To get the vector from atom 1 to atom 2 and distance between them use
>>> wulf.crystal.get_vector(cell, atoms, atom1=0, atom2=0, R=(0,1,0))
array([3.55335, 0. , 0. ])
>>> wulf.crystal.get_distance(cell, atoms, atom1=0, atom2=0, R=(0,1,0))
3.55335
Standardization#
Please read similar section for the cell first.
Standardization of the cell
does not change neither the lattice defined by this cell
nor absolute coordinates of atoms. Therefore, relative coordinates, stored in
atoms["positions"]
should change. Wulfric defines a function, which standardizes the
cell and updates relative coordinated of atoms.
>>> # Position of the first atom relative to the non-standardized cell
>>> atoms["positions"][0]
[0.0, 0.5, 0.882382]
>>> # Position of the same atom in the real space, in absolute coordinates
>>> atoms["positions"][0] @ cell
array([1.776675 , 0. , 7.73010486])
>>> # This function return new cell, but update passes atoms dictionary
>>> cell = wulf.crystal.standardize(cell=cell, atoms=atoms)
>>> # Now the cell is a standard primitive one
>>> cell
array([[-3.55335 , 0. , 0. ],
[ 0. , -4.744935, 0. ],
[ 0. , 0. , -8.760497]])
>>> # Note how the relative positions changed
>>> atoms["positions"][0]
array([-0.5 , 0. , -0.882382])
>>> # But absolute position is the same
>>> atoms["positions"][0] @ cell
array([1.776675 , 0. , 7.73010486])
Translation equivalence#
After standardization relative coordinates of atoms may become negative. It means that the atoms are located outside of the unit cell in real space. The crystal that is defined by the new part of cell and atoms is still the same as before standardization.
One may want to ensure that all atoms are located within the volume of \((0, 0, 0)\) unit cell. In that way the atoms would be changed to their translational equivalent images. To do so use
>>> for p in atoms["positions"]:
... print(p)
...
[-0.5 0. -0.882382]
[ 0. 0. -0.677322]
[-0.5 -0.5 -0.935321]
[ 0. -0.5 -0.117618]
[-0.5 -0.5 -0.322678]
[ 0. 0. -0.064679]
>>> wulf.crystal.ensure_000(atoms)
>>> for p in atoms["positions"]:
... print(p)
...
[0.5 0. 0.117618]
[0. 0. 0.322678]
[0.5 0.5 0.064679]
[0. 0.5 0.882382]
[0.5 0.5 0.677322]
[0. 0. 0.935321]
Resulting pair of atoms and cell still describe the same crystal as at the beginning.