Source code for aiida_kkr.calculations.kkr

# -*- coding: utf-8 -*-
"""
Input plug-in for a KKR calculation.
"""

import os

from aiida.orm.calculation.job import JobCalculation
from aiida_kkr.calculations.voro import VoronoiCalculation
from aiida.common.utils import classproperty
from aiida.common.exceptions import (InputValidationError, ValidationError)
from aiida.common.datastructures import (CalcInfo, CodeInfo)
from aiida.orm import DataFactory
from aiida.common.exceptions import UniquenessError
from aiida_kkr.tools.common_workfunctions import generate_inputcard_from_structure, check_2Dinput_consistency

#define aiida structures from DataFactory of aiida
RemoteData = DataFactory('remote')
ParameterData = DataFactory('parameter')
StructureData = DataFactory('structure')


__copyright__ = (u"Copyright (c), 2017, Forschungszentrum Jülich GmbH, "
                 "IAS-1/PGI-1, Germany. All rights reserved.")
__license__ = "MIT license, see LICENSE.txt file"
__version__ = "0.3"
__contributors__ = ("Jens Broeder", "Philipp Rüßmann")



[docs]class KkrCalculation(JobCalculation): """ AiiDA calculation plugin for a KKR calculation . """
[docs] def _init_internal_params(self): """ Init internal parameters at class load time """ # reuse base class function super(KkrCalculation, self)._init_internal_params() # calculation plugin version self._CALCULATION_PLUGIN_VERSION = __version__ # Default input and output files self._DEFAULT_INPUT_FILE = 'inputcard' # will be shown with inputcat self._DEFAULT_OUTPUT_FILE = 'out_kkr' # verdi shell output will be shown with outputcat # same as _DEFAULT_OUTPUT_FILE: piped output of kkr execution to this file self._OUTPUT_FILE_NAME = self._DEFAULT_OUTPUT_FILE # List of mandatory input files self._INPUT_FILE_NAME = self._DEFAULT_INPUT_FILE self._POTENTIAL = 'potential' # List of optional input files (may be mandatory for some settings in inputcard) self._SHAPEFUN = 'shapefun' # mandatory if nonspherical calculation self._SCOEF = 'scoef' # mandatory for KKRFLEX calculation and some functionalities self._NONCO_ANGLES = 'nonco_angles.dat' # mandatory if noncollinear directions are used that are not (theta, phi)= (0,0) for all atoms self._NONCO_ANGLES_IMP = 'nonco_angles_imp.dat' # mandatory for GREENIMP option (scattering code) self._SHAPEFUN_IMP = 'shapefun_imp' # mandatory for GREENIMP option (scattering code) self._POTENTIAL_IMP = 'potential_imp' # mandatory for GREENIMP option (scattering code) # List of output files that should always be present self._OUT_POTENTIAL = 'out_potential' self._OUTPUT_0_INIT = 'output.0.txt' self._OUTPUT_000 = 'output.000.txt' self._OUTPUT_2 = 'output.2.txt' self._OUT_TIMING_000 = 'out_timing.000.txt' self._NONCO_ANGLES_OUT = 'nonco_angles_out.dat' # special files (some runs) # DOS files self._COMPLEXDOS = 'complex.dos' self._DOS_ATOM = 'dos.atom%i' self._LMDOS = 'lmdos.%2i.%i.dat' # kkrflex files for impurity calculation self._KKRFLEX_GREEN = 'kkrflex_green' self._KKRFLEX_TMAT = 'kkrflex_tmat' self._KKRFLEX_ATOMINFO = 'kkrflex_atominfo' self._KKRFLEX_INTERCELL_REF = 'kkrflex_intercell_ref' self._KKRFLEX_INTERCELL_CMOMS = 'kkrflex_intercell_cmoms' self._ALL_KKRFLEX_FILES = [self._KKRFLEX_GREEN, self._KKRFLEX_TMAT, self._KKRFLEX_ATOMINFO, self._KKRFLEX_INTERCELL_REF, self._KKRFLEX_INTERCELL_CMOMS] # template.product entry point defined in setup.json self._default_parser = 'kkr.kkrparser' # files that will be copied from local computer if parent was KKR calc self._copy_filelist_kkr = [self._SHAPEFUN, self._OUT_POTENTIAL] # list of keywords that are not allowed to be modified (new calculation # starting from structure and voronoi run is needed instead): self._do_never_modify = ['ALATBASIS', 'BRAVAIS', 'NAEZ', '<RBASIS>', 'CARTESIAN', 'INTERFACE', '<NLBASIS>', '<RBLEFT>', 'ZPERIODL', '<NRBASIS>', '<RBRIGHT>', 'ZPERIODR', 'KSHAPE', '<SHAPE>', '<ZATOM>', 'NATYP', '<SITE>', '<CPA-CONC>', '<KAOEZL>', '<KAOEZR>']
#TODO implement workfunction to modify structure (e.g. to use VCA) @classproperty def _use_methods(cls): """ Add use_* methods for calculations. Code below enables the usage my_calculation.use_parameters(my_parameters) """ use_dict = JobCalculation._use_methods use_dict.update({ "parameters": { 'valid_types': ParameterData, 'additional_parameter': None, 'linkname': 'parameters', 'docstring': ("Use a node that specifies the input parameters ") }, "parent_folder": { 'valid_types': RemoteData, 'additional_parameter': None, 'linkname': 'parent_calc_folder', 'docstring': ( "Use a remote or local repository folder as parent folder " "(also for restarts and similar). It should contain all the " "needed files for a KKR calc, only edited files should be " "uploaded from the repository.") }, "impurity_info": { 'valid_types': ParameterData, 'additional_parameter': None, 'linkname': 'impurity_info', 'docstring': ("Use a ParameterNode that specifies properties " "for a follwoing impurity calculation (e.g. setting " "of impurity cluster in scoef file that is " "automatically created).") }, }) return use_dict
[docs] def _prepare_for_submission(self, tempfolder, inputdict): """ Create input files. :param tempfolder: aiida.common.folders.Folder subclass where the plugin should put all its files. :param inputdict: dictionary of the input nodes as they would be returned by get_inputs_dict """ has_parent = False local_copy_list = [] # Check inputdict try: parameters = inputdict.pop(self.get_linkname('parameters')) except KeyError: raise InputValidationError("No parameters specified for this calculation") if not isinstance(parameters, ParameterData): raise InputValidationError("parameters not of type ParameterData") try: imp_info = inputdict.pop(self.get_linkname('impurity_info')) found_imp_info = True except KeyError: imp_info = None found_imp_info = False if found_imp_info and not isinstance(imp_info, ParameterData): raise InputValidationError("impurity_info not of type ParameterData") try: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError("No code specified for this calculation") try: parent_calc_folder = inputdict.pop(self.get_linkname('parent_folder')) except KeyError: raise InputValidationError("Voronoi or previous KKR files needed for KKR calculation, " "you need to provide a Parent Folder/RemoteData node.") #TODO deal with data from folder data if calculation is continued on a different machine if not isinstance(parent_calc_folder, RemoteData): raise InputValidationError("parent_calc_folder must be of type RemoteData") # extract parent calculation parent_calcs = parent_calc_folder.get_inputs(node_type=JobCalculation) n_parents = len(parent_calcs) if n_parents != 1: raise UniquenessError( "Input RemoteData is child of {} " "calculation{}, while it should have a single parent" "".format(n_parents, "" if n_parents == 0 else "s")) parent_calc = parent_calcs[0] has_parent = True if n_parents == 1: parent_calc = parent_calcs[0] has_parent = True # check if parent is either Voronoi or previous KKR calculation self._check_valid_parent(parent_calc) # extract parent input parameter dict for following check try: parent_inp_dict = parent_calc.inp.parameters.get_dict() except: self.logger.error("Failed trying to find input parameter of parent {}".format(parent_calc)) raise InputValidationError("No parameter node found of parent calculation.") # check if no keys are illegally overwritten (i.e. compare with keys in self._do_never_modify) for key in parameters.get_dict().keys(): value = parameters.get_dict()[key] #self.logger.info("Checking {} {}".format(key, value)) if not value is None: if key in self._do_never_modify: oldvalue = parent_inp_dict[key] if value != oldvalue: self.logger.error("You are trying to set keyword {} = {} but this is not allowed since the structure would be modified. Please use a suitable workfunction instead.".format(key, value)) raise InputValidationError("You are trying to modify a keyword that is not allowed to be changed!") #TODO check for remote folder (starting from folder data not implemented yet) # if voronoi calc check if folder from db given, or get folder from rep. # Parent calc does not has to be on the same computer. # so far we copy every thing from local computer ggf if kkr we want to copy remotely # get StructureData node from Parent if Voronoi structure = None self.logger.info("KkrCalculation: Get structure node from voronoi parent") if isinstance(parent_calc, VoronoiCalculation): self.logger.info("KkrCalculation: Parent is Voronoi calculation") try: structure, voro_parent = VoronoiCalculation.find_parent_structure(parent_calc) except: self.logger.error('KkrCalculation: Could not get structure from Voronoi parent.') raise ValidationError("Cound not find structure node") elif isinstance(parent_calc, KkrCalculation): self.logger.info("KkrCalculation: Parent is KKR calculation") try: self.logger.info('KkrCalculation: extract structure from KKR parent') structure, voro_parent = VoronoiCalculation.find_parent_structure(parent_calc) except: self.logger.error('Could not get structure from parent.') raise ValidationError('Cound not find structure node starting from parent {}'.format(parent_calc)) else: self.logger.error("KkrCalculation: Parent is neither Voronoi nor KKR calculation!") raise ValidationError('Cound not find structure node') if inputdict: self.logger.error('KkrCalculation: Unknown inputs for structure lookup') raise ValidationError("Unknown inputs") ################################### # Check for 2D case twoDimcheck, msg = check_2Dinput_consistency(structure, parameters) if not twoDimcheck: raise InputValidationError(msg) # set shapes array either from parent voronoi run or read from inputcard in kkrimporter calculation if parent_calc.get_parser_name() != 'kkr.kkrimporterparser': # get shapes array from voronoi parent shapes = voro_parent.res.shapes else: # extract shapes from input parameters node constructed by kkrimporter calculation shapes = voro_parent.inp.parameters.get_dict().get('<SHAPE>') # Prepare inputcard from Structure and input parameter data input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME) natom, nspin, newsosol = generate_inputcard_from_structure(parameters, structure, input_filename, parent_calc, shapes=shapes) # prepare scoef file if impurity_info was given write_scoef = False if 'KKRFLEX' in parameters.get_dict().get('RUNOPT', []): write_scoef = True if found_imp_info and write_scoef: #TODO implement this! # placeholder: take Fabian's functions later on scoef = [' 13\n', ' 0.0000000000000000000E+00 0.0000000000000000000E+00 0.0000000000000000000E+00 1 29.0 0.000000000E+00\n', ' 0.2220446049250313081E-14 -0.7071067811865453523E+00 -0.7071067811865453523E+00 1 29.0 0.100000000E+01\n', ' -0.7071067811865453523E+00 0.2220446049250313081E-14 -0.7071067811865453523E+00 1 29.0 0.100000000E+01\n', ' 0.7071067811865497932E+00 0.2220446049250313081E-14 -0.7071067811865453523E+00 1 29.0 0.100000000E+01\n', ' 0.2220446049250313081E-14 0.7071067811865497932E+00 -0.7071067811865453523E+00 1 29.0 0.100000000E+01\n', ' -0.7071067811865453523E+00 -0.7071067811865453523E+00 0.2220446049250313081E-14 1 29.0 0.100000000E+01\n', ' 0.7071067811865497932E+00 -0.7071067811865453523E+00 0.2220446049250313081E-14 1 29.0 0.100000000E+01\n', ' -0.7071067811865453523E+00 0.7071067811865497932E+00 0.2220446049250313081E-14 1 29.0 0.100000000E+01\n', ' 0.7071067811865497932E+00 0.7071067811865497932E+00 0.2220446049250313081E-14 1 29.0 0.100000000E+01\n', ' 0.2220446049250313081E-14 -0.7071067811865453523E+00 0.7071067811865497932E+00 1 29.0 0.100000000E+01\n', ' -0.7071067811865453523E+00 0.2220446049250313081E-14 0.7071067811865497932E+00 1 29.0 0.100000000E+01\n', ' 0.7071067811865497932E+00 0.2220446049250313081E-14 0.7071067811865497932E+00 1 29.0 0.100000000E+01\n', ' 0.2220446049250313081E-14 0.7071067811865497932E+00 0.7071067811865497932E+00 1 29.0 0.100000000E+01\n'] scoef_filename = os.path.join(tempfolder.get_abs_path(''), self._SCOEF) self.logger.info('Writing scoef file {}'.format(scoef_filename)) with open(scoef_filename, 'w') as file: file.writelines(scoef) #os.listdir('.') elif write_scoef: self.logger.info('Need to write scoef file but no impurity_info given!') raise ValidationError('Found RUNOPT KKRFLEX but no impurity_info in inputs') ################# # Decide what files to copy based on settings to the code (e.g. KKRFLEX option needs scoef) if has_parent: # copy the right files #TODO check first if file, exists and throw # warning, now this will throw an error outfolderpath = parent_calc.out.retrieved.folder.abspath outfolderpath = os.path.join(outfolderpath, 'path') self.logger.info("out folder path {}".format(outfolderpath)) copylist = [] if isinstance(parent_calc, KkrCalculation): copylist = self._copy_filelist_kkr # TODO ggf copy remotely... if isinstance(parent_calc, VoronoiCalculation): copylist = [parent_calc._SHAPEFUN] # copy either overwrite potential or voronoi output potential # (voronoi caclualtion retreives only one of the two) if parent_calc._POTENTIAL_IN_OVERWRITE in os.listdir(outfolderpath): copylist.append(parent_calc._POTENTIAL_IN_OVERWRITE) else: copylist.append(parent_calc._OUT_POTENTIAL_voronoi) #change copylist in case the calculation starts from an imported calculation if parent_calc.get_parser_name() == 'kkr.kkrimporterparser': copylist = [] if not os.path.exists(os.path.join(outfolderpath, self._OUT_POTENTIAL)): copylist.append(self._POTENTIAL) else: copylist.append(self._OUT_POTENTIAL) if os.path.exists(os.path.join(outfolderpath, self._SHAPEFUN)): copylist.append(self._SHAPEFUN) # create local_copy_list from copylist and change some names automatically for file1 in copylist: filename = file1 if (file1 == 'output.pot' or file1 == self._OUT_POTENTIAL or (isinstance(parent_calc, VoronoiCalculation) and file1 == parent_calc._POTENTIAL_IN_OVERWRITE)): filename = self._POTENTIAL local_copy_list.append(( os.path.join(outfolderpath, file1), os.path.join(filename))) # TODO different copy lists, depending on the keywors input self.logger.info('local copy list: {}'.format(local_copy_list)) # Prepare CalcInfo to be returned to aiida calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = [] # TODO retrieve list needs some logic, retrieve certain files, # only if certain input keys are specified.... calcinfo.retrieve_list = [self._DEFAULT_OUTPUT_FILE, self._INPUT_FILE_NAME, self._POTENTIAL, self._SHAPEFUN, self._SCOEF, self._NONCO_ANGLES_OUT, self._OUT_POTENTIAL, self._OUTPUT_0_INIT, self._OUTPUT_000, self._OUTPUT_2, self._OUT_TIMING_000] # for special cases add files to retireve list: # 1. dos calculation, add *dos* files if NPOL==0 retrieve_dos_files = False print('NPOL in parameter input:', parameters.get_dict()['NPOL']) if 'NPOL' in parameters.get_dict().keys(): if parameters.get_dict()['NPOL'] == 0: retrieve_dos_files = True if 'TESTOPT' in parameters.get_dict().keys(): testopts = parameters.get_dict()['TESTOPT'] if testopts is not None : stripped_test_opts = [i.strip() for i in testopts] if 'DOS' in stripped_test_opts: retrieve_dos_files = True if retrieve_dos_files: print('adding files for dos output', self._COMPLEXDOS, self._DOS_ATOM, self._LMDOS) add_files = [self._COMPLEXDOS] for iatom in range(natom): add_files.append(self._DOS_ATOM%(iatom+1)) for ispin in range(nspin): add_files.append((self._LMDOS%(iatom+1, ispin+1)).replace(' ','0')) print(add_files) calcinfo.retrieve_list += add_files # 2. KKRFLEX calculation retrieve_kkrflex_files = False if 'RUNOPT' in parameters.get_dict().keys(): runopts = parameters.get_dict()['RUNOPT'] if runopts is not None : stripped_run_opts = [i.strip() for i in runopts] if 'KKRFLEX' in stripped_run_opts: retrieve_kkrflex_files = True if retrieve_kkrflex_files: add_files = self._ALL_KKRFLEX_FILES print('adding files for KKRFLEX output', add_files) calcinfo.retrieve_list += add_files codeinfo = CodeInfo() codeinfo.cmdline_params = [] codeinfo.code_uuid = code.uuid codeinfo.stdout_name = self._DEFAULT_OUTPUT_FILE calcinfo.codes_info = [codeinfo] return calcinfo
[docs] def _check_valid_parent(self, calc): """ Check that calc is a valid parent for a FleurCalculation. It can be a VoronoiCalculation, KKRCalculation """ try: if (((not isinstance(calc, VoronoiCalculation))) and (not isinstance(calc, KkrCalculation))): raise ValueError("Parent calculation must be a VoronoiCalculation or a KkrCalculation") except ImportError: if ((not isinstance(calc, KkrCalculation)) ): raise ValueError("Parent calculation must be a VoronoiCalculation or a KkrCalculation")
[docs] def _set_parent_remotedata(self, remotedata): """ Used to set a parent remotefolder in the restart of fleur. """ if not isinstance(remotedata,RemoteData): raise ValueError('remotedata must be a RemoteData') # complain if another remotedata is already found input_remote = self.get_inputs(node_type=RemoteData) if input_remote: raise ValidationError("Cannot set several parent calculation to a KKR calculation") self.use_parent_folder(remotedata)