首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >与显式代码和外部代码相结合的BSpline具有不同的行为

与显式代码和外部代码相结合的BSpline具有不同的行为
EN

Stack Overflow用户
提问于 2018-08-22 02:34:42
回答 1查看 55关注 0票数 0

下面是一个示例代码,其中BSplineComp与ExplicitComp或ExternalCodeComp组合在一起。这两者都进行相同的计算,并且两个分量的梯度都是使用有限差分计算的。

如果我运行Bspline+ExplicitComp版本,那么在2,3次迭代内就能得到结果。如果我运行Bspline+ExternalCodeComp版本,我必须等待很长时间。在这种情况下,它试图找到输出相对于每个输入的梯度。例如,有9个控制点被插值到B样条分量中的70个点。然后,外部分量必须与插值点一样多地求值(70次)

因此,在B样条与昂贵的外部代码相结合的情况下,有限差分所需的插值点数与其所需的点数一样多,这成为计算的瓶颈。

基于这一输入,我有两个问题

1-如果外部代码组件基于显式组件,那么导致行为差异的主要差异是什么?(考虑到两者的输入都是shape=70)

2-在前面提到的scneario中,b样条与昂贵的外部代码组合在一起,除了这里显示的方式之外,是否有一种更有效的方式来组合它们。

主代码:'external‘变量是切换外部/显式代码comp的标志。设置true/false以运行上面解释的两种情况。

代码语言:javascript
复制
from openmdao.components.bsplines_comp import BsplinesComp
from openmdao.api import  IndepVarComp, Problem, ExplicitComponent,ExecComp,ExternalCodeComp
from openmdao.api import  ScipyOptimizeDriver, SqliteRecorder, CaseReader
import matplotlib.pyplot as plt
import  numpy as np

external=True # change this to true for the case with external code comp. or false for the case with explicit comp.  

rr=np.arange(0,70,1)
"Explicit component for the area under the line calculation" 
class AreaComp(ExplicitComponent):
    def initialize(self):
        self.options.declare('lenrr', int)        
        self.options.declare('rr', types=np.ndarray)        
    def setup(self):        
        self.add_input('h', shape=lenrr)
        self.add_output('area')
        self.declare_partials(of='area', wrt='h', method='fd')
    def compute(self, inputs, outputs):
        rr = self.options['rr']        
        outputs['area'] = np.trapz(rr,inputs['h'])          

class ExternalAreaComp(ExternalCodeComp):
    def setup(self):
        self.add_input('h', shape=70)
        self.add_output('area')

        self.input_file = 'paraboloid_input.dat'
        self.output_file = 'paraboloid_output.dat'

        # providing these is optional; the component will verify that any input
        # files exist before execution and that the output files exist after.
        self.options['external_input_files'] = [self.input_file]
        self.options['external_output_files'] = [self.output_file]

        self.options['command'] = [
            'python', 'extcode_paraboloid.py', self.input_file, self.output_file
        ]

        # this external code does not provide derivatives, use finite difference
        self.declare_partials(of='*', wrt='*', method='fd')

    def compute(self, inputs, outputs):
        h = inputs['h']

        # generate the input file for the paraboloid external code
        np.savetxt(self.input_file,h)
        # the parent compute function actually runs the external code
        super(ExternalAreaComp, self).compute(inputs, outputs)

        # parse the output file from the external code and set the value of f_xy
        f_xy=np.load('a.npy')

        outputs['area'] = f_xy


prob  = Problem()
model = prob.model

n_cp = 9
lenrr = len(rr)
"Initialize the design variables"
x = np.random.rand(n_cp)

model.add_subsystem('px', IndepVarComp('x', val=x))
model.add_subsystem('interp', BsplinesComp(num_control_points=n_cp,
                                           num_points=lenrr,
                                           in_name='h_cp',
                                           out_name='h'))

if external:
    comp=ExternalAreaComp()
    model.add_subsystem('AreaComp', comp)
else:
    comp = AreaComp(lenrr=lenrr, rr=rr)
    model.add_subsystem('AreaComp', comp)

case_recorder_filename2 = 'cases4.sql'
recorder2 = SqliteRecorder(case_recorder_filename2)
comp.add_recorder(recorder2)
comp.recording_options['record_outputs']=True
comp.recording_options['record_inputs']=True


model.connect('px.x', 'interp.h_cp')
model.connect('interp.h', 'AreaComp.h')
model.add_constraint('interp.h', lower=0.9, upper=1, indices=[0])

prob.driver = ScipyOptimizeDriver()
prob.driver.options['optimizer'] = 'SLSQP'
prob.driver.options['disp'] = True
#prob.driver.options['optimizer'] = 'COBYLA'
#prob.driver.options['disp'] = True

prob.driver.options['tol'] = 1e-9

model.add_design_var('px.x', lower=1,upper=10)
model.add_objective('AreaComp.area',scaler=1)

prob.setup(check=True)
#prob.run_model()
prob.run_driver()
cr = CaseReader(case_recorder_filename2)

case_keys = cr.system_cases.list_cases()
cou=-1
for case_key in case_keys:
    cou=cou+1
    case = cr.system_cases.get_case(case_key)
    plt.plot(rr,case.inputs['h'],'-*')

外部代码extcode_paraboloid.py如下

代码语言:javascript
复制
import numpy as np
if __name__ == '__main__':
    import sys

    input_filename = sys.argv[1]
    output_filename = sys.argv[2]

    h=np.loadtxt(input_filename)
    rr=np.arange(0,70,1)

    rk= np.trapz(rr,h)          
    np.save('a',np.array(rk))
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-08-22 10:20:59

在这两种情况下,您的代码都需要3次迭代才能运行。外部代码的运行时间要长得多,这仅仅是因为file-io的开销,以及每次调用函数时都需要进行系统调用来假脱机一个新进程。是的,系统调用的开销很大,文件i/o也不便宜。如果你有一个更昂贵的分析,这不是什么大问题,但你可以看到为什么它应该被避免,如果可能的话。

在这种情况下,您可以降低FD成本。由于你只有9个B样条变量,你已经正确地推断出你可以运行更少的FD步骤。您希望使用OpenMDAO v2.4中的approximate semi-total derivative特性来跨组设置FD,而不是跨每个单独的组件设置FD。

它就像这样简单:

代码语言:javascript
复制
.
. 
. 

if external:
    comp=ExternalAreaComp()
    model.add_subsystem('AreaComp', comp)
else:
    comp = AreaComp(lenrr=lenrr, rr=rr)
    model.add_subsystem('AreaComp', comp)

model.approx_totals()

.
. 
. 
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51954750

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档