Source code for openmdao.components.metamodel
""" Metamodel provides basic Meta Modeling capability."""
import sys
from copy import deepcopy
from openmdao.core.component import Component, _NotSet
[docs]class MetaModel(Component):
"""Class that creates a reduced order model for outputs from
parameters. Each output may have it's own surrogate model.
Training inputs and outputs are automatically created with
'train:' prepended to the corresponding parameter/output name.
For a Float variable, the training data is an array of length m.
"""
def __init__(self):
super(MetaModel, self).__init__()
# This surrogate will be used for all outputs that don't have
# a specific surrogate assigned to them
self.default_surrogate = None
# keep list of params and outputs that are not the training vars
self._surrogate_param_names = []
self._surrogate_output_names = []
# training will occur on first execution
self.train = True
self._training_input = []
self._training_output = {}
# When set to False (default), the metamodel retrains with the new
# dataset whenever the training data values are changed. When set to
# True, the new data is appended to the old data and all of the data
# is used to train.
self.warm_restart = False
# keeps track of which sur_<name> slots are full
self._surrogate_overrides = set()
[docs] def add_param(self, name, val=_NotSet, **kwargs):
""" Add a `param` input to this component and a corresponding
training parameter.
Args
----
name : string
Name of the input.
val : float or ndarray or object
Initial value for the input.
"""
super(MetaModel, self).add_param(name, val, **kwargs)
super(MetaModel, self).add_param('train:'+name, val=list(), pass_by_obj=True)
self._surrogate_param_names.append(name)
[docs] def add_output(self, name, val=_NotSet, **kwargs):
""" Add an output to this component and a corresponding
training output.
Args
----
name : string
Name of the variable output.
val : float or ndarray
Initial value for the output. While the value is overwritten during
execution, it is useful for infering size.
"""
super(MetaModel, self).add_output(name, val, **kwargs)
super(MetaModel, self).add_output('train:'+name, val=list(), pass_by_obj=True)
self._surrogate_output_names.append(name)
self._training_output[name] = []
if self._unknowns_dict[name].get('surrogate'):
self._unknowns_dict[name]['default_surrogate'] = False
else:
self._unknowns_dict[name]['default_surrogate'] = True
def _setup_variables(self):
"""Returns our params and unknowns dictionaries,
re-keyed to use absolute variable names.
Also instantiates surrogates for the output variables
that use the default surrogate.
"""
# create an instance of the default surrogate for outputs that
# did not have a surrogate specified
if self.default_surrogate is not None:
for name in self._surrogate_output_names:
if self._unknowns_dict[name].get('default_surrogate'):
surrogate = deepcopy(self.default_surrogate)
self._unknowns_dict[name]['surrogate'] = surrogate
# training will occur on first execution after setup
self.train = True
return super(MetaModel, self)._setup_variables()
[docs] def check_setup(self, out_stream=sys.stdout):
"""Write a report to the given stream indicating any potential problems found
with the current configuration of this ``Problem``.
Args
----
out_stream : a file-like object, optional
"""
# All outputs must have surrogates assigned
# either explicitly or through the default surrogate
if self.default_surrogate is None:
no_sur = []
for name in self._surrogate_output_names:
surrogate = self._unknowns_dict[name].get('surrogate')
if surrogate is None:
no_sur.append(name)
if len(no_sur) > 0:
msg = ("No default surrogate model is defined and the following"
" outputs do not have a surrogate model:\n%s\n"
"Either specify a default_surrogate, or specify a "
"surrogate model for all outputs."
% no_sur)
out_stream.write(msg)
[docs] def solve_nonlinear(self, params, unknowns, resids):
"""Predict outputs.
If the training flag is set, train the metamodel first.
Args
----
params : `VecWrapper`, optional
`VecWrapper` containing parameters. (p)
unknowns : `VecWrapper`, optional
`VecWrapper` containing outputs and states. (u)
resids : `VecWrapper`, optional
`VecWrapper` containing residuals. (r)
"""
# Train first
if self.train:
if self.warm_restart:
base = len(self._training_input)
else:
self._training_input = []
base = 0
# add training data for each input
for name in self._surrogate_param_names:
val = self.params['train:'+name]
num_sample = len(val)
for j in range(base, base + num_sample):
if j > len(self._training_input) - 1:
self._training_input.append([])
self._training_input[j].append(val[j-base])
# add training data for each output
for name in self._surrogate_output_names:
if not self.warm_restart:
self._training_output[name] = []
self._training_output[name].extend(self.unknowns['train:'+name])
surrogate = self._unknowns_dict[name].get('surrogate')
if surrogate is not None:
surrogate.train(self._training_input, self._training_output[name])
self.train = False
# Now Predict for current inputs
inputs = []
for name in self._surrogate_param_names:
val = params[name]
inputs.append(val)
for name in self._surrogate_output_names:
surrogate = self._unknowns_dict[name].get('surrogate')
if surrogate:
unknowns[name] = surrogate.predict(inputs)
else:
raise RuntimeError("Metamodel '%s': No surrogate specified for output '%s'"
% (self.pathname, name))