def _pre_init(self, pa_name, group, dgraph, fd, boundary_params):
        """Return a tuple of the form (pa_inputs, pa_outputs, renames)
        for the PseudoAssembly that would be created given the nodes in
        group and the given graph.
        """

        # First, find our group boundary
        self._orig_group_nodes = list(group) + list(boundary_params)
        allnodes = dgraph.find_prefixed_nodes(self._orig_group_nodes)
        out_edges = nx.edge_boundary(dgraph, allnodes)
        in_edges = nx.edge_boundary(dgraph,
                                    set(dgraph.nodes()).difference(allnodes))
        solver_states = []
        if fd is False:
            for comp in group:

                # Keep any node marked 'solver_state'. Note, only inputs can
                # be solver_states.
                solver_states.extend([
                    node for node in dgraph.find_prefixed_nodes([comp])
                    if 'solver_state' in dgraph.node[node]
                ])

        pa_inputs = edges_to_dict(in_edges).values()
        pa_inputs.extend(solver_states)
        pa_outputs = set([a[0] for a in out_edges])

        renames = {}

        # Add pseudoassy inputs
        for varpath in list(flatten_list_of_iters(pa_inputs)) + \
                       list(pa_outputs):
            varname = to_PA_var(varpath, pa_name)
            if varpath in dgraph:
                renames[varpath] = varname
                old = dgraph.base_var(varpath)
                if old != varpath:
                    renames[old] = to_PA_var(old, pa_name)

        # make boundary params outputs of the PA
        pa_outputs.update(boundary_params)

        return pa_inputs, pa_outputs, renames
    def _pre_init(self, pa_name, group, dgraph, fd, boundary_params):
        """Return a tuple of the form (pa_inputs, pa_outputs, renames)
        for the PseudoAssembly that would be created given the nodes in
        group and the given graph.
        """

        # First, find our group boundary
        self._orig_group_nodes = list(group) + list(boundary_params)
        allnodes = dgraph.find_prefixed_nodes(self._orig_group_nodes)
        out_edges = nx.edge_boundary(dgraph, allnodes)
        in_edges = nx.edge_boundary(dgraph,
                                    set(dgraph.nodes()).difference(allnodes))
        solver_states = []
        if fd is False:
            for comp in group:

                # Keep any node marked 'solver_state'. Note, only inputs can
                # be solver_states.
                solver_states.extend([node for node in dgraph.find_prefixed_nodes([comp])
                                      if 'solver_state' in dgraph.node[node]])

        pa_inputs = edges_to_dict(in_edges).values()
        pa_inputs.extend(solver_states)
        pa_outputs = set([a[0] for a in out_edges])

        renames = {}

        # Add pseudoassy inputs
        for varpath in list(flatten_list_of_iters(pa_inputs)) + \
                       list(pa_outputs):
            varname = to_PA_var(varpath, pa_name)
            if varpath in dgraph:
                renames[varpath] = varname
                old = dgraph.base_var(varpath)
                if old != varpath:
                    renames[old] = to_PA_var(old, pa_name)

        # make boundary params outputs of the PA
        pa_outputs.update(boundary_params)

        return pa_inputs, pa_outputs, renames
    def calc_derivatives(self,
                         first=False,
                         second=False,
                         savebase=True,
                         required_inputs=None,
                         required_outputs=None):
        """Calculate the derivatives for this non-differentiable block using
        Finite Difference."""
        # We don't do this in __init__ because some inputs and outputs
        # are added after creation (for nested driver support).
        if self.fd is None:
            from openmdao.main.derivatives import FiniteDifference
            self.fd = FiniteDifference(self)

        if hasattr(self.wflow, '_severed_edges'):
            self.wflow.sever_edges(self.wflow._severed_edges)

        try:
            # First, linearize about operating point.
            # Note: Only needed for differentiable islands, which are handled
            # with Fake Finite Difference.
            # Don't do this for full-model finite difference.
            if first and self.ffd_order > 0:
                for name in self.comps:

                    # TODO: I think the cache is blown away each time before
                    # this is called
                    if name in self.wflow._J_cache:
                        continue

                    comp = self.wflow.scope.get(name)

                    # Assemblies need some required inputs and outputs
                    # to calculate the Jacobians
                    if has_interface(comp, IAssembly):

                        # Need to know which assy bdry variables are
                        # required, and pass them in. Cache this once.
                        if name not in self.ffd_cache:
                            dgraph = self.wflow.scope._depgraph
                            inputs = [
                                dgraph.base_var(inp)
                                for inp in flatten_list_of_iters(self.inputs)
                            ]
                            outputs = [
                                dgraph.base_var(outp) for outp in self.outputs
                            ]
                            from openmdao.main.depgraph import _get_inner_edges
                            edges = _get_inner_edges(dgraph, inputs, outputs)

                            req_inputs = []
                            req_outputs = []
                            for inp in inputs:
                                comp_str, _, var_str = inp.partition('.')
                                if comp_str == name:
                                    req_inputs.append(var_str)

                            for inp in outputs:
                                comp_str, _, var_str = inp.partition('.')
                                if comp_str == name:
                                    req_outputs.append(var_str)

                            for edge in edges:
                                src, target = edge
                                comp_str, _, var_str = src.partition('.')
                                if comp_str == name:
                                    req_outputs.append(var_str)
                                comp_str, _, var_str = target.partition('.')
                                if comp_str == name:
                                    req_inputs.append(var_str)

                            self.ffd_cache[name] = (req_inputs, req_outputs)

                        req_inputs, req_outputs = self.ffd_cache[name]

                        comp.calc_derivatives(first,
                                              second,
                                              savebase=True,
                                              required_inputs=req_inputs,
                                              required_outputs=req_outputs)

                    # Comp list contains full graph, so don't double up on
                    # the subdrivers.
                    elif not has_interface(comp, IDriver):
                        comp.calc_derivatives(first, second, True)

            self.J = self.fd.calculate()
        finally:
            if hasattr(self.wflow, '_severed_edges'):
                self.wflow.unsever_edges()

        return self.J
    def calc_derivatives(self, first=False, second=False, savebase=True,
                         required_inputs=None, required_outputs=None):
        """Calculate the derivatives for this non-differentiable block using
        Finite Difference."""
        # We don't do this in __init__ because some inputs and outputs
        # are added after creation (for nested driver support).
        if self.fd is None:
            from openmdao.main.derivatives import FiniteDifference
            self.fd = FiniteDifference(self)

        if hasattr(self.wflow, '_severed_edges'):
            self.wflow.sever_edges(self.wflow._severed_edges)

        try:
            # First, linearize about operating point.
            # Note: Only needed for differentiable islands, which are handled
            # with Fake Finite Difference.
            # Don't do this for full-model finite difference.
            if first and self.ffd_order > 0:
                for name in self.comps:

                    # TODO: I think the cache is blown away each time before
                    # this is called
                    if name in self.wflow._J_cache:
                        continue

                    comp = self.wflow.scope.get(name)

                    # Assemblies need some required inputs and outputs
                    # to calculate the Jacobians
                    if has_interface(comp, IAssembly):

                        # Need to know which assy bdry variables are
                        # required, and pass them in. Cache this once.
                        if name not in self.ffd_cache:
                            dgraph = self.wflow.scope._depgraph
                            inputs = [dgraph.base_var(inp)
                                       for inp in flatten_list_of_iters(self.inputs)]
                            outputs = [dgraph.base_var(outp)
                                       for outp in self.outputs]
                            from openmdao.main.depgraph import _get_inner_edges
                            edges = _get_inner_edges(dgraph, inputs, outputs)

                            req_inputs = []
                            req_outputs = []
                            for inp in inputs:
                                comp_str, _, var_str = inp.partition('.')
                                if comp_str == name:
                                    req_inputs.append(var_str)

                            for inp in outputs:
                                comp_str, _, var_str = inp.partition('.')
                                if comp_str == name:
                                    req_outputs.append(var_str)

                            for edge in edges:
                                src, target = edge
                                comp_str, _, var_str = src.partition('.')
                                if comp_str == name:
                                    req_outputs.append(var_str)
                                comp_str, _, var_str = target.partition('.')
                                if comp_str == name:
                                    req_inputs.append(var_str)

                            self.ffd_cache[name] = (req_inputs, req_outputs)

                        req_inputs, req_outputs = self.ffd_cache[name]

                        comp.calc_derivatives(first, second, savebase=True,
                                              required_inputs=req_inputs,
                                              required_outputs=req_outputs)

                    # Comp list contains full graph, so don't double up on
                    # the subdrivers.
                    elif not has_interface(comp, IDriver):
                        comp.calc_derivatives(first, second, True)

            self.J = self.fd.calculate()
        finally:
            if hasattr(self.wflow, '_severed_edges'):
                self.wflow.unsever_edges()

        return self.J