Reputation: 65
This is not an issue with OpenMDAO, but more a topic I would like to hear your thoughts about with a (simplistic) suggestion for a solution. The issue is about setting the initial value for a variable that needs to be converged with a solver. In my case I can have a model where one tool (InitialGuess
) is able to give an initial guess for a variable (let's say for y1
), which would be converged in a group where y1
is output of another tool (D1
). At the moment it is not allowed to have two tools that have y1
as output (and understandably so). Hence it is not possible to have InitialGuess
as a preconditioner and let D1
calculate the updated value(s). However, in some models it could be helpful to have such a preconditioner as one of the components. In an aircraft design example, a lower fidelity tool could provide initial guesses, in order for the solver to start at a nicer point with higher fidelity tools.
I have created a small example to illustrate my point (based on the Sellar problem). The code is shown below the picture. This picture shows the 'initial guess connections' that should be created in blue:
from openmdao.core.explicitcomponent import ExplicitComponent
from openmdao.devtools.problem_viewer.problem_viewer import view_model
from openmdao.solvers.nonlinear.nonlinear_block_gs import NonlinearBlockGS
class InitialGuess(ExplicitComponent):
def setup(self):
self.add_input('x1', val=0.)
self.add_input('z1', val=0.)
self.add_input('z2', val=0.)
self.add_output('y1', val=0.)
self.add_output('y2', val=0.)
def compute(self, inputs, outputs):
x1 = inputs['x1']
z1 = inputs['z1']
z2 = inputs['z2']
outputs['y1'] = (-0.1 + ((z1 - 0.1) ** 2 + 0.8 * z2 + x1) ** (0.5)) ** 2
outputs['y2'] = z1 + z2 - 0.1 + ((z1 - 0.1) ** 2 + 0.8 * z2 + x1) ** (0.5)
class D1(ExplicitComponent):
def setup(self):
self.add_input('x1', val=0.)
self.add_input('z1', val=0.)
self.add_input('z2', val=0.)
self.add_input('y2', val=0.)
self.add_output('y1', val=0.)
def compute(self, inputs, outputs):
x1 = inputs['x1']
z1 = inputs['z1']
z2 = inputs['z2']
y2 = inputs['y2']
outputs['y1'] = z1 ** 2. + x1 + z2 - .2 * y2
class D2(ExplicitComponent):
def setup(self):
self.add_input('z1', val=0.)
self.add_input('z2', val=0.)
self.add_input('y1', val=0.)
self.add_output('y2', val=0.)
def compute(self, inputs, outputs):
z1 = inputs['z1']
z2 = inputs['z2']
y1 = inputs['y1']
outputs['y2'] = abs(y1)**.5 + z1 + z2
if __name__ == "__main__":
from openmdao.core.problem import Problem
from openmdao.core.group import Group
from openmdao.core.indepvarcomp import IndepVarComp
model = Group()
ivc = IndepVarComp()
ivc.add_output('x1', 3.0)
ivc.add_output('z1', 2.0)
ivc.add_output('z2', 2.0)
model.add_subsystem('des_vars', ivc)
model.add_subsystem('initial_guess', InitialGuess())
conv_group = Group()
conv_group.add_subsystem('d1_comp', D1())
conv_group.add_subsystem('d2_comp', D2())
model.add_subsystem('conv_group', conv_group)
model.connect('des_vars.x1', 'initial_guess.x1')
model.connect('des_vars.x1', 'conv_group.d1_comp.x1')
model.connect('des_vars.z1', 'initial_guess.z1')
model.connect('des_vars.z1', 'conv_group.d1_comp.z1')
model.connect('des_vars.z1', 'conv_group.d2_comp.z1')
model.connect('des_vars.z2', 'initial_guess.z2')
model.connect('des_vars.z2', 'conv_group.d1_comp.z2')
model.connect('des_vars.z2', 'conv_group.d2_comp.z2')
model.connect('conv_group.d1_comp.y1', 'conv_group.d2_comp.y1')
model.connect('conv_group.d2_comp.y2', 'conv_group.d1_comp.y2')
###
# PSEUDO_CODE
# model.connect_as('initial_guess', 'initial_guess.y1', 'conv_group.d2_comp.y1')
# model.connect_as('initial_guess', 'initial_guess.y2', 'conv_group.d1_comp.y1')
###
conv_group.nonlinear_solver = NonlinearBlockGS()
prob = Problem(model)
prob.setup()
view_model(prob, outfile='n2_initial_guess_example.html', show_browser=False)
prob.run_model()
print('y1 guess = ' + str(prob['initial_guess.y1'][0]))
print('y1 conv = ' + str(prob['conv_group.d1_comp.y1'][0]))
print('y2 guess = ' + str(prob['initial_guess.y2'][0]))
print('y2 conv = ' + str(prob['conv_group.d2_comp.y2'][0]))
In the example above the initial_guess
and the conv_group
are completely separated, however, it would be very helpful if, with a single run_model
call, the initial_guess
component updates the y1
and y2
values before the conv_group
is solved. In this case, this would even mean that conv_group
would be solved at the first iteration, but in a realistic case it would merely lead to less iterations. The pseudo-code in the example provides a suggestion as to how this could be defined in the script.
I was wondering if there already is a way to do this in OpenMDAO? If not, I thought it might be a nice idea to include a capability like this.
Upvotes: 0
Views: 140
Reputation: 2704
OpenMDAO master branch (as of 05/01/2019) OpenMDAO supports providing an initial guess using the guess_nonlinear
method on systems: http://openmdao.org/twodocs/versions/latest/features/core_features/grouping_components/guess_method.html
This feature will be released in V2.7, but can be accessed by pulling from master in the meantime.
This method gets run before solving the group and sets the implicit outputs to whatever initial value you want.
A convenient thing about guess_nonlinear
is that you can use it to do whatever calculations you want, assuming that you only need access to inputs, outputs, or residuals.
Upvotes: 1