OpenMDAO

Define a problem in OpenMDAO

This example does not intend to cover all the features of OpenMDAO For more details and tutorials on OpenMDAO, please refer to OpenMDAO’s documentation. In this example, we solve a constrained problem given by

\[ \begin{align}\begin{aligned} \underset{x_1, x_2 \in \mathbb{R}}{\text{minimize}} \quad x_1^2 + x_2^2\\\newline \text{subject to} \quad x_1 \geq 0 \newline \quad \quad \quad \quad x_1 + x_2 = 1 \newline \quad \quad \quad \quad x_1 - x_2 \geq 1 \end{aligned}\end{align} \]

We know the solution of this problem is \(x_1=1\), and \(x_2=0\). However, we start from an initial guess of \(x_1=500.0\), and \(x_2=5.0\) for the purposes of this tutorial.

The problem functions are written using OpenMDAO components (and groups) as follows:

import openmdao.api as om

# minimize x^2 + y^2 subject to x>=0, x+y=1, x-y>=1.

class QuadraticComp(om.ExplicitComponent):
    def setup(self): 
        # add_inputs
        self.add_input('x', 1.)
        self.add_input('y', 1.)
        
        # add_outputs
        self.add_output('objective')
        self.add_output('constraint_1')
        self.add_output('constraint_2')

        # declare_partials
        self.declare_partials(of='objective', wrt='*')
        self.declare_partials(of='constraint_1', wrt='x', val=1.)
        self.declare_partials(of='constraint_1', wrt='y', val=1.)
        self.declare_partials(of='constraint_2', wrt='x', val=1.)
        self.declare_partials(of='constraint_2', wrt='y', val=-1.)

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

        outputs['objective'] = x**2 + y**2
        outputs['constraint_1'] = x + y
        outputs['constraint_2'] = x - y

    def compute_partials(self, inputs, partials):
        x = inputs['x']
        y = inputs['y']

        partials['objective', 'x'] = 2 * x
        partials['objective', 'y'] = 2 * y

Once your functions are defined within an OpenMDAO Component or Group, create an OpenMDAO Problem object and add the Component or Group object as a subsystem of the Problem object’s model. Next, specify the model’s design variables, objective, and constraints. Lastly, set up the problem and define the initial values for the design variables.

# Create an OpenMDAO Problem object
om_prob = om.Problem()

# Add QuadraticFunc() as a subsystem to the OpenMDAO Problem object
om_prob.model.add_subsystem('quadratic', QuadraticComp(), promotes=['*'])

# Add optimization variables and functions to the Problem model
om_prob.model.add_design_var('x', lower=0.)
om_prob.model.add_design_var('y')
om_prob.model.add_objective('objective')
om_prob.model.add_constraint('constraint_1', equals=1.)
om_prob.model.add_constraint('constraint_2', lower=1.)

# Setup the OpenMDAO problem
om_prob.setup()

# Set initial values for the design variables
om_prob.set_val('x', 500.)
om_prob.set_val('y', 5.)

After the OpenMDAO’s Problem object is set up with initial values for all variables, create an OpenMDAOProblem object that wraps the Problem object for interfacing with modOpt.

import modopt as mo

# Instantiate the modopt.OpenMDAOProblem() object that wraps for modopt
# the Problem() object defined earlier, and name your problem
prob = mo.OpenMDAOProblem(problem_name='quadratic_openmdao', 
                          om_problem=om_prob)
/Users/modopt/modopt/external_libraries/openmdao/openmdao_problem.py:47: UserWarning: This version of OpenMDAO wrapper does not support SURF paradigm.
  warnings.warn('This version of OpenMDAO wrapper does not support SURF paradigm.')

Solve your problem using an optimizer

Once your problem model is wrapped for modOpt, import your preferred optimizer from modOpt and solve it, following the standard procedure. Here we will use the SLSQP optimizer from the SciPy library.

# Setup your preferred optimizer (SLSQP) with the Problem object 
# Pass in the options for your chosen optimizer
optimizer = mo.SLSQP(prob, solver_options={'maxiter':20})

# Check first derivatives at the initial guess, if needed
optimizer.check_first_derivatives(prob.x0)

# Solve your optimization problem
optimizer.solve()

# Print results of optimization
optimizer.print_results()
----------------------------------------------------------------------------
Derivative type | Calc norm  | FD norm    | Abs error norm | Rel error norm 
----------------------------------------------------------------------------

Gradient        | 1.0000e+03 | 1.0000e+03 | 1.5473e-05     | 1.5472e-08    
Jacobian        | 2.0000e+00 | 2.0000e+00 | 5.0495e-09     | 2.5248e-09    
----------------------------------------------------------------------------


	Solution from Scipy SLSQP:
	----------------------------------------------------------------------------------------------------
	Problem                  : quadratic_openmdao
	Solver                   : scipy-slsqp
	Success                  : True
	Message                  : Optimization terminated successfully
	Status                   : 0
	Total time               : 0.013110160827636719
	Objective                : 1.0000000068019972
	Gradient norm            : 2.000000006801997
	Total function evals     : 2
	Total gradient evals     : 2
	Major iterations         : 2
	Total callbacks          : 17
	Reused callbacks         : 0
	obj callbacks            : 5
	grad callbacks           : 3
	hess callbacks           : 0
	con callbacks            : 6
	jac callbacks            : 3
	----------------------------------------------------------------------------------------------------

Scaling API

Please refer to the code snippet below as a guide for scaling the design variables, objective, and constraints independent of their definitions.

Warning

The results provided by the optimizer will always be scaled, while the values from the models will remain unscaled.

# Add optimization variables and functions to the Problem model
om_prob.model.add_design_var('x', lower=0., scaler=2.)
om_prob.model.add_design_var('y', scaler=2.)
om_prob.model.add_objective('objective', scaler=5.)
om_prob.model.add_constraint('constraint_1', equals=1., scaler=10.)
om_prob.model.add_constraint('constraint_2', lower=1., scaler=100.)