def _get_model_par_seq(self, par_seq, name): """ Converts a provided sequence `par_seq` of model parameter names and indices to a list of indices, removes duplicates and checks if every provided name/index is valid. Parameters ---------- par_seq : 1D array_like of {int, str} A sequence of integers and strings determining which model parameters need to be used for a certain operation. name : str A string stating the name of the variable the result of this method will be stored in. Used for error messages. Returns ------- par_seq_conv : list of int The provided sequence `par_seq` converted to a sorted list of model parameter indices. """ # Do some logging logger = getCLogger('INIT') logger.info("Converting sequence of model parameter names/indices.") # Remove all unwanted characters from the string and split it up par_seq = split_seq(par_seq) # Check elements if they are ints or strings, and if they are valid for i, par_idx in enumerate(par_seq): try: # If par_idx is a string, try to use it as a parameter name if isinstance(par_idx, str): par_seq[i] = self._par_name.index(par_idx) # If not, try to use it as a parameter index else: self._par_name[par_idx] par_seq[i] = par_idx % self._n_par # If any operation above fails, raise error except Exception as error: err_msg = ("Input argument %r[%i] is invalid! (%s)" % (name, i, error)) raise_error(err_msg, InputError, logger) # If everything went without exceptions, check if list is not empty and # remove duplicates if par_seq: par_seq = list(sset(par_seq)) else: err_msg = "Input argument %r is empty!" % (name) raise_error(err_msg, ValueError, logger) # Log end logger.info("Finished converting sequence of model parameter " "names/indices.") # Return it return (par_seq)
def test_raise_error(): # Create a logger and check if an error can be properly raised and logged logger = logging.getLogger('TEST') with pytest.raises(ValueError, match='ERROR'): raise_error('ERROR', ValueError, logger) try: raise ValueError('Error') except Exception as error: with pytest.raises(ValueError, match='Test Error'): raise_error('Test ' + str(error), type(error), logger, error.__traceback__)
def check_compatibility(emul_version): """ Checks if the provided `emul_version` is compatible with the current version of *PRISM*. Raises a :class:`~RequestError` if *False* and indicates which version of *PRISM* still supports the provided `emul_version`. """ # Do some logging logger = getCLogger('COMP_CHECK') logger.info("Performing version compatibility check.") # Loop over all compatibility versions for version in compat_version: # If a compat_version is the same or newer than the emul_version # then it is incompatible if compare_versions(version, emul_version): err_msg = ("The provided emulator is incompatible with the current" " version of PRISM (v%s). The last compatible version " "is v%s." % (__version__, version)) raise_error(err_msg, RequestError, logger) # Check if emul_version is 1.0.x and raise warning if so if not compare_versions(emul_version, '1.1.0'): warn_msg = ("The provided emulator was constructed with an " "unmaintained version of PRISM (v%s). Compatibility with " "the current version of PRISM cannot be guaranteed." % (emul_version)) raise_warning(warn_msg, RequestWarning, logger, 2) # Check if emul_version is not newer than prism_version if not compare_versions(__version__, emul_version): err_msg = ("The provided emulator was constructed with a version later" " than the current version of PRISM (v%s). Use v%s or later" " to use this emulator." % (__version__, emul_version)) raise_error(err_msg, RequestError, logger) else: logger.info("Version compatibility check was successful.")
def _check_mod_set(self, mod_set, name): """ Checks validity of provided set of model outputs `mod_set` in this :obj:`~ModelLink` instance. Parameters ---------- mod_set : 1D or 2D array_like or dict Model output (set) to validate in this :obj:`~ModelLink` instance. name : str The name of the model output (set), which is used in the error message if the validation fails. Returns ------- mod_set : 1D or 2D :obj:`~numpy.ndarray` object The provided `mod_set` if the validation was successful. If `mod_set` was a dict, it will be converted to a :obj:`~numpy.ndarray` object (sorted on :attr:`~data_idx`). """ # Make logger logger = getCLogger('CHECK') logger.info("Validating provided set of model outputs %r." % (name)) # If mod_set is a dict, try to convert it to a NumPy array if isinstance(mod_set, dict): try: mod_set = np_array([mod_set[idx] for idx in self._data_idx]).T except KeyError as error: err_msg = ( "Input argument %r is missing data identifier '%r'!" % (name, error.args[0])) raise_error(err_msg, KeyError, logger) # Make sure that mod_set is a NumPy array mod_set = np_array(mod_set) # Raise error if mod_set is not 1D or 2D if not (mod_set.ndim == 1 or mod_set.ndim == 2): err_msg = ("Input argument %r is not one-dimensional or " "two-dimensional!" % (name)) raise_error(err_msg, ShapeError, logger) # Raise error if mod_set does not have n_data data values if not (mod_set.shape[-1] == self._n_data): err_msg = ("Input argument %r has incorrect number of data values " "(%i != %i)!" % (name, mod_set.shape[-1], self._n_data)) raise_error(err_msg, ShapeError, logger) # Check if mod_set solely consists out of floats mod_set = check_vals(mod_set, name, 'float') # Log again and return mod_set logger.info("Finished validating provided set of model outputs %r." % (name)) return (mod_set)
def check_vals(values, name, *args): """ Checks if all values in provided input argument `values` with `name` meet all criteria given in `args`. If no criteria are given, it is checked if all values are finite. Returns `values` (0 or 1 in case of bool) if *True* and raises a :class:`~ValueError` or :class:`~TypeError` if *False*. Parameters ---------- values : array_like of {bool; complex; float; int; str} The values to be checked against all given criteria in `args`. It must be possible to convert `values` to a :obj:`~numpy.ndarray` object. name : str The name of the input argument, which is used in the error message if a criterion is not met. args : positional arguments in {'bool'; 'complex'; 'float'; 'int'; 'neg'; \ 'nneg'; 'normal'; 'npos'; 'nzero'; 'pos'; \ 'str'} Sequence of strings determining the criteria that `values` must meet. If `args` is empty, it is checked if `values` are finite. Returns ------- return_values : array_like of {complex; float; int; str} If `args` contained 'bool', returns 0s or 1s. Else, returns `values`. Notes ----- If `values` contains integers, but `args` contains 'float', `return_values` will be cast as float. """ # Define logger logger = getRLogger('CHECK') # Convert args to a list args = list(args) # Check type of values if isinstance(values, tuple): arr_type = 'tuple' elif isinstance(values, list): arr_type = 'list' elif isinstance(values, np.ndarray): arr_type = 'ndarray' elif np.isscalar(values) or values is None: arr_type = 'scalar' else: err_msg = "Input argument %r is not array_like!" % (name) raise_error(err_msg, InputError, logger) # Convert values to a NumPy array try: values = np.asanyarray(values) except Exception as error: # pragma: no cover err_msg = ("Input argument %r cannot be converted to a NumPy array! " "(%s)" % (name, error)) raise_error(err_msg, InputError, logger) else: # Since NumPy v1.16.0, anything can be converted to NumPy arrays # So, check if the dtype is not np.object_ if issubclass(values.dtype.type, np.object_): err_msg = ("Input argument %r cannot be converted to a NumPy " "dtype!" % (name)) raise_error(err_msg, TypeError, logger) # Check if values is not empty and raise error if so if not values.size: err_msg = "Input argument %r is empty!" % (name) raise_error(err_msg, InputError, logger) # Loop over all criteria while args: # Check for bool if 'bool' in args: # Convert values to str values = np.char.lower(np.asanyarray(values, dtype=str)) # Check if values available are accepted as bools check_list = np.zeros_like(values, dtype=int, subok=False) check_list[values == '0'] = 1 check_list[values == 'false'] = 1 values[values == 'false'] = '0' check_list[values == '1'] = 1 check_list[values == 'true'] = 1 values[values == 'true'] = '1' # Check if check_list solely contains 1s if not check_list.all(): # If not, raise error index = np.unravel_index(np.argmin(check_list), values.shape) err_msg = ("Input argument '%s%s' is not of type 'bool'!" % (name, list(index) if values.ndim != 0 else '')) raise_error(err_msg, TypeError, logger) else: # If so, convert values to integers and break the loop values = np.asanyarray(values, dtype=int) break # Check for string elif 'str' in args: # Check if str is provided and break if so if issubclass(values.dtype.type, str): break else: err_msg = "Input argument %r is not of type 'str'!" % (name) raise_error(err_msg, TypeError, logger) # Check for complex elif 'complex' in args: # Check if complex is provided and continue if so if issubclass(values.dtype.type, (np.integer, np.floating, np.complexfloating)): # Remove 'complex' from args and check it again args.remove('complex') values = np.asanyarray(values, dtype=complex) continue else: err_msg = ("Input argument %r is not of type 'complex'!" % (name)) raise_error(err_msg, TypeError, logger) # Check for float elif 'float' in args: # Check if float is provided and continue if so if issubclass(values.dtype.type, (np.integer, np.floating)): # Remove 'float' from args and check it again args.remove('float') values = np.asanyarray(values, dtype=float) continue else: err_msg = "Input argument %r is not of type 'float'!" % (name) raise_error(err_msg, TypeError, logger) # Check for integer elif 'int' in args: # Check if int is provided and continue if so if issubclass(values.dtype.type, np.integer): # Remove 'int' from args and check it again args.remove('int') continue else: err_msg = "Input argument %r is not of type 'int'!" % (name) raise_error(err_msg, TypeError, logger) # Check for negative value elif 'neg' in args: # Check if value is negative and continue if so try: index = list(np.argwhere(values >= 0)[0]) except IndexError: args.remove('neg') continue else: err_msg = ("Input argument '%s%s' is not negative!" % (name, index if values.ndim != 0 else '')) raise_error(err_msg, ValueError, logger) # Check for non-negative value elif 'nneg' in args: # Check if value is non-negative and continue if so try: index = list(np.argwhere(values < 0)[0]) except IndexError: args.remove('nneg') continue else: err_msg = ("Input argument '%s%s' is not non-negative!" % (name, index if values.ndim != 0 else '')) raise_error(err_msg, ValueError, logger) # Check for normalized value [-1, 1] elif 'normal' in args: # Check if value is normal and continue if so try: index = list(np.argwhere(abs(values) > 1)[0]) except IndexError: args.remove('normal') continue else: err_msg = ("Input argument '%s%s' is not normalized!" % (name, index if values.ndim != 0 else '')) raise_error(err_msg, ValueError, logger) # Check for non-positive value elif 'npos' in args: # Check if value is non-positive and continue if so try: index = list(np.argwhere(values > 0)[0]) except IndexError: args.remove('npos') continue else: err_msg = ("Input argument '%s%s' is not non-positive!" % (name, index if values.ndim != 0 else '')) raise_error(err_msg, ValueError, logger) # Check for non-zero value elif 'nzero' in args: # Check if value is non-zero and continue if so try: index = list(np.argwhere(values == 0)[0]) except IndexError: args.remove('nzero') continue else: err_msg = ("Input argument '%s%s' is not non-zero!" % (name, index if values.ndim != 0 else '')) raise_error(err_msg, ValueError, logger) # Check for positive value elif 'pos' in args: # Check if value is positive and continue if so try: index = list(np.argwhere(values <= 0)[0]) except IndexError: args.remove('pos') continue else: err_msg = ("Input argument '%s%s' is not positive!" % (name, index if values.ndim != 0 else '')) raise_error(err_msg, ValueError, logger) # If none of the criteria is found, the criteria are invalid else: err_msg = ( "Input argument 'args' contains invalid elements (%s)!" % (args)) raise_error(err_msg, ValueError, logger) # If no criteria are left, it must be a finite value else: # Check if value is finite and continue if so try: index = list(np.argwhere(~np.isfinite(values))[0]) except IndexError: pass except TypeError: err_msg = ( "Input argument '%s%s' is not of type 'int' or 'float'!" % (name, index if values.ndim != 0 else '')) raise_error(err_msg, TypeError, logger) else: err_msg = ("Input argument '%s%s' is not finite!" % (name, index if values.ndim != 0 else '')) raise_error(err_msg, ValueError, logger) # Convert values back to its original type if (arr_type == 'tuple'): values = tuple(values.tolist()) elif (arr_type == 'list'): values = values.tolist() elif (arr_type == 'scalar'): values = values.item() # Return values return (values)
def _check_md_var(self, md_var, name): """ Checks validity of provided set of model discrepancy variances `md_var` in this :obj:`~ModelLink` instance. Parameters ---------- md_var : 1D or 2D array_like or dict Model discrepancy variance set to validate in this :obj:`~ModelLink` instance. name : str The name of the model discrepancy set, which is used in the error message if the validation fails. Returns ------- md_var : 2D :obj:`~numpy.ndarray` object The (converted) provided `md_var` if the validation was successful. If `md_var` was a dict, it will be converted to a :obj:`~numpy.ndarray` object. """ # Make logger logger = getCLogger('CHECK') logger.info("Validating provided set of model discrepancy variances " "%r." % (name)) # If md_var is a dict, convert it to a NumPy array if isinstance(md_var, dict): md_var = np_array([md_var[idx] for idx in md_var.keys()]) # Make sure that md_var is a NumPy array md_var = np_array(md_var) # Raise error if md_var is not 1D or 2D if not (md_var.ndim == 1 or md_var.ndim == 2): err_msg = ("Input argument %r is not one-dimensional or " "two-dimensional!" % (name)) raise_error(err_msg, ShapeError, logger) # Check if md_var contains n_data values if not (md_var.shape[0] == self._n_data): err_msg = ("Received array of model discrepancy variances %r has " "incorrect number of data points (%i != %i)!" % (name, md_var.shape[0], self._n_data)) raise ShapeError(err_msg) # Check if single or dual values were given if (md_var.ndim == 1): md_var = np_array([md_var] * 2).T elif (md_var.shape[1] == 2): pass else: err_msg = ("Received array of model discrepancy variances %r has " "incorrect number of values (%i != 2)!" % (name, md_var.shape[1])) raise ShapeError(err_msg) # Check if all values are non-negative floats md_var = check_vals(md_var, 'md_var', 'nneg', 'float') # Log again and return md_var logger.info("Finished validating provided set of model discrepancy " "variances %r." % (name)) return (md_var)
def _check_sam_set(self, sam_set, name): """ Checks validity of provided set of model parameter samples `sam_set` in this :obj:`~ModelLink` instance. Parameters ---------- sam_set : 1D or 2D array_like or dict Parameter/sample set to validate in this :obj:`~ModelLink` instance. name : str The name of the parameter/sample set, which is used in the error message if the validation fails. Returns ------- sam_set : 1D or 2D :obj:`~numpy.ndarray` object The provided `sam_set` if the validation was successful. If `sam_set` was a dict, it will be converted to a :obj:`~numpy.ndarray` object. """ # Make logger logger = getCLogger('CHECK') logger.info("Validating provided set of model parameter samples %r." % (name)) # If sam_set is a dict, convert it to a NumPy array if isinstance(sam_set, dict): sam_set = np_array(sdict(sam_set).values()).T # Make sure that sam_set is a NumPy array sam_set = np_array(sam_set) # Raise error if sam_set is not 1D or 2D if not (sam_set.ndim == 1 or sam_set.ndim == 2): err_msg = ("Input argument %r is not one-dimensional or " "two-dimensional!" % (name)) raise_error(err_msg, ShapeError, logger) # Raise error if sam_set does not have n_par parameter values if not (sam_set.shape[-1] == self._n_par): err_msg = ("Input argument %r has incorrect number of parameters " "(%i != %i)!" % (name, sam_set.shape[-1], self._n_par)) raise_error(err_msg, ShapeError, logger) # Check if sam_set solely consists out of floats sam_set = check_vals(sam_set, name, 'float') # Check if all samples are within parameter space sam_set_2D = np_array(sam_set, ndmin=2) rng = self._par_rng check = np.apply_along_axis( lambda x: ((rng[:, 0] <= x) * (x <= rng[:, 1])).all(), 1, sam_set_2D) # If check is not empty (can be indexed), raise error try: index = np.argwhere(~check)[0] except IndexError: pass else: err_msg = ("Input argument '%s%s' is outside parameter space!" % (name, index if sam_set.ndim != 1 else '')) raise_error(err_msg, ValueError, logger) # Log again and return sam_set logger.info("Finished validating provided set of model parameter " "samples %r." % (name)) return (sam_set)
def start_gui(pipeline_obj): # pragma: no cover # Create a logger logger = getCLogger('GUI') logger.info("Starting %s." % (APP_NAME)) # Import Pipeline class here to avoid an ImportError from prism import Pipeline # Check if provided pipeline_obj is an instance of the Pipeline class if not isinstance(pipeline_obj, Pipeline): err_msg = ("Input argument 'pipeline_obj' must be an instance of the " "Pipeline class!") raise_error(err_msg, TypeError, logger) # Activate worker mode with pipeline_obj.worker_mode: if pipeline_obj._is_controller: # Temporarily switch the backend of MPL cur_backend = mpl.rcParams['backend'] switch_backend('Agg') # Obtain application instance qapp = QW.QApplication.instance() # If qapp is None, create a new one if qapp is None: QW.QApplication.setAttribute(QC.Qt.AA_EnableHighDpiScaling) qapp = QW.QApplication([APP_NAME]) # Set name of application qapp.setApplicationName(APP_NAME) # Hide the 'whats this' tooltip on Windows QW.QApplication.setAttribute( QC.Qt.AA_DisableWindowContextHelpButton) # Make sure that the application quits when last window closes qapp.lastWindowClosed.connect(qapp.quit, QC.Qt.QueuedConnection) # Initialize main window and draw (show) it main_window = MainViewerWindow(pipeline_obj) main_window.show() main_window.raise_() main_window.activateWindow() # Replace KeyboardInterrupt error by system's default handler signal.signal(signal.SIGINT, signal.SIG_DFL) # Start application qapp.exec_() # Delete this application to stop event processing del qapp # Switch back to previous backend switch_backend(cur_backend) # Log that GUI has been closed logger.info("Exited %s." % (APP_NAME)) # Return the main window instance return(main_window)