Source code for aiida_vasp.parsers.content_parsers.kpoints
"""
The ``KPOINTS`` parser interface.
-----------------------------
Contains the parsing interfaces to parsevasp used to parse ``KPOINTS`` content.
"""
import numpy as np
from parsevasp.kpoints import Kpoint, Kpoints
from aiida_vasp.parsers.content_parsers.base import BaseFileParser
from aiida_vasp.utils.aiida_utils import get_data_class
[docs]
class KpointsParser(BaseFileParser):
"""The parser interface that enables parsing of ``KPOINTS`` content.
The parser is triggered by using the ``kpoints-kpoints`` quantity key. The quantity key ``kpoints``
will on the other hand parse the k-points using the XML parser.
"""
DEFAULT_SETTINGS = {'quantities_to_parse': ['kpoints-kpoints']}
PARSABLE_QUANTITIES = {
'kpoints-kpoints': {
'inputs': [],
'name': 'kpoints',
'prerequisites': [],
},
}
def _init_from_handler(self, handler):
"""Initialize using a file like handler.
Parameters
----------
handler : object
A file like object that provides the necessary content to be parsed.
"""
try:
self._content_parser = Kpoints(file_handler=handler, logger=self._logger)
except SystemExit:
self._logger.warning('Parsevasp exited abnormally.')
def _init_from_data(self, data):
"""Initialize using AiiDA KpointsData."""
if isinstance(data, get_data_class('core.array.kpoints')):
self._content_data = data
else:
raise TypeError('The supplied AiiDA data structure is not a KpointsData.')
@property
def kpoints(self):
"""
Return kpoints that is ready to be consumed by the the AiiDA ``KpointsData``.
AiiDA does not support the line mode used in VASP, so we give a warning that parsing
this is not supported.
Returns
-------
aiida_kpoints : dict
A dict that contain keys ``comment``, ``divisions``, ``shifts``, ``points``, ``tetra``,
``tetra_volume``, ``mode`` ``centering``, ``num_kpoints``, ``weights`` and ``cartesian``
which are compatible with consumption of the initialization of the AiiDA KpointsData.
"""
aiida_kpoints = parsevasp_to_aiida(self._content_parser, self._logger)
return aiida_kpoints
def _content_data_to_content_parser(self):
"""
Convert an AiiDA ``KpointsData`` to a content parser instance of ``Kpoints`` from ``parsevasp``.
Returns
-------
content_parser : object
An instance of ``Kpoints`` from ``parsevasp``.
"""
try:
# Check if the ``KpointsData`` contain a mesh.
_ = self._content_data.base.attributes.get('mesh')
mode = 'automatic'
except AttributeError:
pass
try:
# Check to see if the ``KpointsData`` contain an explicit k-point list.
_ = self._content_data.base.attributes.get('array|kpoints')
mode = 'explicit'
except AttributeError:
pass
kpoints_dict = {}
for keyword in [
'comment', 'divisions', 'shifts', 'points', 'tetra', 'tetra_volume', 'mode', 'centering', 'num_kpoints',
'generating_vectors'
]:
kpoints_dict[keyword] = None
kpoints_dict.update(getattr(self, '_get_kpointsdict_' + mode)(self._content_data))
# We brake hard if ``parsevasp`` fail here. If we can not write we will not try another parser.
content_parser = Kpoints(kpoints_dict=kpoints_dict, logger=self._logger)
return content_parser
def _get_kpointsdict_explicit(self, kpoints_data):
"""
Turn Aiida ``KpointData`` into a k-points dictionary with explicit generation of points.
Parameters
----------
kpoints_data : object
An AiiDA ``KpointsData`` object containing explicit k-point sets.
Returns
-------
kpoints_dict : dict
A dictionary that can be used to initialize a ``parsevasp`` ``Kpoints`` instance.
"""
kpoints_dict = {}
kpts = []
try:
points, weights = kpoints_data.get_kpoints(also_weights=True)
except AttributeError:
points = kpoints_data.get_kpoints()
weights = None
for index, point in enumerate(points):
if weights is not None:
kpt = Kpoint(point, weight=weights[index])
else:
# No weights supplied, so set them to 1.0
kpt = Kpoint(point, weight=1.0)
kpts.append(kpt)
kpoints_dict['points'] = kpts
kpoints_dict['mode'] = 'explicit'
kpoints_dict['num_kpoints'] = len(kpts)
return kpoints_dict
@staticmethod
def _get_kpointsdict_automatic(kpointsdata):
"""
Turn Aiida ``KpointData`` into a k-point dictionary with automatic generation of points.
Parameters
----------
kpoints_data : object
An AiiDA ``KpointsData`` object containing meshed k-point sets.
Returns
-------
kpoints_dict : dict
A dictionary that can be used to initialize a ``parsevasp`` ``Kpoints`` instance.
"""
kpoints_dict = {}
# Automatic mode
mesh = kpointsdata.get_kpoints_mesh()
kpoints_dict['divisions'] = mesh[0]
kpoints_dict['shifts'] = mesh[1]
kpoints_dict['mode'] = 'automatic'
# Here we need to make a choice, so should add more to AiiDA to make this better defined
kpoints_dict['centering'] = 'Gamma'
kpoints_dict['num_kpoints'] = 0
return kpoints_dict
[docs]
def parsevasp_to_aiida(kpoints, logger):
"""``parsevasp`` to AiiDA conversion.
Generate an AiiDA data structure that can be consumed by ``KpointsData`` on initialization
from the ``parsevasp`` instance of the ``Kpoints`` class.
Parameters
----------
kpoints : object
An instance of the ``Kpoints`` class in ``parsevasp``.
Returns
-------
kpoints_dict : dict
A dictionary representation which are ready to be used when creating an
AiiDA ``KpointsData`` instance.
"""
if kpoints.entries.get('mode') == 'line':
# AiiDA does not support line mode
logger.warning('The read KPOINTS contained line mode which is'
'not supported. Returning None.')
return None
# Fetch a dictionary containing the k-points information
kpoints_dict = kpoints.get_dict()
# Now unpack points, weights and check direct versus cartesian. Set to
# None if mode is automatic.
points = []
weights = []
cartesian = []
for key, value in kpoints_dict.items():
if key == 'points':
if value is not None:
for item in value:
points.append(item[0])
weights.append(item[1])
# AiiDA wants cartesian and not direct flags, so revert
cartesian.append(not item[2])
else:
points = None
weights = None
cartesian = None
# Make sure weights is ndarray
if weights is not None:
weights = np.array(weights)
# Check that we only have similar elements in the direct list as
# AiiDA can only work with all points being either in direct or cartesian
# coordinates.
if cartesian is not None:
if not cartesian.count(cartesian[0]) == len(cartesian):
raise ValueError('Different coordinate systems have been detected among the k-points.')
cartesian = cartesian[0]
# Modify dict to AiiDA spec
kpoints_dict['points'] = points
kpoints_dict['weights'] = weights
kpoints_dict['cartesian'] = cartesian
return kpoints_dict