Source code for openmdao.main.pseudoassembly
'''The PseudoAssembly is used to aggregate blocks of components that cannot
provide derivatives, and thus must be finite differenced.'''
from openmdao.main.derivatives import FiniteDifference
[docs]def to_PA_var(name, pa_name):
''' Converts an input to a unique input name on a pseudoassembly.'''
return pa_name + '.' + name.replace('.', '|')
[docs]def from_PA_var(name):
''' Converts a pseudoassembly input name back to the real input.'''
if '~' in name:
name = name.partition('.')[2].replace('|', '.')
return name
[docs]class PseudoAssembly(object):
"""The PseudoAssembly is used to aggregate blocks of components that cannot
provide derivatives, and thus must be finite differenced. It is not a real
assembly, and should never be used in an OpenMDAO model."""
def __init__(self, name, comps, inputs, outputs, wflow):
"""Initialized with list of components, and the parent workflow."""
if '~' not in name:
name = "~" + name
self.name = name
self.comps = comps
self.wflow = wflow
self.inputs = list(inputs)
self.outputs = list(outputs)
self.mapped_inputs = []
for varpath in self.inputs:
if isinstance(varpath, basestring):
val = to_PA_var(varpath, name).partition('.')[2]
else:
val = tuple([to_PA_var(vp, name).partition('.')[2]
for vp in varpath])
self.mapped_inputs.append(val)
#self.mapped_inputs = [to_PA_var(varpath, name).partition('.')[2]
# for varpath in self.inputs]
self.mapped_outputs = [to_PA_var(varpath, name).partition('.')[2]
for varpath in self.outputs]
self.itername = ''
self.fd = None
self.J = None
# By default, use fake finite-difference on components if they have
# derivatives.
self.ffd_order = 1
#print [comp.name for comp in comps], inputs, outputs
[docs] def set_itername(self, name):
"""Comp API compatibility; allows iteration coord to be set in
components."""
self.itername = name
[docs] def run(self, ffd_order=0, case_id=''):
"""Run all components contained in this assy. Used by finite
difference."""
# Override fake finite difference if requested. This enables a pure
# finite-differences for check_derivatives.
if self.ffd_order == 0:
ffd_order = 0
for name in self.comps:
comp = self.wflow.scope.get(name)
comp.set_itername(self.itername+'-fd')
comp.run(ffd_order=ffd_order, case_id=case_id)
[docs] def calc_derivatives(self, first=False, second=False, savebase=True,
required_inputs=None, required_outputs=None):
"""Calculate the derivatives for this non-differentiable block using
Finite Difference."""
# We don't do this in __init__ because some inputs and outputs
# are added after creation (for nested driver support).
if self.fd is None:
self.fd = FiniteDifference(self)
if hasattr(self.wflow, '_severed_edges'):
self.wflow.sever_edges(self.wflow._severed_edges)
try:
# First, linearize about operating point.
# Note: Only needed for differentiable islands, which are handled
# with Fake Finite Difference.
# Don't do this for full-model finite difference.
if first and self.ffd_order>0:
for name in self.comps:
comp = self.wflow.scope.get(name)
comp.calc_derivatives(first, second, True)
self.J = self.fd.calculate()
finally:
if hasattr(self.wflow, '_severed_edges'):
self.wflow.unsever_edges()
pass
[docs] def provideJ(self):
"""Jacobian for this block"""
return self.mapped_inputs, self.mapped_outputs, self.J
[docs] def get(self, varname):
""" Return the value of a variable in the Pseudoassembly. Used
when sizing variables in the Jacobian."""
return self.wflow.scope.get(from_PA_var(self.name+'.'+varname))