Source code for aiida_kkr.tools.common_workfunctions

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Here workfunctions and normal functions using aiida-stuff (typically used 
within workfunctions) are collected.
"""
if __name__=='__main__':
    from aiida import is_dbenv_loaded, load_dbenv
    if not is_dbenv_loaded():
        load_dbenv()

from aiida.common.exceptions import InputValidationError
from aiida.work import workfunction as wf
from aiida.orm import DataFactory
from aiida_kkr.tools.kkr_params import kkrparams

#define aiida structures from DataFactory of aiida
ParameterData = DataFactory('parameter')

[docs]@wf def update_params_wf(parameternode, updatenode): """ Work function to update a KKR input parameter node. Stores new node in database and creates a link from old parameter node to new node Returns updated parameter node using update_params function :note: Input nodes need to be valid aiida ParameterData objects. :param parameternode: Input aiida ParameterData node cotaining KKR specific parameters :param updatenode: Input aiida ParameterData node containing a dictionary with the parameters that are supposed to be changed. :note: If 'nodename' is contained in dict of updatenode the string corresponding to this key will be used as nodename for the new node. Otherwise a default name is used :note: Similar for 'nodedesc' which gives new node a description :example: updated_params = ParameterData(dict={'nodename': 'my_changed_name', 'nodedesc': 'My description text', 'EMIN': -1, 'RMAX': 10.}) new_params_node = update_params_wf(input_node, updated_params) """ updatenode_dict = updatenode.get_dict() if 'nodename' in updatenode_dict.keys(): # take nodename out of dict (should only contain valid KKR parameter) nodename = updatenode_dict.pop('nodename') else: nodename = None if 'nodedesc' in updatenode_dict.keys(): # take nodename out of dict (should only contain valid KKR parameter later on) nodedesc = updatenode_dict.pop('nodedesc') else: nodedesc = None # do nothing if updatenode is empty if len(updatenode_dict.keys())==0: print('Input node is empty, do nothing!') raise InputValidationError('Nothing to store in input') # new_parameternode = update_params(parameternode, nodename=nodename, nodedesc=nodedesc, **updatenode_dict) return new_parameternode
[docs]def update_params(node, nodename=None, nodedesc=None, **kwargs): """ Update parameter node given with the values given as kwargs. Returns new node. :param node: Input parameter node (needs to be valid KKR input parameter node). :param **kwargs: Input keys with values as in kkrparams. :param linkname: Input linkname string. Give link from old to new node a name . If no linkname is given linkname defaults to 'updated parameters' :return: parameter node :example usage: OutputNode = KkrCalculation.update_params(InputNode, EMIN=-1, NSTEPS=30) :note: Keys are set as in kkrparams class. Check documentation of kkrparams for further information. :note: By default nodename is 'updated KKR parameters' and description contains list of changed """ # check if node is a valid KKR parameters node if not isinstance(node, ParameterData): print('Input node is not a valid ParameterData node') raise InputValidationError('update_params needs valid parameter node as input') #initialize temporary kkrparams instance containing all possible KKR parameters params = kkrparams() # extract input dict from node inp_params = node.get_dict() # check if input dict contains only values for KKR parameters for key in inp_params: if key not in params.values.keys(): print('Input node contains unvalid key "{}"'.format(key)) raise InputValidationError('unvalid key "{}" in input parameter node'.format(key)) # copy values from input node for key in inp_params: value = inp_params[key] params.set_value(key, value, silent=True) # to keep track of changed values: changed_params = {} # check if values are given as **kwargs (otherwise return input node) if len(kwargs)==0: print('No additional input keys given, return input node') return node else: for key in kwargs: if kwargs[key] != inp_params[key]: params.set_value(key, kwargs[key], silent=True) changed_params[key] = kwargs[key] if len(changed_params.keys())==0: print('No keys have been changed, return input node') return node # set linkname with input or default value if nodename is None or type(nodename) is not str: nodename = 'updated KKR parameters' if nodedesc is None or type(nodedesc) is not str: nodedesc = 'changed parameters: {}'.format(changed_params) # create new node ParaNode = ParameterData(dict=params.values) ParaNode.label = nodename ParaNode.description = nodedesc return ParaNode
#TODO implment VCA functionality # maybe one starts from a calculation closest to the VCA case and slowly # increase ZATOM which violates the _do_never_modify rule in KKR calculation # this should then create a new structure and modify the old potential accordingly # general rule: Nover destroy the data provenance!!! @wf def prepare_VCA_structure_wf(): pass def prepare_VCA_structure(): pass #TODO implement 2D input helper # a helper workfunction would be nice to create the vacuum region etc. for 2D calculation @wf def prepare_2Dcalc_wf(): pass def prepare_2Dcalc(): pass
[docs]def test_and_get_codenode(codenode, expected_code_type, use_exceptions=False): """ Pass a code node and an expected code (plugin) type. Check that the code exists, is unique, and return the Code object. :param codenode: the name of the code to load (in the form label@machine) :param expected_code_type: a string with the plugin that is expected to be loaded. In case no plugins exist with the given name, show all existing plugins of that type :param use_exceptions: if True, raise a ValueError exception instead of calling sys.exit(1) :return: a Code object :example usage: from kkr_scf workflow:: if 'voronoi' in inputs: try: test_and_get_codenode(inputs.voronoi, 'kkr.voro', use_exceptions=True) except ValueError: error = ("The code you provided for voronoi does not " "use the plugin kkr.voro") self.control_end_wc(error) """ import sys from aiida.common.exceptions import NotExistent from aiida.orm import Code try: if codenode is None: raise ValueError code = codenode if code.get_input_plugin_name() != expected_code_type: raise ValueError except (NotExistent, ValueError): from aiida.orm.querybuilder import QueryBuilder qb = QueryBuilder() qb.append(Code, filters={'attributes.input_plugin': {'==': expected_code_type}}, project='*') valid_code_labels = ["{}@{}".format(c.label, c.get_computer().name) for [c] in qb.all()] if valid_code_labels: msg = ("Pass as further parameter a valid code label.\n" "Valid labels with a {} executable are:\n".format( expected_code_type)) msg += "\n".join("* {}".format(l) for l in valid_code_labels) if use_exceptions: raise ValueError(msg) else: print >> sys.stderr, msg sys.exit(1) else: msg = ("Code not valid, and no valid codes for {}.\n" "Configure at least one first using\n" " verdi code setup".format( expected_code_type)) if use_exceptions: raise ValueError(msg) else: print >> sys.stderr, msg sys.exit(1) return code
[docs]def get_inputs_kkr(code, remote, options, label='', description='', parameters=None, serial=False): """ Get the input for a voronoi calc. Wrapper for KkrProcess setting structure, code, options, label, description etc. :param code: a valid KKRcode installation (e.g. input from Code.get_from_string('codename@computername')) :param remote: remote directory of parent calculation (Voronoi or previous KKR calculation) """ from aiida_kkr.calculations.kkr import KkrCalculation KkrProcess = KkrCalculation.process() # then reuse common inputs setter inputs = get_inputs_common(KkrProcess, code, remote, None, options, label, description, parameters, serial) return inputs
[docs]def get_inputs_kkrimporter(code, remote, options, label='', description='', parameters=None, serial=False): """ Get the input for a voronoi calc. Wrapper for KkrProcess setting structure, code, options, label, description etc. """ from aiida_kkr.calculations.kkr import KkrCalculation KkrProcess = KkrCalculation.process() # then reuse common inputs setter inputs = get_inputs_common(KkrProcess, code, remote, None, options, label, description, parameters, serial) return inputs
[docs]def get_inputs_voronoi(code, structure, options, label='', description='', params=None, serial=True): """ Get the input for a voronoi calc. Wrapper for VoronoiProcess setting structure, code, options, label, description etc. """ # get process for VoronoiCalculation from aiida_kkr.calculations.voro import VoronoiCalculation VoronoiProcess = VoronoiCalculation.process() # then reuse common inputs setter all options inputs = get_inputs_common(VoronoiProcess, code, None, structure, options, label, description, params, serial) return VoronoiProcess, inputs
[docs]def get_inputs_common(process, code, remote, structure, options, label, description, params, serial): """ Base function common in get_inputs_* functions for different codes """ inputs = process.get_inputs_template() if structure: inputs.structure = structure if remote: inputs.parent_folder = remote if code: inputs.code = code if params: inputs.parameters = params for key, val in options.iteritems(): if val==None: #leave them out, otherwise the dict schema won't validate continue else: inputs._options[key] = val if description: inputs['_description'] = description else: inputs['_description'] = '' if label: inputs['_label'] = label else: inputs['_label'] = '' if serial: inputs._options.withmpi = False # for now inputs._options.resources = {"num_machines": 1} ''' options = { "max_wallclock_seconds": int, "resources": dict, "custom_scheduler_commands": unicode, "queue_name": basestring, "computer": Computer, "withmpi": bool, "mpirun_extra_params": Any(list, tuple), "import_sys_environment": bool, "environment_variables": dict, "priority": unicode, "max_memory_kb": int, "prepend_text": unicode, "append_text": unicode} ''' return inputs
[docs]def get_parent_paranode(remote_data): """ Return the input parameter of the parent calulation giving the remote_data node """ inp_para = remote_data.inp.remote_folder.inp.parameters return inp_para
[docs]def generate_inputcard_from_structure(parameters, structure, input_filename, parent_calc=None, shapes=None, isvoronoi=False, use_input_alat=False): """ Takes information from parameter and structure data and writes input file 'input_filename' :param parameters: input parameters node containing KKR-related input parameter :param structure: input structure node containing lattice information :param input_filename: input filename, typically called 'inputcard' optional arguments :param parent_calc: input parent calculation node used to determine if EMIN parameter is automatically overwritten (from voronoi output) or not :param shapes: input shapes array (set automatically by aiida_kkr.calculations.Kkrcaluation and shall not be overwritten) :param isvoronoi: tell whether or not the parameter set is for a voronoi calculation or kkr calculation (have different lists of mandatory keys) :param use_input_alat: True/False, determines whether the input alat value is taken or the new alat is computed from the Bravais vectors :note: assumes valid structure and parameters, i.e. for 2D case all necessary information has to be given. This is checked with function 'check_2D_input' called in aiida_kkr.calculations.Kkrcaluation """ from aiida.common.constants import elements as PeriodicTableElements from numpy import array from aiida_kkr.tools.kkr_params import kkrparams from aiida_kkr.tools.common_functions import get_Ang2aBohr, get_alat_from_bravais from aiida_kkr.calculations.voro import VoronoiCalculation #list of globally used constants a_to_bohr = get_Ang2aBohr() # Get the connection between coordination number and element symbol # maybe do in a differnt way _atomic_numbers = {data['symbol']: num for num, data in PeriodicTableElements.iteritems()} # KKR wants units in bohr bravais = array(structure.cell)*a_to_bohr alat_input = parameters.get_dict().get('ALATBASIS') if use_input_alat and alat_input is not None: alat = alat_input else: alat = get_alat_from_bravais(bravais, is3D=structure.pbc[2]) bravais = bravais/alat sites = structure.sites naez = len(sites) positions = [] charges = [] weights = [] # for CPA isitelist = [] # counter sites array for CPA isite = 0 for site in sites: pos = site.position #TODO maybe convert to rel pos and make sure that type is right for script (array or tuple) abspos = array(pos)*a_to_bohr/alat # also in units of alat positions.append(abspos) isite += 1 sitekind = structure.get_kind(site.kind_name) for ikind in range(len(sitekind.symbols)): site_symbol = sitekind.symbols[ikind] if not sitekind.has_vacancies(): charges.append(_atomic_numbers[site_symbol]) else: charges.append(0.0) #TODO deal with VCA case if sitekind.is_alloy(): weights.append(sitekind.weights[ikind]) else: weights.append(1.) isitelist.append(isite) weights = array(weights) isitelist = array(isitelist) charges = array(charges) positions = array(positions) ###################################### # Prepare keywords for kkr from input structure # get parameter dictionary input_dict = parameters.get_dict() # get rid of structure related inputs that are overwritten from structure input for key in ['BRAVAIS', 'ALATBASIS', 'NAEZ', '<ZATOM>', '<RBASIS>', 'CARTESIAN']: if input_dict.get(key) is not None: print('WARNING: automatically removing value of key', key) input_dict.pop(key) # automatically rescale RMAX, GMAX, RCLUSTZ, RCLUSTXY which are scaled with the lattice constant if alat_input is not None: if input_dict.get('RMAX') is not None: print('rescale RMAX', alat_input/alat) input_dict['RMAX'] = input_dict['RMAX']*alat_input/alat if input_dict.get('GMAX') is not None: print('rescale GMAX', 1/(alat_input/alat)) input_dict['GMAX'] = input_dict['GMAX']*1/(alat_input/alat) if input_dict.get('RCLUSTZ') is not None: print('rescale RCLUSTZ', alat_input/alat) input_dict['RCLUSTZ'] = input_dict['RCLUSTZ']*alat_input/alat if input_dict.get('RCLUSTXY') is not None: print('rescale RCLUSTXY', alat_input/alat) input_dict['RCLUSTXY'] = input_dict['RCLUSTXY']*alat_input/alat # empty kkrparams instance (contains formatting info etc.) if not isvoronoi: params = kkrparams() else: params = kkrparams(params_type='voronoi') # for KKR calculation set EMIN automatically from parent_calc (ausways in res.emin of voronoi and kkr) if ('EMIN' not in input_dict.keys() or input_dict['EMIN'] is None) and parent_calc is not None: print('Overwriting EMIN with value from parent calculation') if isinstance(parent_calc, VoronoiCalculation): emin = parent_calc.res.emin else: emin = parent_calc.res.energy_contour_group['emin'] print('Setting emin:',emin, 'is emin None?',emin is None) params.set_value('EMIN', emin) # overwrite keywords with input parameter for key in input_dict.keys(): params.set_value(key, input_dict[key], silent=True) # Write input to file (the parameters that are set here are not allowed to be modfied externally) params.set_multiple_values(BRAVAIS=bravais, ALATBASIS=alat, NAEZ=naez, ZATOM=charges, RBASIS=positions, CARTESIAN=True) # for CPA case: if len(weights)>naez: natyp = len(weights) params.set_value('NATYP', natyp) params.set_value('<CPA-CONC>', weights) params.set_value('<SITE>', isitelist) else: natyp = naez # write shapes (extracted from voronoi parent automatically in kkr calculation plugin) if shapes is not None: params.set_value('<SHAPE>', shapes) # change input values of 2D input to new alat: rbl = params.get_value('<RBLEFT>') rbr = params.get_value('<RBRIGHT>') zper_l = params.get_value('ZPERIODL') zper_r = params.get_value('ZPERIODR') if rbl is not None: params.set_value('<RBLEFT>', array(rbl)*a_to_bohr/alat) if rbr is not None: params.set_value('<RBRIGHT>', array(rbr)*a_to_bohr/alat) if zper_l is not None: params.set_value('ZPERIODL', array(zper_l)*a_to_bohr/alat) if zper_r is not None: params.set_value('ZPERIODR', array(zper_r)*a_to_bohr/alat) # write inputfile params.fill_keywords_to_inputfile(output=input_filename) nspin = params.get_value('NSPIN') newsosol = False if 'NEWSOSOL' in params.get_value('RUNOPT'): newsosol = True return natyp, nspin, newsosol
[docs]def check_2Dinput_consistency(structure, parameters): """ Check if structure and parameter data are complete and matching. :param input: structure, needs to be a valid aiida StructureData node :param input: parameters, needs to be valid aiida ParameterData node returns (False, errormessage) if an inconsistency has been found, otherwise return (True, '2D consistency check complete') """ # default is bulk, get 2D info from structure.pbc info (periodic boundary contitions) is2D = False if not all(structure.pbc): # check periodicity, assumes finite size in z-direction if structure.pbc != (True, True, False): return (False, "Structure.pbc is neither (True, True, True) for bulk nor (True, True, False) for surface calculation!") is2D = True # check for necessary info in 2D case inp_dict = parameters.get_dict() set_keys = [i for i in inp_dict.keys() if inp_dict[i] is not None] has2Dinfo = True for icheck in ['INTERFACE', '<NRBASIS>', '<RBLEFT>', '<RBRIGHT>', 'ZPERIODL', 'ZPERIODR', '<NLBASIS>']: if icheck not in set_keys: has2Dinfo = False if has2Dinfo and not inp_dict['INTERFACE'] and is2D: return (False, "'INTERFACE' parameter set to False but structure is 2D") if has2Dinfo!=is2D: return (False, "2D info given in parameters but structure is 3D\nstructure is 2D? {}\ninput has 2D info? {}\nset keys are: {}".format(is2D, has2Dinfo, set_keys)) # if everything is ok: return (True, "2D consistency check complete")
[docs]def structure_from_params(parameters): """ Construct aiida structure out of kkr parameter set (if ALATBASIS, RBASIS, ZATOM etc. are given) :param input: parameters, kkrparams object with structure information set (e.g. extracted from read_inputcard function) :returns: success, boolean to determine if structure creatoin was successful :returns: structure, an aiida StructureData object """ from aiida_kkr.tools.common_functions import get_aBohr2Ang from aiida.common.constants import elements as PeriodicTableElements from numpy import array, shape StructureData = DataFactory('structure') is_complete = True for icheck in ['<ZATOM>', '<RBASIS>', 'BRAVAIS', 'ALATBASIS']: if parameters.get_value(icheck) is None: is_complete = False # set natyp natyp = parameters.get_value('NATYP') naez = parameters.get_value('NAEZ') if natyp is None: if naez is None: is_complete = False else: natyp = naez # check if all necessary info for 2D calculation is there if parameters.get_value('INTERFACE'): for icheck in ['<NRBASIS>', '<RBLEFT>', '<RBRIGHT>', 'ZPERIODL', 'ZPERIODR', '<NLBASIS>']: if parameters.get_value(icheck) is None: is_complete = False # check CPA case if natyp != naez: for icheck in ['<SITE>', '<CPA-CONC>']: if parameters.get_value(icheck) is None: is_complete = False if not is_complete: return is_complete, StructureData() # extract cell using BRAVAIS and ALATBASIS and create empty structure alat = parameters.get_value('ALATBASIS') cell = array(parameters.get_value('BRAVAIS')) * alat * get_aBohr2Ang() struc = StructureData(cell=cell) # extract sites with positions, charges/Atom labels, weights pos_all = array(parameters.get_value('<RBASIS>')) # positions in units of alat if not parameters.get_value('CARTESIAN'): # convert from internal to cartesian coordinates for isite in range(len(pos_all)): tmp_pos = pos_all[isite] pos_all[isite] = tmp_pos[0]*cell[0]+tmp_pos[1]*cell[1]+tmp_pos[2]*cell[2] # cell already contains alat factor to convert to Ang. units else: pos_all = pos_all * alat * get_aBohr2Ang() # now positions are in Ang. units zatom_all = parameters.get_value('<ZATOM>') if natyp==naez: weights = [1. for i in range(natyp)] sites = range(1,natyp+1) else: weights = parameters.get_value('<CPA-CONC>') sites = parameters.get_value('<SITE>') for isite in sites: pos = pos_all[sites.index(isite)] weight = weights[sites.index(isite)] if abs(zatom_all[isite-1]-int(zatom_all[isite-1]))>10**-4: # TODO deal with VCA (non-integer zatom) print('VCA not implemented yet, stopping here!') raise NotImplementedError('VCA functionality not implemented') if zatom_all[isite-1]<1: symbol = 'H' weight = 0.0 struc.append_atom(position=pos, symbols='H', weights=0.0, mass=1.0) else: symbol = PeriodicTableElements.get(zatom_all[isite-1]).get('symbol') struc.append_atom(position=pos, symbols=symbol, weights=weight) # set correct pbc for 2D case if parameters.get_value('INTERFACE'): struc.set_pbc((True, True, False)) # finally return structure return is_complete, struc
''' if __name__=='__main__': from aiida import is_dbenv_loaded, load_dbenv if not is_dbenv_loaded(): load_dbenv() p = kkrparams(params_type='kkr') # automatically read keywords from inpucard p.read_keywords_from_inputcard(inputcard='/Users/ruess/sourcecodes/aiida/development/calc_import_test/inputcard') # extract structure success, struc = structure_from_params(p) print(success, struc) print(struc.kinds) for site in struc.sites: print(site) p1 = kkrparams() p2 = kkrparams() for key in p.get_dict().keys(): p1.set_value(key, p.get_value(key), silent=True) p2.set_value(key, p.get_value(key), silent=True) print('create new inputcard') generate_inputcard_from_structure(p1, struc, '/Users/ruess/sourcecodes/aiida/development/calc_import_test/inputcard_test', use_input_alat=True) print('create new inputcard2') generate_inputcard_from_structure(p2, struc, '/Users/ruess/sourcecodes/aiida/development/calc_import_test/inputcard_test2', use_input_alat=False) #""" from aiida_kkr.calculations.voro import VoronoiCalculation from aiida.orm import Code#, load_node ParameterData = DataFactory('parameter') ParaNode = ParameterData(dict=p.values) VoroCalc = VoronoiCalculation() VoroCalc.label = 'Voronoi start potential' VoroCalc.set_withmpi(False) VoroCalc.set_resources({"num_machines" : 1}) VoroCalc.set_max_wallclock_seconds(300) VoroCalc.set_computer('my_mac') VoroCalc.use_structure(struc) # use voronoi code for this calculation code = Code.get_from_string('voronoi@my_mac') VoroCalc.use_code(code) VoroCalc.use_parameters(ParaNode) calc = VoroCalc calc.store_all() print("created calculation; calc=Calculation(uuid='{}') # ID={}".format(calc.uuid, calc.dbnode.pk)) calc.submit() print("submitted calculation; calc=Calculation(uuid='{}') # ID={}".format(calc.uuid, calc.dbnode.pk)) #""" '''