Skip to content

Fermi surfaces, and two bugs that a missing dependency had been hiding - #18

Merged
Starlitnightly merged 1 commit into
mainfrom
fermi-surfaces
Aug 5, 2026
Merged

Fermi surfaces, and two bugs that a missing dependency had been hiding#18
Starlitnightly merged 1 commit into
mainfrom
fermi-surfaces

Conversation

@Starlitnightly

Copy link
Copy Markdown
Contributor

mv.elec.fermi_surface computes Fermi surfaces through IFermi: total area, how many disconnected sheets, and whether there is a surface at all.

Checked against arithmetic, not against another code

A free-electron band, E = ħ²k²/2m, has a spherical Fermi surface of area 4πk_F² exactly. So the computed area needs no DFT to validate. Simple cubic, a = 4 Å, zone boundary at 0.785 Å⁻¹:

E_F (eV) k_F (Å⁻¹) area 4πk_F² ratio
0.4 0.324 1.3216 1.3193 1.002 inside
0.8 0.458 2.6355 2.6386 0.999 inside
1.5 0.628 4.9978 4.9474 1.010 inside
3.0 0.887 6.5513 9.8948 0.662 clipped

The last row is not an error. The sphere has grown past the zone boundary and is genuinely truncated — the area that remains is the one that carries current.

A line-mode band structure is refused rather than interpolated: Fourier interpolation needs a grid, a high-symmetry line has no interior, and feeding one in produces a plausible surface rather than an error. An insulator is reported as zero sheets, not raised.

Two bugs that had nothing to do with Fermi surfaces

Adding IFermi pulled BoltzTraP2 in as a dependency, and that removed a mask.

mv.elec.transport guarded on the wrong thing. import BoltzTraP2 succeeds without touching netCDF; the modules it actually uses do not. The guard started passing on an installation that cannot run — netCDF4 built against a different numpy raises numpy.dtype size changed, a ValueError, which was not caught either. It now imports the submodules it reaches, catches both, and says this is a rebuild rather than a missing package. The test's skip condition had the identical shallow check and the identical fix.

Underneath that, the tutorial had been calling it wrongly. mv.elec.transport(metals, bands) — where transport wants one BandStructure per material and bands is the bands-axis object with one row per band. It had been wrong for a long time and was invisible: the missing BoltzTraP2 raised ImportError first, and the cell's except swallowed it. Removing the import failure exposed the argument error underneath.

Cost

Not cheap. Interpolation at the default factor of five takes minutes for one 12×12×12 mesh and scales as the cube of the factor — a shortlist tool, not a library one. The notes say so.

The fermi extra pins netcdf4<1.7 and adds cmake: 1.7.4 has no wheel for an older glibc and its source build wants netCDF headers, and BoltzTraP2 needs cmake at build time. Three layers, each blocking once.

Verification

971 pass on py3.12, 851 on py3.10. Contract-verified rate stays 165/165. All 16 notebooks execute. Docs build.

🤖 Generated with Claude Code

https://claude.ai/code/session_016wh5NzNE9Wn49nuUm7zg2a

mv.elec.fermi_surface computes Fermi surfaces through IFermi: total area, how
many disconnected sheets, and whether there is a surface at all.

The check that makes it trustworthy needs no DFT. A free-electron band,
E = hbar^2 k^2 / 2m, has a spherical Fermi surface of area 4 pi kF^2 exactly, so
the computed area can be checked against arithmetic rather than against another
code. On a simple cubic cell with a = 4 A it matches to within 1% at kF of
0.324, 0.458 and 0.628 - and falls to 0.66 of the sphere at kF = 0.887, which is
where the sphere has grown past the zone boundary at 0.785 and is genuinely
truncated. That clipping is physics; the area that remains is the one that
carries current.

A line-mode band structure is refused rather than interpolated. Fourier
interpolation needs a grid and a high-symmetry line has no interior, so feeding
one in produces a plausible surface rather than an error. An insulator, where no
band crosses the level, is reported as zero sheets rather than raised.

Adding it turned up two bugs that had nothing to do with Fermi surfaces.

mv.elec.transport guarded on `import BoltzTraP2`, which succeeds without
touching netCDF, while the modules it actually uses do not. IFermi pulls
BoltzTraP2 in as a dependency, so the guard started passing on an installation
that cannot run - netCDF4 built against a different numpy raises "numpy.dtype
size changed", which is a ValueError and was not caught either. It now imports
the submodules it reaches and catches both, and says that this is a rebuild
rather than a missing package. The test's skip condition had the same shallow
check and has the same fix.

Underneath that, the tutorial had been calling mv.elec.transport(metals, bands)
where transport wants one BandStructure per material and `bands` is the
bands-axis object with one row per band. It had been wrong for a long time and
nobody could see it: the missing BoltzTraP2 raised ImportError first and the
cell's `except` swallowed it. Removing the import failure exposed the argument
error underneath.

IFermi is not cheap - interpolation at the default factor of five takes minutes
for one 12x12x12 mesh and scales as the cube of the factor - and the notes say
so. The fermi extra pins netcdf4 below 1.7 and adds cmake, because 1.7.4 has no
wheel for an older glibc and BoltzTraP2 needs cmake at build time.

971 tests pass on py3.12, 851 on py3.10. Contract-verified rate stays 165/165,
all 16 notebooks execute, docs build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wh5NzNE9Wn49nuUm7zg2a
@Starlitnightly
Starlitnightly merged commit 544f41a into main Aug 5, 2026
6 checks passed
@Starlitnightly
Starlitnightly deleted the fermi-surfaces branch August 5, 2026 01:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant