jthomas
jthomas

Reputation: 2583

OpenMDAO v0.13: performing an optimization when using multiple instances of a components initiated in a loop

I am setting up an optimization in OpenMDAO v0.13 using several components that are used many times. My assembly seems to be working just fine with the default driver, but when I run with an optimizer it does not solve. The optimizer simply runs with the inputs given and returns the answer using those inputs. I am not sure what the issue is, but I would appreciate any insights. I have included a simple code mimicking my structure that reproduces the error. I think the problem is in the connections, summer.fs does not update after initialization.

from openmdao.main.api import Assembly, Component
from openmdao.lib.datatypes.api import Float, Array, List
from openmdao.lib.drivers.api import DOEdriver, SLSQPdriver, COBYLAdriver, CaseIteratorDriver
from pyopt_driver.pyopt_driver import pyOptDriver

import numpy as np


class component1(Component):

    x = Float(iotype='in')
    y = Float(iotype='in')
    term1 = Float(iotype='out')
    a = Float(iotype='in', default_value=1)
    def execute(self):
        x = self.x
        a = self.a

        term1 = a*x**2
        self.term1 = term1

        print "In comp1", self.name, self.a, self.x, self.term1

    def list_deriv_vars(self):
        return ('x',), ('term1',)

    def provideJ(self):

        x = self.x
        a = self.a
        dterm1_dx = 2.*a*x

        J = np.array([[dterm1_dx]])
        print 'In comp1, J = %s' % J

        return J


class component2(Component):

    x = Float(iotype='in')
    y = Float(iotype='in')
    term1 = Float(iotype='in')
    f = Float(iotype='out')

    def execute(self):

        y = self.y
        x = self.x
        term1 = self.term1
        f = term1 + x + y**2

        self.f = f
        print "In comp2", self.name, self.x, self.y, self.term1, self.f



class summer(Component):


    total = Float(iotype='out', desc='sum of all f values')

    def __init__(self, size):
        super(summer, self).__init__()
        self.size = size

        self.add('fs', Array(np.ones(size), iotype='in', desc='f values from all cases'))

    def execute(self):
        self.total = sum(self.fs)
        print 'In summer, fs = %s and total = %s' % (self.fs, self.total)


class assembly(Assembly):

    x = Float(iotype='in')
    y = Float(iotype='in')
    total = Float(iotype='out')

    def __init__(self, size):

        super(assembly, self).__init__()

        self.size = size

        self.add('a_vals', Array(np.zeros(size), iotype='in', dtype='float'))
        self.add('fs', Array(np.zeros(size), iotype='out', dtype='float'))

        print 'in init a_vals = %s' % self.a_vals


    def configure(self):

        # self.add('driver', SLSQPdriver())
        self.add('driver', pyOptDriver())
        self.driver.optimizer = 'SNOPT'
        # self.driver.pyopt_diff = True

        #create this first, so we can connect to it
        self.add('summer', summer(size=len(self.a_vals)))
        self.connect('summer.total', 'total')

        print 'in configure a_vals = %s' % self.a_vals

        # create instances of components
        for i in range(0, self.size):
            c1 = self.add('comp1_%d'%i, component1())
            c1.missing_deriv_policy = 'assume_zero'

            c2 = self.add('comp2_%d'%i, component2())
            self.connect('a_vals[%d]' % i, 'comp1_%d.a' % i)
            self.connect('x', ['comp1_%d.x'%i, 'comp2_%d.x'%i])
            self.connect('y', ['comp1_%d.y'%i, 'comp2_%d.y'%i])
            self.connect('comp1_%d.term1'%i, 'comp2_%d.term1'%i)

            self.connect('comp2_%d.f'%i, 'summer.fs[%d]'%i)

            self.driver.workflow.add(['comp1_%d'%i, 'comp2_%d'%i])

        self.connect('summer.fs[:]', 'fs[:]')
        self.driver.workflow.add(['summer'])

        # set up main driver (optimizer)
        self.driver.iprint = 1
        self.driver.maxiter = 100
        self.driver.accuracy = 1.0e-6
        self.driver.add_parameter('x', low=-5., high=5.)
        self.driver.add_parameter('y', low=-5., high=5.)
        self.driver.add_objective('summer.total')


if __name__ == "__main__":
    """ the result should be -1 at (x, y) = (-0.5, 0) """

    import time
    from openmdao.main.api import set_as_top
    a_vals = np.array([1., 1., 1., 1.])
    test = set_as_top(assembly(size=len(a_vals)))
    test.a_vals = a_vals
    print test.a_vals
    test.x = 2.
    test.y = 2.

    tt = time.time()
    test.run()

    print "Elapsed time: ", time.time()-tt, "seconds"

    print 'result = ', test.summer.total
    print '(x, y) = (%s, %s)' % (test.x, test.y)
    print test.fs

Upvotes: 0

Views: 123

Answers (1)

Kenneth Moore
Kenneth Moore

Reputation: 2202

I played around with your model, and found that the following line caused problems:

#self.connect('summer.fs[:]', 'fs[:]')

When I commented it out, I got the optimization to move.

I am not sure what is happening there, but the graph transformations sometimes have some issues with component input nodes that are promoted as outputs on the assembly boundary. If you still want those values to be available on the assembly, you could try promoting the outputs from the comp2_n components instead.

Upvotes: 2

Related Questions