def use_parent_calculation(self,calc): """ Set the parent calculation of Ph, from which it will inherit the outputsubfolder. The link will be created from parent RemoteData to PhCalculation """ from aiida.common.exceptions import NotExistent self._check_valid_parent(calc) remotedatas = calc.get_outputs(type=RemoteData) if not remotedatas: raise NotExistent("No output remotedata found in " "the parent") if len(remotedatas) != 1: raise UniquenessError("More than one output remotedata found in " "the parent") remotedata = remotedatas[0] self._set_parent_remotedata(remotedata)
def store(self, *args, **kwargs): """ Store the node, ensuring that the combination (element,name,version) is unique. """ # TODO: this uniqueness check is not race-condition free. try: existing = self.get(self.element, self.name, self.version, match_aliases=False) except NotExistent: pass else: raise UniquenessError( f"Gaussian Basis Set already exists for" f" element={self.element}, name={self.name}, version={self.version}: {existing.uuid}" ) return super(BasisSet, self).store(*args, **kwargs)
def _prepare_group_for_upload(cls, group_name, group_description=None, dry_run=False): """Prepare a (possibly new) group to upload a POTCAR family to.""" if not dry_run: group, group_created = Group.objects.get_or_create(label=group_name, type_string=cls.potcar_family_type_string) else: group = cls.get_potcar_group(group_name) group_created = bool(not group) if not group: group = Group(label=group_name) if group.user.pk != get_current_user().pk: raise UniquenessError( 'There is already a POTCAR family group with name {}, but it belongs to user {}, therefore you cannot modify it'.format( group_name, group.user.email)) if group_description: group.description = group_description elif group_created: raise ValueError('A new POTCAR family {} should be created but no description was given!'.format(group_name)) return group
def get_or_create_famgroup(cls, famname): '''Returns a PAW family group, creates it if it didn't exists''' from aiida.orm import Group from aiida.backends.djsite.utils import get_automatic_user # TODO: maybe replace with Group.get_or_create? try: group = Group.get(name=famname, type_string=cls.group_type) group_created = False except NotExistent: group = Group(name=famname, type_string=cls.group_type, user=get_automatic_user()) group_created = True if group.user != get_automatic_user(): raise UniquenessError("There is already a UpfFamily group " "with name {}, but it belongs to user {}," " therefore you cannot modify it".format( famname, group.user.email)) return group, group_created
def use_parent_calculation(self, calc): """ Set the parent calculation, from which it will inherit the outputsubfolder. The link will be created from parent RemoteData and NamelistCalculation """ if not isinstance(calc, PwCalculation): raise ValueError("Parent calculation must be a PwCalculation") from aiida.common.exceptions import UniquenessError localdatas = [_[1] for _ in calc.get_outputs(also_labels=True)] if len(localdatas) == 0: raise UniquenessError( "No output retrieved data found in the parent" "calc, probably it did not finish yet, " "or it crashed") localdata = [ _[1] for _ in calc.get_outputs(also_labels=True) if _[0] == 'remote_folder' ] localdata = localdata[0] self.use_parent_folder(localdata)
def _do_create_link(self, src, label, link_type): """ Create a link from a source node with label and a link type :param src: The source node :type src: :class:`Node` :param label: The link label :param link_type: The link type """ from aiida.backends.sqlalchemy import get_scoped_session session = get_scoped_session() try: with session.begin_nested(): link = DbLink(input_id=src.id, output_id=self.id, label=label, type=link_type.value) session.add(link) except SQLAlchemyError as exc: raise UniquenessError("There is already a link with the same " "name (raw message was {})" "".format(exc))
def _check_valid_parent(self, parent_calc_folder): """ Check that calc is a valid parent for a FleurCalculation. It can be a VoronoiCalculation, KKRCalculation """ overwrite_pot = False # extract parent calculation parent_calcs = parent_calc_folder.get_inputs(node_type=JobCalculation) n_parents = len(parent_calcs) if n_parents != 1: raise UniquenessError( "Input RemoteData is child of {} " "calculation{}, while it should have a single parent" "".format(n_parents, "" if n_parents == 0 else "s")) else: parent_calc = parent_calcs[0] overwrite_pot = True if ((not self._is_KkrCalc(parent_calc))): raise ValueError("Parent calculation must be a KkrCalculation") return overwrite_pot, parent_calc
def _replace_link_from(self, src, label, link_type=LinkType.UNSPECIFIED): """ Replace an input link with the given label, or simply creates it if it does not exist. :note: In subclasses, change only this. Moreover, remember to call the super() method in order to properly use the caching logic! :param src: the source object :param str label: the name of the label to set the link from src. """ # If both are stored, write directly on the DB if self.is_stored and src.is_stored: self._replace_dblink_from(src, label, link_type) # If the link was in the local cache, remove it # (this could happen if I first store the output node, then # the input node. try: del self._inputlinks_cache[label] except KeyError: pass else: # at least one is not stored: set in the internal cache # See if I am pointing to already saved nodes and I am already # linking to a given node # It is similar to the 'add' method, but if I am replacing the # same node, I will not complain (k!=label) if src.uuid in [ v[0].uuid for k, v in self._inputlinks_cache.iteritems() if k != label ]: raise UniquenessError( "A link from node with UUID={} and " "the current node (UUID={}) already exists!".format( src.uuid, self.uuid)) # I insert the link directly in the cache rather than calling # _add_cachelink_from because this latter performs an undesired check self._inputlinks_cache[label] = (src, link_type)
def get_result_parameterdata_node(self): """ Return the parameterdata node. :raise UniquenessError: if the node is not unique :raise NotExistent: if the node does not exist """ from aiida.orm.data.parameter import ParameterData from aiida.common.exceptions import NotExistent out_parameters = self._calc.get_outputs(type=ParameterData, also_labels=True) out_parameterdata = [i[1] for i in out_parameters if i[0] == self.get_linkname_outparams()] if not out_parameterdata: raise NotExistent("No output .res ParameterData node found") elif len(out_parameterdata) > 1: from aiida.common.exceptions import UniquenessError raise UniquenessError("Output ParameterData should be found once, " "found it instead {} times" .format(len(out_parameterdata))) return out_parameterdata[0]
def get_extended_symmetries(self): """ Return the extended dictionary of symmetries. """ data = self._calc.get_outputs(node_type=ParameterData, also_labels=True) all_data = [ i[1] for i in data if i[0] == self.get_linkname_outparams() ] if len(all_data) > 1: raise UniquenessError('More than one output parameterdata found.') elif not all_data: return [] else: compact_list = all_data[0].get_dict()[ 'symmetries'] # rimetti lo zero new_list = [] # copy what wasn't compact for element in compact_list: new_dict = {} for keys in [ 't_rev', 'equivalent_ions', 'fractional_translation' ]: try: new_dict[keys] = element[keys] except KeyError: pass # expand the rest new_dict['name'] = self._possible_symmetries[ element['symmetry_number']]['name'] new_dict['rotation'] = self._possible_symmetries[ element['symmetry_number']]['matrix'] new_dict['inversion'] = self._possible_symmetries[ element['symmetry_number']]['inversion'] new_list.append(new_dict) return new_list
def set_value(cls, key, value, with_transaction=True, subspecifier_value=None, other_attribs={}, stop_if_existing=False): """ Set a new value in the DB, possibly associated to the given subspecifier. :note: This method also stored directly in the DB. :param key: a string with the key to create (must be a level-0 attribute, that is it cannot contain the separator cls._sep). :param value: the value to store (a basic data type or a list or a dict) :param subspecifier_value: must be None if this class has no subspecifier set (e.g., the DbSetting class). Must be the value of the subspecifier (e.g., the dbnode) for classes that define it (e.g. DbAttribute and DbExtra) :param with_transaction: True if you want this function to be managed with transactions. Set to False if you already have a manual management of transactions in the block where you are calling this function (useful for speed improvements to avoid recursive transactions) :param other_attribs: a dictionary of other parameters, to store only on the level-zero attribute (e.g. for description in DbSetting). :param stop_if_existing: if True, it will stop with an UniquenessError exception if the new entry would violate an uniqueness constraint in the DB (same key, or same key+node, depending on the specific subclass). Otherwise, it will first delete the old value, if existent. The use with True is useful if you want to use a given attribute as a "locking" value, e.g. to avoid to perform an action twice on the same node. Note that, if you are using transactions, you may get the error only when the transaction is committed. """ cls.validate_key(key) try: if with_transaction: sid = transaction.savepoint() # create_value returns a list of nodes to store to_store = cls.create_value(key, value, subspecifier_value=subspecifier_value, other_attribs=other_attribs) if to_store: # if not stop_if_existing: # # Delete the olf values if stop_if_existing is False, # # otherwise don't delete them and hope they don't # # exist. If they exist, I'll get an UniquenessError # # ## NOTE! Be careful in case the extra/attribute to # ## store is not a simple attribute but a list or dict: # ## like this, it should be ok because if we are # ## overwriting an entry it will stop anyway to avoid # ## to overwrite the main entry, but otherwise # ## there is the risk that trailing pieces remain # ## so in general it is good to recursively clean # ## all sub-items. # cls.del_value(key, # subspecifier_value=subspecifier_value) for my_obj in to_store: my_obj.save() # cls.objects.bulk_create(to_store) if with_transaction: transaction.savepoint_commit(sid) except BaseException as exc: # All exceptions including CTRL+C, ... from django.db.utils import IntegrityError from aiida.common.exceptions import UniquenessError if with_transaction: transaction.savepoint_rollback(sid) if isinstance(exc, IntegrityError) and stop_if_existing: raise UniquenessError( 'Impossible to create the required ' 'entry ' "in table '{}', " 'another entry already exists and the creation would ' 'violate an uniqueness constraint.\nFurther details: ' '{}'.format(cls.__name__, exc)) raise
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 (with the Code(s)!) """ local_copy_list = [] remote_copy_list = [] remote_symlink_list = [] # Settings can be undefined, and defaults to an empty dictionary. # They will be used for any input that doen't fit elsewhere. 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 settings_dict = _uppercase_dict(settings.get_dict(), dict_name='settings') initialise = settings_dict.pop('INITIALISE', None) if initialise is not None: if not isinstance(initialise, bool): raise InputValidationError("INITIALISE must be " " a boolean") try: parameters = inputdict.pop(self.get_linkname('parameters')) except KeyError: if not initialise: raise InputValidationError( "No parameters specified for this calculation") else: pass if not initialise: if not isinstance(parameters, ParameterData): raise InputValidationError( "parameters is not of type ParameterData") parent_calc_folder = inputdict.pop(self.get_linkname('parent_folder'), None) if parent_calc_folder is None: raise InputValidationError( "No parent calculation found, it is needed to " "use Yambo") if not isinstance(parent_calc_folder, RemoteData): raise InputValidationError("parent_calc_folder must be of" " type RemoteData") main_code = inputdict.pop(self.get_linkname('code'), None) if main_code is None: raise InputValidationError("No input code found!") preproc_code = inputdict.pop(self.get_linkname('preprocessing_code'), None) if preproc_code is not None: if not isinstance(preproc_code, Code): raise InputValidationError("preprocessing_code, if specified," "must be of type Code") parent_calc = parent_calc_folder.get_inputs_dict( link_type=LinkType.CREATE)['remote_folder'] yambo_parent = isinstance(parent_calc, YamboCalculation) # flags for yambo interfaces try: precode_parameters = inputdict.pop( self.get_linkname('precode_parameters')) except KeyError: precode_parameters = ParameterData(dict={}) if not isinstance(precode_parameters, ParameterData): raise InputValidationError('precode_parameters is not ' 'of type ParameterData') precode_param_dict = precode_parameters.get_dict() # check the precode parameters given in input input_cmdline = settings_dict.pop('CMDLINE', None) import re precode_params_list = [] pattern = re.compile(r"(^\-)([a-zA-Z])") for key, value in precode_param_dict.iteritems(): if re.search(pattern, key) is not None: if key == '-O' or key == '-H' or key == '-h' or key == '-F': raise InputValidationError( "Precode flag {} is not allowed".format(str(key))) else: if precode_param_dict[key] is True: precode_params_list.append(str(key)) elif precode_param_dict[key] is False: pass else: precode_params_list.append('{}'.format(str(key))) precode_params_list.append('{}'.format(str(value))) else: raise InputValidationError( "Wrong format of precode_parameters") # Adding manual cmdline input (e.g. for DB fragmentation) if input_cmdline is not None: precode_params_list = precode_params_list + input_cmdline # TODO: check that remote data must be on the same computer ############################## # END OF INITIAL INPUT CHECK # ############################## if not initialise: ################################################### # Prepare yambo input file ################################################### params_dict = parameters.get_dict() # extract boolean keys boolean_dict = { k: v for k, v in params_dict.iteritems() if isinstance(v, bool) } params_dict = { k: v for k, v in params_dict.iteritems() if k not in boolean_dict.keys() } # reorganize the dictionary and create a list of dictionaries with key, value and units parameters_list = [] for k, v in params_dict.iteritems(): if "_units" in k: continue units_key = "{}_units".format(k) try: units = params_dict[units_key] except KeyError: units = None this_dict = {} this_dict['key'] = k this_dict['value'] = v this_dict['units'] = units parameters_list.append(this_dict) input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME) with open(input_filename, 'w') as infile: infile.write(self._LOGOSTRING) for k, v in boolean_dict.iteritems(): if v: infile.write("{}\n".format(k)) for this_dict in parameters_list: key = this_dict['key'] value = this_dict['value'] units = this_dict['units'] if isinstance(value, (tuple, list)): # write the input flags for the Drude term and for the parallelization options of vers. 4 # (it can be implemented in a better way) if key.startswith('DrudeW'): value_string = " ( " + ",".join( [str(_) for _ in value]) + " )" the_string = "{} = {}".format(key, value_string) the_string += " {}".format(units) infile.write(the_string + "\n") continue if key == 'SE_CPU': value_string = " \" " + " ".join( [str(_) for _ in value]) + " \" " the_string = "{} = {}".format(key, value_string) infile.write("SE_ROLEs = \" q qp b \" " + "\n") infile.write(the_string + "\n") continue if key == 'X_all_q_CPU': value_string = " \" " + " ".join( [str(_) for _ in value]) + " \" " the_string = "{} = {}".format(key, value_string) infile.write("X_all_q_ROLEs = \" q k c v \" " + "\n") infile.write(the_string + "\n") continue if key == 'X_finite_q_CPU': value_string = " \" " + " ".join( [str(_) for _ in value]) + " \" " the_string = "{} = {}".format(key, value_string) infile.write("X_finite_q_ROLEs = \" q k c v \" " + "\n") infile.write(the_string + "\n") continue if key == 'X_q_0_CPU': value_string = " \" " + " ".join( [str(_) for _ in value]) + " \" " the_string = "{} = {}".format(key, value_string) infile.write("X_q_0_ROLEs = \" k c v \" " + "\n") infile.write(the_string + "\n") continue if key == 'QPkrange' or key == 'QPerange': value_string = '' for v in value: value_string += " | ".join([str(_) for _ in v ]) + " |\n" the_string = "% {}\n {}".format(key, value_string) the_string += "%" infile.write(the_string + "\n") continue value_string = " | ".join([str(_) for _ in value]) + " |" the_string = "% {}\n {}".format(key, value_string) if units is not None: the_string += " {}".format(units) the_string += "\n%" else: the_value = '"{}"'.format(value) if isinstance( value, basestring) else '{}'.format(value) the_string = "{} = {}".format(key, the_value) if units is not None: the_string += " {}".format(units) infile.write(the_string + "\n") ############################################ # set copy of the parent calculation ############################################ parent_calcs = parent_calc_folder.get_inputs(link_type=LinkType.CREATE) if len(parent_calcs) > 1: raise UniquenessError( "More than one parent totalenergy calculation" "has been found for parent_calc_folder {}".format( parent_calc_folder)) if len(parent_calcs) == 0: raise InputValidationError( "No parent calculation associated with parent_folder {}". format(parent_calc_folder)) parent_calc = parent_calcs[0] if yambo_parent: try: parent_settings = _uppercase_dict( parent_calc.inp.settings.get_dict(), dict_name='parent settings') parent_initialise = parent_settings['INITIALISE'] except KeyError: parent_initialise = False if yambo_parent: remote_copy_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), "SAVE"), "SAVE/")) if not parent_initialise: cancopy = False if parent_calc.get_state() == calc_states.FINISHED: cancopy = True if 'yambo_wrote' in parent_calc.get_outputs_dict( )['output_parameters'].get_dict().keys(): if parent_calc.get_outputs_dict( )['output_parameters'].get_dict()['yambo_wrote'] == True: cancopy = True if parent_calc.get_outputs_dict( )['output_parameters'].get_dict()['yambo_wrote'] == False: cancopy = False if cancopy: remote_copy_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), "aiida"), "aiida/")) else: remote_copy_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), PwCalculation._OUTPUT_SUBFOLDER, "{}.save".format(parent_calc._PREFIX), "*"), ".")) ############################################ # set Calcinfo ############################################ calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = [] calcinfo.remote_copy_list = remote_copy_list calcinfo.remote_symlink_list = [] # remote_symlink_list # Retrieve by default the output file and the xml file calcinfo.retrieve_list = [] calcinfo.retrieve_list.append('r*') calcinfo.retrieve_list.append('l*') calcinfo.retrieve_list.append('o*') calcinfo.retrieve_list.append('LOG/l-*_CPU_1') extra_retrieved = settings_dict.pop( 'ADDITIONAL_RETRIEVE_LIST', ['aiida/ndb.QP', 'aiida/ndb.HF_and_locXC']) for extra in extra_retrieved: calcinfo.retrieve_list.append(extra) from aiida.common.datastructures import code_run_modes, CodeInfo # c1 = interface dft codes and yambo (ex. p2y or a2y) c1 = CodeInfo() c1.withmpi = True c1.cmdline_params = precode_params_list # c2 = yambo initialization c2 = CodeInfo() c2.withmpi = True c2.cmdline_params = [] c2.code_uuid = main_code.uuid # if the parent calculation is a yambo calculation skip the interface (c1) and the initialization (c2) if yambo_parent: c1 = None if not parent_initialise: c2 = None else: c1.cmdline_params = precode_params_list c1.code_uuid = preproc_code.uuid # c3 = yambo calculation c3 = CodeInfo() c3.withmpi = self.get_withmpi() c3.cmdline_params = [ "-F", self._INPUT_FILE_NAME, '-J', self._OUTPUT_FILE_NAME ] c3.code_uuid = main_code.uuid if initialise: c2 = None c3 = None #calcinfo.codes_info = [c1, c2, c3] if not yambo_parent else [c3] if yambo_parent: if not parent_initialise: calcinfo.codes_info = [c3] else: calcinfo.codes_info = [c2, c3] elif initialise: calcinfo.codes_info = [c1] else: calcinfo.codes_info = [c1, c2, c3] calcinfo.codes_run_mode = code_run_modes.SERIAL if settings_dict: 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 _get_and_verify_hostfiles(self, tempfolder): """ Check inputdict for host_Greenfunction_folder and extract impurity_info, paths to kkrflex-files and path of shapefun file :param inputdict: input dictionary containing all input nodes to KkrimpCalculation :returns: * imp_info: Dict node containing impurity information like position, Z_imp, cluster size, etc. * kkrflex_file_paths: dict of absolute file paths for the kkrflex files * shapefun_path: absolute path of the shapefunction file in the host calculation (needed to construct shapefun_imp) * shapes: mapping array of atoms to shapes (<SHAPE> input) :note: shapefun_path is None if host_Greenfunction calculation was not full-potential :raises: * InputValidationError, if inputdict does not contain 'host_Greenfunction' * InputValidationError, if host_Greenfunction_folder not of right type * UniquenessError, if host_Greenfunction_folder does not have exactly one parent * InputValidationError, if host_Greenfunction does not have an input node impurity_info * InputValidationError, if host_Greenfunction was not a KKRFLEX calculation """ # get mandatory input nodes (extract host_Greenfunction_folder) host_parent = self.inputs.host_Greenfunction_folder # extract parent calculation parent_calcs = host_parent.get_incoming(node_class=CalcJob) n_parents = len(parent_calcs.all_link_labels()) if n_parents != 1: raise UniquenessError( "Input RemoteData is child of {} " "calculation{}, while it should have a single parent" "".format(n_parents, "" if n_parents == 0 else "s")) else: parent_calc = parent_calcs.first().node # extract impurity_info if 'impurity_info' in self.inputs: imp_info_inputnode = self.inputs.impurity_info if not isinstance(imp_info_inputnode, Dict): raise InputValidationError("impurity_info not of type Dict") if 'impurity_info' in parent_calc.get_incoming().all_link_labels(): imp_info = parent_calc.get_incoming().get_node_by_label( 'impurity_info') else: imp_info = None if imp_info is None: raise InputValidationError( "host_Greenfunction calculation does not have an input node impurity_info" ) found_impurity_inputnode = True found_host_parent = True else: if 'impurity_info' in parent_calc.get_incoming().all_link_labels(): imp_info = parent_calc.get_incoming().get_node_by_label( 'impurity_info') else: imp_info = None if imp_info is None: raise InputValidationError( "host_Greenfunction calculation does not have an input node impurity_info" ) found_impurity_inputnode = False # if impurity input is seperate input, check if it is the same as # the one from the parent calc (except for 'Zimp'). If that's not the # case, raise an error if found_impurity_inputnode and found_host_parent: #TODO: implement also 'ilayer_center' check if imp_info_inputnode.get_dict().get( 'Rcut') == imp_info.get_dict().get('Rcut'): check_consistency_imp_info = True try: if (imp_info_inputnode.get_dict().get('hcut') == imp_info.get_dict().get('hcut') and imp_info_inputnode.get_dict().get( 'cylinder_orient') == imp_info.get_dict().get('cylinder_orient') and imp_info_inputnode.get_dict().get('Rimp_rel') == imp_info.get_dict().get('Rimp_rel') and imp_info_inputnode.get_dict().get('imp_cls') == imp_info.get_dict().get('imp_cls')): print( 'impurity_info node from input and from previous GF calculation are compatible' ) check_consistency_imp_info = True else: print( 'impurity_info node from input and from previous GF calculation are NOT compatible!. ' 'Please check your impurity_info nodes for consistency.' ) check_consistency_imp_info = False except AttributeError: print( "Non default values of the impurity_info node from input and from previous " "GF calculation are compatible. Default values haven't been checked" ) check_consistency_imp_info = True else: print( 'impurity_info node from input and from previous GF calculation are NOT compatible!. ' 'Please check your impurity_info nodes for consistency.') check_consistency_imp_info = False if check_consistency_imp_info: imp_info = imp_info_inputnode else: raise InputValidationError( "impurity_info nodes (input and GF calc) are not compatible" ) # check if host parent was KKRFLEX calculation hostfolder = parent_calc.outputs.retrieved input_file = hostfolder.open(KkrCalculation._DEFAULT_INPUT_FILE) params_host_calc = kkrparams( params_type='kkr' ) # initialize kkrparams instance to use read_keywords_from_inputcard params_host_calc.read_keywords_from_inputcard(inputcard=input_file) if 'RUNOPT' not in list(params_host_calc.get_dict().keys()): host_ok = False elif 'KKRFLEX' not in params_host_calc.get_dict().get('RUNOPT', []): host_ok = False else: host_ok = True if not host_ok: raise InputValidationError( "host_Greenfunction calculation was not a KKRFLEX run") # extract information from Efshift host GF input node (not mandatory) if 'host_Greenfunction_folder_Efshift' in self.inputs: host_parent_Efshift = self.inputs.host_Greenfunction_folder_Efshift parent_calcs_Efshift = host_parent_Efshift.get_incoming( node_class=CalcJob) parent_calc_Efshift = parent_calcs_Efshift.first().node hostfolder_Efshift = parent_calc_Efshift.outputs.retrieved else: hostfolder_Efshift = None kkrflex_file_paths = {} for filename in self._ALL_KKRFLEX_FILES: if filename in hostfolder.list_object_names(): kkrflex_file_paths[filename] = hostfolder # take tmat and green file from Fermi level overwrite directory (second GF_writeout calculation) if hostfolder_Efshift is not None and filename in [ self._KKRFLEX_TMAT, self._KKRFLEX_GREEN ]: if filename in hostfolder_Efshift.list_object_names(): kkrflex_file_paths[filename] = hostfolder_Efshift # extract shapes array from parameters read from inputcard shapes = params_host_calc.get_dict().get('<SHAPE>', None) if shapes is None: # fallback if SHAPES is not explicityl set (assume each atom has it's own shapefun shapes = range(1, params_host_calc.get_dict().get('NAEZ') + 1) elif type(shapes) == int: shapes = [shapes] # extract input structure and voro_parent to get shapefun in next step try: structure, voro_parent = VoronoiCalculation.find_parent_structure( parent_calc) except: raise InputValidationError( "No structure node found from host GF parent") # extract shapefun path for read-in shapefun_path = {} if VoronoiCalculation._SHAPEFUN in voro_parent.outputs.retrieved.list_object_names( ): shapefun_path = voro_parent.outputs.retrieved else: shapefun_path = None return imp_info, kkrflex_file_paths, shapefun_path, shapes, parent_calc, params_host_calc, structure
def upload_psf_family(folder, group_name, group_description, stop_if_existing=True): """ Upload a set of PSF files in a given group. :param folder: a path containing all PSF files to be added. Only files ending in .PSF (case-insensitive) are considered. :param group_name: the name of the group to create. If it exists and is non-empty, a UniquenessError is raised. :param group_description: a string to be set as the group description. Overwrites previous descriptions, if the group was existing. :param stop_if_existing: if True, check for the md5 of the files and, if the file already exists in the DB, raises a MultipleObjectsError. If False, simply adds the existing PsfData node to the group. """ import os import aiida.common from aiida.common import aiidalogger from aiida.orm import Group from aiida.common.exceptions import UniquenessError, NotExistent from aiida.backends.utils import get_automatic_user from aiida.orm.querybuilder import QueryBuilder if not os.path.isdir(folder): raise ValueError("folder must be a directory") # only files, and only those ending with .psf or .PSF; # go to the real file if it is a symlink files = [ os.path.realpath(os.path.join(folder, i)) for i in os.listdir(folder) if os.path.isfile(os.path.join(folder, i)) and i.lower().endswith('.psf') ] nfiles = len(files) try: group = Group.get(name=group_name, type_string=PSFGROUP_TYPE) group_created = False except NotExistent: group = Group(name=group_name, type_string=PSFGROUP_TYPE, user=get_automatic_user()) group_created = True if group.user != get_automatic_user(): raise UniquenessError("There is already a PsfFamily group with name {}" ", but it belongs to user {}, therefore you " "cannot modify it".format( group_name, group.user.email)) # Always update description, even if the group already existed group.description = group_description # NOTE: GROUP SAVED ONLY AFTER CHECKS OF UNICITY pseudo_and_created = [] for f in files: md5sum = aiida.common.utils.md5_file(f) qb = QueryBuilder() qb.append(PsfData, filters={'attributes.md5': {'==': md5sum}}) existing_psf = qb.first() #existing_psf = PsfData.query(dbattributes__key="md5", # dbattributes__tval = md5sum) if existing_psf is None: # return the psfdata instances, not stored pseudo, created = PsfData.get_or_create(f, use_first=True, store_psf=False) # to check whether only one psf per element exists # NOTE: actually, created has the meaning of "to_be_created" pseudo_and_created.append((pseudo, created)) else: if stop_if_existing: raise ValueError("A PSF with identical MD5 to " " {} cannot be added with stop_if_existing" "".format(f)) existing_psf = existing_psf[0] pseudo_and_created.append((existing_psf, False)) # check whether pseudo are unique per element elements = [(i[0].element, i[0].md5sum) for i in pseudo_and_created] # If group already exists, check also that I am not inserting more than # once the same element if not group_created: for aiida_n in group.nodes: # Skip non-pseudos if not isinstance(aiida_n, PsfData): continue elements.append((aiida_n.element, aiida_n.md5sum)) elements = set(elements) # Discard elements with the same MD5, that would # not be stored twice elements_names = [e[0] for e in elements] if not len(elements_names) == len(set(elements_names)): duplicates = set( [x for x in elements_names if elements_names.count(x) > 1]) duplicates_string = ", ".join(i for i in duplicates) raise UniquenessError("More than one PSF found for the elements: " + duplicates_string + ".") # At this point, save the group, if still unstored if group_created: group.store() # save the psf in the database, and add them to group for pseudo, created in pseudo_and_created: if created: pseudo.store() aiidalogger.debug("New node {} created for file {}".format( pseudo.uuid, pseudo.filename)) else: aiidalogger.debug("Reusing node {} for file {}".format( pseudo.uuid, pseudo.filename)) # Add elements to the group all togetehr group.add_nodes(pseudo for pseudo, created in pseudo_and_created) nuploaded = len([_ for _, created in pseudo_and_created if created]) return nfiles, nuploaded
def _prepare_for_submission(self, tempfolder, inputdict): """ Create input files. :param tempfolder: aiida.common.folders.Folder subclass where the plugin should put all its files. :param inputdict: dictionary of the input nodes as they would be returned by get_inputs_dict """ has_parent = False local_copy_list = [] # Check inputdict try: parameters = inputdict.pop(self.get_linkname('parameters')) except KeyError: raise InputValidationError( "No parameters specified for this calculation") if not isinstance(parameters, ParameterData): raise InputValidationError("parameters not of type ParameterData") try: imp_info = inputdict.pop(self.get_linkname('impurity_info')) found_imp_info = True except KeyError: imp_info = None found_imp_info = False if found_imp_info and not isinstance(imp_info, ParameterData): raise InputValidationError( "impurity_info not of type ParameterData") try: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError( "No code specified for this calculation") # get qdos inputs try: kpath = inputdict.pop(self.get_linkname('kpoints')) found_kpath = True except KeyError: found_kpath = False try: parent_calc_folder = inputdict.pop( self.get_linkname('parent_folder')) except KeyError: raise InputValidationError( "Voronoi or previous KKR files needed for KKR calculation, " "you need to provide a Parent Folder/RemoteData node.") #TODO deal with data from folder data if calculation is continued on a different machine if not isinstance(parent_calc_folder, RemoteData): raise InputValidationError( "parent_calc_folder must be of type RemoteData") # extract parent calculation parent_calcs = parent_calc_folder.get_inputs(node_type=JobCalculation) n_parents = len(parent_calcs) if n_parents != 1: raise UniquenessError( "Input RemoteData is child of {} " "calculation{}, while it should have a single parent" "".format(n_parents, "" if n_parents == 0 else "s")) parent_calc = parent_calcs[0] has_parent = True if n_parents == 1: parent_calc = parent_calcs[0] has_parent = True # check if parent is either Voronoi or previous KKR calculation self._check_valid_parent(parent_calc) # extract parent input parameter dict for following check try: parent_inp_dict = parent_calc.inp.parameters.get_dict() except: self.logger.error( "Failed trying to find input parameter of parent {}".format( parent_calc)) raise InputValidationError( "No parameter node found of parent calculation.") # check if no keys are illegally overwritten (i.e. compare with keys in self._do_never_modify) for key in parameters.get_dict().keys(): value = parameters.get_dict()[key] #self.logger.info("Checking {} {}".format(key, value)) if not value is None: if key in self._do_never_modify: oldvalue = parent_inp_dict[key] if oldvalue is None and key in __kkr_default_params__: oldvalue = __kkr_default_params__.get(key) if value != oldvalue: self.logger.error( "You are trying to set keyword {} = {} but this is not allowed since the structure would be modified. Please use a suitable workfunction instead." .format(key, value)) raise InputValidationError( "You are trying to modify a keyword that is not allowed to be changed! (key={}, oldvalue={}, newvalue={})" .format(key, oldvalue, value)) #TODO check for remote folder (starting from folder data not implemented yet) # if voronoi calc check if folder from db given, or get folder from rep. # Parent calc does not has to be on the same computer. # so far we copy every thing from local computer ggf if kkr we want to copy remotely # get StructureData node from Parent if Voronoi structure = None self.logger.info( "KkrCalculation: Get structure node from voronoi parent") if isinstance(parent_calc, VoronoiCalculation): self.logger.info("KkrCalculation: Parent is Voronoi calculation") try: structure, voro_parent = VoronoiCalculation.find_parent_structure( parent_calc) except: self.logger.error( 'KkrCalculation: Could not get structure from Voronoi parent.' ) raise ValidationError("Cound not find structure node") elif isinstance(parent_calc, KkrCalculation): self.logger.info("KkrCalculation: Parent is KKR calculation") try: self.logger.info( 'KkrCalculation: extract structure from KKR parent') structure, voro_parent = VoronoiCalculation.find_parent_structure( parent_calc) except: self.logger.error('Could not get structure from parent.') raise ValidationError( 'Cound not find structure node starting from parent {}'. format(parent_calc)) else: self.logger.error( "KkrCalculation: Parent is neither Voronoi nor KKR calculation!" ) raise ValidationError('Cound not find structure node') if inputdict: self.logger.error( 'KkrCalculation: Unknown inputs for structure lookup') raise ValidationError("Unknown inputs") # for VCA: check if input structure and parameter node define VCA structure vca_structure = vca_check(structure, parameters) ################################### # prepare scoef file if impurity_info was given write_scoef = False runopt = parameters.get_dict().get('RUNOPT', None) kkrflex_opt = False if runopt is not None: if 'KKRFLEX' in runopt: kkrflex_opt = True if kkrflex_opt: write_scoef = True elif found_imp_info: self.logger.info( 'Found impurity_info in inputs of the calculation, automatically add runopt KKRFLEX' ) write_scoef = True runopt = parameters.get_dict().get('RUNOPT', []) runopt.append('KKRFLEX') parameters = update_params_wf( parameters, ParameterData( dict={ 'RUNOPT': runopt, 'nodename': 'update_KKRFLEX', 'nodedesc': 'Update Parameter node with KKRFLEX runopt' })) if found_imp_info and write_scoef: scoef_filename = os.path.join(tempfolder.get_abs_path(''), self._SCOEF) imp_info_dict = imp_info.get_dict() Rcut = imp_info_dict.get('Rcut', None) hcut = imp_info_dict.get('hcut', -1.) cylinder_orient = imp_info_dict.get('cylinder_orient', [0., 0., 1.]) ilayer_center = imp_info_dict.get('ilayer_center', 0) for i in range(len(cylinder_orient)): try: len(cylinder_orient[i]) vec_shape = False except TypeError: vec_shape = True if ilayer_center > len(structure.sites) - 1: raise IndexError( 'Index of the reference site is out of range! Possible values: 0 to {}.' .format(len(structure.sites) - 1)) elif Rcut < 0: raise ValueError('Cutoff radius has to be positive!') elif vec_shape == False or len(cylinder_orient) != 3: raise TypeError( 'Input orientation vector ({}) has the wrong shape! It needs to be a 3D-vector!' .format(cylinder_orient)) else: print('Input parameters for make_scoef read in correctly!') make_scoef(structure, Rcut, scoef_filename, hcut, cylinder_orient, ilayer_center) elif write_scoef: self.logger.info( 'Need to write scoef file but no impurity_info given!') raise ValidationError( 'Found RUNOPT KKRFLEX but no impurity_info in inputs') # Check for 2D case twoDimcheck, msg = check_2Dinput_consistency(structure, parameters) if not twoDimcheck: raise InputValidationError(msg) # set shapes array either from parent voronoi run or read from inputcard in kkrimporter calculation if parent_calc.get_parser_name() != 'kkr.kkrimporterparser': # get shapes array from voronoi parent shapes = voro_parent.res.shapes else: # extract shapes from input parameters node constructed by kkrimporter calculation shapes = voro_parent.inp.parameters.get_dict().get('<SHAPE>') # use_alat_input = parameters.get_dict().get('use_input_alat', False) # qdos option, ensure low T, E-contour, qdos run option and write qvec.dat file if found_kpath: # check qdos settings change_values = [] runopt = parameters.get_dict().get('RUNOPT') if runopt is None: runopt = [] runopt = [i.strip() for i in runopt] if 'qdos' not in runopt: runopt.append('qdos') change_values.append(['RUNOPT', runopt]) tempr = parameters.get_dict().get('TEMPR') if tempr is None or tempr > 100.: change_values.append(['TEMPR', 50.]) N1 = parameters.get_dict().get('TEMPR') if N1 is None or N1 > 0: change_values.append(['NPT1', 0]) N2 = parameters.get_dict().get('NPT2') if N2 is None: change_values.append(['NPT2', 100]) N3 = parameters.get_dict().get('NPT3') if N3 is None or N3 > 0.: change_values.append(['NPT3', 0]) NPOL = parameters.get_dict().get('NPOL') if NPOL is None or NPOL > 0.: change_values.append(['NPOL', 0]) if change_values != []: new_params = { 'nodename': 'changed_params_qdos', 'nodedesc': 'Changed parameters to mathc qdos mode. Changed values: {}' .format(change_values) } for key, val in change_values: new_params[key] = val new_params_node = ParameterData(dict=new_params) parameters = update_params_wf(parameters, new_params_node) # write qvec.dat file kpath_array = kpath.get_kpoints() # convert automatically to internal units if use_alat_input: alat = parameters.get_dict().get('ALATBASIS') else: alat = get_alat_from_bravais( array(structure.cell), is3D=structure.pbc[2]) * get_Ang2aBohr() kpath_array = kpath_array * (alat / 2. / pi) qvec = ['%i\n' % len(kpath_array)] qvec += [ '%e %e %e\n' % (kpt[0], kpt[1], kpt[2]) for kpt in kpath_array ] qvecpath = tempfolder.get_abs_path(self._QVEC) with open(qvecpath, 'w') as file: file.writelines(qvec) # Prepare inputcard from Structure and input parameter data input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME) natom, nspin, newsosol = generate_inputcard_from_structure( parameters, structure, input_filename, parent_calc, shapes=shapes, vca_structure=vca_structure, use_input_alat=use_alat_input) ################# # Decide what files to copy based on settings to the code (e.g. KKRFLEX option needs scoef) if has_parent: # copy the right files #TODO check first if file, exists and throw # warning, now this will throw an error outfolderpath = parent_calc.out.retrieved.folder.abspath outfolderpath = os.path.join(outfolderpath, 'path') self.logger.info("out folder path {}".format(outfolderpath)) copylist = [] if isinstance(parent_calc, KkrCalculation): copylist = self._copy_filelist_kkr # TODO ggf copy remotely... if isinstance(parent_calc, VoronoiCalculation): copylist = [parent_calc._SHAPEFUN] # copy either overwrite potential or voronoi output potential # (voronoi caclualtion retreives only one of the two) if parent_calc._POTENTIAL_IN_OVERWRITE in os.listdir( outfolderpath): copylist.append(parent_calc._POTENTIAL_IN_OVERWRITE) else: copylist.append(parent_calc._OUT_POTENTIAL_voronoi) #change copylist in case the calculation starts from an imported calculation if parent_calc.get_parser_name() == 'kkr.kkrimporterparser': copylist = [] if not os.path.exists( os.path.join(outfolderpath, self._OUT_POTENTIAL)): copylist.append(self._POTENTIAL) else: copylist.append(self._OUT_POTENTIAL) if os.path.exists(os.path.join(outfolderpath, self._SHAPEFUN)): copylist.append(self._SHAPEFUN) # create local_copy_list from copylist and change some names automatically for file1 in copylist: filename = file1 if (file1 == 'output.pot' or file1 == self._OUT_POTENTIAL or (isinstance(parent_calc, VoronoiCalculation) and file1 == parent_calc._POTENTIAL_IN_OVERWRITE)): filename = self._POTENTIAL local_copy_list.append( (os.path.join(outfolderpath, file1), os.path.join(filename))) # for set-ef option: ef_set = parameters.get_dict().get('ef_set', None) if ef_set is not None: print('local copy list before change: {}'.format( local_copy_list)) print( "found 'ef_set' in parameters: change EF of potential to this value" ) potcopy_info = [ i for i in local_copy_list if i[1] == self._POTENTIAL ][0] with open(potcopy_info[0]) as file: # change potential and copy list local_copy_list.remove(potcopy_info) pot_new_name = tempfolder.get_abs_path(self._POTENTIAL + '_new_ef') local_copy_list.append((pot_new_name, self._POTENTIAL)) # change potential txt = file.readlines() potstart = [] for iline in range(len(txt)): line = txt[iline] if 'exc:' in line: potstart.append(iline) for ipotstart in potstart: tmpline = txt[ipotstart + 3] tmpline = tmpline.split() newline = '%10.5f%20.14f%20.14f\n' % (float( tmpline[0]), ef_set, float(tmpline[-1])) txt[ipotstart + 3] = newline # write new file pot_new_ef = open(pot_new_name, 'w') pot_new_ef.writelines(txt) pot_new_ef.close() # TODO different copy lists, depending on the keywors input print('local copy list: {}'.format(local_copy_list)) self.logger.info('local copy list: {}'.format(local_copy_list)) # Prepare CalcInfo to be returned to aiida calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = [] # TODO retrieve list needs some logic, retrieve certain files, # only if certain input keys are specified.... calcinfo.retrieve_list = [ self._DEFAULT_OUTPUT_FILE, self._INPUT_FILE_NAME, self._POTENTIAL, self._SHAPEFUN, self._SCOEF, self._NONCO_ANGLES_OUT, self._OUT_POTENTIAL, self._OUTPUT_0_INIT, self._OUTPUT_000, self._OUTPUT_2, self._OUT_TIMING_000 ] # for special cases add files to retireve list: # 1. dos calculation, add *dos* files if NPOL==0 retrieve_dos_files = False print('NPOL in parameter input:', parameters.get_dict()['NPOL']) if 'NPOL' in parameters.get_dict().keys(): if parameters.get_dict()['NPOL'] == 0: retrieve_dos_files = True if 'TESTOPT' in parameters.get_dict().keys(): testopts = parameters.get_dict()['TESTOPT'] if testopts is not None: stripped_test_opts = [i.strip() for i in testopts] if 'DOS' in stripped_test_opts: retrieve_dos_files = True if retrieve_dos_files: print('adding files for dos output', self._COMPLEXDOS, self._DOS_ATOM, self._LMDOS) add_files = [self._COMPLEXDOS] for iatom in range(natom): add_files.append(self._DOS_ATOM % (iatom + 1)) for ispin in range(nspin): add_files.append( (self._LMDOS % (iatom + 1, ispin + 1)).replace( ' ', '0')) calcinfo.retrieve_list += add_files # 2. KKRFLEX calculation retrieve_kkrflex_files = False if 'RUNOPT' in parameters.get_dict().keys(): runopts = parameters.get_dict()['RUNOPT'] if runopts is not None: stripped_run_opts = [i.strip() for i in runopts] if 'KKRFLEX' in stripped_run_opts: retrieve_kkrflex_files = True if retrieve_kkrflex_files: add_files = self._ALL_KKRFLEX_FILES print('adding files for KKRFLEX output', add_files) calcinfo.retrieve_list += add_files # 3. qdos claculation retrieve_qdos_files = False if 'RUNOPT' in parameters.get_dict().keys(): runopts = parameters.get_dict()['RUNOPT'] if runopts is not None: stripped_run_opts = [i.strip() for i in runopts] if 'qdos' in stripped_run_opts: retrieve_qdos_files = True if retrieve_qdos_files: print('adding files for qdos output', self._QDOS_ATOM, self._QVEC) add_files = [self._QVEC] for iatom in range(natom): for ispin in range(nspin): add_files.append( (self._QDOS_ATOM % (iatom + 1, ispin + 1)).replace( ' ', '0')) calcinfo.retrieve_list += add_files codeinfo = CodeInfo() codeinfo.cmdline_params = [] codeinfo.code_uuid = code.uuid codeinfo.stdout_name = self._DEFAULT_OUTPUT_FILE calcinfo.codes_info = [codeinfo] return calcinfo
def prepare_for_submission(self, tempfolder): """ Create input files. :param tempfolder: aiida.common.folders.Folder subclass where the plugin should put all its files. :param inputdict: dictionary of the input nodes as they would be returned by get_inputs_dict """ has_parent = False local_copy_list = [] # get mandatory input nodes parameters = self.inputs.parameters code = self.inputs.code parent_calc_folder = self.inputs.parent_folder # now check for optional nodes # for GF writeout if 'impurity_info' in self.inputs: imp_info = self.inputs.impurity_info found_imp_info = True else: imp_info = None found_imp_info = False # for qdos funcitonality if 'kpoints' in self.inputs: kpath = self.inputs.kpoints found_kpath = True else: found_kpath = False # extract parent calculation parent_calcs = parent_calc_folder.get_incoming(node_class=CalcJobNode) n_parents = len(parent_calcs.all_link_labels()) if n_parents != 1: raise UniquenessError( "Input RemoteData is child of {} " "calculation{}, while it should have a single parent" "".format(n_parents, "" if n_parents == 0 else "s")) # TODO change to exit code if n_parents == 1: parent_calc = parent_calcs.first().node has_parent = True # check if parent is either Voronoi or previous KKR calculation #self._check_valid_parent(parent_calc) # extract parent input parameter dict for following check try: parent_inp_dict = parent_calc.inputs.parameters.get_dict() except: self.logger.error( "Failed trying to find input parameter of parent {}".format( parent_calc)) raise InputValidationError( "No parameter node found of parent calculation.") # check if no keys are illegally overwritten (i.e. compare with keys in self._do_never_modify) for key in list(parameters.get_dict().keys()): value = parameters.get_dict()[key] #self.logger.info("Checking {} {}".format(key, value)) if not value is None: if key in self._do_never_modify: oldvalue = parent_inp_dict[key] if oldvalue is None and key in __kkr_default_params__: oldvalue = __kkr_default_params__.get(key) if value == oldvalue: values_eqivalent = True else: values_eqivalent = False # check if values match up to certain numerical accuracy if type(value) == float: if abs(value - oldvalue) < self._eps: values_eqivalent = True elif type(value) == list or type(value) == ndarray: tmp_value, tmp_oldvalue = array(value).reshape( -1), array(oldvalue).reshape(-1) values_eqivalent_tmp = [] for ival in range(len(tmp_value)): if abs(tmp_value[ival] - tmp_oldvalue[ival]) < self._eps: values_eqivalent_tmp.append(True) else: values_eqivalent_tmp.append(False) if all(values_eqivalent_tmp) and len(value) == len( oldvalue): values_eqivalent = True if not values_eqivalent: self.logger.error( "You are trying to set keyword {} = {} but this is not allowed since the structure would be modified. Please use a suitable workfunction instead." .format(key, value)) raise InputValidationError( "You are trying to modify a keyword that is not allowed to be changed! (key={}, oldvalue={}, newvalue={})" .format(key, oldvalue, value)) #TODO check for remote folder (starting from folder data not implemented yet) # if voronoi calc check if folder from db given, or get folder from rep. # Parent calc does not has to be on the same computer. # so far we copy every thing from local computer ggf if kkr we want to copy remotely # get StructureData node from Parent if Voronoi structure = None self.logger.info( "KkrCalculation: Get structure node from voronoi parent") try: structure, voro_parent = VoronoiCalculation.find_parent_structure( parent_calc) except: self.logger.error( 'KkrCalculation: Could not get structure from Voronoi parent ({}).' .format(parent_calc)) raise ValidationError( "Cound not find structure node from parent {}".format( parent_calc)) # for VCA: check if input structure and parameter node define VCA structure vca_structure = vca_check(structure, parameters) ################################### # check whether or not the alat from the input parameters are used (this enters as a scaling factor for some parameters) use_alat_input = parameters.get_dict().get('use_input_alat', False) # prepare scoef file if impurity_info was given write_scoef = False runopt = parameters.get_dict().get('RUNOPT', None) kkrflex_opt = False if runopt is not None: if 'KKRFLEX' in runopt: kkrflex_opt = True if kkrflex_opt: write_scoef = True elif found_imp_info: self.logger.info( 'Found impurity_info in inputs of the calculation, automatically add runopt KKRFLEX' ) write_scoef = True runopt = parameters.get_dict().get('RUNOPT', []) runopt.append('KKRFLEX') parameters = update_params_wf( parameters, Dict( dict={ 'RUNOPT': runopt, 'nodename': 'update_KKRFLEX', 'nodedesc': 'Update Parameter node with KKRFLEX runopt' })) if found_imp_info and write_scoef: imp_info_dict = imp_info.get_dict() Rcut = imp_info_dict.get('Rcut', None) hcut = imp_info_dict.get('hcut', -1.) cylinder_orient = imp_info_dict.get('cylinder_orient', [0., 0., 1.]) ilayer_center = imp_info_dict.get('ilayer_center', 0) for i in range(len(cylinder_orient)): try: len(cylinder_orient[i]) vec_shape = False except TypeError: vec_shape = True if ilayer_center > len(structure.sites) - 1: raise IndexError( 'Index of the reference site is out of range! Possible values: 0 to {}.' .format(len(structure.sites) - 1)) elif Rcut < 0: raise ValueError('Cutoff radius has to be positive!') elif vec_shape == False or len(cylinder_orient) != 3: raise TypeError( 'Input orientation vector ({}) has the wrong shape! It needs to be a 3D-vector!' .format(cylinder_orient)) else: print('Input parameters for make_scoef read in correctly!') with tempfolder.open(self._SCOEF, 'w') as scoef_file: if use_alat_input: alat_input = parameters.get_dict().get( 'ALATBASIS', None) / get_Ang2aBohr() self.logger.info('alat_input is ' + str(alat_input)) else: self.logger.info('alat_input is None') alat_input = None make_scoef(structure, Rcut, scoef_file, hcut, cylinder_orient, ilayer_center, alat_input) elif write_scoef: self.logger.info( 'Need to write scoef file but no impurity_info given!') raise ValidationError( 'Found RUNOPT KKRFLEX but no impurity_info in inputs') # Check for 2D case twoDimcheck, msg = check_2Dinput_consistency(structure, parameters) if not twoDimcheck: raise InputValidationError(msg) # set shapes array either from parent voronoi run or read from inputcard in kkrimporter calculation if parent_calc.process_label == 'VoronoiCalculation' or parent_calc.process_label == 'KkrCalculation': # get shapes array from voronoi parent shapes = voro_parent.outputs.output_parameters.get_dict().get( 'shapes') else: # extract shapes from input parameters node constructed by kkrimporter calculation shapes = voro_parent.inputs.parameters.get_dict().get('<SHAPE>') self.logger.info('Extracted shapes: {}'.format(shapes)) # qdos option, ensure low T, E-contour, qdos run option and write qvec.dat file if found_kpath: # check qdos settings change_values = [] runopt = parameters.get_dict().get('RUNOPT') if runopt is None: runopt = [] runopt = [i.strip() for i in runopt] if 'qdos' not in runopt: runopt.append('qdos') change_values.append(['RUNOPT', runopt]) tempr = parameters.get_dict().get('TEMPR') if tempr is None or tempr > 100.: change_values.append(['TEMPR', 50.]) N1 = parameters.get_dict().get('NPT1') if N1 is None or N1 > 0: change_values.append(['NPT1', 0]) N2 = parameters.get_dict().get('NPT2') if N2 is None: change_values.append(['NPT2', 100]) N3 = parameters.get_dict().get('NPT3') if N3 is None or N3 > 0.: change_values.append(['NPT3', 0]) NPOL = parameters.get_dict().get('NPOL') if NPOL is None or NPOL > 0.: change_values.append(['NPOL', 0]) if change_values != []: new_params = {} #{'nodename': 'changed_params_qdos', 'nodedesc': 'Changed parameters to mathc qdos mode. Changed values: {}'.format(change_values)} for key, val in parameters.get_dict().items(): new_params[key] = val for key, val in change_values: new_params[key] = val new_params_node = Dict(dict=new_params) #parameters = update_params_wf(parameters, new_params_node) parameters = new_params_node # write qvec.dat file kpath_array = kpath.get_kpoints(cartesian=True) # convert automatically to internal units alat = get_alat_from_bravais( array( structure.cell), is3D=structure.pbc[2]) * get_Ang2aBohr() if use_alat_input: alat_input = parameters.get_dict().get('ALATBASIS') else: alat_input = alat kpath_array = kpath_array * ( alat_input / alat) / get_Ang2aBohr() / (2 * pi / alat) # now write file qvec = ['%i\n' % len(kpath_array)] qvec += [ '%e %e %e\n' % (kpt[0], kpt[1], kpt[2]) for kpt in kpath_array ] with tempfolder.open(self._QVEC, 'w') as qvecfile: qvecfile.writelines(qvec) # Prepare inputcard from Structure and input parameter data with tempfolder.open(self._INPUT_FILE_NAME, u'w') as input_file: natom, nspin, newsosol, warnings_write_inputcard = generate_inputcard_from_structure( parameters, structure, input_file, parent_calc, shapes=shapes, vca_structure=vca_structure, use_input_alat=use_alat_input) ################# # Decide what files to copy based on settings to the code (e.g. KKRFLEX option needs scoef) if has_parent: # copy the right files #TODO check first if file, exists and throw # warning, now this will throw an error outfolder = parent_calc.outputs.retrieved copylist = [] if parent_calc.process_class == KkrCalculation: copylist = [self._OUT_POTENTIAL] # TODO ggf copy remotely from remote node if present ... elif parent_calc.process_class == VoronoiCalculation: copylist = [parent_calc.process_class._SHAPEFUN] # copy either overwrite potential or voronoi output potential # (voronoi caclualtion retreives only one of the two) if parent_calc.process_class._POTENTIAL_IN_OVERWRITE in outfolder.list_object_names( ): copylist.append( parent_calc.process_class._POTENTIAL_IN_OVERWRITE) else: copylist.append( parent_calc.process_class._OUT_POTENTIAL_voronoi) #change copylist in case the calculation starts from an imported calculation else: #if parent_calc.process_class == KkrImporterCalculation: if self._OUT_POTENTIAL in outfolder.list_object_names(): copylist.append(self._OUT_POTENTIAL) else: copylist.append(self._POTENTIAL) if self._SHAPEFUN in outfolder.list_object_names(): copylist.append(self._SHAPEFUN) # create local_copy_list from copylist and change some names automatically for file1 in copylist: # deal with special case that file is written to another name if (file1 == 'output.pot' or file1 == self._OUT_POTENTIAL or (parent_calc.process_class == VoronoiCalculation and file1 == parent_calc.process_class._POTENTIAL_IN_OVERWRITE)): filename = self._POTENTIAL else: filename = file1 # now add to copy list local_copy_list.append((outfolder.uuid, file1, filename)) # add shapefun file from voronoi parent if needed if self._SHAPEFUN not in copylist: try: struc, voro_parent = VoronoiCalculation.find_parent_structure( parent_calc) except ValueError: return self.exit_codes.ERROR_NO_SHAPEFUN_FOUND # copy shapefun from retrieved of voro calc voro_retrieved = voro_parent.outputs.retrieved local_copy_list.append( (voro_retrieved.uuid, VoronoiCalculation._SHAPEFUN, self._SHAPEFUN)) # for set-ef option: ef_set = parameters.get_dict().get('ef_set', None) if ef_set is not None: print('local copy list before change: {}'.format( local_copy_list)) print( "found 'ef_set' in parameters: change EF of potential to this value" ) potcopy_info = [ i for i in local_copy_list if i[2] == self._POTENTIAL ][0] with load_node(potcopy_info[0]).open( potcopy_info[1]) as potfile: # remove previous output potential from copy list local_copy_list.remove(potcopy_info) # create potential here by readin in old potential and overwriting with changed Fermi energy with tempfolder.open(self._POTENTIAL, 'w') as pot_new_ef: # change potential txt = potfile.readlines() potstart = [] for iline in range(len(txt)): line = txt[iline] if 'exc:' in line: potstart.append(iline) for ipotstart in potstart: tmpline = txt[ipotstart + 3] tmpline = tmpline.split() newline = '%10.5f%20.14f%20.14f\n' % (float( tmpline[0]), ef_set, float(tmpline[-1])) txt[ipotstart + 3] = newline # write new file pot_new_ef.writelines(txt) # now this directory contains the updated potential file, thus it is not needed to put it in the local copy list anymore # TODO different copy lists, depending on the keywors input print('local copy list: {}'.format(local_copy_list)) self.logger.info('local copy list: {}'.format(local_copy_list)) # Prepare CalcInfo to be returned to aiida calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = [] # TODO retrieve list needs some logic, retrieve certain files, # only if certain input keys are specified.... calcinfo.retrieve_list = [ self._DEFAULT_OUTPUT_FILE, self._INPUT_FILE_NAME, self._SCOEF, self._NONCO_ANGLES_OUT, self._OUT_POTENTIAL, self._OUTPUT_0_INIT, self._OUTPUT_000, self._OUTPUT_2, self._OUT_TIMING_000 ] # for special cases add files to retireve list: # 1. dos calculation, add *dos* files if NPOL==0 retrieve_dos_files = False print('NPOL in parameter input:', parameters.get_dict()['NPOL']) if 'NPOL' in list(parameters.get_dict().keys()): if parameters.get_dict()['NPOL'] == 0: retrieve_dos_files = True if 'TESTOPT' in list(parameters.get_dict().keys()): testopts = parameters.get_dict()['TESTOPT'] if testopts is not None: stripped_test_opts = [i.strip() for i in testopts] if 'DOS' in stripped_test_opts: retrieve_dos_files = True if retrieve_dos_files: print('adding files for dos output', self._COMPLEXDOS, self._DOS_ATOM, self._LMDOS) add_files = [self._COMPLEXDOS] for iatom in range(natom): add_files.append(self._DOS_ATOM % (iatom + 1)) for ispin in range(nspin): add_files.append( (self._LMDOS % (iatom + 1, ispin + 1)).replace( ' ', '0')) calcinfo.retrieve_list += add_files # 2. KKRFLEX calculation retrieve_kkrflex_files = False if 'RUNOPT' in list(parameters.get_dict().keys()): runopts = parameters.get_dict()['RUNOPT'] if runopts is not None: stripped_run_opts = [i.strip() for i in runopts] if 'KKRFLEX' in stripped_run_opts: retrieve_kkrflex_files = True if retrieve_kkrflex_files: add_files = self._ALL_KKRFLEX_FILES print('adding files for KKRFLEX output', add_files) calcinfo.retrieve_list += add_files # 3. qdos claculation retrieve_qdos_files = False if 'RUNOPT' in list(parameters.get_dict().keys()): runopts = parameters.get_dict()['RUNOPT'] if runopts is not None: stripped_run_opts = [i.strip() for i in runopts] if 'qdos' in stripped_run_opts: retrieve_qdos_files = True if retrieve_qdos_files: print('adding files for qdos output', self._QDOS_ATOM, self._QVEC) add_files = [self._QVEC] for iatom in range(natom): for ispin in range(nspin): add_files.append( (self._QDOS_ATOM % (iatom + 1, ispin + 1)).replace( ' ', '0')) # retrieve also qdos_sx,y,z files if written out add_files.append( (self._QDOS_SX % (iatom + 1)).replace(' ', '0')) add_files.append( (self._QDOS_SY % (iatom + 1)).replace(' ', '0')) add_files.append( (self._QDOS_SZ % (iatom + 1)).replace(' ', '0')) calcinfo.retrieve_list += add_files # 4. Jij calculation retrieve_Jij_files = False if 'RUNOPT' in list(parameters.get_dict().keys()): runopts = parameters.get_dict()['RUNOPT'] if runopts is not None: stripped_run_opts = [i.strip() for i in runopts] if 'XCPL' in stripped_run_opts: retrieve_Jij_files = True if retrieve_Jij_files: add_files = [self._SHELLS_DAT] + [ self._Jij_ATOM % iatom for iatom in range(1, natom + 1) ] print('adding files for Jij output', add_files) calcinfo.retrieve_list += add_files codeinfo = CodeInfo() codeinfo.cmdline_params = [] codeinfo.code_uuid = code.uuid codeinfo.stdout_name = self._DEFAULT_OUTPUT_FILE calcinfo.codes_info = [codeinfo] return calcinfo
def from_cp2k(cls, fhandle, filters=None, duplicate_handling="ignore"): """ Constructs a list with basis set objects from a Basis Set in CP2K format :param fhandle: open file handle :param filters: a dict with attribute filter functions :param duplicate_handling: how to handle duplicates ("ignore", "error", "new" (version)) :rtype: list """ if not filters: filters = {} def matches_criteria(bset): return all(fspec(bset[field]) for field, fspec in filters.items()) def exists(bset): try: cls.get(bset["element"], bset["name"], match_aliases=False) except NotExistent: return False return True bsets = [ bs for bs in (_basissetdata2dict(bs) for bs in BasisSetData.datafile_iter(fhandle)) if matches_criteria(bs) ] if duplicate_handling == "ignore": # simply filter duplicates bsets = [bs for bs in bsets if not exists(bs)] elif duplicate_handling == "error": for bset in bsets: try: latest = cls.get(bset["element"], bset["name"], match_aliases=False) except NotExistent: pass else: raise UniquenessError( f"Gaussian Basis Set already exists for" f" element={bset['element']}, name={bset['name']}: {latest.uuid}" ) elif duplicate_handling == "new": for bset in bsets: try: latest = cls.get(bset["element"], bset["name"], match_aliases=False) except NotExistent: pass else: bset["version"] = latest.version + 1 else: raise ValueError( f"Specified duplicate handling strategy not recognized: '{duplicate_handling}'" ) return [cls(**bs) for bs in bsets]
def neworder_potential_wf(settings_node, parent_calc_folder, **kwargs): #, parent_calc_folder2=None): """ Workfunction to create database structure for aiida_kkr.tools.modify_potential.neworder_potential function A temporary file is written in a Sandbox folder on the computer specified via the input computer node before the output potential is stored as SingleFileData in the Database. :param settings_node: settings for the neworder_potentail function (ParameterData) :param parent_calc_folder: parent calculation remote folder node where the input potential is retreived from (RemoteData) :param parent_calc_folder2: *optional*, parent calculation remote folder node where the second input potential is retreived from in case 'pot2' and 'replace_newpos' are also set in settings_node (RemoteData) :returns: output_potential node (SingleFileData) .. note:: The settings_node dictionary needs to be of the following form:: settings_dict = {'pot1': '<filename_input_potential>', 'out_pot': '<filename_output_potential>', 'neworder': [list of intended order in output potential]} Optional entries are:: 'pot2': '<filename_second_input_file>' 'replace_newpos': [[position in neworder list which is replace with potential from pot2, position in pot2 that is chosen for replacement]] 'label': 'label_for_output_node' 'description': 'longer_description_for_output_node' """ import os from aiida_kkr.tools.tools_kkrimp import modify_potential from aiida.common.folders import SandboxFolder from aiida.common.exceptions import UniquenessError from aiida.orm.calculation.job import JobCalculation from aiida.orm import DataFactory if 'parent_calc_folder2' in kwargs.keys(): parent_calc_folder2 = kwargs.get('parent_calc_folder2', None) else: parent_calc_folder2 = None # get aiida data types used here ParameterData = DataFactory('parameter') RemoteData = DataFactory('remote') SingleFileData = DataFactory('singlefile') # check input consistency if not isinstance(settings_node, ParameterData): raise InputValidationError( 'settings_node needs to be a valid aiida ParameterData node') if not isinstance(parent_calc_folder, RemoteData): raise InputValidationError( 'parent_calc_folder needs to be a valid aiida RemoteData node') if parent_calc_folder2 is not None and not isinstance( parent_calc_folder2, RemoteData): raise InputValidationError( 'parent_calc_folder2 needs to be a valid aiida RemoteData node') settings_dict = settings_node.get_dict() pot1 = settings_dict.get('pot1', None) if pot1 is None: raise InputValidationError( 'settings_node_dict needs to have key "pot1" containing the filename of the input potential' ) out_pot = settings_dict.get('out_pot', None) if out_pot is None: raise InputValidationError( 'settings_node_dict needs to have key "out_pot" containing the filename of the input potential' ) neworder = settings_dict.get('neworder', None) if neworder is None: raise InputValidationError( 'settings_node_dict needs to have key "neworder" containing the list of new positions' ) pot2 = settings_dict.get('pot2', None) replace_newpos = settings_dict.get('replace_newpos', None) # Create Sandbox folder for generation of output potential file # and construct output potential with SandboxFolder() as tempfolder: # Get abolute paths of input files from parent calc and filename parent_calcs = parent_calc_folder.get_inputs(node_type=JobCalculation) n_parents = len(parent_calcs) if n_parents != 1: raise UniquenessError( "Input RemoteData is child of {} " "calculation{}, while it should have a single parent" "".format(n_parents, "" if n_parents == 0 else "s")) else: parent_calc = parent_calcs[0] remote_path = parent_calc.out.retrieved.get_abs_path('') pot1_path = os.path.join(remote_path, pot1) # extract nspin from parent calc's input parameter node nspin = parent_calc.inp.parameters.get_dict().get('NSPIN') neworder_spin = [] for iatom in neworder: for ispin in range(nspin): neworder_spin.append(iatom * nspin + ispin) neworder = neworder_spin # Copy optional files? if pot2 is not None and parent_calc_folder2 is not None: parent_calcs = parent_calc_folder2.get_inputs( node_type=JobCalculation) n_parents = len(parent_calcs) if n_parents != 1: raise UniquenessError( "Input RemoteData of parent_calc_folder2 is child of {} " "calculation{}, while it should have a single parent" "".format(n_parents, "" if n_parents == 0 else "s")) else: parent_calc = parent_calcs[0] remote_path = parent_calc.out.retrieved.get_abs_path('') pot2_path = os.path.join(remote_path, pot2) else: pot2_path = None # change file path to Sandbox folder accordingly out_pot_path = tempfolder.get_abs_path(out_pot) # run neworder_potential function modify_potential().neworder_potential(pot1_path, out_pot_path, neworder, potfile_2=pot2_path, replace_from_pot2=replace_newpos) # store output potential to SingleFileData output_potential_sfd_node = SingleFileData(file=out_pot_path) lbl = settings_dict.get('label', None) if lbl is not None: output_potential_sfd_node.label = lbl desc = settings_dict.get('description', None) if desc is not None: output_potential_sfd_node.description = desc #TODO create shapefun sfd node accordingly """ out_shape_path = output_shapefun_sfd_node = SingleFileData(file=out_shape_path) lbl2 = settings_dict.get('label_shape', None) if lbl2 is None and lbl is not None: lbl2 = lbl if lbl2 is not None: output_shapefun_sfd_node.label = lbl2 desc2 = settings_dict.get('description_shape', None) if desc2 is None and desc is not None: desc2 = desc if desc2 is not None: output_shapefun_sfd_node.description = desc2 return output_potential_sfd_node, output_shapefun_sfd_node """ return output_potential_sfd_node
def prepare_for_submission(self, folder): """ This is the routine to be called when you make a FLEUR calculation. This routine checks the inputs and modifies copy lists accordingly. The standard files to be copied are given here. :param folder: a aiida.common.folders.Folder subclass where the plugin should put all its files. """ local_copy_list = [] remote_copy_list = [] remote_symlink_list = [] mode_retrieved_filelist = [] filelist_tocopy_remote = [] settings_dict = {} has_fleurinp = False has_parent = False fleurinpgen = False copy_remotely = True with_hdf5 = False code = self.inputs.code codesdesc = code.description # TODO: ggf also check settings # In code description we write with what libs the code was compiled # we look for certain keywords in the description # also ggf, to be back comportable, the plugin should know the version number if codesdesc is not None: if 'hdf5' in codesdesc: with_hdf5 = True elif 'Hdf5' in codesdesc: with_hdf5 = True elif 'HDF5' in codesdesc: with_hdf5 = True else: with_hdf5 = False # a Fleur calc can be created from a fleurinpData alone # (then no parent is needed) all files are in the repo, but usually it is # a child of a inpgen calc or an other fleur calc (some or all files are # in a remote source). if the User has not changed something, the # calculation does not need theoretical a new FleurinpData it could use # the one from the parent, but the plug-in desgin is in a way that it has # to be there and it just copies files if changes occurred.. if 'fleurinpdata' in self.inputs: fleurinp = self.inputs.fleurinpdata else: fleurinp = None if fleurinp is None: has_fleurinp = False else: has_fleurinp = True if 'parent_folder' in self.inputs: parent_calc_folder = self.inputs.parent_folder else: parent_calc_folder = None if parent_calc_folder is None: has_parent = False if not has_fleurinp: raise InputValidationError('No parent calculation found and no fleurinp data ' 'given, need either one or both for a ' "'fleurcalculation'.") else: # extract parent calculation parent_calcs = parent_calc_folder.get_incoming(node_class=CalcJob).all() n_parents = len(parent_calcs) if n_parents != 1: raise UniquenessError('Input RemoteData is child of {} ' 'calculation{}, while it should have a single parent' ''.format(n_parents, '' if n_parents == 0 else 's')) parent_calc = parent_calcs[0].node parent_calc_class = parent_calc.process_class has_parent = True # check that it is a valid parent # self._check_valid_parent(parent_calc) # if inpgen calc do # check if folder from db given, or get folder from rep. # Parent calc does not has to be on the same computer. if parent_calc_class is FleurCalculation: new_comp = self.node.computer old_comp = parent_calc.computer if new_comp.uuid != old_comp.uuid: # don't copy files, copy files locally copy_remotely = False elif parent_calc_class is FleurinputgenCalculation: fleurinpgen = True new_comp = self.node.computer old_comp = parent_calc.computer if new_comp.uuid != old_comp.uuid: # don't copy files, copy files locally copy_remotely = False else: raise InputValidationError("parent_calc, must be either an 'inpgen calculation' or" " a 'fleur calculation'.") # check existence of settings (optional) if 'settings' in self.inputs: settings = self.inputs.settings else: settings = None if settings is None: settings_dict = {} else: settings_dict = settings.get_dict() # check for for allowed keys, ignore unknown keys but warn. for key in settings_dict.keys(): if key not in self._settings_keys: self.logger.warning( 'settings dict key %s for Fleur calculation' 'not recognized, only %s are allowed.' '', key, str(self._settings_keys)) # TODO: Detailed check of FleurinpData # if certain files are there in fleurinpData # from where to copy # file copy stuff TODO check in fleur input if has_fleurinp: # add files belonging to fleurinp into local_copy_list allfiles = fleurinp.files for file1 in allfiles: local_copy_list.append((fleurinp.uuid, file1, file1)) modes = fleurinp.get_fleur_modes() # add files to mode_retrieved_filelist if modes['band']: mode_retrieved_filelist.append(self._BAND_FILE_NAME) mode_retrieved_filelist.append(self._BAND_GNU_FILE_NAME) if modes['dos']: mode_retrieved_filelist.append(self._DOS_FILE_NAME) if modes['forces']: # if l_f="T" retrieve relax.xml mode_retrieved_filelist.append(self._RELAX_FILE_NAME) if modes['ldau']: if with_hdf5: mode_retrieved_filelist.append(self._NMMPMAT_HDF5_FILE_NAME) else: mode_retrieved_filelist.append(self._NMMPMAT_FILE_NAME) if modes['force_theorem']: if 'remove_from_retrieve_list' not in settings_dict: settings_dict['remove_from_retrieve_list'] = [] if with_hdf5: settings_dict['remove_from_retrieve_list'].append(self._CDN_LAST_HDF5_FILE_NAME) else: settings_dict['remove_from_retrieve_list'].append(self._CDN1_FILE_NAME) # if noco, ldau, gw... # TODO: check from where it was copied, and copy files of its parent # if needed if has_parent: # copy necessary files # TODO: check first if file exist and throw a warning if not outfolder_uuid = parent_calc.outputs.retrieved.uuid self.logger.info('out folder path %s', outfolder_uuid) outfolder_filenames = [x.name for x in parent_calc.outputs.retrieved.list_objects()] has_nmmpmat_file = self._NMMPMAT_FILE_NAME in outfolder_filenames if (self._NMMPMAT_FILE_NAME in outfolder_filenames or \ self._NMMPMAT_HDF5_FILE_NAME in outfolder_filenames): if has_fleurinp: if 'n_mmp_mat' in fleurinp.files: self.logger.warning('Ingnoring n_mmp_mat from fleurinp. ' 'There is already an n_mmp_mat file ' 'for the parent calculation') local_copy_list.remove((fleurinp.uuid, 'n_mmp_mat', 'n_mmp_mat')) if fleurinpgen and (not has_fleurinp): for file1 in self._copy_filelist_inpgen: local_copy_list.append((outfolder_uuid, os.path.join(file1), os.path.join(file1))) elif not fleurinpgen and (not has_fleurinp): # fleurCalc # need to copy inp.xml from the parent calc if with_hdf5: copylist = self._copy_scf_hdf elif has_nmmpmat_file: copylist = self._copy_scf_ldau_nohdf else: copylist = self._copy_scf for file1 in copylist: local_copy_list.append((outfolder_uuid, file1[0], file1[1])) # TODO: get inp.xml from parent fleurinpdata; otherwise it will be doubled in rep elif fleurinpgen and has_fleurinp: # everything is taken care of pass elif not fleurinpgen and has_fleurinp: # inp.xml will be copied from fleurinp if with_hdf5: copylist = self._copy_scf_noinp_hdf elif has_nmmpmat_file: copylist = self._copy_scf_ldau_noinp_nohdf else: copylist = self._copy_scf_noinp for file1 in copylist: local_copy_list.append((outfolder_uuid, file1[0], file1[1])) # TODO: not on same computer -> copy needed files from repository # if they are not there throw an error if copy_remotely: # on same computer. # from fleurmodes if modes['dos']: pass elif modes['band']: pass else: filelist_tocopy_remote = filelist_tocopy_remote + \ self._copy_filelist_scf_remote # from settings, user specified # TODO: check if list? for file1 in settings_dict.get('additional_remotecopy_list', []): filelist_tocopy_remote.append(file1) for file1 in settings_dict.get('remove_from_remotecopy_list', []): if file1 in filelist_tocopy_remote: filelist_tocopy_remote.remove(file1) for file1 in filelist_tocopy_remote: remote_copy_list.append( (parent_calc_folder.computer.uuid, os.path.join(parent_calc_folder.get_remote_path(), file1), self._get_output_folder)) self.logger.info('remote copy file list %s', str(remote_copy_list)) # create a JUDFT_WARN_ONLY file in the calculation folder with io.StringIO(u'/n') as handle: warn_only_filename = self._JUDFT_WARN_ONLY_INFO_FILE_NAME folder.create_file_from_filelike(handle, filename=warn_only_filename, mode='w') ########## MAKE CALCINFO ########### calcinfo = CalcInfo() calcinfo.uuid = self.uuid # Empty command line by default #cmdline_params = settings_dict.pop('CMDLINE', []) # calcinfo.cmdline_params = (list(cmdline_params) # + ["-in", self._INPUT_FILE_NAME]) self.logger.info('local copy file list %s', str(local_copy_list)) calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list calcinfo.remote_symlink_list = remote_symlink_list # Retrieve by default the output file and the xml file retrieve_list = [] retrieve_list.append(self._OUTXML_FILE_NAME) retrieve_list.append(self._INPXML_FILE_NAME) retrieve_list.append(self._SHELLOUTPUT_FILE_NAME) retrieve_list.append(self._ERROR_FILE_NAME) retrieve_list.append(self._USAGE_FILE_NAME) # retrieve_list.append(self._TIME_INFO_FILE_NAME) # retrieve_list.append(self._OUT_FILE_NAME) if with_hdf5: retrieve_list.append(self._CDN_LAST_HDF5_FILE_NAME) else: retrieve_list.append(self._CDN1_FILE_NAME) for mode_file in mode_retrieved_filelist: retrieve_list.append(mode_file) self.logger.info('retrieve_list: %s', str(retrieve_list)) # user specific retrieve add_retrieve = settings_dict.get('additional_retrieve_list', []) self.logger.info('add_retrieve: %s', str(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() # should look like: codepath -xmlInput < inp.xml > shell.out 2>&1 walltime_sec = self.node.get_attribute('max_wallclock_seconds') cmdline_params = [] # , "-wtime", "{}".format(walltime_sec)]"-xml" cmdline_params.append('-minimalOutput') if with_hdf5: cmdline_params.append('-last_extra') cmdline_params.append('-no_send') if walltime_sec: walltime_min = int(max(1, walltime_sec / 60)) cmdline_params.append('-wtime') cmdline_params.append('{}'.format(int(walltime_min))) # user specific commandline_options for command in settings_dict.get('cmdline', []): cmdline_params.append(command) codeinfo.cmdline_params = list(cmdline_params) # + ["<", self._INPXML_FILE_NAME, # ">", self._SHELLOUTPUT_FILE_NAME, "2>&1"] codeinfo.code_uuid = code.uuid codeinfo.withmpi = self.node.get_attribute('max_wallclock_seconds') codeinfo.stdin_name = None # self._INPUT_FILE_NAME codeinfo.stdout_name = self._SHELLOUTPUT_FILE_NAME #codeinfo.join_files = True codeinfo.stderr_name = self._ERROR_FILE_NAME calcinfo.codes_info = [codeinfo] return calcinfo
def from_cp2k(cls, fhandle, filters=None, duplicate_handling="ignore", ignore_invalid=False): """ Constructs a list with pseudopotential objects from a Pseudopotential in CP2K format :param fhandle: open file handle :param filters: a dict with attribute filter functions :param duplicate_handling: how to handle duplicates ("ignore", "error", "new" (version)) :param ignore_invalid: whether to ignore invalid entries silently :rtype: list """ from cp2k_input_tools.pseudopotentials import PseudopotentialData if not filters: filters = {} def matches_criteria(pseudo): return all( fspec(pseudo[field]) for field, fspec in filters.items()) def exists(pseudo): try: cls.get(pseudo["element"], pseudo["name"], match_aliases=False) except NotExistent: return False return True pseudos = [ p for p in (_pseudodata2dict(p) for p in PseudopotentialData.datafile_iter( fhandle, keep_going=ignore_invalid)) if matches_criteria(p) ] if duplicate_handling == "ignore": # simply filter duplicates pseudos = [p for p in pseudos if not exists(p)] elif duplicate_handling == "error": for pseudo in pseudos: try: latest = cls.get(pseudo["element"], pseudo["name"], match_aliases=False) except NotExistent: pass else: raise UniquenessError( f"Gaussian Pseudopotential already exists for" f" element={pseudo['element']}, name={pseudo['name']}: {latest.uuid}" ) elif duplicate_handling == "new": for pseudo in pseudos: try: latest = cls.get(pseudo["element"], pseudo["name"], match_aliases=False) except NotExistent: pass else: pseudo["version"] = latest.version + 1 else: raise ValueError( f"Specified duplicate handling strategy not recognized: '{duplicate_handling}'" ) return [cls(**p) for p in pseudos]
def upload_psml_family(folder, group_label, group_description, stop_if_existing=True): """ Upload a set of PSML files in a given group. :param folder: a path containing all PSML files to be added. Only files ending in .psml (case sensitive) are considered. :param group_label: the name of the group to create. If it exists and is non-empty, a UniquenessError is raised. :param group_description: a string to be set as the group description. Overwrites previous descriptions, if the group was existing. :param stop_if_existing: if True, check for the md5 of the files and, if the file already exists in the DB, raises a MultipleObjectsError. If False, simply adds the existing PsmlData node to the group. """ import os from aiida import orm from aiida.common import AIIDA_LOGGER as aiidalogger from aiida.common.exceptions import UniquenessError from aiida.orm.querybuilder import QueryBuilder from aiida_siesta.groups.pseudos import PsmlFamily message = ( #pylint: disable=invalid-name 'This function has been deprecated and will be removed in `v2.0.0`. ' + '`upload_psml_family` is substitued by `fam.create_from_folder` ' + 'where `fam` is an instance of the families classes in `aiida_pseudo.groups.family`.' ) warnings.warn(message, AiidaSiestaDeprecationWarning) if not os.path.isdir(folder): raise ValueError("folder must be a directory") # only files, and only those ending with .psml; # go to the real file if it is a symlink files = [ os.path.realpath(os.path.join(folder, i)) for i in os.listdir(folder) if os.path.isfile(os.path.join(folder, i)) and i.endswith('.psml') ] nfiles = len(files) automatic_user = orm.User.objects.get_default() #group, group_created = orm.Group.objects.get_or_create( # label=group_label, type_string=PSMLGROUP_TYPE, user=automatic_user #) group, group_created = PsmlFamily.objects.get_or_create( label=group_label, user=automatic_user) if group.user.email != automatic_user.email: raise UniquenessError( "There is already a PsmlFamily group with name {}" ", but it belongs to user {}, therefore you " "cannot modify it".format(group_label, group.user.email)) # Always update description, even if the group already existed group.description = group_description # NOTE: GROUP SAVED ONLY AFTER CHECKS OF UNICITY pseudo_and_created = [] for afile in files: md5sum = md5_file(afile) qb = QueryBuilder() qb.append(PsmlData, filters={'attributes.md5': {'==': md5sum}}) existing_psml = qb.first() #existing_psml = PsmlData.query(dbattributes__key="md5", # dbattributes__tval = md5sum) if existing_psml is None: # return the psmldata instances, not stored pseudo, created = PsmlData.get_or_create(afile, use_first=True, store_psml=False) # to check whether only one psml per element exists # NOTE: actually, created has the meaning of "to_be_created" pseudo_and_created.append((pseudo, created)) else: if stop_if_existing: raise ValueError("A PSML with identical MD5 to " " {} cannot be added with stop_if_existing" "".format(afile)) existing_psml = existing_psml[0] pseudo_and_created.append((existing_psml, False)) # check whether pseudo are unique per element elements = [(i[0].element, i[0].md5sum) for i in pseudo_and_created] # If group already exists, check also that I am not inserting more than # once the same element if not group_created: for aiida_n in group.nodes: # Skip non-pseudos if not isinstance(aiida_n, PsmlData): continue elements.append((aiida_n.element, aiida_n.md5sum)) elements = set(elements) # Discard elements with the same MD5, that would # not be stored twice elements_names = [e[0] for e in elements] if not len(elements_names) == len(set(elements_names)): duplicates = {x for x in elements_names if elements_names.count(x) > 1} duplicates_string = ", ".join(i for i in duplicates) raise UniquenessError("More than one PSML found for the elements: " + duplicates_string + ".") # At this point, save the group, if still unstored if group_created: group.store() # save the psml in the database, and add them to group for pseudo, created in pseudo_and_created: if created: pseudo.store() aiidalogger.debug("New node {} created for file {}".format( pseudo.uuid, pseudo.filename)) else: aiidalogger.debug("Reusing node {} for file {}".format( pseudo.uuid, pseudo.filename)) # Add elements to the group all togetehr group.add_nodes([pseudo for pseudo, created in pseudo_and_created]) nuploaded = len([_ for _, created in pseudo_and_created if created]) return nfiles, nuploaded
def _get_and_verify_hostfiles(self, inputdict): """ Check inputdict for host_Greenfunction_folder and extract impurity_info, paths to kkrflex-files and path of shapefun file :param inputdict: input dictionary containing all input nodes to KkrimpCalculation :returns: * imp_info: ParameterData node containing impurity information like position, Z_imp, cluster size, etc. * kkrflex_file_paths: dict of absolute file paths for the kkrflex files * shapefun_path: absolute path of the shapefunction file in the host calculation (needed to construct shapefun_imp) * shapes: mapping array of atoms to shapes (<SHAPE> input) :note: shapefun_path is None if host_Greenfunction calculation was not full-potential :raises: * InputValidationError, if inputdict does not contain 'host_Greenfunction' * InputValidationError, if host_Greenfunction_folder not of right type * UniquenessError, if host_Greenfunction_folder does not have exactly one parent * InputValidationError, if host_Greenfunction does not have an input node impurity_info * InputValidationError, if host_Greenfunction was not a KKRFLEX calculation """ # get host_parent node and check consistency try: host_parent = inputdict.pop(self.get_linkname('host_Greenfunction_folder')) except KeyError: raise InputValidationError("No host_Greenfunction_folder specified for this calculation") # check host parent type if not isinstance(host_parent, RemoteData): raise InputValidationError("host_Greenfunction_folder not of type RemoteData") # extract parent calculation parent_calcs = host_parent.get_inputs(node_type=JobCalculation) n_parents = len(parent_calcs) if n_parents != 1: raise UniquenessError( "Input RemoteData is child of {} " "calculation{}, while it should have a single parent" "".format(n_parents, "" if n_parents == 0 else "s")) else: parent_calc = parent_calcs[0] # extract impurity_info imp_info = parent_calc.get_inputs_dict().get('impurity_info', None) if imp_info is None: raise InputValidationError("host_Greenfunction calculation does not have an input node impurity_info") # check if host parent was KKRFLEX calculation hostfolderpath = parent_calc.out.retrieved.folder.abspath hostfolderpath = os.path.join(hostfolderpath, 'path') input_file = os.path.join(hostfolderpath, KkrCalculation()._DEFAULT_INPUT_FILE) params_host_calc = kkrparams(params_type='kkr') # initialize kkrparams instance to use read_keywords_from_inputcard params_host_calc.read_keywords_from_inputcard(inputcard=input_file) if 'RUNOPT' not in params_host_calc.get_dict().keys(): host_ok = False elif 'KKRFLEX' not in params_host_calc.get_dict().get('RUNOPT', []): host_ok = False else: host_ok = True if not host_ok: raise InputValidationError("host_Greenfunction calculation was not a KKRFLEX run") # extract absolute paths of kkrflex_* files kkrflex_file_paths = {} for file in self._ALL_KKRFLEX_FILES: file_abspath = os.path.join(hostfolderpath, file) if os.path.exists(file_abspath): kkrflex_file_paths[file] = file_abspath # extract absolute path of host shapefun file_abspath = os.path.join(hostfolderpath, KkrCalculation()._SHAPEFUN) if os.path.exists(file_abspath): shapefun_path = file_abspath else: shapefun_path = None # extract shapes array from parameters read from inputcard shapes = params_host_calc.get_dict().get('<SHAPE>', None) if type(shapes)==int: shapes = [shapes] # extract input structure try: structure, voro_parent = VoronoiCalculation.find_parent_structure(parent_calc) except: structure, voro_parent = None, None if structure is None: raiseInputValidationError("No structure node found from host GF parent") return imp_info, kkrflex_file_paths, shapefun_path, shapes, parent_calc, params_host_calc, structure
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!) """ try: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError("No code specified for this calculation") local_copy_list = [] remote_copy_list = [] remote_symlink_list = [] 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: qpoints = inputdict.pop(self.get_linkname('qpoints')) except KeyError: raise InputValidationError("No qpoints specified for this calculation") if not isinstance(qpoints, KpointsData): raise InputValidationError("qpoints is not of type KpointsData") # Settings can be undefined, and defaults to an empty dictionary. # They will be used for any input that doen't fit elsewhere. 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 settings_dict = _uppercase_dict(settings.get_dict(), dict_name='settings') parent_calc_folder = inputdict.pop(self.get_linkname('parent_folder'),None) if parent_calc_folder is None: raise InputValidationError("No parent calculation found, needed to " "compute phonons") # TODO: to be a PwCalculation is not sufficient: it could also be a nscf # calculation that is invalid for phonons if not isinstance(parent_calc_folder, RemoteData): raise InputValidationError("parent_calc_folder, if specified," "must be of type RemoteData") restart_flag = False # extract parent calculation parent_calcs = parent_calc_folder.get_inputs(node_type=JobCalculation) n_parents = len(parent_calcs) if n_parents != 1: raise UniquenessError("Input RemoteData is child of {} " "calculation{}, while it should have " "a single parent".format(n_parents, "" if n_parents==0 else "s")) parent_calc = parent_calcs[0] # check that it is a valid parent self._check_valid_parent(parent_calc) if not isinstance(parent_calc, PwCalculation): restart_flag = True # Also, the parent calculation must be on the same computer new_comp = self.get_computer() old_comp = parent_calc.get_computer() if ( not new_comp.uuid == old_comp.uuid ): raise InputValidationError("PhCalculation must be launched on the same computer" " of the parent: {}".format(old_comp.get_name())) # put by default, default_parent_output_folder = ./out try: default_parent_output_folder = parent_calc._OUTPUT_SUBFOLDER except AttributeError: try: default_parent_output_folder = parent_calc._get_output_folder() except AttributeError: raise InputValidationError("Parent of PhCalculation does not " "have a default output subfolder") #os.path.join( # parent_calc.OUTPUT_SUBFOLDER, # '{}.save'.format(parent_calc.PREFIX)) parent_calc_out_subfolder = settings_dict.pop('PARENT_CALC_OUT_SUBFOLDER', default_parent_output_folder) # Here, there should be no other inputs if inputdict: raise InputValidationError("The following input data nodes are " "unrecognized: {}".format(inputdict.keys())) ############################## # END OF INITIAL INPUT CHECK # ############################## # I put the first-level keys as uppercase (i.e., namelist and card names) # and the second-level keys as lowercase # (deeper levels are unchanged) input_params = _uppercase_dict(parameters.get_dict(), dict_name='parameters') input_params = {k: _lowercase_dict(v, dict_name=k) for k, v in input_params.iteritems()} prepare_for_d3 = settings_dict.pop('PREPARE_FOR_D3',False) if prepare_for_d3: self._blocked_keywords += [('INPUTPH', 'fildrho'), ('INPUTPH', 'drho_star%open'), ('INPUTPH', 'drho_star%ext'), ('INPUTPH', 'drho_star%dir')] # I remove unwanted elements (for the moment, instead, I stop; to change when # we setup a reasonable logging) for nl, flag in self._blocked_keywords: if nl in input_params: if flag in input_params[nl]: raise InputValidationError( "You cannot specify explicitly the '{}' flag in the '{}' " "namelist or card.".format(flag, nl)) # Set some variables (look out at the case! NAMELISTS should be uppercase, # internal flag names must be lowercase) if 'INPUTPH' not in input_params: raise InputValidationError("No namelist INPUTPH found in input") # I cannot decide what to do in the calculation input_params['INPUTPH']['outdir'] = self._OUTPUT_SUBFOLDER input_params['INPUTPH']['iverbosity'] = 1 # in human language 1=high input_params['INPUTPH']['prefix'] = self._PREFIX input_params['INPUTPH']['fildyn'] = self._OUTPUT_DYNAMICAL_MATRIX_PREFIX if prepare_for_d3: input_params['INPUTPH']['fildrho'] = self._DRHO_PREFIX input_params['INPUTPH']['drho_star%open'] = True input_params['INPUTPH']['drho_star%ext'] = self._DRHO_STAR_EXT input_params['INPUTPH']['drho_star%dir'] = self._FOLDER_DRHO # qpoints part try: mesh,offset = qpoints.get_kpoints_mesh() if any([i!=0. for i in offset]): raise NotImplementedError("Computation of phonons on a mesh with" " non zero offset is not implemented, at the level of ph.x") input_params["INPUTPH"]["ldisp"] = True input_params["INPUTPH"]["nq1"] = mesh[0] input_params["INPUTPH"]["nq2"] = mesh[1] input_params["INPUTPH"]["nq3"] = mesh[2] postpend_text = None except AttributeError: # this is the case where no mesh was set. Maybe it's a list try: list_of_points = qpoints.get_kpoints(cartesian=True) except AttributeError as e: # In this case, there are no info on the qpoints at all raise InputValidationError("Neither a qpoints mesh or a valid " "list of qpoints was found in input", e.message) # change to 2pi/a coordinates lattice_parameter = numpy.linalg.norm(qpoints.cell[0]) list_of_points *= lattice_parameter / (2.*numpy.pi) # add here the list of point coordinates if len(list_of_points)>1: input_params["INPUTPH"]["qplot"] = True input_params["INPUTPH"]["ldisp"] = True postpend_text = "{}\n".format(len(list_of_points)) for points in list_of_points: postpend_text += "{} {} {} 1\n".format(*points) # Note: the weight is fixed to 1, because ph.x calls these # things weights but they are not such. If they are going to # exist with the meaning of weights, they will be supported else: input_params["INPUTPH"]["ldisp"] = False postpend_text = "" for points in list_of_points: postpend_text += "{} {} {}\n".format(*points) # =================== NAMELISTS ======================== # customized namelists, otherwise not present in the distributed ph code try: namelists_toprint = settings_dict.pop('NAMELISTS') if not isinstance(namelists_toprint, list): raise InputValidationError( "The 'NAMELISTS' value, if specified in the settings input " "node, must be a list of strings") except KeyError: # list of namelists not specified in the settings; do automatic detection namelists_toprint = self._compulsory_namelists input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME) # create a folder for the dynamical matrices if not restart_flag: # if it is a restart, it will be copied over tempfolder.get_subfolder(self._FOLDER_DYNAMICAL_MATRIX, create=True) with open(input_filename,'w') as infile: infile.write('AiiDA calculation\n') for namelist_name in namelists_toprint: infile.write("&{0}\n".format(namelist_name)) # namelist content; set to {} if not present, so that we leave an # empty namelist namelist = input_params.pop(namelist_name,{}) for k, v in sorted(namelist.iteritems()): infile.write(get_input_data_text(k,v)) infile.write("/\n") # add list of qpoints if required if postpend_text is not None: infile.write(postpend_text) #TODO: write nat_todo if input_params: raise InputValidationError( "The following namelists are specified in input_params, but are " "not valid namelists for the current type of calculation: " "{}".format(",".join(input_params.keys()))) # copy the parent scratch symlink = settings_dict.pop('PARENT_FOLDER_SYMLINK', _default_symlink_usage) # a boolean if symlink: # I create a symlink to each file/folder in the parent ./out tempfolder.get_subfolder(self._OUTPUT_SUBFOLDER, create=True) remote_symlink_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), parent_calc_out_subfolder, "*"), self._OUTPUT_SUBFOLDER ) ) # I also create a symlink for the ./pseudo folder # TODO: suppress this when the recover option of QE will be fixed # (bug when trying to find pseudo file) remote_symlink_list.append((parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), self._get_pseudo_folder()), self._get_pseudo_folder() )) #pass else: # here I copy the whole folder ./out remote_copy_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), parent_calc_out_subfolder), self._OUTPUT_SUBFOLDER)) # I also copy the ./pseudo folder # TODO: suppress this when the recover option of QE will be fixed # (bug when trying to find pseudo file) remote_copy_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), self._get_pseudo_folder()), self._get_pseudo_folder())) if restart_flag: # in this case, copy in addition also the dynamical matrices if symlink: remote_symlink_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), self._FOLDER_DYNAMICAL_MATRIX), self._FOLDER_DYNAMICAL_MATRIX)) else: # copy the dynamical matrices remote_copy_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), self._FOLDER_DYNAMICAL_MATRIX), '.')) # no need to copy the _ph0, since I copied already the whole ./out folder # here we may create an aiida.EXIT file create_exit_file = settings_dict.pop('ONLY_INITIALIZATION',False) if create_exit_file: exit_filename = tempfolder.get_abs_path( '{}.EXIT'.format(self._PREFIX)) with open(exit_filename,'w') as f: f.write('\n') calcinfo = CalcInfo() calcinfo.uuid = self.uuid # Empty command line by default cmdline_params = settings_dict.pop('CMDLINE', []) calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list calcinfo.remote_symlink_list = remote_symlink_list codeinfo = CodeInfo() codeinfo.cmdline_params = (list(cmdline_params) + ["-in", self._INPUT_FILE_NAME]) codeinfo.stdout_name = self._OUTPUT_FILE_NAME codeinfo.code_uuid = code.uuid calcinfo.codes_info = [codeinfo] # Retrieve by default the output file and the xml file calcinfo.retrieve_list = [] calcinfo.retrieve_list.append(self._OUTPUT_FILE_NAME) calcinfo.retrieve_list.append(self._FOLDER_DYNAMICAL_MATRIX) calcinfo.retrieve_list.append( os.path.join(self._OUTPUT_SUBFOLDER, '_ph0', '{}.phsave'.format(self._PREFIX), self._OUTPUT_XML_TENSOR_FILE_NAME)) extra_retrieved = settings_dict.pop('ADDITIONAL_RETRIEVE_LIST', []) for extra in extra_retrieved: calcinfo.retrieve_list.append( extra ) if settings_dict: 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 upload_basisset_family(cls, folder, group_name, group_description, stop_if_existing=True, extension=".basis", dry_run=False): """ Upload a set of Basis Set files in a given group. :param folder: a path containing all Basis Set files to be added. Only files ending in the set extension (case-insensitive) are considered. :param group_name: the name of the group to create. If it exists and is non-empty, a UniquenessError is raised. :param group_description: a string to be set as the group description. Overwrites previous descriptions, if the group was existing. :param stop_if_existing: if True, check for the md5 of the files and, if the file already exists in the DB, raises a MultipleObjectsError. If False, simply adds the existing BasisSetData node to the group. :param extension: the filename extension to look for :param dry_run: If True, do not change the database. """ from aiida.common import aiidalogger from aiida.orm import Group from aiida.common.exceptions import UniquenessError, NotExistent from aiida_crystal17.aiida_compatability import get_automatic_user automatic_user = get_automatic_user() if not os.path.isdir(folder): raise ValueError("folder must be a directory") # only files, and only those ending with specified exension; # go to the real file if it is a symlink files = [ os.path.realpath(os.path.join(folder, i)) for i in os.listdir(folder) if os.path.isfile(os.path.join(folder, i)) and i.lower().endswith(extension) ] nfiles = len(files) try: group = Group.get(name=group_name, type_string=BASISGROUP_TYPE) group_created = False except NotExistent: group = Group(name=group_name, type_string=BASISGROUP_TYPE, user=automatic_user) group_created = True if group.user.email != automatic_user.email: raise UniquenessError( "There is already a BasisFamily group with name {}" ", but it belongs to user {}, therefore you " "cannot modify it".format(group_name, group.user.email)) # Always update description, even if the group already existed group.description = group_description # NOTE: GROUP SAVED ONLY AFTER CHECKS OF UNICITY basis_and_created = _retrieve_basis_sets(files, stop_if_existing) # check whether basisset are unique per element elements = [(i[0].element, i[0].md5sum) for i in basis_and_created] # If group already exists, check also that I am not inserting more than # once the same element if not group_created: for aiida_n in group.nodes: # Skip non-basis sets if not isinstance(aiida_n, BasisSetData): continue elements.append((aiida_n.element, aiida_n.md5sum)) elements = set( elements) # Discard elements with the same MD5, that would # not be stored twice elements_names = [e[0] for e in elements] if not len(elements_names) == len(set(elements_names)): duplicates = set( [x for x in elements_names if elements_names.count(x) > 1]) duplicates_string = ", ".join(i for i in duplicates) raise UniquenessError( "More than one Basis found for the elements: " + duplicates_string + ".") # At this point, save the group, if still unstored if group_created and not dry_run: group.store() # save the basis set in the database, and add them to group for basisset, created in basis_and_created: if created: if not dry_run: basisset.store() aiidalogger.debug("New node {0} created for file {1}".format( # pylint: disable=logging-format-interpolation basisset.uuid, basisset.filename)) else: aiidalogger.debug("Reusing node {0} for file {1}".format( # pylint: disable=logging-format-interpolation basisset.uuid, basisset.filename)) # Add elements to the group all together if not dry_run: group.add_nodes(basis for basis, created in basis_and_created) nuploaded = len([_ for _, created in basis_and_created if created]) return nfiles, nuploaded
def _prepare_for_submission(self, tempfolder, inputdict): """ This is the routine to be called when you make a fleur calculation Here should be checked if all the files are there to run fleur. And input files (inp.xml) can be modified. :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 local_copy_list = [] remote_copy_list = [] remote_symlink_list = [] mode_retrieved_filelist = [] #filelocal_copy_list = [] #filelist_tocopy = [] filelist_tocopy_remote = [] settings_dict = {} #fleur_calc = False #new_inp_file = False #ignore_mode = False has_fleurinp = False has_parent = False #restart_flag = False fleurinpgen = False copy_remotely = True ########################################## ############# INPUT CHECK ################ ########################################## try: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError( "No code specified for this calculation") # a Fleur calc can be created from a fleurinpData alone #(then no parent is needed) all files are in the repo, but usually it is # a child of a inpgen calc or an other fleur calc (some or all files are # in a remote source). if the User has not changed something, the #calculation does not need theoretical a new FleurinpData it could use #the one from the parent, but the plug-in desgin is in a way that it has # to be there and it just copies files if changes occured.. fleurinp = inputdict.pop(self.get_linkname('fleurinpdata'), None) if fleurinp is None: #xml_inp_dict = {} has_fleurinp = False else: if not isinstance(fleurinp, FleurinpData): raise InputValidationError( "The FleurinpData node given is not of type FleurinpData.") has_fleurinp = True parent_calc_folder = inputdict.pop(self.get_linkname('parent_folder'), None) #print parent_calc_folder if parent_calc_folder is None: has_parent = False if not has_fleurinp: raise InputValidationError( "No parent calculation found and no fleurinp data " "given, need either one or both for a " "'fleurcalculation'.") else: # if not isinstance(parent_calc_folder, RemoteData): raise InputValidationError("parent_calc_folder, if specified," "must be of type RemoteData") # extract parent calculation parent_calcs = parent_calc_folder.get_inputs( node_type=JobCalculation) n_parents = len(parent_calcs) if n_parents != 1: raise UniquenessError( "Input RemoteData is child of {} " "calculation{}, while it should have a single parent" "".format(n_parents, "" if n_parents == 0 else "s")) parent_calc = parent_calcs[0] has_parent = True #print parent_calc # check that it is a valid parent #self._check_valid_parent(parent_calc) # if inpgen calc do # check if folder from db given, or get folder from rep. # Parent calc does not has to be on the same computer. if isinstance(parent_calc, FleurCalculation): new_comp = self.get_computer() old_comp = parent_calc.get_computer() if new_comp.uuid != old_comp.uuid: #dont copy files, copy files localy copy_remotely = False #raise InputValidationError( # "FleurCalculation must be launched on the same computer" # " of the parent: {}".format(old_comp.get_name())) elif isinstance(parent_calc, FleurinputgenCalculation): fleurinpgen = True new_comp = self.get_computer() old_comp = parent_calc.get_computer() if new_comp.uuid != old_comp.uuid: #dont copy files, copy files localy copy_remotely = False else: raise InputValidationError( "parent_calc, must be either an 'inpgen calculation' or" " a 'fleur 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)) #print settings_dict # Here, there should be no other inputs if inputdict: raise InputValidationError("The following input data nodes are " "unrecognized: {}".format( inputdict.keys())) #TODO: Detailed check of FleurinpData # if certain files are there in fleurinpData. # from where to copy ############################## # END OF INITIAL INPUT CHECK # # file copy stuff TODO check in fleur input if has_fleurinp: self._DEFAULT_INPUT_FILE = fleurinp.get_file_abs_path( self._INPXML_FILE_NAME) #local_copy_list.append(( # fleurinp.get_file_abs_path(self._INPXML_FILE_NAME), # self._INPXML_FILE_NAME)) #copy ALL files from inp.xml allfiles = fleurinp.files for file1 in allfiles: local_copy_list.append( (fleurinp.get_file_abs_path(file1), file1)) modes = fleurinp.get_fleur_modes() # add files to mode_retrieved_filelist if modes['band']: mode_retrieved_filelist.append(self._BAND_FILE_NAME) mode_retrieved_filelist.append(self._BAND_GNU_FILE_NAME) if modes['dos']: mode_retrieved_filelist.append(self._DOS_FILE_NAME) if modes['forces']: print 'FORCES!!!' mode_retrieved_filelist.append(self._NEW_XMlINP_FILE_NAME) mode_retrieved_filelist.append(self._FORCE_FILE_NAME) if modes['ldau']: mode_retrieved_filelist.append(self._NMMPMAT_FILE_NAME) #if noco, ldau, gw... # TODO: check from where it was copied, and copy files of its parent # if needed #self.logger.info("@@@@@@@@@@@@@@@@@@@@@@@@has_parent {}".format(has_parent)) if has_parent: # copy the right files #TODO check first if file, exist and throw # warning, now this will throw an error outfolderpath = parent_calc.out.retrieved.folder.abspath self.logger.info("out folder path {}".format(outfolderpath)) #print outfolderpath if fleurinpgen and (not has_fleurinp): for file1 in self._copy_filelist_inpgen: local_copy_list.append( (os.path.join(outfolderpath, 'path', file1), os.path.join(file1))) elif not fleurinpgen and (not has_fleurinp): # fleurCalc for file1 in self._copy_filelist_scf: local_copy_list.append( (os.path.join(outfolderpath, 'path', file1), os.path.join(file1))) filelist_tocopy_remote = filelist_tocopy_remote # + self._copy_filelist_scf_remote #TODO get inp.xml from parent fleurinpdata, since otherwise it will be doubled in repo elif fleurinpgen and has_fleurinp: # everything is taken care of pass elif not fleurinpgen and has_fleurinp: # input file is already taken care of for file1 in self._copy_filelist_scf1: local_copy_list.append( (os.path.join(outfolderpath, 'path', file1), os.path.join(file1))) filelist_tocopy_remote = filelist_tocopy_remote # + self._copy_filelist_scf_remote # TODO not on same computer -> copy needed files from repository, # if they are not there, throw error if copy_remotely: # on same computer. #print('copy files remotely') # from fleurmodes if modes['pot8']: filelist_tocopy_remote = filelist_tocopy_remote + self._copy_filelist_scf_remote filelist_tocopy_remote.append(self._POT_FILE_NAME) # #filelist_tocopy_remote.append(self._POT2_FILE_NAME) elif modes['dos']: pass elif modes['band']: pass else: filelist_tocopy_remote = filelist_tocopy_remote + self._copy_filelist_scf_remote # from settings, user specified #TODO check if list? for file1 in settings_dict.get('additional_remotecopy_list', []): filelist_tocopy_remote.append(file1) for file1 in settings_dict.get('remove_from_remotecopy_list', []): if file1 in filelist_tocopy_remote: filelist_tocopy_remote.remove(file1) for file1 in filelist_tocopy_remote: remote_copy_list.append( (parent_calc_folder.get_computer().uuid, os.path.join(parent_calc_folder.get_remote_path(), file1), self._OUTPUT_FOLDER)) #print remote_copy_list #self.logger.info("remote copy file list {}".format(remote_copy_list)) ########## MAKE CALCINFO ########### calcinfo = CalcInfo() calcinfo.uuid = self.uuid # Empty command line by default #cmdline_params = settings_dict.pop('CMDLINE', []) #calcinfo.cmdline_params = (list(cmdline_params) # + ["-in", self._INPUT_FILE_NAME]) #print local_copy_list self.logger.info("local copy file list {}".format(local_copy_list)) calcinfo.local_copy_list = local_copy_list calcinfo.remote_copy_list = remote_copy_list #(remotemachinename, remoteabspath, relativedestpath) calcinfo.remote_symlink_list = remote_symlink_list #calcinfo.stdout_name = self._OUTPUT_FILE_NAME # Retrieve by default the output file and the xml file retrieve_list = [] retrieve_list.append(self._OUTXML_FILE_NAME) retrieve_list.append(self._INPXML_FILE_NAME) #calcinfo.retrieve_list.append(self._OUTPUT_FILE_NAME) retrieve_list.append(self._SHELLOUTPUT_FILE_NAME) retrieve_list.append(self._CDN1_FILE_NAME) retrieve_list.append(self._ERROR_FILE_NAME) #calcinfo.retrieve_list.append(self._TIME_INFO_FILE_NAME) retrieve_list.append(self._OUT_FILE_NAME) #calcinfo.retrieve_list.append(self._INP_FILE_NAME) #calcinfo.retrieve_list.append(self._ENPARA_FILE_NAME) #calcinfo.retrieve_list.append(self._SYMOUT_FILE_NAME) #calcinfo.retrieve_list.append(self._KPTS_FILE_NAME) # if certain things are modefied, flags set, #other files should be retrieved, example DOS.x... #print "mode_retrieved_filelist", repr(mode_retrieved_filelist) for mode_file in mode_retrieved_filelist: retrieve_list.append(mode_file) #print('retrieve_list: {}'.format(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() # should look like: codepath -xmlInput < inp.xml > shell.out 2>&1 walltime_sec = self.get_max_wallclock_seconds() #self.logger.info("!!!!!!!!!!!!!!!!!!! walltime_sec : {}" # "".format(walltime_sec)) cmdline_params = ["-xml"] #, "-wtime", "{}".format(walltime_sec)] #walltime_sec = self.get_max_wallclock_seconds() #print('walltime: {}'.format(walltime_sec)) if walltime_sec: walltime_min = max(1, walltime_sec / 60) cmdline_params.append("-wtime") cmdline_params.append("{}".format(walltime_min)) # user specific commandline_options for command in settings_dict.get('cmdline', []): cmdline_params.append(command) codeinfo.cmdline_params = list(cmdline_params) # + ["<", self._INPXML_FILE_NAME, # ">", self._SHELLOUTPUT_FILE_NAME, "2>&1"] codeinfo.code_uuid = code.uuid codeinfo.withmpi = self.get_withmpi() codeinfo.stdin_name = self._INPUT_FILE_NAME codeinfo.stdout_name = self._SHELLOUTPUT_FILE_NAME #codeinfo.join_files = True codeinfo.stderr_name = self._ERROR_FILE_NAME calcinfo.codes_info = [codeinfo] ''' # not needed in new version if fleurinpgen:# execute twice, as long start density stop codeinfo1 = CodeInfo() cmdline_params = ["-xmlInput"] codeinfo1.cmdline_params = list(cmdline_params) # + ["<", self._INPUT_FILE_NAME])#, # ">",self._OUTPUT_FILE_NAME] codeinfo1.code_uuid = code.uuid codeinfo1.withmpi = self.get_withmpi() codeinfo1.stdin_name = self._INPUT_FILE_NAME codeinfo1.stdout_name = self._SHELLOUTPUT_FILE_NAME calcinfo.codes_info.append(codeinfo1) if settings_dict: 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 upload_upf_family(folder, group_label, group_description, stop_if_existing=True): """Upload a set of UPF files in a given group. :param folder: a path containing all UPF files to be added. Only files ending in .UPF (case-insensitive) are considered. :param group_label: the name of the group to create. If it exists and is non-empty, a UniquenessError is raised. :param group_description: string to be set as the group description. Overwrites previous descriptions. :param stop_if_existing: if True, check for the md5 of the files and, if the file already exists in the DB, raises a MultipleObjectsError. If False, simply adds the existing UPFData node to the group. """ # pylint: disable=too-many-locals,too-many-branches import os from aiida import orm from aiida.common import AIIDA_LOGGER from aiida.common.exceptions import UniquenessError from aiida.common.files import md5_file if not os.path.isdir(folder): raise ValueError('folder must be a directory') # only files, and only those ending with .upf or .UPF; # go to the real file if it is a symlink filenames = [ os.path.realpath(os.path.join(folder, i)) for i in os.listdir(folder) if os.path.isfile(os.path.join(folder, i)) and i.lower().endswith('.upf') ] nfiles = len(filenames) automatic_user = orm.User.objects.get_default() group, group_created = orm.Group.objects.get_or_create( label=group_label, type_string=UPFGROUP_TYPE, user=automatic_user) if group.user.email != automatic_user.email: raise UniquenessError( 'There is already a UpfFamily group with label {}' ', but it belongs to user {}, therefore you ' 'cannot modify it'.format(group_label, group.user.email)) # Always update description, even if the group already existed group.description = group_description # NOTE: GROUP SAVED ONLY AFTER CHECKS OF UNICITY pseudo_and_created = [] for filename in filenames: md5sum = md5_file(filename) builder = orm.QueryBuilder() builder.append(UpfData, filters={'attributes.md5': {'==': md5sum}}) existing_upf = builder.first() if existing_upf is None: # return the upfdata instances, not stored pseudo, created = UpfData.get_or_create(filename, use_first=True, store_upf=False) # to check whether only one upf per element exists # NOTE: actually, created has the meaning of "to_be_created" pseudo_and_created.append((pseudo, created)) else: if stop_if_existing: raise ValueError('A UPF with identical MD5 to ' ' {} cannot be added with stop_if_existing' ''.format(filename)) existing_upf = existing_upf[0] pseudo_and_created.append((existing_upf, False)) # check whether pseudo are unique per element elements = [(i[0].element, i[0].md5sum) for i in pseudo_and_created] # If group already exists, check also that I am not inserting more than # once the same element if not group_created: for aiida_n in group.nodes: # Skip non-pseudos if not isinstance(aiida_n, UpfData): continue elements.append((aiida_n.element, aiida_n.md5sum)) elements = set(elements) # Discard elements with the same MD5, that would # not be stored twice elements_names = [e[0] for e in elements] if not len(elements_names) == len(set(elements_names)): duplicates = {x for x in elements_names if elements_names.count(x) > 1} duplicates_string = ', '.join(i for i in duplicates) raise UniquenessError('More than one UPF found for the elements: ' + duplicates_string + '.') # At this point, save the group, if still unstored if group_created: group.store() # save the upf in the database, and add them to group for pseudo, created in pseudo_and_created: if created: pseudo.store() AIIDA_LOGGER.debug('New node {} created for file {}'.format( pseudo.uuid, pseudo.filename)) else: AIIDA_LOGGER.debug('Reusing node {} for file {}'.format( pseudo.uuid, pseudo.filename)) # Add elements to the group all togetehr group.add_nodes([pseudo for pseudo, created in pseudo_and_created]) nuploaded = len([_ for _, created in pseudo_and_created if created]) return nfiles, nuploaded
def upload_basisset_family( cls, folder, group_name, group_description, stop_if_existing=True, extension=".basis", dry_run=False, ): """ Upload a set of Basis Set files in a given group. :param folder: a path (str or pathlib.Path) containing all Basis Set files to be added. Only files ending in set extension (case-sensitive) considered :param group_name: the name of the group to create. If it exists and is non-empty, a UniquenessError is raised. :param group_description: a string to be set as the group description. Overwrites previous descriptions, if the group was existing. :param stop_if_existing: if True, check for the md5 of the files and, if file already exists in the DB, raises a MultipleObjectsError. If False, simply adds the existing BasisSetData node to the group. :param extension: the filename extension to look for :param dry_run: If True, do not change the database. """ if isinstance(folder, str): folder = pathlib.Path(folder) if not folder.is_dir(): raise ValueError("folder must be a directory") paths = [ p.resolve() for p in folder.glob("*{}".format(extension)) if p.is_file() ] automatic_user = User.objects.get_default() group, group_created = BasisSetFamily.objects.get_or_create( label=group_name, user=automatic_user ) if group.user.email != automatic_user.email: raise UniquenessError( "There is already a BasisSetFamily group with name {}" ", but it belongs to user {}, therefore you " "cannot modify it".format(group_name, group.user.email) ) # Always update description, even if the group already existed group.description = group_description # NOTE: GROUP SAVED ONLY AFTER CHECKS OF UNICITY basis_and_created = retrieve_basis_sets(paths, stop_if_existing) # check whether basisset are unique per element elements = [(i[0].element, i[0].md5sum) for i in basis_and_created] # If group already exists, check also that I am not inserting more than # once the same element if not group_created: for aiida_n in group.nodes: # Skip non-basis sets if not isinstance(aiida_n, BasisSetData): continue elements.append((aiida_n.element, aiida_n.md5sum)) elements = set(elements) # Discard elements with the same MD5, that would # not be stored twice elements_names = [e[0] for e in elements] if not len(elements_names) == len(set(elements_names)): duplicates = set([x for x in elements_names if elements_names.count(x) > 1]) duplicates_string = ", ".join(i for i in duplicates) raise UniquenessError( ("More than one Basis found for the elements: " "{}").format( duplicates_string ) ) # At this point, save the group, if still unstored if group_created and not dry_run: group.store() # save the basis set in the database, and add them to group for basisset, created in basis_and_created: if created and not dry_run: basisset.store() # Add elements to the group all together if not dry_run: group.add_nodes([basis for basis, created in basis_and_created]) nuploaded = len([_ for _, created in basis_and_created if created]) return len(paths), nuploaded