diffpes
Differentiable ARPES simulation in JAX: from tight-binding or DFT electronic structure to band structures, spectra, Fermi surfaces, and detector-level photoemission data. Gradients flow through the entire chain.
Tutorials · Guides · API reference
pip install diffpes
From bands to spectra
The graphene π bands and their simulated ARPES spectrum: the occupied band is bright. The Fermi function at 300 K cuts off the unoccupied band.
The self-energy is an explicit model parameter — the same cut at 20, 80, and 250 meV linewidth.
Stagger the two onsite energies and the Dirac crossing opens into a gap; ARPES sees the valence band.
The Fermi edge at 25, 100, and 300 K.
Across the Brillouin zone
Constant-energy slices: Dirac points grow into trigonally warped pockets, touch at the van Hove singularity, and close into a ring around Γ.
Move the Fermi level and the Fermi surface follows — hole-doped by 1 eV.
The intensity cube
The full cube I(kx, ky, E) around one Dirac point, rendered by intensity transparency over its deepest constant-energy slice.
Energy-window integrals of the cube — rings collapse onto the cone apex.
EDC and MDC stacks pulled from the same cut.
Through the detector
The same physics before and after the instrument: analyser calibration, point-spread functions, transmission, background, exposure, and counting.
Expected counts and one reproducible Poisson acquisition.
Rotate the photon polarization and difference the two acquisitions — matrix-element contrast at the detector.
First spectrum
This script is complete — a graphene π-band model, a momentum cut through the Dirac point, and the finite-temperature spectral function:
import jax.numpy as jnp
import matplotlib.pyplot as plt
from diffpes.plots import plot_arpes_spectrum
from diffpes.simul import assemble_spectral_intensity_bands_chunk
from diffpes.tightb import (
build_kpath,
diagonalize_tb,
kpath_arc_length,
kpoints_frac_to_cart,
)
from diffpes.types import (
make_arpes_spectrum,
make_crystal_geometry,
make_orbital_basis,
make_self_energy_model,
make_tb_model,
)
# Graphene pi bands: two carbon sites, six nearest-neighbour hoppings.
a = 2.46
crystal = make_crystal_geometry(
lattice=jnp.asarray(
[[a, 0.0, 0.0], [a / 2, a * 3**0.5 / 2, 0.0], [0.0, 0.0, 20.0]]
),
positions=jnp.asarray([[0.0, 0.0, 0.0], [1 / 3, 1 / 3, 0.0]]),
species=("C", "C"),
)
basis = make_orbital_basis(
atom_indices=(0, 1), n=(2, 2), l=(0, 0), m=(0, 0), labels=("pz_A", "pz_B")
)
model = make_tb_model(
hopping_amplitudes=-2.7 * jnp.ones(6, dtype=jnp.complex128),
onsite_energies=jnp.zeros(2),
soc_lambdas=jnp.zeros(0),
geometry=crystal,
basis=basis,
hopping_pairs=((0, 1), (0, 1), (0, 1), (1, 0), (1, 0), (1, 0)),
hopping_cells=(
(0, 0, 0), (-1, 0, 0), (0, -1, 0),
(0, 0, 0), (1, 0, 0), (0, 1, 0),
),
shell_index=(-1, -1),
)
# A straight momentum cut through the Dirac point at K.
path = build_kpath(
jnp.asarray([[0.0, 0.0, 0.0], [0.5, 1.0, 0.0]]),
crystal,
301,
("Gamma", "K'"),
)
bands = diagonalize_tb(model, path.kpoints)
# Occupied spectral function: Lorentzian self-energy + Fermi cutoff at 300 K.
energies = jnp.linspace(-9.2, 1.2, 480)
intensity = assemble_spectral_intensity_bands_chunk(
bands.eigenvalues,
jnp.ones((path.kpoints.shape[0], energies.shape[0], 2)),
energies,
make_self_energy_model(gamma=0.09),
jnp.asarray(0.0),
300.0,
allow_degenerate_value_only=True,
)
spectrum = make_arpes_spectrum(
intensity,
energies,
kpath_arc_length(path, crystal),
kpoints_frac_to_cart(path.kpoints, crystal),
)
plot_arpes_spectrum(spectrum, cmap="magma")
plt.show()
From DFT
A real material, straight from VASP output: the Bi₂Se₃ slab bands along M–Γ–M and their occupied spectrum at 35 K.
The near-Fermi window of the same calculation, sharpened to a 12 meV linewidth.
Slab and bulk calculations on the same M–Γ–K–M path: the slab carries states inside the bulk gap.
EDCs around Γ from the same map.
DOSCAR densities of states and the CHGCAR charge density, resolving all six quintuple layers.
diffpes.inout reads EIGENVAL, PROCAR, POSCAR, KPOINTS, OUTCAR,
DOSCAR, CHGCAR, WAVECAR, Wannier90 hr.dat/tb.dat, and Cartesian
hopping lists. Parsed eigenvalues and orbital projections drop into the same
spectral calls as tight-binding models, so a converged VASP calculation
becomes a simulated ARPES measurement.
Differentiable end to end
jax.grad, jax.vmap, and jax.jit work through the whole pipeline —
crystal geometry, hoppings, self-energy, matrix elements, experiment
geometry, and detector response to expected counts. Fit any of it to
measured spectra by gradient descent.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file diffpes-2026.6.13.tar.gz.
File metadata
- Download URL: diffpes-2026.6.13.tar.gz
- Upload date:
- Size: 642.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a97bd2fd49093780d142f10733144209fa9236695b3b154d9514aca71d4e0966
|
|
| MD5 |
1ab24e4c6f097a9775a581665ea4f2d6
|
|
| BLAKE2b-256 |
18c4fb35b0d9d1be1c559747155a911822d4edb80a53b78bca0f4431770d7322
|
File details
Details for the file diffpes-2026.6.13-py3-none-any.whl.
File metadata
- Download URL: diffpes-2026.6.13-py3-none-any.whl
- Upload date:
- Size: 772.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b87e1db00aa3e20cd32d69205bfbed86fddbf3d4688585eef62219933f70314c
|
|
| MD5 |
e0893c60a49d7cd18aa2c55a4ce492af
|
|
| BLAKE2b-256 |
b88b91484e7395aa3b67ce6c1cfee7a554dac1b8290ef0606bc669420fe8ebe4
|