Source code for aiida_fleur.calculation.fleurinputgen

###############################################################################
# Copyright (c), Forschungszentrum Jülich GmbH, IAS-1/PGI-1, Germany.         #
#                All rights reserved.                                         #
# This file is part of the AiiDA-FLEUR package.                               #
#                                                                             #
# The code is hosted on GitHub at https://github.com/JuDFTteam/aiida-fleur    #
# For further information on the license, see the LICENSE.txt file            #
# For further information please visit http://www.flapw.de or                 #
# http://aiida-fleur.readthedocs.io/en/develop/                               #
###############################################################################
"""
Input plug-in for the FLEUR input generator 'inpgen'.
The input generator for the Fleur code is a preprocessor
and should be run locally (with the direct scheduler) or inline,
because it does not take many resources.
"""
from aiida.engine import CalcJob
from aiida.common.exceptions import InputValidationError
from aiida.common.datastructures import CalcInfo, CodeInfo
from aiida.orm import StructureData, Dict

from aiida_fleur.data.fleurinp import FleurinpData
import io


[docs]class FleurinputgenCalculation(CalcJob): """ JobCalculationClass for the inpgen, which is a preprocessor for a FLEUR calculation. For more information about produced files and the FLEUR-code family, go to http://www.flapw.de/. """ __version__ = '1.2.2' # Default input and output files _INPUT_FILE = 'aiida.in' # will be shown with inputcat _OUTPUT_FILE = 'out' # 'shell.out' #will be shown with outputcat # created file names, some needed for Fleur calc _INPXML_FILE_NAME = 'inp.xml' _INPUT_FILE_NAME = 'aiida.in' _SHELLOUT_FILE_NAME = 'shell.out' _OUTPUT_FILE_NAME = 'out' _ERROR_FILE_NAME = 'out.error' _STRUCT_FILE_NAME = 'struct.xsf' _JUDFT_WARN_ONLY_INFO_FILE_NAME = 'JUDFT_WARN_ONLY' _settings_keys = [ 'additional_retrieve_list', 'remove_from_retrieve_list', 'cmdline', 'significant_figures_cell', 'significant_figures_positions', 'profile' ] # TODO switch all these to init_internal_params? _OUTPUT_SUBFOLDER = './fleur_inp_out/' _PREFIX = 'aiida' # Additional files that should always be retrieved for the specific plugin _internal_retrieve_list = [] _automatic_namelists = {}
[docs] @classmethod def define(cls, spec): super().define(spec) spec.input('metadata.options.input_filename', valid_type=str, default=cls._INPUT_FILE) spec.input('metadata.options.output_filename', valid_type=str, default=cls._INPXML_FILE_NAME) spec.input('structure', valid_type=StructureData, help='Choose the input structure to use') spec.input('parameters', valid_type=Dict, required=False, help='Use a node that specifies the input parameters ' 'for the namelists') spec.input('settings', valid_type=Dict, required=False, help='This parameter data node is used to specify for some ' 'advanced features how the plugin behaves. You can add files' 'the retrieve list, or add command line switches, ' 'for all available features here check the documentation.') # parser spec.input('metadata.options.parser_name', valid_type=str, default='fleur.fleurinpgenparser') # declaration of outputs of the calclation spec.output('fleurinp', valid_type=FleurinpData, required=True) # exit codes # spec.exit_code(251, 'ERROR_WRONG_INPUT_PARAMS', # message='Input parameters for inpgen contain unknown keys.') # spec.exit_code(253, 'ERROR_ATOM_POSITION_NEEDED', # message='Fleur lattice needs atom positions as input.') # spec.exit_code(254, 'ERROR_INPUT_PARAMS_LEFTOVER', # message='Excessive input parameters were specified.') spec.exit_code(300, 'ERROR_NO_RETRIEVED_FOLDER', message='No retrieved folder found.') spec.exit_code(301, 'ERROR_OPENING_OUTPUTS', message='One of the output files can not be opened.') spec.exit_code(306, 'ERROR_NO_INPXML', message='XML input file was not found.') spec.exit_code(307, 'ERROR_MISSING_RETRIEVED_FILES', message='Some required files were not retrieved.') spec.exit_code(308, 'ERROR_FLEURINPDATA_INPUT_NOT_VALID', message=('During parsing: FleurinpData could not be initialized, see log. ')) spec.exit_code(309, 'ERROR_FLEURINPDATA_NOT_VALID', message='During parsing: FleurinpData failed validation.') spec.exit_code(310, 'ERROR_UNKNOWN_PROFILE', message='The profile {profile} is not known to the used inpgen code')
[docs] def prepare_for_submission(self, folder): """ This is the routine to be called when you want to create the input files for the inpgen with the plug-in. :param folder: a aiida.common.folders.Folder subclass where the plugin should put all its files. """ local_copy_list = [] remote_copy_list = [] remote_symlink_list = [] # first check existence of structure and if 1D, 2D, 3D structure = self.inputs.structure # check existence of parameters (optional) if 'parameters' in self.inputs: parameters = self.inputs.parameters else: parameters = None if parameters is None: # use default parameters_dict = {} else: parameters_dict = _lowercase_dict(parameters.get_dict(), dict_name='parameters') code = self.inputs.code # check existence of settings (optional) if 'settings' in self.inputs: settings = self.inputs.settings else: settings = None if settings is None: settings_dict = {} else: settings_dict = settings.get_dict() # check for for allowed keys, ignore unknown keys but warn. for key in settings_dict.keys(): if key not in self._settings_keys: # TODO warning self.logger.info('settings dict key %s for Fleur calculation' 'not recognized, only %s are allowed.', key, str(self._settings_keys)) #Check that if a inpgen profile is given no additional parameters are provided #since this will overwrite the effects of the inpgen profile (For now we just issue a warning) if 'profile' in settings_dict: lapw_parameters_given = any('atom' in key for key in parameters_dict) comp = parameters_dict.get('comp', {}) lapw_parameters_given = lapw_parameters_given or \ 'kmax' in comp or \ 'gmax' in comp or \ 'gmaxxc' in comp if lapw_parameters_given: self.logger.warning('Inpgen profile specified but atom/LAPW basis specific ' 'parameters are provided. These will conflict/override each other') ####################################### #### WRITE ALL CARDS IN INPUT FILE #### with folder.open(self._INPUT_FILE_NAME, 'w') as input_file: write_inpgen_file_aiida_struct(structure, input_file, input_params=parameters_dict, settings=settings_dict) # create a JUDFT_WARN_ONLY file in the calculation folder with io.StringIO('/n') as handle: warn_only_filename = self._JUDFT_WARN_ONLY_INFO_FILE_NAME folder.create_file_from_filelike(handle, filename=warn_only_filename, mode='w') calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list calcinfo.remote_symlink_list = remote_symlink_list # Retrieve per default only out file and inp.xml file? retrieve_list = [] retrieve_list.append(self._INPXML_FILE_NAME) retrieve_list.append(self._OUTPUT_FILE_NAME) retrieve_list.append(self._SHELLOUT_FILE_NAME) retrieve_list.append(self._ERROR_FILE_NAME) retrieve_list.append(self._STRUCT_FILE_NAME) retrieve_list.append(self._INPUT_FILE_NAME) # user specific retrieve add_retrieve = settings_dict.get('additional_retrieve_list', []) for file1 in add_retrieve: retrieve_list.append(file1) remove_retrieve = settings_dict.get('remove_from_retrieve_list', []) for file1 in remove_retrieve: if file1 in retrieve_list: retrieve_list.remove(file1) calcinfo.retrieve_list = [] for file1 in retrieve_list: calcinfo.retrieve_list.append(file1) codeinfo = CodeInfo() # , "-electronConfig"] # TODO? let the user decide -electronconfig? # We support different inpgen and fleur version via reading the version from the code node extras code_extras = code.extras code_version = code_extras.get('version', 32) if int(code_version) < 32: # run old inpgen cmdline_params = ['-explicit'] codeinfo.stdin_name = self._INPUT_FILE_NAME else: cmdline_params = ['-explicit', '-inc', '+all', '-f', f'{self._INPUT_FILE_NAME}'] if 'profile' in settings_dict: cmdline_params.extend(['-profile', settings_dict['profile']]) # user specific commandline_options for command in settings_dict.get('cmdline', []): cmdline_params.append(command) codeinfo.cmdline_params = (list(cmdline_params)) codeinfo.code_uuid = code.uuid codeinfo.stdout_name = self._SHELLOUT_FILE_NAME # shell output will be piped in file codeinfo.stderr_name = self._ERROR_FILE_NAME # std error too calcinfo.codes_info = [codeinfo] return calcinfo
def _lowercase_dict(dic, dict_name): """ Converts every entry in a dictionary to lowercase :param dic: parameters dictionary :param dict_name: dictionary name """ from collections import Counter if not isinstance(dic, dict): raise TypeError('_lowercase_dict accepts only dictionaries as argument') new_dict = {str(k).lower(): val for k, val in dic.items()} if len(new_dict) != len(dic): num_items = Counter(str(k).lower() for k in dic.keys()) double_keys = ','.join([k for k, val in num_items if val > 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
[docs]def write_inpgen_file_aiida_struct(structure, file, input_params=None, settings=None): """Wraps around masci_tools write inpgen_file, unpacks aiida structure""" from masci_tools.io.fleur_inpgen import write_inpgen_file atoms_dict_list = [] kind_list = [] for kind in structure.kinds: kind_list.append(kind.get_raw()) for site in structure.sites: atoms_dict_list.append(site.get_raw()) if settings is None: settings = {} write_settings = {} if 'significant_figures_cell' in settings: write_settings['significant_figures_cell'] = settings.get('significant_figures_cell') if 'significant_figures_positions' in settings: write_settings['significant_figures_positions'] = settings.get('significant_figures_positions') report = write_inpgen_file(structure.cell, atoms_dict_list, kind_list, file=file, pbc=structure.pbc, input_params=input_params, **write_settings) return report