from builtins import zip
from . import (get_explicit_k_path as _raw_explicit_path, get_path as
_raw_get_path)
DEPRECATION_DOCS_URL = "http://seekpath.readthedocs.io/en/latest/maindoc.html#aiida-integration"
def _aiida_to_tuple(aiida_structure):
"""
Convert an AiiDA structure to a tuple of the format
(cell, scaled_positions, element_numbers).
.. deprecated:: 1.8
Use the methods in AiiDA instead.
:param aiida_structure: the AiiDA structure
:return: (structure_tuple, kind_info, kinds) where structure_tuple
is a tuple of format (cell, scaled_positions, element_numbers);
kind_info is a dictionary mapping the kind_names to
the numbers used in element_numbers. When possible, it uses
the Z number of the element, otherwise it uses numbers > 1000;
kinds is a list of the kinds of the structure.
"""
import warnings
warnings.warn(
'this method has been deprecated and moved to AiiDA, see {}'.format(
DEPRECATION_DOCS_URL), DeprecationWarning)
import numpy as np
from aiida.common.constants import elements
def get_new_number(the_list, start_from):
"""
Get the first integer >= start_from not yet in the list
"""
retval = start_from
comp_list = sorted(_ for _ in the_list if _ >= start_from)
current_pos = 0
found = False
while not found:
if len(comp_list) <= current_pos:
return retval
if retval == comp_list[current_pos]:
current_pos += 1
retval += 1
else:
found = True
return retval
Z = {v['symbol']: k for k, v in elements.items()}
cell = np.array(aiida_structure.cell)
abs_pos = np.array([_.position for _ in aiida_structure.sites])
rel_pos = np.dot(abs_pos, np.linalg.inv(cell))
kinds = {k.name: k for k in aiida_structure.kinds}
kind_numbers = {}
for kind in aiida_structure.kinds:
if len(kind.symbols) == 1:
realnumber = Z[kind.symbols[0]]
if realnumber in list(kind_numbers.values()):
number = get_new_number(list(kind_numbers.values()),
start_from=realnumber * 1000)
else:
number = realnumber
kind_numbers[kind.name] = number
else:
number = get_new_number(list(kind_numbers.values()),
start_from=200000)
kind_numbers[kind.name] = number
numbers = [kind_numbers[s.kind_name] for s in aiida_structure.sites]
return ((cell, rel_pos, numbers), kind_numbers, list(aiida_structure.kinds))
def _tuple_to_aiida(structure_tuple, kind_info=None, kinds=None):
"""
Convert an tuple of the format
(cell, scaled_positions, element_numbers) to an AiiDA structure.
Unless the element_numbers are identical to the Z number of the atoms,
you should pass both kind_info and kinds, with the same format as returned
by get_tuple_from_aiida_structure.
.. deprecated:: 1.8
Use the methods in AiiDA instead.
:param structure_tuple: the structure in format (structure_tuple, kind_info)
:param kind_info: a dictionary mapping the kind_names to
the numbers used in element_numbers. If not provided, assumes {element_name: element_Z}
:param kinds: a list of the kinds of the structure.
"""
import warnings
warnings.warn(
'this method has been deprecated and moved to AiiDA, see {}'.format(
DEPRECATION_DOCS_URL), DeprecationWarning)
from aiida.common.constants import elements
from aiida.orm.data.structure import Kind, Site, StructureData
import numpy as np
import copy
if kind_info is None and kinds is not None:
raise ValueError("If you pass kind_info, you should also pass kinds")
if kinds is None and kind_info is not None:
raise ValueError("If you pass kinds, you should also pass kind_info")
Z = {v['symbol']: k for k, v in elements.items()}
cell, rel_pos, numbers = structure_tuple
if kind_info:
_kind_info = copy.copy(kind_info)
_kinds = copy.copy(kinds)
else:
try:
# For each site
symbols = [elements[num]['symbol'] for num in numbers]
except KeyError as e:
raise ValueError(
"You did not pass kind_info, but at least one number "
"is not a valid Z number: {}".format(e.message))
_kind_info = {elements[num]['symbol']: num for num in set(numbers)}
# Get the default kinds
_kinds = [Kind(symbols=sym) for sym in set(symbols)]
_kinds_dict = {k.name: k for k in _kinds}
# Now I will use in any case _kinds and _kind_info
if len(_kind_info.values()) != len(set(_kind_info.values())):
raise ValueError(
"There is at least a number repeated twice in kind_info!")
# Invert the mapping
mapping_num_kindname = {v: k for k, v in _kind_info.items()}
# Create the actual mapping
try:
mapping_to_kinds = {
num: _kinds_dict[kindname]
for num, kindname in mapping_num_kindname.items()
}
except KeyError as e:
raise ValueError("Unable to find '{}' in the kinds list".format(
e.message))
try:
site_kinds = [mapping_to_kinds[num] for num in numbers]
except KeyError as e:
raise ValueError(
"Unable to find kind in kind_info for number {}".format(e.message))
out_structure = StructureData(cell=cell)
for k in _kinds:
out_structure.append_kind(k)
abs_pos = np.dot(rel_pos, cell)
if len(abs_pos) != len(site_kinds):
raise ValueError(
"The length of the positions array is different from the "
"length of the element numbers")
for kind, pos in zip(site_kinds, abs_pos):
out_structure.append_site(Site(kind_name=kind.name, position=pos))
return out_structure
[docs]def get_explicit_k_path(structure,
with_time_reversal=True,
reference_distance=0.025,
recipe='hpkot',
threshold=1.e-7):
"""
Return the kpoint path for band structure (in scaled and absolute
coordinates), given a crystal structure,
using the paths proposed in the various publications (see description
of the 'recipe' input parameter). The parameters are the same
as get get_explicit_k_path in __init__, but here all structures are
input and returned as AiiDA structures rather than tuples, and similarly
k-points-related information as a AiiDA KpointsData class.
.. deprecated:: 1.8
Use the methods in AiiDA instead.
:param structure: The AiiDA StructureData for which we want to obtain
the suggested path.
:param with_time_reversal: if False, and the group has no inversion
symmetry, additional lines are returned.
:param reference_distance: a reference target distance between neighboring
k-points in the path, in units of 1/ang. The actual value will be as
close as possible to this value, to have an integer number of points in
each path.
:param recipe: choose the reference publication that defines the special
points and paths.
Currently, the following value is implemented:
'hpkot': HPKOT paper:
Y. Hinuma, G. Pizzi, Y. Kumagai, F. Oba, I. Tanaka, Band structure
diagram paths based on crystallography, Comp. Mat. Sci. 128, 140 (2017).
DOI: 10.1016/j.commatsci.2016.10.015
:param threshold: the threshold to use to verify if we are in
and edge case (e.g., a tetragonal cell, but a==c). For instance,
in the tI lattice, if abs(a-c) < threshold, a EdgeCaseWarning is issued.
Note that depending on the bravais lattice, the meaning of the
threshold is different (angle, length, ...)
:return: a dictionary with the following
keys:
- has_inversion_symmetry: True or False, depending on whether the
input crystal structure has inversion symmetry or not.
- augmented_path: if True, it means that the path was
augmented with the -k points (this happens if both
has_inversion_symmetry is False, and the user set
with_time_reversal=False in the input)
- primitive_structure: the StructureData for the primitive cell
- reciprocal_primitive_lattice: reciprocal-cell vectors for the
primitive cell (vectors are rows: reciprocal_primitive_lattice[0,:]
is the first vector)
- volume_original_wrt_prim: volume ratio of the user-provided cell
with respect to the the crystallographic primitive cell
- explicit_kpoints: An AiiDA KPointsData object (without weights)
with the kpoints and the respective labels.
For each segment, the two endpoints are always included,
independently of the length.
- explicit_kpoints_linearcoord: array of floats, giving the
coordinate at which to plot the corresponding point.
- segments: a list of length-2 tuples, with the start and end index
of each segment. **Note**! The indices are supposed to be used as
follows: the labels for the i-th segment are given by::
segment_indices = segments[i]
segment_points = explicit_kpoints.get_kpoints[slice(*segment_indices)]
This means, in particular, that if you want the label of the start
and end points, you should do::
start_point = explicit_kpoints.get_kpoints[segment_indices[0]]
stop_point = explicit_kpoints.get_kpoints[segment_indices[1]-1]
(note the minus one!)
Also, note that if segments[i-1][1] == segments[i][0] + 1 it means
that the point was calculated only once, and it belongs to both
paths. Instead, if segments[i-1][1] == segments[i][0], then
this is a 'break' point in the path (e.g., segments[i-1][1] is the
X point, and segments[i][0] is the R point, and typically in a
graphical representation they are shown at the same coordinate,
with a label "R|X").
"""
import warnings
warnings.warn(
'this method has been deprecated and moved to AiiDA, see {}'.format(
DEPRECATION_DOCS_URL), DeprecationWarning)
import copy
from aiida.orm import DataFactory
struc_tuple, kind_info, kinds = _aiida_to_tuple(structure)
retdict = _raw_explicit_path(struc_tuple)
# Replace primitive structure with AiiDA StructureData
primitive_lattice = retdict.pop('primitive_lattice')
primitive_positions = retdict.pop('primitive_positions')
primitive_types = retdict.pop('primitive_types')
primitive_tuple = (primitive_lattice, primitive_positions, primitive_types)
primitive_structure = _tuple_to_aiida(primitive_tuple, kind_info, kinds)
retdict['primitive_structure'] = primitive_structure
# Remove reciprocal_primitive_lattice, recalculated by kpoints class
retdict.pop('reciprocal_primitive_lattice')
KpointsData = DataFactory('array.kpoints')
kpoints_abs = retdict.pop('explicit_kpoints_abs')
kpoints_rel = retdict.pop('explicit_kpoints_rel')
kpoints_labels = retdict.pop('explicit_kpoints_labels')
# Expects something of the type [[0,'X'],[34,'L'],...]
# So I generate it, skipping empty labels
labels = [[idx, label] for idx, label in enumerate(kpoints_labels) if label]
kpoints = KpointsData()
kpoints.set_cell_from_structure(primitive_structure)
kpoints.set_kpoints(kpoints_abs, cartesian=True, labels=labels)
retdict['explicit_kpoints'] = kpoints
return retdict
[docs]def get_path(structure,
with_time_reversal=True,
reference_distance=0.025,
recipe='hpkot',
threshold=1.e-7):
"""
Return the kpoint path information for band structure given a
crystal structure, using the paths from the chosen recipe/reference.
The parameters are the same
as get get_path in __init__, but here all structures are
input and returned as AiiDA structures rather than tuples.
If you use this module, please cite the paper of the corresponding
recipe (see parameter below).
.. deprecated:: 1.8
Use the methods in AiiDA instead.
:param structure: The crystal structure for which we want to obtain
the suggested path. It should be an AiiDA StructureData object.
:param with_time_reversal: if False, and the group has no inversion
symmetry, additional lines are returned as described in the HPKOT
paper.
:param recipe: choose the reference publication that defines the special
points and paths.
Currently, the following value is implemented:
'hpkot': HPKOT paper:
Y. Hinuma, G. Pizzi, Y. Kumagai, F. Oba, I. Tanaka, Band structure
diagram paths based on crystallography, Comp. Mat. Sci. 128, 140 (2017).
DOI: 10.1016/j.commatsci.2016.10.015
:param threshold: the threshold to use to verify if we are in
and edge case (e.g., a tetragonal cell, but a==c). For instance,
in the tI lattice, if abs(a-c) < threshold, a EdgeCaseWarning is issued.
Note that depending on the bravais lattice, the meaning of the
threshold is different (angle, length, ...)
:return: a dictionary with the following
keys:
- point_coords: a dictionary with label -> float coordinates
- path: a list of length-2 tuples, with the labels of the starting
and ending point of each label section
- has_inversion_symmetry: True or False, depending on whether the
input crystal structure has inversion symmetry or not.
- augmented_path: if True, it means that the path was
augmented with the -k points (this happens if both
has_inversion_symmetry is False, and the user set
with_time_reversal=False in the input)
- bravais_lattice: the Bravais lattice string (like 'cP', 'tI', ...)
- bravais_lattice_extended: the specific case used to define labels and
coordinates (like 'cP1', 'tI2', ...)
- conv_structure: AiiDA StructureData for the crystallographic conventional
cell
- primitive_structure: AiiDA StructureData for the crystallographic primitive
cell
- reciprocal_primitive_lattice: reciprocal-cell vectors for the
primitive cell (vectors are rows: reciprocal_primitive_lattice[0,:]
is the first vector)
- primitive_transformation_matrix: the transformation matrix P between
the conventional and the primitive cell
- inverse_primitive_transformation_matrix: the inverse of the matrix P
(the determinant is integer and gives the ratio in volume between
the conventional and primitive cells)
- volume_original_wrt_conv: volume ratio of the user-provided cell
with respect to the the crystallographic conventional cell
- volume_original_wrt_prim: volume ratio of the user-provided cell
with respect to the the crystallographic primitive cell
:note: An EdgeCaseWarning is issued for edge cases (e.g. if a==b==c for
orthorhombic systems). In this case, still one of the valid cases
is picked.
"""
import warnings
warnings.warn(
'this method has been deprecated and moved to AiiDA, see {}'.format(
DEPRECATION_DOCS_URL), DeprecationWarning)
import copy
from aiida.orm import DataFactory
struc_tuple, kind_info, kinds = _aiida_to_tuple(structure)
retdict = _raw_get_path(struc_tuple)
# Replace conv structure with AiiDA StructureData
conv_lattice = retdict.pop('conv_lattice')
conv_positions = retdict.pop('conv_positions')
conv_types = retdict.pop('conv_types')
conv_tuple = (conv_lattice, conv_positions, conv_types)
conv_structure = _tuple_to_aiida(conv_tuple, kind_info, kinds)
retdict['conv_structure'] = conv_structure
# Replace primitive structure with AiiDA StructureData
primitive_lattice = retdict.pop('primitive_lattice')
primitive_positions = retdict.pop('primitive_positions')
primitive_types = retdict.pop('primitive_types')
primitive_tuple = (primitive_lattice, primitive_positions, primitive_types)
primitive_structure = _tuple_to_aiida(primitive_tuple, kind_info, kinds)
retdict['primitive_structure'] = primitive_structure
return retdict