Source code for openmdao.solvers.nonlinear.newton

"""Define the NewtonSolver class."""

from __future__ import print_function

import numpy as np

from openmdao.solvers.solver import NonlinearSolver
from openmdao.recorders.recording_iteration_stack import Recording
from openmdao.utils.general_utils import warn_deprecation


[docs]class NewtonSolver(NonlinearSolver): """ Newton solver. The default linear solver is the linear_solver in the containing system. Attributes ---------- linear_solver : LinearSolver Linear solver to use to find the Newton search direction. The default is the parent system's linear solver. linesearch : NonlinearSolver Line search algorithm. Default is None for no line search. """ SOLVER = 'NL: Newton'
[docs] def __init__(self, **kwargs): """ Initialize all attributes. Parameters ---------- **kwargs : dict options dictionary. """ super(NewtonSolver, self).__init__(**kwargs) # Slot for linear solver self.linear_solver = None # Slot for linesearch self.linesearch = None
@property def line_search(self): """ Return the current linesearch object. """ warn_deprecation("The 'line_search' attribute provides backwards compatibility " "with OpenMDAO 1.x ; use 'linesearch' instead.") return self.linesearch @line_search.setter def line_search(self, solver): """ Set the linesearch solver. """ warn_deprecation("The 'line_search' attribute provides backwards compatibility " "with OpenMDAO 1.x ; use 'linesearch' instead.") self.linesearch = solver def _declare_options(self): """ Declare options before kwargs are processed in the init method. """ super(NewtonSolver, self)._declare_options() self.options.declare('solve_subsystems', types=bool, default=False, desc='Set to True to turn on sub-solvers (Hybrid Newton).') self.options.declare('max_sub_solves', types=int, default=10, desc='Maximum number of subsystem solves.') self.options.declare('cs_reconverge', types=bool, default=True, desc='When True, when this driver solves under a complex step, nudge ' 'the Solution vector by a small amount so that it reconverges.') self.supports['gradients'] = True self.supports['implicit_components'] = True def _setup_solvers(self, system, depth): """ Assign system instance, set depth, and optionally perform setup. Parameters ---------- system : System pointer to the owning system. depth : int depth of the current system (already incremented). """ super(NewtonSolver, self)._setup_solvers(system, depth) self._disallow_discrete_outputs() if self.linear_solver is not None: self.linear_solver._setup_solvers(self._system, self._depth + 1) else: self.linear_solver = system.linear_solver if self.linesearch is not None: self.linesearch._setup_solvers(self._system, self._depth + 1) def _assembled_jac_solver_iter(self): """ Return a generator of linear solvers using assembled jacs. """ if self.linear_solver is not None: for s in self.linear_solver._assembled_jac_solver_iter(): yield s def _set_solver_print(self, level=2, type_='all'): """ Control printing for solvers and subsolvers in the model. Parameters ---------- level : int iprint level. Set to 2 to print residuals each iteration; set to 1 to print just the iteration totals; set to 0 to disable all printing except for failures, and set to -1 to disable all printing including failures. type_ : str Type of solver to set: 'LN' for linear, 'NL' for nonlinear, or 'all' for all. """ super(NewtonSolver, self)._set_solver_print(level=level, type_=type_) if self.linear_solver is not None and type_ != 'NL': self.linear_solver._set_solver_print(level=level, type_=type_) if self.linesearch is not None: self.linesearch._set_solver_print(level=level, type_=type_) def _run_apply(self): """ Run the apply_nonlinear method on the system. """ self._recording_iter.stack.append(('_run_apply', 0)) system = self._system # Disable local fd approx_status = system._owns_approx_jac system._owns_approx_jac = False try: system._apply_nonlinear() finally: self._recording_iter.stack.pop() # Enable local fd system._owns_approx_jac = approx_status def _linearize_children(self): """ Return a flag that is True when we need to call linearize on our subsystems' solvers. Returns ------- boolean Flag for indicating child linerization """ return (self.options['solve_subsystems'] and self._iter_count <= self.options['max_sub_solves']) def _linearize(self): """ Perform any required linearization operations such as matrix factorization. """ if self.linear_solver is not None: self.linear_solver._linearize() if self.linesearch is not None: self.linesearch._linearize() def _iter_initialize(self): """ Perform any necessary pre-processing operations. Returns ------- float initial error. float error at the first iteration. """ if self.options['debug_print']: self._err_cache['inputs'] = self._system._inputs._copy_views() self._err_cache['outputs'] = self._system._outputs._copy_views() system = self._system # When under a complex step from higher in the hierarchy, sometimes the step is too small # to trigger reconvergence, so nudge the outputs slightly so that we always get at least # one iteration of Newton. if system.under_complex_step and self.options['cs_reconverge']: system._outputs._data += np.linalg.norm(self._system._outputs._data) * 1e-10 # Execute guess_nonlinear if specified. system._guess_nonlinear() with Recording('Newton_subsolve', 0, self): if self.options['solve_subsystems'] and \ (self._iter_count <= self.options['max_sub_solves']): self._solver_info.append_solver() # should call the subsystems solve before computing the first residual self._gs_iter() self._solver_info.pop() self._run_apply() norm = self._iter_get_norm() norm0 = norm if norm != 0.0 else 1.0 return norm0, norm def _single_iteration(self): """ Perform the operations in the iteration loop. """ system = self._system self._solver_info.append_subsolver() do_subsolve = self.options['solve_subsystems'] and \ (self._iter_count < self.options['max_sub_solves']) do_sub_ln = self.linear_solver._linearize_children() # Disable local fd approx_status = system._owns_approx_jac system._owns_approx_jac = False system._vectors['residual']['linear'].set_vec(system._residuals) system._vectors['residual']['linear'] *= -1.0 my_asm_jac = self.linear_solver._assembled_jac system._linearize(my_asm_jac, sub_do_ln=do_sub_ln) if (my_asm_jac is not None and system.linear_solver._assembled_jac is not my_asm_jac): my_asm_jac._update(system) self._linearize() self.linear_solver.solve(['linear'], 'fwd') if self.linesearch: self.linesearch._do_subsolve = do_subsolve self.linesearch.solve() else: system._outputs += system._vectors['output']['linear'] self._solver_info.pop() # Hybrid newton support. if do_subsolve: with Recording('Newton_subsolve', 0, self): self._solver_info.append_solver() self._gs_iter() self._solver_info.pop() # Enable local fd system._owns_approx_jac = approx_status def _set_complex_step_mode(self, active): """ Turn on or off complex stepping mode. Recurses to turn on or off complex stepping mode in all subsystems and their vectors. Parameters ---------- active : bool Complex mode flag; set to True prior to commencing complex step. """ if self.linear_solver is not None: self.linear_solver._set_complex_step_mode(active) if self.linear_solver._assembled_jac is not None: self.linear_solver._assembled_jac.set_complex_step_mode(active) def _mpi_print_header(self): """ Print header text before solving. """ if (self.options['iprint'] > 0 and self._system.comm.rank == 0): pathname = self._system.pathname if pathname: nchar = len(pathname) prefix = self._solver_info.prefix header = prefix + "\n" header += prefix + nchar * "=" + "\n" header += prefix + pathname + "\n" header += prefix + nchar * "=" print(header)
[docs] def cleanup(self): """ Clean up resources prior to exit. """ super(NewtonSolver, self).cleanup() if self.linear_solver: self.linear_solver.cleanup() if self.linesearch: self.linesearch.cleanup()