Band Structures

Bands accepts a record of canonical band structures keyed by material label. Each dataset declares type: "phonon" | "electronic"; phonon frequencies are in THz and electronic energies in eV. Convert raw pymatgen data once with normalize_band_structure(raw) before passing it to a renderer.

Phonon Bands with Custom Styling

The symmetry-point labels on the x axis are clickable: each opens a small Brillouin zone marking that point on the k-path. The zone comes from the reciprocal lattice the band data carries (pymatgen’s lattice_rec, phonopy’s recip_lattice); pass a structure to build it from a crystal’s lattice instead.

Interactive example

svelte<script lang="ts">
  import { Bands } from 'matterviz'
  import { phonon_bands } from '#site/phonons/index.js'

  const band_structs = { '': phonon_bands['mp-2758-Sr4Se4-pbe'] }

  const line_kwargs = {
    acoustic: { stroke: '#e74c3c', stroke_width: 2 },
    optical: { stroke: '#3498db', stroke_width: 1.5 },
  }
</script>

<Bands {band_structs} {line_kwargs} />

Electronic Band Structures

The normalize_band_structure adapter converts pymatgen BandStructureSymmLine objects, including spin-keyed bands ("1" for spin-up, "-1" for spin-down), to canonical arrays. The demo fixtures below are converted when loaded. The Fermi level is drawn as a dashed line whenever the data carries efermi or the fermi_level prop is set:

Interactive example

svelte<script lang="ts">
  import { Bands } from 'matterviz'
  import { electronic_bands } from '#site/electronic/bands/index.js'
</script>

<Bands band_structs={{ '': electronic_bands.cao_2605 }} y_axis={{ label: 'Energy (eV)' }} />

Spin-Polarized Electronic Bands

Spin-polarized electronic band structures can be shown in overlay, up_only, or down_only mode:

Interactive example

svelte<script lang="ts">
  import { Bands } from 'matterviz'
  import { electronic_bands } from '#site/electronic/bands/index.js'
</script>

<Bands
  band_structs={{ '': electronic_bands.vbr2_971787 }}
  band_spin_mode="overlay"
  y_axis={{ label: 'Energy (eV)' }}
/>

Electronic Gap Annotation

For gapped electronic structures, Bands automatically annotates VBM/CBM and the gap (E_g). You can toggle this with show_gap_annotation:

Interactive example

svelte<script lang="ts">
  import { Bands } from 'matterviz'
  import { electronic_bands } from '#site/electronic/bands/index.js'
</script>

<Bands
  band_structs={{ '': electronic_bands.cao_2605 }}
  band_spin_mode="up_only"
  show_gap_annotation
  y_axis={{ label: 'Energy (eV)' }}
/>

Comparing Multiple Band Structures

Interactive example

svelte<script lang="ts">
  import { Bands } from 'matterviz'
  import { phonon_bands } from '#site/phonons/index.js'

  const band_struct = phonon_bands['mp-2758-Sr4Se4-pbe']

  // Create multiple versions with slight variations for demo
  const band_structs = {
    DFT: band_struct,
    'Model A': {
      ...band_struct,
      bands: band_struct.bands.map((band) => band.map((freq) => freq * 1.05)),
    },
    'Model B': {
      ...band_struct,
      bands: band_struct.bands.map((band) => band.map((freq) => freq * 0.95)),
    },
  }
</script>

<Bands {band_structs} show_controls controls_open />

Path Alignment Modes (strict, intersection, union)

When comparing multiple structures, path_mode="strict" (the default) fails fast if symmetry-path segments do not match exactly.

Interactive example

svelte<script lang="ts">
  import { Bands } from 'matterviz'
  import { phonon_bands } from '#site/phonons/index.js'

  const canonical = phonon_bands['mp-2758-Sr4Se4-pbe']
  const alt_path = {
    ...canonical,
    qpoints: [
      { label: 'GAMMA', frac_coords: [0, 0, 0] },
      { label: null, frac_coords: [0.3, 0.3, 0] },
      { label: 'K', frac_coords: [0.5, 0.5, 0] },
    ],
    branches: [{ start_index: 0, end_index: 2, name: 'GAMMA-K' }],
    labels_dict: { GAMMA: [0, 0, 0], K: [0.5, 0.5, 0] },
    distance: [0, 1, 2],
    bands: canonical.bands.map((band) => [band[0], band[2], band.at(-1) ?? band[0]]),
  }
</script>

<Bands band_structs={{ canonical, alt_path }} path_mode="strict" show_controls controls_open />

Switch to path_mode="intersection" to compare only shared segments, or path_mode="union" to show all segments.

Phonon Units and Highlight Regions

Use units to convert phonon frequencies on the y-axis, and highlight_regions to shade custom windows.

Interactive example

svelte<script lang="ts">
  import { Bands } from 'matterviz'
  import { phonon_bands } from '#site/phonons/index.js'
</script>

<Bands
  band_structs={{ '': phonon_bands['mp-2758-Sr4Se4-pbe'] }}
  units="cm^-1"
  highlight_regions={[
    { y_min: 40, y_max: 120, color: 'rgba(255, 193, 7, 0.35)', label: 'Target window' },
  ]}
  show_controls
  controls_open
/>

Imaginary Mode Shading

For phonons with negative frequencies, shade_imaginary_modes shades the y < 0 region by default. Disable it with shade_imaginary_modes={false} if you want a clean axis-only view.

Fat Bands (Band-Resolved Quantities)

Basic Fat Bands

Add band_widths to your band structure data - a 2D array matching the shape of bands where each value represents the ribbon width at that point:

Interactive example

svelte<script lang="ts">
  import { Bands } from 'matterviz'
  import { phonon_bands } from '#site/phonons/index.js'

  const base_bs = phonon_bands['mp-2758-Sr4Se4-pbe']

  // Simulate electron-phonon coupling: only certain bands couple strongly
  const coupled_bands = [1, 4, 7] // bands with significant coupling
  const band_widths = base_bs.bands.map((band, idx) =>
    band.map((_, q_idx) => {
      const pos = q_idx / band.length
      const envelope = Math.sin(pos * Math.PI) ** 0.5 // smooth peak at zone boundary
      return coupled_bands.includes(idx) ? envelope * (0.6 + 0.4 * Math.cos(idx)) : 0
    }),
  )

  const band_struct = { ...base_bs, band_widths }
</script>

<Bands band_structs={{ '': band_struct }} ribbon_config={{ opacity: 0.4, max_width: 8 }} />

Custom Ribbon Styling

Customize the ribbon appearance with ribbon_config. You can set color, opacity, maximum width, and scale factor:

Interactive example

svelte<script lang="ts">
  import { Bands } from 'matterviz'
  import { phonon_bands } from '#site/phonons/index.js'

  const base_bs = phonon_bands['mp-2758-Sr4Se4-pbe']

  // Simulate d-orbital character: peaks at specific k-points for select bands
  const band_widths = base_bs.bands.map((band, idx) =>
    band.map((_, q_idx) => {
      const pos = q_idx / band.length
      if (idx === 2) return Math.exp(-8 * (pos - 0.3) ** 2) // Gaussian peak near Γ
      if (idx === 5) return Math.exp(-8 * (pos - 0.7) ** 2) // Peak near zone edge
      if (idx === 8) return 0.5 * Math.sin(pos * Math.PI) ** 2 // Weaker, broad
      return 0
    }),
  )

  const band_struct = { ...base_bs, band_widths }
</script>

<Bands
  band_structs={{ '': band_struct }}
  ribbon_config={{ color: '#e74c3c', opacity: 0.4, max_width: 10 }}
  line_kwargs={{ stroke: '#2c3e50', stroke_width: 1.2 }}
/>

Comparing Structures with Fat Bands

When comparing multiple band structures, each can have its own band_widths. The ribbon color defaults to the line color for each structure:

Interactive example

svelte<script lang="ts">
  import { Bands } from 'matterviz'
  import { phonon_bands } from '#site/phonons/index.js'

  const base_bs = phonon_bands['mp-2758-Sr4Se4-pbe']

  // DFT vs ML: coupling on different bands with smooth Gaussian profiles
  const gauss = (pos: number, center: number, width: number) =>
    Math.exp(-((pos - center) ** 2) / width)
  const make_widths = (active_bands: number[], centers: number[]) =>
    base_bs.bands.map((band, idx) =>
      band.map((_, q_idx) =>
        active_bands.includes(idx)
          ? gauss(q_idx / band.length, centers[idx % centers.length], 0.08)
          : 0,
      ),
    )

  const band_structs = {
    DFT: { ...base_bs, band_widths: make_widths([1, 3, 6], [0.3, 0.5, 0.7]) },
    'ML model': {
      ...base_bs,
      bands: base_bs.bands.map((band) => band.map((freq) => freq * 1.015)),
      band_widths: make_widths([2, 5, 8], [0.4, 0.6, 0.8]),
    },
  }
</script>

<Bands {band_structs} ribbon_config={{ opacity: 0.4, max_width: 10 }} />

Supported Formats

The Bands component automatically detects and handles:

FormatKey FieldsDescription
Pymatgen Electronic@class: "BandStructureSymmLine", kpoints, bands (dict), lattice_recStandard pymatgen electronic band structure
Pymatgen Phonon@class: "PhononBandStructureSymmLine", qpoints, bands (array), lattice_recPymatgen phonon band structure (phonopy-convention recip_lattice, no 2π, is accepted too and scaled)
Native mattervizqpoints, bands (array), branches, distanceInternal matterviz format

Pymatgen inputs need the reciprocal lattice because k-path distances are measured in Cartesian reciprocal space (|Mᵀ·Δq|, like pymatgen and phonopy); the native format carries precomputed distance values instead. A pymatgen-shaped input without lattice_rec.matrix (or recip_lattice.matrix) is rejected with an error naming the missing key, which Bands shows in place of the plot.