Source code for aiida_castep.calculations.utils

"""
Utility module
"""
from collections import Counter

from aiida.common import InputValidationError


[docs]def get_castep_ion_line( name, pos, label=None, spin=None, occupation=None, mix_num=None ): """ Generate a line in POSITIONS_ABS or POSITIONS_FRAC block :param name: A sting or tuple of the names :param pos: Position, a sequence of 3 :param label: A string or tuple of the labels :param spin: Spins. (spin), (spin1, spin2), (x, y, z) or ((x1,y1,z1), (x2, y2, z2)) :param occupation: tuple of two of the occupation number :param mix_num: mixture number, can be any integer but should not be repeated in a single cell file :return line: a string of the line to be added to the cell file """ # Check if we are dealing with mixtures if isinstance(name, (tuple, list)): lines = [ "{n:18} {x:18.10f} {y:18.10f} {z:18.10f}".format( n=n, x=pos[0], y=pos[1], z=pos[2] ) for n in name ] assert sum(occupation) == 1, "Occupation need to sum up to 1" lines = [lines[i] + f" MIXTURE= ({mix_num} {occupation[i]})" for i in range(2)] if label is not None: if not isinstance(label, (list, tuple)): label = [label, label] lines = [line + f" LABEL={l} " for line, l in zip(lines, label)] # Handle spin # spin might be (s1, s2) or (s1) or ((s11, s12, s12), (s21, s22, s23)) if not isinstance(spin, (list, tuple)): spin = [spin, spin] elif len(spin) == 3: # Passed a 3D spin for both atoms spin = [spin, spin] if isinstance(spin[0], (list, tuple)): lines = [ l + " SPIN=( {:.6f} {:6.f} {:.6f} )".format(*s) for l, s in zip(lines, spin) ] else: if None not in spin: lines = [l + f" SPIN={s:.3f} " for l, s in zip(lines, spin)] return "\n".join(lines) line = "{name:18} {x:18.10f} {y:18.10f} {z:18.10f}".format( name=name, x=pos[0], y=pos[1], z=pos[2] ) if spin is not None: if isinstance(spin, (list, tuple)): line += " SPIN=( {:.6f} {:.6f} {:.6f} ) ".format(*spin) else: line += f" SPIN={spin:.3f} " if label is not None: line += f" LABEL={label} " return line
[docs]def _lowercase_dict(in_dict, dict_name): """ Make sure the dictionary's keys are in lower case :param dict_name: A string of the name for the dictionary. Only used in warning message. """ if isinstance(in_dict, dict): new_dict = {str(key).lower(): value for key, value in in_dict.items()} if len(new_dict) != len(in_dict): num_items = Counter(str(key).lower() for key in in_dict.keys()) double_keys = ",".join([key for key, value in num_items if value > 1]) raise InputValidationError( "Inside the dictionary '{}' there are the following keys that " "are repeated more than once when compared case-insensitively: {}." "This is not allowed.".format(dict_name, double_keys) ) return new_dict raise TypeError("_lowercase_dict accepts only dictionaries as argument")
[docs]def _uppercase_dict(in_dict, dict_name): """ Make sure the dictionary's keys are in upper case :param dict_name: A string of the name for the dictionary. Only used in warning message. """ if isinstance(in_dict, dict): new_dict = {str(key).upper(): value for key, value in in_dict.items()} if len(new_dict) != len(in_dict): num_items = Counter(str(key).upper() for key in in_dict.keys()) double_keys = ",".join([key for key, value in num_items if value > 1]) raise InputValidationError( "Inside the dictionary '{}' there are the following keys that " "are repeated more than once when compared case-insensitively: {}." "This is not allowed.".format(dict_name, double_keys) ) return new_dict raise TypeError("_uppercase_dict accepts only dictionaries as argument")