Source code for openmdao.utils.name_maps

"""Maps between promoted/relative/absolute names."""

from difflib import get_close_matches
from itertools import chain

LOCAL = 1 << 0
CONTINUOUS = 1 << 1
DISTRIBUTED = 1 << 2


def _get_flags(local=None, continuous=None, distributed=None):
    """
    Get a mask and expected value for the given flags.

    The test will be flag & mask == expected.

    Parameters
    ----------
    local : bool or None
        If True, the variable is local.
    continuous : bool or None
        If True, the variable is continuous.
    distributed : bool or None
        If True, the variable is distributed.

    Returns
    -------
    mask : int
        The mask for the given flags.
    expected : int
        The expected value for the given flags.
    """
    mask = 0
    expected = 0

    if local is not None:
        mask |= LOCAL
        if local:
            expected |= LOCAL
    if continuous is not None:
        mask |= CONTINUOUS
        if continuous:
            expected |= CONTINUOUS
    if distributed is not None:
        mask |= DISTRIBUTED
        if distributed:
            expected |= DISTRIBUTED

    return mask, expected


[docs]class NameResolver(object): """ Resolve names between absolute and promoted names. Parameters ---------- pathname : str The pathname of the system. msginfo : str The message information for the system. check_dups : bool If True, check for duplicate names. Attributes ---------- _pathname : str The pathname of the system. _prefix : str The prefix of the system. _pathlen : int The length of the pathname. _abs2prom : dict A dictionary of absolute to promoted names. _abs2prom_in : dict A dictionary of absolute to promoted names for inputs. _abs2prom_out : dict A dictionary of absolute to promoted names for outputs. _prom2abs : dict A dictionary of promoted to absolute names. _prom2abs_in : dict A dictionary of promoted to absolute names for inputs. _prom2abs_out : dict A dictionary of promoted to absolute names for outputs. _prom_no_multi_abs : bool If True, all promoted names map to a single absolute name. _conns : dict or None The connections dictionary. msginfo : str The message information for the system. has_remote : bool If True, the name resolver has remote variables. """
[docs] def __init__(self, pathname, msginfo='', check_dups=False): """ Initialize the name resolver. Parameters ---------- pathname : str The pathname of the system. msginfo : str The message information for the system. check_dups : bool If True, check for duplicate names. """ self._pathname = pathname self._prefix = pathname + '.' if pathname else '' self._pathlen = len(pathname) + 1 if pathname else 0 self._abs2prom = {'input': {}, 'output': {}} self._abs2prom_in = self._abs2prom['input'] self._abs2prom_out = self._abs2prom['output'] self._prom2abs = {'input': {}, 'output': {}} self._prom2abs_in = self._prom2abs['input'] self._prom2abs_out = self._prom2abs['output'] self._prom_no_multi_abs = True self._conns = None self.msginfo = msginfo if msginfo else pathname self.has_remote = False
[docs] def add_mapping(self, absname, promname, iotype, local=True, continuous=True, distributed=False): """ Add a mapping between an absolute name and a promoted name. Parameters ---------- absname : str Absolute name. promname : str Promoted name. iotype : str Either 'input' or 'output'. local : bool If True, the variable is local. continuous : bool If True, the variable is continuous. distributed : bool If True, the variable is distributed. """ _, flags = _get_flags(local=local, continuous=continuous, distributed=distributed) if not flags & LOCAL: self.has_remote = True self._abs2prom[iotype][absname] = (promname, flags) p2a = self._prom2abs[iotype] if promname in p2a: self._prom_no_multi_abs = False p2a[promname].append(absname) else: p2a[promname] = [absname]
[docs] def contains(self, name, iotype=None): """ Check if the name resolver contains the given name. Parameters ---------- name : str The name to check. iotype : str Either 'input', 'output', or None to check all iotypes. Returns ------- bool True if the name resolver contains the given name, False otherwise. """ if iotype is None: return self.contains(name, 'input') or self.contains(name, 'output') return name in self._prom2abs[iotype] or name in self._abs2prom[iotype] or \ self._prefix + name in self._abs2prom[iotype]
def _auto_ivc_update(self, auto_ivc_resolver, auto2tgt): """ Update the name resolver with the auto_ivc component's name resolver. Parameters ---------- auto_ivc_resolver : NameResolver The name resolver of the auto_ivc component. auto2tgt : dict A dictionary of auto_ivc outputs to their targets. """ old_abs2prom_out = self._abs2prom_out old_prom2abs_out = self._prom2abs_out self._abs2prom = {'input': self._abs2prom_in, 'output': {}} self._prom2abs = {'input': self._prom2abs_in, 'output': {}} self._abs2prom_out = self._abs2prom['output'] self._prom2abs_out = self._prom2abs['output'] # set promoted name of auto_ivc outputs to the promoted name of the input they connect to for absname, flags in auto_ivc_resolver.flags_iter('output'): pname = self._abs2prom_in[auto2tgt[absname][0]][0] self._abs2prom_out[absname] = (pname, flags) # don't add target prom name to our prom2abs because it causes undesired matches. Just # map the absname (since we're at the top level absname is same as relative name). self._prom2abs_out[absname] = [absname] self._abs2prom_out.update(old_abs2prom_out) self._prom2abs_out.update(old_prom2abs_out) self.has_remote |= auto_ivc_resolver.has_remote
[docs] def update_from_ranks(self, myrank, others): """ Update the name resolver with name resolvers from multiple ranks. Parameters ---------- myrank : int The rank of the current process. others : list of NameResolver The name resolvers to update with. """ # use our existing abs2prom to determine which vars are local to this rank locabs = self._abs2prom # reset our abs2prom so all ranks will have the same order self._abs2prom = {'input': {}, 'output': {}} self._abs2prom_in = self._abs2prom['input'] self._abs2prom_out = self._abs2prom['output'] for rank, other in enumerate(others): for io in ('input', 'output'): loc_abs2prom = locabs[io] my_abs2prom = self._abs2prom[io] if rank == myrank: my_abs2prom.update(loc_abs2prom) else: if other is not None: for absname, (promname, flags) in other._abs2prom[io].items(): if absname not in my_abs2prom: if absname in loc_abs2prom: flags |= LOCAL else: flags &= ~LOCAL self.has_remote = True my_abs2prom[absname] = (promname, flags) self._populate_prom2abs()
def _populate_prom2abs(self): """ Populate the _prom2abs dictionary based on the _abs2prom dictionary. """ self._prom2abs = {'input': {}, 'output': {}} for iotype, promdict in self._prom2abs.items(): for absname, (promname, _) in self._abs2prom[iotype].items(): if promname in promdict: self._prom_no_multi_abs = False promdict[promname].append(absname) else: promdict[promname] = [absname] self._prom2abs_in = self._prom2abs['input'] self._prom2abs_out = self._prom2abs['output'] def _check_dup_prom_outs(self): for promname, abslist in self._prom2abs_out.items(): if len(abslist) > 1: raise ValueError(f"{self.msginfo}: Output name '{promname}' refers to " f"multiple outputs: {sorted(abslist)}.")
[docs] def num_proms(self, iotype=None): """ Get the number of promoted names. Parameters ---------- iotype : str Either 'input', 'output', or None to count all iotypes. Returns ------- int The number of promoted names of the specified iotype. """ if iotype is None: return len(self._prom2abs_in) + len(self._prom2abs_out) return len(self._prom2abs[iotype])
[docs] def num_abs(self, iotype=None, local=None): """ Get the number of absolute names. Parameters ---------- iotype : str Either 'input', 'output', or None to count all iotypes. local : bool or None If True, count only local names. If False, count only non-local names. If None, count all names. Returns ------- int The number of absolute names of the specified iotype. """ if iotype is None: return self.num_abs('input', local) + self.num_abs('output', local) if local is None: return len(self._abs2prom[iotype]) else: count = 0 mask, expected = _get_flags(local=local) for _, flags in self._abs2prom[iotype].values(): if flags & mask == expected: count += 1 return count
[docs] def is_prom(self, promname, iotype=None): """ Check if a promoted name exists. Parameters ---------- promname : str The promoted name to check. iotype : str Either 'input', 'output', or None to check all iotypes. Returns ------- bool True if the promoted name exists, False otherwise. """ if iotype is None: return promname in self._prom2abs_in or promname in self._prom2abs_out return promname in self._prom2abs[iotype]
[docs] def is_abs(self, absname, iotype=None): """ Check if an absolute name exists. Parameters ---------- absname : str The absolute name to check. iotype : str Either 'input', 'output', or None to check all iotypes. Returns ------- bool True if the absolute name exists, False otherwise. """ if iotype is None: return absname in self._abs2prom_in or absname in self._abs2prom_out return absname in self._abs2prom[iotype]
[docs] def check_flags(self, absname, iotype=None, local=None, continuous=None, distributed=None): """ Check if an absolute name has the specified flag values. Parameters ---------- absname : str The absolute name to check. iotype : str Either 'input', 'output', or None to check all iotypes. local : bool or None If True, checked flag must be local. If False, checked flag must be remote. If None, this part of the flag is ignored. continuous : bool or None If True, checked flag must be continuous. If False, checked flag must be discrete. If None, this part of the flag is ignored. distributed : bool or None If True, checked flag must be distributed. If False, checked flag must be non-distributed. If None, this part of the flag is ignored. Returns ------- bool True if the absolute name has the specified flag values, False otherwise. """ if iotype is None: iotype = self.get_abs_iotype(absname) if iotype is None: return False a2p = self._abs2prom[iotype] if absname in a2p: mask, expected = _get_flags(local=local, continuous=continuous, distributed=distributed) return a2p[absname][1] & mask == expected return False
[docs] def is_local(self, absname, iotype=None): """ Check if an absolute name exists. Parameters ---------- absname : str The absolute name to check. iotype : str Either 'input', 'output', or None to check all iotypes. Returns ------- bool True if the absolute name is local, False otherwise. """ return self.check_flags(absname, iotype, local=True)
[docs] def get_abs_iotype(self, absname, report_error=False): """ Get the iotype of an absolute name. Parameters ---------- absname : str The absolute name to get the iotype of. report_error : bool If True, raise an error if the absolute name is not found. Returns ------- str or None The iotype of the absolute name or None if the absolute name is not found. """ if absname in self._abs2prom_out: return 'output' if absname in self._abs2prom_in: return 'input' if report_error: raise KeyError( self._add_guesses(absname, f"{self.msginfo}: Variable name '{absname}' not found.", include_prom=False, include_abs=True))
[docs] def get_prom_iotype(self, promname, report_error=False): """ Get the iotype of a promoted name. If the promoted name is both an input and an output, the returned iotype will be 'output', which is always unambiguous. Parameters ---------- promname : str The promoted name to get the iotype of. report_error : bool If True, raise an error if the promoted name is not found. Returns ------- str or None The iotype of the promoted name or None if the promoted name is not found. """ if promname in self._prom2abs_out: return 'output' if promname in self._prom2abs_in: return 'input' if report_error: raise KeyError( self._add_guesses(promname, f"{self.msginfo}: Variable name '{promname}' not found."))
[docs] def get_iotype(self, name, report_error=False): """ Get the iotype of a name. The name may be an absolute or promoted name. Parameters ---------- name : str The name to get the iotype of. report_error : bool If True, raise an error if the name is not found. Returns ------- str The iotype of the variable. """ if name in self._abs2prom_out: return 'output' if name in self._abs2prom_in: return 'input' if name in self._prom2abs_out: return 'output' if name in self._prom2abs_in: return 'input' if report_error: raise KeyError( self._add_guesses(name, f"{self.msginfo}: Variable name '{name}' not found."), include_abs=True)
[docs] def prom2abs_iter(self, iotype, local=None, continuous=None, distributed=None): """ Yield promoted names and their absolute names. Parameters ---------- iotype : str Either 'input', 'output', or None to yield all iotypes. local : bool or None If True, yield only local names. If False, yield only non-local names. If None, yield all names. continuous : bool or None If True, yield only continuous names. If False, yield only discrete names. If None, yield all names. distributed : bool or None If True, yield only distributed names. If False, yield only non-distributed names. If None, yield all names. Yields ------ promname : str Promoted name. absnames : list of str Absolute names corresponding to the promoted name. """ if iotype is None: yield from self.prom2abs_iter('input', local, continuous, distributed) yield from self.prom2abs_iter('output', local, continuous, distributed) else: if local is None and continuous is None and distributed is None: yield from self._prom2abs[iotype].items() else: a2p = self._abs2prom[iotype] mask, expected = _get_flags(local=local, continuous=continuous, distributed=distributed) for prom, absnames in self._prom2abs[iotype].items(): absnames = [n for n in absnames if a2p[n][1] & mask == expected] if absnames: yield prom, absnames
[docs] def abs2prom_iter(self, iotype=None, local=None, continuous=None, distributed=None): """ Yield absolute names and their promoted names. Parameters ---------- iotype : str Either 'input', 'output', or None to yield all iotypes. local : bool or None If True, yield only local names. If False, yield only non-local names. If None, yield all names. continuous : bool or None If True, yield only continuous names. If False, yield only discrete names. If None, yield all names. distributed : bool or None If True, yield only distributed names. If False, yield only non-distributed names. If None, yield all names. Yields ------ absname : str Absolute name. promname : str Promoted name. """ if iotype is None: yield from self.abs2prom_iter('input', local, continuous, distributed) yield from self.abs2prom_iter('output', local, continuous, distributed) else: if local is None and continuous is None and distributed is None: for absname, (promname, _) in self._abs2prom[iotype].items(): yield absname, promname else: mask, expected = _get_flags(local=local, continuous=continuous, distributed=distributed) for absname, (promname, flags) in self._abs2prom[iotype].items(): if flags & mask == expected: yield absname, promname
[docs] def prom_iter(self, iotype=None, local=None, continuous=None, distributed=None): """ Yield promoted names. Parameters ---------- iotype : str Either 'input', 'output', or None to yield all iotypes. local : bool or None If True, yield only local names. If False, yield only non-local names. If None, yield all names. continuous : bool or None If True, yield only continuous names. If False, yield only discrete names. If None, yield all names. distributed : bool or None If True, yield only distributed names. If False, yield only non-distributed names. If None, yield all names. Yields ------ promname : str Promoted name. """ if iotype is None: yield from self.prom_iter('input', local, continuous, distributed) yield from self.prom_iter('output', local, continuous, distributed) elif local is None and continuous is None and distributed is None: yield from self._prom2abs[iotype] else: mask, expected = _get_flags(local=local, continuous=continuous, distributed=distributed) a2p = self._abs2prom[iotype] for promname, absnames in self._prom2abs[iotype].items(): for absname in absnames: _, flags = a2p[absname] if flags & mask == expected: yield promname # yield promoted name if any absname matches the flags
[docs] def abs_iter(self, iotype=None, local=None, continuous=None, distributed=None): """ Yield absolute names. Parameters ---------- iotype : str Either 'input', 'output', or None to yield all iotypes. local : bool or None If True, yield only local names. If False, yield only non-local names. If None, yield all names. continuous : bool or None If True, yield only continuous names. If False, yield only discrete names. If None, yield all names. distributed : bool or None If True, yield only distributed names. If False, yield only non-distributed names. If None, yield all names. Yields ------ absname : str Absolute name. """ if iotype is None: yield from self.abs_iter('input', local, continuous, distributed) yield from self.abs_iter('output', local, continuous, distributed) else: if local is None and continuous is None and distributed is None: yield from self._abs2prom[iotype] else: mask, expected = _get_flags(local=local, continuous=continuous, distributed=distributed) for absname, (_, flags) in self._abs2prom[iotype].items(): if flags & mask == expected: yield absname
[docs] def flags(self, absname, iotype=None): """ Get the flags for a variable. Parameters ---------- absname : str The absolute name of the variable. iotype : str Either 'input', 'output', or None to check all iotypes. Returns ------- tuple Tuple of the form (promoted_name, int). """ if iotype is None: iotype = self.get_abs_iotype(absname, report_error=True) return self._abs2prom[iotype][absname][1]
[docs] def flags_iter(self, iotype=None): """ Yield absolute names and the corresponding flags. Parameters ---------- iotype : str Either 'input', 'output', or None to yield all iotypes. Yields ------ absname : str Absolute name. flags : int Flags for the variable. """ if iotype is None: yield from self.flags_iter('input') yield from self.flags_iter('output') else: for absname, (_, flags) in self._abs2prom[iotype].items(): yield absname, flags
[docs] def source(self, name, iotype=None, report_error=True): """ Get the source of a variable. If the variable is an input, the source is the connected output. If the variable is an output, the source is the variable itself. Parameters ---------- name : str The name to get the source of. iotype : str Either 'input', 'output', or None to allow all iotypes. If not None, the given name must correspond to the specified iotype. report_error : bool If True, raise an error if the source is not found. Returns ------- str The source corresponding to the name. """ if self._conns is None: raise RuntimeError(f"{self.msginfo}: Can't find source for '{name}' because " "connections are not yet known.") if name in self._abs2prom_in: if iotype is None or iotype == 'input': try: return self._conns[name] except KeyError: pass elif name in self._abs2prom_out: if iotype is None or iotype == 'output': return name else: # promoted absnames = self.absnames(name, iotype, report_error=False) if absnames is not None: absname = absnames[0] # absolute input? if absname in self._conns: if iotype is None or iotype == 'input': return self._conns[absname] if absname in self._abs2prom_out: if iotype is None or iotype == 'output': return absname if report_error: io = '' if iotype is None else f'{iotype} ' raise RuntimeError(self._add_guesses(name, f"{self.msginfo}: Can't find source for {io}" f"'{name}'.", include_abs=True))
[docs] def abs2rel(self, absname, iotype=None, check=False): """ Convert an absolute name to a relative name. Parameters ---------- absname : str The absolute name to convert. iotype : str Either 'input', 'output', or None to allow all iotypes. check : bool If True, check if the absolute name is found. Returns ------- str or None The relative name corresponding to the absolute name or None if check is True and the absolute name is not found. """ if not check or self.is_abs(absname, iotype): return absname[self._pathlen:]
[docs] def rel2abs(self, relname, iotype=None, check=False): """ Convert a relative name to an absolute name. Parameters ---------- relname : str The relative name to convert. iotype : str Either 'input', 'output', or None to allow all iotypes. check : bool If True, check if the relative name is found. Returns ------- str or None The absolute name corresponding to the relative name or None if check is True and the absolute name is not found. """ if check: absname = self._prefix + relname if self.is_abs(absname, iotype): return absname else: return self._prefix + relname
[docs] def rel2abs_iter(self, relnames): """ Yield absolute names corresponding to a list of relative names. Parameters ---------- relnames : list of str The relative names to convert. Yields ------ absname : str The absolute name corresponding to the relative name. """ if self._prefix: for relname in relnames: yield self._prefix + relname else: yield from relnames
[docs] def abs2prom(self, absname, iotype=None): """ Convert an absolute name to a promoted name. Parameters ---------- absname : str The absolute name to convert. iotype : str Either 'input', 'output', or None to check all iotypes. Returns ------- str The promoted name corresponding to the absolute name. """ try: if iotype is None: iotype = self.get_abs_iotype(absname, report_error=True) return self._abs2prom[iotype][absname][0] except KeyError: raise KeyError( self._add_guesses(absname, f"{self.msginfo}: Variable name '{absname}' not found.", include_prom=False, include_abs=True))
[docs] def absnames(self, promname, iotype=None, report_error=True): """ Get the absolute names corresponding to a promoted name. Parameters ---------- promname : str The promoted name to get the absolute names of. iotype : str Either 'input', 'output', or None to check all iotypes. report_error : bool If True, raise an error if the promoted name is not found. Returns ------- list of str or None The absolute names corresponding to the promoted name, or None if report_error is False and the promoted name is not found. """ if iotype is None: iotype = self.get_prom_iotype(promname) if iotype is None: # name is not promoted, try absolute name if promname in self._abs2prom_out: return (promname,) if promname in self._abs2prom_in: return (promname,) if report_error: raise KeyError( self._add_guesses(promname, f"{self.msginfo}: Variable '{promname}' " "not found.")) return try: return self._prom2abs[iotype][promname] except KeyError: if report_error: raise KeyError(self._add_guesses(promname, f"{self.msginfo}: {iotype} variable " f"'{promname}' not found."))
[docs] def prom2abs(self, promname, iotype=None): """ Convert a promoted name to an unique absolute name. If the promoted name doesn't correspond to a single absolute name, an error is raised. Parameters ---------- promname : str The promoted name to convert. iotype : str or None Either 'input', 'output', or None to check all iotypes. Returns ------- str The absolute name corresponding to the promoted name. """ try: if iotype is None: iotype = self.get_prom_iotype(promname) if self._prom_no_multi_abs: return self._prom2abs[iotype][promname][0] lst = self._prom2abs[iotype][promname] if len(lst) == 1: return lst[0] if self._conns is None: # we can't refer to the source since we don't know the connections yet raise RuntimeError(f"{self.msginfo}: The promoted name {promname} is invalid " f"because it refers to multiple inputs: [{' ,'.join(lst)}]. " "Access the value from the connected output variable instead.") # report to the user which connected output to access src_name = self.source(lst[0]) try: # find the promoted source name if we can (may not be in the scope of our System) src_name = self._abs2prom['output'][src_name][0] except KeyError: pass raise RuntimeError(f"{self.msginfo}: The promoted name {promname} is invalid because it" f" refers to multiple inputs: [{' ,'.join(lst)}]. Access the value " f"from the connected output variable {src_name} instead.") except KeyError: raise KeyError(self._add_guesses(promname, f"{self.msginfo}: Variable name '{promname}' " "not found."))
[docs] def any2abs(self, name, iotype=None, default=None): """ Convert any name to a unique absolute name. Parameters ---------- name : str Promoted or relative name. iotype : str or None Either 'input', 'output', or None to check all iotypes. default : str or None The value to return if the name is not found. Default is None. Returns ------- str Absolute name. """ if iotype is None: iotype = self.get_prom_iotype(name) if iotype is None: iotype = self.get_abs_iotype(name) if iotype is None: return default if name in self._abs2prom[iotype]: return name if name in self._prom2abs[iotype]: return self.prom2abs(name, iotype) # try relative name absname = self._prefix + name if absname in self._abs2prom[iotype]: return absname return default
[docs] def any2abs_key(self, key): """ Convert any jacobian key to an absolute key. Parameters ---------- key : (str, str) The jacobian key to convert. Returns ------- (str, str) or None The absolute key or None if the key is not found. """ of = self.any2abs(key[0], 'output') if of is None: return # try the input first in order to mimic the old behavior where an exception # is raised if the input is ambiguous. wrt = self.any2abs(key[1], 'input') if wrt is None: wrt = self.any2abs(key[1], 'output') if wrt is None: return return (of, wrt)
[docs] def any2prom(self, name, iotype=None, default=None): """ Convert any name to a promoted name. Parameters ---------- name : str Promoted or relative name. iotype : str or None Either 'input', 'output', or None to check all iotypes. default : str or None The value to return if the name is not found. Default is None. Returns ------- str Promoted name. """ if iotype is None: iotype = self.get_abs_iotype(name) if iotype is None: iotype = self.get_prom_iotype(name) if iotype is None: return default a2p = self._abs2prom[iotype] if name in a2p: return a2p[name][0] if name in self._prom2abs[iotype]: return name return default
[docs] def prom_or_rel2abs(self, name, iotype=None, report_error=False): """ Convert any promoted or relative name to a unique absolute name. Parameters ---------- name : str Promoted or relative name. iotype : str or None Either 'input', 'output', or None to check all iotypes. report_error : bool If True, raise an error if the name is not found. Returns ------- str Absolute name. """ # try a faster lookup first. This is typically called from within Component Vectors using # the local name. if self._prom_no_multi_abs: try: return self._prom2abs[iotype][name][0] except KeyError: pass if iotype is None: iotype = self.get_prom_iotype(name) if name in self._prom2abs[iotype]: return self.prom2abs(name, iotype) # try relative name absname = self._prefix + name if absname in self._abs2prom[iotype]: return absname if report_error: raise KeyError(self._add_guesses(name, f"{self.msginfo}: Variable name '{name}' not found."))
[docs] def prom2prom(self, promname, other, iotype=None): """ Convert a promoted name in other to our promoted name. This requires a matching absolute name between the two NameResolvers. Parameters ---------- promname : str The promoted name to convert. other : NameResolver The other name resolver. iotype : str or None Either 'input', 'output', or None to check all iotypes. Returns ------- str The promoted name corresponding to the converted promoted name or the original promoted name if no match is found. """ if iotype is None: iotype = other.get_prom_iotype(promname) if iotype is None: return promname absnames = other.absnames(promname, iotype, report_error=False) if absnames: absname = absnames[0] if absname in self._abs2prom[iotype]: return self._abs2prom[iotype][absname][0] return promname
def _add_guesses(self, name, msg, n=10, cutoff=0.15, include_prom=True, include_abs=False): """ Add guess information to a message. Parameters ---------- name : str The name to report an error for. msg : str The message to report. n : int The number of close matches to return. cutoff : float The cutoff for the close matches. include_prom : bool If True, include promoted names in the guess list. include_abs : bool If True, include absolute names in the guess list. """ names = [] if include_prom: names.append(self.prom_iter()) if include_abs: names.append(self.abs_iter()) guesses = sorted(set(get_close_matches(name, chain(*names), n=n, cutoff=cutoff))) if guesses: msg = f"{msg} Perhaps you meant one of the following variables: {guesses}." return msg
[docs]def rel_key2abs_key(obj, rel_key, delim='.'): """ Map relative variable name pair to absolute variable name pair. Parameters ---------- obj : object Object to which the given key is relative. The object must have a `pathname` attribute that is a string delimited by 'delim'. rel_key : (str, str) Given relative variable name pair. delim : str Delimiter between the parts of the object pathname. Returns ------- (str, str) Absolute variable name pair. """ if obj.pathname: of, wrt = rel_key pre = obj.pathname + delim return (pre + of, pre + wrt) return rel_key
[docs]def abs_key2rel_key(obj, abs_key): """ Map relative variable name pair to absolute variable name pair. Parameters ---------- obj : object Object to which the given key is relative. The object must have a `pathname` attribute. abs_key : (str, str) Given absolute variable name pair. Returns ------- (str, str) Relative variable name pair. """ if obj.pathname: of, wrt = abs_key plen = len(obj.pathname) + 1 return (of[plen:], wrt[plen:]) return abs_key
[docs]def abs_key_iter(system, rel_ofs, rel_wrts): """ Return absolute jacobian keys given relative 'of' and 'wrt' names. Parameters ---------- system : System The scoping system. rel_ofs : iter of str Names of the relative 'of' variables. rel_wrts : iter of str Names of the relative 'wrt' variables. Yields ------ abs_of Absolute 'of' name. abs_wrt Absolute 'wrt' name. """ if system.pathname: prefix = system.pathname + '.' abs_wrts = [prefix + r for r in rel_wrts] for rel_of in rel_ofs: abs_of = prefix + rel_of for abs_wrt in abs_wrts: yield abs_of, abs_wrt else: for abs_of in rel_ofs: for abs_wrt in rel_wrts: yield abs_of, abs_wrt