Skip to content

Commit

Permalink
Merge 129862c into 0e3181f
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew S. Rosen committed Jun 30, 2021
2 parents 0e3181f + 129862c commit 1a78ba7
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 5 deletions.
76 changes: 71 additions & 5 deletions pymatgen/io/vasp/outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ def __init__(
parse_projected_eigen=False,
parse_potcar_file=True,
occu_tol=1e-8,
separate_spins=False,
exception_on_bad_xml=True,
):
"""
Expand Down Expand Up @@ -325,6 +326,10 @@ def __init__(
occu_tol (float): Sets the minimum tol for the determination of the
vbm and cbm. Usually the default of 1e-8 works well enough,
but there may be pathological cases.
separate_spins (bool): Whether the band gap, CBM, and VBM should be
reported for each individual spin channel. Defaults to False,
which computes the eigenvalue band properties independent of
the spin orientation. If True, the calculation must be spin-polarized.
exception_on_bad_xml (bool): Whether to throw a ParseException if a
malformed XML is detected. Default to True, which ensures only
proper vasprun.xml are parsed. You can set to False if you want
Expand All @@ -335,6 +340,7 @@ def __init__(
self.ionic_step_skip = ionic_step_skip
self.ionic_step_offset = ionic_step_offset
self.occu_tol = occu_tol
self.separate_spins = separate_spins
self.exception_on_bad_xml = exception_on_bad_xml

with zopen(filename, "rt") as f:
Expand All @@ -346,7 +352,7 @@ def __init__(
preamble = steps.pop(0)
self.nionic_steps = len(steps)
new_steps = steps[ionic_step_offset :: int(ionic_step_skip)]
# add the tailing informat in the last step from the run
# add the tailing information in the last step from the run
to_parse = "<calculation>".join(new_steps)
if steps[-1] != new_steps[-1]:
to_parse = "{}<calculation>{}{}".format(preamble, to_parse, steps[-1].split("</calculation>")[-1])
Expand Down Expand Up @@ -952,13 +958,26 @@ def get_band_structure(
def eigenvalue_band_properties(self):
"""
Band properties from the eigenvalues as a tuple,
(band gap, cbm, vbm, is_band_gap_direct).
(band gap, cbm, vbm, is_band_gap_direct). In the case of separate_spins=True,
the band gap, cbm, vbm, and is_band_gap_direct are each lists of length 2,
with index 0 representing the spin-up channel and index 1 representing
the spin-down channel.
"""
vbm = -float("inf")
vbm_kpoint = None
cbm = float("inf")
cbm_kpoint = None
vbm_spins = []
vbm_spins_kpoints = []
cbm_spins = []
cbm_spins_kpoints = []
if self.separate_spins and len(self.eigenvalues.keys()) != 2:
raise ValueError("The separate_spins flag can only be True if ISPIN = 2")

for spin, d in self.eigenvalues.items():
if self.separate_spins:
vbm = -float("inf")
cbm = float("inf")
for k, val in enumerate(d):
for (eigenval, occu) in val:
if occu > self.occu_tol and eigenval > vbm:
Expand All @@ -967,6 +986,18 @@ def eigenvalue_band_properties(self):
elif occu <= self.occu_tol and eigenval < cbm:
cbm = eigenval
cbm_kpoint = k
if self.separate_spins:
vbm_spins.append(vbm)
vbm_spins_kpoints.append(vbm_kpoint)
cbm_spins.append(cbm)
cbm_spins_kpoints.append(cbm_kpoint)
if self.separate_spins:
return (
[max(cbm_spins[0] - vbm_spins[0], 0), max(cbm_spins[1] - vbm_spins[1], 0)],
[cbm_spins[0], cbm_spins[1]],
[vbm_spins[0], vbm_spins[1]],
[vbm_spins_kpoints[0] == cbm_spins_kpoints[0], vbm_spins_kpoints[1] == cbm_spins_kpoints[1]],
)
return max(cbm - vbm, 0), cbm, vbm, vbm_kpoint == cbm_kpoint

def calculate_efermi(self):
Expand Down Expand Up @@ -1451,6 +1482,7 @@ def __init__(
parse_projected_eigen: Union[bool, str] = False,
parse_potcar_file: Union[bool, str] = False,
occu_tol: float = 1e-8,
separate_spins: bool = False,
):
"""
Args:
Expand All @@ -1468,9 +1500,14 @@ def __init__(
occu_tol: Sets the minimum tol for the determination of the
vbm and cbm. Usually the default of 1e-8 works well enough,
but there may be pathological cases.
separate_spins (bool): Whether the band gap, CBM, and VBM should be
reported for each individual spin channel. Defaults to False,
which computes the eigenvalue band properties independent of
the spin orientation. If True, the calculation must be spin-polarized.
"""
self.filename = filename
self.occu_tol = occu_tol
self.separate_spins = separate_spins

with zopen(filename, "rt") as f:
self.efermi = None
Expand Down Expand Up @@ -5223,20 +5260,25 @@ class Eigenval:
kpoint index is 0-based (unlike the 1-based indexing in VASP).
"""

def __init__(self, filename, occu_tol=1e-8):
def __init__(self, filename, occu_tol=1e-8, separate_spins=False):
"""
Reads input from filename to construct Eigenval object
Args:
filename (str): filename of EIGENVAL to read in
occu_tol (float): tolerance for determining band gap
separate_spins (bool): whether the band gap, CBM, and VBM should be
reported for each individual spin channel. Defaults to False,
which computes the eigenvalue band properties independent of
the spin orientation. If True, the calculation must be spin-polarized.
Returns:
a pymatgen.io.vasp.outputs.Eigenval object
"""

self.filename = filename
self.occu_tol = occu_tol
self.separate_spins = separate_spins

with zopen(filename, "r") as f:
self.ispin = int(f.readline().split()[-1])
Expand Down Expand Up @@ -5279,14 +5321,26 @@ def __init__(self, filename, occu_tol=1e-8):
def eigenvalue_band_properties(self):
"""
Band properties from the eigenvalues as a tuple,
(band gap, cbm, vbm, is_band_gap_direct).
(band gap, cbm, vbm, is_band_gap_direct). In the case of separate_spins=True,
the band gap, cbm, vbm, and is_band_gap_direct are each lists of length 2,
with index 0 representing the spin-up channel and index 1 representing
the spin-down channel.
"""

vbm = -float("inf")
vbm_kpoint = None
cbm = float("inf")
cbm_kpoint = None
vbm_spins = []
vbm_spins_kpoints = []
cbm_spins = []
cbm_spins_kpoints = []
if self.separate_spins and len(self.eigenvalues.keys()) != 2:
raise ValueError("The separate_spins flag can only be True if ISPIN = 2")

for spin, d in self.eigenvalues.items():
if self.separate_spins:
vbm = -float("inf")
cbm = float("inf")
for k, val in enumerate(d):
for (eigenval, occu) in val:
if occu > self.occu_tol and eigenval > vbm:
Expand All @@ -5295,6 +5349,18 @@ def eigenvalue_band_properties(self):
elif occu <= self.occu_tol and eigenval < cbm:
cbm = eigenval
cbm_kpoint = k
if self.separate_spins:
vbm_spins.append(vbm)
vbm_spins_kpoints.append(vbm_kpoint)
cbm_spins.append(cbm)
cbm_spins_kpoints.append(cbm_kpoint)
if self.separate_spins:
return (
[max(cbm_spins[0] - vbm_spins[0], 0), max(cbm_spins[1] - vbm_spins[1], 0)],
[cbm_spins[0], cbm_spins[1]],
[vbm_spins[0], vbm_spins[1]],
[vbm_spins_kpoints[0] == cbm_spins_kpoints[0], vbm_spins_kpoints[1] == cbm_spins_kpoints[1]],
)
return max(cbm - vbm, 0), cbm, vbm, vbm_kpoint == cbm_kpoint


Expand Down
24 changes: 24 additions & 0 deletions pymatgen/io/vasp/tests/test_outputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,18 @@ def test_kpointset_electronvelocities(self):
vasprun = Vasprun(vpath, parse_potcar_file=False)
self.assertEqual(vasprun.eigenvalues[Spin.up].shape[0], len(vasprun.actual_kpoints))

def test_eigenvalue_band_properties_separate_spins(self):
eig = Vasprun(self.TEST_FILES_DIR / "vasprun_eig_separate_spins.xml.gz", separate_spins=True)
props = eig.eigenvalue_band_properties
self.assertAlmostEqual(props[0][0], 2.8772, places=4)
self.assertAlmostEqual(props[0][1], 1.2810, places=4)
self.assertAlmostEqual(props[1][0], 3.6741, places=4)
self.assertAlmostEqual(props[1][1], 1.6225, places=4)
self.assertAlmostEqual(props[2][0], 0.7969, places=4)
self.assertAlmostEqual(props[2][1], 0.3415, places=4)
self.assertEqual(props[3][0], True)
self.assertEqual(props[3][1], True)


class OutcarTest(PymatgenTest):
_multiprocess_shared_ = True
Expand Down Expand Up @@ -2017,6 +2029,18 @@ def test_eigenvalue_band_properties(self):
self.assertAlmostEqual(props[2], 1.1434, places=4)
self.assertEqual(props[3], False)

def test_eigenvalue_band_properties_separate_spins(self):
eig = Eigenval(self.TEST_FILES_DIR / "EIGENVAL_separate_spins.gz", separate_spins=True)
props = eig.eigenvalue_band_properties
self.assertAlmostEqual(props[0][0], 2.8772, places=4)
self.assertAlmostEqual(props[0][1], 1.2810, places=4)
self.assertAlmostEqual(props[1][0], 3.6741, places=4)
self.assertAlmostEqual(props[1][1], 1.6225, places=4)
self.assertAlmostEqual(props[2][0], 0.7969, places=4)
self.assertAlmostEqual(props[2][1], 0.3415, places=4)
self.assertEqual(props[3][0], True)
self.assertEqual(props[3][1], True)


class WavederTest(PymatgenTest):
_multiprocess_shared_ = True
Expand Down
Binary file added test_files/EIGENVAL_separate_spins.gz
Binary file not shown.
Binary file added test_files/vasprun_eig_separate_spins.xml.gz
Binary file not shown.

0 comments on commit 1a78ba7

Please sign in to comment.