Skip to main content

kpenvelope

PyPI DOI tests

A solver for the quantum states of holes confined in thin semiconductor layers (wurtzite crystals such as GaN and AlN). It computes the energy levels and wavefunctions from the standard six-band k.p model of Chuang and Chang, solved together with the electrostatics of the charge itself, so the confining potential is not guessed -- it emerges from the balance between the fixed interface charge and the hole gas. Built for polarization-induced two-dimensional hole gases, and usable for any layered wurtzite structure you can supply cited parameters for.

Install

pip install kpenvelope

For development: clone the repository and pip install -e .[test].

Quick start

import numpy as np
from kpenvelope import (gan_rinke2008, sheet_density_from_cm2,
                        solve_self_consistent)

p = gan_rinke2008()                        # cited GaN parameter set
z = np.linspace(0.0, 6.0, 97)              # grid in nm, from the interface
ps = sheet_density_from_cm2(4.6e13)        # measured density, lab units in
res = solve_self_consistent(p, z, ps, temperature_K=300.0)
# res.energies, res.masses, res.density, res.occupations, res.converged

Conventions, stated once: energies in eV on the valence-electron scale (holes occupy the highest eigenvalues), lengths in nm, sheet densities in nm^-2. sheet_density_from_cm2 / sheet_density_to_cm2 convert to and from the cm^-2 numbers a lab quotes, exactly.

What it can do

Energy levels and wavefunctions. solve_subbands solves the six-band problem on a 1D grid at any in-plane momentum, with hard walls at the grid ends. solve_heterostructure lifts that restriction: layered stacks with position-dependent material parameters and band offsets (built with layered_profile), using the symmetrized discretization that keeps the problem exactly Hermitian for any profile. Strain enters through the Bir-Pikus terms (strain_blocks), per layer, when you supply cited deformation potentials.

Self-consistency with the charge. solve_self_consistent (hard-wall) and solve_self_consistent_hetero (finite barriers) iterate the quantum problem with Gauss's law until the potential and the charge agree, at your measured sheet density and temperature (temperature_K; the default 0 reproduces the historical cold filling exactly). The result carries a converged flag, so an iteration-starved run reports its failure instead of hiding it. Filling uses either parabolic subbands (closed-form, including the finite-temperature closed form) or the full computed dispersion on a k-grid (fill_subbands_kgrid), which refuses a momentum window the occupied states outgrow.

What the states are made of. band_character resolves each state into heavy-hole, light-hole and split-off fractions, and character_vs_k tracks how the composition changes with momentum -- the physics behind "one subband, many masses": the top subband is pure heavy-hole at zero momentum, and its mass moves as other components mix in.

Numbers an experiment measures. subband_dispersion and local_mass give the energy-versus-momentum curves and the local effective mass (a hole mass is not one number, and a flat band honestly reports an infinite mass). spin_splitting / splitting_vs_k quantify the splitting an asymmetric potential induces. group_velocity and dos_from_dispersion are the ingredients every transport estimate needs. dipole_matrix and oscillator_strengths give the intersubband optical matrix elements and dimensionless strengths that set absorption spectra and detector design -- and, new in v0.9, depolarization_shift applies the collective correction between the two: a doped well's measured resonance sits above the subband spacing by the depolarization shift (Allen/Tsui/Vinter 1976; Ando/Fowler/Stern 1982), the effect every absorption experiment must subtract before comparing with the band structure, with isb_lineshape giving the strength-weighted spectrum at your measured linewidth.

Calibrate the numbers it refuses to ship

The band offset of YOUR heterostructure and the sheet density in YOUR well are exactly the numbers no package should invent -- and both are routinely inferred from the same measured intersubband resonance. The lab tools close that loop through the package's own solver, pure NumPy:

from kpenvelope import (fit_band_offset, offset_sensitivity,
                        sheet_density_from_shift)

# The offset that makes the calculated spacing match your resonance
# (you supply the profile shape; the scale is calibrated):
fit = fit_band_offset(z, params_list, band_edge_unit, e_meas_ev=0.0042,
                      bracket=(0.4, 1.5), sigma_e_ev=1e-4)
print(fit["offset_ev"], "+/-", fit["sigma_offset_ev"])

# The sheet density from the measured depolarization-shifted
# resonance -- an exact closed-form inversion, not a fit:
inv = sheet_density_from_shift(z, env_i, env_j, e_i, e_j,
                               e_meas_ev=0.21, eps_r=10.4)

Both refuse rather than guess: a bracket that does not straddle the measured spacing is refused with the calculated values at both ends; a transition that barely feels the offset is refused (a deep-well measurement cannot calibrate a barrier height it never probes -- offset_sensitivity answers that before the measurement); and a resonance at or below the bare spacing is refused, because the depolarization shift only pushes the resonance up. One convention worth knowing: at k = 0 the solver's states come in exactly degenerate Kramers pairs, so the first intersubband spacing is between states 0 and 2 -- the tools' default.

Cited parameter sets

No physical number in this package is made up, and none is accepted without a source -- the reference field of WurtziteParameters is mandatory.

  • gan_rinke2008(): the GW-based GaN valence set of Rinke et al., Phys. Rev. B 77, 075202 (2008) (A1..A6, Delta_CR = 10 meV, Delta_SO = 17 meV), as tabulated in Extended Data Table 1 of Chang et al., Nature Electronics 9, 346 (2026). eps_r = 10.4 (field along the c axis) from Barker and Ilegems, Phys. Rev. B 7, 743 (1973).
  • aln_rinke2008(): the matching AlN set (Delta_CR = -295 meV from Rinke et al.; Delta_SO = 22 meV from de Carvalho et al., Appl. Phys. Lett. 97, 232101 (2010)), intended as a barrier material. Its permittivity is deliberately NaN and the self-consistent solver refuses to run on it: no vetted value is shipped, and none is needed for a barrier.
  • demo_single_band(): a synthetic, decoupled set the test suite uses because it has exact textbook solutions. Labeled non-physical.

For any other material, populate WurtziteParameters from the literature (e.g. Vurgaftman and Meyer, J. Appl. Phys. 94, 3675 (2003)) and record the source. No default band offset is shipped either: alignments are material- and strain-specific, so layered_profile takes them from you, with a citation.

How it is checked

Every physics claim in the test suite (62 tests, Python 3.9-3.13, run in CI on every push) is anchored to a closed form, an exact identity, or two independent code paths agreeing -- never to a stored number:

  • the assembled matrix is exactly Hermitian with every coupling on, and the layered assembly reduces to the uniform one at machine precision when every layer is the same material;
  • the decoupled well reproduces the textbook square-well levels (hard wall and finite barrier, including the analytic decay into the barrier);
  • the cited GaN set lands on its closed-form splittings (5.20 and 21.80 meV) and asymptotic masses (1.89 and 0.18 m0);
  • the finite-temperature filling matches direct numerical integration of the Fermi-Dirac occupation (two independent code paths) and reduces to the cold filling as T goes to 0;
  • filling conserves charge exactly, the k-grid filler agrees with the closed-form filler on an exactly parabolic model, and the strain terms are validated by a structural identity with the kinetic template;
  • the intersubband dipoles reproduce the textbook closed forms, obey the f-sum rule, respect parity selection, and are invariant under a shift of the coordinate origin;
  • the depolarization geometry integral matches an independent quadrature over the analytic well wavefunctions, is exactly unchanged by a shift of the origin, and scales exactly with the well width; the shift formula's defining identity holds to machine precision.

One comparison against the source paper is on record and stated honestly: a hard-wall run at the measured density puts the gas centroid at 0.62 nm against the paper's 0.568 nm, with the same subband structure; the difference is the filling model (parabolic here, computed-dispersion there). Do not publish self-consistent numbers without checking the barrier and filling model against your own system.

Honest limits

Deliberate scope -- designed out with reasons, not overlooked: scattering lifetimes (roughness, impurities, phonons) need cited screening and roughness parameters per structure, and this package ships no number it cannot source, so it provides the DOS and velocity factors every lifetime integral needs instead; the Poisson solve uses one cited permittivity (not a spatially varying profile); and k-linear bulk-inversion-asymmetry terms beyond the six-band Chuang-Chang model are not included.

Associated paper

T. M. Mahim, A. S. M. Mohsin and M. M. Rahman, "Origin of the conflicting hole masses in the GaN/AlN two-dimensional hole gas" (under review); code for the paper: https://github.com/Tanvir-Mahmud-Mahim/gan-2dhg-masses-lifetimes

and S. L. Chuang and C. S. Chang, Phys. Rev. B 54, 2491 (1996). This package is the general-purpose tool; the paper repository reproduces the specific published study.

Support and governance

Written and maintained by Tanvir Mahmud Mahim (Department of Electrical and Electronic Engineering, BRAC University), who reviews every change and takes the final decision on scope and releases. Design questions are discussed in the open in issues and pull requests, and the standing rule of CONTRIBUTING.md binds the maintainer exactly as it binds contributors: a change that touches physics arrives with a test, and a constant arrives with its source.

Support runs through the issue tracker. Usage questions are welcome alongside bug reports; a docstring that left a unit or a sign convention unclear is treated as a documentation bug, not user error. While the version is below 1.0 the API may still move between minor versions; such changes are called out in the release notes.

License

Apache-2.0. Every release is archived on Zenodo under the concept DOI 10.5281/zenodo.22015269, which always resolves to the latest version.

Metadata

Release files for kpenvelope 0.10.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for kpenvelope 0.10.0
File Size Uploaded
kpenvelope-0.10.0.tar.gz 50.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for kpenvelope 0.10.0
File Interpreter ABI Platform
kpenvelope-0.10.0-py3-none-any.whl Python 3 none any Details

Total release size: 90.8 kB

Release files / kpenvelope-0.10.0.tar.gz

Download URL kpenvelope-0.10.0.tar.gz
Size 50.9 kB
Tags Source
SHA-256 checksum
How to use checksums
f7b67140a65cdb858f5e966d4853a025dad58e531294c2918e38695c06babfa7
BLAKE2b-256 checksum
How to use checksums
5adc8956c37002bf8ba2cff4e85bdac1a92d48f99487aaf62aaf71f7f3a77fb9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release files / kpenvelope-0.10.0-py3-none-any.whl

Download URL kpenvelope-0.10.0-py3-none-any.whl
Size 39.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3bac451cb3d0cce70934e87eeee3bbf038d83c5e7fc7deaab3833661c2153539
BLAKE2b-256 checksum
How to use checksums
002fced49b2283c8e8d641aeebb20ef3d4bb22ac45e39d4c460c14f3698c5db1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release history Release notifications | RSS feed

0.13.1

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.1

2 release files

0.11.0

2 release files

This release

0.10.0 This release

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release 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