K points#
On this page we describe one of the main usage of wulfric - automatic generation of high-symmetry points for any given crystal.
For the full technical reference see Kpoints and wulfric.kpoints.
In the examples below we use crystal with six atoms and orthorhombic cell.
>>> import wulfric
>>> import numpy as np
>>> 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": np.array([
... [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],
... ]),
... }
Raw data#
If you only need the data about the high-symmetry points and path, then use
wulfric.kpoints.get_path_and_points()
# Default convention is "HPKOT"
>>> path, points = wulfric.kpoints.get_path_and_points(cell, atoms)
>>> path
'GAMMA-X-S-Y-GAMMA-Z-U-R-T-Z|X-U|Y-T|S-R'
>>> # points is a dictionary {name: coordinates}
>>> for name in points:
... print(f"{name:<5} : {points[name]}")
...
GAMMA : [0. 0. 0.]
X : [0. 0.5 0. ]
Z : [0. 0. 0.5]
U : [0. 0.5 0.5]
Y : [0.5 0. 0. ]
S : [0.5 0.5 0. ]
T : [0.5 0. 0.5]
R : [0.5 0.5 0.5]
For the strict definition of how the path is specified see K-path.
By default coordinates are relative to the reciprocal cell, defined by cell. Use
return_relative = False, to obtain absolute coordinates instead.
Kpoints class#
A convenient way to manage kpoints and kpath for calculations or for plotting is
implemented with the Kpoints class.
Creation#
Usually it is created from some crystal (cell + atoms) using
Kpoints.from_crystal()
>>> kp = wulfric.Kpoints.from_crystal(cell, atoms)
>>> kp.hs_names
['GAMMA', 'X', 'Z', 'U', 'Y', 'S', 'T', 'R']
However, it could be created manually as well:
>>> rcell = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
>>> names = ["GAMMA", "X"]
>>> coordinates = [[0, 0, 0], [0.5, 0, 0]]
>>> labels = [R"$\Gamma$", "X"]
>>> kp = wulfric.Kpoints(rcell, names=names, coordinates=coordinates, labels=labels)
>>> kp.hs_names
['GAMMA', 'X']
For the full list of available parameters see wulfric.Kpoints.
High-symmetry points#
Information about high-symmetry points is accessible through the following properties
Kpoints.hs_namesList of names of high-symmetry points.
>>> kp.hs_names ['GAMMA', 'X']
Kpoints.hs_coordinatesDictionary with coordinates of high-symmetry points.
>>> kp.hs_coordinates {'GAMMA': array([0, 0, 0]), 'X': array([0.5, 0. , 0. ])}
Kpoints.hs_labelsDictionary of labels of high-symmetry points. Usually used for plotting.
>>> kp.hs_labels {'GAMMA': '$\\Gamma$', 'X': 'X'}
Note
Names of high-symmetry points have to be unique.
Adding a point#
>>> kp.add_hs_point(name="M", coordinate=[0.5, 0.5, 0], label="M")
>>> kp.hs_names
['GAMMA', 'X', 'M']
>>> kp.hs_coordinates
{'GAMMA': array([0, 0, 0]), 'X': array([0.5, 0. , 0. ]), 'M': array([0.5, 0.5, 0. ])}
>>> kp.hs_labels
{'GAMMA': '$\\Gamma$', 'X': 'X', 'M': 'M'}
Getting summary of high-symmetry points#
In order to have a summary of the high-symmetry points the predefined method
Kpoints.hs_table() may be used:
>>> kp = wulfric.Kpoints.from_crystal(cell, atoms)
>>> print(kp.hs_table(decimals=4))
Name rel_b1 rel_b2 rel_b3 k_x k_y k_z
GAMMA 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000
X 0.0000 0.5000 0.0000 0.8841 0.0000 0.0000
Z 0.0000 0.0000 0.5000 0.0000 0.0000 0.3586
U 0.0000 0.5000 0.5000 0.8841 0.0000 0.3586
Y 0.5000 0.0000 0.0000 0.0000 0.6621 0.0000
S 0.5000 0.5000 0.0000 0.8841 0.6621 0.0000
T 0.5000 0.0000 0.5000 0.0000 0.6621 0.3586
R 0.5000 0.5000 0.5000 0.8841 0.6621 0.3586
K-path#
The k-path is the route in the reciprocal space, between the high-symmetry points.
Wulfric uses a string of the special format, that is described in K-path.
>>> # Create a Kpoints instance
>>> rcell = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
>>> names = ["G", "K", "X", "R"]
>>> coordinates = [[0, 0, 0], [0.5, 0.5, 0], [0.5, 0, 0], [0.5, 0.5, 0.5]]
>>> labels = ["$\Gamma$", "K", "X", "R"]
>>> kp = wulfric.Kpoints(rcell, names=names, coordinates=coordinates, labels=labels)
>>> # Default path is constructed from the list of high-symmetry points
>>> kp.path
[['G', 'K', 'X', 'R']]
>>> # Only the names from Kpoints.hs_names are allowed to be used in the path
>>> # Next line causes an ValueError, because high-symmetry point "S" is not defined
>>> kp.path = "G-K-X|R-S"
Traceback (most recent call last):
...
ValueError: Point 'S' is not defined. Defined points are:
G : [0 0 0]
K : [0.5 0.5 0. ]
X : [0.5 0. 0. ]
R : [0.5 0.5 0.5]
>>> # Now we split path into two subpaths
>>> kp.path = "G-K-X|R-G"
>>> kp.path
[['G', 'K', 'X'], ['R', 'G']]
>>> # We can add a point to de used in the path
>>> kp.add_hs_point(name="S", coordinate=[0.5, 0.5, 0.5], label="S")
>>> # Now it is possible to use "S" it in the path
>>> kp.path = "G-K-X|R-S"
>>> kp.path
[['G', 'K', 'X'], ['R', 'S']]
>>> # The path_string property returns the path in the string format
>>> kp.path_string
'G-K-X|R-S'
Note
Internally wulfric stores the path as a list of subpaths, where each subpath
is a list of high-symmetry point's names. This format is also correct for assigning
the Kpoints.path attribute.
Configuration#
The amount of kpoints to be generated between each pair of high-symmetry points in the path
is controlled by the Kpoints.n property.
>>> # Default value is 100
>>> kp.n
100
>>> kp.n = 10
>>> kp.n
10
Once the configuration of the Kpoints is done, it can be used for calculation or plotting.
Calculation#
There is one method suitable for calculation: Kpoints.points(). It is an array
of all generated kpoints. For each pair of high-symmetry points it generates
Kpoints.n points between them. The first and the last points are always
the high-symmetry points of this section of the path.
>>> rcell = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
>>> names = ["G", "K", "X"]
>>> coordinates = [[0, 0, 0], [0.5, 0.5, 0], [0.5, 0, 0]]
>>> labels = ["$\Gamma$", "K", "X"]
>>> kp = wulfric.Kpoints(rcell, names=names, coordinates=coordinates, labels=labels, n=4)
>>> kp.points()
array([[0. , 0. , 0. ],
[0.1, 0.1, 0. ],
[0.2, 0.2, 0. ],
[0.3, 0.3, 0. ],
[0.4, 0.4, 0. ],
[0.5, 0.5, 0. ],
[0.5, 0.5, 0. ],
[0.5, 0.4, 0. ],
[0.5, 0.3, 0. ],
[0.5, 0.2, 0. ],
[0.5, 0.1, 0. ],
[0.5, 0. , 0. ]])
Hint
For each section the last point is repeated twice, because it is the first point of the next section of the path.
array([[0. , 0. , 0. ], # <--- Gamma
[0.1, 0.1, 0. ],
[0.2, 0.2, 0. ],
[0.3, 0.3, 0. ],
[0.4, 0.4, 0. ],
[0.5, 0.5, 0. ], # <--- K
[0.5, 0.5, 0. ], # <--- K
[0.5, 0.4, 0. ],
[0.5, 0.3, 0. ],
[0.5, 0.2, 0. ],
[0.5, 0.1, 0. ],
[0.5, 0. , 0. ]]) # <--- X
Plotting#
For plotting there is one property Kpoints.labels and two methods
(Kpoints.ticks(), Kpoints.flat_points()). Two of them are for the
high-symmetry points and describe the labels and position of ticks on the x-axis:
>>> kp.labels
['$\\Gamma$', 'K', 'X']
>>> import numpy as np
>>> np.around(kp.ticks(), decimals=4)
array([0. , 0.7071, 1.2071])
The third property gives the coordinates of the Kpoints.points() for the plot:
>>> for point in kp.flat_points():
... print(round(point, 4))
...
0.0
0.1414
0.2828
0.4243
0.5657
0.7071
0.7071
0.8071
0.9071
1.0071
1.1071
1.2071
Note
Those coordinates are directly corresponds to the k-points from the previous subsection.
0.0 # <--- Gamma
0.1414
0.2828
0.4243
0.5657
0.7071 # <--- K
0.7071 # <--- K
0.8071
0.9071
1.0071
1.1071
1.2071 # <--- X
Hint
Repeated Kpoints.points() or Kpoints.flat_points() can be used
to restore the position of high-symmetry points in the path.