How-to guides

Task-oriented recipes. Each snippet is self-contained given a wannierised model/res (Workflow A) or a checkpoint on disk; the .win-keyword route via bin/postw90.jl is noted where it exists. The docstrings (see API) carry the full keyword lists.

Run the drop-in binaries

From a repository clone (or install launchers with install_cli() and drop the prefix):

julia --project=. bin/wannier90.jl -pp seedname     # .nnkp generation (byte-identical)
julia --project=. bin/wannier90.jl seedname         # wannierise; writes .wout/.chk/_hr/_tb/bands
julia --project=. bin/postw90.jl seedname           # all postw90 modules per the .win keywords
julia --project=. bin/w90chk2chk.jl -export seedname   # .chk → .chk.fmt   (-import: reverse)

postw90.jl dispatches on the same keywords as postw90.x: berry/berry_task (ahc, morb, kubo, sc, shc, kdotp), gyrotropic, dos, kpath, kslice, geninterp, boltzwann, spin_moment, plus the module-specific controls (berry_kmesh, fermi_energy*, kubo_*, shc_*, dos_*, boltz_*, …) — and writes the reference-named .dat files in the reference formats. See compatibility for the exact keyword semantics.

Disentanglement recipes

# energy windows (classic)
res = run_wannier(model; win_min=-5.0, win_max=17.0, froz_max=6.4)

# PDWF: freeze by projectability, not energy (robust against intruding states)
dis = disentangle(model; win_min=εF-20, win_max=εF+10,
                  froz_proj=true, proj_min=0.02, proj_max=0.95)

# dis_spheres: disentangle only inside k-spheres (k-localised entanglement)
dis = disentangle(model; win_max=20.0, spheres=[(SVector(0.5,0.5,0.5), 0.4)],
                  sphere_first_wann=1)

# SCDM for the entangled case: energy-smeared automatic projections
A = scdm_projections(model; dir="UNK_dir", mode=:erfc, mu=εF, sigma=2.0)   # from UNK files
# (or in-memory: wannier_model(scfres; num_wann, num_bands, scdm_mode=:erfc, scdm_mu, scdm_sigma))

# scdm_auto: fit the SCDM erfc (μ, σ) from the projectability-vs-energy curve (Vitale et al.),
# so even those two numbers are automatic. Pass a projectability matrix + energies, or an .amn
# array (projectability computed as the trial-space projector diagonal):
fit = scdm_auto(A, model.eig)          # ⇒ (; mu, sigma, mu_fit, sigma_fit, rms)
A   = scdm_projections(model; dir="UNK_dir", mode=:erfc, mu=fit.mu, sigma=fit.sigma)
# A LARGE `rms` means the projectability is not erfc-like — then character-based PDWF, not
# SCDM-erfc, is the right tool (e.g. graphene π vs σ; see example 08 minimal). scdm_auto is at
# its best on the PSEUDO-ATOMIC projectabilities Quantum ESPRESSO's projwfc/atomic_proj writes
# (the Vitale setting): there P(ε) drops cleanly and the (μ, σ) fit is well-conditioned. A
# crude hydrogenic guess can give a degenerate fit even when the manifold is separable — the
# quality of the projectability basis, not just the physics, decides whether the fit is clean.

Localisation variants

res = wannierise(model; algorithm=:rcg)                     # native CG (default)
res = wannierise(model; algorithm=:w90, num_iter=500)       # reference-exact trajectory
res = wannierise(model; algorithm=:gamma)                   # Γ-only real-orthogonal sweeps
# via .win keywords, all through run_wannier(seedname):
#   guiding_centres, precond, slwf_num/slwf_constrain/slwf_lambda/slwf_centres (SLWF+C),
#   site_symmetry + .dmn (symmetry-adapted WFs), use_ss_functional (Stengel–Spaldin),
#   gamma_only, higher_order_n (higher-order finite differences)

Symmetry-adapted WFs read seedname.dmn (pw2wannier90's write_dmn) and work for both the isolated case and combined with disentanglement (the constrained ΩI optimiser); the gauge then satisfies U(Rk) = d(R)·U(k)·D(R)† to the `symmetrizeeps` tolerance.

Berry-phase physics (postw90 parity)

All modules consume a BerryModel (from seedname.chk(.fmt) + .eig + .mmn, or a TB file, or in-memory operators):

bm = BerryModel("seedname")                                  # honours use_ws_distance

ahc = anomalous_hall(bm; fermi_energy=12.6279, kmesh=(25,25,25))          # S/cm
scan = ahc_fermiscan(bm; fermi_energies=11.0:0.1:13.0, kmesh=(25,25,25),
                     adpt_kmesh=5, adpt_thresh=100.0)                     # adaptive refinement
kubo = optical_conductivity(bm; fermi_energy, freqs=0:0.01:7, kmesh=(25,25,25))
σS, σA = kubo_S(kubo, 1, 1, 10), kubo_A(kubo, 1, 2, 10)                  # tensor components

mm = MorbModel("seedname"; transl_inv_full=true)             # needs .uHu
M = orbital_magnetisation(mm; fermi_energy, kmesh=(25,25,25))            # μ_B/cell

sm = ShcModel("seedname")                                    # needs .spn; ShcRyooModel: .sHu/.sIu
shc = shc_fermiscan(sm; fermi_energies, kmesh=(15,15,15))                # (ħ/e)·S/cm
shc = shc_tetra(sm; kmesh=(15,15,15), fermi_energies)                    # tetrahedron method

sc = shift_current("seedname"; fermi_energy, freqs, kmesh=(25,25,25))    # σ_abc(0;ω,−ω)
η  = injection_current(bm; freqs, fermi_energy, kmesh=(12,12,12), smr_width=0.1)
kp = kdotp(bm; kpoint=[0,0.5,0], bands=[4,5])                            # Löwdin orders 0–2
gy = gyrotropic(mm; tasks=(:D0,:Dw,:C,:K,:NOA,:dos), fermi_energies, freqs,
                kmesh=(25,25,25), smr_width=0.1)

DOS, transport coefficients and structure plots:

es, dos = density_of_states(bm; energies=8:0.01:14, kmesh=(25,25,25))    # adaptive smearing
es, dos, up, dn = density_of_states(bm; energies, spin=SpinModel("seedname"))  # spin-decomposed
r = boltzwann(bm; kmesh=(30,30,30), relax_time=10.0, mus=[5.0], temps=[300.0],
              win=(minimum(eig), maximum(eig)))                          # σ, S, κ, TDF

kp = kpath(bm; segments=kpath_segments(win), tasks=(:bands,:curv), fermi_energy)
write_kpath("seedname", kp)                                              # reference file set
ks = kslice(bm; b1=[...], b2=[...], mesh=(200,200), tasks=(:bands,:curv), fermi_energy)
write_kslice("seedname", ks.coords, ks.bands; curv=ks.curv)
tabulate_3d(...) ; write_frmsf(...)                                      # FermiSurfer export

Ballistic transport (Landauer)

For a system periodic along one axis (transport = true, transport_mode = bulk in the .win, or directly):

model, win, res = run_wannier("seedname")
energies, qc, dos = run_transport(model, win, res)     # writes _qc.dat/_dos.dat (+_htB.dat)
# or from explicit principal-layer blocks:
energies, qc, dos = transport_bulk(H00, H01; win_min=-3.0, win_max=3.0, energy_step=0.01)

tran_lcr (lead–conductor–lead) is not implemented — see compatibility.

Symmetrised (irreducible-BZ) integration

With a .sym file (pw2wannier90) or a point group filtered from Oₕ:

sym = read_sym("seedname.sym")                        # Cartesian rotations
reps, wts, _ = irreducible_kmesh((25,25,25), sym; kaction=:cart, lattice=bm.lattice)
ahc, n = anomalous_hall_sym(bm, sym; fermi_energy, kmesh=(25,25,25))     # pseudovector rule
M, n   = orbital_magnetisation_sym(mm, sym; fermi_energy, kmesh=(25,25,25))
es, d, n = density_of_states_sym(bm, sym; energies, kmesh=(25,25,25))

Typical speed-up: the full mesh collapses to its irreducible wedge (e.g. 78/512 points for bcc Fe's magnetic group) with results equal to the full-BZ sum.

Tight-binding model input (no chk/mmn/eig)

Interpolate the entire physics stack from a _hr.dat or _tb.dat alone — the WannierBerri System_tb workflow:

bm = tb_model("seedname_tb.dat")                      # H(R) + r(R) → full BerryModel
anomalous_hall(bm; fermi_energy, kmesh=(25,25,25))
# hr-only files work for everything that doesn't need the position operator

write_tb/write_hr produce the same files from a finished run, so models round-trip.

In-memory models (DFTK or any Julia DFT)

wmodel = wannier_model(scfres; num_wann=4)                        # SCDM, projection-free
wmodel = wannier_model(scfres, projections; num_wann, num_bands)  # explicit trial orbitals
# or fully manual, from your own arrays:
wmodel = wannier_model(; unit_cell, kpoints, mp_grid, num_wann, M, A, kpb, gpb, eig)

Everything downstream (disentanglement, localisation, hamiltonian_operator, the Berry stack) treats these models identically to file-read ones.

Checkpoint interchange

chk = read_chk("seedname.chk")            # or read_chk_fmt; write_chk / write_chk_fmt

Checkpoints interoperate with wannier90.x/postw90.x in both directions — their restart=plot reproduces its own bands from our .chk, and our BerryModel consumes theirs.