MuxComp#

MuxComp works to combine multiple inputs into a single value. This can be useful in situations where scalar outputs from multiple components need to be fed into a single vectorized component.

MuxComp combines two or more inputs into a single output by stacking them along an axis.

MuxComp Options#

MuxComp has a single option, vec_size, which provides the number of inputs to be combined into a single output. The default value of vec_size is 2.

Adding Variables#

A single MuxComp can mux multiple variables, so long as all variables are compatible with the given vec_size. Variables are added via the add_var method.

The axis along which the muxing is to occur is given via the axis argument.

The variables are joined along a new dimension, the index of which is given by axis. The specified axis follows the convention used by the numpy.stack function. Giving axis = 0 will stack the inputs along the first axis (vertically). Giving axis = 1 will stack the inputs along the second axis (horizontally). Giving axis = -1 will stack the inputs along the last axis, and so is dependent on the shape of the inputs. Due to the axis convention of numpy.stack, the axis index is only valid if it is less than or equal to the number of dimensions in the inputs. For example, 1D arrays can be stacked vertically (axis = 0) or horizontally (axis = 1), but not depth-wise (axis = 2).

MuxComp.add_var(name, val=1.0, shape=None, units=None, desc='', axis=0)[source]

Add an output variable to be muxed, and all associated input variables.

Parameters:
namestr

Name of the variable in this component’s namespace.

valfloat or list or tuple or ndarray or Iterable

The initial value of the variable being added in user-defined units. Default is 1.0.

shapeint or tuple or list or None

Shape of the input variables to be muxed, only required if val is not an array. Default is None.

unitsstr or None

Units in which this input variable will be provided to the component during execution. Default is None, which means it is unitless.

descstr

Description of the variable.

axisint

The axis along which the elements will be stacked. Note that N-dimensional inputs cannot be stacked along an axis greater than N.

Example: Muxing 3 (n x 1) columns into a single (n x 3) matrix#

In this example we start with three (n x 1) column vectors (x, y, and z) and combine them into a single position vector r (n x 3). This is achieved by stacking the vectors along axis = 1. Like the previous example, this is somewhat contrived but is intended to demonstrate the capabilities of the MuxComp.

import numpy as np
import openmdao.api as om

# The number of elements to be muxed
n = 3

# The size of each element to be muxed
m = 100

p = om.Problem()

mux_comp = p.model.add_subsystem(name='mux', subsys=om.MuxComp(vec_size=n))

mux_comp.add_var('r', shape=(m,), axis=1, units='m')

p.model.add_subsystem(name='vec_mag_comp',
                      subsys=om.VectorMagnitudeComp(vec_size=m, length=n, in_name='r',
                                                    mag_name='r_mag', units='m'))

p.model.connect('mux.r', 'vec_mag_comp.r')

p.setup()

p.set_val('mux.r_0', 1 + np.random.rand(m))
p.set_val('mux.r_1', 1 + np.random.rand(m))
p.set_val('mux.r_2', 1 + np.random.rand(m))

p.run_model()

# Verify the results against numpy.dot in a for loop.
for i in range(n):
    r_i = [p.get_val('mux.r_0')[i], p.get_val('mux.r_1')[i], p.get_val('mux.r_2')[i]]
    expected_i = np.sqrt(np.dot(r_i, r_i))
    print(p.get_val('vec_mag_comp.r_mag')[i])
2.2868145687763044
2.5882497142915994
3.1634019903189774

Example: Using om.slicer to reduce a 3-column matrix into constituent vectors#

The inverse functionality, that is “demuxing” or breaking up of inputs into multiple values, is accomplished using the om.slicer() utility.

This example is contrived and could be achieved with a single vectorized component, but it serves to give an example of how this would be implemented.

import numpy as np
import openmdao.api as om

# The number of elements to be demuxed
n = 3

arr_5x3 = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
    [10, 11, 12],
    [13, 14, 15],
])

p = om.Problem()

p.model.add_subsystem('indep', om.IndepVarComp('x', arr_5x3, units='km'), promotes=['*'])
p.model.add_subsystem('indep2', om.IndepVarComp('y', arr_5x3, units='km'), promotes=['*'])

p.model.add_subsystem(name='longitude_comp',
                      subsys=om.ExecComp('long = atan(y/x)',
                                         x={'val': np.ones(n), 'units': 'km'},
                                         y={'val': np.ones(n), 'units': 'km'},
                                         long={'val': np.ones(n), 'units': 'rad'}))

# Use the src_indices arg in promotes to perform the demuxing
p.model.promotes('longitude_comp', inputs=['x'], src_indices=om.slicer[0, :])
p.model.promotes('longitude_comp', inputs=['y'], src_indices=om.slicer[1, :])


p.setup()

p.run_model()

print(p.get_val('longitude_comp.x'))
print(p.get_val('longitude_comp.y'))
print(p.get_val('longitude_comp.long'))
[1. 2. 3.]
[4. 5. 6.]
[1.32581766 1.19028995 1.10714872]