Source code for tests.test_diffpes.test_inout.test_procar

"""Validate VASP PROCAR parsing.

Covers non-spin, spin-polarized, and SOC projections together with orbital ordering and malformed or truncated blocks.
"""

import io
import tempfile
from pathlib import Path

import chex
import jax.numpy as jnp
import pytest
from beartype.typing import TextIO

import diffpes
from diffpes.inout import (
    read_chgcar,
    read_doscar,
    read_eigenval,
    read_kpoints,
    read_poscar,
    read_procar,
)
from diffpes.types import (
    BandStructure,
    FullDensityOfStates,
    SOCVolumetricData,
    SpinBandStructure,
    SpinOrbitalProjection,
    VolumetricData,
    make_orbital_projection,
    make_spin_orbital_projection,
)

_FIXTURES_DIR: Path = Path(__file__).resolve().parent / "fixtures"


[docs] class TestReadProcar(chex.TestCase): """Validate :func:`diffpes.inout.read_procar`. Verifies that the PROCAR parser produces an OrbitalProjection with correct projection array shape and optional spin/oam absent when not present in the file. :see: :func:`~diffpes.inout.read_procar` """
[docs] def test_parses_minimal_procar(self) -> None: """Read minimal PROCAR and assert OrbitalProjection shape and sample values. The test loads the minimal PROCAR fixture. Asserts projections shape (2, 2, 1, 9), selected projection values (0.1 and 0.18), and that spin and oam are None. Validates k-point/band/ion block parsing and orbital channel ordering. Notes ----- The test builds the inputs in the test body and checks the stated property with the documented numerical or structural assertions.""" path: Path orb: ( diffpes.types.OrbitalProjection | diffpes.types.SpinOrbitalProjection ) path = _FIXTURES_DIR / "PROCAR" orb = read_procar(str(path)) chex.assert_shape(orb.projections, (2, 2, 1, 9)) chex.assert_trees_all_close( orb.projections[0, 0, 0, 0], jnp.float64(0.1), atol=1e-12 ) chex.assert_trees_all_close( orb.projections[1, 1, 0, 0], jnp.float64(0.18), atol=1e-12 ) assert orb.spin is None assert orb.oam is None
[docs] def test_spin_procar_legacy(self) -> None: """Read spin-polarized PROCAR in legacy mode and verify only first spin block. Parses the PROCAR_spin fixture with ``return_mode="legacy"``, which extracts only the first spin-up block. The test checks the projection shape and the selected s-orbital value. It also verifies that ``spin`` is ``None`` in legacy mode. This exercises the backward-compatible single-block extraction. Notes ----- The test builds the inputs in the test body and checks the stated property with the documented numerical or structural assertions.""" path: Path orb: ( diffpes.types.OrbitalProjection | diffpes.types.SpinOrbitalProjection ) path = _FIXTURES_DIR / "PROCAR_spin" orb = read_procar(str(path), return_mode="legacy") chex.assert_shape(orb.projections, (2, 2, 1, 9)) chex.assert_trees_all_close( orb.projections[0, 0, 0, 0], jnp.float64(0.1), atol=1e-12 ) assert orb.spin is None
[docs] def test_spin_procar_full(self) -> None: """Read spin-polarized PROCAR in full mode and verify SpinOrbitalProjection output. Parses the PROCAR_spin fixture with ``return_mode="full"``, returning a ``SpinOrbitalProjection`` with both ``projections`` and ``spin`` arrays. The test checks the result type and both shapes. It compares the averaged s-orbital value with 0.09. This value is the mean of the two spin values. The comparison uses ``atol=1e-12``. Notes ----- The test builds the inputs in the test body and checks the stated property with the documented numerical or structural assertions.""" path: Path orb: ( diffpes.types.OrbitalProjection | diffpes.types.SpinOrbitalProjection ) path = _FIXTURES_DIR / "PROCAR_spin" orb = read_procar(str(path), return_mode="full") assert isinstance(orb, SpinOrbitalProjection) chex.assert_shape(orb.projections, (2, 2, 1, 9)) chex.assert_shape(orb.spin, (2, 2, 1, 6)) chex.assert_trees_all_close( orb.projections[0, 0, 0, 0], jnp.float64(0.09), atol=1e-12 )
[docs] class TestReadProcarErrors(chex.TestCase): """Validate error handling in :func:`read_procar`. :see: :func:`~diffpes.inout.read_procar` """
[docs] def test_empty_procar_raises(self) -> None: """Verify that an empty PROCAR file raises ``ValueError``. The test writes a file with no valid k-points block and calls ``read_procar``. Asserts a ``ValueError`` matching ``"No valid PROCAR blocks found"``. Notes ----- The test builds the inputs in the test body and checks the stated property with the documented numerical or structural assertions.""" fh: TextIO tmpname: str with tempfile.NamedTemporaryFile( mode="w", suffix=".PROCAR", delete=False ) as fh: fh.write("PROCAR empty\n") tmpname = fh.name try: with pytest.raises(ValueError, match="No valid PROCAR blocks"): read_procar(tmpname) finally: import os os.unlink(tmpname)
[docs] class TestReadProcarSOC(chex.TestCase): """Validate SOC PROCAR parsing (procar.py lines 191-210, 282-283, 307). :see: :func:`~diffpes.inout.read_procar` """
[docs] def test_soc_procar_full(self) -> None: """Read SOC PROCAR (4 blocks) in full mode and verify spin components. The test uses the ``PROCAR_soc`` fixture. It has a title before the first k-point block and a blank line before the band header. The fixture contains four blocks. The test checks a ``SpinOrbitalProjection`` with projections shape (1, 1, 1, 9) and spin shape (1, 1, 1, 6). Notes ----- The test builds the inputs in the test body and checks the stated property with the documented numerical or structural assertions.""" path: Path orb: ( diffpes.types.OrbitalProjection | diffpes.types.SpinOrbitalProjection ) path = _FIXTURES_DIR / "PROCAR_soc" orb = read_procar(str(path), return_mode="full") assert isinstance(orb, SpinOrbitalProjection) chex.assert_shape(orb.projections, (1, 1, 1, 9)) chex.assert_shape(orb.spin, (1, 1, 1, 6)) chex.assert_trees_all_close( orb.projections[0, 0, 0, 0], jnp.float64(0.1), atol=1e-12 )