def _prepare_xsf(self, index=None): """ 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.iteritems() } indices = range(self.numsteps) if index is not None: indices = [index] return_string = "ANIMSTEPS {}\nCRYSTAL\n".format(len(indices)) for idx in indices: return_string += "PRIMVEC {}\n".format(idx + 1) structure = self.get_step_structure(index=idx) sites = structure.sites if structure.is_alloy() or structure.has_vacancies(): raise NotImplementedError("XSF for alloys or systems with " "vacancies not implemented.") for cell_vector in structure.cell: return_string += " ".join(["%18.5f" % i for i in cell_vector]) return_string += "\n" return_string += "PRIMCOORD {}\n".format(idx + 1) return_string += "%d 1\n" % len(sites) for site in sites: # I checked above that it is not an alloy, therefore I take the # first symbol return_string += "%s " % _atomic_numbers[structure.get_kind( site.kind_name).symbols[0]] return_string += "%18.10f %18.10f %18.10f\n" % tuple( site.position) return return_string
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" ! THis is the new econfig therefore returns a parameterData node """ from aiida.common.constants import elements as PeriodicTableElements _atomic_numbers = {data['symbol']: num for num, data in PeriodicTableElements.iteritems()} #from aiida_fleur.tools.merge_parameter import merge_parameter kindo = structure.get_kind(kind) symbol = kindo.symbol head = kindo.name.rstrip('01223456789') #print(kindo) charge = _atomic_numbers[kindo.symbol] id = float("{}.{}".format(charge, kindo.name[len(head):])) #print('id {}'.format(id)) # 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.iteritems(): if 'atom' in key: if val.get('element', None) == symbol: # remember id is atomic number.some int if (id and float(id) == float(val.get('id', -1))): val.update({'econfig' : econfig}) #print 'here1' break elif not id: #print 'here2' val.update({'econfig' : econfig}) else: pass else: if id: if species_name: new_parameterd = {'atom': {'element' : symbol, 'econfig' : econfig, 'id' : id, 'name' : species_name}} else: new_parameterd = {'atom': {'element' : symbol, 'econfig' : econfig, 'id' : id}} else: new_parameterd = {'atom': {'element' : symbol, 'econfig' : econfig}} new_parameter= ParameterData(dict=new_parameterd) #if parameterData: # new_parameter = merge_parameter(parameterData, new_parameter) return new_parameter#structure
def _prepare_xsf(self, index=None): """ 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.iteritems() } indices = 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() positions = self.get_positions() symbols = self.get_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) #~ structure = self.get_step_structure(index=idx) #~ sites = structure.sites #~ if structure.is_alloy() or structure.has_vacancies(): #~ raise NotImplementedError("XSF for alloys or systems with " #~ "vacancies not implemented.") 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 sym, pos raise return return_string
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.iteritems() } new_struc = StructureData(cell=struc.cell) isite = 0 for site in struc.sites: sname = site.kind_name kind = struc.get_kind(sname) pos = site.position zatom = _atomic_numbers[kind.get_symbols_string()] if isite == imp_info.get_dict().get('ilayer_center'): zatom = imp_info.get_dict().get('Zimp')[0] symbol = PeriodicTableElements.get(zatom).get('symbol') new_struc.append_atom(position=pos, symbols=symbol) isite += 1 return new_struc
def _prepare_for_submission(self, tempfolder, inputdict): """ This is the routine to be called when you want to create the input files and related stuff with a plugin. :param tempfolder: a aiida.common.folders.Folder subclass where the plugin should put all its files. :param inputdict: a dictionary with the input nodes, as they would be returned by get_inputdata_dict (without the Code!) """ local_copy_list = [] remote_copy_list = [] # Process the settings dictionary first # Settings can be undefined, and defaults to an empty dictionary settings = inputdict.pop(self.get_linkname('settings'), None) if settings is None: settings_dict = {} else: if not isinstance(settings, ParameterData): raise InputValidationError( "settings, if specified, must be of " "type ParameterData") # Settings converted to UPPERCASE # Presumably to standardize the usage and avoid # ambiguities settings_dict = _uppercase_dict(settings.get_dict(), dict_name='settings') 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 is not of type " "ParameterData") try: structure = inputdict.pop(self.get_linkname('structure')) except KeyError: raise InputValidationError("No structure specified for this " "calculation") if not isinstance(structure, StructureData): raise InputValidationError( "structure is not of type StructureData") bandskpoints = inputdict.pop(self.get_linkname('bandskpoints'), None) if bandskpoints is None: flagbands = False else: flagbands = True if not isinstance(bandskpoints, KpointsData): raise InputValidationError( "kpoints for bands is not of type KpointsData") singlefile = inputdict.pop(self.get_linkname('singlefile'), None) if singlefile is not None: if not isinstance(singlefile, SinglefileData): raise InputValidationError("singlefile, if specified," "must be of type SinglefileData") parent_calc_folder = inputdict.pop(self.get_linkname('parent_folder'), None) if parent_calc_folder is not None: if not isinstance(parent_calc_folder, RemoteData): raise InputValidationError("parent_calc_folder, if specified," "must be of type RemoteData") try: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError( "No code specified for this calculation") # Here, there should be no more parameters... if inputdict: raise InputValidationError("The following input data nodes are " "unrecognized: {}".format( inputdict.keys())) ############################## # END OF INITIAL INPUT CHECK # ############################## # # There should be a warning for duplicated (canonicalized) keys # in the original dictionary in the script input_params = FDFDict(parameters.get_dict()) # Look for blocked keywords and # add the proper values to the dictionary for blocked_key in self._aiida_blocked_keywords: canonical_blocked = FDFDict.translate_key(blocked_key) for key in input_params: if key == canonical_blocked: raise InputValidationError( "You cannot specify explicitly the '{}' flag in the " "input parameters".format( input_params.get_last_key(key))) input_params.update({'system-name': self._PREFIX}) input_params.update({'system-label': self._PREFIX}) input_params.update({'number-of-species': len(structure.kinds)}) input_params.update({'number-of-atoms': len(structure.sites)}) # # Regarding the lattice-constant parameter: # -- The variable "alat" is not typically kept anywhere, and # has already been used to define the vectors. # We need to specify that the units of these vectors are Ang... input_params.update({'lattice-constant': '1.0 Ang'}) # Note that this will break havoc with the band-k-points "pi/a" # option. The use of this option should be banned. # Note that the implicit coordinate convention of the Structure # class corresponds to the "Ang" convention in Siesta. # The "atomic-coordinates-format" keyword is blocked to ScaledCartesian, # which is given in terms of the lattice constant (1.0 Ang). input_params.update({'atomic-coordinates-format': 'ScaledCartesian'}) # ============== 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 ------------ # Only the species index and the mass are necessary # Dictionary to get the mass of a given element datmn = dict([(v['symbol'], v['mass']) for k, v in elements.iteritems()]) spind = {} spcount = 0 for kind in structure.kinds: spcount += 1 spind[kind.name] = spcount # ------------ 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:10} \n".format( site.position[0], site.position[1], site.position[2], spind[site.kind_name], datmn[kind.symbol])) atomic_positions_card = "".join(atomic_positions_card_list) del atomic_positions_card_list # Free memory atomic_positions_card += "%endblock atomiccoordinatesandatomicspecies\n" # --------------- K-POINTS-FOR-BANDS ----------------! #This part is computed only if flagbands=True #Two possibility are supported in Siesta: BandLines ad BandPoints #At the moment the 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 is not supported at the moment because currently #a=1 always. BandLinesScale ReciprocalLatticeVectors is always set if flagbands: bandskpoints_card_list = [ "BandLinesScale ReciprocalLatticeVectors\n" ] if bandskpoints.labels == None: bandskpoints_card_list.append("%block BandPoints\n") for s in bandskpoints.get_kpoints(): bandskpoints_card_list.append( "{0:8.3f} {1:8.3f} {2:8.3f} \n".format( s[0], s[1], s[2])) fbkpoints_card = "".join(bandskpoints_card_list) fbkpoints_card += "%endblock BandPoints\n" else: bandskpoints_card_list.append("%block BandLines\n") savs = [] listforbands = bandskpoints.get_kpoints() for s, m in bandskpoints.labels: savs.append(s) rawindex = 0 for s, m in bandskpoints.labels: rawindex = rawindex + 1 nkpnt, x, y, z, = listforbands[s] if rawindex == 1: bandskpoints_card_list.append( "{0:2} {1:8.3f} {2:8.3f} {3:8.3f} {4:1}\n".format( 1, x, y, z, m)) else: bandskpoints_card_list.append( "{0:.0f} {1:8.3f} {2:8.3f} {3:8.3f} {4:1}\n". format(nkpnt, x, y, z, m)) fbkpoints_card = "".join(bandskpoints_card_list) fbkpoints_card += "%endblock BandLines\n" del bandskpoints_card_list # -------------ADDITIONAL FILES ----------- # I create the subfolder that will contain additional Siesta files tempfolder.get_subfolder(self._SFILES_SUBFOLDER, create=True) # I create the subfolder with the output data tempfolder.get_subfolder(self._OUTPUT_SUBFOLDER, create=True) if singlefile is not None: lfile = singlefile.get_file_abs_path().split("path/", 1)[1] local_copy_list.append((singlefile.get_file_abs_path(), os.path.join(self._SFILES_SUBFOLDER, lfile))) # ================ Namelists and cards =================== input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME) with open(input_filename, 'w') as infile: # here print keys and values tp file for k, v in sorted(input_params.iteritems()): infile.write(get_input_data_text(k, v)) # ,mapping=mapping_species)) # Write previously generated cards now infile.write("#\n# -- Structural Info follows\n#\n") infile.write(cell_parameters_card) infile.write(atomic_positions_card) if flagbands: infile.write("#\n# -- Bandlines/Bandpoints Info follows\n#\n") infile.write(fbkpoints_card) # ------------------------------------- END of fdf file creation # 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, it is just the density-matrix file # # It will be copied to the current calculation's working folder. if parent_calc_folder is not None: remote_copy_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), self._restart_copy_from), self._restart_copy_to)) calcinfo = CalcInfo() calcinfo.uuid = self.uuid # # Empty command line by default # Why use 'pop' ? cmdline_params = settings_dict.pop('CMDLINE', []) # Comment this paragraph better, if applicable to Siesta # #we commented calcinfo.stin_name and added it here in cmdline_params #in this way the mpirun ... pw.x ... < aiida.in #is replaced by mpirun ... pw.x ... -in aiida.in # in the scheduler, _get_run_line, if cmdline_params is empty, it # simply uses < calcinfo.stin_name if cmdline_params: calcinfo.cmdline_params = list(cmdline_params) calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list calcinfo.stdin_name = self._INPUT_FILE_NAME calcinfo.stdout_name = self._OUTPUT_FILE_NAME calcinfo.fc_name = self._FC_FILE_NAME # # Code information object # codeinfo = CodeInfo() codeinfo.cmdline_params = list(cmdline_params) codeinfo.stdin_name = self._INPUT_FILE_NAME codeinfo.stdout_name = self._OUTPUT_FILE_NAME codeinfo.fc_name = self._FC_FILE_NAME codeinfo.code_uuid = code.uuid calcinfo.codes_info = [codeinfo] # Retrieve by default: the output file, the xml file, and the # messages file. # If flagbands=True we also add the bands file to the retrieve list! # This is extremely important because the parser parses the bands # only if aiida.bands is in the retrieve list!! calcinfo.retrieve_list = [] calcinfo.retrieve_list.append(self._OUTPUT_FILE_NAME) if flagbands: calcinfo.retrieve_list.append(self._BANDS_FILE_NAME) # 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 _prepare_for_submission(self, tempfolder, inputdict): """ This is the routine to be called when you want to create the input files for a SPEX with the plug-in. :param tempfolder: a aiida.common.folders.Folder subclass where the plugin should put all its files. :param inputdict: a dictionary with the input nodes, as they would be returned by get_inputdata_dict (without the Code!) """ #from aiida.common.utils import get_unique_filename, get_suggestion #import re # 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() } possible_namelists = self._possible_namelists possible_params = self._possible_params local_copy_list = [] remote_copy_list = [] remote_symlink_list = [] bulk = True film = False # convert these 'booleans' to the inpgen format. replacer_values_bool = [ True, False, 'True', 'False', 't', 'T', 'F', 'f' ] # some keywords require a string " around them in the input file. string_replace = ['econfig', 'lo', 'element', 'name'] # of some keys only the values are writen to the file, specify them here. val_only_namelist = ['soc', 'qss'] # Scaling comes from the Structure # but we have to convert from Angstroem to a.u (bohr radii) scaling_factors = [1.0, 1.0, 1.0] # scaling_lat = 1. #/bohr_to_ang scaling_pos = 1. / bohr_to_ang # Angstrom to atomic own_lattice = False #not self._use_aiida_structure # The inpfile gen is run in serial TODO: How to do this by default? #self.set_withmpi(False) ########################################## ############# INPUT CHECK ################ ########################################## # first check existence of structure and if 1D, 2D, 3D try: structure = inputdict.pop(self.get_linkname('structure')) except KeyError: raise InputValidationError("No structure specified for this" " calculation") if not isinstance(structure, StructureData): raise InputValidationError( "structure is not of type StructureData") pbc = structure.pbc if False in pbc: bulk = False film = True # check existence of parameters (optional) parameters = inputdict.pop(self.get_linkname('parameters'), None) if parameters is None: # use default parameters_dict = {} else: if not isinstance(parameters, ParameterData): raise InputValidationError( "parameters, if specified, must be of " "type ParameterData") parameters_dict = _lowercase_dict(parameters.get_dict(), dict_name='parameters') namelists_toprint = possible_namelists input_params = parameters_dict if 'title' in input_params.keys(): self._inp_title = input_params.pop('title') #check input_parameters # check code try: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError("No code specified for this " "calculation") # check existence of settings (optional) settings = inputdict.pop(self.get_linkname('settings'), None) if settings is None: settings_dict = {} else: if not isinstance(settings, ParameterData): raise InputValidationError( "settings, if specified, must be of " "type ParameterData") else: settings_dict = settings.get_dict() #check for for allowed keys, ignor 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 {} for Fleur calculation" "not reconized, only {} are allowed." "".format(key, self._settings_keys)) # Here, there should be no more parameters... if inputdict: raise InputValidationError("The following input data nodes are " "unrecognized: {}".format( inputdict.keys())) ############################## # END OF INITIAL INPUT CHECK # # ####################################################### ######### PREPARE PARAMETERS FOR INPUT FILE ########### #### STRUCTURE_PARAMETERS #### scaling_factor_card = "" cell_parameters_card = "" if not own_lattice: cell = structure.cell for vector in cell: scaled = [a * scaling_pos for a in vector] #scaling_pos=1./bohr_to_ang cell_parameters_card += ("{0:18.10f} {1:18.10f} {2:18.10f}" "\n".format(scaled[0], scaled[1], scaled[2])) scaling_factor_card += ("{0:18.10f} {1:18.10f} {2:18.10f}" "\n".format(scaling_factors[0], scaling_factors[1], scaling_factors[2])) #### ATOMIC_POSITIONS #### # TODO: be careful with units atomic_positions_card_list = [""] atomic_positions_card_listtmp = [""] # Fleur does not have any keyword before the atomic species. # first the number of atoms then the form nuclear charge, postion # Fleur hast the option of nuclear charge as floats, # allows the user to distinguish two atoms and break the symmetry. if not own_lattice: natoms = len(structure.sites) #for FLEUR true, general not, because you could put several # atoms on a site # TODO: test that only one atom at site? # TODO this feature might change in Fleur, do different. that in inpgen kind gets a name, which will also be the name in fleur inp.xml. # now user has to make kind_name = atom id. for site in structure.sites: kind_name = site.kind_name kind = structure.get_kind(kind_name) if kind.has_vacancies(): # then we do not at atoms with weights smaller one if kind.weights[0] < 1.0: natoms = natoms - 1 # Log message? continue site_symbol = kind.symbols[ 0] # TODO: list I assume atoms therefore I just get the first one... atomic_number = _atomic_numbers[site_symbol] atomic_number_name = atomic_number if site_symbol != kind_name: # This is an important fact, if usere renames it becomes a new species! suc = True try: head = kind_name.rstrip('0123456789') kind_namet = int(kind_name[len(head):]) except ValueError: suc = False if suc: atomic_number_name = '{}.{}'.format( atomic_number, kind_namet) # per default we use relative coordinates in Fleur # we have to scale back to atomic units from angstrom pos = site.position if bulk: vector_rel = abs_to_rel(pos, cell) elif film: vector_rel = abs_to_rel_f(pos, cell, structure.pbc) vector_rel[2] = vector_rel[2] * scaling_pos atomic_positions_card_listtmp.append( " {0:3} {1:18.10f} {2:18.10f} {3:18.10f}" "\n".format(atomic_number_name, vector_rel[0], vector_rel[1], vector_rel[2])) #TODO check format # we write it later, since we do not know what natoms is before the loop... atomic_positions_card_list.append(" {0:3}\n".format(natoms)) for card in atomic_positions_card_listtmp: atomic_positions_card_list.append(card) else: # TODO with own lattice atomic positions have to come from somewhere # else.... User input? raise InputValidationError("fleur lattice needs also the atom " " position as input," " not implemented yet, sorry!") atomic_positions_card = "".join(atomic_positions_card_list) del atomic_positions_card_list # Free memory #### Kpts #### # TODO: kpts ####################################### #### WRITE ALL CARDS IN INPUT FILE #### input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME) # TODO: with open(input_filename, 'w') as infile: #first write title infile.write("{0}\n".format(self._inp_title)) #then write &input namelist infile.write("&{0}".format('input')) # namelist content; set to {} if not present, so that we leave an # empty namelist namelist = input_params.pop('input', {}) for k, val in sorted(namelist.iteritems()): infile.write(get_input_data_text(k, val, False, mapping=None)) infile.write("/\n") # Write lattice information now infile.write(cell_parameters_card) infile.write("{0:18.10f}\n".format(scaling_lat)) infile.write(scaling_factor_card) infile.write("\n") # Write Atomic positons infile.write(atomic_positions_card) # Write namelists after atomic positions for namels_name in namelists_toprint: namelist = input_params.pop(namels_name, {}) if namelist: if 'atom' in namels_name: namels_name = 'atom' infile.write("&{0}\n".format(namels_name)) if namels_name in val_only_namelist: for k, val in sorted(namelist.iteritems()): infile.write( get_input_data_text(k, val, True, mapping=None)) else: for k, val in sorted(namelist.iteritems()): infile.write( get_input_data_text(k, val, False, mapping=None)) infile.write("/\n") #infile.write(kpoints_card) if input_params: raise InputValidationError( "input_params leftover: The following namelists are specified" " in input_params, but are " "not valid namelists for the current type of calculation: " "{}".format(",".join(input_params.keys()))) 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 = [] # TODO: let the user specify? #settings_retrieve_list = settings_dict.pop( # 'ADDITIONAL_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._INPUT_FILE_NAME) #calcinfo.retrieve_list += settings_retrieve_list #calcinfo.retrieve_list += self._internal_retrieve_list # user specific retrieve add_retrieve = settings_dict.get('additional_retrieve_list', []) #print('add_retrieve: {}'.format(add_retrieve)) 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() cmdline_params = [] # 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.stdin_name = self._INPUT_FILE_NAME 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 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 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 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) charges[mask_replace_Bi_Pb] = 82 print('WARNING: Bi potential not available, using Pb instead!!!') ###################################### # 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: print('WARNING: automatically removing value of key', key) 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: 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
def _prepare_for_submission(self, tempfolder, inputdict): """ This is the routine to be called when you want to create the input files for the inpgen with the plug-in. :param tempfolder: a aiida.common.folders.Folder subclass where the plugin should put all its files. :param inputdict: a dictionary with the input nodes, as they would be returned by get_inputdata_dict (without the Code!) """ #from aiida.common.utils import get_unique_filename, get_suggestion #import re # 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() } possible_namelists = self._possible_namelists possible_params = self._possible_params local_copy_list = [] remote_copy_list = [] remote_symlink_list = [] bulk = True film = False # convert these 'booleans' to the inpgen format. replacer_values_bool = [ True, False, 'True', 'False', 't', 'T', 'F', 'f' ] # some keywords require a string " around them in the input file. string_replace = ['econfig', 'lo', 'element', 'name'] # of some keys only the values are writen to the file, specify them here. val_only_namelist = ['soc', 'qss'] # Scaling comes from the Structure # but we have to convert from Angstroem to a.u (bohr radii) scaling_factors = [1.0, 1.0, 1.0] # scaling_lat = 1. #/bohr_a scaling_pos = 1. / bohr_a # Angstrom to atomic own_lattice = False #not self._use_aiida_structure # The inpfile gen is run in serial TODO: How to do this by default? #self.set_withmpi(False) ########################################## ############# INPUT CHECK ################ ########################################## # first check existence of structure and if 1D, 2D, 3D try: structure = inputdict.pop(self.get_linkname('structure')) except KeyError: raise InputValidationError("No structure specified for this" " calculation") if not isinstance(structure, StructureData): raise InputValidationError( "structure is not of type StructureData") pbc = structure.pbc if False in pbc: bulk = False film = True # check existence of parameters (optional) parameters = inputdict.pop(self.get_linkname('parameters'), None) if parameters is None: # use default parameters_dict = {} else: if not isinstance(parameters, ParameterData): raise InputValidationError( "parameters, if specified, must be of " "type ParameterData") parameters_dict = _lowercase_dict(parameters.get_dict(), dict_name='parameters') # we write always out rel coordinates, because thats the way FLEUR uses # them best. we have to convert them from abs, becauses thats how they #are stored in a Structure node. cartesian=F is default if 'input' in parameters_dict: parameters_dict['input']['cartesian'] = False if film: parameters_dict['input']['film'] = True else: if bulk: parameters_dict['input'] = {'cartesian': False} elif film: parameters_dict['input'] = {'cartesian': False, 'film': True} namelists_toprint = possible_namelists # check parameters keys TODO: values needed, or keep plug-in as stupid as possible? #if parameters_dict:# TODO remove, unnesseary now? input_params = parameters_dict #TODO:?make everything lowercase in the database, and change it to inpgen format? #_lowercase_dict(parameters.get_dict(), #dict_name='parameters') #input_params = {k: _lowercase_dict(val, dict_name=k) # for k, val in input_params.iteritems()} #input_params_keys = input_params.keys() if 'title' in input_params.keys(): self._inp_title = input_params.pop('title') #TODO validate type of values of the input parameter keys ? #check input_parameters for namelist, paramdic in input_params.iteritems(): if 'atom' in namelist: # this namelist can be specified more often # special atom namelist needs to be set for writing, # but insert it in the right spot! index = namelists_toprint.index('atom') + 1 namelists_toprint.insert(index, namelist) namelist = 'atom' if namelist not in possible_namelists: raise InputValidationError( "The namelist '{}' is not supported by the fleur" " inputgenerator. Check on the fleur website or add '{}'" "to _possible_namelists.".format(namelist, namelist)) for para in paramdic.keys(): if para not in possible_params[namelist]: raise InputValidationError( "The property '{}' is not supported by the " "namelist '{}'. " "Check the fleur website, or if it really is," " update _possible_params. ".format(para, namelist)) if paramdic[para] in replacer_values_bool: # because 1/1.0 == True, and 0/0.0 == False # maybe change in convert_to_fortran that no error occurs if isinstance(paramdic[para], (int, float)): if isinstance(paramdic[para], bool): paramdic[para] = convert_to_fortran_bool( paramdic[para]) else: paramdic[para] = convert_to_fortran_bool( paramdic[para]) if para in string_replace: #TODO check if its in the parameter dict #print para paramdic[para] = convert_to_fortran_string(paramdic[para]) #print "{}".format(paramdic[para]) #in fleur it is possible to give a lattice namelist if 'lattice' in input_params.keys(): own_lattice = True structure = inputdict.pop(self.get_linkname('structure'), None) if structure is not None: #two structures given? #which one should be prepared? TODO: print warning or even error if self._use_aiida_structure: if not isinstance(structure, StructureData): raise InputValidationError( "structure is not of type" " StructureData") input_params.pop('lattice', {}) own_lattice = False ''' # TODO allow only usual kpt meshes and use therefore Aiida kpointData if self._use_kpoints: try: kpoints = inputdict.pop(self.get_linkname('kpoints')) except KeyError: raise InputValidationError("No kpoints specified for this" " calculation") if not isinstance(kpoints, KpointsData): raise InputValidationError("kpoints is not of type KpointsData") ''' #TODO I think the code should not be in the input dict. check local, # verus remote, codeinfos, one several codes.. try: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError("No code specified for this " "calculation") # check existence of settings (optional) settings = inputdict.pop(self.get_linkname('settings'), None) #print('settings: {}'.format(settings)) if settings is None: settings_dict = {} else: if not isinstance(settings, ParameterData): raise InputValidationError( "settings, if specified, must be of " "type ParameterData") else: settings_dict = settings.get_dict() #check for for allowed keys, ignor unknown keys but warn. for key in settings_dict.keys(): if key not in self._settings_keys: #TODO warrning self.logger.info("settings dict key {} for Fleur calculation" "not reconized, only {} are allowed." "".format(key, self._settings_keys)) # Here, there should be no more parameters... if inputdict: raise InputValidationError("The following input data nodes are " "unrecognized: {}".format( inputdict.keys())) ############################## # END OF INITIAL INPUT CHECK # ####################################################### ######### PREPARE PARAMETERS FOR INPUT FILE ########### #### STRUCTURE_PARAMETERS #### scaling_factor_card = "" cell_parameters_card = "" if not own_lattice: cell = structure.cell for vector in cell: scaled = [a * scaling_pos for a in vector] #scaling_pos=1./bohr_a cell_parameters_card += ("{0:18.10f} {1:18.10f} {2:18.10f}" "\n".format(scaled[0], scaled[1], scaled[2])) scaling_factor_card += ("{0:18.10f} {1:18.10f} {2:18.10f}" "\n".format(scaling_factors[0], scaling_factors[1], scaling_factors[2])) #### ATOMIC_POSITIONS #### # TODO: be careful with units atomic_positions_card_list = [""] # Fleur does not have any keyword before the atomic species. # first the number of atoms then the form nuclear charge, postion # Fleur hast the option of nuclear charge as floats, # allows the user to distinguish two atoms and break the symmetry. if not own_lattice: natoms = len(structure.sites) #for FLEUR true, general not, because you could put several # atoms on a site # TODO: test that only one atom at site? atomic_positions_card_list.append(" {0:3}\n".format(natoms)) # TODO this feature might change in Fleur, do different. that in inpgen kind gets a name, which will also be the name in fleur inp.xml. # now user has to make kind_name = atom id. for site in structure.sites: kind_name = site.kind_name site_symbol = structure.get_kind(kind_name).symbols[ 0] # TODO: list I assume atoms therefore I just get the first one... atomic_number = _atomic_numbers[site_symbol] atomic_number_name = atomic_number if site_symbol != kind_name: # This is an important fact, if usere renames it becomes a new species! suc = True try: head = kind_name.rstrip('0123456789') kind_namet = int(kind_name[len(head):]) except ValueError: suc = False if suc: atomic_number_name = '{}.{}'.format( atomic_number, kind_namet) # per default we use relative coordinates in Fleur # we have to scale back to atomic units from angstrom pos = site.position #print 'pos {}'.format(pos) if bulk: vector_rel = abs_to_rel(pos, cell) elif film: vector_rel = abs_to_rel_f(pos, cell, structure.pbc) vector_rel[2] = vector_rel[2] * scaling_pos atomic_positions_card_list.append( " {0:3} {1:18.10f} {2:18.10f} {3:18.10f}" "\n".format(atomic_number_name, vector_rel[0], vector_rel[1], vector_rel[2])) #print atomic_positions_card_list #TODO check format else: # TODO with own lattice atomic positions have to come from somewhere # else.... User input? raise InputValidationError("fleur lattice needs also the atom " " position as input," " not implemented yet, sorry!") atomic_positions_card = "".join(atomic_positions_card_list) del atomic_positions_card_list # Free memory #### Kpts #### # TODO: kpts #kpoints_card = ""#.join(kpoints_card_list) #del kpoints_card_list ####################################### #### WRITE ALL CARDS IN INPUT FILE #### input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME) with open(input_filename, 'w') as infile: #first write title infile.write("{0}\n".format(self._inp_title)) #then write &input namelist infile.write("&{0}".format('input')) # namelist content; set to {} if not present, so that we leave an # empty namelist namelist = input_params.pop('input', {}) for k, val in sorted(namelist.iteritems()): infile.write(get_input_data_text(k, val, False, mapping=None)) infile.write("/\n") # Write lattice information now infile.write(cell_parameters_card) infile.write("{0:18.10f}\n".format(scaling_lat)) infile.write(scaling_factor_card) infile.write("\n") # Write Atomic positons infile.write(atomic_positions_card) # Write namelists after atomic positions for namels_name in namelists_toprint: namelist = input_params.pop(namels_name, {}) if namelist: if 'atom' in namels_name: namels_name = 'atom' infile.write("&{0}\n".format(namels_name)) if namels_name in val_only_namelist: for k, val in sorted(namelist.iteritems()): infile.write( get_input_data_text(k, val, True, mapping=None)) else: for k, val in sorted(namelist.iteritems()): infile.write( get_input_data_text(k, val, False, mapping=None)) infile.write("/\n") #infile.write(kpoints_card) if input_params: raise InputValidationError( "input_params leftover: The following namelists are specified" " in input_params, but are " "not valid namelists for the current type of calculation: " "{}".format(",".join(input_params.keys()))) 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 = [] # TODO: let the user specify? #settings_retrieve_list = settings_dict.pop( # 'ADDITIONAL_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) #calcinfo.retrieve_list += settings_retrieve_list #calcinfo.retrieve_list += self._internal_retrieve_list # user specific retrieve add_retrieve = settings_dict.get('additional_retrieve_list', []) #print('add_retrieve: {}'.format(add_retrieve)) 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() cmdline_params = ["-explicit"] # TODO? let the user decide -econfig? # 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.stdin_name = self._INPUT_FILE_NAME 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] ''' if settings_dict: try: Parserclass = self.get_parserclass() parser = Parserclass(self) parser_opts = parser.get_parser_settings_key() settings_dict.pop(parser_opts) except (KeyError, AttributeError): # the key parser_opts isn't # inside the dictionary raise InputValidationError( "The following keys have been found in the settings " "input node, but were not understood: {}" "".format(",".join(settings_dict.keys()))) ''' return calcinfo
def _prepare_for_submission(self, tempfolder, inputdict): """ This is the routine to be called when you want to create the input files and related stuff with a plugin. :param tempfolder: a aiida.common.folders.Folder subclass where the plugin should put all its files. :param inputdict: a dictionary with the input nodes, as they would be returned by get_inputdata_dict (without the Code!) """ local_copy_list = [] remote_copy_list = [] # Process the settings dictionary first # Settings can be undefined, and defaults to an empty dictionary settings = inputdict.pop(self.get_linkname('settings'), None) if settings is None: settings_dict = {} else: if not isinstance(settings, ParameterData): raise InputValidationError( "settings, if specified, must be of " "type ParameterData") # Settings converted to UPPERCASE # Presumably to standardize the usage and avoid # ambiguities settings_dict = _uppercase_dict(settings.get_dict(), dict_name='settings') 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 is not of type " "ParameterData") # Basis can be undefined, and defaults to an empty dictionary, # Siesta will use default parameters basis = inputdict.pop(self.get_linkname('basis'), None) if basis is None: input_basis = {} else: if not isinstance(basis, ParameterData): raise InputValidationError("basis not of type ParameterData") # input_basis=FDFDict(basis.get_dict()) input_basis = basis.get_dict() try: structure = inputdict.pop(self.get_linkname('structure')) except KeyError: raise InputValidationError("No structure specified for this " "calculation") if not isinstance(structure, StructureData): raise InputValidationError( "structure is not of type StructureData") # k-points # It is now possible to elide the kpoints node. # # Note also that a *different* set of k-points is needed if a band # calculation is carried out. This should be specified somehow in # the 'settings' dictionary (see QE example...) kpoints = inputdict.pop(self.get_linkname('kpoints'), None) if kpoints is None: # Do nothing. Assume it is a gamma-point calculation pass else: if not isinstance(kpoints, KpointsData): raise InputValidationError("kpoints, if specified, must be of " "type KpointsData") bandskpoints = inputdict.pop(self.get_linkname('bandskpoints'), None) if bandskpoints is None: flagbands = False else: flagbands = True if not isinstance(bandskpoints, KpointsData): raise InputValidationError( "kpoints for bands is not of type KpointsData") pseudos = {} # I create here a dictionary that associates each kind name to a pseudo for link in inputdict.keys(): if link.startswith(self._get_linkname_pseudo_prefix()): kindstring = link[len(self._get_linkname_pseudo_prefix()):] kinds = kindstring.split('_') the_pseudo = inputdict.pop(link) if not isinstance(the_pseudo, PsfData): raise InputValidationError("Pseudo for kind(s) {} is not " " of type PsfData".format( ",".join(kinds))) # # Note that we can associate the same pseudo object to different # atom kinds # for kind in kinds: pseudos[kind] = the_pseudo parent_calc_folder = inputdict.pop(self.get_linkname('parent_folder'), None) if parent_calc_folder is not None: if not isinstance(parent_calc_folder, RemoteData): raise InputValidationError("parent_calc_folder, if specified," "must be of type RemoteData") try: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError( "No code specified for this calculation") # Here, there should be no more parameters... if inputdict: raise InputValidationError("The following input data nodes are " "unrecognized: {}".format( inputdict.keys())) # Check structure, get species, check peudos kindnames = [k.name for k in structure.kinds] if set(kindnames) != set(pseudos.keys()): err_msg = ("Mismatch between the defined pseudos and the list of " "kinds of the structure. Pseudos: {}; kinds: {}".format( ",".join(pseudos.keys()), ",".join(list(kindnames)))) raise InputValidationError(err_msg) ############################## # END OF INITIAL INPUT CHECK # ############################## # # There should be a warning for duplicated (canonicalized) keys # in the original dictionary in the script input_params = FDFDict(parameters.get_dict()) # Look for blocked keywords and # add the proper values to the dictionary for blocked_key in self._aiida_blocked_keywords: canonical_blocked = FDFDict.translate_key(blocked_key) for key in input_params: if key == canonical_blocked: raise InputValidationError( "You cannot specify explicitly the '{}' flag in the " "input parameters".format( input_params.get_last_key(key))) input_params.update({'system-name': self._PREFIX}) input_params.update({'system-label': self._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)}) # # Regarding the lattice-constant parameter: # -- The variable "alat" is not typically kept anywhere, and # has already been used to define the vectors. # We need to specify that the units of these vectors are Ang... input_params.update({'lattice-constant': '1.0 Ang'}) # Note that this will break havoc with the band-k-points "pi/a" # option. The use of this option should be banned. # Note that the implicit coordinate convention of the Structure # class corresponds to the "Ang" convention in Siesta. # That is why the "atomic-coordinates-format" keyword is blocked # and reset. input_params.update({'atomic-coordinates-format': 'Ang'}) # ============== 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 ------------ # I create the subfolder that will contain the pseudopotentials tempfolder.get_subfolder(self._PSEUDO_SUBFOLDER, create=True) # I create the subfolder with the output data tempfolder.get_subfolder(self._OUTPUT_SUBFOLDER, create=True) atomic_species_card_list = [] # Dictionary to get the atomic number of a given element datmn = dict([(v['symbol'], k) for k, v in elements.iteritems()]) spind = {} spcount = 0 for kind in structure.kinds: ps = pseudos[kind.name] # I add this pseudo file to the list of files to copy, # with the appropiate name local_copy_list.append((ps.get_file_abs_path(), os.path.join(self._PSEUDO_SUBFOLDER, kind.name + ".psf"))) spcount += 1 spind[kind.name] = spcount atomic_species_card_list.append("{0:5} {1:5} {2:5}\n".format( spind[kind.name], datmn[kind.symbol], kind.name.rjust(6))) 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 ---------------- if kpoints is not None: # # Get a mesh for sampling # NOTE that there is not yet support for the 'kgrid-cutoff' # option in Siesta. # try: mesh, offset = kpoints.get_kpoints_mesh() has_mesh = True except AttributeError: raise InputValidationError("K-point sampling for scf " "must be given in mesh form") kpoints_card_list = ["%block kgrid_monkhorst_pack\n"] # # This will fail if has_mesh is False (for the case of a list), # since in that case 'offset' is undefined. # 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 ----------------! #This part is computed only if flagbands=True #Two possibility are supported in Siesta: BandLines ad BandPoints #At the moment the 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 is not supported at the moment because currently #a=1 always. BandLinesScale ReciprocalLatticeVectors is always set if flagbands: bandskpoints_card_list = [ "BandLinesScale ReciprocalLatticeVectors\n" ] if bandskpoints.labels == None: bandskpoints_card_list.append("%block BandPoints\n") for s in bandskpoints.get_kpoints(): bandskpoints_card_list.append( "{0:8.3f} {1:8.3f} {2:8.3f} \n".format( s[0], s[1], s[2])) fbkpoints_card = "".join(bandskpoints_card_list) fbkpoints_card += "%endblock BandPoints\n" else: bandskpoints_card_list.append("%block BandLines\n") savs = [] listforbands = bandskpoints.get_kpoints() for s, m in bandskpoints.labels: savs.append(s) rawindex = 0 for s, m in bandskpoints.labels: rawindex = rawindex + 1 x, y, z = listforbands[s] 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, m)) else: bandskpoints_card_list.append( "{0:3} {1:8.3f} {2:8.3f} {3:8.3f} {4:1} \n".format( s - savs[rawindex - 2], x, y, z, m)) fbkpoints_card = "".join(bandskpoints_card_list) fbkpoints_card += "%endblock BandLines\n" del bandskpoints_card_list # ================ Namelists and cards =================== input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME) with open(input_filename, 'w') as infile: # here print keys and values tp file for k, v in sorted(input_params.iteritems()): infile.write(get_input_data_text(k, v)) # ,mapping=mapping_species)) # Basis set info is processed just like the general # parameters section. Some discipline is needed to # put any basis-related parameters (including blocks) # in the basis dictionary in the input script. # if basis is not None: infile.write("#\n# -- Basis Set Info follows\n#\n") for k, v in input_basis.iteritems(): infile.write(get_input_data_text(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 flagbands: infile.write("#\n# -- Bandlines/Bandpoints Info follows\n#\n") infile.write(fbkpoints_card) # Write max wall-clock time infile.write("#\n# -- Max wall-clock time block\n#\n") infile.write("max.walltime {}".format( self.get_max_wallclock_seconds())) # ------------------------------------- END of fdf file creation # 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, it is just the density-matrix file # # It will be copied to the current calculation's working folder. if parent_calc_folder is not None: remote_copy_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), self._restart_copy_from), self._restart_copy_to)) calcinfo = CalcInfo() calcinfo.uuid = self.uuid # # Empty command line by default # Why use 'pop' ? cmdline_params = settings_dict.pop('CMDLINE', []) # Comment this paragraph better, if applicable to Siesta # #we commented calcinfo.stin_name and added it here in cmdline_params #in this way the mpirun ... pw.x ... < aiida.in #is replaced by mpirun ... pw.x ... -in aiida.in # in the scheduler, _get_run_line, if cmdline_params is empty, it # simply uses < calcinfo.stin_name if cmdline_params: calcinfo.cmdline_params = list(cmdline_params) calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list calcinfo.stdin_name = self._INPUT_FILE_NAME calcinfo.stdout_name = self._OUTPUT_FILE_NAME calcinfo.xml_name = self._XML_FILE_NAME calcinfo.json_name = self._JSON_FILE_NAME calcinfo.messages_name = self._MESSAGES_FILE_NAME # # Code information object # codeinfo = CodeInfo() codeinfo.cmdline_params = list(cmdline_params) codeinfo.stdin_name = self._INPUT_FILE_NAME codeinfo.stdout_name = self._OUTPUT_FILE_NAME codeinfo.xml_name = self._XML_FILE_NAME codeinfo.json_name = self._JSON_FILE_NAME codeinfo.messages_name = self._MESSAGES_FILE_NAME codeinfo.code_uuid = code.uuid calcinfo.codes_info = [codeinfo] # Retrieve by default: the output file, the xml file, and the # messages file. # If flagbands=True we also add the bands file to the retrieve list! # This is extremely important because the parser parses the bands # only if aiida.bands is in the retrieve list!! calcinfo.retrieve_list = [] calcinfo.retrieve_list.append(self._OUTPUT_FILE_NAME) calcinfo.retrieve_list.append(self._XML_FILE_NAME) calcinfo.retrieve_list.append(self._JSON_FILE_NAME) calcinfo.retrieve_list.append(self._MESSAGES_FILE_NAME) if flagbands: calcinfo.retrieve_list.append(self._BANDS_FILE_NAME) # 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 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 from aiida_kkr.tools.common_functions import get_Ang2aBohr, get_alat_from_bravais import numpy as np #list of globally used constants a_to_bohr = get_Ang2aBohr() #get the connection between coordination number and element symbol _atomic_numbers = { data['symbol']: num for num, data in PeriodicTableElements.iteritems() } #convert units from Å to Bohr (KKR needs Bohr) bravais = np.array(structure.cell) * a_to_bohr alat = get_alat_from_bravais(bravais, is3D=structure.pbc[2]) #bravais = bravais/alat #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] * a_to_bohr / alat 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 break_symmetry(structure, atoms=['all'], site=[], pos=[], new_kinds_names={}, parameterData=None): """ This routine introduces different 'kind objects' in a structure and names them that inpgen will make different species/atomgroups out of them. If nothing specified breaks ALL symmetry (i.e. every atom gets their own kind) params: StructureData params: atoms: python list of symbols, exp: ['W', 'Be']. This would make for all Be and W atoms their own kinds. params: site: python list of integers, exp: [1, 4, 8]. This would create for atom 1, 4 and 8 their own kinds. params: pos: python list of tuples of 3, exp [(0.0, 0.0, -1.837927), ...]. This will create a new kind for the atom at that position. Be carefull the number given has to match EXACTLY the position in the structure. return: StructureData, a AiiDA crystal structure with new kind specification. """ # TODO proper input checks? from aiida.common.constants import elements as PeriodicTableElements _atomic_numbers = { data['symbol']: num for num, data in PeriodicTableElements.iteritems() } #get all atoms, get the symbol of the atom #if wanted make individual kind for that atom #kind names will be atomsymbol+number #create new structure with new kinds and atoms #Param = DataFactory('parameter') symbol_count = { } # Counts the atom symbol occurence to set id's and kind names right replace = [] # all atoms symbols ('W') to be replaced replace_siteN = [] # all site integers to be replaced replace_pos = [] #all the atom positions to be replaced new_parameterd = None struc = is_structure(structure) if not struc: print 'Error, no structure given' # throw error? cell = struc.cell pbc = struc.pbc sites = struc.sites #natoms = len(sites) new_structure = DataFactory('structure')(cell=cell, pbc=pbc) for sym in atoms: replace.append(sym) for position in pos: replace_pos.append(position) for atom in site: replace_siteN.append(atom) if parameterData: para = parameterData.get_dict() new_parameterd = dict(para) else: new_parameterd = {} for i, site in enumerate(sites): kind_name = site.kind_name pos = site.position kind = struc.get_kind(kind_name) symbol = kind.symbol replace_kind = False if symbol in replace or 'all' in replace: replace_kind = True if pos in replace_pos: replace_kind = True if i in replace_siteN: replace_kind = True if replace_kind: if symbol in symbol_count: symbol_count[symbol] = symbol_count[symbol] + 1 symbol_new_kinds_names = new_kinds_names.get(symbol, []) print(symbol_new_kinds_names) if symbol_new_kinds_names and ((len(symbol_new_kinds_names)) == symbol_count[symbol]): newkindname = symbol_new_kinds_names[symbol_count[symbol] - 1] else: newkindname = '{}{}'.format(symbol, symbol_count[symbol]) else: symbol_count[symbol] = 1 symbol_new_kinds_names = new_kinds_names.get(symbol, []) #print(symbol_new_kinds_names) if symbol_new_kinds_names and ((len(symbol_new_kinds_names)) == symbol_count[symbol]): newkindname = symbol_new_kinds_names[symbol_count[symbol] - 1] else: newkindname = '{}{}'.format(symbol, symbol_count[symbol]) #print(newkindname) new_kind = Kind(name=newkindname, symbols=symbol) new_structure.append_kind(new_kind) # now we have to add an atom list to parameterData with the corresponding id. if parameterData: id_a = symbol_count[ symbol] #'{}.{}'.format(charge, symbol_count[symbol]) #print 'id: {}'.format(id) for key, val in para.iteritems(): if 'atom' in key: if val.get('element', None) == symbol: if id_a and id_a == val.get('id', None): break # we assume the user is smart and provides a para node, # which incooperates the symmetry breaking already elif id_a: # != 1: # copy parameter of symbol and add id val_new = dict(val) # getting the charge over element might be risky charge = _atomic_numbers.get( (val.get('element'))) idp = '{}.{}'.format(charge, symbol_count[symbol]) idp = float("{0:.2f}".format(float(idp))) # dot cannot be stored in AiiDA dict... val_new.update({u'id': idp}) atomlistname = 'atom{}'.format(id_a) i = 0 while new_parameterd.get(atomlistname, {}): i = i + 1 atomlistname = 'atom{}'.format(id_a + i) symbol_new_kinds_names = new_kinds_names.get( symbol, []) #print(symbol_new_kinds_names) if symbol_new_kinds_names and ( (len(symbol_new_kinds_names)) == symbol_count[symbol]): species_name = symbol_new_kinds_names[ symbol_count[symbol] - 1] val_new.update({u'name': species_name}) new_parameterd[atomlistname] = val_new else: pass #TODO write basic parameter data node else: newkindname = kind_name if not kind_name in new_structure.get_kind_names(): new_structure.append_kind(kind) new_structure.append_site(Site(kind_name=newkindname, position=pos)) #print 'natoms: {}, nkinds: {}'.format(natoms, len(new_structure.get_kind_names())) if parameterData: para_new = ParameterData(dict=new_parameterd) else: para_new = None new_structure.label = structure.label new_structure.description = structure.description + 'more kinds, less sym' return new_structure, para_new