【问题标题】:Openmdao V1.7 Sellar MDFOpenmdao V1.7 赛拉 MDF
【发布时间】:2017-04-25 21:21:49
【问题描述】:

我在 OpenMDAO 的文档页面 (http://openmdao.readthedocs.io/en/1.7.3/usr-guide/tutorials/sellar.html) 上发现了有关卖方问题的 MDA 的一些奇怪之处

如果我提取代码并仅运行 MDA(在学科中添加计数器),我观察到学科之间的调用次数是不同的(d1 学科的 d2 数量是预期的两倍)。有人有答案吗?

这是结果

耦合变量:25.588303、12.058488 纪律 1 和 2 调用次数 (10,5)

这是代码

# For printing, use this import if you are running Python 2.x from __future__ import print_function


import numpy as np

from openmdao.api import Component from openmdao.api import ExecComp, IndepVarComp, Group, NLGaussSeidel, \
                         ScipyGMRES

class SellarDis1(Component):
    """Component containing Discipline 1."""

    def __init__(self):
        super(SellarDis1, self).__init__()

        # Global Design Variable
        self.add_param('z', val=np.zeros(2))

        # Local Design Variable
        self.add_param('x', val=0.)

        # Coupling parameter
        self.add_param('y2', val=1.0)

        # Coupling output
        self.add_output('y1', val=1.0)
        self.execution_count = 0

    def solve_nonlinear(self, params, unknowns, resids):
        """Evaluates the equation
        y1 = z1**2 + z2 + x1 - 0.2*y2"""

        z1 = params['z'][0]
        z2 = params['z'][1]
        x1 = params['x']
        y2 = params['y2']

        unknowns['y1'] = z1**2 + z2 + x1 - 0.2*y2
        self.execution_count += 1
    def linearize(self, params, unknowns, resids):
        """ Jacobian for Sellar discipline 1."""
        J = {}

        J['y1','y2'] = -0.2
        J['y1','z'] = np.array([[2*params['z'][0], 1.0]])
        J['y1','x'] = 1.0

        return J


class SellarDis2(Component):
    """Component containing Discipline 2."""

    def __init__(self):
        super(SellarDis2, self).__init__()

        # Global Design Variable
        self.add_param('z', val=np.zeros(2))

        # Coupling parameter
        self.add_param('y1', val=1.0)

        # Coupling output
        self.add_output('y2', val=1.0)
        self.execution_count = 0
    def solve_nonlinear(self, params, unknowns, resids):
        """Evaluates the equation
        y2 = y1**(.5) + z1 + z2"""

        z1 = params['z'][0]
        z2 = params['z'][1]
        y1 = params['y1']

        # Note: this may cause some issues. However, y1 is constrained to be
        # above 3.16, so lets just let it converge, and the optimizer will
        # throw it out
        y1 = abs(y1)

        unknowns['y2'] = y1**.5 + z1 + z2
        self.execution_count += 1
    def linearize(self, params, unknowns, resids):
        """ Jacobian for Sellar discipline 2."""
        J = {}

        J['y2', 'y1'] = .5*params['y1']**-.5

        #Extra set of brackets below ensure we have a 2D array instead of a 1D array
        # for the Jacobian;  Note that Jacobian is 2D (num outputs x num inputs).
        J['y2', 'z'] = np.array([[1.0, 1.0]])

        return J



class SellarDerivatives(Group):
    """ Group containing the Sellar MDA. This version uses the disciplines
    with derivatives."""

    def __init__(self):
        super(SellarDerivatives, self).__init__()

        self.add('px', IndepVarComp('x', 1.0), promotes=['x'])
        self.add('pz', IndepVarComp('z', np.array([5.0, 2.0])), promotes=['z'])

        self.add('d1', SellarDis1(), promotes=['z', 'x', 'y1', 'y2'])
        self.add('d2', SellarDis2(), promotes=['z', 'y1', 'y2'])

        self.add('obj_cmp', ExecComp('obj = x**2 + z[1] + y1 + exp(-y2)',
                                     z=np.array([0.0, 0.0]), x=0.0, y1=0.0, y2=0.0),
                 promotes=['obj', 'z', 'x', 'y1', 'y2'])

        self.add('con_cmp1', ExecComp('con1 = 3.16 - y1'), promotes=['y1', 'con1'])
        self.add('con_cmp2', ExecComp('con2 = y2 - 24.0'), promotes=['con2', 'y2'])

        self.nl_solver = NLGaussSeidel()
        self.nl_solver.options['atol'] = 1.0e-12

        self.ln_solver = ScipyGMRES()
         from openmdao.api import Problem, ScipyOptimizer

top = Problem() top.root = SellarDerivatives()

#top.driver = ScipyOptimizer()
#top.driver.options['optimizer'] = 'SLSQP'
#top.driver.options['tol'] = 1.0e-8
#
#top.driver.add_desvar('z', lower=np.array([-10.0, 0.0]),
#                     upper=np.array([10.0, 10.0]))
#top.driver.add_desvar('x', lower=0.0, upper=10.0)
#
#top.driver.add_objective('obj')
#top.driver.add_constraint('con1', upper=0.0)
#top.driver.add_constraint('con2', upper=0.0)

top.setup()

# Setting initial values for design variables top['x'] = 1.0 top['z'] = np.array([5.0, 2.0])

top.run()

print("\n")

print("Coupling vars: %f, %f" % (top['y1'], top['y2']))


count1 = top.root.d1.execution_count 
count2 = top.root.d2.execution_count 
print("Number of discipline 1 and 2 calls (%i,%i)"% (count1,count2))

【问题讨论】:

    标签: mdf openmdao


    【解决方案1】:

    这是一个很好的观察。只要你有一个循环,“头”组件就会第二次运行。原因如下:

    如果您的模型包含包含隐式状态的组件,则单个执行如下所示:

    1. 调用solve_nonlinear执行组件
    2. 调用apply_nonlinear计算残差。

    我们在此模型中没有任何具有隐式状态的组件,但我们通过循环间接地创造了对组件的需求。我们的执行如下所示:

    1. 调用solve_nonlinear 执行所有组件。
    2. 仅在“头”组件上调用apply_nonlinear(缓存未知数,调用solve_nolinear,并保存未知数的差异)以生成我们可以收敛的残差。

    这里,头组件只是执行的第一个组件,但它决定了循环运行的顺序。您可以通过构建一个包含 2 个以上组件的循环来验证只有一个头组件获得额外的运行.

    【讨论】:

    • 感谢您的回答!我现在更好地理解了这种行为。
    • 我现在更好地理解了这种行为。但是,如果您使用带有 Newton 方法的 stateconnection 组件的教程,我希望现在我们添加了一个具有“apply_nonlinear”功能的组件,这两个学科将只为“solve_linear”部分调用。但在这里,学科一也被称为 2 次(顺序为:学科 1、学科 2、学科 1 和状态连接)。 openmdao 还需要一个“头”组件吗?
    • 我认为有了状态连接组件,模型中还是有循环的。您可以通过手动设置执行顺序来防止任何学科组件重复运行,以便状态连接组件(具有自己的 apply_linear)是顺序中的“头”或第一个组件。
    猜你喜欢
    • 2020-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-07
    • 1970-01-01
    • 1970-01-01
    • 2018-04-28
    相关资源
    最近更新 更多