Skip to content

Commit

Permalink
Merge pull request #3131 from robfalck/i2881
Browse files Browse the repository at this point in the history
linesearch is now a property of Solver.
  • Loading branch information
swryan committed Feb 23, 2024
2 parents dba7ebc + c8ac933 commit 6b42683
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 10 deletions.
2 changes: 1 addition & 1 deletion openmdao/core/driver.py
Expand Up @@ -434,7 +434,7 @@ def _check_for_invalid_desvar_values(self):
Check for design variable values that exceed their bounds.
This method's behavior is controlled by the OPENMDAO_INVALID_DESVAR environment variable,
which may take on values 'ignore', 'error', 'warn'.
which may take on values 'ignore', 'raise'', 'warn'.
- 'ignore' : Proceed without checking desvar bounds.
- 'warn' : Issue a warning if one or more desvar values exceed bounds.
- 'raise' : Raise an exception if one or more desvar values exceed bounds.
Expand Down
2 changes: 1 addition & 1 deletion openmdao/core/system.py
Expand Up @@ -4832,7 +4832,7 @@ def _reset_iter_counts(self):
if s._nonlinear_solver:
nl = s._nonlinear_solver
nl._iter_count = 0
if hasattr(nl, 'linesearch') and nl.linesearch:
if nl.linesearch:
nl.linesearch._iter_count = 0

def get_reports_dir(self):
Expand Down
4 changes: 2 additions & 2 deletions openmdao/recorders/recording_manager.py
Expand Up @@ -156,7 +156,7 @@ def _get_all_requesters(problem):
nl = system._nonlinear_solver
if nl:
yield nl
if hasattr(nl, 'linesearch') and nl.linesearch:
if nl.linesearch:
yield nl.linesearch


Expand Down Expand Up @@ -231,7 +231,7 @@ def record_model_options(problem, run_number):
nl = system._nonlinear_solver
if nl:
recorder.record_metadata_solver(nl, run_number)
if hasattr(nl, 'linesearch') and nl.linesearch:
if nl.linesearch:
recorder.record_metadata_solver(nl.linesearch, run_number)

ln = system._linear_solver
Expand Down
5 changes: 3 additions & 2 deletions openmdao/solvers/nonlinear/broyden.py
Expand Up @@ -46,7 +46,7 @@ class BroydenSolver(NonlinearSolver):
Most recent Jacobian matrix.
linear_solver : LinearSolver
Linear solver to use for calculating inverse Jacobian.
linesearch : NonlinearSolver
_linesearch : NonlinearSolver
Line search algorithm. Default is None for no line search.
size : int
Total length of the states being solved.
Expand Down Expand Up @@ -76,7 +76,8 @@ def __init__(self, **kwargs):
self.linear_solver = None

# Slot for linesearch
self.linesearch = BoundsEnforceLS()
self.supports['linesearch'] = True
self._linesearch = BoundsEnforceLS()

self.cite = CITATION

Expand Down
5 changes: 3 additions & 2 deletions openmdao/solvers/nonlinear/newton.py
Expand Up @@ -25,7 +25,7 @@ class NewtonSolver(NonlinearSolver):
linear_solver : LinearSolver
Linear solver to use to find the Newton search direction. The default
is the parent system's linear solver.
linesearch : NonlinearSolver
_linesearch : NonlinearSolver
Line search algorithm. Default is None for no line search.
"""

Expand All @@ -41,7 +41,8 @@ def __init__(self, **kwargs):
self.linear_solver = None

# Slot for linesearch
self.linesearch = BoundsEnforceLS()
self.supports['linesearch'] = True
self._linesearch = BoundsEnforceLS()

def _declare_options(self):
"""
Expand Down
26 changes: 24 additions & 2 deletions openmdao/solvers/solver.py
Expand Up @@ -198,6 +198,7 @@ def __init__(self, **kwargs):
self.supports = OptionsDictionary(parent_name=self.msginfo)
self.supports.declare('gradients', types=bool, default=False)
self.supports.declare('implicit_components', types=bool, default=False)
self.supports.declare('linesearch', types=bool, default=False)

self._declare_options()
self.options.update(kwargs)
Expand Down Expand Up @@ -593,6 +594,27 @@ def _declare_options(self):
desc='If True, the states are cached after a successful solve and '
'used to restart the solver in the case of a failed solve.')

@property
def linesearch(self):
"""
Get the linesearch solver associated with this solver.
Returns
-------
NonlinearSolver or None
The linesearch associated with this solver, or None if it does not support one.
"""
if not self.supports['linesearch']:
return None
else:
return self._linesearch

@linesearch.setter
def linesearch(self, ls):
if not self.supports['linesearch']:
raise AttributeError(f'{self.msginfo}: This solver does not support a linesearch.')
self._linesearch = ls

def _setup_solvers(self, system, depth):
"""
Assign system instance, set depth, and optionally perform setup.
Expand Down Expand Up @@ -687,8 +709,8 @@ def _solve(self):
force_one_iteration = False

with Recording(type(self).__name__, self._iter_count, self) as rec:

if stall_count == 3 and not self.linesearch.options['print_bound_enforce']:
ls = self.linesearch
if stall_count == 3 and ls and not ls.options['print_bound_enforce']:

self.linesearch.options['print_bound_enforce'] = True

Expand Down
27 changes: 27 additions & 0 deletions openmdao/solvers/tests/test_solver_features.py
Expand Up @@ -219,6 +219,33 @@ def test_feature_stall_detection_broyden(self):

prob.run_model()

def test_linesearch_property(self):
import openmdao.api as om

ns = om.NewtonSolver()
bs = om.BroydenSolver()
nlbgs = om.NonlinearBlockGS()
nlbj = om.NonlinearBlockJac()

for solver in [ns, bs, nlbgs, nlbj]:
with self.subTest(msg=solver.msginfo):
if solver in (ns, bs):
self.assertIsInstance(solver.linesearch, om.BoundsEnforceLS)
else:
self.assertIsNone(solver.linesearch)

new_ls = om.ArmijoGoldsteinLS()

if solver in (nlbgs, nlbj):
with self.assertRaises(AttributeError) as e:
solver.linesearch = new_ls
expected = (f'{solver.msginfo}: This solver does not '
'support a linesearch.')
self.assertEqual(expected, str(e.exception))
else:
solver.linesearch = new_ls
self.assertIs(solver.linesearch, new_ls)


if __name__ == "__main__":
unittest.main()

0 comments on commit 6b42683

Please sign in to comment.