Moving Some Systems Outside of the Optimization Loop#

Sometimes a model contains systems that don’t need to be executed during each iteration of an optimization loop. If those systems are expensive to execute, leaving them out of the optimization loop can significantly improve performance.

If the Problem option group_by_pre_opt_post is True, OpenMDAO will separate all of the Components in the model into pre, optimization, or post sets based on the dependency graph. Only those Components found in the optimization set will be executed during each iteration of the optimization, while the ones in the pre and post sets will only execute before or after the optimization loop.

It’s also possible, by setting the always_opt option on a component, to force that component to be included in the optimization loop regardless of its dependencies. Note that depending on what its dependecies actually are, moving it to the optimization loop may also force other components to move there even if they would normally be in pre or post.

Example#

import numpy as np

import openmdao.api as om
from openmdao.test_suite.components.exec_comp_for_test import ExecComp4Test

size = 3

prob = om.Problem(group_by_pre_opt_post=True)
prob.driver = om.ScipyOptimizeDriver(optimizer='SLSQP', disp=False)

model = prob.model

model.add_subsystem('pre1', ExecComp4Test('y=2.*x', x=np.ones(size), y=np.zeros(size)))
model.add_subsystem('pre2', ExecComp4Test('y=3.*x', x=np.ones(size), y=np.zeros(size)))

model.add_subsystem('iter1', ExecComp4Test('y=x1 + x2*4. + x3',
                                            x1=np.ones(size), x2=np.ones(size),
                                            x3=np.ones(size), y=np.zeros(size)))
model.add_subsystem('iter2', ExecComp4Test('y=.5*x', x=np.ones(size), y=np.zeros(size)))
model.add_subsystem('iter4', ExecComp4Test('y=7.*x', x=np.ones(size), y=np.zeros(size)))
model.add_subsystem('iter3', ExecComp4Test('y=6.*x', x=np.ones(size), y=np.zeros(size)))

model.add_subsystem('post1', ExecComp4Test('y=8.*x', x=np.ones(size), y=np.zeros(size)))
model.add_subsystem('post2', ExecComp4Test('y=x1*9. + x2*5', x1=np.ones(size),
                                            x2=np.ones(size), y=np.zeros(size)))

# we don't want ExecComps to be colored because it makes the iter counting more complicated
for comp in model.system_iter(typ=ExecComp4Test):
    comp.options['do_coloring'] = False
    comp.options['has_diag_partials'] = True


model.connect('pre1.y', ['iter1.x1', 'post2.x1'])
model.connect('pre2.y', 'iter1.x2')
model.connect('iter1.y', ['iter2.x', 'iter4.x'])
model.connect('iter2.y', 'post2.x2')
model.connect('iter3.y', 'post1.x')
model.connect('iter4.y', 'iter3.x')

prob.model.add_design_var('iter1.x3', lower=0, upper=10)
prob.model.add_constraint('iter2.y', upper=10.)
prob.model.add_objective('iter3.y', index=0)

prob.setup()
prob.run_driver()
Coloring for 'iter1' (class ExecComp4Test)

Jacobian shape: (3, 9)  (33.33% nonzero)
FWD solves: 3   REV solves: 0
Total colors vs. total size: 3 vs 9  (66.67% improvement)

Sparsity computed using tolerance: 1e-25
Time to compute sparsity:   0.0007 sec
Time to compute coloring:   0.0016 sec
Memory to compute coloring:   0.5000 MB

Coloring for 'iter2' (class ExecComp4Test)

Jacobian shape: (3, 3)  (33.33% nonzero)
FWD solves: 1   REV solves: 0
Total colors vs. total size: 1 vs 3  (66.67% improvement)

Sparsity computed using tolerance: 1e-25
Time to compute sparsity:   0.0003 sec
Time to compute coloring:   0.0008 sec
Memory to compute coloring:   0.0000 MB

Coloring for 'iter4' (class ExecComp4Test)

Jacobian shape: (3, 3)  (33.33% nonzero)
FWD solves: 1   REV solves: 0
Total colors vs. total size: 1 vs 3  (66.67% improvement)

Sparsity computed using tolerance: 1e-25
Time to compute sparsity:   0.0003 sec
Time to compute coloring:   0.0008 sec
Memory to compute coloring:   0.0000 MB

Coloring for 'iter3' (class ExecComp4Test)

Jacobian shape: (3, 3)  (33.33% nonzero)
FWD solves: 1   REV solves: 0
Total colors vs. total size: 1 vs 3  (66.67% improvement)

Sparsity computed using tolerance: 1e-25
Time to compute sparsity:   0.0003 sec
Time to compute coloring:   0.0009 sec
Memory to compute coloring:   0.0000 MB
False

Check pre/post via the command line#

Use the openmdao list_pre_post command to view the sets of components found in the pre and post sets. Any components not shown will be found in the optimization set and will run during each iteration of the optimization loop. Running the command on a script containing the example model above would show the following:

    Pre-optimization systems:
        pre1
        pre2

    Post-optimization systems:
        post1
        post2

Note that the pre and post sets will only be shown if the driver is an optimizer and the group_by_pre_opt_post option of the Problem is True.