Modify Children of a Group with Configure Method

Most of the time, the setup method is the only one you need to define on a group. The main exception is the case where you want to modify a solver that was set in one of your children groups. When you call add_subsystem, the system you add is instantiated, but its setup method is not called until after the parent group’s setup method is finished with its execution. That means that anything you do with that subsystem (e.g., changing the nonlinear solver) will potentially be overwritten by the child system’s setup if it is assigned there as well.

To get around this timing problem, there is a second setup method called configure, that runs after the setup on all subsystems has completed. While setup recurses from the top down, configure recurses from the bottom up, so that the highest system in the hierarchy takes precedence over all lower ones for any modifications.

Here is a simple example where a lower system sets a solver, but we want to change it to a different one in the top-most system.

from openmdao.api import Problem, Group, ImplicitComponent, NewtonSolver, ScipyKrylov, NonlinearBlockGS

class ImplSimple(ImplicitComponent):

    def setup(self):
        self.add_input('a', val=1.)
        self.add_output('x', val=0.)

    def apply_nonlinear(self, inputs, outputs, residuals):
        residuals['x'] = np.exp(outputs['x']) - \
            inputs['a']**2 * outputs['x']**2

    def linearize(self, inputs, outputs, jacobian):
        jacobian['x', 'x'] = np.exp(outputs['x']) - \
            2 * inputs['a']**2 * outputs['x']
        jacobian['x', 'a'] = -2 * inputs['a'] * outputs['x']**2


class Sub(Group):
    def setup(self):
        self.add_subsystem('comp', ImplSimple())

    def configure(self):
        # This solver won't solve the sytem. We want
        # to override it in the parent.
        self.nonlinear_solver = NonlinearBlockGS()


class Super(Group):
    def setup(self):
        self.add_subsystem('sub', Sub())

    def configure(self):
        # This will solve it.
        self.sub.nonlinear_solver = NewtonSolver()
        self.sub.linear_solver = ScipyKrylov()


top = Problem()
top.model = Super()

top.setup(check=False)

print(isinstance(top.model.sub.nonlinear_solver, NewtonSolver))
print(isinstance(top.model.sub.linear_solver, ScipyKrylov))
True
True

Uses of setup vs. configure

To understand when to use setup and when to use configure, see the Theory Manual entry on how the setup stack works..