下面是一个示例代码,其中BSplineComp与ExplicitComp或ExternalCodeComp组合在一起。这两者都进行相同的计算,并且两个分量的梯度都是使用有限差分计算的。
如果我运行Bspline+ExplicitComp版本,那么在2,3次迭代内就能得到结果。如果我运行Bspline+ExternalCodeComp版本,我必须等待很长时间。在这种情况下,它试图找到输出相对于每个输入的梯度。例如,有9个控制点被插值到B样条分量中的70个点。然后,外部分量必须与插值点一样多地求值(70次)
因此,在B样条与昂贵的外部代码相结合的情况下,有限差分所需的插值点数与其所需的点数一样多,这成为计算的瓶颈。
基于这一输入,我有两个问题
1-如果外部代码组件基于显式组件,那么导致行为差异的主要差异是什么?(考虑到两者的输入都是shape=70)
2-在前面提到的scneario中,b样条与昂贵的外部代码组合在一起,除了这里显示的方式之外,是否有一种更有效的方式来组合它们。
主代码:'external‘变量是切换外部/显式代码comp的标志。设置true/false以运行上面解释的两种情况。
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如下
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))发布于 2018-08-22 10:20:59
在这两种情况下,您的代码都需要3次迭代才能运行。外部代码的运行时间要长得多,这仅仅是因为file-io的开销,以及每次调用函数时都需要进行系统调用来假脱机一个新进程。是的,系统调用的开销很大,文件i/o也不便宜。如果你有一个更昂贵的分析,这不是什么大问题,但你可以看到为什么它应该被避免,如果可能的话。
在这种情况下,您可以降低FD成本。由于你只有9个B样条变量,你已经正确地推断出你可以运行更少的FD步骤。您希望使用OpenMDAO v2.4中的approximate semi-total derivative特性来跨组设置FD,而不是跨每个单独的组件设置FD。
它就像这样简单:
.
.
.
if external:
comp=ExternalAreaComp()
model.add_subsystem('AreaComp', comp)
else:
comp = AreaComp(lenrr=lenrr, rr=rr)
model.add_subsystem('AreaComp', comp)
model.approx_totals()
.
.
. https://stackoverflow.com/questions/51954750
复制相似问题