Unit Testing Partial Derivatives

If you want to check the implementations of a Component’s partial derivatives as part of a unit test, you can make use of a custom assert function, assert_check_partials.

openmdao.utils.assert_utils.assert_check_partials(data, atol=1e-06, rtol=1e-06)[source]

Raise assertion if any entry from the return from check_partials is above a tolerance.

Parameters
datadict of dicts of dicts
First key:

is the component name;

Second key:

is the (output, input) tuple of strings;

Third key:

is one of [‘rel error’, ‘abs error’, ‘magnitude’, ‘J_fd’, ‘J_fwd’, ‘J_rev’];

For ‘rel error’, ‘abs error’, ‘magnitude’ the value is: A tuple containing norms for

forward - fd, adjoint - fd, forward - adjoint.

For ‘J_fd’, ‘J_fwd’, ‘J_rev’ the value is: A numpy array representing the computed

Jacobian for the three different methods of computation.

atolfloat

Absolute error. Default is 1e-6.

rtolfloat

Relative error. Default is 1e-6.

In your unit test, after calling check_partials on a Component, you can call the assert_check_partials function with the returned value from check_partials.

Usage

In the following code, compute_partials is intentionally coded incorrectly to demonstrate how assert_check_partials can be used to detect this kind of error.

import numpy as np
import openmdao.api as om
from openmdao.utils.assert_utils import assert_check_partials

class BrokenDerivComp(om.ExplicitComponent):
    def setup(self):
        self.add_input('x1', 3.0)
        self.add_input('x2', 5.0)

        self.add_output('y', 5.5)

    def setup_partials(self):
        self.declare_partials(of='*', wrt='*')

    def compute(self, inputs, outputs):
        """ Compute outputs. """
        outputs['y'] = 3.0 * inputs['x1'] + 4.0 * inputs['x2']

    def compute_partials(self, inputs, partials):
        """Intentionally incorrect derivative."""
        J = partials
        J['y', 'x1'] = np.array([4.0])
        J['y', 'x2'] = np.array([40])


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

prob.set_solver_print(level=0)

prob.setup()
prob.run_model()

data = prob.check_partials(out_stream=None)
print(data)

try:
    assert_check_partials(data, atol=1.e-6, rtol=1.e-6)
except ValueError as err:
    print(str(err))
{'': {('y', 'x1'): {'J_fwd': array([[4.]]), 'J_fd': array([[3.]]), 'abs error': ErrorTuple(forward=1.0, reverse=None, forward_reverse=None), 'magnitude': MagnitudeTuple(forward=4.0, reverse=None, fd=3.0), 'rel error': ErrorTuple(forward=0.3333333333333333, reverse=None, forward_reverse=None)}, ('y', 'x2'): {'J_fwd': array([[40.]]), 'J_fd': array([[4.]]), 'abs error': ErrorTuple(forward=36.0, reverse=None, forward_reverse=None), 'magnitude': MagnitudeTuple(forward=40.0, reverse=None, fd=4.0), 'rel error': ErrorTuple(forward=9.0, reverse=None, forward_reverse=None)}}}

==============================================================
Assert Check Partials failed for the following Components
with absolute tolerance = 1e-06 and relative tolerance = 1e-06
==============================================================
------------
Component: 
------------
< output > wrt < variable > | abs/rel | norm   | norm value        
--------------------------- | ------- | ------ | ------------------
y wrt x1                    | abs     | fwd-fd | 1.0               
y wrt x1                    | rel     | fwd-fd | 0.3333333333333333
y wrt x2                    | abs     | fwd-fd | 36.0              
y wrt x2                    | rel     | fwd-fd | 9.0