def validate_parameters(param_data, potential_object) -> bool: """Validate the inputs for an optimization calculation. :param param_data: input parameters for the optimization calculations :type param_data: orm.Dict :param potential_object: LAMMPS potential :type potential_object: EmpiricalPotential :raises InputValidationError: if there is no parameters data passed :raises InputValidationError: if the units of the parameters and the potential are different. :return: whether the parameters are valid or not :rtype: bool """ if param_data is None: raise InputValidationError('parameter data not set') validate_against_schema(param_data.get_dict(), 'optimize.schema.json') # ensure the potential and paramters are in the same unit systems # TODO convert between unit systems (e.g. using https://pint.readthedocs.io) if 'units' in param_data.get_dict(): punits = param_data.get_dict()['units'] if not punits == potential_object.default_units: raise InputValidationError( f'the units of the parameters ({punits}) and potential ' f'({potential_object.default_units}) are different') return True
def _get_following_text(self, inputdict, settings): """ Add the kpoints after the namelist. This function should consume the content of inputdict (if it requires a different node) or the keys inside settings, using the 'pop' method, so that inputdict and settings should remain empty at the end of _prepare_for_submission, if all flags/nodes were recognized """ from aiida.common.exceptions import InputValidationError try: kpoints = inputdict.pop(self.get_linkname('kpoints')) except KeyError: raise InputValidationError( "No kpoints specified for this calculation") if not isinstance(kpoints, KpointsData): raise InputValidationError("kpoints is not of type KpointsData") try: klist = kpoints.get_kpoints() except AttributeError: klist = kpoints.get_kpoints_mesh(print_list=True) retlist = ["{}".format(len(klist))] for k in klist: retlist.append("{:18.10f} {:18.10f} {:18.10f}".format(*k)) return "\n".join(retlist) + "\n"
def _check_and_extract_input_nodes(self, tempfolder): """ Extract input nodes from inputdict and check consitency of input nodes :param inputdict: dict of inputnodes :returns: * parameters (aiida_kkr.tools.kkr_params.kkrparams), optional: parameters of KKRimp that end up in config.cfg * code (KKRimpCodeNode): code of KKRimp on some machine * imp_info (DictNode): parameter node of the impurity information, extracted from host_parent_calc * kkrflex_file_paths (dict): dictionary of {filenames: absolute_path_to_file} for the kkrflex-files * shapfun_path (str): absolute path of the shapefunction of the host parent calculation * host_parent_calc (KkrCalculation): node of the parent host calculation where the kkrflex-files were created * impurity_potential (SinglefileData): single file data node containing the starting potential for the impurity calculation * parent_calc_folder (RemoteData): remote directory of a parent KKRimp calculation """ # get mandatory input nodes (extract code) code = self.inputs.code # now check for optional nodes if 'parameters' in self.inputs: parameters = self.inputs.parameters else: parameters = None if parameters is not None: # convert to kkrparams instance parameters = kkrparams(params_type='kkrimp', **parameters.get_dict()) # get hostfiles imp_info, kkrflex_file_paths, shapfun_path, shapes, host_parent_calc, params_host, structure = self._get_and_verify_hostfiles( tempfolder) # check impurity potential or parent calculation input # impurity_potential if 'impurity_potential' in self.inputs: impurity_potential = self.inputs.impurity_potential found_imp_pot = True else: impurity_potential = None found_imp_pot = False # parent calculation folder if 'parent_calc_folder' in self.inputs: parent_calc_folder = self.inputs.parent_calc_folder found_parent_calc = True else: parent_calc_folder = None found_parent_calc = False # consistency checks if not found_parent_calc and not found_imp_pot: raise InputValidationError( "Neither impurity_potential nor parent_calc_folder specified for this calculation.\n" "Please provide either impurity_potential or parent_calc_folder." ) elif found_parent_calc and found_imp_pot: raise InputValidationError( "Both impurity_potential and parent_calc_folder specified for this calculation.\n" "Please provide one one, i.e. either impurity_potential or parent_calc_folder." ) # Done checking inputs, returning... return parameters, code, imp_info, kkrflex_file_paths, shapfun_path, shapes, host_parent_calc, params_host, impurity_potential, parent_calc_folder, structure
def _retrieve_basis_sets(self, inputdict, instruct): """ retrieve BasisSetData objects from the inputdict, associate them with an atomic element and validate a 1-to-1 mapping between the two :param inputdict: dictionary of inputs :param instruct: input StructureData :return: basissets dict {element: BasisSetData} """ basissets = {} # I create here a dictionary that associates each kind name to a basisset for link in inputdict.keys(): if link.startswith(self._get_linkname_basisset_prefix()): element = link[len(self._get_linkname_basisset_prefix()):] the_basisset = inputdict.pop(link) if not isinstance(the_basisset, BasisSetData): raise InputValidationError( "basisset for element '{}' is not of " "type BasisSetData".format(element)) basissets[element] = the_basisset # Check retrieved elements match the required elements elements_required = [k.symbol for k in instruct.kinds] if set(elements_required) != set(basissets.keys()): err_msg = ( "Mismatch between the defined basissets and the list of " "elements of the structure. Basissets: {}; elements: {}". format(",".join(basissets.keys()), ",".join( list(elements_required)))) raise InputValidationError(err_msg) return basissets
def get_name_from_quantum_numbers(cls, angular_momentum, magnetic_number=None): """ Returns the orbital_name correponding to the angular_momentum alone, or to both angular_number with magnetic_number. For example using angular_momentum=1 and magnetic_number=1 will return "Px" """ # importing a copy of the conversion_dict convert_ref = copy.deepcopy(conversion_dict) orbital_name = [x for x in convert_ref if any([convert_ref[x][y]['angular_momentum'] == angular_momentum for y in convert_ref[x]])] if len(orbital_name) == 0: raise InputValidationError("No orbital name corresponding to the " "angular_momentum {} could be found".format(angular_momentum)) if magnetic_number is not None: # finds angu orbital_name = orbital_name[0] orbital_name = [x for x in convert_ref[orbital_name] if convert_ref[orbital_name][x]['magnetic_number'] == magnetic_number] if len(orbital_name) == 0: raise InputValidationError("No orbital name corresponding to " "the magnetic_number {} could be " "found".format(magnetic_number)) return orbital_name[0]
def _validate_inputs(self, inputdict): """ Validate input links. """ # Check code try: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError("No code specified for this " "calculation") # Check input files try: surface_sample = inputdict.pop(self.get_linkname('surface_sample')) if not isinstance(surface_sample, SinglefileData): raise InputValidationError( "surface_sample not of type SinglefileData") except KeyError: raise InputValidationError( "No input structure specified for calculation") try: cell = inputdict.pop(self.get_linkname('cell')) if not isinstance(cell, SinglefileData): raise InputValidationError("cell not of type SinglefileData") except KeyError: raise InputValidationError( "No input structure specified for calculation") # Check that nothing is left unparsed if inputdict: raise ValidationError("Unrecognized inputs: {}".format(inputdict)) return code, surface_sample, cell
def _get_validated_parameters_dict(self, parameters): param_dict_raw = parameters.get_dict() # keys to lowercase, check for duplicates param_dict = { key.lower(): value for key, value in param_dict_raw.items() } if len(param_dict) != len(param_dict_raw): counter = Counter([k.lower() for k in param_dict_raw]) counter = {key: val for key, val in counter if val > 1} raise InputValidationError( 'The following keys were found more than once in the parameters: {}. Check for duplicates written in upper- / lowercase.' .format(counter)) # check for blocked keywords existing_blocked_keys = [] for key in self._blocked_parameter_keys: if key in param_dict: existing_blocked_keys.append(key) if existing_blocked_keys: raise InputValidationError( 'The following blocked keys were found in the parameters: {}'. format(existing_blocked_keys)) return param_dict
def build_response(status=200, headers=None, data=None): """ :param status: status of the response, e.g. 200=OK, 400=bad request :param headers: dictionary for additional header k,v pairs, e.g. X-total-count=<number of rows resulting from query> :param data: a dictionary with the data returned by the Resource :return: a Flask response object """ ## Type checks # mandatory parameters if not isinstance(data, dict): raise InputValidationError("data must be a dictionary") # non-mandatory parameters if status is not None: try: status = int(status) except ValueError: raise InputValidationError("status must be an integer") if headers is not None and not isinstance(headers, dict): raise InputValidationError("header must be a dictionary") # Build response response = jsonify(data) response.status_code = status if headers is not None: for k, v in headers.iteritems(): response.headers[k] = v return response
def get_name_from_quantum_numbers(cls, angular_momentum, magnetic_number=None): """ Returns the orbital_name correponding to the angular_momentum alone, or to both angular_number with magnetic_number. For example using angular_momentum=1 and magnetic_number=1 will return "Px" """ orbital_name = [ x for x in CONVERSION_DICT if any([CONVERSION_DICT[x][y]['angular_momentum'] == angular_momentum for y in CONVERSION_DICT[x]]) ] if not orbital_name: raise InputValidationError( 'No orbital name corresponding to the ' 'angular_momentum {} could be found'.format(angular_momentum) ) if magnetic_number is not None: # finds angular momentum orbital_name = orbital_name[0] orbital_name = [ x for x in CONVERSION_DICT[orbital_name] if CONVERSION_DICT[orbital_name][x]['magnetic_number'] == magnetic_number ] if not orbital_name: raise InputValidationError( 'No orbital name corresponding to ' 'the magnetic_number {} could be ' 'found'.format(magnetic_number) ) return orbital_name[0]
def finilize_cross_results(cross_data, gap_threshold): """Analyze the final result of kpt-cross calculation, and return valid crossings.""" if not isinstance(cross_data, orm.ArrayData): raise InputValidationError( 'Invalide type {} for parameter `cross_data`'.format( type(cross_data))) if not isinstance(gap_threshold, orm.Float): raise InputValidationError( 'Invalide type {} for parameter `gap_threshold`'.format( type(gap_threshold))) gap_thr = gap_threshold.value kpts = cross_data.get_array('kpoints') gaps = cross_data.get_array('gaps') w1 = np.where(gaps <= gap_thr)[0] w2 = np.where(gaps > gap_thr)[0] crossings = kpts[w1, :] low_gap = kpts[w2, :] res = orm.ArrayData() res.set_array('crossings', crossings) res.set_array('cr_gaps', gaps[w1]) res.set_array('low_gap', low_gap) res.set_array('cr_lg', gaps[w2]) return res
def start(self): # for kpoints, we will need to have the scf step, and use YamboWorkflow not YamboRestartWf # self.ctx.max_iterations = 20 self.ctx.iteration = 0 self.ctx.skip_prescf = False self.ctx.very_first = True convergence_parameters_dict = self.inputs.convergence_parameters.get_dict( ) # Mandatory inputs try: self.ctx.variable_to_converge = convergence_parameters_dict[ 'variable_to_converge'] except KeyError: raise InputValidationError( 'variable_to_converge not defined in input!') try: self.ctx.start_value = convergence_parameters_dict['start_value'] except KeyError: raise InputValidationError('start_value not defined in input!') try: self.ctx.step = convergence_parameters_dict['step'] except KeyError: raise InputValidationError('step not defined in input!') try: self.ctx.max_value = convergence_parameters_dict['max_value'] except KeyError: raise InputValidationError('max_value not defined in input!') try: self.ctx.conv_tol = convergence_parameters_dict['conv_tol'] except KeyError: raise InputValidationError('conv_tol not defined in input!') # Optional inputs try: self.ctx.conv_window = convergence_parameters_dict['conv_window'] except KeyError: self.ctx.conv_window = 3 try: self.ctx.loop_length = convergence_parameters_dict['loop_length'] except KeyError: self.ctx.loop_length = 4 self.ctx.distance_kpoints = self.ctx.start_value self.ctx.en_diffs = [] if self.ctx.variable_to_converge == 'bands': self.ctx.conv_elem = {'BndsRnXp': [], 'GbndRnge': []} elif self.ctx.variable_to_converge == 'W_cutoff': self.ctx.conv_elem = {'NGsBlkXp': []} elif self.ctx.variable_to_converge == 'FFT_cutoff': self.ctx.conv_elem = {'FFTGvecs': []} elif self.ctx.variable_to_converge == 'kpoints': self.ctx.conv_elem = {'kpoints': []} else: self.ctx.conv_elem = {self.ctx.variable_to_converge: []} self.report( 'WARNING: the variable to converge is {}, not recognized but I try anyway to converge it' ) self.report("Setup step completed.")
def generate_cubic_grid(structure, centers, distance, dim): """Generate a cubic grids centered in `centers` of size `distance` and dimensionality `dim`. :param structure: aiida.orm.StructureData node used to get the cell of the material. :param centers: aiida.orm.ArrayData containing an array named `centers`. Each element of `centers` is used to generate a cubic grid around it. :param distance: aiida.orm.Float indicating the lateral size of the cubic grid. :param dim: aiida.orm.Int determining the dimensionality of the grid. e.g.: dim=1 -> 5x1x1 dim = 2 -> 5x5x1 dim = 3 -> 5x5x5 :return: aiida.orm.KpointsData containing the generated grids. """ if not isinstance(structure, orm.StructureData): raise InputValidationError( 'Invalide type {} for parameter `structure`'.format( type(structure))) if not isinstance(centers, orm.ArrayData): raise InputValidationError( 'Invalide type {} for parameter `centers`'.format(type(centers))) if not isinstance(distance, orm.Float): raise InputValidationError( 'Invalide type {} for parameter `distance`'.format(type(distance))) npoints = 5 centers = centers.get_array('pinned') dist = distance.value / (npoints - 1) dim = dim.value # yapf: disable l = np.arange(-(npoints-1)//2, (npoints-1)//2 + 1) + ((npoints + 1)%2) * 0.5 lx = l ly = l if dim > 1 else [0,] lz = l if dim > 2 else [0,] grid = np.array(list(product(lx, ly, lz))) * dist res = np.empty((0,3)) for n,c in enumerate(centers): new = c + grid if n == 0: attach = new else: old_tree = KDTree(res) new_tree = KDTree(new) query = new_tree.query_ball_tree(old_tree, r=dist*1.74) attach = np.array([new[n] for n,q in enumerate(query) if not q]) if len(attach): res = np.vstack((res, attach)) kpt = orm.KpointsData() kpt.set_cell_from_structure(structure) kpt.set_kpoints(res, cartesian=True) return kpt
def _generate_NEBinputdata(self,neb_parameters,settings_dict): """ This methods generate the input data for the NEB part of the calculation """ # 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(neb_parameters.get_dict(), dict_name='parameters') input_params = {k: _lowercase_dict(v, dict_name=k) for k, v in input_params.iteritems()} # For the neb input there is no blocked keyword # Create an empty dictionary for the compulsory namelist 'PATH' # if not present if 'PATH' not in input_params: input_params['PATH'] = {} # In case of climbing image, we need the corresponding card climbing_image = False if input_params['PATH'].get('ci_scheme','no-ci').lower() in ['manual']: climbing_image = True try: climbing_image_list = settings_dict.pop("CLIMBING_IMAGES") except KeyError: raise InputValidationError("No climbing image specified for this calculation") if not isinstance(climbing_image_list, list): raise InputValidationError("Climbing images should be provided as a list") if [ i for i in climbing_image_list if i<2 or i >= input_params['PATH'].get('num_of_images',2)]: raise InputValidationError("The climbing images should be in the range between the first " "and the last image") climbing_image_card = "CLIMBING_IMAGES\n" climbing_image_card += ", ".join([str(_) for _ in climbing_image_list]) + "\n" inputfile = "" inputfile += "&PATH\n" # namelist content; set to {} if not present, so that we leave an # empty namelist namelist = input_params.pop('PATH', {}) for k, v in sorted(namelist.iteritems()): inputfile += convert_input_to_namelist_entry(k, v) inputfile += "/\n" # Write cards now if climbing_image: inputfile += climbing_image_card 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()))) return inputfile
def validate_input(self): """ # validate input and find out which path (1, or 2) to take # return True means run voronoi if false run kkr directly """ inputs = self.inputs if 'remote_data' in inputs: input_ok = True else: error = 'ERROR: No remote_data was provided as Input' self.ctx.errors.append(error) self.control_end_wc(error) input_ok = False # extract correct remote folder of last calculation if input remote_folder node is not from KkrCalculation but kkr_scf_wc workflow input_remote = self.inputs.remote_data # check if input_remote has single KkrCalculation parent parents = input_remote.get_inputs(node_type=JobCalculation) nparents = len(parents) if nparents != 1: # extract parent workflow and get uuid of last calc from output node parent_workflow = input_remote.inp.last_RemoteData if not isinstance(parent_workflow, WorkCalculation): raise InputValidationError( "Input remote_data node neither output of a KKR/voronoi calculation nor of kkr_scf_wc workflow" ) parent_workflow_out = parent_workflow.out.output_kkr_scf_wc_ParameterResults uuid_last_calc = parent_workflow_out.get_dict().get( 'last_calc_nodeinfo').get('uuid') last_calc = load_node(uuid_last_calc) if not isinstance(last_calc, KkrCalculation) and not isinstance( last_calc, VoronoiCalculation): raise InputValidationError( "Extracted last_calc node not of type KkrCalculation: check remote_data input node" ) # overwrite remote_data node with extracted remote folder output_remote = last_calc.out.remote_folder self.inputs.remote_data = output_remote if 'kkr' in inputs: try: test_and_get_codenode(inputs.kkr, 'kkr.kkr', use_exceptions=True) except ValueError: error = ("The code you provided for kkr does not " "use the plugin kkr.kkr") self.ctx.errors.append(error) self.control_end_wc(error) input_ok = False # set self.ctx.input_params_KKR self.ctx.input_params_KKR = get_parent_paranode( self.inputs.remote_data) return input_ok
def start(self): """This function performs some neccessary checks to ensure all neccessary information is provided, and stores the provided parameters in self.ctx variables.""" self.ctx.max_iterations = 20 self.ctx.iteration = 0 self.ctx.skip_prescf = False self.ctx.very_first = True convergence_parameters_dict = self.inputs.convergence_parameters.get_dict( ) # Mandatory inputs try: self.ctx.variable_to_converge = convergence_parameters_dict[ 'variable_to_converge'] except KeyError: raise InputValidationError( 'variable_to_converge not defined in input!') try: self.ctx.start_value = convergence_parameters_dict['start_value'] except KeyError: raise InputValidationError('start_value not defined in input!') try: self.ctx.step = convergence_parameters_dict['step'] except KeyError: raise InputValidationError('step not defined in input!') try: self.ctx.max_value = convergence_parameters_dict['max_value'] except KeyError: raise InputValidationError('max_value not defined in input!') try: self.ctx.conv_tol = convergence_parameters_dict['conv_tol'] except KeyError: raise InputValidationError('conv_tol not defined in input!') # Optional inputs try: self.ctx.conv_window = convergence_parameters_dict['conv_window'] except KeyError: self.ctx.conv_window = 3 try: self.ctx.loop_length = convergence_parameters_dict['loop_length'] except KeyError: self.ctx.loop_length = 4 self.ctx.distance_kpoints = self.ctx.start_value self.ctx.en_diffs = [] if self.ctx.variable_to_converge == 'bands': self.ctx.conv_elem = {'BndsRnXp': [], 'GbndRnge': []} elif self.ctx.variable_to_converge == 'W_cutoff': self.ctx.conv_elem = {'NGsBlkXp': []} elif self.ctx.variable_to_converge == 'FFT_cutoff': self.ctx.conv_elem = {'FFTGvecs': []} elif self.ctx.variable_to_converge == 'kpoints': self.ctx.conv_elem = {'kpoints': []} else: self.ctx.conv_elem = {self.ctx.variable_to_converge: []} self.report( 'WARNING: the variable to converge is {}, not recognized but I try anyway to converge it' ) self.report("Setup step completed.")
def generate_kpt_cross(structure, kpoints, step): """Generate a x,y,z cross around each point. :param structure: The StructureData to be used. :param kpoints: The original list of kpoints in crystal coordinates. :param step: The size of the step for the cross. :return: A KpointsData object containing all the kpt generated, including the original ones """ if not isinstance(structure, orm.StructureData): raise InputValidationError( 'Invalide type {} for parameter `structure`'.format( type(structure))) if not isinstance(kpoints, orm.ArrayData): raise InputValidationError( 'Invalide type {} for parameter `kpoints`'.format(type(kpoints))) if not isinstance(step, orm.Float): raise InputValidationError( 'Invalide type {} for parameter `step`'.format(type(step))) try: kpt_cryst = kpoints.get_array('kpoints') except: kpt_cryst = kpoints.get_array('crossings') try: skips = kpoints.get_array('skips') except: skips = [0] * len(kpt_cryst) step = step.value cell = structure.cell recipr = recipr_base(cell) kpts_cart = np.dot(kpt_cryst, recipr) # Apply cross shifts to original kpts shifts = np.array([ [step, 0, 0], [0, step, 0], [0, 0, step], [0, 0, 0], [-step, 0, 0], [0, -step, 0], [0, 0, -step], ]) app = np.empty((0, 3)) for s, k in zip(skips, kpts_cart): if s: continue app = np.vstack((app, k + shifts)) new_kpt = orm.KpointsData() new_kpt.set_cell(cell) new_kpt.set_kpoints(app, cartesian=True) return new_kpt
def validate_input_parameters(self, input_nodes): """ Validate the parameters input node and create from it the input parameter dictionary that contains all the necessary namelists and their flags that should be written to the input file of the calculation :param input_nodes: dictionary of sanitized and validated input nodes :returns: input_parameters a dictionary with input namelists and their flags """ qpoints = input_nodes[self.get_linkname('qpoints')] parameters = input_nodes[self.get_linkname('parameters')].get_dict() # Transform first-level keys (i.e. namelist and card names) to uppercase and second-level to lowercase input_parameters = _uppercase_dict(parameters, dict_name='parameters') input_parameters = { k: _lowercase_dict(v, dict_name=k) for k, v in input_parameters.iteritems() } # Check that required namelists are present for namelist in self._compulsory_namelists: if not namelist in input_parameters: raise InputValidationError( "the required namelist '{}' was not defined".format( namelist)) # Check for presence of blocked keywords for namelist, flag in self._blocked_keywords: if namelist in input_parameters and flag in input_parameters[ namelist]: raise InputValidationError( "explicit definition of the '{}' " "flag in the '{}' namelist or card is not allowed".format( flag, namelist)) # Validate qpoint input node try: mesh, offset = qpoints.get_kpoints_mesh() except AttributeError: raise NotImplementedError( 'support for explicit qpoints is not implemented, only uniform meshes' ) if any([i != 0. for i in offset]): raise NotImplementedError( 'support for qpoint meshes with non-zero offsets is not implemented' ) input_parameters['INPUTHP']['iverbosity'] = 2 input_parameters['INPUTHP']['outdir'] = self._OUTPUT_SUBFOLDER input_parameters['INPUTHP']['prefix'] = self._PREFIX input_parameters['INPUTHP']['nq1'] = mesh[0] input_parameters['INPUTHP']['nq2'] = mesh[1] input_parameters['INPUTHP']['nq3'] = mesh[2] return input_parameters
def crop_kpoints(structure, kpt_data, centers, radius): """Crop a given set of k-points `kpt_data` that are within a spherical radius `r` from a set of centers `centers`. :param structure: aiida.orm.StructureData used to get the cell of the material. :param kpt_data: aiida.orm.KpointsData to crop. :param centers: aiida.orm.ArrayData containing an array named `centers`. Each element of `centers` is used as the center of a spherical cropping. :param radius: radius of the sphere cropping. :return: aiida.orm.KpointsData node containing the cropped kpoints """ if not isinstance(structure, orm.StructureData): raise InputValidationError( 'Invalide type {} for parameter `structure`'.format( type(structure))) if not isinstance(kpt_data, orm.KpointsData): raise InputValidationError( 'Invalide type {} for parameter `kpt_data`'.format(type(kpt_data))) if not isinstance(centers, orm.ArrayData): raise InputValidationError( 'Invalide type {} for parameter `centers`'.format(type(centers))) if not isinstance(radius, orm.Float): raise InputValidationError( 'Invalide type {} for parameter `radius`'.format(type(radius))) centers = centers.get_array('centers') if len(centers.shape) != 2 or centers.shape[1] != 3: raise InputValidationError( 'Invalide shape {} for array `centers`. Expected (*,3)'.format( centers.shape)) r = radius.value cell = np.array(structure.cell) recipr = recipr_base(cell) try: kpt_cryst = np.array(kpt_data.get_kpoints_mesh(print_list=True)) except MemoryError: return orm.Bool(False) kpt_cart = np.dot(kpt_cryst, recipr) c_cryst = centers c_cart = np.dot(c_cryst, recipr) kpt_cart = KDTree(kpt_cart) centers = KDTree(c_cart) query = kpt_cart.query_ball_tree(centers, r=r) where = [n for n, l in enumerate(query) if len(l)] new = orm.KpointsData() new.set_kpoints(kpt_cryst[where]) return new
def _create_win_string( parameters, kpoints, structure=None, kpoint_path=None, projections=None, random_projections=False, ): from aiida.orm import DataFactory from aiida.orm.data.base import List # prepare the main input text input_file_lines = [] if isinstance(parameters, DataFactory('parameter')): parameters = parameters.get_dict() try: parameters.setdefault('mp_grid', kpoints.get_kpoints_mesh()[0]) except AttributeError: pass input_file_lines += _format_parameters(parameters) block_inputs = {} if projections is None: # If no projections are specified, random projections is used (Dangerous!) if random_projections: block_inputs['projections'] = ['random'] else: block_inputs['projections'] = [] elif isinstance(projections, (tuple, list)): if random_projections: raise InputValidationError( 'random_projections cannot be True with (tuple,list) projections.' 'Instead, use "random" string as first element of the list.') block_inputs['projections'] = projections elif isinstance(projections, List): if random_projections: raise InputValidationError( 'random_projections cannot be True if with List-type projections.' 'Instead, use "random" string as first element of the List.') block_inputs['projections'] = projections.get_attr('list') else: block_inputs['projections'] = _format_all_projections( projections, random_projections=True) if structure is not None: block_inputs['unit_cell_cart'] = _format_unit_cell(structure) block_inputs['atoms_cart'] = _format_atoms_cart(structure) if kpoints is not None: block_inputs['kpoints'] = _format_kpoints(kpoints) if kpoint_path is not None: block_inputs['kpoint_path'] = _format_kpoint_path(kpoint_path) input_file_lines += _format_block_inputs(block_inputs) return '\n'.join(input_file_lines) + '\n'
def load_group(identifier=None, pk=None, uuid=None, label=None, query_with_dashes=True): """ Load a group by one of its identifiers: pk, uuid or label. If the type of the identifier is unknown simply pass it without a keyword and the loader will attempt to infer the type :param identifier: pk (integer), uuid (string) or label (string) of a group :param pk: pk of a group :param uuid: uuid of a group, or the beginning of the uuid :param label: label of a group :param bool query_with_dashes: allow to query for a uuid with dashes :returns: the group instance :raise InputValidationError: if none or more than one of the identifiers are supplied :raise TypeError: if the provided identifier has the wrong type :raise NotExistent: if no matching Group is found :raise MultipleObjectsError: if more than one Group was found """ from aiida.orm.utils.loaders import IdentifierType, GroupEntityLoader # Verify that at least and at most one identifier is specified inputs_provided = [value is not None for value in (identifier, pk, uuid, label)].count(True) if inputs_provided == 0: raise InputValidationError("one of the parameters 'identifier', pk', 'uuid' or 'label' has to be specified") elif inputs_provided > 1: raise InputValidationError("only one of parameters 'identifier', pk', 'uuid' or 'label' has to be specified") if pk is not None: if not isinstance(pk, int): raise TypeError('a pk has to be an integer') identifier = pk identifier_type = IdentifierType.ID elif uuid is not None: if not isinstance(uuid, basestring): raise TypeError('uuid has to be a string type') identifier = uuid identifier_type = IdentifierType.UUID elif label is not None: if not isinstance(label, basestring): raise TypeError('label has to be a string type') identifier = label identifier_type = IdentifierType.LABEL else: identifier = str(identifier) identifier_type = None return GroupEntityLoader.load_entity(identifier, identifier_type, query_with_dashes=query_with_dashes)
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_inputs_dict (with the Code!) """ 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: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError("No code specified for this " "calculation") if inputdict: raise ValidationError("Cannot add other nodes beside parameters") ############################## # END OF INITIAL INPUT CHECK # ############################## input_json = parameters.get_dict() # write all the input to a file input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME) with open(input_filename, 'w') as infile: json.dump(input_json, infile) # ============================ calcinfo ================================ calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.retrieve_list = [self._OUTPUT_FILE_NAME] codeinfo = CodeInfo() codeinfo.cmdline_params = [ self._INPUT_FILE_NAME, self._OUTPUT_FILE_NAME ] codeinfo.code_uuid = code.uuid calcinfo.codes_info = [codeinfo] return calcinfo
def load_inpxml(self, validate_xml_schema=True, return_included_tags=False, **kwargs): """ Returns the lxml etree and the schema dictionary corresponding to the version. If validate_xml_schema=True the file will also be validated against the schema Keyword arguments are passed on to the parser """ from masci_tools.io.io_fleurxml import load_inpxml from masci_tools.util.xml.common_functions import validate_xml self._validate() with self.open(path='inp.xml', mode='rb') as inpxmlfile: try: xmltree, schema_dict = load_inpxml(inpxmlfile, **kwargs) except ValueError as exc: # prob inp.xml file broken err_msg = ( 'The inp.xml file is probably broken, could not parse it to an xml etree.' ) raise InputValidationError(err_msg) from exc except FileNotFoundError as exc: # prob inp.xml file broken err_msg = ( 'The inp.xml file is probably broken, could not find corresponding input schema.' ) raise InputValidationError(err_msg) from exc xmltree, included_tags = self._include_files(xmltree) develop_version = self.inp_version != schema_dict['inp_version'] if validate_xml_schema and not develop_version: try: validate_xml(xmltree, schema_dict.xmlschema, error_header= 'Input file is not validated against the schema') except etree.DocumentInvalid as err: raise InputValidationError(err) from err elif develop_version: self.logger.warning( f'You are using a Fleur input file with file version {self.inp_version}.\n' 'This version has no corresponding XML Schema stored in masci-tools.\n' 'Unexpected Errors can occur. If that is the case you can try to add the ' 'XML Schema for this file version to masci-tools') if return_included_tags: return xmltree, schema_dict, included_tags else: return xmltree, schema_dict
def validate_input(self): """ # validate input and find out which path (1, or 2) to take # return True means run voronoi if false run kkr directly """ inputs = self.inputs if 'remote_data' in inputs: input_ok = True else: input_ok = False return self.exit_codes.ERROR_NO_INPUT_REMOTE_DATA # extract correct remote folder of last calculation if input remote_folder node is not from KkrCalculation but kkr_scf_wc workflow input_remote = self.inputs.remote_data # check if input_remote has single KkrCalculation parent parents = input_remote.get_incoming(node_class=CalcJobNode) nparents = len(parents.all_link_labels()) if nparents != 1: # extract parent workflow and get uuid of last calc from output node parent_workflow = input_remote.inputs.last_RemoteData if not isinstance(parent_workflow, WorkChainNode): raise InputValidationError( "Input remote_data node neither output of a KKR/voronoi calculation nor of kkr_scf_wc workflow" ) parent_workflow_out = parent_workflow.outputs.output_kkr_scf_wc_ParameterResults uuid_last_calc = parent_workflow_out.get_dict().get( 'last_calc_nodeinfo').get('uuid') last_calc = load_node(uuid_last_calc) if not isinstance(last_calc, KkrCalculation) and not isinstance( last_calc, VoronoiCalculation): raise InputValidationError( "Extracted last_calc node not of type KkrCalculation: check remote_data input node" ) # overwrite remote_data node with extracted remote folder output_remote = last_calc.outputs.remote_folder self.inputs.remote_data = output_remote if 'kkr' in inputs: try: test_and_get_codenode(inputs.kkr, 'kkr.kkr', use_exceptions=True) except ValueError: input_ok = False return self.exit_codes.ERROR_KKRCODE_NOT_CORRECT # set self.ctx.input_params_KKR self.ctx.input_params_KKR = get_parent_paranode( self.inputs.remote_data) return input_ok
def analyze_kpt_cross(bands_data, old_data, gap_threshold): """Analyze the result of kpt-cross calculation, returning the list of lowst gap and skippable points.""" if not isinstance(bands_data, orm.BandsData): raise InputValidationError( 'Invalide type {} for parameter `bands_data`'.format( type(bands_data))) if not isinstance(old_data, orm.ArrayData): raise InputValidationError( 'Invalide type {} for parameter `old_data`'.format(type(old_data))) if not isinstance(gap_threshold, orm.Float): raise InputValidationError( 'Invalide type {} for parameter `gap_threshold`'.format( type(gap_threshold))) gap_thr = gap_threshold.value calculation = bands_data.creator gaps = get_gap_array_from_PwCalc(calculation) kpt_cryst = bands_data.get_kpoints() res = orm.ArrayData() gaps = np.array(gaps).reshape(-1, 7) min_pos = np.argmin(gaps, axis=1) min_gap = np.min(gaps, axis=1) app = np.where((min_pos == 3) | (min_gap < gap_thr))[0] new_skips = np.zeros(min_pos.shape) new_skips[app] = 1 app_kpt = kpt_cryst.reshape(-1, 7, 3) new_kpt = app_kpt[list(range(len(min_pos))), min_pos, :] try: kpt = old_data.get_array('kpoints') gaps = old_data.get_array('gaps') skips = old_data.get_array('skips') except: kpt = new_kpt gaps = min_gap skips = new_skips else: w = np.where(skips == 0) kpt[w] = new_kpt gaps[w] = min_gap skips[w] = new_skips res.set_array('skips', skips) res.set_array('kpoints', kpt) res.set_array('gaps', gaps) return res
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 """ # Check inputdict try: parameters = inputdict.pop(self.get_linkname('parameters')) except KeyError: raise InputValidationError("No parameters specified for this " "calculation") if not isinstance(parameters, MultiplyParameters): raise InputValidationError("parameters not of type " "MultiplyParameters") try: code = inputdict.pop(self.get_linkname('code')) except KeyError: raise InputValidationError("No code specified for this " "calculation") if inputdict: raise ValidationError("Unknown inputs besides MultiplyParameters") # In this example, the input file is simply a json dict. # Adapt for your particular code! input_dict = parameters.get_dict() # Write input to file input_filename = tempfolder.get_abs_path(self._INPUT_FILE_NAME) with open(input_filename, 'w') as infile: json.dump(input_dict, infile) # Prepare CalcInfo to be returned to aiida calcinfo = CalcInfo() calcinfo.uuid = self.uuid calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.retrieve_list = [self._OUTPUT_FILE_NAME] codeinfo = CodeInfo() # will call ./code.py in.json out.json codeinfo.cmdline_params = [ self._INPUT_FILE_NAME, self._OUTPUT_FILE_NAME ] codeinfo.code_uuid = code.uuid calcinfo.codes_info = [codeinfo] return calcinfo
def _prepare_for_submission(self, tempfolder, inputdict): from aiida.orm.calculation.job.codtools import commandline_params_from_dict import shutil try: cif = inputdict.pop(self.get_linkname('cif')) except KeyError: raise InputValidationError( "no CIF file is specified for this calculation") if not isinstance(cif, CifData): raise InputValidationError("cif is not of type CifData") parameters = inputdict.pop(self.get_linkname('parameters'), None) if parameters is None: parameters = ParameterData(dict={}) if not isinstance(parameters, ParameterData): raise InputValidationError( "parameters is not of type ParameterData") code = inputdict.pop(self.get_linkname('code'), None) if code is None: raise InputValidationError("Code not found in input") self._validate_resources(**self.get_resources()) input_filename = tempfolder.get_abs_path(self._DEFAULT_INPUT_FILE) shutil.copy(cif.get_file_abs_path(), input_filename) commandline_params = self._default_commandline_params commandline_params.extend( commandline_params_from_dict(parameters.get_dict())) calcinfo = CalcInfo() calcinfo.uuid = self.uuid # The command line parameters should be generated from 'parameters' calcinfo.local_copy_list = [] calcinfo.remote_copy_list = [] calcinfo.retrieve_list = [ self._DEFAULT_OUTPUT_FILE, self._DEFAULT_ERROR_FILE ] calcinfo.retrieve_singlefile_list = [] codeinfo = CodeInfo() codeinfo.cmdline_params = commandline_params codeinfo.stdin_name = self._DEFAULT_INPUT_FILE codeinfo.stdout_name = self._DEFAULT_OUTPUT_FILE codeinfo.stderr_name = self._DEFAULT_ERROR_FILE codeinfo.code_uuid = code.uuid calcinfo.codes_info = [codeinfo] return calcinfo
def validate_parameters(param_data, potential_object): if param_data is None: raise InputValidationError("parameter data not set") validate_against_schema(param_data.get_dict(), "md.schema.json") # ensure the potential and paramters are in the same unit systems # TODO convert between unit systems (e.g. using https://pint.readthedocs.io) punits = param_data.get_dict()["units"] if not punits == potential_object.default_units: raise InputValidationError( "the units of the parameters ({}) and potential ({}) are different" .format(punits, potential_object.default_units)) return True
def _render_section(self, output, params, indent=0): """ It takes a dictionary and recurses through. For key-value pair it checks whether the value is a dictionary and prepends the key with & It passes the valued to the same function, increasing the indentation If the value is a list, I assume that this is something the user wants to store repetitively eg: dict['KEY'] = ['val1', 'val2'] ===> KEY val1 KEY val2 or dict['KIND'] = [{'_': 'Ba', 'ELEMENT':'Ba'}, {'_': 'Ti', 'ELEMENT':'Ti'}, {'_': 'O', 'ELEMENT':'O'}] ====> &KIND Ba ELEMENT Ba &END KIND &KIND Ti ELEMENT Ti &END KIND &KIND O ELEMENT O &END KIND """ for key, val in sorted(params.items()): if key.upper() != key: raise InputValidationError("keyword '%s' not upper case" % key) if key.startswith('@') or key.startswith('$'): raise InputValidationError("CP2K preprocessor not supported") if isinstance(val, dict): output.append('%s&%s %s' % (' ' * indent, key, val.pop('_', ''))) self._render_section(output, val, indent + 3) output.append('%s&END %s' % (' ' * indent, key)) elif isinstance(val, list): for listitem in val: self._render_section(output, {key: listitem}, indent) elif isinstance(val, bool): val_str = '.true.' if val else '.false.' output.append('%s%s %s' % (' ' * indent, key, val_str)) else: output.append('%s%s %s' % (' ' * indent, key, val))
def validate_parameters(param_data, potential_object): if param_data is None: raise InputValidationError('parameter data not set') validate_against_schema(param_data.get_dict(), 'md-multi.schema.json') # ensure the potential and parameters are in the same unit systems # TODO convert between unit systems (e.g. using https://pint.readthedocs.io) punits = param_data.get_dict()['units'] if not punits == potential_object.default_units: raise InputValidationError( f'the units of the parameters ({punits}) and potential ' f'({potential_object.default_units}) are different') return True
def load_node(identifier=None, pk=None, uuid=None, sub_class=None, query_with_dashes=True): """ Load a node by one of its identifiers: pk or uuid. If the type of the identifier is unknown simply pass it without a keyword and the loader will attempt to infer the type :param identifier: pk (integer) or uuid (string) :param pk: pk of a node :param uuid: uuid of a node, or the beginning of the uuid :param sub_class: an optional tuple of orm classes, that should each be strict sub class of Node, to narrow the queryset :param bool query_with_dashes: allow to query for a uuid with dashes :returns: the node instance :raise InputValidationError: if none or more than one of the identifiers are supplied :raise TypeError: if the provided identifier has the wrong type :raise NotExistent: if no matching Node is found :raise MultipleObjectsError: if more than one Node was found """ from aiida.orm.utils.loaders import IdentifierType, NodeEntityLoader # Verify that at least and at most one identifier is specified inputs_provided = [value is not None for value in (identifier, pk, uuid)].count(True) if inputs_provided == 0: raise InputValidationError("one of the parameters 'identifier', 'pk' or 'uuid' has to be specified") elif inputs_provided > 1: raise InputValidationError("only one of parameters 'identifier', 'pk' or 'uuid' has to be specified") if pk is not None: if not isinstance(pk, int): raise TypeError('a pk has to be an integer') identifier = pk identifier_type = IdentifierType.ID elif uuid is not None: if not isinstance(uuid, basestring): raise TypeError('uuid has to be a string type') identifier = uuid identifier_type = IdentifierType.UUID else: identifier = str(identifier) identifier_type = None if sub_class is not None and not isinstance(sub_class, tuple): sub_class = (sub_class,) return NodeEntityLoader.load_entity(identifier, identifier_type, sub_class, query_with_dashes)