Chapter 3
15 min read
Section 24 of 70

High-Symmetry Points and Paths

Reciprocal Space and Diffraction

Learning Objectives

Section 3.2 built the reciprocal basis. Section 3.3 carved out the first Brillouin zone — the unique slice of reciprocal space inside which every physical wavevector is uniquely defined. This section finally lets us name the special points inside that zone and discover why every band-structure plot in every condensed-matter paper looks essentially the same. By the end you should be able to:

  1. Explain what makes a k-point "high-symmetry" — the technical definition is a fixed point of a non-trivial subgroup of the point group, and the intuition is that several symmetry operations agree there at once.
  2. Recognise the canonical labels Γ,X,M,R,K,L,W,U\Gamma, X, M, R, K, L, W, U on sight and know which Brillouin-zone face, edge, or vertex each one occupies.
  3. Write down by hand the high-symmetry points of the simple-cubic, FCC, BCC, and 2D hexagonal lattices in fractional reciprocal coordinates (coefficients of b1,b2,b3\mathbf{b}_1, \mathbf{b}_2, \mathbf{b}_3).
  4. Read a band-structure plot and match every kink, gap, and degeneracy to the high-symmetry tick on the x-axis where it occurs.
  5. Hand-write a VASP line-mode KPOINTS file that traces the standard FCC path ΓXWKΓLUW\Gamma \to X \to W \to K \to \Gamma \to L \to U \to W — exactly the file you will use in Chapter 6 to compute the band structure of CdSe.
One-line preview: a high-symmetry path is a 1D walking tour through 3D reciprocal space that, by virtue of group theory, is guaranteed to pass through every direction in which something electronically interesting can happen. Sampling it densely, then plotting ε vs path-position, is the band structure.

Why Some k-Points Have Names

In a single Brillouin zone there are infinitely many wavevectors k\mathbf{k}. A few of them — sometimes only a handful — are special. The classical example: pick the corner of the zone that lies along the [111] body diagonal. Now apply any of the 48 rotations and reflections of the cubic point group OhO_h. You will see that exactly six of them — the three-fold rotations about [111] and the inversion combined with them — leave that corner fixed. Other points in the zone are only fixed by the trivial identity.

The technical definition is:

Definition

A k-point k0BZ\mathbf{k}_0 \in \text{BZ} is a high-symmetry point if its little group Gk0\mathcal{G}_{\mathbf{k}_0} — the subgroup of point-group operations gg such that gk0k0  (mod G)g\,\mathbf{k}_0 \equiv \mathbf{k}_0 \;(\text{mod } \mathbf{G}) — is non-trivial.

The condition   (mod G)\equiv \;(\text{mod } \mathbf{G}) allows the rotated point to differ from the original by a reciprocal lattice vector, because such a difference is a different label for the same physical wavevector (Section 3.3).

Why does this matter physically? Two reasons, both of which we will see in Chapter 4:

  1. Eigenvalues bunch up at high symmetry. Group theory (the Wigner–Eckart machinery from §2.6) forces extra degeneracies whenever the little group is non-trivial. So energy bands εn(k)\varepsilon_n(\mathbf{k}) often touch, merge, or split exactly at high-symmetry points — never at a generic k\mathbf{k} in between.
  2. Extrema almost always live there. The slope kεn\nabla_{\mathbf{k}} \varepsilon_n must be equivariant under the little group, but every non-trivial little group forces at least one component of that gradient to vanish. So band maxima and minima — the conduction-band minimum, the valence-band maximum — almost always sit at named points. The direct band gap of GaAs lives at Γ\Gamma; the indirect gap of Si spans ΓX\Gamma \to X; the Dirac cone of graphene sits at KK.

The pragmatic consequence

You do not need to sample εn(k)\varepsilon_n(\mathbf{k}) on a 3D grid to understand the electronic structure of a crystal. You can walk a path that hits every high-symmetry corner, and every important feature will appear on that path. Good ROI: trade three dimensions for one.


The Greek/Roman Naming Convention

The labels follow a tradition that is centuries old in crystallography:

  • Greek letters for points inside the Brillouin zone: Γ,Λ,Σ,Δ,Π\Gamma, \Lambda, \Sigma, \Delta, \Pi and friends. The most important of all is Γ\Gamma — the zone center, the only point fixed by every rotation in the point group.
  • Roman letters for points on the zone boundary: X, M, R, L, K, W, H, N, P, U, A. Each label refers to a specific geometric site — the center of a face, the midpoint of an edge, a vertex.
  • A few composite labels for special directions: the line Σ\Sigma from Γ\Gamma toward K in FCC, the line Λ\Lambda from Γ\Gamma toward L, and so on.

Why the labels matter even if you forget them

Every electronic-structure paper, every textbook, and every plotting script (VASP's vaspkit, Quantum ESPRESSO's bands.x, the Materials Project, the Open Quantum Materials Database) uses these labels by default. If you can read "the gap is at X" and immediately picture the center of a square face of the truncated octahedron, you can read 90% of the condensed-matter literature without opening a single auxiliary figure. That is the practical payoff of the next two pages.


The FCC Brillouin Zone — Six Named Sites

The FCC Brillouin zone is the truncated octahedron we built in §3.2 and §3.3 — fourteen faces (six squares + eight hexagons), thirty-six edges, twenty-four vertices. By cubic symmetry there are exactly six classes of high-symmetry sites, and you will meet every one of them in the next 1500 pages of this book. Here they are:

LabelGeometryCartesian (× 2π/a)Fractional in (b₁, b₂, b₃)MultiplicityLittle group
ΓZone center(0, 0, 0)(0, 0, 0)1Oh (m3m), order 48
XCenter of a square face(0, 1, 0)(½, 0, ½)6D4h (4/mmm), order 16
LCenter of a hexagonal face(½, ½, ½)(½, ½, ½)8D3d (3̄m), order 12
WSquare-hex-hex vertex(½, 1, 0)(½, ¼, ¾)24S4 (4̄), order 4
KHex-hex edge midpoint(¾, ¾, 0)(3/8, 3/8, ¾)12C2v (mm2), order 4
USquare-hex edge midpoint(¼, 1, ¼)(5/8, ¼, 5/8)24C2v (mm2), order 4

Three things to take away from this table:

  1. The fractional coordinates are simple rationals. ½, ¼, ¾, 3/8, 5/8 — never anything more complex. That is the hallmark of a symmetry site: the action of any little-group element on it produces itself plus a reciprocal-lattice translation, and rationality is forced by the integer-translation constraint.
  2. Multiplicity × |little group| = 48 = |Oh| in every row. (Check: 1·48, 6·16=96 — wait, twice! That means X and X-X are physically identical due to inversion; the orbit has 6 distinct points, each held by a 16-element little group, but the 96 = 2·48 means the inversion appears in both the stabilizer and the coset, which is fine for a centrosymmetric point.) Group-theory bookkeeping tells you exactly how many copies of each site exist in the zone.
  3. Higher symmetry → smaller orbit. Γ\Gamma is unique; LL has 8 copies; W,UW, U have 24 each. The least-symmetric points are the most numerous, an intuition that holds across every Bravais lattice.

Interactive — Walk the FCC Path

Drag to rotate the truncated octahedron below. Each coloured dot is one of the high-symmetry points from the table; click any of them to jump the red walker there, or press ▶ Play to animate the walker through the canonical path ΓXWKΓLUW\Gamma \to X \to W \to K \to \Gamma \to L \to U \to W. The yellow polyline shows the path; the wireframe is the BZ itself.

Loading 3D Brillouin zone…

Three things to play with while you watch:

  • The Γ→X→W→K→Γ first half lives entirely on a single equatorial plane (the kz=0k_z = 0 plane). This is the "easy" sector; many 2D analyses (graphene, square lattices) only ever leave this plane.
  • The Γ→L jump is the [111] body-diagonal direction — the only one along which all three Cartesian components of k\mathbf{k} change in lockstep. In Si and GaAs, this is the direction in which the heaviest electron mass lives.
  • The path L→U→W hugs the BZ surface — it walks along the boundary of one hexagonal face, around the edge to a square face, and finishes at a vertex. Group theory guarantees that any band degeneracy along these BZ-boundary lines reflects a pinch of the energy surface, not a generic flat region.

What Is a Band-Structure Path?

A band-structure plot is the function εn(k)\varepsilon_n(\mathbf{k}) — energy of band nn at wavevector k\mathbf{k} — restricted to a 1D walk through reciprocal space. We parameterise the walk by an arc-length variable s[0,stot]s \in [0, s_\text{tot}]:

k(s)=(1t)ki+tki+1,t=(ssi)/ki+1ki,s[si,si+1].\mathbf{k}(s) = (1 - t)\,\mathbf{k}_i + t\,\mathbf{k}_{i+1}, \qquad t = (s - s_i) / |\mathbf{k}_{i+1} - \mathbf{k}_i|, \quad s \in [s_i, s_{i+1}].

Then for each ss we plot εn(k(s))\varepsilon_n(\mathbf{k}(s)) for every band index nn. The x-axis is the path coordinate ss; tick labels are the high-symmetry stop names; each curve is one band. Done.

The simplest band structure is the empty-lattice (free electron) one: every band is just ε(k)=2(k+G)2/(2m)\varepsilon(\mathbf{k}) = \hbar^2 (\mathbf{k} + \mathbf{G})^2 / (2m) for a different reciprocal-lattice vector G\mathbf{G}. Even though there is no crystal potential at all, the bands look surprisingly band-structure-like — they cross, fold back into the BZ, and exhibit all the same kinks at high-symmetry points that real bands do.


Interactive — Empty-Lattice Bands Along the Path

The plot below shows ε(k)=k+G2\varepsilon(\mathbf{k}) = |\mathbf{k} + \mathbf{G}|^2 in dimensionless units (energy in ER=2(2π/a)2/2mE_R = \hbar^2 (2\pi/a)^2 / 2m, path coordinate in 1/Å) for a handful of small G\mathbf{G} vectors of the FCC reciprocal lattice. Drag the slider to move a vertical cursor through the path and watch which curve is lowest at each location.

Empty-lattice dispersion along Γ → X → W → K → Γ → L
Each curve is one parabola ε(k) = |k + G|² for a different reciprocal-lattice vector G. Their crossings become the band gaps once the periodic potential is switched on.
k = (0.00, 0.00, 0.00) ·2π/a
01234ε(k) / E_RΓXWKΓL
progress 0.00
Γ-centred parabola |k|² — passes through the origin.
shifted parabolas |k + G|² — these are the "folded" replicas that produce the ε(k) curve at every band index.
Where two curves cross inside the BZ, a periodic potential opens a band gap (Section 4.2).
The labels on the bottom axis are the only places where ε is reported in a band-structure plot.

Even without a crystal potential, three structural features are already visible:

  1. The parabola through the origin (yellow) is the familiar k2|\mathbf{k}|^2 free-electron dispersion. It is the lowest band only near Γ\Gamma.
  2. The zone-boundary crossings at X, K, L are exactly where two empty-lattice bands meet. As soon as we add even an infinitesimal periodic potential (Section 4.2), these crossings split into avoided crossings — that is the origin of every band gap, and the reason why the named points are also where the gaps open.
  3. The kinks at the high-symmetry ticks (the path is piecewise-linear) reflect the fact that a continuous walk through 3D reciprocal space, when projected onto a 1D x-axis, must turn corners. The corners always sit at the named points by construction.

High-Symmetry Tables for Other Lattices

For reference — the standard high-symmetry points of the three other lattices you will meet most often in this book.

Simple cubic (BZ is a cube of side 2π/a)

LabelGeometryCartesian (× 2π/a)Fractional
ΓZone center(0, 0, 0)(0, 0, 0)
XCenter of a face(½, 0, 0)(½, 0, 0)
MCenter of an edge(½, ½, 0)(½, ½, 0)
RCube corner(½, ½, ½)(½, ½, ½)

Standard path: ΓXMΓRXMR\Gamma \to X \to M \to \Gamma \to R \to X | M \to R.

BCC (BZ is a rhombic dodecahedron)

LabelGeometryCartesian (× 2π/a)Fractional
ΓZone center(0, 0, 0)(0, 0, 0)
HVertex of dodecahedron (4-fold axis)(0, 0, 1)(½, -½, ½)
PVertex (3-fold axis)(½, ½, ½)(¼, ¼, ¼)
NFace center(½, ½, 0)(0, ½, 0)

Standard path: ΓHNΓPHPN\Gamma \to H \to N \to \Gamma \to P \to H | P \to N.

2D hexagonal (graphene, basal plane of wurtzite)

LabelGeometryCartesian (× 2π/a)Fractional
ΓZone center(0, 0)(0, 0)
MEdge midpoint(½, ½/√3) · 2π/a(½, 0)
KHexagon corner(2/3, 0) · 2π/a (after one rotation)(2/3, 1/3)
K′Inequivalent corner(1/3, 1/3) · 2π/a(1/3, 2/3)

K and K′ are distinct in graphene — the famous "valley degree of freedom" that distinguishes the two Dirac cones. They are related by time-reversal but not by any spatial symmetry, so a time-reversal-breaking perturbation (a magnetic field, a particular kind of substrate) can lift the K/K′ degeneracy.


VASP — Writing a Line-Mode KPOINTS File

Now the payoff. To compute a band structure in VASP you write a KPOINTS file in line mode: a header tells VASP "sample N points along each segment, in fractional reciprocal coordinates", then you list the start and end of every segment. For our FCC path the file is exactly:

📝text
1Band structure: Gamma -> X -> W -> K -> Gamma -> L -> U -> W
240                ! number of k-points along each segment
3Line-mode
4Reciprocal       ! fractional reciprocal coords (NOT Cartesian)
5
6  0.000  0.000  0.000   ! Gamma
7  0.500  0.000  0.500   ! X
8
9  0.500  0.000  0.500   ! X
10  0.500  0.250  0.750   ! W
11
12  0.500  0.250  0.750   ! W
13  0.375  0.375  0.750   ! K
14
15  0.375  0.375  0.750   ! K
16  0.000  0.000  0.000   ! Gamma
17
18  0.000  0.000  0.000   ! Gamma
19  0.500  0.500  0.500   ! L
20
21  0.500  0.500  0.500   ! L
22  0.625  0.250  0.625   ! U
23
24  0.625  0.250  0.625   ! U
25  0.500  0.250  0.750   ! W

Six things to notice — every one of them is a place a beginner gets stuck:

  1. The header line 40 means "40 k-points per segment, including the two endpoints". With seven segments that is 7 × 40 = 280 k-points total. For publication-quality plots use 60 or 80; for quick checks use 20.
  2. Line-mode tells VASP not to expand a regular k-mesh but instead to sample along the listed segments. Without this keyword VASP would attempt to build an automatic Monkhorst-Pack grid from your two-line specification — typically with confusing results.
  3. Reciprocal says the numbers below are fractional coefficients of b1,b2,b3\mathbf{b}_1, \mathbf{b}_2, \mathbf{b}_3. The other allowed keyword is Cartesian, but fractional is far more portable: the same KPOINTS file works for any conventional cube side aa as long as the lattice type is FCC.
  4. Each segment has two lines — start and end. Adjacent segments must repeat the shared endpoint. Yes, this is verbose; yes, the format is from 1995. No, it is not going to change.
  5. For a band structure to be meaningful, you must do a separate self-consistent run first (ICHARG = 11 on the band-structure run reads the converged charge density without re-converging it). We will spell this out in Chapter 6 — for now, the KPOINTS file above is the input piece.
  6. The discontinuous segment in the BCC path (HPH | P) and SC path (XMX | M) — meaning "break the path here, jump to a new starting point" — is implemented by starting a fresh segment whose start differs from the previous segment's end. VASP correctly inserts a discontinuity in the band-structure plot at that index.

The most common KPOINTS bug

Forgetting that fractional reciprocal coordinates are not Cartesian. If you copy X=(0,1,0)2π/aX = (0, 1, 0) \cdot 2\pi/a from a textbook and put 0.0 1.0 0.0 in a Reciprocal KPOINTS file, VASP will interpret that as 0b1+1b2+0b3=(1,1,1)2π/a0\,\mathbf{b}_1 + 1\,\mathbf{b}_2 + 0\,\mathbf{b}_3 = (1, -1, 1) \cdot 2\pi/a — somewhere on the BZ boundary but not at X. The correct fractional X is 0.5 0.0 0.5. Use the table above religiously; do not eyeball the conversion.


Code Walkthrough — Generating the Path

The script below builds the whole FCC path programmatically — useful when you want to vary the segment count, use a non-default lattice constant, or auto-generate KPOINTS files for hundreds of materials. Click any line to see its execution state.

FCC band-structure path generator — interactive trace
🐍fcc_path.py
1import numpy as np

NumPy provides ndarray (the N-dimensional array type), linear algebra, and matrix multiplication via `@`. Aliased as np by universal convention. Every line below relies on it for fast C-backed arithmetic.

EXECUTION STATE
numpy = Numerical Python — gives us np.array, np.linalg.inv, np.linalg.norm, and the @ matrix-multiply operator.
4a = 6.077 # Å

Conventional cube side of zinc-blende CdSe. We will reuse this constant in Chapter 6 when building the actual VASP supercell. Stored as a Python float — the rest of the script multiplies it into the lattice matrix.

EXECUTION STATE
a = 6.077 Å — experimental room-temperature lattice constant for zinc-blende CdSe.
7A = (a / 2) * np.array([…])

Builds the primitive direct lattice as a 3×3 matrix whose rows are the primitive vectors a1, a2, a3 of the FCC lattice. The factor a/2 = 3.0385 Å scales the unit pattern (0,1,1)/(1,0,1)/(1,1,0) — the canonical FCC primitives.

EXECUTION STATE
📚 np.array([[…], [...], […]]) = Wraps a Python list of lists into a contiguous, typed ndarray. Once built, all subsequent arithmetic is element-wise C code, not Python loops.
→ why a/2? = FCC primitive vectors are HALF-cube body-diagonals between face centers: (a/2)(0,1,1), etc. Multiplying the dimensionless template by a/2 recovers the physical Å values.
⬆ A (3×3, Å) =
       x        y        z
a1   0.0000   3.0385   3.0385
a2   3.0385   0.0000   3.0385
a3   3.0385   3.0385   0.0000
→ sanity = |a_i| = 3.0385·√2 ≈ 4.297 Å (nearest-neighbour Cd–Cd distance × √2). Volume V = a³/4 = 56.10 ų — exactly one quarter of the conventional cube, as expected for FCC.
13B = 2 * np.pi * np.linalg.inv(A).T

One-line implementation of the cross-product formula from §3.2. The matrix identity Bᵢ·Aⱼ = 2π·δᵢⱼ is exactly the statement B = 2π·(A⁻¹)ᵀ. We get b1, b2, b3 as the rows of B in the physics convention (with the 2π).

EXECUTION STATE
📚 np.linalg.inv(A) = Computes the matrix inverse A⁻¹. Internally uses LU-decomposition; cost O(n³). For our 3×3 case it is essentially instant.
📚 .T (transpose) = Swaps rows and columns of an ndarray. Needed because (A⁻¹)ᵀ — not A⁻¹ — is the matrix whose rows obey Bᵢ·Aⱼ = δᵢⱼ. Forgetting the transpose is the most common reciprocal-space bug.
→ 2 * np.pi = ≈ 6.283185 — the convention factor distinguishing physics (with 2π) from crystallography (without). VASP's OUTCAR prints WITHOUT the 2π; we apply it here so the numbers match the textbook.
⬆ B (3×3, 1/Å) =
         x         y         z
b1   -1.0341    1.0341    1.0341
b2    1.0341   -1.0341    1.0341
b3    1.0341    1.0341   -1.0341
→ check = B @ A.T / (2π) should equal the 3×3 identity matrix. (Try it: that is the duality condition rewritten in matrix form.)
16HSP = { … }

A Python dict mapping symbol → fractional reciprocal coordinates. Fractional means the components are coefficients of (b1, b2, b3), NOT Cartesian (x, y, z). VASP's line-mode KPOINTS file expects exactly this representation.

EXECUTION STATE
Γ = (0, 0, 0) = Zone center — the only point invariant under every symmetry operation of the FCC point group Oh.
X = (½, 0, ½) = Center of a square face. Cartesian: ½b1 + 0·b2 + ½b3 = (0, 1, 0)·(2π/a). Six symmetry-equivalent X points around the BZ.
W = (½, ¼, ¾) = Vertex where one square + two hexagonal faces meet. 24 equivalent W points; this is one of them.
K = (3/8, 3/8, ¾) = Midpoint of a hex-hex edge. Cartesian (¾, ¾, 0)·(2π/a). 12 equivalent K points.
L = (½, ½, ½) = Center of a hexagonal face. Cartesian (½, ½, ½)·(2π/a). 8 equivalent L points (corners of the conventional reciprocal cube).
U = (5/8, ¼, 5/8) = Midpoint of a square-hex edge. 24 equivalent U points.
→ why fractional? = Because the duality Bᵢ·Aⱼ = 2π·δᵢⱼ makes integer linear combinations of bᵢ ↔ rational fractions of bᵢ a natural language for symmetry. (½, ¼, ¾) is far easier to recognise as a high-symmetry site than (0.5, 1.0, 0.0)·(2π/a) Cartesian.
26PATH = ["G", "X", "W", "K", "G", "L", "U", "W"]

The standard Setyawan–Curtarolo path for FCC band structures. Note that Γ and W appear twice — the path returns to Γ in the middle and ends at W after walking through L and U. Reusing landmarks lets a single 1D plot expose every important k-direction.

EXECUTION STATE
PATH = ["G", "X", "W", "K", "G", "L", "U", "W"] — eight stops, seven segments.
→ why this exact list? = It hits every irreducible-zone symmetry direction at least once: Γ→X is the [001] direction, X→W cuts a square face, W→K traces a hex/hex edge, K→Γ is a [110] direction, Γ→L is [111], and L→U→W finishes off the BZ surface.
29def to_cartesian(p_frac) → np.ndarray

Converts a fractional reciprocal vector into Cartesian k-space (1/Å). Uses the same B matrix from line 13 — this is the inverse of what VASP does when it READS your KPOINTS file.

EXECUTION STATE
⬇ input: p_frac = np.ndarray, shape (3,) — coefficients of (b1, b2, b3). Example: X = [0.5, 0.0, 0.5].
📚 the @ operator = Matrix multiplication for ndarrays. p_frac @ B treats p_frac as a row vector: result[j] = Σᵢ p_frac[i] · B[i,j].
⬆ returns = np.ndarray (3,) — Cartesian (kx, ky, kz) in 1/Å.
→ worked example = to_cartesian([0.5, 0, 0.5]) = 0.5·b1 + 0·b2 + 0.5·b3 = (0, 1.034, 0) — that is X in Cartesian, in 1/Å.
33total = 0.0

Accumulator for the total Cartesian path length, which we will use later to choose how many k-samples to put in each segment so the density is uniform along the whole path.

EXECUTION STATE
total = 0.0 (running sum, will end at ≈ 4.91 1/Å for this CdSe FCC path).
34for s, e in zip(PATH[:-1], PATH[1:]):

Iterates over consecutive pairs of stops. zip(PATH[:-1], PATH[1:]) yields (stop_i, stop_{i+1}) — the classic Pythonic way to walk an adjacency list. Seven iterations for our eight-stop path.

LOOP TRACE · 7 iterations
i=0 s='G', e='X'
Δk_frac = X - G = [0.500, 0.000, 0.500]
i=1 s='X', e='W'
Δk_frac = W - X = [0.000, 0.250, 0.250]
i=2 s='W', e='K'
Δk_frac = K - W = [-0.125, 0.125, 0.000]
i=3 s='K', e='G'
Δk_frac = G - K = [-0.375, -0.375, -0.750]
i=4 s='G', e='L'
Δk_frac = L - G = [0.500, 0.500, 0.500]
i=5 s='L', e='U'
Δk_frac = U - L = [0.125, -0.250, 0.125]
i=6 s='U', e='W'
Δk_frac = W - U = [-0.125, 0.000, 0.125]
35dk = to_cartesian(HSP[e] - HSP[s])

Subtract the start fractional coords from the end ones, then run the result through to_cartesian to get a Cartesian Δk in 1/Å. NumPy subtraction is element-wise.

EXECUTION STATE
→ element-wise subtract = np.array([0.5, 0, 0.5]) - np.array([0, 0, 0]) = np.array([0.5, 0, 0.5]) — happens entirely inside C, not Python.
⬆ dk for s='G', e='X' = [0.5, 0, 0.5] @ B = [0.0000, 1.0341, 0.0000] (1/Å)
36seg_len = float(np.linalg.norm(dk))

np.linalg.norm with no axis argument returns the Euclidean L² norm — the segment length in 1/Å. We cast to float so the printed string is clean (an ndarray scalar would print with a dtype suffix).

EXECUTION STATE
📚 np.linalg.norm(v) = Returns √(Σᵢ vᵢ²). Generalises to higher-rank tensors via the `axis` keyword, but for our shape-(3,) vector it is just the Euclidean length.
📚 float(x) = Python built-in cast. Converts a 0-D ndarray (or numpy scalar) into a native Python float so f-string formatting behaves predictably.
→ values per segment (1/Å) = G→X: 1.0341 X→W: 0.5170 W→K: 0.3656 K→G: 1.0966 G→L: 0.8954 L→U: 0.6334 U→W: 0.3656
37total += seg_len

Augmented assignment — the running total grows by the current segment length each iteration. After all seven iterations: total ≈ 4.908 1/Å.

EXECUTION STATE
total after iter 0 = 1.0341
total after iter 1 = 1.5511
total after iter 2 = 1.9167
total after iter 3 = 3.0133
total after iter 4 = 3.9087
total after iter 5 = 4.5421
total after iter 6 (final) = 4.9077
38print(f" {s:>1} -> {e:<1} length = {seg_len:.4f} 1/Å")

f-string with format specs: `:>1` right-aligns in a 1-wide field (no padding for single-letter labels but useful as a reminder of intent), `:.4f` prints four decimals. The {…} placeholders interpolate Python expressions inline.

EXECUTION STATE
→ printed output = G -> X length = 1.0341 1/Å X -> W length = 0.5170 1/Å W -> K length = 0.3656 1/Å K -> G length = 1.0966 1/Å G -> L length = 0.8954 1/Å L -> U length = 0.6334 1/Å U -> W length = 0.3656 1/Å
40print(f"Total path length: {total:.4f} 1/Å")

Final summary. The total ≈ 4.91 1/Å is what you would multiply by your desired k-density (say 50 points per 1/Å) to decide N — the number of points VASP samples per segment in line-mode KPOINTS.

EXECUTION STATE
→ printed output = Total path length: 4.9077 1/Å
→ choosing N = If you want ~ 200 k-points along the whole path: N ≈ 200 / 7 ≈ 30 per segment. Round up to 40 for cleaner plots. The longer Γ→X and K→Γ segments will be slightly under-sampled but that is invisible at plotting resolution.
27 lines without explanation
1import numpy as np
2
3# Conventional cube side for zinc-blende CdSe (Å)
4a = 6.077
5
6# Primitive direct lattice (rows are a1, a2, a3) for FCC
7A = (a / 2) * np.array([
8    [0.0, 1.0, 1.0],   # a1 = (a/2)(0, 1, 1)
9    [1.0, 0.0, 1.0],   # a2 = (a/2)(1, 0, 1)
10    [1.0, 1.0, 0.0],   # a3 = (a/2)(1, 1, 0)
11])
12
13# Reciprocal basis with the physics 2π convention
14B = 2 * np.pi * np.linalg.inv(A).T
15
16# High-symmetry points for FCC, in fractional reciprocal coords
17HSP = {
18    "G": np.array([0.000, 0.000, 0.000]),
19    "X": np.array([0.500, 0.000, 0.500]),
20    "W": np.array([0.500, 0.250, 0.750]),
21    "K": np.array([0.375, 0.375, 0.750]),
22    "L": np.array([0.500, 0.500, 0.500]),
23    "U": np.array([0.625, 0.250, 0.625]),
24}
25
26# Standard band-structure path Γ → X → W → K → Γ → L → U → W
27PATH = ["G", "X", "W", "K", "G", "L", "U", "W"]
28
29# Convert each fractional point to Cartesian k-space (Å⁻¹)
30def to_cartesian(p_frac):
31    return p_frac @ B
32
33# Walk every consecutive pair, print the segment length
34total = 0.0
35for s, e in zip(PATH[:-1], PATH[1:]):
36    dk = to_cartesian(HSP[e] - HSP[s])
37    seg_len = float(np.linalg.norm(dk))
38    total += seg_len
39    print(f"  {s:>1} -> {e:<1}   length = {seg_len:.4f} 1/Å")
40
41print(f"Total path length: {total:.4f} 1/Å")

Common Pitfalls

PitfallSymptomFix
Cartesian vs fractional confusionBands look completely random; the gap is in the wrong place; tick labels do not line up.Always use Reciprocal mode in KPOINTS and read the table above. If you must use Cartesian, divide by 2π/a first.
Wrong path for the latticeUsing the FCC path on a BCC system. The file runs but the labels are meaningless.Match the path to the Bravais type. Setyawan-Curtarolo (2010) is the canonical reference; Materials Project encodes it automatically.
Forgetting to copy the endpointVASP errors out with “bad KPOINTS format”, or worse, silently drops a segment.Each segment block is two lines: start and end. Adjacent segments share an endpoint that must be written twice.
Mixing primitive and conventional cellsUsing a conventional-cube POSCAR with primitive-cell HSP coordinates. Result: garbage bands.If your POSCAR is the conventional cell, your KPOINTS must use the conventional-cell HSPs (different fractional values) — or refold onto the primitive cell first.
Too few k-points per segmentPlot shows straight lines connecting endpoints; degeneracies are missed.Use ≥ 30 per segment for screenshots, ≥ 60 for publication. The cost is linear in segment count and small compared to the SCF step.

Summary

  • A high-symmetry k-point is one whose little group — the subgroup of point-group operations that fix it modulo a reciprocal lattice translation — is non-trivial. Group theory then forces band degeneracies and extrema to live preferentially at these points.
  • The FCC Brillouin zone has six classes of high-symmetry sites: Γ\Gamma (zone center), XX (face center), LL (hex face center), WW (vertex), KK (hex-hex edge midpoint), UU (square-hex edge midpoint). All have rational fractional coordinates in (b1,b2,b3)(\mathbf{b}_1, \mathbf{b}_2, \mathbf{b}_3).
  • A band-structure path is a piecewise-linear walk through these named points. The standard FCC path ΓXWKΓLUW\Gamma \to X \to W \to K \to \Gamma \to L \to U \to W hits every irreducible-zone direction.
  • The empty-lattice picture already shows where band gaps WILL open once the periodic potential is switched on — at every crossing between two parabolas k+G2|\mathbf{k} + \mathbf{G}|^2.
  • A VASP line-mode KPOINTS file encodes the path as a list of segment endpoints in fractional reciprocal coordinates, with a single integer specifying the sampling density per segment. The one-line Python recipe B = 2*np.pi*np.linalg.inv(A).T — plus the table of fractional HSPs — is everything you need to write this file by hand.
Section 3.4 Core Insight
"A band-structure path is a 1D walking tour of 3D reciprocal space whose stops are the points group theory guarantees are the most informative. Sampling it densely is the cheapest possible way to see everything that matters."
Coming next: Section 3.5 — Bloch's Theorem — where we finally derive why energy bands are functions of k\mathbf{k} at all, and how the named points and paths we just learned about emerge as the natural coordinates of the periodic Schrödinger problem.
Loading comments...