def add_to_graph(self, startgraph, dgraph, excludes=()):
        """Add this PseudoAssembly to the given graph, absorbing
        nodes that represent components contained in this PA.
        """
        from openmdao.main.depgraph import is_subvar_node, \
                                 is_input_base_node, is_output_base_node

        self._removed_comps = [c for c in self.comps if c not in excludes]

        # Add pseudoassys to graph
        dgraph.add_node(self.name,
                        pa_object=self,
                        comp=True,
                        pseudo='assembly',
                        valid=True)

        empty = {}
        for oldname, newname in self.renames.items():
            attrs = startgraph.node.get(oldname, empty).copy()
            if oldname in self.boundary_params:
                attrs['iotype'] = 'out'
            dgraph.add_node(newname, attr_dict=attrs)
            for u, v, data in dgraph.edges(oldname, data=True):
                dgraph.add_edge(newname, v, attr_dict=data.copy())
            for u, v, data in dgraph.in_edges(oldname, data=True):
                dgraph.add_edge(u, newname, attr_dict=data.copy())

            if is_subvar_node(dgraph, newname):
                dgraph.node[newname]['basevar'] = \
                    newname.split('[', 1)[0]
            if is_input_base_node(dgraph, newname):
                dgraph.add_edge(newname, self.name)
            elif is_output_base_node(dgraph, newname):
                dgraph.add_edge(self.name, newname)
    def add_to_graph(self, startgraph, dgraph, excludes=()):
        """Add this PseudoAssembly to the given graph, absorbing
        nodes that represent components contained in this PA.
        """
        from openmdao.main.depgraph import is_subvar_node, is_input_base_node, is_output_base_node

        self._removed_comps = [c for c in self.comps if c not in excludes]

        # Add pseudoassys to graph
        dgraph.add_node(self.name, pa_object=self, comp=True, pseudo="assembly", valid=True)

        empty = {}
        for oldname, newname in self.renames.items():
            attrs = startgraph.node.get(oldname, empty).copy()
            if oldname in self.boundary_params:
                attrs["iotype"] = "out"
            dgraph.add_node(newname, attr_dict=attrs)
            for u, v, data in dgraph.edges(oldname, data=True):
                dgraph.add_edge(newname, v, attr_dict=data.copy())
            for u, v, data in dgraph.in_edges(oldname, data=True):
                dgraph.add_edge(u, newname, attr_dict=data.copy())

            if is_subvar_node(dgraph, newname):
                dgraph.node[newname]["basevar"] = newname.split("[", 1)[0]
            if is_input_base_node(dgraph, newname):
                dgraph.add_edge(newname, self.name)
            elif is_output_base_node(dgraph, newname):
                dgraph.add_edge(self.name, newname)

        dgraph.config_changed()
    def matvecREV(self, arg):
        '''Callback function for performing the matrix vector product of the
        workflow's full Jacobian with an incoming vector arg.'''

        dgraph = self._derivative_graph
        comps = self._comp_edge_list()
        result = zeros(len(arg))

        # We can call applyJ on each component one-at-a-time, and poke the
        # results into the result vector.
        for compname, data in comps.iteritems():
            if compname == '@fake':
                continue

            comp_inputs = data['inputs']
            comp_outputs = data['outputs']
            comp_residuals = data['residuals']

            inputs = {}
            outputs = {}
            out_bounds = []

            for varname in comp_outputs:
                node = '%s.%s' % (compname, varname)

                # Ouputs define unique edges, so don't duplicate anything
                if is_subvar_node(dgraph, node):
                    if dgraph.base_var(node).split('.', 1)[1] in comp_outputs:
                        continue

                i1, i2 = self.get_bounds(node)
                if isinstance(i1, list):
                    inputs[varname] = arg[i1].copy()
                    if varname not in comp_residuals:
                        outputs[varname] = zeros(len(i1))
                        out_bounds.append((varname, i1, i2))
                else:
                    inputs[varname] = arg[i1:i2].copy()
                    if varname not in comp_residuals:
                        outputs[varname] = zeros(i2 - i1)
                        out_bounds.append((varname, i1, i2))

            for varname in comp_inputs:
                node = '%s.%s' % (compname, varname)

                i1, i2 = self.get_bounds(node)
                if isinstance(i1, list):
                    outputs[varname] = zeros(len(i1))
                else:
                    outputs[varname] = zeros(i2 - i1)
                out_bounds.append((varname, i1, i2))

            if '~' in compname:
                comp = self._derivative_graph.node[compname]['pa_object']
            else:
                comp = self.scope.get(compname)

            # Preconditioning
            if hasattr(comp, 'applyMinvT'):
                inputs = applyMinvT(comp, inputs, self._shape_cache)

            applyJT(comp, inputs, outputs, comp_residuals, self._shape_cache,
                    self._J_cache.get(compname))
            #print inputs, outputs

            for varname, i1, i2 in out_bounds:
                if isinstance(i1, list):
                    result[i1] += outputs[varname]
                else:
                    result[i1:i2] += outputs[varname]

        # Each parameter adds an equation
        for src, target in self._edges.iteritems():
            if '@in' in src or '@fake' in src:
                if isinstance(target, list):
                    target = target[0]

                i1, i2 = self.get_bounds(target)
                result[i1:i2] += arg[i1:i2]

            # A fake output needs to make it into the result vector to prevent
            # the solution from blowing up. Its derivative will be zero
            # regardless.
            if '@fake' in target:
                i1, i2 = self.get_bounds(src)
                result[i1:i2] += arg[i1:i2]

        #print arg, result
        return result
    def matvecREV(self, arg):
        '''Callback function for performing the matrix vector product of the
        workflow's full Jacobian with an incoming vector arg.'''

        dgraph = self._derivative_graph
        comps = self._comp_edge_list()
        result = zeros(len(arg))

        # We can call applyJ on each component one-at-a-time, and poke the
        # results into the result vector.
        for compname, data in comps.iteritems():
            if compname == '@fake':
                continue

            comp_inputs = data['inputs']
            comp_outputs = data['outputs']
            comp_residuals = data['residuals']

            inputs = {}
            outputs = {}
            out_bounds = []

            for varname in comp_outputs:
                node = '%s.%s' % (compname, varname)

                # Ouputs define unique edges, so don't duplicate anything
                if is_subvar_node(dgraph, node):
                    if dgraph.base_var(node).split('.', 1)[1] in comp_outputs:
                        continue

                i1, i2 = self.get_bounds(node)
                if isinstance(i1, list):
                    inputs[varname] = arg[i1].copy()
                    if varname not in comp_residuals:
                        outputs[varname] = zeros(len(i1))
                        out_bounds.append((varname, i1, i2))
                else:
                    inputs[varname] = arg[i1:i2].copy()
                    if varname not in comp_residuals:
                        outputs[varname] = zeros(i2-i1)
                        out_bounds.append((varname, i1, i2))

            for varname in comp_inputs:
                node = '%s.%s' % (compname, varname)

                i1, i2 = self.get_bounds(node)
                if isinstance(i1, list):
                    outputs[varname] = zeros(len(i1))
                else:
                    outputs[varname] = zeros(i2-i1)
                out_bounds.append((varname, i1, i2))

            if '~' in compname:
                comp = self._derivative_graph.node[compname]['pa_object']
            else:
                comp = self.scope.get(compname)

            # Preconditioning
            if hasattr(comp, 'applyMinvT'):
                inputs = applyMinvT(comp, inputs, self._shape_cache)

            applyJT(comp, inputs, outputs, comp_residuals,
                    self._shape_cache, self._J_cache.get(compname))
            #print inputs, outputs

            for varname, i1, i2 in out_bounds:
                if isinstance(i1, list):
                    result[i1] += outputs[varname]
                else:
                    result[i1:i2] += outputs[varname]

        # Each parameter adds an equation
        for src, target in self._edges.iteritems():
            if '@in' in src or '@fake' in src:
                if isinstance(target, list):
                    target = target[0]

                i1, i2 = self.get_bounds(target)
                result[i1:i2] += arg[i1:i2]

        #print arg, result
        return result