Source code for baseclasses.solvers.BaseSolver

"""
BaseSolver

Holds a basic Python Analysis Classes (base and inherited).
"""
from difflib import get_close_matches
import copy
import warnings
from ..utils import CaseInsensitiveDict, CaseInsensitiveSet, Error, pp


# =============================================================================
# BaseSolver Class
# =============================================================================
[docs] class BaseSolver: """ Abstract Class for a basic Solver Object """ def __init__( self, name, category, defaultOptions={}, options={}, immutableOptions=set(), deprecatedOptions={}, comm=None, informs={}, checkDefaultOptions=True, caseSensitiveOptions=False, ): """ Solver Class Initialization Parameters ---------- name : str The name of the solver category : dict The category of the solver defaultOptions : dict, optional The default options dictionary options : dict, optional The user-supplied options dictionary immutableOptions : set of strings, optional A set of immutable option names, which cannot be modified after solver creation. deprecatedOptions : dict, optional A dictionary containing deprecated option names, and a message to display if they were used. comm : MPI Communicator, optional The comm object to be used. If none, serial execution is assumed. informs : dict, optional A dictionary of exit code: exit message mappings. checkDefaultOptions : bool, optional A flag to specify whether the default options should be used for error checking. This is used in cases where the default options are not the complete set, which is common for external solvers. In such cases, no error checking is done when calling ``setOption``, but the default options are still set as options upon solver creation. caseSensitiveOptions : bool, optional A flag to specify whether the option names are case sensitive or insensitive. """ self.name = name self.category = category if not caseSensitiveOptions: self.options = CaseInsensitiveDict() self.defaultOptions = CaseInsensitiveDict(defaultOptions) self.immutableOptions = CaseInsensitiveSet(immutableOptions) self.deprecatedOptions = CaseInsensitiveDict(deprecatedOptions) else: self.options = {} self.defaultOptions = defaultOptions self.immutableOptions = immutableOptions self.deprecatedOptions = deprecatedOptions self.comm = comm self.informs = informs self.solverCreated = False self.checkDefaultOptions = checkDefaultOptions # Initialize Options for key, (optionType, optionValue) in self.defaultOptions.items(): # Check if the default is given in a list of possible values if isinstance(optionValue, list) and optionType is not list: # Default is the first element of the list self.setOption(key, optionValue[0]) else: self.setOption(key, optionValue) for key in options: self.setOption(key, options[key]) self.solverCreated = True def __call__(self, *args, **kwargs): """ Run Analyzer (Calling Routine) """ # Checks pass
[docs] def setOption(self, name, value): """ Default implementation of setOption() Parameters ---------- name : str Name of option to set. Not case sensitive. value : varies Value to set. Type is checked for consistency. """ # Check if the option exists if self.checkDefaultOptions: try: defaultType, defaultValue = self.defaultOptions[name] except KeyError: if name in self.deprecatedOptions: raise Error(f"Option {name} is deprecated. {self.deprecatedOptions[name]}") else: guess = get_close_matches(name, list(self.defaultOptions.keys()), n=1, cutoff=0.0)[0] raise Error(f"Option {name} is not a valid {self.name} option. Perhaps you meant {guess}?") # Make sure we are not trying to change an immutable option if # we are not allowed to. if self.solverCreated and name in self.immutableOptions: raise Error(f"Option {name} cannot be modified after the solver is created.") if self.checkDefaultOptions: # If the default provides a list of acceptable values, check whether the value is valid if isinstance(defaultValue, list) and defaultType is not list: if value in defaultValue: self.options[name] = value else: raise Error( f"Value for option {name} is not valid. " + f"Value must be one of {defaultValue} with data type {defaultType}. " + f"Received value is {value} with data type {type(value)}." ) else: # If a list is not provided, check just the type if isinstance(value, defaultType): self.options[name] = value else: raise Error( f"Datatype for option {name} is not valid. " + f"Expected data type {defaultType}. " + f"Received data type is {type(value)}." ) else: # no error checking self.options[name] = value
[docs] def getOption(self, name): """ Default implementation of getOption() Parameters ---------- name : str Name of option to get. Not case sensitive Returns ------- value : varies Return the current value of the option. """ if name in self.defaultOptions or not self.checkDefaultOptions: if name in self.options: return self.options[name] else: raise Error( f"Option {name} was not found. " + "Because options checking has been disabled, make sure the option has been set first." ) else: guess = get_close_matches(name, list(self.defaultOptions.keys()), n=1, cutoff=0.0)[0] raise Error(f"{name} is not a valid option name. Perhaps you meant {guess}?")
def printCurrentOptions(self): self.printOptions() warnings.warn("printCurrentOptions is deprecated. Use printOptions instead.", DeprecationWarning, stacklevel=2)
[docs] def printOptions(self): """ Prints a nicely formatted dictionary of all the current solver options to the stdout on the root processor """ self.pp("+----------------------------------------+") self.pp("|" + f"All {self.name} Options:".center(40) + "|") self.pp("+----------------------------------------+") options = self.getOptions() self.pp(options)
def getOptions(self): return copy.copy(self.options)
[docs] def getModifiedOptions(self): """ Prints a nicely formatted dictionary of all the modified solver options to the stdout on the root processor """ modifiedOptions = {} for key in self.options.keys(): defaultType, defaultValue = self.defaultOptions[key] if defaultType is not list and isinstance(defaultValue, list): defaultValue = defaultValue[0] optionValue = self.getOption(key) if optionValue != defaultValue: modifiedOptions[key] = optionValue return modifiedOptions
[docs] def printModifiedOptions(self): """ Prints a nicely formatted dictionary of all the current solver options that have been modified from the defaults to the root processor """ self.pp("+----------------------------------------+") self.pp("|" + f"All Modified {self.name} Options:".center(40) + "|") self.pp("+----------------------------------------+") modifiedOptions = self.getModifiedOptions() self.pp(modifiedOptions)
[docs] def pp(self, obj, flush=True): """ This method prints ``obj`` (via pprint) on the root proc of ``self.comm`` if it exists. Otherwise it will just print ``obj``. Parameters ---------- obj : object Any Python object to be printed flush : bool If True, the stream will be flushed. """ # Call the parallel safe pp routine defined in utils pp(obj, self.comm, flush=flush)