Quick start
End-to-end: get a tight-binding Hamiltonian from the API, project it into a near-Fermi subspace, and run a surface Green’s function on the result.
Note
This guide assumes you have tailwater installed
(pip install tailwater) and a username/password issued by the
Tailwater team. The client talks to the hosted API at
https://api.tailwater.io automatically — no configuration needed.
1. Get the artifacts from the API
project=True returns the artifacts you need for fine-tuning and
analysis in one credit-billed call:
from pymatgen.core import Structure
from tailwater import tw_api_call
structure = Structure.from_file("MyMaterial.cif")
paths = tw_api_call(
structure = structure,
user = "acme-research",
password = "...",
output_path = "./outputs",
filename = "my_material",
project = True,
)
# paths = {"embeddings": "...", "npz": "...", "win": "..."}
# The Hamiltonian comes back as a sparse SparseHR (wannier90_hr.npz).
# For small systems (< 30 atoms) it is also auto-converted to a dense
# HDF5, added under paths["hdf5"] — see :doc:`exporting_models`.
The returned dict always contains a "win" key — the parsed
wannier90.win file the server actually ran inference on, useful
for tracing graph-construction differences across API and offline
runs.
2. (Optional) Fine-tune the heads to fit a near-Fermi window
Fit the output heads to the predicted Hamiltonian’s eigenvalues inside a
narrow energy window (a compact, downfolded model), using the bundle’s
embeddings.pt + sparse wannier90_hr.npz:
from tailwater import subspace_projection
subspace_projection(
start_lr = 1e-4,
end_lr = 1e-5,
num_epochs = 20,
energy_range = (-2.0, 2.0), # eV, relative to E_F
decay_sigma = 0.5,
device = "cpu",
save_path = "./projection_out",
embed_path = paths["embeddings"],
hr_npz_path = paths["npz"], # fit the heads to the sparse Hamiltonian
)
After training, ./projection_out/ contains a fine-tuned heads
checkpoint, a projected (subspace-restricted) HDF5 model, and a
.basis.json describing the orbital basis of the projection.
3. Analyze the model
For semiconductors and insulators, anchor the energy zero to the valence band maximum so every calculator below shares a physically natural reference — see Fermi alignment for semiconductors and insulators for the full guide.
import numpy as np
from tailwater import (
as_tbmodels, align_to_vbm,
SurfaceGreensFunction, BulkDOS, bulk_band_structure,
)
model = as_tbmodels(paths["npz"]) # dense tbmodels.Model from the sparse .npz
model = align_to_vbm(model) # (optional, for non-metals) VBM -> 0
# Bulk DOS (KPM, k-mesh averaged)
dos = BulkDOS(model, k_mesh=(8, 8, 8), energies=(-4, 4)).run()
dos.figure.savefig("bulk_dos.png")
# Surface Green's function (Lopez-Sancho).
# n_jobs=-1 parallelizes across every CPU core — see :doc:`performance`.
sgf = SurfaceGreensFunction(
model, surface=np.eye(3),
energies=np.linspace(-1, 1, 201),
k_path=[[0, 0.5, 0], [0, 0, 0], [0.333, 0.333, 0]],
k_labels=["M", r"$\Gamma$", "K"],
n_jobs=-1,
).run()
sgf.figure_top.savefig("surface_top.png")
# Bulk band structure (auto k-path via seekpath, if installed)
fig = bulk_band_structure(model, auto=True, structure=structure,
spacing=0.02, e_range=(-3, 3))
fig.savefig("bands.png")
# 2D Fermi-arc / constant-energy surface map at E = 0
# See :doc:`fermi_arcs` for the full guide.
from tailwater import FermiArcMap
arc = FermiArcMap(
model, surface=np.eye(3),
energy=0.0, Nx=40, Ny=40,
thickness=6, NN=5, eps=0.005,
n_jobs=-1,
).run()
arc.figure_top_interpolated.savefig("fermi_arc_top.png")