Skip to main content

diffpes

License PyPI Downloads PyPI version Python Versions Documentation Status tests codecov DOI Ruff ty jax_badge Lines of Code

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

Graphene tight-binding band structure along Gamma-K-M-Gamma next to the simulated ARPES spectrum of the same path

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 same Dirac cone cut simulated with 20, 80, and 250 meV self-energy linewidths

The self-energy is an explicit model parameter — the same cut at 20, 80, and 250 meV linewidth.

Gapped honeycomb band structure and the simulated ARPES spectrum showing only the occupied valence band

Stagger the two onsite energies and the Dirac crossing opens into a gap; ARPES sees the valence band.

Momentum-summed Fermi edge simulated at 25, 100, and 300 kelvin

The Fermi edge at 25, 100, and 300 K.

Across the Brillouin zone

Four constant-energy ARPES maps of graphene: Dirac points, trigonally warped pockets, the van Hove crossing at M, and a Gamma-centered ring

Constant-energy slices: Dirac points grow into trigonally warped pockets, touch at the van Hove singularity, and close into a ring around Γ.

Simulated Fermi surface of hole-doped graphene with pockets at every zone corner

Move the Fermi level and the Fermi surface follows — hole-doped by 1 eV.

Three-dimensional pi and pi-star band surfaces of graphene with six Dirac points

Gaussian-broadened pi-band density of states with van Hove peaks and the occupied part shaded

The intensity cube

Translucent three-dimensional rendering of the simulated ARPES intensity cube around the Dirac point

The full cube I(kx, ky, E) around one Dirac point, rendered by intensity transparency over its deepest constant-energy slice.

Three energy-window integrals of the Dirac cone cube showing rings collapsing onto the apex

Energy-window integrals of the cube — rings collapse onto the cone apex.

Stacked energy distribution curves and momentum distribution curves through the Dirac cone

EDC and MDC stacks pulled from the same cut.

Through the detector

Intrinsic spectral function beside the same spectrum after analyser optics, resolution, background, and Poisson counting

The same physics before and after the instrument: analyser calibration, point-spread functions, transmission, background, exposure, and counting.

Expected detector image beside one Poisson-sampled acquisition of the same spectrum

Expected counts and one reproducible Poisson acquisition.

Expected photoelectron counts over the detector plane

Difference of expected detector counts between two photon polarizations

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

Bi2Se3 slab band structure read from a VASP EIGENVAL file next to its simulated occupied ARPES map along M-Gamma-M

A real material, straight from VASP output: the Bi₂Se₃ slab bands along M–Γ–M and their occupied spectrum at 35 K.

Near-Fermi window of the Bi2Se3 slab spectrum with quantum-well-split conduction states above the valence manifold

The near-Fermi window of the same calculation, sharpened to a 12 meV linewidth.

Simulated spectra of the Bi2Se3 six-quintuple-layer slab and of bulk Bi2Se3 on the same M-Gamma-K-M path, with in-gap states only in the slab

Slab and bulk calculations on the same M–Γ–K–M path: the slab carries states inside the bulk gap.

Stacked energy distribution curves around Gamma from the Bi2Se3 slab spectrum

EDCs around Γ from the same map.

Normalized densities of states of the Bi2Se3 slab and bulk from DOSCAR files

Planar-averaged CHGCAR charge density of the slab showing six quintuple layers separated by van der Waals gaps

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

diffpes-2026.6.13.tar.gz (642.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

diffpes-2026.6.13-py3-none-any.whl (772.1 kB view details)

Uploaded Python 3

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

Hashes for diffpes-2026.6.13.tar.gz
Algorithm Hash digest
SHA256 a97bd2fd49093780d142f10733144209fa9236695b3b154d9514aca71d4e0966
MD5 1ab24e4c6f097a9775a581665ea4f2d6
BLAKE2b-256 18c4fb35b0d9d1be1c559747155a911822d4edb80a53b78bca0f4431770d7322

See more details on using hashes here.

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

Hashes for diffpes-2026.6.13-py3-none-any.whl
Algorithm Hash digest
SHA256 b87e1db00aa3e20cd32d69205bfbed86fddbf3d4688585eef62219933f70314c
MD5 e0893c60a49d7cd18aa2c55a4ce492af
BLAKE2b-256 b88b91484e7395aa3b67ce6c1cfee7a554dac1b8290ef0606bc669420fe8ebe4

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

2026.6.13 This release

2 files

2026.6.12

2 files

2026.6.11

2 files

2026.6.9

2 files

2026.6.4

2 files

2026.6.3

2 files

2026.6.2

2 files

2026.6.1

2 files

2026.3.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page