def cmd_export( group, max_atoms, max_atomic_number, number_species, partial_occupancies, include_duplicates, no_cod_hydrogen, sssp_only, filename ): """Pass.""" from aiida import orm from aiida.common.constants import elements from aiida.tools.importexport import export filters_elements = set() filters_structures = {'and': []} if no_cod_hydrogen: filters_structures['and'].append({'id': {'!in': get_cod_hydrogen_structure_ids()}}) if max_atoms is not None: filters_structures['and'].append({'attributes.sites': {'shorter': max_atoms + 1}}) if max_atomic_number: filters_elements = filters_elements.union({e['symbol'] for z, e in elements.items() if z > max_atomic_number}) if sssp_only: # All elements with atomic number of Radon or lower, with the exception of Astatine filters_elements = filters_elements.union({e['symbol'] for z, e in elements.items() if z > 86 or z == 85}) builder = orm.QueryBuilder().append( orm.Group, filters={'id': group.pk}, tag='group').append( orm.StructureData, with_group='group', filters=filters_structures) duplicates = [] if max_atomic_number or sssp_only: structures = [] for structure, in builder.iterall(): if all([element not in filters_elements for element in structure.get_symbols_set()]): structures.append(structure) else: structures = builder.all(flat=True) if include_duplicates: for structure in structures: dupes = [] structure_duplicates = structure.get_extra('duplicates') for database, uuids in structure_duplicates.items(): dupes.extend(uuids) for duplicate in dupes: if duplicate != structure.uuid: duplicates.append(orm.load_node(duplicate)) export(structures + duplicates, outfile=filename, create_backward=False, return_backward=False)
def change_struc_imp_aux_wf(struc, imp_info): # Note: works for single imp at center only! from aiida.common.constants import elements as PeriodicTableElements _atomic_numbers = {data['symbol']: num for num, data in PeriodicTableElements.items()} new_struc = StructureData(cell=struc.cell) new_struc.pbc = struc.pbc # take also pbc values from parent struc isite = 0 for site in struc.sites: sname = site.kind_name kind = struc.get_kind(sname) pos = site.position # intermediate fix to avoid crash for old structures with vacuum:'{H0.00X1.00}' # use atom kind='X' in the future for new structures if kind.get_symbols_string()=='{H0.00X1.00}': zatom = 0 else: zatom = _atomic_numbers[kind.get_symbols_string()] if isite == imp_info.get_dict().get('ilayer_center'): zatom = imp_info.get_dict().get('Zimp') if type(zatom)==list: zatom = zatom[0] # here this works for single impurity only! symbol = PeriodicTableElements.get(zatom).get('symbol') new_struc.append_atom(position=pos, symbols=symbol) isite += 1 return new_struc
def structure_to_spglib_tuple(structure): """ Convert an AiiDA structure to a tuple of the format (cell, scaled_positions, element_numbers). :param structure: the AiiDA structure :return: (structure_tuple, kind_info, kinds) where structure_tuple is a tuple of format (cell, scaled_positions, element_numbers); kind_info is a dictionary mapping the kind_names to the numbers used in element_numbers. When possible, it uses the Z number of the element, otherwise it uses numbers > 1000; kinds is a list of the kinds of the structure. """ def get_new_number(the_list, start_from): """ Get the first integer >= start_from not yet in the list """ retval = start_from comp_list = sorted(_ for _ in the_list if _ >= start_from) current_pos = 0 found = False while not found: if len(comp_list) <= current_pos: return retval if retval == comp_list[current_pos]: current_pos += 1 retval += 1 else: found = True return retval z_numbers = {v['symbol']: k for k, v in elements.items()} cell = np.array(structure.cell) abs_pos = np.array([_.position for _ in structure.sites]) rel_pos = np.dot(abs_pos, np.linalg.inv(cell)) # kinds = {k.name: k for k in structure.kinds} kind_numbers = {} for kind in structure.kinds: if len(kind.symbols) == 1: realnumber = z_numbers[kind.symbols[0]] if realnumber in kind_numbers.values(): number = get_new_number(list(kind_numbers.values()), start_from=realnumber * 1000) else: number = realnumber kind_numbers[kind.name] = number else: number = get_new_number(list(kind_numbers.values()), start_from=200000) kind_numbers[kind.name] = number numbers = [kind_numbers[s.kind_name] for s in structure.sites] return ((cell, rel_pos, numbers), kind_numbers, list(structure.kinds))
def get_structure_data(structure): """ Function to take data from AiiDA's StructureData type and store it into a single numpy array of the following form: a = [[x-Position 1st atom, y-Position 1st atom, z-Position 1st atom, index 1st atom, charge 1st atom, 0.], [x-Position 2nd atom, y-Position 2nd atom, z-Position 2nd atom, index 2nd atom, charge 1st atom, 0.], [..., ..., ..., ..., ..., ...], ... ] :param structure: input structure of the type StructureData :return: numpy array a[# of atoms in the unit cell][5] containing the structure related data (positions in units of the unit cell length) :note: """ #import packages from aiida.common.constants import elements as PeriodicTableElements import numpy as np #get the connection between coordination number and element symbol _atomic_numbers = { data['symbol']: num for num, data in PeriodicTableElements.items() } #initialize the array that will be returned later (it will be a (# of atoms in the cell) x 6-matrix) a = np.zeros((len(structure.sites), 6)) k = 0 #running index for filling up the array with data correctly charges = [ ] #will be needed to return the charge number for the different atoms later #loop to fill up the a-array with positions, index, charge and a 0. for every atom in the cell sites = structure.sites n = len(structure.sites) + 1 #needed to do the indexing of atoms m = len(structure.sites) #needed to do the indexing of atoms for site in sites: for j in range(3): a[k][j] = site.position[j] sitekind = structure.get_kind(site.kind_name) naez = n - m m = m - 1 #convert the element symbol from StructureData to the charge number for ikind in range(len(sitekind.symbols)): site_symbol = sitekind.symbols[ikind] charges.append(_atomic_numbers[site_symbol]) i = len(charges) - 1 a[k][3] = int(naez) a[k][4] = float(charges[i]) k = k + 1 return a
def _prepare_xsf(self, index=None, main_file_name=''): # pylint: disable=unused-argument """ Write the given trajectory to a string of format XSF (for XCrySDen). """ from aiida.common.constants import elements _atomic_numbers = { data['symbol']: num for num, data in elements.items() } indices = list(range(self.numsteps)) if index is not None: indices = [index] return_string = 'ANIMSTEPS {}\nCRYSTAL\n'.format(len(indices)) # Do the checks once and for all here: structure = self.get_step_structure(index=0) if structure.is_alloy or structure.has_vacancies: raise NotImplementedError( 'XSF for alloys or systems with vacancies not implemented.') cells = self.get_cells() if cells is None: raise ValueError( 'No cell parameters have been supplied for TrajectoryData') positions = self.get_positions() symbols = self.symbols atomic_numbers_list = [_atomic_numbers[s] for s in symbols] nat = len(symbols) for idx in indices: return_string += 'PRIMVEC {}\n'.format(idx + 1) for cell_vector in cells[idx]: return_string += ' '.join( ['{:18.5f}'.format(i) for i in cell_vector]) return_string += '\n' return_string += 'PRIMCOORD {}\n'.format(idx + 1) return_string += '{} 1\n'.format(nat) for atn, pos in zip(atomic_numbers_list, positions[idx]): try: return_string += '{} {:18.10f} {:18.10f} {:18.10f}\n'.format( atn, pos[0], pos[1], pos[2]) except: print(atn, pos) raise return return_string.encode('utf-8'), {}
def _tuple_to_aiida(structure_tuple, kind_info=None, kinds=None): """ Convert an tuple of the format (cell, scaled_positions, element_numbers) to an AiiDA structure. Unless the element_numbers are identical to the Z number of the atoms, you should pass both kind_info and kinds, with the same format as returned by get_tuple_from_aiida_structure. .. deprecated:: 1.8 Use the methods in AiiDA instead. :param structure_tuple: the structure in format (structure_tuple, kind_info) :param kind_info: a dictionary mapping the kind_names to the numbers used in element_numbers. If not provided, assumes {element_name: element_Z} :param kinds: a list of the kinds of the structure. """ import warnings warnings.warn( 'this method has been deprecated and moved to AiiDA, see {}'.format( DEPRECATION_DOCS_URL), DeprecationWarning) from aiida.common.constants import elements from aiida.orm.data.structure import Kind, Site, StructureData import numpy as np import copy if kind_info is None and kinds is not None: raise ValueError("If you pass kind_info, you should also pass kinds") if kinds is None and kind_info is not None: raise ValueError("If you pass kinds, you should also pass kind_info") Z = {v['symbol']: k for k, v in elements.items()} cell, rel_pos, numbers = structure_tuple if kind_info: _kind_info = copy.copy(kind_info) _kinds = copy.copy(kinds) else: try: # For each site symbols = [elements[num]['symbol'] for num in numbers] except KeyError as e: raise ValueError( "You did not pass kind_info, but at least one number " "is not a valid Z number: {}".format(e.message)) _kind_info = {elements[num]['symbol']: num for num in set(numbers)} # Get the default kinds _kinds = [Kind(symbols=sym) for sym in set(symbols)] _kinds_dict = {k.name: k for k in _kinds} # Now I will use in any case _kinds and _kind_info if len(_kind_info.values()) != len(set(_kind_info.values())): raise ValueError( "There is at least a number repeated twice in kind_info!") # Invert the mapping mapping_num_kindname = {v: k for k, v in _kind_info.items()} # Create the actual mapping try: mapping_to_kinds = { num: _kinds_dict[kindname] for num, kindname in mapping_num_kindname.items() } except KeyError as e: raise ValueError("Unable to find '{}' in the kinds list".format( e.message)) try: site_kinds = [mapping_to_kinds[num] for num in numbers] except KeyError as e: raise ValueError( "Unable to find kind in kind_info for number {}".format(e.message)) out_structure = StructureData(cell=cell) for k in _kinds: out_structure.append_kind(k) abs_pos = np.dot(rel_pos, cell) if len(abs_pos) != len(site_kinds): raise ValueError( "The length of the positions array is different from the " "length of the element numbers") for kind, pos in zip(site_kinds, abs_pos): out_structure.append_site(Site(kind_name=kind.name, position=pos)) return out_structure
def _aiida_to_tuple(aiida_structure): """ Convert an AiiDA structure to a tuple of the format (cell, scaled_positions, element_numbers). .. deprecated:: 1.8 Use the methods in AiiDA instead. :param aiida_structure: the AiiDA structure :return: (structure_tuple, kind_info, kinds) where structure_tuple is a tuple of format (cell, scaled_positions, element_numbers); kind_info is a dictionary mapping the kind_names to the numbers used in element_numbers. When possible, it uses the Z number of the element, otherwise it uses numbers > 1000; kinds is a list of the kinds of the structure. """ import warnings warnings.warn( 'this method has been deprecated and moved to AiiDA, see {}'.format( DEPRECATION_DOCS_URL), DeprecationWarning) import numpy as np from aiida.common.constants import elements def get_new_number(the_list, start_from): """ Get the first integer >= start_from not yet in the list """ retval = start_from comp_list = sorted(_ for _ in the_list if _ >= start_from) current_pos = 0 found = False while not found: if len(comp_list) <= current_pos: return retval if retval == comp_list[current_pos]: current_pos += 1 retval += 1 else: found = True return retval Z = {v['symbol']: k for k, v in elements.items()} cell = np.array(aiida_structure.cell) abs_pos = np.array([_.position for _ in aiida_structure.sites]) rel_pos = np.dot(abs_pos, np.linalg.inv(cell)) kinds = {k.name: k for k in aiida_structure.kinds} kind_numbers = {} for kind in aiida_structure.kinds: if len(kind.symbols) == 1: realnumber = Z[kind.symbols[0]] if realnumber in list(kind_numbers.values()): number = get_new_number(list(kind_numbers.values()), start_from=realnumber * 1000) else: number = realnumber kind_numbers[kind.name] = number else: number = get_new_number(list(kind_numbers.values()), start_from=200000) kind_numbers[kind.name] = number numbers = [kind_numbers[s.kind_name] for s in aiida_structure.sites] return ((cell, rel_pos, numbers), kind_numbers, list(aiida_structure.kinds))
def generate_inputcard_from_structure(parameters, structure, input_filename, parent_calc=None, shapes=None, isvoronoi=False, use_input_alat=False, vca_structure=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 masci_tools.io.kkr_params import kkrparams from masci_tools.io.common_functions import get_Ang2aBohr, get_alat_from_bravais from aiida_kkr.calculations.voro import VoronoiCalculation # initialize list of warnings warnings = [] #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.items() } # 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 wmess = 'found alat in input parameters, this will trigger scaling of RMAX, GMAX and RCLUSTZ!' print('WARNING: ' + wmess) warnings.append(wmess) 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 sitekind.is_alloy: wght = sitekind.weights[ikind] else: wght = 1. if not sitekind.has_vacancies: zatom_tmp = _atomic_numbers[site_symbol] else: zatom_tmp = 0.0 if vca_structure and ikind > 0 and not isvoronoi: # for VCA case take weighted average (only for KKR code, voronoi code uses zatom of first site for dummy calculation) zatom = zatom * wght_last + zatom_tmp * wght # also reset weight to 1 wght = 1. else: zatom = zatom_tmp if vca_structure and isvoronoi: wght = 1. wght_last = wght # for VCA mode # make sure that for VCA only averaged position is written (or first for voronoi code) if ((vca_structure and ((len(sitekind.symbols) == 1) or (not isvoronoi and ikind == 1) or (isvoronoi and ikind == 0))) or (not vca_structure)): charges.append(zatom) weights.append(wght) isitelist.append(isite) weights = array(weights) isitelist = array(isitelist) charges = array(charges) positions = array(positions) # workaround for voronoi calculation with Zatom=83 (Bi potential not there!) if isvoronoi: from numpy import where mask_replace_Bi_Pb = where(charges == 83) if len(mask_replace_Bi_Pb[0]) > 0: charges[mask_replace_Bi_Pb] = 82 wmess = 'Bi potential not available, using Pb instead!!!' print('WARNING: ' + wmess) warnings.append(wmess) ###################################### # Prepare keywords for kkr from input structure # get parameter dictionary input_dict = parameters.get_dict() # remove special keys that are used for special cases but are not part of the KKR parameter set for key in _ignored_keys: if input_dict.get(key) is not None: wmess = 'automatically removing value of key {}'.format(key) print('WARNING: ' + wmess) warnings.append(wmess) input_dict.pop(key) # 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: wmess = 'automatically removing value of key {}'.format(key) print('WARNING: ' + wmess) warnings.append(wmess) 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: wmess = 'rescale RMAX: {}'.format(alat_input / alat) print('WARNING: ' + wmess) warnings.append(wmess) input_dict['RMAX'] = input_dict['RMAX'] * alat_input / alat if input_dict.get('GMAX') is not None: wmess = 'rescale GMAX: {}'.format(1 / (alat_input / alat)) print('WARNING: ' + wmess) warnings.append(wmess) input_dict['GMAX'] = input_dict['GMAX'] * 1 / (alat_input / alat) if input_dict.get('RCLUSTZ') is not None: wmess = 'rescale RCLUSTZ: {}'.format(alat_input / alat) print('WARNING: ' + wmess) warnings.append(wmess) input_dict['RCLUSTZ'] = input_dict['RCLUSTZ'] * alat_input / alat if input_dict.get('RCLUSTXY') is not None: wmess = 'rescale RCLUSTXY: {}'.format(alat_input / alat) print('WARNING: ' + wmess) warnings.append(wmess) 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 (always in res.emin of voronoi and kkr) if not provided in input node if ('EMIN' not in list(input_dict.keys()) or input_dict['EMIN'] is None) and parent_calc is not None: wmess = 'Overwriting EMIN with value from parent calculation {}'.format( parent_calc) print('WARNING: ' + wmess) warnings.append(wmess) if parent_calc.process_class == VoronoiCalculation: emin = parent_calc.outputs.output_parameters.get_dict().get('emin') else: emin = parent_calc.outputs.output_parameters.get_dict().get( 'energy_contour_group').get('emin') print('Setting emin:', emin, 'is emin None?', emin is None) params.set_value('EMIN', emin) # overwrite keywords with input parameter for key in list(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, warnings
def prepare_calc_parameters(parameters, spin_type, magnetization_per_site, structure, kmax): """Prepare a calc_parameter node for a inpgen jobcalc Depending on the imports merge information :param parameters: given calc_parameter orm.Dict node to merge with :param spin_type: type of magnetic calculation :param magnetization_per_site: list :param kmax: int, basis cutoff for the simulations :return: orm.Dict """ # pylint: disable=too-many-locals #parameters_b = None # Spin type options if spin_type == SpinType.NONE: jspins = 1 else: jspins = 2 add_parameter_dict = {'comp': {'jspins': jspins}} # electronic Structure options # None. Gff we want to increase the smearing and kpoint density in case of metal if kmax is not None: # add kmax from protocol add_parameter_dict = recursive_merge(add_parameter_dict, {'comp': { 'kmax': kmax }}) if parameters is not None: add_parameter_dict = recursive_merge(add_parameter_dict, parameters.get_dict()) # In general better use aiida-fleur merge methods for calc parameters... if magnetization_per_site is not None: # Do for now sake we have this. If the structure is not rightly prepared it will run, but # the set magnetization will be wrong atomic_numbers = { data['symbol']: num for num, data in PeriodicTableElements.items() } if spin_type == SpinType.NONE: import warnings warnings.warn( '`magnetization_per_site` will be ignored as `spin_type` is set to SpinType.NONE' ) if spin_type == SpinType.COLLINEAR: # add atom lists for each kind and set bmu in muBohr # this will break symmetry and changes the structure, if needed # be careful here because this may override things the plugin is during already. # This is very fragile and not robust, it will be wrong if input structure is wrong, # i.e does not have enough kinds but we do not change the input structure that way. # In the end this should be implemented aiida-fleur and imported mag_dict = {} sites = list(structure.sites) for i, val in enumerate(magnetization_per_site): kind_name = sites[i].kind_name kind = structure.get_kind(kind_name) site_symbol = kind.symbols[0] # assume atoms atomic_number = atomic_numbers[site_symbol] if kind_name != site_symbol: head = kind_name.rstrip('0123456789') try: kind_namet = int(kind_name[len(head):]) except ValueError: kind_namet = 0 kind_id = f'{atomic_number}.{kind_namet}' else: kind_id = f'{atomic_number}' mag_dict[f'atom{i}'] = { 'z': atomic_number, 'id': kind_id, 'bmu': val } # Better would be a valid parameter data merge from aiida-fleur, to merge atom lists # right add_parameter_dict = recursive_merge(add_parameter_dict, mag_dict) #structure, parameters_b = break_symmetry(structure, parameterdata=orm.Dict(dict=add_parameter_dict)) new_parameters = orm.Dict(dict=add_parameter_dict) return new_parameters, structure
def prepare_for_submission( self, folder): # noqa: MC0001 - is mccabe too complex funct - """ Create the input files from the input nodes passed to this instance of the `CalcJob`. :param folder: an `aiida.common.folders.Folder` to temporarily write files on disk :return: `aiida.common.datastructures.CalcInfo` instance """ # ============================ Initializations ============================= # All input ports are validated, here asses their presence in case optional. code = self.inputs.code # self.initialize preprocess structure and basis. Decides whether use ions or pseudos structure, basis_dict, floating_species_names, ion_or_pseudo_str = self.initialize( ) ion_or_pseudo = self.inputs[ion_or_pseudo_str] parameters = self.inputs.parameters if 'kpoints' in self.inputs: kpoints = self.inputs.kpoints else: kpoints = None # As internal convention, the keys of the settings dict are uppercase if 'settings' in self.inputs: settings = self.inputs.settings.get_dict() settings_dict = {str(k).upper(): v for (k, v) in settings.items()} else: settings_dict = {} if 'bandskpoints' in self.inputs: bandskpoints = self.inputs.bandskpoints else: bandskpoints = None if 'parent_calc_folder' in self.inputs: parent_calc_folder = self.inputs.parent_calc_folder else: parent_calc_folder = None lua_inputs = self.inputs.lua if 'script' in lua_inputs: lua_script = lua_inputs.script else: lua_script = None if 'parameters' in lua_inputs: lua_parameters = lua_inputs.parameters else: lua_parameters = None if 'input_files' in lua_inputs: lua_input_files = lua_inputs.input_files else: lua_input_files = None if 'retrieve_list' in lua_inputs: lua_retrieve_list = lua_inputs.retrieve_list else: lua_retrieve_list = None # List of files to copy in the folder where the calculation runs, e.g. pseudo files local_copy_list = [] # List of files for restart remote_copy_list = [] # ================ Preprocess of input parameters ================= input_params = FDFDict(parameters.get_dict()) input_params.update( {'system-name': self.inputs.metadata.options.prefix}) input_params.update( {'system-label': self.inputs.metadata.options.prefix}) input_params.update({'use-tree-timer': 'T'}) input_params.update({'xml-write': 'T'}) input_params.update({'number-of-species': len(structure.kinds)}) input_params.update({'number-of-atoms': len(structure.sites)}) input_params.update({'geometry-must-converge': 'T'}) input_params.update({'lattice-constant': '1.0 Ang'}) input_params.update({'atomic-coordinates-format': 'Ang'}) if lua_script is not None: input_params.update({'md-type-of-run': 'Lua'}) input_params.update({'lua-script': lua_script.filename}) local_copy_list.append( (lua_script.uuid, lua_script.filename, lua_script.filename)) if lua_input_files is not None: # Copy the whole contents of the FolderData object for file in lua_input_files.list_object_names(): local_copy_list.append((lua_input_files.uuid, file, file)) if ion_or_pseudo_str == "ions": input_params.update({'user-basis': 'T'}) # NOTES: # 1) The lattice-constant parameter must be 1.0 Ang to impose the units and consider # that the dimenstions of the lattice vectors are already correct with no need of alat. # This breaks the band-k-points "pi/a" option. The use of this option is banned. # 2) The implicit coordinate convention of the StructureData class corresponds to the "Ang" # convention in Siesta. That is why "atomic-coordinates-format" is blocked and reset. # 3) The Siesta code doesn't raise any warining if the geometry is not converged, unless # the keyword geometry-must-converge is set. That's why it is always added. # ============================ Preparation of input data ================================= # -------------------------------- CELL_PARAMETERS --------------------------------------- cell_parameters_card = "%block lattice-vectors\n" for vector in structure.cell: cell_parameters_card += ("{0:18.10f} {1:18.10f} {2:18.10f}" "\n".format(*vector)) cell_parameters_card += "%endblock lattice-vectors\n" # ----------------------------ATOMIC_SPECIES & PSEUDOS/IONS------------------------------- atomic_species_card_list = [] # Dictionary to get the atomic number of a given element datmn = {v['symbol']: k for k, v in elements.items()} spind = {} spcount = 0 for kind in structure.kinds: spcount += 1 # species count spind[kind.name] = spcount atomic_number = datmn[kind.symbol] # Siesta expects negative atomic numbers for floating species if kind.name in floating_species_names: atomic_number = -atomic_number #Create the core of the chemicalspecieslabel block atomic_species_card_list.append("{0:5} {1:5} {2:5}\n".format( spind[kind.name], atomic_number, kind.name.rjust(6))) psp_or_ion = ion_or_pseudo[kind.name] # Add pseudo (ion) file to the list of files to copy (create), with the appropiate name. # In the case of sub-species (different kind.name but same kind.symbol, e.g., 'C_surf', # sharing the same pseudo with 'C'), we copy the file ('C.psf') twice, once as 'C.psf', # and once as 'C_surf.psf'. This is required by Siesta. # It is passed as list of tuples with format ('node_uuid', 'filename', 'relativedestpath'). # Since no subfolder is present in Siesta for pseudos, filename == relativedestpath. if isinstance(psp_or_ion, IonData): file_name = kind.name + ".ion" with folder.open(file_name, 'w', encoding='utf8') as handle: handle.write(psp_or_ion.get_content_ascii_format()) if isinstance(psp_or_ion, (PsfData, DeprecatedPsfData)): local_copy_list.append( (psp_or_ion.uuid, psp_or_ion.filename, kind.name + ".psf")) if isinstance(psp_or_ion, (PsmlData, DeprecatedPsmlData)): local_copy_list.append((psp_or_ion.uuid, psp_or_ion.filename, kind.name + ".psml")) atomic_species_card_list = (["%block chemicalspecieslabel\n"] + list(atomic_species_card_list)) atomic_species_card = "".join(atomic_species_card_list) atomic_species_card += "%endblock chemicalspecieslabel\n" # Free memory del atomic_species_card_list # -------------------------------------- ATOMIC_POSITIONS ----------------------------------- atomic_positions_card_list = [ "%block atomiccoordinatesandatomicspecies\n" ] countatm = 0 for site in structure.sites: countatm += 1 atomic_positions_card_list.append( "{0:18.10f} {1:18.10f} {2:18.10f} {3:4} {4:6} {5:6}\n".format( site.position[0], site.position[1], site.position[2], spind[site.kind_name], site.kind_name.rjust(6), countatm)) atomic_positions_card = "".join(atomic_positions_card_list) del atomic_positions_card_list # Free memory atomic_positions_card += "%endblock atomiccoordinatesandatomicspecies\n" # --------------------------------------- K-POINTS ---------------------------------------- # It is optional, if not specified, gamma point only is performed (default of siesta) if kpoints is not None: mesh, offset = kpoints.get_kpoints_mesh() kpoints_card_list = ["%block kgrid_monkhorst_pack\n"] kpoints_card_list.append("{0:6} {1:6} {2:6} {3:18.10f}\n".format( mesh[0], 0, 0, offset[0])) kpoints_card_list.append("{0:6} {1:6} {2:6} {3:18.10f}\n".format( 0, mesh[1], 0, offset[1])) kpoints_card_list.append("{0:6} {1:6} {2:6} {3:18.10f}\n".format( 0, 0, mesh[2], offset[2])) kpoints_card = "".join(kpoints_card_list) kpoints_card += "%endblock kgrid_monkhorst_pack\n" del kpoints_card_list # ------------------------------------ K-POINTS-FOR-BANDS ---------------------------------- # Two possibility are supported in Siesta: BandLines ad BandPoints. # User can't choose directly one of the two options, BandsLine is set automatically # if bandskpoints has labels, BandsPoints if bandskpoints has no labels. # BandLinesScale=pi/a not supported because a=1 always. BandLinesScale ReciprocalLatticeVectors # always set. if bandskpoints is not None: #the band line scale bandskpoints_card_list = [ "BandLinesScale ReciprocalLatticeVectors\n" ] #set the BandPoints if bandskpoints.labels is None: bandskpoints_card_list.append("%block BandPoints\n") for kpo in bandskpoints.get_kpoints(): bandskpoints_card_list.append( "{0:8.3f} {1:8.3f} {2:8.3f} \n".format( kpo[0], kpo[1], kpo[2])) fbkpoints_card = "".join(bandskpoints_card_list) fbkpoints_card += "%endblock BandPoints\n" #set the BandLines else: bandskpoints_card_list.append("%block BandLines\n") savindx = [] listforbands = bandskpoints.get_kpoints() for indx, label in bandskpoints.labels: savindx.append(indx) rawindex = 0 for indx, label in bandskpoints.labels: rawindex = rawindex + 1 x, y, z = listforbands[indx] if rawindex == 1: bandskpoints_card_list.append( "{0:3} {1:8.3f} {2:8.3f} {3:8.3f} {4:1} \n".format( 1, x, y, z, label)) else: bandskpoints_card_list.append( "{0:3} {1:8.3f} {2:8.3f} {3:8.3f} {4:1} \n".format( indx - savindx[rawindex - 2], x, y, z, label)) fbkpoints_card = "".join(bandskpoints_card_list) fbkpoints_card += "%endblock BandLines\n" del bandskpoints_card_list # ================================= Operations for restart ================================= # The presence of a 'parent_calc_folder' input node signals that we want to # get something from there, as indicated in the self._restart_copy_from attribute. # In Siesta's case, for now, just the density-matrix file is copied # to the current calculation's working folder. # ISSUE: Is this mechanism flexible enough? An alternative would be to # pass the information about which file(s) to copy in the metadata.options dictionary if parent_calc_folder is not None: remote_copy_list.append( (parent_calc_folder.computer.uuid, os.path.join(parent_calc_folder.get_remote_path(), self._restart_copy_from), self._restart_copy_to)) input_params.update({'dm-use-save-dm': "T"}) # ===================================== FDF file creation ==================================== # To have easy access to inputs metadata options metadataoption = self.inputs.metadata.options # input_filename = self.inputs.metadata.options.input_filename input_filename = folder.get_abs_path(metadataoption.input_filename) # Print to file with open(input_filename, 'w') as infile: # Parameters for k, v in sorted(input_params.get_filtered_items()): infile.write("%s %s\n" % (k, v)) # Basis set info is processed just like the general parameters section. if basis_dict: #It migh also be empty dict. In such case we do not write. infile.write("#\n# -- Basis Set Info follows\n#\n") for k, v in basis_dict.items(): infile.write("%s %s\n" % (k, v)) # Write previously generated cards now infile.write("#\n# -- Structural Info follows\n#\n") infile.write(atomic_species_card) infile.write(cell_parameters_card) infile.write(atomic_positions_card) if kpoints is not None: infile.write("#\n# -- K-points Info follows\n#\n") infile.write(kpoints_card) if bandskpoints is not None: infile.write("#\n# -- Bandlines/Bandpoints Info follows\n#\n") infile.write(fbkpoints_card) # Write max wall-clock time # This should prevent SiestaCalculation from being terminated by scheduler, however the # strategy is not 100% effective since SIESTA checks the simulation time versus max-walltime # only at the end of each SCF and geometry step. The scheduler might kill the process in between. infile.write("#\n# -- Max wall-clock time block\n#\n") infile.write( f"maxwalltime {metadataoption.max_wallclock_seconds}\n") # ================================== Lua parameters file =================================== if lua_parameters is not None: lua_config_filename = folder.get_abs_path("config.lua") # Generate a 'config.lua' file with Lua syntax with open(lua_config_filename, 'w') as f_lua: f_lua.write("--- Lua script parameters \n") for k, v in lua_parameters.get_dict().items(): if isinstance(v, str): f_lua.write('%s = "%s"\n' % (k, v)) else: f_lua.write("%s = %s\n" % (k, v)) # ============================= Code and Calc info ========================================= # Code information object and Calc information object are now # only used to set up the CMDLINE (the bash line that launches siesta) # and to set up the list of files to retrieve. cmdline_params = settings_dict.pop('CMDLINE', []) codeinfo = CodeInfo() codeinfo.cmdline_params = list(cmdline_params) codeinfo.stdin_name = metadataoption.input_filename codeinfo.stdout_name = metadataoption.output_filename codeinfo.code_uuid = code.uuid calcinfo = CalcInfo() calcinfo.uuid = str(self.uuid) calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list calcinfo.codes_info = [codeinfo] # Retrieve by default: the output file, the xml file, the messages file, and the json timing file. # If bandskpoints, also the bands file is added to the retrieve list. calcinfo.retrieve_list = [] xml_file = str(metadataoption.prefix) + ".xml" bands_file = str(metadataoption.prefix) + ".bands" calcinfo.retrieve_list.append(metadataoption.output_filename) calcinfo.retrieve_list.append(xml_file) calcinfo.retrieve_list.append(self._JSON_FILE) calcinfo.retrieve_list.append(self._MESSAGES_FILE) calcinfo.retrieve_list.append(self._BASIS_ENTHALPY_FILE) calcinfo.retrieve_list.append("*.ion.xml") if bandskpoints is not None: calcinfo.retrieve_list.append(bands_file) if lua_retrieve_list is not None: calcinfo.retrieve_list += lua_retrieve_list.get_list() # If we ever want to avoid having the config.lua file in the repository, # since the information is already in the lua_parameters dictionary: # if lua_parameters is not None: # calcinfo.provenance_exclude_list = ['config.lua'] # Any other files specified in the settings dictionary settings_retrieve_list = settings_dict.pop('ADDITIONAL_RETRIEVE_LIST', []) calcinfo.retrieve_list += settings_retrieve_list return calcinfo
def create_corehole_para(structure, kind, econfig, species_name='corehole', parameterdata=None): """ This methods sets of electron configurations for a kind or position given, make sure to break the symmetry for this position/kind beforehand, otherwise you will create several coreholes. :param structure: StructureData :param kind: a string with the kind_name (TODO: alternative the kind object) :param econfig: string, e.g. econfig = "[Kr] 5s2 4d10 4f13 | 5p6 5d5 6s2" to set, i.e. the corehole :return: a Dict node """ # TODO: Since fleur MaXR5 there is a default econfig file and the order behavior # has changed. now to atom lists only change the default if they have an id. from aiida.common.constants import elements as PeriodicTableElements from aiida import orm _atomic_numbers = { data['symbol']: num for num, data in PeriodicTableElements.items() } #from aiida_fleur.tools.merge_parameter import merge_parameter kindo = structure.get_kind(kind) symbol = kindo.symbol head = kindo.name.rstrip('01223456789') charge = _atomic_numbers[kindo.symbol] a_id = float('{}.{}'.format(charge, kindo.name[len(head):])) # get kind symbol, get kind name, #&atom element="W" jri=921 lmax=8 rmt=2.52 dx=0.014 lo="5p" econfig="[Kr] 5s2 4d10 4f13 | 5p6 5d4 6s2" / #count = 0 if parameterdata: new_parameterd = parameterdata.get_dict( ) # dict()otherwise parameterdata is changed for key, val in new_parameterd.items(): if 'atom' in key: if val.get('element', None) == symbol: # remember atomic id is atomic number.some int if (a_id and float(a_id) == float(val.get('id', -1))): val.update({'econfig': econfig}) break if not a_id: val.update({'econfig': econfig}) else: pass else: if a_id: if species_name: new_parameterd = { 'atom': { 'element': symbol, 'econfig': econfig, 'id': a_id, 'name': species_name } } else: new_parameterd = { 'atom': { 'element': symbol, 'econfig': econfig, 'id': a_id } } else: new_parameterd = {'atom': {'element': symbol, 'econfig': econfig}} new_parameter = orm.Dict(dict=new_parameterd) #if parameterdata: # new_parameter = merge_parameter(parameterdata, new_parameter) return new_parameter #structure