Source code for openmdao.solvers.nonlinear.newton
"""Define the NewtonSolver class."""
from openmdao.solvers.linesearch.backtracking import BoundsEnforceLS
from openmdao.solvers.solver import NonlinearSolver
from openmdao.recorders.recording_iteration_stack import Recording
[docs]
class NewtonSolver(NonlinearSolver):
"""
Newton solver.
The default linear solver is the linear_solver in the containing system.
Parameters
----------
**kwargs : dict
Options dictionary.
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.
"""
super().__init__(**kwargs)
self.linear_solver = None
self._linesearch = BoundsEnforceLS()
def _declare_options(self):
"""
Declare options before kwargs are processed in the init method.
"""
super()._declare_options()
self.options.declare('solve_subsystems', types=bool,
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.options.declare('reraise_child_analysiserror', types=bool, default=False,
desc='When the option is true, a solver will reraise any '
'AnalysisError that arises during subsolve; when false, it will '
'continue solving.')
self.supports['linesearch'] = True
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()._setup_solvers(system, depth)
self._disallow_discrete_outputs()
if not isinstance(self.options._dict['solve_subsystems']['val'], bool):
msg = '{}: solve_subsystems must be set by the user.'
raise ValueError(msg.format(self.msginfo))
if self.linear_solver is not None:
self.linear_solver._setup_solvers(system, self._depth + 1)
else:
self.linear_solver = system.linear_solver
if self.linesearch is not None:
self.linesearch._setup_solvers(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()._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.push(('_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.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
-------
bool
Flag for indicating child linerization
"""
return (self.options['solve_subsystems'] and not self._system().under_complex_step
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.
"""
system = self._system()
solve_subsystems = self.options['solve_subsystems'] and not system.under_complex_step
if self.options['debug_print']:
self._err_cache['inputs'] = system._inputs._copy_views()
self._err_cache['outputs'] = system._outputs._copy_views()
# Execute guess_nonlinear if specified and
# we have not restarted from a saved point
if not self._restarted and system._has_guess:
system._guess_nonlinear()
with Recording('Newton_subsolve', 0, self) as rec:
if 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()
rec.abs = norm
norm0 = norm if norm != 0.0 else 1.0
rec.rel = norm / norm0
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 not system.under_complex_step 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
try:
system._dresiduals.set_vec(system._residuals)
system._dresiduals *= -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('fwd')
if self.linesearch and not system.under_complex_step:
self.linesearch._do_subsolve = do_subsolve
self.linesearch.solve()
else:
system._outputs += system._doutputs
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()
finally:
# 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)
[docs]
def cleanup(self):
"""
Clean up resources prior to exit.
"""
super().cleanup()
if self.linear_solver:
self.linear_solver.cleanup()
if self.linesearch:
self.linesearch.cleanup()
[docs]
def use_relevance(self):
"""
Return True if relevance should be active.
Returns
-------
bool
True if relevance should be active.
"""
return False