Source code for aiida_castep.calculations.utils

"""
Utility module
"""
from __future__ import absolute_import
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] + " MIXTURE= ({} {})".format(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 + " LABEL={} ".format(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 + " SPIN={:.3f} ".format(s) 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 += " SPIN={:.3f} ".format(spin) if label is not None: line += " LABEL={} ".format(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 = 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 = 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")