From e6cdfa46d7be21aaa5906d54813e798d09cc2180 Mon Sep 17 00:00:00 2001 From: Matt W Date: Thu, 7 Jan 2021 06:05:33 -0500 Subject: [PATCH] Update workbook 6 --- .../J6_Optimization.ipynb | 914 +++++++++++------- .../J6_Optimization_solved.ipynb | 724 ++++++++++++++ .../Tutorial 6 - Optimization/SpecTable1.png | Bin 0 -> 13412 bytes .../Treatment_Train.png | Bin 0 -> 31344 bytes .../Tutorial 6 - Optimization/wastewater.png | Bin 35901 -> 0 bytes .../M6_Optimization.mlx | Bin 53218 -> 53221 bytes .../M6_Optimization_solved.mlx | Bin 53423 -> 53221 bytes 7 files changed, 1273 insertions(+), 365 deletions(-) create mode 100644 tutorial/julia/Tutorial 6 - Optimization/J6_Optimization_solved.ipynb create mode 100644 tutorial/julia/Tutorial 6 - Optimization/SpecTable1.png create mode 100644 tutorial/julia/Tutorial 6 - Optimization/Treatment_Train.png delete mode 100644 tutorial/julia/Tutorial 6 - Optimization/wastewater.png diff --git a/tutorial/julia/Tutorial 6 - Optimization/J6_Optimization.ipynb b/tutorial/julia/Tutorial 6 - Optimization/J6_Optimization.ipynb index bf9179b..3fce1ec 100644 --- a/tutorial/julia/Tutorial 6 - Optimization/J6_Optimization.ipynb +++ b/tutorial/julia/Tutorial 6 - Optimization/J6_Optimization.ipynb @@ -4,499 +4,678 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Initial Boundary Value PDEs \n", + "# Optimization \n", "\n", "
\n", "\n", - "# Learning Objectives\n", + "## Learning Objectives \n", "\n", - "- Be able to recognize Laplace & Poisson equations\n", - "- Describe the basic steps involved in the Method of Lines (MOL)\n", - "- Apply knowledge of how discretize interior nodes\n", - "- Apply knowledge of how to discretize exterior nodes for each boundary condition (Neumann, Robin, Dirichlet)\n", - "- Formulate the system of ODEs created with MOL by interlacing variables on a rectangular grid.\n", + "- Classify optimization problems as linear, quadratic, nonlinear, and mixed-integer problems.\n", + "- Learn how to interpret the solutions of optimization problems.\n", + "- Learn how to input optimization problems in Julia/JuMP).\n", + "- Assess the sensitivity of a solution with respect to constraints.\n", + "- Learn how to formulate and solve optimization problems for multiple examples.\n", "\n", - "
" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Simulating a graded catalyst bed\n", + "
\n", "\n", - "A suction pyrometer is a commonly used instrument for measuring gas phase temperatures in extemely hot environments (one example given [here](https://pubmed.ncbi.nlm.nih.gov/24248279/)). In these environments, it's quite typical to observe a large amount of radiative heat flux. This will typically dominate the heat transfer between the gas phase and an object. As a consequence, directly immersing a measurement device, such as a thermocouple or resistance temperature detector (RTD) into the process gas, will yield meaningless results. Moreover, an object left in this environment for a long period of time will increase in temperature until it too emits a significant amount of thermal radiation. So simply shielding a sensor isn't a viable solution either. This issue is circumvented by using a suction pyrometer. This device consistes of a small jacketing tube attached to a sensor (thermocouple or RTD) and a vacuum pump. By drawing gas through the tube, the jacket is cooled preventing emission, and allowing for a valid reading from the internal sensor. A picture is included below:\n", + "## Organization \n", "\n", - "\n", + "- First, we'll review some of the types of optimization problems.\n", + "- Next, we'll solve a Sudoku puzzle.\n", + "- You'll formulate and solve a simple blending and pooling problem.\n", + "- Lastly, we'll analyze the blending and pooling problem from a sensitivity perspective.\n", + " \n", + "
\n", "\n", - "The key design considerations behind the suction pyrometer are the superficial velocity and Biot number needed to ensure that the temperature rise of the process gas in the jacket is minimal. We'll construct a basic model of heat transfer in this tube and use it to determine adequate settings for reading our temperature range of interest. \n", + "## Problem Forms \n", "\n", - "We'll consider a constant heat capacity gas which we're measuring using a 1ft (0.3048 m) by 0.25 inch (0.00635 m) diameter tube. Heat transfer at the wall surface is proportional to the temperature difference and characterized by the Biot number.\n", + "Optimizers generally exploit a specific mathematical structure in the problem formulation as a means to solve a problem. In fact, some underlying assumptions are always required to ensure a problem is optimized. One common example arrise from combining KKT conditions with constraint qualifications or alternatively regularity conditions for nonlinear programs.\n", "\n", - "## A basic descriptive model\n", + "These problem types range from linear programs for which problems with 100k variables and constraints may routinely be solved [in less than a minute](http://plato.asu.edu/ftp/lpsimp.html) to continuous convex problems to nonconvex nonlinear mixed-integer problems (were some problems with fewer than 5 variables have yet to be solved). As such, using an appropriate optimizer for a particular problem type can transform an unsolveable problem into a trival one. The most heavily studied and widely used descriptors for each program are linear, quadratic, nonlinear, and mixed-integer.\n", "\n", - "A two-dimensional steady-state energy balance is given by:\n", + "**Types of based on objectives/constraints:**\n", "\n", - "\\\\[u_s C_{p} \\frac{\\partial T}{\\partial z} = \\frac{\\Lambda}{R}\\frac{\\partial}{\\partial R}\\left(R\\frac{\\partial T}{\\partial R}\\right), \\\\]\n", + "- **Linear:** All the constraints and the objective are linear.\n", + "- **Quadratic** all the constraints and the objective are quadratic or linear.\n", + "- **Nonlinear** at least one constraint or the objective is not linear.\n", "\n", - "**Limiting cases:** Consider briefly under what conditions we might assume that this equation reduces to the Poisson or Laplace equations below.\n", + "**Types based on manner of variable used:**\n", "\n", - "1. **Poisson Equation:** \\\\[\\nabla^2 x = f(x) \\\\]\n", - "2. **Laplace Equation:** \\\\[\\nabla^2 x = 0 \\\\] \n", + "- **Continuous:** All variables can vary between values in some possibly open interval.\n", + "- **Integer:** All variables are integer valued.\n", + "- **Mixed-integer:** The problem contains both integer and continuous variables.\n", "\n", - "Next, we assume a sufficient cooling flow exists to hold the reactor wall temperature constant at a fixed value of . Then, we can write a symmetry and cooling boundary condition:\n", + "One of the central focuses of optimization research has focused on developing specialized routines for solving programs with other special forms. A number of other important forms include [specially-ordered sets](https://link.springer.com/article/10.1007/BF01589393), [second-order cones](https://ieeexplore.ieee.org/abstract/document/6983691), [equilibrium constraints](https://faculty.wcas.northwestern.edu/~lchrist/Ferris_mathematical_programs2.pdf), as well as a myriad of special nonlinear forms. When attempting to solve a difficult model it's worth checking a few references (such as [Mosek's Modeling Cookbook](https://docs.mosek.com/MOSEKModelingCookbook-letter.pdf)) to see if the problem can be re-written as simplier form or a simplier problem type.\n", "\n", - "\\\\[\\left.\\frac{\\partial T}{\\partial R}\\right\\vert_{R=0} = 0 \\qquad \\qquad \\left.\\frac{\\partial T}{\\partial R}\\right\\vert_{R=R_t} = -Bi(T - T^c) \\\\]\n", + "This trend has lead to the development of a number of specialized languages and software packages used to describe optimization problems interface with a myriad of different optimizers. In Python, the main tools for this are [Pyomo]() and [CVXOPT](https://cvxopt.org/). In Julia, we have [JuMP](https://jump.dev/JuMP.jl/stable/), and [Convex.jl](https://jump.dev/Convex.jl/stable/). A number of other commercial offerings available include [GAMS](https://www.gams.com/), [AIMMS](https://en.wikipedia.org/wiki/AIMMS), and [AMPL](https://ampl.com/) along with a number of offerings associated with commercially available solvers such as [CPLEX Optimization Studio by IBM](https://www.ibm.com/analytics/cplex-optimizer) or [Xpress Mosel for FICO Express](https://www.fico.com/en/products/fico-xpress-optimization). We'll work through formulating and solving a couple of optimization problems using JuMP in this workbook.\n", "\n", - "Note that the symmetry condition (at $R = 0$) is of the Neumann type and the wall boundary condition (at $R = R_t$) is of the Robin type. " + "
" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 1, "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m registry at `C:\\Users\\wilhe\\.julia\\registries\\General`\n", + "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Project.toml`\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Manifest.toml`\n", + "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Project.toml`\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Manifest.toml`\n", + "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Project.toml`\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Manifest.toml`\n" + ] + } + ], "source": [ - "## Further model development\n", + "# Run this block once. Adds Julia packages to your Julia local installation from the package manager\n", "\n", - "Assume the inlet stream is well-mixed and at known temperature:\n", + "using Pkg # Import functions from the package manager into this session.\n", + "Pkg.add(\"JuMP\") # A modeling language for Mathematical optimization in Julia.\n", + "Pkg.add(\"Ipopt\") # A highly performant nonlinear optimizer.\n", + "Pkg.add(\"GLPK\") # An open-source linear and mixed-integer linear optimizer\n", "\n", - "\\\\[\\mathbf T(z=0)=\\mathbf T_{in}. \\\\]\n", - "\n", - "This is because boundary conditions are known in the radial dimension and the system is fully specified at the reactor entrance for all variables (initial temperature).\n", - " \n", - "
\n", - "Note: This problem is treated as an IBVP and not a 2D BVP.\n", - "
" + "nothing # suppresses long output on last line" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Method of Lines Derivation\n", + "
\n", + "\n", + "## Solving Sudoku with Mixed-Integer Programming* \n", "\n", - "We now make use of second-order approximations for the derivatives in the radial direction\n", + "* Adapted from a now defunct example by Ian Dunning at https://www.juliaopt.org/notebooks/JuMP-Sudoku.html.\n", "\n", - "\\\\[\\left.\\frac{\\partial u}{\\partial R}\\right\\vert_{r=R_j} = \\frac{u_{j+1} - u_{j-1}}{2\\Delta R} \\\\]\n", - "\\\\[\\left.\\frac{\\partial^2 u}{\\partial R^2}\\right\\vert_{r=R_j} = \\frac{u_{j+1} - 2u_{j} + u_{j-1}}{\\Delta R^2} \\\\]\n", + "Sudoku is a popular number puzzle. The goal is to place the digits 1,...,9 on a nine-by-nine grid, with some of the digits already filled in. Your solution must satisfy the following rules:\n", "\n", - "where \\\\[\\Delta R_{j} = R_{j+1} - R_{j}\\\\]" + " The numbers 1 to 9 must appear in each 3x3 square\n", + " The numbers 1 to 9 must appear in each row\n", + " The numbers 1 to 9 must appear in each column\n", + "\n", + "Any combination satisfying the above rules is a valid solution. Thus, we're simply seeking any feasible solution. While this isn't strictly an optimization problem (it's a constraint satisfaction problem), but we can make it into equivalent optimization problem. Namely, we can just specify that the objective is some constant value of our choosing. \n", + "\n", + "We can model this problem using 0-1 integer programming: a problem where all the decision variables are binary. We'll use JuMP to create the model, and then we can solve it with any integer optimizer." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 2, "metadata": {}, + "outputs": [], "source": [ - "### Method of Lines Derivation - Interior\n", - "Applying these rules to the energy balance for the interior nodes, where is the number of spatial nodes for discretizing the dimension, we have\n", + "using JuMP, GLPK # import functions from the JuMP & GLPK package into your current environment\n", "\n", - "\\\\[\\frac{\\partial T}{\\partial z} = \\left(u_s C_P\\right)^{-1}\\left(\\frac{\\Lambda}{R}\\frac{\\partial}{\\partial R}\\left(R\\frac{\\partial T}{\\partial R}\\right)\\right) \\\\]\n", + "# create a model called \"sudoku\" which holds all variables, constraints, \n", + "# functions, and options needed to solve the sudoku problem\n", + "sudoku = Model(GLPK.Optimizer)\n", "\n", + "# add binary variables\n", + "@variable(sudoku, x[i=1:9, j=1:9, k=1:9], Bin) # Bin indicates the variable is binary (has a value of 0 or 1)\n", "\n", - "\\\\[\\left.\\frac{d T}{d z}\\right\\vert_{R=R_j} = \\left(u_s C_{p}\\right)^{-1}\\left(\\frac{D}{R}\\frac{T_{j+1} - T_{j-1}}{2\\Delta R} + D\\frac{T_{j+1} - 2T_{j} + T_{j-1}}{\\Delta R^2}\\right) \\qquad j = 2, \\ldots, N_{r-1} \\\\]" + "nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Method of Lines Derivation - Center\n", - "The discretization at the center node ($R=0, j=1$) yields: \n", - "\n", - "\\\\[\\frac{d T_1}{d z} = \\left(u_s C_P\\right)^{-1}\\left(\\frac{\\Lambda}{R}\\frac{T_{2} - T_{0}}{2\\Delta R} + \\Lambda \\frac{T_{2} - 2T_{1} + T_{0}}{\\Delta R^2}\\right).\\\\]\n", - "\n", - "From the boundary condition we have \n", - "\n", - "\\\\[\\left.\\frac{\\partial T}{\\partial R}\\right|_{R=0} = 0 \\Rightarrow \\frac{T_{2} - T_{0}}{2\\Delta R} = 0 \\Rightarrow T_{0} = T_{2} \\\\]\n", - "\n", - "Substituting this into the previous expression yields:\n", - "\n", - "\\\\[\\frac{d T_1}{d z} = \\left(u_s C_P\\right)^{-1}\\left(\\frac{2\\Lambda}{\\Delta R^2}(T_{2} - T_{1})\\right). \\\\]\n", - "\n", - "
\n", - "Activity! This derivation proceeded by solving obtaining algebraic equations from the boundary condition, solving them analytically for the fictive node value, and then substituting this expression in. We could have instead solved a system of coupled differential and algebraic equations (referred to as a differential-algebraic system of equations or DAEs). In many cases, the algebraic equations formed may not have an analytic (closed form) solution and we must resort to solving DAEs. What type of boundary condition(s) may lead algebraic equations with no analytic solution?\n", - "
" + "Only one digit can be in each square. So for each square represented by a pair of i,j variables the sum over k should be equal to one." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "for i = 1:9, j = 1:9 \n", + " @constraint(sudoku, sum(x[i,j,k] for k=1:9) == 1)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each variable can only appear once in each column." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "for j = 1:9, k = 1:9\n", + " @constraint(sudoku, sum(x[i,j,k] for i=1:9) == 1)\n", + "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Method of Lines Derivation - Wall\n", - "The discretization at the wall node yeilds:\n", - "\n", - "\\\\[\\frac{d T_{N_r}}{d z} = \\left(u_s C_p\\right)^{-1}\\left(\\frac{\\Lambda}{R}\\frac{T_{N_{r+1}} - T_{N_{r-1}}}{2\\Delta R} + \\Lambda \\frac{T_{N_{r+1}} - 2T_{N_r} + T_{N_{r-1}}}{\\Delta R^2}\\right) \\\\]\n", - "\n", - "From the boundary condition we have\n", - "\n", - "\\\\[\\left.\\frac{\\partial T}{\\partial R}\\right|_{R=R_t} = 0 \\Rightarrow \\frac{T_{N_{r+1}} - T_{N_{r-1}}}{2\\Delta R} = -Bi(T_{N_r} - T^c) \\Rightarrow T_{N_{r+1}} = T_{N_{r-1}} -2Bi\\Delta R (T_{N_r} - T^c). \\\\]\n", - "\n", - "Substituting this in yields:\n", - "\n", - "\\\\[\\frac{d T_{N_r}}{d z} = \\left(u_s C_p\\right)^{-1}\\left(\\frac{Bi\\Lambda}{R}(T^c - T_{N_r}) + \\frac{2\\Lambda}{\\Delta R^2}(T_{N_{r-1}} - T_{N_r} + Bi\\Delta R (T^c - T_{N_r}))\\right). \\\\]\n", - "\n", - "We'll now use a ODE solver readily available in DifferentialEquations.jl to integrate the above system of differential equations.\n", - "\n", "
\n", - "Activity! Manipulate the code below, to determine the superficial velocity at which a temperature rise of 2K is observed in the tube for gas temperatures ranging from 900 to 1000.\n", + "INTERACTIVE! Each variable can only appear once in each row. Fill in the appropriate expression.\n", "
" ] }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "for ind = 1:9, k = 1:9\n", + " # ADD CODE HERE\n", + "end" + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Solution\n", - "\n", - "For simplicity's sake, we'll assume the following physical constants are valid over the range of interest (constant heat capacities, and thermal heat dispersion coefficients)." + "Each variable can only appear once in each 3-by-3 subgrid." ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "# Input model parameters\n", - "tube_length = 0.3048 # tube length [m]\n", - "tube_radius = 0.00635/2 # radius [m]\n", - "\n", - "heat_coeff = 7.3871/3.6 # heat dispersion coefficient [J/(m s K)]\n", - "\n", - "Tc = 1400 # Tube temperature [K]\n", - "T0 = 900 # Gas design temperature [K] MANIPULATE THIS \n", - "biot_number = 0.001 # Biot number, Bi \n", - "\n", - "us = 0.1 # superficial flow rate (m/h) MANIPULATE THIS \n", - "Cp = 0.74 # pseudo heat capacity\n", - "\n", - "nr = 20 # number of discretization points in radial direction\n", - "delR = tube_radius/(nr - 1); " + "# i is the top left row, j is the top left column\n", + "# We'll sum from i to i+2, e.g. i=4, r=4, 5, 6\n", + "for i = 1:3:7, j = 1:3:7, k = 1:9\n", + " @constraint(sudoku, sum(x[r,c,k] for r=i:i+2, c=j:j+2) == 1)\n", + "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We'll use the adaptive step-size implicit Euler method built into DifferentialEquations.jl to solve the problem. You can read more about the built-in solvers and functionality for DifferentialEquations.jl [here](https://diffeq.sciml.ai/v2.0/). We first define the right-hand side function for the differential equation below. " + "Fills in the provided values using zero to represent an empty cell" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, + "outputs": [], + "source": [ + "# enter the initial values\n", + "init_val = [ 5 3 0 0 7 0 0 0 0;\n", + " 6 0 0 1 9 5 0 0 0;\n", + " 0 9 8 0 0 0 0 6 0;\n", + " \n", + " 8 0 0 0 6 0 0 0 3;\n", + " 4 0 0 8 0 3 0 0 1;\n", + " 7 0 0 0 2 0 0 0 6;\n", + " \n", + " 0 6 0 0 0 0 2 8 0;\n", + " 0 0 0 4 1 9 0 0 5;\n", + " 0 0 0 0 8 0 0 7 9]\n", + "\n", + "# if the value is specified initially add a constraint fixing the variables to that value.\n", + "for i = 1:9, j = 1:9\n", + " if init_val[i,j] != 0\n", + " @constraint(sudoku, x[i,j,init_val[i,j]] == 1)\n", + " end\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "What type of optimization problem was the sudoku? Is GLPK an appropriate optimizer for this type of problem? You'll want to search for GLPK's documentation and check there.\n", + "
\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "f (generic function with 1 method)" + "9×9×9 Array{Float64,3}:\n", + "[:, :, 1] =\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + " 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 2] =\n", + " 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 3] =\n", + " 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + "\n", + "[:, :, 4] =\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 5] =\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 6] =\n", + " 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 1.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 7] =\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + " 1.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0\n", + "\n", + "[:, :, 8] =\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n", + " 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 9] =\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0" ] }, - "execution_count": 7, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "function f(out, y, p, t)\n", - " # Set up center boundary condition rhs equations\n", - " out[1] = (Cp*us)^(-1)*((2*heat_coeff/delR^2)*(y[3] - y[1]))\n", + "optimize!(sudoku)\n", "\n", - " # Define interior node right-hand-side equations\n", - " for i = 2:(nr-1)\n", - " r = i*delR; # get node radial position\n", - " # energy balance discretized equation \n", - " out[i] = (heat_coeff*(Cp*us)^-1)*((y[i+1] - y[i])/(2*delR*r) + (y[i+1] - 2*y[i] + y[i-1])/delR^2)\n", - " end\n", - "\n", - " # Set up wall boundary condition rhs equations\n", - " r = (nr-1)*delR\n", - " out[nr] = (2*heat_coeff*(Cp*us)^(-1))*(y[nr - 1]/delR^2 - y[nr]*(biot_number*(1/r + 1/delR) + 1/delR^2) + \n", - " biot_number*(1/(2*r) + 1/delR)*Tc)\n", - " return \n", - "end" + "x_val = value.(x) # Extract the values of x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now we build and solve the corresponding ODEs problem. \n", + "To display the solution, we need to look for the values of **x**[i, j, k] that are equal to 1.\n", "\n", - "
\n", - "Note: If this is your first time using DifferentialEquations.jl or Plots.jl some time may be spent precompiling. So taking a coffee break after clicking run on the below cell wouldn't be ill-advised.\n", + "
\n", + "INTERACTIVE! Unpack the solution to a 9-by-9 matrix and confirm this is a valid Sudoku solution. Note that the \n", + "solution returned may not be an integer. So you'll need to round this to the nearest integer using iround(y). \n", "
" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 9, "metadata": {}, "outputs": [ { - "name": "stderr", - "output_type": "stream", - "text": [ - "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n", - "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Project.toml`\n", - "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Manifest.toml`\n", - "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions..." - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "ODE Integrated" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n", - "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Project.toml`\n", - "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Manifest.toml`\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] + "data": { + "text/plain": [ + "9×9 Array{Float64,2}:\n", + " 5.0 3.0 2.0 2.0 7.0 6.0 1.0 3.0 7.0\n", + " 6.0 4.0 7.0 1.0 9.0 5.0 9.0 5.0 8.0\n", + " 1.0 9.0 8.0 3.0 4.0 8.0 4.0 6.0 2.0\n", + " 8.0 1.0 9.0 9.0 6.0 1.0 5.0 4.0 3.0\n", + " 4.0 5.0 3.0 8.0 5.0 3.0 7.0 9.0 1.0\n", + " 7.0 2.0 6.0 7.0 2.0 4.0 8.0 2.0 6.0\n", + " 2.0 6.0 1.0 6.0 3.0 2.0 2.0 8.0 4.0\n", + " 3.0 8.0 5.0 4.0 1.0 9.0 6.0 1.0 5.0\n", + " 9.0 7.0 4.0 5.0 8.0 7.0 3.0 7.0 9.0" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "import Pkg; Pkg.add(\"DifferentialEquations\"); Pkg.add(\"Plots\")\n", - "using DifferentialEquations\n", + "final_val = zeros(9,9)\n", + "\n", + "# ADD HERE" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "## Integrating water system treatment in the process industry \n", + "\n", + "* Adapted from [R. Karuppiah & I. Grossmann. Global optimization for the synthesis of integrated water systems in chemical processes. Computers and Chemical Engineering 30 (2006) 650–673](https://www.sciencedirect.com/science/article/abs/pii/S0098135405002991)\n", + "\n", + "The efficient utilitization of water will be an integral part to solving the numerous water management issues of today and the near future. Increasing usage of water, anticipated water scarities, and changing regulations are expected to result in numerous related challenges in the next decades. These challenges may extend to the adaptation of numerous industrial processes. as fresh water is a key resource in such processes. These include washing operations in the food processing, thermal processing (cooling, quenching, and scrubbing) in iron and steel manufacturing, and the desalination of crude oil. In each of this processes, some level of contamination is introduced into the freshwater by the process. This process water is then treated in a central facility to remove contaminants and satisfy local regulatory specifications for the disposal of wastewater. In design or improvement of these central facilities, three key questions must be considered. 1) What unit operations need to be included? 2) How should these process units be connected? 3) How should one operate these processes units?\n", + "\n", + "A typical design objective for a wastewater treatment plant may be to minimize it's overall cost in a given year of operation. That is we minimize the sum of the cost fo freshwater intake, the investment cost associated with each treatment unit, and the operating cost of each unit. This can be achieved by minimizing the follow objective:\n", + "\n", + "$\\Phi = A_R\\sum_{j} C_j^{Inv} F_j^{\\alpha} + H\\sum_{j} F_j C_j^{Op} + H F_{fw} C_{fw}$ \n", + "\n", + "where H is the number of hours of operation per annum ($h$); the cost of freshwater is given by $C_{fw}$ (`$/ton`); the fresh water intake mass if given by $F_{fw}$ (`ton/h`); The annualized factor for investment on treatment units is given by $A_R$; the $C_j^{Inv} F_j^{\\alpha}$ is the investment cost (`$`) of the $j$-th treatment unit which treats a flowrate stream of contaminated water $F_j$ (`ton/h`); $C_j^{Op} F_j^{\\alpha}$ is the operating cost (`$/h`) of the $j$-th treatment unit which treats a flowrate of contaminated water $F_j$ (`ton/h`); the parameter $\\alpha$ is a constant cost function exponent $0 \\leq \\alpha \\leq 1$.\n", + "\n", + "The **mixers** units can be described as follows:\n", + "- The outlet flowrate is the sum of the inlet stream flow rate: $F^{outlet} = \\sum_{i = 1}^{n} F^{inlet}_i $ where $F^{outlet}$, $F^{inlet}_1$, $\\ldots$, $F^{inlet}_n$ are given in (tons/hr) and $n$ is the number of inlets streams.\n", + "- The mass of each contaminant in the outlet equals the mass of each contaminant in the inlet streams: $F^{outlet}C_j^{outlet} = \\sum_{i = 1}^{n} F^{inlet}_i C_j^{inlet}$ \n", + "\n", + "The **splitter** units:\n", + "- The outlet flowrates are the sum of the inlet flowrate: $F^{inlet} = \\sum_{i = 1}^{m} F^{outlet}_i $ where $F^{inlet}$, $F^{outlet}_1$, $\\ldots$, $F^{outlet}_m$ are given in (tons/hr) and $m$ is the number of outlet streams.\n", + "- The each outlet streams contaminant concentration is equal to the inlet stream contaminant concentration: $F^{outlet}_m$. \n", + "\n", + "A fixed mass of contaminant is added to a stream by each **process unit**:\n", + "- A process unit has a single inlet stream and a single outlet stream: $F^{inlet} = F^{outlet}$\n", + "- The process unit reduces the l $F^{inlet}C_j^{inlet} + L^{p}_j = F^{outlet}C_j^{outlet}$\n", + "\n", + "The **treatment units**:\n", + "- A process unit has a single inlet stream and a single outlet stream with the same flowrate: $F^{inlet} = F^{outlet}$\n", + "- The concentration of contaminant in the outlet $C_j^{outlet} = \\beta^{t}_j C_j^{inlet}$ where $\\beta^{t}_j$ is the removal ratio of $j$.\n", + "\n", + "Two contaminants A & B are introduced into the system and the concentration of each contaminant must be reduced to 10ppm prior to discharging it into the enviroment. The cost of freshwater is assumed to be $1/ton, the annualized factor for investment on the treatment unit is taken to be 0.1, and the total time of operation of the plant is 8000h/year. While numerous different treatment technologies and separation train configurations are possible, we've settled on using two distinct units to perform a primary and secondary treatment. We'd like to determine whether the inclusion of a recycle stream(s) may reduce the process cost and whether is preferable to have the recyle stream bridge one unit or two.\n", + "\n", + "\n", + "\n", + "The flowrate of water required by each process unit, along with containment inlet concentration limits, and amount of contaminant discharged are given in Table 1. We'll assume an 𝛼 value of 0.7, an investment. The process unit uses 40 ton/h of freshwater and discharges 1 kg/h of A and 1kg/h of B.\n", + "\n", + "The efficiency of the treatment units are given by the removal ratio in Table 2 along with cost model constants: \n", + "\n", + "\n", + "\n", + "
\n", + "INTERACTIVE! Is this model convex or nonconvex? Continuous or mixed-integer? Why? \n", + "
\n", + "\n", + "We've now started to define objective and constraint functions which compute the concentration and flow rates associated with each stream and the overall system cost. Generally, there are two approaches to doing this. We can introduce a new variable for each concentration and flowrate and allow the optimizer to enforce mass balances. Alternative, we can algebraiclly re-arrange these mass balances and avoid introducing a large number of variables. In some cases, the former approach exposes special forms that an optimizer may use effectively. In other cases, the decreased number of variables may be more beneficial. We primarily use the later approach but we'll introduce variables to handle the recycle-loops which generally cannot be re-arranged into an explicit algebraic form. The optimization variables are $x=\\left(F_{\\mathrm{feed}} ,F_2 ,F_9 ,C_{A,4} ,C_{B,4} ,C_{A,9} ,C_{B,9} ,F_6 ,C_{A,8} ,C_{B,8} \\right)$. Take some time to inspect the constraint function and reflect on the choice of these variables.\n", + "\n", + "
\n", + "Generally optimizers require that all variables have both lower (lb) and upper (ub) bounds defined. If there aren't any obvious choices in a problem, you may have to exercise your best judgement and choose reasonable bounds. Equipment based bounds on flowrate are provided below. Further, we assume 0.1 is a sufficiently high concetration such that it will be an inactive constraint. Furthermore, an initial guess (x0) often must be specified. For convex problems, the solution obtained will generally not depend on this guess. \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# Bounds\n", + "lb = [0.1; 0.1; 0.1; 0.0; 0.0; 0.0; 0.0; 0.1; 0.0; 0.0]\n", + "ub = [100.0; 100.0; 100.0; 0.1; 0.1; 0.1; 0.1; 100.0; 0.1; 0.1]\n", "\n", - "y0 = [T0 for i = 1:nr] # set initial condition to inlet temperature\n", - "prob = ODEProblem(f, y0, (0, tube_length)) # create an ODE problem and solve it\n", - "sol = solve(prob)\n", - "println(\"ODE Integrated\") # Displays when complete" + "# Specify initial guess\n", + "x0 = 0.5*(lb + ub)\n", + "\n", + "nothing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We now generate a plot of the centerline temperature versus position. If Plots.jl hasn't been used before yet this may also take some time." + "We start by creating an optimization problem, assign it the Ipopt optimizer, and add variables to the model." ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { - "image/svg+xml": [ - "\n", - "\n", - "\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - " \n", - " \n", - " \n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n" + "text/plain": [ + "10-element Array{VariableRef,1}:\n", + " x[1]\n", + " x[2]\n", + " x[3]\n", + " x[4]\n", + " x[5]\n", + " x[6]\n", + " x[7]\n", + " x[8]\n", + " x[9]\n", + " x[10]" ] }, - "execution_count": 15, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "using Plots\n", - "plot(sol, label=\"Centerline Temperature\", legend=:bottomright, vars = 1)" + "using JuMP, Ipopt\n", + "\n", + "model = Model(Ipopt.Optimizer)\n", + "\n", + "@variable(model, lb[i] <= x[i=1:10] <= ub[i], start = x0[i])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "INTERACTIVE! Finish the intermediate calculations and constraints used to define the problem.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# process unit #1\n", + "Lp11 = 1.5/1000\n", + "Lp21 = 1/1000\n", + "\n", + "@NLexpression(model, f1e, 40 + x[1])\n", + "@NLexpression(model, c1_1e, Lp11/f1e)\n", + "@NLexpression(model, c2_1e, Lp21/f1e)\n", + "\n", + "# mixer #1\n", + "@NLexpression(model, f4e, x[2] - f1e - x[3]) \n", + "@NLexpression(model, c1_2e, (c1_1e*f1e + x[4]*f4e + x[6]*x[3])/x[2])\n", + "@NLexpression(model, c2_2e, (c2_1e*f1e + x[5]*f4e + x[7]*x[3])/x[2])\n", + "\n", + "# treatment unit #1\n", + "@NLexpression(model, f3e, x[2])\n", + "@NLexpression(model, c1_3e, (1.0 - 0.95)*c1_2e)\n", + "@NLexpression(model, c2_3e, (1.0 - 0.2)*c2_2e)\n", + "\n", + "# splitter #1\n", + "@NLexpression(model, f5e, f3e - f4e)\n", + "@NLexpression(model, c1_5e, c1_3e)\n", + "@NLexpression(model, c2_5e, c2_3e)\n", + "\n", + "@NLconstraint(model, ceq1, x[4] - c1_3e == 0.0)\n", + "@NLconstraint(model, ceq2, x[5] - c2_3e == 0.0)\n", + "\n", + "# mixer #2\n", + "@NLexpression(model, f6e, x[8])\n", + "@NLexpression(model, f8e, f6e - f5e)\n", + "@NLexpression(model, c1_6, (c1_5e*f5e + x[9]*f8e)/f6e)\n", + "@NLexpression(model, c2_6, (c2_5e*f5e + x[10]*f8e)/f6e)\n", + "\n", + "# treatment unit #2\n", + "# ADD CODE HERE!\n", + "\n", + "# splitter #2\n", + "@NLexpression(model, f10e, f7e - f8e - x[3])\n", + "@NLexpression(model, c1_10, c1_7e)\n", + "@NLexpression(model, c2_10, c2_7e)\n", + "\n", + "@NLconstraint(model, ceq3, x[9] - c1_7e == 0)\n", + "@NLconstraint(model, ceq4, x[10] - c2_7e == 0)\n", + "@NLconstraint(model, ceq5, x[6] - c1_7e == 0)\n", + "@NLconstraint(model, ceq6, x[7] - c2_7e == 0)\n", + "\n", + "# inequality effluent constraints\n", + "c1_spec = 0.00001;\n", + "c2_spec = 0.00001;\n", + "@NLconstraint(model, cons_ef1, c1_10 <= c1_spec)\n", + "@NLconstraint(model, cons_ef2, c2_10 <= c2_spec)\n", + "\n", + "nothing" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# financial assumptions\n", + "AR = 0.1 # annualized factor\n", + "H = 8000 # total hours plant operates\n", + "alpha = 0.7 # scaling term for capital cost\n", + "Cwater = 1.0 # cost of freshwater ($/ton)\n", + "Cinv1, Cinv2 = 16800.0, 12600.0 # investment costs\n", + "Cop1, Cop2 = 0.0067, 1.0 # marginal operating costs\n", + " \n", + "@NLobjective(model, Min, AR*(Cinv1*x[2]^alpha + Cinv2*x[8]^alpha) + Cop1*x[2] + Cop2*x[8] + H*x[1]*Cwater)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we solve the optimization problem." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Solve optimization problem\n", + "JuMP.optimize!(model)\n", + "\n", + "# Retrieve solution information\n", + "println(\"Result Status: \", primal_status(model))\n", + "println(\"Termination Status: \", termination_status(model))\n", + "println(\"Objective values: \", objective_value(model))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "INTERACTIVE! Next we extract information about the optimization problem. We'll want to check the status of the solve and the objective value, initially. Generally, optimizers will return codes to indicate if the solve was successful and to indicate any potential numerical issues that occur. Now, check the optimal solution and describe the system configuration.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In some cases, manufacturing plants are permitted a certain allowable amount of emissions at a particular site. Multiple emission sources may exist on a single site and it can be valuable to understand how expensive it is to reduce emissions in each process unit on that site. Let's determine the profitability associated with potentially relaxing our emissions target for this process. In order to do this, we'll perform a sensitivity analysis. That is to say, how much will changing a constraint and/or the variable bounds result in changing the solution? If we can perturb a constraint without affect this solution then the constraint is inactive. Otherwise, the dual values indicate the degree to which the solution may change by altering a constraint. We can obtain this information from the lambda structure. Nonlinear inequality constraint dual values are accessed via as shown below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "println(\"Effluent constraint #1 & #2 duals = $(dual(cons_ef1)) & $(dual(cons_ef2))\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "INTERACTIVE! How much effect would changing the 10ppm effluent specification slightly have on the process cost? \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "INTERACTIVE! Based on the form of the optimization problem, would you expect a local (convex) optimizer to return the best possible objective value? Why or why (not)? What sort of numerical experiments would support a determination in either case?\n", + "
" ] }, { @@ -505,13 +684,18 @@ "source": [ "
\n", "\n", - "# Questions for reflection\n", - "- For a simple, what factors influence whether a system is numerically stable?\n", - "- For IVP-PDEs, the problem size can grow quite rapidly. For a 3D systems, using 20 discretization points in each dimension leads to an 8000-by-8000 system while 256 points leads to a ~16 million by ~16 million system. As illustrated by [Jaroudi, et al.](https://www.tandfonline.com/doi/full/10.1080/00207160.2019.1613526?af=R), a large number of such systems can be solved in a few hours on standard desktop. In light of this fact, how do you think the resulting linear systems formulated and solved?\n", - "- What are some other questions that you'd expect to be important when design a chemical looping combustion system? Can you provide some ideas of how numerical methods could be used to help answer these?\n", + "# A question to reflect on\n", + "We've solved two optimization problems: an integer program and a nonlinear program. The full design of a wastewater treatment plant may be handled by combining these two approaches. This can include which process units, how many process units, and treatment units are included as well as a full exploration of recycle loop configurations. This variety of problem is termed superstructure optimization and developing efficient means of solving these problems remains an active area of research. How might we modify the above problem using 0-1 integer variables to explore other potential recycle configurations?\n", "\n", "
" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/tutorial/julia/Tutorial 6 - Optimization/J6_Optimization_solved.ipynb b/tutorial/julia/Tutorial 6 - Optimization/J6_Optimization_solved.ipynb new file mode 100644 index 0000000..3b18827 --- /dev/null +++ b/tutorial/julia/Tutorial 6 - Optimization/J6_Optimization_solved.ipynb @@ -0,0 +1,724 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Optimization \n", + "\n", + "
\n", + "\n", + "## Learning Objectives \n", + "\n", + "- Classify optimization problems as linear, quadratic, nonlinear, and mixed-integer problems.\n", + "- Learn how to interpret the solutions of optimization problems.\n", + "- Learn how to input optimization problems in Julia/JuMP).\n", + "- Assess the sensitivity of a solution with respect to constraints.\n", + "- Learn how to formulate and solve optimization problems for multiple examples.\n", + "\n", + "
\n", + "\n", + "## Organization \n", + "\n", + "- First, we'll review some of the types of optimization problems.\n", + "- Next, we'll solve a Sudoku puzzle.\n", + "- You'll formulate and solve a simple blending and pooling problem.\n", + "- Lastly, we'll analyze the blending and pooling problem from a sensitivity perspective.\n", + " \n", + "
\n", + "\n", + "## Problem Forms \n", + "\n", + "Optimizers generally exploit a specific mathematical structure in the problem formulation as a means to solve a problem. In fact, some underlying assumptions are always required to ensure a problem is optimized. One common example arrise from combining KKT conditions with constraint qualifications or alternatively regularity conditions for nonlinear programs.\n", + "\n", + "These problem types range from linear programs for which problems with 100k variables and constraints may routinely be solved [in less than a minute](http://plato.asu.edu/ftp/lpsimp.html) to continuous convex problems to nonconvex nonlinear mixed-integer problems (were some problems with fewer than 5 variables have yet to be solved). As such, using an appropriate optimizer for a particular problem type can transform an unsolveable problem into a trival one. The most heavily studied and widely used descriptors for each program are linear, quadratic, nonlinear, and mixed-integer.\n", + "\n", + "**Types of based on objectives/constraints:**\n", + "\n", + "- **Linear:** All the constraints and the objective are linear.\n", + "- **Quadratic** all the constraints and the objective are quadratic or linear.\n", + "- **Nonlinear** at least one constraint or the objective is not linear.\n", + "\n", + "**Types based on manner of variable used:**\n", + "\n", + "- **Continuous:** All variables can vary between values in some possibly open interval.\n", + "- **Integer:** All variables are integer valued.\n", + "- **Mixed-integer:** The problem contains both integer and continuous variables.\n", + "\n", + "One of the central focuses of optimization research has focused on developing specialized routines for solving programs with other special forms. A number of other important forms include [specially-ordered sets](https://link.springer.com/article/10.1007/BF01589393), [second-order cones](https://ieeexplore.ieee.org/abstract/document/6983691), [equilibrium constraints](https://faculty.wcas.northwestern.edu/~lchrist/Ferris_mathematical_programs2.pdf), as well as a myriad of special nonlinear forms. When attempting to solve a difficult model it's worth checking a few references (such as [Mosek's Modeling Cookbook](https://docs.mosek.com/MOSEKModelingCookbook-letter.pdf)) to see if the problem can be re-written as simplier form or a simplier problem type.\n", + "\n", + "This trend has lead to the development of a number of specialized languages and software packages used to describe optimization problems interface with a myriad of different optimizers. In Python, the main tools for this are [Pyomo]() and [CVXOPT](https://cvxopt.org/). In Julia, we have [JuMP](https://jump.dev/JuMP.jl/stable/), and [Convex.jl](https://jump.dev/Convex.jl/stable/). A number of other commercial offerings available include [GAMS](https://www.gams.com/), [AIMMS](https://en.wikipedia.org/wiki/AIMMS), and [AMPL](https://ampl.com/) along with a number of offerings associated with commercially available solvers such as [CPLEX Optimization Studio by IBM](https://www.ibm.com/analytics/cplex-optimizer) or [Xpress Mosel for FICO Express](https://www.fico.com/en/products/fico-xpress-optimization). We'll work through formulating and solving a couple of optimization problems using JuMP in this workbook.\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[32m\u001b[1m Updating\u001b[22m\u001b[39m registry at `C:\\Users\\wilhe\\.julia\\registries\\General`\n", + "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Project.toml`\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Manifest.toml`\n", + "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Project.toml`\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Manifest.toml`\n", + "\u001b[32m\u001b[1m Resolving\u001b[22m\u001b[39m package versions...\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Project.toml`\n", + "\u001b[32m\u001b[1mNo Changes\u001b[22m\u001b[39m to `C:\\Users\\wilhe\\Manifest.toml`\n" + ] + } + ], + "source": [ + "# Run this block once. Adds Julia packages to your Julia local installation from the package manager\n", + "\n", + "using Pkg # Import functions from the package manager into this session.\n", + "Pkg.add(\"JuMP\") # A modeling language for Mathematical optimization in Julia.\n", + "Pkg.add(\"Ipopt\") # A highly performant nonlinear optimizer.\n", + "Pkg.add(\"GLPK\") # An open-source linear and mixed-integer linear optimizer\n", + "\n", + "nothing # suppresses long output on last line" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "## Solving Sudoku with Mixed-Integer Programming* \n", + "\n", + "* Adapted from a now defunct example by Ian Dunning at https://www.juliaopt.org/notebooks/JuMP-Sudoku.html.\n", + "\n", + "Sudoku is a popular number puzzle. The goal is to place the digits 1,...,9 on a nine-by-nine grid, with some of the digits already filled in. Your solution must satisfy the following rules:\n", + "\n", + " The numbers 1 to 9 must appear in each 3x3 square\n", + " The numbers 1 to 9 must appear in each row\n", + " The numbers 1 to 9 must appear in each column\n", + "\n", + "Any combination satisfying the above rules is a valid solution. Thus, we're simply seeking any feasible solution. While this isn't strictly an optimization problem (it's a constraint satisfaction problem), but we can make it into equivalent optimization problem. Namely, we can just specify that the objective is some constant value of our choosing. \n", + "\n", + "We can model this problem using 0-1 integer programming: a problem where all the decision variables are binary. We'll use JuMP to create the model, and then we can solve it with any integer optimizer." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "using JuMP, GLPK # import functions from the JuMP & GLPK package into your current environment\n", + "\n", + "# create a model called \"sudoku\" which holds all variables, constraints, \n", + "# functions, and options needed to solve the sudoku problem\n", + "sudoku = Model(GLPK.Optimizer)\n", + "\n", + "# add binary variables\n", + "@variable(sudoku, x[i=1:9, j=1:9, k=1:9], Bin) # Bin indicates the variable is binary (has a value of 0 or 1)\n", + "\n", + "nothing" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Only one digit can be in each square. So for each square represented by a pair of i,j variables the sum over k should be equal to one." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "for i = 1:9, j = 1:9 \n", + " @constraint(sudoku, sum(x[i,j,k] for k=1:9) == 1)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each variable can only appear once in each column." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "for j = 1:9, k = 1:9\n", + " @constraint(sudoku, sum(x[i,j,k] for i=1:9) == 1)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "INTERACTIVE! Each variable can only appear once in each row. Fill in the appropriate expression.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "for ind = 1:9, k = 1:9\n", + " @constraint(sudoku, sum(x[i,ind,k] for i=1:9) == 1)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each variable can only appear once in each 3-by-3 subgrid." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# i is the top left row, j is the top left column\n", + "# We'll sum from i to i+2, e.g. i=4, r=4, 5, 6\n", + "for i = 1:3:7, j = 1:3:7, k = 1:9\n", + " @constraint(sudoku, sum(x[r,c,k] for r=i:i+2, c=j:j+2) == 1)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Fills in the provided values using zero to represent an empty cell" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# enter the initial values\n", + "init_val = [ 5 3 0 0 7 0 0 0 0;\n", + " 6 0 0 1 9 5 0 0 0;\n", + " 0 9 8 0 0 0 0 6 0;\n", + " \n", + " 8 0 0 0 6 0 0 0 3;\n", + " 4 0 0 8 0 3 0 0 1;\n", + " 7 0 0 0 2 0 0 0 6;\n", + " \n", + " 0 6 0 0 0 0 2 8 0;\n", + " 0 0 0 4 1 9 0 0 5;\n", + " 0 0 0 0 8 0 0 7 9]\n", + "\n", + "# if the value is specified initially add a constraint fixing the variables to that value.\n", + "for i = 1:9, j = 1:9\n", + " if init_val[i,j] != 0\n", + " @constraint(sudoku, x[i,j,init_val[i,j]] == 1)\n", + " end\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "What type of optimization problem was the sudoku? Is GLPK an appropriate optimizer for this type of problem? You'll want to search for GLPK's documentation and check there.\n", + "
\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "9×9×9 Array{Float64,3}:\n", + "[:, :, 1] =\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + " 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 2] =\n", + " 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 1.0 1.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 3] =\n", + " 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + "\n", + "[:, :, 4] =\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 5] =\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 6] =\n", + " 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 1.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 7] =\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + " 1.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0\n", + "\n", + "[:, :, 8] =\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0\n", + " 0.0 0.0 1.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n", + " 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0\n", + "\n", + "[:, :, 9] =\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0\n", + " 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n", + " 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0\n", + " 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "optimize!(sudoku)\n", + "\n", + "x_val = value.(x) # Extract the values of x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To display the solution, we need to look for the values of **x**[i, j, k] that are equal to 1.\n", + "\n", + "
\n", + "INTERACTIVE! Unpack the solution to a 9-by-9 matrix and confirm this is a valid Sudoku solution. Note that the \n", + "solution returned may not be an integer. So you'll need to round this to the nearest integer using iround(y). \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "9×9 Array{Float64,2}:\n", + " 5.0 3.0 2.0 2.0 7.0 6.0 1.0 3.0 7.0\n", + " 6.0 4.0 7.0 1.0 9.0 5.0 9.0 5.0 8.0\n", + " 1.0 9.0 8.0 3.0 4.0 8.0 4.0 6.0 2.0\n", + " 8.0 1.0 9.0 9.0 6.0 1.0 5.0 4.0 3.0\n", + " 4.0 5.0 3.0 8.0 5.0 3.0 7.0 9.0 1.0\n", + " 7.0 2.0 6.0 7.0 2.0 4.0 8.0 2.0 6.0\n", + " 2.0 6.0 1.0 6.0 3.0 2.0 2.0 8.0 4.0\n", + " 3.0 8.0 5.0 4.0 1.0 9.0 6.0 1.0 5.0\n", + " 9.0 7.0 4.0 5.0 8.0 7.0 3.0 7.0 9.0" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "final_val = zeros(9,9)\n", + "\n", + "for i = 1:9, j = 1:9, k = 1:9\n", + " if x_val[i,j,k] != 0\n", + " final_val[i,j] = k\n", + " end\n", + "end\n", + "\n", + "final_val" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "## Integrating water system treatment in the process industry \n", + "\n", + "* Adapted from [R. Karuppiah & I. Grossmann. Global optimization for the synthesis of integrated water systems in chemical processes. Computers and Chemical Engineering 30 (2006) 650–673](https://www.sciencedirect.com/science/article/abs/pii/S0098135405002991)\n", + "\n", + "The efficient utilitization of water will be an integral part to solving the numerous water management issues of today and the near future. Increasing usage of water, anticipated water scarities, and changing regulations are expected to result in numerous related challenges in the next decades. These challenges may extend to the adaptation of numerous industrial processes. as fresh water is a key resource in such processes. These include washing operations in the food processing, thermal processing (cooling, quenching, and scrubbing) in iron and steel manufacturing, and the desalination of crude oil. In each of this processes, some level of contamination is introduced into the freshwater by the process. This process water is then treated in a central facility to remove contaminants and satisfy local regulatory specifications for the disposal of wastewater. In design or improvement of these central facilities, three key questions must be considered. 1) What unit operations need to be included? 2) How should these process units be connected? 3) How should one operate these processes units?\n", + "\n", + "A typical design objective for a wastewater treatment plant may be to minimize it's overall cost in a given year of operation. That is we minimize the sum of the cost fo freshwater intake, the investment cost associated with each treatment unit, and the operating cost of each unit. This can be achieved by minimizing the follow objective:\n", + "\n", + "$\\Phi = A_R\\sum_{j} C_j^{Inv} F_j^{\\alpha} + H\\sum_{j} F_j C_j^{Op} + H F_{fw} C_{fw}$ \n", + "\n", + "where H is the number of hours of operation per annum ($h$); the cost of freshwater is given by $C_{fw}$ (`$/ton`); the fresh water intake mass if given by $F_{fw}$ (`ton/h`); The annualized factor for investment on treatment units is given by $A_R$; the $C_j^{Inv} F_j^{\\alpha}$ is the investment cost (`$`) of the $j$-th treatment unit which treats a flowrate stream of contaminated water $F_j$ (`ton/h`); $C_j^{Op} F_j^{\\alpha}$ is the operating cost (`$/h`) of the $j$-th treatment unit which treats a flowrate of contaminated water $F_j$ (`ton/h`); the parameter $\\alpha$ is a constant cost function exponent $0 \\leq \\alpha \\leq 1$.\n", + "\n", + "The **mixers** units can be described as follows:\n", + "- The outlet flowrate is the sum of the inlet stream flow rate: $F^{outlet} = \\sum_{i = 1}^{n} F^{inlet}_i $ where $F^{outlet}$, $F^{inlet}_1$, $\\ldots$, $F^{inlet}_n$ are given in (tons/hr) and $n$ is the number of inlets streams.\n", + "- The mass of each contaminant in the outlet equals the mass of each contaminant in the inlet streams: $F^{outlet}C_j^{outlet} = \\sum_{i = 1}^{n} F^{inlet}_i C_j^{inlet}$ \n", + "\n", + "The **splitter** units:\n", + "- The outlet flowrates are the sum of the inlet flowrate: $F^{inlet} = \\sum_{i = 1}^{m} F^{outlet}_i $ where $F^{inlet}$, $F^{outlet}_1$, $\\ldots$, $F^{outlet}_m$ are given in (tons/hr) and $m$ is the number of outlet streams.\n", + "- The each outlet streams contaminant concentration is equal to the inlet stream contaminant concentration: $F^{outlet}_m$. \n", + "\n", + "A fixed mass of contaminant is added to a stream by each **process unit**:\n", + "- A process unit has a single inlet stream and a single outlet stream: $F^{inlet} = F^{outlet}$\n", + "- The process unit reduces the l $F^{inlet}C_j^{inlet} + L^{p}_j = F^{outlet}C_j^{outlet}$\n", + "\n", + "The **treatment units**:\n", + "- A process unit has a single inlet stream and a single outlet stream with the same flowrate: $F^{inlet} = F^{outlet}$\n", + "- The concentration of contaminant in the outlet $C_j^{outlet} = \\beta^{t}_j C_j^{inlet}$ where $\\beta^{t}_j$ is the removal ratio of $j$.\n", + "\n", + "Two contaminants A & B are introduced into the system and the concentration of each contaminant must be reduced to 10ppm prior to discharging it into the enviroment. The cost of freshwater is assumed to be $1/ton, the annualized factor for investment on the treatment unit is taken to be 0.1, and the total time of operation of the plant is 8000h/year. While numerous different treatment technologies and separation train configurations are possible, we've settled on using two distinct units to perform a primary and secondary treatment. We'd like to determine whether the inclusion of a recycle stream(s) may reduce the process cost and whether is preferable to have the recyle stream bridge one unit or two.\n", + "\n", + "\n", + "\n", + "The flowrate of water required by each process unit, along with containment inlet concentration limits, and amount of contaminant discharged are given in Table 1. We'll assume an 𝛼 value of 0.7, an investment. The process unit uses 40 ton/h of freshwater and discharges 1 kg/h of A and 1kg/h of B.\n", + "\n", + "The efficiency of the treatment units are given by the removal ratio in Table 2 along with cost model constants: \n", + "\n", + "\n", + "\n", + "
\n", + "INTERACTIVE! Is this model convex or nonconvex? Continuous or mixed-integer? Why? \n", + "
\n", + "\n", + "We've now started to define objective and constraint functions which compute the concentration and flow rates associated with each stream and the overall system cost. Generally, there are two approaches to doing this. We can introduce a new variable for each concentration and flowrate and allow the optimizer to enforce mass balances. Alternative, we can algebraiclly re-arrange these mass balances and avoid introducing a large number of variables. In some cases, the former approach exposes special forms that an optimizer may use effectively. In other cases, the decreased number of variables may be more beneficial. We primarily use the later approach but we'll introduce variables to handle the recycle-loops which generally cannot be re-arranged into an explicit algebraic form. The optimization variables are $x=\\left(F_{\\mathrm{feed}} ,F_2 ,F_9 ,C_{A,4} ,C_{B,4} ,C_{A,9} ,C_{B,9} ,F_6 ,C_{A,8} ,C_{B,8} \\right)$. Take some time to inspect the constraint function and reflect on the choice of these variables.\n", + "\n", + "
\n", + "Generally optimizers require that all variables have both lower (lb) and upper (ub) bounds defined. If there aren't any obvious choices in a problem, you may have to exercise your best judgement and choose reasonable bounds. Equipment based bounds on flowrate are provided below. Further, we assume 0.1 is a sufficiently high concetration such that it will be an inactive constraint. Furthermore, an initial guess (x0) often must be specified. For convex problems, the solution obtained will generally not depend on this guess. \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# Bounds\n", + "lb = [0.1; 0.1; 0.1; 0.0; 0.0; 0.0; 0.0; 0.1; 0.0; 0.0]\n", + "ub = [100.0; 100.0; 100.0; 0.1; 0.1; 0.1; 0.1; 100.0; 0.1; 0.1]\n", + "\n", + "# Specify initial guess\n", + "x0 = 0.5*(lb + ub)\n", + "\n", + "nothing" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We start by creating an optimization problem, assign it the Ipopt optimizer, and add variables to the model." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "10-element Array{VariableRef,1}:\n", + " x[1]\n", + " x[2]\n", + " x[3]\n", + " x[4]\n", + " x[5]\n", + " x[6]\n", + " x[7]\n", + " x[8]\n", + " x[9]\n", + " x[10]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "using JuMP, Ipopt\n", + "\n", + "model = Model(Ipopt.Optimizer)\n", + "\n", + "@variable(model, lb[i] <= x[i=1:10] <= ub[i], start = x0[i])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "INTERACTIVE! Finish the intermediate calculations and constraints used to define the problem.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# process unit #1\n", + "Lp11 = 1.5/1000\n", + "Lp21 = 1/1000\n", + "\n", + "@NLexpression(model, f1e, 40 + x[1])\n", + "@NLexpression(model, c1_1e, Lp11/f1e)\n", + "@NLexpression(model, c2_1e, Lp21/f1e)\n", + "\n", + "# mixer #1\n", + "@NLexpression(model, f4e, x[2] - f1e - x[3]) \n", + "@NLexpression(model, c1_2e, (c1_1e*f1e + x[4]*f4e + x[6]*x[3])/x[2])\n", + "@NLexpression(model, c2_2e, (c2_1e*f1e + x[5]*f4e + x[7]*x[3])/x[2])\n", + "\n", + "# treatment unit #1\n", + "@NLexpression(model, f3e, x[2])\n", + "@NLexpression(model, c1_3e, (1.0 - 0.95)*c1_2e)\n", + "@NLexpression(model, c2_3e, (1.0 - 0.2)*c2_2e)\n", + "\n", + "# splitter #1\n", + "@NLexpression(model, f5e, f3e - f4e)\n", + "@NLexpression(model, c1_5e, c1_3e)\n", + "@NLexpression(model, c2_5e, c2_3e)\n", + "\n", + "@NLconstraint(model, ceq1, x[4] - c1_3e == 0.0)\n", + "@NLconstraint(model, ceq2, x[5] - c2_3e == 0.0)\n", + "\n", + "# mixer #2\n", + "@NLexpression(model, f6e, x[8])\n", + "@NLexpression(model, f8e, f6e - f5e)\n", + "@NLexpression(model, c1_6, (c1_5e*f5e + x[9]*f8e)/f6e)\n", + "@NLexpression(model, c2_6, (c2_5e*f5e + x[10]*f8e)/f6e)\n", + "\n", + "# treatment unit #2\n", + "@NLexpression(model, f7e, f6e)\n", + "@NLexpression(model, c1_7e, (1.0 - 0.1)*c1_6)\n", + "@NLexpression(model, c2_7e, (1.0 - 0.7)*c2_6)\n", + "\n", + "# splitter #2\n", + "@NLexpression(model, f10e, f7e - f8e - x[3])\n", + "@NLexpression(model, c1_10, c1_7e)\n", + "@NLexpression(model, c2_10, c2_7e)\n", + "\n", + "@NLconstraint(model, ceq3, x[9] - c1_7e == 0)\n", + "@NLconstraint(model, ceq4, x[10] - c2_7e == 0)\n", + "@NLconstraint(model, ceq5, x[6] - c1_7e == 0)\n", + "@NLconstraint(model, ceq6, x[7] - c2_7e == 0)\n", + "\n", + "# inequality effluent constraints\n", + "c1_spec = 0.00001;\n", + "c2_spec = 0.00001;\n", + "@NLconstraint(model, cons_ef1, c1_10 <= c1_spec)\n", + "@NLconstraint(model, cons_ef2, c2_10 <= c2_spec)\n", + "\n", + "nothing" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "# financial assumptions\n", + "AR = 0.1 # annualized factor\n", + "H = 8000 # total hours plant operates\n", + "alpha = 0.7 # scaling term for capital cost\n", + "Cwater = 1.0 # cost of freshwater ($/ton)\n", + "Cinv1, Cinv2 = 16800.0, 12600.0 # investment costs\n", + "Cop1, Cop2 = 0.0067, 1.0 # marginal operating costs\n", + " \n", + "@NLobjective(model, Min, AR*(Cinv1*x[2]^alpha + Cinv2*x[8]^alpha) + Cop1*x[2] + Cop2*x[8] + H*x[1]*Cwater)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we solve the optimization problem." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Solve optimization problem\n", + "JuMP.optimize!(model)\n", + "\n", + "# Retrieve solution information\n", + "println(\"Result Status: \", primal_status(model))\n", + "println(\"Termination Status: \", termination_status(model))\n", + "println(\"Objective values: \", objective_value(model))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "INTERACTIVE! Next we extract information about the optimization problem. We'll want to check the status of the solve and the objective value, initially. Generally, optimizers will return codes to indicate if the solve was successful and to indicate any potential numerical issues that occur. Now, check the optimal solution and describe the system configuration.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In some cases, manufacturing plants are permitted a certain allowable amount of emissions at a particular site. Multiple emission sources may exist on a single site and it can be valuable to understand how expensive it is to reduce emissions in each process unit on that site. Let's determine the profitability associated with potentially relaxing our emissions target for this process. In order to do this, we'll perform a sensitivity analysis. That is to say, how much will changing a constraint and/or the variable bounds result in changing the solution? If we can perturb a constraint without affect this solution then the constraint is inactive. Otherwise, the dual values indicate the degree to which the solution may change by altering a constraint. We can obtain this information from the lambda structure. Nonlinear inequality constraint dual values are accessed via as shown below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "println(\"Effluent constraint #1 & #2 duals = $(dual(cons_ef1)) & $(dual(cons_ef2))\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "INTERACTIVE! How much effect would changing the 10ppm effluent specification slightly have on the process cost? \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "INTERACTIVE! Based on the form of the optimization problem, would you expect a local (convex) optimizer to return the best possible objective value? Why or why (not)? What sort of numerical experiments would support a determination in either case?\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "# A question to reflect on\n", + "We've solved two optimization problems: an integer program and a nonlinear program. The full design of a wastewater treatment plant may be handled by combining these two approaches. This can include which process units, how many process units, and treatment units are included as well as a full exploration of recycle loop configurations. This variety of problem is termed superstructure optimization and developing efficient means of solving these problems remains an active area of research. How might we modify the above problem using 0-1 integer variables to explore other potential recycle configurations?\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.5.1", + "language": "julia", + "name": "julia-1.5" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/tutorial/julia/Tutorial 6 - Optimization/SpecTable1.png b/tutorial/julia/Tutorial 6 - Optimization/SpecTable1.png new file mode 100644 index 0000000000000000000000000000000000000000..4c5d33955d07d5cb3e825176f0faf049f5695199 GIT binary patch literal 13412 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 literal 0 HcmV?d00001 diff --git a/tutorial/julia/Tutorial 6 - Optimization/Treatment_Train.png b/tutorial/julia/Tutorial 6 - Optimization/Treatment_Train.png new file mode 100644 index 0000000000000000000000000000000000000000..45ba14f61d0dfa68dd691d2034795a6e321dbc1d GIT binary patch literal 31344 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 GxBnmJwe@TO literal 0 HcmV?d00001 diff --git a/tutorial/julia/Tutorial 6 - Optimization/wastewater.png b/tutorial/julia/Tutorial 6 - Optimization/wastewater.png deleted file mode 100644 index fa8fd83f8f1bc14731ca8fae8c7b1acd13cb3e1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35901 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

1Q)ZUQgkmL>rHrd#L(N^{+$=|MMj-@ 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 B8KwXL diff --git a/tutorial/matlab/Tutorial 6 - Optimization/M6_Optimization.mlx b/tutorial/matlab/Tutorial 6 - Optimization/M6_Optimization.mlx index feb2991d45b9edd3fd8f0f7e26d556fae9baaf4e..d2960477eb65607e1e16c2b75a2b30c330cbbad0 100644 GIT binary patch delta 1088 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 Y#52tcDK+@Ff-os+niN)M=q>Jl04i$kp#T5? delta 9587 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