CrossProductComp

CrossProductComp performs a cross product between two 3-vector inputs. It may be vectorized to provide the result at one or more points simultaneously.

\[ c_i = \bar{a}_i \times \bar{b}_i \]

The first dimension of the inputs holds the vectorized dimension. The default vec_size is 1, providing the cross product of \(a\) and \(b\) at a single point. The lengths of \(a\) and \(b\) at each point must be 3.

The shape of \(a\) and \(b\) will always be (vec_size, 3), but the connection rules of OpenMDAO allow the incoming connection to have shape (3,) when vec_size is 1, since the storage order of the underlying data is the same. The output vector c of CrossProductComp will always have shape (vec_size, 3).

CrossProductComp Options

Options for CrossProductComp allow the user to rename the input variables \(a\) and \(b\) and the output \(c\), as well as specifying their units.

Option Default Acceptable Values Acceptable Types Description Deprecation
a_name a N/A ['str'] The variable name for vector a. N/A
a_units N/A N/A ['str'] The units for vector a. N/A
b_name b N/A ['str'] The variable name for vector b. N/A
b_units N/A N/A ['str'] The units for vector b. N/A
c_name c N/A ['str'] The variable name for vector c. N/A
c_units N/A N/A ['str'] The units for vector c. N/A
distributed False [True, False] ['bool'] True if ALL variables in this component are distributed across multiple processes. The 'distributed' option has been deprecated. Individual inputs and outputs should be set as distributed instead, using calls to add_input() or add_output().
run_root_onlyFalse [True, False] ['bool'] If True, call compute/compute_partials/linearize/apply_linear/apply_nonlinear/compute_jacvec_product only on rank 0 and broadcast the results to the other ranks.N/A
vec_size 1 N/A ['int'] The number of points at which the cross product is computed N/A

CrossProductComp Constructor

The call signature for the CrossProductComp constructor is:

CrossProductComp.__init__(**kwargs)[source]

Initialize the Cross Product component.

CrossProductComp Usage

There are often situations when numerous products need to be computed, essentially in parallel. You can reduce the number of components required by having one CrossProductComp perform multiple operations. This is also convenient when the different operations have common inputs.

The add_product method is used to create additional products after instantiation.

CrossProductComp.add_product(c_name, a_name='a', b_name='b', c_units=None, a_units=None, b_units=None, vec_size=1)[source]

Add a new output product to the cross product component.

Parameters
c_namestr

The name of the vector product output.

a_namestr

The name of the first vector input.

b_namestr

The name of the second vector input.

c_unitsstr or None

The units of the output.

a_unitsstr or None

The units of input a.

b_unitsstr or None

The units of input b.

vec_sizeint

The number of points at which the dot vector product should be computed simultaneously. The shape of the output is (vec_size,).

CrossProductComp Example

In the following example CrossProductComp is used to compute torque as the cross product of force (\(F\)) and radius (\(r\)) at 100 points simultaneously. Note the use of a_name, b_name, and c_name to assign names to the inputs and outputs. Units are assigned using a_units, b_units, and c_units. Note that no internal checks are performed to ensure that c_units are consistent with a_units and b_units.

import numpy as np
import openmdao.api as om

n = 24

p = om.Problem()

p.model.add_subsystem(name='cross_prod_comp',
                      subsys=om.CrossProductComp(vec_size=n,
                                                 a_name='r', b_name='F', c_name='torque',
                                                 a_units='m', b_units='N', c_units='N*m'),
                      promotes_inputs=['r', 'F'])

p.setup()

p.set_val('r', np.random.rand(n, 3))
p.set_val('F', np.random.rand(n, 3))

p.run_model()

# Check the output in units of ft*lbf to ensure that our units work as expected.
expected = []
for i in range(n):
    a_i = p.get_val('r')[i, :]
    b_i = p.get_val('F')[i, :]
    expected.append(np.cross(a_i, b_i) * 0.73756215)

    actual_i = p.get_val('cross_prod_comp.torque', units='ft*lbf')[i]
    rel_error = np.abs(expected[i] - actual_i)/actual_i
    assert np.all(rel_error < 1e-8), f"Relative error: {rel_error}"

print(p.get_val('cross_prod_comp.torque', units='ft*lbf'))
[[ 0.32138744 -0.2035878   0.00319686]
 [ 0.32491804 -0.1797295  -0.32496791]
 [-0.18894356  0.20988513  0.12450725]
 [ 0.17295815 -0.17643644 -0.00676487]
 [-0.06306998 -0.3305812   0.38182732]
 [ 0.06870748 -0.49553168  0.49538466]
 [ 0.10174809  0.15374978 -0.27263325]
 [-0.32792717 -0.06585559  0.18084456]
 [ 0.50100579 -0.23300812 -0.12072586]
 [-0.28919914  0.23113056 -0.11021308]
 [-0.03094729  0.20984813 -0.2320244 ]
 [ 0.14628209  0.10193061 -0.34478243]
 [ 0.48498144 -0.30106494 -0.25470013]
 [ 0.27194678  0.00214722 -0.10231674]
 [-0.10395845  0.11753721 -0.0400588 ]
 [-0.04007173  0.20989649 -0.01703094]
 [-0.45561009  0.09077382 -0.01556034]
 [-0.01435703 -0.27430833  0.16299546]
 [ 0.16545825 -0.09146073 -0.00147539]
 [ 0.32446528  0.43788106 -0.55382568]
 [ 0.288327    0.1325083  -0.56064255]
 [-0.00891304 -0.13558663  0.09237651]
 [ 0.16607697  0.04848361 -0.10237566]
 [-0.00436447  0.06525342  0.00515125]]

DotProductComp Example with Multiple Products

When defining multiple products:

  • An input name in one call to add_product may not be an output name in another call, and vice-versa.

  • The units and shape of variables used across multiple products must be the same in each one.

n = 24

p = om.Problem()

cpc = om.CrossProductComp(vec_size=n,
                          a_name='r', b_name='F', c_name='torque',
                          a_units='m', b_units='N', c_units='N*m')

cpc.add_product(vec_size=n,
                a_name='r', b_name='p', c_name='L',
                a_units='m', b_units='kg*m/s', c_units='kg*m**2/s')

p.model.add_subsystem(name='cross_prod_comp', subsys=cpc,
                      promotes_inputs=['r', 'F', 'p'])

p.setup()

p.set_val('r', np.random.rand(n, 3))
p.set_val('F', np.random.rand(n, 3))
p.set_val('p', np.random.rand(n, 3))

p.run_model()

# Check the output.
expected_T = []
expected_L = []
for i in range(n):
    a_i = p.get_val('r')[i, :]
    b_i = p.get_val('F')[i, :]
    expected_T.append(np.cross(a_i, b_i))

    actual_i = p.get_val('cross_prod_comp.torque')[i]
    rel_error = np.abs(expected_T[i] - actual_i)/actual_i
    assert np.all(rel_error < 1e-8), f"Relative error: {rel_error}"

    b_i = p.get_val('p')[i, :]
    expected_L.append(np.cross(a_i, b_i))

    actual_i = p.get_val('cross_prod_comp.L')[i]
    rel_error = np.abs(expected_L[i] - actual_i)/actual_i
    assert np.all(rel_error < 1e-8), f"Relative error: {rel_error}"

print(p.get_val('cross_prod_comp.torque'))
[[ 0.0535051   0.02166956 -0.03142693]
 [-0.01503407 -0.05541094  0.1500074 ]
 [-0.61314182  0.39870408  0.20084977]
 [-0.09354285  0.02736387  0.03318364]
 [ 0.19642022 -0.16252333 -0.07361895]
 [-0.06999369  0.41595622 -0.35996506]
 [-0.01361024 -0.32131921  0.13604046]
 [-0.68415463  0.46832846  0.7760538 ]
 [ 0.01717301  0.01313438 -0.13795024]
 [ 0.84345782  0.0409266  -0.81806345]
 [ 0.58652286 -0.69134162 -0.64624719]
 [-0.06147001  0.00519747  0.02253233]
 [ 0.29805443 -0.17611179 -0.16906208]
 [ 0.0792407  -0.05472196 -0.02848204]
 [ 0.06456799  0.33673823 -0.39794579]
 [-0.01571555  0.15088935 -0.08087312]
 [ 0.0575491  -0.01823001 -0.01346009]
 [-0.28416191 -0.21400023  0.34742743]
 [ 0.06101763 -0.00652991 -0.0661076 ]
 [-0.00973365 -0.45845949  0.46182488]
 [ 0.00244121  0.15258975 -0.0841211 ]
 [ 0.39082639  0.06922408 -0.58089458]
 [ 0.09028049 -0.08610907  0.03723184]
 [ 0.62478831 -0.17608724 -0.23887975]]
print(p.get_val('cross_prod_comp.L'))
[[-6.29000758e-01  8.34596095e-01 -4.24859537e-01]
 [-1.45488938e-01  2.00284019e-01  1.54201625e-02]
 [ 1.46985884e-01 -5.69885254e-01  2.63393652e-01]
 [ 1.09013125e-01  9.65345811e-02 -2.28028955e-01]
 [ 7.29877942e-01 -6.29398246e-01 -2.33941759e-01]
 [-3.74965787e-01  3.31675440e-01 -1.59374666e-01]
 [-2.06478339e-01 -3.86622210e-01  5.03818494e-01]
 [-1.08417917e-01 -4.08110158e-01  1.37258714e-01]
 [ 4.33512346e-01 -8.62810793e-02 -1.83359623e-02]
 [ 5.86040941e-01 -2.21487855e-01  1.21092222e-01]
 [ 7.90729137e-01 -9.67150465e-01 -8.44075681e-02]
 [ 4.40709942e-03 -2.36099733e-03  4.15249856e-04]
 [ 8.28962832e-01 -4.43184564e-01 -5.66215851e-01]
 [ 1.69478432e-01 -1.04427843e-01 -1.03792960e-01]
 [-1.05445859e-01  2.41674137e-01 -2.84686377e-01]
 [-1.62602315e-01  2.57214596e-01 -1.34071520e-01]
 [-4.30651034e-01  2.85250288e-01  3.02773506e-02]
 [-2.91714583e-02  1.71272952e-01 -8.31040261e-03]
 [ 3.33956501e-01 -3.56318636e-01  1.21377485e-01]
 [-2.07276241e-03 -1.50908085e-01  1.22319103e-01]
 [-1.20251578e-01  2.01751378e-01 -8.49578120e-02]
 [ 5.65786048e-01  8.69691826e-02 -7.31692761e-01]
 [ 4.47877520e-01 -5.15268160e-01  5.21850904e-01]
 [ 2.36790344e-02  1.51342977e-01 -3.68190744e-01]]