ExplicitComponent

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 \cdot y) - z \cdot 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)\)).

ExplicitComponent Methods

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.

class RectangleComp(ExplicitComponent):
    """
    A simple Explicit Component that computes the area of a rectangle.
    """
  • setup() :

    Declare input and output variables via add_input and add_output. Information such as variable names, sizes, units, and bounds are declared here. Also in setup, we 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(self):
        self.add_input('length', val=1.)
        self.add_input('width', val=1.)
        self.add_output('area', val=1.)
    
        self.declare_partials('*', '*')
    
  • compute(inputs, outputs) :

    Compute the outputs given the inputs.

    def compute(self, inputs, outputs):
        outputs['area'] = inputs['length'] * inputs['width']
    
  • compute_partials(inputs, partials) :

    [Optional] Compute the partials (partial derivatives) given the inputs.

    def compute_partials(self, inputs, partials):
        partials['area', 'length'] = inputs['width']
        partials['area', 'width'] = inputs['length']
    
  • 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 \cdot d\_{inputs}\), where \(J\) is the partial derivative Jacobian. If mode is 'rev', this method must compute \(d\_{inputs} = J^T \cdot 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']
    
  • compute_multi_jacvec_product(self, inputs, d_inputs, d_outputs, mode): :

    [Optional] Provide the partial derivatives as a matrix-matrix product. If mode is 'fwd', this method must compute \(d\_{outputs} = J \cdot d\_{inputs}\), where \(J\) is the partial derivative Jacobian, and where both d_outputs and d_inputs are matrices instead of vectors. If mode is 'rev', this method must similarly compute \(d\_{inputs} = J^T \cdot d\_{outputs}\). Note that in this case, the code in compute_multi_jacvec_product is the same as the code in compute_jacvec_product. This won’t always be the case, depending on the math operations that are required for multiplying by a matrix versus multiplying by a vector.

    This method is only used when “vectorize_derivs” is set to True on a design variable or response.

    def compute_multi_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 three are optional, because the class can implement compute_partials, one or both of compute_jacvec_product and compute_multi_jacvec_product, or neither if the user wants to use the finite-difference or complex-step method.