OpenMDAO: How to handle non-converging points in ExplicitComponent.compute?

牧云@^-^@ 提交于 2019-12-11 14:53:44

问题


I've tried to handle non-converging points in the compute method of my ExplicitComponent, by raising an AnalysisError, as suggested in What is the best way to tell openMDAO driver or solver that it is impossible to evaluate the model at some point? (originally I wanted to make a comment in this thread, but I wasn't allowed due to my low Stack Overflow reputation score). However, this doesn't seem to solve my problem. What I expected was that error would be caught, the design point would be skipped and that the optimizer would continue to evaluate other points in order to find a solution. It is correct that the error is caught, but for some reason, the error is then reraised in ScipyOptimizeDriver.run. What is the purpose of this?

This is an example script for reproducing the behaviour:

import numpy as np
from openmdao.api import Problem, Group, IndepVarComp, ExplicitComponent, ScipyOptimizeDriver, ScipyKrylov, AnalysisError


class Test1Comp(ExplicitComponent):

    def setup(self):
        self.add_input('design_x', 1.0)
        self.add_input('design_y', 1.0)
        self.add_input('design_z', 0.5)
        self.add_output('y', val=0.1)
        self.add_output('z', val=0.1)
        self.add_output('obj', val=0.0)

        self.declare_partials(of='*', wrt='*', method='fd', form='central', step=1.0e-4)

    def compute(self, inputs, outputs):
        design_z = inputs['design_z']
        design_x = inputs['design_x']
        design_y = inputs['design_y']

        # Let's assume we have a model that has problems converging around design_x = 5.0
        if 0.49999 < design_x < 0.500001:
            raise AnalysisError()

        z = 4/(design_z + 1)
        y = - design_z - 2*z
        obj = (y/5.833333 - design_x)**2 + z/2.666667*100*(design_y - design_x**2)**2

        outputs["z"] = z
        outputs["y"] = y
        outputs['obj'] = obj


if __name__ == "__main__":

    prob = Problem()
    model = prob.model = Group()

    model.add_subsystem('d1', IndepVarComp('design_x', 1.0))
    model.add_subsystem('d2', IndepVarComp('design_y', 1.0))
    model.add_subsystem('d3', IndepVarComp('design_z', 0.5))
    model.add_subsystem('comp', Test1Comp())

    model.connect('d1.design_x', 'comp.design_x')
    model.connect('d2.design_y', 'comp.design_y')
    model.connect('d3.design_z', 'comp.design_z')

    prob.driver = ScipyOptimizeDriver()
    prob.driver.options["optimizer"] = 'SLSQP'
    prob.driver.options['tol'] = 1e-8
    model.add_design_var("d1.design_x", lower=0.5, upper=1.5)
    model.add_design_var("d2.design_y", lower=0.5, upper=1.5)
    model.add_design_var("d3.design_z", lower=0.0, upper=1.0)
    model.add_objective('comp.obj')

    model.linear_solver = ScipyKrylov()
    model.linear_solver.options['maxiter'] = int(1200)
    model.linear_solver.options['restart'] = int(20)

    # prob.model.approx_totals()
    prob.setup()
    prob.run_driver()
    print(prob['comp.y'])
    print(prob['comp.z'])

Futrthermore, when looking at ExplicitComponent._solve_nonlinear, which is the method calling ExplicitComponent.compute, it appears to me that the natural way of communicating to OpenMDAO that a point is not converging would be to have ExplicitComponent.compute return True. See the source code for the method:

def _solve_nonlinear(self):
    """
    Compute outputs. The model is assumed to be in a scaled state.

    Returns
    -------
    boolean
        Failure flag; True if failed to converge, False is successful.
    float
        absolute error.
    float
        relative error.
    """
    super(ExplicitComponent, self)._solve_nonlinear()

    with Recording(self.pathname + '._solve_nonlinear', self.iter_count, self):
        with self._unscaled_context(
                outputs=[self._outputs], residuals=[self._residuals]):
            self._residuals.set_const(0.0)
            failed = self.compute(self._inputs, self._outputs)
    return bool(failed), 0., 0. 

In summary, could someone clarify what is the recommended way of handling non-converging computations in ExplicitComponent.compute?


回答1:


I have looked at your code, and you specified everything the correct way for telling an optimizer that the component could not evaluate the design point. The problem is that scipy.minimize (which is the optimizer underneath theScipyOptimizeDriver) does not know what do do when it hits a failed point (other than raising an exception), and has no way to report a failure back (at least to my knowledge).

However, pyOptSparseDriver can do something when a point fails: it an try progressing again in the gradient direction, but with a smaller stepsize. I took your code, and exchanged the ScipyOptimizeDriver with a pyOptSparseDriver, used the "SLSQP" optimizer in that package, and it worked around that problematic point just fine, reaching what I assume is a good optimum:

[0.69651727]
[-5.]
[2.]

My driver option code looks like this:

prob.driver = pyOptSparseDriver()
prob.driver.options["optimizer"] = 'SLSQP'
prob.driver.opt_settings['ACC'] = 1e-8

If you don't already have pyoptsparse, which is a separate package not maintained by us, you can get it from https://github.com/mdolab/pyoptsparse -- and you can build and install it with:

python setup.py build
python setup.py install

For your other question, I looked around through our code and found that the failure flag return on _solve_nonlinear is never used for anything. So, the only way to communicate a non-converging status to a driver or a higher-level solver is to raise the AnalysisError. Solvers canraise an AE when they don't converge, if "err_on_maxiter" on its options is set to True (the default is False).

As a final note, I think that while our error handling mechanisms are being used, we haven't thought of everything and are always open to suggestions for improvements.



来源:https://stackoverflow.com/questions/50256104/openmdao-how-to-handle-non-converging-points-in-explicitcomponent-compute

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!