Solver Debug Printing

When working with a model and you have a situation where a nonlinear solver is not converging, it may be helpful to know the complete set of input and output values from the initialization of the failing case so that it can be recreated for debugging purposes. NonlinearSolver provides the debug_print option for this purpose:

NonlinearSolver Options

Option

Default

Acceptable Values

Acceptable Types

Description

atol

1e-10

N/A

N/A

absolute error tolerance

debug_print

False

[True, False]

[‘bool’]

If true, the values of input and output variables at the start of iteration are printed and written to a file after a failure to converge.

err_on_non_converge

False

[True, False]

[‘bool’]

When True, AnalysisError will be raised if we don’t converge.

iprint

1

N/A

[‘int’]

whether to print output

maxiter

10

N/A

[‘int’]

maximum number of iterations

rtol

1e-10

N/A

N/A

relative error tolerance

stall_limit

0

N/A

N/A

Number of iterations after which, if the residual norms are identical within the stall_tol, then terminate as if max iterations were reached. Default is 0, which disables this feature.

stall_tol

1e-12

N/A

N/A

When stall checking is enabled, the threshold below which the residual norm is considered unchanged.

Usage

This example shows how to use the debug_print option for a NonlinearSolver. When this option is set to True, the values of the input and output variables will be displayed and written to a file if the solver fails to converge.

from distutils.version import LooseVersion
import numpy as np

import openmdao.api as om
from openmdao.test_suite.scripts.circuit_analysis import Circuit
from openmdao.utils.general_utils import printoptions

p = om.Problem()
model = p.model

model.add_subsystem('circuit', Circuit())

p.setup()

nl = model.circuit.nonlinear_solver = om.NewtonSolver(solve_subsystems=False)

nl.options['iprint'] = 2
nl.options['debug_print'] = True
nl.options['err_on_non_converge'] = True

# set some poor initial guesses so that we don't converge
p.set_val('circuit.I_in', 0.1, units='A')
p.set_val('circuit.Vg', 0.0, units='V')
p.set_val('circuit.n1.V', 10.)
p.set_val('circuit.n2.V', 1e-3)

opts = {}
# formatting has changed in numpy 1.14 and beyond.
if LooseVersion(np.__version__) >= LooseVersion("1.14"):
    opts["legacy"] = '1.13'

with printoptions(**opts):
    # run the model
    try:
        p.run_model()
    except om.AnalysisError:
        pass

with open(self.filename, 'r') as f:
    print(f.read())
=======
circuit
=======
NL: Newton 0 ; 2.53337743 1
NL: Newton 1 ; 6.97216645e+152 2.75212306e+152
NL: Newton 2 ; 2.56496626e+152 1.01246906e+152
NL: Newton 3 ; 9.43616587e+151 3.72473748e+151
NL: Newton 4 ; 3.47143851e+151 1.37028082e+151
NL: Newton 5 ; 1.27709554e+151 5.04107884e+150
NL: Newton 6 ; 4.69826271e+150 1.8545451e+150
NL: Newton 7 ; 1.72842766e+150 6.822622e+149
NL: Newton 8 ; 6.35865288e+149 2.50995087e+149
NL: Newton 9 ; 2.33926287e+149 9.23377165e+148
NL: Newton 10 ; 8.60583345e+148 3.39698039e+148
NL: NewtonSolver 'NL: Newton' on system 'circuit' failed to converge in 10 iterations.

# Inputs and outputs at start of iteration 'rank0:root._solve_nonlinear|0|NLRunOnce|0|circuit._solve_nonlinear|0':

# nonlinear inputs
{'circuit.D1.V_in': array([ 1.]),
 'circuit.D1.V_out': array([ 0.]),
 'circuit.R1.V_in': array([ 1.]),
 'circuit.R1.V_out': array([ 0.]),
 'circuit.R2.V_in': array([ 1.]),
 'circuit.R2.V_out': array([ 1.]),
 'circuit.n1.I_in:0': array([ 0.1]),
 'circuit.n1.I_out:0': array([ 1.]),
 'circuit.n1.I_out:1': array([ 1.]),
 'circuit.n2.I_in:0': array([ 1.]),
 'circuit.n2.I_out:0': array([ 1.])}

# nonlinear outputs
{'circuit.D1.I': array([ 1.]),
 'circuit.R1.I': array([ 1.]),
 'circuit.R2.I': array([ 1.]),
 'circuit.n1.V': array([ 10.]),
 'circuit.n2.V': array([ 0.001])}

Inputs and outputs at start of iteration have been saved to 'solver_errors.0.out'.

# Inputs and outputs at start of iteration 'rank0:root._solve_nonlinear|0|NLRunOnce|0|circuit._solve_nonlinear|0':

# nonlinear inputs
{'circuit.D1.V_in': array([ 1.]),
 'circuit.D1.V_out': array([ 0.]),
 'circuit.R1.V_in': array([ 1.]),
 'circuit.R1.V_out': array([ 0.]),
 'circuit.R2.V_in': array([ 1.]),
 'circuit.R2.V_out': array([ 1.]),
 'circuit.n1.I_in:0': array([ 0.1]),
 'circuit.n1.I_out:0': array([ 1.]),
 'circuit.n1.I_out:1': array([ 1.]),
 'circuit.n2.I_in:0': array([ 1.]),
 'circuit.n2.I_out:0': array([ 1.])}

# nonlinear outputs
{'circuit.D1.I': array([ 1.]),
 'circuit.R1.I': array([ 1.]),
 'circuit.R2.I': array([ 1.]),
 'circuit.n1.V': array([ 10.]),
 'circuit.n2.V': array([ 0.001])}