Density of States (DOS)

The Dos component visualizes electronic and phonon density of states from canonical data keyed by series label.

Basic Usage

Pass a record to doses. Each entry declares type: "phonon" with frequencies in THz, or type: "electronic" with energies in eV, plus densities.

Interactive example

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

<Dos doses={{ '': phonon_dos['mp-2758-Sr4Se4-pbe'] }} />

Electronic DOS with Spin Polarization

Convert pymatgen CompleteDos and LobsterCompleteDos objects with normalize_dos at the input boundary. Spin-polarized data (stored as {1: [...], -1: [...]}) is automatically extracted and drawn according to spin_mode: mirror (default, spin-down below zero), overlay, up_only or down_only. Use shift_to_fermi() to center energies at E_F = 0:

Interactive example

svelte<script lang="ts">
  import { Dos } from 'matterviz'
  import { shift_to_fermi, normalize_dos, extract_pdos } from '#lib/spectral/helpers.js'
  import { dos_spin_polarization } from '#site/electronic/dos/index.js'

  const total_dos = normalize_dos(shift_to_fermi(dos_spin_polarization))
  if (!total_dos) throw new Error('Invalid DOS fixture')
</script>

{#if total_dos}<Dos doses={{ '': total_dos }} />{/if}

Projected DOS (pDOS)

Extract atom-resolved or orbital-resolved projections from CompleteDos using extract_pdos(raw, type):

Interactive example

svelte<script lang="ts">
  import { Dos } from 'matterviz'
  import { shift_to_fermi, normalize_dos, extract_pdos } from '#lib/spectral/helpers.js'
  import { dos_spin_polarization } from '#site/electronic/dos/index.js'

  let pdos_type = $state<'atom' | 'orbital'>('atom')
  const projected = $derived(extract_pdos(shift_to_fermi(dos_spin_polarization), pdos_type))
</script>

<label style="display: block; margin-bottom: 0.5em">
  Projection:
  <select bind:value={pdos_type}>
    <option value="atom">Atom-resolved (Ta, Zn, Co)</option>
    <option value="orbital">Orbital-resolved (s, p, d)</option>
  </select>
</label>

{#if projected}<Dos doses={projected} stack spin_mode="up_only" />{/if}

Stacking and Smearing

Use stack for filled areas, sigma for Gaussian smearing and normalize (max, sum, integral). Phonon DOS also accepts units (THz, eV, meV, Ha, cm^-1).

Interactive example

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

  const dos = phonon_dos['mp-2758-Sr4Se4-pbe']
  const doses = {
    'Mode A': { ...dos, densities: dos.densities.map((density) => density * 0.45) },
    'Mode B': { ...dos, densities: dos.densities.map((density) => density * 0.35) },
    'Mode C': { ...dos, densities: dos.densities.map((density) => density * 0.2) },
  }
</script>

<Dos {doses} normalize="max" sigma={0.1} stack />

Thermal Properties

thermal_properties(dos, temperatures, unit) integrates the Bose–Einstein occupation over a phonon DOS to give the harmonic free energy F, internal energy U, entropy S and heat capacity Cv (eV and eV/K per whatever the DOS integrates to, 3N modes per cell here). PhononThermalPlot draws them against temperature, in phonopy’s kJ/mol and J/(K·mol) by default, with F and U on the left axis and S and Cv on the right. For this simulated Sr4Se4 DOS (2 atoms in the primitive cell) Cv tends to the classical limit of 6 kB ≈ 49.9 J/(K·mol), reaching 49.5 by 500 K:

Interactive example

svelte<script lang="ts">
  import { PhononThermalPlot, format_num, thermal_properties } from 'matterviz'
  import { phonon_dos } from '#site/phonons/index.js'

  let energy_unit = $state<'kJ/mol' | 'eV'>(`kJ/mol`)
  const dos = phonon_dos['mp-2758-Sr4Se4-pbe']
  const temperatures = Array.from({ length: 81 }, (_, idx) => 10 * idx)
  const { zero_point_energy } = thermal_properties(dos, temperatures)
</script>

<label style="display: block; margin-bottom: 1ex">
  Units
  <select bind:value={energy_unit}>
    <option value="kJ/mol">kJ/mol, J/(K·mol)</option>
    <option value="eV">eV, meV/K</option>
  </select>
  · zero-point energy {format_num(zero_point_energy, `.4f`)} eV/cell
</label>

<PhononThermalPlot {dos} {temperatures} {energy_unit} />

Interactive Explorer

Browse all available DOS files. Click to load, use controls to adjust visualization:

Interactive example

svelte<script lang="ts">
  import { Dos, FilePicker } from 'matterviz'
  import { shift_to_fermi, normalize_dos, extract_pdos } from '#lib/spectral/helpers.js'
  import { dos_spin_polarization, get_dos } from '#site/electronic/dos/index.js'
  import { phonon_dos } from '#site/phonons/index.js'

  const files = [
    {
      name: 'mp-865805 (Ta-Zn-Co)',
      data: shift_to_fermi(dos_spin_polarization),
      category: 'Electronic',
      category_icon: '⚡',
    },
    {
      name: 'KF Lobster',
      data: get_dos('lobster'),
      category: 'Electronic',
      category_icon: '⚡',
    },
    ...Object.entries(phonon_dos).map(([key, data]) => ({
      name: key.replace('mp-', '').replace(/-/g, ' '),
      data,
      category: 'Phonon',
      category_icon: '🔊',
    })),
  ]

  let active_file = $state(files[0].name)
  let pdos_type = $state<'atom' | 'orbital' | null>(null)

  const current_dos = $derived(files.find((file) => file.name === active_file)?.data)
  const doses = $derived.by(() => {
    if (pdos_type) {
      const projected = extract_pdos(current_dos, pdos_type)
      if (!projected) throw new Error(`No ${pdos_type} projected DOS in ${active_file}`)
      return projected
    }
    const total = normalize_dos(current_dos)
    if (!total) throw new Error(`Invalid DOS in ${active_file}`)
    return { '': total }
  })
  const is_electronic = $derived(
    files.find((file) => file.name === active_file)?.category === 'Electronic',
  )
</script>

<div
  style="display: flex; gap: 1em; margin-bottom: 0.5em; align-items: center; flex-wrap: wrap"
>
  <FilePicker
    files={files.map((file) => ({
      name: file.name,
      category: file.category,
      category_icon: file.category_icon,
    }))}
    active_files={[active_file]}
    show_category_filters
    on_click={(file) => ([active_file, pdos_type] = [file.name, null])}
  />
  {#if is_electronic}
    <select bind:value={pdos_type} style="padding: 4px">
      <option value={null}>Total DOS</option>
      <option value="atom">Atom pDOS</option>
      <option value="orbital">Orbital pDOS</option>
    </select>
  {/if}
</div>

<Dos
  {doses}
  stack={pdos_type !== null}
  show_normalize_control
  show_units_control={!is_electronic}
/>