Source code for openmdao.util.options

""" OptionsDictionary class definition. """

import warnings
from six import iteritems


[docs]class OptionsDictionary(object): """ A dictionary for storing options for components/drivers/solvers. It is generally used like a standard Python dictionary, except that 1) you can only set or get keys that have been registered with add_option, and 2) type is enforced Args ---- read_only : bool If this is True, these options should not be modified at run time, and should not be printed in the docs.""" # When this is True, variables marked as 'lock_on_setup' cannot be # changed. In all of OpenMDAO's default OptDicts, this will be set to # True on setup. locked = False def __init__(self, read_only=True): self._options = {} self._deprecations = {} # When this is True, no variables in the dictionary can be modified. self.read_only = read_only # Start out with all dictionaries unlocked. Nobody should be creating # them after setup. # TODO: This really is a hack, but we couldn't figure out a way # around it. OptionsDictionary.locked = False
[docs] def add_option(self, name, value, lower=None, upper=None, values=None, desc='', lock_on_setup=False): """ Adds an option to this options dictionary. Args ---- name : str Name of the option. value : object Default value for this option. The type of this value will be enforced. lower : float, optional Lower bounds for a float value. upper : float, optional Upper bounds for a float value. values : list, optional List of all possible values for an enumeration option. desc : str, optional String containing documentation of this option. """ if name in self._options: print("raising an error") raise ValueError("Option '{}' already exists".format(name)) opt = { 'val': value, 'lower' : lower, 'upper' : upper, 'values' : values, 'desc' : desc, 'lock_on_setup' : lock_on_setup, 'changed' : False } self._check(name, value, opt) self._options[name] = opt
[docs] def remove_option(self, name): """ Removes the named option. Does nothing if the option is not found. Args ---- name : str Name of the option to remove. """ try: del self._options[name] except KeyError: pass
def _add_deprecation(self, oldname, newname): """ For renamed options, maps the old name to the new name and prints a DeprecationWarning on each get/set that uses the old name. Args ---- oldname : str The deprecated name. newname : str The correct name. """ if newname not in self._options: raise NameError("The '%s' option was not found." % newname) self._deprecations[oldname] = newname def __getitem__(self, name): try: return self._options[name]['val'] except KeyError: try: newname = self._deprecations[name] _print_deprecation(name, newname) return self._options[newname]['val'] except KeyError: raise KeyError("Option '{}' has not been added".format(name)) def __contains__(self, name): return name in self._options or name in self._deprecations def __setitem__(self, name, value): """ Set an option using dictionary-like access.""" if name not in self._options: if name in self._deprecations: newname = self._deprecations[name] _print_deprecation(name, newname) name = newname else: raise KeyError("Option '{}' has not been added".format(name)) opt = self._options[name] self._check(name, value, opt) opt['val'] = value opt['changed'] = True def __setattr__(self, name, value): """ To prevent user error, disallow direct setting.""" if name in ['_options', 'read_only', '_deprecations', 'locked']: super(OptionsDictionary, self).__setattr__(name, value) else: raise ValueError("Use dict-like access for option '{}'".format(name))
[docs] def get(self, name, default=None): """ Gets a value from this OptionsDictionary. Returns ------- object The value of the named option. If not found, returns the default value that was passed in. """ if name in self._options: return self._options[name]['val'] elif name in self._deprecations: newname = self._deprecations[name] _print_deprecation(name, newname) return self._options[name]['val'] return default
[docs] def iteritems(self): return self.items()
[docs] def items(self): """ Returns ------- iterator Iterator returning the name and option for each option. """ return ((name, opt['val']) for name, opt in iteritems(self._options))
def _check(self, name, value, opt): """ Type checking happens here. """ # Raise an error when trying to set a restricted variable after # setup. if self.locked and opt['lock_on_setup']: msg = "The '%s' option cannot be changed after setup." % name raise RuntimeError(msg) values = opt['values'] if values is not None: # Only need to check if we are in the list if we are an enum self._check_values(name, value, values) else: lower = opt['lower'] upper = opt['upper'] _type = type(opt['val']) self._check_type(name, value, _type) if lower is not None: self._check_low(name, value, lower) if upper is not None: self._check_high(name, value, upper) def _check_type(self, name, value, _type): """ Check for type. """ if type(value) != _type: msg = "'{}' should be a '{}'" raise ValueError(msg.format(name, _type)) def _check_low(self, name, value, lower): """ Check for violation of lower bounds. """ if value < lower: msg = "minimum allowed value for '{}' is '{}'" raise ValueError(msg.format(name, lower)) def _check_high(self, name, value, upper): """ Check for violation of upper bounds. """ if value > upper: msg = "maximum allowed value for '{}' is '{}'" raise ValueError(msg.format(name, upper)) def _check_values(self, name, value, values): """ Check for value not in enumeration. """ if value not in values: msg = "'{}' must be one of the following values: '{}'" raise ValueError(msg.format(name, values)) def _generate_docstring(self, dictname): """ Generates a numpy-style docstring for an OptionsDictionary. Returns ------- docstring : str string that contains part of a basic numpy docstring. """ docstring = [] for (name, val) in sorted(self.items()): docstring.extend([" ", dictname, "['", name, "']", " : ", type(val).__name__, "("]) if isinstance(val, str): docstring.append("'%s'"%val) else: docstring.append(str(val)) docstring.append(")\n") desc = self._options[name]['desc'] if(desc): docstring.extend([" ", desc, "\n"]) return ''.join(docstring)
def _print_deprecation(name, newname): warnings.simplefilter('always', DeprecationWarning) warnings.warn("Option '%s' is deprecated. Use '%s' instead." % (name, newname), DeprecationWarning,stacklevel=2) warnings.simplefilter('ignore', DeprecationWarning)
[docs]class DeprecatedOptionsDictionary(object): """ The fd_option dicts got renamed to deriv_options, and much of the basic functions have changed. This object will help the user convert to the new format. """ def __init__(self, opt): self.opt = opt def __getitem__(self, name): warnings.simplefilter('always', DeprecationWarning) warnings.warn("fd_options is deprecated. Use deriv_options instead.", DeprecationWarning,stacklevel=2) warnings.simplefilter('ignore', DeprecationWarning) if name == 'force_fd': return self.opt['type'] in ['fd', 'cs'] return self.opt[name] def __setitem__(self, name, value): warnings.simplefilter('always', DeprecationWarning) warnings.warn("fd_options is deprecated. Use deriv_options instead.", DeprecationWarning,stacklevel=2) if name == 'force_fd': msg = "Also, the 'force_fd' option has been removed. To force " + \ "finite difference now, set the 'type' option to 'fd'." warnings.warn(msg) deriv_type = 'fd' if value==True else 'user' self.opt['type'] = deriv_type elif name == 'step_type': msg = "Also, the 'step_type' option has been renamed to 'step_calc." warnings.warn(msg) self.opt['step_calc'] = value elif name == 'form' and value == 'complex_step': msg = "Also, complex step is not longer activated using the 'form' " + \ "option. To turn on complex step, set the 'type' option to 'cs'" warnings.warn(msg) self.opt['type'] = 'cs' elif name == 'extra_check_partials_form': msg = "Also, options for check_partial_derivatives have been changed and expanded. " + \ "Please see the srcdocs for the Component class." warnings.warn(msg) self.opt['check_form'] = value else: self.opt[name] = value warnings.simplefilter('ignore', DeprecationWarning)