Skip to content

dos

Classes for reading/manipulating projwfc.x/dos.x (P)DOS files

EspressoDos(energies, tdos, idos=None, summed_pdos=None, summed_pdos_l=None, atomic_states=None, efermi=None, lsda=None, noncolinear=None, lspinorb=None)

Bases: MSONable

Class for representing DOS data from a dos.x or projwfc.x calculation

unconventional. Use the class method constructors from_filpdos (to parse projwfc.x outputs) or from_fildos (to parse dos.x output) instead.

PARAMETER DESCRIPTION
energies

Energies in eV. Not w.r.t the Fermi level.

TYPE: ndarray

tdos

Total DOS.

TYPE: dict[Spin, ndarray]

idos

Integrated DOS.

TYPE: dict[Spin, ndarray] DEFAULT: None

summed_pdos

Summed PDOS across all states \((l,m)\) or \((l,j, m_j)\) etc. Should be the same as the total DOS but there might be minor rounding differences. This quantity is spin polarized in noncolinear calculations without SOC but tdos is not. This is essentially \(\sum_{l,m} \mathrm{PDOS}_{l,m}\) or \(\sum_{l,j,m_j} \mathrm{PDOS}_{l,j,{m_j}}\).

TYPE: dict[Spin, ndarray] DEFAULT: None

summed_pdos_l

Summed pDOS for each orbital, pretty much just \(\sum_{m} \mathrm{PDOS}_{l,m}\) or \(\sum_{m_j} \mathrm{PDOS}_{l,j,{m_j}}\). Order is not guaranteed.

TYPE: dict[Spin, list[ndarray]] DEFAULT: None

atomic_states

List of AtomicState objects. Order is guaranteed to be the same as projwfc.x output.

TYPE: list[AtomicState] DEFAULT: None

efermi

Fermi energy. # TODO: beter default

TYPE: float DEFAULT: None

lsda

Whether the calculation is spin polarized. None indicates unknown.

TYPE: bool DEFAULT: None

noncolinear

Whether the calculation is noncolinear (with or without SOC). None indicates unknown.

TYPE: bool DEFAULT: None

lspinorb

Whether the calculation includes spin-orbit coupling. None indicates unknown.

TYPE: bool DEFAULT: None

ATTRIBUTE DESCRIPTION
energies

Energies in eV. Not w.r.t the Fermi level.

TYPE: ndarray

tdos

Total DOS.

TYPE: dict[Spin, ndarray]

idos

Integrated DOS.

TYPE: dict[Spin, ndarray]

atomic_states

Ordered list of AtomicState objects.

TYPE: list[AtomicState]

efermi

Fermi energy.

TYPE: float

lsda

Whether the calculation is spin polarized.

TYPE: bool

noncolinear

Whether the calculation is noncolinear.

TYPE: bool

lspinorb

Whether the calculation includes spin-orbit coupling.

TYPE: bool

Source code in pymatgen/io/espresso/outputs/dos.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def __init__(
    self,
    energies: np.ndarray,
    tdos: dict[Spin, np.ndarray],
    idos: dict[Spin, np.ndarray] = None,
    summed_pdos: dict[Spin, np.ndarray] = None,
    summed_pdos_l: dict[Spin, list[np.ndarray]] = None,
    atomic_states: list[AtomicState] = None,
    efermi: float | None = None,
    lsda: bool | None = None,
    noncolinear: bool | None = None,
    lspinorb: bool | None = None,
):
    r"""
    Initializes an Espresso Dos object from a list of energies and densities
    of states. Shouldn't really be used directly unless you're doing something
    unconventional. Use the class method constructors from_filpdos (to parse
    projwfc.x outputs) or from_fildos (to parse dos.x output) instead.

    Args:
        energies (np.ndarray): Energies in eV. *Not* w.r.t the Fermi level.
        tdos (dict[Spin, np.ndarray]): Total DOS.
        idos (dict[Spin, np.ndarray], optional): Integrated DOS.
        summed_pdos (dict[Spin, np.ndarray], optional): Summed PDOS across
            all states $(l,m)$ or $(l,j, m_j)$ etc. Should be the same as the total DOS but there might be minor rounding differences. This quantity is spin
            polarized in noncolinear calculations without SOC but tdos is not. This is essentially $\sum_{l,m} \mathrm{PDOS}_{l,m}$ or $\sum_{l,j,m_j} \mathrm{PDOS}_{l,j,{m_j}}$.
        summed_pdos_l (dict[Spin, list[np.ndarray]], optional): Summed pDOS for each
            orbital, pretty much just $\sum_{m} \mathrm{PDOS}_{l,m}$ or $\sum_{m_j} \mathrm{PDOS}_{l,j,{m_j}}$. Order is not guaranteed.
        atomic_states (list[AtomicState], optional): List of AtomicState objects.
            Order is guaranteed to be the same as projwfc.x output.
        efermi (float, optional): Fermi energy. # TODO: beter default
        lsda (bool, optional): Whether the calculation is spin polarized.
            None indicates unknown.
        noncolinear (bool, optional): Whether the calculation is noncolinear
            (with or without SOC). None indicates unknown.
        lspinorb (bool, optional): Whether the calculation includes spin-orbit
            coupling. None indicates unknown.

    Attributes:
        energies (np.ndarray): Energies in eV. *Not* w.r.t the Fermi level.
        tdos (dict[Spin, np.ndarray]): Total DOS.
        idos (dict[Spin, np.ndarray]): Integrated DOS.
        atomic_states (list[AtomicState]): Ordered list of AtomicState objects.
        efermi (float): Fermi energy.
        lsda (bool): Whether the calculation is spin polarized.
        noncolinear (bool): Whether the calculation is noncolinear.
        lspinorb (bool): Whether the calculation includes spin-orbit coupling.
    """

    self.energies = energies
    self.tdos = tdos
    self.idos = idos
    self.atomic_states = atomic_states  # Order guaranteed to be same as projwfc.x
    self.efermi = efermi
    self.noncolinear = noncolinear
    self.lsda = lsda
    self.lspinorb = lspinorb

    # Shouldn't be exposed to the user
    self._summed_pdos_l = summed_pdos_l  # Order is glob dependent
    self._summed_pdos = summed_pdos

from_filpdos(filpdos) classmethod

Initialize an EspressoDos object from projwfc.x pdos files. This requires the filproj.pdos_tot and all filproj.pdos_atm#_wfc# files to be present.

PARAMETER DESCRIPTION
filpdos

path to the filproj pdos file. Note that this is the same quantity labeled "filproj" in projwfc.x's input, so it's not a full filename. For example, filpdos="path/to/filpdos" will look for files like "path/to/filpdos.pdos_atm#_wfc#...".

TYPE: str | PathLike

Source code in pymatgen/io/espresso/outputs/dos.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
@classmethod
def from_filpdos(cls, filpdos: str | os.PathLike) -> "EspressoDos":
    """
    Initialize an EspressoDos object from projwfc.x pdos files. This requires
    the filproj.pdos_tot and all filproj.pdos_atm#_wfc# files to be present.

    Args:
        filpdos (str | os.PathLike): path to the filproj pdos file. Note that this
            is the same quantity labeled "filproj" in projwfc.x's input, so it's not a full filename. For example, filpdos="path/to/filpdos" will look for files like "path/to/filpdos.pdos_atm#_wfc#...".
    """

    # Read the total DOS first. This is the only way to distinguish between
    # spin polarized calcs noncolinear calcs without SOC. We can't tell
    # colinear non-spin-polarized and noncolinear-with-SOC apart yet.
    E, tdos, summed_pdos, ncl_no_soc, lsda = cls._read_total_pdos(
        f"{filpdos}.pdos_tot"
    )
    all_energies = [E]

    atomic_states = []
    summed_pdos_l = []
    filenames = glob.glob(f"{filpdos}.pdos_atm*")
    for f in filenames:
        E, s_pdos_l, states = cls._read_pdos(f, ncl_no_soc)
        atomic_states.extend(states)
        summed_pdos_l.append(s_pdos_l)
        all_energies.append(E)
    if not np.allclose(all_energies, all_energies[0], rtol=0, atol=1e-4):
        raise WrongDosFormatError("Energies from all files do not match.")

    energies = all_energies[0]
    # Order the atomic states and compute the state #
    atomic_states = cls._order_states(atomic_states)
    lspinorb = atomic_states[0].j is not None
    noncolinear = ncl_no_soc or lspinorb

    return cls(
        energies,
        tdos,
        summed_pdos_l=summed_pdos_l,
        summed_pdos=summed_pdos,
        atomic_states=atomic_states,
        noncolinear=noncolinear,
        lsda=lsda,
        lspinorb=lspinorb,
    )

from_fildos(fildos) classmethod

Constructs a Dos object from a fildos (dos.x) file

The format of Fildos is as follows (TDOS = total DOS, IDOS = integrated DOS):

  • Spin polarized:

    Energy(ev) TDOS(up) TODS(dn) IDOS

  • Everything else:

    Energy(ev) TDOS IDOS

PARAMETER DESCRIPTION
fildos

path to the dos file. Same as the dos.x input.

TYPE: str

Source code in pymatgen/io/espresso/outputs/dos.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
@classmethod
def from_fildos(cls, fildos: str | os.PathLike) -> "EspressoDos":
    """
    Constructs a Dos object from a fildos (dos.x) file

    The format of Fildos is as follows (TDOS = total DOS, IDOS = integrated DOS):

    * Spin polarized:

        > Energy(ev) TDOS(up) TODS(dn) IDOS

    * Everything else:

        > Energy(ev) TDOS IDOS

    Args:
        fildos (str): path to the dos file. Same as the dos.x input.
    """
    with open(fildos, "r") as f:
        header = f.readline()
        if match := re.match(r".*?EFermi\s*=\s*(\d+\.\d+)\s*eV.*?", header):
            efermi = float(match[1])
        else:
            raise ValueError("Cannot find Fermi energy in the header.")

    lsda = False

    data = np.loadtxt(fildos, skiprows=1)
    energies = data[:, 0]
    if (ncols := data.shape[1]) == 3:
        # Colinear or noncolinear (with or without SOC)
        tdos, idos = {Spin.up: data[:, 1]}, data[:, 2]
    elif ncols == 4:
        # spin polarized
        lsda = True
        tdos = {Spin.up: data[:, 1], Spin.down: data[:, 2]}
        idos = data[:, 3]
    else:
        raise WrongDosFormatError(
            f"Unexpected number of columns {ncols} in {fildos}"
        )

    return cls(energies, tdos, idos=idos, efermi=efermi, lsda=lsda)

WrongDosFormatError

Bases: Exception

Raised when the DOS file format is not recognized