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))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[2], line 31
     27 prob.model = BrokenDerivComp()
     29 prob.set_solver_print(level=0)
---> 31 prob.setup()
     32 prob.run_model()
     34 data = prob.check_partials(out_stream=None)

File /usr/share/miniconda/envs/test/lib/python3.11/site-packages/openmdao/core/problem.py:888, in Problem.setup(self, check, logger, mode, force_alloc_complex, distributed_vector_class, local_vector_class, derivatives)
    885         warn_deprecation(msg)
    887 if not isinstance(self.model, Group):
--> 888     raise TypeError("The model for this Problem is of type "
    889                     f"'{self.model.__class__.__name__}'. "
    890                     "The model must be a Group or a sub-class of Group.")
    892 # A distributed vector type is required for MPI
    893 if comm.size > 1:

TypeError: The model for this Problem is of type 'BrokenDerivComp'. The model must be a Group or a sub-class of Group.