Repository: Unfolding technique implemented with DFTK.jl
Author: @physarief78
Summary
This README documents a low-memory, chunked pipeline to perform a supercell self-consistent-field (SCF) calculation with DFTK, compute chunked eigenvalues for DOS, compute band structure along a primitive-cell path using the supercell wavefunctions, and unfold the supercell bands onto the primitive Brillouin zone (BZ). The implementation is intentionally memory-conservative (waves and large arrays are quickly dropped and garbage-collected) and chunked so it runs under limited RAM budgets.
Included example results:
src_code/Si_exapmples/results/Si_Supercell_Unfolded.png— pristine 2×2×2 supercell unfolded bands + DOSsrc_code/Si_exapmples/results/Si_Supercell_PType_Unfolded.png— P-type supercell unfolded bands + DOSsrc_code/Si_exapmples/results/Si_Supercell_NType_Unfolded.png— N-type supercell unfolded bands + DOS
-
Build primitive and supercell models — define lattice, atomic positions, pseudopotentials, create
model_DFTfor both primitive cell and supercell geometry. -
SCF on supercell — run
self_consistent_fieldon the supercell to obtain the converged density$\rho_{SC}$ and Fermi energy$\varepsilon_F$ . Immediately save only the density and Fermi energy to disk and drop the wavefunction-heavy objects from memory. -
Chunked eigenvalue extraction for DOS — iterate over each supercell k-point (or small groups) and call
compute_bands(..., ρ=ρ_{SC})to compute eigenvalues at that k-point. Collect eigenvalues only (no persistent ψ storage). Use DFTK smearing to compute a DOS curve. - Chunked band-path computation — build a path in the primitive BZ (e.g. L → Γ → X → K → Γ), map these primitive path k-points to the supercell reciprocal coordinates, and compute bands for those k-points in small chunks. For each supercell band at each supercell k-point, compute an unfolding spectral weight onto primitive k-points using the supercell plane-wave coefficients.
- Plot — scatter unfolded bands (energy vs primitive-path coordinate) where color (and/or marker area) encodes the spectral weight; plot the DOS beside it.
Note: the code is optimized for low-memory operation: it processes k-points in chunks, drops heavy objects, and forces GC.gc() to reduce peak memory.
This section translates the implementation logic into compact mathematical statements so you can verify and reproduce the approach independently.
Let the primitive direct lattice be given by the 3×3 matrix of column vectors
so that
For the supercell (constructed by integer multiples along direct axes), the supercell direct lattice matrix is
The coordinates used in DFTK are consistent with these definitions.
For a supercell calculation at supercell Bloch vector
where
We want to discover whether a supercell state
then that plane-wave component of the supercell eigenstate maps onto the primitive k-point
In coordinates used by the code we compute for each supercell G-vector a difference vector in Cartesian form
and compare it against the primitive target
should be an integer triplet (within a small numerical tolerance). Code checks all(x -> abs(x - round(x)) < tol, Δ) with tol ≈ 1e-3.
When a plane-wave component labeled by
The code builds the weights by summing abs2(coeffs[iG, ib]) for all matching G-vectors. After summing, a small cutoff (e.g., weights > 0.01) is applied to remove negligibly-weighted contributions in the plotted unfolded bands.
The weights satisfy a normalization property across primitive k-points (for a complete decomposition), i.e. the sum of the weights across all primitive k-points for a given supercell
The density of states unfolded onto the primitive BZ or just the total DOS can be computed by smearing eigenvalues. Let
where the code uses DFTK's smearing function and samples all supercell k-points. If one wants an unfolded DOS resolved per primitive k-point, insert the weight:
In the example code the DOS plotted on the side is the total DOS (summed over supercell k-points) and is converted from Hartree energies to electronvolts using
- DFTK returns energies in Hartree (Ha). The code uses
E_eV = (E_Ha - ε_F) * 27.2114to shift by the Fermi energy and express energies relative to$E_F$ in eV. - Reciprocal transformations use the
reciprocal_latticefrombasis.model(DFTK).
build_primitive_si()andbuild_supercell_si()construct primitive and supercell lattices and atomic lists.- Running SCF on the supercell yields
scfres.ρ(saved to a.jld2file) andscfres.εF. - For memory efficiency,
compute_bands(basis_sc_scf, kgrid_obj; n_bands=320, ρ=ρ_sc)is called for a single k-point or small k-point chunk. Onlyres.eigenvalues(andres.ψtemporarily for unfolding) are used — and cleared after the chunk is processed. - Unfolding logic lives in the nested loop where the code constructs
q_cart = K_sc_cart + recip_lattice_sc * G_intand checks thatn_check = (lattice_prim' * diff) / 2πis close to integer. - We accumulate
plot_k,plot_E,plot_Warrays that are plotted withCairoMakieas a scatter plot wherecolor = weights.
-
Install Julia (v1.9+ recommended) and add packages used in the script:
DFTK,PseudoPotentialData,CairoMakie,JLD2,FFTW,StaticArrays, etc. -
Put the script
si_unfold_dos_fully_chunked.jland thefigures/images into a git repository. -
Run the script with multiple threads to speed up embarrassingly-parallel parts:
julia -t 4 si_unfold_dos_fully_chunked.jlAdjust -t to the number of threads available. If memory is tight, reduce CHUNK_SIZE.
Parameters to tune in the script
E_CUT— plane-wave energy cutoff (Ha). Increase for convergence.KGRID_PRIM,KGRID_SC— k-point grids for primitive and supercell.n_bandspassed tocompute_bands— must be large enough to include all bands of interest.CHUNK_SIZE— number of path k-points computed per chunk (smaller reduces memory peak at cost of more calls).tolfor integer-check in mapping — default1e-3in the code.
Place the example images into figures/ and inspect them. The left panel shows unfolded bands (scatter) along the primitive path L → Γ → X → K → Γ. The color/marker intensity represents the spectral weight: bright/dark points indicate strong primitive character in that supercell eigenstate at that primitive k. The right panel shows the total DOS computed from the supercell eigenvalues. The horizontal dashed line marks the Fermi energy (0 eV on the energy axis).
- The unfolding quality depends on commensurability between the supercell and primitive cell. If supercell is not an integer repetition, unfolding becomes ambiguous.
- The method is plane-wave based: all mapping relies on a consistent plane-wave indexing between primitive and supercell reciprocals.
- Numerical tolerances (for integer matching) must be set carefully — too loose and false matches appear, too strict and genuine matches are lost.
- The
weights > 0.01plotting cutoff is arbitrary; choose a threshold appropriate to your system and basis size.
README.md (this file)
si_unfold_dos_fully_chunked.jl
figures/
├─ Si_Supercell_NType_Unfolded.png
├─ Si_Supercell_PType_Unfolded.png
└─ Si_Supercell_Unfolded.png


