Exemplo n.º 1
0
    def compute_approx_col_iter(self, system, under_cs=False):
        """
        Execute the system to compute the approximate sub-Jacobians.

        Parameters
        ----------
        system : System
            System on which the execution is run.
        under_cs : bool
            True if we're currently under complex step at a higher level.

        Yields
        ------
        int
            column index
        ndarray
            solution array corresponding to the jacobian column at the given column index
        """
        if not self._wrt_meta:
            return

        if system.under_complex_step:

            # If we are nested under another complex step, then warn and swap to FD.
            if not self._fd:
                from openmdao.approximation_schemes.finite_difference import FiniteDifference

                issue_warning(
                    "Nested complex step detected. Finite difference will be used.",
                    prefix=system.pathname,
                    category=DerivativesWarning)

                fd = self._fd = FiniteDifference()
                empty = {}
                for wrt in self._wrt_meta:
                    fd.add_approximation(wrt, system, empty)

            yield from self._fd.compute_approx_col_iter(system)
            return

        saved_inputs = system._inputs._get_data().copy()
        system._inputs._data.imag[:] = 0.0
        saved_outputs = system._outputs.asarray(copy=True)
        system._outputs._data.imag[:] = 0.0
        saved_resids = system._residuals.asarray(copy=True)
        system._residuals._data.imag[:] = 0.0

        # Turn on complex step.
        system._set_complex_step_mode(True)

        try:
            yield from self._compute_approx_col_iter(system, under_cs=True)
        finally:
            # Turn off complex step.
            system._set_complex_step_mode(False)

        system._inputs.set_val(saved_inputs)
        system._outputs.set_val(saved_outputs)
        system._residuals.set_val(saved_resids)
Exemplo n.º 2
0
    def compute_approximations(self, system, jac, total=False):
        """
        Execute the system to compute the approximate sub-Jacobians.

        Parameters
        ----------
        system : System
            System on which the execution is run.
        jac : dict-like
            Approximations are stored in the given dict-like object.
        total : bool
            If True total derivatives are being approximated, else partials.
        """
        if not self._wrt_meta:
            return

        if system.under_complex_step:

            # If we are nested under another complex step, then warn and swap to FD.
            if not self._fd:
                from openmdao.approximation_schemes.finite_difference import FiniteDifference

                msg = "Nested complex step detected. Finite difference will be used for '%s'."
                simple_warning(msg % system.pathname)

                fd = self._fd = FiniteDifference()
                empty = {}
                for wrt in self._wrt_meta:
                    fd.add_approximation(wrt, system, empty)

            self._fd.compute_approximations(system, jac, total=total)
            return

        saved_inputs = system._inputs._get_data().copy()
        system._inputs._data.imag[:] = 0.0
        saved_outputs = system._outputs.asarray(copy=True)
        system._outputs._data.imag[:] = 0.0
        saved_resids = system._residuals.asarray(copy=True)
        system._residuals._data.imag[:] = 0.0

        # Turn on complex step.
        system._set_complex_step_mode(True)

        try:
            self._compute_approximations(system, jac, total, under_cs=True)
        finally:
            # Turn off complex step.
            system._set_complex_step_mode(False)

        system._inputs.set_val(saved_inputs)
        system._outputs.set_val(saved_outputs)
        system._residuals.set_val(saved_resids)
Exemplo n.º 3
0
    def compute_approximations(self, system, jac, total=False):
        """
        Execute the system to compute the approximate sub-Jacobians.

        Parameters
        ----------
        system : System
            System on which the execution is run.
        jac : dict-like
            Approximations are stored in the given dict-like object.
        total : bool
            If True total derivatives are being approximated, else partials.
        """
        if not self._exec_dict:
            return

        if system.under_complex_step:

            # If we are nested under another complex step, then warn and swap to FD.
            if not self._fd:
                from openmdao.approximation_schemes.finite_difference import FiniteDifference

                msg = "Nested complex step detected. Finite difference will be used for '%s'."
                simple_warning(msg % system.pathname)

                fd = self._fd = FiniteDifference()
                empty = {}
                for lst in itervalues(self._exec_dict):
                    for apprx in lst:
                        fd.add_approximation(apprx[0], empty)

            self._fd.compute_approximations(system, jac, total=total)
            return

        # Turn on complex step.
        system._set_complex_step_mode(True)

        self._compute_approximations(system, jac, total, under_cs=True)

        # Turn off complex step.
        system._set_complex_step_mode(False)
    def _setup_partials(self, recurse=True):
        """
        Process all partials and approximations that the user declared.

        Metamodel needs to declare its partials after inputs and outputs are known.

        Parameters
        ----------
        recurse : bool
            Whether to call this method in subsystems.
        """
        super(MetaModelUnStructuredComp, self)._setup_partials()

        vec_size = self.options['vec_size']
        if vec_size > 1:
            # Sparse specification of partials for vectorized models.
            for wrt, n_wrt in self._surrogate_input_names:
                for of, shape_of in self._surrogate_output_names:

                    n_of = np.prod(shape_of)
                    rows = np.repeat(np.arange(n_of), n_wrt)
                    cols = np.tile(np.arange(n_wrt), n_of)
                    nnz = len(rows)
                    rows = np.tile(rows, vec_size) + np.repeat(
                        np.arange(vec_size), nnz) * n_of
                    cols = np.tile(cols, vec_size) + np.repeat(
                        np.arange(vec_size), nnz) * n_wrt

                    self._declare_partials(of=of,
                                           wrt=wrt,
                                           rows=rows,
                                           cols=cols)

        else:
            # Dense specification of partials for non-vectorized models.
            self._declare_partials(
                of=[name[0] for name in self._surrogate_output_names],
                wrt=[name[0] for name in self._surrogate_input_names])

            # warn the user that if they don't explicitly set options for fd,
            #   the defaults will be used
            # get a list of approximated partials
            declared_partials = set()
            for of, wrt, method, fd_options in self._approximated_partials:
                pattern_matches = self._find_partial_matches(of, wrt)
                for of_bundle, wrt_bundle in product(*pattern_matches):
                    of_pattern, of_matches = of_bundle
                    wrt_pattern, wrt_matches = wrt_bundle
                    for rel_key in product(of_matches, wrt_matches):
                        abs_key = rel_key2abs_key(self, rel_key)
                        declared_partials.add(abs_key)
            non_declared_partials = []
            for of, n_of in self._surrogate_output_names:
                has_derivs = False
                surrogate = self._metadata(of).get('surrogate')
                if surrogate:
                    has_derivs = overrides_method('linearize', surrogate,
                                                  SurrogateModel)
                if not has_derivs:
                    for wrt, n_wrt in self._surrogate_input_names:
                        abs_key = rel_key2abs_key(self, (of, wrt))
                        if abs_key not in declared_partials:
                            non_declared_partials.append(abs_key)
            if non_declared_partials:
                msg = "Because the MetaModelUnStructuredComp '{}' uses a surrogate " \
                      "which does not define a linearize method,\nOpenMDAO will use " \
                      "finite differences to compute derivatives. Some of the derivatives " \
                      "will be computed\nusing default finite difference " \
                      "options because they were not explicitly declared.\n".format(self.name)
                msg += "The derivatives computed using the defaults are:\n"
                for abs_key in non_declared_partials:
                    msg += "    {}, {}\n".format(*abs_key)
                simple_warning(msg, RuntimeWarning)

            for out_name, out_shape in self._surrogate_output_names:
                surrogate = self._metadata(out_name).get('surrogate')
                if surrogate and not overrides_method('linearize', surrogate,
                                                      SurrogateModel):
                    self._approx_partials(
                        of=out_name,
                        wrt=[name[0] for name in self._surrogate_input_names],
                        method='fd')
                    if "fd" not in self._approx_schemes:
                        self._approx_schemes['fd'] = FiniteDifference()
Exemplo n.º 5
0
    def compute_approximations(self, system, jac, total=False):
        """
        Execute the system to compute the approximate sub-Jacobians.

        Parameters
        ----------
        system : System
            System on which the execution is run.
        jac : dict-like
            Approximations are stored in the given dict-like object.
        total : bool
            If True total derivatives are being approximated, else partials.
        """
        if system.under_complex_step:

            # If we are nested under another complex step, then warn and swap to FD.
            if not self._fd:
                from openmdao.approximation_schemes.finite_difference import FiniteDifference

                msg = "Nested complex step detected. Finite difference will be used for '%s'."
                simple_warning(msg % system.pathname)

                fd = self._fd = FiniteDifference()
                for item in self._exec_list:
                    fd.add_approximation(item[0:2], {})

            self._fd.compute_approximations(system, jac, total=total)
            return

        if len(self._exec_list) == 0:
            return

        if total:
            current_vec = system._outputs
        else:
            current_vec = system._residuals

        # Clean vector for results
        results_clone = current_vec._clone(True)

        # Turn on complex step.
        system._set_complex_step_mode(True)
        results_clone.set_complex_step_mode(True)

        # To support driver src_indices, we need to override some checks in Jacobian, but do it
        # selectively.
        uses_src_indices = (system._owns_approx_of_idx or system._owns_approx_wrt_idx) and \
            not isinstance(jac, dict)

        use_parallel_fd = system._num_par_fd > 1 and (
            system._full_comm is not None and system._full_comm.size > 1)
        num_par_fd = system._num_par_fd if use_parallel_fd else 1
        is_parallel = use_parallel_fd or system.comm.size > 1

        results = defaultdict(list)
        iproc = system.comm.rank
        owns = system._owning_rank
        mycomm = system._full_comm if use_parallel_fd else system.comm

        fd_count = 0
        approx_groups = self._get_approx_groups(system)
        for tup in approx_groups:
            wrt, delta, fact, in_idx, in_size, outputs = tup
            for i_count, idx in enumerate(in_idx):
                if fd_count % num_par_fd == system._par_fd_id:
                    # Run the Finite Difference
                    result = self._run_point_complex(system, wrt, idx, delta,
                                                     results_clone, total)

                    if is_parallel:
                        for of, _, out_idx in outputs:
                            if owns[of] == iproc:
                                results[(of, wrt)].append(
                                    (i_count, result._views_flat[of]
                                     [out_idx].imag.copy()))
                    else:
                        for of, subjac, out_idx in outputs:
                            subjac[:, i_count] = result._views_flat[of][
                                out_idx].imag

                fd_count += 1

        if is_parallel:
            results = _gather_jac_results(mycomm, results)

        for wrt, _, fact, _, _, outputs in approx_groups:
            for of, subjac, _ in outputs:
                key = (of, wrt)
                if is_parallel:
                    for i, result in results[key]:
                        subjac[:, i] = result

                subjac *= fact
                if uses_src_indices:
                    jac._override_checks = True
                    jac[key] = subjac
                    jac._override_checks = False
                else:
                    jac[key] = subjac

        # Turn off complex step.
        system._set_complex_step_mode(False)