Commits (3)
......@@ -110,6 +110,15 @@ class x_abinit_section_dataset_header(MSection):
''',
a_legacy=LegacyDefinition(name='x_abinit_vprim_3'))
x_abinit_unit_cell_volume = Quantity(
type=np.dtype(np.float64),
unit='meter**3',
shape=[],
description='''
Unit cell volume
''',
a_legacy=LegacyDefinition(name='x_abinit_unit_cell_volume')) # OK 'legacy'?
class x_abinit_section_var(MSection):
'''
......
......@@ -27,6 +27,10 @@ import os
import logging
import time
import sys
import glob
from nomad.units import ureg
from nomad.datamodel.metainfo.public import section_dos
try:
basestring
......@@ -56,6 +60,7 @@ class ABINITContext(object):
"""context for the sample parser"""
def __init__(self):
self.input_filename = None
self.parser = None
self.current_dataset = None
self.abinitVars = None
......@@ -87,7 +92,9 @@ class ABINITContext(object):
def startedParsing(self, filename, parser):
"""called when parsing starts"""
self.input_filename = filename
self.parser = parser
# allows to reset values if the same superContext is used to parse different files
self.initialize_values()
......@@ -198,9 +205,141 @@ class ABINITContext(object):
n_atom += 1
backend.addArrayValues("atom_forces_raw", atom_forces)
# Fermi energy found in mainfile (can also be retrieved from _DOS file)
if section["x_abinit_fermi_energy"] is not None:
backend.addArrayValues("energy_reference_fermi",
np.array([unit_conversion.convert_unit(section["x_abinit_fermi_energy"][-1], "hartree")]))
np.array([unit_conversion.convert_unit(section["x_abinit_fermi_energy"][-1], "hartree")]))
# #########################
# DOS CODE
backend = backend.superBackend
sscc = backend.entry_archive.section_run[0].section_single_configuration_calculation
sscc_last = sscc[-1]
sscc_idx = len(sscc) # current index, base one.
# Fermi energy from mainfile (if found). It's a numpy array of shape (1,)
fermi_energy_mainfile = sscc_last.energy_reference_fermi
# In ABINIT, NOMAD`s 'sscc' are called 'datasets', and are numbered from one.
# We pick up DOS file according to sscc (dataset) index.
# BEWARE: the DOS output file can have different name patterns.
# The only guarantee is that it will contain the dataset index
# and that it will end with `_DOS`.
mainfile_base = (self.input_filename).split('.out')[0]
dosfile_pattern = mainfile_base + f'*{sscc_idx}*_DOS'
fname_dos = glob.glob(dosfile_pattern)
if len(fname_dos) == 1:
fname_dos = fname_dos[0]
dos_file_exists = True
elif len(fname_dos) == 0 and sscc_idx == 1:
# catch a run with one dataset, e.g., `BASEo_DOS`
dosfile_pattern = mainfile_base + 'o_DOS'
fname_dos = glob.glob(dosfile_pattern)
if len(fname_dos) == 0:
dos_file_exists = False
else:
fname_dos = fname_dos[0]
dos_file_exists = True
else: # more than one file matches pattern (unexpected)
dos_file_exists = False
logger.warning('Multiple DOS filenames matching expected pattern.')
# DOS file: open
if dos_file_exists:
try:
with open(fname_dos, 'r') as textfile:
body = textfile.read()
except FileNotFoundError:
logger.warning(f'File not found: {fname_dos}')
except Exception as err:
logger.error(f'Exception on {__file__}', exc_info=err)
# DOS file: identify energy units
regex = r'\s*at\s*(?P<num_dos_values>\d*)\s*energies\s*\(in\s*(?P<energy_unit>\w*)\)'
match = re.search(regex, body, re.MULTILINE)
if match:
num_dos_values = int(match.group('num_dos_values'))
energy_unit = match.group('energy_unit')
if energy_unit == 'Hartree':
units_dos_file = ureg.a_u_energy
elif energy_unit == 'eV':
units_dos_file = ureg.eV
# DOS file: pick up Fermi energy
match = re.search(r'^#\s*Fermi energy :\s*(?P<fermi_energy>[-+]*\d*\.\d*)', body, re.MULTILINE)
if match:
# `fermiFU`: energy_fermi with `file` units (eV or Hartree)
fermiFU = float(match.group('fermi_energy')) * units_dos_file
fermi_energy_J = fermiFU.to(ureg.J) # normalizer expects Joules
try:
# if Fermi was found in mainfile, then confirm it matches dos file
if fermi_energy_mainfile is not None:
assert np.allclose(fermi_energy_mainfile, fermi_energy_J)
except AssertionError as error_msg:
logger.error('fermi mismatch ', error_msg)
# normalizer expects numpy array of shape (1,) without units (needs `ndmin=1`)
sscc_last.energy_reference_fermi = np.array(fermi_energy_J.magnitude, ndmin=1)
# DOS file: open it again, this time directly to a Numpy array
try:
dos_data = np.genfromtxt(fname_dos)
except FileNotFoundError:
logger.warning(f'File not found: {fname_dos}')
except Exception as err:
logger.error(f'Exception on {__file__}', exc_info=err)
# Slice `dos_data` according to `num_dos_values`. Doing so way we treat
# correctly the number of spin levels
if dos_data.shape[0] == num_dos_values:
spin_treat = False
else:
spin_treat = True
dos_energies_Joules = (dos_data[:num_dos_values, 0] * units_dos_file).to(ureg.J)
dos_values = np.zeros((2, num_dos_values))
dos_values_integrated = np.zeros((2, num_dos_values))
if spin_treat:
dos_values[0] = dos_data[:num_dos_values, 1] # start till num_dos_values
dos_values[1] = dos_data[num_dos_values:, 1] # num_dos_values till end
dos_values_integrated[0] = dos_data[:num_dos_values, 2] # likewise
dos_values_integrated[1] = dos_data[num_dos_values:, 2]
else:
dos_values[0] = dos_data[:num_dos_values, 1]
dos_values[1] = dos_data[:num_dos_values, 1]
dos_values_integrated[0] = dos_data[:num_dos_values, 2]
dos_values_integrated[1] = dos_data[:num_dos_values, 2]
# NOMAD metainfo needs dos_values (A) without physical units,
# (B) without unit-cell normalization, and (C) without Fermi-energy shift
# In ABINIT
# - DOS units are (electrons/Hartree/cell) and
# - integrated DOS are in (in electrons/cell)
# - `_DOS` file has dos_values without Fermi shift,
# - `_DOS` file uses energies in Hartree, regardless of the value
# of ABINIT's variable `enunit` (energy units for bandstructures)
# Retrieve unit cell volume.
# Original value was in 'bohr**3', but the Archive stores it in 'meter**3'
# hence we need to convert it back to bohrs**3
unit_cell_vol_m3 = sscc_last.m_parent.x_abinit_section_dataset[0].x_abinit_section_dataset_header[0].x_abinit_unit_cell_volume
unit_cell_vol_bohr3 = unit_cell_vol_m3.to('bohr**3')
dos_values = dos_values * unit_cell_vol_bohr3.magnitude
dos_values_integrated = dos_values_integrated * unit_cell_vol_bohr3.magnitude
# SECTION DOS: creation and filling
dos_sec = sscc_last.m_create(section_dos)
dos_sec.dos_kind = 'electronic'
dos_sec.number_of_dos_values = dos_values.shape[0]
dos_sec.dos_energies = dos_energies_Joules
dos_sec.dos_values = dos_values
dos_sec.dos_integrated_values = dos_values_integrated
# Code for DOS: end
#################################
def onClose_section_eigenvalues(self, backend, gIndex, section):
"""Trigger called when section_eigenvalues is closed.
......@@ -645,6 +784,8 @@ memestimationMatcher = \
coverageIgnore=True),
# We ignore the variables printed here, as what is printed is Abinit version dependent and depends
# on the actual values of multiple parameters. The most important variables are repeated later.
# TMK: there are exceptions: `nspinor` is only printed here
SM(r"(-|P)?(\s*\S*\s*=\s*[0-9]+)+",
coverageIgnore=True, repeats=True),
SM(r"={80}",
......@@ -907,6 +1048,12 @@ SCFCycleMatcher = \
r"\s*sigma\(2 1\)=\s*(?P<x_abinit_stress_tensor_yx>[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\s*$")
]
),
SM(startReStr=r"\s*--- !ResultsGS\s*",
required=False,
coverageIgnore=True,
subMatchers=[SM(r"\s*fermie\s*:\s*(?P<x_abinit_fermi_energy>[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)$")
]
),
SM(startReStr=r"\s*Integrated electronic density in atomic spheres:\s*$",
required=False,
coverageIgnore=True,
......@@ -1013,7 +1160,7 @@ datasetHeaderMatcher = \
SM(r"\s*R\(1\)=(?P<x_abinit_vprim_1>(\s*[0-9.-]+){3})\s*G\(1\)=(\s*[0-9.-]+){3}\s*$"),
SM(r"\s*R\(2\)=(?P<x_abinit_vprim_2>(\s*[0-9.-]+){3})\s*G\(2\)=(\s*[0-9.-]+){3}\s*$"),
SM(r"\s*R\(3\)=(?P<x_abinit_vprim_3>(\s*[0-9.-]+){3})\s*G\(3\)=(\s*[0-9.-]+){3}\s*$"),
SM(r"\s*Unit cell volume ucvol=\s*[-+0-9.eEdD]*\s*bohr\^3\s*$"),
SM(r"\s*Unit cell volume ucvol=\s*(?P<x_abinit_unit_cell_volume__bohr3>[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)\s*bohr\^3\s*$"),
SM(r"\s*Angles \(23,13,12\)=(\s*[-+0-9.eEdD]*){3}\s*degrees\s*$"),
SM(r"\s*getcut: wavevector=(\s*[0-9.]*){3}\s*ngfft=(\s*[0-9]*){3}\s*$"),
SM(r"\s*ecut\(hartree\)=\s*[0-9.]*\s*=> boxcut\(ratio\)=\s*[0-9.]*\s*$"),
......@@ -1157,5 +1304,4 @@ class AbinitParser():
},
superContext=ABINITContext(),
superBackend=backend)
return backend