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