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.add_subsystem('comp', 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))
{'comp': {('y', 'x1'): {'J_fwd': array([[4.]]), 'J_fd': array([[3.]]), 'abs error': ErrorTuple(forward=1.0000000004688445, reverse=None, forward_reverse=None), 'rel error': ErrorTuple(forward=0.3333333335417087, reverse=None, forward_reverse=None), 'magnitude': MagnitudeTuple(forward=4.0, reverse=None, fd=2.9999999995311555)}, ('y', 'x2'): {'J_fwd': array([[40.]]), 'J_fd': array([[4.]]), 'abs error': ErrorTuple(forward=35.99999999944089, reverse=None, forward_reverse=None), 'rel error': ErrorTuple(forward=8.99999999860222, reverse=None, forward_reverse=None), 'magnitude': MagnitudeTuple(forward=40.0, reverse=None, fd=4.000000000559112)}}}
==============================================================
Assert Check Partials failed for the following Components
with absolute tolerance = 1e-06 and relative tolerance = 1e-06
==============================================================
----------------
Component: comp
----------------
< output > wrt < variable > | abs/rel | norm | norm value
--------------------------- | ------- | ------ | ------------------
y wrt x1 | abs | fwd-fd | 1.0000000004688445
y wrt x1 | rel | fwd-fd | 0.3333333335417087
y wrt x2 | abs | fwd-fd | 35.99999999944089
y wrt x2 | rel | fwd-fd | 8.99999999860222