ExecComp

ExecComp is a component that provides a shortcut for building an ExplicitComponent that represents a set of simple mathematical relationships between inputs and outputs. The ExecComp automatically takes care of all of the component API methods, so you just need to instantiate it with an equation or a list of equations.

ExecComp Options

Option

Default

Acceptable Values

Acceptable Types

Description

distributed

False

[True, False]

[‘bool’]

True if the component has variables that are distributed across multiple processes.

has_diag_partials

False

[True, False]

[‘bool’]

If True, treat all array/array partials as diagonal if both arrays have size > 1. All arrays with size > 1 must have the same flattened size or an exception will be raised.

shape

N/A

N/A

[‘int’, ‘tuple’, ‘list’]

Shape to be assigned to all variables in this component. Default is None, which means shape may be provided for variables individually.

shape_by_conn

False

[True, False]

[‘bool’]

If True, shape all inputs and outputs based on their connection. Default is False.

units

N/A

N/A

[‘str’]

Units to be assigned to all variables in this component. Default is None, which means units may be provided for variables individually.

ExecComp Constructor

The call signature for the ExecComp constructor is:

ExecComp.__init__(exprs=[], **kwargs)[source]

Create a <Component> using only an expression string.

Given a list of assignment statements, this component creates input and output variables at construction time. All variables appearing on the left-hand side of an assignment are outputs, and the rest are inputs. Each variable is assumed to be of type float unless the initial value for that variable is supplied in **kwargs. Derivatives are calculated using complex step.

The following functions are available for use in expressions:

Function

Description

abs(x)

Absolute value of x

acos(x)

Inverse cosine of x

acosh(x)

Inverse hyperbolic cosine of x

arange(start, stop, step)

Array creation

arccos(x)

Inverse cosine of x

arccosh(x)

Inverse hyperbolic cosine of x

arcsin(x)

Inverse sine of x

arcsinh(x)

Inverse hyperbolic sine of x

arctan(x)

Inverse tangent of x

arctan2(y, x)

4-quadrant arctangent function of y and x

asin(x)

Inverse sine of x

asinh(x)

Inverse hyperbolic sine of x

atan(x)

Inverse tangent of x

cos(x)

Cosine of x

cosh(x)

Hyperbolic cosine of x

dot(x, y)

Dot product of x and y

e

Euler’s number

erf(x)

Error function

erfc(x)

Complementary error function

exp(x)

Exponential function

expm1(x)

exp(x) - 1

factorial(x)

Factorial of all numbers in x (DEPRECATED, not available with SciPy >=1.5)

fmax(x, y)

Element-wise maximum of x and y

fmin(x, y)

Element-wise minimum of x and y

inner(x, y)

Inner product of arrays x and y

isinf(x)

Element-wise detection of np.inf

isnan(x)

Element-wise detection of np.nan

kron(x, y)

Kronecker product of arrays x and y

linspace(x, y, N)

Numpy linear spaced array creation

log(x)

Natural logarithm of x

log10(x)

Base-10 logarithm of x

log1p(x)

log(1+x)

matmul(x, y)

Matrix multiplication of x and y

maximum(x, y)

Element-wise maximum of x and y

minimum(x, y)

Element-wise minimum of x and y

ones(N)

Create an array of ones

outer(x, y)

Outer product of x and y

pi

Pi

power(x, y)

Element-wise x**y

prod(x)

The product of all elements in x

sin(x)

Sine of x

sinh(x)

Hyperbolic sine of x

sum(x)

The sum of all elements in x

tan(x)

Tangent of x

tanh(x)

Hyperbolic tangent of x

tensordot(x, y)

Tensor dot product of x and y

zeros(N)

Create an array of zeros

Parameters
exprsstr, tuple of str or list of str

An assignment statement or iter of them. These express how the outputs are calculated based on the inputs. In addition to standard Python operators, a subset of numpy and scipy functions is supported.

**kwargsdict of named args

Initial values of variables can be set by setting a named arg with the var name. If the value is a dict it is assumed to contain metadata. To set the initial value in addition to other metadata, assign the initial value to the ‘value’ entry of the dict.

Notes

If a variable has an initial value that is anything other than 1.0, either because it has a different type than float or just because its initial value is != 1.0, you must use a keyword arg to set the initial value. For example, let’s say we have an ExecComp that takes an array ‘x’ as input and outputs a float variable ‘y’ which is the sum of the entries in ‘x’.

import numpy
import openmdao.api as om
excomp = om.ExecComp('y=sum(x)', x=numpy.ones(10, dtype=float))

In this example, ‘y’ would be assumed to be the default type of float and would be given the default initial value of 1.0, while ‘x’ would be initialized with a size 10 float array of ones.

If you want to assign certain metadata for ‘x’ in addition to its initial value, you can do it as follows:

excomp = ExecComp('y=sum(x)',
                  x={'value': numpy.ones(10, dtype=float),
                     'units': 'ft'})

ExecComp Variable Metadata

The values of the kwargs can be dicts which define the initial value for the variables along with other metadata. For example,

ExecComp('xdot=x/t', x={'units': 'ft'}, t={'units': 's'}, xdot={'units': 'ft/s')

Here is a list of the possible metadata that can be assigned to a variable in this way. The Applies To column indicates whether the metadata is appropriate for input variables, output variables, or both.

Name

Description

Valid Types

Applies To

Default

value

Initial value in user-defined units

float, list, tuple, ndarray

input & output

1

shape

Variable shape, only needed if not an array

int, tuple, list, None

input & output

None

shape_by_conn

Determine variable shape based on its connection

bool

input & output

False

copy_shape

Determine variable shape based on named variable

str

input & output

None

units

Units of variable

str, None

input & output

None

desc

Description of variable

str

input & output

“”

res_units

Units of residuals

str, None

output

None

ref

Value of variable when scaled value is 1

float, ndarray

output

1

ref0

Value of variable when scaled value is 0

float, ndarray

output

0

res_ref

Value of residual when scaled value is 1

float, ndarray

output

1

lower

Lower bound of variable

float, list, tuple, ndarray, Iterable, None

output

None

upper

Lower bound of variable

float, list, tuple, ndarray, Iterable, None

output

None

src_indices

Global indices of the variable

int, list of ints, tuple of ints, int ndarray, Iterable, None

input

None

flat_src_indices

If True, src_indices are indices into flattened source

bool

input

None

tags

Used to tag variables for later filtering

str, list of strs

input & output

None

These metadata are passed to the Component methods add_input and add_output. For more information about these metadata, see the documentation for the arguments to these Component methods:

Registering User Functions

To get your own functions added to the internal namespace of ExecComp so you can call them from within an ExecComp expression, you can use the ExecComp.register function.

classmethod ExecComp.register(name, callable_obj, complex_safe)[source]

Register a callable to be usable within ExecComp expressions.

Parameters
namestr

Name of the callable.

callable_objcallable

The callable.

complex_safebool

If True, the given callable works correctly with complex numbers.

Note that you’re required, when registering a new function, to indicate whether that function is complex safe or not.

ExecComp Example: Simple

For example, here is a simple component that takes the input and adds one to it.

import openmdao.api as om

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

model.add_subsystem('comp', om.ExecComp('y=x+1.'))

model.set_input_defaults('comp.x', 2.0)

prob.setup()

prob.set_solver_print(level=0)
prob.run_model()

print(prob.get_val('comp.y'))
[3.]

ExecComp Example: Multiple Outputs

You can also create an ExecComp with multiple outputs by placing the expressions in a list.

import openmdao.api as om

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

model.add_subsystem('comp', om.ExecComp(['y1=x+1.', 'y2=x-1.']), promotes=['x'])

prob.setup()

prob.set_val('x', 2.0)

prob.set_solver_print(level=0)
prob.run_model()

print(prob.get_val('comp.y1'))
[3.]
print(prob.get_val('comp.y2'))
[1.]

ExecComp Example: Arrays

You can declare an ExecComp with arrays for inputs and outputs, but when you do, you must also pass in a correctly-sized array as an argument to the ExecComp call, or set the ‘shape’ metadata for that variable as described earlier. If specifying the value directly, it can be the initial value in the case of unconnected inputs, or just an empty array with the correct size.

import numpy as np

import openmdao.api as om

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

model.add_subsystem('comp', om.ExecComp('y=x[1]',
                                        x=np.array([1., 2., 3.]),
                                        y=0.0))

prob.setup()

prob.set_solver_print(level=0)
prob.run_model()

print(prob.get_val('comp.y'))
[2.]

ExecComp Example: Math Functions

Functions from the math library are available for use in the expression strings.

import numpy as np

import openmdao.api as om

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

model.add_subsystem('comp', om.ExecComp('z = sin(x)**2 + cos(y)**2'))

prob.setup()

prob.set_val('comp.x', np.pi/2.0)
prob.set_val('comp.y', np.pi/2.0)

prob.set_solver_print(level=0)
prob.run_model()

print(prob.get_val('comp.z'))
[1.]

ExecComp Example: Variable Properties

You can also declare properties like ‘units’, ‘upper’, or ‘lower’ on the inputs and outputs. In this example we declare all our inputs to be inches to trigger conversion from a variable expressed in feet in one connection source.

import openmdao.api as om

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

model.add_subsystem('comp', om.ExecComp('z=x+y',
                                        x={'value': 0.0, 'units': 'inch'},
                                        y={'value': 0.0, 'units': 'inch'},
                                        z={'value': 0.0, 'units': 'inch'}))

prob.setup()

prob.set_val('comp.x', 12.0, units='inch')
prob.set_val('comp.y', 1.0, units='ft')

prob.set_solver_print(level=0)
prob.run_model()

print(prob.get_val('comp.z'))
[24.]

ExecComp Example: Diagonal Partials

If all of your ExecComp’s array inputs and array outputs are the same size and happen to have diagonal partials, you can make computation of derivatives for your ExecComp faster by specifying a has_diag_partials=True argument to __init__ or via the component options. This will cause the ExecComp to solve for its partials by complex stepping all entries of an array input at once instead of looping over each entry individually.

import numpy as np
import openmdao.api as om

p = om.Problem()
model = p.model

model.add_subsystem('comp', om.ExecComp('y=3.0*x + 2.5',
                                        has_diag_partials=True,
                                        x=np.ones(5), y=np.ones(5)))

p.setup()

p.set_val('comp.x', np.ones(5))

p.run_model()

J = p.compute_totals(of=['comp.y'], wrt=['comp.x'], return_format='array')

print(J)
[[ 3. -0. -0. -0. -0.]
 [-0.  3. -0. -0. -0.]
 [-0. -0.  3. -0. -0.]
 [-0. -0. -0.  3. -0.]
 [-0. -0. -0. -0.  3.]]

ExecComp Example: Options

Other options that can apply to all the variables in the component are variable shape and units. These can also be set as a keyword argument in the constructor or via the component options. In the following example the variables all share the same shape, which is specified in the constructor, and common units that are specified by setting the option.

import openmdao.api as om

model = om.Group()

xcomp = model.add_subsystem('comp', om.ExecComp('y=2*x', shape=(2,)))

xcomp.options['units'] = 'm'

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

prob.set_val('comp.x', [100., 200.], units='cm')

prob.run_model()

print(prob.get_val('comp.y'))
[2. 4.]

ExecComp Example: User function registration

If the function is complex safe, then you don’t need to do anything differently than you would for any other ExecComp.

import openmdao.api as om

om.ExecComp.register("myfunc", lambda x: x * x, complex_safe=True)
p = om.Problem()
comp = p.model.add_subsystem("comp", om.ExecComp("y = 2 * myfunc(x)"))

p.setup()
p.run_model()
J = p.compute_totals(of=['comp.y'], wrt=['comp.x'])
print(J['comp.y', 'comp.x'][0][0])
4.0

ExecComp Example: Complex unsafe user function registration

If the function isn’t complex safe, then derivatives involving that function will have to be computed using finite difference instead of complex step. The way to specify that fd should be used for a given derivative is to call declare_partials.

import openmdao.api as om

# the following function isn't really complex unsafe, but we'll call it unsafe anyway
# for demonstration purposes
om.ExecComp.register("unsafe", lambda x: x * x, complex_safe=False)
p = om.Problem()
comp = p.model.add_subsystem("comp", om.ExecComp("y = 2 * unsafe(x)"))

# because our function is complex unsafe, we must declare that the partials
# with respect to 'x' use 'fd' instead of 'cs'
comp.declare_partials('*', 'x', method='fd')

p.setup()
p.run_model()
J = p.compute_totals(of=['comp.y'], wrt=['comp.x'])
print(J['comp.y', 'comp.x'][0][0])
4.000001999848735