Commit cb4f01ca authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Fixed issues with DOS normalization and added extended description to the...

Fixed issues with DOS normalization and added extended description to the metainfo description about the normalization procedure.
parent 42f21688
Pipeline #78948 passed with stages
in 23 minutes and 24 seconds
......@@ -239,8 +239,8 @@ normalize = NomadConfig(
# The threshold for point equality in k-space. Unit: 1/m.
k_space_precision=150e6,
# The energy threshold for how much a band can be on top or below the fermi
# level in order to detect a gap. k_B x T at room temperature. Unit: Joule
band_structure_energy_tolerance=300 * 1.38064852E-23,
# level in order to detect a gap. Unit: Joule.
band_structure_energy_tolerance=1.6022e-20, # 0.1 eV
springer_db_path=os.path.join(
os.path.dirname(os.path.abspath(__file__)),
'normalizing/data/springer.msg'
......
......@@ -1264,10 +1264,17 @@ class section_dos(MSection):
shape=['number_of_dos_values'],
unit='joule',
description='''
Array containing the set of discrete energy values with respect to the top of the
valence band for the density (electronic-energy) of states (DOS). This is the
total DOS, see atom_projected_dos_energies and species_projected_dos_energies for
Array containing the set of discrete energy values with respect to the
highest occupied energy level. This is the total DOS, see
atom_projected_dos_energies and species_projected_dos_energies for
partial density of states.
If not available through energy_reference_highest_occupied, the highest
occupied energy level is detected by searching for a non-zero DOS value
below (or nearby) the reported energy_reference_fermi. In case the
highest occupied energy level cannot be detected accurately, the
normalized values are not reported. For calculations with multiple
spin-channels, the normalization is determined by the first channel.
''',
a_legacy=LegacyDefinition(name='dos_energies_normalized'))
......
......@@ -47,7 +47,8 @@ class DosNormalizer(Normalizer):
dos_values = dos.dos_values
dos_energies = dos.dos_energies
energy_reference_fermi = scc.energy_reference_fermi
if dos_energies is None or dos_values is None or energy_reference_fermi is None:
energy_reference_highest_occupied = scc.energy_reference_highest_occupied
if dos_energies is None or dos_values is None or (energy_reference_fermi is None and energy_reference_highest_occupied is None):
continue
# Normalize DOS values to be 1/J/atom/m^3
......@@ -68,6 +69,15 @@ class DosNormalizer(Normalizer):
dos_values_normalized = dos_values / (number_of_atoms * unit_cell_volume)
# Normalize energies so that they are normalized to HOMO.
dos_energies_normalized = None
# Primarily normalize to the HOMO level reported by the parser.
# The first channel is used.
energy_reference = None
if energy_reference_highest_occupied is not None:
energy_reference = energy_reference_highest_occupied[0]
# If HOMO is not reported, search for it in the DOS values
else:
i_channel = 0
fermi_energy = energy_reference_fermi[i_channel]
fermi_idx = (np.abs(dos_energies - fermi_energy)).argmin()
......@@ -77,6 +87,13 @@ class DosNormalizer(Normalizer):
zero_found = False
energy_reference = fermi_energy
# First check that the closest dos energy to fermi_energy is not too
# far away. If it is very far away, the normalization may be very
# inaccurate and we do not report it.
fermi_energy_closest = dos_energies[fermi_idx]
distance = np.abs(fermi_energy_closest - fermi_energy)
if distance.magnitude <= energy_threshold:
# Walk through the energies in descencing direction to see if a
# gap is nearby (see energy_threshold). If gap found, continue
# until HOMO found
......@@ -84,7 +101,7 @@ class DosNormalizer(Normalizer):
while True:
try:
value = dos_values_normalized[i_channel, idx]
energy_distance = fermi_energy - dos_energies[idx]
energy_distance = fermi_energy_closest - dos_energies[idx]
except IndexError:
break
if energy_distance.magnitude > energy_threshold and not zero_found:
......@@ -114,10 +131,15 @@ class DosNormalizer(Normalizer):
break
idx += 1
dos_energies_normalized = dos_energies - energy_reference
# Add quantities to NOMAD's Metainfo
scc_url = '/section_run/0/section_single_configuration_calculation/%d/section_dos/0' % scc.m_parent_index
self._backend.openContext(scc_url)
dos.dos_values_normalized = dos_values_normalized
# Data for DOS fingerprint
dos_fingerprint = None
if energy_reference is not None:
dos_energies_normalized = dos_energies - energy_reference
dos.dos_energies_normalized = dos_energies_normalized
try:
dos_fingerprint = DOSFingerprint().calculate(
dos_energies_normalized.magnitude,
......@@ -126,13 +148,7 @@ class DosNormalizer(Normalizer):
)
except Exception as e:
self.logger.error('could not generate dos fingerprint', exc_info=e)
# Add quantities to NOMAD's Metainfo
scc_url = '/section_run/0/section_single_configuration_calculation/%d/section_dos/0' % scc.m_parent_index
self._backend.openContext(scc_url)
dos.dos_values_normalized = dos_values_normalized
dos.dos_energies_normalized = dos_energies_normalized
if dos_fingerprint is not None:
else:
sec_dos_fingerprint = dos.m_create(section_dos_fingerprint)
sec_dos_fingerprint.bins = dos_fingerprint.bins
sec_dos_fingerprint.indices = dos_fingerprint.indices
......
......@@ -48,9 +48,9 @@ def test_fingerprint(dos_si_vasp):
# y_vasp = dos_si_vasp.get_value('dos_values_normalized', 0)
# x_fhiaims = dos_si_fhiaims.get_value('dos_energies_normalized', 0)
# y_fhiaims = dos_si_fhiaims.get_value('dos_values_normalized', 0)
# mpl.plot(x_vasp, y_vasp[0], label="VASP")
# mpl.plot(x_exciting, y_exciting[0], label="exciting")
# mpl.plot(x_fhiaims, y_fhiaims[0], label="FHI-aims")
# mpl.plot(x_vasp, y_vasp[0], label="VASP", marker=".")
# mpl.plot(x_exciting, y_exciting[0], label="exciting", marker=".")
# mpl.plot(x_fhiaims, y_fhiaims[0], label="FHI-aims", marker=".")
# mpl.legend()
# mpl.show()
......
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