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#
Option | Default | Acceptable Values | Acceptable Types | Description |
---|---|---|---|---|
always_opt | False | [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_surrogate | N/A | N/A | ['SurrogateModel', 'NoneType'] | Surrogate that will be used for all outputs that don't have a specific surrogate assigned to them. |
derivs_method | N/A | ['jax', 'cs', 'fd', None] | N/A | The method to use for computing derivatives |
distributed | False | [True, False] | ['bool'] | If True, set all variables in this component as distributed across multiple processes |
run_root_only | False | [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_jit | True | [True, False] | ['bool'] | If True, attempt to use jit on compute_primal, assuming jax or some other AD package is active. |
vec_size | 1 | N/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
.
Derivatives: Yes
Description: Based on Kriging interpolation; prediction returns mean predicted value, optionally returns RMSE.
Derivatives: Yes
Description: Based on the N-Dimensional Interpolation library by Stephen Marone.
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.