{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# OpenMDAO\n", "\n", "## Define a problem in OpenMDAO\n", "\n", "This example does not intend to cover all the features of OpenMDAO\n", "For more details and tutorials on OpenMDAO, please refer to **[OpenMDAO's documentation](https://openmdao.org/newdocs/versions/latest/main.html)**.\n", "In this example, we solve a constrained problem given by\n", "\n", "$$\n", "\\underset{x_1, x_2 \\in \\mathbb{R}}{\\text{minimize}} \\quad x_1^2 + x_2^2\n", "\n", "\\newline\n", "\\text{subject to} \\quad x_1 \\geq 0\n", "\\newline\n", "\\quad \\quad \\quad \\quad x_1 + x_2 = 1\n", "\\newline\n", "\\quad \\quad \\quad \\quad x_1 - x_2 \\geq 1\n", "$$\n", "\n", "We know the solution of this problem is $x_1=1$, and $x_2=0$.\n", "However, we start from an initial guess of $x_1=500.0$, and $x_2=5.0$ for the purposes of this tutorial.\n", "\n", "The problem functions are written using OpenMDAO components (and groups) as follows:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "import openmdao.api as om\n", "\n", "# minimize x^2 + y^2 subject to x>=0, x+y=1, x-y>=1.\n", "\n", "class QuadraticComp(om.ExplicitComponent):\n", " def setup(self): \n", " # add_inputs\n", " self.add_input('x', 1.)\n", " self.add_input('y', 1.)\n", " \n", " # add_outputs\n", " self.add_output('objective')\n", " self.add_output('constraint_1')\n", " self.add_output('constraint_2')\n", "\n", " # declare_partials\n", " self.declare_partials(of='objective', wrt='*')\n", " self.declare_partials(of='constraint_1', wrt='x', val=1.)\n", " self.declare_partials(of='constraint_1', wrt='y', val=1.)\n", " self.declare_partials(of='constraint_2', wrt='x', val=1.)\n", " self.declare_partials(of='constraint_2', wrt='y', val=-1.)\n", "\n", " def compute(self, inputs, outputs):\n", " x = inputs['x']\n", " y = inputs['y']\n", "\n", " outputs['objective'] = x**2 + y**2\n", " outputs['constraint_1'] = x + y\n", " outputs['constraint_2'] = x - y\n", "\n", " def compute_partials(self, inputs, partials):\n", " x = inputs['x']\n", " y = inputs['y']\n", "\n", " partials['objective', 'x'] = 2 * x\n", " partials['objective', 'y'] = 2 * y" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once your functions are defined within an OpenMDAO `Component` or `Group`, \n", "create an OpenMDAO `Problem` object and add the `Component` or `Group` object\n", "as a subsystem of the `Problem` object's model.\n", "Next, specify the model's design variables, objective, and constraints.\n", "Lastly, set up the problem and define the initial values for the design variables." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# Create an OpenMDAO Problem object\n", "om_prob = om.Problem()\n", "\n", "# Add QuadraticFunc() as a subsystem to the OpenMDAO Problem object\n", "om_prob.model.add_subsystem('quadratic', QuadraticComp(), promotes=['*'])\n", "\n", "# Add optimization variables and functions to the Problem model\n", "om_prob.model.add_design_var('x', lower=0.)\n", "om_prob.model.add_design_var('y')\n", "om_prob.model.add_objective('objective')\n", "om_prob.model.add_constraint('constraint_1', equals=1.)\n", "om_prob.model.add_constraint('constraint_2', lower=1.)\n", "\n", "# Setup the OpenMDAO problem\n", "om_prob.setup()\n", "\n", "# Set initial values for the design variables\n", "om_prob.set_val('x', 500.)\n", "om_prob.set_val('y', 5.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After the OpenMDAO's `Problem` object is set up with initial values for all variables,\n", "create an `OpenMDAOProblem` object that wraps the `Problem` object for interfacing with modOpt." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/modopt/modopt/external_libraries/openmdao/openmdao_problem.py:47: UserWarning: This version of OpenMDAO wrapper does not support SURF paradigm.\n", " warnings.warn('This version of OpenMDAO wrapper does not support SURF paradigm.')\n" ] } ], "source": [ "import modopt as mo\n", "\n", "# Instantiate the modopt.OpenMDAOProblem() object that wraps for modopt\n", "# the Problem() object defined earlier, and name your problem\n", "prob = mo.OpenMDAOProblem(problem_name='quadratic_openmdao', \n", " om_problem=om_prob)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Solve your problem using an optimizer\n", "\n", "Once your problem model is wrapped for modOpt, import your preferred optimizer\n", "from modOpt and solve it, following the standard procedure.\n", "Here we will use the `SLSQP` optimizer from the SciPy library." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "----------------------------------------------------------------------------\n", "Derivative type | Calc norm | FD norm | Abs error norm | Rel error norm \n", "----------------------------------------------------------------------------\n", "\n", "Gradient | 1.0000e+03 | 1.0000e+03 | 1.5473e-05 | 1.5472e-08 \n", "Jacobian | 2.0000e+00 | 2.0000e+00 | 5.0495e-09 | 2.5248e-09 \n", "----------------------------------------------------------------------------\n", "\n", "\n", "\tSolution from Scipy SLSQP:\n", "\t----------------------------------------------------------------------------------------------------\n", "\tProblem : quadratic_openmdao\n", "\tSolver : scipy-slsqp\n", "\tSuccess : True\n", "\tMessage : Optimization terminated successfully\n", "\tStatus : 0\n", "\tTotal time : 0.013110160827636719\n", "\tObjective : 1.0000000068019972\n", "\tGradient norm : 2.000000006801997\n", "\tTotal function evals : 2\n", "\tTotal gradient evals : 2\n", "\tMajor iterations : 2\n", "\tTotal callbacks : 17\n", "\tReused callbacks : 0\n", "\tobj callbacks : 5\n", "\tgrad callbacks : 3\n", "\thess callbacks : 0\n", "\tcon callbacks : 6\n", "\tjac callbacks : 3\n", "\t----------------------------------------------------------------------------------------------------\n" ] } ], "source": [ "# Setup your preferred optimizer (SLSQP) with the Problem object \n", "# Pass in the options for your chosen optimizer\n", "optimizer = mo.SLSQP(prob, solver_options={'maxiter':20})\n", "\n", "# Check first derivatives at the initial guess, if needed\n", "optimizer.check_first_derivatives(prob.x0)\n", "\n", "# Solve your optimization problem\n", "optimizer.solve()\n", "\n", "# Print results of optimization\n", "optimizer.print_results()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Scaling API\n", "\n", "Please refer to the code snippet below as a guide for scaling \n", "the design variables, objective, and constraints independent \n", "of their definitions.\n", "```{warning}\n", "The results provided by the optimizer will always be scaled,\n", "while the values from the models will remain unscaled.\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Add optimization variables and functions to the Problem model\n", "om_prob.model.add_design_var('x', lower=0., scaler=2.)\n", "om_prob.model.add_design_var('y', scaler=2.)\n", "om_prob.model.add_objective('objective', scaler=5.)\n", "om_prob.model.add_constraint('constraint_1', equals=1., scaler=10.)\n", "om_prob.model.add_constraint('constraint_2', lower=1., scaler=100.)" ] } ], "metadata": { "kernelspec": { "display_name": "venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.13" } }, "nbformat": 4, "nbformat_minor": 2 }