Explicit variables are those that are computed as an explicit function of other variables. For instance, \(z\) would be an explicit variable, given \(z=sin(y)\), while \(y\) would not be, given that it is defined implicitly by the nonlinear equation \(cos(x⋅y)−z⋅y=0\).
In OpenMDAO, explicit variables are defined by writing a class that inherits from the ExplicitComponent class. The explicit variables would be considered outputs while the variables on which they depend would be considered inputs (e.g., \(y\) in \(z=sin(y)\)).
The implementation of each method will be illustrated using a simple explicit component that computes the output area as a function of inputs length and width.
import openmdao.api as om class RectangleComp(om.ExplicitComponent): """ A simple Explicit Component that computes the area of a rectangle. """
[fv-az520-749:46425] mca_base_component_repository_open: unable to open mca_btl_openib: librdmacm.so.1: cannot open shared object file: No such file or directory (ignored)
Declare input and output variables via
add_output. Information such as variable names, sizes, units, and bounds are declared here.
def setup(self): self.add_input('length', val=1.) self.add_input('width', val=1.) self.add_output('area', val=1.)
Declare partial derivatives that this component provides, using wild cards to say that this component provides derivatives of all outputs with respect to all inputs.
def setup_partials(self): self.declare_partials('*', '*')
outputs given the
def compute(self, inputs, outputs): outputs['area'] = inputs['length'] * inputs['width']
[Optional] Compute the
partials (partial derivatives) given the
def compute_partials(self, inputs, partials): partials['area', 'length'] = inputs['width'] partials['area', 'width'] = inputs['length']
The Matrix-Free API: Providing derivatives as a matrix-vector product#
compute_jacvec_product(inputs, d_inputs, d_outputs, mode):
[Optional] Provide the partial derivatives as a matrix-vector product. If
mode is ‘fwd’, this method must compute \(d\_outputs=J⋅d\_inputs\), where J is the partial derivative Jacobian. If
mode is ‘rev’, this method must compute \(d\_inputs=J^T⋅d\_outputs\).
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): if mode == 'fwd': if 'area' in d_outputs: if 'length' in d_inputs: d_outputs['area'] += inputs['width'] * d_inputs['length'] if 'width' in d_inputs: d_outputs['area'] += inputs['length'] * d_inputs['width'] elif mode == 'rev': if 'area' in d_outputs: if 'length' in d_inputs: d_inputs['length'] += inputs['width'] * d_outputs['area'] if 'width' in d_inputs: d_inputs['width'] += inputs['length'] * d_outputs['area']
Note that the last two are optional, because the class can implement
compute_jacvec_product, or neither if the user wants to use the finite-difference or complex-step method.