Changing Check Settings for FD or CS

Changing Settings for Inputs on a Specific Component

You can change the settings for the approximation schemes that will be used to compare with your component’s derivatives by calling the set_check_partial_options method.

Component.set_check_partial_options(wrt, method='fd', form=None, step=None, step_calc=None, minimum_step=None, directional=False)[source]

Set options that will be used for checking partial derivatives.

Parameters
wrtstr or list of str

The name or names of the variables that derivatives are taken with respect to. This can contain the name of any input or output variable. May also contain a glob pattern.

methodstr

Method for check: “fd” for finite difference, “cs” for complex step.

formstr

Finite difference form for check, can be “forward”, “central”, or “backward”. Leave undeclared to keep unchanged from previous or default value.

stepfloat

Step size for finite difference check. Leave undeclared to keep unchanged from previous or default value.

step_calcstr

Step type for computing the size of the finite difference step. It can be ‘abs’ for absolute, ‘rel_avg’ for a size relative to the absolute value of the vector input, or ‘rel_element’ for a size relative to each value in the vector input. In addition, it can be ‘rel_legacy’ for a size relative to the norm of the vector. For backwards compatibilty, it can be ‘rel’, which currently defaults to ‘rel_legacy’, but in the future will default to ‘rel_avg’. Defaults to None, in which case the approximation method provides its default value.

minimum_stepfloat

Minimum step size allowed when using one of the relative step_calc options.

directionalbool

Set to True to perform a single directional derivative for each vector variable in the pattern named in wrt.

Note

If you want to use method=”cs”, then you must also pass force_alloc_complex=True to setup. See the example below.

This allows custom tailoring of the approximation settings on a variable basis.

Usage Examples

import openmdao.api as om
from openmdao.core.tests.test_check_derivs import ParaboloidTricky
from openmdao.test_suite.components.paraboloid_mat_vec import ParaboloidMatVec

prob = om.Problem()

comp = prob.model.add_subsystem('comp', ParaboloidTricky())
prob.model.add_subsystem('comp2', ParaboloidMatVec())

prob.model.connect('comp.f_xy', 'comp2.x')

prob.set_solver_print(level=0)

comp.set_check_partial_options(wrt='*', step=1e-2)

prob.setup()
prob.run_model()

prob.check_partials(compact_print=True)
----------------------------------
Component: ParaboloidTricky 'comp'
----------------------------------
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 6.0000e-07 | n/a        | 6.0000e-07 | 4.9644e-14 | n/a        | n/a        | 8.2740e-08 | n/a        | n/a       
'f_xy'     wrt 'y'          | 8.0000e-07 | n/a        | 8.0000e-07 | 6.6192e-14 | n/a        | n/a        | 8.2740e-08 | n/a        | n/a       
-----------------------------------
Component: ParaboloidMatVec 'comp2'
-----------------------------------
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 3.8000e+01 | 3.8000e+01 | 3.8000e+01 | 1.0409e-06 | 1.0409e-06 | 0.0000e+00 | 2.7393e-08 | 2.7393e-08 | 0.0000e+00 >ABS_TOL
'f_xy'     wrt 'y'          | 3.0000e+01 | 3.0000e+01 | 3.0000e+01 | 1.0043e-06 | 1.0043e-06 | 0.0000e+00 | 3.3476e-08 | 3.3476e-08 | 0.0000e+00 >ABS_TOL

#################################################################
Sub Jacobian with Largest Relative Error: ParaboloidTricky 'comp'
#################################################################
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------
'f_xy'     wrt 'x'          | 6.0000e-07 | n/a        | 6.0000e-07 | 4.9644e-14 | n/a        | n/a        | 8.2740e-08 | n/a        | n/a       
{'comp': {('f_xy', 'x'): {'J_fwd': array([[-6.e-07]]),
   'J_fd': array([[-6.0000005e-07]]),
   'abs error': ErrorTuple(forward=4.964422262660536e-14, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=6e-07, reverse=None, fd=6.000000496442226e-07),
   'rel error': ErrorTuple(forward=8.274036419837383e-08, reverse=None, forward_reverse=None)},
  ('f_xy', 'y'): {'J_fwd': array([[8.e-07]]),
   'J_fd': array([[8.00000066e-07]]),
   'abs error': ErrorTuple(forward=6.619229683547381e-14, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=8e-07, reverse=None, fd=8.000000661922968e-07),
   'rel error': ErrorTuple(forward=8.274036419837383e-08, reverse=None, forward_reverse=None)}},
 'comp2': {('f_xy', 'x'): {'J_fwd': array([[38.]]),
   'J_rev': array([[38.]]),
   'J_fd': array([[38.00000104]]),
   'abs error': ErrorTuple(forward=1.0409276001155376e-06, reverse=1.0409276001155376e-06, forward_reverse=0.0),
   'magnitude': MagnitudeTuple(forward=38.0, reverse=38.0, fd=38.0000010409276),
   'rel error': ErrorTuple(forward=2.739283083162063e-08, reverse=2.739283083162063e-08, forward_reverse=0.0)},
  ('f_xy', 'y'): {'J_fwd': array([[30.]]),
   'J_rev': array([[30.]]),
   'J_fd': array([[30.000001]]),
   'abs error': ErrorTuple(forward=1.0042822395917028e-06, reverse=1.0042822395917028e-06, forward_reverse=0.0),
   'magnitude': MagnitudeTuple(forward=30.0, reverse=30.0, fd=30.00000100428224),
   'rel error': ErrorTuple(forward=3.3476073532409224e-08, reverse=3.3476073532409224e-08, forward_reverse=0.0)}}}

Here, we show how to set the method. In this case, we use complex step on TrickyParaboloid because the finite difference is less accurate.


Note

You need to set force_alloc_complex to True during setup to utilize complex step during a check.

import openmdao.api as om
from openmdao.core.tests.test_check_derivs import ParaboloidTricky
from openmdao.test_suite.components.paraboloid_mat_vec import ParaboloidMatVec

prob = om.Problem()

comp = prob.model.add_subsystem('comp', ParaboloidTricky())
prob.model.add_subsystem('comp2', ParaboloidMatVec())

prob.model.connect('comp.f_xy', 'comp2.x')

prob.set_solver_print(level=0)

comp.set_check_partial_options(wrt='*', method='cs')

prob.setup(force_alloc_complex=True)
prob.run_model()

prob.check_partials(compact_print=True)
----------------------------------
Component: ParaboloidTricky 'comp'
----------------------------------
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 6.0000e-07 | n/a        | 6.0000e-07 | 0.0000e+00 | n/a        | n/a        | 0.0000e+00 | n/a        | n/a       
'f_xy'     wrt 'y'          | 8.0000e-07 | n/a        | 8.0000e-07 | 1.0588e-22 | n/a        | n/a        | 1.3235e-16 | n/a        | n/a       
-----------------------------------
Component: ParaboloidMatVec 'comp2'
-----------------------------------
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 3.8000e+01 | 3.8000e+01 | 3.8000e+01 | 1.0409e-06 | 1.0409e-06 | 0.0000e+00 | 2.7393e-08 | 2.7393e-08 | 0.0000e+00 >ABS_TOL
'f_xy'     wrt 'y'          | 3.0000e+01 | 3.0000e+01 | 3.0000e+01 | 1.0043e-06 | 1.0043e-06 | 0.0000e+00 | 3.3476e-08 | 3.3476e-08 | 0.0000e+00 >ABS_TOL

##################################################################
Sub Jacobian with Largest Relative Error: ParaboloidMatVec 'comp2'
##################################################################
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------
'f_xy'     wrt 'y'          | 3.0000e+01 | 3.0000e+01 | 3.0000e+01 | 1.0043e-06 | 1.0043e-06 | 0.0000e+00 | 3.3476e-08 | 3.3476e-08 | 0.0000e+00
{'comp': {('f_xy', 'x'): {'J_fwd': array([[-6.e-07]]),
   'J_fd': array([[-6.e-07]]),
   'abs error': ErrorTuple(forward=0.0, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=6e-07, reverse=None, fd=6e-07),
   'rel error': ErrorTuple(forward=0.0, reverse=None, forward_reverse=None)},
  ('f_xy', 'y'): {'J_fwd': array([[8.e-07]]),
   'J_fd': array([[8.e-07]]),
   'abs error': ErrorTuple(forward=1.0587911840678754e-22, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=8e-07, reverse=None, fd=7.999999999999999e-07),
   'rel error': ErrorTuple(forward=1.3234889800848445e-16, reverse=None, forward_reverse=None)}},
 'comp2': {('f_xy', 'x'): {'J_fwd': array([[38.]]),
   'J_rev': array([[38.]]),
   'J_fd': array([[38.00000104]]),
   'abs error': ErrorTuple(forward=1.0409276001155376e-06, reverse=1.0409276001155376e-06, forward_reverse=0.0),
   'magnitude': MagnitudeTuple(forward=38.0, reverse=38.0, fd=38.0000010409276),
   'rel error': ErrorTuple(forward=2.739283083162063e-08, reverse=2.739283083162063e-08, forward_reverse=0.0)},
  ('f_xy', 'y'): {'J_fwd': array([[30.]]),
   'J_rev': array([[30.]]),
   'J_fd': array([[30.000001]]),
   'abs error': ErrorTuple(forward=1.0042822395917028e-06, reverse=1.0042822395917028e-06, forward_reverse=0.0),
   'magnitude': MagnitudeTuple(forward=30.0, reverse=30.0, fd=30.00000100428224),
   'rel error': ErrorTuple(forward=3.3476073532409224e-08, reverse=3.3476073532409224e-08, forward_reverse=0.0)}}}

Directional Derivatives

You can also specify that an input or set of inputs be checked using a directional derivative. For vector inputs, this means that, instead of calculating the derivative with respect to each element of the array, we calculate the derivative with respect to a linear combination of all array indices. For finite difference and complex step, the step value is applied simultaneously to all elements of the vector. This is a much quicker check because it only requires a single execution of the component for the variable rather than one for each element of the array.

import openmdao.api as om
from openmdao.test_suite.components.array_comp import ArrayComp

prob = om.Problem()
model = prob.model
mycomp = model.add_subsystem('mycomp', ArrayComp(), promotes=['*'])

prob.setup()
prob.run_model()

data = prob.check_partials()
-----------------------------
Component: ArrayComp 'mycomp'
-----------------------------
  mycomp: 'y1' wrt 'bb'
    Analytic Magnitude: 1.640884e+01
          Fd Magnitude: 1.640884e+01 (fd:forward)
    Absolute Error (Jan - Jfd) : 0.000000e+00

    Relative Error (Jan - Jfd) / Jfd : 0.000000e+00

    Raw Analytic Derivative (Jfor)
[[ 1.   6.  -1.   1. ]
 [ 3.   2.5  0.   4. ]
 [-2.   2.   8.  -5. ]
 [ 7.   4.   1.   6. ]]

    Raw FD Derivative (Jfd)
[[ 1.   6.  -1.   1. ]
 [ 3.   2.5  0.   4. ]
 [-2.   2.   8.  -5. ]
 [ 7.   4.   1.   6. ]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  mycomp: 'y1' wrt (d)'x1'
    Analytic Magnitude: 1.978004e+01
          Fd Magnitude: 1.978004e+01 (fd:forward)
    Absolute Error (Jan - Jfd) : 1.776357e-15

    Relative Error (Jan - Jfd) / Jfd : 8.980552e-17

    Raw Analytic Derivative (Jfor)
[[ 9. ]
 [14.5]
 [ 8. ]
 [ 6. ]]

    Directional FD Derivative (Jfd)
[[ 9. ]
 [14.5]
 [ 8. ]
 [ 6. ]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  mycomp: 'y1' wrt (d)'x2'
    Analytic Magnitude: 6.527413e+01
          Fd Magnitude: 6.527413e+01 (fd:forward)
    Absolute Error (Jan - Jfd) : 3.552714e-15

    Relative Error (Jan - Jfd) / Jfd : 5.442759e-17

    Raw Analytic Derivative (Jfor)
[[29.7 ]
 [47.85]
 [26.4 ]
 [19.8 ]]

    Directional FD Derivative (Jfd)
[[29.7 ]
 [47.85]
 [26.4 ]
 [19.8 ]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

If your component is matrix-free and you request directional derivatives, then your reverse mode derivatives will be verified using the dot product test described here.

import numpy as np

import openmdao.api as om

class ArrayCompMatrixFree(om.ExplicitComponent):

    def setup(self):

        J1 = np.array([[1.0, 3.0, -2.0, 7.0],
                        [6.0, 2.5, 2.0, 4.0],
                        [-1.0, 0.0, 8.0, 1.0],
                        [1.0, 4.0, -5.0, 6.0]])

        self.J1 = J1
        self.J2 = J1 * 3.3
        self.Jb = J1.T

        # Inputs
        self.add_input('x1', np.zeros([4]))
        self.add_input('x2', np.zeros([4]))
        self.add_input('bb', np.zeros([4]))

        # Outputs
        self.add_output('y1', np.zeros([4]))

        self.set_check_partial_options('*', directional=True)

    def compute(self, inputs, outputs):
        """
        Execution.
        """
        outputs['y1'] = self.J1.dot(inputs['x1']) + self.J2.dot(inputs['x2']) + self.Jb.dot(inputs['bb'])

    def compute_jacvec_product(self, inputs, dinputs, doutputs, mode):
        """Returns the product of the incoming vector with the Jacobian."""

        if mode == 'fwd':
            if 'x1' in dinputs:
                doutputs['y1'] += self.J1.dot(dinputs['x1'])
            if 'x2' in dinputs:
                doutputs['y1'] += self.J2.dot(dinputs['x2'])
            if 'bb' in dinputs:
                doutputs['y1'] += self.Jb.dot(dinputs['bb'])

        elif mode == 'rev':
            if 'x1' in dinputs:
                dinputs['x1'] += self.J1.T.dot(doutputs['y1'])
            if 'x2' in dinputs:
                dinputs['x2'] += self.J2.T.dot(doutputs['y1'])
            if 'bb' in dinputs:
                dinputs['bb'] += self.Jb.T.dot(doutputs['y1'])

prob = om.Problem()
model = prob.model
model.add_subsystem('mycomp', ArrayCompMatrixFree(), promotes=['*'])

prob.setup()
prob.run_model()

data = prob.check_partials()
---------------------------------------
Component: ArrayCompMatrixFree 'mycomp'
---------------------------------------
  mycomp: 'y1' wrt (d)'bb'
     Forward Magnitude: 7.274949e+00
          Fd Magnitude: 7.274949e+00 (fd:forward)
    Absolute Error (Jfor - Jfd) : 1.017536e-15
    Absolute Error (Jrev - Jfd  Dot Product Test) : 0.000000e+00
    Absolute Error (Jrev - Jfor Dot Product Test) : 0.000000e+00

    Relative Error (Jfor - Jfd) / Jfd : 1.398685e-16
    Relative Error (Jrev - Jfd  Dot Product Test) / Jfd : 0.000000e+00
    Relative Error (Jrev - Jfor Dot Product Test) / Jfd : 0.000000e+00

    Directional Forward Derivative (Jfor)
[[3.04982001]
 [1.88531044]
 [3.20878924]
 [5.45644151]]

    Directional FD Derivative (Jfd)
[[3.04982001]
 [1.88531044]
 [3.20878924]
 [5.45644151]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  mycomp: 'y1' wrt (d)'x1'
     Forward Magnitude: 5.596310e+00
          Fd Magnitude: 5.596310e+00 (fd:forward)
    Absolute Error (Jfor - Jfd) : 1.017536e-15
    Absolute Error (Jrev - Jfd  Dot Product Test) : 0.000000e+00
    Absolute Error (Jrev - Jfor Dot Product Test) : 0.000000e+00

    Relative Error (Jfor - Jfd) / Jfd : 1.818227e-16
    Relative Error (Jrev - Jfd  Dot Product Test) / Jfd : 0.000000e+00
    Relative Error (Jrev - Jfor Dot Product Test) / Jfd : 0.000000e+00

    Directional Forward Derivative (Jfor)
[[2.45403931]
 [4.02202726]
 [1.34586544]
 [2.70338976]]

    Directional FD Derivative (Jfd)
[[2.45403931]
 [4.02202726]
 [1.34586544]
 [2.70338976]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  mycomp: 'y1' wrt (d)'x2'
     Forward Magnitude: 2.712142e+01
          Fd Magnitude: 2.712142e+01 (fd:forward)
    Absolute Error (Jfor - Jfd) : 8.152394e-15
    Absolute Error (Jrev - Jfd  Dot Product Test) : -8.881784e-15
    Absolute Error (Jrev - Jfor Dot Product Test) : -1.776357e-15

    Relative Error (Jfor - Jfd) / Jfd : 3.005887e-16
    Relative Error (Jrev - Jfd  Dot Product Test) / Jfd : -3.274822e-16
    Relative Error (Jrev - Jfor Dot Product Test) / Jfd : -6.549645e-17

    Directional Forward Derivative (Jfor)
[[18.91843826]
 [ 2.9183692 ]
 [12.86599957]
 [14.26931695]]

    Directional FD Derivative (Jfd)
[[18.91843826]
 [ 2.9183692 ]
 [12.86599957]
 [14.26931695]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Changing Global Settings For Whole Model

You can change the settings globally for all approximations used for all components. This is done by passing in a value for any of the following arguments:

Name

Description

method

Method for check: “fd” for finite difference, “cs” for complex step.

form

Finite difference form for check, can be “forward”, “central”, or backward.

step

Step size for finite difference check.

step_calc

When “abs”, the step is absolute. A relative step can also be chosen with one of the values in table below.

minimum_step

Minimum allowable step when using one of the relative step_calc options.

If you need to scale the finite difference step by the variable’s magnitude, the following additional choices for “step_calc” are available:

step_calc

Step size is scaled by

“rel_avg”

Average absolute value of the vector.

“rel_element”

Absolute value of each vector element.

“rel_legacy”

Norm of the vector.

“rel”

Same as “rel_legacy” currently, but will become “rel_avg” in the OpenMDAO 3.12.0.

Note

The global check options take precedence over the ones defined on a component.

Usage Examples

import openmdao.api as om
from openmdao.core.tests.test_check_derivs import ParaboloidTricky
from openmdao.test_suite.components.paraboloid_mat_vec import ParaboloidMatVec

prob = om.Problem()

prob.model.add_subsystem('comp', ParaboloidTricky())
prob.model.add_subsystem('comp2', ParaboloidMatVec())

prob.model.connect('comp.f_xy', 'comp2.x')

prob.set_solver_print(level=0)

prob.setup()
prob.run_model()

prob.check_partials(step=1e-2, compact_print=True)
----------------------------------
Component: ParaboloidTricky 'comp'
----------------------------------
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 6.0000e-07 | n/a        | 6.0000e-07 | 4.9644e-14 | n/a        | n/a        | 8.2740e-08 | n/a        | n/a       
'f_xy'     wrt 'y'          | 8.0000e-07 | n/a        | 8.0000e-07 | 6.6192e-14 | n/a        | n/a        | 8.2740e-08 | n/a        | n/a       
-----------------------------------
Component: ParaboloidMatVec 'comp2'
-----------------------------------
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 3.8000e+01 | 3.8000e+01 | 3.8010e+01 | 1.0000e-02 | 1.0000e-02 | 0.0000e+00 | 2.6309e-04 | 2.6309e-04 | 0.0000e+00 >ABS_TOL >REL_TOL
'f_xy'     wrt 'y'          | 3.0000e+01 | 3.0000e+01 | 3.0010e+01 | 1.0000e-02 | 1.0000e-02 | 0.0000e+00 | 3.3322e-04 | 3.3322e-04 | 0.0000e+00 >ABS_TOL >REL_TOL

##################################################################
Sub Jacobian with Largest Relative Error: ParaboloidMatVec 'comp2'
##################################################################
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------
'f_xy'     wrt 'y'          | 3.0000e+01 | 3.0000e+01 | 3.0010e+01 | 1.0000e-02 | 1.0000e-02 | 0.0000e+00 | 3.3322e-04 | 3.3322e-04 | 0.0000e+00
{'comp': {('f_xy', 'x'): {'J_fwd': array([[-6.e-07]]),
   'J_fd': array([[-6.0000005e-07]]),
   'abs error': ErrorTuple(forward=4.964422262660536e-14, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=6e-07, reverse=None, fd=6.000000496442226e-07),
   'rel error': ErrorTuple(forward=8.274036419837383e-08, reverse=None, forward_reverse=None)},
  ('f_xy', 'y'): {'J_fwd': array([[8.e-07]]),
   'J_fd': array([[8.00000066e-07]]),
   'abs error': ErrorTuple(forward=6.619229683547381e-14, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=8e-07, reverse=None, fd=8.000000661922968e-07),
   'rel error': ErrorTuple(forward=8.274036419837383e-08, reverse=None, forward_reverse=None)}},
 'comp2': {('f_xy', 'x'): {'J_fwd': array([[38.]]),
   'J_rev': array([[38.]]),
   'J_fd': array([[38.01]]),
   'abs error': ErrorTuple(forward=0.010000000008403731, reverse=0.010000000008403731, forward_reverse=0.0),
   'magnitude': MagnitudeTuple(forward=38.0, reverse=38.0, fd=38.010000000008404),
   'rel error': ErrorTuple(forward=0.0002630886610997506, reverse=0.0002630886610997506, forward_reverse=0.0)},
  ('f_xy', 'y'): {'J_fwd': array([[30.]]),
   'J_rev': array([[30.]]),
   'J_fd': array([[30.01]]),
   'abs error': ErrorTuple(forward=0.010000000004311005, reverse=0.010000000004311005, forward_reverse=0.0),
   'magnitude': MagnitudeTuple(forward=30.0, reverse=30.0, fd=30.01000000000431),
   'rel error': ErrorTuple(forward=0.0003332222593905221, reverse=0.0003332222593905221, forward_reverse=0.0)}}}

Here is an example where we check our partials using complex step.

Note

You need to set force_alloc_complex to True during setup to utilize complex step during a check.

import openmdao.api as om
from openmdao.core.tests.test_check_derivs import ParaboloidTricky
from openmdao.test_suite.components.paraboloid_mat_vec import ParaboloidMatVec

prob = om.Problem()

prob.model.add_subsystem('comp', ParaboloidTricky())
prob.model.add_subsystem('comp2', ParaboloidMatVec())

prob.model.connect('comp.f_xy', 'comp2.x')

prob.set_solver_print(level=0)

prob.setup(force_alloc_complex=True)
prob.run_model()

prob.check_partials(method='cs', compact_print=True)
----------------------------------
Component: ParaboloidTricky 'comp'
----------------------------------
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 6.0000e-07 | n/a        | 6.0000e-07 | 0.0000e+00 | n/a        | n/a        | 0.0000e+00 | n/a        | n/a       
'f_xy'     wrt 'y'          | 8.0000e-07 | n/a        | 8.0000e-07 | 1.0588e-22 | n/a        | n/a        | 1.3235e-16 | n/a        | n/a       
-----------------------------------
Component: ParaboloidMatVec 'comp2'
-----------------------------------
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 3.8000e+01 | 3.8000e+01 | 3.8000e+01 | 0.0000e+00 | 0.0000e+00 | 0.0000e+00 | 0.0000e+00 | 0.0000e+00 | 0.0000e+00
'f_xy'     wrt 'y'          | 3.0000e+01 | 3.0000e+01 | 3.0000e+01 | 3.5527e-15 | 3.5527e-15 | 0.0000e+00 | 1.1842e-16 | 1.1842e-16 | 0.0000e+00

#################################################################
Sub Jacobian with Largest Relative Error: ParaboloidTricky 'comp'
#################################################################
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------
'f_xy'     wrt 'y'          | 8.0000e-07 | n/a        | 8.0000e-07 | 1.0588e-22 | n/a        | n/a        | 1.3235e-16 | n/a        | n/a       
{'comp': {('f_xy', 'x'): {'J_fwd': array([[-6.e-07]]),
   'J_fd': array([[-6.e-07]]),
   'abs error': ErrorTuple(forward=0.0, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=6e-07, reverse=None, fd=6e-07),
   'rel error': ErrorTuple(forward=0.0, reverse=None, forward_reverse=None)},
  ('f_xy', 'y'): {'J_fwd': array([[8.e-07]]),
   'J_fd': array([[8.e-07]]),
   'abs error': ErrorTuple(forward=1.0587911840678754e-22, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=8e-07, reverse=None, fd=7.999999999999999e-07),
   'rel error': ErrorTuple(forward=1.3234889800848445e-16, reverse=None, forward_reverse=None)}},
 'comp2': {('f_xy', 'x'): {'J_fwd': array([[38.]]),
   'J_rev': array([[38.]]),
   'J_fd': array([[38.]]),
   'abs error': ErrorTuple(forward=0.0, reverse=0.0, forward_reverse=0.0),
   'magnitude': MagnitudeTuple(forward=38.0, reverse=38.0, fd=38.0),
   'rel error': ErrorTuple(forward=0.0, reverse=0.0, forward_reverse=0.0)},
  ('f_xy', 'y'): {'J_fwd': array([[30.]]),
   'J_rev': array([[30.]]),
   'J_fd': array([[30.]]),
   'abs error': ErrorTuple(forward=3.552713678800501e-15, reverse=3.552713678800501e-15, forward_reverse=0.0),
   'magnitude': MagnitudeTuple(forward=30.0, reverse=30.0, fd=29.999999999999996),
   'rel error': ErrorTuple(forward=1.1842378929335005e-16, reverse=1.1842378929335005e-16, forward_reverse=0.0)}}}

In this example, we check our partials with finite difference, but we use central differencing for more accuracy.

import openmdao.api as om
from openmdao.core.tests.test_check_derivs import ParaboloidTricky
from openmdao.test_suite.components.paraboloid_mat_vec import ParaboloidMatVec

prob = om.Problem()

prob.model.add_subsystem('comp', ParaboloidTricky())
prob.model.add_subsystem('comp2', ParaboloidMatVec())

prob.model.connect('comp.f_xy', 'comp2.x')

prob.set_solver_print(level=0)

prob.setup()
prob.run_model()

prob.check_partials(form='central', compact_print=True)
----------------------------------
Component: ParaboloidTricky 'comp'
----------------------------------
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 6.0000e-07 | n/a        | 5.9863e-07 | 1.3677e-09 | n/a        | n/a        | 2.2848e-03 | n/a        | n/a        >REL_TOL
'f_xy'     wrt 'y'          | 8.0000e-07 | n/a        | 8.0114e-07 | 1.1369e-09 | n/a        | n/a        | 1.4192e-03 | n/a        | n/a        >REL_TOL
-----------------------------------
Component: ParaboloidMatVec 'comp2'
-----------------------------------
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 3.8000e+01 | 3.8000e+01 | 3.8000e+01 | 4.6168e-08 | 4.6168e-08 | 0.0000e+00 | 1.2149e-09 | 1.2149e-09 | 0.0000e+00
'f_xy'     wrt 'y'          | 3.0000e+01 | 3.0000e+01 | 3.0000e+01 | 9.5224e-09 | 9.5224e-09 | 0.0000e+00 | 3.1741e-10 | 3.1741e-10 | 0.0000e+00

#################################################################
Sub Jacobian with Largest Relative Error: ParaboloidTricky 'comp'
#################################################################
'<output>' wrt '<variable>' | fwd mag.   | rev mag.   | check mag. | a(fwd-chk) | a(rev-chk) | a(fwd-rev) | r(fwd-chk) | r(rev-chk) | r(fwd-rev)
------------------------------------------------------------------------------------------------------------------------------------------------
'f_xy'     wrt 'x'          | 6.0000e-07 | n/a        | 5.9863e-07 | 1.3677e-09 | n/a        | n/a        | 2.2848e-03 | n/a        | n/a       
{'comp': {('f_xy', 'x'): {'J_fwd': array([[-6.e-07]]),
   'J_fd': array([[-5.98632255e-07]]),
   'abs error': ErrorTuple(forward=1.3677451221155663e-09, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=6e-07, reverse=None, fd=5.986322548778844e-07),
   'rel error': ErrorTuple(forward=0.0022847835394278477, reverse=None, forward_reverse=None)},
  ('f_xy', 'y'): {'J_fwd': array([[8.e-07]]),
   'J_fd': array([[8.01136935e-07]]),
   'abs error': ErrorTuple(forward=1.1369345695129958e-09, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=8e-07, reverse=None, fd=8.01136934569513e-07),
   'rel error': ErrorTuple(forward=0.0014191513590918411, reverse=None, forward_reverse=None)}},
 'comp2': {('f_xy', 'x'): {'J_fwd': array([[38.]]),
   'J_rev': array([[38.]]),
   'J_fd': array([[38.00000005]]),
   'abs error': ErrorTuple(forward=4.616777005139738e-08, reverse=4.616777005139738e-08, forward_reverse=0.0),
   'magnitude': MagnitudeTuple(forward=38.0, reverse=38.0, fd=38.00000004616777),
   'rel error': ErrorTuple(forward=1.214941315665954e-09, reverse=1.214941315665954e-09, forward_reverse=0.0)},
  ('f_xy', 'y'): {'J_fwd': array([[30.]]),
   'J_rev': array([[30.]]),
   'J_fd': array([[30.00000001]]),
   'abs error': ErrorTuple(forward=9.522409527562559e-09, reverse=9.522409527562559e-09, forward_reverse=0.0),
   'magnitude': MagnitudeTuple(forward=30.0, reverse=30.0, fd=30.00000000952241),
   'rel error': ErrorTuple(forward=3.1741365081800053e-10, reverse=3.1741365081800053e-10, forward_reverse=0.0)}}}

In this example, we use a relative step-size. This is sometimes needed for casese where an input variable’s value can be in a wide range of orders of magnitude, and we don’t want the step size to become to small or large for it.

import openmdao.api as om
from openmdao.core.tests.test_check_derivs import ParaboloidTricky

prob = om.Problem()

prob.model.add_subsystem('comp', ParaboloidTricky())

prob.set_solver_print(level=0)

prob.setup()
prob.run_model()

prob.check_partials(step_calc='rel', compact_print=True)
----------------------------------
Component: ParaboloidTricky 'comp'
----------------------------------
'<output>' wrt '<variable>' | calc mag.  | check mag. | a(cal-chk) | r(cal-chk)
-------------------------------------------------------------------------------

'f_xy'     wrt 'x'          | 6.0000e-07 | 0.0000e+00 | 6.0000e-07 | 1.0000e+00 >REL_TOL
'f_xy'     wrt 'y'          | 8.0000e-07 | 0.0000e+00 | 8.0000e-07 | 1.0000e+00 >REL_TOL

#################################################################
Sub Jacobian with Largest Relative Error: ParaboloidTricky 'comp'
#################################################################
'<output>' wrt '<variable>' | calc mag.  | check mag. | a(cal-chk) | r(cal-chk)
-------------------------------------------------------------------------------
'f_xy'     wrt 'x'          | 6.0000e-07 | 0.0000e+00 | 6.0000e-07 | 1.0000e+00
/usr/share/miniconda/envs/test/lib/python3.8/site-packages/openmdao/approximation_schemes/finite_difference.py:135: OMDeprecationWarning:When using 'rel' as the step_calc, the fd stepsize is currently scaled by the norm of the vector variable. This is not ideal for larger vectors, and this behavior is being changed in OpenMDAO 3.12.0. To preserve the older way of doing this calculation, set step_calc to 'rel_legacy'.
{'comp': {('f_xy', 'x'): {'J_fwd': array([[-6.e-07]]),
   'J_fd': array([[0.]]),
   'abs error': ErrorTuple(forward=6e-07, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=6e-07, reverse=None, fd=0.0),
   'rel error': ErrorTuple(forward=1.0, reverse=None, forward_reverse=None)},
  ('f_xy', 'y'): {'J_fwd': array([[8.e-07]]),
   'J_fd': array([[0.]]),
   'abs error': ErrorTuple(forward=8e-07, reverse=None, forward_reverse=None),
   'magnitude': MagnitudeTuple(forward=8e-07, reverse=None, fd=0.0),
   'rel error': ErrorTuple(forward=1.0, reverse=None, forward_reverse=None)}}}

Finally, in this example, we have a vector input whose elements differ by several orders of magnitude, and whose middle element is sometimes zero. If you try to scale the step size by a variable that is zero, you end up with a step size equal to zero, which causes division by zero during computation. To prevent this, OpenMDAO defines a minimum step size of 1e-12. You can change this value by setting the minimum_step argument to a new value whenever you set the step_calc argument. Here, we also use the ‘rel_element’ step_calc so that each element computes its own step size relative to the magnitude of that single element.

import numpy as np

import openmdao.api as om


class FDComp(om.ExplicitComponent):

    def initialize(self):
        self.options.declare('vec_size', types=int, default=1)

    def setup(self):
        nn = self.options['vec_size']

        self.add_input('x_element', np.ones((nn, )))
        self.add_output('y', np.ones((nn, )))

    def setup_partials(self):
        self.declare_partials('*', 'x_element')

        self.set_check_partial_options('x_element', method='fd', step_calc='rel_element', minimum_step=1e-15)

    def compute(self, inputs, outputs):
        x = inputs['x_element']
        outputs['y'] = 0.5 * x ** 2

    def compute_partials(self, inputs, partials):
        x = inputs['x_element']
        partials['y', 'x_element'] = np.diag(x)


prob = om.Problem()
model = prob.model

model.add_subsystem('comp', FDComp(vec_size=3))

prob.setup(force_alloc_complex=True)

x = np.array([1e10, 0.0, 1e-10])
prob.set_val('comp.x_element', x)

prob.run_model()

totals = prob.check_partials()
------------------------
Component: FDComp 'comp'
------------------------
  comp: 'y' wrt 'x_element'
    Analytic Magnitude: 1.000000e+10
          Fd Magnitude: 1.000001e+10 (fd:forward)
    Absolute Error (Jan - Jfd) : 5.000397e+03 *

    Relative Error (Jan - Jfd) / Jfd : 5.000394e-07

    Raw Analytic Derivative (Jfor)
[[1.e+10 0.e+00 0.e+00]
 [0.e+00 0.e+00 0.e+00]
 [0.e+00 0.e+00 1.e-10]]

    Raw FD Derivative (Jfd)
[[1.0000005e+10 0.0000000e+00 0.0000000e+00]
 [0.0000000e+00 5.0000000e-16 0.0000000e+00]
 [0.0000000e+00 0.0000000e+00 1.0000050e-10]]

 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -