def _validate_keys(self, input_dict): """ Validates the keys otherwise raise ValidationError. Does basic validation from the parent followed by validations for the quantum numbers. Raises exceptions should the input_dict fail the valiation or if it contains any unsupported keywords. :param input_dict: the dictionary of keys to be validated :return validated_dict: a validated dictionary """ validated_dict = super()._validate_keys(input_dict) # Validate m knowing the value of l angular_momentum = validated_dict['angular_momentum'] # l quantum number, must be there magnetic_number = validated_dict['magnetic_number'] # m quantum number, must be there if angular_momentum >= 0: accepted_range = [0, 2 * angular_momentum] else: accepted_range = [0, -angular_momentum] if magnetic_number < min(accepted_range) or magnetic_number > max(accepted_range): raise ValidationError( 'the magnetic number must be in the range [{}, {}]'.format(min(accepted_range), max(accepted_range)) ) # Check if it is a known combination try: self.get_name_from_quantum_numbers( validated_dict['angular_momentum'], magnetic_number=validated_dict['magnetic_number'] ) except ValidationError: raise ValidationError('Invalid angular momentum magnetic number combination') return validated_dict
def _parse_xyz_vel(self, inputstring): """ Load velocities from a XYZ file. .. note:: The steps and symbols must be set manually before calling this import function as a consistency measure. See also comment for :py:meth:`._import_xy_pos` """ from aiida.common.exceptions import ValidationError from aiida.common.utils import xyz_parser_iterator from numpy import array numsteps = self.numsteps if numsteps == 0: raise ValidationError( "steps must be set before importing positional data") numsites = self.numsites if numsites == 0: raise ValidationError( "symbols must be set before importing positional data") velocities = array([[list(velocity) for _, velocity in atoms] for _, _, atoms in xyz_parser_iterator(inputstring) ]) if velocities.shape != (numsteps, numsites, 3): raise ValueError( "TrajectoryData.positions must have shape (s,n,3), " "with s=number of steps={} and " "n=number of symbols={}".format(numsteps, numsites)) self.set_array('velocities', velocities)
def _mpirun_command_validator(self, mpirun_cmd): """ Validates the mpirun_command variable. MUST be called after properly checking for a valid scheduler. """ if not isinstance(mpirun_cmd, (tuple, list)) or not ( all(isinstance(i, basestring) for i in mpirun_cmd)): raise ValidationError("the mpirun_command must be a list of strings") try: job_resource_keys = self.get_scheduler()._job_resource_class.get_valid_keys() except MissingPluginError: raise ValidationError("Unable to load the scheduler for this computer") subst = {i: 'value' for i in job_resource_keys} subst['tot_num_mpiprocs'] = 'value' try: for arg in mpirun_cmd: arg.format(**subst) except KeyError as e: raise ValidationError("In workdir there is an unknown replacement " "field '{}'".format(e.message)) except ValueError as e: raise ValidationError("Error in the string: '{}'".format(e.message))
def validate_list_of_string_tuples(val, tuple_length): """ Check that: 1. ``val`` is a list or tuple 2. each element of the list: a. is a list or tuple b. is of length equal to the parameter tuple_length c. each of the two elements is a string Return if valid, raise ValidationError if invalid """ from aiida.common.exceptions import ValidationError err_msg = ("the value must be a list (or tuple) " "of length-N list (or tuples), whose elements are strings; " "N={}".format(tuple_length)) if not isinstance(val, (list, tuple)): raise ValidationError(err_msg) for f in val: if (not isinstance(f, (list, tuple)) or len(f) != tuple_length or not all(isinstance(s, basestring) for s in f)): raise ValidationError(err_msg) return True
def _validate(self, param, is_exact=True): """ Used internally to verify the sanity of exclude, include lists """ from aiida.orm import DataFactory, CalculationFactory for i in param: if not any([i.startswith('calculation'), i.startswith('code'), i.startswith('data'), i == 'all', ]): raise ValidationError("Module not recognized, allow prefixes " " are: calculation, code or data") the_param = [i + '.' for i in param] factorydict = {'calculation': locals()['CalculationFactory'], 'data': locals()['DataFactory']} for i in the_param: base, module = i.split('.', 1) if base == 'code': if module: raise ValidationError("Cannot have subclasses for codes") elif base == 'all': continue else: if is_exact: try: factorydict[base](module.rstrip('.')) except MissingPluginError: raise ValidationError("Cannot find the class to be excluded") return the_param
def validate(self): """ Check if the attributes and files retrieved from the DB are valid. Raise a ValidationError if something is wrong. Must be able to work even before storing: therefore, use the get_attr and similar methods that automatically read either from the DB or from the internal attribute cache. For the base class, this is always valid. Subclasses will reimplement this. In the subclass, always call the super().validate() method first! """ if not self.get_name().strip(): raise ValidationError("No name specified") self._hostname_validator(self.get_hostname()) self._description_validator(self.get_description()) self._enabled_state_validator(self.is_enabled()) self._transport_type_validator(self.get_transport_type()) self._scheduler_type_validator(self.get_scheduler_type()) self._workdir_validator(self.get_workdir()) try: mpirun_cmd = self.get_mpirun_command() except DbContentError: raise ValidationError("Error in the DB content of the transport_params") # To be called AFTER the validation of the scheduler self._mpirun_command_validator(mpirun_cmd)
def _validate_keys(self, input_dict): """Validate the keys otherwise raise ValidationError. Does basic validation from the parent followed by validations for the quantum numbers. Raises exceptions should the input_dict fail the valiation or if it contains any unsupported keywords. :param input_dict: the dictionary of keys to be validated :return validated_dict: a validated dictionary """ validated_dict = super()._validate_keys(input_dict) # Validate m knowing the value of l total_angular_momentum = validated_dict['total_angular_momentum'] # j quantum number, must be there angular_momentum = validated_dict['angular_momentum'] # l quantum number, must be there accepted_range = [abs(angular_momentum - 0.5), angular_momentum + 0.5] if total_angular_momentum < min(accepted_range) or total_angular_momentum > max(accepted_range): raise ValidationError( 'the total angular momentum must be in the range [{}, {}]'.format( min(accepted_range), max(accepted_range) ) ) magnetic_number = validated_dict['magnetic_number'] # m quantum number, must be there accepted_range = [-total_angular_momentum, total_angular_momentum] if magnetic_number < min(accepted_range) or magnetic_number > max(accepted_range): raise ValidationError( 'the magnetic number must be in the range [{}, {}]'.format(min(accepted_range), max(accepted_range)) ) return validated_dict
def extract_cif(infile, folder, nodes_export_subfolder="nodes", aiida_export_subfolder="aiida", silent=False): """ Extract the nodes to be imported from a TCOD CIF file. TCOD CIFs, exported by AiiDA, may contain an importable subset of AiiDA database, which can be imported. This function prepares SandboxFolder with files required for import. :param infile: file path :param folder: a SandboxFolder, used to extract the file tree :param nodes_export_subfolder: name of the subfolder for AiiDA nodes :param aiida_export_subfolder: name of the subfolder for AiiDA data inside the TCOD CIF internal file tree :param silent: suppress debug print """ import os import urllib2 import CifFile from aiida.common.exceptions import ValidationError from aiida.common.utils import md5_file, sha1_file from aiida.tools.dbexporters.tcod import decode_textfield values = CifFile.ReadCif(infile) values = values[values.keys()[0]] # taking the first datablock in CIF for i in range(0,len(values['_tcod_file_id'])-1): name = values['_tcod_file_name'][i] if not name.startswith(aiida_export_subfolder+os.sep): continue dest_path = os.path.relpath(name,aiida_export_subfolder) if name.endswith(os.sep): if not os.path.exists(folder.get_abs_path(dest_path)): folder.get_subfolder(folder.get_abs_path(dest_path),create=True) continue contents = values['_tcod_file_contents'][i] if contents == '?' or contents == '.': uri = values['_tcod_file_uri'][i] if uri is not None and uri != '?' and uri != '.': contents = urllib2.urlopen(uri).read() encoding = values['_tcod_file_content_encoding'][i] if encoding == '.': encoding = None contents = decode_textfield(contents,encoding) if os.path.dirname(dest_path) != '': folder.get_subfolder(os.path.dirname(dest_path)+os.sep,create=True) with open(folder.get_abs_path(dest_path),'w') as f: f.write(contents) f.flush() md5 = values['_tcod_file_md5sum'][i] if md5 is not None: if md5_file(folder.get_abs_path(dest_path)) != md5: raise ValidationError("MD5 sum for extracted file '{}' is " "different from given in the CIF " "file".format(dest_path)) sha1 = values['_tcod_file_sha1sum'][i] if sha1 is not None: if sha1_file(folder.get_abs_path(dest_path)) != sha1: raise ValidationError("SHA1 sum for extracted file '{}' is " "different from given in the CIF " "file".format(dest_path))
def validate_n(value): """Validate the value of the number of radial nodes.""" if not isinstance(value, int): raise ValidationError('number of radial nodes (n) must be integer') if value < 0 or value > 2: raise ValidationError('number of radial nodes (n) must be between 0 and 2') return value
def validate_l(value): """Validate the value of the angular momentum.""" if not isinstance(value, int): raise ValidationError('angular momentum (l) must be integer') if value < 0 or value > 3: raise ValidationError('angular momentum (l) must be between 0 and 3') return value
def _validate_keys(self, input_dict): """ Checks all the input_dict and tries to validate them, to ensure that they have been properly set raises Exceptions indicating any problems that should arise during the validation :param input_dict: a dictionary of inputs :return: input_dict: the original dictionary with all validated kyes now removed :return: validated_dict: a dictionary containing all the input keys which have now been validated. """ validated_dict = {} if '_orbital_type' in input_dict: raise ValidationError('You cannot manually set the _orbital_type') entry_point = get_entry_point_from_class(self.__class__.__module__, self.__class__.__name__)[1] if entry_point is None: raise ValidationError( 'Unable to detect entry point for current class {}, maybe you did not register an entry point for it?' .format(self.__class__)) validated_dict['_orbital_type'] = entry_point.name for name, validator in self._base_fields_required: try: value = input_dict.pop(name) except KeyError: raise ValidationError( "Missing required parameter '{}'".format(name)) # This might raise ValidationError try: value = validator(value) except ValidationError as exc: raise exc.__class__("Error validating '{}': {}".format( name, str(exc))) validated_dict[name] = value for name, validator, default_value in self._base_fields_optional: try: value = input_dict.pop(name) except KeyError: value = default_value # This might raise ValidationError try: value = validator(value) except ValidationError as exc: raise exc.__class__("Error validating '{}': {}".format( name, str(exc))) validated_dict[name] = value if input_dict: raise ValidationError('Unknown keys: {}'.format( list(input_dict.keys()))) return validated_dict
def _validate(self): super(StructSettingsData, self)._validate() fname = self._ops_filename if fname not in self.get_folder_list(): raise ValidationError("operations not set") try: validate_with_dict(self.data, self._data_schema) except SchemeError as err: raise ValidationError(err)
def validate_l(value): """ Validate the value of the angular momentum """ if not isinstance(value, six.integer_types): raise ValidationError('angular momentum (l) must be integer') if value < -5 or value > 3: raise ValidationError('angular momentum (l) must be between -5 and 3') return value
def _remote_abs_path_validator(self, remote_abs_path): """ Validates the remote_abs_path. """ from aiida.common.exceptions import ValidationError import os.path if not os.path.isabs(remote_abs_path): raise ValidationError("This is not a valid absolute path") if not os.path.split(remote_abs_path)[1]: raise ValidationError("This is a folder, not an executable")
def _validate(self): super(SymmetryData, self)._validate() fname = self._ops_filename if fname not in self.get_folder_list(): raise ValidationError("operations not set") try: jsonschema.validate(self.data, self.data_schema) except SchemeError as err: raise ValidationError(err)
def _label_validator(self, label): """ Validates the label. """ from aiida.common.exceptions import ValidationError if not label.strip(): raise ValidationError("No label specified") if "@" in label: raise ValidationError("Can not use '@' in label")
def validate_spin(value): """ Validate the value of the spin """ if not isinstance(value, int): raise ValidationError('spin must be integer') if value < -1 or value > 1: raise ValidationError('spin must be among -1, 1 or 0 (undefined)') return value
def validate_j(value): """Validate the value of the total angular momentum.""" if not isinstance(value, (int, float)): raise ValidationError('total angular momentum (j) must be float') if not value % 0.5 == 0.0: raise ValidationError('total angular momentum (j) must be a half integer') if value < 0.5 or value > 3.5: raise ValidationError('total angular momentum (j) must be between 0.5 and 3.5') return value
def _validate(self): from aiida.common.exceptions import ValidationError super(RemoteData, self)._validate() try: self.get_remote_path() except AttributeError: raise ValidationError("attribute 'remote_path' not set.") computer = self.get_computer() if computer is None: raise ValidationError("Remote computer not set.")
def _validate(self): from aiida.common.exceptions import ValidationError super()._validate() try: self.get_remote_path() except AttributeError as exception: raise ValidationError("attribute 'remote_path' not set.") from exception computer = self.computer if computer is None: raise ValidationError('Remote computer not set.')
def _validate(self): super(KindData, self)._validate() try: jsonschema.validate(self.data, self.data_schema) except SchemeError as err: raise ValidationError(err) kinds = self.data["kind_names"] for key, value in self.data.items(): if len(value) != len(kinds): raise ValidationError( "'{}' array not the same length as 'kind_names'" "".format(key))
def array_list_checker(array_list, array_name, orb_length): """ Does basic checks over everything in the array_list. Makes sure that all the arrays are np.ndarray floats, that the length is same as required_length, raises exception using array_name if there is a failure """ if not all([isinstance(_, np.ndarray) for _ in array_list]): raise ValidationError( "{} was not composed " "entirely of ndarrays".format(array_name)) if len(array_list) != orb_length: raise ValidationError("{} did not have the same length as the " "list of orbitals".format(array_name))
def _validate(self): """ Validates MD5 hash of CIF file. """ from aiida.common.exceptions import ValidationError super(CifData, self)._validate() try: attr_md5 = self.get_attribute('md5') except AttributeError: raise ValidationError("attribute 'md5' not set.") md5 = self.generate_md5() if attr_md5 != md5: raise ValidationError("Attribute 'md5' says '{}' but '{}' was parsed instead.".format(attr_md5, md5))
def _validate(self): from aiida.common.exceptions import ValidationError super(SinglefileData, self)._validate() try: filename = self.filename except AttributeError: raise ValidationError("attribute 'filename' not set.") if [filename] != self.get_folder_list(): raise ValidationError("The list of files in the folder does not " "match the 'filename' attribute. " "_filename='{}', content: {}".format( filename, self.get_folder_list()))
def validate_key(key): """ Validate the key string to check if it is valid (e.g., if it does not contain the separator symbol.). :return: None if the key is valid :raise ValidationError: if the key is not valid """ if not isinstance(key, basestring): raise ValidationError("The key must be a string.") if not key: raise ValidationError("The key cannot be an empty string.") if _sep in key: raise ValidationError("The separator symbol '{}' cannot be present " "in the key of this field.".format(_sep))
def _parse_xyz_pos(self, inputstring): """ Load positions from a XYZ file. .. note:: The steps and symbols must be set manually before calling this import function as a consistency measure. Even though the symbols and steps could be extracted from the XYZ file, the data present in the XYZ file may or may not be correct and the same logic would have to be present in the XYZ-velocities function. It was therefore decided not to implement it at all but require it to be set explicitly. .. usage:: from aiida.orm.data.array.trajectory import TrajectoryData t = TrajectoryData() # get sites and number of timesteps t.set_array('steps', arange(ntimesteps)) t.set_array('symbols', array([site.kind for site in s.sites])) t.importfile('some-calc/AIIDA-PROJECT-pos-1.xyz', 'xyz_pos') """ from aiida.common.exceptions import ValidationError from aiida.common.utils import xyz_parser_iterator from numpy import array numsteps = self.numsteps if numsteps == 0: raise ValidationError( "steps must be set before importing positional data") numsites = self.numsites if numsites == 0: raise ValidationError( "symbols must be set before importing positional data") positions = array([[list(position) for _, position in atoms] for _, _, atoms in xyz_parser_iterator(inputstring) ]) if positions.shape != (numsteps, numsites, 3): raise ValueError( "TrajectoryData.positions must have shape (s,n,3), " "with s=number of steps={} and " "n=number of symbols={}".format(numsteps, numsites)) self.set_array('positions', positions)
def should_call_default_mpiprocs_per_machine(ctx): # pylint: disable=invalid-name """ Return True if the scheduler can accept 'default_mpiprocs_per_machine', False otherwise. If there is a problem in determining the scheduler, return True to avoid exceptions. """ from aiida.common.exceptions import ValidationError scheduler_ep = ctx.params['scheduler'] if scheduler_ep is not None: try: scheduler_cls = scheduler_ep.load() except ImportError: raise ImportError(f"Unable to load the '{scheduler_ep.name}' scheduler") else: raise ValidationError( 'The should_call_... function should always be run (and prompted) AFTER asking for a scheduler' ) job_resource_cls = scheduler_cls.job_resource_class if job_resource_cls is None: # Odd situation... return False return job_resource_cls.accepts_default_mpiprocs_per_machine()
def set_group_name(self, gname): """ Set the name of the group to be created """ if not isinstance(gname, basestring): raise ValidationError("group name must be a string") self.group_name = gname
def store(self, with_transaction=True, use_cache=None): """ Store a new node in the DB, also saving its repository directory and attributes, and reparsing the file so that the md5 and the element are correctly reset. After being called attributes cannot be changed anymore! Instead, extras can be changed only AFTER calling this store() function. :note: After successful storage, those links that are in the cache, and for which also the parent node is already stored, will be automatically stored. The others will remain unstored. :parameter with_transaction: if False, no transaction is used. This is meant to be used ONLY if the outer calling function has already a transaction open! """ from aiida.common.exceptions import ValidationError basis_abspath = self.get_file_abs_path() if not basis_abspath: raise ValidationError("No valid Basis Set was passed!") metadata, content = parse_basis(basis_abspath) md5sum = md5_from_string(content) for key, val in flatten_dict(metadata).items(): self._set_attr(key, val) self._set_attr('md5', md5sum) return super(BasisSetData, self).store(with_transaction=with_transaction, use_cache=use_cache)
def _enabled_state_validator(cls, enabled_state): """ Validates the hostname. """ if not isinstance(enabled_state, bool): raise ValidationError("Invalid value '{}' for the enabled state, must " "be a boolean".format(str(enabled_state)))