MetaModelUnStructuredComp#

MetaModelUnStructuredComp lets you quickly create a component with surrogate models used to compute the outputs based on unstructured training data. Generally, this is used to construct a low-computational-cost replacement for computationally expensive components.

You can define MetaModelUnStructuredComp with as many inputs and outputs as you like, and you can also use a different surrogate model for each output.

Note

What’s the difference between MetaModelUnStructuredComp and a surrogate model? In OpenMDAO, “surrogate model” refers to the model for a single response, and MetaModelUnStructuredComp represents a collection of surrogate models trained at the same locations in the design space.

MetaModelUnStructuredComp Options#

OptionDefaultAcceptable ValuesAcceptable TypesDescription
always_optFalse[True, False]['bool']If True, force nonlinear operations on this component to be included in the optimization loop even if this component is not relevant to the design variables and responses.
default_surrogateN/AN/A['SurrogateModel', 'NoneType']Surrogate that will be used for all outputs that don't have a specific surrogate assigned to them.
derivs_methodN/A['jax', 'cs', 'fd', None]N/AThe method to use for computing derivatives
distributedFalse[True, False]['bool']If True, set all variables in this component as distributed across multiple processes
run_root_onlyFalse[True, False]['bool']If True, call compute, compute_partials, linearize, apply_linear, apply_nonlinear, and compute_jacvec_product only on rank 0 and broadcast the results to the other ranks.
use_jitTrue[True, False]['bool']If True, attempt to use jit on compute_primal, assuming jax or some other AD package is active.
vec_size1N/A['int']Number of points that will be simultaneously predicted by the surrogate.

MetaModelUnStructuredComp Constructor#

The call signature for the MetaModelUnStructuredComp constructor is:

MetaModelUnStructuredComp.__init__(**kwargs)[source]

Initialize all attributes.

Simple Example#

The following example demonstrates a simple Problem in which a MetaModelUnStructuredComp uses surrogates to mimic the sine and cosine functions.

In this example, the MetaModelUnStructuredComp trig has a single input, x, and two outputs, sin_x and cos_x.

KrigingSurrogate is given as the surrogate for the sin_x output. Although no surrogate has been given for the cos_x output, a default_surrogate is specified for the component. Any output which has not had a surrogate assigned will use one of the default type. If default_surrogate is not specified, then a surrogate must be given for all outputs.

The first time a MetaModelUnStructuredComp runs, it will train the surrogates using the training data that has been provided, and then it will predict the output values. This training step only occurs on the first run.

# create a MetaModelUnStructuredComp, specifying surrogates for the outputs
import numpy as np
import openmdao.api as om

trig = om.MetaModelUnStructuredComp()

x_train = np.linspace(0,10,20)

trig.add_input('x', 0., training_data=x_train)

trig.add_output('sin_x', 0.,
                training_data=.5*np.sin(x_train),
                surrogate=om.KrigingSurrogate())
trig.add_output('cos_x', 0.,
                training_data=.5*np.cos(x_train))

trig.options['default_surrogate'] = om.KrigingSurrogate()

# add it to a Problem, run and check the predicted values
prob = om.Problem()
prob.model.add_subsystem('trig', trig)
prob.setup()

prob.set_val('trig.x', 2.1)
prob.run_model()

print(prob.get_val('trig.sin_x'))
print(prob.get_val('trig.cos_x'))
[0.4316104]
[-0.25241565]

Available Surrogates#

The following surrogates are available to use with MetaModelUnStructuredComp.

KrigingSurrogate

Derivatives: Yes

Description: Based on Kriging interpolation; prediction returns mean predicted value, optionally returns RMSE.

NearestNeighbor

Derivatives: Yes

Description: Based on the N-Dimensional Interpolation library by Stephen Marone.

ResponseSurface

Derivatives: Yes

Description: Based on second order response surface equations.

Advanced usage#

You can specify the training data after instantiation if you like, by setting the component’s options. Training data is provided in the options to the trig component using the variable names prefixed with train_. This can be done anytime before the MetaModelUnStructuredComp runs for the first time.

The inputs and outputs of a MetaModelUnStructuredComp are not limited to scalar values. The following modified version of the example uses an array to predict sine and cosine as a single output array of two values. You will also note that the default surrogate can be passed as an argument to the MetaModelUnStructuredComp constructor, as an alternative to specifying it later.

# create a MetaModelUnStructuredComp that predicts sine and cosine as an array
trig = om.MetaModelUnStructuredComp(default_surrogate=om.KrigingSurrogate())
trig.add_input('x', 0)
trig.add_output('y', np.zeros(2))

# add it to a Problem
prob = om.Problem()
prob.model.add_subsystem('trig', trig)
prob.setup()

# provide training data
trig.options['train_x'] = np.linspace(0, 10, 20)
trig.options['train_y'] = np.column_stack((
    .5*np.sin(trig.options['train_x']),
    .5*np.cos(trig.options['train_x'])
))

# train the surrogate and check predicted value
prob.set_val('trig.x', 2.1)
prob.run_model()
print(prob.get_val('trig.y'))
[ 0.43161089 -0.25241615]

In addition, it’s possible to vectorize the input and output variables so that you can make multiple predictions for the inputs and outputs in a single execution of the MetaModelUnStructuredComp component. This is done by setting the vec_size argument when constructing the MetaModelUnStructuredComp component and giving it the number of predictions to make. The following example vectorizes the trig component so that it makes three predictions at a time. In this case, the input is three independent values of x and the output is the corresponding predicted values for the sine and cosine functions at those three points. Note that a vectorized MetaModelUnStructuredComp component requires the first dimension of all input and output variables to be the same size as specified in the vec_size argument.

size = 3

# create a vectorized MetaModelUnStructuredComp for sine and cosine
trig = om.MetaModelUnStructuredComp(vec_size=size, default_surrogate=om.KrigingSurrogate())
trig.add_input('x', np.zeros(size))
trig.add_output('y', np.zeros((size, 2)))

# add it to a Problem
prob = om.Problem()
prob.model.add_subsystem('trig', trig)
prob.setup()

# provide training data
trig.options['train_x'] = np.linspace(0, 10, 20)
trig.options['train_y'] = np.column_stack((
    .5*np.sin(trig.options['train_x']),
    .5*np.cos(trig.options['train_x'])
))

# train the surrogate and check predicted value
prob.set_val('trig.x', np.array([2.1, 3.2, 4.3]))
prob.run_model()

print(prob.get_val('trig.y'))
[[ 0.43161089 -0.25241615]
 [-0.02918421 -0.49914071]
 [-0.45808581 -0.20039903]]

Using Surrogates That Do Not Define Linearize Method#

In some cases, you might define surrogates but not define a linearize method. In this case, the MetaModelUnStructuredComp derivatives will be computed using finite differences for the output variables that use that surrogate. By default, the default options for the finite differencing method will be used.

If you would like to specify finite differencing options, you can do so by calling the declare_partials method in the component’s setup_partials or in a parent group’s configure method. This example, which uses a surrogate with no linearize method and no training for simplicity, shows declare_partials called in setup_partials.

from math import sin

class SinSurrogate(om.SurrogateModel):
    def train(self, x, y):
        pass

    def predict(self, x):
        return sin(x)

class TrigWithFdInSetup(om.MetaModelUnStructuredComp):
    def setup(self):
        surrogate = SinSurrogate()
        self.add_input('x', 0.)
        self.add_output('sin_x', 0., surrogate=surrogate)

    def setup_partials(self):
        self.declare_partials('sin_x', 'x', method='fd',
                              form='backward', step=1e-7, step_calc='rel')

# Testing explicitly setting fd inside of setup
prob = om.Problem()

trig = TrigWithFdInSetup()
prob.model.add_subsystem('trig', trig, promotes_inputs=['x'])

prob.setup(check=True)
prob.set_val('x', 5.)

trig.train = False
prob.run_model()

J = prob.compute_totals(of=['trig.sin_x'], wrt=['x'])
deriv_using_fd = J[('trig.sin_x', 'x')]
INFO: checking out_of_order...
INFO:     out_of_order check complete (0.000374 sec).
INFO: checking system...
INFO:     system check complete (0.000009 sec).
INFO: checking solvers...
INFO:     solvers check complete (0.000071 sec).
INFO: checking dup_inputs...
INFO:     dup_inputs check complete (0.000013 sec).
INFO: checking missing_recorders...
WARNING: The Problem has no recorder of any kind attached
INFO:     missing_recorders check complete (0.000359 sec).
INFO: checking unserializable_options...
INFO:     unserializable_options check complete (0.000080 sec).
INFO: checking comp_has_no_outputs...
INFO:     comp_has_no_outputs check complete (0.000013 sec).
INFO: checking auto_ivc_warnings...
INFO:     auto_ivc_warnings check complete (0.000002 sec).
/tmp/ipykernel_21501/959289868.py:8: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)
  return sin(x)
print(deriv_using_fd[0])
[0.28366195]

Complex step has not been tested with MetaModelUnStructuredComp and will result in an exception if used.