crystod.bz#

Brillouin zones, k paths and special k points (the crystod-bz domain).

crystod.bz is the Python form of the crystod-bz command. It builds the first Brillouin zone of a lattice as a polyhedron, generates the seekpath high-symmetry k path or parses a manual one, folds the reciprocal lattice of a supercell into the unit-cell zone (--trans-mat), and lists the ISO-IR special k points of a space group (--show-kpoint). The plotting helpers return plain Plotly scatter3d trace dictionaries, so the geometry can be rendered by any Plotly front end or read as numbers. Reciprocal lattices are (3, 3) arrays whose rows are b1, b2, b3; the command uses inv(lattice).T, without the factor of 2 pi.

Brillouin-zone geometry and k paths (crystod-bz -c POSCAR)

  • get_brillouin_zone_3d: vertices, edges and facets of the first Brillouin zone by Voronoi decomposition of the reciprocal lattice.

  • get_seekpath_kpath: the seekpath path of a cell as coordinate segments, labels, standardized primitive lattice and space group.

  • parse_manual_band: a --band string as (N, 3) segments.

  • prettify_label: GAMMA, X_1 and the like as plot labels.

  • build_bz_traces: the Plotly traces of the zone with its k path.

Supercell folding (crystod-bz --trans-mat)

  • parse_transformation_matrix: a --trans-mat string as a (3, 3) matrix.

  • get_folded_gamma_points: the |det T| unit-cell q points that fold onto the supercell Gamma point.

  • build_supercell_bz_traces: the Plotly traces of the unit-cell zone tiled with the supercell zone at those points.

Special k-point tables (crystod-bz --show-kpoint)

  • get_special_kpoints: the ISO-IR special k points of a space group in the primitive (and, for centred lattices, conventional) reciprocal basis.

A typical session:

import numpy as np
from phonopy.interface.vasp import read_vasp
from crystod import bz
from crystod.examples import example_path

cell = read_vasp(example_path("221_PPOSCAR_ScF3"))
segments, labels, lattice, symbol, number = bz.get_seekpath_kpath(cell, 1e-5)
rec_lat = np.linalg.inv(lattice).T
vertices, ridges, facets = bz.get_brillouin_zone_3d(rec_lat)
traces = bz.build_bz_traces(rec_lat, segments, labels)

sg_type, names, primitive, conventional = bz.get_special_kpoints("Fm-3m")

Attributes resolve lazily (PEP 562): import crystod.bz costs nothing beyond NumPy, and scipy, seekpath, phonopy and the ISO-IR tables are loaded by the first call that needs them. Bad input raises ValueError through this namespace (the implementation modules raise SystemExit, which suits the command line).

crystod.bz.build_bz_traces(rec_lat, segments, label_segments)[source]#

Build the Plotly traces of a Brillouin zone with an optional k path.

This is the figure crystod-bz writes for the unit-cell zone: the reciprocal basis vectors b1, b2, b3 (red, green, blue), the zone edges and corners (black; hovering a corner shows its fractional coordinates) and, when segments is given, the k path (goldenrod) with a marker and label at every k point. The traces are plain dictionaries of scatter3d specifications, ready for plotly.graph_objects.Figure(data=traces) or for json.dumps into a page that loads plotly.js, which is what the command does.

Parameters:
  • rec_lat (ndarray[tuple[Any, ...], dtype[_ScalarT]]) – (3, 3) reciprocal lattice, rows b1, b2, b3 (see get_brillouin_zone_3d).

  • segments (list[ndarray[tuple[Any, ...], dtype[_ScalarT]]] | None) – k-path segments as (N_i, 3) arrays of fractional coordinates in the basis rec_lat, as returned by get_seekpath_kpath or parse_manual_band; None draws the zone alone.

  • label_segments (list[list[str]] | None) – One list of N_i labels per segment, shown after prettify_label; None leaves the markers unlabelled.

Returns:

A list of Plotly scatter3d trace dictionaries.

Return type:

list[dict]

Example

>>> import numpy as np
>>> from crystod import bz
>>> rec_lat = np.linalg.inv(4.07 * np.eye(3)).T
>>> path = bz.parse_manual_band("0 0 0  1/2 0 0  1/2 1/2 0  0 0 0")
>>> traces = bz.build_bz_traces(rec_lat, path, [["GM", "X", "M", "GM"]])
>>> len(traces), traces[-1]["text"]
(12, ['Γ', 'X', 'M', 'Γ'])
crystod.bz.build_supercell_bz_traces(rec_lat, rec_super_lat, centers)[source]#

Build the Plotly traces of a unit-cell zone tiled with supercell zones.

This is the figure of crystod-bz --trans-mat: the unit-cell reciprocal basis and zone edges (black, dotted), the supercell reciprocal basis (red, green, blue) and a copy of the supercell Brillouin zone (red) centred at every point of centers, with a marker there whose hover text gives the unit-cell fractional coordinates. As in build_bz_traces, the result is a list of plain scatter3d trace dictionaries.

Parameters:
  • rec_lat (ndarray[tuple[Any, ...], dtype[float64]]) – (3, 3) reciprocal lattice of the unit cell (rows).

  • rec_super_lat (ndarray[tuple[Any, ...], dtype[float64]]) – (3, 3) reciprocal lattice of the supercell (rows), inv(trans_mat @ lattice).T in the command’s convention.

  • centers (ndarray[tuple[Any, ...], dtype[float64]]) – (n, 3) Cartesian centres of the supercell zones, normally the output of get_folded_gamma_points.

Returns:

A list of Plotly scatter3d trace dictionaries.

Return type:

list[dict]

Example

>>> import numpy as np
>>> from crystod import bz
>>> lattice = 4.07 * np.eye(3)
>>> T = bz.parse_transformation_matrix("2 0 0  0 2 0  0 0 2")
>>> rec_lat = np.linalg.inv(lattice).T
>>> rec_super_lat = np.linalg.inv(T @ lattice).T
>>> centers = bz.get_folded_gamma_points(T, rec_lat)
>>> traces = bz.build_supercell_bz_traces(rec_lat, rec_super_lat, centers)
>>> len(centers), len(traces)
(8, 61)
crystod.bz.get_brillouin_zone_3d(rec_lat)[source]#

Construct the first Brillouin zone of a reciprocal lattice.

The first Brillouin zone is the Wigner-Seitz cell of the reciprocal lattice. It is found by a Voronoi decomposition (scipy) of the 3x3x3 block of reciprocal-lattice points around the origin: the Voronoi cell of the origin is the zone. This is the polyhedron crystod-bz draws, for the unit cell and, with --trans-mat, for the supercell as well.

Parameters:

rec_lat (ndarray[tuple[Any, ...], dtype[_ScalarT]]) – (3, 3) array whose rows are the reciprocal basis vectors b1, b2, b3. Any overall scale is accepted; the command uses inv(lattice).T (no factor of 2 pi), for which cartesian @ inv(rec_lat) are fractional coordinates.

Returns:

The tuple (vertices, ridges, facets) where vertices is an (N, 3) array with the Cartesian coordinates of the zone corners, ridges is a list with one (M + 1, 3) array per facet holding the closed polyline of its edges (the first vertex is repeated at the end), and facets is the same list without the repeated vertex.

Return type:

tuple[ndarray[tuple[Any, …], dtype[_ScalarT]], list, list]

Example

>>> import numpy as np
>>> from crystod import bz
>>> rec_lat = np.linalg.inv(4.07 * np.eye(3)).T   # cubic, a = 4.07 A
>>> vertices, ridges, facets = bz.get_brillouin_zone_3d(rec_lat)
>>> len(vertices), len(facets)
(8, 6)
>>> np.allclose(np.abs(vertices @ np.linalg.inv(rec_lat)), 0.5)
True
crystod.bz.get_folded_gamma_points(trans_mat, rec_lat)[source]#

Unit-cell q points that fold onto the Gamma point of a supercell.

The reciprocal lattice of the supercell trans_mat @ lattice is |det T| times denser than that of the unit cell, and its points fall into |det T| classes modulo the unit-cell reciprocal lattice. One representative per class, brought into the unit-cell Brillouin zone by the minimum-image rule, is a unit-cell q point that becomes Gamma in the supercell; crystod-bz --trans-mat prints these points and tiles the supercell zone at each of them.

Parameters:
  • trans_mat (ndarray[tuple[Any, ...], dtype[float64]]) – (3, 3) unit-cell to supercell transformation matrix (rows, integer entries), e.g. from parse_transformation_matrix.

  • rec_lat (ndarray[tuple[Any, ...], dtype[float64]]) – (3, 3) reciprocal lattice of the unit cell, rows b1, b2, b3 (the command uses inv(lattice).T).

Returns:

(|det T|, 3) array of Cartesian coordinates, Gamma included; points @ inv(rec_lat) gives them in fractional coordinates of the unit-cell reciprocal basis.

Return type:

ndarray[tuple[Any, …], dtype[float64]]

Example

>>> import numpy as np
>>> from crystod import bz
>>> rec_lat = np.linalg.inv(4.07 * np.eye(3)).T
>>> T = bz.parse_transformation_matrix("1 1 0  -1 1 0  0 0 1")
>>> points = bz.get_folded_gamma_points(T, rec_lat)
>>> np.round(points @ np.linalg.inv(rec_lat), 3).tolist()
[[0.0, 0.0, 0.0], [0.5, 0.5, 0.0]]
crystod.bz.get_seekpath_kpath(cell, tolerance)[source]#

Generate the recommended high-symmetry k path of a cell with seekpath.

This is the automatic path of crystod-bz -c POSCAR without --band. seekpath standardizes the cell first, so the coordinates refer to the reciprocal basis of the seekpath standardized primitive cell, which is returned alongside; when that cell differs from the input, the command prints a note and draws the zone for the standardized cell.

Parameters:
  • cell (PhonopyAtoms) – The crystal structure as phonopy’s PhonopyAtoms (any object with cell, scaled_positions and numbers attributes works).

  • tolerance (float) – Symmetry tolerance forwarded to seekpath and spglib (--tolerance; the command uses 1e-5).

Returns:

The tuple (segments, label_segments, primitive_lattice, symbol, number) where segments is a list of (N, 3) arrays of fractional k coordinates, one per continuous piece of the path, label_segments the matching lists of N seekpath labels (GAMMA, X, …), primitive_lattice the (3, 3) row-vector lattice of the standardized primitive cell, and symbol/number the international symbol and number of the detected space group.

Raises:

SystemExit – seekpath is not installed (ValueError when called through crystod.bz).

Example

>>> from phonopy.interface.vasp import read_vasp
>>> from crystod import bz
>>> from crystod.examples import example_path
>>> cell = read_vasp(example_path("221_PPOSCAR_ScF3"))
>>> segments, labels, lattice, symbol, number = bz.get_seekpath_kpath(
...     cell, 1e-5)
>>> symbol, number
('Pm-3m', 221)
>>> labels
[['GAMMA', 'X', 'M', 'GAMMA', 'R', 'X'], ['R', 'M']]
crystod.bz.get_special_kpoints(space_group_symbol)[source]#

Special (high-symmetry) k points of a space group in the ISO-IR convention.

This is the table behind crystod-bz --show-kpoint --space-group Pnma. The k points and their labels (GM, X, M, R, …) come from the bundled ISO-IR tables (CIR_data), so they are the points at which crystod, crystod-phonon --irreps, crystod-mag and crystod-group label irreps. Coordinates are given in the primitive reciprocal basis; for centred lattices (F, I, C, A, B, R) the conventional-basis coordinates are returned as well, since the two bases differ there.

Parameters:

space_group_symbol (str) – Space-group symbol ("Pnma", "Fm-3m" or the full symbol "P m -3 m") or the number as a string ("221").

Returns:

The tuple (sg_type, names, primitive_kpoints, conventional_kpoints) where sg_type is the spglib SpaceGroupType record of the resolved group (sg_type.number, sg_type.international_short), names the ISO-IR labels ordered with GM first and then by the sum of the absolute primitive coordinates and by name, primitive_kpoints the matching [kx, ky, kz] lists in the primitive reciprocal basis, and conventional_kpoints the same points in the conventional reciprocal basis, or None for primitive (P) lattices, where the two bases coincide.

Raises:

SystemExit – The symbol or number is not a standard space group (ValueError when called through crystod.bz).

Example

>>> from crystod import bz
>>> sg_type, names, primitive, conventional = bz.get_special_kpoints("Fm-3m")
>>> sg_type.number, names
(225, ['GM', 'X', 'L', 'W'])
>>> primitive[1], conventional[1]
([0.5, 0.0, 0.5], [0.0, 1.0, 0.0])
crystod.bz.parse_manual_band(band)[source]#

Parse a --band string into k-path segments.

The string has the format of the crystod-bz --band option: continuous segments separated by commas, each a whitespace-separated list of fractional coordinates, three numbers per k point; fractions such as 1/2 are accepted. The coordinates refer to the reciprocal basis of the lattice the path is drawn on, which for the command is the input cell as given.

Parameters:

band (str) – The path string, e.g. "0 0 0  0 1/2 0  1/2 1/2 0, 1/2 1/2 0  1/2 1/2 1/2".

Returns:

One (N_i, 3) float array per comma-separated segment, in the order given; empty segments (a trailing comma) are skipped.

Raises:
  • SystemExit – The number of values in a segment is not a multiple of 3, a segment has fewer than two k points, or the string holds no k point at all (ValueError when called through crystod.bz).

  • ValueError – A token is neither a number nor a fraction.

Return type:

list[ndarray[tuple[Any, …], dtype[_ScalarT]]]

Example

>>> from crystod import bz
>>> path = "0 0 0  1/2 1/2 0, 1/2 1/2 0  1/2 1/2 1/2"
>>> segments = bz.parse_manual_band(path)
>>> len(segments), segments[0].tolist()
(2, [[0.0, 0.0, 0.0], [0.5, 0.5, 0.0]])
crystod.bz.parse_transformation_matrix(text)[source]#

Parse a --trans-mat string into a (3, 3) transformation matrix.

The nine numbers are read row-wise, as crystod-bz --trans-mat takes them; fractions such as 1/2 are accepted. The matrix T maps the unit-cell lattice A (row vectors) to the supercell lattice T @ A, so |det T| is the number of unit cells in the supercell.

Parameters:

text (str) – Nine whitespace-separated numbers, e.g. "0 1 2  -1 0 2  1 -1 2".

Returns:

The (3, 3) float matrix.

Raises:
  • SystemExit – The string does not hold exactly nine numbers, or the matrix is singular (ValueError when called through crystod.bz).

  • ValueError – A token is neither a number nor a fraction.

Return type:

ndarray[tuple[Any, …], dtype[float64]]

Example

>>> from crystod import bz
>>> bz.parse_transformation_matrix("2 0 0  0 2 0  0 0 2").tolist()
[[2.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 2.0]]
crystod.bz.prettify_label(label)[source]#

Convert a seekpath k-point label into its display form.

crystod-bz places these labels next to the k-path markers of the HTML plot: GAMMA (or GM), DELTA, SIGMA and LAMBDA become the Greek letters, and a _ suffix becomes an HTML subscript, so X_1 turns into X<sub>1</sub>. Any other label is returned unchanged.

Parameters:

label (str) – A seekpath-style label such as "GAMMA", "X_1" or "SIGMA_0".

Returns:

The label as an HTML fragment for Plotly text.

Return type:

str

Example

>>> from crystod import bz
>>> bz.prettify_label("GAMMA"), bz.prettify_label("SIGMA_0")
('Γ', 'Σ<sub>0</sub>')