def test_base_var(self):
     self.assertEqual(base_var(self.dep, 'B.a'), 'B.a')
     self.assertEqual(base_var(self.dep, 'a'), 'a')
     self.assertEqual(base_var(self.dep, 'a.x'), 'a')
     self.assertEqual(base_var(self.dep, 'a.x.y'), 'a')
     self.assertEqual(base_var(self.dep, 'a.x[3].y'), 'a')
     self.assertEqual(base_var(self.dep, 'A.c[2]'), 'A.c')
def _filter_ignored(scope, lst):
    # Remove any vars that the user designates as 'deriv_ignore'
    unignored = []
    topvars = scope._system.vector_vars

    for name in lst:
        collapsed_name = scope.name2collapsed[name]
        if collapsed_name in topvars and topvars[collapsed_name].get('deriv_ignore'):
            continue

        # The user sets 'deriv_ignore' in the basevar, so we have to check that for
        # subvars.
        base = base_var(scope._depgraph, name)
        if base != name:
            collname = scope.name2collapsed.get(base)
            if collname and collname in topvars and \
               topvars[collname].get('deriv_ignore'):
                continue

        unignored.append(name)

    return unignored
def _update_graph_metadata(G, scope):
    nmeta = G.node
    conns = []
    for node, data in G.nodes_iter(data=True):
        data = data.copy() # don't corrupt metadata of other subgraphs/parent graphs
        nmeta[node] = data
        if 'pseudo' in data:
            data['label'] = node.replace('_pseudo_', '_p_') # shorten all pseudocomp names
            data['shape'] = 'diamond'
        elif 'driver' in data:
            data['shape'] = 'invhouse'
            if scope:
                driver = getattr(scope, node)
                for pcomp in driver.list_pseudocomps():
                    conns.append((pcomp, node))
                if hasattr(driver, 'list_param_targets'):
                    conns.extend([(node, p) for p in driver.list_param_targets() if p in G])
        elif 'comp' in data:
            data['shape'] = 'box'
        elif 'var' in data or 'basevar' in data: # var node
            try:
                parts = node.split('.', 1)
            except AttributeError:
                pass
            else:
                if hasattr(G, 'base_var'):
                    base = base_var(G, node)
                    if G.node[base].get('iotype') == 'state':
                        data['shape'] = 'doubleoctagon'
                    else:
                        data['shape'] = 'ellipse'
                else:
                    data['shape'] = 'ellipse'
        data['margin'] = '0.0'

    G.add_edges_from(conns, style='dotted')
    def __init__(self, system, inputs, outputs, return_format='array'):
        """ Performs finite difference on the components in a given
        System. """

        self.inputs = inputs
        self.outputs = outputs
        self.in_bounds = {}
        self.system = system
        self.scope = system.scope
        self.return_format = return_format

        options = system.options
        driver = options.parent

        self.fd_step = options.fd_step*ones((len(self.inputs)))
        self.low = [None] * len(self.inputs)
        self.high = [None] * len(self.inputs)

        self.form = options.fd_form
        self.form_custom = {}
        self.step_type = options.fd_step_type
        self.step_type_custom = {}
        self.relative_threshold = 1.0e-4

        dgraph = self.scope._depgraph
        driver_params = []
        driver_targets = []

        if hasattr(driver, 'get_parameters'):
            driver_params = driver.get_parameters()
            driver_targets = driver.list_param_targets()

        in_size = 0
        for j, srcs in enumerate(self.inputs):

            low = high = None

            # Support for parameter groups
            if isinstance(srcs, basestring):
                srcs = [srcs]

            # Local stepsize support
            meta = self.scope.get_metadata(base_var(dgraph, srcs[0]))

            if 'fd_step' in meta:
                self.fd_step[j] = meta['fd_step']

            if 'low' in meta:
                low = meta['low']
            if 'high' in meta:
                high = meta['high']

            # Settings in the add_parameter call trump all others
            param_srcs = [item for item in srcs if item in driver_targets]
            if param_srcs:
                if param_srcs[0] in driver_params:
                    param = driver_params[param_srcs[0]]
                    if param.fd_step is not None:
                        self.fd_step[j] = param.fd_step
                    if param.low is not None:
                        low = param.low
                    if param.high is not None:
                        high = param.high
                else:
                    # have to check through all the param groups
                    for param_group in driver_params:
                        is_fd_step_not_set = is_low_not_set = \
                                             is_high_not_set = True
                        if not isinstance(param_group, str) and \
                           param_srcs[0] in param_group:
                            param = driver_params[param_group]
                            if is_fd_step_not_set and param.fd_step is not None:
                                self.fd_step[j] = param.fd_step
                                is_fd_step_not_set = False
                            if is_low_not_set and param.low is not None:
                                low = param.low
                                is_low_not_set = False
                            if is_high_not_set and param.high is not None:
                                high = param.high
                                is_high_not_set = False

            if 'fd_step_type' in meta:
                self.step_type_custom[j] = meta['fd_step_type']
                step_type = self.step_type_custom[j]
            else:
                step_type = self.step_type

            # Bounds scaled
            if step_type == 'bounds_scaled':
                if low is None and high is None:
                    raise RuntimeError("For variable '%s', a finite "
                                       "difference step type of bounds_scaled "
                                       "is used but required low and "
                                       "high values are not set" % srcs[0])
                if low == - float_info.max:
                    raise RuntimeError("For variable '%s', a finite "
                                       "difference step type of "
                                       "bounds_scaled is used but required "
                                       "low value is not set" % srcs[0])
                if high == float_info.max:
                    raise RuntimeError("For variable '%s', a finite "
                                       "difference step type of "
                                       "bounds_scaled is used but required "
                                       "high value is not set" % srcs[0])
                self.fd_step[j] = (high - low) * self.fd_step[j]

            if 'fd_form' in meta:
                self.form_custom[j] = meta['fd_form']

            val = self.scope.get(srcs[0])
            width = flattened_size(srcs[0], val, self.scope)

            for src in srcs:
                self.in_bounds[src] = (in_size, in_size+width)
            in_size += width

            self.high[j] = high
            self.low[j] = low

        out_size = 0
        for src in self.outputs:
            val = self.scope.get(src)
            width = flattened_size(src, val)
            out_size += width

        # Size our Jacobian
        if return_format == 'dict':
            self.J = {}
            for okey in outputs:

                self.J[okey] = {}
                for ikey in inputs:
                    if isinstance(ikey, tuple):
                        ikey = ikey[0]

                    # If output not on this process, just allocate a dummy
                    # array
                    if MPI and okey not in self.system.vec['u']:
                        osize = 0
                    else:
                        osize = self.system.vec['u'][okey].size

                    isize = self.system.vec['p'][ikey].size

                    self.J[okey][ikey] = zeros((osize, isize))
        else:
            self.J = zeros((out_size, in_size))

        self.y_base = zeros((out_size,))
        self.y = zeros((out_size,))
        self.y2 = zeros((out_size,))