Commit 54fb372e authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Refactored the band gap information to support more spin channels and easier...

Refactored the band gap information to support more spin channels and easier querying for band structures without band gap.
parent 68f3a587
Pipeline #73793 canceled with stages
in 25 seconds
......@@ -2222,7 +2222,8 @@ class section_band_gap(MSection):
type=float,
unit="joule",
description="""
Band gap energy.
Band gap energy. Value of zero corresponds to a band structure without
a band gap.
""",
a_legacy=LegacyDefinition(name='value')
)
......@@ -2316,19 +2317,13 @@ class section_k_band(MSection):
section_band_gap = SubSection(
sub_section=section_band_gap.m_def,
repeats=False,
a_legacy=LegacyDefinition(name='section_band_gap')
)
section_band_gap_spin_up = SubSection(
sub_section=section_band_gap.m_def,
repeats=False,
a_legacy=LegacyDefinition(name='section_band_gap')
)
section_band_gap_spin_down = SubSection(
sub_section=section_band_gap.m_def,
repeats=False,
repeats=True,
description=""",
Contains information for band gaps detected in the band structure.
Contains a section for each spin channel in the same order as reported
for the band energies. For channels without a band gap, a band gap
value of zero is reported.
""",
a_legacy=LegacyDefinition(name='section_band_gap')
)
......@@ -2336,7 +2331,7 @@ class section_k_band(MSection):
type=bool,
description="""
Boolean indicating whether the path follows the standard path for this
cell. The AFLOW standard by Setyawan and Curtarolo is used
bravais lattice. The AFLOW standard by Setyawan and Curtarolo is used
(https://doi.org/10.1016/j.commatsci.2010.05.010).
""",
a_legacy=LegacyDefinition(name='is_standard_path')
......
......@@ -164,8 +164,6 @@ class BandStructureNormalizer(Normalizer):
# Handle spin channels separately to find gaps for spin up and down
n_channels = energies.shape[0] # pylint: disable=E1136 # pylint/issues/3139
gaps: List[section_band_gap] = [None, None]
for channel in range(n_channels):
if n_channels == 1:
vbm = valence_band_maximum
......@@ -212,6 +210,8 @@ class BandStructureNormalizer(Normalizer):
# If a highest point of the valence band and a lowest point of the
# conduction band are found, and the difference between them is
# positive, save the information location and value.
gap = section_band_gap()
if lower_defined and upper_defined and gap_upper_energy - gap_lower_energy >= 0:
# See if the gap is direct or indirect by comparing the k-point
......@@ -221,34 +221,14 @@ class BandStructureNormalizer(Normalizer):
k_point_distance = self.get_k_space_distance(reciprocal_cell, k_point_lower, k_point_upper)
is_direct_gap = k_point_distance <= config.normalize.k_space_precision
gap = section_band_gap()
gap.type = "direct" if is_direct_gap else "indirect"
gap.value = float(gap_upper_energy - gap_lower_energy)
gap.conduction_band_min_k_point = k_point_upper
gap.conduction_band_min_energy = float(gap_upper_energy)
gap.valence_band_max_k_point = k_point_lower
gap.valence_band_max_energy = float(gap_lower_energy)
gaps[channel] = gap
# For unpolarized calculations we simply report the gap if it is found.
if n_channels == 1:
if gaps[0] is not None:
band.m_add_sub_section(section_k_band.section_band_gap, gaps[0])
# For polarized calculations we report the gap separately for both
# channels. Also we report the smaller gap as the the total gap for the
# calculation.
elif n_channels == 2:
if gaps[0] is not None:
band.m_add_sub_section(section_k_band.section_band_gap_spin_up, gaps[0])
if gaps[1] is not None:
band.m_add_sub_section(section_k_band.section_band_gap_spin_down, gaps[1])
if gaps[0] is not None and gaps[1] is not None:
if gaps[0].value <= gaps[1].value:
band.m_add_sub_section(section_k_band.section_band_gap, gaps[0])
else:
band.m_add_sub_section(section_k_band.section_band_gap, gaps[1])
else:
if gaps[0] is not None:
band.m_add_sub_section(section_k_band.section_band_gap, gaps[0])
elif gaps[1] is not None:
band.m_add_sub_section(section_k_band.section_band_gap, gaps[1])
gap.value = 0.0
# Add section to band
band.m_add_sub_section(section_k_band.section_band_gap, gap)
......@@ -29,47 +29,43 @@ ureg = UnitRegistry()
def test_band_gaps(bands_unpolarized_no_gap, bands_polarized_no_gap, bands_unpolarized_gap_indirect, bands_polarized_gap_indirect):
"""Tests that band gaps are correctly identified for different cases.
"""
def test_generic(bs, n_channels):
def test_generic(bs):
"""Generic tests for band structure data."""
assert bs.brillouin_zone is not None
assert bs.reciprocal_cell.shape == (3, 3)
# Unpolarized, no gaps
bs = bands_unpolarized_no_gap.entry_archive.section_run[0].section_single_configuration_calculation[0].section_k_band[0]
test_generic(bs, n_channels=1)
assert bs.section_band_gap is None
assert bs.section_band_gap_spin_up is None
assert bs.section_band_gap_spin_down is None
test_generic(bs)
assert len(bs.section_band_gap) == 1
assert bs.section_band_gap[0].value == 0
# Polarized, no gaps
bs = bands_polarized_no_gap.entry_archive.section_run[0].section_single_configuration_calculation[0].section_k_band[0]
test_generic(bs, n_channels=2)
assert bs.section_band_gap is None
assert bs.section_band_gap_spin_up is None
assert bs.section_band_gap_spin_down is None
test_generic(bs)
assert len(bs.section_band_gap) == 2
assert bs.section_band_gap[0].value == 0
assert bs.section_band_gap[1].value == 0
# Unpolarized, finite gap, indirect
bs = bands_unpolarized_gap_indirect.entry_archive.section_run[0].section_single_configuration_calculation[0].section_k_band[0]
test_generic(bs, n_channels=1)
gap_ev = (bs.section_band_gap.value * ureg.J).to(ureg.eV).magnitude
test_generic(bs)
assert len(bs.section_band_gap) == 1
gap = bs.section_band_gap[0]
gap_ev = (gap.value * ureg.J).to(ureg.eV).magnitude
assert gap_ev == pytest.approx(0.62, 0.01)
assert bs.section_band_gap.type == "indirect"
assert bs.section_band_gap_spin_up is None
assert bs.section_band_gap_spin_down is None
assert gap.type == "indirect"
# Polarized, finite gap, indirect
bs = bands_polarized_gap_indirect.entry_archive.section_run[0].section_single_configuration_calculation[0].section_k_band[0]
test_generic(bs, n_channels=2)
gap = bs.section_band_gap
gap_up = bs.section_band_gap_spin_up
gap_down = bs.section_band_gap_spin_down
gap_ev = (gap.value * ureg.J).to(ureg.eV).magnitude
test_generic(bs)
assert len(bs.section_band_gap) == 2
gap_up = bs.section_band_gap[0]
gap_down = bs.section_band_gap[1]
gap_up_ev = (gap_up.value * ureg.J).to(ureg.eV).magnitude
gap_down_ev = (gap_down.value * ureg.J).to(ureg.eV).magnitude
assert gap_up.type == "indirect"
assert gap_down.type == "indirect"
assert gap_up_ev != gap_down_ev
assert gap_up_ev == gap_ev
assert gap_up_ev == pytest.approx(0.956, 0.01)
assert gap_down_ev == pytest.approx(1.230, 0.01)
......@@ -79,6 +75,4 @@ def test_phonon_band(phonon):
"""
bs = phonon.entry_archive.section_run[0].section_single_configuration_calculation[0].section_k_band[0]
assert bs.is_standard_path is None
assert bs.section_band_gap is None
assert bs.section_band_gap_spin_up is None
assert bs.section_band_gap_spin_down is None
assert len(bs.section_band_gap) == 0
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment